@mthanhlm/autodev 0.3.6 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/PUBLISH.md +1 -1
- package/README.md +18 -6
- package/autodev/bin/autodev-tools.cjs +219 -12
- package/autodev/templates/config.json +1 -5
- package/autodev/workflows/autodev.md +1 -0
- package/autodev/workflows/execute-phase.md +18 -13
- package/autodev/workflows/explore-codebase.md +2 -2
- package/autodev/workflows/help.md +12 -3
- package/autodev/workflows/new-project.md +0 -2
- package/autodev/workflows/review-phase.md +6 -4
- package/autodev/workflows/review-plan.md +3 -2
- package/autodev/workflows/review-task.md +70 -0
- package/autodev/workflows/verify-work.md +2 -2
- package/bin/install.js +55 -2
- package/commands/autodev/execute-phase.md +2 -2
- package/commands/autodev/explore-codebase.md +2 -2
- package/commands/autodev/review-phase.md +1 -1
- package/commands/autodev/review-task.md +25 -0
- package/hooks/autodev-context-monitor.js +1 -8
- package/hooks/autodev-git-guard.js +1 -9
- package/hooks/autodev-paths.js +60 -0
- package/hooks/autodev-phase-boundary.sh +34 -5
- package/hooks/autodev-read-guard.js +1 -8
- package/hooks/autodev-session-state.sh +35 -6
- package/hooks/autodev-statusline.js +3 -10
- package/hooks/autodev-workflow-guard.js +1 -9
- package/package.json +1 -1
package/PUBLISH.md
CHANGED
package/README.md
CHANGED
|
@@ -8,14 +8,16 @@
|
|
|
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
|
-
-
|
|
12
|
-
-
|
|
11
|
+
- 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
|
+
- Runs a multi-lens review pass, using foreground review agents when the environment supports them
|
|
13
14
|
- Ships manual commands when you want direct control:
|
|
14
15
|
- `/autodev-help`
|
|
15
16
|
- `/autodev-new-project`
|
|
16
17
|
- `/autodev-explore-codebase`
|
|
17
18
|
- `/autodev-plan-phase`
|
|
18
19
|
- `/autodev-execute-phase`
|
|
20
|
+
- `/autodev-review-task`
|
|
19
21
|
- `/autodev-review-phase`
|
|
20
22
|
- `/autodev-verify-work`
|
|
21
23
|
- `/autodev-cleanup`
|
|
@@ -43,6 +45,12 @@ Local install for one repo:
|
|
|
43
45
|
npx @mthanhlm/autodev@latest --local
|
|
44
46
|
```
|
|
45
47
|
|
|
48
|
+
Install and also disable Claude Code background tasks in the target `settings.json`:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npx @mthanhlm/autodev@latest --global --disable-background-tasks
|
|
52
|
+
```
|
|
53
|
+
|
|
46
54
|
Uninstall:
|
|
47
55
|
|
|
48
56
|
```bash
|
|
@@ -64,8 +72,10 @@ Typical brownfield route:
|
|
|
64
72
|
-> explore codebase
|
|
65
73
|
-> plan phase
|
|
66
74
|
-> review phase plan
|
|
67
|
-
-> execute
|
|
68
|
-
->
|
|
75
|
+
-> execute one task
|
|
76
|
+
-> review task
|
|
77
|
+
-> execute one task
|
|
78
|
+
-> review task
|
|
69
79
|
-> review
|
|
70
80
|
-> verify
|
|
71
81
|
```
|
|
@@ -99,10 +109,12 @@ project -> track -> phase -> tasks
|
|
|
99
109
|
- `/autodev` stops after planning so the user can review the phase before any execution starts
|
|
100
110
|
- The phase keeps one user-facing orchestration session
|
|
101
111
|
- Each task is preferably executed by a fresh foreground delegated agent
|
|
102
|
-
- After each task, the
|
|
112
|
+
- After each task, the delegated agent reports back with files changed, verification, and blockers
|
|
113
|
+
- `/autodev` stops after each completed task and reopens a task review checkpoint before any further execution
|
|
103
114
|
- No waves by default
|
|
104
|
-
- No parallel execution
|
|
115
|
+
- No automatic parallel execution
|
|
105
116
|
- 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
|
|
117
|
+
- 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`
|
|
106
118
|
|
|
107
119
|
## Git Policy
|
|
108
120
|
|
|
@@ -9,12 +9,9 @@ const DEFAULT_CONFIG = {
|
|
|
9
9
|
},
|
|
10
10
|
workflow: {
|
|
11
11
|
research: false,
|
|
12
|
-
review_after_execute: true
|
|
13
|
-
codebase_parallel_agents: 4
|
|
14
|
-
},
|
|
15
|
-
execution: {
|
|
16
|
-
parallel: false
|
|
12
|
+
review_after_execute: true
|
|
17
13
|
},
|
|
14
|
+
execution: {},
|
|
18
15
|
git: {
|
|
19
16
|
mode: 'read-only'
|
|
20
17
|
},
|
|
@@ -31,13 +28,38 @@ const DEFAULT_CONFIG = {
|
|
|
31
28
|
|
|
32
29
|
const CODEBASE_FILES = ['structure.md', 'domain.md', 'runtime.md', 'quality.md', 'summary.md'];
|
|
33
30
|
|
|
31
|
+
function findWorkspaceRoot(startDir) {
|
|
32
|
+
let cursor = path.resolve(startDir || process.cwd());
|
|
33
|
+
let gitRoot = null;
|
|
34
|
+
|
|
35
|
+
while (true) {
|
|
36
|
+
if (fileExists(path.join(cursor, '.autodev'))) {
|
|
37
|
+
return cursor;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!gitRoot && fileExists(path.join(cursor, '.git'))) {
|
|
41
|
+
gitRoot = cursor;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const parent = path.dirname(cursor);
|
|
45
|
+
if (parent === cursor) {
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
cursor = parent;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return gitRoot || path.resolve(startDir || process.cwd());
|
|
52
|
+
}
|
|
53
|
+
|
|
34
54
|
function autodevDir(cwd) {
|
|
35
|
-
return path.join(cwd, '.autodev');
|
|
55
|
+
return path.join(findWorkspaceRoot(cwd), '.autodev');
|
|
36
56
|
}
|
|
37
57
|
|
|
38
58
|
function rootPaths(cwd) {
|
|
59
|
+
const workspaceRoot = findWorkspaceRoot(cwd);
|
|
39
60
|
const root = autodevDir(cwd);
|
|
40
61
|
return {
|
|
62
|
+
workspaceRoot,
|
|
41
63
|
root,
|
|
42
64
|
config: path.join(root, 'config.json'),
|
|
43
65
|
project: path.join(root, 'PROJECT.md'),
|
|
@@ -86,6 +108,7 @@ function padPhase(number) {
|
|
|
86
108
|
}
|
|
87
109
|
|
|
88
110
|
function detectExistingCodebase(cwd) {
|
|
111
|
+
const workspaceRoot = findWorkspaceRoot(cwd);
|
|
89
112
|
const knownFiles = [
|
|
90
113
|
'package.json',
|
|
91
114
|
'package-lock.json',
|
|
@@ -117,16 +140,16 @@ function detectExistingCodebase(cwd) {
|
|
|
117
140
|
'__tests__'
|
|
118
141
|
];
|
|
119
142
|
|
|
120
|
-
if (knownFiles.some(name => fileExists(path.join(
|
|
143
|
+
if (knownFiles.some(name => fileExists(path.join(workspaceRoot, name)))) {
|
|
121
144
|
return true;
|
|
122
145
|
}
|
|
123
146
|
|
|
124
|
-
if (knownDirs.some(name => fileExists(path.join(
|
|
147
|
+
if (knownDirs.some(name => fileExists(path.join(workspaceRoot, name)))) {
|
|
125
148
|
return true;
|
|
126
149
|
}
|
|
127
150
|
|
|
128
151
|
try {
|
|
129
|
-
const entries = fs.readdirSync(
|
|
152
|
+
const entries = fs.readdirSync(workspaceRoot, { withFileTypes: true });
|
|
130
153
|
return entries.some(entry => {
|
|
131
154
|
if (entry.name === '.autodev' || entry.name === '.claude' || entry.name.startsWith('.')) {
|
|
132
155
|
return false;
|
|
@@ -257,6 +280,35 @@ function readSingleLineField(content, label) {
|
|
|
257
280
|
return match ? match[1].trim() : null;
|
|
258
281
|
}
|
|
259
282
|
|
|
283
|
+
function parseStateSnapshot(content) {
|
|
284
|
+
if (!content) {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const currentPhaseRaw = readSingleLineField(content, 'Current Phase');
|
|
289
|
+
const currentPhaseNumber = currentPhaseRaw && /^\d+$/.test(currentPhaseRaw)
|
|
290
|
+
? Number(currentPhaseRaw)
|
|
291
|
+
: null;
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
currentPhase: currentPhaseNumber,
|
|
295
|
+
currentPhaseType: readSingleLineField(content, 'Current Phase Type'),
|
|
296
|
+
currentStep: readSingleLineField(content, 'Current Step'),
|
|
297
|
+
currentTask: readSingleLineField(content, 'Current Task'),
|
|
298
|
+
currentTaskStatus: readSingleLineField(content, 'Current Task Status'),
|
|
299
|
+
status: readSingleLineField(content, 'Status'),
|
|
300
|
+
nextCommand: readSingleLineField(content, 'Next Command')
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function readStateSnapshot(filePath) {
|
|
305
|
+
return parseStateSnapshot(readText(filePath));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function isBlockedTaskStatus(value) {
|
|
309
|
+
return typeof value === 'string' && /^blocked/i.test(value.trim());
|
|
310
|
+
}
|
|
311
|
+
|
|
260
312
|
function parseTaskDependsOn(value) {
|
|
261
313
|
if (!value || /^none$/i.test(value)) {
|
|
262
314
|
return [];
|
|
@@ -336,9 +388,16 @@ function listTasksForPhaseDetails(phaseDetails) {
|
|
|
336
388
|
}
|
|
337
389
|
|
|
338
390
|
function nextExecutableTask(tasks) {
|
|
339
|
-
return tasks.find(task => !task.summaryExists && task.ready)
|
|
340
|
-
|
|
341
|
-
|
|
391
|
+
return tasks.find(task => !task.summaryExists && task.ready) || null;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function lastCompletedTask(tasks) {
|
|
395
|
+
const completed = tasks.filter(task => task.summaryExists);
|
|
396
|
+
return completed.length > 0 ? completed[completed.length - 1] : null;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function hasDependencyDeadlock(tasks) {
|
|
400
|
+
return tasks.some(task => !task.summaryExists) && !nextExecutableTask(tasks);
|
|
342
401
|
}
|
|
343
402
|
|
|
344
403
|
function listPhases(cwd, slug = readActiveTrack(cwd)) {
|
|
@@ -396,19 +455,51 @@ function resolvePhase(cwd, slug, requestedPhase, mode) {
|
|
|
396
455
|
return phases.find(phase => phase.number === numeric) || null;
|
|
397
456
|
}
|
|
398
457
|
|
|
458
|
+
const track = trackPaths(cwd, slug);
|
|
459
|
+
const trackState = track ? readStateSnapshot(track.state) : null;
|
|
460
|
+
const currentStatePhase = trackState?.currentPhase
|
|
461
|
+
? phases.find(phase => phase.number === trackState.currentPhase) || null
|
|
462
|
+
: null;
|
|
463
|
+
|
|
399
464
|
if (mode === 'plan') {
|
|
465
|
+
if (currentStatePhase && (
|
|
466
|
+
isBlockedTaskStatus(trackState.currentTaskStatus)
|
|
467
|
+
|| trackState.currentStep === 'planning'
|
|
468
|
+
|| trackState.currentStep === 'plan_review'
|
|
469
|
+
)) {
|
|
470
|
+
return currentStatePhase;
|
|
471
|
+
}
|
|
400
472
|
return phases.find(phase => !phase.planExists) || phases[0];
|
|
401
473
|
}
|
|
402
474
|
|
|
403
475
|
if (mode === 'execute') {
|
|
476
|
+
if (currentStatePhase && (
|
|
477
|
+
trackState?.currentStep === 'execution'
|
|
478
|
+
|| trackState?.currentStep === 'task_review'
|
|
479
|
+
)) {
|
|
480
|
+
return currentStatePhase;
|
|
481
|
+
}
|
|
404
482
|
return phases.find(phase => phase.planExists && !phase.summaryExists) || null;
|
|
405
483
|
}
|
|
406
484
|
|
|
485
|
+
if (mode === 'task_review') {
|
|
486
|
+
if (currentStatePhase && trackState?.currentStep === 'task_review') {
|
|
487
|
+
return currentStatePhase;
|
|
488
|
+
}
|
|
489
|
+
return phases.find(phase => phase.planExists && !phase.summaryExists && phase.taskDoneCount > 0) || null;
|
|
490
|
+
}
|
|
491
|
+
|
|
407
492
|
if (mode === 'review') {
|
|
493
|
+
if (currentStatePhase && trackState?.currentStep === 'review') {
|
|
494
|
+
return currentStatePhase;
|
|
495
|
+
}
|
|
408
496
|
return phases.find(phase => phase.summaryExists && !phase.reviewExists) || null;
|
|
409
497
|
}
|
|
410
498
|
|
|
411
499
|
if (mode === 'verify') {
|
|
500
|
+
if (currentStatePhase && trackState?.currentStep === 'verification') {
|
|
501
|
+
return currentStatePhase;
|
|
502
|
+
}
|
|
412
503
|
return phases.find(phase => phase.reviewExists && !phase.uatExists)
|
|
413
504
|
|| [...phases].reverse().find(phase => phase.reviewExists)
|
|
414
505
|
|| null;
|
|
@@ -470,6 +561,7 @@ function buildRoute(cwd) {
|
|
|
470
561
|
}
|
|
471
562
|
|
|
472
563
|
const phases = listPhases(cwd, activeTrack);
|
|
564
|
+
const trackState = readStateSnapshot(track.state);
|
|
473
565
|
if (phases.length === 0) {
|
|
474
566
|
return {
|
|
475
567
|
kind: 'track_setup',
|
|
@@ -481,6 +573,84 @@ function buildRoute(cwd) {
|
|
|
481
573
|
};
|
|
482
574
|
}
|
|
483
575
|
|
|
576
|
+
const currentStatePhase = trackState?.currentPhase
|
|
577
|
+
? phases.find(phase => phase.number === trackState.currentPhase) || null
|
|
578
|
+
: null;
|
|
579
|
+
|
|
580
|
+
if (currentStatePhase && (
|
|
581
|
+
isBlockedTaskStatus(trackState?.currentTaskStatus)
|
|
582
|
+
)) {
|
|
583
|
+
return {
|
|
584
|
+
kind: 'plan_phase',
|
|
585
|
+
command: '/autodev',
|
|
586
|
+
manualCommand: `/autodev-plan-phase ${currentStatePhase.number}`,
|
|
587
|
+
reason: 'blocked_phase_requires_replanning',
|
|
588
|
+
projectType,
|
|
589
|
+
trackSlug: activeTrack,
|
|
590
|
+
phaseNumber: currentStatePhase.number
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (currentStatePhase && trackState?.currentStep === 'execution') {
|
|
595
|
+
return {
|
|
596
|
+
kind: 'execute_phase',
|
|
597
|
+
command: '/autodev',
|
|
598
|
+
manualCommand: `/autodev-execute-phase ${currentStatePhase.number}`,
|
|
599
|
+
reason: 'phase_execution_in_progress',
|
|
600
|
+
projectType,
|
|
601
|
+
trackSlug: activeTrack,
|
|
602
|
+
phaseNumber: currentStatePhase.number
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
if (currentStatePhase && trackState?.currentStep === 'task_review') {
|
|
607
|
+
return {
|
|
608
|
+
kind: 'task_review',
|
|
609
|
+
command: '/autodev',
|
|
610
|
+
manualCommand: `/autodev-review-task ${currentStatePhase.number}`,
|
|
611
|
+
reason: 'task_execution_checkpoint_pending_user_review',
|
|
612
|
+
projectType,
|
|
613
|
+
trackSlug: activeTrack,
|
|
614
|
+
phaseNumber: currentStatePhase.number
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (currentStatePhase && trackState?.currentStep === 'plan_review') {
|
|
619
|
+
return {
|
|
620
|
+
kind: 'plan_review',
|
|
621
|
+
command: '/autodev',
|
|
622
|
+
manualCommand: `/autodev-execute-phase ${currentStatePhase.number}`,
|
|
623
|
+
reason: 'phase_plan_awaits_review',
|
|
624
|
+
projectType,
|
|
625
|
+
trackSlug: activeTrack,
|
|
626
|
+
phaseNumber: currentStatePhase.number
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
if (currentStatePhase && trackState?.currentStep === 'review' && !currentStatePhase.reviewExists) {
|
|
631
|
+
return {
|
|
632
|
+
kind: 'review_phase',
|
|
633
|
+
command: '/autodev',
|
|
634
|
+
manualCommand: `/autodev-review-phase ${currentStatePhase.number}`,
|
|
635
|
+
reason: 'phase_review_in_progress',
|
|
636
|
+
projectType,
|
|
637
|
+
trackSlug: activeTrack,
|
|
638
|
+
phaseNumber: currentStatePhase.number
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if (currentStatePhase && trackState?.currentStep === 'verification' && !currentStatePhase.uatExists) {
|
|
643
|
+
return {
|
|
644
|
+
kind: 'verify_phase',
|
|
645
|
+
command: '/autodev',
|
|
646
|
+
manualCommand: `/autodev-verify-work ${currentStatePhase.number}`,
|
|
647
|
+
reason: 'phase_verification_in_progress',
|
|
648
|
+
projectType,
|
|
649
|
+
trackSlug: activeTrack,
|
|
650
|
+
phaseNumber: currentStatePhase.number
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
|
|
484
654
|
const nextReview = phases.find(phase => phase.summaryExists && !phase.reviewExists);
|
|
485
655
|
if (nextReview && config.workflow.review_after_execute !== false) {
|
|
486
656
|
return {
|
|
@@ -507,6 +677,19 @@ function buildRoute(cwd) {
|
|
|
507
677
|
};
|
|
508
678
|
}
|
|
509
679
|
|
|
680
|
+
const nextTaskReview = phases.find(phase => phase.planExists && !phase.summaryExists && phase.taskDoneCount > 0);
|
|
681
|
+
if (nextTaskReview) {
|
|
682
|
+
return {
|
|
683
|
+
kind: 'task_review',
|
|
684
|
+
command: '/autodev',
|
|
685
|
+
manualCommand: `/autodev-review-task ${nextTaskReview.number}`,
|
|
686
|
+
reason: 'phase_task_completed_awaiting_user_review',
|
|
687
|
+
projectType,
|
|
688
|
+
trackSlug: activeTrack,
|
|
689
|
+
phaseNumber: nextTaskReview.number
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
|
|
510
693
|
const nextPlannedReview = phases.find(phase => phase.planExists && !phase.summaryExists);
|
|
511
694
|
if (nextPlannedReview) {
|
|
512
695
|
return {
|
|
@@ -643,6 +826,7 @@ function buildStatus(cwd) {
|
|
|
643
826
|
|
|
644
827
|
return {
|
|
645
828
|
cwd,
|
|
829
|
+
workspace_root: paths.workspaceRoot,
|
|
646
830
|
autodev_exists: fileExists(paths.root),
|
|
647
831
|
project_exists: fileExists(paths.project),
|
|
648
832
|
existing_code_detected: existingCodebase,
|
|
@@ -706,6 +890,8 @@ function initPayload(cwd, mode, requestedPhase) {
|
|
|
706
890
|
? 'review'
|
|
707
891
|
: mode === 'verify-work'
|
|
708
892
|
? 'verify'
|
|
893
|
+
: mode === 'review-task'
|
|
894
|
+
? 'task_review'
|
|
709
895
|
: mode === 'execute-phase' || mode === 'review-plan'
|
|
710
896
|
? 'execute'
|
|
711
897
|
: mode === 'plan-phase'
|
|
@@ -714,6 +900,17 @@ function initPayload(cwd, mode, requestedPhase) {
|
|
|
714
900
|
const phase = phaseMode && activeTrack ? resolvePhase(cwd, activeTrack, requestedPhase, phaseMode) : null;
|
|
715
901
|
const tasks = phase ? listTasksForPhaseDetails(phase) : [];
|
|
716
902
|
const nextTask = nextExecutableTask(tasks);
|
|
903
|
+
const lastCompleted = lastCompletedTask(tasks);
|
|
904
|
+
const dependencyDeadlock = hasDependencyDeadlock(tasks);
|
|
905
|
+
const trackStateSnapshot = track ? readStateSnapshot(track.state) : null;
|
|
906
|
+
const currentTaskNumber = (() => {
|
|
907
|
+
const raw = trackStateSnapshot?.currentTask;
|
|
908
|
+
if (raw && /^\d+$/.test(raw)) {
|
|
909
|
+
return Number(raw);
|
|
910
|
+
}
|
|
911
|
+
return lastCompleted ? lastCompleted.number : null;
|
|
912
|
+
})();
|
|
913
|
+
const currentTask = tasks.find(task => task.number === currentTaskNumber) || lastCompleted || null;
|
|
717
914
|
const codebase = codebasePaths(cwd);
|
|
718
915
|
|
|
719
916
|
return {
|
|
@@ -742,6 +939,7 @@ function initPayload(cwd, mode, requestedPhase) {
|
|
|
742
939
|
requirements_path: track ? track.requirements : null,
|
|
743
940
|
roadmap_path: track ? track.roadmap : null,
|
|
744
941
|
track_state_path: track ? track.state : null,
|
|
942
|
+
track_state: trackStateSnapshot,
|
|
745
943
|
phases_dir: track ? track.phasesDir : null,
|
|
746
944
|
route,
|
|
747
945
|
phase_found: Boolean(phase),
|
|
@@ -761,9 +959,15 @@ function initPayload(cwd, mode, requestedPhase) {
|
|
|
761
959
|
task_count: tasks.length,
|
|
762
960
|
task_done_count: tasks.filter(task => task.summaryExists).length,
|
|
763
961
|
all_tasks_done: tasks.length > 0 && tasks.every(task => task.summaryExists),
|
|
962
|
+
dependency_deadlock: dependencyDeadlock,
|
|
764
963
|
next_task_number: nextTask ? nextTask.number : null,
|
|
765
964
|
next_task_path: nextTask ? nextTask.taskPath : null,
|
|
766
965
|
next_task_summary_path: nextTask ? nextTask.summaryPath : null,
|
|
966
|
+
current_task_number: currentTask ? currentTask.number : null,
|
|
967
|
+
current_task_path: currentTask ? currentTask.taskPath : null,
|
|
968
|
+
current_task_summary_path: currentTask ? currentTask.summaryPath : null,
|
|
969
|
+
last_completed_task_number: lastCompleted ? lastCompleted.number : null,
|
|
970
|
+
last_completed_task_summary_path: lastCompleted ? lastCompleted.summaryPath : null,
|
|
767
971
|
tasks: tasks.map(task => ({
|
|
768
972
|
number: task.number,
|
|
769
973
|
title: task.title,
|
|
@@ -867,6 +1071,7 @@ module.exports = {
|
|
|
867
1071
|
buildRoute,
|
|
868
1072
|
buildStatus,
|
|
869
1073
|
detectExistingCodebase,
|
|
1074
|
+
findWorkspaceRoot,
|
|
870
1075
|
initPayload,
|
|
871
1076
|
listPhases,
|
|
872
1077
|
listTracks,
|
|
@@ -878,5 +1083,7 @@ module.exports = {
|
|
|
878
1083
|
rootPaths,
|
|
879
1084
|
trackPaths,
|
|
880
1085
|
listTasksForPhaseDetails,
|
|
1086
|
+
lastCompletedTask,
|
|
1087
|
+
hasDependencyDeadlock,
|
|
881
1088
|
nextExecutableTask
|
|
882
1089
|
};
|
|
@@ -27,6 +27,7 @@ node "$AUTODEV_ROOT/autodev/bin/autodev-tools.cjs" status
|
|
|
27
27
|
- `plan_phase`: load and perform the plan-phase workflow.
|
|
28
28
|
- `plan_review`: load and perform the review-plan workflow so the user can review the phase plan before any execution starts.
|
|
29
29
|
- `execute_phase`: load and perform the execute-phase workflow, which should advance one task-sized unit in the current phase session by trying a fresh foreground agent first and falling back cleanly when agent delegation is unavailable.
|
|
30
|
+
- `task_review`: load and perform the review-task workflow so the user can review one completed task before any further execution starts.
|
|
30
31
|
- `review_phase`: load and perform the review-phase workflow.
|
|
31
32
|
- `verify_phase`: load and perform the verify-work workflow.
|
|
32
33
|
|
|
@@ -42,7 +42,7 @@ node "$AUTODEV_ROOT/autodev/bin/autodev-tools.cjs" init execute-phase "$ARGUMENT
|
|
|
42
42
|
5. Determine the next executable task:
|
|
43
43
|
- prefer the first task without a summary whose dependencies are already done
|
|
44
44
|
- if no executable task remains and all task summaries exist, move to phase aggregation
|
|
45
|
-
- if
|
|
45
|
+
- if `dependency_deadlock` is true, stop before execution, set both state files to `Current Step: planning`, set `Current Task: none`, set `Current Task Status: blocked_dependencies`, keep `Next Command: /autodev`, and ask the user how to repair the plan
|
|
46
46
|
|
|
47
47
|
6. Before running a task, show the user:
|
|
48
48
|
- task id and title
|
|
@@ -68,10 +68,10 @@ node "$AUTODEV_ROOT/autodev/bin/autodev-tools.cjs" init execute-phase "$ARGUMENT
|
|
|
68
68
|
|
|
69
69
|
8. If worker delegation is unavailable, do not stop on the raw platform message.
|
|
70
70
|
Treat messages like `specialized agents aren't available` as a capability limitation, then:
|
|
71
|
-
- tell the user plainly that
|
|
71
|
+
- tell the user plainly that delegated-agent execution is unavailable in this environment
|
|
72
72
|
- continue with the same task in the current session
|
|
73
73
|
- keep the same task boundaries
|
|
74
|
-
- still write the task summary before
|
|
74
|
+
- still write the task summary before stopping
|
|
75
75
|
|
|
76
76
|
9. After the delegated agent returns, or after the current-session fallback completes:
|
|
77
77
|
- confirm `TASK-NN-SUMMARY.md` exists
|
|
@@ -80,11 +80,12 @@ node "$AUTODEV_ROOT/autodev/bin/autodev-tools.cjs" init execute-phase "$ARGUMENT
|
|
|
80
80
|
|
|
81
81
|
10. If more tasks remain:
|
|
82
82
|
- update project and track state to:
|
|
83
|
-
- `Current Step:
|
|
84
|
-
- `Current Task: <
|
|
85
|
-
- `Current Task Status:
|
|
83
|
+
- `Current Step: task_review`
|
|
84
|
+
- `Current Task: <current-task>`
|
|
85
|
+
- `Current Task Status: complete`
|
|
86
86
|
- `Next Command: /autodev`
|
|
87
|
-
-
|
|
87
|
+
- stop after this task
|
|
88
|
+
- end by telling the user `/autodev` will open the task review checkpoint before any next-task execution
|
|
88
89
|
|
|
89
90
|
11. If all tasks are done:
|
|
90
91
|
- write or update `NN-SUMMARY.md` from the template with:
|
|
@@ -97,26 +98,30 @@ node "$AUTODEV_ROOT/autodev/bin/autodev-tools.cjs" init execute-phase "$ARGUMENT
|
|
|
97
98
|
12. Update the active track `STATE.md` so it points to:
|
|
98
99
|
- `Current Phase: N`
|
|
99
100
|
- `Current Phase Type: <type>`
|
|
100
|
-
- `Current Step:
|
|
101
|
-
- `Current Task:
|
|
101
|
+
- `Current Step: task_review`
|
|
102
|
+
- `Current Task: <current-task>`
|
|
102
103
|
- `Current Task Status: complete`
|
|
103
104
|
- `Next Command: /autodev`
|
|
104
105
|
- current ISO timestamp
|
|
105
106
|
|
|
106
107
|
13. Update `.autodev/STATE.md` so it points to:
|
|
107
108
|
- `Active Track: <slug>`
|
|
108
|
-
- `Current Step:
|
|
109
|
-
- `Current Task:
|
|
109
|
+
- `Current Step: task_review`
|
|
110
|
+
- `Current Task: <current-task>`
|
|
110
111
|
- `Current Task Status: complete`
|
|
111
112
|
- `Next Command: /autodev`
|
|
112
113
|
- current ISO timestamp
|
|
113
114
|
|
|
114
115
|
14. If the current task is blocked or incomplete:
|
|
115
116
|
- say so clearly in the task summary
|
|
116
|
-
- set both state files to `Current Step:
|
|
117
|
+
- set both state files to `Current Step: planning`
|
|
117
118
|
- set `Current Task: <same-task>`
|
|
118
119
|
- set `Current Task Status: blocked`
|
|
119
120
|
- keep `Next Command: /autodev`
|
|
121
|
+
- tell the user the phase should be revised before more execution
|
|
120
122
|
|
|
121
|
-
15. End with a short outcome summary and the next recommended command.
|
|
123
|
+
15. End with a short outcome summary and the next recommended command.
|
|
124
|
+
- Never execute a second task in the same run.
|
|
125
|
+
- If all tasks are now done, say `/autodev` will open the phase-review checkpoint next.
|
|
126
|
+
- Otherwise, say `/autodev` will open the task-review checkpoint for the completed task.
|
|
122
127
|
</process>
|
|
@@ -3,7 +3,7 @@ Map a brownfield repository quickly and produce a usable codebase brief without
|
|
|
3
3
|
</purpose>
|
|
4
4
|
|
|
5
5
|
<rules>
|
|
6
|
-
- Prefer
|
|
6
|
+
- Prefer a small set of foreground codebase agents, run one at a time, when agent delegation is available.
|
|
7
7
|
- Give each agent a disjoint write target.
|
|
8
8
|
- Use read-only investigation only. No git writes.
|
|
9
9
|
- Focus on information that will improve planning and execution for the active track.
|
|
@@ -26,7 +26,7 @@ node "$AUTODEV_ROOT/autodev/bin/autodev-tools.cjs" init explore-codebase
|
|
|
26
26
|
- the active track docs if they exist
|
|
27
27
|
- representative repo files needed to orient the exploration
|
|
28
28
|
|
|
29
|
-
4. If
|
|
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:
|
|
30
30
|
- `autodev-codebase-structure` for standalone installs, or `autodev:autodev-codebase-structure` in direct plugin runs, owns `.autodev/codebase/structure.md`
|
|
31
31
|
- `autodev-codebase-domain` for standalone installs, or `autodev:autodev-codebase-domain` in direct plugin runs, owns `.autodev/codebase/domain.md`
|
|
32
32
|
- `autodev-codebase-runtime` for standalone installs, or `autodev:autodev-codebase-runtime` in direct plugin runs, owns `.autodev/codebase/runtime.md`
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# autodev
|
|
2
2
|
|
|
3
3
|
Lean Claude Code workflow. No automatic commits. No branches. No worktrees. Git is read-only.
|
|
4
|
+
It resolves `.autodev/` state from the repo root even when you start Claude in a nested subdirectory.
|
|
4
5
|
|
|
5
6
|
## Main Entry
|
|
6
7
|
|
|
@@ -14,13 +15,15 @@ Lean Claude Code workflow. No automatic commits. No branches. No worktrees. Git
|
|
|
14
15
|
- `/autodev-new-project`
|
|
15
16
|
Creates project state in `.autodev/` and the first active track under `.autodev/tracks/<track>/`.
|
|
16
17
|
- `/autodev-explore-codebase`
|
|
17
|
-
Uses
|
|
18
|
+
Uses foreground codebase agents, one at a time when available, to map a brownfield repo into `.autodev/codebase/`.
|
|
18
19
|
- `/autodev-plan-phase [phase]`
|
|
19
20
|
Creates or revises one phase plan plus task files in `.autodev/tracks/<track>/phases/NN-type-name/`.
|
|
20
21
|
- `/autodev-execute-phase [phase]`
|
|
21
|
-
Orchestrates one phase
|
|
22
|
+
Orchestrates exactly one task for the phase, preferring a fresh foreground delegated agent, and writes `TASK-NN-SUMMARY.md`.
|
|
23
|
+
- `/autodev-review-task [phase]`
|
|
24
|
+
Pauses after one completed task so the user can review the result before executing the next task or starting phase review.
|
|
22
25
|
- `/autodev-review-phase [phase]`
|
|
23
|
-
Uses
|
|
26
|
+
Uses foreground review agents, one at a time when available, for code quality, security, integration, and polish, then writes `NN-REVIEW.md`.
|
|
24
27
|
- `/autodev-verify-work [phase]`
|
|
25
28
|
Records manual verification in `NN-UAT.md` and captures fix-return gaps when verification fails.
|
|
26
29
|
- `/autodev-progress`
|
|
@@ -58,6 +61,9 @@ Typical brownfield route:
|
|
|
58
61
|
-> plan
|
|
59
62
|
-> review plan
|
|
60
63
|
-> execute
|
|
64
|
+
-> review task
|
|
65
|
+
-> execute
|
|
66
|
+
-> review task
|
|
61
67
|
-> review
|
|
62
68
|
-> verify
|
|
63
69
|
-> next track or cleanup
|
|
@@ -85,9 +91,12 @@ Blocked:
|
|
|
85
91
|
- `git merge`
|
|
86
92
|
- `git rebase`
|
|
87
93
|
- `git worktree`
|
|
94
|
+
- `git clean`
|
|
95
|
+
- `git cherry-pick`
|
|
88
96
|
- `git push`
|
|
89
97
|
- `git pull`
|
|
90
98
|
- `git stash`
|
|
91
99
|
- `git reset`
|
|
92
100
|
- `git fetch`
|
|
93
101
|
- `git clone`
|
|
102
|
+
- mutating `git branch`, `git tag`, and `git remote` commands
|
|
@@ -31,8 +31,6 @@ node "$AUTODEV_ROOT/autodev/bin/autodev-tools.cjs" init new-project
|
|
|
31
31
|
- `project.type: "greenfield"` or `"brownfield"`
|
|
32
32
|
- `workflow.research: false`
|
|
33
33
|
- `workflow.review_after_execute: true`
|
|
34
|
-
- `workflow.codebase_parallel_agents: 4`
|
|
35
|
-
- `execution.parallel: false`
|
|
36
34
|
- `git.mode: "read-only"`
|
|
37
35
|
|
|
38
36
|
6. Write `.autodev/PROJECT.md` with:
|
|
@@ -3,7 +3,7 @@ Run the automatic review bundle after execution so the user sees code quality, s
|
|
|
3
3
|
</purpose>
|
|
4
4
|
|
|
5
5
|
<rules>
|
|
6
|
-
- Prefer
|
|
6
|
+
- Prefer a small set of foreground review agents, run one at a time, when agent delegation is available.
|
|
7
7
|
- Review findings should be concrete and evidence-based.
|
|
8
8
|
- Do not edit repository code in this step unless the user explicitly asks for fixes.
|
|
9
9
|
- Write one consolidated review artifact for the phase.
|
|
@@ -28,7 +28,7 @@ node "$AUTODEV_ROOT/autodev/bin/autodev-tools.cjs" init review-phase "$ARGUMENTS
|
|
|
28
28
|
- `NN-SUMMARY.md`
|
|
29
29
|
- relevant source files and tests
|
|
30
30
|
|
|
31
|
-
4. If
|
|
31
|
+
4. If agent delegation is available, explicitly call the `Agent` tool for these review subagents, one at a time in the foreground:
|
|
32
32
|
- `autodev-review-quality` for standalone installs, or `autodev:autodev-review-quality` in direct plugin runs
|
|
33
33
|
- `autodev-review-security` for standalone installs, or `autodev:autodev-review-security` in direct plugin runs
|
|
34
34
|
- `autodev-review-integration` for standalone installs, or `autodev:autodev-review-integration` in direct plugin runs
|
|
@@ -54,9 +54,11 @@ node "$AUTODEV_ROOT/autodev/bin/autodev-tools.cjs" init review-phase "$ARGUMENTS
|
|
|
54
54
|
- a final recommendation
|
|
55
55
|
|
|
56
56
|
8. If blockers are found:
|
|
57
|
-
- set project and track state back to `Current Step:
|
|
57
|
+
- set project and track state back to `Current Step: planning`
|
|
58
|
+
- set `Current Task: none`
|
|
59
|
+
- set `Current Task Status: blocked_review`
|
|
58
60
|
- keep `Next Command: /autodev`
|
|
59
|
-
- make the recommendation point back to the same phase
|
|
61
|
+
- make the recommendation point back to revising the same phase with blocker-fix tasks
|
|
60
62
|
|
|
61
63
|
9. If blockers are not found:
|
|
62
64
|
- set project and track state to `Current Step: verification`
|
|
@@ -32,13 +32,14 @@ node "$AUTODEV_ROOT/autodev/bin/autodev-tools.cjs" init review-plan "$ARGUMENTS"
|
|
|
32
32
|
- shared verification plan
|
|
33
33
|
|
|
34
34
|
5. Ask the user what to do next with `AskUserQuestion`:
|
|
35
|
-
- execute
|
|
35
|
+
- execute one task now
|
|
36
36
|
- revise the phase plan
|
|
37
37
|
- stop here
|
|
38
38
|
|
|
39
39
|
6. If the user chooses to execute now:
|
|
40
40
|
- load the execute-phase workflow
|
|
41
|
-
- perform
|
|
41
|
+
- perform exactly one task in the same turn
|
|
42
|
+
- stop after that task completes
|
|
42
43
|
|
|
43
44
|
7. If the user chooses to revise the plan:
|
|
44
45
|
- load the plan-phase workflow
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<purpose>
|
|
2
|
+
Pause after one completed task so the user can review the outcome before more execution happens in the same phase.
|
|
3
|
+
</purpose>
|
|
4
|
+
|
|
5
|
+
<rules>
|
|
6
|
+
- This is a control checkpoint, not an execution step.
|
|
7
|
+
- Do not auto-run the next task from this checkpoint without explicit user approval.
|
|
8
|
+
- Prefer `/autodev` as the entrypoint even at this checkpoint.
|
|
9
|
+
- If all tasks are done, do not auto-start phase review without explicit user approval.
|
|
10
|
+
</rules>
|
|
11
|
+
|
|
12
|
+
<process>
|
|
13
|
+
1. Run:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
AUTODEV_ROOT="${CLAUDE_PLUGIN_ROOT:-$HOME/.claude}"
|
|
17
|
+
node "$AUTODEV_ROOT/autodev/bin/autodev-tools.cjs" init review-task "$ARGUMENTS"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
2. If no phase is found, stop and direct the user to `/autodev`.
|
|
21
|
+
|
|
22
|
+
3. Read:
|
|
23
|
+
- `.autodev/STATE.md`
|
|
24
|
+
- the active track `STATE.md`
|
|
25
|
+
- `NN-PLAN.md`
|
|
26
|
+
- all `TASK-*.md`
|
|
27
|
+
- the current task summary from `current_task_summary_path`
|
|
28
|
+
- if `Current Task` is missing or stale, infer the checkpoint task from the most recent existing `TASK-*-SUMMARY.md`
|
|
29
|
+
- `NN-SUMMARY.md` if it exists
|
|
30
|
+
|
|
31
|
+
4. Show the user a concise checkpoint summary:
|
|
32
|
+
- completed task id and title
|
|
33
|
+
- what changed
|
|
34
|
+
- verification run
|
|
35
|
+
- blockers or open concerns
|
|
36
|
+
- the next ready task, if any
|
|
37
|
+
|
|
38
|
+
5. If more tasks remain, ask the user what to do next with `AskUserQuestion`:
|
|
39
|
+
- execute the next task now
|
|
40
|
+
- revise the phase plan
|
|
41
|
+
- stop here
|
|
42
|
+
|
|
43
|
+
6. If all tasks are done, ask the user what to do next with `AskUserQuestion`:
|
|
44
|
+
- run phase review now
|
|
45
|
+
- revise the phase plan
|
|
46
|
+
- stop here
|
|
47
|
+
|
|
48
|
+
7. If the user chooses to execute the next task:
|
|
49
|
+
- load the execute-phase workflow
|
|
50
|
+
- perform exactly one task in the same turn
|
|
51
|
+
- stop again after that task completes
|
|
52
|
+
|
|
53
|
+
8. If the user chooses to run phase review:
|
|
54
|
+
- load the review-phase workflow
|
|
55
|
+
- perform it in the same turn
|
|
56
|
+
|
|
57
|
+
9. If the user chooses to revise the plan:
|
|
58
|
+
- load the plan-phase workflow
|
|
59
|
+
- revise the same phase in the same turn
|
|
60
|
+
|
|
61
|
+
10. If the user chooses to stop here:
|
|
62
|
+
- keep both state files on:
|
|
63
|
+
- `Current Step: task_review`
|
|
64
|
+
- `Current Task: <current-task>`
|
|
65
|
+
- `Current Task Status: complete`
|
|
66
|
+
- `Next Command: /autodev`
|
|
67
|
+
- end with a short note that no further execution has started
|
|
68
|
+
|
|
69
|
+
11. End with a short outcome summary and `/autodev` as the default next command.
|
|
70
|
+
</process>
|
|
@@ -42,9 +42,9 @@ node "$AUTODEV_ROOT/autodev/bin/autodev-tools.cjs" init verify-work "$ARGUMENTS"
|
|
|
42
42
|
7. Update the active track `STATE.md`:
|
|
43
43
|
- if verification passed and another phase remains, move to the next phase and set `Current Step: planning`
|
|
44
44
|
- if verification passed and no phase remains, set `Current Step: complete`
|
|
45
|
-
- if verification failed, keep the same phase active and set `Current Step:
|
|
45
|
+
- if verification failed, keep the same phase active and set `Current Step: planning`
|
|
46
46
|
- when moving to another phase, set `Current Task: none` and `Current Task Status: idle`
|
|
47
|
-
- when verification failed, set `Current Task: none` and `Current Task Status:
|
|
47
|
+
- when verification failed, set `Current Task: none` and `Current Task Status: blocked_verification`
|
|
48
48
|
- always set `Next Command: /autodev`
|
|
49
49
|
- always refresh the ISO timestamp
|
|
50
50
|
|
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-paths.js',
|
|
17
18
|
'autodev-context-monitor.js',
|
|
18
19
|
'autodev-git-guard.js',
|
|
19
20
|
'autodev-phase-boundary.sh',
|
|
@@ -268,6 +269,13 @@ function removeManagedSettings(settings) {
|
|
|
268
269
|
delete settings.statusLine;
|
|
269
270
|
}
|
|
270
271
|
|
|
272
|
+
if (settings.env && typeof settings.env === 'object') {
|
|
273
|
+
delete settings.env.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS;
|
|
274
|
+
if (Object.keys(settings.env).length === 0) {
|
|
275
|
+
delete settings.env;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
271
279
|
if (!settings.hooks || typeof settings.hooks !== 'object') {
|
|
272
280
|
return settings;
|
|
273
281
|
}
|
|
@@ -321,6 +329,13 @@ function ensureHook(settings, eventName, matcher, command, timeout) {
|
|
|
321
329
|
settings.hooks[eventName].push(entry);
|
|
322
330
|
}
|
|
323
331
|
|
|
332
|
+
function ensureEnvSetting(settings, name, value) {
|
|
333
|
+
if (!settings.env || typeof settings.env !== 'object') {
|
|
334
|
+
settings.env = {};
|
|
335
|
+
}
|
|
336
|
+
settings.env[name] = String(value);
|
|
337
|
+
}
|
|
338
|
+
|
|
324
339
|
function setExecutableBits(targetDir) {
|
|
325
340
|
const candidates = [
|
|
326
341
|
path.join(targetDir, 'hooks', 'autodev-phase-boundary.sh'),
|
|
@@ -397,6 +412,10 @@ function configureSettings(targetDir, isGlobal, options = {}) {
|
|
|
397
412
|
};
|
|
398
413
|
}
|
|
399
414
|
|
|
415
|
+
if (options.disableBackgroundTasks) {
|
|
416
|
+
ensureEnvSetting(settings, 'CLAUDE_CODE_DISABLE_BACKGROUND_TASKS', '1');
|
|
417
|
+
}
|
|
418
|
+
|
|
400
419
|
writeSettings(settingsPath, settings);
|
|
401
420
|
return settingsPath;
|
|
402
421
|
}
|
|
@@ -546,6 +565,8 @@ function printHelp() {
|
|
|
546
565
|
Options:
|
|
547
566
|
--global Install to Claude Code config directory
|
|
548
567
|
--local Install to the current project (.claude/)
|
|
568
|
+
--disable-background-tasks
|
|
569
|
+
Set CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 in Claude Code settings.json
|
|
549
570
|
--uninstall Remove autodev from the selected location
|
|
550
571
|
--help Show this help
|
|
551
572
|
|
|
@@ -553,6 +574,7 @@ Examples:
|
|
|
553
574
|
npx @mthanhlm/autodev@latest
|
|
554
575
|
npx @mthanhlm/autodev@latest --global
|
|
555
576
|
npx @mthanhlm/autodev@latest --local
|
|
577
|
+
npx @mthanhlm/autodev@latest --global --disable-background-tasks
|
|
556
578
|
npx @mthanhlm/autodev@latest --global --uninstall
|
|
557
579
|
`);
|
|
558
580
|
}
|
|
@@ -582,6 +604,27 @@ Location:
|
|
|
582
604
|
});
|
|
583
605
|
}
|
|
584
606
|
|
|
607
|
+
function askDisableBackgroundTasks() {
|
|
608
|
+
return new Promise(resolve => {
|
|
609
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
610
|
+
console.log(`
|
|
611
|
+
Background tasks:
|
|
612
|
+
${cyan}1${reset}) Disable in Claude Code settings ${yellow}(Recommended for autodev)${reset}
|
|
613
|
+
-> sets CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1
|
|
614
|
+
${cyan}2${reset}) Leave background-task settings unchanged
|
|
615
|
+
`);
|
|
616
|
+
rl.question(`Select background-task policy ${yellow}[1]${reset}: `, answer => {
|
|
617
|
+
rl.close();
|
|
618
|
+
const trimmed = answer.trim().toLowerCase();
|
|
619
|
+
if (trimmed === '2' || trimmed === 'n' || trimmed === 'no' || trimmed === 'leave') {
|
|
620
|
+
resolve(false);
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
resolve(true);
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
|
|
585
628
|
async function main() {
|
|
586
629
|
const args = process.argv.slice(2);
|
|
587
630
|
if (args.includes('--help') || args.includes('-h')) {
|
|
@@ -590,20 +633,30 @@ async function main() {
|
|
|
590
633
|
}
|
|
591
634
|
|
|
592
635
|
let scope = null;
|
|
636
|
+
let scopeWasExplicit = false;
|
|
593
637
|
if (args.includes('--global') || args.includes('-g')) {
|
|
594
638
|
scope = 'global';
|
|
639
|
+
scopeWasExplicit = true;
|
|
595
640
|
} else if (args.includes('--local') || args.includes('-l')) {
|
|
596
641
|
scope = 'local';
|
|
642
|
+
scopeWasExplicit = true;
|
|
597
643
|
}
|
|
598
644
|
|
|
599
645
|
if (!scope) {
|
|
600
646
|
scope = await askScope();
|
|
601
647
|
}
|
|
602
648
|
|
|
603
|
-
|
|
649
|
+
const uninstallRequested = args.includes('--uninstall') || args.includes('-u');
|
|
650
|
+
const disableBackgroundTasks = args.includes('--disable-background-tasks')
|
|
651
|
+
? true
|
|
652
|
+
: uninstallRequested || scopeWasExplicit
|
|
653
|
+
? false
|
|
654
|
+
: await askDisableBackgroundTasks();
|
|
655
|
+
|
|
656
|
+
if (uninstallRequested) {
|
|
604
657
|
uninstall({ scope });
|
|
605
658
|
} else {
|
|
606
|
-
install({ scope });
|
|
659
|
+
install({ scope, disableBackgroundTasks });
|
|
607
660
|
}
|
|
608
661
|
}
|
|
609
662
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: autodev:execute-phase
|
|
3
|
-
description: Execute
|
|
3
|
+
description: Execute exactly one active-track task at a time, preferring a fresh foreground agent with clean fallback when unavailable
|
|
4
4
|
argument-hint: "[phase-number]"
|
|
5
5
|
allowed-tools:
|
|
6
6
|
- Read
|
|
@@ -14,7 +14,7 @@ allowed-tools:
|
|
|
14
14
|
- Agent
|
|
15
15
|
---
|
|
16
16
|
<objective>
|
|
17
|
-
Execute one
|
|
17
|
+
Execute exactly one task-sized unit for the active phase, then stop at the post-task checkpoint.
|
|
18
18
|
</objective>
|
|
19
19
|
|
|
20
20
|
<execution_context>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: autodev:explore-codebase
|
|
3
|
-
description: Map an existing codebase, preferring
|
|
3
|
+
description: Map an existing codebase, preferring foreground delegated agents with clean fallback when unavailable
|
|
4
4
|
allowed-tools:
|
|
5
5
|
- Read
|
|
6
6
|
- Write
|
|
@@ -12,7 +12,7 @@ allowed-tools:
|
|
|
12
12
|
- Agent
|
|
13
13
|
---
|
|
14
14
|
<objective>
|
|
15
|
-
Explore the current repository, use
|
|
15
|
+
Explore the current repository, use foreground codebase agents when available, and write the brownfield map into `.autodev/codebase/`.
|
|
16
16
|
</objective>
|
|
17
17
|
|
|
18
18
|
<execution_context>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: autodev:review-phase
|
|
3
|
-
description: Run a review pass for code quality, security, integration, and product polish, preferring
|
|
3
|
+
description: Run a review pass for code quality, security, integration, and product polish, preferring foreground agents when available
|
|
4
4
|
argument-hint: "[phase-number]"
|
|
5
5
|
allowed-tools:
|
|
6
6
|
- Read
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: autodev:review-task
|
|
3
|
+
description: Pause after one completed task so the user can review it before any further phase execution
|
|
4
|
+
argument-hint: "[phase-number]"
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Edit
|
|
9
|
+
- Bash
|
|
10
|
+
- Grep
|
|
11
|
+
- Glob
|
|
12
|
+
- AskUserQuestion
|
|
13
|
+
- TodoWrite
|
|
14
|
+
---
|
|
15
|
+
<objective>
|
|
16
|
+
Review one completed task and decide whether to continue execution, revise the phase, or stop here.
|
|
17
|
+
</objective>
|
|
18
|
+
|
|
19
|
+
<execution_context>
|
|
20
|
+
@~/.claude/autodev/workflows/review-task.md
|
|
21
|
+
</execution_context>
|
|
22
|
+
|
|
23
|
+
<process>
|
|
24
|
+
Execute the workflow in @~/.claude/autodev/workflows/review-task.md end-to-end.
|
|
25
|
+
</process>
|
|
@@ -3,18 +3,11 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const path = require('path');
|
|
6
|
+
const { readProjectConfig } = require('./autodev-paths.js');
|
|
6
7
|
|
|
7
8
|
const WARNING_THRESHOLD = 35;
|
|
8
9
|
const CRITICAL_THRESHOLD = 20;
|
|
9
10
|
|
|
10
|
-
function readProjectConfig(cwd) {
|
|
11
|
-
try {
|
|
12
|
-
return JSON.parse(fs.readFileSync(path.join(cwd, '.autodev', 'config.json'), 'utf8'));
|
|
13
|
-
} catch {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
11
|
let input = '';
|
|
19
12
|
const stdinTimeout = setTimeout(() => process.exit(0), 10000);
|
|
20
13
|
process.stdin.setEncoding('utf8');
|
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
function readProjectConfig(cwd) {
|
|
7
|
-
try {
|
|
8
|
-
return JSON.parse(fs.readFileSync(path.join(cwd, '.autodev', 'config.json'), 'utf8'));
|
|
9
|
-
} catch {
|
|
10
|
-
return null;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
4
|
+
const { readProjectConfig } = require('./autodev-paths.js');
|
|
13
5
|
|
|
14
6
|
function shouldBlock(command) {
|
|
15
7
|
const blockedPatterns = [
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
function fileExists(filePath) {
|
|
7
|
+
try {
|
|
8
|
+
return fs.existsSync(filePath);
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function findWorkspaceRoot(startDir) {
|
|
15
|
+
let cursor = path.resolve(startDir || process.cwd());
|
|
16
|
+
let gitRoot = null;
|
|
17
|
+
|
|
18
|
+
while (true) {
|
|
19
|
+
if (fileExists(path.join(cursor, '.autodev'))) {
|
|
20
|
+
return cursor;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!gitRoot && fileExists(path.join(cursor, '.git'))) {
|
|
24
|
+
gitRoot = cursor;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const parent = path.dirname(cursor);
|
|
28
|
+
if (parent === cursor) {
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
cursor = parent;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return gitRoot || path.resolve(startDir || process.cwd());
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function findAutodevRoot(startDir) {
|
|
38
|
+
const workspaceRoot = findWorkspaceRoot(startDir);
|
|
39
|
+
const autodevRoot = path.join(workspaceRoot, '.autodev');
|
|
40
|
+
return fileExists(autodevRoot) ? autodevRoot : null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readProjectConfig(startDir) {
|
|
44
|
+
const autodevRoot = findAutodevRoot(startDir);
|
|
45
|
+
if (!autodevRoot) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
return JSON.parse(fs.readFileSync(path.join(autodevRoot, 'config.json'), 'utf8'));
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = {
|
|
57
|
+
findAutodevRoot,
|
|
58
|
+
findWorkspaceRoot,
|
|
59
|
+
readProjectConfig
|
|
60
|
+
};
|
|
@@ -1,12 +1,41 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
find_workspace_root() {
|
|
4
|
+
local cursor
|
|
5
|
+
cursor="$(pwd -P)"
|
|
6
|
+
local git_root=""
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
while true; do
|
|
9
|
+
if [ -d "$cursor/.autodev" ]; then
|
|
10
|
+
printf '%s\n' "$cursor"
|
|
11
|
+
return 0
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
if [ -z "$git_root" ] && [ -e "$cursor/.git" ]; then
|
|
15
|
+
git_root="$cursor"
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
if [ "$cursor" = "/" ]; then
|
|
19
|
+
break
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
cursor="$(dirname "$cursor")"
|
|
23
|
+
done
|
|
24
|
+
|
|
25
|
+
if [ -n "$git_root" ]; then
|
|
26
|
+
printf '%s\n' "$git_root"
|
|
27
|
+
return 0
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
return 1
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
WORKSPACE_ROOT="$(find_workspace_root)" || exit 0
|
|
34
|
+
CONFIG="$WORKSPACE_ROOT/.autodev/config.json"
|
|
35
|
+
|
|
36
|
+
[ -f "$CONFIG" ] || exit 0
|
|
8
37
|
|
|
9
|
-
ENABLED=$(node -e "try{const c=
|
|
38
|
+
ENABLED=$(node -e "const fs=require('fs');const p=process.argv[1];try{const c=JSON.parse(fs.readFileSync(p,'utf8'));process.stdout.write(c.hooks?.phase_boundary===false?'0':'1')}catch{process.stdout.write('0')}" "$CONFIG" 2>/dev/null)
|
|
10
39
|
if [ "$ENABLED" != "1" ]; then
|
|
11
40
|
exit 0
|
|
12
41
|
fi
|
|
@@ -2,14 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
-
|
|
6
|
-
function readProjectConfig(cwd) {
|
|
7
|
-
try {
|
|
8
|
-
return JSON.parse(fs.readFileSync(path.join(cwd, '.autodev', 'config.json'), 'utf8'));
|
|
9
|
-
} catch {
|
|
10
|
-
return null;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
5
|
+
const { readProjectConfig } = require('./autodev-paths.js');
|
|
13
6
|
|
|
14
7
|
let input = '';
|
|
15
8
|
const stdinTimeout = setTimeout(() => process.exit(0), 3000);
|
|
@@ -1,13 +1,42 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
find_workspace_root() {
|
|
4
|
+
local cursor
|
|
5
|
+
cursor="$(pwd -P)"
|
|
6
|
+
local git_root=""
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
while true; do
|
|
9
|
+
if [ -d "$cursor/.autodev" ]; then
|
|
10
|
+
printf '%s\n' "$cursor"
|
|
11
|
+
return 0
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
if [ -z "$git_root" ] && [ -e "$cursor/.git" ]; then
|
|
15
|
+
git_root="$cursor"
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
if [ "$cursor" = "/" ]; then
|
|
19
|
+
break
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
cursor="$(dirname "$cursor")"
|
|
23
|
+
done
|
|
24
|
+
|
|
25
|
+
if [ -n "$git_root" ]; then
|
|
26
|
+
printf '%s\n' "$git_root"
|
|
27
|
+
return 0
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
return 1
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
WORKSPACE_ROOT="$(find_workspace_root)" || exit 0
|
|
34
|
+
CONFIG="$WORKSPACE_ROOT/.autodev/config.json"
|
|
35
|
+
STATE="$WORKSPACE_ROOT/.autodev/STATE.md"
|
|
36
|
+
|
|
37
|
+
[ -f "$CONFIG" ] || exit 0
|
|
9
38
|
|
|
10
|
-
ENABLED=$(node -e "try{const c=
|
|
39
|
+
ENABLED=$(node -e "const fs=require('fs');const p=process.argv[1];try{const c=JSON.parse(fs.readFileSync(p,'utf8'));process.stdout.write(c.hooks?.session_state===false?'0':'1')}catch{process.stdout.write('0')}" "$CONFIG" 2>/dev/null)
|
|
11
40
|
if [ "$ENABLED" != "1" ]; then
|
|
12
41
|
exit 0
|
|
13
42
|
fi
|
|
@@ -3,19 +3,12 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const path = require('path');
|
|
6
|
+
const { findAutodevRoot } = require('./autodev-paths.js');
|
|
6
7
|
|
|
7
8
|
function readStateFields(cwd) {
|
|
8
9
|
try {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
while (cursor && cursor !== path.dirname(cursor)) {
|
|
12
|
-
const candidate = path.join(cursor, '.autodev', 'STATE.md');
|
|
13
|
-
if (fs.existsSync(candidate)) {
|
|
14
|
-
statePath = candidate;
|
|
15
|
-
break;
|
|
16
|
-
}
|
|
17
|
-
cursor = path.dirname(cursor);
|
|
18
|
-
}
|
|
10
|
+
const autodevRoot = findAutodevRoot(cwd);
|
|
11
|
+
const statePath = autodevRoot ? path.join(autodevRoot, 'STATE.md') : null;
|
|
19
12
|
|
|
20
13
|
if (!statePath) {
|
|
21
14
|
return {
|
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const fs = require('fs');
|
|
4
3
|
const path = require('path');
|
|
5
|
-
|
|
6
|
-
function readProjectConfig(cwd) {
|
|
7
|
-
try {
|
|
8
|
-
return JSON.parse(fs.readFileSync(path.join(cwd, '.autodev', 'config.json'), 'utf8'));
|
|
9
|
-
} catch {
|
|
10
|
-
return null;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
4
|
+
const { readProjectConfig } = require('./autodev-paths.js');
|
|
13
5
|
|
|
14
6
|
let input = '';
|
|
15
7
|
const stdinTimeout = setTimeout(() => process.exit(0), 3000);
|
package/package.json
CHANGED