pi-interactive-shell 0.9.0 → 0.10.1
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/CHANGELOG.md +32 -0
- package/README.md +21 -13
- package/SKILL.md +3 -3
- package/background-widget.ts +76 -0
- package/examples/prompts/codex-implement-plan.md +11 -3
- package/examples/prompts/codex-review-impl.md +11 -3
- package/examples/prompts/codex-review-plan.md +11 -3
- package/examples/skills/{codex-5.3-prompting → codex-5-3-prompting}/SKILL.md +1 -1
- package/examples/skills/codex-cli/SKILL.md +11 -5
- package/examples/skills/gpt-5-4-prompting/SKILL.md +202 -0
- package/handoff-utils.ts +92 -0
- package/headless-monitor.ts +6 -1
- package/index.ts +231 -416
- package/notification-utils.ts +134 -0
- package/overlay-component.ts +14 -213
- package/package.json +26 -6
- package/pty-log.ts +59 -0
- package/pty-protocol.ts +33 -0
- package/pty-session.ts +11 -134
- package/reattach-overlay.ts +5 -74
- package/runtime-coordinator.ts +69 -0
- package/scripts/install.js +5 -1
- package/session-manager.ts +21 -11
- package/session-query.ts +170 -0
- package/spawn-helper.ts +37 -0
- package/types.ts +3 -0
package/handoff-utils.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { getAgentDir } from "@mariozechner/pi-coding-agent";
|
|
4
|
+
import type { InteractiveShellConfig } from "./config.js";
|
|
5
|
+
import type { InteractiveShellOptions, InteractiveShellResult } from "./types.js";
|
|
6
|
+
import type { PtyTerminalSession } from "./pty-session.js";
|
|
7
|
+
|
|
8
|
+
export function captureCompletionOutput(
|
|
9
|
+
session: PtyTerminalSession,
|
|
10
|
+
config: InteractiveShellConfig,
|
|
11
|
+
): InteractiveShellResult["completionOutput"] {
|
|
12
|
+
const result = session.getTailLines({
|
|
13
|
+
lines: config.completionNotifyLines,
|
|
14
|
+
ansi: false,
|
|
15
|
+
maxChars: config.completionNotifyMaxChars,
|
|
16
|
+
});
|
|
17
|
+
return {
|
|
18
|
+
lines: result.lines,
|
|
19
|
+
totalLines: result.totalLinesInBuffer,
|
|
20
|
+
truncated: result.lines.length < result.totalLinesInBuffer || result.truncatedByChars,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function captureTransferOutput(
|
|
25
|
+
session: PtyTerminalSession,
|
|
26
|
+
config: InteractiveShellConfig,
|
|
27
|
+
): InteractiveShellResult["transferred"] {
|
|
28
|
+
const result = session.getTailLines({
|
|
29
|
+
lines: config.transferLines,
|
|
30
|
+
ansi: false,
|
|
31
|
+
maxChars: config.transferMaxChars,
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
lines: result.lines,
|
|
35
|
+
totalLines: result.totalLinesInBuffer,
|
|
36
|
+
truncated: result.lines.length < result.totalLinesInBuffer || result.truncatedByChars,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function maybeBuildHandoffPreview(
|
|
41
|
+
session: PtyTerminalSession,
|
|
42
|
+
when: "exit" | "detach" | "kill" | "timeout" | "transfer",
|
|
43
|
+
config: InteractiveShellConfig,
|
|
44
|
+
overrides?: Pick<InteractiveShellOptions, "handoffPreviewEnabled" | "handoffPreviewLines" | "handoffPreviewMaxChars">,
|
|
45
|
+
): InteractiveShellResult["handoffPreview"] | undefined {
|
|
46
|
+
const enabled = overrides?.handoffPreviewEnabled ?? config.handoffPreviewEnabled;
|
|
47
|
+
if (!enabled) return undefined;
|
|
48
|
+
const lines = overrides?.handoffPreviewLines ?? config.handoffPreviewLines;
|
|
49
|
+
const maxChars = overrides?.handoffPreviewMaxChars ?? config.handoffPreviewMaxChars;
|
|
50
|
+
if (lines <= 0 || maxChars <= 0) return undefined;
|
|
51
|
+
const result = session.getTailLines({ lines, ansi: false, maxChars });
|
|
52
|
+
return { type: "tail", when, lines: result.lines };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function maybeWriteHandoffSnapshot(
|
|
56
|
+
session: PtyTerminalSession,
|
|
57
|
+
when: "exit" | "detach" | "kill" | "timeout" | "transfer",
|
|
58
|
+
config: InteractiveShellConfig,
|
|
59
|
+
context: { command: string; cwd?: string },
|
|
60
|
+
overrides?: Pick<InteractiveShellOptions, "handoffSnapshotEnabled" | "handoffSnapshotLines" | "handoffSnapshotMaxChars">,
|
|
61
|
+
): InteractiveShellResult["handoff"] | undefined {
|
|
62
|
+
const enabled = overrides?.handoffSnapshotEnabled ?? config.handoffSnapshotEnabled;
|
|
63
|
+
if (!enabled) return undefined;
|
|
64
|
+
const lines = overrides?.handoffSnapshotLines ?? config.handoffSnapshotLines;
|
|
65
|
+
const maxChars = overrides?.handoffSnapshotMaxChars ?? config.handoffSnapshotMaxChars;
|
|
66
|
+
if (lines <= 0 || maxChars <= 0) return undefined;
|
|
67
|
+
|
|
68
|
+
const baseDir = join(getAgentDir(), "cache", "interactive-shell");
|
|
69
|
+
mkdirSync(baseDir, { recursive: true });
|
|
70
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
71
|
+
const pid = session.pid;
|
|
72
|
+
const filename = `snapshot-${timestamp}-pid${pid}.log`;
|
|
73
|
+
const transcriptPath = join(baseDir, filename);
|
|
74
|
+
const tailResult = session.getTailLines({
|
|
75
|
+
lines,
|
|
76
|
+
ansi: config.ansiReemit,
|
|
77
|
+
maxChars,
|
|
78
|
+
});
|
|
79
|
+
const header = [
|
|
80
|
+
`# interactive-shell snapshot (${when})`,
|
|
81
|
+
`time: ${new Date().toISOString()}`,
|
|
82
|
+
`command: ${context.command}`,
|
|
83
|
+
`cwd: ${context.cwd ?? ""}`,
|
|
84
|
+
`pid: ${pid}`,
|
|
85
|
+
`exitCode: ${session.exitCode ?? ""}`,
|
|
86
|
+
`signal: ${session.signal ?? ""}`,
|
|
87
|
+
`lines: ${tailResult.lines.length} (requested ${lines}, maxChars ${maxChars})`,
|
|
88
|
+
"",
|
|
89
|
+
].join("\n");
|
|
90
|
+
writeFileSync(transcriptPath, header + tailResult.lines.join("\n") + "\n", { encoding: "utf-8" });
|
|
91
|
+
return { type: "snapshot", when, transcriptPath, linesWritten: tailResult.lines.length };
|
|
92
|
+
}
|
package/headless-monitor.ts
CHANGED
|
@@ -2,13 +2,17 @@ import { stripVTControlCharacters } from "node:util";
|
|
|
2
2
|
import type { PtyTerminalSession } from "./pty-session.js";
|
|
3
3
|
import type { InteractiveShellConfig } from "./config.js";
|
|
4
4
|
|
|
5
|
+
/** Runtime options for monitoring a headless dispatch session. */
|
|
5
6
|
export interface HeadlessMonitorOptions {
|
|
6
7
|
autoExitOnQuiet: boolean;
|
|
7
8
|
quietThreshold: number;
|
|
8
9
|
gracePeriod?: number;
|
|
9
10
|
timeout?: number;
|
|
11
|
+
/** Original session start time in ms since epoch, preserved when a foreground session moves headless. */
|
|
12
|
+
startedAt?: number;
|
|
10
13
|
}
|
|
11
14
|
|
|
15
|
+
/** Completion payload emitted when a headless dispatch session finishes. */
|
|
12
16
|
export interface HeadlessCompletionInfo {
|
|
13
17
|
exitCode: number | null;
|
|
14
18
|
signal?: number;
|
|
@@ -22,7 +26,7 @@ export interface HeadlessCompletionInfo {
|
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
export class HeadlessDispatchMonitor {
|
|
25
|
-
readonly startTime
|
|
29
|
+
readonly startTime: number;
|
|
26
30
|
private _disposed = false;
|
|
27
31
|
private quietTimer: ReturnType<typeof setTimeout> | null = null;
|
|
28
32
|
private timeoutTimer: ReturnType<typeof setTimeout> | null = null;
|
|
@@ -39,6 +43,7 @@ export class HeadlessDispatchMonitor {
|
|
|
39
43
|
private options: HeadlessMonitorOptions,
|
|
40
44
|
private onComplete: (info: HeadlessCompletionInfo) => void,
|
|
41
45
|
) {
|
|
46
|
+
this.startTime = options.startedAt ?? Date.now();
|
|
42
47
|
this.subscribe();
|
|
43
48
|
|
|
44
49
|
if (options.autoExitOnQuiet) {
|