autonomous-sdlc-harness 0.1.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 (171) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +7 -0
  3. package/README.md +24 -0
  4. package/dist/cli.js +194 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/config.js +561 -0
  7. package/dist/commands/config.js.map +1 -0
  8. package/dist/commands/daemon.js +791 -0
  9. package/dist/commands/daemon.js.map +1 -0
  10. package/dist/commands/doctor.js +336 -0
  11. package/dist/commands/doctor.js.map +1 -0
  12. package/dist/commands/init.js +2023 -0
  13. package/dist/commands/init.js.map +1 -0
  14. package/dist/commands/registry.js +42 -0
  15. package/dist/commands/registry.js.map +1 -0
  16. package/dist/config/check.js +505 -0
  17. package/dist/config/check.js.map +1 -0
  18. package/dist/config/io.js +177 -0
  19. package/dist/config/io.js.map +1 -0
  20. package/dist/config/model.js +406 -0
  21. package/dist/config/model.js.map +1 -0
  22. package/dist/core/errors.js +71 -0
  23. package/dist/core/errors.js.map +1 -0
  24. package/dist/core/git.js +537 -0
  25. package/dist/core/git.js.map +1 -0
  26. package/dist/core/json.js +125 -0
  27. package/dist/core/json.js.map +1 -0
  28. package/dist/core/layerCoverage.js +141 -0
  29. package/dist/core/layerCoverage.js.map +1 -0
  30. package/dist/core/layerGapRemedy.js +62 -0
  31. package/dist/core/layerGapRemedy.js.map +1 -0
  32. package/dist/core/nameList.js +23 -0
  33. package/dist/core/nameList.js.map +1 -0
  34. package/dist/core/paths.js +153 -0
  35. package/dist/core/paths.js.map +1 -0
  36. package/dist/core/prompt.js +206 -0
  37. package/dist/core/prompt.js.map +1 -0
  38. package/dist/core/repoPaths.js +55 -0
  39. package/dist/core/repoPaths.js.map +1 -0
  40. package/dist/core/report.js +150 -0
  41. package/dist/core/report.js.map +1 -0
  42. package/dist/core/templating.js +88 -0
  43. package/dist/core/templating.js.map +1 -0
  44. package/dist/core/writer.js +479 -0
  45. package/dist/core/writer.js.map +1 -0
  46. package/dist/daemon/backend.js +180 -0
  47. package/dist/daemon/backend.js.map +1 -0
  48. package/dist/daemon/units.js +380 -0
  49. package/dist/daemon/units.js.map +1 -0
  50. package/dist/detect/nestedApplication.js +79 -0
  51. package/dist/detect/nestedApplication.js.map +1 -0
  52. package/dist/detect/presets.js +2033 -0
  53. package/dist/detect/presets.js.map +1 -0
  54. package/dist/detect/signals.js +1368 -0
  55. package/dist/detect/signals.js.map +1 -0
  56. package/dist/doctor/checks.js +3530 -0
  57. package/dist/doctor/checks.js.map +1 -0
  58. package/dist/generators/claudeContext.js +588 -0
  59. package/dist/generators/claudeContext.js.map +1 -0
  60. package/dist/generators/githooks.js +446 -0
  61. package/dist/generators/githooks.js.map +1 -0
  62. package/dist/generators/harnessConfig.js +632 -0
  63. package/dist/generators/harnessConfig.js.map +1 -0
  64. package/dist/generators/notifications.js +191 -0
  65. package/dist/generators/notifications.js.map +1 -0
  66. package/dist/generators/outerLoopScripts.js +165 -0
  67. package/dist/generators/outerLoopScripts.js.map +1 -0
  68. package/dist/generators/permissionProfile.js +1172 -0
  69. package/dist/generators/permissionProfile.js.map +1 -0
  70. package/dist/generators/projectSettings.js +322 -0
  71. package/dist/generators/projectSettings.js.map +1 -0
  72. package/dist/generators/repoRoot.js +417 -0
  73. package/dist/generators/repoRoot.js.map +1 -0
  74. package/dist/generators/scripts.js +557 -0
  75. package/dist/generators/scripts.js.map +1 -0
  76. package/dist/generators/stateDir.js +221 -0
  77. package/dist/generators/stateDir.js.map +1 -0
  78. package/dist/machine/paths.js +111 -0
  79. package/dist/machine/paths.js.map +1 -0
  80. package/dist/machine/plugins.js +224 -0
  81. package/dist/machine/plugins.js.map +1 -0
  82. package/dist/machine/registry.js +330 -0
  83. package/dist/machine/registry.js.map +1 -0
  84. package/package.json +23 -0
  85. package/scripts/README.md +13 -0
  86. package/scripts/daemon/launchd.plist.template +59 -0
  87. package/scripts/daemon/systemd.service.template +58 -0
  88. package/templates/README.md +15 -0
  89. package/templates/claude/CLAUDE.md +54 -0
  90. package/templates/claude/README.md +5 -0
  91. package/templates/claude/context/api.md +29 -0
  92. package/templates/claude/context/conventions.md +23 -0
  93. package/templates/claude/context/data-layer.md +28 -0
  94. package/templates/claude/context/data-storage.md +29 -0
  95. package/templates/claude/context/docs-catalog.md +29 -0
  96. package/templates/claude/context/domain.md +28 -0
  97. package/templates/claude/context/layer.md +20 -0
  98. package/templates/claude/context/module.md +30 -0
  99. package/templates/claude/context/package.md +29 -0
  100. package/templates/claude/context/presentation.md +32 -0
  101. package/templates/claude/context/state-slices.md +28 -0
  102. package/templates/claude/context/tests.md +28 -0
  103. package/templates/claude/harness-task-offer.md +58 -0
  104. package/templates/claude/push-notify.env.example +21 -0
  105. package/templates/claude/qa-accounts.env.example +38 -0
  106. package/templates/claude/qa_test_scenarios.md +110 -0
  107. package/templates/claude/settings.autonomous.json +93 -0
  108. package/templates/claude/settings.autonomous.qa.json +36 -0
  109. package/templates/githooks/README.md +3 -0
  110. package/templates/githooks/pre-push +72 -0
  111. package/templates/repo/README.md +3 -0
  112. package/templates/repo/gitattributes +16 -0
  113. package/templates/repo/gitignore +61 -0
  114. package/templates/repo/gitignore.qa +25 -0
  115. package/templates/repo/mcp.json +17 -0
  116. package/templates/scripts/README.md +5 -0
  117. package/templates/scripts/autonomous-format-stream.sh +95 -0
  118. package/templates/scripts/autonomous-notify.sh +337 -0
  119. package/templates/scripts/autonomous-watcher.sh +3087 -0
  120. package/templates/scripts/cleanup-merged-worktrees.sh +327 -0
  121. package/templates/scripts/commit-on-branch.sh +288 -0
  122. package/templates/scripts/create-worktree.sh +360 -0
  123. package/templates/scripts/deploy.sh +47 -0
  124. package/templates/scripts/lib/harness-run-lib.sh +1481 -0
  125. package/templates/scripts/push-branch.sh +140 -0
  126. package/templates/scripts/refresh-branch.sh +244 -0
  127. package/templates/scripts/restart-watcher.sh +401 -0
  128. package/templates/scripts/scratch-run.sh +302 -0
  129. package/templates/scripts/setup-worktree.sh +262 -0
  130. package/templates/scripts/start-dev-server.sh +99 -0
  131. package/templates/scripts/test.sh +50 -0
  132. package/templates/scripts/typecheck.sh +50 -0
  133. package/templates/state-dir/README-root.md +13 -0
  134. package/templates/state-dir/README.md +9 -0
  135. package/templates/state-dir/architecture_branch_review_point_reviews/README.md +9 -0
  136. package/templates/state-dir/architecture_branch_reviews/README.md +9 -0
  137. package/templates/state-dir/architecture_reviews/README.md +9 -0
  138. package/templates/state-dir/architecture_user_review_reviews/README.md +9 -0
  139. package/templates/state-dir/autonomous_inbox/README.md +9 -0
  140. package/templates/state-dir/autonomous_logs/README.md +9 -0
  141. package/templates/state-dir/branch_statistics/README.md +9 -0
  142. package/templates/state-dir/business_parity_branch_review_point_reviews/README.md +9 -0
  143. package/templates/state-dir/business_parity_branch_reviews/README.md +9 -0
  144. package/templates/state-dir/business_parity_reviews/README.md +9 -0
  145. package/templates/state-dir/business_parity_user_review_reviews/README.md +9 -0
  146. package/templates/state-dir/clarification_digests/README.md +9 -0
  147. package/templates/state-dir/clarifications/README.md +9 -0
  148. package/templates/state-dir/code_reviews/README.md +9 -0
  149. package/templates/state-dir/dispatch_additions/README.md +19 -0
  150. package/templates/state-dir/docs_catalog/README.md +9 -0
  151. package/templates/state-dir/flow_progress/README.md +9 -0
  152. package/templates/state-dir/improvement_observations/README.md +19 -0
  153. package/templates/state-dir/improvement_suggestions.md +29 -0
  154. package/templates/state-dir/lessons.md +23 -0
  155. package/templates/state-dir/qa_review_point_reviews/README.md +9 -0
  156. package/templates/state-dir/qa_reviews/README.md +9 -0
  157. package/templates/state-dir/review_plan_point_reviews/README.md +9 -0
  158. package/templates/state-dir/review_plan_reviews/README.md +9 -0
  159. package/templates/state-dir/scratch/README.md +11 -0
  160. package/templates/state-dir/skeptic_review_plan_reviews/README.md +9 -0
  161. package/templates/state-dir/skeptic_review_point_reviews/README.md +9 -0
  162. package/templates/state-dir/skeptic_reviews/README.md +9 -0
  163. package/templates/state-dir/story_plans/README.md +9 -0
  164. package/templates/state-dir/task_plan_point_reviews/README.md +9 -0
  165. package/templates/state-dir/task_plan_reviews/README.md +9 -0
  166. package/templates/state-dir/task_plans/README.md +9 -0
  167. package/templates/state-dir/task_prompts/README.md +9 -0
  168. package/templates/state-dir/ui_test_plan_reviews/README.md +9 -0
  169. package/templates/state-dir/ui_test_plans/README.md +9 -0
  170. package/templates/state-dir/user_review_fix_plan_point_reviews/README.md +9 -0
  171. package/templates/state-dir/user_reviews/README.md +9 -0
