@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.
@@ -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
- return entry.isDirectory() || /\.(js|ts|tsx|jsx|py|go|rs|java|rb|php|cs|cpp|c|swift|kt|sql|json|yaml|yml|md)$/.test(entry.name);
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
- return {
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
- return {
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
- return {
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
- return {
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
- return {
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 args = process.argv.slice(2);
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