rafcode 2.1.1 → 2.2.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 (120) hide show
  1. package/.claude/settings.local.json +4 -1
  2. package/CLAUDE.md +59 -11
  3. package/RAF/ahslfe-config-wizard/decisions.md +34 -0
  4. package/RAF/ahslfe-config-wizard/input.md +1 -0
  5. package/RAF/ahslfe-config-wizard/outcomes/01-define-config-schema.md +38 -0
  6. package/RAF/ahslfe-config-wizard/outcomes/02-refactor-codebase-to-use-config.md +67 -0
  7. package/RAF/ahslfe-config-wizard/outcomes/03-create-config-documentation.md +37 -0
  8. package/RAF/ahslfe-config-wizard/outcomes/04-implement-raf-config-command.md +47 -0
  9. package/RAF/ahslfe-config-wizard/outcomes/05-update-claude-md.md +26 -0
  10. package/RAF/ahslfe-config-wizard/plans/01-define-config-schema.md +73 -0
  11. package/RAF/ahslfe-config-wizard/plans/02-refactor-codebase-to-use-config.md +74 -0
  12. package/RAF/ahslfe-config-wizard/plans/03-create-config-documentation.md +57 -0
  13. package/RAF/ahslfe-config-wizard/plans/04-implement-raf-config-command.md +66 -0
  14. package/RAF/ahslfe-config-wizard/plans/05-update-claude-md.md +60 -0
  15. package/RAF/ahstvo-token-tracker/decisions.md +44 -0
  16. package/RAF/ahstvo-token-tracker/input.md +3 -0
  17. package/RAF/ahstvo-token-tracker/outcomes/01-full-model-id-support.md +43 -0
  18. package/RAF/ahstvo-token-tracker/outcomes/02-name-generation-no-session.md +33 -0
  19. package/RAF/ahstvo-token-tracker/outcomes/03-unify-stream-json-execution.md +48 -0
  20. package/RAF/ahstvo-token-tracker/outcomes/04-token-tracking-cost-calculation.md +53 -0
  21. package/RAF/ahstvo-token-tracker/outcomes/05-token-cost-console-reporting.md +57 -0
  22. package/RAF/ahstvo-token-tracker/outcomes/06-runtime-verbose-toggle.md +53 -0
  23. package/RAF/ahstvo-token-tracker/outcomes/07-readme-config-docs.md +36 -0
  24. package/RAF/ahstvo-token-tracker/plans/01-full-model-id-support.md +35 -0
  25. package/RAF/ahstvo-token-tracker/plans/02-name-generation-no-session.md +36 -0
  26. package/RAF/ahstvo-token-tracker/plans/03-unify-stream-json-execution.md +44 -0
  27. package/RAF/ahstvo-token-tracker/plans/04-token-tracking-cost-calculation.md +56 -0
  28. package/RAF/ahstvo-token-tracker/plans/05-token-cost-console-reporting.md +55 -0
  29. package/RAF/ahstvo-token-tracker/plans/06-runtime-verbose-toggle.md +48 -0
  30. package/RAF/ahstvo-token-tracker/plans/07-readme-config-docs.md +44 -0
  31. package/README.md +34 -0
  32. package/dist/commands/config.d.ts +3 -0
  33. package/dist/commands/config.d.ts.map +1 -0
  34. package/dist/commands/config.js +173 -0
  35. package/dist/commands/config.js.map +1 -0
  36. package/dist/commands/do.d.ts.map +1 -1
  37. package/dist/commands/do.js +47 -6
  38. package/dist/commands/do.js.map +1 -1
  39. package/dist/commands/plan.d.ts.map +1 -1
  40. package/dist/commands/plan.js +3 -2
  41. package/dist/commands/plan.js.map +1 -1
  42. package/dist/core/claude-runner.d.ts +19 -2
  43. package/dist/core/claude-runner.d.ts.map +1 -1
  44. package/dist/core/claude-runner.js +43 -96
  45. package/dist/core/claude-runner.js.map +1 -1
  46. package/dist/core/failure-analyzer.d.ts.map +1 -1
  47. package/dist/core/failure-analyzer.js +6 -3
  48. package/dist/core/failure-analyzer.js.map +1 -1
  49. package/dist/core/git.d.ts.map +1 -1
  50. package/dist/core/git.js +10 -3
  51. package/dist/core/git.js.map +1 -1
  52. package/dist/core/pull-request.d.ts +1 -1
  53. package/dist/core/pull-request.d.ts.map +1 -1
  54. package/dist/core/pull-request.js +7 -4
  55. package/dist/core/pull-request.js.map +1 -1
  56. package/dist/index.js +2 -0
  57. package/dist/index.js.map +1 -1
  58. package/dist/parsers/stream-renderer.d.ts +16 -1
  59. package/dist/parsers/stream-renderer.d.ts.map +1 -1
  60. package/dist/parsers/stream-renderer.js +34 -4
  61. package/dist/parsers/stream-renderer.js.map +1 -1
  62. package/dist/prompts/execution.d.ts.map +1 -1
  63. package/dist/prompts/execution.js +11 -1
  64. package/dist/prompts/execution.js.map +1 -1
  65. package/dist/types/config.d.ts +95 -4
  66. package/dist/types/config.d.ts.map +1 -1
  67. package/dist/types/config.js +63 -3
  68. package/dist/types/config.js.map +1 -1
  69. package/dist/utils/config.d.ts +59 -7
  70. package/dist/utils/config.d.ts.map +1 -1
  71. package/dist/utils/config.js +276 -21
  72. package/dist/utils/config.js.map +1 -1
  73. package/dist/utils/name-generator.d.ts +3 -7
  74. package/dist/utils/name-generator.d.ts.map +1 -1
  75. package/dist/utils/name-generator.js +75 -61
  76. package/dist/utils/name-generator.js.map +1 -1
  77. package/dist/utils/terminal-symbols.d.ts +21 -0
  78. package/dist/utils/terminal-symbols.d.ts.map +1 -1
  79. package/dist/utils/terminal-symbols.js +62 -0
  80. package/dist/utils/terminal-symbols.js.map +1 -1
  81. package/dist/utils/token-tracker.d.ts +45 -0
  82. package/dist/utils/token-tracker.d.ts.map +1 -0
  83. package/dist/utils/token-tracker.js +107 -0
  84. package/dist/utils/token-tracker.js.map +1 -0
  85. package/dist/utils/validation.d.ts +5 -5
  86. package/dist/utils/validation.d.ts.map +1 -1
  87. package/dist/utils/validation.js +10 -6
  88. package/dist/utils/validation.js.map +1 -1
  89. package/dist/utils/verbose-toggle.d.ts +33 -0
  90. package/dist/utils/verbose-toggle.d.ts.map +1 -0
  91. package/dist/utils/verbose-toggle.js +94 -0
  92. package/dist/utils/verbose-toggle.js.map +1 -0
  93. package/package.json +1 -1
  94. package/src/commands/config.ts +204 -0
  95. package/src/commands/do.ts +56 -5
  96. package/src/commands/plan.ts +3 -2
  97. package/src/core/claude-runner.ts +59 -115
  98. package/src/core/failure-analyzer.ts +6 -3
  99. package/src/core/git.ts +10 -3
  100. package/src/core/pull-request.ts +7 -4
  101. package/src/index.ts +2 -0
  102. package/src/parsers/stream-renderer.ts +54 -4
  103. package/src/prompts/config-docs.md +331 -0
  104. package/src/prompts/execution.ts +13 -1
  105. package/src/types/config.ts +156 -7
  106. package/src/utils/config.ts +335 -21
  107. package/src/utils/name-generator.ts +84 -71
  108. package/src/utils/terminal-symbols.ts +68 -0
  109. package/src/utils/token-tracker.ts +135 -0
  110. package/src/utils/validation.ts +15 -10
  111. package/src/utils/verbose-toggle.ts +103 -0
  112. package/tests/unit/claude-runner.test.ts +171 -7
  113. package/tests/unit/config-command.test.ts +163 -0
  114. package/tests/unit/config.test.ts +608 -30
  115. package/tests/unit/name-generator.test.ts +99 -75
  116. package/tests/unit/pull-request.test.ts +2 -0
  117. package/tests/unit/stream-renderer.test.ts +83 -0
  118. package/tests/unit/terminal-symbols.test.ts +157 -0
  119. package/tests/unit/token-tracker.test.ts +352 -0
  120. package/tests/unit/verbose-toggle.test.ts +204 -0
