@ai-sdlc/orchestrator 0.5.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 (227) hide show
  1. package/dist/action-enforcement.d.ts +26 -0
  2. package/dist/action-enforcement.js +70 -0
  3. package/dist/admission-composite.d.ts +67 -0
  4. package/dist/admission-composite.js +158 -0
  5. package/dist/admission-enrichment.d.ts +142 -0
  6. package/dist/admission-enrichment.js +331 -0
  7. package/dist/admission-hc.d.ts +62 -0
  8. package/dist/admission-hc.js +83 -0
  9. package/dist/admission-score.d.ts +148 -0
  10. package/dist/admission-score.js +237 -0
  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 +49 -0
  28. package/dist/cli/commands/git-remote.js +91 -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.d.ts +26 -0
  32. package/dist/cli/commands/init.js +164 -22
  33. package/dist/cli/commands/mcp-setup.d.ts +31 -0
  34. package/dist/cli/commands/mcp-setup.js +78 -8
  35. package/dist/cli/formatters/table.js +15 -2
  36. package/dist/cli/index.d.ts +14 -1
  37. package/dist/cli/index.js +81 -20
  38. package/dist/cli/versions.d.ts +57 -0
  39. package/dist/cli/versions.js +128 -0
  40. package/dist/code-area-classifier.d.ts +21 -0
  41. package/dist/code-area-classifier.js +48 -0
  42. package/dist/config.d.ts +33 -1
  43. package/dist/config.js +78 -8
  44. package/dist/cycle-utils.d.ts +51 -0
  45. package/dist/cycle-utils.js +77 -0
  46. package/dist/database/adapters/external.d.ts +24 -0
  47. package/dist/database/adapters/external.js +80 -0
  48. package/dist/database/adapters/neon.d.ts +41 -0
  49. package/dist/database/adapters/neon.js +98 -0
  50. package/dist/database/adapters/pg-snapshot-restore.d.ts +28 -0
  51. package/dist/database/adapters/pg-snapshot-restore.js +68 -0
  52. package/dist/database/adapters/sqlite-copy.d.ts +32 -0
  53. package/dist/database/adapters/sqlite-copy.js +145 -0
  54. package/dist/database/connection-injection.d.ts +35 -0
  55. package/dist/database/connection-injection.js +93 -0
  56. package/dist/database/index.d.ts +12 -0
  57. package/dist/database/index.js +23 -0
  58. package/dist/database/registry.d.ts +13 -0
  59. package/dist/database/registry.js +27 -0
  60. package/dist/database/topology.d.ts +52 -0
  61. package/dist/database/topology.js +44 -0
  62. package/dist/database/types.d.ts +89 -0
  63. package/dist/database/types.js +26 -0
  64. package/dist/defaults.d.ts +5 -0
  65. package/dist/defaults.js +5 -0
  66. package/dist/design-authority.d.ts +40 -0
  67. package/dist/design-authority.js +71 -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 +295 -30
  92. package/dist/fix-ci.js +40 -3
  93. package/dist/fix-review.d.ts +66 -0
  94. package/dist/fix-review.js +448 -0
  95. package/dist/harness/adapters/claude-code.d.ts +29 -0
  96. package/dist/harness/adapters/claude-code.js +191 -0
  97. package/dist/harness/adapters/codex.d.ts +25 -0
  98. package/dist/harness/adapters/codex.js +61 -0
  99. package/dist/harness/independence.d.ts +51 -0
  100. package/dist/harness/independence.js +67 -0
  101. package/dist/harness/index.d.ts +14 -0
  102. package/dist/harness/index.js +20 -0
  103. package/dist/harness/registry.d.ts +17 -0
  104. package/dist/harness/registry.js +31 -0
  105. package/dist/harness/types.d.ts +123 -0
  106. package/dist/harness/types.js +8 -0
  107. package/dist/harness/version-probe.d.ts +14 -0
  108. package/dist/harness/version-probe.js +123 -0
  109. package/dist/index.d.ts +39 -5
  110. package/dist/index.js +48 -3
  111. package/dist/models/classifier.d.ts +76 -0
  112. package/dist/models/classifier.js +221 -0
  113. package/dist/models/index.d.ts +3 -0
  114. package/dist/models/index.js +3 -0
  115. package/dist/models/registry.d.ts +97 -0
  116. package/dist/models/registry.js +173 -0
  117. package/dist/pillar-breakdown.d.ts +63 -0
  118. package/dist/pillar-breakdown.js +153 -0
  119. package/dist/pipeline-cycle-detector.d.ts +70 -0
  120. package/dist/pipeline-cycle-detector.js +111 -0
  121. package/dist/priority.d.ts +7 -76
  122. package/dist/priority.js +18 -5
  123. package/dist/review-meta.d.ts +65 -0
  124. package/dist/review-meta.js +149 -0
  125. package/dist/review.d.ts +35 -0
  126. package/dist/review.js +84 -0
  127. package/dist/runners/claude-code-sdk.d.ts +31 -0
  128. package/dist/runners/claude-code-sdk.js +238 -0
  129. package/dist/runners/claude-code.d.ts +3 -0
  130. package/dist/runners/claude-code.js +319 -73
  131. package/dist/runners/codex.js +4 -1
  132. package/dist/runners/copilot.js +4 -1
  133. package/dist/runners/cursor.js +4 -1
  134. package/dist/runners/git-utils.d.ts +81 -0
  135. package/dist/runners/git-utils.js +201 -0
  136. package/dist/runners/index.d.ts +5 -1
  137. package/dist/runners/index.js +4 -0
  138. package/dist/runners/review-agent.d.ts +67 -0
  139. package/dist/runners/review-agent.js +350 -0
  140. package/dist/runners/runner-registry.js +10 -0
  141. package/dist/runners/sdk-review-runner.d.ts +65 -0
  142. package/dist/runners/sdk-review-runner.js +185 -0
  143. package/dist/runners/security-triage.d.ts +20 -4
  144. package/dist/runners/security-triage.js +42 -14
  145. package/dist/runners/types.d.ts +25 -0
  146. package/dist/runtime/attestations.d.ts +274 -0
  147. package/dist/runtime/attestations.js +460 -0
  148. package/dist/runtime/git-env.d.ts +53 -0
  149. package/dist/runtime/git-env.js +60 -0
  150. package/dist/runtime/index.d.ts +7 -0
  151. package/dist/runtime/index.js +7 -0
  152. package/dist/runtime/parallelism-flag.d.ts +10 -0
  153. package/dist/runtime/parallelism-flag.js +18 -0
  154. package/dist/runtime/port-allocator.d.ts +21 -0
  155. package/dist/runtime/port-allocator.js +66 -0
  156. package/dist/runtime/worktree-pool.d.ts +86 -0
  157. package/dist/runtime/worktree-pool.js +204 -0
  158. package/dist/runtime/worktree.d.ts +25 -0
  159. package/dist/runtime/worktree.js +111 -0
  160. package/dist/sa-scoring/auto-calibrate.d.ts +69 -0
  161. package/dist/sa-scoring/auto-calibrate.js +107 -0
  162. package/dist/sa-scoring/c1-sa2-computable.d.ts +42 -0
  163. package/dist/sa-scoring/c1-sa2-computable.js +59 -0
  164. package/dist/sa-scoring/composite.d.ts +107 -0
  165. package/dist/sa-scoring/composite.js +139 -0
  166. package/dist/sa-scoring/depparse-client.d.ts +79 -0
  167. package/dist/sa-scoring/depparse-client.js +187 -0
  168. package/dist/sa-scoring/did-compiler.d.ts +122 -0
  169. package/dist/sa-scoring/did-compiler.js +286 -0
  170. package/dist/sa-scoring/drift-monitor.d.ts +84 -0
  171. package/dist/sa-scoring/drift-monitor.js +186 -0
  172. package/dist/sa-scoring/exemplar-bank.d.ts +78 -0
  173. package/dist/sa-scoring/exemplar-bank.js +154 -0
  174. package/dist/sa-scoring/feedback-store.d.ts +100 -0
  175. package/dist/sa-scoring/feedback-store.js +156 -0
  176. package/dist/sa-scoring/index.d.ts +71 -0
  177. package/dist/sa-scoring/index.js +158 -0
  178. package/dist/sa-scoring/layer1-deterministic.d.ts +115 -0
  179. package/dist/sa-scoring/layer1-deterministic.js +298 -0
  180. package/dist/sa-scoring/layer2-structural.d.ts +71 -0
  181. package/dist/sa-scoring/layer2-structural.js +151 -0
  182. package/dist/sa-scoring/layer3-llm.d.ts +86 -0
  183. package/dist/sa-scoring/layer3-llm.js +282 -0
  184. package/dist/sa-scoring/rescore-orchestrator.d.ts +52 -0
  185. package/dist/sa-scoring/rescore-orchestrator.js +47 -0
  186. package/dist/scheduling/burn-down.d.ts +27 -0
  187. package/dist/scheduling/burn-down.js +43 -0
  188. package/dist/scheduling/calibration.d.ts +42 -0
  189. package/dist/scheduling/calibration.js +150 -0
  190. package/dist/scheduling/index.d.ts +8 -0
  191. package/dist/scheduling/index.js +8 -0
  192. package/dist/scheduling/ledger.d.ts +59 -0
  193. package/dist/scheduling/ledger.js +216 -0
  194. package/dist/scheduling/off-peak.d.ts +27 -0
  195. package/dist/scheduling/off-peak.js +112 -0
  196. package/dist/scheduling/schedule-decision.d.ts +39 -0
  197. package/dist/scheduling/schedule-decision.js +89 -0
  198. package/dist/scheduling/tier-analysis.d.ts +47 -0
  199. package/dist/scheduling/tier-analysis.js +81 -0
  200. package/dist/scheduling/types.d.ts +140 -0
  201. package/dist/scheduling/types.js +11 -0
  202. package/dist/shared.d.ts +13 -0
  203. package/dist/shared.js +32 -0
  204. package/dist/state/index.d.ts +1 -1
  205. package/dist/state/schema.d.ts +6 -1
  206. package/dist/state/schema.js +276 -1
  207. package/dist/state/store.d.ts +71 -1
  208. package/dist/state/store.js +464 -7
  209. package/dist/state/types.d.ts +174 -0
  210. package/dist/types.d.ts +1 -1
  211. package/dist/validate-agent-output.js +4 -1
  212. package/dist/watch.js +6 -0
  213. package/dist/workflow-patterns/artifact-writer.d.ts +16 -0
  214. package/dist/workflow-patterns/artifact-writer.js +34 -0
  215. package/dist/workflow-patterns/classifiers.d.ts +10 -0
  216. package/dist/workflow-patterns/classifiers.js +72 -0
  217. package/dist/workflow-patterns/detector.d.ts +27 -0
  218. package/dist/workflow-patterns/detector.js +186 -0
  219. package/dist/workflow-patterns/index.d.ts +8 -0
  220. package/dist/workflow-patterns/index.js +7 -0
  221. package/dist/workflow-patterns/proposal-generator.d.ts +15 -0
  222. package/dist/workflow-patterns/proposal-generator.js +183 -0
  223. package/dist/workflow-patterns/telemetry-ingest.d.ts +27 -0
  224. package/dist/workflow-patterns/telemetry-ingest.js +103 -0
  225. package/dist/workflow-patterns/types.d.ts +61 -0
  226. package/dist/workflow-patterns/types.js +11 -0
  227. package/package.json +2 -2
