rafcode 3.0.0 → 3.2.1

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 (65) hide show
  1. package/RAF/38-dual-wielder/decisions.md +9 -0
  2. package/RAF/38-dual-wielder/input.md +6 -1
  3. package/RAF/38-dual-wielder/outcomes/8-e2e-test-codex-provider.md +139 -0
  4. package/RAF/38-dual-wielder/plans/8-e2e-test-codex-provider.md +95 -0
  5. package/RAF/39-pathless-rover/decisions.md +16 -0
  6. package/RAF/39-pathless-rover/input.md +2 -0
  7. package/RAF/39-pathless-rover/outcomes/1-fix-codex-stream-renderer.md +21 -0
  8. package/RAF/39-pathless-rover/outcomes/2-wire-provider-flag.md +28 -0
  9. package/RAF/39-pathless-rover/outcomes/3-remove-worktree-flag-do.md +41 -0
  10. package/RAF/39-pathless-rover/outcomes/4-remove-worktree-flag-plan-amend.md +30 -0
  11. package/RAF/39-pathless-rover/outcomes/5-update-prompts-and-docs.md +26 -0
  12. package/RAF/39-pathless-rover/plans/1-fix-codex-stream-renderer.md +43 -0
  13. package/RAF/39-pathless-rover/plans/2-wire-provider-flag.md +48 -0
  14. package/RAF/39-pathless-rover/plans/3-remove-worktree-flag-do.md +41 -0
  15. package/RAF/39-pathless-rover/plans/4-remove-worktree-flag-plan-amend.md +43 -0
  16. package/RAF/39-pathless-rover/plans/5-update-prompts-and-docs.md +31 -0
  17. package/RAF/40-numeric-order-fix/decisions.md +7 -0
  18. package/RAF/40-numeric-order-fix/input.md +19 -0
  19. package/RAF/40-numeric-order-fix/outcomes/1-fix-numeric-sort-order.md +18 -0
  20. package/RAF/40-numeric-order-fix/outcomes/2-add-npm-keywords.md +10 -0
  21. package/RAF/40-numeric-order-fix/plans/1-fix-numeric-sort-order.md +48 -0
  22. package/RAF/40-numeric-order-fix/plans/2-add-npm-keywords.md +23 -0
  23. package/README.md +5 -8
  24. package/dist/commands/do.d.ts.map +1 -1
  25. package/dist/commands/do.js +41 -193
  26. package/dist/commands/do.js.map +1 -1
  27. package/dist/commands/plan.d.ts.map +1 -1
  28. package/dist/commands/plan.js +32 -120
  29. package/dist/commands/plan.js.map +1 -1
  30. package/dist/core/project-manager.d.ts.map +1 -1
  31. package/dist/core/project-manager.js +2 -2
  32. package/dist/core/project-manager.js.map +1 -1
  33. package/dist/core/pull-request.js +2 -2
  34. package/dist/core/pull-request.js.map +1 -1
  35. package/dist/core/state-derivation.js +3 -3
  36. package/dist/core/state-derivation.js.map +1 -1
  37. package/dist/parsers/codex-stream-renderer.d.ts +21 -4
  38. package/dist/parsers/codex-stream-renderer.d.ts.map +1 -1
  39. package/dist/parsers/codex-stream-renderer.js +77 -0
  40. package/dist/parsers/codex-stream-renderer.js.map +1 -1
  41. package/dist/prompts/amend.d.ts +0 -1
  42. package/dist/prompts/amend.d.ts.map +1 -1
  43. package/dist/prompts/amend.js +2 -3
  44. package/dist/prompts/amend.js.map +1 -1
  45. package/dist/prompts/planning.d.ts.map +1 -1
  46. package/dist/prompts/planning.js +2 -3
  47. package/dist/prompts/planning.js.map +1 -1
  48. package/dist/types/config.d.ts +0 -1
  49. package/dist/types/config.d.ts.map +1 -1
  50. package/dist/utils/paths.d.ts +5 -0
  51. package/dist/utils/paths.d.ts.map +1 -1
  52. package/dist/utils/paths.js +9 -0
  53. package/dist/utils/paths.js.map +1 -1
  54. package/package.json +7 -2
  55. package/src/commands/do.ts +42 -220
  56. package/src/commands/plan.ts +34 -127
  57. package/src/core/project-manager.ts +2 -1
  58. package/src/core/pull-request.ts +2 -2
  59. package/src/core/state-derivation.ts +3 -3
  60. package/src/parsers/codex-stream-renderer.ts +106 -4
  61. package/src/prompts/amend.ts +1 -4
  62. package/src/prompts/config-docs.md +1 -1
  63. package/src/prompts/planning.ts +2 -4
  64. package/src/types/config.ts +0 -1
  65. package/src/utils/paths.ts +10 -0
