@claudetree/cli 0.4.2 → 0.4.3

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.
Files changed (59) hide show
  1. package/dist/commands/init.test.d.ts +2 -0
  2. package/dist/commands/init.test.d.ts.map +1 -0
  3. package/dist/commands/init.test.js +94 -0
  4. package/dist/commands/init.test.js.map +1 -0
  5. package/dist/commands/list.test.d.ts +2 -0
  6. package/dist/commands/list.test.d.ts.map +1 -0
  7. package/dist/commands/list.test.js +106 -0
  8. package/dist/commands/list.test.js.map +1 -0
  9. package/dist/commands/start/buildPrompt.d.ts +29 -0
  10. package/dist/commands/start/buildPrompt.d.ts.map +1 -0
  11. package/dist/commands/start/buildPrompt.js +142 -0
  12. package/dist/commands/start/buildPrompt.js.map +1 -0
  13. package/dist/commands/start/buildPrompt.test.d.ts +2 -0
  14. package/dist/commands/start/buildPrompt.test.d.ts.map +1 -0
  15. package/dist/commands/start/buildPrompt.test.js +182 -0
  16. package/dist/commands/start/buildPrompt.test.js.map +1 -0
  17. package/dist/commands/start/createWorktree.d.ts +20 -0
  18. package/dist/commands/start/createWorktree.d.ts.map +1 -0
  19. package/dist/commands/start/createWorktree.js +35 -0
  20. package/dist/commands/start/createWorktree.js.map +1 -0
  21. package/dist/commands/start/createWorktree.test.d.ts +2 -0
  22. package/dist/commands/start/createWorktree.test.d.ts.map +1 -0
  23. package/dist/commands/start/createWorktree.test.js +81 -0
  24. package/dist/commands/start/createWorktree.test.js.map +1 -0
  25. package/dist/commands/start/index.d.ts +4 -0
  26. package/dist/commands/start/index.d.ts.map +1 -0
  27. package/dist/commands/start/index.js +4 -0
  28. package/dist/commands/start/index.js.map +1 -0
  29. package/dist/commands/start/parseIssueInput.d.ts +21 -0
  30. package/dist/commands/start/parseIssueInput.d.ts.map +1 -0
  31. package/dist/commands/start/parseIssueInput.js +78 -0
  32. package/dist/commands/start/parseIssueInput.js.map +1 -0
  33. package/dist/commands/start/parseIssueInput.test.d.ts +2 -0
  34. package/dist/commands/start/parseIssueInput.test.d.ts.map +1 -0
  35. package/dist/commands/start/parseIssueInput.test.js +118 -0
  36. package/dist/commands/start/parseIssueInput.test.js.map +1 -0
  37. package/dist/commands/start.d.ts.map +1 -1
  38. package/dist/commands/start.js +141 -280
  39. package/dist/commands/start.js.map +1 -1
  40. package/dist/commands/start.test.d.ts +2 -0
  41. package/dist/commands/start.test.d.ts.map +1 -0
  42. package/dist/commands/start.test.js +260 -0
  43. package/dist/commands/start.test.js.map +1 -0
  44. package/dist/commands/status.test.d.ts +2 -0
  45. package/dist/commands/status.test.d.ts.map +1 -0
  46. package/dist/commands/status.test.js +172 -0
  47. package/dist/commands/status.test.js.map +1 -0
  48. package/dist/commands/stop.test.d.ts +2 -0
  49. package/dist/commands/stop.test.d.ts.map +1 -0
  50. package/dist/commands/stop.test.js +152 -0
  51. package/dist/commands/stop.test.js.map +1 -0
  52. package/dist/commands/web.d.ts.map +1 -1
  53. package/dist/commands/web.js +95 -1
  54. package/dist/commands/web.js.map +1 -1
  55. package/dist/commands/web.test.d.ts +2 -0
  56. package/dist/commands/web.test.d.ts.map +1 -0
  57. package/dist/commands/web.test.js +133 -0
  58. package/dist/commands/web.test.js.map +1 -0
  59. package/package.json +8 -5
@@ -1,7 +1,8 @@
1
1
  import { Command } from 'commander';
2
2
  import { join } from 'node:path';