@@ -0,0 +1,49 @@
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
+ export declare function detectGitRemote(opts?: DetectRemoteOptions): RemoteInfo;
44
+ /**
45
+ * Substitute `your-org` (and optionally `your-repo`) placeholders in a
46
+ * template body with detected values.
47
+ */
48
+ export declare function applyRemoteToPipelineYaml(template: string, info: RemoteInfo): string;
49
+ //# sourceMappingURL=git-remote.d.ts.map
@@ -0,0 +1,91 @@
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
+ const FALLBACK = { org: 'your-org', repo: 'your-repo', detected: false };
13
+ /**
14
+ * Parse a single remote URL into org/repo. Supports:
15
+ * - https://github.com/foo/bar.git
16
+ * - https://github.com/foo/bar
17
+ * - git@github.com:foo/bar.git
18
+ * - ssh://git@github.com/foo/bar.git
19
+ * - git@gitlab.example.com:group/subgroup/repo.git (org=group, repo=repo)
20
+ */
21
+ export function parseRemoteUrl(url) {
22
+ const trimmed = url.trim();
23
+ if (!trimmed)
24
+ return null;
25
+ // SSH shorthand: git@host:org/repo.git
26
+ const sshShort = /^[^@\s]+@[^:\s]+:(.+?)\/([^/\s]+?)(?:\.git)?$/.exec(trimmed);
27
+ if (sshShort) {
28
+ return { org: sshShort[1].split('/').slice(-1)[0], repo: sshShort[2], detected: true };
29
+ }
30
+ // SSH or HTTPS with a scheme
31
+ let parsed = null;
32
+ try {
33
+ parsed = new URL(trimmed);
34
+ }
35
+ catch {
36
+ parsed = null;
37
+ }
38
+ if (parsed) {
39
+ const path = parsed.pathname.replace(/^\/+/, '').replace(/\.git$/, '');
40
+ const segments = path.split('/').filter(Boolean);
41
+ if (segments.length >= 2) {
42
+ const repo = segments[segments.length - 1];
43
+ const org = segments[segments.length - 2];
44
+ return { org, repo, detected: true };
45
+ }
46
+ }
47
+ return null;
48
+ }
49
+ /**
50
+ * Detect the GitHub-style org/repo from the project's git origin remote.
51
+ * Returns FALLBACK with detected=false when no remote is configured or
52
+ * when the URL cannot be parsed.
53
+ */
54
+ export function detectGitRemote(opts = {}) {
55
+ const cwd = opts.cwd ?? process.cwd();
56
+ const exec = opts.execImpl ?? defaultExec;
57
+ let url;
58
+ try {
59
+ url = exec('git remote get-url origin', {
60
+ cwd,
61
+ encoding: 'utf-8',
62
+ stdio: ['ignore', 'pipe', 'ignore'],
63
+ });
64
+ }
65
+ catch {
66
+ return FALLBACK;
67
+ }
68
+ const parsed = parseRemoteUrl(url);
69
+ return parsed ?? FALLBACK;
70
+ }
71
+ function defaultExec(cmd, opts) {
72
+ return execSync(cmd, {
73
+ cwd: opts.cwd,
74
+ encoding: opts.encoding,
75
+ stdio: opts.stdio,
76
+ });
77
+ }
78
+ /**
79
+ * Substitute `your-org` (and optionally `your-repo`) placeholders in a
80
+ * template body with detected values.
81
+ */
82
+ export function applyRemoteToPipelineYaml(template, info) {
83
+ // Replace the literal `org: your-org` config line with the detected
84
+ // org. We deliberately match the exact YAML key shape rather than a
85
+ // bare token so we don't smear the placeholder if it appears elsewhere.
86
+ let out = template.replace(/(\borg:\s*)your-org(\b)/g, `$1${info.org}$2`);
87
+ // If the template ever introduces a repo: placeholder, substitute it too.
88
+ out = out.replace(/(\brepo:\s*)your-repo(\b)/g, `$1${info.repo}$2`);
89
+ return out;
90
+ }
91
+ //# 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();
@@ -1,6 +1,32 @@
1
1
  /**
2
2
  * ai-sdlc init — initialize a project with AI-SDLC config files.
3
+ *
4
+ * AISDLC-78 enhancements:
5
+ * - Print a 3-line version block at startup so operators see CLI,
6
+ * orchestrator, and plugin versions in one place. Drift triggers a
7
+ * visible warning instead of silently shipping mismatched binaries.
8
+ * - Substitute `your-org` in the pipeline.yaml template using
9
+ * `git remote get-url origin` (ssh + https forms supported). Falls
10
+ * back to the placeholder only when no remote is configured.
11
+ * - Pin `@ai-sdlc/mcp-advisor` in generated `.mcp.json` to the
12
+ * orchestrator version that ran init, with an inline opt-out hint.
13
+ * - Skip `.cursor/mcp.json` unless Cursor is detected (project-local
14
+ * `.cursor/`, user-global `~/.cursor/`) or `--cursor` is passed.
3
15
  */
