rafcode 3.0.0 → 3.8.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.
Files changed (235) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/CLAUDE.md +0 -1
  3. package/RAF/38-dual-wielder/decisions.md +9 -0
  4. package/RAF/38-dual-wielder/input.md +6 -1
  5. package/RAF/38-dual-wielder/outcomes/8-e2e-test-codex-provider.md +139 -0
  6. package/RAF/38-dual-wielder/plans/8-e2e-test-codex-provider.md +95 -0
  7. package/RAF/39-pathless-rover/decisions.md +16 -0
  8. package/RAF/39-pathless-rover/input.md +2 -0
  9. package/RAF/39-pathless-rover/outcomes/1-fix-codex-stream-renderer.md +21 -0
  10. package/RAF/39-pathless-rover/outcomes/2-wire-provider-flag.md +28 -0
  11. package/RAF/39-pathless-rover/outcomes/3-remove-worktree-flag-do.md +41 -0
  12. package/RAF/39-pathless-rover/outcomes/4-remove-worktree-flag-plan-amend.md +30 -0
  13. package/RAF/39-pathless-rover/outcomes/5-update-prompts-and-docs.md +26 -0
  14. package/RAF/39-pathless-rover/plans/1-fix-codex-stream-renderer.md +43 -0
  15. package/RAF/39-pathless-rover/plans/2-wire-provider-flag.md +48 -0
  16. package/RAF/39-pathless-rover/plans/3-remove-worktree-flag-do.md +41 -0
  17. package/RAF/39-pathless-rover/plans/4-remove-worktree-flag-plan-amend.md +43 -0
  18. package/RAF/39-pathless-rover/plans/5-update-prompts-and-docs.md +31 -0
  19. package/RAF/40-numeric-order-fix/decisions.md +7 -0
  20. package/RAF/40-numeric-order-fix/input.md +19 -0
  21. package/RAF/40-numeric-order-fix/outcomes/1-fix-numeric-sort-order.md +18 -0
  22. package/RAF/40-numeric-order-fix/outcomes/2-add-npm-keywords.md +10 -0
  23. package/RAF/40-numeric-order-fix/plans/1-fix-numeric-sort-order.md +48 -0
  24. package/RAF/40-numeric-order-fix/plans/2-add-npm-keywords.md +23 -0
  25. package/RAF/41-echo-chamber/decisions.md +13 -0
  26. package/RAF/41-echo-chamber/input.md +4 -0
  27. package/RAF/41-echo-chamber/outcomes/1-update-codex-model-defaults.md +24 -0
  28. package/RAF/41-echo-chamber/outcomes/2-e2e-test-codex-provider.md +74 -0
  29. package/RAF/41-echo-chamber/plans/1-update-codex-model-defaults.md +28 -0
  30. package/RAF/41-echo-chamber/plans/2-e2e-test-codex-provider.md +103 -0
  31. package/RAF/42-patch-parade/decisions.md +29 -0
  32. package/RAF/42-patch-parade/input.md +9 -0
  33. package/RAF/42-patch-parade/outcomes/1-fix-codex-model-resolution.md +36 -0
  34. package/RAF/42-patch-parade/outcomes/2-fix-provider-aware-name-generation.md +31 -0
  35. package/RAF/42-patch-parade/outcomes/3-fix-codex-error-event-rendering.md +32 -0
  36. package/RAF/42-patch-parade/outcomes/4-update-cli-help-docs.md +28 -0
  37. package/RAF/42-patch-parade/outcomes/5-update-default-codex-models-to-gpt-5-4.md +33 -0
  38. package/RAF/42-patch-parade/outcomes/6-unify-model-config-schema.md +89 -0
  39. package/RAF/42-patch-parade/plans/1-fix-codex-model-resolution.md +35 -0
  40. package/RAF/42-patch-parade/plans/2-fix-provider-aware-name-generation.md +38 -0
  41. package/RAF/42-patch-parade/plans/3-fix-codex-error-event-rendering.md +32 -0
  42. package/RAF/42-patch-parade/plans/4-update-cli-help-docs.md +31 -0
  43. package/RAF/42-patch-parade/plans/5-update-default-codex-models-to-gpt-5-4.md +35 -0
  44. package/RAF/42-patch-parade/plans/6-unify-model-config-schema.md +46 -0
  45. package/RAF/43-swiss-army/decisions.md +34 -0
  46. package/RAF/43-swiss-army/input.md +7 -0
  47. package/RAF/43-swiss-army/outcomes/1-fix-model-validation.md +21 -0
  48. package/RAF/43-swiss-army/outcomes/2-update-commit-format.md +31 -0
  49. package/RAF/43-swiss-army/outcomes/3-wire-reasoning-effort.md +28 -0
  50. package/RAF/43-swiss-army/outcomes/4-remove-provider-flag.md +27 -0
  51. package/RAF/43-swiss-army/outcomes/5-config-wizard-validation.md +23 -0
  52. package/RAF/43-swiss-army/outcomes/6-add-fast-mode.md +32 -0
  53. package/RAF/43-swiss-army/outcomes/7-config-preset.md +31 -0
  54. package/RAF/43-swiss-army/plans/1-fix-model-validation.md +38 -0
  55. package/RAF/43-swiss-army/plans/2-update-commit-format.md +46 -0
  56. package/RAF/43-swiss-army/plans/3-wire-reasoning-effort.md +39 -0
  57. package/RAF/43-swiss-army/plans/4-remove-provider-flag.md +43 -0
  58. package/RAF/43-swiss-army/plans/5-config-wizard-validation.md +42 -0
  59. package/RAF/43-swiss-army/plans/6-add-fast-mode.md +46 -0
  60. package/RAF/43-swiss-army/plans/7-config-preset.md +51 -0
  61. package/RAF/44-config-api-change/decisions.md +22 -0
  62. package/RAF/44-config-api-change/input.md +5 -0
  63. package/RAF/44-config-api-change/outcomes/1-restructure-config-subcommands.md +19 -0
  64. package/RAF/44-config-api-change/outcomes/2-move-preset-under-config.md +17 -0
  65. package/RAF/44-config-api-change/outcomes/3-update-existing-tests-for-config-api.md +14 -0
  66. package/RAF/44-config-api-change/outcomes/4-update-config-command-docs.md +11 -0
  67. package/RAF/44-config-api-change/outcomes/5-fix-codex-name-generation.md +18 -0
  68. package/RAF/44-config-api-change/plans/1-restructure-config-subcommands.md +37 -0
  69. package/RAF/44-config-api-change/plans/2-move-preset-under-config.md +38 -0
  70. package/RAF/44-config-api-change/plans/3-update-existing-tests-for-config-api.md +38 -0
  71. package/RAF/44-config-api-change/plans/4-update-config-command-docs.md +36 -0
  72. package/RAF/44-config-api-change/plans/5-fix-codex-name-generation.md +49 -0
  73. package/RAF/45-signal-cairn/decisions.md +7 -0
  74. package/RAF/45-signal-cairn/input.md +2 -0
  75. package/RAF/45-signal-cairn/outcomes/1-rename-provider-to-harness.md +19 -0
  76. package/RAF/45-signal-cairn/outcomes/2-normalize-model-display-names.md +18 -0
  77. package/RAF/45-signal-cairn/plans/1-rename-provider-to-harness.md +40 -0
  78. package/RAF/45-signal-cairn/plans/2-normalize-model-display-names.md +41 -0
  79. package/RAF/45-signal-lantern/decisions.md +10 -0
  80. package/RAF/45-signal-lantern/input.md +2 -0
  81. package/RAF/45-signal-lantern/outcomes/1-add-effort-and-fast-to-do-model-display.md +15 -0
  82. package/RAF/45-signal-lantern/outcomes/2-capture-codex-post-run-token-usage.md +15 -0
  83. package/RAF/45-signal-lantern/outcomes/3-show-codex-token-summaries-without-fake-cost.md +14 -0
  84. package/RAF/45-signal-lantern/plans/1-add-effort-and-fast-to-do-model-display.md +38 -0
  85. package/RAF/45-signal-lantern/plans/2-capture-codex-post-run-token-usage.md +37 -0
  86. package/RAF/45-signal-lantern/plans/3-show-codex-token-summaries-without-fake-cost.md +40 -0
  87. package/RAF/46-lantern-arc/decisions.md +19 -0
  88. package/RAF/46-lantern-arc/input.md +6 -0
  89. package/RAF/46-lantern-arc/outcomes/1-remove-spark-alias.md +16 -0
  90. package/RAF/46-lantern-arc/outcomes/2-clean-up-worktree-plan-command.md +30 -0
  91. package/RAF/46-lantern-arc/outcomes/3-fix-token-usage-accumulation.md +32 -0
  92. package/RAF/46-lantern-arc/outcomes/4-display-effort-in-compact-mode.md +22 -0
  93. package/RAF/46-lantern-arc/outcomes/5-codex-fast-mode-research.md +38 -0
  94. package/RAF/46-lantern-arc/outcomes/6-optimize-llm-prompts.md +39 -0
  95. package/RAF/46-lantern-arc/plans/1-remove-spark-alias.md +38 -0
  96. package/RAF/46-lantern-arc/plans/2-clean-up-worktree-plan-command.md +33 -0
  97. package/RAF/46-lantern-arc/plans/3-fix-token-usage-accumulation.md +33 -0
  98. package/RAF/46-lantern-arc/plans/4-display-effort-in-compact-mode.md +28 -0
  99. package/RAF/46-lantern-arc/plans/5-codex-fast-mode-research.md +34 -0
  100. package/RAF/46-lantern-arc/plans/6-optimize-llm-prompts.md +48 -0
  101. package/RAF/47-signal-trim/decisions.md +13 -0
  102. package/RAF/47-signal-trim/input.md +2 -0
  103. package/RAF/47-signal-trim/plans/1-remove-cache-from-status.md +73 -0
  104. package/README.md +50 -63
  105. package/dist/commands/config.d.ts.map +1 -1
  106. package/dist/commands/config.js +47 -49
  107. package/dist/commands/config.js.map +1 -1
  108. package/dist/commands/do.d.ts +2 -0
  109. package/dist/commands/do.d.ts.map +1 -1
  110. package/dist/commands/do.js +91 -230
  111. package/dist/commands/do.js.map +1 -1
  112. package/dist/commands/plan.d.ts.map +1 -1
  113. package/dist/commands/plan.js +54 -259
  114. package/dist/commands/plan.js.map +1 -1
  115. package/dist/commands/preset.d.ts +3 -0
  116. package/dist/commands/preset.d.ts.map +1 -0
  117. package/dist/commands/preset.js +158 -0
  118. package/dist/commands/preset.js.map +1 -0
  119. package/dist/core/claude-runner.d.ts +2 -0
  120. package/dist/core/claude-runner.d.ts.map +1 -1
  121. package/dist/core/claude-runner.js +36 -12
  122. package/dist/core/claude-runner.js.map +1 -1
  123. package/dist/core/codex-runner.d.ts +1 -0
  124. package/dist/core/codex-runner.d.ts.map +1 -1
  125. package/dist/core/codex-runner.js +26 -7
  126. package/dist/core/codex-runner.js.map +1 -1
  127. package/dist/core/failure-analyzer.js +2 -1
  128. package/dist/core/failure-analyzer.js.map +1 -1
  129. package/dist/core/git.d.ts +2 -2
  130. package/dist/core/git.d.ts.map +1 -1
  131. package/dist/core/git.js +53 -3
  132. package/dist/core/git.js.map +1 -1
  133. package/dist/core/project-manager.d.ts.map +1 -1
  134. package/dist/core/project-manager.js +2 -2
  135. package/dist/core/project-manager.js.map +1 -1
  136. package/dist/core/pull-request.js +5 -5
  137. package/dist/core/pull-request.js.map +1 -1
  138. package/dist/core/runner-factory.d.ts +4 -4
  139. package/dist/core/runner-factory.d.ts.map +1 -1
  140. package/dist/core/runner-factory.js +8 -8
  141. package/dist/core/runner-factory.js.map +1 -1
  142. package/dist/core/runner-interface.d.ts +1 -1
  143. package/dist/core/runner-types.d.ts +17 -4
  144. package/dist/core/runner-types.d.ts.map +1 -1
  145. package/dist/core/state-derivation.js +3 -3
  146. package/dist/core/state-derivation.js.map +1 -1
  147. package/dist/parsers/codex-stream-renderer.d.ts +28 -4
  148. package/dist/parsers/codex-stream-renderer.d.ts.map +1 -1
  149. package/dist/parsers/codex-stream-renderer.js +110 -0
  150. package/dist/parsers/codex-stream-renderer.js.map +1 -1
  151. package/dist/prompts/amend.d.ts +0 -1
  152. package/dist/prompts/amend.d.ts.map +1 -1
  153. package/dist/prompts/amend.js +31 -104
  154. package/dist/prompts/amend.js.map +1 -1
  155. package/dist/prompts/execution.d.ts.map +1 -1
  156. package/dist/prompts/execution.js +17 -34
  157. package/dist/prompts/execution.js.map +1 -1
  158. package/dist/prompts/planning.d.ts.map +1 -1
  159. package/dist/prompts/planning.js +23 -123
  160. package/dist/prompts/planning.js.map +1 -1
  161. package/dist/types/config.d.ts +33 -32
  162. package/dist/types/config.d.ts.map +1 -1
  163. package/dist/types/config.js +14 -28
  164. package/dist/types/config.js.map +1 -1
  165. package/dist/utils/config.d.ts +36 -16
  166. package/dist/utils/config.d.ts.map +1 -1
  167. package/dist/utils/config.js +209 -104
  168. package/dist/utils/config.js.map +1 -1
  169. package/dist/utils/name-generator.d.ts.map +1 -1
  170. package/dist/utils/name-generator.js +25 -12
  171. package/dist/utils/name-generator.js.map +1 -1
  172. package/dist/utils/paths.d.ts +5 -0
  173. package/dist/utils/paths.d.ts.map +1 -1
  174. package/dist/utils/paths.js +9 -0
  175. package/dist/utils/paths.js.map +1 -1
  176. package/dist/utils/terminal-symbols.d.ts +15 -2
  177. package/dist/utils/terminal-symbols.d.ts.map +1 -1
  178. package/dist/utils/terminal-symbols.js +36 -4
  179. package/dist/utils/terminal-symbols.js.map +1 -1
  180. package/dist/utils/token-tracker.d.ts +6 -1
  181. package/dist/utils/token-tracker.d.ts.map +1 -1
  182. package/dist/utils/token-tracker.js +84 -51
  183. package/dist/utils/token-tracker.js.map +1 -1
  184. package/dist/utils/validation.d.ts +1 -2
  185. package/dist/utils/validation.d.ts.map +1 -1
  186. package/dist/utils/validation.js +4 -25
  187. package/dist/utils/validation.js.map +1 -1
  188. package/package.json +7 -2
  189. package/src/commands/config.ts +60 -63
  190. package/src/commands/do.ts +96 -262
  191. package/src/commands/plan.ts +55 -279
  192. package/src/commands/preset.ts +186 -0
  193. package/src/core/claude-runner.ts +45 -5
  194. package/src/core/codex-runner.ts +32 -7
  195. package/src/core/failure-analyzer.ts +2 -1
  196. package/src/core/git.ts +57 -3
  197. package/src/core/project-manager.ts +2 -1
  198. package/src/core/pull-request.ts +5 -5
  199. package/src/core/runner-factory.ts +9 -9
  200. package/src/core/runner-interface.ts +1 -1
  201. package/src/core/runner-types.ts +17 -4
  202. package/src/core/state-derivation.ts +3 -3
  203. package/src/parsers/codex-stream-renderer.ts +149 -4
  204. package/src/prompts/amend.ts +30 -105
  205. package/src/prompts/config-docs.md +206 -62
  206. package/src/prompts/execution.ts +17 -34
  207. package/src/prompts/planning.ts +23 -124
  208. package/src/types/config.ts +47 -59
  209. package/src/utils/config.ts +248 -115
  210. package/src/utils/name-generator.ts +29 -13
  211. package/src/utils/paths.ts +10 -0
  212. package/src/utils/terminal-symbols.ts +46 -6
  213. package/src/utils/token-tracker.ts +96 -57
  214. package/src/utils/validation.ts +5 -30
  215. package/tests/unit/amend-prompt.test.ts +3 -2
  216. package/tests/unit/claude-runner-interactive.test.ts +21 -3
  217. package/tests/unit/claude-runner.test.ts +39 -0
  218. package/tests/unit/codex-runner.test.ts +163 -0
  219. package/tests/unit/codex-stream-renderer.test.ts +127 -0
  220. package/tests/unit/command-output.test.ts +57 -0
  221. package/tests/unit/commit-planning-artifacts-worktree.test.ts +24 -7
  222. package/tests/unit/commit-planning-artifacts.test.ts +26 -4
  223. package/tests/unit/config-command.test.ts +215 -303
  224. package/tests/unit/config.test.ts +319 -235
  225. package/tests/unit/dependency-integration.test.ts +27 -1
  226. package/tests/unit/do-model-display.test.ts +35 -0
  227. package/tests/unit/execution-prompt.test.ts +49 -19
  228. package/tests/unit/name-generator.test.ts +82 -12
  229. package/tests/unit/plan-command-auto-flag.test.ts +7 -10
  230. package/tests/unit/plan-command.test.ts +14 -17
  231. package/tests/unit/planning-prompt.test.ts +9 -8
  232. package/tests/unit/terminal-symbols.test.ts +94 -3
  233. package/tests/unit/token-tracker.test.ts +180 -1
  234. package/tests/unit/validation.test.ts +9 -41
  235. package/tests/unit/worktree-flag-override.test.ts +0 -186
