@aliou/pi-processes 0.9.5 → 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.
Files changed (174) hide show
  1. package/CONTRIBUTING.md +40 -42
  2. package/README.md +54 -84
  3. package/extensions/process-tabs.ts +26 -0
  4. package/extensions/processes/client.ts +194 -0
  5. package/extensions/processes/commands/clear.ts +29 -0
  6. package/extensions/processes/commands/kill.ts +152 -0
  7. package/extensions/processes/commands/overview.ts +102 -0
  8. package/extensions/processes/components/overview-component.ts +729 -0
  9. package/extensions/processes/components/overview-panel.ts +177 -0
  10. package/extensions/processes/config/defaults.ts +28 -0
  11. package/extensions/processes/config/index.ts +13 -0
  12. package/extensions/processes/config/loader.ts +62 -0
  13. package/extensions/processes/config/migrations/000-import-legacy-process-config.ts +113 -0
  14. package/extensions/processes/config/migrations/001-v0-9-4-to-v0-10-0-config.ts +184 -0
  15. package/extensions/processes/config/migrations/002-stamp-v0-10-0-config-version.ts +21 -0
  16. package/extensions/processes/config/migrations/index.ts +25 -0
  17. package/extensions/processes/config/schema.ts +9 -0
  18. package/extensions/processes/config/types.ts +72 -0
  19. package/extensions/processes/constants.ts +1 -0
  20. package/extensions/processes/handlers/commands.ts +113 -0
  21. package/extensions/processes/handlers/kill-process.ts +31 -0
  22. package/extensions/processes/handlers/notifications.ts +147 -0
  23. package/extensions/processes/handlers/requests.ts +114 -0
  24. package/extensions/processes/handlers/subscriptions.ts +144 -0
  25. package/extensions/processes/hooks/background-blocker.ts +201 -0
  26. package/extensions/processes/hooks/cleanup.ts +43 -0
  27. package/extensions/processes/hooks/event-bridge.ts +32 -0
  28. package/extensions/processes/i18n/index.ts +3 -0
  29. package/extensions/processes/i18n/messages.ts +27 -0
  30. package/extensions/processes/i18n/translator.ts +49 -0
  31. package/extensions/processes/index.ts +109 -0
  32. package/extensions/processes/message-renderer.ts +117 -0
  33. package/extensions/processes/notification-sender.ts +43 -0
  34. package/extensions/processes/notifications/classify.ts +27 -0
  35. package/extensions/processes/notifications/log-matchers.ts +146 -0
  36. package/extensions/processes/notifications/registry.ts +225 -0
  37. package/extensions/processes/notifications/render-content.ts +122 -0
  38. package/extensions/processes/notifications/service.ts +336 -0
  39. package/extensions/processes/notifications/types.ts +11 -0
  40. package/extensions/processes/settings/apply-setting-change.ts +30 -0
  41. package/extensions/processes/settings/build-sections.ts +420 -0
  42. package/extensions/processes/settings/index.ts +42 -0
  43. package/extensions/processes/tools/clear/index.ts +21 -0
  44. package/extensions/processes/tools/clear/render.ts +40 -0
  45. package/extensions/processes/tools/components/index.ts +4 -0
  46. package/extensions/processes/tools/components/process-action-header.ts +31 -0
  47. package/extensions/processes/tools/components/process-action-title.ts +17 -0
  48. package/extensions/processes/tools/components/tool-layout.ts +70 -0
  49. package/extensions/processes/tools/components/watch.ts +53 -0
  50. package/extensions/processes/tools/index.ts +265 -0
  51. package/extensions/processes/tools/list/index.ts +157 -0
  52. package/extensions/processes/tools/list/render.ts +211 -0
  53. package/extensions/processes/tools/notify.ts +191 -0
  54. package/extensions/processes/tools/output/index.ts +307 -0
  55. package/extensions/processes/tools/output/render.ts +229 -0
  56. package/extensions/processes/tools/schema.ts +263 -0
  57. package/extensions/processes/tools/start/index.ts +64 -0
  58. package/extensions/processes/tools/start/render.ts +67 -0
  59. package/extensions/processes/tools/stop/index.ts +35 -0
  60. package/extensions/processes/tools/stop/render.ts +43 -0
  61. package/extensions/processes/tools/update/index.ts +320 -0
  62. package/extensions/processes/tools/update/render.ts +251 -0
  63. package/extensions/processes/tools/utils.ts +112 -0
  64. package/extensions/processes/tools/watch-format.ts +14 -0
  65. package/extensions/processes/tools/write/index.ts +75 -0
  66. package/extensions/processes/tools/write/render.ts +68 -0
  67. package/extensions/processes/utils/truncate.ts +285 -0
  68. package/extensions/processes-dock/client.ts +60 -0
  69. package/extensions/processes-dock/commands/dock.ts +70 -0
  70. package/extensions/processes-dock/commands/pin.ts +166 -0
  71. package/extensions/processes-dock/components/log-dock-component.ts +375 -0
  72. package/extensions/processes-dock/dock-state.ts +33 -0
  73. package/extensions/processes-dock/index.ts +28 -0
  74. package/extensions/processes-dock/logs-client.ts +5 -0
  75. package/extensions/processes-dock/widget/setup.ts +520 -0
  76. package/extensions/processes-dock/widget/status.ts +149 -0
  77. package/extensions/processes-dock/widget/types.ts +16 -0
  78. package/extensions/processes-logs/client.ts +51 -0
  79. package/extensions/processes-logs/commands/logs.ts +108 -0
  80. package/extensions/processes-logs/completions.ts +31 -0
  81. package/extensions/processes-logs/components/log-file-viewer.ts +274 -0
  82. package/extensions/processes-logs/components/log-overlay-component.ts +685 -0
  83. package/extensions/processes-logs/index.ts +27 -0
  84. package/extensions/processes-logs/logs-client.ts +101 -0
  85. package/extensions/shared/line-buffer.ts +35 -0
  86. package/extensions/shared/output-payload.ts +28 -0
  87. package/extensions/shared/ui.ts +146 -0
  88. package/package.json +29 -10
  89. package/schema.json +134 -0
  90. package/skills/pi-processes/SKILL.md +496 -25
  91. package/src/get-manager.ts +15 -0
  92. package/src/manager/index.ts +182 -0
  93. package/src/manager/internal-types.ts +130 -0
  94. package/src/manager/limits.ts +15 -0
  95. package/src/manager/process-log-store.ts +422 -0
  96. package/src/manager/process-output.ts +345 -0
  97. package/src/manager/process-registry.ts +78 -0
  98. package/src/manager/process-runtime-controller.ts +519 -0
  99. package/src/protocol/broadcasts.ts +14 -0
  100. package/src/protocol/channels.ts +30 -0
  101. package/src/protocol/commands.ts +24 -0
  102. package/src/protocol/index.ts +34 -0
  103. package/src/protocol/logs.ts +23 -0
  104. package/src/protocol/notifications.ts +48 -0
  105. package/src/protocol/requests.ts +76 -0
  106. package/src/types.ts +66 -0
  107. package/src/utils/ansi.ts +2 -4
  108. package/src/utils/format.ts +24 -32
  109. package/src/utils/index.ts +5 -1
  110. package/src/utils/is-record.ts +3 -0
  111. package/src/utils/match-line.ts +32 -0
  112. package/src/utils/platform.ts +13 -0
  113. package/src/utils/process-group.ts +4 -0
  114. package/src/utils/shorten-path.ts +11 -0
  115. package/src/utils/signals.ts +22 -0
  116. package/src/commands/clear/command.ts +0 -14
  117. package/src/commands/clear/index.ts +0 -1
  118. package/src/commands/completions.ts +0 -38
  119. package/src/commands/dock/command.ts +0 -29
  120. package/src/commands/dock/index.ts +0 -1
  121. package/src/commands/index.ts +0 -33
  122. package/src/commands/kill/command.ts +0 -69
  123. package/src/commands/kill/index.ts +0 -1
  124. package/src/commands/logs/command.ts +0 -47
  125. package/src/commands/logs/index.ts +0 -1
  126. package/src/commands/pick-process.ts +0 -34
  127. package/src/commands/pin/command.ts +0 -34
  128. package/src/commands/pin/index.ts +0 -1
  129. package/src/commands/processes/command.ts +0 -39
  130. package/src/commands/processes/index.ts +0 -1
  131. package/src/commands/settings/apply-setting-change.ts +0 -72
  132. package/src/commands/settings/build-sections.ts +0 -160
  133. package/src/commands/settings/command.ts +0 -20
  134. package/src/commands/settings/index.ts +0 -1
  135. package/src/components/log-dock-component.test.ts +0 -29
  136. package/src/components/log-dock-component.ts +0 -236
  137. package/src/components/log-file-viewer.ts +0 -317
  138. package/src/components/log-overlay-component.ts +0 -546
  139. package/src/components/panel-helpers.ts +0 -37
  140. package/src/components/process-picker-component.ts +0 -166
  141. package/src/components/processes-component.ts +0 -485
  142. package/src/components/status-format.ts +0 -38
  143. package/src/config.ts +0 -107
  144. package/src/constants/index.ts +0 -16
  145. package/src/constants/types.ts +0 -119
  146. package/src/hooks/background-blocker.ts +0 -66
  147. package/src/hooks/cleanup.ts +0 -10
  148. package/src/hooks/index.ts +0 -32
  149. package/src/hooks/message-renderer.ts +0 -115
  150. package/src/hooks/process-end.ts +0 -69
  151. package/src/hooks/process-watch.ts +0 -85
  152. package/src/hooks/utils.ts +0 -23
  153. package/src/hooks/widget/index.ts +0 -2
  154. package/src/hooks/widget/setup.ts +0 -167
  155. package/src/hooks/widget/status-widget.ts +0 -105
  156. package/src/hooks/widget/types.ts +0 -20
  157. package/src/index.ts +0 -35
  158. package/src/manager.test.ts +0 -331
  159. package/src/manager.ts +0 -758
  160. package/src/tools/actions/clear.ts +0 -20
  161. package/src/tools/actions/debug.ts +0 -155
  162. package/src/tools/actions/index.ts +0 -171
  163. package/src/tools/actions/kill.ts +0 -89
  164. package/src/tools/actions/list.ts +0 -188
  165. package/src/tools/actions/logs.ts +0 -117
  166. package/src/tools/actions/output-truncate.ts +0 -187
  167. package/src/tools/actions/output.test.ts +0 -392
  168. package/src/tools/actions/output.ts +0 -447
  169. package/src/tools/actions/start.ts +0 -245
  170. package/src/tools/actions/write.ts +0 -113
  171. package/src/tools/index.ts +0 -207
  172. package/src/utils/ansi.test.ts +0 -22
  173. package/src/utils/command-executor.test.ts +0 -48
  174. package/src/utils/keybindings.ts +0 -71
