@ai-sdlc/orchestrator 0.6.0 → 0.9.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 (203) hide show
  1. package/dist/admission-composite.d.ts +67 -0
  2. package/dist/admission-composite.js +158 -0
  3. package/dist/admission-enrichment.d.ts +142 -0
  4. package/dist/admission-enrichment.js +331 -0
  5. package/dist/admission-hc.d.ts +62 -0
  6. package/dist/admission-hc.js +83 -0
  7. package/dist/admission-score.d.ts +95 -5
  8. package/dist/admission-score.js +84 -11
  9. package/dist/analysis/analyzer.js +3 -2
  10. package/dist/analysis/convention-detector.d.ts +85 -2
  11. package/dist/analysis/convention-detector.js +375 -70
  12. package/dist/analysis/diff-analyzer.d.ts +33 -0
  13. package/dist/analysis/diff-analyzer.js +122 -0
  14. package/dist/analysis/hotspot-analyzer.js +3 -1
  15. package/dist/analysis/index.d.ts +2 -1
  16. package/dist/analysis/index.js +2 -1
  17. package/dist/artifacts/index.d.ts +65 -0
  18. package/dist/artifacts/index.js +142 -0
  19. package/dist/backlog-adapter.d.ts +121 -0
  20. package/dist/backlog-adapter.js +438 -0
  21. package/dist/calibration.d.ts +43 -0
  22. package/dist/calibration.js +76 -0
  23. package/dist/cli/commands/agents.d.ts +24 -0
  24. package/dist/cli/commands/agents.js +66 -1
  25. package/dist/cli/commands/git-remote.d.ts +49 -0
  26. package/dist/cli/commands/git-remote.js +91 -0
  27. package/dist/cli/commands/health.d.ts +4 -0
  28. package/dist/cli/commands/health.js +13 -1
  29. package/dist/cli/commands/init.d.ts +26 -0
  30. package/dist/cli/commands/init.js +164 -22
  31. package/dist/cli/commands/mcp-setup.d.ts +31 -0
  32. package/dist/cli/commands/mcp-setup.js +78 -8
  33. package/dist/cli/formatters/table.js +15 -2
  34. package/dist/cli/index.d.ts +14 -1
  35. package/dist/cli/index.js +81 -20
  36. package/dist/cli/versions.d.ts +57 -0
  37. package/dist/cli/versions.js +128 -0
  38. package/dist/code-area-classifier.d.ts +21 -0
  39. package/dist/code-area-classifier.js +48 -0
  40. package/dist/config.d.ts +33 -1
  41. package/dist/config.js +78 -8
  42. package/dist/database/adapters/external.d.ts +24 -0
  43. package/dist/database/adapters/external.js +80 -0
  44. package/dist/database/adapters/neon.d.ts +41 -0
  45. package/dist/database/adapters/neon.js +98 -0
  46. package/dist/database/adapters/pg-snapshot-restore.d.ts +28 -0
  47. package/dist/database/adapters/pg-snapshot-restore.js +68 -0
  48. package/dist/database/adapters/sqlite-copy.d.ts +32 -0
  49. package/dist/database/adapters/sqlite-copy.js +145 -0
  50. package/dist/database/connection-injection.d.ts +35 -0
  51. package/dist/database/connection-injection.js +93 -0
  52. package/dist/database/index.d.ts +12 -0
  53. package/dist/database/index.js +23 -0
  54. package/dist/database/registry.d.ts +13 -0
  55. package/dist/database/registry.js +27 -0
  56. package/dist/database/topology.d.ts +52 -0
  57. package/dist/database/topology.js +44 -0
  58. package/dist/database/types.d.ts +89 -0
  59. package/dist/database/types.js +26 -0
  60. package/dist/design-authority.d.ts +40 -0
  61. package/dist/design-authority.js +71 -0
  62. package/dist/design-lookahead.d.ts +64 -0
  63. package/dist/design-lookahead.js +86 -0
  64. package/dist/design-quality-trend.d.ts +87 -0
  65. package/dist/design-quality-trend.js +190 -0
  66. package/dist/design-system-context.d.ts +46 -0
  67. package/dist/design-system-context.js +80 -0
  68. package/dist/design-system-correction-loop.d.ts +64 -0
  69. package/dist/design-system-correction-loop.js +128 -0
  70. package/dist/design-system-metrics.d.ts +61 -0
  71. package/dist/design-system-metrics.js +104 -0
  72. package/dist/design-system-stewardship.d.ts +22 -0
  73. package/dist/design-system-stewardship.js +85 -0
  74. package/dist/design-system-validation.d.ts +37 -0
  75. package/dist/design-system-validation.js +88 -0
  76. package/dist/dispatch/index.d.ts +4 -0
  77. package/dist/dispatch/index.js +4 -0
  78. package/dist/dispatch/merge-gate.d.ts +46 -0
  79. package/dist/dispatch/merge-gate.js +90 -0
  80. package/dist/dispatch/requeue.d.ts +57 -0
  81. package/dist/dispatch/requeue.js +131 -0
  82. package/dist/dispatch/worker-pool.d.ts +62 -0
  83. package/dist/dispatch/worker-pool.js +60 -0
  84. package/dist/execute.d.ts +40 -0
  85. package/dist/execute.js +185 -15
  86. package/dist/fix-ci.js +8 -1
  87. package/dist/fix-review.js +8 -1
  88. package/dist/harness/adapters/claude-code.d.ts +29 -0
  89. package/dist/harness/adapters/claude-code.js +191 -0
  90. package/dist/harness/adapters/codex.d.ts +25 -0
  91. package/dist/harness/adapters/codex.js +61 -0
  92. package/dist/harness/independence.d.ts +51 -0
  93. package/dist/harness/independence.js +67 -0
  94. package/dist/harness/index.d.ts +14 -0
  95. package/dist/harness/index.js +20 -0
  96. package/dist/harness/registry.d.ts +17 -0
  97. package/dist/harness/registry.js +31 -0
  98. package/dist/harness/types.d.ts +123 -0
  99. package/dist/harness/types.js +8 -0
  100. package/dist/harness/version-probe.d.ts +14 -0
  101. package/dist/harness/version-probe.js +123 -0
  102. package/dist/index.d.ts +30 -4
  103. package/dist/index.js +35 -2
  104. package/dist/models/classifier.d.ts +76 -0
  105. package/dist/models/classifier.js +221 -0
  106. package/dist/models/index.d.ts +3 -0
  107. package/dist/models/index.js +3 -0
  108. package/dist/models/registry.d.ts +97 -0
  109. package/dist/models/registry.js +173 -0
  110. package/dist/pillar-breakdown.d.ts +63 -0
  111. package/dist/pillar-breakdown.js +153 -0
  112. package/dist/priority.d.ts +5 -0
  113. package/dist/priority.js +18 -5
  114. package/dist/review-meta.d.ts +65 -0
  115. package/dist/review-meta.js +149 -0
  116. package/dist/review.d.ts +4 -0
  117. package/dist/review.js +12 -2
  118. package/dist/runners/claude-code-sdk.d.ts +31 -0
  119. package/dist/runners/claude-code-sdk.js +238 -0
  120. package/dist/runners/claude-code.d.ts +3 -0
  121. package/dist/runners/claude-code.js +30 -66
  122. package/dist/runners/codex.js +4 -1
  123. package/dist/runners/copilot.js +4 -1
  124. package/dist/runners/cursor.js +4 -1
  125. package/dist/runners/git-utils.d.ts +81 -0
  126. package/dist/runners/git-utils.js +201 -0
  127. package/dist/runners/index.d.ts +3 -0
  128. package/dist/runners/index.js +3 -0
  129. package/dist/runners/review-agent.d.ts +20 -0
  130. package/dist/runners/review-agent.js +171 -41
  131. package/dist/runners/runner-registry.js +10 -0
  132. package/dist/runners/sdk-review-runner.d.ts +65 -0
  133. package/dist/runners/sdk-review-runner.js +185 -0
  134. package/dist/runners/security-triage.d.ts +20 -4
  135. package/dist/runners/security-triage.js +39 -15
  136. package/dist/runners/types.d.ts +6 -0
  137. package/dist/runtime/attestations.d.ts +274 -0
  138. package/dist/runtime/attestations.js +460 -0
  139. package/dist/runtime/git-env.d.ts +53 -0
  140. package/dist/runtime/git-env.js +60 -0
  141. package/dist/runtime/index.d.ts +7 -0
  142. package/dist/runtime/index.js +7 -0
  143. package/dist/runtime/parallelism-flag.d.ts +10 -0
  144. package/dist/runtime/parallelism-flag.js +18 -0
  145. package/dist/runtime/port-allocator.d.ts +21 -0
  146. package/dist/runtime/port-allocator.js +66 -0
  147. package/dist/runtime/worktree-pool.d.ts +86 -0
  148. package/dist/runtime/worktree-pool.js +204 -0
  149. package/dist/runtime/worktree.d.ts +25 -0
  150. package/dist/runtime/worktree.js +111 -0
  151. package/dist/sa-scoring/auto-calibrate.d.ts +69 -0
  152. package/dist/sa-scoring/auto-calibrate.js +107 -0
  153. package/dist/sa-scoring/c1-sa2-computable.d.ts +42 -0
  154. package/dist/sa-scoring/c1-sa2-computable.js +59 -0
  155. package/dist/sa-scoring/composite.d.ts +107 -0
  156. package/dist/sa-scoring/composite.js +139 -0
  157. package/dist/sa-scoring/depparse-client.d.ts +79 -0
  158. package/dist/sa-scoring/depparse-client.js +187 -0
  159. package/dist/sa-scoring/did-compiler.d.ts +122 -0
  160. package/dist/sa-scoring/did-compiler.js +286 -0
  161. package/dist/sa-scoring/drift-monitor.d.ts +84 -0
  162. package/dist/sa-scoring/drift-monitor.js +186 -0
  163. package/dist/sa-scoring/exemplar-bank.d.ts +78 -0
  164. package/dist/sa-scoring/exemplar-bank.js +154 -0
  165. package/dist/sa-scoring/feedback-store.d.ts +100 -0
  166. package/dist/sa-scoring/feedback-store.js +156 -0
  167. package/dist/sa-scoring/index.d.ts +71 -0
  168. package/dist/sa-scoring/index.js +158 -0
  169. package/dist/sa-scoring/layer1-deterministic.d.ts +115 -0
  170. package/dist/sa-scoring/layer1-deterministic.js +298 -0
  171. package/dist/sa-scoring/layer2-structural.d.ts +71 -0
  172. package/dist/sa-scoring/layer2-structural.js +151 -0
  173. package/dist/sa-scoring/layer3-llm.d.ts +86 -0
  174. package/dist/sa-scoring/layer3-llm.js +282 -0
  175. package/dist/sa-scoring/rescore-orchestrator.d.ts +52 -0
  176. package/dist/sa-scoring/rescore-orchestrator.js +47 -0
  177. package/dist/scheduling/burn-down.d.ts +27 -0
  178. package/dist/scheduling/burn-down.js +43 -0
  179. package/dist/scheduling/calibration.d.ts +42 -0
  180. package/dist/scheduling/calibration.js +150 -0
  181. package/dist/scheduling/index.d.ts +8 -0
  182. package/dist/scheduling/index.js +8 -0
  183. package/dist/scheduling/ledger.d.ts +59 -0
  184. package/dist/scheduling/ledger.js +216 -0
  185. package/dist/scheduling/off-peak.d.ts +27 -0
  186. package/dist/scheduling/off-peak.js +112 -0
  187. package/dist/scheduling/schedule-decision.d.ts +39 -0
  188. package/dist/scheduling/schedule-decision.js +89 -0
  189. package/dist/scheduling/tier-analysis.d.ts +47 -0
  190. package/dist/scheduling/tier-analysis.js +81 -0
  191. package/dist/scheduling/types.d.ts +140 -0
  192. package/dist/scheduling/types.js +11 -0
  193. package/dist/shared.d.ts +13 -0
  194. package/dist/shared.js +32 -0
  195. package/dist/state/schema.d.ts +5 -1
  196. package/dist/state/schema.js +223 -1
  197. package/dist/state/store.d.ts +55 -1
  198. package/dist/state/store.js +342 -7
  199. package/dist/state/types.d.ts +139 -0
  200. package/dist/types.d.ts +1 -1
  201. package/dist/validate-agent-output.js +4 -1
  202. package/dist/watch.js +6 -0
  203. package/package.json +2 -2
