@tekmidian/pai 0.8.0 → 0.8.1

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/ARCHITECTURE.md CHANGED
@@ -1,4 +1,4 @@
1
- # PAI Knowledge OS — Architecture
1
+ # PAI Knowledge OS — Architecture (v0.8.0)
2
2
 
3
3
  Technical reference for PAI's architecture, database schema, CLI commands, and development setup.
4
4
 
@@ -151,7 +151,7 @@ If both commands return healthy output, PAI is running. Open a new Claude Code s
151
151
 
152
152
  ## MCP Server
153
153
 
154
- PAI exposes 9 tools, 18 on-demand prompts (skills), and 11 reference resources to Claude Code via a daemon-backed MCP shim. The shim speaks stdio (what Claude Code expects) and proxies each request to the background daemon over NDJSON on a Unix socket.
154
+ PAI exposes 9 tools, 19 on-demand prompts (skills), and 11 reference resources to Claude Code via a daemon-backed MCP shim. The shim speaks stdio (what Claude Code expects) and proxies each request to the background daemon over NDJSON on a Unix socket.
155
155
 
156
156
  ```
157
157
  Claude Code (stdio)
@@ -207,6 +207,7 @@ The MCP server registers 18 prompts that Claude can invoke as on-demand skills.
207
207
  | `name` | Session and project naming conventions |
208
208
  | `observability` | Observation system usage and querying |
209
209
  | `plan` | Forward-looking planning from TODOs and recent activity |
210
+ | `reconstruct` | Retroactively create session notes from JSONL transcripts and git history |
210
211
  | `research` | Structured research methodology |
211
212
  | `review` | Retrospective review of work over a time period |
212
213
  | `route` | Session note routing across projects |
@@ -467,6 +468,8 @@ The PAI daemon is a persistent background service that handles indexing, embeddi
467
468
  - Proxies all MCP tool calls from the shim to PostgreSQL
468
469
  - Re-indexes all active projects on a configurable interval (default: every 5 minutes)
469
470
  - Generates text embeddings asynchronously using Snowflake Arctic Embed (768-dim)
471
+ - Processes the persistent work queue: session summaries, topic detection, note updates
472
+ - Spawns headless Claude CLI processes for AI-powered session summarization
470
473
 
471
474
  ### Configuration
472
475
 
@@ -497,6 +500,62 @@ The daemon runs under the label `com.pai.pai-daemon`. The plist is installed to
497
500
 
498
501
  ---
499
502
 
503
+ ## Work Queue and Session Summary Pipeline
504
+
505
+ The daemon owns a persistent work queue (`~/.config/pai/work-queue.json`) that decouples hook triggers from actual work. Hooks push lightweight work items to the queue and exit immediately. The daemon processes items sequentially from a background worker loop.
506
+
507
+ ### Work Item Types
508
+
509
+ | Type | Who enqueues | Who processes | Description |
510
+ |------|-------------|---------------|-------------|
511
+ | `session-summary` | PreCompact, Stop hooks | `session-summary-worker.ts` | AI-powered summarization of JSONL transcript + git history |
512
+ | `topic-detect` | PreCompact hook | `topic-detect-worker.ts` | BM25-based topic shift detection for note splitting |
513
+ | `session-end` | Stop hook | `work-queue-worker.ts` | General session cleanup coordination |
514
+ | `note-update` | Session summary worker | `work-queue-worker.ts` | Write or update a session note file |
515
+ | `todo-update` | Session summary worker | `work-queue-worker.ts` | Update TODO.md with session state |
516
+
517
+ Work items are retried with exponential backoff (default max 3 attempts). The queue is written atomically (write temp file, then rename) to prevent corruption on daemon restart.
518
+
519
+ ### Session Summary Pipeline
520
+
521
+ ```
522
+ Stop or PreCompact hook fires
523
+
524
+ ├── Hook reads minimal data (session_id, transcript_path, cwd)
525
+ ├── Hook pushes { type: "session-summary", payload: {...} } to work queue
526
+ └── Hook exits (sub-second)
527
+
528
+ ⬇ Daemon worker loop picks up the item
529
+
530
+ session-summary-worker.ts
531
+
532
+ ├── Reads JSONL transcript (500K limit for stop, 200K for compact)
533
+ ├── Reads recent git log (last 20 commits)
534
+ ├── Strips ANTHROPIC_API_KEY from environment
535
+ ├── Spawns headless Claude CLI (Opus for stop, Sonnet for compact)
536
+ ├── Prompt requests: TOPIC: line + Work Done / Key Decisions / Known Issues / Next Steps
537
+ ├── Compares TOPIC: against existing note title (Jaccard similarity < 30% → new note)
538
+ └── Writes or updates session note in project's Notes directory
539
+
540
+ ⬇ If topic-detect was also enqueued
541
+
542
+ topic-detect-worker.ts
543
+
544
+ ├── Extracts recent user messages from JSONL
545
+ ├── Runs BM25 topic shift detector against PAI memory DB
546
+ └── Records topic boundary marker (used by next session-summary run)
547
+ ```
548
+
549
+ ### Cooldown and Force Flags
550
+
551
+ A 30-minute cooldown prevents redundant summary updates during active sessions. The Stop hook sets a `force: true` flag to bypass the cooldown, ensuring the final session state is always written. The PreCompact hook respects the cooldown to avoid O(n) summarizations during rapid compaction cycles.
552
+
553
+ ### Claude Binary Discovery
554
+
555
+ The daemon runs under launchd with a minimal PATH. The `findClaudeBinary()` function checks `~/.local/bin/claude` first, then falls back to standard PATH resolution. This handles the common case where Claude CLI is installed via the npm global prefix, which launchd does not include in its environment PATH.
556
+
557
+ ---
558
+
500
559
  ## Obsidian Bridge
501
560
 
502
561
  PAI can expose your project memory as an Obsidian vault. The vault contains no actual files — only symlinks into each project's `Notes/` directory, so edits in Obsidian are immediately visible to PAI and vice versa.
@@ -583,21 +642,22 @@ Claude Code Event
583
642
  | Hook | Event | Purpose |
584
643
  |------|-------|---------|
585
644
  | `load-core-context.mjs` | SessionStart | Loads PAI skill system and core configuration |
586
- | `load-project-context.mjs` | SessionStart | Detects project, loads notes dir, TODO, session note |
645
+ | `load-project-context.mjs` | SessionStart | Detects project, loads notes dir, TODO, session note; auto-registers new projects from .git, package.json, pubspec.yaml, and other signals |
587
646
  | `initialize-session.mjs` | SessionStart | Creates numbered session note, registers in PAI registry |
588
647
  | `post-compact-inject.mjs` | SessionStart (compact) | Reads saved state and injects into post-compaction context |
589
648
  | `security-validator.mjs` | PreToolUse (Bash) | Validates shell commands against security rules |
590
649
  | `capture-all-events.mjs` | All events | Observability — logs every hook event to session timeline |
591
650
  | `observe.mjs` | PostToolUse | Classifies tool calls into typed observations (decision/bugfix/feature/refactor/discovery/change) |
592
651
  | `inject-observations.mjs` | SessionStart | Injects recent observation context (compact index + timeline) |
593
- | `context-compression-hook.mjs` | PreCompact | Extracts session state, saves checkpoint, writes temp file for relay |
652
+ | `context-compression-hook.mjs` | PreCompact | Extracts session state, saves checkpoint, pushes session-summary work item to daemon |
594
653
  | `capture-tool-output.mjs` | PostToolUse | Records tool inputs/outputs for observability dashboard |
595
654
  | `update-tab-on-action.mjs` | PostToolUse | Updates terminal tab title based on current activity |
596
655
  | `sync-todo-to-md.mjs` | PostToolUse (TodoWrite) | Syncs Claude's internal TODO list to `Notes/TODO.md` |
597
656
  | `cleanup-session-files.mjs` | UserPromptSubmit | Cleans up stale temp files between prompts |
598
657
  | `update-tab-titles.mjs` | UserPromptSubmit | Sets terminal tab title from session context |
599
- | `stop-hook.mjs` | Stop | Writes work items to session note, sends notification |
600
- | `capture-session-summary.mjs` | SessionEnd | Final session summary written to session note |
658
+ | `whisper-rules.mjs` | UserPromptSubmit | Injects critical operating rules on every prompt; rules survive compaction and /clear |
659
+ | `stop-hook.mjs` | Stop | Pushes session-summary work item to daemon queue, sends notification |
660
+ | `capture-session-summary.mjs` | SessionEnd | Pushes session-summary work item to daemon queue |
601
661
  | `subagent-stop-hook.mjs` | SubagentStop | Captures sub-agent completion for observability |
602
662
 
603
663
  ### Context Preservation Relay
@@ -942,8 +1002,14 @@ src/
942
1002
  │ ├── daemon/ # Daemon server internals
943
1003
  │ │ ├── dispatcher.ts # Tool dispatch (zettel, observation, memory)
944
1004
  │ │ ├── handler.ts # NDJSON request handler
945
- │ │ └── server.ts # Socket server
946
- │ ├── indexer/ # Background index scheduler
1005
+ │ │ ├── scheduler.ts # Background index scheduler
1006
+ ├── server.ts # Socket server
1007
+ │ │ ├── state.ts # Shared daemon state
1008
+ │ │ └── types.ts # Shared type definitions
1009
+ │ ├── session-summary-worker.ts # AI-powered session summarization (Opus/Sonnet)
1010
+ │ ├── topic-detect-worker.ts # BM25-based topic shift detection
1011
+ │ ├── work-queue-worker.ts # Generic work item processor
1012
+ │ ├── work-queue.ts # Persistent file-backed work queue
947
1013
  │ ├── config.ts # Runtime configuration
948
1014
  │ └── index.ts # Daemon entry point
949
1015
  ├── daemon-mcp/
@@ -954,12 +1020,14 @@ src/
954
1020
  │ └── index.ts # MCP shim entry point (stdio → socket)
955
1021
  ├── hooks/
956
1022
  │ └── ts/ # TypeScript hook sources by event
957
- │ ├── PreCompact/
958
- │ ├── PreToolUse/
959
- │ ├── PostToolUse/
960
- │ ├── SessionStart/
961
- │ ├── Stop/
962
- └── UserPromptSubmit/
1023
+ │ ├── pre-compact/ # context-compression-hook.ts
1024
+ │ ├── pre-tool-use/ # security-validator
1025
+ │ ├── post-tool-use/ # observe, capture-tool-output, sync-todo-to-md, update-tab-on-action
1026
+ │ ├── session-start/ # load-core-context, load-project-context, initialize-session, inject-observations, post-compact-inject
1027
+ │ ├── session-end/ # capture-session-summary
1028
+ ├── stop/ # stop-hook
1029
+ │ ├── subagent-stop/ # subagent-stop-hook
1030
+ │ └── user-prompt/ # cleanup-session-files, update-tab-titles, whisper-rules
963
1031
  ├── mcp/
964
1032
  │ └── tools/ # Shared tool implementations
965
1033
  │ ├── memory.ts
package/FEATURE.md CHANGED
@@ -28,14 +28,19 @@ different direction: persistent memory, session continuity, and deep Claude Code
28
28
  | **Persistent session memory** | No | Yes — auto-indexed, 449K+ chunks |
29
29
  | **Session registry** | No | Yes — SQLite, tracks 77+ projects |
30
30
  | **Background daemon** | No | Yes — launchd, IPC via Unix socket |
31
- | **MCP server** | No | Yes — 9 tools, 18 prompts, 11 resources exposed to Claude Code |
31
+ | **MCP server** | No | Yes — 9 tools, 19 prompts, 11 resources exposed to Claude Code |
32
32
  | **Keyword search (BM25)** | No | Yes — GIN full-text index, PostgreSQL |
33
33
  | **Semantic search (vector)** | No | Yes — pgvector HNSW, Snowflake Arctic 768-dim |
34
34
  | **Multi-backend storage** | No | Yes — SQLite (simple) or PostgreSQL (full) |
35
35
  | **Obsidian vault bridge** | No | Yes — symlinks + auto-generated topic pages |
36
36
  | **Project lifecycle** | No | Yes — promote, archive, move, detect from cwd |
37
+ | **Auto project registration** | No | Yes — detects .git, package.json, pubspec.yaml, etc. on session start |
37
38
  | **Setup wizard** | No | Yes — idempotent 14-step interactive wizard |
38
- | **Hook system** | No | Yes — pre-compact, session-stop, auto-cleanup |
39
+ | **Hook system** | No | Yes — pre-compact, session-stop, auto-cleanup, whisper rules |
40
+ | **Automatic session notes** | No | Yes — AI-generated via daemon worker (Opus/Sonnet), topic-based splitting |
41
+ | **Topic-based note splitting** | No | Yes — Jaccard similarity detects topic shifts, creates separate notes |
42
+ | **Whisper rules** | No | Yes — injects critical rules on every prompt, survives compaction and /clear |
43
+ | **Session note reconstruction** | No | Yes — /reconstruct skill retroactively creates notes from JSONL + git history |
39
44
  | **Backup / restore** | No | Yes — timestamped pg_dump + registry export |
40
45
  | **Multi-session concurrency** | n/a | Yes — daemon multiplexes Claude sessions |
41
46
  | **Custom statusline** | No | Yes — model, MCPs, context meter, colors |
@@ -6,7 +6,7 @@ Technical reference for PAI's modular plugin system, cross-platform support, use
6
6
 
7
7
  ## Overview
8
8
 
9
- PAI is structured as a modular plugin system with 8 named modules organized into 3 pricing tiers. The architecture supports Claude Code (full integration), Cursor (MCP only), and Gemini CLI (MCP only).
9
+ PAI is structured as a modular plugin system with 8 named modules organized into 3 pricing tiers. The architecture supports Claude Code (full integration), Cursor (MCP only), and Gemini CLI (MCP only). Current version: 0.8.0.
10
10
 
11
11
  ```
