autoworkflow 3.7.0 → 3.8.1
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/settings.json +5 -5
- package/README.md +27 -5
- package/bin/cli.js +343 -73
- package/package.json +1 -1
package/.claude/settings.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"hooks": [
|
|
12
12
|
{
|
|
13
13
|
"type": "command",
|
|
14
|
-
"command": "
|
|
14
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/session-check.sh",
|
|
15
15
|
"timeout": 10,
|
|
16
16
|
"statusMessage": "Checking project state..."
|
|
17
17
|
}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"hooks": [
|
|
25
25
|
{
|
|
26
26
|
"type": "command",
|
|
27
|
-
"command": "
|
|
27
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/pre-edit.sh",
|
|
28
28
|
"timeout": 5,
|
|
29
29
|
"statusMessage": "Checking plan approval..."
|
|
30
30
|
}
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"hooks": [
|
|
36
36
|
{
|
|
37
37
|
"type": "command",
|
|
38
|
-
"command": "
|
|
38
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/pre-tool-router.sh \"$TOOL_INPUT\"",
|
|
39
39
|
"timeout": 300,
|
|
40
40
|
"statusMessage": "Checking workflow gates..."
|
|
41
41
|
}
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"hooks": [
|
|
49
49
|
{
|
|
50
50
|
"type": "command",
|
|
51
|
-
"command": "
|
|
51
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/post-edit.sh",
|
|
52
52
|
"timeout": 120,
|
|
53
53
|
"statusMessage": "Running verification..."
|
|
54
54
|
}
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"hooks": [
|
|
60
60
|
{
|
|
61
61
|
"type": "command",
|
|
62
|
-
"command": "
|
|
62
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/post-bash-router.sh \"$TOOL_INPUT\"",
|
|
63
63
|
"timeout": 30,
|
|
64
64
|
"statusMessage": "Checking post-command actions..."
|
|
65
65
|
}
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Automated workflow enforcement for Claude Code via hooks and system prompts.
|
|
4
4
|
|
|
5
|
-
**v3.
|
|
5
|
+
**v3.8.0** - Smart CLI updates: version tracking + user file preservation.
|
|
6
6
|
|
|
7
7
|
When you use Claude Code with AutoWorkflow, hooks automatically enforce workflow phases, block unauthorized edits, and guide Claude through a structured process for all coding tasks.
|
|
8
8
|
|
|
@@ -14,10 +14,32 @@ When you use Claude Code with AutoWorkflow, hooks automatically enforce workflow
|
|
|
14
14
|
npx autoworkflow init
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
### CLI Commands
|
|
18
|
+
|
|
19
|
+
| Command | Description |
|
|
20
|
+
|---------|-------------|
|
|
21
|
+
| `npx autoworkflow init` | Fresh install (required + recommended files) |
|
|
22
|
+
| `npx autoworkflow init --all` | Include .vscode and config templates |
|
|
23
|
+
| `npx autoworkflow init --minimal` | Required files only |
|
|
24
|
+
| `npx autoworkflow init --force` | Full reinstall (backup + overwrite all) |
|
|
25
|
+
| `npx autoworkflow update` | Smart update - core files only, preserve user files |
|
|
26
|
+
| `npx autoworkflow status` | Show installed version and components |
|
|
27
|
+
|
|
28
|
+
### Smart Updates (v3.8.0)
|
|
29
|
+
|
|
30
|
+
The CLI now tracks installed versions and preserves user customizations:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
npx autoworkflow status # Check version and what would change
|
|
34
|
+
npx autoworkflow update # Update core files, preserve BLUEPRINT.md
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
| File Category | On `init` | On `update` |
|
|
38
|
+
|---------------|-----------|-------------|
|
|
39
|
+
| **Core** (hooks, system, CLAUDE.md) | Installed | Updated |
|
|
40
|
+
| **User** (BLUEPRINT.md, AI_RULES.md) | Created if missing | **Preserved** |
|
|
41
|
+
| **Commands/Skills** | Installed | Updated |
|
|
42
|
+
| **Optional** (.vscode, configs) | Only with `--all` | Skipped |
|
|
21
43
|
|
|
22
44
|
---
|
|
23
45
|
|
package/bin/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { existsSync, cpSync, mkdirSync, chmodSync, renameSync, unlinkSync } from 'fs';
|
|
3
|
+
import { existsSync, cpSync, mkdirSync, chmodSync, renameSync, unlinkSync, readFileSync, writeFileSync } from 'fs';
|
|
4
4
|
import { dirname, join } from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
|
|
@@ -11,6 +11,10 @@ const packageRoot = join(__dirname, '..');
|
|
|
11
11
|
const args = process.argv.slice(2);
|
|
12
12
|
const command = args[0];
|
|
13
13
|
|
|
14
|
+
// Get package version
|
|
15
|
+
const packageJson = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8'));
|
|
16
|
+
const PACKAGE_VERSION = packageJson.version;
|
|
17
|
+
|
|
14
18
|
const colors = {
|
|
15
19
|
green: (text) => `\x1b[32m${text}\x1b[0m`,
|
|
16
20
|
yellow: (text) => `\x1b[33m${text}\x1b[0m`,
|
|
@@ -20,50 +24,147 @@ const colors = {
|
|
|
20
24
|
dim: (text) => `\x1b[2m${text}\x1b[0m`,
|
|
21
25
|
};
|
|
22
26
|
|
|
27
|
+
// File categories for smart updates
|
|
28
|
+
const FILE_CATEGORIES = {
|
|
29
|
+
// Core files - always updated (system functionality)
|
|
30
|
+
core: [
|
|
31
|
+
{ src: '.claude/hooks', dest: '.claude/hooks', name: '.claude/hooks/' },
|
|
32
|
+
{ src: '.claude/settings.json', dest: '.claude/settings.json', name: '.claude/settings.json' },
|
|
33
|
+
{ src: 'system', dest: 'system', name: 'system/' },
|
|
34
|
+
{ src: 'CLAUDE.md', dest: 'CLAUDE.md', name: 'CLAUDE.md' },
|
|
35
|
+
],
|
|
36
|
+
// User-customizable files - preserved on update, created on init
|
|
37
|
+
user: [
|
|
38
|
+
{ src: 'instructions/AI_RULES.md', dest: 'instructions/AI_RULES.md', name: 'instructions/AI_RULES.md' },
|
|
39
|
+
// BLUEPRINT.md is special - never overwrite if user has content
|
|
40
|
+
],
|
|
41
|
+
// Commands and skills - updated (but could have user additions)
|
|
42
|
+
commands: [
|
|
43
|
+
{ src: '.claude/commands', dest: '.claude/commands', name: '.claude/commands/' },
|
|
44
|
+
{ src: '.claude/skills', dest: '.claude/skills', name: '.claude/skills/' },
|
|
45
|
+
],
|
|
46
|
+
// Scripts and git hooks
|
|
47
|
+
scripts: [
|
|
48
|
+
{ src: 'scripts', dest: 'scripts', name: 'scripts/' },
|
|
49
|
+
{ src: 'hooks', dest: 'hooks', name: 'hooks/' },
|
|
50
|
+
],
|
|
51
|
+
// Optional config templates - skip if exists
|
|
52
|
+
optional: [
|
|
53
|
+
{ src: '.vscode', dest: '.vscode', name: '.vscode/' },
|
|
54
|
+
{ src: '.prettierrc', dest: '.prettierrc', name: '.prettierrc' },
|
|
55
|
+
{ src: 'eslint.config.example.js', dest: 'eslint.config.js', name: 'eslint.config.js' },
|
|
56
|
+
{ src: 'tsconfig.example.json', dest: 'tsconfig.json', name: 'tsconfig.json' },
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
|
|
23
60
|
function printHelp() {
|
|
24
61
|
console.log(`
|
|
25
|
-
${colors.bold('AutoWorkflow CLI')}
|
|
62
|
+
${colors.bold('AutoWorkflow CLI')} ${colors.dim(`v${PACKAGE_VERSION}`)}
|
|
26
63
|
|
|
27
64
|
Usage: npx autoworkflow <command> [options]
|
|
28
65
|
|
|
29
66
|
Commands:
|
|
30
|
-
init
|
|
31
|
-
init --all
|
|
32
|
-
init --minimal
|
|
33
|
-
|
|
67
|
+
${colors.cyan('init')} Set up AutoWorkflow in current directory
|
|
68
|
+
${colors.cyan('init --all')} Include optional files (.vscode, configs)
|
|
69
|
+
${colors.cyan('init --minimal')} Required files only
|
|
70
|
+
${colors.cyan('init --force')} Full reinstall (backup + overwrite all)
|
|
71
|
+
|
|
72
|
+
${colors.cyan('update')} Smart update - core files only, preserve user files
|
|
73
|
+
${colors.cyan('status')} Show installed version and what would change
|
|
74
|
+
|
|
75
|
+
${colors.cyan('help')} Show this help message
|
|
34
76
|
|
|
35
77
|
Options:
|
|
36
|
-
--no-backup
|
|
78
|
+
--no-backup Overwrite existing files without backup
|
|
79
|
+
--force Force overwrite all files (with backup)
|
|
37
80
|
|
|
38
81
|
Examples:
|
|
39
|
-
npx autoworkflow init
|
|
40
|
-
npx autoworkflow
|
|
82
|
+
npx autoworkflow init # Fresh install
|
|
83
|
+
npx autoworkflow update # Update core files only
|
|
84
|
+
npx autoworkflow status # Check what would change
|
|
85
|
+
npx autoworkflow init --force # Full reinstall
|
|
41
86
|
`);
|
|
42
87
|
}
|
|
43
88
|
|
|
89
|
+
// Version tracking
|
|
90
|
+
function getInstalledVersion(cwd) {
|
|
91
|
+
const versionFile = join(cwd, '.claude', '.autoworkflow', 'version');
|
|
92
|
+
if (existsSync(versionFile)) {
|
|
93
|
+
try {
|
|
94
|
+
return readFileSync(versionFile, 'utf8').trim();
|
|
95
|
+
} catch (e) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function setInstalledVersion(cwd, version) {
|
|
103
|
+
const stateDir = join(cwd, '.claude', '.autoworkflow');
|
|
104
|
+
mkdirSync(stateDir, { recursive: true });
|
|
105
|
+
writeFileSync(join(stateDir, 'version'), version);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Check if BLUEPRINT.md has user content (not just template)
|
|
109
|
+
function hasUserBlueprint(cwd) {
|
|
110
|
+
const blueprintPath = join(cwd, 'instructions', 'BLUEPRINT.md');
|
|
111
|
+
if (!existsSync(blueprintPath)) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const content = readFileSync(blueprintPath, 'utf8');
|
|
117
|
+
// Check if it's just a template or has real content
|
|
118
|
+
const templateMarkers = [
|
|
119
|
+
'<!-- TEMPLATE',
|
|
120
|
+
'<!-- AUTO-GENERATED TEMPLATE',
|
|
121
|
+
'## Features\n\n(To be documented)',
|
|
122
|
+
'## Features\n\n_None documented yet_',
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
// If it has any template markers, it's not user content
|
|
126
|
+
for (const marker of templateMarkers) {
|
|
127
|
+
if (content.includes(marker)) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// If it has substantial content (more than 200 chars after stripping headers), it's user content
|
|
133
|
+
const strippedContent = content.replace(/^#.*$/gm, '').replace(/\s+/g, ' ').trim();
|
|
134
|
+
return strippedContent.length > 200;
|
|
135
|
+
} catch (e) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
44
140
|
function backupFile(dest, name) {
|
|
45
141
|
if (existsSync(dest)) {
|
|
46
142
|
const backupPath = `${dest}.backup`;
|
|
47
|
-
// If backup already exists, add timestamp
|
|
48
143
|
const finalBackupPath = existsSync(backupPath)
|
|
49
144
|
? `${dest}.backup.${Date.now()}`
|
|
50
145
|
: backupPath;
|
|
51
146
|
try {
|
|
52
147
|
renameSync(dest, finalBackupPath);
|
|
53
|
-
console.log(` ${colors.yellow('↪')} ${name} ${colors.dim(`→ backed up
|
|
148
|
+
console.log(` ${colors.yellow('↪')} ${name} ${colors.dim(`→ backed up`)}`);
|
|
54
149
|
return true;
|
|
55
150
|
} catch (err) {
|
|
56
151
|
console.log(` ${colors.red('✗')} Failed to backup ${name}: ${err.message}`);
|
|
57
152
|
return false;
|
|
58
153
|
}
|
|
59
154
|
}
|
|
60
|
-
return true;
|
|
155
|
+
return true;
|
|
61
156
|
}
|
|
62
157
|
|
|
63
158
|
function copyFile(src, dest, name, options = {}) {
|
|
64
|
-
const noBackup =
|
|
159
|
+
const { noBackup = false, skipIfExists = false } = options;
|
|
65
160
|
|
|
66
161
|
if (existsSync(src)) {
|
|
162
|
+
// Skip if exists and skipIfExists is true
|
|
163
|
+
if (skipIfExists && existsSync(dest)) {
|
|
164
|
+
console.log(` ${colors.dim('○')} ${name} ${colors.dim('(exists, skipped)')}`);
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
|
|
67
168
|
// Backup existing file/folder if it exists
|
|
68
169
|
if (!noBackup && existsSync(dest)) {
|
|
69
170
|
const backed = backupFile(dest, name);
|
|
@@ -84,49 +185,19 @@ function copyFile(src, dest, name, options = {}) {
|
|
|
84
185
|
}
|
|
85
186
|
}
|
|
86
187
|
|
|
87
|
-
function
|
|
88
|
-
const cwd = process.cwd();
|
|
89
|
-
const all = options.all || args.includes('--all');
|
|
90
|
-
const minimal = options.minimal || args.includes('--minimal');
|
|
91
|
-
const noBackup = args.includes('--no-backup');
|
|
92
|
-
|
|
93
|
-
console.log(`\n${colors.bold('AutoWorkflow Setup')}\n`);
|
|
94
|
-
console.log(`Installing to: ${colors.cyan(cwd)}`);
|
|
95
|
-
if (!noBackup) {
|
|
96
|
-
console.log(`${colors.dim('Existing files will be backed up with .backup suffix')}\n`);
|
|
97
|
-
} else {
|
|
98
|
-
console.log(`${colors.yellow('⚠')} ${colors.dim('--no-backup: Existing files will be overwritten')}\n`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Required files
|
|
102
|
-
console.log(colors.bold('Required files:'));
|
|
103
|
-
copyFile(join(packageRoot, 'CLAUDE.md'), join(cwd, 'CLAUDE.md'), 'CLAUDE.md');
|
|
104
|
-
copyFile(join(packageRoot, 'system'), join(cwd, 'system'), 'system/');
|
|
105
|
-
copyFile(join(packageRoot, '.claude'), join(cwd, '.claude'), '.claude/');
|
|
106
|
-
|
|
107
|
-
// Copy instructions folder but remove BLUEPRINT.md (hook will auto-generate it)
|
|
108
|
-
copyFile(join(packageRoot, 'instructions'), join(cwd, 'instructions'), 'instructions/');
|
|
109
|
-
const blueprintPath = join(cwd, 'instructions', 'BLUEPRINT.md');
|
|
110
|
-
if (existsSync(blueprintPath)) {
|
|
111
|
-
try {
|
|
112
|
-
unlinkSync(blueprintPath);
|
|
113
|
-
console.log(` ${colors.cyan('ℹ')} BLUEPRINT.md removed (will be auto-generated on first run)`);
|
|
114
|
-
} catch (e) {
|
|
115
|
-
// Ignore
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Make Claude hooks executable
|
|
188
|
+
function makeHooksExecutable(cwd) {
|
|
120
189
|
if (process.platform !== 'win32' && existsSync(join(cwd, '.claude', 'hooks'))) {
|
|
121
190
|
try {
|
|
122
191
|
const hookFiles = [
|
|
123
192
|
'session-check.sh',
|
|
124
193
|
'post-edit.sh',
|
|
194
|
+
'pre-edit.sh',
|
|
125
195
|
'pre-commit-check.sh',
|
|
126
196
|
'pre-tool-router.sh',
|
|
127
197
|
'phase-transition.sh',
|
|
128
198
|
'audit-runner.sh',
|
|
129
|
-
'blueprint-generator.sh'
|
|
199
|
+
'blueprint-generator.sh',
|
|
200
|
+
'post-commit.sh',
|
|
130
201
|
];
|
|
131
202
|
hookFiles.forEach(hook => {
|
|
132
203
|
const hookPath = join(cwd, '.claude', 'hooks', hook);
|
|
@@ -134,36 +205,15 @@ function init(options = {}) {
|
|
|
134
205
|
chmodSync(hookPath, 0o755);
|
|
135
206
|
}
|
|
136
207
|
});
|
|
137
|
-
console.log(` ${colors.green('✓')}
|
|
208
|
+
console.log(` ${colors.green('✓')} Hooks made executable`);
|
|
138
209
|
} catch (e) {
|
|
139
210
|
// Ignore chmod errors
|
|
140
211
|
}
|
|
141
212
|
}
|
|
213
|
+
}
|
|
142
214
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
console.log(`\n${colors.bold('Recommended files:')}`);
|
|
146
|
-
copyFile(join(packageRoot, 'scripts'), join(cwd, 'scripts'), 'scripts/');
|
|
147
|
-
copyFile(join(packageRoot, 'hooks'), join(cwd, 'hooks'), 'hooks/');
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (all) {
|
|
151
|
-
// Optional files
|
|
152
|
-
console.log(`\n${colors.bold('Optional files:')}`);
|
|
153
|
-
copyFile(join(packageRoot, '.vscode'), join(cwd, '.vscode'), '.vscode/');
|
|
154
|
-
copyFile(join(packageRoot, '.prettierrc'), join(cwd, '.prettierrc'), '.prettierrc');
|
|
155
|
-
|
|
156
|
-
// Copy example configs with proper names
|
|
157
|
-
if (existsSync(join(packageRoot, 'eslint.config.example.js'))) {
|
|
158
|
-
copyFile(join(packageRoot, 'eslint.config.example.js'), join(cwd, 'eslint.config.js'), 'eslint.config.js');
|
|
159
|
-
}
|
|
160
|
-
if (existsSync(join(packageRoot, 'tsconfig.example.json'))) {
|
|
161
|
-
copyFile(join(packageRoot, 'tsconfig.example.json'), join(cwd, 'tsconfig.json'), 'tsconfig.json');
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Setup git hooks if hooks were copied
|
|
166
|
-
if (!minimal && existsSync(join(cwd, 'hooks')) && existsSync(join(cwd, '.git'))) {
|
|
215
|
+
function setupGitHooks(cwd) {
|
|
216
|
+
if (existsSync(join(cwd, 'hooks')) && existsSync(join(cwd, '.git'))) {
|
|
167
217
|
console.log(`\n${colors.bold('Git hooks:')}`);
|
|
168
218
|
const hooksDir = join(cwd, '.git', 'hooks');
|
|
169
219
|
mkdirSync(hooksDir, { recursive: true });
|
|
@@ -171,7 +221,6 @@ function init(options = {}) {
|
|
|
171
221
|
const hooksCopied = copyFile(join(cwd, 'hooks', 'pre-commit'), join(hooksDir, 'pre-commit'), 'pre-commit');
|
|
172
222
|
copyFile(join(cwd, 'hooks', 'commit-msg'), join(hooksDir, 'commit-msg'), 'commit-msg');
|
|
173
223
|
|
|
174
|
-
// Make hooks executable (Unix only)
|
|
175
224
|
if (hooksCopied && process.platform !== 'win32') {
|
|
176
225
|
try {
|
|
177
226
|
chmodSync(join(hooksDir, 'pre-commit'), 0o755);
|
|
@@ -181,6 +230,217 @@ function init(options = {}) {
|
|
|
181
230
|
}
|
|
182
231
|
}
|
|
183
232
|
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// STATUS command
|
|
236
|
+
function status() {
|
|
237
|
+
const cwd = process.cwd();
|
|
238
|
+
const installedVersion = getInstalledVersion(cwd);
|
|
239
|
+
|
|
240
|
+
console.log(`\n${colors.bold('AutoWorkflow Status')}\n`);
|
|
241
|
+
console.log(`Package version: ${colors.cyan(PACKAGE_VERSION)}`);
|
|
242
|
+
console.log(`Installed version: ${installedVersion ? colors.cyan(installedVersion) : colors.dim('not installed')}`);
|
|
243
|
+
|
|
244
|
+
if (installedVersion && installedVersion === PACKAGE_VERSION) {
|
|
245
|
+
console.log(`\n${colors.green('✓')} You're on the latest version.\n`);
|
|
246
|
+
} else if (installedVersion) {
|
|
247
|
+
console.log(`\n${colors.yellow('⚠')} Update available: ${installedVersion} → ${PACKAGE_VERSION}`);
|
|
248
|
+
console.log(` Run: ${colors.cyan('npx autoworkflow update')}\n`);
|
|
249
|
+
} else {
|
|
250
|
+
console.log(`\n${colors.yellow('⚠')} AutoWorkflow not initialized in this directory.`);
|
|
251
|
+
console.log(` Run: ${colors.cyan('npx autoworkflow init')}\n`);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Check what exists
|
|
256
|
+
console.log(`${colors.bold('Installed components:')}`);
|
|
257
|
+
const components = [
|
|
258
|
+
{ path: 'CLAUDE.md', name: 'CLAUDE.md' },
|
|
259
|
+
{ path: '.claude/hooks', name: 'Hooks' },
|
|
260
|
+
{ path: '.claude/settings.json', name: 'Settings' },
|
|
261
|
+
{ path: '.claude/commands', name: 'Commands' },
|
|
262
|
+
{ path: '.claude/skills', name: 'Skills' },
|
|
263
|
+
{ path: 'system', name: 'System docs' },
|
|
264
|
+
{ path: 'instructions/AI_RULES.md', name: 'AI Rules' },
|
|
265
|
+
{ path: 'instructions/BLUEPRINT.md', name: 'Blueprint' },
|
|
266
|
+
{ path: 'scripts', name: 'Scripts' },
|
|
267
|
+
{ path: 'hooks', name: 'Git hooks' },
|
|
268
|
+
];
|
|
269
|
+
|
|
270
|
+
components.forEach(({ path, name }) => {
|
|
271
|
+
const exists = existsSync(join(cwd, path));
|
|
272
|
+
const status = exists ? colors.green('✓') : colors.dim('○');
|
|
273
|
+
console.log(` ${status} ${name}`);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Check for user customizations
|
|
277
|
+
const hasBlueprint = hasUserBlueprint(cwd);
|
|
278
|
+
if (hasBlueprint) {
|
|
279
|
+
console.log(`\n${colors.cyan('ℹ')} User BLUEPRINT.md detected - will be preserved on update.`);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
console.log('');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// UPDATE command - smart update, preserve user files
|
|
286
|
+
function update() {
|
|
287
|
+
const cwd = process.cwd();
|
|
288
|
+
const installedVersion = getInstalledVersion(cwd);
|
|
289
|
+
const noBackup = args.includes('--no-backup');
|
|
290
|
+
|
|
291
|
+
console.log(`\n${colors.bold('AutoWorkflow Update')}\n`);
|
|
292
|
+
|
|
293
|
+
if (!installedVersion) {
|
|
294
|
+
console.log(`${colors.yellow('⚠')} AutoWorkflow not initialized. Run ${colors.cyan('npx autoworkflow init')} instead.\n`);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
console.log(`Updating: ${colors.dim(installedVersion)} → ${colors.cyan(PACKAGE_VERSION)}`);
|
|
299
|
+
console.log(`${colors.dim('User files (BLUEPRINT.md, AI_RULES.md) will be preserved')}\n`);
|
|
300
|
+
|
|
301
|
+
// Update core files
|
|
302
|
+
console.log(colors.bold('Updating core files:'));
|
|
303
|
+
FILE_CATEGORIES.core.forEach(({ src, dest, name }) => {
|
|
304
|
+
copyFile(join(packageRoot, src), join(cwd, dest), name, { noBackup });
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// Update commands and skills
|
|
308
|
+
console.log(`\n${colors.bold('Updating commands/skills:')}`);
|
|
309
|
+
FILE_CATEGORIES.commands.forEach(({ src, dest, name }) => {
|
|
310
|
+
copyFile(join(packageRoot, src), join(cwd, dest), name, { noBackup });
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// Make hooks executable
|
|
314
|
+
makeHooksExecutable(cwd);
|
|
315
|
+
|
|
316
|
+
// Update version
|
|
317
|
+
setInstalledVersion(cwd, PACKAGE_VERSION);
|
|
318
|
+
|
|
319
|
+
console.log(`\n${colors.green('✓')} ${colors.bold('Updated to v' + PACKAGE_VERSION)}`);
|
|
320
|
+
console.log(`\n${colors.dim('User files preserved. Run')} ${colors.cyan('npx autoworkflow init --force')} ${colors.dim('for full reinstall.')}\n`);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// INIT command
|
|
324
|
+
function init(options = {}) {
|
|
325
|
+
const cwd = process.cwd();
|
|
326
|
+
const all = options.all || args.includes('--all');
|
|
327
|
+
const minimal = options.minimal || args.includes('--minimal');
|
|
328
|
+
const force = options.force || args.includes('--force');
|
|
329
|
+
const noBackup = args.includes('--no-backup');
|
|
330
|
+
|
|
331
|
+
const installedVersion = getInstalledVersion(cwd);
|
|
332
|
+
|
|
333
|
+
// IMPORTANT: Check for user BLUEPRINT BEFORE any copying happens
|
|
334
|
+
const userHasBlueprint = hasUserBlueprint(cwd);
|
|
335
|
+
const blueprintPath = join(cwd, 'instructions', 'BLUEPRINT.md');
|
|
336
|
+
let savedBlueprintContent = null;
|
|
337
|
+
|
|
338
|
+
// Save user's BLUEPRINT.md content before any operations
|
|
339
|
+
if (userHasBlueprint) {
|
|
340
|
+
try {
|
|
341
|
+
savedBlueprintContent = readFileSync(blueprintPath, 'utf8');
|
|
342
|
+
} catch (e) {
|
|
343
|
+
// Ignore read errors
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
console.log(`\n${colors.bold('AutoWorkflow Setup')} ${colors.dim(`v${PACKAGE_VERSION}`)}\n`);
|
|
348
|
+
console.log(`Installing to: ${colors.cyan(cwd)}`);
|
|
349
|
+
|
|
350
|
+
if (installedVersion && !force) {
|
|
351
|
+
console.log(`\n${colors.yellow('⚠')} AutoWorkflow v${installedVersion} already installed.`);
|
|
352
|
+
console.log(` Use ${colors.cyan('npx autoworkflow update')} to update core files`);
|
|
353
|
+
console.log(` Use ${colors.cyan('npx autoworkflow init --force')} to reinstall everything\n`);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (!noBackup) {
|
|
358
|
+
console.log(`${colors.dim('Existing files will be backed up with .backup suffix')}\n`);
|
|
359
|
+
} else {
|
|
360
|
+
console.log(`${colors.yellow('⚠')} ${colors.dim('--no-backup: Existing files will be overwritten')}\n`);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Required files
|
|
364
|
+
console.log(colors.bold('Core files:'));
|
|
365
|
+
copyFile(join(packageRoot, 'CLAUDE.md'), join(cwd, 'CLAUDE.md'), 'CLAUDE.md', { noBackup });
|
|
366
|
+
copyFile(join(packageRoot, 'system'), join(cwd, 'system'), 'system/', { noBackup });
|
|
367
|
+
copyFile(join(packageRoot, '.claude'), join(cwd, '.claude'), '.claude/', { noBackup });
|
|
368
|
+
|
|
369
|
+
// Handle instructions folder - preserve user BLUEPRINT.md
|
|
370
|
+
console.log(`\n${colors.bold('Instructions:')}`);
|
|
371
|
+
const instructionsDir = join(cwd, 'instructions');
|
|
372
|
+
mkdirSync(instructionsDir, { recursive: true });
|
|
373
|
+
|
|
374
|
+
// Copy AI_RULES.md (skip if exists and not forcing)
|
|
375
|
+
const aiRulesPath = join(cwd, 'instructions', 'AI_RULES.md');
|
|
376
|
+
if (!existsSync(aiRulesPath) || force) {
|
|
377
|
+
copyFile(
|
|
378
|
+
join(packageRoot, 'instructions', 'AI_RULES.md'),
|
|
379
|
+
aiRulesPath,
|
|
380
|
+
'instructions/AI_RULES.md',
|
|
381
|
+
{ noBackup }
|
|
382
|
+
);
|
|
383
|
+
} else {
|
|
384
|
+
console.log(` ${colors.dim('○')} instructions/AI_RULES.md ${colors.dim('(exists, preserved)')}`);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// BLUEPRINT.md - Restore user content if we saved it
|
|
388
|
+
if (savedBlueprintContent) {
|
|
389
|
+
// Restore the user's BLUEPRINT.md content
|
|
390
|
+
try {
|
|
391
|
+
writeFileSync(blueprintPath, savedBlueprintContent);
|
|
392
|
+
console.log(` ${colors.cyan('★')} instructions/BLUEPRINT.md ${colors.dim('(user content preserved)')}`);
|
|
393
|
+
} catch (e) {
|
|
394
|
+
console.log(` ${colors.red('✗')} Failed to restore BLUEPRINT.md: ${e.message}`);
|
|
395
|
+
}
|
|
396
|
+
} else if (existsSync(blueprintPath)) {
|
|
397
|
+
// Check if current file is a template (package just copied it)
|
|
398
|
+
const currentContent = readFileSync(blueprintPath, 'utf8');
|
|
399
|
+
const isTemplate = currentContent.includes('<!-- TEMPLATE') ||
|
|
400
|
+
currentContent.includes('(To be documented)') ||
|
|
401
|
+
currentContent.includes('_None documented yet_') ||
|
|
402
|
+
currentContent.replace(/^#.*$/gm, '').replace(/\s+/g, ' ').trim().length < 200;
|
|
403
|
+
|
|
404
|
+
if (isTemplate) {
|
|
405
|
+
// Remove template so hook can regenerate project-specific one
|
|
406
|
+
try {
|
|
407
|
+
unlinkSync(blueprintPath);
|
|
408
|
+
console.log(` ${colors.cyan('ℹ')} BLUEPRINT.md template removed (will be auto-generated)`);
|
|
409
|
+
} catch (e) {
|
|
410
|
+
// Ignore
|
|
411
|
+
}
|
|
412
|
+
} else {
|
|
413
|
+
console.log(` ${colors.cyan('★')} instructions/BLUEPRINT.md ${colors.dim('(content preserved)')}`);
|
|
414
|
+
}
|
|
415
|
+
} else {
|
|
416
|
+
console.log(` ${colors.cyan('ℹ')} BLUEPRINT.md will be auto-generated on first run`);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Make Claude hooks executable
|
|
420
|
+
makeHooksExecutable(cwd);
|
|
421
|
+
|
|
422
|
+
if (!minimal) {
|
|
423
|
+
// Recommended files
|
|
424
|
+
console.log(`\n${colors.bold('Scripts:')}`);
|
|
425
|
+
copyFile(join(packageRoot, 'scripts'), join(cwd, 'scripts'), 'scripts/', { noBackup });
|
|
426
|
+
copyFile(join(packageRoot, 'hooks'), join(cwd, 'hooks'), 'hooks/', { noBackup });
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (all) {
|
|
430
|
+
// Optional files - skip if exists
|
|
431
|
+
console.log(`\n${colors.bold('Optional files:')}`);
|
|
432
|
+
FILE_CATEGORIES.optional.forEach(({ src, dest, name }) => {
|
|
433
|
+
copyFile(join(packageRoot, src), join(cwd, dest), name, { noBackup, skipIfExists: !force });
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Setup git hooks
|
|
438
|
+
if (!minimal) {
|
|
439
|
+
setupGitHooks(cwd);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Save installed version
|
|
443
|
+
setInstalledVersion(cwd, PACKAGE_VERSION);
|
|
184
444
|
|
|
185
445
|
console.log(`\n${colors.green('✓')} ${colors.bold('AutoWorkflow initialized!')}\n`);
|
|
186
446
|
|
|
@@ -198,12 +458,22 @@ switch (command) {
|
|
|
198
458
|
case 'init':
|
|
199
459
|
init();
|
|
200
460
|
break;
|
|
461
|
+
case 'update':
|
|
462
|
+
update();
|
|
463
|
+
break;
|
|
464
|
+
case 'status':
|
|
465
|
+
status();
|
|
466
|
+
break;
|
|
201
467
|
case 'help':
|
|
202
468
|
case '--help':
|
|
203
469
|
case '-h':
|
|
204
470
|
case undefined:
|
|
205
471
|
printHelp();
|
|
206
472
|
break;
|
|
473
|
+
case '--version':
|
|
474
|
+
case '-v':
|
|
475
|
+
console.log(PACKAGE_VERSION);
|
|
476
|
+
break;
|
|
207
477
|
default:
|
|
208
478
|
console.log(`${colors.red('Unknown command:')} ${command}`);
|
|
209
479
|
printHelp();
|