@mthanhlm/autodev 0.4.1 → 0.4.3

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "autodev",
3
3
  "description": "A lean Claude Code workflow system with a single entrypoint, task-based phase execution, and read-only git.",
4
- "version": "0.4.1",
4
+ "version": "0.4.3",
5
5
  "author": {
6
6
  "name": "mthanhlm"
7
7
  },
package/README.md CHANGED
@@ -8,9 +8,11 @@
8
8
  - Keeps project state in `.autodev/`
9
9
  - Uses `/autodev` as the main command
10
10
  - Organizes work as `project -> track -> phase -> tasks`
11
+ - Starts new projects and new tracks with a short discovery pass so requirements are based on the user's actual goal
11
12
  - Resolves `.autodev/` state from the repo root even when Claude is started in a nested subdirectory
12
- - Maps brownfield repos with foreground delegated agents when the environment supports them
13
+ - Maps brownfield repos in the main session, with optional specialized helpers when the environment supports them
13
14
  - Runs a multi-lens review pass, using foreground review agents when the environment supports them
15
+ - Can auto-format edited code after Claude writes files when a local formatter is available
14
16
  - Ships manual commands when you want direct control:
15
17
  - `/autodev-help`
16
18
  - `/autodev-new-project`
@@ -105,6 +107,7 @@ project -> track -> phase -> tasks
105
107
  ### Execution Model
106
108
 
107
109
  - `/autodev` remains the single entrypoint
110
+ - Project and track setup should clarify what the user wants to build, for whom, and what done looks like before writing state files
108
111
  - A phase is planned into one phase overview plus multiple task files
109
112
  - `/autodev` stops after planning so the user can review the phase before any execution starts
110
113
  - The phase keeps one user-facing orchestration session
@@ -116,6 +119,7 @@ project -> track -> phase -> tasks
116
119
  - No automatic parallel execution
117
120
  - If specialized agents are unavailable in the current Claude Code environment, the workflow falls back cleanly to current-session execution instead of stopping on platform wording
118
121
  - If you want Claude Code itself to hard-disable background task functionality, install with `--disable-background-tasks`, which writes `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1` into Claude Code `settings.json`
122
+ - The post-edit formatter hook uses only locally available tools such as `prettier`, `ruff`, `black`, `gofmt`, `rustfmt`, or `shfmt`, and skips `.autodev/` files
119
123
 
120
124
  ## Git Policy
121
125
 
@@ -16,6 +16,7 @@ const DEFAULT_CONFIG = {
16
16
  mode: 'read-only'
17
17
  },
18
18
  hooks: {
19
+ auto_format: true,
19
20
  context_warnings: true,
20
21
  read_guard: true,
21
22
  workflow_guard: true,
@@ -10,6 +10,7 @@
10
10
  "mode": "read-only"
11
11
  },