12
12
  PAI Knowledge OS
@@ -54,24 +54,24 @@ Each module has a `plugins/<module>/plugin.json` that declares:
54
54
 
55
55
  | Module | Tier | Hooks | Skills | Description |
56
56
  |--------|------|-------|--------|-------------|
57
- | `core` | free | 6 | 3 | Memory engine, sessions, projects, security |
58
- | `productivity` | free | 2 | 6 | Plan, Review, Journal, Research, Share, Createskill |
59
- | `ui` | free | 2 | 0 | Tab titles, statusline, tab coloring |
57
+ | `core` | free | 6 | 3 | Memory engine, sessions, projects, security, auto-registration |
58
+ | `productivity` | free | 2 | 7 | Plan, Review, Journal, Research, Share, Createskill, Reconstruct |
59
+ | `ui` | free | 3 | 0 | Tab titles, statusline, tab coloring, whisper rules |
60
60
  | `context-preservation` | free | 3 | 0 | Context compression and relay |
61
61
  | `semantic-search` | pro | 0 | 0 | pgvector, reranking, hybrid search |
62
- | `observability` | pro | 13 | 2 | Event capture, classification, summaries |
62
+ | `observability` | pro | 13 | 2 | Event capture, classification, AI-powered session summaries, topic detection |
63
63
  | `zettelkasten` | enterprise | 0 | 5 | Graph operations, vault intelligence |
