rafcode 2.4.0 → 2.4.1-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 (72) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/CLAUDE.md +3 -1
  3. package/RAF/ahwidh-quick-fix-gremlin/decisions.md +37 -0
  4. package/RAF/ahwidh-quick-fix-gremlin/input.md +35 -0
  5. package/RAF/ahwidh-quick-fix-gremlin/outcomes/01-fix-name-generation-prompt.md +33 -0
  6. package/RAF/ahwidh-quick-fix-gremlin/outcomes/02-fix-amend-commit-scope.md +43 -0
  7. package/RAF/ahwidh-quick-fix-gremlin/outcomes/03-fix-diverged-main-branch-sync.md +32 -0
  8. package/RAF/ahwidh-quick-fix-gremlin/outcomes/04-wire-rate-limit-to-do-command.md +61 -0
  9. package/RAF/ahwidh-quick-fix-gremlin/outcomes/05-add-config-get-set-flags.md +125 -0
  10. package/RAF/ahwidh-quick-fix-gremlin/outcomes/06-sync-worktree-branch-before-execution.md +96 -0
  11. package/RAF/ahwidh-quick-fix-gremlin/outcomes/07-update-frontmatter-format.md +107 -0
  12. package/RAF/ahwidh-quick-fix-gremlin/outcomes/08-remove-plan-token-report.md +76 -0
  13. package/RAF/ahwidh-quick-fix-gremlin/plans/01-fix-name-generation-prompt.md +52 -0
  14. package/RAF/ahwidh-quick-fix-gremlin/plans/02-fix-amend-commit-scope.md +48 -0
  15. package/RAF/ahwidh-quick-fix-gremlin/plans/03-fix-diverged-main-branch-sync.md +49 -0
  16. package/RAF/ahwidh-quick-fix-gremlin/plans/04-wire-rate-limit-to-do-command.md +78 -0
  17. package/RAF/ahwidh-quick-fix-gremlin/plans/05-add-config-get-set-flags.md +101 -0
  18. package/RAF/ahwidh-quick-fix-gremlin/plans/06-sync-worktree-branch-before-execution.md +92 -0
  19. package/RAF/ahwidh-quick-fix-gremlin/plans/07-update-frontmatter-format.md +105 -0
  20. package/RAF/ahwidh-quick-fix-gremlin/plans/08-remove-plan-token-report.md +50 -0
  21. package/dist/commands/config.d.ts.map +1 -1
  22. package/dist/commands/config.js +209 -1
  23. package/dist/commands/config.js.map +1 -1
  24. package/dist/commands/do.d.ts.map +1 -1
  25. package/dist/commands/do.js +36 -5
  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 +2 -55
  29. package/dist/commands/plan.js.map +1 -1
  30. package/dist/core/claude-runner.d.ts +0 -6
  31. package/dist/core/claude-runner.d.ts.map +1 -1
  32. package/dist/core/claude-runner.js +1 -5
  33. package/dist/core/claude-runner.js.map +1 -1
  34. package/dist/core/worktree.d.ts +12 -0
  35. package/dist/core/worktree.d.ts.map +1 -1
  36. package/dist/core/worktree.js +33 -1
  37. package/dist/core/worktree.js.map +1 -1
  38. package/dist/prompts/amend.d.ts.map +1 -1
  39. package/dist/prompts/amend.js +3 -1
  40. package/dist/prompts/amend.js.map +1 -1
  41. package/dist/prompts/planning.d.ts.map +1 -1
  42. package/dist/prompts/planning.js +3 -1
  43. package/dist/prompts/planning.js.map +1 -1
  44. package/dist/utils/frontmatter.d.ts +13 -3
  45. package/dist/utils/frontmatter.d.ts.map +1 -1
  46. package/dist/utils/frontmatter.js +40 -10
  47. package/dist/utils/frontmatter.js.map +1 -1
  48. package/dist/utils/name-generator.d.ts.map +1 -1
  49. package/dist/utils/name-generator.js +7 -16
  50. package/dist/utils/name-generator.js.map +1 -1
  51. package/dist/utils/terminal-symbols.js +2 -2
  52. package/dist/utils/terminal-symbols.js.map +1 -1
  53. package/package.json +1 -1
  54. package/src/commands/config.ts +242 -0
  55. package/src/commands/do.ts +37 -4
  56. package/src/commands/plan.ts +2 -65
  57. package/src/core/claude-runner.ts +1 -12
  58. package/src/core/worktree.ts +37 -1
  59. package/src/prompts/amend.ts +3 -1
  60. package/src/prompts/planning.ts +3 -1
  61. package/src/utils/frontmatter.ts +41 -11
  62. package/src/utils/name-generator.ts +7 -16
  63. package/src/utils/terminal-symbols.ts +2 -2
  64. package/tests/unit/commit-planning-artifacts-worktree.test.ts +6 -14
  65. package/tests/unit/commit-planning-artifacts.test.ts +4 -12
  66. package/tests/unit/config-command.test.ts +170 -0
  67. package/tests/unit/frontmatter.test.ts +95 -1
  68. package/tests/unit/name-generator.test.ts +1 -1
  69. package/tests/unit/post-execution-picker.test.ts +1 -0
  70. package/tests/unit/worktree.test.ts +68 -1
  71. package/src/utils/session-parser.ts +0 -161
  72. package/tests/unit/session-parser.test.ts +0 -301
