@yemi33/minions 0.1.1884 → 0.1.1885
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.
- package/README.md +78 -64
- package/docs/README.md +36 -0
- package/docs/{distribution.md → archive/distribution.md} +2 -0
- package/docs/auto-discovery.md +21 -8
- package/docs/completion-reports.md +215 -0
- package/docs/engine-restart.md +2 -0
- package/docs/kb-sweep.md +173 -0
- package/docs/onboarding.md +246 -0
- package/docs/runtime-adapters.md +213 -0
- package/docs/team-memory.md +116 -0
- package/docs/teams-production.md +3 -1
- package/docs/watches.md +201 -0
- package/package.json +1 -1
- package/playbooks/docs.md +1 -1
- package/playbooks/fix.md +1 -12
- package/playbooks/implement-shared.md +1 -12
- package/playbooks/implement.md +2 -2
- package/playbooks/plan-to-prd.md +3 -3
- package/playbooks/review.md +1 -1
- package/playbooks/shared-rules.md +1 -3
- package/playbooks/verify.md +1 -1
- package/prompts/cc-system.md +10 -2
package/docs/kb-sweep.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Knowledge Base Sweep
|
|
2
|
+
|
|
3
|
+
How the `knowledge/` directory stays curated, deduplicated, and small enough to keep injecting into agent prompts.
|
|
4
|
+
|
|
5
|
+
## What the Sweep Does
|
|
6
|
+
|
|
7
|
+
The KB sweep is a 3-pass cycle implemented in [`engine/kb-sweep.js`](../engine/kb-sweep.js) that prunes duplicate, stale, and oversized entries from `knowledge/`. It runs asynchronously, archives (rather than deletes) what it removes, and writes a `_swept` frontmatter flag so the expensive rewrite pass is idempotent.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Entries in knowledge/<category>/*.md
|
|
11
|
+
→ Pass 1: Hash dedup (cheap, no LLM)
|
|
12
|
+
→ Pass 2: LLM batch sweep (Haiku, batches of 30)
|
|
13
|
+
→ Pass 3: Per-entry rewrite (Haiku, concurrency 5)
|
|
14
|
+
→ Prune _swept/ archive (>30 days)
|
|
15
|
+
→ Invalidate KB cache, write engine/kb-swept.json
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## The 3-Pass Cycle
|
|
19
|
+
|
|
20
|
+
### Pass 1 — Hash Dedup
|
|
21
|
+
|
|
22
|
+
Cheap, deterministic. No LLM call.
|
|
23
|
+
|
|
24
|
+
For every entry, compute `sha256(normalize(content).slice(0,500) + ':' + content.length)` (source: [`engine/kb-sweep.js:29`](../engine/kb-sweep.js#L29)). Group by hash. When two or more entries collide, keep the most recent (by `date:` frontmatter, then mtime) and archive the rest into `knowledge/_swept/` with a `<!-- swept: ... | reason: hash-duplicate of ... -->` header (source: [`engine/kb-sweep.js:84-105`](../engine/kb-sweep.js#L84)).
|
|
25
|
+
|
|
26
|
+
This catches near-identical re-postings across batches that the LLM pass might miss because they land in different batches.
|
|
27
|
+
|
|
28
|
+
### Pass 2 — LLM Batch Sweep
|
|
29
|
+
|
|
30
|
+
The remaining survivors are sent to Claude Haiku in batches of `LLM_BATCH_SIZE = 30` (source: [`engine/kb-sweep.js:25`](../engine/kb-sweep.js#L25)). Each batch prompt asks for three lists in JSON:
|
|
31
|
+
|
|
32
|
+
| Field | What the LLM looks for |
|
|
33
|
+
|-------|------------------------|
|
|
34
|
+
| `duplicates` | Entries with substantially the same content. Returned as `{keep, remove[], reason}`. |
|
|
35
|
+
| `reclassify` | Entries in the wrong category. Returned as `{index, from, to, reason}`. |
|
|
36
|
+
| `remove` | Stale or empty entries (boilerplate, "no changes needed", bail-out notes). Returned as `{index, reason}`. |
|
|
37
|
+
|
|
38
|
+
Each action archives the file via the same `_archiveKbFile()` helper used by Pass 1; reclassification rewrites the `category:` frontmatter line and moves the file into the new category directory (source: [`engine/kb-sweep.js:243-279`](../engine/kb-sweep.js#L243)).
|
|
39
|
+
|
|
40
|
+
Reclassification targets are validated against `shared.KB_CATEGORIES` (`architecture`, `conventions`, `project-notes`, `build-reports`, `reviews` — source: [`engine/shared.js:1012`](../engine/shared.js#L1012)); unknown categories are silently dropped.
|
|
41
|
+
|
|
42
|
+
If a batch returns invalid JSON or the runtime is unavailable, that batch is skipped with a warning and the rest of the sweep continues (source: [`engine/kb-sweep.js:139-151`](../engine/kb-sweep.js#L139)).
|
|
43
|
+
|
|
44
|
+
### Pass 3 — Per-Entry Rewrite
|
|
45
|
+
|
|
46
|
+
Survivors of Passes 1 and 2 that are still on disk get rewritten one entry at a time, with up to `NORMALIZE_CONCURRENCY = 5` parallel workers (source: [`engine/kb-sweep.js:26`](../engine/kb-sweep.js#L26)). Each entry is sent to Haiku with a fixed template prompt that:
|
|
47
|
+
|
|
48
|
+
- Caps output at ~800 words.
|
|
49
|
+
- Forces the structure `## Summary` → `## Key Findings` → `## Action Items` (omit if none) → `## References` (omit if none).
|
|
50
|
+
- Preserves all `file:line` references and code snippets.
|
|
51
|
+
- Drops boilerplate (full dates, agent IDs in the body, narrative scaffolding).
|
|
52
|
+
|
|
53
|
+
After a successful rewrite, the entry's frontmatter gains a `_swept: <ISO timestamp>` key (source: [`engine/kb-sweep.js:229`](../engine/kb-sweep.js#L229)).
|
|
54
|
+
|
|
55
|
+
## The `_swept` Frontmatter Flag
|
|
56
|
+
|
|
57
|
+
`_swept` makes Pass 3 idempotent across runs. Semantics:
|
|
58
|
+
|
|
59
|
+
- **Set** to `new Date().toISOString()` whenever the rewrite pass successfully rewrites the body (source: [`engine/kb-sweep.js:229`](../engine/kb-sweep.js#L229)).
|
|
60
|
+
- **Skipped** by Pass 3 on subsequent sweeps as long as the file's `mtimeMs` is `<= sweptAt + 1000` ms (1 s grace for FS timestamp jitter — source: [`engine/kb-sweep.js:196-202`](../engine/kb-sweep.js#L196)).
|
|
61
|
+
- **Re-processed** if the file is edited after the sweep flag was set (mtime exceeds the grace window).
|
|
62
|
+
|
|
63
|
+
Hash dedup (Pass 1) and the LLM sweep (Pass 2) ignore `_swept` — those passes act on metadata and similarity, not on whether the entry was previously rewritten.
|
|
64
|
+
|
|
65
|
+
The flag key is exported as `SWEPT_FLAG_KEY` for tests (source: [`engine/kb-sweep.js:27,429`](../engine/kb-sweep.js#L27)).
|
|
66
|
+
|
|
67
|
+
## 30-Day Archive Retention
|
|
68
|
+
|
|
69
|
+
Archived entries land in `knowledge/_swept/` with their original filename (deduped via `shared.uniquePath()` if it collides). They're not deleted immediately — `_pruneOldSwept()` runs at the end of every sweep and removes any file whose `mtimeMs` is older than `SWEPT_RETENTION_MS = 30 * 24 * 60 * 60 * 1000` (30 days — source: [`engine/kb-sweep.js:23,69-81`](../engine/kb-sweep.js#L23)).
|
|
70
|
+
|
|
71
|
+
This gives humans a recovery window: if a sweep archives something useful, you have ~30 days to copy it back out of `_swept/` before it's permanently pruned.
|
|
72
|
+
|
|
73
|
+
## Manual Trigger
|
|
74
|
+
|
|
75
|
+
The dashboard exposes two endpoints:
|
|
76
|
+
|
|
77
|
+
| Method | Path | Purpose |
|
|
78
|
+
|--------|------|---------|
|
|
79
|
+
| `POST` | `/api/knowledge/sweep` | Start a sweep in the background. Returns `202 { ok: true, started: true }` (source: [`dashboard.js:7351`](../dashboard.js#L7351), [`dashboard.js:4285`](../dashboard.js#L4285)). |
|
|
80
|
+
| `GET` | `/api/knowledge/sweep/status` | Poll `{ inFlight, startedAt, lastResult, lastCompletedAt }` (source: [`dashboard.js:7352`](../dashboard.js#L7352), [`dashboard.js:4335`](../dashboard.js#L4335)). |
|
|
81
|
+
|
|
82
|
+
POST body is optional. Pass `{ "pinnedKeys": ["knowledge/conventions/foo.md", ...] }` to add request-level pins on top of `pinned.md`. Pinned entries are excluded from every pass (source: [`engine/kb-sweep.js:345-356`](../engine/kb-sweep.js#L345)).
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Trigger a sweep
|
|
86
|
+
curl -X POST http://localhost:7331/api/knowledge/sweep \
|
|
87
|
+
-H 'Content-Type: application/json' \
|
|
88
|
+
-d '{}'
|
|
89
|
+
|
|
90
|
+
# Poll status
|
|
91
|
+
curl http://localhost:7331/api/knowledge/sweep/status
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Concurrency Guard
|
|
95
|
+
|
|
96
|
+
Only one sweep runs at a time. The POST handler refuses to start a second sweep with `{ ok: true, alreadyRunning: true, startedAt }` while one is in flight (source: [`dashboard.js:4306-4311`](../dashboard.js#L4306)).
|
|
97
|
+
|
|
98
|
+
The guard auto-releases when the sweep has been running longer than `staleGuardMs(entryCount)` — `max(30 min, 1 s × entryCount)` — which protects against a dashboard process that died mid-sweep leaving the flag stuck (source: [`engine/kb-sweep.js:413-417`](../engine/kb-sweep.js#L413), [`dashboard.js:4286-4305`](../dashboard.js#L4286)).
|
|
99
|
+
|
|
100
|
+
### Persistent State
|
|
101
|
+
|
|
102
|
+
Sweep state is mirrored to `engine/kb-sweep-state.json` so the dashboard can recover the in-flight indicator across restarts:
|
|
103
|
+
|
|
104
|
+
```jsonc
|
|
105
|
+
// While running
|
|
106
|
+
{ "status": "in-flight", "startedAt": 1715472000000, "startedAtIso": "2026-05-12T..." }
|
|
107
|
+
|
|
108
|
+
// On success
|
|
109
|
+
{ "status": "completed", "startedAt": ..., "completedAt": ..., "durationMs": ..., "summary": "...", "lastResult": { ... } }
|
|
110
|
+
|
|
111
|
+
// On failure
|
|
112
|
+
{ "status": "failed", "startedAt": ..., "completedAt": ..., "error": "..." }
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Memory still wins when present; the disk file is a fallback (source: [`engine/kb-sweep.js:298-320`](../engine/kb-sweep.js#L298), [`dashboard.js:4296-4354`](../dashboard.js#L4296)). A separate `engine/kb-swept.json` is written after each successful sweep with the human-readable summary line shown by the dashboard's "swept N days ago" badge (source: [`engine/kb-sweep.js:407`](../engine/kb-sweep.js#L407)).
|
|
116
|
+
|
|
117
|
+
## Dashboard UI
|
|
118
|
+
|
|
119
|
+
The KB page surfaces sweep state in two places:
|
|
120
|
+
|
|
121
|
+
1. **Trigger button** (`kbSweep()` in [`dashboard/js/render-kb.js:166`](../dashboard/js/render-kb.js#L166)) posts to `/api/knowledge/sweep` with the user's pinned keys and shows an immediate "queued" toast. If the response says `alreadyRunning`, the toast switches to "already running (Xm elapsed) — let it finish first".
|
|
122
|
+
2. **`#kb-swept-time` indicator** renders `swept N days ago · now sweeping (Xm)` (source: [`dashboard/js/render-kb.js:72-82`](../dashboard/js/render-kb.js#L72)). The `sweepInFlight` and `sweepStartedAt` fields come from `GET /api/knowledge` (source: [`dashboard.js:4262-4267`](../dashboard.js#L4262)).
|
|
123
|
+
|
|
124
|
+
### Polling
|
|
125
|
+
|
|
126
|
+
The KB page is refreshed by the dashboard's main refresh loop **every third tick (~12 s)** in [`dashboard/js/refresh.js:121`](../dashboard/js/refresh.js#L121). There is no separate sweep-status polling timer and no auto-stop — the in-flight badge keeps refreshing for as long as the user has the dashboard open and the sweep is running. Sweeps can take many minutes for a large KB; the persistent `engine/kb-sweep-state.json` flag plus the indefinite refresh loop are what let the indicator survive dashboard restarts and arbitrarily long sweeps.
|
|
127
|
+
|
|
128
|
+
## Result Summary
|
|
129
|
+
|
|
130
|
+
`runKbSweep()` returns (and persists in `engine/kb-swept.json`):
|
|
131
|
+
|
|
132
|
+
```js
|
|
133
|
+
{
|
|
134
|
+
ok: true,
|
|
135
|
+
entriesBefore, entriesAfter,
|
|
136
|
+
bytesBefore, bytesAfter,
|
|
137
|
+
hashDuplicatesArchived, // Pass 1
|
|
138
|
+
llmDuplicatesArchived, // Pass 2 (within-batch dupes)
|
|
139
|
+
staleRemoved, // Pass 2 (LLM-flagged removals)
|
|
140
|
+
reclassified, // Pass 2 (category moves)
|
|
141
|
+
rewritten, // Pass 3
|
|
142
|
+
rewriteBytesBefore, rewriteBytesAfter,
|
|
143
|
+
sweptArchivePruned, // Files purged from _swept/ (>30 days)
|
|
144
|
+
durationMs,
|
|
145
|
+
summary: '<n> hash-dup, <n> llm-dup, <n> stale, <n> reclassified, <n> rewritten (<bytes> saved)',
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
After a non-dry-run sweep, `queries.invalidateKnowledgeBaseCache()` is called so the next `/api/knowledge` read sees the new state (source: [`engine/kb-sweep.js:408`](../engine/kb-sweep.js#L408)).
|
|
150
|
+
|
|
151
|
+
## What NOT to Do
|
|
152
|
+
|
|
153
|
+
> **Never write to `knowledge/` directly.** The sweep is the only writer. Every other code path — agents, dashboard handlers, scripts — should write findings to `notes/inbox/<agent>-<id>-<date>.md` and let the consolidation engine classify them into `knowledge/<category>/`.
|
|
154
|
+
|
|
155
|
+
Why this matters:
|
|
156
|
+
|
|
157
|
+
- Direct writes bypass the hash-dedup, classification, and `_swept` rewrite passes, so future sweeps see a "fresh" entry and may immediately rewrite or archive it.
|
|
158
|
+
- Editing an entry resets its `mtime`, which makes the `_swept` flag stale (re-rewrite on next sweep) and silently changes the entry the next consolidation pass diffs against.
|
|
159
|
+
- Manual edits collide with the LLM rewrite pass — your edit can be replaced wholesale by the next sweep.
|
|
160
|
+
- Per the team-wide rule injected into every dispatched playbook: **"Never delete, move, or overwrite files in `knowledge/`."**
|
|
161
|
+
|
|
162
|
+
If you spot an incorrect KB entry, write a note to `notes/inbox/` describing the issue. The next sweep will reclassify, merge, or remove it; humans can also pin entries (via `pinned.md` or the dashboard) to keep them out of the sweep entirely.
|
|
163
|
+
|
|
164
|
+
The archive directory `knowledge/_swept/` is also off-limits to manual edits — touching it interferes with the 30-day retention timestamp.
|
|
165
|
+
|
|
166
|
+
## Related Files
|
|
167
|
+
|
|
168
|
+
- [`engine/kb-sweep.js`](../engine/kb-sweep.js) — implementation
|
|
169
|
+
- [`engine/queries.js`](../engine/queries.js) — `getKnowledgeBaseEntries()`, `invalidateKnowledgeBaseCache()`
|
|
170
|
+
- [`dashboard.js`](../dashboard.js) — `handleKnowledgeSweep`, `handleKnowledgeSweepStatus`, `handleKnowledgeList`
|
|
171
|
+
- [`dashboard/js/render-kb.js`](../dashboard/js/render-kb.js) — UI trigger and indicator
|
|
172
|
+
- [`dashboard/js/refresh.js`](../dashboard/js/refresh.js) — refresh cadence (every 3rd tick)
|
|
173
|
+
- [`engine/shared.js`](../engine/shared.js) — `KB_CATEGORIES`, `getPinnedItems()`, `uniquePath()`
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# Minions Onboarding — Your First 30 Minutes
|
|
2
|
+
|
|
3
|
+
A fast, hands-on walkthrough for new contributors. Follow the eight steps below in order. Each step is independent enough that you can stop, take a break, and resume without losing state.
|
|
4
|
+
|
|
5
|
+
> **Prerequisites:** Node.js 18+, Git, and a working Claude Code CLI (`npm install -g @anthropic-ai/claude-code`) authenticated against your Anthropic API key or Claude Max subscription. See the top-level [README.md](../README.md#prerequisites) for the full list.
|
|
6
|
+
|
|
7
|
+
> **Screenshots:** This walkthrough is intentionally text-only on the first pass. If you want annotated dashboard screenshots, run the [`capture-demos`](../README.md#dashboard) skill after Step 5; it drives Playwright over the running dashboard and writes images you can drop alongside each section.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Step 1 — Clone the repo
|
|
12
|
+
|
|
13
|
+
You only need to clone if you intend to modify Minions itself. End users normally `npm install -g @yemi33/minions` instead, but a checkout is the right starting point for contributors.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
git clone https://github.com/yemi33/minions.git ~/minions-dev
|
|
17
|
+
cd ~/minions-dev
|
|
18
|
+
npm install # installs dev tooling (Playwright); engine itself has zero deps
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
You should now have:
|
|
22
|
+
|
|
23
|
+
- `engine.js` — the orchestrator entry point
|
|
24
|
+
- `dashboard.js` — the HTTP/UI server (binds `:7331`)
|
|
25
|
+
- `minions.js` — the CLI shim that maps `minions <cmd>` to the right script
|
|
26
|
+
- `playbooks/`, `agents/`, `prompts/`, `docs/` — content the engine renders into agent prompts
|
|
27
|
+
- `test/` — `unit.test.js`, the integration suite, and Playwright specs
|
|
28
|
+
|
|
29
|
+
If you cloned to somewhere other than `~/.minions`, the CLI will still work — `minions init` writes runtime state under `~/.minions/` regardless of where the source lives. The `node` scripts under your checkout will use the checkout as the runtime root when invoked directly (e.g. `node ./engine.js start`).
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Step 2 — Run `minions doctor`
|
|
34
|
+
|
|
35
|
+
`minions doctor` runs the same preflight checks the engine runs at startup. It is safe to run any time and never mutates state.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
minions doctor
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
A healthy Mac/Linux run looks roughly like:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
Runtime:
|
|
45
|
+
|
|
46
|
+
✓ node: v20.11.1 (>= 18 required)
|
|
47
|
+
✓ git: git version 2.45.0
|
|
48
|
+
✓ claude CLI: 1.0.x found on PATH
|
|
49
|
+
|
|
50
|
+
Authentication:
|
|
51
|
+
|
|
52
|
+
✓ ANTHROPIC_API_KEY: set
|
|
53
|
+
✓ gh auth: logged in as <you>
|
|
54
|
+
|
|
55
|
+
Filesystem:
|
|
56
|
+
|
|
57
|
+
✓ ~/.minions writable
|
|
58
|
+
✓ engine/ writable
|
|
59
|
+
|
|
60
|
+
All checks passed.
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
What to look for:
|
|
64
|
+
|
|
65
|
+
- `✓` (green) means OK. `!` is a warning the engine can usually work around. `✗` (critical) will block dispatch — fix it before continuing.
|
|
66
|
+
- The most common failure is `claude CLI: not found` — re-install with `npm install -g @anthropic-ai/claude-code` and re-run.
|
|
67
|
+
- On Windows, runtime resolution prefers native `claude.exe` over the POSIX bash shim; if you see runtime-resolution warnings, `gh auth status` and `where.exe claude` are good first stops.
|
|
68
|
+
|
|
69
|
+
If `doctor` reports any `✗`, stop and resolve it. The remaining steps assume a clean preflight.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Step 3 — Bootstrap state with `minions init`
|
|
74
|
+
|
|
75
|
+
`minions init` scaffolds `~/.minions/` with a default config, five agent charters, default playbooks, an empty knowledge base, and the engine state files.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
minions init
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
After init you will have (under `~/.minions/`):
|
|
82
|
+
|
|
83
|
+
- `config.json` — projects, agents, schedules
|
|
84
|
+
- `agents/<name>/charter.md` — the personality + boundaries for each of the five default agents (`ripley`, `dallas`, `lambert`, `rebecca`, `ralph`)
|
|
85
|
+
- `engine/dispatch.json`, `engine/control.json`, `engine/metrics.json` — empty queues, ready for first dispatch
|
|
86
|
+
- `pinned.md`, `notes.md` — empty team-memory surfaces
|
|
87
|
+
- `playbooks/`, `prompts/`, `routing.md` — agent prompt scaffolding
|
|
88
|
+
|
|
89
|
+
`init` is safe to re-run; existing customizations to `config.json`, charters, notes, knowledge, and routing are preserved. To force overwrites after a major version bump, use `minions init --force`.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Step 4 — Link your first project with `minions add`
|
|
94
|
+
|
|
95
|
+
A "project" tells Minions which repo to discover work for and where to spawn worktrees. The CLI auto-detects host (GitHub/ADO), org, repo name, and main branch from the directory's git remote.
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
minions add ~/code/your-repo
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
You will be prompted to confirm or override the auto-detected fields, plus a one-line description. The description is **important** — agents read it during routing to decide whether a work item belongs to your repo. Keep it specific (e.g. "Internal billing API and webhook ingestion service") rather than generic ("Backend code").
|
|
102
|
+
|
|
103
|
+
After `minions add` returns, `~/.minions/config.json` has a new `projects[]` entry, and `~/.minions/projects/<name>/` holds the per-project `work-items.json` + `pull-requests.json` trackers.
|
|
104
|
+
|
|
105
|
+
To link several at once interactively, run `minions scan` (default: scans `~` to depth 3 for git repos and lets you multi-select).
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Step 5 — Start engine + dashboard with `minions restart`
|
|
110
|
+
|
|
111
|
+
`minions restart` starts (or restarts) both the engine daemon and the dashboard server. Use `restart` rather than `start` whenever you have edited engine code or playbooks — it ensures the daemon picks up your changes.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
minions restart
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
You should see logs like `Engine started (pid=…)` and `Dashboard listening on http://localhost:7331`. Open the dashboard:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
minions dash # opens http://localhost:7331 in your default browser
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
A quick tour of the panels you will use most often during onboarding:
|
|
124
|
+
|
|
125
|
+
| Panel | What it shows | When to use it |
|
|
126
|
+
|---|---|---|
|
|
127
|
+
| **Projects bar** (top) | All linked projects with descriptions | Confirm Step 4 worked |
|
|
128
|
+
| **Minions Members** | Five agent cards with status, current task, last result | Watch dispatches happen in real time |
|
|
129
|
+
| **Live Output** (per agent) | Streaming `live-output.log` | Tail an agent while it runs — refreshes every 3 s |
|
|
130
|
+
| **Command Center** (CC button, top-right) | Conversational chat with Claude over your full Minions state | Ask questions, queue work, draft plans |
|
|
131
|
+
| **Work Items** | Paginated table of all work, filterable by project/status | Inspect / retry / archive |
|
|
132
|
+
| **Plans & PRD** | Plan markdown drafts and approved PRDs | Approve, discuss-and-revise, or reject plans |
|
|
133
|
+
| **Pull Requests** | Cached PR status (build, review, merge) | Track the PRs your agents create |
|
|
134
|
+
| **Engine** | Dispatch queue, engine log, LLM call performance | Diagnose timing / token issues |
|
|
135
|
+
|
|
136
|
+
The engine ticks every 60 seconds. If the dashboard says "Engine: stopped", run `minions restart` again — the dashboard auto-restarts a dead engine on its next health check, but a manual restart is faster.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Step 6 — Your first dispatch via the Command Center
|
|
141
|
+
|
|
142
|
+
The fastest way to give Minions a task is the Command Center (CC).
|
|
143
|
+
|
|
144
|
+
1. Click the **CC** button in the top-right of the dashboard.
|
|
145
|
+
2. In the slide-out chat, type a one-line description, e.g.:
|
|
146
|
+
```
|
|
147
|
+
Add a brief comment to README.md explaining what minions doctor does
|
|
148
|
+
```
|
|
149
|
+
3. CC will propose an action (usually `POST /api/work-items` or `POST /api/plan`). Confirm.
|
|
150
|
+
|
|
151
|
+
Within one tick (≤60 s), the engine will:
|
|
152
|
+
|
|
153
|
+
1. Pick an idle agent that matches the work type (per `routing.md`).
|
|
154
|
+
2. Create a git worktree for that agent under `<your-repo>/.worktrees/<work-item-id>/`.
|
|
155
|
+
3. Render the appropriate playbook (`playbooks/implement.md` for an implement task) with your project context, charter, pinned notes, and recent team notes.
|
|
156
|
+
4. Spawn `engine/spawn-agent.js`, which invokes the Claude CLI with the rendered prompt.
|
|
157
|
+
|
|
158
|
+
Watch the **Minions Members** card for that agent flip to "working", and click into the **Live Output** tab to tail the streaming agent log.
|
|
159
|
+
|
|
160
|
+
You can do the same thing from the CLI: `minions work "Add a brief comment to README.md…"`.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Step 7 — Your first plan via `/plan`
|
|
165
|
+
|
|
166
|
+
For multi-step work, use a plan instead of a one-shot work item. In the Command Center, type:
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
/plan Audit our README and propose three concrete improvements with examples
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
CC will queue a `plan` work item. The plan agent writes a markdown draft to `~/.minions/plans/<project>-<date>.md`, then chains automatically to a `plan-to-prd` agent that produces a structured PRD JSON in `~/.minions/prd/`. The PRD lands with `status: "awaiting-approval"`.
|
|
173
|
+
|
|
174
|
+
Open **Plans & PRD** in the dashboard. You will see the new plan with three buttons:
|
|
175
|
+
|
|
176
|
+
- **Approve** — materializes one work item per PRD entry, with `depends_on` honored.
|
|
177
|
+
- **Discuss & Revise** — opens a doc-chat modal where you can iterate on the plan with Claude.
|
|
178
|
+
- **Reject** — closes the plan with a reason.
|
|
179
|
+
|
|
180
|
+
After approving, agents start picking up the materialized work items on the next tick. When all PRD items complete, the engine auto-dispatches a `verify` task that builds + tests the changes and writes a manual testing guide. Archiving (after verify) is a manual dashboard action — see [docs/plan-lifecycle.md](plan-lifecycle.md) for the full pipeline.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Step 8 — Your first PR review
|
|
185
|
+
|
|
186
|
+
When an `implement` agent finishes a work item, it pushes a branch and opens a PR. The engine polls PR status every `prPollStatusEvery` ticks (≈12 minutes by default) and dispatches a `review` agent automatically.
|
|
187
|
+
|
|
188
|
+
To watch the cycle end-to-end on your first task:
|
|
189
|
+
|
|
190
|
+
1. Open **Pull Requests** in the dashboard. Find the PR your agent just opened.
|
|
191
|
+
2. Wait for the next status poll (or run `minions dispatch` to wake the daemon immediately). The PR's `reviewStatus` will flip from `pending` to `waiting`, then a review agent will spawn.
|
|
192
|
+
3. Click the agent card to watch the live review. The reviewer reads the diff, runs targeted checks, then posts a comment that starts with `VERDICT: APPROVE` or `VERDICT: REQUEST_CHANGES`.
|
|
193
|
+
4. If the verdict is `REQUEST_CHANGES`, the engine queues a `fix` task for the original author with a feedback note. The cycle repeats until approved or capped by the eval-loop config.
|
|
194
|
+
|
|
195
|
+
You can also trigger reviews manually by commenting on the PR — see [docs/pr-review-fix-loop.md](pr-review-fix-loop.md).
|
|
196
|
+
|
|
197
|
+
> **Note on self-approval:** Minions agents share a single GitHub identity, so `gh pr review --approve` is blocked by GitHub's self-approval rule. Reviewers post a `VERDICT:` **comment** instead, and the engine treats that comment as authoritative.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Debugging — Where to look when something goes wrong
|
|
202
|
+
|
|
203
|
+
When an agent gets stuck, hangs, or produces unexpected output, these files are your first stops. They live under `~/.minions/` (or wherever your runtime root is).
|
|
204
|
+
|
|
205
|
+
| File | What it tells you |
|
|
206
|
+
|---|---|
|
|
207
|
+
| `agents/<id>/live-output.log` | Real-time streaming output from the agent's Claude CLI session — same content the dashboard's Live Output tab shows. Tail this with `tail -f ~/.minions/agents/<id>/live-output.log`. |
|
|
208
|
+
| `agents/<id>/last-prompt.txt` | The most recent rendered prompt the agent received (playbook + system prompt + injected context). Inspect this to see exactly what the agent was asked to do, including pinned notes, team notes, charter, and dispatch context. If your install does not surface this file yet, the live equivalents are `engine/tmp/prompt-<dispatch-id>.md` (while the agent is running) and `engine/contexts/<dispatch-id>.md` (when the prompt was sidecarred because it exceeded `maxDispatchPromptBytes`). |
|
|
209
|
+
| `agents/<id>/output.log` | The agent's final output after completion (post-mortem read; `live-output.log` is the during-run read). |
|
|
210
|
+
| `agents/<id>/history.md` | Last 20 tasks for that agent with timestamps, results, projects, and branches. Useful when a single agent is misbehaving over multiple tasks. |
|
|
211
|
+
| `engine/log.json` | Audit trail of engine actions (last 2500 entries, rotated to 2000). Filter with `jq` for a specific agent or work item. |
|
|
212
|
+
| `engine/dispatch.json` | Pending / active / completed dispatch queue. If an agent appears idle on the dashboard but the queue says `active`, the engine likely lost process tracking — try `minions restart`. |
|
|
213
|
+
| `engine/metrics.json` | Per-agent token usage, runtime, error counts. Useful for spotting agents that are silently retrying. |
|
|
214
|
+
| `notes/inbox/` | Raw findings agents drop after each task. Look here for the latest learnings before the consolidator merges them into `notes.md`. |
|
|
215
|
+
| `pinned.md` and `notes.md` | Team memory that gets injected into every agent prompt. If an agent keeps making the same mistake, pin a correction here. |
|
|
216
|
+
|
|
217
|
+
Other quick debugging knobs:
|
|
218
|
+
|
|
219
|
+
- `minions status` — text snapshot of agents, projects, dispatch queue, and quality metrics.
|
|
220
|
+
- `minions queue` — focused view of the dispatch queue (pending, active, completed).
|
|
221
|
+
- `minions kill` — kills all active agents and resets their dispatches to `pending`. Use when an agent is wedged.
|
|
222
|
+
- `minions cleanup` — manually run the temp-file / worktree / zombie sweep that normally runs every 10 ticks.
|
|
223
|
+
|
|
224
|
+
For deeper dives: [docs/engine-restart.md](engine-restart.md), [docs/auto-discovery.md](auto-discovery.md), and [docs/self-improvement.md](self-improvement.md).
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## What's next
|
|
229
|
+
|
|
230
|
+
You have:
|
|
231
|
+
|
|
232
|
+
- Linked a project, started the engine, opened the dashboard.
|
|
233
|
+
- Dispatched a one-shot task via Command Center.
|
|
234
|
+
- Authored a plan, approved it, and watched the PRD materialize into work items.
|
|
235
|
+
- Watched an agent open and review a PR.
|
|
236
|
+
- Located the files you need to debug a stuck agent.
|
|
237
|
+
|
|
238
|
+
From here, sensible next reads are:
|
|
239
|
+
|
|
240
|
+
- [README.md](../README.md) — the full feature catalog and CLI reference.
|
|
241
|
+
- [CLAUDE.md](../CLAUDE.md) — engineering conventions, tick cycle, locking rules. Read this before editing engine code.
|
|
242
|
+
- [docs/plan-lifecycle.md](plan-lifecycle.md) — plan → PRD → implement → verify → archive in detail.
|
|
243
|
+
- [docs/command-center.md](command-center.md) — full CC capabilities, including doc-chat and plan steering.
|
|
244
|
+
- [docs/pr-review-fix-loop.md](pr-review-fix-loop.md) — how the review/fix cycle is bounded and steered.
|
|
245
|
+
|
|
246
|
+
Welcome to the team.
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Runtime Adapters
|
|
2
|
+
|
|
3
|
+
How Minions abstracts over CLI runtimes (Claude Code, GitHub Copilot CLI, future
|
|
4
|
+
adapters). Engine code never special-cases a runtime by name — every CLI-specific
|
|
5
|
+
behavior is hidden behind an adapter object resolved through `resolveRuntime()`.
|
|
6
|
+
|
|
7
|
+
## What lives in `engine/runtimes/`
|
|
8
|
+
|
|
9
|
+
| File | Role |
|
|
10
|
+
|------|------|
|
|
11
|
+
| `engine/runtimes/index.js` | Adapter registry. `resolveRuntime(name)`, `listRuntimes()`, `registerRuntime()`. Engine code MUST go through these. |
|
|
12
|
+
| `engine/runtimes/claude.js` | Claude Code adapter. Owns binary probe, `--system-prompt-file`, JSONL parser, model shorthands, budget cap, bare mode. |
|
|
13
|
+
| `engine/runtimes/copilot.js` | GitHub Copilot CLI adapter. Owns standalone-vs-`gh-copilot` resolution, stdin-only prompt delivery, `https://api.githubcopilot.com/models` discovery, effort `'max' → 'xhigh'` mapping. |
|
|
14
|
+
|
|
15
|
+
`resolveRuntime(name)` throws when `name` is unknown so misconfigurations surface
|
|
16
|
+
at dispatch time instead of producing silent fallbacks deep inside spawn logic.
|
|
17
|
+
The default name is `'claude'` (matches `engine.defaultCli` fallback).
|
|
18
|
+
|
|
19
|
+
## Adapter Interface
|
|
20
|
+
|
|
21
|
+
Every adapter exports the following surface. The Claude adapter is the canonical
|
|
22
|
+
implementation — copy it as the template for new runtimes and only override the
|
|
23
|
+
methods that genuinely differ.
|
|
24
|
+
|
|
25
|
+
| Member | Type / Returns | Purpose |
|
|
26
|
+
|--------|----------------|---------|
|
|
27
|
+
| `name` | string | Adapter identifier (matches the registry key). |
|
|
28
|
+
| `capabilities` | flag object (see below) | Feature flags consumed by engine code. |
|
|
29
|
+
| `resolveBinary({ env, config? })` | `{ bin, native, leadingArgs }` or null | Probe PATH, npm-globals, package overrides; cached to `engine/<name>-caps.json`. `leadingArgs` is `[]` for direct binaries and `['copilot']` for the `gh copilot` extension fallback. |
|
|
30
|
+
| `capsFile` | absolute path | Cache file for `resolveBinary()` results. |
|
|
31
|
+
| `installHint` | string | Surfaced when `resolveBinary()` returns null. Consumed by `engine/preflight.js` and `engine/spawn-agent.js`. |
|
|
32
|
+
| `listModels()` | `Promise<{id,name,provider}[] | null>` | Model catalog or null when discovery isn't supported. |
|
|
33
|
+
| `modelsCache` | absolute path | Cache file for `listModels()` results. |
|
|
34
|
+
| `spawnScript` | absolute path | Path to the runtime-agnostic spawn wrapper (currently `engine/spawn-agent.js` for both runtimes). |
|
|
35
|
+
| `buildArgs(opts)` | `string[]` | CLI args excluding the binary itself. |
|
|
36
|
+
| `buildSpawnFlags(opts)` | `string[]` | Optional; engine-side flags consumed by `engine/spawn-agent.js` (overrides the default `_buildAgentSpawnFlags`). |
|
|
37
|
+
| `buildPrompt(promptText, sysPromptText)` | string | Final prompt delivered to the CLI. Adapters that lack `--system-prompt-file` (Copilot) prepend a `<system>` block here. |
|
|
38
|
+
| `getUserAssetDirs({ homeDir })` | `string[]` | Runtime-native global asset roots passed to spawn as `--add-dir` so worktrees still see them. |
|
|
39
|
+
| `getSkillRoots({ homeDir, project? })` | `[{ scope, dir, projectName? }]` | Where `collectSkillFiles` looks for native + project skill markdown. |
|
|
40
|
+
| `getSkillWriteTargets({ homeDir, project? })` | `{ personal, project }` | Where `extractSkillsFromOutput` writes auto-extracted skills. |
|
|
41
|
+
| `getResumeSessionId(...)` | string or null | Pre-spawn resume probe; runtime-agnostic stamp prevents cross-runtime session-ID confusion. |
|
|
42
|
+
| `saveSession(...)` | void | Writes `agents/<id>/session.json` after a successful turn. |
|
|
43
|
+
| `resolveModel(input)` | string or undefined | Shorthand expansion / passthrough. Returning undefined causes the adapter to omit `--model` and the CLI uses its own default. |
|
|
44
|
+
| `modelLooksFamiliar(model)` | boolean | Heuristic powering the preflight "stale model after CLI switch" warning. |
|
|
45
|
+
| `parseOutput(raw)` | `{ text, usage, sessionId, model }` | Final-event parser. |
|
|
46
|
+
| `parseStreamChunk(line)` | event object or null | Single JSONL line → typed event. |
|
|
47
|
+
| `parseError(rawOutput)` | `{ message, code, retriable }` | Codes: `auth-failure`, `context-limit`, `budget-exceeded`, `crash`, null. |
|
|
48
|
+
| `createStreamConsumer(ctx)` | consumer object | Stream accumulator used by `engine/llm.js`. |
|
|
49
|
+
| `detectPermissionGate`, `getPromptDeliveryMode`, `usesSystemPromptFile`, `classifyFailure` | misc | Adapter-owned policy that engine code reads through accessors instead of branching on `runtime.name`. |
|
|
50
|
+
|
|
51
|
+
The contract surface is documented inline at the top of `engine/runtimes/claude.js`
|
|
52
|
+
(see the JSDoc block at the start of that file). Keep that block authoritative —
|
|
53
|
+
this table is the human-readable mirror.
|
|
54
|
+
|
|
55
|
+
## Capability Flags
|
|
56
|
+
|
|
57
|
+
Engine code branches on flags, never on runtime names. Add a flag whenever a
|
|
58
|
+
real behavioral split appears between adapters.
|
|
59
|
+
|
|
60
|
+
| Flag | Claude | Copilot | Gates |
|
|
61
|
+
|------|--------|---------|-------|
|
|
62
|
+
| `streaming` | ✓ | ✓ | JSONL events on stdout. |
|
|
63
|
+
| `sessionResume` | ✓ | ✓ | `--resume <id>`. |
|
|
64
|
+
| `midRunSessionId` | ✓ | ✗ | Resumable session ID emitted before the terminal `result` event; when false, steering waits for a checkpoint. |
|
|
65
|
+
| `systemPromptFile` | ✓ | ✗ | sysprompt via `--system-prompt-file` (else inlined into stdin by `buildPrompt`). |
|
|
66
|
+
| `effortLevels` | ✓ | ✓ | `--effort low\|medium\|high\|xhigh`. Copilot maps `'max' → 'xhigh'` inside `resolveModel`/`buildArgs`; Claude leaves `'max'` alone. |
|
|
67
|
+
| `costTracking` | ✓ | ✗ | USD + token counts in the result event. Copilot only emits `premiumRequests`. |
|
|
68
|
+
| `modelShorthands` | ✓ | ✗ | Bare `sonnet`/`opus`/`haiku` accepted. Copilot expects full model IDs (`claude-sonnet-4.5`, `gpt-5.4`). |
|
|
69
|
+
| `modelDiscovery` | ✗ | ✓ | `listModels()` returns a real catalog (Copilot reads `https://api.githubcopilot.com/models`). |
|
|
70
|
+
| `promptViaArg` | ✗ | ✗ | If true, prompt goes via `--prompt <text>` instead of stdin. False on both because Windows ARG_MAX (~32 KB) breaks `-p "<long-prompt>"` outright. |
|
|
71
|
+
| `budgetCap` | ✓ | ✗ | `--max-budget-usd <n>`. |
|
|
72
|
+
| `bareMode` | ✓ | ✗ | `--bare` (suppresses CLAUDE.md auto-discovery). Closest Copilot equivalent is `--no-custom-instructions`, gated separately. |
|
|
73
|
+
| `fallbackModel` | ✓ | ✗ | `--fallback-model <id>` on rate-limit. |
|
|
74
|
+
| `sessionPersistenceControl` | ✓ | ✗ | Engine writes `session.json`. Copilot manages session state internally in `~/.copilot/session-state/`. |
|
|
75
|
+
| `resumePromptCarryover` | ✗ | ✓ | CC resume turns prepend recent visible Q&A in stdin because Copilot's session store is opaque to Minions. |
|
|
76
|
+
| `resumeBookkeepingTurn` | ✓ | — | Claude CLI injects a synthetic "Continue from where you left off." meta turn on `--resume`; CC prompts must tell the model not to treat it as user intent. |
|
|
77
|
+
| `streamConsumer` | ✓ | ✓ | Adapter implements `createStreamConsumer(ctx)` — required by `engine/llm.js` accumulator. |
|
|
78
|
+
|
|
79
|
+
When a behavior is genuinely uniform across all current adapters, it still gets
|
|
80
|
+
a flag if a future runtime might disagree — flags are cheap.
|
|
81
|
+
|
|
82
|
+
## CC vs Agent Runtime Independence
|
|
83
|
+
|
|
84
|
+
Command Center (CC) / doc-chat and the agent fleet are two **independent**
|
|
85
|
+
runtime paths. Setting `engine.ccCli: copilot` for CC alone must NOT switch the
|
|
86
|
+
agent fleet to Copilot, and vice versa. Both paths fall through to
|
|
87
|
+
`engine.defaultCli` (the fleet-wide default), but they never see each other's
|
|
88
|
+
overrides.
|
|
89
|
+
|
|
90
|
+
Six helpers in `engine/shared.js` are the single source of truth for runtime/model
|
|
91
|
+
resolution. Engine code MUST go through them instead of reading config keys
|
|
92
|
+
directly.
|
|
93
|
+
|
|
94
|
+
| Helper | Chain |
|
|
95
|
+
|--------|-------|
|
|
96
|
+
| `resolveAgentCli(agent, engine)` | `agent.cli` → `engine.defaultCli` → `'claude'` |
|
|
97
|
+
| `resolveCcCli(engine)` | `engine.ccCli` → `engine.defaultCli` → `'claude'` |
|
|
98
|
+
| `resolveAgentModel(agent, engine)` | `agent.model` → `engine.defaultModel` → undefined |
|
|
99
|
+
| `resolveCcModel(engine)` | `engine.ccModel` → `engine.defaultModel` → undefined |
|
|
100
|
+
| `resolveAgentMaxBudget(agent, engine)` | `agent.maxBudgetUsd` → `engine.maxBudgetUsd`. Honors literal `0`. |
|
|
101
|
+
| `resolveAgentBareMode(agent, engine)` | `agent.bareMode` → `engine.claudeBareMode` → false. Strict null check so per-agent `false` overrides engine `true`. |
|
|
102
|
+
|
|
103
|
+
Agent dispatch resolves the runtime once at spawn time:
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
// engine.js spawnAgent (~line 1158)
|
|
107
|
+
const runtime = resolveRuntime(shared.resolveAgentCli(agentConfig, engineConfig));
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
CC / doc-chat resolves the runtime via `engine/llm.js` `_resolveRuntimeFor()`:
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
// engine/llm.js (~line 247)
|
|
114
|
+
if (!runtimeName && callOpts.engineConfig) runtimeName = resolveCcCli(callOpts.engineConfig);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Tests in `test/unit/runtime-fleet-helpers.test.js` enforce that
|
|
118
|
+
`resolveAgentCli` does NOT fall through to `engine.ccCli` and that `resolveCcCli`
|
|
119
|
+
does NOT inspect any agent settings.
|
|
120
|
+
|
|
121
|
+
## `--cli` / `--model` / `--effort` Knobs
|
|
122
|
+
|
|
123
|
+
Three CLI flags switch the fleet without dashboard interaction. They forward
|
|
124
|
+
through `engine/cli.js` `parseRuntimeFleetFlags()` and persist into `config.json`.
|
|
125
|
+
|
|
126
|
+
| Flag | Persists to | Notes |
|
|
127
|
+
|------|-------------|-------|
|
|
128
|
+
| `--cli <runtime>` | `engine.defaultCli` | Validated against the runtime registry; unknown runtimes exit non-zero with the registered list. Implicitly clears any explicit `engine.ccCli` (with a warning naming the prior value). |
|
|
129
|
+
| `--model <id>` | `engine.defaultModel` | Empty-string sentinel (`--model ''`) **deletes** `engine.defaultModel` — never pin to a literal empty string. Compatibility with `defaultCli` is checked; mismatch emits an incompatibility warning suggesting `--model ''`. |
|
|
130
|
+
| `--effort <level>` | dispatched as `opts.effort` | One of `low|medium|high|xhigh`. Copilot maps `'max' → 'xhigh'`; Claude passes `'max'` through unchanged. Capability-gated by `effortLevels`. |
|
|
131
|
+
|
|
132
|
+
Examples:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
minions start --cli copilot --model claude-sonnet-4.5
|
|
136
|
+
minions restart --cli claude --model '' # '' DELETES defaultModel
|
|
137
|
+
minions config set-cli copilot --model gpt-5.4
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Inside the adapters, `buildArgs(opts)` translates the resolved values into the
|
|
141
|
+
CLI-specific argv:
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
// engine/runtimes/copilot.js buildArgs
|
|
145
|
+
if (model) args.push('--model', String(model));
|
|
146
|
+
if (mappedEffort) args.push('--effort', mappedEffort);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
When `resolveAgentModel` returns undefined the adapter omits `--model` from
|
|
150
|
+
`buildArgs` entirely and the underlying CLI uses its own default.
|
|
151
|
+
|
|
152
|
+
## Per-Runtime Skill Roots
|
|
153
|
+
|
|
154
|
+
Skills live in runtime-native directories so a skill placed there is genuinely
|
|
155
|
+
visible to that runtime when invoked outside Minions. The adapter's
|
|
156
|
+
`getSkillRoots()` and `getSkillWriteTargets()` are the discovery and write
|
|
157
|
+
contracts.
|
|
158
|
+
|
|
159
|
+
| Adapter | Personal skill root | Project skill roots | Auto-extraction write targets |
|
|
160
|
+
|---------|---------------------|---------------------|-------------------------------|
|
|
161
|
+
| Claude | `~/.claude/skills` | `<repo>/.claude/skills` | personal: `~/.claude/skills`; project: `<repo>/.claude/skills` |
|
|
162
|
+
| Copilot | `~/.copilot/skills` (+ `~/.agents/skills`) | `<repo>/.github/skills`, `<repo>/.agents/skills` | personal: `~/.copilot/skills`; project: `<repo>/.github/skills` |
|
|
163
|
+
|
|
164
|
+
`getUserAssetDirs()` returns the union of runtime-native global roots and is
|
|
165
|
+
passed to spawn as `--add-dir` so spawned agents read the same global assets the
|
|
166
|
+
user sees outside Minions. The cross-runtime portable location
|
|
167
|
+
`~/.agents/skills` is included by every adapter that opts into it (Copilot today;
|
|
168
|
+
add it to new adapters when the runtime can read directly from there).
|
|
169
|
+
|
|
170
|
+
## The "No Runtime-Name Branching" Rule
|
|
171
|
+
|
|
172
|
+
The whole point of this layer: **engine code MUST gate behavior on
|
|
173
|
+
`runtime.capabilities.*` flags, not on `runtime.name === 'claude'` comparisons.**
|
|
174
|
+
|
|
175
|
+
If you find yourself wanting to write `if (runtime.name === 'copilot')` in
|
|
176
|
+
engine code, the correct response is one of:
|
|
177
|
+
|
|
178
|
+
1. Add a capability flag to the contract and set it on every adapter.
|
|
179
|
+
2. Add a method to the adapter and call it through the runtime object.
|
|
180
|
+
3. Move the special case into the adapter's `buildArgs` / `buildPrompt` /
|
|
181
|
+
`parseOutput` where it belongs.
|
|
182
|
+
|
|
183
|
+
Tests in `test/unit/runtime-adapters.test.js` and `test/unit/copilot-adapter.test.js`
|
|
184
|
+
enforce the contract; the source-inspection tests in `test/unit.test.js` look
|
|
185
|
+
for `resolveRuntime(...)` call sites and reject runtime-name comparisons in new
|
|
186
|
+
code.
|
|
187
|
+
|
|
188
|
+
## Adding a New Runtime
|
|
189
|
+
|
|
190
|
+
1. Create `engine/runtimes/<name>.js` implementing the full adapter contract.
|
|
191
|
+
Copy `claude.js` as the template and override only the members that differ.
|
|
192
|
+
Set a useful `installHint`.
|
|
193
|
+
2. Register it in `engine/runtimes/index.js`:
|
|
194
|
+
```js
|
|
195
|
+
registry.set('<name>', require('./<name>'));
|
|
196
|
+
```
|
|
197
|
+
3. Add unit coverage modeled on `test/unit/runtime-adapters.test.js`.
|
|
198
|
+
|
|
199
|
+
Once registered, the dashboard `/api/runtimes` endpoint, the `--cli` flag
|
|
200
|
+
validator, the agent CLI dropdown, the `engine/preflight.js` per-runtime binary
|
|
201
|
+
check, and the model discovery cache all light up automatically. Use capability
|
|
202
|
+
flags — never special-case in engine code.
|
|
203
|
+
|
|
204
|
+
## See Also
|
|
205
|
+
|
|
206
|
+
- `CLAUDE.md` → "Runtime Adapters" section — short reference table that mirrors
|
|
207
|
+
this doc and is enforced by `test/unit.test.js`.
|
|
208
|
+
- `docs/copilot-cli-schema.md` — empirical Copilot CLI behavior captured during
|
|
209
|
+
the spike that produced the Copilot adapter; cite it when changing
|
|
210
|
+
Copilot-specific capability flags.
|
|
211
|
+
- `engine/runtimes/claude.js` JSDoc header — authoritative adapter contract.
|
|
212
|
+
- `engine/shared.js` `resolveAgentCli` / `resolveCcCli` etc. — the only
|
|
213
|
+
supported way to read runtime / model selection from config.
|