@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
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
const fs = require('fs-extra');
|
|
9
9
|
const path = require('path');
|
|
10
10
|
const yaml = require('js-yaml');
|
|
11
|
+
const { createValidator, getSchemaPath } = require('../utils/schema-validator');
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Policy Manager - Handles policy loading with intelligent caching
|
|
@@ -86,6 +87,34 @@ class PolicyManager {
|
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
if (policyPath && policyContent) {
|
|
90
|
+
// Schema validation (warn and fall back to defaults on failure)
|
|
91
|
+
try {
|
|
92
|
+
const schemaFilePath = getSchemaPath('policy.schema.json', projectRoot);
|
|
93
|
+
const validate = createValidator(schemaFilePath);
|
|
94
|
+
const schemaResult = validate(policyContent);
|
|
95
|
+
if (!schemaResult.valid) {
|
|
96
|
+
console.warn('Policy has schema violations:', schemaResult.errors);
|
|
97
|
+
console.warn('Falling back to default policy');
|
|
98
|
+
const defaultPolicy = this.getDefaultPolicy();
|
|
99
|
+
if (this.enableCaching) {
|
|
100
|
+
this.policyCache.set(projectRoot, {
|
|
101
|
+
policy: defaultPolicy,
|
|
102
|
+
cachedAt: Date.now(),
|
|
103
|
+
ttl: cacheTTL,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
...defaultPolicy,
|
|
108
|
+
_isDefault: true,
|
|
109
|
+
_schemaErrors: schemaResult.errors,
|
|
110
|
+
_cacheHit: false,
|
|
111
|
+
_loadDuration: Date.now() - startTime,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
} catch (schemaErr) {
|
|
115
|
+
console.warn('Could not validate policy schema:', schemaErr.message);
|
|
116
|
+
}
|
|
117
|
+
|
|
89
118
|
// Validate policy structure
|
|
90
119
|
this.validatePolicy(policyContent);
|
|
91
120
|
|
|
@@ -299,27 +328,30 @@ class PolicyManager {
|
|
|
299
328
|
gates: {
|
|
300
329
|
budget_limit: {
|
|
301
330
|
enabled: true,
|
|
331
|
+
mode: 'block',
|
|
302
332
|
description: 'Enforce change budget limits',
|
|
303
333
|
},
|
|
304
334
|
spec_completeness: {
|
|
305
335
|
enabled: true,
|
|
336
|
+
mode: 'block',
|
|
306
337
|
description: 'Require complete working specifications',
|
|
307
338
|
},
|
|
308
|
-
|
|
309
|
-
enabled: true,
|
|
310
|
-
description: 'Validate API contracts',
|
|
311
|
-
},
|
|
312
|
-
coverage_threshold: {
|
|
339
|
+
scope_boundary: {
|
|
313
340
|
enabled: true,
|
|
314
|
-
|
|
341
|
+
mode: 'block',
|
|
342
|
+
description: 'Enforce spec scope boundaries',
|
|
315
343
|
},
|
|
316
|
-
|
|
344
|
+
god_object: {
|
|
317
345
|
enabled: true,
|
|
318
|
-
|
|
346
|
+
mode: 'warn',
|
|
347
|
+
thresholds: { warning: 1750, critical: 2000 },
|
|
348
|
+
description: 'Detect oversized source files',
|
|
319
349
|
},
|
|
320
|
-
|
|
350
|
+
todo_detection: {
|
|
321
351
|
enabled: true,
|
|
322
|
-
|
|
352
|
+
mode: 'warn',
|
|
353
|
+
thresholds: { min_confidence: 0.8 },
|
|
354
|
+
description: 'Scan for TODO/FIXME/HACK/XXX markers',
|
|
323
355
|
},
|
|
324
356
|
},
|
|
325
357
|
};
|
|
@@ -54,7 +54,7 @@ async function scaffoldClaudeHooks(projectDir, levels = ['safety', 'quality', 's
|
|
|
54
54
|
// Map levels to hook scripts
|
|
55
55
|
const hookMapping = {
|
|
56
56
|
safety: ['block-dangerous.sh', 'scan-secrets.sh', 'worktree-guard.sh', 'worktree-write-guard.sh', 'stop-worktree-check.sh', 'session-caws-status.sh'],
|
|
57
|
-
quality: ['quality-check.sh', 'validate-spec.sh'],
|
|
57
|
+
quality: ['quality-check.sh', 'validate-spec.sh', 'doc-frontmatter-check.sh'],
|
|
58
58
|
scope: ['scope-guard.sh', 'naming-check.sh'],
|
|
59
59
|
audit: ['audit.sh', 'session-log.sh'],
|
|
60
60
|
lite: ['block-dangerous.sh', 'scope-guard.sh', 'lite-sprawl-check.sh', 'simplification-guard.sh'],
|
|
@@ -81,6 +81,7 @@ async function scaffoldClaudeHooks(projectDir, levels = ['safety', 'quality', 's
|
|
|
81
81
|
'block-dangerous.sh',
|
|
82
82
|
'scope-guard.sh',
|
|
83
83
|
'naming-check.sh',
|
|
84
|
+
'doc-frontmatter-check.sh',
|
|
84
85
|
'lite-sprawl-check.sh',
|
|
85
86
|
'simplification-guard.sh',
|
|
86
87
|
'worktree-guard.sh',
|
|
@@ -90,6 +91,23 @@ async function scaffoldClaudeHooks(projectDir, levels = ['safety', 'quality', 's
|
|
|
90
91
|
'session-log.sh',
|
|
91
92
|
];
|
|
92
93
|
|
|
94
|
+
// Copy supporting scripts (not hooks themselves, but used by hooks)
|
|
95
|
+
const supportingScripts = [
|
|
96
|
+
'classify_command.py',
|
|
97
|
+
'test_classify_command.py',
|
|
98
|
+
'test_wrapper_smoke.sh',
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
for (const script of supportingScripts) {
|
|
102
|
+
const sourcePath = path.join(claudeHooksTemplateDir, script);
|
|
103
|
+
const destPath = path.join(claudeHooksDir, script);
|
|
104
|
+
|
|
105
|
+
if (fs.existsSync(sourcePath)) {
|
|
106
|
+
await fs.copy(sourcePath, destPath);
|
|
107
|
+
await fs.chmod(destPath, 0o755);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
93
111
|
for (const script of allHookScripts) {
|
|
94
112
|
if (enabledHooks.has(script)) {
|
|
95
113
|
const sourcePath = path.join(claudeHooksTemplateDir, script);
|
|
@@ -254,6 +272,11 @@ function generateClaudeSettings(levels, _enabledHooks) {
|
|
|
254
272
|
command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/validate-spec.sh',
|
|
255
273
|
timeout: 15,
|
|
256
274
|
},
|
|
275
|
+
{
|
|
276
|
+
type: 'command',
|
|
277
|
+
command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/doc-frontmatter-check.sh',
|
|
278
|
+
timeout: 10,
|
|
279
|
+
},
|
|
257
280
|
],
|
|
258
281
|
});
|
|
259
282
|
}
|
|
@@ -279,7 +279,7 @@ if [ -d ".caws" ]; then
|
|
|
279
279
|
var reg = JSON.parse(require('fs').readFileSync('.caws/worktrees.json', 'utf8'));
|
|
280
280
|
var wts = Object.values(reg.worktrees || {});
|
|
281
281
|
var active = wts.filter(function(w) {
|
|
282
|
-
return w.status === 'active' && w.baseBranch === '$CURRENT_BRANCH';
|
|
282
|
+
return (w.status === 'active' || w.status === 'fresh' || w.status === 'merged') && w.baseBranch === '$CURRENT_BRANCH';
|
|
283
283
|
});
|
|
284
284
|
console.log(active.length + ':' + active.map(function(w) { return w.name; }).join(','));
|
|
285
285
|
} catch(e) { console.log('0:'); }
|
|
@@ -338,7 +338,7 @@ if [ -d ".caws" ]; then
|
|
|
338
338
|
HAS_ACTIVE_WT=$(node -e "
|
|
339
339
|
try {
|
|
340
340
|
var reg = JSON.parse(require('fs').readFileSync('.caws/worktrees.json', 'utf8'));
|
|
341
|
-
var active = Object.values(reg.worktrees || {}).filter(function(w) { return w.status === 'active'; });
|
|
341
|
+
var active = Object.values(reg.worktrees || {}).filter(function(w) { return w.status === 'active' || w.status === 'fresh' || w.status === 'merged'; });
|
|
342
342
|
console.log(active.length > 0 ? 'yes' : 'no');
|
|
343
343
|
} catch(e) { console.log('no'); }
|
|
344
344
|
" 2>/dev/null)
|
|
@@ -358,110 +358,42 @@ if [ -d ".caws" ]; then
|
|
|
358
358
|
fi
|
|
359
359
|
# ===== End Multi-Agent Safety Guard =====
|
|
360
360
|
|
|
361
|
-
#
|
|
362
|
-
# 1. Try Node.js script (if exists)
|
|
363
|
-
# 2. Try CAWS CLI
|
|
364
|
-
# 3. Try Makefile target
|
|
365
|
-
# 4. Try Python scripts
|
|
366
|
-
# 5. Skip gracefully (warn only)
|
|
367
|
-
|
|
361
|
+
# Run CAWS quality gates
|
|
368
362
|
QUALITY_GATES_RAN=false
|
|
363
|
+
QUALITY_GATES_WARNED=false
|
|
369
364
|
|
|
370
|
-
#
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
exit 1
|
|
394
|
-
fi
|
|
395
|
-
fi
|
|
396
|
-
# Option 2: Legacy Node.js quality gates script (deprecated)
|
|
397
|
-
elif [ -f "scripts/quality-gates/run-quality-gates.js" ]; then
|
|
398
|
-
if command -v node >/dev/null 2>&1; then
|
|
399
|
-
echo "Running legacy Node.js quality gates script..."
|
|
400
|
-
if node scripts/quality-gates/run-quality-gates.js; then
|
|
401
|
-
echo "Quality gates passed"
|
|
402
|
-
QUALITY_GATES_RAN=true
|
|
403
|
-
else
|
|
404
|
-
echo "Quality gates failed - commit blocked"
|
|
405
|
-
echo "Fix the violations above before committing"
|
|
406
|
-
exit 1
|
|
407
|
-
fi
|
|
408
|
-
fi
|
|
409
|
-
# Option 3: CAWS CLI validation
|
|
410
|
-
elif command -v caws >/dev/null 2>&1; then
|
|
411
|
-
# In a worktree, validate only the associated spec to avoid false positives
|
|
412
|
-
CAWS_VALIDATE_ARGS="--quiet"
|
|
413
|
-
WORKTREE_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
|
|
414
|
-
if [ -f ".caws/worktrees.json" ] && command -v node >/dev/null 2>&1; then
|
|
415
|
-
SPEC_ID=$(node -e "
|
|
416
|
-
try {
|
|
417
|
-
var reg = JSON.parse(require('fs').readFileSync('.caws/worktrees.json', 'utf8'));
|
|
418
|
-
var wt = Object.values(reg.worktrees || {}).find(function(w) {
|
|
419
|
-
return w.branch === '$WORKTREE_BRANCH';
|
|
420
|
-
});
|
|
421
|
-
if (wt && wt.specId) console.log(wt.specId);
|
|
422
|
-
} catch(e) {}
|
|
423
|
-
" 2>/dev/null || echo "")
|
|
424
|
-
if [ -n "$SPEC_ID" ]; then
|
|
425
|
-
CAWS_VALIDATE_ARGS="--quiet --spec-id $SPEC_ID"
|
|
426
|
-
fi
|
|
427
|
-
fi
|
|
428
|
-
echo "Running CAWS CLI validation..."
|
|
429
|
-
if caws validate $CAWS_VALIDATE_ARGS 2>/dev/null; then
|
|
430
|
-
echo "CAWS validation passed"
|
|
431
|
-
QUALITY_GATES_RAN=true
|
|
432
|
-
else
|
|
433
|
-
echo "CAWS validation failed, but allowing commit (non-blocking)"
|
|
434
|
-
echo "Run 'caws validate' for details"
|
|
435
|
-
QUALITY_GATES_RAN=true
|
|
436
|
-
fi
|
|
437
|
-
# Option 3: Makefile target
|
|
438
|
-
elif [ -f "Makefile" ] && grep -q "caws-validate\\|caws-gates" Makefile; then
|
|
439
|
-
echo "Running Makefile quality gates..."
|
|
440
|
-
if make caws-validate >/dev/null 2>&1 || make caws-gates >/dev/null 2>&1; then
|
|
441
|
-
echo "Makefile quality gates passed"
|
|
442
|
-
QUALITY_GATES_RAN=true
|
|
443
|
-
else
|
|
444
|
-
echo "Makefile quality gates failed, but allowing commit (non-blocking)"
|
|
445
|
-
QUALITY_GATES_RAN=true
|
|
446
|
-
fi
|
|
447
|
-
# Option 4: Python scripts
|
|
448
|
-
elif [ -f "scripts/simple_gates.py" ] && command -v python3 >/dev/null 2>&1; then
|
|
449
|
-
echo "Running Python quality gates script..."
|
|
450
|
-
if python3 scripts/simple_gates.py all --tier 2 --profile backend-api >/dev/null 2>&1; then
|
|
451
|
-
echo "Python quality gates passed"
|
|
365
|
+
# Resolve spec ID from worktree context if available
|
|
366
|
+
SPEC_ID=""
|
|
367
|
+
WORKTREE_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
|
|
368
|
+
if [ -f ".caws/worktrees.json" ] && command -v node >/dev/null 2>&1; then
|
|
369
|
+
SPEC_ID=$(node -e "
|
|
370
|
+
try {
|
|
371
|
+
var reg = JSON.parse(require('fs').readFileSync('.caws/worktrees.json', 'utf8'));
|
|
372
|
+
var wt = Object.values(reg.worktrees || {}).find(function(w) {
|
|
373
|
+
return w.branch === '$WORKTREE_BRANCH';
|
|
374
|
+
});
|
|
375
|
+
if (wt && wt.specId) console.log(wt.specId);
|
|
376
|
+
} catch(e) {}
|
|
377
|
+
" 2>/dev/null || echo "")
|
|
378
|
+
fi
|
|
379
|
+
|
|
380
|
+
GATES_ARGS="--context=commit --quiet"
|
|
381
|
+
if [ -n "$SPEC_ID" ]; then
|
|
382
|
+
GATES_ARGS="$GATES_ARGS --spec-id $SPEC_ID"
|
|
383
|
+
fi
|
|
384
|
+
|
|
385
|
+
if command -v caws >/dev/null 2>&1; then
|
|
386
|
+
if caws gates run $GATES_ARGS; then
|
|
387
|
+
echo "Quality gates passed"
|
|
452
388
|
QUALITY_GATES_RAN=true
|
|
453
389
|
else
|
|
454
|
-
|
|
455
|
-
|
|
390
|
+
GATE_EXIT=$?
|
|
391
|
+
echo "Quality gates BLOCKED the commit (exit code $GATE_EXIT)"
|
|
392
|
+
exit 1
|
|
456
393
|
fi
|
|
457
|
-
# Option 5: Skip gracefully
|
|
458
394
|
else
|
|
459
|
-
echo "
|
|
460
|
-
echo "
|
|
461
|
-
echo " - Install quality gates: npm install --save-dev @paths.design/quality-gates"
|
|
462
|
-
echo " - Install CAWS CLI: npm install -g @paths.design/caws-cli"
|
|
463
|
-
echo " - Use Python: python3 scripts/simple_gates.py"
|
|
464
|
-
echo " - Use Makefile: make caws-gates"
|
|
395
|
+
echo "CAWS CLI not available — skipping quality gates"
|
|
396
|
+
echo "Install with: npm install -g @paths.design/caws-cli"
|
|
465
397
|
QUALITY_GATES_RAN=true
|
|
466
398
|
fi
|
|
467
399
|
|
|
@@ -534,7 +466,11 @@ ${todoSuggestion
|
|
|
534
466
|
fi
|
|
535
467
|
fi
|
|
536
468
|
|
|
537
|
-
|
|
469
|
+
if [ "$QUALITY_GATES_WARNED" = true ]; then
|
|
470
|
+
echo "Proceeding with commit (some quality checks had warnings)"
|
|
471
|
+
else
|
|
472
|
+
echo "All quality checks passed - proceeding with commit"
|
|
473
|
+
fi
|
|
538
474
|
exit 0
|
|
539
475
|
`;
|
|
540
476
|
}
|
|
@@ -554,6 +490,13 @@ function generatePostCommitHook() {
|
|
|
554
490
|
exit 0
|
|
555
491
|
fi
|
|
556
492
|
|
|
493
|
+
# Skip during merge operations — caws worktree merge triggers a merge
|
|
494
|
+
# commit, and writing to .caws/provenance/chain.json here would dirty
|
|
495
|
+
# the tree mid-merge, blocking subsequent operations.
|
|
496
|
+
if [ -f ".git/MERGE_HEAD" ] || [ -f "$(git rev-parse --git-dir 2>/dev/null)/MERGE_HEAD" ]; then
|
|
497
|
+
exit 0
|
|
498
|
+
fi
|
|
499
|
+
|
|
557
500
|
# Get the current commit hash
|
|
558
501
|
COMMIT_HASH=$(git rev-parse HEAD)
|
|
559
502
|
|
|
@@ -828,7 +771,7 @@ if [ -f "$CAWS_ROOT/.caws/worktrees.json" ] && command -v node >/dev/null 2>&1;
|
|
|
828
771
|
try {
|
|
829
772
|
var reg = JSON.parse(require('fs').readFileSync('$CAWS_ROOT/.caws/worktrees.json', 'utf8'));
|
|
830
773
|
var active = Object.values(reg.worktrees || {}).filter(function(w) {
|
|
831
|
-
return w.status === 'active' && w.baseBranch === '$CURRENT_BRANCH';
|
|
774
|
+
return (w.status === 'active' || w.status === 'fresh' || w.status === 'merged') && w.baseBranch === '$CURRENT_BRANCH';
|
|
832
775
|
});
|
|
833
776
|
console.log(active.length);
|
|
834
777
|
} catch(e) { console.log('0'); }
|
package/dist/scaffold/index.js
CHANGED
|
@@ -728,11 +728,12 @@ async function scaffoldProject(options) {
|
|
|
728
728
|
} else {
|
|
729
729
|
console.log('2. Run: caws validate (verify setup)');
|
|
730
730
|
console.log('3. Run: caws diagnose (check project health)');
|
|
731
|
-
console.log('4. Customize .caws/
|
|
732
|
-
console.log('5.
|
|
731
|
+
console.log('4. Customize .caws/specs/<spec-id>.yaml for your project');
|
|
732
|
+
console.log('5. Treat .caws/working-spec.yaml as a compatibility mirror');
|
|
733
|
+
console.log('6. Optional: Create .caws/policy.yaml for tier-specific budgets');
|
|
733
734
|
if (!qualityGatesAdded && !options.minimal) {
|
|
734
735
|
console.log(
|
|
735
|
-
chalk.gray('
|
|
736
|
+
chalk.gray('7. Note: Quality gates scripts skipped (git hooks use CAWS CLI by default)')
|
|
736
737
|
);
|
|
737
738
|
console.log(
|
|
738
739
|
chalk.gray(
|
|
@@ -11,6 +11,8 @@ const fs = require('fs-extra');
|
|
|
11
11
|
const path = require('path');
|
|
12
12
|
const crypto = require('crypto');
|
|
13
13
|
|
|
14
|
+
const { mergeFilesTouched } = require('../utils/working-state');
|
|
15
|
+
|
|
14
16
|
const SESSIONS_DIR = '.caws/sessions';
|
|
15
17
|
const REGISTRY_FILE = '.caws/sessions.json';
|
|
16
18
|
const CAPSULE_SCHEMA_VERSION = 'caws.capsule.v1';
|
|
@@ -79,16 +81,62 @@ function getWorkspaceFingerprint(cwd) {
|
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
/**
|
|
82
|
-
*
|
|
84
|
+
* Load the best available spec synchronously (feature specs first, then legacy).
|
|
85
|
+
* @param {string} root - Repository root
|
|
86
|
+
* @param {string} [specId] - Optional specific spec ID
|
|
87
|
+
* @returns {object|null} Parsed spec object or null
|
|
88
|
+
*/
|
|
89
|
+
function loadBestSpecSync(root, specId) {
|
|
90
|
+
const yaml = require('js-yaml');
|
|
91
|
+
|
|
92
|
+
// If a specific spec ID is requested, load it directly
|
|
93
|
+
if (specId) {
|
|
94
|
+
for (const ext of ['.yaml', '.yml']) {
|
|
95
|
+
const p = path.join(root, '.caws/specs', `${specId}${ext}`);
|
|
96
|
+
if (fs.existsSync(p)) {
|
|
97
|
+
try { return yaml.load(fs.readFileSync(p, 'utf8')); } catch { /* skip */ }
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Check registry for active feature specs
|
|
103
|
+
const registryPath = path.join(root, '.caws/specs/registry.json');
|
|
104
|
+
if (fs.existsSync(registryPath)) {
|
|
105
|
+
try {
|
|
106
|
+
const registry = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
|
107
|
+
const specs = registry.specs || {};
|
|
108
|
+
const activeIds = Object.keys(specs).filter(
|
|
109
|
+
id => specs[id].status !== 'closed' && specs[id].status !== 'archived'
|
|
110
|
+
);
|
|
111
|
+
for (const id of activeIds) {
|
|
112
|
+
const specEntry = specs[id];
|
|
113
|
+
const p = path.join(root, '.caws/specs', specEntry.path || `${id}.yaml`);
|
|
114
|
+
if (fs.existsSync(p)) {
|
|
115
|
+
try { return yaml.load(fs.readFileSync(p, 'utf8')); } catch { /* skip */ }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
} catch { /* fall through */ }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Legacy fallback
|
|
122
|
+
const legacyPath = path.join(root, '.caws/working-spec.yaml');
|
|
123
|
+
if (fs.existsSync(legacyPath)) {
|
|
124
|
+
try { return yaml.load(fs.readFileSync(legacyPath, 'utf8')); } catch { /* skip */ }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get project name from best available spec or directory
|
|
83
132
|
* @param {string} root - Repository root
|
|
133
|
+
* @param {string} [specId] - Optional specific spec ID
|
|
84
134
|
* @returns {string}
|
|
85
135
|
*/
|
|
86
|
-
function getProjectName(root) {
|
|
136
|
+
function getProjectName(root, specId) {
|
|
87
137
|
try {
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
if (fs.existsSync(specPath)) {
|
|
91
|
-
const spec = yaml.load(fs.readFileSync(specPath, 'utf8'));
|
|
138
|
+
const spec = loadBestSpecSync(root, specId);
|
|
139
|
+
if (spec) {
|
|
92
140
|
return spec.title || spec.id || path.basename(root);
|
|
93
141
|
}
|
|
94
142
|
} catch {
|
|
@@ -98,16 +146,15 @@ function getProjectName(root) {
|
|
|
98
146
|
}
|
|
99
147
|
|
|
100
148
|
/**
|
|
101
|
-
* Get skein ID from
|
|
149
|
+
* Get skein ID from best available spec
|
|
102
150
|
* @param {string} root - Repository root
|
|
151
|
+
* @param {string} [specId] - Optional specific spec ID
|
|
103
152
|
* @returns {string}
|
|
104
153
|
*/
|
|
105
|
-
function getSkeinId(root) {
|
|
154
|
+
function getSkeinId(root, specId) {
|
|
106
155
|
try {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
if (fs.existsSync(specPath)) {
|
|
110
|
-
const spec = yaml.load(fs.readFileSync(specPath, 'utf8'));
|
|
156
|
+
const spec = loadBestSpecSync(root, specId);
|
|
157
|
+
if (spec) {
|
|
111
158
|
return spec.id || 'unknown';
|
|
112
159
|
}
|
|
113
160
|
} catch {
|
|
@@ -201,8 +248,8 @@ function startSession(options = {}) {
|
|
|
201
248
|
|
|
202
249
|
const capsule = {
|
|
203
250
|
schema: CAPSULE_SCHEMA_VERSION,
|
|
204
|
-
project: getProjectName(root),
|
|
205
|
-
skein_id: getSkeinId(root),
|
|
251
|
+
project: getProjectName(root, specId),
|
|
252
|
+
skein_id: getSkeinId(root, specId),
|
|
206
253
|
session_id: sessionId,
|
|
207
254
|
role,
|
|
208
255
|
spec_id: specId || null,
|
|
@@ -318,6 +365,11 @@ function checkpointSession(data = {}) {
|
|
|
318
365
|
// Write updated capsule
|
|
319
366
|
fs.writeFileSync(capsulePath, JSON.stringify(capsule, null, 2));
|
|
320
367
|
|
|
368
|
+
// Bridge to working state (per-spec)
|
|
369
|
+
if (capsule.spec_id && capsule.work_summary.paths_touched.length > 0) {
|
|
370
|
+
try { mergeFilesTouched(capsule.spec_id, capsule.work_summary.paths_touched, root); } catch { /* non-fatal */ }
|
|
371
|
+
}
|
|
372
|
+
|
|
321
373
|
return capsule;
|
|
322
374
|
}
|
|
323
375
|
|
|
@@ -385,6 +437,11 @@ function endSession(data = {}) {
|
|
|
385
437
|
// Write finalized capsule
|
|
386
438
|
fs.writeFileSync(capsulePath, JSON.stringify(capsule, null, 2));
|
|
387
439
|
|
|
440
|
+
// Bridge to working state (per-spec)
|
|
441
|
+
if (capsule.spec_id && capsule.work_summary.paths_touched.length > 0) {
|
|
442
|
+
try { mergeFilesTouched(capsule.spec_id, capsule.work_summary.paths_touched, root); } catch { /* non-fatal */ }
|
|
443
|
+
}
|
|
444
|
+
|
|
388
445
|
// Update registry
|
|
389
446
|
registry.sessions[sessionId].status = 'completed';
|
|
390
447
|
registry.sessions[sessionId].ended_at = capsule.ended_at;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Sidecar Registry
|
|
3
|
+
* Central registry of all bounded governance sidecars.
|
|
4
|
+
* @author @darianrosebrook
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { analyzeSpecDrift } = require('./spec-drift');
|
|
8
|
+
const { diagnoseQualityGaps } = require('./quality-gaps');
|
|
9
|
+
const { draftWaiver } = require('./waiver-draft');
|
|
10
|
+
const { summarizeProvenance } = require('./provenance-summary');
|
|
11
|
+
const { createSidecarOutput, createNoStateOutput, formatSidecarText } = require('./schema');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Registry of available sidecars.
|
|
15
|
+
* Each entry has a function and a short description for help text.
|
|
16
|
+
*/
|
|
17
|
+
const SIDECARS = {
|
|
18
|
+
drift: { fn: analyzeSpecDrift, description: 'Analyze spec drift vs implementation evidence' },
|
|
19
|
+
gaps: { fn: diagnoseQualityGaps, description: 'Diagnose quality gaps preventing phase advancement' },
|
|
20
|
+
'waiver-draft': { fn: draftWaiver, description: 'Generate pre-filled waiver templates from gate failures' },
|
|
21
|
+
provenance: { fn: summarizeProvenance, description: 'Summarize work provenance for merge readiness' },
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
SIDECARS,
|
|
26
|
+
analyzeSpecDrift,
|
|
27
|
+
diagnoseQualityGaps,
|
|
28
|
+
draftWaiver,
|
|
29
|
+
summarizeProvenance,
|
|
30
|
+
createSidecarOutput,
|
|
31
|
+
createNoStateOutput,
|
|
32
|
+
formatSidecarText,
|
|
33
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Sidecar Lifecycle Listeners
|
|
3
|
+
* Registers lightweight event handlers that print hints to run sidecars
|
|
4
|
+
* at governance-significant moments. Non-blocking, non-fatal.
|
|
5
|
+
* @author @darianrosebrook
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { lifecycle, EVENTS } = require('../utils/lifecycle-events');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Register sidecar hint listeners on the lifecycle emitter.
|
|
12
|
+
* Call once during CLI startup.
|
|
13
|
+
*/
|
|
14
|
+
function registerSidecarListeners() {
|
|
15
|
+
// On gate blocked → suggest waiver draft
|
|
16
|
+
lifecycle.on(EVENTS.GATES_BLOCKED, (payload) => {
|
|
17
|
+
try {
|
|
18
|
+
if (process.env.CAWS_QUIET === '1') return;
|
|
19
|
+
const specFlag = payload.specId ? ` --spec-id ${payload.specId}` : '';
|
|
20
|
+
const gateFlag = payload.gateName ? ` --gate ${payload.gateName}` : '';
|
|
21
|
+
console.log(
|
|
22
|
+
`\n [Sidecar] Waiver draft available: caws sidecar waiver-draft${specFlag}${gateFlag}`
|
|
23
|
+
);
|
|
24
|
+
} catch { /* non-fatal */ }
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// On phase transition (non-complete) → suggest quality gap analysis
|
|
28
|
+
lifecycle.on(EVENTS.PHASE_TRANSITION, (payload) => {
|
|
29
|
+
try {
|
|
30
|
+
if (process.env.CAWS_QUIET === '1') return;
|
|
31
|
+
if (payload.newPhase === 'complete') return;
|
|
32
|
+
const specFlag = payload.specId ? ` --spec-id ${payload.specId}` : '';
|
|
33
|
+
console.log(
|
|
34
|
+
`\n [Sidecar] Phase changed to ${payload.newPhase}. Run: caws sidecar gaps${specFlag}`
|
|
35
|
+
);
|
|
36
|
+
} catch { /* non-fatal */ }
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = { registerSidecarListeners };
|