npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@codifycli/plugin-core

v1.0.0

Published

TypeScript library for building Codify plugins to manage system resources (applications, CLI tools, settings) through infrastructure-as-code

Downloads

654

Readme

@codifycli/plugin-core

Core library for building Codify plugins. Codify is an infrastructure-as-code tool that manages system resources (applications, CLI tools, and settings) through declarative JSON configuration files.

Overview

This library provides the foundational abstractions and runtime for creating Codify plugins. Plugins extend Codify's capabilities by implementing resources that can be created, modified, and destroyed on a system. Examples of resources include:

  • CLI Tools: Homebrew, Docker, Git
  • Applications: Google Chrome, VS Code, Zoom
  • Settings: Git configs, AWS profiles, system preferences

Installation

npm install @codifycli/plugin-core

Requirements:

  • Node.js >= 22.0.0
  • TypeScript 5.x (for development)

Quick Start

Here's a minimal example of creating a plugin with a single resource:

import { Resource, ResourceSettings, Plugin, runPlugin, getPty } from '@codifycli/plugin-core';
import { StringIndexedObject } from '@codifycli/schemas';

// Define the resource configuration type
interface GitConfig extends StringIndexedObject {
  userName?: string;
  userEmail?: string;
}

// Implement the Resource abstract class
class GitConfigResource extends Resource<GitConfig> {
  getSettings(): ResourceSettings<GitConfig> {
    return {
      id: 'git-config',
      operatingSystems: ['darwin', 'linux'],
      schema: {
        type: 'object',
        properties: {
          userName: { type: 'string' },
          userEmail: { type: 'string' }
        }
      }
    };
  }

  async refresh(parameters: Partial<GitConfig>) {
    const pty = getPty();

    const nameResult = await pty.spawnSafe('git config --global user.name');
    const emailResult = await pty.spawnSafe('git config --global user.email');

    return {
      userName: nameResult.status === 'success' ? nameResult.data.trim() : undefined,
      userEmail: emailResult.status === 'success' ? emailResult.data.trim() : undefined
    };
  }

  async create(plan) {
    const pty = getPty();
    const config = plan.desiredConfig!;

    if (config.userName) {
      await pty.spawn(`git config --global user.name "${config.userName}"`);
    }
    if (config.userEmail) {
      await pty.spawn(`git config --global user.email "${config.userEmail}"`);
    }
  }

  async destroy(plan) {
    const pty = getPty();

    await pty.spawn('git config --global --unset user.name');
    await pty.spawn('git config --global --unset user.email');
  }
}

// Create and run the plugin
const plugin = Plugin.create('my-plugin', [new GitConfigResource()]);
runPlugin(plugin);

Core Concepts

Plugin

The top-level container that manages multiple resource types. Handles IPC communication with the Codify CLI.

const plugin = Plugin.create('plugin-name', [
  new Resource1(),
  new Resource2(),
  // ... more resources
]);

runPlugin(plugin);

Resource

The fundamental building block representing a manageable system entity. Resources must implement:

  • getSettings(): Return resource configuration (id, schema, OS support, etc.)
  • refresh(parameters, context): Query the current system state
  • create(plan): Install/create the resource
  • destroy(plan): Uninstall/remove the resource
  • modify(parameterChange, plan): Update individual parameters (optional)

Plan

Represents a set of changes needed to transform the current state into the desired state. Plans contain:

  • Resource Operation: CREATE, DESTROY, MODIFY, RECREATE, or NOOP
  • Parameter Changes: Individual parameter-level operations (ADD, REMOVE, MODIFY, NOOP)

The planning workflow:

  1. Validate: Check user configuration against schema
  2. Plan: Compare desired vs. current state, generate change set
  3. Apply: Execute the plan to make changes

Stateful vs Stateless Modes

Stateless Mode (default):

  • Plans computed by comparing desired config against current system state
  • Only manages parameters explicitly declared in config
  • No destroy operations (removing from config = ignored by Codify)

Stateful Mode:

  • Tracks previous state between runs
  • Supports destroy operations
  • Plans compare desired vs. state, then match state to current system
  • Enables granular change detection

Stateful Parameters

Parameters with their own lifecycle, tied to the parent resource. Examples:

  • Homebrew formulas (can be installed/uninstalled within Homebrew)
  • NVM Node versions (managed within NVM)
import { StatefulParameter } from '@codifycli/plugin-core';

class BrewFormulaParameter extends StatefulParameter<BrewConfig, string[]> {
  async refresh(desired, config) {
    const pty = getPty();
    const result = await pty.spawn('brew list --formula');
    return result.data.split('\n').filter(Boolean);
  }

  async add(formulas, plan) {
    const pty = getPty();
    await pty.spawn(`brew install --formula ${formulas.join(' ')}`);
  }

  async remove(formulas, plan) {
    const pty = getPty();
    await pty.spawn(`brew uninstall --formula ${formulas.join(' ')}`);
  }

  async modify(newValue, previousValue, plan) {
    // Handle formula updates
  }
}

