@tekmidian/pai 0.7.9 → 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 +82 -14
- package/FEATURE.md +7 -2
- package/PLUGIN-ARCHITECTURE.md +31 -16
- package/README.md +94 -9
- package/dist/hooks/whisper-rules.mjs +1 -5
- package/dist/hooks/whisper-rules.mjs.map +2 -2
- package/dist/skills/Whisper/SKILL.md +49 -0
- package/package.json +1 -1
- package/src/hooks/ts/user-prompt/whisper-rules.ts +5 -8
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,
|
|
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,
|
|
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
|
-
| `
|
|
600
|
-
| `
|
|
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
|
-
│ │
|
|
946
|
-
│ ├──
|
|
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
|
-
│ ├──
|
|
958
|
-
│ ├──
|
|
959
|
-
│ ├──
|
|
960
|
-
│ ├──
|
|
961
|
-
│ ├──
|
|
962
|
-
│
|
|
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,
|
|
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 |
|
package/PLUGIN-ARCHITECTURE.md
CHANGED
|
@@ -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 |
|
|
59
|
-
| `ui` | free |
|
|
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:
|
|
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 (
|
|
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:
|
|
82
|
+
Total: 19 skills across 5 modules.
|
|
83
83
|
|
|
84
84
|
**Core (3):** Sessions, Route, Name
|
|
85
85
|
|
|
86
|
-
**Productivity (
|
|
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 (
|
|
166
|
-
| Hooks (
|
|
167
|
-
| Skills (
|
|
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 —
|
|
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
|
-
-
|
|
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.
|
|
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
|
-
|
|
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** |
|
|
123
|
-
| **Stop** |
|
|
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
|
-
|
|
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-
|
|
5
|
-
"mappings": ";;;AAgBA,SAAS,YAAY,oBAAoB;AACzC,SAAS,YAAY;AACrB,SAAS,eAAe;AAExB,IAAM,eAAe,KAAK,QAAQ,GAAG,WAAW,kBAAkB;AAElE,SAAS,kBAA0B;
|
|
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
|
@@ -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-
|
|
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 { /*
|
|
30
|
+
} catch { /* ignore read errors */ }
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
//
|
|
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() {
|