Files
flynn/docs/plans/2026-02-11-skills-infrastructure-plan.md
William Valentin fae3565480 docs(skills): add skills infrastructure plan
- Three-phase plan for skills system improvements
- Phase 1: Command Dispatch (flynn skills CLI commands)
- Phase 2: Skills Watcher (auto-reload with chokidar)
- Phase 3: Installer Specs (auto-install brew/node/go/download)
- Model strategy: glm-4.7-flash for mechanical, glm-4.7 for complex
- Estimated 8-11 hours total
2026-02-11 14:48:21 -08:00

19 KiB

Skills Infrastructure Improvement Plan

Created: 2026-02-11 Branch: feature/skills-infrastructure Goal: Improve Flynn's skills system to match OpenClaw's maintenance capabilities Model Strategy: Use glm-4.7-flash for mechanical tasks, glm-4.7 for complex tasks/orchestration


Executive Summary

This plan implements three critical infrastructure improvements to Flynn's skills system:

  1. Command Dispatch Mode - Expose flynn skills CLI commands for skill management
  2. Skills Watcher - Auto-reload skills when files change
  3. Installer Specs - Auto-install dependencies (brew/node/go) when installing skills

Approach: Infrastructure first, then integrate high-value skills from ClawHub in future phases.


Prioritization Rationale

Based on user decisions:

  • Feature set: All three P0-P1 features (command dispatch, watcher, installer specs)
  • Approach: Infrastructure first - build solid foundation before adding skills
  • Skill format: Keep manifest.json (more explicit than YAML frontmatter)
  • Agent isolation: Defer - focus on single-agent optimization
  • Skill registry: Keep private - no public FlyHub

Phase 1: Command Dispatch Mode (P0)

Impact: High (users can manage skills without manual file operations) Complexity: Low (exposes existing SkillInstaller/SkillRegistry) Estimated effort: 2-3 hours

1.1 Create CLI Skills Module

File: src/cli/skills.ts

Structure:

import { Command } from 'commander';
import { SkillRegistry } from '../skills/registry.js';
import { SkillInstaller } from '../skills/installer.js';
import { loadAllSkills } from '../skills/loader.js';

export const skillsCommand = new Command('skills')
  .description('Manage Flynn skills')
  .action(async () => {
    await skillsCommand.outputHelp();
  });

// Subcommands will be registered here

Subcommands to implement:

flynn skills list

  • List all registered skills
  • Show tier (bundled/managed/workspace)
  • Show availability status (available/unavailable with reasons)
  • Output format: table (default) or JSON (with --json flag)

flynn skills info <name>

  • Show detailed information about a specific skill
  • Display: name, description, version, author, tier, requirements, tools
  • Show availability status and reasons if unavailable
  • Show directory path

flynn skills install <path>

  • Install a skill from a local directory or remote URL
  • Validate skill structure (manifest.json + SKILL.md)
  • Run requirement checks
  • Install to skills.managed_dir (default: ~/.flynn/workspace/skills)
  • Register with SkillRegistry
  • Support --force flag to overwrite existing

flynn skills uninstall <name>

  • Remove skill from managed directory
  • Unregister from SkillRegistry
  • Confirm before deletion unless --yes flag
  • Check if skill is bundled (deny uninstallation of bundled skills)

flynn skills refresh

  • Reload all skills from disk
  • Update SkillRegistry
  • Show count of skills loaded/removed

1.2 Register Skills Command

File: src/cli/index.ts

Add skills command to CLI:

import { skillsCommand } from './skills.js';

program.addCommand(skillsCommand);

1.3 Update Doctor Checks

File: src/cli/doctor.ts

Enhance skills check (line 192-198):

  • Show per-skill availability details
  • Report missing binaries with which output
  • Report missing environment variables
  • Suggest installation commands

1.4 Tests

File: src/cli/skills.test.ts

Test coverage for all subcommands:

  • skills list - output format, filtering
  • skills info - single skill, non-existent skill
  • skills install - local path, remote URL, duplicate, force
  • skills uninstall - normal, bundled skill denial, non-existent
  • skills refresh - registry update

Phase 2: Skills Watcher (P1)

Impact: Medium (hot-reload skills without daemon restart) Complexity: Medium (integrate file watcher into daemon lifecycle) Estimated effort: 3-4 hours

2.1 Install Dependencies

Add to package.json:

{
  "dependencies": {
    "chokidar": "^3.5.3"
  }
}

2.2 Create Skills Watcher Module

File: src/skills/watcher.ts

Interface:

import { SkillRegistry } from './registry.js';
import type { Skill } from './types.js';

