@paths.design/caws-cli 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +295 -150
  2. package/dist/budget-derivation.d.ts +35 -0
  3. package/dist/budget-derivation.d.ts.map +1 -0
  4. package/dist/budget-derivation.js +204 -0
  5. package/dist/cicd-optimizer.d.ts +142 -0
  6. package/dist/cicd-optimizer.d.ts.map +1 -0
  7. package/dist/cicd-optimizer.js +504 -0
  8. package/dist/commands/burnup.d.ts +6 -0
  9. package/dist/commands/burnup.d.ts.map +1 -0
  10. package/dist/commands/burnup.js +90 -0
  11. package/dist/commands/init.d.ts +5 -0
  12. package/dist/commands/init.d.ts.map +1 -0
  13. package/dist/commands/init.js +514 -0
  14. package/dist/commands/provenance.d.ts +32 -0
  15. package/dist/commands/provenance.d.ts.map +1 -0
  16. package/dist/commands/provenance.js +979 -0
  17. package/dist/commands/tool.d.ts +13 -0
  18. package/dist/commands/tool.d.ts.map +1 -0
  19. package/dist/commands/tool.js +138 -0
  20. package/dist/commands/validate.d.ts +7 -0
  21. package/dist/commands/validate.d.ts.map +1 -0
  22. package/dist/commands/validate.js +80 -0
  23. package/dist/config/index.d.ts +29 -0
  24. package/dist/config/index.d.ts.map +1 -0
  25. package/dist/config/index.js +132 -0
  26. package/dist/error-handler.d.ts +50 -0
  27. package/dist/error-handler.d.ts.map +1 -0
  28. package/dist/error-handler.js +253 -0
  29. package/dist/generators/working-spec.d.ts +13 -0
  30. package/dist/generators/working-spec.d.ts.map +1 -0
  31. package/dist/generators/working-spec.js +204 -0
  32. package/dist/index.d.ts +3 -12
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +193 -2983
  35. package/dist/scaffold/cursor-hooks.d.ts +7 -0
  36. package/dist/scaffold/cursor-hooks.d.ts.map +1 -0
  37. package/dist/scaffold/cursor-hooks.js +152 -0
  38. package/dist/scaffold/git-hooks.d.ts +20 -0
  39. package/dist/scaffold/git-hooks.d.ts.map +1 -0
  40. package/dist/scaffold/git-hooks.js +417 -0
  41. package/dist/scaffold/index.d.ts +20 -0
  42. package/dist/scaffold/index.d.ts.map +1 -0
  43. package/dist/scaffold/index.js +486 -0
  44. package/dist/test-analysis.d.ts +182 -0
  45. package/dist/test-analysis.d.ts.map +1 -0
  46. package/dist/test-analysis.js +580 -0
  47. package/dist/tool-interface.d.ts +236 -0
  48. package/dist/tool-interface.d.ts.map +1 -0
  49. package/dist/tool-interface.js +314 -0
  50. package/dist/tool-loader.d.ts +77 -0
  51. package/dist/tool-loader.d.ts.map +1 -0
  52. package/dist/tool-loader.js +298 -0
  53. package/dist/tool-validator.d.ts +72 -0
  54. package/dist/tool-validator.d.ts.map +1 -0
  55. package/dist/tool-validator.js +387 -0
  56. package/dist/utils/detection.d.ts +7 -0
  57. package/dist/utils/detection.d.ts.map +1 -0
  58. package/dist/utils/detection.js +174 -0
  59. package/dist/utils/finalization.d.ts +17 -0
  60. package/dist/utils/finalization.d.ts.map +1 -0
  61. package/dist/utils/finalization.js +229 -0
  62. package/dist/utils/project-analysis.d.ts +14 -0
  63. package/dist/utils/project-analysis.d.ts.map +1 -0
  64. package/dist/utils/project-analysis.js +105 -0
  65. package/dist/validation/spec-validation.d.ts +29 -0
  66. package/dist/validation/spec-validation.d.ts.map +1 -0
  67. package/dist/validation/spec-validation.js +376 -0
  68. package/dist/waivers-manager.d.ts +167 -0
  69. package/dist/waivers-manager.d.ts.map +1 -0
  70. package/dist/waivers-manager.js +549 -0
  71. package/package.json +10 -12
  72. package/templates/.cursor/README.md +311 -0
  73. package/templates/.cursor/hooks/audit.sh +55 -0
  74. package/templates/.cursor/hooks/block-dangerous.sh +77 -0
  75. package/templates/.cursor/hooks/caws-quality-check.sh +52 -0
  76. package/templates/.cursor/hooks/caws-scope-guard.sh +74 -0
  77. package/templates/.cursor/hooks/caws-tool-validation.sh +121 -0
  78. package/templates/.cursor/hooks/format.sh +38 -0
  79. package/templates/.cursor/hooks/naming-check.sh +64 -0
  80. package/templates/.cursor/hooks/scan-secrets.sh +46 -0
  81. package/templates/.cursor/hooks/scope-guard.sh +52 -0
  82. package/templates/.cursor/hooks/validate-spec.sh +38 -0
  83. package/templates/.cursor/hooks.json +59 -0
  84. package/templates/.github/copilot/instructions.md +311 -0
  85. package/templates/.idea/runConfigurations/CAWS_Evaluate.xml +5 -0
  86. package/templates/.idea/runConfigurations/CAWS_Validate.xml +5 -0
  87. package/templates/.vscode/launch.json +56 -0
  88. package/templates/.vscode/settings.json +93 -0
  89. package/templates/.windsurf/workflows/caws-guided-development.md +92 -0
  90. package/templates/apps/tools/caws/README.md +1 -1
  91. package/templates/apps/tools/caws/schemas/working-spec.schema.json +21 -3
  92. package/templates/codemod/test.js +93 -1
  93. package/templates/apps/tools/caws/prompt-lint.js.backup +0 -274
  94. package/templates/apps/tools/caws/provenance.js.backup +0 -73
