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
@@ -0,0 +1,127 @@
1
+ import { renderCodexStreamEvent } from '../../src/parsers/codex-stream-renderer.js';
2
+
3
+ describe('renderCodexStreamEvent', () => {
4
+ describe('item.completed with item.type: error (NEW-1)', () => {
5
+ it('renders an error line from item.completed error event', () => {
6
+ const line = JSON.stringify({
7
+ type: 'item.completed',
8
+ item: { type: 'error', message: 'model not found: gpt-bad' },
9
+ });
10
+ const result = renderCodexStreamEvent(line);
11
+ expect(result.display).toContain('✗ Error: model not found: gpt-bad');
12
+ expect(result.textContent).toBe('model not found: gpt-bad');
13
+ });
14
+
15
+ it('uses fallback text when item.message is absent', () => {
16
+ const line = JSON.stringify({
17
+ type: 'item.completed',
18
+ item: { type: 'error' },
19
+ });
20
+ const result = renderCodexStreamEvent(line);
21
+ expect(result.display).toContain('✗ Error: Unknown error');
22
+ });
23
+ });
24
+
25
+ describe('turn.failed with nested error.message (NEW-2)', () => {
26
+ it('surfaces nested error.message instead of generic fallback', () => {
27
+ const line = JSON.stringify({
28
+ type: 'turn.failed',
29
+ error: { message: 'rate limit exceeded' },
30
+ });
31
+ const result = renderCodexStreamEvent(line);
32
+ expect(result.display).toContain('✗ Failed: rate limit exceeded');
33
+ expect(result.textContent).toBe('rate limit exceeded');
34
+ });
35
+
36
+ it('falls back to event.message when error object is absent', () => {
37
+ const line = JSON.stringify({
38
+ type: 'turn.failed',
39
+ message: 'context length exceeded',
40
+ });
41
+ const result = renderCodexStreamEvent(line);
42
+ expect(result.display).toContain('✗ Failed: context length exceeded');
43
+ });
44
+
45
+ it('falls back to generic text when neither error.message nor message is present', () => {
46
+ const line = JSON.stringify({ type: 'turn.failed' });
47
+ const result = renderCodexStreamEvent(line);
48
+ expect(result.display).toContain('✗ Failed: Turn failed');
49
+ });
50
+ });
51
+
52
+ describe('existing event types remain unchanged', () => {
53
+ it('renders item.completed agent_message', () => {
54
+ const line = JSON.stringify({
55
+ type: 'item.completed',
56
+ item: { type: 'agent_message', text: 'Done.' },
57
+ });
58
+ const result = renderCodexStreamEvent(line);
59
+ expect(result.display).toBe('Done.\n');
60
+ expect(result.textContent).toBe('Done.');
61
+ });
62
+
63
+ it('renders item.completed command_execution', () => {
64
+ const line = JSON.stringify({
65
+ type: 'item.completed',
66
+ item: { type: 'command_execution', command: 'npm test', exit_code: 0 },
67
+ });
68
+ const result = renderCodexStreamEvent(line);
69
+ expect(result.display).toContain('Running: npm test');
70
+ expect(result.display).toContain('[✓]');
71
+ });
72
+
73
+ it('renders top-level error event', () => {
74
+ const line = JSON.stringify({ type: 'error', message: 'fatal error' });
75
+ const result = renderCodexStreamEvent(line);
76
+ expect(result.display).toContain('✗ Error: fatal error');
77
+ });
78
+ });
79
+
80
+ describe('turn.completed usage extraction', () => {
81
+ it('captures input and output tokens as usageData', () => {
82
+ const line = JSON.stringify({
83
+ type: 'turn.completed',
84
+ model: 'gpt-5.4',
85
+ usage: {
86
+ input_tokens: 1234,
87
+ output_tokens: 567,
88
+ },
89
+ });
90
+
91
+ const result = renderCodexStreamEvent(line);
92
+
93
+ expect(result.display).toBe(' → Usage: in: 1234, out: 567\n');
94
+ expect(result.usageData).toEqual({
95
+ inputTokens: 1234,
96
+ outputTokens: 567,
97
+ cacheReadInputTokens: 0,
98
+ cacheCreationInputTokens: 0,
99
+ modelUsage: {
100
+ 'gpt-5.4': {
101
+ inputTokens: 1234,
102
+ outputTokens: 567,
103
+ cacheReadInputTokens: 0,
104
+ cacheCreationInputTokens: 0,
105
+ costUsd: null,
106
+ },
107
+ },
108
+ totalCostUsd: null,
109
+ });
110
+ });
111
+
112
+ it('keeps Codex cost unavailable when no exact cost field is present', () => {
113
+ const line = JSON.stringify({
114
+ type: 'turn.completed',
115
+ usage: {
116
+ input_tokens: 99,
117
+ output_tokens: 1,
118
+ },
119
+ });
120
+
121
+ const result = renderCodexStreamEvent(line);
122
+
123
+ expect(result.usageData?.totalCostUsd).toBeNull();
124
+ expect(result.usageData?.modelUsage.codex?.costUsd).toBeNull();
125
+ });
126
+ });
127
+ });
@@ -8,9 +8,13 @@ import {
8
8
  formatProjectHeader,
9
9
  formatSummary,
10
10
  formatProgressBar,
11
+ formatTaskTokenSummary,
12
+ formatTokenTotalSummary,
11
13
  TaskStatus,
12
14
  } from '../../src/utils/terminal-symbols.js';
