@paths.design/caws-cli 5.0.1 → 6.0.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.
@@ -9,6 +9,7 @@ const path = require('path');
9
9
  const yaml = require('js-yaml');
10
10
  const chalk = require('chalk');
11
11
  const { safeAsync, outputResult } = require('../error-handler');
12
+ const { question, closeReadline } = require('../utils/promise-utils');
12
13
  const { SPEC_TYPES } = require('../constants/spec-types');
13
14
 
14
15
  // Import suggestFeatureBreakdown from spec-resolver
@@ -488,42 +489,40 @@ async function migrateFromLegacy(options = {}) {
488
489
  * @returns {Promise<string>} User's choice: 'cancel', 'rename', 'merge', 'override'
489
490
  */
490
491
  async function askConflictResolution() {
491
- return new Promise((resolve) => {
492
- const readline = require('readline');
493
-
494
- console.log(chalk.blue('\n🔄 Conflict Resolution Options:'));
495
- console.log(chalk.gray(" 1. Cancel - Don't create the spec"));
496
- console.log(chalk.gray(' 2. Rename - Create with auto-generated name'));
497
- console.log(chalk.gray(' 3. Merge - Merge with existing spec (not implemented)'));
498
- console.log(chalk.gray(' 4. Override - Replace existing spec (use --force)'));
499
-
500
- console.log(chalk.yellow('\nEnter your choice (1-4) or the option name:'));
501
-
502
- const rl = readline.createInterface({
503
- input: process.stdin,
504
- output: process.stdout,
505
- });
506
-
507
- rl.question('> ', (answer) => {
508
- rl.close();
509
-
510
- const trimmed = answer.trim().toLowerCase();
511
-
512
- // Handle numeric choices
513
- if (trimmed === '1' || trimmed === 'cancel') {
514
- resolve('cancel');
515
- } else if (trimmed === '2' || trimmed === 'rename') {
516
- resolve('rename');
517
- } else if (trimmed === '3' || trimmed === 'merge') {
518
- resolve('merge');
519
- } else if (trimmed === '4' || trimmed === 'override') {
520
- resolve('override');
521
- } else {
522
- console.log(chalk.red('❌ Invalid choice. Defaulting to cancel.'));
523
- resolve('cancel');
524
- }
525
- });
492
+ const readline = require('readline');
493
+
494
+ console.log(chalk.blue('\n🔄 Conflict Resolution Options:'));
495
+ console.log(chalk.gray(" 1. Cancel - Don't create the spec"));
496
+ console.log(chalk.gray(' 2. Rename - Create with auto-generated name'));
497
+ console.log(chalk.gray(' 3. Merge - Merge with existing spec (not implemented)'));
498
+ console.log(chalk.gray(' 4. Override - Replace existing spec (use --force)'));
499
+ console.log(chalk.yellow('\nEnter your choice (1-4) or the option name:'));
500
+
501
+ const rl = readline.createInterface({
502
+ input: process.stdin,
503
+ output: process.stdout,
526
504
  });
505
+
506
+ try {
507
+ const answer = await question(rl, '> ');
508
+ const trimmed = answer.trim().toLowerCase();
509
+
510
+ // Handle numeric choices
511
+ if (trimmed === '1' || trimmed === 'cancel') {
512
+ return 'cancel';
513
+ } else if (trimmed === '2' || trimmed === 'rename') {
514
+ return 'rename';
515
+ } else if (trimmed === '3' || trimmed === 'merge') {
516
+ return 'merge';
517
+ } else if (trimmed === '4' || trimmed === 'override') {
518
+ return 'override';
519
+ } else {
520
+ console.log(chalk.red('❌ Invalid choice. Defaulting to cancel.'));
521
+ return 'cancel';
522
+ }
523
+ } finally {
524
+ await closeReadline(rl);
525
+ }
527
526
  }
528
527
 
529
528
  /**
@@ -9,6 +9,7 @@ const path = require('path');
9
9
  const yaml = require('js-yaml');
10
10
  const chalk = require('chalk');
11
11
  const { safeAsync, outputResult } = require('../error-handler');
12
+ const { parallel } = require('../utils/async-utils');
12
13
 
13
14
  /**
14
15
  * Load working specification (legacy single file approach)
@@ -758,13 +759,15 @@ async function statusCommand(options = {}) {
758
759
  const modes = require('../config/modes');
759
760
  const currentMode = await modes.getCurrentMode();
760
761
 
761
- // Load all status data
762
- const spec = await loadWorkingSpec(options.spec || '.caws/working-spec.yaml');
763
- const specs = await loadSpecsFromMultiSpec();
764
- const hooks = await checkGitHooks();
765
- const provenance = await loadProvenanceChain();
766
- const waivers = await loadWaiverStatus();
767
- const gates = await checkQualityGates();
762
+ // Load all status data in parallel for better performance
763
+ const [spec, specs, hooks, provenance, waivers, gates] = await parallel([
764
+ () => loadWorkingSpec(options.spec || '.caws/working-spec.yaml'),
765
+ () => loadSpecsFromMultiSpec(),
766
+ () => checkGitHooks(),
767
+ () => loadProvenanceChain(),
768
+ () => loadWaiverStatus(),
769
+ () => checkQualityGates(),
770
+ ]);
768
771
 
769
772
  // Display status (visual mode if requested)
770
773
  if (options.visual || options.json) {
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  const path = require('path');
8
- const chalk = require('chalk');
8
+ const { commandWrapper, Output } = require('../utils/command-wrapper');
9
9
 
10
10
  // Import tool system
11
11
  const ToolLoader = require('../tool-loader');
@@ -32,16 +32,19 @@ async function initializeToolSystem() {
32
32
  // Set up event listeners for tool system
33
33
  toolLoader.on('discovery:complete', ({ tools: _tools, count }) => {
34
34
  if (count > 0) {
35
- console.log(chalk.blue(`🔧 Discovered ${count} tools`));
35
+ Output.info(`Discovered ${count} tools`);
36
36
  }
37
37
  });
38
38
 
39
39
  toolLoader.on('tool:loaded', ({ id, metadata }) => {
40
- console.log(chalk.gray(` ✓ Loaded tool: ${metadata.name} (${id})`));
40
+ // Only log in verbose mode or when not using JSON output
41
+ if (!process.env.CAWS_OUTPUT_FORMAT || process.env.CAWS_OUTPUT_FORMAT !== 'json') {
42
+ console.log(` ✓ Loaded tool: ${metadata.name} (${id})`);
43
+ }
41
44
  });
42
45
 
43
46
  toolLoader.on('tool:error', ({ id, error }) => {
44
- console.warn(chalk.yellow(`⚠️ Failed to load tool ${id}: ${error}`));
47
+ Output.warning(`Failed to load tool ${id}: ${error}`);
45
48
  });
46
49
 
47
50
  // Auto-discover tools on initialization
@@ -49,8 +52,10 @@ async function initializeToolSystem() {
49
52
 
50
53
  return toolLoader;
51
54
  } catch (error) {
52
- console.warn(chalk.yellow('⚠️ Tool system initialization failed:'), error.message);
53
- console.warn(chalk.blue('💡 Continuing without dynamic tools'));
55
+ Output.warning(
56
+ `Tool system initialization failed: ${error.message}`,
57
+ 'Continuing without dynamic tools'
58
+ );
54
59
  return null;
55
60
  }
56
61
  }
@@ -61,75 +66,70 @@ async function initializeToolSystem() {
61
66
  * @param {Object} options - Command options
62
67
  */
63
68
  async function executeTool(toolId, options) {
64
- try {
65
- // Initialize tool system
66
- const loader = await initializeToolSystem();
69
+ return commandWrapper(
70
+ async () => {
71
+ // Initialize tool system
72
+ const loader = await initializeToolSystem();
67
73
 
68
- if (!loader) {
69
- console.error(chalk.red('Tool system not available'));
70
- process.exit(1);
71
- }
74
+ if (!loader) {
75
+ throw new Error('Tool system not available');
76
+ }
72
77
 
73
- // Load all tools first
74
- await loader.loadAllTools();
75
- const tool = loader.getTool(toolId);
78
+ // Load all tools first
79
+ await loader.loadAllTools();
80
+ const tool = loader.getTool(toolId);
76
81
 
77
- if (!tool) {
78
- console.error(chalk.red(`❌ Tool '${toolId}' not found`));
79
- console.log(chalk.blue('💡 Available tools:'));
80
- const tools = loader.getAllTools();
81
- for (const [id, t] of tools) {
82
- console.log(` - ${id}: ${t.metadata.name}`);
82
+ if (!tool) {
83
+ const tools = loader.getAllTools();
84
+ const availableTools = Array.from(tools, ([id, t]) => `${id}: ${t.metadata.name}`).join(
85
+ ', '
86
+ );
87
+ throw new Error(`Tool '${toolId}' not found.\n` + `Available tools: ${availableTools}`);
83
88
  }
84
- process.exit(1);
85
- }
86
89
 
87
- // Validate tool before execution
88
- const validation = await toolValidator.validateTool(tool);
89
- if (!validation.valid) {
90
- console.error(chalk.red('❌ Tool validation failed:'));
91
- validation.errors.forEach((error) => {
92
- console.error(` ${chalk.red('✗')} ${error}`);
93
- });
94
- process.exit(1);
95
- }
90
+ // Validate tool before execution
91
+ const validation = await toolValidator.validateTool(tool);
92
+ if (!validation.valid) {
93
+ throw new Error(
94
+ `Tool validation failed:\n` + validation.errors.map((e) => ` - ${e}`).join('\n')
95
+ );
96
+ }
96
97
 
97
- // Parse parameters
98
- let params = {};
99
- if (options.params) {
100
- try {
101
- params = JSON.parse(options.params);
102
- } catch (error) {
103
- console.error(chalk.red('❌ Invalid JSON parameters:'), error.message);
104
- process.exit(1);
98
+ // Parse parameters
99
+ let params = {};
100
+ if (options.params) {
101
+ try {
102
+ params = JSON.parse(options.params);
103
+ } catch (error) {
104
+ throw new Error(`Invalid JSON parameters: ${error.message}`);
105
+ }
105
106
  }
106
- }
107
107
 
108
- console.log(chalk.blue(`🚀 Executing tool: ${tool.metadata.name}`));
108
+ Output.progress(`Executing tool: ${tool.metadata.name}`);
109
109
 
110
- // Execute tool
111
- const result = await tool.module.execute(params, {
112
- workingDirectory: process.cwd(),
113
- timeout: options.timeout,
114
- });
110
+ // Execute tool
111
+ const result = await tool.module.execute(params, {
112
+ workingDirectory: process.cwd(),
113
+ timeout: options.timeout,
114
+ });
115
115
 
116
- // Display results
117
- if (result.success) {
118
- console.log(chalk.green('Tool execution successful'));
119
- if (result.output && typeof result.output === 'object') {
120
- console.log(chalk.gray('Output:'), JSON.stringify(result.output, null, 2));
116
+ // Display results
117
+ if (result.success) {
118
+ Output.success('Tool execution successful', {
119
+ output: result.output,
120
+ });
121
+ return result;
122
+ } else {
123
+ throw new Error(
124
+ `Tool execution failed:\n` + result.errors.map((e) => ` - ${e}`).join('\n')
125
+ );
121
126
  }
122
- } else {
123
- console.error(chalk.red('❌ Tool execution failed'));
124
- result.errors.forEach((error) => {
125
- console.error(` ${chalk.red('✗')} ${error}`);
126
- });
127
- process.exit(1);
127
+ },
128
+ {
129
+ commandName: `tool ${toolId}`,
130
+ context: { toolId, options },
128
131
  }
129
- } catch (error) {
130
- console.error(chalk.red(`❌ Error executing tool ${toolId}:`), error.message);
131
- process.exit(1);
132
- }
132
+ );
133
133
  }
134
134
 
135
135
  module.exports = {
@@ -14,6 +14,7 @@ const yaml = require('js-yaml');
14
14
  const chalk = require('chalk');
15
15
  const { initializeGlobalSetup } = require('../config');
16
16
  const WaiversManager = require('../waivers-manager');
17
+ const { commandWrapper, Output } = require('../utils/command-wrapper');
17
18
 
18
19
  const WAIVER_DIR = '.caws/waivers';
19
20
 
@@ -24,46 +25,44 @@ const WAIVER_DIR = '.caws/waivers';
24
25
  * @param {object} options - Command options
25
26
  */
26
27
  async function waiversCommand(subcommand = 'list', options = {}) {
27
- try {
28
- console.log('🔍 Detecting CAWS setup...');
29
- const setup = initializeGlobalSetup();
30
-
31
- if (setup.hasWorkingSpec) {
32
- console.log(`✅ Detected ${setup.setupType} CAWS setup`);
33
- console.log(` Capabilities: ${setup.capabilities.join(', ')}`);
34
- }
35
-
36
- // Ensure waivers directory exists
37
- const waiversDir = path.join(process.cwd(), WAIVER_DIR);
38
- if (!fs.existsSync(waiversDir)) {
39
- fs.mkdirSync(waiversDir, { recursive: true });
40
- }
41
-
42
- switch (subcommand) {
43
- case 'create':
44
- await createWaiver(options);
45
- break;
46
- case 'list':
47
- await listWaivers(options);
48
- break;
49
- case 'show':
50
- await showWaiver(options.id, options);
51
- break;
52
- case 'revoke':
53
- await revokeWaiver(options.id, options);
54
- break;
55
- default:
56
- console.error(chalk.red(`\n❌ Unknown waiver subcommand: ${subcommand}`));
57
- console.log(chalk.yellow('\n💡 Available subcommands: create, list, show, revoke'));
58
- process.exit(1);
28
+ return commandWrapper(
29
+ async () => {
30
+ Output.info('Detecting CAWS setup...');
31
+ const setup = initializeGlobalSetup();
32
+
33
+ if (setup.hasWorkingSpec) {
34
+ Output.success(`Detected ${setup.setupType} CAWS setup`, {
35
+ capabilities: setup.capabilities,
36
+ });
37
+ }
38
+
39
+ // Ensure waivers directory exists
40
+ const waiversDir = path.join(process.cwd(), WAIVER_DIR);
41
+ if (!fs.existsSync(waiversDir)) {
42
+ fs.mkdirSync(waiversDir, { recursive: true });
43
+ }
44
+
45
+ switch (subcommand) {
46
+ case 'create':
47
+ return await createWaiver(options);
48
+ case 'list':
49
+ return await listWaivers(options);
50
+ case 'show':
51
+ return await showWaiver(options.id, options);
52
+ case 'revoke':
53
+ return await revokeWaiver(options.id, options);
54
+ default:
55
+ throw new Error(
56
+ `Unknown waiver subcommand: ${subcommand}.\n` +
57
+ 'Available subcommands: create, list, show, revoke'
58
+ );
59
+ }
60
+ },
61
+ {
62
+ commandName: `waivers ${subcommand}`,
63
+ context: { subcommand, options },
59
64
  }
60
- } catch (error) {
61
- console.error(chalk.red(`\n❌ Waiver command failed: ${error.message}`));
62
- if (options.verbose) {
63
- console.error(error.stack);
64
- }
65
- process.exit(1);
66
- }
65
+ );
67
66
  }
68
67
 
69
68
  /**
package/dist/index.js CHANGED
@@ -114,6 +114,7 @@ program
114
114
  .option('--minimal', 'Only essential components', false)
115
115
  .option('--with-codemods', 'Include codemod scripts', false)
116
116
  .option('--with-oidc', 'Include OIDC trusted publisher setup', false)
117
+ .option('--with-quality-gates', 'Install quality gates package and scripts', false)
117
118
  .action(scaffoldProject);
118
119
 
119
120
  // Validate command
@@ -54,51 +54,83 @@ class PolicyManager {
54
54
  }
55
55
  }
56
56
 
57
- // Load from file
58
- const policyPath = path.join(projectRoot, '.caws', 'policy.yaml');
59
-
60
- try {
61
- const content = await fs.readFile(policyPath, 'utf-8');
62
- const policy = yaml.load(content);
57
+ // Load from file - check multiple locations for backward compatibility
58
+ const policyPaths = [
59
+ path.join(projectRoot, '.caws', 'policy.yaml'), // Preferred location
60
+ path.join(projectRoot, '.caws', 'policy', 'tier-policy.json'), // Legacy location
61
+ ];
62
+
63
+ let policyPath = null;
64
+ let policyContent = null;
65
+
66
+ // Try each path in order
67
+ for (const candidatePath of policyPaths) {
68
+ try {
69
+ if (await fs.pathExists(candidatePath)) {
70
+ policyPath = candidatePath;
71
+ const content = await fs.readFile(candidatePath, 'utf-8');
72
+
73
+ // Handle JSON format (legacy)
74
+ if (candidatePath.endsWith('.json')) {
75
+ policyContent = JSON.parse(content);
76
+ } else {
77
+ // Handle YAML format (preferred)
78
+ policyContent = yaml.load(content);
79
+ }
80
+ break;
81
+ }
82
+ } catch (error) {
83
+ // Continue to next path if this one fails
84
+ continue;
85
+ }
86
+ }
63
87
 
88
+ if (policyPath && policyContent) {
64
89
  // Validate policy structure
65
- this.validatePolicy(policy);
90
+ this.validatePolicy(policyContent);
66
91
 
67
92
  // Update cache
68
93
  if (this.enableCaching) {
69
94
  this.policyCache.set(projectRoot, {
70
- policy,
95
+ policy: policyContent,
71
96
  cachedAt: Date.now(),
72
97
  ttl: cacheTTL,
73
98
  });
74
99
  }
75
100
 
101
+ // Warn if using legacy location
102
+ if (policyPath.endsWith('.json')) {
103
+ console.warn(
104
+ '⚠️ Using legacy policy file location: .caws/policy/tier-policy.json\n' +
105
+ ' Migrate to .caws/policy.yaml for better compatibility\n' +
106
+ ' Run: caws init --migrate-policy'
107
+ );
108
+ }
109
+
76
110
  return {
77
- ...policy,
111
+ ...policyContent,
78
112
  _cacheHit: false,
79
113
  _loadDuration: Date.now() - startTime,
114
+ _policyPath: policyPath,
80
115
  };
81
- } catch (error) {
82
- if (error.code === 'ENOENT') {
83
- // Policy file doesn't exist - use default
84
- const defaultPolicy = this.getDefaultPolicy();
85
-
86
- if (this.enableCaching) {
87
- this.policyCache.set(projectRoot, {
88
- policy: defaultPolicy,
89
- cachedAt: Date.now(),
90
- ttl: cacheTTL,
91
- });
92
- }
116
+ } else {
117
+ // Policy file doesn't exist - use default
118
+ const defaultPolicy = this.getDefaultPolicy();
93
119
 
94
- return {
95
- ...defaultPolicy,
96
- _isDefault: true,
97
- _cacheHit: false,
98
- _loadDuration: Date.now() - startTime,
99
- };
120
+ if (this.enableCaching) {
121
+ this.policyCache.set(projectRoot, {
122
+ policy: defaultPolicy,
123
+ cachedAt: Date.now(),
124
+ ttl: cacheTTL,
125
+ });
100
126
  }
101
- throw error;
127
+
128
+ return {
129
+ ...defaultPolicy,
130
+ _isDefault: true,
131
+ _cacheHit: false,
132
+ _loadDuration: Date.now() - startTime,
133
+ };
102
134
  }
103
135
  } catch (error) {
104
136
  throw new Error(`Policy load failed: ${error.message}`);
@@ -117,6 +117,7 @@ async function scaffoldGitHooks(projectDir, options = {}) {
117
117
 
118
118
  /**
119
119
  * Generate pre-commit hook content with staged file quality gates
120
+ * Implements fallback chain: Node script → CLI → Python scripts → Skip gracefully
120
121
  */
121
122
  function generatePreCommitHook(options) {
122
123
  const { qualityGates = true, stagedOnly = true } = options;
@@ -124,6 +125,7 @@ function generatePreCommitHook(options) {
124
125
  return `#!/bin/bash
125
126
  # CAWS Pre-commit Hook
126
127
  # Runs validation and quality checks before commits
128
+ # Implements graceful fallback chain to avoid blocking commits
127
129
 
128
130
  set -e
129
131
 
@@ -136,39 +138,87 @@ if [ ! -d ".caws" ]; then
136
138
  exit 0
137
139
  fi
138
140
 
139
- # Run quality gates
140
- if command -v node >/dev/null 2>&1; then
141
- if node scripts/quality-gates/run-quality-gates.js; then
142
- echo "✅ Quality gates passed"
141
+ # Fallback chain for quality gates:
142
+ # 1. Try Node.js script (if exists)
143
+ # 2. Try CAWS CLI
144
+ # 3. Try Makefile target
145
+ # 4. Try Python scripts
146
+ # 5. Skip gracefully (warn only)
147
+
148
+ QUALITY_GATES_RAN=false
149
+
150
+ # Option 1: Node.js quality gates script
151
+ if [ -f "scripts/quality-gates/run-quality-gates.js" ]; then
152
+ if command -v node >/dev/null 2>&1; then
153
+ echo "📁 Running Node.js quality gates script..."
154
+ if node scripts/quality-gates/run-quality-gates.js; then
155
+ echo "✅ Quality gates passed"
156
+ QUALITY_GATES_RAN=true
157
+ else
158
+ echo "❌ Quality gates failed - commit blocked"
159
+ echo "💡 Fix the violations above before committing"
160
+ exit 1
161
+ fi
162
+ fi
163
+ # Option 2: CAWS CLI validation
164
+ elif command -v caws >/dev/null 2>&1; then
165
+ echo "📋 Running CAWS CLI validation..."
166
+ if caws validate --quiet 2>/dev/null; then
167
+ echo "✅ CAWS validation passed"
168
+ QUALITY_GATES_RAN=true
143
169
  else
144
- echo " Quality gates failed - commit blocked"
145
- echo "💡 Fix the violations above before committing"
146
- echo "📖 See docs/refactoring.md for crisis response plan"
147
- exit 1
170
+ echo "⚠️ CAWS validation failed, but allowing commit (non-blocking)"
171
+ echo "💡 Run 'caws validate' for details"
172
+ QUALITY_GATES_RAN=true
173
+ fi
174
+ # Option 3: Makefile target
175
+ elif [ -f "Makefile" ] && grep -q "caws-validate\\|caws-gates" Makefile; then
176
+ echo "🔧 Running Makefile quality gates..."
177
+ if make caws-validate >/dev/null 2>&1 || make caws-gates >/dev/null 2>&1; then
178
+ echo "✅ Makefile quality gates passed"
179
+ QUALITY_GATES_RAN=true
180
+ else
181
+ echo "⚠️ Makefile quality gates failed, but allowing commit (non-blocking)"
182
+ QUALITY_GATES_RAN=true
183
+ fi
184
+ # Option 4: Python scripts
185
+ elif [ -f "scripts/simple_gates.py" ] && command -v python3 >/dev/null 2>&1; then
186
+ echo "🐍 Running Python quality gates script..."
187
+ if python3 scripts/simple_gates.py all --tier 2 --profile backend-api >/dev/null 2>&1; then
188
+ echo "✅ Python quality gates passed"
189
+ QUALITY_GATES_RAN=true
190
+ else
191
+ echo "⚠️ Python quality gates failed, but allowing commit (non-blocking)"
192
+ QUALITY_GATES_RAN=true
148
193
  fi
194
+ # Option 5: Skip gracefully
149
195
  else
150
- echo "⚠️ Node.js not found - skipping quality gates"
151
- echo "💡 Install Node.js to enable automatic quality checking"
152
- exit 0
196
+ echo "⚠️ Quality gates not available - skipping"
197
+ echo "💡 Available options:"
198
+ echo " • Install: npm install -g @paths.design/caws-cli"
199
+ echo " • Use Python: python3 scripts/simple_gates.py"
200
+ echo " • Use Makefile: make caws-gates"
201
+ QUALITY_GATES_RAN=true
153
202
  fi
154
203
 
155
- # Run hidden TODO analysis on staged files only
156
- echo "🔍 Checking for hidden TODOs in staged files..."
157
- if command -v python3 >/dev/null 2>&1; then
158
- if python3 scripts/v3/analysis/todo_analyzer.py --staged-only --ci-mode --min-confidence 0.8 >/dev/null 2>&1; then
159
- echo "✅ No critical hidden TODOs found in staged files"
160
- else
161
- echo "❌ Critical hidden TODOs detected in staged files - commit blocked"
162
- echo "💡 Fix stub implementations and placeholder code before committing"
163
- echo "📖 See docs/PLACEHOLDER-DETECTION-GUIDE.md for classification"
164
- echo ""
165
- echo "🔍 Running detailed analysis on staged files..."
166
- python3 scripts/v3/analysis/todo_analyzer.py --staged-only --min-confidence 0.8
167
- exit 1
204
+ # Run hidden TODO analysis on staged files only (if Python available)
205
+ if [ "$QUALITY_GATES_RAN" = true ]; then
206
+ echo "🔍 Checking for hidden TODOs in staged files..."
207
+ if command -v python3 >/dev/null 2>&1 && [ -f "scripts/v3/analysis/todo_analyzer.py" ]; then
208
+ if python3 scripts/v3/analysis/todo_analyzer.py --staged-only --ci-mode --min-confidence 0.8 >/dev/null 2>&1; then
209
+ echo "✅ No critical hidden TODOs found in staged files"
210
+ else
211
+ echo " Critical hidden TODOs detected in staged files - commit blocked"
212
+ echo "💡 Fix stub implementations and placeholder code before committing"
213
+ echo "📖 See docs/PLACEHOLDER-DETECTION-GUIDE.md for classification"
214
+ echo ""
215
+ echo "🔍 Running detailed analysis on staged files..."
216
+ python3 scripts/v3/analysis/todo_analyzer.py --staged-only --min-confidence 0.8
217
+ exit 1
218
+ fi
219
+ elif command -v python3 >/dev/null 2>&1; then
220
+ echo "⚠️ Python3 found but TODO analyzer not available - skipping"
168
221
  fi
169
- else
170
- echo "⚠️ Python3 not found - skipping hidden TODO analysis"
171
- echo "💡 Install Python3 to enable automatic TODO checking"
172
222
  fi
173
223
 
174
224
  echo "✅ All quality checks passed - proceeding with commit"
@@ -280,6 +330,18 @@ if [ -f "package.json" ]; then
280
330
  # Don't fail on warnings, just warn
281
331
  fi
282
332
  fi
333
+ elif [ -f "requirements.txt" ] || [ -f "pyproject.toml" ]; then
334
+ # Python project security checks
335
+ if command -v pip-audit >/dev/null 2>&1; then
336
+ echo "🔍 Checking Python vulnerabilities..."
337
+ pip-audit --desc 2>/dev/null || echo "⚠️ Install pip-audit for vulnerability checks: pip install pip-audit"
338
+ fi
339
+ elif [ -f "Cargo.toml" ]; then
340
+ # Rust project security checks
341
+ if command -v cargo-audit >/dev/null 2>&1; then
342
+ echo "🔍 Checking Rust vulnerabilities..."
343
+ cargo audit 2>/dev/null || echo "⚠️ Install cargo-audit for vulnerability checks: cargo install cargo-audit"
344
+ fi
283
345
  fi
284
346
 
285
347
  echo "🎉 Pre-push checks completed!"