@chorus-aidlc/chorus-openclaw-plugin 0.17.2 → 0.18.0
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 +3 -2
- package/bin/resolve-spec-mode.sh +107 -0
- package/dist/commands.d.ts +2 -1
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +49 -7
- package/dist/commands.js.map +1 -1
- package/dist/event-router.d.ts +16 -5
- package/dist/event-router.d.ts.map +1 -1
- package/dist/event-router.js +33 -6
- package/dist/event-router.js.map +1 -1
- package/dist/spec-mode.d.ts +81 -0
- package/dist/spec-mode.d.ts.map +1 -0
- package/dist/spec-mode.js +153 -0
- package/dist/spec-mode.js.map +1 -0
- package/package.json +2 -1
- package/skills/brainstorm/SKILL.md +1 -1
- package/skills/chorus/SKILL.md +9 -6
- package/skills/chorus-cli/SKILL.md +1 -1
- package/skills/code-reviewer/SKILL.md +2 -1
- package/skills/develop/SKILL.md +11 -9
- package/skills/docs/SKILL.md +1 -1
- package/skills/idea/SKILL.md +1 -1
- package/skills/openspec-aware/SKILL.md +46 -34
- package/skills/orchestrate/SKILL.md +9 -1
- package/skills/proposal/SKILL.md +13 -10
- package/skills/proposal-reviewer/SKILL.md +4 -2
- package/skills/quick-dev/SKILL.md +3 -3
- package/skills/review/SKILL.md +5 -5
- package/skills/spec-lite/SKILL.md +159 -0
- package/skills/task-reviewer/SKILL.md +5 -1
- package/skills/yolo/SKILL.md +37 -22
- package/src/commands.ts +58 -7
- package/src/event-router.ts +43 -6
- package/src/spec-mode.ts +208 -0
|
@@ -4,7 +4,7 @@ description: Adversarial read-only review of a submitted Chorus proposal — doc
|
|
|
4
4
|
license: AGPL-3.0
|
|
5
5
|
metadata:
|
|
6
6
|
author: chorus
|
|
7
|
-
version: "0.
|
|
7
|
+
version: "0.18.0"
|
|
8
8
|
category: project-management
|
|
9
9
|
mcp_server: chorus
|
|
10
10
|
---
|
|
@@ -19,7 +19,8 @@ You have been asked to **review a submitted Chorus proposal**. Your job is **not
|
|
|
19
19
|
|
|
20
20
|
## Hard rules (READ-ONLY)
|
|
21
21
|
|
|
22
|
-
- **You are READ-ONLY.** Do NOT edit, write, or create files. Do NOT
|
|
22
|
+
- **You are READ-ONLY.** Do NOT edit, write, or create files. Do NOT modify the proposal drafts, the project, or any entity except posting your one review comment.
|
|
23
|
+
- **Bash is READ-ONLY inspection only:** ls, cat, grep/rg, find, git ls-files/log/show/diff. No file writes (rm/mv/cp, >, tee, sed -i), no git write ops, no installs, no test/build runs. Use it to confirm a file or directory exists before flagging it as missing.
|
|
23
24
|
- **Keep your comment under 800 characters.** PASS items: names only. NOTE items: one-line description. BLOCKER items: evidence + expected/actual.
|
|
24
25
|
- **Classify every finding** as BLOCKER (blocks implementation) or NOTE (non-blocking). Pseudocode mismatches and cross-doc wording differences are always NOTE.
|
|
25
26
|
- **End with a single line beginning `VERDICT:`** followed by exactly one of `PASS`, `PASS WITH NOTES`, or `FAIL`. Has BLOCKERs → FAIL. Only NOTEs → PASS WITH NOTES. Nothing → PASS.
|
|
@@ -66,6 +67,7 @@ chorus_get_elaboration({ ideaUuid: "<idea-uuid>" })
|
|
|
66
67
|
- Do tasks cover ALL requirements from the documents?
|
|
67
68
|
- Are there scope additions not in the original idea?
|
|
68
69
|
- Are there contradictions between documents and tasks?
|
|
70
|
+
- **Intent alignment** — You already have the originating Idea (`inputUuids[0]`) + its elaboration; also read its human comments (`chorus_get_comments({ targetType: "idea", targetUuid })`, `author.type == "user"`). Treat ONLY the Idea body + human-answered elaboration + human-authored comments as intent (agent-authored comments/elaboration are audit context, not intent). Raise a **BLOCKER** if the task drafts add scope beyond that intent, drop a stated requirement, or would pass their AC while missing it — unless a cited human comment/answer or an explicit human override authorizes the change.
|
|
69
71
|
|
|
70
72
|
## Finding classification
|
|
71
73
|
|
|
@@ -4,7 +4,7 @@ description: Quick Task workflow — skip Idea→Proposal, create tasks directly
|
|
|
4
4
|
license: AGPL-3.0
|
|
5
5
|
metadata:
|
|
6
6
|
author: chorus
|
|
7
|
-
version: "0.
|
|
7
|
+
version: "0.18.0"
|
|
8
8
|
category: project-management
|
|
9
9
|
mcp_server: chorus
|
|
10
10
|
---
|
|
@@ -154,7 +154,7 @@ chorus_submit_for_verify({
|
|
|
154
154
|
})
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
-
Submitting is not final verification. Spawn the required task-reviewer skill through `sessions_spawn` as described in `/develop`, wait for it, and read
|
|
157
|
+
Submitting is not final verification. Spawn the required task-reviewer skill through `sessions_spawn` as described in `/develop`, wait for it, and read THIS round's `VERDICT:` Task comment — the one posted after your dispatch, not an older round's. `PASS` and `PASS WITH NOTES` continue. On `FAIL`, do not verify or hand off: fix every unresolved BLOCKER, repeat AC self-check and submission, then run a fresh independent task review.
|
|
158
158
|
|
|
159
159
|
### Step 8: Permission-Aware Verification
|
|
160
160
|
|
|
@@ -177,7 +177,7 @@ Quick Tasks support sub-agent execution just like proposal-based tasks. **Sessio
|
|
|
177
177
|
- **Main agent**: create quick tasks, work them yourself, or hand task UUIDs to sub-agents
|
|
178
178
|
- **Sub-agents**: create your own session (`chorus_create_session`), checkin/checkout per task, pass `sessionUuid` to `chorus_update_task` / `chorus_report_work`, and close the session on exit — see `/develop` for the full manual protocol
|
|
179
179
|
|
|
180
|
-
>
|
|
180
|
+
> There is no team object to create: to run several quick tasks in parallel, dispatch one sub-agent per task with `sessions_spawn`, issuing them in a single message. If `sessions_spawn` is unavailable or workers fail repeatedly, work the tasks sequentially as the main agent.
|
|
181
181
|
|
|
182
182
|
---
|
|
183
183
|
|
package/skills/review/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: Chorus Review workflow — approve/reject proposals, verify tasks,
|
|
|
4
4
|
license: AGPL-3.0
|
|
5
5
|
metadata:
|
|
6
6
|
author: chorus
|
|
7
|
-
version: "0.
|
|
7
|
+
version: "0.18.0"
|
|
8
8
|
category: project-management
|
|
9
9
|
mcp_server: chorus
|
|
10
10
|
---
|
|
@@ -67,12 +67,12 @@ Key responsibilities:
|
|
|
67
67
|
When reviewing proposals, tasks, or an Idea's final aggregate code change, get an independent VERDICT before approving/verifying/shipping. On OpenClaw there is **no PostToolUse hook** to remind you — invoke the review yourself, inline.
|
|
68
68
|
|
|
69
69
|
1. **Preferred — spawn a reviewer sub-agent.** Use the OpenClaw `sessions_spawn` tool to spawn a sub-agent whose `task` tells it to **invoke the `/proposal-reviewer` skill** (for proposals), the `/task-reviewer` skill (for tasks), or the `/code-reviewer` skill (the final ship-time gateway over an Idea's aggregate code change, after its last task is verified — pass the `ideaUuid`; it posts its VERDICT on the **idea**) — all bundled with this plugin — against the entity. Wait for it (poll the `subagents` tool or use `sessions_yield` — do NOT detach; you must have the VERDICT before proceeding). The sub-agent inherits the plugin skills, so the reviewer skill is available to it; it posts a VERDICT comment with detailed findings. Example task prompt: `Run the /proposal-reviewer skill to review proposalUuid <uuid>; post your VERDICT comment when done.`
|
|
70
|
-
2. **Read the VERDICT.** After the reviewer completes, call `chorus_get_comments` and find
|
|
70
|
+
2. **Read the VERDICT.** After the reviewer completes, call `chorus_get_comments` and find THIS round's `VERDICT:` comment — the one posted after your dispatch, not an older round's. There are exactly three possible outcomes:
|
|
71
71
|
- **VERDICT: PASS** — No issues found. Approve (proposals) or mark AC passed and verify (tasks).
|
|
72
72
|
- **VERDICT: PASS WITH NOTES** — Minor non-blocking notes. Still approve/verify. Notes are informational.
|
|
73
73
|
- **VERDICT: FAIL** — BLOCKERs found. Reject (proposals) or reopen (tasks). For a **code-review gateway** FAIL, do not reopen the verified tasks — instead fix via the **quick-dev** workflow (`/quick-dev`): `chorus_create_tasks` with `proposalUuid` set to the current approved proposal so the fix tasks attach to it. Group related small BLOCKERs into one cohesive task by default; split only materially large or independently testable fixes. Each fix task must self-check its acceptance criteria and pass independent task review plus admin verification. Re-run the gateway only after every fix task is successfully `done`; if there is a failed or cancelled fix task, stop and escalate instead. Fix the specific BLOCKERs listed in the comment before resubmitting.
|
|
74
|
-
3. **No new VERDICT comment?**
|
|
75
|
-
4. **Track rounds.** Count existing VERDICT comments before spawning. After 3 rounds of FAIL on the same item, stop the loop and escalate to human review.
|
|
74
|
+
3. **No new VERDICT comment?** Check what the reviewer *did* post. A comment reporting that the round limit was reached, or any other explicit refusal to review, is a deliberate escalation to a human: STOP — do not respawn, do not self-review, do not post a VERDICT of your own. If it posted nothing at all, respawn it ONCE, telling it to stay within its turn budget and reserve its last turns for the VERDICT, then apply this same check again to what the retry posts. An explicit refusal from the retry still means STOP; only a second true silence lets you review the item yourself as a read-only pass using the checklists below and POST the VERDICT — **absence is never a PASS**.
|
|
75
|
+
4. **Track rounds.** Count existing VERDICT comments before spawning. After 3 rounds of FAIL on the same item, stop the loop and escalate to human review: post a comment saying the round limit was reached and a human decision is needed, and post no VERDICT. Nobody — including you on a later turn — may replace that escalation with a self-reviewed VERDICT.
|
|
76
76
|
5. **Fallback — review it yourself (no `sessions_spawn` on the host).** If spawning is unavailable (disabled by policy, or the spawn fails), perform the review yourself as a **focused, read-only pass** using the quality checklists in the workflows below: read the entity, its comments, and the relevant documents/code, run read-only test/build commands where applicable, and do NOT modify anything. Then record your VERDICT via `chorus_add_comment` ending with a `VERDICT:` line (PASS / PASS WITH NOTES / FAIL), classifying every finding as BLOCKER or NOTE. The `/proposal-reviewer`, `/task-reviewer`, and `/code-reviewer` skills are the authoritative checklists for this manual pass — read the relevant one and follow its procedure.
|
|
77
77
|
|
|
78
78
|
---
|
|
@@ -332,7 +332,7 @@ chorus_pm_update_document({ documentUuid: "<doc-uuid>", content: "Updated..." })
|
|
|
332
332
|
- **Unblock the team** — Prioritize proposal reviews to keep PM and Developer work flowing
|
|
333
333
|
- **Use delete sparingly** — Prefer closing over deleting; closing preserves history
|
|
334
334
|
- **Document decisions** — Use comments to explain approval/rejection reasoning
|
|
335
|
-
- **Verify between waves** — In
|
|
335
|
+
- **Verify between waves** — In wave-based execution, verify tasks to `done` between waves to unblock downstream dependencies
|
|
336
336
|
|
|
337
337
|
---
|
|
338
338
|
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spec-lite
|
|
3
|
+
description: Lightweight, Chorus-native local specs for Chorus PM workflows on OpenClaw — a durable local spec `.chorus/specs/<slug>/spec.md` (one per capability/feature) edited in place and NEVER synced (git history is its record), plus one dated folder per change effort `.chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/` holding Chorus-typed docs (prd.md, tech_design.md, …) that ARE mirrored 1:1 into persistent Chorus Documents via `--arg-file`. The fallback when OpenSpec isn't in use; a low-token alternative to the heavier openspec-aware path. Read from proposal / develop / yolo when the spec mode resolves to `lite`.
|
|
4
|
+
license: AGPL-3.0
|
|
5
|
+
metadata:
|
|
6
|
+
author: chorus
|
|
7
|
+
version: "0.18.0"
|
|
8
|
+
category: project-management
|
|
9
|
+
mcp_server: chorus
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# spec-lite — durable local spec + per-change synced docs (OpenClaw plugin)
|
|
13
|
+
|
|
14
|
+
A **shared sub-procedure** for the Chorus stage skills (proposal, develop, yolo) — the lightweight
|
|
15
|
+
spec mode, modelled on **superpowers** (a durable spec that lives on, plus per-effort artifacts):
|
|
16
|
+
one **durable local spec** per capability (`<slug>/spec.md`, edited in place, **never synced** — git
|
|
17
|
+
history is its 留痕), plus one **dated folder per change effort** (`<slug>/<YYYY-MM-DD>-<change-slug>/`
|
|
18
|
+
of Chorus-typed docs — `prd.md`, … — that **are** mirrored 1:1 into persistent Chorus Documents).
|
|
19
|
+
No new CLI, MCP tool, backend, or schema — mirroring reuses the existing document tools.
|
|
20
|
+
|
|
21
|
+
> **Tool namespace:** Chorus MCP tools are exposed under a `chorus__` prefix on OpenClaw (e.g.
|
|
22
|
+
> `chorus__chorus_pm_add_document_draft`). Bare names are used in prose for readability — prepend
|
|
23
|
+
> `chorus__` when invoking the MCP tools directly. **Document-mirror calls do NOT go through the MCP
|
|
24
|
+
> harness** — they use the `chorus` CLI (`chorus mcp call`, preferred) or the `chorus-api.sh` wrapper
|
|
25
|
+
> (fallback), independent of the `chorus__` namespacing (see Mirror below).
|
|
26
|
+
|
|
27
|
+
## Mode (how you got here)
|
|
28
|
+
|
|
29
|
+
**OpenClaw has no SessionStart hook and no injected `## Spec Mode` value** — unlike the Claude Code /
|
|
30
|
+
Codex / Pi ports, nothing precomputes the mode into your context. The mode is **resolved by sourcing
|
|
31
|
+
the resolver the plugin ships** (`bin/resolve-spec-mode.sh`, byte-identical to the Claude Code copy),
|
|
32
|
+
the moment a stage skill reaches its spec-mode step — see `openspec-aware` §1 for the locate-and-source
|
|
33
|
+
block. Never re-derive the rule from prose. You can also print the resolved mode any time with **`/chorus spec`** (or the
|
|
34
|
+
`## /chorus status` block). You are here because that resolution returned **`lite`**; if it returned
|
|
35
|
+
anything else, this skill is a no-op — return to the caller. (For the record, the rule: an explicit
|
|
36
|
+
`CHORUS_SPEC_MODE` wins, else OpenSpec when usable, else lite.)
|
|
37
|
+
|
|
38
|
+
## The durable local spec — `<slug>/spec.md`
|
|
39
|
+
|
|
40
|
+
`.chorus/specs/<slug>/spec.md` — `<slug>` (kebab-case) names a **capability/feature, not one change**.
|
|
41
|
+
This is the single, cumulative, human-readable "current truth" of the capability: **edited in place**
|
|
42
|
+
by every change, **never mirrored to Chorus, carries no Chorus ids**. Minimal frontmatter only
|
|
43
|
+
(`slug`, `title`, `status: draft|active|done`, `created`), then plain prose — `## Intent`,
|
|
44
|
+
`## Requirements` (prose + `- [ ]` acceptance points, no `SHALL`/scenario grammar), `## Non-goals`.
|
|
45
|
+
Start from the inline **durable `spec.md` template** below.
|
|
46
|
+
Its git history is the whole record — no changelog section, no Chorus round-trip. **This file NEVER
|
|
47
|
+
enters the mirror loop.**
|
|
48
|
+
|
|
49
|
+
`status` describes the **capability**, not a single change: `active` while any change is in flight,
|
|
50
|
+
`done` when the current change delivers and none is open. A **new** change against a `done` capability
|
|
51
|
+
reopens it to `active`, back to `done` on delivery.
|
|
52
|
+
|
|
53
|
+
### Template — the durable `spec.md`
|
|
54
|
+
|
|
55
|
+
```markdown
|
|
56
|
+
---
|
|
57
|
+
slug: <kebab-case-capability>
|
|
58
|
+
title: <Capability title>
|
|
59
|
+
status: draft # draft | active | done
|
|
60
|
+
created: <YYYY-MM-DD>
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Intent
|
|
64
|
+
<what this capability is for, in prose>
|
|
65
|
+
|
|
66
|
+
## Requirements
|
|
67
|
+
<prose, no SHALL/scenario grammar>
|
|
68
|
+
- [ ] <acceptance point>
|
|
69
|
+
|
|
70
|
+
## Non-goals
|
|
71
|
+
- <explicitly out of scope>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Per-change dated folders — `<slug>/<YYYY-MM-DD>-<change-slug>/`
|
|
75
|
+
|
|
76
|
+
Each change effort is **one dated folder directly under `<slug>/`** (no `changes/` wrapper), e.g.
|
|
77
|
+
`.chorus/specs/<slug>/2026-09-08-add-export/`. Date + slug so same-day changes don't collide and
|
|
78
|
+
folders sort by date. It holds the **Chorus-typed** docs for THAT change — one file per Document type:
|
|
79
|
+
|
|
80
|
+
| File | `Document.type` | Required? |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `prd.md` | `prd` | **yes** — the primary per-change doc |
|
|
83
|
+
| `tech_design.md` | `tech_design` | optional — the "how" |
|
|
84
|
+
| `adr.md` / `guide.md` / `spec.md` | `adr` / `guide` / `spec` | optional |
|
|
85
|
+
|
|
86
|
+
These files **ARE synced** — each maps to **one persistent Chorus Document** of its type. Their
|
|
87
|
+
frontmatter carries the sync ids `proposalUuid` and `documentUuid` (the type is implied by the
|
|
88
|
+
filename). Start from the inline **dated-folder document template** below. A different change to the same
|
|
89
|
+
capability is a different dated folder. The **current change's** folder is edited and re-mirrored
|
|
90
|
+
throughout its effort (until delivery); only **previously-delivered** dated folders are left frozen —
|
|
91
|
+
you don't reach back and rewrite a past change.
|
|
92
|
+
|
|
93
|
+
> **Two files named `spec.md`, different roles.** The durable `<slug>/spec.md` (local only, no ids) is
|
|
94
|
+
> NOT the same as a per-change `spec`-type doc, which would live at `<slug>/<date>-<slug>/spec.md`
|
|
95
|
+
> (synced, carries ids). Prefer `prd.md` as the per-change primary doc to avoid the confusion.
|
|
96
|
+
|
|
97
|
+
### Template — a dated-folder document
|
|
98
|
+
|
|
99
|
+
The document **type is implied by the filename** (`prd.md` → `prd`, `tech_design.md` → `tech_design`, …),
|
|
100
|
+
**NOT** a frontmatter key.
|
|
101
|
+
|
|
102
|
+
```markdown
|
|
103
|
+
---
|
|
104
|
+
title: <Document title as it appears in Chorus>
|
|
105
|
+
proposalUuid: <uuid> # written on first mirror
|
|
106
|
+
documentUuid: # empty until the draft materializes on approval
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
# <Document title>
|
|
110
|
+
<body — this file's bytes are the source of truth for the Chorus Document>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Flow (one change)
|
|
114
|
+
|
|
115
|
+
1. Confirm mode = `lite` (resolved by the shipped resolver, `openspec-aware` §1; else no-op).
|
|
116
|
+
2. Create the dated folder `<slug>/<YYYY-MM-DD>-<change-slug>/` and write its **synced** change docs —
|
|
117
|
+
`prd.md` (required), `tech_design.md` etc. only if warranted (use the **dated-folder document template** above).
|
|
118
|
+
3. **Update `<slug>/spec.md` in place** to the new cumulative truth (Requirements, acceptance points,
|
|
119
|
+
`status`) — local only, no sync.
|
|
120
|
+
4. Create the proposal container with one literal locator line in `description` (own line, no trailing
|
|
121
|
+
punctuation) so develop finds the change:
|
|
122
|
+
`Spec-lite: .chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/`
|
|
123
|
+
5. **Mirror the dated folder's docs** to Chorus (below). Add tasks via `chorus_pm_add_task_draft` —
|
|
124
|
+
**no `tasks.md`**, no CLI / validate / archive, no delta grammar. **Tasks live in Chorus.**
|
|
125
|
+
6. Develop → keep editing `spec.md` + the change docs, re-mirroring the change docs as work lands and
|
|
126
|
+
ticking acceptance points. On delivery set the durable `spec.md` `status: done`.
|
|
127
|
+
|
|
128
|
+
## Mirror — only the dated-folder docs (never `spec.md`)
|
|
129
|
+
|
|
130
|
+
Every dated-folder `<type>.md` maps to **one persistent Chorus Document** of that `type`, tracked by
|
|
131
|
+
`documentUuid` in the file's frontmatter. Fill `content` from the file's bytes with `--arg-file` —
|
|
132
|
+
never re-type the body (drifts, burns ~20k tokens). One call per file; resolve identity by
|
|
133
|
+
`documentUuid` / `(proposalUuid, type)`, **never by `title` alone** (a lookup finding zero or >1 MUST
|
|
134
|
+
**halt**). Guard every call with the `chorus_check_response` halt-on-error helper (`openspec-aware`
|
|
135
|
+
§6). No `chorus` on `PATH`? Fall back to the `chorus-api.sh` wrapper + `json_encode_file`
|
|
136
|
+
(`openspec-aware` §3.6; the OpenClaw wrapper is invoked as `chorus-api.sh mcp-tool <tool> "$PAYLOAD"`).
|
|
137
|
+
**`<slug>/spec.md` is NEVER in this loop.**
|
|
138
|
+
|
|
139
|
+
- **First time a doc is authored** (its dated folder is new): write `proposalUuid` into frontmatter,
|
|
140
|
+
mirror into a proposal **draft** —
|
|
141
|
+
`chorus mcp call chorus_pm_add_document_draft "{\"proposalUuid\":\"$P\",\"type\":\"prd\",\"title\":\"PRD: $TITLE\"}" --arg-file content=".chorus/specs/$SLUG/$DATED/prd.md"`.
|
|
142
|
+
Edit the draft via `chorus_pm_update_document_draft` (returned `draftUuid`) before approval. On
|
|
143
|
+
approval it materializes into a persistent Document — resolve by `(proposalUuid, type)` via
|
|
144
|
+
`chorus_get_documents`, record `documentUuid` in frontmatter, re-mirror once so local == Chorus.
|
|
145
|
+
- **Later edits** (a doc that already has a `documentUuid`): edit the file, then
|
|
146
|
+
`chorus mcp call chorus_pm_update_document "{\"documentUuid\":\"$D\"}" --arg-file content=".chorus/specs/$SLUG/$DATED/<type>.md"`.
|
|
147
|
+
Each update **auto-increments the Document version** — that version history is the change doc's
|
|
148
|
+
record in Chorus, alongside git.
|
|
149
|
+
|
|
150
|
+
## 留痕: git history + Document versions
|
|
151
|
+
|
|
152
|
+
`git log -- .chorus/specs/$SLUG/` is the audit trail — the durable `spec.md`'s in-place diffs plus each
|
|
153
|
+
dated folder's change docs; the mirrored Documents' auto-incremented versions are the parallel record
|
|
154
|
+
in Chorus. No changelog section to maintain. Only `.chorus/specs/` is version-controlled (`.chorus/*` +
|
|
155
|
+
`!.chorus/specs/`).
|
|
156
|
+
|
|
157
|
+
**Single-writer:** the folder is shared — in a multi-task wave only the **orchestrator / main agent**
|
|
158
|
+
edits + re-mirrors; parallel workers report via `chorus_report_work` only, re-reading before any write.
|
|
159
|
+
**Task state lives in Chorus**, not the docs — the `- [ ]` points are acceptance intent, not a tracker.
|
|
@@ -4,7 +4,7 @@ description: Adversarial verification of a submitted Chorus task against its AC
|
|
|
4
4
|
license: AGPL-3.0
|
|
5
5
|
metadata:
|
|
6
6
|
author: chorus
|
|
7
|
-
version: "0.
|
|
7
|
+
version: "0.18.0"
|
|
8
8
|
category: project-management
|
|
9
9
|
mcp_server: chorus
|
|
10
10
|
---
|
|
@@ -58,6 +58,10 @@ chorus_get_document({ documentUuid: "<doc-uuid>" })
|
|
|
58
58
|
|
|
59
59
|
**Hallucination check:** Flag anything that looks LLM-fabricated as NOTE — API signatures, CLI flags, config keys, model IDs, endpoint URLs, package names, or any external detail the developer likely wrote from memory.
|
|
60
60
|
|
|
61
|
+
**Step 7: Intent alignment**
|
|
62
|
+
|
|
63
|
+
Resolve the originating Idea (this task's proposal → `inputUuids[0]`) and read its body + human-answered elaboration + human-authored comments (`answeredBy.type` / `author.type == "user"`; agent-authored entries are audit context, not intent). Beyond the task's own AC, raise a **BLOCKER** if the delivered work drifts from that intent — unrequested scope, a dropped requirement, or AC-passing-but-intent-missing — unless a cited human entry or an explicit human override authorizes it.
|
|
64
|
+
|
|
61
65
|
## Finding classification
|
|
62
66
|
|
|
63
67
|
**BLOCKER** — blocks correctness: AC not actually implemented; build or test failures; implementation diverges from proposal documents (semantic contradiction); edge cases causing runtime errors; missing error handling for required scenarios.
|
package/skills/yolo/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: Full-auto AI-DLC pipeline — from prompt to done. Automates the en
|
|
|
4
4
|
license: AGPL-3.0
|
|
5
5
|
metadata:
|
|
6
6
|
author: chorus
|
|
7
|
-
version: "0.
|
|
7
|
+
version: "0.18.0"
|
|
8
8
|
category: project-management
|
|
9
9
|
mcp_server: chorus
|
|
10
10
|
---
|
|
@@ -15,7 +15,7 @@ Full-auto AI-DLC pipeline. User provides a prompt; agent drives the entire lifec
|
|
|
15
15
|
|
|
16
16
|
> **Tool namespace:** Chorus tools are exposed by the connected MCP server under a `chorus__` prefix on OpenClaw (e.g. `chorus__chorus_pm_create_proposal`). Bare names are used below for readability — prepend `chorus__` when invoking. See `/chorus` for the full rule.
|
|
17
17
|
|
|
18
|
-
> **OpenClaw adaptations summarized (details inline below):** (1) elaboration is **self-answered as plain text** — no `AskUserQuestion`, no user interaction; (2) reviewers run **inline** after each submit — spawn a sub-agent with the OpenClaw `sessions_spawn` tool and tell it to run the `/proposal-reviewer` or `/task-reviewer` skill, with a read-only self-review fallback when `sessions_spawn` is unavailable; (3) sessions are **manual** if you dispatch sub-agents (no SubagentStart hook); (4) task execution
|
|
18
|
+
> **OpenClaw adaptations summarized (details inline below):** (1) elaboration is **self-answered as plain text** — no `AskUserQuestion`, no user interaction; (2) reviewers run **inline** after each submit — spawn a sub-agent with the OpenClaw `sessions_spawn` tool and tell it to run the `/proposal-reviewer` or `/task-reviewer` skill, with a read-only self-review fallback when `sessions_spawn` is unavailable; (3) sessions are **manual** if you dispatch sub-agents (no SubagentStart hook); (4) task execution dispatches **one sub-agent per unblocked task via `sessions_spawn`** (whole wave in one message), falling back to **sequential main-agent waves** when `sessions_spawn` is unavailable or workers fail repeatedly — there is no team object to create first.
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
@@ -25,7 +25,7 @@ Full-auto AI-DLC pipeline. User provides a prompt; agent drives the entire lifec
|
|
|
25
25
|
|
|
26
26
|
1. **Planning** -- create project, idea, self-elaboration, proposal with docs & tasks
|
|
27
27
|
2. **Proposal Review** -- proposal-reviewer adversarial loop
|
|
28
|
-
3. **Execution** --
|
|
28
|
+
3. **Execution** -- dependency-ordered waves: one worker sub-agent per unblocked task, or sequential main-agent execution as fallback
|
|
29
29
|
4. **Verification** -- task-reviewer adversarial loop + admin verify
|
|
30
30
|
5. **Report** -- completion summary
|
|
31
31
|
|
|
@@ -42,7 +42,8 @@ Full-auto AI-DLC pipeline. User provides a prompt; agent drives the entire lifec
|
|
|
42
42
|
Admin Approve --> Tasks materialize
|
|
43
43
|
|
|
|
44
44
|
v
|
|
45
|
-
|
|
45
|
+
Wave execution (loop chorus_get_unblocked_tasks; one sessions_spawn worker
|
|
46
|
+
per task, or sequential main-agent fallback)
|
|
46
47
|
| (implement task + task-reviewer per task)
|
|
47
48
|
v
|
|
48
49
|
Admin Verify each task --> unblock next
|
|
@@ -188,21 +189,19 @@ In /yolo mode, the agent generates elaboration questions and answers them itself
|
|
|
188
189
|
|
|
189
190
|
#### Step 1.4: Create Proposal
|
|
190
191
|
|
|
191
|
-
1. **
|
|
192
|
+
1. **Resolve the spec mode (inline).** OpenClaw has no SessionStart hook to precompute it — so you resolve the **whole** contract yourself, not just "is OpenSpec active". Load the `openspec-aware` skill and run its **§1 resolution block (which sources the plugin's shipped `bin/resolve-spec-mode.sh`)**, which yields one of `lite` / `openspec` / `off` (the canonical resolver, byte-identical to the Claude Code copy; `/chorus spec` prints the same result from the TS mirror `src/spec-mode.ts`). Rule: an explicit `CHORUS_SPEC_MODE` wins; when unset, **OpenSpec is the default whenever usable** (`CHORUS_OPENSPEC_MODE` ≠ `off`, an `openspec/` directory at the project root, and the `openspec` CLI on `PATH`), else **spec-lite**. Route: `openspec` (usable) → **2a**; `off` → **2b**; `lite` → **2c**. If the resolution says the mode **cannot be honored** (explicit `CHORUS_SPEC_MODE=openspec` but OpenSpec unusable), **halt** and surface it — do NOT fall back or enter 2a with no OpenSpec.
|
|
192
193
|
|
|
193
|
-
> **OpenClaw note:**
|
|
194
|
+
> **OpenClaw note:** this is mandatory — yolo runs unattended, so silently picking the wrong mode is exactly the failure scenario the resolution contract exists to prevent.
|
|
194
195
|
|
|
195
|
-
|
|
196
|
-
- Any check fails (or `CHORUS_OPENSPEC_MODE=off`) → free-form branch (sub-step 2b below).
|
|
197
|
-
|
|
198
|
-
2. **Create the empty proposal container.** In OpenSpec mode, the `description` MUST contain the literal line `OpenSpec change slug: <slug>` (use the `$SLUG` you'll pick in 2a); in free-form mode, omit that line.
|
|
196
|
+
2. **Create the empty proposal container.** The `description` MUST carry the mode's locator line — OpenSpec: `OpenSpec change slug: <slug>`; spec-lite: `Spec-lite: .chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/`; free-form: none. `description` is only settable at creation, so decide the slug/dated-path first.
|
|
199
197
|
|
|
200
198
|
```
|
|
201
199
|
chorus_pm_create_proposal({
|
|
202
200
|
projectUuid: "<project-uuid>",
|
|
203
201
|
title: "<feature name>",
|
|
204
|
-
description: "<summary>\n\nOpenSpec change slug: <slug>",
|
|
205
|
-
// description: "<summary
|
|
202
|
+
description: "<summary>\n\nOpenSpec change slug: <slug>", // OpenSpec (2a)
|
|
203
|
+
// description: "<summary>\n\nSpec-lite: .chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/", // spec-lite (2c)
|
|
204
|
+
// description: "<summary>", // free-form (2b)
|
|
206
205
|
inputType: "idea",
|
|
207
206
|
inputUuids: ["<idea-uuid>"]
|
|
208
207
|
})
|
|
@@ -210,7 +209,7 @@ In /yolo mode, the agent generates elaboration questions and answers them itself
|
|
|
210
209
|
|
|
211
210
|
Then branch:
|
|
212
211
|
|
|
213
|
-
**2a. OpenSpec mode (
|
|
212
|
+
**2a. OpenSpec mode (resolved mode = openspec, usable).** Follow `openspec-aware` §3 end-to-end:
|
|
214
213
|
- Pick `$SLUG`, run `openspec new change "$SLUG"` (§3.1–§3.2).
|
|
215
214
|
- Author `proposal.md`, `design.md`, and one `specs/<capability>/spec.md` per capability locally on disk (§3.3). ADDED Requirements only; per-spec fallback to free-form Markdown if MODIFIED/REMOVED is needed.
|
|
216
215
|
- Define the `chorus_check_response` helper (§6); prefer `chorus mcp call … --arg-file content=<file>` for mirrors (§3.4/§3.6) — the bash-wrapper fallback's `json_encode_file` is only needed when `chorus` is not on `PATH`.
|
|
@@ -220,7 +219,7 @@ In /yolo mode, the agent generates elaboration questions and answers them itself
|
|
|
220
219
|
|
|
221
220
|
Then continue to step 3 (task drafts).
|
|
222
221
|
|
|
223
|
-
**2b. Free-form mode (
|
|
222
|
+
**2b. Free-form mode (resolved mode = free-form).** Only when step 1 resolved to free-form — i.e. explicit `CHORUS_SPEC_MODE=off` (unset never comes here: it resolves to OpenSpec when usable, else spec-lite/2c). Add a tech design document draft directly via MCP, content authored inline:
|
|
224
223
|
|
|
225
224
|
```
|
|
226
225
|
chorus_pm_add_document_draft({
|
|
@@ -231,6 +230,8 @@ In /yolo mode, the agent generates elaboration questions and answers them itself
|
|
|
231
230
|
})
|
|
232
231
|
```
|
|
233
232
|
|
|
233
|
+
**2c. spec-lite mode (resolved mode = lite).** Load the `spec-lite` skill. Pick `$SLUG` (a **capability**). Ensure the durable `.chorus/specs/<slug>/spec.md` exists (local-only, no ids; use the `spec-lite` skill's inline durable-spec template) and update it in place. Create this change's **dated folder** `.chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/` with its **synced** Chorus-typed docs (shape = the `spec-lite` skill's inline dated-folder document template) — `prd.md` (primary), optional `tech_design.md`… The `description` carries the `Spec-lite: .chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/` locator (step 2). Mirror **each** dated-folder `<type>.md` to its persistent Document byte-exact — first time `chorus mcp call chorus_pm_add_document_draft "{\"proposalUuid\":\"<uuid>\",\"type\":\"prd\",\"title\":\"PRD: <feature>\"}" --arg-file content=.chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/prd.md`, later edits via `chorus_pm_update_document` against the recorded `documentUuid` (`chorus-api.sh mcp-tool …` fallback when `chorus` not on `PATH`). **`spec.md` is never mirrored.** No `openspec/changes/` scaffold; no `tasks.md`. Then continue to step 3.
|
|
234
|
+
|
|
234
235
|
3. **Add task drafts incrementally** (use returned `draftUuid` for dependency chaining). `acceptanceCriteriaItems` is **required** on every draft — at least one non-blank criterion, or the call is rejected:
|
|
235
236
|
```
|
|
236
237
|
# First task
|
|
@@ -272,6 +273,20 @@ In /yolo mode, the agent generates elaboration questions and answers them itself
|
|
|
272
273
|
|
|
273
274
|
---
|
|
274
275
|
|
|
276
|
+
### Reviewer contract (applies to every review gate below)
|
|
277
|
+
|
|
278
|
+
Every gate in Phases 2, 4 and 4.5 follows the same three steps. They are written once here; the phases below only name their entity and their stage-specific actions.
|
|
279
|
+
|
|
280
|
+
1. **Spawn and wait.** Spawn the reviewer as a read-only sub-agent, then wait for it: spawn it with `sessions_spawn` and wait by polling the `subagents` tool or via `sessions_yield` — do **not** detach. The verdict is the `VERDICT:` comment the reviewer posts, not the spawn result.
|
|
281
|
+
2. **Read THIS round's VERDICT.** Call `chorus_get_comments` on the entity and find the `VERDICT:` comment posted **after your dispatch**, not an older round's. Do not advance the gate before you have read it.
|
|
282
|
+
3. **No VERDICT for this round?** Check what the reviewer *did* post:
|
|
283
|
+
- **A reported round limit, or any other explicit refusal to review** — a deliberate escalation to a human. STOP: do not respawn, do not self-review, do not post a VERDICT of your own.
|
|
284
|
+
- **Nothing at all** — respawn ONCE, telling it to stay within its turn budget and reserve its last turns for the VERDICT, then apply this same check again to what the retry posts. An explicit refusal from the retry still means STOP; only a second true silence lets you review the entity yourself as a read-only pass and POST the VERDICT, then proceed on what you posted rather than looping forever.
|
|
285
|
+
|
|
286
|
+
**Absence is never a PASS**, and a round limit reached by someone else is never yours to clear.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
275
290
|
### Phase 2: Proposal Review Loop
|
|
276
291
|
|
|
277
292
|
> **OpenClaw difference:** there is no PostToolUse hook injecting a "spawn the reviewer" reminder. Run the reviewer **inline**, right after `chorus_pm_submit_proposal`.
|
|
@@ -288,7 +303,7 @@ Then:
|
|
|
288
303
|
```
|
|
289
304
|
chorus_get_comments({ targetType: "proposal", targetUuid: "<proposal-uuid>" })
|
|
290
305
|
```
|
|
291
|
-
Look for
|
|
306
|
+
Look for THIS round's `VERDICT:` comment — the one posted after your dispatch, not an older round's.
|
|
292
307
|
|
|
293
308
|
2. **Act on the VERDICT:**
|
|
294
309
|
|
|
@@ -322,15 +337,15 @@ Then:
|
|
|
322
337
|
Proposal UUID: <uuid>"
|
|
323
338
|
```
|
|
324
339
|
|
|
325
|
-
4. **No new VERDICT
|
|
340
|
+
4. **No new VERDICT for this round?** Apply step 3 of the **Reviewer contract**, reviewing the proposal yourself if the reviewer stays silent.
|
|
326
341
|
|
|
327
342
|
---
|
|
328
343
|
|
|
329
|
-
### Phase 3: Task Execution (
|
|
344
|
+
### Phase 3: Task Execution (Waves)
|
|
330
345
|
|
|
331
346
|
After proposal approval, tasks exist in `open` status. Execute them in dependency-ordered waves.
|
|
332
347
|
|
|
333
|
-
> **OpenClaw difference:**
|
|
348
|
+
> **OpenClaw difference:** there is no team or group object to create — parallelism comes from dispatching **one sub-agent per unblocked task** with OpenClaw's own `sessions_spawn` tool, issuing the whole wave in a single message. If your host does not expose `sessions_spawn`, or spawned workers fail repeatedly, run waves **sequentially as the main agent**: loop `chorus_get_unblocked_tasks`, implement each ready task yourself, verify it, then loop again for the next wave. The sequential loop below is written for that fallback and is always safe; see `/develop` §"Optional: sub-agent dispatch" for the parallel form (workers need the manual session instructions, since there is no SubagentStart hook).
|
|
334
349
|
|
|
335
350
|
```
|
|
336
351
|
wave = 1
|
|
@@ -363,7 +378,7 @@ loop:
|
|
|
363
378
|
wave += 1
|
|
364
379
|
```
|
|
365
380
|
|
|
366
|
-
> **
|
|
381
|
+
> **Parallel form:** to run a wave in parallel instead of serially, dispatch one worker sub-agent per unblocked task with `sessions_spawn`, issuing the whole wave in a single message, then wait for the wave before verifying. Because there is no SubagentStart hook, each worker prompt **must** include the manual session instructions explicitly — see `/develop` "Optional: sub-agent dispatch". The main agent still owns review + verification, and the wave-by-wave dependency structure above is unchanged.
|
|
367
382
|
|
|
368
383
|
---
|
|
369
384
|
|
|
@@ -393,7 +408,7 @@ for the just-submitted task:
|
|
|
393
408
|
|
|
394
409
|
# 3. Read task-reviewer VERDICT
|
|
395
410
|
comments = chorus_get_comments({ targetType: "task", targetUuid: "<task-uuid>" })
|
|
396
|
-
# Find
|
|
411
|
+
# Find THIS round's "VERDICT:" comment — the one posted after your dispatch, not an older round's
|
|
397
412
|
|
|
398
413
|
# 4. Act on VERDICT — three possible outcomes:
|
|
399
414
|
if VERDICT is "PASS":
|
|
@@ -429,7 +444,7 @@ ESCALATE: "Task '{title}' failed review after {maxRounds} rounds.
|
|
|
429
444
|
|
|
430
445
|
Continue with remaining tasks -- do not halt the entire pipeline for one stuck task.
|
|
431
446
|
|
|
432
|
-
**No new VERDICT
|
|
447
|
+
**No new VERDICT for this round?** Apply step 3 of the **Reviewer contract**, reviewing the task yourself if the reviewer stays silent.
|
|
433
448
|
|
|
434
449
|
---
|
|
435
450
|
|
|
@@ -450,7 +465,7 @@ ESCALATE: "Idea '{title}' failed code review after {maxCodeReviewRounds} rounds.
|
|
|
450
465
|
Last BLOCKERs: <list>. Manual intervention needed. Idea UUID: <uuid>"
|
|
451
466
|
```
|
|
452
467
|
|
|
453
|
-
**No VERDICT
|
|
468
|
+
**No new VERDICT for this round?** Apply step 3 of the **Reviewer contract**, reviewing the idea's aggregate change yourself if the reviewer stays silent.
|
|
454
469
|
|
|
455
470
|
> The gateway is **behavioral** like the other two reviewers: its verdict is advisory and does not change the Idea's stored status; the orchestrator honors it. It runs **before** the completion report so the report is never written while a FAIL is outstanding.
|
|
456
471
|
|
package/src/commands.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ChorusMcpClient } from "./mcp-client.js";
|
|
2
|
+
import { resolveSpecModeFromEnv, type SpecModeResult } from "./spec-mode.js";
|
|
2
3
|
|
|
3
4
|
// ===== Response types from Chorus MCP tools =====
|
|
4
5
|
//
|
|
@@ -62,7 +63,7 @@ interface AssignmentsResponse {
|
|
|
62
63
|
|
|
63
64
|
// ===== Skill catalog =====
|
|
64
65
|
//
|
|
65
|
-
// All
|
|
66
|
+
// All 11 skills bundled with the Chorus OpenClaw plugin
|
|
66
67
|
// (packages/openclaw-plugin/skills/*/SKILL.md). The `name` here matches each
|
|
67
68
|
// skill's SKILL.md frontmatter `name`, which is exactly the slash command
|
|
68
69
|
// OpenClaw exposes (see invocation hint below).
|
|
@@ -76,7 +77,8 @@ const PLUGIN_SKILLS = [
|
|
|
76
77
|
{ name: "quick-dev", description: "Skip Idea→Proposal — create tasks directly, execute, verify" },
|
|
77
78
|
{ name: "review", description: "Approve/reject proposals, verify tasks, project governance" },
|
|
78
79
|
{ name: "yolo", description: "Full-auto AI-DLC pipeline — from prompt to done" },
|
|
79
|
-
{ name: "openspec-aware", description: "
|
|
80
|
+
{ name: "openspec-aware", description: "OpenSpec-mode authoring for PM workflows (the default when openspec/ + CLI present)" },
|
|
81
|
+
{ name: "spec-lite", description: "Chorus-native lightweight local specs (.chorus/specs/<slug>/) — the fallback when OpenSpec isn't usable" },
|
|
80
82
|
{ name: "chorus-cli", description: "Install, configure agents (chorus agents add|remove|list), env vars, and chorus mcp operations" },
|
|
81
83
|
] as const;
|
|
82
84
|
|
|
@@ -104,7 +106,28 @@ function formatSkillsList(): string {
|
|
|
104
106
|
].join("\n");
|
|
105
107
|
}
|
|
106
108
|
|
|
107
|
-
|
|
109
|
+
// Spec-mode lines for the status block. Since OpenClaw has no SessionStart hook
|
|
110
|
+
// to precompute the mode, `/chorus` is the resolver's real runtime caller and the
|
|
111
|
+
// user-visible surface: it prints `CHORUS_SPEC_MODE=<mode> (<reason>)`, the
|
|
112
|
+
// `CHORUS_OPENSPEC_ACTIVE=1` line only for a usable OpenSpec, and a halt warning
|
|
113
|
+
// when an explicit `=openspec` cannot be honored. The stage skills resolve the
|
|
114
|
+
// SAME contract inline (see src/spec-mode.ts — the single source of truth).
|
|
115
|
+
function specModeLines(spec: SpecModeResult): string[] {
|
|
116
|
+
const lines = [`CHORUS_SPEC_MODE=${spec.specMode} (${spec.specReason})`];
|
|
117
|
+
if (spec.chorusOpenspecActive) {
|
|
118
|
+
lines.push(`CHORUS_OPENSPEC_ACTIVE=1 (${spec.openspecUsableReason})`);
|
|
119
|
+
}
|
|
120
|
+
if (spec.specFail) {
|
|
121
|
+
lines.push(`WARNING: spec-mode halt — ${spec.specFail}`);
|
|
122
|
+
}
|
|
123
|
+
return lines;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function formatStatus(
|
|
127
|
+
checkin: CheckinResponse,
|
|
128
|
+
connectionStatus: string,
|
|
129
|
+
spec: SpecModeResult,
|
|
130
|
+
): string {
|
|
108
131
|
const projects = Object.values(checkin?.activeProjects ?? {});
|
|
109
132
|
const activeIdeaTotal = projects.reduce(
|
|
110
133
|
(total, p) => total + (p.activeIdeaCount ?? 0),
|
|
@@ -116,11 +139,26 @@ function formatStatus(checkin: CheckinResponse, connectionStatus: string): strin
|
|
|
116
139
|
`Active projects: ${projects.length} (${activeIdeaTotal} active idea(s))`,
|
|
117
140
|
...projects.map((p) => ` - ${p.name ?? "(unnamed)"}: ${p.activeIdeaCount ?? 0}`),
|
|
118
141
|
`Notifications: ${checkin?.notifications?.unread ?? 0} unread`,
|
|
142
|
+
...specModeLines(spec),
|
|
119
143
|
`Skills: ${PLUGIN_SKILLS.map((s) => s.name).join(", ")}`,
|
|
120
144
|
];
|
|
121
145
|
return lines.join("\n");
|
|
122
146
|
}
|
|
123
147
|
|
|
148
|
+
// Detailed spec-mode view for `/chorus spec` — the mode + reason, the openspec
|
|
149
|
+
// usability breakdown, and the install hint when OpenSpec is merely missing.
|
|
150
|
+
function formatSpec(spec: SpecModeResult): string {
|
|
151
|
+
const lines = [
|
|
152
|
+
"Spec mode (resolved inline — OpenClaw has no SessionStart hook):",
|
|
153
|
+
...specModeLines(spec).map((l) => ` ${l}`),
|
|
154
|
+
` OpenSpec usable: ${spec.openspecUsable ? "yes" : "no"} (${spec.openspecUsableReason})`,
|
|
155
|
+
];
|
|
156
|
+
if (spec.openspecHint) {
|
|
157
|
+
lines.push(` Enable OpenSpec: ${spec.openspecHint}`);
|
|
158
|
+
}
|
|
159
|
+
return lines.join("\n");
|
|
160
|
+
}
|
|
161
|
+
|
|
124
162
|
function formatTaskList(taskTracker: Record<string, TaskTrackerProject> | undefined): string {
|
|
125
163
|
const lines: string[] = [];
|
|
126
164
|
let total = 0;
|
|
@@ -159,6 +197,7 @@ const HELP_TEXT = [
|
|
|
159
197
|
"Chorus commands:",
|
|
160
198
|
" /chorus Show connection status and summary",
|
|
161
199
|
" /chorus status Same as above",
|
|
200
|
+
" /chorus spec Show the resolved spec mode (OpenSpec / spec-lite / off)",
|
|
162
201
|
" /chorus tasks List assigned tasks",
|
|
163
202
|
" /chorus ideas List assigned ideas",
|
|
164
203
|
" /chorus skills List available Chorus skills",
|
|
@@ -171,28 +210,40 @@ function errorText(prefix: string, err: unknown): string {
|
|
|
171
210
|
|
|
172
211
|
// ===== Registration =====
|
|
173
212
|
|
|
213
|
+
// Default spec-mode resolver: wires the real env + cwd. Injectable so the
|
|
214
|
+
// command test can pass a deterministic result without touching the disk/PATH.
|
|
215
|
+
function defaultResolveSpec(): SpecModeResult {
|
|
216
|
+
return resolveSpecModeFromEnv(process.env, process.cwd());
|
|
217
|
+
}
|
|
218
|
+
|
|
174
219
|
export function registerChorusCommands(
|
|
175
220
|
api: { registerCommand: (command: unknown) => void },
|
|
176
221
|
mcpClient: ChorusMcpClient,
|
|
177
|
-
getStatus: () => string
|
|
222
|
+
getStatus: () => string,
|
|
223
|
+
resolveSpec: () => SpecModeResult = defaultResolveSpec,
|
|
178
224
|
): void {
|
|
179
225
|
api.registerCommand({
|
|
180
226
|
name: "chorus",
|
|
181
|
-
description: "Chorus plugin commands: status, tasks, ideas, skills",
|
|
227
|
+
description: "Chorus plugin commands: status, spec, tasks, ideas, skills",
|
|
182
228
|
acceptsArgs: true,
|
|
183
229
|
async handler(ctx: { args?: string }) {
|
|
184
230
|
const sub = (ctx.args ?? "").trim().toLowerCase();
|
|
185
231
|
|
|
186
|
-
// /chorus or /chorus status — connection + checkin
|
|
232
|
+
// /chorus or /chorus status — connection + checkin + spec-mode summary.
|
|
187
233
|
if (!sub || sub === "status") {
|
|
188
234
|
try {
|
|
189
235
|
const checkin = (await mcpClient.callTool("chorus_checkin", {})) as CheckinResponse;
|
|
190
|
-
return { text: formatStatus(checkin, getStatus()) };
|
|
236
|
+
return { text: formatStatus(checkin, getStatus(), resolveSpec()) };
|
|
191
237
|
} catch (err) {
|
|
192
238
|
return { text: errorText("Failed to check in", err), isError: true };
|
|
193
239
|
}
|
|
194
240
|
}
|
|
195
241
|
|
|
242
|
+
// /chorus spec — the resolved spec mode (the resolver's user-visible surface).
|
|
243
|
+
if (sub === "spec") {
|
|
244
|
+
return { text: formatSpec(resolveSpec()) };
|
|
245
|
+
}
|
|
246
|
+
|
|
196
247
|
// /chorus tasks — assigned tasks via chorus_get_my_assignments.
|
|
197
248
|
if (sub === "tasks") {
|
|
198
249
|
try {
|