rafcode 2.1.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +4 -1
- package/CLAUDE.md +59 -11
- package/RAF/ahslfe-config-wizard/decisions.md +34 -0
- package/RAF/ahslfe-config-wizard/input.md +1 -0
- package/RAF/ahslfe-config-wizard/outcomes/01-define-config-schema.md +38 -0
- package/RAF/ahslfe-config-wizard/outcomes/02-refactor-codebase-to-use-config.md +67 -0
- package/RAF/ahslfe-config-wizard/outcomes/03-create-config-documentation.md +37 -0
- package/RAF/ahslfe-config-wizard/outcomes/04-implement-raf-config-command.md +47 -0
- package/RAF/ahslfe-config-wizard/outcomes/05-update-claude-md.md +26 -0
- package/RAF/ahslfe-config-wizard/plans/01-define-config-schema.md +73 -0
- package/RAF/ahslfe-config-wizard/plans/02-refactor-codebase-to-use-config.md +74 -0
- package/RAF/ahslfe-config-wizard/plans/03-create-config-documentation.md +57 -0
- package/RAF/ahslfe-config-wizard/plans/04-implement-raf-config-command.md +66 -0
- package/RAF/ahslfe-config-wizard/plans/05-update-claude-md.md +60 -0
- package/RAF/ahstvo-token-tracker/decisions.md +44 -0
- package/RAF/ahstvo-token-tracker/input.md +3 -0
- package/RAF/ahstvo-token-tracker/outcomes/01-full-model-id-support.md +43 -0
- package/RAF/ahstvo-token-tracker/outcomes/02-name-generation-no-session.md +33 -0
- package/RAF/ahstvo-token-tracker/outcomes/03-unify-stream-json-execution.md +48 -0
- package/RAF/ahstvo-token-tracker/outcomes/04-token-tracking-cost-calculation.md +53 -0
- package/RAF/ahstvo-token-tracker/outcomes/05-token-cost-console-reporting.md +57 -0
- package/RAF/ahstvo-token-tracker/outcomes/06-runtime-verbose-toggle.md +53 -0
- package/RAF/ahstvo-token-tracker/outcomes/07-readme-config-docs.md +36 -0
- package/RAF/ahstvo-token-tracker/plans/01-full-model-id-support.md +35 -0
- package/RAF/ahstvo-token-tracker/plans/02-name-generation-no-session.md +36 -0
- package/RAF/ahstvo-token-tracker/plans/03-unify-stream-json-execution.md +44 -0
- package/RAF/ahstvo-token-tracker/plans/04-token-tracking-cost-calculation.md +56 -0
- package/RAF/ahstvo-token-tracker/plans/05-token-cost-console-reporting.md +55 -0
- package/RAF/ahstvo-token-tracker/plans/06-runtime-verbose-toggle.md +48 -0
- package/RAF/ahstvo-token-tracker/plans/07-readme-config-docs.md +44 -0
- package/RAF/ahtahs-token-reaper/decisions.md +37 -0
- package/RAF/ahtahs-token-reaper/input.md +20 -0
- package/RAF/ahtahs-token-reaper/outcomes/01-extend-token-tracker-data-model.md +42 -0
- package/RAF/ahtahs-token-reaper/outcomes/02-accumulate-usage-in-retry-loop.md +31 -0
- package/RAF/ahtahs-token-reaper/outcomes/03-per-attempt-display-formatting.md +60 -0
- package/RAF/ahtahs-token-reaper/outcomes/04-add-model-name-to-claude-call-logs.md +57 -0
- package/RAF/ahtahs-token-reaper/outcomes/05-handle-invalid-config-in-raf-config.md +46 -0
- package/RAF/ahtahs-token-reaper/outcomes/06-fix-verbose-toggle-timer-display.md +38 -0
- package/RAF/ahtahs-token-reaper/plans/01-extend-token-tracker-data-model.md +36 -0
- package/RAF/ahtahs-token-reaper/plans/02-accumulate-usage-in-retry-loop.md +36 -0
- package/RAF/ahtahs-token-reaper/plans/03-per-attempt-display-formatting.md +43 -0
- package/RAF/ahtahs-token-reaper/plans/04-add-model-name-to-claude-call-logs.md +38 -0
- package/RAF/ahtahs-token-reaper/plans/05-handle-invalid-config-in-raf-config.md +36 -0
- package/RAF/ahtahs-token-reaper/plans/06-fix-verbose-toggle-timer-display.md +40 -0
- package/README.md +34 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +195 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/do.d.ts.map +1 -1
- package/dist/commands/do.js +55 -7
- package/dist/commands/do.js.map +1 -1
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +5 -3
- package/dist/commands/plan.js.map +1 -1
- package/dist/core/claude-runner.d.ts +19 -2
- package/dist/core/claude-runner.d.ts.map +1 -1
- package/dist/core/claude-runner.js +43 -96
- package/dist/core/claude-runner.js.map +1 -1
- package/dist/core/failure-analyzer.d.ts.map +1 -1
- package/dist/core/failure-analyzer.js +6 -3
- package/dist/core/failure-analyzer.js.map +1 -1
- package/dist/core/git.d.ts.map +1 -1
- package/dist/core/git.js +10 -3
- package/dist/core/git.js.map +1 -1
- package/dist/core/pull-request.d.ts +1 -1
- package/dist/core/pull-request.d.ts.map +1 -1
- package/dist/core/pull-request.js +9 -4
- package/dist/core/pull-request.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/parsers/stream-renderer.d.ts +16 -1
- package/dist/parsers/stream-renderer.d.ts.map +1 -1
- package/dist/parsers/stream-renderer.js +34 -4
- package/dist/parsers/stream-renderer.js.map +1 -1
- package/dist/prompts/execution.d.ts.map +1 -1
- package/dist/prompts/execution.js +11 -1
- package/dist/prompts/execution.js.map +1 -1
- package/dist/types/config.d.ts +95 -4
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +63 -3
- package/dist/types/config.js.map +1 -1
- package/dist/utils/config.d.ts +65 -7
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +297 -21
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/name-generator.d.ts +3 -7
- package/dist/utils/name-generator.d.ts.map +1 -1
- package/dist/utils/name-generator.js +75 -61
- package/dist/utils/name-generator.js.map +1 -1
- package/dist/utils/terminal-symbols.d.ts +25 -0
- package/dist/utils/terminal-symbols.d.ts.map +1 -1
- package/dist/utils/terminal-symbols.js +87 -0
- package/dist/utils/terminal-symbols.js.map +1 -1
- package/dist/utils/token-tracker.d.ts +55 -0
- package/dist/utils/token-tracker.d.ts.map +1 -0
- package/dist/utils/token-tracker.js +142 -0
- package/dist/utils/token-tracker.js.map +1 -0
- package/dist/utils/validation.d.ts +5 -5
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +10 -6
- package/dist/utils/validation.js.map +1 -1
- package/dist/utils/verbose-toggle.d.ts +33 -0
- package/dist/utils/verbose-toggle.d.ts.map +1 -0
- package/dist/utils/verbose-toggle.js +94 -0
- package/dist/utils/verbose-toggle.js.map +1 -0
- package/package.json +1 -1
- package/src/commands/config.ts +230 -0
- package/src/commands/do.ts +64 -6
- package/src/commands/plan.ts +5 -3
- package/src/core/claude-runner.ts +59 -115
- package/src/core/failure-analyzer.ts +6 -3
- package/src/core/git.ts +10 -3
- package/src/core/pull-request.ts +9 -4
- package/src/index.ts +2 -0
- package/src/parsers/stream-renderer.ts +54 -4
- package/src/prompts/config-docs.md +331 -0
- package/src/prompts/execution.ts +13 -1
- package/src/types/config.ts +156 -7
- package/src/utils/config.ts +357 -21
- package/src/utils/name-generator.ts +84 -71
- package/src/utils/terminal-symbols.ts +103 -0
- package/src/utils/token-tracker.ts +177 -0
- package/src/utils/validation.ts +15 -10
- package/src/utils/verbose-toggle.ts +103 -0
- package/tests/unit/claude-runner.test.ts +171 -7
- package/tests/unit/config-command.test.ts +242 -0
- package/tests/unit/config.test.ts +632 -30
- package/tests/unit/name-generator.test.ts +99 -75
- package/tests/unit/pull-request.test.ts +2 -0
- package/tests/unit/stream-renderer.test.ts +83 -0
- package/tests/unit/terminal-symbols.test.ts +245 -0
- package/tests/unit/timer-verbose-integration.test.ts +170 -0
- package/tests/unit/token-tracker.test.ts +685 -0
- package/tests/unit/verbose-toggle.test.ts +204 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Task: Update token summary formatting to show per-attempt breakdowns
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
Update `formatTaskTokenSummary()` to display a per-attempt breakdown when a task took multiple attempts, while keeping single-attempt output unchanged.
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
With tasks 01 and 02 complete, the `TaskUsageEntry` now contains an `attempts` array with per-attempt `UsageData`. The formatting function needs to render this breakdown so users can see token consumption per retry attempt.
|
|
8
|
+
|
|
9
|
+
## Dependencies
|
|
10
|
+
01, 02
|
|
11
|
+
|
|
12
|
+
## Requirements
|
|
13
|
+
- When a task has only 1 attempt, output is identical to the current format (no visual change)
|
|
14
|
+
- When a task has multiple attempts, show each attempt's tokens and cost on its own line, followed by a total line
|
|
15
|
+
- Always show the breakdown (not gated by --verbose flag)
|
|
16
|
+
- The grand total summary (`formatTokenTotalSummary`) remains unchanged — it shows combined totals only
|
|
17
|
+
- Update `formatTaskTokenSummary()` signature to accept the `attempts` array from `TaskUsageEntry`
|
|
18
|
+
- Cover changes with unit tests
|
|
19
|
+
|
|
20
|
+
## Implementation Steps
|
|
21
|
+
1. Update `formatTaskTokenSummary()` to accept the full `TaskUsageEntry` (or at minimum `usage`, `cost`, and `attempts`)
|
|
22
|
+
2. For single-attempt tasks (array length 1), render the existing format unchanged
|
|
23
|
+
3. For multi-attempt tasks, render each attempt on its own indented line with attempt number, tokens, and cost, then a total line
|
|
24
|
+
4. Update all call sites that invoke `formatTaskTokenSummary()` to pass the attempts data
|
|
25
|
+
5. Add tests for both single-attempt and multi-attempt formatting
|
|
26
|
+
|
|
27
|
+
## Acceptance Criteria
|
|
28
|
+
- [ ] Single-attempt tasks display identically to current format
|
|
29
|
+
- [ ] Multi-attempt tasks show per-attempt lines plus a total
|
|
30
|
+
- [ ] Formatting is clean and readable in terminal output
|
|
31
|
+
- [ ] `formatTokenTotalSummary()` is unchanged
|
|
32
|
+
- [ ] All call sites updated
|
|
33
|
+
- [ ] All tests pass
|
|
34
|
+
|
|
35
|
+
## Notes
|
|
36
|
+
- Example multi-attempt output (approximate):
|
|
37
|
+
```
|
|
38
|
+
Attempt 1: 1,234 in / 567 out | Est. cost: $0.02
|
|
39
|
+
Attempt 2: 2,345 in / 890 out | Est. cost: $0.04
|
|
40
|
+
Total: 3,579 in / 1,457 out | Est. cost: $0.06
|
|
41
|
+
```
|
|
42
|
+
- Keep the dim styling consistent with existing token output
|
|
43
|
+
- The TokenTracker's `calculateCost()` can be used to get per-attempt costs if needed
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Task: Add model name to all Claude invocation log messages
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
Display the short model alias (e.g., "sonnet", "haiku") in all log messages where RAF invokes Claude for non-task purposes.
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
When RAF calls Claude for auxiliary tasks (name generation, failure analysis, PR generation, config), it logs a message but doesn't indicate which model is being used. Users want visibility into which model is handling each call, especially since models are configurable per scenario.
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
- Use the format "...with <model>..." — append the model name before the trailing ellipsis
|
|
11
|
+
- Display the short alias (sonnet, haiku, opus) not the full model ID
|
|
12
|
+
- Apply to all four Claude invocation log messages:
|
|
13
|
+
1. Name generation: `src/commands/plan.ts` line 158 — currently "Generating project name suggestions..."
|
|
14
|
+
2. Failure analysis: `src/commands/do.ts` line 1111 — currently "Analyzing failure..."
|
|
15
|
+
3. PR generation: `src/core/pull-request.ts` — currently no explicit log message (add one)
|
|
16
|
+
4. Config session: `src/commands/config.ts` line 184 — currently "Starting config session with Claude..."
|
|
17
|
+
- Create a utility to extract the short alias from a full model ID string (e.g., "claude-sonnet-4-5-20250929" → "sonnet")
|
|
18
|
+
- The model value comes from `getModel()` calls in each module — the short name should be derived from whatever that returns
|
|
19
|
+
|
|
20
|
+
## Implementation Steps
|
|
21
|
+
1. Add a `getModelShortName(modelId: string)` utility that extracts the short alias from a model ID string — handle both full IDs ("claude-sonnet-4-5-20250929") and already-short names ("sonnet")
|
|
22
|
+
2. Update the name generation log in `src/commands/plan.ts` to include the model: "Generating project name suggestions with sonnet..."
|
|
23
|
+
3. Update the failure analysis log in `src/commands/do.ts` to include the model: "Analyzing failure with haiku..."
|
|
24
|
+
4. Add a log message for PR generation in `src/core/pull-request.ts`: "Generating PR with haiku..."
|
|
25
|
+
5. Update the config session log in `src/commands/config.ts` to include the model: "Starting config session with sonnet..."
|
|
26
|
+
6. Cover the `getModelShortName()` utility with unit tests
|
|
27
|
+
|
|
28
|
+
## Acceptance Criteria
|
|
29
|
+
- [ ] All four Claude invocation points show the model short name in their log messages
|
|
30
|
+
- [ ] Short name extraction works for full model IDs and already-short names
|
|
31
|
+
- [ ] Log format follows the "...with <model>..." pattern
|
|
32
|
+
- [ ] Unit tests cover the short name utility
|
|
33
|
+
- [ ] All tests pass
|
|
34
|
+
|
|
35
|
+
## Notes
|
|
36
|
+
- The model for each scenario is retrieved via `getModel('nameGeneration')`, `getModel('failureAnalysis')`, `getModel('prGeneration')`, `getModel('config')` from `src/utils/config.ts`
|
|
37
|
+
- Some call sites may need to retrieve the model earlier or pass it around to have it available at the log point — for instance, name generation logs in `plan.ts` but the model is determined inside `name-generator.ts`
|
|
38
|
+
- For the config session, there's already a line showing the model — consolidate if appropriate
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Task: Handle invalid config gracefully in raf config command
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
Make `raf config` resilient to invalid or corrupt config files so it can serve as the recovery path for broken configurations.
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
In `src/commands/config.ts`, the command calls `getModel('config')` and `getEffort('config')` early in execution. These read from the resolved config, which requires loading and validating `~/.raf/raf.config.json`. If that file contains invalid JSON or fails schema validation, these calls throw and `raf config` exits immediately — blocking the user from using the interactive editor to fix their config. Since `raf config` is the intended way to edit config, it must survive a broken config file.
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
- Wrap the config-loading path in `raf config` with error handling that catches JSON parse errors and schema validation failures
|
|
11
|
+
- On error, warn the user with a visible message (e.g., "Config file has errors, using defaults") that includes the specific error
|
|
12
|
+
- Fall back to default config values for model and effort so the interactive session can launch
|
|
13
|
+
- The interactive Claude session should still receive the current (broken) config file contents as context, so the user can see and fix the issue
|
|
14
|
+
- Only apply this resilience to `raf config` — other commands should continue to fail fast on invalid config
|
|
15
|
+
- Cover the error-handling path with tests
|
|
16
|
+
|
|
17
|
+
## Implementation Steps
|
|
18
|
+
1. In `src/commands/config.ts`, wrap the `getModel('config')` and `getEffort('config')` calls in a try-catch
|
|
19
|
+
2. On catch, log a warning with the error details and fall back to the default model/effort values from `DEFAULT_CONFIG`
|
|
20
|
+
3. Ensure the rest of the command continues normally — the interactive session launches with defaults
|
|
21
|
+
4. Make sure the broken config file contents are still shown to Claude in the session prompt so the user can diagnose and fix
|
|
22
|
+
5. Add tests for the error-recovery path (invalid JSON, schema validation failure)
|
|
23
|
+
|
|
24
|
+
## Acceptance Criteria
|
|
25
|
+
- [ ] `raf config` launches successfully even when `~/.raf/raf.config.json` is invalid JSON
|
|
26
|
+
- [ ] `raf config` launches successfully even when config fails schema validation
|
|
27
|
+
- [ ] A clear warning is displayed to the user about the config error
|
|
28
|
+
- [ ] The interactive session uses default model/effort values as fallback
|
|
29
|
+
- [ ] The broken config content is still visible in the session for the user to fix
|
|
30
|
+
- [ ] Other commands (`raf plan`, `raf do`, `raf status`) still fail fast on invalid config
|
|
31
|
+
- [ ] All tests pass
|
|
32
|
+
|
|
33
|
+
## Notes
|
|
34
|
+
- Check whether `loadConfig()` or the individual `getModel()`/`getEffort()` accessors are the right place to catch — it may be cleaner to catch at the `loadConfig()` level and return defaults
|
|
35
|
+
- The post-session validation already checks for config errors after the session ends — this change handles the pre-session path
|
|
36
|
+
- Consider whether `raf config --reset` also needs this fix (it probably doesn't since reset deletes the file without loading it)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Task: Fix Verbose Toggle Timer Display
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
Stop the interactive timer and task-name prefix from displaying when verbose mode is toggled ON, and resume them when toggled OFF.
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
When a user presses Tab during task execution to toggle verbose ON, the timer/status line continues updating and gets interleaved with Claude's streamed output. This produces garbled lines like:
|
|
8
|
+
```
|
|
9
|
+
● 01-extend-token-tracker-data-model 39sNow let me add the accumulateUsage() function.
|
|
10
|
+
```
|
|
11
|
+
The timer callback and status line need to be aware of the verbose toggle state so they pause/clear when verbose is ON and resume when verbose is OFF.
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
- When verbose toggles ON mid-execution: immediately clear the status line and stop timer display updates
|
|
15
|
+
- When verbose toggles OFF mid-execution: resume the timer/status line from the actual elapsed time (no reset)
|
|
16
|
+
- When started with `--verbose` flag: current behavior is already correct (no timer callback) — preserve this
|
|
17
|
+
- No task name or timer shown at all while verbose output is streaming — no header line either
|
|
18
|
+
- Tool use descriptions (→ Reading file.ts) and Claude text output continue to display normally when verbose is ON
|
|
19
|
+
- The timer itself keeps counting internally regardless of display state (elapsed time stays accurate)
|
|
20
|
+
|
|
21
|
+
## Implementation Steps
|
|
22
|
+
1. Read the current timer callback setup in `do.ts` where `createTaskTimer` is called — understand how the `onTick` callback currently works
|
|
23
|
+
2. Read `verbose-toggle.ts` to understand the toggle mechanism and its `isVerbose` property
|
|
24
|
+
3. Modify the timer's `onTick` callback to check `verboseToggle.isVerbose` on each tick — if verbose is ON, clear the status line and skip the update; if OFF, render the status line as normal
|
|
25
|
+
4. Ensure the status line is cleared immediately when verbose toggles ON (so the last timer line doesn't linger above the verbose output). This may require hooking into the toggle event or simply having the next tick handle it
|
|
26
|
+
5. Verify that toggling OFF restores the status line with the correct elapsed time on the next tick
|
|
27
|
+
6. Add tests for the new behavior: timer callback respects verbose state, status line cleared on verbose ON, resumed on verbose OFF
|
|
28
|
+
|
|
29
|
+
## Acceptance Criteria
|
|
30
|
+
- [ ] Toggling verbose ON clears the status line and stops timer/task-name display
|
|
31
|
+
- [ ] Toggling verbose OFF resumes the timer/status line with correct elapsed time
|
|
32
|
+
- [ ] No task name prefix appears on verbose output lines
|
|
33
|
+
- [ ] Starting with `--verbose` flag still works as before (no timer at all)
|
|
34
|
+
- [ ] Timer internally tracks elapsed time correctly regardless of display state
|
|
35
|
+
- [ ] All existing tests pass
|
|
36
|
+
|
|
37
|
+
## Notes
|
|
38
|
+
- The key files are `src/commands/do.ts` (timer callback setup around line 914), `src/utils/status-line.ts`, `src/utils/timer.ts`, and `src/utils/verbose-toggle.ts`
|
|
39
|
+
- The fix is likely a small change to the `onTick` callback — check `verboseToggle.isVerbose` and conditionally clear/update the status line
|
|
40
|
+
- Be careful with the edge case where `verbose` is the initial flag (no toggle exists) vs. runtime toggle via Tab
|
package/README.md
CHANGED
|
@@ -52,6 +52,7 @@ That's it! RAF will guide you through breaking down your task and then execute i
|
|
|
52
52
|
- **Git Integration**: Automatic commits after each completed task
|
|
53
53
|
- **Task Dependencies**: Tasks can depend on other tasks, with automatic blocking on failure
|
|
54
54
|
- **Worktree Mode**: Isolate planning and execution in a git worktree branch with `--worktree`
|
|
55
|
+
- **Configurable**: Customize models, effort levels, timeouts, and more via `raf config`
|
|
55
56
|
|
|
56
57
|
## Commands
|
|
57
58
|
|
|
@@ -89,6 +90,33 @@ raf status # List all projects (includes worktree projects that diffe
|
|
|
89
90
|
raf status abcdef # Show details for a project (shows both main and worktree if they differ)
|
|
90
91
|
```
|
|
91
92
|
|
|
93
|
+
### `raf config`
|
|
94
|
+
|
|
95
|
+
View and edit RAF configuration through an interactive Claude session. Configuration is stored at `~/.raf/raf.config.json`. All settings are optional — only set what you want to change.
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
raf config # Interactive config editor
|
|
99
|
+
raf config "use haiku for name generation" # Start with a specific request
|
|
100
|
+
raf config --reset # Reset config to defaults
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Precedence**: CLI flags > config file > built-in defaults
|
|
104
|
+
|
|
105
|
+
Example `~/.raf/raf.config.json`:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"models": {
|
|
110
|
+
"execute": "sonnet",
|
|
111
|
+
"nameGeneration": "haiku"
|
|
112
|
+
},
|
|
113
|
+
"worktree": true,
|
|
114
|
+
"timeout": 45
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Run `raf config` without arguments and ask what's available — the session has full knowledge of every configurable option.
|
|
119
|
+
|
|
92
120
|
### `raf migrate-project-ids-base26`
|
|
93
121
|
|
|
94
122
|
Rename legacy project folders (3-char base36 or 6-char base36 with digits) to the current 6-char base26 format.
|
|
@@ -181,6 +209,12 @@ Alias: `raf act`
|
|
|
181
209
|
|
|
182
210
|
> **Note:** `raf do` and `raf plan -y` run Claude with `--dangerously-skip-permissions` for fully automated execution without interactive prompts.
|
|
183
211
|
|
|
212
|
+
### `raf config [prompt]`
|
|
213
|
+
|
|
214
|
+
| Option | Description |
|
|
215
|
+
|--------|-------------|
|
|
216
|
+
| `--reset` | Reset config file to defaults |
|
|
217
|
+
|
|
184
218
|
### `raf status [identifier]`
|
|
185
219
|
|
|
186
220
|
| Option | Description |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiHpC,wBAAgB,mBAAmB,IAAI,OAAO,CAgB7C"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import * as readline from 'node:readline';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
import { ClaudeRunner } from '../core/claude-runner.js';
|
|
7
|
+
import { shutdownHandler } from '../core/shutdown-handler.js';
|
|
8
|
+
import { logger } from '../utils/logger.js';
|
|
9
|
+
import { getConfigPath, getModel, getEffort, getModelShortName, validateConfig, ConfigValidationError, resetConfigCache, } from '../utils/config.js';
|
|
10
|
+
import { DEFAULT_CONFIG } from '../types/config.js';
|
|
11
|
+
/**
|
|
12
|
+
* Load the config documentation markdown from src/prompts/config-docs.md.
|
|
13
|
+
* Resolved relative to this file's location in the dist/ tree.
|
|
14
|
+
*/
|
|
15
|
+
function loadConfigDocs() {
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname = path.dirname(__filename);
|
|
18
|
+
// From dist/commands/config.js -> ../../src/prompts/config-docs.md
|
|
19
|
+
const docsPath = path.join(__dirname, '..', '..', 'src', 'prompts', 'config-docs.md');
|
|
20
|
+
return fs.readFileSync(docsPath, 'utf-8');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Read the current user config file contents, or a message indicating no file exists.
|
|
24
|
+
*/
|
|
25
|
+
function getCurrentConfigState(configPath) {
|
|
26
|
+
if (!fs.existsSync(configPath)) {
|
|
27
|
+
return 'No config file exists yet. All settings use defaults. The file will be created at: ' + configPath;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
31
|
+
return `Current config file (${configPath}):\n\`\`\`json\n${content}\`\`\``;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return 'Config file exists but could not be read: ' + configPath;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build the system prompt for the config editing Claude session.
|
|
39
|
+
*/
|
|
40
|
+
function buildConfigSystemPrompt(configDocs, configState) {
|
|
41
|
+
return [
|
|
42
|
+
'You are helping the user edit their RAF configuration.',
|
|
43
|
+
'You have full permission to read and write ~/.raf/raf.config.json.',
|
|
44
|
+
'',
|
|
45
|
+
'# Current Config State',
|
|
46
|
+
configState,
|
|
47
|
+
'',
|
|
48
|
+
'# Config Documentation',
|
|
49
|
+
configDocs,
|
|
50
|
+
].join('\n');
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Validate the config file after the Claude session ends and report results.
|
|
54
|
+
*/
|
|
55
|
+
function postSessionValidation(configPath) {
|
|
56
|
+
if (!fs.existsSync(configPath)) {
|
|
57
|
+
logger.info('No config file exists — using all defaults.');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
62
|
+
const parsed = JSON.parse(content);
|
|
63
|
+
validateConfig(parsed);
|
|
64
|
+
logger.success('Config updated successfully.');
|
|
65
|
+
// Show a summary of what's set
|
|
66
|
+
const userConfig = parsed;
|
|
67
|
+
const keys = Object.keys(userConfig);
|
|
68
|
+
if (keys.length > 0) {
|
|
69
|
+
logger.info(`Custom settings: ${keys.join(', ')}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (error instanceof ConfigValidationError) {
|
|
74
|
+
logger.warn(`Config validation warning: ${error.message}`);
|
|
75
|
+
logger.warn('The file was not deleted — you can fix it manually or run `raf config` again.');
|
|
76
|
+
}
|
|
77
|
+
else if (error instanceof SyntaxError) {
|
|
78
|
+
logger.warn('Config file contains invalid JSON.');
|
|
79
|
+
logger.warn('The file was not deleted — you can fix it manually or run `raf config` again.');
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
logger.warn(`Could not validate config: ${error}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Prompt the user for Y/N confirmation via readline.
|
|
88
|
+
*/
|
|
89
|
+
async function confirm(message) {
|
|
90
|
+
const rl = readline.createInterface({
|
|
91
|
+
input: process.stdin,
|
|
92
|
+
output: process.stdout,
|
|
93
|
+
});
|
|
94
|
+
return new Promise((resolve) => {
|
|
95
|
+
rl.question(message, (answer) => {
|
|
96
|
+
rl.close();
|
|
97
|
+
resolve(answer.trim().toLowerCase() === 'y');
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
export function createConfigCommand() {
|
|
102
|
+
const command = new Command('config')
|
|
103
|
+
.description('View and edit RAF configuration with Claude')
|
|
104
|
+
.argument('[prompt...]', 'Optional initial prompt for the config session')
|
|
105
|
+
.option('--reset', 'Delete config file and restore all defaults')
|
|
106
|
+
.action(async (promptParts, options) => {
|
|
107
|
+
if (options.reset) {
|
|
108
|
+
await handleReset();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const initialPrompt = promptParts.length > 0 ? promptParts.join(' ') : undefined;
|
|
112
|
+
await runConfigSession(initialPrompt);
|
|
113
|
+
});
|
|
114
|
+
return command;
|
|
115
|
+
}
|
|
116
|
+
async function handleReset() {
|
|
117
|
+
const configPath = getConfigPath();
|
|
118
|
+
if (!fs.existsSync(configPath)) {
|
|
119
|
+
logger.info('No config file exists — already using defaults.');
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const confirmed = await confirm('This will delete ~/.raf/raf.config.json and restore all defaults. Continue? [y/N] ');
|
|
123
|
+
if (!confirmed) {
|
|
124
|
+
logger.info('Cancelled.');
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
fs.unlinkSync(configPath);
|
|
128
|
+
logger.success('Config file deleted. All settings restored to defaults.');
|
|
129
|
+
}
|
|
130
|
+
async function runConfigSession(initialPrompt) {
|
|
131
|
+
const configPath = getConfigPath();
|
|
132
|
+
// Try to load config, but fall back to defaults if it's broken
|
|
133
|
+
// This allows raf config to be used to fix a broken config file
|
|
134
|
+
let model;
|
|
135
|
+
let effort;
|
|
136
|
+
let configError = null;
|
|
137
|
+
try {
|
|
138
|
+
model = getModel('config');
|
|
139
|
+
effort = getEffort('config');
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
// Config file has errors - fall back to defaults so the session can launch
|
|
143
|
+
configError = error instanceof Error ? error : new Error(String(error));
|
|
144
|
+
model = DEFAULT_CONFIG.models.config;
|
|
145
|
+
effort = DEFAULT_CONFIG.effort.config;
|
|
146
|
+
// Clear the cached config so subsequent calls don't use the broken cache
|
|
147
|
+
resetConfigCache();
|
|
148
|
+
}
|
|
149
|
+
// Warn user if config has errors, before starting the session
|
|
150
|
+
if (configError) {
|
|
151
|
+
logger.warn(`Config file has errors, using defaults: ${configError.message}`);
|
|
152
|
+
logger.warn('Fix the config in this session or run `raf config --reset` to start fresh.');
|
|
153
|
+
logger.newline();
|
|
154
|
+
}
|
|
155
|
+
// Set effort level env var for the Claude session
|
|
156
|
+
process.env['CLAUDE_CODE_EFFORT_LEVEL'] = effort;
|
|
157
|
+
// Load config docs
|
|
158
|
+
let configDocs;
|
|
159
|
+
try {
|
|
160
|
+
configDocs = loadConfigDocs();
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
logger.error(`Failed to load config documentation: ${error}`);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
// Build system prompt
|
|
167
|
+
const configState = getCurrentConfigState(configPath);
|
|
168
|
+
const systemPrompt = buildConfigSystemPrompt(configDocs, configState);
|
|
169
|
+
// Build user message
|
|
170
|
+
const userMessage = initialPrompt
|
|
171
|
+
?? 'Show me my current config and help me make changes.';
|
|
172
|
+
// Set up Claude runner
|
|
173
|
+
const claudeRunner = new ClaudeRunner({ model });
|
|
174
|
+
shutdownHandler.init();
|
|
175
|
+
shutdownHandler.registerClaudeRunner(claudeRunner);
|
|
176
|
+
const configModel = getModelShortName(model);
|
|
177
|
+
logger.info(`Starting config session with ${configModel}...`);
|
|
178
|
+
logger.newline();
|
|
179
|
+
try {
|
|
180
|
+
const exitCode = await claudeRunner.runInteractive(systemPrompt, userMessage, {
|
|
181
|
+
dangerouslySkipPermissions: true,
|
|
182
|
+
});
|
|
183
|
+
if (exitCode !== 0) {
|
|
184
|
+
logger.warn(`Claude exited with code ${exitCode}`);
|
|
185
|
+
}
|
|
186
|
+
// Post-session validation
|
|
187
|
+
logger.newline();
|
|
188
|
+
postSessionValidation(configPath);
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
logger.error(`Config session failed: ${error}`);
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"do.d.ts","sourceRoot":"","sources":["../../src/commands/do.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"do.d.ts","sourceRoot":"","sources":["../../src/commands/do.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqDpC;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC;AAE3D;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,KAAK,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EAC1D,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,OAAO,GACf,MAAM,CAkBR;AA0BD,wBAAgB,eAAe,IAAI,OAAO,CAiBzC;AAoSD;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuBhG"}
|
package/dist/commands/do.js
CHANGED
|
@@ -12,10 +12,12 @@ import { validatePlansExist, resolveModelOption } from '../utils/validation.js';
|
|
|
12
12
|
import { getRafDir, extractProjectNumber, extractProjectName, extractTaskNameFromPlanFile, resolveProjectIdentifierWithDetails, getOutcomeFilePath, parseProjectPrefix } from '../utils/paths.js';
|
|
13
13
|
import { pickPendingProject, getPendingProjects, getPendingWorktreeProjects } from '../ui/project-picker.js';
|
|
14
14
|
import { logger } from '../utils/logger.js';
|
|
15
|
-
import { getConfig } from '../utils/config.js';
|
|
15
|
+
import { getConfig, getEffort, getWorktreeDefault, getModel, getModelShortName } from '../utils/config.js';
|
|
16
16
|
import { createTaskTimer, formatElapsedTime } from '../utils/timer.js';
|
|
17
17
|
import { createStatusLine } from '../utils/status-line.js';
|
|
18
|
-
import { formatProjectHeader, formatSummary, formatTaskProgress, } from '../utils/terminal-symbols.js';
|
|
18
|
+
import { formatProjectHeader, formatSummary, formatTaskProgress, formatTaskTokenSummary, formatTokenTotalSummary, } from '../utils/terminal-symbols.js';
|
|
19
|
+
import { TokenTracker } from '../utils/token-tracker.js';
|
|
20
|
+
import { VerboseToggle } from '../utils/verbose-toggle.js';
|
|
19
21
|
import { deriveProjectState, discoverProjects, getNextExecutableTask, getDerivedStats, getDerivedStatsForTasks, isProjectComplete, hasProjectFailed, parseOutcomeStatus, } from '../core/state-derivation.js';
|
|
20
22
|
import { analyzeFailure } from '../core/failure-analyzer.js';
|
|
21
23
|
import { getRepoRoot, getRepoBasename, getCurrentBranch, computeWorktreePath, computeWorktreeBaseDir, validateWorktree, listWorktreeProjects, mergeWorktreeBranch, removeWorktree, resolveWorktreeProjectByIdentifier, } from '../core/worktree.js';
|
|
@@ -60,11 +62,11 @@ export function createDoCommand() {
|
|
|
60
62
|
async function runDoCommand(projectIdentifierArg, options) {
|
|
61
63
|
const rafDir = getRafDir();
|
|
62
64
|
let projectIdentifier = projectIdentifierArg;
|
|
63
|
-
let worktreeMode = options.worktree ??
|
|
65
|
+
let worktreeMode = options.worktree ?? getWorktreeDefault();
|
|
64
66
|
// Validate and resolve model option
|
|
65
67
|
let model;
|
|
66
68
|
try {
|
|
67
|
-
model = resolveModelOption(options.model, options.sonnet);
|
|
69
|
+
model = resolveModelOption(options.model, options.sonnet, 'execute');
|
|
68
70
|
}
|
|
69
71
|
catch (error) {
|
|
70
72
|
logger.error(error.message);
|
|
@@ -543,6 +545,11 @@ async function executeSingleProject(projectPath, projectName, options) {
|
|
|
543
545
|
const projectManager = new ProjectManager();
|
|
544
546
|
shutdownHandler.init();
|
|
545
547
|
shutdownHandler.registerClaudeRunner(claudeRunner);
|
|
548
|
+
// Initialize token tracker for usage reporting
|
|
549
|
+
const tokenTracker = new TokenTracker();
|
|
550
|
+
// Set up runtime verbose toggle (Tab key to toggle during execution)
|
|
551
|
+
const verboseToggle = new VerboseToggle(verbose);
|
|
552
|
+
shutdownHandler.onShutdown(() => verboseToggle.stop());
|
|
546
553
|
// Start project timer
|
|
547
554
|
const projectStartTime = Date.now();
|
|
548
555
|
if (verbose) {
|
|
@@ -636,6 +643,8 @@ async function executeSingleProject(projectPath, projectName, options) {
|
|
|
636
643
|
lines.push('<promise>BLOCKED</promise>');
|
|
637
644
|
return lines.join('\n');
|
|
638
645
|
}
|
|
646
|
+
// Start verbose toggle listener (Tab key)
|
|
647
|
+
verboseToggle.start();
|
|
639
648
|
let task = getNextTaskToProcess(state);
|
|
640
649
|
while (task) {
|
|
641
650
|
const taskIndex = state.tasks.findIndex((t) => t.id === task.id);
|
|
@@ -701,11 +710,18 @@ async function executeSingleProject(projectPath, projectName, options) {
|
|
|
701
710
|
let attempts = 0;
|
|
702
711
|
let lastOutput = '';
|
|
703
712
|
let failureReason = '';
|
|
713
|
+
// Collect usage data from all attempts (for accurate token tracking across retries)
|
|
714
|
+
const attemptUsageData = [];
|
|
704
715
|
// Track failure history for each attempt (attempt number -> reason)
|
|
705
716
|
const failureHistory = [];
|
|
706
717
|
// Set up timer for elapsed time tracking
|
|
707
718
|
const statusLine = createStatusLine();
|
|
708
719
|
const timer = createTaskTimer(verbose ? undefined : (elapsed) => {
|
|
720
|
+
// When verbose is toggled ON at runtime, clear the status line and skip updates
|
|
721
|
+
if (verboseToggle.isVerbose) {
|
|
722
|
+
statusLine.clear();
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
709
725
|
// Show running status with task name and timer (updates in place)
|
|
710
726
|
statusLine.update(formatTaskProgress(taskNumber, totalTasks, 'running', displayName, elapsed, taskId));
|
|
711
727
|
});
|
|
@@ -743,10 +759,22 @@ async function executeSingleProject(projectPath, projectName, options) {
|
|
|
743
759
|
outcomeFilePath,
|
|
744
760
|
} : undefined;
|
|
745
761
|
// Run Claude (use worktree root as cwd if in worktree mode)
|
|
762
|
+
const executeEffort = getEffort('execute');
|
|
763
|
+
const runnerOptions = {
|
|
764
|
+
timeout,
|
|
765
|
+
outcomeFilePath,
|
|
766
|
+
commitContext,
|
|
767
|
+
cwd: worktreeCwd,
|
|
768
|
+
effortLevel: executeEffort,
|
|
769
|
+
verboseCheck: () => verboseToggle.isVerbose,
|
|
770
|
+
};
|
|
746
771
|
const result = verbose
|
|
747
|
-
? await claudeRunner.runVerbose(prompt,
|
|
748
|
-
: await claudeRunner.run(prompt,
|
|
772
|
+
? await claudeRunner.runVerbose(prompt, runnerOptions)
|
|
773
|
+
: await claudeRunner.run(prompt, runnerOptions);
|
|
749
774
|
lastOutput = result.output;
|
|
775
|
+
if (result.usageData) {
|
|
776
|
+
attemptUsageData.push(result.usageData);
|
|
777
|
+
}
|
|
750
778
|
// Parse result
|
|
751
779
|
const parsed = parseOutput(result.output);
|
|
752
780
|
if (result.timedOut) {
|
|
@@ -857,6 +885,11 @@ Task completed. No detailed report provided.
|
|
|
857
885
|
// Minimal mode: show completed task line
|
|
858
886
|
logger.info(formatTaskProgress(taskNumber, totalTasks, 'completed', displayName, elapsedMs, task.id));
|
|
859
887
|
}
|
|
888
|
+
// Track and display token usage for this task
|
|
889
|
+
if (attemptUsageData.length > 0) {
|
|
890
|
+
const entry = tokenTracker.addTask(task.id, attemptUsageData);
|
|
891
|
+
logger.dim(formatTaskTokenSummary(entry, (u) => tokenTracker.calculateCost(u)));
|
|
892
|
+
}
|
|
860
893
|
completedInSession.add(task.id);
|
|
861
894
|
}
|
|
862
895
|
else {
|
|
@@ -872,12 +905,18 @@ Task completed. No detailed report provided.
|
|
|
872
905
|
}
|
|
873
906
|
if (verbose) {
|
|
874
907
|
logger.error(` Task ${taskLabel} failed: ${failureReason} (${elapsedFormatted})`);
|
|
875
|
-
|
|
908
|
+
const analysisModel = getModelShortName(getModel('failureAnalysis'));
|
|
909
|
+
logger.info(` Analyzing failure with ${analysisModel}...`);
|
|
876
910
|
}
|
|
877
911
|
else {
|
|
878
912
|
// Minimal mode: show failed task line
|
|
879
913
|
logger.info(formatTaskProgress(taskNumber, totalTasks, 'failed', displayName, elapsedMs, task.id));
|
|
880
914
|
}
|
|
915
|
+
// Track token usage even for failed tasks (partial data still useful for totals)
|
|
916
|
+
if (attemptUsageData.length > 0) {
|
|
917
|
+
const entry = tokenTracker.addTask(task.id, attemptUsageData);
|
|
918
|
+
logger.dim(formatTaskTokenSummary(entry, (u) => tokenTracker.calculateCost(u)));
|
|
919
|
+
}
|
|
881
920
|
// Analyze failure and generate structured report
|
|
882
921
|
const analysisReport = await analyzeFailure(lastOutput, failureReason, task.id);
|
|
883
922
|
// Save failure outcome with status marker, analysis, and details
|
|
@@ -906,6 +945,8 @@ ${stashName ? `- Stash: ${stashName}` : ''}
|
|
|
906
945
|
// Get next task to process
|
|
907
946
|
task = getNextTaskToProcess(state);
|
|
908
947
|
}
|
|
948
|
+
// Stop verbose toggle listener before summary output
|
|
949
|
+
verboseToggle.stop();
|
|
909
950
|
// Ensure context is cleared for summary
|
|
910
951
|
logger.clearContext();
|
|
911
952
|
// Get final stats
|
|
@@ -957,6 +998,13 @@ ${stashName ? `- Stash: ${stashName}` : ''}
|
|
|
957
998
|
logger.info(formatSummary(sessionStats.completed, sessionStats.failed, sessionStats.pending, projectElapsedMs, sessionStats.blocked));
|
|
958
999
|
}
|
|
959
1000
|
}
|
|
1001
|
+
// Show token usage summary if any tasks reported usage data
|
|
1002
|
+
const trackerEntries = tokenTracker.getEntries();
|
|
1003
|
+
if (trackerEntries.length > 0) {
|
|
1004
|
+
logger.newline();
|
|
1005
|
+
const totals = tokenTracker.getTotals();
|
|
1006
|
+
logger.dim(formatTokenTotalSummary(totals.usage, totals.cost));
|
|
1007
|
+
}
|
|
960
1008
|
// Show retry history for tasks that had failures (even if eventually successful)
|
|
961
1009
|
if (projectRetryHistory.length > 0) {
|
|
962
1010
|
logger.newline();
|