3
- import { access } from 'node:fs/promises';
3
+ import { access, readFile } from 'node:fs/promises';
4
4
  import { spawn } from 'node:child_process';
5
+ import { watch } from 'node:fs';
5
6
  import { WebSocketBroadcaster } from '@claudetree/core';
6
7
  const CONFIG_DIR = '.claudetree';
7
8
  export const webCommand = new Command('web')
@@ -24,6 +25,91 @@ export const webCommand = new Command('web')
24
25
  // Start WebSocket server
25
26
  const wss = new WebSocketBroadcaster(wsPort);
26
27
  console.log(` WebSocket: ws://localhost:${wsPort}`);
28
+ // Watch sessions.json for changes and broadcast updates
29
+ const sessionsPath = join(configDir, 'sessions.json');
30
+ const eventsDir = join(configDir, 'events');
31
+ let lastSessionsContent = '';
32
+ let sessionFileWatcher = null;
33
+ let eventsWatcher = null;
34
+ let debounceTimer = null;
35
+ let eventsDebounceTimer = null;
36
+ // Track last event count per session for incremental updates
37
+ const lastEventCounts = new Map();
38
+ const broadcastSessionUpdate = async () => {
39
+ try {
40
+ const content = await readFile(sessionsPath, 'utf-8');
41
+ if (content !== lastSessionsContent) {
42
+ lastSessionsContent = content;
43
+ const sessions = JSON.parse(content);
44
+ wss.broadcast({
45
+ type: 'session:updated',
46
+ payload: { sessions },
47
+ timestamp: new Date(),
48
+ });
49
+ }
50
+ }
51
+ catch {
52
+ // File might not exist yet or be temporarily unavailable
53
+ }
54
+ };
55
+ // Broadcast new events only (incremental)
56
+ const broadcastNewEvents = async (sessionId) => {
57
+ try {
58
+ const eventPath = join(eventsDir, `${sessionId}.json`);
59
+ const content = await readFile(eventPath, 'utf-8');
60
+ const events = JSON.parse(content);
61
+ const lastCount = lastEventCounts.get(sessionId) || 0;
62
+ if (events.length > lastCount) {
63
+ const newEvents = events.slice(lastCount);
64
+ lastEventCounts.set(sessionId, events.length);
65
+ // Broadcast each new event for live streaming effect
66
+ for (const event of newEvents) {
67
+ wss.broadcast({
68
+ type: 'event:created',
69
+ payload: { sessionId, event },
70
+ timestamp: new Date(),
71
+ });
72
+ }
73
+ }
74
+ }
75
+ catch {
76
+ // Events file might not exist yet
77
+ }
78
+ };
79
+ // Initial read
80
+ await broadcastSessionUpdate();
81
+ // Watch sessions.json for changes
82
+ try {
83
+ sessionFileWatcher = watch(sessionsPath, { persistent: true }, () => {
84
+ if (debounceTimer)
85
+ clearTimeout(debounceTimer);
86
+ debounceTimer = setTimeout(broadcastSessionUpdate, 100);
87
+ });
88
+ }
89
+ catch {
90
+ sessionFileWatcher = watch(configDir, { persistent: true }, (_, filename) => {
91
+ if (filename === 'sessions.json') {
92
+ if (debounceTimer)
93
+ clearTimeout(debounceTimer);
94
+ debounceTimer = setTimeout(broadcastSessionUpdate, 100);
95
+ }
96
+ });
97
+ }
98
+ // Watch events directory for live streaming
99
+ try {
100
+ await access(eventsDir);
101
+ eventsWatcher = watch(eventsDir, { persistent: true }, (_, filename) => {
102
+ if (filename?.endsWith('.json')) {
103
+ const sessionId = filename.replace('.json', '');
104
+ if (eventsDebounceTimer)
105
+ clearTimeout(eventsDebounceTimer);
106
+ eventsDebounceTimer = setTimeout(() => broadcastNewEvents(sessionId), 50);
107
+ }
108
+ });
109
+ }
110
+ catch {
111
+ // Events directory might not exist yet - will be created when first session starts
112
+ }
27
113
  // Start Next.js dev server
28
114
  const webPath = join(cwd, 'node_modules', '@claudetree', 'web');