@@ -0,0 +1,31 @@
1
+ ---
2
+ effort: low
3
+ ---
4
+ # Task: Update Prompts, Docs, and Config Docs for Removed --worktree Flag
5
+
6
+ ## Objective
7
+ Update all user-facing text (prompts, help text, docs, config documentation) to reflect that `--worktree` is no longer needed for `raf do` and `raf plan --amend`.
8
+
9
+ ## Dependencies
10
+ 3, 4
11
+
12
+ ## Requirements
13
+ - Update planning prompt output that suggests `raf do <project> --worktree` → just `raf do <project>`
14
+ - Update amend prompt output if it references `--worktree`
15
+ - Update config-docs.md if it documents the `--worktree` flag behavior for `do` or `amend`
16
+ - Update README.md CLI usage sections
17
+ - Remove any references to `--worktree` in error messages that were in `do.ts`
18
+
19
+ ## Implementation Steps
20
+ 1. Read `src/prompts/planning.ts` — update the exit message that suggests `--worktree`
21
+ 2. Read `src/prompts/amend.ts` — update if it references `--worktree`
22
+ 3. Read `src/prompts/config-docs.md` — update worktree config documentation
23
+ 4. Read `README.md` — update CLI usage for `raf do` and `raf plan`
24
+ 5. Grep for any remaining `--worktree` references across the codebase and update as needed
25
+
26
+ ## Acceptance Criteria
27
+ - [ ] No prompts suggest `--worktree` for `raf do`
28
+ - [ ] No prompts suggest `--worktree` for `raf plan --amend`
29
+ - [ ] README accurately reflects current CLI flags
30
+ - [ ] Config docs accurately describe worktree behavior
31
+ - [ ] No stale `--worktree` references in codebase (except for `raf plan` new project creation where it's still valid)
@@ -0,0 +1,7 @@
1
+ # Project Decisions
2
+
3
+ ## Should the fix be a shared helper function or inline fixes at each call site?
4
+ Shared helper function — create one numeric sort utility and use it everywhere for DRY and consistency.
5
+
6
+ ## Which Codex-related keywords should be added to package.json?
7
+ codex, openai-codex, claude-code, agentic-coding, coding-agent — broader set including agentic coding terms.
@@ -0,0 +1,19 @@
1
+ - [ ] raf do executes task in lexigraphical order (1 then 10). should execute in order of numbers **➜**  **BindNotes** **git:(****main****)** raf do 
2
+
3
+ ✔ **Select a project to execute:** 14 mosaic-drift (0/19 tasks) [worktree]
4
+
5
+ ✔ **After tasks complete, what should happen with branch "14-mosaic-drift"?** Merge into current branch
6
+
7
+ Rebased onto main
8
+
9
+ RAF v3.0.0 | Ceiling: claude-opus-4-6
10
+
11
+ ▶ mosaic-drift (19 tasks)
12
+
13
+   Press Tab to toggle verbose mode
14
+
15
+ ✓ 1-archived-notebook-name-validation (opus) 5m 6s 
16
+
17
+   Tokens: 25 in / 7,798 out | Cost: $1.06
18
+
19
+ ● 10-localize-dutch (sonnet) 43s
@@ -0,0 +1,18 @@
1
+ # Outcome: Fix numeric sort order for plan/outcome files
2
+
3
+ ## Summary
4
+ Added a `numericFileSort` comparator helper and replaced all lexicographical `.sort()` calls on plan/outcome file lists with numeric sorting.
5
+
6
+ ## Changes Made
7
+
8
+ - **`src/utils/paths.ts`**: Added `numericFileSort(a, b)` export that uses `parseInt` to compare leading numeric prefixes.
9
+ - **`src/core/state-derivation.ts`**: Updated plan files sort (line ~201) and outcome files sort (line ~208) to use `numericFileSort`.
10
+ - **`src/commands/plan.ts`**: Updated three `.sort()` calls (lines ~342, ~565, ~726) to use `numericFileSort`.
11
+ - **`src/core/project-manager.ts`**: Updated outcome files sort (line ~146) to use `numericFileSort`.
12
+ - **`src/core/pull-request.ts`**: Updated outcome files sort (line ~211) to use `numericFileSort`.
13
+
14
+ ## Verification
15
+ - Build passes with `npm run build` (no TypeScript errors).
16
+ - Files named `1-x.md` through `19-x.md` now sort as 1, 2, 3, ..., 10, ..., 19.
17
+
18
+ <promise>COMPLETE</promise>
@@ -0,0 +1,10 @@
1
+ # Outcome: Add Codex and agentic coding keywords to package.json
2
+
3
+ ## Summary
4
+ Added five new discoverability keywords to `package.json` to make the package findable via Codex and agentic coding search terms on npm.
5
+
6
+ ## Changes Made
7
+
8
+ - **`package.json`**: Added `codex`, `openai-codex`, `claude-code`, `agentic-coding`, `coding-agent` to the `keywords` array. All existing keywords remain intact.
9
+
10
+ <promise>COMPLETE</promise>
@@ -0,0 +1,48 @@
1
+ ---
2
+ effort: low
3
+ ---
4
+ # Task: Fix numeric sort order for plan/outcome files
5
+
6
+ ## Objective
7
+ Replace all lexicographical `.sort()` calls on numbered file lists with numeric sorting so tasks execute in correct order (1, 2, 10 instead of 1, 10, 2).
8
+
9
+ ## Context
10
+ `raf do` reads plan files named like `1-task.md`, `10-task.md` and sorts them with `.sort()`, which uses string comparison. This causes task 10 to execute before task 2. The codebase already has correct numeric sorting for projects (`a.number - b.number`), but plan/outcome file sorting was missed.
11
+
12
+ ## Requirements
13
+ - Create a shared numeric sort helper function in `src/utils/paths.ts` (where `TASK_ID_PATTERN` already lives)
14
+ - The helper should extract the leading number from filenames like `1-task-name.md` and sort numerically
15
+ - Replace all lexicographical `.sort()` calls on plan/outcome files across the codebase
16
+
17
+ ## Implementation Steps
18
+
19
+ 1. Add a `numericFileSort` comparator function to `src/utils/paths.ts`:
20
+ ```typescript
21
+ export function numericFileSort(a: string, b: string): number {
22
+ const numA = parseInt(a, 10);
23
+ const numB = parseInt(b, 10);
24
+ return numA - numB;
25
+ }
26
+ ```
27
+ `parseInt` on `"10-task-name.md"` returns `10` — it stops at the first non-numeric character.
28
+
29
+ 2. Update the following `.sort()` calls to use `numericFileSort`:
30
+ - `src/core/state-derivation.ts` ~line 201: plan files `.sort()` → `.sort(numericFileSort)`
31
+ - `src/core/state-derivation.ts` ~line 208: outcome files `.sort()` → `.sort(numericFileSort)`
32
+ - `src/commands/plan.ts` ~lines 342, 565, 726: plan file sorting after creation
33
+ - `src/project-manager.ts` ~line 146: outcome files sorting
34
+ - `src/pull-request.ts` ~line 211: outcome files sorting for PR generation
35
+
36
+ 3. Add the import `import { numericFileSort } from '../utils/paths'` (adjust relative path) to each file that needs it.
37
+
38
+ 4. **Do NOT touch** `src/core/worktree.ts` line 353 — worktree directory sorting may not follow the same numeric prefix pattern; verify before changing.
39
+
40
+ ## Acceptance Criteria
41
+ - [ ] `numericFileSort` helper exists in `src/utils/paths.ts`
42
+ - [ ] All plan/outcome file `.sort()` calls use numeric comparison
43
+ - [ ] Files named `1-x.md` through `19-x.md` sort as 1,2,3,...,10,...,19 (not 1,10,11,...,19,2,3,...)
44
+ - [ ] Project builds without errors (`npm run build` or equivalent)
45
+
46
+ ## Notes
47
+ - `parseInt("10-task-name.md", 10)` correctly returns `10` — no regex needed.
48
+ - The existing `TASK_ID_PATTERN` in `paths.ts` confirms the numeric prefix convention.
@@ -0,0 +1,23 @@
1
+ ---
2
+ effort: low
3
+ ---
4
+ # Task: Add Codex and agentic coding keywords to package.json
5
+
6
+ ## Objective
7
+ Add discoverability keywords to package.json so the package is findable via Codex and agentic coding search terms on npm.
8
+
9
+ ## Context
10
+ The package currently has keywords like "claude", "anthropic", "ai", but is missing Codex-related and agentic coding terms that users might search for.
11
+
12
+ ## Requirements
13
+ - Add the following keywords to the `keywords` array in `package.json`: `codex`, `openai-codex`, `claude-code`, `agentic-coding`, `coding-agent`
14
+ - Keep existing keywords intact
15
+
16
+ ## Implementation Steps
17
+ 1. Open `package.json`
18
+ 2. Add the five new keywords to the existing `keywords` array: `codex`, `openai-codex`, `claude-code`, `agentic-coding`, `coding-agent`
19
+
20
+ ## Acceptance Criteria
21
+ - [ ] All five new keywords are present in `package.json` `keywords` array
22
+ - [ ] Existing keywords are unchanged
23
+ - [ ] `package.json` is valid JSON
package/README.md CHANGED
@@ -85,11 +85,9 @@ raf plan --worktree # Plan in an isolated git worktree
85
85
  Execute project tasks. Without arguments, shows a picker to select a pending project.
86
86
 
87
87
  ```bash
88
- raf do # Interactive picker
88
+ raf do # Interactive picker (includes worktree projects)
89
89
  raf do abcdef # Execute by project ID
90
90
  raf do my-project # Execute by name
91
- raf do --worktree # Pick and execute a worktree project
92
- raf do my-feature -w # Execute in worktree (picker will ask what to do after)
93
91
  ```
94
92
 
95
93
  Note: In non-verbose mode, the completion summary reflects the tasks executed in that run (the remaining tasks at start), so the elapsed time maps to those tasks.
@@ -208,8 +206,8 @@ Worktree mode runs planning and execution in an isolated git worktree, keeping y
208
206
  # Plan in a worktree (creates branch and worktree directory)
209
207
  raf plan my-feature --worktree
210
208
 
211
- # Execute tasks in the worktree
212
- raf do my-feature --worktree
209
+ # Execute tasks in the worktree (auto-detected, no flag needed)
210
+ raf do my-feature
213
211
  ```
214
212
 
215
213
  ### Post-execution picker
@@ -238,10 +236,11 @@ If `gh` is missing or unauthenticated, the option falls back to "Leave branch" w
238
236
 
239
237
  - `--worktree` creates a git worktree at `~/.raf/worktrees/<repo>/<project>/` with a new branch named after the project folder (e.g., `abcdef-my-feature`)
240
238
  - All planning artifacts, code changes, and commits happen in the worktree branch
239
+ - `raf do` auto-detects whether a project lives in a worktree — no `--worktree` flag needed
241
240
  - After successful post-actions (merge, PR, or leave), the worktree directory is cleaned up automatically — the git branch is preserved
242
241
  - On merge conflicts, the merge is aborted and you get instructions for manual resolution
243
242
  - If tasks fail, the worktree is kept for inspection
244
- - Use `--no-worktree` to disable worktree mode for a single invocation (useful when `worktree: true` is set in config)
243
+ - Use `--no-worktree` on `raf plan` to disable worktree mode for a single invocation (useful when `worktree: true` is set in config)
245
244
 
246
245
  ## Command Reference
247
246
 
@@ -266,8 +265,6 @@ If `gh` is missing or unauthenticated, the option falls back to "Leave branch" w
266
265
  | `-p, --provider <name>` | LLM provider to use (`claude`, `codex`) |
267
266
  | `-m, --model <name>` | Model to use (e.g. `sonnet`, `codex/gpt54`) |
268
267
  | `--sonnet` | Shorthand for `--model sonnet` |
269
- | `-w, --worktree` | Execute tasks in a git worktree |
270
- | `--no-worktree` | Disable worktree mode (overrides config) |
271
268
 
272
269
  Alias: `raf act`
273
270
 
@@ -1 +1 @@
1
- {"version":3,"file":"do.d.ts","sourceRoot":"","sources":["../../src/commands/do.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2DpC;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC;AAsE3D;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,KAAK,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EAC1D,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,OAAO,GACf,MAAM,CAkBR;AA0BD,wBAAgB,eAAe,IAAI,OAAO,CAmBzC;AA+TD;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuBhG"}
1
+ {"version":3,"file":"do.d.ts","sourceRoot":"","sources":["../../src/commands/do.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsDpC;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC;AAuE3D;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,KAAK,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EAC1D,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,OAAO,GACf,MAAM,CAkBR;AA0BD,wBAAgB,eAAe,IAAI,OAAO,CAiBzC;AAkOD;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuBhG"}
@@ -10,18 +10,18 @@ import { getExecutionPrompt } from '../prompts/execution.js';
10
10
  import { parseOutput, isRetryableFailure } from '../parsers/output-parser.js';
11
11
  import { validatePlansExist, resolveModelOption } from '../utils/validation.js';
12
12
  import { getRafDir, extractProjectNumber, extractProjectName, extractTaskNameFromPlanFile, resolveProjectIdentifierWithDetails, getOutcomeFilePath } from '../utils/paths.js';
13
- import { pickPendingProject, getPendingProjects, getPendingWorktreeProjects, formatProjectChoice } from '../ui/project-picker.js';
13
+ import { pickPendingProject, getPendingProjects, getPendingWorktreeProjects } from '../ui/project-picker.js';
14
14
  import { logger } from '../utils/logger.js';
15
- import { getConfig, getWorktreeDefault, getModel, getModelShortName, resolveFullModelId, getSyncMainBranch, resolveEffortToModel, applyModelCeiling, getShowCacheTokens } from '../utils/config.js';
15
+ import { getConfig, getModel, getModelShortName, resolveFullModelId, getSyncMainBranch, resolveEffortToModel, applyModelCeiling, getShowCacheTokens } from '../utils/config.js';
16
16
  import { getVersion } from '../utils/version.js';
17
17
  import { createTaskTimer, formatElapsedTime } from '../utils/timer.js';
18
18
  import { createStatusLine } from '../utils/status-line.js';
19
19
  import { formatProjectHeader, formatSummary, formatTaskProgress, formatTaskTokenSummary, formatTokenTotalSummary, } from '../utils/terminal-symbols.js';
20
20
  import { TokenTracker } from '../utils/token-tracker.js';
21
21
  import { VerboseToggle } from '../utils/verbose-toggle.js';
22
- import { deriveProjectState, discoverProjects, getNextExecutableTask, getDerivedStats, getDerivedStatsForTasks, isProjectComplete, hasProjectFailed, parseOutcomeStatus, } from '../core/state-derivation.js';
22
+ import { deriveProjectState, getNextExecutableTask, getDerivedStats, getDerivedStatsForTasks, isProjectComplete, hasProjectFailed, parseOutcomeStatus, } from '../core/state-derivation.js';
23
23
  import { analyzeFailure } from '../core/failure-analyzer.js';
24
- import { getRepoRoot, getRepoBasename, getCurrentBranch, computeWorktreePath, computeWorktreeBaseDir, validateWorktree, listWorktreeProjects, mergeWorktreeBranch, removeWorktree, resolveWorktreeProjectByIdentifier, pushMainBranch, pullMainBranch, detectMainBranch, rebaseOntoMain, } from '../core/worktree.js';
24
+ import { getRepoRoot, getRepoBasename, getCurrentBranch, mergeWorktreeBranch, removeWorktree, resolveWorktreeProjectByIdentifier, pushMainBranch, pullMainBranch, detectMainBranch, rebaseOntoMain, } from '../core/worktree.js';
25
25
  import { createPullRequest, prPreflight } from '../core/pull-request.js';
26
26
  /**
27
27
  * Resolve the execution model for a task from its frontmatter metadata.
@@ -36,7 +36,7 @@ import { createPullRequest, prPreflight } from '../core/pull-request.js';
36
36
  * @param ceilingModel - The ceiling model (usually models.execute from config)
37
37
  * @param isRetry - Whether this is a retry attempt (escalates to ceiling)
38
38
  */
39
- function resolveTaskModel(frontmatter, frontmatterWarnings, ceilingModel, isRetry) {
39
+ function resolveTaskModel(frontmatter, frontmatterWarnings, ceilingModel, isRetry, provider) {
40
40
  const warnings = frontmatterWarnings ? [...frontmatterWarnings] : [];
41
41
  // Retry escalation: always use the ceiling model on retry
42
42
  if (isRetry) {
@@ -57,7 +57,7 @@ function resolveTaskModel(frontmatter, frontmatterWarnings, ceilingModel, isRetr
57
57
  }
58
58
  // Effort-based resolution - apply ceiling
59
59
  if (frontmatter.effort) {
60
- const mappedModel = resolveEffortToModel(frontmatter.effort);
60
+ const mappedModel = resolveEffortToModel(frontmatter.effort, provider);
61
61
  const model = applyModelCeiling(mappedModel, ceilingModel);
62
62
  return { model, missingFrontmatter: false, warnings };
63
63
  }
@@ -99,8 +99,6 @@ export function createDoCommand() {
99
99
  .option('-f, --force', 'Re-run all tasks regardless of status')
100
100
  .option('-m, --model <name>', 'Model to use (sonnet, haiku, opus)')
101
101
  .option('--sonnet', 'Use Sonnet model (shorthand for --model sonnet)')
102
- .option('-w, --worktree', 'Execute tasks in a git worktree')
103
- .option('--no-worktree', 'Disable worktree mode (overrides config)')
104
102
  .option('-p, --provider <provider>', 'CLI provider to use (claude, codex)')
105
103
  .action(async (project, options) => {
106
104
  await runDoCommand(project, options);
@@ -110,7 +108,6 @@ export function createDoCommand() {
110
108
  async function runDoCommand(projectIdentifierArg, options) {
111
109
  const rafDir = getRafDir();
112
110
  let projectIdentifier = projectIdentifierArg;
113
- let worktreeMode = options.worktree ?? getWorktreeDefault();
114
111
  // Validate and resolve model option
115
112
  let model;
116
113
  try {
@@ -120,44 +117,10 @@ async function runDoCommand(projectIdentifierArg, options) {
120
117
  logger.error(error.message);
121
118
  process.exit(1);
122
119
  }
123
- // Variables for worktree context (set when --worktree is used)
120
+ // Variables for worktree context (derived from where the project is found)
124
121
  let worktreeRoot;
125
122
  let originalBranch;
126
123
  let mainBranchName = null;
127
- if (worktreeMode) {
128
- // Validate git repo
129
- const repoRoot = getRepoRoot();
130
- if (!repoRoot) {
131
- logger.error('--worktree requires a git repository');
132
- process.exit(1);
133
- }
134
- const repoBasename = getRepoBasename();
135
- const rafRelativePath = path.relative(repoRoot, rafDir);
136
- // Record original branch before any worktree operations
137
- originalBranch = getCurrentBranch() ?? undefined;
138
- // Sync main branch before worktree operations (if enabled)
139
- if (getSyncMainBranch()) {
140
- const syncResult = pullMainBranch();
141
- mainBranchName = syncResult.mainBranch;
142
- if (syncResult.success) {
143
- if (syncResult.hadChanges) {
144
- logger.info(`Synced ${syncResult.mainBranch} from remote`);
145
- }
146
- }
147
- else {
148
- logger.warn(`Could not sync main branch: ${syncResult.error}`);
149
- }
150
- }
151
- if (!projectIdentifier) {
152
- // Auto-discovery flow
153
- const selected = await discoverAndPickWorktreeProject(repoBasename, rafDir, rafRelativePath);
154
- if (!selected) {
155
- process.exit(0);
156
- }
157
- worktreeRoot = selected.worktreeRoot;
158
- projectIdentifier = selected.projectFolder;
159
- }
160
- }
161
124
  // Handle no project identifier (non-worktree mode) - show interactive picker
162
125
  if (!projectIdentifier) {
163
126
  // Discover worktree projects for the current repo (if in a git repo)
@@ -183,9 +146,8 @@ async function runDoCommand(projectIdentifierArg, options) {
183
146
  }
184
147
  // Use the selected project
185
148
  projectIdentifier = selectedProject.folder;
186
- // If a worktree project was selected, auto-switch to worktree mode
149
+ // If a worktree project was selected, record worktree context
187
150
  if (selectedProject.source === 'worktree' && selectedProject.worktreeRoot) {
188
- worktreeMode = true;
189
151
  worktreeRoot = selectedProject.worktreeRoot;
190
152
  originalBranch = getCurrentBranch() ?? undefined;
191
153
  }
@@ -200,83 +162,21 @@ async function runDoCommand(projectIdentifierArg, options) {
200
162
  }
201
163
  // Resolve project identifier
202
164
  let resolvedProject;
203
- if (worktreeMode) {
204
- // Worktree mode: resolve project inside the worktree
165
+ if (worktreeRoot) {
166
+ // Worktree was set by the picker — resolve project inside the worktree
205
167
  const repoRoot = getRepoRoot();
206
- const repoBasename = getRepoBasename();
207
168
  const rafRelativePath = path.relative(repoRoot, rafDir);
208
- // If worktreeRoot was set by auto-discovery, use it directly
209
- if (worktreeRoot) {
210
- const wtRafDir = path.join(worktreeRoot, rafRelativePath);
211
- const result = resolveProjectIdentifierWithDetails(wtRafDir, projectIdentifier);
212
- if (!result.path) {
213
- logger.error(`Project not found in worktree: ${projectIdentifier}`);
214
- process.exit(1);
215
- }
216
- const projectName = extractProjectName(result.path) ?? projectIdentifier;
217
- resolvedProject = { identifier: projectIdentifier, path: result.path, name: projectName };
218
- }
219
- else {
220
- // Explicit identifier: resolve from main repo to get folder name, then validate worktree
221
- const mainResult = resolveProjectIdentifierWithDetails(rafDir, projectIdentifier);
222
- let projectFolderName;
223
- if (mainResult.path) {
224
- // Found in main repo - use its folder name
225
- projectFolderName = path.basename(mainResult.path);
226
- }
227
- else {
228
- // Not found in main repo - try to find it in worktrees directly
229
- // This handles projects that only exist in worktrees
230
- const worktreeBaseDir = computeWorktreeBaseDir(repoBasename);
231
- if (!fs.existsSync(worktreeBaseDir)) {
232
- logger.error(`No worktree found for project "${projectIdentifier}". Did you plan with --worktree?`);
233
- process.exit(1);
234
- }
235
- // Search worktrees for the project
236
- const wtProjects = listWorktreeProjects(repoBasename);
237
- let found = false;
238
- for (const wtProjectDir of wtProjects) {
239
- const wtPath = computeWorktreePath(repoBasename, wtProjectDir);
240
- const wtRafDir = path.join(wtPath, rafRelativePath);
241
- if (!fs.existsSync(wtRafDir))
242
- continue;
243
- const resolution = resolveProjectIdentifierWithDetails(wtRafDir, projectIdentifier);
244
- if (resolution.path) {
245
- projectFolderName = path.basename(resolution.path);
246
- worktreeRoot = wtPath;
247
- found = true;
248
- break;
249
- }
250
- }
251
- if (!found) {
252
- logger.error(`No worktree found for project "${projectIdentifier}". Did you plan with --worktree?`);
253
- process.exit(1);
254
- }
255
- }
256
- // Compute worktree path if not already set
257
- if (!worktreeRoot) {
258
- worktreeRoot = computeWorktreePath(repoBasename, projectFolderName);
259
- }
260
- // Validate the worktree
261
- const wtProjectRelPath = path.join(rafRelativePath, projectFolderName);
262
- const validation = validateWorktree(worktreeRoot, wtProjectRelPath);
263
- if (!validation.exists || !validation.isValidWorktree) {
264
- logger.error(`No worktree found for project "${projectIdentifier}". Did you plan with --worktree?`);
265
- logger.error(`Expected worktree at: ${worktreeRoot}`);
266
- process.exit(1);
267
- }
268
- if (!validation.hasProjectFolder || !validation.hasPlans) {
269
- logger.error(`Worktree exists but project content is missing.`);
270
- logger.error(`Expected project folder at: ${validation.projectPath ?? path.join(worktreeRoot, wtProjectRelPath)}`);
271
- process.exit(1);
272
- }
273
- const projectPath = validation.projectPath;
274
- const projectName = extractProjectName(projectPath) ?? projectIdentifier;
275
- resolvedProject = { identifier: projectIdentifier, path: projectPath, name: projectName };
169
+ const wtRafDir = path.join(worktreeRoot, rafRelativePath);
170
+ const result = resolveProjectIdentifierWithDetails(wtRafDir, projectIdentifier);
171
+ if (!result.path) {
172
+ logger.error(`Project not found in worktree: ${projectIdentifier}`);
173
+ process.exit(1);
276
174
  }
175
+ const projectName = extractProjectName(result.path) ?? projectIdentifier;
176
+ resolvedProject = { identifier: projectIdentifier, path: result.path, name: projectName };
277
177
  }
278
178
  else {
279
- // Standard mode: check worktrees first (worktree takes priority), then main repo
179
+ // Auto-detect: check worktrees first (worktree takes priority), then main repo
280
180
  const repoRoot = getRepoRoot();
281
181
  const repoBasename = repoRoot ? getRepoBasename() : null;
282
182
  // Try worktree resolution first (preferred when project exists in both)
@@ -287,8 +187,6 @@ async function runDoCommand(projectIdentifierArg, options) {
287
187
  const wtRafDir = path.join(wtResolution.worktreeRoot, rafRelativePath);
288
188
  const wtProjectPath = path.join(wtRafDir, wtResolution.folder);
289
189
  if (fs.existsSync(wtProjectPath)) {
290
- // Auto-switch to worktree mode
291
- worktreeMode = true;
292
190
  worktreeRoot = wtResolution.worktreeRoot;
293
191
  originalBranch = getCurrentBranch() ?? undefined;
294
192
  const projectName = extractProjectName(wtResolution.folder) ?? projectIdentifier;
@@ -326,9 +224,25 @@ async function runDoCommand(projectIdentifierArg, options) {
326
224
  const autoCommit = config.autoCommit;
327
225
  // Configure logger
328
226
  logger.configure({ verbose, debug });
329
- // Show post-execution picker before task execution (worktree mode only)
227
+ // Worktree setup: sync main branch and show post-execution picker
330
228
  let postAction = 'leave';
331
- if (worktreeMode && worktreeRoot) {
229
+ if (worktreeRoot) {
230
+ if (!originalBranch) {
231
+ originalBranch = getCurrentBranch() ?? undefined;
232
+ }
233
+ // Sync main branch before worktree operations (if enabled)
234
+ if (getSyncMainBranch()) {
235
+ const syncResult = pullMainBranch();
236
+ mainBranchName = syncResult.mainBranch;
237
+ if (syncResult.success) {
238
+ if (syncResult.hadChanges) {
239
+ logger.info(`Synced ${syncResult.mainBranch} from remote`);
240
+ }
241
+ }
242
+ else {
243
+ logger.warn(`Could not sync main branch: ${syncResult.error}`);
244
+ }
245
+ }
332
246
  try {
333
247
  postAction = await pickPostExecutionAction(worktreeRoot);
334
248
  }
@@ -365,6 +279,7 @@ async function runDoCommand(projectIdentifierArg, options) {
365
279
  maxRetries,
366
280
  autoCommit,
367
281
  model,
282
+ provider: options.provider,
368
283
  worktreeCwd: worktreeRoot,
369
284
  });
370
285
  }
@@ -374,7 +289,7 @@ async function runDoCommand(projectIdentifierArg, options) {
374
289
  process.exit(1);
375
290
  }
376
291
  // Execute post-execution action based on picker choice
377
- if (worktreeMode && worktreeRoot) {
292
+ if (worktreeRoot) {
378
293
  const worktreeBranch = path.basename(worktreeRoot);
379
294
  if (result.success) {
380
295
  await executePostAction(postAction, worktreeRoot, worktreeBranch, originalBranch, resolvedProject.path);
@@ -497,75 +412,8 @@ async function executePostAction(action, worktreeRoot, worktreeBranch, originalB
497
412
  }
498
413
  }
499
414
  }
500
- /**
501
- * Auto-discovery flow for `raf do --worktree` without a project identifier.
502
- * Shows both worktree AND main-repo pending projects in an interactive picker.
503
- * Worktree projects above a threshold filter are included; main-repo projects
504
- * are always included. Duplicates are deduped with worktree taking precedence.
505
- *
506
- * @returns Selected project info or null if cancelled/no projects
507
- */
508
- async function discoverAndPickWorktreeProject(repoBasename, rafDir, rafRelativePath) {
509
- // Get worktree pending projects (uses shared utility from project-picker)
510
- const wtPendingProjects = getPendingWorktreeProjects(repoBasename, rafRelativePath);
511
- // Find the highest-numbered completed project in the MAIN tree for threshold filter
512
- const mainProjects = discoverProjects(rafDir);
513
- let highestCompletedNumber = 0;
514
- for (const project of mainProjects) {
515
- const state = deriveProjectState(project.path);
516
- if (isProjectComplete(state) && state.tasks.length > 0) {
517
- if (project.number > highestCompletedNumber) {
518
- highestCompletedNumber = project.number;
519
- }
520
- }
521
- }
522
- // Filter threshold: highest completed - 3 (or 0 if none completed)
523
- const threshold = highestCompletedNumber > 3 ? highestCompletedNumber - 3 : 0;
524
- // Apply threshold filter to worktree projects only
525
- const filteredWtProjects = wtPendingProjects.filter((p) => p.number >= threshold);
526
- // Get main-repo pending projects (no threshold filter)
527
- const mainPendingProjects = getPendingProjects(rafDir);
528
- // Merge and deduplicate: worktree versions take precedence
529
- const allProjects = [...mainPendingProjects, ...filteredWtProjects];
530
- const seen = new Map();
531
- for (const project of allProjects) {
532
- const existing = seen.get(project.folder);
533
- if (!existing || project.source === 'worktree') {
534
- seen.set(project.folder, project);
535
- }
536
- }
537
- const deduped = Array.from(seen.values());
538
- // Sort by project number
539
- deduped.sort((a, b) => a.number - b.number);
540
- if (deduped.length === 0) {
541
- logger.info('No pending projects found.');
542
- return null;
543
- }
544
- // Show interactive picker
545
- const choices = deduped.map((p) => ({
546
- name: formatProjectChoice(p),
547
- value: p,
548
- }));
549
- try {
550
- const selected = await select({
551
- message: 'Select a project to execute:',
552
- choices,
553
- });
554
- return {
555
- worktreeRoot: selected.worktreeRoot,
556
- projectFolder: selected.folder,
557
- };
558
- }
559
- catch (error) {
560
- // Handle Ctrl+C (user cancellation)
561
- if (error instanceof Error && error.message.includes('User force closed')) {
562
- return null;
563
- }
564
- throw error;
565
- }
566
- }
567
415
  async function executeSingleProject(projectPath, projectName, options) {
568
- const { timeout, verbose, debug, force, maxRetries, autoCommit, model, worktreeCwd } = options;
416
+ const { timeout, verbose, debug, force, maxRetries, autoCommit, model, provider, worktreeCwd } = options;
569
417
  if (!validatePlansExist(projectPath)) {
570
418
  return {
571
419
  projectName,
@@ -798,7 +646,7 @@ async function executeSingleProject(projectPath, projectName, options) {
798
646
  const isRetry = attempts > 1;
799
647
  // Resolve the model for this attempt (escalates to ceiling on retry)
800
648
  const modelResolution = resolveTaskModel(task.frontmatter, undefined, // warnings already logged above
801
- ceilingModel, isRetry);
649
+ ceilingModel, isRetry, provider);
802
650
  // Update current model for timer callback display
803
651
  currentModel = modelResolution.model;
804
652
  // Log missing frontmatter warning on first attempt only
@@ -806,7 +654,7 @@ async function executeSingleProject(projectPath, projectName, options) {
806
654
  logger.warn(` No effort frontmatter found — using ceiling model`);
807
655
  }
808
656
  // Create a runner for this attempt's model
809
- const taskRunner = createRunner({ model: modelResolution.model });
657
+ const taskRunner = createRunner({ model: modelResolution.model, provider });
810
658
  shutdownHandler.registerClaudeRunner(taskRunner);
811
659
  if (verbose && isRetry) {
812
660
  const retryModel = resolveFullModelId(modelResolution.model);