portable-agent-layer 0.10.0 → 0.12.0

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 (45) hide show
  1. package/assets/skills/{analyze-pdf.md → analyze-pdf/SKILL.md} +4 -4
  2. package/{src → assets/skills/analyze-pdf}/tools/pdf-download.ts +3 -3
  3. package/assets/skills/{analyze-youtube.md → analyze-youtube/SKILL.md} +4 -4
  4. package/{src → assets/skills/analyze-youtube}/tools/youtube-analyze.ts +2 -2
  5. package/assets/skills/{council.md → council/SKILL.md} +3 -2
  6. package/assets/skills/{create-skill.md → create-skill/SKILL.md} +2 -1
  7. package/assets/skills/{extract-entities.md → extract-entities/SKILL.md} +4 -5
  8. package/{src → assets/skills/extract-entities}/tools/entity-save.ts +3 -3
  9. package/assets/skills/{extract-wisdom.md → extract-wisdom/SKILL.md} +3 -2
  10. package/assets/skills/{first-principles.md → first-principles/SKILL.md} +3 -2
  11. package/assets/skills/{fyzz-chat-api.md → fyzz-chat-api/SKILL.md} +6 -6
  12. package/{src → assets/skills/fyzz-chat-api}/tools/fyzz-api.ts +6 -6
  13. package/assets/skills/{reflect.md → reflect/SKILL.md} +2 -1
  14. package/assets/skills/{research.md → research/SKILL.md} +2 -1
  15. package/assets/skills/{review.md → review/SKILL.md} +2 -1
  16. package/assets/skills/{summarize.md → summarize/SKILL.md} +3 -2
  17. package/assets/skills/telos/SKILL.md +94 -0
  18. package/assets/skills/telos/tools/update-telos.ts +100 -0
  19. package/assets/skills/think/SKILL.md +47 -0
  20. package/assets/templates/AGENTS.md.template +51 -32
  21. package/assets/templates/PAL/ALGORITHM.md +120 -0
  22. package/assets/templates/PAL/CONTEXT_ROUTING.md +28 -0
  23. package/assets/templates/PAL/MEMORY_SYSTEM.md +26 -0
  24. package/assets/templates/PAL/OPINION_TRACKING.md +3 -0
  25. package/assets/templates/PAL/STEERING_RULES.md +43 -0
  26. package/assets/templates/PAL/WORK_TRACKING.md +14 -0
  27. package/assets/templates/pal-settings.json +32 -0
  28. package/assets/templates/settings.claude.json +80 -0
  29. package/package.json +4 -7
  30. package/src/cli/index.ts +7 -0
  31. package/src/cli/setup-identity.ts +119 -0
  32. package/src/hooks/lib/claude-md.ts +52 -26
  33. package/src/hooks/lib/context.ts +49 -25
  34. package/src/hooks/lib/paths.ts +2 -0
  35. package/src/hooks/lib/security.ts +2 -0
  36. package/src/hooks/lib/setup.ts +4 -16
  37. package/src/targets/claude/install.ts +20 -93
  38. package/src/targets/claude/uninstall.ts +22 -47
  39. package/src/targets/lib.ts +207 -48
  40. package/src/targets/opencode/install.ts +13 -2
  41. package/src/targets/opencode/uninstall.ts +4 -1
  42. package/assets/templates/STEERING-RULES.md +0 -23
  43. package/assets/templates/telos/IDENTITY.md +0 -4
  44. package/src/cli/install.ts +0 -86
  45. package/src/cli/uninstall.ts +0 -45
@@ -41,6 +41,101 @@ export function writeJson(path: string, data: unknown): void {
41
41
  writeFileSync(path, `${JSON.stringify(data, null, 2)}\n`, "utf-8");
42
42
  }
43
43
 
