autoworkflow 1.2.0 → 2.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/.claude/commands/analyze.md +77 -0
- package/.claude/commands/audit.md +220 -0
- package/.claude/commands/build.md +159 -0
- package/.claude/commands/commit.md +165 -0
- package/.claude/commands/fix.md +119 -0
- package/.claude/commands/plan.md +101 -0
- package/.claude/commands/suggest.md +195 -0
- package/.claude/commands/verify.md +113 -0
- package/.claude/settings.json +129 -0
- package/.claude/settings.local.json +9 -0
- package/.prettierrc +11 -0
- package/.vscode/extensions.json +27 -0
- package/.vscode/settings.json +69 -0
- package/.vscode/tasks.json +161 -0
- package/CLAUDE.md +344 -0
- package/README.md +180 -195
- package/eslint.config.example.js +83 -0
- package/hooks/commit-msg +137 -0
- package/hooks/pre-commit +152 -0
- package/instructions/AI_RULES.md +284 -0
- package/instructions/BLUEPRINT.md +170 -0
- package/instructions/CLAUDE.md +472 -0
- package/package.json +45 -45
- package/scripts/autoworkflow.sh +324 -0
- package/scripts/check-ui-enforcement.sh +177 -0
- package/scripts/ensure-no-errors.sh +116 -0
- package/scripts/run-verification.sh +112 -0
- package/scripts/setup.sh +201 -0
- package/system/gates.md +390 -0
- package/system/loops.md +348 -0
- package/system/router.md +415 -0
- package/system/triggers.md +369 -0
- package/tsconfig.example.json +52 -0
- package/bin/cli.js +0 -299
- package/lib/index.js +0 -9
- package/lib/install.js +0 -745
package/bin/cli.js
DELETED
|
@@ -1,299 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* autoworkflow - CLI
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* npx autoworkflow run Run all checks (with fix loop)
|
|
7
|
-
* npx autoworkflow list List all rules
|
|
8
|
-
* npx autoworkflow check <id> Run specific rule
|
|
9
|
-
* npx autoworkflow commit-msg Validate commit message
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const fs = require('fs');
|
|
13
|
-
const path = require('path');
|
|
14
|
-
const { execSync } = require('child_process');
|
|
15
|
-
|
|
16
|
-
// Colors
|
|
17
|
-
const c = {
|
|
18
|
-
reset: '\x1b[0m',
|
|
19
|
-
red: '\x1b[31m',
|
|
20
|
-
green: '\x1b[32m',
|
|
21
|
-
yellow: '\x1b[33m',
|
|
22
|
-
blue: '\x1b[34m',
|
|
23
|
-
cyan: '\x1b[36m',
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const log = (color, msg) => console.log(`${color}${msg}${c.reset}`);
|
|
27
|
-
|
|
28
|
-
// Find config file
|
|
29
|
-
function findConfig() {
|
|
30
|
-
const configPath = path.join(process.cwd(), 'enforce.config.json');
|
|
31
|
-
if (fs.existsSync(configPath)) {
|
|
32
|
-
return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
33
|
-
}
|
|
34
|
-
// Return defaults
|
|
35
|
-
return {
|
|
36
|
-
rules: {
|
|
37
|
-
'no-todo-comments': { enabled: true, blocking: true },
|
|
38
|
-
'no-console-logs': { enabled: true, blocking: true },
|
|
39
|
-
'typescript': { enabled: true, blocking: true },
|
|
40
|
-
'eslint': { enabled: true, blocking: true, autoFix: true },
|
|
41
|
-
'circular-deps': { enabled: true, blocking: true },
|
|
42
|
-
},
|
|
43
|
-
fixLoop: { enabled: true, maxAttempts: 5, autoFixRules: ['eslint'] },
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Rule implementations
|
|
48
|
-
const rules = {
|
|
49
|
-
'no-todo-comments': {
|
|
50
|
-
name: 'No TODO/FIXME Comments',
|
|
51
|
-
check: (config) => {
|
|
52
|
-
try {
|
|
53
|
-
const patterns = config.patterns || ['TODO', 'FIXME', 'XXX', 'HACK'];
|
|
54
|
-
const output = execSync(
|
|
55
|
-
`grep -rn "${patterns.join('\\|')}" --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" . 2>/dev/null | grep -v node_modules | grep -v ".git" || true`,
|
|
56
|
-
{ encoding: 'utf-8' }
|
|
57
|
-
).trim();
|
|
58
|
-
const matches = output ? output.split('\n').filter(Boolean) : [];
|
|
59
|
-
return { passed: matches.length === 0, details: matches.slice(0, 5) };
|
|
60
|
-
} catch {
|
|
61
|
-
return { passed: true, details: [] };
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
'no-console-logs': {
|
|
67
|
-
name: 'No Console Logs',
|
|
68
|
-
check: (config) => {
|
|
69
|
-
try {
|
|
70
|
-
const output = execSync(
|
|
71
|
-
'grep -rn "console\\.\\(log\\|debug\\|info\\)" --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" . 2>/dev/null | grep -v node_modules | grep -v ".git" | grep -v "eslint-disable" || true',
|
|
72
|
-
{ encoding: 'utf-8' }
|
|
73
|
-
).trim();
|
|
74
|
-
const matches = output ? output.split('\n').filter(Boolean) : [];
|
|
75
|
-
return { passed: matches.length === 0, details: matches.slice(0, 5) };
|
|
76
|
-
} catch {
|
|
77
|
-
return { passed: true, details: [] };
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
|
|
82
|
-
'typescript': {
|
|
83
|
-
name: 'TypeScript',
|
|
84
|
-
check: (config) => {
|
|
85
|
-
try {
|
|
86
|
-
const cmd = config.command || 'npx tsc --noEmit';
|
|
87
|
-
execSync(cmd, { encoding: 'utf-8', stdio: 'pipe' });
|
|
88
|
-
return { passed: true, details: [] };
|
|
89
|
-
} catch (error) {
|
|
90
|
-
const output = error.stdout || error.message || '';
|
|
91
|
-
const errors = output.split('\n').filter(l => l.includes('error')).slice(0, 5);
|
|
92
|
-
return { passed: false, details: errors };
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
|
|
97
|
-
'eslint': {
|
|
98
|
-
name: 'ESLint',
|
|
99
|
-
check: (config) => {
|
|
100
|
-
try {
|
|
101
|
-
const cmd = config.command || 'npx eslint . --max-warnings 0';
|
|
102
|
-
execSync(cmd, { encoding: 'utf-8', stdio: 'pipe' });
|
|
103
|
-
return { passed: true, details: [] };
|
|
104
|
-
} catch (error) {
|
|
105
|
-
const output = error.stdout || error.message || '';
|
|
106
|
-
const errors = output.split('\n').filter(l => l.includes('error') || l.includes('warning')).slice(0, 5);
|
|
107
|
-
return { passed: false, details: errors };
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
|
-
fix: (config) => {
|
|
111
|
-
try {
|
|
112
|
-
const cmd = config.fixCommand || 'npx eslint . --fix';
|
|
113
|
-
execSync(cmd, { encoding: 'utf-8', stdio: 'pipe' });
|
|
114
|
-
return true;
|
|
115
|
-
} catch {
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
'circular-deps': {
|
|
122
|
-
name: 'Circular Dependencies',
|
|
123
|
-
check: (config) => {
|
|
124
|
-
try {
|
|
125
|
-
const paths = config.paths || ['src/'];
|
|
126
|
-
const output = execSync(
|
|
127
|
-
`npx madge --circular --extensions ts,tsx,js,jsx ${paths.join(' ')} 2>&1`,
|
|
128
|
-
{ encoding: 'utf-8' }
|
|
129
|
-
);
|
|
130
|
-
const hasCircular = output.includes('Found');
|
|
131
|
-
return { passed: !hasCircular, details: hasCircular ? output.split('\n').slice(0, 5) : [] };
|
|
132
|
-
} catch {
|
|
133
|
-
return { passed: true, details: [] };
|
|
134
|
-
}
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
// Run all checks with fix loop
|
|
140
|
-
function runChecks(config) {
|
|
141
|
-
const fixLoop = config.fixLoop || { enabled: true, maxAttempts: 5 };
|
|
142
|
-
let attempt = 0;
|
|
143
|
-
let results = [];
|
|
144
|
-
const fixedRules = [];
|
|
145
|
-
|
|
146
|
-
log(c.blue, '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
147
|
-
log(c.blue, '🔒 autoworkflow');
|
|
148
|
-
log(c.blue, '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
149
|
-
console.log('');
|
|
150
|
-
|
|
151
|
-
while (attempt < fixLoop.maxAttempts) {
|
|
152
|
-
attempt++;
|
|
153
|
-
|
|
154
|
-
if (attempt > 1) {
|
|
155
|
-
log(c.cyan, `\n🔄 Fix Loop - Attempt ${attempt}/${fixLoop.maxAttempts}\n`);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
results = [];
|
|
159
|
-
let index = 0;
|
|
160
|
-
const ruleIds = Object.keys(config.rules).filter(id => config.rules[id]?.enabled);
|
|
161
|
-
|
|
162
|
-
for (const ruleId of ruleIds) {
|
|
163
|
-
const rule = rules[ruleId];
|
|
164
|
-
const ruleConfig = config.rules[ruleId];
|
|
165
|
-
if (!rule) continue;
|
|
166
|
-
|
|
167
|
-
index++;
|
|
168
|
-
const result = rule.check(ruleConfig);
|
|
169
|
-
const status = result.passed ? c.green + '✅' : (ruleConfig.blocking ? c.red + '❌' : c.yellow + '⚠️');
|
|
170
|
-
|
|
171
|
-
console.log(`${c.yellow}[${index}/${ruleIds.length}]${c.reset} ${rule.name}... ${status}${c.reset}`);
|
|
172
|
-
|
|
173
|
-
if (!result.passed && result.details.length > 0) {
|
|
174
|
-
result.details.forEach(d => console.log(` ${d}`));
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
results.push({ ruleId, ...result, blocking: ruleConfig.blocking, fixable: !!rule.fix && ruleConfig.autoFix });
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Check if we need to fix
|
|
181
|
-
const failedFixable = results.filter(
|
|
182
|
-
r => !r.passed && r.fixable && (fixLoop.autoFixRules || []).includes(r.ruleId)
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
if (failedFixable.length === 0) break;
|
|
186
|
-
|
|
187
|
-
// Attempt fixes
|
|
188
|
-
log(c.cyan, '\n🔧 Attempting auto-fix...');
|
|
189
|
-
for (const result of failedFixable) {
|
|
190
|
-
const rule = rules[result.ruleId];
|
|
191
|
-
const ruleConfig = config.rules[result.ruleId];
|
|
192
|
-
if (rule?.fix?.(ruleConfig)) {
|
|
193
|
-
log(c.green, ` ✅ Fixed: ${rule.name}`);
|
|
194
|
-
if (!fixedRules.includes(result.ruleId)) fixedRules.push(result.ruleId);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Summary
|
|
200
|
-
const failed = results.filter(r => !r.passed && r.blocking).length;
|
|
201
|
-
|
|
202
|
-
console.log('');
|
|
203
|
-
log(c.blue, '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
204
|
-
|
|
205
|
-
if (failed > 0) {
|
|
206
|
-
log(c.red, '⛔ COMMIT BLOCKED');
|
|
207
|
-
log(c.red, ` ${failed} check(s) failed. Fix issues and try again.`);
|
|
208
|
-
process.exit(1);
|
|
209
|
-
} else {
|
|
210
|
-
log(c.green, '✅ ALL CHECKS PASSED');
|
|
211
|
-
if (fixedRules.length > 0) {
|
|
212
|
-
log(c.green, ` Auto-fixed: ${fixedRules.join(', ')}`);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
console.log('');
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Validate commit message
|
|
219
|
-
function validateCommitMessage(msgFile) {
|
|
220
|
-
const config = findConfig();
|
|
221
|
-
const commitConfig = config.commitMessage || {};
|
|
222
|
-
|
|
223
|
-
if (!commitConfig.enabled) return;
|
|
224
|
-
|
|
225
|
-
const message = fs.readFileSync(msgFile, 'utf-8').trim().split('\n')[0];
|
|
226
|
-
const pattern = commitConfig.pattern || '^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\\(.+\\))?: .{1,72}$';
|
|
227
|
-
|
|
228
|
-
if (!new RegExp(pattern).test(message)) {
|
|
229
|
-
log(c.red, '');
|
|
230
|
-
log(c.red, '⛔ Invalid commit message format!');
|
|
231
|
-
log(c.red, '');
|
|
232
|
-
log(c.yellow, 'Expected: type(scope): description');
|
|
233
|
-
log(c.yellow, 'Examples:');
|
|
234
|
-
console.log(' feat: add user authentication');
|
|
235
|
-
console.log(' fix(auth): resolve login bug');
|
|
236
|
-
console.log(' docs: update readme');
|
|
237
|
-
log(c.red, '');
|
|
238
|
-
log(c.red, `Your message: "${message}"`);
|
|
239
|
-
log(c.red, '');
|
|
240
|
-
process.exit(1);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// List rules
|
|
245
|
-
function listRules(config) {
|
|
246
|
-
log(c.blue, 'Available rules:');
|
|
247
|
-
console.log('');
|
|
248
|
-
|
|
249
|
-
for (const [id, ruleConfig] of Object.entries(config.rules)) {
|
|
250
|
-
const rule = rules[id];
|
|
251
|
-
const status = ruleConfig.enabled ? c.green + '✅' : c.red + '❌';
|
|
252
|
-
const blocking = ruleConfig.blocking ? '🔒' : '⚠️';
|
|
253
|
-
const name = rule?.name || id;
|
|
254
|
-
console.log(` ${status}${c.reset} ${blocking} ${name} (${id})`);
|
|
255
|
-
}
|
|
256
|
-
console.log('');
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// Main
|
|
260
|
-
function main() {
|
|
261
|
-
const args = process.argv.slice(2);
|
|
262
|
-
const command = args[0] || 'run';
|
|
263
|
-
const config = findConfig();
|
|
264
|
-
|
|
265
|
-
switch (command) {
|
|
266
|
-
case 'run':
|
|
267
|
-
runChecks(config);
|
|
268
|
-
break;
|
|
269
|
-
|
|
270
|
-
case 'commit-msg':
|
|
271
|
-
const msgFile = args[1];
|
|
272
|
-
if (msgFile) validateCommitMessage(msgFile);
|
|
273
|
-
break;
|
|
274
|
-
|
|
275
|
-
case 'list':
|
|
276
|
-
listRules(config);
|
|
277
|
-
break;
|
|
278
|
-
|
|
279
|
-
case '--help':
|
|
280
|
-
case '-h':
|
|
281
|
-
console.log('');
|
|
282
|
-
log(c.blue, 'autoworkflow');
|
|
283
|
-
console.log('');
|
|
284
|
-
console.log('Usage:');
|
|
285
|
-
console.log(' npx autoworkflow run Run all checks');
|
|
286
|
-
console.log(' npx autoworkflow list List all rules');
|
|
287
|
-
console.log(' npx autoworkflow commit-msg Validate commit message');
|
|
288
|
-
console.log(' npx autoworkflow --help Show this help');
|
|
289
|
-
console.log('');
|
|
290
|
-
console.log('Configuration: enforce.config.json');
|
|
291
|
-
console.log('');
|
|
292
|
-
break;
|
|
293
|
-
|
|
294
|
-
default:
|
|
295
|
-
runChecks(config);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
main();
|