@paths.design/caws-cli 7.0.1 ā 7.0.2
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/commands/diagnose.js +3 -2
- package/dist/commands/init.js +23 -3
- package/dist/commands/templates.js +10 -0
- package/dist/commands/validate.js +12 -0
- package/dist/generators/working-spec.js +26 -6
- package/dist/index.js +3 -1
- package/dist/scaffold/cursor-hooks.js +10 -2
- package/dist/scaffold/git-hooks.js +62 -3
- package/dist/scaffold/index.js +57 -15
- package/dist/utils/detection.js +30 -3
- package/dist/validation/spec-validation.js +26 -2
- package/package.json +1 -1
|
@@ -481,9 +481,10 @@ async function diagnoseCommand(options = {}) {
|
|
|
481
481
|
|
|
482
482
|
if (issueCount === 0) {
|
|
483
483
|
console.log(chalk.blue('š Next steps:'));
|
|
484
|
-
console.log(chalk.blue(' ⢠Run: caws status to view project health'));
|
|
484
|
+
console.log(chalk.blue(' ⢠Run: caws status --visual to view project health'));
|
|
485
485
|
console.log(chalk.blue(' ⢠Run: caws validate to check working spec'));
|
|
486
|
-
console.log(chalk.blue(' ā¢
|
|
486
|
+
console.log(chalk.blue(' ⢠Optional: Create .caws/policy.yaml for custom budgets'));
|
|
487
|
+
console.log(chalk.blue(' ⢠Start implementing: caws iterate --current-state "Ready to begin"'));
|
|
487
488
|
}
|
|
488
489
|
} catch (error) {
|
|
489
490
|
console.error(chalk.red('\nā Error running diagnosis:'), error.message);
|
package/dist/commands/init.js
CHANGED
|
@@ -481,11 +481,31 @@ Happy coding! šÆ
|
|
|
481
481
|
console.log(chalk.blue('\nNext steps:'));
|
|
482
482
|
console.log('1. Review .caws/working-spec.yaml');
|
|
483
483
|
console.log('2. Customize the specification for your needs');
|
|
484
|
-
|
|
484
|
+
|
|
485
|
+
// Show contract requirements if Tier 1 or 2
|
|
486
|
+
// Use answers if available (interactive mode), otherwise default to 2
|
|
487
|
+
const riskTier = answers?.riskTier || 2;
|
|
488
|
+
if (riskTier === 1 || riskTier === 2) {
|
|
489
|
+
console.log(chalk.yellow('\nā ļø Important: Contract Requirements'));
|
|
490
|
+
console.log(` Tier ${riskTier} changes require at least one contract.`);
|
|
491
|
+
console.log(' For infrastructure/setup work, add a minimal contract:');
|
|
492
|
+
console.log(chalk.gray(' contracts:'));
|
|
493
|
+
console.log(chalk.gray(' - type: "project_setup"'));
|
|
494
|
+
console.log(chalk.gray(' path: ".caws/working-spec.yaml"'));
|
|
495
|
+
console.log(chalk.gray(' description: "Project-level CAWS configuration"'));
|
|
496
|
+
console.log(' Or use "chore" mode for maintenance work (mode: chore)');
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
console.log('\nš Recommended Setup Workflow:');
|
|
500
|
+
console.log(' 1. Review .caws/working-spec.yaml');
|
|
501
|
+
console.log(' 2. Run: caws scaffold (adds tools and templates)');
|
|
502
|
+
console.log(' 3. Run: caws validate (verify setup)');
|
|
503
|
+
console.log(' 4. Run: caws diagnose (check health)');
|
|
504
|
+
console.log(' 5. Optional: Create .caws/policy.yaml for custom budgets');
|
|
485
505
|
if (answers?.useCursorHooks || options.interactive === false) {
|
|
486
|
-
console.log('
|
|
506
|
+
console.log(' 6. Restart Cursor IDE to activate quality gates');
|
|
487
507
|
}
|
|
488
|
-
console.log('
|
|
508
|
+
console.log('\nš” Quick start: caws scaffold && caws validate && caws diagnose');
|
|
489
509
|
} catch (error) {
|
|
490
510
|
console.error(chalk.red('ā Error during initialization:'), error.message);
|
|
491
511
|
if (error.stack) {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
const fs = require('fs-extra');
|
|
8
8
|
const path = require('path');
|
|
9
9
|
const chalk = require('chalk');
|
|
10
|
+
const { findPackageRoot } = require('../utils/detection');
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Built-in template definitions
|
|
@@ -64,11 +65,20 @@ const BUILTIN_TEMPLATES = {
|
|
|
64
65
|
|
|
65
66
|
/**
|
|
66
67
|
* Get template directory path
|
|
68
|
+
* Works in both development and global install scenarios
|
|
67
69
|
* @returns {string|null} Template directory path or null
|
|
68
70
|
*/
|
|
69
71
|
function getTemplateDir() {
|
|
72
|
+
// Find package root using shared utility
|
|
73
|
+
const packageRoot = findPackageRoot(__dirname);
|
|
74
|
+
|
|
70
75
|
const possiblePaths = [
|
|
76
|
+
// First: Try templates relative to package root (works in dev and global install)
|
|
77
|
+
path.join(packageRoot, 'templates'),
|
|
78
|
+
// Fallback: Try relative to current file location (for development)
|
|
71
79
|
path.join(__dirname, '../../templates'),
|
|
80
|
+
path.join(__dirname, '../templates'),
|
|
81
|
+
// Legacy fallbacks
|
|
72
82
|
path.join(process.cwd(), 'packages/caws-cli/templates'),
|
|
73
83
|
path.join(process.cwd(), 'templates'),
|
|
74
84
|
];
|
|
@@ -194,6 +194,18 @@ async function validateCommand(specFile, options = {}) {
|
|
|
194
194
|
if (error.suggestion) {
|
|
195
195
|
console.log(` ${chalk.blue('š” ' + error.suggestion)}`);
|
|
196
196
|
}
|
|
197
|
+
if (error.example) {
|
|
198
|
+
console.log(` ${chalk.gray('Example:')}`);
|
|
199
|
+
if (error.example.contracts) {
|
|
200
|
+
error.example.contracts.forEach((contract) => {
|
|
201
|
+
console.log(` ${chalk.gray('- type: ' + contract.type)}`);
|
|
202
|
+
console.log(` ${chalk.gray('path: ' + contract.path)}`);
|
|
203
|
+
if (contract.description) {
|
|
204
|
+
console.log(` ${chalk.gray('description: ' + contract.description)}`);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
197
209
|
});
|
|
198
210
|
|
|
199
211
|
// Show warnings
|
|
@@ -97,12 +97,32 @@ function generateWorkingSpec(answers) {
|
|
|
97
97
|
.map((s) => s.trim())
|
|
98
98
|
.filter((s) => s),
|
|
99
99
|
},
|
|
100
|
-
contracts:
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
100
|
+
contracts: (() => {
|
|
101
|
+
// If contract type/path provided, use it
|
|
102
|
+
if (answers.contractType && answers.contractPath) {
|
|
103
|
+
return [
|
|
104
|
+
{
|
|
105
|
+
type: answers.contractType,
|
|
106
|
+
path: answers.contractPath,
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// For Tier 1 & 2, provide minimal project_setup contract if none specified
|
|
112
|
+
const riskTier = answers.riskTier || 2;
|
|
113
|
+
if (riskTier === 1 || riskTier === 2) {
|
|
114
|
+
return [
|
|
115
|
+
{
|
|
116
|
+
type: 'project_setup',
|
|
117
|
+
path: '.caws/working-spec.yaml',
|
|
118
|
+
description: 'Project-level CAWS configuration. Feature-specific contracts will be added as features are developed.',
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Tier 3 doesn't require contracts
|
|
124
|
+
return [];
|
|
125
|
+
})(),
|
|
106
126
|
observability: {
|
|
107
127
|
logs: (answers.observabilityLogs || '')
|
|
108
128
|
.split(',')
|
package/dist/index.js
CHANGED
|
@@ -138,7 +138,7 @@ program
|
|
|
138
138
|
.option('--json', 'Output machine-readable JSON to stdout', false)
|
|
139
139
|
.option(
|
|
140
140
|
'--gates <gates>',
|
|
141
|
-
'Run only specific gates (comma-separated: naming,code_freeze,duplication,god_objects,documentation)',
|
|
141
|
+
'Run only specific gates (comma-separated: naming,code_freeze,duplication,god_objects,hidden-todo,documentation,placeholders)',
|
|
142
142
|
''
|
|
143
143
|
)
|
|
144
144
|
.option('--fix', 'Attempt automatic fixes (experimental)', false)
|
|
@@ -168,7 +168,9 @@ VALID GATES:
|
|
|
168
168
|
code_freeze Enforce code freeze compliance
|
|
169
169
|
duplication Detect functional duplication
|
|
170
170
|
god_objects Prevent oversized files
|
|
171
|
+
hidden-todo Detect hidden incomplete implementations
|
|
171
172
|
documentation Check documentation quality
|
|
173
|
+
placeholders Placeholder governance (explicit degradations)
|
|
172
174
|
|
|
173
175
|
EXAMPLES:
|
|
174
176
|
# Run all gates in development mode
|
|
@@ -9,7 +9,7 @@ const path = require('path');
|
|
|
9
9
|
const chalk = require('chalk');
|
|
10
10
|
|
|
11
11
|
// Import detection utilities
|
|
12
|
-
const { detectCAWSSetup } = require('../utils/detection');
|
|
12
|
+
const { detectCAWSSetup, findPackageRoot } = require('../utils/detection');
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Scaffold Cursor hooks for a CAWS project
|
|
@@ -28,9 +28,17 @@ async function scaffoldCursorHooks(projectDir, levels = ['safety', 'quality', 's
|
|
|
28
28
|
|
|
29
29
|
// Determine template directory - prefer bundled templates
|
|
30
30
|
const setup = detectCAWSSetup(projectDir);
|
|
31
|
-
|
|
31
|
+
|
|
32
|
+
// Find package root using shared utility
|
|
33
|
+
const packageRoot = findPackageRoot(__dirname);
|
|
34
|
+
|
|
35
|
+
// Try templates relative to package root first (works in both dev and global install)
|
|
36
|
+
const bundledTemplateDir = path.join(packageRoot, 'templates');
|
|
37
|
+
const fallbackTemplateDir = path.join(__dirname, '../../templates');
|
|
32
38
|
const templateDir = fs.existsSync(bundledTemplateDir)
|
|
33
39
|
? bundledTemplateDir
|
|
40
|
+
: fs.existsSync(fallbackTemplateDir)
|
|
41
|
+
? fallbackTemplateDir
|
|
34
42
|
: setup.templateDir || path.resolve(__dirname, '../templates');
|
|
35
43
|
|
|
36
44
|
const cursorTemplateDir = path.join(templateDir, '.cursor');
|
|
@@ -364,12 +364,71 @@ fi
|
|
|
364
364
|
# Run full validation suite
|
|
365
365
|
if command -v caws >/dev/null 2>&1; then
|
|
366
366
|
echo "š Running comprehensive CAWS validation..."
|
|
367
|
-
|
|
367
|
+
|
|
368
|
+
# Run validation and capture output
|
|
369
|
+
VALIDATION_OUTPUT=$(caws validate 2>&1)
|
|
370
|
+
VALIDATION_EXIT=$?
|
|
371
|
+
|
|
372
|
+
if [ $VALIDATION_EXIT -eq 0 ]; then
|
|
368
373
|
echo "ā
CAWS validation passed"
|
|
369
374
|
else
|
|
370
375
|
echo "ā CAWS validation failed"
|
|
371
|
-
echo "
|
|
372
|
-
echo "
|
|
376
|
+
echo ""
|
|
377
|
+
echo "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"
|
|
378
|
+
echo "Validation Errors:"
|
|
379
|
+
echo "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"
|
|
380
|
+
echo "$VALIDATION_OUTPUT" | grep -E "(ā|error|Error|Missing|required)" || echo "$VALIDATION_OUTPUT"
|
|
381
|
+
echo ""
|
|
382
|
+
|
|
383
|
+
# Check for contract-related errors
|
|
384
|
+
if echo "$VALIDATION_OUTPUT" | grep -qi "contract"; then
|
|
385
|
+
echo "š” Contract Requirements:"
|
|
386
|
+
echo " ⢠Tier 1 & 2 changes require at least one contract"
|
|
387
|
+
echo " ⢠For infrastructure/setup work, use 'chore' mode or add a minimal contract:"
|
|
388
|
+
echo ""
|
|
389
|
+
echo " Example minimal contract (.caws/working-spec.yaml):"
|
|
390
|
+
echo " contracts:"
|
|
391
|
+
echo " - type: 'project_setup'"
|
|
392
|
+
echo " path: '.caws/working-spec.yaml'"
|
|
393
|
+
echo " description: 'Project-level CAWS configuration'"
|
|
394
|
+
echo ""
|
|
395
|
+
echo " Or change mode to 'chore' for maintenance work:"
|
|
396
|
+
echo " mode: chore"
|
|
397
|
+
echo ""
|
|
398
|
+
fi
|
|
399
|
+
|
|
400
|
+
# Check for active waivers
|
|
401
|
+
echo "š Checking for active waivers..."
|
|
402
|
+
if command -v caws >/dev/null 2>&1 && caws waivers list --status=active --format=count 2>/dev/null | grep -q "[1-9]"; then
|
|
403
|
+
ACTIVE_WAIVERS=$(caws waivers list --status=active 2>/dev/null)
|
|
404
|
+
echo "ā ļø Active waivers found:"
|
|
405
|
+
echo "$ACTIVE_WAIVERS" | head -5
|
|
406
|
+
echo ""
|
|
407
|
+
echo "š” Note: Waivers may not cover all validation failures"
|
|
408
|
+
echo " Review waiver coverage: caws waivers list --status=active"
|
|
409
|
+
else
|
|
410
|
+
echo " No active waivers found"
|
|
411
|
+
echo ""
|
|
412
|
+
echo "š” If this is infrastructure/setup work, you can create a waiver:"
|
|
413
|
+
echo " caws waivers create \\"
|
|
414
|
+
echo " --title='Initial CAWS setup' \\"
|
|
415
|
+
echo " --reason=infrastructure_limitation \\"
|
|
416
|
+
echo " --gates=contracts \\"
|
|
417
|
+
echo " --expires-at='2024-12-31T23:59:59Z' \\"
|
|
418
|
+
echo " --approved-by='@your-team' \\"
|
|
419
|
+
echo " --impact-level=low \\"
|
|
420
|
+
echo " --mitigation-plan='Contracts will be added as features are developed'"
|
|
421
|
+
fi
|
|
422
|
+
|
|
423
|
+
echo ""
|
|
424
|
+
echo "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"
|
|
425
|
+
echo "Next Steps:"
|
|
426
|
+
echo " 1. Review errors above"
|
|
427
|
+
echo " 2. Fix issues in .caws/working-spec.yaml"
|
|
428
|
+
echo " 3. Run: caws validate (to verify fixes)"
|
|
429
|
+
echo " 4. Commit fixes: git commit --no-verify (allowed)"
|
|
430
|
+
echo " 5. Push again: git push"
|
|
431
|
+
echo "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"
|
|
373
432
|
exit 1
|
|
374
433
|
fi
|
|
375
434
|
fi
|
package/dist/scaffold/index.js
CHANGED
|
@@ -9,7 +9,7 @@ const path = require('path');
|
|
|
9
9
|
const chalk = require('chalk');
|
|
10
10
|
|
|
11
11
|
// Import detection utilities
|
|
12
|
-
const { detectCAWSSetup } = require('../utils/detection');
|
|
12
|
+
const { detectCAWSSetup, findPackageRoot } = require('../utils/detection');
|
|
13
13
|
const { detectsPublishing } = require('../utils/project-analysis');
|
|
14
14
|
|
|
15
15
|
// Import git hooks scaffolding
|
|
@@ -23,8 +23,33 @@ const CLI_VERSION = require('../../package.json').version;
|
|
|
23
23
|
* @param {string} targetDir - Target directory for scaffolding
|
|
24
24
|
* @param {Object} options - Scaffold options
|
|
25
25
|
*/
|
|
26
|
+
/**
|
|
27
|
+
* Find the template directory using robust path resolution
|
|
28
|
+
* Works in both development and global install scenarios
|
|
29
|
+
* @returns {string|null} Template directory path or null
|
|
30
|
+
*/
|
|
31
|
+
function findTemplateDir() {
|
|
32
|
+
// Find package root using shared utility
|
|
33
|
+
const packageRoot = findPackageRoot(__dirname);
|
|
34
|
+
|
|
35
|
+
// Try templates relative to package root first (works in both dev and global install)
|
|
36
|
+
const possiblePaths = [
|
|
37
|
+
path.join(packageRoot, 'templates'),
|
|
38
|
+
path.resolve(__dirname, '../../templates'), // Dev fallback
|
|
39
|
+
path.resolve(__dirname, '../templates'), // Legacy fallback
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
for (const testPath of possiblePaths) {
|
|
43
|
+
if (fs.existsSync(testPath)) {
|
|
44
|
+
return testPath;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
26
51
|
async function scaffoldIDEIntegrations(targetDir, options) {
|
|
27
|
-
const templateDir = path.join(__dirname, '../../templates');
|
|
52
|
+
const templateDir = findTemplateDir() || path.join(__dirname, '../../templates');
|
|
28
53
|
|
|
29
54
|
console.log(chalk.cyan('šØ Setting up IDE integrations...'));
|
|
30
55
|
|
|
@@ -192,19 +217,24 @@ async function scaffoldProject(options) {
|
|
|
192
217
|
setup.templateDir = cawsSetup.templateDir;
|
|
193
218
|
setup.hasTemplateDir = true;
|
|
194
219
|
} else if (!setup.templateDir) {
|
|
195
|
-
// Try to find template directory using
|
|
220
|
+
// Try to find template directory using robust path resolution
|
|
196
221
|
const possiblePaths = [
|
|
197
|
-
// 1.
|
|
222
|
+
// 1. Use the helper function to find templates (works in dev and global install)
|
|
223
|
+
findTemplateDir(),
|
|
224
|
+
// 2. Bundled templates relative to package root
|
|
225
|
+
path.join(findPackageRoot(__dirname), 'templates'),
|
|
226
|
+
// 3. Legacy fallback paths
|
|
198
227
|
path.join(__dirname, '../../templates'),
|
|
199
|
-
|
|
228
|
+
path.join(__dirname, '../templates'),
|
|
229
|
+
// 4. CI paths
|
|
200
230
|
'/home/runner/work/coding-agent-working-standard/coding-agent-working-standard/packages/caws-template',
|
|
201
231
|
'/workspace/packages/caws-template',
|
|
202
232
|
'/caws/packages/caws-template',
|
|
203
|
-
//
|
|
233
|
+
// 5. Monorepo relative paths
|
|
204
234
|
path.resolve(process.cwd(), '../../../packages/caws-template'),
|
|
205
235
|
path.resolve(process.cwd(), '../../packages/caws-template'),
|
|
206
236
|
path.resolve(process.cwd(), '../packages/caws-template'),
|
|
207
|
-
];
|
|
237
|
+
].filter(Boolean); // Remove null values
|
|
208
238
|
|
|
209
239
|
for (const testPath of possiblePaths) {
|
|
210
240
|
if (fs.existsSync(testPath)) {
|
|
@@ -567,10 +597,22 @@ async function scaffoldProject(options) {
|
|
|
567
597
|
console.warn(chalk.yellow(`ā ļø Failed to add ${enhancement.name}:`), copyError.message);
|
|
568
598
|
}
|
|
569
599
|
} else {
|
|
570
|
-
// If source doesn't exist in template,
|
|
600
|
+
// If source doesn't exist in template, check if it should be a file or directory
|
|
571
601
|
try {
|
|
572
|
-
|
|
573
|
-
|
|
602
|
+
// Check if the enhancement name looks like a file (has extension)
|
|
603
|
+
const hasExtension = path.extname(enhancement.name).length > 0;
|
|
604
|
+
|
|
605
|
+
if (hasExtension) {
|
|
606
|
+
// Create an empty file for file-like enhancements
|
|
607
|
+
await fs.ensureDir(path.dirname(destPath));
|
|
608
|
+
await fs.writeFile(destPath, '');
|
|
609
|
+
console.log(chalk.yellow(`ā ļø Created empty ${enhancement.description} (template not found)`));
|
|
610
|
+
console.log(chalk.gray(` Template expected at: ${sourcePath}`));
|
|
611
|
+
} else {
|
|
612
|
+
// Create directory for directory-like enhancements
|
|
613
|
+
await fs.ensureDir(destPath);
|
|
614
|
+
console.log(chalk.green(`ā
Created ${enhancement.description}`));
|
|
615
|
+
}
|
|
574
616
|
addedCount++;
|
|
575
617
|
addedFiles.push(enhancement.name);
|
|
576
618
|
} catch (createError) {
|
|
@@ -629,19 +671,19 @@ async function scaffoldProject(options) {
|
|
|
629
671
|
console.log('3. Push to trigger automated publishing');
|
|
630
672
|
console.log('4. Your existing CAWS tools remain unchanged');
|
|
631
673
|
} else {
|
|
632
|
-
console.log('2.
|
|
633
|
-
console.log('3. Run
|
|
674
|
+
console.log('2. Run: caws validate (verify setup)');
|
|
675
|
+
console.log('3. Run: caws diagnose (check project health)');
|
|
676
|
+
console.log('4. Customize .caws/working-spec.yaml for your project');
|
|
677
|
+
console.log('5. Optional: Create .caws/policy.yaml for tier-specific budgets');
|
|
634
678
|
if (!qualityGatesAdded && !options.minimal) {
|
|
635
679
|
console.log(
|
|
636
|
-
chalk.gray('
|
|
680
|
+
chalk.gray('6. Note: Quality gates scripts skipped (git hooks use CAWS CLI by default)')
|
|
637
681
|
);
|
|
638
682
|
console.log(
|
|
639
683
|
chalk.gray(
|
|
640
684
|
' Add --with-quality-gates flag if you want local scripts without global CLI'
|
|
641
685
|
)
|
|
642
686
|
);
|
|
643
|
-
} else {
|
|
644
|
-
console.log('4. Your existing CAWS tools remain unchanged');
|
|
645
687
|
}
|
|
646
688
|
}
|
|
647
689
|
}
|
package/dist/utils/detection.js
CHANGED
|
@@ -8,6 +8,25 @@ const fs = require('fs-extra');
|
|
|
8
8
|
const path = require('path');
|
|
9
9
|
const chalk = require('chalk');
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Find the package root directory by looking for package.json
|
|
13
|
+
* Works in both development (src/) and production (dist/) scenarios
|
|
14
|
+
* @param {string} startDir - Directory to start searching from (defaults to __dirname)
|
|
15
|
+
* @returns {string} Package root directory path
|
|
16
|
+
*/
|
|
17
|
+
function findPackageRoot(startDir = __dirname) {
|
|
18
|
+
let packageRoot = startDir;
|
|
19
|
+
for (let i = 0; i < 5; i++) {
|
|
20
|
+
const packageJsonPath = path.join(packageRoot, 'package.json');
|
|
21
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
22
|
+
return packageRoot;
|
|
23
|
+
}
|
|
24
|
+
packageRoot = path.dirname(packageRoot);
|
|
25
|
+
}
|
|
26
|
+
// Fallback to startDir if package.json not found
|
|
27
|
+
return startDir;
|
|
28
|
+
}
|
|
29
|
+
|
|
11
30
|
/**
|
|
12
31
|
* Detect CAWS setup in a directory
|
|
13
32
|
* @param {string} cwd - Current working directory
|
|
@@ -93,10 +112,17 @@ function detectCAWSSetup(cwd = process.cwd()) {
|
|
|
93
112
|
|
|
94
113
|
// Check for template directory - try multiple possible locations
|
|
95
114
|
let templateDir = null;
|
|
115
|
+
|
|
116
|
+
// Find package root using shared utility
|
|
117
|
+
const packageRoot = findPackageRoot(__dirname);
|
|
118
|
+
|
|
96
119
|
const possibleTemplatePaths = [
|
|
97
|
-
// FIRST: Try bundled templates (
|
|
98
|
-
{ path: path.
|
|
99
|
-
|
|
120
|
+
// FIRST: Try bundled templates relative to package root (works in dev and global install)
|
|
121
|
+
{ path: path.join(packageRoot, 'templates'), source: 'bundled with CLI (package root)' },
|
|
122
|
+
// Fallback: Try relative to current file location (for development)
|
|
123
|
+
{ path: path.resolve(__dirname, '../../templates'), source: 'bundled with CLI (dev fallback)' },
|
|
124
|
+
{ path: path.resolve(__dirname, '../templates'), source: 'bundled with CLI (legacy fallback)' },
|
|
125
|
+
{ path: path.resolve(__dirname, 'templates'), source: 'bundled with CLI (legacy fallback 2)' },
|
|
100
126
|
// Try relative to current working directory (for monorepo setups)
|
|
101
127
|
{ path: path.resolve(cwd, '../caws-template'), source: 'monorepo parent directory' },
|
|
102
128
|
{ path: path.resolve(cwd, '../../caws-template'), source: 'monorepo grandparent' },
|
|
@@ -171,4 +197,5 @@ function detectCAWSSetup(cwd = process.cwd()) {
|
|
|
171
197
|
|
|
172
198
|
module.exports = {
|
|
173
199
|
detectCAWSSetup,
|
|
200
|
+
findPackageRoot,
|
|
174
201
|
};
|
|
@@ -325,11 +325,35 @@ function validateWorkingSpecWithSuggestions(spec, options = {}) {
|
|
|
325
325
|
// Tier-specific validations
|
|
326
326
|
if (spec.risk_tier === 1 || spec.risk_tier === 2) {
|
|
327
327
|
if (!spec.contracts || spec.contracts.length === 0) {
|
|
328
|
+
const isChoreMode = spec.mode === 'chore';
|
|
329
|
+
const suggestion = isChoreMode
|
|
330
|
+
? 'For infrastructure/setup work, add a minimal project_setup contract or create a waiver'
|
|
331
|
+
: 'Add API contracts (OpenAPI, GraphQL, etc.) or change mode to "chore" for maintenance work';
|
|
332
|
+
|
|
328
333
|
errors.push({
|
|
329
334
|
instancePath: '/contracts',
|
|
330
|
-
message:
|
|
331
|
-
suggestion:
|
|
335
|
+
message: `Contracts required for Tier ${spec.risk_tier} changes`,
|
|
336
|
+
suggestion: suggestion,
|
|
332
337
|
canAutoFix: false,
|
|
338
|
+
example: isChoreMode
|
|
339
|
+
? {
|
|
340
|
+
contracts: [
|
|
341
|
+
{
|
|
342
|
+
type: 'project_setup',
|
|
343
|
+
path: '.caws/working-spec.yaml',
|
|
344
|
+
description: 'Project-level CAWS configuration. Feature-specific contracts will be added as features are developed.',
|
|
345
|
+
},
|
|
346
|
+
],
|
|
347
|
+
}
|
|
348
|
+
: {
|
|
349
|
+
contracts: [
|
|
350
|
+
{
|
|
351
|
+
type: 'openapi',
|
|
352
|
+
path: 'docs/api/feature.yaml',
|
|
353
|
+
version: '1.0.0',
|
|
354
|
+
},
|
|
355
|
+
],
|
|
356
|
+
},
|
|
333
357
|
});
|
|
334
358
|
}
|
|
335
359
|
}
|