@@ -13,27 +13,23 @@ import {
13
13
  validateEnvironment,
14
14
  reportValidation,
15
15
  validateProjectName,
16
- resolveModelOption,
17
16
  } from '../utils/validation.js';
18
17
  import { logger } from '../utils/logger.js';
19
- import { getWorktreeDefault, getModel, getModelShortName, getSyncMainBranch } from '../utils/config.js';
18
+ import { formatModelDisplay, getModel } from '../utils/config.js';
19
+ import type { ModelEntry } from '../types/config.js';
20
20
  import { generateProjectNames } from '../utils/name-generator.js';
21
21
  import { pickProjectName } from '../ui/name-picker.js';
22
22
  import {
23
23
  getPlansDir,
24
24
  getRafDir,
25
- getNextProjectNumber,
26
- formatProjectNumber,
27
25
  resolveProjectIdentifierWithDetails,
28
26
  getInputPath,
29
- getDecisionsPath,
30
- getOutcomesDir,
31
27
  extractTaskNameFromPlanFile,
32
28
  decodeTaskId,
33
29
  encodeTaskId,
34
30
  TASK_ID_PATTERN,
31
+ numericFileSort,
35
32
  } from '../utils/paths.js';
36
- import { sanitizeProjectName } from '../utils/validation.js';
37
33
  import {
38
34
  deriveProjectState,
39
35
  isProjectComplete,
@@ -42,24 +38,14 @@ import {
42
38
  import {
43
39
  getRepoBasename,
44
40
  getRepoRoot,
45
- createWorktree,
46
- createWorktreeFromBranch,
47
- branchExists,
48
41
  validateWorktree,
49
- removeWorktree,
50
- computeWorktreeBaseDir,
51
- pullMainBranch,
52
42
  resolveWorktreeProjectByIdentifier,
53
43
  } from '../core/worktree.js';
54
44
 
55
45
  interface PlanCommandOptions {
56
46
  amend?: boolean;
57
- model?: string;
58
- sonnet?: boolean;
59
47
  auto?: boolean;
60
- worktree?: boolean;
61
48
  resume?: string;
62
- provider?: string;
63
49
  }
64
50
 
65
51
  export function createPlanCommand(): Command {
@@ -70,28 +56,15 @@ export function createPlanCommand(): Command {
70
56
  '-a, --amend',
71
57
  'Add tasks to an existing project (requires project identifier as argument)'
72
58
  )
73
- .option('-m, --model <name>', 'Model to use (sonnet, haiku, opus)')
74
- .option('--sonnet', 'Use Sonnet model (shorthand for --model sonnet)')
75
59
  .option('-y, --auto', 'Skip permission prompts for file operations')
76
- .option('-w, --worktree', 'Create a git worktree for isolated planning')
77
- .option('--no-worktree', 'Disable worktree mode (overrides config)')
78
60
  .option('-r, --resume <identifier>', 'Resume a planning session for an existing project')
79
- .option('-p, --provider <provider>', 'CLI provider to use (claude, codex)')
80
61
  .action(async (projectName: string | undefined, options: PlanCommandOptions) => {
81
- // Validate and resolve model option
82
- let model: string;
83
- try {
84
- model = resolveModelOption(options.model, options.sonnet, 'plan');
85
- } catch (error) {
86
- logger.error((error as Error).message);
87
- process.exit(1);
88
- }
62
+ const modelEntry = getModel('plan');
89
63
 
90
64
  const autoMode = options.auto ?? false;
91
- const worktreeMode = options.worktree ?? getWorktreeDefault();
92
65
 
93
66
  if (options.resume) {
94
- await runResumeCommand(options.resume, model);
67
+ await runResumeCommand(options.resume, modelEntry);
95
68
  } else if (options.amend) {
96
69
  if (!projectName) {
97
70
  logger.error('--amend requires a project identifier');
@@ -99,16 +72,16 @@ export function createPlanCommand(): Command {
99
72
  logger.error(' or: raf plan --amend <project>');
100
73
  process.exit(1);
101
74
  }
102
- await runAmendCommand(projectName, model, autoMode, worktreeMode);
75
+ await runAmendCommand(projectName, modelEntry, autoMode);
103
76
  } else {
104
- await runPlanCommand(projectName, model, autoMode, worktreeMode);
77
+ await runPlanCommand(projectName, modelEntry, autoMode);
105
78
  }
106
79
  });
107
80
 
108
81
  return command;
109
82
  }
110
83
 
111
- async function runPlanCommand(projectName?: string, model?: string, autoMode: boolean = false, worktreeMode: boolean = false): Promise<void> {
84
+ async function runPlanCommand(projectName?: string, modelEntry?: ModelEntry, autoMode: boolean = false): Promise<void> {
112
85
  // Validate environment
113
86
  const validation = validateEnvironment();
114
87
  reportValidation(validation);
@@ -124,7 +97,6 @@ async function runPlanCommand(projectName?: string, model?: string, autoMode: bo
124
97
  const mainResult = resolveProjectIdentifierWithDetails(rafDir, projectName);
125
98
 
126
99
  let existingFolder: string | null = null;
127
- let existingWorktreeMode = false;
128
100
 
129
101
  if (mainResult.path) {
130
102
  existingFolder = path.basename(mainResult.path);
@@ -134,7 +106,6 @@ async function runPlanCommand(projectName?: string, model?: string, autoMode: bo
134
106
  const wtResult = resolveWorktreeProjectByIdentifier(repoBasename, projectName);
135
107
  if (wtResult) {
136
108
  existingFolder = wtResult.folder;
137
- existingWorktreeMode = true;
138
109
  }
139
110
  }
140
111
  }
@@ -153,7 +124,7 @@ async function runPlanCommand(projectName?: string, model?: string, autoMode: bo
153
124
  });
154
125
 
155
126
  if (answer === 'amend') {
156
- await runAmendCommand(existingFolder, model, autoMode, existingWorktreeMode);
127
+ await runAmendCommand(existingFolder, modelEntry, autoMode);
157
128
  return;
158
129
  } else if (answer === 'cancel') {
159
130
  logger.info('Aborted.');
@@ -163,15 +134,6 @@ async function runPlanCommand(projectName?: string, model?: string, autoMode: bo
163
134
  }
164
135
  }
165
136
 
166
- // Validate git repo for worktree mode
167
- if (worktreeMode) {
168
- const repoRoot = getRepoRoot();
169
- if (!repoRoot) {
170
- logger.error('--worktree requires a git repository');
171
- process.exit(1);
172
- }
173
- }
174
-
175
137
  // Open editor for user input
176
138
  logger.info('Opening editor for project description...');
177
139
  logger.info('(Save and close the editor when done)');
@@ -199,7 +161,8 @@ async function runPlanCommand(projectName?: string, model?: string, autoMode: bo
199
161
  // Get or generate project name
200
162
  let finalProjectName = projectName;
201
163
  if (!finalProjectName) {
202
- const nameModel = getModelShortName(getModel('nameGeneration'));
164
+ const nameEntry = getModel('nameGeneration');
165
+ const nameModel = formatModelDisplay(nameEntry.model);
203
166
  logger.info(`Generating project name suggestions with ${nameModel}...`);
204
167
  const suggestedNames = await generateProjectNames(cleanInput);
205
168
  logger.newline();
@@ -219,103 +182,32 @@ async function runPlanCommand(projectName?: string, model?: string, autoMode: bo
219
182
  process.exit(1);
220
183
  }
221
184
 
222
- let projectPath: string;
223
- let worktreePath: string | null = null;
224
- let worktreeBranch: string | null = null;
225
-
226
- if (worktreeMode) {
227
- // Worktree mode: create worktree, then project folder inside it
228
- const repoBasename = getRepoBasename()!;
229
- const repoRoot = getRepoRoot()!;
230
- const rafDir = getRafDir();
231
-
232
- // Sync main branch before creating worktree (if enabled)
233
- if (getSyncMainBranch()) {
234
- const syncResult = pullMainBranch();
235
- if (syncResult.success) {
236
- if (syncResult.hadChanges) {
237
- logger.info(`Synced ${syncResult.mainBranch} from remote`);
238
- }
239
- } else {
240
- logger.warn(`Could not sync main branch: ${syncResult.error}`);
241
- }
242
- }
243
-
244
- // Compute project number from main repo's RAF directory (scan worktrees too)
245
- const projectNumber = getNextProjectNumber(rafDir, repoBasename);
246
- const sanitizedName = sanitizeProjectName(finalProjectName);
247
- const folderName = `${formatProjectNumber(projectNumber)}-${sanitizedName}`;
248
-
249
- // Create worktree
250
- const result = createWorktree(repoBasename, folderName);
251
- if (!result.success) {
252
- logger.error(`Failed to create worktree: ${result.error}`);
253
- process.exit(1);
254
- }
255
-
256
- worktreePath = result.worktreePath;
257
- worktreeBranch = result.branch;
185
+ // Standard mode: create project in main repo
186
+ const projectManager = new ProjectManager();
187
+ const projectPath = projectManager.createProject(finalProjectName);
258
188
 
259
- // Compute project path inside worktree at the same relative path
260
- const rafRelativePath = path.relative(repoRoot, rafDir);
261
- projectPath = path.join(worktreePath, rafRelativePath, folderName);
262
-
263
- // Create project folder structure inside the worktree
264
- fs.mkdirSync(projectPath, { recursive: true });
265
- fs.mkdirSync(getPlansDir(projectPath), { recursive: true });
266
- fs.mkdirSync(getOutcomesDir(projectPath), { recursive: true });
267
- fs.writeFileSync(getDecisionsPath(projectPath), '# Project Decisions\n');
268
-
269
- // Save input inside worktree project folder
270
- fs.writeFileSync(getInputPath(projectPath), userInput);
271
-
272
- logger.success(`Created worktree: ${worktreePath}`);
273
- logger.success(`Branch: ${worktreeBranch}`);
274
- logger.success(`Project: ${projectPath}`);
275
- logger.newline();
276
- } else {
277
- // Standard mode: create project in main repo
278
- const projectManager = new ProjectManager();
279
- projectPath = projectManager.createProject(finalProjectName);
280
-
281
- logger.success(`Created project: ${projectPath}`);
282
- logger.newline();
189
+ logger.success(`Created project: ${projectPath}`);
190
+ logger.newline();
283
191
 
284
- // Save input
285
- projectManager.saveInput(projectPath, userInput);
286
- }
192
+ // Save input
193
+ projectManager.saveInput(projectPath, userInput);
287
194
 
288
195
  // Set up shutdown handler
289
- const claudeRunner = createRunner({ model });
196
+ const claudeRunner = createRunner({ model: modelEntry?.model, harness: modelEntry?.harness, reasoningEffort: modelEntry?.reasoningEffort, fast: modelEntry?.fast });
290
197
  shutdownHandler.init();
291
198
  shutdownHandler.registerClaudeRunner(claudeRunner);
292
199
 
293
200
  // Register cleanup callback
294
201
  shutdownHandler.onShutdown(() => {
295
- if (worktreeMode && worktreePath) {
296
- // Clean up worktree if no plans were created
297
- const plansDir = getPlansDir(projectPath);
298
- const hasPlanFiles = fs.existsSync(plansDir) &&
299
- fs.readdirSync(plansDir).filter((f) => f.endsWith('.md')).length > 0;
300
- if (!hasPlanFiles) {
301
- const removal = removeWorktree(worktreePath);
302
- if (removal.success) {
303
- logger.debug(`Cleaned up worktree: ${worktreePath}`);
304
- } else {
305
- logger.warn(`Failed to clean up worktree: ${removal.error}`);
306
- }
307
- }
308
- } else {
309
- const projectManager = new ProjectManager();
310
- projectManager.cleanupEmptyProject(projectPath);
311
- }
202
+ const projectManager = new ProjectManager();
203
+ projectManager.cleanupEmptyProject(projectPath);
312
204
  });
313
205
 
314
206
  // Run planning session
315
207
  logger.info('Starting planning session...');
316
208
  logger.info('The planner will interview you about each task.');
317
- if (model) {
318
- logger.info(`Using model: ${model}`);
209
+ if (modelEntry) {
210
+ logger.info(`Using model: ${formatModelDisplay(modelEntry.model, modelEntry.harness, { includeHarness: true })}`);
319
211
  }
320
212
  if (autoMode) {
321
213
  logger.warn('Auto mode enabled: permission prompts will be skipped.');
@@ -325,14 +217,11 @@ async function runPlanCommand(projectName?: string, model?: string, autoMode: bo
325
217
  const { systemPrompt, userMessage } = getPlanningPrompt({
326
218
  projectPath,
327
219
  inputContent: userInput,
328
- worktreeMode,
329
220
  });
330
221
 
331
222
  try {
332
223
  const exitCode = await claudeRunner.runInteractive(systemPrompt, userMessage, {
333
224
  dangerouslySkipPermissions: autoMode,
334
- // Run session in the worktree root if in worktree mode
335
- cwd: worktreePath ?? undefined,
336
225
  });
337
226
 
338
227
  if (exitCode !== 0) {
@@ -342,7 +231,7 @@ async function runPlanCommand(projectName?: string, model?: string, autoMode: bo
342
231
  // Check for created plan files
343
232
  const plansDir = getPlansDir(projectPath);
344
233
  const planFiles = fs.existsSync(plansDir)
345
- ? fs.readdirSync(plansDir).filter((f) => f.endsWith('.md')).sort()
234
+ ? fs.readdirSync(plansDir).filter((f) => f.endsWith('.md')).sort(numericFileSort)
346
235
  : [];
347
236
 
348
237
  if (planFiles.length === 0) {
@@ -359,45 +248,23 @@ async function runPlanCommand(projectName?: string, model?: string, autoMode: bo
359
248
  // Commit planning artifacts (input.md, decisions.md, and plan files)
360
249
  const planAbsolutePaths = planFiles.map((f) => path.join(plansDir, f));
361
250
  await commitPlanningArtifacts(projectPath, {
362
- cwd: worktreePath ?? undefined,
363
251
  additionalFiles: planAbsolutePaths,
364
252
  });
365
253
 
366
254
  logger.newline();
367
- if (worktreeMode) {
368
- logger.info(`Worktree: ${worktreePath}`);
369
- logger.info(`Branch: ${worktreeBranch}`);
370
- logger.info(`Run 'raf do ${finalProjectName} --worktree' to execute the plans.`);
371
- } else {
372
- logger.info(`Run 'raf do ${finalProjectName}' to execute the plans.`);
373
- }
255
+ logger.info(`Run 'raf do ${finalProjectName}' to execute the plans.`);
374
256
  }
375
257
  } catch (error) {
376
258
  logger.error(`Planning failed: ${error}`);
377
259
  throw error;
378
260
  } finally {
379
- if (worktreeMode && worktreePath) {
380
- // Clean up worktree if no plans were created
381
- const plansDir = getPlansDir(projectPath);
382
- const hasPlanFiles = fs.existsSync(plansDir) &&
383
- fs.readdirSync(plansDir).filter((f) => f.endsWith('.md')).length > 0;
384
- if (!hasPlanFiles) {
385
- const removal = removeWorktree(worktreePath);
386
- if (removal.success) {
387
- logger.debug(`Cleaned up worktree: ${worktreePath}`);
388
- } else {
389
- logger.warn(`Failed to clean up worktree: ${removal.error}`);
390
- }
391
- }
392
- } else if (!worktreeMode) {
393
- // Cleanup empty project folder if no plans were created (standard mode only)
394
- const projectManager = new ProjectManager();
395
- projectManager.cleanupEmptyProject(projectPath);
396
- }
261
+ // Cleanup empty project folder if no plans were created
262
+ const projectManager = new ProjectManager();
263
+ projectManager.cleanupEmptyProject(projectPath);
397
264
  }
398
265
  }
399
266
 
400
- async function runAmendCommand(identifier: string, model?: string, autoMode: boolean = false, worktreeMode: boolean = false): Promise<void> {
267
+ async function runAmendCommand(identifier: string, modelEntry?: ModelEntry, autoMode: boolean = false): Promise<void> {
401
268
  // Validate environment
402
269
  const validation = validateEnvironment();
403
270
  reportValidation(validation);
@@ -406,112 +273,26 @@ async function runAmendCommand(identifier: string, model?: string, autoMode: boo
406
273
  process.exit(1);
407
274
  }
408
275
 
276
+ // Auto-detect project location: check worktrees first, then main repo
409
277
  let worktreePath: string | null = null;
410
- let projectPath: string;
411
-
412
- if (worktreeMode) {
413
- // Worktree mode: resolve project from worktree directory
414
- const repoBasename = getRepoBasename();
415
- if (!repoBasename) {
416
- logger.error('--worktree requires a git repository');
417
- process.exit(1);
418
- }
419
-
420
- const repoRoot = getRepoRoot()!;
421
- const rafDir = getRafDir();
422
- const rafRelativePath = path.relative(repoRoot, rafDir);
423
-
424
- const worktreeBaseDir = computeWorktreeBaseDir(repoBasename);
425
-
426
- // Search through existing worktree directories for the project
427
- let matchedWorktreeDir: string | null = null;
428
- let matchedProjectPath: string | null = null;
429
-
430
- if (fs.existsSync(worktreeBaseDir)) {
431
- const entries = fs.readdirSync(worktreeBaseDir, { withFileTypes: true });
432
-
433
- for (const entry of entries) {
434
- if (!entry.isDirectory()) continue;
435
- const wtPath = path.join(worktreeBaseDir, entry.name);
436
- const wtRafDir = path.join(wtPath, rafRelativePath);
437
- if (!fs.existsSync(wtRafDir)) continue;
438
-
439
- const resolution = resolveProjectIdentifierWithDetails(wtRafDir, identifier);
440
- if (resolution.path) {
441
- if (matchedWorktreeDir) {
442
- logger.error(`Ambiguous: project "${identifier}" found in multiple worktrees`);
443
- process.exit(1);
444
- }
445
- matchedWorktreeDir = wtPath;
446
- matchedProjectPath = resolution.path;
447
- }
448
- }
449
- }
450
-
451
- if (!matchedWorktreeDir || !matchedProjectPath) {
452
- // Worktree not found — try to recreate it
453
- // First, resolve the project from the main repo to get the folder name
454
- const mainResolution = resolveProjectIdentifierWithDetails(rafDir, identifier);
455
- if (!mainResolution.path) {
456
- logger.error(`Project not found in any worktree or main repo: ${identifier}`);
457
- logger.error(`Searched in: ${worktreeBaseDir}`);
458
- process.exit(1);
459
- }
460
-
461
- const folderName = path.basename(mainResolution.path);
278
+ let projectPath: string | undefined;
462
279
 
463
- if (branchExists(folderName)) {
464
- // Branch exists — recreate worktree from it
465
- const result = createWorktreeFromBranch(repoBasename, folderName);
466
- if (!result.success) {
467
- logger.error(`Failed to recreate worktree from branch: ${result.error}`);
468
- process.exit(1);
469
- }
470
- matchedWorktreeDir = result.worktreePath;
471
- matchedProjectPath = path.join(result.worktreePath, rafRelativePath, folderName);
472
- logger.info(`Recreated worktree from branch: ${folderName}`);
473
- } else {
474
- // No branch — create fresh worktree and copy project files
475
- // Sync main branch before creating worktree (if enabled)
476
- if (getSyncMainBranch()) {
477
- const syncResult = pullMainBranch();
478
- if (syncResult.success) {
479
- if (syncResult.hadChanges) {
480
- logger.info(`Synced ${syncResult.mainBranch} from remote`);
481
- }
482
- } else {
483
- logger.warn(`Could not sync main branch: ${syncResult.error}`);
484
- }
485
- }
280
+ const repoBasename = getRepoBasename();
281
+ const rafDir = getRafDir();
486
282
 
487
- const result = createWorktree(repoBasename, folderName);
488
- if (!result.success) {
489
- logger.error(`Failed to create worktree: ${result.error}`);
490
- process.exit(1);
491
- }
492
- matchedWorktreeDir = result.worktreePath;
493
- const wtProjectPath = path.join(result.worktreePath, rafRelativePath, folderName);
494
- // Copy project folder from main repo into the new worktree
495
- fs.cpSync(mainResolution.path, wtProjectPath, { recursive: true });
496
- matchedProjectPath = wtProjectPath;
497
- logger.info(`Created fresh worktree and copied project files: ${folderName}`);
498
- }
283
+ // 1. Try worktree resolution first (if we're in a git repo)
284
+ if (repoBasename) {
285
+ const wtResult = resolveWorktreeProjectByIdentifier(repoBasename, identifier);
286
+ if (wtResult) {
287
+ const repoRoot = getRepoRoot()!;
288
+ const rafRelativePath = path.relative(repoRoot, rafDir);
289
+ worktreePath = wtResult.worktreeRoot;
290
+ projectPath = path.join(wtResult.worktreeRoot, rafRelativePath, wtResult.folder);
499
291
  }
292
+ }
500
293
 
501
- worktreePath = matchedWorktreeDir;
502
- projectPath = matchedProjectPath;
503
-
504
- // Validate the worktree is valid
505
- const relProjectPath = path.relative(worktreePath, projectPath);
506
- const wtValidation = validateWorktree(worktreePath, relProjectPath);
507
- if (!wtValidation.isValidWorktree) {
508
- logger.error(`Invalid worktree at: ${worktreePath}`);
509
- logger.error('The worktree may have been removed or corrupted.');
510
- process.exit(1);
511
- }
512
- } else {
513
- // Standard mode: resolve from main repo
514
- const rafDir = getRafDir();
294
+ // 2. Fall back to main repo
295
+ if (!projectPath) {
515
296
  const resolution = resolveProjectIdentifierWithDetails(rafDir, identifier);
516
297
 
517
298
  if (!resolution.path) {
@@ -570,7 +351,7 @@ async function runAmendCommand(identifier: string, model?: string, autoMode: boo
570
351
  // Show existing tasks summary
571
352
  logger.info('Amending existing project:');
572
353
  logger.info(` Path: ${projectPath}`);
573
- if (worktreeMode && worktreePath) {
354
+ if (worktreePath) {
574
355
  logger.info(` Worktree: ${worktreePath}`);
575
356
  }
576
357
  logger.info(` Existing tasks: ${existingTasks.length}`);
@@ -616,15 +397,15 @@ async function runAmendCommand(identifier: string, model?: string, autoMode: boo
616
397
  fs.writeFileSync(inputPath, updatedInput);
617
398
 
618
399
  // Set up shutdown handler
619
- const claudeRunner = createRunner({ model });
400
+ const claudeRunner = createRunner({ model: modelEntry?.model, harness: modelEntry?.harness, reasoningEffort: modelEntry?.reasoningEffort, fast: modelEntry?.fast });
620
401
  shutdownHandler.init();
621
402
  shutdownHandler.registerClaudeRunner(claudeRunner);
622
403
 
623
404
  // Run amend planning session
624
405
  logger.info('Starting amendment session...');
625
406
  logger.info('The planner will interview you about each new task.');
626
- if (model) {
627
- logger.info(`Using model: ${model}`);
407
+ if (modelEntry) {
408
+ logger.info(`Using model: ${formatModelDisplay(modelEntry.model, modelEntry.harness, { includeHarness: true })}`);
628
409
  }
629
410
  if (autoMode) {
630
411
  logger.warn('Auto mode enabled: permission prompts will be skipped.');
@@ -636,7 +417,6 @@ async function runAmendCommand(identifier: string, model?: string, autoMode: boo
636
417
  existingTasks,
637
418
  nextTaskNumber,
638
419
  newTaskDescription: cleanInput,
639
- worktreeMode,
640
420
  });
641
421
 
642
422
  try {
@@ -652,7 +432,7 @@ async function runAmendCommand(identifier: string, model?: string, autoMode: boo
652
432
 
653
433
  // Check for new plan files
654
434
  const allPlanFiles = fs.existsSync(plansDir)
655
- ? fs.readdirSync(plansDir).filter((f) => f.endsWith('.md')).sort()
435
+ ? fs.readdirSync(plansDir).filter((f) => f.endsWith('.md')).sort(numericFileSort)
656
436
  : [];
657
437
 
658
438
  const newPlanFiles = allPlanFiles.filter((f) => {
@@ -687,11 +467,7 @@ async function runAmendCommand(identifier: string, model?: string, autoMode: boo
687
467
  if (newPlanFiles.length > 0) {
688
468
  logger.newline();
689
469
  logger.info(`Total tasks: ${allPlanFiles.length}`);
690
- if (worktreeMode) {
691
- logger.info(`Run 'raf do ${identifier} --worktree' to execute the new tasks.`);
692
- } else {
693
- logger.info(`Run 'raf do ${identifier}' to execute the new tasks.`);
694
- }
470
+ logger.info(`Run 'raf do ${identifier}' to execute the new tasks.`);
695
471
  }
696
472
  } catch (error) {
697
473
  logger.error(`Amendment failed: ${error}`);
@@ -729,7 +505,7 @@ ${taskList}
729
505
  `;
730
506
  }
731
507
 
732
- async function runResumeCommand(identifier: string, model?: string): Promise<void> {
508
+ async function runResumeCommand(identifier: string, modelEntry?: ModelEntry): Promise<void> {
733
509
  // Validate environment
734
510
  const validation = validateEnvironment();
735
511
  reportValidation(validation);
@@ -793,13 +569,13 @@ async function runResumeCommand(identifier: string, model?: string): Promise<voi
793
569
  }
794
570
 
795
571
  logger.info(`Project: ${folderName}`);
796
- if (model) {
797
- logger.info(`Model: ${model}`);
572
+ if (modelEntry) {
573
+ logger.info(`Model: ${formatModelDisplay(modelEntry.model, modelEntry.harness, { includeHarness: true })}`);
798
574
  }
799
575
  logger.newline();
800
576
 
801
577
  // Set up shutdown handler
802
- const claudeRunner = createRunner({ model });
578
+ const claudeRunner = createRunner({ model: modelEntry?.model, harness: modelEntry?.harness, reasoningEffort: modelEntry?.reasoningEffort, fast: modelEntry?.fast });
803
579
  shutdownHandler.init();
804
580
  shutdownHandler.registerClaudeRunner(claudeRunner);
805
581
 
@@ -817,7 +593,7 @@ async function runResumeCommand(identifier: string, model?: string): Promise<voi
817
593
  // Check for created/updated plan files after resume session
818
594
  const plansDir = getPlansDir(projectPath);
819
595
  const planFiles = fs.existsSync(plansDir)
820
- ? fs.readdirSync(plansDir).filter((f) => f.endsWith('.md')).sort()
596
+ ? fs.readdirSync(plansDir).filter((f) => f.endsWith('.md')).sort(numericFileSort)
821
597
  : [];
822
598
 
823
599
  if (planFiles.length > 0) {