portable-agent-layer 0.16.0 → 0.18.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A cross-platform, cross-agent layer for portable AI workflows, memory, and accumulated knowledge.
4
4
 
5
- PAL lets you carry your agent context across **Windows**, **macOS**, and **Linux**, and work across different agent runtimes and interfaces such as **Claude** and **OpenCode**. Its core idea is simple: your knowledge and workflows should belong to **you**, not to a single machine, tool, or vendor.
5
+ PAL lets you carry your agent context across **Windows**, **macOS**, and **Linux**, and work across different agent runtimes and interfaces such as **Claude Code**, **opencode**, **Cursor**, and **Codex**. Its core idea is simple: your knowledge and workflows should belong to **you**, not to a single machine, tool, or vendor.
6
6
 
7
7
  > Inspired in part by [Daniel Miessler](https://danielmiessler.com)'s work on [Personal AI Infrastructure](https://github.com/danielmiessler/Personal_AI_Infrastructure). PAL is an independent open-source implementation focused on portability across platforms and agents. It is not affiliated with or endorsed by Daniel Miessler.
8
8
 
@@ -33,7 +33,7 @@ With PAL, you can:
33
33
  > **Bun is required.** PAL is built on [Bun](https://bun.sh) and will not work with Node.js or other runtimes. Install it with `curl -fsSL https://bun.sh/install | bash`.
34
34
 
35
35
  - [Bun](https://bun.sh) >= 1.3.0
36
- - At least one of: [Claude Code](https://claude.ai/code) or [opencode](https://opencode.ai)
36
+ - At least one of: [Claude Code](https://claude.ai/code), [opencode](https://opencode.ai), [Cursor](https://cursor.com), or [Codex](https://openai.com/index/introducing-codex/)
37
37
 
38
38
  ### Package mode (recommended)
39
39
 
@@ -94,6 +94,15 @@ pal cli install --cursor # Cursor only
94
94
  pal cli install # all available (default)
95
95
  ```
96
96
 
97
+ ### Supported agents
98
+
99
+ | Agent | Support | Skills | Hooks | AGENTS.md | Subagents |
100
+ |-------|---------|--------|-------|-----------|-----------|
101
+ | Claude Code | Full | Yes | Yes | Yes | Yes |
102
+ | opencode | Full | Yes | Yes (plugin) | Yes | Yes |
103
+ | Cursor | Full | Yes | Yes | Yes (injected via hook) | Yes |
104
+ | Codex | Partial | Yes | No | Yes | No |
105
+
97
106
  ---
98
107
 
99
108
  ## Environment variables
@@ -114,6 +123,7 @@ pal cli install # all available (default)
114
123
  | `PAL_CLAUDE_DIR` | Override Claude config dir (default: `~/.claude`) |
115
124
  | `PAL_OPENCODE_DIR` | Override opencode config dir (default: `~/.config/opencode`) |
116
125
  | `PAL_CURSOR_DIR` | Override Cursor config dir (default: `~/.cursor`) |
126
+ | `PAL_CODEX_DIR` | Override Codex config dir (default: `~/.codex`) |
117
127
  | `PAL_AGENTS_DIR` | Override agents dir (default: `~/.agents`) |
118
128
 
119
129
  ---
@@ -162,7 +172,7 @@ Your setup should be able to travel with you.
162
172
  ## Features
163
173
 
164
174
  - **Cross-platform**: works on Windows, macOS, and Linux
165
- - **Cross-agent**: designed to work across multiple agent ecosystems
175
+ - **Cross-agent**: full support for Claude Code, opencode, and Cursor; partial support for Codex
166
176
  - **Portable knowledge**: export and import accumulated knowledge
167
177
  - **TypeScript-first**: built in TypeScript from day one
168
178
  - **Open source**: hackable, inspectable, extensible
@@ -2,11 +2,11 @@
2
2
 
3
3
  Core: transition from CURRENT STATE to IDEAL STATE using verifiable criteria. Every criterion is atomic, binary testable, and checked off with evidence.
4
4
 
5
- ## The Four Phases
5
+ ## The Five Phases
6
6
 
7
7
  All work happens inside these phases. No work outside the phase structure until the Algorithm completes.
8
8
 
9
- ### ━━━ 👁️ OBSERVE ━━━ 1/4
9
+ ### ━━━ 👁️ OBSERVE ━━━ 1/5
10
10
 
11
11
  Thinking-only. No tool calls except context recovery (Grep/Glob/Read).
12
12
 
@@ -44,7 +44,7 @@ Output:
44
44
  🏹 CAPABILITIES: [list each selected skill/tool and why]
45
45
  ```
46
46
 
47
- ### ━━━ 🧠 PLAN ━━━ 2/4
47
+ ### ━━━ 🧠 PLAN ━━━ 2/5
48
48
 
49
49
  **Pressure test the criteria:**
50
50
 
@@ -59,7 +59,7 @@ Refine criteria if the pressure test reveals gaps. Add criteria for uncovered fa
59
59
  - Decide execution order — what's serial, what can parallelize
60
60
  - If Advanced+ complexity, use EnterPlanMode for user alignment
61
61
 
62
- ### ━━━ ⚡ EXECUTE ━━━ 3/4
62
+ ### ━━━ ⚡ EXECUTE ━━━ 3/5
63
63
 
64
64
  Do the work. Invoke selected capabilities via tool calls.
65
65
 
@@ -67,7 +67,7 @@ Do the work. Invoke selected capabilities via tool calls.
67
67
  - If a criterion can't be met, flag it immediately — don't defer to VERIFY
68
68
  - Make decisions explicit — state why you chose approach A over B
69
69
 
70
- ### ━━━ ✅ VERIFY ━━━ 4/4
70
+ ### ━━━ ✅ VERIFY ━━━ 4/5
71
71
 
72
72
  No rubber-stamping. Each criterion needs specific evidence.
73
73
 
@@ -88,13 +88,30 @@ For EACH criterion:
88
88
 
89
89
  If any criteria failed, fix and re-verify before completing.
90
90
 
91
+ ### ━━━ 📚 LEARN ━━━ 5/5
92
+
93
+ Reflect on the work and capture reusable knowledge. Skip this phase when the work was trivial or purely mechanical.
94
+
95
+ **1. Reflection** (one sentence each):
96
+ - What would I do differently next time?
97
+ - What would a better algorithm have done differently?
98
+
99
+ **2. Wisdom Frame** — if the session produced a genuine, reusable insight:
100
+
101
+ ```bash
102
+ bun ~/.agents/PAL/tools/wisdom-frame.ts --domain <domain> --observation "insight" [--type principle|contextual-rule|anti-pattern|evolution]
103
+ ```
104
+
105
+ Domains: `development`, `workflow`, `communication`, `infrastructure`, `integration`, or any fitting domain.
106
+ Only write if the insight is **genuine and reusable** — not every session produces one. When in doubt, skip.
107
+
91
108
  ## Output Format
92
109
 
93
110
  ```
94
111
  ♻️ ALGORITHM ═══════════════════════════
95
112
  🗒️ TASK: [brief description]
96
113
 
97
- ━━━ 👁️ OBSERVE ━━━ 1/4
114
+ ━━━ 👁️ OBSERVE ━━━ 1/5
98
115
  🔎 REVERSE ENGINEERING:
99
116
  [reverse engineering output]
100
117
 
@@ -103,18 +120,22 @@ If any criteria failed, fix and re-verify before completing.
103
120
 
104
121
  🏹 CAPABILITIES: [selected capabilities]
105
122
 
106
- ━━━ 🧠 PLAN ━━━ 2/4
123
+ ━━━ 🧠 PLAN ━━━ 2/5
107
124
  🧠 RISKS: [risks]
108
125
  🧠 PREMORTEM: [failure modes]
109
126
  📐 APPROACH: [execution plan]
110
127
 
111
- ━━━ ⚡ EXECUTE ━━━ 3/4
128
+ ━━━ ⚡ EXECUTE ━━━ 3/5
112
129
  [work happens here]
113
130
 
114
- ━━━ ✅ VERIFY ━━━ 4/4
131
+ ━━━ ✅ VERIFY ━━━ 4/5
115
132
  ✅ VERIFICATION:
116
133
  [criterion-by-criterion evidence]
117
134
 
118
135
  🔧 CHANGE: [what changed]
119
136
  🗣️ {{IDENTITY_NAME}}: [summary]
137
+
138
+ ━━━ 📚 LEARN ━━━ 5/5
139
+ 🪞 REFLECT: [what I'd do differently]
140
+ 📝 WISDOM: [frame update if genuine insight, or "No new insight"]
120
141
  ```
@@ -1,12 +1,12 @@
1
1
  # PAL — Portable Agent Layer
2
2
 
3
- PAL is a persistent, cross-platform, cross-agent layer for portable AI workflows, memory, and accumulated knowledge. It runs inside any compatible AI coding agent (Claude Code, opencode) as an interconnected set of skills, hooks, tools, memory, and configuration — all orchestrated by The Algorithm.
3
+ PAL is a persistent, cross-platform, cross-agent layer for portable AI workflows, memory, and accumulated knowledge. It runs inside any compatible AI coding agent (Claude Code, opencode, Cursor, Codex) as an interconnected set of skills, hooks, tools, memory, and configuration — all orchestrated by The Algorithm.
4
4
 
5
5
  ## How It Works
6
6
 
7
7
  **CLAUDE.md** (or the agent equivalent) is the entry point — generated from a template by the CLI installer. It defines execution modes, The Algorithm routing, and the context routing table. The agent loads it natively every session. A SessionStart hook keeps it fresh automatically.
8
8
 
9
- **The PAL home directory (`~/.agents/PAL/`)** contains all system documentation, user context (TELOS), and routing files. The rest of the system lives in the PAL package (`src/`) and the agent's config directory (`~/.claude/` or `~/.config/opencode/`).
9
+ **The PAL home directory (`~/.agents/PAL/`)** contains all system documentation, user context (TELOS), and routing files. The rest of the system lives in the PAL package (`src/`) and the agent's config directory (`~/.claude/`, `~/.config/opencode/`, `~/.cursor/`, or `~/.codex/`).
10
10
 
11
11
  ## Directory Structure
12
12
 
@@ -29,7 +29,7 @@ PAL is a persistent, cross-platform, cross-agent layer for portable AI workflows
29
29
  hooks/ # Session lifecycle hooks
30
30
  handlers/ # Individual stop/prompt handlers
31
31
  lib/ # Shared utilities
32
- targets/ # Agent-specific installers (Claude, opencode)
32
+ targets/ # Agent-specific installers (Claude, opencode, Cursor)
33
33
  tools/ # Standalone CLI tools
34
34
  assets/
35
35
  skills/ # Bundled skills (16+)
@@ -104,6 +104,7 @@ pal # Start agent session with auto-summary on exit
104
104
  pal cli init # Scaffold PAL home + install hooks
105
105
  pal cli install [--claude] # Register hooks/skills for Claude Code
106
106
  pal cli install [--opencode] # Register hooks/skills for opencode
107
+ pal cli install [--cursor] # Register hooks/skills for Cursor
107
108
  pal cli uninstall # Remove hooks/skills
108
109
  pal cli status # Show configuration
109
110
  pal cli doctor # Check prerequisites and health
@@ -116,8 +117,8 @@ pal cli update # Update PAL
116
117
 
117
118
  PAL is designed to work identically across:
118
119
  - **Platforms:** macOS, Linux, Windows
119
- - **Agents:** Claude Code, opencode (and future tools)
120
- - **Environment overrides:** `PAL_HOME`, `PAL_PKG`, `PAL_CLAUDE_DIR`, `PAL_OPENCODE_DIR`, `PAL_AGENTS_DIR`
120
+ - **Agents:** Claude Code (full), opencode (full), Cursor (full), Codex (partial — AGENTS.md and skills only, no hooks or subagents)
121
+ - **Environment overrides:** `PAL_HOME`, `PAL_PKG`, `PAL_CLAUDE_DIR`, `PAL_OPENCODE_DIR`, `PAL_CURSOR_DIR`, `PAL_CODEX_DIR`, `PAL_AGENTS_DIR`
121
122
 
122
123
  ## Extending PAL
123
124
 
@@ -433,9 +433,14 @@ src/targets/
433
433
  │ ├── install.ts # Register hooks + skills in opencode config
434
434
  │ ├── uninstall.ts
435
435
  │ └── plugin.ts # opencode plugin interface
436
+ ├── cursor/ # Cursor specific
437
+ │ ├── install.ts # Register hooks + skills in ~/.cursor/
438
+ │ └── uninstall.ts
436
439
  └── lib.ts # Shared: JSON read/write, settings merge, TELOS scaffold
437
440
  ```
438
441
 
442
+ Codex support is partial — AGENTS.md is symlinked to `~/.codex/AGENTS.md` automatically (no dedicated target installer needed).
443
+
439
444
  ### Path Resolution
440
445
 
441
446
  All paths resolve through `src/hooks/lib/paths.ts`:
@@ -446,6 +451,8 @@ All paths resolve through `src/hooks/lib/paths.ts`:
446
451
  | PAL package | Auto-detected from source | `PAL_PKG` |
447
452
  | Claude config | `~/.claude` | `PAL_CLAUDE_DIR` |
448
453
  | opencode config | `~/.config/opencode` | `PAL_OPENCODE_DIR` |
454
+ | Cursor config | `~/.cursor` | `PAL_CURSOR_DIR` |
455
+ | Codex config | `~/.codex` | `PAL_CODEX_DIR` |
449
456
  | Agents dir | `~/.agents` | `PAL_AGENTS_DIR` |
450
457
 
451
458
  ### Portability Contract
@@ -19,7 +19,8 @@
19
19
  "Bash(file //*)",
20
20
  "Bash(stat //*)",
21
21
  "Bash(readlink //*)",
22
- "Bash(bun ~/.agents/skills/*/tools/*.ts *)"
22
+ "Bash(bun ~/.agents/skills/*/tools/*.ts *)",
23
+ "Bash(bun run tool:wisdom-frame *)"
23
24
  ]
24
25
  },
25
26
  "hooks": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "portable-agent-layer",
3
- "version": "0.16.0",
3
+ "version": "0.18.0",
4
4
  "description": "PAL — Portable Agent Layer: persistent personal context for AI coding assistants",
5
5
  "type": "module",
6
6
  "bin": {
@@ -44,7 +44,8 @@
44
44
  "prepare": "husky",
45
45
  "install:all": "bun run src/cli/index.ts cli install",
46
46
  "uninstall": "bun run src/cli/index.ts cli uninstall",
47
- "tool:analyze": "bun run src/tools/analyze.ts",
47
+ "tool:analyze": "bun run src/tools/agent/analyze.ts",
48
+ "tool:wisdom-frame": "bun run src/tools/agent/wisdom-frame.ts",
48
49
  "tool:reflect": "bun run src/tools/relationship-reflect.ts",
49
50
  "tool:export": "bun run src/tools/export.ts",
50
51
  "tool:import": "bun run src/tools/import.ts",
@@ -48,19 +48,25 @@ function latestMtime(...filePaths: string[]): number {
48
48
  return latest;
49
49
  }
50
50
 
51
- /** Ensure CLAUDE.md is a symlink pointing to AGENTS.md */
52
- function ensureSymlink(): void {
53
- const { outputPath, symlinkPath } = getOutputPaths();
51
+ /** Create or verify a symlink pointing to AGENTS.md */
52
+ function ensureOneSymlink(linkPath: string, targetPath: string): void {
54
53
  try {
55
- const stat = lstatSync(symlinkPath);
56
- // If it exists but isn't a symlink (e.g. old generated file), remove it
57
- if (!stat.isSymbolicLink()) unlinkSync(symlinkPath);
54
+ const stat = lstatSync(linkPath);
55
+ if (!stat.isSymbolicLink()) unlinkSync(linkPath);
58
56
  else return; // already a symlink, leave it
59
57
  } catch {
60
58
  // doesn't exist — create it
61
59
  }
62
- const relTarget = relative(dirname(symlinkPath), outputPath).replaceAll("\\", "/");
63
- symlinkSync(relTarget, symlinkPath);
60
+ ensureDir(dirname(linkPath));
61
+ const relTarget = relative(dirname(linkPath), targetPath).replaceAll("\\", "/");
62
+ symlinkSync(relTarget, linkPath);
63
+ }
64
+
65
+ /** Ensure all agent symlinks point to the canonical AGENTS.md */
66
+ function ensureSymlinks(): void {
67
+ const { outputPath, symlinkPath } = getOutputPaths();
68
+ ensureOneSymlink(symlinkPath, outputPath);
69
+ ensureOneSymlink(resolve(platform.codexDir(), "AGENTS.md"), outputPath);
64
70
  }
65
71
 
66
72
  /** Returns true if AGENTS.md needs to be regenerated */
@@ -150,7 +156,7 @@ export function buildClaudeMd(): string {
150
156
  /** Regenerate AGENTS.md if any source file is newer, and ensure CLAUDE.md symlink exists. Returns true if rebuilt. */
151
157
  export function regenerateIfNeeded(): boolean {
152
158
  const { outputPath } = getOutputPaths();
153
- ensureSymlink();
159
+ ensureSymlinks();
154
160
  if (!needsRebuild()) return false;
155
161
  ensureDir(dirname(outputPath));
156
162
  writeFileSync(outputPath, buildClaudeMd(), "utf-8");
@@ -68,6 +68,7 @@ export const platform = {
68
68
  claudeDir: () => process.env.PAL_CLAUDE_DIR || resolve(h, ".claude"),
69
69
  opencodeDir: () => process.env.PAL_OPENCODE_DIR || resolve(h, ".config", "opencode"),
70
70
  cursorDir: () => process.env.PAL_CURSOR_DIR || resolve(h, ".cursor"),
71
+ codexDir: () => process.env.PAL_CODEX_DIR || resolve(h, ".codex"),
71
72
  agentsDir: () => process.env.PAL_AGENTS_DIR || resolve(h, ".agents"),
72
73
  } as const;
73
74
 
@@ -80,5 +81,6 @@ export const assets = {
80
81
  agentsMdTemplate: () => pkg("assets", "templates", "AGENTS.md.template"),
81
82
  claudeSettingsTemplate: () => pkg("assets", "templates", "settings.claude.json"),
82
83
  cursorHooksTemplate: () => pkg("assets", "templates", "hooks.cursor.json"),
84
+ agentTools: () => pkg("src", "tools", "agent"),
83
85
  palDocs: () => pkg("assets", "templates", "PAL"),
84
86
  } as const;
@@ -273,10 +273,11 @@ export function copyPalDocs(): number {
273
273
  count++;
274
274
  }
275
275
 
276
- // Symlink ~/.agents/PAL/telos and ~/.agents/PAL/memory → <palHome>/...
276
+ // Symlink ~/.agents/PAL/{telos,memory,tools}source locations
277
277
  const linkType = process.platform === "win32" ? "junction" : "dir";
278
278
  ensureSymlink(resolve(PAL_DOCS_DIR, "telos"), resolve(palHome(), "telos"), linkType);
279
279
  ensureSymlink(resolve(PAL_DOCS_DIR, "memory"), resolve(palHome(), "memory"), linkType);
280
+ ensureSymlink(resolve(PAL_DOCS_DIR, "tools"), assets.agentTools(), linkType);
280
281
 
281
282
  return count;
282
283
  }
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Unified Learning Analysis — graduation patterns + ratings summary.
4
+ *
5
+ * Reads failures and session learnings, finds recurring patterns,
6
+ * summarizes ratings, and generates recommendations.
7
+ *
8
+ * Usage: bun run tool:analyze
9
+ */
10
+
11
+ import { parseArgs } from "node:util";
12
+ import { type AnalysisResult, analyze } from "../../hooks/lib/graduation";
13
+
14
+ // ── ANSI Colors ──
15
+
16
+ const c = {
17
+ bold: (s: string) => `\x1b[1m${s}\x1b[0m`,
18
+ dim: (s: string) => `\x1b[2m${s}\x1b[0m`,
19
+ cyan: (s: string) => `\x1b[36m${s}\x1b[0m`,
20
+ yellow: (s: string) => `\x1b[33m${s}\x1b[0m`,
21
+ green: (s: string) => `\x1b[32m${s}\x1b[0m`,
22
+ red: (s: string) => `\x1b[31m${s}\x1b[0m`,
23
+ magenta: (s: string) => `\x1b[35m${s}\x1b[0m`,
24
+ };
25
+
26
+ export function printReport(result: AnalysisResult): void {
27
+ const hasPatterns = result.candidates.length > 0 || result.emerging.length > 0;
28
+ const hasRatings = result.ratings !== null;
29
+
30
+ if (!hasPatterns && !hasRatings) {
31
+ console.log("\n No patterns or ratings data found.\n");
32
+ return;
33
+ }
34
+
35
+ if (result.ratings) {
36
+ const r = result.ratings;
37
+ const avgColor = r.average >= 7 ? c.green : r.average <= 4 ? c.red : c.yellow;
38
+ console.log(
39
+ `\n ${c.bold("Ratings:")} ${avgColor(`${r.average.toFixed(1)}/10`)} avg (${r.total} total)`
40
+ );
41
+ console.log(
42
+ ` ${c.red(`Low (≤4): ${r.low.count}`)} | ${c.green(`High (≥7): ${r.high.count}`)}`
43
+ );
44
+ }
45
+
46
+ if (result.candidates.length > 0) {
47
+ console.log(
48
+ `\n ${c.bold(c.green(`Graduation Report — ${result.candidates.length} pattern(s) detected`))}\n`
49
+ );
50
+ console.log(` ${c.dim("─────────────────────────────────────────────────")}\n`);
51
+
52
+ for (const candidate of result.candidates) {
53
+ console.log(
54
+ ` ${c.cyan(`[${candidate.domain}]`)} ${c.bold(`${candidate.entries.length}x`)} occurrences`
55
+ );
56
+ console.log("");
57
+
58
+ for (const entry of candidate.entries) {
59
+ const sourceType = entry.source.startsWith("failure:") ? "failure" : "learning";
60
+ const tag =
61
+ sourceType === "failure"
62
+ ? c.red(`[${sourceType}]`)
63
+ : c.yellow(`[${sourceType}]`);
64
+ console.log(
65
+ ` ${c.dim(entry.date || "unknown")} ${tag} ${entry.text.slice(0, 100)}`
66
+ );
67
+ }
68
+
69
+ console.log(`\n ${c.dim("Files:")}`);
70
+ for (const entry of candidate.entries) {
71
+ console.log(` ${c.dim(entry.path)}`);
72
+ }
73
+
74
+ console.log("");
75
+ console.log(
76
+ ` Target frame: ${c.magenta(`memory/wisdom/frames/${candidate.domain}.md`)}`
77
+ );
78
+ console.log(` ${c.dim("─────────────────────────────────────────────────")}\n`);
79
+ }
80
+ }
81
+
82
+ if (result.emerging.length > 0) {
83
+ console.log(` ${c.bold(c.yellow("Emerging (2x — one more to graduate)"))}\n`);
84
+ for (const group of result.emerging) {
85
+ console.log(
86
+ ` ${c.cyan(`[${group.domain}]`)} ${c.bold(`${group.entries.length}x`)}`
87
+ );
88
+ for (const entry of group.entries) {
89
+ const sourceType = entry.source.startsWith("failure:") ? "failure" : "learning";
90
+ const tag =
91
+ sourceType === "failure"
92
+ ? c.red(`[${sourceType}]`)
93
+ : c.yellow(`[${sourceType}]`);
94
+ console.log(
95
+ ` ${c.dim(entry.date || "unknown")} ${tag} ${entry.text.slice(0, 80)}`
96
+ );
97
+ }
98
+ console.log(" Files:");
99
+ for (const entry of group.entries) {
100
+ console.log(` ${c.dim(entry.path)}`);
101
+ }
102
+ console.log("");
103
+ }
104
+ }
105
+
106
+ if (result.recommendations.length > 0) {
107
+ console.log(` ${c.bold("Recommendations:")}\n`);
108
+ for (const rec of result.recommendations) {
109
+ console.log(` ${rec}`);
110
+ }
111
+ console.log("");
112
+ }
113
+
114
+ if (result.candidates.length > 0) {
115
+ console.log(` To crystallize: add a line to the wisdom frame file.`);
116
+ console.log(` Format: ${c.green("- Your principle here [CRYSTAL: 85%]")}\n`);
117
+ }
118
+ }
119
+
120
+ async function run() {
121
+ const { values } = parseArgs({
122
+ args: Bun.argv.slice(2),
123
+ options: {
124
+ help: { type: "boolean", short: "h" },
125
+ actionable: { type: "boolean", short: "a" },
126
+ },
127
+ });
128
+
129
+ if (values.help) {
130
+ console.log(`
131
+ PAL Learning Analysis — unified graduation + ratings report
132
+
133
+ Reads all captured failures (rating ≤3) and session learnings,
134
+ groups recurring patterns via Dice similarity on context text,
135
+ and summarizes rating trends.
136
+
137
+ Sections:
138
+ Ratings Overall average, low/high counts
139
+ Graduation Patterns with 3+ occurrences → ready to crystallize
140
+ Emerging Patterns with 2 occurrences → one more to graduate
141
+
142
+ Flags:
143
+ --actionable, -a Generate actionable recommendations via Haiku inference
144
+
145
+ To crystallize a graduated pattern, add it to the target wisdom frame:
146
+ - Your principle here [CRYSTAL: 85%]
147
+
148
+ Usage: bun run tool:analyze [--actionable] [--help]
149
+ `);
150
+ process.exit(0);
151
+ }
152
+
153
+ const result = await analyze({ actionable: values.actionable });
154
+ printReport(result);
155
+ }
156
+
157
+ if (import.meta.main) run();