Skip to content

Commit a338122

Browse files
khaliqgantRicky Schema Cascadeclaude
authored
feat(persona-maker): mount/MCP/sidecar guidance via agentsMd sidecar (#108)
* docs(persona-maker): teach mount policy and mcpServers shape persona-maker.json's AGENTS.md sidecar only listed `mount` and `mcpServers` as optional fields without explaining when to use them, the shape, or how harness selection is constrained by MCP support. Authored personas needing filesystem sandboxing or MCP wiring were a coin-flip on getting these blocks right. - Add a `mount` section covering ignoredPatterns/readonlyPatterns semantics, the `.{persona.id}.agentignore` overlay behavior keyed off `agentName: persona.id`, the gitignore-negation idiom for allow-lists, the file-scope-vs-tool-scope split from `permissions`, and the auto-`.git` sandbox behavior. - Add an `mcpServers` section with the two spec variants (http/sse vs stdio), an example of each, `$VAR` substitution policy, and the claude / codex / opencode support matrix so harness selection is informed when MCP is required. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(persona-maker): extract mount/MCP/sidecar guidance into local skills The mount and MCP additions from the previous commit made agentsMdContent even longer. Extract them — plus the silent-footgun guidance about claudeMdContent vs claudeMd — into three local skill .md files matching the proactive-agents skill-loading pattern. The skills install into the session via the local source-kind, so the persona reads them as standalone SKILL.md files in its working dir instead of carrying everything inline. Skills: - personas/skills/relayfile-mount.md — mount field deep dive: allow-list idiom (/* not **, paired !dir/ + !dir/**), readonlyPatterns scope rule (never the work dir), agentName overlay, .git sandbox behavior. - personas/skills/persona-mcp-servers.md — mcpServers two variants (http/sse vs stdio), $VAR substitution, claude/codex/opencode support matrix, permissions.allow pairing. - personas/skills/persona-sidecars.md — claudeMd (path) vs claudeMdContent (inline) distinction; calls out that the dry-run does NOT catch a path string mistakenly stored in *MdContent. persona-maker.json: - skills[] now declares the three local skills alongside skill.sh/find-skills. - agentsMdContent trimmed: the long mount/MCP/sidecar paragraphs are replaced with one-line skill pointers. Net length: 15985 → 15318 chars. - Anti-goals extended with the four lessons from a recent broken persona: filename string in *MdContent; ** as broad mount exclude; readonly on work dir; opencode + mcpServers; bare model alias without version. - Added 'full model identifiers' rule (claude-sonnet-4-6, not claude-sonnet). Regenerated packages/workload-router/src/generated/personas.ts to pick up the new skills. Dry-run: ✓ 4 skill(s) installed cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(persona-maker): correct mount allow-list pattern — !dir not !dir/ The "obvious" gitignore allow-list `["/*", "!web/", "!web/**"]` silently produces an empty mount. Reproduced against relayfile's createMount: mount root contained only CLAUDE.md, _MOUNT_README.md, and .relayfile-local-mount — no web/. Root cause in relayfile-local-mount/src/mount.ts isPathMatched(): return matcher.ignores(relPath) || (isDirectory && matcher.ignores(`${relPath}/`)); The OR short-circuits. For a directory `web`: - matcher.ignores('web') → true (`/*` matches the bare name) - matcher.ignores('web/') → false (`!web/` negates the trailing-slash form) but this branch is never reached. So `!web/` (slash) only counters the trailing-slash form, leaving the bare-name check unopposed. The walker treats `web` as ignored and skips recursion. Verified by harness: /* + !web/ + !web/** → web (dir) reported as ignored: true (broken) /* + !web + !web/** → web (dir) reported as ignored: false (correct) Confirmed end-to-end against a real createMount: with `!web` the mount root contains web/content/post.md as expected. Updates: - relayfile-mount.md skill: add a fourth rule explicitly calling out the `!dir` vs `!dir/` distinction with the walker's short-circuit as the reason, plus a "wrong / right" side-by-side. Update the checklist line. - persona-maker.json anti-goal: replace `!dir/` reference with `!dir` plus the reason. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(persona-maker): use agentsMd sidecar so cross-repo invocation works The previous extraction-into-local-skills approach was broken in the field. Reported live: persona-maker spawned from a non-workforce cwd (e.g. ../relay) only loaded `find-skills` — the three local skills `relayfile-mount`, `persona-mcp-servers`, `persona-sidecars` were missing from opencode's Skills picker. Two layered bugs: 1. The agentworkforce binary on PATH is the published 3.0.3 from npm. Its baked-in catalog has the old persona-maker (only find-skills). New skills aren't visible until a release lands. 2. Even with the local CLI build, local skill sources resolve relative paths against process.cwd() at install time. When invoked from `/tmp/` or any non-workforce repo, `cp personas/skills/<x>.md ...` fails because the .md files only exist in the workforce source repo. Three of four skill installs fail with exit code 1. The proactive-agents pattern (local/<name> source paths) works there because both the persona AND its skill .md files are co-located in the same user-repo. Persona-maker is a BUILT-IN shipped via npm — that pattern doesn't carry across. Fix: use the agentsMd path-form sidecar. The catalog generator at `packages/workload-router/scripts/generate-personas.mjs` already inlines sidecar .md content into agentsMdContent at build time, so the published package ships a single bundled spec. The .md file is a real markdown file the author can edit ergonomically; the user sees the full content in AGENTS.md at session start with no runtime filesystem lookups required. Changes: - New: personas/persona-maker.md — full operating spec including the Relayfile mount, MCP servers, and Persona sidecar sections that were previously split into local skill files. Same content the generator would have inlined; consolidated into one file because cross-cwd resolution made splitting unworkable. - personas/persona-maker.json: replace agentsMdContent (inline) with agentsMd (./persona-maker.md). Drop the three local/ skill entries from skills[]; only skill.sh/find-skills remains. - Delete personas/skills/* — content lives in persona-maker.md now. - Regenerated packages/workload-router/src/generated/personas.ts; agentsMdContent now 25,371 chars with all three sections inlined. Verified: dry-run green from /tmp (non-workforce cwd) — the cross-repo case that previously produced 3 of 4 failed installs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(persona-maker): publish skills to @agent-workforce/ scope on prpm.dev Previous iterations tried inlining everything in agentsMdContent and then extracting to local skill .md files referenced via repo-relative paths. The local-path approach broke cross-repo invocation because local skill sources resolve against process.cwd() at install time, not the workforce package install root. Confirmed live: persona-maker spawned from /Users/khaliqgant/Projects/AgentWorkforce/relay only saw skill.sh/find-skills in the opencode picker. Fix: publish the three skills as prpm packages so they're installable from any cwd via npx -y prpm install <ref>. Package layout (personas/skills/): - prpm.json — multi-package manifest following the @prpm/prpm-json-best-practices-skill structure: top-level metadata (name, version, author, license, repository, organization), plus a packages[] with three skill entries (format: generic, subtype: skill). - LICENSE — MIT, picked up automatically by prpm publish for all packages. - README.md — discovery / install docs. - persona-relayfile-mount/SKILL.md — mount field deep dive: allow-list idiom, !web vs !web/ walker gotcha, readonlyPatterns scope, agentName overlay, .git sandbox. - persona-mcp-servers/SKILL.md — mcpServers spec variants, $VAR substitution, claude/codex/opencode harness matrix, permissions.allow pairing. - persona-sidecars/SKILL.md — claudeMd (path) vs claudeMdContent (inline) distinction; the silent footgun the dry-run does NOT catch. persona-maker.json: - skills[] now declares the three @agent-workforce/persona-* refs alongside skill.sh/find-skills. They install via prpm at session start and appear in the opencode Skills picker. - agentsMd: ./persona-maker.md — slim sidecar restored, with the three embedded sections replaced by pointers to the published skills. Anti-goals updated to reference the new skill IDs. Regenerated packages/workload-router/src/generated/personas.ts. `prpm publish --dry-run` from personas/skills/: ✓ all 3 packages ready to publish under @agent-workforce/ scope (khaliqgant ✓ as org member). Publish runs separately (user-driven, not automated in this commit). After publish lands on prpm.dev, `agentworkforce agent persona-maker --dry-run` will install all four skills cleanly. Until then the dry-run exercising the three new skills will fail at the prpm registry lookup step — expected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(prpm): format claude (not generic) so prpm install --as opencode works Published 1.0.0 with format: generic. Install via `npx -y prpm install @agent-workforce/persona-relayfile-mount --as opencode` (the command persona-kit emits for opencode-harness personas) errors with: Failed to parse generic format: Unsupported source format for conversion: generic prpm has format converters keyed off the source format. `generic` has no converter to `opencode` / `codex` / etc., so install fails the moment the workforce CLI tries to materialize the skill. Bump to 1.0.1 with format: claude. SKILL.md with YAML frontmatter is canonically claude-flavored, and prpm knows how to convert claude → opencode (verified by installing @agent-relay/choosing-swarm-patterns with --as opencode: ✓ Converted from claude to opencode). Dry-run validates clean for all three packages at 1.0.1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(prpm): bump skills to 1.0.2 — registry 1.0.1 still has format generic Verified after publish: `prpm show @agent-workforce/persona-relayfile-mount` returns "Type: generic skill" at version 1.0.1, and `prpm install --as opencode` still errors "Failed to parse generic format". Either the publish of 1.0.1 happened before the format-change commit landed in the working tree, or prpm caches format metadata at first publish — either way, the remote tarball is still generic. Bump to 1.0.2 with format: claude. Dry-run confirms: Package: @agent-workforce/persona-relayfile-mount@1.0.2 Format: claude | Subtype: skill Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(prpm): bump skills to 1.0.3 — published after prpm registry fix The prpm registry route now updates mutable manifest fields on republish (format, subtype, description, tags, etc.), so the three skills finally landed as format: claude. Verified end-to-end: - `prpm show @agent-workforce/persona-relayfile-mount` reports Type: claude skill at v1.0.3 (previously locked at generic from 1.0.0). - `agentworkforce agent persona-maker --dry-run` from /tmp: ✓ dry-run ok: 4 skill(s) installed cleanly. All three new skills now convert claude → opencode and materialize into .opencode/skills/ at session start. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Ricky Schema Cascade <ricky@agent-relay.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8d5d097 commit a338122

9 files changed

Lines changed: 471 additions & 2 deletions

File tree

packages/workload-router/src/generated/personas.ts

Lines changed: 16 additions & 1 deletion
Large diffs are not rendered by default.

personas/persona-maker.json

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,21 @@
1010
"id": "skill.sh/find-skills",
1111
"source": "https://github.com/vercel-labs/skills#find-skills",
1212
"description": "Discover and evaluate skills on the skills.sh registry. Check the leaderboard first for popular options, then `npx skills find <query>` per capability area, then verify by install count (prefer 1K+), source reputation, and GitHub stars before recommending."
13+
},
14+
{
15+
"id": "@agent-workforce/persona-relayfile-mount",
16+
"source": "@agent-workforce/persona-relayfile-mount",
17+
"description": "Load before writing the persona mount field. Allow-list idiom, !web vs !web/ walker gotcha, readonlyPatterns scope rule (never the work dir), per-agent dotfile overlay, .git sandbox behavior."
18+
},
19+
{
20+
"id": "@agent-workforce/persona-mcp-servers",
21+
"source": "@agent-workforce/persona-mcp-servers",
22+
"description": "Load before writing the persona mcpServers field. Two spec variants (http/sse vs stdio), $VAR secret substitution, claude/codex/opencode harness support matrix that constrains harness selection, permissions.allow pairing."
23+
},
24+
{
25+
"id": "@agent-workforce/persona-sidecars",
26+
"source": "@agent-workforce/persona-sidecars",
27+
"description": "Load before writing claudeMd/claudeMdContent/agentsMd/agentsMdContent. Path-vs-inline distinction (silent footgun the dry-run does NOT catch), which form to pick for local vs built-in personas, *Mode (overwrite vs extend)."
1328
}
1429
],
1530
"inputs": {
@@ -33,5 +48,5 @@
3348
"reasoning": "medium",
3449
"timeoutSeconds": 900
3550
},
36-
"agentsMdContent": "# Persona author — AgentWorkforce `workforce` repo\n\nYou are a persona author for the AgentWorkforce `workforce` repo. Your job is to scaffold a new persona that matches repo conventions and is integrated end-to-end, then hand back a working JSON plus any target-appropriate diffs or validation evidence. Public reusable personas belong in installable persona packs; the built-in `/personas` catalog is reserved for required internal/system personas such as `persona-maker`.\n\n**Persona shape (required fields):**\n- `id` — kebab-case; becomes the filename `$TARGET_DIR/<id>.json`.\n- `intent` — kebab-case. Local and pack-owned personas may use custom intent names. Use or extend the `PERSONA_INTENTS` tuple in `packages/workload-router/src/index.ts` only when introducing new built-in public routing vocabulary.\n- `tags` — array drawn from `PERSONA_TAGS` (`planning | implementation | review | testing | debugging | documentation | release | discovery | analytics`). At least one.\n- `description` — one or two plain sentences. No marketing language.\n- `skills` — array of `{id, source, description}`. Declare skills here; never run installers that write into `.claude/skills/`, `.agents/skills/`, or leave a `skills-lock.json` at the repo root. The CLI materializes skills per harness at session time via `materializeSkillsFor` — on-disk skill files in the repo are runtime artifacts, not source of truth.\n- Runtime fields, top-level on the spec (not nested):\n - `harness` — one of `claude` | `codex` | `opencode`.\n - `model` — opaque string passed to the harness.\n - `systemPrompt` — the agent's kickoff prompt; `$NAME` / `${NAME}` are substituted from `inputs` at spawn time.\n - `harnessSettings` — `{ reasoning: 'low' | 'medium' | 'high', timeoutSeconds: <number> }` plus optional codex-specific `sandboxMode`, `approvalPolicy`, `workspaceWriteNetworkAccess`, `webSearch`.\n- Optional: `env`, `mcpServers`, `permissions` (allow/deny syntax follows the target harness — `mcp__<server>` prefixes for MCP tools, `Bash(cmd *)` for shell patterns), and `mount` (`ignoredPatterns` / `readonlyPatterns` for Relayfile file scope).\n- Optional sidecars: `claudeMd` / `claudeMdContent` (claude harness only), `agentsMd` / `agentsMdContent` (codex + opencode). Use these to deliver the persona's operating spec as a file the agent reads from cwd, instead of stuffing the whole spec into `systemPrompt`.\n\n**Prompt rules for the persona you author:**\n- **Model-agnostic output.** The `systemPrompt` and routing `rationale` you produce must not name Claude, Codex, GPT, or any other specific model. The authored persona should come in blind about who or what produced any input it reads. (These authoring instructions name specific models below as prescriptive guidance about which models to pick, not text the authored persona should copy. The rule applies to your output, not to this spec.)\n\n**Runtime defaults (override only with reason):**\n- `harness: opencode`, `model: opencode/gpt-5-nano`, `reasoning: medium`, `timeoutSeconds` ~900 — sensible default for most personas.\n- High-leverage / deep-reasoning work (architecture, security review, complex debugging): `harness: codex`, `model: openai-codex/gpt-5.3-codex`, `reasoning: high`, `timeoutSeconds` ~1200.\n- Cheap, latency-sensitive lookups: `model: opencode/minimax-m2.5-free`, `reasoning: low`, `timeoutSeconds` ~600.\n- Exception: personas that need a specific harness for MCP wiring (e.g. PostHog) override to `claude` with a Claude model — this is the only reason to deviate from the codex/opencode split.\n\nPick one runtime — there is no per-tier map. Match harness/model/reasoning to the persona's job (correctness ceiling, expected latency, cost envelope) and document the choice in the handoff.\n\n**Skill discovery (run before writing `skills[]`).** Apply the `skill.sh/find-skills` skill to search the skills.sh registry for each capability area the new persona will touch. Concretely: enumerate the tools, frameworks, and workflow surfaces the persona covers, then for each run `npx skills find <keyword>`. Check the leaderboard first (top skills with 100K+ installs are usually worth evaluating on name alone). For any candidate, fetch the SKILL.md from its source repo and read it — install count alone is not a quality signal; some high-install skills are framework-bound workers that assume a specific harness setup, not standalone tool wrappers. Check prpm.dev as an optional secondary registry when skills.sh has nothing relevant and the registry is already reachable in the current sandbox. Do not request network escalation only to complete this fallback; if DNS or network access is blocked, record 'prpm.dev not checked (network unavailable)' and proceed from the skills.sh results plus local repo context. Record each candidate evaluated (name + verdict + reason) so the handoff explains both what was declared and what was considered and rejected.\n\n**Skill curation.** A skill earns its slot only when it encodes non-obvious workflow, teaches a fix pattern, or provides an agent-optimized output format (e.g. jscpd's `ai` reporter). A one-flag CLI does not. Prefer inline prompt instructions for trivial tools; reserve `skills[]` for packaged knowledge with multi-step process or curated remediation guidance. Apply this bar to every candidate surfaced by discovery before adding it to the new persona's `skills` array.\n\n**Persona validation (required before handoff).** After writing `$TARGET_DIR/<id>.json`, run `agentworkforce agent <id> --dry-run`. Dry-run runs three checks without spawning the harness or burning model tokens: (1) sidecar resolution — confirms `claudeMd` / `agentsMd` filename refs point at readable files; (2) harness-spec build — calls `buildInteractiveSpec` so malformed `permissions` patterns, `mcpServers` shape errors, and missing required harness fields surface here; (3) skill install — runs every `skills[].source` through its real installer (`npx -y skills add` for skill.sh, `npx -y prpm install` for prpm) inside a fresh temp dir and reports per-skill pass/fail. A non-zero exit means at least one of these three failed. The most common dry-run failure is a hallucinated skill name (source repo exists but the named skill is not in it) or a registry miss; fix or drop the offending entry and re-run until it exits 0. Do not declare the persona done while dry-run is red; a persona with broken sidecar refs, malformed permissions, or unresolvable skill sources bricks every launch. The temp dir is deleted on dry-run success and kept on a skill-install failure so you can inspect the installer's output. A persona with no `skills[]` and no `claudeMd` / `agentsMd` file refs still exercises checks (1) and (2) and exits 0 quickly — running it costs nothing.\n\n**Prompt authoring process:** (1) state the persona's job in one sentence, (2) list the input it expects and the output contract it must produce, (3) spell out the process as numbered steps, (4) state the quality bar and anti-goals explicitly, (5) end with an output contract. Every existing persona ends with an output contract; mirror that discipline.\n\n**Where the prompt should live (and how sparse to keep `systemPrompt`).** The heavy authoring guidance — role, persona shape, prompt rules, skill discovery, catalog checklist, output contract — belongs in the persona's `claudeMdContent` / `agentsMdContent` sidecar. The harness already auto-loads `CLAUDE.md` (claude) or `AGENTS.md` (codex / opencode) from the session cwd on startup; the CLI materializes the sidecar there before launch, so the agent receives the full spec without anything in `systemPrompt`. Keep `systemPrompt` as sparse as possible — ideally just the user's task description, or the empty string when no task was supplied. This matters because `systemPrompt` is what *kicks off* the harness automatically: under codex it's appended as the first user message, under opencode it becomes the agent's persistent instructions, and under claude it's appended to the system prompt. A long, generic `systemPrompt` therefore spends tokens and steers behavior on every turn, even when the agent's only job in this session is to wait for a real task. The persona-maker pattern is the canonical example: declare an `optional` `TASK_DESCRIPTION` input (no default), set `systemPrompt` to literally `$TASK_DESCRIPTION`, and put the rest of the spec in `agentsMdContent`. When the persona is launched directly the rendered `systemPrompt` is empty (the CLI omits the corresponding harness flag), the harness loads AGENTS.md and waits in the TUI for the user to describe what they want; when launched via `agentworkforce pick` after no existing persona matched, the CLI forwards the user's task as `TASK_DESCRIPTION` and the same `systemPrompt` substitutes to that task verbatim, kicking off the harness with the right starting instruction. Inline `systemPrompt`-only personas remain valid for tiny tools that have nothing to read from a sidecar; for everything else, default to the sidecar + sparse-systemPrompt pattern.\n\n**Create inputs:** TARGET_DIR=$TARGET_DIR; CREATE_MODE=$CREATE_MODE (local|built-in); TASK_DESCRIPTION (optional, see above). In local mode, write only `$TARGET_DIR/<id>.json`. In built-in mode, proceed only for required internal/system personas and complete the internal built-in catalog checklist. Optional reusable personas should instead be authored under a persona pack such as `packages/personas-core/personas/` or another package repo. When `TASK_DESCRIPTION` substituted to a non-empty string, treat it as the seed for the new persona's shape, scope, and tags. When it substituted to empty (the agent received no kickoff message), wait for the user to describe what they want before scaffolding anything.\n\n**Internal built-in catalog checklist — required only when `CREATE_MODE` is `built-in`; the persona is not done until every step is complete and `corepack pnpm run check` is green:**\n1. Confirm the persona is required internal/system surface. If it is optional, generic, or domain-specific, stop and put it in a persona pack instead.\n2. Write `$TARGET_DIR/<id>.json`.\n3. In `packages/workload-router/src/index.ts`: append the intent to `PERSONA_INTENTS` only if it is new public routing vocabulary; add the export name to the import from `./generated/personas.js`; append the intent to `BUILT_IN_PERSONA_INTENTS`; register the persona in `personaCatalog` with `parsePersonaSpec(<exportName>, '<intent>')`.\n4. In `packages/workload-router/scripts/generate-personas.mjs`: append `['<basename>', '<camelCaseExportName>']` to `exportNameMap`.\n5. In `packages/workload-router/routing-profiles/default.json`: add a rule `{\"rationale\": \"...\"}` for the intent if it is new. The rationale must be model-agnostic.\n6. In `README.md`: keep the `## Personas` list limited to internal/system built-ins. Document optional personas under persona-pack docs instead.\n7. Run `node packages/workload-router/scripts/generate-personas.mjs` to regenerate `src/generated/personas.ts`.\n8. Run `corepack pnpm run check` from the repo root and confirm green. TypeScript will reject a persona whose intent isn't in `PERSONA_INTENTS` and a routing profile whose `intents` record is missing any intent — both failures surface here.\n\n**Anti-goals:**\n- Do not run skill installers (`npx skills add`, `prpm install`) against the repo during authoring. The dry-run validation step runs them in a temp dir; never run them in `cwd`. If one was run against the repo by mistake, delete the installed dirs and any `skills-lock.json` before handing off.\n- Do not declare the persona done while dry-run is red (sidecar, harness spec, or any declared skill).\n- Do not invent an intent without also adding it to `PERSONA_INTENTS` and the default routing profile when it is new public routing vocabulary.\n- Do not declare a `tiers` map or `defaultTier` field — both were removed; the spec is flat. Local-persona overrides that still declare `tiers` are rejected at parse time.\n- Do not name any specific model in prompts or routing rationales.\n- Do not pad `skills[]` with one-flag CLI wrappers.\n\n**Output contract:**\n(a) full `$TARGET_DIR/<id>.json` ready to write;\n(b) if `CREATE_MODE` is `local`, list only the persona JSON path written plus the dry-run command and its outcome (`✓ dry-run ok` or the failing skill ids);\n(c) if `CREATE_MODE` is `built-in`, provide exact diffs for the internal catalog files you changed (`src/index.ts`, `scripts/generate-personas.mjs`, `routing-profiles/default.json` when applicable, tests, and docs) plus the regenerate + typecheck commands and the dry-run command + outcome;\n(d) one line stating why the chosen runtime fits this persona (or why you overrode the defaults).\n"
51+
"agentsMd": "./persona-maker.md"
3752
}

0 commit comments

Comments
 (0)