64
64
  | `creative` | enterprise | 0 | 2 | Art direction, story, voice/prosody |
65
65
 
66
66
  ### Hook Distribution
67
67
 
68
- Total: 26 hook registrations across 6 modules.
68
+ Total: 27 hook registrations across 6 modules.
69
69
 
70
- **Core (6):** load-core-context, load-project-context, initialize-session, security-validator, stop-hook, pai-session-stop.sh
70
+ **Core (6):** load-core-context, load-project-context (with auto-registration), initialize-session, security-validator, stop-hook, pai-session-stop.sh
71
71
 
72
72
  **Productivity (2):** sync-todo-to-md, cleanup-session-files
73
73
 
74
- **UI (2):** update-tab-titles, update-tab-on-action
74
+ **UI (3):** update-tab-titles, update-tab-on-action, whisper-rules
75
75
 
76
76
  **Context Preservation (3):** context-compression-hook, pai-pre-compact.sh, post-compact-inject
77
77
 
@@ -79,11 +79,11 @@ Total: 26 hook registrations across 6 modules.
79
79
 
80
80
  ### Skill Distribution
81
81
 
82
- Total: 18 skills across 5 modules.
82
+ Total: 19 skills across 5 modules.
83
83
 
84
84
  **Core (3):** Sessions, Route, Name
