portable-agent-layer 0.88.1 → 0.89.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 (87) hide show
  1. package/README.md +8 -5
  2. package/assets/agents/gemini-researcher.md +13 -0
  3. package/assets/agents/grok-researcher.md +13 -0
  4. package/assets/agents/multi-perspective-researcher.md +12 -0
  5. package/assets/agents/perplexity-researcher.md +13 -0
  6. package/assets/agents/skill-author.md +17 -0
  7. package/assets/agents/subagent-author.md +18 -1
  8. package/assets/skills/create-subagent/SKILL.md +3 -2
  9. package/assets/skills/create-subagent/authoring-guide.md +16 -0
  10. package/assets/templates/PAL/MEMORY_SYSTEM.md +1 -1
  11. package/assets/templates/PAL/README.md +6 -5
  12. package/assets/templates/PAL/SYSTEM_ARCHITECTURE.md +4 -1
  13. package/assets/templates/hooks.antigravity.json +42 -0
  14. package/assets/templates/settings.antigravity.json +44 -0
  15. package/package.json +1 -1
  16. package/src/cli/builtin-tools.ts +28 -23
  17. package/src/cli/doctor/agents.ts +64 -64
  18. package/src/cli/identity.ts +26 -20
  19. package/src/cli/index.ts +109 -312
  20. package/src/cli/knowledge.ts +160 -201
  21. package/src/cli/ledger-command.ts +69 -0
  22. package/src/cli/ledger.ts +20 -101
  23. package/src/cli/personal-context.ts +15 -12
  24. package/src/cli/rule.ts +29 -29
  25. package/src/cli/server.ts +33 -52
  26. package/src/cli/session-agent.ts +7 -9
  27. package/src/cli/skill.ts +73 -64
  28. package/src/cli/subagent.ts +51 -58
  29. package/src/cli/tree.ts +177 -0
  30. package/src/hooks/InvocationContext.ts +26 -0
  31. package/src/hooks/LedgerCommit.ts +4 -2
  32. package/src/hooks/LedgerSnapshot.ts +2 -0
  33. package/src/hooks/SecurityValidator.ts +2 -0
  34. package/src/hooks/StopOrchestrator.ts +12 -2
  35. package/src/hooks/UserPromptOrchestrator.ts +3 -15
  36. package/src/hooks/handlers/context-digests.ts +21 -21
  37. package/src/hooks/handlers/readme-sync.ts +6 -2
  38. package/src/hooks/lib/agent-definition.ts +6 -16
  39. package/src/hooks/lib/agent-registry.ts +88 -0
  40. package/src/hooks/lib/agent.ts +47 -23
  41. package/src/hooks/lib/antigravity-inference.ts +84 -0
  42. package/src/hooks/lib/antigravity-transcript.ts +129 -0
  43. package/src/hooks/lib/claim-check.ts +13 -2
  44. package/src/hooks/lib/claim-log.ts +5 -1
  45. package/src/hooks/lib/claude-md.ts +21 -1
  46. package/src/hooks/lib/context.ts +5 -1
  47. package/src/hooks/lib/export-manifest.ts +23 -0
  48. package/src/hooks/lib/export.ts +1 -24
  49. package/src/hooks/lib/hook-turn.ts +25 -2
  50. package/src/hooks/lib/import-merge.ts +1 -1
  51. package/src/hooks/lib/inference.ts +77 -1
  52. package/src/hooks/lib/invocation-context.ts +99 -0
  53. package/src/hooks/lib/ledger-hook.ts +14 -6
  54. package/src/hooks/lib/models.ts +34 -17
  55. package/src/hooks/lib/paths.ts +39 -6
  56. package/src/hooks/lib/prompt-capture.ts +28 -0
  57. package/src/hooks/lib/readme-sync.ts +3 -31
  58. package/src/hooks/lib/security-gate.ts +10 -3
  59. package/src/hooks/lib/semi-static.ts +13 -3
  60. package/src/hooks/lib/session-context.ts +3 -1
  61. package/src/hooks/lib/stop.ts +1 -0
  62. package/src/hooks/lib/transcript.ts +6 -3
  63. package/src/targets/agent-render.ts +3 -3
  64. package/src/targets/antigravity/install.ts +68 -0
  65. package/src/targets/antigravity/uninstall.ts +66 -0
  66. package/src/targets/lib.ts +81 -64
  67. package/src/targets/opencode/install.ts +2 -0
  68. package/src/targets/opencode/permission.ts +39 -0
  69. package/src/targets/opencode/uninstall.ts +9 -8
  70. package/src/tools/agent/algorithm-reflect.ts +70 -62
  71. package/src/tools/agent/algorithm-synthesize.ts +39 -14
  72. package/src/tools/agent/analyze.ts +31 -36
  73. package/src/tools/agent/handoff-note.ts +51 -54
  74. package/src/tools/agent/interaction.ts +32 -28
  75. package/src/tools/agent/project.ts +300 -287
  76. package/src/tools/agent/relationship-note.ts +40 -36
  77. package/src/tools/agent/relationship-reflect.ts +23 -32
  78. package/src/tools/agent/synthesize.ts +26 -27
  79. package/src/tools/agent/thread.ts +42 -51
  80. package/src/tools/agent/wisdom-frame.ts +45 -38
  81. package/src/tools/control-room/server.ts +0 -2
  82. package/src/tools/control-room/ui/dist/assets/{index-BZMDzWFa.js → index-mFpXpiBj.js} +1 -1
  83. package/src/tools/control-room/ui/dist/index.html +1 -1
  84. package/src/tools/control-room/ui/screens/log.tsx +10 -1
  85. package/src/tools/lib/command.ts +275 -0
  86. package/src/tools/subagent-doctor.ts +74 -9
  87. package/src/tools/token-cost.ts +18 -17
