rafcode 2.0.0 → 2.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/RAF/ahrren-turbo-finder/decisions.md +19 -0
- package/RAF/ahrren-turbo-finder/input.md +2 -0
- package/RAF/ahrren-turbo-finder/outcomes/01-worktree-auto-detect.md +40 -0
- package/RAF/ahrren-turbo-finder/outcomes/02-medium-effort-do.md +34 -0
- package/RAF/ahrren-turbo-finder/plans/01-worktree-auto-detect.md +44 -0
- package/RAF/ahrren-turbo-finder/plans/02-medium-effort-do.md +39 -0
- package/RAF/ahrtxf-session-sentinel/decisions.md +19 -0
- package/RAF/ahrtxf-session-sentinel/input.md +1 -0
- package/RAF/ahrtxf-session-sentinel/outcomes/01-capture-session-id.md +37 -0
- package/RAF/ahrtxf-session-sentinel/outcomes/02-resume-flag.md +45 -0
- package/RAF/ahrtxf-session-sentinel/plans/01-capture-session-id.md +41 -0
- package/RAF/ahrtxf-session-sentinel/plans/02-resume-flag.md +51 -0
- package/dist/commands/do.d.ts.map +1 -1
- package/dist/commands/do.js +61 -20
- package/dist/commands/do.js.map +1 -1
- package/dist/core/claude-runner.d.ts +19 -0
- package/dist/core/claude-runner.d.ts.map +1 -1
- package/dist/core/claude-runner.js +199 -29
- package/dist/core/claude-runner.js.map +1 -1
- package/dist/core/shutdown-handler.d.ts.map +1 -1
- package/dist/core/shutdown-handler.js +4 -0
- package/dist/core/shutdown-handler.js.map +1 -1
- package/dist/core/worktree.d.ts +18 -0
- package/dist/core/worktree.d.ts.map +1 -1
- package/dist/core/worktree.js +61 -0
- package/dist/core/worktree.js.map +1 -1
- package/dist/parsers/stream-renderer.d.ts +3 -0
- package/dist/parsers/stream-renderer.d.ts.map +1 -1
- package/dist/parsers/stream-renderer.js +1 -1
- package/dist/parsers/stream-renderer.js.map +1 -1
- package/dist/types/config.d.ts +1 -0
- package/dist/types/config.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/commands/do.ts +67 -21
- package/src/core/claude-runner.ts +244 -31
- package/src/core/shutdown-handler.ts +5 -0
- package/src/core/worktree.ts +77 -0
- package/src/parsers/stream-renderer.ts +4 -1
- package/src/types/config.ts +1 -0
- package/tests/unit/claude-runner-interactive.test.ts +24 -0
- package/tests/unit/claude-runner.test.ts +509 -55
- package/tests/unit/post-execution-picker.test.ts +1 -0
- package/tests/unit/stream-renderer.test.ts +30 -0
- package/tests/unit/worktree.test.ts +102 -0
|
@@ -14,6 +14,36 @@ describe('renderStreamEvent', () => {
|
|
|
14
14
|
expect(result.display).toBe('');
|
|
15
15
|
expect(result.textContent).toBe('');
|
|
16
16
|
});
|
|
17
|
+
|
|
18
|
+
it('should extract session_id from system init events', () => {
|
|
19
|
+
const line = JSON.stringify({
|
|
20
|
+
type: 'system',
|
|
21
|
+
subtype: 'init',
|
|
22
|
+
session_id: 'abc123-session-id',
|
|
23
|
+
tools: ['Read', 'Write', 'Bash'],
|
|
24
|
+
model: 'claude-opus-4-6',
|
|
25
|
+
});
|
|
26
|
+
const result = renderStreamEvent(line);
|
|
27
|
+
expect(result.sessionId).toBe('abc123-session-id');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should return undefined sessionId for system events without session_id', () => {
|
|
31
|
+
const line = JSON.stringify({
|
|
32
|
+
type: 'system',
|
|
33
|
+
subtype: 'other',
|
|
34
|
+
});
|
|
35
|
+
const result = renderStreamEvent(line);
|
|
36
|
+
expect(result.sessionId).toBeUndefined();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should not return sessionId for non-system events', () => {
|
|
40
|
+
const line = JSON.stringify({
|
|
41
|
+
type: 'assistant',
|
|
42
|
+
message: { content: [{ type: 'text', text: 'hello' }] },
|
|
43
|
+
});
|
|
44
|
+
const result = renderStreamEvent(line);
|
|
45
|
+
expect(result.sessionId).toBeUndefined();
|
|
46
|
+
});
|
|
17
47
|
});
|
|
18
48
|
|
|
19
49
|
describe('assistant events with text', () => {
|
|
@@ -42,6 +42,7 @@ const {
|
|
|
42
42
|
mergeWorktreeBranch,
|
|
43
43
|
removeWorktree,
|
|
44
44
|
listWorktreeProjects,
|
|
45
|
+
resolveWorktreeProjectByIdentifier,
|
|
45
46
|
} = await import('../../src/core/worktree.js');
|
|
46
47
|
|
|
47
48
|
const HOME = os.homedir();
|
|
@@ -520,4 +521,105 @@ describe('worktree utilities', () => {
|
|
|
520
521
|
expect(result).toEqual([]);
|
|
521
522
|
});
|
|
522
523
|
});
|
|
524
|
+
|
|
525
|
+
describe('resolveWorktreeProjectByIdentifier', () => {
|
|
526
|
+
const worktreeDirs = [
|
|
527
|
+
{ name: 'ahrren-turbo-finder', isDirectory: () => true },
|
|
528
|
+
{ name: 'abcdef-cool-feature', isDirectory: () => true },
|
|
529
|
+
{ name: 'ghijkl-another-thing', isDirectory: () => true },
|
|
530
|
+
];
|
|
531
|
+
|
|
532
|
+
beforeEach(() => {
|
|
533
|
+
mockExistsSync.mockReturnValue(true);
|
|
534
|
+
mockReaddirSync.mockReturnValue(worktreeDirs);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it('should resolve by full folder name (exact match)', () => {
|
|
538
|
+
const result = resolveWorktreeProjectByIdentifier('myapp', 'ahrren-turbo-finder');
|
|
539
|
+
|
|
540
|
+
expect(result).not.toBeNull();
|
|
541
|
+
expect(result!.folder).toBe('ahrren-turbo-finder');
|
|
542
|
+
expect(result!.worktreeRoot).toBe(
|
|
543
|
+
path.join(HOME, '.raf', 'worktrees', 'myapp', 'ahrren-turbo-finder')
|
|
544
|
+
);
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
it('should resolve by full folder name case-insensitively', () => {
|
|
548
|
+
const result = resolveWorktreeProjectByIdentifier('myapp', 'Ahrren-Turbo-Finder');
|
|
549
|
+
|
|
550
|
+
expect(result).not.toBeNull();
|
|
551
|
+
expect(result!.folder).toBe('ahrren-turbo-finder');
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
it('should resolve by base26 prefix (6-char ID)', () => {
|
|
555
|
+
const result = resolveWorktreeProjectByIdentifier('myapp', 'ahrren');
|
|
556
|
+
|
|
557
|
+
expect(result).not.toBeNull();
|
|
558
|
+
expect(result!.folder).toBe('ahrren-turbo-finder');
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
it('should resolve by base26 prefix for different project', () => {
|
|
562
|
+
const result = resolveWorktreeProjectByIdentifier('myapp', 'abcdef');
|
|
563
|
+
|
|
564
|
+
expect(result).not.toBeNull();
|
|
565
|
+
expect(result!.folder).toBe('abcdef-cool-feature');
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
it('should resolve by project name', () => {
|
|
569
|
+
const result = resolveWorktreeProjectByIdentifier('myapp', 'turbo-finder');
|
|
570
|
+
|
|
571
|
+
expect(result).not.toBeNull();
|
|
572
|
+
expect(result!.folder).toBe('ahrren-turbo-finder');
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
it('should resolve by project name case-insensitively', () => {
|
|
576
|
+
const result = resolveWorktreeProjectByIdentifier('myapp', 'Turbo-Finder');
|
|
577
|
+
|
|
578
|
+
expect(result).not.toBeNull();
|
|
579
|
+
expect(result!.folder).toBe('ahrren-turbo-finder');
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
it('should return null when no match found', () => {
|
|
583
|
+
const result = resolveWorktreeProjectByIdentifier('myapp', 'nonexistent');
|
|
584
|
+
|
|
585
|
+
expect(result).toBeNull();
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
it('should return null when no worktree projects exist', () => {
|
|
589
|
+
mockExistsSync.mockReturnValue(false);
|
|
590
|
+
|
|
591
|
+
const result = resolveWorktreeProjectByIdentifier('myapp', 'turbo-finder');
|
|
592
|
+
|
|
593
|
+
expect(result).toBeNull();
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
it('should return null for ambiguous name match (multiple projects with same name)', () => {
|
|
597
|
+
mockReaddirSync.mockReturnValue([
|
|
598
|
+
{ name: 'ahrren-my-feature', isDirectory: () => true },
|
|
599
|
+
{ name: 'abcdef-my-feature', isDirectory: () => true },
|
|
600
|
+
]);
|
|
601
|
+
|
|
602
|
+
const result = resolveWorktreeProjectByIdentifier('myapp', 'my-feature');
|
|
603
|
+
|
|
604
|
+
// Ambiguous: two projects named "my-feature"
|
|
605
|
+
expect(result).toBeNull();
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
it('should prefer full folder name match over name match', () => {
|
|
609
|
+
// "abcdef-cool-feature" could match as full folder name
|
|
610
|
+
const result = resolveWorktreeProjectByIdentifier('myapp', 'abcdef-cool-feature');
|
|
611
|
+
|
|
612
|
+
expect(result).not.toBeNull();
|
|
613
|
+
expect(result!.folder).toBe('abcdef-cool-feature');
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
it('should return correct worktreeRoot path', () => {
|
|
617
|
+
const result = resolveWorktreeProjectByIdentifier('myapp', 'another-thing');
|
|
618
|
+
|
|
619
|
+
expect(result).not.toBeNull();
|
|
620
|
+
expect(result!.worktreeRoot).toBe(
|
|
621
|
+
path.join(HOME, '.raf', 'worktrees', 'myapp', 'ghijkl-another-thing')
|
|
622
|
+
);
|
|
623
|
+
});
|
|
624
|
+
});
|
|
523
625
|
});
|