@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.
- package/dist/adapter/claude-cli.d.ts +41 -0
- package/dist/adapter/claude-cli.d.ts.map +1 -0
- package/dist/adapter/claude-cli.js +525 -0
- package/dist/adapter/claude-cli.js.map +1 -0
- package/dist/adapter/events.d.ts +52 -0
- package/dist/adapter/events.d.ts.map +1 -0
- package/dist/adapter/events.js +134 -0
- package/dist/adapter/events.js.map +1 -0
- package/dist/client/messages.d.ts +140 -0
- package/dist/client/messages.d.ts.map +1 -0
- package/dist/client/messages.js +6 -0
- package/dist/client/messages.js.map +1 -0
- package/dist/client/websocket.d.ts +115 -0
- package/dist/client/websocket.d.ts.map +1 -0
- package/dist/client/websocket.js +434 -0
- package/dist/client/websocket.js.map +1 -0
- package/dist/commands/register.d.ts +10 -0
- package/dist/commands/register.d.ts.map +1 -0
- package/dist/commands/register.js +175 -0
- package/dist/commands/register.js.map +1 -0
- package/dist/commands/start.d.ts +9 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +54 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +38 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/stop.d.ts +5 -0
- package/dist/commands/stop.d.ts.map +1 -0
- package/dist/commands/stop.js +59 -0
- package/dist/commands/stop.js.map +1 -0
- package/dist/commands/unregister.d.ts +5 -0
- package/dist/commands/unregister.d.ts.map +1 -0
- package/dist/commands/unregister.js +28 -0
- package/dist/commands/unregister.js.map +1 -0
- package/dist/config/config.d.ts +68 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +193 -0
- package/dist/config/config.js.map +1 -0
- package/dist/handlers/context-handler.d.ts +37 -0
- package/dist/handlers/context-handler.d.ts.map +1 -0
- package/dist/handlers/context-handler.js +303 -0
- package/dist/handlers/context-handler.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/message-reader.d.ts +25 -0
- package/dist/message-reader.d.ts.map +1 -0
- package/dist/message-reader.js +454 -0
- package/dist/message-reader.js.map +1 -0
- package/dist/session-discovery.d.ts +48 -0
- package/dist/session-discovery.d.ts.map +1 -0
- package/dist/session-discovery.js +496 -0
- package/dist/session-discovery.js.map +1 -0
- package/dist/session-watcher.d.ts +92 -0
- package/dist/session-watcher.d.ts.map +1 -0
- package/dist/session-watcher.js +494 -0
- package/dist/session-watcher.js.map +1 -0
- package/dist/session-watcher.test.d.ts +9 -0
- package/dist/session-watcher.test.d.ts.map +1 -0
- package/dist/session-watcher.test.js +149 -0
- package/dist/session-watcher.test.js.map +1 -0
- package/jest.config.js +8 -0
- package/package.json +42 -0
- package/src/adapter/claude-cli.ts +591 -0
- package/src/adapter/events.ts +186 -0
- package/src/client/messages.ts +193 -0
- package/src/client/websocket.ts +509 -0
- package/src/commands/register.ts +201 -0
- package/src/commands/start.ts +70 -0
- package/src/commands/status.ts +47 -0
- package/src/commands/stop.ts +58 -0
- package/src/commands/unregister.ts +30 -0
- package/src/config/config.ts +163 -0
- package/src/handlers/context-handler.ts +337 -0
- package/src/index.ts +45 -0
- package/src/message-reader.ts +485 -0
- package/src/session-discovery.ts +557 -0
- package/src/session-watcher.test.ts +141 -0
- package/src/session-watcher.ts +560 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for Claude CLI stream-json output
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface StreamEvent {
|
|
6
|
+
type: 'system' | 'assistant' | 'result' | 'user';
|
|
7
|
+
subtype?: string;
|
|
8
|
+
session_id?: string;
|
|
9
|
+
message?: MessageContent;
|
|
10
|
+
result?: string;
|
|
11
|
+
permission_denials?: PermissionDenial[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface MessageContent {
|
|
15
|
+
content: ContentBlock[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ContentBlock {
|
|
19
|
+
type: 'text' | 'tool_use' | 'tool_result' | 'thinking';
|
|
20
|
+
text?: string;
|
|
21
|
+
name?: string;
|
|
22
|
+
input?: unknown;
|
|
23
|
+
id?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface PermissionDenial {
|
|
27
|
+
tool_name: string;
|
|
28
|
+
id: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface AskUserInput {
|
|
32
|
+
questions: Question[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface Question {
|
|
36
|
+
question: string;
|
|
37
|
+
header?: string;
|
|
38
|
+
options: QuestionOption[];
|
|
39
|
+
multiSelect?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface QuestionOption {
|
|
43
|
+
label: string;
|
|
44
|
+
description?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ProgressInfo {
|
|
48
|
+
action: string;
|
|
49
|
+
target: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Format raw stream output into human-readable string for verbose display
|
|
54
|
+
* Returns null if the event should be skipped
|
|
55
|
+
*/
|
|
56
|
+
export function formatVerboseOutput(line: string): string | null {
|
|
57
|
+
try {
|
|
58
|
+
const data = JSON.parse(line) as StreamEvent;
|
|
59
|
+
|
|
60
|
+
if (data.type === 'system' && data.subtype === 'init') {
|
|
61
|
+
return '● Task started';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (data.type === 'assistant' && data.message?.content) {
|
|
65
|
+
// Check for text content first
|
|
66
|
+
const texts = data.message.content
|
|
67
|
+
.filter(c => c.type === 'text' && c.text)
|
|
68
|
+
.map(c => c.text!.substring(0, 100));
|
|
69
|
+
if (texts.length > 0) {
|
|
70
|
+
const joined = texts.join(' ');
|
|
71
|
+
const truncated = joined.length > 80 ? joined.substring(0, 80) + '...' : joined;
|
|
72
|
+
return `💬 ${truncated}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check for tool use
|
|
76
|
+
const tools = data.message.content.filter(c => c.type === 'tool_use');
|
|
77
|
+
if (tools.length > 0) {
|
|
78
|
+
const tool = tools[0];
|
|
79
|
+
const name = tool.name || 'unknown';
|
|
80
|
+
const input = tool.input as Record<string, unknown> | undefined;
|
|
81
|
+
|
|
82
|
+
switch (name) {
|
|
83
|
+
case 'Read': return `📖 Reading ${input?.file_path || 'file'}`;
|
|
84
|
+
case 'Write': return `✏️ Writing ${input?.file_path || 'file'}`;
|
|
85
|
+
case 'Edit': return `🔧 Editing ${input?.file_path || 'file'}`;
|
|
86
|
+
case 'Bash': {
|
|
87
|
+
const cmd = String(input?.command || '').substring(0, 60);
|
|
88
|
+
return `⚡ ${cmd}`;
|
|
89
|
+
}
|
|
90
|
+
case 'Glob': return `🔍 Searching: ${input?.pattern || ''}`;
|
|
91
|
+
case 'Grep': return `🔎 Grepping: ${input?.pattern || ''}`;
|
|
92
|
+
case 'Task': return `📋 Spawning agent`;
|
|
93
|
+
case 'TodoWrite': return `📝 Updating todos`;
|
|
94
|
+
case 'WebSearch': return `🌐 Searching: ${input?.query || ''}`;
|
|
95
|
+
case 'WebFetch': return `🌐 Fetching: ${input?.url || ''}`;
|
|
96
|
+
default: return `🔧 ${name}`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (data.type === 'result') {
|
|
102
|
+
// Check for permission denials
|
|
103
|
+
if (data.permission_denials?.length) {
|
|
104
|
+
const denial = data.permission_denials[0];
|
|
105
|
+
const toolName = denial.tool_name;
|
|
106
|
+
if (toolName === 'AskUserQuestion') {
|
|
107
|
+
return '❓ Waiting for your input';
|
|
108
|
+
} else {
|
|
109
|
+
return `⚠️ Permission required: ${toolName}`;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return '✓ Completed';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Skip uninteresting events
|
|
116
|
+
return null;
|
|
117
|
+
} catch {
|
|
118
|
+
// Not JSON - show raw if short enough
|
|
119
|
+
const trimmed = line.trim();
|
|
120
|
+
if (trimmed && trimmed.length < 100) {
|
|
121
|
+
return trimmed;
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Extract progress info from tool use
|
|
129
|
+
*/
|
|
130
|
+
export function extractProgressFromToolUse(
|
|
131
|
+
toolName: string,
|
|
132
|
+
input: unknown
|
|
133
|
+
): ProgressInfo | null {
|
|
134
|
+
const inputObj = input as Record<string, unknown>;
|
|
135
|
+
|
|
136
|
+
switch (toolName) {
|
|
137
|
+
case 'Read':
|
|
138
|
+
return {
|
|
139
|
+
action: 'Reading',
|
|
140
|
+
target: (inputObj?.file_path as string) || 'file'
|
|
141
|
+
};
|
|
142
|
+
case 'Write':
|
|
143
|
+
return {
|
|
144
|
+
action: 'Writing',
|
|
145
|
+
target: (inputObj?.file_path as string) || 'file'
|
|
146
|
+
};
|
|
147
|
+
case 'Edit':
|
|
148
|
+
return {
|
|
149
|
+
action: 'Editing',
|
|
150
|
+
target: (inputObj?.file_path as string) || 'file'
|
|
151
|
+
};
|
|
152
|
+
case 'Bash':
|
|
153
|
+
const cmd = (inputObj?.command as string) || '';
|
|
154
|
+
return {
|
|
155
|
+
action: 'Running',
|
|
156
|
+
target: cmd.length > 30 ? cmd.substring(0, 30) + '...' : cmd
|
|
157
|
+
};
|
|
158
|
+
case 'Glob':
|
|
159
|
+
return {
|
|
160
|
+
action: 'Searching',
|
|
161
|
+
target: (inputObj?.pattern as string) || 'files'
|
|
162
|
+
};
|
|
163
|
+
case 'Grep':
|
|
164
|
+
return {
|
|
165
|
+
action: 'Searching',
|
|
166
|
+
target: (inputObj?.pattern as string) || 'pattern'
|
|
167
|
+
};
|
|
168
|
+
case 'WebSearch':
|
|
169
|
+
return {
|
|
170
|
+
action: 'Searching web',
|
|
171
|
+
target: (inputObj?.query as string) || ''
|
|
172
|
+
};
|
|
173
|
+
case 'WebFetch':
|
|
174
|
+
return {
|
|
175
|
+
action: 'Fetching',
|
|
176
|
+
target: (inputObj?.url as string) || 'URL'
|
|
177
|
+
};
|
|
178
|
+
case 'Task':
|
|
179
|
+
return {
|
|
180
|
+
action: 'Running agent',
|
|
181
|
+
target: (inputObj?.description as string) || ''
|
|
182
|
+
};
|
|
183
|
+
default:
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message types for daemon <-> server communication
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Server -> Daemon messages
|
|
6
|
+
|
|
7
|
+
export interface PingMessage {
|
|
8
|
+
type: 'ping';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface TaskStartMessage {
|
|
12
|
+
type: 'task_start';
|
|
13
|
+
task_id: string;
|
|
14
|
+
instruction: string;
|
|
15
|
+
project_path?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface TaskResumeMessage {
|
|
19
|
+
type: 'task_resume';
|
|
20
|
+
task_id: string;
|
|
21
|
+
session_id: string;
|
|
22
|
+
message: string;
|
|
23
|
+
project_path?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface TaskCancelMessage {
|
|
27
|
+
type: 'task_cancel';
|
|
28
|
+
task_id: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface GetMessagesMessage {
|
|
32
|
+
type: 'get_messages';
|
|
33
|
+
request_id: string;
|
|
34
|
+
session_id: string;
|
|
35
|
+
limit: number;
|
|
36
|
+
before_uuid?: string; // Cursor for backwards pagination (load older)
|
|
37
|
+
after_uuid?: string; // Cursor for forwards pagination (load newer)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface WatchSessionMessage {
|
|
41
|
+
type: 'watch_session';
|
|
42
|
+
session_id: string;
|
|
43
|
+
file_path: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface UnwatchSessionMessage {
|
|
47
|
+
type: 'unwatch_session';
|
|
48
|
+
session_id: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ContextRequestMessage {
|
|
52
|
+
type: 'context_request';
|
|
53
|
+
request_id: string;
|
|
54
|
+
session_id: string;
|
|
55
|
+
include: {
|
|
56
|
+
initial_prompt?: boolean;
|
|
57
|
+
recent_messages?: number; // Number of recent messages to include
|
|
58
|
+
last_tool_use?: boolean;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type ServerMessage =
|
|
63
|
+
| PingMessage
|
|
64
|
+
| TaskStartMessage
|
|
65
|
+
| TaskResumeMessage
|
|
66
|
+
| TaskCancelMessage
|
|
67
|
+
| GetMessagesMessage
|
|
68
|
+
| WatchSessionMessage
|
|
69
|
+
| UnwatchSessionMessage
|
|
70
|
+
| ContextRequestMessage;
|
|
71
|
+
|
|
72
|
+
// Daemon -> Server messages
|
|
73
|
+
|
|
74
|
+
export interface PongMessage {
|
|
75
|
+
type: 'pong';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface StatusMessage {
|
|
79
|
+
type: 'status';
|
|
80
|
+
running_tasks: string[];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface EventMessage {
|
|
84
|
+
type: 'event';
|
|
85
|
+
task_id: string;
|
|
86
|
+
event_type: string;
|
|
87
|
+
[key: string]: unknown;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface SessionInfo {
|
|
91
|
+
session_id: string;
|
|
92
|
+
slug: string;
|
|
93
|
+
title: string;
|
|
94
|
+
project: string;
|
|
95
|
+
project_name: string;
|
|
96
|
+
file_path: string;
|
|
97
|
+
last_message: string;
|
|
98
|
+
last_activity: string;
|
|
99
|
+
is_active: boolean;
|
|
100
|
+
message_count: number;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface ReportSessionsMessage {
|
|
104
|
+
type: 'report_sessions';
|
|
105
|
+
sessions: SessionInfo[];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface MessageEntry {
|
|
109
|
+
uuid: string;
|
|
110
|
+
role: 'USER' | 'AGENT' | 'SYSTEM';
|
|
111
|
+
content: string;
|
|
112
|
+
timestamp: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface MessagesResponseMessage {
|
|
116
|
+
type: 'messages';
|
|
117
|
+
request_id: string;
|
|
118
|
+
session_id: string;
|
|
119
|
+
messages: MessageEntry[];
|
|
120
|
+
has_more: boolean;
|
|
121
|
+
oldest_uuid?: string;
|
|
122
|
+
newest_uuid?: string;
|
|
123
|
+
error?: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface SessionActivityMessage {
|
|
127
|
+
type: 'session_activity';
|
|
128
|
+
session_id: string;
|
|
129
|
+
file_path: string;
|
|
130
|
+
last_message: string;
|
|
131
|
+
message_count: number;
|
|
132
|
+
is_completion: boolean; // True when last message is from assistant (agent finished)
|
|
133
|
+
user_message_uuid?: string; // UUID of the triggering user message (for positioning verbose output)
|
|
134
|
+
last_activity: string; // ISO timestamp of the last message (not file mtime)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Session status for dashboard summaries
|
|
138
|
+
export type SessionStatus = 'working' | 'waiting_for_input' | 'completed' | 'errored' | 'stale';
|
|
139
|
+
|
|
140
|
+
export interface SessionStatusMessage {
|
|
141
|
+
type: 'session_status';
|
|
142
|
+
session_id: string;
|
|
143
|
+
status: SessionStatus;
|
|
144
|
+
status_detail?: string; // e.g., "Asked: Which database should I use?"
|
|
145
|
+
last_tool_use?: string; // e.g., "Read file: schema.sql"
|
|
146
|
+
message_count: number;
|
|
147
|
+
elapsed_minutes: number;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface ContextResponseMessage {
|
|
151
|
+
type: 'context_response';
|
|
152
|
+
request_id: string;
|
|
153
|
+
session_id: string;
|
|
154
|
+
context: {
|
|
155
|
+
title: string;
|
|
156
|
+
project_path: string;
|
|
157
|
+
initial_prompt?: string;
|
|
158
|
+
recent_messages?: Array<{
|
|
159
|
+
role: 'USER' | 'AGENT';
|
|
160
|
+
content: string;
|
|
161
|
+
}>;
|
|
162
|
+
last_tool_use?: string;
|
|
163
|
+
message_count: number;
|
|
164
|
+
started_at?: string;
|
|
165
|
+
last_activity_at: string;
|
|
166
|
+
status: SessionStatus;
|
|
167
|
+
status_detail?: string;
|
|
168
|
+
};
|
|
169
|
+
error?: string;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export type DaemonMessage =
|
|
173
|
+
| PongMessage
|
|
174
|
+
| StatusMessage
|
|
175
|
+
| EventMessage
|
|
176
|
+
| ReportSessionsMessage
|
|
177
|
+
| MessagesResponseMessage
|
|
178
|
+
| SessionActivityMessage
|
|
179
|
+
| SessionStatusMessage
|
|
180
|
+
| ContextResponseMessage;
|
|
181
|
+
|
|
182
|
+
// Event types sent from daemon to server
|
|
183
|
+
export type EventType =
|
|
184
|
+
| 'SESSION_STARTED' // Internal: triggers file watching (not sent to server)
|
|
185
|
+
| 'WAIT_FOR_USER'
|
|
186
|
+
| 'TASK_COMPLETE'
|
|
187
|
+
| 'OUTPUT' // Legacy: verbose output from CLI stream (being phased out)
|
|
188
|
+
| 'PROGRESS'
|
|
189
|
+
| 'ERROR'
|
|
190
|
+
// New JSONL-based event types
|
|
191
|
+
| 'AGENT_RESPONSE' // Assistant entry with text content
|
|
192
|
+
| 'VERBOSE' // Tool use, thinking, tool result
|
|
193
|
+
| 'USER_MESSAGE'; // User message (for passive observers)
|