portable-agent-layer 0.11.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.
- package/assets/skills/telos/SKILL.md +42 -8
- package/assets/skills/telos/tools/update-telos.ts +0 -1
- package/assets/templates/AGENTS.md.template +56 -2
- package/assets/templates/PAL/ALGORITHM.md +120 -0
- package/assets/templates/PAL/CONTEXT_ROUTING.md +16 -0
- package/assets/templates/PAL/STEERING_RULES.md +20 -0
- package/assets/templates/pal-settings.json +32 -0
- package/assets/templates/settings.claude.json +14 -14
- package/package.json +4 -3
- package/src/cli/index.ts +7 -0
- package/src/cli/setup-identity.ts +119 -0
- package/src/hooks/lib/claude-md.ts +55 -4
- package/src/hooks/lib/context.ts +59 -8
- package/src/hooks/lib/security.ts +1 -0
- package/src/hooks/lib/setup.ts +4 -16
- package/src/targets/claude/install.ts +4 -0
- package/src/targets/lib.ts +17 -0
- package/assets/templates/telos/IDENTITY.md +0 -4
- package/src/cli/install.ts +0 -86
- package/src/cli/uninstall.ts +0 -45
|
@@ -16,7 +16,6 @@ All files live in `~/.agents/PAL/telos/`:
|
|
|
16
16
|
| `PROJECTS.md` | Active projects, status, priority |
|
|
17
17
|
| `BELIEFS.md` | Core principles and values |
|
|
18
18
|
| `CHALLENGES.md` | Current obstacles |
|
|
19
|
-
| `IDENTITY.md` | AI identity and personality |
|
|
20
19
|
| `MISSION.md` | Purpose and direction |
|
|
21
20
|
| `STRATEGIES.md` | Approaches and plans |
|
|
22
21
|
| `IDEAS.md` | Ideas to explore |
|
|
@@ -26,7 +25,7 @@ All files live in `~/.agents/PAL/telos/`:
|
|
|
26
25
|
|
|
27
26
|
## Reading
|
|
28
27
|
|
|
29
|
-
Read the file directly from `~/.agents/PAL/telos/` when the user asks about any area.
|
|
28
|
+
Read the file directly from `~/.agents/PAL/telos/` when the user asks about any area. Summarize what's relevant — don't dump the entire file unless asked.
|
|
30
29
|
|
|
31
30
|
## Updating
|
|
32
31
|
|
|
@@ -36,25 +35,60 @@ Use the update tool for all changes. It validates the file, creates a backup, ap
|
|
|
36
35
|
bun ~/.agents/skills/telos/tools/update-telos.ts <FILE> "<content>" "<description>"
|
|
37
36
|
```
|
|
38
37
|
|
|
39
|
-
**Example:**
|
|
40
|
-
```bash
|
|
41
|
-
bun ~/.agents/skills/telos/tools/update-telos.ts PROJECTS.md "| New Project | In progress | High | Description |" "Added New Project"
|
|
42
|
-
```
|
|
43
|
-
|
|
44
38
|
## Routing
|
|
45
39
|
|
|
46
40
|
| Intent | Action |
|
|
47
41
|
|--------|--------|
|
|
48
|
-
| "what am I working on", "my projects", "priorities" | Read `PROJECTS.md`, summarize |
|
|
42
|
+
| "what am I working on", "my projects", "priorities" | Read `PROJECTS.md`, summarize active work |
|
|
49
43
|
| "my goals", "what are my goals" | Read `GOALS.md`, present current state |
|
|
50
44
|
| "update goals/projects/beliefs/challenges" | Read the target file, discuss changes with user, then run update tool |
|
|
51
45
|
| "add a project", "new project" | Read `PROJECTS.md`, confirm with user, run update tool |
|
|
46
|
+
| "complete/remove a project" | Read `PROJECTS.md`, confirm with user, update status via tool |
|
|
52
47
|
| "what do I believe", "my principles" | Read `BELIEFS.md` |
|
|
53
48
|
| "current obstacles", "challenges" | Read `CHALLENGES.md` |
|
|
49
|
+
| "I learned something", "lesson" | Discuss, then append to `LEARNED.md` via tool |
|
|
50
|
+
| "I have an idea" | Discuss, then append to `IDEAS.md` via tool |
|
|
54
51
|
| General "update telos", "telos" | Ask which area to review/update |
|
|
55
52
|
|
|
53
|
+
## Examples
|
|
54
|
+
|
|
55
|
+
**Example 1: Checking projects**
|
|
56
|
+
```
|
|
57
|
+
User: "what am I working on?"
|
|
58
|
+
→ Read PROJECTS.md
|
|
59
|
+
→ Summarize active work by priority — don't list every column
|
|
60
|
+
→ Highlight status changes, blockers, what needs attention
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Example 2: Adding a project**
|
|
64
|
+
```
|
|
65
|
+
User: "add my new side project"
|
|
66
|
+
→ Ask: "What's the project name, status, and priority?"
|
|
67
|
+
→ User provides details
|
|
68
|
+
→ Show the row you'll add, confirm
|
|
69
|
+
→ Run: bun ~/.agents/skills/telos/tools/update-telos.ts PROJECTS.md "| Side Project | In progress | Medium | Description |" "Added Side Project"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Example 3: Updating goals**
|
|
73
|
+
```
|
|
74
|
+
User: "I finished the migration, update my goals"
|
|
75
|
+
→ Read GOALS.md to see current state
|
|
76
|
+
→ Discuss what changed — what's done, what's next
|
|
77
|
+
→ Run tool to append updated goals
|
|
78
|
+
→ Remind: CLAUDE.md regenerates next session
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Anti-patterns
|
|
82
|
+
|
|
83
|
+
- **Don't dump raw file contents.** Summarize what's relevant to the user's question. They can ask for the full file if needed.
|
|
84
|
+
- **Don't update without confirming.** Always show what you'll change and get a "yes" before running the tool.
|
|
85
|
+
- **Don't create new TELOS files.** Only the 10 listed files are valid. If something doesn't fit, suggest the closest match.
|
|
86
|
+
- **Don't mix TELOS with identity.** AI/principal identity lives in `pal-settings.json`, not TELOS. TELOS is personal context — goals, beliefs, projects.
|
|
87
|
+
- **Don't reference stale data.** If TELOS was loaded earlier in the session via context routing, re-read the file before updating — it may have changed.
|
|
88
|
+
|
|
56
89
|
## Rules
|
|
57
90
|
|
|
58
91
|
- **Always read the file first** before making changes — match the existing format exactly
|
|
59
92
|
- **Confirm changes** with the user before running the update tool
|
|
60
93
|
- **Always use the tool** for writes — never edit TELOS files directly
|
|
94
|
+
- CLAUDE.md auto-regenerates from these files on next session start
|
|
@@ -1,8 +1,62 @@
|
|
|
1
1
|
# PAL — Portable Agent Layer
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Identity
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
You are {{IDENTITY_NAME}} ({{IDENTITY_DISPLAY}}). Your user is {{PRINCIPAL_NAME}}. Greet with: {{IDENTITY_CATCHPHRASE}}
|
|
6
|
+
|
|
7
|
+
## Modes
|
|
8
|
+
|
|
9
|
+
Every response uses exactly one mode. Classify the request BEFORE responding:
|
|
10
|
+
|
|
11
|
+
- **Greetings, ratings, acknowledgments** → MINIMAL
|
|
12
|
+
- **Single-step, quick tasks (under 2 minutes of work)** → NATIVE
|
|
13
|
+
- **Everything else** → ALGORITHM
|
|
14
|
+
|
|
15
|
+
Your first output MUST be the mode header. No freeform output. No skipping this step.
|
|
16
|
+
|
|
17
|
+
### MINIMAL
|
|
18
|
+
|
|
19
|
+
For greetings and brief acknowledgments only.
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
══════════════ PAL ══════════════
|
|
23
|
+
🗣️ {{IDENTITY_NAME}}: [response]
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### NATIVE
|
|
27
|
+
|
|
28
|
+
FOR: Simple tasks that won't take much effort or time. More advanced tasks use ALGORITHM below.
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
══════════════ PAL | NATIVE ══════════════
|
|
32
|
+
🗒️ TASK: [brief description]
|
|
33
|
+
[work]
|
|
34
|
+
🔄 ITERATION on: [16 words of context if this is a follow-up]
|
|
35
|
+
📃 CONTENT: [Up to 128 lines of the content, if there is any]
|
|
36
|
+
🔧 CHANGE: [what changed, if applicable]
|
|
37
|
+
✅ VERIFY: [how we verified, if applicable]
|
|
38
|
+
🗣️ {{IDENTITY_NAME}}: [brief summary]
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
On follow-ups, include the ITERATION line. On first response to a new request, omit it.
|
|
42
|
+
|
|
43
|
+
### ALGORITHM
|
|
44
|
+
|
|
45
|
+
FOR: Multi-step, complex, or difficult work. Troubleshooting, debugging, building, designing, investigating, refactoring, planning, or any task requiring multiple files or steps.
|
|
46
|
+
|
|
47
|
+
**MANDATORY FIRST ACTION:** Read `~/.agents/PAL/ALGORITHM.md` and follow its instructions exactly.
|
|
48
|
+
|
|
49
|
+
Start your response with the following header in this mode:
|
|
50
|
+
══════════════ PAL | ALGORITHM ══════════════
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
### Critical Rules (Zero Exceptions)
|
|
55
|
+
|
|
56
|
+
- **Mandatory output format** — Every response MUST use exactly one of the output formats above (ALGORITHM, NATIVE, or MINIMAL). No freeform output.
|
|
57
|
+
- **Response format before questions** — Always complete the current response format output FIRST, then invoke AskUserQuestion at the end.
|
|
58
|
+
|
|
59
|
+
---
|
|
6
60
|
|
|
7
61
|
{{SETUP_PROMPT}}
|
|
8
62
|
## Context Routing
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# The Algorithm
|
|
2
|
+
|
|
3
|
+
Core: transition from CURRENT STATE to IDEAL STATE using verifiable criteria. Every criterion is atomic, binary testable, and checked off with evidence.
|
|
4
|
+
|
|
5
|
+
## The Four Phases
|
|
6
|
+
|
|
7
|
+
All work happens inside these phases. No work outside the phase structure until the Algorithm completes.
|
|
8
|
+
|
|
9
|
+
### ━━━ 👁️ OBSERVE ━━━ 1/4
|
|
10
|
+
|
|
11
|
+
Thinking-only. No tool calls except context recovery (Grep/Glob/Read).
|
|
12
|
+
|
|
13
|
+
**1. Reverse engineer the request:**
|
|
14
|
+
|
|
15
|
+
🔎 REVERSE ENGINEERING:
|
|
16
|
+
- What did they explicitly say they wanted?
|
|
17
|
+
- What is implied that they wanted but didn't say?
|
|
18
|
+
- What did they explicitly say they don't want?
|
|
19
|
+
- What is obvious they don't want that they didn't say?
|
|
20
|
+
- What are common gotchas for this type of work?
|
|
21
|
+
|
|
22
|
+
**2. Define verifiable criteria:**
|
|
23
|
+
|
|
24
|
+
Write atomic criteria — each one is a single testable end-state. Apply the splitting test:
|
|
25
|
+
- **"And"/"With" test**: Joins two verifiable things? → Split them.
|
|
26
|
+
- **Independent failure test**: Part A can pass while Part B fails? → Separate criteria.
|
|
27
|
+
- **Scope word test**: "All", "every", "complete" → Enumerate what "all" means.
|
|
28
|
+
|
|
29
|
+
Format:
|
|
30
|
+
```
|
|
31
|
+
- [ ] C-1: [8-12 word atomic criterion]
|
|
32
|
+
- [ ] C-2: [8-12 word atomic criterion]
|
|
33
|
+
- [ ] C-A1: [anti-criterion — what must NOT happen]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Include at least one anti-criterion (C-A prefix).
|
|
37
|
+
|
|
38
|
+
**3. Select capabilities:**
|
|
39
|
+
|
|
40
|
+
Scan the available skills listing. Select skills and tools you'll invoke during EXECUTE. Selecting a capability = commitment to invoke it via tool call. Don't select what you won't use.
|
|
41
|
+
|
|
42
|
+
Output:
|
|
43
|
+
```
|
|
44
|
+
🏹 CAPABILITIES: [list each selected skill/tool and why]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### ━━━ 🧠 PLAN ━━━ 2/4
|
|
48
|
+
|
|
49
|
+
**Pressure test the criteria:**
|
|
50
|
+
|
|
51
|
+
🧠 RISKS: What are the riskiest assumptions?
|
|
52
|
+
🧠 PREMORTEM: How could this approach fail?
|
|
53
|
+
🧠 PREREQUISITES: What must be true before we start?
|
|
54
|
+
|
|
55
|
+
Refine criteria if the pressure test reveals gaps. Add criteria for uncovered failure modes.
|
|
56
|
+
|
|
57
|
+
**Plan the execution:**
|
|
58
|
+
- Validate prerequisites (env vars, dependencies, files, state)
|
|
59
|
+
- Decide execution order — what's serial, what can parallelize
|
|
60
|
+
- If Advanced+ complexity, use EnterPlanMode for user alignment
|
|
61
|
+
|
|
62
|
+
### ━━━ ⚡ EXECUTE ━━━ 3/4
|
|
63
|
+
|
|
64
|
+
Do the work. Invoke selected capabilities via tool calls.
|
|
65
|
+
|
|
66
|
+
- Check off criteria as they're satisfied: `- [x] C-1: ...`
|
|
67
|
+
- If a criterion can't be met, flag it immediately — don't defer to VERIFY
|
|
68
|
+
- Make decisions explicit — state why you chose approach A over B
|
|
69
|
+
|
|
70
|
+
### ━━━ ✅ VERIFY ━━━ 4/4
|
|
71
|
+
|
|
72
|
+
No rubber-stamping. Each criterion needs specific evidence.
|
|
73
|
+
|
|
74
|
+
For EACH criterion:
|
|
75
|
+
- Test that it's actually complete
|
|
76
|
+
- Cite the evidence (test output, file content, diff, tool result)
|
|
77
|
+
- Mark pass or fail
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
✅ VERIFICATION:
|
|
81
|
+
- [x] C-1: [criterion] — PASS: [evidence]
|
|
82
|
+
- [x] C-2: [criterion] — PASS: [evidence]
|
|
83
|
+
- [ ] C-3: [criterion] — FAIL: [what went wrong]
|
|
84
|
+
- [x] C-A1: [anti-criterion] — PASS: [confirmed not present]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Capability check:** Confirm every selected capability was actually invoked via tool call. Text output alone does not count.
|
|
88
|
+
|
|
89
|
+
If any criteria failed, fix and re-verify before completing.
|
|
90
|
+
|
|
91
|
+
## Output Format
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
♻️ ALGORITHM ═══════════════════════════
|
|
95
|
+
🗒️ TASK: [brief description]
|
|
96
|
+
|
|
97
|
+
━━━ 👁️ OBSERVE ━━━ 1/4
|
|
98
|
+
🔎 REVERSE ENGINEERING:
|
|
99
|
+
[reverse engineering output]
|
|
100
|
+
|
|
101
|
+
📋 CRITERIA:
|
|
102
|
+
[criteria checklist]
|
|
103
|
+
|
|
104
|
+
🏹 CAPABILITIES: [selected capabilities]
|
|
105
|
+
|
|
106
|
+
━━━ 🧠 PLAN ━━━ 2/4
|
|
107
|
+
🧠 RISKS: [risks]
|
|
108
|
+
🧠 PREMORTEM: [failure modes]
|
|
109
|
+
📐 APPROACH: [execution plan]
|
|
110
|
+
|
|
111
|
+
━━━ ⚡ EXECUTE ━━━ 3/4
|
|
112
|
+
[work happens here]
|
|
113
|
+
|
|
114
|
+
━━━ ✅ VERIFY ━━━ 4/4
|
|
115
|
+
✅ VERIFICATION:
|
|
116
|
+
[criterion-by-criterion evidence]
|
|
117
|
+
|
|
118
|
+
🔧 CHANGE: [what changed]
|
|
119
|
+
🗣️ {{IDENTITY_NAME}}: [summary]
|
|
120
|
+
```
|
|
@@ -10,3 +10,19 @@ Load context on-demand by reading the file at the path listed. Only load what th
|
|
|
10
10
|
| Work tracking (projects, sessions) | `~/.agents/PAL/WORK_TRACKING.md` |
|
|
11
11
|
| Opinion tracking | `~/.agents/PAL/OPINION_TRACKING.md` |
|
|
12
12
|
| Steering rules | `~/.agents/PAL/STEERING_RULES.md` |
|
|
13
|
+
| Algorithm (complex work phases) | `~/.agents/PAL/ALGORITHM.md` |
|
|
14
|
+
|
|
15
|
+
## User Context (TELOS)
|
|
16
|
+
|
|
17
|
+
| Topic | Path |
|
|
18
|
+
|-------|------|
|
|
19
|
+
| Projects & priorities | `~/.agents/PAL/telos/PROJECTS.md` |
|
|
20
|
+
| Goals (short/medium/long-term) | `~/.agents/PAL/telos/GOALS.md` |
|
|
21
|
+
| Beliefs & principles | `~/.agents/PAL/telos/BELIEFS.md` |
|
|
22
|
+
| Current challenges | `~/.agents/PAL/telos/CHALLENGES.md` |
|
|
23
|
+
| Mission & direction | `~/.agents/PAL/telos/MISSION.md` |
|
|
24
|
+
| Strategies & approaches | `~/.agents/PAL/telos/STRATEGIES.md` |
|
|
25
|
+
| Ideas to explore | `~/.agents/PAL/telos/IDEAS.md` |
|
|
26
|
+
| Key lessons learned | `~/.agents/PAL/telos/LEARNED.md` |
|
|
27
|
+
| Mental models | `~/.agents/PAL/telos/MODELS.md` |
|
|
28
|
+
| Narrative context | `~/.agents/PAL/telos/NARRATIVES.md` |
|
|
@@ -3,21 +3,41 @@
|
|
|
3
3
|
Behavioral directives — act on these, don't just know them.
|
|
4
4
|
|
|
5
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
|
+
Bad: Hook throws error → remove the entire hook. Build fails → delete and rewrite the config.
|
|
7
|
+
Correct: Hook throws error → read it, trace the error, fix the specific line.
|
|
6
8
|
|
|
7
9
|
**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.
|
|
10
|
+
Bad: "The file is correct" without reading it. "Tests pass" without running them. "The deploy succeeded" without checking.
|
|
11
|
+
Correct: Read the file → confirm contents. Run tests → report actual output. Check deploy status → report what you see.
|
|
8
12
|
|
|
9
13
|
**First principles over bolt-ons.** Most problems are symptoms. Understand → Simplify → Reduce → Add (last resort). Don't accrue technical debt through band-aid solutions.
|
|
14
|
+
Bad: Page slow → add caching layer. Actual issue: bad SQL query.
|
|
15
|
+
Correct: Profile → find the slow query → fix it. No new components.
|
|
10
16
|
|
|
11
17
|
**Read before modifying.** Understand existing code, imports, and patterns before suggesting changes.
|
|
18
|
+
Bad: Add rate limiting without reading existing middleware → break session management.
|
|
19
|
+
Correct: Read the handler, imports, and patterns first → integrate with what's already there.
|
|
12
20
|
|
|
13
21
|
**One change when debugging.** Isolate, verify, proceed. Don't change multiple things at once.
|
|
22
|
+
Bad: Page broken → change CSS, API, config, and routes at once. Still broken, now you don't know which change helped or hurt.
|
|
23
|
+
Correct: Dev tools → 404 on API → fix the route → verify → move to next issue.
|
|
14
24
|
|
|
15
25
|
**Minimal scope.** Only change what was asked. No bonus refactoring, no extra cleanup, no unsolicited improvements.
|
|
26
|
+
Bad: Fix bug on line 42, also refactor the whole file → 200-line diff for a one-line fix.
|
|
27
|
+
Correct: Fix the bug → 1-line diff.
|
|
16
28
|
|
|
17
29
|
**Ask before destructive actions.** Deletes, force pushes, production deploys — always ask first.
|
|
30
|
+
Bad: "Clean up cruft" → delete 15 files including backups without asking.
|
|
31
|
+
Correct: List candidates → explain consequences → ask approval first.
|
|
18
32
|
|
|
19
33
|
**Plan means stop.** "Create a plan" = present and STOP. No execution without approval.
|
|
34
|
+
Bad: User says "plan the migration" → you plan it AND start executing it.
|
|
35
|
+
Correct: Present the plan → wait for explicit "go ahead" before writing any code.
|
|
20
36
|
|
|
21
37
|
**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?"
|
|
38
|
+
Bad: User says "you broke it" → "What did I do wrong?" or "Can you clarify?"
|
|
39
|
+
Correct: Review your recent actions → find the mistake → fix it → explain what happened.
|
|
22
40
|
|
|
23
41
|
**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.
|
|
42
|
+
Bad: Memory says user dislikes verbose summaries → you write a 3-paragraph recap after every change.
|
|
43
|
+
Correct: Memory says user dislikes verbose summaries → you keep the summary to one line.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"identity": {
|
|
3
|
+
"ai": {
|
|
4
|
+
"name": "",
|
|
5
|
+
"fullName": "",
|
|
6
|
+
"displayName": "",
|
|
7
|
+
"catchphrase": ""
|
|
8
|
+
},
|
|
9
|
+
"principal": {
|
|
10
|
+
"name": "",
|
|
11
|
+
"timezone": ""
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"loadAtStartup": {
|
|
15
|
+
"_docs": "Files force-loaded into session context at startup. Injected as <system-reminder> blocks.",
|
|
16
|
+
"files": [
|
|
17
|
+
"~/.agents/PAL/STEERING_RULES.md",
|
|
18
|
+
"~/.agents/PAL/telos/PROJECTS.md"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"dynamicContext": {
|
|
22
|
+
"_docs": "Dynamic context sections injected at session start. Set to false to disable.",
|
|
23
|
+
"wisdom": true,
|
|
24
|
+
"opinions": true,
|
|
25
|
+
"relationship": true,
|
|
26
|
+
"learningDigest": true,
|
|
27
|
+
"synthesis": true,
|
|
28
|
+
"signalTrends": true,
|
|
29
|
+
"failurePatterns": true,
|
|
30
|
+
"activeWork": true
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"permissions": {
|
|
3
3
|
"allow": [
|
|
4
|
-
"Read",
|
|
5
|
-
"Grep",
|
|
6
|
-
"Glob",
|
|
4
|
+
"Read(//*)",
|
|
5
|
+
"Grep(//*)",
|
|
6
|
+
"Glob(//*)",
|
|
7
7
|
"WebFetch",
|
|
8
8
|
"WebSearch",
|
|
9
|
-
"Bash(cat
|
|
10
|
-
"Bash(head
|
|
11
|
-
"Bash(tail
|
|
12
|
-
"Bash(ls
|
|
13
|
-
"Bash(find
|
|
9
|
+
"Bash(cat //*)",
|
|
10
|
+
"Bash(head //*)",
|
|
11
|
+
"Bash(tail //*)",
|
|
12
|
+
"Bash(ls //*)",
|
|
13
|
+
"Bash(find //*)",
|
|
14
14
|
"Bash(grep *)",
|
|
15
|
-
"Bash(rg
|
|
15
|
+
"Bash(rg //*)",
|
|
16
16
|
"Bash(wc *)",
|
|
17
|
-
"Bash(diff
|
|
18
|
-
"Bash(which
|
|
19
|
-
"Bash(file
|
|
20
|
-
"Bash(stat
|
|
21
|
-
"Bash(readlink
|
|
17
|
+
"Bash(diff //*)",
|
|
18
|
+
"Bash(which //*)",
|
|
19
|
+
"Bash(file //*)",
|
|
20
|
+
"Bash(stat //*)",
|
|
21
|
+
"Bash(readlink //*)",
|
|
22
22
|
"Bash(bun ~/.agents/skills/*/tools/*.ts *)"
|
|
23
23
|
]
|
|
24
24
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "portable-agent-layer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "PAL — Portable Agent Layer: persistent personal context for AI coding assistants",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"check-write": "biome check --write",
|
|
43
43
|
"lint-staged": "lint-staged",
|
|
44
44
|
"prepare": "husky",
|
|
45
|
-
"install:all": "bun run src/cli/
|
|
46
|
-
"uninstall": "bun run src/cli/
|
|
45
|
+
"install:all": "bun run src/cli/index.ts cli install",
|
|
46
|
+
"uninstall": "bun run src/cli/index.ts cli uninstall",
|
|
47
47
|
"tool:analyze": "bun run src/tools/analyze.ts",
|
|
48
48
|
"tool:opinion": "bun run src/tools/opinion.ts",
|
|
49
49
|
"tool:reflect": "bun run src/tools/relationship-reflect.ts",
|
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
"typescript": "^5.9.0"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
|
+
"@clack/prompts": "^1.1.0",
|
|
76
77
|
"adm-zip": "^0.5.16"
|
|
77
78
|
}
|
|
78
79
|
}
|
package/src/cli/index.ts
CHANGED
|
@@ -425,6 +425,13 @@ async function init(args: string[]) {
|
|
|
425
425
|
}
|
|
426
426
|
|
|
427
427
|
async function install(targets: { claude: boolean; opencode: boolean }) {
|
|
428
|
+
// Scaffold TELOS + PAL settings, then prompt for missing identity
|
|
429
|
+
const { scaffoldTelos, scaffoldPalSettings } = await import("../targets/lib");
|
|
430
|
+
const { promptIdentity } = await import("./setup-identity");
|
|
431
|
+
scaffoldTelos();
|
|
432
|
+
scaffoldPalSettings();
|
|
433
|
+
await promptIdentity();
|
|
434
|
+
|
|
428
435
|
if (targets.claude) {
|
|
429
436
|
console.log("━━━ Claude Code ━━━");
|
|
430
437
|
await import("../targets/claude/install");
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive identity setup — prompts for missing fields in pal-settings.json.
|
|
3
|
+
* Called during `pal install`. Skips fields that already have values.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { resolve } from "node:path";
|
|
8
|
+
import * as clack from "@clack/prompts";
|
|
9
|
+
import { palHome } from "../hooks/lib/paths";
|
|
10
|
+
|
|
11
|
+
interface PalSettings {
|
|
12
|
+
identity?: {
|
|
13
|
+
ai?: { name?: string; fullName?: string; displayName?: string; catchphrase?: string };
|
|
14
|
+
principal?: { name?: string; timezone?: string };
|
|
15
|
+
};
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function settingsPath(): string {
|
|
20
|
+
return resolve(palHome(), "memory", "pal-settings.json");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function readSettings(): PalSettings {
|
|
24
|
+
const p = settingsPath();
|
|
25
|
+
if (!existsSync(p)) return {};
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(readFileSync(p, "utf-8"));
|
|
28
|
+
} catch {
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function writeSettings(settings: PalSettings): void {
|
|
34
|
+
writeFileSync(settingsPath(), `${JSON.stringify(settings, null, 2)}\n`, "utf-8");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Prompt for missing identity fields. Skips any field that already has a value. */
|
|
38
|
+
export async function promptIdentity(): Promise<void> {
|
|
39
|
+
// Skip interactive prompts in non-TTY environments (tests, CI)
|
|
40
|
+
if (!process.stdin.isTTY) return;
|
|
41
|
+
|
|
42
|
+
const settings = readSettings();
|
|
43
|
+
if (!settings.identity) settings.identity = {};
|
|
44
|
+
if (!settings.identity.ai) settings.identity.ai = {};
|
|
45
|
+
if (!settings.identity.principal) settings.identity.principal = {};
|
|
46
|
+
|
|
47
|
+
const ai = settings.identity.ai;
|
|
48
|
+
const principal = settings.identity.principal;
|
|
49
|
+
|
|
50
|
+
// Check if anything is missing
|
|
51
|
+
const needsPrincipal = !principal.name;
|
|
52
|
+
const needsAi = !ai.name;
|
|
53
|
+
const needsCatchphrase = !ai.catchphrase;
|
|
54
|
+
const needsTimezone = !principal.timezone;
|
|
55
|
+
|
|
56
|
+
if (!needsPrincipal && !needsAi && !needsCatchphrase && !needsTimezone) {
|
|
57
|
+
clack.log.info("Identity already configured");
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
clack.intro("Identity Setup");
|
|
62
|
+
|
|
63
|
+
if (needsPrincipal) {
|
|
64
|
+
const name = await clack.text({
|
|
65
|
+
message: "What's your name?",
|
|
66
|
+
placeholder: "e.g. John",
|
|
67
|
+
validate: (v) => (!v || v.length === 0 ? "Name is required" : undefined),
|
|
68
|
+
});
|
|
69
|
+
if (clack.isCancel(name)) {
|
|
70
|
+
clack.cancel("Setup cancelled");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
principal.name = name;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (needsAi) {
|
|
77
|
+
const name = await clack.text({
|
|
78
|
+
message: "Name your AI",
|
|
79
|
+
defaultValue: "Assistant",
|
|
80
|
+
placeholder: "e.g. Jarvis, Friday, Atlas",
|
|
81
|
+
});
|
|
82
|
+
if (clack.isCancel(name)) {
|
|
83
|
+
clack.cancel("Setup cancelled");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
ai.name = name;
|
|
87
|
+
ai.fullName = `${name} — Personal AI`;
|
|
88
|
+
ai.displayName = name.toUpperCase();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (needsCatchphrase) {
|
|
92
|
+
const catchphrase = await clack.text({
|
|
93
|
+
message: "Startup catchphrase ({name} gets replaced with AI name)",
|
|
94
|
+
defaultValue: "{name} here, ready when you are.",
|
|
95
|
+
placeholder: "{name} online. What's the mission?",
|
|
96
|
+
});
|
|
97
|
+
if (clack.isCancel(catchphrase)) {
|
|
98
|
+
clack.cancel("Setup cancelled");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
ai.catchphrase = catchphrase;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (needsTimezone) {
|
|
105
|
+
const guess = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
106
|
+
const tz = await clack.text({
|
|
107
|
+
message: "Your timezone",
|
|
108
|
+
defaultValue: guess,
|
|
109
|
+
});
|
|
110
|
+
if (clack.isCancel(tz)) {
|
|
111
|
+
clack.cancel("Setup cancelled");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
principal.timezone = tz;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
writeSettings(settings);
|
|
118
|
+
clack.outro("Identity saved ✓");
|
|
119
|
+
}
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
writeFileSync,
|
|
18
18
|
} from "node:fs";
|
|
19
19
|
import { dirname, relative, resolve } from "node:path";
|
|
20
|
-
import { assets, ensureDir, paths, platform } from "./paths";
|
|
20
|
+
import { assets, ensureDir, palHome, paths, platform } from "./paths";
|
|
21
21
|
import { buildSetupPrompt, readSetupState } from "./setup";
|
|
22
22
|
|
|
23
23
|
const TEMPLATE_PATH = assets.agentsMdTemplate();
|
|
@@ -70,8 +70,12 @@ export function needsRebuild(): boolean {
|
|
|
70
70
|
|
|
71
71
|
const outputMtime = statSync(outputPath).mtimeMs;
|
|
72
72
|
|
|
73
|
-
// Collect source files: template + setup.json + PAL docs
|
|
74
|
-
const sources: string[] = [
|
|
73
|
+
// Collect source files: template + setup.json + identity + PAL docs
|
|
74
|
+
const sources: string[] = [
|
|
75
|
+
TEMPLATE_PATH,
|
|
76
|
+
resolve(paths.state(), "setup.json"),
|
|
77
|
+
palSettingsPath(),
|
|
78
|
+
];
|
|
75
79
|
|
|
76
80
|
// Track PAL doc sources for rebuild detection
|
|
77
81
|
const palDocsDir = assets.palDocs();
|
|
@@ -84,6 +88,47 @@ export function needsRebuild(): boolean {
|
|
|
84
88
|
return latestMtime(...sources) > outputMtime;
|
|
85
89
|
}
|
|
86
90
|
|
|
91
|
+
interface Identity {
|
|
92
|
+
ai: { name: string; displayName: string; catchphrase: string };
|
|
93
|
+
principal: { name: string };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const IDENTITY_DEFAULTS: Identity = {
|
|
97
|
+
ai: { name: "Assistant", displayName: "ASSISTANT", catchphrase: "" },
|
|
98
|
+
principal: { name: "" },
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
function palSettingsPath(): string {
|
|
102
|
+
return resolve(palHome(), "memory", "pal-settings.json");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Load identity from pal-settings.json */
|
|
106
|
+
export function loadIdentity(): Identity {
|
|
107
|
+
const p = palSettingsPath();
|
|
108
|
+
if (!existsSync(p)) return IDENTITY_DEFAULTS;
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const data = JSON.parse(readFileSync(p, "utf-8"));
|
|
112
|
+
const ai = data.identity?.ai ?? {};
|
|
113
|
+
const principal = data.identity?.principal ?? {};
|
|
114
|
+
const name = ai.name || IDENTITY_DEFAULTS.ai.name;
|
|
115
|
+
const catchphrase = (ai.catchphrase || "").replace("{name}", name);
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
ai: {
|
|
119
|
+
name,
|
|
120
|
+
displayName: ai.displayName || IDENTITY_DEFAULTS.ai.displayName,
|
|
121
|
+
catchphrase,
|
|
122
|
+
},
|
|
123
|
+
principal: {
|
|
124
|
+
name: principal.name || IDENTITY_DEFAULTS.principal.name,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
} catch {
|
|
128
|
+
return IDENTITY_DEFAULTS;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
87
132
|
/** Render AGENTS.md from the template using current state */
|
|
88
133
|
export function buildClaudeMd(): string {
|
|
89
134
|
const template = existsSync(TEMPLATE_PATH)
|
|
@@ -92,8 +137,14 @@ export function buildClaudeMd(): string {
|
|
|
92
137
|
|
|
93
138
|
const state = readSetupState();
|
|
94
139
|
const setupPrompt = state ? buildSetupPrompt(state) : null;
|
|
140
|
+
const identity = loadIdentity();
|
|
95
141
|
|
|
96
|
-
return template
|
|
142
|
+
return template
|
|
143
|
+
.replace("{{SETUP_PROMPT}}", setupPrompt ? `${setupPrompt}\n` : "")
|
|
144
|
+
.replaceAll("{{IDENTITY_NAME}}", identity.ai.name)
|
|
145
|
+
.replaceAll("{{IDENTITY_DISPLAY}}", identity.ai.displayName)
|
|
146
|
+
.replaceAll("{{IDENTITY_CATCHPHRASE}}", identity.ai.catchphrase)
|
|
147
|
+
.replaceAll("{{PRINCIPAL_NAME}}", identity.principal.name);
|
|
97
148
|
}
|
|
98
149
|
|
|
99
150
|
/** 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
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
7
|
+
import { homedir } from "node:os";
|
|
7
8
|
import { resolve } from "node:path";
|
|
8
9
|
import { parse } from "./frontmatter";
|
|
9
10
|
import { readFailures, readLearnings } from "./learning-store";
|
|
@@ -21,6 +22,49 @@ import {
|
|
|
21
22
|
staleProjects,
|
|
22
23
|
} from "./work-tracking";
|
|
23
24
|
|
|
25
|
+
interface PalSettings {
|
|
26
|
+
loadAtStartup?: { files?: string[] };
|
|
27
|
+
dynamicContext?: Record<string, boolean>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Load pal-settings.json from memory/ */
|
|
31
|
+
function loadPalSettings(): PalSettings {
|
|
32
|
+
const p = resolve(paths.memory(), "pal-settings.json");
|
|
33
|
+
if (!existsSync(p)) return {};
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(readFileSync(p, "utf-8"));
|
|
36
|
+
} catch {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Check if a dynamic context section is enabled (defaults to true) */
|
|
42
|
+
function isEnabled(settings: PalSettings, key: string): boolean {
|
|
43
|
+
return settings.dynamicContext?.[key] !== false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Load and concatenate loadAtStartup files */
|
|
47
|
+
function loadStartupFiles(settings: PalSettings): string {
|
|
48
|
+
const files = settings.loadAtStartup?.files;
|
|
49
|
+
if (!files || files.length === 0) return "";
|
|
50
|
+
|
|
51
|
+
const home = homedir();
|
|
52
|
+
const sections: string[] = [];
|
|
53
|
+
|
|
54
|
+
for (const file of files) {
|
|
55
|
+
const resolved = file.replace("~", home);
|
|
56
|
+
if (!existsSync(resolved)) continue;
|
|
57
|
+
try {
|
|
58
|
+
const content = readFileSync(resolved, "utf-8").trim();
|
|
59
|
+
if (content) sections.push(content);
|
|
60
|
+
} catch {
|
|
61
|
+
/* skip unreadable files */
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return sections.join("\n\n---\n\n");
|
|
66
|
+
}
|
|
67
|
+
|
|
24
68
|
/** Count lines in a signals JSONL file */
|
|
25
69
|
export function countSignals(filename: string): number {
|
|
26
70
|
const filepath = resolve(paths.signals(), filename);
|
|
@@ -321,15 +365,22 @@ export function loadRelationshipContext(): string {
|
|
|
321
365
|
* things that change per-session and can't live in a static file.
|
|
322
366
|
*/
|
|
323
367
|
export function buildSystemReminder(): string {
|
|
324
|
-
const
|
|
325
|
-
const
|
|
326
|
-
const
|
|
327
|
-
const
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
const
|
|
368
|
+
const settings = loadPalSettings();
|
|
369
|
+
const startup = loadStartupFiles(settings);
|
|
370
|
+
const work = isEnabled(settings, "activeWork") ? loadActiveWork() : null;
|
|
371
|
+
const wisdom = isEnabled(settings, "wisdom") ? loadWisdomContext() : "";
|
|
372
|
+
const relationship = isEnabled(settings, "relationship")
|
|
373
|
+
? loadRelationshipContext()
|
|
374
|
+
: "";
|
|
375
|
+
const digest = isEnabled(settings, "learningDigest") ? loadLearningDigest() : "";
|
|
376
|
+
const trends = isEnabled(settings, "signalTrends") ? loadSignalTrends() : "";
|
|
377
|
+
const failures = isEnabled(settings, "failurePatterns") ? loadFailurePatterns() : "";
|
|
378
|
+
const synthesis = isEnabled(settings, "synthesis")
|
|
379
|
+
? loadSynthesisRecommendations()
|
|
380
|
+
: "";
|
|
381
|
+
const opinions = isEnabled(settings, "opinions") ? loadOpinionContext() : "";
|
|
332
382
|
const parts: string[] = [];
|
|
383
|
+
if (startup) parts.push(startup);
|
|
333
384
|
if (wisdom) parts.push(wisdom);
|
|
334
385
|
if (opinions) parts.push(opinions);
|
|
335
386
|
if (relationship) parts.push(relationship);
|
package/src/hooks/lib/setup.ts
CHANGED
|
@@ -26,20 +26,8 @@ export interface SetupState {
|
|
|
26
26
|
const SETUP_STEPS: Record<string, Omit<SetupStep, "done">> = {
|
|
27
27
|
mission: {
|
|
28
28
|
file: "telos/MISSION.md",
|
|
29
|
-
question: "What's your
|
|
30
|
-
hint: "Write their
|
|
31
|
-
},
|
|
32
|
-
ai_name: {
|
|
33
|
-
file: "telos/IDENTITY.md",
|
|
34
|
-
question:
|
|
35
|
-
"What would you like to call your AI? (Pick a name — this is how I'll identify myself.)",
|
|
36
|
-
hint: "Write the chosen AI name and identity to telos/IDENTITY.md with fields: name, fullName (name — Personal AI), displayName (UPPERCASED)",
|
|
37
|
-
},
|
|
38
|
-
catchphrase: {
|
|
39
|
-
file: "telos/IDENTITY.md",
|
|
40
|
-
question:
|
|
41
|
-
'What should your AI\'s startup catchphrase be? (e.g. "{name} here, ready to go" — {name} gets replaced with the AI name.)',
|
|
42
|
-
hint: "Append the catchphrase to telos/IDENTITY.md under a ## Catchphrase heading. Support {name} as a placeholder.",
|
|
29
|
+
question: "What do you do? What's your role and core purpose?",
|
|
30
|
+
hint: "Write their role and core purpose to telos/MISSION.md",
|
|
43
31
|
},
|
|
44
32
|
goals: {
|
|
45
33
|
file: "telos/GOALS.md",
|
|
@@ -152,8 +140,8 @@ export function buildSetupPrompt(state: SetupState): string | null {
|
|
|
152
140
|
const lines: string[] = [
|
|
153
141
|
"## IMPORTANT: PAL First-Run Setup Required",
|
|
154
142
|
"",
|
|
155
|
-
"TELOS files are empty —
|
|
156
|
-
"You MUST start the setup process immediately
|
|
143
|
+
"TELOS files are empty — the user's identity is already configured (via the installer),",
|
|
144
|
+
"but personal context is still needed. You MUST start the setup process immediately.",
|
|
157
145
|
"Greet them, explain that PAL needs to learn about them to personalize future sessions,",
|
|
158
146
|
"and ask the first remaining question below. Do NOT wait for the user to ask about setup.",
|
|
159
147
|
"",
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
log,
|
|
20
20
|
mergeSettings,
|
|
21
21
|
readJson,
|
|
22
|
+
scaffoldPalSettings,
|
|
22
23
|
writeJson,
|
|
23
24
|
} from "../lib";
|
|
24
25
|
|
|
@@ -57,6 +58,9 @@ copyAgents();
|
|
|
57
58
|
const palDocsCount = copyPalDocs();
|
|
58
59
|
log.success(`Installed ${palDocsCount} PAL docs to ~/.agents/PAL/`);
|
|
59
60
|
|
|
61
|
+
// --- Scaffold PAL settings ---
|
|
62
|
+
scaffoldPalSettings();
|
|
63
|
+
|
|
60
64
|
// --- Generate ~/.claude/AGENTS.md and symlink ~/.claude/CLAUDE.md → AGENTS.md ---
|
|
61
65
|
regenerateIfNeeded();
|
|
62
66
|
log.success("Generated ~/.config/opencode/AGENTS.md (→ ~/.claude/CLAUDE.md symlink)");
|
package/src/targets/lib.ts
CHANGED
|
@@ -155,6 +155,23 @@ export function scaffoldTelos(): void {
|
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
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
|
+
|
|
158
175
|
// --- PAL docs (modular context routing files) ---
|
|
159
176
|
|
|
160
177
|
const PAL_DOCS_DIR = resolve(platform.agentsDir(), "PAL");
|
package/src/cli/install.ts
DELETED
|
@@ -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]");
|
package/src/cli/uninstall.ts
DELETED
|
@@ -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()}.`);
|