85
85
 
86
- **Productivity (6):** Plan, Review, Journal, Research, Share, Createskill
86
+ **Productivity (7):** Plan, Review, Journal, Research, Share, Createskill, Reconstruct
87
87
 
88
88
  **Observability (2):** Observability, SearchHistory
89
89
 
@@ -162,9 +162,9 @@ Claude Code gets the complete PAI experience:
162
162
  |------------|---------|
163
163
  | MCP Tools (9) | Full |
164
164
  | MCP Resources (11) | Full |
165
- | MCP Prompts (18) | Full |
166
- | Hooks (26 registrations) | Full |
167
- | Skills (18 SKILL.md stubs) | Full |
165
+ | MCP Prompts (19) | Full |
166
+ | Hooks (27 registrations) | Full |
167
+ | Skills (19 SKILL.md stubs) | Full |
168
168
  | Statusline | Full |
169
169
  | Tab management | Full |
170
170
 
@@ -342,16 +342,31 @@ No migration needed. The plugin architecture is purely additive:
342
342
 
343
343
  ## Future Roadmap
344
344
 
345
- ### Phase 1 (v0.7.0 — Current)
345
+ ### Phase 1 (v0.7.0 — Implemented)
346
346
  - Module manifest system
347
347
  - Cross-platform manifests
348
348
  - User extension points
349
349
  - Tier annotations (no enforcement)
350
350
 
351
- ### Phase 2 (v0.8.0)
351
+ ### Phase 2 (v0.8.0 — Implemented)
352
+ - AI-powered session notes via daemon work queue
353
+ - Topic-based note splitting (Jaccard similarity)
354
+ - Whisper rules hook (persistent rule injection)
355
+ - Reconstruct skill (retroactive session note creation)
356
+ - API key stripping for cost-safe headless Claude spawning
357
+
358
+ ### Phase 3 (v0.9.0)
352
359
  - `pai plugins list` — show installed modules and tiers
353
360
  - `pai plugins enable/disable <module>` — selective module activation
