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