@ai-sdlc/orchestrator 0.9.0 → 0.13.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/adapters.d.ts +42 -3
- package/dist/adapters.js +133 -3
- package/dist/admission-composite.d.ts +134 -1
- package/dist/admission-composite.js +170 -6
- package/dist/admission-enrichment.d.ts +12 -3
- package/dist/admission-enrichment.js +71 -10
- package/dist/admission-hc.d.ts +20 -1
- package/dist/admission-hc.js +12 -1
- package/dist/admission-score.d.ts +44 -0
- package/dist/admission-score.js +11 -2
- package/dist/backlog-adapter.d.ts +45 -0
- package/dist/backlog-adapter.js +145 -1
- package/dist/calibration.d.ts +81 -0
- package/dist/calibration.js +76 -0
- package/dist/cli/commands/git-remote.d.ts +25 -0
- package/dist/cli/commands/git-remote.js +80 -1
- package/dist/cli/commands/init-features.d.ts +507 -0
- package/dist/cli/commands/init-features.js +1100 -0
- package/dist/cli/commands/init-templates.d.ts +301 -0
- package/dist/cli/commands/init-templates.js +1341 -0
- package/dist/cli/commands/init.d.ts +67 -0
- package/dist/cli/commands/init.js +301 -2
- package/dist/cli/commands/run.js +9 -1
- package/dist/cli/index.d.ts +11 -0
- package/dist/cli/index.js +42 -8
- package/dist/compliance/composer.d.ts +79 -0
- package/dist/compliance/composer.js +258 -0
- package/dist/compliance/errors.d.ts +64 -0
- package/dist/compliance/errors.js +85 -0
- package/dist/compliance/loader.d.ts +52 -0
- package/dist/compliance/loader.js +124 -0
- package/dist/compliance/types.d.ts +184 -0
- package/dist/compliance/types.js +41 -0
- package/dist/compliance-clearance.d.ts +269 -0
- package/dist/compliance-clearance.js +269 -0
- package/dist/config.js +17 -0
- package/dist/cost-tracker.d.ts +22 -0
- package/dist/cost-tracker.js +41 -0
- package/dist/database/adapters/external.js +5 -1
- package/dist/design-authority.d.ts +13 -0
- package/dist/design-authority.js +13 -0
- package/dist/embedding/adapters/openai-text-embedding-3-small.d.ts +71 -0
- package/dist/embedding/adapters/openai-text-embedding-3-small.js +190 -0
- package/dist/embedding/consumers/tessellation-drift.d.ts +74 -0
- package/dist/embedding/consumers/tessellation-drift.js +76 -0
- package/dist/embedding/cross-provider.d.ts +78 -0
- package/dist/embedding/cross-provider.js +75 -0
- package/dist/embedding/deprecation.d.ts +151 -0
- package/dist/embedding/deprecation.js +229 -0
- package/dist/embedding/errors.d.ts +90 -0
- package/dist/embedding/errors.js +150 -0
- package/dist/embedding/index.d.ts +29 -0
- package/dist/embedding/index.js +24 -0
- package/dist/embedding/pipeline-load.d.ts +146 -0
- package/dist/embedding/pipeline-load.js +178 -0
- package/dist/embedding/registry.d.ts +45 -0
- package/dist/embedding/registry.js +61 -0
- package/dist/embedding/stale-vector.d.ts +110 -0
- package/dist/embedding/stale-vector.js +92 -0
- package/dist/embedding/storage/index.d.ts +51 -0
- package/dist/embedding/storage/index.js +43 -0
- package/dist/embedding/storage/jsonl-backend.d.ts +150 -0
- package/dist/embedding/storage/jsonl-backend.js +332 -0
- package/dist/embedding/storage/types.d.ts +135 -0
- package/dist/embedding/storage/types.js +13 -0
- package/dist/embedding/types.d.ts +180 -0
- package/dist/embedding/types.js +10 -0
- package/dist/execute.d.ts +29 -2
- package/dist/execute.js +179 -35
- package/dist/index.d.ts +14 -2
- package/dist/index.js +19 -2
- package/dist/journey/inheritance-validator.d.ts +396 -0
- package/dist/journey/inheritance-validator.js +370 -0
- package/dist/journey/state-id-drift-rule.d.ts +137 -0
- package/dist/journey/state-id-drift-rule.js +245 -0
- package/dist/journey-sa2-router.d.ts +395 -0
- package/dist/journey-sa2-router.js +308 -0
- package/dist/models/classifier.d.ts +3 -1
- package/dist/models/classifier.js +61 -5
- package/dist/pillar-breakdown.d.ts +23 -1
- package/dist/pillar-breakdown.js +9 -0
- package/dist/runners/review-agent.js +6 -2
- package/dist/runners/runner-registry.d.ts +36 -0
- package/dist/runners/runner-registry.js +90 -0
- package/dist/runtime/attestations.d.ts +663 -11
- package/dist/runtime/attestations.js +971 -24
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/parallelism-flag.d.ts +21 -3
- package/dist/runtime/parallelism-flag.js +27 -6
- package/dist/runtime/port-allocator.d.ts +11 -0
- package/dist/runtime/port-allocator.js +30 -0
- package/dist/sa-scoring/layer3-llm.js +6 -1
- package/dist/sa-scoring/revision-proposal-config.d.ts +178 -0
- package/dist/sa-scoring/revision-proposal-config.js +198 -0
- package/dist/sa-scoring/revision-proposal.d.ts +285 -0
- package/dist/sa-scoring/revision-proposal.js +417 -0
- package/dist/signal-ingestion/adapters/community-thread.d.ts +43 -0
- package/dist/signal-ingestion/adapters/community-thread.js +55 -0
- package/dist/signal-ingestion/adapters/in-app-feedback.d.ts +67 -0
- package/dist/signal-ingestion/adapters/in-app-feedback.js +51 -0
- package/dist/signal-ingestion/adapters/manual.d.ts +78 -0
- package/dist/signal-ingestion/adapters/manual.js +112 -0
- package/dist/signal-ingestion/adapters/support-ticket.d.ts +47 -0
- package/dist/signal-ingestion/adapters/support-ticket.js +51 -0
- package/dist/signal-ingestion/classifier.d.ts +205 -0
- package/dist/signal-ingestion/classifier.js +494 -0
- package/dist/signal-ingestion/clustering-types.d.ts +36 -0
- package/dist/signal-ingestion/clustering-types.js +14 -0
- package/dist/signal-ingestion/clustering.d.ts +200 -0
- package/dist/signal-ingestion/clustering.js +413 -0
- package/dist/signal-ingestion/config.d.ts +351 -0
- package/dist/signal-ingestion/config.js +587 -0
- package/dist/signal-ingestion/d1.d.ts +252 -0
- package/dist/signal-ingestion/d1.js +235 -0
- package/dist/signal-ingestion/errors.d.ts +73 -0
- package/dist/signal-ingestion/errors.js +108 -0
- package/dist/signal-ingestion/governance-events.d.ts +181 -0
- package/dist/signal-ingestion/governance-events.js +189 -0
- package/dist/signal-ingestion/index.d.ts +35 -0
- package/dist/signal-ingestion/index.js +53 -0
- package/dist/signal-ingestion/manual-share-metric.d.ts +93 -0
- package/dist/signal-ingestion/manual-share-metric.js +106 -0
- package/dist/signal-ingestion/registry.d.ts +40 -0
- package/dist/signal-ingestion/registry.js +137 -0
- package/dist/signal-ingestion/residency.d.ts +227 -0
- package/dist/signal-ingestion/residency.js +238 -0
- package/dist/signal-ingestion/significance.d.ts +554 -0
- package/dist/signal-ingestion/significance.js +555 -0
- package/dist/signal-ingestion/types.d.ts +191 -0
- package/dist/signal-ingestion/types.js +8 -0
- package/dist/substrate/drift-composition.d.ts +270 -0
- package/dist/substrate/drift-composition.js +306 -0
- package/dist/substrate/drift-tui-surface.d.ts +61 -0
- package/dist/substrate/drift-tui-surface.js +102 -0
- package/dist/substrate/identity-class.d.ts +176 -0
- package/dist/substrate/identity-class.js +201 -0
- package/dist/tessellation/cross-soul-provenance-rule.d.ts +133 -0
- package/dist/tessellation/cross-soul-provenance-rule.js +171 -0
- package/dist/tessellation/inter-soul-embedding-distance-rule.d.ts +61 -0
- package/dist/tessellation/inter-soul-embedding-distance-rule.js +67 -0
- package/dist/tessellation/rule-registry.d.ts +269 -0
- package/dist/tessellation/rule-registry.js +92 -0
- package/dist/tessellation/soul-slug-ast-scan-rule.d.ts +90 -0
- package/dist/tessellation/soul-slug-ast-scan-rule.js +158 -0
- package/dist/tessellation-admission.d.ts +162 -0
- package/dist/tessellation-admission.js +146 -0
- package/dist/tessellation-drift.d.ts +246 -0
- package/dist/tessellation-drift.js +250 -0
- package/dist/validate-config.js +13 -0
- package/dist/validate-issue.js +2 -2
- package/dist/variant/cardinality-activation.d.ts +126 -0
- package/dist/variant/cardinality-activation.js +101 -0
- package/dist/variant/deprecation-lifecycle.d.ts +184 -0
- package/dist/variant/deprecation-lifecycle.js +208 -0
- package/dist/variant/drift-extension.d.ts +136 -0
- package/dist/variant/drift-extension.js +164 -0
- package/dist/variant/engineering-review.d.ts +185 -0
- package/dist/variant/engineering-review.js +142 -0
- package/dist/variant/index.d.ts +32 -0
- package/dist/variant/index.js +32 -0
- package/dist/variant/inheritance-validator.d.ts +165 -0
- package/dist/variant/inheritance-validator.js +139 -0
- package/dist/variant/internal-adopter/index.d.ts +11 -0
- package/dist/variant/internal-adopter/index.js +10 -0
- package/dist/variant/internal-adopter/products.d.ts +156 -0
- package/dist/variant/internal-adopter/products.js +366 -0
- package/dist/variant-admission.d.ts +316 -0
- package/dist/variant-admission.js +247 -0
- package/package.json +11 -8
|
@@ -12,8 +12,24 @@
|
|
|
12
12
|
* orchestrator version that ran init, with an inline opt-out hint.
|
|
13
13
|
* - Skip `.cursor/mcp.json` unless Cursor is detected (project-local
|
|
14
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.
|
|
15
29
|
*/
|
|
16
30
|
import { Command } from 'commander';
|
|
31
|
+
import { type WizardFlags } from './init-features.js';
|
|
32
|
+
export declare const PIPELINE_YAML = "apiVersion: ai-sdlc.io/v1alpha1\nkind: Pipeline\nmetadata:\n name: default\nspec:\n triggers:\n - event: issue.labeled\n filter:\n labels:\n - ai-eligible\n providers:\n sourceControl:\n type: github\n config:\n org: your-org\n stages:\n - name: validate\n qualityGates:\n - default-gates\n - name: code\n agent: default-agent\n timeout: PT30M\n onFailure:\n strategy: retry\n maxRetries: 2\n - name: review\n qualityGates:\n - default-gates\n # backlog: holds settings specific to the backlog-task (/ai-sdlc execute)\n # workflow. These were formerly in a separate pipeline-backlog.yaml file\n # (deprecated by AISDLC-245.5). Slash commands + pipeline-cli readers\n # prefer this canonical location.\n backlog:\n branching:\n pattern: 'ai-sdlc/{issueIdLower}-{slug}'\n targetBranch: main\n cleanup: on-merge\n pullRequest:\n titleTemplate: 'feat: {issueTitle} ({issueId})'\n descriptionSections:\n - summary\n - changes\n - closes\n includeProvenance: true\n closeKeyword: References\n";
|
|
17
33
|
/**
|
|
18
34
|
* Tier-based agent-role templates (AISDLC-79).
|
|
19
35
|
*
|
|
@@ -28,5 +44,56 @@ export type AgentRoleTier = 'coding' | 'research' | 'meta';
|
|
|
28
44
|
export declare const AGENT_ROLE_TIERS: readonly AgentRoleTier[];
|
|
29
45
|
/** Resolve the agent-role.yaml template for a given tier. Default = `coding`. */
|
|
30
46
|
export declare function getAgentRoleYaml(tier?: AgentRoleTier): string;
|
|
47
|
+
/**
|
|
48
|
+
* Options for `scaffoldSoulDsbs`.
|
|
49
|
+
*/
|
|
50
|
+
export interface ScaffoldSoulDsbsOptions {
|
|
51
|
+
/**
|
|
52
|
+
* Name of the platform-root DesignSystemBinding resource.
|
|
53
|
+
* Referenced in each per-soul DSB's `spec.extends` field.
|
|
54
|
+
* Defaults to `'platform-dsb'` when not supplied.
|
|
55
|
+
*/
|
|
56
|
+
platformDsbName?: string;
|
|
57
|
+
/**
|
|
58
|
+
* When true, print what would be created without writing files.
|
|
59
|
+
* Mirrors the `--dry-run` semantics of `initProject()`.
|
|
60
|
+
*/
|
|
61
|
+
dryRun?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Output prefix for console lines (e.g. ' ' for indented workspace output).
|
|
64
|
+
* Defaults to empty string.
|
|
65
|
+
*/
|
|
66
|
+
prefix?: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Scaffold per-soul DesignSystemBinding template files for a Tessellated Platform.
|
|
70
|
+
*
|
|
71
|
+
* Creates `.ai-sdlc/souls/<slug>/design-system-binding.yaml` for each soul slug.
|
|
72
|
+
* Each file is a complete `DesignSystemBinding` resource that additively extends
|
|
73
|
+
* the platform-root DSB per RFC-0009 §6 resolution rules.
|
|
74
|
+
*
|
|
75
|
+
* Idempotent: existing soul DSB files are skipped (not overwritten).
|
|
76
|
+
*
|
|
77
|
+
* @param soulSlugs - soul identifiers to scaffold (e.g. ['soul-a', 'soul-b'])
|
|
78
|
+
* @param aiSdlcDir - absolute path to the `.ai-sdlc/` directory (config dir)
|
|
79
|
+
* @param options - optional configuration: platformDsbName, dryRun, prefix
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* scaffoldSoulDsbs(['soul-a', 'soul-b', 'soul-c'], '/project/.ai-sdlc', {
|
|
84
|
+
* platformDsbName: 'acme-platform-dsb',
|
|
85
|
+
* });
|
|
86
|
+
* // Created: /project/.ai-sdlc/souls/soul-a/design-system-binding.yaml
|
|
87
|
+
* // Created: /project/.ai-sdlc/souls/soul-b/design-system-binding.yaml
|
|
88
|
+
* // Created: /project/.ai-sdlc/souls/soul-c/design-system-binding.yaml
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export declare function scaffoldSoulDsbs(soulSlugs: readonly string[], aiSdlcDir: string, options?: ScaffoldSoulDsbsOptions): void;
|
|
92
|
+
/**
|
|
93
|
+
* Build the WizardFlags struct from Commander's parsed options. Pulled
|
|
94
|
+
* out into a function so tests can drive the wizard pipeline directly
|
|
95
|
+
* without going through Commander's stateful option store.
|
|
96
|
+
*/
|
|
97
|
+
export declare function buildWizardFlags(opts: Record<string, unknown>): WizardFlags;
|
|
31
98
|
export declare const initCommand: Command;
|
|
32
99
|
//# sourceMappingURL=init.d.ts.map
|
|
@@ -12,6 +12,20 @@
|
|
|
12
12
|
* orchestrator version that ran init, with an inline opt-out hint.
|
|
13
13
|
* - Skip `.cursor/mcp.json` unless Cursor is detected (project-local
|
|
14
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.
|
|
15
29
|
*/
|
|
16
30
|
import { Command } from 'commander';
|
|
17
31
|
import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync } from 'node:fs';
|
|
@@ -20,7 +34,9 @@ import { detectAgentsDetailed, installMcpServer } from './mcp-setup.js';
|
|
|
20
34
|
import { detectWorkspace, generateWorkspaceYaml } from './workspace-detect.js';
|
|
21
35
|
import { detectGitRemote, applyRemoteToPipelineYaml } from './git-remote.js';
|
|
22
36
|
import { resolveVersions, formatVersionBlock } from '../versions.js';
|
|
23
|
-
|
|
37
|
+
import { applyFeatureSelection, buildProductionAdapters, ensureClaudeMdPointer, renderNextSteps, resolveFeatureSelection, resolveInstallTarget, runComplianceStep, } from './init-features.js';
|
|
38
|
+
import { CALIBRATION_YAML_STUB, EMBEDDING_CONFIG_YAML_STUB, buildSoulDsbTemplate, } from './init-templates.js';
|
|
39
|
+
export const PIPELINE_YAML = `apiVersion: ai-sdlc.io/v1alpha1
|
|
24
40
|
kind: Pipeline
|
|
25
41
|
metadata:
|
|
26
42
|
name: default
|
|
@@ -48,6 +64,23 @@ spec:
|
|
|
48
64
|
- name: review
|
|
49
65
|
qualityGates:
|
|
50
66
|
- default-gates
|
|
67
|
+
# backlog: holds settings specific to the backlog-task (/ai-sdlc execute)
|
|
68
|
+
# workflow. These were formerly in a separate pipeline-backlog.yaml file
|
|
69
|
+
# (deprecated by AISDLC-245.5). Slash commands + pipeline-cli readers
|
|
70
|
+
# prefer this canonical location.
|
|
71
|
+
backlog:
|
|
72
|
+
branching:
|
|
73
|
+
pattern: 'ai-sdlc/{issueIdLower}-{slug}'
|
|
74
|
+
targetBranch: main
|
|
75
|
+
cleanup: on-merge
|
|
76
|
+
pullRequest:
|
|
77
|
+
titleTemplate: 'feat: {issueTitle} ({issueId})'
|
|
78
|
+
descriptionSections:
|
|
79
|
+
- summary
|
|
80
|
+
- changes
|
|
81
|
+
- closes
|
|
82
|
+
includeProvenance: true
|
|
83
|
+
closeKeyword: References
|
|
51
84
|
`;
|
|
52
85
|
export const AGENT_ROLE_TIERS = ['coding', 'research', 'meta'];
|
|
53
86
|
const AGENT_ROLE_YAML_CODING = `apiVersion: ai-sdlc.io/v1alpha1
|
|
@@ -228,6 +261,51 @@ spec:
|
|
|
228
261
|
action: demote-one-level
|
|
229
262
|
cooldown: 2w
|
|
230
263
|
`;
|
|
264
|
+
/**
|
|
265
|
+
* Scaffold per-soul DesignSystemBinding template files for a Tessellated Platform.
|
|
266
|
+
*
|
|
267
|
+
* Creates `.ai-sdlc/souls/<slug>/design-system-binding.yaml` for each soul slug.
|
|
268
|
+
* Each file is a complete `DesignSystemBinding` resource that additively extends
|
|
269
|
+
* the platform-root DSB per RFC-0009 §6 resolution rules.
|
|
270
|
+
*
|
|
271
|
+
* Idempotent: existing soul DSB files are skipped (not overwritten).
|
|
272
|
+
*
|
|
273
|
+
* @param soulSlugs - soul identifiers to scaffold (e.g. ['soul-a', 'soul-b'])
|
|
274
|
+
* @param aiSdlcDir - absolute path to the `.ai-sdlc/` directory (config dir)
|
|
275
|
+
* @param options - optional configuration: platformDsbName, dryRun, prefix
|
|
276
|
+
*
|
|
277
|
+
* @example
|
|
278
|
+
* ```ts
|
|
279
|
+
* scaffoldSoulDsbs(['soul-a', 'soul-b', 'soul-c'], '/project/.ai-sdlc', {
|
|
280
|
+
* platformDsbName: 'acme-platform-dsb',
|
|
281
|
+
* });
|
|
282
|
+
* // Created: /project/.ai-sdlc/souls/soul-a/design-system-binding.yaml
|
|
283
|
+
* // Created: /project/.ai-sdlc/souls/soul-b/design-system-binding.yaml
|
|
284
|
+
* // Created: /project/.ai-sdlc/souls/soul-c/design-system-binding.yaml
|
|
285
|
+
* ```
|
|
286
|
+
*/
|
|
287
|
+
export function scaffoldSoulDsbs(soulSlugs, aiSdlcDir, options = {}) {
|
|
288
|
+
const { platformDsbName = 'platform-dsb', dryRun = false, prefix = '' } = options;
|
|
289
|
+
if (soulSlugs.length === 0) {
|
|
290
|
+
console.log(`${prefix}No soul slugs provided — skipping soul DSB scaffolding.`);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
for (const slug of soulSlugs) {
|
|
294
|
+
const soulDir = join(aiSdlcDir, 'souls', slug);
|
|
295
|
+
const dsbPath = join(soulDir, 'design-system-binding.yaml');
|
|
296
|
+
if (dryRun) {
|
|
297
|
+
console.log(`${prefix}Would create ${dsbPath}`);
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
if (existsSync(dsbPath)) {
|
|
301
|
+
console.log(`${prefix} skip souls/${slug}/design-system-binding.yaml (already exists)`);
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
mkdirSync(soulDir, { recursive: true });
|
|
305
|
+
writeFileSync(dsbPath, buildSoulDsbTemplate(slug, platformDsbName), 'utf-8');
|
|
306
|
+
console.log(`${prefix} created souls/${slug}/design-system-binding.yaml`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
231
309
|
const GITIGNORE_PATHS = ['.ai-sdlc/state.db', '.ai-sdlc/state/', '.ai-sdlc/audit.jsonl'];
|
|
232
310
|
/** Ensure .gitignore includes AI-SDLC runtime artifact entries. */
|
|
233
311
|
function ensureGitignore(projectDir, dryRun, prefix = '') {
|
|
@@ -255,6 +333,16 @@ function initProject(projectDir, configDirName, dryRun, prefix = '', options = {
|
|
|
255
333
|
{ name: 'agent-role.yaml', content: getAgentRoleYaml(options.tier) },
|
|
256
334
|
{ name: 'quality-gate.yaml', content: QUALITY_GATE_YAML },
|
|
257
335
|
{ name: 'autonomy-policy.yaml', content: AUTONOMY_POLICY_YAML },
|
|
336
|
+
// RFC-0031 §12.6 per-org calibration config (Refit AISDLC-310):
|
|
337
|
+
// confidence thresholds + rejection weights/floor for the DID revision
|
|
338
|
+
// proposal mechanism. Defaults match the operator-affirmed shipped values.
|
|
339
|
+
{ name: 'calibration.yaml', content: CALIBRATION_YAML_STUB },
|
|
340
|
+
// RFC-0019 §15.1 per-org embedding-framework defaults (AISDLC-340).
|
|
341
|
+
// Documents every NEW re-walkthrough field (scaleEscalationHeuristic,
|
|
342
|
+
// perConsumerOverridesAllowed, crossProviderPolicy split, catalogDedup
|
|
343
|
+
// milestones, unifiedCostReport, adapterBillingModelRespected). The
|
|
344
|
+
// file is OPTIONAL — when absent, framework defaults match these values.
|
|
345
|
+
{ name: 'embedding-config.yaml', content: EMBEDDING_CONFIG_YAML_STUB },
|
|
258
346
|
];
|
|
259
347
|
if (dryRun) {
|
|
260
348
|
console.log(`${prefix}Would create ${configDir}/`);
|
|
@@ -301,6 +389,60 @@ function initWorkspaceRoot(workspacePath, repos, configDirName, dryRun) {
|
|
|
301
389
|
console.log(` created workspace.yaml`);
|
|
302
390
|
}
|
|
303
391
|
}
|
|
392
|
+
/**
|
|
393
|
+
* Build the WizardFlags struct from Commander's parsed options. Pulled
|
|
394
|
+
* out into a function so tests can drive the wizard pipeline directly
|
|
395
|
+
* without going through Commander's stateful option store.
|
|
396
|
+
*/
|
|
397
|
+
export function buildWizardFlags(opts) {
|
|
398
|
+
const addRaw = typeof opts.add === 'string' ? opts.add : undefined;
|
|
399
|
+
let add;
|
|
400
|
+
if (addRaw === 'dor' ||
|
|
401
|
+
addRaw === 'attestation' ||
|
|
402
|
+
addRaw === 'classifier' ||
|
|
403
|
+
addRaw === 'branch-protection' ||
|
|
404
|
+
addRaw === 'workflows' ||
|
|
405
|
+
addRaw === 'signal-ingestion') {
|
|
406
|
+
add = addRaw;
|
|
407
|
+
}
|
|
408
|
+
return {
|
|
409
|
+
yes: !!opts.yes,
|
|
410
|
+
withDor: !!opts.withDor,
|
|
411
|
+
withAttestation: !!opts.withAttestation,
|
|
412
|
+
withClassifier: !!opts.withClassifier,
|
|
413
|
+
withBranchProtection: !!opts.withBranchProtection,
|
|
414
|
+
withWorkflows: !!opts.withWorkflows,
|
|
415
|
+
withSignalIngestion: !!opts.withSignalIngestion,
|
|
416
|
+
add,
|
|
417
|
+
dryRun: !!opts.dryRun,
|
|
418
|
+
workspace: typeof opts.workspace === 'string' ? opts.workspace : undefined,
|
|
419
|
+
force: !!opts.force,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
/** Validate a `--add` arg and return either the normalized value or null+error. */
|
|
423
|
+
function validateAddArg(addRaw) {
|
|
424
|
+
if (addRaw === undefined || addRaw === null || addRaw === false)
|
|
425
|
+
return { ok: true };
|
|
426
|
+
if (typeof addRaw !== 'string')
|
|
427
|
+
return { ok: false, error: `--add must be a string` };
|
|
428
|
+
const allowed = [
|
|
429
|
+
'dor',
|
|
430
|
+
'attestation',
|
|
431
|
+
'classifier',
|
|
432
|
+
'branch-protection',
|
|
433
|
+
'workflows',
|
|
434
|
+
'signal-ingestion',
|
|
435
|
+
'souls',
|
|
436
|
+
];
|
|
437
|
+
if (!allowed.includes(addRaw)) {
|
|
438
|
+
return {
|
|
439
|
+
ok: false,
|
|
440
|
+
error: `--add: unknown feature '${addRaw}'. Expected one of: ${allowed.join(', ')}.`,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
// 'souls' is handled separately in the action; cast the rest to WizardFlags['add']
|
|
444
|
+
return { ok: true, value: addRaw };
|
|
445
|
+
}
|
|
304
446
|
export const initCommand = new Command('init')
|
|
305
447
|
.description('Initialize AI-SDLC configuration in the current project')
|
|
306
448
|
.option('--dry-run', 'Show what would be created without writing files')
|
|
@@ -308,10 +450,48 @@ export const initCommand = new Command('init')
|
|
|
308
450
|
.option('--cursor', 'Force-install Cursor MCP config even if Cursor is not detected')
|
|
309
451
|
.option('-d, --dir <path>', 'Config directory name', '.ai-sdlc')
|
|
310
452
|
.option('--role <tier>', `Agent-role tool tier: ${AGENT_ROLE_TIERS.join(' | ')} (default: coding)`, 'coding')
|
|
453
|
+
// ── AISDLC-143 wizard flags ─────────────────────────────────────────
|
|
454
|
+
.option('-y, --yes', 'Accept all defaults (non-interactive; CI/scripts)')
|
|
455
|
+
.option('--with-dor', 'Scaffold Definition-of-Ready gate config + workflow')
|
|
456
|
+
.option('--with-attestation', 'Scaffold attestation infrastructure (audit-only)')
|
|
457
|
+
.option('--with-classifier', 'Scaffold review classifier config stub')
|
|
458
|
+
.option('--with-branch-protection', 'Apply recommended branch-protection rule to main (requires gh)')
|
|
459
|
+
// ── AISDLC-261 workflow scaffold flags ──────────────────────────────
|
|
460
|
+
.option('--with-workflows', 'Scaffold GitHub Actions workflow bundle (gate, review, attestation, auto-merge)')
|
|
461
|
+
.option('--force', 'Overwrite existing workflow files (use with --with-workflows or --add workflows)')
|
|
462
|
+
// ── AISDLC-348 RFC-0030 signal-ingestion scaffold flag ──────────────
|
|
463
|
+
.option('--with-signal-ingestion', 'Scaffold RFC-0030 signal-ingestion config (default OFF; opt in via AI_SDLC_SIGNAL_INGESTION soak)')
|
|
464
|
+
.option('--add <feature>', 'Extend an already-initialized repo with a single feature: dor | attestation | classifier | branch-protection | workflows | signal-ingestion | souls')
|
|
465
|
+
// ── RFC-0009 Phase 2.2 — per-soul DSB scaffolding ──────────────────
|
|
466
|
+
.option('--souls <slugs>', 'Comma-separated soul slugs for --add souls (e.g. --souls soul-a,soul-b,soul-c)')
|
|
467
|
+
.option('--platform-dsb <name>', 'Platform-root DSB name for --add souls extends reference (default: platform-dsb)', 'platform-dsb')
|
|
468
|
+
.option('--workspace <name>', 'Opt into a per-workspace install at packages/<name>/.ai-sdlc/ instead of the git-root default')
|
|
311
469
|
.action(async (opts) => {
|
|
312
|
-
const projectDir = process.cwd();
|
|
313
470
|
const configDirName = opts.dir ?? '.ai-sdlc';
|
|
314
471
|
const dryRun = !!opts.dryRun;
|
|
472
|
+
// ── AISDLC-262: resolve install target via git rev-parse --show-toplevel ─
|
|
473
|
+
// Default: install at the git root. If the root already has .ai-sdlc/,
|
|
474
|
+
// refuse with a clear message unless --workspace <name> is passed.
|
|
475
|
+
// Printed on the first output line so adopters can sanity-check the
|
|
476
|
+
// resolved target before any files are written (AC #4).
|
|
477
|
+
const targetResult = resolveInstallTarget({
|
|
478
|
+
cwd: process.cwd(),
|
|
479
|
+
workspace: typeof opts.workspace === 'string' ? opts.workspace : undefined,
|
|
480
|
+
configDirName,
|
|
481
|
+
// --add is the extension path: the root's .ai-sdlc/ already exists by
|
|
482
|
+
// design, so the "already installed" nesting check must be suppressed.
|
|
483
|
+
skipExistingCheck: !!opts.add,
|
|
484
|
+
});
|
|
485
|
+
if (targetResult.error) {
|
|
486
|
+
console.error(`Error: ${targetResult.error}`);
|
|
487
|
+
process.exitCode = 1;
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
const projectDir = targetResult.installDir;
|
|
491
|
+
if (targetResult.resolved) {
|
|
492
|
+
console.log(`Resolved install target: ${projectDir}`);
|
|
493
|
+
console.log('');
|
|
494
|
+
}
|
|
315
495
|
const tierInput = (opts.role ?? 'coding');
|
|
316
496
|
if (!AGENT_ROLE_TIERS.includes(tierInput)) {
|
|
317
497
|
console.error(`Error: invalid --role value '${tierInput}'. Expected one of: ${AGENT_ROLE_TIERS.join(', ')}.`);
|
|
@@ -320,6 +500,76 @@ export const initCommand = new Command('init')
|
|
|
320
500
|
}
|
|
321
501
|
const tier = tierInput;
|
|
322
502
|
const cursorOptIn = !!opts.cursor;
|
|
503
|
+
// ── Validate --add early so we error before doing any work. ────────
|
|
504
|
+
const addCheck = validateAddArg(opts.add);
|
|
505
|
+
if (!addCheck.ok) {
|
|
506
|
+
console.error(`Error: ${addCheck.error}`);
|
|
507
|
+
process.exitCode = 1;
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
const flags = buildWizardFlags(opts);
|
|
511
|
+
// ── --add souls path (RFC-0009 Phase 2.2) ─────────────────────────
|
|
512
|
+
// Scaffold per-soul DSB templates for a Tessellated Platform.
|
|
513
|
+
// Usage: ai-sdlc init --add souls --souls soul-a,soul-b,soul-c
|
|
514
|
+
if (addCheck.value === 'souls') {
|
|
515
|
+
const soulsRaw = typeof opts.souls === 'string' ? opts.souls : '';
|
|
516
|
+
const platformDsbName = typeof opts.platformDsb === 'string' ? opts.platformDsb : 'platform-dsb';
|
|
517
|
+
const soulSlugs = soulsRaw
|
|
518
|
+
.split(',')
|
|
519
|
+
.map((slug) => slug.trim())
|
|
520
|
+
.filter((slug) => slug.length > 0);
|
|
521
|
+
if (soulSlugs.length === 0) {
|
|
522
|
+
console.error('Error: --add souls requires --souls <slug1,slug2,...> with at least one soul slug.');
|
|
523
|
+
process.exitCode = 1;
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
// Validate soul slugs (must match ^[a-z0-9-]+$ per RFC-0009 §5.2 soulId pattern)
|
|
527
|
+
const invalidSlugs = soulSlugs.filter((slug) => !/^[a-z0-9-]+$/.test(slug));
|
|
528
|
+
if (invalidSlugs.length > 0) {
|
|
529
|
+
console.error(`Error: invalid soul slug(s): ${invalidSlugs.join(', ')}. Slugs must match ^[a-z0-9-]+$.`);
|
|
530
|
+
process.exitCode = 1;
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
const aiSdlcDir = join(projectDir, configDirName);
|
|
534
|
+
console.log(`Scaffolding per-soul DSB templates in ${aiSdlcDir}/souls/:`);
|
|
535
|
+
console.log('');
|
|
536
|
+
scaffoldSoulDsbs(soulSlugs, aiSdlcDir, {
|
|
537
|
+
platformDsbName,
|
|
538
|
+
dryRun,
|
|
539
|
+
prefix: ' ',
|
|
540
|
+
});
|
|
541
|
+
console.log('');
|
|
542
|
+
console.log(`Done. Edit each .ai-sdlc/souls/<slug>/design-system-binding.yaml to`);
|
|
543
|
+
console.log(`configure soul-specific design system bindings.`);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
// ── --add path: extend an already-initialized repo ────────────────
|
|
547
|
+
// AC #7: skip the "always-scaffold-baseline" path entirely; the
|
|
548
|
+
// wizard dispatcher's `--add` branch knows to write only the chosen
|
|
549
|
+
// feature's templates (no pipeline.yaml, no MCP setup, no workspace
|
|
550
|
+
// detection). This is the safe re-run path on a repo that already
|
|
551
|
+
// ran `ai-sdlc init` once.
|
|
552
|
+
if (flags.add) {
|
|
553
|
+
const adapters = buildProductionAdapters();
|
|
554
|
+
const selection = await resolveFeatureSelection(flags, adapters);
|
|
555
|
+
console.log(`Extending AI-SDLC config with --add ${flags.add}:`);
|
|
556
|
+
console.log('');
|
|
557
|
+
const result = await applyFeatureSelection(projectDir, selection, flags, adapters);
|
|
558
|
+
renderNextSteps(selection, result, adapters);
|
|
559
|
+
// Reviewer feedback (round 2, suggestion #5): when the operator
|
|
560
|
+
// explicitly requested branch-protection via a non-interactive flag
|
|
561
|
+
// (--add branch-protection here; --yes / --with-branch-protection
|
|
562
|
+
// in runWizardStage) and the apply failed (gh missing, not
|
|
563
|
+
// authenticated, etc.), surface a non-zero exit so CI scripts can
|
|
564
|
+
// detect failure instead of seeing the silent log line.
|
|
565
|
+
if (flags.add === 'branch-protection' &&
|
|
566
|
+
result.branchProtection &&
|
|
567
|
+
!result.branchProtection.applied &&
|
|
568
|
+
result.branchProtection.error) {
|
|
569
|
+
process.exitCode = 1;
|
|
570
|
+
}
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
323
573
|
// ── version provenance (AC #1) ────────────────────────────────────
|
|
324
574
|
const versions = resolveVersions({ workDir: projectDir });
|
|
325
575
|
console.log(formatVersionBlock(versions));
|
|
@@ -377,6 +627,12 @@ export const initCommand = new Command('init')
|
|
|
377
627
|
}
|
|
378
628
|
}
|
|
379
629
|
console.log(`\nAI-SDLC workspace initialized in ${projectDir}/`);
|
|
630
|
+
// ── AISDLC-143 wizard (workspace root) ────────────────────────
|
|
631
|
+
// The wizard runs ONCE at the workspace root: the baseline gate
|
|
632
|
+
// workflow + per-feature workflows live at `<workspace-root>/.github/`,
|
|
633
|
+
// not in each child repo. Children share the same CI from the
|
|
634
|
+
// root since GHA workflows always live at the repo root anyway.
|
|
635
|
+
await runWizardStage(projectDir, flags);
|
|
380
636
|
console.log(`Run 'ai-sdlc health' to verify your configuration.`);
|
|
381
637
|
}
|
|
382
638
|
else {
|
|
@@ -399,7 +655,50 @@ export const initCommand = new Command('init')
|
|
|
399
655
|
}
|
|
400
656
|
}
|
|
401
657
|
console.log(`\nAI-SDLC config initialized in ${join(projectDir, configDirName)}/`);
|
|
658
|
+
// ── AISDLC-143 wizard (single-repo) ──────────────────────────
|
|
659
|
+
await runWizardStage(projectDir, flags);
|
|
402
660
|
console.log(`Run 'ai-sdlc health' to verify your configuration.`);
|
|
403
661
|
}
|
|
404
662
|
});
|
|
663
|
+
/**
|
|
664
|
+
* Run the AISDLC-143 wizard stage: prompt the user (or short-circuit on
|
|
665
|
+
* --yes / --with-X), apply the chosen feature templates, append the
|
|
666
|
+
* CLAUDE.md pointer, and render the "next steps" summary.
|
|
667
|
+
*
|
|
668
|
+
* Pulled out of the inline action body so both the single-repo and
|
|
669
|
+
* workspace-root branches share the same wiring. Adapters are built
|
|
670
|
+
* once here (production = real disk writes; tests inject stubs by
|
|
671
|
+
* calling `applyFeatureSelection`/`renderNextSteps` directly).
|
|
672
|
+
*/
|
|
673
|
+
async function runWizardStage(projectDir, flags) {
|
|
674
|
+
const adapters = buildProductionAdapters();
|
|
675
|
+
console.log('');
|
|
676
|
+
console.log('━━━ Compliance posture ━━━');
|
|
677
|
+
console.log('');
|
|
678
|
+
// RFC-0022 §7 / AISDLC-324: compliance step runs BEFORE gate-config feature prompts
|
|
679
|
+
await runComplianceStep(projectDir, flags, adapters);
|
|
680
|
+
console.log('');
|
|
681
|
+
console.log('━━━ Feature wizard ━━━');
|
|
682
|
+
console.log('');
|
|
683
|
+
const selection = await resolveFeatureSelection(flags, adapters);
|
|
684
|
+
console.log('');
|
|
685
|
+
console.log('Scaffolding selected features:');
|
|
686
|
+
const result = await applyFeatureSelection(projectDir, selection, flags, adapters);
|
|
687
|
+
ensureClaudeMdPointer(projectDir, adapters, flags.dryRun);
|
|
688
|
+
renderNextSteps(selection, result, adapters);
|
|
689
|
+
// Reviewer feedback (round 2, suggestion #5): when branch-protection
|
|
690
|
+
// was non-interactively requested (--with-branch-protection or --yes,
|
|
691
|
+
// both used by CI scripts) and the apply failed (gh missing, not
|
|
692
|
+
// authenticated, etc.), surface a non-zero exit so CI can detect
|
|
693
|
+
// failure instead of seeing the silent log line. Interactive prompt
|
|
694
|
+
// answers don't trip this — the human already saw the error and can
|
|
695
|
+
// re-run `ai-sdlc init --add branch-protection` themselves.
|
|
696
|
+
const branchProtectionRequestedNonInteractively = flags.yes || flags.withBranchProtection;
|
|
697
|
+
if (branchProtectionRequestedNonInteractively &&
|
|
698
|
+
result.branchProtection &&
|
|
699
|
+
!result.branchProtection.applied &&
|
|
700
|
+
result.branchProtection.error) {
|
|
701
|
+
process.exitCode = 1;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
405
704
|
//# sourceMappingURL=init.js.map
|
package/dist/cli/commands/run.js
CHANGED
|
@@ -8,6 +8,8 @@ export const runCommand = new Command('run')
|
|
|
8
8
|
.description('Run the AI-SDLC pipeline for a specific issue')
|
|
9
9
|
.requiredOption('-i, --issue <id>', 'Issue ID to process')
|
|
10
10
|
.option('--state <path>', 'SQLite state database path')
|
|
11
|
+
.option('--runner <name>', 'Select a registered runner by name (e.g. claude-code, copilot, cursor). ' +
|
|
12
|
+
'Fails fast when the name is not registered — no silent fallback.')
|
|
11
13
|
.action(async (opts, cmd) => {
|
|
12
14
|
const globalOpts = cmd.parent?.opts() ?? {};
|
|
13
15
|
const format = globalOpts.format ?? 'table';
|
|
@@ -16,7 +18,13 @@ export const runCommand = new Command('run')
|
|
|
16
18
|
statePath: opts.state,
|
|
17
19
|
});
|
|
18
20
|
try {
|
|
19
|
-
|
|
21
|
+
// opts.runner maps to ExecuteOptions.runnerName; resolveRunner() in execute.ts
|
|
22
|
+
// applies the full precedence chain and throws on unknown names.
|
|
23
|
+
// Only pass overrides when a flag is explicitly set to avoid breaking callers
|
|
24
|
+
// that match on the exact argument list (e.g. existing tests).
|
|
25
|
+
const result = opts.runner
|
|
26
|
+
? await orchestrator.run(opts.issue, { runnerName: opts.runner })
|
|
27
|
+
: await orchestrator.run(opts.issue);
|
|
20
28
|
console.log(formatOutput(format, {
|
|
21
29
|
type: 'run',
|
|
22
30
|
issueId: opts.issue,
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -16,4 +16,15 @@ import { type VersionTriple } from './versions.js';
|
|
|
16
16
|
* exact factory at startup.
|
|
17
17
|
*/
|
|
18
18
|
export declare function buildProgram(versions: VersionTriple): Command;
|
|
19
|
+
/**
|
|
20
|
+
* Determine whether this module is being run as the CLI entry point,
|
|
21
|
+
* correctly handling npm bin symlinks.
|
|
22
|
+
*
|
|
23
|
+
* Exported for unit-testing; production callers should use the module-level
|
|
24
|
+
* `isMainEntry` constant instead.
|
|
25
|
+
*
|
|
26
|
+
* @param moduleUrl - The `import.meta.url` of the caller module.
|
|
27
|
+
* @param argv1 - `process.argv[1]` (the script path Node was given).
|
|
28
|
+
*/
|
|
29
|
+
export declare function computeIsMainEntry(moduleUrl: string, argv1: string | undefined): boolean;
|
|
19
30
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/cli/index.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* unknown-subcommand hint that points at the upgrade flow when version
|
|
8
8
|
* drift is detected.
|
|
9
9
|
*/
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import { realpathSync } from 'fs';
|
|
10
12
|
import { Command } from 'commander';
|
|
11
13
|
import { initCommand } from './commands/init.js';
|
|
12
14
|
import { runCommand } from './commands/run.js';
|
|
@@ -83,14 +85,46 @@ export function buildProgram(versions) {
|
|
|
83
85
|
// Bin entry: only run argv parsing when invoked as a script, not when the
|
|
84
86
|
// module is imported by tests.
|
|
85
87
|
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
// IMPORTANT: Node does NOT resolve symlinks for `process.argv[1]`. When
|
|
89
|
+
// the CLI is installed globally via npm (`npm install -g`), the bin path
|
|
90
|
+
// is a symlink (e.g. `/usr/local/bin/ai-sdlc`) that points to the real
|
|
91
|
+
// `dist/cli/index.js`. The old `endsWith('cli/index.js')` check failed
|
|
92
|
+
// for that case because argv[1] held the symlink path, not the target.
|
|
93
|
+
//
|
|
94
|
+
// Fix: compare `import.meta.url` (always the real module's file URL) to
|
|
95
|
+
// `pathToFileURL(realpathSync(argv[1]))` (which resolves the symlink).
|
|
96
|
+
// When imported by Vitest, argv[1] points at the test runner, so
|
|
97
|
+
// `realpathSync` still returns a path that differs from import.meta.url
|
|
98
|
+
// and the guard correctly returns false.
|
|
99
|
+
/**
|
|
100
|
+
* Determine whether this module is being run as the CLI entry point,
|
|
101
|
+
* correctly handling npm bin symlinks.
|
|
102
|
+
*
|
|
103
|
+
* Exported for unit-testing; production callers should use the module-level
|
|
104
|
+
* `isMainEntry` constant instead.
|
|
105
|
+
*
|
|
106
|
+
* @param moduleUrl - The `import.meta.url` of the caller module.
|
|
107
|
+
* @param argv1 - `process.argv[1]` (the script path Node was given).
|
|
108
|
+
*/
|
|
109
|
+
export function computeIsMainEntry(moduleUrl, argv1) {
|
|
110
|
+
if (!argv1)
|
|
111
|
+
return false;
|
|
112
|
+
try {
|
|
113
|
+
// Resolve symlinks on BOTH sides before comparing, so that:
|
|
114
|
+
// 1. npm bin symlinks in argv1 resolve to the real dist/cli/index.js.
|
|
115
|
+
// 2. OS-level dir symlinks (e.g. macOS /tmp → /private/tmp) don't cause
|
|
116
|
+
// spurious mismatches when moduleUrl itself was built from an
|
|
117
|
+
// unresolved path.
|
|
118
|
+
const realArgv1 = realpathSync(argv1);
|
|
119
|
+
const realModule = realpathSync(fileURLToPath(moduleUrl));
|
|
120
|
+
return realArgv1 === realModule;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// realpathSync / fileURLToPath can throw — treat as "not main".
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const isMainEntry = computeIsMainEntry(import.meta.url, process.argv[1]);
|
|
94
128
|
if (isMainEntry) {
|
|
95
129
|
const program = buildProgram(resolveVersions());
|
|
96
130
|
program.parse();
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regime → DerivedGates composer (RFC-0022 §9 Phase 2).
|
|
3
|
+
*
|
|
4
|
+
* Reads the CompliancePosture's declared regime list, looks up each regime's
|
|
5
|
+
* DerivedGates values from spec/compliance/regime-mappings.yaml, composes
|
|
6
|
+
* them using "tightest-wins" semantics (RFC-0022 §6), applies adopter
|
|
7
|
+
* regimeOverrides per-regime (OQ-1), and finally applies operator-level
|
|
8
|
+
* derivedGates overrides last.
|
|
9
|
+
*
|
|
10
|
+
* Key design decisions:
|
|
11
|
+
* - YAML mapping file is read at call time (not module load) so tests can
|
|
12
|
+
* supply a custom mappings path without patching module state.
|
|
13
|
+
* - Tightest-wins is axis-specific: ordinal enums max, boolean OR, numeric max.
|
|
14
|
+
* - UnknownRegime is thrown when a declared regime ID has no entry in the
|
|
15
|
+
* mapping table (Phase 2 activation of the placeholder from Phase 1 errors.ts).
|
|
16
|
+
* - Adopter regimeOverrides (OQ-1) let per-regime control values be adjusted
|
|
17
|
+
* before the operator-override pass, enabling bespoke auditor interpretations
|
|
18
|
+
* without changing the canonical mapping table.
|
|
19
|
+
* - Operator derivedGates overrides (validated by the loader) always win last.
|
|
20
|
+
*/
|
|
21
|
+
import type { CompliancePosture, DerivedGates } from './types.js';
|
|
22
|
+
/**
|
|
23
|
+
* Options for `composePostureDerivedGates`.
|
|
24
|
+
*/
|
|
25
|
+
export interface ComposerOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Absolute path to the regime-mappings.yaml file.
|
|
28
|
+
* Defaults to resolving `spec/compliance/regime-mappings.yaml` from the
|
|
29
|
+
* repository root (detected by walking up from __dirname).
|
|
30
|
+
*
|
|
31
|
+
* Override in tests to supply fixture mapping files.
|
|
32
|
+
*/
|
|
33
|
+
mappingsPath?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Root directory for finding the default mappings file.
|
|
36
|
+
* Defaults to walking up from the module file location.
|
|
37
|
+
*/
|
|
38
|
+
repoRoot?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The result of composing a CompliancePosture's regimes into DerivedGates.
|
|
42
|
+
*/
|
|
43
|
+
export interface CompositionResult {
|
|
44
|
+
/**
|
|
45
|
+
* The composed DerivedGates after regime composition + overrides.
|
|
46
|
+
*/
|
|
47
|
+
derivedGates: DerivedGates;
|
|
48
|
+
/**
|
|
49
|
+
* List of regime IDs that were composed.
|
|
50
|
+
* Empty means baseline was used (no regimes declared).
|
|
51
|
+
*/
|
|
52
|
+
composedRegimes: string[];
|
|
53
|
+
/**
|
|
54
|
+
* Regime IDs that had adopter regimeOverrides applied.
|
|
55
|
+
*/
|
|
56
|
+
regimesWithAdopterOverrides: string[];
|
|
57
|
+
/**
|
|
58
|
+
* Gate fields that were overridden by the operator-level derivedGates.
|
|
59
|
+
*/
|
|
60
|
+
operatorOverriddenFields: string[];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Compose a loaded CompliancePosture into a concrete DerivedGates value.
|
|
64
|
+
*
|
|
65
|
+
* Algorithm (RFC-0022 §6):
|
|
66
|
+
* 1. Start from BASELINE_DERIVED_GATES.
|
|
67
|
+
* 2. For each declared regime, look up its entry in regime-mappings.yaml.
|
|
68
|
+
* Throw UnknownRegime if not found.
|
|
69
|
+
* 3. Apply adopter regimeOverrides (per-regime) before composition (OQ-1).
|
|
70
|
+
* 4. Merge each regime's gates into the accumulator via tightest-wins.
|
|
71
|
+
* 5. Apply operator derivedGates overrides last — they always win.
|
|
72
|
+
*
|
|
73
|
+
* @param posture - A single CompliancePosture (element from the loader's list).
|
|
74
|
+
* @param options - Optional override for the mappings file path.
|
|
75
|
+
* @returns CompositionResult with the resolved DerivedGates.
|
|
76
|
+
* @throws UnknownRegime if any declared regime ID is not in the mapping table.
|
|
77
|
+
*/
|
|
78
|
+
export declare function composePostureDerivedGates(posture: CompliancePosture, options?: ComposerOptions): CompositionResult;
|
|
79
|
+
//# sourceMappingURL=composer.d.ts.map
|