rafcode 2.3.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 +21 -4
- package/RAF/ahvrih-rate-forge/decisions.md +70 -0
- package/RAF/ahvrih-rate-forge/input.md +44 -0
- package/RAF/ahvrih-rate-forge/outcomes/01-remove-claude-command-config.md +58 -0
- package/RAF/ahvrih-rate-forge/outcomes/02-fix-mixed-attempt-cost.md +46 -0
- package/RAF/ahvrih-rate-forge/outcomes/03-rate-limit-estimation.md +82 -0
- package/RAF/ahvrih-rate-forge/outcomes/04-show-version-in-do-logs.md +45 -0
- package/RAF/ahvrih-rate-forge/outcomes/05-sync-main-before-worktree.md +96 -0
- package/RAF/ahvrih-rate-forge/outcomes/06-sync-readme-with-codebase.md +45 -0
- package/RAF/ahvrih-rate-forge/outcomes/07-no-session-persistence.md +26 -0
- package/RAF/ahvrih-rate-forge/outcomes/08-plan-execution-metadata.md +130 -0
- package/RAF/ahvrih-rate-forge/plans/01-remove-claude-command-config.md +36 -0
- package/RAF/ahvrih-rate-forge/plans/02-fix-mixed-attempt-cost.md +33 -0
- package/RAF/ahvrih-rate-forge/plans/03-rate-limit-estimation.md +82 -0
- package/RAF/ahvrih-rate-forge/plans/04-show-version-in-do-logs.md +32 -0
- package/RAF/ahvrih-rate-forge/plans/05-sync-main-before-worktree.md +40 -0
- package/RAF/ahvrih-rate-forge/plans/06-sync-readme-with-codebase.md +61 -0
- package/RAF/ahvrih-rate-forge/plans/07-no-session-persistence.md +28 -0
- package/RAF/ahvrih-rate-forge/plans/08-plan-execution-metadata.md +123 -0
- 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/README.md +27 -7
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +209 -6
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/do.d.ts.map +1 -1
- package/dist/commands/do.js +140 -21
- package/dist/commands/do.js.map +1 -1
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +27 -5
- 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 +4 -9
- 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 +3 -3
- package/dist/core/failure-analyzer.js.map +1 -1
- package/dist/core/pull-request.js +3 -3
- package/dist/core/pull-request.js.map +1 -1
- package/dist/core/state-derivation.d.ts +5 -0
- package/dist/core/state-derivation.d.ts.map +1 -1
- package/dist/core/state-derivation.js +14 -4
- package/dist/core/state-derivation.js.map +1 -1
- package/dist/core/worktree.d.ts +44 -0
- package/dist/core/worktree.d.ts.map +1 -1
- package/dist/core/worktree.js +247 -0
- package/dist/core/worktree.js.map +1 -1
- package/dist/prompts/amend.d.ts.map +1 -1
- package/dist/prompts/amend.js +28 -11
- package/dist/prompts/amend.js.map +1 -1
- package/dist/prompts/planning.d.ts.map +1 -1
- package/dist/prompts/planning.js +28 -11
- package/dist/prompts/planning.js.map +1 -1
- package/dist/types/config.d.ts +30 -13
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +14 -10
- package/dist/types/config.js.map +1 -1
- package/dist/utils/config.d.ts +47 -4
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +176 -30
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/frontmatter.d.ts +53 -0
- package/dist/utils/frontmatter.d.ts.map +1 -0
- package/dist/utils/frontmatter.js +115 -0
- package/dist/utils/frontmatter.js.map +1 -0
- package/dist/utils/name-generator.d.ts.map +1 -1
- package/dist/utils/name-generator.js +9 -19
- package/dist/utils/name-generator.js.map +1 -1
- package/dist/utils/session-parser.d.ts +44 -0
- package/dist/utils/session-parser.d.ts.map +1 -0
- package/dist/utils/session-parser.js +122 -0
- package/dist/utils/session-parser.js.map +1 -0
- package/dist/utils/terminal-symbols.d.ts +22 -3
- package/dist/utils/terminal-symbols.d.ts.map +1 -1
- package/dist/utils/terminal-symbols.js +52 -18
- package/dist/utils/terminal-symbols.js.map +1 -1
- package/dist/utils/token-tracker.d.ts +20 -0
- package/dist/utils/token-tracker.d.ts.map +1 -1
- package/dist/utils/token-tracker.js +57 -2
- package/dist/utils/token-tracker.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/config.ts +242 -7
- package/src/commands/do.ts +177 -23
- package/src/commands/plan.ts +27 -4
- package/src/core/claude-runner.ts +4 -16
- package/src/core/failure-analyzer.ts +3 -3
- package/src/core/pull-request.ts +3 -3
- package/src/core/state-derivation.ts +20 -4
- package/src/core/worktree.ts +266 -0
- package/src/prompts/amend.ts +28 -11
- package/src/prompts/config-docs.md +91 -29
- package/src/prompts/planning.ts +28 -11
- package/src/types/config.ts +46 -21
- package/src/utils/config.ts +200 -33
- package/src/utils/frontmatter.ts +140 -0
- package/src/utils/name-generator.ts +9 -19
- package/src/utils/terminal-symbols.ts +68 -16
- package/src/utils/token-tracker.ts +65 -2
- package/tests/unit/claude-runner-interactive.test.ts +8 -6
- package/tests/unit/claude-runner.test.ts +5 -66
- 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 +176 -6
- package/tests/unit/config.test.ts +268 -45
- package/tests/unit/frontmatter.test.ts +276 -0
- package/tests/unit/name-generator.test.ts +1 -1
- package/tests/unit/post-execution-picker.test.ts +6 -0
- package/tests/unit/terminal-symbols.test.ts +142 -0
- package/tests/unit/token-tracker.test.ts +304 -1
- package/tests/unit/validation.test.ts +6 -4
- package/tests/unit/worktree.test.ts +309 -0
package/src/core/worktree.ts
CHANGED
|
@@ -432,3 +432,269 @@ export function resolveWorktreeProjectByIdentifier(
|
|
|
432
432
|
// Ambiguous or no match
|
|
433
433
|
return null;
|
|
434
434
|
}
|
|
435
|
+
|
|
436
|
+
export interface SyncMainBranchResult {
|
|
437
|
+
success: boolean;
|
|
438
|
+
/** The detected main branch name (e.g., 'main' or 'master') */
|
|
439
|
+
mainBranch: string | null;
|
|
440
|
+
/** Whether any changes were pulled */
|
|
441
|
+
hadChanges: boolean;
|
|
442
|
+
error?: string;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export interface RebaseResult {
|
|
446
|
+
success: boolean;
|
|
447
|
+
error?: string;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Detect the main branch name from the remote.
|
|
452
|
+
* Uses refs/remotes/origin/HEAD, falling back to main/master.
|
|
453
|
+
* Reuses the same logic as detectBaseBranch from pull-request.ts.
|
|
454
|
+
*/
|
|
455
|
+
export function detectMainBranch(cwd?: string): string | null {
|
|
456
|
+
// Try to find the default branch from the remote
|
|
457
|
+
try {
|
|
458
|
+
const output = execSync('git symbolic-ref refs/remotes/origin/HEAD', {
|
|
459
|
+
encoding: 'utf-8',
|
|
460
|
+
stdio: 'pipe',
|
|
461
|
+
...(cwd ? { cwd } : {}),
|
|
462
|
+
}).trim();
|
|
463
|
+
// Output is like "refs/remotes/origin/main"
|
|
464
|
+
const parts = output.split('/');
|
|
465
|
+
return parts[parts.length - 1] ?? null;
|
|
466
|
+
} catch {
|
|
467
|
+
// Fallback: check for common branch names
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Try common default branches
|
|
471
|
+
for (const branch of ['main', 'master']) {
|
|
472
|
+
try {
|
|
473
|
+
execSync(`git rev-parse --verify "refs/heads/${branch}"`, {
|
|
474
|
+
encoding: 'utf-8',
|
|
475
|
+
stdio: 'pipe',
|
|
476
|
+
...(cwd ? { cwd } : {}),
|
|
477
|
+
});
|
|
478
|
+
return branch;
|
|
479
|
+
} catch {
|
|
480
|
+
// Try next
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Pull the main branch from remote before worktree creation.
|
|
489
|
+
* Uses fetch + merge --ff-only to safely update the main branch.
|
|
490
|
+
* This ensures the worktree is created from the latest remote state.
|
|
491
|
+
*
|
|
492
|
+
* Only pulls if currently on the main branch or if the main branch exists.
|
|
493
|
+
* Fails gracefully if there are conflicts or the branch has diverged.
|
|
494
|
+
*
|
|
495
|
+
* @param cwd - The directory to run git commands in (defaults to current directory)
|
|
496
|
+
*/
|
|
497
|
+
export function pullMainBranch(cwd?: string): SyncMainBranchResult {
|
|
498
|
+
const mainBranch = detectMainBranch(cwd);
|
|
499
|
+
|
|
500
|
+
if (!mainBranch) {
|
|
501
|
+
return {
|
|
502
|
+
success: false,
|
|
503
|
+
mainBranch: null,
|
|
504
|
+
hadChanges: false,
|
|
505
|
+
error: 'Could not detect main branch (no origin/HEAD or main/master found)',
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Get current branch to check if we need to switch
|
|
510
|
+
const currentBranch = getCurrentBranch();
|
|
511
|
+
|
|
512
|
+
// If not on main, we need to fetch and update the main branch ref
|
|
513
|
+
// without checking it out (to avoid disrupting the user's work)
|
|
514
|
+
if (currentBranch !== mainBranch) {
|
|
515
|
+
// Fetch the main branch from origin
|
|
516
|
+
try {
|
|
517
|
+
execSync(`git fetch origin ${mainBranch}:${mainBranch}`, {
|
|
518
|
+
encoding: 'utf-8',
|
|
519
|
+
stdio: 'pipe',
|
|
520
|
+
...(cwd ? { cwd } : {}),
|
|
521
|
+
});
|
|
522
|
+
logger.debug(`Fetched ${mainBranch} from origin`);
|
|
523
|
+
return {
|
|
524
|
+
success: true,
|
|
525
|
+
mainBranch,
|
|
526
|
+
hadChanges: true, // We fetched updates (may or may not have actual changes)
|
|
527
|
+
};
|
|
528
|
+
} catch (error) {
|
|
529
|
+
// This can fail if the local main has diverged from remote
|
|
530
|
+
// Try a simple fetch without updating the local ref
|
|
531
|
+
try {
|
|
532
|
+
execSync(`git fetch origin ${mainBranch}`, {
|
|
533
|
+
encoding: 'utf-8',
|
|
534
|
+
stdio: 'pipe',
|
|
535
|
+
...(cwd ? { cwd } : {}),
|
|
536
|
+
});
|
|
537
|
+
logger.debug(`Fetched origin/${mainBranch} (local ${mainBranch} diverged, not updated)`);
|
|
538
|
+
return {
|
|
539
|
+
success: false,
|
|
540
|
+
mainBranch,
|
|
541
|
+
hadChanges: false,
|
|
542
|
+
error: `Local ${mainBranch} has diverged from origin, not updated`,
|
|
543
|
+
};
|
|
544
|
+
} catch (fetchError) {
|
|
545
|
+
const msg = fetchError instanceof Error ? fetchError.message : String(fetchError);
|
|
546
|
+
return {
|
|
547
|
+
success: false,
|
|
548
|
+
mainBranch,
|
|
549
|
+
hadChanges: false,
|
|
550
|
+
error: `Failed to fetch ${mainBranch}: ${msg}`,
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// Currently on main branch - can do a regular pull with ff-only
|
|
557
|
+
// First, check for uncommitted changes that would block the pull
|
|
558
|
+
try {
|
|
559
|
+
const status = execSync('git status --porcelain', {
|
|
560
|
+
encoding: 'utf-8',
|
|
561
|
+
stdio: 'pipe',
|
|
562
|
+
...(cwd ? { cwd } : {}),
|
|
563
|
+
}).trim();
|
|
564
|
+
|
|
565
|
+
if (status) {
|
|
566
|
+
return {
|
|
567
|
+
success: false,
|
|
568
|
+
mainBranch,
|
|
569
|
+
hadChanges: false,
|
|
570
|
+
error: `Cannot pull ${mainBranch}: uncommitted changes in working directory`,
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
} catch (error) {
|
|
574
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
575
|
+
return {
|
|
576
|
+
success: false,
|
|
577
|
+
mainBranch,
|
|
578
|
+
hadChanges: false,
|
|
579
|
+
error: `Failed to check git status: ${msg}`,
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Fetch and merge with ff-only
|
|
584
|
+
try {
|
|
585
|
+
execSync(`git fetch origin ${mainBranch}`, {
|
|
586
|
+
encoding: 'utf-8',
|
|
587
|
+
stdio: 'pipe',
|
|
588
|
+
...(cwd ? { cwd } : {}),
|
|
589
|
+
});
|
|
590
|
+
} catch (error) {
|
|
591
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
592
|
+
return {
|
|
593
|
+
success: false,
|
|
594
|
+
mainBranch,
|
|
595
|
+
hadChanges: false,
|
|
596
|
+
error: `Failed to fetch from origin: ${msg}`,
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
try {
|
|
601
|
+
const output = execSync(`git merge --ff-only origin/${mainBranch}`, {
|
|
602
|
+
encoding: 'utf-8',
|
|
603
|
+
stdio: 'pipe',
|
|
604
|
+
...(cwd ? { cwd } : {}),
|
|
605
|
+
});
|
|
606
|
+
const hadChanges = !output.includes('Already up to date');
|
|
607
|
+
return {
|
|
608
|
+
success: true,
|
|
609
|
+
mainBranch,
|
|
610
|
+
hadChanges,
|
|
611
|
+
};
|
|
612
|
+
} catch (error) {
|
|
613
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
614
|
+
return {
|
|
615
|
+
success: false,
|
|
616
|
+
mainBranch,
|
|
617
|
+
hadChanges: false,
|
|
618
|
+
error: `Cannot fast-forward ${mainBranch}: ${msg.includes('Not possible to fast-forward') ? 'branch has diverged from origin' : msg}`,
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Push the main branch to remote before PR creation.
|
|
625
|
+
* Ensures the PR base is up to date.
|
|
626
|
+
*
|
|
627
|
+
* @param cwd - The directory to run git commands in (defaults to current directory)
|
|
628
|
+
*/
|
|
629
|
+
export function pushMainBranch(cwd?: string): SyncMainBranchResult {
|
|
630
|
+
const mainBranch = detectMainBranch(cwd);
|
|
631
|
+
|
|
632
|
+
if (!mainBranch) {
|
|
633
|
+
return {
|
|
634
|
+
success: false,
|
|
635
|
+
mainBranch: null,
|
|
636
|
+
hadChanges: false,
|
|
637
|
+
error: 'Could not detect main branch (no origin/HEAD or main/master found)',
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
try {
|
|
642
|
+
execSync(`git push origin ${mainBranch}`, {
|
|
643
|
+
encoding: 'utf-8',
|
|
644
|
+
stdio: 'pipe',
|
|
645
|
+
...(cwd ? { cwd } : {}),
|
|
646
|
+
});
|
|
647
|
+
return {
|
|
648
|
+
success: true,
|
|
649
|
+
mainBranch,
|
|
650
|
+
hadChanges: true,
|
|
651
|
+
};
|
|
652
|
+
} catch (error) {
|
|
653
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
654
|
+
// Check if it's just "already up to date"
|
|
655
|
+
if (msg.includes('Everything up-to-date')) {
|
|
656
|
+
return {
|
|
657
|
+
success: true,
|
|
658
|
+
mainBranch,
|
|
659
|
+
hadChanges: false,
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
return {
|
|
663
|
+
success: false,
|
|
664
|
+
mainBranch,
|
|
665
|
+
hadChanges: false,
|
|
666
|
+
error: `Failed to push ${mainBranch}: ${msg}`,
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* Rebase the current branch onto the main branch.
|
|
673
|
+
* If the rebase fails with conflicts, aborts the rebase and returns failure.
|
|
674
|
+
*
|
|
675
|
+
* @param mainBranch - The main branch name to rebase onto (e.g., 'main' or 'master')
|
|
676
|
+
* @param cwd - The directory to run git commands in (defaults to current directory)
|
|
677
|
+
*/
|
|
678
|
+
export function rebaseOntoMain(mainBranch: string, cwd: string): RebaseResult {
|
|
679
|
+
try {
|
|
680
|
+
execSync(`git rebase ${mainBranch}`, {
|
|
681
|
+
encoding: 'utf-8',
|
|
682
|
+
stdio: 'pipe',
|
|
683
|
+
cwd,
|
|
684
|
+
});
|
|
685
|
+
return { success: true };
|
|
686
|
+
} catch (error) {
|
|
687
|
+
// Abort the failed rebase to restore clean state
|
|
688
|
+
try {
|
|
689
|
+
execSync('git rebase --abort', {
|
|
690
|
+
encoding: 'utf-8',
|
|
691
|
+
stdio: 'pipe',
|
|
692
|
+
cwd,
|
|
693
|
+
});
|
|
694
|
+
} catch {
|
|
695
|
+
// Ignore abort errors
|
|
696
|
+
}
|
|
697
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
698
|
+
return { success: false, error: msg };
|
|
699
|
+
}
|
|
700
|
+
}
|
package/src/prompts/amend.ts
CHANGED
|
@@ -135,9 +135,12 @@ After interviewing the user about all NEW tasks, create plan files starting from
|
|
|
135
135
|
- ${projectPath}/plans/${encodeTaskId(nextTaskNumber + 1)}-task-name.md
|
|
136
136
|
- etc.
|
|
137
137
|
|
|
138
|
-
Each plan file
|
|
138
|
+
Each plan file MUST have Obsidian-style frontmatter at the top, before the \`# Task:\` heading. The frontmatter uses standard YAML format with opening and closing \`---\` delimiters:
|
|
139
139
|
|
|
140
140
|
\`\`\`markdown
|
|
141
|
+
---
|
|
142
|
+
effort: medium
|
|
143
|
+
---
|
|
141
144
|
# Task: [Task Name]
|
|
142
145
|
|
|
143
146
|
## Objective
|
|
@@ -175,6 +178,24 @@ Each plan file should follow this structure:
|
|
|
175
178
|
[Reference to existing task outcomes if relevant]
|
|
176
179
|
\`\`\`
|
|
177
180
|
|
|
181
|
+
### Frontmatter Requirements
|
|
182
|
+
|
|
183
|
+
The \`effort\` field is REQUIRED in every plan file. It indicates task complexity and determines which Claude model will execute the task:
|
|
184
|
+
- \`effort: low\` — Trivial/mechanical changes, simple one-file edits, config changes
|
|
185
|
+
- \`effort: medium\` — Well-scoped feature work, bug fixes with clear plans, multi-file changes following existing patterns
|
|
186
|
+
- \`effort: high\` — Architectural changes, complex logic, tasks requiring deep codebase understanding
|
|
187
|
+
|
|
188
|
+
Optionally, you can add an explicit \`model\` field to override the effort-based model selection:
|
|
189
|
+
\`\`\`markdown
|
|
190
|
+
---
|
|
191
|
+
effort: medium
|
|
192
|
+
model: opus
|
|
193
|
+
---
|
|
194
|
+
# Task: ...
|
|
195
|
+
\`\`\`
|
|
196
|
+
|
|
197
|
+
This is rarely needed — prefer using the \`effort\` label so the user's config controls the actual model used.
|
|
198
|
+
|
|
178
199
|
### Step 5: Confirm Completion
|
|
179
200
|
|
|
180
201
|
After creating all new plan files:
|
|
@@ -201,19 +222,15 @@ Planning complete! To exit this session and run your tasks:
|
|
|
201
222
|
7. Specify task dependencies using the ## Dependencies section with task IDs only (e.g., "01, 02")
|
|
202
223
|
8. Tasks without dependencies should omit the Dependencies section entirely
|
|
203
224
|
9. Be specific - vague plans lead to poor execution
|
|
225
|
+
10. ALWAYS include the \`effort\` frontmatter field in every plan file — assess each task's complexity
|
|
204
226
|
|
|
205
227
|
## Plan Output Style
|
|
206
228
|
|
|
207
|
-
|
|
208
|
-
-
|
|
209
|
-
-
|
|
210
|
-
-
|
|
211
|
-
-
|
|
212
|
-
- Existing project files to modify
|
|
213
|
-
- Previous plan/outcome files for context
|
|
214
|
-
- Project structure and directories
|
|
215
|
-
- Let the executing agent decide implementation specifics
|
|
216
|
-
- Plans guide the work; they don't prescribe exact code`;
|
|
229
|
+
Plans can include whatever level of detail you deem helpful for the executing agent. Use your judgment:
|
|
230
|
+
- Include implementation details when they clarify the approach
|
|
231
|
+
- Code snippets are acceptable when they help illustrate a specific pattern
|
|
232
|
+
- File paths are helpful when referencing existing project files, patterns, or directories
|
|
233
|
+
- Focus on clarity — the goal is for the executing agent to understand what needs to be done`;
|
|
217
234
|
|
|
218
235
|
const userMessage = `I want to add the following new tasks to this project:
|
|
219
236
|
|
|
@@ -37,24 +37,36 @@ Controls which Claude model is used for each scenario. Values can be a short ali
|
|
|
37
37
|
| Key | Default | Description |
|
|
38
38
|
|-----|---------|-------------|
|
|
39
39
|
| `models.plan` | `"opus"` | Model used for planning sessions (`raf plan`) |
|
|
40
|
-
| `models.execute` | `"opus"` |
|
|
40
|
+
| `models.execute` | `"opus"` | Ceiling model for task execution (`raf do`). Per-task models from effort frontmatter are capped to this tier. Also used as the fallback when a plan has no effort frontmatter. |
|
|
41
41
|
| `models.nameGeneration` | `"sonnet"` | Model used for generating project names |
|
|
42
42
|
| `models.failureAnalysis` | `"haiku"` | Model used for analyzing task failures |
|
|
43
43
|
| `models.prGeneration` | `"sonnet"` | Model used for generating PR titles and descriptions |
|
|
44
44
|
| `models.config` | `"sonnet"` | Model used for the interactive config editor (`raf config`) |
|
|
45
45
|
|
|
46
|
-
### `
|
|
46
|
+
### `effortMapping` — Task Effort to Model Mapping
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
Maps task complexity labels (in plan frontmatter) to Claude models. When a plan file has `effort: medium`, RAF resolves the execution model using this mapping.
|
|
49
49
|
|
|
50
50
|
| Key | Default | Description |
|
|
51
51
|
|-----|---------|-------------|
|
|
52
|
-
| `
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
| `effortMapping.low` | `"haiku"` | Model for low-complexity tasks |
|
|
53
|
+
| `effortMapping.medium` | `"sonnet"` | Model for medium-complexity tasks |
|
|
54
|
+
| `effortMapping.high` | `"opus"` | Model for high-complexity tasks |
|
|
55
|
+
|
|
56
|
+
Values must be a short alias (`"sonnet"`, `"haiku"`, `"opus"`) or a full model ID.
|
|
57
|
+
|
|
58
|
+
**Interaction with `models.execute`**: The `models.execute` value acts as a ceiling. If a task's effort maps to a more expensive model than the ceiling, the ceiling model is used instead. This gives users budget control while allowing tasks to use cheaper models when appropriate.
|
|
59
|
+
|
|
60
|
+
Example:
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"models": { "execute": "sonnet" },
|
|
64
|
+
"effortMapping": { "low": "haiku", "medium": "sonnet", "high": "opus" }
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
- Task with `effort: low` → haiku (under ceiling)
|
|
68
|
+
- Task with `effort: medium` → sonnet (at ceiling)
|
|
69
|
+
- Task with `effort: high` → sonnet (capped to ceiling, not opus)
|
|
58
70
|
|
|
59
71
|
### `timeout` — Task Timeout
|
|
60
72
|
|
|
@@ -80,6 +92,18 @@ Controls the effort level passed to Claude for each scenario. All values must be
|
|
|
80
92
|
- **Default**: `false`
|
|
81
93
|
- **Description**: When `true`, `raf plan` and `raf do` default to worktree mode (isolated git worktree). Can be overridden per-command with `--worktree` flag.
|
|
82
94
|
|
|
95
|
+
### `syncMainBranch` — Sync Main Branch with Remote
|
|
96
|
+
|
|
97
|
+
- **Type**: boolean
|
|
98
|
+
- **Default**: `true`
|
|
99
|
+
- **Description**: When `true`, RAF automatically syncs the main branch with the remote before worktree operations:
|
|
100
|
+
- **Before worktree creation** (`raf plan --worktree`): Pulls the main branch from remote to ensure the worktree starts from the latest code
|
|
101
|
+
- **Before PR creation** (post-execution "Create PR" action): Pushes the main branch to remote so the PR base is up to date
|
|
102
|
+
|
|
103
|
+
The main branch is auto-detected from `refs/remotes/origin/HEAD`, falling back to `main` or `master` if not set.
|
|
104
|
+
|
|
105
|
+
Failures in sync operations produce warnings but don't block the workflow. For example, if the local main branch has diverged from remote, the sync will warn and continue.
|
|
106
|
+
|
|
83
107
|
### `commitFormat` — Commit Message Templates
|
|
84
108
|
|
|
85
109
|
Controls the format of git commit messages. Templates use `{placeholder}` syntax for variable substitution.
|
|
@@ -147,25 +171,62 @@ Example override:
|
|
|
147
171
|
|
|
148
172
|
Only specify the fields you want to change — unset fields keep their defaults.
|
|
149
173
|
|
|
150
|
-
### `
|
|
174
|
+
### `display` — Token Summary Display Options
|
|
175
|
+
|
|
176
|
+
Controls what information is shown in token usage summaries after tasks and in the grand total.
|
|
177
|
+
|
|
178
|
+
| Key | Default | Description |
|
|
179
|
+
|-----|---------|-------------|
|
|
180
|
+
| `display.showRateLimitEstimate` | `true` | Show estimated 5h rate limit window percentage (e.g., `~42% of 5h window`) |
|
|
181
|
+
| `display.showCacheTokens` | `true` | Show cache read/create token counts in summaries |
|
|
182
|
+
|
|
183
|
+
Example:
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"display": {
|
|
188
|
+
"showRateLimitEstimate": false,
|
|
189
|
+
"showCacheTokens": true
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### `rateLimitWindow` — Rate Limit Configuration
|
|
195
|
+
|
|
196
|
+
Controls the rate limit estimation calculation.
|
|
197
|
+
|
|
198
|
+
| Key | Default | Description |
|
|
199
|
+
|-----|---------|-------------|
|
|
200
|
+
| `rateLimitWindow.sonnetTokenCap` | `88000` | The Sonnet-equivalent token cap for the 5-hour window. All token usage is normalized to Sonnet-equivalent tokens using pricing ratios. |
|
|
201
|
+
|
|
202
|
+
The 5h window percentage is calculated as: `(estimatedCost / sonnetCostPerToken) / sonnetTokenCap * 100`
|
|
151
203
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
204
|
+
Where `sonnetCostPerToken` is derived from the configured Sonnet pricing. Heavier models (Opus) consume the window faster than lighter ones (Haiku) in proportion to their API pricing ratios.
|
|
205
|
+
|
|
206
|
+
Example:
|
|
207
|
+
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"rateLimitWindow": {
|
|
211
|
+
"sonnetTokenCap": 100000
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
155
215
|
|
|
156
216
|
## Validation Rules
|
|
157
217
|
|
|
158
218
|
The config is validated when loaded. Invalid configs cause an error with a descriptive message. The following rules are enforced:
|
|
159
219
|
|
|
160
220
|
- **Unknown keys are rejected** at every nesting level. Typos like `"model"` instead of `"models"` will be caught.
|
|
161
|
-
- **Model values** must be a short alias (`"sonnet"`, `"haiku"`, `"opus"`) or a full model ID matching the pattern `claude-{family}-{version}` (e.g., `"claude-sonnet-4-5-20250929"`).
|
|
162
|
-
-
|
|
221
|
+
- **Model values** (`models.*`, `effortMapping.*`) must be a short alias (`"sonnet"`, `"haiku"`, `"opus"`) or a full model ID matching the pattern `claude-{family}-{version}` (e.g., `"claude-sonnet-4-5-20250929"`).
|
|
222
|
+
- **`effortMapping` keys** must be `"low"`, `"medium"`, or `"high"`.
|
|
163
223
|
- **`timeout`** must be a positive finite number.
|
|
164
224
|
- **`maxRetries`** must be a non-negative integer.
|
|
165
|
-
- **`autoCommit
|
|
225
|
+
- **`autoCommit`**, **`worktree`**, and **`syncMainBranch`** must be booleans.
|
|
166
226
|
- **`commitFormat` values** must be strings.
|
|
167
|
-
- **`claudeCommand`** must be a non-empty string.
|
|
168
227
|
- **`pricing`** categories must be `"opus"`, `"sonnet"`, or `"haiku"`. Each field must be a non-negative number.
|
|
228
|
+
- **`display` values** (`showRateLimitEstimate`, `showCacheTokens`) must be booleans.
|
|
229
|
+
- **`rateLimitWindow.sonnetTokenCap`** must be a positive number.
|
|
169
230
|
- The config file must be valid JSON containing an object (not an array or primitive).
|
|
170
231
|
|
|
171
232
|
## CLI Precedence
|
|
@@ -199,14 +260,11 @@ Uses Sonnet instead of Opus for task execution. Everything else stays at default
|
|
|
199
260
|
"plan": "sonnet",
|
|
200
261
|
"execute": "sonnet"
|
|
201
262
|
},
|
|
202
|
-
"effort": {
|
|
203
|
-
"plan": "medium"
|
|
204
|
-
},
|
|
205
263
|
"worktree": true
|
|
206
264
|
}
|
|
207
265
|
```
|
|
208
266
|
|
|
209
|
-
Uses Sonnet for
|
|
267
|
+
Uses Sonnet for planning and caps task execution at Sonnet (tasks with `effort: high` will use Sonnet instead of Opus). Defaults to worktree mode.
|
|
210
268
|
|
|
211
269
|
### Full — All Settings Explicit
|
|
212
270
|
|
|
@@ -220,29 +278,33 @@ Uses Sonnet for both planning and execution, reduces planning effort, and defaul
|
|
|
220
278
|
"prGeneration": "sonnet",
|
|
221
279
|
"config": "sonnet"
|
|
222
280
|
},
|
|
223
|
-
"
|
|
224
|
-
"
|
|
225
|
-
"
|
|
226
|
-
"
|
|
227
|
-
"failureAnalysis": "low",
|
|
228
|
-
"prGeneration": "medium",
|
|
229
|
-
"config": "medium"
|
|
281
|
+
"effortMapping": {
|
|
282
|
+
"low": "haiku",
|
|
283
|
+
"medium": "sonnet",
|
|
284
|
+
"high": "opus"
|
|
230
285
|
},
|
|
231
286
|
"timeout": 60,
|
|
232
287
|
"maxRetries": 3,
|
|
233
288
|
"autoCommit": true,
|
|
234
289
|
"worktree": false,
|
|
290
|
+
"syncMainBranch": true,
|
|
235
291
|
"commitFormat": {
|
|
236
292
|
"task": "{prefix}[{projectId}:{taskId}] {description}",
|
|
237
293
|
"plan": "{prefix}[{projectId}] Plan: {projectName}",
|
|
238
294
|
"amend": "{prefix}[{projectId}] Amend: {projectName}",
|
|
239
295
|
"prefix": "RAF"
|
|
240
296
|
},
|
|
241
|
-
"claudeCommand": "claude",
|
|
242
297
|
"pricing": {
|
|
243
298
|
"opus": { "inputPerMTok": 15, "outputPerMTok": 75, "cacheReadPerMTok": 1.5, "cacheCreatePerMTok": 18.75 },
|
|
244
299
|
"sonnet": { "inputPerMTok": 3, "outputPerMTok": 15, "cacheReadPerMTok": 0.3, "cacheCreatePerMTok": 3.75 },
|
|
245
300
|
"haiku": { "inputPerMTok": 1, "outputPerMTok": 5, "cacheReadPerMTok": 0.1, "cacheCreatePerMTok": 1.25 }
|
|
301
|
+
},
|
|
302
|
+
"display": {
|
|
303
|
+
"showRateLimitEstimate": true,
|
|
304
|
+
"showCacheTokens": true
|
|
305
|
+
},
|
|
306
|
+
"rateLimitWindow": {
|
|
307
|
+
"sonnetTokenCap": 88000
|
|
246
308
|
}
|
|
247
309
|
}
|
|
248
310
|
```
|
package/src/prompts/planning.ts
CHANGED
|
@@ -80,9 +80,12 @@ After interviewing the user about all tasks, create plan files in the plans fold
|
|
|
80
80
|
- ${projectPath}/plans/02-task-name.md
|
|
81
81
|
- etc.
|
|
82
82
|
|
|
83
|
-
Each plan file
|
|
83
|
+
Each plan file MUST have Obsidian-style frontmatter at the top, before the \`# Task:\` heading. The frontmatter uses standard YAML format with opening and closing \`---\` delimiters:
|
|
84
84
|
|
|
85
85
|
\`\`\`markdown
|
|
86
|
+
---
|
|
87
|
+
effort: medium
|
|
88
|
+
---
|
|
86
89
|
# Task: [Task Name]
|
|
87
90
|
|
|
88
91
|
## Objective
|
|
@@ -117,6 +120,24 @@ Each plan file should follow this structure:
|
|
|
117
120
|
[Any additional context, warnings, or considerations]
|
|
118
121
|
\`\`\`
|
|
119
122
|
|
|
123
|
+
### Frontmatter Requirements
|
|
124
|
+
|
|
125
|
+
The \`effort\` field is REQUIRED in every plan file. It indicates task complexity and determines which Claude model will execute the task:
|
|
126
|
+
- \`effort: low\` — Trivial/mechanical changes, simple one-file edits, config changes
|
|
127
|
+
- \`effort: medium\` — Well-scoped feature work, bug fixes with clear plans, multi-file changes following existing patterns
|
|
128
|
+
- \`effort: high\` — Architectural changes, complex logic, tasks requiring deep codebase understanding
|
|
129
|
+
|
|
130
|
+
Optionally, you can add an explicit \`model\` field to override the effort-based model selection:
|
|
131
|
+
\`\`\`markdown
|
|
132
|
+
---
|
|
133
|
+
effort: medium
|
|
134
|
+
model: opus
|
|
135
|
+
---
|
|
136
|
+
# Task: ...
|
|
137
|
+
\`\`\`
|
|
138
|
+
|
|
139
|
+
This is rarely needed — prefer using the \`effort\` label so the user's config controls the actual model used.
|
|
140
|
+
|
|
120
141
|
### Step 4: Infer Task Dependencies
|
|
121
142
|
|
|
122
143
|
For each task, analyze which other tasks must complete successfully before it can begin. Add a \`## Dependencies\` section to plan files that have prerequisites.
|
|
@@ -166,19 +187,15 @@ Planning complete! To exit this session and run your tasks:
|
|
|
166
187
|
6. Only add Dependencies section when a task genuinely requires another to complete first
|
|
167
188
|
7. Dependencies must only reference lower-numbered tasks to prevent circular dependencies
|
|
168
189
|
8. Be specific - vague plans lead to poor execution
|
|
190
|
+
9. ALWAYS include the \`effort\` frontmatter field in every plan file — assess each task's complexity
|
|
169
191
|
|
|
170
192
|
## Plan Output Style
|
|
171
193
|
|
|
172
|
-
|
|
173
|
-
-
|
|
174
|
-
-
|
|
175
|
-
-
|
|
176
|
-
-
|
|
177
|
-
- Existing project files to modify
|
|
178
|
-
- Previous plan/outcome files for context
|
|
179
|
-
- Project structure and directories
|
|
180
|
-
- Let the executing agent decide implementation specifics
|
|
181
|
-
- Plans guide the work; they don't prescribe exact code`;
|
|
194
|
+
Plans can include whatever level of detail you deem helpful for the executing agent. Use your judgment:
|
|
195
|
+
- Include implementation details when they clarify the approach
|
|
196
|
+
- Code snippets are acceptable when they help illustrate a specific pattern
|
|
197
|
+
- File paths are helpful when referencing existing project files, patterns, or directories
|
|
198
|
+
- Focus on clarity — the goal is for the executing agent to understand what needs to be done`;
|
|
182
199
|
|
|
183
200
|
const userMessage = `Here is my project description:
|
|
184
201
|
|