rafcode 2.4.0 → 2.4.1-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 +3 -1
- package/RAF/ahwidh-quick-fix-gremlin/decisions.md +37 -0
- package/RAF/ahwidh-quick-fix-gremlin/input.md +35 -0
- package/RAF/ahwidh-quick-fix-gremlin/outcomes/01-fix-name-generation-prompt.md +33 -0
- package/RAF/ahwidh-quick-fix-gremlin/outcomes/02-fix-amend-commit-scope.md +43 -0
- package/RAF/ahwidh-quick-fix-gremlin/outcomes/03-fix-diverged-main-branch-sync.md +32 -0
- package/RAF/ahwidh-quick-fix-gremlin/outcomes/04-wire-rate-limit-to-do-command.md +61 -0
- package/RAF/ahwidh-quick-fix-gremlin/outcomes/05-add-config-get-set-flags.md +125 -0
- package/RAF/ahwidh-quick-fix-gremlin/outcomes/06-sync-worktree-branch-before-execution.md +96 -0
- package/RAF/ahwidh-quick-fix-gremlin/outcomes/07-update-frontmatter-format.md +107 -0
- package/RAF/ahwidh-quick-fix-gremlin/outcomes/08-remove-plan-token-report.md +76 -0
- package/RAF/ahwidh-quick-fix-gremlin/plans/01-fix-name-generation-prompt.md +52 -0
- package/RAF/ahwidh-quick-fix-gremlin/plans/02-fix-amend-commit-scope.md +48 -0
- package/RAF/ahwidh-quick-fix-gremlin/plans/03-fix-diverged-main-branch-sync.md +49 -0
- package/RAF/ahwidh-quick-fix-gremlin/plans/04-wire-rate-limit-to-do-command.md +78 -0
- package/RAF/ahwidh-quick-fix-gremlin/plans/05-add-config-get-set-flags.md +101 -0
- package/RAF/ahwidh-quick-fix-gremlin/plans/06-sync-worktree-branch-before-execution.md +92 -0
- package/RAF/ahwidh-quick-fix-gremlin/plans/07-update-frontmatter-format.md +105 -0
- package/RAF/ahwidh-quick-fix-gremlin/plans/08-remove-plan-token-report.md +50 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +209 -1
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/do.d.ts.map +1 -1
- package/dist/commands/do.js +36 -5
- package/dist/commands/do.js.map +1 -1
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +2 -55
- package/dist/commands/plan.js.map +1 -1
- package/dist/core/claude-runner.d.ts +0 -6
- package/dist/core/claude-runner.d.ts.map +1 -1
- package/dist/core/claude-runner.js +1 -5
- package/dist/core/claude-runner.js.map +1 -1
- package/dist/core/worktree.d.ts +12 -0
- package/dist/core/worktree.d.ts.map +1 -1
- package/dist/core/worktree.js +33 -1
- package/dist/core/worktree.js.map +1 -1
- package/dist/prompts/amend.d.ts.map +1 -1
- package/dist/prompts/amend.js +3 -1
- package/dist/prompts/amend.js.map +1 -1
- package/dist/prompts/planning.d.ts.map +1 -1
- package/dist/prompts/planning.js +3 -1
- package/dist/prompts/planning.js.map +1 -1
- package/dist/utils/frontmatter.d.ts +13 -3
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +40 -10
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/name-generator.d.ts.map +1 -1
- package/dist/utils/name-generator.js +7 -16
- package/dist/utils/name-generator.js.map +1 -1
- package/dist/utils/terminal-symbols.js +2 -2
- package/dist/utils/terminal-symbols.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/config.ts +242 -0
- package/src/commands/do.ts +37 -4
- package/src/commands/plan.ts +2 -65
- package/src/core/claude-runner.ts +1 -12
- package/src/core/worktree.ts +37 -1
- package/src/prompts/amend.ts +3 -1
- package/src/prompts/planning.ts +3 -1
- package/src/utils/frontmatter.ts +41 -11
- package/src/utils/name-generator.ts +7 -16
- package/src/utils/terminal-symbols.ts +2 -2
- package/tests/unit/commit-planning-artifacts-worktree.test.ts +6 -14
- package/tests/unit/commit-planning-artifacts.test.ts +4 -12
- package/tests/unit/config-command.test.ts +170 -0
- package/tests/unit/frontmatter.test.ts +95 -1
- package/tests/unit/name-generator.test.ts +1 -1
- package/tests/unit/post-execution-picker.test.ts +1 -0
- package/tests/unit/worktree.test.ts +68 -1
- package/src/utils/session-parser.ts +0 -161
- package/tests/unit/session-parser.test.ts +0 -301
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Outcome: Remove Token Report from Plan Command
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
Successfully removed all token usage reporting from `raf plan` and `raf plan --amend` commands. The session-parser module and its tests have been deleted, and the sessionId parameter has been removed from the ClaudeRunner API.
|
|
5
|
+
|
|
6
|
+
## Changes Made
|
|
7
|
+
|
|
8
|
+
### 1. Updated `src/commands/plan.ts`
|
|
9
|
+
|
|
10
|
+
**Removed imports** (lines 3, 19-22):
|
|
11
|
+
- Removed `import * as crypto from 'node:crypto';`
|
|
12
|
+
- Removed `TokenTracker` from `../utils/token-tracker.js`
|
|
13
|
+
- Removed `parseSessionById` from `../utils/session-parser.js`
|
|
14
|
+
- Removed `formatTokenTotalSummary, TokenSummaryOptions` from `../utils/terminal-symbols.js`
|
|
15
|
+
- Removed `getDisplayConfig, getPricingConfig` from `../utils/config.js`
|
|
16
|
+
|
|
17
|
+
**Updated `runPlanCommand()` function** (lines 286-303):
|
|
18
|
+
- Removed `sessionId` generation using `crypto.randomUUID()`
|
|
19
|
+
- Removed `sessionCwd` variable
|
|
20
|
+
- Removed `sessionId` parameter from `claudeRunner.runInteractive()` call
|
|
21
|
+
- Removed `displayPlanSessionTokenSummary(sessionId, sessionCwd)` call after session completion
|
|
22
|
+
|
|
23
|
+
**Updated `runAmendCommand()` function** (lines 599-617):
|
|
24
|
+
- Removed `sessionId` generation using `crypto.randomUUID()`
|
|
25
|
+
- Removed `sessionCwd` variable
|
|
26
|
+
- Removed `sessionId` parameter from `claudeRunner.runInteractive()` call
|
|
27
|
+
- Removed `displayPlanSessionTokenSummary(sessionId, sessionCwd)` call after session completion
|
|
28
|
+
|
|
29
|
+
**Deleted function** (lines 700-739):
|
|
30
|
+
- Completely removed the `displayPlanSessionTokenSummary()` function
|
|
31
|
+
|
|
32
|
+
### 2. Updated `src/core/claude-runner.ts`
|
|
33
|
+
|
|
34
|
+
**Removed from ClaudeRunnerOptions interface** (lines 34-39):
|
|
35
|
+
- Deleted the `sessionId?: string` field
|
|
36
|
+
- Deleted the associated JSDoc comment describing the sessionId parameter
|
|
37
|
+
|
|
38
|
+
**Updated `runInteractive()` method** (lines 284-305):
|
|
39
|
+
- Removed `sessionId` from the destructured options parameter
|
|
40
|
+
- Removed the conditional code block that added `--session-id` flag to Claude CLI args
|
|
41
|
+
|
|
42
|
+
### 3. Deleted files
|
|
43
|
+
- Deleted `src/utils/session-parser.ts` entirely
|
|
44
|
+
- Deleted `tests/unit/session-parser.test.ts` entirely
|
|
45
|
+
|
|
46
|
+
## Testing
|
|
47
|
+
|
|
48
|
+
- **Build verification**: `npm run build` passes with no TypeScript errors
|
|
49
|
+
- **Test suite**: All 1284 tests pass (down from 1299, indicating the session-parser tests were successfully removed)
|
|
50
|
+
- **No broken imports**: No import errors for the deleted modules
|
|
51
|
+
|
|
52
|
+
## Key Improvements
|
|
53
|
+
|
|
54
|
+
1. **Simplified planning command**: Removed unnecessary token tracking complexity from interactive planning sessions
|
|
55
|
+
2. **Dead code elimination**: Deleted the session-parser module that only existed to support this removed feature
|
|
56
|
+
3. **Cleaner API**: The `runInteractive()` method no longer has the unused sessionId parameter
|
|
57
|
+
4. **Reduced dependencies**: Removed crypto, TokenTracker, and session-parser imports from plan.ts
|
|
58
|
+
|
|
59
|
+
## Acceptance Criteria
|
|
60
|
+
|
|
61
|
+
- ✅ `raf plan` no longer displays a token usage summary after the planning session
|
|
62
|
+
- ✅ `raf plan --amend` no longer displays a token usage summary after the amendment session
|
|
63
|
+
- ✅ `src/utils/session-parser.ts` is deleted
|
|
64
|
+
- ✅ `tests/unit/session-parser.test.ts` is deleted
|
|
65
|
+
- ✅ `sessionId` parameter removed from `runInteractive()` API
|
|
66
|
+
- ✅ No unused imports remain in modified files
|
|
67
|
+
- ✅ Build passes (`npm run build`)
|
|
68
|
+
- ✅ Tests pass (`npm test` - 1284/1284 tests pass)
|
|
69
|
+
|
|
70
|
+
## Notes
|
|
71
|
+
|
|
72
|
+
- Token tracking utilities (`TokenTracker`, `formatTokenTotalSummary`, `formatTaskTokenSummary`) remain in the codebase as they are still used by `raf do` command
|
|
73
|
+
- Display and pricing config sections remain valid for `raf do` usage
|
|
74
|
+
- No changes were made to the execution token reporting in `raf do` - that functionality is preserved and unaffected
|
|
75
|
+
|
|
76
|
+
<promise>COMPLETE</promise>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
effort: low
|
|
2
|
+
---
|
|
3
|
+
# Task: Fix project name generation prompt
|
|
4
|
+
|
|
5
|
+
## Objective
|
|
6
|
+
Fix the multi-name generation prompt so Haiku doesn't output preamble text as the first "name" option.
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
When using Haiku for name generation (`models.nameGeneration: haiku`), the model outputs a preamble like "I'll generate 5 creative project names with differ..." before the actual names. This preamble gets sanitized into a long kebab-case string and appears as the first option in the picker. The prompt needs stronger instructions to prevent this.
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
- Tighten the `MULTI_NAME_GENERATION_PROMPT` in `src/utils/name-generator.ts` to prevent preamble
|
|
13
|
+
- Also tighten the single `NAME_GENERATION_PROMPT` for consistency
|
|
14
|
+
- Do NOT change the parsing logic (user chose prompt-only fix)
|
|
15
|
+
- Must work reliably with Haiku, Sonnet, and Opus models
|
|
16
|
+
|
|
17
|
+
## Implementation Steps
|
|
18
|
+
|
|
19
|
+
1. **Edit `src/utils/name-generator.ts`** — Update `MULTI_NAME_GENERATION_PROMPT` (line 22):
|
|
20
|
+
- Move the output format instruction to the very beginning of the prompt
|
|
21
|
+
- Add explicit "Do NOT include any introduction, explanation, or preamble" instruction
|
|
22
|
+
- Remove the numbered style descriptions (they encourage Haiku to narrate)
|
|
23
|
+
- Simplify to a direct instruction format. Example approach:
|
|
24
|
+
```
|
|
25
|
+
Output EXACTLY 5 project names, one per line. No introductions, no explanations, no numbering, no quotes.
|
|
26
|
+
|
|
27
|
+
Rules:
|
|
28
|
+
- Each name: 1-3 words, kebab-case, lowercase with hyphens only
|
|
29
|
+
- Use varied styles: metaphorical, playful, action-oriented, abstract, cultural reference
|
|
30
|
+
- Make them memorable and evocative
|
|
31
|
+
- For projects with many unrelated tasks, prefer abstract/metaphorical names
|
|
32
|
+
|
|
33
|
+
Project description:
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
2. **Update `NAME_GENERATION_PROMPT`** (line 6) similarly:
|
|
37
|
+
- Lead with the output format constraint
|
|
38
|
+
- Add "No introduction, no explanation" reinforcement
|
|
39
|
+
|
|
40
|
+
3. **Update tests** if any exist in `tests/unit/name-generator.test.ts` that assert on the prompt text
|
|
41
|
+
|
|
42
|
+
## Acceptance Criteria
|
|
43
|
+
- [ ] Multi-name prompt starts with output format instruction
|
|
44
|
+
- [ ] Prompt explicitly forbids preamble/introduction text
|
|
45
|
+
- [ ] Numbered style descriptions removed or simplified to avoid narration
|
|
46
|
+
- [ ] Single-name prompt similarly tightened
|
|
47
|
+
- [ ] Existing tests pass
|
|
48
|
+
|
|
49
|
+
## Notes
|
|
50
|
+
- The parsing already strips numbering prefixes and quotes, but can't distinguish a preamble sentence from a valid name
|
|
51
|
+
- Haiku is particularly prone to prefacing its response with context
|
|
52
|
+
- The key insight: put the output format constraint FIRST, before the creative instructions
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
effort: low
|
|
2
|
+
---
|
|
3
|
+
# Task: Fix amend mode commit to exclude plan files
|
|
4
|
+
|
|
5
|
+
## Objective
|
|
6
|
+
Change the amend commit to only include `input.md` and `decisions.md`, not the new plan files.
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
In `raf plan --amend`, the commit includes new plan files alongside `input.md` and `decisions.md`. Plan files should be committed by Claude during task execution instead, matching how initial planning only commits `input.md` and `decisions.md`.
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
- Remove `additionalFiles: newPlanPaths` from the amend commit call
|
|
13
|
+
- Keep the `isAmend: true` flag (commit message should still use "Amend:" prefix)
|
|
14
|
+
- Initial planning commit behavior must remain unchanged
|
|
15
|
+
|
|
16
|
+
## Implementation Steps
|
|
17
|
+
|
|
18
|
+
1. **Edit `src/commands/plan.ts`** at lines 650-656. Change:
|
|
19
|
+
```typescript
|
|
20
|
+
// Commit planning artifacts (input.md, decisions.md, and new plan files)
|
|
21
|
+
const newPlanPaths = newPlanFiles.map(f => path.join(plansDir, f));
|
|
22
|
+
await commitPlanningArtifacts(projectPath, {
|
|
23
|
+
cwd: worktreePath ?? undefined,
|
|
24
|
+
additionalFiles: newPlanPaths,
|
|
25
|
+
isAmend: true,
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
To:
|
|
29
|
+
```typescript
|
|
30
|
+
// Commit planning artifacts (input.md, decisions.md only — plan files committed during execution)
|
|
31
|
+
await commitPlanningArtifacts(projectPath, {
|
|
32
|
+
cwd: worktreePath ?? undefined,
|
|
33
|
+
isAmend: true,
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
Remove the `newPlanPaths` variable as well since it's no longer used.
|
|
37
|
+
|
|
38
|
+
2. **Update tests** — Check `tests/unit/commit-planning-artifacts.test.ts` and `tests/unit/commit-planning-artifacts-worktree.test.ts` for any amend-specific tests that assert plan files are committed. Update them to verify plan files are NOT included.
|
|
39
|
+
|
|
40
|
+
## Acceptance Criteria
|
|
41
|
+
- [ ] Amend commit only stages `input.md` and `decisions.md`
|
|
42
|
+
- [ ] `additionalFiles` is not passed in the amend code path
|
|
43
|
+
- [ ] Initial planning commit behavior unchanged
|
|
44
|
+
- [ ] Existing tests updated and passing
|
|
45
|
+
|
|
46
|
+
## Notes
|
|
47
|
+
- The `commitPlanningArtifacts` function itself doesn't need changes — just stop passing `additionalFiles` from the amend caller
|
|
48
|
+
- The `additionalFiles` parameter on `commitPlanningArtifacts` can remain in the interface for future use
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
effort: low
|
|
2
|
+
---
|
|
3
|
+
# Task: Fix diverged main branch sync to report failure
|
|
4
|
+
|
|
5
|
+
## Objective
|
|
6
|
+
Change `pullMainBranch()` to return `success: false` when the local main branch has diverged from remote, so callers surface the issue as a warning.
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
PR #5 review comment: When `git fetch origin main:main` fails because local main has diverged, the code falls back to `git fetch origin main` and returns `success: true` with an `error` message. Callers only check `success === false` to warn, so the divergence is silently ignored. Worktree/PR flows then proceed on stale base refs.
|
|
10
|
+
|
|
11
|
+
The fix: return `success: false` so callers show the warning. Per user decision, this should be a visible warning that lets execution continue (not a hard error).
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
- Change `success: true` to `success: false` in the diverged branch path
|
|
15
|
+
- Callers already handle `success: false` with `logger.warn()` — no caller changes needed
|
|
16
|
+
- The plain `git fetch origin main` should still run (we still want the remote ref updated)
|
|
17
|
+
|
|
18
|
+
## Implementation Steps
|
|
19
|
+
|
|
20
|
+
1. **Edit `src/core/worktree.ts`** at lines 533-538. Change:
|
|
21
|
+
```typescript
|
|
22
|
+
return {
|
|
23
|
+
success: true,
|
|
24
|
+
mainBranch,
|
|
25
|
+
hadChanges: false,
|
|
26
|
+
error: `Local ${mainBranch} has diverged from origin, not updated`,
|
|
27
|
+
};
|
|
28
|
+
```
|
|
29
|
+
To:
|
|
30
|
+
```typescript
|
|
31
|
+
return {
|
|
32
|
+
success: false,
|
|
33
|
+
mainBranch,
|
|
34
|
+
hadChanges: false,
|
|
35
|
+
error: `Local ${mainBranch} has diverged from origin, not updated`,
|
|
36
|
+
};
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
2. **Update tests** — Find tests for `pullMainBranch` (likely in `tests/unit/worktree.test.ts`) and update the diverged branch test case to assert `success: false`.
|
|
40
|
+
|
|
41
|
+
## Acceptance Criteria
|
|
42
|
+
- [ ] Diverged branch path returns `success: false`
|
|
43
|
+
- [ ] Callers show a warning via `logger.warn()` (already happens for `success: false`)
|
|
44
|
+
- [ ] Execution continues after the warning (callers don't abort on sync failure)
|
|
45
|
+
- [ ] Tests updated and passing
|
|
46
|
+
|
|
47
|
+
## Notes
|
|
48
|
+
- The second `git fetch origin main` (plain fetch) still runs successfully — it updates `origin/main` remote ref even though local `main` isn't updated. This is fine.
|
|
49
|
+
- Callers in `do.ts` (line 243-250) and `plan.ts` already have the pattern: `if (!syncResult.success) { logger.warn(...) }` — no changes needed there.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
effort: low
|
|
2
|
+
---
|
|
3
|
+
# Task: Wire rate-limit estimate display config to do command
|
|
4
|
+
|
|
5
|
+
## Objective
|
|
6
|
+
Pass the `display` config settings (`showRateLimitEstimate`, `showCacheTokens`) through to `formatTaskTokenSummary()` and `formatTokenTotalSummary()` calls in the do command.
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
PR #5 review comment: The formatter defaults `showRateLimitEstimate` to `false` in its destructuring, but `raf do` calls both formatters without passing `TokenSummaryOptions`. Despite `display.showRateLimitEstimate` defaulting to `true` in config, the feature is effectively dead code.
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
- Read display config settings and pass them as `TokenSummaryOptions` to both formatters
|
|
13
|
+
- Calculate and pass `rateLimitPercentage` from `TokenTracker`
|
|
14
|
+
- Per-task summaries should show cumulative rate limit percentage
|
|
15
|
+
- Grand total summary should show the final cumulative percentage
|
|
16
|
+
|
|
17
|
+
## Implementation Steps
|
|
18
|
+
|
|
19
|
+
1. **Edit `src/commands/do.ts`** — Build `TokenSummaryOptions` from config and pass to formatters.
|
|
20
|
+
|
|
21
|
+
Near the top of `runDoCommand()` (after tokenTracker is created), import and read display config:
|
|
22
|
+
```typescript
|
|
23
|
+
import { getShowRateLimitEstimate, getShowCacheTokens } from '../utils/config.js';
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
2. **Update per-task token display** (lines 1218-1220 and 1246-1248). Change both locations from:
|
|
27
|
+
```typescript
|
|
28
|
+
logger.dim(formatTaskTokenSummary(entry, (u) => tokenTracker.calculateCost(u)));
|
|
29
|
+
```
|
|
30
|
+
To:
|
|
31
|
+
```typescript
|
|
32
|
+
const taskRateLimitPct = tokenTracker.getCumulativeRateLimitPercentage();
|
|
33
|
+
logger.dim(formatTaskTokenSummary(entry, (u) => tokenTracker.calculateCost(u), {
|
|
34
|
+
showCacheTokens: getShowCacheTokens(),
|
|
35
|
+
showRateLimitEstimate: getShowRateLimitEstimate(),
|
|
36
|
+
rateLimitPercentage: taskRateLimitPct,
|
|
37
|
+
}));
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
3. **Update grand total display** (lines 1360-1361). Change from:
|
|
41
|
+
```typescript
|
|
42
|
+
logger.dim(formatTokenTotalSummary(totals.usage, totals.cost));
|
|
43
|
+
```
|
|
44
|
+
To:
|
|
45
|
+
```typescript
|
|
46
|
+
const totalRateLimitPct = tokenTracker.getCumulativeRateLimitPercentage();
|
|
47
|
+
logger.dim(formatTokenTotalSummary(totals.usage, totals.cost, {
|
|
48
|
+
showCacheTokens: getShowCacheTokens(),
|
|
49
|
+
showRateLimitEstimate: getShowRateLimitEstimate(),
|
|
50
|
+
rateLimitPercentage: totalRateLimitPct,
|
|
51
|
+
}));
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
4. **Update the default value** in `formatTokenTotalSummary` (line 258 of `terminal-symbols.ts`). Change:
|
|
55
|
+
```typescript
|
|
56
|
+
const { showCacheTokens = true, showRateLimitEstimate = false, rateLimitPercentage } = options;
|
|
57
|
+
```
|
|
58
|
+
To:
|
|
59
|
+
```typescript
|
|
60
|
+
const { showCacheTokens = true, showRateLimitEstimate = true, rateLimitPercentage } = options;
|
|
61
|
+
```
|
|
62
|
+
This makes the default behavior match the config default (`display.showRateLimitEstimate: true`), so any callers that don't pass options still get the feature.
|
|
63
|
+
|
|
64
|
+
Also update `formatTokenLine` if it has a similar default for `showRateLimitEstimate`.
|
|
65
|
+
|
|
66
|
+
5. **Update tests** — Adjust any test expectations that rely on `showRateLimitEstimate` defaulting to `false`.
|
|
67
|
+
|
|
68
|
+
## Acceptance Criteria
|
|
69
|
+
- [ ] Per-task token summaries show rate limit percentage when `display.showRateLimitEstimate` is `true`
|
|
70
|
+
- [ ] Grand total summary shows rate limit percentage
|
|
71
|
+
- [ ] Setting `display.showRateLimitEstimate: false` in config suppresses the display
|
|
72
|
+
- [ ] `display.showCacheTokens` config is also wired through
|
|
73
|
+
- [ ] Tests pass
|
|
74
|
+
|
|
75
|
+
## Notes
|
|
76
|
+
- The `TokenTracker.getCumulativeRateLimitPercentage()` method already exists and works correctly
|
|
77
|
+
- The `getShowRateLimitEstimate()` and `getShowCacheTokens()` config helpers already exist in `src/utils/config.ts`
|
|
78
|
+
- All the pieces are in place — this task just wires them together
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
effort: medium
|
|
2
|
+
---
|
|
3
|
+
# Task: Add --get and --set flags to raf config
|
|
4
|
+
|
|
5
|
+
## Objective
|
|
6
|
+
Add `raf config --get [key]` and `raf config --set <key> <value>` for quick config viewing/editing without launching the interactive Claude wizard.
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
Currently `raf config` always launches an interactive Claude session (Sonnet). For quick operations like checking a single value or changing a model, this is slow and expensive. The `--get` and `--set` flags provide instant, non-interactive alternatives.
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
- `raf config --get` (no key): Print full merged config as JSON (defaults + overrides)
|
|
13
|
+
- `raf config --get models.plan`: Print plain value (e.g., `opus`) — no key prefix, no quotes for strings
|
|
14
|
+
- `raf config --set models.plan sonnet`: Set a leaf-level config value
|
|
15
|
+
- Only support leaf-level keys (dot-notation like `models.plan`, `timeout`, `display.showRateLimitEstimate`)
|
|
16
|
+
- Do NOT support setting entire nested objects (e.g., `--set models '{...}'`)
|
|
17
|
+
- If `--get` or `--set` is present, do NOT launch the interactive wizard
|
|
18
|
+
- If value matches default after `--set`, remove the key from config file (keep config minimal)
|
|
19
|
+
- If removing the last key from a section empties it, remove the section too
|
|
20
|
+
- Validate the value before saving (use existing validation)
|
|
21
|
+
- `--set` and `--get` are mutually exclusive; show error if both provided
|
|
22
|
+
- `--reset` takes precedence / is mutually exclusive with `--get`/`--set`
|
|
23
|
+
|
|
24
|
+
## Implementation Steps
|
|
25
|
+
|
|
26
|
+
1. **Edit `src/commands/config.ts`** — Add new options to the command:
|
|
27
|
+
```typescript
|
|
28
|
+
.option('--get [key]', 'Show config value (all config if no key, or specific dot-notation key)')
|
|
29
|
+
.option('--set <key> <value>', 'Set a config value using dot-notation key')
|
|
30
|
+
```
|
|
31
|
+
Note: Commander.js handles `--set key value` with two arguments. You may need to use `.option('--set <key_value...>')` or handle it as a positional pair. Consider using a variadic option that takes exactly 2 values.
|
|
32
|
+
|
|
33
|
+
2. **Update the `ConfigCommandOptions` interface**:
|
|
34
|
+
```typescript
|
|
35
|
+
interface ConfigCommandOptions {
|
|
36
|
+
reset?: boolean;
|
|
37
|
+
get?: true | string; // true when --get with no key, string when --get <key>
|
|
38
|
+
set?: string[]; // [key, value]
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
3. **Update the action handler** to check for `--get`/`--set` before launching the wizard:
|
|
43
|
+
```typescript
|
|
44
|
+
if (options.reset) { await handleReset(); return; }
|
|
45
|
+
if (options.get !== undefined) { handleGet(options.get); return; }
|
|
46
|
+
if (options.set) { handleSet(options.set); return; }
|
|
47
|
+
// ... existing wizard launch
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
4. **Implement `handleGet(key?: string | true)`**:
|
|
51
|
+
- If `key` is `true` (no argument): Print `JSON.stringify(resolveConfig(), null, 2)`
|
|
52
|
+
- If `key` is a string: Use dot-notation to traverse the resolved config object
|
|
53
|
+
- Print the value: strings plain, numbers/booleans as-is, objects as JSON
|
|
54
|
+
- If key not found, print error and exit with code 1
|
|
55
|
+
|
|
56
|
+
5. **Implement `handleSet(args: string[])`**:
|
|
57
|
+
- Parse `args` as `[key, value]`
|
|
58
|
+
- Read the current user config file (or start with `{}`)
|
|
59
|
+
- Parse the value: try `JSON.parse(value)` first (for numbers, booleans), fall back to string
|
|
60
|
+
- Use dot-notation to set the leaf value in the user config object
|
|
61
|
+
- Check if the value matches the default at that path — if so, remove the key instead
|
|
62
|
+
- If removing leaves an empty parent object, remove the parent too
|
|
63
|
+
- Validate the resulting config with `validateConfig()`
|
|
64
|
+
- Save with `saveConfig()`
|
|
65
|
+
- If config becomes empty `{}`, delete the file entirely
|
|
66
|
+
- Print confirmation message
|
|
67
|
+
|
|
68
|
+
6. **Add helper functions**:
|
|
69
|
+
- `getNestedValue(obj, dotPath)` — traverse an object by dot-notation path
|
|
70
|
+
- `setNestedValue(obj, dotPath, value)` — set a leaf value by dot-notation path
|
|
71
|
+
- `deleteNestedValue(obj, dotPath)` — remove a leaf and clean up empty parents
|
|
72
|
+
- `getDefaultValue(dotPath)` — get the default value at a dot-notation path from `DEFAULT_CONFIG`
|
|
73
|
+
|
|
74
|
+
7. **Add tests** in `tests/unit/config-command.test.ts` (or update existing):
|
|
75
|
+
- `--get` with no key returns full config JSON
|
|
76
|
+
- `--get models.plan` returns just the value
|
|
77
|
+
- `--get nonexistent.key` exits with error
|
|
78
|
+
- `--set models.plan sonnet` updates the config file
|
|
79
|
+
- `--set timeout 120` parses as number
|
|
80
|
+
- `--set autoCommit false` parses as boolean
|
|
81
|
+
- `--set models.plan opus` (default value) removes the key from file
|
|
82
|
+
- Setting last key in section removes the section
|
|
83
|
+
- Validation errors are reported
|
|
84
|
+
|
|
85
|
+
## Acceptance Criteria
|
|
86
|
+
- [ ] `raf config --get` prints full merged config as formatted JSON
|
|
87
|
+
- [ ] `raf config --get models.plan` prints plain value (e.g., `opus`)
|
|
88
|
+
- [ ] `raf config --set models.plan sonnet` updates config file
|
|
89
|
+
- [ ] Setting value to default removes it from config file
|
|
90
|
+
- [ ] Empty sections cleaned up after removal
|
|
91
|
+
- [ ] Value parsing handles strings, numbers, and booleans
|
|
92
|
+
- [ ] Invalid keys/values show clear error messages
|
|
93
|
+
- [ ] Interactive wizard NOT launched when --get or --set is present
|
|
94
|
+
- [ ] Tests pass
|
|
95
|
+
|
|
96
|
+
## Notes
|
|
97
|
+
- The `resolveConfig()` function already merges defaults + overrides, perfect for `--get`
|
|
98
|
+
- The `saveConfig()` function already handles file creation and formatting
|
|
99
|
+
- The `validateConfig()` function validates the entire user config, so validate after modification
|
|
100
|
+
- For `--get`, use the resolved (merged) config; for `--set`, read/write only the user config file
|
|
101
|
+
- Commander.js variadic options: `--set <key> <value>` can be done with `.option('--set <items...>')` which collects remaining args
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
effort: medium
|
|
2
|
+
---
|
|
3
|
+
# Task: Sync worktree branch with main before execution
|
|
4
|
+
|
|
5
|
+
## Objective
|
|
6
|
+
Before executing tasks in a worktree, rebase the worktree branch onto the latest main branch to ensure it's up-to-date.
|
|
7
|
+
|
|
8
|
+
## Dependencies
|
|
9
|
+
03
|
|
10
|
+
|
|
11
|
+
## Context
|
|
12
|
+
When using `raf do` in worktree mode, the worktree branch may be behind main (e.g., if other worktree projects were merged into main since this branch was created). Rebasing before execution ensures the branch starts from the latest main, reducing merge conflicts later.
|
|
13
|
+
|
|
14
|
+
## Requirements
|
|
15
|
+
- After `pullMainBranch()` syncs main, rebase the worktree branch onto main
|
|
16
|
+
- Only do this in worktree mode in `raf do`
|
|
17
|
+
- If rebase has conflicts: abort the rebase, show a warning, and continue execution without sync
|
|
18
|
+
- The rebase should happen after worktree project selection but before task execution begins
|
|
19
|
+
- Respect the `syncMainBranch` config setting — skip if disabled
|
|
20
|
+
|
|
21
|
+
## Implementation Steps
|
|
22
|
+
|
|
23
|
+
1. **Add a `rebaseOntoMain` function to `src/core/worktree.ts`**:
|
|
24
|
+
```typescript
|
|
25
|
+
export interface RebaseResult {
|
|
26
|
+
success: boolean;
|
|
27
|
+
error?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function rebaseOntoMain(mainBranch: string, cwd: string): RebaseResult {
|
|
31
|
+
try {
|
|
32
|
+
execSync(`git rebase ${mainBranch}`, {
|
|
33
|
+
encoding: 'utf-8',
|
|
34
|
+
stdio: 'pipe',
|
|
35
|
+
cwd,
|
|
36
|
+
});
|
|
37
|
+
return { success: true };
|
|
38
|
+
} catch (error) {
|
|
39
|
+
// Abort the failed rebase to restore clean state
|
|
40
|
+
try {
|
|
41
|
+
execSync('git rebase --abort', {
|
|
42
|
+
encoding: 'utf-8',
|
|
43
|
+
stdio: 'pipe',
|
|
44
|
+
cwd,
|
|
45
|
+
});
|
|
46
|
+
} catch {
|
|
47
|
+
// Ignore abort errors
|
|
48
|
+
}
|
|
49
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
50
|
+
return { success: false, error: msg };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
2. **Edit `src/commands/do.ts`** — Add the rebase step after `pullMainBranch()` in the worktree mode section (after line 251, before task execution). The rebase should run inside the worktree directory:
|
|
56
|
+
```typescript
|
|
57
|
+
// After pullMainBranch sync and worktree project selection...
|
|
58
|
+
// Rebase worktree branch onto main before execution
|
|
59
|
+
if (getSyncMainBranch() && worktreeCwd) {
|
|
60
|
+
const mainBranch = syncResult?.mainBranch ?? detectMainBranch();
|
|
61
|
+
if (mainBranch) {
|
|
62
|
+
const rebaseResult = rebaseOntoMain(mainBranch, worktreeCwd);
|
|
63
|
+
if (rebaseResult.success) {
|
|
64
|
+
logger.info(`Rebased onto ${mainBranch}`);
|
|
65
|
+
} else {
|
|
66
|
+
logger.warn(`Could not rebase onto ${mainBranch}: ${rebaseResult.error}`);
|
|
67
|
+
logger.warn('Continuing with current branch state.');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
Note: The exact placement depends on where `worktreeCwd` is set. Find the right location — it should be after the worktree is validated/created and `worktreeCwd` is determined, but before the task execution loop begins.
|
|
73
|
+
|
|
74
|
+
3. **Determine the main branch name** — The `pullMainBranch()` result includes `mainBranch`. Store it so it can be used for the rebase. If `pullMainBranch()` wasn't called (syncMainBranch disabled), you can detect main branch with the existing `detectMainBranch()` utility from `worktree.ts`.
|
|
75
|
+
|
|
76
|
+
4. **Add tests** for the new `rebaseOntoMain` function:
|
|
77
|
+
- Successful rebase
|
|
78
|
+
- Rebase with conflicts → aborts and returns failure
|
|
79
|
+
- Verify `git rebase --abort` is called on failure
|
|
80
|
+
|
|
81
|
+
## Acceptance Criteria
|
|
82
|
+
- [ ] Worktree branch is rebased onto main before task execution
|
|
83
|
+
- [ ] Conflicts cause rebase abort + warning, execution continues
|
|
84
|
+
- [ ] Rebase only happens in worktree mode
|
|
85
|
+
- [ ] Respects `syncMainBranch` config setting
|
|
86
|
+
- [ ] Main branch name correctly determined from sync result or detection
|
|
87
|
+
- [ ] Tests pass
|
|
88
|
+
|
|
89
|
+
## Notes
|
|
90
|
+
- The rebase should use the local main branch ref (which was just synced by `pullMainBranch`)
|
|
91
|
+
- If `pullMainBranch` failed (e.g., diverged — now returns `success: false` after task 03), the rebase should still attempt using whatever local main is available
|
|
92
|
+
- This runs in the worktree directory (`cwd`), not the main repo
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
effort: medium
|
|
3
|
+
---
|
|
4
|
+
# Task: Update plan frontmatter to standard Obsidian format
|
|
5
|
+
|
|
6
|
+
## Objective
|
|
7
|
+
Change the plan file frontmatter format from closing-delimiter-only to standard Obsidian/YAML frontmatter with both opening and closing `---` delimiters.
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
Currently plan files use a non-standard format with only a closing `---`:
|
|
11
|
+
```
|
|
12
|
+
effort: medium
|
|
13
|
+
---
|
|
14
|
+
# Task: ...
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Standard Obsidian (and Jekyll/Hugo/etc.) frontmatter uses `---` on both top and bottom:
|
|
18
|
+
```
|
|
19
|
+
---
|
|
20
|
+
effort: medium
|
|
21
|
+
---
|
|
22
|
+
# Task: ...
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
This change aligns RAF with the widely-used standard format, improving compatibility with Obsidian and other markdown tools.
|
|
26
|
+
|
|
27
|
+
## Requirements
|
|
28
|
+
- Update the frontmatter parser to support the standard `---`/`---` format
|
|
29
|
+
- Also keep backward compatibility with the old closing-only format (existing plan files should still parse)
|
|
30
|
+
- Update all prompts that instruct Claude to generate frontmatter
|
|
31
|
+
- Update CLAUDE.md documentation
|
|
32
|
+
- Update existing plan files in this project to use the new format (if any are being generated)
|
|
33
|
+
|
|
34
|
+
## Implementation Steps
|
|
35
|
+
|
|
36
|
+
1. **Edit `src/utils/frontmatter.ts`** — Update `parsePlanFrontmatter()` to handle both formats:
|
|
37
|
+
- Check if content starts with `---` (after optional leading whitespace/newline)
|
|
38
|
+
- If it does: standard format — find the closing `---` after it, extract content between the two delimiters
|
|
39
|
+
- If it doesn't: legacy format — find the first `---` and treat everything before it as frontmatter (current behavior)
|
|
40
|
+
|
|
41
|
+
Updated parsing logic:
|
|
42
|
+
```typescript
|
|
43
|
+
export function parsePlanFrontmatter(content: string): FrontmatterParseResult {
|
|
44
|
+
const result: FrontmatterParseResult = { frontmatter: {}, hasFrontmatter: false, warnings: [] };
|
|
45
|
+
const trimmedContent = content.trimStart();
|
|
46
|
+
|
|
47
|
+
let frontmatterSection: string;
|
|
48
|
+
|
|
49
|
+
if (trimmedContent.startsWith('---')) {
|
|
50
|
+
// Standard format: ---\nkey: value\n---
|
|
51
|
+
const afterOpener = trimmedContent.substring(3);
|
|
52
|
+
// Skip the rest of the opener line (handles "---\n" or "--- \n")
|
|
53
|
+
const openerEnd = afterOpener.indexOf('\n');
|
|
54
|
+
if (openerEnd === -1) return result;
|
|
55
|
+
const rest = afterOpener.substring(openerEnd + 1);
|
|
56
|
+
const closerIndex = rest.indexOf('---');
|
|
57
|
+
if (closerIndex === -1) return result;
|
|
58
|
+
frontmatterSection = rest.substring(0, closerIndex);
|
|
59
|
+
} else {
|
|
60
|
+
// Legacy format: key: value\n---
|
|
61
|
+
const delimiterIndex = content.indexOf('---');
|
|
62
|
+
if (delimiterIndex === -1) return result;
|
|
63
|
+
frontmatterSection = content.substring(0, delimiterIndex);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ... rest of key: value parsing remains the same
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
2. **Update prompts** — Edit these files to show the new format with opening `---`:
|
|
71
|
+
- `src/prompts/planning.ts` — Update the frontmatter example/instructions
|
|
72
|
+
- `src/prompts/amend.ts` — Same update
|
|
73
|
+
- Search for any other prompts that reference frontmatter format
|
|
74
|
+
|
|
75
|
+
3. **Update CLAUDE.md** — Change the plan file format documentation from:
|
|
76
|
+
```
|
|
77
|
+
effort: medium
|
|
78
|
+
---
|
|
79
|
+
```
|
|
80
|
+
To:
|
|
81
|
+
```
|
|
82
|
+
---
|
|
83
|
+
effort: medium
|
|
84
|
+
---
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
4. **Update tests** in `tests/unit/frontmatter.test.ts`:
|
|
88
|
+
- Add tests for the new standard format (opening + closing `---`)
|
|
89
|
+
- Keep tests for legacy format (closing only) to verify backward compatibility
|
|
90
|
+
- Test edge cases: extra whitespace before opening `---`, empty frontmatter block (`---\n---`)
|
|
91
|
+
|
|
92
|
+
5. **Update this project's planning prompt** — The system prompt at the top of this conversation also references the frontmatter format. This is injected by RAF, so updating the prompts in step 2 covers future projects.
|
|
93
|
+
|
|
94
|
+
## Acceptance Criteria
|
|
95
|
+
- [ ] Parser handles standard `---`/`---` format correctly
|
|
96
|
+
- [ ] Parser still handles legacy closing-only format (backward compatibility)
|
|
97
|
+
- [ ] Planning prompts generate the new standard format
|
|
98
|
+
- [ ] CLAUDE.md documentation updated
|
|
99
|
+
- [ ] Tests cover both old and new formats
|
|
100
|
+
- [ ] All existing tests pass
|
|
101
|
+
|
|
102
|
+
## Notes
|
|
103
|
+
- Backward compatibility is important since existing projects have plan files in the old format
|
|
104
|
+
- The parser should be robust: handle `---` with trailing spaces, `---` with trailing content on the same line, etc.
|
|
105
|
+
- Obsidian treats `---` at the very start of a file as YAML frontmatter — this is the standard we're adopting
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
effort: low
|
|
2
|
+
---
|
|
3
|
+
# Task: Remove Token Report from Plan Command
|
|
4
|
+
|
|
5
|
+
## Objective
|
|
6
|
+
Remove all token usage reporting from `raf plan` and `raf plan --amend`, including the session-parser module that only exists to support this feature.
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
The token report shown after planning sessions is not useful — planning is an interactive session where token cost is less relevant than during automated task execution. Removing it simplifies the plan command and eliminates dead code.
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
- Remove token reporting from both `runPlanCommand()` and `runAmendCommand()` in `src/commands/plan.ts`
|
|
13
|
+
- Delete the `displayPlanSessionTokenSummary()` function entirely
|
|
14
|
+
- Delete `src/utils/session-parser.ts` (only consumer is the plan command)
|
|
15
|
+
- Delete `tests/unit/session-parser.test.ts`
|
|
16
|
+
- Remove the `sessionId` parameter from `claudeRunner.runInteractive()` in `src/core/claude-runner.ts` (no other callers use it)
|
|
17
|
+
- Clean up all unused imports in affected files
|
|
18
|
+
|
|
19
|
+
## Implementation Steps
|
|
20
|
+
|
|
21
|
+
1. **Edit `src/commands/plan.ts`:**
|
|
22
|
+
- Remove imports: `TokenTracker`, `formatTokenTotalSummary`, `TokenSummaryOptions`, `parseSessionById`, `crypto`
|
|
23
|
+
- Remove `getDisplayConfig` and `getPricingConfig` from the config import (if not used elsewhere in the file)
|
|
24
|
+
- In `runPlanCommand()`: remove `sessionId` generation, `sessionCwd` variable, remove `sessionId` from `claudeRunner.runInteractive()` options, remove `displayPlanSessionTokenSummary()` call
|
|
25
|
+
- In `runAmendCommand()`: same removals as above
|
|
26
|
+
- Delete the entire `displayPlanSessionTokenSummary()` function (around lines 702-741)
|
|
27
|
+
|
|
28
|
+
2. **Edit `src/core/claude-runner.ts`:**
|
|
29
|
+
- Remove `sessionId` from the `runInteractive()` method's options interface/parameter
|
|
30
|
+
- Remove any internal usage of `sessionId` (e.g., passing `--session-id` flag to Claude CLI)
|
|
31
|
+
|
|
32
|
+
3. **Delete files:**
|
|
33
|
+
- `src/utils/session-parser.ts`
|
|
34
|
+
- `tests/unit/session-parser.test.ts`
|
|
35
|
+
|
|
36
|
+
4. **Verify:** Run `npm run build` and `npm test` to ensure no broken imports or failing tests
|
|
37
|
+
|
|
38
|
+
## Acceptance Criteria
|
|
39
|
+
- [ ] `raf plan` no longer displays a token usage summary after the planning session
|
|
40
|
+
- [ ] `raf plan --amend` no longer displays a token usage summary after the amendment session
|
|
41
|
+
- [ ] `src/utils/session-parser.ts` is deleted
|
|
42
|
+
- [ ] `tests/unit/session-parser.test.ts` is deleted
|
|
43
|
+
- [ ] `sessionId` parameter removed from `runInteractive()` API
|
|
44
|
+
- [ ] No unused imports remain in modified files
|
|
45
|
+
- [ ] Build passes (`npm run build`)
|
|
46
|
+
- [ ] Tests pass (`npm test`)
|
|
47
|
+
|
|
48
|
+
## Notes
|
|
49
|
+
- The `TokenTracker`, `formatTokenTotalSummary`, and `formatTaskTokenSummary` utilities in `token-tracker.ts` and `terminal-symbols.ts` are still used by `raf do` — do NOT delete those modules
|
|
50
|
+
- The `display` and `pricing` config sections remain valid for `raf do` usage — do NOT remove them from config types/defaults
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4UpC,wBAAgB,mBAAmB,IAAI,OAAO,CAsC7C"}
|