rafcode 2.4.1-0 → 2.5.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.
- package/CLAUDE.md +4 -4
- package/RAF/ahwqwq-model-whisperer/decisions.md +22 -0
- package/RAF/ahwqwq-model-whisperer/input.md +5 -0
- package/RAF/ahwqwq-model-whisperer/outcomes/01-show-model-on-task-line.md +49 -0
- package/RAF/ahwqwq-model-whisperer/outcomes/02-use-claude-cost-estimation.md +107 -0
- package/RAF/ahwqwq-model-whisperer/outcomes/03-add-plan-resume-flag.md +87 -0
- package/RAF/ahwqwq-model-whisperer/plans/01-show-model-on-task-line.md +45 -0
- package/RAF/ahwqwq-model-whisperer/plans/02-use-claude-cost-estimation.md +115 -0
- package/RAF/ahwqwq-model-whisperer/plans/03-add-plan-resume-flag.md +70 -0
- package/RAF/ahwvrz-legacy-sunset/decisions.md +10 -0
- package/RAF/ahwvrz-legacy-sunset/input.md +10 -0
- package/RAF/ahwvrz-legacy-sunset/outcomes/01-remove-migrate-command.md +30 -0
- package/RAF/ahwvrz-legacy-sunset/outcomes/02-fix-resume-worktree-resolution.md +62 -0
- package/RAF/ahwvrz-legacy-sunset/plans/01-remove-migrate-command.md +65 -0
- package/RAF/ahwvrz-legacy-sunset/plans/02-fix-resume-worktree-resolution.md +72 -0
- package/README.md +0 -17
- package/dist/commands/do.js +13 -15
- package/dist/commands/do.js.map +1 -1
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +98 -2
- package/dist/commands/plan.js.map +1 -1
- package/dist/core/claude-runner.d.ts +8 -0
- package/dist/core/claude-runner.d.ts.map +1 -1
- package/dist/core/claude-runner.js +72 -0
- package/dist/core/claude-runner.js.map +1 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/parsers/stream-renderer.d.ts +2 -0
- package/dist/parsers/stream-renderer.d.ts.map +1 -1
- package/dist/parsers/stream-renderer.js +2 -0
- package/dist/parsers/stream-renderer.js.map +1 -1
- package/dist/prompts/amend.d.ts.map +1 -1
- package/dist/prompts/amend.js +3 -1
- package/dist/prompts/amend.js.map +1 -1
- package/dist/prompts/planning.d.ts.map +1 -1
- package/dist/prompts/planning.js +4 -1
- package/dist/prompts/planning.js.map +1 -1
- package/dist/types/config.d.ts +4 -28
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +0 -24
- package/dist/types/config.js.map +1 -1
- package/dist/utils/config.d.ts +1 -26
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +2 -98
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/terminal-symbols.d.ts +7 -16
- package/dist/utils/terminal-symbols.d.ts.map +1 -1
- package/dist/utils/terminal-symbols.js +16 -42
- package/dist/utils/terminal-symbols.js.map +1 -1
- package/dist/utils/token-tracker.d.ts +4 -30
- package/dist/utils/token-tracker.d.ts.map +1 -1
- package/dist/utils/token-tracker.js +17 -98
- package/dist/utils/token-tracker.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/do.ts +14 -15
- package/src/commands/plan.ts +112 -1
- package/src/core/claude-runner.ts +81 -0
- package/src/index.ts +0 -2
- package/src/parsers/stream-renderer.ts +4 -0
- package/src/prompts/amend.ts +3 -1
- package/src/prompts/config-docs.md +1 -72
- package/src/prompts/planning.ts +4 -1
- package/src/types/config.ts +4 -57
- package/src/utils/config.ts +2 -112
- package/src/utils/terminal-symbols.ts +16 -46
- package/src/utils/token-tracker.ts +19 -113
- package/tests/unit/claude-runner.test.ts +1 -0
- package/tests/unit/config-command.test.ts +4 -13
- package/tests/unit/config.test.ts +6 -148
- package/tests/unit/plan-resume-worktree-resolution.test.ts +153 -0
- package/tests/unit/stream-renderer.test.ts +82 -0
- package/tests/unit/terminal-symbols.test.ts +86 -124
- package/tests/unit/token-tracker.test.ts +159 -679
- package/src/commands/migrate.ts +0 -269
- package/tests/unit/migrate-command.test.ts +0 -197
|
@@ -135,49 +135,12 @@ Controls the format of git commit messages. Templates use `{placeholder}` syntax
|
|
|
135
135
|
|
|
136
136
|
Unknown placeholders are left as-is in the output.
|
|
137
137
|
|
|
138
|
-
### `pricing` — Token Cost Pricing
|
|
139
|
-
|
|
140
|
-
Controls per-model token pricing used for cost estimation. Prices are in dollars per million tokens. Each model category (`opus`, `sonnet`, `haiku`) has four pricing fields:
|
|
141
|
-
|
|
142
|
-
| Field | Description |
|
|
143
|
-
|-------|-------------|
|
|
144
|
-
| `inputPerMTok` | Cost per million input tokens |
|
|
145
|
-
| `outputPerMTok` | Cost per million output tokens |
|
|
146
|
-
| `cacheReadPerMTok` | Cost per million cache read tokens (discounted) |
|
|
147
|
-
| `cacheCreatePerMTok` | Cost per million cache creation tokens |
|
|
148
|
-
|
|
149
|
-
**Default values:**
|
|
150
|
-
|
|
151
|
-
| Category | Input | Output | Cache Read | Cache Create |
|
|
152
|
-
|----------|-------|--------|------------|--------------|
|
|
153
|
-
| `opus` | $15 | $75 | $1.50 | $18.75 |
|
|
154
|
-
| `sonnet` | $3 | $15 | $0.30 | $3.75 |
|
|
155
|
-
| `haiku` | $1 | $5 | $0.10 | $1.25 |
|
|
156
|
-
|
|
157
|
-
Full model IDs from CLI output (e.g., `claude-opus-4-6`) are automatically mapped to the corresponding pricing category based on the model family name.
|
|
158
|
-
|
|
159
|
-
Example override:
|
|
160
|
-
|
|
161
|
-
```json
|
|
162
|
-
{
|
|
163
|
-
"pricing": {
|
|
164
|
-
"opus": {
|
|
165
|
-
"inputPerMTok": 10,
|
|
166
|
-
"outputPerMTok": 50
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
Only specify the fields you want to change — unset fields keep their defaults.
|
|
173
|
-
|
|
174
138
|
### `display` — Token Summary Display Options
|
|
175
139
|
|
|
176
140
|
Controls what information is shown in token usage summaries after tasks and in the grand total.
|
|
177
141
|
|
|
178
142
|
| Key | Default | Description |
|
|
179
143
|
|-----|---------|-------------|
|
|
180
|
-
| `display.showRateLimitEstimate` | `true` | Show estimated 5h rate limit window percentage (e.g., `~42% of 5h window`) |
|
|
181
144
|
| `display.showCacheTokens` | `true` | Show cache read/create token counts in summaries |
|
|
182
145
|
|
|
183
146
|
Example:
|
|
@@ -185,34 +148,11 @@ Example:
|
|
|
185
148
|
```json
|
|
186
149
|
{
|
|
187
150
|
"display": {
|
|
188
|
-
"showRateLimitEstimate": false,
|
|
189
151
|
"showCacheTokens": true
|
|
190
152
|
}
|
|
191
153
|
}
|
|
192
154
|
```
|
|
193
155
|
|
|
194
|
-
### `rateLimitWindow` — Rate Limit Configuration
|
|
195
|
-
|
|
196
|
-
Controls the rate limit estimation calculation.
|
|
197
|
-
|
|
198
|
-
| Key | Default | Description |
|
|
199
|
-
|-----|---------|-------------|
|
|
200
|
-
| `rateLimitWindow.sonnetTokenCap` | `88000` | The Sonnet-equivalent token cap for the 5-hour window. All token usage is normalized to Sonnet-equivalent tokens using pricing ratios. |
|
|
201
|
-
|
|
202
|
-
The 5h window percentage is calculated as: `(estimatedCost / sonnetCostPerToken) / sonnetTokenCap * 100`
|
|
203
|
-
|
|
204
|
-
Where `sonnetCostPerToken` is derived from the configured Sonnet pricing. Heavier models (Opus) consume the window faster than lighter ones (Haiku) in proportion to their API pricing ratios.
|
|
205
|
-
|
|
206
|
-
Example:
|
|
207
|
-
|
|
208
|
-
```json
|
|
209
|
-
{
|
|
210
|
-
"rateLimitWindow": {
|
|
211
|
-
"sonnetTokenCap": 100000
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
```
|
|
215
|
-
|
|
216
156
|
## Validation Rules
|
|
217
157
|
|
|
218
158
|
The config is validated when loaded. Invalid configs cause an error with a descriptive message. The following rules are enforced:
|
|
@@ -224,9 +164,7 @@ The config is validated when loaded. Invalid configs cause an error with a descr
|
|
|
224
164
|
- **`maxRetries`** must be a non-negative integer.
|
|
225
165
|
- **`autoCommit`**, **`worktree`**, and **`syncMainBranch`** must be booleans.
|
|
226
166
|
- **`commitFormat` values** must be strings.
|
|
227
|
-
- **`
|
|
228
|
-
- **`display` values** (`showRateLimitEstimate`, `showCacheTokens`) must be booleans.
|
|
229
|
-
- **`rateLimitWindow.sonnetTokenCap`** must be a positive number.
|
|
167
|
+
- **`display` values** (`showCacheTokens`) must be booleans.
|
|
230
168
|
- The config file must be valid JSON containing an object (not an array or primitive).
|
|
231
169
|
|
|
232
170
|
## CLI Precedence
|
|
@@ -294,17 +232,8 @@ Uses Sonnet for planning and caps task execution at Sonnet (tasks with `effort:
|
|
|
294
232
|
"amend": "{prefix}[{projectId}] Amend: {projectName}",
|
|
295
233
|
"prefix": "RAF"
|
|
296
234
|
},
|
|
297
|
-
"pricing": {
|
|
298
|
-
"opus": { "inputPerMTok": 15, "outputPerMTok": 75, "cacheReadPerMTok": 1.5, "cacheCreatePerMTok": 18.75 },
|
|
299
|
-
"sonnet": { "inputPerMTok": 3, "outputPerMTok": 15, "cacheReadPerMTok": 0.3, "cacheCreatePerMTok": 3.75 },
|
|
300
|
-
"haiku": { "inputPerMTok": 1, "outputPerMTok": 5, "cacheReadPerMTok": 0.1, "cacheCreatePerMTok": 1.25 }
|
|
301
|
-
},
|
|
302
235
|
"display": {
|
|
303
|
-
"showRateLimitEstimate": true,
|
|
304
236
|
"showCacheTokens": true
|
|
305
|
-
},
|
|
306
|
-
"rateLimitWindow": {
|
|
307
|
-
"sonnetTokenCap": 88000
|
|
308
237
|
}
|
|
309
238
|
}
|
|
310
239
|
```
|
package/src/prompts/planning.ts
CHANGED
|
@@ -168,7 +168,10 @@ or for multiple dependencies:
|
|
|
168
168
|
### Step 5: Confirm Completion
|
|
169
169
|
|
|
170
170
|
After creating all plan files:
|
|
171
|
-
1. Provide a summary of the tasks you've created
|
|
171
|
+
1. Provide a summary of the tasks you've created, including the effort level for each task. Example:
|
|
172
|
+
- Task 01: setup-database (effort: low)
|
|
173
|
+
- Task 02: implement-auth (effort: medium)
|
|
174
|
+
- Task 03: refactor-api (effort: high)
|
|
172
175
|
2. Display this exit message to the user:
|
|
173
176
|
|
|
174
177
|
\`\`\`
|
package/src/types/config.ts
CHANGED
|
@@ -39,38 +39,12 @@ export interface CommitFormatConfig {
|
|
|
39
39
|
prefix: string;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
/** Pricing category derived from model family name. */
|
|
43
|
-
export type PricingCategory = 'opus' | 'sonnet' | 'haiku';
|
|
44
|
-
|
|
45
|
-
/** Per-direction pricing for a single model category, in dollars per million tokens. */
|
|
46
|
-
export interface ModelPricing {
|
|
47
|
-
inputPerMTok: number;
|
|
48
|
-
outputPerMTok: number;
|
|
49
|
-
cacheReadPerMTok: number;
|
|
50
|
-
cacheCreatePerMTok: number;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** Pricing config: per-category pricing in dollars per million tokens. */
|
|
54
|
-
export interface PricingConfig {
|
|
55
|
-
opus: ModelPricing;
|
|
56
|
-
sonnet: ModelPricing;
|
|
57
|
-
haiku: ModelPricing;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
42
|
/** Display options for token usage summaries. */
|
|
61
43
|
export interface DisplayConfig {
|
|
62
|
-
/** Show estimated 5h rate limit window percentage. Default: true */
|
|
63
|
-
showRateLimitEstimate: boolean;
|
|
64
44
|
/** Show cache token counts in summaries. Default: true */
|
|
65
45
|
showCacheTokens: boolean;
|
|
66
46
|
}
|
|
67
47
|
|
|
68
|
-
/** Rate limit window configuration. */
|
|
69
|
-
export interface RateLimitWindowConfig {
|
|
70
|
-
/** Sonnet-equivalent token cap for the 5h window. Default: 88000 */
|
|
71
|
-
sonnetTokenCap: number;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
48
|
export interface RafConfig {
|
|
75
49
|
models: ModelsConfig;
|
|
76
50
|
/** Maps task complexity labels (low/medium/high) to models. Used for per-task effort frontmatter. */
|
|
@@ -82,9 +56,7 @@ export interface RafConfig {
|
|
|
82
56
|
/** Sync main branch with remote before worktree/PR operations. Default: true */
|
|
83
57
|
syncMainBranch: boolean;
|
|
84
58
|
commitFormat: CommitFormatConfig;
|
|
85
|
-
pricing: PricingConfig;
|
|
86
59
|
display: DisplayConfig;
|
|
87
|
-
rateLimitWindow: RateLimitWindowConfig;
|
|
88
60
|
}
|
|
89
61
|
|
|
90
62
|
export const DEFAULT_CONFIG: RafConfig = {
|
|
@@ -112,33 +84,9 @@ export const DEFAULT_CONFIG: RafConfig = {
|
|
|
112
84
|
amend: '{prefix}[{projectId}] Amend: {projectName}',
|
|
113
85
|
prefix: 'RAF',
|
|
114
86
|
},
|
|
115
|
-
pricing: {
|
|
116
|
-
opus: {
|
|
117
|
-
inputPerMTok: 15,
|
|
118
|
-
outputPerMTok: 75,
|
|
119
|
-
cacheReadPerMTok: 1.5,
|
|
120
|
-
cacheCreatePerMTok: 18.75,
|
|
121
|
-
},
|
|
122
|
-
sonnet: {
|
|
123
|
-
inputPerMTok: 3,
|
|
124
|
-
outputPerMTok: 15,
|
|
125
|
-
cacheReadPerMTok: 0.3,
|
|
126
|
-
cacheCreatePerMTok: 3.75,
|
|
127
|
-
},
|
|
128
|
-
haiku: {
|
|
129
|
-
inputPerMTok: 1,
|
|
130
|
-
outputPerMTok: 5,
|
|
131
|
-
cacheReadPerMTok: 0.1,
|
|
132
|
-
cacheCreatePerMTok: 1.25,
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
87
|
display: {
|
|
136
|
-
showRateLimitEstimate: true,
|
|
137
88
|
showCacheTokens: true,
|
|
138
89
|
},
|
|
139
|
-
rateLimitWindow: {
|
|
140
|
-
sonnetTokenCap: 88000,
|
|
141
|
-
},
|
|
142
90
|
};
|
|
143
91
|
|
|
144
92
|
/** Deep partial type for user config files — all fields optional at every level */
|
|
@@ -190,17 +138,14 @@ export interface StatusCommandOptions {
|
|
|
190
138
|
json?: boolean;
|
|
191
139
|
}
|
|
192
140
|
|
|
193
|
-
export interface MigrateCommandOptions {
|
|
194
|
-
dryRun?: boolean;
|
|
195
|
-
worktree?: boolean;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
141
|
/** Per-model token usage breakdown from stream-json result event. */
|
|
199
142
|
export interface ModelTokenUsage {
|
|
200
143
|
inputTokens: number;
|
|
201
144
|
outputTokens: number;
|
|
202
145
|
cacheReadInputTokens: number;
|
|
203
146
|
cacheCreationInputTokens: number;
|
|
147
|
+
/** Cost in USD for this model's usage (provided by Claude CLI). */
|
|
148
|
+
costUsd: number;
|
|
204
149
|
}
|
|
205
150
|
|
|
206
151
|
/** Token usage data extracted from Claude CLI stream-json result event. */
|
|
@@ -212,4 +157,6 @@ export interface UsageData {
|
|
|
212
157
|
cacheCreationInputTokens: number;
|
|
213
158
|
/** Per-model breakdown (e.g., { "claude-opus-4-6": { ... } }). */
|
|
214
159
|
modelUsage: Record<string, ModelTokenUsage>;
|
|
160
|
+
/** Total cost in USD for this usage (provided by Claude CLI). */
|
|
161
|
+
totalCostUsd: number;
|
|
215
162
|
}
|
package/src/utils/config.ts
CHANGED
|
@@ -12,11 +12,7 @@ import {
|
|
|
12
12
|
TaskEffortLevel,
|
|
13
13
|
ModelScenario,
|
|
14
14
|
CommitFormatType,
|
|
15
|
-
PricingCategory,
|
|
16
|
-
ModelPricing,
|
|
17
|
-
PricingConfig,
|
|
18
15
|
DisplayConfig,
|
|
19
|
-
RateLimitWindowConfig,
|
|
20
16
|
EffortMappingConfig,
|
|
21
17
|
} from '../types/config.js';
|
|
22
18
|
|
|
@@ -38,12 +34,9 @@ export function getClaudeSettingsPath(): string {
|
|
|
38
34
|
|
|
39
35
|
const VALID_TOP_LEVEL_KEYS = new Set<string>([
|
|
40
36
|
'models', 'effortMapping', 'timeout', 'maxRetries', 'autoCommit',
|
|
41
|
-
'worktree', 'syncMainBranch', 'commitFormat', '
|
|
37
|
+
'worktree', 'syncMainBranch', 'commitFormat', 'display',
|
|
42
38
|
]);
|
|
43
39
|
|
|
44
|
-
const VALID_PRICING_CATEGORIES = new Set<string>(['opus', 'sonnet', 'haiku']);
|
|
45
|
-
const VALID_PRICING_FIELDS = new Set<string>(['inputPerMTok', 'outputPerMTok', 'cacheReadPerMTok', 'cacheCreatePerMTok']);
|
|
46
|
-
|
|
47
40
|
const VALID_MODEL_KEYS = new Set<string>([
|
|
48
41
|
'plan', 'execute', 'nameGeneration', 'failureAnalysis', 'prGeneration', 'config',
|
|
49
42
|
]);
|
|
@@ -52,9 +45,7 @@ const VALID_EFFORT_MAPPING_KEYS = new Set<string>(['low', 'medium', 'high']);
|
|
|
52
45
|
|
|
53
46
|
const VALID_COMMIT_FORMAT_KEYS = new Set<string>(['task', 'plan', 'amend', 'prefix']);
|
|
54
47
|
|
|
55
|
-
const VALID_DISPLAY_KEYS = new Set<string>(['
|
|
56
|
-
|
|
57
|
-
const VALID_RATE_LIMIT_WINDOW_KEYS = new Set<string>(['sonnetTokenCap']);
|
|
48
|
+
const VALID_DISPLAY_KEYS = new Set<string>(['showCacheTokens']);
|
|
58
49
|
|
|
59
50
|
export class ConfigValidationError extends Error {
|
|
60
51
|
constructor(message: string) {
|
|
@@ -167,27 +158,6 @@ export function validateConfig(config: unknown): UserConfig {
|
|
|
167
158
|
}
|
|
168
159
|
}
|
|
169
160
|
|
|
170
|
-
// pricing
|
|
171
|
-
if (obj.pricing !== undefined) {
|
|
172
|
-
if (typeof obj.pricing !== 'object' || obj.pricing === null || Array.isArray(obj.pricing)) {
|
|
173
|
-
throw new ConfigValidationError('pricing must be an object');
|
|
174
|
-
}
|
|
175
|
-
const pricing = obj.pricing as Record<string, unknown>;
|
|
176
|
-
checkUnknownKeys(pricing, VALID_PRICING_CATEGORIES, 'pricing');
|
|
177
|
-
for (const [category, catVal] of Object.entries(pricing)) {
|
|
178
|
-
if (typeof catVal !== 'object' || catVal === null || Array.isArray(catVal)) {
|
|
179
|
-
throw new ConfigValidationError(`pricing.${category} must be an object`);
|
|
180
|
-
}
|
|
181
|
-
const fields = catVal as Record<string, unknown>;
|
|
182
|
-
checkUnknownKeys(fields, VALID_PRICING_FIELDS, `pricing.${category}`);
|
|
183
|
-
for (const [field, val] of Object.entries(fields)) {
|
|
184
|
-
if (typeof val !== 'number' || val < 0 || !Number.isFinite(val)) {
|
|
185
|
-
throw new ConfigValidationError(`pricing.${category}.${field} must be a non-negative number`);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
161
|
// display
|
|
192
162
|
if (obj.display !== undefined) {
|
|
193
163
|
if (typeof obj.display !== 'object' || obj.display === null || Array.isArray(obj.display)) {
|
|
@@ -202,20 +172,6 @@ export function validateConfig(config: unknown): UserConfig {
|
|
|
202
172
|
}
|
|
203
173
|
}
|
|
204
174
|
|
|
205
|
-
// rateLimitWindow
|
|
206
|
-
if (obj.rateLimitWindow !== undefined) {
|
|
207
|
-
if (typeof obj.rateLimitWindow !== 'object' || obj.rateLimitWindow === null || Array.isArray(obj.rateLimitWindow)) {
|
|
208
|
-
throw new ConfigValidationError('rateLimitWindow must be an object');
|
|
209
|
-
}
|
|
210
|
-
const rlw = obj.rateLimitWindow as Record<string, unknown>;
|
|
211
|
-
checkUnknownKeys(rlw, VALID_RATE_LIMIT_WINDOW_KEYS, 'rateLimitWindow');
|
|
212
|
-
if (rlw.sonnetTokenCap !== undefined) {
|
|
213
|
-
if (typeof rlw.sonnetTokenCap !== 'number' || rlw.sonnetTokenCap <= 0 || !Number.isFinite(rlw.sonnetTokenCap)) {
|
|
214
|
-
throw new ConfigValidationError('rateLimitWindow.sonnetTokenCap must be a positive number');
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
175
|
return config as UserConfig;
|
|
220
176
|
}
|
|
221
177
|
|
|
@@ -233,19 +189,9 @@ function deepMerge(defaults: RafConfig, overrides: UserConfig): RafConfig {
|
|
|
233
189
|
if (overrides.commitFormat) {
|
|
234
190
|
result.commitFormat = { ...defaults.commitFormat, ...overrides.commitFormat };
|
|
235
191
|
}
|
|
236
|
-
if (overrides.pricing) {
|
|
237
|
-
result.pricing = {
|
|
238
|
-
opus: { ...defaults.pricing.opus, ...overrides.pricing.opus },
|
|
239
|
-
sonnet: { ...defaults.pricing.sonnet, ...overrides.pricing.sonnet },
|
|
240
|
-
haiku: { ...defaults.pricing.haiku, ...overrides.pricing.haiku },
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
192
|
if (overrides.display) {
|
|
244
193
|
result.display = { ...defaults.display, ...overrides.display };
|
|
245
194
|
}
|
|
246
|
-
if (overrides.rateLimitWindow) {
|
|
247
|
-
result.rateLimitWindow = { ...defaults.rateLimitWindow, ...overrides.rateLimitWindow };
|
|
248
|
-
}
|
|
249
195
|
if (overrides.timeout !== undefined) result.timeout = overrides.timeout;
|
|
250
196
|
if (overrides.maxRetries !== undefined) result.maxRetries = overrides.maxRetries;
|
|
251
197
|
if (overrides.autoCommit !== undefined) result.autoCommit = overrides.autoCommit;
|
|
@@ -271,7 +217,6 @@ export function resolveConfig(configPath?: string): RafConfig {
|
|
|
271
217
|
effortMapping: { ...DEFAULT_CONFIG.effortMapping },
|
|
272
218
|
commitFormat: { ...DEFAULT_CONFIG.commitFormat },
|
|
273
219
|
display: { ...DEFAULT_CONFIG.display },
|
|
274
|
-
rateLimitWindow: { ...DEFAULT_CONFIG.rateLimitWindow },
|
|
275
220
|
};
|
|
276
221
|
}
|
|
277
222
|
|
|
@@ -459,40 +404,6 @@ export function resolveFullModelId(modelName: string): string {
|
|
|
459
404
|
return modelName;
|
|
460
405
|
}
|
|
461
406
|
|
|
462
|
-
/**
|
|
463
|
-
* Map a full model ID (e.g., `claude-opus-4-6`) or short alias to a pricing category.
|
|
464
|
-
* Returns null if the model cannot be mapped.
|
|
465
|
-
*/
|
|
466
|
-
export function resolveModelPricingCategory(modelId: string): PricingCategory | null {
|
|
467
|
-
// Short aliases map directly
|
|
468
|
-
if (modelId === 'opus' || modelId === 'sonnet' || modelId === 'haiku') {
|
|
469
|
-
return modelId;
|
|
470
|
-
}
|
|
471
|
-
// Full model IDs: extract family from `claude-{family}-{version}`
|
|
472
|
-
const match = modelId.match(/^claude-([a-z]+)-/);
|
|
473
|
-
if (match) {
|
|
474
|
-
const family = match[1];
|
|
475
|
-
if (family === 'opus' || family === 'sonnet' || family === 'haiku') {
|
|
476
|
-
return family;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
return null;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
/**
|
|
483
|
-
* Get pricing config for a specific model category.
|
|
484
|
-
*/
|
|
485
|
-
export function getPricing(category: PricingCategory): ModelPricing {
|
|
486
|
-
return getResolvedConfig().pricing[category];
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* Get the full pricing config.
|
|
491
|
-
*/
|
|
492
|
-
export function getPricingConfig(): PricingConfig {
|
|
493
|
-
return getResolvedConfig().pricing;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
407
|
/**
|
|
497
408
|
* Get the full display config.
|
|
498
409
|
*/
|
|
@@ -500,20 +411,6 @@ export function getDisplayConfig(): DisplayConfig {
|
|
|
500
411
|
return getResolvedConfig().display;
|
|
501
412
|
}
|
|
502
413
|
|
|
503
|
-
/**
|
|
504
|
-
* Get the full rate limit window config.
|
|
505
|
-
*/
|
|
506
|
-
export function getRateLimitWindowConfig(): RateLimitWindowConfig {
|
|
507
|
-
return getResolvedConfig().rateLimitWindow;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
/**
|
|
511
|
-
* Get whether to show rate limit estimate in token summaries.
|
|
512
|
-
*/
|
|
513
|
-
export function getShowRateLimitEstimate(): boolean {
|
|
514
|
-
return getResolvedConfig().display.showRateLimitEstimate;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
414
|
/**
|
|
518
415
|
* Get whether to show cache tokens in summaries.
|
|
519
416
|
*/
|
|
@@ -521,13 +418,6 @@ export function getShowCacheTokens(): boolean {
|
|
|
521
418
|
return getResolvedConfig().display.showCacheTokens;
|
|
522
419
|
}
|
|
523
420
|
|
|
524
|
-
/**
|
|
525
|
-
* Get the Sonnet-equivalent token cap for the 5h rate limit window.
|
|
526
|
-
*/
|
|
527
|
-
export function getSonnetTokenCap(): number {
|
|
528
|
-
return getResolvedConfig().rateLimitWindow.sonnetTokenCap;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
421
|
/**
|
|
532
422
|
* Render a commit message template by replacing {placeholder} tokens with values.
|
|
533
423
|
* Unknown placeholders are left as-is.
|
|
@@ -11,10 +11,6 @@ import type { CostBreakdown, TaskUsageEntry } from './token-tracker.js';
|
|
|
11
11
|
export interface TokenSummaryOptions {
|
|
12
12
|
/** Whether to show cache token counts. Default: true */
|
|
13
13
|
showCacheTokens?: boolean;
|
|
14
|
-
/** Whether to show rate limit percentage. Default: true */
|
|
15
|
-
showRateLimitEstimate?: boolean;
|
|
16
|
-
/** Rate limit percentage to display (requires showRateLimitEstimate: true) */
|
|
17
|
-
rateLimitPercentage?: number;
|
|
18
14
|
}
|
|
19
15
|
|
|
20
16
|
/**
|
|
@@ -49,7 +45,8 @@ function truncate(str: string, maxLength: number): string {
|
|
|
49
45
|
* @param name - Task name
|
|
50
46
|
* @param elapsedMs - Optional elapsed time in milliseconds
|
|
51
47
|
* @param taskId - Optional task ID prefix display
|
|
52
|
-
* @
|
|
48
|
+
* @param model - Optional model short name to display (e.g., "sonnet", "opus", "haiku")
|
|
49
|
+
* @returns Formatted string like "● 001-auth-login (sonnet) 1:23" or "✓ 001-auth-login (opus) 1/5"
|
|
53
50
|
*/
|
|
54
51
|
export function formatTaskProgress(
|
|
55
52
|
current: number,
|
|
@@ -57,19 +54,21 @@ export function formatTaskProgress(
|
|
|
57
54
|
status: TaskStatus,
|
|
58
55
|
name: string,
|
|
59
56
|
elapsedMs?: number,
|
|
60
|
-
taskId?: string
|
|
57
|
+
taskId?: string,
|
|
58
|
+
model?: string
|
|
61
59
|
): string {
|
|
62
60
|
const symbol = SYMBOLS[status];
|
|
63
61
|
const displayName = truncate(name || 'task', 40);
|
|
64
62
|
const idPrefix = taskId ? `${taskId}-` : '';
|
|
63
|
+
const modelSuffix = model ? ` (${model})` : '';
|
|
65
64
|
|
|
66
65
|
// Show elapsed time for running tasks, completed tasks, and failed tasks
|
|
67
66
|
if (elapsedMs !== undefined) {
|
|
68
67
|
const timeStr = formatElapsedTime(elapsedMs);
|
|
69
|
-
return `${symbol} ${idPrefix}${displayName} ${timeStr}`;
|
|
68
|
+
return `${symbol} ${idPrefix}${displayName}${modelSuffix} ${timeStr}`;
|
|
70
69
|
}
|
|
71
70
|
|
|
72
|
-
return `${symbol} ${idPrefix}${displayName} ${current}/${total}`;
|
|
71
|
+
return `${symbol} ${idPrefix}${displayName}${modelSuffix} ${current}/${total}`;
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
/**
|
|
@@ -155,17 +154,6 @@ export function formatCost(cost: number): string {
|
|
|
155
154
|
return `$${cost.toFixed(2)}`;
|
|
156
155
|
}
|
|
157
156
|
|
|
158
|
-
/**
|
|
159
|
-
* Formats a rate limit percentage for display.
|
|
160
|
-
* Uses tilde (~) prefix to indicate estimate.
|
|
161
|
-
*/
|
|
162
|
-
export function formatRateLimitPercentage(percentage: number): string {
|
|
163
|
-
if (percentage === 0) return '~0% of 5h window';
|
|
164
|
-
if (percentage < 0.1) return `~${percentage.toFixed(2)}% of 5h window`;
|
|
165
|
-
if (percentage < 1) return `~${percentage.toFixed(1)}% of 5h window`;
|
|
166
|
-
return `~${Math.round(percentage)}% of 5h window`;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
157
|
/**
|
|
170
158
|
* Formats a single line of token usage (for a single attempt or total).
|
|
171
159
|
* Used internally by formatTaskTokenSummary.
|
|
@@ -177,7 +165,7 @@ function formatTokenLine(
|
|
|
177
165
|
indent: string = ' ',
|
|
178
166
|
options: TokenSummaryOptions = {}
|
|
179
167
|
): string {
|
|
180
|
-
const { showCacheTokens = true
|
|
168
|
+
const { showCacheTokens = true } = options;
|
|
181
169
|
const parts: string[] = [];
|
|
182
170
|
const tokenPart = `${formatNumber(usage.inputTokens)} in / ${formatNumber(usage.outputTokens)} out`;
|
|
183
171
|
parts.push(prefix ? `${prefix}: ${tokenPart}` : `Tokens: ${tokenPart}`);
|
|
@@ -195,27 +183,21 @@ function formatTokenLine(
|
|
|
195
183
|
}
|
|
196
184
|
}
|
|
197
185
|
|
|
198
|
-
parts.push(`
|
|
199
|
-
|
|
200
|
-
if (showRateLimitEstimate && rateLimitPercentage !== undefined) {
|
|
201
|
-
parts.push(formatRateLimitPercentage(rateLimitPercentage));
|
|
202
|
-
}
|
|
186
|
+
parts.push(`Cost: ${formatCost(costValue)}`);
|
|
203
187
|
|
|
204
188
|
return `${indent}${parts.join(' | ')}`;
|
|
205
189
|
}
|
|
206
190
|
|
|
207
191
|
/**
|
|
208
192
|
* Formats a per-task token usage summary.
|
|
209
|
-
* For single-attempt tasks: " Tokens: 5,234 in / 1,023 out | Cache: 18,500 read |
|
|
193
|
+
* For single-attempt tasks: " Tokens: 5,234 in / 1,023 out | Cache: 18,500 read | Cost: $0.42"
|
|
210
194
|
* For multi-attempt tasks: shows per-attempt breakdown plus total.
|
|
211
195
|
*
|
|
212
196
|
* @param entry - The TaskUsageEntry containing accumulated usage, cost, and attempts array
|
|
213
|
-
* @param
|
|
214
|
-
* @param options - Display options for showing cache tokens and rate limit percentage
|
|
197
|
+
* @param options - Display options for showing cache tokens
|
|
215
198
|
*/
|
|
216
199
|
export function formatTaskTokenSummary(
|
|
217
200
|
entry: TaskUsageEntry,
|
|
218
|
-
calculateAttemptCost?: (usage: UsageData) => CostBreakdown,
|
|
219
201
|
options: TokenSummaryOptions = {}
|
|
220
202
|
): string {
|
|
221
203
|
// Single-attempt: render exactly as before (no per-attempt breakdown)
|
|
@@ -224,19 +206,10 @@ export function formatTaskTokenSummary(
|
|
|
224
206
|
}
|
|
225
207
|
|
|
226
208
|
// Multi-attempt: show per-attempt lines plus total
|
|
227
|
-
// Per-attempt lines don't show rate limit (only show on total)
|
|
228
|
-
const perAttemptOptions: TokenSummaryOptions = {
|
|
229
|
-
...options,
|
|
230
|
-
showRateLimitEstimate: false,
|
|
231
|
-
rateLimitPercentage: undefined,
|
|
232
|
-
};
|
|
233
|
-
|
|
234
209
|
const lines: string[] = [];
|
|
235
210
|
entry.attempts.forEach((attemptUsage, i) => {
|
|
236
|
-
const attemptCost =
|
|
237
|
-
|
|
238
|
-
: 0;
|
|
239
|
-
lines.push(formatTokenLine(attemptUsage, attemptCost, `Attempt ${i + 1}`, ' ', perAttemptOptions));
|
|
211
|
+
const attemptCost = attemptUsage.totalCostUsd;
|
|
212
|
+
lines.push(formatTokenLine(attemptUsage, attemptCost, `Attempt ${i + 1}`, ' ', options));
|
|
240
213
|
});
|
|
241
214
|
lines.push(formatTokenLine(entry.usage, entry.cost.totalCost, 'Total', ' ', options));
|
|
242
215
|
return lines.join('\n');
|
|
@@ -248,14 +221,14 @@ export function formatTaskTokenSummary(
|
|
|
248
221
|
*
|
|
249
222
|
* @param usage - Total usage data
|
|
250
223
|
* @param cost - Total cost breakdown
|
|
251
|
-
* @param options - Display options for cache tokens
|
|
224
|
+
* @param options - Display options for cache tokens
|
|
252
225
|
*/
|
|
253
226
|
export function formatTokenTotalSummary(
|
|
254
227
|
usage: UsageData,
|
|
255
228
|
cost: CostBreakdown,
|
|
256
229
|
options: TokenSummaryOptions = {}
|
|
257
230
|
): string {
|
|
258
|
-
const { showCacheTokens = true
|
|
231
|
+
const { showCacheTokens = true } = options;
|
|
259
232
|
const lines: string[] = [];
|
|
260
233
|
const divider = '── Token Usage Summary ──────────────────';
|
|
261
234
|
lines.push(divider);
|
|
@@ -272,11 +245,8 @@ export function formatTokenTotalSummary(
|
|
|
272
245
|
lines.push(`Cache: ${cacheParts.join(' / ')}`);
|
|
273
246
|
}
|
|
274
247
|
|
|
275
|
-
lines.push(`
|
|
248
|
+
lines.push(`Total cost: ${formatCost(cost.totalCost)}`);
|
|
276
249
|
|
|
277
|
-
if (showRateLimitEstimate && rateLimitPercentage !== undefined) {
|
|
278
|
-
lines.push(formatRateLimitPercentage(rateLimitPercentage));
|
|
279
|
-
}
|
|
280
250
|
lines.push('─────────────────────────────────────────');
|
|
281
251
|
return lines.join('\n');
|
|
282
252
|
}
|