@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
@@ -1,20 +0,0 @@
1
- import type { ExecuteResult } from "../../constants";
2
- import type { ProcessManager } from "../../manager";
3
-
4
- export function executeClear(manager: ProcessManager): ExecuteResult {
5
- const cleared = manager.clearFinished();
6
- const message =
7
- cleared > 0
8
- ? `Cleared ${cleared} finished process(es)`
9
- : "No finished processes to clear";
10
-
11
- return {
12
- content: [{ type: "text", text: message }],
13
- details: {
14
- action: "clear",
15
- success: true,
16
- message,
17
- cleared,
18
- },
19
- };
20
- }
@@ -1,155 +0,0 @@
1
- import { ToolCallHeader } from "@aliou/pi-utils-ui";
2
- import type { Theme } from "@earendil-works/pi-coding-agent";
3
- import type { ExecuteResult, ProcessInfo } from "../../constants";
4
-
5
- interface DebugParams {
6
- preview?: "start" | "list" | "output" | "logs" | "error";
7
- }
8
-
9
- export function renderDebugCall(
10
- args: DebugParams,
11
- theme: Theme,
12
- ): ToolCallHeader {
13
- return new ToolCallHeader(
14
- {
15
- toolName: "Process",
16
- action: "debug_preview",
17
- mainArg: args.preview,
18
- },
19
- theme,
20
- );
21
- }
22
-
23
- function mockProcess(overrides?: Partial<ProcessInfo>): ProcessInfo {
24
- const now = Date.now();
25
- return {
26
- id: "proc_42",
27
- name: "demo-server",
28
- pid: 4242,
29
- command: "pnpm dev --port 3000",
30
- cwd: "/tmp/demo",
31
- startTime: now - 12_000,
32
- endTime: null,
33
- status: "running",
34
- exitCode: null,
35
- success: null,
36
- stdoutFile: "/tmp/pi-processes-demo/proc_42-stdout.log",
37
- stderrFile: "/tmp/pi-processes-demo/proc_42-stderr.log",
38
- alertOnSuccess: false,
39
- alertOnFailure: true,
40
- alertOnKill: false,
41
- ...overrides,
42
- };
43
- }
44
-
45
- /**
46
- * Temporary no-side-effect previews for process tool renderers.
47
- * Remove before release.
48
- */
49
- export function executeDebugPreview(params: DebugParams): ExecuteResult {
50
- const preview = params.preview ?? "start";
51
-
52
- if (preview === "start") {
53
- const process = mockProcess();
54
- const message = [
55
- `Started "${process.name}" (${process.id}, PID: ${process.pid})`,
56
- "Log files:",
57
- ` stdout: ${process.stdoutFile}`,
58
- ` stderr: ${process.stderrFile}`,
59
- ].join("\n");
60
- return {
61
- content: [{ type: "text", text: message }],
62
- details: {
63
- action: "start",
64
- success: true,
65
- message,
66
- process,
67
- },
68
- };
69
- }
70
-
71
- if (preview === "list") {
72
- const processes = [
73
- mockProcess(),
74
- mockProcess({
75
- id: "proc_11",
76
- name: "builder",
77
- pid: 1011,
78
- command: "pnpm build --watch",
79
- }),
80
- mockProcess({
81
- id: "proc_10",
82
- name: "tests",
83
- pid: 1010,
84
- command: "pnpm test",
85
- status: "exited",
86
- success: false,
87
- endTime: Date.now() - 3_000,
88
- exitCode: 1,
89
- }),
90
- ];
91
-
92
- return {
93
- content: [{ type: "text", text: "Debug preview: list" }],
94
- details: {
95
- action: "list",
96
- success: true,
97
- message: "Debug preview: list",
98
- processes,
99
- },
100
- };
101
- }
102
-
103
- if (preview === "output") {
104
- const message =
105
- '"demo-server" (proc_42) [running]: 4 stdout lines, 2 stderr lines';
106
- const content = [
107
- message,
108
- "stdout:",
109
- "starting...",
110
- "loading config",
111
- "ready on http://localhost:3000",
112
- "watching for changes",
113
- "",
114
- "stderr:",
115
- "warn: deprecated option in config",
116
- "error: simulated stack trace line",
117
- "",
118
- "Process is still running. Use watches instead of polling.",
119
- "",
120
- "[Complete currently-retained logs:",
121
- "stdout=/tmp/pi-processes-demo/proc_42-stdout.log",
122
- "stderr=/tmp/pi-processes-demo/proc_42-stderr.log]",
123
- ].join("\n");
124
-
125
- return {
126
- content: [{ type: "text", text: content }],
127
- details: {
128
- action: "output",
129
- success: true,
130
- message,
131
- logFiles: {
132
- stdoutFile: "/tmp/pi-processes-demo/proc_42-stdout.log",
133
- stderrFile: "/tmp/pi-processes-demo/proc_42-stderr.log",
134
- },
135
- },
136
- };
137
- }
138
-
139
- if (preview === "logs") {
140
- return {
141
- content: [{ type: "text", text: "Debug preview: logs" }],
142
- details: {
143
- action: "logs",
144
- success: true,
145
- message: "Debug preview: logs",
146
- logFiles: {
147
- stdoutFile: "/tmp/pi-processes-demo/proc_42-stdout.log",
148
- stderrFile: "/tmp/pi-processes-demo/proc_42-stderr.log",
149
- },
150
- },
151
- };
152
- }
153
-
154
- throw new Error("Invalid logWatches[0].pattern: Unterminated group");
155
- }
@@ -1,171 +0,0 @@
1
- import { ToolBody, ToolCallHeader } from "@aliou/pi-utils-ui";
2
- import type {
3
- AgentToolResult,
4
- ExtensionContext,
5
- Theme,
6
- ToolRenderResultOptions,
7
- } from "@earendil-works/pi-coding-agent";
8
- import type { Component } from "@earendil-works/pi-tui";
9
- import type {
10
- ExecuteResult,
11
- ProcessAction,
12
- ProcessesDetails,
13
- } from "../../constants";
14
- import type { ProcessManager } from "../../manager";
15
- import { executeClear } from "./clear";
16
- import { executeDebugPreview, renderDebugCall } from "./debug";
17
- import { executeKill, renderKillCall } from "./kill";
18
- import { executeList, renderListResult } from "./list";
19
- import { executeLogs, renderLogsCall, renderLogsResult } from "./logs";
20
- import { executeOutput, renderOutputCall, renderOutputResult } from "./output";
21
- import { executeStart, renderStartCall, renderStartResult } from "./start";
22
- import { executeWrite, renderWriteCall } from "./write";
23
-
24
- const DEBUG_PREVIEW_ENABLED = process.env.PI_PROCESSES_DEBUG_PREVIEW === "1";
25
-
26
- interface ActionParams {
27
- action: ProcessAction | string;
28
- command?: string;
29
- name?: string;
30
- id?: string;
31
- input?: string;
32
- end?: boolean;
33
- cwd?: string;
34
- alertOnSuccess?: boolean;
35
- alertOnFailure?: boolean;
36
- alertOnKill?: boolean;
37
- logWatches?: Array<{
38
- pattern: string;
39
- stream?: "stdout" | "stderr" | "both";
40
- repeat?: boolean;
41
- }>;
42
- preview?: "start" | "list" | "output" | "logs" | "error";
43
- }
44
-
45
- export async function executeAction(
46
- params: ActionParams,
47
- manager: ProcessManager,
48
- ctx: ExtensionContext,
49
- ): Promise<ExecuteResult> {
50
- switch (params.action) {
51
- case "start":
52
- return executeStart(params, manager, ctx);
53
- case "list":
54
- return executeList(manager);
55
- case "output":
56
- return executeOutput(params, manager);
57
- case "logs":
58
- return executeLogs(params, manager);
59
- case "kill":
60
- return executeKill(params, manager);
61
- case "clear":
62
- return executeClear(manager);
63
- case "write":
64
- return executeWrite(params, manager);
65
- case "debug_preview":
66
- if (!DEBUG_PREVIEW_ENABLED) {
67
- throw new Error(
68
- "Action 'debug_preview' is disabled. Set PI_PROCESSES_DEBUG_PREVIEW=1 to enable.",
69
- );
70
- }
71
- return executeDebugPreview(params);
72
- default:
73
- return {
74
- content: [{ type: "text", text: `Unknown action: ${params.action}` }],
75
- details: {
76
- action: params.action as ProcessAction,
77
- success: false,
78
- message: `Unknown action: ${params.action}`,
79
- },
80
- };
81
- }
82
- }
83
-
84
- export function renderActionCall(args: ActionParams, theme: Theme): Component {
85
- switch (args.action) {
86
- case "start":
87
- return renderStartCall(args, theme);
88
- case "output":
89
- return renderOutputCall(args, theme);
90
- case "logs":
91
- return renderLogsCall(args, theme);
92
- case "kill":
93
- return renderKillCall(args, theme);
94
- case "write":
95
- return renderWriteCall(args, theme);
96
- case "debug_preview":
97
- return renderDebugCall(args, theme);
98
- default:
99
- return new ToolCallHeader(
100
- { toolName: "Process", action: args.action },
101
- theme,
102
- );
103
- }
104
- }
105
-
106
- export function renderActionResult(
107
- result: AgentToolResult<ProcessesDetails>,
108
- options: ToolRenderResultOptions,
109
- theme: Theme,
110
- ): Component {
111
- const { details } = result;
112
-
113
- if (!details) {
114
- return new ToolBody(
115
- {
116
- fields: [
117
- {
118
- label: "Result",
119
- value: "No result details available.",
120
- showCollapsed: true,
121
- },
122
- ],
123
- },
124
- options,
125
- theme,
126
- );
127
- }
128
-
129
- switch (details.action) {
130
- case "start":
131
- return renderStartResult(result, options, theme);
132
- case "list":
133
- return renderListResult(result, options, theme);
134
- case "output":
135
- return renderOutputResult(result, options, theme);
136
- case "logs":
137
- return renderLogsResult(result, options, theme);
138
- case "kill":
139
- case "write":
140
- case "clear":
141
- case "debug_preview":
142
- // Default rendering for these actions
143
- return new ToolBody(
144
- {
145
- fields: [
146
- {
147
- label: "Result",
148
- value: details.message,
149
- showCollapsed: true,
150
- },
151
- ],
152
- },
153
- options,
154
- theme,
155
- );
156
- default:
157
- return new ToolBody(
158
- {
159
- fields: [
160
- {
161
- label: "Result",
162
- value: details.message,
163
- showCollapsed: true,
164
- },
165
- ],
166
- },
167
- options,
168
- theme,
169
- );
170
- }
171
- }
@@ -1,89 +0,0 @@
1
- import { ToolCallHeader } from "@aliou/pi-utils-ui";
2
- import type { Theme } from "@earendil-works/pi-coding-agent";
3
- import type { ExecuteResult } from "../../constants";
4
- import type { ProcessManager } from "../../manager";
5
-
6
- interface KillParams {
7
- id?: string;
8
- }
9
-
10
- export function renderKillCall(args: KillParams, theme: Theme): ToolCallHeader {
11
- return new ToolCallHeader(
12
- {
13
- toolName: "Process",
14
- action: "kill",
15
- mainArg: args.id,
16
- },
17
- theme,
18
- );
19
- }
20
-
21
- export async function executeKill(
22
- params: KillParams,
23
- manager: ProcessManager,
24
- ): Promise<ExecuteResult> {
25
- if (!params.id) {
26
- return {
27
- content: [{ type: "text", text: "Missing required parameter: id" }],
28
- details: {
29
- action: "kill",
30
- success: false,
31
- message: "Missing required parameter: id",
32
- },
33
- };
34
- }
35
-
36
- const proc = manager.get(params.id);
37
- if (!proc) {
38
- const message = `Process not found: ${params.id}`;
39
- return {
40
- content: [{ type: "text", text: message }],
41
- details: {
42
- action: "kill",
43
- success: false,
44
- message,
45
- },
46
- };
47
- }
48
-
49
- const result = await manager.kill(proc.id, {
50
- signal: "SIGTERM",
51
- timeoutMs: 3000,
52
- });
53
-
54
- if (result.ok) {
55
- const message = `Terminated "${proc.name}" (${proc.id})`;
56
- return {
57
- content: [{ type: "text", text: message }],
58
- details: {
59
- action: "kill",
60
- success: true,
61
- message,
62
- },
63
- };
64
- }
65
-
66
- if (result.reason === "timeout") {
67
- const message =
68
- `SIGTERM timed out for "${proc.name}" (${proc.id}). ` +
69
- "Run /ps and press x on terminate_timeout to force kill (SIGKILL).";
70
- return {
71
- content: [{ type: "text", text: message }],
72
- details: {
73
- action: "kill",
74
- success: false,
75
- message,
76
- },
77
- };
78
- }
79
-
80
- const message = `Failed to terminate "${proc.name}" (${proc.id})`;
81
- return {
82
- content: [{ type: "text", text: message }],
83
- details: {
84
- action: "kill",
85
- success: false,
86
- message,
87
- },
88
- };
89
- }
@@ -1,188 +0,0 @@
1
- import { ToolBody, ToolFooter } from "@aliou/pi-utils-ui";
2
- import type {
3
- AgentToolResult,
4
- Theme,
5
- ToolRenderResultOptions,
6
- } from "@earendil-works/pi-coding-agent";
7
- import { Text } from "@earendil-works/pi-tui";
8
- import type { ExecuteResult, ProcessesDetails } from "../../constants";
9
- import type { ProcessManager } from "../../manager";
10
- import {
11
- formatRuntime,
12
- formatStatus,
13
- formatStatusTag,
14
- formatTimestamp,
15
- truncateCmd,
16
- } from "../../utils";
17
-
18
- export function executeList(manager: ProcessManager): ExecuteResult {
19
- const processes = manager.list();
20
-
21
- if (processes.length === 0) {
22
- return {
23
- content: [{ type: "text", text: "No background processes running" }],
24
- details: {
25
- action: "list",
26
- success: true,
27
- message: "No background processes running",
28
- processes: [],
29
- },
30
- };
31
- }
32
-
33
- const summary = processes
34
- .map(
35
- (p) =>
36
- `${p.id} "${p.name}": ${truncateCmd(p.command)} [${formatStatus(p)}] ${formatRuntime(p.startTime, p.endTime)}`,
37
- )
38
- .join("\n");
39
-
40
- const message = `${processes.length} process(es):\n${summary}`;
41
- return {
42
- content: [{ type: "text", text: message }],
43
- details: {
44
- action: "list",
45
- success: true,
46
- message,
47
- processes,
48
- },
49
- };
50
- }
51
-
52
- export function renderListResult(
53
- result: AgentToolResult<ProcessesDetails>,
54
- options: ToolRenderResultOptions,
55
- theme: Theme,
56
- ): ToolBody {
57
- const { details } = result;
58
-
59
- if (!details.processes || details.processes.length === 0) {
60
- return new ToolBody(
61
- {
62
- fields: [
63
- {
64
- label: "Processes",
65
- value: "No background processes running",
66
- showCollapsed: true,
67
- },
68
- ],
69
- },
70
- options,
71
- theme,
72
- );
73
- }
74
-
75
- const processes = [...details.processes];
76
-
77
- const statusRank = (status: string): number => {
78
- switch (status) {
79
- case "running":
80
- return 0;
81
- case "terminating":
82
- return 1;
83
- case "terminate_timeout":
84
- return 2;
85
- case "killed":
86
- return 3;
87
- case "exited":
88
- return 4;
89
- default:
90
- return 5;
91
- }
92
- };
93
-
94
- processes.sort((a, b) => {
95
- const rankDiff = statusRank(a.status) - statusRank(b.status);
96
- if (rankDiff !== 0) return rankDiff;
97
- return b.startTime - a.startTime;
98
- });
99
-
100
- const runningCount = processes.filter(
101
- (p) => p.status === "running" || p.status === "terminating",
102
- ).length;
103
-
104
- const lines: string[] = [
105
- theme.fg(
106
- "success",
107
- `${processes.length} process(es), ${runningCount} running/terminating`,
108
- ),
109
- ];
110
-
111
- for (const process of processes) {
112
- const status = formatStatusTag(process, theme);
113
- lines.push(
114
- [
115
- `- ${theme.fg("accent", process.name)} ${theme.fg("muted", `(${process.id})`)}`,
116
- ` pid: ${process.pid} status: ${status}`,
117
- ` started: ${theme.fg("muted", formatTimestamp(process.startTime))}`,
118
- ` ended: ${theme.fg("muted", formatTimestamp(process.endTime))}`,
119
- ` runtime: ${theme.fg("muted", formatRuntime(process.startTime, process.endTime))}`,
120
- ].join("\n"),
121
- );
122
- }
123
-
124
- const fields: Array<
125
- { label: string; value: string; showCollapsed?: boolean } | Text
126
- > = [new Text(lines.join("\n"), 0, 0)];
127
-
128
- const running = details.processes.filter(
129
- (p) => p.status === "running" || p.status === "terminating",
130
- );
131
- const finishedOk = details.processes.filter(
132
- (p) => p.status === "exited" && p.success,
133
- ).length;
134
- const failed = details.processes.filter(
135
- (p) => p.status === "exited" && !p.success,
136
- ).length;
137
- const killed = details.processes.filter((p) => p.status === "killed").length;
138
-
139
- const runningSummary =
140
- running.length > 0
141
- ? running
142
- .slice(0, 3)
143
- .map(
144
- (p) =>
145
- `${theme.fg("accent", `"${p.name}"`)} [${formatStatusTag(p, theme)}]`,
146
- )
147
- .join(", ")
148
- : theme.fg("muted", "no running process");
149
-
150
- const restParts: string[] = [];
151
- if (finishedOk > 0) restParts.push(`${finishedOk} finished`);
152
- if (failed > 0) restParts.push(`${failed} failed`);
153
- if (killed > 0) restParts.push(`${killed} killed`);
154
- const restSummary =
155
- restParts.length > 0 ? theme.fg("muted", ` + ${restParts.join(", ")}`) : "";
156
-
157
- fields.push({
158
- label: "Processes",
159
- value: runningSummary + restSummary,
160
- showCollapsed: true,
161
- });
162
-
163
- const footerItems: Array<{
164
- label: string;
165
- value: string;
166
- }> = [];
167
- if (runningCount > 0) {
168
- footerItems.push({ label: "running", value: String(runningCount) });
169
- }
170
- if (failed > 0) {
171
- footerItems.push({ label: "failed", value: String(failed) });
172
- }
173
- if (killed > 0) {
174
- footerItems.push({ label: "killed", value: String(killed) });
175
- }
176
-
177
- return new ToolBody(
178
- {
179
- fields,
180
- footer:
181
- footerItems.length > 0
182
- ? new ToolFooter(theme, { items: footerItems, separator: " | " })
183
- : undefined,
184
- },
185
- options,
186
- theme,
187
- );
188
- }