export interface SkillsWatcherConfig {
  registry: SkillRegistry;
  skillDirs: string[];  // bundled, managed, workspace
  debounceMs?: number;  // default: 250
  onSkillChange?: (skill: Skill, action: 'add' | 'update' | 'remove') => void;
}

export class SkillsWatcher {
  constructor(config: SkillsWatcherConfig);
  start(): void;
  stop(): void;
  reloadSkill(path: string): void;
  reloadAll(): void;
}

Implementation:

  • Use chokidar to watch all three skill directories
  • Debounce file changes (configurable, default 250ms)
  • Watch for: SKILL.md, manifest.json, directory add/remove
  • On change: call loadSkill() and register/unregister with SkillRegistry
  • Emit events via callback for logging/debugging

2.3 Integrate Watcher into Daemon

File: src/daemon/services.ts

Modify initSkills():

import { SkillsWatcher } from '../skills/watcher.js';

async function initSkills(config: Config): Promise<void> {
  // Existing skill loading code
  const skills = loadAllSkills({
    bundledDir: config.skills.bundled_dir,
    managedDir: config.skills.managed_dir,
    workspaceDir: config.skills.workspace_dir,
  });

  skills.forEach(skill => skillRegistry.register(skill));

  // NEW: Start watcher if enabled
  if (config.skills.load?.watch) {
    const watcher = new SkillsWatcher({
      registry: skillRegistry,
      skillDirs: [
        config.skills.bundled_dir,
        config.skills.managed_dir,
        config.skills.workspace_dir,
      ].filter(Boolean) as string[],
      debounceMs: config.skills.load.watch_debounce_ms || 250,
      onSkillChange: (skill, action) => {
        console.log(`Skill ${skill.manifest.name} ${action}d`);
      },
    });
    watcher.start();
    // Store watcher reference for cleanup
    (global as any).skillsWatcher = watcher;
  }
}

Add cleanup in shutdown handler:

process.on('SIGTERM', () => {
  const watcher = (global as any).skillsWatcher;
  if (watcher) {
    watcher.stop();
  }
  // ... existing cleanup
});

2.4 Add Configuration Schema

File: src/config/schema.ts

Add to SkillsLoadConfig:

skills_load_config: z.object({
  watch: z.boolean().default(false),
  watch_debounce_ms: z.number().default(250),
})

2.5 Tests

File: src/skills/watcher.test.ts

Test coverage:

  • Watcher initialization
  • File change detection (add skill)
  • File change detection (update skill)
  • File change detection (remove skill)
  • Debounce behavior
  • Multiple skill directories
  • Stop/start lifecycle

Phase 3: Installer Specs (P1)

Impact: Medium (automatic dependency installation) Complexity: Medium (detect package managers, run install commands) Estimated effort: 3-4 hours

3.1 Update SkillManifest Type

File: src/skills/types.ts

Add installer specs to manifest:

export interface SkillManifest {
  // ... existing fields
  /** Installation specifications for automatic dependency installation */
  installers?: SkillInstaller[];
}

export type SkillInstallerKind = 'brew' | 'node' | 'go' | 'download' | 'manual';

export interface SkillInstaller {
  /** Installer type */
  kind: SkillInstallerKind;
  /** Display label shown to user */
  label: string;
  /** For brew: formula name. For node: package name. For go: import path. */
  package?: string;
  /** For download: URL to download */
  url?: string;
  /** For download: archive format (tar.gz, zip, etc.) */
  archive?: string;
  /** For download: extract directory */
  extract?: boolean;
  /** Binaries to verify after installation */
  bins?: string[];
  /** OS platforms this installer supports */
  os?: string[];
}

3.2 Create Package Manager Detector

File: src/skills/package-manager.ts

Interface:

export type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun';

export interface PackageManagerInfo {
  name: PackageManager;
  command: string;
  available: boolean;
}

export function detectPackageManager(): PackageManagerInfo | null;
export function runPackageInstall(pkgManager: PackageManager, packages: string[]): Promise<void>;

