@anhlp/alp 0.0.0-beta.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 (175) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +7 -0
  3. package/README.md +416 -0
  4. package/dist/THIRD-PARTY-NOTICES.txt +626 -0
  5. package/dist/acp.js +19723 -0
  6. package/dist/alpd.js +41053 -0
  7. package/docs/modular-monorepo.md +210 -0
  8. package/package.json +126 -0
  9. package/packages/cli/README.md +7 -0
  10. package/packages/cli/dist/THIRD-PARTY-NOTICES.txt +626 -0
  11. package/packages/cli/dist/acp/agent.d.ts +29 -0
  12. package/packages/cli/dist/acp/agent.js +390 -0
  13. package/packages/cli/dist/acp/main.d.ts +4 -0
  14. package/packages/cli/dist/acp/main.js +23 -0
  15. package/packages/cli/dist/acp.js +19723 -0
  16. package/packages/cli/dist/alpd.js +41053 -0
  17. package/packages/cli/dist/backup.d.ts +4 -0
  18. package/packages/cli/dist/backup.js +146 -0
  19. package/packages/cli/dist/build-manifest.json +139 -0
  20. package/packages/cli/dist/cli.d.ts +2 -0
  21. package/packages/cli/dist/cli.js +24 -0
  22. package/packages/cli/dist/daemon.d.ts +35 -0
  23. package/packages/cli/dist/daemon.js +122 -0
  24. package/packages/cli/dist/index.d.ts +1 -0
  25. package/packages/cli/dist/index.js +5 -0
  26. package/packages/cli/dist/interfaces.d.ts +16 -0
  27. package/packages/cli/dist/interfaces.js +96 -0
  28. package/packages/cli/dist/library.d.ts +140 -0
  29. package/packages/cli/dist/library.js +319 -0
  30. package/packages/cli/dist/paths.d.ts +3 -0
  31. package/packages/cli/dist/paths.js +18 -0
  32. package/packages/cli/dist/projects.d.ts +9 -0
  33. package/packages/cli/dist/projects.js +159 -0
  34. package/packages/cli/dist/sessions.d.ts +39 -0
  35. package/packages/cli/dist/sessions.js +425 -0
  36. package/packages/cli/dist/tasks.d.ts +29 -0
  37. package/packages/cli/dist/tasks.js +345 -0
  38. package/packages/cli/dist/usage.d.ts +3 -0
  39. package/packages/cli/dist/usage.js +72 -0
  40. package/packages/client/README.md +24 -0
  41. package/packages/client/dist/acp-probe.d.ts +44 -0
  42. package/packages/client/dist/acp-probe.js +37 -0
  43. package/packages/client/dist/antigravity-probe.d.ts +34 -0
  44. package/packages/client/dist/antigravity-probe.js +95 -0
  45. package/packages/client/dist/command.d.ts +5 -0
  46. package/packages/client/dist/command.js +33 -0
  47. package/packages/client/dist/doctor.d.ts +13 -0
  48. package/packages/client/dist/doctor.js +587 -0
  49. package/packages/client/dist/index.d.ts +101 -0
  50. package/packages/client/dist/index.js +422 -0
  51. package/packages/client/dist/ipc.d.ts +42 -0
  52. package/packages/client/dist/ipc.js +69 -0
  53. package/packages/client/dist/library-test.d.ts +94 -0
  54. package/packages/client/dist/library-test.js +37 -0
  55. package/packages/client/dist/mcp-probe.d.ts +24 -0
  56. package/packages/client/dist/mcp-probe.js +147 -0
  57. package/packages/client/dist/portable.d.ts +18 -0
  58. package/packages/client/dist/portable.js +27 -0
  59. package/packages/client/dist/prompt.d.ts +32 -0
  60. package/packages/client/dist/prompt.js +59 -0
  61. package/packages/client/dist/render.d.ts +6 -0
  62. package/packages/client/dist/render.js +120 -0
  63. package/packages/client/dist/rpc/client.d.ts +82 -0
  64. package/packages/client/dist/rpc/client.js +292 -0
  65. package/packages/client/dist/rpc/errors.d.ts +42 -0
  66. package/packages/client/dist/rpc/errors.js +34 -0
  67. package/packages/client/dist/rpc/framing.d.ts +21 -0
  68. package/packages/client/dist/rpc/framing.js +57 -0
  69. package/packages/client/dist/rpc/handshake.d.ts +19 -0
  70. package/packages/client/dist/rpc/handshake.js +8 -0
  71. package/packages/client/dist/rpc/reconnect.d.ts +97 -0
  72. package/packages/client/dist/rpc/reconnect.js +227 -0
  73. package/packages/client/dist/rpc/timers.d.ts +11 -0
  74. package/packages/client/dist/rpc/timers.js +11 -0
  75. package/packages/client/dist/service.d.ts +50 -0
  76. package/packages/client/dist/service.js +143 -0
  77. package/packages/client/dist/setup.d.ts +2 -0
  78. package/packages/client/dist/setup.js +146 -0
  79. package/packages/client/dist/supervise.d.ts +12 -0
  80. package/packages/client/dist/supervise.js +81 -0
  81. package/packages/client/dist/websocket.d.ts +51 -0
  82. package/packages/client/dist/websocket.js +190 -0
  83. package/packages/core/README.md +7 -0
  84. package/packages/core/dist/adapter.d.ts +20 -0
  85. package/packages/core/dist/adapter.js +30 -0
  86. package/packages/core/dist/atomic.d.ts +2 -0
  87. package/packages/core/dist/atomic.js +28 -0
  88. package/packages/core/dist/backup.d.ts +27 -0
  89. package/packages/core/dist/backup.js +582 -0
  90. package/packages/core/dist/beads.d.ts +65 -0
  91. package/packages/core/dist/beads.js +213 -0
  92. package/packages/core/dist/delegation.d.ts +2 -0
  93. package/packages/core/dist/delegation.js +32 -0
  94. package/packages/core/dist/errors.d.ts +4 -0
  95. package/packages/core/dist/errors.js +11 -0
  96. package/packages/core/dist/formulas.d.ts +25 -0
  97. package/packages/core/dist/formulas.js +201 -0
  98. package/packages/core/dist/hook-run.d.ts +44 -0
  99. package/packages/core/dist/hook-run.js +60 -0
  100. package/packages/core/dist/hooks.d.ts +35 -0
  101. package/packages/core/dist/hooks.js +30 -0
  102. package/packages/core/dist/index.d.ts +30 -0
  103. package/packages/core/dist/index.js +63 -0
  104. package/packages/core/dist/init.d.ts +30 -0
  105. package/packages/core/dist/init.js +88 -0
  106. package/packages/core/dist/library-edit.d.ts +90 -0
  107. package/packages/core/dist/library-edit.js +315 -0
  108. package/packages/core/dist/library.d.ts +28 -0
  109. package/packages/core/dist/library.js +108 -0
  110. package/packages/core/dist/log.d.ts +37 -0
  111. package/packages/core/dist/log.js +156 -0
  112. package/packages/core/dist/migrations.d.ts +9 -0
  113. package/packages/core/dist/migrations.js +95 -0
  114. package/packages/core/dist/models.d.ts +11 -0
  115. package/packages/core/dist/models.js +44 -0
  116. package/packages/core/dist/permissions.d.ts +14 -0
  117. package/packages/core/dist/permissions.js +229 -0
  118. package/packages/core/dist/profiles.d.ts +27 -0
  119. package/packages/core/dist/profiles.js +122 -0
  120. package/packages/core/dist/promptsafe.d.ts +1 -0
  121. package/packages/core/dist/promptsafe.js +13 -0
  122. package/packages/core/dist/providers.d.ts +90 -0
  123. package/packages/core/dist/providers.js +101 -0
  124. package/packages/core/dist/resolver.d.ts +41 -0
  125. package/packages/core/dist/resolver.js +240 -0
  126. package/packages/core/dist/safe-path.d.ts +2 -0
  127. package/packages/core/dist/safe-path.js +24 -0
  128. package/packages/core/dist/shell.d.ts +16 -0
  129. package/packages/core/dist/shell.js +22 -0
  130. package/packages/core/dist/state-schema.d.ts +9 -0
  131. package/packages/core/dist/state-schema.js +54 -0
  132. package/packages/core/dist/task-rows.d.ts +47 -0
  133. package/packages/core/dist/task-rows.js +45 -0
  134. package/packages/core/dist/tasks.d.ts +128 -0
  135. package/packages/core/dist/tasks.js +835 -0
  136. package/packages/core/dist/teams.d.ts +69 -0
  137. package/packages/core/dist/teams.js +159 -0
  138. package/packages/core/dist/template-path.d.ts +2 -0
  139. package/packages/core/dist/template-path.js +10 -0
  140. package/packages/core/dist/trust.d.ts +8 -0
  141. package/packages/core/dist/trust.js +47 -0
  142. package/packages/core/dist/types.d.ts +51 -0
  143. package/packages/core/dist/upgrade.d.ts +10 -0
  144. package/packages/core/dist/upgrade.js +200 -0
  145. package/packages/core/dist/user-settings.d.ts +6 -0
  146. package/packages/core/dist/user-settings.js +72 -0
  147. package/packages/core/dist/validation.d.ts +17 -0
  148. package/packages/core/dist/validation.js +295 -0
  149. package/packages/core/dist/verify.d.ts +15 -0
  150. package/packages/core/dist/verify.js +123 -0
  151. package/packages/core/dist/workflow.d.ts +14 -0
  152. package/packages/core/dist/workflow.js +24 -0
  153. package/templates/ALP.md +52 -0
  154. package/templates/agents/lead/AGENT.md +90 -0
  155. package/templates/agents/main/AGENT.md +150 -0
  156. package/templates/agents/oracle/AGENT.md +44 -0
  157. package/templates/agents/peer/AGENT.md +55 -0
  158. package/templates/agents/reviewer/AGENT.md +80 -0
  159. package/templates/agents/supervisor/AGENT.md +72 -0
  160. package/templates/providers/antigravity.json +6 -0
  161. package/templates/providers/opencode.json +7 -0
  162. package/templates/role-skills.json +8 -0
  163. package/templates/skills/bug-loop/SKILL.md +44 -0
  164. package/templates/skills/bug-loop/references/test-proof.md +25 -0
  165. package/templates/skills/goal-griller/SKILL.md +49 -0
  166. package/templates/skills/prompt-leverage/SKILL.md +37 -0
  167. package/templates/skills/prompt-leverage/references/brief-template.md +27 -0
  168. package/templates/skills/sequence-execution-plan/SKILL.md +42 -0
  169. package/templates/skills/sequence-execution-plan/references/plan-template.md +22 -0
  170. package/templates/skills/smart-commits/SKILL.md +40 -0
  171. package/templates/skills/xia/SKILL.md +42 -0
  172. package/templates/teams/cafe/HOUSE_RULES.md +1 -0
  173. package/templates/teams/cafe/team.json +15 -0
  174. package/templates/teams/pho/HOUSE_RULES.md +1 -0
  175. package/templates/teams/pho/team.json +14 -0