@@ -0,0 +1,56 @@
1
+ # Task: Add Token Tracking and Cost Calculation
2
+
3
+ ## Objective
4
+ Implement token usage accumulation across tasks and cost calculation using configurable per-model pricing.
5
+
6
+ ## Context
7
+ After task execution is unified to stream-json (task 03), each task returns `UsageData` with token counts. This task adds the infrastructure to accumulate usage across multiple tasks and calculate estimated costs based on configurable pricing.
8
+
9
+ ## Dependencies
10
+ 01, 03
11
+
12
+ ## Requirements
13
+ - Add pricing config to the RAF config schema with current prices as defaults
14
+ - Pricing is per-model, per-direction (input vs output), in dollars per million tokens
15
+ - Support pricing tiers: standard (prompts <= 200K tokens) and extended (prompts > 200K tokens) for Opus and Sonnet; flat rate for Haiku
16
+ - Cache read tokens should use a discounted price (typically 90% off input price — check Claude pricing page)
17
+ - Cache creation tokens should use standard input price
18
+ - Accumulate usage data across all tasks in a project execution run
19
+ - Calculate estimated cost from token counts × configurable prices
20
+
21
+ ## Implementation Steps
22
+ 1. Define pricing types and add pricing config to the config schema in `src/types/config.ts` with default values:
23
+ - Opus: $15/MTok input, $75/MTok output (standard tier)
24
+ - Sonnet: $3/MTok input, $15/MTok output
25
+ - Haiku: $1/MTok input, $5/MTok output
26
+ - Cache read discount: 90% off input price
27
+ - Cache creation: same as input price
28
+ 2. Add config validation for pricing fields in `src/utils/config.ts`
29
+ 3. Add pricing accessor helpers (e.g., `getPricing(model)`)
30
+ 4. Create a `TokenTracker` utility (e.g., `src/utils/token-tracker.ts`) that:
31
+ - Accepts `UsageData` from each task execution
32
+ - Accumulates totals (input, output, cache read, cache creation tokens per model)
33
+ - Calculates cost from token counts × configured prices
34
+ - Provides per-task summaries and a grand total
35
+ 5. Map model IDs from CLI output (e.g., `claude-opus-4-6`) to pricing tiers — the modelUsage keys are full model IDs, so need a mapping from full ID to pricing category (opus/sonnet/haiku)
36
+ 6. Update config docs in `src/prompts/config-docs.md`
37
+ 7. Add tests for cost calculation and token accumulation
38
+
39
+ ## Acceptance Criteria
40
+ - [ ] Pricing config added with sensible defaults matching current Claude API pricing
41
+ - [ ] `TokenTracker` accumulates usage across multiple tasks correctly
42
+ - [ ] Cost calculation is accurate: `tokens × price_per_token` for each category
43
+ - [ ] Per-model pricing works (different costs for opus vs sonnet vs haiku)
44
+ - [ ] Cache tokens use discounted pricing
45
+ - [ ] Config validation accepts valid pricing, rejects invalid
46
+ - [ ] All tests pass
47
+
48
+ ## Notes
49
+ - Current pricing (as of 2026-02-10):
50
+ - Opus 4.6: $15/MTok input, $75/MTok output (standard ≤200K context)
51
+ - Sonnet 4.5: $3/MTok input, $15/MTok output
52
+ - Haiku 4.5: $1/MTok input, $5/MTok output
53
+ - Cache read is typically 10% of input price
54
+ - Cache creation is typically 25% more than input price
55
+ - Model ID mapping: `claude-opus-4-6` → opus pricing, `claude-sonnet-4-5-*` → sonnet pricing, etc. Use a pattern-based lookup
56
+ - Consider whether to track extended context pricing (>200K tokens) — may not be worth the complexity initially since RAF tasks rarely exceed 200K
@@ -0,0 +1,55 @@
1
+ # Task: Add Token/Cost Reporting to Console Output
2
+
3
+ ## Objective
4
+ Display per-task token usage and cost estimates after each task, and a grand total after all tasks complete.
5
+
6
+ ## Context
7
+ With token tracking infrastructure in place (task 04), this task wires it into the `raf do` execution flow to display usage reports in the terminal. This is the user-facing part of the feature.
8
+
9
+ ## Dependencies
10
+ 04
11
+
12
+ ## Requirements
13
+ - After each task completes, print a concise usage summary line to the console
14
+ - After all tasks finish (or project completes), print a grand total summary
15
+ - Show: input tokens, output tokens, cache tokens (combined read+creation), estimated cost in USD
16
+ - Format numbers with thousands separators for readability (e.g., `12,345`)
17
+ - Format cost as USD with 2-4 decimal places (e.g., `$1.23` or `$0.0042`)
18
+ - Keep the output compact — avoid verbose multi-line reports for each task
19
+ - Respect the existing console output style (chalk colors, log levels, etc.)
20
+
21
+ ## Implementation Steps
22
+ 1. In `src/commands/do.ts`, instantiate a `TokenTracker` at the start of project execution
23
+ 2. After each task execution, feed the returned `UsageData` into the tracker
24
+ 3. Print a per-task summary line after each task completes (e.g., ` Tokens: 5,234 in / 1,023 out | Cache: 18,500 read | Est. cost: $0.42`)
25
+ 4. After all tasks complete, print a total summary section with aggregated stats
26
+ 5. Handle edge cases: tasks that fail before returning usage data, context overflow, timeouts
27
+ 6. Use existing logging/formatting utilities (chalk, logger) for consistent styling
28
+ 7. Update CLAUDE.md and README.md to document the token tracking feature
29
+
30
+ ## Acceptance Criteria
31
+ - [ ] Per-task token summary displayed after each task completion
32
+ - [ ] Grand total displayed after all tasks finish
33
+ - [ ] Numbers are formatted with thousands separators
34
+ - [ ] Cost displayed in USD format
35
+ - [ ] Failed tasks that have partial usage data are still included in totals
36
+ - [ ] Tasks with no usage data (timeout, crash) are handled gracefully
37
+ - [ ] Output is compact and doesn't overwhelm the console
38
+ - [ ] CLAUDE.md updated with token tracking documentation
39
+ - [ ] All tests pass
40
+
41
+ ## Notes
42
+ - Example per-task output format:
43
+ ```
44
+ Task 01 complete ✓
45
+ Tokens: 5,234 in / 1,023 out | Cache: 18,500 read | Est. cost: $0.42
46
+ ```
47
+ - Example total output format:
48
+ ```
49
+ ── Token Usage Summary ──────────────────
50
+ Total tokens: 45,678 in / 12,345 out
51
+ Cache: 125,000 read / 8,000 created
52
+ Estimated cost: $3.75
53
+ ─────────────────────────────────────────
54
+ ```
55
+ - These are just examples — the implementing agent should match the existing output style
@@ -0,0 +1,48 @@
1
+ # Task: Add Runtime Verbose Toggle During Task Execution
2
+
3
+ ## Objective
4
+ Allow users to press Tab during task execution to toggle verbose mode on/off in real-time, showing or hiding tool-use activity lines.
5
+
6
+ ## Context
7
+ After task 03 unifies all execution to stream-json, the underlying data stream always includes tool-use events. Verbose mode becomes purely a display concern — whether to print tool descriptions (e.g., "Reading src/file.ts", "Running: npm test") to the console. This makes runtime toggling straightforward: listen for Tab key on stdin and flip a boolean that controls display output.
8
+
9
+ ## Dependencies
10
+ 03
11
+
12
+ ## Requirements
13
+ - During non-interactive task execution (`raf do`), listen for Tab keypress on `process.stdin`
14
+ - Pressing Tab toggles verbose display: tool-use lines are shown or hidden
15
+ - The initial state matches the `--verbose` flag (off by default, on if `--verbose` was passed)
16
+ - Display a brief indicator when toggling (e.g., `[verbose: on]` / `[verbose: off]`)
17
+ - stdin must be in raw mode to capture individual keypresses without requiring Enter
18
+ - Restore stdin to normal mode after task execution completes (or on process exit)
19
+ - Do not interfere with Ctrl+C signal handling (SIGINT must still work)
20
+ - Do not interfere with the child process — stdin is already `'ignore'` for spawned Claude
21
+
22
+ ## Implementation Steps
23
+ 1. Before starting task execution in `do.ts`, set up `process.stdin` in raw mode to capture keypresses
24
+ 2. Listen for the Tab key (character code `\t` or `0x09`) on the stdin `data` event
25
+ 3. Maintain a `verboseDisplay` boolean that the stream-json renderer checks before printing tool-use lines
26
+ 4. On Tab press, flip the boolean and print a brief status indicator
27
+ 5. Pass the `verboseDisplay` reference (or a callback/event emitter) to the stream renderer so it can check the current state for each event
28
+ 6. On task completion (or process exit/error), restore stdin to cooked mode and remove the listener
29
+ 7. Integrate with the shutdown handler to ensure clean terminal state on Ctrl+C
30
+ 8. Add tests for the toggle mechanism
31
+
32
+ ## Acceptance Criteria
33
+ - [ ] Tab key toggles verbose display during task execution
34
+ - [ ] Initial verbose state matches the `--verbose` CLI flag
35
+ - [ ] Tool-use lines appear/disappear immediately on toggle
36
+ - [ ] Brief status indicator shown on toggle
37
+ - [ ] Ctrl+C still works for graceful shutdown
38
+ - [ ] Terminal state is properly restored after execution
39
+ - [ ] No interference with child process stdin
40
+ - [ ] Works correctly across multiple sequential tasks
41
+ - [ ] All existing tests pass
42
+
43
+ ## Notes
44
+ - Node.js `process.stdin.setRawMode(true)` is already used in `runInteractive()` so the pattern is familiar in this codebase
45
+ - The shutdown handler in `src/core/shutdown-handler.ts` already manages terminal cleanup — coordinate with it
46
+ - Since the child process has `stdio: ['ignore', 'pipe', 'pipe']`, parent stdin is free to use for keypress detection
47
+ - Consider showing the toggle hint at the start of execution: `Press Tab to toggle verbose mode`
48
+ - Edge case: if stdin is not a TTY (piped input), skip the keypress listener entirely
@@ -0,0 +1,44 @@
1
+ # Task: Document raf config in README and Strengthen README Update Policy
2
+
3
+ ## Objective
4
+ Add `raf config` documentation to README.md and add explicit guidance in CLAUDE.md about keeping the README updated when CLI commands or important features change.
5
+
6
+ ## Context
7
+ The `raf config` command is fully implemented but completely missing from the user-facing README. Users have no way to discover that configuration is possible, how to use it, or what can be configured. Additionally, CLAUDE.md's "Important Reminders" section should be more explicit about when README updates are required to prevent this gap from recurring.
8
+
9
+ ## Requirements
10
+
11
+ ### README updates
12
+ - Add a `raf config` section alongside the existing command documentation sections (`raf plan`, `raf do`, `raf status`)
13
+ - Keep it brief and consistent with the existing README style
14
+ - Include: command usage, what it does (interactive Claude session for config), basic config file example, 1-2 common use cases
15
+ - Add `raf config` to the Command Reference table at the bottom
16
+ - Mention the config file location (`~/.raf/raf.config.json`) and three-tier precedence (CLI flag > config > defaults)
17
+ - Do NOT duplicate the full config schema — reference that `raf config` itself provides interactive help
18
+
19
+ ### CLAUDE.md updates
20
+ - Expand the "Important Reminders" section to explicitly state that README must be updated when:
21
+ - New CLI commands are added
22
+ - Existing command APIs change (new flags, changed behavior)
23
+ - Important features are added (like worktrees, config, token tracking)
24
+ - Keep the reminder actionable and specific, not vague
25
+
26
+ ## Implementation Steps
27
+ 1. Read the current README.md to understand section ordering, style, and formatting conventions
28
+ 2. Add a `### raf config` section after the existing `raf status` section, following the same format
29
+ 3. Include a minimal config example showing 2-3 common settings (e.g., changing default model, setting worktree default)
30
+ 4. Add `raf config` entries to the Command Reference tables
31
+ 5. Update CLAUDE.md's "Important Reminders" with explicit README update policy
32
+
33
+ ## Acceptance Criteria
34
+ - [ ] README has a `raf config` section with usage and basic example
35
+ - [ ] `raf config` appears in the Command Reference table
36
+ - [ ] Config file location and precedence rules are mentioned
37
+ - [ ] CLAUDE.md has explicit guidance about when to update README
38
+ - [ ] No existing README content is broken or removed
39
+ - [ ] Documentation tone and style match the rest of the README
40
+
41
+ ## Notes
42
+ - The existing README documents `raf config` options: `--reset` flag and inline prompt (`raf config "use haiku"`) — these should be mentioned
43
+ - Look at `src/prompts/config-docs.md` for the full config reference — pick 2-3 of the most common/useful settings for the README example
44
+ - The README currently has a "Features" section that lists 7 features — consider adding "Configurable" to that list if not present
package/README.md CHANGED
@@ -52,6 +52,7 @@ That's it! RAF will guide you through breaking down your task and then execute i
52
52
  - **Git Integration**: Automatic commits after each completed task
