@tekmidian/pai 0.10.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -31
- package/dist/{auto-route-D54Kv7yI.mjs → auto-route-CLYcToZ5.mjs} +4 -4
- package/dist/{auto-route-D54Kv7yI.mjs.map → auto-route-CLYcToZ5.mjs.map} +1 -1
- package/dist/cli/index.mjs +23 -10150
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/program.d.mts +11 -0
- package/dist/cli/program.d.mts.map +1 -0
- package/dist/cli/program.mjs +249 -0
- package/dist/cli/program.mjs.map +1 -0
- package/dist/{clusters-BwmdKB-V.mjs → clusters-BYPw7vfW.mjs} +2 -2
- package/dist/{clusters-BwmdKB-V.mjs.map → clusters-BYPw7vfW.mjs.map} +1 -1
- package/dist/{config-DqBY3aT0.mjs → config-BuhHWyOK.mjs} +1 -1
- package/dist/{config-DqBY3aT0.mjs.map → config-BuhHWyOK.mjs.map} +1 -1
- package/dist/daemon/index.mjs +16 -18
- package/dist/daemon/index.mjs.map +1 -1
- package/dist/{daemon-CSKj8Xm9.mjs → daemon-DDplhnm9.mjs} +185 -31
- package/dist/daemon-DDplhnm9.mjs.map +1 -0
- package/dist/daemon-mcp/index.mjs +2 -2
- package/dist/{db-CYmBWcjh.mjs → db-CmYbAVCD.mjs} +1 -1
- package/dist/{db-CYmBWcjh.mjs.map → db-CmYbAVCD.mjs.map} +1 -1
- package/dist/{detect-CdaA48EI.mjs → detect-KjycLtXM.mjs} +1 -1
- package/dist/{detect-CdaA48EI.mjs.map → detect-KjycLtXM.mjs.map} +1 -1
- package/dist/{detector-Q_lVXtj-.mjs → detector-ZiOhHszd.mjs} +2 -2
- package/dist/{detector-Q_lVXtj-.mjs.map → detector-ZiOhHszd.mjs.map} +1 -1
- package/dist/{embeddings-DGRAPAYb.mjs → embeddings-BJPOcbik.mjs} +1 -1
- package/dist/{embeddings-DGRAPAYb.mjs.map → embeddings-BJPOcbik.mjs.map} +1 -1
- package/dist/{factory-BufouUQ1.mjs → factory-CayQCMoN.mjs} +5 -8
- package/dist/{factory-BufouUQ1.mjs.map → factory-CayQCMoN.mjs.map} +1 -1
- package/dist/{helpers-crDEr6S2.mjs → helpers-IjZkXBhj.mjs} +1 -1
- package/dist/{helpers-crDEr6S2.mjs.map → helpers-IjZkXBhj.mjs.map} +1 -1
- package/dist/hooks/observe.mjs +21 -0
- package/dist/hooks/observe.mjs.map +2 -2
- package/dist/index.mjs +9 -10
- package/dist/{indexer-backend-DcYThufd.mjs → indexer-backend-Cm5RS7y0.mjs} +3 -3
- package/dist/{indexer-backend-DcYThufd.mjs.map → indexer-backend-Cm5RS7y0.mjs.map} +1 -1
- package/dist/{ipc-client-C3pjwy2m.mjs → ipc-client-CoyUHPod.mjs} +1 -1
- package/dist/{ipc-client-C3pjwy2m.mjs.map → ipc-client-CoyUHPod.mjs.map} +1 -1
- package/dist/{kg-entity-DVzTsy6G.mjs → kg-entity-LXblD7LZ.mjs} +1 -1
- package/dist/{kg-entity-DVzTsy6G.mjs.map → kg-entity-LXblD7LZ.mjs.map} +1 -1
- package/dist/{kg-extraction-iR1BKKWK.mjs → kg-extraction-C8DEUHTS.mjs} +17 -6
- package/dist/kg-extraction-C8DEUHTS.mjs.map +1 -0
- package/dist/{latent-ideas-dWVuI_dg.mjs → latent-ideas-RyC6eyqI.mjs} +4 -4
- package/dist/{latent-ideas-dWVuI_dg.mjs.map → latent-ideas-RyC6eyqI.mjs.map} +1 -1
- package/dist/main-resolver-uFxNiDy7.mjs +1339 -0
- package/dist/main-resolver-uFxNiDy7.mjs.map +1 -0
- package/dist/{migrate-fLD6rAdO.mjs → migrate-B02wDDgS.mjs} +2 -2
- package/dist/{migrate-fLD6rAdO.mjs.map → migrate-B02wDDgS.mjs.map} +1 -1
- package/dist/{neighborhood-DixmpfPT.mjs → neighborhood-CklGIB8r.mjs} +2 -2
- package/dist/{neighborhood-DixmpfPT.mjs.map → neighborhood-CklGIB8r.mjs.map} +1 -1
- package/dist/{note-context-BY7rfGdl.mjs → note-context-qvZlVXVO.mjs} +1 -1
- package/dist/{note-context-BY7rfGdl.mjs.map → note-context-qvZlVXVO.mjs.map} +1 -1
- package/dist/{pai-marker-CXQPX2P6.mjs → pai-marker-HVBwwBW-.mjs} +1 -1
- package/dist/{pai-marker-CXQPX2P6.mjs.map → pai-marker-HVBwwBW-.mjs.map} +1 -1
- package/dist/pick-Dt21ojcW.mjs +8250 -0
- package/dist/pick-Dt21ojcW.mjs.map +1 -0
- package/dist/{postgres-Cp25xZes.mjs → postgres-B451Hnkm.mjs} +2 -2
- package/dist/{postgres-Cp25xZes.mjs.map → postgres-B451Hnkm.mjs.map} +1 -1
- package/dist/{query-feedback-C0hKUHVz.mjs → query-feedback-fapbqRgh.mjs} +1 -1
- package/dist/{query-feedback-C0hKUHVz.mjs.map → query-feedback-fapbqRgh.mjs.map} +1 -1
- package/dist/{reranker-CMNZcfVx.mjs → reranker-C08R99zn.mjs} +1 -1
- package/dist/{reranker-CMNZcfVx.mjs.map → reranker-C08R99zn.mjs.map} +1 -1
- package/dist/{search-i2nlQ-JM.mjs → search-HcdKtMla.mjs} +3 -3
- package/dist/{search-i2nlQ-JM.mjs.map → search-HcdKtMla.mjs.map} +1 -1
- package/dist/{sqlite-B3JHfJc2.mjs → sqlite-DQpY1Esi.mjs} +3 -3
- package/dist/{sqlite-B3JHfJc2.mjs.map → sqlite-DQpY1Esi.mjs.map} +1 -1
- package/dist/{state-BHZAhLou.mjs → state-BIlxNRUn.mjs} +1 -1
- package/dist/{state-BHZAhLou.mjs.map → state-BIlxNRUn.mjs.map} +1 -1
- package/dist/{stop-words-BaMEGVeY.mjs → stop-words-BwplsQ3z.mjs} +1 -1
- package/dist/{stop-words-BaMEGVeY.mjs.map → stop-words-BwplsQ3z.mjs.map} +1 -1
- package/dist/{sync-CmBKOL3K.mjs → sync-uPR4g438.mjs} +205 -6
- package/dist/sync-uPR4g438.mjs.map +1 -0
- package/dist/{themes-DO8mUVwD.mjs → themes-Cg8oG9Ra.mjs} +3 -3
- package/dist/{themes-DO8mUVwD.mjs.map → themes-Cg8oG9Ra.mjs.map} +1 -1
- package/dist/{tools-C1JNgya7.mjs → tools-Op3C2Nm6.mjs} +26 -26
- package/dist/{tools-C1JNgya7.mjs.map → tools-Op3C2Nm6.mjs.map} +1 -1
- package/dist/{trace-TYCLMCkY.mjs → trace-CLK-NPkb.mjs} +1 -1
- package/dist/{trace-TYCLMCkY.mjs.map → trace-CLK-NPkb.mjs.map} +1 -1
- package/dist/{utils-BAxjW3j8.mjs → utils-CqqgB0dH.mjs} +1 -1
- package/dist/{utils-BAxjW3j8.mjs.map → utils-CqqgB0dH.mjs.map} +1 -1
- package/dist/{vault-indexer-vNnL3Azb.mjs → vault-indexer-Dn_BccVK.mjs} +2 -2
- package/dist/{vault-indexer-vNnL3Azb.mjs.map → vault-indexer-Dn_BccVK.mjs.map} +1 -1
- package/dist/{work-queue-worker-BxsBtz6j.mjs → work-queue-worker-DHOIVVQ7.mjs} +5 -5
- package/dist/{work-queue-worker-BxsBtz6j.mjs.map → work-queue-worker-DHOIVVQ7.mjs.map} +1 -1
- package/dist/{zettelkasten-B-94vghH.mjs → zettelkasten-NwBD1NsT.mjs} +4 -4
- package/dist/{zettelkasten-B-94vghH.mjs.map → zettelkasten-NwBD1NsT.mjs.map} +1 -1
- package/docs/commands/README.md +151 -0
- package/docs/commands/_examples/kg.md +13 -0
- package/docs/commands/_examples/memory.md +13 -0
- package/docs/commands/_examples/observation.md +13 -0
- package/docs/commands/_examples/projects.md +16 -0
- package/docs/commands/_examples/skill.md +13 -0
- package/docs/commands/_examples/zettel.md +13 -0
- package/docs/commands/backup.md +24 -0
- package/docs/commands/clear-names.md +27 -0
- package/docs/commands/daemon.md +78 -0
- package/docs/commands/db.md +74 -0
- package/docs/commands/end.md +24 -0
- package/docs/commands/help.md +30 -0
- package/docs/commands/kg.md +89 -0
- package/docs/commands/mcp.md +35 -0
- package/docs/commands/memory.md +125 -0
- package/docs/commands/notify.md +83 -0
- package/docs/commands/observation.md +80 -0
- package/docs/commands/obsidian.md +60 -0
- package/docs/commands/pause.md +35 -0
- package/docs/commands/project.md +345 -0
- package/docs/commands/projects.md +364 -0
- package/docs/commands/registry.md +68 -0
- package/docs/commands/restore.md +31 -0
- package/docs/commands/sessions.md +25 -0
- package/docs/commands/setup.md +20 -0
- package/docs/commands/shell-init.md +18 -0
- package/docs/commands/skill.md +54 -0
- package/docs/commands/topic.md +46 -0
- package/docs/commands/update.md +18 -0
- package/docs/commands/zettel.md +151 -0
- package/docs/mcp-skill-guide.md +292 -0
- package/package.json +4 -2
- package/scripts/build-docs.mjs +371 -0
- package/src/hooks/ts/post-tool-use/observe.ts +43 -0
- package/dist/daemon-CSKj8Xm9.mjs.map +0 -1
- package/dist/db-BtuN768f.mjs +0 -206
- package/dist/db-BtuN768f.mjs.map +0 -1
- package/dist/indexer-D7MvSQPY.mjs +0 -1
- package/dist/kg-extraction-iR1BKKWK.mjs.map +0 -1
- package/dist/sync-CmBKOL3K.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# PAI Knowledge OS
|
|
1
|
+
# PAI Knowledge OS
|
|
2
2
|
|
|
3
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
|
|
|
@@ -54,6 +54,34 @@ That's it. Claude Code now has persistent memory across all sessions.
|
|
|
54
54
|
|
|
55
55
|
---
|
|
56
56
|
|
|
57
|
+
## Command Reference
|
|
58
|
+
|
|
59
|
+
Every `pai` command area has its own man page, **generated from the live CLI** so it never drifts from the actual commands. Read them three ways:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pai help # list all command areas (the index)
|
|
63
|
+
pai help memory # the full man page for one area, in your terminal
|
|
64
|
+
pai memory --help # terse Commander help for any command
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Browse the same pages on GitHub under [`docs/commands/`](docs/commands/README.md). Each page lists every subcommand, its arguments and options, and worked examples. The reference below in this README is the *guided tour*; `docs/commands/` is the *complete reference*.
|
|
68
|
+
|
|
69
|
+
| Area | What it covers |
|
|
70
|
+
|------|----------------|
|
|
71
|
+
| [`pai memory`](docs/commands/memory.md) | Federated search, indexing, embeddings |
|
|
72
|
+
| [`pai projects`](docs/commands/projects.md) | Project registry: add, cd, info, health, rebind |
|
|
73
|
+
| [`pai kg`](docs/commands/kg.md) | Temporal knowledge graph |
|
|
74
|
+
| [`pai zettel`](docs/commands/zettel.md) | Zettelkasten intelligence over your vault |
|
|
75
|
+
| [`pai observation`](docs/commands/observation.md) | Automatic tool-call observation capture |
|
|
76
|
+
| [`pai skill`](docs/commands/skill.md) | Skill telemetry (self-educating skill system) |
|
|
77
|
+
| [`pai obsidian`](docs/commands/obsidian.md) | Obsidian vault sync |
|
|
78
|
+
| [`pai daemon`](docs/commands/daemon.md) | Daemon lifecycle |
|
|
79
|
+
| [`pai notify`](docs/commands/notify.md) | Notification configuration |
|
|
80
|
+
| [`pai backup`](docs/commands/backup.md) · [`pai restore`](docs/commands/restore.md) | Data safety |
|
|
81
|
+
| … | See [the full index](docs/commands/README.md) for all areas |
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
57
85
|
## Automatic Session Notes — by Topic
|
|
58
86
|
|
|
59
87
|
PAI's headline feature: **every session is automatically documented.** No manual note-taking, no "pause session" commands, no forgetting to save what you did.
|
|
@@ -299,34 +327,32 @@ All hooks are TypeScript compiled to `.mjs` modules. They run as separate proces
|
|
|
299
327
|
|
|
300
328
|
PAI gives you a complete picture of every Claude Code session running on your machine — live tabs in iTerm2, paused snapshots on disk, and everything in between.
|
|
301
329
|
|
|
302
|
-
### The Core Idea:
|
|
330
|
+
### The Core Idea: One Entry Point
|
|
303
331
|
|
|
304
|
-
|
|
332
|
+
Two ways in, both forgiving:
|
|
305
333
|
|
|
306
|
-
|
|
334
|
+
- **`pai`** (no args) — opens the **interactive picker**: type to search across projects *and* sessions, then act on the highlighted row with a single key.
|
|
335
|
+
- **`pai <name>`** — the universal session command when you already know the name. It does the right thing based on session state:
|
|
336
|
+
- **Live session** — switches the iTerm2 tab to front (no new Claude launched)
|
|
337
|
+
- **Resumable session** — probes `claude --resume`, launches with the saved UUID
|
|
338
|
+
- **Transcript/stub** — starts a fresh Claude in the same project directory
|
|
339
|
+
- **No match** — searches `~/.claude/history.jsonl`, shows a candidate picker
|
|
307
340
|
|
|
308
341
|
```bash
|
|
309
|
-
pai
|
|
310
|
-
pai
|
|
311
|
-
pai
|
|
312
|
-
pai
|
|
342
|
+
pai # Interactive picker — search, then go / new / cd / finder / remove
|
|
343
|
+
pai aibroker # Switch to the live AIBroker tab (iTerm comes to front)
|
|
344
|
+
pai youdrill # Resume the youdrill session, or fresh if snapshot is stale
|
|
345
|
+
pai mdf # Free-text search across your prompt history
|
|
346
|
+
pai 0856d40b # Resume by UUID prefix
|
|
347
|
+
pai --list # Static deduped table (the old no-args behaviour)
|
|
313
348
|
```
|
|
314
349
|
|
|
315
|
-
|
|
316
|
-
1. Is "mdf" a named session in the catalog? → launch immediately, no picker
|
|
317
|
-
2. Is "mdf" a UUID prefix? → direct filesystem resume
|
|
318
|
-
3. Otherwise → grep `~/.claude/history.jsonl` for matching prompts, show candidate list, you pick a number
|
|
319
|
-
|
|
320
|
-
The picked session resolves to the best resumable snapshot in the same project directory. PAI probes `claude --resume` automatically. If the snapshot is stale, it starts a fresh Claude in the same directory.
|
|
321
|
-
|
|
322
|
-
### Daily Verbs
|
|
323
|
-
|
|
324
|
-
The commands you'll use every day:
|
|
350
|
+
### Daily Commands
|
|
325
351
|
|
|
326
352
|
```bash
|
|
327
|
-
pai #
|
|
328
|
-
pai
|
|
329
|
-
pai
|
|
353
|
+
pai # Interactive picker (projects + sessions; search then act)
|
|
354
|
+
pai --list # Static deduped listing (one row per name)
|
|
355
|
+
pai <name> # Switch / resume / fresh — universal
|
|
330
356
|
pai pause # Save state checkpoint (write ## Continue to TODO.md)
|
|
331
357
|
pai pause all # Pause every live Claude session at once
|
|
332
358
|
pai end # Finalize: save state + mark session note Completed
|
|
@@ -339,15 +365,64 @@ And inside Claude Code, the two slash commands that matter:
|
|
|
339
365
|
/end → same as /pause, plus marks the session note Completed
|
|
340
366
|
```
|
|
341
367
|
|
|
342
|
-
###
|
|
368
|
+
### The Interactive Picker
|
|
369
|
+
|
|
370
|
+
Run `pai` with no arguments to open a self-contained terminal selector (no `fzf` or other dependency) over a **unified, deduped list of both projects and sessions** — tagged so the two stay distinct. It's the one place to answer "where did I work on X, and take me there."
|
|
371
|
+
|
|
372
|
+
```
|
|
373
|
+
pai — find a project or session
|
|
374
|
+
search > samba
|
|
375
|
+
|
|
376
|
+
live Chenarlier now …/Raspi/Chenarlier samba setup monster reverse proxy
|
|
377
|
+
project Glidr 2d …/apps/glidr claude pai research
|
|
378
|
+
|
|
379
|
+
────────────────────────────────────────
|
|
380
|
+
Chenarlier /Users/…/Raspi/Chenarlier
|
|
381
|
+
recent notes:
|
|
382
|
+
10 - Samba Setup/01 - Samba Server Setup.md 1mo
|
|
383
|
+
00 - Monster/00 - Monster.md 3mo
|
|
384
|
+
────────────────────────────────────────
|
|
385
|
+
g go to tab · n new · c cd · f finder · d remove · s search · ↑↓ move · q quit
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
**Two modes.** You start in *command mode* (single keys are actions). Press `s` (or `/`) to enter *search mode* (type a topic — it filters by name, path, **and folded-in note file/folder names**, so `samba` finds a project literally named "Chenarlier"); `Enter` or `esc` returns to command mode.
|
|
389
|
+
|
|
390
|
+
**Command keys** act immediately on the highlighted row:
|
|
343
391
|
|
|
344
|
-
|
|
392
|
+
| Key | Action |
|
|
393
|
+
|-----|--------|
|
|
394
|
+
| `g` | **Go to** the running iTerm2 tab (for live rows) |
|
|
395
|
+
| `n` | **New** Claude session in that directory (current terminal) |
|
|
396
|
+
| `c` | **cd** into the folder only — no Claude (your shell stays there) |
|
|
397
|
+
| `f` | Open the folder in **Finder** / Explorer / `xdg-open` (keeps the picker open) |
|
|
398
|
+
| `d` | **Remove** from PAI's list — archives the project (reversible, files untouched); asks `y/N` first |
|
|
399
|
+
| `s` `/` | Enter **search** mode |
|
|
400
|
+
| `↑↓` `j` `k` | Move the highlight |
|
|
401
|
+
| `q` `esc` | Quit |
|
|
345
402
|
|
|
346
|
-
|
|
403
|
+
`Enter` on a row takes the smart default: a live row → go to its tab, otherwise → new session.
|
|
347
404
|
|
|
348
|
-
|
|
405
|
+
The `c` (cd) action needs PAI's shell integration to change your shell's directory — see [Finding the Claude Binary](#finding-the-claude-binary) / `pai shell-init`. On a non-interactive terminal (piped output), `pai` falls back to the static listing automatically.
|
|
349
406
|
|
|
350
|
-
|
|
407
|
+
### Static Listing
|
|
408
|
+
|
|
409
|
+
`pai --list` shows a single deduped table — one row per session name, regardless of how many snapshots exist on disk:
|
|
410
|
+
|
|
411
|
+
```
|
|
412
|
+
Sessions:
|
|
413
|
+
|
|
414
|
+
# name status age project last prompt
|
|
415
|
+
-- ---------- ---------- -------- ---------------------------- --------------------------
|
|
416
|
+
1 AIBroker live now — —
|
|
417
|
+
2 PAI resumable 2m ago /…dev/ai/PAI "refactor session listing…"
|
|
418
|
+
3 MDF transcript 3d ago /…MDF/Infrastruktur/Webseiten "ok so we recently had…"
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
Status values: `live` (active iTerm tab), `resumable` (clean snapshot on disk), `transcript` (history available, not resumable), `stub` (empty or minimal).
|
|
422
|
+
|
|
423
|
+
### Finding Sessions by Topic
|
|
424
|
+
|
|
425
|
+
`pai <topic>` first checks session names, then falls back to searching your prompt history:
|
|
351
426
|
|
|
352
427
|
```
|
|
353
428
|
Sessions matching "mdf":
|
|
@@ -360,16 +435,14 @@ Sessions matching "mdf":
|
|
|
360
435
|
Enter # to launch (1-2), or press Enter to cancel:
|
|
361
436
|
```
|
|
362
437
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
Use `pai <topic> --auto` (or `-y`) to auto-pick #1 without the prompt. Use `pai <topic> 2` to pick #2 directly.
|
|
438
|
+
Use `pai <topic> --auto` (or `-y`) to auto-pick #1. Use `pai <topic> 2` to pick directly.
|
|
366
439
|
|
|
367
440
|
### Power User Access
|
|
368
441
|
|
|
369
|
-
The full session management namespace is still available
|
|
442
|
+
The full session management namespace is still available:
|
|
370
443
|
|
|
371
444
|
```bash
|
|
372
|
-
pai sessions # Live + disk listing (
|
|
445
|
+
pai sessions # Live + disk listing (with more columns)
|
|
373
446
|
pai sessions --all # Include unnamed orphan sessions
|
|
374
447
|
pai sessions --all-tabs # Include shell tabs in the live section
|
|
375
448
|
pai sessions goto <name> # Named-session resolver (same as pai <name>)
|
|
@@ -950,6 +1023,9 @@ External URLs (`https://`, `mailto:`, etc.) are excluded — only relative paths
|
|
|
950
1023
|
| v0.9.17 | Switch live-session listing to `sessions` IPC (metadata-only, faster); `--all-tabs` flag |
|
|
951
1024
|
| v0.9.18 | `pai projects`: moved-project auto-detect, rebind command, active-only default listing |
|
|
952
1025
|
| v0.10.0 | Topic-first redesign: `pai <topic>` universal resolver, history search, sticky tab titles |
|
|
1026
|
+
| v0.10.1 | `pai sessions clear-names` recovery command |
|
|
1027
|
+
| v0.11.0 | Deduped session listing + universal `pai <name>` (switch / resume / fresh) |
|
|
1028
|
+
| v0.12.0 | Interactive picker: `pai` opens a modal search-and-act selector over projects + sessions (g go · n new · c cd · f finder · d remove); note-keyword filtering; quoted exit-dir path |
|
|
953
1029
|
|
|
954
1030
|
---
|
|
955
1031
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { r as readPaiMarker } from "./pai-marker-
|
|
2
|
-
import { t as detectProject } from "./detect-
|
|
1
|
+
import { r as readPaiMarker } from "./pai-marker-HVBwwBW-.mjs";
|
|
2
|
+
import { t as detectProject } from "./detect-KjycLtXM.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-ZiOhHszd.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-CLYcToZ5.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto-route-
|
|
1
|
+
{"version":3,"file":"auto-route-CLYcToZ5.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"}
|