yadflow 2.2.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -5
- package/README.md +75 -22
- package/cli/doctor.mjs +45 -3
- package/cli/epic-state.mjs +19 -2
- package/cli/errors.mjs +2 -0
- package/cli/manifest.mjs +23 -1
- package/cli/setup.mjs +109 -10
- package/docs/index.html +62 -11
- package/package.json +5 -3
- package/skills/sdlc/config.yaml +41 -4
- package/skills/sdlc/install.sh +1 -1
- package/skills/sdlc/module-help.csv +6 -1
- package/skills/yad-analysis/SKILL.md +10 -5
- package/skills/yad-connect-design/SKILL.md +1 -1
- package/skills/yad-connect-design/references/design-registry.md +4 -2
- package/skills/yad-connect-learning/SKILL.md +140 -0
- package/skills/yad-connect-learning/references/learning-context.md +79 -0
- package/skills/yad-connect-learning/references/learning-registry.md +60 -0
- package/skills/yad-connect-testing/SKILL.md +121 -0
- package/skills/yad-connect-testing/references/testing-context.md +67 -0
- package/skills/yad-connect-testing/references/testing-registry.md +55 -0
- package/skills/yad-epic/SKILL.md +10 -5
- package/skills/yad-epic/references/state-schema.md +42 -11
- package/skills/yad-hub-bridge/SKILL.md +2 -2
- package/skills/yad-learn/SKILL.md +146 -0
- package/skills/yad-learn/references/learning-state.md +75 -0
- package/skills/yad-review-gate/SKILL.md +14 -11
- package/skills/yad-review-gate/references/gating.md +1 -1
- package/skills/yad-run/references/run-loop.md +3 -3
- package/skills/yad-spec/SKILL.md +3 -1
- package/skills/yad-status/SKILL.md +35 -15
- package/skills/yad-stories/SKILL.md +3 -1
- package/skills/yad-test-cases/SKILL.md +173 -0
- package/skills/yad-test-cases/references/test-cases-schema.md +70 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,16 +1,27 @@
|
|
|
1
|
-
# [2.
|
|
1
|
+
# [2.4.0](https://github.com/abdelrahmannasr/yadflow/compare/v2.3.0...v2.4.0) (2026-06-13)
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
### Bug Fixes
|
|
5
5
|
|
|
6
|
-
* address
|
|
6
|
+
* address CodeRabbit review on PR [#48](https://github.com/abdelrahmannasr/yadflow/issues/48) ([2f182f7](https://github.com/abdelrahmannasr/yadflow/commit/2f182f72b68e226196b6190802771b0e12b585f9))
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
### Features
|
|
10
10
|
|
|
11
|
-
* add
|
|
12
|
-
*
|
|
13
|
-
|
|
11
|
+
* add DeepTutor learning layer across all SDLC stages ([bd8d4ea](https://github.com/abdelrahmannasr/yadflow/commit/bd8d4eaaa0258242a62ed1b131f7e3f74506af64))
|
|
12
|
+
* make learning-layer output local-only (never committed or pushed) ([aa8f74e](https://github.com/abdelrahmannasr/yadflow/commit/aa8f74eb61855d3a663810a0c68cf8e37fbedd66))
|
|
13
|
+
|
|
14
|
+
# [2.2.0](https://github.com/abdelrahmannasr/yadflow/compare/v2.1.0...v2.2.0) (2026-06-14)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* add parallel test-cases step with pluggable testing-tool connection ([#45](https://github.com/abdelrahmannasr/yadflow/issues/45)) ([19c282f](https://github.com/abdelrahmannasr/yadflow/commit/19c282f6bd737364bca122179b05de8ea94493a9))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Continuous Integration
|
|
23
|
+
|
|
24
|
+
* wire the hub's gate-sync + verified-commits CI and stamp the CLI version ([#46](https://github.com/abdelrahmannasr/yadflow/issues/46)) ([c856398](https://github.com/abdelrahmannasr/yadflow/commit/c856398a213b17aebea9c46204dbf955b92ea9cf))
|
|
14
25
|
|
|
15
26
|
# [1.1.0](https://github.com/abdelrahmannasr/sdlc-workflow/compare/v1.0.3...v1.1.0) (2026-06-09)
|
|
16
27
|
|
package/README.md
CHANGED
|
@@ -49,8 +49,11 @@ human**. Detailed walkthroughs for each phase follow below.
|
|
|
49
49
|
| `skills/yad-architecture/` | Front state 3: author `architecture.md` + the locked `contract.md`; hash-lock the contract surface. |
|
|
50
50
|
| `skills/yad-ui/` | Front state 5: author `ui-design.md` + `DESIGN.md` (Impeccable slash-commands, or graceful fallback). |
|
|
51
51
|
| `skills/yad-stories/` | Front state 7: break the epic into repo-tagged stories with stable `EP-<slug>-S0N` IDs. |
|
|
52
|
+
| `skills/yad-test-cases/` | Front state 9: with the test architect author `test-cases.md`; implement the automation in the connected testing tool, or produce artifacts only. |
|
|
52
53
|
| `skills/yad-connect-repos/` | Connect code repos to the hub (GitHub/GitLab, local-user auth); cache a Repomix pack + **code-map** per repo so the front phases are code-aware. |
|
|
53
|
-
| `skills/yad-
|
|
54
|
+
| `skills/yad-connect-learning/` | Connect a learning tool (DeepTutor-first, pluggable) — a CLI subprocess like Repomix; record `.sdlc/learning.json` + an optional grounded knowledge base. |
|
|
55
|
+
| `skills/yad-learn/` | The cross-cutting **learning layer**: tutor any member, at any stage, in the context of what's being built; records a personal, local-only skills log (gitignored, never committed/pushed). Opt-in, never gates. |
|
|
56
|
+
| `skills/yad-review-gate/` | The reusable **team review + approve gate** (used for all five reviews). |
|
|
54
57
|
| `skills/yad-spec/` | Build Step A: run the Spec Kit ceremony once per story per repo → `specs/<story-id>/`. |
|
|
55
58
|
| `skills/yad-implement/` | Build Step B: implement ONE atomic task as a small diff on its own branch. |
|
|
56
59
|
| `skills/yad-checks/` | Build Step C: wire + run the CI gates (spec-link, contract-check, build/test/lint, verified-commits). |
|
|
@@ -122,18 +125,24 @@ a manual `yad gate sync` racing CI, or GitLab pipelines — two simultaneous syn
|
|
|
122
125
|
*commits* via the rebase retry but each works from the state it read at start, so the rarer of two
|
|
123
126
|
simultaneous advancements can be lost; the next event or scheduled sweep re-syncs and converges.
|
|
124
127
|
|
|
125
|
-
### What `setup` walks you through (
|
|
128
|
+
### What `setup` walks you through (10 steps)
|
|
126
129
|
|
|
127
130
|
1. **Preflight** — confirm the hub is a git repo (offers `git init`); check `git`/`node`/`npx`.
|
|
128
|
-
2. **Install the module** — copy all
|
|
131
|
+
2. **Install the module** — copy all 22 `yad-*` skills into the IDE skill dirs you pick
|
|
129
132
|
(`.claude/`, `.agents/`, `.zencoder/`, `.opencode/`) and register `_bmad/sdlc/`.
|
|
130
133
|
3. **Hub platform & roster** — detect GitHub/GitLab from the remote; record reviewers → `.sdlc/hub.json`.
|
|
131
134
|
4. **Connect a design tool** — record the design tool (Figma / pencil / none) → `.sdlc/design.json` so
|
|
132
135
|
the UI step can materialize the design; the MCP itself is confirmed later by `yad-connect-design`.
|
|
133
|
-
5. **Connect
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
136
|
+
5. **Connect a testing tool** — record the testing tool (Playwright / cypress / pytest / none) →
|
|
137
|
+
`.sdlc/testing.json` so the test-cases step can implement the automation; the MCP itself is confirmed
|
|
138
|
+
later by `yad-connect-testing`.
|
|
139
|
+
6. **Connect a learning tool** — record the learning tool (DeepTutor / none) → `.sdlc/learning.json` so
|
|
140
|
+
the learning layer can tutor the team; the CLI + knowledge base are confirmed later by
|
|
141
|
+
`yad-connect-learning`.
|
|
142
|
+
7. **Connect code repos** — register each repo into `.sdlc/repos.json` and cache a Repomix pack.
|
|
143
|
+
8. **Wire each repo** — CI gates, PR/MR template, and review-comment scaffold.
|
|
144
|
+
9. **AI review** — optionally write `.coderabbit.yaml`.
|
|
145
|
+
10. **Done** — stamp `.sdlc/cli-version.json` and hand off the AI-only steps (code-maps; first epic).
|
|
137
146
|
|
|
138
147
|
The deterministic file work runs automatically; the AI-only steps are handed to the Claude Code skills
|
|
139
148
|
with a printed next-action. Re-run `… check --fix` any time the workflow updates — it never re-asks for
|
|
@@ -170,10 +179,12 @@ with a fix-it hint per finding. Failures carry stable, greppable codes, also pri
|
|
|
170
179
|
| `YAD-STATE-003` | a registered repo path is missing or not a git repo | fix the path in `.sdlc/repos.json` or re-connect the repo |
|
|
171
180
|
| `YAD-CFG-001` | `hub.json` names an unknown platform | expected `github`, `gitlab`, or `null` — fix it or re-run `yad setup` |
|
|
172
181
|
| `YAD-CFG-002` | `design.json` names an unknown design tool | expected one of `config.yaml` `design.tools` (e.g. `figma`, `pencil`), or `none` — fix it or re-run `yad setup` |
|
|
182
|
+
| `YAD-CFG-003` | `testing.json` names an unknown testing tool | expected one of `config.yaml` `testing.tools` (e.g. `playwright`, `cypress`, `pytest`), or `none` — fix it or re-run `yad setup` |
|
|
183
|
+
| `YAD-CFG-004` | `learning.json` names an unknown learning tool | expected one of `config.yaml` `learning.tools` (e.g. `deeptutor`), or `none` — fix it or re-run `yad setup` |
|
|
173
184
|
|
|
174
185
|
Filing a bug? Attach `yad doctor --json` — it contains no secrets (names, paths, and check results only).
|
|
175
186
|
|
|
176
|
-
## Agent skills (all
|
|
187
|
+
## Agent skills (all 22)
|
|
177
188
|
|
|
178
189
|
The CLI **installs and wires** the module; the skills below are the **agents you invoke by name** in your
|
|
179
190
|
AI IDE (e.g. *“run `yad-epic`”*) to actually do the work. State lives in files you can also edit
|
|
@@ -191,12 +202,36 @@ directly. Each skill stops at a gate and never auto-advances unless a step has *
|
|
|
191
202
|
Records the tool + project/file references in `.sdlc/design.json` (local-user / MCP-session auth, no
|
|
192
203
|
stored tokens), detecting the design-tool MCP and degrading to markdown-only when absent. Idempotent
|
|
193
204
|
and refreshable; one connection per project.
|
|
205
|
+
- **`yad-connect-testing`** — Connects a testing tool (Playwright-first, pluggable) so the test-cases
|
|
206
|
+
step can implement the actual automation tests inside it, alongside the Markdown. Records the tool +
|
|
207
|
+
project/suite references in `.sdlc/testing.json` (local-user / MCP-session auth, no stored tokens),
|
|
208
|
+
detecting the testing-tool MCP and degrading to artifacts-only when absent. Idempotent and
|
|
209
|
+
refreshable; one connection per project.
|
|
210
|
+
- **`yad-connect-learning`** — Connects a learning/tutoring tool (DeepTutor-first, pluggable) so the
|
|
211
|
+
cross-cutting learning layer can tutor any team member in the context of what's being built. Records
|
|
212
|
+
the tool + an optional grounded knowledge base in `.sdlc/learning.json` (local-user auth, no stored
|
|
213
|
+
tokens), detecting the **DeepTutor CLI on PATH** (a subprocess like Repomix — DeepTutor ships no MCP)
|
|
214
|
+
and degrading to **harness-native** tutoring when absent. Idempotent and refreshable; one connection
|
|
215
|
+
per project.
|
|
216
|
+
|
|
217
|
+
### The learning layer (cross-cutting — any member, any stage)
|
|
218
|
+
|
|
219
|
+
- **`yad-learn`** — At **any** SDLC stage, a team member can ask to learn a concept and be tutored *in
|
|
220
|
+
the context of what the team is building* — e.g. *"teach me why the architecture hash-locks the
|
|
221
|
+
contract surface"*. Routes the request to the connected learning tool (`.sdlc/learning.json`,
|
|
222
|
+
DeepTutor-first) grounded in the project knowledge base, or degrades to **harness-native** tutoring
|
|
223
|
+
(the harness model reading the artifacts) when nothing is connected — so it always works. Renders a
|
|
224
|
+
tutorial artifact and appends to a per-member **learning ledger** kept **local-only** (gitignored,
|
|
225
|
+
never committed or pushed — to the hub or any code repo) so it stays a private, personal **skills log**
|
|
226
|
+
(`yad-status` rolls up the local records). **Purely opt-in — it never blocks a gate** and
|
|
227
|
+
never touches epic state, approvals, or the contract lock. *AI builds, the hand decides* — and now the
|
|
228
|
+
hand can also learn, on demand, what it is deciding about.
|
|
194
229
|
|
|
195
230
|
### Front half — author the "thinking" (once per epic, human-gated)
|
|
196
231
|
|
|
197
232
|
- **`yad-analysis`** — *Optional* front state 1. With the analyst, pressure-test a feature idea
|
|
198
233
|
and write the discovery brief into `analysis.md`. Assigns the `EP-<slug>` ID and seeds `.sdlc/` state
|
|
199
|
-
(the
|
|
234
|
+
(the 12-step chain that puts analysis before epic). If skipped, the epic step does this shaping inline.
|
|
200
235
|
- **`yad-epic`** — The epic front state. Shape the idea with the analyst (or read `analysis.md`
|
|
201
236
|
when it already ran), then write the epic with the pm into `epic.md`. The entry point when analysis is
|
|
202
237
|
skipped: assigns the `EP-<slug>` ID and seeds `.sdlc/` state.
|
|
@@ -211,6 +246,13 @@ directly. Each skill stops at a gate and never auto-advances unless a step has *
|
|
|
211
246
|
- **`yad-stories`** — Front state 7. With the pm, break the approved epic into user stories, each
|
|
212
247
|
tagged with the repos that must implement it. Assigns zero-padded `EP-<slug>-S0N` IDs, one file per
|
|
213
248
|
story under `stories/`. Reads epic + architecture + contract + UI.
|
|
249
|
+
- **`yad-test-cases`** — Front state 9, a **parallel, non-blocking** track: it opens when the stories
|
|
250
|
+
gate passes (the epic is already `ready-for-build`, so the build half runs alongside it). With the
|
|
251
|
+
test architect (Murat), author `test-cases.md` covering the approved stories (risk-based P0–P3 cases +
|
|
252
|
+
story→case traceability). When a testing tool is connected (`yad-connect-testing`), also **implements
|
|
253
|
+
the automation tests** in the connected code repo(s) (generate or link), recording the case→test map in
|
|
254
|
+
`test-links.json`; degrades to artifacts-only otherwise. Reads epic + architecture + contract + UI +
|
|
255
|
+
stories.
|
|
214
256
|
|
|
215
257
|
### The review gate (cross-cutting — used by every review)
|
|
216
258
|
|
|
@@ -269,7 +311,7 @@ merging the approved, fully-resolved review PR — never on a machine.
|
|
|
269
311
|
|
|
270
312
|
As of **Phase 4a** the `automation` dial is no longer inert: the orchestrator `yad-run` reads it and,
|
|
271
313
|
for the safe **back** steps, advances on its own when a step is set to `machine_advance` (and has
|
|
272
|
-
*earned* it — see "Run the back half on the dial" below). The engineer review and all
|
|
314
|
+
*earned* it — see "Run the back half on the dial" below). The engineer review and all five front
|
|
273
315
|
states stay `human_approve` forever.
|
|
274
316
|
|
|
275
317
|
## Using the workflow end to end (all the steps, in order)
|
|
@@ -280,17 +322,19 @@ detailed sections below expand every phase. Invoke a skill by name in your agent
|
|
|
280
322
|
|
|
281
323
|
### 0 — One-time setup
|
|
282
324
|
|
|
283
|
-
> **Shortcut:** `npx yadflow setup`
|
|
284
|
-
>
|
|
285
|
-
> `… check --fix` any time afterwards to reconcile. The manual steps below are the
|
|
286
|
-
> equivalent and still work.
|
|
325
|
+
> **Shortcut:** `npx yadflow setup` runs the guided wizard interactively — module install, hub
|
|
326
|
+
> detect + roster, connect a design/testing/learning tool (each optional), connect repos, wire each
|
|
327
|
+
> repo. Run `… check --fix` any time afterwards to reconcile. The manual steps below are the
|
|
328
|
+
> long-hand equivalent and still work.
|
|
287
329
|
|
|
288
330
|
1. **Install the module:** `bash skills/sdlc/install.sh` (re-run after any BMAD update).
|
|
289
331
|
2. **Have your code repo(s).** They are **separate git repos** (one `.git` each). For the demo they
|
|
290
332
|
live under `demo-repos/<repo>/` — regenerate from `demo-repos/README.md`.
|
|
291
333
|
3. **Optional tools** (the workflow degrades gracefully and records it if any are absent): **Spec Kit**
|
|
292
334
|
(`/speckit.*`), **Impeccable** (`/impeccable …`), **Repomix** (`npx repomix`, used by
|
|
293
|
-
`yad-connect-repos` and `yad-backfill`), **CodeRabbit** (advisory AI review)
|
|
335
|
+
`yad-connect-repos` and `yad-backfill`), **CodeRabbit** (advisory AI review), **DeepTutor**
|
|
336
|
+
(`deeptutor`, the learning layer's tutor — degrades to harness-native, used by `yad-connect-learning`
|
|
337
|
+
and `yad-learn`).
|
|
294
338
|
4. **Wire each code repo once:** `yad-checks repo:<repo> action: wire` (installs the CI gates —
|
|
295
339
|
*merges* with any existing CI, never clobbers), `yad-pr-template repo:<repo> action: wire` (PR/MR
|
|
296
340
|
template + risk routing), `yad-review-comments repo:<repo> action: wire` (review-comment scaffold).
|
|
@@ -301,11 +345,16 @@ detailed sections below expand every phase. Invoke a skill by name in your agent
|
|
|
301
345
|
(SSH or credential helper; GitHub or GitLab; no stored tokens). Re-run for any new repo. Freshness is a
|
|
302
346
|
**human decision**: `yad repo list` shows fresh/stale, `yad repo refresh [name]` re-packs a moved repo
|
|
303
347
|
(skills flag staleness and point here — they never silently re-pack). Greenfield → skip it.
|
|
304
|
-
6. **(Optional)
|
|
348
|
+
6. **(Optional) Connect tools** so the matching steps do real work (each degrades gracefully and is
|
|
349
|
+
recorded if absent): `yad-connect-design action: connect` (Figma-first → `design.json`, lets
|
|
350
|
+
`yad-ui` materialize screens), `yad-connect-testing action: connect` (Playwright-first →
|
|
351
|
+
`testing.json`, lets `yad-test-cases` implement automation), `yad-connect-learning action: connect`
|
|
352
|
+
(DeepTutor-first → `learning.json`, powers the cross-cutting learning layer).
|
|
353
|
+
7. **(Optional) Put the hub on a platform** so the front-half review runs through real PRs:
|
|
305
354
|
`yad-connect-repos action: detect-hub`, then `action: roster` once per reviewer (login → SDLC
|
|
306
355
|
name + role), and `yad-pr-template repo:hub action: wire` / `yad-review-comments repo:hub action:
|
|
307
356
|
wire` / `yad-checks repo:hub action: wire`. With no hub platform the front gate just runs file-only.
|
|
308
|
-
|
|
357
|
+
8. **Conventions:** commits and PR/MR titles follow Conventional Commits (lowercase after the type), the
|
|
309
358
|
human author owns each commit with an optional per-commit `Co-Authored-By` AI trailer — see
|
|
310
359
|
[`CONTRIBUTING.md`](CONTRIBUTING.md).
|
|
311
360
|
|
|
@@ -320,7 +369,10 @@ threads are resolved. Details: **“Run the full front half by hand”** below.
|
|
|
320
369
|
7. `yad-architecture` → `architecture.md` + locked `contract.md` → review (**escalated**: contract).
|
|
321
370
|
8. `yad-ui` → `ui-design.md` + `DESIGN.md` → review (base rule).
|
|
322
371
|
9. `yad-stories` → repo-tagged `stories/EP-<slug>-S0N.md` → review (**per-repo**).
|
|
323
|
-
→ `state.json` reaches `currentStep: ready-for-build
|
|
372
|
+
→ `state.json` reaches `currentStep: ready-for-build` — **the build half can start now.**
|
|
373
|
+
10. `yad-test-cases` → `test-cases.md` (+ automation tests when a testing tool is connected) → review (base rule).
|
|
374
|
+
**Parallel, non-blocking:** opens when the stories gate passes and runs alongside the build half; its
|
|
375
|
+
review never moves `currentStep` off `ready-for-build`.
|
|
324
376
|
|
|
325
377
|
### B — Build half (per story, per repo)
|
|
326
378
|
From a `ready-for-build` story, for **each** repo the story is tagged with. Details: **“Run the full
|
|
@@ -355,12 +407,13 @@ Details: **“Run the back half on the dial”** below.
|
|
|
355
407
|
## Run the full front half by hand
|
|
356
408
|
|
|
357
409
|
The front half walks **epic → review → architecture+contract → review → UI design → review → stories
|
|
358
|
-
→ review → `ready-for-build
|
|
359
|
-
|
|
410
|
+
→ review → `ready-for-build`**, then **test cases → review** runs as a **parallel, non-blocking track**
|
|
411
|
+
alongside the build half. It is all files under `epics/EP-<slug>/`. The skills below guide you, but you
|
|
412
|
+
can also edit the files directly — that's the point.
|
|
360
413
|
|
|
361
414
|
Each authoring step is the same shape: an author skill produces an artifact, sets its step `done`,
|
|
362
415
|
moves `currentStep` to the matching review, and **stops at the gate**. Then **`yad-review-gate`**
|
|
363
|
-
(one gate, reused for all
|
|
416
|
+
(one gate, reused for all five reviews) takes `open → comment → approve → advance`. When the hub is on a
|
|
364
417
|
platform, the **`yad gate`** CLI runs that gate over a real PR/MR — `open` raises the review PR, `sync`
|
|
365
418
|
pulls approvals + comment threads into the ledger, and the step **auto-advances when the approved,
|
|
366
419
|
fully-resolved PR is merged** (the merge is the human approval act).
|
|
@@ -417,7 +470,7 @@ accumulate, and the step moves forward only when the rule is met. **File-only**
|
|
|
417
470
|
in any story's `repos`**.
|
|
418
471
|
|
|
419
472
|
### Check status anytime
|
|
420
|
-
Invoke **`yad-status`** (read-only) to see the full
|
|
473
|
+
Invoke **`yad-status`** (read-only) to see the full 10-step chain, every step's dials/status, the
|
|
421
474
|
contract lock, story repo tags, and which approvals the active gate still needs.
|
|
422
475
|
|
|
423
476
|
## Worked example (already in this repo)
|
package/cli/doctor.mjs
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import fs from 'node:fs';
|
|
7
7
|
import { c, log, ok, info, warn, fail, hand, run, has, exists, readJSON, readJSONStrict } from './lib.mjs';
|
|
8
|
-
import { VERSION, PROJECT_FILES, DESIGN_TOOLS } from './manifest.mjs';
|
|
8
|
+
import { VERSION, PROJECT_FILES, DESIGN_TOOLS, TESTING_TOOLS, LEARNING_TOOLS } from './manifest.mjs';
|
|
9
9
|
import { loadLedger, epicRoot } from './epic-state.mjs';
|
|
10
10
|
import { gitHead } from './setup.mjs';
|
|
11
11
|
import { cliFor } from './platform.mjs';
|
|
@@ -88,13 +88,55 @@ export function projectChecks(checks, root) {
|
|
|
88
88
|
}
|
|
89
89
|
if (designBroken) { /* reported above */ }
|
|
90
90
|
else if (typeof design !== 'object' || Array.isArray(design) || design === null) check(checks, 'design', 'project', 'fail', `${PROJECT_FILES.designConfig} has the wrong shape [YAD-STATE-002]`, 'expected a JSON object');
|
|
91
|
-
else if (
|
|
92
|
-
else if (!
|
|
91
|
+
else if (design.tool === 'none') check(checks, 'design', 'project', 'ok', 'design: markdown-only');
|
|
92
|
+
else if (!DESIGN_TOOLS.includes(design.tool)) check(checks, 'design', 'project', 'fail', `${PROJECT_FILES.designConfig}: unknown or missing design tool '${design.tool}' [YAD-CFG-002]`, `expected one of ${DESIGN_TOOLS.join(', ')}, or none`);
|
|
93
93
|
else if (design.source && design.source !== 'unavailable') check(checks, 'design', 'project', 'ok', `design: ${design.tool} (${design.source})`);
|
|
94
94
|
else if (design.source === 'unavailable') check(checks, 'design', 'project', 'warn', `design: ${design.tool} MCP unavailable — yad-ui runs markdown-only`, 'connect the MCP, then run `yad-connect-design` (action: refresh)');
|
|
95
95
|
else check(checks, 'design', 'project', 'warn', `design: ${design.tool} recorded but the MCP is not confirmed`, 'run `yad-connect-design` in Claude Code to detect the MCP');
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
// testing.json: parse + shape + tool + MCP confirmation (absent is the normal artifacts-only default —
|
|
99
|
+
// pre-feature projects have none, so silence rather than warn when the file does not exist).
|
|
100
|
+
const testingPath = path.join(root, PROJECT_FILES.testingConfig);
|
|
101
|
+
if (exists(testingPath)) {
|
|
102
|
+
let testing = null, testingBroken = false;
|
|
103
|
+
try {
|
|
104
|
+
testing = readJSONStrict(testingPath, null);
|
|
105
|
+
} catch (e) {
|
|
106
|
+
testingBroken = true;
|
|
107
|
+
check(checks, 'testing', 'project', 'fail', `${PROJECT_FILES.testingConfig} does not parse [${e.code || 'YAD-STATE-001'}]`, e.hint || 'fix the JSON or restore it from git');
|
|
108
|
+
}
|
|
109
|
+
if (testingBroken) { /* reported above */ }
|
|
110
|
+
else if (typeof testing !== 'object' || Array.isArray(testing) || testing === null) check(checks, 'testing', 'project', 'fail', `${PROJECT_FILES.testingConfig} has the wrong shape [YAD-STATE-002]`, 'expected a JSON object');
|
|
111
|
+
else if (testing.tool === 'none') check(checks, 'testing', 'project', 'ok', 'testing: artifacts-only');
|
|
112
|
+
else if (!TESTING_TOOLS.includes(testing.tool)) check(checks, 'testing', 'project', 'fail', `${PROJECT_FILES.testingConfig}: unknown or missing testing tool '${testing.tool}' [YAD-CFG-003]`, `expected one of ${TESTING_TOOLS.join(', ')}, or none`);
|
|
113
|
+
else if (testing.source && testing.source !== 'unavailable') check(checks, 'testing', 'project', 'ok', `testing: ${testing.tool} (${testing.source})`);
|
|
114
|
+
else if (testing.source === 'unavailable') check(checks, 'testing', 'project', 'warn', `testing: ${testing.tool} MCP unavailable — yad-test-cases runs artifacts-only`, 'connect the MCP, then run `yad-connect-testing` (action: refresh)');
|
|
115
|
+
else check(checks, 'testing', 'project', 'warn', `testing: ${testing.tool} recorded but the MCP is not confirmed`, 'run `yad-connect-testing` in Claude Code to detect the MCP');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// learning.json: parse + shape + tool + CLI confirmation (absent is the normal harness-native default —
|
|
119
|
+
// pre-feature projects have none, so silence rather than warn when the file does not exist). DeepTutor
|
|
120
|
+
// has no MCP, so `source` is deeptutor-cli (found on PATH) or harness-native (degraded).
|
|
121
|
+
const learningPath = path.join(root, PROJECT_FILES.learningConfig);
|
|
122
|
+
if (exists(learningPath)) {
|
|
123
|
+
let learning = null, learningBroken = false;
|
|
124
|
+
try {
|
|
125
|
+
learning = readJSONStrict(learningPath, null);
|
|
126
|
+
} catch (e) {
|
|
127
|
+
learningBroken = true;
|
|
128
|
+
check(checks, 'learning', 'project', 'fail', `${PROJECT_FILES.learningConfig} does not parse [${e.code || 'YAD-STATE-001'}]`, e.hint || 'fix the JSON or restore it from git');
|
|
129
|
+
}
|
|
130
|
+
if (learningBroken) { /* reported above */ }
|
|
131
|
+
else if (typeof learning !== 'object' || Array.isArray(learning) || learning === null) check(checks, 'learning', 'project', 'fail', `${PROJECT_FILES.learningConfig} has the wrong shape [YAD-STATE-002]`, 'expected a JSON object');
|
|
132
|
+
else if (learning.tool === 'none') check(checks, 'learning', 'project', 'ok', 'learning: harness-native');
|
|
133
|
+
else if (!LEARNING_TOOLS.includes(learning.tool)) check(checks, 'learning', 'project', 'fail', `${PROJECT_FILES.learningConfig}: unknown or missing learning tool '${learning.tool}' [YAD-CFG-004]`, `expected one of ${LEARNING_TOOLS.join(', ')}, or none`);
|
|
134
|
+
else if (learning.source === 'deeptutor-cli') check(checks, 'learning', 'project', 'ok', `learning: ${learning.tool} (${learning.source})`);
|
|
135
|
+
else if (learning.source === 'harness-native') check(checks, 'learning', 'project', 'warn', `learning: ${learning.tool} CLI unavailable — yad-learn tutors harness-native`, 'install the deeptutor CLI, then run `yad-connect-learning` (action: refresh)');
|
|
136
|
+
else if (learning.source == null) check(checks, 'learning', 'project', 'warn', `learning: ${learning.tool} recorded but the CLI is not confirmed`, 'run `yad-connect-learning` in Claude Code to detect the CLI');
|
|
137
|
+
else check(checks, 'learning', 'project', 'fail', `${PROJECT_FILES.learningConfig}: unknown source '${learning.source}' [YAD-STATE-002]`, 'expected deeptutor-cli, harness-native, or null');
|
|
138
|
+
}
|
|
139
|
+
|
|
98
140
|
// repos.json: parse + every entry is a live git repo; staleness vs syncedHead
|
|
99
141
|
let registry = { repos: [] };
|
|
100
142
|
let regBroken = false;
|
package/cli/epic-state.mjs
CHANGED
|
@@ -197,9 +197,24 @@ export function gatePredicate({
|
|
|
197
197
|
|
|
198
198
|
// Advance the step in state.json once the predicate passes. Mirrors yad-review-gate Step 3:
|
|
199
199
|
// mark this review step done, unblock the next step, or set `ready-for-build` for the last one.
|
|
200
|
+
//
|
|
201
|
+
// `test-cases` is a PARALLEL, non-blocking track so the build half can start while the tester works:
|
|
202
|
+
// approving `stories-review` makes the epic `ready-for-build` (the build half keys off this) AND opens
|
|
203
|
+
// `test-cases` for the tester; completing `test-cases-review` never pulls `currentStep` back from
|
|
204
|
+
// `ready-for-build`. Both rules degrade safely for an old chain that has no test-cases steps.
|
|
200
205
|
export function advanceState(state, step) {
|
|
201
206
|
const i = state.steps.findIndex((s) => s.id === step.id);
|
|
202
207
|
state.steps[i] = { ...state.steps[i], status: 'done' };
|
|
208
|
+
if (step.id === 'stories-review') {
|
|
209
|
+
const tc = state.steps.find((s) => s.id === 'test-cases');
|
|
210
|
+
if (tc && tc.status === 'blocked') tc.status = 'in_progress';
|
|
211
|
+
state.currentStep = 'ready-for-build';
|
|
212
|
+
return state;
|
|
213
|
+
}
|
|
214
|
+
if (step.id === 'test-cases-review') {
|
|
215
|
+
state.currentStep = 'ready-for-build';
|
|
216
|
+
return state;
|
|
217
|
+
}
|
|
203
218
|
const next = state.steps[i + 1];
|
|
204
219
|
if (next) {
|
|
205
220
|
next.status = next.type === 'review+approve' ? 'in_review' : 'in_progress';
|
|
@@ -210,11 +225,13 @@ export function advanceState(state, step) {
|
|
|
210
225
|
return state;
|
|
211
226
|
}
|
|
212
227
|
|
|
213
|
-
// Mark a step in-review (idempotent) and point currentStep at it
|
|
228
|
+
// Mark a step in-review (idempotent) and point currentStep at it — EXCEPT once the epic is
|
|
229
|
+
// `ready-for-build`: the parallel `test-cases` track must not pull currentStep back (the build half
|
|
230
|
+
// runs alongside the tester, and only the test-cases review is in flight at that point).
|
|
214
231
|
export function markInReview(state, step) {
|
|
215
232
|
const i = state.steps.findIndex((s) => s.id === step.id);
|
|
216
233
|
if (state.steps[i].status !== 'done') state.steps[i].status = 'in_review';
|
|
217
|
-
state.currentStep = step.id;
|
|
234
|
+
if (state.currentStep !== 'ready-for-build') state.currentStep = step.id;
|
|
218
235
|
return state;
|
|
219
236
|
}
|
|
220
237
|
|
package/cli/errors.mjs
CHANGED
|
@@ -21,6 +21,8 @@ export const CODES = {
|
|
|
21
21
|
'YAD-STATE-003': 'a registered repo path is missing or not a git repository',
|
|
22
22
|
'YAD-CFG-001': 'hub.json names an unknown platform (expected github, gitlab, or null)',
|
|
23
23
|
'YAD-CFG-002': 'design.json names an unknown design tool (expected one of config.yaml design.tools, or none)',
|
|
24
|
+
'YAD-CFG-003': 'testing.json names an unknown testing tool (expected one of config.yaml testing.tools, or none)',
|
|
25
|
+
'YAD-CFG-004': 'learning.json names an unknown learning tool (expected one of config.yaml learning.tools, or none)',
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
export const err = (code, message, hint) => new YadError(code, message, hint);
|
package/cli/manifest.mjs
CHANGED
|
@@ -10,15 +10,19 @@ import { readFileSync } from 'node:fs';
|
|
|
10
10
|
const { version } = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
11
11
|
export const VERSION = version;
|
|
12
12
|
|
|
13
|
-
// The
|
|
13
|
+
// The 22 hand-authored yad-* skills (mirrors skills/sdlc/install.sh).
|
|
14
14
|
export const SKILLS = [
|
|
15
15
|
'yad-analysis',
|
|
16
16
|
'yad-epic',
|
|
17
17
|
'yad-architecture',
|
|
18
18
|
'yad-ui',
|
|
19
19
|
'yad-stories',
|
|
20
|
+
'yad-test-cases',
|
|
20
21
|
'yad-connect-repos',
|
|
21
22
|
'yad-connect-design',
|
|
23
|
+
'yad-connect-testing',
|
|
24
|
+
'yad-connect-learning',
|
|
25
|
+
'yad-learn',
|
|
22
26
|
'yad-spec',
|
|
23
27
|
'yad-implement',
|
|
24
28
|
'yad-checks',
|
|
@@ -87,11 +91,29 @@ export const MODULE_FILES = ['config.yaml', 'module-help.csv'];
|
|
|
87
91
|
export const DESIGN_TOOLS = ['figma', 'pencil'];
|
|
88
92
|
export const DESIGN_PRIMARY = 'figma';
|
|
89
93
|
|
|
94
|
+
// Supported testing-tool adapters (mirrors skills/sdlc/config.yaml `testing.tools`); `TESTING_PRIMARY`
|
|
95
|
+
// is the fallback `registerTesting`/setup use when an unknown tool is named, and `none` is the explicit
|
|
96
|
+
// artifacts-only choice. (doctor does NOT fall back — an unknown tool there is a hard YAD-CFG-003 fail,
|
|
97
|
+
// mirroring the design-tool YAD-CFG-002.)
|
|
98
|
+
export const TESTING_TOOLS = ['playwright', 'cypress', 'pytest'];
|
|
99
|
+
export const TESTING_PRIMARY = 'playwright';
|
|
100
|
+
|
|
101
|
+
// Supported learning-tool adapters (mirrors skills/sdlc/config.yaml `learning.tools`); `LEARNING_PRIMARY`
|
|
102
|
+
// is the fallback `registerLearning`/setup use when an unknown tool is named, and `none` is the explicit
|
|
103
|
+
// harness-native choice (yad-learn tutors via the harness model when no tool is connected). DeepTutor is
|
|
104
|
+
// a CLI subprocess (no MCP), so the connect skill detects the binary — not an MCP — but the registry +
|
|
105
|
+
// degrade shape mirrors design/testing. (doctor does NOT fall back — an unknown tool there is a hard
|
|
106
|
+
// YAD-CFG-004 fail, mirroring the design-tool YAD-CFG-002.)
|
|
107
|
+
export const LEARNING_TOOLS = ['deeptutor'];
|
|
108
|
+
export const LEARNING_PRIMARY = 'deeptutor';
|
|
109
|
+
|
|
90
110
|
// Project-level files setup produces (used by `check` to spot missing setup).
|
|
91
111
|
export const PROJECT_FILES = {
|
|
92
112
|
reposRegistry: '.sdlc/repos.json',
|
|
93
113
|
hubConfig: '.sdlc/hub.json',
|
|
94
114
|
designConfig: '.sdlc/design.json',
|
|
115
|
+
testingConfig: '.sdlc/testing.json',
|
|
116
|
+
learningConfig: '.sdlc/learning.json',
|
|
95
117
|
version: '.sdlc/cli-version.json',
|
|
96
118
|
};
|
|
97
119
|
|
package/cli/setup.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
c, log, step, ok, info, warn, hand, fail, ask, askYesNo, run, has,
|
|
6
6
|
exists, readJSON, writeJSON,
|
|
7
7
|
} from './lib.mjs';
|
|
8
|
-
import { VERSION, IDE_FOLDER_TARGETS, PROJECT_FILES, DESIGN_TOOLS, DESIGN_PRIMARY } from './manifest.mjs';
|
|
8
|
+
import { VERSION, IDE_FOLDER_TARGETS, PROJECT_FILES, DESIGN_TOOLS, DESIGN_PRIMARY, TESTING_TOOLS, TESTING_PRIMARY, LEARNING_TOOLS, LEARNING_PRIMARY } from './manifest.mjs';
|
|
9
9
|
import { moduleActions, repoActions, hubActions, authorsActions } from './plan.mjs';
|
|
10
10
|
|
|
11
11
|
const ALL_IDES = [...IDE_FOLDER_TARGETS, '.opencode'];
|
|
@@ -87,6 +87,62 @@ export function registerDesign(root, { tool, project_url = null, files = null, t
|
|
|
87
87
|
return design;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
// Record the project's testing-tool connection into .sdlc/testing.json (the deterministic half of the
|
|
91
|
+
// connect loop; MCP detection itself is an AI step, handed off to `yad-connect-testing`). An unknown
|
|
92
|
+
// tool falls back to the primary adapter rather than being rejected — mirrors registerDesign. `none` is
|
|
93
|
+
// the explicit artifacts-only choice.
|
|
94
|
+
export function registerTesting(root, { tool, project_url = null, suites = null, today = null } = {}) {
|
|
95
|
+
// Idempotent re-connect: carry the original first-connect date forward; only lastSyncedAt moves.
|
|
96
|
+
const testingPath = path.join(root, PROJECT_FILES.testingConfig);
|
|
97
|
+
const prev = readJSON(testingPath, null);
|
|
98
|
+
const connectedAt = prev && prev.connectedAt ? prev.connectedAt : today;
|
|
99
|
+
let t = (tool || '').toLowerCase();
|
|
100
|
+
if (t === 'none' || t === '') {
|
|
101
|
+
const off = { tool: 'none', provider: null, project_url: null, auth: 'user',
|
|
102
|
+
suites: {}, connectedAt, lastSyncedAt: today, source: 'unavailable' };
|
|
103
|
+
writeJSON(testingPath, off);
|
|
104
|
+
return off;
|
|
105
|
+
}
|
|
106
|
+
if (!TESTING_TOOLS.includes(t)) { warn(`unknown testing tool '${tool}' — using ${TESTING_PRIMARY}`); t = TESTING_PRIMARY; }
|
|
107
|
+
// source stays null until `yad-connect-testing` detects the MCP in the harness (AI step). doctor
|
|
108
|
+
// reports a recorded-but-unconfirmed connection as a warning pointing at that skill.
|
|
109
|
+
const testing = {
|
|
110
|
+
tool: t, provider: null, project_url: project_url || null, auth: 'user',
|
|
111
|
+
suites: suites || {},
|
|
112
|
+
connectedAt, lastSyncedAt: today, source: null,
|
|
113
|
+
};
|
|
114
|
+
writeJSON(testingPath, testing);
|
|
115
|
+
return testing;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Record the project's learning-tool connection into .sdlc/learning.json (the deterministic half of the
|
|
119
|
+
// connect loop; CLI detection + the kb build are AI steps, handed off to `yad-connect-learning`). An
|
|
120
|
+
// unknown tool falls back to the primary adapter rather than being rejected — mirrors registerDesign/
|
|
121
|
+
// registerTesting. `none` is the explicit harness-native choice (yad-learn tutors via the harness model).
|
|
122
|
+
// DeepTutor has no MCP, so `source` stays null at setup until the connect skill detects the CLI on PATH.
|
|
123
|
+
export function registerLearning(root, { tool, kb = null, today = null } = {}) {
|
|
124
|
+
// Idempotent re-connect: carry the original first-connect date forward; only lastSyncedAt moves.
|
|
125
|
+
const learningPath = path.join(root, PROJECT_FILES.learningConfig);
|
|
126
|
+
const prev = readJSON(learningPath, null);
|
|
127
|
+
const connectedAt = prev && prev.connectedAt ? prev.connectedAt : today;
|
|
128
|
+
let t = (tool || '').toLowerCase();
|
|
129
|
+
if (t === 'none' || t === '') {
|
|
130
|
+
const off = { tool: 'none', provider: null, version: null, kb: null, kb_sources: [], auth: 'user',
|
|
131
|
+
connectedAt, lastSyncedAt: today, source: 'harness-native' };
|
|
132
|
+
writeJSON(learningPath, off);
|
|
133
|
+
return off;
|
|
134
|
+
}
|
|
135
|
+
if (!LEARNING_TOOLS.includes(t)) { warn(`unknown learning tool '${tool}' — using ${LEARNING_PRIMARY}`); t = LEARNING_PRIMARY; }
|
|
136
|
+
// source stays null until `yad-connect-learning` detects the CLI on PATH (AI step). doctor reports a
|
|
137
|
+
// recorded-but-unconfirmed connection as a warning pointing at that skill.
|
|
138
|
+
const learning = {
|
|
139
|
+
tool: t, provider: null, version: null, kb: kb || null, kb_sources: [], auth: 'user',
|
|
140
|
+
connectedAt, lastSyncedAt: today, source: null,
|
|
141
|
+
};
|
|
142
|
+
writeJSON(learningPath, learning);
|
|
143
|
+
return learning;
|
|
144
|
+
}
|
|
145
|
+
|
|
90
146
|
function applyActions(actions, { force = false } = {}) {
|
|
91
147
|
let changed = 0;
|
|
92
148
|
for (const a of actions) {
|
|
@@ -100,7 +156,7 @@ function applyActions(actions, { force = false } = {}) {
|
|
|
100
156
|
}
|
|
101
157
|
|
|
102
158
|
export async function runSetup(root, opts = {}) {
|
|
103
|
-
const total =
|
|
159
|
+
const total = 10;
|
|
104
160
|
log(c.bold(`\nSDLC Workflow setup ${c.dim('v' + VERSION)}`));
|
|
105
161
|
log(c.dim(`target: ${root}`));
|
|
106
162
|
|
|
@@ -178,8 +234,43 @@ export async function runSetup(root, opts = {}) {
|
|
|
178
234
|
: `wrote ${PROJECT_FILES.designConfig} (${tool})`);
|
|
179
235
|
}
|
|
180
236
|
|
|
181
|
-
// 5. Connect
|
|
182
|
-
step(5, total, 'Connect
|
|
237
|
+
// 5. Connect a testing tool (Playwright-first, pluggable; the test-cases step implements automation here)
|
|
238
|
+
step(5, total, 'Connect a testing tool (playwright / cypress / pytest / none)');
|
|
239
|
+
const testingPath = path.join(root, PROJECT_FILES.testingConfig);
|
|
240
|
+
if (exists(testingPath) && !(await askYesNo('testing.json exists — reconfigure?', false))) {
|
|
241
|
+
info('keeping existing .sdlc/testing.json');
|
|
242
|
+
} else {
|
|
243
|
+
let tool = (await ask(`Testing tool (${TESTING_TOOLS.join('/')}/none)`, TESTING_PRIMARY)).toLowerCase();
|
|
244
|
+
if (![...TESTING_TOOLS, 'none'].includes(tool)) {
|
|
245
|
+
warn(`unknown testing tool '${tool}' — using ${TESTING_PRIMARY}`);
|
|
246
|
+
tool = TESTING_PRIMARY;
|
|
247
|
+
}
|
|
248
|
+
const project_url = tool === 'none' ? null : (await ask(' project/config reference (blank to set later)', '')) || null;
|
|
249
|
+
registerTesting(root, { tool, project_url, today: opts.today ?? null });
|
|
250
|
+
ok(tool === 'none'
|
|
251
|
+
? `wrote ${PROJECT_FILES.testingConfig} (artifacts-only)`
|
|
252
|
+
: `wrote ${PROJECT_FILES.testingConfig} (${tool})`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// 6. Connect a learning tool (DeepTutor-first, pluggable; the learning layer tutors team members here)
|
|
256
|
+
step(6, total, 'Connect a learning tool (deeptutor / none)');
|
|
257
|
+
const learningPath = path.join(root, PROJECT_FILES.learningConfig);
|
|
258
|
+
if (exists(learningPath) && !(await askYesNo('learning.json exists — reconfigure?', false))) {
|
|
259
|
+
info('keeping existing .sdlc/learning.json');
|
|
260
|
+
} else {
|
|
261
|
+
let tool = (await ask(`Learning tool (${LEARNING_TOOLS.join('/')}/none)`, LEARNING_PRIMARY)).toLowerCase();
|
|
262
|
+
if (![...LEARNING_TOOLS, 'none'].includes(tool)) {
|
|
263
|
+
warn(`unknown learning tool '${tool}' — using ${LEARNING_PRIMARY}`);
|
|
264
|
+
tool = LEARNING_PRIMARY;
|
|
265
|
+
}
|
|
266
|
+
registerLearning(root, { tool, today: opts.today ?? null });
|
|
267
|
+
ok(tool === 'none'
|
|
268
|
+
? `wrote ${PROJECT_FILES.learningConfig} (harness-native)`
|
|
269
|
+
: `wrote ${PROJECT_FILES.learningConfig} (${tool})`);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// 7. Connect code repos
|
|
273
|
+
step(7, total, 'Connect code repos');
|
|
183
274
|
const regPath = path.join(root, PROJECT_FILES.reposRegistry);
|
|
184
275
|
const registry = readJSON(regPath, { repos: [] });
|
|
185
276
|
const known = new Set(registry.repos.map((r) => r.name));
|
|
@@ -202,8 +293,8 @@ export async function runSetup(root, opts = {}) {
|
|
|
202
293
|
}
|
|
203
294
|
}
|
|
204
295
|
|
|
205
|
-
//
|
|
206
|
-
step(
|
|
296
|
+
// 8. Wire each connected repo + the hub itself
|
|
297
|
+
step(8, total, 'Wire connected repos + the hub (CI gates, PR template, comment scaffold, gate-sync)');
|
|
207
298
|
if (registry.repos.length === 0) info('no repos to wire');
|
|
208
299
|
for (const repo of registry.repos) {
|
|
209
300
|
log(` ${c.bold(repo.name)} ${c.dim(`(${repo.platform})`)}`);
|
|
@@ -218,8 +309,8 @@ export async function runSetup(root, opts = {}) {
|
|
|
218
309
|
// author allowlists for the verified-commits gate (hub + every repo), from the roster emails
|
|
219
310
|
applyActions(authorsActions(root, registry.repos), { force: true });
|
|
220
311
|
|
|
221
|
-
//
|
|
222
|
-
step(
|
|
312
|
+
// 9. Optional CodeRabbit
|
|
313
|
+
step(9, total, 'AI review (CodeRabbit)');
|
|
223
314
|
for (const repo of registry.repos) {
|
|
224
315
|
const cr = path.join(path.resolve(root, repo.path), '.coderabbit.yaml');
|
|
225
316
|
if (exists(cr)) { info(`${repo.name}: .coderabbit.yaml present`); continue; }
|
|
@@ -229,8 +320,8 @@ export async function runSetup(root, opts = {}) {
|
|
|
229
320
|
}
|
|
230
321
|
}
|
|
231
322
|
|
|
232
|
-
//
|
|
233
|
-
step(
|
|
323
|
+
// 10. Summary + version stamp
|
|
324
|
+
step(10, total, 'Done');
|
|
234
325
|
writeJSON(path.join(root, PROJECT_FILES.version), { version: VERSION, ideTargets, updatedAt: opts.today ?? null });
|
|
235
326
|
ok(`stamped ${PROJECT_FILES.version} (v${VERSION})`);
|
|
236
327
|
log('');
|
|
@@ -240,6 +331,14 @@ export async function runSetup(root, opts = {}) {
|
|
|
240
331
|
if (design && design.tool && design.tool !== 'none') {
|
|
241
332
|
hand(`confirm the design tool: run \`yad-connect-design\` to detect the ${design.tool} MCP (or it degrades to markdown-only)`);
|
|
242
333
|
}
|
|
334
|
+
const testing = readJSON(testingPath, null);
|
|
335
|
+
if (testing && testing.tool && testing.tool !== 'none') {
|
|
336
|
+
hand(`confirm the testing tool: run \`yad-connect-testing\` to detect the ${testing.tool} MCP (or it degrades to artifacts-only)`);
|
|
337
|
+
}
|
|
338
|
+
const learning = readJSON(learningPath, null);
|
|
339
|
+
if (learning && learning.tool && learning.tool !== 'none') {
|
|
340
|
+
hand(`confirm the learning tool: run \`yad-connect-learning\` to detect the ${learning.tool} CLI (or it degrades to harness-native)`);
|
|
341
|
+
}
|
|
243
342
|
hand('author your first epic: run `yad-epic`');
|
|
244
343
|
log('');
|
|
245
344
|
log(c.dim('Re-run anytime: `yad check` (report) / `yad check --fix` (reconcile).'));
|