53
53
  - **Task Dependencies**: Tasks can depend on other tasks, with automatic blocking on failure
54
54
  - **Worktree Mode**: Isolate planning and execution in a git worktree branch with `--worktree`
55
+ - **Configurable**: Customize models, effort levels, timeouts, and more via `raf config`
55
56
 
56
57
  ## Commands
57
58
 
@@ -89,6 +90,33 @@ raf status # List all projects (includes worktree projects that diffe
89
90
  raf status abcdef # Show details for a project (shows both main and worktree if they differ)
90
91
  ```
91
92
 
93
+ ### `raf config`
94
+
95
+ View and edit RAF configuration through an interactive Claude session. Configuration is stored at `~/.raf/raf.config.json`. All settings are optional — only set what you want to change.
96
+
97
+ ```bash
98
+ raf config # Interactive config editor
99
+ raf config "use haiku for name generation" # Start with a specific request
100
+ raf config --reset # Reset config to defaults
101
+ ```
102
+
103
+ **Precedence**: CLI flags > config file > built-in defaults
104
+
105
+ Example `~/.raf/raf.config.json`:
106
+
107
+ ```json
108
+ {
109
+ "models": {
110
+ "execute": "sonnet",
111
+ "nameGeneration": "haiku"
112
+ },
113
+ "worktree": true,
114
+ "timeout": 45
115
+ }
116
+ ```
117
+
118
+ Run `raf config` without arguments and ask what's available — the session has full knowledge of every configurable option.
119
+
92
120
  ### `raf migrate-project-ids-base26`
93
121
 
94
122
  Rename legacy project folders (3-char base36 or 6-char base36 with digits) to the current 6-char base26 format.
@@ -181,6 +209,12 @@ Alias: `raf act`
181
209
 
182
210
  > **Note:** `raf do` and `raf plan -y` run Claude with `--dangerously-skip-permissions` for fully automated execution without interactive prompts.
183
211
 
212
+ ### `raf config [prompt]`
213
+
214
+ | Option | Description |
215
+ |--------|-------------|
216
+ | `--reset` | Reset config file to defaults |
217
+
184
218
  ### `raf status [identifier]`
185
219
 
186
220
  | Option | Description |
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function createConfigCommand(): Command;
3
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8GpC,wBAAgB,mBAAmB,IAAI,OAAO,CAgB7C"}
@@ -0,0 +1,173 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import * as readline from 'node:readline';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { Command } from 'commander';
6
+ import { ClaudeRunner } from '../core/claude-runner.js';
7
+ import { shutdownHandler } from '../core/shutdown-handler.js';
8
+ import { logger } from '../utils/logger.js';
9
+ import { getConfigPath, getModel, getEffort, validateConfig, ConfigValidationError, } from '../utils/config.js';
10
+ /**
11
+ * Load the config documentation markdown from src/prompts/config-docs.md.
12
+ * Resolved relative to this file's location in the dist/ tree.
13
+ */
14
+ function loadConfigDocs() {
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const __dirname = path.dirname(__filename);
17
+ // From dist/commands/config.js -> ../../src/prompts/config-docs.md
18
+ const docsPath = path.join(__dirname, '..', '..', 'src', 'prompts', 'config-docs.md');
19
+ return fs.readFileSync(docsPath, 'utf-8');
20
+ }
21
+ /**
22
+ * Read the current user config file contents, or a message indicating no file exists.
23
+ */
24
+ function getCurrentConfigState(configPath) {
25
+ if (!fs.existsSync(configPath)) {
26
+ return 'No config file exists yet. All settings use defaults. The file will be created at: ' + configPath;
27
+ }
28
+ try {
29
+ const content = fs.readFileSync(configPath, 'utf-8');
30
+ return `Current config file (${configPath}):\n\`\`\`json\n${content}\`\`\``;
31
+ }
32
+ catch {
33
+ return 'Config file exists but could not be read: ' + configPath;
34
+ }
35
+ }
36
+ /**
37
+ * Build the system prompt for the config editing Claude session.
38
+ */
39
+ function buildConfigSystemPrompt(configDocs, configState) {
40
+ return [
41
+ 'You are helping the user edit their RAF configuration.',
42
+ 'You have full permission to read and write ~/.raf/raf.config.json.',
43
+ '',
44
+ '# Current Config State',
45
+ configState,
46
+ '',
47
+ '# Config Documentation',
48
+ configDocs,
49
+ ].join('\n');
50
+ }
51
+ /**
52
+ * Validate the config file after the Claude session ends and report results.
53
+ */
54
+ function postSessionValidation(configPath) {
55
+ if (!fs.existsSync(configPath)) {
56
+ logger.info('No config file exists — using all defaults.');
57
+ return;
58
+ }
59
+ try {
60
+ const content = fs.readFileSync(configPath, 'utf-8');
61
+ const parsed = JSON.parse(content);
62
+ validateConfig(parsed);
63
+ logger.success('Config updated successfully.');
64
+ // Show a summary of what's set
65
+ const userConfig = parsed;
66
+ const keys = Object.keys(userConfig);
67
+ if (keys.length > 0) {
68
+ logger.info(`Custom settings: ${keys.join(', ')}`);
69
+ }
70
+ }
71
+ catch (error) {
72
+ if (error instanceof ConfigValidationError) {
73
+ logger.warn(`Config validation warning: ${error.message}`);
74
+ logger.warn('The file was not deleted — you can fix it manually or run `raf config` again.');
75
+ }
76
+ else if (error instanceof SyntaxError) {
77
+ logger.warn('Config file contains invalid JSON.');
78
+ logger.warn('The file was not deleted — you can fix it manually or run `raf config` again.');
79
+ }
80
+ else {
81
+ logger.warn(`Could not validate config: ${error}`);
82
+ }
83
+ }
84
+ }
85
+ /**
86
+ * Prompt the user for Y/N confirmation via readline.
87
+ */
88
+ async function confirm(message) {
89
+ const rl = readline.createInterface({
90
+ input: process.stdin,
91
+ output: process.stdout,
92
+ });
93
+ return new Promise((resolve) => {
94
+ rl.question(message, (answer) => {
95
+ rl.close();
96
+ resolve(answer.trim().toLowerCase() === 'y');
97
+ });
98
+ });
99
+ }
100
+ export function createConfigCommand() {
101
+ const command = new Command('config')
102
+ .description('View and edit RAF configuration with Claude')
103
+ .argument('[prompt...]', 'Optional initial prompt for the config session')
104
+ .option('--reset', 'Delete config file and restore all defaults')
105
+ .action(async (promptParts, options) => {
106
+ if (options.reset) {
107
+ await handleReset();
108
+ return;
109
+ }
110
+ const initialPrompt = promptParts.length > 0 ? promptParts.join(' ') : undefined;
111
+ await runConfigSession(initialPrompt);
112
+ });
113
+ return command;
114
+ }
115
+ async function handleReset() {
116
+ const configPath = getConfigPath();
117
+ if (!fs.existsSync(configPath)) {
118
+ logger.info('No config file exists — already using defaults.');
119
+ return;
120
+ }
121
+ const confirmed = await confirm('This will delete ~/.raf/raf.config.json and restore all defaults. Continue? [y/N] ');
122
+ if (!confirmed) {
123
+ logger.info('Cancelled.');
124
+ return;
125
+ }
126
+ fs.unlinkSync(configPath);
127
+ logger.success('Config file deleted. All settings restored to defaults.');
128
+ }
129
+ async function runConfigSession(initialPrompt) {
130
+ const configPath = getConfigPath();
131
+ const model = getModel('config');
132
+ const effort = getEffort('config');
133
+ // Set effort level env var for the Claude session
134
+ process.env['CLAUDE_CODE_EFFORT_LEVEL'] = effort;
135
+ // Load config docs
136
+ let configDocs;
137
+ try {
138
+ configDocs = loadConfigDocs();
139
+ }
140
+ catch (error) {
141
+ logger.error(`Failed to load config documentation: ${error}`);
142
+ process.exit(1);
143
+ }
144
+ // Build system prompt
145
+ const configState = getCurrentConfigState(configPath);
146
+ const systemPrompt = buildConfigSystemPrompt(configDocs, configState);
147
+ // Build user message
148
+ const userMessage = initialPrompt
149
+ ?? 'Show me my current config and help me make changes.';
150
+ // Set up Claude runner
151
+ const claudeRunner = new ClaudeRunner({ model });
152
+ shutdownHandler.init();
153
+ shutdownHandler.registerClaudeRunner(claudeRunner);
154
+ logger.info('Starting config session with Claude...');
155
+ logger.info(`Using model: ${model}`);
156
+ logger.newline();
157
+ try {
158
+ const exitCode = await claudeRunner.runInteractive(systemPrompt, userMessage, {
159
+ dangerouslySkipPermissions: true,
160
+ });
161
+ if (exitCode !== 0) {
162
+ logger.warn(`Claude exited with code ${exitCode}`);
163
+ }
164
+ // Post-session validation
165
+ logger.newline();
166
+ postSessionValidation(configPath);
167
+ }
168
+ catch (error) {
169
+ logger.error(`Config session failed: ${error}`);
170
+ throw error;
171
+ }
172
+ }
173
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
@@ -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;AAiDpC;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC;AAE3D;;;;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;AAoSD;;;;;;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;AAqDpC;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC;AAE3D;;;;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;AAoSD;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuBhG"}
@@ -12,10 +12,12 @@ import { validatePlansExist, resolveModelOption } from '../utils/validation.js';
12
12
  import { getRafDir, extractProjectNumber, extractProjectName, extractTaskNameFromPlanFile, resolveProjectIdentifierWithDetails, getOutcomeFilePath, parseProjectPrefix } from '../utils/paths.js';