4
16
  import { Command } from 'commander';
17
+ /**
18
+ * Tier-based agent-role templates (AISDLC-79).
19
+ *
20
+ * Three named tiers map to escalating tool surfaces. The default (`coding`)
21
+ * preserves pre-AISDLC-79 behavior plus `NotebookEdit` (parity with the
22
+ * Claude Code SDK's filesystem editing surface). Higher tiers add tools only
23
+ * when their use-case justifies the additional surface area / risk.
24
+ *
25
+ * See `backlog/decisions/AISDLC-79-agent-role-tools-defaults.md` for rationale.
26
+ */
27
+ export type AgentRoleTier = 'coding' | 'research' | 'meta';
28
+ export declare const AGENT_ROLE_TIERS: readonly AgentRoleTier[];
29
+ /** Resolve the agent-role.yaml template for a given tier. Default = `coding`. */
30
+ export declare function getAgentRoleYaml(tier?: AgentRoleTier): string;
5
31
  export declare const initCommand: Command;
6
32
  //# sourceMappingURL=init.d.ts.map
@@ -1,11 +1,25 @@
1
1
  /**
2
2
  * ai-sdlc init — initialize a project with AI-SDLC config files.
3
+ *
4
+ * AISDLC-78 enhancements:
5
+ * - Print a 3-line version block at startup so operators see CLI,
6
+ * orchestrator, and plugin versions in one place. Drift triggers a
7
+ * visible warning instead of silently shipping mismatched binaries.
8
+ * - Substitute `your-org` in the pipeline.yaml template using
9
+ * `git remote get-url origin` (ssh + https forms supported). Falls
10
+ * back to the placeholder only when no remote is configured.
11
+ * - Pin `@ai-sdlc/mcp-advisor` in generated `.mcp.json` to the
12
+ * orchestrator version that ran init, with an inline opt-out hint.
13
+ * - Skip `.cursor/mcp.json` unless Cursor is detected (project-local
14
+ * `.cursor/`, user-global `~/.cursor/`) or `--cursor` is passed.
3
15
  */
