rafcode 3.2.1 → 3.8.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 (200) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/CLAUDE.md +0 -1
  3. package/RAF/41-echo-chamber/decisions.md +13 -0
  4. package/RAF/41-echo-chamber/input.md +4 -0
  5. package/RAF/41-echo-chamber/outcomes/1-update-codex-model-defaults.md +24 -0
  6. package/RAF/41-echo-chamber/outcomes/2-e2e-test-codex-provider.md +74 -0
  7. package/RAF/41-echo-chamber/plans/1-update-codex-model-defaults.md +28 -0
  8. package/RAF/41-echo-chamber/plans/2-e2e-test-codex-provider.md +103 -0
  9. package/RAF/42-patch-parade/decisions.md +29 -0
  10. package/RAF/42-patch-parade/input.md +9 -0
  11. package/RAF/42-patch-parade/outcomes/1-fix-codex-model-resolution.md +36 -0
  12. package/RAF/42-patch-parade/outcomes/2-fix-provider-aware-name-generation.md +31 -0
  13. package/RAF/42-patch-parade/outcomes/3-fix-codex-error-event-rendering.md +32 -0
  14. package/RAF/42-patch-parade/outcomes/4-update-cli-help-docs.md +28 -0
  15. package/RAF/42-patch-parade/outcomes/5-update-default-codex-models-to-gpt-5-4.md +33 -0
  16. package/RAF/42-patch-parade/outcomes/6-unify-model-config-schema.md +89 -0
  17. package/RAF/42-patch-parade/plans/1-fix-codex-model-resolution.md +35 -0
  18. package/RAF/42-patch-parade/plans/2-fix-provider-aware-name-generation.md +38 -0
  19. package/RAF/42-patch-parade/plans/3-fix-codex-error-event-rendering.md +32 -0
  20. package/RAF/42-patch-parade/plans/4-update-cli-help-docs.md +31 -0
  21. package/RAF/42-patch-parade/plans/5-update-default-codex-models-to-gpt-5-4.md +35 -0
  22. package/RAF/42-patch-parade/plans/6-unify-model-config-schema.md +46 -0
  23. package/RAF/43-swiss-army/decisions.md +34 -0
  24. package/RAF/43-swiss-army/input.md +7 -0
  25. package/RAF/43-swiss-army/outcomes/1-fix-model-validation.md +21 -0
  26. package/RAF/43-swiss-army/outcomes/2-update-commit-format.md +31 -0
  27. package/RAF/43-swiss-army/outcomes/3-wire-reasoning-effort.md +28 -0
  28. package/RAF/43-swiss-army/outcomes/4-remove-provider-flag.md +27 -0
  29. package/RAF/43-swiss-army/outcomes/5-config-wizard-validation.md +23 -0
  30. package/RAF/43-swiss-army/outcomes/6-add-fast-mode.md +32 -0
  31. package/RAF/43-swiss-army/outcomes/7-config-preset.md +31 -0
  32. package/RAF/43-swiss-army/plans/1-fix-model-validation.md +38 -0
  33. package/RAF/43-swiss-army/plans/2-update-commit-format.md +46 -0
  34. package/RAF/43-swiss-army/plans/3-wire-reasoning-effort.md +39 -0
  35. package/RAF/43-swiss-army/plans/4-remove-provider-flag.md +43 -0
  36. package/RAF/43-swiss-army/plans/5-config-wizard-validation.md +42 -0
  37. package/RAF/43-swiss-army/plans/6-add-fast-mode.md +46 -0
  38. package/RAF/43-swiss-army/plans/7-config-preset.md +51 -0
  39. package/RAF/44-config-api-change/decisions.md +22 -0
  40. package/RAF/44-config-api-change/input.md +5 -0
  41. package/RAF/44-config-api-change/outcomes/1-restructure-config-subcommands.md +19 -0
  42. package/RAF/44-config-api-change/outcomes/2-move-preset-under-config.md +17 -0
  43. package/RAF/44-config-api-change/outcomes/3-update-existing-tests-for-config-api.md +14 -0
  44. package/RAF/44-config-api-change/outcomes/4-update-config-command-docs.md +11 -0
  45. package/RAF/44-config-api-change/outcomes/5-fix-codex-name-generation.md +18 -0
  46. package/RAF/44-config-api-change/plans/1-restructure-config-subcommands.md +37 -0
  47. package/RAF/44-config-api-change/plans/2-move-preset-under-config.md +38 -0
  48. package/RAF/44-config-api-change/plans/3-update-existing-tests-for-config-api.md +38 -0
  49. package/RAF/44-config-api-change/plans/4-update-config-command-docs.md +36 -0
  50. package/RAF/44-config-api-change/plans/5-fix-codex-name-generation.md +49 -0
  51. package/RAF/45-signal-cairn/decisions.md +7 -0
  52. package/RAF/45-signal-cairn/input.md +2 -0
  53. package/RAF/45-signal-cairn/outcomes/1-rename-provider-to-harness.md +19 -0
  54. package/RAF/45-signal-cairn/outcomes/2-normalize-model-display-names.md +18 -0
  55. package/RAF/45-signal-cairn/plans/1-rename-provider-to-harness.md +40 -0
  56. package/RAF/45-signal-cairn/plans/2-normalize-model-display-names.md +41 -0
  57. package/RAF/45-signal-lantern/decisions.md +10 -0
  58. package/RAF/45-signal-lantern/input.md +2 -0
  59. package/RAF/45-signal-lantern/outcomes/1-add-effort-and-fast-to-do-model-display.md +15 -0
  60. package/RAF/45-signal-lantern/outcomes/2-capture-codex-post-run-token-usage.md +15 -0
  61. package/RAF/45-signal-lantern/outcomes/3-show-codex-token-summaries-without-fake-cost.md +14 -0
  62. package/RAF/45-signal-lantern/plans/1-add-effort-and-fast-to-do-model-display.md +38 -0
  63. package/RAF/45-signal-lantern/plans/2-capture-codex-post-run-token-usage.md +37 -0
  64. package/RAF/45-signal-lantern/plans/3-show-codex-token-summaries-without-fake-cost.md +40 -0
  65. package/RAF/46-lantern-arc/decisions.md +19 -0
  66. package/RAF/46-lantern-arc/input.md +6 -0
  67. package/RAF/46-lantern-arc/outcomes/1-remove-spark-alias.md +16 -0
  68. package/RAF/46-lantern-arc/outcomes/2-clean-up-worktree-plan-command.md +30 -0
  69. package/RAF/46-lantern-arc/outcomes/3-fix-token-usage-accumulation.md +32 -0
  70. package/RAF/46-lantern-arc/outcomes/4-display-effort-in-compact-mode.md +22 -0
  71. package/RAF/46-lantern-arc/outcomes/5-codex-fast-mode-research.md +38 -0
  72. package/RAF/46-lantern-arc/outcomes/6-optimize-llm-prompts.md +39 -0
  73. package/RAF/46-lantern-arc/plans/1-remove-spark-alias.md +38 -0
  74. package/RAF/46-lantern-arc/plans/2-clean-up-worktree-plan-command.md +33 -0
  75. package/RAF/46-lantern-arc/plans/3-fix-token-usage-accumulation.md +33 -0
  76. package/RAF/46-lantern-arc/plans/4-display-effort-in-compact-mode.md +28 -0
  77. package/RAF/46-lantern-arc/plans/5-codex-fast-mode-research.md +34 -0
  78. package/RAF/46-lantern-arc/plans/6-optimize-llm-prompts.md +48 -0
  79. package/RAF/47-signal-trim/decisions.md +13 -0
  80. package/RAF/47-signal-trim/input.md +2 -0
  81. package/RAF/47-signal-trim/plans/1-remove-cache-from-status.md +73 -0
  82. package/README.md +47 -57
  83. package/dist/commands/config.d.ts.map +1 -1
  84. package/dist/commands/config.js +47 -49
  85. package/dist/commands/config.js.map +1 -1
  86. package/dist/commands/do.d.ts +2 -0
  87. package/dist/commands/do.d.ts.map +1 -1
  88. package/dist/commands/do.js +57 -44
  89. package/dist/commands/do.js.map +1 -1
  90. package/dist/commands/plan.d.ts.map +1 -1
  91. package/dist/commands/plan.js +36 -153
  92. package/dist/commands/plan.js.map +1 -1
  93. package/dist/commands/preset.d.ts +3 -0
  94. package/dist/commands/preset.d.ts.map +1 -0
  95. package/dist/commands/preset.js +158 -0
  96. package/dist/commands/preset.js.map +1 -0
  97. package/dist/core/claude-runner.d.ts +2 -0
  98. package/dist/core/claude-runner.d.ts.map +1 -1
  99. package/dist/core/claude-runner.js +36 -12
  100. package/dist/core/claude-runner.js.map +1 -1
  101. package/dist/core/codex-runner.d.ts +1 -0
  102. package/dist/core/codex-runner.d.ts.map +1 -1
  103. package/dist/core/codex-runner.js +26 -7
  104. package/dist/core/codex-runner.js.map +1 -1
  105. package/dist/core/failure-analyzer.js +2 -1
  106. package/dist/core/failure-analyzer.js.map +1 -1
  107. package/dist/core/git.d.ts +2 -2
  108. package/dist/core/git.d.ts.map +1 -1
  109. package/dist/core/git.js +53 -3
  110. package/dist/core/git.js.map +1 -1
  111. package/dist/core/pull-request.js +3 -3
  112. package/dist/core/pull-request.js.map +1 -1
  113. package/dist/core/runner-factory.d.ts +4 -4
  114. package/dist/core/runner-factory.d.ts.map +1 -1
  115. package/dist/core/runner-factory.js +8 -8
  116. package/dist/core/runner-factory.js.map +1 -1
  117. package/dist/core/runner-interface.d.ts +1 -1
  118. package/dist/core/runner-types.d.ts +17 -4
  119. package/dist/core/runner-types.d.ts.map +1 -1
  120. package/dist/parsers/codex-stream-renderer.d.ts +7 -0
  121. package/dist/parsers/codex-stream-renderer.d.ts.map +1 -1
  122. package/dist/parsers/codex-stream-renderer.js +37 -4
  123. package/dist/parsers/codex-stream-renderer.js.map +1 -1
  124. package/dist/prompts/amend.d.ts.map +1 -1
  125. package/dist/prompts/amend.js +29 -101
  126. package/dist/prompts/amend.js.map +1 -1
  127. package/dist/prompts/execution.d.ts.map +1 -1
  128. package/dist/prompts/execution.js +17 -34
  129. package/dist/prompts/execution.js.map +1 -1
  130. package/dist/prompts/planning.d.ts.map +1 -1
  131. package/dist/prompts/planning.js +21 -120
  132. package/dist/prompts/planning.js.map +1 -1
  133. package/dist/types/config.d.ts +33 -31
  134. package/dist/types/config.d.ts.map +1 -1
  135. package/dist/types/config.js +14 -28
  136. package/dist/types/config.js.map +1 -1
  137. package/dist/utils/config.d.ts +36 -16
  138. package/dist/utils/config.d.ts.map +1 -1
  139. package/dist/utils/config.js +209 -104
  140. package/dist/utils/config.js.map +1 -1
  141. package/dist/utils/name-generator.d.ts.map +1 -1
  142. package/dist/utils/name-generator.js +25 -12
  143. package/dist/utils/name-generator.js.map +1 -1
  144. package/dist/utils/terminal-symbols.d.ts +15 -2
  145. package/dist/utils/terminal-symbols.d.ts.map +1 -1
  146. package/dist/utils/terminal-symbols.js +36 -4
  147. package/dist/utils/terminal-symbols.js.map +1 -1
  148. package/dist/utils/token-tracker.d.ts +6 -1
  149. package/dist/utils/token-tracker.d.ts.map +1 -1
  150. package/dist/utils/token-tracker.js +84 -51
  151. package/dist/utils/token-tracker.js.map +1 -1
  152. package/dist/utils/validation.d.ts +1 -2
  153. package/dist/utils/validation.d.ts.map +1 -1
  154. package/dist/utils/validation.js +4 -25
  155. package/dist/utils/validation.js.map +1 -1
  156. package/package.json +1 -1
  157. package/src/commands/config.ts +60 -63
  158. package/src/commands/do.ts +63 -51
  159. package/src/commands/plan.ts +34 -165
  160. package/src/commands/preset.ts +186 -0
  161. package/src/core/claude-runner.ts +45 -5
  162. package/src/core/codex-runner.ts +32 -7
  163. package/src/core/failure-analyzer.ts +2 -1
  164. package/src/core/git.ts +57 -3
  165. package/src/core/pull-request.ts +3 -3
  166. package/src/core/runner-factory.ts +9 -9
  167. package/src/core/runner-interface.ts +1 -1
  168. package/src/core/runner-types.ts +17 -4
  169. package/src/parsers/codex-stream-renderer.ts +47 -4
  170. package/src/prompts/amend.ts +29 -101
  171. package/src/prompts/config-docs.md +206 -62
  172. package/src/prompts/execution.ts +17 -34
  173. package/src/prompts/planning.ts +21 -120
  174. package/src/types/config.ts +47 -58
  175. package/src/utils/config.ts +248 -115
  176. package/src/utils/name-generator.ts +29 -13
  177. package/src/utils/terminal-symbols.ts +46 -6
  178. package/src/utils/token-tracker.ts +96 -57
  179. package/src/utils/validation.ts +5 -30
  180. package/tests/unit/amend-prompt.test.ts +3 -2
  181. package/tests/unit/claude-runner-interactive.test.ts +21 -3
  182. package/tests/unit/claude-runner.test.ts +39 -0
  183. package/tests/unit/codex-runner.test.ts +163 -0
  184. package/tests/unit/codex-stream-renderer.test.ts +127 -0
  185. package/tests/unit/command-output.test.ts +57 -0
  186. package/tests/unit/commit-planning-artifacts-worktree.test.ts +24 -7
  187. package/tests/unit/commit-planning-artifacts.test.ts +26 -4
  188. package/tests/unit/config-command.test.ts +215 -303
  189. package/tests/unit/config.test.ts +319 -235
  190. package/tests/unit/dependency-integration.test.ts +27 -1
  191. package/tests/unit/do-model-display.test.ts +35 -0
  192. package/tests/unit/execution-prompt.test.ts +49 -19
  193. package/tests/unit/name-generator.test.ts +82 -12
  194. package/tests/unit/plan-command-auto-flag.test.ts +7 -10
  195. package/tests/unit/plan-command.test.ts +14 -17
  196. package/tests/unit/planning-prompt.test.ts +9 -8
  197. package/tests/unit/terminal-symbols.test.ts +94 -3
  198. package/tests/unit/token-tracker.test.ts +180 -1
  199. package/tests/unit/validation.test.ts +9 -41
  200. package/tests/unit/worktree-flag-override.test.ts +0 -186
