@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.
- package/README.md +15 -12
- package/dist/commands/provenance.js +27 -22
- package/dist/commands/quality-gates.js +190 -455
- package/dist/commands/specs.js +34 -35
- package/dist/commands/status.js +10 -7
- package/dist/commands/tool.js +63 -63
- package/dist/commands/waivers.js +38 -39
- package/dist/index.js +1 -0
- package/dist/policy/PolicyManager.js +60 -28
- package/dist/scaffold/git-hooks.js +89 -27
- package/dist/scaffold/index.js +25 -0
- package/dist/utils/async-utils.js +188 -0
- package/dist/utils/command-wrapper.js +200 -0
- package/dist/utils/promise-utils.js +72 -0
- package/package.json +1 -1
- package/templates/.cursor/hooks/block-dangerous.sh +8 -2
- package/templates/.cursor/rules/README.md +5 -7
- package/templates/scripts/v3/analysis/todo_analyzer.py +48 -1
- package/templates/.cursor/rules/10-authorship-and-attribution.mdc +0 -15
- package/templates/.cursor/rules/15-sophisticated-todo-detection.mdc +0 -425
- /package/templates/.cursor/rules/{11-documentation-quality-standards.mdc → 10-documentation-quality-standards.mdc} +0 -0
- /package/templates/.cursor/rules/{12-scope-management-waivers.mdc → 11-scope-management-waivers.mdc} +0 -0
- /package/templates/.cursor/rules/{13-implementation-completeness.mdc → 12-implementation-completeness.mdc} +0 -0
- /package/templates/.cursor/rules/{14-language-agnostic-standards.mdc → 13-language-agnostic-standards.mdc} +0 -0
package/dist/scaffold/index.js
CHANGED
|
@@ -330,6 +330,31 @@ async function scaffoldProject(options) {
|
|
|
330
330
|
required: false,
|
|
331
331
|
});
|
|
332
332
|
|
|
333
|
+
// Install quality gates package if requested
|
|
334
|
+
if (options.withQualityGates) {
|
|
335
|
+
console.log(chalk.blue('\n📦 Installing quality gates package...'));
|
|
336
|
+
try {
|
|
337
|
+
const { execSync } = require('child_process');
|
|
338
|
+
const npmCommand = fs.existsSync(path.join(currentDir, 'package.json'))
|
|
339
|
+
? 'npm install --save-dev @paths.design/quality-gates'
|
|
340
|
+
: 'npm install -g @paths.design/quality-gates';
|
|
341
|
+
|
|
342
|
+
console.log(chalk.gray(` Running: ${npmCommand}`));
|
|
343
|
+
execSync(npmCommand, {
|
|
344
|
+
cwd: currentDir,
|
|
345
|
+
stdio: 'inherit',
|
|
346
|
+
});
|
|
347
|
+
console.log(chalk.green('✅ Quality gates package installed'));
|
|
348
|
+
console.log(chalk.blue('💡 You can now use: caws quality-gates'));
|
|
349
|
+
} catch (error) {
|
|
350
|
+
console.log(chalk.yellow(`⚠️ Failed to install quality gates package: ${error.message}`));
|
|
351
|
+
console.log(
|
|
352
|
+
chalk.gray(' You can install manually: npm install -g @paths.design/quality-gates')
|
|
353
|
+
);
|
|
354
|
+
console.log(chalk.gray(' Or use Python scripts: python3 scripts/simple_gates.py'));
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
333
358
|
// Add commit conventions for setups that don't have them
|
|
334
359
|
if (!setup.hasTemplates || !fs.existsSync(path.join(currentDir, 'COMMIT_CONVENTIONS.md'))) {
|
|
335
360
|
enhancements.push({
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Async Operation Utilities
|
|
3
|
+
* Provides consistent patterns for async operations, parallel execution, and resource cleanup
|
|
4
|
+
* @author @darianrosebrook
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Execute multiple async operations in parallel
|
|
9
|
+
* @param {Array<Promise>} promises - Array of promises to execute
|
|
10
|
+
* @param {Object} options - Options
|
|
11
|
+
* @param {boolean} [options.failFast=true] - Stop on first error
|
|
12
|
+
* @returns {Promise<Array>} Array of results
|
|
13
|
+
*/
|
|
14
|
+
async function parallel(promises, options = {}) {
|
|
15
|
+
const { failFast = true } = options;
|
|
16
|
+
|
|
17
|
+
if (failFast) {
|
|
18
|
+
return Promise.all(promises);
|
|
19
|
+
} else {
|
|
20
|
+
// Wait for all promises, collecting both successes and failures
|
|
21
|
+
return Promise.allSettled(promises).then((results) => {
|
|
22
|
+
return results.map((result) => {
|
|
23
|
+
if (result.status === 'fulfilled') {
|
|
24
|
+
return { success: true, value: result.value };
|
|
25
|
+
} else {
|
|
26
|
+
return { success: false, error: result.reason };
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Execute async operations sequentially
|
|
35
|
+
* @param {Array<Function>} operations - Array of async functions to execute
|
|
36
|
+
* @param {Object} options - Options
|
|
37
|
+
* @param {boolean} [options.stopOnError=true] - Stop on first error
|
|
38
|
+
* @returns {Promise<Array>} Array of results
|
|
39
|
+
*/
|
|
40
|
+
async function sequential(operations, options = {}) {
|
|
41
|
+
const { stopOnError = true } = options;
|
|
42
|
+
const results = [];
|
|
43
|
+
|
|
44
|
+
for (const operation of operations) {
|
|
45
|
+
try {
|
|
46
|
+
const result = await operation();
|
|
47
|
+
results.push({ success: true, value: result });
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (stopOnError) {
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
results.push({ success: false, error });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return results;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Retry an async operation with exponential backoff
|
|
61
|
+
* @param {Function} operation - Async function to retry
|
|
62
|
+
* @param {Object} options - Retry options
|
|
63
|
+
* @param {number} [options.maxRetries=3] - Maximum number of retries
|
|
64
|
+
* @param {number} [options.initialDelay=1000] - Initial delay in ms
|
|
65
|
+
* @param {number} [options.maxDelay=10000] - Maximum delay in ms
|
|
66
|
+
* @param {Function} [options.shouldRetry] - Function to determine if error should be retried
|
|
67
|
+
* @returns {Promise<any>} Operation result
|
|
68
|
+
*/
|
|
69
|
+
async function retry(operation, options = {}) {
|
|
70
|
+
const {
|
|
71
|
+
maxRetries = 3,
|
|
72
|
+
initialDelay = 1000,
|
|
73
|
+
maxDelay = 10000,
|
|
74
|
+
shouldRetry = () => true,
|
|
75
|
+
} = options;
|
|
76
|
+
|
|
77
|
+
let lastError;
|
|
78
|
+
let delay = initialDelay;
|
|
79
|
+
|
|
80
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
81
|
+
try {
|
|
82
|
+
return await operation();
|
|
83
|
+
} catch (error) {
|
|
84
|
+
lastError = error;
|
|
85
|
+
|
|
86
|
+
if (attempt === maxRetries || !shouldRetry(error)) {
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Wait before retrying with exponential backoff
|
|
91
|
+
// eslint-disable-next-line no-undef
|
|
92
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
93
|
+
delay = Math.min(delay * 2, maxDelay);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
throw lastError;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Execute operation with timeout
|
|
102
|
+
* @param {Promise} promise - Promise to execute
|
|
103
|
+
* @param {number} timeoutMs - Timeout in milliseconds
|
|
104
|
+
* @param {string} [errorMessage] - Custom error message
|
|
105
|
+
* @returns {Promise<any>} Operation result
|
|
106
|
+
*/
|
|
107
|
+
async function withTimeout(promise, timeoutMs, errorMessage = 'Operation timed out') {
|
|
108
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
109
|
+
// eslint-disable-next-line no-undef
|
|
110
|
+
setTimeout(() => {
|
|
111
|
+
reject(new Error(`${errorMessage} (${timeoutMs}ms)`));
|
|
112
|
+
}, timeoutMs);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
return Promise.race([promise, timeoutPromise]);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Execute operation with resource cleanup
|
|
120
|
+
* @param {Function} operation - Async operation to execute
|
|
121
|
+
* @param {Function} cleanup - Cleanup function (called in finally)
|
|
122
|
+
* @returns {Promise<any>} Operation result
|
|
123
|
+
*/
|
|
124
|
+
async function withCleanup(operation, cleanup) {
|
|
125
|
+
try {
|
|
126
|
+
return await operation();
|
|
127
|
+
} finally {
|
|
128
|
+
await cleanup();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Execute multiple operations and collect all errors
|
|
134
|
+
* @param {Array<Function>} operations - Array of async functions
|
|
135
|
+
* @returns {Promise<{successes: Array, errors: Array}>} Results and errors
|
|
136
|
+
*/
|
|
137
|
+
async function collectResults(operations) {
|
|
138
|
+
const results = await Promise.allSettled(
|
|
139
|
+
operations.map((op) => op())
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const successes = [];
|
|
143
|
+
const errors = [];
|
|
144
|
+
|
|
145
|
+
results.forEach((result, index) => {
|
|
146
|
+
if (result.status === 'fulfilled') {
|
|
147
|
+
successes.push({ index, value: result.value });
|
|
148
|
+
} else {
|
|
149
|
+
errors.push({ index, error: result.reason });
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
return { successes, errors };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Execute operation with cancellation support
|
|
158
|
+
* @param {Function} operation - Async operation to execute
|
|
159
|
+
* @param {AbortSignal} signal - Abort signal for cancellation
|
|
160
|
+
* @returns {Promise<any>} Operation result
|
|
161
|
+
*/
|
|
162
|
+
async function withCancellation(operation, signal) {
|
|
163
|
+
if (signal.aborted) {
|
|
164
|
+
throw new Error('Operation cancelled');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return new Promise((resolve, reject) => {
|
|
168
|
+
signal.addEventListener('abort', () => {
|
|
169
|
+
reject(new Error('Operation cancelled'));
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
operation()
|
|
173
|
+
.then(resolve)
|
|
174
|
+
.catch(reject);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
module.exports = {
|
|
179
|
+
parallel,
|
|
180
|
+
sequential,
|
|
181
|
+
retry,
|
|
182
|
+
withTimeout,
|
|
183
|
+
withCleanup,
|
|
184
|
+
collectResults,
|
|
185
|
+
withCancellation,
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Unified Command Wrapper
|
|
3
|
+
* Provides consistent error handling and output formatting for all CLI commands
|
|
4
|
+
* @author @darianrosebrook
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { safeAsync, handleCliError, outputResult, isJsonOutput } = require('../error-handler');
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Unified command wrapper that provides:
|
|
12
|
+
* - Consistent error handling
|
|
13
|
+
* - Standardized output formatting
|
|
14
|
+
* - Execution timing
|
|
15
|
+
* - JSON output support
|
|
16
|
+
*
|
|
17
|
+
* @param {Function} commandFn - Async command function to execute
|
|
18
|
+
* @param {Object} options - Command options
|
|
19
|
+
* @param {string} options.commandName - Name of the command (for error context)
|
|
20
|
+
* @param {boolean} [options.includeTiming=true] - Include execution timing
|
|
21
|
+
* @param {boolean} [options.exitOnError=true] - Exit process on error
|
|
22
|
+
* @param {Object} [options.context={}] - Additional context for error handling
|
|
23
|
+
* @returns {Promise<any>} Command result
|
|
24
|
+
*/
|
|
25
|
+
async function commandWrapper(commandFn, options = {}) {
|
|
26
|
+
const {
|
|
27
|
+
commandName = 'command',
|
|
28
|
+
includeTiming = true,
|
|
29
|
+
exitOnError = true,
|
|
30
|
+
context = {},
|
|
31
|
+
} = options;
|
|
32
|
+
|
|
33
|
+
return safeAsync(
|
|
34
|
+
async () => {
|
|
35
|
+
try {
|
|
36
|
+
const result = await commandFn();
|
|
37
|
+
return result;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
// Enhance error with command context
|
|
40
|
+
error.commandName = commandName;
|
|
41
|
+
error.context = { ...context, ...error.context };
|
|
42
|
+
|
|
43
|
+
// Handle error with unified handler
|
|
44
|
+
handleCliError(
|
|
45
|
+
error,
|
|
46
|
+
{
|
|
47
|
+
command: commandName,
|
|
48
|
+
...context,
|
|
49
|
+
},
|
|
50
|
+
exitOnError
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// If exitOnError is false, rethrow for caller to handle
|
|
54
|
+
if (!exitOnError) {
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
commandName,
|
|
60
|
+
includeTiming
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Unified output utilities for consistent formatting
|
|
66
|
+
*/
|
|
67
|
+
const Output = {
|
|
68
|
+
/**
|
|
69
|
+
* Output success message
|
|
70
|
+
* @param {string} message - Success message
|
|
71
|
+
* @param {Object} [data] - Additional data to output
|
|
72
|
+
*/
|
|
73
|
+
success(message, data = {}) {
|
|
74
|
+
if (isJsonOutput()) {
|
|
75
|
+
outputResult(
|
|
76
|
+
{
|
|
77
|
+
success: true,
|
|
78
|
+
message,
|
|
79
|
+
...data,
|
|
80
|
+
},
|
|
81
|
+
true
|
|
82
|
+
);
|
|
83
|
+
} else {
|
|
84
|
+
console.log(chalk.green(`✅ ${message}`));
|
|
85
|
+
if (Object.keys(data).length > 0 && !isJsonOutput()) {
|
|
86
|
+
console.log(chalk.gray(JSON.stringify(data, null, 2)));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Output error message
|
|
93
|
+
* @param {string} message - Error message
|
|
94
|
+
* @param {string[]} [suggestions] - Recovery suggestions
|
|
95
|
+
*/
|
|
96
|
+
error(message, suggestions = []) {
|
|
97
|
+
if (isJsonOutput()) {
|
|
98
|
+
outputResult(
|
|
99
|
+
{
|
|
100
|
+
success: false,
|
|
101
|
+
error: {
|
|
102
|
+
message,
|
|
103
|
+
suggestions,
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
false
|
|
107
|
+
);
|
|
108
|
+
} else {
|
|
109
|
+
console.error(chalk.red(`❌ ${message}`));
|
|
110
|
+
if (suggestions.length > 0) {
|
|
111
|
+
console.error(chalk.yellow('\n💡 Suggestions:'));
|
|
112
|
+
suggestions.forEach((suggestion) => {
|
|
113
|
+
console.error(chalk.yellow(` ${suggestion}`));
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Output warning message
|
|
121
|
+
* @param {string} message - Warning message
|
|
122
|
+
* @param {string} [suggestion] - Optional suggestion
|
|
123
|
+
*/
|
|
124
|
+
warning(message, suggestion = null) {
|
|
125
|
+
if (isJsonOutput()) {
|
|
126
|
+
outputResult(
|
|
127
|
+
{
|
|
128
|
+
warning: true,
|
|
129
|
+
message,
|
|
130
|
+
suggestion,
|
|
131
|
+
},
|
|
132
|
+
true
|
|
133
|
+
);
|
|
134
|
+
} else {
|
|
135
|
+
console.warn(chalk.yellow(`⚠️ ${message}`));
|
|
136
|
+
if (suggestion) {
|
|
137
|
+
console.warn(chalk.blue(` 💡 ${suggestion}`));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Output info message
|
|
144
|
+
* @param {string} message - Info message
|
|
145
|
+
* @param {Object} [data] - Additional data
|
|
146
|
+
*/
|
|
147
|
+
info(message, data = {}) {
|
|
148
|
+
if (isJsonOutput()) {
|
|
149
|
+
outputResult(
|
|
150
|
+
{
|
|
151
|
+
info: true,
|
|
152
|
+
message,
|
|
153
|
+
...data,
|
|
154
|
+
},
|
|
155
|
+
true
|
|
156
|
+
);
|
|
157
|
+
} else {
|
|
158
|
+
console.log(chalk.blue(`ℹ️ ${message}`));
|
|
159
|
+
if (Object.keys(data).length > 0) {
|
|
160
|
+
console.log(chalk.gray(JSON.stringify(data, null, 2)));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Output data in JSON format
|
|
167
|
+
* @param {Object} data - Data to output
|
|
168
|
+
* @param {boolean} [success=true] - Whether operation was successful
|
|
169
|
+
*/
|
|
170
|
+
json(data, success = true) {
|
|
171
|
+
outputResult(data, success);
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Output progress message
|
|
176
|
+
* @param {string} message - Progress message
|
|
177
|
+
*/
|
|
178
|
+
progress(message) {
|
|
179
|
+
if (!isJsonOutput()) {
|
|
180
|
+
console.log(chalk.blue(`🔄 ${message}`));
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Output section header
|
|
186
|
+
* @param {string} title - Section title
|
|
187
|
+
*/
|
|
188
|
+
section(title) {
|
|
189
|
+
if (!isJsonOutput()) {
|
|
190
|
+
console.log(chalk.bold(`\n${title}`));
|
|
191
|
+
console.log('─'.repeat(Math.min(title.length, 60)));
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
module.exports = {
|
|
197
|
+
commandWrapper,
|
|
198
|
+
Output,
|
|
199
|
+
isJsonOutput,
|
|
200
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Promise Utilities
|
|
3
|
+
* Utilities for converting callback-based APIs to promises
|
|
4
|
+
* @author @darianrosebrook
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Convert readline question to promise
|
|
9
|
+
* @param {readline.Interface} rl - Readline interface
|
|
10
|
+
* @param {string} question - Question to ask
|
|
11
|
+
* @returns {Promise<string>} User's answer
|
|
12
|
+
*/
|
|
13
|
+
function question(rl, questionText) {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
rl.question(questionText, (answer) => {
|
|
16
|
+
resolve(answer);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Close readline interface and return promise
|
|
23
|
+
* @param {readline.Interface} rl - Readline interface
|
|
24
|
+
* @returns {Promise<void>}
|
|
25
|
+
*/
|
|
26
|
+
function closeReadline(rl) {
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
rl.once('close', resolve);
|
|
29
|
+
rl.close();
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Create a promise that resolves when event fires
|
|
35
|
+
* @param {EventEmitter} emitter - Event emitter
|
|
36
|
+
* @param {string} event - Event name
|
|
37
|
+
* @param {Object} options - Options
|
|
38
|
+
* @param {number} [options.timeout] - Timeout in ms
|
|
39
|
+
* @returns {Promise<any>} Event data
|
|
40
|
+
*/
|
|
41
|
+
function once(emitter, event, options = {}) {
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
const { timeout } = options;
|
|
44
|
+
|
|
45
|
+
const timeoutId = timeout
|
|
46
|
+
? // eslint-disable-next-line no-undef
|
|
47
|
+
setTimeout(() => {
|
|
48
|
+
emitter.removeListener(event, handler);
|
|
49
|
+
reject(new Error(`Event '${event}' timed out after ${timeout}ms`));
|
|
50
|
+
}, timeout)
|
|
51
|
+
: null;
|
|
52
|
+
|
|
53
|
+
const handler = (...args) => {
|
|
54
|
+
if (timeoutId) {
|
|
55
|
+
// eslint-disable-next-line no-undef
|
|
56
|
+
clearTimeout(timeoutId);
|
|
57
|
+
}
|
|
58
|
+
emitter.removeListener(event, handler);
|
|
59
|
+
resolve(args.length === 1 ? args[0] : args);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
emitter.once(event, handler);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
question,
|
|
68
|
+
closeReadline,
|
|
69
|
+
once,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
|
package/package.json
CHANGED
|
@@ -16,12 +16,19 @@ COMMAND=$(echo "$INPUT" | jq -r '.command // ""')
|
|
|
16
16
|
CWD=$(echo "$INPUT" | jq -r '.cwd // ""')
|
|
17
17
|
|
|
18
18
|
# Hard blocks - never allow these
|
|
19
|
+
# CRITICAL: These commands can cause catastrophic data loss
|
|
20
|
+
# git init, git reset --hard, and git push --force were added after an incident
|
|
21
|
+
# where an agent panicked at quality gates and wiped thousands of lines of work
|
|
19
22
|
HARD_BLOCKS=(
|
|
20
23
|
"rm -rf /"
|
|
21
24
|
"rm -rf /*"
|
|
22
25
|
"rm -rf ~"
|
|
23
26
|
"rm -rf $HOME"
|
|
24
27
|
"> /dev/sda"
|
|
28
|
+
"git init" # Can wipe entire git history and stashed changes
|
|
29
|
+
"git commit --amend --no-edit" # Can rewrite commit history destructively
|
|
30
|
+
"git reset --hard" # Can lose uncommitted work and stashed changes
|
|
31
|
+
"git push --force" # Can overwrite remote repository history
|
|
25
32
|
"dd if="
|
|
26
33
|
"mkfs"
|
|
27
34
|
"format c:"
|
|
@@ -38,10 +45,9 @@ for blocked in "${HARD_BLOCKS[@]}"; do
|
|
|
38
45
|
done
|
|
39
46
|
|
|
40
47
|
# Ask permission for risky operations
|
|
48
|
+
# Note: git commands moved to HARD_BLOCKS after catastrophic data loss incident
|
|
41
49
|
ASK_PERMISSION=(
|
|
42
50
|
"rm -rf"
|
|
43
|
-
"git push --force"
|
|
44
|
-
"git reset --hard"
|
|
45
51
|
"npm publish"
|
|
46
52
|
"docker rmi"
|
|
47
53
|
"docker system prune"
|
|
@@ -17,13 +17,11 @@ This directory contains modular rule files that Cursor uses to guide development
|
|
|
17
17
|
- `06-typescript-conventions.mdc` - TypeScript/JS conventions and best practices
|
|
18
18
|
- `07-process-ops.mdc` - Process discipline and server management
|
|
19
19
|
- `08-solid-and-architecture.mdc` - SOLID principles and architectural patterns
|
|
20
|
-
- `09-docstrings.mdc` - Language-specific docstring formats and
|
|
21
|
-
- `10-
|
|
22
|
-
- `
|
|
23
|
-
- `
|
|
24
|
-
- `
|
|
25
|
-
- `19-language-agnostic-standards.mdc` - Universal engineering standards across all programming languages
|
|
26
|
-
- `20-sophisticated-todo-detection.mdc` - Advanced TODO detection patterns and hidden implementation analysis
|
|
20
|
+
- `09-docstrings.mdc` - Language-specific docstring formats, standards, and file headers
|
|
21
|
+
- `10-documentation-quality-standards.mdc` - Engineering-grade documentation standards
|
|
22
|
+
- `11-scope-management-waivers.mdc` - Scope management, change budgets, and emergency waiver procedures
|
|
23
|
+
- `12-implementation-completeness.mdc` - Anti-fake implementation guardrails with sophisticated TODO detection
|
|
24
|
+
- `13-language-agnostic-standards.mdc` - Universal engineering standards (conditional - applies to code files only)
|
|
27
25
|
|
|
28
26
|
## How MDC Works
|
|
29
27
|
|
|
@@ -453,6 +453,29 @@ class HiddenTodoAnalyzer:
|
|
|
453
453
|
# Console and logging
|
|
454
454
|
r'console\.(log|warn|error|info)',
|
|
455
455
|
r'\blogging\s+implementation\b',
|
|
456
|
+
|
|
457
|
+
# TODO system documentation (false positives when documenting TODO system itself)
|
|
458
|
+
r'\btodo\s+template\s+system\b',
|
|
459
|
+
r'\btodo\s+template\b',
|
|
460
|
+
r'\btodo\s+instance\b',
|
|
461
|
+
r'\btodo\s+step\b',
|
|
462
|
+
r'\btodo\s+integration\b',
|
|
463
|
+
r'\btodo\s+system\b',
|
|
464
|
+
r'\btodotemplate\b',
|
|
465
|
+
r'\btodoinstance\b',
|
|
466
|
+
r'\btodostep\b',
|
|
467
|
+
r'\btodointegration\b',
|
|
468
|
+
r'\btodotemplatesystem\b',
|
|
469
|
+
r'\btodoprogress\b',
|
|
470
|
+
r'\btododependency\b',
|
|
471
|
+
r'\btodoqualityenforcer\b',
|
|
472
|
+
r'\btodoworkflowhooks\b',
|
|
473
|
+
r'\btodostatus\b',
|
|
474
|
+
r'\btodopriority\b',
|
|
475
|
+
r'\btodosteptype\b',
|
|
476
|
+
# Rust doc comment patterns when mentioning TODO system types
|
|
477
|
+
r'^\s*//[!]/.*\btodo\b.*(template|instance|step|integration|system)\b',
|
|
478
|
+
r'^\s*///.*\btodo\b.*(template|instance|step|integration|system)\b',
|
|
456
479
|
]
|
|
457
480
|
|
|
458
481
|
# Engineering-grade TODO template patterns (for suggestions)
|
|
@@ -612,6 +635,30 @@ class HiddenTodoAnalyzer:
|
|
|
612
635
|
for indicator in self.documentation_indicators:
|
|
613
636
|
if re.search(indicator, comment, re.IGNORECASE):
|
|
614
637
|
return True
|
|
638
|
+
|
|
639
|
+
# Check for Rust doc comments (//! and ///) that mention TODO system types
|
|
640
|
+
todo_system_types = [
|
|
641
|
+
r'todo\s+template',
|
|
642
|
+
r'todo\s+instance',
|
|
643
|
+
r'todo\s+step',
|
|
644
|
+
r'todo\s+integration',
|
|
645
|
+
r'todo\s+system',
|
|
646
|
+
r'todotemplate',
|
|
647
|
+
r'todoinstance',
|
|
648
|
+
r'todostep',
|
|
649
|
+
r'todointegration',
|
|
650
|
+
r'todotemplatesystem',
|
|
651
|
+
r'todoprogress',
|
|
652
|
+
r'tododependency',
|
|
653
|
+
]
|
|
654
|
+
|
|
655
|
+
# If comment mentions TODO system types and appears to be documentation, exclude it
|
|
656
|
+
if any(re.search(pattern, comment, re.IGNORECASE) for pattern in todo_system_types):
|
|
657
|
+
# Check if it's describing the system rather than a TODO item
|
|
658
|
+
# If it contains "TODO:" followed by a colon, it's likely a real TODO
|
|
659
|
+
if not re.search(r'\bTODO\s*:\s*', comment, re.IGNORECASE):
|
|
660
|
+
return True
|
|
661
|
+
|
|
615
662
|
return False
|
|
616
663
|
|
|
617
664
|
def has_todo_indicators(self, comment: str) -> bool:
|
|
@@ -1522,7 +1569,7 @@ class HiddenTodoAnalyzer:
|
|
|
1522
1569
|
'blocking_analysis': {}
|
|
1523
1570
|
}
|
|
1524
1571
|
|
|
1525
|
-
print(f"
|
|
1572
|
+
print(f"Found {len(staged_files)} staged files to analyze")
|
|
1526
1573
|
|
|
1527
1574
|
# Analyze staged files
|
|
1528
1575
|
analysis_results = self.analyze_files(staged_files, min_confidence)
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: File headers and authorship
|
|
3
|
-
globs:
|
|
4
|
-
alwaysApply: false
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# File Header & Attribution
|
|
8
|
-
|
|
9
|
-
For top-of-file documentation, include author signature:
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
Author: @darianrosebrook
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Only in source files with public entrypoints or non-trivial modules.
|