@paths.design/caws-cli 9.3.2 → 10.0.1
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/README.md +58 -27
- package/dist/commands/archive.js +67 -28
- package/dist/commands/burnup.js +20 -11
- package/dist/commands/diagnose.js +34 -22
- package/dist/commands/evaluate.js +27 -15
- package/dist/commands/gates.js +122 -0
- package/dist/commands/init.js +143 -15
- package/dist/commands/iterate.js +77 -4
- package/dist/commands/parallel.js +4 -0
- package/dist/commands/plan.js +9 -19
- package/dist/commands/provenance.js +53 -17
- package/dist/commands/quality-monitor.js +64 -45
- package/dist/commands/sidecar.js +71 -0
- package/dist/commands/specs.js +233 -44
- package/dist/commands/status.js +113 -9
- package/dist/commands/tutorial.js +10 -9
- package/dist/commands/validate.js +49 -6
- package/dist/commands/verify-acs.js +35 -78
- package/dist/commands/waivers.js +69 -12
- package/dist/commands/worktree.js +50 -25
- package/dist/error-handler.js +2 -13
- package/dist/gates/budget-limit.js +116 -0
- package/dist/gates/feedback.js +260 -0
- package/dist/gates/format.js +179 -0
- package/dist/gates/god-object.js +117 -0
- package/dist/gates/pipeline.js +167 -0
- package/dist/gates/scope-boundary.js +93 -0
- package/dist/gates/spec-completeness.js +102 -0
- package/dist/gates/todo-detection.js +205 -0
- package/dist/index.js +130 -151
- package/dist/parallel/parallel-manager.js +3 -3
- package/dist/policy/PolicyManager.js +42 -10
- package/dist/scaffold/claude-hooks.js +24 -1
- package/dist/scaffold/git-hooks.js +45 -102
- package/dist/scaffold/index.js +4 -3
- package/dist/session/session-manager.js +71 -14
- package/dist/sidecars/index.js +33 -0
- package/dist/sidecars/listeners.js +40 -0
- package/dist/sidecars/provenance-summary.js +238 -0
- package/dist/sidecars/quality-gaps.js +258 -0
- package/dist/sidecars/schema.js +149 -0
- package/dist/sidecars/spec-drift.js +151 -0
- package/dist/sidecars/waiver-draft.js +176 -0
- package/dist/templates/.caws/schemas/policy.schema.json +50 -0
- package/dist/templates/.caws/schemas/waivers.schema.json +30 -24
- package/dist/templates/.caws/schemas/working-spec.schema.json +51 -8
- package/dist/templates/.caws/schemas/worktrees.schema.json +3 -1
- package/dist/templates/.caws/templates/working-spec.template.yml +7 -3
- package/dist/templates/.claude/hooks/audit.sh +0 -0
- package/dist/templates/.claude/hooks/block-dangerous.sh +52 -11
- package/dist/templates/.claude/hooks/classify_command.py +592 -0
- package/dist/templates/.claude/hooks/doc-frontmatter-check.sh +173 -0
- package/dist/templates/.claude/hooks/quality-check.sh +23 -10
- package/dist/templates/.claude/hooks/scope-guard.sh +34 -32
- package/dist/templates/.claude/hooks/session-caws-status.sh +2 -2
- package/dist/templates/.claude/hooks/session-log.sh +76 -3
- package/dist/templates/.claude/hooks/stop-worktree-check.sh +1 -1
- package/dist/templates/.claude/hooks/test_classify_command.py +370 -0
- package/dist/templates/.claude/hooks/test_wrapper_smoke.sh +96 -0
- package/dist/templates/.claude/hooks/worktree-guard.sh +2 -2
- package/dist/templates/.claude/hooks/worktree-write-guard.sh +1 -1
- package/dist/templates/.claude/settings.json +26 -0
- package/dist/templates/.cursor/hooks/caws-quality-check.sh +4 -4
- package/dist/templates/.cursor/hooks/caws-scope-guard.sh +1 -1
- package/dist/templates/.cursor/hooks/session-log.sh +924 -0
- package/dist/templates/.cursor/hooks.json +25 -0
- package/dist/templates/.cursor/rules/02-quality-gates.mdc +3 -5
- package/dist/templates/.cursor/rules/10-documentation-quality-standards.mdc +6 -11
- package/dist/templates/.cursor/rules/11-scope-management-waivers.mdc +14 -18
- package/dist/templates/.cursor/rules/12-implementation-completeness.mdc +4 -4
- package/dist/templates/.cursor/rules/13-language-agnostic-standards.mdc +3 -13
- package/dist/templates/.github/copilot-instructions.md +5 -5
- package/dist/templates/.idea/runConfigurations/CAWS_Evaluate.xml +1 -1
- package/dist/templates/.junie/guidelines.md +2 -2
- package/dist/templates/.vscode/settings.json +3 -1
- package/dist/templates/.windsurf/rules/caws-quality-standards.md +2 -2
- package/dist/templates/.windsurf/workflows/caws-guided-development.md +3 -3
- package/dist/templates/CLAUDE.md +43 -8
- package/dist/templates/agents.md +29 -9
- package/dist/templates/docs/README.md +8 -7
- package/dist/templates/scripts/new_feature.sh +80 -0
- package/dist/test-analysis.js +43 -30
- package/dist/tool-loader.js +1 -1
- package/dist/utils/agent-session.js +202 -0
- package/dist/utils/detection.js +8 -2
- package/dist/utils/finalization.js +7 -6
- package/dist/utils/gitignore-updater.js +3 -0
- package/dist/utils/lifecycle-events.js +94 -0
- package/dist/utils/quality-gates-utils.js +29 -44
- package/dist/utils/schema-validator.js +42 -0
- package/dist/utils/spec-resolver.js +93 -21
- package/dist/utils/working-state.js +505 -0
- package/dist/validation/spec-validation.js +92 -22
- package/dist/waivers-manager.js +60 -6
- package/dist/worktree/worktree-manager.js +390 -93
- package/package.json +6 -6
- package/templates/.caws/schemas/policy.schema.json +50 -0
- package/templates/.caws/schemas/waivers.schema.json +30 -24
- package/templates/.caws/schemas/working-spec.schema.json +51 -8
- package/templates/.caws/schemas/worktrees.schema.json +3 -1
- package/templates/.caws/templates/working-spec.template.yml +7 -3
- package/templates/.claude/hooks/block-dangerous.sh +52 -11
- package/templates/.claude/hooks/classify_command.py +592 -0
- package/templates/.claude/hooks/doc-frontmatter-check.sh +173 -0
- package/templates/.claude/hooks/quality-check.sh +23 -10
- package/templates/.claude/hooks/scope-guard.sh +34 -32
- package/templates/.claude/hooks/session-caws-status.sh +2 -2
- package/templates/.claude/hooks/session-log.sh +76 -3
- package/templates/.claude/hooks/stop-worktree-check.sh +1 -1
- package/templates/.claude/hooks/test_classify_command.py +370 -0
- package/templates/.claude/hooks/test_wrapper_smoke.sh +96 -0
- package/templates/.claude/hooks/worktree-guard.sh +2 -2
- package/templates/.claude/hooks/worktree-write-guard.sh +1 -1
- package/templates/.claude/settings.json +26 -0
- package/templates/.cursor/hooks/caws-quality-check.sh +4 -4
- package/templates/.cursor/hooks/caws-scope-guard.sh +1 -1
- package/templates/.cursor/hooks/session-log.sh +924 -0
- package/templates/.cursor/hooks.json +25 -0
- package/templates/.cursor/rules/02-quality-gates.mdc +3 -5
- package/templates/.cursor/rules/10-documentation-quality-standards.mdc +6 -11
- package/templates/.cursor/rules/11-scope-management-waivers.mdc +14 -18
- package/templates/.cursor/rules/12-implementation-completeness.mdc +4 -4
- package/templates/.cursor/rules/13-language-agnostic-standards.mdc +3 -13
- package/templates/.github/copilot-instructions.md +5 -5
- package/templates/.idea/runConfigurations/CAWS_Evaluate.xml +1 -1
- package/templates/.junie/guidelines.md +2 -2
- package/templates/.vscode/settings.json +3 -1
- package/templates/.windsurf/rules/caws-quality-standards.md +2 -2
- package/templates/.windsurf/workflows/caws-guided-development.md +3 -3
- package/templates/CLAUDE.md +43 -8
- package/templates/{AGENTS.md → agents.md} +29 -9
- package/templates/docs/README.md +8 -7
- package/templates/scripts/new_feature.sh +80 -0
- package/dist/budget-derivation.d.ts +0 -74
- package/dist/budget-derivation.d.ts.map +0 -1
- package/dist/cicd-optimizer.d.ts +0 -142
- package/dist/cicd-optimizer.d.ts.map +0 -1
- package/dist/commands/archive.d.ts +0 -51
- package/dist/commands/archive.d.ts.map +0 -1
- package/dist/commands/burnup.d.ts +0 -6
- package/dist/commands/burnup.d.ts.map +0 -1
- package/dist/commands/diagnose.d.ts +0 -52
- package/dist/commands/diagnose.d.ts.map +0 -1
- package/dist/commands/evaluate.d.ts +0 -8
- package/dist/commands/evaluate.d.ts.map +0 -1
- package/dist/commands/init.d.ts +0 -5
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/iterate.d.ts +0 -8
- package/dist/commands/iterate.d.ts.map +0 -1
- package/dist/commands/mode.d.ts +0 -25
- package/dist/commands/mode.d.ts.map +0 -1
- package/dist/commands/parallel.d.ts +0 -7
- package/dist/commands/parallel.d.ts.map +0 -1
- package/dist/commands/plan.d.ts +0 -49
- package/dist/commands/plan.d.ts.map +0 -1
- package/dist/commands/provenance.d.ts +0 -32
- package/dist/commands/provenance.d.ts.map +0 -1
- package/dist/commands/quality-gates.d.ts +0 -6
- package/dist/commands/quality-gates.d.ts.map +0 -1
- package/dist/commands/quality-gates.js +0 -444
- package/dist/commands/quality-monitor.d.ts +0 -17
- package/dist/commands/quality-monitor.d.ts.map +0 -1
- package/dist/commands/session.d.ts +0 -7
- package/dist/commands/session.d.ts.map +0 -1
- package/dist/commands/specs.d.ts +0 -77
- package/dist/commands/specs.d.ts.map +0 -1
- package/dist/commands/status.d.ts +0 -44
- package/dist/commands/status.d.ts.map +0 -1
- package/dist/commands/templates.d.ts +0 -74
- package/dist/commands/templates.d.ts.map +0 -1
- package/dist/commands/tool.d.ts +0 -13
- package/dist/commands/tool.d.ts.map +0 -1
- package/dist/commands/troubleshoot.d.ts +0 -8
- package/dist/commands/troubleshoot.d.ts.map +0 -1
- package/dist/commands/troubleshoot.js +0 -104
- package/dist/commands/tutorial.d.ts +0 -55
- package/dist/commands/tutorial.d.ts.map +0 -1
- package/dist/commands/validate.d.ts +0 -15
- package/dist/commands/validate.d.ts.map +0 -1
- package/dist/commands/waivers.d.ts +0 -8
- package/dist/commands/waivers.d.ts.map +0 -1
- package/dist/commands/workflow.d.ts +0 -85
- package/dist/commands/workflow.d.ts.map +0 -1
- package/dist/commands/worktree.d.ts +0 -7
- package/dist/commands/worktree.d.ts.map +0 -1
- package/dist/config/index.d.ts +0 -29
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/lite-scope.d.ts +0 -33
- package/dist/config/lite-scope.d.ts.map +0 -1
- package/dist/config/modes.d.ts +0 -264
- package/dist/config/modes.d.ts.map +0 -1
- package/dist/constants/spec-types.d.ts +0 -93
- package/dist/constants/spec-types.d.ts.map +0 -1
- package/dist/error-handler.d.ts +0 -151
- package/dist/error-handler.d.ts.map +0 -1
- package/dist/generators/jest-config-generator.d.ts +0 -32
- package/dist/generators/jest-config-generator.d.ts.map +0 -1
- package/dist/generators/jest-config.d.ts +0 -32
- package/dist/generators/jest-config.d.ts.map +0 -1
- package/dist/generators/jest-config.js +0 -242
- package/dist/generators/working-spec.d.ts +0 -13
- package/dist/generators/working-spec.d.ts.map +0 -1
- package/dist/index-new.d.ts +0 -5
- package/dist/index-new.d.ts.map +0 -1
- package/dist/index-new.js +0 -317
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.backup +0 -4711
- package/dist/minimal-cli.d.ts +0 -3
- package/dist/minimal-cli.d.ts.map +0 -1
- package/dist/parallel/parallel-manager.d.ts +0 -67
- package/dist/parallel/parallel-manager.d.ts.map +0 -1
- package/dist/policy/PolicyManager.d.ts +0 -104
- package/dist/policy/PolicyManager.d.ts.map +0 -1
- package/dist/scaffold/claude-hooks.d.ts +0 -28
- package/dist/scaffold/claude-hooks.d.ts.map +0 -1
- package/dist/scaffold/cursor-hooks.d.ts +0 -7
- package/dist/scaffold/cursor-hooks.d.ts.map +0 -1
- package/dist/scaffold/git-hooks.d.ts +0 -38
- package/dist/scaffold/git-hooks.d.ts.map +0 -1
- package/dist/scaffold/index.d.ts +0 -17
- package/dist/scaffold/index.d.ts.map +0 -1
- package/dist/session/session-manager.d.ts +0 -94
- package/dist/session/session-manager.d.ts.map +0 -1
- package/dist/spec/SpecFileManager.d.ts +0 -146
- package/dist/spec/SpecFileManager.d.ts.map +0 -1
- package/dist/templates/.cursor/hooks/caws-tool-validation.sh +0 -121
- package/dist/templates/.github/copilot/instructions.md +0 -311
- package/dist/test-analysis.d.ts +0 -231
- package/dist/test-analysis.d.ts.map +0 -1
- package/dist/tool-interface.d.ts +0 -236
- package/dist/tool-interface.d.ts.map +0 -1
- package/dist/tool-loader.d.ts +0 -77
- package/dist/tool-loader.d.ts.map +0 -1
- package/dist/tool-validator.d.ts +0 -72
- package/dist/tool-validator.d.ts.map +0 -1
- package/dist/utils/async-utils.d.ts +0 -73
- package/dist/utils/async-utils.d.ts.map +0 -1
- package/dist/utils/command-wrapper.d.ts +0 -66
- package/dist/utils/command-wrapper.d.ts.map +0 -1
- package/dist/utils/detection.d.ts +0 -14
- package/dist/utils/detection.d.ts.map +0 -1
- package/dist/utils/error-categories.d.ts +0 -52
- package/dist/utils/error-categories.d.ts.map +0 -1
- package/dist/utils/finalization.d.ts +0 -17
- package/dist/utils/finalization.d.ts.map +0 -1
- package/dist/utils/git-lock.d.ts +0 -13
- package/dist/utils/git-lock.d.ts.map +0 -1
- package/dist/utils/gitignore-updater.d.ts +0 -39
- package/dist/utils/gitignore-updater.d.ts.map +0 -1
- package/dist/utils/ide-detection.d.ts +0 -89
- package/dist/utils/ide-detection.d.ts.map +0 -1
- package/dist/utils/project-analysis.d.ts +0 -34
- package/dist/utils/project-analysis.d.ts.map +0 -1
- package/dist/utils/promise-utils.d.ts +0 -30
- package/dist/utils/promise-utils.d.ts.map +0 -1
- package/dist/utils/quality-gates-utils.d.ts +0 -49
- package/dist/utils/quality-gates-utils.d.ts.map +0 -1
- package/dist/utils/quality-gates.d.ts +0 -49
- package/dist/utils/quality-gates.d.ts.map +0 -1
- package/dist/utils/quality-gates.js +0 -402
- package/dist/utils/spec-resolver.d.ts +0 -80
- package/dist/utils/spec-resolver.d.ts.map +0 -1
- package/dist/utils/typescript-detector.d.ts +0 -66
- package/dist/utils/typescript-detector.d.ts.map +0 -1
- package/dist/utils/yaml-validation.d.ts +0 -32
- package/dist/utils/yaml-validation.d.ts.map +0 -1
- package/dist/validation/spec-validation.d.ts +0 -43
- package/dist/validation/spec-validation.d.ts.map +0 -1
- package/dist/waivers-manager.d.ts +0 -167
- package/dist/waivers-manager.d.ts.map +0 -1
- package/dist/worktree/worktree-manager.d.ts +0 -54
- package/dist/worktree/worktree-manager.d.ts.map +0 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Unified quality gates CLI command
|
|
3
|
+
* Delegates to the v2 gate pipeline (src/gates/pipeline.js) for evaluation
|
|
4
|
+
* and formatting (src/gates/format.js) for output.
|
|
5
|
+
* @author @darianrosebrook
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { evaluateGates } = require('../gates/pipeline');
|
|
9
|
+
const { formatText, formatJson, formatEnrichedText } = require('../gates/format');
|
|
10
|
+
const { enrichGateResults } = require('../gates/feedback');
|
|
11
|
+
const { resolveSpec } = require('../utils/spec-resolver');
|
|
12
|
+
const { commandWrapper } = require('../utils/command-wrapper');
|
|
13
|
+
const { recordGates, loadState } = require('../utils/working-state');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Run quality gates via the v2 pipeline
|
|
17
|
+
* @param {Object} options - Command options
|
|
18
|
+
* @param {string} [options.context] - Execution context (cli, commit, edit)
|
|
19
|
+
* @param {string} [options.specId] - Target spec ID
|
|
20
|
+
* @param {string} [options.specFile] - Explicit spec file path
|
|
21
|
+
* @param {string} [options.file] - Single file to check (for edit context)
|
|
22
|
+
* @param {boolean} [options.json] - Output as JSON
|
|
23
|
+
* @param {string} [options.format] - Output format (text, json)
|
|
24
|
+
* @param {boolean} [options.quiet] - Minimal output
|
|
25
|
+
*/
|
|
26
|
+
async function gatesCommand(options = {}) {
|
|
27
|
+
return commandWrapper(
|
|
28
|
+
async () => {
|
|
29
|
+
const projectRoot = process.cwd();
|
|
30
|
+
const context = options.context || 'cli';
|
|
31
|
+
|
|
32
|
+
// Resolve spec (working-spec or feature spec)
|
|
33
|
+
let spec = null;
|
|
34
|
+
try {
|
|
35
|
+
const resolved = await resolveSpec({
|
|
36
|
+
specId: options.specId,
|
|
37
|
+
specFile: options.specFile,
|
|
38
|
+
warnLegacy: false,
|
|
39
|
+
interactive: false,
|
|
40
|
+
});
|
|
41
|
+
spec = resolved.spec;
|
|
42
|
+
} catch {
|
|
43
|
+
// No spec available — gates that need it will handle gracefully
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Get file list based on context
|
|
47
|
+
let stagedFiles = [];
|
|
48
|
+
const { execSync } = require('child_process');
|
|
49
|
+
if (context === 'commit') {
|
|
50
|
+
try {
|
|
51
|
+
stagedFiles = execSync('git diff --cached --name-only', {
|
|
52
|
+
cwd: projectRoot,
|
|
53
|
+
encoding: 'utf8',
|
|
54
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
55
|
+
})
|
|
56
|
+
.trim()
|
|
57
|
+
.split('\n')
|
|
58
|
+
.filter(Boolean);
|
|
59
|
+
} catch {
|
|
60
|
+
/* no staged files */
|
|
61
|
+
}
|
|
62
|
+
} else if (options.file) {
|
|
63
|
+
stagedFiles = [options.file];
|
|
64
|
+
} else if (context === 'cli') {
|
|
65
|
+
// For CLI context, use all tracked files so gates can provide meaningful analysis
|
|
66
|
+
try {
|
|
67
|
+
stagedFiles = execSync('git ls-files', {
|
|
68
|
+
cwd: projectRoot,
|
|
69
|
+
encoding: 'utf8',
|
|
70
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
71
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
72
|
+
})
|
|
73
|
+
.trim()
|
|
74
|
+
.split('\n')
|
|
75
|
+
.filter(Boolean);
|
|
76
|
+
} catch {
|
|
77
|
+
/* not a git repo or git unavailable */
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const report = await evaluateGates({ projectRoot, stagedFiles, spec, context });
|
|
82
|
+
|
|
83
|
+
// Record to working state
|
|
84
|
+
if (spec && spec.id) {
|
|
85
|
+
try { recordGates(spec.id, report, context, projectRoot); } catch { /* non-fatal */ }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (options.json || options.format === 'json') {
|
|
89
|
+
console.log(formatJson(report));
|
|
90
|
+
} else if (!options.quiet) {
|
|
91
|
+
// Enrich feedback on failure or --verbose
|
|
92
|
+
if (!report.passed || options.verbose) {
|
|
93
|
+
try {
|
|
94
|
+
const state = spec?.id ? loadState(spec.id, projectRoot) : null;
|
|
95
|
+
const enrichments = enrichGateResults(report, { spec, state, projectRoot });
|
|
96
|
+
if (enrichments.size > 0) {
|
|
97
|
+
console.log(formatEnrichedText(report, enrichments));
|
|
98
|
+
} else {
|
|
99
|
+
console.log(formatText(report));
|
|
100
|
+
}
|
|
101
|
+
} catch {
|
|
102
|
+
console.log(formatText(report));
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
console.log(formatText(report));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Exit with appropriate code
|
|
110
|
+
if (!report.passed) {
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
commandName: 'gates',
|
|
116
|
+
context: { options },
|
|
117
|
+
exitOnError: true,
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
module.exports = { gatesCommand };
|
package/dist/commands/init.js
CHANGED
|
@@ -8,6 +8,7 @@ const fs = require('fs-extra');
|
|
|
8
8
|
const path = require('path');
|
|
9
9
|
const inquirer = require('inquirer');
|
|
10
10
|
const chalk = require('chalk');
|
|
11
|
+
const yaml = require('js-yaml');
|
|
11
12
|
|
|
12
13
|
// Import shared utilities
|
|
13
14
|
const { detectCAWSSetup } = require('../utils/detection');
|
|
@@ -28,6 +29,117 @@ const {
|
|
|
28
29
|
parseIDESelection,
|
|
29
30
|
} = require('../utils/ide-detection');
|
|
30
31
|
|
|
32
|
+
function buildInitialFeatureSpec(specContent, fallbackId) {
|
|
33
|
+
const parsed = yaml.load(specContent);
|
|
34
|
+
const riskTier =
|
|
35
|
+
typeof parsed.risk_tier === 'string'
|
|
36
|
+
? parseInt(parsed.risk_tier.replace(/^T/i, ''), 10) || 3
|
|
37
|
+
: parsed.risk_tier || 3;
|
|
38
|
+
const aiConfidenceRaw = parsed.ai_assessment?.confidence_level;
|
|
39
|
+
const aiConfidence =
|
|
40
|
+
typeof aiConfidenceRaw === 'number' && aiConfidenceRaw <= 1
|
|
41
|
+
? Math.max(1, Math.min(10, Math.round(aiConfidenceRaw * 10)))
|
|
42
|
+
: typeof aiConfidenceRaw === 'number'
|
|
43
|
+
? Math.max(1, Math.min(10, Math.round(aiConfidenceRaw)))
|
|
44
|
+
: 8;
|
|
45
|
+
const normalizedContracts =
|
|
46
|
+
Array.isArray(parsed.contracts) && parsed.contracts.length > 0
|
|
47
|
+
? parsed.contracts.map((contract, index) => ({
|
|
48
|
+
type: ['openapi', 'graphql', 'proto', 'pact'].includes(contract?.type)
|
|
49
|
+
? contract.type
|
|
50
|
+
: 'openapi',
|
|
51
|
+
path:
|
|
52
|
+
contract?.path ||
|
|
53
|
+
(index === 0 ? 'docs/api/initial-feature.yaml' : `docs/api/contract-${index + 1}.yaml`),
|
|
54
|
+
}))
|
|
55
|
+
: riskTier <= 2
|
|
56
|
+
? [{ type: 'openapi', path: 'docs/api/initial-feature.yaml' }]
|
|
57
|
+
: [];
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
id: parsed.id || fallbackId,
|
|
61
|
+
title: parsed.title || 'New CAWS Project',
|
|
62
|
+
risk_tier: riskTier,
|
|
63
|
+
mode: parsed.mode || 'feature',
|
|
64
|
+
blast_radius: parsed.blast_radius || { modules: ['src', 'tests'], data_migration: false },
|
|
65
|
+
operational_rollback_slo: parsed.operational_rollback_slo || '5m',
|
|
66
|
+
scope: parsed.scope || {
|
|
67
|
+
in: ['src/', 'tests/'],
|
|
68
|
+
out: ['node_modules/', 'dist/', 'build/'],
|
|
69
|
+
},
|
|
70
|
+
invariants: Array.isArray(parsed.invariants) && parsed.invariants.length > 0
|
|
71
|
+
? parsed.invariants
|
|
72
|
+
: ['System maintains data consistency'],
|
|
73
|
+
acceptance: Array.isArray(parsed.acceptance) && parsed.acceptance.length > 0
|
|
74
|
+
? parsed.acceptance
|
|
75
|
+
: [
|
|
76
|
+
{
|
|
77
|
+
id: 'A1',
|
|
78
|
+
given: 'Current system state',
|
|
79
|
+
when: 'the initial project is bootstrapped',
|
|
80
|
+
then: 'the CAWS project should validate successfully',
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
non_functional: parsed.non_functional || {
|
|
84
|
+
a11y: ['keyboard'],
|
|
85
|
+
perf: { api_p95_ms: 250 },
|
|
86
|
+
security: [],
|
|
87
|
+
},
|
|
88
|
+
contracts: normalizedContracts,
|
|
89
|
+
observability: parsed.observability || { logs: [], metrics: [], traces: [] },
|
|
90
|
+
migrations: Array.isArray(parsed.migrations) ? parsed.migrations : [],
|
|
91
|
+
rollback: Array.isArray(parsed.rollback) ? parsed.rollback : [],
|
|
92
|
+
ai_assessment: {
|
|
93
|
+
confidence_level: aiConfidence,
|
|
94
|
+
uncertainty_areas: Array.isArray(parsed.ai_assessment?.uncertainty_areas)
|
|
95
|
+
? parsed.ai_assessment.uncertainty_areas
|
|
96
|
+
: [],
|
|
97
|
+
recommended_pairing:
|
|
98
|
+
parsed.ai_assessment?.recommended_pairing !== undefined
|
|
99
|
+
? Boolean(parsed.ai_assessment.recommended_pairing)
|
|
100
|
+
: aiConfidence <= 6,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function writeInitialSpecArtifacts(specContent, fallbackId) {
|
|
106
|
+
const canonicalSpec = buildInitialFeatureSpec(specContent, fallbackId);
|
|
107
|
+
const now = new Date().toISOString();
|
|
108
|
+
const canonicalContent = yaml.dump(canonicalSpec, { indent: 2 });
|
|
109
|
+
const specsDir = path.join('.caws', 'specs');
|
|
110
|
+
const featureSpecPath = path.join(specsDir, `${canonicalSpec.id}.yaml`);
|
|
111
|
+
const workingSpecPath = path.join('.caws', 'working-spec.yaml');
|
|
112
|
+
const registryPath = path.join(specsDir, 'registry.json');
|
|
113
|
+
|
|
114
|
+
await fs.ensureDir(specsDir);
|
|
115
|
+
await fs.writeFile(featureSpecPath, canonicalContent);
|
|
116
|
+
await fs.writeFile(workingSpecPath, canonicalContent);
|
|
117
|
+
await fs.writeJson(
|
|
118
|
+
registryPath,
|
|
119
|
+
{
|
|
120
|
+
version: '1.0.0',
|
|
121
|
+
specs: {
|
|
122
|
+
[canonicalSpec.id]: {
|
|
123
|
+
path: `${canonicalSpec.id}.yaml`,
|
|
124
|
+
type: 'feature',
|
|
125
|
+
status: 'active',
|
|
126
|
+
created_at: now,
|
|
127
|
+
updated_at: now,
|
|
128
|
+
owner: null,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
lastUpdated: now,
|
|
132
|
+
},
|
|
133
|
+
{ spaces: 2 }
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
canonicalSpec,
|
|
138
|
+
featureSpecPath,
|
|
139
|
+
workingSpecPath,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
31
143
|
/**
|
|
32
144
|
* Initialize a new project with CAWS
|
|
33
145
|
*/
|
|
@@ -492,10 +604,12 @@ async function initProject(projectName, options) {
|
|
|
492
604
|
console.log(chalk.blue('\nGenerating CAWS working spec...'));
|
|
493
605
|
const specContent = generateWorkingSpec(answers);
|
|
494
606
|
|
|
495
|
-
// Write
|
|
607
|
+
// Write canonical feature spec plus legacy compatibility mirror
|
|
496
608
|
await fs.ensureDir('.caws');
|
|
497
|
-
await
|
|
609
|
+
const initialSpec = await writeInitialSpecArtifacts(specContent, answers.projectId);
|
|
610
|
+
console.log(chalk.green(`Created ${initialSpec.featureSpecPath}`));
|
|
498
611
|
console.log(chalk.green('Created .caws/working-spec.yaml'));
|
|
612
|
+
console.log(chalk.green('Created .caws/specs/registry.json'));
|
|
499
613
|
|
|
500
614
|
// Optionally create policy.yaml (optional - defaults work fine)
|
|
501
615
|
const policyPath = path.join('.caws', 'policy.yaml');
|
|
@@ -503,7 +617,6 @@ async function initProject(projectName, options) {
|
|
|
503
617
|
const { PolicyManager } = require('../policy/PolicyManager');
|
|
504
618
|
const policyManager = new PolicyManager();
|
|
505
619
|
const defaultPolicy = policyManager.getDefaultPolicy();
|
|
506
|
-
const yaml = require('js-yaml');
|
|
507
620
|
const policyContent = yaml.dump(defaultPolicy, { indent: 2 });
|
|
508
621
|
await fs.writeFile(policyPath, policyContent);
|
|
509
622
|
console.log(chalk.green('Created .caws/policy.yaml (optional - defaults work fine)'));
|
|
@@ -522,10 +635,20 @@ ${answers.projectDescription}
|
|
|
522
635
|
## Risk Tier: ${answers.riskTier === 1 ? 'High (T1)' : answers.riskTier === 2 ? 'Medium (T2)' : 'Low (T3)'}
|
|
523
636
|
|
|
524
637
|
## Next Steps
|
|
525
|
-
1. Review and customize \`.caws/
|
|
638
|
+
1. Review and customize \`.caws/specs/${answers.projectId}.yaml\`
|
|
526
639
|
2. Set up your development environment
|
|
527
640
|
3. Implement features according to the spec
|
|
528
|
-
4. Run \`caws validate\` to check your progress
|
|
641
|
+
4. Run \`caws validate --spec-id ${answers.projectId}\` to check your progress
|
|
642
|
+
|
|
643
|
+
## Multi-Agent Recommendation
|
|
644
|
+
The initial project spec is also available in \`.caws/specs/${answers.projectId}.yaml\`.
|
|
645
|
+
For multi-agent work, treat feature specs in \`.caws/specs/\` as canonical and use
|
|
646
|
+
\`.caws/working-spec.yaml\` only as a compatibility mirror:
|
|
647
|
+
|
|
648
|
+
\`\`\`bash
|
|
649
|
+
caws specs create my-feature --type feature --title "My Feature"
|
|
650
|
+
caws validate --spec-id my-feature
|
|
651
|
+
\`\`\`
|
|
529
652
|
|
|
530
653
|
## Quality Gates
|
|
531
654
|
- **Coverage**: ${answers.riskTier === 1 ? '90%+' : answers.riskTier === 2 ? '80%+' : '70%+'}
|
|
@@ -584,7 +707,7 @@ Happy coding! `;
|
|
|
584
707
|
projectTitle: path.basename(process.cwd()),
|
|
585
708
|
projectDescription: `A ${detectedType} project managed with CAWS`,
|
|
586
709
|
riskTier: 2,
|
|
587
|
-
projectId: `PROJ-${Math.floor(Math.random() * 1000) + 1}`,
|
|
710
|
+
projectId: `PROJ-${String(Math.floor(Math.random() * 1000) + 1).padStart(3, '0')}`,
|
|
588
711
|
useCursorHooks: true,
|
|
589
712
|
generateExamples: false,
|
|
590
713
|
projectMode: 'feature',
|
|
@@ -624,8 +747,10 @@ Happy coding! `;
|
|
|
624
747
|
|
|
625
748
|
const specContent = generateWorkingSpec(defaultAnswers);
|
|
626
749
|
await fs.ensureDir('.caws');
|
|
627
|
-
await
|
|
750
|
+
const initialSpec = await writeInitialSpecArtifacts(specContent, defaultAnswers.projectId);
|
|
751
|
+
console.log(chalk.green(`Created ${initialSpec.featureSpecPath}`));
|
|
628
752
|
console.log(chalk.green('Created .caws/working-spec.yaml'));
|
|
753
|
+
console.log(chalk.green('Created .caws/specs/registry.json'));
|
|
629
754
|
|
|
630
755
|
// Optionally create policy.yaml (optional - defaults work fine)
|
|
631
756
|
const policyPath = path.join('.caws', 'policy.yaml');
|
|
@@ -633,7 +758,6 @@ Happy coding! `;
|
|
|
633
758
|
const { PolicyManager } = require('../policy/PolicyManager');
|
|
634
759
|
const policyManager = new PolicyManager();
|
|
635
760
|
const defaultPolicy = policyManager.getDefaultPolicy();
|
|
636
|
-
const yaml = require('js-yaml');
|
|
637
761
|
const policyContent = yaml.dump(defaultPolicy, { indent: 2 });
|
|
638
762
|
await fs.writeFile(policyPath, policyContent);
|
|
639
763
|
console.log(chalk.green('Created .caws/policy.yaml (optional - defaults work fine)'));
|
|
@@ -670,8 +794,10 @@ Happy coding! `;
|
|
|
670
794
|
// Success message
|
|
671
795
|
console.log(chalk.green('\nCAWS project initialized successfully!'));
|
|
672
796
|
console.log(chalk.blue('\nNext steps:'));
|
|
673
|
-
console.log('1. Review .caws/
|
|
674
|
-
console.log('2.
|
|
797
|
+
console.log('1. Review .caws/specs/<spec-id>.yaml');
|
|
798
|
+
console.log('2. Treat .caws/working-spec.yaml as the compatibility mirror, not the long-term source of truth');
|
|
799
|
+
console.log('3. If multiple agents will collaborate, create more feature specs with `caws specs create <id>`');
|
|
800
|
+
console.log('4. Use `--spec-id` on validation/status/diagnose commands for feature work');
|
|
675
801
|
|
|
676
802
|
// Show contract requirements if Tier 1 or 2
|
|
677
803
|
// Use answers if available (interactive mode), otherwise default to 2
|
|
@@ -688,16 +814,18 @@ Happy coding! `;
|
|
|
688
814
|
}
|
|
689
815
|
|
|
690
816
|
console.log('\nRecommended Setup Workflow:');
|
|
691
|
-
console.log(' 1. Review .caws/
|
|
817
|
+
console.log(' 1. Review .caws/specs/<spec-id>.yaml');
|
|
692
818
|
console.log(' 2. Run: caws scaffold (adds tools and templates)');
|
|
693
|
-
console.log(' 3.
|
|
694
|
-
console.log(' 4. Run: caws
|
|
695
|
-
console.log(' 5.
|
|
819
|
+
console.log(' 3. For multi-agent work, run: caws specs create <feature-id>');
|
|
820
|
+
console.log(' 4. Run: caws validate --spec-id <spec-id> (verify setup)');
|
|
821
|
+
console.log(' 5. Run: caws diagnose --spec-id <spec-id> (check health)');
|
|
822
|
+
console.log(' 6. Optional: Create .caws/policy.yaml for custom budgets');
|
|
696
823
|
const finalIDEs = answers?.selectedIDEs || [];
|
|
697
824
|
if (finalIDEs.includes('cursor') || finalIDEs.includes('claude') || options.interactive === false) {
|
|
698
|
-
console.log('
|
|
825
|
+
console.log(' 7. Restart your IDE to activate quality gates');
|
|
699
826
|
}
|
|
700
827
|
console.log('\nQuick start: caws scaffold && caws validate && caws diagnose');
|
|
828
|
+
console.log('Multi-agent quick start: caws specs create my-feature && caws validate --spec-id my-feature');
|
|
701
829
|
} catch (error) {
|
|
702
830
|
console.error(chalk.red('Error during initialization:'), error.message);
|
|
703
831
|
if (error.stack) {
|
package/dist/commands/iterate.js
CHANGED
|
@@ -14,6 +14,7 @@ const { initializeGlobalSetup } = require('../config');
|
|
|
14
14
|
|
|
15
15
|
// Import spec resolution system
|
|
16
16
|
const { resolveSpec } = require('../utils/spec-resolver');
|
|
17
|
+
const { loadState } = require('../utils/working-state');
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Iterate command handler
|
|
@@ -21,12 +22,12 @@ const { resolveSpec } = require('../utils/spec-resolver');
|
|
|
21
22
|
* @param {string} specFile - Path to working spec file
|
|
22
23
|
* @param {object} options - Command options
|
|
23
24
|
*/
|
|
24
|
-
async function iterateCommand(specFile
|
|
25
|
+
async function iterateCommand(specFile, options = {}) {
|
|
25
26
|
try {
|
|
26
27
|
// Resolve spec using priority system
|
|
27
28
|
const resolved = await resolveSpec({
|
|
28
29
|
specId: options.specId,
|
|
29
|
-
specFile,
|
|
30
|
+
specFile: specFile || undefined,
|
|
30
31
|
warnLegacy: false,
|
|
31
32
|
});
|
|
32
33
|
|
|
@@ -36,12 +37,19 @@ async function iterateCommand(specFile = '.caws/working-spec.yaml', options = {}
|
|
|
36
37
|
const setup = initializeGlobalSetup();
|
|
37
38
|
|
|
38
39
|
if (setup.hasWorkingSpec) {
|
|
39
|
-
console.log(`Detected ${setup.
|
|
40
|
+
console.log(`Detected ${setup.type} CAWS setup`);
|
|
40
41
|
console.log(` Capabilities: ${setup.capabilities.join(', ')}`);
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
// Parse current state from options
|
|
44
|
-
|
|
45
|
+
let currentState = {};
|
|
46
|
+
if (options.currentState) {
|
|
47
|
+
try {
|
|
48
|
+
currentState = JSON.parse(options.currentState);
|
|
49
|
+
} catch {
|
|
50
|
+
console.error('Invalid JSON in --current-state. Using defaults.');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
45
53
|
const stateDescription = currentState.description || 'Starting implementation';
|
|
46
54
|
|
|
47
55
|
console.log(chalk.blue('\nIterative Development Guidance\n'));
|
|
@@ -50,9 +58,48 @@ async function iterateCommand(specFile = '.caws/working-spec.yaml', options = {}
|
|
|
50
58
|
console.log(`ID: ${spec.id} | Tier: ${spec.risk_tier} | Mode: ${spec.mode}`);
|
|
51
59
|
console.log(`Current State: ${stateDescription}\n`);
|
|
52
60
|
|
|
61
|
+
// Load working state for evidence-based guidance
|
|
62
|
+
let workingState = null;
|
|
63
|
+
try { workingState = loadState(spec.id); } catch { /* non-fatal */ }
|
|
64
|
+
|
|
53
65
|
// Analyze progress based on mode
|
|
54
66
|
const guidance = generateGuidance(spec, currentState, options);
|
|
55
67
|
|
|
68
|
+
// If working state exists, overlay evidence-based data
|
|
69
|
+
if (workingState && workingState.phase !== 'not-started') {
|
|
70
|
+
guidance.phase = formatPhase(workingState.phase);
|
|
71
|
+
|
|
72
|
+
// Build evidence-based completed steps
|
|
73
|
+
const evidence = [];
|
|
74
|
+
if (workingState.validation && workingState.validation.passed) {
|
|
75
|
+
evidence.push(`Validation passed (Grade ${workingState.validation.grade || '?'})`);
|
|
76
|
+
}
|
|
77
|
+
if (workingState.evaluation) {
|
|
78
|
+
evidence.push(`Evaluation: ${workingState.evaluation.percentage}% (Grade ${workingState.evaluation.grade})`);
|
|
79
|
+
}
|
|
80
|
+
if (workingState.gates && workingState.gates.passed) {
|
|
81
|
+
evidence.push(`All gates passing (last run: ${workingState.gates.context} context)`);
|
|
82
|
+
}
|
|
83
|
+
if (workingState.acceptance_criteria) {
|
|
84
|
+
const ac = workingState.acceptance_criteria;
|
|
85
|
+
evidence.push(`AC verification: ${ac.pass}/${ac.total} pass, ${ac.fail} fail, ${ac.unchecked} unchecked`);
|
|
86
|
+
}
|
|
87
|
+
if (workingState.files_touched && workingState.files_touched.length > 0) {
|
|
88
|
+
evidence.push(`${workingState.files_touched.length} file(s) touched`);
|
|
89
|
+
}
|
|
90
|
+
if (evidence.length > 0) {
|
|
91
|
+
guidance.completed = evidence;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Overlay blockers and next actions from state
|
|
95
|
+
if (workingState.blockers && workingState.blockers.length > 0) {
|
|
96
|
+
guidance.blockers = workingState.blockers.map(b => b.message);
|
|
97
|
+
}
|
|
98
|
+
if (workingState.next_actions && workingState.next_actions.length > 0) {
|
|
99
|
+
guidance.nextActions = workingState.next_actions;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
56
103
|
// Display guidance
|
|
57
104
|
console.log(chalk.blue('Current Phase:\n'));
|
|
58
105
|
console.log(` ${guidance.phase}\n`);
|
|
@@ -72,6 +119,18 @@ async function iterateCommand(specFile = '.caws/working-spec.yaml', options = {}
|
|
|
72
119
|
guidance.blockers.forEach((blocker) => {
|
|
73
120
|
console.log(chalk.red(` ${blocker}`));
|
|
74
121
|
});
|
|
122
|
+
|
|
123
|
+
// Show quality gap summary if sidecars available
|
|
124
|
+
try {
|
|
125
|
+
const { diagnoseQualityGaps } = require('../sidecars');
|
|
126
|
+
if (workingState) {
|
|
127
|
+
const gapResult = diagnoseQualityGaps(workingState, spec);
|
|
128
|
+
if (gapResult.data && gapResult.data.gaps && gapResult.data.gaps.length > 0) {
|
|
129
|
+
console.log(chalk.yellow(`\n Quality gaps: ${gapResult.data.summary}`));
|
|
130
|
+
console.log(chalk.yellow(' Run: caws sidecar gaps --spec-id ' + spec.id));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} catch { /* sidecars not available — non-fatal */ }
|
|
75
134
|
}
|
|
76
135
|
|
|
77
136
|
console.log(chalk.blue('\nRecommendations:\n'));
|
|
@@ -337,4 +396,18 @@ function getQualityGates(riskTier) {
|
|
|
337
396
|
return gates[riskTier] || gates[2];
|
|
338
397
|
}
|
|
339
398
|
|
|
399
|
+
/**
|
|
400
|
+
* Format phase slug into human-readable label
|
|
401
|
+
*/
|
|
402
|
+
function formatPhase(phase) {
|
|
403
|
+
const labels = {
|
|
404
|
+
'not-started': 'Not Started',
|
|
405
|
+
'spec-authoring': 'Spec Authoring',
|
|
406
|
+
'implementation': 'Implementation',
|
|
407
|
+
'verification': 'Verification',
|
|
408
|
+
'complete': 'Complete',
|
|
409
|
+
};
|
|
410
|
+
return labels[phase] || phase;
|
|
411
|
+
}
|
|
412
|
+
|
|
340
413
|
module.exports = { iterateCommand };
|
|
@@ -128,6 +128,10 @@ function handleStatus() {
|
|
|
128
128
|
const statusColor =
|
|
129
129
|
agent.status === 'active'
|
|
130
130
|
? chalk.green
|
|
131
|
+
: agent.status === 'fresh'
|
|
132
|
+
? chalk.cyan
|
|
133
|
+
: agent.status === 'merged'
|
|
134
|
+
? chalk.blue
|
|
131
135
|
: agent.status === 'missing'
|
|
132
136
|
? chalk.red
|
|
133
137
|
: chalk.yellow;
|
package/dist/commands/plan.js
CHANGED
|
@@ -10,7 +10,7 @@ const chalk = require('chalk');
|
|
|
10
10
|
const { safeAsync, outputResult } = require('../error-handler');
|
|
11
11
|
|
|
12
12
|
// Import spec resolution system
|
|
13
|
-
const { resolveSpec } = require('../utils/spec-resolver');
|
|
13
|
+
const { resolveSpec, loadSpecsRegistry } = require('../utils/spec-resolver');
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Plan templates for different spec types
|
|
@@ -86,15 +86,11 @@ const PLAN_TEMPLATES = {
|
|
|
86
86
|
* @returns {Promise<Object|null>} Spec data or null
|
|
87
87
|
*/
|
|
88
88
|
async function loadSpecForPlanning(specId) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return resolved.spec;
|
|
95
|
-
} catch (error) {
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
89
|
+
const resolved = await resolveSpec({
|
|
90
|
+
specId,
|
|
91
|
+
warnLegacy: false,
|
|
92
|
+
});
|
|
93
|
+
return resolved.spec;
|
|
98
94
|
}
|
|
99
95
|
|
|
100
96
|
/**
|
|
@@ -398,21 +394,18 @@ async function planCommand(action, options = {}) {
|
|
|
398
394
|
|
|
399
395
|
if (status.specCount === 1) {
|
|
400
396
|
// Use the single spec automatically
|
|
401
|
-
const registry = await
|
|
397
|
+
const registry = await loadSpecsRegistry();
|
|
402
398
|
const singleSpecId = Object.keys(registry.specs)[0];
|
|
403
399
|
console.log(chalk.blue(`Auto-detected single spec: ${singleSpecId}`));
|
|
404
400
|
|
|
405
401
|
const spec = await loadSpecForPlanning(singleSpecId);
|
|
406
|
-
if (!spec) {
|
|
407
|
-
throw new Error(`Auto-detected spec '${singleSpecId}' could not be loaded`);
|
|
408
|
-
}
|
|
409
|
-
|
|
410
402
|
await generateAndDisplayPlan(spec, singleSpecId, options);
|
|
411
403
|
} else if (status.specCount > 1) {
|
|
404
|
+
const registry = await loadSpecsRegistry();
|
|
412
405
|
throw new Error(
|
|
413
406
|
'Multiple specs detected. Please specify which one: caws plan generate --spec-id <id>\n' +
|
|
414
407
|
'Available specs: ' +
|
|
415
|
-
Object.keys(
|
|
408
|
+
Object.keys(registry.specs || {}).join(', ')
|
|
416
409
|
);
|
|
417
410
|
} else {
|
|
418
411
|
throw new Error('No specs found. Create a spec first: caws specs create <id>');
|
|
@@ -420,9 +413,6 @@ async function planCommand(action, options = {}) {
|
|
|
420
413
|
} else {
|
|
421
414
|
// Load spec for planning
|
|
422
415
|
const spec = await loadSpecForPlanning(specId);
|
|
423
|
-
if (!spec) {
|
|
424
|
-
throw new Error(`Spec '${specId}' not found`);
|
|
425
|
-
}
|
|
426
416
|
|
|
427
417
|
return await generateAndDisplayPlan(spec, specId, options);
|
|
428
418
|
}
|
|
@@ -12,6 +12,41 @@ const crypto = require('crypto');
|
|
|
12
12
|
const yaml = require('js-yaml');
|
|
13
13
|
const { execSync } = require('child_process');
|
|
14
14
|
const { commandWrapper } = require('../utils/command-wrapper');
|
|
15
|
+
const { resolveSpec } = require('../utils/spec-resolver');
|
|
16
|
+
|
|
17
|
+
async function resolveProvenanceSpec(options = {}) {
|
|
18
|
+
try {
|
|
19
|
+
return await resolveSpec({
|
|
20
|
+
specId: options.specId,
|
|
21
|
+
specFile: options.specFile,
|
|
22
|
+
warnLegacy: false,
|
|
23
|
+
});
|
|
24
|
+
} catch (error) {
|
|
25
|
+
const shouldFallbackToLegacy =
|
|
26
|
+
!options.specId &&
|
|
27
|
+
!options.specFile &&
|
|
28
|
+
error.message.includes('schema violations');
|
|
29
|
+
|
|
30
|
+
if (!shouldFallbackToLegacy) {
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const legacyPath = path.join(process.cwd(), '.caws', 'working-spec.yaml');
|
|
35
|
+
if (!(await fs.pathExists(legacyPath))) {
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const legacyContent = await fs.readFile(legacyPath, 'utf8');
|
|
40
|
+
const legacySpec = yaml.load(legacyContent);
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
path: legacyPath,
|
|
44
|
+
type: 'legacy',
|
|
45
|
+
spec: legacySpec,
|
|
46
|
+
degradedValidation: true,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
15
50
|
|
|
16
51
|
/**
|
|
17
52
|
* Get quality gates status from saved report
|
|
@@ -90,14 +125,8 @@ async function updateProvenance(options) {
|
|
|
90
125
|
// Ensure output directory exists
|
|
91
126
|
await fs.ensureDir(output);
|
|
92
127
|
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
if (!(await fs.pathExists(specPath))) {
|
|
96
|
-
throw new Error('Working spec not found - not in CAWS project');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const specContent = await fs.readFile(specPath, 'utf8');
|
|
100
|
-
const spec = yaml.load(specContent);
|
|
128
|
+
const resolved = await resolveProvenanceSpec(options);
|
|
129
|
+
const spec = resolved.spec;
|
|
101
130
|
|
|
102
131
|
// Load existing provenance chain
|
|
103
132
|
const provenanceChain = await loadProvenanceChain(output);
|
|
@@ -116,6 +145,9 @@ async function updateProvenance(options) {
|
|
|
116
145
|
title: spec.title,
|
|
117
146
|
risk_tier: spec.risk_tier,
|
|
118
147
|
mode: spec.mode,
|
|
148
|
+
type: resolved.type,
|
|
149
|
+
path: resolved.path,
|
|
150
|
+
status: spec.status || null,
|
|
119
151
|
waiver_ids: spec.waiver_ids || [],
|
|
120
152
|
},
|
|
121
153
|
quality_gates: getQualityGatesStatus(),
|
|
@@ -148,6 +180,10 @@ async function updateProvenance(options) {
|
|
|
148
180
|
|
|
149
181
|
if (!quiet) {
|
|
150
182
|
console.log(`Provenance updated for commit ${commit.substring(0, 8)}`);
|
|
183
|
+
console.log(` Spec: ${spec.id} (${resolved.type}) -> ${resolved.path}`);
|
|
184
|
+
if (resolved.degradedValidation) {
|
|
185
|
+
console.log(' Note: using legacy spec metadata despite schema validation issues');
|
|
186
|
+
}
|
|
151
187
|
console.log(` Chain length: ${provenanceChain.length} entries`);
|
|
152
188
|
console.log(` Hash: ${newEntry.hash.substring(0, 16)}...`);
|
|
153
189
|
}
|
|
@@ -929,17 +965,12 @@ async function initProvenance(options) {
|
|
|
929
965
|
await fs.ensureDir(output);
|
|
930
966
|
console.log(`Created provenance directory: ${output}`);
|
|
931
967
|
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
if (
|
|
935
|
-
console.log('');
|
|
936
|
-
console.log('Not in a CAWS project - missing working spec');
|
|
937
|
-
console.log('Run "caws init" first to create a CAWS project');
|
|
938
|
-
process.exit(1);
|
|
968
|
+
const resolved = await resolveProvenanceSpec(options);
|
|
969
|
+
console.log(`Found CAWS spec: ${resolved.spec.id} (${resolved.type})`);
|
|
970
|
+
if (resolved.degradedValidation) {
|
|
971
|
+
console.log(' Proceeding with legacy spec metadata despite schema validation issues');
|
|
939
972
|
}
|
|
940
973
|
|
|
941
|
-
console.log('Found CAWS working spec');
|
|
942
|
-
|
|
943
974
|
// Initialize empty chain
|
|
944
975
|
const initialChain = [];
|
|
945
976
|
await saveProvenanceChain(initialChain, output);
|
|
@@ -947,6 +978,11 @@ async function initProvenance(options) {
|
|
|
947
978
|
|
|
948
979
|
// Create environment configuration hints
|
|
949
980
|
const envConfig = {
|
|
981
|
+
spec: {
|
|
982
|
+
id: resolved.spec.id,
|
|
983
|
+
path: resolved.path,
|
|
984
|
+
type: resolved.type,
|
|
985
|
+
},
|
|
950
986
|
cursor_tracking_api: cursorApi || process.env.CURSOR_TRACKING_API || 'not_configured',
|
|
951
987
|
cursor_checkpoint_api: process.env.CURSOR_CHECKPOINT_API || 'not_configured',
|
|
952
988
|
cursor_project_id: process.env.CURSOR_PROJECT_ID || 'not_configured',
|