13
13
  import { pickPendingProject, getPendingProjects, getPendingWorktreeProjects } from '../ui/project-picker.js';
14
14
  import { logger } from '../utils/logger.js';
15
- import { getConfig } from '../utils/config.js';
15
+ import { getConfig, getEffort, getWorktreeDefault } from '../utils/config.js';
16
16
  import { createTaskTimer, formatElapsedTime } from '../utils/timer.js';
17
17
  import { createStatusLine } from '../utils/status-line.js';
18
- import { formatProjectHeader, formatSummary, formatTaskProgress, } from '../utils/terminal-symbols.js';
18
+ import { formatProjectHeader, formatSummary, formatTaskProgress, formatTaskTokenSummary, formatTokenTotalSummary, } from '../utils/terminal-symbols.js';
19
+ import { TokenTracker } from '../utils/token-tracker.js';
20
+ import { VerboseToggle } from '../utils/verbose-toggle.js';
19
21
  import { deriveProjectState, discoverProjects, getNextExecutableTask, getDerivedStats, getDerivedStatsForTasks, isProjectComplete, hasProjectFailed, parseOutcomeStatus, } from '../core/state-derivation.js';
20
22
  import { analyzeFailure } from '../core/failure-analyzer.js';
21
23
  import { getRepoRoot, getRepoBasename, getCurrentBranch, computeWorktreePath, computeWorktreeBaseDir, validateWorktree, listWorktreeProjects, mergeWorktreeBranch, removeWorktree, resolveWorktreeProjectByIdentifier, } from '../core/worktree.js';
