@ai-sdlc/orchestrator 0.10.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/adapters.d.ts +42 -3
- package/dist/adapters.js +133 -3
- package/dist/admission-composite.d.ts +112 -1
- package/dist/admission-composite.js +85 -4
- package/dist/admission-enrichment.d.ts +12 -3
- package/dist/admission-enrichment.js +64 -13
- package/dist/admission-score.d.ts +30 -0
- package/dist/admission-score.js +4 -1
- 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/init-features.d.ts +301 -2
- package/dist/cli/commands/init-features.js +634 -7
- package/dist/cli/commands/init-templates.d.ts +198 -1
- package/dist/cli/commands/init-templates.js +943 -1
- package/dist/cli/commands/init.d.ts +45 -0
- package/dist/cli/commands/init.js +147 -5
- 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/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 +171 -30
- 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/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 +173 -13
- package/dist/runtime/attestations.js +252 -40
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +1 -1
- 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 +10 -8
|
@@ -44,6 +44,51 @@ export type AgentRoleTier = 'coding' | 'research' | 'meta';
|
|
|
44
44
|
export declare const AGENT_ROLE_TIERS: readonly AgentRoleTier[];
|
|
45
45
|
/** Resolve the agent-role.yaml template for a given tier. Default = `coding`. */
|
|
46
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;
|
|
47
92
|
/**
|
|
48
93
|
* Build the WizardFlags struct from Commander's parsed options. Pulled
|
|
49
94
|
* out into a function so tests can drive the wizard pipeline directly
|
|
@@ -34,7 +34,8 @@ import { detectAgentsDetailed, installMcpServer } from './mcp-setup.js';
|
|
|
34
34
|
import { detectWorkspace, generateWorkspaceYaml } from './workspace-detect.js';
|
|
35
35
|
import { detectGitRemote, applyRemoteToPipelineYaml } from './git-remote.js';
|
|
36
36
|
import { resolveVersions, formatVersionBlock } from '../versions.js';
|
|
37
|
-
import { applyFeatureSelection, buildProductionAdapters, ensureClaudeMdPointer, renderNextSteps, resolveFeatureSelection, } from './init-features.js';
|
|
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';
|
|
38
39
|
export const PIPELINE_YAML = `apiVersion: ai-sdlc.io/v1alpha1
|
|
39
40
|
kind: Pipeline
|
|
40
41
|
metadata:
|
|
@@ -260,6 +261,51 @@ spec:
|
|
|
260
261
|
action: demote-one-level
|
|
261
262
|
cooldown: 2w
|
|
262
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
|
+
}
|
|
263
309
|
const GITIGNORE_PATHS = ['.ai-sdlc/state.db', '.ai-sdlc/state/', '.ai-sdlc/audit.jsonl'];
|
|
264
310
|
/** Ensure .gitignore includes AI-SDLC runtime artifact entries. */
|
|
265
311
|
function ensureGitignore(projectDir, dryRun, prefix = '') {
|
|
@@ -287,6 +333,16 @@ function initProject(projectDir, configDirName, dryRun, prefix = '', options = {
|
|
|
287
333
|
{ name: 'agent-role.yaml', content: getAgentRoleYaml(options.tier) },
|
|
288
334
|
{ name: 'quality-gate.yaml', content: QUALITY_GATE_YAML },
|
|
289
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 },
|
|
290
346
|
];
|
|
291
347
|
if (dryRun) {
|
|
292
348
|
console.log(`${prefix}Would create ${configDir}/`);
|
|
@@ -344,7 +400,9 @@ export function buildWizardFlags(opts) {
|
|
|
344
400
|
if (addRaw === 'dor' ||
|
|
345
401
|
addRaw === 'attestation' ||
|
|
346
402
|
addRaw === 'classifier' ||
|
|
347
|
-
addRaw === 'branch-protection'
|
|
403
|
+
addRaw === 'branch-protection' ||
|
|
404
|
+
addRaw === 'workflows' ||
|
|
405
|
+
addRaw === 'signal-ingestion') {
|
|
348
406
|
add = addRaw;
|
|
349
407
|
}
|
|
350
408
|
return {
|
|
@@ -353,8 +411,12 @@ export function buildWizardFlags(opts) {
|
|
|
353
411
|
withAttestation: !!opts.withAttestation,
|
|
354
412
|
withClassifier: !!opts.withClassifier,
|
|
355
413
|
withBranchProtection: !!opts.withBranchProtection,
|
|
414
|
+
withWorkflows: !!opts.withWorkflows,
|
|
415
|
+
withSignalIngestion: !!opts.withSignalIngestion,
|
|
356
416
|
add,
|
|
357
417
|
dryRun: !!opts.dryRun,
|
|
418
|
+
workspace: typeof opts.workspace === 'string' ? opts.workspace : undefined,
|
|
419
|
+
force: !!opts.force,
|
|
358
420
|
};
|
|
359
421
|
}
|
|
360
422
|
/** Validate a `--add` arg and return either the normalized value or null+error. */
|
|
@@ -363,13 +425,22 @@ function validateAddArg(addRaw) {
|
|
|
363
425
|
return { ok: true };
|
|
364
426
|
if (typeof addRaw !== 'string')
|
|
365
427
|
return { ok: false, error: `--add must be a string` };
|
|
366
|
-
const allowed = [
|
|
428
|
+
const allowed = [
|
|
429
|
+
'dor',
|
|
430
|
+
'attestation',
|
|
431
|
+
'classifier',
|
|
432
|
+
'branch-protection',
|
|
433
|
+
'workflows',
|
|
434
|
+
'signal-ingestion',
|
|
435
|
+
'souls',
|
|
436
|
+
];
|
|
367
437
|
if (!allowed.includes(addRaw)) {
|
|
368
438
|
return {
|
|
369
439
|
ok: false,
|
|
370
440
|
error: `--add: unknown feature '${addRaw}'. Expected one of: ${allowed.join(', ')}.`,
|
|
371
441
|
};
|
|
372
442
|
}
|
|
443
|
+
// 'souls' is handled separately in the action; cast the rest to WizardFlags['add']
|
|
373
444
|
return { ok: true, value: addRaw };
|
|
374
445
|
}
|
|
375
446
|
export const initCommand = new Command('init')
|
|
@@ -385,11 +456,42 @@ export const initCommand = new Command('init')
|
|
|
385
456
|
.option('--with-attestation', 'Scaffold attestation infrastructure (audit-only)')
|
|
386
457
|
.option('--with-classifier', 'Scaffold review classifier config stub')
|
|
387
458
|
.option('--with-branch-protection', 'Apply recommended branch-protection rule to main (requires gh)')
|
|
388
|
-
|
|
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')
|
|
389
469
|
.action(async (opts) => {
|
|
390
|
-
const projectDir = process.cwd();
|
|
391
470
|
const configDirName = opts.dir ?? '.ai-sdlc';
|
|
392
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
|
+
}
|
|
393
495
|
const tierInput = (opts.role ?? 'coding');
|
|
394
496
|
if (!AGENT_ROLE_TIERS.includes(tierInput)) {
|
|
395
497
|
console.error(`Error: invalid --role value '${tierInput}'. Expected one of: ${AGENT_ROLE_TIERS.join(', ')}.`);
|
|
@@ -406,6 +508,41 @@ export const initCommand = new Command('init')
|
|
|
406
508
|
return;
|
|
407
509
|
}
|
|
408
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
|
+
}
|
|
409
546
|
// ── --add path: extend an already-initialized repo ────────────────
|
|
410
547
|
// AC #7: skip the "always-scaffold-baseline" path entirely; the
|
|
411
548
|
// wizard dispatcher's `--add` branch knows to write only the chosen
|
|
@@ -536,6 +673,11 @@ export const initCommand = new Command('init')
|
|
|
536
673
|
async function runWizardStage(projectDir, flags) {
|
|
537
674
|
const adapters = buildProductionAdapters();
|
|
538
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('');
|
|
539
681
|
console.log('━━━ Feature wizard ━━━');
|
|
540
682
|
console.log('');
|
|
541
683
|
const selection = await resolveFeatureSelection(flags, adapters);
|
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
|