@xortex/xcode 3.0.5 → 3.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/INSTALLATION.md +285 -0
- package/QUICKSTART.md +151 -0
- package/SYSTEM_PROMPT.md +583 -0
- package/SYSTEM_PROMPT_EXTRACTED.md +1 -0
- package/Untitled +1 -0
- package/bin/xcode +69 -120
- package/bootstrap/state.ts +1758 -0
- package/bun-bundle-hook.js +38 -17
- package/bun-bundle-shim.ts +12 -0
- package/bun.lock +645 -0
- package/context/QueuedMessageContext.tsx +63 -0
- package/context/fpsMetrics.tsx +30 -0
- package/context/mailbox.tsx +38 -0
- package/context/modalContext.tsx +58 -0
- package/context/notifications.tsx +240 -0
- package/context/overlayContext.tsx +151 -0
- package/context/promptOverlayContext.tsx +125 -0
- package/context/stats.tsx +220 -0
- package/context/voice.tsx +88 -0
- package/coordinator/coordinatorMode.ts +369 -0
- package/costHook.ts +22 -0
- package/dialogLaunchers.tsx +133 -0
- package/entrypoints/cli.tsx +1 -1
- package/extract_prompt.ts +304 -0
- package/ink.ts +85 -0
- package/install.sh +221 -0
- package/interactiveHelpers.tsx +366 -0
- package/macro.ts +1 -1
- package/memdir/findRelevantMemories.ts +141 -0
- package/memdir/memdir.ts +511 -0
- package/memdir/memoryAge.ts +53 -0
- package/memdir/memoryScan.ts +94 -0
- package/memdir/memoryTypes.ts +271 -0
- package/memdir/paths.ts +291 -0
- package/memdir/teamMemPaths.ts +292 -0
- package/memdir/teamMemPrompts.ts +100 -0
- package/moreright/useMoreRight.tsx +26 -0
- package/native-ts/color-diff/index.ts +999 -0
- package/native-ts/file-index/index.ts +370 -0
- package/native-ts/yoga-layout/enums.ts +134 -0
- package/native-ts/yoga-layout/index.ts +2578 -0
- package/outputStyles/loadOutputStylesDir.ts +98 -0
- package/package.json +5 -42
- package/plugins/builtinPlugins.ts +159 -0
- package/plugins/bundled/index.ts +23 -0
- package/projectOnboardingState.ts +83 -0
- package/public/claude-files.png +0 -0
- package/public/leak-tweet.png +0 -0
- package/query/config.ts +46 -0
- package/query/deps.ts +40 -0
- package/query/stopHooks.ts +470 -0
- package/query/tokenBudget.ts +93 -0
- package/replLauncher.tsx +27 -0
- package/schemas/hooks.ts +222 -0
- package/screens/Doctor.tsx +575 -0
- package/screens/REPL.tsx +7107 -0
- package/screens/ResumeConversation.tsx +399 -0
- package/scripts/postinstall.js +90 -0
- package/server/createDirectConnectSession.ts +88 -0
- package/server/directConnectManager.ts +213 -0
- package/server/types.ts +57 -0
- package/setup.ts +477 -0
- package/stub_types.sh +13 -0
- package/tasks.ts +39 -0
- package/tools.ts +396 -0
- package/tsconfig.json +16 -0
- package/upstreamproxy/relay.ts +455 -0
- package/upstreamproxy/upstreamproxy.ts +285 -0
- package/vim/motions.ts +82 -0
- package/vim/operators.ts +556 -0
- package/vim/textObjects.ts +186 -0
- package/vim/transitions.ts +490 -0
- package/vim/types.ts +199 -0
- package/voice/voiceModeEnabled.ts +54 -0
- package/utils/bunBundleCompat.ts +0 -44
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/* eslint-disable eslint-plugin-n/no-unsupported-features/node-builtins */
|
|
2
|
+
|
|
3
|
+
import type { SDKMessage } from '../entrypoints/agentSdkTypes.js'
|
|
4
|
+
import type {
|
|
5
|
+
SDKControlPermissionRequest,
|
|
6
|
+
StdoutMessage,
|
|
7
|
+
} from '../entrypoints/sdk/controlTypes.js'
|
|
8
|
+
import type { RemotePermissionResponse } from '../remote/RemoteSessionManager.js'
|
|
9
|
+
import { logForDebugging } from '../utils/debug.js'
|
|
10
|
+
import { jsonParse, jsonStringify } from '../utils/slowOperations.js'
|
|
11
|
+
import type { RemoteMessageContent } from '../utils/teleport/api.js'
|
|
12
|
+
|
|
13
|
+
export type DirectConnectConfig = {
|
|
14
|
+
serverUrl: string
|
|
15
|
+
sessionId: string
|
|
16
|
+
wsUrl: string
|
|
17
|
+
authToken?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type DirectConnectCallbacks = {
|
|
21
|
+
onMessage: (message: SDKMessage) => void
|
|
22
|
+
onPermissionRequest: (
|
|
23
|
+
request: SDKControlPermissionRequest,
|
|
24
|
+
requestId: string,
|
|
25
|
+
) => void
|
|
26
|
+
onConnected?: () => void
|
|
27
|
+
onDisconnected?: () => void
|
|
28
|
+
onError?: (error: Error) => void
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isStdoutMessage(value: unknown): value is StdoutMessage {
|
|
32
|
+
return (
|
|
33
|
+
typeof value === 'object' &&
|
|
34
|
+
value !== null &&
|
|
35
|
+
'type' in value &&
|
|
36
|
+
typeof value.type === 'string'
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class DirectConnectSessionManager {
|
|
41
|
+
private ws: WebSocket | null = null
|
|
42
|
+
private config: DirectConnectConfig
|
|
43
|
+
private callbacks: DirectConnectCallbacks
|
|
44
|
+
|
|
45
|
+
constructor(config: DirectConnectConfig, callbacks: DirectConnectCallbacks) {
|
|
46
|
+
this.config = config
|
|
47
|
+
this.callbacks = callbacks
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
connect(): void {
|
|
51
|
+
const headers: Record<string, string> = {}
|
|
52
|
+
if (this.config.authToken) {
|
|
53
|
+
headers['authorization'] = `Bearer ${this.config.authToken}`
|
|
54
|
+
}
|
|
55
|
+
// Bun's WebSocket supports headers option but the DOM typings don't
|
|
56
|
+
this.ws = new WebSocket(this.config.wsUrl, {
|
|
57
|
+
headers,
|
|
58
|
+
} as unknown as string[])
|
|
59
|
+
|
|
60
|
+
this.ws.addEventListener('open', () => {
|
|
61
|
+
this.callbacks.onConnected?.()
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
this.ws.addEventListener('message', event => {
|
|
65
|
+
const data = typeof event.data === 'string' ? event.data : ''
|
|
66
|
+
const lines = data.split('\n').filter((l: string) => l.trim())
|
|
67
|
+
|
|
68
|
+
for (const line of lines) {
|
|
69
|
+
let raw: unknown
|
|
70
|
+
try {
|
|
71
|
+
raw = jsonParse(line)
|
|
72
|
+
} catch {
|
|
73
|
+
continue
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!isStdoutMessage(raw)) {
|
|
77
|
+
continue
|
|
78
|
+
}
|
|
79
|
+
const parsed = raw
|
|
80
|
+
|
|
81
|
+
// Handle control requests (permission requests)
|
|
82
|
+
if (parsed.type === 'control_request') {
|
|
83
|
+
if (parsed.request.subtype === 'can_use_tool') {
|
|
84
|
+
this.callbacks.onPermissionRequest(
|
|
85
|
+
parsed.request,
|
|
86
|
+
parsed.request_id,
|
|
87
|
+
)
|
|
88
|
+
} else {
|
|
89
|
+
// Send an error response for unrecognized subtypes so the
|
|
90
|
+
// server doesn't hang waiting for a reply that never comes.
|
|
91
|
+
logForDebugging(
|
|
92
|
+
`[DirectConnect] Unsupported control request subtype: ${parsed.request.subtype}`,
|
|
93
|
+
)
|
|
94
|
+
this.sendErrorResponse(
|
|
95
|
+
parsed.request_id,
|
|
96
|
+
`Unsupported control request subtype: ${parsed.request.subtype}`,
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
continue
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Forward SDK messages (assistant, result, system, etc.)
|
|
103
|
+
if (
|
|
104
|
+
parsed.type !== 'control_response' &&
|
|
105
|
+
parsed.type !== 'keep_alive' &&
|
|
106
|
+
parsed.type !== 'control_cancel_request' &&
|
|
107
|
+
parsed.type !== 'streamlined_text' &&
|
|
108
|
+
parsed.type !== 'streamlined_tool_use_summary' &&
|
|
109
|
+
!(parsed.type === 'system' && parsed.subtype === 'post_turn_summary')
|
|
110
|
+
) {
|
|
111
|
+
this.callbacks.onMessage(parsed)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
this.ws.addEventListener('close', () => {
|
|
117
|
+
this.callbacks.onDisconnected?.()
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
this.ws.addEventListener('error', () => {
|
|
121
|
+
this.callbacks.onError?.(new Error('WebSocket connection error'))
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
sendMessage(content: RemoteMessageContent): boolean {
|
|
126
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
127
|
+
return false
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Must match SDKUserMessage format expected by `--input-format stream-json`
|
|
131
|
+
const message = jsonStringify({
|
|
132
|
+
type: 'user',
|
|
133
|
+
message: {
|
|
134
|
+
role: 'user',
|
|
135
|
+
content: content,
|
|
136
|
+
},
|
|
137
|
+
parent_tool_use_id: null,
|
|
138
|
+
session_id: '',
|
|
139
|
+
})
|
|
140
|
+
this.ws.send(message)
|
|
141
|
+
return true
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
respondToPermissionRequest(
|
|
145
|
+
requestId: string,
|
|
146
|
+
result: RemotePermissionResponse,
|
|
147
|
+
): void {
|
|
148
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Must match SDKControlResponse format expected by StructuredIO
|
|
153
|
+
const response = jsonStringify({
|
|
154
|
+
type: 'control_response',
|
|
155
|
+
response: {
|
|
156
|
+
subtype: 'success',
|
|
157
|
+
request_id: requestId,
|
|
158
|
+
response: {
|
|
159
|
+
behavior: result.behavior,
|
|
160
|
+
...(result.behavior === 'allow'
|
|
161
|
+
? { updatedInput: result.updatedInput }
|
|
162
|
+
: { message: result.message }),
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
})
|
|
166
|
+
this.ws.send(response)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Send an interrupt signal to cancel the current request
|
|
171
|
+
*/
|
|
172
|
+
sendInterrupt(): void {
|
|
173
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Must match SDKControlRequest format expected by StructuredIO
|
|
178
|
+
const request = jsonStringify({
|
|
179
|
+
type: 'control_request',
|
|
180
|
+
request_id: crypto.randomUUID(),
|
|
181
|
+
request: {
|
|
182
|
+
subtype: 'interrupt',
|
|
183
|
+
},
|
|
184
|
+
})
|
|
185
|
+
this.ws.send(request)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private sendErrorResponse(requestId: string, error: string): void {
|
|
189
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
const response = jsonStringify({
|
|
193
|
+
type: 'control_response',
|
|
194
|
+
response: {
|
|
195
|
+
subtype: 'error',
|
|
196
|
+
request_id: requestId,
|
|
197
|
+
error,
|
|
198
|
+
},
|
|
199
|
+
})
|
|
200
|
+
this.ws.send(response)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
disconnect(): void {
|
|
204
|
+
if (this.ws) {
|
|
205
|
+
this.ws.close()
|
|
206
|
+
this.ws = null
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
isConnected(): boolean {
|
|
211
|
+
return this.ws?.readyState === WebSocket.OPEN
|
|
212
|
+
}
|
|
213
|
+
}
|
package/server/types.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ChildProcess } from 'child_process'
|
|
2
|
+
import { z } from 'zod/v4'
|
|
3
|
+
import { lazySchema } from '../utils/lazySchema.js'
|
|
4
|
+
|
|
5
|
+
export const connectResponseSchema = lazySchema(() =>
|
|
6
|
+
z.object({
|
|
7
|
+
session_id: z.string(),
|
|
8
|
+
ws_url: z.string(),
|
|
9
|
+
work_dir: z.string().optional(),
|
|
10
|
+
}),
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
export type ServerConfig = {
|
|
14
|
+
port: number
|
|
15
|
+
host: string
|
|
16
|
+
authToken: string
|
|
17
|
+
unix?: string
|
|
18
|
+
/** Idle timeout for detached sessions (ms). 0 = never expire. */
|
|
19
|
+
idleTimeoutMs?: number
|
|
20
|
+
/** Maximum number of concurrent sessions. */
|
|
21
|
+
maxSessions?: number
|
|
22
|
+
/** Default workspace directory for sessions that don't specify cwd. */
|
|
23
|
+
workspace?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type SessionState =
|
|
27
|
+
| 'starting'
|
|
28
|
+
| 'running'
|
|
29
|
+
| 'detached'
|
|
30
|
+
| 'stopping'
|
|
31
|
+
| 'stopped'
|
|
32
|
+
|
|
33
|
+
export type SessionInfo = {
|
|
34
|
+
id: string
|
|
35
|
+
status: SessionState
|
|
36
|
+
createdAt: number
|
|
37
|
+
workDir: string
|
|
38
|
+
process: ChildProcess | null
|
|
39
|
+
sessionKey?: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Stable session key → session metadata. Persisted to ~/.claude/server-sessions.json
|
|
44
|
+
* so sessions can be resumed across server restarts.
|
|
45
|
+
*/
|
|
46
|
+
export type SessionIndexEntry = {
|
|
47
|
+
/** Server-assigned session ID (matches the subprocess's claude session). */
|
|
48
|
+
sessionId: string
|
|
49
|
+
/** The claude transcript session ID for --resume. Same as sessionId for direct sessions. */
|
|
50
|
+
transcriptSessionId: string
|
|
51
|
+
cwd: string
|
|
52
|
+
permissionMode?: string
|
|
53
|
+
createdAt: number
|
|
54
|
+
lastActiveAt: number
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type SessionIndex = Record<string, SessionIndexEntry>
|