354
- - Build system reads `pai-plugin.json` to generate platform manifests
361
+ - License validation system
362
+ - `pai license activate <key>` command
363
+ - Graceful tier gating with upgrade prompts
364
+
365
+ ### Phase 4 (v1.0.0)
366
+ - Plugin marketplace integration
367
+ - Third-party plugin support
368
+ - Plugin dependency resolution
369
+ - Community plugin repository
355
370
 
356
371
  ### Phase 3 (v0.9.0)
357
372
  - License validation system
package/README.md CHANGED
@@ -1,8 +1,27 @@
1
- # PAI Knowledge OS
1
+ # PAI Knowledge OS — v0.8.0
2
2
 
3
- Claude Code has a memory problem. Every new session starts cold — no idea what you built yesterday, what decisions you made, or where you left off. You re-explain everything, every time. PAI fixes this.
3
+ Claude Code has a memory problem. Every new session starts cold — no idea what you built yesterday, what decisions you made, or where you left off. PAI fixes this.
4
4
 
5
- Install PAI and Claude remembers. Ask it what you were working on. Ask it to find that conversation about the database schema. Ask it to pick up exactly where the last session ended. It knows.
5
+ ## Automatic Session Notes by Topic
6
+
7
+ PAI's headline feature: **every session is automatically documented.** No manual note-taking, no "pause session" commands, no forgetting to save what you did.
8
+
9
+ When you work, a background daemon watches your session **continuously**. Every time Claude's context compacts — which happens automatically as the conversation grows — the daemon reads the JSONL transcript, combines it with your git history, and spawns a headless Claude process to write a structured session note. Not just at session end. Midway through your work, while you're still coding. The notes build up in real time as you go — what was built, what decisions were made, what problems were hit, what's left to do.
10
+
11
+ **When you change topics mid-session, PAI creates a new note.** If you start the day debugging audio, then pivot to a Flutter rewrite, you get two notes — not one giant file mixing unrelated work:
12
+
13
+ ```
14
+ Notes/2026/03/
15
+ 0001 - 2026-03-23 - Phase 1 Research and Architecture.md
16
+ 0002 - 2026-03-24 - Background Audio and iOS Conflicts.md
17
+ 0003 - 2026-03-24 - Flutter Rewrite with Whisper.md ← auto-split, same day
18
+ ```
19
+
20
+ Topic detection uses Jaccard word similarity between the new summary's topic and the existing note's title. Below 30% overlap = new note.
21
+
22
+ **Model tiering:** Opus for final session summaries (best quality, runs once). Sonnet for mid-session checkpoints (good quality, runs on compaction). All using your Max plan — no API charges.
23
+
24
+ This is not a template or a skeleton. These are real notes with build error chronologies, architectural decisions with rationale, code snippets, and "what was tried and failed" sections. The kind of notes you'd write yourself if you had time.
6
25
 
7
26
  ---
8
27
 
@@ -56,6 +75,7 @@ Install PAI and Claude remembers. Ask it what you were working on. Ask it to fin
56
75
  - "Go" — reads your TODO.md continuation prompt and picks up exactly where the last session stopped
57
76
  - "What was I working on?" — progressive context injection loads recent observations at session start
58
77
  - "Continue the daemon refactor" — session summaries give Claude full context without re-explaining
78
+ - "/reconstruct" — retroactively creates session notes from JSONL transcripts and git history when automatic capture missed a session
59
79
 
60
80
  ### Keeping Things Safe
61
81
 
@@ -114,15 +134,59 @@ PAI runs hooks at every stage of a Claude Code session:
114
134
 
115
135
  | Event | What PAI Does |
116
136
  |-------|--------------|
117
- | **Session Start** | Loads project context, detects which project you're in, creates a session note |
118
- | **User Prompt** | Cleans up temp files, updates terminal tab titles |
119
- | **Pre-Compact** | Saves session state checkpoint, sends notification |
137
+ | **Session Start** | Loads project context, detects which project you're in, auto-registers new projects, creates a session note |
138
+ | **User Prompt** | Cleans up temp files, updates terminal tab titles, injects whisper rules on every prompt |
139
+ | **Pre-Compact** | Saves session state checkpoint, pushes `session-summary` work item to daemon, sends notification |
120
140
  | **Post-Compact** | Injects preserved state back into Claude's context |
121
141
  | **Tool Use** | Classifies tool calls into structured observations (decision/bugfix/feature/refactor/discovery/change) |
