fullstack-agentic-flow 2.0.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.
Files changed (91) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +348 -0
  3. package/dist/src/cli/args.js +74 -0
  4. package/dist/src/cli/commands/doctor.js +133 -0
  5. package/dist/src/cli/commands/init.js +103 -0
  6. package/dist/src/cli/commands/shared.js +20 -0
  7. package/dist/src/cli/commands/tools.js +21 -0
  8. package/dist/src/cli/commands/update.js +21 -0
  9. package/dist/src/cli/main.js +6 -0
  10. package/dist/src/cli/output.js +11 -0
  11. package/dist/src/cli/prompter.js +13 -0
  12. package/dist/src/cli/report.js +13 -0
  13. package/dist/src/cli/run.js +48 -0
  14. package/dist/src/domain/canon-mapping.js +26 -0
  15. package/dist/src/domain/claude-md-mode.js +17 -0
  16. package/dist/src/domain/enum-parse.js +9 -0
  17. package/dist/src/domain/errors.js +13 -0
  18. package/dist/src/domain/pipeline-config.js +58 -0
  19. package/dist/src/domain/runtime.js +19 -0
  20. package/dist/src/domain/tool.js +129 -0
  21. package/dist/src/domain/topology.js +39 -0
  22. package/dist/src/install/adapters.js +39 -0
  23. package/dist/src/install/applier.js +150 -0
  24. package/dist/src/install/detect.js +70 -0
  25. package/dist/src/install/managed-section.js +35 -0
  26. package/dist/src/install/manifest.js +63 -0
  27. package/dist/src/install/planned-file.js +77 -0
  28. package/dist/src/install/planner.js +139 -0
  29. package/dist/src/install/rules-preset.js +37 -0
  30. package/dist/src/install/setup-guide.js +60 -0
  31. package/dist/src/io/fs.js +39 -0
  32. package/dist/src/io/paths.js +6 -0
  33. package/dist/src/io/shell.js +25 -0
  34. package/dist/src/registry/commands.js +52 -0
  35. package/dist/src/registry/subagents.js +52 -0
  36. package/dist/src/registry/tools.js +99 -0
  37. package/package.json +53 -0
  38. package/payload/ai-agents/agents/00-bootstrap.md +473 -0
  39. package/payload/ai-agents/agents/00a-scaffold.md +295 -0
  40. package/payload/ai-agents/agents/00b-module-scaffold.md +108 -0
  41. package/payload/ai-agents/agents/01-intake.md +308 -0
  42. package/payload/ai-agents/agents/01b-impact-analysis.md +274 -0
  43. package/payload/ai-agents/agents/02a-dependency-map.md +247 -0
  44. package/payload/ai-agents/agents/02b-backend-contract.md +434 -0
  45. package/payload/ai-agents/agents/02c-ui-contract.md +367 -0
  46. package/payload/ai-agents/agents/02d-interface-seam.md +373 -0
  47. package/payload/ai-agents/agents/03a-backend-sequence.md +233 -0
  48. package/payload/ai-agents/agents/03b-ui-sequence.md +230 -0
  49. package/payload/ai-agents/agents/04a-backend-implementer.md +290 -0
  50. package/payload/ai-agents/agents/04b-ui-implementer.md +302 -0
  51. package/payload/ai-agents/agents/05-ci-validator.md +125 -0
  52. package/payload/ai-agents/agents/06-qa-spec.md +249 -0
  53. package/payload/ai-agents/agents/07-security.md +228 -0
  54. package/payload/ai-agents/agents/08-performance.md +208 -0
  55. package/payload/ai-agents/agents/09a-feature-notes.md +156 -0
  56. package/payload/ai-agents/agents/09b-release.md +111 -0
  57. package/payload/ai-agents/architecture-context.template.md +554 -0
  58. package/payload/ai-agents/archive/README.md +22 -0
  59. package/payload/ai-agents/canon/backend-canon.md +295 -0
  60. package/payload/ai-agents/canon/frontend-canon.md +47 -0
  61. package/payload/ai-agents/canon/typed-data.md +158 -0
  62. package/payload/ai-agents/observations/README.md +11 -0
  63. package/payload/ai-agents/scripts/check-test-with-change.mjs +103 -0
  64. package/payload/ai-agents/scripts/check-typed-boundaries.mjs +329 -0
  65. package/payload/ai-agents/scripts/lib/config.mjs +63 -0
  66. package/payload/ai-agents/scripts/lib/glob.mjs +40 -0
  67. package/payload/ai-agents/state/README.md +108 -0
  68. package/payload/ai-agents/state/current-stage.md +10 -0
  69. package/payload/ai-agents/templates/README.md +22 -0
  70. package/payload/ai-agents/templates/ci/github-actions.rules.yml +35 -0
  71. package/payload/ai-agents/templates/ci/gitlab-ci.rules.yml +30 -0
  72. package/payload/ai-agents/toolchain.md +214 -0
  73. package/payload/commands/bootstrap.md +17 -0
  74. package/payload/commands/contract.md +64 -0
  75. package/payload/commands/finalize.md +26 -0
  76. package/payload/commands/impact.md +25 -0
  77. package/payload/commands/implement.md +58 -0
  78. package/payload/commands/intake.md +16 -0
  79. package/payload/commands/perf.md +20 -0
  80. package/payload/commands/qa.md +15 -0
  81. package/payload/commands/release.md +22 -0
  82. package/payload/commands/resume.md +52 -0
  83. package/payload/commands/scaffold-module.md +20 -0
  84. package/payload/commands/scaffold.md +35 -0
  85. package/payload/commands/seam.md +28 -0
  86. package/payload/commands/security.md +16 -0
  87. package/payload/commands/sequence.md +22 -0
  88. package/payload/commands/status.md +37 -0
  89. package/payload/legacy/v1-checksums.json +197 -0
  90. package/payload/root/contracts-README.md +19 -0
  91. package/payload/root/pipeline-section.md +33 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,348 @@