44
+ // --- Settings template merge/unmerge ---
45
+
46
+ type HookEntry = { matcher?: string; hooks?: Array<{ type: string; command: string }> };
47
+ type Settings = Record<string, unknown> & {
48
+ hooks?: Record<string, HookEntry[]>;
49
+ permissions?: { allow?: string[]; deny?: string[]; ask?: string[] };
50
+ };
51
+
52
+ /**
53
+ * Load a settings template, replacing {{PKG_ROOT}} with the actual path.
54
+ */
55
+ export function loadSettingsTemplate(templatePath: string, pkgRoot: string): Settings {
56
+ const raw = readFileSync(templatePath, "utf-8");
57
+ const resolved = raw.replaceAll("{{PKG_ROOT}}", pkgRoot);
58
+ return JSON.parse(resolved) as Settings;
59
+ }
60
+
61
+ /**
62
+ * Merge a PAL settings template into existing settings.
63
+ * - hooks: deduplicate by command string
64
+ * - permissions.allow: deduplicate by value
65
+ * - other keys: template values are added if not already present
66
+ */
67
+ export function mergeSettings(existing: Settings, template: Settings): Settings {
68
+ const result = { ...existing };
69
+
70
+ // Merge hooks (deduplicate by command)
71
+ if (template.hooks) {
72
+ if (!result.hooks) result.hooks = {};
73
+ for (const [event, entries] of Object.entries(template.hooks)) {
74
+ const current = result.hooks[event] ?? [];
75
+ for (const entry of entries) {
76
+ const cmd = entry.hooks?.[0]?.command;
77
+ if (cmd && !current.some((e) => e.hooks?.[0]?.command === cmd)) {
78
+ current.push(entry);
79
+ }
80
+ }
81
+ result.hooks[event] = current;
82
+ }
83
+ }
84
+
85
+ // Merge permissions.allow (deduplicate)
86
+ if (template.permissions?.allow) {
87
+ if (!result.permissions) result.permissions = {};
88
+ if (!result.permissions.allow) result.permissions.allow = [];
89
+ for (const perm of template.permissions.allow) {
90
+ if (!result.permissions.allow.includes(perm)) {
91
+ result.permissions.allow.push(perm);
92
+ }
93
+ }
94
+ }
95
+
96
+ return result;
97
+ }
98
+
99
+ /**
100
+ * Remove everything a PAL settings template added from existing settings.
101
+ * - hooks: remove entries whose command matches any template command
102
+ * - permissions.allow: remove entries that appear in the template
103
+ * - cleans up empty arrays/objects
104
+ */
105
+ export function unmergeSettings(existing: Settings, template: Settings): Settings {
106
+ const result = { ...existing };
107
+
108
+ // Collect all PAL hook commands from template
109
+ if (template.hooks && result.hooks) {
110
+ const palCommands = new Set<string>();
111
+ for (const entries of Object.values(template.hooks)) {
112
+ for (const entry of entries) {
113
+ const cmd = entry.hooks?.[0]?.command;
114
+ if (cmd) palCommands.add(cmd);
115
+ }
116
+ }
117
+
118
+ for (const [event, entries] of Object.entries(result.hooks)) {
119
+ result.hooks[event] = entries.filter((e) => {
120
+ const cmd = e.hooks?.[0]?.command;
121
+ return !cmd || !palCommands.has(cmd);
122
+ });
123
+ if (result.hooks[event].length === 0) delete result.hooks[event];
124
+ }
125
+ if (Object.keys(result.hooks).length === 0) delete result.hooks;
126
+ }
127
+
128
+ // Remove PAL permissions
129
+ if (template.permissions?.allow && result.permissions?.allow) {
130
+ const palPerms = new Set(template.permissions.allow);
131
+ result.permissions.allow = result.permissions.allow.filter((p) => !palPerms.has(p));
132
+ if (result.permissions.allow.length === 0) delete result.permissions.allow;
133
+ if (Object.keys(result.permissions).length === 0) delete result.permissions;
134
+ }
135
+
136
+ return result;
137
+ }
138
+
44
139
  // --- TELOS scaffolding ---
45
140
 
46
141
  /** Copy template files into telos/ without overwriting existing ones */
@@ -60,14 +155,82 @@ export function scaffoldTelos(): void {
60
155
  }
61
156
  }
62
157
 