package/src/cli/index.ts CHANGED
@@ -1,32 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
  /**
3
- * PAL CLI — Portable Agent Layer
4
- *
5
- * Usage:
6
- * pal [agent-args...] Start the first installed agent: claude, codex, cursor-agent, copilot, opencode
7
- * pal cli <command> [options] Admin commands
8
- *
9
- * Admin commands (pal cli ...):
10
- * init Scaffold PAL home, install hooks for all targets
11
- * install [--claude] [--opencode] [--cursor] [--codex] Register hooks/skills for targets
12
- * uninstall [--claude] [--opencode] [--cursor] [--codex] Remove hooks/skills for targets
13
- * update Update PAL (git pull or npm update)
14
- * export [path] [--dry-run] Export user state to zip
15
- * import [path] [--dry-run] [--overwrite] Merge user state from zip
16
- * status Show current PAL configuration
17
- * doctor Check prerequisites and system health
18
- * usage Summarize token usage and cost
19
- * ledger <sub> [filters] Query the action ledger (log · show · stats)
20
- * rule list|approve|deny [id] Review rules drafted from your corrections
21
- * server start|stop|restart|status The control room, a local page over ~/.pal
22
- * <tool> [args] Run a built-in agent tool (project, thread, analyze, …)
23
- * skill run <skill> <tool> [-- args] Run a skill's own tool by name, not by path
24
- * skill link <name> Link a personal ~/.pal/skills/<name>/ into installed agents
25
- * skill doctor <name|--all> Evaluate one skill, or every installed skill, against the authoring best practices
26
- * subagent link <name> Install a personal ~/.pal/agents/<name>.md into installed agents
27
- * subagent doctor <name> Evaluate a subagent against the authoring best practices
28
- * debug [on|off] Enable / disable verbose hook debug logging
29
- * version | -v Print the installed PAL version
3
+ * PAL CLI — Portable Agent Layer. `pal [agent-args...]` starts the first
4
+ * installed agent; `pal cli <command>` runs the command tree in ./tree.ts.
30
5
  */
31
6
 
32
7
  import { spawnSync } from "node:child_process";
@@ -41,6 +16,12 @@ import {
41
16
  } from "node:fs";
42
17
  import { homedir } from "node:os";
43
18
  import { resolve } from "node:path";
