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.
Files changed (135) 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/RAF/ahtahs-token-reaper/decisions.md +37 -0
  32. package/RAF/ahtahs-token-reaper/input.md +20 -0
  33. package/RAF/ahtahs-token-reaper/outcomes/01-extend-token-tracker-data-model.md +42 -0
  34. package/RAF/ahtahs-token-reaper/outcomes/02-accumulate-usage-in-retry-loop.md +31 -0
  35. package/RAF/ahtahs-token-reaper/outcomes/03-per-attempt-display-formatting.md +60 -0
  36. package/RAF/ahtahs-token-reaper/outcomes/04-add-model-name-to-claude-call-logs.md +57 -0
  37. package/RAF/ahtahs-token-reaper/outcomes/05-handle-invalid-config-in-raf-config.md +46 -0
  38. package/RAF/ahtahs-token-reaper/outcomes/06-fix-verbose-toggle-timer-display.md +38 -0
  39. package/RAF/ahtahs-token-reaper/plans/01-extend-token-tracker-data-model.md +36 -0
  40. package/RAF/ahtahs-token-reaper/plans/02-accumulate-usage-in-retry-loop.md +36 -0
  41. package/RAF/ahtahs-token-reaper/plans/03-per-attempt-display-formatting.md +43 -0
  42. package/RAF/ahtahs-token-reaper/plans/04-add-model-name-to-claude-call-logs.md +38 -0
  43. package/RAF/ahtahs-token-reaper/plans/05-handle-invalid-config-in-raf-config.md +36 -0
  44. package/RAF/ahtahs-token-reaper/plans/06-fix-verbose-toggle-timer-display.md +40 -0
  45. package/README.md +34 -0
  46. package/dist/commands/config.d.ts +3 -0
  47. package/dist/commands/config.d.ts.map +1 -0
  48. package/dist/commands/config.js +195 -0
  49. package/dist/commands/config.js.map +1 -0
  50. package/dist/commands/do.d.ts.map +1 -1
  51. package/dist/commands/do.js +55 -7
  52. package/dist/commands/do.js.map +1 -1
  53. package/dist/commands/plan.d.ts.map +1 -1
  54. package/dist/commands/plan.js +5 -3
  55. package/dist/commands/plan.js.map +1 -1
  56. package/dist/core/claude-runner.d.ts +19 -2
  57. package/dist/core/claude-runner.d.ts.map +1 -1
  58. package/dist/core/claude-runner.js +43 -96
  59. package/dist/core/claude-runner.js.map +1 -1
  60. package/dist/core/failure-analyzer.d.ts.map +1 -1
  61. package/dist/core/failure-analyzer.js +6 -3
  62. package/dist/core/failure-analyzer.js.map +1 -1
  63. package/dist/core/git.d.ts.map +1 -1
  64. package/dist/core/git.js +10 -3
  65. package/dist/core/git.js.map +1 -1
  66. package/dist/core/pull-request.d.ts +1 -1
  67. package/dist/core/pull-request.d.ts.map +1 -1
  68. package/dist/core/pull-request.js +9 -4
  69. package/dist/core/pull-request.js.map +1 -1
  70. package/dist/index.js +2 -0
  71. package/dist/index.js.map +1 -1
  72. package/dist/parsers/stream-renderer.d.ts +16 -1
  73. package/dist/parsers/stream-renderer.d.ts.map +1 -1
  74. package/dist/parsers/stream-renderer.js +34 -4
  75. package/dist/parsers/stream-renderer.js.map +1 -1
  76. package/dist/prompts/execution.d.ts.map +1 -1
  77. package/dist/prompts/execution.js +11 -1
  78. package/dist/prompts/execution.js.map +1 -1
  79. package/dist/types/config.d.ts +95 -4
  80. package/dist/types/config.d.ts.map +1 -1
  81. package/dist/types/config.js +63 -3
  82. package/dist/types/config.js.map +1 -1
  83. package/dist/utils/config.d.ts +65 -7
  84. package/dist/utils/config.d.ts.map +1 -1
  85. package/dist/utils/config.js +297 -21
  86. package/dist/utils/config.js.map +1 -1
  87. package/dist/utils/name-generator.d.ts +3 -7
  88. package/dist/utils/name-generator.d.ts.map +1 -1
  89. package/dist/utils/name-generator.js +75 -61
  90. package/dist/utils/name-generator.js.map +1 -1
  91. package/dist/utils/terminal-symbols.d.ts +25 -0
  92. package/dist/utils/terminal-symbols.d.ts.map +1 -1
  93. package/dist/utils/terminal-symbols.js +87 -0
  94. package/dist/utils/terminal-symbols.js.map +1 -1
  95. package/dist/utils/token-tracker.d.ts +55 -0
  96. package/dist/utils/token-tracker.d.ts.map +1 -0
  97. package/dist/utils/token-tracker.js +142 -0
  98. package/dist/utils/token-tracker.js.map +1 -0
  99. package/dist/utils/validation.d.ts +5 -5
  100. package/dist/utils/validation.d.ts.map +1 -1
  101. package/dist/utils/validation.js +10 -6
  102. package/dist/utils/validation.js.map +1 -1
  103. package/dist/utils/verbose-toggle.d.ts +33 -0
  104. package/dist/utils/verbose-toggle.d.ts.map +1 -0
  105. package/dist/utils/verbose-toggle.js +94 -0
  106. package/dist/utils/verbose-toggle.js.map +1 -0
  107. package/package.json +1 -1
  108. package/src/commands/config.ts +230 -0
  109. package/src/commands/do.ts +64 -6
  110. package/src/commands/plan.ts +5 -3
  111. package/src/core/claude-runner.ts +59 -115
  112. package/src/core/failure-analyzer.ts +6 -3
  113. package/src/core/git.ts +10 -3
  114. package/src/core/pull-request.ts +9 -4
  115. package/src/index.ts +2 -0
  116. package/src/parsers/stream-renderer.ts +54 -4
  117. package/src/prompts/config-docs.md +331 -0
  118. package/src/prompts/execution.ts +13 -1
  119. package/src/types/config.ts +156 -7
  120. package/src/utils/config.ts +357 -21
  121. package/src/utils/name-generator.ts +84 -71
  122. package/src/utils/terminal-symbols.ts +103 -0
  123. package/src/utils/token-tracker.ts +177 -0
  124. package/src/utils/validation.ts +15 -10
  125. package/src/utils/verbose-toggle.ts +103 -0
  126. package/tests/unit/claude-runner.test.ts +171 -7
  127. package/tests/unit/config-command.test.ts +242 -0
  128. package/tests/unit/config.test.ts +632 -30
  129. package/tests/unit/name-generator.test.ts +99 -75
  130. package/tests/unit/pull-request.test.ts +2 -0
  131. package/tests/unit/stream-renderer.test.ts +83 -0
  132. package/tests/unit/terminal-symbols.test.ts +245 -0
  133. package/tests/unit/timer-verbose-integration.test.ts +170 -0
  134. package/tests/unit/token-tracker.test.ts +685 -0
  135. 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
- declare const VALID_MODELS: readonly ["sonnet", "haiku", "opus"];
13
- export type ValidModelName = (typeof VALID_MODELS)[number];
14
- export declare function validateModelName(model: string): ValidModelName | null;
15
- export declare function resolveModelOption(model?: string, sonnet?: boolean): ValidModelName;
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":"AAKA,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,QAAA,MAAM,YAAY,sCAAuC,CAAC;AAE1D,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAMtE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,cAAc,CAsBnF"}
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"}
@@ -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 (VALID_MODELS.includes(normalized)) {
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: ${VALID_MODELS.join(', ')}`);
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 to opus
98
- return 'opus';
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":"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"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rafcode",
3
- "version": "2.1.1",
3
+ "version": "2.3.0",
4
4
  "description": "Automated Task Planning & Execution with Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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
+ }
@@ -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 ?? false;
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, { timeout, outcomeFilePath, commitContext, cwd: worktreeCwd, effortLevel: 'medium' })
945
- : await claudeRunner.run(prompt, { timeout, outcomeFilePath, commitContext, cwd: worktreeCwd, effortLevel: 'medium' });
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
- logger.info(' Analyzing failure...');
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();
@@ -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 ?? false;
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
- logger.info('Generating project name suggestions...');
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