@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.
- package/dist/commands/init.test.d.ts +2 -0
- package/dist/commands/init.test.d.ts.map +1 -0
- package/dist/commands/init.test.js +94 -0
- package/dist/commands/init.test.js.map +1 -0
- package/dist/commands/list.test.d.ts +2 -0
- package/dist/commands/list.test.d.ts.map +1 -0
- package/dist/commands/list.test.js +106 -0
- package/dist/commands/list.test.js.map +1 -0
- package/dist/commands/start/buildPrompt.d.ts +29 -0
- package/dist/commands/start/buildPrompt.d.ts.map +1 -0
- package/dist/commands/start/buildPrompt.js +142 -0
- package/dist/commands/start/buildPrompt.js.map +1 -0
- package/dist/commands/start/buildPrompt.test.d.ts +2 -0
- package/dist/commands/start/buildPrompt.test.d.ts.map +1 -0
- package/dist/commands/start/buildPrompt.test.js +182 -0
- package/dist/commands/start/buildPrompt.test.js.map +1 -0
- package/dist/commands/start/createWorktree.d.ts +20 -0
- package/dist/commands/start/createWorktree.d.ts.map +1 -0
- package/dist/commands/start/createWorktree.js +35 -0
- package/dist/commands/start/createWorktree.js.map +1 -0
- package/dist/commands/start/createWorktree.test.d.ts +2 -0
- package/dist/commands/start/createWorktree.test.d.ts.map +1 -0
- package/dist/commands/start/createWorktree.test.js +81 -0
- package/dist/commands/start/createWorktree.test.js.map +1 -0
- package/dist/commands/start/index.d.ts +4 -0
- package/dist/commands/start/index.d.ts.map +1 -0
- package/dist/commands/start/index.js +4 -0
- package/dist/commands/start/index.js.map +1 -0
- package/dist/commands/start/parseIssueInput.d.ts +21 -0
- package/dist/commands/start/parseIssueInput.d.ts.map +1 -0
- package/dist/commands/start/parseIssueInput.js +78 -0
- package/dist/commands/start/parseIssueInput.js.map +1 -0
- package/dist/commands/start/parseIssueInput.test.d.ts +2 -0
- package/dist/commands/start/parseIssueInput.test.d.ts.map +1 -0
- package/dist/commands/start/parseIssueInput.test.js +118 -0
- package/dist/commands/start/parseIssueInput.test.js.map +1 -0
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +141 -280
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/start.test.d.ts +2 -0
- package/dist/commands/start.test.d.ts.map +1 -0
- package/dist/commands/start.test.js +260 -0
- package/dist/commands/start.test.js.map +1 -0
- package/dist/commands/status.test.d.ts +2 -0
- package/dist/commands/status.test.d.ts.map +1 -0
- package/dist/commands/status.test.js +172 -0
- package/dist/commands/status.test.js.map +1 -0
- package/dist/commands/stop.test.d.ts +2 -0
- package/dist/commands/stop.test.d.ts.map +1 -0
- package/dist/commands/stop.test.js +152 -0
- package/dist/commands/stop.test.js.map +1 -0
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +95 -1
- package/dist/commands/web.js.map +1 -1
- package/dist/commands/web.test.d.ts +2 -0
- package/dist/commands/web.test.d.ts.map +1 -0
- package/dist/commands/web.test.js +133 -0
- package/dist/commands/web.test.js.map +1 -0
- package/package.json +8 -5
package/dist/commands/web.js
CHANGED
|
@@ -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);
|
package/dist/commands/web.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/commands/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/commands/web.ts"],"names":[],"mappings":"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"}
|
|
@@ -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.
|
|
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.
|
|
33
|
-
"@claudetree/shared": "0.4.
|
|
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
|
}
|