4
16
  import { Command } from 'commander';
5
17
  import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync } from 'node:fs';
6
18
  import { join, basename } from 'node:path';
7
- import { detectAgents, installMcpServer } from './mcp-setup.js';
19
+ import { detectAgentsDetailed, installMcpServer } from './mcp-setup.js';
8
20
  import { detectWorkspace, generateWorkspaceYaml } from './workspace-detect.js';
21
+ import { detectGitRemote, applyRemoteToPipelineYaml } from './git-remote.js';
22
+ import { resolveVersions, formatVersionBlock } from '../versions.js';
9
23
  const PIPELINE_YAML = `apiVersion: ai-sdlc.io/v1alpha1
10
24
  kind: Pipeline
11
25
  metadata:
@@ -35,20 +49,31 @@ spec:
35
49
  qualityGates:
36
50
  - default-gates
37
51
  `;
38
- const AGENT_ROLE_YAML = `apiVersion: ai-sdlc.io/v1alpha1
52
+ export const AGENT_ROLE_TIERS = ['coding', 'research', 'meta'];
53
+ const AGENT_ROLE_YAML_CODING = `apiVersion: ai-sdlc.io/v1alpha1
39
54
  kind: AgentRole
40
55
  metadata:
41
56
  name: default-agent
42
57
  spec:
