portable-agent-layer 0.10.0 → 0.11.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.
- package/assets/skills/{analyze-pdf.md → analyze-pdf/SKILL.md} +4 -4
- package/{src → assets/skills/analyze-pdf}/tools/pdf-download.ts +3 -3
- package/assets/skills/{analyze-youtube.md → analyze-youtube/SKILL.md} +4 -4
- package/{src → assets/skills/analyze-youtube}/tools/youtube-analyze.ts +2 -2
- package/assets/skills/{council.md → council/SKILL.md} +3 -2
- package/assets/skills/{create-skill.md → create-skill/SKILL.md} +2 -1
- package/assets/skills/{extract-entities.md → extract-entities/SKILL.md} +4 -5
- package/{src → assets/skills/extract-entities}/tools/entity-save.ts +3 -3
- package/assets/skills/{extract-wisdom.md → extract-wisdom/SKILL.md} +3 -2
- package/assets/skills/{first-principles.md → first-principles/SKILL.md} +3 -2
- package/assets/skills/{fyzz-chat-api.md → fyzz-chat-api/SKILL.md} +6 -6
- package/{src → assets/skills/fyzz-chat-api}/tools/fyzz-api.ts +6 -6
- package/assets/skills/{reflect.md → reflect/SKILL.md} +2 -1
- package/assets/skills/{research.md → research/SKILL.md} +2 -1
- package/assets/skills/{review.md → review/SKILL.md} +2 -1
- package/assets/skills/{summarize.md → summarize/SKILL.md} +3 -2
- package/assets/skills/telos/SKILL.md +60 -0
- package/assets/skills/telos/tools/update-telos.ts +101 -0
- package/assets/skills/think/SKILL.md +47 -0
- package/assets/templates/AGENTS.md.template +8 -43
- package/assets/templates/PAL/CONTEXT_ROUTING.md +12 -0
- package/assets/templates/PAL/MEMORY_SYSTEM.md +26 -0
- package/assets/templates/PAL/OPINION_TRACKING.md +3 -0
- package/assets/templates/{STEERING-RULES.md → PAL/STEERING_RULES.md} +1 -1
- package/assets/templates/PAL/WORK_TRACKING.md +14 -0
- package/assets/templates/settings.claude.json +80 -0
- package/package.json +1 -5
- package/src/hooks/lib/claude-md.ts +10 -35
- package/src/hooks/lib/context.ts +0 -27
- package/src/hooks/lib/paths.ts +2 -0
- package/src/hooks/lib/security.ts +1 -0
- package/src/targets/claude/install.ts +16 -93
- package/src/targets/claude/uninstall.ts +22 -47
- package/src/targets/lib.ts +190 -48
- package/src/targets/opencode/install.ts +13 -2
- package/src/targets/opencode/uninstall.ts +4 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Work Tracking
|
|
2
|
+
|
|
3
|
+
PAL tracks your work across sessions in `memory/state/sessions.json` (auto-captured) and `memory/state/projects.json` (AI-managed).
|
|
4
|
+
|
|
5
|
+
## Projects
|
|
6
|
+
|
|
7
|
+
Update `projects.json` via the work-tracking library when:
|
|
8
|
+
- **Starting sustained multi-session work** → create a project with objectives and an id (slugified, e.g. "pdf-template-engine")
|
|
9
|
+
- **Making a key decision** → add to the project's `decisions` array
|
|
10
|
+
- **Completing a milestone** → add to `completed`, remove from `nextSteps`
|
|
11
|
+
- **Session ends with open work** → update `nextSteps` and `handoff`
|
|
12
|
+
- **Work is done** → set status to "completed"
|
|
13
|
+
|
|
14
|
+
Do not create projects for one-off questions or quick fixes.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Read",
|
|
5
|
+
"Grep",
|
|
6
|
+
"Glob",
|
|
7
|
+
"WebFetch",
|
|
8
|
+
"WebSearch",
|
|
9
|
+
"Bash(cat *)",
|
|
10
|
+
"Bash(head *)",
|
|
11
|
+
"Bash(tail *)",
|
|
12
|
+
"Bash(ls *)",
|
|
13
|
+
"Bash(find *)",
|
|
14
|
+
"Bash(grep *)",
|
|
15
|
+
"Bash(rg *)",
|
|
16
|
+
"Bash(wc *)",
|
|
17
|
+
"Bash(diff *)",
|
|
18
|
+
"Bash(which *)",
|
|
19
|
+
"Bash(file *)",
|
|
20
|
+
"Bash(stat *)",
|
|
21
|
+
"Bash(readlink *)",
|
|
22
|
+
"Bash(bun ~/.agents/skills/*/tools/*.ts *)"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"hooks": {
|
|
26
|
+
"SessionStart": [
|
|
27
|
+
{
|
|
28
|
+
"matcher": "",
|
|
29
|
+
"hooks": [
|
|
30
|
+
{
|
|
31
|
+
"type": "command",
|
|
32
|
+
"command": "bun run {{PKG_ROOT}}/src/hooks/LoadContext.ts"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"UserPromptSubmit": [
|
|
38
|
+
{
|
|
39
|
+
"matcher": "",
|
|
40
|
+
"hooks": [
|
|
41
|
+
{
|
|
42
|
+
"type": "command",
|
|
43
|
+
"command": "bun run {{PKG_ROOT}}/src/hooks/UserPromptOrchestrator.ts"
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"PreToolUse": [
|
|
49
|
+
{
|
|
50
|
+
"matcher": "Bash|Write|Edit",
|
|
51
|
+
"hooks": [
|
|
52
|
+
{
|
|
53
|
+
"type": "command",
|
|
54
|
+
"command": "bun run {{PKG_ROOT}}/src/hooks/SecurityValidator.ts"
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"matcher": "Skill",
|
|
60
|
+
"hooks": [
|
|
61
|
+
{
|
|
62
|
+
"type": "command",
|
|
63
|
+
"command": "bun run {{PKG_ROOT}}/src/hooks/SkillGuard.ts"
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
"Stop": [
|
|
69
|
+
{
|
|
70
|
+
"matcher": "",
|
|
71
|
+
"hooks": [
|
|
72
|
+
{
|
|
73
|
+
"type": "command",
|
|
74
|
+
"command": "bun run {{PKG_ROOT}}/src/hooks/StopOrchestrator.ts"
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "portable-agent-layer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "PAL — Portable Agent Layer: persistent personal context for AI coding assistants",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -44,10 +44,6 @@
|
|
|
44
44
|
"prepare": "husky",
|
|
45
45
|
"install:all": "bun run src/cli/install.ts",
|
|
46
46
|
"uninstall": "bun run src/cli/uninstall.ts",
|
|
47
|
-
"ai:entity-save": "bun run src/tools/entity-save.ts",
|
|
48
|
-
"ai:fyzz-api": "bun run src/tools/fyzz-api.ts",
|
|
49
|
-
"ai:pdf-download": "bun run src/tools/pdf-download.ts",
|
|
50
|
-
"ai:youtube-analyze": "bun run src/tools/youtube-analyze.ts",
|
|
51
47
|
"tool:analyze": "bun run src/tools/analyze.ts",
|
|
52
48
|
"tool:opinion": "bun run src/tools/opinion.ts",
|
|
53
49
|
"tool:reflect": "bun run src/tools/relationship-reflect.ts",
|
|
@@ -17,8 +17,7 @@ import {
|
|
|
17
17
|
writeFileSync,
|
|
18
18
|
} from "node:fs";
|
|
19
19
|
import { dirname, relative, resolve } from "node:path";
|
|
20
|
-
import {
|
|
21
|
-
import { assets, ensureDir, palHome, paths, platform } from "./paths";
|
|
20
|
+
import { assets, ensureDir, paths, platform } from "./paths";
|
|
22
21
|
import { buildSetupPrompt, readSetupState } from "./setup";
|
|
23
22
|
|
|
24
23
|
const TEMPLATE_PATH = assets.agentsMdTemplate();
|
|
@@ -71,54 +70,30 @@ export function needsRebuild(): boolean {
|
|
|
71
70
|
|
|
72
71
|
const outputMtime = statSync(outputPath).mtimeMs;
|
|
73
72
|
|
|
74
|
-
// Collect source files: template + setup.json +
|
|
75
|
-
const sources: string[] = [
|
|
76
|
-
TEMPLATE_PATH,
|
|
77
|
-
resolve(dirname(TEMPLATE_PATH), "STEERING-RULES.md"),
|
|
78
|
-
resolve(paths.state(), "setup.json"),
|
|
79
|
-
];
|
|
73
|
+
// Collect source files: template + setup.json + PAL docs
|
|
74
|
+
const sources: string[] = [TEMPLATE_PATH, resolve(paths.state(), "setup.json")];
|
|
80
75
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
76
|
+
// Track PAL doc sources for rebuild detection
|
|
77
|
+
const palDocsDir = assets.palDocs();
|
|
78
|
+
if (existsSync(palDocsDir)) {
|
|
79
|
+
for (const f of readdirSync(palDocsDir).filter((f) => f.endsWith(".md"))) {
|
|
80
|
+
sources.push(resolve(palDocsDir, f));
|
|
85
81
|
}
|
|
86
82
|
}
|
|
87
83
|
|
|
88
84
|
return latestMtime(...sources) > outputMtime;
|
|
89
85
|
}
|
|
90
86
|
|
|
91
|
-
function memoryPaths(): string {
|
|
92
|
-
const mem = resolve(palHome(), "memory");
|
|
93
|
-
return [
|
|
94
|
-
`- **Wisdom frames**: \`${resolve(mem, "wisdom", "frames")}/\` — crystallized principles per domain (loaded every session)`,
|
|
95
|
-
`- **Relationship notes**: \`${resolve(mem, "relationship")}/YYYY-MM/YYYY-MM-DD.md\` — daily interaction observations (loaded every session)`,
|
|
96
|
-
`- **Session learnings**: \`${resolve(mem, "learning", "session")}/YYYY-MM/*.md\` — reusable insights from sessions (loaded every session)`,
|
|
97
|
-
`- **Failure captures**: \`${resolve(mem, "learning", "failures")}/YYYY-MM/{timestamp}_{slug}/capture.md\` — what went wrong and why`,
|
|
98
|
-
`- **Signals**: \`${resolve(mem, "signals")}/ratings.jsonl\` — append-only rating signal log (do not edit directly)`,
|
|
99
|
-
].join("\n");
|
|
100
|
-
}
|
|
101
|
-
|
|
102
87
|
/** Render AGENTS.md from the template using current state */
|
|
103
88
|
export function buildClaudeMd(): string {
|
|
104
89
|
const template = existsSync(TEMPLATE_PATH)
|
|
105
90
|
? readFileSync(TEMPLATE_PATH, "utf-8")
|
|
106
|
-
: "# PAL Context\n\n{{SETUP_PROMPT}}\n
|
|
91
|
+
: "# PAL Context\n\n{{SETUP_PROMPT}}\n";
|
|
107
92
|
|
|
108
93
|
const state = readSetupState();
|
|
109
94
|
const setupPrompt = state ? buildSetupPrompt(state) : null;
|
|
110
|
-
const telos = loadTelos();
|
|
111
|
-
|
|
112
|
-
const steeringPath = resolve(dirname(TEMPLATE_PATH), "STEERING-RULES.md");
|
|
113
|
-
const steeringRules = existsSync(steeringPath)
|
|
114
|
-
? readFileSync(steeringPath, "utf-8").trim()
|
|
115
|
-
: "";
|
|
116
95
|
|
|
117
|
-
return template
|
|
118
|
-
.replace("{{SETUP_PROMPT}}", setupPrompt ? `${setupPrompt}\n` : "")
|
|
119
|
-
.replace("{{TELOS}}", telos ? `${telos}\n` : "")
|
|
120
|
-
.replace("{{MEMORY_PATHS}}", memoryPaths())
|
|
121
|
-
.replace("{{STEERING_RULES}}", steeringRules);
|
|
96
|
+
return template.replace("{{SETUP_PROMPT}}", setupPrompt ? `${setupPrompt}\n` : "");
|
|
122
97
|
}
|
|
123
98
|
|
|
124
99
|
/** Regenerate AGENTS.md if any source file is newer, and ensure CLAUDE.md symlink exists. Returns true if rebuilt. */
|
package/src/hooks/lib/context.ts
CHANGED
|
@@ -21,33 +21,6 @@ import {
|
|
|
21
21
|
staleProjects,
|
|
22
22
|
} from "./work-tracking";
|
|
23
23
|
|
|
24
|
-
/** Load all populated TELOS files as a single markdown string */
|
|
25
|
-
export function loadTelos(): string {
|
|
26
|
-
const telosDir = paths.telos();
|
|
27
|
-
if (!existsSync(telosDir)) return "";
|
|
28
|
-
|
|
29
|
-
const files = readdirSync(telosDir)
|
|
30
|
-
.filter((f) => f.endsWith(".md"))
|
|
31
|
-
.sort();
|
|
32
|
-
|
|
33
|
-
const sections: string[] = [];
|
|
34
|
-
|
|
35
|
-
for (const file of files) {
|
|
36
|
-
const content = readFileSync(resolve(telosDir, file), "utf-8").trim();
|
|
37
|
-
// Skip empty templates (only have a heading and comment)
|
|
38
|
-
const realLines = content
|
|
39
|
-
.split("\n")
|
|
40
|
-
.filter(
|
|
41
|
-
(l) =>
|
|
42
|
-
!l.startsWith("#") && !l.startsWith("<!--") && !l.startsWith("-->") && l.trim()
|
|
43
|
-
);
|
|
44
|
-
if (realLines.length === 0) continue;
|
|
45
|
-
sections.push(content);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return sections.join("\n\n---\n\n");
|
|
49
|
-
}
|
|
50
|
-
|
|
51
24
|
/** Count lines in a signals JSONL file */
|
|
52
25
|
export function countSignals(filename: string): number {
|
|
53
26
|
const filepath = resolve(paths.signals(), filename);
|
package/src/hooks/lib/paths.ts
CHANGED
|
@@ -77,4 +77,6 @@ export const assets = {
|
|
|
77
77
|
hooks: () => pkg("src", "hooks"),
|
|
78
78
|
telosTemplates: () => pkg("assets", "templates", "telos"),
|
|
79
79
|
agentsMdTemplate: () => pkg("assets", "templates", "AGENTS.md.template"),
|
|
80
|
+
claudeSettingsTemplate: () => pkg("assets", "templates", "settings.claude.json"),
|
|
81
|
+
palDocs: () => pkg("assets", "templates", "PAL"),
|
|
80
82
|
} as const;
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PAL — Claude Code target installer (TypeScript)
|
|
3
|
-
* Merges
|
|
4
|
-
* Copies skills
|
|
3
|
+
* Merges settings template into existing settings.json (never overwrites).
|
|
4
|
+
* Copies skills, agents, and PAL docs. Generates CLAUDE.md from TELOS.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { copyFileSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { resolve } from "node:path";
|
|
9
9
|
import { regenerateIfNeeded } from "../../hooks/lib/claude-md";
|
|
10
|
-
import { palHome, palPkg, platform } from "../../hooks/lib/paths";
|
|
10
|
+
import { assets, palHome, palPkg, platform } from "../../hooks/lib/paths";
|
|
11
11
|
import {
|
|
12
12
|
copyAgents,
|
|
13
|
+
copyPalDocs,
|
|
13
14
|
copySkills,
|
|
14
15
|
countAgents,
|
|
15
16
|
countMd,
|
|
16
17
|
countSkills,
|
|
18
|
+
loadSettingsTemplate,
|
|
17
19
|
log,
|
|
20
|
+
mergeSettings,
|
|
18
21
|
readJson,
|
|
19
22
|
writeJson,
|
|
20
23
|
} from "../lib";
|
|
@@ -35,96 +38,13 @@ const backup = `${SETTINGS}.bak.${Date.now()}`;
|
|
|
35
38
|
copyFileSync(SETTINGS, backup);
|
|
36
39
|
log.info("Backed up settings.json");
|
|
37
40
|
|
|
38
|
-
// ---
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
{
|
|
43
|
-
matcher: "",
|
|
44
|
-
hooks: [
|
|
45
|
-
{ type: "command", command: `bun run ${PKG_ROOT}/src/hooks/LoadContext.ts` },
|
|
46
|
-
],
|
|
47
|
-
},
|
|
48
|
-
],
|
|
49
|
-
UserPromptSubmit: [
|
|
50
|
-
{
|
|
51
|
-
matcher: "",
|
|
52
|
-
hooks: [
|
|
53
|
-
{
|
|
54
|
-
type: "command",
|
|
55
|
-
command: `bun run ${PKG_ROOT}/src/hooks/UserPromptOrchestrator.ts`,
|
|
56
|
-
},
|
|
57
|
-
],
|
|
58
|
-
},
|
|
59
|
-
],
|
|
60
|
-
PreToolUse: [
|
|
61
|
-
{
|
|
62
|
-
matcher: "Bash|Write|Edit",
|
|
63
|
-
hooks: [
|
|
64
|
-
{
|
|
65
|
-
type: "command",
|
|
66
|
-
command: `bun run ${PKG_ROOT}/src/hooks/SecurityValidator.ts`,
|
|
67
|
-
},
|
|
68
|
-
],
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
matcher: "Skill",
|
|
72
|
-
hooks: [
|
|
73
|
-
{ type: "command", command: `bun run ${PKG_ROOT}/src/hooks/SkillGuard.ts` },
|
|
74
|
-
],
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
Stop: [
|
|
78
|
-
{
|
|
79
|
-
matcher: "",
|
|
80
|
-
hooks: [
|
|
81
|
-
{
|
|
82
|
-
type: "command",
|
|
83
|
-
command: `bun run ${PKG_ROOT}/src/hooks/StopOrchestrator.ts`,
|
|
84
|
-
},
|
|
85
|
-
],
|
|
86
|
-
},
|
|
87
|
-
],
|
|
88
|
-
},
|
|
89
|
-
};
|
|
41
|
+
// --- Load template and merge into existing settings ---
|
|
42
|
+
const template = loadSettingsTemplate(assets.claudeSettingsTemplate(), PKG_ROOT);
|
|
43
|
+
const existing = readJson<Record<string, unknown>>(SETTINGS, {});
|
|
44
|
+
const merged = mergeSettings(existing, template);
|
|
90
45
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
type Settings = { hooks?: Record<string, HookEntry[]>; env?: Record<string, string> };
|
|
94
|
-
|
|
95
|
-
const settings = readJson<Settings>(SETTINGS, {});
|
|
96
|
-
if (!settings.hooks) settings.hooks = {};
|
|
97
|
-
|
|
98
|
-
for (const [event, entries] of Object.entries(hooksPayload.hooks)) {
|
|
99
|
-
const existing = settings.hooks[event] ?? [];
|
|
100
|
-
for (const entry of entries) {
|
|
101
|
-
const cmd = entry.hooks[0]?.command;
|
|
102
|
-
const alreadyPresent = existing.some((e) => e.hooks?.[0]?.command === cmd);
|
|
103
|
-
if (!alreadyPresent) existing.push(entry);
|
|
104
|
-
}
|
|
105
|
-
settings.hooks[event] = existing;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// --- Add PAL tool permissions (auto-allow ai: scripts) ---
|
|
109
|
-
type SettingsWithPermissions = Settings & { permissions?: { allow?: string[] } };
|
|
110
|
-
const s = settings as SettingsWithPermissions;
|
|
111
|
-
if (!s.permissions) s.permissions = {};
|
|
112
|
-
if (!s.permissions.allow) s.permissions.allow = [];
|
|
113
|
-
const aiTools = [
|
|
114
|
-
"ai:entity-save",
|
|
115
|
-
"ai:fyzz-api",
|
|
116
|
-
"ai:pdf-download",
|
|
117
|
-
"ai:youtube-analyze",
|
|
118
|
-
];
|
|
119
|
-
for (const tool of aiTools) {
|
|
120
|
-
const perm = `Bash(bun run ${tool} *)`;
|
|
121
|
-
if (!s.permissions.allow.includes(perm)) {
|
|
122
|
-
s.permissions.allow.push(perm);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
writeJson(SETTINGS, settings);
|
|
127
|
-
log.success("Merged hooks into settings.json");
|
|
46
|
+
writeJson(SETTINGS, merged);
|
|
47
|
+
log.success("Merged PAL settings into settings.json");
|
|
128
48
|
|
|
129
49
|
// --- Copy skills ---
|
|
130
50
|
const skillsDir = resolve(CLAUDE_DIR, "skills");
|
|
@@ -133,13 +53,16 @@ copySkills(skillsDir);
|
|
|
133
53
|
// --- Copy agents ---
|
|
134
54
|
copyAgents();
|
|
135
55
|
|
|
56
|
+
// --- Copy PAL system docs ---
|
|
57
|
+
const palDocsCount = copyPalDocs();
|
|
58
|
+
log.success(`Installed ${palDocsCount} PAL docs to ~/.agents/PAL/`);
|
|
59
|
+
|
|
136
60
|
// --- Generate ~/.claude/AGENTS.md and symlink ~/.claude/CLAUDE.md → AGENTS.md ---
|
|
137
61
|
regenerateIfNeeded();
|
|
138
62
|
log.success("Generated ~/.config/opencode/AGENTS.md (→ ~/.claude/CLAUDE.md symlink)");
|
|
139
63
|
|
|
140
64
|
log.success("Claude Code installation complete");
|
|
141
65
|
console.log("");
|
|
142
|
-
log.info(`Hooks: 5 (SessionStart, UserPromptSubmit, PreToolUse×2, Stop)`);
|
|
143
66
|
log.info(`Skills: ${countSkills()}`);
|
|
144
67
|
log.info(`Agents: ${countAgents()}`);
|
|
145
68
|
log.info(`TELOS: ${countMd(resolve(palHome(), "telos"))} files`);
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PAL — Claude Code uninstaller (TypeScript)
|
|
3
|
-
* Removes
|
|
3
|
+
* Removes exactly what the settings template added, plus skills, agents, and PAL docs.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { copyFileSync, existsSync, unlinkSync } from "node:fs";
|
|
7
7
|
import { resolve } from "node:path";
|
|
8
|
-
import { palPkg, platform } from "../../hooks/lib/paths";
|
|
9
|
-
import {
|
|
8
|
+
import { assets, palPkg, platform } from "../../hooks/lib/paths";
|
|
9
|
+
import {
|
|
10
|
+
loadSettingsTemplate,
|
|
11
|
+
log,
|
|
12
|
+
readJson,
|
|
13
|
+
removeAgents,
|
|
14
|
+
removePalDocs,
|
|
15
|
+
removeSkills,
|
|
16
|
+
unmergeSettings,
|
|
17
|
+
writeJson,
|
|
18
|
+
} from "../lib";
|
|
10
19
|
|
|
11
|
-
const PKG_ROOT = palPkg();
|
|
20
|
+
const PKG_ROOT = palPkg().replaceAll("\\", "/");
|
|
12
21
|
const CLAUDE_DIR = platform.claudeDir();
|
|
13
22
|
const SETTINGS = resolve(CLAUDE_DIR, "settings.json");
|
|
14
23
|
|
|
@@ -21,50 +30,13 @@ if (!existsSync(SETTINGS)) {
|
|
|
21
30
|
copyFileSync(SETTINGS, `${SETTINGS}.bak.${Date.now()}`);
|
|
22
31
|
log.info("Backed up settings.json");
|
|
23
32
|
|
|
24
|
-
// ---
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
command?: string;
|
|
29
|
-
};
|
|
30
|
-
type Settings = { hooks?: Record<string, HookEntry[]>; env?: Record<string, string> };
|
|
33
|
+
// --- Load template and unmerge from existing settings ---
|
|
34
|
+
const template = loadSettingsTemplate(assets.claudeSettingsTemplate(), PKG_ROOT);
|
|
35
|
+
const existing = readJson<Record<string, unknown>>(SETTINGS, {});
|
|
36
|
+
const cleaned = unmergeSettings(existing, template);
|
|
31
37
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (settings.hooks) {
|
|
35
|
-
for (const [event, entries] of Object.entries(settings.hooks)) {
|
|
36
|
-
settings.hooks[event] = entries.filter((entry) => {
|
|
37
|
-
// New format: { matcher, hooks: [{ command }] }
|
|
38
|
-
if (entry.hooks) return !entry.hooks.some((h) => h.command?.includes(PKG_ROOT));
|
|
39
|
-
// Old flat format: { type, command }
|
|
40
|
-
if (entry.command) return !entry.command.includes(PKG_ROOT);
|
|
41
|
-
return true;
|
|
42
|
-
});
|
|
43
|
-
if (settings.hooks[event].length === 0) delete settings.hooks[event];
|
|
44
|
-
}
|
|
45
|
-
if (Object.keys(settings.hooks).length === 0) delete settings.hooks;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// --- Remove env ---
|
|
49
|
-
if (settings.env) {
|
|
50
|
-
// Clean up env vars
|
|
51
|
-
delete settings.env.PAL_DIR;
|
|
52
|
-
if (Object.keys(settings.env).length === 0) delete settings.env;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// --- Remove PAL tool permissions ---
|
|
56
|
-
type SettingsWithPermissions = Settings & { permissions?: { allow?: string[] } };
|
|
57
|
-
const s = settings as SettingsWithPermissions;
|
|
58
|
-
if (s.permissions?.allow) {
|
|
59
|
-
s.permissions.allow = s.permissions.allow.filter(
|
|
60
|
-
(p) => !p.includes(PKG_ROOT) && !p.startsWith("Bash(bun run ai:")
|
|
61
|
-
);
|
|
62
|
-
if (s.permissions.allow.length === 0) delete s.permissions.allow;
|
|
63
|
-
if (Object.keys(s.permissions).length === 0) delete s.permissions;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
writeJson(SETTINGS, settings);
|
|
67
|
-
log.success("Removed PAL hooks and env from settings.json");
|
|
38
|
+
writeJson(SETTINGS, cleaned);
|
|
39
|
+
log.success("Removed PAL settings from settings.json");
|
|
68
40
|
|
|
69
41
|
// --- Remove PAL skills ---
|
|
70
42
|
const removed = removeSkills(resolve(CLAUDE_DIR, "skills"));
|
|
@@ -82,6 +54,9 @@ if (removedAgents.length > 0) {
|
|
|
82
54
|
log.info("No PAL agents found");
|
|
83
55
|
}
|
|
84
56
|
|
|
57
|
+
// --- Remove PAL system docs ---
|
|
58
|
+
removePalDocs();
|
|
59
|
+
|
|
85
60
|
// --- Remove AGENTS.md and CLAUDE.md symlink ---
|
|
86
61
|
const agentsMd = resolve(platform.opencodeDir(), "AGENTS.md");
|
|
87
62
|
const claudeMd = resolve(CLAUDE_DIR, "CLAUDE.md");
|