@@ -1,23 +1,48 @@
1
1
  /**
2
2
  * MCP server setup — detect coding agents and install MCP config.
3
+ *
4
+ * AISDLC-78 changes:
5
+ * - The `npx -y` arg list now pins `@ai-sdlc/mcp-advisor@<version>` so
6
+ * fresh installs don't silently jump to whatever is published when
7
+ * the orchestrator binary itself was last updated. Each generated
8
+ * config file carries a single top-level `_aiSdlcComment` documenting
9
+ * how to opt back into floating-tag behaviour.
10
+ * - Cursor is no longer detected by binary-on-PATH alone; the user
11
+ * must either have a `.cursor/` directory present (real signal of
12
+ * use) or pass `--cursor` to opt in. This avoids writing
13
+ * `.cursor/mcp.json` into projects whose author has Cursor
14
+ * installed but is not using it on this repo.
3
15
  */
4
16
  import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
5
17
  import { join, dirname } from 'node:path';
6
18
  import { execSync } from 'node:child_process';
7
- function standardEntry(env) {
19
+ /**
20
+ * The opt-out comment we write alongside the pinned mcp-advisor entry.
21
+ * Lives at the top level of the generated MCP config under a leading-
22
+ * underscore key so JSON parsers and MCP clients ignore it; humans
23
+ * editing by hand see exactly how to re-enable floating-latest behaviour.
24
+ */
25
+ const PIN_OPT_OUT_COMMENT = 'Pinned to the orchestrator version that ran `ai-sdlc init`. ' +
26
+ 'To always pull the latest published mcp-advisor, change args to ["-y", "@ai-sdlc/mcp-advisor"].';
27
+ function pinnedSpec(version) {
28
+ return version && version !== '0.0.0'
29
+ ? `@ai-sdlc/mcp-advisor@${version}`
30
+ : '@ai-sdlc/mcp-advisor';
31
+ }
32
+ function standardEntry(version, env) {
8
33
  const entry = {
9
34
  command: 'npx',
10
- args: ['-y', '@ai-sdlc/mcp-advisor'],
35
+ args: ['-y', pinnedSpec(version)],
11
36
  };
12
37
  if (env)
13
38
  entry.env = env;
14
39
  return entry;
15
40
  }
16
- function vscodeEntry(env) {
41
+ function vscodeEntry(version, env) {
17
42
  const entry = {
18
43
  type: 'stdio',
19
44
  command: 'npx',
20
- args: ['-y', '@ai-sdlc/mcp-advisor'],
45
+ args: ['-y', pinnedSpec(version)],
21
46
  };
22
47
  if (env)
23
48
  entry.env = env;
@@ -39,6 +64,7 @@ const AGENT_SPECS = [
39
64
  entryFn: standardEntry,
40
65
  configDir: '.cursor',
41
66
  binary: 'cursor',
67
+ requiresOptIn: true,
42
68
  },
43
69
  {
44
70
  name: 'VS Code',
@@ -66,31 +92,67 @@ function hasBinary(name) {
66
92
  return false;
67
93
  }
68
94
  }
69
- export function detectAgents(projectDir, options) {
95
+ function hasUserCursorDir() {
96
+ const home = process.env.HOME ?? process.env.USERPROFILE;
97
+ if (!home)
98
+ return false;
99
+ return existsSync(join(home, '.cursor'));
100
+ }
101
+ export function detectAgentsDetailed(projectDir, options) {
70
102
  const detected = [];
103
+ const skipped = [];
71
104
  const env = options?.isWorkspace ? { AI_SDLC_WORKSPACE: '.' } : undefined;
105
+ const pinVersion = options?.pinVersion;
72
106
  for (const spec of AGENT_SPECS) {
73
107
  if (spec.alwaysDetect) {
74
108
  detected.push({
75
109
  name: spec.name,
76
110
  configPath: spec.configPath,
77
111
  configKey: spec.configKey,
78
- serverEntry: spec.entryFn(env),
112
+ serverEntry: spec.entryFn(pinVersion, env),
79
113
  });
80
114
  continue;
81
115
  }
82
116
  const hasDir = spec.configDir ? existsSync(join(projectDir, spec.configDir)) : false;
83
117
  const hasBin = spec.binary ? hasBinary(spec.binary) : false;
118
+ if (spec.requiresOptIn) {
119
+ // For Cursor specifically: explicit --cursor flag, OR a project-local
120
+ // .cursor/ dir, OR a user-global ~/.cursor/ presence. Just having
121
+ // `cursor` on PATH is no longer sufficient (too noisy on dev boxes
122
+ // where Cursor is installed but not used per-project).
123
+ const userOptIn = options?.cursorOptIn === true;
124
+ if (userOptIn || hasDir || hasUserCursorDir()) {
125
+ detected.push({
126
+ name: spec.name,
127
+ configPath: spec.configPath,
128
+ configKey: spec.configKey,
129
+ serverEntry: spec.entryFn(pinVersion, env),
130
+ });
131
+ }
132
+ else {
133
+ skipped.push({
134
+ name: spec.name,
135
+ reason: 'no .cursor/ directory in project or $HOME; pass --cursor to install Cursor MCP config',
136
+ });
137
+ }
138
+ continue;
139
+ }
84
140
  if (hasDir || hasBin) {
85
141
  detected.push({
86
142
  name: spec.name,
87
143
  configPath: spec.configPath,
88
144
  configKey: spec.configKey,
89
- serverEntry: spec.entryFn(env),
145
+ serverEntry: spec.entryFn(pinVersion, env),
90
146
  });
91
147
  }
92
148
  }
93
- return detected;
149
+ return { detected, skipped };
150
+ }
151
+ /**
152
+ * Backwards-compatible wrapper. New code should prefer detectAgentsDetailed.
153
+ */
154
+ export function detectAgents(projectDir, options) {
155
+ return detectAgentsDetailed(projectDir, options).detected;
94
156
  }
95
157
  export function installMcpServer(projectDir, agent, dryRun) {
96
158
  const fullPath = join(projectDir, agent.configPath);
@@ -110,6 +172,13 @@ export function installMcpServer(projectDir, agent, dryRun) {
110
172
  if (!dryRun) {
111
173
  section['ai-sdlc'] = agent.serverEntry;
112
174
  existing[agent.configKey] = section;
175
+ // Top-level pin-comment: standard convention is a single
176
+ // `_aiSdlcComment` key at the root of the document, not inside each
177
+ // server entry. We add it on first write but never overwrite an
178
+ // existing top-level value (caller may have customised it).
179
+ if (!('_aiSdlcComment' in existing)) {
180
+ existing._aiSdlcComment = PIN_OPT_OUT_COMMENT;
181
+ }
113
182
  writeFileSync(fullPath, JSON.stringify(existing, null, 2) + '\n', 'utf-8');
114
183
  }
115
184
  return 'merged';
@@ -120,6 +189,7 @@ export function installMcpServer(projectDir, agent, dryRun) {
120
189
  mkdirSync(dir, { recursive: true });
121
190
  }
122
191
  const config = {
192
+ _aiSdlcComment: PIN_OPT_OUT_COMMENT,
123
193
  [agent.configKey]: {
124
194
  'ai-sdlc': agent.serverEntry,
125
195
  },
@@ -37,7 +37,12 @@ export function formatTable(data) {
37
37
  lines.push('Health Check');
38
38
  lines.push('─'.repeat(40));
39
39
  lines.push(`Config: ${data.configValid ? 'valid' : 'INVALID'}`);
40
- lines.push(`State Store: ${data.stateStoreConnected ? 'connected' : 'not configured'}`);
40
+ // AISDLC-78: the previous "not configured" string read like a setup
41
+ // failure to fresh users. The state store is created on demand by
42
+ // the first pipeline run; explicitly flag deferred initialization.
43
+ lines.push(`State Store: ${data.stateStoreConnected
44
+ ? 'connected'
45
+ : 'deferred (initializes on first pipeline run; pass --init-state to create now)'}`);
41
46
  const errors = data.errors;
42
47
  if (errors.length > 0) {
43
48
  lines.push('');
@@ -65,8 +70,16 @@ export function formatTable(data) {
65
70
  for (const agent of agents) {
66
71
  const total = agent.totalTasks;
67
72
  const success = agent.successCount;
73
+ const declaredOnly = agent.declaredOnly === true;
68
74
  const pct = total > 0 ? `${Math.round((success / total) * 100)}%` : '-';
69
- const lastTask = agent.lastTaskAt ? agent.lastTaskAt.split('T')[0] : '-';
75
+ // AISDLC-78 (AC #10): mark declared-but-not-executed rows so a
76
+ // fresh-install user sees their `agent-role.yaml` declarations
77
+ // even before the first pipeline run populates the ledger.
78
+ const lastTask = declaredOnly
79
+ ? '(declared, not yet executed)'
80
+ : agent.lastTaskAt
81
+ ? agent.lastTaskAt.split('T')[0]
82
+ : '-';
70
83
  lines.push(String(agent.agentName ?? '').padEnd(20) +
71
84
  String(agent.currentLevel ?? 0).padEnd(7) +
72
85
  String(total).padEnd(7) +
@@ -1,6 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * AI-SDLC CLI — entry point for the Commander-based CLI.
4
+ *
5
+ * AISDLC-78: replaces the literal `0.1.0` version with the real package
6
+ * version, prints a 3-line provenance block on `--version`, and adds an
7
+ * unknown-subcommand hint that points at the upgrade flow when version
8
+ * drift is detected.
4
9
  */
5
- export {};
10
+ import { Command } from 'commander';
11
+ import { type VersionTriple } from './versions.js';
12
+ /**
13
+ * Build the Commander program. Exported so integration tests can drive
14
+ * the real argv pipeline (`--version`, unknown-subcommand) without
15
+ * invoking the bundled binary on disk. The bin shim below calls this
16
+ * exact factory at startup.
17
+ */
18
+ export declare function buildProgram(versions: VersionTriple): Command;
6
19
  //# sourceMappingURL=index.d.ts.map
package/dist/cli/index.js CHANGED
@@ -1,6 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * AI-SDLC CLI — entry point for the Commander-based CLI.
4
+ *
5
+ * AISDLC-78: replaces the literal `0.1.0` version with the real package
6
+ * version, prints a 3-line provenance block on `--version`, and adds an
7
+ * unknown-subcommand hint that points at the upgrade flow when version
8
+ * drift is detected.
4
9
  */
5
10
  import { Command } from 'commander';
6
11
  import { initCommand } from './commands/init.js';
@@ -14,24 +19,80 @@ import { complexityCommand } from './commands/complexity.js';
14
19
  import { costCommand } from './commands/cost.js';
15
20
  import { dashboardCommand } from './commands/dashboard.js';
16
21
  import { validateCommand } from './commands/validate.js';
17
- const program = new Command();
18
- program
19
- .name('ai-sdlc')
20
- .description('AI-SDLC Orchestrator drive issues through the SDLC with AI agents')
21
- .version('0.1.0')
22
- .option('-c, --config <dir>', 'Config directory path', '.ai-sdlc')
23
- .option('-f, --format <type>', 'Output format: table, json, minimal', 'table')
24
- .option('-v, --verbose', 'Enable verbose output');
25
- program.addCommand(initCommand);
26
- program.addCommand(runCommand);
27
- program.addCommand(startCommand);
28
- program.addCommand(statusCommand);
29
- program.addCommand(healthCommand);
30
- program.addCommand(agentsCommand);
31
- program.addCommand(routingCommand);
32
- program.addCommand(complexityCommand);
33
- program.addCommand(costCommand);
34
- program.addCommand(dashboardCommand);
35
- program.addCommand(validateCommand);
36
- program.parse();
22
+ import { resolveVersions, formatVersionBlock, upgradeHint, } from './versions.js';
23
+ /**
24
+ * Build the Commander program. Exported so integration tests can drive
25
+ * the real argv pipeline (`--version`, unknown-subcommand) without
26
+ * invoking the bundled binary on disk. The bin shim below calls this
27
+ * exact factory at startup.
28
+ */
29
+ export function buildProgram(versions) {
30
+ const program = new Command();
31
+ program
32
+ .name('ai-sdlc')
33
+ .description('AI-SDLC Orchestrator — drive issues through the SDLC with AI agents')
34
+ // Anchor commander's --version to the real package version so a user
35
+ // who just ran `npm i -g @ai-sdlc/orchestrator` sees the version they
36
+ // installed, not the literal that was hardcoded years ago.
37
+ .version(versions.cli, '-V, --version', 'Print the CLI version')
38
+ .option('-c, --config <dir>', 'Config directory path', '.ai-sdlc')
39
+ .option('-f, --format <type>', 'Output format: table, json, minimal', 'table')
40
+ .option('-v, --verbose', 'Enable verbose output');
41
+ // Override commander's default --version handler so we can emit the full
42
+ // 3-line block (CLI + orchestrator + plugin) instead of just the literal.
43
+ //
44
+ // IMPORTANT: commander 12's `.version()` registers its own
45
+ // `option:version` listener (in registration order) that writes the
46
+ // bare version string and calls `_exit`. Plain `.on()` would queue
47
+ // *after* commander's listener, so our 3-line block would never
48
+ // print. `prependListener` puts ours first; calling commander's own
49
+ // `_exit` here prevents the default listener from ever firing while
50
+ // still going through `exitOverride()` so tests can intercept.
51
+ // commander's Command extends EventEmitter; the .d.ts only re-exports
52
+ // .on/.once, so we reach for prependListener via the EventEmitter cast.
53
+ program.prependListener('option:version', () => {
54
+ const writeOut = program.configureOutput().writeOut ?? ((s) => process.stdout.write(s));
55
+ writeOut(`${formatVersionBlock(versions)}\n`);
56
+ program._exit(0, 'commander.version', versions.cli);
57
+ });
58
+ program.addCommand(initCommand);
59
+ program.addCommand(runCommand);
60
+ program.addCommand(startCommand);
61
+ program.addCommand(statusCommand);
62
+ program.addCommand(healthCommand);
63
+ program.addCommand(agentsCommand);
64
+ program.addCommand(routingCommand);
65
+ program.addCommand(complexityCommand);
66
+ program.addCommand(costCommand);
67
+ program.addCommand(dashboardCommand);
68
+ program.addCommand(validateCommand);
69
+ // Unknown-subcommand handler (AC #9): hint at version drift / upgrade so
70
+ // users who installed an outdated CLI find out fast.
71
+ program.on('command:*', (operands) => {
72
+ const unknown = operands[0] ?? '';
73
+ const writeErr = program.configureOutput().writeErr ?? ((s) => process.stderr.write(s));
74
+ writeErr(`Unknown subcommand: ${unknown}\n`);
75
+ writeErr('\n');
76
+ writeErr(`${upgradeHint(versions)}\n`);
77
+ writeErr('\n');
78
+ writeErr('Run `ai-sdlc --help` to see the available subcommands.\n');
79
+ program._exit(1, 'commander.unknownCommand', unknown);
80
+ });
81
+ return program;
82
+ }
83
+ // Bin entry: only run argv parsing when invoked as a script, not when the
84
+ // module is imported by tests.
85
+ //
86
+ // `process.argv[1]` is the path Node resolved for the entry script.
87
+ // When it ends with the compiled CLI bundle (`dist/cli/index.js`) we are
88
+ // the main entry. When imported (e.g. via Vitest) argv[1] points at the
89
+ // test runner, so we skip parsing.
90
+ const isMainEntry = (() => {
91
+ const entry = process.argv[1] ?? '';
92
+ return entry.endsWith('cli/index.js') || entry.endsWith('cli/index.ts');
93
+ })();
94
+ if (isMainEntry) {
95
+ const program = buildProgram(resolveVersions());
96
+ program.parse();
97
+ }
37
98
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Version provenance — surface the running CLI, orchestrator runtime,
3
+ * and (optionally) plugin versions in one place so users can spot drift.
4
+ *
5
+ * Why this exists: the original CLI hardcoded `0.1.0` in commander while
6
+ * the published `@ai-sdlc/orchestrator` package was at 0.6.0+. Operators
7
+ * who ran `ai-sdlc --version` saw the stale literal and assumed the
8
+ * orchestrator itself was that old. AISDLC-78 anchors `--version` to
9
+ * the package.json that ships with the binary and warns when components
10
+ * disagree.
11
+ */
12
+ export interface VersionTriple {
13
+ /** The CLI binary's reported version. Tracks the orchestrator package. */
14
+ cli: string;
15
+ /** The orchestrator runtime version (from @ai-sdlc/orchestrator/package.json). */
16
+ orchestrator: string;
17
+ /** The plugin version, if discoverable in the workspace. */
18
+ plugin?: string;
19
+ /** Whether any of the discoverable components disagree on version. */
20
+ drift: boolean;
21
+ }
22
+ export interface ResolveVersionsOptions {
23
+ /** Override the working directory used to discover the plugin. */
24
+ workDir?: string;
25
+ /** Inject an orchestrator package.json path (testing). */
26
+ orchestratorPackageJsonPath?: string;
27
+ /** Inject a plugin version (testing). */
28
+ pluginVersionOverride?: string;
29
+ }
30
+ /**
31
+ * Compute the active CLI/orchestrator/plugin versions.
32
+ *
33
+ * Drift detection: when at least two known components disagree on
34
+ * version string (case-insensitive), `drift` is set. The plugin is
35
+ * compared only when discovered.
36
+ */
37
+ export declare function resolveVersions(opts?: ResolveVersionsOptions): VersionTriple;
38
+ /**
39
+ * Render the canonical 3-line version block. Always emits a
40
+ * trailing newline. When drift is detected, appends a warning line
41
+ * pointing at the upgrade hint.
42
+ *
43
+ * The CLI and orchestrator rows are rendered separately even though
44
+ * today they always show the same number — both ship from the same
45
+ * `@ai-sdlc/orchestrator` package.json. The shape is intentionally
46
+ * future-proofed: when the CLI is split into a thinner veneer with
47
+ * its own version it will start to differ from the orchestrator
48
+ * runtime, and operators are then trained to read both rows. Until
49
+ * then a duplicate value is the honest answer.
50
+ */
51
+ export declare function formatVersionBlock(versions: VersionTriple): string;
52
+ /**
53
+ * Return the upgrade hint shown by unknown-subcommand handler and by
54
+ * version drift warnings. Centralised so tests can assert one string.
55
+ */
56
+ export declare function upgradeHint(versions: VersionTriple): string;
57
+ //# sourceMappingURL=versions.d.ts.map
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Version provenance — surface the running CLI, orchestrator runtime,
3
+ * and (optionally) plugin versions in one place so users can spot drift.
4
+ *
5
+ * Why this exists: the original CLI hardcoded `0.1.0` in commander while
6
+ * the published `@ai-sdlc/orchestrator` package was at 0.6.0+. Operators
7
+ * who ran `ai-sdlc --version` saw the stale literal and assumed the
8
+ * orchestrator itself was that old. AISDLC-78 anchors `--version` to
9
+ * the package.json that ships with the binary and warns when components
10
+ * disagree.
11
+ */
12
+ import { existsSync, readFileSync } from 'node:fs';
13
+ import { dirname, join, resolve } from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+ const FALLBACK_VERSION = '0.0.0';
16
+ function readPackageVersion(pkgPath) {
17
+ try {
18
+ if (!existsSync(pkgPath))
19
+ return undefined;
20
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
21
+ return pkg.version;
22
+ }
23
+ catch {
24
+ return undefined;
25
+ }
26
+ }
27
+ /**
28
+ * Resolve the orchestrator package's own package.json. The CLI bundle
29
+ * lives at `dist/cli/index.js` so we walk up two levels.
30
+ */
31
+ function findOrchestratorPackageJson() {
32
+ // import.meta.url points at the compiled CLI module location.
33
+ const here = dirname(fileURLToPath(import.meta.url));
34
+ // Try ../package.json (when running from dist/cli/) and ../../package.json
35
+ // (when running from src/cli/ in tests via tsx).
36
+ const candidates = [
37
+ resolve(here, '..', 'package.json'),
38
+ resolve(here, '..', '..', 'package.json'),
39
+ ];
40
+ for (const candidate of candidates) {
41
+ if (existsSync(candidate)) {
42
+ try {
43
+ const pkg = JSON.parse(readFileSync(candidate, 'utf-8'));
44
+ if (pkg.name === '@ai-sdlc/orchestrator')
45
+ return candidate;
46
+ }
47
+ catch {
48
+ // continue
49
+ }
50
+ }
51
+ }
52
+ return candidates[0];
53
+ }
54
+ /**
55
+ * Best-effort search for the plugin manifest. We look in the project
56
+ * working directory first (the common case for a co-located checkout),
57
+ * then a few well-known parent paths. Returns undefined silently when
58
+ * not found — the plugin is optional metadata.
59
+ */
60
+ function findPluginVersion(workDir) {
61
+ const tries = [
62
+ join(workDir, 'ai-sdlc-plugin', 'plugin.json'),
63
+ join(workDir, '..', 'ai-sdlc-plugin', 'plugin.json'),
64
+ join(workDir, '..', '..', 'ai-sdlc-plugin', 'plugin.json'),
65
+ ];
66
+ for (const t of tries) {
67
+ const v = readPackageVersion(t);
68
+ if (v)
69
+ return v;
70
+ }
71
+ return undefined;
72
+ }
73
+ /**
74
+ * Compute the active CLI/orchestrator/plugin versions.
75
+ *
76
+ * Drift detection: when at least two known components disagree on
77
+ * version string (case-insensitive), `drift` is set. The plugin is
78
+ * compared only when discovered.
79
+ */
80
+ export function resolveVersions(opts = {}) {
81
+ const orchPath = opts.orchestratorPackageJsonPath ?? findOrchestratorPackageJson();
82
+ const orchestrator = readPackageVersion(orchPath) ?? FALLBACK_VERSION;
83
+ // The CLI ships from the same package.json today, so cli === orchestrator
84
+ // by construction. Drift detection between cli/orchestrator is therefore
85
+ // a no-op until the CLI is split out (planned: a thinner veneer that
86
+ // versions independently of the runtime). We keep them logically
87
+ // separate so callers and tests don't have to change shape on that day.
88
+ const cli = orchestrator;
89
+ const plugin = opts.pluginVersionOverride ?? findPluginVersion(opts.workDir ?? process.cwd());
90
+ const observed = [cli, orchestrator, plugin].filter((v) => Boolean(v));
91
+ const drift = new Set(observed.map((v) => v.toLowerCase())).size > 1;
92
+ return { cli, orchestrator, plugin, drift };
93
+ }
94
+ /**
95
+ * Render the canonical 3-line version block. Always emits a
96
+ * trailing newline. When drift is detected, appends a warning line
97
+ * pointing at the upgrade hint.
98
+ *
99
+ * The CLI and orchestrator rows are rendered separately even though
100
+ * today they always show the same number — both ship from the same
101
+ * `@ai-sdlc/orchestrator` package.json. The shape is intentionally
102
+ * future-proofed: when the CLI is split into a thinner veneer with
103
+ * its own version it will start to differ from the orchestrator
104
+ * runtime, and operators are then trained to read both rows. Until
105
+ * then a duplicate value is the honest answer.
106
+ */
107
+ export function formatVersionBlock(versions) {
108
+ const lines = [
109
+ `ai-sdlc CLI: ${versions.cli}`,
110
+ `orchestrator: ${versions.orchestrator}`,
111
+ `plugin: ${versions.plugin ?? '(not detected)'}`,
112
+ ];
113
+ if (versions.drift) {
114
+ lines.push('', 'WARN versions out of sync — components disagree.', ' Run `npm install -g @ai-sdlc/orchestrator@latest` (or pnpm equivalent) to align.');
115
+ }
116
+ return lines.join('\n');
117
+ }
118
+ /**
119
+ * Return the upgrade hint shown by unknown-subcommand handler and by
120
+ * version drift warnings. Centralised so tests can assert one string.
121
+ */
122
+ export function upgradeHint(versions) {
123
+ if (versions.drift) {
124
+ return `Detected version drift (cli=${versions.cli}, orchestrator=${versions.orchestrator}${versions.plugin ? `, plugin=${versions.plugin}` : ''}). Run \`npm install -g @ai-sdlc/orchestrator@latest\` to align.`;
125
+ }
126
+ return `Run \`ai-sdlc --version\` to confirm you are on the latest @ai-sdlc/orchestrator (currently ${versions.orchestrator}).`;
127
+ }
128
+ //# sourceMappingURL=versions.js.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Classify a code area as frontend-bearing or not (RFC-0008 C3).
3
+ *
4
+ * `hasFrontendComponents` is the branch predicate in the §A.5 defect-risk
5
+ * blend: frontend-bearing areas blend code-quality signals with design-
6
+ * quality signals; pure-code areas use only code signals.
7
+ *
8
+ * Classification order:
9
+ * 1. If the state store has a recent `code_area_metrics` row for the
10
+ * area, trust that explicitly — operators can correct the default
11
+ * by writing the metric.
12
+ * 2. Otherwise, apply a path-string heuristic: common frontend
13
+ * directory/extension markers.
14
+ *
15
+ * The state-store path matters because this runs in the hot admission
16
+ * loop — we don't want to ship a filesystem crawl on every issue score.
17
+ */
18
+ import type { StateStore } from './state/store.js';
19
+ export declare function checkHasFrontendComponents(codeArea: string, store?: StateStore): boolean;
20
+ export declare function matchesFrontendHeuristic(codeArea: string): boolean;
21
+ //# sourceMappingURL=code-area-classifier.d.ts.map
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Classify a code area as frontend-bearing or not (RFC-0008 C3).
3
+ *
4
+ * `hasFrontendComponents` is the branch predicate in the §A.5 defect-risk
5
+ * blend: frontend-bearing areas blend code-quality signals with design-
6
+ * quality signals; pure-code areas use only code signals.
7
+ *
8
+ * Classification order:
9
+ * 1. If the state store has a recent `code_area_metrics` row for the
10
+ * area, trust that explicitly — operators can correct the default
11
+ * by writing the metric.
12
+ * 2. Otherwise, apply a path-string heuristic: common frontend
13
+ * directory/extension markers.
14
+ *
15
+ * The state-store path matters because this runs in the hot admission
16
+ * loop — we don't want to ship a filesystem crawl on every issue score.
17
+ */
18
+ const FRONTEND_PATH_MARKERS = [
19
+ 'components/',
20
+ 'ui/',
21
+ 'frontend/',
22
+ 'web/',
23
+ 'webapp/',
24
+ 'src/app/', // Next.js app-dir convention
25
+ 'pages/',
26
+ 'routes/',
27
+ 'views/',
28
+ ];
29
+ const FRONTEND_EXTENSIONS = ['.tsx', '.jsx', '.vue', '.svelte', '.astro'];
30
+ export function checkHasFrontendComponents(codeArea, store) {
31
+ const authoritative = store?.getCodeAreaMetrics(codeArea);
32
+ if (authoritative)
33
+ return authoritative.hasFrontendComponents ?? false;
34
+ return matchesFrontendHeuristic(codeArea);
35
+ }
36
+ export function matchesFrontendHeuristic(codeArea) {
37
+ const lower = codeArea.toLowerCase();
38
+ for (const marker of FRONTEND_PATH_MARKERS) {
39
+ if (lower.includes(marker))
40
+ return true;
41
+ }
42
+ for (const ext of FRONTEND_EXTENSIONS) {
43
+ if (lower.endsWith(ext))
44
+ return true;
45
+ }
46
+ return false;
47
+ }
48
+ //# sourceMappingURL=code-area-classifier.js.map
package/dist/config.d.ts CHANGED
@@ -5,9 +5,17 @@
5
5
  * Unlike the dogfood config loader, this version does NOT import builder
6
6
  * functions — it performs pure YAML loading and validation only.
7
7
  */
8
- import { type Pipeline, type AgentRole, type QualityGate, type AutonomyPolicy, type AdapterBinding, type AdapterRegistry } from '@ai-sdlc/reference';
8
+ import { type Pipeline, type AgentRole, type QualityGate, type AutonomyPolicy, type AdapterBinding, type DesignSystemBinding, type DesignIntentDocument, type AdapterRegistry } from '@ai-sdlc/reference';
9
9
  export interface AiSdlcConfig {
10
+ /** Default pipeline (first Pipeline resource loaded; preserved for backward compat). */
10
11
  pipeline?: Pipeline;
12
+ /**
13
+ * All Pipeline resources found in the config directory. The dual-workflow setup
14
+ * (RFC-0010 §11): one pipeline for the public GitHub-issue path (API-key billed),
15
+ * another for the internal backlog path (subscription-billed). cli-watch picks
16
+ * by name based on the issue ID prefix.
17
+ */
18
+ pipelines?: Pipeline[];
11
19
  agentRole?: AgentRole;
12
20
  qualityGate?: QualityGate;
13
21
  autonomyPolicy?: AutonomyPolicy;
@@ -15,13 +23,37 @@ export interface AiSdlcConfig {
15
23
  adapterBinding?: AdapterBinding;
16
24
  /** All AdapterBinding resources found in the config directory. */
17
25
  adapterBindings?: AdapterBinding[];
26
+ /** All DesignSystemBinding resources found in the config directory (RFC-0006). */
27
+ designSystemBindings?: DesignSystemBinding[];
28
+ /** All DesignIntentDocument resources found in the config directory (RFC-0008). */
29
+ designIntentDocuments?: DesignIntentDocument[];
18
30
  adapterRegistry?: AdapterRegistry;
31
+ /**
32
+ * Per-file load issues collected during a non-fatal load. Each entry
33
+ * is `{ file, error }` for a YAML file that failed to parse or
34
+ * validate. Non-resource YAMLs (no `apiVersion`/`kind`) and DID→DSB
35
+ * cross-reference failures are NOT recorded here — the former are
36
+ * silently skipped, the latter throw outright.
37
+ */
38
+ warnings?: ConfigLoadWarning[];
39
+ }
40
+ export interface ConfigLoadWarning {
41
+ file: string;
42
+ error: string;
19
43
  }
20
44
  /**
21
45
  * Load all YAML files from the given directory, validate each against
22
46
  * the AI-SDLC JSON Schema, and return typed resources keyed by kind.
23
47
  */
24
48
  export declare function loadConfig(configDir: string): AiSdlcConfig;
49
+ /**
50
+ * Validate that every DesignIntentDocument's `spec.designSystemRef.name`
51
+ * resolves to a loaded DesignSystemBinding. Namespace match is required
52
+ * only when both resources declare a namespace (RFC-0008 §4.5).
53
+ *
54
+ * Unidirectional: DID → DSB. DesignSystemBinding surface is not modified.
55
+ */
56
+ export declare function validateDesignIntentDocumentReferences(config: AiSdlcConfig): void;
25
57
  /**
26
58
  * Async variant of loadConfig that also scans for local adapter plugins.
27
59
  */