29
115
  // For development, use the packages/web directly
@@ -50,6 +136,14 @@ export const webCommand = new Command('web')
50
136
  // Handle cleanup
51
137
  const cleanup = () => {
52
138
  console.log('\nShutting down...');
139
+ if (debounceTimer)
140
+ clearTimeout(debounceTimer);
141
+ if (eventsDebounceTimer)
142
+ clearTimeout(eventsDebounceTimer);
143
+ if (sessionFileWatcher)
144
+ sessionFileWatcher.close();
145
+ if (eventsWatcher)
146
+ eventsWatcher.close();
53
147
  wss.close();
54
148
  nextProcess.kill();
55
149
  process.exit(0);
@@ -1 +1 @@
1
- {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/commands/web.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/commands/web.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=web.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.test.d.ts","sourceRoot":"","sources":["../../src/commands/web.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,133 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { mkdtemp, rm, mkdir } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ // Mock WebSocketBroadcaster
6
+ vi.mock('@claudetree/core', () => ({
7
+ WebSocketBroadcaster: vi.fn().mockImplementation(() => ({
8
+ close: vi.fn(),
9
+ })),
10
+ }));
11
+ // Mock child_process spawn
12
+ vi.mock('node:child_process', () => ({
13
+ spawn: vi.fn().mockReturnValue({
14
+ on: vi.fn(),
15
+ kill: vi.fn(),
16
+ }),
17
+ }));
18
+ // Import after mock
19
+ import { webCommand } from './web.js';
20
+ import { spawn } from 'node:child_process';
21
+ import { WebSocketBroadcaster } from '@claudetree/core';
22
+ describe('webCommand', () => {
23
+ let testDir;
24
+ let originalCwd;
25
+ let originalExit;
26
+ let consoleLogSpy;
27
+ let consoleErrorSpy;
28
+ beforeEach(async () => {
29
+ testDir = await mkdtemp(join(tmpdir(), 'claudetree-web-test-'));
30
+ originalCwd = process.cwd();
31
+ process.chdir(testDir);
32
+ originalExit = process.exit;
33
+ process.exit = vi.fn();
34
+ consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
35
+ consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
36
+ vi.clearAllMocks();
37
+ // Default spawn mock that returns an object with on and kill methods
38
+ vi.mocked(spawn).mockReturnValue({
39
+ on: vi.fn(),
40
+ kill: vi.fn(),
41
+ });
42
+ });
43
+ afterEach(async () => {
44
+ process.chdir(originalCwd);
45
+ process.exit = originalExit;
46
+ consoleLogSpy.mockRestore();
47
+ consoleErrorSpy.mockRestore();
48
+ vi.clearAllMocks();
49
+ await rm(testDir, { recursive: true, force: true });
50
+ });
51
+ describe('when not initialized', () => {
52
+ it('should display error and exit with code 1', async () => {
53
+ const exitError = new Error('process.exit called');
54
+ process.exit.mockImplementation(() => {
55
+ throw exitError;
56
+ });
57
+ await expect(webCommand.parseAsync(['node', 'test'])).rejects.toThrow('process.exit called');
58
+ expect(consoleErrorSpy).toHaveBeenCalledWith('Error: claudetree not initialized. Run "claudetree init" first.');
59
+ expect(process.exit).toHaveBeenCalledWith(1);
60
+ });
61
+ });
62
+ describe('when initialized', () => {
63
+ beforeEach(async () => {
64
+ await mkdir(join(testDir, '.claudetree'), { recursive: true });
65
+ });
66
+ it('should display startup message with default ports', async () => {
67
+ await webCommand.parseAsync(['node', 'test']);
68
+ expect(consoleLogSpy).toHaveBeenCalledWith('Starting claudetree dashboard...\n');
69
+ expect(consoleLogSpy).toHaveBeenCalledWith(' WebSocket: ws://localhost:3001');
70
+ expect(consoleLogSpy).toHaveBeenCalledWith(' Web UI: http://localhost:3000');
71
+ });
72
+ it('should use custom port when --port option is provided', async () => {
73
+ await webCommand.parseAsync(['node', 'test', '--port', '4000']);
74
+ expect(consoleLogSpy).toHaveBeenCalledWith(' Web UI: http://localhost:4000');
75
+ });
76
+ it('should use custom WebSocket port when --ws-port option is provided', async () => {
77
+ await webCommand.parseAsync(['node', 'test', '--ws-port', '4001']);
78
+ expect(consoleLogSpy).toHaveBeenCalledWith(' WebSocket: ws://localhost:4001');
79
+ });
80
+ it('should spawn Next.js dev server with correct arguments', async () => {
81
+ await webCommand.parseAsync(['node', 'test', '--port', '5000']);
82
+ expect(spawn).toHaveBeenCalledWith('npx', ['next', 'dev', '-p', '5000'], expect.objectContaining({
83
+ stdio: 'inherit',
84
+ }));
85
+ // Verify env contains the required values
86
+ const spawnCall = vi.mocked(spawn).mock.calls[0];
87
+ const options = spawnCall?.[2];
88
+ expect(options?.env?.CLAUDETREE_WS_PORT).toBe('3001');
89
+ expect(options?.env?.CLAUDETREE_ROOT).toContain('claudetree-web-test');
90
+ });
91
+ it('should set CLAUDETREE_WS_PORT environment variable', async () => {
92
+ await webCommand.parseAsync([
93
+ 'node',
94
+ 'test',
95
+ '--port',
96
+ '3000',
97
+ '--ws-port',
98
+ '3002',
99
+ ]);
100
+ const spawnCall = vi.mocked(spawn).mock.calls[0];
101
+ const options = spawnCall?.[2];
102
+ expect(options?.env?.CLAUDETREE_WS_PORT).toBe('3002');
103
+ });
104
+ it('should display instructions for stopping', async () => {
105
+ await webCommand.parseAsync(['node', 'test']);
106
+ expect(consoleLogSpy).toHaveBeenCalledWith('\nPress Ctrl+C to stop.\n');
107
+ });
108
+ describe('error handling', () => {
109
+ it('should handle spawn error and close WebSocket', async () => {
110
+ const mockOn = vi.fn();
111
+ vi.mocked(spawn).mockReturnValue({
112
+ on: mockOn,
113
+ kill: vi.fn(),
114
+ });
115
+ await webCommand.parseAsync(['node', 'test']);
116
+ // Find the error handler
117
+ const errorHandler = mockOn.mock.calls.find((call) => call[0] === 'error')?.[1];
118
+ // Setup process.exit to throw
119
+ const exitError = new Error('process.exit called');
120
+ process.exit.mockImplementation(() => {
121
+ throw exitError;
122
+ });
123
+ if (errorHandler) {
124
+ expect(() => errorHandler(new Error('spawn error'))).toThrow('process.exit called');
125
+ expect(consoleErrorSpy).toHaveBeenCalledWith('Failed to start web server:', 'spawn error');
126
+ // Verify WebSocketBroadcaster was instantiated and close would be called
127
+ expect(WebSocketBroadcaster).toHaveBeenCalled();
128
+ }
129
+ });
130
+ });
131
+ });
132
+ });
133
+ //# sourceMappingURL=web.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.test.js","sourceRoot":"","sources":["../../src/commands/web.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claudetree/cli",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "CLI for claudetree - Manage Claude sessions with git worktrees",
5
5
  "type": "module",
6
6
  "bin": {
@@ -29,16 +29,19 @@
29
29
  "dependencies": {
30
30
  "async-mutex": "^0.5.0",
31
31
  "commander": "^13.0.0",
32
- "@claudetree/core": "0.4.2",
33
- "@claudetree/shared": "0.4.2"
32
+ "@claudetree/core": "0.4.3",
33
+ "@claudetree/shared": "0.4.3"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^22.10.0",
37
- "typescript": "^5.7.0"
37
+ "typescript": "^5.7.0",
38
+ "vitest": "^3.0.0"
38
39
  },
39
40
  "scripts": {
40
41
  "build": "tsc",
41
42
  "dev": "tsc --watch",
42
- "start": "node dist/index.js"
43
+ "start": "node dist/index.js",
44
+ "test": "vitest",
45
+ "test:run": "vitest run"
43
46
  }
44
47
  }