43
58
  role: developer
44
59
  goal: Implement issue requirements with tests
60
+ # Tier: coding-agent (default).
61
+ # Filesystem + shell editing surface only. No web access, no sub-agent
62
+ # spawning, no Skill loading. This matches the pre-AISDLC-79 default
63
+ # plus NotebookEdit (parity with the Claude Code SDK's editing surface).
45
64
  tools:
46
- - Edit
47
- - Write
48
- - Read
49
- - Glob
50
- - Grep
51
- - Bash
65
+ - Edit # write to existing files
66
+ - Write # create new files
67
+ - Read # read source / configs
68
+ - Glob # discover files by pattern
69
+ - Grep # search code
70
+ - Bash # run tests, lint, build, git
71
+ - NotebookEdit # edit Jupyter notebooks (parity with SDK editing surface)
72
+ # Excluded by default (opt in via --role research|meta during init):
73
+ # - WebFetch — pulls untrusted remote content into context (research tier)
74
+ # - WebSearch — same (research tier)
75
+ # - Task — spawns sub-agents; cost + reasoning amplification (meta tier)
76
+ # - Skill — loads external skill packs (meta tier)
52
77
  constraints:
53
78
  maxFilesPerChange: 15
54
79
  requireTests: true
@@ -56,6 +81,75 @@ spec:
56
81
  - .github/workflows/**
57
82
  - .ai-sdlc/**
58
83
  `;
84
+ const AGENT_ROLE_YAML_RESEARCH = `apiVersion: ai-sdlc.io/v1alpha1
85
+ kind: AgentRole
86
+ metadata:
87
+ name: default-agent
88
+ spec:
89
+ role: research-developer
90
+ goal: Investigate, gather references, and implement issue requirements with tests
91
+ # Tier: research-agent.
92
+ # Coding-tier surface plus read-only web access. Use for tasks that need
93
+ # to consult external docs, package registries, or RFCs.
94
+ tools:
95
+ - Edit # write to existing files
96
+ - Write # create new files
97
+ - Read # read source / configs
98
+ - Glob # discover files by pattern
99
+ - Grep # search code
100
+ - Bash # run tests, lint, build, git
101
+ - NotebookEdit # edit Jupyter notebooks
102
+ - WebFetch # fetch a known URL (docs, RFCs, API references)
103
+ - WebSearch # discover sources by query
104
+ # Excluded (use --role meta to opt in):
105
+ # - Task — spawns sub-agents (meta tier)
106
+ # - Skill — loads external skill packs (meta tier)
107
+ constraints:
108
+ maxFilesPerChange: 15
109
+ requireTests: true
110
+ blockedPaths:
111
+ - .github/workflows/**
112
+ - .ai-sdlc/**
113
+ `;
114
+ const AGENT_ROLE_YAML_META = `apiVersion: ai-sdlc.io/v1alpha1
115
+ kind: AgentRole
116
+ metadata:
117
+ name: default-agent
118
+ spec:
119
+ role: meta-developer
120
+ goal: Orchestrate sub-agents, load skill packs, and implement complex multi-step work
121
+ # Tier: meta-agent.
122
+ # Full surface: filesystem + shell + web + sub-agent + skill loading.
123
+ # Use only when the task legitimately needs sub-agent fan-out or external
124
+ # skill packs. Higher cost amplification and broader attack surface.
125
+ tools:
126
+ - Edit # write to existing files
127
+ - Write # create new files
128
+ - Read # read source / configs
129
+ - Glob # discover files by pattern
130
+ - Grep # search code
131
+ - Bash # run tests, lint, build, git
132
+ - NotebookEdit # edit Jupyter notebooks
133
+ - WebFetch # fetch a known URL
134
+ - WebSearch # discover sources by query
135
+ - Task # spawn sub-agents for parallel / specialized work
136
+ - Skill # load external skill packs (declared capabilities)
137
+ constraints:
138
+ maxFilesPerChange: 15
139
+ requireTests: true
140
+ blockedPaths:
141
+ - .github/workflows/**
142
+ - .ai-sdlc/**
143
+ `;
144
+ const AGENT_ROLE_YAML_BY_TIER = {
145
+ coding: AGENT_ROLE_YAML_CODING,
146
+ research: AGENT_ROLE_YAML_RESEARCH,
147
+ meta: AGENT_ROLE_YAML_META,
148
+ };
149
+ /** Resolve the agent-role.yaml template for a given tier. Default = `coding`. */
150
+ export function getAgentRoleYaml(tier = 'coding') {
151
+ return AGENT_ROLE_YAML_BY_TIER[tier];
152
+ }
59
153
  const QUALITY_GATE_YAML = `apiVersion: ai-sdlc.io/v1alpha1
60
154
  kind: QualityGate
61
155
  metadata:
@@ -139,24 +233,26 @@ const GITIGNORE_PATHS = ['.ai-sdlc/state.db', '.ai-sdlc/state/', '.ai-sdlc/audit
139
233
  function ensureGitignore(projectDir, dryRun, prefix = '') {
140
234
  const gitignorePath = join(projectDir, '.gitignore');
141
235
  const existing = existsSync(gitignorePath) ? readFileSync(gitignorePath, 'utf-8') : '';
142
- // Only check path entries (not comments) to avoid duplicate blocks
143
- const missing = GITIGNORE_PATHS.filter((entry) => !existing.includes(entry));
236
+ const SENTINEL = '# ai-sdlc:runtime-gitignore';
237
+ if (existing.includes(SENTINEL))
238
+ return;
239
+ const missing = GITIGNORE_PATHS.filter((entry) => !existing.split('\n').some((line) => line.trim() === entry));
144
240
  if (missing.length === 0)
145
241
  return;
146
242
  if (dryRun) {
147
243
  console.log(`${prefix} Would update .gitignore`);
148
244
  return;
149
245
  }
150
- const block = '\n# AI-SDLC runtime artifacts\n' + missing.join('\n') + '\n';
246
+ const block = (existing.length > 0 ? '\n' : '') + `${SENTINEL}\n` + missing.join('\n') + '\n';
151
247
  appendFileSync(gitignorePath, block, 'utf-8');
152
248
  console.log(`${prefix} updated .gitignore`);
153
249
  }
154
250
  /** Initialize a single project directory with AI-SDLC config files. */
155
- function initProject(projectDir, configDirName, dryRun, prefix = '') {
251
+ function initProject(projectDir, configDirName, dryRun, prefix = '', options = { pipelineYaml: PIPELINE_YAML, tier: 'coding' }) {
156
252
  const configDir = join(projectDir, configDirName);
157
253
  const files = [
158
- { name: 'pipeline.yaml', content: PIPELINE_YAML },
159
- { name: 'agent-role.yaml', content: AGENT_ROLE_YAML },
254
+ { name: 'pipeline.yaml', content: options.pipelineYaml },
255
+ { name: 'agent-role.yaml', content: getAgentRoleYaml(options.tier) },
160
256
  { name: 'quality-gate.yaml', content: QUALITY_GATE_YAML },
161
257
  { name: 'autonomy-policy.yaml', content: AUTONOMY_POLICY_YAML },
162
258
  ];
@@ -209,11 +305,35 @@ export const initCommand = new Command('init')
209
305
  .description('Initialize AI-SDLC configuration in the current project')
210
306
  .option('--dry-run', 'Show what would be created without writing files')
211
307
  .option('--skip-mcp', 'Skip MCP server auto-configuration')
308
+ .option('--cursor', 'Force-install Cursor MCP config even if Cursor is not detected')
212
309
  .option('-d, --dir <path>', 'Config directory name', '.ai-sdlc')
310
+ .option('--role <tier>', `Agent-role tool tier: ${AGENT_ROLE_TIERS.join(' | ')} (default: coding)`, 'coding')
213
311
  .action(async (opts) => {
214
312
  const projectDir = process.cwd();
215
313
  const configDirName = opts.dir ?? '.ai-sdlc';
216
314
  const dryRun = !!opts.dryRun;
315
+ const tierInput = (opts.role ?? 'coding');
316
+ if (!AGENT_ROLE_TIERS.includes(tierInput)) {
317
+ console.error(`Error: invalid --role value '${tierInput}'. Expected one of: ${AGENT_ROLE_TIERS.join(', ')}.`);
318
+ process.exitCode = 1;
319
+ return;
320
+ }
321
+ const tier = tierInput;
322
+ const cursorOptIn = !!opts.cursor;
323
+ // ── version provenance (AC #1) ────────────────────────────────────
324
+ const versions = resolveVersions({ workDir: projectDir });
325
+ console.log(formatVersionBlock(versions));
326
+ console.log('');
327
+ // ── pipeline.yaml org/repo substitution (AC #2) ───────────────────
328
+ const remote = detectGitRemote({ cwd: projectDir });
329
+ const pipelineYaml = applyRemoteToPipelineYaml(PIPELINE_YAML, remote);
330
+ if (remote.detected) {
331
+ console.log(`Detected git remote: ${remote.org}/${remote.repo}`);
332
+ }
333
+ else {
334
+ console.log(`No git origin remote detected — pipeline.yaml will use the 'your-org' placeholder.`);
335
+ }
336
+ console.log('');
217
337
  // Detect workspace (multi-repo parent directory)
218
338
  const workspace = detectWorkspace(projectDir);
219
339
  if (workspace.isWorkspace) {
@@ -227,14 +347,30 @@ export const initCommand = new Command('init')
227
347
  // Cascade into each child repo
228
348
  for (const repo of workspace.repos) {
229
349
  console.log(`\n${repo.name}/`);
230
- initProject(repo.absPath, configDirName, dryRun, ' ');
350
+ // Per-repo remote detection so each child gets its own org/repo.
351
+ const childRemote = detectGitRemote({ cwd: repo.absPath });
352
+ const childPipeline = applyRemoteToPipelineYaml(PIPELINE_YAML, childRemote);
353
+ if (childRemote.detected) {
354
+ console.log(` detected remote: ${childRemote.org}/${childRemote.repo}`);
355
+ }
356
+ initProject(repo.absPath, configDirName, dryRun, ' ', {
357
+ pipelineYaml: childPipeline,
358
+ tier,
359
+ });
231
360
  }
232
361
  // MCP setup at workspace root (serves all repos)
233
362
  if (!opts.skipMcp) {
234
- const agents = detectAgents(projectDir, { isWorkspace: true });
363
+ const { detected, skipped } = detectAgentsDetailed(projectDir, {
364
+ isWorkspace: true,
365
+ pinVersion: versions.orchestrator,
366
+ cursorOptIn,
367
+ });
235
368
  console.log(`\nMCP server setup (workspace root):`);
236
- console.log(` detected ${agents.map((a) => a.name).join(', ')}`);
237
- for (const agent of agents) {
369
+ console.log(` detected ${detected.map((a) => a.name).join(', ') || '(none)'}`);
370
+ for (const sk of skipped) {
371
+ console.log(` skip ${sk.name} — ${sk.reason}`);
372
+ }
373
+ for (const agent of detected) {
238
374
  const status = installMcpServer(projectDir, agent, dryRun);
239
375
  const pad = status === 'merged' ? ' ' : '';
240
376
  console.log(` ${status}${pad} ${agent.configPath} (${agent.name})`);
@@ -245,12 +381,18 @@ export const initCommand = new Command('init')
245
381
  }
246
382
  else {
247
383
  // Single-repo mode (original behavior)
248
- initProject(projectDir, configDirName, dryRun);
384
+ initProject(projectDir, configDirName, dryRun, '', { pipelineYaml, tier });
249
385
  if (!opts.skipMcp) {
250
- const agents = detectAgents(projectDir);
386
+ const { detected, skipped } = detectAgentsDetailed(projectDir, {
387
+ pinVersion: versions.orchestrator,
388
+ cursorOptIn,
389
+ });
251
390
  console.log(`\nMCP server setup:`);
252
- console.log(` detected ${agents.map((a) => a.name).join(', ')}`);
253
- for (const agent of agents) {
391
+ console.log(` detected ${detected.map((a) => a.name).join(', ') || '(none)'}`);
392
+ for (const sk of skipped) {
393
+ console.log(` skip ${sk.name} — ${sk.reason}`);
394
+ }
395
+ for (const agent of detected) {
254
396
  const status = installMcpServer(projectDir, agent, dryRun);
255
397
  const pad = status === 'merged' ? ' ' : '';
256
398
  console.log(` ${status}${pad} ${agent.configPath} (${agent.name})`);
@@ -1,5 +1,17 @@
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
  export interface DetectedAgent {
5
17
  name: string;
@@ -10,7 +22,26 @@ export interface DetectedAgent {
10
22
  export interface DetectAgentsOptions {
11
23
  /** When true, adds AI_SDLC_WORKSPACE env to server entries. */
12
24
  isWorkspace?: boolean;
25
+ /** Pin the mcp-advisor to this version in generated configs. */
26
+ pinVersion?: string;
27
+ /** User explicitly asked for Cursor MCP install. */
28
+ cursorOptIn?: boolean;
13
29
  }
30
+ /**
31
+ * Returned alongside detected agents to log skip decisions.
32
+ */
33
+ export interface DetectAgentsResult {
34
+ detected: DetectedAgent[];
35
+ /** Per-spec skip explanations for human-readable logging. */
36
+ skipped: Array<{
37
+ name: string;
38
+ reason: string;
39
+ }>;
40
+ }
41
+ export declare function detectAgentsDetailed(projectDir: string, options?: DetectAgentsOptions): DetectAgentsResult;
42
+ /**
43
+ * Backwards-compatible wrapper. New code should prefer detectAgentsDetailed.
44
+ */
14
45
  export declare function detectAgents(projectDir: string, options?: DetectAgentsOptions): DetectedAgent[];
15
46
  export declare function installMcpServer(projectDir: string, agent: DetectedAgent, dryRun: boolean): 'created' | 'merged' | 'skipped';
16
47
  //# sourceMappingURL=mcp-setup.d.ts.map