@@ -0,0 +1,76 @@
1
+ # Outcome: Remove Token Report from Plan Command
2
+
3
+ ## Summary
4
+ Successfully removed all token usage reporting from `raf plan` and `raf plan --amend` commands. The session-parser module and its tests have been deleted, and the sessionId parameter has been removed from the ClaudeRunner API.
5
+
6
+ ## Changes Made
7
+
8
+ ### 1. Updated `src/commands/plan.ts`
9
+
10
+ **Removed imports** (lines 3, 19-22):
11
+ - Removed `import * as crypto from 'node:crypto';`
12
+ - Removed `TokenTracker` from `../utils/token-tracker.js`
13
+ - Removed `parseSessionById` from `../utils/session-parser.js`
14
+ - Removed `formatTokenTotalSummary, TokenSummaryOptions` from `../utils/terminal-symbols.js`
15
+ - Removed `getDisplayConfig, getPricingConfig` from `../utils/config.js`
16
+
17
+ **Updated `runPlanCommand()` function** (lines 286-303):
18
+ - Removed `sessionId` generation using `crypto.randomUUID()`
19
+ - Removed `sessionCwd` variable
20
+ - Removed `sessionId` parameter from `claudeRunner.runInteractive()` call
21
+ - Removed `displayPlanSessionTokenSummary(sessionId, sessionCwd)` call after session completion
22
+
23
+ **Updated `runAmendCommand()` function** (lines 599-617):
24
+ - Removed `sessionId` generation using `crypto.randomUUID()`
25
+ - Removed `sessionCwd` variable
26
+ - Removed `sessionId` parameter from `claudeRunner.runInteractive()` call
27
+ - Removed `displayPlanSessionTokenSummary(sessionId, sessionCwd)` call after session completion
28
+
29
+ **Deleted function** (lines 700-739):
30
+ - Completely removed the `displayPlanSessionTokenSummary()` function
31
+
32
+ ### 2. Updated `src/core/claude-runner.ts`
33
+
34
+ **Removed from ClaudeRunnerOptions interface** (lines 34-39):
35
+ - Deleted the `sessionId?: string` field
36
+ - Deleted the associated JSDoc comment describing the sessionId parameter
37
+
38
+ **Updated `runInteractive()` method** (lines 284-305):
39
+ - Removed `sessionId` from the destructured options parameter
40
+ - Removed the conditional code block that added `--session-id` flag to Claude CLI args
41
+
42
+ ### 3. Deleted files
43
+ - Deleted `src/utils/session-parser.ts` entirely
44
+ - Deleted `tests/unit/session-parser.test.ts` entirely
45
+
46
+ ## Testing
47
+
48
+ - **Build verification**: `npm run build` passes with no TypeScript errors
49
+ - **Test suite**: All 1284 tests pass (down from 1299, indicating the session-parser tests were successfully removed)
50
+ - **No broken imports**: No import errors for the deleted modules
51
+
52
+ ## Key Improvements
53
+
54
+ 1. **Simplified planning command**: Removed unnecessary token tracking complexity from interactive planning sessions
55
+ 2. **Dead code elimination**: Deleted the session-parser module that only existed to support this removed feature
56
+ 3. **Cleaner API**: The `runInteractive()` method no longer has the unused sessionId parameter
57
+ 4. **Reduced dependencies**: Removed crypto, TokenTracker, and session-parser imports from plan.ts
58
+
59
+ ## Acceptance Criteria
60
+
61
+ - ✅ `raf plan` no longer displays a token usage summary after the planning session
62
+ - ✅ `raf plan --amend` no longer displays a token usage summary after the amendment session
63
+ - ✅ `src/utils/session-parser.ts` is deleted
64
+ - ✅ `tests/unit/session-parser.test.ts` is deleted
65
+ - ✅ `sessionId` parameter removed from `runInteractive()` API
66
+ - ✅ No unused imports remain in modified files
67
+ - ✅ Build passes (`npm run build`)
68
+ - ✅ Tests pass (`npm test` - 1284/1284 tests pass)
69
+
70
+ ## Notes
71
+
72
+ - Token tracking utilities (`TokenTracker`, `formatTokenTotalSummary`, `formatTaskTokenSummary`) remain in the codebase as they are still used by `raf do` command
73
+ - Display and pricing config sections remain valid for `raf do` usage
74
+ - No changes were made to the execution token reporting in `raf do` - that functionality is preserved and unaffected
75
+
76
+ <promise>COMPLETE</promise>
@@ -0,0 +1,52 @@
1
+ effort: low
2
+ ---
3
+ # Task: Fix project name generation prompt
4
+
5
+ ## Objective
6
+ Fix the multi-name generation prompt so Haiku doesn't output preamble text as the first "name" option.
7
+
8
+ ## Context
9
+ When using Haiku for name generation (`models.nameGeneration: haiku`), the model outputs a preamble like "I'll generate 5 creative project names with differ..." before the actual names. This preamble gets sanitized into a long kebab-case string and appears as the first option in the picker. The prompt needs stronger instructions to prevent this.
10
+
11
+ ## Requirements
12
+ - Tighten the `MULTI_NAME_GENERATION_PROMPT` in `src/utils/name-generator.ts` to prevent preamble
13
+ - Also tighten the single `NAME_GENERATION_PROMPT` for consistency
14
+ - Do NOT change the parsing logic (user chose prompt-only fix)
15
+ - Must work reliably with Haiku, Sonnet, and Opus models
16
+
17
+ ## Implementation Steps
18
+
19
+ 1. **Edit `src/utils/name-generator.ts`** — Update `MULTI_NAME_GENERATION_PROMPT` (line 22):
20
+ - Move the output format instruction to the very beginning of the prompt
21
+ - Add explicit "Do NOT include any introduction, explanation, or preamble" instruction
22
+ - Remove the numbered style descriptions (they encourage Haiku to narrate)
23
+ - Simplify to a direct instruction format. Example approach:
24
+ ```
25
+ Output EXACTLY 5 project names, one per line. No introductions, no explanations, no numbering, no quotes.
26
+
27
+ Rules:
28
+ - Each name: 1-3 words, kebab-case, lowercase with hyphens only
29
+ - Use varied styles: metaphorical, playful, action-oriented, abstract, cultural reference
30
+ - Make them memorable and evocative
31
+ - For projects with many unrelated tasks, prefer abstract/metaphorical names
32
+
33
+ Project description:
34
+ ```
35
+
36
+ 2. **Update `NAME_GENERATION_PROMPT`** (line 6) similarly:
37
+ - Lead with the output format constraint
38
+ - Add "No introduction, no explanation" reinforcement
39
+
40
+ 3. **Update tests** if any exist in `tests/unit/name-generator.test.ts` that assert on the prompt text
41
+
42
+ ## Acceptance Criteria
43
+ - [ ] Multi-name prompt starts with output format instruction
44
+ - [ ] Prompt explicitly forbids preamble/introduction text
45
+ - [ ] Numbered style descriptions removed or simplified to avoid narration
46
+ - [ ] Single-name prompt similarly tightened
47
+ - [ ] Existing tests pass
48
+
49
+ ## Notes
50
+ - The parsing already strips numbering prefixes and quotes, but can't distinguish a preamble sentence from a valid name
51
+ - Haiku is particularly prone to prefacing its response with context
52
+ - The key insight: put the output format constraint FIRST, before the creative instructions
@@ -0,0 +1,48 @@
1
+ effort: low
2
+ ---
3
+ # Task: Fix amend mode commit to exclude plan files
4
+
5
+ ## Objective
6
+ Change the amend commit to only include `input.md` and `decisions.md`, not the new plan files.
7
+
8
+ ## Context
9
+ In `raf plan --amend`, the commit includes new plan files alongside `input.md` and `decisions.md`. Plan files should be committed by Claude during task execution instead, matching how initial planning only commits `input.md` and `decisions.md`.
10
+
11
+ ## Requirements
12
+ - Remove `additionalFiles: newPlanPaths` from the amend commit call
13
+ - Keep the `isAmend: true` flag (commit message should still use "Amend:" prefix)
14
+ - Initial planning commit behavior must remain unchanged
15
+
16
+ ## Implementation Steps
17
+
18
+ 1. **Edit `src/commands/plan.ts`** at lines 650-656. Change:
19
+ ```typescript
20
+ // Commit planning artifacts (input.md, decisions.md, and new plan files)
21
+ const newPlanPaths = newPlanFiles.map(f => path.join(plansDir, f));
22
+ await commitPlanningArtifacts(projectPath, {
23
+ cwd: worktreePath ?? undefined,
24
+ additionalFiles: newPlanPaths,
25
+ isAmend: true,
26
+ });
27
+ ```
28
+ To:
29
+ ```typescript
30
+ // Commit planning artifacts (input.md, decisions.md only — plan files committed during execution)
31
+ await commitPlanningArtifacts(projectPath, {
32
+ cwd: worktreePath ?? undefined,
33
+ isAmend: true,
34
+ });
35
+ ```
36
+ Remove the `newPlanPaths` variable as well since it's no longer used.
37
+
38
+ 2. **Update tests** — Check `tests/unit/commit-planning-artifacts.test.ts` and `tests/unit/commit-planning-artifacts-worktree.test.ts` for any amend-specific tests that assert plan files are committed. Update them to verify plan files are NOT included.
39
+
40
+ ## Acceptance Criteria
41
+ - [ ] Amend commit only stages `input.md` and `decisions.md`
42
+ - [ ] `additionalFiles` is not passed in the amend code path
43
+ - [ ] Initial planning commit behavior unchanged
44
+ - [ ] Existing tests updated and passing
45
+
46
+ ## Notes
47
+ - The `commitPlanningArtifacts` function itself doesn't need changes — just stop passing `additionalFiles` from the amend caller
48
+ - The `additionalFiles` parameter on `commitPlanningArtifacts` can remain in the interface for future use
@@ -0,0 +1,49 @@
1
+ effort: low
2
+ ---
3
+ # Task: Fix diverged main branch sync to report failure
4
+
5
+ ## Objective
6
+ Change `pullMainBranch()` to return `success: false` when the local main branch has diverged from remote, so callers surface the issue as a warning.
7
+
8
+ ## Context
9
+ PR #5 review comment: When `git fetch origin main:main` fails because local main has diverged, the code falls back to `git fetch origin main` and returns `success: true` with an `error` message. Callers only check `success === false` to warn, so the divergence is silently ignored. Worktree/PR flows then proceed on stale base refs.
10
+
11
+ The fix: return `success: false` so callers show the warning. Per user decision, this should be a visible warning that lets execution continue (not a hard error).
12
+
13
+ ## Requirements
14
+ - Change `success: true` to `success: false` in the diverged branch path
15
+ - Callers already handle `success: false` with `logger.warn()` — no caller changes needed
16
+ - The plain `git fetch origin main` should still run (we still want the remote ref updated)
17
+
18
+ ## Implementation Steps
19
+
20
+ 1. **Edit `src/core/worktree.ts`** at lines 533-538. Change:
21
+ ```typescript
22
+ return {
23
+ success: true,
24
+ mainBranch,
25
+ hadChanges: false,
26
+ error: `Local ${mainBranch} has diverged from origin, not updated`,
27
+ };
28
+ ```
29
+ To:
30
+ ```typescript
31
+ return {
32
+ success: false,
33
+ mainBranch,
34
+ hadChanges: false,
35
+ error: `Local ${mainBranch} has diverged from origin, not updated`,
36
+ };
37
+ ```
38
+
39
+ 2. **Update tests** — Find tests for `pullMainBranch` (likely in `tests/unit/worktree.test.ts`) and update the diverged branch test case to assert `success: false`.
40
+
41
+ ## Acceptance Criteria
42
+ - [ ] Diverged branch path returns `success: false`
43
+ - [ ] Callers show a warning via `logger.warn()` (already happens for `success: false`)
44
+ - [ ] Execution continues after the warning (callers don't abort on sync failure)
45
+ - [ ] Tests updated and passing
46
+
47
+ ## Notes
48
+ - The second `git fetch origin main` (plain fetch) still runs successfully — it updates `origin/main` remote ref even though local `main` isn't updated. This is fine.
49
+ - Callers in `do.ts` (line 243-250) and `plan.ts` already have the pattern: `if (!syncResult.success) { logger.warn(...) }` — no changes needed there.
@@ -0,0 +1,78 @@
1
+ effort: low
2
+ ---
3
+ # Task: Wire rate-limit estimate display config to do command
4
+
5
+ ## Objective
6
+ Pass the `display` config settings (`showRateLimitEstimate`, `showCacheTokens`) through to `formatTaskTokenSummary()` and `formatTokenTotalSummary()` calls in the do command.
7
+
8
+ ## Context
9
+ PR #5 review comment: The formatter defaults `showRateLimitEstimate` to `false` in its destructuring, but `raf do` calls both formatters without passing `TokenSummaryOptions`. Despite `display.showRateLimitEstimate` defaulting to `true` in config, the feature is effectively dead code.
10
+
11
+ ## Requirements
12
+ - Read display config settings and pass them as `TokenSummaryOptions` to both formatters
13
+ - Calculate and pass `rateLimitPercentage` from `TokenTracker`
14
+ - Per-task summaries should show cumulative rate limit percentage
15
+ - Grand total summary should show the final cumulative percentage
16
+
17
+ ## Implementation Steps
18
+
19
+ 1. **Edit `src/commands/do.ts`** — Build `TokenSummaryOptions` from config and pass to formatters.
20
+
21
+ Near the top of `runDoCommand()` (after tokenTracker is created), import and read display config:
22
+ ```typescript
23
+ import { getShowRateLimitEstimate, getShowCacheTokens } from '../utils/config.js';
24
+ ```
25
+
26
+ 2. **Update per-task token display** (lines 1218-1220 and 1246-1248). Change both locations from:
27
+ ```typescript
28
+ logger.dim(formatTaskTokenSummary(entry, (u) => tokenTracker.calculateCost(u)));
29
+ ```
30
+ To:
31
+ ```typescript
32
+ const taskRateLimitPct = tokenTracker.getCumulativeRateLimitPercentage();
33
+ logger.dim(formatTaskTokenSummary(entry, (u) => tokenTracker.calculateCost(u), {
34
+ showCacheTokens: getShowCacheTokens(),
35
+ showRateLimitEstimate: getShowRateLimitEstimate(),
36
+ rateLimitPercentage: taskRateLimitPct,
37
+ }));
38
+ ```
39
+
40
+ 3. **Update grand total display** (lines 1360-1361). Change from:
41
+ ```typescript
42
+ logger.dim(formatTokenTotalSummary(totals.usage, totals.cost));
43
+ ```
44
+ To:
45
+ ```typescript
46
+ const totalRateLimitPct = tokenTracker.getCumulativeRateLimitPercentage();
47
+ logger.dim(formatTokenTotalSummary(totals.usage, totals.cost, {
48
+ showCacheTokens: getShowCacheTokens(),
49
+ showRateLimitEstimate: getShowRateLimitEstimate(),
50
+ rateLimitPercentage: totalRateLimitPct,
51
+ }));
52
+ ```
53
+
54
+ 4. **Update the default value** in `formatTokenTotalSummary` (line 258 of `terminal-symbols.ts`). Change:
55
+ ```typescript
56
+ const { showCacheTokens = true, showRateLimitEstimate = false, rateLimitPercentage } = options;
57
+ ```
58
+ To:
59
+ ```typescript
60
+ const { showCacheTokens = true, showRateLimitEstimate = true, rateLimitPercentage } = options;
61
+ ```
62
+ This makes the default behavior match the config default (`display.showRateLimitEstimate: true`), so any callers that don't pass options still get the feature.
63
+
64
+ Also update `formatTokenLine` if it has a similar default for `showRateLimitEstimate`.
65
+
66
+ 5. **Update tests** — Adjust any test expectations that rely on `showRateLimitEstimate` defaulting to `false`.
67
+
68
+ ## Acceptance Criteria
69
+ - [ ] Per-task token summaries show rate limit percentage when `display.showRateLimitEstimate` is `true`
70
+ - [ ] Grand total summary shows rate limit percentage
71
+ - [ ] Setting `display.showRateLimitEstimate: false` in config suppresses the display
72
+ - [ ] `display.showCacheTokens` config is also wired through
73
+ - [ ] Tests pass
74
+
75
+ ## Notes
76
+ - The `TokenTracker.getCumulativeRateLimitPercentage()` method already exists and works correctly
77
+ - The `getShowRateLimitEstimate()` and `getShowCacheTokens()` config helpers already exist in `src/utils/config.ts`
78
+ - All the pieces are in place — this task just wires them together
@@ -0,0 +1,101 @@
1
+ effort: medium
2
+ ---
3
+ # Task: Add --get and --set flags to raf config
4
+
5
+ ## Objective
6
+ Add `raf config --get [key]` and `raf config --set <key> <value>` for quick config viewing/editing without launching the interactive Claude wizard.
7
+
8
+ ## Context
9
+ Currently `raf config` always launches an interactive Claude session (Sonnet). For quick operations like checking a single value or changing a model, this is slow and expensive. The `--get` and `--set` flags provide instant, non-interactive alternatives.
10
+
11
+ ## Requirements
12
+ - `raf config --get` (no key): Print full merged config as JSON (defaults + overrides)
13
+ - `raf config --get models.plan`: Print plain value (e.g., `opus`) — no key prefix, no quotes for strings
14
+ - `raf config --set models.plan sonnet`: Set a leaf-level config value
15
+ - Only support leaf-level keys (dot-notation like `models.plan`, `timeout`, `display.showRateLimitEstimate`)
16
+ - Do NOT support setting entire nested objects (e.g., `--set models '{...}'`)
17
+ - If `--get` or `--set` is present, do NOT launch the interactive wizard
18
+ - If value matches default after `--set`, remove the key from config file (keep config minimal)
19
+ - If removing the last key from a section empties it, remove the section too
20
+ - Validate the value before saving (use existing validation)
21
+ - `--set` and `--get` are mutually exclusive; show error if both provided
22
+ - `--reset` takes precedence / is mutually exclusive with `--get`/`--set`
23
+
24
+ ## Implementation Steps
25
+
26
+ 1. **Edit `src/commands/config.ts`** — Add new options to the command:
27
+ ```typescript
28
+ .option('--get [key]', 'Show config value (all config if no key, or specific dot-notation key)')
29
+ .option('--set <key> <value>', 'Set a config value using dot-notation key')
30
+ ```
31
+ Note: Commander.js handles `--set key value` with two arguments. You may need to use `.option('--set <key_value...>')` or handle it as a positional pair. Consider using a variadic option that takes exactly 2 values.
32
+
33
+ 2. **Update the `ConfigCommandOptions` interface**:
34
+ ```typescript
35
+ interface ConfigCommandOptions {
36
+ reset?: boolean;
37
+ get?: true | string; // true when --get with no key, string when --get <key>
38
+ set?: string[]; // [key, value]
39
+ }
40
+ ```
41
+
42
+ 3. **Update the action handler** to check for `--get`/`--set` before launching the wizard:
43
+ ```typescript
44
+ if (options.reset) { await handleReset(); return; }
45
+ if (options.get !== undefined) { handleGet(options.get); return; }
46
+ if (options.set) { handleSet(options.set); return; }
47
+ // ... existing wizard launch
48
+ ```
49
+
50
+ 4. **Implement `handleGet(key?: string | true)`**:
51
+ - If `key` is `true` (no argument): Print `JSON.stringify(resolveConfig(), null, 2)`
52
+ - If `key` is a string: Use dot-notation to traverse the resolved config object
53
+ - Print the value: strings plain, numbers/booleans as-is, objects as JSON
54
+ - If key not found, print error and exit with code 1
55
+
56
+ 5. **Implement `handleSet(args: string[])`**:
57
+ - Parse `args` as `[key, value]`
58
+ - Read the current user config file (or start with `{}`)
59
+ - Parse the value: try `JSON.parse(value)` first (for numbers, booleans), fall back to string
60
+ - Use dot-notation to set the leaf value in the user config object
61
+ - Check if the value matches the default at that path — if so, remove the key instead
62
+ - If removing leaves an empty parent object, remove the parent too
63
+ - Validate the resulting config with `validateConfig()`
64
+ - Save with `saveConfig()`
65
+ - If config becomes empty `{}`, delete the file entirely
66
+ - Print confirmation message
67
+
68
+ 6. **Add helper functions**:
69
+ - `getNestedValue(obj, dotPath)` — traverse an object by dot-notation path
70
+ - `setNestedValue(obj, dotPath, value)` — set a leaf value by dot-notation path
71
+ - `deleteNestedValue(obj, dotPath)` — remove a leaf and clean up empty parents
72
+ - `getDefaultValue(dotPath)` — get the default value at a dot-notation path from `DEFAULT_CONFIG`
73
+
74
+ 7. **Add tests** in `tests/unit/config-command.test.ts` (or update existing):
75
+ - `--get` with no key returns full config JSON
76
+ - `--get models.plan` returns just the value
77
+ - `--get nonexistent.key` exits with error
78
+ - `--set models.plan sonnet` updates the config file
79
+ - `--set timeout 120` parses as number
80
+ - `--set autoCommit false` parses as boolean
81
+ - `--set models.plan opus` (default value) removes the key from file
82
+ - Setting last key in section removes the section
83
+ - Validation errors are reported
84
+
85
+ ## Acceptance Criteria
86
+ - [ ] `raf config --get` prints full merged config as formatted JSON
87
+ - [ ] `raf config --get models.plan` prints plain value (e.g., `opus`)
88
+ - [ ] `raf config --set models.plan sonnet` updates config file
89
+ - [ ] Setting value to default removes it from config file
90
+ - [ ] Empty sections cleaned up after removal
91
+ - [ ] Value parsing handles strings, numbers, and booleans
92
+ - [ ] Invalid keys/values show clear error messages
93
+ - [ ] Interactive wizard NOT launched when --get or --set is present
94
+ - [ ] Tests pass
95
+
96
+ ## Notes
97
+ - The `resolveConfig()` function already merges defaults + overrides, perfect for `--get`
98
+ - The `saveConfig()` function already handles file creation and formatting
99
+ - The `validateConfig()` function validates the entire user config, so validate after modification
100
+ - For `--get`, use the resolved (merged) config; for `--set`, read/write only the user config file
101
+ - Commander.js variadic options: `--set <key> <value>` can be done with `.option('--set <items...>')` which collects remaining args
@@ -0,0 +1,92 @@
1
+ effort: medium
2
+ ---
3
+ # Task: Sync worktree branch with main before execution
4
+
5
+ ## Objective
6
+ Before executing tasks in a worktree, rebase the worktree branch onto the latest main branch to ensure it's up-to-date.
7
+
8
+ ## Dependencies
9
+ 03
10
+
11
+ ## Context
12
+ When using `raf do` in worktree mode, the worktree branch may be behind main (e.g., if other worktree projects were merged into main since this branch was created). Rebasing before execution ensures the branch starts from the latest main, reducing merge conflicts later.
13
+
14
+ ## Requirements
15
+ - After `pullMainBranch()` syncs main, rebase the worktree branch onto main
16
+ - Only do this in worktree mode in `raf do`
17
+ - If rebase has conflicts: abort the rebase, show a warning, and continue execution without sync
18
+ - The rebase should happen after worktree project selection but before task execution begins
19
+ - Respect the `syncMainBranch` config setting — skip if disabled
20
+
21
+ ## Implementation Steps
22
+
23
+ 1. **Add a `rebaseOntoMain` function to `src/core/worktree.ts`**:
24
+ ```typescript
25
+ export interface RebaseResult {
26
+ success: boolean;
27
+ error?: string;
28
+ }
29
+
30
+ export function rebaseOntoMain(mainBranch: string, cwd: string): RebaseResult {
31
+ try {
32
+ execSync(`git rebase ${mainBranch}`, {
33
+ encoding: 'utf-8',
34
+ stdio: 'pipe',
35
+ cwd,
36
+ });
37
+ return { success: true };
38
+ } catch (error) {
39
+ // Abort the failed rebase to restore clean state
40
+ try {
41
+ execSync('git rebase --abort', {
42
+ encoding: 'utf-8',
43
+ stdio: 'pipe',
44
+ cwd,
45
+ });
46
+ } catch {
47
+ // Ignore abort errors
48
+ }
49
+ const msg = error instanceof Error ? error.message : String(error);
50
+ return { success: false, error: msg };
51
+ }
52
+ }
53
+ ```
54
+
55
+ 2. **Edit `src/commands/do.ts`** — Add the rebase step after `pullMainBranch()` in the worktree mode section (after line 251, before task execution). The rebase should run inside the worktree directory:
56
+ ```typescript
57
+ // After pullMainBranch sync and worktree project selection...
58
+ // Rebase worktree branch onto main before execution
59
+ if (getSyncMainBranch() && worktreeCwd) {
60
+ const mainBranch = syncResult?.mainBranch ?? detectMainBranch();
61
+ if (mainBranch) {
62
+ const rebaseResult = rebaseOntoMain(mainBranch, worktreeCwd);
63
+ if (rebaseResult.success) {
64
+ logger.info(`Rebased onto ${mainBranch}`);
65
+ } else {
66
+ logger.warn(`Could not rebase onto ${mainBranch}: ${rebaseResult.error}`);
67
+ logger.warn('Continuing with current branch state.');
68
+ }
69
+ }
70
+ }
71
+ ```
72
+ Note: The exact placement depends on where `worktreeCwd` is set. Find the right location — it should be after the worktree is validated/created and `worktreeCwd` is determined, but before the task execution loop begins.
73
+
74
+ 3. **Determine the main branch name** — The `pullMainBranch()` result includes `mainBranch`. Store it so it can be used for the rebase. If `pullMainBranch()` wasn't called (syncMainBranch disabled), you can detect main branch with the existing `detectMainBranch()` utility from `worktree.ts`.
75
+
76
+ 4. **Add tests** for the new `rebaseOntoMain` function:
77
+ - Successful rebase
78
+ - Rebase with conflicts → aborts and returns failure
79
+ - Verify `git rebase --abort` is called on failure
80
+
81
+ ## Acceptance Criteria
82
+ - [ ] Worktree branch is rebased onto main before task execution
83
+ - [ ] Conflicts cause rebase abort + warning, execution continues
84
+ - [ ] Rebase only happens in worktree mode
85
+ - [ ] Respects `syncMainBranch` config setting
86
+ - [ ] Main branch name correctly determined from sync result or detection
87
+ - [ ] Tests pass
88
+
89
+ ## Notes
90
+ - The rebase should use the local main branch ref (which was just synced by `pullMainBranch`)
91
+ - If `pullMainBranch` failed (e.g., diverged — now returns `success: false` after task 03), the rebase should still attempt using whatever local main is available
92
+ - This runs in the worktree directory (`cwd`), not the main repo
@@ -0,0 +1,105 @@
1
+ ---
2
+ effort: medium
3
+ ---
4
+ # Task: Update plan frontmatter to standard Obsidian format
5
+
6
+ ## Objective
7
+ Change the plan file frontmatter format from closing-delimiter-only to standard Obsidian/YAML frontmatter with both opening and closing `---` delimiters.
8
+
9
+ ## Context
10
+ Currently plan files use a non-standard format with only a closing `---`:
11
+ ```
12
+ effort: medium
13
+ ---
14
+ # Task: ...
15
+ ```
16
+
17
+ Standard Obsidian (and Jekyll/Hugo/etc.) frontmatter uses `---` on both top and bottom:
18
+ ```
19
+ ---
20
+ effort: medium
21
+ ---
22
+ # Task: ...
23
+ ```
24
+
25
+ This change aligns RAF with the widely-used standard format, improving compatibility with Obsidian and other markdown tools.
26
+
27
+ ## Requirements
28
+ - Update the frontmatter parser to support the standard `---`/`---` format
29
+ - Also keep backward compatibility with the old closing-only format (existing plan files should still parse)
30
+ - Update all prompts that instruct Claude to generate frontmatter
31
+ - Update CLAUDE.md documentation
32
+ - Update existing plan files in this project to use the new format (if any are being generated)
33
+
34
+ ## Implementation Steps
35
+
36
+ 1. **Edit `src/utils/frontmatter.ts`** — Update `parsePlanFrontmatter()` to handle both formats:
37
+ - Check if content starts with `---` (after optional leading whitespace/newline)
38
+ - If it does: standard format — find the closing `---` after it, extract content between the two delimiters
39
+ - If it doesn't: legacy format — find the first `---` and treat everything before it as frontmatter (current behavior)
40
+
41
+ Updated parsing logic:
42
+ ```typescript
43
+ export function parsePlanFrontmatter(content: string): FrontmatterParseResult {
44
+ const result: FrontmatterParseResult = { frontmatter: {}, hasFrontmatter: false, warnings: [] };
45
+ const trimmedContent = content.trimStart();
46
+
47
+ let frontmatterSection: string;
48
+
49
+ if (trimmedContent.startsWith('---')) {
50
+ // Standard format: ---\nkey: value\n---
51
+ const afterOpener = trimmedContent.substring(3);
52
+ // Skip the rest of the opener line (handles "---\n" or "--- \n")
53
+ const openerEnd = afterOpener.indexOf('\n');
54
+ if (openerEnd === -1) return result;
55
+ const rest = afterOpener.substring(openerEnd + 1);
56
+ const closerIndex = rest.indexOf('---');
57
+ if (closerIndex === -1) return result;
58
+ frontmatterSection = rest.substring(0, closerIndex);
59
+ } else {
60
+ // Legacy format: key: value\n---
61
+ const delimiterIndex = content.indexOf('---');
62
+ if (delimiterIndex === -1) return result;
63
+ frontmatterSection = content.substring(0, delimiterIndex);
64
+ }
65
+
66
+ // ... rest of key: value parsing remains the same
67
+ }
68
+ ```
69
+
70
+ 2. **Update prompts** — Edit these files to show the new format with opening `---`:
71
+ - `src/prompts/planning.ts` — Update the frontmatter example/instructions
72
+ - `src/prompts/amend.ts` — Same update
73
+ - Search for any other prompts that reference frontmatter format
74
+
75
+ 3. **Update CLAUDE.md** — Change the plan file format documentation from:
76
+ ```
77
+ effort: medium
78
+ ---
79
+ ```
80
+ To:
81
+ ```
82
+ ---
83
+ effort: medium
84
+ ---
85
+ ```
86
+
87
+ 4. **Update tests** in `tests/unit/frontmatter.test.ts`:
88
+ - Add tests for the new standard format (opening + closing `---`)
89
+ - Keep tests for legacy format (closing only) to verify backward compatibility
90
+ - Test edge cases: extra whitespace before opening `---`, empty frontmatter block (`---\n---`)
91
+
92
+ 5. **Update this project's planning prompt** — The system prompt at the top of this conversation also references the frontmatter format. This is injected by RAF, so updating the prompts in step 2 covers future projects.
93
+
94
+ ## Acceptance Criteria
95
+ - [ ] Parser handles standard `---`/`---` format correctly
96
+ - [ ] Parser still handles legacy closing-only format (backward compatibility)
97
+ - [ ] Planning prompts generate the new standard format
98
+ - [ ] CLAUDE.md documentation updated
99
+ - [ ] Tests cover both old and new formats
100
+ - [ ] All existing tests pass
101
+
102
+ ## Notes
103
+ - Backward compatibility is important since existing projects have plan files in the old format
104
+ - The parser should be robust: handle `---` with trailing spaces, `---` with trailing content on the same line, etc.
105
+ - Obsidian treats `---` at the very start of a file as YAML frontmatter — this is the standard we're adopting
@@ -0,0 +1,50 @@
1
+ effort: low
2
+ ---
3
+ # Task: Remove Token Report from Plan Command
4
+
5
+ ## Objective
6
+ Remove all token usage reporting from `raf plan` and `raf plan --amend`, including the session-parser module that only exists to support this feature.
7
+
8
+ ## Context
9
+ The token report shown after planning sessions is not useful — planning is an interactive session where token cost is less relevant than during automated task execution. Removing it simplifies the plan command and eliminates dead code.
10
+
11
+ ## Requirements
12
+ - Remove token reporting from both `runPlanCommand()` and `runAmendCommand()` in `src/commands/plan.ts`
13
+ - Delete the `displayPlanSessionTokenSummary()` function entirely
14
+ - Delete `src/utils/session-parser.ts` (only consumer is the plan command)
15
+ - Delete `tests/unit/session-parser.test.ts`
16
+ - Remove the `sessionId` parameter from `claudeRunner.runInteractive()` in `src/core/claude-runner.ts` (no other callers use it)
17
+ - Clean up all unused imports in affected files
18
+
19
+ ## Implementation Steps
20
+
21
+ 1. **Edit `src/commands/plan.ts`:**
22
+ - Remove imports: `TokenTracker`, `formatTokenTotalSummary`, `TokenSummaryOptions`, `parseSessionById`, `crypto`
23
+ - Remove `getDisplayConfig` and `getPricingConfig` from the config import (if not used elsewhere in the file)
24
+ - In `runPlanCommand()`: remove `sessionId` generation, `sessionCwd` variable, remove `sessionId` from `claudeRunner.runInteractive()` options, remove `displayPlanSessionTokenSummary()` call
25
+ - In `runAmendCommand()`: same removals as above
26
+ - Delete the entire `displayPlanSessionTokenSummary()` function (around lines 702-741)
27
+
28
+ 2. **Edit `src/core/claude-runner.ts`:**
29
+ - Remove `sessionId` from the `runInteractive()` method's options interface/parameter
30
+ - Remove any internal usage of `sessionId` (e.g., passing `--session-id` flag to Claude CLI)
31
+
32
+ 3. **Delete files:**
33
+ - `src/utils/session-parser.ts`
34
+ - `tests/unit/session-parser.test.ts`
35
+
36
+ 4. **Verify:** Run `npm run build` and `npm test` to ensure no broken imports or failing tests
37
+
38
+ ## Acceptance Criteria
39
+ - [ ] `raf plan` no longer displays a token usage summary after the planning session
40
+ - [ ] `raf plan --amend` no longer displays a token usage summary after the amendment session
41
+ - [ ] `src/utils/session-parser.ts` is deleted
42
+ - [ ] `tests/unit/session-parser.test.ts` is deleted
43
+ - [ ] `sessionId` parameter removed from `runInteractive()` API
44
+ - [ ] No unused imports remain in modified files
45
+ - [ ] Build passes (`npm run build`)
46
+ - [ ] Tests pass (`npm test`)
47
+
48
+ ## Notes
49
+ - The `TokenTracker`, `formatTokenTotalSummary`, and `formatTaskTokenSummary` utilities in `token-tracker.ts` and `terminal-symbols.ts` are still used by `raf do` — do NOT delete those modules
50
+ - The `display` and `pricing` config sections remain valid for `raf do` usage — do NOT remove them from config types/defaults
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgHpC,wBAAgB,mBAAmB,IAAI,OAAO,CAgB7C"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4UpC,wBAAgB,mBAAmB,IAAI,OAAO,CAsC7C"}