@jaggerxtrm/specialists 3.3.1 → 3.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/config/hooks/specialists-complete.mjs +60 -0
  2. package/config/hooks/specialists-session-start.mjs +120 -0
  3. package/config/skills/specialists-creator/SKILL.md +506 -0
  4. package/config/skills/specialists-creator/scripts/validate-specialist.ts +41 -0
  5. package/config/skills/specialists-usage-workspace/iteration-1/eval-bead-background/old_skill/outputs/result.md +105 -0
  6. package/config/skills/specialists-usage-workspace/iteration-1/eval-bead-background/with_skill/outputs/result.md +93 -0
  7. package/config/skills/specialists-usage-workspace/iteration-1/eval-fresh-setup/old_skill/outputs/result.md +113 -0
  8. package/config/skills/specialists-usage-workspace/iteration-1/eval-fresh-setup/with_skill/outputs/result.md +131 -0
  9. package/config/skills/specialists-usage-workspace/iteration-1/eval-yaml-debug/old_skill/outputs/result.md +159 -0
  10. package/config/skills/specialists-usage-workspace/iteration-1/eval-yaml-debug/with_skill/outputs/result.md +150 -0
  11. package/config/skills/specialists-usage-workspace/iteration-2/eval-bug-investigation/with_skill/outputs/result.md +180 -0
  12. package/config/skills/specialists-usage-workspace/iteration-2/eval-bug-investigation/with_skill/timing.json +5 -0
  13. package/config/skills/specialists-usage-workspace/iteration-2/eval-bug-investigation/without_skill/outputs/result.md +223 -0
  14. package/config/skills/specialists-usage-workspace/iteration-2/eval-bug-investigation/without_skill/timing.json +5 -0
  15. package/config/skills/specialists-usage-workspace/iteration-2/eval-code-review/with_skill/timing.json +5 -0
  16. package/config/skills/specialists-usage-workspace/iteration-2/eval-code-review/without_skill/outputs/result.md +146 -0
  17. package/config/skills/specialists-usage-workspace/iteration-2/eval-code-review/without_skill/timing.json +5 -0
  18. package/config/skills/specialists-usage-workspace/iteration-2/eval-test-coverage/with_skill/outputs/result.md +89 -0
  19. package/config/skills/specialists-usage-workspace/iteration-2/eval-test-coverage/with_skill/timing.json +5 -0
  20. package/config/skills/specialists-usage-workspace/iteration-2/eval-test-coverage/without_skill/outputs/result.md +96 -0
  21. package/config/skills/specialists-usage-workspace/iteration-2/eval-test-coverage/without_skill/timing.json +5 -0
  22. package/config/skills/specialists-usage-workspace/skill-snapshot/SKILL.md.old +237 -0
  23. package/config/skills/using-specialists/SKILL.md +158 -0
  24. package/config/skills/using-specialists/evals/evals.json +68 -0
  25. package/config/specialists/.serena/project.yml +151 -0
  26. package/config/specialists/auto-remediation.specialist.yaml +70 -0
  27. package/config/specialists/bug-hunt.specialist.yaml +96 -0
  28. package/config/specialists/explorer.specialist.yaml +79 -0
  29. package/config/specialists/memory-processor.specialist.yaml +140 -0
  30. package/config/specialists/overthinker.specialist.yaml +63 -0
  31. package/config/specialists/parallel-runner.specialist.yaml +61 -0
  32. package/config/specialists/planner.specialist.yaml +87 -0
  33. package/config/specialists/specialists-creator.specialist.yaml +82 -0
  34. package/config/specialists/sync-docs.specialist.yaml +53 -0
  35. package/config/specialists/test-runner.specialist.yaml +58 -0
  36. package/config/specialists/xt-merge.specialist.yaml +78 -0
  37. package/dist/index.js +246 -214
  38. package/package.json +2 -3
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ // specialists-complete — Claude Code UserPromptSubmit hook
3
+ // Checks .specialists/ready/ for completed background job markers and injects
4
+ // completion banners into Claude's context.
5
+ //
6
+ // Installed by: specialists install
7
+
8
+ import { existsSync, readdirSync, readFileSync, unlinkSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+
11
+ const cwd = process.env.CLAUDE_PROJECT_DIR ?? process.cwd();
12
+ const readyDir = join(cwd, '.specialists', 'ready');
13
+
14
+ // Exit silently if no ready dir or nothing to report
15
+ if (!existsSync(readyDir)) process.exit(0);
16
+
17
+ let markers;
18
+ try {
19
+ markers = readdirSync(readyDir).filter(f => !f.startsWith('.'));
20
+ } catch {
21
+ process.exit(0);
22
+ }
23
+
24
+ if (markers.length === 0) process.exit(0);
25
+
26
+ const banners = [];
27
+
28
+ for (const jobId of markers) {
29
+ const markerPath = join(readyDir, jobId);
30
+ const statusPath = join(cwd, '.specialists', 'jobs', jobId, 'status.json');
31
+
32
+ try {
33
+ let specialist = jobId;
34
+ let elapsed = '';
35
+
36
+ if (existsSync(statusPath)) {
37
+ const status = JSON.parse(readFileSync(statusPath, 'utf-8'));
38
+ specialist = status.specialist ?? jobId;
39
+ elapsed = status.elapsed_s !== undefined ? `, ${status.elapsed_s}s` : '';
40
+ }
41
+
42
+ banners.push(
43
+ `[Specialist '${specialist}' completed (job ${jobId}${elapsed}). Run: specialists result ${jobId}]`
44
+ );
45
+
46
+ // Delete marker so it only fires once
47
+ unlinkSync(markerPath);
48
+ } catch {
49
+ // Ignore malformed entries
50
+ try { unlinkSync(markerPath); } catch { /* ignore */ }
51
+ }
52
+ }
53
+
54
+ if (banners.length === 0) process.exit(0);
55
+
56
+ // UserPromptSubmit hooks inject content via JSON
57
+ process.stdout.write(JSON.stringify({
58
+ type: 'inject',
59
+ content: banners.join('\n'),
60
+ }) + '\n');
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+ // specialists-session-start — Claude Code SessionStart hook
3
+ // Injects specialists context at the start of every session:
4
+ // • using-specialists skill (behavioral delegation guide)
5
+ // • Active background jobs (if any)
6
+ // • Available specialists list
7
+ // • Key CLI commands reminder
8
+ //
9
+ // Installed by: specialists init
10
+ // Hook type: SessionStart
11
+
12
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
13
+ import { join } from 'node:path';
14
+
15
+
16
+ const cwd = process.env.CLAUDE_PROJECT_DIR ?? process.cwd();
17
+ const jobsDir = join(cwd, '.specialists', 'jobs');
18
+ const lines = [];
19
+
20
+ // ── 0. using-specialists skill ─────────────────────────────────────────────
21
+ // Inject the behavioral delegation guide so Claude knows when and how to
22
+ // use specialists without waiting for the user to ask.
23
+ const skillPath = join(cwd, '.specialists', 'default', 'skills', 'using-specialists', 'SKILL.md');
24
+ if (existsSync(skillPath)) {
25
+ const raw = readFileSync(skillPath, 'utf-8');
26
+ // Strip YAML frontmatter (--- ... ---) if present
27
+ const content = raw.startsWith('---')
28
+ ? raw.replace(/^---[\s\S]*?---\n?/, '').trimStart()
29
+ : raw;
30
+ lines.push(content);
31
+ }
32
+
33
+ // ── 1. Active background jobs ──────────────────────────────────────────────
34
+ if (existsSync(jobsDir)) {
35
+ let entries = [];
36
+ try { entries = readdirSync(jobsDir); } catch { /* ignore */ }
37
+
38
+ const activeJobs = [];
39
+ for (const jobId of entries) {
40
+ const statusPath = join(jobsDir, jobId, 'status.json');
41
+ if (!existsSync(statusPath)) continue;
42
+ try {
43
+ const s = JSON.parse(readFileSync(statusPath, 'utf-8'));
44
+ if (s.status === 'running' || s.status === 'starting') {
45
+ const elapsed = s.elapsed_s !== undefined ? ` (${s.elapsed_s}s)` : '';
46
+ activeJobs.push(
47
+ ` • ${s.specialist ?? jobId} [${s.status}]${elapsed} → specialists result ${jobId}`
48
+ );
49
+ }
50
+ } catch { /* malformed status.json */ }
51
+ }
52
+
53
+ if (activeJobs.length > 0) {
54
+ lines.push('## Specialists — Active Background Jobs');
55
+ lines.push('');
56
+ lines.push(...activeJobs);
57
+ lines.push('');
58
+ lines.push('Use `specialists feed <job-id> --follow` to stream events, or `specialists result <job-id>` when done.');
59
+ lines.push('');
60
+ }
61
+ }
62
+
63
+ // ── 2. Available specialists (read YAML dirs directly) ────────────────────
64
+ function readSpecialistNames(dir) {
65
+ if (!existsSync(dir)) return [];
66
+ try {
67
+ return readdirSync(dir)
68
+ .filter(f => f.endsWith('.specialist.yaml'))
69
+ .map(f => f.replace('.specialist.yaml', ''));
70
+ } catch {
71
+ return [];
72
+ }
73
+ }
74
+
75
+ const defaultNames = readSpecialistNames(join(cwd, '.specialists', 'default', 'specialists'));
76
+ const userNames = readSpecialistNames(join(cwd, '.specialists', 'user', 'specialists'));
77
+
78
+ // User takes precedence on name collision; merge and sort
79
+ const allNames = [...new Set([...userNames, ...defaultNames])].sort();
80
+
81
+ if (allNames.length > 0) {
82
+ lines.push('## Specialists — Available');
83
+ lines.push('');
84
+ if (defaultNames.length > 0) {
85
+ lines.push(`default (${defaultNames.length}): ${defaultNames.join(', ')}`);
86
+ }
87
+ if (userNames.length > 0) {
88
+ const extraUser = userNames.filter(n => !defaultNames.includes(n));
89
+ if (extraUser.length > 0) {
90
+ lines.push(`user (${extraUser.length}): ${extraUser.join(', ')}`);
91
+ }
92
+ }
93
+ lines.push('');
94
+ }
95
+
96
+ // ── 3. Key commands reminder ───────────────────────────────────────────────
97
+ lines.push('## Specialists — Session Quick Reference');
98
+ lines.push('');
99
+ lines.push('```');
100
+ lines.push('specialists list # discover available specialists');
101
+ lines.push('specialists run <name> --prompt "..." # run foreground (streams output)');
102
+ lines.push('specialists run <name> --prompt "..." --background # run async → returns job ID');
103
+ lines.push('specialists run <name> --follow # background + stream live output');
104
+ lines.push('specialists feed <job-id> --follow # tail live events');
105
+ lines.push('specialists result <job-id> # read final output');
106
+ lines.push('specialists status # system health');
107
+ lines.push('specialists doctor # troubleshoot issues');
108
+ lines.push('```');
109
+ lines.push('');
110
+ lines.push('MCP tools: specialist_init · use_specialist · start_specialist · poll_specialist · run_parallel');
111
+
112
+ // ── Output ─────────────────────────────────────────────────────────────────
113
+ if (lines.length === 0) process.exit(0);
114
+
115
+ process.stdout.write(JSON.stringify({
116
+ hookSpecificOutput: {
117
+ hookEventName: 'SessionStart',
118
+ additionalSystemPrompt: lines.join('\n'),
119
+ },
120
+ }) + '\n');
@@ -0,0 +1,506 @@
1
+ # Specialist Author Guide
2
+
3
+ > Source of truth: `src/specialist/schema.ts` | Runtime: `src/specialist/runner.ts`
4
+
5
+ ---
6
+
7
+ ## ACTION REQUIRED BEFORE ANYTHING ELSE
8
+
9
+ Run these commands **right now**, before reading further, before writing any YAML, before doing anything else:
10
+
11
+ ```bash
12
+ pi --list-models
13
+ ```
14
+
15
+ Read the output. Pick one primary model and one fallback from **different providers**. Then ping both:
16
+
17
+ ```bash
18
+ pi --model <chosen-primary> --print "ping" # must print: pong
19
+ pi --model <chosen-fallback> --print "ping" # must print: pong
20
+ ```
21
+
22
+ If a ping fails, pick the next best model in that tier and ping again. **Do not proceed until both return "pong".**
23
+
24
+ Model tiers:
25
+ - **Heavy** (deep reasoning, multi-phase): Opus / Pro / GLM-5
26
+ - **Standard** (authoring, review, codegen): Sonnet / Flash-Pro
27
+ - **Light** (fast context, reports, tests): Haiku / Flash
28
+
29
+ Rules:
30
+ - Always pick the **highest version** in a family (`claude-sonnet-4-6` not `4-5`, `gemini-3.1-pro-preview` not `gemini-2.5-pro`)
31
+ - `model` and `fallback_model` must be **different providers**
32
+ - Never write a model string you have not pinged in this session
33
+
34
+ ---
35
+
36
+ ---
37
+
38
+ ## Model Setup (for a new specialist OR "setup my specialists models")
39
+
40
+ ### Quick Reference: Specialists CLI
41
+
42
+ ```bash
43
+ specialists list # all specialists + current model
44
+ specialists models # all pi models, flagged with thinking/images, shows current assignments
45
+ specialists edit <name> --model <value> # change primary model
46
+ specialists edit <name> --fallback-model <v> # change fallback model
47
+ specialists edit <name> --model <v> --dry-run # preview without writing
48
+ specialists edit <name> --permission HIGH # change permission level
49
+ specialists status # system health
50
+ specialists doctor # prereq + hook diagnostics
51
+ ```
52
+
53
+ ---
54
+
55
+ ### Scenario: "Setup my specialists models"
56
+
57
+ When a user asks to set up or re-balance specialist models, run this workflow:
58
+
59
+ #### Step 1 — Inventory
60
+
61
+ ```bash
62
+ specialists list # shows each specialist + its current model
63
+ specialists models # shows all available models on pi, with current assignments marked ←
64
+ ```
65
+
66
+ Read both outputs carefully:
67
+ - `specialists list` → what specialists exist and what they currently use
68
+ - `specialists models` → what models are available, and which specialists already use each one (the `←` markers show assignments)
69
+
70
+ #### Step 2 — Classify each specialist by tier
71
+
72
+ | Tier | Specialists (typical) | Recommended model class |
73
+ |------|-----------------------|------------------------|
74
+ | **Heavy** — deep reasoning, multi-phase, architecture | `overthinker`, `feature-design`, `bug-hunt`, `planner`, `parallel-review` | Opus / Pro / GLM-5 |
75
+ | **Standard** — code generation, review, authoring, docs | `codebase-explorer`, `specialist-author`, `sync-docs`, `xt-merge` | Sonnet / Flash-Pro |
76
+ | **Light** — fast context, reporting, test runs | `init-session`, `report-generator`, `test-runner`, `auto-remediation` | Haiku / Flash |
77
+
78
+ Adjust tiers based on what the user actually has installed. Custom specialists: read their `description` and `permission_required` to infer tier.
79
+
80
+ #### Step 3 — Select models with provider diversity
81
+
82
+ Rules:
83
+ 1. **Pick the highest version in each family** — `glm-5` not `glm-4.7`, `claude-sonnet-4-6` not `4-5`, `gemini-3.1-pro-preview` not `gemini-2.5-pro`
84
+ 2. **`model` and `fallback_model` must be different providers** — never stack two anthropic models
85
+ 3. **Spread providers across tiers** — don't assign all specialists to anthropic; distribute across anthropic / google-gemini-cli / zai / openai-codex where available
86
+ 4. **Match thinking capability to tier** — heavy specialists benefit from `thinking: yes` models
87
+
88
+ Example distribution (based on current `specialists models` output):
89
+
90
+ | Tier | model | fallback_model |
91
+ |------|-------|----------------|
92
+ | Heavy | `anthropic/claude-opus-4-6` | `google-gemini-cli/gemini-3.1-pro-preview` |
93
+ | Standard | `anthropic/claude-sonnet-4-6` | `google-gemini-cli/gemini-3-flash-preview` |
94
+ | Light | `anthropic/claude-haiku-4-5` | `zai/glm-5-turbo` |
95
+
96
+ If anthropic is not available, use `zai/glm-5` (heavy), `google-gemini-cli/gemini-3.1-pro-preview` (standard), `google-gemini-cli/gemini-3-flash-preview` (light).
97
+
98
+ #### Step 4 — ⛔ Ping each chosen model before assigning
99
+
100
+ ```bash
101
+ # REQUIRED — do not skip, do not assume a model works without pinging
102
+ pi --model <provider>/<primary-model-id> --print "ping" # must return "pong"
103
+ pi --model <provider>/<fallback-model-id> --print "ping" # must return "pong"
104
+ ```
105
+
106
+ Ping **both** primary and fallback. If ping fails → pick next best in that tier and ping again. Do not assign a model that did not respond.
107
+
108
+ #### Step 5 — Apply with `specialists edit`
109
+
110
+ ```bash
111
+ # Example: upgrade heavy-tier specialists
112
+ specialists edit overthinker --model anthropic/claude-opus-4-6 --fallback-model google-gemini-cli/gemini-3.1-pro-preview
113
+ specialists edit feature-design --model anthropic/claude-opus-4-6 --fallback-model google-gemini-cli/gemini-3.1-pro-preview
114
+ specialists edit bug-hunt --model anthropic/claude-opus-4-6 --fallback-model google-gemini-cli/gemini-3.1-pro-preview
115
+
116
+ # Standard tier
117
+ specialists edit codebase-explorer --model anthropic/claude-sonnet-4-6 --fallback-model google-gemini-cli/gemini-3-flash-preview
118
+ specialists edit sync-docs --model anthropic/claude-sonnet-4-6 --fallback-model google-gemini-cli/gemini-3-flash-preview
119
+
120
+ # Light tier
121
+ specialists edit init-session --model anthropic/claude-haiku-4-5 --fallback-model zai/glm-5-turbo
122
+ specialists edit report-generator --model anthropic/claude-haiku-4-5 --fallback-model zai/glm-5-turbo
123
+ ```
124
+
125
+ Use `--dry-run` first to preview any change before writing.
126
+
127
+ #### Step 6 — Verify
128
+
129
+ ```bash
130
+ specialists list # confirm all models updated correctly
131
+ specialists models # confirm assignments look balanced
132
+ ```
133
+
134
+ ---
135
+
136
+ ### For a new specialist (single model selection)
137
+
138
+ > **See [⛔ MANDATORY FIRST STEP](#-mandatory-first-step--verify-models-before-writing-any-yaml) at the top of this skill.**
139
+ > Use `pi --list-models` (not `specialists models`) to discover models, ping both before writing YAML.
140
+
141
+ ```bash
142
+ # 1. pi --list-models — see exactly what's available on pi right now
143
+ # 2. Pick tier + pick highest version in family
144
+ # 3. pi --model <primary> --print "ping" — must return "pong"
145
+ # 4. pi --model <fallback> --print "ping" — must return "pong"
146
+ # 5. Write YAML with verified model strings
147
+ ```
148
+
149
+ **Rule:** Never hardcode a model without pinging it. If ping fails, try the next best in that tier.
150
+
151
+ ---
152
+
153
+ ## Quick Start: Minimal Skeleton
154
+
155
+ ```yaml
156
+ specialist:
157
+ metadata:
158
+ name: my-specialist # kebab-case, required
159
+ version: 1.0.0 # semver, required
160
+ description: "One sentence." # required
161
+ category: workflow # required (free text)
162
+
163
+ execution:
164
+ model: anthropic/claude-sonnet-4-6 # run model setup workflow above to choose + verify
165
+ permission_required: READ_ONLY
166
+
167
+ prompt:
168
+ task_template: |
169
+ $prompt
170
+
171
+ Working directory: $cwd
172
+ ```
173
+
174
+ Validate before committing:
175
+ ```bash
176
+ bun skills/specialist-author/scripts/validate-specialist.ts specialists/my-specialist.specialist.yaml
177
+ ```
178
+
179
+ ---
180
+
181
+ ## Schema Reference
182
+
183
+ ### `specialist.metadata` (required)
184
+
185
+ | Field | Type | Required | Notes |
186
+ |-------|------|----------|-------|
187
+ | `name` | string | yes | kebab-case: `[a-z][a-z0-9-]*` |
188
+ | `version` | string | yes | semver: `1.0.0` |
189
+ | `description` | string | yes | One sentence |
190
+ | `category` | string | yes | Free text (e.g. `workflow`, `analysis`, `codegen`) |
191
+ | `author` | string | no | Optional |
192
+ | `created` | string | no | Optional date |
193
+ | `updated` | string | no | Optional date, quote it: `"2026-03-22"` |
194
+ | `tags` | string[] | no | Optional list |
195
+
196
+ ### `specialist.execution` (required)
197
+
198
+ | Field | Type | Default | Notes |
199
+ |-------|------|---------|-------|
200
+ | `model` | string | — | required — ping before using |
201
+ | `fallback_model` | string | — | must be a different provider |
202
+ | `mode` | enum | `auto` | `tool` \| `skill` \| `auto` |
203
+ | `timeout_ms` | number | `120000` | ms |
204
+ | `stall_timeout_ms` | number | — | kill if no event for N ms |
205
+ | `response_format` | enum | `text` | `text` \| `json` \| `markdown` |
206
+ | `permission_required` | enum | `READ_ONLY` | see tier table below |
207
+ | `thinking_level` | enum | — | `off` \| `minimal` \| `low` \| `medium` \| `high` \| `xhigh` |
208
+
209
+ **Permission tiers** — controls which pi tools are available:
210
+
211
+ | Level | pi --tools | Use when |
212
+ |-------|-----------|----------|
213
+ | `READ_ONLY` | `read,grep,find,ls` | Read-only analysis, no bash |
214
+ | `LOW` | `+bash` | Inspect/run commands, no file edits |
215
+ | `MEDIUM` | `+edit` | Can edit existing files |
216
+ | `HIGH` | `+write` | Full access — can create new files |
217
+
218
+ **Common pitfall:** `READ_WRITE` is **not** a valid value — use `LOW` or higher.
219
+
220
+ ### `specialist.prompt` (required)
221
+
222
+ | Field | Type | Required | Notes |
223
+ |-------|------|----------|-------|
224
+ | `task_template` | string | yes | Template string with `$variable` substitution |
225
+ | `system` | string | no | System prompt / agents.md content |
226
+ | `skill_inherit` | string | no | Single skill folder/file injected via `pi --skill` (Agent Forge compat) |
227
+ | `output_schema` | object | no | JSON schema for structured output |
228
+ | `examples` | array | no | Few-shot examples |
229
+
230
+ ### `specialist.skills` (optional)
231
+
232
+ ```yaml
233
+ skills:
234
+ paths: # passed as pi --skill; folder (reads SKILL.md inside) or direct file
235
+ - skills/my-skill/ # folder — pi loads SKILL.md from inside
236
+ - ~/.agents/skills/domain/ # same
237
+ - skills/notes.md # direct file also accepted
238
+ scripts:
239
+ - run: ./scripts/pre-check.sh # file path OR shell command
240
+ phase: pre # "pre" or "post"
241
+ inject_output: true # true = stdout available as $pre_script_output
242
+ - run: "bd ready" # inline command — runs via shell
243
+ phase: pre
244
+ inject_output: true
245
+ - run: ./scripts/cleanup.sh
246
+ phase: post
247
+ ```
248
+
249
+ `run` accepts either a **file path** (`./scripts/foo.sh`, `~/scripts/foo.sh`) or a **shell command** (`bd ready`, `git status`). Pre-run validation checks that file paths exist and shell commands are on `PATH`. Shebang typos (e.g. `pytho` instead of `python`) are caught and reported as errors before the session starts.
250
+
251
+ `path` is accepted as a deprecated alias for `run`.
252
+
253
+ ### `specialist.capabilities` (optional)
254
+
255
+ Informational declarations used by pre-run validation and future tooling (e.g. `specialists doctor`).
256
+
257
+ ```yaml
258
+ capabilities:
259
+ required_tools: [bash, read, grep, glob] # pi tools this specialist needs
260
+ external_commands: [bd, git, gh] # CLI binaries validated on PATH before run
261
+ ```
262
+
263
+ `external_commands` causes a hard failure if any binary is not found on `PATH` — the session will not start.
264
+
265
+ ### `specialist.output_file` (optional, top-level)
266
+
267
+ ```yaml
268
+ output_file: .specialists/my-specialist-result.md
269
+ ```
270
+
271
+ Writes the final session output to this file path after the session completes. Relative to the working directory.
272
+
273
+ ### `specialist.communication` (optional)
274
+
275
+ ```yaml
276
+ communication:
277
+ next_specialists: planner # single specialist to chain after completion
278
+ # or an array:
279
+ next_specialists: [planner, test-runner]
280
+ ```
281
+
282
+ `next_specialists` declares which specialist(s) should receive this specialist's output as `$previous_result`. Chaining is executed by the caller (e.g. `run_parallel` pipeline) — this field is declarative metadata.
283
+
284
+ ### `specialist.validation` (optional)
285
+
286
+ Drives the staleness detection shown in `specialists status` and `specialists list`.
287
+
288
+ | Field | Type | Notes |
289
+ |-------|------|-------|
290
+ | `files_to_watch` | string[] | Paths to monitor. If any file's mtime is newer than `metadata.updated`, the specialist is marked **STALE** |
291
+ | `stale_threshold_days` | number | If the specialist has been STALE for more than N days, escalates to **AGED** |
292
+ | `references` | array | Accepted, currently unused |
293
+
294
+ **Staleness states:**
295
+
296
+ | State | Condition |
297
+ |-------|-----------|
298
+ | `OK` | No watched files changed, or no `files_to_watch` / `updated` set |
299
+ | `STALE` | A watched file's mtime > `metadata.updated` |
300
+ | `AGED` | STALE + days since `updated` > `stale_threshold_days` |
301
+
302
+ ```yaml
303
+ specialist:
304
+ metadata:
305
+ updated: "2026-03-01"
306
+
307
+ validation:
308
+ files_to_watch:
309
+ - src/specialist/schema.ts
310
+ - src/specialist/runner.ts
311
+ stale_threshold_days: 30
312
+ ```
313
+
314
+ This specialist goes STALE the moment `schema.ts` or `runner.ts` is modified after March 1st, and AGED if that condition persists for more than 30 days.
315
+
316
+ ### `specialist.beads_integration` (optional)
317
+
318
+ | Value | Behavior |
319
+ |-------|----------|
320
+ | `auto` (default) | Create tracking bead when permission_required is LOW+ |
321
+ | `always` | Always create a tracking bead |
322
+ | `never` | Never create a tracking bead |
323
+
324
+ ---
325
+
326
+ ## Built-in Template Variables
327
+
328
+ These are **always available** in `task_template` — no configuration needed:
329
+
330
+ | Variable | Value |
331
+ |----------|-------|
332
+ | `$prompt` | The user's prompt passed to `use_specialist` |
333
+ | `$cwd` | `process.cwd()` at invocation time |
334
+ | `$pre_script_output` | Stdout of `pre` scripts with `inject_output: true` (empty string if none) |
335
+
336
+ **When invoked via `--bead`** (inputBeadId present):
337
+
338
+ | Variable | Value |
339
+ |----------|-------|
340
+ | `$bead_context` | Full bead content (replaces `$prompt`) |
341
+ | `$bead_id` | The bead ID |
342
+
343
+ **Custom variables** can be passed at invocation time via `--variables key=value` and accessed as `$key`.
344
+
345
+ ---
346
+
347
+ ## Skills Injection (`skills.paths`)
348
+
349
+ Files listed under `skills.paths` are read and appended to the system prompt at runtime:
350
+
351
+ ```yaml
352
+ skills:
353
+ paths:
354
+ - skills/specialist-author/SKILL.md
355
+ - .claude/agents.md
356
+ ```
357
+
358
+ Each file is appended as:
359
+ ```
360
+ ---
361
+ # Skill: <path>
362
+
363
+ <file content>
364
+ ```
365
+
366
+ Missing files are silently skipped (no error).
367
+
368
+ `skill_inherit` (in `prompt:`) works the same way but for a single file — it is an Agent Forge compatibility field, appended under `# Service Knowledge`.
369
+
370
+ ---
371
+
372
+ ## Pre/Post Scripts
373
+
374
+ Scripts run **locally** (not inside the agent session):
375
+
376
+ ```yaml
377
+ skills:
378
+ scripts:
379
+ - path: scripts/gather-context.sh
380
+ phase: pre
381
+ inject_output: true # stdout -> $pre_script_output in task_template
382
+ - path: scripts/notify.sh
383
+ phase: post
384
+ inject_output: false # runs after session, output discarded
385
+ ```
386
+
387
+ - `pre` scripts run before the agent session starts; use `inject_output: true` to surface their stdout.
388
+ - `post` scripts run after the session completes (cleanup, notifications).
389
+ - Timeout: 30 seconds per script.
390
+ - Exit code is captured but does not abort the run.
391
+
392
+ ---
393
+
394
+ ## Annotated Full Example
395
+
396
+ ```yaml
397
+ specialist:
398
+ metadata:
399
+ name: code-reviewer
400
+ version: 1.0.0
401
+ description: "Reviews a PR diff for correctness, style, and security issues."
402
+ category: code-quality
403
+ author: team@example.com
404
+ updated: "2026-03-22"
405
+ tags: [review, code-quality, security]
406
+
407
+ execution:
408
+ mode: tool
409
+ model: anthropic/claude-sonnet-4-6
410
+ fallback_model: google-gemini-cli/gemini-3.1-pro-preview
411
+ timeout_ms: 300000
412
+ stall_timeout_ms: 60000
413
+ response_format: markdown
414
+ permission_required: READ_ONLY # not READ_WRITE
415
+
416
+ prompt:
417
+ system: |
418
+ You are an expert code reviewer. Focus on correctness, maintainability, and security.
419
+ Do NOT modify any files -- output a markdown review only.
420
+
421
+ task_template: |
422
+ Review the following changes:
423
+
424
+ $prompt
425
+
426
+ $pre_script_output
427
+
428
+ Working directory: $cwd
429
+
430
+ Output a structured markdown review with sections: Summary, Issues, Suggestions.
431
+
432
+ skill_inherit: skills/code-review/guidelines.md
433
+
434
+ skills:
435
+ paths:
436
+ - skills/code-review/
437
+ scripts:
438
+ - run: scripts/get-diff.sh
439
+ phase: pre
440
+ inject_output: true
441
+
442
+ capabilities:
443
+ required_tools: [bash, read]
444
+ external_commands: [git]
445
+
446
+ communication:
447
+ next_specialists: [sync-docs]
448
+
449
+ output_file: .specialists/review.md
450
+ beads_integration: auto
451
+ ```
452
+
453
+ ---
454
+
455
+ ## Common Errors and Fixes
456
+
457
+ | Zod Error | Cause | Fix |
458
+ |-----------|-------|-----|
459
+ | `Must be kebab-case` | `name` has uppercase or spaces | Use `my-specialist` not `MySpecialist` |
460
+ | `Must be semver` | `version: "v1.0"` | Use `version: 1.0.0` (no `v` prefix) |
461
+ | `Invalid enum value ... 'READ_WRITE'` | Wrong permission value | Use `READ_ONLY`, `LOW`, `MEDIUM`, or `HIGH` |
462
+ | `Invalid enum value ... 'auto'` on permission_required | Using `auto` for permission_required | `auto` is only valid for `beads_integration` |
463
+ | `Required` on `task_template` | `task_template` missing from `prompt:` | Add `task_template` (even if just `$prompt`) |
464
+ | `Required` on `model` | `model` missing from `execution:` | Add a model string |
465
+ | `Required` on `description` | Missing `description` in `metadata` | Add description string |
466
+ | `Required` on `category` | Missing `category` in `metadata` | Add category string |
467
+ | Silently ignored / no output | YAML valid but `task_template` doesn't use `$prompt` | Add `$prompt` to `task_template` |
468
+ | `defaults` key unrecognized | Using `defaults:` top-level key | Remove it; use `--variables` at invocation or built-ins |
469
+
470
+ ---
471
+
472
+ ## File Placement
473
+
474
+ Specialists are discovered from three scopes (highest priority first):
475
+
476
+ 1. **Project**: `<project-root>/specialists/*.specialist.yaml`
477
+ 2. **User**: `~/.agents/specialists/*.specialist.yaml`
478
+ 3. **System**: package-bundled specialists
479
+
480
+ Name your file `<metadata.name>.specialist.yaml`.
481
+
482
+ ---
483
+
484
+ ## Validation Workflow
485
+
486
+ A bundled validator is included with this skill so the agent does not need to reconstruct the `bun -e` one-liner from memory. It prints `OK <file>` on success and a field-by-field error list on failure.
487
+
488
+ ```bash
489
+ # 1. MANDATORY: discover + ping models (see top of this skill)
490
+ pi --list-models
491
+ pi --model <provider>/<primary-model-id> --print "ping" # must return "pong"
492
+ pi --model <provider>/<fallback-model-id> --print "ping" # must return "pong"
493
+
494
+ # 2. Write the YAML with the verified model
495
+
496
+ # 3. Validate schema with the bundled helper
497
+ bun skills/specialist-author/scripts/validate-specialist.ts specialists/my-specialist.specialist.yaml
498
+
499
+ # 4. List to confirm discovery
500
+ specialists list
501
+
502
+ # 5. Smoke test
503
+ specialists run my-specialist --prompt "ping" --no-beads
504
+ ```
505
+
506
+ If you need the underlying implementation, read `skills/specialist-author/scripts/validate-specialist.ts`. It is a thin Bun/TypeScript wrapper over `parseSpecialist()` from `src/specialist/schema.ts`, which keeps the helper cross-platform for Windows, macOS, and Linux.