@@ -0,0 +1,417 @@
1
+ /**
2
+ * Generator: the files `init` writes at the adopting repository's own root — the ignore rules for
3
+ * the machine-local files the harness configures by path, the line-ending attributes that keep its
4
+ * shell assets runnable, and the browser wiring the interactive test phase needs.
5
+ *
6
+ * **The rule this module exists to enforce: nothing here may take a file the adopter already owns
7
+ * away from them.** All three targets are files a repository is likely to have before the harness
8
+ * arrives, and two of them are shared with tooling the harness knows nothing about. So the two
9
+ * shared ones are merged rather than written: `.gitignore` gains a managed block holding only the
10
+ * lines that are not already somewhere in the file, and `.mcp.json` gains only the servers it does
11
+ * not already declare. `.gitattributes` is the one create-if-absent target, because a repository
12
+ * that has one has already made every decision this template would otherwise make for it.
13
+ *
14
+ * ## Four non-obvious choices, and where each comes from
15
+ *
16
+ * 1. **What is ignored is the *file*, never the *path*.** `pushEnvPath`, `qa.credentialsPath` and
17
+ * the client env file at `<appDir>/.env` name machine-local files whose location is repo-scoped
18
+ * configuration — the same for everyone who clones — which is why the path is in
19
+ * `harness.config.json` and only the contents stay local (`schemas/harness.config.schema.json`).
20
+ * Each is ignored under the path the config actually holds, so an adopter who moves one moves its
21
+ * ignore rule with it, and the committed `.example` sibling a fresh clone copies from is
22
+ * deliberately *not* matched by any of the three. The third differs from the other two only in how
23
+ * the config names it — its **directory** is the configured value and its basename is fixed — and
24
+ * it is here because `scripts/setup-worktree.sh` symlinks it into every working copy it
25
+ * bootstraps, so without the rule every fresh worktree opens with an untracked entry.
26
+ * 2. **A directory the tree generator gives a README is ignored by its contents, never as a
27
+ * directory.** That README is the committed contract for what belongs in the directory
28
+ * (`generators/stateDir.ts`), while what the run daemon writes beside it — the transcripts in
29
+ * `autonomous_logs`, the dropped prompts in `autonomous_inbox` — is machine-local. An ignore rule
30
+ * on the directory would take the README with it and could not be undone by a negation: git
31
+ * cannot re-include a file whose parent directory is excluded. Hence `…/autonomous_logs/*` plus
32
+ * `!…/autonomous_logs/README.md`, and the same pair for `autonomous_inbox` and for
33
+ * `clarifications`, rather than one directory rule each. The browser group
34
+ * ({@link TEMPLATES.gitignoreQa}) is the converse case and is spelled the converse way: none of
35
+ * its directories carries a committed contract file, so each is a whole-directory rule and none
36
+ * goes through {@link treeDirectory} — {@link QA_ARTIFACTS_DIR} deliberately has no
37
+ * `STATE_DIR_ENTRIES` row.
38
+ * 3. **`.mcp.json` is written when `phases.qa` is on *and* `qa.driver` is `web-playwright`, and in
39
+ * no other case** ({@link browserWiringApplies}), so an adopter who never drives a browser pays
40
+ * neither the browser tool schemas in every session's context nor a launched browser process
41
+ * (`cli/templates/repo/README.md`). A **mobile** driver gets a note and no file: the two mobile
42
+ * variants of the interactive test agent ship declared-not-implemented with built-ins-only tool
43
+ * allowlists, so a block written for them would declare servers nothing can ever start. The
44
+ * permission profile gates the same wiring from the other side, and
45
+ * {@link assertServersMatchProfile} checks the two agree: the profile's interactive-test fragment
46
+ * names the servers it starts, and a server it starts that this file does not declare never
47
+ * starts at all — an un-loaded tool stalls an unattended run rather than failing it, which is the
48
+ * one failure mode both files exist to prevent.
49
+ * 4. **The block covers files a harness command creates, and of the `.bak` siblings only the
50
+ * configuration's.** OS and editor noise (`.DS_Store`, `*~`) is per-developer and per-OS, so it
51
+ * belongs in the adopter's own ignore file or a global `core.excludesFile` and not in a block
52
+ * every adopter shares. The `.bak` copies `init --force` takes of every other generated file are
53
+ * the adopter's previous copies of files they edit, from a deliberate regeneration whose value is
54
+ * that they stay visible — only the {@link CONFIG_FILENAME} `.bak`, which `config set` writes on
55
+ * the documented path, is ignored, and it is ignored root-anchored. A bare `*.bak` has no path
56
+ * component and so matches at every depth of the adopter's tree; this block is append-only, so a
57
+ * too-wide rule can be withdrawn only by hand in every adopter's file.
58
+ *
59
+ * Nothing here touches the filesystem beyond reading its templates: the generator plans, and `init`
60
+ * applies the plan once.
61
+ */
62
+ import { join } from 'node:path';
63
+ import { browserWiringApplies, CONFIG_FILENAME, DEFAULTS } from '../config/model.js';
64
+ import { internal } from '../core/errors.js';
65
+ import { isJsonObject } from '../core/json.js';
66
+ import { readTemplate } from '../core/paths.js';
67
+ import { normalizeRepoDir } from '../core/repoPaths.js';
68
+ import { renderTemplate } from '../core/templating.js';
69
+ import { PUSH_ENV_PATH, QA_CREDENTIALS_PATH } from './harnessConfig.js';
70
+ import { QA_TEMPLATE_PATH as QA_PROFILE_FRAGMENT } from './permissionProfile.js';
71
+ import { STATE_DIR_ENTRIES } from './stateDir.js';
72
+ /** The templates' subdirectory under `cli/templates/`, addressed as {@link readTemplate} wants it. */
73
+ const TEMPLATE_DIR = 'repo';
74
+ /**
75
+ * The templates, stored dot-less and given their dot here.
76
+ *
77
+ * The naming rule is `cli/templates/README.md`'s and it is load-bearing twice over: a real
78
+ * `.gitignore` inside the harness repository would be honoured by git and could exclude files that
79
+ * are meant to ship, and a packaged one is rewritten to `.npmignore` on publish, so a dot-named
80
+ * template does not survive the wire.
81
+ *
82
+ * `gitignoreQa` is a **fragment** of `gitignore` rather than a file of its own, on the pattern
83
+ * `claude/settings.autonomous.qa.json` already sets: it is rendered into the `qaBrowserArtifacts`
84
+ * token when — and only when — {@link browserWiringApplies} holds. Its rules and *its comments* are
85
+ * then both absent from a repository that drives no browser, which one token per rule in the parent
86
+ * template could not do: the write engine drops a blank line but keeps a comment, so gated rules with
87
+ * an ungated comment group would leave the file explaining rules it does not carry.
88
+ */
89
+ const TEMPLATES = Object.freeze({
90
+ gitignore: 'gitignore',
91
+ gitignoreQa: 'gitignore.qa',
92
+ gitattributes: 'gitattributes',
93
+ mcp: 'mcp.json',
94
+ });
95
+ /** Where each of those lands, relative to the adopting repository's root. */
96
+ export const GITIGNORE_PATH = '.gitignore';
97
+ export const GITATTRIBUTES_PATH = '.gitattributes';
98
+ export const MCP_PATH = '.mcp.json';
99
+ /**
100
+ * The line introducing the managed block in `.gitignore`, and the whole of how a re-run recognises
101
+ * its own lines.
102
+ *
103
+ * It has to stay byte-stable across releases: the write engine finds the block by matching this
104
+ * exact line, so changing it would make the next `init` add a second block rather than extend the
105
+ * first. It names the block's extent — the run of non-blank lines beneath it — because that is what
106
+ * the engine actually maintains: a later run's additions are inserted at the end of that run.
107
+ */
108
+ export const GITIGNORE_BLOCK_HEADER = '# >>> autonomous-sdlc-harness (managed block: this line to the next blank line; re-run init to extend it) >>>';
109
+ /**
110
+ * The run daemon's log directory, whose contents are machine-local.
111
+ *
112
+ * This and {@link INBOX_DIR} are named here as strings and checked against {@link STATE_DIR_ENTRIES}
113
+ * by {@link treeDirectory} rather than imported as rows, because that table has no "is this directory
114
+ * machine-local" column and inventing one would put the ignore decision in the generator that writes
115
+ * the tree. The check is what keeps the two in step: rename a directory there and this generator
116
+ * fails loudly instead of ignoring a path the tree no longer has.
117
+ */
118
+ const LOGS_DIR = 'autonomous_logs';
119
+ /** The unattended loop's drop point for prompts, whose contents are machine-local likewise. */
120
+ const INBOX_DIR = 'autonomous_inbox';
121
+ /**
122
+ * The park-and-ask channel, whose questions and answers are machine-local.
123
+ *
124
+ * It has a row in {@link STATE_DIR_ENTRIES} and therefore a committed README of its own, so it is
125
+ * ignored **by its contents** with a negation for that README, exactly as {@link LOGS_DIR} and
126
+ * {@link INBOX_DIR} are — a directory rule here would take the README with it and no negation could
127
+ * bring it back. Its questions sit one level further down, under a `<branch>` subdirectory, which
128
+ * the contents glob covers along with everything else beneath the directory.
129
+ * {@link clarificationsIgnoreRules} builds all three spellings of that decision — including the
130
+ * whole-directory rule an earlier release wrote — and goes through {@link treeDirectory}, which
131
+ * checks the row still **exists** for the same reason it checks for the other two.
132
+ */
133
+ const CLARIFICATIONS_DIR = 'clarifications';
134
+ /**
135
+ * The throwaway files an agent runs a probe or a mutation check from, which are machine-local and
136
+ * must never reach a commit — the property the directory's own README states as its contract.
137
+ *
138
+ * Named here as a string and checked against {@link STATE_DIR_ENTRIES} by {@link treeDirectory} for
139
+ * the reason {@link LOGS_DIR} gives, and ignored **by its contents** with a negation for its README
140
+ * for the reason the three above are.
141
+ */
142
+ const SCRATCH_DIR = 'scratch';
143
+ /**
144
+ * Every run-artifact directory ignored **by its contents** with its committed README excepted, in
145
+ * block order — the declaration of which contract files the managed block owes a negation to.
146
+ *
147
+ * {@link contentsIgnoredDirectories} exports it because `doctor` has to answer for each of these
148
+ * paths whether or not the negation for it is still in the adopter's file. A set read back out of
149
+ * that file loses the pair whose negation was deleted, which is the one state the check exists to
150
+ * catch. A directory added here is tested with no edit on the `doctor` side.
151
+ */
152
+ const CONTENTS_IGNORED_DIRS = Object.freeze([
153
+ Object.freeze({ dir: LOGS_DIR, role: "the run daemon's log directory" }),
154
+ Object.freeze({ dir: CLARIFICATIONS_DIR, role: 'the park-and-ask channel' }),
155
+ Object.freeze({ dir: INBOX_DIR, role: "the unattended loop's drop point for prompts" }),
156
+ Object.freeze({ dir: SCRATCH_DIR, role: 'the throwaway-probe directory' }),
157
+ ]);
158
+ /**
159
+ * The unattended loop's stop, pause and dispatch-count control files, written **flat** at the root
160
+ * of the run-artifact tree while a run is in flight.
161
+ *
162
+ * They must never be committed, and the tree around them is what makes that easy to get wrong: it is
163
+ * otherwise a committed tree, so a single `git add <stateDir>/` takes whichever of these happen to
164
+ * exist at that moment. A committed `STOP` is the worst of them — it halts every run for everyone
165
+ * who clones, at a step whose own instruction forbids deleting the file.
166
+ *
167
+ * There is no run-control *subdirectory* to ignore instead: the flow keeps these flat because on a
168
+ * case-insensitive filesystem `PAUSE` and a `pause/` directory are the same path and collide. So
169
+ * each name here becomes its own file rule.
170
+ */
171
+ const RUN_CONTROL_ARTIFACTS = Object.freeze([
172
+ '.dispatch_counter',
173
+ 'STOP',
174
+ 'AUTONOMOUS_STOP',
175
+ 'PAUSE',
176
+ 'RESUME',
177
+ 'PAUSE_ACK',
178
+ 'PAUSE_PROGRESS.md',
179
+ ]);
180
+ /**
181
+ * Where the interactive-test agent sends a screenshot, under the run-artifact tree.
182
+ *
183
+ * It is **not** a {@link STATE_DIR_ENTRIES} row and does not go through {@link treeDirectory}, and
184
+ * both are deliberate: the tree generator neither creates it nor commits a contract file into it, so
185
+ * it is ignored as a whole directory like the two browser-server directories beside it rather than by
186
+ * its contents. Routing it through that helper would fail the run for a row nothing is meant to add.
187
+ */
188
+ const QA_ARTIFACTS_DIR = 'qa_artifacts';
189
+ /** The client env file's fixed basename; its directory is `appDir`, which is the configured half. */
190
+ const CLIENT_ENV_FILENAME = '.env';
191
+ /** The per-directory contract file the tree generator writes, which the ignore rule excepts. */
192
+ const README_FILENAME = 'README.md';
193
+ /** The key `.mcp.json` declares its servers under. */
194
+ const SERVERS_KEY = 'mcpServers';
195
+ /** The key the permission profile's interactive-test fragment starts those servers with. */
196
+ const ENABLED_SERVERS_KEY = 'enabledMcpjsonServers';
197
+ /**
198
+ * Render a repo-root template through {@link renderTemplate}.
199
+ *
200
+ * **`assertNoneSurvive` is off**, because every value substituted here is a configured path and a
201
+ * configured path is the adopter's to write. One carrying `{{…}}` would be a strange thing to put in
202
+ * `harness.config.json`, but it is not a fault in this CLI, and the post-check reports its findings
203
+ * as exactly that.
204
+ *
205
+ * **The lax {@link normalizeRepoDir} is what feeds it**, not the strict variant: a gitignore pattern
206
+ * is matched literally, so `./x` and `x` are two different rules and resolving a `..` in one would
207
+ * change which files it covers.
208
+ */
209
+ function render(template, values) {
210
+ return renderTemplate(readTemplate(`${TEMPLATE_DIR}/${template}`), values, {
211
+ describe: `the repo-root template ${template}`,
212
+ });
213
+ }
214
+ /** Read one of the CLI's templates and parse it as a JSON object. */
215
+ function parseTemplateObject(path) {
216
+ const text = readTemplate(path);
217
+ let parsed;
218
+ try {
219
+ parsed = JSON.parse(text);
220
+ }
221
+ catch (error) {
222
+ const detail = error instanceof Error ? error.message : String(error);
223
+ throw internal(`the template ${path} is not valid JSON (${detail})`);
224
+ }
225
+ if (!isJsonObject(parsed))
226
+ throw internal(`the template ${path} is not a JSON object`);
227
+ return parsed;
228
+ }
229
+ /** The servers the permission profile's interactive-test fragment starts for a run. */
230
+ function serversStartedByProfile() {
231
+ const fragment = parseTemplateObject(QA_PROFILE_FRAGMENT);
232
+ const enabled = fragment[ENABLED_SERVERS_KEY];
233
+ if (!Array.isArray(enabled)) {
234
+ throw internal(`the permission profile's interactive-test fragment ${QA_PROFILE_FRAGMENT} has no \`${ENABLED_SERVERS_KEY}\` list, so the servers it starts cannot be checked against the ones ${MCP_PATH} declares`);
235
+ }
236
+ return enabled.map((entry) => {
237
+ if (typeof entry !== 'string') {
238
+ throw internal(`\`${ENABLED_SERVERS_KEY}\` in ${QA_PROFILE_FRAGMENT} carries an entry that is not a string`);
239
+ }
240
+ return entry;
241
+ });
242
+ }
243
+ /**
244
+ * The declared servers and the started servers have to be the same set, and the check runs in
245
+ * **both** directions because the two disagreements fail differently and neither fails loudly:
246
+ *
247
+ * - a server the profile starts that this file does not declare never starts, so the tools the
248
+ * profile allow-listed do not exist at run time — and an un-loaded tool in print mode stalls
249
+ * rather than prompting, which is a run that parks with no diagnostic;
250
+ * - a server declared here that the profile does not start is wiring nothing enables, so it reads
251
+ * as available while no run can ever call it.
252
+ *
253
+ * Both are faults in these two shipped templates rather than in the adopting repository, so both
254
+ * exit {@link EXIT.INTERNAL}. The end-to-end pairing — the generated profile against the generated
255
+ * `.mcp.json` — is the fixture tests', which can see the files this module only plans.
256
+ */
257
+ function assertServersMatchProfile(servers) {
258
+ const declared = Object.keys(servers);
259
+ const started = serversStartedByProfile();
260
+ const undeclared = started.filter((name) => !declared.includes(name));
261
+ if (undeclared.length > 0) {
262
+ throw internal(`the permission profile's interactive-test fragment starts the MCP server(s) ${undeclared.join(', ')}, which the ${MCP_PATH} template does not declare: the server would never start, so the tools the profile allow-lists would not exist at run time and the phase would stall rather than fail`);
263
+ }
264
+ const unstarted = declared.filter((name) => !started.includes(name));
265
+ if (unstarted.length > 0) {
266
+ throw internal(`the ${MCP_PATH} template declares the MCP server(s) ${unstarted.join(', ')}, which the permission profile's interactive-test fragment does not start: the wiring would be present and unreachable`);
267
+ }
268
+ }
269
+ /** The `mcpServers` object of the browser-wiring template, checked against the profile fragment. */
270
+ function browserWiring() {
271
+ const template = parseTemplateObject(`${TEMPLATE_DIR}/${TEMPLATES.mcp}`);
272
+ const servers = template[SERVERS_KEY];
273
+ if (!isJsonObject(servers)) {
274
+ throw internal(`the ${MCP_PATH} template has no \`${SERVERS_KEY}\` object, so there is nothing to merge`);
275
+ }
276
+ assertServersMatchProfile(servers);
277
+ return template;
278
+ }
279
+ /**
280
+ * One machine-local run-daemon directory, repo-relative, having checked the tree still has a row for
281
+ * it. `role` names what the directory is for and what a stale rule would let through, so the two
282
+ * callers fail with their own diagnosis rather than a shared one.
283
+ */
284
+ function treeDirectory(stateDir, dir, role) {
285
+ if (!STATE_DIR_ENTRIES.some((entry) => entry.dir === dir)) {
286
+ throw internal(`the ignore rules name ${dir} as ${role}, which the run-artifact tree no longer has: the generated rule would ignore a path nothing writes to, and the directory that replaced it would be committed file by file`);
287
+ }
288
+ return `${stateDir}/${dir}`;
289
+ }
290
+ /** One row of {@link CONTENTS_IGNORED_DIRS}, spelled for a configured `stateDir`. */
291
+ function contentsIgnoredDirectory(stateDir, dir) {
292
+ const row = CONTENTS_IGNORED_DIRS.find((entry) => entry.dir === dir);
293
+ if (row === undefined) {
294
+ throw internal(`${dir} is not one of the contents-ignored directories this generator declares`);
295
+ }
296
+ const directory = treeDirectory(normalizeRepoDir(stateDir), row.dir, row.role);
297
+ const readmePath = `${directory}/${README_FILENAME}`;
298
+ return { directory, contents: `${directory}/*`, readmePath, readmeException: `!${readmePath}` };
299
+ }
300
+ /**
301
+ * All of them, for one configured `stateDir` — the set of committed contract files the managed block
302
+ * has to leave committable, taken from this generator rather than from any file it wrote.
303
+ *
304
+ * Exported for `doctor`, which asks git about each of these paths: derived from the artifact under
305
+ * audit, the question would go missing along with the negation line that answers it.
306
+ */
307
+ export function contentsIgnoredDirectories(stateDir) {
308
+ return CONTENTS_IGNORED_DIRS.map((row) => contentsIgnoredDirectory(stateDir, row.dir));
309
+ }
310
+ /**
311
+ * Build them for one configured `stateDir`, having checked the tree still has a row for the channel:
312
+ * a rule spelled for a directory the tree no longer writes is a rule that covers nothing, and the
313
+ * directory that replaced it would be committed file by file.
314
+ */
315
+ export function clarificationsIgnoreRules(stateDir) {
316
+ const channel = contentsIgnoredDirectory(stateDir, CLARIFICATIONS_DIR);
317
+ return { ...channel, supersededDirectoryRule: `${channel.directory}/` };
318
+ }
319
+ /**
320
+ * Enqueue the repository-root files: the ignore block (`merge-lines`), the line-ending attributes
321
+ * (`create-if-absent`), and — only where {@link browserWiringApplies} holds, i.e. the interactive
322
+ * test phase enabled **and** its driver the browser one — the browser wiring (`merge-json`).
323
+ *
324
+ * Each is one action in the run's log, so a re-run reports per file whether it was created, merged
325
+ * into or left alone rather than reporting "repository root" once.
326
+ */
327
+ export function writeRepoRootFiles({ repoRoot, config, plan }) {
328
+ const files = [];
329
+ const notes = [];
330
+ const stateDir = normalizeRepoDir(config.stateDir ?? DEFAULTS.stateDir);
331
+ const logs = contentsIgnoredDirectory(stateDir, LOGS_DIR);
332
+ const inbox = contentsIgnoredDirectory(stateDir, INBOX_DIR);
333
+ const scratch = contentsIgnoredDirectory(stateDir, SCRATCH_DIR);
334
+ const clarifications = clarificationsIgnoreRules(stateDir);
335
+ const pushEnv = normalizeRepoDir(config.pushEnvPath ?? PUSH_ENV_PATH);
336
+ // The phase, not the driver: every driver reads a credentials file, so the ignore rule below is
337
+ // gated on the phase alone. The browser wiring is the one decision that also reads the driver.
338
+ const qaEnabled = config.phases?.qa === true;
339
+ const wiresBrowser = browserWiringApplies(config);
340
+ // Empty when the phase is off: the write engine drops blank lines from a managed block, so the
341
+ // rule is simply absent rather than being written as a pattern matching nothing.
342
+ const credentials = qaEnabled ? normalizeRepoDir(config.qa?.credentialsPath ?? QA_CREDENTIALS_PATH) : '';
343
+ // The repository root is `.`, and `./.env` and `.env` are two different gitignore patterns: a
344
+ // pattern is matched literally, so the root case is spelled as the bare filename.
345
+ const appDir = normalizeRepoDir(config.appDir ?? DEFAULTS.appDir);
346
+ const clientEnv = appDir === '.' ? CLIENT_ENV_FILENAME : `${appDir}/${CLIENT_ENV_FILENAME}`;
347
+ // The browser group, gated on the DRIVER rather than the phase: only the browser driver starts an
348
+ // MCP server, so a mobile-driver repository — whose interactive-test variants ship
349
+ // declared-not-implemented with built-ins-only allowlists — has nothing that could write any of
350
+ // these paths. Empty otherwise, and the write engine drops the blank line, so the rules and the
351
+ // comments explaining them are simply absent rather than present and inert.
352
+ const qaBrowserArtifacts = wiresBrowser
353
+ ? render(TEMPLATES.gitignoreQa, { qaArtifactsDir: `${stateDir}/${QA_ARTIFACTS_DIR}/` }).trimEnd()
354
+ : '';
355
+ const ignoreBlock = render(TEMPLATES.gitignore, {
356
+ pushEnvPath: pushEnv,
357
+ qaCredentialsPath: credentials,
358
+ clientEnvPath: clientEnv,
359
+ logsGlob: logs.contents,
360
+ logsReadmeException: logs.readmeException,
361
+ // One token carrying seven rules: the names are declared once, above, and the template holds one
362
+ // comment for the group rather than seven near-identical ones.
363
+ runControlArtifacts: RUN_CONTROL_ARTIFACTS.map((name) => `${stateDir}/${name}`).join('\n'),
364
+ clarificationsGlob: clarifications.contents,
365
+ clarificationsReadmeException: clarifications.readmeException,
366
+ inboxGlob: inbox.contents,
367
+ inboxReadmeException: inbox.readmeException,
368
+ scratchGlob: scratch.contents,
369
+ scratchReadmeException: scratch.readmeException,
370
+ // Derived from the configuration's own filename, so a rename moves its ignore rule with it.
371
+ configBackupFile: `${CONFIG_FILENAME}.bak`,
372
+ qaBrowserArtifacts,
373
+ });
374
+ plan.add({
375
+ path: join(repoRoot, GITIGNORE_PATH),
376
+ policy: 'merge-lines',
377
+ content: ignoreBlock,
378
+ blockHeader: GITIGNORE_BLOCK_HEADER,
379
+ label: 'ignore rules for the harness machine-local files',
380
+ });
381
+ files.push(GITIGNORE_PATH);
382
+ plan.add({
383
+ path: join(repoRoot, GITATTRIBUTES_PATH),
384
+ policy: 'create-if-absent',
385
+ content: render(TEMPLATES.gitattributes, {
386
+ githooksDir: normalizeRepoDir(config.githooksDir ?? DEFAULTS.githooksDir),
387
+ }),
388
+ label: 'line-ending attributes',
389
+ });
390
+ files.push(GITATTRIBUTES_PATH);
391
+ if (wiresBrowser) {
392
+ plan.add({
393
+ path: join(repoRoot, MCP_PATH),
394
+ policy: 'merge-json',
395
+ content: browserWiring(),
396
+ label: 'browser MCP wiring',
397
+ });
398
+ files.push(MCP_PATH);
399
+ }
400
+ else if (qaEnabled) {
401
+ // The phase is on and its driver reaches the application some other way. A distinct note, not
402
+ // the phase-off one: "turn the phase on" is advice this adopter has already taken, and the thing
403
+ // they need to know is that the omission is the driver's doing and is not a gap to fill in.
404
+ notes.push(`no ${MCP_PATH} was written because qa.driver is ${config.qa?.driver ?? DEFAULTS.qa.driver}, which reaches the application through a device runner rather than a browser: the mobile interactive-test variants are declared-not-implemented in this release and carry built-ins-only tool allowlists, so there is no MCP server for them to start. The permission profile omits its browser half for the same reason. To wire a browser instead, change the driver in ${CONFIG_FILENAME} — by hand, or with \`config set qa.driver web-playwright\`, which copies the file to a .bak sibling before replacing it — then re-run \`init --force\`, which moves the half a plain re-run cannot: an ordinary re-run merges ${MCP_PATH} in but keeps the create-if-absent permission profile, leaving the profile starting none of the servers ${MCP_PATH} declares — which stalls the phase rather than failing it — while --force regenerates that profile from the config in effect. --force overwrites generated files but never ${CONFIG_FILENAME}, which init reads on every run, so the driver just set is the one both halves are wired from, and everything --force regenerates is copied to a .bak sibling first`);
405
+ }
406
+ else {
407
+ notes.push(`no ${MCP_PATH} was written, because phases.qa is off and nothing would drive a browser: an adopter who never runs the interactive test phase pays neither the browser tool schemas in every session's context nor a launched browser process. Turn the phase on in ${CONFIG_FILENAME} — by hand, or with \`config set phases.qa true\` (and \`config set qa.driver <driver>\` for a driver other than the default), which copies the file to a .bak sibling before replacing it — then re-run \`init --force\`, which moves the half a plain re-run cannot: an ordinary re-run merges ${MCP_PATH} in but keeps the create-if-absent permission profile, which would then start none of the servers it declares, while --force regenerates that profile from the config in effect. --force overwrites generated files but never ${CONFIG_FILENAME}, which init reads on every run, so the phase just turned on is the one both halves are wired from, and everything --force regenerates is copied to a .bak sibling first`);
408
+ }
409
+ // The patterns, in block order and without the comment lines, so a caller can list what is now
410
+ // ignored without re-deriving it from the rendered text.
411
+ const ignored = ignoreBlock
412
+ .split('\n')
413
+ .map((line) => line.trim())
414
+ .filter((line) => line !== '' && !line.startsWith('#'));
415
+ return { files, ignored, mcpWritten: wiresBrowser, notes };
416
+ }
417
+ //# sourceMappingURL=repoRoot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repoRoot.js","sourceRoot":"","sources":["../../src/generators/repoRoot.ts"],"names":[],"mappings":"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"}