158
+ // --- PAL settings scaffolding ---
159
+
160
+ /** Copy pal-settings.json template to memory/ without overwriting */
161
+ export function scaffoldPalSettings(): void {
162
+ const src = resolve(assets.skills(), "..", "templates", "pal-settings.json");
163
+ if (!existsSync(src)) return;
164
+
165
+ const memDir = resolve(palHome(), "memory");
166
+ mkdirSync(memDir, { recursive: true });
167
+
168
+ const dst = resolve(memDir, "pal-settings.json");
169
+ if (!existsSync(dst)) {
170
+ copyFileSync(src, dst);
171
+ log.info("Created pal-settings.json from template");
172
+ }
173
+ }
174
+
175
+ // --- PAL docs (modular context routing files) ---
176
+
177
+ const PAL_DOCS_DIR = resolve(platform.agentsDir(), "PAL");
178
+
179
+ /**
180
+ * Install PAL system docs into ~/.agents/PAL/.
181
+ * Always overwrites — these are engine-managed, not user-editable.
182
+ */
183
+ export function copyPalDocs(): number {
184
+ const srcDir = assets.palDocs();
185
+ if (!existsSync(srcDir)) return 0;
186
+
187
+ mkdirSync(PAL_DOCS_DIR, { recursive: true });
188
+ let count = 0;
189
+
190
+ for (const file of readdirSync(srcDir).filter((f) => f.endsWith(".md"))) {
191
+ const src = resolve(srcDir, file);
192
+ const dst = resolve(PAL_DOCS_DIR, file);
193
+ copyFileSync(src, dst);
194
+ count++;
195
+ }
196
+
197
+ // Symlink ~/.agents/PAL/telos and ~/.agents/PAL/memory → <palHome>/...
198
+ const linkType = process.platform === "win32" ? "junction" : "dir";
199
+ ensureSymlink(resolve(PAL_DOCS_DIR, "telos"), resolve(palHome(), "telos"), linkType);
200
+ ensureSymlink(resolve(PAL_DOCS_DIR, "memory"), resolve(palHome(), "memory"), linkType);
201
+
202
+ return count;
203
+ }
204
+
205
+ /** Remove PAL system docs from ~/.agents/PAL/ */
206
+ export function removePalDocs(): void {
207
+ if (!existsSync(PAL_DOCS_DIR)) return;
208
+ for (const file of readdirSync(PAL_DOCS_DIR).filter((f) => f.endsWith(".md"))) {
209
+ try {
210
+ unlinkSync(resolve(PAL_DOCS_DIR, file));
211
+ } catch {
212
+ /* gone */
213
+ }
214
+ }
215
+ try {
216
+ rmSync(PAL_DOCS_DIR, { recursive: true });
217
+ log.info("Removed ~/.agents/PAL/");
218
+ } catch {
219
+ /* gone */
220
+ }
221
+ }
222
+
63
223
  // --- Skills ---
64
224
 
65
225
  const AGENTS_SKILLS_DIR = resolve(platform.agentsDir(), "skills");
66
226
 
67
227
  /**
68
- * Install PAL skills into the shared ~/.agents/skills/<name>/SKILL.md standard,
69
- * then symlink ~/.claude/skills/<name> → ../../.agents/skills/<name>.
70
- * Additive — skips skills already installed.
228
+ * Install PAL skills by symlinking:
229
+ * ~/.agents/skills/<name> → <repo>/assets/skills/<name> (source of truth)
230
+ * ~/.claude/skills/<name> ~/.agents/skills/<name> (Claude Code discovery)
231
+ *
232
+ * Symlinks mean tools inside skills can import from the repo (src/hooks/lib/*)
233
+ * and everything resolves naturally. Additive — skips skills already installed.
71
234
  */