@@ -0,0 +1,979 @@
1
+ /**
2
+ * @fileoverview Provenance Command Handler
3
+ * Manages CAWS provenance tracking and audit trails
4
+ * @author @darianrosebrook
5
+ */
6
+
7
+ const fs = require('fs-extra');
8
+ const path = require('path');
9
+ const crypto = require('crypto');
10
+ const yaml = require('js-yaml');
11
+
12
+ /**
13
+ * Provenance command handler
14
+ * @param {string} subcommand - The subcommand to execute
15
+ * @param {Object} options - Command options
16
+ */
17
+ async function provenanceCommand(subcommand, options) {
18
+ try {
19
+ switch (subcommand) {
20
+ case 'update':
21
+ return await updateProvenance(options);
22
+ case 'show':
23
+ return await showProvenance(options);
24
+ case 'verify':
25
+ return await verifyProvenance(options);
26
+ case 'analyze-ai':
27
+ return await analyzeAIProvenance(options);
28
+ case 'init':
29
+ return await initProvenance(options);
30
+ case 'install-hooks':
31
+ return await installHooks(options);
32
+ default:
33
+ console.error(`❌ Unknown provenance subcommand: ${subcommand}`);
34
+ console.log('Available commands: update, show, verify, analyze-ai, init, install-hooks');
35
+ process.exit(1);
36
+ }
37
+ } catch (error) {
38
+ console.error(`❌ Provenance command failed: ${error.message}`);
39
+ process.exit(1);
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Update provenance with new commit information
45
+ * @param {Object} options - Command options
46
+ */
47
+ async function updateProvenance(options) {
48
+ const { commit, message, author, quiet = false, output = '.caws/provenance' } = options;
49
+
50
+ if (!commit) {
51
+ throw new Error('Commit hash is required for provenance update');
52
+ }
53
+
54
+ // Ensure output directory exists
55
+ await fs.ensureDir(output);
56
+
57
+ // Load current working spec
58
+ const specPath = '.caws/working-spec.yaml';
59
+ if (!(await fs.pathExists(specPath))) {
60
+ throw new Error('Working spec not found - not in CAWS project');
61
+ }
62
+
63
+ const specContent = await fs.readFile(specPath, 'utf8');
64
+ const spec = yaml.load(specContent);
65
+
66
+ // Load existing provenance chain
67
+ const provenanceChain = await loadProvenanceChain(output);
68
+
69
+ // Create new provenance entry
70
+ const newEntry = {
71
+ id: `prov-${Date.now()}`,
72
+ timestamp: new Date().toISOString(),
73
+ commit: {
74
+ hash: commit,
75
+ message: message || '',
76
+ author: author || 'Unknown',
77
+ },
78
+ working_spec: {
79
+ id: spec.id,
80
+ title: spec.title,
81
+ risk_tier: spec.risk_tier,
82
+ mode: spec.mode,
83
+ waiver_ids: spec.waiver_ids || [],
84
+ },
85
+ quality_gates: {
86
+ // This would be populated by recent validation results
87
+ // For now, we'll mark as unknown
88
+ status: 'unknown',
89
+ last_validated: new Date().toISOString(),
90
+ },
91
+ agent: {
92
+ type: detectAgentType(),
93
+ confidence_level: null, // Would be populated by agent actions
94
+ },
95
+ cursor_tracking: await getCursorTrackingData(commit), // AI code tracking data
96
+ checkpoints: await getCursorCheckpoints(), // Composer checkpoint data
97
+ };
98
+
99
+ // Calculate hash including previous chain
100
+ const previousHash =
101
+ provenanceChain.length > 0 ? provenanceChain[provenanceChain.length - 1].hash : '';
102
+ newEntry.previous_hash = previousHash;
103
+
104
+ const hashContent = JSON.stringify(
105
+ {
106
+ ...newEntry,
107
+ hash: undefined, // Exclude hash from hash calculation
108
+ },
109
+ Object.keys(newEntry).sort()
110
+ );
111
+
112
+ newEntry.hash = crypto.createHash('sha256').update(hashContent).digest('hex');
113
+
114
+ // Add to chain and save
115
+ provenanceChain.push(newEntry);
116
+ await saveProvenanceChain(provenanceChain, output);
117
+
118
+ if (!quiet) {
119
+ console.log(`✅ Provenance updated for commit ${commit.substring(0, 8)}`);
120
+ console.log(` Chain length: ${provenanceChain.length} entries`);
121
+ console.log(` Hash: ${newEntry.hash.substring(0, 16)}...`);
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Show current provenance information
127
+ * @param {Object} options - Command options
128
+ */
129
+ async function showProvenance(options) {
130
+ const { output = '.caws/provenance', format = 'text' } = options;
131
+
132
+ const chain = await loadProvenanceChain(output);
133
+
134
+ if (chain.length === 0) {
135
+ if (format === 'dashboard') {
136
+ console.log('┌─ CAWS Provenance Dashboard ──────────────────────┐');
137
+ console.log('│ ℹ️ No provenance data found │');
138
+ console.log('│ │');
139
+ console.log('│ 💡 Run "caws provenance init" to get started │');
140
+ console.log('└─────────────────────────────────────────────────┘');
141
+ } else {
142
+ console.log('ℹ️ No provenance data found');
143
+ console.log(`💡 Run "caws provenance init" to get started`);
144
+ }
145
+ return;
146
+ }
147
+
148
+ if (format === 'json') {
149
+ console.log(JSON.stringify(chain, null, 2));
150
+ return;
151
+ }
152
+
153
+ if (format === 'dashboard') {
154
+ await showDashboardFormat(chain, output);
155
+ return;
156
+ }
157
+
158
+ console.log('📜 CAWS Provenance Chain');
159
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
160
+ console.log(`Total entries: ${chain.length}`);
161
+ console.log('');
162
+
163
+ // Show last 5 entries
164
+ const recent = chain.slice(-5);
165
+ recent.forEach((entry, index) => {
166
+ const commit = entry.commit.hash.substring(0, 8);
167
+ const time = new Date(entry.timestamp).toLocaleString();
168
+ const offset = chain.length - recent.length + index + 1;
169
+
170
+ console.log(`${offset}. ${commit} - ${time}`);
171
+ console.log(` ${entry.commit.message.split('\n')[0]}`);
172
+ console.log(` ${entry.commit.author}`);
173
+ if (entry.working_spec) {
174
+ console.log(` Spec: ${entry.working_spec.id} (${entry.working_spec.risk_tier})`);
175
+ }
176
+ if (entry.agent && entry.agent.type !== 'human') {
177
+ console.log(` Agent: ${entry.agent.type}`);
178
+ }
179
+
180
+ // Display AI code tracking if available
181
+ if (entry.cursor_tracking && entry.cursor_tracking.available) {
182
+ const tracking = entry.cursor_tracking;
183
+ console.log(
184
+ ` 🤖 AI Code: ${tracking.ai_code_breakdown.composer_chat.percentage}% composer, ${tracking.ai_code_breakdown.tab_completions.percentage}% tab-complete, ${tracking.ai_code_breakdown.manual_human.percentage}% manual`
185
+ );
186
+ console.log(
187
+ ` 📊 Quality: ${Math.round(tracking.quality_metrics.ai_code_quality_score * 100)}% AI score, ${Math.round(tracking.quality_metrics.acceptance_rate * 100)}% acceptance`
188
+ );
189
+ }
190
+
191
+ // Display checkpoint info if available
192
+ if (entry.checkpoints && entry.checkpoints.available && entry.checkpoints.checkpoints) {
193
+ console.log(` 🔄 Checkpoints: ${entry.checkpoints.checkpoints.length} created`);
194
+ }
195
+
196
+ console.log('');
197
+ });
198
+
199
+ if (chain.length > 5) {
200
+ console.log(`... and ${chain.length - 5} earlier entries`);
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Verify provenance chain integrity
206
+ * @param {Object} options - Command options
207
+ */
208
+ async function verifyProvenance(options) {
209
+ const { output = '.caws/provenance' } = options;
210
+
211
+ const chain = await loadProvenanceChain(output);
212
+
213
+ if (chain.length === 0) {
214
+ console.log('ℹ️ No provenance data to verify');
215
+ return;
216
+ }
217
+
218
+ console.log('🔍 Verifying provenance chain integrity...');
219
+
220
+ let valid = true;
221
+ for (let i = 0; i < chain.length; i++) {
222
+ const entry = chain[i];
223
+
224
+ // Verify hash integrity
225
+ const expectedPreviousHash = i === 0 ? '' : chain[i - 1].hash;
226
+ if (entry.previous_hash !== expectedPreviousHash) {
227
+ console.error(`❌ Chain break at entry ${i + 1}: previous hash mismatch`);
228
+ valid = false;
229
+ }
230
+
231
+ // Recalculate and verify current hash
232
+ const entryForHash = { ...entry };
233
+ delete entryForHash.hash; // Remove hash field before calculation
234
+
235
+ const hashContent = JSON.stringify(entryForHash, Object.keys(entryForHash).sort());
236
+
237
+ const calculatedHash = crypto.createHash('sha256').update(hashContent).digest('hex');
238
+
239
+ if (calculatedHash !== entry.hash) {
240
+ console.error(`❌ Hash verification failed at entry ${i + 1}`);
241
+ console.error(` Expected: ${entry.hash}`);
242
+ console.error(` Calculated: ${calculatedHash}`);
243
+ valid = false;
244
+ }
245
+ }
246
+
247
+ if (valid) {
248
+ console.log('✅ Provenance chain integrity verified');
249
+ console.log(` ${chain.length} entries, all hashes valid`);
250
+ } else {
251
+ console.error('❌ Provenance chain integrity compromised');
252
+ process.exit(1);
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Load existing provenance chain from files
258
+ * @param {string} outputDir - Directory containing provenance files
259
+ * @returns {Array} Array of provenance entries
260
+ */
261
+ async function loadProvenanceChain(outputDir) {
262
+ const chainFile = path.join(outputDir, 'chain.json');
263
+
264
+ if (!(await fs.pathExists(chainFile))) {
265
+ return [];
266
+ }
267
+
268
+ try {
269
+ const content = await fs.readFile(chainFile, 'utf8');
270
+ return JSON.parse(content);
271
+ } catch (error) {
272
+ console.warn(`Warning: Could not load provenance chain: ${error.message}`);
273
+ return [];
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Save provenance chain to file
279
+ * @param {Array} chain - Provenance entries array
280
+ * @param {string} outputDir - Directory to save to
281
+ */
282
+ async function saveProvenanceChain(chain, outputDir) {
283
+ const chainFile = path.join(outputDir, 'chain.json');
284
+ await fs.writeFile(chainFile, JSON.stringify(chain, null, 2));
285
+ }
286
+
287
+ /**
288
+ * Analyze AI patterns and effectiveness from provenance data
289
+ * @param {Object} options - Command options
290
+ */
291
+ async function analyzeAIProvenance(options) {
292
+ const { output = '.caws/provenance' } = options;
293
+
294
+ const chain = await loadProvenanceChain(output);
295
+
296
+ if (chain.length === 0) {
297
+ console.log('ℹ️ No provenance data to analyze');
298
+ return;
299
+ }
300
+
301
+ console.log('🤖 AI Code Effectiveness Analysis');
302
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
303
+
304
+ // Filter entries with AI tracking data
305
+ const aiEntries = chain.filter(
306
+ (entry) =>
307
+ entry.cursor_tracking &&
308
+ entry.cursor_tracking.available &&
309
+ entry.agent &&
310
+ entry.agent.type !== 'human'
311
+ );
312
+
313
+ if (aiEntries.length === 0) {
314
+ console.log('ℹ️ No AI tracking data found in provenance');
315
+ console.log('💡 Configure CURSOR_TRACKING_API and CURSOR_CHECKPOINT_API environment variables');
316
+ return;
317
+ }
318
+
319
+ console.log(`Analyzed ${aiEntries.length} AI-assisted commits`);
320
+ console.log('');
321
+
322
+ // Analyze AI code contribution patterns
323
+ const contributionPatterns = analyzeContributionPatterns(aiEntries);
324
+ const qualityMetrics = analyzeQualityMetrics(aiEntries);
325
+ const checkpointAnalysis = analyzeCheckpointUsage(aiEntries);
326
+
327
+ console.log('📊 AI Contribution Patterns:');
328
+ console.log(` Average Composer/Chat contribution: ${contributionPatterns.avgComposerPercent}%`);
329
+ console.log(
330
+ ` Average Tab completion contribution: ${contributionPatterns.avgTabCompletePercent}%`
331
+ );
332
+ console.log(` Average Manual override rate: ${contributionPatterns.avgManualPercent}%`);
333
+ console.log('');
334
+
335
+ console.log('🎯 Quality Metrics:');
336
+ console.log(
337
+ ` Average AI code quality score: ${Math.round(qualityMetrics.avgQualityScore * 100)}%`
338
+ );
339
+ console.log(` Average acceptance rate: ${Math.round(qualityMetrics.avgAcceptanceRate * 100)}%`);
340
+ console.log(
341
+ ` Average human override rate: ${Math.round(qualityMetrics.avgOverrideRate * 100)}%`
342
+ );
343
+ console.log('');
344
+
345
+ console.log('🔄 Checkpoint Analysis:');
346
+ console.log(
347
+ ` Commits with checkpoints: ${checkpointAnalysis.entriesWithCheckpoints}/${aiEntries.length}`
348
+ );
349
+ console.log(` Average checkpoints per commit: ${checkpointAnalysis.avgCheckpointsPerEntry}`);
350
+ console.log(` Checkpoint revert rate: ${Math.round(checkpointAnalysis.revertRate * 100)}%`);
351
+ console.log('');
352
+
353
+ // Provide insights and recommendations
354
+ provideAIInsights(contributionPatterns, qualityMetrics, checkpointAnalysis);
355
+ }
356
+
357
+ /**
358
+ * Analyze AI contribution patterns across entries
359
+ */
360
+ function analyzeContributionPatterns(aiEntries) {
361
+ const contributions = aiEntries
362
+ .filter((entry) => entry.cursor_tracking?.ai_code_breakdown)
363
+ .map((entry) => entry.cursor_tracking.ai_code_breakdown);
364
+
365
+ if (contributions.length === 0) return {};
366
+
367
+ const avgComposer =
368
+ contributions.reduce((sum, c) => sum + c.composer_chat.percentage, 0) / contributions.length;
369
+ const avgTab =
370
+ contributions.reduce((sum, c) => sum + c.tab_completions.percentage, 0) / contributions.length;
371
+ const avgManual =
372
+ contributions.reduce((sum, c) => sum + c.manual_human.percentage, 0) / contributions.length;
373
+
374
+ return {
375
+ avgComposerPercent: Math.round(avgComposer),
376
+ avgTabCompletePercent: Math.round(avgTab),
377
+ avgManualPercent: Math.round(avgManual),
378
+ };
379
+ }
380
+
381
+ /**
382
+ * Analyze AI quality metrics across entries
383
+ */
384
+ function analyzeQualityMetrics(aiEntries) {
385
+ const metrics = aiEntries
386
+ .filter((entry) => entry.cursor_tracking?.quality_metrics)
387
+ .map((entry) => entry.cursor_tracking.quality_metrics);
388
+
389
+ if (metrics.length === 0) return {};
390
+
391
+ const avgQuality = metrics.reduce((sum, m) => sum + m.ai_code_quality_score, 0) / metrics.length;
392
+ const avgAcceptance = metrics.reduce((sum, m) => sum + m.acceptance_rate, 0) / metrics.length;
393
+ const avgOverride = metrics.reduce((sum, m) => sum + m.human_override_rate, 0) / metrics.length;
394
+
395
+ return {
396
+ avgQualityScore: avgQuality,
397
+ avgAcceptanceRate: avgAcceptance,
398
+ avgOverrideRate: avgOverride,
399
+ };
400
+ }
401
+
402
+ /**
403
+ * Analyze checkpoint usage patterns
404
+ */
405
+ function analyzeCheckpointUsage(aiEntries) {
406
+ const entriesWithCheckpoints = aiEntries.filter(
407
+ (entry) => entry.checkpoints?.available && entry.checkpoints.checkpoints?.length > 0
408
+ ).length;
409
+
410
+ const totalCheckpoints = aiEntries
411
+ .filter((entry) => entry.checkpoints?.available)
412
+ .reduce((sum, entry) => sum + (entry.checkpoints.checkpoints?.length || 0), 0);
413
+
414
+ // Mock revert rate - in real implementation, this would track actual reverts
415
+ const revertRate = 0.15; // 15% estimated revert rate
416
+
417
+ return {
418
+ entriesWithCheckpoints,
419
+ avgCheckpointsPerEntry:
420
+ entriesWithCheckpoints > 0 ? (totalCheckpoints / entriesWithCheckpoints).toFixed(1) : 0,
421
+ revertRate,
422
+ };
423
+ }
424
+
425
+ /**
426
+ * Install git hooks for automatic provenance updates
427
+ * @param {Object} options - Command options
428
+ */
429
+ async function installHooks(options) {
430
+ const { output = '.caws/provenance', skipPreCommit = false, skipPostCommit = false } = options;
431
+
432
+ console.log('🔗 Installing CAWS Provenance Git Hooks');
433
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
434
+
435
+ // Check if we're in a git repository
436
+ if (!(await fs.pathExists('.git'))) {
437
+ console.log('❌ Not in a git repository');
438
+ console.log('💡 Initialize git first: git init');
439
+ process.exit(1);
440
+ }
441
+
442
+ // Check if provenance is initialized
443
+ if (!(await fs.pathExists(path.join(output, 'chain.json')))) {
444
+ console.log('❌ Provenance not initialized');
445
+ console.log('💡 Run "caws provenance init" first');
446
+ process.exit(1);
447
+ }
448
+
449
+ console.log('✅ Found git repository and provenance setup');
450
+
451
+ // Ensure hooks directory exists
452
+ const hooksDir = '.git/hooks';
453
+ await fs.ensureDir(hooksDir);
454
+ console.log('✅ Ensured hooks directory exists');
455
+
456
+ let hooksInstalled = 0;
457
+
458
+ // Install pre-commit hook for validation
459
+ if (!skipPreCommit) {
460
+ try {
461
+ const preCommitHook = await createPreCommitHook(output);
462
+ const preCommitPath = path.join(hooksDir, 'pre-commit');
463
+
464
+ await fs.writeFile(preCommitPath, preCommitHook);
465
+ await fs.chmod(preCommitPath, '755');
466
+ console.log('✅ Installed pre-commit hook for provenance validation');
467
+ hooksInstalled++;
468
+ } catch (error) {
469
+ console.warn('⚠️ Failed to install pre-commit hook:', error.message);
470
+ }
471
+ }
472
+
473
+ // Install post-commit hook for provenance updates
474
+ if (!skipPostCommit) {
475
+ try {
476
+ const postCommitHook = await createPostCommitHook(output);
477
+ const postCommitPath = path.join(hooksDir, 'post-commit');
478
+
479
+ await fs.writeFile(postCommitPath, postCommitHook);
480
+ await fs.chmod(postCommitPath, '755');
481
+ console.log('✅ Installed post-commit hook for provenance updates');
482
+ hooksInstalled++;
483
+ } catch (error) {
484
+ console.warn('⚠️ Failed to install post-commit hook:', error.message);
485
+ }
486
+ }
487
+
488
+ console.log('');
489
+ console.log('🎉 Git hooks installation complete!');
490
+ console.log('');
491
+ console.log(`Installed ${hooksInstalled} hook(s):`);
492
+ if (!skipPreCommit) {
493
+ console.log(' • pre-commit: Validates provenance before commits');
494
+ }
495
+ if (!skipPostCommit) {
496
+ console.log(' • post-commit: Updates provenance after commits');
497
+ }
498
+ console.log('');
499
+ console.log('💡 Your commits will now automatically maintain provenance!');
500
+ console.log(' Run "caws provenance show" to view the updated chain');
501
+ }
502
+
503
+ /**
504
+ * Create pre-commit hook script for provenance validation
505
+ * @param {string} outputDir - Provenance output directory
506
+ * @returns {string} Hook script content
507
+ */
508
+ async function createPreCommitHook(outputDir) {
509
+ const scriptPath = path.resolve('node_modules/.bin/caws');
510
+ const fallbackPath = path.resolve('packages/caws-cli/dist/index.js');
511
+
512
+ return `#!/bin/sh
513
+ # CAWS Provenance Pre-commit Hook
514
+ # Validates provenance integrity before allowing commits
515
+
516
+ echo "🔍 Validating CAWS provenance..."
517
+
518
+ # Find caws CLI
519
+ if command -v caws >/dev/null 2>&1; then
520
+ CAWS_CMD="caws"
521
+ elif [ -x "${scriptPath}" ]; then
522
+ CAWS_CMD="${scriptPath}"
523
+ elif [ -x "${fallbackPath}" ]; then
524
+ CAWS_CMD="node ${fallbackPath}"
525
+ else
526
+ echo "⚠️ CAWS CLI not found, skipping provenance validation"
527
+ exit 0
528
+ fi
529
+
530
+ # Run provenance verification
531
+ if $CAWS_CMD provenance verify --output "${outputDir}" >/dev/null 2>&1; then
532
+ echo "✅ Provenance validation passed"
533
+ exit 0
534
+ else
535
+ echo "❌ Provenance validation failed"
536
+ echo "💡 Run 'caws provenance show' to investigate"
537
+ exit 1
538
+ fi
539
+ `;
540
+ }
541
+
542
+ /**
543
+ * Create post-commit hook script for provenance updates
544
+ * @param {string} outputDir - Provenance output directory
545
+ * @returns {string} Hook script content
546
+ */
547
+ async function createPostCommitHook(outputDir) {
548
+ const scriptPath = path.resolve('node_modules/.bin/caws');
549
+ const fallbackPath = path.resolve('packages/caws-cli/dist/index.js');
550
+
551
+ return `#!/bin/sh
552
+ # CAWS Provenance Post-commit Hook
553
+ # Updates provenance chain after successful commits
554
+
555
+ echo "📝 Updating CAWS provenance..."
556
+
557
+ # Get the current commit hash
558
+ COMMIT_HASH=$(git rev-parse HEAD)
559
+ COMMIT_MSG=$(git log -1 --pretty=%B | head -n 1)
560
+ AUTHOR=$(git log -1 --pretty=%an)
561
+
562
+ # Find caws CLI
563
+ if command -v caws >/dev/null 2>&1; then
564
+ CAWS_CMD="caws"
565
+ elif [ -x "${scriptPath}" ]; then
566
+ CAWS_CMD="${scriptPath}"
567
+ elif [ -x "${fallbackPath}" ]; then
568
+ CAWS_CMD="node ${fallbackPath}"
569
+ else
570
+ echo "⚠️ CAWS CLI not found, skipping provenance update"
571
+ exit 0
572
+ fi
573
+
574
+ # Update provenance
575
+ if $CAWS_CMD provenance update --commit "$COMMIT_HASH" --message "$COMMIT_MSG" --author "$AUTHOR" --output "${outputDir}" --quiet; then
576
+ echo "✅ Provenance updated for commit \${COMMIT_HASH:0:8}"
577
+ else
578
+ echo "⚠️ Failed to update provenance (non-fatal)"
579
+ fi
580
+
581
+ exit 0
582
+ `;
583
+ }
584
+
585
+ /**
586
+ * Show provenance data in dashboard format
587
+ * @param {Array} chain - Provenance chain entries
588
+ * @param {string} outputDir - Output directory path
589
+ */
590
+ async function showDashboardFormat(chain, outputDir) {
591
+ // Calculate key metrics
592
+ const totalEntries = chain.length;
593
+ const aiEntries = chain.filter(
594
+ (entry) => entry.cursor_tracking?.available && entry.agent?.type !== 'human'
595
+ ).length;
596
+
597
+ const avgQualityScore =
598
+ aiEntries > 0
599
+ ? chain
600
+ .filter((entry) => entry.cursor_tracking?.quality_metrics?.ai_code_quality_score)
601
+ .reduce(
602
+ (sum, entry) => sum + entry.cursor_tracking.quality_metrics.ai_code_quality_score,
603
+ 0
604
+ ) /
605
+ chain.filter((entry) => entry.cursor_tracking?.quality_metrics?.ai_code_quality_score)
606
+ .length
607
+ : 0;
608
+
609
+ const avgAcceptanceRate =
610
+ aiEntries > 0
611
+ ? chain
612
+ .filter((entry) => entry.cursor_tracking?.quality_metrics?.acceptance_rate)
613
+ .reduce((sum, entry) => sum + entry.cursor_tracking.quality_metrics.acceptance_rate, 0) /
614
+ chain.filter((entry) => entry.cursor_tracking?.quality_metrics?.acceptance_rate).length
615
+ : 0;
616
+
617
+ // Check config
618
+ let configStatus = '❌ Not configured';
619
+ try {
620
+ const configPath = path.join(outputDir, 'config.json');
621
+ if (await fs.pathExists(configPath)) {
622
+ const config = JSON.parse(await fs.readFile(configPath, 'utf8'));
623
+ const configured = [
624
+ config.cursor_tracking_api !== 'not_configured',
625
+ config.cursor_checkpoint_api !== 'not_configured',
626
+ config.cursor_project_id !== 'not_configured',
627
+ ].filter(Boolean).length;
628
+ configStatus = configured === 3 ? '✅ Fully configured' : `⚠️ ${configured}/3 configured`;
629
+ }
630
+ } catch (error) {
631
+ // Ignore config read errors
632
+ }
633
+
634
+ // Display dashboard
635
+ console.log('┌─ CAWS Provenance Dashboard ──────────────────────┐');
636
+ console.log(`│ 📊 Total Entries: ${totalEntries.toString().padEnd(33)} │`);
637
+ console.log(`│ 🤖 AI-Assisted: ${aiEntries.toString().padEnd(35)} │`);
638
+ console.log(
639
+ `│ 🎯 Avg Quality: ${(avgQualityScore * 100).toFixed(0).padEnd(2)}%${' '.repeat(33)} │`
640
+ );
641
+ console.log(
642
+ `│ ✅ Avg Acceptance: ${(avgAcceptanceRate * 100).toFixed(0).padEnd(2)}%${' '.repeat(30)} │`
643
+ );
644
+ console.log(`│ ⚙️ Config Status: ${configStatus.padEnd(31)} │`);
645
+ console.log('├─────────────────────────────────────────────────┤');
646
+
647
+ if (totalEntries > 0) {
648
+ console.log('│ Recent Activity: │');
649
+ const recent = chain.slice(-3);
650
+ recent.forEach((entry, index) => {
651
+ const commit = entry.commit.hash.substring(0, 8);
652
+ const time = new Date(entry.timestamp).toLocaleDateString();
653
+ const msg = entry.commit.message.split('\n')[0].substring(0, 30);
654
+ const line = `${index + 1}. ${commit} ${time} ${msg}`;
655
+ console.log(`│ ${line.padEnd(47)} │`);
656
+ });
657
+
658
+ if (aiEntries > 0) {
659
+ console.log('├─────────────────────────────────────────────────┤');
660
+ console.log('│ AI Contribution Breakdown: │');
661
+
662
+ const contributions = chain
663
+ .filter((entry) => entry.cursor_tracking?.ai_code_breakdown)
664
+ .map((entry) => entry.cursor_tracking.ai_code_breakdown);
665
+
666
+ if (contributions.length > 0) {
667
+ const avgComposer =
668
+ contributions.reduce((sum, c) => sum + c.composer_chat.percentage, 0) /
669
+ contributions.length;
670
+ const avgTab =
671
+ contributions.reduce((sum, c) => sum + c.tab_completions.percentage, 0) /
672
+ contributions.length;
673
+ const avgManual =
674
+ contributions.reduce((sum, c) => sum + c.manual_human.percentage, 0) /
675
+ contributions.length;
676
+
677
+ const composerBar = '█'.repeat(Math.round(avgComposer / 5));
678
+ const tabBar = '█'.repeat(Math.round(avgTab / 5));
679
+ const manualBar = '█'.repeat(Math.round(avgManual / 5));
680
+
681
+ console.log(
682
+ `│ Composer/Chat: ${composerBar.padEnd(10)} ${Math.round(avgComposer).toString().padStart(2)}%${' '.repeat(18)} │`
683
+ );
684
+ console.log(
685
+ `│ Tab Complete: ${tabBar.padEnd(10)} ${Math.round(avgTab).toString().padStart(2)}%${' '.repeat(18)} │`
686
+ );
687
+ console.log(
688
+ `│ Manual: ${manualBar.padEnd(10)} ${Math.round(avgManual).toString().padStart(2)}%${' '.repeat(18)} │`
689
+ );
690
+ }
691
+ }
692
+ }
693
+
694
+ console.log('└─────────────────────────────────────────────────┘');
695
+
696
+ // Add insights
697
+ if (aiEntries > 0) {
698
+ console.log('');
699
+ console.log('💡 Insights:');
700
+ if (avgAcceptanceRate > 0.9) {
701
+ console.log(' ✅ High AI acceptance rate indicates effective collaboration');
702
+ } else if (avgAcceptanceRate < 0.7) {
703
+ console.log(' ⚠️ Lower acceptance rate may indicate AI refinement needed');
704
+ }
705
+
706
+ if (avgQualityScore > 0.8) {
707
+ console.log(' 🎯 Excellent AI code quality - great results!');
708
+ }
709
+ }
710
+ }
711
+
712
+ /**
713
+ * Provide insights and recommendations based on AI analysis
714
+ */
715
+ function provideAIInsights(contributionPatterns, qualityMetrics, checkpointAnalysis) {
716
+ console.log('💡 AI Effectiveness Insights:');
717
+
718
+ if (contributionPatterns.avgComposerPercent > 60) {
719
+ console.log(' 📝 High Composer usage suggests complex feature development');
720
+ console.log(' → Consider breaking large features into smaller, focused sessions');
721
+ }
722
+
723
+ if (qualityMetrics.avgOverrideRate > 0.2) {
724
+ console.log(' ✏️ High human override rate indicates AI suggestions need refinement');
725
+ console.log(' → Review AI confidence thresholds or provide clearer requirements');
726
+ }
727
+
728
+ if (checkpointAnalysis.avgCheckpointsPerEntry < 2) {
729
+ console.log(' 🔄 Low checkpoint usage may limit ability to recover from bad AI directions');
730
+ console.log(' → Encourage more frequent checkpointing in Composer sessions');
731
+ }
732
+
733
+ if (qualityMetrics.avgAcceptanceRate > 0.9) {
734
+ console.log(' ✅ High acceptance rate indicates effective AI assistance');
735
+ console.log(' → Current AI integration is working well');
736
+ }
737
+
738
+ console.log('');
739
+ console.log('📈 Recommendations:');
740
+ console.log(
741
+ ` • Target Composer contribution: ${Math.max(40, contributionPatterns.avgComposerPercent - 10)}-${Math.min(80, contributionPatterns.avgComposerPercent + 10)}%`
742
+ );
743
+ console.log(
744
+ ` • Acceptable override rate: <${Math.round((qualityMetrics.avgOverrideRate + 0.1) * 100)}%`
745
+ );
746
+ console.log(' • Checkpoint frequency: Every 10-15 minutes in active sessions');
747
+ }
748
+
749
+ /**
750
+ * Get Cursor AI code tracking data for a commit
751
+ * @param {string} commitHash - Git commit hash to analyze
752
+ * @returns {Promise<Object>} AI code tracking data
753
+ */
754
+ async function getCursorTrackingData(commitHash) {
755
+ try {
756
+ // Check if Cursor tracking API is available
757
+ if (!process.env.CURSOR_TRACKING_API || !process.env.CURSOR_PROJECT_ID) {
758
+ return { available: false, reason: 'Cursor tracking API not configured' };
759
+ }
760
+
761
+ // In a real implementation, this would call the Cursor API
762
+ // For now, we'll return a mock structure showing what data would be available
763
+ const mockTrackingData = {
764
+ available: true,
765
+ commit_hash: commitHash,
766
+ ai_code_breakdown: {
767
+ tab_completions: {
768
+ lines_added: 45,
769
+ percentage: 35,
770
+ files_affected: ['src/utils.js', 'tests/utils.test.js'],
771
+ },
772
+ composer_chat: {
773
+ lines_added: 78,
774
+ percentage: 60,
775
+ files_affected: ['src/new-feature.js', 'src/api.js'],
776
+ checkpoints_created: 3,
777
+ },
778
+ manual_human: {
779
+ lines_added: 5,
780
+ percentage: 5,
781
+ files_affected: ['README.md'],
782
+ },
783
+ },
784
+ change_groups: [
785
+ {
786
+ change_id: 'cg_12345',
787
+ type: 'composer_session',
788
+ lines_ai_generated: 42,
789
+ lines_human_edited: 8,
790
+ confidence_score: 0.85,
791
+ timestamp: new Date().toISOString(),
792
+ },
793
+ ],
794
+ quality_metrics: {
795
+ ai_code_quality_score: 0.78,
796
+ human_override_rate: 0.12,
797
+ acceptance_rate: 0.94,
798
+ },
799
+ };
800
+
801
+ return mockTrackingData;
802
+ } catch (error) {
803
+ return {
804
+ available: false,
805
+ error: error.message,
806
+ reason: 'Failed to retrieve Cursor tracking data',
807
+ };
808
+ }
809
+ }
810
+
811
+ /**
812
+ * Initialize provenance tracking for the project
813
+ * @param {Object} options - Command options
814
+ */
815
+ async function initProvenance(options) {
816
+ const { output = '.caws/provenance', cursorApi } = options;
817
+
818
+ console.log('🚀 Initializing CAWS Provenance Tracking');
819
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
820
+
821
+ // Check if already initialized
822
+ if (await fs.pathExists(path.join(output, 'chain.json'))) {
823
+ console.log('⚠️ Provenance already initialized');
824
+ console.log(` Chain exists at: ${output}/chain.json`);
825
+ console.log('');
826
+ console.log('💡 To reset, delete the provenance directory and run again');
827
+ return;
828
+ }
829
+
830
+ // Ensure output directory exists
831
+ await fs.ensureDir(output);
832
+ console.log(`✅ Created provenance directory: ${output}`);
833
+
834
+ // Load working spec to validate CAWS project
835
+ const specPath = '.caws/working-spec.yaml';
836
+ if (!(await fs.pathExists(specPath))) {
837
+ console.log('');
838
+ console.log('❌ Not in a CAWS project - missing working spec');
839
+ console.log('💡 Run "caws init" first to create a CAWS project');
840
+ process.exit(1);
841
+ }
842
+
843
+ console.log('✅ Found CAWS working spec');
844
+
845
+ // Initialize empty chain
846
+ const initialChain = [];
847
+ await saveProvenanceChain(initialChain, output);
848
+ console.log('✅ Initialized empty provenance chain');
849
+
850
+ // Create environment configuration hints
851
+ const envConfig = {
852
+ cursor_tracking_api: cursorApi || process.env.CURSOR_TRACKING_API || 'not_configured',
853
+ cursor_checkpoint_api: process.env.CURSOR_CHECKPOINT_API || 'not_configured',
854
+ cursor_project_id: process.env.CURSOR_PROJECT_ID || 'not_configured',
855
+ notes: [
856
+ 'Configure CURSOR_TRACKING_API for AI code tracking',
857
+ 'Configure CURSOR_CHECKPOINT_API for session recovery data',
858
+ 'Configure CURSOR_PROJECT_ID to link with Cursor IDE',
859
+ ],
860
+ };
861
+
862
+ await fs.writeFile(path.join(output, 'config.json'), JSON.stringify(envConfig, null, 2));
863
+ console.log('✅ Created configuration template');
864
+
865
+ console.log('');
866
+ console.log('🎉 Provenance tracking initialized!');
867
+ console.log('');
868
+ console.log('Next steps:');
869
+ console.log('1. Install git hooks for automatic provenance (recommended):');
870
+ console.log(' caws provenance install-hooks');
871
+ console.log('');
872
+ console.log('2. Configure environment variables (optional):');
873
+ console.log(' export CURSOR_TRACKING_API="your-api-endpoint"');
874
+ console.log(' export CURSOR_CHECKPOINT_API="your-checkpoint-endpoint"');
875
+ console.log(' export CURSOR_PROJECT_ID="your-project-id"');
876
+ console.log('');
877
+ console.log('3. Manual provenance updates (if not using hooks):');
878
+ console.log(' caws provenance update --commit <hash>');
879
+ console.log('');
880
+ console.log('4. View provenance history:');
881
+ console.log(' caws provenance show');
882
+ }
883
+
884
+ /**
885
+ * Get Cursor Composer/Chat checkpoint data
886
+ * @returns {Promise<Array>} Array of checkpoint data
887
+ */
888
+ async function getCursorCheckpoints() {
889
+ try {
890
+ // Check if Cursor checkpoint API is available
891
+ if (!process.env.CURSOR_CHECKPOINT_API) {
892
+ return { available: false, reason: 'Cursor checkpoint API not configured' };
893
+ }
894
+
895
+ // In a real implementation, this would call the Cursor checkpoint API
896
+ // For now, we'll return a mock structure
897
+ const mockCheckpoints = [
898
+ {
899
+ id: 'cp_001',
900
+ timestamp: new Date(Date.now() - 3600000).toISOString(), // 1 hour ago
901
+ description: 'Initial AI-generated function structure',
902
+ changes_summary: {
903
+ lines_added: 25,
904
+ lines_modified: 0,
905
+ files_affected: ['src/new-feature.js'],
906
+ },
907
+ ai_confidence: 0.82,
908
+ can_revert: true,
909
+ },
910
+ {
911
+ id: 'cp_002',
912
+ timestamp: new Date(Date.now() - 1800000).toISOString(), // 30 min ago
913
+ description: 'Added error handling and validation',
914
+ changes_summary: {
915
+ lines_added: 15,
916
+ lines_modified: 8,
917
+ files_affected: ['src/new-feature.js', 'tests/new-feature.test.js'],
918
+ },
919
+ ai_confidence: 0.91,
920
+ can_revert: true,
921
+ },
922
+ {
923
+ id: 'cp_003',
924
+ timestamp: new Date().toISOString(), // Current
925
+ description: 'Final implementation with documentation',
926
+ changes_summary: {
927
+ lines_added: 12,
928
+ lines_modified: 5,
929
+ files_affected: ['src/new-feature.js', 'README.md'],
930
+ },
931
+ ai_confidence: 0.88,
932
+ can_revert: false, // Latest checkpoint
933
+ },
934
+ ];
935
+
936
+ return { available: true, checkpoints: mockCheckpoints };
937
+ } catch (error) {
938
+ return {
939
+ available: false,
940
+ error: error.message,
941
+ reason: 'Failed to retrieve Cursor checkpoint data',
942
+ };
943
+ }
944
+ }
945
+
946
+ /**
947
+ * Attempt to detect the type of agent/system making changes
948
+ * @returns {string} Agent type identifier
949
+ */
950
+ function detectAgentType() {
951
+ // Check environment variables and context clues
952
+ if (
953
+ process.env.CURSOR_AGENT === 'true' ||
954
+ process.env.CURSOR_TRACKING_API ||
955
+ process.env.CURSOR_CHECKPOINT_API
956
+ ) {
957
+ return 'cursor-ide';
958
+ }
959
+
960
+ if (process.env.GITHUB_ACTIONS === 'true') {
961
+ return 'github-actions';
962
+ }
963
+
964
+ if (process.env.CI === 'true') {
965
+ return 'ci-system';
966
+ }
967
+
968
+ // Default to human unless we can detect otherwise
969
+ return 'human';
970
+ }
971
+
972
+ module.exports = {
973
+ provenanceCommand,
974
+ updateProvenance,
975
+ showProvenance,
976
+ verifyProvenance,
977
+ initProvenance,
978
+ installHooks,
979
+ };