@@ -0,0 +1,39 @@
1
+ export function printer(json: any): (envelope: any) => void;
2
+ /** Reads answers to questions for the user from the terminal, one question at a time. */
3
+ export function answerer(client: any): {
4
+ accept(event: any): void;
5
+ close(): void;
6
+ };
7
+ /** Streams a tree until its root is idle; the first Ctrl-C interrupts it, the second detaches. */
8
+ export function follow(client: any, rootId: any, print: any, { untilIdle, interactive }: {
9
+ untilIdle: any;
10
+ interactive?: boolean | undefined;
11
+ }): Promise<any>;
12
+ export function run(args: any): Promise<void>;
13
+ export function ps(args: any): Promise<void>;
14
+ export function attach(args: any): Promise<void>;
15
+ /** Steers a running root, prompts an idle one, or resumes a closed one; then follows it until idle. */
16
+ export function send(args: any): Promise<void>;
17
+ export function questions(args: any): Promise<void>;
18
+ export function answer(args: any): Promise<void>;
19
+ /** One tree as text: sessions by depth, then questions, unmerged worktrees and leases. */
20
+ export function renderStatus(status: any, root: any): string;
21
+ /** A live dashboard of every tree, or of one, refreshed every second. */
22
+ export function top(args: any): Promise<void>;
23
+ /** The tree's assignment log: delegations, mail, handoffs, worktrees and questions to the user. */
24
+ export function log(args: any): Promise<void>;
25
+ /** The project board every agent on the project shares: live claims, then decisions and findings. */
26
+ export function board(args: any): Promise<void>;
27
+ export function printPauses({ all, runtimes, parked }: {
28
+ all: any;
29
+ runtimes: any;
30
+ parked: any;
31
+ }, always?: boolean): void;
32
+ /** alp pause: hold delegation and wakes on one runtime, or all; --now also parks running assignments. */
33
+ export function pause(args: any): Promise<void>;
34
+ /** alp resume: lift a pause; parked assignments continue where they stopped. */
35
+ export function resume(args: any): Promise<void>;
36
+ /** alp language: the language the user reads (ALPD §54); new sessions use it. */
37
+ export function recall(args: any): Promise<void>;
38
+ /** alp web: opens the web app alpd serves on 127.0.0.1, with the token in the address (ALPD §61). */
39
+ export function interrupt(args: any): Promise<void>;
@@ -0,0 +1,425 @@
1
+ // packages/cli/src/sessions.js
2
+ import { randomUUID } from "node:crypto";
3
+ import path from "node:path";
4
+ import { createInterface } from "node:readline";
5
+ import { parseArgs } from "node:util";
6
+ import { ago, duration, renderLog, renderPs } from "@anhlp/alp-client/render";
7
+ import { isTaskId } from "@anhlp/alp-core/tasks";
8
+ import { connect } from "@anhlp/alp-client/index";
9
+ import { UsageError } from "./usage.js";
10
+ import { start, running, stop } from "./daemon.js";
11
+ import { taskProject } from "./tasks.js";
12
+ function printer(json) {
13
+ const depth = /* @__PURE__ */ new Map();
14
+ const agents = /* @__PURE__ */ new Map();
15
+ const printed = /* @__PURE__ */ new Set();
16
+ const latest = /* @__PURE__ */ new Map();
17
+ const line = (sessionId, text) => console.log(`${" ".repeat(depth.get(sessionId) ?? 0)}${text}`);
18
+ return (envelope) => {
19
+ if (json) {
20
+ console.log(JSON.stringify(envelope));
21
+ return;
22
+ }
23
+ const { sessionId, event } = envelope;
24
+ if (event.type === "session.opened") {
25
+ const { session } = event;
26
+ depth.set(sessionId, session.parentId ? (depth.get(session.parentId) ?? 0) + 1 : 0);
27
+ agents.set(sessionId, session.agent);
28
+ line(sessionId, `\u25B8 ${session.agent} (${session.runtime}:${session.model}, ${session.mode}) ${sessionId}`);
29
+ } else if (event.type === "item") {
30
+ const { item } = event;
31
+ latest.set(item.id, { sessionId, item });
32
+ if (item.kind === "tool_call" && item.status !== "running" && !printed.has(item.id)) {
33
+ printed.add(item.id);
34
+ const detail = item.detail.type === "shell" ? item.detail.command : item.name === "alp_delegate" ? `delegate \u2192 ${item.detail.input?.agent}` : item.name;
35
+ line(sessionId, ` ${item.status === "failed" ? "\u2717" : "\u2713"} ${detail}`);
36
+ }
37
+ if (item.kind === "notice") line(sessionId, ` ${item.level === "error" ? "\u203C" : item.level === "warning" ? "!" : "\u2139"} ${item.text}`);
38
+ if (item.kind === "todo") {
39
+ line(sessionId, " tasks:");
40
+ for (const entry of item.items) line(sessionId, ` ${entry.status === "completed" ? "\u2611" : entry.status === "in_progress" ? "\u25D0" : "\u2610"} ${entry.text}`);
41
+ }
42
+ } else if (event.type === "mail") {
43
+ line(sessionId, ` \u2709 ${event.mail.kind} from ${event.mail.from}`);
44
+ } else if (event.type === "question") {
45
+ const { question } = event;
46
+ line(sessionId, ` ? ${question.agent} asks you [${question.id}]: ${question.body}${question.options?.length ? ` (${question.options.join(" | ")})` : ""}`);
47
+ line(sessionId, ` answer here, or with: alp answer ${question.id} <text>`);
48
+ } else if (event.type === "question.resolved") {
49
+ line(sessionId, ` \u21B3 ${event.questionId} ${event.outcome}${event.answer !== void 0 ? `: ${event.answer}` : ""}`);
50
+ } else if (event.type === "turn.ended") {
51
+ for (const [id, entry] of latest) {
52
+ if (entry.sessionId !== sessionId || printed.has(id) || entry.item.kind !== "assistant_message") continue;
53
+ printed.add(id);
54
+ line(sessionId, ` ${agents.get(sessionId) ?? "agent"}: ${entry.item.text.replace(/\n/g, `
55
+ ${" ".repeat((depth.get(sessionId) ?? 0) + 2)}`)}`);
56
+ }
57
+ if (event.state !== "completed") line(sessionId, ` turn ${event.state}${event.error ? `: ${event.error.message}` : ""}`);
58
+ } else if (event.type === "prompt.failed") {
59
+ line(sessionId, ` prompt failed: ${event.error.message}`);
60
+ } else if (event.type === "session.failed") {
61
+ line(sessionId, ` runtime failed: ${event.error.message}`);
62
+ }
63
+ };
64
+ }
65
+ function answerer(client) {
66
+ const waiting = [];
67
+ let rl;
68
+ const ask = () => {
69
+ if (rl || !waiting.length) return;
70
+ const question = waiting[0];
71
+ rl = createInterface({ input: process.stdin, output: process.stderr });
72
+ rl.question(`answer ${question.id}> `, (text) => {
73
+ rl.close();
74
+ rl = void 0;
75
+ waiting.shift();
76
+ if (text.trim()) client.request("question.answer", { questionId: question.id, text }).catch((error) => console.error(`alp: ${error.message}`));
77
+ ask();
78
+ });
79
+ };
80
+ return {
81
+ accept(event) {
82
+ if (event.type === "question") {
83
+ waiting.push(event.question);
84
+ ask();
85
+ }
86
+ if (event.type === "question.resolved") {
87
+ const index = waiting.findIndex((question) => question.id === event.questionId);
88
+ if (index === 0 && rl) {
89
+ rl.close();
90
+ rl = void 0;
91
+ console.error("");
92
+ }
93
+ if (index >= 0) waiting.splice(index, 1);
94
+ ask();
95
+ }
96
+ },
97
+ close() {
98
+ rl?.close();
99
+ rl = void 0;
100
+ }
101
+ };
102
+ }
103
+ function follow(client, rootId, print, { untilIdle, interactive = false }) {
104
+ const answering = interactive ? answerer(client) : void 0;
105
+ const done = new Promise((resolve, reject) => {
106
+ let failed = false;
107
+ let interrupted = false;
108
+ const finish = () => {
109
+ process.off("SIGINT", onSignal);
110
+ answering?.close();
111
+ resolve(!failed);
112
+ };
113
+ const onSignal = () => {
114
+ if (interrupted) {
115
+ finish();
116
+ return;
117
+ }
118
+ interrupted = true;
119
+ console.error("Interrupting\u2026 press Ctrl-C again to detach.");
120
+ client.request("session.interrupt", { sessionId: rootId }).catch(() => {
121
+ });
122
+ };
123
+ process.on("SIGINT", onSignal);
124
+ client.onClose((error) => {
125
+ process.off("SIGINT", onSignal);
126
+ reject(error);
127
+ });
128
+ let rootEnded = false;
129
+ client.onEvent((envelope) => {
130
+ print(envelope);
131
+ answering?.accept(envelope.event);
132
+ const { sessionId, event } = envelope;
133
+ if (event.type === "turn.ended") {
134
+ if (sessionId === rootId) {
135
+ failed = event.state !== "completed";
136
+ rootEnded = true;
137
+ }
138
+ if (!untilIdle || !rootEnded) return;
139
+ setTimeout(() => {
140
+ client.request("session.get", { sessionId: rootId }).then(({ session }) => {
141
+ if (!session.busy) finish();
142
+ }, finish);
143
+ }, 50);
144
+ return;
145
+ }
146
+ if (sessionId !== rootId) return;
147
+ if (event.type === "turn.started") {
148
+ rootEnded = false;
149
+ } else if (event.type === "session.closed" || event.type === "session.failed" || event.type === "prompt.failed") {
150
+ failed ||= event.type !== "session.closed";
151
+ finish();
152
+ }
153
+ });
154
+ });
155
+ done.catch(() => {
156
+ });
157
+ return done;
158
+ }
159
+ async function run(args) {
160
+ const { values, positionals } = parseArgs({ args, allowPositionals: true, options: {
161
+ agent: { type: "string" },
162
+ team: { type: "string" },
163
+ profile: { type: "string" },
164
+ workflow: { type: "string" },
165
+ model: { type: "string" },
166
+ mode: { type: "string" },
167
+ thinking: { type: "string" },
168
+ project: { type: "string" },
169
+ json: { type: "boolean" }
170
+ } });
171
+ const text = positionals.join(" ").trim();
172
+ if (!text) throw new UsageError();
173
+ const teams = [values.team, values.profile, values.workflow].filter(Boolean);
174
+ if (new Set(teams).size > 1) throw new UsageError();
175
+ const client = await connect(await start(), { name: "alp-cli", version: "1" });
176
+ try {
177
+ const spec = { cwd: path.resolve(values.project ?? process.cwd()), persist: true, agent: values.agent, workflow: teams[0], model: values.model, mode: values.mode, thinking: values.thinking };
178
+ const sessionId = `cli-${randomUUID()}`;
179
+ const done = follow(client, sessionId, printer(values.json), { untilIdle: true, interactive: !values.json && process.stdin.isTTY });
180
+ await client.request("session.create", { sessionId, spec });
181
+ await client.request("session.prompt", { sessionId, clientMessageId: randomUUID(), content: [{ type: "text", text }] });
182
+ if (!await done) process.exitCode = 1;
183
+ await client.request("session.release", { sessionId }).catch(() => {
184
+ });
185
+ } finally {
186
+ client.close();
187
+ }
188
+ }
189
+ async function ps(args) {
190
+ const { values, positionals } = parseArgs({ args, allowPositionals: true, options: { all: { type: "boolean" } } });
191
+ if (positionals.length) throw new UsageError();
192
+ const client = await running();
193
+ const [{ sessions }, pauses] = await Promise.all([client.request("session.list", { includeClosed: values.all }), client.request("daemon.pauses")]).finally(() => client.close());
194
+ printPauses(pauses, false);
195
+ if (!sessions.length) {
196
+ console.log(values.all ? "No sessions" : "No live sessions");
197
+ return;
198
+ }
199
+ for (const line of renderPs(sessions)) console.log(line);
200
+ }
201
+ async function attach(args) {
202
+ const { values, positionals } = parseArgs({ args, allowPositionals: true, options: { json: { type: "boolean" } } });
203
+ if (positionals.length !== 1) throw new UsageError();
204
+ const client = await running();
205
+ try {
206
+ const done = follow(client, positionals[0], printer(values.json), { untilIdle: false, interactive: !values.json && process.stdin.isTTY });
207
+ const { session } = await client.request("session.attach", { sessionId: positionals[0] });
208
+ if (session.id !== positionals[0]) console.error(`Following root ${session.id}`);
209
+ if (session.status === "idle" || session.status === "running") await done;
210
+ else console.error(`Session is ${session.lastError?.code ?? session.status}; resume it with: alp send ${session.id} <text>`);
211
+ } finally {
212
+ client.close();
213
+ }
214
+ }
215
+ async function send(args) {
216
+ const { values, positionals: [sessionId, ...words] } = parseArgs({ args, allowPositionals: true, options: { json: { type: "boolean" } } });
217
+ const text = words.join(" ").trim();
218
+ if (!sessionId || !text) throw new UsageError();
219
+ const client = await connect(await start(), { name: "alp-cli", version: "1" });
220
+ try {
221
+ const { session } = await client.request("session.get", { sessionId });
222
+ if (session.parentId) {
223
+ await client.request("session.message", { sessionId, text });
224
+ if (!values.json) console.error(`Sent to ${session.agent} as mail from the user`);
225
+ return;
226
+ }
227
+ const done = follow(client, sessionId, printer(values.json), { untilIdle: true, interactive: !values.json && process.stdin.isTTY });
228
+ const live = session.status === "idle" || session.status === "running";
229
+ if (live) await client.request("session.attach", { sessionId, replay: false });
230
+ else await client.request("session.create", { sessionId, spec: { cwd: session.projectRoot } });
231
+ const steer = live && !!session.activeTurnId;
232
+ await client.request("session.prompt", { sessionId, clientMessageId: randomUUID(), delivery: steer ? "steer" : "auto", content: [{ type: "text", text }] });
233
+ if (!values.json) console.error(steer ? "Steered the running turn" : live ? "Started a turn" : "Resumed the session");
234
+ if (!await done) process.exitCode = 1;
235
+ await client.request("session.release", { sessionId }).catch(() => {
236
+ });
237
+ } finally {
238
+ client.close();
239
+ }
240
+ }
241
+ async function questions(args) {
242
+ const { values, positionals } = parseArgs({ args, allowPositionals: true, options: { json: { type: "boolean" } } });
243
+ if (positionals.length) throw new UsageError();
244
+ const client = await running();
245
+ const { questions: pending } = await client.request("question.list").finally(() => client.close());
246
+ if (values.json) {
247
+ console.log(JSON.stringify(pending));
248
+ return;
249
+ }
250
+ if (!pending.length) {
251
+ console.log("No questions wait for you");
252
+ return;
253
+ }
254
+ for (const question of pending) {
255
+ console.log(`${question.id} ${question.agent} ${ago(question.askedAt)} ago root ${question.rootId}`);
256
+ console.log(` ${question.body.replace(/\n/g, "\n ")}`);
257
+ if (question.options?.length) console.log(` options: ${question.options.join(" | ")}`);
258
+ }
259
+ }
260
+ async function answer(args) {
261
+ const { values, positionals: [questionId, ...words] } = parseArgs({ args, allowPositionals: true, options: { dismiss: { type: "boolean" }, reason: { type: "string" } } });
262
+ const text = words.join(" ").trim();
263
+ if (!questionId || (values.dismiss ? text : !text)) throw new UsageError();
264
+ const client = await running();
265
+ const result = await client.request("question.answer", values.dismiss ? { questionId, dismiss: true, reason: values.reason } : { questionId, text }).finally(() => client.close());
266
+ console.log(`${values.dismiss ? "Dismissed" : "Answered"} ${result.questionId}`);
267
+ }
268
+ function renderStatus(status, root) {
269
+ const lines = [`${root?.title ? `"${root.title}" ` : ""}${status.rootId} ${status.sessions[0]?.projectRoot ?? ""}`];
270
+ const depth = /* @__PURE__ */ new Map();
271
+ const assignments = new Map(status.assignments.map((assignment) => [assignment.id, assignment]));
272
+ for (const session of status.sessions) {
273
+ depth.set(session.id, session.parentId ? (depth.get(session.parentId) ?? 0) + 1 : 0);
274
+ const assignment = assignments.get(session.id);
275
+ const where = assignment?.worktree ? ` worktree ${assignment.worktree.branch}` : "";
276
+ const mail = session.unreadMail ? ` \u2709${session.unreadMail}` : "";
277
+ lines.push(`${" ".repeat(depth.get(session.id) + 1)}${session.agent.padEnd(9)} ${session.state.padEnd(14)} idle ${duration(session.idleMs).padEnd(7)} ${session.runtime}:${session.model} ${session.mode}${where}${mail}`);
278
+ }
279
+ for (const question of status.questions) lines.push(` ? ${question.id} ${question.agent} asks (${ago(question.askedAt)}): ${question.body.split("\n")[0]}`);
280
+ for (const worktree of status.worktrees) lines.push(` \u2387 unmerged ${worktree.branch} from ${worktree.agent}: ${worktree.files.length} files${worktree.stat ? ` (${worktree.stat.trim()})` : ""}`);
281
+ for (const lease of status.leases) lines.push(` \u26BF ${lease.agent} holds the write lease on ${lease.checkout}`);
282
+ for (const claim of status.claims ?? []) lines.push(` \u2691 ${claim.agent} claims ${claim.paths.join(", ")}${claim.task ? ` for ${claim.task}` : ""}: ${claim.body.split("\n")[0].slice(0, 100)}`);
283
+ return lines.join("\n");
284
+ }
285
+ async function top(args) {
286
+ const { values, positionals } = parseArgs({ args, allowPositionals: true, options: { once: { type: "boolean" } } });
287
+ if (positionals.length > 1) throw new UsageError();
288
+ const client = await running();
289
+ const frame = async () => {
290
+ const { sessions } = await client.request("session.list", { rootsOnly: true });
291
+ const roots = positionals.length ? sessions.filter((session) => session.id === positionals[0]) : sessions;
292
+ if (positionals.length && !roots.length) throw new Error(`Session ${positionals[0]} is not a live root`);
293
+ const blocks = [];
294
+ for (const root of roots) {
295
+ const { status } = await client.request("session.status", { sessionId: root.id }).catch(() => ({}));
296
+ if (status) blocks.push(renderStatus(status, root));
297
+ }
298
+ return `alp top ${(/* @__PURE__ */ new Date()).toLocaleTimeString()} ${roots.length} live ${roots.length === 1 ? "tree" : "trees"}
299
+
300
+ ${blocks.join("\n\n") || "No live sessions"}`;
301
+ };
302
+ try {
303
+ if (values.once || !process.stdout.isTTY) {
304
+ console.log(await frame());
305
+ return;
306
+ }
307
+ let stop2 = false;
308
+ process.once("SIGINT", () => {
309
+ stop2 = true;
310
+ });
311
+ while (!stop2) {
312
+ const text = await frame();
313
+ process.stdout.write(`\x1B[2J\x1B[H${text}
314
+
315
+ (Ctrl-C to exit)
316
+ `);
317
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
318
+ }
319
+ } finally {
320
+ client.close();
321
+ }
322
+ }
323
+ async function log(args) {
324
+ const { values, positionals } = parseArgs({ args, allowPositionals: true, options: { json: { type: "boolean" } } });
325
+ if (positionals.length !== 1) throw new UsageError();
326
+ const client = await running();
327
+ const { rootId, entries } = await client.request("session.log", { sessionId: positionals[0] }).finally(() => client.close());
328
+ if (values.json) {
329
+ for (const entry of entries) console.log(JSON.stringify(entry));
330
+ return;
331
+ }
332
+ if (!entries.length) {
333
+ console.log(`No assignment log for ${rootId}`);
334
+ return;
335
+ }
336
+ for (const line of renderLog(rootId, entries)) console.log(line);
337
+ }
338
+ async function board(args) {
339
+ const { values, positionals } = parseArgs({ args, allowPositionals: true, options: { project: { type: "string" }, json: { type: "boolean" } } });
340
+ if (positionals.length) throw new UsageError();
341
+ const projectRoot = path.resolve(values.project ?? process.cwd());
342
+ const client = await running();
343
+ const { pins } = await client.request("board.list", { projectRoot }).finally(() => client.close());
344
+ if (values.json) {
345
+ console.log(JSON.stringify(pins));
346
+ return;
347
+ }
348
+ if (!pins.length) {
349
+ console.log(`The board for ${projectRoot} is empty`);
350
+ return;
351
+ }
352
+ console.log(`Project board ${projectRoot}`);
353
+ for (const pin of pins) {
354
+ const mark = pin.kind === "claim" ? "\u2691" : pin.kind === "decision" ? "\u25C6" : "\u2022";
355
+ console.log(`${mark} ${pin.kind.padEnd(8)} ${pin.id} ${pin.agent} ${ago(pin.at)} ago${pin.paths ? ` [${pin.paths.join(", ")}]` : ""}`);
356
+ console.log(` ${pin.body.replace(/\n/g, "\n ")}`);
357
+ }
358
+ }
359
+ function printPauses({ all, runtimes, parked }, always = true) {
360
+ const line = (name, pause2) => console.log(`\u23F8 ${name} paused since ${pause2.since.slice(0, 16).replace("T", " ")} by ${pause2.by}: ${pause2.reason}${pause2.resetsAt ? `; the limit resets ${pause2.resetsAt}` : ""}${pause2.checkedAt ? `; ${name} last asked ${ago(pause2.checkedAt)} ago` : ""}`);
361
+ if (all) line("ALP", all);
362
+ for (const [kind, pause2] of Object.entries(runtimes)) line(kind, pause2);
363
+ for (const entry of parked) console.log(` parked ${entry.agent} ${entry.assignmentId} (${entry.runtime}): ${entry.reason}`);
364
+ if (always && !all && !Object.keys(runtimes).length) console.log("Nothing is paused");
365
+ }
366
+ async function pause(args) {
367
+ const { values, positionals } = parseArgs({ args, allowPositionals: true, options: { now: { type: "boolean" }, message: { type: "string", short: "m" }, json: { type: "boolean" } } });
368
+ if (positionals.length > 1 || positionals[0] && !["codex", "claude", "status"].includes(positionals[0])) throw new UsageError();
369
+ const client = await running();
370
+ const state = await (positionals[0] === "status" ? client.request("daemon.pauses") : client.request("daemon.pause", { ...positionals[0] ? { runtime: positionals[0] } : {}, now: !!values.now, ...values.message ? { reason: values.message } : {} })).finally(() => client.close());
371
+ if (values.json) console.log(JSON.stringify(state));
372
+ else printPauses(state);
373
+ }
374
+ async function resume(args) {
375
+ const { values, positionals } = parseArgs({ args, allowPositionals: true, options: { json: { type: "boolean" } } });
376
+ if (positionals.length > 1 || positionals[0] && !["codex", "claude"].includes(positionals[0])) throw new UsageError();
377
+ const client = await running();
378
+ const state = await client.request("daemon.resume", positionals[0] ? { runtime: positionals[0] } : {}).finally(() => client.close());
379
+ if (values.json) console.log(JSON.stringify(state));
380
+ else printPauses(state);
381
+ }
382
+ async function recall(args) {
383
+ const { values, positionals } = parseArgs({ args, allowPositionals: true, options: { project: { type: "string" }, json: { type: "boolean" } } });
384
+ const [target, ...words] = positionals;
385
+ const question = words.join(" ").trim();
386
+ if (!target || !question) throw new UsageError();
387
+ const byTask = isTaskId(target);
388
+ const client = await running();
389
+ const answer2 = await client.request("assignment.recall", {
390
+ ...byTask ? { taskId: target, projectRoot: await taskProject(values.project) } : { assignmentId: target },
391
+ question
392
+ }).finally(() => client.close());
393
+ if (values.json) {
394
+ console.log(JSON.stringify(answer2));
395
+ return;
396
+ }
397
+ console.log(`${answer2.agent} (assignment ${answer2.assignmentId}${answer2.taskId ? `, task ${answer2.taskId}` : ""}, finished ${ago(answer2.finishedAt)} ago):`);
398
+ console.log(answer2.answer);
399
+ }
400
+ async function interrupt(args) {
401
+ if (args.length !== 1) throw new UsageError();
402
+ const client = await running();
403
+ await client.request("session.interrupt", { sessionId: args[0] }).finally(() => client.close());
404
+ console.log("Interrupted");
405
+ }
406
+ export {
407
+ answer,
408
+ answerer,
409
+ attach,
410
+ board,
411
+ follow,
412
+ interrupt,
413
+ log,
414
+ pause,
415
+ printPauses,
416
+ printer,
417
+ ps,
418
+ questions,
419
+ recall,
420
+ renderStatus,
421
+ resume,
422
+ run,
423
+ send,
424
+ top
425
+ };
@@ -0,0 +1,29 @@
1
+ export namespace STATUS_MARK {
2
+ let open: string;
3
+ let in_progress: string;
4
+ let review: string;
5
+ let closed: string;
6
+ }
7
+ /** The ALP project a task command works on: --project, or the current directory. */
8
+ /** alp permissions: the profile each agent runs with, from .alp/settings.json and $ALP_HOME/settings.json. */
9
+ export function taskProject(directory: any): Promise<string>;
10
+ export function taskRow(task: any, tasks: any): string;
11
+ export function warnUnreadable(errors: any): void;
12
+ /** The project's tasks: open, in progress and in review by default; ready lists what nothing blocks. */
13
+ export function tasksCommand(args: any): Promise<void>;
14
+ /** The GitHub CLI, for gh:pr and gh:run gates. */
15
+ export function gh(args: any, { cwd }: {
16
+ cwd: any;
17
+ }): Promise<any>;
18
+ /** alp tasks gates: clears GitHub gates that are done and lists the open ones. alp tasks compact: shrinks old closed tasks. */
19
+ export function taskMaintenance([command]: [any], args: any): Promise<void>;
20
+ /** alp tasks export / import: interchange with beads through its JSONL issue format. */
21
+ export function beads(command: any, args: any): Promise<void>;
22
+ /** alp formula: workflow templates that pour into an epic with a task per step. */
23
+ export function formulaCommand([action, ...args]: [any, ...any[]]): Promise<void>;
24
+ export namespace repeated {
25
+ let type: string;
26
+ let multiple: boolean;
27
+ }
28
+ export function taskCommand([action, ...args]: [any, ...any[]]): Promise<void>;
29
+ export function printTask(task: any, tasks: any): void;