Skip to content

[persona-kit 4/8] Migrate workforce CLI to use @agentworkforce/persona-kit #67

@willwashburn

Description

@willwashburn

Part of the persona-kit consolidation. Depends on #66. This is issue 4 of 8.

Goal

Rewrite packages/cli/ to consume @agentworkforce/persona-kit's top-level orchestration API (buildPersonaSpawnPlan + executePersonaSpawnPlan) instead of the imperative skills/mount/sidecar/input wiring it has today. CLI becomes a thin shell over persona-kit.

After this PR, the CLI's spawn flow reads top-to-bottom:

const loaded = loadPersonas({ cwd, searchDirs });
const spec = loaded.byId.get(personaId);
const persona = resolvePersonaTier(spec, tier);
const plan = buildPersonaSpawnPlan(persona, { cwd, installRoot, envOverrides });
const handle = await executePersonaSpawnPlan(plan, { cwd });
try {
  const child = childProcess.spawn(plan.cli, plan.args, { cwd, env: plan.env, stdio: 'inherit' });
  await waitForExit(child);
} finally {
  await handle.dispose();
}

Everything fancy that's currently in cli.ts (sidecar resolution, mount apply, skills install, input rendering, configfile materialization) is now persona-kit's job.

Files to touch

packages/cli/src/local-personas.ts

  • Delete inline persona-loading logic; import loadPersonas and resolvePersonaTier from @agentworkforce/persona-kit instead.
  • Keep CLI-specific concerns: persona-picker UX, override-merging UX (printing what got overridden, etc.), warnings collection.
  • The cascade-search-dirs configuration stays here (CLI knows about $AGENT_WORKFORCE_HOME etc.); pass the resolved dirs into persona-kit's loadPersonas.

packages/cli/src/cli.ts

  • Replace the inline skills install loop with a call to executePersonaSpawnPlan (or runSkillInstalls if the CLI wants its own orchestration for progress UX — pick one and stick with it; default to the top-level executor).
  • Replace inline sidecar writes with persona-kit's writePersonaSidecars.
  • Replace inline mount apply with persona-kit's applyPersonaMount.
  • Replace inline buildInteractiveSpec calls with buildPersonaSpawnPlan (which composes them).
  • Replace inline input env-binding with plan.env.
  • The CLI keeps: argv parsing (commander/yargs/whatever), agentworkforce subcommands, persona-picker prompts, session recording, routing-profile lookup (still imported from @agentworkforce/workload-router).
  • Drop direct imports of @agentworkforce/harness-kit — everything is via persona-kit now.

Other CLI files

  • grep for any other @agentworkforce/harness-kit or persona-symbol imports from @agentworkforce/workload-router and rewire to persona-kit.

packages/cli/package.json

  • Add @agentworkforce/persona-kit as a workspace dep.
  • Drop @agentworkforce/harness-kit (workload-router still needed for routing profiles + persona catalog).

Tasks

  • Rewrite local-personas.ts to delegate to loadPersonas + resolvePersonaTier.
  • Rewrite the spawn flow in cli.ts to use buildPersonaSpawnPlan + executePersonaSpawnPlan.
  • Verify the CLI's UX is unchanged: persona-picker prompts, error messages, and progress output should look the same. (Persona-kit's executor may need a progress callback — if so, define a small onProgress?: (event: ProgressEvent) => void option on ExecuteOptions and add it in this PR. Document the event shape.)
  • grep for residual @agentworkforce/harness-kit imports inside packages/cli/; remove all.
  • Update CLI's package.json deps.
  • Update any CLI-internal types that referenced harness-kit / workload-router persona types — point them at persona-kit instead.

Tests / Smoke checks

  • pnpm --filter @agentworkforce/cli test passes.
  • agentworkforce <persona-id> for a persona that exercises every field (skills, mount, claudeMdContent, inputs) — actually run it interactively against:
    • claude harness
    • codex harness
    • opencode harness
  • Verify the shell-level outputs match pre-migration: same npx skills add commands run, same files written, same harness argv.
  • Failure-path: a persona declaring a hallucinated skill source aborts with a friendly error message and no orphan child process before the harness would've spawned.
  • executePersonaSpawnPlan(...).dispose() (called via the CLI's exit handler) leaves the cwd in the same state it was in before the run — no .claude/skills/<name>/ directories, no half-written sidecars.

Constraints

  • No user-visible UX regressions. If something looked, sounded, or smelled different to a CLI user before/after this PR, that's a bug.
  • harness-kit is still in the monorepo (gets deleted in 6/8). The CLI should not import from it after this PR; the package itself stays.
  • workload-router still exists and the CLI still imports personaCatalog and routing types from it. Don't touch those imports.

Verification

  • pnpm -r build and pnpm -r test pass.
  • CI workflows (if any) that run agentworkforce against fixture personas pass.
  • grep @agentworkforce/harness-kit inside packages/cli/ returns zero hits.

Reference

Plan section 1.4.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions