oira666_pi-watcher 0.0.4 → 0.0.6
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 +3 -5
- package/package.json +1 -1
- package/src/index.ts +1 -1
- package/src/runtime-args.ts +11 -1
- package/src/runtime-child.mjs +4 -0
- package/src/runtime-protocol.ts +2 -0
- package/src/runtime.ts +3 -0
- package/src/ui.ts +3 -3
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A persistent side-agent conversation for your running [Pi](https://pi.dev) agent.
|
|
4
4
|
|
|
5
|
-
**Package:** `oira666_pi-watcher` · **Version:** `0.0.
|
|
5
|
+
**Package:** `oira666_pi-watcher` · **Version:** `0.0.6` · **License:** MIT
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -23,13 +23,11 @@ pi -e ./src/index.ts
|
|
|
23
23
|
|
|
24
24
|
| Command | Action |
|
|
25
25
|
| --- | --- |
|
|
26
|
-
| **
|
|
26
|
+
| **Alt+W** | Toggle the Watcher panel without sending a prompt or stopping either agent. |
|
|
27
27
|
| `/watcher` | Create/resume the side agent and show its conversation. **Sends no prompt.** |
|
|
28
28
|
| `/watcher Why is it retrying?` | Open and send exactly that question. |
|
|
29
29
|
| `/watcher model` | Open the side agent's model picker. Does not prompt either agent. |
|
|
30
30
|
|
|
31
|
-
Ctrl+W replaces Pi's usual delete-word-left shortcut while Watcher is loaded.
|
|
32
|
-
|
|
33
31
|
These are human-facing TUI controls, not tools exposed to the main agent. They work while the main agent is running, including during compaction.
|
|
34
32
|
|
|
35
33
|
The panel opens immediately while the side process initializes its inherited extensions and MCPs in the background. You can type straight away; a submitted question waits for initialization and is sent once ready. `/stop` cancels a question waiting for startup. Opening without a question still sends no prompt. Saved history and the selected model appear when initialization finishes; reopening an already initialized side agent reuses it.
|
|
@@ -37,7 +35,7 @@ The panel opens immediately while the side process initializes its inherited ext
|
|
|
37
35
|
Inside the overlay:
|
|
38
36
|
|
|
39
37
|
- Type a question and press **Enter**. Use **Shift+Enter** for a newline.
|
|
40
|
-
- **
|
|
38
|
+
- **Alt+W** closes the panel from any Watcher view. Press it again to reopen. **Esc** also returns from chat to the main view without stopping either agent. Conversation, draft, and scroll position are retained.
|
|
41
39
|
- `/model` opens a searchable provider/model picker. Its selection affects **only the side agent** and is persisted in that side conversation. You can change models while the side agent is running, just like in Pi; switching does not abort the current reply.
|
|
42
40
|
- `/stop` cancels only the side agent and clears its queued messages.
|
|
43
41
|
- `/copy` copies to the system clipboard, because terminal mouse selection spans the full terminal width. Messages are numbered in the transcript (`You #n`, `Watcher #n`):
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -269,7 +269,7 @@ export default function watcher(pi: ExtensionAPI): void {
|
|
|
269
269
|
pi.on("session_compact", () => { compacting = false; mainStatus(); });
|
|
270
270
|
pi.on("session_compact_failed", () => { compacting = false; mainStatus(); });
|
|
271
271
|
|
|
272
|
-
pi.registerShortcut("
|
|
272
|
+
pi.registerShortcut("alt+w", {
|
|
273
273
|
description: "Toggle the Watcher panel",
|
|
274
274
|
handler: async (ctx) => {
|
|
275
275
|
if (ctx.mode !== "tui" || !ctx.hasUI) return;
|
package/src/runtime-args.ts
CHANGED
|
@@ -103,6 +103,8 @@ export interface PiLocation {
|
|
|
103
103
|
packageDir: string;
|
|
104
104
|
/** Module exporting `main` and `DefaultPackageManager` (bundle index, else unbundled index). */
|
|
105
105
|
entry: string;
|
|
106
|
+
/** Executable Pi CLI for extensions that spawn process.argv[1]. */
|
|
107
|
+
cliEntry: string;
|
|
106
108
|
docsDir?: string;
|
|
107
109
|
}
|
|
108
110
|
|
|
@@ -138,8 +140,16 @@ export function locatePi(options: { piPackageDir?: string; argv?: readonly strin
|
|
|
138
140
|
existsSync(f),
|
|
139
141
|
);
|
|
140
142
|
if (!entry) continue;
|
|
143
|
+
const bin = readJson(join(packageDir, "package.json"))?.bin;
|
|
144
|
+
const piBin = typeof bin === "string" ? bin : (bin as Record<string, unknown> | undefined)?.pi;
|
|
145
|
+
const cliEntry = [
|
|
146
|
+
...(typeof piBin === "string" ? [resolve(packageDir, piBin)] : []),
|
|
147
|
+
join(packageDir, "dist", "bundle", "cli.js"),
|
|
148
|
+
join(packageDir, "dist", "cli.js"),
|
|
149
|
+
].find((f) => existsSync(f));
|
|
150
|
+
if (!cliEntry) continue;
|
|
141
151
|
const docsDir = join(packageDir, "docs");
|
|
142
|
-
return { packageDir, entry, docsDir: existsSync(docsDir) ? docsDir : undefined };
|
|
152
|
+
return { packageDir, entry, cliEntry, docsDir: existsSync(docsDir) ? docsDir : undefined };
|
|
143
153
|
}
|
|
144
154
|
throw new Error(
|
|
145
155
|
`Cannot locate the ${PI_PACKAGE_NAME} package (tried: ${candidates.join(", ") || "none"}). Pass piPackageDir or set PI_PACKAGE_DIR.`,
|
package/src/runtime-child.mjs
CHANGED
|
@@ -42,6 +42,10 @@ async function run() {
|
|
|
42
42
|
setTimeout(() => process.exit(0), 3000).unref();
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
+
// Inherited extensions launch subagents using execPath + argv[1]. Expose the real
|
|
46
|
+
// CLI before importing Pi or loading extensions, not this IPC-only bootstrap.
|
|
47
|
+
process.argv[1] = config.piCliEntry;
|
|
48
|
+
|
|
45
49
|
const pi = await import(pathToFileURL(config.piEntry).href);
|
|
46
50
|
const { DefaultPackageManager, main } = pi;
|
|
47
51
|
if (typeof main !== "function") throw new Error(`pi entry ${config.piEntry} does not export main()`);
|
package/src/runtime-protocol.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface ChildConfig {
|
|
|
26
26
|
version: 1;
|
|
27
27
|
/** Absolute path of the pi module exporting `main` and `DefaultPackageManager` (bundle/index.js). */
|
|
28
28
|
piEntry: string;
|
|
29
|
+
/** Executable Pi CLI exposed as process.argv[1] in the child. */
|
|
30
|
+
piCliEntry: string;
|
|
29
31
|
cwd: string;
|
|
30
32
|
mainSessionId: string;
|
|
31
33
|
mainSessionFile: string;
|
package/src/runtime.ts
CHANGED
|
@@ -403,6 +403,7 @@ interface Resolved {
|
|
|
403
403
|
childEntry: string;
|
|
404
404
|
piPackageDir: string;
|
|
405
405
|
piEntry: string;
|
|
406
|
+
piCliEntry: string;
|
|
406
407
|
piDocsDir?: string;
|
|
407
408
|
nodePath: string;
|
|
408
409
|
baseArgs: string[];
|
|
@@ -787,6 +788,7 @@ export class WatcherRuntime {
|
|
|
787
788
|
childEntry,
|
|
788
789
|
piPackageDir: pi.packageDir,
|
|
789
790
|
piEntry: pi.entry,
|
|
791
|
+
piCliEntry: pi.cliEntry,
|
|
790
792
|
piDocsDir: pi.docsDir,
|
|
791
793
|
nodePath,
|
|
792
794
|
baseArgs: ["--session-dir", sessionDir, "--session-id", sideSessionId, "--extension", bridgePath],
|
|
@@ -804,6 +806,7 @@ export class WatcherRuntime {
|
|
|
804
806
|
const config: ChildConfig = {
|
|
805
807
|
version: 1,
|
|
806
808
|
piEntry: resolved.piEntry,
|
|
809
|
+
piCliEntry: resolved.piCliEntry,
|
|
807
810
|
cwd: resolved.cwd,
|
|
808
811
|
mainSessionId: options.mainSessionId,
|
|
809
812
|
mainSessionFile: resolved.mainSessionFile,
|
package/src/ui.ts
CHANGED
|
@@ -314,7 +314,7 @@ export class WatcherOverlay implements Component, Focusable {
|
|
|
314
314
|
handleInput(data: string): void {
|
|
315
315
|
if (this.closed) return;
|
|
316
316
|
// Focused overlays own input, so the main editor's shortcut cannot close us.
|
|
317
|
-
if (matchesKey(data, "
|
|
317
|
+
if (matchesKey(data, "alt+w")) {
|
|
318
318
|
this.close("closed");
|
|
319
319
|
return;
|
|
320
320
|
}
|
|
@@ -397,7 +397,7 @@ export class WatcherOverlay implements Component, Focusable {
|
|
|
397
397
|
if (command === "model") this.openPicker(args.trim());
|
|
398
398
|
else if (command === "stop") this.doStop();
|
|
399
399
|
else if (command === "copy") this.doCopy(args);
|
|
400
|
-
else this.setNotice(`Enter send · Shift+Enter newline · PgUp/PgDn scroll · /model [filter] · /stop · ${COPY_USAGE} ·
|
|
400
|
+
else this.setNotice(`Enter send · Shift+Enter newline · PgUp/PgDn scroll · /model [filter] · /stop · ${COPY_USAGE} · Alt+W / Esc close (main agent untouched)`);
|
|
401
401
|
}
|
|
402
402
|
|
|
403
403
|
/** Allow local commands while an approval owns the normal input controls. */
|
|
@@ -745,7 +745,7 @@ export class WatcherOverlay implements Component, Focusable {
|
|
|
745
745
|
else if (inner >= 76) txt = "Enter send · ⇧Enter newline · PgUp/PgDn scroll · /model · /copy · /stop · Esc close";
|
|
746
746
|
else if (inner >= 50) txt = "Enter send · PgUp/PgDn · /model · /copy · /stop · Esc";
|
|
747
747
|
else txt = "Enter send · Esc close";
|
|
748
|
-
txt = `
|
|
748
|
+
txt = `Alt+W close · ${txt}`;
|
|
749
749
|
return th.fg("dim", ` ${txt}`);
|
|
750
750
|
}
|
|
751
751
|
|