@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.
- package/dist/__test-helpers/git-env.d.ts +40 -0
- package/dist/__test-helpers/git-env.js +73 -0
- package/dist/admission-composite.d.ts +89 -0
- package/dist/admission-composite.js +241 -0
- package/dist/admission-enrichment.d.ts +142 -0
- package/dist/admission-enrichment.js +341 -0
- package/dist/admission-hc.d.ts +81 -0
- package/dist/admission-hc.js +94 -0
- package/dist/admission-score.d.ts +109 -5
- package/dist/admission-score.js +90 -11
- package/dist/analysis/analyzer.js +3 -2
- package/dist/analysis/convention-detector.d.ts +85 -2
- package/dist/analysis/convention-detector.js +375 -70
- package/dist/analysis/diff-analyzer.d.ts +33 -0
- package/dist/analysis/diff-analyzer.js +122 -0
- package/dist/analysis/hotspot-analyzer.js +3 -1
- package/dist/analysis/index.d.ts +2 -1
- package/dist/analysis/index.js +2 -1
- package/dist/artifacts/index.d.ts +65 -0
- package/dist/artifacts/index.js +142 -0
- package/dist/backlog-adapter.d.ts +121 -0
- package/dist/backlog-adapter.js +438 -0
- package/dist/calibration.d.ts +43 -0
- package/dist/calibration.js +76 -0
- package/dist/cli/commands/agents.d.ts +24 -0
- package/dist/cli/commands/agents.js +66 -1
- package/dist/cli/commands/git-remote.d.ts +74 -0
- package/dist/cli/commands/git-remote.js +170 -0
- package/dist/cli/commands/health.d.ts +4 -0
- package/dist/cli/commands/health.js +13 -1
- package/dist/cli/commands/init-features.d.ts +208 -0
- package/dist/cli/commands/init-features.js +473 -0
- package/dist/cli/commands/init-templates.d.ts +104 -0
- package/dist/cli/commands/init-templates.js +399 -0
- package/dist/cli/commands/init.d.ts +48 -0
- package/dist/cli/commands/init.js +322 -23
- package/dist/cli/commands/mcp-setup.d.ts +31 -0
- package/dist/cli/commands/mcp-setup.js +78 -8
- package/dist/cli/formatters/table.js +15 -2
- package/dist/cli/index.d.ts +14 -1
- package/dist/cli/index.js +81 -20
- package/dist/cli/versions.d.ts +57 -0
- package/dist/cli/versions.js +128 -0
- package/dist/code-area-classifier.d.ts +21 -0
- package/dist/code-area-classifier.js +48 -0
- package/dist/config.d.ts +33 -1
- package/dist/config.js +78 -8
- package/dist/database/adapters/external.d.ts +24 -0
- package/dist/database/adapters/external.js +80 -0
- package/dist/database/adapters/neon.d.ts +41 -0
- package/dist/database/adapters/neon.js +98 -0
- package/dist/database/adapters/pg-snapshot-restore.d.ts +28 -0
- package/dist/database/adapters/pg-snapshot-restore.js +68 -0
- package/dist/database/adapters/sqlite-copy.d.ts +32 -0
- package/dist/database/adapters/sqlite-copy.js +145 -0
- package/dist/database/connection-injection.d.ts +35 -0
- package/dist/database/connection-injection.js +93 -0
- package/dist/database/index.d.ts +12 -0
- package/dist/database/index.js +23 -0
- package/dist/database/registry.d.ts +13 -0
- package/dist/database/registry.js +27 -0
- package/dist/database/topology.d.ts +52 -0
- package/dist/database/topology.js +44 -0
- package/dist/database/types.d.ts +89 -0
- package/dist/database/types.js +26 -0
- package/dist/design-authority.d.ts +53 -0
- package/dist/design-authority.js +84 -0
- package/dist/design-lookahead.d.ts +64 -0
- package/dist/design-lookahead.js +86 -0
- package/dist/design-quality-trend.d.ts +87 -0
- package/dist/design-quality-trend.js +190 -0
- package/dist/design-system-context.d.ts +46 -0
- package/dist/design-system-context.js +80 -0
- package/dist/design-system-correction-loop.d.ts +64 -0
- package/dist/design-system-correction-loop.js +128 -0
- package/dist/design-system-metrics.d.ts +61 -0
- package/dist/design-system-metrics.js +104 -0
- package/dist/design-system-stewardship.d.ts +22 -0
- package/dist/design-system-stewardship.js +85 -0
- package/dist/design-system-validation.d.ts +37 -0
- package/dist/design-system-validation.js +88 -0
- package/dist/dispatch/index.d.ts +4 -0
- package/dist/dispatch/index.js +4 -0
- package/dist/dispatch/merge-gate.d.ts +46 -0
- package/dist/dispatch/merge-gate.js +90 -0
- package/dist/dispatch/requeue.d.ts +57 -0
- package/dist/dispatch/requeue.js +131 -0
- package/dist/dispatch/worker-pool.d.ts +62 -0
- package/dist/dispatch/worker-pool.js +60 -0
- package/dist/execute.d.ts +40 -0
- package/dist/execute.js +188 -15
- package/dist/fix-ci.js +8 -1
- package/dist/fix-review.js +8 -1
- package/dist/harness/adapters/claude-code.d.ts +29 -0
- package/dist/harness/adapters/claude-code.js +191 -0
- package/dist/harness/adapters/codex.d.ts +25 -0
- package/dist/harness/adapters/codex.js +61 -0
- package/dist/harness/independence.d.ts +51 -0
- package/dist/harness/independence.js +67 -0
- package/dist/harness/index.d.ts +14 -0
- package/dist/harness/index.js +20 -0
- package/dist/harness/registry.d.ts +17 -0
- package/dist/harness/registry.js +31 -0
- package/dist/harness/types.d.ts +123 -0
- package/dist/harness/types.js +8 -0
- package/dist/harness/version-probe.d.ts +14 -0
- package/dist/harness/version-probe.js +123 -0
- package/dist/index.d.ts +30 -4
- package/dist/index.js +35 -2
- package/dist/models/classifier.d.ts +78 -0
- package/dist/models/classifier.js +277 -0
- package/dist/models/index.d.ts +3 -0
- package/dist/models/index.js +3 -0
- package/dist/models/registry.d.ts +97 -0
- package/dist/models/registry.js +173 -0
- package/dist/pillar-breakdown.d.ts +85 -0
- package/dist/pillar-breakdown.js +162 -0
- package/dist/priority.d.ts +5 -0
- package/dist/priority.js +18 -5
- package/dist/review-meta.d.ts +65 -0
- package/dist/review-meta.js +149 -0
- package/dist/review.d.ts +4 -0
- package/dist/review.js +12 -2
- package/dist/runners/claude-code-sdk.d.ts +31 -0
- package/dist/runners/claude-code-sdk.js +238 -0
- package/dist/runners/claude-code.d.ts +3 -0
- package/dist/runners/claude-code.js +30 -66
- package/dist/runners/codex.js +4 -1
- package/dist/runners/copilot.js +4 -1
- package/dist/runners/cursor.js +4 -1
- package/dist/runners/git-utils.d.ts +81 -0
- package/dist/runners/git-utils.js +201 -0
- package/dist/runners/index.d.ts +3 -0
- package/dist/runners/index.js +3 -0
- package/dist/runners/review-agent.d.ts +20 -0
- package/dist/runners/review-agent.js +171 -41
- package/dist/runners/runner-registry.js +10 -0
- package/dist/runners/sdk-review-runner.d.ts +65 -0
- package/dist/runners/sdk-review-runner.js +185 -0
- package/dist/runners/security-triage.d.ts +20 -4
- package/dist/runners/security-triage.js +39 -15
- package/dist/runners/types.d.ts +6 -0
- package/dist/runtime/attestations.d.ts +766 -0
- package/dist/runtime/attestations.js +1195 -0
- package/dist/runtime/git-env.d.ts +53 -0
- package/dist/runtime/git-env.js +60 -0
- package/dist/runtime/index.d.ts +7 -0
- package/dist/runtime/index.js +7 -0
- package/dist/runtime/parallelism-flag.d.ts +28 -0
- package/dist/runtime/parallelism-flag.js +39 -0
- package/dist/runtime/port-allocator.d.ts +32 -0
- package/dist/runtime/port-allocator.js +96 -0
- package/dist/runtime/worktree-pool.d.ts +86 -0
- package/dist/runtime/worktree-pool.js +204 -0
- package/dist/runtime/worktree.d.ts +25 -0
- package/dist/runtime/worktree.js +111 -0
- package/dist/sa-scoring/auto-calibrate.d.ts +69 -0
- package/dist/sa-scoring/auto-calibrate.js +107 -0
- package/dist/sa-scoring/c1-sa2-computable.d.ts +42 -0
- package/dist/sa-scoring/c1-sa2-computable.js +59 -0
- package/dist/sa-scoring/composite.d.ts +107 -0
- package/dist/sa-scoring/composite.js +139 -0
- package/dist/sa-scoring/depparse-client.d.ts +79 -0
- package/dist/sa-scoring/depparse-client.js +187 -0
- package/dist/sa-scoring/did-compiler.d.ts +122 -0
- package/dist/sa-scoring/did-compiler.js +286 -0
- package/dist/sa-scoring/drift-monitor.d.ts +84 -0
- package/dist/sa-scoring/drift-monitor.js +186 -0
- package/dist/sa-scoring/exemplar-bank.d.ts +78 -0
- package/dist/sa-scoring/exemplar-bank.js +154 -0
- package/dist/sa-scoring/feedback-store.d.ts +100 -0
- package/dist/sa-scoring/feedback-store.js +156 -0
- package/dist/sa-scoring/index.d.ts +71 -0
- package/dist/sa-scoring/index.js +158 -0
- package/dist/sa-scoring/layer1-deterministic.d.ts +115 -0
- package/dist/sa-scoring/layer1-deterministic.js +298 -0
- package/dist/sa-scoring/layer2-structural.d.ts +71 -0
- package/dist/sa-scoring/layer2-structural.js +151 -0
- package/dist/sa-scoring/layer3-llm.d.ts +86 -0
- package/dist/sa-scoring/layer3-llm.js +282 -0
- package/dist/sa-scoring/rescore-orchestrator.d.ts +52 -0
- package/dist/sa-scoring/rescore-orchestrator.js +47 -0
- package/dist/scheduling/burn-down.d.ts +27 -0
- package/dist/scheduling/burn-down.js +43 -0
- package/dist/scheduling/calibration.d.ts +42 -0
- package/dist/scheduling/calibration.js +150 -0
- package/dist/scheduling/index.d.ts +8 -0
- package/dist/scheduling/index.js +8 -0
- package/dist/scheduling/ledger.d.ts +59 -0
- package/dist/scheduling/ledger.js +216 -0
- package/dist/scheduling/off-peak.d.ts +27 -0
- package/dist/scheduling/off-peak.js +112 -0
- package/dist/scheduling/schedule-decision.d.ts +39 -0
- package/dist/scheduling/schedule-decision.js +89 -0
- package/dist/scheduling/tier-analysis.d.ts +47 -0
- package/dist/scheduling/tier-analysis.js +81 -0
- package/dist/scheduling/types.d.ts +140 -0
- package/dist/scheduling/types.js +11 -0
- package/dist/shared.d.ts +13 -0
- package/dist/shared.js +32 -0
- package/dist/state/schema.d.ts +5 -1
- package/dist/state/schema.js +223 -1
- package/dist/state/store.d.ts +55 -1
- package/dist/state/store.js +342 -7
- package/dist/state/types.d.ts +139 -0
- package/dist/types.d.ts +1 -1
- package/dist/validate-agent-output.js +4 -1
- package/dist/watch.js +6 -0
- package/package.json +3 -2
|
@@ -1,12 +1,41 @@
|
|
|
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.
|
|
15
|
+
*
|
|
16
|
+
* AISDLC-143 enhancements (Q4(b) of the quality-gate redesign):
|
|
17
|
+
* - Default invocation is now an interactive WIZARD (DoR / attestation /
|
|
18
|
+
* classifier / branch-protection prompts) so adopters get a guided
|
|
19
|
+
* bootstrap instead of having to read the docs to discover features.
|
|
20
|
+
* - `--yes` short-circuits all prompts to "accept defaults" (CI/scripts).
|
|
21
|
+
* - `--with-X` flags opt into individual features without prompting.
|
|
22
|
+
* - `--add <feature>` extends an already-initialized repo with a single
|
|
23
|
+
* feature, idempotently — re-running on an initialized repo is safe.
|
|
24
|
+
* - `.github/workflows/ai-sdlc-gate.yml` is scaffolded UNCONDITIONALLY
|
|
25
|
+
* so every adopter gets the `ai-sdlc/pr-ready` rollup check on day one
|
|
26
|
+
* (Q1: prescriptive default).
|
|
27
|
+
* - A "next steps" summary closes the run with operator action items
|
|
28
|
+
* conditional on which features were chosen.
|
|
3
29
|
*/
|
|
4
30
|
import { Command } from 'commander';
|
|
5
31
|
import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync } from 'node:fs';
|
|
6
32
|
import { join, basename } from 'node:path';
|
|
7
|
-
import {
|
|
33
|
+
import { detectAgentsDetailed, installMcpServer } from './mcp-setup.js';
|
|
8
34
|
import { detectWorkspace, generateWorkspaceYaml } from './workspace-detect.js';
|
|
9
|
-
|
|
35
|
+
import { detectGitRemote, applyRemoteToPipelineYaml } from './git-remote.js';
|
|
36
|
+
import { resolveVersions, formatVersionBlock } from '../versions.js';
|
|
37
|
+
import { applyFeatureSelection, buildProductionAdapters, ensureClaudeMdPointer, renderNextSteps, resolveFeatureSelection, } from './init-features.js';
|
|
38
|
+
export const PIPELINE_YAML = `apiVersion: ai-sdlc.io/v1alpha1
|
|
10
39
|
kind: Pipeline
|
|
11
40
|
metadata:
|
|
12
41
|
name: default
|
|
@@ -34,21 +63,49 @@ spec:
|
|
|
34
63
|
- name: review
|
|
35
64
|
qualityGates:
|
|
36
65
|
- default-gates
|
|
66
|
+
# backlog: holds settings specific to the backlog-task (/ai-sdlc execute)
|
|
67
|
+
# workflow. These were formerly in a separate pipeline-backlog.yaml file
|
|
68
|
+
# (deprecated by AISDLC-245.5). Slash commands + pipeline-cli readers
|
|
69
|
+
# prefer this canonical location.
|
|
70
|
+
backlog:
|
|
71
|
+
branching:
|
|
72
|
+
pattern: 'ai-sdlc/{issueIdLower}-{slug}'
|
|
73
|
+
targetBranch: main
|
|
74
|
+
cleanup: on-merge
|
|
75
|
+
pullRequest:
|
|
76
|
+
titleTemplate: 'feat: {issueTitle} ({issueId})'
|
|
77
|
+
descriptionSections:
|
|
78
|
+
- summary
|
|
79
|
+
- changes
|
|
80
|
+
- closes
|
|
81
|
+
includeProvenance: true
|
|
82
|
+
closeKeyword: References
|
|
37
83
|
`;
|
|
38
|
-
const
|
|
84
|
+
export const AGENT_ROLE_TIERS = ['coding', 'research', 'meta'];
|
|
85
|
+
const AGENT_ROLE_YAML_CODING = `apiVersion: ai-sdlc.io/v1alpha1
|
|
39
86
|
kind: AgentRole
|
|
40
87
|
metadata:
|
|
41
88
|
name: default-agent
|
|
42
89
|
spec:
|
|
43
90
|
role: developer
|
|
44
91
|
goal: Implement issue requirements with tests
|
|
92
|
+
# Tier: coding-agent (default).
|
|
93
|
+
# Filesystem + shell editing surface only. No web access, no sub-agent
|
|
94
|
+
# spawning, no Skill loading. This matches the pre-AISDLC-79 default
|
|
95
|
+
# plus NotebookEdit (parity with the Claude Code SDK's editing surface).
|
|
45
96
|
tools:
|
|
46
|
-
- Edit
|
|
47
|
-
- Write
|
|
48
|
-
- Read
|
|
49
|
-
- Glob
|
|
50
|
-
- Grep
|
|
51
|
-
- Bash
|
|
97
|
+
- Edit # write to existing files
|
|
98
|
+
- Write # create new files
|
|
99
|
+
- Read # read source / configs
|
|
100
|
+
- Glob # discover files by pattern
|
|
101
|
+
- Grep # search code
|
|
102
|
+
- Bash # run tests, lint, build, git
|
|
103
|
+
- NotebookEdit # edit Jupyter notebooks (parity with SDK editing surface)
|
|
104
|
+
# Excluded by default (opt in via --role research|meta during init):
|
|
105
|
+
# - WebFetch — pulls untrusted remote content into context (research tier)
|
|
106
|
+
# - WebSearch — same (research tier)
|
|
107
|
+
# - Task — spawns sub-agents; cost + reasoning amplification (meta tier)
|
|
108
|
+
# - Skill — loads external skill packs (meta tier)
|
|
52
109
|
constraints:
|
|
53
110
|
maxFilesPerChange: 15
|
|
54
111
|
requireTests: true
|
|
@@ -56,6 +113,75 @@ spec:
|
|
|
56
113
|
- .github/workflows/**
|
|
57
114
|
- .ai-sdlc/**
|
|
58
115
|
`;
|
|
116
|
+
const AGENT_ROLE_YAML_RESEARCH = `apiVersion: ai-sdlc.io/v1alpha1
|
|
117
|
+
kind: AgentRole
|
|
118
|
+
metadata:
|
|
119
|
+
name: default-agent
|
|
120
|
+
spec:
|
|
121
|
+
role: research-developer
|
|
122
|
+
goal: Investigate, gather references, and implement issue requirements with tests
|
|
123
|
+
# Tier: research-agent.
|
|
124
|
+
# Coding-tier surface plus read-only web access. Use for tasks that need
|
|
125
|
+
# to consult external docs, package registries, or RFCs.
|
|
126
|
+
tools:
|
|
127
|
+
- Edit # write to existing files
|
|
128
|
+
- Write # create new files
|
|
129
|
+
- Read # read source / configs
|
|
130
|
+
- Glob # discover files by pattern
|
|
131
|
+
- Grep # search code
|
|
132
|
+
- Bash # run tests, lint, build, git
|
|
133
|
+
- NotebookEdit # edit Jupyter notebooks
|
|
134
|
+
- WebFetch # fetch a known URL (docs, RFCs, API references)
|
|
135
|
+
- WebSearch # discover sources by query
|
|
136
|
+
# Excluded (use --role meta to opt in):
|
|
137
|
+
# - Task — spawns sub-agents (meta tier)
|
|
138
|
+
# - Skill — loads external skill packs (meta tier)
|
|
139
|
+
constraints:
|
|
140
|
+
maxFilesPerChange: 15
|
|
141
|
+
requireTests: true
|
|
142
|
+
blockedPaths:
|
|
143
|
+
- .github/workflows/**
|
|
144
|
+
- .ai-sdlc/**
|
|
145
|
+
`;
|
|
146
|
+
const AGENT_ROLE_YAML_META = `apiVersion: ai-sdlc.io/v1alpha1
|
|
147
|
+
kind: AgentRole
|
|
148
|
+
metadata:
|
|
149
|
+
name: default-agent
|
|
150
|
+
spec:
|
|
151
|
+
role: meta-developer
|
|
152
|
+
goal: Orchestrate sub-agents, load skill packs, and implement complex multi-step work
|
|
153
|
+
# Tier: meta-agent.
|
|
154
|
+
# Full surface: filesystem + shell + web + sub-agent + skill loading.
|
|
155
|
+
# Use only when the task legitimately needs sub-agent fan-out or external
|
|
156
|
+
# skill packs. Higher cost amplification and broader attack surface.
|
|
157
|
+
tools:
|
|
158
|
+
- Edit # write to existing files
|
|
159
|
+
- Write # create new files
|
|
160
|
+
- Read # read source / configs
|
|
161
|
+
- Glob # discover files by pattern
|
|
162
|
+
- Grep # search code
|
|
163
|
+
- Bash # run tests, lint, build, git
|
|
164
|
+
- NotebookEdit # edit Jupyter notebooks
|
|
165
|
+
- WebFetch # fetch a known URL
|
|
166
|
+
- WebSearch # discover sources by query
|
|
167
|
+
- Task # spawn sub-agents for parallel / specialized work
|
|
168
|
+
- Skill # load external skill packs (declared capabilities)
|
|
169
|
+
constraints:
|
|
170
|
+
maxFilesPerChange: 15
|
|
171
|
+
requireTests: true
|
|
172
|
+
blockedPaths:
|
|
173
|
+
- .github/workflows/**
|
|
174
|
+
- .ai-sdlc/**
|
|
175
|
+
`;
|
|
176
|
+
const AGENT_ROLE_YAML_BY_TIER = {
|
|
177
|
+
coding: AGENT_ROLE_YAML_CODING,
|
|
178
|
+
research: AGENT_ROLE_YAML_RESEARCH,
|
|
179
|
+
meta: AGENT_ROLE_YAML_META,
|
|
180
|
+
};
|
|
181
|
+
/** Resolve the agent-role.yaml template for a given tier. Default = `coding`. */
|
|
182
|
+
export function getAgentRoleYaml(tier = 'coding') {
|
|
183
|
+
return AGENT_ROLE_YAML_BY_TIER[tier];
|
|
184
|
+
}
|
|
59
185
|
const QUALITY_GATE_YAML = `apiVersion: ai-sdlc.io/v1alpha1
|
|
60
186
|
kind: QualityGate
|
|
61
187
|
metadata:
|
|
@@ -139,24 +265,26 @@ const GITIGNORE_PATHS = ['.ai-sdlc/state.db', '.ai-sdlc/state/', '.ai-sdlc/audit
|
|
|
139
265
|
function ensureGitignore(projectDir, dryRun, prefix = '') {
|
|
140
266
|
const gitignorePath = join(projectDir, '.gitignore');
|
|
141
267
|
const existing = existsSync(gitignorePath) ? readFileSync(gitignorePath, 'utf-8') : '';
|
|
142
|
-
|
|
143
|
-
|
|
268
|
+
const SENTINEL = '# ai-sdlc:runtime-gitignore';
|
|
269
|
+
if (existing.includes(SENTINEL))
|
|
270
|
+
return;
|
|
271
|
+
const missing = GITIGNORE_PATHS.filter((entry) => !existing.split('\n').some((line) => line.trim() === entry));
|
|
144
272
|
if (missing.length === 0)
|
|
145
273
|
return;
|
|
146
274
|
if (dryRun) {
|
|
147
275
|
console.log(`${prefix} Would update .gitignore`);
|
|
148
276
|
return;
|
|
149
277
|
}
|
|
150
|
-
const block = '\n
|
|
278
|
+
const block = (existing.length > 0 ? '\n' : '') + `${SENTINEL}\n` + missing.join('\n') + '\n';
|
|
151
279
|
appendFileSync(gitignorePath, block, 'utf-8');
|
|
152
280
|
console.log(`${prefix} updated .gitignore`);
|
|
153
281
|
}
|
|
154
282
|
/** Initialize a single project directory with AI-SDLC config files. */
|
|
155
|
-
function initProject(projectDir, configDirName, dryRun, prefix = '') {
|
|
283
|
+
function initProject(projectDir, configDirName, dryRun, prefix = '', options = { pipelineYaml: PIPELINE_YAML, tier: 'coding' }) {
|
|
156
284
|
const configDir = join(projectDir, configDirName);
|
|
157
285
|
const files = [
|
|
158
|
-
{ name: 'pipeline.yaml', content:
|
|
159
|
-
{ name: 'agent-role.yaml', content:
|
|
286
|
+
{ name: 'pipeline.yaml', content: options.pipelineYaml },
|
|
287
|
+
{ name: 'agent-role.yaml', content: getAgentRoleYaml(options.tier) },
|
|
160
288
|
{ name: 'quality-gate.yaml', content: QUALITY_GATE_YAML },
|
|
161
289
|
{ name: 'autonomy-policy.yaml', content: AUTONOMY_POLICY_YAML },
|
|
162
290
|
];
|
|
@@ -205,15 +333,120 @@ function initWorkspaceRoot(workspacePath, repos, configDirName, dryRun) {
|
|
|
205
333
|
console.log(` created workspace.yaml`);
|
|
206
334
|
}
|
|
207
335
|
}
|
|
336
|
+
/**
|
|
337
|
+
* Build the WizardFlags struct from Commander's parsed options. Pulled
|
|
338
|
+
* out into a function so tests can drive the wizard pipeline directly
|
|
339
|
+
* without going through Commander's stateful option store.
|
|
340
|
+
*/
|
|
341
|
+
export function buildWizardFlags(opts) {
|
|
342
|
+
const addRaw = typeof opts.add === 'string' ? opts.add : undefined;
|
|
343
|
+
let add;
|
|
344
|
+
if (addRaw === 'dor' ||
|
|
345
|
+
addRaw === 'attestation' ||
|
|
346
|
+
addRaw === 'classifier' ||
|
|
347
|
+
addRaw === 'branch-protection') {
|
|
348
|
+
add = addRaw;
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
yes: !!opts.yes,
|
|
352
|
+
withDor: !!opts.withDor,
|
|
353
|
+
withAttestation: !!opts.withAttestation,
|
|
354
|
+
withClassifier: !!opts.withClassifier,
|
|
355
|
+
withBranchProtection: !!opts.withBranchProtection,
|
|
356
|
+
add,
|
|
357
|
+
dryRun: !!opts.dryRun,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
/** Validate a `--add` arg and return either the normalized value or null+error. */
|
|
361
|
+
function validateAddArg(addRaw) {
|
|
362
|
+
if (addRaw === undefined || addRaw === null || addRaw === false)
|
|
363
|
+
return { ok: true };
|
|
364
|
+
if (typeof addRaw !== 'string')
|
|
365
|
+
return { ok: false, error: `--add must be a string` };
|
|
366
|
+
const allowed = ['dor', 'attestation', 'classifier', 'branch-protection'];
|
|
367
|
+
if (!allowed.includes(addRaw)) {
|
|
368
|
+
return {
|
|
369
|
+
ok: false,
|
|
370
|
+
error: `--add: unknown feature '${addRaw}'. Expected one of: ${allowed.join(', ')}.`,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
return { ok: true, value: addRaw };
|
|
374
|
+
}
|
|
208
375
|
export const initCommand = new Command('init')
|
|
209
376
|
.description('Initialize AI-SDLC configuration in the current project')
|
|
210
377
|
.option('--dry-run', 'Show what would be created without writing files')
|
|
211
378
|
.option('--skip-mcp', 'Skip MCP server auto-configuration')
|
|
379
|
+
.option('--cursor', 'Force-install Cursor MCP config even if Cursor is not detected')
|
|
212
380
|
.option('-d, --dir <path>', 'Config directory name', '.ai-sdlc')
|
|
381
|
+
.option('--role <tier>', `Agent-role tool tier: ${AGENT_ROLE_TIERS.join(' | ')} (default: coding)`, 'coding')
|
|
382
|
+
// ── AISDLC-143 wizard flags ─────────────────────────────────────────
|
|
383
|
+
.option('-y, --yes', 'Accept all defaults (non-interactive; CI/scripts)')
|
|
384
|
+
.option('--with-dor', 'Scaffold Definition-of-Ready gate config + workflow')
|
|
385
|
+
.option('--with-attestation', 'Scaffold attestation infrastructure (audit-only)')
|
|
386
|
+
.option('--with-classifier', 'Scaffold review classifier config stub')
|
|
387
|
+
.option('--with-branch-protection', 'Apply recommended branch-protection rule to main (requires gh)')
|
|
388
|
+
.option('--add <feature>', 'Extend an already-initialized repo with a single feature: dor | attestation | classifier | branch-protection')
|
|
213
389
|
.action(async (opts) => {
|
|
214
390
|
const projectDir = process.cwd();
|
|
215
391
|
const configDirName = opts.dir ?? '.ai-sdlc';
|
|
216
392
|
const dryRun = !!opts.dryRun;
|
|
393
|
+
const tierInput = (opts.role ?? 'coding');
|
|
394
|
+
if (!AGENT_ROLE_TIERS.includes(tierInput)) {
|
|
395
|
+
console.error(`Error: invalid --role value '${tierInput}'. Expected one of: ${AGENT_ROLE_TIERS.join(', ')}.`);
|
|
396
|
+
process.exitCode = 1;
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
const tier = tierInput;
|
|
400
|
+
const cursorOptIn = !!opts.cursor;
|
|
401
|
+
// ── Validate --add early so we error before doing any work. ────────
|
|
402
|
+
const addCheck = validateAddArg(opts.add);
|
|
403
|
+
if (!addCheck.ok) {
|
|
404
|
+
console.error(`Error: ${addCheck.error}`);
|
|
405
|
+
process.exitCode = 1;
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
const flags = buildWizardFlags(opts);
|
|
409
|
+
// ── --add path: extend an already-initialized repo ────────────────
|
|
410
|
+
// AC #7: skip the "always-scaffold-baseline" path entirely; the
|
|
411
|
+
// wizard dispatcher's `--add` branch knows to write only the chosen
|
|
412
|
+
// feature's templates (no pipeline.yaml, no MCP setup, no workspace
|
|
413
|
+
// detection). This is the safe re-run path on a repo that already
|
|
414
|
+
// ran `ai-sdlc init` once.
|
|
415
|
+
if (flags.add) {
|
|
416
|
+
const adapters = buildProductionAdapters();
|
|
417
|
+
const selection = await resolveFeatureSelection(flags, adapters);
|
|
418
|
+
console.log(`Extending AI-SDLC config with --add ${flags.add}:`);
|
|
419
|
+
console.log('');
|
|
420
|
+
const result = await applyFeatureSelection(projectDir, selection, flags, adapters);
|
|
421
|
+
renderNextSteps(selection, result, adapters);
|
|
422
|
+
// Reviewer feedback (round 2, suggestion #5): when the operator
|
|
423
|
+
// explicitly requested branch-protection via a non-interactive flag
|
|
424
|
+
// (--add branch-protection here; --yes / --with-branch-protection
|
|
425
|
+
// in runWizardStage) and the apply failed (gh missing, not
|
|
426
|
+
// authenticated, etc.), surface a non-zero exit so CI scripts can
|
|
427
|
+
// detect failure instead of seeing the silent log line.
|
|
428
|
+
if (flags.add === 'branch-protection' &&
|
|
429
|
+
result.branchProtection &&
|
|
430
|
+
!result.branchProtection.applied &&
|
|
431
|
+
result.branchProtection.error) {
|
|
432
|
+
process.exitCode = 1;
|
|
433
|
+
}
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
// ── version provenance (AC #1) ────────────────────────────────────
|
|
437
|
+
const versions = resolveVersions({ workDir: projectDir });
|
|
438
|
+
console.log(formatVersionBlock(versions));
|
|
439
|
+
console.log('');
|
|
440
|
+
// ── pipeline.yaml org/repo substitution (AC #2) ───────────────────
|
|
441
|
+
const remote = detectGitRemote({ cwd: projectDir });
|
|
442
|
+
const pipelineYaml = applyRemoteToPipelineYaml(PIPELINE_YAML, remote);
|
|
443
|
+
if (remote.detected) {
|
|
444
|
+
console.log(`Detected git remote: ${remote.org}/${remote.repo}`);
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
console.log(`No git origin remote detected — pipeline.yaml will use the 'your-org' placeholder.`);
|
|
448
|
+
}
|
|
449
|
+
console.log('');
|
|
217
450
|
// Detect workspace (multi-repo parent directory)
|
|
218
451
|
const workspace = detectWorkspace(projectDir);
|
|
219
452
|
if (workspace.isWorkspace) {
|
|
@@ -227,37 +460,103 @@ export const initCommand = new Command('init')
|
|
|
227
460
|
// Cascade into each child repo
|
|
228
461
|
for (const repo of workspace.repos) {
|
|
229
462
|
console.log(`\n${repo.name}/`);
|
|
230
|
-
|
|
463
|
+
// Per-repo remote detection so each child gets its own org/repo.
|
|
464
|
+
const childRemote = detectGitRemote({ cwd: repo.absPath });
|
|
465
|
+
const childPipeline = applyRemoteToPipelineYaml(PIPELINE_YAML, childRemote);
|
|
466
|
+
if (childRemote.detected) {
|
|
467
|
+
console.log(` detected remote: ${childRemote.org}/${childRemote.repo}`);
|
|
468
|
+
}
|
|
469
|
+
initProject(repo.absPath, configDirName, dryRun, ' ', {
|
|
470
|
+
pipelineYaml: childPipeline,
|
|
471
|
+
tier,
|
|
472
|
+
});
|
|
231
473
|
}
|
|
232
474
|
// MCP setup at workspace root (serves all repos)
|
|
233
475
|
if (!opts.skipMcp) {
|
|
234
|
-
const
|
|
476
|
+
const { detected, skipped } = detectAgentsDetailed(projectDir, {
|
|
477
|
+
isWorkspace: true,
|
|
478
|
+
pinVersion: versions.orchestrator,
|
|
479
|
+
cursorOptIn,
|
|
480
|
+
});
|
|
235
481
|
console.log(`\nMCP server setup (workspace root):`);
|
|
236
|
-
console.log(` detected ${
|
|
237
|
-
for (const
|
|
482
|
+
console.log(` detected ${detected.map((a) => a.name).join(', ') || '(none)'}`);
|
|
483
|
+
for (const sk of skipped) {
|
|
484
|
+
console.log(` skip ${sk.name} — ${sk.reason}`);
|
|
485
|
+
}
|
|
486
|
+
for (const agent of detected) {
|
|
238
487
|
const status = installMcpServer(projectDir, agent, dryRun);
|
|
239
488
|
const pad = status === 'merged' ? ' ' : '';
|
|
240
489
|
console.log(` ${status}${pad} ${agent.configPath} (${agent.name})`);
|
|
241
490
|
}
|
|
242
491
|
}
|
|
243
492
|
console.log(`\nAI-SDLC workspace initialized in ${projectDir}/`);
|
|
493
|
+
// ── AISDLC-143 wizard (workspace root) ────────────────────────
|
|
494
|
+
// The wizard runs ONCE at the workspace root: the baseline gate
|
|
495
|
+
// workflow + per-feature workflows live at `<workspace-root>/.github/`,
|
|
496
|
+
// not in each child repo. Children share the same CI from the
|
|
497
|
+
// root since GHA workflows always live at the repo root anyway.
|
|
498
|
+
await runWizardStage(projectDir, flags);
|
|
244
499
|
console.log(`Run 'ai-sdlc health' to verify your configuration.`);
|
|
245
500
|
}
|
|
246
501
|
else {
|
|
247
502
|
// Single-repo mode (original behavior)
|
|
248
|
-
initProject(projectDir, configDirName, dryRun);
|
|
503
|
+
initProject(projectDir, configDirName, dryRun, '', { pipelineYaml, tier });
|
|
249
504
|
if (!opts.skipMcp) {
|
|
250
|
-
const
|
|
505
|
+
const { detected, skipped } = detectAgentsDetailed(projectDir, {
|
|
506
|
+
pinVersion: versions.orchestrator,
|
|
507
|
+
cursorOptIn,
|
|
508
|
+
});
|
|
251
509
|
console.log(`\nMCP server setup:`);
|
|
252
|
-
console.log(` detected ${
|
|
253
|
-
for (const
|
|
510
|
+
console.log(` detected ${detected.map((a) => a.name).join(', ') || '(none)'}`);
|
|
511
|
+
for (const sk of skipped) {
|
|
512
|
+
console.log(` skip ${sk.name} — ${sk.reason}`);
|
|
513
|
+
}
|
|
514
|
+
for (const agent of detected) {
|
|
254
515
|
const status = installMcpServer(projectDir, agent, dryRun);
|
|
255
516
|
const pad = status === 'merged' ? ' ' : '';
|
|
256
517
|
console.log(` ${status}${pad} ${agent.configPath} (${agent.name})`);
|
|
257
518
|
}
|
|
258
519
|
}
|
|
259
520
|
console.log(`\nAI-SDLC config initialized in ${join(projectDir, configDirName)}/`);
|
|
521
|
+
// ── AISDLC-143 wizard (single-repo) ──────────────────────────
|
|
522
|
+
await runWizardStage(projectDir, flags);
|
|
260
523
|
console.log(`Run 'ai-sdlc health' to verify your configuration.`);
|
|
261
524
|
}
|
|
262
525
|
});
|
|
526
|
+
/**
|
|
527
|
+
* Run the AISDLC-143 wizard stage: prompt the user (or short-circuit on
|
|
528
|
+
* --yes / --with-X), apply the chosen feature templates, append the
|
|
529
|
+
* CLAUDE.md pointer, and render the "next steps" summary.
|
|
530
|
+
*
|
|
531
|
+
* Pulled out of the inline action body so both the single-repo and
|
|
532
|
+
* workspace-root branches share the same wiring. Adapters are built
|
|
533
|
+
* once here (production = real disk writes; tests inject stubs by
|
|
534
|
+
* calling `applyFeatureSelection`/`renderNextSteps` directly).
|
|
535
|
+
*/
|
|
536
|
+
async function runWizardStage(projectDir, flags) {
|
|
537
|
+
const adapters = buildProductionAdapters();
|
|
538
|
+
console.log('');
|
|
539
|
+
console.log('━━━ Feature wizard ━━━');
|
|
540
|
+
console.log('');
|
|
541
|
+
const selection = await resolveFeatureSelection(flags, adapters);
|
|
542
|
+
console.log('');
|
|
543
|
+
console.log('Scaffolding selected features:');
|
|
544
|
+
const result = await applyFeatureSelection(projectDir, selection, flags, adapters);
|
|
545
|
+
ensureClaudeMdPointer(projectDir, adapters, flags.dryRun);
|
|
546
|
+
renderNextSteps(selection, result, adapters);
|
|
547
|
+
// Reviewer feedback (round 2, suggestion #5): when branch-protection
|
|
548
|
+
// was non-interactively requested (--with-branch-protection or --yes,
|
|
549
|
+
// both used by CI scripts) and the apply failed (gh missing, not
|
|
550
|
+
// authenticated, etc.), surface a non-zero exit so CI can detect
|
|
551
|
+
// failure instead of seeing the silent log line. Interactive prompt
|
|
552
|
+
// answers don't trip this — the human already saw the error and can
|
|
553
|
+
// re-run `ai-sdlc init --add branch-protection` themselves.
|
|
554
|
+
const branchProtectionRequestedNonInteractively = flags.yes || flags.withBranchProtection;
|
|
555
|
+
if (branchProtectionRequestedNonInteractively &&
|
|
556
|
+
result.branchProtection &&
|
|
557
|
+
!result.branchProtection.applied &&
|
|
558
|
+
result.branchProtection.error) {
|
|
559
|
+
process.exitCode = 1;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
263
562
|
//# sourceMappingURL=init.js.map
|
|
@@ -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
|
|
@@ -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
|
-
|
|
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',
|
|
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',
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) +
|