Register in resource settings:

getSettings()
:
ResourceSettings < BrewConfig > {
  return {
    id: 'homebrew',
    parameterSettings: {
      formulas: {
        type: 'stateful',
        implementation: new BrewFormulaParameter()
      }
    }
  };
}

PTY Abstraction

Execute shell commands through the PTY abstraction:

import { getPty } from '@codifycli/plugin-core';

const pty = getPty();

// Spawn command (throws on non-zero exit)
const result = await pty.spawn('brew install jq');

// Spawn safely (returns result with status)
const safeResult = await pty.spawnSafe('which jq');
if (safeResult.status === 'success') {
  console.log(safeResult.data);
}

// With options
await pty.spawn('npm install', {
  cwd: '/path/to/project',
  env: { NODE_ENV: 'production' },
  interactive: true,
  requiresRoot: false
});

Two PTY implementations:

  • BackgroundPty: Async execution during refresh/plan (killed after planning)
  • SequentialPty: Sync execution during apply operations

Resource Settings

Configure resource behavior via ResourceSettings<T>:

getSettings(): ResourceSettings<MyConfig> {
  return {
    // Required: unique type identifier
    id: 'my-resource',

    // Required: supported operating systems
    operatingSystems: ['darwin', 'linux', 'win32'],

    // Optional: supported Linux distributions
    linuxDistros: ['ubuntu', 'debian', 'fedora'],

    // Schema for validation (JSON Schema or Zod)
    schema: {
      type: 'object',
      properties: {
        version: { type: 'string' },
        path: { type: 'string' }
      },
      required: ['version']
    },

    // Allow multiple instances
    allowMultiple: {
      identifyingParameters: ['name', 'path'],
      matcher: (desired, current) => desired.name === current.name
    },

    // Prevent resource from being destroyed
    canDestroy: false,

    // Mark as sensitive (prevents auto-import)
    isSensitive: true,

    // Resource dependencies
    dependencies: ['other-resource-id'],

    // Per-parameter settings
    parameterSettings: {
      path: {
        inputTransformation: {
          to: (input) => untildify(input),    // Expand ~
          from: (current) => tildify(current)  // Convert to ~
        }
      },
      apiKey: {
        isSensitive: true  // Hide in plan output
      },
      tags: {
        type: 'array',
        isElementEqual: (a, b) => a.name === b.name
      }
    }
  };
}

API Reference

Core Classes

Plugin

  • static create(name: string, resources: Resource[]): Plugin
  • async initialize(data): Promise<InitializeResponseData>
  • async plan(data): Promise<PlanResponseData>
  • async apply(data): Promise<void>
  • async validate(data): Promise<ValidateResponseData>
  • async import(data): Promise<ImportResponseData>
  • async match(data): Promise<MatchResponseData>

Resource<T>

  • abstract getSettings(): ResourceSettings<T>
  • async initialize(): Promise<void>
  • async validate(parameters): Promise<void>
  • abstract refresh(parameters, context): Promise<T | T[] | null>
  • abstract create(plan: CreatePlan<T>): Promise<void>
  • abstract destroy(plan: DestroyPlan<T>): Promise<void>
  • async modify(change: ParameterChange<T>, plan: ModifyPlan<T>): Promise<void>

Plan<T>

  • id: string
  • changeSet: ChangeSet<T>
  • coreParameters: ResourceConfig
  • isStateful: boolean
  • desiredConfig: T | null
  • currentConfig: T | null
  • requiresChanges(): boolean
  • toResponse(): PlanResponseData

StatefulParameter<T, V>

  • getSettings(): ParameterSetting
  • abstract refresh(desired, config): Promise<V | null>
  • abstract add(value, plan): Promise<void>
  • abstract modify(newValue, previousValue, plan): Promise<void>
  • abstract remove(value, plan): Promise<void>

Utility Functions

// PTY access
getPty(): IPty

// Path utilities
tildify(absolutePath: string): string
untildify(pathWithTilde: string): string
resolvePathWithVariables(path: string): string
addVariablesToPath(absolutePath: string): string

// File utilities
fileExists(path: string): Promise<boolean>
directoryExists(path: string): Promise<boolean>

// Array utilities
areArraysEqual<T>(a: T[], b: T[], isEqual?: (a: T, b: T) => boolean): boolean

Building Plugins

The library includes a codify-build CLI tool for plugin development:

# Generate plugin documentation and validate schemas
npx codify-build

This tool expects a plugin implementation with a src/resources/ directory structure.

Development

# Install dependencies
npm install

# Run tests
npm test

# Build
npx tsc

# Lint
npx eslint src/

Examples

See the @codifycli/plugin-core tests for more examples:

  • src/plugin/plugin.test.ts - Plugin lifecycle
  • src/resource/resource-controller.test.ts - Resource operations
  • src/plan/plan.test.ts - Plan calculation
  • src/stateful-parameter/stateful-parameter-controller.test.ts - Stateful parameters

License

ISC