rafcode 2.2.0 → 2.4.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.md +19 -4
- 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/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/README.md +27 -7
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +24 -7
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/do.d.ts.map +1 -1
- package/dist/commands/do.js +122 -27
- package/dist/commands/do.js.map +1 -1
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +79 -3
- package/dist/commands/plan.js.map +1 -1
- package/dist/core/claude-runner.d.ts +6 -6
- package/dist/core/claude-runner.d.ts.map +1 -1
- package/dist/core/claude-runner.js +9 -10
- 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.d.ts.map +1 -1
- package/dist/core/pull-request.js +5 -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 +32 -0
- package/dist/core/worktree.d.ts.map +1 -1
- package/dist/core/worktree.js +215 -0
- package/dist/core/worktree.js.map +1 -1
- package/dist/prompts/amend.d.ts.map +1 -1
- package/dist/prompts/amend.js +26 -11
- package/dist/prompts/amend.js.map +1 -1
- package/dist/prompts/planning.d.ts.map +1 -1
- package/dist/prompts/planning.js +26 -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 +53 -4
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +197 -30
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/frontmatter.d.ts +43 -0
- package/dist/utils/frontmatter.d.ts.map +1 -0
- package/dist/utils/frontmatter.js +85 -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 +2 -3
- 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 +28 -5
- package/dist/utils/terminal-symbols.d.ts.map +1 -1
- package/dist/utils/terminal-symbols.js +77 -18
- package/dist/utils/terminal-symbols.js.map +1 -1
- package/dist/utils/token-tracker.d.ts +31 -1
- package/dist/utils/token-tracker.d.ts.map +1 -1
- package/dist/utils/token-tracker.js +94 -4
- package/dist/utils/token-tracker.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/config.ts +26 -7
- package/src/commands/do.ts +157 -29
- package/src/commands/plan.ts +89 -2
- package/src/core/claude-runner.ts +16 -17
- package/src/core/failure-analyzer.ts +3 -3
- package/src/core/pull-request.ts +5 -3
- package/src/core/state-derivation.ts +20 -4
- package/src/core/worktree.ts +230 -0
- package/src/prompts/amend.ts +26 -11
- package/src/prompts/config-docs.md +91 -29
- package/src/prompts/planning.ts +26 -11
- package/src/types/config.ts +46 -21
- package/src/utils/config.ts +222 -33
- package/src/utils/frontmatter.ts +110 -0
- package/src/utils/name-generator.ts +2 -3
- package/src/utils/session-parser.ts +161 -0
- package/src/utils/terminal-symbols.ts +105 -18
- package/src/utils/token-tracker.ts +109 -4
- package/tests/unit/claude-runner-interactive.test.ts +8 -6
- package/tests/unit/claude-runner.test.ts +5 -66
- package/tests/unit/config-command.test.ts +84 -5
- package/tests/unit/config.test.ts +292 -45
- package/tests/unit/frontmatter.test.ts +182 -0
- package/tests/unit/post-execution-picker.test.ts +5 -0
- package/tests/unit/session-parser.test.ts +301 -0
- package/tests/unit/terminal-symbols.test.ts +263 -33
- package/tests/unit/timer-verbose-integration.test.ts +170 -0
- package/tests/unit/token-tracker.test.ts +653 -17
- package/tests/unit/validation.test.ts +6 -4
- package/tests/unit/worktree.test.ts +242 -0
|
@@ -76,10 +76,12 @@ describe('Validation', () => {
|
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
describe('resolveModelOption', () => {
|
|
79
|
-
it('should return
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
expect(
|
|
79
|
+
it('should return a valid model as default', () => {
|
|
80
|
+
// Default comes from config, could be short alias or full model ID
|
|
81
|
+
const result = resolveModelOption();
|
|
82
|
+
expect(result).toMatch(/^(opus|sonnet|haiku|claude-(opus|sonnet|haiku)-.+)$/);
|
|
83
|
+
expect(resolveModelOption(undefined, undefined)).toBe(result);
|
|
84
|
+
expect(resolveModelOption(undefined, false)).toBe(result);
|
|
83
85
|
});
|
|
84
86
|
|
|
85
87
|
it('should use --model flag when provided', () => {
|
|
@@ -43,6 +43,9 @@ const {
|
|
|
43
43
|
removeWorktree,
|
|
44
44
|
listWorktreeProjects,
|
|
45
45
|
resolveWorktreeProjectByIdentifier,
|
|
46
|
+
detectMainBranch,
|
|
47
|
+
pullMainBranch,
|
|
48
|
+
pushMainBranch,
|
|
46
49
|
} = await import('../../src/core/worktree.js');
|
|
47
50
|
|
|
48
51
|
const HOME = os.homedir();
|
|
@@ -622,4 +625,243 @@ describe('worktree utilities', () => {
|
|
|
622
625
|
);
|
|
623
626
|
});
|
|
624
627
|
});
|
|
628
|
+
|
|
629
|
+
describe('detectMainBranch', () => {
|
|
630
|
+
it('should detect main branch from origin/HEAD', () => {
|
|
631
|
+
mockExecSync.mockReturnValue('refs/remotes/origin/main\n');
|
|
632
|
+
expect(detectMainBranch()).toBe('main');
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it('should detect master from origin/HEAD', () => {
|
|
636
|
+
mockExecSync.mockReturnValue('refs/remotes/origin/master\n');
|
|
637
|
+
expect(detectMainBranch()).toBe('master');
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
it('should fall back to main when origin/HEAD not set', () => {
|
|
641
|
+
let callCount = 0;
|
|
642
|
+
mockExecSync.mockImplementation((cmd: unknown) => {
|
|
643
|
+
callCount++;
|
|
644
|
+
const cmdStr = cmd as string;
|
|
645
|
+
if (cmdStr.includes('symbolic-ref')) {
|
|
646
|
+
throw new Error('ref refs/remotes/origin/HEAD is not a symbolic ref');
|
|
647
|
+
}
|
|
648
|
+
if (cmdStr.includes('refs/heads/main') && callCount === 2) {
|
|
649
|
+
return 'valid\n';
|
|
650
|
+
}
|
|
651
|
+
throw new Error('fatal');
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
expect(detectMainBranch()).toBe('main');
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
it('should fall back to master when main does not exist', () => {
|
|
658
|
+
let callCount = 0;
|
|
659
|
+
mockExecSync.mockImplementation((cmd: unknown) => {
|
|
660
|
+
callCount++;
|
|
661
|
+
const cmdStr = cmd as string;
|
|
662
|
+
if (cmdStr.includes('symbolic-ref')) {
|
|
663
|
+
throw new Error('ref refs/remotes/origin/HEAD is not a symbolic ref');
|
|
664
|
+
}
|
|
665
|
+
if (cmdStr.includes('refs/heads/main')) {
|
|
666
|
+
throw new Error('fatal: Needed a single revision');
|
|
667
|
+
}
|
|
668
|
+
if (cmdStr.includes('refs/heads/master') && callCount === 3) {
|
|
669
|
+
return 'valid\n';
|
|
670
|
+
}
|
|
671
|
+
throw new Error('fatal');
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
expect(detectMainBranch()).toBe('master');
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
it('should return null when no main branch found', () => {
|
|
678
|
+
mockExecSync.mockImplementation(() => {
|
|
679
|
+
throw new Error('not found');
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
expect(detectMainBranch()).toBeNull();
|
|
683
|
+
});
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
describe('pullMainBranch', () => {
|
|
687
|
+
it('should return error when main branch cannot be detected', () => {
|
|
688
|
+
mockExecSync.mockImplementation(() => {
|
|
689
|
+
throw new Error('not found');
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
const result = pullMainBranch();
|
|
693
|
+
|
|
694
|
+
expect(result.success).toBe(false);
|
|
695
|
+
expect(result.mainBranch).toBeNull();
|
|
696
|
+
expect(result.error).toContain('Could not detect main branch');
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
it('should fetch main when not on main branch', () => {
|
|
700
|
+
let commands: string[] = [];
|
|
701
|
+
mockExecSync.mockImplementation((cmd: unknown) => {
|
|
702
|
+
const cmdStr = cmd as string;
|
|
703
|
+
commands.push(cmdStr);
|
|
704
|
+
if (cmdStr.includes('symbolic-ref')) return 'refs/remotes/origin/main\n';
|
|
705
|
+
if (cmdStr.includes('branch --show-current')) return 'feature-branch\n';
|
|
706
|
+
if (cmdStr.includes('fetch origin main:main')) return '';
|
|
707
|
+
return '';
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
const result = pullMainBranch();
|
|
711
|
+
|
|
712
|
+
expect(result.success).toBe(true);
|
|
713
|
+
expect(result.mainBranch).toBe('main');
|
|
714
|
+
expect(result.hadChanges).toBe(true);
|
|
715
|
+
expect(commands).toContain('git fetch origin main:main');
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
it('should warn when local main has diverged', () => {
|
|
719
|
+
let commands: string[] = [];
|
|
720
|
+
mockExecSync.mockImplementation((cmd: unknown) => {
|
|
721
|
+
const cmdStr = cmd as string;
|
|
722
|
+
commands.push(cmdStr);
|
|
723
|
+
if (cmdStr.includes('symbolic-ref')) return 'refs/remotes/origin/main\n';
|
|
724
|
+
if (cmdStr.includes('branch --show-current')) return 'feature-branch\n';
|
|
725
|
+
if (cmdStr.includes('fetch origin main:main')) throw new Error('not fast-forward');
|
|
726
|
+
if (cmdStr.includes('fetch origin main')) return '';
|
|
727
|
+
return '';
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
const result = pullMainBranch();
|
|
731
|
+
|
|
732
|
+
expect(result.success).toBe(true);
|
|
733
|
+
expect(result.mainBranch).toBe('main');
|
|
734
|
+
expect(result.hadChanges).toBe(false);
|
|
735
|
+
expect(result.error).toContain('diverged');
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
it('should fail when on main but has uncommitted changes', () => {
|
|
739
|
+
mockExecSync.mockImplementation((cmd: unknown) => {
|
|
740
|
+
const cmdStr = cmd as string;
|
|
741
|
+
if (cmdStr.includes('symbolic-ref')) return 'refs/remotes/origin/main\n';
|
|
742
|
+
if (cmdStr.includes('branch --show-current')) return 'main\n';
|
|
743
|
+
if (cmdStr.includes('status --porcelain')) return ' M file.ts\n';
|
|
744
|
+
return '';
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
const result = pullMainBranch();
|
|
748
|
+
|
|
749
|
+
expect(result.success).toBe(false);
|
|
750
|
+
expect(result.mainBranch).toBe('main');
|
|
751
|
+
expect(result.error).toContain('uncommitted changes');
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
it('should pull successfully when on main with no changes', () => {
|
|
755
|
+
mockExecSync.mockImplementation((cmd: unknown) => {
|
|
756
|
+
const cmdStr = cmd as string;
|
|
757
|
+
if (cmdStr.includes('symbolic-ref')) return 'refs/remotes/origin/main\n';
|
|
758
|
+
if (cmdStr.includes('branch --show-current')) return 'main\n';
|
|
759
|
+
if (cmdStr.includes('status --porcelain')) return '';
|
|
760
|
+
if (cmdStr.includes('fetch origin main')) return '';
|
|
761
|
+
if (cmdStr.includes('merge --ff-only')) return 'Updating abc123..def456\n';
|
|
762
|
+
return '';
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
const result = pullMainBranch();
|
|
766
|
+
|
|
767
|
+
expect(result.success).toBe(true);
|
|
768
|
+
expect(result.mainBranch).toBe('main');
|
|
769
|
+
expect(result.hadChanges).toBe(true);
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
it('should report no changes when already up to date', () => {
|
|
773
|
+
mockExecSync.mockImplementation((cmd: unknown) => {
|
|
774
|
+
const cmdStr = cmd as string;
|
|
775
|
+
if (cmdStr.includes('symbolic-ref')) return 'refs/remotes/origin/main\n';
|
|
776
|
+
if (cmdStr.includes('branch --show-current')) return 'main\n';
|
|
777
|
+
if (cmdStr.includes('status --porcelain')) return '';
|
|
778
|
+
if (cmdStr.includes('fetch origin main')) return '';
|
|
779
|
+
if (cmdStr.includes('merge --ff-only')) return 'Already up to date.\n';
|
|
780
|
+
return '';
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
const result = pullMainBranch();
|
|
784
|
+
|
|
785
|
+
expect(result.success).toBe(true);
|
|
786
|
+
expect(result.mainBranch).toBe('main');
|
|
787
|
+
expect(result.hadChanges).toBe(false);
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
it('should fail when branch has diverged', () => {
|
|
791
|
+
mockExecSync.mockImplementation((cmd: unknown) => {
|
|
792
|
+
const cmdStr = cmd as string;
|
|
793
|
+
if (cmdStr.includes('symbolic-ref')) return 'refs/remotes/origin/main\n';
|
|
794
|
+
if (cmdStr.includes('branch --show-current')) return 'main\n';
|
|
795
|
+
if (cmdStr.includes('status --porcelain')) return '';
|
|
796
|
+
if (cmdStr.includes('fetch origin main')) return '';
|
|
797
|
+
if (cmdStr.includes('merge --ff-only')) throw new Error('Not possible to fast-forward');
|
|
798
|
+
return '';
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
const result = pullMainBranch();
|
|
802
|
+
|
|
803
|
+
expect(result.success).toBe(false);
|
|
804
|
+
expect(result.mainBranch).toBe('main');
|
|
805
|
+
expect(result.error).toContain('diverged from origin');
|
|
806
|
+
});
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
describe('pushMainBranch', () => {
|
|
810
|
+
it('should return error when main branch cannot be detected', () => {
|
|
811
|
+
mockExecSync.mockImplementation(() => {
|
|
812
|
+
throw new Error('not found');
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
const result = pushMainBranch();
|
|
816
|
+
|
|
817
|
+
expect(result.success).toBe(false);
|
|
818
|
+
expect(result.mainBranch).toBeNull();
|
|
819
|
+
expect(result.error).toContain('Could not detect main branch');
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
it('should push main successfully', () => {
|
|
823
|
+
mockExecSync.mockImplementation((cmd: unknown) => {
|
|
824
|
+
const cmdStr = cmd as string;
|
|
825
|
+
if (cmdStr.includes('symbolic-ref')) return 'refs/remotes/origin/main\n';
|
|
826
|
+
if (cmdStr.includes('push origin main')) return '';
|
|
827
|
+
return '';
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
const result = pushMainBranch();
|
|
831
|
+
|
|
832
|
+
expect(result.success).toBe(true);
|
|
833
|
+
expect(result.mainBranch).toBe('main');
|
|
834
|
+
expect(result.hadChanges).toBe(true);
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
it('should report no changes when already up to date', () => {
|
|
838
|
+
mockExecSync.mockImplementation((cmd: unknown) => {
|
|
839
|
+
const cmdStr = cmd as string;
|
|
840
|
+
if (cmdStr.includes('symbolic-ref')) return 'refs/remotes/origin/main\n';
|
|
841
|
+
if (cmdStr.includes('push origin main')) throw new Error('Everything up-to-date');
|
|
842
|
+
return '';
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
const result = pushMainBranch();
|
|
846
|
+
|
|
847
|
+
expect(result.success).toBe(true);
|
|
848
|
+
expect(result.mainBranch).toBe('main');
|
|
849
|
+
expect(result.hadChanges).toBe(false);
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
it('should fail when push is rejected', () => {
|
|
853
|
+
mockExecSync.mockImplementation((cmd: unknown) => {
|
|
854
|
+
const cmdStr = cmd as string;
|
|
855
|
+
if (cmdStr.includes('symbolic-ref')) return 'refs/remotes/origin/main\n';
|
|
856
|
+
if (cmdStr.includes('push origin main')) throw new Error('rejected - non-fast-forward');
|
|
857
|
+
return '';
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
const result = pushMainBranch();
|
|
861
|
+
|
|
862
|
+
expect(result.success).toBe(false);
|
|
863
|
+
expect(result.mainBranch).toBe('main');
|
|
864
|
+
expect(result.error).toContain('Failed to push main');
|
|
865
|
+
});
|
|
866
|
+
});
|
|
625
867
|
});
|