13
15
  import { logger } from '../../src/utils/logger.js';
16
+ import type { UsageData } from '../../src/types/config.js';
17
+ import type { CostBreakdown, TaskUsageEntry } from '../../src/utils/token-tracker.js';
14
18
 
15
19
  /**
16
20
  * Integration tests verifying that do/status commands produce expected output format.
@@ -69,6 +73,13 @@ describe('Command Output Integration', () => {
69
73
  expect(consoleLogSpy).toHaveBeenCalledWith('✓ setup-db 2m 3s');
70
74
  });
71
75
 
76
+ it('should show canonical model labels in task status output', () => {
77
+ const output = formatTaskProgress(1, 3, 'running', 'auth-login', 45000, '001', 'gpt-5.4');
78
+ logger.info(output);
79
+
80
+ expect(consoleLogSpy).toHaveBeenCalledWith('● 001-auth-login (gpt-5.4) 45s');
81
+ });
82
+
72
83
  it('should show failed task with elapsed time', () => {
73
84
  const output = formatTaskProgress(3, 3, 'failed', 'deploy', 300000);
74
85
  logger.info(output);
@@ -76,6 +87,13 @@ describe('Command Output Integration', () => {
76
87
  expect(consoleLogSpy).toHaveBeenCalledWith('✗ deploy 5m 0s');
77
88
  });
78
89
 
90
+ it('should show model metadata in the existing compact model slot', () => {
91
+ const output = formatTaskProgress(1, 3, 'running', 'auth-login', 45000, '01', 'sonnet', { effort: 'low', fast: true });
92
+ logger.info(output);
93
+
94
+ expect(consoleLogSpy).toHaveBeenCalledWith('● 01-auth-login (sonnet, low, fast) 45s');
95
+ });
96
+
79
97
  it('should show pending task with fraction', () => {
80
98
  const output = formatTaskProgress(2, 5, 'pending', 'cleanup');
81
99
  logger.info(output);
@@ -126,6 +144,45 @@ describe('Command Output Integration', () => {
126
144
 
127
145
  expect(consoleLogSpy).toHaveBeenCalledWith('○ no tasks');
128
146
  });
147
+
148
+ it('should log Codex task token summaries without a fake cost', () => {
149
+ const usage: UsageData = {
150
+ inputTokens: 1234,
151
+ outputTokens: 567,
152
+ cacheReadInputTokens: 0,
153
+ cacheCreationInputTokens: 0,
154
+ modelUsage: {},
155
+ totalCostUsd: null,
156
+ };
157
+ const entry: TaskUsageEntry = {
158
+ taskId: '01',
159
+ usage,
160
+ cost: { totalCost: null },
161
+ attempts: [usage],
162
+ };
163
+
164
+ logger.dim(formatTaskTokenSummary(entry));
165
+
166
+ expect(consoleLogSpy).toHaveBeenCalledWith(' Tokens: 1,234 in / 567 out');
167
+ });
168
+
169
+ it('should log Claude total summaries with exact cost when available', () => {
170
+ const usage: UsageData = {
171
+ inputTokens: 5000,
172
+ outputTokens: 900,
173
+ cacheReadInputTokens: 0,
174
+ cacheCreationInputTokens: 0,
175
+ modelUsage: {},
176
+ totalCostUsd: 0.42,
177
+ };
178
+ const cost: CostBreakdown = { totalCost: 0.42 };
179
+
180
+ logger.dim(formatTokenTotalSummary(usage, cost));
181
+
182
+ expect(consoleLogSpy).toHaveBeenCalledWith(
183
+ expect.stringContaining('Total cost: $0.42')
184
+ );
185
+ });
129
186
  });
130
187
 
131
188
  describe('multi-project summary', () => {
@@ -1,8 +1,17 @@
1
+ import { jest } from '@jest/globals';
1
2
  import { execSync } from 'node:child_process';
2
3
  import * as fs from 'node:fs';
3
4
  import * as os from 'node:os';
4
5
  import * as path from 'node:path';
5
6
 
7
+ const suiteHomeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'raf-commit-wt-home-'));
8
+ let mockHomeDir = suiteHomeDir;
9
+
10
+ jest.unstable_mockModule('node:os', () => ({
11
+ homedir: () => mockHomeDir,
12
+ tmpdir: () => os.tmpdir(),
13
+ }));
14
+
6
15
  /**
7
16
  * Integration tests for commitPlanningArtifacts in worktree scenarios.
8
17
  * Uses real git repos and worktrees to verify the commit behavior.
@@ -10,6 +19,7 @@ import * as path from 'node:path';
10
19
 
11
20
  // Import the actual module (no mocking)
12
21
  const { commitPlanningArtifacts } = await import('../../src/core/git.js');
22
+ const { resetConfigCache } = await import('../../src/utils/config.js');
13
23
 
14
24
  /**
15
25
  * Helper: create a temp directory for testing.
@@ -56,11 +66,14 @@ describe('commitPlanningArtifacts - worktree integration', () => {
56
66
  const worktreePaths: string[] = [];
57
67
 
58
68
  beforeEach(() => {
69
+ fs.rmSync(path.join(mockHomeDir, '.raf'), { recursive: true, force: true });
70
+ resetConfigCache();
59
71
  repoDir = makeTempDir('raf-commit-wt-');
60
72
  initGitRepo(repoDir);
61
73
  });
62
74
 
63
75
  afterEach(() => {
76
+ resetConfigCache();
64
77
  // Clean up worktrees first (before removing repo)
65
78
  for (const wt of worktreePaths) {
66
79
  try {
@@ -79,6 +92,10 @@ describe('commitPlanningArtifacts - worktree integration', () => {
79
92
  }
80
93
  });
81
94
 
95
+ afterAll(() => {
96
+ fs.rmSync(suiteHomeDir, { recursive: true, force: true });
97
+ });
98
+
82
99
  function createInitialProject(dir: string, projectFolder: string): string {
83
100
  const projectPath = path.join(dir, 'RAF', projectFolder);
84
101
  fs.mkdirSync(path.join(projectPath, 'plans'), { recursive: true });
@@ -128,7 +145,7 @@ describe('commitPlanningArtifacts - worktree integration', () => {
128
145
 
129
146
  // Verify commit was made
130
147
  const lastMsg = getLastCommitMessage(worktreePath);
131
- expect(lastMsg).toMatch(/RAF\[1\] Plan: my-project/);
148
+ expect(lastMsg).toMatch(/RAF\[my-project\] Plan: /);
132
149
 
133
150
  // Verify both files are in the commit
134
151
  const committedFiles = getLastCommitFiles(worktreePath);
@@ -170,7 +187,7 @@ describe('commitPlanningArtifacts - worktree integration', () => {
170
187
 
171
188
  // Verify commit was made with Amend prefix
172
189
  const lastMsg = getLastCommitMessage(worktreePath);
173
- expect(lastMsg).toMatch(/RAF\[1\] Amend: my-project/);
190
+ expect(lastMsg).toMatch(/RAF\[my-project\] Amend: /);
174
191
 
175
192
  // Verify only input.md and decisions.md are in the commit (not plan files)
176
193
  const committedFiles = getLastCommitFiles(worktreePath);
@@ -214,7 +231,7 @@ describe('commitPlanningArtifacts - worktree integration', () => {
214
231
 
215
232
  // Verify commit was made with Amend prefix
216
233
  const lastMsg = getLastCommitMessage(worktreePath);
217
- expect(lastMsg).toMatch(/RAF\[1\] Amend: my-project/);
234
+ expect(lastMsg).toMatch(/RAF\[my-project\] Amend: /);
218
235
 
219
236
  // Verify all files are in the commit (input, decisions, AND plan files)
220
237
  const committedFiles = getLastCommitFiles(worktreePath);
@@ -282,7 +299,7 @@ describe('commitPlanningArtifacts - worktree integration', () => {
282
299
 
283
300
  // Verify commit was made
284
301
  const lastMsg = getLastCommitMessage(recreatedWtPath);
285
- expect(lastMsg).toMatch(/RAF\[1\] Amend: my-project/);
302
+ expect(lastMsg).toMatch(/RAF\[my-project\] Amend: /);
286
303
 
287
304
  // Verify only input.md and decisions.md are in the commit (not plan files)
288
305
  const committedFiles = getLastCommitFiles(recreatedWtPath);
@@ -351,7 +368,7 @@ describe('commitPlanningArtifacts - worktree integration', () => {
351
368
 
352
369
  // Verify commit was made
353
370
  const lastMsg = getLastCommitMessage(recreatedWtPath);
354
- expect(lastMsg).toMatch(/RAF\[1\] Amend: my-project/);
371
+ expect(lastMsg).toMatch(/RAF\[my-project\] Amend: /);
355
372
 
356
373
  // Verify all files are in the commit (including plan files)
357
374
  const committedFiles = getLastCommitFiles(recreatedWtPath);
@@ -384,7 +401,7 @@ describe('commitPlanningArtifacts - worktree integration', () => {
384
401
 
385
402
  // Verify commit was made
386
403
  const lastMsg = getLastCommitMessage(worktreePath);
387
- expect(lastMsg).toMatch(/RAF\[1\] Plan: my-project/);
404
+ expect(lastMsg).toMatch(/RAF\[my-project\] Plan: /);
388
405
 
389
406
  // Only input.md should be in the commit (decisions.md unchanged)
390
407
  const committedFiles = getLastCommitFiles(worktreePath);
@@ -423,7 +440,7 @@ describe('commitPlanningArtifacts - worktree integration', () => {
423
440
 
424
441
  // Verify commit
425
442
  const lastMsg = getLastCommitMessage(repoDir);
426
- expect(lastMsg).toMatch(/RAF\[1\] Plan: my-project/);
443
+ expect(lastMsg).toMatch(/RAF\[my-project\] Plan: /);
427
444
 
428
445
  const committedFiles = getLastCommitFiles(repoDir);
429
446
  expect(committedFiles).toContain(`RAF/${projectFolder}/input.md`);
@@ -1,4 +1,15 @@
1
1
  import { jest } from '@jest/globals';
2
+ import * as fs from 'node:fs';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+
6
+ const suiteHomeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'raf-commit-home-'));
7
+ let mockHomeDir = suiteHomeDir;
8
+
9
+ jest.unstable_mockModule('node:os', () => ({
10
+ homedir: () => mockHomeDir,
11
+ tmpdir: () => os.tmpdir(),
12
+ }));
2
13
 
3
14
  // Mock execSync before importing the module
4
15
  const mockExecSync = jest.fn();
@@ -18,10 +29,21 @@ jest.unstable_mockModule('../../src/utils/logger.js', () => ({
18
29
 
19
30
  // Import after mocking
20
31
  const { commitPlanningArtifacts } = await import('../../src/core/git.js');
32
+ const { resetConfigCache } = await import('../../src/utils/config.js');
21
33
 
22
34
  describe('commitPlanningArtifacts', () => {
23
35
  beforeEach(() => {
24
36
  jest.clearAllMocks();
37
+ fs.rmSync(path.join(mockHomeDir, '.raf'), { recursive: true, force: true });
38
+ resetConfigCache();
39
+ });
40
+
41
+ afterEach(() => {
42
+ resetConfigCache();
43
+ });
44
+
45
+ afterAll(() => {
46
+ fs.rmSync(suiteHomeDir, { recursive: true, force: true });
25
47
  });
26
48
 
27
49
  it('should commit input.md and decisions.md with correct message format', async () => {
@@ -53,9 +75,9 @@ describe('commitPlanningArtifacts', () => {
53
75
  expect(addCmds.some((cmd) => cmd.includes('input.md'))).toBe(true);
54
76
  expect(addCmds.some((cmd) => cmd.includes('decisions.md'))).toBe(true);
55
77
 
56
- // Verify commit message format
78
+ // Verify commit message format: RAF[project-name] Plan: description
57
79
  expect(mockExecSync).toHaveBeenCalledWith(
58
- expect.stringMatching(/git commit -m "RAF\[3\] Plan: decision-vault"/),
80
+ expect.stringMatching(/git commit -m "RAF\[decision-vault\] Plan: /),
59
81
  expect.any(Object)
60
82
  );
61
83
  });
@@ -78,7 +100,7 @@ describe('commitPlanningArtifacts', () => {
78
100
  await commitPlanningArtifacts('/Users/test/RAF/12-my-feature');
79
101
 
80
102
  expect(mockExecSync).toHaveBeenCalledWith(
81
- expect.stringMatching(/git commit -m "RAF\[12\] Plan: my-feature"/),
103
+ expect.stringMatching(/git commit -m "RAF\[my-feature\] Plan: /),
82
104
  expect.any(Object)
83
105
  );
84
106
  });
@@ -247,7 +269,7 @@ describe('commitPlanningArtifacts', () => {
247
269
  await commitPlanningArtifacts('/Users/test/RAF/3-decision-vault', { isAmend: true });
248
270
 
249
271
  expect(mockExecSync).toHaveBeenCalledWith(
250
- expect.stringMatching(/git commit -m "RAF\[3\] Amend: decision-vault"/),
272
+ expect.stringMatching(/git commit -m "RAF\[decision-vault\] Amend: updated plans"/),
251
273
  expect.any(Object)
252
274
  );
253
275
  });