@mthanhlm/autodev 0.4.2 → 0.4.4
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/README.md +16 -1
- package/autodev/bin/autodev-tools.cjs +314 -69
- package/autodev/templates/project-state.md +2 -0
- package/autodev/templates/state.md +2 -0
- package/autodev/templates/track-state.md +2 -0
- package/autodev/templates/track.md +8 -1
- package/autodev/workflows/autodev-auto.md +62 -0
- package/autodev/workflows/autodev.md +15 -2
- package/autodev/workflows/execute-phase.md +32 -15
- package/autodev/workflows/explore-codebase.md +17 -8
- package/autodev/workflows/help.md +4 -1
- package/autodev/workflows/new-project.md +26 -11
- package/autodev/workflows/plan-phase.md +11 -3
- package/autodev/workflows/review-phase.md +9 -2
- package/autodev/workflows/verify-work.md +17 -3
- package/bin/install.js +113 -23
- package/commands/autodev/auto.md +27 -0
- package/hooks/autodev-phase-boundary.sh +1 -1
- package/hooks/autodev-prompt-guard.js +26 -3
- package/hooks/autodev-read-guard.js +2 -2
- package/hooks/autodev-statusline.js +9 -4
- package/hooks/autodev-workflow-guard.js +1 -1
- package/hooks/hooks.json +4 -4
- package/package.json +1 -1
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
6
|
+
const DEFAULT_RUN_MODE = 'checkpointed';
|
|
7
|
+
const AUTO_RETRY_LIMIT = 2;
|
|
8
|
+
|
|
6
9
|
const DEFAULT_CONFIG = {
|
|
7
10
|
project: {
|
|
8
11
|
type: null
|
|
@@ -28,6 +31,50 @@ const DEFAULT_CONFIG = {
|
|
|
28
31
|
};
|
|
29
32
|
|
|
30
33
|
const CODEBASE_FILES = ['structure.md', 'domain.md', 'runtime.md', 'quality.md', 'summary.md'];
|
|
34
|
+
const CODE_FILE_EXTENSIONS = new Set([
|
|
35
|
+
'.js', '.jsx', '.cjs', '.mjs',
|
|
36
|
+
'.ts', '.tsx',
|
|
37
|
+
'.py',
|
|
38
|
+
'.go',
|
|
39
|
+
'.rs',
|
|
40
|
+
'.java',
|
|
41
|
+
'.rb',
|
|
42
|
+
'.php',
|
|
43
|
+
'.cs',
|
|
44
|
+
'.cpp',
|
|
45
|
+
'.cc',
|
|
46
|
+
'.c',
|
|
47
|
+
'.h',
|
|
48
|
+
'.hpp',
|
|
49
|
+
'.swift',
|
|
50
|
+
'.kt',
|
|
51
|
+
'.kts',
|
|
52
|
+
'.scala'
|
|
53
|
+
]);
|
|
54
|
+
const NESTED_CODE_MARKER_FILES = new Set([
|
|
55
|
+
'package.json',
|
|
56
|
+
'tsconfig.json',
|
|
57
|
+
'jsconfig.json',
|
|
58
|
+
'pyproject.toml',
|
|
59
|
+
'requirements.txt',
|
|
60
|
+
'Pipfile',
|
|
61
|
+
'Cargo.toml',
|
|
62
|
+
'go.mod',
|
|
63
|
+
'pom.xml',
|
|
64
|
+
'build.gradle',
|
|
65
|
+
'Dockerfile'
|
|
66
|
+
]);
|
|
67
|
+
const IGNORED_SCAN_DIRS = new Set([
|
|
68
|
+
'.autodev',
|
|
69
|
+
'.claude',
|
|
70
|
+
'.git',
|
|
71
|
+
'node_modules',
|
|
72
|
+
'dist',
|
|
73
|
+
'build',
|
|
74
|
+
'coverage',
|
|
75
|
+
'tmp',
|
|
76
|
+
'vendor'
|
|
77
|
+
]);
|
|
31
78
|
|
|
32
79
|
function findWorkspaceRoot(startDir) {
|
|
33
80
|
let cursor = path.resolve(startDir || process.cwd());
|
|
@@ -108,6 +155,55 @@ function padPhase(number) {
|
|
|
108
155
|
return String(number).padStart(2, '0');
|
|
109
156
|
}
|
|
110
157
|
|
|
158
|
+
function normalizeRunMode(value) {
|
|
159
|
+
return value === 'auto' ? 'auto' : DEFAULT_RUN_MODE;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function routeCommandForRunMode(runMode) {
|
|
163
|
+
return normalizeRunMode(runMode) === 'auto' ? '/autodev-auto' : '/autodev';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function codeFileNameLooksReal(entryName) {
|
|
167
|
+
return CODE_FILE_EXTENSIONS.has(path.extname(entryName).toLowerCase());
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function directoryContainsCodeMarker(dirPath, depth = 2) {
|
|
171
|
+
if (depth < 0 || !fileExists(dirPath)) {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
let entries;
|
|
176
|
+
try {
|
|
177
|
+
entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
178
|
+
} catch {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
for (const entry of entries) {
|
|
183
|
+
if (entry.name.startsWith('.')) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
188
|
+
if (entry.isFile()) {
|
|
189
|
+
if (codeFileNameLooksReal(entry.name) || NESTED_CODE_MARKER_FILES.has(entry.name)) {
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (!entry.isDirectory() || depth === 0 || IGNORED_SCAN_DIRS.has(entry.name)) {
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (directoryContainsCodeMarker(fullPath, depth - 1)) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
|
|
111
207
|
function detectExistingCodebase(cwd) {
|
|
112
208
|
const workspaceRoot = findWorkspaceRoot(cwd);
|
|
113
209
|
const knownFiles = [
|
|
@@ -155,7 +251,16 @@ function detectExistingCodebase(cwd) {
|
|
|
155
251
|
if (entry.name === '.autodev' || entry.name === '.claude' || entry.name.startsWith('.')) {
|
|
156
252
|
return false;
|
|
157
253
|
}
|
|
158
|
-
|
|
254
|
+
|
|
255
|
+
if (entry.isFile()) {
|
|
256
|
+
return codeFileNameLooksReal(entry.name);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (!entry.isDirectory() || IGNORED_SCAN_DIRS.has(entry.name)) {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return directoryContainsCodeMarker(path.join(workspaceRoot, entry.name));
|
|
159
264
|
});
|
|
160
265
|
} catch {
|
|
161
266
|
return false;
|
|
@@ -281,6 +386,14 @@ function readSingleLineField(content, label) {
|
|
|
281
386
|
return match ? match[1].trim() : null;
|
|
282
387
|
}
|
|
283
388
|
|
|
389
|
+
function readNumericField(content, label, fallback = 0) {
|
|
390
|
+
const raw = readSingleLineField(content, label);
|
|
391
|
+
if (raw && /^\d+$/.test(raw)) {
|
|
392
|
+
return Number(raw);
|
|
393
|
+
}
|
|
394
|
+
return fallback;
|
|
395
|
+
}
|
|
396
|
+
|
|
284
397
|
function parseStateSnapshot(content) {
|
|
285
398
|
if (!content) {
|
|
286
399
|
return null;
|
|
@@ -298,7 +411,9 @@ function parseStateSnapshot(content) {
|
|
|
298
411
|
currentTask: readSingleLineField(content, 'Current Task'),
|
|
299
412
|
currentTaskStatus: readSingleLineField(content, 'Current Task Status'),
|
|
300
413
|
status: readSingleLineField(content, 'Status'),
|
|
301
|
-
nextCommand: readSingleLineField(content, 'Next Command')
|
|
414
|
+
nextCommand: readSingleLineField(content, 'Next Command'),
|
|
415
|
+
runMode: normalizeRunMode(readSingleLineField(content, 'Run Mode')),
|
|
416
|
+
autoRetryCount: readNumericField(content, 'Auto Retry Count', 0)
|
|
302
417
|
};
|
|
303
418
|
}
|
|
304
419
|
|
|
@@ -405,6 +520,26 @@ function hasDependencyDeadlock(tasks) {
|
|
|
405
520
|
return tasks.some(task => !task.summaryExists) && !nextExecutableTask(tasks);
|
|
406
521
|
}
|
|
407
522
|
|
|
523
|
+
function buildRouteResult(route, runMode, options = {}) {
|
|
524
|
+
const normalizedRunMode = normalizeRunMode(runMode);
|
|
525
|
+
return {
|
|
526
|
+
...route,
|
|
527
|
+
command: routeCommandForRunMode(normalizedRunMode),
|
|
528
|
+
run_mode: normalizedRunMode,
|
|
529
|
+
auto_continuable: normalizedRunMode === 'auto' ? options.autoContinuable !== false : false,
|
|
530
|
+
pause_reason: normalizedRunMode === 'auto' ? (options.pauseReason || null) : null
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function phaseTaskSnapshot(phase) {
|
|
535
|
+
const tasks = phase ? listTasksForPhaseDetails(phase) : [];
|
|
536
|
+
return {
|
|
537
|
+
tasks,
|
|
538
|
+
nextTask: nextExecutableTask(tasks),
|
|
539
|
+
dependencyDeadlock: hasDependencyDeadlock(tasks)
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
408
543
|
function listPhases(cwd, slug = readActiveTrack(cwd)) {
|
|
409
544
|
const track = trackPaths(cwd, slug);
|
|
410
545
|
if (!track || !fileExists(track.roadmap)) {
|
|
@@ -513,7 +648,8 @@ function resolvePhase(cwd, slug, requestedPhase, mode) {
|
|
|
513
648
|
return phases[0];
|
|
514
649
|
}
|
|
515
650
|
|
|
516
|
-
function buildRoute(cwd) {
|
|
651
|
+
function buildRoute(cwd, options = {}) {
|
|
652
|
+
const runMode = normalizeRunMode(options.runMode);
|
|
517
653
|
const paths = rootPaths(cwd);
|
|
518
654
|
const config = loadConfig(cwd);
|
|
519
655
|
const existingCodebase = detectExistingCodebase(cwd);
|
|
@@ -524,58 +660,65 @@ function buildRoute(cwd) {
|
|
|
524
660
|
const tracks = listTracks(cwd);
|
|
525
661
|
|
|
526
662
|
if (!projectExists) {
|
|
527
|
-
return {
|
|
663
|
+
return buildRouteResult({
|
|
528
664
|
kind: 'init_project',
|
|
529
|
-
command: '/autodev',
|
|
530
665
|
manualCommand: '/autodev-new-project',
|
|
531
666
|
reason: existingCodebase ? 'existing_code_detected_without_project_state' : 'project_state_missing',
|
|
532
667
|
projectType
|
|
533
|
-
}
|
|
668
|
+
}, runMode, {
|
|
669
|
+
autoContinuable: false,
|
|
670
|
+
pauseReason: 'missing_intent'
|
|
671
|
+
});
|
|
534
672
|
}
|
|
535
673
|
|
|
536
674
|
if (projectType === 'brownfield' && !codebaseMapExists) {
|
|
537
|
-
return {
|
|
675
|
+
return buildRouteResult({
|
|
538
676
|
kind: 'explore_codebase',
|
|
539
|
-
command: '/autodev',
|
|
540
677
|
manualCommand: '/autodev-explore-codebase',
|
|
541
678
|
reason: 'brownfield_project_needs_codebase_map',
|
|
542
679
|
projectType
|
|
543
|
-
};
|
|
680
|
+
}, runMode);
|
|
544
681
|
}
|
|
545
682
|
|
|
546
683
|
if (!activeTrack) {
|
|
547
|
-
return {
|
|
684
|
+
return buildRouteResult({
|
|
548
685
|
kind: 'track_select',
|
|
549
|
-
command: '/autodev',
|
|
550
686
|
manualCommand: null,
|
|
551
687
|
reason: tracks.length > 0 ? 'no_active_track_selected' : 'no_tracks_created',
|
|
552
688
|
projectType
|
|
553
|
-
}
|
|
689
|
+
}, runMode, {
|
|
690
|
+
autoContinuable: false,
|
|
691
|
+
pauseReason: 'track_selection'
|
|
692
|
+
});
|
|
554
693
|
}
|
|
555
694
|
|
|
556
695
|
const track = trackPaths(cwd, activeTrack);
|
|
557
696
|
if (!track || !fileExists(track.track) || !fileExists(track.roadmap) || !fileExists(track.state)) {
|
|
558
|
-
return {
|
|
697
|
+
return buildRouteResult({
|
|
559
698
|
kind: 'track_setup',
|
|
560
|
-
command: '/autodev',
|
|
561
699
|
manualCommand: null,
|
|
562
700
|
reason: 'active_track_missing_required_artifacts',
|
|
563
701
|
projectType,
|
|
564
702
|
trackSlug: activeTrack
|
|
565
|
-
}
|
|
703
|
+
}, runMode, {
|
|
704
|
+
autoContinuable: false,
|
|
705
|
+
pauseReason: 'missing_intent'
|
|
706
|
+
});
|
|
566
707
|
}
|
|
567
708
|
|
|
568
709
|
const phases = listPhases(cwd, activeTrack);
|
|
569
710
|
const trackState = readStateSnapshot(track.state);
|
|
570
711
|
if (phases.length === 0) {
|
|
571
|
-
return {
|
|
712
|
+
return buildRouteResult({
|
|
572
713
|
kind: 'track_setup',
|
|
573
|
-
command: '/autodev',
|
|
574
714
|
manualCommand: null,
|
|
575
715
|
reason: 'active_track_has_no_phases',
|
|
576
716
|
projectType,
|
|
577
717
|
trackSlug: activeTrack
|
|
578
|
-
}
|
|
718
|
+
}, runMode, {
|
|
719
|
+
autoContinuable: false,
|
|
720
|
+
pauseReason: 'missing_intent'
|
|
721
|
+
});
|
|
579
722
|
}
|
|
580
723
|
|
|
581
724
|
const currentStatePhase = trackState?.currentPhase
|
|
@@ -585,153 +728,235 @@ function buildRoute(cwd) {
|
|
|
585
728
|
if (currentStatePhase && (
|
|
586
729
|
isBlockedTaskStatus(trackState?.currentTaskStatus)
|
|
587
730
|
)) {
|
|
588
|
-
|
|
731
|
+
const retryLimitHit = runMode === 'auto' && (trackState?.autoRetryCount || 0) >= AUTO_RETRY_LIMIT;
|
|
732
|
+
return buildRouteResult({
|
|
589
733
|
kind: 'plan_phase',
|
|
590
|
-
command: '/autodev',
|
|
591
734
|
manualCommand: `/autodev-plan-phase ${currentStatePhase.number}`,
|
|
592
735
|
reason: 'blocked_phase_requires_replanning',
|
|
593
736
|
projectType,
|
|
594
737
|
trackSlug: activeTrack,
|
|
595
738
|
phaseNumber: currentStatePhase.number
|
|
596
|
-
}
|
|
739
|
+
}, runMode, retryLimitHit
|
|
740
|
+
? {
|
|
741
|
+
autoContinuable: false,
|
|
742
|
+
pauseReason: 'auto_retry_limit'
|
|
743
|
+
}
|
|
744
|
+
: {});
|
|
597
745
|
}
|
|
598
746
|
|
|
599
747
|
if (currentStatePhase && trackState?.currentStep === 'execution') {
|
|
600
|
-
return {
|
|
748
|
+
return buildRouteResult({
|
|
601
749
|
kind: 'execute_phase',
|
|
602
|
-
command: '/autodev',
|
|
603
750
|
manualCommand: `/autodev-execute-phase ${currentStatePhase.number}`,
|
|
604
751
|
reason: 'phase_execution_in_progress',
|
|
605
752
|
projectType,
|
|
606
753
|
trackSlug: activeTrack,
|
|
607
754
|
phaseNumber: currentStatePhase.number
|
|
608
|
-
};
|
|
755
|
+
}, runMode);
|
|
609
756
|
}
|
|
610
757
|
|
|
611
758
|
if (currentStatePhase && trackState?.currentStep === 'task_review') {
|
|
612
|
-
|
|
759
|
+
if (runMode === 'auto') {
|
|
760
|
+
const snapshot = phaseTaskSnapshot(currentStatePhase);
|
|
761
|
+
if (snapshot.dependencyDeadlock) {
|
|
762
|
+
return buildRouteResult({
|
|
763
|
+
kind: 'plan_phase',
|
|
764
|
+
manualCommand: `/autodev-plan-phase ${currentStatePhase.number}`,
|
|
765
|
+
reason: 'dependency_deadlock_requires_replanning',
|
|
766
|
+
projectType,
|
|
767
|
+
trackSlug: activeTrack,
|
|
768
|
+
phaseNumber: currentStatePhase.number
|
|
769
|
+
}, runMode, {
|
|
770
|
+
autoContinuable: false,
|
|
771
|
+
pauseReason: 'dependency_deadlock'
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
if (currentStatePhase.summaryExists) {
|
|
776
|
+
return buildRouteResult({
|
|
777
|
+
kind: 'review_phase',
|
|
778
|
+
manualCommand: `/autodev-review-phase ${currentStatePhase.number}`,
|
|
779
|
+
reason: 'auto_skips_task_review_after_phase_summary',
|
|
780
|
+
projectType,
|
|
781
|
+
trackSlug: activeTrack,
|
|
782
|
+
phaseNumber: currentStatePhase.number
|
|
783
|
+
}, runMode);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
return buildRouteResult({
|
|
787
|
+
kind: 'execute_phase',
|
|
788
|
+
manualCommand: `/autodev-execute-phase ${currentStatePhase.number}`,
|
|
789
|
+
reason: snapshot.nextTask ? 'auto_skips_task_review_checkpoint' : 'phase_execution_in_progress',
|
|
790
|
+
projectType,
|
|
791
|
+
trackSlug: activeTrack,
|
|
792
|
+
phaseNumber: currentStatePhase.number
|
|
793
|
+
}, runMode);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
return buildRouteResult({
|
|
613
797
|
kind: 'task_review',
|
|
614
|
-
command: '/autodev',
|
|
615
798
|
manualCommand: `/autodev-review-task ${currentStatePhase.number}`,
|
|
616
799
|
reason: 'task_execution_checkpoint_pending_user_review',
|
|
617
800
|
projectType,
|
|
618
801
|
trackSlug: activeTrack,
|
|
619
802
|
phaseNumber: currentStatePhase.number
|
|
620
|
-
};
|
|
803
|
+
}, runMode);
|
|
621
804
|
}
|
|
622
805
|
|
|
623
806
|
if (currentStatePhase && trackState?.currentStep === 'plan_review') {
|
|
624
|
-
|
|
807
|
+
if (runMode === 'auto') {
|
|
808
|
+
return buildRouteResult({
|
|
809
|
+
kind: 'execute_phase',
|
|
810
|
+
manualCommand: `/autodev-execute-phase ${currentStatePhase.number}`,
|
|
811
|
+
reason: 'auto_skips_plan_review_checkpoint',
|
|
812
|
+
projectType,
|
|
813
|
+
trackSlug: activeTrack,
|
|
814
|
+
phaseNumber: currentStatePhase.number
|
|
815
|
+
}, runMode);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
return buildRouteResult({
|
|
625
819
|
kind: 'plan_review',
|
|
626
|
-
command: '/autodev',
|
|
627
820
|
manualCommand: `/autodev-execute-phase ${currentStatePhase.number}`,
|
|
628
821
|
reason: 'phase_plan_awaits_review',
|
|
629
822
|
projectType,
|
|
630
823
|
trackSlug: activeTrack,
|
|
631
824
|
phaseNumber: currentStatePhase.number
|
|
632
|
-
};
|
|
825
|
+
}, runMode);
|
|
633
826
|
}
|
|
634
827
|
|
|
635
828
|
if (currentStatePhase && trackState?.currentStep === 'review' && !currentStatePhase.reviewExists) {
|
|
636
|
-
return {
|
|
829
|
+
return buildRouteResult({
|
|
637
830
|
kind: 'review_phase',
|
|
638
|
-
command: '/autodev',
|
|
639
831
|
manualCommand: `/autodev-review-phase ${currentStatePhase.number}`,
|
|
640
832
|
reason: 'phase_review_in_progress',
|
|
641
833
|
projectType,
|
|
642
834
|
trackSlug: activeTrack,
|
|
643
835
|
phaseNumber: currentStatePhase.number
|
|
644
|
-
};
|
|
836
|
+
}, runMode);
|
|
645
837
|
}
|
|
646
838
|
|
|
647
839
|
if (currentStatePhase && trackState?.currentStep === 'verification' && !currentStatePhase.uatExists) {
|
|
648
|
-
return {
|
|
840
|
+
return buildRouteResult({
|
|
649
841
|
kind: 'verify_phase',
|
|
650
|
-
command: '/autodev',
|
|
651
842
|
manualCommand: `/autodev-verify-work ${currentStatePhase.number}`,
|
|
652
843
|
reason: 'phase_verification_in_progress',
|
|
653
844
|
projectType,
|
|
654
845
|
trackSlug: activeTrack,
|
|
655
846
|
phaseNumber: currentStatePhase.number
|
|
656
|
-
};
|
|
847
|
+
}, runMode);
|
|
657
848
|
}
|
|
658
849
|
|
|
659
850
|
const nextReview = phases.find(phase => phase.summaryExists && !phase.reviewExists);
|
|
660
851
|
if (nextReview && config.workflow.review_after_execute !== false) {
|
|
661
|
-
return {
|
|
852
|
+
return buildRouteResult({
|
|
662
853
|
kind: 'review_phase',
|
|
663
|
-
command: '/autodev',
|
|
664
854
|
manualCommand: `/autodev-review-phase ${nextReview.number}`,
|
|
665
855
|
reason: 'phase_executed_but_not_reviewed',
|
|
666
856
|
projectType,
|
|
667
857
|
trackSlug: activeTrack,
|
|
668
858
|
phaseNumber: nextReview.number
|
|
669
|
-
};
|
|
859
|
+
}, runMode);
|
|
670
860
|
}
|
|
671
861
|
|
|
672
862
|
const nextVerify = phases.find(phase => phase.reviewExists && !phase.uatExists);
|
|
673
863
|
if (nextVerify) {
|
|
674
|
-
return {
|
|
864
|
+
return buildRouteResult({
|
|
675
865
|
kind: 'verify_phase',
|
|
676
|
-
command: '/autodev',
|
|
677
866
|
manualCommand: `/autodev-verify-work ${nextVerify.number}`,
|
|
678
867
|
reason: 'phase_reviewed_but_not_verified',
|
|
679
868
|
projectType,
|
|
680
869
|
trackSlug: activeTrack,
|
|
681
870
|
phaseNumber: nextVerify.number
|
|
682
|
-
};
|
|
871
|
+
}, runMode);
|
|
683
872
|
}
|
|
684
873
|
|
|
685
874
|
const nextTaskReview = phases.find(phase => phase.planExists && !phase.summaryExists && phase.taskDoneCount > 0);
|
|
686
875
|
if (nextTaskReview) {
|
|
687
|
-
|
|
876
|
+
if (runMode === 'auto') {
|
|
877
|
+
const snapshot = phaseTaskSnapshot(nextTaskReview);
|
|
878
|
+
if (snapshot.dependencyDeadlock) {
|
|
879
|
+
return buildRouteResult({
|
|
880
|
+
kind: 'plan_phase',
|
|
881
|
+
manualCommand: `/autodev-plan-phase ${nextTaskReview.number}`,
|
|
882
|
+
reason: 'dependency_deadlock_requires_replanning',
|
|
883
|
+
projectType,
|
|
884
|
+
trackSlug: activeTrack,
|
|
885
|
+
phaseNumber: nextTaskReview.number
|
|
886
|
+
}, runMode, {
|
|
887
|
+
autoContinuable: false,
|
|
888
|
+
pauseReason: 'dependency_deadlock'
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
return buildRouteResult({
|
|
893
|
+
kind: 'execute_phase',
|
|
894
|
+
manualCommand: `/autodev-execute-phase ${nextTaskReview.number}`,
|
|
895
|
+
reason: 'auto_skips_task_review_checkpoint',
|
|
896
|
+
projectType,
|
|
897
|
+
trackSlug: activeTrack,
|
|
898
|
+
phaseNumber: nextTaskReview.number
|
|
899
|
+
}, runMode);
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
return buildRouteResult({
|
|
688
903
|
kind: 'task_review',
|
|
689
|
-
command: '/autodev',
|
|
690
904
|
manualCommand: `/autodev-review-task ${nextTaskReview.number}`,
|
|
691
905
|
reason: 'phase_task_completed_awaiting_user_review',
|
|
692
906
|
projectType,
|
|
693
907
|
trackSlug: activeTrack,
|
|
694
908
|
phaseNumber: nextTaskReview.number
|
|
695
|
-
};
|
|
909
|
+
}, runMode);
|
|
696
910
|
}
|
|
697
911
|
|
|
698
912
|
const nextPlannedReview = phases.find(phase => phase.planExists && !phase.summaryExists);
|
|
699
913
|
if (nextPlannedReview) {
|
|
700
|
-
|
|
914
|
+
if (runMode === 'auto') {
|
|
915
|
+
return buildRouteResult({
|
|
916
|
+
kind: 'execute_phase',
|
|
917
|
+
manualCommand: `/autodev-execute-phase ${nextPlannedReview.number}`,
|
|
918
|
+
reason: 'auto_skips_plan_review_checkpoint',
|
|
919
|
+
projectType,
|
|
920
|
+
trackSlug: activeTrack,
|
|
921
|
+
phaseNumber: nextPlannedReview.number
|
|
922
|
+
}, runMode);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
return buildRouteResult({
|
|
701
926
|
kind: 'plan_review',
|
|
702
|
-
command: '/autodev',
|
|
703
927
|
manualCommand: `/autodev-execute-phase ${nextPlannedReview.number}`,
|
|
704
928
|
reason: 'phase_planned_awaiting_user_review',
|
|
705
929
|
projectType,
|
|
706
930
|
trackSlug: activeTrack,
|
|
707
931
|
phaseNumber: nextPlannedReview.number
|
|
708
|
-
};
|
|
932
|
+
}, runMode);
|
|
709
933
|
}
|
|
710
934
|
|
|
711
935
|
const nextPlan = phases.find(phase => !phase.planExists);
|
|
712
936
|
if (nextPlan) {
|
|
713
|
-
return {
|
|
937
|
+
return buildRouteResult({
|
|
714
938
|
kind: 'plan_phase',
|
|
715
|
-
command: '/autodev',
|
|
716
939
|
manualCommand: `/autodev-plan-phase ${nextPlan.number}`,
|
|
717
940
|
reason: 'phase_not_planned',
|
|
718
941
|
projectType,
|
|
719
942
|
trackSlug: activeTrack,
|
|
720
943
|
phaseNumber: nextPlan.number
|
|
721
|
-
};
|
|
944
|
+
}, runMode);
|
|
722
945
|
}
|
|
723
946
|
|
|
724
|
-
return {
|
|
947
|
+
return buildRouteResult({
|
|
725
948
|
kind: 'track_complete',
|
|
726
|
-
command: '/autodev',
|
|
727
949
|
manualCommand: '/autodev-cleanup',
|
|
728
950
|
reason: 'active_track_fully_verified',
|
|
729
951
|
projectType,
|
|
730
952
|
trackSlug: activeTrack
|
|
731
|
-
}
|
|
953
|
+
}, runMode, {
|
|
954
|
+
autoContinuable: false
|
|
955
|
+
});
|
|
732
956
|
}
|
|
733
957
|
|
|
734
|
-
function buildProgress(cwd) {
|
|
958
|
+
function buildProgress(cwd, options = {}) {
|
|
959
|
+
const runMode = normalizeRunMode(options.runMode);
|
|
735
960
|
const paths = rootPaths(cwd);
|
|
736
961
|
const initialized = fileExists(paths.project);
|
|
737
962
|
const existingCodebase = detectExistingCodebase(cwd);
|
|
@@ -741,10 +966,11 @@ function buildProgress(cwd) {
|
|
|
741
966
|
const tracks = listTracks(cwd);
|
|
742
967
|
const phases = activeTrack ? listPhases(cwd, activeTrack) : [];
|
|
743
968
|
const codebaseMapExists = fileExists(paths.codebaseSummary);
|
|
744
|
-
const route = buildRoute(cwd);
|
|
969
|
+
const route = buildRoute(cwd, { runMode });
|
|
745
970
|
|
|
746
971
|
return {
|
|
747
972
|
initialized,
|
|
973
|
+
runMode,
|
|
748
974
|
projectType,
|
|
749
975
|
existingCodebase,
|
|
750
976
|
codebaseMapExists,
|
|
@@ -763,7 +989,9 @@ function buildProgress(cwd) {
|
|
|
763
989
|
},
|
|
764
990
|
route,
|
|
765
991
|
nextCommand: route.command,
|
|
766
|
-
manualNextCommand: route.manualCommand || null
|
|
992
|
+
manualNextCommand: route.manualCommand || null,
|
|
993
|
+
autoContinuable: route.auto_continuable,
|
|
994
|
+
pauseReason: route.pause_reason
|
|
767
995
|
};
|
|
768
996
|
}
|
|
769
997
|
|
|
@@ -775,6 +1003,7 @@ function renderProgressTable(progress) {
|
|
|
775
1003
|
const lines = [
|
|
776
1004
|
'Autodev Progress',
|
|
777
1005
|
'',
|
|
1006
|
+
`Run Mode: ${progress.runMode}`,
|
|
778
1007
|
`Project Type: ${progress.projectType}`,
|
|
779
1008
|
`Codebase Map: ${progress.codebaseMapExists ? 'ready' : 'missing'}`,
|
|
780
1009
|
`Tracks: ${progress.counts.trackCount}`,
|
|
@@ -788,6 +1017,8 @@ function renderProgressTable(progress) {
|
|
|
788
1017
|
`Task Summaries: ${progress.counts.tasksDone}`,
|
|
789
1018
|
`Next: ${progress.route.command}`,
|
|
790
1019
|
progress.route.manualCommand ? `Manual Shortcut: ${progress.route.manualCommand}` : null,
|
|
1020
|
+
progress.runMode === 'auto' ? `Auto Continue: ${progress.route.auto_continuable ? 'yes' : 'no'}` : null,
|
|
1021
|
+
progress.runMode === 'auto' ? `Pause Reason: ${progress.route.pause_reason || 'none'}` : null,
|
|
791
1022
|
'',
|
|
792
1023
|
'| Phase | Type | Name | Tasks | Done | Plan | Summary | Review | UAT | Status |',
|
|
793
1024
|
'| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |'
|
|
@@ -820,9 +1051,10 @@ function codebasePaths(cwd) {
|
|
|
820
1051
|
return Object.fromEntries(CODEBASE_FILES.map(name => [name.replace(/\.md$/, ''), path.join(codebaseDir, name)]));
|
|
821
1052
|
}
|
|
822
1053
|
|
|
823
|
-
function buildStatus(cwd) {
|
|
1054
|
+
function buildStatus(cwd, options = {}) {
|
|
1055
|
+
const runMode = normalizeRunMode(options.runMode);
|
|
824
1056
|
const paths = rootPaths(cwd);
|
|
825
|
-
const route = buildRoute(cwd);
|
|
1057
|
+
const route = buildRoute(cwd, { runMode });
|
|
826
1058
|
const config = loadConfig(cwd);
|
|
827
1059
|
const existingCodebase = detectExistingCodebase(cwd);
|
|
828
1060
|
const projectType = config.project.type || (existingCodebase ? 'brownfield' : 'greenfield');
|
|
@@ -831,6 +1063,7 @@ function buildStatus(cwd) {
|
|
|
831
1063
|
|
|
832
1064
|
return {
|
|
833
1065
|
cwd,
|
|
1066
|
+
run_mode: runMode,
|
|
834
1067
|
workspace_root: paths.workspaceRoot,
|
|
835
1068
|
autodev_exists: fileExists(paths.root),
|
|
836
1069
|
project_exists: fileExists(paths.project),
|
|
@@ -847,13 +1080,16 @@ function buildStatus(cwd) {
|
|
|
847
1080
|
active: track.active
|
|
848
1081
|
})),
|
|
849
1082
|
codebase_map_exists: fileExists(paths.codebaseSummary),
|
|
1083
|
+
auto_continuable: route.auto_continuable,
|
|
1084
|
+
pause_reason: route.pause_reason,
|
|
850
1085
|
route
|
|
851
1086
|
};
|
|
852
1087
|
}
|
|
853
1088
|
|
|
854
|
-
function buildCleanupPayload(cwd) {
|
|
1089
|
+
function buildCleanupPayload(cwd, options = {}) {
|
|
1090
|
+
const runMode = normalizeRunMode(options.runMode);
|
|
855
1091
|
const paths = rootPaths(cwd);
|
|
856
|
-
const route = buildRoute(cwd);
|
|
1092
|
+
const route = buildRoute(cwd, { runMode });
|
|
857
1093
|
const activeTrack = readActiveTrack(cwd);
|
|
858
1094
|
const track = trackPaths(cwd, activeTrack);
|
|
859
1095
|
const phases = activeTrack ? listPhases(cwd, activeTrack) : [];
|
|
@@ -862,6 +1098,7 @@ function buildCleanupPayload(cwd) {
|
|
|
862
1098
|
|
|
863
1099
|
return {
|
|
864
1100
|
cwd,
|
|
1101
|
+
run_mode: runMode,
|
|
865
1102
|
autodev_exists: fileExists(paths.root),
|
|
866
1103
|
project_exists: fileExists(paths.project),
|
|
867
1104
|
project_state_path: paths.state,
|
|
@@ -871,6 +1108,8 @@ function buildCleanupPayload(cwd) {
|
|
|
871
1108
|
track_state_path: track ? track.state : null,
|
|
872
1109
|
phases_dir: track ? track.phasesDir : null,
|
|
873
1110
|
archive_root: path.join(paths.root, 'archive'),
|
|
1111
|
+
auto_continuable: false,
|
|
1112
|
+
pause_reason: runMode === 'auto' ? 'cleanup_confirmation' : null,
|
|
874
1113
|
route,
|
|
875
1114
|
phase_counts: {
|
|
876
1115
|
total: phases.length,
|
|
@@ -882,10 +1121,11 @@ function buildCleanupPayload(cwd) {
|
|
|
882
1121
|
};
|
|
883
1122
|
}
|
|
884
1123
|
|
|
885
|
-
function initPayload(cwd, mode, requestedPhase) {
|
|
1124
|
+
function initPayload(cwd, mode, requestedPhase, options = {}) {
|
|
1125
|
+
const runMode = normalizeRunMode(options.runMode);
|
|
886
1126
|
const paths = rootPaths(cwd);
|
|
887
1127
|
const config = loadConfig(cwd);
|
|
888
|
-
const route = buildRoute(cwd);
|
|
1128
|
+
const route = buildRoute(cwd, { runMode });
|
|
889
1129
|
const existingCodebase = detectExistingCodebase(cwd);
|
|
890
1130
|
const projectType = config.project.type || (existingCodebase ? 'brownfield' : 'greenfield');
|
|
891
1131
|
const activeTrack = readActiveTrack(cwd);
|
|
@@ -921,6 +1161,7 @@ function initPayload(cwd, mode, requestedPhase) {
|
|
|
921
1161
|
|
|
922
1162
|
return {
|
|
923
1163
|
cwd,
|
|
1164
|
+
run_mode: runMode,
|
|
924
1165
|
autodev_exists: fileExists(paths.root),
|
|
925
1166
|
project_exists: fileExists(paths.project),
|
|
926
1167
|
existing_code_detected: existingCodebase,
|
|
@@ -947,6 +1188,8 @@ function initPayload(cwd, mode, requestedPhase) {
|
|
|
947
1188
|
track_state_path: track ? track.state : null,
|
|
948
1189
|
track_state: trackStateSnapshot,
|
|
949
1190
|
phases_dir: track ? track.phasesDir : null,
|
|
1191
|
+
auto_continuable: route.auto_continuable,
|
|
1192
|
+
pause_reason: route.pause_reason,
|
|
950
1193
|
route,
|
|
951
1194
|
phase_found: Boolean(phase),
|
|
952
1195
|
phase_number: phase ? phase.number : null,
|
|
@@ -995,7 +1238,9 @@ function printJson(value) {
|
|
|
995
1238
|
}
|
|
996
1239
|
|
|
997
1240
|
function main() {
|
|
998
|
-
const
|
|
1241
|
+
const rawArgs = process.argv.slice(2);
|
|
1242
|
+
const runMode = rawArgs.includes('--auto') ? 'auto' : DEFAULT_RUN_MODE;
|
|
1243
|
+
const args = rawArgs.filter(arg => arg !== '--auto');
|
|
999
1244
|
const cwd = process.cwd();
|
|
1000
1245
|
|
|
1001
1246
|
if (args.length === 0) {
|
|
@@ -1007,7 +1252,7 @@ function main() {
|
|
|
1007
1252
|
|
|
1008
1253
|
if (command === 'progress') {
|
|
1009
1254
|
const mode = rest[0] || 'table';
|
|
1010
|
-
const progress = buildProgress(cwd);
|
|
1255
|
+
const progress = buildProgress(cwd, { runMode });
|
|
1011
1256
|
if (mode === 'json') {
|
|
1012
1257
|
printJson(progress);
|
|
1013
1258
|
return;
|
|
@@ -1032,18 +1277,18 @@ function main() {
|
|
|
1032
1277
|
}
|
|
1033
1278
|
|
|
1034
1279
|
if (command === 'status') {
|
|
1035
|
-
printJson(buildStatus(cwd));
|
|
1280
|
+
printJson(buildStatus(cwd, { runMode }));
|
|
1036
1281
|
return;
|
|
1037
1282
|
}
|
|
1038
1283
|
|
|
1039
1284
|
if (command === 'cleanup') {
|
|
1040
|
-
printJson(buildCleanupPayload(cwd));
|
|
1285
|
+
printJson(buildCleanupPayload(cwd, { runMode }));
|
|
1041
1286
|
return;
|
|
1042
1287
|
}
|
|
1043
1288
|
|
|
1044
1289
|
if (command === 'route') {
|
|
1045
1290
|
const mode = rest[0] || 'json';
|
|
1046
|
-
const route = buildRoute(cwd);
|
|
1291
|
+
const route = buildRoute(cwd, { runMode });
|
|
1047
1292
|
if (mode === 'json') {
|
|
1048
1293
|
printJson(route);
|
|
1049
1294
|
return;
|
|
@@ -1059,7 +1304,7 @@ function main() {
|
|
|
1059
1304
|
process.stderr.write('autodev-tools: init mode required\n');
|
|
1060
1305
|
process.exit(1);
|
|
1061
1306
|
}
|
|
1062
|
-
printJson(initPayload(cwd, mode, phase));
|
|
1307
|
+
printJson(initPayload(cwd, mode, phase, { runMode }));
|
|
1063
1308
|
return;
|
|
1064
1309
|
}
|
|
1065
1310
|
|