@ai-sdlc/orchestrator 0.6.0 → 0.10.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 (209) hide show
  1. package/dist/__test-helpers/git-env.d.ts +40 -0
  2. package/dist/__test-helpers/git-env.js +73 -0
  3. package/dist/admission-composite.d.ts +89 -0
  4. package/dist/admission-composite.js +241 -0
  5. package/dist/admission-enrichment.d.ts +142 -0
  6. package/dist/admission-enrichment.js +341 -0
  7. package/dist/admission-hc.d.ts +81 -0
  8. package/dist/admission-hc.js +94 -0
  9. package/dist/admission-score.d.ts +109 -5
  10. package/dist/admission-score.js +90 -11
  11. package/dist/analysis/analyzer.js +3 -2
  12. package/dist/analysis/convention-detector.d.ts +85 -2
  13. package/dist/analysis/convention-detector.js +375 -70
  14. package/dist/analysis/diff-analyzer.d.ts +33 -0
  15. package/dist/analysis/diff-analyzer.js +122 -0
  16. package/dist/analysis/hotspot-analyzer.js +3 -1
  17. package/dist/analysis/index.d.ts +2 -1
  18. package/dist/analysis/index.js +2 -1
  19. package/dist/artifacts/index.d.ts +65 -0
  20. package/dist/artifacts/index.js +142 -0
  21. package/dist/backlog-adapter.d.ts +121 -0
  22. package/dist/backlog-adapter.js +438 -0
  23. package/dist/calibration.d.ts +43 -0
  24. package/dist/calibration.js +76 -0
  25. package/dist/cli/commands/agents.d.ts +24 -0
  26. package/dist/cli/commands/agents.js +66 -1
  27. package/dist/cli/commands/git-remote.d.ts +74 -0
  28. package/dist/cli/commands/git-remote.js +170 -0
  29. package/dist/cli/commands/health.d.ts +4 -0
  30. package/dist/cli/commands/health.js +13 -1
  31. package/dist/cli/commands/init-features.d.ts +208 -0
  32. package/dist/cli/commands/init-features.js +473 -0
  33. package/dist/cli/commands/init-templates.d.ts +104 -0
  34. package/dist/cli/commands/init-templates.js +399 -0
  35. package/dist/cli/commands/init.d.ts +48 -0
  36. package/dist/cli/commands/init.js +322 -23
  37. package/dist/cli/commands/mcp-setup.d.ts +31 -0
  38. package/dist/cli/commands/mcp-setup.js +78 -8
  39. package/dist/cli/formatters/table.js +15 -2
  40. package/dist/cli/index.d.ts +14 -1
  41. package/dist/cli/index.js +81 -20
  42. package/dist/cli/versions.d.ts +57 -0
  43. package/dist/cli/versions.js +128 -0
  44. package/dist/code-area-classifier.d.ts +21 -0
  45. package/dist/code-area-classifier.js +48 -0
  46. package/dist/config.d.ts +33 -1
  47. package/dist/config.js +78 -8
  48. package/dist/database/adapters/external.d.ts +24 -0
  49. package/dist/database/adapters/external.js +80 -0
  50. package/dist/database/adapters/neon.d.ts +41 -0
  51. package/dist/database/adapters/neon.js +98 -0
  52. package/dist/database/adapters/pg-snapshot-restore.d.ts +28 -0
  53. package/dist/database/adapters/pg-snapshot-restore.js +68 -0
  54. package/dist/database/adapters/sqlite-copy.d.ts +32 -0
  55. package/dist/database/adapters/sqlite-copy.js +145 -0
  56. package/dist/database/connection-injection.d.ts +35 -0
  57. package/dist/database/connection-injection.js +93 -0
  58. package/dist/database/index.d.ts +12 -0
  59. package/dist/database/index.js +23 -0
  60. package/dist/database/registry.d.ts +13 -0
  61. package/dist/database/registry.js +27 -0
  62. package/dist/database/topology.d.ts +52 -0
  63. package/dist/database/topology.js +44 -0
  64. package/dist/database/types.d.ts +89 -0
  65. package/dist/database/types.js +26 -0
  66. package/dist/design-authority.d.ts +53 -0
  67. package/dist/design-authority.js +84 -0
  68. package/dist/design-lookahead.d.ts +64 -0
  69. package/dist/design-lookahead.js +86 -0
  70. package/dist/design-quality-trend.d.ts +87 -0
  71. package/dist/design-quality-trend.js +190 -0
  72. package/dist/design-system-context.d.ts +46 -0
  73. package/dist/design-system-context.js +80 -0
  74. package/dist/design-system-correction-loop.d.ts +64 -0
  75. package/dist/design-system-correction-loop.js +128 -0
  76. package/dist/design-system-metrics.d.ts +61 -0
  77. package/dist/design-system-metrics.js +104 -0
  78. package/dist/design-system-stewardship.d.ts +22 -0
  79. package/dist/design-system-stewardship.js +85 -0
  80. package/dist/design-system-validation.d.ts +37 -0
  81. package/dist/design-system-validation.js +88 -0
  82. package/dist/dispatch/index.d.ts +4 -0
  83. package/dist/dispatch/index.js +4 -0
  84. package/dist/dispatch/merge-gate.d.ts +46 -0
  85. package/dist/dispatch/merge-gate.js +90 -0
  86. package/dist/dispatch/requeue.d.ts +57 -0
  87. package/dist/dispatch/requeue.js +131 -0
  88. package/dist/dispatch/worker-pool.d.ts +62 -0
  89. package/dist/dispatch/worker-pool.js +60 -0
  90. package/dist/execute.d.ts +40 -0
  91. package/dist/execute.js +188 -15
  92. package/dist/fix-ci.js +8 -1
  93. package/dist/fix-review.js +8 -1
  94. package/dist/harness/adapters/claude-code.d.ts +29 -0
  95. package/dist/harness/adapters/claude-code.js +191 -0
  96. package/dist/harness/adapters/codex.d.ts +25 -0
  97. package/dist/harness/adapters/codex.js +61 -0
  98. package/dist/harness/independence.d.ts +51 -0
  99. package/dist/harness/independence.js +67 -0
  100. package/dist/harness/index.d.ts +14 -0
  101. package/dist/harness/index.js +20 -0
  102. package/dist/harness/registry.d.ts +17 -0
  103. package/dist/harness/registry.js +31 -0
  104. package/dist/harness/types.d.ts +123 -0
  105. package/dist/harness/types.js +8 -0
  106. package/dist/harness/version-probe.d.ts +14 -0
  107. package/dist/harness/version-probe.js +123 -0
  108. package/dist/index.d.ts +30 -4
  109. package/dist/index.js +35 -2
  110. package/dist/models/classifier.d.ts +78 -0
  111. package/dist/models/classifier.js +277 -0
  112. package/dist/models/index.d.ts +3 -0
  113. package/dist/models/index.js +3 -0
  114. package/dist/models/registry.d.ts +97 -0
  115. package/dist/models/registry.js +173 -0
  116. package/dist/pillar-breakdown.d.ts +85 -0
  117. package/dist/pillar-breakdown.js +162 -0
  118. package/dist/priority.d.ts +5 -0
  119. package/dist/priority.js +18 -5
  120. package/dist/review-meta.d.ts +65 -0
  121. package/dist/review-meta.js +149 -0
  122. package/dist/review.d.ts +4 -0
  123. package/dist/review.js +12 -2
  124. package/dist/runners/claude-code-sdk.d.ts +31 -0
  125. package/dist/runners/claude-code-sdk.js +238 -0
  126. package/dist/runners/claude-code.d.ts +3 -0
  127. package/dist/runners/claude-code.js +30 -66
  128. package/dist/runners/codex.js +4 -1
  129. package/dist/runners/copilot.js +4 -1
  130. package/dist/runners/cursor.js +4 -1
  131. package/dist/runners/git-utils.d.ts +81 -0
  132. package/dist/runners/git-utils.js +201 -0
  133. package/dist/runners/index.d.ts +3 -0
  134. package/dist/runners/index.js +3 -0
  135. package/dist/runners/review-agent.d.ts +20 -0
  136. package/dist/runners/review-agent.js +171 -41
  137. package/dist/runners/runner-registry.js +10 -0
  138. package/dist/runners/sdk-review-runner.d.ts +65 -0
  139. package/dist/runners/sdk-review-runner.js +185 -0
  140. package/dist/runners/security-triage.d.ts +20 -4
  141. package/dist/runners/security-triage.js +39 -15
  142. package/dist/runners/types.d.ts +6 -0
  143. package/dist/runtime/attestations.d.ts +766 -0
  144. package/dist/runtime/attestations.js +1195 -0
  145. package/dist/runtime/git-env.d.ts +53 -0
  146. package/dist/runtime/git-env.js +60 -0
  147. package/dist/runtime/index.d.ts +7 -0
  148. package/dist/runtime/index.js +7 -0
  149. package/dist/runtime/parallelism-flag.d.ts +28 -0
  150. package/dist/runtime/parallelism-flag.js +39 -0
  151. package/dist/runtime/port-allocator.d.ts +32 -0
  152. package/dist/runtime/port-allocator.js +96 -0
  153. package/dist/runtime/worktree-pool.d.ts +86 -0
  154. package/dist/runtime/worktree-pool.js +204 -0
  155. package/dist/runtime/worktree.d.ts +25 -0
  156. package/dist/runtime/worktree.js +111 -0
  157. package/dist/sa-scoring/auto-calibrate.d.ts +69 -0
  158. package/dist/sa-scoring/auto-calibrate.js +107 -0
  159. package/dist/sa-scoring/c1-sa2-computable.d.ts +42 -0
  160. package/dist/sa-scoring/c1-sa2-computable.js +59 -0
  161. package/dist/sa-scoring/composite.d.ts +107 -0
  162. package/dist/sa-scoring/composite.js +139 -0
  163. package/dist/sa-scoring/depparse-client.d.ts +79 -0
  164. package/dist/sa-scoring/depparse-client.js +187 -0
  165. package/dist/sa-scoring/did-compiler.d.ts +122 -0
  166. package/dist/sa-scoring/did-compiler.js +286 -0
  167. package/dist/sa-scoring/drift-monitor.d.ts +84 -0
  168. package/dist/sa-scoring/drift-monitor.js +186 -0
  169. package/dist/sa-scoring/exemplar-bank.d.ts +78 -0
  170. package/dist/sa-scoring/exemplar-bank.js +154 -0
  171. package/dist/sa-scoring/feedback-store.d.ts +100 -0
  172. package/dist/sa-scoring/feedback-store.js +156 -0
  173. package/dist/sa-scoring/index.d.ts +71 -0
  174. package/dist/sa-scoring/index.js +158 -0
  175. package/dist/sa-scoring/layer1-deterministic.d.ts +115 -0
  176. package/dist/sa-scoring/layer1-deterministic.js +298 -0
  177. package/dist/sa-scoring/layer2-structural.d.ts +71 -0
  178. package/dist/sa-scoring/layer2-structural.js +151 -0
  179. package/dist/sa-scoring/layer3-llm.d.ts +86 -0
  180. package/dist/sa-scoring/layer3-llm.js +282 -0
  181. package/dist/sa-scoring/rescore-orchestrator.d.ts +52 -0
  182. package/dist/sa-scoring/rescore-orchestrator.js +47 -0
  183. package/dist/scheduling/burn-down.d.ts +27 -0
  184. package/dist/scheduling/burn-down.js +43 -0
  185. package/dist/scheduling/calibration.d.ts +42 -0
  186. package/dist/scheduling/calibration.js +150 -0
  187. package/dist/scheduling/index.d.ts +8 -0
  188. package/dist/scheduling/index.js +8 -0
  189. package/dist/scheduling/ledger.d.ts +59 -0
  190. package/dist/scheduling/ledger.js +216 -0
  191. package/dist/scheduling/off-peak.d.ts +27 -0
  192. package/dist/scheduling/off-peak.js +112 -0
  193. package/dist/scheduling/schedule-decision.d.ts +39 -0
  194. package/dist/scheduling/schedule-decision.js +89 -0
  195. package/dist/scheduling/tier-analysis.d.ts +47 -0
  196. package/dist/scheduling/tier-analysis.js +81 -0
  197. package/dist/scheduling/types.d.ts +140 -0
  198. package/dist/scheduling/types.js +11 -0
  199. package/dist/shared.d.ts +13 -0
  200. package/dist/shared.js +32 -0
  201. package/dist/state/schema.d.ts +5 -1
  202. package/dist/state/schema.js +223 -1
  203. package/dist/state/store.d.ts +55 -1
  204. package/dist/state/store.js +342 -7
  205. package/dist/state/types.d.ts +139 -0
  206. package/dist/types.d.ts +1 -1
  207. package/dist/validate-agent-output.js +4 -1
  208. package/dist/watch.js +6 -0
  209. package/package.json +3 -2
