@gavin-hu/my-pi-agent 0.14.0 → 0.15.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/README.md +55 -33
- package/README.zh-CN.md +44 -27
- package/extensions/bytefly/README.md +193 -140
- package/extensions/bytefly/block/component.ts +7 -15
- package/extensions/bytefly/block/register.ts +6 -1
- package/extensions/bytefly/doctor/register.ts +24 -5
- package/extensions/bytefly/doctor/tools.ts +4 -0
- package/extensions/bytefly/footer/collect/snapshot.ts +6 -1
- package/extensions/bytefly/footer/config.ts +10 -6
- package/extensions/bytefly/footer/price/price.ts +5 -3
- package/extensions/bytefly/footer/price/rates.ts +63 -26
- package/extensions/bytefly/footer/render/status.ts +3 -3
- package/extensions/bytefly/header/component.ts +3 -2
- package/extensions/bytefly/index.ts +3 -3
- package/extensions/bytefly/notice/component.ts +8 -3
- package/extensions/bytefly/notify/config.ts +6 -11
- package/extensions/bytefly/notify/dispatch.ts +13 -5
- package/extensions/bytefly/panel/commands.ts +21 -1
- package/extensions/bytefly/panel/format.ts +5 -2
- package/extensions/bytefly/panel/settings-runtime.ts +26 -6
- package/extensions/bytefly/panel/settings.ts +41 -4
- package/extensions/bytefly/panel/themes.ts +32 -0
- package/extensions/bytefly/panel/tui/editing.ts +77 -0
- package/extensions/bytefly/panel/tui/entries.ts +24 -0
- package/extensions/bytefly/panel/tui/extensions-view.ts +13 -8
- package/extensions/bytefly/panel/tui/frame.ts +121 -4
- package/extensions/bytefly/panel/tui/settings-view.ts +54 -15
- package/extensions/bytefly/panel/tui.ts +94 -152
- package/extensions/bytefly/picker/discovery.ts +1 -0
- package/extensions/bytefly/picker/types.ts +2 -0
- package/extensions/bytefly/separator/component.ts +14 -4
- package/extensions/bytefly/update/cache.ts +58 -19
- package/extensions/bytefly/update/check.ts +35 -0
- package/extensions/bytefly/update/format.ts +76 -35
- package/extensions/bytefly/update/register.ts +144 -55
- package/extensions/bytefly/update/state.ts +4 -2
- package/extensions/bytefly/update/suppress.ts +66 -0
- package/extensions/bytefly/update/types.ts +15 -0
- package/extensions/check/README.md +253 -0
- package/extensions/check/commands.ts +128 -0
- package/extensions/check/config.ts +39 -0
- package/extensions/check/detect.ts +269 -0
- package/extensions/check/failures.ts +230 -0
- package/extensions/check/format.ts +135 -0
- package/extensions/check/index.ts +26 -0
- package/extensions/check/render.ts +185 -0
- package/extensions/check/runner.ts +176 -0
- package/extensions/check/runtime.ts +244 -0
- package/extensions/check/source.ts +80 -0
- package/extensions/check/tools.ts +173 -0
- package/extensions/check/tui.ts +195 -0
- package/extensions/check/types.ts +99 -0
- package/extensions/document/README.md +1 -1
- package/extensions/document/extract/pdf.ts +8 -16
- package/extensions/document/formats.ts +2 -2
- package/extensions/file-browse/README.md +66 -32
- package/extensions/file-browse/client.ts +95 -32
- package/extensions/file-browse/config.ts +9 -9
- package/extensions/file-browse/git.ts +37 -47
- package/extensions/file-browse/html.ts +39 -58
- package/extensions/file-browse/markdown.ts +110 -92
- package/extensions/file-browse/raw-html.ts +478 -0
- package/extensions/file-browse/router.ts +32 -70
- package/extensions/file-browse/server.ts +18 -56
- package/extensions/file-browse/urls.ts +80 -0
- package/extensions/goal/README.md +103 -47
- package/extensions/goal/clarify-parse.ts +4 -9
- package/extensions/goal/clarify.ts +27 -4
- package/extensions/goal/commands.ts +24 -11
- package/extensions/goal/format.ts +41 -10
- package/extensions/goal/index.ts +39 -16
- package/extensions/goal/plan-route.ts +51 -0
- package/extensions/goal/schema.ts +6 -23
- package/extensions/goal/state.ts +39 -57
- package/extensions/goal/tools.ts +26 -20
- package/extensions/goal/types.ts +11 -1
- package/extensions/job/README.md +20 -6
- package/extensions/job/format.ts +18 -68
- package/extensions/job/index.ts +26 -41
- package/extensions/job/log-writer.ts +208 -0
- package/extensions/job/logs.ts +3 -2
- package/extensions/job/process.ts +6 -99
- package/extensions/job/runtime.ts +42 -181
- package/extensions/job/schema.ts +1 -4
- package/extensions/job/tools.ts +1 -15
- package/extensions/job/tui.ts +117 -307
- package/extensions/job/ui.ts +7 -33
- package/extensions/memory/README.md +1 -1
- package/extensions/memory/errors.ts +5 -6
- package/extensions/memory/tui.ts +14 -24
- package/extensions/plan/README.md +94 -18
- package/extensions/plan/commands.ts +9 -5
- package/extensions/plan/format.ts +87 -0
- package/extensions/plan/index.ts +31 -15
- package/extensions/plan/plans.ts +2 -3
- package/extensions/plan/policy.ts +0 -6
- package/extensions/plan/runtime.ts +4 -2
- package/extensions/plan/steps.ts +37 -8
- package/extensions/plan/todos.ts +44 -3
- package/extensions/plan/tools/enter.ts +94 -0
- package/extensions/plan/tools/exit.ts +296 -0
- package/extensions/plan/tools/nested.ts +21 -0
- package/extensions/plan/tools/steps.ts +136 -0
- package/extensions/plan/tools/todo-seed.ts +76 -0
- package/extensions/plan/tools/write.ts +127 -0
- package/extensions/plan/tools.ts +18 -703
- package/extensions/plan/web-client.ts +6 -22
- package/extensions/plan/web-page.ts +19 -30
- package/extensions/plan/web-router.ts +34 -71
- package/extensions/plan/web.ts +13 -40
- package/extensions/question/answers.ts +3 -8
- package/extensions/question/tui.ts +6 -6
- package/extensions/rewind/README.md +95 -22
- package/extensions/rewind/checklist.ts +185 -0
- package/extensions/rewind/commands.ts +8 -0
- package/extensions/rewind/config.ts +3 -11
- package/extensions/rewind/format.ts +220 -2
- package/extensions/rewind/git.ts +74 -7
- package/extensions/rewind/hunk-picker.ts +114 -0
- package/extensions/rewind/patch.ts +88 -0
- package/extensions/rewind/path-picker.ts +67 -100
- package/extensions/rewind/paths.ts +93 -19
- package/extensions/rewind/restore.ts +53 -10
- package/extensions/rewind/review-tui.ts +320 -0
- package/extensions/rewind/review.ts +397 -0
- package/extensions/rewind/rewind-tui.ts +4 -3
- package/extensions/rewind/runtime.ts +94 -17
- package/extensions/subagent/README.md +61 -6
- package/extensions/subagent/background-tui.ts +208 -0
- package/extensions/subagent/background.ts +470 -0
- package/extensions/subagent/commands.ts +80 -0
- package/extensions/subagent/config.ts +4 -0
- package/extensions/subagent/format.ts +24 -1
- package/extensions/subagent/index.ts +91 -2
- package/extensions/subagent/render.ts +13 -0
- package/extensions/subagent/run.ts +17 -16
- package/extensions/subagent/schema.ts +12 -0
- package/extensions/subagent/types.ts +11 -0
- package/extensions/subagent/ui.ts +50 -0
- package/extensions/todo/README.md +18 -13
- package/extensions/todo/format.ts +3 -2
- package/extensions/todo/runtime.ts +3 -2
- package/extensions/todo/schema.ts +8 -84
- package/extensions/todo/state.ts +5 -46
- package/extensions/todo/tools.ts +2 -1
- package/extensions/todo/types.ts +6 -15
- package/extensions/watch/README.md +14 -6
- package/extensions/watch/child.ts +150 -0
- package/extensions/watch/format.ts +8 -48
- package/extensions/watch/index.ts +42 -58
- package/extensions/watch/runtime.ts +49 -128
- package/extensions/watch/schema.ts +1 -4
- package/extensions/watch/tools.ts +1 -11
- package/extensions/watch/tui.ts +103 -273
- package/extensions/watch/ui.ts +5 -32
- package/extensions/web-access/README.md +1 -1
- package/extensions/web-access/fetch/page.ts +1 -1
- package/extensions/web-access/fetch/tool.ts +6 -2
- package/extensions/web-access/search/search.ts +2 -5
- package/extensions/wechat/README.md +1 -1
- package/extensions/wechat/client.ts +23 -0
- package/extensions/wechat/commands.ts +57 -42
- package/extensions/wechat/deliver.ts +5 -9
- package/extensions/wechat/errors.ts +3 -6
- package/extensions/wechat/inbound.ts +2 -5
- package/extensions/wechat/index.ts +2 -8
- package/extensions/wechat/outbound.ts +2 -4
- package/extensions/wechat/peer-worker.ts +3 -8
- package/extensions/wechat/poll.ts +10 -9
- package/extensions/wechat/presence.ts +35 -62
- package/extensions/wechat/reply-pipeline.ts +3 -1
- package/extensions/wechat/status-screen.ts +1 -1
- package/extensions/wechat/timer.ts +57 -0
- package/extensions/worktree/config.ts +8 -20
- package/extensions/worktree/enter.ts +7 -12
- package/extensions/worktree/git.ts +38 -69
- package/extensions/worktree/index.ts +5 -4
- package/extensions/worktree/registry.ts +7 -7
- package/extensions/worktree/root-tools.ts +2 -2
- package/extensions/worktree/status.ts +3 -2
- package/lib/README.md +22 -8
- package/lib/config.ts +50 -3
- package/lib/errors.ts +12 -0
- package/lib/format.ts +50 -0
- package/lib/git/read.ts +8 -0
- package/lib/goal-state.ts +23 -8
- package/lib/http/headers.ts +54 -0
- package/lib/http/index.ts +13 -0
- package/lib/http/response.ts +46 -0
- package/lib/http/router.ts +52 -0
- package/lib/http/server.ts +91 -0
- package/lib/lines.ts +77 -0
- package/lib/pdf.ts +82 -0
- package/lib/plan-entry.ts +71 -0
- package/lib/plan-state.ts +34 -0
- package/lib/report.ts +99 -0
- package/lib/requirements.ts +25 -19
- package/lib/spawn.ts +170 -0
- package/lib/todo-state.ts +110 -23
- package/lib/tool-names.ts +7 -0
- package/lib/tool-result.ts +33 -0
- package/lib/tui/README.md +68 -3
- package/lib/tui/chrome.ts +28 -4
- package/lib/tui/columns.ts +6 -1
- package/lib/tui/confirm.ts +65 -0
- package/lib/tui/dock-screen.ts +410 -0
- package/lib/tui/glyphs.ts +2 -1
- package/lib/tui/indent.ts +1 -1
- package/lib/tui/index.ts +2 -0
- package/lib/tui/rows.ts +21 -0
- package/lib/tui/status-controller.ts +35 -1
- package/lib/tui/tool.ts +59 -1
- package/lib/web/components.ts +20 -1
- package/lib/web/markup.ts +32 -0
- package/lib/web/runtime.ts +50 -0
- package/package.json +22 -5
- package/skills/init/SKILL.md +165 -0
- package/skills/init/references/agents-md-quality.md +103 -0
- package/skills/init/references/interview-questions.md +81 -0
- package/skills/refactor/SKILL.md +141 -0
- package/extensions/watch/process.ts +0 -63
- package/extensions/web-access/fetch/pdf.ts +0 -45
|
@@ -12,17 +12,18 @@ import { closeSync, openSync, readSync, statSync } from "node:fs";
|
|
|
12
12
|
import { isAbsolute, resolve } from "node:path";
|
|
13
13
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
14
14
|
import { resolveEffectiveCwd } from "../../lib/env.ts";
|
|
15
|
+
import { LineSplitter } from "../../lib/lines.ts";
|
|
16
|
+
import type { KillTreeFn, SpawnFn, SpawnedProcess } from "../../lib/spawn.ts";
|
|
17
|
+
import { createChildSupervisor } from "./child.ts";
|
|
15
18
|
import { DEFAULT_CONFIG, loadConfig, type WatchConfig } from "./config.ts";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
19
|
+
import { sanitizeUntrustedLine } from "../../lib/format.ts";
|
|
20
|
+
import { compileRegex, sourceLabel } from "./format.ts";
|
|
18
21
|
import { createUiController, type UiController } from "./ui.ts";
|
|
19
22
|
import type { PendingMatch, WatchMatch, WatchRecord, WatchSource } from "./types.ts";
|
|
20
23
|
|
|
21
24
|
/** Largest slice read from a file per tick. */
|
|
22
25
|
const MAX_CHUNK = 1024 * 1024;
|
|
23
|
-
/**
|
|
24
|
-
const MAX_LINE_BYTES = 1024 * 1024;
|
|
25
|
-
/** Grace period before a stopped command is escalated to SIGKILL. */
|
|
26
|
+
/** Grace before a stopped command escalates to SIGKILL; `child.ts` defaults the same. */
|
|
26
27
|
const KILL_GRACE_MS = 2000;
|
|
27
28
|
|
|
28
29
|
/** Internal watcher state: the public record plus non-serializable fields. */
|
|
@@ -48,7 +49,7 @@ interface Internal {
|
|
|
48
49
|
regex: RegExp;
|
|
49
50
|
excludes: RegExp[];
|
|
50
51
|
offset: number;
|
|
51
|
-
|
|
52
|
+
lines: LineSplitter;
|
|
52
53
|
rawLines: string[];
|
|
53
54
|
matches: WatchMatch[];
|
|
54
55
|
child?: SpawnedProcess;
|
|
@@ -103,8 +104,11 @@ export interface WatchRuntimeOptions {
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
export function createWatchRuntime(options: WatchRuntimeOptions = {}): WatchRuntime {
|
|
106
|
-
const
|
|
107
|
-
|
|
107
|
+
const children = createChildSupervisor({
|
|
108
|
+
spawn: options.spawn,
|
|
109
|
+
killTree: options.killTree,
|
|
110
|
+
graceMs: options.killGraceMs,
|
|
111
|
+
});
|
|
108
112
|
const now = options.now ?? (() => Date.now());
|
|
109
113
|
const fixedConfig = options.config;
|
|
110
114
|
const autoClock = options.autoClock ?? true;
|
|
@@ -208,7 +212,7 @@ export function createWatchRuntime(options: WatchRuntimeOptions = {}): WatchRunt
|
|
|
208
212
|
for (const exclude of w.excludes) if (exclude.test(line)) return false;
|
|
209
213
|
if (!w.regex.test(line)) return false;
|
|
210
214
|
|
|
211
|
-
const clean =
|
|
215
|
+
const clean = sanitizeUntrustedLine(line);
|
|
212
216
|
w.matches.push({ seq: ++w.seq, line: clean, at: time, seen: false });
|
|
213
217
|
if (w.matches.length > config.maxMatches) {
|
|
214
218
|
w.matches.splice(0, w.matches.length - config.maxMatches);
|
|
@@ -224,85 +228,16 @@ export function createWatchRuntime(options: WatchRuntimeOptions = {}): WatchRunt
|
|
|
224
228
|
};
|
|
225
229
|
|
|
226
230
|
const consume = (w: Internal, chunk: Buffer, time: number): void => {
|
|
227
|
-
const combined = w.pending.length > 0 ? Buffer.concat([w.pending, chunk]) : chunk;
|
|
228
|
-
let start = 0;
|
|
229
231
|
let matched = false;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (newline < 0) break;
|
|
233
|
-
if (emit(w, combined.subarray(start, newline).toString("utf-8"), time)) matched = true;
|
|
234
|
-
start = newline + 1;
|
|
235
|
-
}
|
|
236
|
-
let rest = combined.subarray(start);
|
|
237
|
-
if (rest.length > MAX_LINE_BYTES) {
|
|
238
|
-
if (emit(w, rest.toString("utf-8"), time)) matched = true;
|
|
239
|
-
rest = Buffer.alloc(0);
|
|
232
|
+
for (const line of w.lines.push(chunk)) {
|
|
233
|
+
if (emit(w, line, time)) matched = true;
|
|
240
234
|
}
|
|
241
|
-
w.pending = Buffer.from(rest);
|
|
242
235
|
if (matched && w.wake) fireOnMatch();
|
|
243
236
|
};
|
|
244
237
|
|
|
238
|
+
/** A source that ended mid-line still reports that line. */
|
|
245
239
|
const flushPending = (w: Internal, time: number): void => {
|
|
246
|
-
|
|
247
|
-
const rest = w.pending.toString("utf-8");
|
|
248
|
-
w.pending = Buffer.alloc(0);
|
|
249
|
-
emit(w, rest, time);
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
const clearKillTimer = (w: Internal): void => {
|
|
253
|
-
if (w.killTimer) {
|
|
254
|
-
clearTimeout(w.killTimer);
|
|
255
|
-
w.killTimer = undefined;
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
const killChild = (w: Internal, signal: "SIGTERM" | "SIGKILL"): void => {
|
|
260
|
-
const pid = w.pid;
|
|
261
|
-
try {
|
|
262
|
-
if (pid !== undefined) killTree(pid, signal, signal === "SIGKILL");
|
|
263
|
-
else w.child?.kill(signal);
|
|
264
|
-
} catch {
|
|
265
|
-
// Best-effort: the child may already be gone.
|
|
266
|
-
}
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
/** SIGTERM a command, escalating to SIGKILL when it has not closed in time. */
|
|
270
|
-
const terminateChild = (w: Internal): void => {
|
|
271
|
-
clearKillTimer(w);
|
|
272
|
-
killChild(w, "SIGTERM");
|
|
273
|
-
if (w.pid === undefined && !w.child) return;
|
|
274
|
-
w.killTimer = setTimeout(() => {
|
|
275
|
-
w.killTimer = undefined;
|
|
276
|
-
if (w.child) killChild(w, "SIGKILL");
|
|
277
|
-
}, killGraceMs);
|
|
278
|
-
w.killTimer.unref?.();
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
/** Resolve when every command has closed, or after `ms`, whichever comes first. */
|
|
282
|
-
const waitForChildren = (watches: Internal[], ms: number): Promise<void> => {
|
|
283
|
-
const alive = watches.filter((w) => w.child !== undefined);
|
|
284
|
-
if (alive.length === 0) return Promise.resolve();
|
|
285
|
-
return new Promise((resolve) => {
|
|
286
|
-
let remaining = alive.length;
|
|
287
|
-
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
288
|
-
const settle = (): void => {
|
|
289
|
-
if (--remaining > 0) return;
|
|
290
|
-
if (timer !== undefined) clearTimeout(timer);
|
|
291
|
-
resolve();
|
|
292
|
-
};
|
|
293
|
-
for (const w of alive) {
|
|
294
|
-
let done = false;
|
|
295
|
-
const once = (): void => {
|
|
296
|
-
if (done) return;
|
|
297
|
-
done = true;
|
|
298
|
-
settle();
|
|
299
|
-
};
|
|
300
|
-
w.child?.on("close", once);
|
|
301
|
-
w.child?.on("error", once);
|
|
302
|
-
}
|
|
303
|
-
timer = setTimeout(resolve, ms);
|
|
304
|
-
timer.unref?.();
|
|
305
|
-
});
|
|
240
|
+
for (const line of w.lines.flush()) emit(w, line, time);
|
|
306
241
|
};
|
|
307
242
|
|
|
308
243
|
const pollFile = (w: Internal, time: number): void => {
|
|
@@ -317,7 +252,7 @@ export function createWatchRuntime(options: WatchRuntimeOptions = {}): WatchRunt
|
|
|
317
252
|
}
|
|
318
253
|
if (size < w.offset) {
|
|
319
254
|
w.offset = 0;
|
|
320
|
-
w.
|
|
255
|
+
w.lines.reset();
|
|
321
256
|
}
|
|
322
257
|
if (w.offset >= size) return;
|
|
323
258
|
|
|
@@ -344,42 +279,28 @@ export function createWatchRuntime(options: WatchRuntimeOptions = {}): WatchRunt
|
|
|
344
279
|
};
|
|
345
280
|
|
|
346
281
|
const startCommand = (w: Internal): void => {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
if (w.status !== "running") return;
|
|
365
|
-
flushPending(w, now());
|
|
366
|
-
w.status = "exited";
|
|
367
|
-
w.exitCode = code;
|
|
368
|
-
w.signal = signal;
|
|
369
|
-
w.endedAt = now();
|
|
370
|
-
ui.paint();
|
|
371
|
-
if (runningCount() === 0) stopClock();
|
|
372
|
-
});
|
|
373
|
-
child.on("error", (error) => {
|
|
374
|
-
clearKillTimer(w);
|
|
375
|
-
w.child = undefined;
|
|
376
|
-
if (w.status !== "running") return;
|
|
377
|
-
w.status = "error";
|
|
378
|
-
w.error = error.message;
|
|
379
|
-
w.endedAt = now();
|
|
380
|
-
ui.paint();
|
|
381
|
-
if (runningCount() === 0) stopClock();
|
|
282
|
+
const error = children.start(w, w.source.command ?? "", w.source.cwd ?? process.cwd(), {
|
|
283
|
+
onData: (chunk) => consume(w, chunk, now()),
|
|
284
|
+
onSettled: (outcome) => {
|
|
285
|
+
if (w.status !== "running") return;
|
|
286
|
+
if (outcome.kind === "closed") {
|
|
287
|
+
flushPending(w, now());
|
|
288
|
+
w.status = "exited";
|
|
289
|
+
w.exitCode = outcome.code;
|
|
290
|
+
w.signal = outcome.signal;
|
|
291
|
+
} else {
|
|
292
|
+
w.status = "error";
|
|
293
|
+
w.error = outcome.message;
|
|
294
|
+
}
|
|
295
|
+
w.endedAt = now();
|
|
296
|
+
ui.paint();
|
|
297
|
+
if (runningCount() === 0) stopClock();
|
|
298
|
+
},
|
|
382
299
|
});
|
|
300
|
+
if (error === undefined) return;
|
|
301
|
+
w.status = "error";
|
|
302
|
+
w.error = error;
|
|
303
|
+
w.endedAt = now();
|
|
383
304
|
};
|
|
384
305
|
|
|
385
306
|
const tick = (): void => {
|
|
@@ -443,7 +364,7 @@ export function createWatchRuntime(options: WatchRuntimeOptions = {}): WatchRunt
|
|
|
443
364
|
regex,
|
|
444
365
|
excludes,
|
|
445
366
|
offset,
|
|
446
|
-
|
|
367
|
+
lines: new LineSplitter(),
|
|
447
368
|
rawLines: [],
|
|
448
369
|
matches: [],
|
|
449
370
|
notified: false,
|
|
@@ -476,13 +397,13 @@ export function createWatchRuntime(options: WatchRuntimeOptions = {}): WatchRunt
|
|
|
476
397
|
|
|
477
398
|
const matches = (id: string, lines?: number) => tail(id, lines, (w) => w.matches.map((m) => m.line));
|
|
478
399
|
const logs = (id: string, lines?: number) =>
|
|
479
|
-
tail(id, lines, (w) => w.rawLines.map((line) =>
|
|
400
|
+
tail(id, lines, (w) => w.rawLines.map((line) => sanitizeUntrustedLine(line)));
|
|
480
401
|
|
|
481
402
|
const stop = (id: string): boolean => {
|
|
482
403
|
const w = watches.get(id);
|
|
483
404
|
if (!w || w.status !== "running") return false;
|
|
484
405
|
flushPending(w, now());
|
|
485
|
-
|
|
406
|
+
children.terminate(w);
|
|
486
407
|
w.status = "stopped";
|
|
487
408
|
w.endedAt = now();
|
|
488
409
|
ui.paint();
|
|
@@ -535,8 +456,8 @@ export function createWatchRuntime(options: WatchRuntimeOptions = {}): WatchRunt
|
|
|
535
456
|
for (const w of watches.values()) {
|
|
536
457
|
if (w.status !== "running") continue;
|
|
537
458
|
w.status = "stopped";
|
|
538
|
-
|
|
539
|
-
|
|
459
|
+
children.clearTimer(w);
|
|
460
|
+
children.kill(w, "SIGKILL");
|
|
540
461
|
w.child = undefined;
|
|
541
462
|
}
|
|
542
463
|
watches.clear();
|
|
@@ -554,13 +475,13 @@ export function createWatchRuntime(options: WatchRuntimeOptions = {}): WatchRunt
|
|
|
554
475
|
flushPending(w, now());
|
|
555
476
|
w.status = "stopped";
|
|
556
477
|
w.endedAt = now();
|
|
557
|
-
|
|
558
|
-
|
|
478
|
+
children.clearTimer(w);
|
|
479
|
+
children.kill(w, "SIGTERM");
|
|
559
480
|
}
|
|
560
|
-
await
|
|
481
|
+
await children.wait(running, killGraceMs);
|
|
561
482
|
for (const w of running) {
|
|
562
|
-
|
|
563
|
-
if (w.child)
|
|
483
|
+
children.clearTimer(w);
|
|
484
|
+
if (w.child) children.kill(w, "SIGKILL");
|
|
564
485
|
}
|
|
565
486
|
watches.clear();
|
|
566
487
|
ui.detach();
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
11
11
|
import { Type, type Static } from "typebox";
|
|
12
|
+
import { optionalString } from "../../lib/config.ts";
|
|
12
13
|
import { WATCH_ACTIONS, type WatchAction } from "./types.ts";
|
|
13
14
|
|
|
14
15
|
/** Longest accepted label. */
|
|
@@ -79,10 +80,6 @@ export type WatchCall =
|
|
|
79
80
|
| { action: "stop"; id: string }
|
|
80
81
|
| { action: "clear"; id?: string; all?: boolean };
|
|
81
82
|
|
|
82
|
-
function optionalString(value: unknown): string | undefined {
|
|
83
|
-
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
83
|
function assertRegex(value: string, field: string): void {
|
|
87
84
|
if (value.length > MAX_PATTERN) throw new Error(`${field} is longer than ${MAX_PATTERN} characters.`);
|
|
88
85
|
try {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
11
11
|
import { sanitizeLine } from "../../lib/format.ts";
|
|
12
|
+
import { errorResult } from "../../lib/tool-result.ts";
|
|
12
13
|
import { reuseText, toolDetails } from "../../lib/tui/chrome.ts";
|
|
13
14
|
import { rejectionLine, reuseCallText, toolCallTitle } from "../../lib/tui/tool.ts";
|
|
14
15
|
import { SEPARATORS } from "../../lib/tui/glyphs.ts";
|
|
@@ -32,17 +33,6 @@ export const TOOL_NAME = "watch";
|
|
|
32
33
|
/** Collapsed transcript rows before the result is truncated. */
|
|
33
34
|
const RESULT_ROWS = 8;
|
|
34
35
|
|
|
35
|
-
function errorResult(
|
|
36
|
-
action: WatchDetails["action"],
|
|
37
|
-
message: string,
|
|
38
|
-
): { content: Array<{ type: "text"; text: string }>; details: WatchDetails; isError: true } {
|
|
39
|
-
return {
|
|
40
|
-
content: [{ type: "text", text: `Error: ${message}` }],
|
|
41
|
-
details: { action, error: message },
|
|
42
|
-
isError: true,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
36
|
const GUIDELINES = [
|
|
47
37
|
"Use `watch add` to tail a log file or a long-running command and surface lines matching a pattern.",
|
|
48
38
|
"Read matches with `watch matches <id>`; use `watch logs <id>` for the raw output tail.",
|