@tekmidian/pai 0.5.7 → 0.6.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 +198 -47
- package/FEATURE.md +2 -2
- package/README.md +87 -1
- package/dist/{auto-route-BG6I_4B1.mjs → auto-route-C-DrW6BL.mjs} +3 -3
- package/dist/{auto-route-BG6I_4B1.mjs.map → auto-route-C-DrW6BL.mjs.map} +1 -1
- package/dist/cli/index.mjs +1482 -1628
- package/dist/cli/index.mjs.map +1 -1
- package/dist/clusters-JIDQW65f.mjs +201 -0
- package/dist/clusters-JIDQW65f.mjs.map +1 -0
- package/dist/{config-Cf92lGX_.mjs → config-BuhHWyOK.mjs} +21 -6
- package/dist/config-BuhHWyOK.mjs.map +1 -0
- package/dist/daemon/index.mjs +11 -8
- package/dist/daemon/index.mjs.map +1 -1
- package/dist/{daemon-2ND5WO2j.mjs → daemon-D3hYb5_C.mjs} +669 -218
- package/dist/daemon-D3hYb5_C.mjs.map +1 -0
- package/dist/daemon-mcp/index.mjs +4597 -4
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/db-DdUperSl.mjs +110 -0
- package/dist/db-DdUperSl.mjs.map +1 -0
- package/dist/{detect-BU3Nx_2L.mjs → detect-CdaA48EI.mjs} +1 -1
- package/dist/{detect-BU3Nx_2L.mjs.map → detect-CdaA48EI.mjs.map} +1 -1
- package/dist/{detector-Bp-2SM3x.mjs → detector-jGBuYQJM.mjs} +2 -2
- package/dist/{detector-Bp-2SM3x.mjs.map → detector-jGBuYQJM.mjs.map} +1 -1
- package/dist/{factory-Bzcy70G9.mjs → factory-Ygqe_bVZ.mjs} +7 -5
- package/dist/{factory-Bzcy70G9.mjs.map → factory-Ygqe_bVZ.mjs.map} +1 -1
- package/dist/helpers-BEST-4Gx.mjs +420 -0
- package/dist/helpers-BEST-4Gx.mjs.map +1 -0
- package/dist/hooks/capture-all-events.mjs +2 -2
- package/dist/hooks/capture-all-events.mjs.map +3 -3
- package/dist/hooks/capture-session-summary.mjs +38 -0
- package/dist/hooks/capture-session-summary.mjs.map +3 -3
- package/dist/hooks/cleanup-session-files.mjs +6 -12
- package/dist/hooks/cleanup-session-files.mjs.map +4 -4
- package/dist/hooks/context-compression-hook.mjs +93 -104
- package/dist/hooks/context-compression-hook.mjs.map +4 -4
- package/dist/hooks/initialize-session.mjs +14 -11
- package/dist/hooks/initialize-session.mjs.map +4 -4
- package/dist/hooks/inject-observations.mjs +220 -0
- package/dist/hooks/inject-observations.mjs.map +7 -0
- package/dist/hooks/load-core-context.mjs +2 -2
- package/dist/hooks/load-core-context.mjs.map +3 -3
- package/dist/hooks/load-project-context.mjs +90 -91
- package/dist/hooks/load-project-context.mjs.map +4 -4
- package/dist/hooks/observe.mjs +354 -0
- package/dist/hooks/observe.mjs.map +7 -0
- package/dist/hooks/stop-hook.mjs +94 -107
- package/dist/hooks/stop-hook.mjs.map +4 -4
- package/dist/hooks/sync-todo-to-md.mjs +31 -33
- package/dist/hooks/sync-todo-to-md.mjs.map +4 -4
- package/dist/index.d.mts +30 -7
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +5 -8
- package/dist/indexer-D53l5d1U.mjs +1 -0
- package/dist/{indexer-backend-CIMXedqk.mjs → indexer-backend-jcJFsmB4.mjs} +37 -127
- package/dist/indexer-backend-jcJFsmB4.mjs.map +1 -0
- package/dist/{ipc-client-Bjg_a1dc.mjs → ipc-client-CoyUHPod.mjs} +2 -7
- package/dist/{ipc-client-Bjg_a1dc.mjs.map → ipc-client-CoyUHPod.mjs.map} +1 -1
- package/dist/latent-ideas-bTJo6Omd.mjs +191 -0
- package/dist/latent-ideas-bTJo6Omd.mjs.map +1 -0
- package/dist/neighborhood-BYYbEkUJ.mjs +135 -0
- package/dist/neighborhood-BYYbEkUJ.mjs.map +1 -0
- package/dist/note-context-BK24bX8Y.mjs +126 -0
- package/dist/note-context-BK24bX8Y.mjs.map +1 -0
- package/dist/postgres-CKf-EDtS.mjs +846 -0
- package/dist/postgres-CKf-EDtS.mjs.map +1 -0
- package/dist/{reranker-D7bRAHi6.mjs → reranker-CMNZcfVx.mjs} +1 -1
- package/dist/{reranker-D7bRAHi6.mjs.map → reranker-CMNZcfVx.mjs.map} +1 -1
- package/dist/{search-_oHfguA5.mjs → search-DC1qhkKn.mjs} +2 -58
- package/dist/search-DC1qhkKn.mjs.map +1 -0
- package/dist/{sqlite-WWBq7_2C.mjs → sqlite-l-s9xPjY.mjs} +160 -3
- package/dist/sqlite-l-s9xPjY.mjs.map +1 -0
- package/dist/state-C6_vqz7w.mjs +102 -0
- package/dist/state-C6_vqz7w.mjs.map +1 -0
- package/dist/stop-words-BaMEGVeY.mjs +326 -0
- package/dist/stop-words-BaMEGVeY.mjs.map +1 -0
- package/dist/{indexer-CMPOiY1r.mjs → sync-BOsnEj2-.mjs} +14 -216
- package/dist/sync-BOsnEj2-.mjs.map +1 -0
- package/dist/themes-BvYF0W8T.mjs +148 -0
- package/dist/themes-BvYF0W8T.mjs.map +1 -0
- package/dist/{tools-DV_lsiCc.mjs → tools-DcaJlYDN.mjs} +162 -273
- package/dist/tools-DcaJlYDN.mjs.map +1 -0
- package/dist/trace-CRx9lPuc.mjs +137 -0
- package/dist/trace-CRx9lPuc.mjs.map +1 -0
- package/dist/{vault-indexer-k-kUlaZ-.mjs → vault-indexer-Bi2cRmn7.mjs} +134 -132
- package/dist/vault-indexer-Bi2cRmn7.mjs.map +1 -0
- package/dist/zettelkasten-cdajbnPr.mjs +708 -0
- package/dist/zettelkasten-cdajbnPr.mjs.map +1 -0
- package/package.json +1 -2
- package/src/hooks/ts/lib/project-utils/index.ts +50 -0
- package/src/hooks/ts/lib/project-utils/notify.ts +75 -0
- package/src/hooks/ts/lib/project-utils/paths.ts +218 -0
- package/src/hooks/ts/lib/project-utils/session-notes.ts +363 -0
- package/src/hooks/ts/lib/project-utils/todo.ts +178 -0
- package/src/hooks/ts/lib/project-utils/tokens.ts +39 -0
- package/src/hooks/ts/lib/project-utils.ts +40 -1018
- package/src/hooks/ts/post-tool-use/observe.ts +327 -0
- package/src/hooks/ts/session-end/capture-session-summary.ts +41 -0
- package/src/hooks/ts/session-start/inject-observations.ts +254 -0
- package/dist/chunker-CbnBe0s0.mjs +0 -191
- package/dist/chunker-CbnBe0s0.mjs.map +0 -1
- package/dist/config-Cf92lGX_.mjs.map +0 -1
- package/dist/daemon-2ND5WO2j.mjs.map +0 -1
- package/dist/db-Dp8VXIMR.mjs +0 -212
- package/dist/db-Dp8VXIMR.mjs.map +0 -1
- package/dist/indexer-CMPOiY1r.mjs.map +0 -1
- package/dist/indexer-backend-CIMXedqk.mjs.map +0 -1
- package/dist/mcp/index.d.mts +0 -1
- package/dist/mcp/index.mjs +0 -500
- package/dist/mcp/index.mjs.map +0 -1
- package/dist/postgres-FXrHDPcE.mjs +0 -358
- package/dist/postgres-FXrHDPcE.mjs.map +0 -1
- package/dist/schemas-BFIgGntb.mjs +0 -3405
- package/dist/schemas-BFIgGntb.mjs.map +0 -1
- package/dist/search-_oHfguA5.mjs.map +0 -1
- package/dist/sqlite-WWBq7_2C.mjs.map +0 -1
- package/dist/tools-DV_lsiCc.mjs.map +0 -1
- package/dist/vault-indexer-k-kUlaZ-.mjs.map +0 -1
- package/dist/zettelkasten-e-a4rW_6.mjs +0 -901
- package/dist/zettelkasten-e-a4rW_6.mjs.map +0 -1
- package/templates/README.md +0 -181
- package/templates/skills/CORE/Aesthetic.md +0 -333
- package/templates/skills/CORE/CONSTITUTION.md +0 -1502
- package/templates/skills/CORE/HistorySystem.md +0 -427
- package/templates/skills/CORE/HookSystem.md +0 -1082
- package/templates/skills/CORE/Prompting.md +0 -509
- package/templates/skills/CORE/ProsodyAgentTemplate.md +0 -53
- package/templates/skills/CORE/ProsodyGuide.md +0 -416
- package/templates/skills/CORE/SKILL.md +0 -741
- package/templates/skills/CORE/SkillSystem.md +0 -213
- package/templates/skills/CORE/TerminalTabs.md +0 -119
- package/templates/skills/CORE/VOICE.md +0 -106
- package/templates/skills/createskill-skill.template.md +0 -78
- package/templates/skills/history-system.template.md +0 -371
- package/templates/skills/hook-system.template.md +0 -913
- package/templates/skills/sessions-skill.template.md +0 -102
- package/templates/skills/skill-system.template.md +0 -214
- package/templates/skills/terminal-tabs.template.md +0 -120
- package/templates/templates.md +0 -20
package/ARCHITECTURE.md
CHANGED
|
@@ -21,6 +21,8 @@ Claude Code Session
|
|
|
21
21
|
│ ↓
|
|
22
22
|
│ PostgreSQL + pgvector
|
|
23
23
|
│ (chunks, embeddings, files, FTS)
|
|
24
|
+
│ ├── Observation Store (PostgreSQL)
|
|
25
|
+
│ │ classify → store → query → inject
|
|
24
26
|
│
|
|
25
27
|
├── Registry (SQLite)
|
|
26
28
|
│ ~/.pai/registry.db
|
|
@@ -28,7 +30,8 @@ Claude Code Session
|
|
|
28
30
|
│
|
|
29
31
|
└── CLI (pai)
|
|
30
32
|
project, session, registry, memory,
|
|
31
|
-
daemon, obsidian, zettel,
|
|
33
|
+
daemon, obsidian, zettel, observation,
|
|
34
|
+
backup, restore, setup
|
|
32
35
|
```
|
|
33
36
|
|
|
34
37
|
### Key Components
|
|
@@ -92,15 +95,22 @@ docker run -d \
|
|
|
92
95
|
pai setup
|
|
93
96
|
```
|
|
94
97
|
|
|
95
|
-
The interactive wizard walks through
|
|
98
|
+
The interactive wizard walks through 14 steps:
|
|
96
99
|
|
|
97
|
-
1.
|
|
98
|
-
2.
|
|
99
|
-
3.
|
|
100
|
+
1. Welcome and version check
|
|
101
|
+
2. Storage backend selection (SQLite or PostgreSQL)
|
|
102
|
+
3. Embedding model configuration
|
|
100
103
|
4. CLAUDE.md template installation
|
|
101
|
-
5.
|
|
102
|
-
6.
|
|
103
|
-
7.
|
|
104
|
+
5. PAI skill installation
|
|
105
|
+
6. Steering rules installation
|
|
106
|
+
7. Hook system deployment
|
|
107
|
+
8. TypeScript hook compilation
|
|
108
|
+
9. Claude Code settings configuration
|
|
109
|
+
10. Daemon installation
|
|
110
|
+
11. MCP server registration
|
|
111
|
+
12. Directory creation
|
|
112
|
+
13. Initial indexing
|
|
113
|
+
14. Verification
|
|
104
114
|
|
|
105
115
|
### 4. Install the Daemon
|
|
106
116
|
|
|
@@ -140,7 +150,7 @@ If both commands return healthy output, PAI is running. Open a new Claude Code s
|
|
|
140
150
|
|
|
141
151
|
## MCP Server
|
|
142
152
|
|
|
143
|
-
PAI exposes
|
|
153
|
+
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.
|
|
144
154
|
|
|
145
155
|
```
|
|
146
156
|
Claude Code (stdio)
|
|
@@ -161,12 +171,8 @@ Claude Code (stdio)
|
|
|
161
171
|
| `session_list` | List session notes, optionally filtered by project |
|
|
162
172
|
| `registry_search` | Search project metadata (names, paths, tags) |
|
|
163
173
|
| `project_detect` | Identify which project a given path belongs to |
|
|
164
|
-
| `
|
|
165
|
-
| `
|
|
166
|
-
| `zettel_converse` | Hybrid search with graph expansion and cross-domain connections |
|
|
167
|
-
| `zettel_themes` | Cluster vault notes into thematic groups by embedding similarity |
|
|
168
|
-
| `zettel_health` | Audit vault for broken links, orphans, and isolated clusters |
|
|
169
|
-
| `zettel_suggest` | Suggest link targets weighted by semantics, tags, and graph neighborhood |
|
|
174
|
+
| `project_health` | Audit all registered paths for moved or deleted directories |
|
|
175
|
+
| `project_todo` | Read a project's TODO.md and continuation prompt |
|
|
170
176
|
|
|
171
177
|
### Tool Reference
|
|
172
178
|
|
|
@@ -184,17 +190,52 @@ Claude Code (stdio)
|
|
|
184
190
|
|
|
185
191
|
**`project_detect(path?)`** — Given a filesystem path (defaults to CWD), returns the matching project.
|
|
186
192
|
|
|
187
|
-
**`
|
|
193
|
+
**`project_health(category?)`** — Audits all registered projects to find moved or deleted directories. Categorizes each as `active` (path exists), `stale` (path missing but candidate found nearby), or `dead` (path missing, no candidate). Also reports TODO.md presence and continuation prompts.
|
|
188
194
|
|
|
189
|
-
**`
|
|
195
|
+
**`project_todo(project?)`** — Reads a project's TODO.md without needing the exact file path. Searches Notes/TODO.md, .claude/Notes/TODO.md, tasks/todo.md, and project-root TODO.md in order. Surfaces any `## Continue` section at the top for quick context recovery.
|
|
190
196
|
|
|
191
|
-
|
|
197
|
+
### On-Demand Prompts (Skills)
|
|
192
198
|
|
|
193
|
-
|
|
199
|
+
The MCP server registers 18 prompts that Claude can invoke as on-demand skills. Each prompt provides a focused workflow with instructions, examples, and constraints — loaded only when needed to conserve context.
|
|
194
200
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
201
|
+
| Prompt | Purpose |
|
|
202
|
+
|--------|---------|
|
|
203
|
+
| `art` | Visual art direction and creative guidance |
|
|
204
|
+
| `createskill` | Scaffold new PAI skills |
|
|
205
|
+
| `journal` | Structured journaling workflow |
|
|
206
|
+
| `name` | Session and project naming conventions |
|
|
207
|
+
| `observability` | Observation system usage and querying |
|
|
208
|
+
| `plan` | Forward-looking planning from TODOs and recent activity |
|
|
209
|
+
| `research` | Structured research methodology |
|
|
210
|
+
| `review` | Retrospective review of work over a time period |
|
|
211
|
+
| `route` | Session note routing across projects |
|
|
212
|
+
| `search-history` | Search history analysis and patterns |
|
|
213
|
+
| `sessions` | Session lifecycle management |
|
|
214
|
+
| `share` | Generate social media posts from recent work |
|
|
215
|
+
| `story-explanation` | Narrative explanations of technical concepts |
|
|
216
|
+
| `vault-connect` | Suggest and create vault connections |
|
|
217
|
+
| `vault-context` | Use vault as conversational context |
|
|
218
|
+
| `vault-emerge` | Detect emerging themes in the vault |
|
|
219
|
+
| `vault-orphans` | Find and fix orphaned vault notes |
|
|
220
|
+
| `vault-trace` | Trace idea lineage through vault links |
|
|
221
|
+
|
|
222
|
+
### Reference Resources
|
|
223
|
+
|
|
224
|
+
11 resources available via `pai://` URIs. Claude reads these on demand for reference documentation.
|
|
225
|
+
|
|
226
|
+
| URI | Content |
|
|
227
|
+
|-----|---------|
|
|
228
|
+
| `pai://aesthetic` | Visual and output style guidelines |
|
|
229
|
+
| `pai://constitution` | Core philosophy and principles |
|
|
230
|
+
| `pai://history-system` | Search history tracking system |
|
|
231
|
+
| `pai://hook-system` | Hook architecture and development guide |
|
|
232
|
+
| `pai://mcp-dev-guide` | MCP server development patterns |
|
|
233
|
+
| `pai://prompting` | Prompt engineering best practices |
|
|
234
|
+
| `pai://prosody-agent-template` | Voice agent template |
|
|
235
|
+
| `pai://prosody-guide` | Voice and prosody guidelines |
|
|
236
|
+
| `pai://skill-system` | Skill authoring reference |
|
|
237
|
+
| `pai://terminal-tabs` | Terminal tab management |
|
|
238
|
+
| `pai://voice` | Voice configuration reference |
|
|
198
239
|
|
|
199
240
|
### Installation
|
|
200
241
|
|
|
@@ -389,12 +430,27 @@ pai zettel health
|
|
|
389
430
|
pai zettel suggest "My Seed Note" --limit 5
|
|
390
431
|
```
|
|
391
432
|
|
|
433
|
+
### Observation Management
|
|
434
|
+
|
|
435
|
+
| Subcommand | Description |
|
|
436
|
+
|------------|-------------|
|
|
437
|
+
| `observation list` | List recent observations with optional filters |
|
|
438
|
+
| `observation search <query>` | Search observations by title or narrative text |
|
|
439
|
+
| `observation stats` | Show totals, breakdowns by type and project |
|
|
440
|
+
|
|
441
|
+
```bash
|
|
442
|
+
pai observation list --type decision --limit 10
|
|
443
|
+
pai observation list --project my-app
|
|
444
|
+
pai observation search "database migration"
|
|
445
|
+
pai observation stats
|
|
446
|
+
```
|
|
447
|
+
|
|
392
448
|
### Other Commands
|
|
393
449
|
|
|
394
450
|
```bash
|
|
395
451
|
pai backup # Backup registry, config, and Postgres
|
|
396
452
|
pai restore <path> # Restore from backup (--no-postgres to skip DB)
|
|
397
|
-
pai setup # Interactive
|
|
453
|
+
pai setup # Interactive 14-step setup wizard
|
|
398
454
|
pai search "query" # Quick full-text search shortcut
|
|
399
455
|
```
|
|
400
456
|
|
|
@@ -531,6 +587,8 @@ Claude Code Event
|
|
|
531
587
|
| `post-compact-inject.mjs` | SessionStart (compact) | Reads saved state and injects into post-compaction context |
|
|
532
588
|
| `security-validator.mjs` | PreToolUse (Bash) | Validates shell commands against security rules |
|
|
533
589
|
| `capture-all-events.mjs` | All events | Observability — logs every hook event to session timeline |
|
|
590
|
+
| `observe.mjs` | PostToolUse | Classifies tool calls into typed observations (decision/bugfix/feature/refactor/discovery/change) |
|
|
591
|
+
| `inject-observations.mjs` | SessionStart | Injects recent observation context (compact index + timeline) |
|
|
534
592
|
| `context-compression-hook.mjs` | PreCompact | Extracts session state, saves checkpoint, writes temp file for relay |
|
|
535
593
|
| `capture-tool-output.mjs` | PostToolUse | Records tool inputs/outputs for observability dashboard |
|
|
536
594
|
| `update-tab-on-action.mjs` | PostToolUse | Updates terminal tab title based on current activity |
|
|
@@ -704,6 +762,47 @@ These tables are populated by `src/memory/vault-indexer.ts` and queried by all s
|
|
|
704
762
|
| `detail` | TEXT | Human-readable description |
|
|
705
763
|
| `checked_at` | BIGINT | Timestamp of the audit run |
|
|
706
764
|
|
|
765
|
+
### Observation Tables (PostgreSQL)
|
|
766
|
+
|
|
767
|
+
These tables are populated by the PostToolUse hook classifier and queried by the CLI and MCP tools.
|
|
768
|
+
|
|
769
|
+
**`pai_observations`** — Classified tool call events:
|
|
770
|
+
|
|
771
|
+
| Column | Type | Description |
|
|
772
|
+
|--------|------|-------------|
|
|
773
|
+
| `id` | SERIAL | Primary key |
|
|
774
|
+
| `session_id` | TEXT | Claude Code session identifier |
|
|
775
|
+
| `project_id` | INTEGER | Owning project (nullable) |
|
|
776
|
+
| `project_slug` | TEXT | Project slug for display |
|
|
777
|
+
| `type` | TEXT | Classification: decision, bugfix, feature, refactor, discovery, change |
|
|
778
|
+
| `title` | TEXT | Human-readable observation title |
|
|
779
|
+
| `narrative` | TEXT | Extended description (nullable) |
|
|
780
|
+
| `tool_name` | TEXT | Claude Code tool that triggered the observation |
|
|
781
|
+
| `tool_input_summary` | TEXT | Abbreviated tool input |
|
|
782
|
+
| `files_read` | JSONB | Array of file paths read |
|
|
783
|
+
| `files_modified` | JSONB | Array of file paths modified |
|
|
784
|
+
| `concepts` | JSONB | Extracted concept tags |
|
|
785
|
+
| `content_hash` | TEXT | SHA-256 hash for 30-second deduplication window |
|
|
786
|
+
| `created_at` | TIMESTAMPTZ | Observation timestamp |
|
|
787
|
+
|
|
788
|
+
**`pai_session_summaries`** — Structured end-of-session summaries:
|
|
789
|
+
|
|
790
|
+
| Column | Type | Description |
|
|
791
|
+
|--------|------|-------------|
|
|
792
|
+
| `id` | SERIAL | Primary key |
|
|
793
|
+
| `session_id` | TEXT | Claude Code session identifier (unique) |
|
|
794
|
+
| `project_id` | INTEGER | Owning project (nullable) |
|
|
795
|
+
| `project_slug` | TEXT | Project slug for display |
|
|
796
|
+
| `request` | TEXT | What was requested |
|
|
797
|
+
| `investigated` | TEXT | What was investigated |
|
|
798
|
+
| `learned` | TEXT | What was learned |
|
|
799
|
+
| `completed` | TEXT | What was completed |
|
|
800
|
+
| `next_steps` | TEXT | Recommended next steps |
|
|
801
|
+
| `observation_count` | INTEGER | Number of observations in the session |
|
|
802
|
+
| `created_at` | TIMESTAMPTZ | Summary timestamp |
|
|
803
|
+
|
|
804
|
+
**Indexes:** B-tree on project_id, session_id, type, created_at DESC, content_hash.
|
|
805
|
+
|
|
707
806
|
**Content Tiers:**
|
|
708
807
|
|
|
709
808
|
| Tier | Description | Example |
|
|
@@ -752,37 +851,89 @@ bun run lint # tsc --noEmit
|
|
|
752
851
|
| Output | Purpose |
|
|
753
852
|
|--------|---------|
|
|
754
853
|
| `dist/cli/index.mjs` | `pai` CLI |
|
|
755
|
-
| `dist/mcp/index.mjs` | Direct MCP server (legacy) |
|
|
756
854
|
| `dist/daemon/index.mjs` | Daemon server |
|
|
757
855
|
| `dist/daemon-mcp/index.mjs` | MCP shim (stdio → daemon socket) |
|
|
856
|
+
| `dist/hooks/*.mjs` | Compiled lifecycle hooks |
|
|
758
857
|
|
|
759
858
|
### Source Structure
|
|
760
859
|
|
|
761
860
|
```
|
|
762
861
|
src/
|
|
763
|
-
├── cli/
|
|
764
|
-
│
|
|
765
|
-
├──
|
|
766
|
-
├── daemon
|
|
767
|
-
├──
|
|
768
|
-
├──
|
|
769
|
-
├──
|
|
770
|
-
├──
|
|
771
|
-
│ ├──
|
|
772
|
-
│
|
|
773
|
-
├──
|
|
774
|
-
│
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
862
|
+
├── cli/
|
|
863
|
+
│ ├── commands/ # CLI command modules
|
|
864
|
+
│ │ ├── backup.ts
|
|
865
|
+
│ │ ├── daemon.ts
|
|
866
|
+
│ │ ├── memory.ts
|
|
867
|
+
│ │ ├── observation.ts
|
|
868
|
+
│ │ ├── obsidian.ts
|
|
869
|
+
│ │ ├── project.ts
|
|
870
|
+
│ │ ├── registry.ts
|
|
871
|
+
│ │ ├── session.ts
|
|
872
|
+
│ │ ├── setup/ # 14-step interactive wizard
|
|
873
|
+
│ │ │ ├── steps/ # 01-welcome through 15-verify
|
|
874
|
+
│ │ │ └── index.ts
|
|
875
|
+
│ │ └── zettel.ts
|
|
876
|
+
│ └── index.ts # CLI entry point
|
|
877
|
+
├── daemon/
|
|
878
|
+
│ ├── daemon/ # Daemon server internals
|
|
879
|
+
│ │ ├── dispatcher.ts # Tool dispatch (zettel, observation, memory)
|
|
880
|
+
│ │ ├── handler.ts # NDJSON request handler
|
|
881
|
+
│ │ └── server.ts # Socket server
|
|
882
|
+
│ ├── indexer/ # Background index scheduler
|
|
883
|
+
│ ├── config.ts # Runtime configuration
|
|
884
|
+
│ └── index.ts # Daemon entry point
|
|
885
|
+
├── daemon-mcp/
|
|
886
|
+
│ ├── instructions.ts # MCP server instructions (~1.5KB routing table)
|
|
887
|
+
│ ├── prompts/ # 18 on-demand skill prompts
|
|
888
|
+
│ ├── resources/ # 11 reference resources (pai:// URIs)
|
|
889
|
+
│ └── index.ts # MCP shim entry point (stdio → socket)
|
|
890
|
+
├── hooks/
|
|
891
|
+
│ └── ts/ # TypeScript hook sources by event
|
|
892
|
+
│ ├── PreCompact/
|
|
893
|
+
│ ├── PreToolUse/
|
|
894
|
+
│ ├── PostToolUse/
|
|
895
|
+
│ ├── SessionStart/
|
|
896
|
+
│ ├── Stop/
|
|
897
|
+
│ └── UserPromptSubmit/
|
|
898
|
+
├── mcp/
|
|
899
|
+
│ └── tools/ # Shared tool implementations
|
|
900
|
+
│ ├── memory.ts
|
|
901
|
+
│ ├── observations.ts
|
|
902
|
+
│ ├── projects.ts
|
|
903
|
+
│ ├── registry.ts
|
|
904
|
+
│ ├── sessions.ts
|
|
905
|
+
│ └── zettel.ts
|
|
906
|
+
├── memory/
|
|
907
|
+
│ ├── chunker/ # Text chunking strategies
|
|
908
|
+
│ ├── embeddings.ts # Snowflake Arctic embedding generation
|
|
909
|
+
│ ├── indexer.ts # File indexer with change detection
|
|
910
|
+
│ ├── reranker.ts # Cross-encoder reranking (ms-marco-MiniLM)
|
|
911
|
+
│ ├── search.ts # Multi-mode search (keyword/semantic/hybrid)
|
|
912
|
+
│ └── vault-indexer.ts # Obsidian vault indexing
|
|
913
|
+
├── observations/ # Automatic observation capture
|
|
914
|
+
│ ├── classifier.ts # Rule-based tool call classifier
|
|
915
|
+
│ ├── store.ts # PostgreSQL persistence with deduplication
|
|
916
|
+
│ └── schema.sql # DDL for observation tables
|
|
917
|
+
├── obsidian/ # Obsidian vault bridge
|
|
918
|
+
│ └── vault-fixer.ts # Repairs broken wikilinks and orphans
|
|
919
|
+
├── registry/ # SQLite registry queries and migrations
|
|
920
|
+
├── session/ # Session slug generator
|
|
921
|
+
├── storage/ # Pluggable storage backend
|
|
922
|
+
│ ├── factory.ts # Backend selection (SQLite/PostgreSQL)
|
|
923
|
+
│ ├── interface.ts # StorageInterface contract
|
|
924
|
+
│ ├── postgres.ts # PostgreSQL + pgvector backend
|
|
925
|
+
│ └── sqlite.ts # SQLite backend
|
|
926
|
+
├── utils/ # Shared utilities
|
|
927
|
+
│ ├── hash.ts # SHA-256 hashing
|
|
928
|
+
│ └── stop-words.ts # Stop word lists for search
|
|
929
|
+
├── zettelkasten/ # Luhmann-inspired operations
|
|
930
|
+
│ ├── explore.ts # BFS traversal
|
|
931
|
+
│ ├── surprise.ts # Serendipitous bridge discovery
|
|
932
|
+
│ ├── converse.ts # Hybrid search + graph expansion
|
|
933
|
+
│ ├── themes.ts # Embedding clustering
|
|
934
|
+
│ ├── health.ts # Vault structural audit
|
|
935
|
+
│ └── suggest.ts # Weighted link suggestions
|
|
936
|
+
└── index.ts # Package entry point
|
|
786
937
|
```
|
|
787
938
|
|
|
788
939
|
### Important Notes
|
package/FEATURE.md
CHANGED
|
@@ -28,13 +28,13 @@ 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 —
|
|
31
|
+
| **MCP server** | No | Yes — 9 tools, 18 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
|
-
| **Setup wizard** | No | Yes — idempotent
|
|
37
|
+
| **Setup wizard** | No | Yes — idempotent 14-step interactive wizard |
|
|
38
38
|
| **Hook system** | No | Yes — pre-compact, session-stop, auto-cleanup |
|
|
39
39
|
| **Backup / restore** | No | Yes — timestamped pg_dump + registry export |
|
|
40
40
|
| **Multi-session concurrency** | n/a | Yes — daemon multiplexes Claude sessions |
|
package/README.md
CHANGED
|
@@ -35,6 +35,27 @@ Install PAI and Claude remembers. Ask it what you were working on. Ask it to fin
|
|
|
35
35
|
- "What did I do today?" — daily review across all projects
|
|
36
36
|
- "Journal this thought" — capture freeform reflections with timestamps
|
|
37
37
|
- "Plan my week" — forward-looking priorities based on open TODOs and recent activity
|
|
38
|
+
- "What themes are emerging in my work?" — spot patterns across sessions and projects
|
|
39
|
+
|
|
40
|
+
### Sharing Your Work
|
|
41
|
+
|
|
42
|
+
- "Share on LinkedIn today" — generates a professional post about what you shipped, with real numbers and technical substance
|
|
43
|
+
- "Tweet about the vault migration" — punchy X/Twitter post or thread, with option to post directly
|
|
44
|
+
- "Share on Bluesky this week" — conversational technical post for the Bluesky audience
|
|
45
|
+
- Platform-aware formatting: LinkedIn gets hashtags and narrative, X gets threads and hooks, Bluesky gets conversational tone
|
|
46
|
+
|
|
47
|
+
### Tracking Your Activity
|
|
48
|
+
|
|
49
|
+
- "What changes did I make to the daemon today?" — automatic observation capture tracks every tool call
|
|
50
|
+
- "Show me all decisions from the last session" — observations are classified: decision, bugfix, feature, refactor, discovery, change
|
|
51
|
+
- "What files did I modify in the PAI project this week?" — searchable timeline of every edit, commit, and search
|
|
52
|
+
- "Show observation stats" — totals, breakdowns by type and project, with visual bar charts
|
|
53
|
+
|
|
54
|
+
### Continuing Where You Left Off
|
|
55
|
+
|
|
56
|
+
- "Go" — reads your TODO.md continuation prompt and picks up exactly where the last session stopped
|
|
57
|
+
- "What was I working on?" — progressive context injection loads recent observations at session start
|
|
58
|
+
- "Continue the daemon refactor" — session summaries give Claude full context without re-explaining
|
|
38
59
|
|
|
39
60
|
### Keeping Things Safe
|
|
40
61
|
|
|
@@ -97,7 +118,7 @@ PAI runs hooks at every stage of a Claude Code session:
|
|
|
97
118
|
| **User Prompt** | Cleans up temp files, updates terminal tab titles |
|
|
98
119
|
| **Pre-Compact** | Saves session state checkpoint, sends notification |
|
|
99
120
|
| **Post-Compact** | Injects preserved state back into Claude's context |
|
|
100
|
-
| **Tool Use** |
|
|
121
|
+
| **Tool Use** | Classifies tool calls into structured observations (decision/bugfix/feature/refactor/discovery/change) |
|
|
101
122
|
| **Session End** | Summarizes work done, finalizes session note |
|
|
102
123
|
| **Stop** | Writes work items to session note, sends notification |
|
|
103
124
|
|
|
@@ -105,6 +126,67 @@ All hooks are TypeScript compiled to `.mjs` modules. They run as separate proces
|
|
|
105
126
|
|
|
106
127
|
---
|
|
107
128
|
|
|
129
|
+
## Automatic Observation Capture
|
|
130
|
+
|
|
131
|
+
PAI automatically classifies and stores every significant tool call during your sessions. When you edit a file, run a command, or make a decision, PAI captures it as a structured observation — building a searchable timeline of everything you've done across all projects.
|
|
132
|
+
|
|
133
|
+
### How it works
|
|
134
|
+
|
|
135
|
+
A PostToolUse hook fires after every Claude Code tool call. A rule-based classifier (no AI needed, under 50ms) categorizes each action:
|
|
136
|
+
|
|
137
|
+
| Type | What triggers it | Examples |
|
|
138
|
+
|------|-----------------|----------|
|
|
139
|
+
| **decision** | Git commits, config changes | `git commit`, writing to config files |
|
|
140
|
+
| **bugfix** | Test runs, error investigation | `npm test`, debugging commands |
|
|
141
|
+
| **feature** | New file creation, feature work | Creating components, adding endpoints |
|
|
142
|
+
| **refactor** | Code restructuring | Renaming, moving files, reorganizing |
|
|
143
|
+
| **discovery** | File reads, searches | Reading code, grep searches, glob patterns |
|
|
144
|
+
| **change** | File edits | Editing source files, updating configs |
|
|
145
|
+
|
|
146
|
+
Observations are stored in PostgreSQL with content-hash deduplication (30-second window) to prevent duplicates from rapid tool calls.
|
|
147
|
+
|
|
148
|
+
### Progressive context injection
|
|
149
|
+
|
|
150
|
+
At session start, PAI injects recent observations as layered context:
|
|
151
|
+
|
|
152
|
+
1. **Compact index** (~100 tokens) — observation type counts and active projects
|
|
153
|
+
2. **Timeline** (~500 tokens) — recent observations with timestamps
|
|
154
|
+
3. **On-demand** — full details available via MCP tools
|
|
155
|
+
|
|
156
|
+
This means Claude starts every session already knowing what you were working on, without you re-explaining anything.
|
|
157
|
+
|
|
158
|
+
### Searching observations
|
|
159
|
+
|
|
160
|
+
Ask Claude naturally:
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
"What changes did I make to the daemon today?"
|
|
164
|
+
"Show me all decisions from the last session"
|
|
165
|
+
"What files did I modify in the PAI project this week?"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Or use the CLI:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# List recent observations
|
|
172
|
+
pai observation list
|
|
173
|
+
|
|
174
|
+
# Filter by type
|
|
175
|
+
pai observation list --type decision
|
|
176
|
+
|
|
177
|
+
# Filter by project
|
|
178
|
+
pai observation list --project pai
|
|
179
|
+
|
|
180
|
+
# Show stats
|
|
181
|
+
pai observation stats
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Session summaries
|
|
185
|
+
|
|
186
|
+
When a session ends, PAI generates a structured summary capturing what was requested, investigated, learned, completed, and what the next steps are. These summaries feed into the progressive context system, giving future sessions a concise picture of past work.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
108
190
|
## Auto-Compact Context Window
|
|
109
191
|
|
|
110
192
|
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.
|
|
@@ -342,7 +424,9 @@ External URLs (`https://`, `mailto:`, etc.) are excluded — only relative paths
|
|
|
342
424
|
|
|
343
425
|
PAI works great alongside these tools (also by the same author):
|
|
344
426
|
|
|
427
|
+
- **[AIBroker](https://github.com/mnott/AIBroker)** — Unified message bridge for Claude Code (WhatsApp, Telegram, PAILot — text and voice routing)
|
|
345
428
|
- **[Whazaa](https://github.com/mnott/Whazaa)** — WhatsApp bridge for Claude Code (voice notes, screenshots, session routing)
|
|
429
|
+
- **[Telex](https://github.com/mnott/Telex)** — Telegram bridge for Claude Code (text and voice messaging)
|
|
346
430
|
- **[Coogle](https://github.com/mnott/Coogle)** — Google Workspace MCP daemon (Gmail, Calendar, Drive multiplexing)
|
|
347
431
|
- **[DEVONthink MCP](https://github.com/mnott/devonthink-mcp)** — DEVONthink integration for document search and archival
|
|
348
432
|
|
|
@@ -352,6 +436,8 @@ PAI works great alongside these tools (also by the same author):
|
|
|
352
436
|
|
|
353
437
|
PAI Knowledge OS is inspired by [Daniel Miessler](https://github.com/danielmiessler)'s concept of Personal AI Infrastructure and his [Fabric](https://github.com/danielmiessler/fabric) project — a Python CLI for augmenting human capabilities with reusable AI prompt patterns. Fabric is excellent and solves a different problem; PAI takes the same philosophy in a different direction: persistent memory, session continuity, and deep Claude Code integration. See [FEATURE.md](FEATURE.md) for a detailed comparison.
|
|
354
438
|
|
|
439
|
+
The automatic observation capture system — classifying tool calls into structured observations with progressive context injection — is inspired by [claude-mem](https://github.com/thedotmack/claude-mem) by [thedotmack](https://github.com/thedotmack). claude-mem demonstrated that automatic memory capture during Claude Code sessions dramatically improves continuity. PAI adapts this concept with a rule-based classifier, PostgreSQL storage, and three-layer progressive disclosure.
|
|
440
|
+
|
|
355
441
|
---
|
|
356
442
|
|
|
357
443
|
## License
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as readPaiMarker } from "./pai-marker-CXQPX2P6.mjs";
|
|
2
|
-
import { t as detectProject } from "./detect-
|
|
2
|
+
import { t as detectProject } from "./detect-CdaA48EI.mjs";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import { dirname, resolve } from "node:path";
|
|
5
5
|
|
|
@@ -26,7 +26,7 @@ async function autoRoute(registryDb, federation, cwd, context) {
|
|
|
26
26
|
const markerResult = findMarkerUpward(registryDb, target);
|
|
27
27
|
if (markerResult) return markerResult;
|
|
28
28
|
if (context && context.trim().length > 0) {
|
|
29
|
-
const { detectTopicShift } = await import("./detector-
|
|
29
|
+
const { detectTopicShift } = await import("./detector-jGBuYQJM.mjs").then((n) => n.n);
|
|
30
30
|
const topicResult = await detectTopicShift(registryDb, federation, {
|
|
31
31
|
context,
|
|
32
32
|
threshold: .5
|
|
@@ -83,4 +83,4 @@ function formatAutoRouteJson(result) {
|
|
|
83
83
|
|
|
84
84
|
//#endregion
|
|
85
85
|
export { autoRoute, formatAutoRouteJson };
|
|
86
|
-
//# sourceMappingURL=auto-route-
|
|
86
|
+
//# sourceMappingURL=auto-route-C-DrW6BL.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto-route-
|
|
1
|
+
{"version":3,"file":"auto-route-C-DrW6BL.mjs","names":[],"sources":["../src/session/auto-route.ts"],"sourcesContent":["/**\n * Auto-route: automatic project routing suggestion on session start.\n *\n * Given a working directory (and optional conversation context), determine\n * which registered project the session belongs to.\n *\n * Strategy (in priority order):\n * 1. Path match — exact or parent-directory match in the project registry\n * 2. Marker walk — walk up from cwd looking for Notes/PAI.md, resolve slug\n * 3. Topic match — BM25 keyword search against memory (requires context text)\n *\n * The function is stateless and works with direct DB access (no daemon\n * required), making it fast and safe to call during session startup.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport type { StorageBackend } from \"../storage/interface.js\";\nimport { resolve, dirname } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { readPaiMarker } from \"../registry/pai-marker.js\";\nimport { detectProject } from \"../cli/commands/detect.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type AutoRouteMethod = \"path\" | \"marker\" | \"topic\";\n\nexport interface AutoRouteResult {\n /** Project slug */\n slug: string;\n /** Human-readable project name */\n display_name: string;\n /** Absolute path to the project root */\n root_path: string;\n /** How the project was detected */\n method: AutoRouteMethod;\n /** Confidence [0,1]: 1.0 for path/marker matches, BM25 fraction for topic */\n confidence: number;\n}\n\n// ---------------------------------------------------------------------------\n// Core function\n// ---------------------------------------------------------------------------\n\n/**\n * Determine which project a session should be routed to.\n *\n * @param registryDb Open PAI registry database\n * @param federation Memory storage backend (needed only for topic fallback)\n * @param cwd Working directory to detect from (defaults to process.cwd())\n * @param context Optional conversation text for topic-based fallback\n * @returns Best project match, or null if nothing matched\n */\nexport async function autoRoute(\n registryDb: Database,\n federation: Database | StorageBackend,\n cwd?: string,\n context?: string\n): Promise<AutoRouteResult | null> {\n const target = resolve(cwd ?? process.cwd());\n\n // -------------------------------------------------------------------------\n // Strategy 1: Path match via registry\n // -------------------------------------------------------------------------\n\n const pathMatch = detectProject(registryDb, target);\n\n if (pathMatch) {\n return {\n slug: pathMatch.slug,\n display_name: pathMatch.display_name,\n root_path: pathMatch.root_path,\n method: \"path\",\n confidence: 1.0,\n };\n }\n\n // -------------------------------------------------------------------------\n // Strategy 2: PAI.md marker file walk\n //\n // Walk up from cwd, checking <dir>/Notes/PAI.md at each level.\n // Once found, resolve the slug against the registry to get full project info.\n // -------------------------------------------------------------------------\n\n const markerResult = findMarkerUpward(registryDb, target);\n if (markerResult) {\n return markerResult;\n }\n\n // -------------------------------------------------------------------------\n // Strategy 3: Topic detection (requires context text)\n // -------------------------------------------------------------------------\n\n if (context && context.trim().length > 0) {\n // Lazy import to avoid bundler pulling in daemon/index.mjs at module load time\n const { detectTopicShift } = await import(\"../topics/detector.js\");\n const topicResult = await detectTopicShift(registryDb, federation, {\n context,\n threshold: 0.5, // Lower threshold for initial routing (vs shift detection)\n });\n\n if (topicResult.suggestedProject && topicResult.confidence > 0) {\n // Look up the full project info from the registry\n const projectRow = registryDb\n .prepare(\n \"SELECT slug, display_name, root_path FROM projects WHERE slug = ? AND status != 'archived'\"\n )\n .get(topicResult.suggestedProject) as\n | { slug: string; display_name: string; root_path: string }\n | undefined;\n\n if (projectRow) {\n return {\n slug: projectRow.slug,\n display_name: projectRow.display_name,\n root_path: projectRow.root_path,\n method: \"topic\",\n confidence: topicResult.confidence,\n };\n }\n }\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Marker walk helper\n// ---------------------------------------------------------------------------\n\n/**\n * Walk up the directory tree from `startDir`, checking each level for a\n * `Notes/PAI.md` file. If found, read the slug and look up the project.\n *\n * Stops at the filesystem root or after 20 levels (safety guard).\n */\nfunction findMarkerUpward(\n registryDb: Database,\n startDir: string\n): AutoRouteResult | null {\n let current = startDir;\n let depth = 0;\n\n while (depth < 20) {\n const markerPath = `${current}/Notes/PAI.md`;\n\n if (existsSync(markerPath)) {\n const marker = readPaiMarker(current);\n\n if (marker && marker.status !== \"archived\") {\n // Resolve slug to full project info in the registry\n const projectRow = registryDb\n .prepare(\n \"SELECT slug, display_name, root_path FROM projects WHERE slug = ? AND status != 'archived'\"\n )\n .get(marker.slug) as\n | { slug: string; display_name: string; root_path: string }\n | undefined;\n\n if (projectRow) {\n return {\n slug: projectRow.slug,\n display_name: projectRow.display_name,\n root_path: projectRow.root_path,\n method: \"marker\",\n confidence: 1.0,\n };\n }\n }\n }\n\n const parent = dirname(current);\n if (parent === current) break; // Reached filesystem root\n current = parent;\n depth++;\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Format helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Format an AutoRouteResult as a human-readable string for CLI output.\n */\nexport function formatAutoRoute(result: AutoRouteResult): string {\n const lines: string[] = [\n `slug: ${result.slug}`,\n `display_name: ${result.display_name}`,\n `root_path: ${result.root_path}`,\n `method: ${result.method}`,\n `confidence: ${(result.confidence * 100).toFixed(0)}%`,\n ];\n return lines.join(\"\\n\");\n}\n\n/**\n * Format an AutoRouteResult as JSON for machine consumption.\n */\nexport function formatAutoRouteJson(result: AutoRouteResult): string {\n return JSON.stringify(result, null, 2);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAsDA,eAAsB,UACpB,YACA,YACA,KACA,SACiC;CACjC,MAAM,SAAS,QAAQ,OAAO,QAAQ,KAAK,CAAC;CAM5C,MAAM,YAAY,cAAc,YAAY,OAAO;AAEnD,KAAI,UACF,QAAO;EACL,MAAM,UAAU;EAChB,cAAc,UAAU;EACxB,WAAW,UAAU;EACrB,QAAQ;EACR,YAAY;EACb;CAUH,MAAM,eAAe,iBAAiB,YAAY,OAAO;AACzD,KAAI,aACF,QAAO;AAOT,KAAI,WAAW,QAAQ,MAAM,CAAC,SAAS,GAAG;EAExC,MAAM,EAAE,qBAAqB,MAAM,OAAO;EAC1C,MAAM,cAAc,MAAM,iBAAiB,YAAY,YAAY;GACjE;GACA,WAAW;GACZ,CAAC;AAEF,MAAI,YAAY,oBAAoB,YAAY,aAAa,GAAG;GAE9D,MAAM,aAAa,WAChB,QACC,6FACD,CACA,IAAI,YAAY,iBAAiB;AAIpC,OAAI,WACF,QAAO;IACL,MAAM,WAAW;IACjB,cAAc,WAAW;IACzB,WAAW,WAAW;IACtB,QAAQ;IACR,YAAY,YAAY;IACzB;;;AAKP,QAAO;;;;;;;;AAaT,SAAS,iBACP,YACA,UACwB;CACxB,IAAI,UAAU;CACd,IAAI,QAAQ;AAEZ,QAAO,QAAQ,IAAI;AAGjB,MAAI,WAFe,GAAG,QAAQ,eAEJ,EAAE;GAC1B,MAAM,SAAS,cAAc,QAAQ;AAErC,OAAI,UAAU,OAAO,WAAW,YAAY;IAE1C,MAAM,aAAa,WAChB,QACC,6FACD,CACA,IAAI,OAAO,KAAK;AAInB,QAAI,WACF,QAAO;KACL,MAAM,WAAW;KACjB,cAAc,WAAW;KACzB,WAAW,WAAW;KACtB,QAAQ;KACR,YAAY;KACb;;;EAKP,MAAM,SAAS,QAAQ,QAAQ;AAC/B,MAAI,WAAW,QAAS;AACxB,YAAU;AACV;;AAGF,QAAO;;;;;AAwBT,SAAgB,oBAAoB,QAAiC;AACnE,QAAO,KAAK,UAAU,QAAQ,MAAM,EAAE"}
|