@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,345 @@
1
+ // packages/cli/src/tasks.js
2
+ import { spawn } from "node:child_process";
3
+ import { access, readFile, writeFile } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { parseArgs } from "node:util";
6
+ import { exportBeads, importBeads, parseJsonl } from "@anhlp/alp-core/beads";
7
+ import { findFormula, formulaDirs, listFormulas, pourFormula } from "@anhlp/alp-core/formulas";
8
+ import { describeVerification } from "@anhlp/alp-core/verify";
9
+ import { ago } from "@anhlp/alp-client/render";
10
+ import { parse as toml } from "smol-toml";
11
+ import { addGate, blockersOf, checkGates, childrenOf, closeTask, compactTasks, createTask, describeGate, gateOpen, epicReport, gatesOf, linkTask, listTasks, loadTasks, readyTasks, reopenTask, resolveGate, summarize, TASKS_DIR, updateTask } from "@anhlp/alp-core/tasks";
12
+ import { alpHome } from "@anhlp/alp-client/index";
13
+ import { UsageError } from "./usage.js";
14
+ var STATUS_MARK = { open: "\u25CB", in_progress: "\u25D0", review: "\u25D1", closed: "\u25CF" };
15
+ async function taskProject(directory) {
16
+ const root = path.resolve(directory ?? process.cwd());
17
+ try {
18
+ await access(path.join(root, ".alp"));
19
+ } catch {
20
+ throw new Error(`${root} is not an ALP project (no .alp directory); run alp init, or pass --project`);
21
+ }
22
+ return root;
23
+ }
24
+ function taskRow(task, tasks) {
25
+ const row = summarize(task, tasks);
26
+ const extra = [
27
+ row.blockedBy ? `blocked by ${row.blockedBy.join(", ")}` : "",
28
+ row.gates ? `waits on ${row.gates.join("; ")}` : "",
29
+ row.assignee ? `@${row.assignee}` : "",
30
+ row.labels ? row.labels.map((label) => `#${label}`).join(" ") : ""
31
+ ].filter(Boolean).join(" ");
32
+ return `${STATUS_MARK[task.status]} ${task.id.padEnd(10)} P${task.priority} ${task.type.padEnd(7)} ${task.status.padEnd(11)} ${task.title}${extra ? ` ${extra}` : ""}`;
33
+ }
34
+ function warnUnreadable(errors) {
35
+ for (const { file, error } of errors) console.error(`alp: skipped ${file}: ${error}`);
36
+ }
37
+ async function tasksCommand(args) {
38
+ if (args[0] === "gates" || args[0] === "compact") return taskMaintenance([args[0]], args.slice(1));
39
+ if (args[0] === "export" || args[0] === "import") return beads(args[0], args.slice(1));
40
+ const { values, positionals } = parseArgs({ args, allowPositionals: true, options: {
41
+ all: { type: "boolean" },
42
+ status: { type: "string" },
43
+ label: { type: "string" },
44
+ project: { type: "string" },
45
+ json: { type: "boolean" }
46
+ } });
47
+ if (positionals.length > 1 || positionals.length && positionals[0] !== "ready") throw new UsageError();
48
+ const root = await taskProject(values.project);
49
+ const { tasks, errors } = await loadTasks(root);
50
+ warnUnreadable(errors);
51
+ const rows = positionals[0] === "ready" ? readyTasks(tasks) : listTasks(tasks, { status: values.status, label: values.label, all: values.all });
52
+ if (values.json) {
53
+ console.log(JSON.stringify(rows));
54
+ return;
55
+ }
56
+ if (!rows.length) {
57
+ console.log(positionals[0] === "ready" ? "No task is ready" : `No tasks in ${path.join(root, TASKS_DIR)}`);
58
+ return;
59
+ }
60
+ for (const task of rows) console.log(taskRow(task, tasks));
61
+ }
62
+ var gh = (args, { cwd }) => new Promise((resolve, reject) => {
63
+ const bin = process.env.ALP_GH_BIN ?? "gh";
64
+ const script = process.platform === "win32" && /\.[cm]?js$/i.test(bin);
65
+ const child = spawn(script ? process.execPath : bin, script ? [bin, ...args] : args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
66
+ let stdout = "";
67
+ let stderr = "";
68
+ child.stdout.on("data", (chunk) => {
69
+ stdout += chunk;
70
+ });
71
+ child.stderr.on("data", (chunk) => {
72
+ stderr += chunk;
73
+ });
74
+ child.on("error", (error) => reject(error.code === "ENOENT" ? new Error("gh is not installed or not on PATH") : error));
75
+ child.on("close", (code) => code === 0 ? resolve(stdout) : reject(new Error(stderr.trim() || `gh exited with ${code}`)));
76
+ });
77
+ async function taskMaintenance([command], args) {
78
+ const { values, positionals: rest } = parseArgs({ args, allowPositionals: true, options: {
79
+ project: { type: "string" },
80
+ json: { type: "boolean" },
81
+ days: { type: "string" },
82
+ "dry-run": { type: "boolean" }
83
+ } });
84
+ if (rest.length) throw new UsageError();
85
+ const root = await taskProject(values.project);
86
+ if (command === "compact") {
87
+ const days = values.days === void 0 ? 30 : Number(values.days);
88
+ const changed = await compactTasks(root, { days, dryRun: values["dry-run"] }, "user");
89
+ if (values.json) {
90
+ console.log(JSON.stringify(changed));
91
+ return;
92
+ }
93
+ if (!changed.length) {
94
+ console.log(`No task closed more than ${days} days ago is left to compact`);
95
+ return;
96
+ }
97
+ for (const { id, before, after } of changed) console.log(`${id} ${before} \u2192 ${after} characters`);
98
+ console.log(`${values["dry-run"] ? "Would compact" : "Compacted"} ${changed.length} closed ${changed.length === 1 ? "task" : "tasks"}`);
99
+ return;
100
+ }
101
+ const checked = await checkGates(root, gh);
102
+ const { tasks } = await loadTasks(root);
103
+ const open = tasks.filter((task) => task.status !== "closed").flatMap((task) => task.gates.filter((gate) => gateOpen(gate)).map((gate) => ({ task, gate })));
104
+ if (values.json) {
105
+ console.log(JSON.stringify({ ...checked, open: open.map(({ task, gate }) => ({ task: task.id, ...gate })) }));
106
+ return;
107
+ }
108
+ for (const { task, gate, detail } of checked.cleared) console.log(`\u2713 ${task} ${gate} cleared: ${detail}`);
109
+ for (const { task, gate, error } of checked.errors) console.error(`alp: could not check ${task} ${gate}: ${error}`);
110
+ if (!open.length) {
111
+ console.log("No open gates");
112
+ return;
113
+ }
114
+ for (const { task, gate } of open) {
115
+ const state = checked.pending.find((entry) => entry.task === task.id && entry.gate === gate.id)?.detail;
116
+ console.log(`\u23F8 ${task.id} ${describeGate(gate)}${state ? ` (${state})` : ""} ${task.title}`);
117
+ }
118
+ }
119
+ async function beads(command, args) {
120
+ const { values, positionals } = parseArgs({ args, allowPositionals: true, options: {
121
+ project: { type: "string" },
122
+ output: { type: "string", short: "o" },
123
+ "dry-run": { type: "boolean" },
124
+ json: { type: "boolean" }
125
+ } });
126
+ const root = await taskProject(values.project);
127
+ if (command === "export") {
128
+ if (positionals.length || values["dry-run"]) throw new UsageError();
129
+ const { tasks, errors: errors2 } = await loadTasks(root);
130
+ warnUnreadable(errors2);
131
+ const text = exportBeads(tasks).map((record) => JSON.stringify(record)).join("\n") + (tasks.length ? "\n" : "");
132
+ if (values.output) {
133
+ await writeFile(values.output, text);
134
+ console.error(`Exported ${tasks.length} ${tasks.length === 1 ? "task" : "tasks"} to ${values.output}`);
135
+ } else {
136
+ process.stdout.write(text);
137
+ }
138
+ return;
139
+ }
140
+ if (positionals.length > 1 || values.output) throw new UsageError();
141
+ const file = positionals[0] ?? path.join(root, ".beads", "issues.jsonl");
142
+ const { records, errors } = parseJsonl(await readFile(file, "utf8"));
143
+ const report = await importBeads(root, records, { dryRun: values["dry-run"], by: "user" });
144
+ report.skipped.push(...errors);
145
+ if (values.json) {
146
+ console.log(JSON.stringify(report));
147
+ return;
148
+ }
149
+ const verb = values["dry-run"] ? "Would import" : "Imported";
150
+ for (const { id, from } of report.created) console.log(`+ ${id}${id !== from ? ` (from ${from})` : ""}`);
151
+ for (const { id, from } of report.updated) console.log(`~ ${id}${id !== from ? ` (from ${from})` : ""}`);
152
+ for (const { line, reason } of report.skipped.sort((a, b) => a.line - b.line)) console.error(`alp: skipped line ${line}: ${reason}`);
153
+ for (const warning of report.warnings) console.error(`alp: ${warning}`);
154
+ console.log(`${verb} ${file}: ${report.created.length} created, ${report.updated.length} updated, ${report.skipped.length} skipped`);
155
+ }
156
+ async function formulaCommand([action, ...args]) {
157
+ const { values, positionals } = parseArgs({ args, allowPositionals: true, options: {
158
+ project: { type: "string" },
159
+ var: { type: "string", multiple: true },
160
+ parent: { type: "string" },
161
+ "dry-run": { type: "boolean" },
162
+ json: { type: "boolean" }
163
+ } });
164
+ const root = await taskProject(values.project);
165
+ const home = alpHome();
166
+ if (action === "list") {
167
+ if (positionals.length) throw new UsageError();
168
+ const found = await listFormulas(root, home, { toml });
169
+ if (values.json) {
170
+ console.log(JSON.stringify(found));
171
+ return;
172
+ }
173
+ if (!found.length) {
174
+ console.log(`No formulas in ${formulaDirs(root, home).join(", ")}`);
175
+ return;
176
+ }
177
+ for (const entry of found) console.log(entry.error ? `\u2717 ${entry.name} ${entry.file}: ${entry.error}` : `${entry.name} ${entry.formula.steps.length} steps${entry.formula.description ? ` ${entry.formula.description.split("\n")[0]}` : ""}`);
178
+ return;
179
+ }
180
+ if (positionals.length !== 1) throw new UsageError();
181
+ const { formula, file } = await findFormula(root, home, positionals[0], { toml });
182
+ if (action === "show") {
183
+ if (values.json) {
184
+ console.log(JSON.stringify(formula));
185
+ return;
186
+ }
187
+ console.log(`${formula.formula}${formula.version ? ` v${formula.version}` : ""} ${file}`);
188
+ if (formula.description) console.log(` ${formula.description}`);
189
+ for (const [name, spec] of Object.entries(formula.vars)) console.log(` --var ${name}=\u2026${spec.required ? " (required)" : spec.default !== void 0 ? ` (default ${spec.default})` : ""}${spec.description ? ` ${spec.description}` : ""}`);
190
+ for (const step of formula.steps) console.log(` ${step.id}${step.type === "human" ? " [you]" : step.type !== "task" ? ` [${step.type}]` : ""}: ${step.title}${step.needs.length ? ` after ${step.needs.join(", ")}` : ""}`);
191
+ return;
192
+ }
193
+ if (action !== "pour") throw new UsageError();
194
+ const given = {};
195
+ for (const entry of values.var ?? []) {
196
+ const at = entry.indexOf("=");
197
+ if (at < 1) throw new UsageError();
198
+ given[entry.slice(0, at)] = entry.slice(at + 1);
199
+ }
200
+ const { epic, tasks } = await pourFormula(root, formula, given, "user", { dryRun: values["dry-run"], ...values.parent ? { parent: values.parent } : {} });
201
+ if (values.json) {
202
+ console.log(JSON.stringify({ epic, tasks }));
203
+ return;
204
+ }
205
+ console.log(`${values["dry-run"] ? "Would pour" : "Poured"} ${formula.formula} as ${epic.id} ${epic.title}`);
206
+ const all = [epic, ...tasks];
207
+ for (const task of tasks) console.log(` ${taskRow(task, all)}`);
208
+ }
209
+ var repeated = { type: "string", multiple: true };
210
+ async function taskCommand([action, ...args]) {
211
+ const { values, positionals } = parseArgs({ args, allowPositionals: true, options: {
212
+ description: { type: "string", short: "d" },
213
+ priority: { type: "string", short: "p" },
214
+ type: { type: "string", short: "t" },
215
+ label: { ...repeated, short: "l" },
216
+ path: repeated,
217
+ parent: { type: "string" },
218
+ after: repeated,
219
+ related: repeated,
220
+ from: { type: "string" },
221
+ title: { type: "string" },
222
+ message: { type: "string", short: "m" },
223
+ reason: { type: "string" },
224
+ unverified: { type: "string" },
225
+ project: { type: "string" },
226
+ json: { type: "boolean" },
227
+ human: { type: "string" },
228
+ timer: { type: "string" },
229
+ pr: { type: "string" },
230
+ run: { type: "string" }
231
+ } });
232
+ const root = await taskProject(values.project);
233
+ const fields = {
234
+ ...values.description !== void 0 ? { description: values.description } : {},
235
+ ...values.priority !== void 0 ? { priority: values.priority } : {},
236
+ ...values.type !== void 0 ? { type: values.type } : {},
237
+ ...values.label ? { labels: values.label } : {},
238
+ ...values.path ? { paths: values.path } : {}
239
+ };
240
+ let task;
241
+ if (action === "add") {
242
+ const title = positionals.join(" ").trim();
243
+ if (!title) throw new UsageError();
244
+ task = await createTask(root, { title, ...fields, parent: values.parent, blockedBy: values.after, related: values.related, discoveredFrom: values.from }, "user");
245
+ } else if (action === "show") {
246
+ if (positionals.length !== 1) throw new UsageError();
247
+ const { tasks: tasks2, errors } = await loadTasks(root);
248
+ warnUnreadable(errors);
249
+ task = tasks2.find((candidate) => candidate.id === positionals[0]);
250
+ if (!task) throw new Error(`No task ${positionals[0]} in ${path.join(root, TASKS_DIR)}`);
251
+ if (values.json) {
252
+ console.log(JSON.stringify(task));
253
+ return;
254
+ }
255
+ printTask(task, tasks2);
256
+ return;
257
+ } else if (action === "report") {
258
+ if (positionals.length !== 1) throw new UsageError();
259
+ const { tasks: tasks2, errors } = await loadTasks(root);
260
+ warnUnreadable(errors);
261
+ const report = epicReport(positionals[0], tasks2);
262
+ console.log(values.json ? JSON.stringify(report) : report.text);
263
+ return;
264
+ } else if (action === "edit") {
265
+ if (positionals.length !== 1) throw new UsageError();
266
+ task = await updateTask(root, positionals[0], { ...fields, ...values.title !== void 0 ? { title: values.title } : {}, note: values.message }, "user");
267
+ } else if (action === "close") {
268
+ if (positionals.length !== 1) throw new UsageError();
269
+ task = await closeTask(root, positionals[0], { reason: values.reason, summary: values.message, unverified: values.unverified }, "user");
270
+ } else if (action === "reopen") {
271
+ if (positionals.length !== 1) throw new UsageError();
272
+ task = await reopenTask(root, positionals[0], { note: values.message }, "user");
273
+ } else if (action === "dep") {
274
+ const [change, id, ...rest] = positionals;
275
+ if (!["add", "rm"].includes(change) || !id || rest.length || !(values.after || values.parent || values.related)) throw new UsageError();
276
+ const links = { ...values.after ? { blockedBy: values.after } : {}, ...values.parent ? { parent: values.parent } : {}, ...values.related ? { related: values.related } : {} };
277
+ task = await linkTask(root, id, change === "add" ? { add: links } : { remove: links }, "user");
278
+ } else if (action === "gate") {
279
+ const [change, id, gate, ...rest] = positionals;
280
+ const kinds = ["human", "timer", "pr", "run"].filter((kind) => values[kind] !== void 0);
281
+ if (change === "add" && id && !gate && kinds.length === 1) {
282
+ const kind = kinds[0];
283
+ task = await addGate(root, id, kind === "human" ? { kind, note: values.human } : kind === "timer" ? { kind, until: values.timer } : { kind: `gh:${kind}`, ref: values[kind] }, "user");
284
+ } else if ((change === "clear" || change === "rm") && id && gate && !rest.length && !kinds.length) {
285
+ task = await resolveGate(root, id, gate, { by: "user", note: values.message, remove: change === "rm" });
286
+ } else {
287
+ throw new UsageError();
288
+ }
289
+ } else {
290
+ throw new UsageError();
291
+ }
292
+ if (values.json) {
293
+ console.log(JSON.stringify(task));
294
+ return;
295
+ }
296
+ const { tasks } = await loadTasks(root);
297
+ console.log(`${{ add: "Created", close: "Closed", reopen: "Reopened" }[action] ?? "Updated"} ${TASKS_DIR}/${task.id}.json`);
298
+ if (action === "gate") for (const gate of task.gates.filter((entry) => gateOpen(entry))) console.log(` \u23F8 ${describeGate(gate)}`);
299
+ console.log(taskRow(task, tasks));
300
+ if (action === "close" && tasks.some((entry) => entry.parent === task.id)) console.log(`
301
+ ${epicReport(task.id, tasks).text}`);
302
+ }
303
+ function printTask(task, tasks) {
304
+ console.log(`${STATUS_MARK[task.status]} ${task.id} ${task.title}`);
305
+ console.log(` ${task.type}, P${task.priority}, ${task.status}${task.assignee ? ` with ${task.assignee.agent}` : ""}; created by ${task.createdBy} ${ago(task.createdAt)} ago, rev ${task.rev}`);
306
+ if (task.description) console.log(`
307
+ ${task.description.replace(/\n/g, "\n ")}
308
+ `);
309
+ const blockers = task.status === "closed" ? [] : blockersOf(task, tasks);
310
+ const relations = [
311
+ task.parent && `parent ${task.parent}`,
312
+ task.blockedBy.length && `after ${task.blockedBy.join(", ")}${blockers.length ? ` (open: ${blockers.join(", ")})` : ""}`,
313
+ task.discoveredFrom && `found during ${task.discoveredFrom}`,
314
+ task.related.length && `related ${task.related.join(", ")}`
315
+ ].filter(Boolean);
316
+ if (relations.length) console.log(` ${relations.join("; ")}`);
317
+ for (const gate of task.gates) console.log(` ${gateOpen(gate) ? "\u23F8" : "\u2713"} gate ${describeGate(gate)}${gate.resolved ? ` (cleared by ${gate.resolved.by}${gate.resolved.note ? `: ${gate.resolved.note}` : ""})` : ""}`);
318
+ const inherited = gatesOf(task, tasks).filter((label) => label.startsWith("t-"));
319
+ if (task.status !== "closed" && inherited.length) console.log(` waits on its parent's gates: ${inherited.join("; ")}`);
320
+ if (task.compacted) console.log(` compacted ${task.compacted.at.slice(0, 10)} from ${task.compacted.chars} characters`);
321
+ if (task.labels.length) console.log(` labels: ${task.labels.join(", ")}`);
322
+ if (task.paths.length) console.log(` paths: ${task.paths.join(", ")}`);
323
+ for (const child of childrenOf(task.id, tasks)) console.log(` ${taskRow(child, tasks)}`);
324
+ if (task.verified) console.log(` ${task.verified.skipped ? "\u2013" : task.verified.passed ? "\u2713" : "\u2717"} ${describeVerification(task.verified)}${task.verified.where ? ` in the ${task.verified.where}` : ""}, ${ago(task.verified.at)} ago`);
325
+ if (task.closed) console.log(` closed ${task.closed.reason} by ${task.closed.by}${task.closed.summary ? `: ${task.closed.summary}` : ""}${task.closed.unverified ? ` (unverified: ${task.closed.unverified})` : ""}`);
326
+ for (const entry of task.log.slice(-10)) {
327
+ const { at, by, event, ...detail } = entry;
328
+ const text = Object.entries(detail).map(([key, value]) => `${key} ${Array.isArray(value) ? value.join(", ") : value}`).join("; ");
329
+ console.log(` ${at.slice(0, 16).replace("T", " ")} ${by} ${event}${text ? `: ${text}` : ""}`);
330
+ }
331
+ }
332
+ export {
333
+ STATUS_MARK,
334
+ beads,
335
+ formulaCommand,
336
+ gh,
337
+ printTask,
338
+ repeated,
339
+ taskCommand,
340
+ taskMaintenance,
341
+ taskProject,
342
+ taskRow,
343
+ tasksCommand,
344
+ warnUnreadable
345
+ };
@@ -0,0 +1,3 @@
1
+ export const USAGE: "Usage:\n alp <init|upgrade|update> [directory]\n alp setup [directory] [--runtime codex|claude|mix] [--prefer claude|codex]\n [--claude-model M] [--codex-model M] [--claude-supervisor M] [--codex-supervisor M]\n [--claude-oracle M] [--codex-oracle M] write Ph\u1EDF/Cafe's agent profiles to .alp/settings.json\n alp daemon <start|stop|restart>\n alp daemon status [--json] liveness and readiness; exit 0 ready, 3 not running, 4 not ready\n alp daemon <install|uninstall> run alpd as a login service that restarts after a crash\n alp acp serve ALP to an editor (Zed, JetBrains) over the Agent Client Protocol on stdio\n alp web [--print] open the local web app alpd serves; --print only prints its address\n alp web pair [--on|--off] the link that pairs Paseo's app on another device through the relay; --on or --off turns the relay on or off\n alp doctor [--project DIR] [--fix] [--json] check this machine and project; --fix repairs what is safe to\n alp backup [-o FILE] [--project DIR]... [--no-projects] [--stop] [--json] sessions, settings, library and tasks in one file\n alp backup verify <file> [--json] | alp backup list [--json]\n alp restore <file> [--home-only] [--dry-run] [--stop] [--replace-newer] [--json] checks the whole backup first; keeps what it replaces\n alpd must be stopped for both; --stop stops it and starts it again after\n alp run [--agent A] [--team pho|cafe|ID] [--model M] [--mode read-only|workspace-write|full-access] [--thinking T] [--project DIR] [--json] <prompt>\n alp ps [--all]\n alp top [session] [--once]\n alp attach <session> [--json]\n alp send <session> [--json] <text>\n alp questions [--json]\n alp answer <question> <text> | alp answer <question> --dismiss [--reason R]\n alp log <session> [--json]\n alp board [--project DIR] [--json]\n alp <agents|skills|mcp|hooks> [--project DIR] [--json] the library: built-in, ~/.alp and project entries\n alp providers [--json] ACP and Antigravity providers in your library\n alp teams [--project DIR] [--json] teams sessions run in: members, delegation, house rules\n alp agent <new|edit> <name> [--project [DIR]] [--from A] [-d text] [--provider P] [--model M] [--thinking T] [--mode M]\n [--skills a,b] [--mcp a,b] [--hooks a,b] [--instructions FILE]\n alp team <new|edit> <id> [--project [DIR]] [--from ID] [--label L] [-d text] [--main A] [--member name=role]...\n [--member-model name=M]... [--member-thinking name=T]... [--delegate owner=a,b]... [--max-peers N]\n [--supervisor A] [--supervisor-model M] [--supervisor-thinking T] [--no-supervisor] [--rules FILE]\n alp skill new <name> [--project [DIR]] [--from S] [--file FILE]\n alp mcp add <name> [--project [DIR]] (--command C [--arg A]... [--env K=V]... [--cwd D] | --url U [--header K=V]...)\n alp hook add <name> [--project [DIR]] --event E --command C [--blocking] [--timeout SEC] [--match-agent A] [--match-label L]\n alp provider add <id> --command C [--arg A]... [--env K=V]... [--label L] [-d text] [--models a,b] an ACP agent; use id antigravity for native agy\n alp provider edit antigravity --read-only-mode instructions|reject opt into prompt-only read-only for advisory roles\n alp <agent|team|skill|mcp|hook|provider> <rm|show> <name> [--project [DIR]] | <cp|mv> <from> <to> [--project [DIR]]\n alp <mcp|hook|provider> test <name> [--project [DIR]] list an MCP server's tools; run a hook once; initialize a provider\n without --project, changes go to your library in ~/.alp; built-in agents and teams are never changed\n alp trust [--project DIR] | --revoke [--project DIR] | --list [--json] whether the project's hooks may run\n alp permissions [--project DIR] [--json] each agent's permission profile\n alp permissions check <agent> \"<command>\" what that agent's profile says about a command\n alp tasks [ready] [--all] [--status S] [--label L] [--project DIR] [--json]\n alp tasks gates [--json] open gates; checks GitHub ones\n alp tasks compact [--days 30] [--dry-run]\n alp tasks export [-o file.jsonl] as beads JSONL\n alp tasks import [file.jsonl] [--dry-run] beads JSONL; default .beads/issues.jsonl\n alp formula list | show <name> | pour <name> [--var k=v]... [--parent ID] [--dry-run]\n alp task add <title> [-d text] [-p 0-4] [-t task|bug|feature|chore|epic] [--parent ID] [--after ID]... [-l label]... [--path P]... [--from ID]\n alp task show <id> [--json]\n alp task report <epic> [--json] what an epic came to: tasks, time, rework, verification\n alp task edit <id> [--title T] [-d text] [-p N] [-t type] [-l label]... [--path P]... [-m note]\n alp task close <id> [--reason done|wontfix|duplicate|superseded] [-m summary] [--unverified \"why\"]\n alp verify [--project DIR] [--task ID] [--json] run the project's verify commands; --task records the result\n alp task reopen <id> [-m note]\n alp task dep <add|rm> <id> [--after ID]... [--parent ID] [--related ID]...\n alp task gate add <id> --human \"question\" | --timer +2h|ISO | --pr N|owner/repo#N | --run N|owner/repo#N\n alp task gate <clear|rm> <id> <gate> [-m note]\n alp pause [codex|claude] [--now] [-m reason] | alp pause status hold delegation; --now parks running assignments\n alp resume [codex|claude] lift a pause; parked assignments continue\n alp language [name | --reset] the language agents and ALP write to you in; default Vietnamese\n alp recall <assignment|task> [--project DIR] [--json] <question> ask a finished assignment about its work\n alp interrupt <session>";
2
+ export class UsageError extends Error {
3
+ }
@@ -0,0 +1,72 @@
1
+ // packages/cli/src/usage.js
2
+ var USAGE = `Usage:
3
+ alp <init|upgrade|update> [directory]
4
+ alp setup [directory] [--runtime codex|claude|mix] [--prefer claude|codex]
5
+ [--claude-model M] [--codex-model M] [--claude-supervisor M] [--codex-supervisor M]
6
+ [--claude-oracle M] [--codex-oracle M] write Ph\u1EDF/Cafe's agent profiles to .alp/settings.json
7
+ alp daemon <start|stop|restart>
8
+ alp daemon status [--json] liveness and readiness; exit 0 ready, 3 not running, 4 not ready
9
+ alp daemon <install|uninstall> run alpd as a login service that restarts after a crash
10
+ alp acp serve ALP to an editor (Zed, JetBrains) over the Agent Client Protocol on stdio
11
+ alp web [--print] open the local web app alpd serves; --print only prints its address
12
+ alp web pair [--on|--off] the link that pairs Paseo's app on another device through the relay; --on or --off turns the relay on or off
13
+ alp doctor [--project DIR] [--fix] [--json] check this machine and project; --fix repairs what is safe to
14
+ alp backup [-o FILE] [--project DIR]... [--no-projects] [--stop] [--json] sessions, settings, library and tasks in one file
15
+ alp backup verify <file> [--json] | alp backup list [--json]
16
+ alp restore <file> [--home-only] [--dry-run] [--stop] [--replace-newer] [--json] checks the whole backup first; keeps what it replaces
17
+ alpd must be stopped for both; --stop stops it and starts it again after
18
+ alp run [--agent A] [--team pho|cafe|ID] [--model M] [--mode read-only|workspace-write|full-access] [--thinking T] [--project DIR] [--json] <prompt>
19
+ alp ps [--all]
20
+ alp top [session] [--once]
21
+ alp attach <session> [--json]
22
+ alp send <session> [--json] <text>
23
+ alp questions [--json]
24
+ alp answer <question> <text> | alp answer <question> --dismiss [--reason R]
25
+ alp log <session> [--json]
26
+ alp board [--project DIR] [--json]
27
+ alp <agents|skills|mcp|hooks> [--project DIR] [--json] the library: built-in, ~/.alp and project entries
28
+ alp providers [--json] ACP and Antigravity providers in your library
29
+ alp teams [--project DIR] [--json] teams sessions run in: members, delegation, house rules
30
+ alp agent <new|edit> <name> [--project [DIR]] [--from A] [-d text] [--provider P] [--model M] [--thinking T] [--mode M]
31
+ [--skills a,b] [--mcp a,b] [--hooks a,b] [--instructions FILE]
32
+ alp team <new|edit> <id> [--project [DIR]] [--from ID] [--label L] [-d text] [--main A] [--member name=role]...
33
+ [--member-model name=M]... [--member-thinking name=T]... [--delegate owner=a,b]... [--max-peers N]
34
+ [--supervisor A] [--supervisor-model M] [--supervisor-thinking T] [--no-supervisor] [--rules FILE]
35
+ alp skill new <name> [--project [DIR]] [--from S] [--file FILE]
36
+ alp mcp add <name> [--project [DIR]] (--command C [--arg A]... [--env K=V]... [--cwd D] | --url U [--header K=V]...)
37
+ alp hook add <name> [--project [DIR]] --event E --command C [--blocking] [--timeout SEC] [--match-agent A] [--match-label L]
38
+ alp provider add <id> --command C [--arg A]... [--env K=V]... [--label L] [-d text] [--models a,b] an ACP agent; use id antigravity for native agy
39
+ alp provider edit antigravity --read-only-mode instructions|reject opt into prompt-only read-only for advisory roles
40
+ alp <agent|team|skill|mcp|hook|provider> <rm|show> <name> [--project [DIR]] | <cp|mv> <from> <to> [--project [DIR]]
41
+ alp <mcp|hook|provider> test <name> [--project [DIR]] list an MCP server's tools; run a hook once; initialize a provider
42
+ without --project, changes go to your library in ~/.alp; built-in agents and teams are never changed
43
+ alp trust [--project DIR] | --revoke [--project DIR] | --list [--json] whether the project's hooks may run
44
+ alp permissions [--project DIR] [--json] each agent's permission profile
45
+ alp permissions check <agent> "<command>" what that agent's profile says about a command
46
+ alp tasks [ready] [--all] [--status S] [--label L] [--project DIR] [--json]
47
+ alp tasks gates [--json] open gates; checks GitHub ones
48
+ alp tasks compact [--days 30] [--dry-run]
49
+ alp tasks export [-o file.jsonl] as beads JSONL
50
+ alp tasks import [file.jsonl] [--dry-run] beads JSONL; default .beads/issues.jsonl
51
+ alp formula list | show <name> | pour <name> [--var k=v]... [--parent ID] [--dry-run]
52
+ alp task add <title> [-d text] [-p 0-4] [-t task|bug|feature|chore|epic] [--parent ID] [--after ID]... [-l label]... [--path P]... [--from ID]
53
+ alp task show <id> [--json]
54
+ alp task report <epic> [--json] what an epic came to: tasks, time, rework, verification
55
+ alp task edit <id> [--title T] [-d text] [-p N] [-t type] [-l label]... [--path P]... [-m note]
56
+ alp task close <id> [--reason done|wontfix|duplicate|superseded] [-m summary] [--unverified "why"]
57
+ alp verify [--project DIR] [--task ID] [--json] run the project's verify commands; --task records the result
58
+ alp task reopen <id> [-m note]
59
+ alp task dep <add|rm> <id> [--after ID]... [--parent ID] [--related ID]...
60
+ alp task gate add <id> --human "question" | --timer +2h|ISO | --pr N|owner/repo#N | --run N|owner/repo#N
61
+ alp task gate <clear|rm> <id> <gate> [-m note]
62
+ alp pause [codex|claude] [--now] [-m reason] | alp pause status hold delegation; --now parks running assignments
63
+ alp resume [codex|claude] lift a pause; parked assignments continue
64
+ alp language [name | --reset] the language agents and ALP write to you in; default Vietnamese
65
+ alp recall <assignment|task> [--project DIR] [--json] <question> ask a finished assignment about its work
66
+ alp interrupt <session>`;
67
+ var UsageError = class extends Error {
68
+ };
69
+ export {
70
+ USAGE,
71
+ UsageError
72
+ };
@@ -0,0 +1,24 @@
1
+ # @anhlp/alp-client
2
+
3
+ The ALP RPC client: a portable JSON-RPC engine for Node, browsers and React Native, its
4
+ WebSocket and Node IPC transports, and the Node-only daemon discovery, supervision, services
5
+ and setup helpers.
6
+
7
+ - `@anhlp/alp-client/portable`: `AlpRpcClient`, errors, `hello`, `createReconnectingClient`,
8
+ NDJSON framing. No Node built-ins, `process` or `Buffer`; needs no polyfills.
9
+ - `@anhlp/alp-client/websocket`: `connectWebSocket` over the browser's, React Native's or an
10
+ injected WebSocket. Authentication is the caller's (the gateway's) business.
11
+ - `@anhlp/alp-client` (Node): everything above plus `connect`, `AlpClient`, `connectSocket`,
12
+ `ensureDaemon`, the private-socket checks and lock helpers, unchanged for existing callers.
13
+ - `@anhlp/alp-client/ipc` (Node): `createSocketTransport` for a Unix socket or named pipe.
14
+
15
+ Timeouts and aborts end only the caller's wait, and nothing is ever resent after a disconnect.
16
+ See [docs/alp/client-sdk.md](../../docs/alp/client-sdk.md) for the transport contract, the
17
+ request lifecycle, the old-to-new API table and what still waits on the protocol (Task 06) and
18
+ the gateway (Task 08).
19
+
20
+ `startIntegration` discovers an optional integration's descriptor and starts it detached with `--service`, so the entry writes its own log; discovery and the selected `ALP_HOME` are pinned by tests. Restarting is non-destructive: a daemon left running by an older install is reported as older than the current CLI and is never killed in place.
21
+
22
+ Build: `npm run build --workspace=@anhlp/alp-client`. Test: `npm run test --workspace=@anhlp/alp-client`.
23
+
24
+ Public imports use the package root and its explicitly exported subpaths. See [architecture, provider extension, compatibility and release instructions](../../docs/modular-monorepo.md).
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Starts an ACP provider once and runs initialize (ALPD §46), for `alp provider test`
3
+ * and the settings screen. It reports who the agent says it is and what it supports;
4
+ * a provider that needs a login usually says so here, in authMethods. With `discover`,
5
+ * it also opens a session in `cwd`, as Paseo does, to read the models and modes the agent
6
+ * offers (ALPD §65); an agent that offers none leaves them out.
7
+ * @param {{ command: string, args?: string[], env?: Record<string, string>, label?: string, id?: string }} provider
8
+ * @param {{ cwd?: string, timeoutMs?: number, discover?: boolean }} [options]
9
+ * @returns {Promise<{ protocolVersion: number, agent?: { name?: string, title?: string, version?: string }, loadSession: boolean, mcpHttp: boolean, authMethods: string[],
10
+ * models?: Array<{ id: string, label: string, description?: string }>, currentModel?: string, modes?: Array<{ id: string, label: string, description?: string }>, currentMode?: string }>}
11
+ */
12
+ export function probeAcp(provider: {
13
+ command: string;
14
+ args?: string[];
15
+ env?: Record<string, string>;
16
+ label?: string;
17
+ id?: string;
18
+ }, { cwd, timeoutMs, discover }?: {
19
+ cwd?: string;
20
+ timeoutMs?: number;
21
+ discover?: boolean;
22
+ }): Promise<{
23
+ protocolVersion: number;
24
+ agent?: {
25
+ name?: string;
26
+ title?: string;
27
+ version?: string;
28
+ };
29
+ loadSession: boolean;
30
+ mcpHttp: boolean;
31
+ authMethods: string[];
32
+ models?: Array<{
33
+ id: string;
34
+ label: string;
35
+ description?: string;
36
+ }>;
37
+ currentModel?: string;
38
+ modes?: Array<{
39
+ id: string;
40
+ label: string;
41
+ description?: string;
42
+ }>;
43
+ currentMode?: string;
44
+ }>;
@@ -0,0 +1,37 @@
1
+ // packages/client/src/acp-probe.js
2
+ import { stdioSession } from "./mcp-probe.js";
3
+ async function probeAcp(provider, { cwd, timeoutMs = 2e4, discover = false } = {}) {
4
+ const session = stdioSession({ command: provider.command, args: provider.args ?? [], env: provider.env ?? {} }, cwd, timeoutMs);
5
+ try {
6
+ const result = await session.request("initialize", {
7
+ protocolVersion: 1,
8
+ clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, terminal: false },
9
+ clientInfo: { name: "alp", title: "ALP", version: "1" }
10
+ });
11
+ if (result.protocolVersion !== 1) throw new Error(`${provider.label ?? provider.id} speaks ACP version ${result.protocolVersion}; ALP speaks version 1`);
12
+ const info = result.agentInfo;
13
+ const offered = discover ? await session.request("session/new", { cwd, mcpServers: [] }) : void 0;
14
+ const choices = (list) => Array.isArray(list) ? list.filter((item) => typeof (item?.modelId ?? item?.id) === "string").map((item) => ({ id: item.modelId ?? item.id, label: typeof item.name === "string" && item.name ? item.name : item.modelId ?? item.id, ...typeof item.description === "string" && item.description ? { description: item.description } : {} })) : void 0;
15
+ const config = offered?.configOptions?.find((option) => option.category === "model" && option.type === "select");
16
+ const modeConfig = offered?.configOptions?.find((option) => option.category === "mode" && option.type === "select");
17
+ const flatten = (options) => options?.flatMap((option) => option.options ?? [option]).map((option) => ({ ...option, id: option.value }));
18
+ const modelState = offered?.models ?? (config ? { availableModels: flatten(config.options), currentModelId: config.currentValue } : void 0);
19
+ const modeState = offered?.modes ?? (modeConfig ? { availableModes: flatten(modeConfig.options), currentModeId: modeConfig.currentValue } : void 0);
20
+ const models = choices(modelState?.availableModels);
21
+ const modes = choices(modeState?.availableModes);
22
+ return {
23
+ ...models ? { models, ...typeof modelState.currentModelId === "string" ? { currentModel: modelState.currentModelId } : {} } : {},
24
+ ...modes ? { modes, ...typeof modeState.currentModeId === "string" ? { currentMode: modeState.currentModeId } : {} } : {},
25
+ protocolVersion: result.protocolVersion,
26
+ ...info ? { agent: { ...info.name ? { name: info.name } : {}, ...info.title ? { title: info.title } : {}, ...info.version ? { version: info.version } : {} } } : {},
27
+ loadSession: Boolean(result.agentCapabilities?.loadSession),
28
+ mcpHttp: Boolean(result.agentCapabilities?.mcpCapabilities?.http),
29
+ authMethods: (result.authMethods ?? []).map((method) => method?.name ?? method?.id).filter(Boolean)
30
+ };
31
+ } finally {
32
+ await session.close();
33
+ }
34
+ }
35
+ export {
36
+ probeAcp
37
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Probes Antigravity CLI for version, status and available models.
3
+ * @param {{ command?: string, args?: string[], env?: Record<string, string>, label?: string, id?: string, readOnlyMode?: 'instructions' | 'reject' }} [provider]
4
+ * @param {{ cwd?: string, timeoutMs?: number }} [options]
5
+ */
6
+ export function probeAntigravity(provider?: {
7
+ command?: string;
8
+ args?: string[];
9
+ env?: Record<string, string>;
10
+ label?: string;
11
+ id?: string;
12
+ readOnlyMode?: "instructions" | "reject";
13
+ }, { cwd, timeoutMs }?: {
14
+ cwd?: string;
15
+ timeoutMs?: number;
16
+ }): Promise<{
17
+ version: string;
18
+ agent: {
19
+ name: string;
20
+ title: string;
21
+ version: string;
22
+ };
23
+ models: {
24
+ id: any;
25
+ label: any;
26
+ }[];
27
+ currentModel: any;
28
+ modes: {
29
+ id: string;
30
+ label: string;
31
+ description: string;
32
+ }[];
33
+ currentMode: string;
34
+ }>;