@@ -60,11 +62,11 @@ export function createDoCommand() {
60
62
  async function runDoCommand(projectIdentifierArg, options) {
61
63
  const rafDir = getRafDir();
62
64
  let projectIdentifier = projectIdentifierArg;
63
- let worktreeMode = options.worktree ?? false;
65
+ let worktreeMode = options.worktree ?? getWorktreeDefault();
64
66
  // Validate and resolve model option
65
67
  let model;
66
68
  try {
67
- model = resolveModelOption(options.model, options.sonnet);
69
+ model = resolveModelOption(options.model, options.sonnet, 'execute');
68
70
  }
69
71
  catch (error) {
70
72
  logger.error(error.message);
@@ -543,6 +545,11 @@ async function executeSingleProject(projectPath, projectName, options) {
543
545
  const projectManager = new ProjectManager();
544
546
  shutdownHandler.init();
545
547
  shutdownHandler.registerClaudeRunner(claudeRunner);
548
+ // Initialize token tracker for usage reporting
549
+ const tokenTracker = new TokenTracker();
550
+ // Set up runtime verbose toggle (Tab key to toggle during execution)
551
+ const verboseToggle = new VerboseToggle(verbose);
552
+ shutdownHandler.onShutdown(() => verboseToggle.stop());
546
553
  // Start project timer
547
554
  const projectStartTime = Date.now();
548
555
  if (verbose) {
@@ -636,6 +643,8 @@ async function executeSingleProject(projectPath, projectName, options) {
636
643
  lines.push('<promise>BLOCKED</promise>');
637
644
  return lines.join('\n');
638
645
  }
646
+ // Start verbose toggle listener (Tab key)
647
+ verboseToggle.start();
639
648
  let task = getNextTaskToProcess(state);
640
649
  while (task) {
641
650
  const taskIndex = state.tasks.findIndex((t) => t.id === task.id);
@@ -701,6 +710,7 @@ async function executeSingleProject(projectPath, projectName, options) {
701
710
  let attempts = 0;
702
711
  let lastOutput = '';
703
712
  let failureReason = '';
713
+ let lastUsageData;
704
714
  // Track failure history for each attempt (attempt number -> reason)
705
715
  const failureHistory = [];
706
716
  // Set up timer for elapsed time tracking
@@ -743,10 +753,22 @@ async function executeSingleProject(projectPath, projectName, options) {
743
753
  outcomeFilePath,
744
754
  } : undefined;
745
755
  // Run Claude (use worktree root as cwd if in worktree mode)
756
+ const executeEffort = getEffort('execute');
757
+ const runnerOptions = {
758
+ timeout,
759
+ outcomeFilePath,
760
+ commitContext,
761
+ cwd: worktreeCwd,
762
+ effortLevel: executeEffort,
763
+ verboseCheck: () => verboseToggle.isVerbose,
764
+ };
746
765
  const result = verbose
747
- ? await claudeRunner.runVerbose(prompt, { timeout, outcomeFilePath, commitContext, cwd: worktreeCwd, effortLevel: 'medium' })
748
- : await claudeRunner.run(prompt, { timeout, outcomeFilePath, commitContext, cwd: worktreeCwd, effortLevel: 'medium' });
766
+ ? await claudeRunner.runVerbose(prompt, runnerOptions)
767
+ : await claudeRunner.run(prompt, runnerOptions);
749
768
  lastOutput = result.output;
769
+ if (result.usageData) {
770
+ lastUsageData = result.usageData;
771
+ }
750
772
  // Parse result
751
773
  const parsed = parseOutput(result.output);
752
774
  if (result.timedOut) {
@@ -857,6 +879,11 @@ Task completed. No detailed report provided.
857
879
  // Minimal mode: show completed task line
858
880
  logger.info(formatTaskProgress(taskNumber, totalTasks, 'completed', displayName, elapsedMs, task.id));
859
881
  }
882
+ // Track and display token usage for this task
883
+ if (lastUsageData) {
884
+ const entry = tokenTracker.addTask(task.id, lastUsageData);
885
+ logger.dim(formatTaskTokenSummary(entry.usage, entry.cost));
886
+ }
860
887
  completedInSession.add(task.id);
861
888
  }
862
889
  else {
@@ -878,6 +905,11 @@ Task completed. No detailed report provided.
878
905
  // Minimal mode: show failed task line
879
906
  logger.info(formatTaskProgress(taskNumber, totalTasks, 'failed', displayName, elapsedMs, task.id));
880
907
  }
908
+ // Track token usage even for failed tasks (partial data still useful for totals)
909
+ if (lastUsageData) {
910
+ const entry = tokenTracker.addTask(task.id, lastUsageData);
911
+ logger.dim(formatTaskTokenSummary(entry.usage, entry.cost));
912
+ }
881
913
  // Analyze failure and generate structured report
882
914
  const analysisReport = await analyzeFailure(lastOutput, failureReason, task.id);
883
915
  // Save failure outcome with status marker, analysis, and details
@@ -906,6 +938,8 @@ ${stashName ? `- Stash: ${stashName}` : ''}
906
938
  // Get next task to process
907
939
  task = getNextTaskToProcess(state);
908
940
  }
941
+ // Stop verbose toggle listener before summary output
942
+ verboseToggle.stop();
909
943
  // Ensure context is cleared for summary
910
944
  logger.clearContext();
911
945
  // Get final stats
@@ -957,6 +991,13 @@ ${stashName ? `- Stash: ${stashName}` : ''}
957
991
  logger.info(formatSummary(sessionStats.completed, sessionStats.failed, sessionStats.pending, projectElapsedMs, sessionStats.blocked));
958
992
  }
959
993
  }
994
+ // Show token usage summary if any tasks reported usage data
995
+ const trackerEntries = tokenTracker.getEntries();
996
+ if (trackerEntries.length > 0) {
997
+ logger.newline();
998
+ const totals = tokenTracker.getTotals();
999
+ logger.dim(formatTokenTotalSummary(totals.usage, totals.cost));
1000
+ }
960
1001
  // Show retry history for tasks that had failures (even if eventually successful)
961
1002
  if (projectRetryHistory.length > 0) {
962
1003
  logger.newline();