1
+ # fullstack-agentic-flow
2
+
3
+ A gated, multi-agent delivery pipeline for Claude Code and Codex. Backend and
4
+ frontend are designed and built **in parallel** against a frozen interface
5
+ seam, **test-first**, with **typed data at every boundary**, and a measured
6
+ **risk class** that decides how much process each change gets.
7
+
8
+ ```
9
+ npx fullstack-agentic-flow init
10
+ ```
11
+
12
+ It works on four repository shapes:
13
+
14
+ | Topology | What the repo holds | The seam between tracks |
15
+ |----------|---------------------|-------------------------|
16
+ | `mvc` | Server-rendered app (Laravel + Inertia/Blade, Rails, Django templates…) | A typed **view model** per screen |
17
+ | `unified-api` | Backend API + frontend in one repo (REST, GraphQL, RPC) | The API contract, in-repo |
18
+ | `split-backend` | API only; frontends live elsewhere | A contract **exported** to `contracts/` |
19
+ | `split-frontend` | UI only, any framework (React, Vue, Svelte, mobile…) | A contract **imported** from the API repo |
20
+
21
+ The agent prompts name no language or framework. Everything repo-specific lives
22
+ in one generated file, `architecture-context.md`. Everything that should be the
23
+ **same across all your repos** (module layout, naming, typed data, TDD) lives in
24
+ the pipeline's **canon**.
25
+
26
+ ---
27
+
28
+ ## What v2 adds
29
+
30
+ - **Topologies.** The seam agent (02d) freezes whatever crosses between tracks:
31
+ page contracts in MVC, endpoints or GraphQL operations in API repos, and a
32
+ versioned contract file that travels between split repos (`/seam export`,
33
+ `/seam import`).
34
+ - **Backend canon.** One module shape for every framework: Domain /
35
+ Application / Infrastructure / Interface, one use case per action, fixed
36
+ repository method names. There are mappings for Laravel, NestJS, Express,
37
+ Django/FastAPI, Spring and Go. `/scaffold-module` generates a new module from
38
+ the repo's mapping, so module eighteen looks like module two.
39
+ - **Typed data (T1–T6).** No untyped arrays, maps, `mixed` or `any` across a
40
+ boundary. Named Data classes instead, even when that means many of them.
41
+ Contracts name every shape, and a diff-based CI check enforces T1 for PHP,
42
+ TypeScript and Python.
43
+ - **TDD.** Contracts name the tests, with the reason each should fail. Sequences
44
+ put the acceptance tests first as pending specs. Implementers run
45
+ red → green → refactor per test and log it. CI checks that source changes come
46
+ with test changes.
47
+ - **Impact analysis (01b).** Before design, the agent predicts the blast radius
48
+ with the code graphs and scores ten dimensions. The highest score sets the
49
+ risk class L0–L3, and L2/L3 add characterisation tests, a mandatory perf
50
+ review, and rollout plans. After implementation it compares actual against
51
+ predicted and flags untested escapes before merge.
52
+ - **Toolchain.** find-skills, superpowers, claude-mem, impeccable,
53
+ task-observer, ponytail, headroom, code-review-graph and graphify, each wired
54
+ to specific agents under an explicit precedence order
55
+ (`.ai-agents/toolchain.md`).
56
+ - **Nothing after the merge.** QA, security and performance review run against
57
+ the branch diff, so Critical findings block the merge rather than landing on
58
+ main. `/finalize` then writes the feature's release note as a changeset,
59
+ archives the contracts and reasoning, and resets the state directory — all
60
+ committed to the same branch. `/release` later assembles those notes into the
61
+ changelog; that is release administration, not feature work.
62
+ - **Installer.** An `npx` CLI that installs into Claude Code (`.claude/`) and/or
63
+ Codex (`.agents/skills/`) from one source of truth, with the pipeline section
64
+ written once into `AGENTS.md`. It updates safely: files your team edited are
65
+ never overwritten.
66
+
67
+ ---
68
+
69
+ ## Pipeline
70
+
71
+ ```
72
+ /scaffold ─► 00a one real vertical slice, canon layout, test-first (empty repos)
73
+ │ Gate A
74
+ /bootstrap ─► 00 architecture-context.md: topology, canon mapping, rules
75
+ │ Gate B
76
+ /intake ─► 01 requirements, business language only
77
+ │ Gate 0
78
+ /impact ─► 01b predicted blast radius → risk class L0–L3
79
+
80
+ /contract
81
+ ┌─────────────┼─────────────┐
82
+ 02a deps 02b backend 02c ui ◄── parallel
83
+ └─────────────┼─────────────┘
84
+ 02d interface seam [FROZEN] page-contract | http-api | graphql | export | import
85
+ │ Gate 1
86
+ /sequence
87
+ ┌──────┴──────┐
88
+ 03a backend 03b ui ◄── parallel; tests named first
89
+ └──────┬──────┘
90
+ /implement (00b for new modules, 04a, 04b — one task, one commit, TDD loop)
91
+ │ Gate 2 every commit
92
+ 05 CI (typed boundaries, test-with-change, design detector, contract integrity)
93
+
94
+ /impact --verify ─► actual vs predicted, escapes
95
+
96
+ ┌──────┴──────┐
97
+ 06 qa 07 security ◄── parallel, on the branch;
98
+ └──────┬──────┘ 08 perf mandatory at L2+
99
+ │ Gate 3 approve-merge
100
+ /finalize ─► 09a release note + archive + state reset, committed to the branch
101
+
102
+ merge ◄── nothing runs after this
103
+ ·
104
+ /release ─► 09b at release time: assemble notes, retrospective
105
+ ```
106
+
107
+ | Gate | After | Decision |
108
+ |------|-------|----------|
109
+ | A | scaffold | Is this the shape all future code should copy? |
110
+ | B | bootstrap | Is this actually how the repo works, and is the canon mapping right? |
111
+ | 0 | intake | Are these the right requirements? |
112
+ | 1 | contracts + seam | Right design? Do the halves agree? Are the named types and tests right? |
113
+ | 2 | every commit | Is this code right, and did the tests come first? |
114
+ | 3 | qa + security (+ perf), still on the branch | Is this safe to merge? |
115
+
116
+ ---
117
+
118
+ ## Install
119
+
120
+ No setup on the install side: the package is public on npm, so `npx` fetches it
121
+ directly. [PUBLISHING.md](PUBLISHING.md) covers releasing it, the versioning
122
+ policy, and rolling it out across repositories.
123
+
124
+ ```
125
+ npx fullstack-agentic-flow init # interactive
126
+ npx fullstack-agentic-flow init --topology mvc --runtime claude-code,codex --tools all --yes
127
+ npx fullstack-agentic-flow init … --run-tools # also run the tools' shell install steps
128
+ npx fullstack-agentic-flow doctor # check files, config, tools
129
+ npx fullstack-agentic-flow update # refresh pipeline files, keep your choices
130
+ npx fullstack-agentic-flow tools # list tools and their install commands
131
+ ```
132
+
133
+ What gets written:
134
+
135
+ | Path | Owner after install |
136
+ |------|---------------------|
137
+ | `.ai-agents/` agents, canon, scripts, toolchain | pipeline — refreshed by `update` unless your team edited a file (then a `.incoming` copy is written beside it) |
138
+ | `.ai-agents/state/current-stage.md`, `rules.config.json` | your team — written once |
139
+ | `.ai-agents/pipeline.config.json`, `SETUP.md` | regenerated from your choices |
140
+ | `.claude/commands/`, `.claude/agents/` | pipeline (Claude Code runtime) |
141
+ | `.agents/skills/flow-*/SKILL.md` | pipeline (Codex runtime; invoke as `$flow-intake` etc.) |
142
+ | `AGENTS.md` | yours — one delimited section holds the pipeline instructions, read by Codex and by Claude Code |
143
+ | `CLAUDE.md` | yours — a managed section with a single `@AGENTS.md` import, because Claude Code reads `AGENTS.md` only when no `CLAUDE.md` exists. `--claude-md none` skips it |
144
+ | `.gitignore` | yours — one delimited section is managed |
145
+ | `.claude/settings.json` | yours — `enabledPlugins` / `extraKnownMarketplaces` keys merged in |
146
+ | `contracts/` | split topologies only |
147
+
148
+ Then: an empty repo gets `/scaffold` and then `/bootstrap`. A repo with code
149
+ gets `/bootstrap`. After that, `/intake`.
150
+
151
+ **Upgrading from v1**: run `init` over the existing install. Files you
152
+ customised get a `.incoming` beside them for you to merge. The installer lists
153
+ the v1 files that v2 renamed (`02d-api-seam.md` → `02d-interface-seam.md`,
154
+ `contract-api.md` → `contract-seam.md`) so you can delete them. Re-run
155
+ `/bootstrap` afterwards: the context needs §1.1 topology and §5.0 canon mapping,
156
+ and the pipeline-wide rules in §10.0.
157
+
158
+ ---
159
+
160
+ ## Commands
161
+
162
+ ```
163
+ /scaffold first vertical slice, canon layout, test-first (empty repos)
164
+ /bootstrap architecture-context.md (once per repo, again after drift)
165
+ /scaffold-module canon skeleton for a new backend module
166
+ /intake start a feature
167
+ /impact risk class before design; --verify before merge
168
+ /contract 02a + 02b + 02c in parallel, then 02d freezes the seam
169
+ /seam export / import / diff cross-repo contracts (split topologies)
170
+ /sequence 03a + 03b in parallel
171
+ /implement next task, TDD loop, one commit (--track, --retry-task)
172
+ /qa /security /perf review the branch before merging
173
+ /finalize release note, archive, state reset — the last commit on the branch
174
+ /release assemble the changelog at release time
175
+ /status /resume
176
+ ```
177
+
178
+ In Codex the same commands are skills: `$flow-intake`, `$flow-impact`, and so on.
179
+
180
+ ---
181
+
182
+ ## Development of this package
183
+
184
+ TypeScript, strict (`noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`),
185
+ zero runtime dependencies, written test-first with `node:test`.
186
+
187
+ ```
188
+ npm install
189
+ npm test # build + 39 tests (installer, adapters, rule scripts, payload vocabulary)
190
+ ```
191
+
192
+ `.gitlab-ci.yml` runs the suite on every merge request and publishes on a `v*`
193
+ tag through npm trusted publishing (OIDC) — no `NPM_TOKEN`, with a provenance
194
+ attestation attached automatically. `npm version minor && git push
195
+ --follow-tags` is the whole release.
196
+
197
+ The rule scripts in `payload/ai-agents/scripts/` are plain `.mjs` with
198
+ `// @ts-check`, so target repos run them without a build step, and this repo
199
+ type-checks them with the rest.
200
+
201
+ ---
202
+
203
+ ## Why the two tracks are parallel
204
+
205
+ Sequential full-stack work has a familiar shape: backend gets built, frontend
206
+ starts, frontend discovers the API is missing a field, backend changes, frontend
207
+ adapts. The rework is not a discipline problem. It is structural — the frontend's
208
+ requirements are discovered while building it, which is after the backend was
209
+ specified.
210
+
211
+ So both contracts are written simultaneously, each declaring its side of the API,
212
+ and a reconciliation agent diffs them before either implementer starts.
213
+
214
+ That agent — **02d, the interface seam** — is where this design earns its keep. It
215
+ finds the type mismatches, the missing display fields, the disagreement about
216
+ what an empty result looks like, the error case one side renders and the other
217
+ never returns. Those are the integration bugs, and it finds them during design
218
+ rather than during integration.
219
+
220
+ Once frozen, `contract-seam.md` outranks both contracts. The UI builds against
221
+ mocks that match it exactly. Neither implementer may quietly adapt — a change
222
+ comes back to the seam and both tracks are notified.
223
+
224
+ ---
225
+
226
+
227
+ ---
228
+
229
+ ## What the UI track actually specifies
230
+
231
+ Interface work is where most agentic pipelines go thin — "build the page" and
232
+ hope. This one specifies:
233
+
234
+ - **Screen structure** as a region hierarchy, and visual hierarchy tied to the
235
+ primary task from intake
236
+ - **Composition** — every element mapped to a component from the approved
237
+ inventory. Inventing one is a Gate 1 decision, never an implementation detail.
238
+ - **Every required state** — initial load, background refetch, empty-no-data,
239
+ empty-no-results (a different state, with different copy), partial failure,
240
+ recoverable error, fatal error, success feedback, in-flight submission. With
241
+ the actual copy, not a note that copy is needed.
242
+ - **Forms** — field order, where server errors land, double-submit prevention,
243
+ unsaved-changes behaviour
244
+ - **Interaction** — keyboard paths, focus management on every transition,
245
+ debounce intervals, motion and its reduced-motion fallback
246
+ - **Responsive** — per breakpoint, in the direction the usage context demands
247
+ - **Accessibility** — against a stated target, tested individually by the QA agent
248
+ - **Tokens, never literal values** — a hex code in a diff is a review failure
249
+
250
+ The UI implementer's self-check enforces all of it before committing.
251
+
252
+ ---
253
+
254
+ ## Changing the design system
255
+
256
+ Adopting a UI kit, replacing a component library, or migrating a styling approach
257
+ is **not a feature**. Running it through `/intake` is a category error: the
258
+ feature pipeline assumes the conventions are stable and the feature conforms to
259
+ them, and here the conventions themselves are the deliverable.
260
+
261
+ First, decide which of two things you actually mean.
262
+
263
+ **Selective borrowing** — you want a datatable, a chart wrapper, two or three
264
+ components from a kit. This needs no special handling at all. It is a Gate 1
265
+ component approval, which is the mechanism already built in: the UI contract
266
+ proposes it under §9, a human approves, it enters §8.3, features compose from it.
267
+
268
+ **Wholesale adoption** — the kit becomes the design system. Its tokens replace
269
+ yours, its components become the inventory, existing screens migrate. This is a
270
+ migration, and it wants its own sequence:
271
+
272
+ 1. **Do it on its own branch, outside the feature pipeline.** Integrate the kit,
273
+ wire its tokens into wherever §8.4 points, and rebuild **one real screen**
274
+ with it end to end — including every required state. One screen, not a demo
275
+ page, because that screen becomes the new exemplar.
276
+
277
+ 2. **Re-run `/bootstrap`.** This is the step people skip, and skipping it is what
278
+ produces permanently hybrid codebases. The context does not update itself:
279
+ until §8.3, §8.4 and §8.10 are re-derived, the UI implementer keeps composing
280
+ from the old inventory and every new screen is built in the outgoing style.
281
+
282
+ Bootstrap diffs its findings against the existing context and asks before
283
+ overwriting anything you hand-corrected, so this is safe to run mid-migration.
284
+
285
+ 3. **Check that §8.10 is now a kit-built screen.** If the exemplar is still a
286
+ pre-migration file, every future UI task imitates the thing you are trying to
287
+ leave.
288
+
289
+ 4. **Expect §11 to grow.** Bootstrap classifies old-pattern-plus-new-pattern as a
290
+ migration in progress: it documents the new one and records the old one as
291
+ known debt. Give it an explicit rule — "screens under {path} are
292
+ pre-migration; migrate on touch, do not extend" — so agents neither imitate
293
+ the old style nor opportunistically rewrite screens the feature did not ask
294
+ them to.
295
+
296
+ Two things worth knowing before adopting any large kit:
297
+
298
+ **Put what you use in the inventory, not what the kit ships.** A §8.3 listing two
299
+ hundred components is equivalent to no inventory — the agent cannot choose, so it
300
+ picks plausibly, and "plausibly" is how you end up with three different modals.
301
+ Add entries as features genuinely need them.
302
+
303
+ **Record where the kit fights the framework.** Kits that ship their own DOM
304
+ behaviour, plugin initialisation, or jQuery-era widgets frequently conflict with
305
+ reactive rendering. Wherever you find a workaround, it belongs in §8 as a
306
+ convention or in §11 as debt. Otherwise every agent rediscovers the same conflict
307
+ and invents a different workaround for it.
308
+
309
+ ---
310
+
311
+
312
+ ---
313
+
314
+ ## Design decisions worth knowing
315
+
316
+ **One task, one commit, then stop.** Not a throughput limitation. It is what
317
+ keeps review tractable and what makes a wrong turn cost one commit.
318
+
319
+ **Agents stop rather than improvise.** When a contract can't be implemented as
320
+ written, the implementer reports options and waits. An agent that works around a
321
+ bad contract produces code nobody specified and nobody reviews against anything.
322
+
323
+ **The exemplars do the heavy lifting.** `architecture-context.md` §5.8 and §8.10
324
+ contain real, currently-committed files pasted verbatim. Structural imitation
325
+ beats description, and it is why generated code matches your house style rather
326
+ than the model's defaults.
327
+
328
+ **CI is the backbone.** Agents stay light because mechanical checking happens
329
+ every push without anyone remembering to ask. Only rules that won't false-positive
330
+ belong there — three reliable checks beat fifteen approximate ones, because a
331
+ noisy job gets disabled within a week and never comes back.
332
+
333
+ **Judge against stated scale.** The architecture context records real volumes.
334
+ Contracts design to them, the performance agent judges against them. Without that
335
+ anchor, every review becomes a list of improvements nobody acts on.
336
+
337
+ **Archive the reasoning.** Before the merge, contracts and the decisions log are
338
+ archived into the branch. In a year, when someone asks why a field works the way it does, that is the
339
+ only record — the diff shows what, never why.
340
+
341
+ **Name the shape, even when it costs a class.** Arrays are the fastest way to
342
+ write the first version and the slowest way to change the tenth. The canon
343
+ trades a little typing now for a codebase an agent, or a colleague, can read
344
+ from signatures alone.
345
+
346
+ **Measure before deciding how careful to be.** A copy change and a change to
347
+ how invoices are totalled should not get the same process. The impact agent
348
+ makes that difference explicit, and the risk class makes it binding.
@@ -0,0 +1,74 @@
1
+ import { parseArgs } from 'node:util';
2
+ import { resolve } from 'node:path';
3
+ /** Parsed command line, as a named type (the parse edge for argv). */
4
+ export class CliArgs {
5
+ command;
6
+ target;
7
+ topology;
8
+ runtime;
9
+ tools;
10
+ claudeMd;
11
+ yes;
12
+ runTools;
13
+ force;
14
+ dryRun;
15
+ constructor(command, target, topology, runtime, tools,
16
+ /** 'import' (default) writes a CLAUDE.md stub that imports AGENTS.md; 'none' writes no CLAUDE.md. */
17
+ claudeMd, yes, runTools, force, dryRun) {
18
+ this.command = command;
19
+ this.target = target;
20
+ this.topology = topology;
21
+ this.runtime = runtime;
22
+ this.tools = tools;
23
+ this.claudeMd = claudeMd;
24
+ this.yes = yes;
25
+ this.runTools = runTools;
26
+ this.force = force;
27
+ this.dryRun = dryRun;
28
+ }
29
+ static parse(argv) {
30
+ const { values, positionals } = parseArgs({
31
+ args: [...argv],
32
+ allowPositionals: true,
33
+ strict: true,
34
+ options: {
35
+ target: { type: 'string' },
36
+ topology: { type: 'string' },
37
+ runtime: { type: 'string' },
38
+ tools: { type: 'string' },
39
+ 'claude-md': { type: 'string' },
40
+ yes: { type: 'boolean', short: 'y', default: false },
41
+ 'run-tools': { type: 'boolean', default: false },
42
+ force: { type: 'boolean', default: false },
43
+ 'dry-run': { type: 'boolean', default: false },
44
+ help: { type: 'boolean', short: 'h', default: false },
45
+ version: { type: 'boolean', short: 'v', default: false },
46
+ },
47
+ });
48
+ const command = values.help ? 'help' : values.version ? 'version' : (positionals[0] ?? 'help');
49
+ return new CliArgs(command, resolve(values.target ?? process.cwd()), values.topology ?? null, values.runtime ?? null, values.tools ?? null, values['claude-md'] ?? null, values.yes, values['run-tools'], values.force, values['dry-run']);
50
+ }
51
+ }
52
+ export const USAGE = `Usage: npx fullstack-agentic-flow <command> [options]
53
+
54
+ Commands:
55
+ init Install or re-install the pipeline into a repository
56
+ update Refresh pipeline files, keeping the choices in .ai-agents/pipeline.config.json
57
+ doctor Check files, configuration, and tools
58
+ tools List the supported skills and tools with their install steps
59
+ help Show this message
60
+
61
+ Options:
62
+ --target <dir> Repository root (default: current directory)
63
+ --topology <t> mvc | unified-api | split-backend | split-frontend
64
+ --runtime <list> claude-code,codex
65
+ --claude-md <mode> import (default) | none — how Claude Code reaches the
66
+ pipeline section, which lives in AGENTS.md
67
+ --tools <list> all | none | comma list of:
68
+ find-skills,superpowers,claude-mem,impeccable,task-observer,
69
+ ponytail,headroom,code-review-graph,graphify
70
+ -y, --yes Non-interactive; detected or default answers for anything not given
71
+ --run-tools Run the shell install steps for the selected tools
72
+ --force Overwrite pipeline files the team has edited (default: write .incoming)
73
+ --dry-run Show what would change without writing
74
+ `;
@@ -0,0 +1,133 @@
1
+ import { homedir } from 'node:os';
2
+ import { join } from 'node:path';
3
+ import { PipelineConfig } from '../../domain/pipeline-config.js';
4
+ import { Runtime } from '../../domain/runtime.js';
5
+ import { ProbeKind } from '../../domain/tool.js';
6
+ import { COMMAND_CATALOG } from '../../registry/commands.js';
7
+ import { SUBAGENT_CATALOG } from '../../registry/subagents.js';
8
+ import { TOOL_CATALOG } from '../../registry/tools.js';
9
+ import { pathExists, readTextOrNull } from '../../io/fs.js';
10
+ import { onPath } from '../../io/shell.js';
11
+ var CheckStatus;
12
+ (function (CheckStatus) {
13
+ CheckStatus["Ok"] = "ok";
14
+ CheckStatus["Warn"] = "warn";
15
+ CheckStatus["Fail"] = "fail";
16
+ })(CheckStatus || (CheckStatus = {}));
17
+ class CheckResult {
18
+ status;
19
+ subject;
20
+ detail;
21
+ constructor(status, subject, detail) {
22
+ this.status = status;
23
+ this.subject = subject;
24
+ this.detail = detail;
25
+ }
26
+ }
27
+ async function probe(target, toolProbe) {
28
+ switch (toolProbe.kind) {
29
+ case ProbeKind.Binary:
30
+ return toolProbe.targets.some((name) => onPath(name));
31
+ case ProbeKind.AnyPath:
32
+ for (const path of toolProbe.targets) {
33
+ const absolute = path.startsWith('~/') ? join(homedir(), path.slice(2)) : join(target, path);
34
+ if (await pathExists(absolute))
35
+ return true;
36
+ }
37
+ return false;
38
+ }
39
+ }
40
+ async function checks(target) {
41
+ const results = [];
42
+ const add = (status, subject, detail) => {
43
+ results.push(new CheckResult(status, subject, detail));
44
+ };
45
+ const agentFiles = [...new Set([...SUBAGENT_CATALOG.all().map((a) => a.agentFile), ...COMMAND_CATALOG.all().flatMap((c) => c.agentFiles)])];
46
+ const missingAgents = [];
47
+ for (const file of agentFiles) {
48
+ if (!(await pathExists(join(target, '.ai-agents', 'agents', file))))
49
+ missingAgents.push(file);
50
+ }
51
+ if (missingAgents.length > 0)
52
+ add(CheckStatus.Fail, 'agent files', `missing: ${missingAgents.join(', ')} — run init or update`);
53
+ else
54
+ add(CheckStatus.Ok, 'agent files', `${agentFiles.length} present`);
55
+ const configText = await readTextOrNull(join(target, '.ai-agents', 'pipeline.config.json'));
56
+ let config = null;
57
+ if (configText === null) {
58
+ add(CheckStatus.Fail, 'pipeline.config.json', 'missing — run init');
59
+ }
60
+ else {
61
+ try {
62
+ config = PipelineConfig.parse(configText);
63
+ add(CheckStatus.Ok, 'pipeline.config.json', `topology ${config.topology}, runtimes ${config.runtimes.join(', ')}, pipeline ${config.pipelineVersion}`);
64
+ }
65
+ catch (error) {
66
+ add(CheckStatus.Fail, 'pipeline.config.json', error instanceof Error ? error.message : 'unreadable');
67
+ }
68
+ }
69
+ if (config !== null) {
70
+ for (const runtime of config.runtimes) {
71
+ const missing = [];
72
+ for (const command of COMMAND_CATALOG.all()) {
73
+ const path = runtime === Runtime.ClaudeCode ? `.claude/commands/${command.slug}.md` : `.agents/skills/flow-${command.slug}/SKILL.md`;
74
+ if (!(await pathExists(join(target, path))))
75
+ missingPathsPush(missing, path);
76
+ }
77
+ if (missing.length > 0)
78
+ add(CheckStatus.Fail, `${runtime} adapter`, `missing: ${missing.join(', ')}`);
79
+ else
80
+ add(CheckStatus.Ok, `${runtime} adapter`, `${COMMAND_CATALOG.all().length} commands`);
81
+ }
82
+ for (const tool of TOOL_CATALOG.forIds(config.tools)) {
83
+ for (const runtime of config.runtimes) {
84
+ const support = tool.supportFor(runtime);
85
+ if (support === null)
86
+ continue;
87
+ const found = await probe(target, support.probe);
88
+ add(found ? CheckStatus.Ok : CheckStatus.Warn, `${tool.name} (${runtime})`, found ? 'detected' : 'not detected — see .ai-agents/SETUP.md');
89
+ }
90
+ }
91
+ }
92
+ const context = await readTextOrNull(join(target, '.ai-agents', 'architecture-context.md'));
93
+ if (context === null) {
94
+ add(CheckStatus.Warn, 'architecture-context.md', 'not written yet — run /bootstrap (or /scaffold first on an empty repo)');
95
+ }
96
+ else if (/\{[A-Z_ ]{3,}\}|\{\}/.test(context)) {
97
+ add(CheckStatus.Warn, 'architecture-context.md', 'still has template placeholders — agents will stop until /bootstrap finishes');
98
+ }
99
+ else {
100
+ add(CheckStatus.Ok, 'architecture-context.md', 'present');
101
+ }
102
+ const rules = await readTextOrNull(join(target, '.ai-agents', 'rules.config.json'));
103
+ if (rules === null || /"paths": \[\]/.test(rules)) {
104
+ add(CheckStatus.Warn, 'rules.config.json', 'typed-boundary paths not configured — /bootstrap sets them from the canon mapping');
105
+ }
106
+ else {
107
+ add(CheckStatus.Ok, 'rules.config.json', 'configured');
108
+ }
109
+ if (!(await pathExists(join(target, '.git'))))
110
+ add(CheckStatus.Warn, 'git', 'not a git repository — the pipeline commits per task');
111
+ return results;
112
+ }
113
+ function missingPathsPush(list, path) {
114
+ if (list.length < 5)
115
+ list.push(path);
116
+ else if (list.length === 5)
117
+ list.push('…');
118
+ }
119
+ export async function runDoctor(args, output) {
120
+ const results = await checks(args.target);
121
+ for (const result of results) {
122
+ const line = `${result.status.toUpperCase().padEnd(4)} ${result.subject} — ${result.detail}`;
123
+ if (result.status === CheckStatus.Fail)
124
+ output.error(line);
125
+ else if (result.status === CheckStatus.Warn)
126
+ output.warn(line);
127
+ else
128
+ output.info(line);
129
+ }
130
+ const failed = results.filter((result) => result.status === CheckStatus.Fail).length;
131
+ output.info(failed === 0 ? 'Doctor: no failures.' : `Doctor: ${failed} failure(s).`);
132
+ return failed === 0 ? 0 : 1;
133
+ }