rafcode 2.1.1 → 2.3.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/settings.local.json +4 -1
- package/CLAUDE.md +59 -11
- package/RAF/ahslfe-config-wizard/decisions.md +34 -0
- package/RAF/ahslfe-config-wizard/input.md +1 -0
- package/RAF/ahslfe-config-wizard/outcomes/01-define-config-schema.md +38 -0
- package/RAF/ahslfe-config-wizard/outcomes/02-refactor-codebase-to-use-config.md +67 -0
- package/RAF/ahslfe-config-wizard/outcomes/03-create-config-documentation.md +37 -0
- package/RAF/ahslfe-config-wizard/outcomes/04-implement-raf-config-command.md +47 -0
- package/RAF/ahslfe-config-wizard/outcomes/05-update-claude-md.md +26 -0
- package/RAF/ahslfe-config-wizard/plans/01-define-config-schema.md +73 -0
- package/RAF/ahslfe-config-wizard/plans/02-refactor-codebase-to-use-config.md +74 -0
- package/RAF/ahslfe-config-wizard/plans/03-create-config-documentation.md +57 -0
- package/RAF/ahslfe-config-wizard/plans/04-implement-raf-config-command.md +66 -0
- package/RAF/ahslfe-config-wizard/plans/05-update-claude-md.md +60 -0
- package/RAF/ahstvo-token-tracker/decisions.md +44 -0
- package/RAF/ahstvo-token-tracker/input.md +3 -0
- package/RAF/ahstvo-token-tracker/outcomes/01-full-model-id-support.md +43 -0
- package/RAF/ahstvo-token-tracker/outcomes/02-name-generation-no-session.md +33 -0
- package/RAF/ahstvo-token-tracker/outcomes/03-unify-stream-json-execution.md +48 -0
- package/RAF/ahstvo-token-tracker/outcomes/04-token-tracking-cost-calculation.md +53 -0
- package/RAF/ahstvo-token-tracker/outcomes/05-token-cost-console-reporting.md +57 -0
- package/RAF/ahstvo-token-tracker/outcomes/06-runtime-verbose-toggle.md +53 -0
- package/RAF/ahstvo-token-tracker/outcomes/07-readme-config-docs.md +36 -0
- package/RAF/ahstvo-token-tracker/plans/01-full-model-id-support.md +35 -0
- package/RAF/ahstvo-token-tracker/plans/02-name-generation-no-session.md +36 -0
- package/RAF/ahstvo-token-tracker/plans/03-unify-stream-json-execution.md +44 -0
- package/RAF/ahstvo-token-tracker/plans/04-token-tracking-cost-calculation.md +56 -0
- package/RAF/ahstvo-token-tracker/plans/05-token-cost-console-reporting.md +55 -0
- package/RAF/ahstvo-token-tracker/plans/06-runtime-verbose-toggle.md +48 -0
- package/RAF/ahstvo-token-tracker/plans/07-readme-config-docs.md +44 -0
- package/RAF/ahtahs-token-reaper/decisions.md +37 -0
- package/RAF/ahtahs-token-reaper/input.md +20 -0
- package/RAF/ahtahs-token-reaper/outcomes/01-extend-token-tracker-data-model.md +42 -0
- package/RAF/ahtahs-token-reaper/outcomes/02-accumulate-usage-in-retry-loop.md +31 -0
- package/RAF/ahtahs-token-reaper/outcomes/03-per-attempt-display-formatting.md +60 -0
- package/RAF/ahtahs-token-reaper/outcomes/04-add-model-name-to-claude-call-logs.md +57 -0
- package/RAF/ahtahs-token-reaper/outcomes/05-handle-invalid-config-in-raf-config.md +46 -0
- package/RAF/ahtahs-token-reaper/outcomes/06-fix-verbose-toggle-timer-display.md +38 -0
- package/RAF/ahtahs-token-reaper/plans/01-extend-token-tracker-data-model.md +36 -0
- package/RAF/ahtahs-token-reaper/plans/02-accumulate-usage-in-retry-loop.md +36 -0
- package/RAF/ahtahs-token-reaper/plans/03-per-attempt-display-formatting.md +43 -0
- package/RAF/ahtahs-token-reaper/plans/04-add-model-name-to-claude-call-logs.md +38 -0
- package/RAF/ahtahs-token-reaper/plans/05-handle-invalid-config-in-raf-config.md +36 -0
- package/RAF/ahtahs-token-reaper/plans/06-fix-verbose-toggle-timer-display.md +40 -0
- package/README.md +34 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +195 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/do.d.ts.map +1 -1
- package/dist/commands/do.js +55 -7
- package/dist/commands/do.js.map +1 -1
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +5 -3
- package/dist/commands/plan.js.map +1 -1
- package/dist/core/claude-runner.d.ts +19 -2
- package/dist/core/claude-runner.d.ts.map +1 -1
- package/dist/core/claude-runner.js +43 -96
- package/dist/core/claude-runner.js.map +1 -1
- package/dist/core/failure-analyzer.d.ts.map +1 -1
- package/dist/core/failure-analyzer.js +6 -3
- package/dist/core/failure-analyzer.js.map +1 -1
- package/dist/core/git.d.ts.map +1 -1
- package/dist/core/git.js +10 -3
- package/dist/core/git.js.map +1 -1
- package/dist/core/pull-request.d.ts +1 -1
- package/dist/core/pull-request.d.ts.map +1 -1
- package/dist/core/pull-request.js +9 -4
- package/dist/core/pull-request.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/parsers/stream-renderer.d.ts +16 -1
- package/dist/parsers/stream-renderer.d.ts.map +1 -1
- package/dist/parsers/stream-renderer.js +34 -4
- package/dist/parsers/stream-renderer.js.map +1 -1
- package/dist/prompts/execution.d.ts.map +1 -1
- package/dist/prompts/execution.js +11 -1
- package/dist/prompts/execution.js.map +1 -1
- package/dist/types/config.d.ts +95 -4
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +63 -3
- package/dist/types/config.js.map +1 -1
- package/dist/utils/config.d.ts +65 -7
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +297 -21
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/name-generator.d.ts +3 -7
- package/dist/utils/name-generator.d.ts.map +1 -1
- package/dist/utils/name-generator.js +75 -61
- package/dist/utils/name-generator.js.map +1 -1
- package/dist/utils/terminal-symbols.d.ts +25 -0
- package/dist/utils/terminal-symbols.d.ts.map +1 -1
- package/dist/utils/terminal-symbols.js +87 -0
- package/dist/utils/terminal-symbols.js.map +1 -1
- package/dist/utils/token-tracker.d.ts +55 -0
- package/dist/utils/token-tracker.d.ts.map +1 -0
- package/dist/utils/token-tracker.js +142 -0
- package/dist/utils/token-tracker.js.map +1 -0
- package/dist/utils/validation.d.ts +5 -5
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +10 -6
- package/dist/utils/validation.js.map +1 -1
- package/dist/utils/verbose-toggle.d.ts +33 -0
- package/dist/utils/verbose-toggle.d.ts.map +1 -0
- package/dist/utils/verbose-toggle.js +94 -0
- package/dist/utils/verbose-toggle.js.map +1 -0
- package/package.json +1 -1
- package/src/commands/config.ts +230 -0
- package/src/commands/do.ts +64 -6
- package/src/commands/plan.ts +5 -3
- package/src/core/claude-runner.ts +59 -115
- package/src/core/failure-analyzer.ts +6 -3
- package/src/core/git.ts +10 -3
- package/src/core/pull-request.ts +9 -4
- package/src/index.ts +2 -0
- package/src/parsers/stream-renderer.ts +54 -4
- package/src/prompts/config-docs.md +331 -0
- package/src/prompts/execution.ts +13 -1
- package/src/types/config.ts +156 -7
- package/src/utils/config.ts +357 -21
- package/src/utils/name-generator.ts +84 -71
- package/src/utils/terminal-symbols.ts +103 -0
- package/src/utils/token-tracker.ts +177 -0
- package/src/utils/validation.ts +15 -10
- package/src/utils/verbose-toggle.ts +103 -0
- package/tests/unit/claude-runner.test.ts +171 -7
- package/tests/unit/config-command.test.ts +242 -0
- package/tests/unit/config.test.ts +632 -30
- package/tests/unit/name-generator.test.ts +99 -75
- package/tests/unit/pull-request.test.ts +2 -0
- package/tests/unit/stream-renderer.test.ts +83 -0
- package/tests/unit/terminal-symbols.test.ts +245 -0
- package/tests/unit/timer-verbose-integration.test.ts +170 -0
- package/tests/unit/token-tracker.test.ts +685 -0
- package/tests/unit/verbose-toggle.test.ts +204 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ClaudeModelName, ModelScenario } from '../types/config.js';
|
|
1
2
|
export interface ValidationResult {
|
|
2
3
|
valid: boolean;
|
|
3
4
|
warnings: string[];
|
|
@@ -9,9 +10,8 @@ export declare function sanitizeProjectName(name: string): string;
|
|
|
9
10
|
export declare function validateProjectExists(rafDir: string, projectName: string): string | null;
|
|
10
11
|
export declare function validatePlansExist(projectPath: string): boolean;
|
|
11
12
|
export declare function reportValidation(result: ValidationResult): void;
|
|
12
|
-
|
|
13
|
-
export type ValidModelName =
|
|
14
|
-
export declare function validateModelName(model: string):
|
|
15
|
-
export declare function resolveModelOption(model?: string, sonnet?: boolean):
|
|
16
|
-
export {};
|
|
13
|
+
/** @deprecated Use ClaudeModelName from types/config.js instead */
|
|
14
|
+
export type ValidModelName = ClaudeModelName;
|
|
15
|
+
export declare function validateModelName(model: string): ClaudeModelName | null;
|
|
16
|
+
export declare function resolveModelOption(model?: string, sonnet?: boolean, scenario?: ModelScenario): ClaudeModelName;
|
|
17
17
|
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAIzE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,wBAAgB,mBAAmB,IAAI,gBAAgB,CA4BtD;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAIzD;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMxD;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAaxF;AAED,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAQ/D;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAQ/D;AAED,mEAAmE;AACnE,MAAM,MAAM,cAAc,GAAG,eAAe,CAAC;AAE7C,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CASvE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,QAAQ,GAAE,aAAyB,GAAG,eAAe,CAsBzH"}
|
package/dist/utils/validation.js
CHANGED
|
@@ -2,6 +2,8 @@ import * as fs from 'node:fs';
|
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import { execSync } from 'node:child_process';
|
|
4
4
|
import { logger } from './logger.js';
|
|
5
|
+
import { VALID_MODEL_ALIASES, FULL_MODEL_ID_PATTERN } from '../types/config.js';
|
|
6
|
+
import { getModel } from './config.js';
|
|
5
7
|
export function validateEnvironment() {
|
|
6
8
|
const result = {
|
|
7
9
|
valid: true,
|
|
@@ -69,15 +71,17 @@ export function reportValidation(result) {
|
|
|
69
71
|
logger.error(error);
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
|
-
const VALID_MODELS = ['sonnet', 'haiku', 'opus'];
|
|
73
74
|
export function validateModelName(model) {
|
|
74
75
|
const normalized = model.toLowerCase();
|
|
75
|
-
if (
|
|
76
|
+
if (VALID_MODEL_ALIASES.includes(normalized)) {
|
|
77
|
+
return normalized;
|
|
78
|
+
}
|
|
79
|
+
if (FULL_MODEL_ID_PATTERN.test(normalized)) {
|
|
76
80
|
return normalized;
|
|
77
81
|
}
|
|
78
82
|
return null;
|
|
79
83
|
}
|
|
80
|
-
export function resolveModelOption(model, sonnet) {
|
|
84
|
+
export function resolveModelOption(model, sonnet, scenario = 'execute') {
|
|
81
85
|
// Check for conflicting flags
|
|
82
86
|
if (model && sonnet) {
|
|
83
87
|
throw new Error('Cannot specify both --model and --sonnet flags');
|
|
@@ -90,11 +94,11 @@ export function resolveModelOption(model, sonnet) {
|
|
|
90
94
|
if (model) {
|
|
91
95
|
const validated = validateModelName(model);
|
|
92
96
|
if (!validated) {
|
|
93
|
-
throw new Error(`Invalid model name: "${model}". Valid options: ${
|
|
97
|
+
throw new Error(`Invalid model name: "${model}". Valid options: ${VALID_MODEL_ALIASES.join(', ')} or a full model ID (e.g., claude-sonnet-4-5-20250929)`);
|
|
94
98
|
}
|
|
95
99
|
return validated;
|
|
96
100
|
}
|
|
97
|
-
// Default
|
|
98
|
-
return
|
|
101
|
+
// Default from config
|
|
102
|
+
return getModel(scenario);
|
|
99
103
|
}
|
|
100
104
|
//# sourceMappingURL=validation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime verbose toggle for task execution.
|
|
3
|
+
*
|
|
4
|
+
* Listens for Tab keypress on process.stdin to toggle verbose display on/off.
|
|
5
|
+
* When verbose is on, tool-use activity lines from stream-json are displayed.
|
|
6
|
+
* When verbose is off, they are suppressed (but data is still captured).
|
|
7
|
+
*
|
|
8
|
+
* Requires a TTY stdin. Silently skips setup when stdin is not a TTY (e.g., piped input).
|
|
9
|
+
*/
|
|
10
|
+
export declare class VerboseToggle {
|
|
11
|
+
private _verbose;
|
|
12
|
+
private _active;
|
|
13
|
+
private _dataHandler;
|
|
14
|
+
constructor(initialVerbose: boolean);
|
|
15
|
+
/** Current verbose display state. */
|
|
16
|
+
get isVerbose(): boolean;
|
|
17
|
+
/** Whether the toggle listener is currently active. */
|
|
18
|
+
get isActive(): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Start listening for Tab keypress on stdin.
|
|
21
|
+
* Sets stdin to raw mode to capture individual keypresses.
|
|
22
|
+
* Shows a hint message about the toggle.
|
|
23
|
+
*
|
|
24
|
+
* No-op if stdin is not a TTY or if already active.
|
|
25
|
+
*/
|
|
26
|
+
start(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Stop listening and restore stdin to normal mode.
|
|
29
|
+
* Safe to call multiple times.
|
|
30
|
+
*/
|
|
31
|
+
stop(): void;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=verbose-toggle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verbose-toggle.d.ts","sourceRoot":"","sources":["../../src/utils/verbose-toggle.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAyC;gBAEjD,cAAc,EAAE,OAAO;IAInC,qCAAqC;IACrC,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,uDAAuD;IACvD,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;;;;OAMG;IACH,KAAK,IAAI,IAAI;IAoCb;;;OAGG;IACH,IAAI,IAAI,IAAI;CAwBb"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime verbose toggle for task execution.
|
|
3
|
+
*
|
|
4
|
+
* Listens for Tab keypress on process.stdin to toggle verbose display on/off.
|
|
5
|
+
* When verbose is on, tool-use activity lines from stream-json are displayed.
|
|
6
|
+
* When verbose is off, they are suppressed (but data is still captured).
|
|
7
|
+
*
|
|
8
|
+
* Requires a TTY stdin. Silently skips setup when stdin is not a TTY (e.g., piped input).
|
|
9
|
+
*/
|
|
10
|
+
import { logger } from './logger.js';
|
|
11
|
+
export class VerboseToggle {
|
|
12
|
+
_verbose;
|
|
13
|
+
_active = false;
|
|
14
|
+
_dataHandler = null;
|
|
15
|
+
constructor(initialVerbose) {
|
|
16
|
+
this._verbose = initialVerbose;
|
|
17
|
+
}
|
|
18
|
+
/** Current verbose display state. */
|
|
19
|
+
get isVerbose() {
|
|
20
|
+
return this._verbose;
|
|
21
|
+
}
|
|
22
|
+
/** Whether the toggle listener is currently active. */
|
|
23
|
+
get isActive() {
|
|
24
|
+
return this._active;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Start listening for Tab keypress on stdin.
|
|
28
|
+
* Sets stdin to raw mode to capture individual keypresses.
|
|
29
|
+
* Shows a hint message about the toggle.
|
|
30
|
+
*
|
|
31
|
+
* No-op if stdin is not a TTY or if already active.
|
|
32
|
+
*/
|
|
33
|
+
start() {
|
|
34
|
+
if (this._active)
|
|
35
|
+
return;
|
|
36
|
+
if (!process.stdin.isTTY)
|
|
37
|
+
return;
|
|
38
|
+
try {
|
|
39
|
+
process.stdin.setRawMode(true);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Cannot set raw mode — skip
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
process.stdin.resume();
|
|
46
|
+
this._dataHandler = (data) => {
|
|
47
|
+
for (let i = 0; i < data.length; i++) {
|
|
48
|
+
const byte = data[i];
|
|
49
|
+
if (byte === 0x09) {
|
|
50
|
+
// Tab key
|
|
51
|
+
this._verbose = !this._verbose;
|
|
52
|
+
const state = this._verbose ? 'on' : 'off';
|
|
53
|
+
logger.dim(` [verbose: ${state}]`);
|
|
54
|
+
}
|
|
55
|
+
else if (byte === 0x03) {
|
|
56
|
+
// Ctrl+C — re-emit SIGINT so the shutdown handler catches it
|
|
57
|
+
process.emit('SIGINT');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
process.stdin.on('data', this._dataHandler);
|
|
62
|
+
this._active = true;
|
|
63
|
+
// Show toggle hint
|
|
64
|
+
logger.dim(' Press Tab to toggle verbose mode');
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Stop listening and restore stdin to normal mode.
|
|
68
|
+
* Safe to call multiple times.
|
|
69
|
+
*/
|
|
70
|
+
stop() {
|
|
71
|
+
if (!this._active)
|
|
72
|
+
return;
|
|
73
|
+
if (this._dataHandler) {
|
|
74
|
+
process.stdin.off('data', this._dataHandler);
|
|
75
|
+
this._dataHandler = null;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
if (process.stdin.isTTY) {
|
|
79
|
+
process.stdin.setRawMode(false);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// Ignore — stdin may already be closed
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
process.stdin.pause();
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Ignore
|
|
90
|
+
}
|
|
91
|
+
this._active = false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=verbose-toggle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verbose-toggle.js","sourceRoot":"","sources":["../../src/utils/verbose-toggle.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,OAAO,aAAa;IAChB,QAAQ,CAAU;IAClB,OAAO,GAAG,KAAK,CAAC;IAChB,YAAY,GAAoC,IAAI,CAAC;IAE7D,YAAY,cAAuB;QACjC,IAAI,CAAC,QAAQ,GAAG,cAAc,CAAC;IACjC,CAAC;IAED,qCAAqC;IACrC,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,uDAAuD;IACvD,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED;;;;;;OAMG;IACH,KAAK;QACH,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK;YAAE,OAAO;QAEjC,IAAI,CAAC;YACH,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,6BAA6B;YAC7B,OAAO;QACT,CAAC;QAED,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAEvB,IAAI,CAAC,YAAY,GAAG,CAAC,IAAY,EAAE,EAAE;YACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACrC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;gBAErB,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;oBAClB,UAAU;oBACV,IAAI,CAAC,QAAQ,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC;oBAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;oBAC3C,MAAM,CAAC,GAAG,CAAC,eAAe,KAAK,GAAG,CAAC,CAAC;gBACtC,CAAC;qBAAM,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;oBACzB,6DAA6D;oBAC7D,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACzB,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QAEF,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAC5C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QAEpB,mBAAmB;QACnB,MAAM,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;IACnD,CAAC;IAED;;;OAGG;IACH,IAAI;QACF,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAE1B,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;YAC7C,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC3B,CAAC;QAED,IAAI,CAAC;YACH,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;gBACxB,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,uCAAuC;QACzC,CAAC;QAED,IAAI,CAAC;YACH,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;IACvB,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,230 @@
|
|
|
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 {
|
|
10
|
+
getConfigPath,
|
|
11
|
+
getModel,
|
|
12
|
+
getEffort,
|
|
13
|
+
getModelShortName,
|
|
14
|
+
validateConfig,
|
|
15
|
+
ConfigValidationError,
|
|
16
|
+
resetConfigCache,
|
|
17
|
+
} from '../utils/config.js';
|
|
18
|
+
import { DEFAULT_CONFIG } from '../types/config.js';
|
|
19
|
+
|
|
20
|
+
interface ConfigCommandOptions {
|
|
21
|
+
reset?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Load the config documentation markdown from src/prompts/config-docs.md.
|
|
26
|
+
* Resolved relative to this file's location in the dist/ tree.
|
|
27
|
+
*/
|
|
28
|
+
function loadConfigDocs(): string {
|
|
29
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
30
|
+
const __dirname = path.dirname(__filename);
|
|
31
|
+
// From dist/commands/config.js -> ../../src/prompts/config-docs.md
|
|
32
|
+
const docsPath = path.join(__dirname, '..', '..', 'src', 'prompts', 'config-docs.md');
|
|
33
|
+
return fs.readFileSync(docsPath, 'utf-8');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Read the current user config file contents, or a message indicating no file exists.
|
|
38
|
+
*/
|
|
39
|
+
function getCurrentConfigState(configPath: string): string {
|
|
40
|
+
if (!fs.existsSync(configPath)) {
|
|
41
|
+
return 'No config file exists yet. All settings use defaults. The file will be created at: ' + configPath;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
45
|
+
return `Current config file (${configPath}):\n\`\`\`json\n${content}\`\`\``;
|
|
46
|
+
} catch {
|
|
47
|
+
return 'Config file exists but could not be read: ' + configPath;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Build the system prompt for the config editing Claude session.
|
|
53
|
+
*/
|
|
54
|
+
function buildConfigSystemPrompt(configDocs: string, configState: string): string {
|
|
55
|
+
return [
|
|
56
|
+
'You are helping the user edit their RAF configuration.',
|
|
57
|
+
'You have full permission to read and write ~/.raf/raf.config.json.',
|
|
58
|
+
'',
|
|
59
|
+
'# Current Config State',
|
|
60
|
+
configState,
|
|
61
|
+
'',
|
|
62
|
+
'# Config Documentation',
|
|
63
|
+
configDocs,
|
|
64
|
+
].join('\n');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Validate the config file after the Claude session ends and report results.
|
|
69
|
+
*/
|
|
70
|
+
function postSessionValidation(configPath: string): void {
|
|
71
|
+
if (!fs.existsSync(configPath)) {
|
|
72
|
+
logger.info('No config file exists — using all defaults.');
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
78
|
+
const parsed: unknown = JSON.parse(content);
|
|
79
|
+
validateConfig(parsed);
|
|
80
|
+
logger.success('Config updated successfully.');
|
|
81
|
+
|
|
82
|
+
// Show a summary of what's set
|
|
83
|
+
const userConfig = parsed as Record<string, unknown>;
|
|
84
|
+
const keys = Object.keys(userConfig);
|
|
85
|
+
if (keys.length > 0) {
|
|
86
|
+
logger.info(`Custom settings: ${keys.join(', ')}`);
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
if (error instanceof ConfigValidationError) {
|
|
90
|
+
logger.warn(`Config validation warning: ${error.message}`);
|
|
91
|
+
logger.warn('The file was not deleted — you can fix it manually or run `raf config` again.');
|
|
92
|
+
} else if (error instanceof SyntaxError) {
|
|
93
|
+
logger.warn('Config file contains invalid JSON.');
|
|
94
|
+
logger.warn('The file was not deleted — you can fix it manually or run `raf config` again.');
|
|
95
|
+
} else {
|
|
96
|
+
logger.warn(`Could not validate config: ${error}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Prompt the user for Y/N confirmation via readline.
|
|
103
|
+
*/
|
|
104
|
+
async function confirm(message: string): Promise<boolean> {
|
|
105
|
+
const rl = readline.createInterface({
|
|
106
|
+
input: process.stdin,
|
|
107
|
+
output: process.stdout,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return new Promise((resolve) => {
|
|
111
|
+
rl.question(message, (answer) => {
|
|
112
|
+
rl.close();
|
|
113
|
+
resolve(answer.trim().toLowerCase() === 'y');
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function createConfigCommand(): Command {
|
|
119
|
+
const command = new Command('config')
|
|
120
|
+
.description('View and edit RAF configuration with Claude')
|
|
121
|
+
.argument('[prompt...]', 'Optional initial prompt for the config session')
|
|
122
|
+
.option('--reset', 'Delete config file and restore all defaults')
|
|
123
|
+
.action(async (promptParts: string[], options: ConfigCommandOptions) => {
|
|
124
|
+
if (options.reset) {
|
|
125
|
+
await handleReset();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const initialPrompt = promptParts.length > 0 ? promptParts.join(' ') : undefined;
|
|
130
|
+
await runConfigSession(initialPrompt);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return command;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function handleReset(): Promise<void> {
|
|
137
|
+
const configPath = getConfigPath();
|
|
138
|
+
|
|
139
|
+
if (!fs.existsSync(configPath)) {
|
|
140
|
+
logger.info('No config file exists — already using defaults.');
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const confirmed = await confirm(
|
|
145
|
+
'This will delete ~/.raf/raf.config.json and restore all defaults. Continue? [y/N] '
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
if (!confirmed) {
|
|
149
|
+
logger.info('Cancelled.');
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
fs.unlinkSync(configPath);
|
|
154
|
+
logger.success('Config file deleted. All settings restored to defaults.');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function runConfigSession(initialPrompt?: string): Promise<void> {
|
|
158
|
+
const configPath = getConfigPath();
|
|
159
|
+
|
|
160
|
+
// Try to load config, but fall back to defaults if it's broken
|
|
161
|
+
// This allows raf config to be used to fix a broken config file
|
|
162
|
+
let model: string;
|
|
163
|
+
let effort: string;
|
|
164
|
+
let configError: Error | null = null;
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
model = getModel('config');
|
|
168
|
+
effort = getEffort('config');
|
|
169
|
+
} catch (error) {
|
|
170
|
+
// Config file has errors - fall back to defaults so the session can launch
|
|
171
|
+
configError = error instanceof Error ? error : new Error(String(error));
|
|
172
|
+
model = DEFAULT_CONFIG.models.config;
|
|
173
|
+
effort = DEFAULT_CONFIG.effort.config;
|
|
174
|
+
// Clear the cached config so subsequent calls don't use the broken cache
|
|
175
|
+
resetConfigCache();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Warn user if config has errors, before starting the session
|
|
179
|
+
if (configError) {
|
|
180
|
+
logger.warn(`Config file has errors, using defaults: ${configError.message}`);
|
|
181
|
+
logger.warn('Fix the config in this session or run `raf config --reset` to start fresh.');
|
|
182
|
+
logger.newline();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Set effort level env var for the Claude session
|
|
186
|
+
process.env['CLAUDE_CODE_EFFORT_LEVEL'] = effort;
|
|
187
|
+
|
|
188
|
+
// Load config docs
|
|
189
|
+
let configDocs: string;
|
|
190
|
+
try {
|
|
191
|
+
configDocs = loadConfigDocs();
|
|
192
|
+
} catch (error) {
|
|
193
|
+
logger.error(`Failed to load config documentation: ${error}`);
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Build system prompt
|
|
198
|
+
const configState = getCurrentConfigState(configPath);
|
|
199
|
+
const systemPrompt = buildConfigSystemPrompt(configDocs, configState);
|
|
200
|
+
|
|
201
|
+
// Build user message
|
|
202
|
+
const userMessage = initialPrompt
|
|
203
|
+
?? 'Show me my current config and help me make changes.';
|
|
204
|
+
|
|
205
|
+
// Set up Claude runner
|
|
206
|
+
const claudeRunner = new ClaudeRunner({ model });
|
|
207
|
+
shutdownHandler.init();
|
|
208
|
+
shutdownHandler.registerClaudeRunner(claudeRunner);
|
|
209
|
+
|
|
210
|
+
const configModel = getModelShortName(model);
|
|
211
|
+
logger.info(`Starting config session with ${configModel}...`);
|
|
212
|
+
logger.newline();
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const exitCode = await claudeRunner.runInteractive(systemPrompt, userMessage, {
|
|
216
|
+
dangerouslySkipPermissions: true,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
if (exitCode !== 0) {
|
|
220
|
+
logger.warn(`Claude exited with code ${exitCode}`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Post-session validation
|
|
224
|
+
logger.newline();
|
|
225
|
+
postSessionValidation(configPath);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
logger.error(`Config session failed: ${error}`);
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
}
|
package/src/commands/do.ts
CHANGED
|
@@ -13,14 +13,18 @@ import { getRafDir, extractProjectNumber, extractProjectName, extractTaskNameFro
|
|
|
13
13
|
import { pickPendingProject, getPendingProjects, getPendingWorktreeProjects } from '../ui/project-picker.js';
|
|
14
14
|
import type { PendingProjectInfo } from '../ui/project-picker.js';
|
|
15
15
|
import { logger } from '../utils/logger.js';
|
|
16
|
-
import { getConfig } from '../utils/config.js';
|
|
16
|
+
import { getConfig, getEffort, getWorktreeDefault, getModel, getModelShortName } from '../utils/config.js';
|
|
17
17
|
import { createTaskTimer, formatElapsedTime } from '../utils/timer.js';
|
|
18
18
|
import { createStatusLine } from '../utils/status-line.js';
|
|
19
19
|
import {
|
|
20
20
|
formatProjectHeader,
|
|
21
21
|
formatSummary,
|
|
22
22
|
formatTaskProgress,
|
|
23
|
+
formatTaskTokenSummary,
|
|
24
|
+
formatTokenTotalSummary,
|
|
23
25
|
} from '../utils/terminal-symbols.js';
|
|
26
|
+
import { TokenTracker } from '../utils/token-tracker.js';
|
|
27
|
+
import { VerboseToggle } from '../utils/verbose-toggle.js';
|
|
24
28
|
import {
|
|
25
29
|
deriveProjectState,
|
|
26
30
|
discoverProjects,
|
|
@@ -134,12 +138,12 @@ export function createDoCommand(): Command {
|
|
|
134
138
|
async function runDoCommand(projectIdentifierArg: string | undefined, options: DoCommandOptions): Promise<void> {
|
|
135
139
|
const rafDir = getRafDir();
|
|
136
140
|
let projectIdentifier = projectIdentifierArg;
|
|
137
|
-
let worktreeMode = options.worktree ??
|
|
141
|
+
let worktreeMode = options.worktree ?? getWorktreeDefault();
|
|
138
142
|
|
|
139
143
|
// Validate and resolve model option
|
|
140
144
|
let model: string;
|
|
141
145
|
try {
|
|
142
|
-
model = resolveModelOption(options.model as string | undefined, options.sonnet);
|
|
146
|
+
model = resolveModelOption(options.model as string | undefined, options.sonnet, 'execute');
|
|
143
147
|
} catch (error) {
|
|
144
148
|
logger.error((error as Error).message);
|
|
145
149
|
process.exit(1);
|
|
@@ -711,6 +715,13 @@ async function executeSingleProject(
|
|
|
711
715
|
shutdownHandler.init();
|
|
712
716
|
shutdownHandler.registerClaudeRunner(claudeRunner);
|
|
713
717
|
|
|
718
|
+
// Initialize token tracker for usage reporting
|
|
719
|
+
const tokenTracker = new TokenTracker();
|
|
720
|
+
|
|
721
|
+
// Set up runtime verbose toggle (Tab key to toggle during execution)
|
|
722
|
+
const verboseToggle = new VerboseToggle(verbose);
|
|
723
|
+
shutdownHandler.onShutdown(() => verboseToggle.stop());
|
|
724
|
+
|
|
714
725
|
// Start project timer
|
|
715
726
|
const projectStartTime = Date.now();
|
|
716
727
|
|
|
@@ -816,6 +827,9 @@ async function executeSingleProject(
|
|
|
816
827
|
return lines.join('\n');
|
|
817
828
|
}
|
|
818
829
|
|
|
830
|
+
// Start verbose toggle listener (Tab key)
|
|
831
|
+
verboseToggle.start();
|
|
832
|
+
|
|
819
833
|
let task = getNextTaskToProcess(state);
|
|
820
834
|
|
|
821
835
|
while (task) {
|
|
@@ -891,12 +905,19 @@ async function executeSingleProject(
|
|
|
891
905
|
let attempts = 0;
|
|
892
906
|
let lastOutput = '';
|
|
893
907
|
let failureReason = '';
|
|
908
|
+
// Collect usage data from all attempts (for accurate token tracking across retries)
|
|
909
|
+
const attemptUsageData: import('../types/config.js').UsageData[] = [];
|
|
894
910
|
// Track failure history for each attempt (attempt number -> reason)
|
|
895
911
|
const failureHistory: Array<{ attempt: number; reason: string }> = [];
|
|
896
912
|
|
|
897
913
|
// Set up timer for elapsed time tracking
|
|
898
914
|
const statusLine = createStatusLine();
|
|
899
915
|
const timer = createTaskTimer(verbose ? undefined : (elapsed) => {
|
|
916
|
+
// When verbose is toggled ON at runtime, clear the status line and skip updates
|
|
917
|
+
if (verboseToggle.isVerbose) {
|
|
918
|
+
statusLine.clear();
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
900
921
|
// Show running status with task name and timer (updates in place)
|
|
901
922
|
statusLine.update(formatTaskProgress(taskNumber, totalTasks, 'running', displayName, elapsed, taskId));
|
|
902
923
|
});
|
|
@@ -940,11 +961,23 @@ async function executeSingleProject(
|
|
|
940
961
|
} : undefined;
|
|
941
962
|
|
|
942
963
|
// Run Claude (use worktree root as cwd if in worktree mode)
|
|
964
|
+
const executeEffort = getEffort('execute');
|
|
965
|
+
const runnerOptions = {
|
|
966
|
+
timeout,
|
|
967
|
+
outcomeFilePath,
|
|
968
|
+
commitContext,
|
|
969
|
+
cwd: worktreeCwd,
|
|
970
|
+
effortLevel: executeEffort,
|
|
971
|
+
verboseCheck: () => verboseToggle.isVerbose,
|
|
972
|
+
};
|
|
943
973
|
const result = verbose
|
|
944
|
-
? await claudeRunner.runVerbose(prompt,
|
|
945
|
-
: await claudeRunner.run(prompt,
|
|
974
|
+
? await claudeRunner.runVerbose(prompt, runnerOptions)
|
|
975
|
+
: await claudeRunner.run(prompt, runnerOptions);
|
|
946
976
|
|
|
947
977
|
lastOutput = result.output;
|
|
978
|
+
if (result.usageData) {
|
|
979
|
+
attemptUsageData.push(result.usageData);
|
|
980
|
+
}
|
|
948
981
|
|
|
949
982
|
// Parse result
|
|
950
983
|
const parsed = parseOutput(result.output);
|
|
@@ -1059,6 +1092,13 @@ Task completed. No detailed report provided.
|
|
|
1059
1092
|
// Minimal mode: show completed task line
|
|
1060
1093
|
logger.info(formatTaskProgress(taskNumber, totalTasks, 'completed', displayName, elapsedMs, task.id));
|
|
1061
1094
|
}
|
|
1095
|
+
|
|
1096
|
+
// Track and display token usage for this task
|
|
1097
|
+
if (attemptUsageData.length > 0) {
|
|
1098
|
+
const entry = tokenTracker.addTask(task.id, attemptUsageData);
|
|
1099
|
+
logger.dim(formatTaskTokenSummary(entry, (u) => tokenTracker.calculateCost(u)));
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1062
1102
|
completedInSession.add(task.id);
|
|
1063
1103
|
} else {
|
|
1064
1104
|
// Stash any uncommitted changes on complete failure
|
|
@@ -1074,12 +1114,19 @@ Task completed. No detailed report provided.
|
|
|
1074
1114
|
|
|
1075
1115
|
if (verbose) {
|
|
1076
1116
|
logger.error(` Task ${taskLabel} failed: ${failureReason} (${elapsedFormatted})`);
|
|
1077
|
-
|
|
1117
|
+
const analysisModel = getModelShortName(getModel('failureAnalysis'));
|
|
1118
|
+
logger.info(` Analyzing failure with ${analysisModel}...`);
|
|
1078
1119
|
} else {
|
|
1079
1120
|
// Minimal mode: show failed task line
|
|
1080
1121
|
logger.info(formatTaskProgress(taskNumber, totalTasks, 'failed', displayName, elapsedMs, task.id));
|
|
1081
1122
|
}
|
|
1082
1123
|
|
|
1124
|
+
// Track token usage even for failed tasks (partial data still useful for totals)
|
|
1125
|
+
if (attemptUsageData.length > 0) {
|
|
1126
|
+
const entry = tokenTracker.addTask(task.id, attemptUsageData);
|
|
1127
|
+
logger.dim(formatTaskTokenSummary(entry, (u) => tokenTracker.calculateCost(u)));
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1083
1130
|
// Analyze failure and generate structured report
|
|
1084
1131
|
const analysisReport = await analyzeFailure(lastOutput, failureReason, task.id);
|
|
1085
1132
|
|
|
@@ -1114,6 +1161,9 @@ ${stashName ? `- Stash: ${stashName}` : ''}
|
|
|
1114
1161
|
task = getNextTaskToProcess(state);
|
|
1115
1162
|
}
|
|
1116
1163
|
|
|
1164
|
+
// Stop verbose toggle listener before summary output
|
|
1165
|
+
verboseToggle.stop();
|
|
1166
|
+
|
|
1117
1167
|
// Ensure context is cleared for summary
|
|
1118
1168
|
logger.clearContext();
|
|
1119
1169
|
|
|
@@ -1182,6 +1232,14 @@ ${stashName ? `- Stash: ${stashName}` : ''}
|
|
|
1182
1232
|
}
|
|
1183
1233
|
}
|
|
1184
1234
|
|
|
1235
|
+
// Show token usage summary if any tasks reported usage data
|
|
1236
|
+
const trackerEntries = tokenTracker.getEntries();
|
|
1237
|
+
if (trackerEntries.length > 0) {
|
|
1238
|
+
logger.newline();
|
|
1239
|
+
const totals = tokenTracker.getTotals();
|
|
1240
|
+
logger.dim(formatTokenTotalSummary(totals.usage, totals.cost));
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1185
1243
|
// Show retry history for tasks that had failures (even if eventually successful)
|
|
1186
1244
|
if (projectRetryHistory.length > 0) {
|
|
1187
1245
|
logger.newline();
|
package/src/commands/plan.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
resolveModelOption,
|
|
16
16
|
} from '../utils/validation.js';
|
|
17
17
|
import { logger } from '../utils/logger.js';
|
|
18
|
+
import { getWorktreeDefault, getModel, getModelShortName } from '../utils/config.js';
|
|
18
19
|
import { generateProjectNames } from '../utils/name-generator.js';
|
|
19
20
|
import { pickProjectName } from '../ui/name-picker.js';
|
|
20
21
|
import {
|
|
@@ -73,14 +74,14 @@ export function createPlanCommand(): Command {
|
|
|
73
74
|
// Validate and resolve model option
|
|
74
75
|
let model: string;
|
|
75
76
|
try {
|
|
76
|
-
model = resolveModelOption(options.model, options.sonnet);
|
|
77
|
+
model = resolveModelOption(options.model, options.sonnet, 'plan');
|
|
77
78
|
} catch (error) {
|
|
78
79
|
logger.error((error as Error).message);
|
|
79
80
|
process.exit(1);
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
const autoMode = options.auto ?? false;
|
|
83
|
-
const worktreeMode = options.worktree ??
|
|
84
|
+
const worktreeMode = options.worktree ?? getWorktreeDefault();
|
|
84
85
|
|
|
85
86
|
if (options.amend) {
|
|
86
87
|
if (!projectName) {
|
|
@@ -154,7 +155,8 @@ async function runPlanCommand(projectName?: string, model?: string, autoMode: bo
|
|
|
154
155
|
// Get or generate project name
|
|
155
156
|
let finalProjectName = projectName;
|
|
156
157
|
if (!finalProjectName) {
|
|
157
|
-
|
|
158
|
+
const nameModel = getModelShortName(getModel('nameGeneration'));
|
|
159
|
+
logger.info(`Generating project name suggestions with ${nameModel}...`);
|
|
158
160
|
const suggestedNames = await generateProjectNames(cleanInput);
|
|
159
161
|
logger.newline();
|
|
160
162
|
|