typeclaw 0.20.0 → 0.21.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/package.json +1 -1
- package/src/agent/restart/index.ts +101 -0
- package/src/agent/tools/restart.ts +23 -52
- package/src/channels/adapters/discord-bot-classify.ts +8 -2
- package/src/channels/adapters/discord-bot.ts +27 -2
- package/src/channels/adapters/github/decoy-reviewer.ts +43 -0
- package/src/channels/adapters/github/inbound.ts +69 -0
- package/src/channels/adapters/github/index.ts +11 -1
- package/src/channels/adapters/github/reactions.ts +138 -4
- package/src/channels/adapters/slack-bot-classify.ts +2 -2
- package/src/channels/adapters/slack-bot.ts +25 -2
- package/src/channels/engagement.ts +71 -31
- package/src/channels/router.ts +112 -10
- package/src/channels/types.ts +16 -1
- package/src/cli/builtins.ts +1 -0
- package/src/cli/dreams.ts +147 -0
- package/src/cli/index.ts +1 -0
- package/src/dreams/git.ts +85 -0
- package/src/dreams/index.ts +134 -0
- package/src/dreams/parse.ts +224 -0
- package/src/dreams/render.ts +155 -0
- package/src/dreams/types.ts +50 -0
- package/src/server/index.ts +49 -0
- package/src/shared/protocol.ts +2 -0
- package/src/skills/typeclaw-channel-github/SKILL.md +3 -9
- package/src/tui/index.ts +70 -18
package/src/tui/index.ts
CHANGED
|
@@ -11,19 +11,25 @@ export type TerminalFactory = () => Terminal
|
|
|
11
11
|
|
|
12
12
|
const DEFAULT_HANDSHAKE_TIMEOUT_MS = 30_000
|
|
13
13
|
|
|
14
|
-
// Bare slash-command names (no leading `/`) the TUI intercepts client-side
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
14
|
+
// Bare slash-command names (no leading `/`) the TUI intercepts client-side.
|
|
15
|
+
// The hatching ritual tells the agent to point users at `/quit` (see
|
|
16
|
+
// src/init/hatching.ts); without an intercept the literal text would be shipped
|
|
17
|
+
// to the LLM as a chat message. Grammar (case-insensitive, whitespace-tolerant,
|
|
18
|
+
// `//foo` escapes to a literal prompt) comes from `parseCommand` in
|
|
19
|
+
// src/commands so channel and TUI slash commands stay consistent. Arguments
|
|
20
|
+
// after the name disqualify the match: `/quit me a story` is a real prompt, not
|
|
21
|
+
// a command.
|
|
22
22
|
const QUIT_COMMAND_NAMES: ReadonlySet<string> = new Set(['quit', 'exit'])
|
|
23
|
+
const TUI_COMMAND_NAMES: ReadonlySet<TuiCommandName> = new Set(['quit', 'reload', 'restart'])
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
type TuiCommandName = 'quit' | 'reload' | 'restart'
|
|
26
|
+
|
|
27
|
+
function parseBareTuiCommand(text: string): TuiCommandName | null {
|
|
25
28
|
const parsed = parseCommand(text)
|
|
26
|
-
|
|
29
|
+
if (parsed === null || parsed.args.length > 0) return null
|
|
30
|
+
if (QUIT_COMMAND_NAMES.has(parsed.name)) return 'quit'
|
|
31
|
+
if (TUI_COMMAND_NAMES.has(parsed.name as TuiCommandName)) return parsed.name as TuiCommandName
|
|
32
|
+
return null
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
export type VersionMismatch = { expected: string; actual: string }
|
|
@@ -203,6 +209,25 @@ export function createTui({
|
|
|
203
209
|
updateQueuePanel(msg.pending)
|
|
204
210
|
break
|
|
205
211
|
}
|
|
212
|
+
case 'reload_result': {
|
|
213
|
+
for (const result of msg.results) {
|
|
214
|
+
const text = result.ok
|
|
215
|
+
? `${colors.green('●')} ${colors.bold(`[${result.scope}]`)} ${result.summary}`
|
|
216
|
+
: `${colors.red('●')} ${colors.bold(`[${result.scope}]`)} ${result.reason}`
|
|
217
|
+
appendHistory(new Text(text, 0, 0))
|
|
218
|
+
}
|
|
219
|
+
tui.requestRender()
|
|
220
|
+
break
|
|
221
|
+
}
|
|
222
|
+
case 'restart_result': {
|
|
223
|
+
const text =
|
|
224
|
+
msg.status === 'accepted'
|
|
225
|
+
? colors.green(colors.dim(msg.message ?? 'restart scheduled; reconnecting when the new container is up'))
|
|
226
|
+
: colors.red(`restart failed: ${msg.error ?? 'unknown error'}`)
|
|
227
|
+
appendHistory(new Text(text, 0, 0))
|
|
228
|
+
tui.requestRender()
|
|
229
|
+
break
|
|
230
|
+
}
|
|
206
231
|
}
|
|
207
232
|
})
|
|
208
233
|
|
|
@@ -222,6 +247,25 @@ export function createTui({
|
|
|
222
247
|
})
|
|
223
248
|
}
|
|
224
249
|
|
|
250
|
+
function runTuiCommand(command: TuiCommandName): boolean {
|
|
251
|
+
if (command === 'quit') {
|
|
252
|
+
shutdown(0)
|
|
253
|
+
return true
|
|
254
|
+
}
|
|
255
|
+
if (command === 'reload') {
|
|
256
|
+
client.send({ type: 'reload' })
|
|
257
|
+
appendHistory(new Text(colors.dim('reloading...'), 0, 0))
|
|
258
|
+
tui.requestRender()
|
|
259
|
+
return true
|
|
260
|
+
}
|
|
261
|
+
client.send({ type: 'restart' })
|
|
262
|
+
appendHistory(
|
|
263
|
+
new Text(colors.yellow(colors.dim('restart requested... reconnecting when the new container is up')), 0, 0),
|
|
264
|
+
)
|
|
265
|
+
tui.requestRender()
|
|
266
|
+
return true
|
|
267
|
+
}
|
|
268
|
+
|
|
225
269
|
// Esc aborts an in-flight reply. The Editor does not bind Esc, so a
|
|
226
270
|
// top-level input listener can intercept it without fighting the editor.
|
|
227
271
|
tui.addInputListener((data) => {
|
|
@@ -252,8 +296,13 @@ export function createTui({
|
|
|
252
296
|
|
|
253
297
|
editor.onSubmit = (text) => {
|
|
254
298
|
if (text.trim().length === 0) return
|
|
255
|
-
|
|
256
|
-
|
|
299
|
+
const command = parseBareTuiCommand(text)
|
|
300
|
+
if (command !== null) {
|
|
301
|
+
if (command !== 'quit') {
|
|
302
|
+
editor.setText('')
|
|
303
|
+
editor.addToHistory(text)
|
|
304
|
+
}
|
|
305
|
+
runTuiCommand(command)
|
|
257
306
|
return
|
|
258
307
|
}
|
|
259
308
|
editor.setText('')
|
|
@@ -275,13 +324,16 @@ export function createTui({
|
|
|
275
324
|
|
|
276
325
|
if (initialPrompt) {
|
|
277
326
|
// initialPrompt bypasses editor.onSubmit, so the quit intercept above
|
|
278
|
-
// would never run. Guard the same way so `typeclaw tui /quit` exits
|
|
279
|
-
//
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
327
|
+
// would never run. Guard the same way so `typeclaw tui /quit` exits —
|
|
328
|
+
// and `/reload` / `/restart` stay websocket control frames — instead of
|
|
329
|
+
// leaking the command into the agent's chat context.
|
|
330
|
+
const command = parseBareTuiCommand(initialPrompt)
|
|
331
|
+
if (command !== null) {
|
|
332
|
+
runTuiCommand(command)
|
|
333
|
+
if (command === 'quit') return { lostConnection: false }
|
|
334
|
+
} else {
|
|
335
|
+
await send(initialPrompt)
|
|
283
336
|
}
|
|
284
|
-
await send(initialPrompt)
|
|
285
337
|
}
|
|
286
338
|
|
|
287
339
|
const lostConnection = await closed
|