@@ -4,6 +4,7 @@ import { execSync, spawn } from 'node:child_process';
4
4
  import { logger } from '../utils/logger.js';
5
5
  import { renderCodexStreamEvent } from '../parsers/codex-stream-renderer.js';
6
6
  import { getModel } from '../utils/config.js';
7
+ import { mergeUsageData } from '../utils/token-tracker.js';
7
8
  import type { ICliRunner } from './runner-interface.js';
8
9
  import type { RunnerOptions, RunnerConfig, RunResult } from './runner-types.js';
9
10
  import { createCompletionDetector } from './completion-detector.js';
@@ -36,9 +37,11 @@ export class CodexRunner implements ICliRunner {
36
37
  private activeProcess: pty.IPty | null = null;
37
38
  private killed = false;
38
39
  private model: string;
40
+ private reasoningEffort?: string;
39
41
 
40
42
  constructor(config: RunnerConfig = {}) {
41
- this.model = config.model ?? getModel('execute', 'codex');
43
+ this.model = config.model ?? getModel('execute').model;
44
+ this.reasoningEffort = config.reasoningEffort;
42
45
  }
43
46
 
44
47
  /**
@@ -54,7 +57,14 @@ export class CodexRunner implements ICliRunner {
54
57
 
55
58
  return new Promise((resolve) => {
56
59
  const combinedPrompt = buildCombinedPrompt(systemPrompt, userMessage);
57
- const args = ['-m', this.model, combinedPrompt];
60
+ const args = ['-m', this.model];
61
+
62
+ // Add reasoning effort via config override when configured
63
+ if (this.reasoningEffort) {
64
+ args.push('-c', `model_reasoning_effort="${this.reasoningEffort}"`);
65
+ }
66
+
67
+ args.push(combinedPrompt);
58
68
 
59
69
  logger.debug(`Starting interactive Codex session with model: ${this.model}`);
60
70
 
@@ -167,6 +177,7 @@ export class CodexRunner implements ICliRunner {
167
177
  let stderr = '';
168
178
  let timedOut = false;
169
179
  let contextOverflow = false;
180
+ let usageData: import('../types/config.js').UsageData | undefined;
170
181
 
171
182
  const codexPath = getCodexPath();
172
183
 
@@ -175,15 +186,23 @@ export class CodexRunner implements ICliRunner {
175
186
  logger.debug(`Codex path: ${codexPath}`);
176
187
 
177
188
  logger.debug('Spawning process...');
178
- const proc = spawn(codexPath, [
189
+ const execArgs = [
179
190
  'exec',
180
191
  '--full-auto',
181
192
  '--json',
182
193
  '--ephemeral',
183
194
  '-m',
184
195
  this.model,
185
- prompt,
186
- ], {
196
+ ];
197
+
198
+ // Add reasoning effort via config override when configured
199
+ if (this.reasoningEffort) {
200
+ execArgs.push('-c', `model_reasoning_effort="${this.reasoningEffort}"`);
201
+ }
202
+
203
+ execArgs.push(prompt);
204
+
205
+ const proc = spawn(codexPath, execArgs, {
187
206
  cwd,
188
207
  env: process.env,
189
208
  stdio: ['ignore', 'pipe', 'pipe'],
@@ -244,6 +263,10 @@ export class CodexRunner implements ICliRunner {
244
263
  }
245
264
  }
246
265
 
266
+ if (rendered.usageData) {
267
+ usageData = mergeUsageData(usageData, rendered.usageData);
268
+ }
269
+
247
270
  if (shouldDisplay() && rendered.display) {
248
271
  process.stdout.write(rendered.display);
249
272
  }
@@ -262,6 +285,9 @@ export class CodexRunner implements ICliRunner {
262
285
  if (rendered.textContent) {
263
286
  output += rendered.textContent;
264
287
  }
288
+ if (rendered.usageData) {
289
+ usageData = mergeUsageData(usageData, rendered.usageData);
290
+ }
265
291
  if (shouldDisplay() && rendered.display) {
266
292
  process.stdout.write(rendered.display);
267
293
  }
@@ -281,8 +307,7 @@ export class CodexRunner implements ICliRunner {
281
307
  exitCode: exitCode ?? (this.killed ? 130 : 1),
282
308
  timedOut,
283
309
  contextOverflow,
284
- // Codex does not provide usage data in the same format — omit gracefully
285
- usageData: undefined,
310
+ usageData,
286
311
  });
287
312
  });
288
313
  });
@@ -308,7 +308,8 @@ Respond with ONLY a markdown report in this exact format:
308
308
  const claudePath = getClaudePath();
309
309
 
310
310
  // Use configured model for failure analysis
311
- const failureModel = getModel('failureAnalysis');
311
+ const failureEntry = getModel('failureAnalysis');
312
+ const failureModel = failureEntry.model;
312
313
  const proc = spawn(claudePath, [
313
314
  '--model', failureModel,
314
315
  '--no-session-persistence',
package/src/core/git.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { execSync } from 'node:child_process';
2
+ import * as fs from 'node:fs';
2
3
  import * as path from 'node:path';
3
4
  import { logger } from '../utils/logger.js';
4
5
  import { extractProjectNumber, extractProjectName } from '../utils/paths.js';
@@ -213,10 +214,56 @@ export function isFileCommittedInHead(filePath: string): boolean {
213
214
  }
214
215
  }
215
216
 
217
+ /**
218
+ * Extract a concise description from input.md for use in plan commit messages.
219
+ * Reads the first non-empty line and truncates to ~70 chars.
220
+ * Falls back to the project name if input.md can't be read.
221
+ */
222
+ function extractPlanDescription(projectPath: string, fallback: string): string {
223
+ const inputPath = path.join(projectPath, 'input.md');
224
+ try {
225
+ const content = fs.readFileSync(inputPath, 'utf-8');
226
+ const firstLine = content.split('\n').find(line => line.trim().length > 0);
227
+ if (firstLine) {
228
+ // Strip leading markdown markers (-, *, [ ], [x], #, etc.)
229
+ let cleaned = firstLine.trim().replace(/^[-*#>\s]*(\[.\]\s*)?/, '').trim();
230
+ if (cleaned.length > 70) {
231
+ cleaned = cleaned.substring(0, 67) + '...';
232
+ }
233
+ if (cleaned.length > 0) {
234
+ return cleaned;
235
+ }
236
+ }
237
+ } catch {
238
+ // Can't read input.md, use fallback
239
+ }
240
+ return fallback;
241
+ }
242
+
243
+ /**
244
+ * Generate a description for amend commit messages.
245
+ * Uses the count and names of additional plan files if provided.
246
+ */
247
+ function extractAmendDescription(additionalFiles?: string[]): string {
248
+ if (!additionalFiles || additionalFiles.length === 0) {
249
+ return 'updated plans';
250
+ }
251
+ // Extract task names from plan file paths (e.g., "plans/3-fix-bug.md" → "fix-bug")
252
+ const taskNames = additionalFiles
253
+ .map(f => path.basename(f, '.md'))
254
+ .map(name => name.replace(/^\d+-/, ''))
255
+ .filter(name => name.length > 0);
256
+
257
+ if (taskNames.length <= 3) {
258
+ return taskNames.join(', ');
259
+ }
260
+ return `${taskNames.length} tasks`;
261
+ }
262
+
216
263
  /**
217
264
  * Commit planning artifacts (input.md and decisions.md) for a project.
218
- * Uses commit message format: RAF[NNN] Plan: project-name
219
- * For amendments: RAF[NNN] Amend: project-name
265
+ * Uses commit message format: RAF[project-name] Plan: description
266
+ * For amendments: RAF[project-name] Amend: description
220
267
  *
221
268
  * @param projectPath - Full path to the project folder (e.g., /path/to/RAF/017-decision-vault)
222
269
  * @param options - Optional settings
@@ -254,10 +301,17 @@ export async function commitPlanningArtifacts(projectPath: string, options?: { c
254
301
  const formatType = options?.isAmend ? 'amend' as const : 'plan' as const;
255
302
  const template = getCommitFormat(formatType);
256
303
  const commitPrefix = getCommitPrefix();
304
+
305
+ // Auto-generate description based on commit type
306
+ const description = options?.isAmend
307
+ ? extractAmendDescription(options?.additionalFiles)
308
+ : extractPlanDescription(projectPath, projectName);
309
+
257
310
  const commitMessage = renderCommitMessage(template, {
258
311
  prefix: commitPrefix,
259
- projectId: projectNumber,
312
+ projectId: projectName, // backwards compat: {projectId} resolves to projectName
260
313
  projectName,
314
+ description,
261
315
  });
262
316
 
263
317
  // Build list of files to stage (absolute paths)
@@ -3,7 +3,7 @@ import * as fs from 'node:fs';
3
3
  import * as os from 'node:os';
4
4
  import * as path from 'node:path';
5
5
  import { logger } from '../utils/logger.js';
6
- import { getModel, getModelShortName } from '../utils/config.js';
6
+ import { formatModelDisplay, getModel } from '../utils/config.js';
7
7
  import { extractProjectName, getInputPath, getDecisionsPath, getOutcomesDir, TASK_ID_PATTERN, numericFileSort } from '../utils/paths.js';
8
8
 
9
9
  export interface PrCreateResult {
@@ -279,7 +279,7 @@ Respond with ONLY the PR body in this exact format (no extra text, no code fence
279
279
  [1-3 bullet points describing how to verify these changes work correctly]`;
280
280
 
281
281
  try {
282
- const prModel = getModelShortName(getModel('prGeneration'));
282
+ const prModel = formatModelDisplay(getModel('prGeneration').model);
283
283
  logger.info(`Generating PR with ${prModel}...`);
284
284
  const body = await callClaudeForPrBody(prompt, timeoutMs);
285
285
  return body;
@@ -365,7 +365,7 @@ async function callClaudeForPrBody(prompt: string, timeoutMs: number): Promise<s
365
365
  let output = '';
366
366
  let stderr = '';
367
367
 
368
- const prModel = getModel('prGeneration');
368
+ const prModel = getModel('prGeneration').model;
369
369
  const proc = spawn(claudePath, [
370
370
  '--model', prModel,
371
371
  '--no-session-persistence',
@@ -1,35 +1,35 @@
1
- import type { HarnessProvider } from '../types/config.js';
1
+ import type { HarnessName } from '../types/config.js';
2
2
  import type { ICliRunner } from './runner-interface.js';
3
3
  import type { RunnerConfig } from './runner-types.js';
4
4
  import { ClaudeRunner } from './claude-runner.js';
5
5
  import { CodexRunner } from './codex-runner.js';
6
6
 
7
7
  /**
8
- * Create a CLI runner for the given provider configuration.
8
+ * Create a CLI runner for the given harness configuration.
9
9
  */
10
10
  export function createRunner(config: RunnerConfig = {}): ICliRunner {
11
- const provider = config.provider ?? 'claude';
11
+ const harness = config.harness ?? 'claude';
12
12
 
13
- switch (provider) {
13
+ switch (harness) {
14
14
  case 'claude':
15
15
  return new ClaudeRunner(config);
16
16
  case 'codex':
17
17
  return new CodexRunner(config);
18
18
  default:
19
- throw new Error(`Unknown provider: ${provider}`);
19
+ throw new Error(`Unknown harness: ${harness}`);
20
20
  }
21
21
  }
22
22
 
23
23
  /**
24
- * Get the binary name for a given provider.
24
+ * Get the binary name for a given harness.
25
25
  */
26
- export function getProviderBinaryName(provider: HarnessProvider): string {
27
- switch (provider) {
26
+ export function getHarnessBinaryName(harness: HarnessName): string {
27
+ switch (harness) {
28
28
  case 'claude':
29
29
  return 'claude';
30
30
  case 'codex':
31
31
  return 'codex';
32
32
  default:
33
- throw new Error(`Unknown provider: ${provider}`);
33
+ throw new Error(`Unknown harness: ${harness}`);
34
34
  }
35
35
  }
@@ -1,7 +1,7 @@
1
1
  import type { RunnerOptions, RunResult } from './runner-types.js';
2
2
 
3
3
  /**
4
- * Provider-agnostic CLI runner interface.
4
+ * Harness-agnostic CLI runner interface.
5
5
  * Both ClaudeRunner and CodexRunner implement this interface.
6
6
  */
7
7
  export interface ICliRunner {
@@ -1,4 +1,4 @@
1
- import type { UsageData, HarnessProvider } from '../types/config.js';
1
+ import type { UsageData, HarnessName } from '../types/config.js';
2
2
 
3
3
  /**
4
4
  * Options for a single runner execution (run, runVerbose, runInteractive, etc.).
@@ -52,14 +52,27 @@ export interface RunnerOptions {
52
52
  export interface RunnerConfig {
53
53
  /**
54
54
  * Model to use (e.g., opus, sonnet, haiku, gpt-5.4).
55
- * Default: provider-specific default.
55
+ * Default: harness-specific default.
56
56
  */
57
57
  model?: string;
58
58
  /**
59
- * CLI provider to use.
59
+ * CLI harness to use.
60
60
  * Default: 'claude'.
61
61
  */
62
- provider?: HarnessProvider;
62
+ harness?: HarnessName;
63
+ /**
64
+ * Reasoning effort level to pass to the CLI.
65
+ * Claude CLI: --effort <level>
66
+ * Codex CLI: -c model_reasoning_effort="<level>"
67
+ * Only included when explicitly set.
68
+ */
69
+ reasoningEffort?: string;
70
+ /**
71
+ * Enable fast mode for faster output.
72
+ * Claude CLI: --settings '{"fastMode": true}'
73
+ * Only included when explicitly set to true.
74
+ */
75
+ fast?: boolean;
63
76
  }
64
77
 
65
78
  /**
@@ -9,10 +9,13 @@
9
9
  * - TodoList: Task tracking (skipped)
10
10
  */
11
11
 
12
+ import type { UsageData } from '../types/config.js';
12
13
  import type { RenderResult } from './stream-renderer.js';
13
14
 
14
15
  export interface CodexEvent {
15
16
  type: string;
17
+ id?: string;
18
+ model?: string;
16
19
  /** Claude flat-format fields (AgentMessage) */
17
20
  content?: string;
18
21
  /** Claude flat-format fields (CommandExecution) */
@@ -32,9 +35,12 @@ export interface CodexEvent {
32
35
  exit_code?: number;
33
36
  path?: string;
34
37
  change_kind?: string;
38
+ message?: string;
35
39
  };
36
40
  /** Error message (for error / turn.failed events) */
37
41
  message?: string;
42
+ /** Nested error object (for turn.failed events) */
43
+ error?: { message?: string };
38
44
  /** Usage data (for turn.completed events) */
39
45
  usage?: {
40
46
  input_tokens?: number;
@@ -172,25 +178,62 @@ function renderItemCompleted(event: CodexEvent): RenderResult {
172
178
  textContent: '',
173
179
  };
174
180
  }
181
+ case 'error': {
182
+ const msg = item.message ?? 'Unknown error';
183
+ return {
184
+ display: ` ✗ Error: ${msg}\n`,
185
+ textContent: msg,
186
+ };
187
+ }
175
188
  default:
176
189
  return { display: '', textContent: '' };
177
190
  }
178
191
  }
179
192
 
180
193
  function renderTurnCompleted(event: CodexEvent): RenderResult {
194
+ const usageData = extractUsageData(event);
195
+
181
196
  if (event.usage) {
182
197
  const { input_tokens, output_tokens } = event.usage;
183
198
  const parts: string[] = [];
184
- if (input_tokens) parts.push(`in: ${input_tokens}`);
185
- if (output_tokens) parts.push(`out: ${output_tokens}`);
199
+ if (input_tokens !== undefined) parts.push(`in: ${input_tokens}`);
200
+ if (output_tokens !== undefined) parts.push(`out: ${output_tokens}`);
186
201
  if (parts.length > 0) {
187
202
  return {
188
203
  display: ` → Usage: ${parts.join(', ')}\n`,
189
204
  textContent: '',
205
+ usageData,
190
206
  };
191
207
  }
192
208
  }
193
- return { display: '', textContent: '' };
209
+ return { display: '', textContent: '', usageData };
210
+ }
211
+
212
+ function extractUsageData(event: CodexEvent): UsageData | undefined {
213
+ if (!event.usage) {
214
+ return undefined;
215
+ }
216
+
217
+ const modelId = event.model ?? 'codex';
218
+ const inputTokens = event.usage.input_tokens ?? 0;
219
+ const outputTokens = event.usage.output_tokens ?? 0;
220
+
221
+ return {
222
+ inputTokens,
223
+ outputTokens,
224
+ cacheReadInputTokens: 0,
225
+ cacheCreationInputTokens: 0,
226
+ modelUsage: {
227
+ [modelId]: {
228
+ inputTokens,
229
+ outputTokens,
230
+ cacheReadInputTokens: 0,
231
+ cacheCreationInputTokens: 0,
232
+ costUsd: null,
233
+ },
234
+ },
235
+ totalCostUsd: null,
236
+ };
194
237
  }
195
238
 
196
239
  function renderError(event: CodexEvent): RenderResult {
@@ -202,7 +245,7 @@ function renderError(event: CodexEvent): RenderResult {
202
245
  }
203
246
 
204
247
  function renderTurnFailed(event: CodexEvent): RenderResult {
205
- const msg = event.message ?? 'Turn failed';
248
+ const msg = event.error?.message ?? event.message ?? 'Turn failed';
206
249
  return {
207
250
  display: ` ✗ Failed: ${msg}\n`,
208
251
  textContent: msg,
@@ -56,15 +56,14 @@ export function getAmendPrompt(params: AmendPromptParams): AmendPromptResult {
56
56
  ? modifiableTasks.map((t) => `- Task ${t.id}: ${t.taskName}`).join('\n')
57
57
  : '(none)';
58
58
 
59
- const systemPrompt = `You are a project planning assistant for RAF (Ralph's Automation Framework). Your task is to ADD NEW TASKS or MODIFY PENDING tasks in an existing project.
59
+ const systemPrompt = `You are a project planning assistant for RAF (Ralph's Automation Framework). Add new tasks or modify pending tasks in an existing project.
60
60
 
61
- ## IMPORTANT: Amendment Mode
61
+ ## Amendment Mode
62
62
 
63
- You are in AMENDMENT MODE. This means:
64
- - You MAY modify [MODIFIABLE] tasks (pending/failed) if the user requests changes
65
- - NEVER modify [PROTECTED] tasks (completed) - their outcomes depend on the original plan
66
- - DO NOT renumber existing tasks
67
- - You can create NEW tasks starting from number ${encodeTaskId(nextTaskNumber)}
63
+ - [PROTECTED] tasks (completed): NEVER modify — their outcomes depend on the original plan
64
+ - [MODIFIABLE] tasks (pending/failed): MAY modify if the user requests changes
65
+ - Do NOT renumber existing tasks
66
+ - New tasks start from number ${encodeTaskId(nextTaskNumber)}
68
67
 
69
68
  ## Project Location
70
69
 
@@ -72,54 +71,30 @@ Project folder: ${projectPath}
72
71
 
73
72
  ## Existing Tasks
74
73
 
75
- The following tasks already exist in this project:
76
-
77
74
  ${existingTasksSummary}
78
75
 
79
- ### Protected Tasks (COMPLETED - cannot be modified)
76
+ ### Protected (COMPLETED)
80
77
  ${protectedTasksList}
81
78
 
82
- ### Modifiable Tasks (PENDING/FAILED - can be modified if requested)
79
+ ### Modifiable (PENDING/FAILED)
83
80
  ${modifiableTasksList}
84
81
 
85
82
  ## Instructions
86
83
 
87
84
  ### Step 1: Read Context
88
85
 
89
- First, read the original project description from:
90
- - ${projectPath}/input.md
91
-
92
- And review existing decisions from:
93
- - ${projectPath}/decisions.md (if it exists)
86
+ Read the original project description (\`${projectPath}/input.md\`) and existing decisions (\`${projectPath}/decisions.md\`, if it exists).
94
87
 
95
88
  ### Step 2: Analyze New Requirements
96
89
 
97
- Read the user's description of new tasks and identify what needs to be added. Consider:
98
- - How the new tasks relate to existing ones
99
- - Dependencies on completed tasks (check the ## Dependencies section in existing plan files)
100
- - Whether new tasks should reference existing task outcomes
101
-
102
- **Identifying Follow-up Tasks**: When a new task is a follow-up, fix, or iteration of a previously completed task, you MUST reference the previous task's outcome in the new plan's Context section. This gives the executing agent full context about what was done before.
103
-
104
- Use this format in the Context section:
90
+ Consider how new tasks relate to existing ones and their dependencies. For follow-up/fix tasks, reference the previous task's outcome in the Context section:
105
91
  \`This is a follow-up to task NN. See outcome: {projectPath}/outcomes/NN-task-name.md\`
106
92
 
107
- The outcome file paths for completed tasks are listed above in the Existing Tasks section.
108
-
109
93
  ### Step 3: Interview the User
110
94
 
111
- For EACH new task you identify, use the AskUserQuestion tool to gather:
112
- - Specific requirements and constraints
113
- - Technology preferences
114
- - Any existing code or patterns to follow
115
- - Edge cases to handle
116
-
117
- ### Step 3.5: Record Decisions
95
+ For EACH new task, use AskUserQuestion to gather specific requirements, technology preferences, existing patterns, and edge cases.
118
96
 
119
- After EACH interview question is answered, append the Q&A pair to the existing decisions file:
120
- - ${projectPath}/decisions.md
121
-
122
- Use this format:
97
+ After EACH answer, append the Q&A to \`${projectPath}/decisions.md\`:
123
98
  \`\`\`markdown
124
99
  ## [Question asked]
125
100
  [User's answer]
@@ -127,12 +102,9 @@ Use this format:
127
102
 
128
103
  ### Step 4: Create New Plan Files
129
104
 
130
- After interviewing the user about all NEW tasks, create plan files starting from the next available number:
131
- - ${projectPath}/plans/${encodeTaskId(nextTaskNumber)}-task-name.md
132
- - ${projectPath}/plans/${encodeTaskId(nextTaskNumber + 1)}-task-name.md
133
- - etc.
105
+ Create plan files starting from \`${projectPath}/plans/${encodeTaskId(nextTaskNumber)}-task-name.md\`. Use kebab-case names.
134
106
 
135
- Each plan file MUST have Obsidian-style frontmatter at the top, before the \`# Task:\` heading. The frontmatter uses standard YAML format with opening and closing \`---\` delimiters:
107
+ Each plan file MUST have this structure:
136
108
 
137
109
  \`\`\`markdown
138
110
  ---
@@ -141,68 +113,38 @@ effort: medium
141
113
  # Task: [Task Name]
142
114
 
143
115
  ## Objective
144
- [Clear, one-sentence description of what this task accomplishes]
116
+ [Clear, one-sentence description]
145
117
 
146
118
  ## Context
147
- [Why this task is needed, how it fits into the larger project]
148
- [Reference relevant existing tasks if applicable]
149
- [For follow-up/fix tasks: "This is a follow-up to task NN. See outcome: {projectPath}/outcomes/NN-task-name.md"]
119
+ [Why this task is needed, relation to existing tasks]
120
+ [For follow-ups: "This is a follow-up to task NN. See outcome: {projectPath}/outcomes/NN-task-name.md"]
150
121
 
151
122
  ## Dependencies
152
- [Optional section - omit if task has no dependencies]
153
- [Comma-separated list of task IDs this task depends on, e.g., "1, 2"]
154
- [If a dependency fails, this task will be automatically blocked]
123
+ [Optional omit if none. Comma-separated task IDs, e.g., "1, 2"]
155
124
 
156
125
  ## Requirements
157
- [Specific requirements gathered from the user interview]
158
126
  - Requirement 1
159
127
  - Requirement 2
160
- - ...
161
128
 
162
129
  ## Implementation Steps
163
130
  1. [Step 1]
164
131
  2. [Step 2]
165
- 3. [Step 3]
166
- ...
167
132
 
168
133
  ## Acceptance Criteria
169
134
  - [ ] Criterion 1
170
135
  - [ ] Criterion 2
171
- - [ ] All tests pass
172
136
 
173
137
  ## Notes
174
- [Any additional context, warnings, or considerations]
175
- [Reference to existing task outcomes if relevant]
176
- \`\`\`
177
-
178
- ### Frontmatter Requirements
179
-
180
- The \`effort\` field is REQUIRED in every plan file. It indicates task complexity and determines which model will execute the task:
181
- - \`effort: low\` — Trivial/mechanical changes, simple one-file edits, config changes
182
- - \`effort: medium\` — Well-scoped feature work, bug fixes with clear plans, multi-file changes following existing patterns
183
- - \`effort: high\` — Architectural changes, complex logic, tasks requiring deep codebase understanding
184
-
185
- Optionally, you can add an explicit \`model\` field to override the effort-based model selection:
186
- \`\`\`markdown
187
- ---
188
- effort: medium
189
- model: opus
190
- ---
191
- # Task: ...
138
+ [Additional context, warnings, references to existing task outcomes]
192
139
  \`\`\`
193
140
 
194
- This is rarely needed — prefer using the \`effort\` label so the user's config controls the actual model used.
141
+ **Frontmatter fields:**
142
+ - \`effort\` (REQUIRED): \`low\` (trivial/mechanical), \`medium\` (well-scoped feature work), \`high\` (architectural/complex)
143
+ - \`model\` (optional): Override effort-based model selection. Rarely needed.
195
144
 
196
145
  ### Step 5: Confirm Completion
197
146
 
198
- After creating all new plan files:
199
- 1. Provide a summary of:
200
- - The new tasks you've created, including the effort level for each task. Example format:
201
- - Task 2: add-caching (effort: medium)
202
- - Task 3: update-docs (effort: low)
203
- - How they relate to existing tasks
204
- - Total task count in the project
205
- 2. Display this exit message to the user:
147
+ Summarize new tasks with effort levels, their relation to existing tasks, and total task count. Then display:
206
148
 
207
149
  \`\`\`
208
150
  Planning complete! To exit this session and run your tasks:
@@ -210,26 +152,12 @@ Planning complete! To exit this session and run your tasks:
210
152
  2. Then run: raf do <project>
211
153
  \`\`\`
212
154
 
213
- ## Important Rules
214
-
215
- 1. NEVER modify COMPLETED task plans - they are [PROTECTED] because their outcomes depend on the original plan
216
- 2. You MAY modify non-completed task plans (pending/failed) if the user requests changes - they are [MODIFIABLE]
217
- 3. ALWAYS interview the user before creating or modifying plans
218
- 4. New tasks start from number ${encodeTaskId(nextTaskNumber)}
219
- 5. Use descriptive, kebab-case names for plan files
220
- 6. Each plan should be self-contained with all context needed
221
- 7. Specify task dependencies using the ## Dependencies section with task IDs only (e.g., "1, 2")
222
- 8. Tasks without dependencies should omit the Dependencies section entirely
223
- 9. Be specific - vague plans lead to poor execution
224
- 10. ALWAYS include the \`effort\` frontmatter field in every plan file — assess each task's complexity
225
-
226
- ## Plan Output Style
227
-
228
- Plans can include whatever level of detail you deem helpful for the executing agent. Use your judgment:
229
- - Include implementation details when they clarify the approach
230
- - Code snippets are acceptable when they help illustrate a specific pattern
231
- - File paths are helpful when referencing existing project files, patterns, or directories
232
- - Focus on clarity — the goal is for the executing agent to understand what needs to be done`;
155
+ ## Rules
156
+
157
+ - Always interview the user before creating or modifying plans
158
+ - Each plan must be self-contained with all context needed
159
+ - Be specific vague plans lead to poor execution
160
+ - Include implementation details, code snippets, and file paths when they clarify the approach`;
233
161
 
234
162
  const userMessage = `I want to add the following new tasks to this project:
235
163