@@ -0,0 +1,76 @@
1
+ import type { ProcessInfo } from "../types";
2
+
3
+ // UI emits, core listens and calls reply synchronously.
4
+ // Reply callbacks make this an in-process protocol, not serializable IPC/RPC.
5
+
6
+ export interface RequestListPayload {
7
+ reply: (processes: ProcessInfo[]) => void;
8
+ }
9
+
10
+ export interface RequestGetPayload {
11
+ id: string;
12
+ reply: (info: ProcessInfo | null) => void;
13
+ }
14
+
15
+ export interface RequestOutputPayload {
16
+ id: string;
17
+ tailLines?: number;
18
+ reply: (
19
+ output: { stdout: string[]; stderr: string[]; status: string } | null,
20
+ ) => void;
21
+ }
22
+
23
+ export interface RequestCombinedOutputPayload {
24
+ id: string;
25
+ tailLines?: number;
26
+ reply: (
27
+ lines: Array<{ type: "stdout" | "stderr"; text: string }> | null,
28
+ ) => void;
29
+ }
30
+
31
+ export interface RequestLogFilesPayload {
32
+ id: string;
33
+ reply: (
34
+ files: {
35
+ stdoutFile: string;
36
+ stderrFile: string;
37
+ combinedFile: string;
38
+ } | null,
39
+ ) => void;
40
+ }
41
+
42
+ export interface RequestFileSizePayload {
43
+ id: string;
44
+ reply: (sizes: { stdout: number; stderr: number } | null) => void;
45
+ }
46
+
47
+ export interface ProcessProtocolConfig {
48
+ execution: {
49
+ shellPath: string | undefined;
50
+ };
51
+ interception: {
52
+ blockBackgroundCommands: boolean;
53
+ };
54
+ processList: {
55
+ maxPreviewLines: number;
56
+ maxVisibleProcesses: number;
57
+ };
58
+ output: {
59
+ defaultTailLines: number;
60
+ maxOutputLines: number;
61
+ maxOutputBytes: number;
62
+ };
63
+ follow: {
64
+ enabledByDefault: boolean;
65
+ autoHideOnFinish: boolean;
66
+ };
67
+ widget: {
68
+ showStatusWidget: boolean;
69
+ dockDefaultState: "closed" | "collapsed" | "expanded";
70
+ dockHeight: number;
71
+ };
72
+ }
73
+
74
+ export interface RequestConfigPayload {
75
+ reply: (config: ProcessProtocolConfig) => void;
76
+ }
package/src/types.ts ADDED
@@ -0,0 +1,66 @@
1
+ export type ProcessStatus =
2
+ | "running"
3
+ | "terminating"
4
+ | "terminate_timeout"
5
+ | "exited"
6
+ | "killed";
7
+
8
+ export const LIVE_STATUSES: ReadonlySet<ProcessStatus> = new Set([
9
+ "running",
10
+ "terminating",
11
+ "terminate_timeout",
12
+ ]);
13
+
14
+ export type ProcessEndReason =
15
+ | "exit"
16
+ | "signal"
17
+ | "spawn_error"
18
+ | "missing_pid"
19
+ | "kill_timeout"
20
+ | "lost";
21
+
22
+ export interface ProcessSignalInfo {
23
+ name: NodeJS.Signals;
24
+ number: number | null;
25
+ description: string;
26
+ }
27
+
28
+ export interface ProcessInfo {
29
+ id: string;
30
+ name: string;
31
+ pid: number; // On Unix, this is also the PGID (process group leader)
32
+ command: string;
33
+ cwd: string;
34
+ startTime: number;
35
+ endTime: number | null;
36
+ status: ProcessStatus;
37
+ exitCode: number | null;
38
+ success: boolean | null; // null if running, true if exit code 0, false otherwise
39
+ stdoutFile: string;
40
+ stderrFile: string;
41
+ endReason: ProcessEndReason | null;
42
+ signal: ProcessSignalInfo | null;
43
+ errorMessage: string | null;
44
+ }
45
+
46
+ export type ManagerEvent =
47
+ | { type: "process_started"; info: ProcessInfo }
48
+ | { type: "process_ended"; info: ProcessInfo }
49
+ | {
50
+ type: "process_output_changed";
51
+ id: string;
52
+ appendedText?: Array<{ type: "stdout" | "stderr"; text: string }>;
53
+ droppedLines?: number;
54
+ }
55
+ | { type: "processes_changed" };
56
+
57
+ export type KillResult =
58
+ | { ok: true; info: ProcessInfo }
59
+ | { ok: false; info: ProcessInfo; reason: "not_found" | "timeout" | "error" };
60
+
61
+ export type WriteResult =
62
+ | { ok: true }
63
+ | {
64
+ ok: false;
65
+ reason: "not_found" | "process_exited" | "stdin_closed" | "write_error";
66
+ };
package/src/utils/ansi.ts CHANGED
@@ -12,7 +12,7 @@ const BEL = String.fromCodePoint(0x0007);
12
12
 
