# 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:**
```typescript
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:
```typescript
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`:
```json
{
  "dependencies": {
    "chokidar": "^3.5.3"
  }
}
```

### 2.2 Create Skills Watcher Module

**File:** `src/skills/watcher.ts`

**Interface:**
```typescript
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()`:
```typescript
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:
```typescript
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:
```typescript
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:
```typescript
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:**
```typescript
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`:
```typescript
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:**
```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:**
```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`):**
```yaml
skills:
  load:
    watch: true  # Enable skills watcher
    watch_debounce_ms: 250  # Debounce file changes
```

### Add Package Manager Preference

**Optional config:**
```yaml
skills:
  install:
    package_manager: pnpm  # Preferred manager: npm | pnpm | yarn | bun
```

---

## Git Workflow

### Branch Setup

```bash
# 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
```bash
# 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

- OpenClaw Skills Documentation: https://docs.openclaw.ai/tools/skills.md
- ClawHub: https://clawhub.ai
- Chokidar: https://github.com/paulmillr/chokidar
- Flynn Skills System: `/home/will/lab/flynn/src/skills/`
- Previous Gap Analysis: `/home/will/lab/flynn/docs/plans/2026-02-06-openclaw-feature-gap-analysis.md`

---

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