autoworkflow 1.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 ADDED
@@ -0,0 +1,206 @@
1
+ # autoworkflow
2
+
3
+ Zero-config code quality enforcement. Just install and commit.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/autoworkflow.svg)](https://www.npmjs.com/package/autoworkflow)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm install autoworkflow --save-dev
12
+ ```
13
+
14
+ **That's it.** Enforcement is now active on all commits.
15
+
16
+ ## What Happens
17
+
18
+ ```
19
+ npm install autoworkflow
20
+
21
+
22
+ ┌──────────────────────────────────┐
23
+ │ Auto-setup (postinstall): │
24
+ │ ✓ Creates .husky/pre-commit │
25
+ │ ✓ Creates .husky/commit-msg │
26
+ │ ✓ Creates enforce.config.json │
27
+ │ ✓ Adds prepare script │
28
+ └──────────────────────────────────┘
29
+
30
+
31
+ Every "git commit" runs checks automatically
32
+ ```
33
+
34
+ ## Default Checks
35
+
36
+ | Check | Blocking | Auto-Fix |
37
+ |-------|----------|----------|
38
+ | No TODO/FIXME comments | ✅ | - |
39
+ | No console.log | ✅ | - |
40
+ | TypeScript errors | ✅ | - |
41
+ | ESLint errors | ✅ | ✅ |
42
+ | Circular dependencies | ✅ | - |
43
+ | Commit message format | ✅ | - |
44
+
45
+ ## How It Works
46
+
47
+ ### Pre-Commit Hook
48
+ Every time you run `git commit`, autoworkflow:
49
+ 1. Runs all enabled checks
50
+ 2. If ESLint fails, auto-fixes and retries (up to 5 attempts)
51
+ 3. Blocks commit if any blocking check fails
52
+ 4. Allows commit if all checks pass
53
+
54
+ ### Commit Message Validation
55
+ Enforces [Conventional Commits](https://www.conventionalcommits.org/):
56
+
57
+ ```bash
58
+ # ✅ Valid
59
+ git commit -m "feat: add user authentication"
60
+ git commit -m "fix(api): resolve timeout issue"
61
+ git commit -m "docs: update installation guide"
62
+
63
+ # ❌ Invalid (blocked)
64
+ git commit -m "fixed stuff"
65
+ git commit -m "WIP"
66
+ ```
67
+
68
+ ## Configuration
69
+
70
+ Edit `enforce.config.json` in your project root:
71
+
72
+ ```json
73
+ {
74
+ "rules": {
75
+ "no-todo-comments": {
76
+ "enabled": true,
77
+ "blocking": true,
78
+ "patterns": ["TODO", "FIXME", "XXX", "HACK"]
79
+ },
80
+ "no-console-logs": {
81
+ "enabled": true,
82
+ "blocking": true
83
+ },
84
+ "typescript": {
85
+ "enabled": true,
86
+ "blocking": true,
87
+ "command": "npx tsc --noEmit"
88
+ },
89
+ "eslint": {
90
+ "enabled": true,
91
+ "blocking": true,
92
+ "autoFix": true,
93
+ "command": "npx eslint . --max-warnings 0",
94
+ "fixCommand": "npx eslint . --fix"
95
+ },
96
+ "circular-deps": {
97
+ "enabled": true,
98
+ "blocking": true,
99
+ "paths": ["src/"]
100
+ }
101
+ },
102
+ "fixLoop": {
103
+ "enabled": true,
104
+ "maxAttempts": 5,
105
+ "autoFixRules": ["eslint"]
106
+ },
107
+ "commitMessage": {
108
+ "enabled": true,
109
+ "pattern": "^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\\(.+\\))?: .{1,72}$"
110
+ }
111
+ }
112
+ ```
113
+
114
+ ### Rule Options
115
+
116
+ | Option | Type | Description |
117
+ |--------|------|-------------|
118
+ | `enabled` | boolean | Enable/disable the rule |
119
+ | `blocking` | boolean | If true, fails the commit on violation |
120
+ | `autoFix` | boolean | Enable auto-fix for this rule |
121
+ | `command` | string | Custom check command |
122
+ | `fixCommand` | string | Custom fix command |
123
+
124
+ ### Disable a Rule
125
+
126
+ ```json
127
+ {
128
+ "rules": {
129
+ "no-console-logs": { "enabled": false }
130
+ }
131
+ }
132
+ ```
133
+
134
+ ### Non-Blocking Warning
135
+
136
+ ```json
137
+ {
138
+ "rules": {
139
+ "circular-deps": { "enabled": true, "blocking": false }
140
+ }
141
+ }
142
+ ```
143
+
144
+ ## CLI Commands
145
+
146
+ ```bash
147
+ # Run all checks manually
148
+ npx autoworkflow run
149
+
150
+ # List all rules and their status
151
+ npx autoworkflow list
152
+
153
+ # Validate a commit message
154
+ npx autoworkflow commit-msg .git/COMMIT_EDITMSG
155
+
156
+ # Show help
157
+ npx autoworkflow --help
158
+ ```
159
+
160
+ ## Fix Loop
161
+
162
+ When ESLint (or other auto-fixable rules) fail:
163
+
164
+ ```
165
+ [1/5] ESLint... ❌
166
+ src/app.ts: 'unused' is defined but never used
167
+
168
+ 🔧 Attempting auto-fix...
169
+ ✅ Fixed: ESLint
170
+
171
+ 🔄 Fix Loop - Attempt 2/5
172
+
173
+ [1/5] ESLint... ✅
174
+
175
+ ✅ ALL CHECKS PASSED
176
+ ```
177
+
178
+ ## Skip Enforcement (Emergency)
179
+
180
+ ```bash
181
+ git commit --no-verify -m "emergency: hotfix for production"
182
+ ```
183
+
184
+ Use sparingly. All skipped commits are your responsibility.
185
+
186
+ ## Requirements
187
+
188
+ - Node.js >= 18.0.0
189
+ - Git repository (initialized with `git init`)
190
+
191
+ ### Optional Peer Dependencies
192
+
193
+ - TypeScript >= 5.0.0 (for TypeScript checks)
194
+ - ESLint >= 8.0.0 (for ESLint checks)
195
+
196
+ ## Uninstall
197
+
198
+ ```bash
199
+ npm uninstall autoworkflow
200
+ rm -rf .husky
201
+ rm enforce.config.json
202
+ ```
203
+
204
+ ## License
205
+
206
+ MIT
package/bin/cli.js ADDED
@@ -0,0 +1,299 @@
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();
package/lib/index.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * autoworkflow
3
+ *
4
+ * Automatic code quality enforcement with fix loops.
5
+ */
6
+
7
+ module.exports = {
8
+ version: '1.0.0',
9
+ };
package/lib/install.js ADDED
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * autoworkflow - Auto-Install Script
4
+ *
5
+ * This runs automatically when the package is installed.
6
+ * Sets up Git hooks and creates default config.
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const { execSync } = require('child_process');
12
+
13
+ // Colors for output
14
+ const c = {
15
+ reset: '\x1b[0m',
16
+ green: '\x1b[32m',
17
+ yellow: '\x1b[33m',
18
+ blue: '\x1b[34m',
19
+ red: '\x1b[31m',
20
+ };
21
+
22
+ function log(color, message) {
23
+ console.log(`${color}${message}${c.reset}`);
24
+ }
25
+
26
+ function findProjectRoot() {
27
+ // Start from where npm install was run (not node_modules)
28
+ let dir = process.env.INIT_CWD || process.cwd();
29
+
30
+ // Walk up to find package.json (project root)
31
+ while (dir !== path.dirname(dir)) {
32
+ if (fs.existsSync(path.join(dir, 'package.json'))) {
33
+ // Make sure it's not our own package.json
34
+ const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf-8'));
35
+ if (pkg.name !== 'autoworkflow') {
36
+ return dir;
37
+ }
38
+ }
39
+ dir = path.dirname(dir);
40
+ }
41
+
42
+ return process.env.INIT_CWD || process.cwd();
43
+ }
44
+
45
+ function isGitRepo(dir) {
46
+ return fs.existsSync(path.join(dir, '.git'));
47
+ }
48
+
49
+ function ensureHuskyDir(projectRoot) {
50
+ const huskyDir = path.join(projectRoot, '.husky');
51
+ if (!fs.existsSync(huskyDir)) {
52
+ fs.mkdirSync(huskyDir, { recursive: true });
53
+ }
54
+
55
+ // Create husky internal dir
56
+ const huskyInternalDir = path.join(huskyDir, '_');
57
+ if (!fs.existsSync(huskyInternalDir)) {
58
+ fs.mkdirSync(huskyInternalDir, { recursive: true });
59
+ }
60
+
61
+ return huskyDir;
62
+ }
63
+
64
+ function createPreCommitHook(huskyDir, projectRoot) {
65
+ const hookPath = path.join(huskyDir, 'pre-commit');
66
+
67
+ // Check if hook already exists
68
+ if (fs.existsSync(hookPath)) {
69
+ log(c.yellow, ' ⚠ Pre-commit hook already exists, skipping...');
70
+ return;
71
+ }
72
+
73
+ const hookContent = `#!/bin/bash
74
+ # ============================================
75
+ # autoworkflow - Pre-Commit Hook
76
+ # ============================================
77
+ # Auto-generated. Modify enforce.config.json to customize.
78
+ # ============================================
79
+
80
+ npx autoworkflow run
81
+ `;
82
+
83
+ fs.writeFileSync(hookPath, hookContent);
84
+ fs.chmodSync(hookPath, '755');
85
+ log(c.green, ' ✓ Created pre-commit hook');
86
+ }
87
+
88
+ function createCommitMsgHook(huskyDir) {
89
+ const hookPath = path.join(huskyDir, 'commit-msg');
90
+
91
+ if (fs.existsSync(hookPath)) {
92
+ log(c.yellow, ' ⚠ Commit-msg hook already exists, skipping...');
93
+ return;
94
+ }
95
+
96
+ const hookContent = `#!/bin/bash
97
+ # ============================================
98
+ # autoworkflow - Commit Message Hook
99
+ # ============================================
100
+
101
+ npx autoworkflow commit-msg "$1"
102
+ `;
103
+
104
+ fs.writeFileSync(hookPath, hookContent);
105
+ fs.chmodSync(hookPath, '755');
106
+ log(c.green, ' ✓ Created commit-msg hook');
107
+ }
108
+
109
+ function createDefaultConfig(projectRoot) {
110
+ const configPath = path.join(projectRoot, 'enforce.config.json');
111
+
112
+ if (fs.existsSync(configPath)) {
113
+ log(c.yellow, ' ⚠ Config file already exists, skipping...');
114
+ return;
115
+ }
116
+
117
+ const defaultConfig = {
118
+ version: '1.0.0',
119
+ rules: {
120
+ 'no-todo-comments': {
121
+ enabled: true,
122
+ blocking: true,
123
+ patterns: ['TODO', 'FIXME', 'XXX', 'HACK']
124
+ },
125
+ 'no-console-logs': {
126
+ enabled: true,
127
+ blocking: true,
128
+ allow: ['warn', 'error']
129
+ },
130
+ 'typescript': {
131
+ enabled: true,
132
+ blocking: true,
133
+ command: 'npx tsc --noEmit'
134
+ },
135
+ 'eslint': {
136
+ enabled: true,
137
+ blocking: true,
138
+ autoFix: true,
139
+ command: 'npx eslint . --max-warnings 0',
140
+ fixCommand: 'npx eslint . --fix'
141
+ },
142
+ 'circular-deps': {
143
+ enabled: true,
144
+ blocking: true,
145
+ paths: ['src/']
146
+ }
147
+ },
148
+ fixLoop: {
149
+ enabled: true,
150
+ maxAttempts: 5,
151
+ autoFixRules: ['eslint']
152
+ },
153
+ commitMessage: {
154
+ enabled: true,
155
+ pattern: '^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\\(.+\\))?: .{1,72}$'
156
+ }
157
+ };
158
+
159
+ fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));
160
+ log(c.green, ' ✓ Created enforce.config.json');
161
+ }
162
+
163
+ function initializeHusky(projectRoot) {
164
+ try {
165
+ // Initialize husky
166
+ execSync('npx husky', { cwd: projectRoot, stdio: 'ignore' });
167
+ log(c.green, ' ✓ Initialized husky');
168
+ } catch (error) {
169
+ // Husky might already be initialized, that's okay
170
+ }
171
+ }
172
+
173
+ function updatePackageJson(projectRoot) {
174
+ const pkgPath = path.join(projectRoot, 'package.json');
175
+
176
+ if (!fs.existsSync(pkgPath)) {
177
+ log(c.yellow, ' ⚠ No package.json found');
178
+ return;
179
+ }
180
+
181
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
182
+
183
+ // Add prepare script for husky if not exists
184
+ if (!pkg.scripts) {
185
+ pkg.scripts = {};
186
+ }
187
+
188
+ if (!pkg.scripts.prepare) {
189
+ pkg.scripts.prepare = 'husky';
190
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
191
+ log(c.green, ' ✓ Added prepare script to package.json');
192
+ }
193
+ }
194
+
195
+ function main() {
196
+ console.log('');
197
+ log(c.blue, '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
198
+ log(c.blue, '🔧 autoworkflow - Setting up...');
199
+ log(c.blue, '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
200
+ console.log('');
201
+
202
+ const projectRoot = findProjectRoot();
203
+
204
+ // Skip if we're in the package itself
205
+ if (projectRoot.includes('node_modules')) {
206
+ return;
207
+ }
208
+
209
+ log(c.blue, `Project root: ${projectRoot}`);
210
+ console.log('');
211
+
212
+ // Check if it's a git repo
213
+ if (!isGitRepo(projectRoot)) {
214
+ log(c.yellow, '⚠ Not a git repository. Initialize git first:');
215
+ log(c.yellow, ' git init');
216
+ console.log('');
217
+ return;
218
+ }
219
+
220
+ // Setup husky
221
+ const huskyDir = ensureHuskyDir(projectRoot);
222
+ initializeHusky(projectRoot);
223
+
224
+ // Create hooks
225
+ createPreCommitHook(huskyDir, projectRoot);
226
+ createCommitMsgHook(huskyDir);
227
+
228
+ // Create config
229
+ createDefaultConfig(projectRoot);
230
+
231
+ // Update package.json
232
+ updatePackageJson(projectRoot);
233
+
234
+ console.log('');
235
+ log(c.green, '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
236
+ log(c.green, '✅ Setup complete!');
237
+ log(c.green, '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
238
+ console.log('');
239
+ log(c.blue, 'Enforcement is now active. Just commit as usual:');
240
+ console.log(' git add .');
241
+ console.log(' git commit -m "feat: your feature"');
242
+ console.log('');
243
+ log(c.blue, 'Configuration: enforce.config.json');
244
+ log(c.blue, 'CLI commands: npx autoworkflow --help');
245
+ console.log('');
246
+ }
247
+
248
+ // Only run if this is the actual install (not during npm pack)
249
+ if (!process.env.npm_config_ignore_scripts) {
250
+ main();
251
+ }
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "autoworkflow",
3
+ "version": "1.0.0",
4
+ "description": "Automatic code quality enforcement with fix loops, configurable rules, and Git hook integration",
5
+ "keywords": [
6
+ "git-hooks",
7
+ "pre-commit",
8
+ "eslint",
9
+ "typescript",
10
+ "code-quality",
11
+ "enforcement",
12
+ "husky",
13
+ "lint-staged",
14
+ "commitlint",
15
+ "autoworkflow"
16
+ ],
17
+ "author": "autoworkflow",
18
+ "license": "MIT",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/autoworkflow/autoworkflow"
22
+ },
23
+ "homepage": "https://github.com/autoworkflow/autoworkflow#readme",
24
+ "bugs": {
25
+ "url": "https://github.com/autoworkflow/autoworkflow/issues"
26
+ },
27
+ "main": "lib/index.js",
28
+ "bin": {
29
+ "autoworkflow": "bin/cli.js"
30
+ },
31
+ "files": [
32
+ "bin",
33
+ "lib",
34
+ "templates"
35
+ ],
36
+ "scripts": {
37
+ "postinstall": "node lib/install.js"
38
+ },
39
+ "dependencies": {
40
+ "husky": "^9.1.7",
41
+ "madge": "^8.0.0"
42
+ },
43
+ "peerDependencies": {
44
+ "typescript": ">=5.0.0",
45
+ "eslint": ">=8.0.0"
46
+ },
47
+ "peerDependenciesMeta": {
48
+ "typescript": {
49
+ "optional": true
50
+ },
51
+ "eslint": {
52
+ "optional": true
53
+ }
54
+ },
55
+ "engines": {
56
+ "node": ">=18.0.0"
57
+ }
58
+ }