72
235
  export function copySkills(claudeSkillsDir: string): number {
73
236
  const skillsDir = assets.skills();
@@ -75,69 +238,65 @@ export function copySkills(claudeSkillsDir: string): number {
75
238
 
76
239
  mkdirSync(AGENTS_SKILLS_DIR, { recursive: true });
77
240
  mkdirSync(claudeSkillsDir, { recursive: true });
241
+ const linkType = process.platform === "win32" ? "junction" : "dir";
78
242
  let count = 0;
79
243
 
80
- for (const file of readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
81
- const name = file.replace(/\.md$/, "");
82
- const src = resolve(skillsDir, file);
83
- const agentSkillDir = resolve(AGENTS_SKILLS_DIR, name);
84
- const agentSkillFile = resolve(agentSkillDir, "SKILL.md");
244
+ for (const name of readdirSync(skillsDir)) {
245
+ const srcDir = resolve(skillsDir, name);
246
+ if (!existsSync(resolve(srcDir, "SKILL.md"))) continue;
247
+
248
+ // ~/.agents/skills/<name> <repo>/assets/skills/<name>
249
+ const agentLink = resolve(AGENTS_SKILLS_DIR, name);
250
+ ensureSymlink(agentLink, srcDir, linkType);
251
+
252
+ // ~/.claude/skills/<name> → ~/.agents/skills/<name>
85
253
  const claudeLink = resolve(claudeSkillsDir, name);
254
+ ensureSymlink(claudeLink, agentLink, linkType);
86
255
 
87
- // Install into ~/.agents/skills/<name>/SKILL.md
88
- if (!existsSync(agentSkillFile)) {
89
- mkdirSync(agentSkillDir, { recursive: true });
90
- copyFileSync(src, agentSkillFile);
91
- log.info(`Added skill: ${name}`);
92
- count++;
93
- } else {
94
- log.warn(`Skill exists, skipping: ${name}`);
95
- }
256
+ log.info(`Linked skill: ${name}`);
257
+ count++;
258
+ }
259
+ return count;
260
+ }
96
261
 
97
- // Create ~/.claude/skills/<name> symlink if missing or not a symlink
98
- // Use 'junction' on Windows (no admin required), 'dir' symlink on Unix
99
- const linkType = process.platform === "win32" ? "junction" : "dir";
262
+ /** Create or update a symlink/junction, replacing any non-symlink entry. */
263
+ function ensureSymlink(link: string, target: string, type: "dir" | "junction"): void {
264
+ try {
265
+ const st = lstatSync(link);
266
+ if (st.isSymbolicLink()) return; // already a symlink, leave it
267
+ rmSync(link, { recursive: true, force: true });
268
+ } catch {
269
+ // doesn't exist or broken — clean up just in case
100
270
  try {
101
- const st = lstatSync(claudeLink);
102
- if (!st.isSymbolicLink()) {
103
- rmSync(claudeLink, { recursive: true, force: true });
104
- symlinkSync(`../../.agents/skills/${name}`, claudeLink, linkType);
105
- }
271
+ rmSync(link, { recursive: true, force: true });
106
272
  } catch {
107
- // Entry might exist but lstatSync failed (broken symlink/junction on Windows)
108
- try {
109
- rmSync(claudeLink, { recursive: true, force: true });
110
- } catch {
111
- /* gone */
112
- }
113
- symlinkSync(`../../.agents/skills/${name}`, claudeLink, linkType);
273
+ /* gone */
114
274
  }
115
275
  }
116
- return count;
276
+ symlinkSync(target, link, type);
117
277
  }
118
278
 
119
- /** Remove PAL skills from ~/.agents/skills/ and their symlinks from ~/.claude/skills/ */
279
+ /** Remove PAL skill symlinks from ~/.agents/skills/ and ~/.claude/skills/ */
120
280
  export function removeSkills(claudeSkillsDir: string): string[] {
121
281
  const skillsDir = assets.skills();
122
282
  if (!existsSync(skillsDir)) return [];
123
283
 
124
284
  const removed: string[] = [];
125
- for (const file of readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
126
- const name = file.replace(/\.md$/, "");
285
+ for (const name of readdirSync(skillsDir)) {
286
+ if (!existsSync(resolve(skillsDir, name, "SKILL.md"))) continue;
127
287
 
128
- const agentSkillDir = resolve(AGENTS_SKILLS_DIR, name);
129
- if (existsSync(agentSkillDir)) {
130
- rmSync(agentSkillDir, { recursive: true });
131
- removed.push(name);
132
- log.info(`Removed skill: ${name}`);
133
- }
134
-
135
- const claudeLink = resolve(claudeSkillsDir, name);
136
- try {
137
- unlinkSync(claudeLink);
138
- } catch {
139
- /* already gone */
288
+ for (const link of [
289
+ resolve(AGENTS_SKILLS_DIR, name),
290
+ resolve(claudeSkillsDir, name),
291
+ ]) {
292
+ try {
293
+ unlinkSync(link);
294
+ } catch {
295
+ /* already gone */
296
+ }
140
297
  }
298
+ removed.push(name);
299
+ log.info(`Removed skill: ${name}`);
141
300
  }
142
301
  return removed;
143
302
  }
@@ -7,7 +7,14 @@ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "
7
7
  import { resolve } from "node:path";
8
8
  import { regenerateIfNeeded } from "../../hooks/lib/claude-md";
9
9
  import { palPkg, platform } from "../../hooks/lib/paths";
10
- import { copyAgentsForOpencode, copySkills, countSkills, log, writeJson } from "../lib";
10
+ import {
11
+ copyAgentsForOpencode,
12
+ copyPalDocs,
13
+ copySkills,
14
+ countSkills,
15
+ log,
16
+ writeJson,
17
+ } from "../lib";
11
18
 
12
19
  const PKG_ROOT = palPkg();
13
20
  const OC_GLOBAL_DIR = platform.opencodeDir();
@@ -53,7 +60,11 @@ log.success("Installed skills to ~/.agents/skills/");
53
60
  const ocAgentsDir = resolve(OC_GLOBAL_DIR, "agents");
54
61
  copyAgentsForOpencode(ocAgentsDir);
55
62
 
56
- // --- 5. Generate ~/.config/opencode/AGENTS.md ---
63
+ // --- 5. Copy PAL system docs ---
64
+ const palDocsCount = copyPalDocs();
65
+ log.success(`Installed ${palDocsCount} PAL docs to ~/.agents/PAL/`);
66
+
67
+ // --- 6. Generate ~/.config/opencode/AGENTS.md ---
57
68
  regenerateIfNeeded();
58
69
  log.success("Generated ~/.config/opencode/AGENTS.md");
59
70
 
@@ -6,7 +6,7 @@
6
6
  import { unlinkSync } from "node:fs";
7
7
  import { resolve } from "node:path";
8
8
  import { platform } from "../../hooks/lib/paths";
9
- import { log, removeAgentsFromOpencode, removeSkills } from "../lib";
9
+ import { log, removeAgentsFromOpencode, removePalDocs, removeSkills } from "../lib";
10
10
 
11
11
  const OC_GLOBAL_DIR = platform.opencodeDir() || "";
12
12
 
@@ -38,6 +38,9 @@ if (removedAgents.length > 0)
38
38
  `Removed ${removedAgents.length} opencode agent(s): ${removedAgents.join(", ")}`
39
39
  );
40
40
 
41
+ // --- Remove PAL system docs ---
42
+ removePalDocs();
43
+
41
44
  // --- Remove AGENTS.md and CLAUDE.md symlink ---
42
45
  const agentsMd = resolve(OC_GLOBAL_DIR, "AGENTS.md");
43
46
  const claudeMd = resolve(PAL_CLAUDE_DIR, "CLAUDE.md");
@@ -1,23 +0,0 @@
1
- ## Steering Rules
2
-
3
- Behavioral directives — act on these, don't just know them.
4
-
5
- **Surgical fixes only.** When debugging, make precise corrections to the broken behavior. Never delete or rearchitect components as a fix. If you believe a component is the root cause, explain your reasoning and ask before removing it.
6
-
7
- **Never assert without verification.** Don't say something "is" a certain way unless you've verified it with your tools. After making changes, verify the result before claiming success. Evidence required — tests, diffs, tool output. Never "Done!" without proof.
8
-
9
- **First principles over bolt-ons.** Most problems are symptoms. Understand → Simplify → Reduce → Add (last resort). Don't accrue technical debt through band-aid solutions.
10
-
11
- **Read before modifying.** Understand existing code, imports, and patterns before suggesting changes.
12
-
13
- **One change when debugging.** Isolate, verify, proceed. Don't change multiple things at once.
14
-
15
- **Minimal scope.** Only change what was asked. No bonus refactoring, no extra cleanup, no unsolicited improvements.
16
-
17
- **Ask before destructive actions.** Deletes, force pushes, production deploys — always ask first.
18
-
19
- **Plan means stop.** "Create a plan" = present and STOP. No execution without approval.
20
-
21
- **Error recovery.** When told you did something wrong — review the session, identify the violation, fix it, then explain what happened and capture the learning. Don't ask "What did I do wrong?"
22
-
23
- **Act on what you know.** When tracked opinions or relationship notes reveal user preferences, apply them to your behavior. If you know the user prefers concise responses, be concise. If they prefer manual commits, never offer to commit.
@@ -1,4 +0,0 @@
1
- # AI Identity
2
-
3
- <!-- The name and identity of your personal AI assistant. -->
4
-
@@ -1,86 +0,0 @@
1
- /**
2
- * PAL — main installer entry point (TypeScript)
3
- * Usage: bun run install.ts [--claude] [--opencode] [--all]
4
- * Default: installs for both targets.
5
- */
6
-
7
- import { ensureSetupState, isSetupComplete } from "../hooks/lib/setup";
8
- import { log, scaffoldTelos } from "../targets/lib";
9
-
10
- // --- Parse args ---
11
- const args = process.argv.slice(2);
12
- let installClaude = false;
13
- let installOpencode = false;
14
-
15
- if (args.length === 0) {
16
- installClaude = true;
17
- installOpencode = true;
18
- }
19
-
20
- for (const arg of args) {
21
- if (arg === "--claude") installClaude = true;
22
- else if (arg === "--opencode") installOpencode = true;
23
- else if (arg === "--all") {
24
- installClaude = true;
25
- installOpencode = true;
26
- } else if (arg === "--help" || arg === "-h") {
27
- console.log("Usage: bun run install.ts [--claude] [--opencode] [--all]");
28
- console.log("");
29
- console.log(" --claude Install hooks/skills for Claude Code");
30
- console.log(" --opencode Install context/skills for opencode");
31
- console.log(" --all Install for both (default)");
32
- process.exit(0);
33
- } else {
34
- log.error(`Unknown option: ${arg}`);
35
- process.exit(1);
36
- }
37
- }
38
-
39
- // --- Check bun ---
40
- if (installClaude) {
41
- try {
42
- Bun.version; // always available in bun
43
- } catch {
44
- log.error("bun is required: curl -fsSL https://bun.sh/install | bash");
45
- process.exit(1);
46
- }
47
- }
48
-
49
- console.log("");
50
- console.log(" ╔═══════════════════════════════════╗");
51
- console.log(" ║ PAL — Portable Agent Layer ║");
52
- console.log(" ║ Non-destructive · Modular ║");
53
- console.log(" ╚═══════════════════════════════════╝");
54
- console.log("");
55
-
56
- // --- Scaffold TELOS + seed setup state ---
57
- scaffoldTelos();
58
- ensureSetupState();
59
-
60
- // --- Run target installers ---
61
- if (installClaude) {
62
- console.log("━━━ Claude Code ━━━");
63
- await import("../targets/claude/install");
64
- console.log("");
65
- }
66
-
67
- if (installOpencode) {
68
- console.log("━━━ opencode ━━━");
69
- await import("../targets/opencode/install");
70
- console.log("");
71
- }
72
-
73
- log.success("Done. Existing config was preserved — only new entries were added.");
74
- console.log("");
75
- log.info("Next steps:");
76
-
77
- const state = ensureSetupState();
78
- if (!isSetupComplete(state)) {
79
- log.info(" 1. Start a session — PAL will guide you through first-run setup");
80
- log.info(" 2. Or fill in telos/*.md manually, then re-run install.ts");
81
- } else {
82
- log.info(" 1. Fill in telos/*.md with your info (if not already done)");
83
- log.info(" 2. Re-run install.ts to regenerate context files");
84
- }
85
- log.info(" 3. Add skills by dropping .md files into skills/");
86
- log.info(" 4. Uninstall: bun run uninstall.ts [--claude] [--opencode]");
@@ -1,45 +0,0 @@
1
- /**
2
- * PAL — main uninstaller entry point (TypeScript)
3
- * Usage: bun run uninstall.ts [--claude] [--opencode] [--all]
4
- */
5
-
6
- import { palHome } from "../hooks/lib/paths";
7
- import { log } from "../targets/lib";
8
-
9
- const args = process.argv.slice(2);
10
- let removeClaude = false;
11
- let removeOpencode = false;
12
-
13
- if (args.length === 0) {
14
- removeClaude = true;
15
- removeOpencode = true;
16
- }
17
-
18
- for (const arg of args) {
19
- if (arg === "--claude") removeClaude = true;
20
- else if (arg === "--opencode") removeOpencode = true;
21
- else if (arg === "--all") {
22
- removeClaude = true;
23
- removeOpencode = true;
24
- } else if (arg === "--help" || arg === "-h") {
25
- console.log("Usage: bun run uninstall.ts [--claude] [--opencode] [--all]");
26
- process.exit(0);
27
- } else {
28
- log.error(`Unknown option: ${arg}`);
29
- process.exit(1);
30
- }
31
- }
32
-
33
- if (removeClaude) {
34
- console.log("━━━ Claude Code ━━━");
35
- await import("../targets/claude/uninstall");
36
- console.log("");
37
- }
38
-
39
- if (removeOpencode) {
40
- console.log("━━━ opencode ━━━");
41
- await import("../targets/opencode/uninstall");
42
- console.log("");
43
- }
44
-
45
- log.success(`PAL uninstalled. Your TELOS, skills, and memory are still in ${palHome()}.`);