rafcode 3.0.0 → 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.
- package/.claude/settings.local.json +3 -1
- package/CLAUDE.md +0 -1
- package/RAF/38-dual-wielder/decisions.md +9 -0
- package/RAF/38-dual-wielder/input.md +6 -1
- package/RAF/38-dual-wielder/outcomes/8-e2e-test-codex-provider.md +139 -0
- package/RAF/38-dual-wielder/plans/8-e2e-test-codex-provider.md +95 -0
- package/RAF/39-pathless-rover/decisions.md +16 -0
- package/RAF/39-pathless-rover/input.md +2 -0
- package/RAF/39-pathless-rover/outcomes/1-fix-codex-stream-renderer.md +21 -0
- package/RAF/39-pathless-rover/outcomes/2-wire-provider-flag.md +28 -0
- package/RAF/39-pathless-rover/outcomes/3-remove-worktree-flag-do.md +41 -0
- package/RAF/39-pathless-rover/outcomes/4-remove-worktree-flag-plan-amend.md +30 -0
- package/RAF/39-pathless-rover/outcomes/5-update-prompts-and-docs.md +26 -0
- package/RAF/39-pathless-rover/plans/1-fix-codex-stream-renderer.md +43 -0
- package/RAF/39-pathless-rover/plans/2-wire-provider-flag.md +48 -0
- package/RAF/39-pathless-rover/plans/3-remove-worktree-flag-do.md +41 -0
- package/RAF/39-pathless-rover/plans/4-remove-worktree-flag-plan-amend.md +43 -0
- package/RAF/39-pathless-rover/plans/5-update-prompts-and-docs.md +31 -0
- package/RAF/40-numeric-order-fix/decisions.md +7 -0
- package/RAF/40-numeric-order-fix/input.md +19 -0
- package/RAF/40-numeric-order-fix/outcomes/1-fix-numeric-sort-order.md +18 -0
- package/RAF/40-numeric-order-fix/outcomes/2-add-npm-keywords.md +10 -0
- package/RAF/40-numeric-order-fix/plans/1-fix-numeric-sort-order.md +48 -0
- package/RAF/40-numeric-order-fix/plans/2-add-npm-keywords.md +23 -0
- package/RAF/41-echo-chamber/decisions.md +13 -0
- package/RAF/41-echo-chamber/input.md +4 -0
- package/RAF/41-echo-chamber/outcomes/1-update-codex-model-defaults.md +24 -0
- package/RAF/41-echo-chamber/outcomes/2-e2e-test-codex-provider.md +74 -0
- package/RAF/41-echo-chamber/plans/1-update-codex-model-defaults.md +28 -0
- package/RAF/41-echo-chamber/plans/2-e2e-test-codex-provider.md +103 -0
- package/RAF/42-patch-parade/decisions.md +29 -0
- package/RAF/42-patch-parade/input.md +9 -0
- package/RAF/42-patch-parade/outcomes/1-fix-codex-model-resolution.md +36 -0
- package/RAF/42-patch-parade/outcomes/2-fix-provider-aware-name-generation.md +31 -0
- package/RAF/42-patch-parade/outcomes/3-fix-codex-error-event-rendering.md +32 -0
- package/RAF/42-patch-parade/outcomes/4-update-cli-help-docs.md +28 -0
- package/RAF/42-patch-parade/outcomes/5-update-default-codex-models-to-gpt-5-4.md +33 -0
- package/RAF/42-patch-parade/outcomes/6-unify-model-config-schema.md +89 -0
- package/RAF/42-patch-parade/plans/1-fix-codex-model-resolution.md +35 -0
- package/RAF/42-patch-parade/plans/2-fix-provider-aware-name-generation.md +38 -0
- package/RAF/42-patch-parade/plans/3-fix-codex-error-event-rendering.md +32 -0
- package/RAF/42-patch-parade/plans/4-update-cli-help-docs.md +31 -0
- package/RAF/42-patch-parade/plans/5-update-default-codex-models-to-gpt-5-4.md +35 -0
- package/RAF/42-patch-parade/plans/6-unify-model-config-schema.md +46 -0
- package/RAF/43-swiss-army/decisions.md +34 -0
- package/RAF/43-swiss-army/input.md +7 -0
- package/RAF/43-swiss-army/outcomes/1-fix-model-validation.md +21 -0
- package/RAF/43-swiss-army/outcomes/2-update-commit-format.md +31 -0
- package/RAF/43-swiss-army/outcomes/3-wire-reasoning-effort.md +28 -0
- package/RAF/43-swiss-army/outcomes/4-remove-provider-flag.md +27 -0
- package/RAF/43-swiss-army/outcomes/5-config-wizard-validation.md +23 -0
- package/RAF/43-swiss-army/outcomes/6-add-fast-mode.md +32 -0
- package/RAF/43-swiss-army/outcomes/7-config-preset.md +31 -0
- package/RAF/43-swiss-army/plans/1-fix-model-validation.md +38 -0
- package/RAF/43-swiss-army/plans/2-update-commit-format.md +46 -0
- package/RAF/43-swiss-army/plans/3-wire-reasoning-effort.md +39 -0
- package/RAF/43-swiss-army/plans/4-remove-provider-flag.md +43 -0
- package/RAF/43-swiss-army/plans/5-config-wizard-validation.md +42 -0
- package/RAF/43-swiss-army/plans/6-add-fast-mode.md +46 -0
- package/RAF/43-swiss-army/plans/7-config-preset.md +51 -0
- package/RAF/44-config-api-change/decisions.md +22 -0
- package/RAF/44-config-api-change/input.md +5 -0
- package/RAF/44-config-api-change/outcomes/1-restructure-config-subcommands.md +19 -0
- package/RAF/44-config-api-change/outcomes/2-move-preset-under-config.md +17 -0
- package/RAF/44-config-api-change/outcomes/3-update-existing-tests-for-config-api.md +14 -0
- package/RAF/44-config-api-change/outcomes/4-update-config-command-docs.md +11 -0
- package/RAF/44-config-api-change/outcomes/5-fix-codex-name-generation.md +18 -0
- package/RAF/44-config-api-change/plans/1-restructure-config-subcommands.md +37 -0
- package/RAF/44-config-api-change/plans/2-move-preset-under-config.md +38 -0
- package/RAF/44-config-api-change/plans/3-update-existing-tests-for-config-api.md +38 -0
- package/RAF/44-config-api-change/plans/4-update-config-command-docs.md +36 -0
- package/RAF/44-config-api-change/plans/5-fix-codex-name-generation.md +49 -0
- package/RAF/45-signal-cairn/decisions.md +7 -0
- package/RAF/45-signal-cairn/input.md +2 -0
- package/RAF/45-signal-cairn/outcomes/1-rename-provider-to-harness.md +19 -0
- package/RAF/45-signal-cairn/outcomes/2-normalize-model-display-names.md +18 -0
- package/RAF/45-signal-cairn/plans/1-rename-provider-to-harness.md +40 -0
- package/RAF/45-signal-cairn/plans/2-normalize-model-display-names.md +41 -0
- package/RAF/45-signal-lantern/decisions.md +10 -0
- package/RAF/45-signal-lantern/input.md +2 -0
- package/RAF/45-signal-lantern/outcomes/1-add-effort-and-fast-to-do-model-display.md +15 -0
- package/RAF/45-signal-lantern/outcomes/2-capture-codex-post-run-token-usage.md +15 -0
- package/RAF/45-signal-lantern/outcomes/3-show-codex-token-summaries-without-fake-cost.md +14 -0
- package/RAF/45-signal-lantern/plans/1-add-effort-and-fast-to-do-model-display.md +38 -0
- package/RAF/45-signal-lantern/plans/2-capture-codex-post-run-token-usage.md +37 -0
- package/RAF/45-signal-lantern/plans/3-show-codex-token-summaries-without-fake-cost.md +40 -0
- package/RAF/46-lantern-arc/decisions.md +19 -0
- package/RAF/46-lantern-arc/input.md +6 -0
- package/RAF/46-lantern-arc/outcomes/1-remove-spark-alias.md +16 -0
- package/RAF/46-lantern-arc/outcomes/2-clean-up-worktree-plan-command.md +30 -0
- package/RAF/46-lantern-arc/outcomes/3-fix-token-usage-accumulation.md +32 -0
- package/RAF/46-lantern-arc/outcomes/4-display-effort-in-compact-mode.md +22 -0
- package/RAF/46-lantern-arc/outcomes/5-codex-fast-mode-research.md +38 -0
- package/RAF/46-lantern-arc/outcomes/6-optimize-llm-prompts.md +39 -0
- package/RAF/46-lantern-arc/plans/1-remove-spark-alias.md +38 -0
- package/RAF/46-lantern-arc/plans/2-clean-up-worktree-plan-command.md +33 -0
- package/RAF/46-lantern-arc/plans/3-fix-token-usage-accumulation.md +33 -0
- package/RAF/46-lantern-arc/plans/4-display-effort-in-compact-mode.md +28 -0
- package/RAF/46-lantern-arc/plans/5-codex-fast-mode-research.md +34 -0
- package/RAF/46-lantern-arc/plans/6-optimize-llm-prompts.md +48 -0
- package/RAF/47-signal-trim/decisions.md +13 -0
- package/RAF/47-signal-trim/input.md +2 -0
- package/RAF/47-signal-trim/plans/1-remove-cache-from-status.md +73 -0
- package/README.md +50 -63
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +47 -49
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/do.d.ts +2 -0
- package/dist/commands/do.d.ts.map +1 -1
- package/dist/commands/do.js +91 -230
- package/dist/commands/do.js.map +1 -1
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +54 -259
- package/dist/commands/plan.js.map +1 -1
- package/dist/commands/preset.d.ts +3 -0
- package/dist/commands/preset.d.ts.map +1 -0
- package/dist/commands/preset.js +158 -0
- package/dist/commands/preset.js.map +1 -0
- package/dist/core/claude-runner.d.ts +2 -0
- package/dist/core/claude-runner.d.ts.map +1 -1
- package/dist/core/claude-runner.js +36 -12
- package/dist/core/claude-runner.js.map +1 -1
- package/dist/core/codex-runner.d.ts +1 -0
- package/dist/core/codex-runner.d.ts.map +1 -1
- package/dist/core/codex-runner.js +26 -7
- package/dist/core/codex-runner.js.map +1 -1
- package/dist/core/failure-analyzer.js +2 -1
- package/dist/core/failure-analyzer.js.map +1 -1
- package/dist/core/git.d.ts +2 -2
- package/dist/core/git.d.ts.map +1 -1
- package/dist/core/git.js +53 -3
- package/dist/core/git.js.map +1 -1
- package/dist/core/project-manager.d.ts.map +1 -1
- package/dist/core/project-manager.js +2 -2
- package/dist/core/project-manager.js.map +1 -1
- package/dist/core/pull-request.js +5 -5
- package/dist/core/pull-request.js.map +1 -1
- package/dist/core/runner-factory.d.ts +4 -4
- package/dist/core/runner-factory.d.ts.map +1 -1
- package/dist/core/runner-factory.js +8 -8
- package/dist/core/runner-factory.js.map +1 -1
- package/dist/core/runner-interface.d.ts +1 -1
- package/dist/core/runner-types.d.ts +17 -4
- package/dist/core/runner-types.d.ts.map +1 -1
- package/dist/core/state-derivation.js +3 -3
- package/dist/core/state-derivation.js.map +1 -1
- package/dist/parsers/codex-stream-renderer.d.ts +28 -4
- package/dist/parsers/codex-stream-renderer.d.ts.map +1 -1
- package/dist/parsers/codex-stream-renderer.js +110 -0
- package/dist/parsers/codex-stream-renderer.js.map +1 -1
- package/dist/prompts/amend.d.ts +0 -1
- package/dist/prompts/amend.d.ts.map +1 -1
- package/dist/prompts/amend.js +31 -104
- package/dist/prompts/amend.js.map +1 -1
- package/dist/prompts/execution.d.ts.map +1 -1
- package/dist/prompts/execution.js +17 -34
- package/dist/prompts/execution.js.map +1 -1
- package/dist/prompts/planning.d.ts.map +1 -1
- package/dist/prompts/planning.js +23 -123
- package/dist/prompts/planning.js.map +1 -1
- package/dist/types/config.d.ts +33 -32
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +14 -28
- package/dist/types/config.js.map +1 -1
- package/dist/utils/config.d.ts +36 -16
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +209 -104
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/name-generator.d.ts.map +1 -1
- package/dist/utils/name-generator.js +25 -12
- package/dist/utils/name-generator.js.map +1 -1
- package/dist/utils/paths.d.ts +5 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +9 -0
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/terminal-symbols.d.ts +15 -2
- package/dist/utils/terminal-symbols.d.ts.map +1 -1
- package/dist/utils/terminal-symbols.js +36 -4
- package/dist/utils/terminal-symbols.js.map +1 -1
- package/dist/utils/token-tracker.d.ts +6 -1
- package/dist/utils/token-tracker.d.ts.map +1 -1
- package/dist/utils/token-tracker.js +84 -51
- package/dist/utils/token-tracker.js.map +1 -1
- package/dist/utils/validation.d.ts +1 -2
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +4 -25
- package/dist/utils/validation.js.map +1 -1
- package/package.json +7 -2
- package/src/commands/config.ts +60 -63
- package/src/commands/do.ts +96 -262
- package/src/commands/plan.ts +55 -279
- package/src/commands/preset.ts +186 -0
- package/src/core/claude-runner.ts +45 -5
- package/src/core/codex-runner.ts +32 -7
- package/src/core/failure-analyzer.ts +2 -1
- package/src/core/git.ts +57 -3
- package/src/core/project-manager.ts +2 -1
- package/src/core/pull-request.ts +5 -5
- package/src/core/runner-factory.ts +9 -9
- package/src/core/runner-interface.ts +1 -1
- package/src/core/runner-types.ts +17 -4
- package/src/core/state-derivation.ts +3 -3
- package/src/parsers/codex-stream-renderer.ts +149 -4
- package/src/prompts/amend.ts +30 -105
- package/src/prompts/config-docs.md +206 -62
- package/src/prompts/execution.ts +17 -34
- package/src/prompts/planning.ts +23 -124
- package/src/types/config.ts +47 -59
- package/src/utils/config.ts +248 -115
- package/src/utils/name-generator.ts +29 -13
- package/src/utils/paths.ts +10 -0
- package/src/utils/terminal-symbols.ts +46 -6
- package/src/utils/token-tracker.ts +96 -57
- package/src/utils/validation.ts +5 -30
- package/tests/unit/amend-prompt.test.ts +3 -2
- package/tests/unit/claude-runner-interactive.test.ts +21 -3
- package/tests/unit/claude-runner.test.ts +39 -0
- package/tests/unit/codex-runner.test.ts +163 -0
- package/tests/unit/codex-stream-renderer.test.ts +127 -0
- package/tests/unit/command-output.test.ts +57 -0
- package/tests/unit/commit-planning-artifacts-worktree.test.ts +24 -7
- package/tests/unit/commit-planning-artifacts.test.ts +26 -4
- package/tests/unit/config-command.test.ts +215 -303
- package/tests/unit/config.test.ts +319 -235
- package/tests/unit/dependency-integration.test.ts +27 -1
- package/tests/unit/do-model-display.test.ts +35 -0
- package/tests/unit/execution-prompt.test.ts +49 -19
- package/tests/unit/name-generator.test.ts +82 -12
- package/tests/unit/plan-command-auto-flag.test.ts +7 -10
- package/tests/unit/plan-command.test.ts +14 -17
- package/tests/unit/planning-prompt.test.ts +9 -8
- package/tests/unit/terminal-symbols.test.ts +94 -3
- package/tests/unit/token-tracker.test.ts +180 -1
- package/tests/unit/validation.test.ts +9 -41
- 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\[
|
|
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\[
|
|
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\[
|
|
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\[
|
|
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\[
|
|
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\[
|
|
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\[
|
|
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\[
|
|
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\[
|
|
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\[
|
|
272
|
+
expect.stringMatching(/git commit -m "RAF\[decision-vault\] Amend: updated plans"/),
|
|
251
273
|
expect.any(Object)
|
|
252
274
|
);
|
|
253
275
|
});
|