cohorte 3.0.0-dev.7 → 3.0.0-dev.8
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 +59 -392
- package/assets/manifest.json +1 -1
- package/dist/bundle-manifest.json +14 -14
- package/dist/chunks/{brainstorm-DasVwI1D.mjs → brainstorm-SJwOIEif.mjs} +2 -2
- package/dist/chunks/{brainstorm-DasVwI1D.mjs.map → brainstorm-SJwOIEif.mjs.map} +1 -1
- package/dist/chunks/{build-crOld8gE.mjs → build-C_-mbZfD.mjs} +2 -2
- package/dist/chunks/{build-crOld8gE.mjs.map → build-C_-mbZfD.mjs.map} +1 -1
- package/dist/chunks/{fix-B_oGzGVn.mjs → fix-C9roGiQz.mjs} +2 -2
- package/dist/chunks/{fix-B_oGzGVn.mjs.map → fix-C9roGiQz.mjs.map} +1 -1
- package/dist/chunks/{intake-Dd1KVXnO.mjs → intake-C1pesxCJ.mjs} +2 -2
- package/dist/chunks/{intake-Dd1KVXnO.mjs.map → intake-C1pesxCJ.mjs.map} +1 -1
- package/dist/chunks/{refactor-CoNoqLZr.mjs → refactor-DV3RfGjk.mjs} +2 -2
- package/dist/chunks/{refactor-CoNoqLZr.mjs.map → refactor-DV3RfGjk.mjs.map} +1 -1
- package/dist/chunks/{run-NGAxdsIX.mjs → run-BGabwbDL.mjs} +6 -2
- package/dist/chunks/{run-NGAxdsIX.mjs.map → run-BGabwbDL.mjs.map} +1 -1
- package/dist/cli.mjs +7 -7
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,437 +1,104 @@
|
|
|
1
|
-
|
|
1
|
+
# Cohorte
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Cohorte V3 est un orchestrateur durable de pipelines multi-agents, construit autour de Pi et d'un état SQLite. Il transforme une spécification validée en run observable, reprend les runs interrompus et protège chaque effet par des contrats typés, une politique de sécurité et un journal idempotent.
|
|
4
4
|
|
|
5
|
-
[
|
|
6
|
-
[](https://www.npmjs.com/package/cohorte)
|
|
7
|
-
[](https://github.com/TheBidouilleAgency/cohorte/actions/workflows/publish.yml)
|
|
8
|
-
[](https://nodejs.org)
|
|
9
|
-
[](LICENSE)
|
|
10
|
-
[](https://thebidouilleagency.github.io/cohorte/)
|
|
5
|
+
[Documentation complète](https://thebidouilleagency.github.io/cohorte/) · [Spécification V3](docs/v3/SPEC.md) · [Design](docs/v3/DESIGN.md) · [Référence CLI](docs/v3/CLI.md)
|
|
11
6
|
|
|
12
|
-
|
|
7
|
+
## Installation
|
|
13
8
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
> **V3 migration status:** the V3 runtime-independent pipeline is under active migration. V2 sources are retained
|
|
17
|
-
> under [`legacy/v2`](legacy/v2); V3 builds are immutable, snapshot-pinned and can be exercised offline with the
|
|
18
|
-
> fake runtime. See [`docs/v3`](docs/v3/PLAN.md) for the implementation plan and release gates.
|
|
19
|
-
|
|
20
|
-
For maintainers, see the [maintenance and release guide](docs/guide/maintainers.md) for the architecture, test
|
|
21
|
-
matrix, troubleshooting, PR workflow and npm/GitHub release procedure.
|
|
22
|
-
|
|
23
|
-
A **portable, stack-agnostic multi-agent pipeline** for your coding agent — Claude Code, Codex CLI,
|
|
24
|
-
Cursor, Gemini CLI or OpenCode. Install it once globally, then one command per project
|
|
25
|
-
(`/cohorte-init-pipeline`) adapts it to that project's stack.
|
|
26
|
-
|
|
27
|
-
- **The dev pipeline** — a human **lead** drives feature work through gated commands, dispatching
|
|
28
|
-
**stateless agents** that only communicate through a frozen contract:
|
|
29
|
-
|
|
30
|
-
```
|
|
31
|
-
/cohorte-brainstorm → /cohorte-spec → (design) → /cohorte-build <id> → /cohorte-review → (/cohorte-fix) → /cohorte-ship
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
<div align="center">
|
|
35
|
-
<img src="https://raw.githubusercontent.com/TheBidouilleAgency/cohorte/main/assets/demo-cli.gif" alt="cohorte doctor reporting a green pipeline, the spec board, and the gate denying a chained destructive command" width="760">
|
|
36
|
-
<br>
|
|
37
|
-
<sub><code>cohorte doctor</code> · the spec board · the gate refusing a hard-denied command chained behind a benign one.<br>
|
|
38
|
-
Recorded from the real CLI by <code>scripts/demo/record-cli.sh</code> — no output is hand-edited.</sub>
|
|
39
|
-
</div>
|
|
40
|
-
|
|
41
|
-
## Why a pipeline at all
|
|
42
|
-
|
|
43
|
-
Your coding agent is already good at the first prompt of a feature. It gets worse at every one
|
|
44
|
-
after — because what it knows lives in a conversation, and a conversation gets summarised,
|
|
45
|
-
truncated, and re-sent at input price on every turn. Cohorte is the set of constraints that stop
|
|
46
|
-
that degradation. Each one is a failure you have already had:
|
|
47
|
-
|
|
48
|
-
| The failure | The constraint |
|
|
49
|
-
| --- | --- |
|
|
50
|
-
| *"It forgot what we decided."* | The spec is **frozen to disk** and re-read by every stateless agent. Nothing is remembered, so nothing is forgotten — and `/clear` between stages is always safe. |
|
|
51
|
-
| *"The API and the UI don't fit."* | The **contract is authored before any implementer is dispatched**. Surfaces import it read-only and never talk to each other — which is what makes building them in parallel safe. |
|
|
52
|
-
| *"It edited a file I didn't want it to."* | **One owner per tree**, enforced in the rendered agent's frontmatter — not requested politely in a prompt. |
|
|
53
|
-
| *"It ran something it shouldn't have."* | `gate.py` is a **real blocking hook**, branch-aware, hard-denying destructive commands from every agent — subagents included. |
|
|
54
|
-
| *"The review said it was fine."* | Review is a **separate read-only agent** that sees the spec and the diff, never the conversation that produced them. A dead reviewer's zero findings can't read as ship. |
|
|
55
|
-
| *"Four agents just told me it doesn't compile."* | A **deterministic preflight** runs typecheck/lint/tests first. Red ⇒ **zero agents spawned**. |
|
|
56
|
-
| *"We fixed this same thing last week."* | `/cohorte-retro` turns repeated findings into ratified conventions **baked into the implementers**, so the next build never produces them. |
|
|
57
|
-
|
|
58
|
-
None of this is intelligence added to your agent — it's the opposite. It moves the decisions an
|
|
59
|
-
agent makes badly under context pressure into files, hooks and ownership boundaries, where they're
|
|
60
|
-
cheap and don't degrade with conversation length. Which is why it's markdown, shell and one Python
|
|
61
|
-
hook: **your app code never imports anything from Cohorte.**
|
|
62
|
-
|
|
63
|
-
[**The long version — including the four cases where you shouldn't use it →**](https://thebidouilleagency.github.io/cohorte/guide/why-cohorte)
|
|
64
|
-
|
|
65
|
-
## How it works — three layers
|
|
66
|
-
|
|
67
|
-
| Layer | What it holds | Lives in | Scope |
|
|
68
|
-
| --- | --- | --- | --- |
|
|
69
|
-
| **Generic core** | the workflow doctrine: commands, fixed agents, templates, hooks — zero project facts | `~/.claude` (global) — or vendored in a repo's `.claude/` (bundled) | identical everywhere, installed once |
|
|
70
|
-
| **Project profile** | stack, surfaces, commands, conventions, gates | `PIPELINE.md` + rendered surface agents + `gate-config.json`, **committed in each repo** | generated per project by `/cohorte-init-pipeline` |
|
|
71
|
-
| **User config** | kanban board links + shared Obsidian vault path | `~/.claude/cohorte.config.yaml` | personal, project-independent |
|
|
72
|
-
|
|
73
|
-
The core never hardcodes stack facts. Two mechanisms keep it generic:
|
|
74
|
-
|
|
75
|
-
1. **Runtime indirection** — commands/agents read project facts from `PIPELINE.md` (dev pipeline) or
|
|
76
|
-
`~/.claude/cohorte.config.yaml` (kanban board links + shared vault) at run time — an agent's
|
|
77
|
-
_first action_ is to read its config.
|
|
78
|
-
2. **Render-at-init** — things that must be in agent frontmatter (name, `tools:`, surface ownership)
|
|
79
|
-
are rendered per **surface** by `/cohorte-init-pipeline` from `implementer.template.md`.
|
|
80
|
-
|
|
81
|
-
## Which coding agent — [full matrix →](https://thebidouilleagency.github.io/cohorte/reference/runtimes)
|
|
82
|
-
|
|
83
|
-
The doctrine is one set of source prompts. The installer renders them into whatever your agent
|
|
84
|
-
actually reads, and branches the instructions on what it can actually do:
|
|
9
|
+
Prérequis : Node `24.16.0` minimum supporté et pnpm `12.4.2` pour contribuer au dépôt. Pour utiliser la CLI publiée :
|
|
85
10
|
|
|
86
11
|
```sh
|
|
87
|
-
|
|
88
|
-
cohorte
|
|
89
|
-
cohorte install # detects what you have and asks
|
|
12
|
+
npm install -g cohorte
|
|
13
|
+
cohorte --version
|
|
90
14
|
```
|
|
91
15
|
|
|
92
|
-
|
|
93
|
-
| --- | --- | --- | --- | --- |
|
|
94
|
-
| **Claude Code** | `.claude/commands/*.md` | ✅ `.claude/agents` | ✅ blocking hook, deny + **ask** | ✅ |
|
|
95
|
-
| **Codex CLI** | `.agents/skills/*/SKILL.md` | ✅ `.codex/agents` (TOML) | ✅ blocking hook, deny only | — |
|
|
96
|
-
| **Cursor** | `.cursor/commands/*.md` | ✅ `.cursor/agents` | ✅ blocking hook, deny + **ask** | — |
|
|
97
|
-
| **Gemini CLI** | `.gemini/commands/*.toml` | ✅ `.gemini/agents` | ✅ blocking hook, deny only | — |
|
|
98
|
-
| **OpenCode** | `.opencode/commands/*.md` | ✅ `.opencode/agents` | advisory `--check` | — |
|
|
99
|
-
|
|
100
|
-
The same `gate.py` is registered as a real blocking hook on four of the five — it speaks each
|
|
101
|
-
runtime's envelope (`PreToolUse`, `beforeShellExecution`, `BeforeTool`). Codex and Gemini have **no
|
|
102
|
-
confirmation tier**, so a pattern that would be queried elsewhere is **denied** there rather than
|
|
103
|
-
falling through; the rendered prompts say so. OpenCode extends via plugins rather than hooks, so
|
|
104
|
-
there the commands call `gate.py --check` themselves — same config and verdicts, but advisory.
|
|
105
|
-
|
|
106
|
-
**Real subagents are a requirement, not a capability to degrade around** — the pipeline's isolation
|
|
107
|
-
guarantee is that boundary, so a runtime without them is refused at install rather than rendered
|
|
108
|
-
into a pipeline whose central promise is absent. All five have them.
|
|
109
|
-
`/cohorte-doctor` reports what the runtime you are in can actually enforce.
|
|
110
|
-
|
|
111
|
-
On Codex the commands ship as **skills** (`$cohorte-build`) rather than custom prompts: prompts are
|
|
112
|
-
deprecated and user-scoped, so a teammate cloning the repo would get the profile but none of the
|
|
113
|
-
commands — `.agents/skills/` is committed with it instead.
|
|
114
|
-
|
|
115
|
-
One thing does not travel: model pins. The profile names Anthropic aliases, meaningless elsewhere,
|
|
116
|
-
so agents inherit the runtime's own model. Claude Code remains the reference implementation and its
|
|
117
|
-
install is unchanged.
|
|
118
|
-
|
|
119
|
-
## Prerequisites
|
|
120
|
-
|
|
121
|
-
Only one hard requirement — the rest is optional and independent:
|
|
122
|
-
|
|
123
|
-
- **Node ≥ 18 + npm** — _required_, for the `cohorte` CLI that lays down the core. Nothing else needs it.
|
|
124
|
-
- **[`uv`](https://docs.astral.sh/uv/) + the Serena CLI** — _optional_, the default code-retrieval
|
|
125
|
-
provider. Install it separately (`uv tool install -p 3.13 serena-agent && uv tool update-shell`); the
|
|
126
|
-
cohorte install neither needs nor touches it, so the order between the two is irrelevant. Without Serena
|
|
127
|
-
the pipeline still runs — agents just fall back to Grep/Read. Having it installed **before**
|
|
128
|
-
`/cohorte-init-pipeline` lets init wire it in one pass (otherwise `/cohorte-update-pipeline` wires it later).
|
|
129
|
-
- **On a new machine cloning a repo that's already pipeline-ised:** the Serena registration is committed
|
|
130
|
-
in the repo's `.mcp.json` (project scope, portable) — you don't re-wire. Just install the Serena CLI,
|
|
131
|
-
restart the session, and run `/cohorte-doctor` to confirm it connects.
|
|
132
|
-
|
|
133
|
-
## Install
|
|
134
|
-
|
|
135
|
-
The pipeline ships as an npm package (`cohorte`), so releases are semver-tagged — no clone
|
|
136
|
-
needed, works on macOS/Linux/Windows. **Install it once, globally**; every command below is
|
|
137
|
-
written as `cohorte …`, and that single form is what the rest of this README, the docs, and the
|
|
138
|
-
[Francois extension](https://github.com/TheBidouilleAgency/francois-plugin-cohorte) all assume:
|
|
16
|
+
Dans un projet :
|
|
139
17
|
|
|
140
18
|
```sh
|
|
141
|
-
|
|
19
|
+
cohorte init
|
|
20
|
+
cohorte doctor
|
|
21
|
+
cohorte discover --json
|
|
22
|
+
cohorte config validate
|
|
142
23
|
```
|
|
143
24
|
|
|
144
|
-
`
|
|
145
|
-
you don't own. It is the escape hatch, not the path — see
|
|
146
|
-
[why one form](https://thebidouilleagency.github.io/cohorte/reference/installers).
|
|
147
|
-
|
|
148
|
-
**Global (recommended)** — install the generic core ONCE into `~/.claude`; it serves every repo on
|
|
149
|
-
your machine. Nothing is copied per project; the gate hook is registered once and reads each repo's
|
|
150
|
-
own `gate-config.json`:
|
|
151
|
-
|
|
152
|
-
```sh
|
|
153
|
-
cohorte install --global
|
|
154
|
-
```
|
|
25
|
+
`cohorte init` crée `.cohorte/`. Les fichiers de configuration générés peuvent être réconciliés avec `cohorte reconcile --plan`, puis `cohorte reconcile --apply`. L'état d'exécution reste local et n'a pas vocation à être versionné.
|
|
155
26
|
|
|
156
|
-
|
|
157
|
-
commit: `PIPELINE.md`, the rendered surface agents, `gate-config.json`, `settings.json`, `specs/`.
|
|
158
|
-
**That's what makes team work possible in global mode**: everything project-specific travels with the
|
|
159
|
-
repo; each teammate just runs the same global one-liner once, guided by the committed
|
|
160
|
-
`.claude/pipeline.json` pointer (core version + install command) that `/cohorte-init-pipeline` writes.
|
|
27
|
+
## Workflow V3
|
|
161
28
|
|
|
162
|
-
|
|
163
|
-
<summary><strong>Alternative: per-project (bundled)</strong> — vendor the core into the repo itself.</summary>
|
|
29
|
+
Le workflow est piloté par la CLI ; il n'exige pas un runtime V2 ni des slash commands :
|
|
164
30
|
|
|
165
31
|
```sh
|
|
166
|
-
|
|
167
|
-
cohorte
|
|
32
|
+
cohorte brainstorm "Ajouter une fonctionnalité"
|
|
33
|
+
cohorte spec validate <feature-id>
|
|
34
|
+
cohorte spec freeze <feature-id>
|
|
35
|
+
cohorte run <feature-id> --detach --json
|
|
36
|
+
cohorte status <run-id> --json
|
|
37
|
+
cohorte tail <run-id>
|
|
38
|
+
cohorte review <run-id>
|
|
39
|
+
cohorte fix <run-id>
|
|
40
|
+
cohorte ship <run-id>
|
|
168
41
|
```
|
|
169
42
|
|
|
170
|
-
|
|
171
|
-
**zero-setup onboarding** (teammates get the core with `git clone`, no install step at all) and a
|
|
172
|
-
core version **pinned per repo** (no drift between projects or teammates). Cost: the core is
|
|
173
|
-
duplicated in every repo and each repo updates separately.
|
|
174
|
-
|
|
175
|
-
</details>
|
|
176
|
-
|
|
177
|
-
<details>
|
|
178
|
-
<summary><strong>No Node/npm?</strong> The original script installers still work.</summary>
|
|
43
|
+
Pour un workflow complet borné :
|
|
179
44
|
|
|
180
45
|
```sh
|
|
181
|
-
|
|
182
|
-
sh install.sh --global sh install.sh
|
|
183
|
-
# or piped:
|
|
184
|
-
curl -fsSL https://raw.githubusercontent.com/TheBidouilleAgency/cohorte/main/install.sh | sh -s -- --global
|
|
46
|
+
cohorte loop <feature-id>
|
|
185
47
|
```
|
|
186
48
|
|
|
187
|
-
|
|
188
|
-
# Windows (PowerShell 5.1+)
|
|
189
|
-
.\install.ps1 -Global # or without -Global for per-project
|
|
190
|
-
# or: & ([scriptblock]::Create((irm https://raw.githubusercontent.com/TheBidouilleAgency/cohorte/main/install.ps1))) -Global
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
Script installs from a git checkout stamp the version as `<semver> (<sha>)`; the npm CLI stamps the
|
|
194
|
-
published semver. Both land in `.claude/pipeline/VERSION` and the `pipeline.json` pointer.
|
|
195
|
-
|
|
196
|
-
</details>
|
|
49
|
+
Les commandes `build`, `audit`, `refactor`, `fleet`, `retro`, `align-ds`, `intake` et `patch` couvrent les variantes de développement. Les commandes `pause`, `resume`, `cancel`, `retry`, `approve`, `deny`, `inspect`, `diff`, `logs`, `shutdown` et `gc` administrent les runs et leur état durable.
|
|
197
50
|
|
|
198
|
-
|
|
199
|
-
> agents are scanned at session start — in an already-open session the new `/cohorte-init-pipeline`,
|
|
200
|
-
> `/cohorte-build`, etc. won't appear until you reload. This is the #1 "the install didn't work" trap.
|
|
201
|
-
|
|
202
|
-
Then, in Claude Code (from any repo, once the core is installed either way):
|
|
203
|
-
|
|
204
|
-
```
|
|
205
|
-
/cohorte-init-pipeline
|
|
206
|
-
```
|
|
51
|
+
## Obsidian
|
|
207
52
|
|
|
208
|
-
|
|
209
|
-
design system), **interviews** you for the gaps, and **generates**:
|
|
210
|
-
|
|
211
|
-
- `PIPELINE.md` — the project profile (a machine-readable `pipeline-profile` YAML block + prose conventions)
|
|
212
|
-
- one implementer agent per **surface** (e.g. `backend.md`, `frontend.md`) with strict tree ownership
|
|
213
|
-
and a per-surface `model:` tier (Haiku for mechanical surfaces, bigger models where design decisions live)
|
|
214
|
-
- `.claude/gate-config.json` + `.claude/settings.json` — the destructive-command gate, plus an
|
|
215
|
-
`allow` list of the project's read-only commands so agents don't stall on permission prompts
|
|
216
|
-
- a **code-retrieval provider** wired as a committed project-scope MCP server —
|
|
217
|
-
[Serena](https://github.com/oraios/serena) by default (live LSP symbol navigation: agents query
|
|
218
|
-
symbols instead of grep-and-reading whole files; `graphify` or `none` also available via the
|
|
219
|
-
profile's `retrieval.provider`)
|
|
220
|
-
- `scripts/new-feature.sh` + `remove-feature.sh` — parallel worktree isolation (if you enable it)
|
|
221
|
-
- `specs/_template.md` (and, on first decision, `specs/_decisions.md` — the project's one-line-per-decision
|
|
222
|
-
journal, read by `/cohorte-brainstorm`, `/cohorte-spec` and `/cohorte-audit` so features stop re-litigating settled ground)
|
|
223
|
-
|
|
224
|
-
Sanity-check `PIPELINE.md`, commit it, and run `/cohorte-brainstorm`.
|
|
225
|
-
|
|
226
|
-
## Update
|
|
53
|
+
Le tableau Kanban Obsidian est optionnel et se connecte explicitement au projet :
|
|
227
54
|
|
|
228
55
|
```sh
|
|
229
|
-
|
|
230
|
-
cohorte
|
|
231
|
-
cohorte
|
|
56
|
+
cohorte obsidian create <vault-root> <board-path>
|
|
57
|
+
cohorte obsidian connect <vault-root> <board-path>
|
|
58
|
+
cohorte obsidian status
|
|
59
|
+
cohorte obsidian move <feature-id> <column>
|
|
232
60
|
```
|
|
233
61
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
The installer refreshes the generic core (commands, hook, templates) **without** touching your
|
|
237
|
-
`PIPELINE.md`, rendered agents, `gate-config.json`, `settings.json`, or your filled
|
|
238
|
-
`~/.claude/cohorte.config.yaml`.
|
|
239
|
-
|
|
240
|
-
From inside Claude Code, prefer **`/cohorte-update-pipeline`**: it runs the right update invocation for your
|
|
241
|
-
install scope, reports `old → new` — and then **reconciles the repo's generated files to the new
|
|
242
|
-
core**: new profile fields are added at their defaults (you're only asked for genuinely new
|
|
243
|
-
decisions), surface agents are re-rendered, settings are patched additively, new capabilities get
|
|
244
|
-
wired. **`/cohorte-init-pipeline` is one-time per project** — after init, `/cohorte-update-pipeline` is the only
|
|
245
|
-
maintenance command you ever run (`/cohorte-build` auto-grows surfaces as specs need them).
|
|
62
|
+
La connexion déplace les cartes lors des transitions prévues du workflow ; elle ne copie pas le vault dans le dépôt et ne lit que le fichier de tableau configuré.
|
|
246
63
|
|
|
247
|
-
##
|
|
248
|
-
|
|
249
|
-
The two read-only halves of the pipeline, in the shell:
|
|
64
|
+
## Providers, modèles et sécurité
|
|
250
65
|
|
|
251
66
|
```sh
|
|
252
|
-
cohorte
|
|
253
|
-
cohorte
|
|
254
|
-
cohorte
|
|
67
|
+
cohorte auth login
|
|
68
|
+
cohorte auth status
|
|
69
|
+
cohorte providers list
|
|
70
|
+
cohorte providers test
|
|
71
|
+
cohorte models list
|
|
72
|
+
cohorte policy explain -- <argv>
|
|
73
|
+
cohorte doctor --json
|
|
255
74
|
```
|
|
256
75
|
|
|
257
|
-
|
|
258
|
-
extension below renders.
|
|
76
|
+
Pi est le runtime de référence de V3. Le runtime fake reste disponible pour les tests hors ligne et les smoke tests. Aucun fallback automatique ne change de provider pendant un run : le snapshot épingle le bundle, les prompts, la configuration et le runtime utilisés.
|
|
259
77
|
|
|
260
|
-
|
|
261
|
-
`U+001F` between fields (a spec title with a space in it never misaligns a column), or `--json`
|
|
262
|
-
for the native document. Both read through `lib/`, so every consumer — the CLI, a Francois
|
|
263
|
-
panel — answers from one implementation and the repo never gets two verdicts.
|
|
78
|
+
Chaque erreur affiche un code stable, son impact et l'action corrective. Les codes de contrôle sont `0` (terminé), `2` (usage), `3` (rejeté), `4` (accepté mais encore en attente) et `16` (annulé) ; les erreurs de run utilisent les classes documentées dans [`docs/v3/reference/exit-codes.md`](docs/v3/reference/exit-codes.md).
|
|
264
79
|
|
|
265
|
-
|
|
266
|
-
[Francois](https://github.com/antoine-gmnz/francois) extension panel expects. It is the one
|
|
267
|
-
Francois-aware surface in the package, and it exists for
|
|
268
|
-
[**francois-plugin-cohorte**](https://github.com/TheBidouilleAgency/francois-plugin-cohorte): a
|
|
269
|
-
manifest-only extension that renders the spec board, the doctor report and the 30-day cost as
|
|
270
|
-
three panels beside your sessions.
|
|
80
|
+
## Migration depuis V2
|
|
271
81
|
|
|
272
|
-
|
|
273
|
-
never a shell, never an absolute path. So this one needs the global install, and nothing else
|
|
274
|
-
will do:
|
|
82
|
+
Le runtime et les fichiers V2 ne sont plus présents dans le dépôt. La compatibilité fonctionnelle est couverte par les tests V3 ; l'importeur reste disponible pour convertir un ancien projet :
|
|
275
83
|
|
|
276
84
|
```sh
|
|
277
|
-
|
|
278
|
-
|
|
85
|
+
cohorte init --export-v2 <bundle-dir>
|
|
86
|
+
cohorte init --from-v2 <bundle-dir> --yes
|
|
87
|
+
cohorte migrate --rollback <report-id>
|
|
279
88
|
```
|
|
280
89
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
Versions are tracked with npm semver — the published package is the release artifact.
|
|
284
|
-
Publishing is fully automated: [`publish.yml`](.github/workflows/publish.yml) runs on every push
|
|
285
|
-
to `main`; when `package.json`'s version isn't on the registry yet it publishes to npm (trusted
|
|
286
|
-
publishing / provenance), pushes the `vX.Y.Z` tag, and creates the GitHub release. Pushes without
|
|
287
|
-
a version bump just run the sanity checks.
|
|
288
|
-
|
|
289
|
-
**Releasing = editing one line.** Bump `"version"` in `package.json` (by hand, or
|
|
290
|
-
`npm version patch --no-git-tag-version`), commit, push — CI does the rest (publish + tag +
|
|
291
|
-
release). No local tagging needed.
|
|
292
|
-
|
|
293
|
-
`npm i -g cohorte@latest` then serves the new version everywhere; installed cores record
|
|
294
|
-
it in `.claude/pipeline/VERSION` and bundled repos in their committed `pipeline.json` pointer.
|
|
295
|
-
`install` and `update` compare themselves against the registry and say so when they are behind,
|
|
296
|
-
so a CLI pinned at an old version cannot quietly re-lay an old core.
|
|
297
|
-
|
|
298
|
-
## The commands
|
|
299
|
-
|
|
300
|
-
| Command | Role |
|
|
301
|
-
| -------------------- | ------------------------------------------------------------------------------------- |
|
|
302
|
-
| `/cohorte-init-pipeline` | Detect stack → interview → generate the profile + agents. Run once per project. |
|
|
303
|
-
| `/cohorte-intake [paste]` | Triage anything that arrives (ticket, email, trace, thread) into a `/cohorte-patch` handoff or a brainstorm seed — staged to disk, carded in Ideas. |
|
|
304
|
-
| `/cohorte-brainstorm` | Interactive persona panel that pressure-tests a feature idea. |
|
|
305
|
-
| `/cohorte-spec` | Freeze the feature spec + contract into `specs/<id>.md` (UI features also get a standalone design brief at `specs/design/<id>.md`). Also applies review returns. |
|
|
306
|
-
| `/cohorte-patch [bug]` | Bug-fix entry: triage a bug and freeze a ~60-line patch spec (`specs/patch-<slug>.md`) whose regression test replaces the contract. Then the normal `/cohorte-build → review → ship`. |
|
|
307
|
-
| `/cohorte-build <id>` | Readiness gate on the frozen spec, then the lead authors the contract and dispatches one implementer per surface in parallel. |
|
|
308
|
-
| `/cohorte-review <id>` | Read-only review agents (one per touched surface, parallel) audit the diff vs the spec; out-of-scope findings go to the refactor backlog. `--pr <num>` reviews an **incoming** GitHub PR in a throwaway worktree and offers to post the report as a comment. |
|
|
309
|
-
| `/cohorte-fix <id>` | Apply a review report: remediation into the spec, re-dispatch only the surfaces with findings. |
|
|
310
|
-
| `/cohorte-fleet plan\|status\|sync` | Fly several features in parallel: overlap matrix + merge order, one worktree per feature, live status, post-merge rebase sweep. Coordination only — never headless execution. |
|
|
311
|
-
| `/cohorte-ship <id>` | Release agent commits, pushes, opens the PR; watches CI; proposes worktree teardown. |
|
|
312
|
-
| `/cohorte-audit [path]` | Prioritized refactor backlog for existing code. |
|
|
313
|
-
| `/cohorte-refactor <domain>` | Apply the backlog for one surface, TDD-first. |
|
|
314
|
-
| `/cohorte-retro [last n]` | Mine the accumulated review findings for patterns; ratified ones become §Conventions rules and the surface agents are re-rendered — the next build never produces the finding. |
|
|
315
|
-
| `/cohorte-align-ds` | Align the code UI kit to the design system (no-op if none configured). |
|
|
316
|
-
| `/cohorte-update-pipeline` | Refresh the installed core (global or bundled) to the latest published version. |
|
|
317
|
-
| `/cohorte-doctor` | Diagnose the installation (core, agents↔surfaces, hooks, gate, retrieval, worktrees). |
|
|
318
|
-
|
|
319
|
-
### Run the loop cheaply — `/clear` between stages
|
|
320
|
-
|
|
321
|
-
Every command reloads all the state it needs **from disk** — the frozen spec, the contract, the diff, the
|
|
322
|
-
Remediation checkboxes, the freshness stamp, and the last `/cohorte-review` report (staged to a gitignored
|
|
323
|
-
`specs/reports/<id>.md`). Nothing essential lives in the conversation. So the loop is **`/clear`-safe at
|
|
324
|
-
every boundary**:
|
|
325
|
-
|
|
326
|
-
```
|
|
327
|
-
/cohorte-spec → /clear → /cohorte-build → /clear → /cohorte-review → /clear → /cohorte-fix → /clear → /cohorte-review → /cohorte-ship
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
`/clear`-ing between stages sheds the accumulated main-thread context, which is the single biggest token
|
|
331
|
-
lever: long sessions (>150k) are expensive even when cached. Each command tells you when its handoff is
|
|
332
|
-
safe to clear. If you'd rather stay in one session, `/compact` mid-task does the lighter version. (Claude
|
|
333
|
-
can't fire `/clear` itself — it's a client-side command; the pipeline just makes it always safe to type.)
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
### Run features in parallel — one session per feature
|
|
90
|
+
Voir [`docs/v3/MIGRATION.md`](docs/v3/MIGRATION.md) pour les garanties, les sauvegardes et le rollback. Cette procédure ne transforme jamais un ancien run en run V3 : un run V3 démarre avec son propre snapshot et son propre état SQLite.
|
|
337
91
|
|
|
338
|
-
|
|
339
|
-
(`scripts/new-feature.sh <id>` — slots tracked in `.worktrees/slots.tsv`). That isolation is exactly
|
|
340
|
-
what makes **parallel features** safe, and it's the real throughput multiplier when you're rate-limited:
|
|
341
|
-
while feature A's `/cohorte-build` runs its agents (minutes of wall-clock you'd otherwise spend waiting), a
|
|
342
|
-
second Claude Code session can `/cohorte-spec` or `/cohorte-review` feature B.
|
|
92
|
+
## Contribuer
|
|
343
93
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
session 2: /cohorte-review feat-b → …
|
|
94
|
+
```sh
|
|
95
|
+
pnpm install --frozen-lockfile
|
|
96
|
+
pnpm ci:typecheck
|
|
97
|
+
pnpm ci:unit
|
|
98
|
+
pnpm ci:integration
|
|
99
|
+
pnpm ci:acceptance
|
|
351
100
|
```
|
|
352
101
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
- **One feature per session.** All lead-side state is keyed by feature id on disk
|
|
356
|
-
(`specs/<id>.md`, `<contract.path>/<id>.*`, `specs/reports/<id>*`), so sessions never share state —
|
|
357
|
-
but a single session interleaving two features accumulates both in its context, paying for both.
|
|
358
|
-
- **Disjoint surfaces per feature are guaranteed** (each worktree is a full checkout), and each
|
|
359
|
-
feature's DB/ports come from its slot — two features' dev servers collide on neither.
|
|
360
|
-
- **The contract package is the one shared tree.** Two features editing
|
|
361
|
-
`<contract.path>/<their-own-id>.<ext>` never conflict (one file per feature); merge order only
|
|
362
|
-
matters if a later feature *imports* an earlier one's contract — ship the dependency first.
|
|
363
|
-
- `/cohorte-ship` one at a time: it commits from the feature's branch and the freshness gate keeps a stale
|
|
364
|
-
verdict from shipping; after each merge, rebase the other live worktrees (`git rebase main`) so
|
|
365
|
-
their eventual reviews diff against reality.
|
|
366
|
-
- `/cohorte-doctor` check 6 shows the live slot table (feature ↔ worktree ↔ ports) when you lose track.
|
|
367
|
-
|
|
368
|
-
### Workflows — deterministic multi-agent runs (opt-in)
|
|
369
|
-
|
|
370
|
-
Four **workflow scripts** ship for the Claude Code Workflow runtime — the same fan-out the
|
|
371
|
-
commands orchestrate, but driven by a deterministic script instead of the lead reasoning it out
|
|
372
|
-
turn by turn. Three are opt-in variants of their same-named commands; the fourth, `loop.js`,
|
|
373
|
-
exists **only** as a workflow:
|
|
374
|
-
|
|
375
|
-
| Script | What it runs |
|
|
376
|
-
| ----------------------- | ----------------------------------------------------------------------------------------- |
|
|
377
|
-
| `workflows/review.js` | Preflight gate (aborts while red — zero agents), one reviewer per touched surface, adversarial cross-check of CRITICAL/security findings, merged verdict only. |
|
|
378
|
-
| `workflows/audit.js` | One auditor per domain (every surface + shared) concurrently, prioritized `specs/refactor-backlog.md`. |
|
|
379
|
-
| `workflows/refactor.js` | Big domains only: `shared` first and alone, then parallel surface implementers, per-domain verify + one retry. |
|
|
380
|
-
| `workflows/loop.js` | `/cohorte-loop` — build → review → [fix → review]* for one feature, unattended and resumable; exits on zero blocking findings, treading water, maxRounds, or anything that needs a human (contract change, unreviewed surface). No conversational fallback, on purpose. |
|
|
381
|
-
|
|
382
|
-
The essentials:
|
|
383
|
-
|
|
384
|
-
- **The conversational commands stay the default path** — and the fallback when workflows are
|
|
385
|
-
disabled or the client is too old. A workflow runs only when you explicitly ask for it
|
|
386
|
-
("run the review workflow").
|
|
387
|
-
- **Prerequisite: Claude Code ≥ 2.1.154** with workflows enabled. `/cohorte-doctor` (check 8) tells you
|
|
388
|
-
which path your session will take and why.
|
|
389
|
-
- **No input mid-run — questions at the edges.** A workflow runs to completion without asking
|
|
390
|
-
anything: whatever would have been a mid-run question lands in the result at the end. The
|
|
391
|
-
destructive-command gate still fires inside workflow subagents; in unattended runs its confirms
|
|
392
|
-
become denies.
|
|
393
|
-
- Phase 0 of every script is the `profile-reader` agent (haiku) — it reads `PIPELINE.md` and hands
|
|
394
|
-
the script the profile as JSON, since workflow scripts have no filesystem access. Mechanical
|
|
395
|
-
phases run on haiku; judgment phases use the same pinned agents as the commands.
|
|
396
|
-
|
|
397
|
-
Details: `profile/SCHEMA.md` §Workflows.
|
|
102
|
+
La suite complète locale est `pnpm ci:local`. La CI vérifie lint, types, unités, intégration, schémas, migrations, packaging, sécurité, crash recovery, dogfood, acceptance et compatibilité Pi. Consultez [`docs/guide/maintainers.md`](docs/guide/maintainers.md) et [`docs/v3/workspace.md`](docs/v3/workspace.md) pour l'architecture et les procédures de release.
|
|
398
103
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
Cohorte sends nothing, anywhere. There is no telemetry, no usage pings, no collector — the
|
|
402
|
-
opt-in stats that shipped through 2.2.0 were removed in 2.3.0, sender included. Everything the
|
|
403
|
-
pipeline records (`pipeline-metrics.jsonl`, `specs/reports/`) stays in your repo, and the only
|
|
404
|
-
network calls are the ones you can see: `git`, `gh`, and whatever MCP providers you wired
|
|
405
|
-
yourself.
|
|
406
|
-
|
|
407
|
-
## License
|
|
408
|
-
|
|
409
|
-
[AGPL-3.0](LICENSE). Free to use, including commercially — but if you modify it and distribute it
|
|
410
|
-
or offer it as a network service, you must publish your modifications under the same license.
|
|
411
|
-
|
|
412
|
-
## Profile reference
|
|
413
|
-
|
|
414
|
-
See `profile/SCHEMA.md` for every field in `PIPELINE.md` and how the pipeline uses it.
|
|
415
|
-
|
|
416
|
-
## Layout of this repo
|
|
417
|
-
|
|
418
|
-
```
|
|
419
|
-
package.json # npm package (cohorte) — semver source of truth
|
|
420
|
-
bin/cli.js # the npm CLI: install / update / specs / doctor / metrics / version (cross-platform, no deps)
|
|
421
|
-
bin/report.js # the four renderings of `specs` / `doctor` (human, porcelain, json, panel)
|
|
422
|
-
lib/ # shared readers behind those verbs — doctor.js (the JS port of
|
|
423
|
-
# /cohorte-doctor) + runtime.js / versions.js / yaml.js. No deps.
|
|
424
|
-
install.sh # script installer (fresh + --update) for npm-less setups — still needs Node
|
|
425
|
-
install.ps1 # same installer for Windows PowerShell (fresh + -Update)
|
|
426
|
-
core/ # copied verbatim into ~/.claude (global) or <project>/.claude (bundled)
|
|
427
|
-
agents/ # implementer.template.md (rendered per surface) + review / release / profile-reader
|
|
428
|
-
commands/ # init-pipeline + the pipeline commands + /cohorte-update-pipeline
|
|
429
|
-
hooks/ # gate.py (destructive-command gate; branch-aware; preflight phase gate)
|
|
430
|
-
templates/ # handoff / brainstorm-return / design-brief / review-feedback / pr-body / spec
|
|
431
|
-
workflows/ # Workflow-runtime scripts: review.js / audit.js / refactor.js (opt-in variants) + loop.js (workflow-only)
|
|
432
|
-
profile/
|
|
433
|
-
PIPELINE.template.md # the profile skeleton /cohorte-init-pipeline fills
|
|
434
|
-
SCHEMA.md # field reference
|
|
435
|
-
cohorte.config.template.yaml # seeds ~/.claude/cohorte.config.yaml (kanban)
|
|
436
|
-
scripts/ # worktree-isolation templates + the shipped preflight/kanban scripts
|
|
437
|
-
```
|
|
104
|
+
Licence AGPL-3.0.
|
package/assets/manifest.json
CHANGED
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
"chunks/auth-BT5oVp76.mjs.map": "d2f9f55f7e62812dc5b51ed2c19e8868c2da10598f111b0e0efbdabdb1f2ff21",
|
|
14
14
|
"chunks/auth-Dnlksuo5.mjs": "52a6f8492d257e2f2a0a616c83dbc45829340cc0bb49fa82229121f062b4c264",
|
|
15
15
|
"chunks/auth-Dnlksuo5.mjs.map": "546c1e45dd5999a4d6dc51fdb6ab95b7409083d7b27e1581a7de8096da9e2382",
|
|
16
|
-
"chunks/brainstorm-
|
|
17
|
-
"chunks/brainstorm-
|
|
18
|
-
"chunks/build-
|
|
19
|
-
"chunks/build-
|
|
16
|
+
"chunks/brainstorm-SJwOIEif.mjs": "06f3fe997017774640f0c2b2b455904012f0e5fb60d72c6b6792a918ee7bf71b",
|
|
17
|
+
"chunks/brainstorm-SJwOIEif.mjs.map": "5fadd2db4d5b824e43b31e7526a54998f5616d44ac5c65ad545288c26134573d",
|
|
18
|
+
"chunks/build-C_-mbZfD.mjs": "11161809a2867a30dd845ad00825093e3c36dee1b5bd181f1c80c9084057c405",
|
|
19
|
+
"chunks/build-C_-mbZfD.mjs.map": "efcbdc5cdcf77c67e57288021bc5944d747dadeaf8db37c935dc4553a226ed76",
|
|
20
20
|
"chunks/cancel-KV_cpsqS.mjs": "a58787b67ff0e4e36f301f01bf683e7a39340fcc7a9f8201887afc250a248155",
|
|
21
21
|
"chunks/cancel-KV_cpsqS.mjs.map": "d95aa4e6b9ba3388b200f834c124dab064eafb119b08aba8f69fea560643d7dc",
|
|
22
22
|
"chunks/canonical-C8UxeEmB.mjs": "81de21827f9dcbb402599424cbbc0850bcfdf51eb71d7d6e9cf6fb094a40e17b",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"chunks/discover-BZPolSZG.mjs.map": "396d11b9ccdc0b8fe879a1e5fcfbfb04c056c1916191ef15b5bd36165fff085b",
|
|
40
40
|
"chunks/doctor-DcDFPvQM.mjs": "d08f6ecc79b75cfb82a3b6f4c3145cbd9b35ae7431ff933f3d35e072e25d425d",
|
|
41
41
|
"chunks/doctor-DcDFPvQM.mjs.map": "7ee779e9419a358ab2444edde39b3338e638ffc4706e507acaf5d6cb789fac18",
|
|
42
|
-
"chunks/fix-
|
|
43
|
-
"chunks/fix-
|
|
42
|
+
"chunks/fix-C9roGiQz.mjs": "edacaee034f478b0f16730164300b8ada75a7b78402b9a01c826e19ce14d528f",
|
|
43
|
+
"chunks/fix-C9roGiQz.mjs.map": "2ff554a9e85ae5987ec0d58a29bd37c27aebc8f55c898504f3b10b1bc017609c",
|
|
44
44
|
"chunks/fleet-yWHQY2ya.mjs": "2df278b37aadcb285fe929dfb0423ab07b8bd8c0dcd6a03168ab2001df64e11f",
|
|
45
45
|
"chunks/fleet-yWHQY2ya.mjs.map": "c18184e24858e9c26fbca4d94bb9bad8dce827e8e8bb77f0523197bd124ab752",
|
|
46
46
|
"chunks/gc-BBXlBF5w.mjs": "8d5e39d1aee4be244d065c25ed7e43fcbf47218e241903c772f772bcf201b317",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"chunks/init--FxxkrcT.mjs.map": "df0af94f86e7238dc61dd195a7e7373840414a9e0a5d41646cb76093e08e674e",
|
|
50
50
|
"chunks/inspect-Buby4cQD.mjs": "bc5b0fc2eb07d1baaaf9de9437d4a3f78ce8175b730c06e1e5eff8ac6f012a1f",
|
|
51
51
|
"chunks/inspect-Buby4cQD.mjs.map": "2d14f18044a5a6626f3239ff845886c777d083b1e67c7d3154fccdfce4346e50",
|
|
52
|
-
"chunks/intake-
|
|
53
|
-
"chunks/intake-
|
|
52
|
+
"chunks/intake-C1pesxCJ.mjs": "ed22895a7970c83a9435dee7c742ef0544794d0cee6921c00bcf16b88726fcad",
|
|
53
|
+
"chunks/intake-C1pesxCJ.mjs.map": "5c01188c11105d65da18472cb347f1f8453b720803e15aee526063278421fd05",
|
|
54
54
|
"chunks/logs-Giq5sUau.mjs": "9ab8d3b016b9f68b8881ebedcdb4dd1a5bf5da9e9a7e6d44d375a159e5f7bb8b",
|
|
55
55
|
"chunks/logs-Giq5sUau.mjs.map": "3bc0a5b2d7d52c7b33e9f942595486444b17c8105b495d43b4e77ccb69545bb5",
|
|
56
56
|
"chunks/loop-BWsUpyus.mjs": "4cad29a23228663b6943887f8752e2012b1488f439da922b09cc347f2a4f64d0",
|
|
@@ -71,8 +71,8 @@
|
|
|
71
71
|
"chunks/providers-Dx78imKj.mjs.map": "a57182df4581da66ce5d02d852d3f083d7752801290918342dd6094acb959576",
|
|
72
72
|
"chunks/reconcile-BHn66X3C.mjs": "80a63fbf5917ac7743239e81f22a1a5f248b4dc95c28c136a393bac6d74b474b",
|
|
73
73
|
"chunks/reconcile-BHn66X3C.mjs.map": "bc900c4b21386388eccc88b279b703925638d7b6e0d3e562ee37ae1a014e3682",
|
|
74
|
-
"chunks/refactor-
|
|
75
|
-
"chunks/refactor-
|
|
74
|
+
"chunks/refactor-DV3RfGjk.mjs": "47844d3cbcc5475452c8b5c573da3a1885f032740bb59acd51aa0b5a8dc15390",
|
|
75
|
+
"chunks/refactor-DV3RfGjk.mjs.map": "40dd95f551f6de83b4a97489f7d88af6c5fb31c1b41f1e50282bbe32061b1d05",
|
|
76
76
|
"chunks/render-DyZr5Lgi.mjs": "8d4c9b51ad6048e11cdbc7338cb7d07abe7b7814fe5e14f8428d802fc1b978a5",
|
|
77
77
|
"chunks/render-DyZr5Lgi.mjs.map": "831346f7b83596ab50faa0e592926c41268de624ba52ec3bebedbaf25fd425b4",
|
|
78
78
|
"chunks/resume-GJQRb2Eb.mjs": "5688498415a41acada1a0a98cbbd800ee0b0ca4552b484b46328f54cb7f6339a",
|
|
@@ -83,8 +83,8 @@
|
|
|
83
83
|
"chunks/retry-CLrUem1H.mjs.map": "a060e5390b03042ff85d68e7c341f4e42df139d534ec80eb9af041c22f889e63",
|
|
84
84
|
"chunks/review-ZsBqp3vZ.mjs": "977b0e0e1006ffa23c042247de58e6d6ea69dd1a23d65fe110c8585909ce574d",
|
|
85
85
|
"chunks/review-ZsBqp3vZ.mjs.map": "40e858ee0bac1b347e4b07f06a70ef1445c2b9ff0b0c6a7793db682259d86b14",
|
|
86
|
-
"chunks/run-
|
|
87
|
-
"chunks/run-
|
|
86
|
+
"chunks/run-BGabwbDL.mjs": "b0308e798300b892bb6ce141b814b83e9e6dcdc78ea562213267152c7257c929",
|
|
87
|
+
"chunks/run-BGabwbDL.mjs.map": "94e4b3bb63d4606aab1f1b3de5bd34ed87b4085091a66a3e70b0e61f2569f5a9",
|
|
88
88
|
"chunks/run-tool-C3rAv4XF.mjs": "d2f87bc9b0e51b20fe0c96b98b2606e4d475e3e8e7e607da30848f6f72d8776d",
|
|
89
89
|
"chunks/run-tool-C3rAv4XF.mjs.map": "fd8f084a10550f3cedbc1e5c0269e624af28d6c6283592b4e893c74b67911a55",
|
|
90
90
|
"chunks/scan-C5uDr7Yq.mjs": "d7f51a0929a093b4c80866396503391bf696909ef9f09315be15ce082f9cd66e",
|
|
@@ -123,6 +123,6 @@
|
|
|
123
123
|
"chunks/update-BXIppsbi.mjs.map": "ef6972b3c42b9abd0e7dcc12b6e92ecfd980bfe5cf2a304138fd4afca6ec3adc",
|
|
124
124
|
"chunks/update-pipeline-Du-Gu1Pl.mjs": "820c09282249c9ea241de05bd8ade83fa5fc3e7b9a230d8a8a12c59daa442116",
|
|
125
125
|
"chunks/update-pipeline-Du-Gu1Pl.mjs.map": "16ce3e605d68be2dfeb77ce2763329fd0f10c07adf87d0050740c042832c8783",
|
|
126
|
-
"cli.mjs": "
|
|
127
|
-
"cli.mjs.map": "
|
|
126
|
+
"cli.mjs": "459ecbf88d8b6a396d683d6ce2a3f2583a87edb068e0cedaeb47e913ecdcd63d",
|
|
127
|
+
"cli.mjs.map": "4d99d28b7f1a74e6c0a9d0ec6853d48e22481c7fc614844630a8a42714ccc0f3"
|
|
128
128
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as moveConfiguredCard, t as configuredIdeas } from "./obsidian-BHLkXx_H.mjs";
|
|
2
2
|
import { N as errorOf, j as CohorteError } from "./src-Cu3TxNob.mjs";
|
|
3
|
-
import { t as run } from "./run-
|
|
3
|
+
import { t as run } from "./run-BGabwbDL.mjs";
|
|
4
4
|
import { access, mkdir, writeFile } from "node:fs/promises";
|
|
5
5
|
import { dirname, join, resolve } from "node:path";
|
|
6
6
|
import { stringify } from "yaml";
|
|
@@ -120,4 +120,4 @@ const brainstorm = {
|
|
|
120
120
|
//#endregion
|
|
121
121
|
export { brainstorm as default };
|
|
122
122
|
|
|
123
|
-
//# sourceMappingURL=brainstorm-
|
|
123
|
+
//# sourceMappingURL=brainstorm-SJwOIEif.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brainstorm-
|
|
1
|
+
{"version":3,"file":"brainstorm-SJwOIEif.mjs","names":[],"sources":["../../../../apps/cli/src/commands/brainstorm/index.ts"],"sourcesContent":["import { access, mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join, resolve } from 'node:path';\nimport { CohorteError, errorOf } from '@cohorte/base';\nimport { stringify } from 'yaml';\nimport type { CommandModule } from '../../contract/index.ts';\nimport { configuredIdeas, moveConfiguredCard } from '../obsidian/index.ts';\nimport run from '../run/index.ts';\n\nfunction slugify(value: string): string {\n const slug = value\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 64);\n return slug || 'feature';\n}\n\nfunction optionValue(args: readonly string[], flag: string): string | undefined {\n const index = args.indexOf(flag);\n return index === -1 ? undefined : args[index + 1];\n}\n\nfunction firstInput(args: readonly string[]): string | undefined {\n const valueFlags = new Set(['--id', '--output', '--ticket']);\n for (let index = 0; index < args.length; index++) {\n const value = args[index];\n if (!value?.startsWith('--')) return value;\n if (valueFlags.has(value)) index++;\n }\n return undefined;\n}\n\nconst brainstorm: CommandModule = {\n verb: 'brainstorm',\n async run(ctx, args) {\n let input = firstInput(args.positionals);\n let sourceId: string | undefined;\n if (!input) {\n const ideas = await configuredIdeas(ctx.env.HOME ?? ctx.cwd);\n const ticket = optionValue(args.positionals, '--ticket');\n const selected = ticket ? ideas.find((card) => card.id === ticket) : ideas.length === 1 ? ideas[0] : undefined;\n if (!selected) {\n if (!ideas.length) return 2;\n ctx.stdio.stdout.write(`${ideas.map((card) => `${card.id}\\t${card.title}`).join('\\n')}\\n`);\n return 2;\n }\n input = selected.title;\n sourceId = selected.id;\n }\n\n const id = slugify(optionValue(args.positionals, '--id') ?? sourceId ?? input);\n const output = resolve(\n ctx.cwd,\n optionValue(args.positionals, '--output') ?? join('.cohorte', 'specs', `${id}.yaml`),\n );\n const spec = {\n id,\n kind: 'feature' as const,\n status: 'draft' as const,\n title: input,\n acceptance: [`Define the observable outcome for: ${input}`],\n surfaces: {},\n openQuestions: [\n 'What user or operator problem does this solve?',\n 'Which project surfaces are affected?',\n 'What must be true for this feature to be accepted?',\n ],\n };\n\n try {\n await access(output);\n throw new CohorteError(errorOf('conflict/unexpected', `refusing to overwrite existing spec ${output}`));\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;\n }\n\n await mkdir(dirname(output), { recursive: true });\n await writeFile(output, stringify(spec), 'utf8');\n const reportPath = join(ctx.cwd, 'specs', 'reports', `${id}-brainstorm.md`);\n await mkdir(dirname(reportPath), { recursive: true });\n await writeFile(\n reportPath,\n [\n `# Brainstorm — ${input}`,\n '',\n `- id: ${id}`,\n `- generated: ${ctx.clock.now()}`,\n `- source: ${sourceId ?? 'cli'}`,\n '',\n '## Problem',\n '',\n input,\n '',\n '## Perspectives',\n '',\n '- Product: clarify the measurable user outcome.',\n '- UX: identify the primary flow and failure states.',\n '- Architecture: identify affected surfaces and boundaries.',\n '- Delivery: define acceptance evidence and rollout constraints.',\n '',\n '## Open questions',\n '',\n ...spec.openQuestions.map((question) => `- [ ] ${question}`),\n '',\n '## Handoff',\n '',\n `Run \\`cohorte spec validate ${id}\\` then \\`cohorte spec freeze ${id}\\` after answering the questions.`,\n '',\n ].join('\\n'),\n 'utf8',\n );\n if (sourceId) await moveConfiguredCard(ctx.env.HOME ?? ctx.cwd, sourceId, 'brainstorm', input);\n const result = { id, status: spec.status, path: output, reportPath, next: `cohorte spec validate ${id}` };\n ctx.stdio.stdout.write(`${args.json ? JSON.stringify(result) : `created ${output}\\nnext: ${result.next}`}\\n`);\n if (args.positionals.includes('--run')) {\n return run.run(ctx, {\n ...args,\n positionals: [\n id,\n output,\n '--phases',\n 'BRAINSTORM,SPEC,PREFLIGHT,BUILD,TEST,REVIEW,FIX,TEST,REVIEW,SHIP',\n '--with-fix',\n '--runtime',\n optionValue(args.positionals, '--runtime') ?? 'pi',\n ],\n });\n }\n return 0;\n },\n};\n\nexport default brainstorm;\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as run } from "./run-
|
|
1
|
+
import { t as run } from "./run-BGabwbDL.mjs";
|
|
2
2
|
//#region src/commands/build/index.ts
|
|
3
3
|
const build = {
|
|
4
4
|
verb: "build",
|
|
@@ -16,4 +16,4 @@ const build = {
|
|
|
16
16
|
//#endregion
|
|
17
17
|
export { build as default };
|
|
18
18
|
|
|
19
|
-
//# sourceMappingURL=build-
|
|
19
|
+
//# sourceMappingURL=build-C_-mbZfD.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-
|
|
1
|
+
{"version":3,"file":"build-C_-mbZfD.mjs","names":[],"sources":["../../../../apps/cli/src/commands/build/index.ts"],"sourcesContent":["import type { CommandModule } from '../../contract/index.ts';\nimport run from '../run/index.ts';\n\nconst build: CommandModule = {\n verb: 'build',\n async run(ctx, args) {\n return run.run(ctx, { ...args, positionals: [...args.positionals, '--phases', 'PREFLIGHT,BUILD,TEST'] });\n },\n};\n\nexport default build;\n"],"mappings":";;AAGA,MAAM,QAAuB;CAC3B,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,OAAO,IAAI,IAAI,KAAK;GAAE,GAAG;GAAM,aAAa;IAAC,GAAG,KAAK;IAAa;IAAY;GAAsB;EAAE,CAAC;CACzG;AACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as moveConfiguredCard } from "./obsidian-BHLkXx_H.mjs";
|
|
2
2
|
import { t as resolveSpecPath } from "./spec-path-tptqR7W8.mjs";
|
|
3
|
-
import { t as run } from "./run-
|
|
3
|
+
import { t as run } from "./run-BGabwbDL.mjs";
|
|
4
4
|
import { access, readFile, writeFile } from "node:fs/promises";
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
6
|
//#region src/commands/fix/index.ts
|
|
@@ -79,4 +79,4 @@ const fix = {
|
|
|
79
79
|
//#endregion
|
|
80
80
|
export { fix as default };
|
|
81
81
|
|
|
82
|
-
//# sourceMappingURL=fix-
|
|
82
|
+
//# sourceMappingURL=fix-C9roGiQz.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fix-
|
|
1
|
+
{"version":3,"file":"fix-C9roGiQz.mjs","names":[],"sources":["../../../../apps/cli/src/commands/fix/index.ts"],"sourcesContent":["// apps/cli/src/commands/fix/index.ts — DESIGN §9 verb `fix` (PLAN §3 rule 3 \"later units fill\n// stub files that lie inside their owned paths; they never edit ... the registry\"). Wave-0 stub: exits with the\n// documented not-available error (spec 24 `configuration/phase-not-available`); filled by the Wave-4/5\n// unit that owns `apps/cli/src/commands/fix/**`.\nimport { access, readFile, writeFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport type { CommandModule } from '../../contract/index.ts';\nimport { resolveSpecPath } from '../../project/spec-path.ts';\nimport { moveConfiguredCard } from '../obsidian/index.ts';\nimport run from '../run/index.ts';\n\nfunction surfacesFromReport(report: string): string[] {\n const surfaces = new Set<string>();\n for (const match of report.matchAll(/(?:^|\\s)(apps|packages)\\/([A-Za-z0-9._/-]+)/gu)) {\n const path = `${match[1]}/${match[2]}`.split('/');\n if (path.length >= 3) surfaces.add(path.slice(0, -1).join('-'));\n }\n return [...surfaces].sort();\n}\n\nconst fix: CommandModule = {\n verb: 'fix',\n async run(ctx, args) {\n const runId = args.positionals[0];\n if (!runId) return 2;\n const reportPath = args.positionals[1];\n if (reportPath && !runId.startsWith('run_')) {\n let specPath = resolveSpecPath(ctx.cwd, runId);\n try {\n await access(specPath);\n } catch {\n specPath = resolve(ctx.cwd, 'specs', `${runId}.md`);\n }\n let report: string;\n try {\n report = await readFile(reportPath, 'utf8');\n } catch {\n ctx.stdio.stderr.write(`review report not found: ${reportPath}\\n`);\n return 1;\n }\n try {\n await access(specPath);\n } catch {\n ctx.stdio.stderr.write(`spec not found: ${specPath}\\n`);\n return 1;\n }\n const source = await readFile(specPath, 'utf8');\n const heading = /\\n## Remediation\\n/u.test(source) ? '' : '\\n\\n## Remediation\\n';\n const entry = `\\n\\n### ${ctx.clock.now()}\\n\\n${report.trim()}\\n`;\n await writeFile(specPath, `${source.replace(/\\s+$/u, '')}${heading}${entry}`, 'utf8');\n await moveConfiguredCard(ctx.env.HOME ?? ctx.cwd, runId, 'fix');\n const surfaces = surfacesFromReport(report);\n const followUp = await run.run(ctx, {\n ...args,\n positionals: [\n runId,\n specPath,\n '--profile',\n 'feature',\n '--phases',\n 'PREFLIGHT,BUILD,TEST,REVIEW',\n ...(surfaces.length > 0 ? ['--surfaces', surfaces.join(',')] : []),\n '--with-fix',\n ],\n });\n ctx.stdio.stdout.write(\n `${JSON.stringify({ feature: runId, specPath, reportPath, status: 'remediation-recorded', surfaces, followUp })}\\n`,\n );\n return followUp;\n }\n const result = await ctx.controller.send('retry', { target: { kind: 'phase', state: 'FIX' } }, { runId });\n ctx.stdio.stdout.write(`${JSON.stringify(result)}\\n`);\n if (result.status === 'pending' || result.status === 'completed')\n await moveConfiguredCard(ctx.env.HOME ?? ctx.cwd, runId, 'fix');\n return result.status === 'rejected' ? 3 : result.status === 'pending' ? 4 : 0;\n },\n};\n\nexport default fix;\n"],"mappings":";;;;;;AAWA,SAAS,mBAAmB,QAA0B;CACpD,MAAM,2BAAW,IAAI,IAAY;CACjC,KAAK,MAAM,SAAS,OAAO,SAAS,+CAA+C,GAAG;EACpF,MAAM,OAAO,GAAG,MAAM,GAAG,GAAG,MAAM,KAAK,MAAM,GAAG;EAChD,IAAI,KAAK,UAAU,GAAG,SAAS,IAAI,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC;CAChE;CACA,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC,KAAK;AAC5B;AAEA,MAAM,MAAqB;CACzB,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,MAAM,QAAQ,KAAK,YAAY;EAC/B,IAAI,CAAC,OAAO,OAAO;EACnB,MAAM,aAAa,KAAK,YAAY;EACpC,IAAI,cAAc,CAAC,MAAM,WAAW,MAAM,GAAG;GAC3C,IAAI,WAAW,gBAAgB,IAAI,KAAK,KAAK;GAC7C,IAAI;IACF,MAAM,OAAO,QAAQ;GACvB,QAAQ;IACN,WAAW,QAAQ,IAAI,KAAK,SAAS,GAAG,MAAM,IAAI;GACpD;GACA,IAAI;GACJ,IAAI;IACF,SAAS,MAAM,SAAS,YAAY,MAAM;GAC5C,QAAQ;IACN,IAAI,MAAM,OAAO,MAAM,4BAA4B,WAAW,GAAG;IACjE,OAAO;GACT;GACA,IAAI;IACF,MAAM,OAAO,QAAQ;GACvB,QAAQ;IACN,IAAI,MAAM,OAAO,MAAM,mBAAmB,SAAS,GAAG;IACtD,OAAO;GACT;GACA,MAAM,SAAS,MAAM,SAAS,UAAU,MAAM;GAC9C,MAAM,UAAU,sBAAsB,KAAK,MAAM,IAAI,KAAK;GAC1D,MAAM,QAAQ,WAAW,IAAI,MAAM,IAAI,EAAE,MAAM,OAAO,KAAK,EAAE;GAC7D,MAAM,UAAU,UAAU,GAAG,OAAO,QAAQ,SAAS,EAAE,IAAI,UAAU,SAAS,MAAM;GACpF,MAAM,mBAAmB,IAAI,IAAI,QAAQ,IAAI,KAAK,OAAO,KAAK;GAC9D,MAAM,WAAW,mBAAmB,MAAM;GAC1C,MAAM,WAAW,MAAM,IAAI,IAAI,KAAK;IAClC,GAAG;IACH,aAAa;KACX;KACA;KACA;KACA;KACA;KACA;KACA,GAAI,SAAS,SAAS,IAAI,CAAC,cAAc,SAAS,KAAK,GAAG,CAAC,IAAI,CAAC;KAChE;IACF;GACF,CAAC;GACD,IAAI,MAAM,OAAO,MACf,GAAG,KAAK,UAAU;IAAE,SAAS;IAAO;IAAU;IAAY,QAAQ;IAAwB;IAAU;GAAS,CAAC,EAAE,GAClH;GACA,OAAO;EACT;EACA,MAAM,SAAS,MAAM,IAAI,WAAW,KAAK,SAAS,EAAE,QAAQ;GAAE,MAAM;GAAS,OAAO;EAAM,EAAE,GAAG,EAAE,MAAM,CAAC;EACxG,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,MAAM,EAAE,GAAG;EACpD,IAAI,OAAO,WAAW,aAAa,OAAO,WAAW,aACnD,MAAM,mBAAmB,IAAI,IAAI,QAAQ,IAAI,KAAK,OAAO,KAAK;EAChE,OAAO,OAAO,WAAW,aAAa,IAAI,OAAO,WAAW,YAAY,IAAI;CAC9E;AACF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import brainstorm from "./brainstorm-
|
|
1
|
+
import brainstorm from "./brainstorm-SJwOIEif.mjs";
|
|
2
2
|
//#region src/commands/intake/index.ts
|
|
3
3
|
const intake = {
|
|
4
4
|
verb: "intake",
|
|
@@ -9,4 +9,4 @@ const intake = {
|
|
|
9
9
|
//#endregion
|
|
10
10
|
export { intake as default };
|
|
11
11
|
|
|
12
|
-
//# sourceMappingURL=intake-
|
|
12
|
+
//# sourceMappingURL=intake-C1pesxCJ.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intake-
|
|
1
|
+
{"version":3,"file":"intake-C1pesxCJ.mjs","names":[],"sources":["../../../../apps/cli/src/commands/intake/index.ts"],"sourcesContent":["import type { CommandModule } from '../../contract/index.ts';\nimport brainstorm from '../brainstorm/index.ts';\n\nconst intake: CommandModule = {\n verb: 'intake',\n async run(ctx, args) {\n return brainstorm.run(ctx, args);\n },\n};\n\nexport default intake;\n"],"mappings":";;AAGA,MAAM,SAAwB;CAC5B,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,OAAO,WAAW,IAAI,KAAK,IAAI;CACjC;AACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as specContentSha256 } from "./schema-Bq9qtgsd.mjs";
|
|
2
|
-
import { t as run } from "./run-
|
|
2
|
+
import { t as run } from "./run-BGabwbDL.mjs";
|
|
3
3
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { parse, stringify } from "yaml";
|
|
@@ -150,4 +150,4 @@ const refactor = {
|
|
|
150
150
|
//#endregion
|
|
151
151
|
export { refactor as default };
|
|
152
152
|
|
|
153
|
-
//# sourceMappingURL=refactor-
|
|
153
|
+
//# sourceMappingURL=refactor-DV3RfGjk.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refactor-CoNoqLZr.mjs","names":[],"sources":["../../../../apps/cli/src/commands/refactor/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport { specContentSha256 } from '@cohorte/config/schema';\nimport { parse, stringify } from 'yaml';\nimport type { CommandModule } from '../../contract/index.ts';\nimport run from '../run/index.ts';\n\nconst exec = promisify(execFile);\n\nasync function configuredGates(root: string): Promise<string[]> {\n try {\n const source = await readFile(join(root, 'PIPELINE.md'), 'utf8');\n const block = /```yaml pipeline-profile\\s*\\n([\\s\\S]*?)\\n```/u.exec(source)?.[1];\n const commands = block ? (parse(block) as { commands?: Record<string, unknown> }).commands : undefined;\n if (!commands) return [];\n return ['format', 'lint', 'typecheck', 'test']\n .map((key) => commands[key])\n .filter((value): value is string => typeof value === 'string' && value.length > 0 && !value.startsWith('<'));\n } catch {\n return [];\n }\n}\n\nasync function verifyDomain(root: string, domain: string): Promise<number> {\n const commands = await configuredGates(root);\n if (!commands.length) return 0;\n const results: string[] = [];\n let status = 0;\n for (const command of commands) {\n try {\n const result = await exec('sh', ['-lc', command], { cwd: root, maxBuffer: 2_000_000 });\n results.push(`$ ${command}\\n${result.stdout || '(no output)'}${result.stderr || ''}`);\n } catch (error) {\n const failure = error as { stdout?: string; stderr?: string; message?: string };\n results.push(`$ ${command}\\n${failure.stdout ?? ''}${failure.stderr ?? failure.message ?? String(error)}`);\n status = 15;\n }\n }\n await mkdir(join(root, 'specs', 'reports'), { recursive: true });\n await writeFile(join(root, 'specs', 'reports', `refactor-verify.${domain}.txt`), `${results.join('\\n\\n')}\\n`, 'utf8');\n return status;\n}\n\nconst refactor: CommandModule = {\n verb: 'refactor',\n async run(ctx, args) {\n const backlogPath = join(ctx.cwd, 'specs', 'refactor-backlog.md');\n let source: string;\n try {\n source = await readFile(backlogPath, 'utf8');\n } catch {\n ctx.stdio.stderr.write(`backlog not found: ${backlogPath}\\n`);\n return 1;\n }\n const requested = args.positionals.filter((value) => !value.startsWith('--'));\n const headings = [...source.matchAll(/^##\\s+([^\\n]+)$/gmu)];\n const domains =\n requested.includes('all') || requested.length === 0\n ? headings.map((match) => match[1]?.trim()).filter((x): x is string => Boolean(x))\n : requested;\n if (!domains.length) return 2;\n const completed: Array<{ domain: string; tasks: string[] }> = [];\n const outcomes: Array<{\n domain: string;\n spec?: string;\n tasks: string[];\n status: number;\n attempts: number;\n skipped?: boolean;\n reason?: string;\n }> = [];\n await mkdir(join(ctx.cwd, 'specs', 'reports'), { recursive: true });\n const runDomain = async (domain: string): Promise<number> => {\n const heading = headings.find((match) => match[1]?.trim() === domain);\n const start = heading?.index ?? -1;\n const next = start < 0 ? -1 : source.indexOf('\\n## ', start + 1);\n const block = start < 0 ? '' : source.slice(start, next < 0 ? source.length : next);\n const tasks = block.split(/\\r?\\n/u).filter((line) => /^- \\[ \\]/u.test(line));\n if (!tasks.length) return 0;\n if (tasks.length < 5) {\n outcomes.push({\n domain,\n tasks,\n status: 0,\n attempts: 0,\n skipped: true,\n reason: 'small backlog: use the conversational refactor workflow',\n });\n return 0;\n }\n const id = `refactor-${domain\n .toLowerCase()\n .replace(/[^a-z0-9]+/gu, '-')\n .replace(/^-|-$/gu, '')}`;\n const draft = {\n id,\n kind: 'feature' as const,\n status: 'draft' as const,\n title: `Refactor ${domain}`,\n acceptance: tasks.map((task) => task.replace(/^- \\[ \\]\\s*/u, '')),\n surfaces: { [domain]: { tasks } },\n openQuestions: [],\n };\n const spec = { ...draft, status: 'frozen' as const, sha256: specContentSha256(draft) };\n const path = join(ctx.cwd, '.cohorte', 'specs', `${id}.yaml`);\n await mkdir(join(ctx.cwd, '.cohorte', 'specs'), { recursive: true });\n await writeFile(path, stringify(spec), 'utf8');\n const first = await run.run(ctx, {\n ...args,\n positionals: [path, '--profile', 'feature', '--phases', 'BUILD,TEST,REVIEW', '--with-fix'],\n });\n const firstGates = first === 0 ? await verifyDomain(ctx.cwd, domain) : 0;\n const result =\n first === 3 || firstGates !== 0\n ? await run.run(ctx, {\n ...args,\n positionals: [path, '--profile', 'feature', '--phases', 'BUILD,TEST,REVIEW', '--with-fix'],\n })\n : first;\n const finalGates = result === 0 ? await verifyDomain(ctx.cwd, domain) : 0;\n const status = finalGates !== 0 ? finalGates : result;\n outcomes.push({ domain, spec: path, tasks, status, attempts: first === 3 || firstGates !== 0 ? 2 : 1 });\n if (status === 0) {\n completed.push({ domain, tasks });\n }\n return status;\n };\n\n // V2 deliberately serialises the shared contract slice, then fans out\n // disjoint surfaces. Keeping that ordering avoids parallel agents editing\n // the same contract while retaining the wall-clock win for independent\n // domains.\n const ordered = domains.includes('shared')\n ? ['shared', ...domains.filter((domain) => domain !== 'shared')]\n : domains;\n const sharedStatus = ordered[0] === 'shared' ? await runDomain('shared') : 0;\n const parallelDomains = ordered[0] === 'shared' ? ordered.slice(1) : ordered;\n const statuses = sharedStatus === 0 ? await Promise.all(parallelDomains.map((domain) => runDomain(domain))) : [];\n for (const item of completed) {\n source = source\n .split(/\\r?\\n/u)\n .map((line) =>\n line.startsWith('- [ ] ') && item.tasks.includes(line) ? line.replace('- [ ] ', '- [x] ') : line,\n )\n .join('\\n');\n }\n await writeFile(backlogPath, `${source}\\n`, 'utf8');\n await writeFile(\n join(ctx.cwd, 'specs', 'reports', 'refactor.json'),\n `${JSON.stringify({ generatedAt: ctx.clock.now(), domains, outcomes }, null, 2)}\\n`,\n 'utf8',\n );\n return Math.max(sharedStatus, ...statuses, 0);\n },\n};\n\nexport default refactor;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"refactor-DV3RfGjk.mjs","names":[],"sources":["../../../../apps/cli/src/commands/refactor/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport { specContentSha256 } from '@cohorte/config/schema';\nimport { parse, stringify } from 'yaml';\nimport type { CommandModule } from '../../contract/index.ts';\nimport run from '../run/index.ts';\n\nconst exec = promisify(execFile);\n\nasync function configuredGates(root: string): Promise<string[]> {\n try {\n const source = await readFile(join(root, 'PIPELINE.md'), 'utf8');\n const block = /```yaml pipeline-profile\\s*\\n([\\s\\S]*?)\\n```/u.exec(source)?.[1];\n const commands = block ? (parse(block) as { commands?: Record<string, unknown> }).commands : undefined;\n if (!commands) return [];\n return ['format', 'lint', 'typecheck', 'test']\n .map((key) => commands[key])\n .filter((value): value is string => typeof value === 'string' && value.length > 0 && !value.startsWith('<'));\n } catch {\n return [];\n }\n}\n\nasync function verifyDomain(root: string, domain: string): Promise<number> {\n const commands = await configuredGates(root);\n if (!commands.length) return 0;\n const results: string[] = [];\n let status = 0;\n for (const command of commands) {\n try {\n const result = await exec('sh', ['-lc', command], { cwd: root, maxBuffer: 2_000_000 });\n results.push(`$ ${command}\\n${result.stdout || '(no output)'}${result.stderr || ''}`);\n } catch (error) {\n const failure = error as { stdout?: string; stderr?: string; message?: string };\n results.push(`$ ${command}\\n${failure.stdout ?? ''}${failure.stderr ?? failure.message ?? String(error)}`);\n status = 15;\n }\n }\n await mkdir(join(root, 'specs', 'reports'), { recursive: true });\n await writeFile(join(root, 'specs', 'reports', `refactor-verify.${domain}.txt`), `${results.join('\\n\\n')}\\n`, 'utf8');\n return status;\n}\n\nconst refactor: CommandModule = {\n verb: 'refactor',\n async run(ctx, args) {\n const backlogPath = join(ctx.cwd, 'specs', 'refactor-backlog.md');\n let source: string;\n try {\n source = await readFile(backlogPath, 'utf8');\n } catch {\n ctx.stdio.stderr.write(`backlog not found: ${backlogPath}\\n`);\n return 1;\n }\n const requested = args.positionals.filter((value) => !value.startsWith('--'));\n const headings = [...source.matchAll(/^##\\s+([^\\n]+)$/gmu)];\n const domains =\n requested.includes('all') || requested.length === 0\n ? headings.map((match) => match[1]?.trim()).filter((x): x is string => Boolean(x))\n : requested;\n if (!domains.length) return 2;\n const completed: Array<{ domain: string; tasks: string[] }> = [];\n const outcomes: Array<{\n domain: string;\n spec?: string;\n tasks: string[];\n status: number;\n attempts: number;\n skipped?: boolean;\n reason?: string;\n }> = [];\n await mkdir(join(ctx.cwd, 'specs', 'reports'), { recursive: true });\n const runDomain = async (domain: string): Promise<number> => {\n const heading = headings.find((match) => match[1]?.trim() === domain);\n const start = heading?.index ?? -1;\n const next = start < 0 ? -1 : source.indexOf('\\n## ', start + 1);\n const block = start < 0 ? '' : source.slice(start, next < 0 ? source.length : next);\n const tasks = block.split(/\\r?\\n/u).filter((line) => /^- \\[ \\]/u.test(line));\n if (!tasks.length) return 0;\n if (tasks.length < 5) {\n outcomes.push({\n domain,\n tasks,\n status: 0,\n attempts: 0,\n skipped: true,\n reason: 'small backlog: use the conversational refactor workflow',\n });\n return 0;\n }\n const id = `refactor-${domain\n .toLowerCase()\n .replace(/[^a-z0-9]+/gu, '-')\n .replace(/^-|-$/gu, '')}`;\n const draft = {\n id,\n kind: 'feature' as const,\n status: 'draft' as const,\n title: `Refactor ${domain}`,\n acceptance: tasks.map((task) => task.replace(/^- \\[ \\]\\s*/u, '')),\n surfaces: { [domain]: { tasks } },\n openQuestions: [],\n };\n const spec = { ...draft, status: 'frozen' as const, sha256: specContentSha256(draft) };\n const path = join(ctx.cwd, '.cohorte', 'specs', `${id}.yaml`);\n await mkdir(join(ctx.cwd, '.cohorte', 'specs'), { recursive: true });\n await writeFile(path, stringify(spec), 'utf8');\n const first = await run.run(ctx, {\n ...args,\n positionals: [path, '--profile', 'feature', '--phases', 'BUILD,TEST,REVIEW', '--with-fix'],\n });\n const firstGates = first === 0 ? await verifyDomain(ctx.cwd, domain) : 0;\n const result =\n first === 3 || firstGates !== 0\n ? await run.run(ctx, {\n ...args,\n positionals: [path, '--profile', 'feature', '--phases', 'BUILD,TEST,REVIEW', '--with-fix'],\n })\n : first;\n const finalGates = result === 0 ? await verifyDomain(ctx.cwd, domain) : 0;\n const status = finalGates !== 0 ? finalGates : result;\n outcomes.push({ domain, spec: path, tasks, status, attempts: first === 3 || firstGates !== 0 ? 2 : 1 });\n if (status === 0) {\n completed.push({ domain, tasks });\n }\n return status;\n };\n\n // V2 deliberately serialises the shared contract slice, then fans out\n // disjoint surfaces. Keeping that ordering avoids parallel agents editing\n // the same contract while retaining the wall-clock win for independent\n // domains.\n const ordered = domains.includes('shared')\n ? ['shared', ...domains.filter((domain) => domain !== 'shared')]\n : domains;\n const sharedStatus = ordered[0] === 'shared' ? await runDomain('shared') : 0;\n const parallelDomains = ordered[0] === 'shared' ? ordered.slice(1) : ordered;\n const statuses = sharedStatus === 0 ? await Promise.all(parallelDomains.map((domain) => runDomain(domain))) : [];\n for (const item of completed) {\n source = source\n .split(/\\r?\\n/u)\n .map((line) =>\n line.startsWith('- [ ] ') && item.tasks.includes(line) ? line.replace('- [ ] ', '- [x] ') : line,\n )\n .join('\\n');\n }\n await writeFile(backlogPath, `${source}\\n`, 'utf8');\n await writeFile(\n join(ctx.cwd, 'specs', 'reports', 'refactor.json'),\n `${JSON.stringify({ generatedAt: ctx.clock.now(), domains, outcomes }, null, 2)}\\n`,\n 'utf8',\n );\n return Math.max(sharedStatus, ...statuses, 0);\n },\n};\n\nexport default refactor;\n"],"mappings":"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"}
|
|
@@ -94,7 +94,11 @@ const run = {
|
|
|
94
94
|
else ctx.stdio.stdout.write(`${result.status}\n`);
|
|
95
95
|
if (result.status === "pending" || result.status === "completed") {
|
|
96
96
|
const card = spec?.replace(/\.ya?ml$/, "").split(/[\\/]/).pop();
|
|
97
|
-
if (card)
|
|
97
|
+
if (card) {
|
|
98
|
+
const firstPhase = phases?.[0];
|
|
99
|
+
const stage = firstPhase === "BRAINSTORM" ? "brainstorm" : firstPhase === "SPEC" ? "spec" : firstPhase === "REVIEW" || profile === "review" ? "review" : firstPhase === "FIX" ? "fix" : firstPhase === "SHIP" ? "ship" : "building";
|
|
100
|
+
await moveConfiguredCard(ctx.env.HOME ?? ctx.cwd, card, stage);
|
|
101
|
+
}
|
|
98
102
|
}
|
|
99
103
|
return result.status === "rejected" ? 3 : result.status === "pending" ? 4 : 0;
|
|
100
104
|
}
|
|
@@ -102,4 +106,4 @@ const run = {
|
|
|
102
106
|
//#endregion
|
|
103
107
|
export { run_exports as n, run as t };
|
|
104
108
|
|
|
105
|
-
//# sourceMappingURL=run-
|
|
109
|
+
//# sourceMappingURL=run-BGabwbDL.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-
|
|
1
|
+
{"version":3,"file":"run-BGabwbDL.mjs","names":[],"sources":["../../../../apps/cli/src/commands/run/index.ts"],"sourcesContent":["// apps/cli/src/commands/run/index.ts — DESIGN §9 verb `run` (PLAN §3 rule 3 \"later units fill\n// stub files that lie inside their owned paths; they never edit ... the registry\"). Wave-0 stub: exits with the\n// documented not-available error (spec 24 `configuration/phase-not-available`); filled by the Wave-4/5\n// unit that owns `apps/cli/src/commands/run/**`.\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { CohorteError, errorOf, type Sha256, type SurfaceId, sha256Hex } from '@cohorte/base';\nimport { loadConfig, resolveConfig } from '@cohorte/config';\nimport { createKeyStore, createTrustStore } from '@cohorte/security/auth';\nimport type { CommandModule } from '../../contract/index.ts';\nimport { resolveSpecPath } from '../../project/spec-path.ts';\nimport { moveConfiguredCard } from '../obsidian/index.ts';\n\nconst run: CommandModule = {\n verb: 'run',\n async run(ctx, args) {\n const valueAfter = (flag: string): string | undefined => {\n const index = args.positionals.indexOf(flag);\n return index >= 0 ? args.positionals[index + 1] : undefined;\n };\n const profile = (valueAfter('--profile') ??\n args.positionals.find((value) => ['feature', 'bugfix', 'review'].includes(value)) ??\n 'feature') as 'feature' | 'bugfix' | 'review';\n if (!['feature', 'bugfix', 'review'].includes(profile)) return 2;\n const spec = args.positionals.find(\n (value, index) =>\n !value.startsWith('--') &&\n args.positionals[index - 1] !== '--profile' &&\n args.positionals[index - 1] !== '--runtime' &&\n args.positionals[index - 1] !== '--script' &&\n args.positionals[index - 1] !== '--model' &&\n args.positionals[index - 1] !== '--wait' &&\n args.positionals[index - 1] !== '--phases' &&\n !['feature', 'bugfix', 'review'].includes(value),\n );\n const model = valueAfter('--model');\n const phasesValue = valueAfter('--phases');\n const surfaces = args.positionals\n .flatMap((value, index) =>\n value === '--surface' && args.positionals[index + 1] ? [args.positionals[index + 1] as string] : [],\n )\n .concat((valueAfter('--surfaces') ?? '').split(',').filter(Boolean));\n const phases = phasesValue\n ?.split(',')\n .map((value) => value.trim().toUpperCase())\n .filter((value): value is 'BRAINSTORM' | 'SPEC' | 'PREFLIGHT' | 'BUILD' | 'TEST' | 'REVIEW' | 'FIX' | 'SHIP' =>\n ['BRAINSTORM', 'SPEC', 'PREFLIGHT', 'BUILD', 'TEST', 'REVIEW', 'FIX', 'SHIP'].includes(value),\n );\n const waitValue = valueAfter('--wait');\n const waitMs = waitValue === undefined ? undefined : Number(waitValue) * 1000;\n if (waitValue !== undefined && (!Number.isFinite(waitMs) || (waitMs as number) < 0)) return 2;\n const trustProjectConfig = args.positionals.includes('--trust-project-config');\n let consent: { policySha256: Sha256; via: 'cli-flag' } | undefined;\n // Test/fake contexts intentionally omit HOME; a real CLI invocation always\n // supplies it and is the only context allowed to resolve project trust.\n if (ctx.env.HOME && existsSync(join(ctx.cwd, '.cohorte', 'config.yaml'))) {\n const home = ctx.env.HOME ?? ctx.cwd;\n const loaded = await loadConfig({ cwd: ctx.cwd, home });\n const keys = createKeyStore({ directory: join(home, '.cohorte', 'keys') });\n const trustStore = createTrustStore({ directory: join(home, '.cohorte', 'trust'), keys });\n const resolved = await resolveConfig(loaded, {\n trustStore,\n projectKeyId: sha256Hex(loaded.projectRoot).slice(0, 24),\n });\n if (resolved.status === 'untrusted' && !trustProjectConfig) {\n throw new CohorteError(\n errorOf(\n 'security/project-policy-untrusted',\n `project policy is untrusted (${resolved.loosenedKeys.join(', ') || 'unknown key'})`,\n ),\n );\n }\n if (resolved.status === 'untrusted') consent = { policySha256: resolved.policySha256, via: 'cli-flag' };\n }\n const payload = {\n profile,\n unattended: false,\n ...(spec ? { spec: { path: resolveSpecPath(ctx.cwd, spec) } } : {}),\n ...(valueAfter('--runtime') ? { runtime: valueAfter('--runtime') as string } : {}),\n ...(valueAfter('--script') ? { fakeScript: valueAfter('--script') as string } : {}),\n ...(phasesValue !== undefined ? { phases: phases ?? [] } : {}),\n ...(surfaces.length > 0 ? { surfaces: surfaces as SurfaceId[] } : {}),\n ...(args.positionals.includes('--with-fix') ? { withFix: true } : {}),\n ...(args.positionals.includes('--unattended') ? { unattended: true } : {}),\n ...(model ? { modelOverrides: { implementer: { provider: 'default', model } } } : {}),\n ...(consent ? { consent } : {}),\n };\n const result =\n waitMs === undefined\n ? await ctx.controller.send('start', payload)\n : await ctx.controller.send('start', payload, { waitMs });\n if (result.status === 'pending' && result.result && typeof result.result === 'object' && 'runId' in result.result) {\n const runId = (result.result as { runId?: string }).runId;\n if (runId) await ctx.hostSpawner.spawnDetached(runId);\n }\n if (args.json) ctx.stdio.stdout.write(`${JSON.stringify(result)}\\n`);\n else ctx.stdio.stdout.write(`${result.status}\\n`);\n if (result.status === 'pending' || result.status === 'completed') {\n const card = spec\n ?.replace(/\\.ya?ml$/, '')\n .split(/[\\\\/]/)\n .pop();\n if (card) {\n // A run is accepted before its host has entered the first phase. Reflect\n // that phase instead of jumping straight to Building. This matters for\n // `brainstorm --run`, where the run may still fail during recovery or\n // preflight before it ever reaches BUILD.\n const firstPhase = phases?.[0];\n const stage =\n firstPhase === 'BRAINSTORM'\n ? 'brainstorm'\n : firstPhase === 'SPEC'\n ? 'spec'\n : firstPhase === 'REVIEW' || profile === 'review'\n ? 'review'\n : firstPhase === 'FIX'\n ? 'fix'\n : firstPhase === 'SHIP'\n ? 'ship'\n : 'building';\n await moveConfiguredCard(ctx.env.HOME ?? ctx.cwd, card, stage);\n }\n }\n return result.status === 'rejected' ? 3 : result.status === 'pending' ? 4 : 0;\n },\n};\n\nexport default run;\n"],"mappings":"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"}
|
package/dist/cli.mjs
CHANGED
|
@@ -16,12 +16,12 @@ const COMMAND_MODULES = Object.freeze({
|
|
|
16
16
|
init: () => import("./chunks/init--FxxkrcT.mjs"),
|
|
17
17
|
doctor: () => import("./chunks/doctor-DcDFPvQM.mjs"),
|
|
18
18
|
discover: () => import("./chunks/discover-BZPolSZG.mjs"),
|
|
19
|
-
run: () => import("./chunks/run-
|
|
19
|
+
run: () => import("./chunks/run-BGabwbDL.mjs").then((n) => n.n),
|
|
20
20
|
loop: () => import("./chunks/loop-BWsUpyus.mjs"),
|
|
21
|
-
build: () => import("./chunks/build-
|
|
22
|
-
intake: () => import("./chunks/intake-
|
|
21
|
+
build: () => import("./chunks/build-C_-mbZfD.mjs"),
|
|
22
|
+
intake: () => import("./chunks/intake-C1pesxCJ.mjs"),
|
|
23
23
|
audit: () => import("./chunks/audit-B4ZWQ_DI.mjs"),
|
|
24
|
-
refactor: () => import("./chunks/refactor-
|
|
24
|
+
refactor: () => import("./chunks/refactor-DV3RfGjk.mjs"),
|
|
25
25
|
fleet: () => import("./chunks/fleet-yWHQY2ya.mjs"),
|
|
26
26
|
retro: () => import("./chunks/retro-Dr-2RTNN.mjs"),
|
|
27
27
|
"align-ds": () => import("./chunks/align-ds-r_X_1aQ_.mjs"),
|
|
@@ -40,7 +40,7 @@ const COMMAND_MODULES = Object.freeze({
|
|
|
40
40
|
tail: () => import("./chunks/tail-BuhT-2PH.mjs"),
|
|
41
41
|
diff: () => import("./chunks/diff-Qw0II0sj.mjs"),
|
|
42
42
|
review: () => import("./chunks/review-ZsBqp3vZ.mjs"),
|
|
43
|
-
fix: () => import("./chunks/fix-
|
|
43
|
+
fix: () => import("./chunks/fix-C9roGiQz.mjs"),
|
|
44
44
|
ship: () => import("./chunks/ship-BizfUe62.mjs"),
|
|
45
45
|
auth: () => import("./chunks/auth-Dnlksuo5.mjs"),
|
|
46
46
|
providers: () => import("./chunks/providers-Dx78imKj.mjs"),
|
|
@@ -53,7 +53,7 @@ const COMMAND_MODULES = Object.freeze({
|
|
|
53
53
|
policy: () => import("./chunks/policy-Du7WVBFX.mjs"),
|
|
54
54
|
gc: () => import("./chunks/gc-BBXlBF5w.mjs"),
|
|
55
55
|
update: () => import("./chunks/update-BXIppsbi.mjs"),
|
|
56
|
-
brainstorm: () => import("./chunks/brainstorm-
|
|
56
|
+
brainstorm: () => import("./chunks/brainstorm-SJwOIEif.mjs"),
|
|
57
57
|
patch: () => import("./chunks/patch-sAgVqjpx.mjs"),
|
|
58
58
|
"run-tool": () => import("./chunks/run-tool-C3rAv4XF.mjs"),
|
|
59
59
|
send: () => import("./chunks/send-BdnJ85Nz.mjs"),
|
|
@@ -80,7 +80,7 @@ async function loadCliContext(inputs) {
|
|
|
80
80
|
//#region src/cli.ts
|
|
81
81
|
/** Mirrors `apps/cli/package.json` "version"; `apps/cli/test/registry/version.test.ts` reads that file and fails
|
|
82
82
|
* if the two ever drift (a literal keeps `--version` free of any file read on the one-shot start-up path). */
|
|
83
|
-
const COHORTE_VERSION = "3.0.0-dev.
|
|
83
|
+
const COHORTE_VERSION = "3.0.0-dev.8";
|
|
84
84
|
function printError(deps, info) {
|
|
85
85
|
deps.stderr.write(`error: ${info.code}: ${info.message}\n`);
|
|
86
86
|
deps.stderr.write(`impact: ${info.impact}\n`);
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":[],"sources":["../../../apps/cli/src/lazy.ts","../../../apps/cli/src/cli.ts"],"sourcesContent":["// apps/cli/src/lazy.ts — the ONLY `import(` site of `apps/cli/src` (DESIGN 1.2 net 3 rule g, DESIGN 1.3, DESIGN\n// 6.3 §\"read-only verbs never load core/runtime\"). `check-layers` refuses `import(` anywhere else under\n// `apps/cli/src/**` (rule g is per FILE, not per occurrence), so a verb module that needs `compose`/`host`/etc.\n// reaches them only through here, which keeps `cli.ts` (and every module reachable from it without going through\n// `lazy.ts`) inside François' 10 s / 4 MiB one-shot budget for a verb that never needs the heavy ports.\nimport type { CliContext, CommandModule } from './contract/index.ts';\n\n/** What `compose/index.ts` (U4.01) exports: assembles the real `CliContext` from process-level inputs. */\nexport interface ComposeInputs {\n readonly cwd: string;\n readonly env: NodeJS.ProcessEnv;\n readonly stdio: {\n readonly stdout: NodeJS.WritableStream;\n readonly stderr: NodeJS.WritableStream;\n readonly stdin: NodeJS.ReadableStream;\n };\n}\n\n/**\n * One LITERAL specifier per verb, closed over a thunk. A computed specifier (`` import(`./commands/${verb}/…`) ``)\n * is invisible to every bundler: rolldown would leave the template in `dist/cli.mjs` and emit no verb chunk, so the\n * SHIPPED cli could not execute a single verb (it would try to load a `.ts` file next to `dist/`). Written out, each\n * specifier is statically analysable: rolldown code-splits one chunk per verb, laziness is preserved, and\n * `apps/cli/test/registry/lazy.test.ts` pins this map against `VERBS` in both directions so a verb added to the\n * registry without a loader — or the reverse — fails Wave 0's own check.\n */\nexport const COMMAND_MODULES: Readonly<Record<string, () => Promise<unknown>>> = Object.freeze({\n init: () => import('./commands/init/index.ts'),\n doctor: () => import('./commands/doctor/index.ts'),\n discover: () => import('./commands/discover/index.ts'),\n run: () => import('./commands/run/index.ts'),\n loop: () => import('./commands/loop/index.ts'),\n build: () => import('./commands/build/index.ts'),\n intake: () => import('./commands/intake/index.ts'),\n audit: () => import('./commands/audit/index.ts'),\n refactor: () => import('./commands/refactor/index.ts'),\n fleet: () => import('./commands/fleet/index.ts'),\n retro: () => import('./commands/retro/index.ts'),\n 'align-ds': () => import('./commands/align-ds/index.ts'),\n 'update-pipeline': () => import('./commands/update-pipeline/index.ts'),\n status: () => import('./commands/status/index.ts'),\n inspect: () => import('./commands/inspect/index.ts'),\n resume: () => import('./commands/resume/index.ts'),\n pause: () => import('./commands/pause/index.ts'),\n cancel: () => import('./commands/cancel/index.ts'),\n shutdown: () => import('./commands/shutdown/index.ts'),\n approve: () => import('./commands/approve/index.ts'),\n deny: () => import('./commands/deny/index.ts'),\n retry: () => import('./commands/retry/index.ts'),\n skip: () => import('./commands/skip/index.ts'),\n logs: () => import('./commands/logs/index.ts'),\n tail: () => import('./commands/tail/index.ts'),\n diff: () => import('./commands/diff/index.ts'),\n review: () => import('./commands/review/index.ts'),\n fix: () => import('./commands/fix/index.ts'),\n ship: () => import('./commands/ship/index.ts'),\n auth: () => import('./commands/auth/index.ts'),\n providers: () => import('./commands/providers/index.ts'),\n models: () => import('./commands/models/index.ts'),\n config: () => import('./commands/config/index.ts'),\n migrate: () => import('./commands/migrate/index.ts'),\n reconcile: () => import('./commands/reconcile/index.ts'),\n spec: () => import('./commands/spec/index.ts'),\n obsidian: () => import('./commands/obsidian/index.ts'),\n policy: () => import('./commands/policy/index.ts'),\n gc: () => import('./commands/gc/index.ts'),\n update: () => import('./commands/update/index.ts'),\n brainstorm: () => import('./commands/brainstorm/index.ts'),\n patch: () => import('./commands/patch/index.ts'),\n 'run-tool': () => import('./commands/run-tool/index.ts'),\n send: () => import('./commands/send/index.ts'),\n __host: () => import('./commands/__host/index.ts'),\n});\n\n/** Loads `commands/<verb>/index.ts` and returns its default export. */\nexport async function loadCommandModule(verb: string): Promise<CommandModule> {\n const load = COMMAND_MODULES[verb];\n if (!load) throw new RangeError(`lazy.ts: no command module registered for verb \"${verb}\"`);\n const loaded = await load();\n const mod = loaded as { default?: CommandModule };\n if (!mod.default || typeof mod.default.run !== 'function') {\n throw new TypeError(`lazy.ts: commands/${verb}/index.ts has no default CommandModule export`);\n }\n return mod.default;\n}\n\n/**\n * The one `import(` of `./compose/index.ts` (DESIGN 1.2 net 3 rule g): `cli.ts` reaches the real `CliContext`\n * only through here, so a read-only verb whose action never runs never pays for it either (DESIGN 1.3).\n */\nexport async function loadCliContext(inputs: ComposeInputs): Promise<CliContext> {\n const loaded: unknown = await import('./compose/index.ts');\n const mod = loaded as { composeCliContext?: (inputs: ComposeInputs) => Promise<CliContext> };\n if (typeof mod.composeCliContext !== 'function') {\n throw new TypeError('lazy.ts: compose/index.ts has no composeCliContext export');\n }\n return mod.composeCliContext(inputs);\n}\n","#!/usr/bin/env node\n// apps/cli/src/cli.ts — DESIGN §9 CLI row: the whole verb registry, built from `contract/verbs.ts` and nothing\n// else (PLAN §3 rule 3 \"cli.ts pre-registers every verb against commands/<verb>/index.ts; ... nobody edits the\n// registry\"). `cli.ts` never imports `core`/`runtime-*`/`security`/`persistence` directly: a verb's module, and the\n// real `CliContext`, are reached only through `lazy.ts`'s single `import()` site (DESIGN 1.2 net 3 rule g, DESIGN\n// 1.3 \"read-only verbs never load core/runtime\") — so `--help` and `--version`, which commander answers itself\n// without ever calling `dispatch`, pay nothing beyond this file and `commander`.\nimport { toErrorInfo } from '@cohorte/base';\nimport { Command, CommanderError } from 'commander';\nimport {\n type CliContext,\n CONTROLLER_EXIT_CODES,\n type CommandArgs,\n EXIT_CODE_BY_CLASS,\n formatExitCodesHelp,\n VERBS,\n type VerbSpec,\n} from './contract/index.ts';\nimport { loadCliContext, loadCommandModule } from './lazy.ts';\n\n/** Mirrors `apps/cli/package.json` \"version\"; `apps/cli/test/registry/version.test.ts` reads that file and fails\n * if the two ever drift (a literal keeps `--version` free of any file read on the one-shot start-up path). */\nexport const COHORTE_VERSION = '3.0.0-dev.7';\n\nexport interface RuntimeDeps {\n readonly stdout: NodeJS.WritableStream;\n readonly stderr: NodeJS.WritableStream;\n /** Built lazily: only a verb whose action actually runs calls this (never `--help`/`--version`, DESIGN 1.3). */\n readonly context: () => Promise<CliContext>;\n}\n\nfunction printError(deps: RuntimeDeps, info: ReturnType<typeof toErrorInfo>): void {\n // spec 21: \"cause / impact / run / next action / exit code\"\n deps.stderr.write(`error: ${info.code}: ${info.message}\\n`);\n deps.stderr.write(`impact: ${info.impact}\\n`);\n deps.stderr.write(`next action: ${info.remediation}\\n`);\n}\n\n/** Loads the verb's module, builds its `CliContext` and runs it, converting any throw at this one boundary\n * (DESIGN 2.8: \"Every error has ... an exit code\"; the module itself never writes to stdio on error). */\nasync function dispatch(deps: RuntimeDeps, verb: string, args: CommandArgs): Promise<number> {\n try {\n const mod = await loadCommandModule(verb);\n const ctx = await deps.context();\n return await mod.run(ctx, args);\n } catch (error) {\n // DESIGN 2.8: \"Unknown throwables -> `<nearest class>/unexpected`\". A `CohorteError` keeps its own code through\n // `toErrorInfo`, so the Wave-0 stubs still answer `configuration/phase-not-available`; anything else — a\n // TypeError in a filled-in verb, a failed store open, a module that will not load — is reported as what it is.\n const info = toErrorInfo(error, { code: 'configuration/unexpected', class: 'configuration' });\n printError(deps, info);\n return EXIT_CODE_BY_CLASS[info.class];\n }\n}\n\ninterface ExitBox {\n code: number;\n}\n\nfunction addAction(command: Command, deps: RuntimeDeps, exit: ExitBox, verb: VerbSpec, subVerb?: string): void {\n command.allowUnknownOption(true).allowExcessArguments(true);\n // `--json` only where DESIGN 2.3.5 gives the verb a published document (`JSON_OUTPUTS`): registering it on every\n // verb would promise a machine output that no schema backs, and the Wave-4 `--json validates` tests read that map.\n if (verb.json) command.option('--json', 'print the machine-readable document for this verb (DESIGN 2.3.5)');\n command.argument('[args...]', 'verb arguments').action(async (positionals: string[], options: { json?: boolean }) => {\n const args: CommandArgs = {\n positionals,\n options,\n json: Boolean(options.json),\n ...(subVerb === undefined ? {} : { subVerb }),\n };\n exit.code = await dispatch(deps, verb.name, args);\n });\n}\n\n/** Builds the whole commander tree. Exported so a test can assert on `--help` / registration without spawning a\n * process (PLAN U0.10 test list: \"every DESIGN §9 verb is registered exactly once and appears in --help\"). */\nexport function buildProgram(deps: RuntimeDeps, exit: ExitBox): Command {\n const program = new Command('cohorte')\n .description('Cohorte: a durable multi-agent development pipeline with a runtime-independent orchestrator.')\n .version(COHORTE_VERSION, '--version', 'print the Cohorte version')\n .exitOverride()\n .configureOutput({\n writeOut: (str) => deps.stdout.write(str),\n writeErr: (str) => deps.stderr.write(str),\n })\n .addHelpText('after', `\\n${formatExitCodesHelp()}\\n`);\n\n for (const verb of VERBS) {\n const sub = program.command(verb.name, verb.hidden ? { hidden: true } : undefined).description(verb.summary);\n if (verb.subVerbs && verb.subVerbs.length > 0) {\n addAction(sub, deps, exit, verb); // bare `cohorte <verb>` with no sub-verb\n for (const subVerb of verb.subVerbs) {\n addAction(sub.command(subVerb), deps, exit, verb, subVerb);\n }\n } else {\n addAction(sub, deps, exit, verb);\n }\n }\n return program;\n}\n\n/**\n * `CONTROLLER_EXIT_CODES.usage` (2) for anything commander itself refuses (bad flag, unknown command, no command at\n * all), `completed` (0) for the routes where commander did exactly what was asked and then stopped the parse.\n *\n * Keyed off commander's OWN `exitCode`, not off the code NAME: commander raises `commander.help` both for the\n * advertised `help [command]` route (which prints the help and means exit 0) and for a bare `cohorte` with no\n * command (exit 1 there — a usage error), and an allow-list of names got the first case wrong while `--help`,\n * raising `commander.helpDisplayed`, was right. `error.exitCode` already separates the two.\n */\nfunction commanderExitCode(error: CommanderError): number {\n return error.exitCode === 0 ? CONTROLLER_EXIT_CODES.completed : CONTROLLER_EXIT_CODES.usage;\n}\n\n/** Parses `argv` (no leading node/script entries) and runs the matched verb. Never calls `process.exit`: the\n * caller (`main()` below, or a test) owns that decision. */\nexport async function runCli(argv: readonly string[], deps: RuntimeDeps): Promise<number> {\n const exit: ExitBox = { code: CONTROLLER_EXIT_CODES.completed };\n const program = buildProgram(deps, exit);\n try {\n await program.parseAsync(argv, { from: 'user' });\n } catch (error) {\n if (error instanceof CommanderError) return commanderExitCode(error);\n throw error;\n }\n return exit.code;\n}\n\nasync function realContext(): Promise<CliContext> {\n return loadCliContext({\n cwd: process.cwd(),\n env: process.env,\n stdio: { stdout: process.stdout, stderr: process.stderr, stdin: process.stdin },\n });\n}\n\nasync function main(): Promise<void> {\n const deps: RuntimeDeps = { stdout: process.stdout, stderr: process.stderr, context: realContext };\n const code = await runCli(process.argv.slice(2), deps);\n process.exitCode = code;\n}\n\n// `import.meta.main` (node >= 24.2; `apps/cli` engines are `^24.16.0 || >=26.1.0`) and NOT a hand-rolled\n// `import.meta.url === \\`file://${process.argv[1]}\\``: npm installs `bin` as a SYMLINK\n// (`node_modules/.bin/cohorte` -> `../cohorte/dist/cli.mjs`), so the comparison is false for every real install and\n// the published CLI would print nothing and exit 0. It also breaks on any install path with a space or a non-ASCII\n// character (`import.meta.url` is percent-encoded, `argv[1]` is not).\nif (import.meta.main) {\n main().catch((error: unknown) => {\n process.stderr.write(`fatal: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}\\n`);\n process.exitCode = 1;\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":[],"sources":["../../../apps/cli/src/lazy.ts","../../../apps/cli/src/cli.ts"],"sourcesContent":["// apps/cli/src/lazy.ts — the ONLY `import(` site of `apps/cli/src` (DESIGN 1.2 net 3 rule g, DESIGN 1.3, DESIGN\n// 6.3 §\"read-only verbs never load core/runtime\"). `check-layers` refuses `import(` anywhere else under\n// `apps/cli/src/**` (rule g is per FILE, not per occurrence), so a verb module that needs `compose`/`host`/etc.\n// reaches them only through here, which keeps `cli.ts` (and every module reachable from it without going through\n// `lazy.ts`) inside François' 10 s / 4 MiB one-shot budget for a verb that never needs the heavy ports.\nimport type { CliContext, CommandModule } from './contract/index.ts';\n\n/** What `compose/index.ts` (U4.01) exports: assembles the real `CliContext` from process-level inputs. */\nexport interface ComposeInputs {\n readonly cwd: string;\n readonly env: NodeJS.ProcessEnv;\n readonly stdio: {\n readonly stdout: NodeJS.WritableStream;\n readonly stderr: NodeJS.WritableStream;\n readonly stdin: NodeJS.ReadableStream;\n };\n}\n\n/**\n * One LITERAL specifier per verb, closed over a thunk. A computed specifier (`` import(`./commands/${verb}/…`) ``)\n * is invisible to every bundler: rolldown would leave the template in `dist/cli.mjs` and emit no verb chunk, so the\n * SHIPPED cli could not execute a single verb (it would try to load a `.ts` file next to `dist/`). Written out, each\n * specifier is statically analysable: rolldown code-splits one chunk per verb, laziness is preserved, and\n * `apps/cli/test/registry/lazy.test.ts` pins this map against `VERBS` in both directions so a verb added to the\n * registry without a loader — or the reverse — fails Wave 0's own check.\n */\nexport const COMMAND_MODULES: Readonly<Record<string, () => Promise<unknown>>> = Object.freeze({\n init: () => import('./commands/init/index.ts'),\n doctor: () => import('./commands/doctor/index.ts'),\n discover: () => import('./commands/discover/index.ts'),\n run: () => import('./commands/run/index.ts'),\n loop: () => import('./commands/loop/index.ts'),\n build: () => import('./commands/build/index.ts'),\n intake: () => import('./commands/intake/index.ts'),\n audit: () => import('./commands/audit/index.ts'),\n refactor: () => import('./commands/refactor/index.ts'),\n fleet: () => import('./commands/fleet/index.ts'),\n retro: () => import('./commands/retro/index.ts'),\n 'align-ds': () => import('./commands/align-ds/index.ts'),\n 'update-pipeline': () => import('./commands/update-pipeline/index.ts'),\n status: () => import('./commands/status/index.ts'),\n inspect: () => import('./commands/inspect/index.ts'),\n resume: () => import('./commands/resume/index.ts'),\n pause: () => import('./commands/pause/index.ts'),\n cancel: () => import('./commands/cancel/index.ts'),\n shutdown: () => import('./commands/shutdown/index.ts'),\n approve: () => import('./commands/approve/index.ts'),\n deny: () => import('./commands/deny/index.ts'),\n retry: () => import('./commands/retry/index.ts'),\n skip: () => import('./commands/skip/index.ts'),\n logs: () => import('./commands/logs/index.ts'),\n tail: () => import('./commands/tail/index.ts'),\n diff: () => import('./commands/diff/index.ts'),\n review: () => import('./commands/review/index.ts'),\n fix: () => import('./commands/fix/index.ts'),\n ship: () => import('./commands/ship/index.ts'),\n auth: () => import('./commands/auth/index.ts'),\n providers: () => import('./commands/providers/index.ts'),\n models: () => import('./commands/models/index.ts'),\n config: () => import('./commands/config/index.ts'),\n migrate: () => import('./commands/migrate/index.ts'),\n reconcile: () => import('./commands/reconcile/index.ts'),\n spec: () => import('./commands/spec/index.ts'),\n obsidian: () => import('./commands/obsidian/index.ts'),\n policy: () => import('./commands/policy/index.ts'),\n gc: () => import('./commands/gc/index.ts'),\n update: () => import('./commands/update/index.ts'),\n brainstorm: () => import('./commands/brainstorm/index.ts'),\n patch: () => import('./commands/patch/index.ts'),\n 'run-tool': () => import('./commands/run-tool/index.ts'),\n send: () => import('./commands/send/index.ts'),\n __host: () => import('./commands/__host/index.ts'),\n});\n\n/** Loads `commands/<verb>/index.ts` and returns its default export. */\nexport async function loadCommandModule(verb: string): Promise<CommandModule> {\n const load = COMMAND_MODULES[verb];\n if (!load) throw new RangeError(`lazy.ts: no command module registered for verb \"${verb}\"`);\n const loaded = await load();\n const mod = loaded as { default?: CommandModule };\n if (!mod.default || typeof mod.default.run !== 'function') {\n throw new TypeError(`lazy.ts: commands/${verb}/index.ts has no default CommandModule export`);\n }\n return mod.default;\n}\n\n/**\n * The one `import(` of `./compose/index.ts` (DESIGN 1.2 net 3 rule g): `cli.ts` reaches the real `CliContext`\n * only through here, so a read-only verb whose action never runs never pays for it either (DESIGN 1.3).\n */\nexport async function loadCliContext(inputs: ComposeInputs): Promise<CliContext> {\n const loaded: unknown = await import('./compose/index.ts');\n const mod = loaded as { composeCliContext?: (inputs: ComposeInputs) => Promise<CliContext> };\n if (typeof mod.composeCliContext !== 'function') {\n throw new TypeError('lazy.ts: compose/index.ts has no composeCliContext export');\n }\n return mod.composeCliContext(inputs);\n}\n","#!/usr/bin/env node\n// apps/cli/src/cli.ts — DESIGN §9 CLI row: the whole verb registry, built from `contract/verbs.ts` and nothing\n// else (PLAN §3 rule 3 \"cli.ts pre-registers every verb against commands/<verb>/index.ts; ... nobody edits the\n// registry\"). `cli.ts` never imports `core`/`runtime-*`/`security`/`persistence` directly: a verb's module, and the\n// real `CliContext`, are reached only through `lazy.ts`'s single `import()` site (DESIGN 1.2 net 3 rule g, DESIGN\n// 1.3 \"read-only verbs never load core/runtime\") — so `--help` and `--version`, which commander answers itself\n// without ever calling `dispatch`, pay nothing beyond this file and `commander`.\nimport { toErrorInfo } from '@cohorte/base';\nimport { Command, CommanderError } from 'commander';\nimport {\n type CliContext,\n CONTROLLER_EXIT_CODES,\n type CommandArgs,\n EXIT_CODE_BY_CLASS,\n formatExitCodesHelp,\n VERBS,\n type VerbSpec,\n} from './contract/index.ts';\nimport { loadCliContext, loadCommandModule } from './lazy.ts';\n\n/** Mirrors `apps/cli/package.json` \"version\"; `apps/cli/test/registry/version.test.ts` reads that file and fails\n * if the two ever drift (a literal keeps `--version` free of any file read on the one-shot start-up path). */\nexport const COHORTE_VERSION = '3.0.0-dev.8';\n\nexport interface RuntimeDeps {\n readonly stdout: NodeJS.WritableStream;\n readonly stderr: NodeJS.WritableStream;\n /** Built lazily: only a verb whose action actually runs calls this (never `--help`/`--version`, DESIGN 1.3). */\n readonly context: () => Promise<CliContext>;\n}\n\nfunction printError(deps: RuntimeDeps, info: ReturnType<typeof toErrorInfo>): void {\n // spec 21: \"cause / impact / run / next action / exit code\"\n deps.stderr.write(`error: ${info.code}: ${info.message}\\n`);\n deps.stderr.write(`impact: ${info.impact}\\n`);\n deps.stderr.write(`next action: ${info.remediation}\\n`);\n}\n\n/** Loads the verb's module, builds its `CliContext` and runs it, converting any throw at this one boundary\n * (DESIGN 2.8: \"Every error has ... an exit code\"; the module itself never writes to stdio on error). */\nasync function dispatch(deps: RuntimeDeps, verb: string, args: CommandArgs): Promise<number> {\n try {\n const mod = await loadCommandModule(verb);\n const ctx = await deps.context();\n return await mod.run(ctx, args);\n } catch (error) {\n // DESIGN 2.8: \"Unknown throwables -> `<nearest class>/unexpected`\". A `CohorteError` keeps its own code through\n // `toErrorInfo`, so the Wave-0 stubs still answer `configuration/phase-not-available`; anything else — a\n // TypeError in a filled-in verb, a failed store open, a module that will not load — is reported as what it is.\n const info = toErrorInfo(error, { code: 'configuration/unexpected', class: 'configuration' });\n printError(deps, info);\n return EXIT_CODE_BY_CLASS[info.class];\n }\n}\n\ninterface ExitBox {\n code: number;\n}\n\nfunction addAction(command: Command, deps: RuntimeDeps, exit: ExitBox, verb: VerbSpec, subVerb?: string): void {\n command.allowUnknownOption(true).allowExcessArguments(true);\n // `--json` only where DESIGN 2.3.5 gives the verb a published document (`JSON_OUTPUTS`): registering it on every\n // verb would promise a machine output that no schema backs, and the Wave-4 `--json validates` tests read that map.\n if (verb.json) command.option('--json', 'print the machine-readable document for this verb (DESIGN 2.3.5)');\n command.argument('[args...]', 'verb arguments').action(async (positionals: string[], options: { json?: boolean }) => {\n const args: CommandArgs = {\n positionals,\n options,\n json: Boolean(options.json),\n ...(subVerb === undefined ? {} : { subVerb }),\n };\n exit.code = await dispatch(deps, verb.name, args);\n });\n}\n\n/** Builds the whole commander tree. Exported so a test can assert on `--help` / registration without spawning a\n * process (PLAN U0.10 test list: \"every DESIGN §9 verb is registered exactly once and appears in --help\"). */\nexport function buildProgram(deps: RuntimeDeps, exit: ExitBox): Command {\n const program = new Command('cohorte')\n .description('Cohorte: a durable multi-agent development pipeline with a runtime-independent orchestrator.')\n .version(COHORTE_VERSION, '--version', 'print the Cohorte version')\n .exitOverride()\n .configureOutput({\n writeOut: (str) => deps.stdout.write(str),\n writeErr: (str) => deps.stderr.write(str),\n })\n .addHelpText('after', `\\n${formatExitCodesHelp()}\\n`);\n\n for (const verb of VERBS) {\n const sub = program.command(verb.name, verb.hidden ? { hidden: true } : undefined).description(verb.summary);\n if (verb.subVerbs && verb.subVerbs.length > 0) {\n addAction(sub, deps, exit, verb); // bare `cohorte <verb>` with no sub-verb\n for (const subVerb of verb.subVerbs) {\n addAction(sub.command(subVerb), deps, exit, verb, subVerb);\n }\n } else {\n addAction(sub, deps, exit, verb);\n }\n }\n return program;\n}\n\n/**\n * `CONTROLLER_EXIT_CODES.usage` (2) for anything commander itself refuses (bad flag, unknown command, no command at\n * all), `completed` (0) for the routes where commander did exactly what was asked and then stopped the parse.\n *\n * Keyed off commander's OWN `exitCode`, not off the code NAME: commander raises `commander.help` both for the\n * advertised `help [command]` route (which prints the help and means exit 0) and for a bare `cohorte` with no\n * command (exit 1 there — a usage error), and an allow-list of names got the first case wrong while `--help`,\n * raising `commander.helpDisplayed`, was right. `error.exitCode` already separates the two.\n */\nfunction commanderExitCode(error: CommanderError): number {\n return error.exitCode === 0 ? CONTROLLER_EXIT_CODES.completed : CONTROLLER_EXIT_CODES.usage;\n}\n\n/** Parses `argv` (no leading node/script entries) and runs the matched verb. Never calls `process.exit`: the\n * caller (`main()` below, or a test) owns that decision. */\nexport async function runCli(argv: readonly string[], deps: RuntimeDeps): Promise<number> {\n const exit: ExitBox = { code: CONTROLLER_EXIT_CODES.completed };\n const program = buildProgram(deps, exit);\n try {\n await program.parseAsync(argv, { from: 'user' });\n } catch (error) {\n if (error instanceof CommanderError) return commanderExitCode(error);\n throw error;\n }\n return exit.code;\n}\n\nasync function realContext(): Promise<CliContext> {\n return loadCliContext({\n cwd: process.cwd(),\n env: process.env,\n stdio: { stdout: process.stdout, stderr: process.stderr, stdin: process.stdin },\n });\n}\n\nasync function main(): Promise<void> {\n const deps: RuntimeDeps = { stdout: process.stdout, stderr: process.stderr, context: realContext };\n const code = await runCli(process.argv.slice(2), deps);\n process.exitCode = code;\n}\n\n// `import.meta.main` (node >= 24.2; `apps/cli` engines are `^24.16.0 || >=26.1.0`) and NOT a hand-rolled\n// `import.meta.url === \\`file://${process.argv[1]}\\``: npm installs `bin` as a SYMLINK\n// (`node_modules/.bin/cohorte` -> `../cohorte/dist/cli.mjs`), so the comparison is false for every real install and\n// the published CLI would print nothing and exit 0. It also breaks on any install path with a space or a non-ASCII\n// character (`import.meta.url` is percent-encoded, `argv[1]` is not).\nif (import.meta.main) {\n main().catch((error: unknown) => {\n process.stderr.write(`fatal: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}\\n`);\n process.exitCode = 1;\n });\n}\n"],"mappings":"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"}
|