19
+ import {
20
+ AGENT_NAMES,
21
+ AGENT_REGISTRY,
22
+ type AgentName,
23
+ INFERENCE_PRIORITY,
24
+ } from "../hooks/lib/agent-registry";
44
25
  import {
45
26
  appendImportLog,
46
27
  mergeArchive,
@@ -52,19 +33,35 @@ import { logDebug } from "../hooks/lib/log";
52
33
  import { ensureRegistered, writeRegistryEntry } from "../hooks/lib/machine";
53
34
  import { palHome, palPkg, paths, platform, toPath } from "../hooks/lib/paths";
54
35
  import { log, narrateSteps } from "../targets/lib";
55
- import { builtinToolVerbs, runBuiltinTool } from "./builtin-tools";
36
+ import { helpText, runCommand } from "../tools/lib/command";
56
37
  import { type DoctorResult, detectAgents } from "./doctor/agents";
57
38
  import { installChromium } from "./doctor/environment";
58
39
  import { runDoctor } from "./doctor/run";
59
40
  import { findSessionAgent, NO_SESSION_AGENT_MESSAGE } from "./session-agent";
41
+ import { cliTree, type DebugState } from "./tree";
60
42
 
61
43
  const allArgs = process.argv.slice(2);
44
+ const CLI_PATH = ["pal", "cli"];
45
+ const tree = cliTree({
46
+ init: ({ argv }) => init(argv),
47
+ install: async ({ argv }) => {
48
+ banner();
49
+ process.exit(await install(resolveTargets(argv), argv));
50
+ },
51
+ uninstall: ({ argv }) => uninstall(argv),
52
+ update: () => update(),
53
+ export: ({ argv }) => exportState(argv),
54
+ import: ({ argv }) => importState(argv),
55
+ status: () => status(),
56
+ doctor: ({ argv }) => doctor(argv),
57
+ debug: (state) => cliDebug(state),
58
+ version: () => showVersion(),
59
+ });
62
60
 
63
61
  // ── Route: pal cli <command> or pal [claude-args] ──
64
62
 
65
63
  if (allArgs[0] === "cli") {
66
- const [, command, ...args] = allArgs;
67
- await runCli(command, args);
64
+ process.exitCode = await runCommand(tree, allArgs.slice(1), CLI_PATH);
68
65
  } else if (allArgs[0] === "--help" || allArgs[0] === "-h" || allArgs[0] === "help") {
69
66
  showHelp();
70
67
  } else {
@@ -146,124 +143,12 @@ async function session(sessionArgs: string[]) {
146
143
  process.exit(exitCode);
147
144
  }
148
145
 
149
- // ── CLI dispatcher ──
150
-
151
- async function runCli(command: string | undefined, args: string[]) {
152
- if (command && (await runBuiltinTool(command, args))) return;
153
- switch (command) {
154
- case "init":
155
- await init(args);
156
- break;
157
- case "install":
158
- banner();
159
- process.exit(await install(resolveTargets(args), args));
160
- break;
161
- case "uninstall":
162
- await uninstall(args);
163
- break;
164
- case "export":
165
- await exportState(args);
166
- break;
167
- case "import":
168
- await importState(args);
169
- break;
170
- case "update":
171
- await update();
172
- break;
173
- case "status":
174
- await status();
175
- break;
176
- case "doctor": {
177
- const exitCode = runDoctor(args);
178
- if (args.includes("--probe-inference") || args.includes("--probe")) {
179
- await probeInference();
180
- }
181
- process.exit(exitCode);
182
- break;
183
- }
184
- case "migrate": {
185
- const { runMigrate } = await import("./migrate");
186
- runMigrate(args);
187
- break;
188
- }
189
- case "usage": {
190
- const { usage } = await import("../tools/token-cost");
191
- usage();
192
- break;
193
- }
194
- case "knowledge": {
195
- const { runKnowledge } = await import("./knowledge");
196
- const code = await runKnowledge(args);
197
- if (code !== 0) process.exit(code);
198
- break;
199
- }
200
- case "ledger": {
201
- const { runLedger } = await import("./ledger");
202
- const code = await runLedger(args);
203
- if (code !== 0) process.exit(code);
204
- break;
205
- }
206
- case "rule": {
207
- const { runRule } = await import("./rule");
208
- const code = await runRule(args);
209
- if (code !== 0) process.exit(code);
210
- break;
211
- }
212
- case "server": {
213
- const { runServer } = await import("./server");
214
- const code = await runServer(args);
215
- if (code !== 0) process.exit(code);
216
- break;
217
- }
218
- case "subagent": {
219
- const { runSubagent } = await import("./subagent");
220
- const code = await runSubagent(args);
221
- if (code !== 0) process.exit(code);
222
- break;
223
- }
224
- case "skill": {
225
- const { runSkill } = await import("./skill");
226
- const code = await runSkill(args);
227
- if (code !== 0) process.exit(code);
228
- break;
229
- }
230
- case "actor":
231
- case "machine": {
232
- const { runIdentity } = await import("./identity");
233
- const code = runIdentity(command, args);
234
- if (code !== 0) process.exit(code);
235
- break;
236
- }
237
- case "telos": {
238
- const { runTelos } = await import("./personal-context");
239
- const code = runTelos(args);
240
- if (code !== 0) process.exit(code);
241
- break;
242
- }
243
- case "timezone": {
244
- const { runTimezone } = await import("./personal-context");
245
- const code = runTimezone(args);
246
- if (code !== 0) process.exit(code);
247
- break;
248
- }
249
- case "debug":
250
- cliDebug(args);
251
- break;
252
- case "version":
253
- case "-v":
254
- case "--version":
255
- showVersion();
256
- break;
257
- case "--help":
258
- case "-h":
259
- case "help":
260
- showHelp();
261
- break;
262
- default:
263
- if (command) log.error(`Unknown command: ${command}`);
264
- showHelp();
265
- process.exit(command ? 1 : 0);
146
+ async function doctor(args: string[]): Promise<never> {
147
+ const exitCode = runDoctor(args);
148
+ if (args.includes("--probe-inference") || args.includes("--probe")) {
149
+ await probeInference();
266
150
  }
151
+ process.exit(exitCode);
267
152
  }
268
153
 
269
154
  // ── Helpers ──
@@ -277,145 +162,52 @@ function banner() {
277
162
  }
278
163
 
279
164
  function showHelp() {
280
- console.log(`
281
- Usage:
282
- pal [agent-args...] Start the first installed agent
283
- pal cli <command> [options] Admin commands
284
-
285
- Admin commands:
286
- pal cli init [--claude] [--opencode] [--cursor] [--codex] Scaffold and install (default: all), then run the doctor
287
- pal cli install [--claude] [--opencode] [--cursor] [--codex] Register hooks for targets, then run the doctor
288
- Add --verbose to either for the step-by-step log
289
- pal cli uninstall [--claude] [--opencode] [--cursor] [--codex] Remove hooks for targets
290
- pal cli update Update PAL (git pull or npm update)
291
- pal cli export [path] [--dry-run] Export state to zip
292
- pal cli import [path] [--dry-run] Merge state from zip (--overwrite to replace)
293
- pal cli status Show PAL configuration
294
- pal cli doctor [--verbose] [--json] Find what is wrong and how to fix it; exits 1 on any failure
295
- [--probe-inference] Also fire real inference per route
296
- pal cli migrate [--list] [--dry-run] Run pending data migrations
297
- pal cli <tool> [args] Run a built-in agent tool ('<tool> --help' for its flags):
298
- ${builtinToolVerbs.join(" · ")}
299
- pal cli usage Summarize token usage and cost
300
- pal cli actor [label <name>] Show or rename this actor (who caused a record)
301
- pal cli machine [label <name>] Show or rename this install (where it was written)
302
- pal cli telos Which TELOS topics are answered, in interview order
303
- pal cli timezone [<zone>] Show or set your timezone (IANA name)
304
- pal cli knowledge <sub> [args] Query & manage the knowledge store
305
- (search · graph · stats · hubs · find · show · add · ls)
306
- pal cli ledger <sub> [filters] Query the action ledger (log · show · stats)
307
- e.g. ledger log --project X --since 7d
308
- pal cli rule list|approve|deny [id] Review rules drafted from your corrections
309
- pal cli server start|stop|restart|status The control room: a local page to open before a terminal
310
- pal cli skill run <skill> <tool> [-- args] Run ~/.pal/skills/<skill>/tools/<tool> by name
311
- pal cli skill link <name> Link a personal ~/.pal/skills/<name>/ into installed agents
312
- pal cli skill doctor <name|--all> Evaluate one skill, or every installed skill
313
- pal cli skill author-model Print the flagship model that authors skills for the active agent
314
- pal cli subagent link <name> Install a personal ~/.pal/agents/<name>.md into installed agents
315
- pal cli subagent doctor <name> Evaluate a subagent against the authoring best practices
316
- pal cli subagent list List the user-authored subagents in ~/.pal/agents/
317
- pal cli subagent author-model Print the flagship model that authors subagents for the active agent
318
- pal cli debug [on|off] Enable/disable verbose hook debug logging (persisted)
319
- pal cli version | -v Print the installed PAL version
320
-
321
- Environment:
322
- PAL_HOME Override user state directory (default: ~/.pal or repo root)
323
- PAL_PKG Override package root
324
- PAL_CLAUDE_DIR Override Claude config dir (default: ~/.claude)
325
- PAL_OPENCODE_DIR Override opencode config dir (default: ~/.config/opencode)
326
- PAL_CURSOR_DIR Override Cursor config dir (default: ~/.cursor)
327
- PAL_COPILOT_DIR Override Copilot config dir (default: ~/.copilot)
328
- PAL_CODEX_DIR Override Codex config dir (default: ~/.codex)
329
- PAL_AGENTS_DIR Override agents dir (default: ~/.agents)
330
- `);
165
+ console.log(
166
+ `Usage: pal [agent-args...]\n\n Start the first installed agent, passing the arguments on\n\n${helpText(tree, CLI_PATH)}`
167
+ );
331
168
  }
332
169
 
333
- type Targets = {
334
- claude: boolean;
335
- opencode: boolean;
336
- cursor: boolean;
337
- copilot: boolean;
338
- codex: boolean;
339
- };
170
+ type Targets = Record<AgentName, boolean>;
171
+
172
+ function targetsWhere(selected: (agent: AgentName) => boolean): Targets {
173
+ return Object.fromEntries(
174
+ AGENT_NAMES.map((agent) => [agent, selected(agent)])
175
+ ) as Targets;
176
+ }
177
+
178
+ function namedTargets(args: string[]): AgentName[] {
179
+ if (args.includes("--all")) return AGENT_NAMES;
180
+ return AGENT_NAMES.filter((agent) => args.includes(`--${agent}`));
181
+ }
340
182
 
341
183
  function parseTargets(args: string[]): Targets {
342
- let claude = false;
343
- let opencode = false;
344
- let cursor = false;
345
- let copilot = false;
346
- let codex = false;
347
- for (const arg of args) {
348
- if (arg === "--claude") claude = true;
349
- else if (arg === "--opencode") opencode = true;
350
- else if (arg === "--cursor") cursor = true;
351
- else if (arg === "--copilot") copilot = true;
352
- else if (arg === "--codex") codex = true;
353
- else if (arg === "--all") {
354
- claude = true;
355
- opencode = true;
356
- cursor = true;
357
- copilot = true;
358
- codex = true;
359
- }
360
- }
361
- if (!claude && !opencode && !cursor && !copilot && !codex)
362
- return { claude: true, opencode: true, cursor: true, copilot: true, codex: true };
363
- return { claude, opencode, cursor, copilot, codex };
184
+ const named = namedTargets(args);
185
+ return named.length === 0
186
+ ? targetsWhere(() => true)
187
+ : targetsWhere((a) => named.includes(a));
364
188
  }
365
189
 
366
190
  /** Resolve targets against available agents. Errors if explicitly requested but missing. */
367
191
  function resolveTargets(args: string[], health?: DoctorResult): Targets {
368
192
  const requested = parseTargets(args);
369
193
  const h = health || detectAgents();
370
- const explicit = args.some(
371
- (a) =>
372
- a === "--claude" ||
373
- a === "--opencode" ||
374
- a === "--cursor" ||
375
- a === "--copilot" ||
376
- a === "--codex" ||
377
- a === "--all"
378
- );
379
194
 
380
- if (explicit) {
381
- if (requested.claude && !h.claude.available) {
382
- log.error("Claude Code is not installed. Run 'pal cli doctor' for details.");
383
- process.exit(1);
384
- }
385
- if (requested.opencode && !h.opencode.available) {
386
- log.error("opencode is not installed. Run 'pal cli doctor' for details.");
387
- process.exit(1);
388
- }
389
- if (requested.cursor && !h.cursor.available) {
390
- log.error("Cursor is not installed. Run 'pal cli doctor' for details.");
391
- process.exit(1);
392
- }
393
- if (requested.copilot && !h.copilot.available) {
394
- log.error("Copilot is not installed. Run 'pal cli doctor' for details.");
395
- process.exit(1);
396
- }
397
- if (requested.codex && !h.codex.available) {
398
- log.error("Codex is not installed. Run 'pal cli doctor' for details.");
195
+ if (namedTargets(args).length > 0) {
196
+ const missing = AGENT_NAMES.find((agent) => requested[agent] && !h[agent].available);
197
+ if (missing) {
198
+ log.error(
199
+ `${AGENT_REGISTRY[missing].label} is not installed. Run 'pal cli doctor' for details.`
200
+ );
399
201
  process.exit(1);
400
202
  }
401
203
  return requested;
402
204
  }
403
205
 
404
- // Default (no flags) — install for available agents only
405
- const targets: Targets = {
406
- claude: h.claude.available,
407
- opencode: h.opencode.available,
408
- cursor: h.cursor.available,
409
- copilot: h.copilot.available,
410
- codex: h.codex.available,
411
- };
412
-
413
- if (!targets.claude) log.info("Skipping Claude Code (not installed)");
414
- if (!targets.opencode) log.info("Skipping opencode (not installed)");
415
- if (!targets.cursor) log.info("Skipping Cursor (not installed)");
416
- if (!targets.copilot) log.info("Skipping Copilot (not installed)");
417
- if (!targets.codex) log.info("Skipping Codex (not installed)");
418
-
206
+ const targets = targetsWhere((agent) => h[agent].available);
207
+ for (const agent of AGENT_NAMES) {
208
+ if (!targets[agent])
209
+ log.info(`Skipping ${AGENT_REGISTRY[agent].label} (not installed)`);
210
+ }
419
211
  return targets;
420
212
  }
421
213
 
@@ -435,10 +227,9 @@ async function probeInference(): Promise<void> {
435
227
  const yellow = "\x1b[33m";
436
228
  const dim = "\x1b[90m";
437
229
  const reset = "\x1b[0m";
438
- const agents = ["claude", "codex", "opencode", "copilot", "cursor"] as const;
439
230
  const savedAgent = process.env.PAL_AGENT;
440
231
  try {
441
- for (const agent of agents) {
232
+ for (const agent of INFERENCE_PRIORITY) {
442
233
  process.env.PAL_AGENT = agent;
443
234
  const preview = previewInferenceRoute();
444
235
  const tag = `${agent.padEnd(10)} → ${preview.route.padEnd(15)}`;
@@ -515,14 +306,44 @@ function runQuietly(cmd: string, args: string[], cwd: string): number | null {
515
306
  return r.status;
516
307
  }
517
308
 
518
- function targetInstallers(): [keyof Targets, string, () => Promise<unknown>][] {
519
- return [
520
- ["claude", "Claude Code", () => import("../targets/claude/install")],
521
- ["opencode", "opencode", () => import("../targets/opencode/install")],
522
- ["cursor", "Cursor", () => import("../targets/cursor/install")],
523
- ["copilot", "Copilot", () => import("../targets/copilot/install")],
524
- ["codex", "Codex", () => import("../targets/codex/install")],
525
- ];
309
+ function targetScripts(): Record<
310
+ AgentName,
311
+ { install: () => Promise<unknown>; uninstall: () => Promise<unknown> }
312
+ > {
313
+ return {
314
+ claude: {
315
+ install: () => import("../targets/claude/install"),
316
+ uninstall: () => import("../targets/claude/uninstall"),
317
+ },
318
+ opencode: {
319
+ install: () => import("../targets/opencode/install"),
320
+ uninstall: () => import("../targets/opencode/uninstall"),
321
+ },
322
+ cursor: {
323
+ install: () => import("../targets/cursor/install"),
324
+ uninstall: () => import("../targets/cursor/uninstall"),
325
+ },
326
+ copilot: {
327
+ install: () => import("../targets/copilot/install"),
328
+ uninstall: () => import("../targets/copilot/uninstall"),
329
+ },
330
+ codex: {
331
+ install: () => import("../targets/codex/install"),
332
+ uninstall: () => import("../targets/codex/uninstall"),
333
+ },
334
+ antigravity: {
335
+ install: () => import("../targets/antigravity/install"),
336
+ uninstall: () => import("../targets/antigravity/uninstall"),
337
+ },
338
+ };
339
+ }
340
+
341
+ function targetInstallers(): [AgentName, string, () => Promise<unknown>][] {
342
+ return AGENT_NAMES.map((agent) => [
343
+ agent,
344
+ AGENT_REGISTRY[agent].label,
345
+ targetScripts()[agent].install,
346
+ ]);
526
347
  }
527
348
 
528
349
  async function install(targets: Targets, args: string[]): Promise<number> {
@@ -572,7 +393,8 @@ async function install(targets: Targets, args: string[]): Promise<number> {
572
393
 
573
394
  // The rest of the shared work reads what the installers just wrote: the index
574
395
  // walks ~/.pal/skills, and the digests land in ~/.cursor/rules and
575
- // ~/.copilot/instructions, which are skipped when the agent's home is absent.
396
+ // ~/.copilot/instructions and the Antigravity plugin's rules/, which are skipped
397
+ // when the agent's home is absent.
576
398
  const { writeContextDigests } = await import("../hooks/handlers/context-digests");
577
399
  const indexedSkills = generateSkillIndex();
578
400
  writeContextDigests();
@@ -606,33 +428,9 @@ async function refreshControlRoom(): Promise<void> {
606
428
  async function uninstall(args: string[]) {
607
429
  const targets = parseTargets(args);
608
430
 
609
- if (targets.claude) {
610
- console.log("━━━ Claude Code ━━━");
611
- await import("../targets/claude/uninstall");
612
- console.log("");
613
- }
614
-
615
- if (targets.opencode) {
616
- console.log("━━━ opencode ━━━");
617
- await import("../targets/opencode/uninstall");
618
- console.log("");
619
- }
620
-
621
- if (targets.cursor) {
622
- console.log("━━━ Cursor ━━━");
623
- await import("../targets/cursor/uninstall");
624
- console.log("");
625
- }
626
-
627
- if (targets.copilot) {
628
- console.log("━━━ Copilot ━━━");
629
- await import("../targets/copilot/uninstall");
630
- console.log("");
631
- }
632
-
633
- if (targets.codex) {
634
- console.log("━━━ Codex ━━━");
635
- await import("../targets/codex/uninstall");
431
+ for (const agent of AGENT_NAMES.filter((a) => targets[a])) {
432
+ console.log(`━━━ ${AGENT_REGISTRY[agent].label} ━━━`);
433
+ await targetScripts()[agent].uninstall();
636
434
  console.log("");
637
435
  }
638
436
 
@@ -891,18 +689,17 @@ async function update() {
891
689
  process.exit(reinstallInFreshProcess());
892
690
  }
893
691
 
894
- function cliDebug(args: string[]) {
692
+ function cliDebug(state: DebugState) {
895
693
  const stateDir = resolve(palHome(), "memory", "state");
896
694
  const flagFile = resolve(stateDir, "debug-enabled");
897
695
  // Must match log.ts's logFile() — reporting a different path sends anyone
898
696
  // debugging a hook to an empty file.
899
697
  const logFile = resolve(paths.debug(), "debug.log");
900
- const sub = args[0];
901
- if (sub === "on") {
698
+ if (state === "on") {
902
699
  mkdirSync(stateDir, { recursive: true });
903
700
  writeFileSync(flagFile, "");
904
701
  log.success(`Debug logging enabled → ${logFile}`);
905
- } else if (sub === "off") {
702
+ } else if (state === "off") {
906
703
  rmSync(flagFile, { force: true });
907
704
  log.success("Debug logging disabled");
908
705
  } else {