@@ -0,0 +1,74 @@
1
+ /**
2
+ * git remote parsing — extract org/repo from `git remote get-url origin`
3
+ * to substitute into pipeline.yaml during init.
4
+ *
5
+ * Why this exists: AISDLC-78 — fresh installs got a literal `your-org`
6
+ * placeholder in their pipeline.yaml, which made the file unrunnable
7
+ * until the user noticed and edited it. We auto-detect the org/repo
8
+ * from the git remote and only fall back to placeholders when no
9
+ * remote is configured (e.g. a brand-new local-only repo).
10
+ */
11
+ export interface RemoteInfo {
12
+ /** Organization or user name. */
13
+ org: string;
14
+ /** Repository name (without `.git` suffix). */
15
+ repo: string;
16
+ /** Whether the values came from a real git remote (vs the fallback). */
17
+ detected: boolean;
18
+ }
19
+ /**
20
+ * Parse a single remote URL into org/repo. Supports:
21
+ * - https://github.com/foo/bar.git
22
+ * - https://github.com/foo/bar
23
+ * - git@github.com:foo/bar.git
24
+ * - ssh://git@github.com/foo/bar.git
25
+ * - git@gitlab.example.com:group/subgroup/repo.git (org=group, repo=repo)
26
+ */
27
+ export declare function parseRemoteUrl(url: string): RemoteInfo | null;
28
+ export interface DetectRemoteOptions {
29
+ /** Override `execSync` for tests. */
30
+ execImpl?: (cmd: string, opts: {
31
+ cwd: string;
32
+ encoding: 'utf-8';
33
+ stdio: unknown;
34
+ }) => string;
35
+ /** Project directory (defaults to process.cwd). */
36
+ cwd?: string;
37
+ }
38
+ /**
39
+ * Detect the GitHub-style org/repo from the project's git origin remote.
40
+ * Returns FALLBACK with detected=false when no remote is configured or
41
+ * when the URL cannot be parsed.
42
+ *
43
+ * The git invocation is hardened against two failure modes (AISDLC-104):
44
+ *
45
+ * 1. **cwd inheritance race under parallel test workers.** Every git
46
+ * command uses `git -C <cwd>` so the working dir is pinned at the
47
+ * git argv level rather than relying solely on `child_process`
48
+ * honouring the `cwd:` spawn option. Both should agree, but `git -C`
49
+ * is a git-internal contract independent of any subprocess cwd
50
+ * inheritance race that can happen when `process.chdir()` is
51
+ * interleaved with subprocess spawn under thread/fork pools.
52
+ *
53
+ * 2. **Host-repo origin bleed via parent-directory walk-up.** If `cwd`
54
+ * contains an invalid `.git` (e.g. an empty directory left by an
55
+ * init test setup) git normally walks UP looking for a real `.git`
56
+ * and can resolve to an ancestor repository — i.e. when run from
57
+ * inside the ai-sdlc-framework checkout the test would silently see
58
+ * the framework's own origin rather than the fallback. We defend by
59
+ * calling `git rev-parse --show-toplevel` first and confirming the
60
+ * reported toplevel matches `cwd` (after symlink resolution). When
61
+ * it doesn't, we treat the directory as not-a-repo and return the
62
+ * fallback rather than reporting the ancestor's remote. This was
63
+ * preferred over `GIT_CEILING_DIRECTORIES` because the ceiling-list
64
+ * semantics only block walking INTO the listed dirs, not up FROM
65
+ * them — empirically `GIT_CEILING_DIRECTORIES=<cwd>` did not stop
66
+ * git from finding a parent repo.
67
+ */
68
+ export declare function detectGitRemote(opts?: DetectRemoteOptions): RemoteInfo;
69
+ /**
70
+ * Substitute `your-org` (and optionally `your-repo`) placeholders in a
71
+ * template body with detected values.
72
+ */
73
+ export declare function applyRemoteToPipelineYaml(template: string, info: RemoteInfo): string;
74
+ //# sourceMappingURL=git-remote.d.ts.map
@@ -0,0 +1,170 @@
1
+ /**
2
+ * git remote parsing — extract org/repo from `git remote get-url origin`
3
+ * to substitute into pipeline.yaml during init.
4
+ *
5
+ * Why this exists: AISDLC-78 — fresh installs got a literal `your-org`
6
+ * placeholder in their pipeline.yaml, which made the file unrunnable
7
+ * until the user noticed and edited it. We auto-detect the org/repo
8
+ * from the git remote and only fall back to placeholders when no
9
+ * remote is configured (e.g. a brand-new local-only repo).
10
+ */
11
+ import { execSync } from 'node:child_process';
12
+ import { realpathSync } from 'node:fs';
13
+ const FALLBACK = { org: 'your-org', repo: 'your-repo', detected: false };
14
+ /**
15
+ * Parse a single remote URL into org/repo. Supports:
16
+ * - https://github.com/foo/bar.git
17
+ * - https://github.com/foo/bar
18
+ * - git@github.com:foo/bar.git
19
+ * - ssh://git@github.com/foo/bar.git
20
+ * - git@gitlab.example.com:group/subgroup/repo.git (org=group, repo=repo)
21
+ */
22
+ export function parseRemoteUrl(url) {
23
+ const trimmed = url.trim();
24
+ if (!trimmed)
25
+ return null;
26
+ // SSH shorthand: git@host:org/repo.git
27
+ const sshShort = /^[^@\s]+@[^:\s]+:(.+?)\/([^/\s]+?)(?:\.git)?$/.exec(trimmed);
28
+ if (sshShort) {
29
+ return { org: sshShort[1].split('/').slice(-1)[0], repo: sshShort[2], detected: true };
30
+ }
31
+ // SSH or HTTPS with a scheme
32
+ let parsed = null;
33
+ try {
34
+ parsed = new URL(trimmed);
35
+ }
36
+ catch {
37
+ parsed = null;
38
+ }
39
+ if (parsed) {
40
+ const path = parsed.pathname.replace(/^\/+/, '').replace(/\.git$/, '');
41
+ const segments = path.split('/').filter(Boolean);
42
+ if (segments.length >= 2) {
43
+ const repo = segments[segments.length - 1];
44
+ const org = segments[segments.length - 2];
45
+ return { org, repo, detected: true };
46
+ }
47
+ }
48
+ return null;
49
+ }
50
+ /**
51
+ * Detect the GitHub-style org/repo from the project's git origin remote.
52
+ * Returns FALLBACK with detected=false when no remote is configured or
53
+ * when the URL cannot be parsed.
54
+ *
55
+ * The git invocation is hardened against two failure modes (AISDLC-104):
56
+ *
57
+ * 1. **cwd inheritance race under parallel test workers.** Every git
58
+ * command uses `git -C <cwd>` so the working dir is pinned at the
59
+ * git argv level rather than relying solely on `child_process`
60
+ * honouring the `cwd:` spawn option. Both should agree, but `git -C`
61
+ * is a git-internal contract independent of any subprocess cwd
62
+ * inheritance race that can happen when `process.chdir()` is
63
+ * interleaved with subprocess spawn under thread/fork pools.
64
+ *
65
+ * 2. **Host-repo origin bleed via parent-directory walk-up.** If `cwd`
66
+ * contains an invalid `.git` (e.g. an empty directory left by an
67
+ * init test setup) git normally walks UP looking for a real `.git`
68
+ * and can resolve to an ancestor repository — i.e. when run from
69
+ * inside the ai-sdlc-framework checkout the test would silently see
70
+ * the framework's own origin rather than the fallback. We defend by
71
+ * calling `git rev-parse --show-toplevel` first and confirming the
72
+ * reported toplevel matches `cwd` (after symlink resolution). When
73
+ * it doesn't, we treat the directory as not-a-repo and return the
74
+ * fallback rather than reporting the ancestor's remote. This was
75
+ * preferred over `GIT_CEILING_DIRECTORIES` because the ceiling-list
76
+ * semantics only block walking INTO the listed dirs, not up FROM
77
+ * them — empirically `GIT_CEILING_DIRECTORIES=<cwd>` did not stop
78
+ * git from finding a parent repo.
79
+ */
80
+ export function detectGitRemote(opts = {}) {
81
+ const cwd = opts.cwd ?? process.cwd();
82
+ const exec = opts.execImpl ?? defaultExec;
83
+ // Step 1: confirm cwd is a real git repo whose toplevel IS cwd.
84
+ // If `git rev-parse --show-toplevel` errors OR returns an ancestor,
85
+ // treat as not-a-repo and return FALLBACK. This is the host-repo
86
+ // bleed defense: an empty/invalid `.git/` in cwd causes git to walk
87
+ // UP to a parent repo, and `--show-toplevel` then reports the parent
88
+ // — comparing realpaths catches it.
89
+ let toplevel;
90
+ try {
91
+ toplevel = exec(`git -C ${shellQuote(cwd)} rev-parse --show-toplevel`, {
92
+ cwd,
93
+ encoding: 'utf-8',
94
+ stdio: ['ignore', 'pipe', 'ignore'],
95
+ }).trim();
96
+ }
97
+ catch {
98
+ return FALLBACK;
99
+ }
100
+ if (!sameDir(toplevel, cwd)) {
101
+ // git resolved to an ancestor repository — host-repo bleed. The
102
+ // operator is in a directory that isn't itself a real git root, so
103
+ // we deliberately do NOT report the ancestor's origin; emit
104
+ // FALLBACK so init prints the explicit "no git origin remote
105
+ // detected" message and substitutes `your-org`.
106
+ return FALLBACK;
107
+ }
108
+ // Step 2: ask for the origin URL. If unset (no remote configured)
109
+ // or unparseable, fall back.
110
+ let url;
111
+ try {
112
+ url = exec(`git -C ${shellQuote(cwd)} remote get-url origin`, {
113
+ cwd,
114
+ encoding: 'utf-8',
115
+ stdio: ['ignore', 'pipe', 'ignore'],
116
+ });
117
+ }
118
+ catch {
119
+ return FALLBACK;
120
+ }
121
+ const parsed = parseRemoteUrl(url);
122
+ return parsed ?? FALLBACK;
123
+ }
124
+ /**
125
+ * Compare two filesystem paths after symlink + canonicalization to
126
+ * decide whether they refer to the same directory. macOS aliases /tmp
127
+ * to /private/tmp, so a string compare of `cwd` against the toplevel
128
+ * git reports would otherwise fail spuriously. Falls back to literal
129
+ * compare when realpath isn't available (deleted dir, permission).
130
+ */
131
+ function sameDir(a, b) {
132
+ const norm = (p) => {
133
+ try {
134
+ return realpathSync(p);
135
+ }
136
+ catch {
137
+ return p;
138
+ }
139
+ };
140
+ return norm(a) === norm(b);
141
+ }
142
+ /**
143
+ * Quote a path for safe single-token interpolation into a shell command.
144
+ * Wraps in single quotes and escapes any embedded single quotes by
145
+ * closing the quote, emitting an escaped quote, then reopening.
146
+ */
147
+ function shellQuote(s) {
148
+ return `'${s.replace(/'/g, `'\\''`)}'`;
149
+ }
150
+ function defaultExec(cmd, opts) {
151
+ return execSync(cmd, {
152
+ cwd: opts.cwd,
153
+ encoding: opts.encoding,
154
+ stdio: opts.stdio,
155
+ });
156
+ }
157
+ /**
158
+ * Substitute `your-org` (and optionally `your-repo`) placeholders in a
159
+ * template body with detected values.
160
+ */
161
+ export function applyRemoteToPipelineYaml(template, info) {
162
+ // Replace the literal `org: your-org` config line with the detected
163
+ // org. We deliberately match the exact YAML key shape rather than a
164
+ // bare token so we don't smear the placeholder if it appears elsewhere.
165
+ let out = template.replace(/(\borg:\s*)your-org(\b)/g, `$1${info.org}$2`);
166
+ // If the template ever introduces a repo: placeholder, substitute it too.
167
+ out = out.replace(/(\brepo:\s*)your-repo(\b)/g, `$1${info.repo}$2`);
168
+ return out;
169
+ }
170
+ //# sourceMappingURL=git-remote.js.map
@@ -1,5 +1,9 @@
1
1
  /**
2
2
  * ai-sdlc health — validate config, state store, and adapter connectivity.
3
+ *
4
+ * AISDLC-78: surfaces the deferred-state-store wording (handled by the
5
+ * table formatter) and adds an opt-in `--init-state` flag for users who
6
+ * want the SQLite database created eagerly rather than on first run.
3
7
  */
4
8
  import { Command } from 'commander';
5
9
  export declare const healthCommand: Command;
@@ -1,18 +1,30 @@
1
1
  /**
2
2
  * ai-sdlc health — validate config, state store, and adapter connectivity.
3
+ *
4
+ * AISDLC-78: surfaces the deferred-state-store wording (handled by the
5
+ * table formatter) and adds an opt-in `--init-state` flag for users who
6
+ * want the SQLite database created eagerly rather than on first run.
3
7
  */
4
8
  import { Command } from 'commander';
9
+ import { join } from 'node:path';
5
10
  import { Orchestrator } from '../../orchestrator.js';
6
11
  import { formatOutput } from '../formatters/index.js';
7
12
  export const healthCommand = new Command('health')
8
13
  .description('Check orchestrator health')
9
14
  .option('--state <path>', 'SQLite state database path')
15
+ .option('--init-state', 'Eagerly initialize the SQLite state store (defaults to .ai-sdlc/state.db when --state is omitted)')
10
16
  .action(async (opts, cmd) => {
11
17
  const globalOpts = cmd.parent?.opts() ?? {};
12
18
  const format = globalOpts.format ?? 'table';
19
+ // When the user opts into eager initialization, default the path to
20
+ // .ai-sdlc/state.db inside the configured config dir so we don't
21
+ // litter cwd with an unexpected file.
22
+ const configDir = globalOpts.config ?? '.ai-sdlc';
23
+ const statePath = opts.state ??
24
+ (opts.initState ? join(configDir, 'state.db') : undefined);
13
25
  const orchestrator = new Orchestrator({
14
26
  configDir: globalOpts.config,
15
- statePath: opts.state,
27
+ statePath,
16
28
  });
17
29
  try {
18
30
  const result = await orchestrator.health();
@@ -0,0 +1,208 @@
1
+ /**
2
+ * `ai-sdlc init` interactive wizard + feature dispatcher (AISDLC-143).
3
+ *
4
+ * Per Q4(b) of the operator-ratified quality-gate redesign, `ai-sdlc init`
5
+ * is a wizard by default with `--yes` for non-interactive (CI/scripts) and
6
+ * `--with-X` flags for explicit opt-in (`--with-dor`, `--with-attestation`,
7
+ * `--with-classifier`, `--with-branch-protection`). This module owns:
8
+ *
9
+ * 1. The ordered prompt list (resolveFeatureSelection).
10
+ * 2. The feature-toggle → file-write dispatcher (applyFeatureSelection).
11
+ * 3. The branch-protection helper (applyBranchProtection) including the
12
+ * `--dry-run` JSON-print path required by AC #6.
13
+ * 4. The "next steps" summary printed at the end of init (AC #5).
14
+ *
15
+ * Test surface: every public function takes a small options bag with
16
+ * injectable side-effect adapters (prompter, writeFile, runCommand) so the
17
+ * test suite can drive every wizard branch hermetically without spinning
18
+ * up a TTY or shelling out to `gh`. Production callers in `init.ts` pass
19
+ * the real adapters.
20
+ */
21
+ /** Per-feature on/off bits derived from prompts + flags. */
22
+ export interface FeatureSelection {
23
+ dor: boolean;
24
+ attestation: boolean;
25
+ classifier: boolean;
26
+ branchProtection: boolean;
27
+ }
28
+ /** All feature flags off — used as the initial state before flags + prompts. */
29
+ export declare const NO_FEATURES: FeatureSelection;
30
+ /** All features on — the answer used by `--yes` (accept all defaults). */
31
+ export declare const ALL_FEATURES: FeatureSelection;
32
+ /** Flag-bag controlling wizard behavior (already parsed from argv). */
33
+ export interface WizardFlags {
34
+ /** `--yes` short-circuits the wizard; treats every prompt as "yes". */
35
+ yes: boolean;
36
+ /** `--with-dor` forces the DoR feature on without prompting. */
37
+ withDor: boolean;
38
+ /** `--with-attestation` forces attestation infra on without prompting. */
39
+ withAttestation: boolean;
40
+ /** `--with-classifier` forces the classifier on without prompting. */
41
+ withClassifier: boolean;
42
+ /** `--with-branch-protection` forces branch-protection on without prompting. */
43
+ withBranchProtection: boolean;
44
+ /**
45
+ * `--add <feature>` extends an already-initialized repo with a single
46
+ * feature without re-prompting. AC #7 (idempotent extension). When set,
47
+ * the wizard short-circuits to scaffold ONLY this feature.
48
+ */
49
+ add?: 'dor' | 'attestation' | 'classifier' | 'branch-protection';
50
+ /** `--dry-run` — print what would be done, don't write. */
51
+ dryRun: boolean;
52
+ }
53
+ /**
54
+ * Single-question prompter contract — accepts a question + default and
55
+ * returns the user's answer. The production adapter wraps `@inquirer/prompts`
56
+ * so the user gets a real readline TTY; tests inject a stub that returns
57
+ * scripted answers without touching stdin.
58
+ *
59
+ * Why a single-question primitive instead of "ask all questions at once":
60
+ * the prompts are conditional in some cases (e.g. branch-protection only
61
+ * makes sense after the user has chosen which CI gates exist). Keeping
62
+ * the primitive small lets `resolveFeatureSelection` decide ordering +
63
+ * skip questions whose answer is already determined by a `--with-X` flag.
64
+ */
65
+ export type Prompter = (question: string, defaultYes: boolean) => Promise<boolean>;
66
+ /**
67
+ * Side-effect adapter bag — every part of the dispatcher that touches
68
+ * disk or shells out goes through this so tests can assert on intents
69
+ * without mocking `node:fs` globally.
70
+ */
71
+ export interface FeatureAdapters {
72
+ /** Resolve to an interactive prompt answer. */
73
+ prompt: Prompter;
74
+ /** Write a file. Production = `node:fs.writeFileSync`. */
75
+ writeFile: (path: string, contents: string) => void;
76
+ /**
77
+ * Append `contents` to `path` exactly once: if `sentinel` is already
78
+ * present in the file, no-op. If the file doesn't exist, behaves like
79
+ * a write. Used for the husky pre-push sign block + CLAUDE.md pointer
80
+ * (both of which need to coexist with user-edited content).
81
+ */
82
+ appendOnce: (path: string, contents: string, sentinel: string) => 'appended' | 'skipped';
83
+ /** mkdir -p. Production = `node:fs.mkdirSync({ recursive: true })`. */
84
+ mkdirp: (path: string) => void;
85
+ /** Test for path existence. Production = `node:fs.existsSync`. */
86
+ exists: (path: string) => boolean;
87
+ /** Run a shell command (used for `gh api`). Production = `execSync`. */
88
+ runCommand: (cmd: string, args: string[]) => {
89
+ stdout: string;
90
+ exitCode: number;
91
+ };
92
+ /** Sink for operator-visible output (defaults to console.log). */
93
+ log: (line: string) => void;
94
+ }
95
+ /**
96
+ * Build the production adapter bag. Pulled into a factory so tests can
97
+ * compose a partial override bag (e.g. only override `prompt`) and let
98
+ * the rest fall through to real disk writes.
99
+ *
100
+ * The `prompt` adapter is a lazy import of `@inquirer/prompts.confirm`
101
+ * so that:
102
+ * 1. Tests don't pay the import cost when they inject their own stub.
103
+ * 2. `--yes` runs (which never call `prompt`) don't pay it either.
104
+ * 3. The orchestrator's runtime `dist/` is smaller for the common case.
105
+ */
106
+ export declare function buildProductionAdapters(): FeatureAdapters;
107
+ /**
108
+ * Resolve the per-feature on/off vector by combining (in priority order):
109
+ * 1. `--add <feature>` — if set, ONLY that feature is on; everything
110
+ * else is suppressed (idempotent extension, AC #7).
111
+ * 2. `--yes` — accept every default (every feature on).
112
+ * 3. `--with-X` flags — opt-in without prompting.
113
+ * 4. Interactive prompts for any feature still undetermined.
114
+ *
115
+ * Returns a fully-determined FeatureSelection. The dispatcher then writes
116
+ * exactly the union of features marked true.
117
+ */
118
+ export declare function resolveFeatureSelection(flags: WizardFlags, adapters: Pick<FeatureAdapters, 'prompt' | 'log'>): Promise<FeatureSelection>;
119
+ /** Return value of `applyFeatureSelection` — what was actually written. */
120
+ export interface ApplyResult {
121
+ /** Files that were newly created on this run. */
122
+ created: string[];
123
+ /** Files that already existed and were left untouched (idempotent). */
124
+ skipped: string[];
125
+ /** Files that would have been created if not for `--dry-run`. */
126
+ wouldCreate: string[];
127
+ /** Branch-protection result, if attempted. */
128
+ branchProtection?: BranchProtectionResult;
129
+ }
130
+ /**
131
+ * Write the union of feature templates into the project dir. AC #4 says
132
+ * the BASELINE workflow templates (gate workflow) are always written; the
133
+ * per-feature template sets are written only when their toggle is on.
134
+ *
135
+ * Idempotent: any file that already exists at the target path is skipped
136
+ * with a "skip" log line. This is what makes `--add <feature>` safe to
137
+ * run on an already-initialized repo (AC #7).
138
+ */
139
+ export declare function applyFeatureSelection(projectDir: string, selection: FeatureSelection, flags: WizardFlags, adapters: FeatureAdapters): Promise<ApplyResult>;
140
+ export interface BranchProtectionResult {
141
+ /** Whether the rule was actually applied. False in dry-run. */
142
+ applied: boolean;
143
+ /** The PUT body as a JSON string (always populated for visibility). */
144
+ bodyJson: string;
145
+ /** Error message from `gh api`, if non-zero exit. */
146
+ error?: string;
147
+ }
148
+ /**
149
+ * Recommended branch-protection ruleset for AI-SDLC adopters. AC #1 #4:
150
+ * the required checks are `ai-sdlc/pr-ready` (the gate aggregator) and
151
+ * `codecov/patch` (the de facto coverage signal). Other AI-SDLC apps
152
+ * post their own statuses but they're all rolled into pr-ready.
153
+ *
154
+ * The body conforms to the GitHub REST API
155
+ * `PUT /repos/{owner}/{repo}/branches/{branch}/protection` schema.
156
+ */
157
+ export declare const RECOMMENDED_BRANCH_PROTECTION_BODY: {
158
+ required_status_checks: {
159
+ strict: boolean;
160
+ contexts: string[];
161
+ };
162
+ enforce_admins: boolean;
163
+ required_pull_request_reviews: {
164
+ dismiss_stale_reviews: boolean;
165
+ require_code_owner_reviews: boolean;
166
+ required_approving_review_count: number;
167
+ };
168
+ restrictions: null;
169
+ allow_force_pushes: boolean;
170
+ allow_deletions: boolean;
171
+ };
172
+ /**
173
+ * Apply (or print, in dry-run) the recommended branch protection rule
174
+ * to the `main` branch of the repo at `projectDir`. AC #6 explicitly
175
+ * requires that `--dry-run` print the JSON without applying.
176
+ *
177
+ * The repo identity (`owner/repo`) is resolved by shelling out to
178
+ * `gh repo view --json nameWithOwner -q .nameWithOwner`. We could parse
179
+ * the git remote ourselves (see git-remote.ts) but `gh` already resolves
180
+ * forks + renames + custom default branches consistently, and the user
181
+ * needs `gh` on PATH for the PUT to work anyway.
182
+ */
183
+ export declare function applyBranchProtection(projectDir: string, flags: WizardFlags, adapters: Pick<FeatureAdapters, 'runCommand' | 'log'>): Promise<BranchProtectionResult>;
184
+ /**
185
+ * Print the structured "next steps" summary at the end of init. AC #5:
186
+ * the summary must include operator action items conditional on which
187
+ * features were chosen (e.g. `gh secret set` commands when attestation
188
+ * was opted in).
189
+ *
190
+ * Returns the rendered summary as a string in addition to logging it,
191
+ * so tests can assert on it without re-stringifying console output.
192
+ */
193
+ export declare function renderNextSteps(selection: FeatureSelection, result: ApplyResult, adapters: Pick<FeatureAdapters, 'log'>): string;
194
+ /**
195
+ * The pointer block we append to CLAUDE.md so a freshly-initialized repo's
196
+ * Claude Code sessions know where to find the AI-SDLC quality-gate docs.
197
+ * Idempotent — guarded by a sentinel so re-running init doesn't duplicate
198
+ * the block.
199
+ */
200
+ export declare const CLAUDE_MD_POINTER = "\n<!-- ai-sdlc:recommendation-pointer -->\n## AI-SDLC quality gate\n\nThis repo is bootstrapped with the AI-SDLC framework. The single PR-ready\nmerge gate is `ai-sdlc/pr-ready` (see `.github/workflows/ai-sdlc-gate.yml`).\nRun `ai-sdlc health` to verify your local config; see\n`docs/operations/init.md` for the adopter guide.\n<!-- end ai-sdlc:recommendation-pointer -->\n";
201
+ /** Sentinel marker used by the CLAUDE.md pointer for idempotency. */
202
+ export declare const CLAUDE_MD_SENTINEL = "<!-- ai-sdlc:recommendation-pointer -->";
203
+ /**
204
+ * Append the recommendation pointer to CLAUDE.md (or create the file if
205
+ * missing). Idempotent: if the sentinel is already present we no-op.
206
+ */
207
+ export declare function ensureClaudeMdPointer(projectDir: string, adapters: Pick<FeatureAdapters, 'exists' | 'writeFile' | 'appendOnce' | 'log'>, dryRun: boolean): void;
208
+ //# sourceMappingURL=init-features.d.ts.map