@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
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ai-sdlc init` interactive wizard + feature dispatcher (AISDLC-143).
|
|
3
|
+
*
|
|
4
|
+
* Per Q4(b) of the operator-ratified quality-gate redesign, `ai-sdlc init`
|
|
5
|
+
* is a wizard by default with `--yes` for non-interactive (CI/scripts) and
|
|
6
|
+
* `--with-X` flags for explicit opt-in (`--with-dor`, `--with-attestation`,
|
|
7
|
+
* `--with-classifier`, `--with-branch-protection`). This module owns:
|
|
8
|
+
*
|
|
9
|
+
* 1. The ordered prompt list (resolveFeatureSelection).
|
|
10
|
+
* 2. The feature-toggle → file-write dispatcher (applyFeatureSelection).
|
|
11
|
+
* 3. The branch-protection helper (applyBranchProtection) including the
|
|
12
|
+
* `--dry-run` JSON-print path required by AC #6.
|
|
13
|
+
* 4. The "next steps" summary printed at the end of init (AC #5).
|
|
14
|
+
*
|
|
15
|
+
* Test surface: every public function takes a small options bag with
|
|
16
|
+
* injectable side-effect adapters (prompter, writeFile, runCommand) so the
|
|
17
|
+
* test suite can drive every wizard branch hermetically without spinning
|
|
18
|
+
* up a TTY or shelling out to `gh`. Production callers in `init.ts` pass
|
|
19
|
+
* the real adapters.
|
|
20
|
+
*/
|
|
21
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
22
|
+
import { join, dirname } from 'node:path';
|
|
23
|
+
import { execFileSync } from 'node:child_process';
|
|
24
|
+
import { ATTESTATION_TEMPLATES, BASELINE_WORKFLOW_TEMPLATES, CLASSIFIER_TEMPLATES, DOR_TEMPLATES, HUSKY_PREPUSH_SIGN_SNIPPET, } from './init-templates.js';
|
|
25
|
+
/** All feature flags off — used as the initial state before flags + prompts. */
|
|
26
|
+
export const NO_FEATURES = {
|
|
27
|
+
dor: false,
|
|
28
|
+
attestation: false,
|
|
29
|
+
classifier: false,
|
|
30
|
+
branchProtection: false,
|
|
31
|
+
};
|
|
32
|
+
/** All features on — the answer used by `--yes` (accept all defaults). */
|
|
33
|
+
export const ALL_FEATURES = {
|
|
34
|
+
dor: true,
|
|
35
|
+
attestation: true,
|
|
36
|
+
classifier: true,
|
|
37
|
+
branchProtection: true,
|
|
38
|
+
};
|
|
39
|
+
// ── Production adapter factory ───────────────────────────────────────────
|
|
40
|
+
/**
|
|
41
|
+
* Build the production adapter bag. Pulled into a factory so tests can
|
|
42
|
+
* compose a partial override bag (e.g. only override `prompt`) and let
|
|
43
|
+
* the rest fall through to real disk writes.
|
|
44
|
+
*
|
|
45
|
+
* The `prompt` adapter is a lazy import of `@inquirer/prompts.confirm`
|
|
46
|
+
* so that:
|
|
47
|
+
* 1. Tests don't pay the import cost when they inject their own stub.
|
|
48
|
+
* 2. `--yes` runs (which never call `prompt`) don't pay it either.
|
|
49
|
+
* 3. The orchestrator's runtime `dist/` is smaller for the common case.
|
|
50
|
+
*/
|
|
51
|
+
export function buildProductionAdapters() {
|
|
52
|
+
return {
|
|
53
|
+
prompt: async (question, defaultYes) => {
|
|
54
|
+
// Lazy import — see docblock above for why.
|
|
55
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
56
|
+
return confirm({ message: question, default: defaultYes });
|
|
57
|
+
},
|
|
58
|
+
writeFile: (path, contents) => writeFileSync(path, contents, 'utf-8'),
|
|
59
|
+
appendOnce: (path, contents, sentinel) => {
|
|
60
|
+
// Make sure the parent dir exists (appendFileSync errors with ENOENT
|
|
61
|
+
// otherwise; we may be writing into a freshly-created `.husky/`).
|
|
62
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
63
|
+
const existing = existsSync(path) ? readFileSync(path, 'utf-8') : '';
|
|
64
|
+
if (existing.includes(sentinel))
|
|
65
|
+
return 'skipped';
|
|
66
|
+
const sep = existing.length > 0 && !existing.endsWith('\n') ? '\n' : '';
|
|
67
|
+
writeFileSync(path, existing + sep + contents, 'utf-8');
|
|
68
|
+
return 'appended';
|
|
69
|
+
},
|
|
70
|
+
mkdirp: (path) => mkdirSync(path, { recursive: true }),
|
|
71
|
+
exists: (path) => existsSync(path),
|
|
72
|
+
runCommand: (cmd, args) => {
|
|
73
|
+
try {
|
|
74
|
+
// Use `execFileSync` (no shell) so args are passed as a true
|
|
75
|
+
// argv array — never word-split or shell-interpreted. The prior
|
|
76
|
+
// `execSync(\`${cmd} ${args.join(' ')}\`)` form ran the command
|
|
77
|
+
// through `/bin/sh -c` and silently broke whenever any argument
|
|
78
|
+
// contained whitespace (e.g. macOS users with `~/Documents/My
|
|
79
|
+
// Project/` in their projectDir, where the `--input <tmpPath>`
|
|
80
|
+
// arg to `gh api` would word-split and `gh` would see two
|
|
81
|
+
// unrelated tokens — branch protection would silently fail or
|
|
82
|
+
// apply wrong content). Switching to `execFileSync` eliminates
|
|
83
|
+
// both word-splitting AND any shell-injection surface in one
|
|
84
|
+
// change. Stderr stays muted so users still get the clean
|
|
85
|
+
// single-line error rendered by `applyBranchProtection`.
|
|
86
|
+
const stdout = execFileSync(cmd, args, {
|
|
87
|
+
encoding: 'utf-8',
|
|
88
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
89
|
+
});
|
|
90
|
+
return { stdout, exitCode: 0 };
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
const e = err;
|
|
94
|
+
return {
|
|
95
|
+
stdout: typeof e.stdout === 'string' ? e.stdout : (e.stdout?.toString() ?? ''),
|
|
96
|
+
exitCode: e.status ?? 1,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
log: (line) => console.log(line),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// ── Wizard ───────────────────────────────────────────────────────────────
|
|
104
|
+
/**
|
|
105
|
+
* Resolve the per-feature on/off vector by combining (in priority order):
|
|
106
|
+
* 1. `--add <feature>` — if set, ONLY that feature is on; everything
|
|
107
|
+
* else is suppressed (idempotent extension, AC #7).
|
|
108
|
+
* 2. `--yes` — accept every default (every feature on).
|
|
109
|
+
* 3. `--with-X` flags — opt-in without prompting.
|
|
110
|
+
* 4. Interactive prompts for any feature still undetermined.
|
|
111
|
+
*
|
|
112
|
+
* Returns a fully-determined FeatureSelection. The dispatcher then writes
|
|
113
|
+
* exactly the union of features marked true.
|
|
114
|
+
*/
|
|
115
|
+
export async function resolveFeatureSelection(flags, adapters) {
|
|
116
|
+
// ── Path 1: `--add` — single-feature extension ────────────────────────
|
|
117
|
+
if (flags.add) {
|
|
118
|
+
const sel = { ...NO_FEATURES };
|
|
119
|
+
switch (flags.add) {
|
|
120
|
+
case 'dor':
|
|
121
|
+
sel.dor = true;
|
|
122
|
+
break;
|
|
123
|
+
case 'attestation':
|
|
124
|
+
sel.attestation = true;
|
|
125
|
+
break;
|
|
126
|
+
case 'classifier':
|
|
127
|
+
sel.classifier = true;
|
|
128
|
+
break;
|
|
129
|
+
case 'branch-protection':
|
|
130
|
+
sel.branchProtection = true;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
return sel;
|
|
134
|
+
}
|
|
135
|
+
// ── Path 2: `--yes` — all defaults on, no prompts ─────────────────────
|
|
136
|
+
if (flags.yes) {
|
|
137
|
+
return { ...ALL_FEATURES };
|
|
138
|
+
}
|
|
139
|
+
// ── Path 3+4: `--with-X` overrides + prompts for the rest ─────────────
|
|
140
|
+
const sel = { ...NO_FEATURES };
|
|
141
|
+
// DoR
|
|
142
|
+
if (flags.withDor) {
|
|
143
|
+
sel.dor = true;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
sel.dor = await adapters.prompt('Will this repo use Definition-of-Ready gates?', true);
|
|
147
|
+
}
|
|
148
|
+
// Attestation
|
|
149
|
+
if (flags.withAttestation) {
|
|
150
|
+
sel.attestation = true;
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
sel.attestation = await adapters.prompt('Do you want attestation infrastructure (audit-only)?', true);
|
|
154
|
+
}
|
|
155
|
+
// Classifier
|
|
156
|
+
if (flags.withClassifier) {
|
|
157
|
+
sel.classifier = true;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
sel.classifier = await adapters.prompt('Add review classifier for cost-optimized reviews?', true);
|
|
161
|
+
}
|
|
162
|
+
// Branch protection
|
|
163
|
+
if (flags.withBranchProtection) {
|
|
164
|
+
sel.branchProtection = true;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
sel.branchProtection = await adapters.prompt('Apply recommended branch protection? (required: ai-sdlc/pr-ready + codecov/patch)', true);
|
|
168
|
+
}
|
|
169
|
+
return sel;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Write the union of feature templates into the project dir. AC #4 says
|
|
173
|
+
* the BASELINE workflow templates (gate workflow) are always written; the
|
|
174
|
+
* per-feature template sets are written only when their toggle is on.
|
|
175
|
+
*
|
|
176
|
+
* Idempotent: any file that already exists at the target path is skipped
|
|
177
|
+
* with a "skip" log line. This is what makes `--add <feature>` safe to
|
|
178
|
+
* run on an already-initialized repo (AC #7).
|
|
179
|
+
*/
|
|
180
|
+
export async function applyFeatureSelection(projectDir, selection, flags, adapters) {
|
|
181
|
+
const result = { created: [], skipped: [], wouldCreate: [] };
|
|
182
|
+
// Build the union of templates to write.
|
|
183
|
+
const templateSets = [];
|
|
184
|
+
// `--add` mode: skip the baseline (we're EXTENDING an existing init).
|
|
185
|
+
if (!flags.add) {
|
|
186
|
+
templateSets.push(BASELINE_WORKFLOW_TEMPLATES);
|
|
187
|
+
}
|
|
188
|
+
if (selection.dor)
|
|
189
|
+
templateSets.push(DOR_TEMPLATES);
|
|
190
|
+
if (selection.attestation)
|
|
191
|
+
templateSets.push(ATTESTATION_TEMPLATES);
|
|
192
|
+
if (selection.classifier)
|
|
193
|
+
templateSets.push(CLASSIFIER_TEMPLATES);
|
|
194
|
+
for (const set of templateSets) {
|
|
195
|
+
for (const [relPath, contents] of Object.entries(set.files)) {
|
|
196
|
+
const absPath = join(projectDir, relPath);
|
|
197
|
+
if (flags.dryRun) {
|
|
198
|
+
result.wouldCreate.push(relPath);
|
|
199
|
+
adapters.log(` would create ${relPath}`);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (adapters.exists(absPath)) {
|
|
203
|
+
result.skipped.push(relPath);
|
|
204
|
+
adapters.log(` skip ${relPath} (already exists)`);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
// mkdir -p the parent
|
|
208
|
+
adapters.mkdirp(dirname(absPath));
|
|
209
|
+
adapters.writeFile(absPath, contents);
|
|
210
|
+
result.created.push(relPath);
|
|
211
|
+
adapters.log(` created ${relPath}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// Husky pre-push sign hook is a separate concern from the
|
|
215
|
+
// FeatureTemplateSet because it's an APPEND (not a write-from-empty)
|
|
216
|
+
// — adopters often already have a .husky/pre-push from their existing
|
|
217
|
+
// tooling and we don't want to clobber it. Only fired when attestation
|
|
218
|
+
// is on.
|
|
219
|
+
if (selection.attestation && !flags.dryRun) {
|
|
220
|
+
const hookPath = join(projectDir, '.husky', 'pre-push');
|
|
221
|
+
if (!adapters.exists(hookPath)) {
|
|
222
|
+
// No existing hook — write a minimal one with the sign block.
|
|
223
|
+
adapters.mkdirp(dirname(hookPath));
|
|
224
|
+
adapters.writeFile(hookPath, `#!/usr/bin/env bash\nset -euo pipefail\n\n${HUSKY_PREPUSH_SIGN_SNIPPET}`);
|
|
225
|
+
result.created.push('.husky/pre-push');
|
|
226
|
+
adapters.log(` created .husky/pre-push`);
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
const status = adapters.appendOnce(hookPath, HUSKY_PREPUSH_SIGN_SNIPPET, '# ai-sdlc:attestation-sign-block');
|
|
230
|
+
if (status === 'appended') {
|
|
231
|
+
adapters.log(` updated .husky/pre-push (appended sign block)`);
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
result.skipped.push('.husky/pre-push');
|
|
235
|
+
adapters.log(` skip .husky/pre-push (sign block already present)`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
else if (selection.attestation && flags.dryRun) {
|
|
240
|
+
result.wouldCreate.push('.husky/pre-push');
|
|
241
|
+
adapters.log(' would update .husky/pre-push (sign block)');
|
|
242
|
+
}
|
|
243
|
+
// Branch protection (always last — depends on the gate workflow being
|
|
244
|
+
// present so the required check exists when the rule is applied).
|
|
245
|
+
if (selection.branchProtection) {
|
|
246
|
+
result.branchProtection = await applyBranchProtection(projectDir, flags, adapters);
|
|
247
|
+
}
|
|
248
|
+
return result;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Recommended branch-protection ruleset for AI-SDLC adopters. AC #1 #4:
|
|
252
|
+
* the required checks are `ai-sdlc/pr-ready` (the gate aggregator) and
|
|
253
|
+
* `codecov/patch` (the de facto coverage signal). Other AI-SDLC apps
|
|
254
|
+
* post their own statuses but they're all rolled into pr-ready.
|
|
255
|
+
*
|
|
256
|
+
* The body conforms to the GitHub REST API
|
|
257
|
+
* `PUT /repos/{owner}/{repo}/branches/{branch}/protection` schema.
|
|
258
|
+
*/
|
|
259
|
+
export const RECOMMENDED_BRANCH_PROTECTION_BODY = {
|
|
260
|
+
required_status_checks: {
|
|
261
|
+
strict: true,
|
|
262
|
+
contexts: ['ai-sdlc/pr-ready', 'codecov/patch'],
|
|
263
|
+
},
|
|
264
|
+
enforce_admins: false,
|
|
265
|
+
required_pull_request_reviews: {
|
|
266
|
+
dismiss_stale_reviews: true,
|
|
267
|
+
require_code_owner_reviews: false,
|
|
268
|
+
required_approving_review_count: 1,
|
|
269
|
+
},
|
|
270
|
+
restrictions: null,
|
|
271
|
+
allow_force_pushes: false,
|
|
272
|
+
allow_deletions: false,
|
|
273
|
+
};
|
|
274
|
+
/**
|
|
275
|
+
* Apply (or print, in dry-run) the recommended branch protection rule
|
|
276
|
+
* to the `main` branch of the repo at `projectDir`. AC #6 explicitly
|
|
277
|
+
* requires that `--dry-run` print the JSON without applying.
|
|
278
|
+
*
|
|
279
|
+
* The repo identity (`owner/repo`) is resolved by shelling out to
|
|
280
|
+
* `gh repo view --json nameWithOwner -q .nameWithOwner`. We could parse
|
|
281
|
+
* the git remote ourselves (see git-remote.ts) but `gh` already resolves
|
|
282
|
+
* forks + renames + custom default branches consistently, and the user
|
|
283
|
+
* needs `gh` on PATH for the PUT to work anyway.
|
|
284
|
+
*/
|
|
285
|
+
export async function applyBranchProtection(projectDir, flags, adapters) {
|
|
286
|
+
const bodyJson = JSON.stringify(RECOMMENDED_BRANCH_PROTECTION_BODY, null, 2);
|
|
287
|
+
if (flags.dryRun) {
|
|
288
|
+
adapters.log('');
|
|
289
|
+
adapters.log('Branch-protection dry-run — would PUT the following body:');
|
|
290
|
+
adapters.log(bodyJson);
|
|
291
|
+
adapters.log('');
|
|
292
|
+
adapters.log(' endpoint: PUT /repos/{owner}/{repo}/branches/main/protection');
|
|
293
|
+
adapters.log(' apply with: gh api -X PUT repos/{owner}/{repo}/branches/main/protection ...');
|
|
294
|
+
return { applied: false, bodyJson };
|
|
295
|
+
}
|
|
296
|
+
// Resolve owner/repo via gh.
|
|
297
|
+
const ownerRepo = adapters.runCommand('gh', [
|
|
298
|
+
'repo',
|
|
299
|
+
'view',
|
|
300
|
+
'--json',
|
|
301
|
+
'nameWithOwner',
|
|
302
|
+
'-q',
|
|
303
|
+
'.nameWithOwner',
|
|
304
|
+
]);
|
|
305
|
+
if (ownerRepo.exitCode !== 0) {
|
|
306
|
+
return {
|
|
307
|
+
applied: false,
|
|
308
|
+
bodyJson,
|
|
309
|
+
error: `gh repo view failed: ${ownerRepo.stdout.trim() || 'unknown error'}`,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
const slug = ownerRepo.stdout.trim();
|
|
313
|
+
if (!slug) {
|
|
314
|
+
return {
|
|
315
|
+
applied: false,
|
|
316
|
+
bodyJson,
|
|
317
|
+
error: 'gh repo view returned empty owner/repo',
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
// Use the file-based input form so we don't have to thread quoted JSON
|
|
321
|
+
// through a shell. We write to a tmpfile, point gh at it, and let the
|
|
322
|
+
// adapter's runCommand spawn `gh` directly.
|
|
323
|
+
const tmpPath = join(projectDir, '.ai-sdlc', 'branch-protection-body.json');
|
|
324
|
+
// adapters.runCommand can't write files, so we use the underlying
|
|
325
|
+
// primitives directly here — branch protection is a one-shot operation
|
|
326
|
+
// and doesn't need to be hermetic in the same way as the file writes.
|
|
327
|
+
// Tests inject a stub that intercepts the runCommand call and never
|
|
328
|
+
// touches this path. See test for the contract.
|
|
329
|
+
try {
|
|
330
|
+
mkdirSync(dirname(tmpPath), { recursive: true });
|
|
331
|
+
writeFileSync(tmpPath, bodyJson, 'utf-8');
|
|
332
|
+
}
|
|
333
|
+
catch (err) {
|
|
334
|
+
return {
|
|
335
|
+
applied: false,
|
|
336
|
+
bodyJson,
|
|
337
|
+
error: `failed to stage branch-protection body: ${err.message}`,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
const apply = adapters.runCommand('gh', [
|
|
341
|
+
'api',
|
|
342
|
+
'-X',
|
|
343
|
+
'PUT',
|
|
344
|
+
`repos/${slug}/branches/main/protection`,
|
|
345
|
+
'--input',
|
|
346
|
+
tmpPath,
|
|
347
|
+
]);
|
|
348
|
+
if (apply.exitCode !== 0) {
|
|
349
|
+
return {
|
|
350
|
+
applied: false,
|
|
351
|
+
bodyJson,
|
|
352
|
+
error: `gh api PUT failed: ${apply.stdout.trim() || 'unknown error'}`,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
adapters.log(` applied branch protection to ${slug}:main`);
|
|
356
|
+
return { applied: true, bodyJson };
|
|
357
|
+
}
|
|
358
|
+
// ── Next-steps summary ───────────────────────────────────────────────────
|
|
359
|
+
/**
|
|
360
|
+
* Print the structured "next steps" summary at the end of init. AC #5:
|
|
361
|
+
* the summary must include operator action items conditional on which
|
|
362
|
+
* features were chosen (e.g. `gh secret set` commands when attestation
|
|
363
|
+
* was opted in).
|
|
364
|
+
*
|
|
365
|
+
* Returns the rendered summary as a string in addition to logging it,
|
|
366
|
+
* so tests can assert on it without re-stringifying console output.
|
|
367
|
+
*/
|
|
368
|
+
export function renderNextSteps(selection, result, adapters) {
|
|
369
|
+
const lines = [];
|
|
370
|
+
lines.push('');
|
|
371
|
+
lines.push('━━━ Next steps ━━━');
|
|
372
|
+
lines.push('');
|
|
373
|
+
// Always present (baseline gate)
|
|
374
|
+
lines.push('1. Commit the scaffolded files:');
|
|
375
|
+
lines.push(' git add .ai-sdlc .github/workflows package.json');
|
|
376
|
+
lines.push(' git commit -m "chore: bootstrap AI-SDLC config"');
|
|
377
|
+
lines.push('');
|
|
378
|
+
let stepN = 2;
|
|
379
|
+
if (selection.dor) {
|
|
380
|
+
lines.push(`${stepN}. DoR (Definition-of-Ready) is in WARN-ONLY mode by default.`);
|
|
381
|
+
lines.push(' Tune .ai-sdlc/dor-config.yaml then flip evaluationMode: enforce');
|
|
382
|
+
lines.push(' after a soak window confirms the false-positive rate is low.');
|
|
383
|
+
lines.push('');
|
|
384
|
+
stepN++;
|
|
385
|
+
}
|
|
386
|
+
if (selection.attestation) {
|
|
387
|
+
lines.push(`${stepN}. Attestation infrastructure was scaffolded in AUDIT-ONLY mode.`);
|
|
388
|
+
lines.push(' a) Bootstrap your signing key: /ai-sdlc init-signing-key');
|
|
389
|
+
lines.push(' b) Open a PR adding the printed YAML block to');
|
|
390
|
+
lines.push(' .ai-sdlc/trusted-reviewers.yaml');
|
|
391
|
+
// AISDLC-152: removed the optional CI-side signer step (the AISDLC-87
|
|
392
|
+
// CI-attestor was retired in AISDLC-140 sub-4 alongside attestation
|
|
393
|
+
// becoming audit-only). New adopters no longer need to provision the
|
|
394
|
+
// AI_SDLC_CI_ATTESTOR_PRIVATE_KEY secret.
|
|
395
|
+
lines.push('');
|
|
396
|
+
stepN++;
|
|
397
|
+
}
|
|
398
|
+
if (selection.classifier) {
|
|
399
|
+
lines.push(`${stepN}. Review classifier config was scaffolded.`);
|
|
400
|
+
lines.push(' The classifier RUNTIME ships in AISDLC-141 (follow-up). Until');
|
|
401
|
+
lines.push(' that lands, .ai-sdlc/review-classifier.yaml is advisory only.');
|
|
402
|
+
lines.push('');
|
|
403
|
+
stepN++;
|
|
404
|
+
}
|
|
405
|
+
if (selection.branchProtection) {
|
|
406
|
+
if (result.branchProtection?.applied) {
|
|
407
|
+
lines.push(`${stepN}. Branch protection on \`main\` was updated.`);
|
|
408
|
+
lines.push(' Required checks: ai-sdlc/pr-ready, codecov/patch');
|
|
409
|
+
}
|
|
410
|
+
else if (result.branchProtection?.error) {
|
|
411
|
+
lines.push(`${stepN}. Branch protection was NOT applied:`);
|
|
412
|
+
lines.push(` ${result.branchProtection.error}`);
|
|
413
|
+
lines.push(' After resolving (gh auth login, repo permissions, etc.) re-run:');
|
|
414
|
+
lines.push(' ai-sdlc init --add branch-protection');
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
lines.push(`${stepN}. Branch protection (dry-run) — see JSON above; apply with:`);
|
|
418
|
+
lines.push(' ai-sdlc init --add branch-protection');
|
|
419
|
+
}
|
|
420
|
+
lines.push('');
|
|
421
|
+
stepN++;
|
|
422
|
+
}
|
|
423
|
+
lines.push(`${stepN}. Verify your configuration: ai-sdlc health`);
|
|
424
|
+
lines.push('');
|
|
425
|
+
lines.push('Adopter docs: https://github.com/ai-sdlc-framework/ai-sdlc/blob/main/docs/operations/init.md');
|
|
426
|
+
const out = lines.join('\n');
|
|
427
|
+
for (const line of lines)
|
|
428
|
+
adapters.log(line);
|
|
429
|
+
return out;
|
|
430
|
+
}
|
|
431
|
+
// ── CLAUDE.md recommendation pointer (AC #4) ─────────────────────────────
|
|
432
|
+
/**
|
|
433
|
+
* The pointer block we append to CLAUDE.md so a freshly-initialized repo's
|
|
434
|
+
* Claude Code sessions know where to find the AI-SDLC quality-gate docs.
|
|
435
|
+
* Idempotent — guarded by a sentinel so re-running init doesn't duplicate
|
|
436
|
+
* the block.
|
|
437
|
+
*/
|
|
438
|
+
export const CLAUDE_MD_POINTER = `
|
|
439
|
+
<!-- ai-sdlc:recommendation-pointer -->
|
|
440
|
+
## AI-SDLC quality gate
|
|
441
|
+
|
|
442
|
+
This repo is bootstrapped with the AI-SDLC framework. The single PR-ready
|
|
443
|
+
merge gate is \`ai-sdlc/pr-ready\` (see \`.github/workflows/ai-sdlc-gate.yml\`).
|
|
444
|
+
Run \`ai-sdlc health\` to verify your local config; see
|
|
445
|
+
\`docs/operations/init.md\` for the adopter guide.
|
|
446
|
+
<!-- end ai-sdlc:recommendation-pointer -->
|
|
447
|
+
`;
|
|
448
|
+
/** Sentinel marker used by the CLAUDE.md pointer for idempotency. */
|
|
449
|
+
export const CLAUDE_MD_SENTINEL = '<!-- ai-sdlc:recommendation-pointer -->';
|
|
450
|
+
/**
|
|
451
|
+
* Append the recommendation pointer to CLAUDE.md (or create the file if
|
|
452
|
+
* missing). Idempotent: if the sentinel is already present we no-op.
|
|
453
|
+
*/
|
|
454
|
+
export function ensureClaudeMdPointer(projectDir, adapters, dryRun) {
|
|
455
|
+
const path = join(projectDir, 'CLAUDE.md');
|
|
456
|
+
if (dryRun) {
|
|
457
|
+
adapters.log(' would update CLAUDE.md (recommendation pointer)');
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
if (!adapters.exists(path)) {
|
|
461
|
+
adapters.writeFile(path, `# Project instructions\n${CLAUDE_MD_POINTER}`);
|
|
462
|
+
adapters.log(' created CLAUDE.md');
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
const status = adapters.appendOnce(path, CLAUDE_MD_POINTER, CLAUDE_MD_SENTINEL);
|
|
466
|
+
if (status === 'appended') {
|
|
467
|
+
adapters.log(' updated CLAUDE.md (recommendation pointer)');
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
adapters.log(' skip CLAUDE.md (recommendation pointer already present)');
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
//# sourceMappingURL=init-features.js.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedded template strings scaffolded by `ai-sdlc init` (AISDLC-143).
|
|
3
|
+
*
|
|
4
|
+
* Why these live in code instead of being read from disk at runtime:
|
|
5
|
+
* - The orchestrator ships as an npm package; users `npm i -g
|
|
6
|
+
* @ai-sdlc/orchestrator` and run `ai-sdlc init` in a brand new repo.
|
|
7
|
+
* The `dist/` payload would not include arbitrary `*.yml` source
|
|
8
|
+
* fixtures unless we listed each one in `package.json#files` AND
|
|
9
|
+
* plumbed a `__dirname`-based loader through esm import-meta.url.
|
|
10
|
+
* - Embedded strings work identically when invoked via `node ./dist/...`
|
|
11
|
+
* from a checkout, via `npx @ai-sdlc/orchestrator init`, or via a
|
|
12
|
+
* pre-bundled binary, with no runtime filesystem dependency.
|
|
13
|
+
*
|
|
14
|
+
* Drift policy: when the canonical workflow at
|
|
15
|
+
* `.github/workflows/ai-sdlc-gate.yml` (or the audit-only
|
|
16
|
+
* `verify-attestation.yml`) is updated for adopters, mirror the change
|
|
17
|
+
* here. The init-workspace test suite includes a smoke check that the
|
|
18
|
+
* embedded copy at least parses as a YAML mapping; AISDLC-140 sub-3's
|
|
19
|
+
* cutover memo will add a hard byte-equality assertion against the live
|
|
20
|
+
* file once the framework's own copy stabilizes.
|
|
21
|
+
*
|
|
22
|
+
* Q-decisions baked into the templates:
|
|
23
|
+
* - Q1 (prescriptive default): the gate workflow is scaffolded
|
|
24
|
+
* unconditionally — every adopter gets `ai-sdlc/pr-ready` on day one.
|
|
25
|
+
* - Q3 (attestation = audit-only): verify-attestation template is the
|
|
26
|
+
* audit-only variant; signing infrastructure is opt-in via
|
|
27
|
+
* `--with-attestation`.
|
|
28
|
+
* - Q4(b) (interactive default with --yes escape hatch): see
|
|
29
|
+
* `init-features.ts` for the wizard wiring; this module just supplies
|
|
30
|
+
* the byte content.
|
|
31
|
+
*/
|
|
32
|
+
/** `.github/workflows/ai-sdlc-gate.yml` — single rollup PR-ready check. */
|
|
33
|
+
export declare const AI_SDLC_GATE_WORKFLOW = "name: AI-SDLC PR Ready Gate\n\n# Single rollup status check `ai-sdlc/pr-ready` that aggregates every PR\n# signal AI-SDLC adopters need into ONE branch-protection entry. Replaces\n# the historical pattern of enumerating N required checks by name + app_id,\n# which is brittle against path filters, [skip ci] tokens, matrix changes,\n# and multi-app posters. See `docs/operations/quality-gate.md` for the\n# full rationale.\n#\n# Industry pattern: `re-actors/alls-green` (\"alls-green\") is the de facto\n# community fix; named adopters include aiohttp, attrs, conda, setuptools,\n# pytest, pip-tools, Open edX, PyCA, PyPA, Mergify.\n\non:\n pull_request:\n types: [opened, synchronize, reopened]\n merge_group:\n types: [checks_requested]\n\nconcurrency:\n group: ai-sdlc-gate-${{ github.event.pull_request.number || github.event.merge_group.head_sha || github.ref }}\n cancel-in-progress: true\n\npermissions:\n contents: read\n pull-requests: read\n checks: write\n\njobs:\n detect:\n name: Detect Changes\n runs-on: ubuntu-latest\n outputs:\n docs_only: ${{ steps.filter.outputs.docs_only }}\n steps:\n - uses: actions/checkout@v4\n - id: filter\n uses: dorny/paths-filter@v3\n with:\n predicate-quantifier: 'every'\n filters: |\n docs_only:\n - 'docs/**'\n - '*.md'\n\n lint:\n name: Lint & Format\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: pnpm/action-setup@v4\n - uses: actions/setup-node@v4\n with:\n node-version: 22\n cache: pnpm\n - run: pnpm install --frozen-lockfile\n - run: pnpm lint\n - run: pnpm format:check\n\n build-test:\n name: Build & Test (Node ${{ matrix.node-version }})\n needs: detect\n if: needs.detect.outputs.docs_only != 'true'\n runs-on: ubuntu-latest\n strategy:\n fail-fast: false\n matrix:\n node-version: [20, 22]\n steps:\n - uses: actions/checkout@v4\n - uses: pnpm/action-setup@v4\n - uses: actions/setup-node@v4\n with:\n node-version: ${{ matrix.node-version }}\n cache: pnpm\n - run: pnpm install --frozen-lockfile\n - run: pnpm build\n - run: pnpm test\n\n coverage:\n name: Coverage\n needs: detect\n if: needs.detect.outputs.docs_only != 'true'\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: pnpm/action-setup@v4\n - uses: actions/setup-node@v4\n with:\n node-version: 22\n cache: pnpm\n - run: pnpm install --frozen-lockfile\n - run: pnpm build\n - run: pnpm test:coverage\n\n pr-ready:\n name: ai-sdlc/pr-ready\n needs: [detect, lint, build-test, coverage]\n if: always()\n runs-on: ubuntu-latest\n steps:\n - name: Aggregate required signals\n uses: re-actors/alls-green@release/v1\n with:\n jobs: ${{ toJSON(needs) }}\n";
|
|
34
|
+
/**
|
|
35
|
+
* `.github/workflows/verify-attestation.yml` — AUDIT-ONLY verifier.
|
|
36
|
+
*
|
|
37
|
+
* Per Q3 in /tmp/quality-gate-redesign-final.md, attestation infrastructure
|
|
38
|
+
* is opt-in and audit-only. This workflow logs verification results to the
|
|
39
|
+
* action run log but does NOT post a required-status check or block merges.
|
|
40
|
+
* Operators who want to promote attestation to a hard gate can edit the
|
|
41
|
+
* \`Log audit result\` step to write to commit statuses.
|
|
42
|
+
*/
|
|
43
|
+
export declare const VERIFY_ATTESTATION_WORKFLOW = "name: AI-SDLC Verify Review Attestation\n\n# Reads the DSSE attestation at .ai-sdlc/attestations/<head-sha>.dsse.json\n# and verifies the signature against any-of-N pubkeys in\n# .ai-sdlc/trusted-reviewers.yaml.\n#\n# AUDIT-ONLY: this workflow logs verification results (success/failure with\n# reason) for forensic purposes but does NOT post a required commit status.\n# The single merge gate is `ai-sdlc/pr-ready` from ai-sdlc-gate.yml.\n\non:\n pull_request:\n types: [opened, synchronize, reopened]\n branches: [main]\n paths-ignore:\n - 'docs/**'\n - '*.md'\n merge_group:\n types: [checks_requested]\n\nconcurrency:\n group: verify-attestation-${{ github.event.pull_request.number || github.event.merge_group.head_sha }}\n cancel-in-progress: true\n\njobs:\n verify:\n name: Verify attestation\n runs-on: ubuntu-latest\n permissions:\n contents: read\n steps:\n - name: Resolve subject SHA + base SHA from event payload\n id: resolve\n run: |\n if [ \"${{ github.event_name }}\" = \"merge_group\" ]; then\n echo \"head_sha=${{ github.event.merge_group.head_sha }}\" >> \"$GITHUB_OUTPUT\"\n echo \"base_sha=${{ github.event.merge_group.base_sha }}\" >> \"$GITHUB_OUTPUT\"\n else\n echo \"head_sha=${{ github.event.pull_request.head.sha }}\" >> \"$GITHUB_OUTPUT\"\n echo \"base_sha=${{ github.event.pull_request.base.sha }}\" >> \"$GITHUB_OUTPUT\"\n fi\n\n - uses: actions/checkout@v4\n with:\n fetch-depth: 0\n ref: ${{ steps.resolve.outputs.head_sha }}\n\n - name: Log audit result\n env:\n HEAD_SHA: ${{ steps.resolve.outputs.head_sha }}\n run: |\n if [ -f \".ai-sdlc/attestations/${HEAD_SHA}.dsse.json\" ]; then\n echo \"::notice::ai-sdlc attestation AUDIT \u2014 envelope present at ${HEAD_SHA}\"\n else\n echo \"::notice::ai-sdlc attestation AUDIT \u2014 no envelope on ${HEAD_SHA} (audit-only, not blocking)\"\n fi\n";
|
|
44
|
+
/**
|
|
45
|
+
* `.husky/pre-push` snippet that signs an attestation when one is missing
|
|
46
|
+
* for the current HEAD. Installed when `--with-attestation` is opted in;
|
|
47
|
+
* the actual `sign-attestation.mjs` script ships separately with the
|
|
48
|
+
* orchestrator and is referenced by the canonical command stub here.
|
|
49
|
+
*
|
|
50
|
+
* Adopters typically already have a `.husky/pre-push` from their existing
|
|
51
|
+
* tooling; the wizard appends our snippet behind a sentinel so we can
|
|
52
|
+
* extend an existing hook without trampling user content.
|
|
53
|
+
*/
|
|
54
|
+
export declare const HUSKY_PREPUSH_SIGN_SNIPPET = "# ai-sdlc:attestation-sign-block\n# Signs the DSSE attestation envelope for the current HEAD when verdict\n# files exist. Skip with AI_SDLC_SKIP_ATTESTATION_SIGN=1.\nif [ -z \"${AI_SDLC_SKIP_ATTESTATION_SIGN:-}\" ] && [ -x \"./scripts/check-attestation-sign.sh\" ]; then\n ./scripts/check-attestation-sign.sh\nfi\n# end ai-sdlc:attestation-sign-block\n";
|
|
55
|
+
/**
|
|
56
|
+
* `.ai-sdlc/trusted-reviewers.yaml` stub — empty allowlist with operator
|
|
57
|
+
* instructions. The wizard scaffolds this so adopters have a single file
|
|
58
|
+
* to receive contributor pubkey PRs into; bootstrap a contributor with
|
|
59
|
+
* `/ai-sdlc init-signing-key` and append the printed YAML block.
|
|
60
|
+
*/
|
|
61
|
+
export declare const TRUSTED_REVIEWERS_STUB = "# Trusted contributor signing keys for review attestations.\n#\n# This file is a stub created by `ai-sdlc init --with-attestation`. Add\n# entries by running `/ai-sdlc init-signing-key` on a contributor's\n# machine and opening a PR that appends the printed YAML block below.\n#\n# Schema:\n# - identity: free-form string (typically email or GitHub handle)\n# - machine: free-form label (lets one identity register multiple keys)\n# - pubkey: PEM-encoded ed25519 public key (multi-line block scalar)\n# - addedAt: ISO 8601 date the entry was added\n# - addedBy: GitHub handle of the maintainer who approved this entry's PR\n#\n# The verifier in CI uses a strict YAML format: every scalar value\n# single-quoted; `pubkey:` is a `|` block scalar with each PEM line\n# indented exactly 6 spaces; no tab characters anywhere.\n\nreviewers: []\n";
|
|
62
|
+
/**
|
|
63
|
+
* `.ai-sdlc/dor-config.yaml` stub — Definition-of-Ready rubric config.
|
|
64
|
+
*
|
|
65
|
+
* Mirrors the production DoR config used by the framework itself; ships
|
|
66
|
+
* in warn-only mode so a fresh adopter does not get blocked by the rubric
|
|
67
|
+
* while they tune it.
|
|
68
|
+
*/
|
|
69
|
+
export declare const DOR_CONFIG_STUB = "# Definition-of-Ready (DoR) gate configuration.\n#\n# The DoR rubric scores incoming issues + backlog tasks against seven\n# criteria (binary-testable ACs, no wishlist markers, references resolve,\n# bounded scope, surface named, done-state describable, no invisible\n# dependencies). Failing the rubric posts a clarification comment on the\n# issue / PR.\n#\n# Ships in warn-only mode by default \u2014 flip to 'enforce' after the soak\n# window confirms the false-positive rate is low.\n\napiVersion: ai-sdlc.io/v1alpha1\nkind: DorConfig\nmetadata:\n name: ai-sdlc-dor\nspec:\n rubricVersion: v1\n evaluationMode: warn-only\n\n notifications:\n authorChannel: true\n # dedicatedChannel:\n # slack: '#ai-sdlc-dor'\n\n staleness:\n warnAfterDays: 14\n closeAfterDays: 28\n closedLabel: 'closed-as-stale-dor'\n";
|
|
70
|
+
/** `.github/workflows/dor-ingress.yml` — minimal DoR ingress shim. */
|
|
71
|
+
export declare const DOR_INGRESS_WORKFLOW = "name: AI-SDLC DoR Ingress\n\n# Wires the DoR rubric into the GitHub issue + PR lifecycle:\n# - issues:opened / issues:edited \u2192 score the issue body, post the\n# idempotent clarification comment when needs-clarification.\n# - pull_request touching backlog/tasks/*.md \u2192 score the changed\n# task bodies (the in-repo equivalent of an issue).\n#\n# Ships in warn-only mode (see .ai-sdlc/dor-config.yaml). Flip the\n# config's `evaluationMode` to `enforce` after the soak window.\n\non:\n issues:\n types: [opened, edited]\n pull_request:\n types: [opened, synchronize, reopened]\n paths:\n - 'backlog/tasks/*.md'\n\nconcurrency:\n group: dor-ingress-${{ github.event.issue.number || github.event.pull_request.number }}\n cancel-in-progress: true\n\njobs:\n evaluate:\n name: Evaluate against DoR rubric\n runs-on: ubuntu-latest\n permissions:\n issues: write\n pull-requests: write\n contents: read\n steps:\n - uses: actions/checkout@v4\n - uses: pnpm/action-setup@v4\n - uses: actions/setup-node@v4\n with:\n node-version: 22\n cache: pnpm\n - run: pnpm install --frozen-lockfile\n - name: Evaluate DoR (placeholder \u2014 wire to your DoR runner)\n run: |\n echo \"::notice::DoR ingress shim invoked. Wire pnpm --filter @ai-sdlc/pipeline-cli evaluate-dor here.\"\n";
|
|
72
|
+
/**
|
|
73
|
+
* `.ai-sdlc/review-classifier.yaml` stub — cost-optimized review tiers.
|
|
74
|
+
*
|
|
75
|
+
* The actual classifier code ships via AISDLC-141 (a follow-up). For now
|
|
76
|
+
* the wizard scaffolds the config stub + a pointer to the classifier
|
|
77
|
+
* docs so adopters who opt in via `--with-classifier` are ready to flip
|
|
78
|
+
* on the workflow once AISDLC-141 lands.
|
|
79
|
+
*/
|
|
80
|
+
export declare const REVIEW_CLASSIFIER_STUB = "# Review classifier configuration (AISDLC-141, follow-up).\n#\n# The review classifier inspects a PR diff and decides which review tier\n# to invoke (cheap-pattern-match \u2192 mid-tier-LLM \u2192 full reviewer fan-out)\n# to keep review costs bounded as PR volume grows.\n#\n# This file is a stub scaffolded by `ai-sdlc init --with-classifier`.\n# The classifier runtime ships in AISDLC-141; until then this config is\n# advisory only. See docs/operations/init.md for the migration path.\n\napiVersion: ai-sdlc.io/v1alpha1\nkind: ReviewClassifier\nmetadata:\n name: default-classifier\nspec:\n tiers:\n - name: cheap\n maxFilesChanged: 5\n maxLinesChanged: 50\n strategy: pattern-match\n - name: mid\n maxFilesChanged: 20\n maxLinesChanged: 500\n strategy: single-llm-pass\n - name: full\n strategy: full-reviewer-fanout\n routing:\n docsOnly: cheap\n testOnly: cheap\n default: mid\n";
|
|
81
|
+
/**
|
|
82
|
+
* The set of feature templates exported as a single map so the wizard
|
|
83
|
+
* dispatcher can iterate without each feature growing its own switch
|
|
84
|
+
* statement.
|
|
85
|
+
*
|
|
86
|
+
* Each entry maps a relative path inside the target repo to the literal
|
|
87
|
+
* bytes to write. Paths are POSIX-style; the writer joins them onto the
|
|
88
|
+
* project dir using `node:path/join` which is platform-correct.
|
|
89
|
+
*/
|
|
90
|
+
export interface FeatureTemplateSet {
|
|
91
|
+
/** Files under the project root keyed by relative POSIX path. */
|
|
92
|
+
files: Record<string, string>;
|
|
93
|
+
}
|
|
94
|
+
export declare const DOR_TEMPLATES: FeatureTemplateSet;
|
|
95
|
+
export declare const ATTESTATION_TEMPLATES: FeatureTemplateSet;
|
|
96
|
+
export declare const CLASSIFIER_TEMPLATES: FeatureTemplateSet;
|
|
97
|
+
/**
|
|
98
|
+
* Always-on baseline workflow templates (regardless of wizard answers).
|
|
99
|
+
* Matches AC #4 in AISDLC-143: pipeline.yaml + agent-role.yaml +
|
|
100
|
+
* quality-gate.yaml + autonomy-policy.yaml are scaffolded by `initProject`
|
|
101
|
+
* (existing logic) and the gate workflow is scaffolded by this map.
|
|
102
|
+
*/
|
|
103
|
+
export declare const BASELINE_WORKFLOW_TEMPLATES: FeatureTemplateSet;
|
|
104
|
+
//# sourceMappingURL=init-templates.d.ts.map
|