122
- | **Session End** | Summarizes work done, finalizes session note |
123
- | **Stop** | Writes work items to session note, sends notification |
142
+ | **Session End** | Pushes `session-summary` work item to daemon for AI-powered note generation |
143
+ | **Stop** | Pushes `session-summary` work item to daemon, sends notification |
144
+
145
+ All hooks are TypeScript compiled to `.mjs` modules. They run as separate processes and communicate via stdin (JSON input from Claude Code) and stdout (context injection back into the conversation). Hooks are thin relays — they capture minimal data and immediately push work items to the daemon queue, which handles all heavy processing asynchronously.
146
+
147
+ ---
148
+
149
+ ## Automatic Session Notes
150
+
151
+ PAI automatically writes structured session notes after every session ends — no manual journaling required. The daemon spawns a headless Claude CLI process (using your Max plan, not the API) to summarize the JSONL conversation transcript combined with recent git history.
152
+
153
+ ### What Gets Generated
154
+
155
+ Each session note contains:
156
+
157
+ - **Work Done** — concrete description of what was accomplished
158
+ - **Key Decisions** — choices made and their rationale
159
+ - **Known Issues** — bugs found, blockers, or open questions
160
+ - **Next Steps** — where to pick up in the next session
161
+
162
+ The summarizer uses tiered model selection based on the trigger:
163
+
164
+ | Trigger | Model | Timeout | JSONL Limit |
165
+ |---------|-------|---------|-------------|
166
+ | Session end (Stop hook) | Opus | 5 minutes | 500K bytes |
167
+ | Auto-compaction (PreCompact hook) | Sonnet | 2 minutes | 200K bytes |
168
+
169
+ ### Topic-Based Note Splitting
124
170
 
125
- All hooks are TypeScript compiled to `.mjs` modules. They run as separate processes, communicate via stdin (JSON input from Claude Code) and stdout (context injection back into the conversation).
171
+ When a session covers multiple distinct topics, PAI creates separate notes rather than one long note for the whole session. The summarizer outputs a `TOPIC:` line describing the subject of the current work. PAI compares this against the existing note title using Jaccard word similarity when similarity falls below 30%, a new note is created automatically.
172
+
173
+ Notes within the same day are numbered sequentially: `0042 - 2026-03-24 - Session Name.md`, `0043 - 2026-03-24 - Different Topic.md`, and so on.
174
+
175
+ ### One Note Per Session
176
+
177
+ Each compaction within a session updates the existing note rather than creating a new one. The 30-minute cooldown between summaries prevents redundant updates. Stop hook triggers bypass the cooldown with a force flag to ensure the final state is always captured.
178
+
179
+ ### Garbage Title Filter
180
+
181
+ Session note titles are validated before creation. Over 20 patterns are rejected, including: task notification strings, `[object Object]`, hex hashes, bare numbers, and other non-descriptive artifacts that can appear in session transcripts. Titles must describe actual work done and are capped at 60 characters.
182
+
183
+ ### Finding the Claude Binary
184
+
185
+ The daemon runs under launchd with a minimal PATH that does not include `~/.local/bin/`. PAI resolves the Claude CLI binary by checking `~/.local/bin/claude` first, then falling back to PATH lookup, before spawning headless summarization processes.
186
+
187
+ ### Stripping the API Key
188
+
189
+ When spawning headless Claude CLI processes for summarization, the daemon strips `ANTHROPIC_API_KEY` from the subprocess environment. This forces the spawned process to authenticate via your Max plan (free) rather than using the API key (billable). Without this, every automatic session note would incur API charges.
126
190
 
127
191
  ---
128
192
 
@@ -187,6 +251,27 @@ When a session ends, PAI generates a structured summary capturing what was reque
187
251
 
188
252
  ---
189
253
 
254
+ ## Whisper Rules
255
+
256
+ PAI provides a hook that injects user-defined rules into every prompt via `UserPromptSubmit`. Rules survive compaction, `/clear`, and session restarts — they fire on every single turn, making them the most reliable way to enforce behavioral constraints.
257
+
258
+ **PAI ships the mechanism. You provide the rules.** The file `~/.claude/whisper-rules.md` does not exist by default. Use the `/whisper` skill to manage your rules:
259
+
260
+ ```
261
+ /whisper — show current rules
262
+ /whisper add "NEVER send emails" — add a rule
263
+ /whisper remove 3 — remove rule #3
264
+ /whisper list — list with line numbers
265
+ ```
266
+
267
+ Or edit `~/.claude/whisper-rules.md` directly — one rule per line, plain text.
268
+
269
+ **Keep rules focused.** Every rule is injected on every prompt. Too many rules dilute effectiveness and waste tokens. Reserve whisper rules for truly critical constraints that keep getting violated despite being in CLAUDE.md.
270
+
271
+ The pattern is inspired by [Letta's claude-subconscious](https://github.com/letta-ai/claude-subconscious) approach to persistent context injection.
272
+
273
+ ---
274
+
190
275
  ## Auto-Compact Context Window
191
276
 
192
277
  Claude Code can automatically compact your context window when it fills up, preventing session interruptions mid-task. PAI's statusline shows you at a glance whether auto-compact is active.
@@ -13,11 +13,7 @@ function getWhisperRules() {
13
13
  } catch {
14
14
  }
15
15
  }
