@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.
@@ -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(' • Run: node apps/tools/caws/gates.js for quality gates'));
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);
@@ -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
- console.log('3. Run "caws validate" to check your setup');
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('4. Restart Cursor IDE to activate quality gates');
506
+ console.log(' 6. Restart Cursor IDE to activate quality gates');
487
507
  }
488
- console.log('5. Start implementing your features!');
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
- type: answers.contractType || '',
103
- path: answers.contractPath || '',
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
- const bundledTemplateDir = path.join(__dirname, '../../templates');
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
- if caws validate --quiet; then
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 "šŸ’” Fix issues locally, then push again"
372
- echo "šŸ’” You can commit fixes with: git commit --no-verify"
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
@@ -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 absolute paths that work in CI
220
+ // Try to find template directory using robust path resolution
196
221
  const possiblePaths = [
197
- // 1. Bundled templates in CLI package (for global installs) - CHECK THIS FIRST!
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
- // 2. CI paths
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
- // 3. Monorepo relative paths
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, create the directory structure
600
+ // If source doesn't exist in template, check if it should be a file or directory
571
601
  try {
572
- await fs.ensureDir(destPath);
573
- console.log(chalk.green(`āœ… Created ${enhancement.description}`));
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. Customize your working spec in .caws/working-spec.yaml');
633
- console.log('3. Run validation: caws validate --suggestions');
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('4. Note: Quality gates scripts skipped (git hooks use CAWS CLI by default)')
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
  }
@@ -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 (for npm-installed CLI)
98
- { path: path.resolve(__dirname, '../templates'), source: 'bundled with CLI' },
99
- { path: path.resolve(__dirname, 'templates'), source: 'bundled with CLI (fallback)' },
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: 'Contracts required for Tier 1 and 2 changes',
331
- suggestion: 'Specify API contracts (OpenAPI, GraphQL, etc.)',
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paths.design/caws-cli",
3
- "version": "7.0.1",
3
+ "version": "7.0.2",
4
4
  "description": "CAWS CLI - Coding Agent Workflow System command line tools",
5
5
  "main": "dist/index.js",
6
6
  "bin": {