12
12
  "hooks": {
13
+ "auto_format": true,
13
14
  "context_warnings": true,
14
15
  "read_guard": true,
15
16
  "workflow_guard": true,
@@ -3,8 +3,15 @@
3
3
  Type: [feature|bugfix|refactor|research|polish]
4
4
  Status: active
5
5
 
6
+ ## Why This Track
7
+ [Why this work matters right now.]
8
+
6
9
  ## Outcome
7
- [What this track is trying to achieve.]
10
+ [What changes for the user or operator when this track is done.]
11
+
12
+ ## Success Signals
13
+ - [Observable user or operator outcome]
14
+ - [Verification or quality bar]
8
15
 
9
16
  ## Scope Boundaries
10
17
  - [What is in scope]
@@ -37,13 +37,22 @@ node "$AUTODEV_ROOT/autodev/bin/autodev-tools.cjs" status
37
37
 
38
38
  6. If the route is `track_select` or `track_setup`, handle it directly:
39
39
  - If tracks exist, ask whether to continue one of them or create a new track.
40
- - If no tracks exist, ask only for the current initiative name, desired outcome, and likely phase types.
40
+ - When creating or repairing a track, do a short track-discovery pass before writing files.
41
+ - Ask for:
42
+ - the initiative name
43
+ - what the user wants to ship or change in this track
44
+ - what done looks like
45
+ - scope boundaries and explicit non-goals
46
+ - dependencies or constraints only if they affect planning
47
+ - If the request is vague, summarize the understanding in a few bullets and resolve ambiguity before writing.
48
+ - If no tracks exist, do not jump straight to phase types before the track goal is clear.
41
49
  - Create or repair:
42
50
  - `.autodev/ACTIVE_TRACK`
43
51
  - `.autodev/tracks/<slug>/TRACK.md`
44
52
  - `.autodev/tracks/<slug>/REQUIREMENTS.md`
45
53
  - `.autodev/tracks/<slug>/ROADMAP.md`
46
54
  - `.autodev/tracks/<slug>/STATE.md`
55
+ - Fill the track and requirements docs from the clarified intent, not generic placeholders.
47
56
  - Use a lowercase hyphenated slug.
48
57
  - Use roadmap headings in this format:
49
58
 
@@ -3,8 +3,11 @@ Map a brownfield repository quickly and produce a usable codebase brief without
3
3
  </purpose>
4
4
 
5
5
  <rules>
6
- - Prefer a small set of foreground codebase agents, run one at a time, when agent delegation is available.
6
+ - The main `/autodev` session owns this workflow and the final synthesis.
7
+ - Specialized codebase agents are optional implementation helpers, not a separate user-facing mode.
8
+ - Prefer a small set of foreground codebase agents, run one at a time, only when agent delegation is clearly available.
7
9
  - Give each agent a disjoint write target.
10
+ - If agent delegation is unavailable or unclear, continue inline without surfacing platform confusion.
8
11
  - Use read-only investigation only. No git writes.
9
12
  - Focus on information that will improve planning and execution for the active track.
10
13
  </rules>
@@ -26,34 +29,36 @@ node "$AUTODEV_ROOT/autodev/bin/autodev-tools.cjs" init explore-codebase
26
29
  - the active track docs if they exist
27
30
  - representative repo files needed to orient the exploration
28
31
 
29
- 4. If agent delegation is available, explicitly call the `Agent` tool for these subagents, one at a time in the foreground, with owned outputs:
32
+ 4. Do a small amount of direct repo reading first so the current session understands the workspace before deciding whether helper agents are useful.
33
+
34
+ 5. If agent delegation is clearly available, explicitly call the `Agent` tool for these subagents, one at a time in the foreground, with owned outputs:
30
35
  - `autodev-codebase-structure` for standalone installs, or `autodev:autodev-codebase-structure` in direct plugin runs, owns `.autodev/codebase/structure.md`
31
36
  - `autodev-codebase-domain` for standalone installs, or `autodev:autodev-codebase-domain` in direct plugin runs, owns `.autodev/codebase/domain.md`
32
37
  - `autodev-codebase-runtime` for standalone installs, or `autodev:autodev-codebase-runtime` in direct plugin runs, owns `.autodev/codebase/runtime.md`
33
38
  - `autodev-codebase-quality` for standalone installs, or `autodev:autodev-codebase-quality` in direct plugin runs, owns `.autodev/codebase/quality.md`
34
39
 
35
- 5. Tell each agent:
40
+ 6. Tell each agent:
36
41
  - it is not alone in the codebase
37
42
  - it must not overwrite another agent's file
38
43
  - it should stay read-only except for its owned output file
39
44
  - it should prefer concise, decision-useful findings over exhaustive listing
40
45
 
41
- 6. If agent delegation is unavailable, do not stop on the raw platform message.
46
+ 7. If agent delegation is unavailable, do not stop on the raw platform message.
42
47
  Treat messages like `specialized agents aren't available` as an environment limitation, tell the user exploration will continue in the current session, and write the four owned output files directly.
43
48
 
44
- 7. After the four agent outputs are written, or after the current-session fallback writes them, review them and synthesize `.autodev/codebase/summary.md` with:
49
+ 8. After the four agent outputs are written, or after the current-session fallback writes them, review them and synthesize `.autodev/codebase/summary.md` with:
45
50
  - architectural overview
46
51
  - major constraints
47
52
  - current track implications
48
53
  - hotspots and likely safe change points
49
54
  - the next planning risks to watch
50
55
 
51
- 8. Update `.autodev/STATE.md` and the active track `STATE.md` so they reflect:
56
+ 9. Update `.autodev/STATE.md` and the active track `STATE.md` so they reflect:
52
57
  - `Current Step: planning`
53
58
  - `Next Command: /autodev`
54
59
  - refreshed ISO timestamp
55
60
 
56
- 9. End with:
61
+ 10. End with:
57
62
  - the biggest structural insight
58
63
  - the biggest risk
59
64
  - `/autodev` as the next command
@@ -2,6 +2,7 @@
2
2
 
3
3
  Lean Claude Code workflow. No automatic commits. No branches. No worktrees. Git is read-only.
4
4
  It resolves `.autodev/` state from the repo root even when you start Claude in a nested subdirectory.
5
+ It can auto-format edited code after writes when the repo already has a local formatter available.
5
6
 
6
7
  ## Main Entry
7
8
 
@@ -5,7 +5,8 @@ Initialize `.autodev/` with useful project-level context, then create the first
5
5
  <rules>
6
6
  - Do not run `git init`, `git add`, `git commit`, `git checkout`, `git switch`, `git merge`, `git rebase`, `git worktree`, `git push`, `git pull`, `git stash`, `git reset`, `git fetch`, or `git clone`.
7
7
  - Git is read-only only.
8
- - Use concise questioning. Ask only for details that materially change requirements or roadmap.
8
+ - Use concise questioning, but do a short discovery pass before writing project state.
9
+ - Ask only for details that materially change requirements or roadmap.
9
10
  - Treat the repository as a `project`, then put the current initiative in a `track`.
10
11
  </rules>
11
12
 
@@ -19,11 +20,15 @@ node "$AUTODEV_ROOT/autodev/bin/autodev-tools.cjs" init new-project
19
20
 
20
21
  2. If `.autodev/PROJECT.md` already exists, stop and tell the user to use `/autodev`.
21
22
 
22
- 3. If the current prompt does not provide enough information, ask only for:
23
- - the product or repository purpose
24
- - whether this is greenfield or brownfield if ambiguous
25
- - the first track name and desired outcome
26
- - hard constraints only if they materially affect planning
23
+ 3. Do a short discovery pass before writing project or track files.
24
+ - If the current prompt is thin, ask for:
25
+ - what the user wants to build or change right now
26
+ - who it is for or what pain it solves
27
+ - what done looks like for the first track
28
+ - scope boundaries, constraints, and explicit non-goals that materially affect planning
29
+ - whether this is greenfield or brownfield if ambiguous
30
+ - Prefer one compact batch of questions over a long drip of follow-ups.
31
+ - Reflect the understanding back in 2-5 bullets before writing files when the original request was vague.
27
32
 
28
33
  4. Read the templates from the execution context.
29
34
 
@@ -32,11 +37,13 @@ node "$AUTODEV_ROOT/autodev/bin/autodev-tools.cjs" init new-project
32
37
  - `workflow.research: false`
33
38
  - `workflow.review_after_execute: true`
34
39
  - `git.mode: "read-only"`
40
+ - `hooks.auto_format: true`
35
41
 
36
42
  6. Write `.autodev/PROJECT.md` with:
37
43
  - one-line summary
38
44
  - project type
39
45
  - repo or product problem
46
+ - current user goal or job to be done
40
47
  - users or operators
41
48
  - success criteria
42
49
  - constraints
@@ -57,12 +64,14 @@ node "$AUTODEV_ROOT/autodev/bin/autodev-tools.cjs" init new-project
57
64
  9. Create `.autodev/tracks/<slug>/TRACK.md` with:
58
65
  - track name
59
66
  - type or workstream shape
60
- - desired outcome
61
- - scope boundaries
67
+ - why this track exists now
68
+ - desired outcome and what done looks like
69
+ - scope boundaries and explicit non-goals
62
70
  - known dependencies
63
71
  - brownfield notes if this is an existing repo
64
72
 
65
73
  10. Write `.autodev/tracks/<slug>/REQUIREMENTS.md` with clear must-have requirements and acceptance bullets for the track only.
74
+ Tie the acceptance bullets to the discovered outcome instead of placeholder implementation tasks.
66
75
 
67
76
  11. Write `.autodev/tracks/<slug>/ROADMAP.md` with 2-8 phases using this exact heading format:
68
77
 
@@ -88,5 +97,5 @@ Each phase must include:
88
97
 
89
98
  13. If the project is brownfield, do not fake a codebase map. Continue into codebase exploration inside `/autodev` by default. Mention `/autodev-explore-codebase` only as a manual shortcut. Otherwise, the next step is planning phase 1 through `/autodev`.
90
99
 
91
- 14. End with a short summary and direct the user to `/autodev`. Mention the manual shortcut only if useful.
100
+ 14. End with a short summary of what the user wants to build or change and direct the user to `/autodev`. Mention the manual shortcut only if useful.
92
101
  </process>
package/bin/install.js CHANGED
@@ -14,6 +14,7 @@ const MANAGED_PREFIX = 'autodev-';
14
14
  const ROOT_COMMAND = 'autodev';
15
15
  const HOOK_FILES = [
16
16
  'hooks.json',
17
+ 'autodev-auto-format.js',
17
18
  'autodev-paths.js',
18
19
  'autodev-context-monitor.js',
19
20
  'autodev-git-guard.js',
@@ -375,6 +376,9 @@ function configureSettings(targetDir, isGlobal, options = {}) {
375
376
  const contextMonitorCommand = isGlobal
376
377
  ? buildGlobalCommand(targetDir, 'autodev-context-monitor.js')
377
378
  : buildLocalCommand('autodev-context-monitor.js');
379
+ const autoFormatCommand = isGlobal
380
+ ? buildGlobalCommand(targetDir, 'autodev-auto-format.js')
381
+ : buildLocalCommand('autodev-auto-format.js');
378
382
  const promptGuardCommand = isGlobal
379
383
  ? buildGlobalCommand(targetDir, 'autodev-prompt-guard.js')
380
384
  : buildLocalCommand('autodev-prompt-guard.js');
@@ -402,6 +406,7 @@ function configureSettings(targetDir, isGlobal, options = {}) {
402
406
  ensureHook(settings, preToolEvent, 'Write|Edit', readGuardCommand, 5);
403
407
  ensureHook(settings, preToolEvent, 'Write|Edit', workflowGuardCommand, 5);
404
408
  ensureHook(settings, preToolEvent, 'Bash', gitGuardCommand, 5);
409
+ ensureHook(settings, postToolEvent, 'Edit|Write|MultiEdit', autoFormatCommand, 10);
405
410
  ensureHook(settings, postToolEvent, 'Bash|Edit|Write|MultiEdit|Agent|Task', contextMonitorCommand, 10);
406
411
  ensureHook(settings, postToolEvent, 'Write|Edit', phaseBoundaryCommand, 5);
407
412
 
@@ -0,0 +1,210 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { spawnSync } = require('child_process');
6
+ const { findWorkspaceRoot, readProjectConfig } = require('./autodev-paths.js');
7
+
8
+ const PRETTIER_EXTENSIONS = new Set([
9
+ '.js', '.jsx', '.cjs', '.mjs',
10
+ '.ts', '.tsx',
11
+ '.json',
12
+ '.css', '.scss', '.less',
13
+ '.html',
14
+ '.yml', '.yaml',
15
+ '.vue', '.svelte'
16
+ ]);
17
+
18
+ const PYTHON_EXTENSIONS = new Set(['.py']);
19
+ const SHELL_EXTENSIONS = new Set(['.sh', '.bash', '.zsh']);
20
+
21
+ function fileExists(filePath) {
22
+ try {
23
+ return fs.existsSync(filePath);
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
28
+
29
+ function isFile(filePath) {
30
+ try {
31
+ return fs.statSync(filePath).isFile();
32
+ } catch {
33
+ return false;
34
+ }
35
+ }
36
+
37
+ function isInside(targetPath, rootPath) {
38
+ const relative = path.relative(rootPath, targetPath);
39
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
40
+ }
41
+
42
+ function resolveHookFilePath(data, cwd) {
43
+ const raw = data.tool_input?.file_path || data.tool_input?.path || '';
44
+ if (!raw) {
45
+ return null;
46
+ }
47
+ return path.isAbsolute(raw) ? raw : path.resolve(cwd, raw);
48
+ }
49
+
50
+ function resolveExecutable(workspaceRoot, candidates) {
51
+ for (const candidate of candidates) {
52
+ if (Array.isArray(candidate)) {
53
+ const absolute = path.join(workspaceRoot, ...candidate);
54
+ if (isFile(absolute)) {
55
+ return absolute;
56
+ }
57
+
58
+ if (process.platform === 'win32' && isFile(`${absolute}.cmd`)) {
59
+ return `${absolute}.cmd`;
60
+ }
61
+
62
+ if (process.platform === 'win32' && isFile(`${absolute}.exe`)) {
63
+ return `${absolute}.exe`;
64
+ }
65
+
66
+ if (isFile(`${absolute}.cmd`)) {
67
+ return `${absolute}.cmd`;
68
+ }
69
+
70
+ if (isFile(`${absolute}.exe`)) {
71
+ return `${absolute}.exe`;
72
+ }
73
+
74
+ if (isFile(`${absolute}.js`)) {
75
+ return `${absolute}.js`;
76
+ }
77
+
78
+ if (fileExists(absolute)) {
79
+ return absolute;
80
+ }
81
+ continue;
82
+ }
83
+
84
+ const result = spawnSync('sh', ['-lc', `command -v "${candidate}"`], {
85
+ cwd: workspaceRoot,
86
+ encoding: 'utf8'
87
+ });
88
+ if (result.status === 0 && result.stdout.trim()) {
89
+ return result.stdout.trim();
90
+ }
91
+ }
92
+
93
+ return null;
94
+ }
95
+
96
+ function resolveFile(workspaceRoot, candidates) {
97
+ for (const candidate of candidates) {
98
+ const absolute = path.join(workspaceRoot, ...candidate);
99
+ if (isFile(absolute)) {
100
+ return absolute;
101
+ }
102
+ }
103
+
104
+ return null;
105
+ }
106
+
107
+ function formatterCommand(filePath, workspaceRoot) {
108
+ const ext = path.extname(filePath).toLowerCase();
109
+
110
+ if (PRETTIER_EXTENSIONS.has(ext)) {
111
+ const prettierScript = resolveFile(workspaceRoot, [
112
+ ['node_modules', 'prettier', 'bin', 'prettier.cjs'],
113
+ ['node_modules', 'prettier', 'bin', 'prettier.js']
114
+ ]);
115
+ if (prettierScript) {
116
+ return [process.execPath, [prettierScript, '--write', filePath]];
117
+ }
118
+
119
+ const prettier = resolveExecutable(workspaceRoot, [
120
+ ['node_modules', '.bin', 'prettier'],
121
+ 'prettier'
122
+ ]);
123
+ return prettier ? [prettier, ['--write', filePath]] : null;
124
+ }
125
+
126
+ if (PYTHON_EXTENSIONS.has(ext)) {
127
+ const ruff = resolveExecutable(workspaceRoot, [
128
+ ['.venv', 'bin', 'ruff'],
129
+ ['.venv', 'Scripts', 'ruff.exe'],
130
+ 'ruff'
131
+ ]);
132
+ if (ruff) {
133
+ return [ruff, ['format', filePath]];
134
+ }
135
+
136
+ const black = resolveExecutable(workspaceRoot, [
137
+ ['.venv', 'bin', 'black'],
138
+ ['.venv', 'Scripts', 'black.exe'],
139
+ 'black'
140
+ ]);
141
+ return black ? [black, [filePath]] : null;
142
+ }
143
+
144
+ if (ext === '.go') {
145
+ const gofmt = resolveExecutable(workspaceRoot, ['gofmt']);
146
+ return gofmt ? [gofmt, ['-w', filePath]] : null;
147
+ }
148
+
149
+ if (ext === '.rs') {
150
+ const rustfmt = resolveExecutable(workspaceRoot, ['rustfmt']);
151
+ return rustfmt ? [rustfmt, [filePath]] : null;
152
+ }
153
+
154
+ if (SHELL_EXTENSIONS.has(ext)) {
155
+ const shfmt = resolveExecutable(workspaceRoot, ['shfmt']);
156
+ return shfmt ? [shfmt, ['-w', filePath]] : null;
157
+ }
158
+
159
+ return null;
160
+ }
161
+
162
+ try {
163
+ const input = fs.readFileSync(0, 'utf8');
164
+ const data = JSON.parse(input);
165
+ if (!['Edit', 'Write', 'MultiEdit'].includes(data.tool_name)) {
166
+ process.exit(0);
167
+ }
168
+
169
+ const cwd = data.cwd || process.cwd();
170
+ const workspaceRoot = findWorkspaceRoot(cwd);
171
+ const config = readProjectConfig(cwd);
172
+ if (config && config.hooks?.auto_format === false) {
173
+ process.exit(0);
174
+ }
175
+
176
+ const filePath = resolveHookFilePath(data, cwd);
177
+ if (!filePath || !isFile(filePath)) {
178
+ process.exit(0);
179
+ }
180
+
181
+ const autodevRoot = path.join(workspaceRoot, '.autodev');
182
+ if (fileExists(autodevRoot) && isInside(filePath, autodevRoot)) {
183
+ process.exit(0);
184
+ }
185
+
186
+ const formatter = formatterCommand(filePath, workspaceRoot);
187
+ if (!formatter) {
188
+ process.exit(0);
189
+ }
190
+
191
+ const [command, args] = formatter;
192
+ const result = spawnSync(command, args, {
193
+ cwd: workspaceRoot,
194
+ encoding: 'utf8'
195
+ });
196
+
197
+ if (result.status !== 0) {
198
+ const stderr = (result.stderr || '').trim();
199
+ process.stdout.write(JSON.stringify({
200
+ hookSpecificOutput: {
201
+ hookEventName: 'PostToolUse',
202
+ additionalContext: stderr
203
+ ? `AUTO-FORMAT WARNING: formatting failed for ${path.basename(filePath)}: ${stderr}`
204
+ : `AUTO-FORMAT WARNING: formatting failed for ${path.basename(filePath)}.`
205
+ }
206
+ }));
207
+ }
208
+ } catch {
209
+ process.exit(0);
210
+ }
package/hooks/hooks.json CHANGED
@@ -54,6 +54,16 @@
54
54
  }
55
55
  ],
56
56
  "PostToolUse": [
57
+ {
58
+ "matcher": "Edit|Write|MultiEdit",
59
+ "hooks": [
60
+ {
61
+ "type": "command",
62
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/autodev-auto-format.js\"",
63
+ "timeout": 10
64
+ }
65
+ ]
66
+ },
57
67
  {
58
68
  "matcher": "Bash|Edit|Write|MultiEdit|Agent|Task",
59
69
  "hooks": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mthanhlm/autodev",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "A lean Claude Code workflow system with a single entrypoint, task-based phase execution, and read-only git.",
5
5
  "bin": {
6
6
  "autodev": "bin/install.js"