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.
@@ -11,7 +11,7 @@
11
11
  "hooks": [
12
12
  {
13
13
  "type": "command",
14
- "command": "./.claude/hooks/session-check.sh",
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": "./.claude/hooks/pre-edit.sh",
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": "./.claude/hooks/pre-tool-router.sh \"$TOOL_INPUT\"",
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": "./.claude/hooks/post-edit.sh",
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": "./.claude/hooks/post-bash-router.sh \"$TOOL_INPUT\"",
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.7.0** - Fail-closed enforcement: BLOCKS edits by default unless all gates pass.
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
- Options:
18
- - `npx autoworkflow init` - Required + recommended files
19
- - `npx autoworkflow init --all` - Include .vscode and config templates
20
- - `npx autoworkflow init --minimal` - Required files only
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 Set up AutoWorkflow in current directory
31
- init --all Include optional files (.vscode, configs)
32
- init --minimal Required files only
33
- help Show this help message
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 Overwrite existing files without 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 init --all
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 to ${finalBackupPath.split('/').pop()}`)}`);
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; // No backup needed
155
+ return true;
61
156
  }
62
157
 
63
158
  function copyFile(src, dest, name, options = {}) {
64
- const noBackup = args.includes('--no-backup');
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 init(options = {}) {
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('✓')} Claude hooks made executable`);
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
- if (!minimal) {
144
- // Recommended files
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autoworkflow",
3
- "version": "3.7.0",
3
+ "version": "3.8.1",
4
4
  "description": "Automated workflow enforcement for Claude Code via hooks and system prompts",
5
5
  "type": "module",
6
6
  "bin": {