Implementation:

  • Detect available package managers (check npm --version, pnpm --version, etc.)
  • Prefer pnpm over npm, yarn, bun (matching Flynn's runtime preference)
  • Run install with correct command for detected manager
  • Capture output and errors

3.3 Extend SkillInstaller

File: src/skills/installer.ts

Add auto-installation logic:

installDependencies(skill: Skill): Promise<void>

Algorithm:

  1. Check if skill has installers in manifest
  2. Filter installers by current OS
  3. If multiple installers available, ask user to choose (or pick first)
  4. For each installer:
    • brew: Run brew install <formula>
    • node: Run <pm> install <package> (using detected package manager)
    • go: Check for go binary, install via brew if missing, then go install <import>
    • download: Download URL, extract archive, move to ~/.flynn/tools/<skill-name>/
  5. After installation, verify binaries (if specified) using which
  6. Log warnings for failed installations (don't block skill registration)

uninstallDependencies(skill: Skill): Promise<void>

Algorithm:

  1. Check if skill has installers
  2. Reverse installation:
    • brew: Run brew uninstall <formula>
    • node: Run <pm> uninstall <package>
    • go: Remove binary from GOBIN
    • download: Remove ~/.flynn/tools/<skill-name>/
  3. Ask user before removal unless --yes flag

3.4 Update CLI Install Command

File: src/cli/skills.ts

Enhance flynn skills install:

skillsCommand
  .command('install <path>')
  .description('Install a skill from path or URL')
  .option('--auto-install', 'Automatically install dependencies')
  .action(async (path, options) => {
    // Load skill from path
    const skill = loadSkill(path, 'managed');

    if (!skill) {
      console.error('Failed to load skill');
      return;
    }

    // Check requirements
    const { available, reasons } = checkRequirements(skill.manifest.requirements);
    if (!available) {
      console.error('Skill requirements not met:');
      reasons.forEach(reason => console.error(`  - ${reason}`));
      return;
    }

    // Install dependencies
    if (options.autoInstall || skill.manifest.installers) {
      console.log('Installing dependencies...');
      await installDependencies(skill);
    }

    // Install skill
    await skillInstaller.install(path);
  });

3.5 Create Example Skills

Create example skills to test installer specs:

Example 1: Skill with brew dependency

~/.flynn/workspace/skills/example-brew/
├── manifest.json
└── SKILL.md

manifest.json:

{
  "name": "example-brew",
  "description": "Example skill with brew dependency",
  "version": "1.0.0",
  "tier": "workspace",
  "installers": [
    {
      "kind": "brew",
      "label": "Install jq (brew)",
      "formula": "jq",
      "bins": ["jq"]
    }
  ]
}

Example 2: Skill with node dependency

~/.flynn/workspace/skills/example-node/
├── manifest.json
└── SKILL.md

manifest.json:

{
  "name": "example-node",
  "description": "Example skill with node dependency",
  "version": "1.0.0",
  "tier": "workspace",
  "installers": [
    {
      "kind": "node",
      "label": "Install typescript (npm)",
      "package": "typescript",
      "bins": ["tsc"]
    }
  ]
}

3.6 Tests

File: src/skills/package-manager.test.ts

Test coverage:

  • Package manager detection (npm, pnpm, yarn, bun)
  • Install execution
  • Error handling for missing package manager

File: src/skills/installer.test.ts (extend existing tests)

Additional test coverage:

  • installDependencies() with brew installer
  • installDependencies() with node installer
  • installDependencies() with go installer
  • installDependencies() with download installer
  • OS filtering for installers
  • uninstallDependencies()

Configuration Changes

Add Skills Watcher Settings

Default config (~/.flynn/flynn.yaml):

skills:
  load:
    watch: true  # Enable skills watcher
    watch_debounce_ms: 250  # Debounce file changes

Add Package Manager Preference

Optional config:

skills:
  install:
    package_manager: pnpm  # Preferred manager: npm | pnpm | yarn | bun

Git Workflow

Branch Setup

# Create feature branch
git checkout -b feature/skills-infrastructure

# Ensure we're up to date
git fetch origin
git rebase origin/main

Commit Strategy

Commit frequently with atomic changes:

  1. Initial commit:

    git add .
    git commit -m "feat(skills): add command dispatch mode
    
    - Create src/cli/skills.ts with subcommands
    - Add skills list/info/install/uninstall/refresh
    - Register skills command in CLI
    - Enhance doctor with per-skill details
    - Add CLI tests
    
  2. Second commit:

    git add src/skills/watcher.ts
    git add src/daemon/services.ts
    git add src/config/schema.ts
    git commit -m "feat(skills): add skills watcher for auto-reload
    
    - Implement SkillsWatcher class with chokidar
    - Watch bundled/managed/workspace directories
    - Debounce file changes (configurable)
    - Integrate watcher into daemon lifecycle
    - Add configuration schema for watcher
    - Add watcher tests
    
  3. Third commit:

    git add src/skills/types.ts
    git add src/skills/package-manager.ts
    git add src/skills/installer.ts
    git add src/cli/skills.ts
    git commit -m "feat(skills): add installer specs for auto-install
    
    - Add SkillInstaller type to manifest
    - Implement package manager detection
    - Extend installer with dependency installation
    - Support brew/node/go/download installers
    - Add CLI --auto-install flag
    - Add example skills for testing
    - Add package manager and installer tests
    
  4. Final commit:

    git add package.json
    git add package-lock.json
    git add .flynn/
    git add docs/
    git commit -m "docs(skills): update documentation and configuration
    
    - Add chokidar dependency
    - Add skills watcher config to default config
    - Create SKILLS.md documentation
    - Update CHANGELOG.md"
    

Documentation Updates

File: docs/plans/2026-02-11-skills-infrastructure-plan.md (this file)

File: CHANGELOG.md (update with new features)

File: README.md (update with skills CLI commands)


Testing Strategy

Unit Tests (already extensive)

  • src/skills/registry.test.ts - 11 tests
  • src/skills/loader.test.ts - 21 tests
  • src/skills/installer.test.ts - 13 tests (extend)
  • Total: 45+ tests

New Tests Required

  • src/cli/skills.test.ts - 20+ tests (CLI commands)
  • src/skills/watcher.test.ts - 15+ tests (file watching)
  • src/skills/package-manager.test.ts - 10+ tests (package managers)
  • Total: ~45 new tests

Integration Tests

  • Test skills watcher with real file system changes
  • Test installer specs with real package manager (use temp directories)
  • Test CLI commands end-to-end (install → list → info → uninstall)

Test Execution

# Run all tests
pnpm test:run

# Run specific test file
pnpm test:run src/skills/watcher.test.ts

# Run tests in watch mode during development
pnpm test

Future Phases (Post-Infrastructure)

After completing these three phases, the foundation will be solid for integrating skills from ClawHub. Suggested next steps:

Phase 4: Integrate Top 3 Skills from ClawHub

  1. github skill (12,020 downloads)

    • Wrap gh CLI as a tool
    • Implement: issues, PRs, CI runs, API queries
    • File: src/tools/github.ts
  2. self-improving-agent skill (17,007 downloads)

    • Enhance memory extraction hooks
    • Capture learnings from errors/corrections
    • Extend: src/backends/native/orchestrator.ts
  3. agent-browser skill (12,862 downloads)

    • Evaluate Rust-based browser CLI vs current Puppeteer
    • Consider performance improvements
    • File: src/tools/browser/ (enhance or replace)

Phase 5: Per-Agent Skill Isolation (P2)

Add skills.allow and skills.deny arrays to agent config:

  • Allowlist skills per agent
  • Modify getSystemPromptAdditions() to filter by agent
  • Update CLI to show per-agent skill sets

Phase 6: Session Skill Snapshot (P2)

Cache skill snapshot in session metadata:

  • Store skillNames[] in SessionStore
  • Use consistent skill set across compaction
  • Add snapshotSkills() method to SkillRegistry

Phase 7: Remote Skills Registry (P3) - Optional

Consider building a private or public skill registry:

  • Skill discovery API
  • Skill download and install
  • Version management
  • Requires infrastructure decision (keep private per user preference)

Success Criteria

Phase 1 (Command Dispatch)

  • flynn skills list shows all registered skills
  • flynn skills info <name> displays skill details
  • flynn skills install <path> installs local skill
  • flynn skills uninstall <name> removes skill
  • flynn skills refresh reloads skills
  • Doctor shows per-skill availability details
  • All CLI commands have test coverage
  • Help text is clear and complete

Phase 2 (Skills Watcher)

  • Skills reload automatically when SKILL.md changes
  • Skills reload automatically when manifest.json changes
  • Skills reload when skill directory is added/removed
  • File changes are debounced (configurable)
  • Watcher stops cleanly on daemon shutdown
  • Configuration toggle enables/disables watcher
  • All watcher features have test coverage

Phase 3 (Installer Specs)

  • Brew dependencies install automatically
  • Node dependencies install with detected package manager
  • Go dependencies install (including installing go if needed)
  • Download installers work (URL fetch + extract)
  • Installers respect OS filtering
  • Failed installations don't block skill registration
  • CLI --auto-install flag works
  • Example skills demonstrate all installer types
  • All installer features have test coverage

Overall

  • All tests pass (pnpm test:run)
  • Linting passes (pnpm lint)
  • Type checking passes (pnpm typecheck)
  • Documentation is updated
  • Change log is documented
  • Git history is clean (atomic commits)

Risks and Mitigations

Risk Likelihood Impact Mitigation
Chokidar file descriptor limits Low Medium Add warning in docs, recommend increasing ulimit
Package manager detection conflicts Low Low Prefer pnpm, fall back gracefully
Installer execution security Medium High Prompt for user confirmation before running installs
Watcher performance with many skills Low Low Debounce by default (250ms), make configurable
Circular dependency in installers Very Low High Manual review of manifest.json, no auto-resolution

References


Plan Status: Ready for implementation Estimated Total Effort: 8-11 hours Next Step: Create branch and begin Phase 1