@cmdctrl/claude-code 0.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.
Files changed (83) hide show
  1. package/dist/adapter/claude-cli.d.ts +41 -0
  2. package/dist/adapter/claude-cli.d.ts.map +1 -0
  3. package/dist/adapter/claude-cli.js +525 -0
  4. package/dist/adapter/claude-cli.js.map +1 -0
  5. package/dist/adapter/events.d.ts +52 -0
  6. package/dist/adapter/events.d.ts.map +1 -0
  7. package/dist/adapter/events.js +134 -0
  8. package/dist/adapter/events.js.map +1 -0
  9. package/dist/client/messages.d.ts +140 -0
  10. package/dist/client/messages.d.ts.map +1 -0
  11. package/dist/client/messages.js +6 -0
  12. package/dist/client/messages.js.map +1 -0
  13. package/dist/client/websocket.d.ts +115 -0
  14. package/dist/client/websocket.d.ts.map +1 -0
  15. package/dist/client/websocket.js +434 -0
  16. package/dist/client/websocket.js.map +1 -0
  17. package/dist/commands/register.d.ts +10 -0
  18. package/dist/commands/register.d.ts.map +1 -0
  19. package/dist/commands/register.js +175 -0
  20. package/dist/commands/register.js.map +1 -0
  21. package/dist/commands/start.d.ts +9 -0
  22. package/dist/commands/start.d.ts.map +1 -0
  23. package/dist/commands/start.js +54 -0
  24. package/dist/commands/start.js.map +1 -0
  25. package/dist/commands/status.d.ts +5 -0
  26. package/dist/commands/status.d.ts.map +1 -0
  27. package/dist/commands/status.js +38 -0
  28. package/dist/commands/status.js.map +1 -0
  29. package/dist/commands/stop.d.ts +5 -0
  30. package/dist/commands/stop.d.ts.map +1 -0
  31. package/dist/commands/stop.js +59 -0
  32. package/dist/commands/stop.js.map +1 -0
  33. package/dist/commands/unregister.d.ts +5 -0
  34. package/dist/commands/unregister.d.ts.map +1 -0
  35. package/dist/commands/unregister.js +28 -0
  36. package/dist/commands/unregister.js.map +1 -0
  37. package/dist/config/config.d.ts +68 -0
  38. package/dist/config/config.d.ts.map +1 -0
  39. package/dist/config/config.js +193 -0
  40. package/dist/config/config.js.map +1 -0
  41. package/dist/handlers/context-handler.d.ts +37 -0
  42. package/dist/handlers/context-handler.d.ts.map +1 -0
  43. package/dist/handlers/context-handler.js +303 -0
  44. package/dist/handlers/context-handler.js.map +1 -0
  45. package/dist/index.d.ts +3 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +39 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/message-reader.d.ts +25 -0
  50. package/dist/message-reader.d.ts.map +1 -0
  51. package/dist/message-reader.js +454 -0
  52. package/dist/message-reader.js.map +1 -0
  53. package/dist/session-discovery.d.ts +48 -0
  54. package/dist/session-discovery.d.ts.map +1 -0
  55. package/dist/session-discovery.js +496 -0
  56. package/dist/session-discovery.js.map +1 -0
  57. package/dist/session-watcher.d.ts +92 -0
  58. package/dist/session-watcher.d.ts.map +1 -0
  59. package/dist/session-watcher.js +494 -0
  60. package/dist/session-watcher.js.map +1 -0
  61. package/dist/session-watcher.test.d.ts +9 -0
  62. package/dist/session-watcher.test.d.ts.map +1 -0
  63. package/dist/session-watcher.test.js +149 -0
  64. package/dist/session-watcher.test.js.map +1 -0
  65. package/jest.config.js +8 -0
  66. package/package.json +42 -0
  67. package/src/adapter/claude-cli.ts +591 -0
  68. package/src/adapter/events.ts +186 -0
  69. package/src/client/messages.ts +193 -0
  70. package/src/client/websocket.ts +509 -0
  71. package/src/commands/register.ts +201 -0
  72. package/src/commands/start.ts +70 -0
  73. package/src/commands/status.ts +47 -0
  74. package/src/commands/stop.ts +58 -0
  75. package/src/commands/unregister.ts +30 -0
  76. package/src/config/config.ts +163 -0
  77. package/src/handlers/context-handler.ts +337 -0
  78. package/src/index.ts +45 -0
  79. package/src/message-reader.ts +485 -0
  80. package/src/session-discovery.ts +557 -0
  81. package/src/session-watcher.test.ts +141 -0
  82. package/src/session-watcher.ts +560 -0
  83. package/tsconfig.json +19 -0
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ /**
3
+ * Tests for SessionWatcher file change detection
4
+ *
5
+ * This test verifies that the SessionWatcher reliably detects
6
+ * file changes when an external process appends to a JSONL file
7
+ * (simulating Claude CLI writing to session files).
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const os = __importStar(require("os"));
46
+ const child_process_1 = require("child_process");
47
+ const session_watcher_1 = require("./session-watcher");
48
+ describe('SessionWatcher', () => {
49
+ let tempDir;
50
+ let tempFile;
51
+ let watcher;
52
+ let events;
53
+ beforeEach(() => {
54
+ // Create a temp directory and file for each test
55
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'session-watcher-test-'));
56
+ tempFile = path.join(tempDir, 'test-session.jsonl');
57
+ // Write initial content with uuid (required for processing)
58
+ fs.writeFileSync(tempFile, '{"uuid":"init-1","type":"user","message":{"content":"initial message"}}\n');
59
+ events = [];
60
+ watcher = new session_watcher_1.SessionWatcher((event) => {
61
+ events.push(event);
62
+ });
63
+ });
64
+ afterEach(() => {
65
+ watcher.unwatchAll();
66
+ // Clean up temp files
67
+ if (fs.existsSync(tempFile)) {
68
+ fs.unlinkSync(tempFile);
69
+ }
70
+ if (fs.existsSync(tempDir)) {
71
+ fs.rmdirSync(tempDir);
72
+ }
73
+ });
74
+ it('should detect file changes when external process appends content', async () => {
75
+ // Start watching the file
76
+ watcher.watchSession('test-session-123', tempFile);
77
+ // Wait for watcher to initialize
78
+ await sleep(100);
79
+ // Simulate external process (Claude CLI) appending to file
80
+ // This mimics how the CLI incrementally writes JSONL lines
81
+ fs.appendFileSync(tempFile, '{"uuid":"resp-1","type":"assistant","message":{"content":[{"type":"text","text":"response 1"}]}}\n');
82
+ // Wait for the watcher to detect the change
83
+ // Using 2 seconds as a reasonable timeout - if fs.watch works, it should be much faster
84
+ // If using polling at 500ms, we need at least that long plus processing time
85
+ await sleep(2000);
86
+ // The watcher should have detected the change and fired the callback
87
+ expect(events.length).toBeGreaterThan(0);
88
+ expect(events[0].sessionId).toBe('test-session-123');
89
+ expect(events[0].type).toBe('AGENT_RESPONSE');
90
+ expect(events[0].content).toBe('response 1');
91
+ });
92
+ it('should detect multiple sequential appends', async () => {
93
+ watcher.watchSession('test-session-456', tempFile);
94
+ await sleep(100);
95
+ // Simulate multiple rapid appends (like Claude streaming output)
96
+ fs.appendFileSync(tempFile, '{"uuid":"line-1","type":"assistant","message":{"content":[{"type":"text","text":"line 1"}]}}\n');
97
+ await sleep(100);
98
+ fs.appendFileSync(tempFile, '{"uuid":"line-2","type":"assistant","message":{"content":[{"type":"text","text":"line 2"}]}}\n');
99
+ await sleep(100);
100
+ fs.appendFileSync(tempFile, '{"uuid":"line-3","type":"assistant","message":{"content":[{"type":"text","text":"line 3"}]}}\n');
101
+ // Wait for detection (accounting for polling interval)
102
+ await sleep(2000);
103
+ // Should have detected all events
104
+ expect(events.length).toBe(3);
105
+ expect(events.map(e => e.content)).toEqual(['line 1', 'line 2', 'line 3']);
106
+ });
107
+ it('should detect file changes from external process (simulates Claude CLI)', async () => {
108
+ // This is the critical test - external processes appending to files
109
+ // is exactly how the Claude CLI writes to session JSONL files.
110
+ // fs.watch() on macOS often fails to detect these changes.
111
+ watcher.watchSession('test-session-external', tempFile);
112
+ await sleep(100);
113
+ // Use shell to append - this is an external process, just like Claude CLI
114
+ const jsonLine = '{"uuid":"ext-1","type":"assistant","message":{"content":[{"type":"text","text":"external append"}]}}';
115
+ (0, child_process_1.execSync)(`echo '${jsonLine}' >> "${tempFile}"`);
116
+ // Wait for detection
117
+ await sleep(2000);
118
+ // The watcher MUST detect changes from external processes
119
+ expect(events.length).toBeGreaterThan(0);
120
+ expect(events[0].sessionId).toBe('test-session-external');
121
+ expect(events[0].type).toBe('AGENT_RESPONSE');
122
+ });
123
+ it('should emit VERBOSE for tool_use entries', async () => {
124
+ watcher.watchSession('test-session-tool', tempFile);
125
+ await sleep(100);
126
+ // Append a tool_use entry
127
+ const toolEntry = '{"uuid":"tool-1","type":"assistant","message":{"content":[{"type":"tool_use","name":"Read","input":{"file_path":"/test/file.ts"}}]}}';
128
+ fs.appendFileSync(tempFile, toolEntry + '\n');
129
+ await sleep(2000);
130
+ expect(events.length).toBe(1);
131
+ expect(events[0].type).toBe('VERBOSE');
132
+ expect(events[0].content).toContain('Reading');
133
+ });
134
+ it('should emit USER_MESSAGE for user entries', async () => {
135
+ watcher.watchSession('test-session-user', tempFile);
136
+ await sleep(100);
137
+ // Append a user message entry
138
+ const userEntry = '{"uuid":"user-1","type":"user","message":{"content":"hello agent"}}';
139
+ fs.appendFileSync(tempFile, userEntry + '\n');
140
+ await sleep(2000);
141
+ expect(events.length).toBe(1);
142
+ expect(events[0].type).toBe('USER_MESSAGE');
143
+ expect(events[0].content).toBe('hello agent');
144
+ });
145
+ });
146
+ function sleep(ms) {
147
+ return new Promise((resolve) => setTimeout(resolve, ms));
148
+ }
149
+ //# sourceMappingURL=session-watcher.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-watcher.test.js","sourceRoot":"","sources":["../src/session-watcher.test.ts"],"names":[],"mappings":"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"}
package/jest.config.js ADDED
@@ -0,0 +1,8 @@
1
+ /** @type {import('ts-jest').JestConfigWithTsJest} */
2
+ module.exports = {
3
+ preset: 'ts-jest',
4
+ testEnvironment: 'node',
5
+ testMatch: ['**/*.test.ts'],
6
+ moduleFileExtensions: ['ts', 'js'],
7
+ testTimeout: 10000, // 10 seconds for file watcher tests
8
+ };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@cmdctrl/claude-code",
3
+ "version": "0.1.0",
4
+ "description": "CmdCtrl daemon for Claude Code - connects your workstation to the CmdCtrl orchestration server",
5
+ "main": "./dist/index.js",
6
+ "bin": {
7
+ "cmdctrl-claude-code": "./dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "ts-node src/index.ts",
12
+ "start": "node dist/index.js",
13
+ "lint": "eslint src --ext .ts",
14
+ "clean": "rm -rf dist",
15
+ "test": "jest"
16
+ },
17
+ "keywords": [
18
+ "cmdctrl",
19
+ "daemon",
20
+ "claude",
21
+ "ai",
22
+ "orchestration"
23
+ ],
24
+ "author": "",
25
+ "license": "MIT",
26
+ "dependencies": {
27
+ "commander": "^12.1.0",
28
+ "ws": "^8.18.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/jest": "^29.5.14",
32
+ "@types/node": "^22.10.2",
33
+ "@types/ws": "^8.5.13",
34
+ "jest": "^29.7.0",
35
+ "ts-jest": "^29.2.5",
36
+ "typescript": "^5.7.2",
37
+ "ts-node": "^10.9.2"
38
+ },
39
+ "engines": {
40
+ "node": ">=18.0.0"
41
+ }
42
+ }