13
13
  const ANSI_REPLACEMENTS: RegExp[] = [
14
14
  // CSI sequences: SGR, cursor movement, erase, scroll, etc.
15
- new RegExp(`${ESC}\\[[0-9;]*[A-Za-z]`, "gu"),
15
+ new RegExp(`${ESC}\\[[\\x30-\\x3f]*[\\x20-\\x2f]*[\\x40-\\x7e]`, "gu"),
16
16
  // OSC sequences: ESC]...<BEL> or ESC]...<ESC>\\.
17
17
  new RegExp(`${ESC}\\][^${BEL}${ESC}]*(?:${BEL}|${ESC}\\\\)`, "gu"),
18
18
  // APC sequences: ESC_...<BEL> or ESC_...<ESC>\\.
@@ -25,9 +25,7 @@ const ANSI_REPLACEMENTS: RegExp[] = [
25
25
  // biome-ignore lint/suspicious/noControlCharactersInRegex: this regex intentionally targets terminal control characters.
26
26
  const TERMINAL_CONTROL_CHARS = /[\u0000-\u0008\u000b-\u001f\u007f]/gu;
27
27
 
28
- /**
29
- * Check if a string contains ANSI escape codes.
30
- */
28
+ /** Check if a string contains ANSI escape codes. */
31
29
  export function hasAnsi(str: string): boolean {
32
30
  return str.includes(ESC);
33
31
  }
@@ -1,12 +1,16 @@
1
- import type { Theme } from "@earendil-works/pi-coding-agent";
2
- import type { ProcessInfo } from "../constants";
1
+ import type { ProcessStatus } from "../types";
3
2
 
3
+ /**
4
+ * Format a process runtime as a human-readable string.
5
+ * "3s", "2m 15s", "1h 30m"
6
+ */
4
7
  export function formatRuntime(
5
8
  startTime: number,
6
9
  endTime: number | null,
10
+ now: number = Date.now(),
7
11
  ): string {
8
- const end = endTime ?? Date.now();
9
- const ms = end - startTime;
12
+ const end = endTime ?? now;
13
+ const ms = Math.max(0, end - startTime);
10
14
  const seconds = Math.floor(ms / 1000);
11
15
  const minutes = Math.floor(seconds / 60);
12
16
  const hours = Math.floor(minutes / 60);
@@ -20,8 +24,16 @@ export function formatRuntime(
20
24
  return `${seconds}s`;
21
25
  }
22
26
 
23
- export function formatStatus(proc: ProcessInfo): string {
24
- switch (proc.status) {
27
+ /**
28
+ * Format a process status as a plain string.
29
+ * "running", "exit(0)", "exit(1)"
30
+ */
31
+ export function formatStatus(proc: {
32
+ status: string;
33
+ success: boolean | null;
34
+ exitCode: number | null;
35
+ }): string {
36
+ switch (proc.status as ProcessStatus) {
25
37
  case "running":
26
38
  return "running";
27
39
  case "terminating":
@@ -37,38 +49,18 @@ export function formatStatus(proc: ProcessInfo): string {
37
49
  }
38
50
  }
39
51
 
52
+ /**
53
+ * Truncate a command string to a maximum length.
54
+ */
40
55
  export function truncateCmd(cmd: string, max = 40): string {
41
56
  if (cmd.length <= max) return cmd;
42
57
  return `${cmd.slice(0, max - 3)}...`;
43
58
  }
44
59
 
60
+ /**
61
+ * Format a timestamp as an ISO string or "-" if null.
62
+ */
45
63
  export function formatTimestamp(ts: number | null): string {
46
64
  if (!ts) return "-";
47
65
  return new Date(ts).toISOString().replace("T", " ").slice(0, 19);
48
66
  }
49
-
50
- export function formatStatusTag(
51
- process: {
52
- status: string;
53
- success: boolean | null;
54
- exitCode: number | null;
55
- },
56
- theme: Theme,
57
- ): string {
58
- switch (process.status) {
59
- case "running":
60
- return theme.fg("accent", "running");
61
- case "terminating":
62
- return theme.fg("warning", "terminating");
63
- case "terminate_timeout":
64
- return theme.fg("error", "terminate_timeout");
65
- case "killed":
66
- return theme.fg("warning", "killed");
67
- case "exited":
68
- return process.success
69
- ? theme.fg("success", "exit(0)")
70
- : theme.fg("error", `exit(${process.exitCode ?? "?"})`);
71
- default:
72
- return theme.fg("muted", process.status);
73
- }
74
- }
@@ -1,9 +1,13 @@
1
1
  export { hasAnsi, stripAnsi } from "./ansi";
2
+ export { resolveShellExecutable, spawnCommand } from "./command-executor";
2
3
  export {
3
4
  formatRuntime,
4
5
  formatStatus,
5
- formatStatusTag,
6
6
  formatTimestamp,
7
7
  truncateCmd,
8
8
  } from "./format";
9
+ export type { LineMatchMode } from "./match-line";
10
+ export { compileLineMatcher } from "./match-line";
9
11
  export { isProcessGroupAlive, killProcessGroup } from "./process-group";
12
+ export { walkCommands, wordToString } from "./shell-utils";
13
+ export { shortenPath } from "./shorten-path";
@@ -0,0 +1,3 @@
1
+ export function isRecord(value: unknown): value is Record<string, unknown> {
2
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Shared line-matching logic used by both the notification log-matchers
3
+ * and the output tool action. Pi-agnostic — no Pi imports.
4
+ */
5
+
6
+ export type LineMatchMode = "literal" | "regex";
7
+
8
+ /**
9
+ * Compile a pattern + mode into a line-matching predicate.
10
+ *
11
+ * - literal: substring match (case-sensitive).
12
+ * - regex: RegExp test (throws on invalid pattern).
13
+ */
14
+ export function compileLineMatcher(
15
+ pattern: string,
16
+ mode: LineMatchMode,
17
+ ): (line: string) => boolean {
18
+ // Guard against the match-all footgun for an empty pattern. An empty
19
+ // literal would match every line via String#includes(""), and an empty
20
+ // regex matches at every position. Callers should treat "" as "no filter",
21
+ // but defend here too so the shared primitive can never become a match-all.
22
+ if (pattern.length === 0) {
23
+ return () => false;
24
+ }
25
+
26
+ if (mode === "literal") {
27
+ return (line) => line.includes(pattern);
28
+ }
29
+
30
+ const regex = new RegExp(pattern);
31
+ return (line) => regex.test(line);
32
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Platform gate for the processes extensions.
3
+ *
4
+ * The manager spawns detached process groups and kills them by negative pid
5
+ * (`src/utils/process-group.ts`), which is a POSIX-only operation. On Windows
6
+ * there is no process-group kill and detached spawn semantics differ, so the
7
+ * extensions intentionally no-op there rather than silently misbehaving.
8
+ *
9
+ * Main (0.9.4) shipped the same guard. The rewrite had dropped it.
10
+ */
11
+ export function isWindowsPlatform(): boolean {
12
+ return process.platform === "win32";
13
+ }
@@ -3,6 +3,7 @@
3
3
  * Uses signal 0 to test existence without actually sending a signal.
4
4
  */
5
5
  export function isProcessGroupAlive(pgid: number): boolean {
6
+ if (!Number.isInteger(pgid) || pgid <= 0) return false;
6
7
  try {
7
8
  process.kill(-pgid, 0);
8
9
  return true;
@@ -18,5 +19,8 @@ export function isProcessGroupAlive(pgid: number): boolean {
18
19
  * Negative PID targets the process group.
19
20
  */
20
21
  export function killProcessGroup(pgid: number, signal: NodeJS.Signals): void {
22
+ if (!Number.isInteger(pgid) || pgid <= 0) {
23
+ throw new RangeError("Process group ID must be a positive integer");
24
+ }
21
25
  process.kill(-pgid, signal);
22
26
  }
@@ -0,0 +1,11 @@
1
+ import { homedir } from "node:os";
2
+ import { sep } from "node:path";
3
+
4
+ /** Replace a home-directory prefix with `~` for display. */
5
+ export function shortenPath(path: string, home = homedir()): string {
6
+ if (path === home) return "~";
7
+ if (path.startsWith(`${home}${sep}`)) {
8
+ return `~${path.slice(home.length)}`;
9
+ }
10
+ return path;
11
+ }
@@ -0,0 +1,22 @@
1
+ import type { ProcessSignalInfo } from "../types";
2
+
3
+ const SIGNALS: Partial<
4
+ Record<NodeJS.Signals, { number: number; description: string }>
5
+ > = {
6
+ SIGINT: { number: 2, description: "interrupt" },
7
+ SIGTERM: { number: 15, description: "termination request" },
8
+ SIGKILL: { number: 9, description: "forced kill" },
9
+ SIGHUP: { number: 1, description: "terminal hangup" },
10
+ SIGQUIT: { number: 3, description: "quit" },
11
+ SIGABRT: { number: 6, description: "abort" },
12
+ SIGALRM: { number: 14, description: "alarm" },
13
+ };
14
+
15
+ export function formatSignalInfo(signal: NodeJS.Signals): ProcessSignalInfo {
16
+ const known = SIGNALS[signal];
17
+ return {
18
+ name: signal,
19
+ number: known?.number ?? null,
20
+ description: known?.description ?? signal,
21
+ };
22
+ }
@@ -1,14 +0,0 @@
1
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- import type { ProcessManager } from "../../manager";
3
-
4
- export function registerPsClearCommand(
5
- pi: ExtensionAPI,
6
- manager: ProcessManager,
7
- ): void {
8
- pi.registerCommand("ps:clear", {
9
- description: "Remove all finished processes from the list",
10
- handler: async (_args, _ctx) => {
11
- manager.clearFinished();
12
- },
13
- });
14
- }
@@ -1 +0,0 @@
1
- export { registerPsClearCommand } from "./command";
@@ -1,38 +0,0 @@
1
- import type { ProcessManager } from "../manager";
2
-
3
- export function runningProcessCompletions(manager: ProcessManager) {
4
- return (prefix: string) => {
5
- const processes = manager.list();
6
- const lower = prefix.toLowerCase();
7
- return processes
8
- .filter(
9
- (p) =>
10
- p.status === "running" &&
11
- (p.id.toLowerCase().startsWith(lower) ||
12
- p.name.toLowerCase().startsWith(lower)),
13
- )
14
- .map((p) => ({
15
- value: p.id,
16
- label: p.id,
17
- description: p.name,
18
- }));
19
- };
20
- }
21
-
22
- export function allProcessCompletions(manager: ProcessManager) {
23
- return (prefix: string) => {
24
- const processes = manager.list();
25
- const lower = prefix.toLowerCase();
26
- return processes
27
- .filter(
28
- (p) =>
29
- p.id.toLowerCase().startsWith(lower) ||
30
- p.name.toLowerCase().startsWith(lower),
31
- )
32
- .map((p) => ({
33
- value: p.id,
34
- label: p.id,
35
- description: p.name,
36
- }));
37
- };
38
- }
@@ -1,29 +0,0 @@
1
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- import type { DockActions } from "../../hooks/widget";
3
-
4
- export function registerPsDockCommand(
5
- pi: ExtensionAPI,
6
- dockActions: DockActions,
7
- ): void {
8
- pi.registerCommand("ps:dock", {
9
- description: "Control dock visibility",
10
- getArgumentCompletions: () => [
11
- { value: "show", label: "show — make the dock visible" },
12
- { value: "hide", label: "hide — hide the dock" },
13
- { value: "toggle", label: "toggle — cycle visibility" },
14
- ],
15
- handler: async (args, _ctx) => {
16
- const arg = args.trim().toLowerCase();
17
-
18
- if (arg === "show") {
19
- dockActions.expand();
20
- } else if (arg === "hide") {
21
- dockActions.hide();
22
- } else if (arg === "toggle" || arg === "") {
23
- dockActions.toggle();
24
- } else {
25
- dockActions.toggle();
26
- }
27
- },
28
- });
29
- }
@@ -1 +0,0 @@
1
- export { registerPsDockCommand } from "./command";
@@ -1,33 +0,0 @@
1
- /**
2
- * Process commands with /ps: prefix.
3
- *
4
- * /ps - View and manage all background processes
5
- * /ps:logs [id] - Open log viewer overlay (search, scroll, stream filter)
6
- * /ps:pin [id] - Pin the dock to a specific process
7
- * /ps:kill [id] - Kill a running process
8
- * /ps:clear - Remove all finished processes from the list
9
- * /ps:dock [show|hide|toggle] - Control dock visibility
10
- */
11
-
12
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
13
- import type { DockActions } from "../hooks/widget";
14
- import type { ProcessManager } from "../manager";
15
- import { registerPsClearCommand } from "./clear";
16
- import { registerPsDockCommand } from "./dock";
17
- import { registerPsKillCommand } from "./kill";
18
- import { registerPsLogsCommand } from "./logs";
19
- import { registerPsPinCommand } from "./pin";
20
- import { registerPsCommand } from "./processes";
21
-
22
- export function setupProcessesCommands(
23
- pi: ExtensionAPI,
24
- manager: ProcessManager,
25
- dockActions: DockActions,
26
- ): void {
27
- registerPsCommand(pi, manager, dockActions);
28
- registerPsPinCommand(pi, manager, dockActions);
29
- registerPsLogsCommand(pi, manager);
30
- registerPsKillCommand(pi, manager, dockActions);
31
- registerPsClearCommand(pi, manager);
32
- registerPsDockCommand(pi, dockActions);
33
- }
@@ -1,69 +0,0 @@
1
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- import { LIVE_STATUSES } from "../../constants";
3
- import type { DockActions } from "../../hooks/widget";
4
- import type { ProcessManager } from "../../manager";
5
- import { runningProcessCompletions } from "../completions";
6
- import { pickProcess } from "../pick-process";
7
-
8
- export function registerPsKillCommand(
9
- pi: ExtensionAPI,
10
- manager: ProcessManager,
11
- dockActions: DockActions,
12
- ): void {
13
- pi.registerCommand("ps:kill", {
14
- description: "Kill a running process",
15
- getArgumentCompletions: runningProcessCompletions(manager),
16
- handler: async (args, ctx) => {
17
- const arg = args.trim();
18
-
19
- let processId: string | undefined;
20
-
21
- if (arg) {
22
- const proc = manager.get(arg);
23
- if (!proc) {
24
- return;
25
- }
26
- if (!LIVE_STATUSES.has(proc.status)) {
27
- return;
28
- }
29
- processId = proc.id;
30
- } else {
31
- const running = manager
32
- .list()
33
- .filter((p) => LIVE_STATUSES.has(p.status));
34
-
35
- if (running.length === 0) {
36
- return;
37
- }
38
-
39
- if (running.length === 1 && running[0]) {
40
- processId = running[0].id;
41
- } else {
42
- processId = await pickProcess(
43
- ctx,
44
- manager,
45
- "Select process to kill",
46
- (p) => LIVE_STATUSES.has(p.status),
47
- );
48
- if (!processId) return;
49
- }
50
- }
51
-
52
- if (!processId) return;
53
-
54
- const proc = manager.get(processId);
55
- if (!proc) return;
56
-
57
- const signal =
58
- proc.status === "terminate_timeout" ? "SIGKILL" : "SIGTERM";
59
- const timeoutMs = signal === "SIGKILL" ? 200 : 3000;
60
- const result = await manager.kill(proc.id, { signal, timeoutMs });
61
-
62
- if (result.ok) {
63
- if (dockActions.getFocusedProcessId() === proc.id) {
64
- dockActions.setFocus(null);
65
- }
66
- }
67
- },
68
- });
69
- }
@@ -1 +0,0 @@
1
- export { registerPsKillCommand } from "./command";
@@ -1,47 +0,0 @@
1
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- import { LogOverlayComponent } from "../../components/log-overlay-component";
3
- import type { ProcessManager } from "../../manager";
4
- import { allProcessCompletions } from "../completions";
5
-
6
- export function registerPsLogsCommand(
7
- pi: ExtensionAPI,
8
- manager: ProcessManager,
9
- ): void {
10
- pi.registerCommand("ps:logs", {
11
- description:
12
- "Open log viewer for a process (search, scroll, stream filter)",
13
- getArgumentCompletions: allProcessCompletions(manager),
14
- handler: async (args, ctx) => {
15
- if (!ctx.hasUI) return;
16
-
17
- const arg = args.trim();
18
- let processId: string | undefined;
19
-
20
- if (arg) {
21
- const proc = manager.get(arg);
22
- if (!proc) return;
23
- processId = proc.id;
24
- }
25
-
26
- await ctx.ui.custom<null>(
27
- (_tui, theme, _kb, done) => {
28
- return new LogOverlayComponent({
29
- tui: _tui,
30
- theme,
31
- manager,
32
- initialProcessId: processId,
33
- done: () => done(null),
34
- });
35
- },
36
- {
37
- overlay: true,
38
- overlayOptions: {
39
- width: "90%",
40
- maxHeight: "80%",
41
- anchor: "center",
42
- },
43
- },
44
- );
45
- },
46
- });
47
- }
@@ -1 +0,0 @@
1
- export { registerPsLogsCommand } from "./command";
@@ -1,34 +0,0 @@
1
- import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
2
- import { ProcessPickerComponent } from "../components/process-picker-component";
3
- import type { ProcessInfo } from "../constants";
4
- import type { ProcessManager } from "../manager";
5
-
6
- export async function pickProcess(
7
- ctx: ExtensionCommandContext,
8
- manager: ProcessManager,
9
- title: string,
10
- filter?: (proc: ProcessInfo) => boolean,
11
- ): Promise<string | undefined> {
12
- if (!ctx.hasUI) {
13
- return undefined;
14
- }
15
-
16
- const result = await ctx.ui.custom<string | null>((tui, theme, _kb, done) => {
17
- return new ProcessPickerComponent(
18
- tui,
19
- theme,
20
- (processId?: string) => {
21
- done(processId ?? null);
22
- },
23
- manager,
24
- title,
25
- filter,
26
- );
27
- });
28
-
29
- if (result === undefined || result === null) {
30
- return undefined;
31
- }
32
-
33
- return result;
34
- }