16
- return [
17
- "NEVER suggest pausing, stopping, or ending the session. The user decides when to stop. Not you. Ever.",
18
- "NEVER send emails. Always create drafts. No exceptions.",
19
- "NEVER add Co-Authored-By or AI attribution to git commits."
20
- ].join("\n");
16
+ return "";
21
17
  }
22
18
  function main() {
23
19
  const rules = getWhisperRules();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/hooks/ts/user-prompt/whisper-rules.ts"],
4
- "sourcesContent": ["#!/usr/bin/env node\n\n/**\n * whisper-rules.ts\n *\n * UserPromptSubmit hook that injects critical non-negotiable rules into every\n * prompt as a <system-reminder>. This ensures rules survive compaction \u2014 even\n * if CLAUDE.md content is lost during context compression, the whisper\n * re-injects the absolute rules on every single turn.\n *\n * Inspired by Letta's \"claude-subconscious\" whisper pattern.\n *\n * Rules are loaded from ~/.claude/whisper-rules.md if it exists,\n * otherwise falls back to hardcoded critical rules.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nconst WHISPER_FILE = join(homedir(), \".claude\", \"whisper-rules.md\");\n\nfunction getWhisperRules(): string {\n // User-customizable whisper file takes priority\n if (existsSync(WHISPER_FILE)) {\n try {\n const content = readFileSync(WHISPER_FILE, \"utf-8\").trim();\n if (content) return content;\n } catch { /* fall through to defaults */ }\n }\n\n // Hardcoded critical rules \u2014 the ones that keep getting violated\n return [\n \"NEVER suggest pausing, stopping, or ending the session. The user decides when to stop. Not you. Ever.\",\n \"NEVER send emails. Always create drafts. No exceptions.\",\n \"NEVER add Co-Authored-By or AI attribution to git commits.\",\n ].join(\"\\n\");\n}\n\nfunction main() {\n const rules = getWhisperRules();\n if (!rules) return;\n\n // Output as system-reminder \u2014 Claude Code injects this into the conversation\n console.log(`<system-reminder>\n${rules}\n</system-reminder>`);\n}\n\nmain();\n"],
5
- "mappings": ";;;AAgBA,SAAS,YAAY,oBAAoB;AACzC,SAAS,YAAY;AACrB,SAAS,eAAe;AAExB,IAAM,eAAe,KAAK,QAAQ,GAAG,WAAW,kBAAkB;AAElE,SAAS,kBAA0B;AAEjC,MAAI,WAAW,YAAY,GAAG;AAC5B,QAAI;AACF,YAAM,UAAU,aAAa,cAAc,OAAO,EAAE,KAAK;AACzD,UAAI,QAAS,QAAO;AAAA,IACtB,QAAQ;AAAA,IAAiC;AAAA,EAC3C;AAGA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAEA,SAAS,OAAO;AACd,QAAM,QAAQ,gBAAgB;AAC9B,MAAI,CAAC,MAAO;AAGZ,UAAQ,IAAI;AAAA,EACZ,KAAK;AAAA,mBACY;AACnB;AAEA,KAAK;",
4
+ "sourcesContent": ["#!/usr/bin/env node\n\n/**\n * whisper-rules.ts\n *\n * UserPromptSubmit hook that injects critical non-negotiable rules into every\n * prompt as a <system-reminder>. This ensures rules survive compaction \u2014 even\n * if CLAUDE.md content is lost during context compression, the whisper\n * re-injects the absolute rules on every single turn.\n *\n * Inspired by Letta's \"claude-subconscious\" whisper pattern.\n *\n * Rules are loaded from ~/.claude/whisper-rules.md if it exists,\n * otherwise falls back to hardcoded critical rules.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nconst WHISPER_FILE = join(homedir(), \".claude\", \"whisper-rules.md\");\n\nfunction getWhisperRules(): string {\n // User-managed whisper file \u2014 PAI provides the hook, user provides the rules\n // Configure via /whisper skill or edit ~/.claude/whisper-rules.md directly\n if (existsSync(WHISPER_FILE)) {\n try {\n const content = readFileSync(WHISPER_FILE, \"utf-8\").trim();\n if (content) return content;\n } catch { /* ignore read errors */ }\n }\n\n // No rules configured \u2014 silent (no injection)\n return \"\";\n}\n\nfunction main() {\n const rules = getWhisperRules();\n if (!rules) return;\n\n // Output as system-reminder \u2014 Claude Code injects this into the conversation\n console.log(`<system-reminder>\n${rules}\n</system-reminder>`);\n}\n\nmain();\n"],
5
+ "mappings": ";;;AAgBA,SAAS,YAAY,oBAAoB;AACzC,SAAS,YAAY;AACrB,SAAS,eAAe;AAExB,IAAM,eAAe,KAAK,QAAQ,GAAG,WAAW,kBAAkB;AAElE,SAAS,kBAA0B;AAGjC,MAAI,WAAW,YAAY,GAAG;AAC5B,QAAI;AACF,YAAM,UAAU,aAAa,cAAc,OAAO,EAAE,KAAK;AACzD,UAAI,QAAS,QAAO;AAAA,IACtB,QAAQ;AAAA,IAA2B;AAAA,EACrC;AAGA,SAAO;AACT;AAEA,SAAS,OAAO;AACd,QAAM,QAAQ,gBAAgB;AAC9B,MAAI,CAAC,MAAO;AAGZ,UAAQ,IAAI;AAAA,EACZ,KAAK;AAAA,mBACY;AACnB;AAEA,KAAK;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: Whisper
3
+ description: "Manage whisper rules — persistent behavioral constraints injected on every prompt. USE WHEN user says 'whisper', 'add whisper rule', 'remove whisper rule', 'list whisper rules', 'show whisper rules', '/whisper', OR wants to manage persistent behavioral rules."
4
+ ---
5
+
6
+ ## Whisper Rules Management
7
+
8
+ USE WHEN user says 'whisper', 'add whisper rule', 'remove whisper rule', 'list whisper rules', 'show whisper rules', '/whisper', OR wants to manage persistent behavioral rules.
9
+
10
+ Manage the rules that PAI injects into every prompt via the whisper-rules hook.
11
+
12
+ Rules are stored in `~/.claude/whisper-rules.md` — one rule per line, plain text.
13
+ The hook reads this file on every UserPromptSubmit and injects it as a `<system-reminder>`.
14
+ Rules survive compaction, /clear, and session restarts.
15
+
16
+ ### Usage
17
+
18
+ - `/whisper` — show current rules
19
+ - `/whisper add <rule>` — add a new rule
20
+ - `/whisper remove <number>` — remove rule by line number
21
+ - `/whisper list` — list rules with line numbers
22
+ - `/whisper clear` — remove all rules (with confirmation)
23
+
24
+ ### Workflow
25
+
26
+ **Show current rules:**
27
+ Read `~/.claude/whisper-rules.md` and display each rule with a line number.
28
+ If the file doesn't exist, say "No whisper rules configured."
29
+
30
+ **Add a rule:**
31
+ Append the rule as a new line to `~/.claude/whisper-rules.md`.
32
+ Create the file if it doesn't exist.
33
+ Do NOT add duplicate rules — check if a similar rule already exists.
34
+
35
+ **Remove a rule:**
36
+ Read the file, remove the line at the given number, write the file back.
37
+ Show the removed rule for confirmation.
38
+
39
+ **Clear all rules:**
40
+ Ask for confirmation first ("This will remove all N rules. Confirm?").
41
+ Only proceed if the user explicitly confirms.
42
+
43
+ ### Important
44
+
45
+ - Rules should be short, imperative statements (1-2 lines max)
46
+ - Every rule is injected on EVERY prompt — keep the list focused on truly critical rules
47
+ - Too many rules dilute their effectiveness and waste tokens
48
+ - The file does not exist by default — PAI ships the hook, the user adds their own rules
49
+ - Rules are global (shared across all sessions and projects)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tekmidian/pai",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "PAI Knowledge OS — Personal AI Infrastructure with federated memory and project management",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -21,20 +21,17 @@ import { homedir } from "node:os";
21
21
  const WHISPER_FILE = join(homedir(), ".claude", "whisper-rules.md");
22
22
 
23
23
  function getWhisperRules(): string {
24
- // User-customizable whisper file takes priority
24
+ // User-managed whisper file PAI provides the hook, user provides the rules
25
+ // Configure via /whisper skill or edit ~/.claude/whisper-rules.md directly
25
26
  if (existsSync(WHISPER_FILE)) {
26
27
  try {
27
28
  const content = readFileSync(WHISPER_FILE, "utf-8").trim();
28
29
  if (content) return content;
29
- } catch { /* fall through to defaults */ }
30
+ } catch { /* ignore read errors */ }
30
31
  }
31
32
 
32
- // Hardcoded critical rules — the ones that keep getting violated
33
- return [
34
- "NEVER suggest pausing, stopping, or ending the session. The user decides when to stop. Not you. Ever.",
35
- "NEVER send emails. Always create drafts. No exceptions.",
36
- "NEVER add Co-Authored-By or AI attribution to git commits.",
37
- ].join("\n");
33
+ // No rules configured silent (no injection)
34
+ return "";
38
35
  }
39
36
 
40
37
  function main() {