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 +13 -3
- package/assets/templates/PAL/ALGORITHM.md +30 -9
- package/assets/templates/PAL/README.md +6 -5
- package/assets/templates/PAL/SYSTEM_ARCHITECTURE.md +7 -0
- package/assets/templates/settings.claude.json +2 -1
- package/package.json +3 -2
- package/src/hooks/lib/claude-md.ts +15 -9
- package/src/hooks/lib/paths.ts +2 -0
- package/src/targets/lib.ts +2 -1
- package/src/tools/agent/analyze.ts +157 -0
- package/src/tools/agent/wisdom-frame.ts +235 -0
- package/src/tools/export.ts +23 -17
- package/src/tools/import.ts +65 -77
- package/src/tools/relationship-reflect.ts +80 -85
- package/src/tools/session-summary.ts +44 -41
- package/src/tools/token-cost.ts +134 -92
- package/src/tools/analyze.ts +0 -152
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 **
|
|
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)
|
|
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**:
|
|
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
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
123
|
+
━━━ 🧠 PLAN ━━━ 2/5
|
|
107
124
|
🧠 RISKS: [risks]
|
|
108
125
|
🧠 PREMORTEM: [failure modes]
|
|
109
126
|
📐 APPROACH: [execution plan]
|
|
110
127
|
|
|
111
|
-
━━━ ⚡ EXECUTE ━━━ 3/
|
|
128
|
+
━━━ ⚡ EXECUTE ━━━ 3/5
|
|
112
129
|
[work happens here]
|
|
113
130
|
|
|
114
|
-
━━━ ✅ VERIFY ━━━ 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
|
|
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
|
|
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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "portable-agent-layer",
|
|
3
|
-
"version": "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
|
-
/**
|
|
52
|
-
function
|
|
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(
|
|
56
|
-
|
|
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
|
-
|
|
63
|
-
|
|
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
|
-
|
|
159
|
+
ensureSymlinks();
|
|
154
160
|
if (!needsRebuild()) return false;
|
|
155
161
|
ensureDir(dirname(outputPath));
|
|
156
162
|
writeFileSync(outputPath, buildClaudeMd(), "utf-8");
|
package/src/hooks/lib/paths.ts
CHANGED
|
@@ -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;
|
package/src/targets/lib.ts
CHANGED
|
@@ -273,10 +273,11 @@ export function copyPalDocs(): number {
|
|
|
273
273
|
count++;
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
-
// Symlink ~/.agents/PAL/telos
|
|
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();
|