@chorus-aidlc/chorus-pi 0.0.3 → 0.17.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.
- package/README.md +87 -0
- package/agents/chorus-code-reviewer.md +2 -1
- package/agents/chorus-proposal-reviewer.md +2 -1
- package/agents/chorus-task-reviewer.md +2 -1
- package/bin/chorus-mcp-call.sh +1 -1
- package/extensions/chorus.ts +89 -6
- package/lib/lib.ts +43 -0
- package/package.json +6 -7
- package/skills/brainstorm/SKILL.md +1 -1
- package/skills/chorus/SKILL.md +2 -2
- package/skills/chorus-cli/SKILL.md +11 -1
- package/skills/develop/SKILL.md +1 -1
- package/skills/docs/SKILL.md +1 -1
- package/skills/idea/SKILL.md +1 -1
- package/skills/openspec-aware/SKILL.md +2 -2
- package/skills/orchestrate/SKILL.md +1 -1
- package/skills/proposal/SKILL.md +1 -1
- package/skills/quick-dev/SKILL.md +1 -1
- package/skills/review/SKILL.md +1 -1
- package/skills/yolo/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -93,6 +93,93 @@ packages/chorus-pi/
|
|
|
93
93
|
|
|
94
94
|
The extension goes beyond the Codex port in one key way: by using Pi's `tool_call` event (pre-execution, mutable input), it **auto-injects the Chorus session UUID + workflow into each dispatched worker's task** — the Pi-native equivalent of Claude's `SubagentStart` hook. The Codex port has no pre-spawn mutation channel, so its workers must manage sessions manually. On Pi, dispatch a worker via the `subagent` tool and the extension handles session creation + context injection, then closes the session when the (ephemeral) tool call returns.
|
|
95
95
|
|
|
96
|
+
|
|
97
|
+
### Subagent run modes: blocking (bundled) vs async (nicobailon `pi-subagents`)
|
|
98
|
+
|
|
99
|
+
The bundled `subagent` tool (pi's official reference pattern) is **blocking**:
|
|
100
|
+
spawn → run → exit within one tool call, so the extension closes the Chorus
|
|
101
|
+
session at `tool_result`. If you instead use the nicobailon `pi-subagents`
|
|
102
|
+
package's `subagent` tool, top-level launches are **async (detached)** by
|
|
103
|
+
default: `tool_result` returns immediately with `details.asyncId` and the run
|
|
104
|
+
completes later. The extension detects this case (`asyncId`/`runId` in
|
|
105
|
+
`details`) and defers session close to `subagent:async-complete` /
|
|
106
|
+
`subagent:process-terminal` (with `session_shutdown` sweep as a final guard).
|
|
107
|
+
Tasks that already carry an injected `--- Chorus session` block (e.g. a
|
|
108
|
+
main-agent wave template) are never re-injected.
|
|
109
|
+
|
|
110
|
+
### Coexistence with nicobailon `pi-subagents`: load-order rule
|
|
111
|
+
|
|
112
|
+
The bundled subagent (pi's official reference pattern, at `extensions/subagent/`)
|
|
113
|
+
registers a tool named `subagent`. The nicobailon `pi-subagents` package registers
|
|
114
|
+
a tool with the **same name**. pi's extension loader rejects a duplicate tool
|
|
115
|
+
registration with a conflict error (verified on pi 0.84.4:
|
|
116
|
+
`Tool "subagent" conflicts with ...`), so the two cannot both register.
|
|
117
|
+
|
|
118
|
+
**Recommended setup (keep nicobailon, zero conflicts)**: exclude the bundled
|
|
119
|
+
subagent extension via a package filter in `settings.packages` — pi's package
|
|
120
|
+
entries accept an object form with per-resource glob patterns:
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
"packages": [
|
|
124
|
+
"npm:pi-subagents",
|
|
125
|
+
{
|
|
126
|
+
"source": "git:github.com/Chorus-AIDLC/chorus/packages/chorus-pi",
|
|
127
|
+
"extensions": ["!extensions/subagent/**"]
|
|
128
|
+
}
|
|
129
|
+
]
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
This keeps `chorus.ts` (session hooks) and the `agents/*.md` files (discovered
|
|
133
|
+
via `pi.subagents.agents`) while the bundled `subagent` tool never registers —
|
|
134
|
+
no conflict error, nicobailon wins deterministically.
|
|
135
|
+
|
|
136
|
+
| Setup | What happens |
|
|
137
|
+
|-------|--------------|
|
|
138
|
+
| Only `@chorus-aidlc/chorus-pi` (no external subagents) | Bundled subagent registers and handles dispatch (single/parallel/chain, blocking) |
|
|
139
|
+
| Both installed, with the filter above | nicobailon's `subagent` tool is the only one. Chorus session hooks keep working (they match on the tool name) |
|
|
140
|
+
| Both installed, no filter: `npm:pi-subagents` listed **before** chorus-pi | nicobailon wins; the bundled subagent reports a conflict error at load (harmless inside an interactive session, noisy for CLI commands like `pi packages list`) |
|
|
141
|
+
| Both installed, no filter: `npm:pi-subagents` listed **after** chorus-pi | Bundled subagent wins (it loaded first); nicobailon's tool is rejected. Flip the order to switch |
|
|
142
|
+
**How to verify which implementation is active**: run
|
|
143
|
+
`subagent({ action: "list" })`. nicobailon output shows `Package agents /
|
|
144
|
+
Builtin agents / User agents` sections; the bundled subagent's output has no
|
|
145
|
+
such sections.
|
|
146
|
+
|
|
147
|
+
### Tips when combining with nicobailon `pi-subagents`
|
|
148
|
+
|
|
149
|
+
- **Sessions work with either tool.** Chorus hooks match on the tool name,
|
|
150
|
+
so `checkin → in_progress → report → checkout → submit_for_verify` flows are
|
|
151
|
+
identical; only close timing differs (blocking closes at `tool_result`, async
|
|
152
|
+
closes on `subagent:async-complete`/`process-terminal`).
|
|
153
|
+
- **Why the packaged agents do not set `async: false`.** Under nicobailon
|
|
154
|
+
0.65 a foreground (`async: false`) child runs inside the parent process
|
|
155
|
+
and never loads the parent's ambient extensions — tools registered by an
|
|
156
|
+
ambient adapter such as `pi-mcp-adapter` (`mcp`, `mcpScript`) are
|
|
157
|
+
unavailable, and nicobailon's child-tool diagnostic treats an allowlist
|
|
158
|
+
that declares them as a failed run (exit 1) even if the agent never
|
|
159
|
+
called them. The Chorus reviewers/worker need `mcp` to post verdicts and
|
|
160
|
+
check in, so they run as background children (nicobailon default). Wait
|
|
161
|
+
for completion with `bg_wait`/the run notification; the bundled subagent
|
|
162
|
+
is unaffected because its child is a separate `pi --mode json` process
|
|
163
|
+
that loads extensions.
|
|
164
|
+
- **`workflowScript` / `runs.run` / `runs.all`**: nicobailon-only. The bundled
|
|
165
|
+
subagent has no `workflowScript` mode — use `parallel`/`chain` via its own
|
|
166
|
+
schema, or keep nicobailon for scripted waves.
|
|
167
|
+
- **Model selection per reviewer**: nicobailon honors `subagent({..., model})`
|
|
168
|
+
per call, `subagents.agentOverrides.<name>.model` in settings, and agent
|
|
169
|
+
frontmatter `model:`. The bundled subagent honors only agent frontmatter
|
|
170
|
+
`model:` (it reads `name`/`description`/`tools`/`model`; its schema has no
|
|
171
|
+
per-call model parameter) — set it in `~/.pi/agent/agents/chorus-*-reviewer.md`.
|
|
172
|
+
- **Agent files**: bundled subagent reads package `agents/*.md` + `~/.pi/agent/agents/*.md`
|
|
173
|
+
(user overrides package). nicobailon reads builtin/package/user/project with
|
|
174
|
+
richer frontmatter (`excludeTools`, `thinking`, `inheritSkills`, `extensions`,
|
|
175
|
+
per-agent `tools` allowlists, `model`, ...).
|
|
176
|
+
- **Tool-name clash**: both register a `subagent` tool and pi rejects a duplicate
|
|
177
|
+
registration with a conflict error. To keep nicobailon (needed for its
|
|
178
|
+
async/`workflowScript` features), use the package filter above (exclude
|
|
179
|
+
`extensions/subagent/**`); if you instead rely on ordering, list
|
|
180
|
+
`npm:pi-subagents` **before** chorus-pi. Either way, verify with
|
|
181
|
+
`subagent({ action: "list" })` (nicobailon shows `Package/Builtin/User agents`
|
|
182
|
+
sections; bundled does not).
|
|
96
183
|
## License
|
|
97
184
|
|
|
98
185
|
AGPL-3.0
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: chorus-code-reviewer
|
|
3
3
|
description: Final ship-time review of an Idea's aggregate code change — the whole feature across all its tasks, not one task. Read-only; posts a VERDICT comment on the Idea. Spawn via the blocking subagent tool after the last task of an idea-rooted proposal is verified.
|
|
4
|
-
tools: read, grep, find, ls, bash, mcp
|
|
4
|
+
tools: read, grep, find, ls, bash, mcp, mcpScript
|
|
5
|
+
acceptance: { level: "none", reason: "read-only chorus reviewer; verdict is posted via chorus_add_comment to Chorus, not returned to parent; suppress acceptance-report injection" }
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
CRITICAL: READ-ONLY code review of an ENTIRE Idea's aggregate change. You CANNOT edit, write, or create files in the project directory.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: chorus-proposal-reviewer
|
|
3
3
|
description: Review submitted Chorus proposals for quality — check document completeness, task granularity, AC alignment, and cross-task dependencies. Spawn via the blocking subagent tool after chorus_pm_submit_proposal.
|
|
4
|
-
tools: read, grep, find, ls, bash, mcp
|
|
4
|
+
tools: read, grep, find, ls, bash, mcp, mcpScript
|
|
5
|
+
acceptance: { level: "none", reason: "read-only chorus reviewer; verdict is posted via chorus_add_comment to Chorus, not returned to parent; suppress acceptance-report injection" }
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
CRITICAL: READ-ONLY proposal review. You CANNOT edit, write, create files, or run Bash commands beyond read-only inspection.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: chorus-task-reviewer
|
|
3
3
|
description: Review submitted Chorus tasks — verify implementation against AC and proposal documents. Spawn via the blocking subagent tool after chorus_submit_for_verify.
|
|
4
|
-
tools: read, grep, find, ls, bash, mcp
|
|
4
|
+
tools: read, grep, find, ls, bash, mcp, mcpScript
|
|
5
|
+
acceptance: { level: "none", reason: "read-only chorus reviewer; verdict is posted via chorus_add_comment to Chorus, not returned to parent; suppress acceptance-report injection" }
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
CRITICAL: READ-ONLY task review. You CANNOT edit, write, or create files in the project directory.
|
package/bin/chorus-mcp-call.sh
CHANGED
|
@@ -143,7 +143,7 @@ ACCEPT="Accept: application/json, text/event-stream"
|
|
|
143
143
|
CT="Content-Type: application/json"
|
|
144
144
|
|
|
145
145
|
INIT=$(cat <<JSON
|
|
146
|
-
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"chorus-codex-hook","version":"0.17.
|
|
146
|
+
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"chorus-codex-hook","version":"0.17.3"}}}
|
|
147
147
|
JSON
|
|
148
148
|
)
|
|
149
149
|
|
package/extensions/chorus.ts
CHANGED
|
@@ -18,9 +18,13 @@
|
|
|
18
18
|
* lacks (Codex has no pre-spawn mutation channel, so its
|
|
19
19
|
* workers must manage sessions manually).
|
|
20
20
|
* - tool_result → close the ephemeral worker session(s) once the `subagent`
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
21
|
+
* - tool_result → for the official blocking subagent, close the ephemeral
|
|
22
|
+
* worker session(s) once the `subagent` tool call returns
|
|
23
|
+
* (spawn → run → exit within one tool call, so there is no
|
|
24
|
+
* persistent agentId and no separate close tool). For the
|
|
25
|
+
* nicobailon `pi-subagents` tool (async/detached by default,
|
|
26
|
+
* `details.asyncId` on tool_result) the sessions are deferred
|
|
27
|
+
* and closed on subagent:async-complete / process-terminal.
|
|
24
28
|
* → reviewer nudges after submit_proposal / submit_for_verify
|
|
25
29
|
* / admin_verify_task (the 3 PostToolUse hooks)
|
|
26
30
|
* - tool_execution_end → fallback close of the worker session(s) if tool_result
|
|
@@ -40,6 +44,8 @@ import {
|
|
|
40
44
|
isWorkerAgent,
|
|
41
45
|
subagentTaskItems,
|
|
42
46
|
sessionWorkflow,
|
|
47
|
+
hasSessionMarker,
|
|
48
|
+
extractRunIdFromToolResultEvent,
|
|
43
49
|
detectOpenSpec,
|
|
44
50
|
buildSessionBanner,
|
|
45
51
|
parseMaxCodeReviewRounds,
|
|
@@ -196,6 +202,10 @@ async function mcpCall<T = unknown>(tool: string, args: Record<string, unknown>
|
|
|
196
202
|
//
|
|
197
203
|
// toolCallId → the Chorus session UUIDs created for that `subagent` invocation.
|
|
198
204
|
const callSessions = new Map<string, string[]>();
|
|
205
|
+
// runId (nicobailon async/detached `subagent` runs) → sessionUuid(s); closed on
|
|
206
|
+
// subagent:async-complete / subagent:process-terminal (blocking runs close at
|
|
207
|
+
// tool_result via callSessions and never enter this map).
|
|
208
|
+
const runIdToSid = new Map<string, string[]>();
|
|
199
209
|
let checkinContext: string | null = null;
|
|
200
210
|
let injectedOnce = false;
|
|
201
211
|
|
|
@@ -360,6 +370,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
360
370
|
const created: string[] = [];
|
|
361
371
|
for (const item of subagentTaskItems(event.input)) {
|
|
362
372
|
if (!isWorkerAgent(item.agent)) continue;
|
|
373
|
+
// Manual main-agent template already injected — never double-inject.
|
|
374
|
+
if (hasSessionMarker(item.task)) continue;
|
|
363
375
|
try {
|
|
364
376
|
const session = await mcpCall<{ uuid?: string }>("chorus_create_session", { name: item.agent });
|
|
365
377
|
if (!session?.uuid) continue;
|
|
@@ -382,10 +394,24 @@ export default function (pi: ExtensionAPI) {
|
|
|
382
394
|
if (!CONFIGURED) return;
|
|
383
395
|
|
|
384
396
|
// ── subagent tool finished → close the worker session(s) ───────────
|
|
385
|
-
//
|
|
386
|
-
//
|
|
387
|
-
//
|
|
397
|
+
// The official blocking subagent closes sessions at tool_result; the
|
|
398
|
+
// nicobailon `pi-subagents` tool is async (detached) by default, so its
|
|
399
|
+
// tool_result carries `details.asyncId` and the run completes later via
|
|
400
|
+
// the pi event bus — in that case move the sessions to runIdToSid and
|
|
401
|
+
// let subagent:async-complete / subagent:process-terminal close them.
|
|
388
402
|
if (event.toolName === "subagent") {
|
|
403
|
+
const runId = extractRunIdFromToolResultEvent(event);
|
|
404
|
+
if (runId) {
|
|
405
|
+
const sids = callSessions.get(event.toolCallId);
|
|
406
|
+
if (sids && sids.length > 0) {
|
|
407
|
+
runIdToSid.set(runId, [...(runIdToSid.get(runId) ?? []), ...sids]);
|
|
408
|
+
callSessions.delete(event.toolCallId);
|
|
409
|
+
ctx.ui.notify(`Chorus session(s): ${sids.map((s) => s.slice(0, 8)).join(",")}… deferred to async run ${runId.slice(0, 8)}…`, "info");
|
|
410
|
+
}
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
// Blocking run (or no run id) — close now. closeCallSessions is
|
|
414
|
+
// idempotent and retains any session whose close fails for a shutdown retry.
|
|
389
415
|
await closeCallSessions(event.toolCallId, ctx);
|
|
390
416
|
return;
|
|
391
417
|
}
|
|
@@ -431,20 +457,77 @@ export default function (pi: ExtensionAPI) {
|
|
|
431
457
|
pi.on("tool_execution_end", async (event, ctx) => {
|
|
432
458
|
if (!CONFIGURED) return;
|
|
433
459
|
if (event.toolName === "subagent") {
|
|
460
|
+
// tool_result already moved async sessions to runIdToSid — nothing left
|
|
461
|
+
// in callSessions for them. Blocking runs (or failed injection) close here.
|
|
434
462
|
await closeCallSessions(event.toolCallId, ctx);
|
|
435
463
|
}
|
|
436
464
|
});
|
|
437
465
|
|
|
466
|
+
// ── nicobailon async/detached `subagent` runs: close by runId ──────
|
|
467
|
+
// tool_result deferred these sessions to runIdToSid; completion arrives on
|
|
468
|
+
// the pi event bus. Delete the mapping BEFORE issuing the close so a
|
|
469
|
+
// duplicate lifecycle event cannot double-close; re-add on failure so the
|
|
470
|
+
// shutdown sweep can still retry it.
|
|
471
|
+
//
|
|
472
|
+
// In-flight closes are tracked so session_shutdown can await them before
|
|
473
|
+
// sweeping: a close that fails after the sweep ran would otherwise re-add
|
|
474
|
+
// its entry after the map was cleared (retry lost + stale entry).
|
|
475
|
+
const inflightCloses = new Set<Promise<void>>();
|
|
476
|
+
const closeRunSessions = (runId: string): void => {
|
|
477
|
+
const sids = runIdToSid.get(runId);
|
|
478
|
+
if (!sids || sids.length === 0) return;
|
|
479
|
+
runIdToSid.delete(runId);
|
|
480
|
+
const p: Promise<void> = (async () => {
|
|
481
|
+
const failed: string[] = [];
|
|
482
|
+
for (const sid of sids) {
|
|
483
|
+
try { await mcpCall("chorus_close_session", { sessionUuid: sid }); } catch { failed.push(sid); }
|
|
484
|
+
}
|
|
485
|
+
if (failed.length > 0) {
|
|
486
|
+
runIdToSid.set(runId, failed);
|
|
487
|
+
console.warn(`[chorus-pi] failed to close ${failed.length} session(s) for run ${runId}: ${failed.join(", ")} — will retry at session_shutdown`);
|
|
488
|
+
}
|
|
489
|
+
})();
|
|
490
|
+
inflightCloses.add(p);
|
|
491
|
+
void p.finally(() => inflightCloses.delete(p));
|
|
492
|
+
};
|
|
493
|
+
// Only `runId` is trusted on async-complete; `id` (runId-or-id shape) is
|
|
494
|
+
// accepted only on process-terminal, since async-complete could carry an
|
|
495
|
+
// unrelated id field alongside runId.
|
|
496
|
+
const eventBusRunId = (data: unknown, allowId: boolean): string | null => {
|
|
497
|
+
const d = (data ?? {}) as Record<string, unknown>;
|
|
498
|
+
if (typeof d.runId === "string" && d.runId) return d.runId;
|
|
499
|
+
if (allowId && typeof d.id === "string" && d.id) return d.id;
|
|
500
|
+
return null;
|
|
501
|
+
};
|
|
502
|
+
pi.events.on("subagent:async-complete", (data) => {
|
|
503
|
+
const runId = eventBusRunId(data, false);
|
|
504
|
+
if (runId) closeRunSessions(runId);
|
|
505
|
+
});
|
|
506
|
+
pi.events.on("subagent:process-terminal", (data) => {
|
|
507
|
+
const runId = eventBusRunId(data, true);
|
|
508
|
+
if (runId) closeRunSessions(runId);
|
|
509
|
+
});
|
|
510
|
+
|
|
438
511
|
// SessionEnd → close any stray worker sessions (replaces Claude's on-session-end.sh).
|
|
439
512
|
// Retries every session still tracked in callSessions (e.g. a subagent call whose
|
|
440
513
|
// close failed and was retained, or that never saw a tool_result/tool_execution_end).
|
|
441
514
|
pi.on("session_shutdown", async () => {
|
|
515
|
+
// Wait for in-flight async closes to settle FIRST: a close that fails
|
|
516
|
+
// re-adds into runIdToSid, and the sweep below must see it (otherwise the
|
|
517
|
+
// retry is lost and a stale entry survives the clear).
|
|
518
|
+
await Promise.allSettled([...inflightCloses]);
|
|
442
519
|
for (const sids of callSessions.values()) {
|
|
443
520
|
for (const sid of sids) {
|
|
444
521
|
await mcpCall("chorus_close_session", { sessionUuid: sid }).catch(() => {});
|
|
445
522
|
}
|
|
446
523
|
}
|
|
524
|
+
for (const sids of runIdToSid.values()) {
|
|
525
|
+
for (const sid of sids) {
|
|
526
|
+
await mcpCall("chorus_close_session", { sessionUuid: sid }).catch(() => {});
|
|
527
|
+
}
|
|
528
|
+
}
|
|
447
529
|
callSessions.clear();
|
|
530
|
+
runIdToSid.clear();
|
|
448
531
|
injectedOnce = false;
|
|
449
532
|
checkinContext = null;
|
|
450
533
|
mcpSessionId = null;
|
package/lib/lib.ts
CHANGED
|
@@ -182,6 +182,49 @@ export function sessionWorkflow(sessionUuid: string): string {
|
|
|
182
182
|
].join("\n");
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
/**
|
|
186
|
+
* True when a worker task already carries an injected session block.
|
|
187
|
+
*
|
|
188
|
+
* Matches the block header at the start of a line (`--- Chorus session`), so
|
|
189
|
+
* prose that merely mentions "Chorus session" does not suppress injection.
|
|
190
|
+
* Covers both this extension's injected block and any main-agent template.
|
|
191
|
+
* The header is exactly "--- Chorus session" followed by " (…)" or end of line;
|
|
192
|
+
* a hyphenated continuation like "--- Chorus session-notes" is not a header.
|
|
193
|
+
*/
|
|
194
|
+
export function hasSessionMarker(task: string): boolean {
|
|
195
|
+
return /^--- Chorus session(?=[ (\u2014]|$)/m.test(task);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Detect an async (detached) nicobailon pi-subagents `subagent` run from its
|
|
200
|
+
* tool_result EVENT, and extract the run id that its completion events will
|
|
201
|
+
* carry.
|
|
202
|
+
*
|
|
203
|
+
* The official bundled subagent (blocking) returns at tool_result with no run
|
|
204
|
+
* id — the session lifecycle closes there. The nicobailon `pi-subagents` tool
|
|
205
|
+
* launches async (detached) by default: spawn returns immediately with
|
|
206
|
+
* `details.asyncId`, and completion arrives later on the pi event bus as
|
|
207
|
+
* `subagent:async-complete` / `subagent:process-terminal` with `{runId}`/`{id}`.
|
|
208
|
+
* Only `details.asyncId`/`details.runId` are trusted — the nicobailon contract
|
|
209
|
+
* always carries the run id in `details` for async launches (verified against
|
|
210
|
+
* pi-subagents src/runs/foreground/subagent-executor.ts: async started returns
|
|
211
|
+
* `details: { mode, results, asyncId, asyncDir }` (:1584/:1375), and nicobailon
|
|
212
|
+
* itself reads `result.details.asyncId` (:1711/:1967/:2119); `id`/`prefix`
|
|
213
|
+
* appear only in completion-event payloads, never in tool_result details), and
|
|
214
|
+
* a blocking run's worker output (even standalone JSON) must never be
|
|
215
|
+
* misclassified as an async run (which would leak the session until
|
|
216
|
+
* session_shutdown).
|
|
217
|
+
*/
|
|
218
|
+
export function extractRunIdFromToolResultEvent(event: {
|
|
219
|
+
details?: unknown;
|
|
220
|
+
}): string | null {
|
|
221
|
+
const d = (event.details ?? {}) as Record<string, unknown>;
|
|
222
|
+
for (const key of ["asyncId", "runId"]) {
|
|
223
|
+
if (typeof d[key] === "string" && (d[key] as string).length > 0) return d[key] as string;
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
|
|
185
228
|
/**
|
|
186
229
|
* Resolved OpenSpec mode for a repo. `active` is the effective on/off; `reason`
|
|
187
230
|
* is a human-readable explanation; `optout` marks an explicit opt-out (so the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chorus-aidlc/chorus-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.3",
|
|
4
4
|
"description": "Chorus AI-DLC collaboration platform extension for the Pi coding agent. Provides skills for every stage of the AI-DLC lifecycle, read-only reviewer subagents, and session-aware extension hooks. The Chorus MCP server is auto-discovered from the repo's .mcp.json by pi-mcp-adapter — no installer required.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Chorus-AIDLC"
|
|
@@ -51,12 +51,11 @@
|
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
53
|
"pi": {
|
|
54
|
-
"extensions": [
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
]
|
|
54
|
+
"extensions": ["./extensions"],
|
|
55
|
+
"skills": ["./skills"],
|
|
56
|
+
"subagents": {
|
|
57
|
+
"agents": ["./agents"]
|
|
58
|
+
}
|
|
60
59
|
},
|
|
61
60
|
"//note": "Reviewer subagents use pi's official subagent pattern, bundled at extensions/subagent/ (index.ts + agents.ts, copied from earendil-works/pi's examples). The copied agents.ts discovers this package's own agents/*.md via a package-relative BUNDLED_DIR, so the 3 reviewer agents load with ZERO manual copy into ~/.pi/agent/agents/. pi auto-loads extensions/subagent/index.ts as a subdirectory-with-index extension (no manifest entry needed)."
|
|
62
61
|
}
|
package/skills/chorus/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: Chorus AI Agent collaboration platform — overview, common tools,
|
|
|
4
4
|
license: AGPL-3.0
|
|
5
5
|
metadata:
|
|
6
6
|
author: chorus
|
|
7
|
-
version: "0.17.
|
|
7
|
+
version: "0.17.3"
|
|
8
8
|
category: project-management
|
|
9
9
|
mcp_server: chorus
|
|
10
10
|
---
|
|
@@ -19,7 +19,7 @@ This is the **core skill** — it covers the platform overview, shared tools, an
|
|
|
19
19
|
> - **Namespaced form (preferred):** `mcp__chorus__<tool>` — e.g. `mcp__chorus__chorus_checkin`, `mcp__chorus__chorus_pm_create_idea`.
|
|
20
20
|
> - **Flattened alias:** `chorus_<tool>` → the server prefix produces a **doubled** `chorus_chorus_*` (e.g. `chorus_chorus_pm_create_idea`). The double `chorus_` is expected, not a typo.
|
|
21
21
|
>
|
|
22
|
-
> So: wherever a skill names a tool `chorus_…`, call it as `mcp__chorus__chorus_…`. If a tool ever reads as *"not found"*, you almost certainly dropped the `mcp__chorus__` prefix. This is Pi-specific —
|
|
22
|
+
> So: wherever a skill names a tool `chorus_…`, call it as `mcp__chorus__chorus_…`. If a tool ever reads as *"not found"*, you almost certainly dropped the `mcp__chorus__` prefix. This is Pi-specific — other harnesses resolve the bare names directly.
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
@@ -4,7 +4,7 @@ description: How to install, configure, and use the `chorus` CLI — install it,
|
|
|
4
4
|
license: AGPL-3.0
|
|
5
5
|
metadata:
|
|
6
6
|
author: chorus
|
|
7
|
-
version: "0.17.
|
|
7
|
+
version: "0.17.3"
|
|
8
8
|
category: project-management
|
|
9
9
|
mcp_server: chorus
|
|
10
10
|
---
|
|
@@ -35,6 +35,16 @@ Agent configuration lives in `~/.chorus/daemon.json`; `chorus agents` is the CRU
|
|
|
35
35
|
- `chorus agents remove <name|uuid>` — remove a configured agent from `~/.chorus/daemon.json`
|
|
36
36
|
(matched by UUID or name; an ambiguous name → use the UUID).
|
|
37
37
|
|
|
38
|
+
- `chorus agents run --name <name|uuid> [--type <type>] [--] [agent args…]` — launch a
|
|
39
|
+
configured agent's binary in the FOREGROUND with its Chorus connection injected into the
|
|
40
|
+
child only (`CHORUS_URL` / `CHORUS_API_KEY` / `CHORUS_AGENT_PROFILE`; nothing is exported to
|
|
41
|
+
the parent shell, and the key is never printed). Agent selection: single agent by default; else `--name`, else `CHORUS_AGENT_PROFILE`; ambiguous/none →
|
|
42
|
+
error. Backend defaults to the agent's stored `agentType`, overridable with `--type`.
|
|
43
|
+
Everything after `--` is passed to the agent **verbatim** (never inspected). Type → binary:
|
|
44
|
+
`claude-code`/`claude`→`claude`, `codex`→`codex`, `kiro`→`kiro-cli`, `pi`→`pi`,
|
|
45
|
+
`opencode`→`opencode`, `openclaw`→`openclaw`, `dsh`→`dsh-jsonrpc-agent`. Agents added as
|
|
46
|
+
opencode/openclaw/dsh are stored as `offline` → pass `--type` explicitly to launch them.
|
|
47
|
+
|
|
38
48
|
## 3. Connection environment variables
|
|
39
49
|
|
|
40
50
|
- `CHORUS_URL` — the Chorus instance URL.
|
package/skills/develop/SKILL.md
CHANGED
package/skills/docs/SKILL.md
CHANGED
package/skills/idea/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: Opt-in OpenSpec-mode authoring for Chorus PM workflows in Pi. Detec
|
|
|
4
4
|
license: AGPL-3.0
|
|
5
5
|
metadata:
|
|
6
6
|
author: chorus
|
|
7
|
-
version: "0.17.
|
|
7
|
+
version: "0.17.3"
|
|
8
8
|
category: project-management
|
|
9
9
|
mcp_server: chorus
|
|
10
10
|
---
|
|
@@ -385,7 +385,7 @@ The hook is read-only; you (the agent) perform the archive:
|
|
|
385
385
|
1-byte drift (a spec that mirrored correctly will read as 1 byte longer on the
|
|
386
386
|
server). Use `jq -j` (no trailing newline) or `cmp` directly, and never rely
|
|
387
387
|
on recursive `jq`, `head`, command substitution, or newline normalization.
|
|
388
|
-
(Unlike the
|
|
388
|
+
(Unlike the plugin-style Chorus surfaces, the `chorus-pi` package ships
|
|
389
389
|
no `verify-document-roundtrip.sh` — the `--arg-file` byte-equality guarantee
|
|
390
390
|
makes it unnecessary.)
|
|
391
391
|
|
package/skills/proposal/SKILL.md
CHANGED
package/skills/review/SKILL.md
CHANGED