@tekmidian/pai 0.66.1 → 0.66.3

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.
Files changed (185) hide show
  1. package/dist/{aibroker-client-B8c42Lh8.mjs → aibroker-client-Ck3p3k_-.mjs} +2 -2
  2. package/dist/{aibroker-client-C5Fw7DNz.mjs → aibroker-client-WCZi_tn-.mjs} +3 -3
  3. package/dist/{aibroker-client-C5Fw7DNz.mjs.map → aibroker-client-WCZi_tn-.mjs.map} +1 -1
  4. package/dist/{auto-route-o0BOsXn7.mjs → auto-route-BnizyALK.mjs} +65 -4
  5. package/dist/auto-route-BnizyALK.mjs.map +1 -0
  6. package/dist/auto-route-D6fW1Q8z.mjs +3 -0
  7. package/dist/{chain-CZfKIEq9.mjs → chain-Fz4NLm4D.mjs} +3 -4
  8. package/dist/{chain-CZfKIEq9.mjs.map → chain-Fz4NLm4D.mjs.map} +1 -1
  9. package/dist/cli/index.mjs +18 -23
  10. package/dist/cli/index.mjs.map +1 -1
  11. package/dist/cli/program.mjs +17 -22
  12. package/dist/{config-YinjgXEJ.mjs → config-CnsDMETf.mjs} +1 -1
  13. package/dist/{config-BbLFD7Uf.mjs → config-Ddc4DIPk.mjs} +5 -3
  14. package/dist/config-Ddc4DIPk.mjs.map +1 -0
  15. package/dist/{context-handover-cache-pkHzmL3c.mjs → context-handover-cache-DcPEW6vf.mjs} +1221 -13
  16. package/dist/context-handover-cache-DcPEW6vf.mjs.map +1 -0
  17. package/dist/daemon/index.mjs +17 -19
  18. package/dist/daemon/index.mjs.map +1 -1
  19. package/dist/daemon-Bl8TpHdM.mjs +5048 -0
  20. package/dist/daemon-Bl8TpHdM.mjs.map +1 -0
  21. package/dist/daemon-ZhHU6D0i.mjs +18 -0
  22. package/dist/daemon-mcp/index.mjs +12 -9
  23. package/dist/daemon-mcp/index.mjs.map +1 -1
  24. package/dist/{embeddings-CcscYWwk.mjs → embeddings-D0WOQpYR.mjs} +1 -1
  25. package/dist/{embeddings-Bx3q0QOY.mjs → embeddings-DMVzpvRZ.mjs} +18 -9
  26. package/dist/embeddings-DMVzpvRZ.mjs.map +1 -0
  27. package/dist/{env-JNEIrQWg.mjs → env-DiolswKQ.mjs} +1 -1
  28. package/dist/{env-JNEIrQWg.mjs.map → env-DiolswKQ.mjs.map} +1 -1
  29. package/dist/factory-BUnZgnKW.mjs +2892 -0
  30. package/dist/factory-BUnZgnKW.mjs.map +1 -0
  31. package/dist/factory-CrVlo2hY.mjs +8 -0
  32. package/dist/{fallback-CIiq2Raw.mjs → fallback-awOmq_9A.mjs} +23 -11
  33. package/dist/fallback-awOmq_9A.mjs.map +1 -0
  34. package/dist/{chunker-BH4i-F2b.mjs → helpers-CZsi_49C.mjs} +221 -2
  35. package/dist/helpers-CZsi_49C.mjs.map +1 -0
  36. package/dist/hooks/block-sleep-poll.mjs +6 -5
  37. package/dist/hooks/block-sleep-poll.mjs.map +3 -3
  38. package/dist/hooks/context-compression-hook.mjs +1 -0
  39. package/dist/hooks/context-compression-hook.mjs.map +2 -2
  40. package/dist/hooks/load-project-context.mjs +6 -5
  41. package/dist/hooks/load-project-context.mjs.map +3 -3
  42. package/dist/hooks/post-compact-inject.mjs +6 -5
  43. package/dist/hooks/post-compact-inject.mjs.map +3 -3
  44. package/dist/hooks/route-agents-to-worker.mjs +6 -5
  45. package/dist/hooks/route-agents-to-worker.mjs.map +3 -3
  46. package/dist/hooks/security-validator.mjs +1 -0
  47. package/dist/hooks/security-validator.mjs.map +2 -2
  48. package/dist/hooks/whisper-rules.mjs +1 -0
  49. package/dist/hooks/whisper-rules.mjs.map +2 -2
  50. package/dist/hooks/worker-guard.mjs +6 -5
  51. package/dist/hooks/worker-guard.mjs.map +3 -3
  52. package/dist/hooks/worker-proxy.mjs +6 -5
  53. package/dist/hooks/worker-proxy.mjs.map +3 -3
  54. package/dist/hooks/worker-status-line.mjs +6 -5
  55. package/dist/hooks/worker-status-line.mjs.map +3 -3
  56. package/dist/hooks/worker-supervision.mjs +6 -5
  57. package/dist/hooks/worker-supervision.mjs.map +3 -3
  58. package/dist/index.mjs +6 -7
  59. package/dist/{ipc-client-D16Xw6Uo.mjs → ipc-client-DkxRay-o.mjs} +2 -2
  60. package/dist/{ipc-client-D16Xw6Uo.mjs.map → ipc-client-DkxRay-o.mjs.map} +1 -1
  61. package/dist/{main-resolver-DPPwHNtn.mjs → main-resolver-CVGPICy-.mjs} +7 -7
  62. package/dist/{main-resolver-DPPwHNtn.mjs.map → main-resolver-CVGPICy-.mjs.map} +1 -1
  63. package/dist/main-resolver-DDShdDBm.mjs +7 -0
  64. package/dist/{migrate-BD7D8EEh.mjs → migrate-Bq0esMOa.mjs} +2 -2
  65. package/dist/{migrate-BD7D8EEh.mjs.map → migrate-Bq0esMOa.mjs.map} +1 -1
  66. package/dist/{pai-home-Cm9rcJgX.mjs → pai-home-UncxWxlX.mjs} +1 -1
  67. package/dist/{pai-home-Cm9rcJgX.mjs.map → pai-home-UncxWxlX.mjs.map} +1 -1
  68. package/dist/{pai-marker-D1MMswkz.mjs → pai-marker-DXVpFsYz.mjs} +1 -1
  69. package/dist/{pai-marker-D1MMswkz.mjs.map → pai-marker-DXVpFsYz.mjs.map} +1 -1
  70. package/dist/{planner-Dm5Acyo1.mjs → planner-GdiYe2FO.mjs} +10 -7
  71. package/dist/{planner-Dm5Acyo1.mjs.map → planner-GdiYe2FO.mjs.map} +1 -1
  72. package/dist/postgres-DKyiCA5V.mjs +5 -0
  73. package/dist/{postgres-Ceqsa64C.mjs → postgres-H85VSYHI.mjs} +364 -9
  74. package/dist/postgres-H85VSYHI.mjs.map +1 -0
  75. package/dist/{program-JJ0TwSHI.mjs → program-D3MSfx86.mjs} +142 -100
  76. package/dist/program-D3MSfx86.mjs.map +1 -0
  77. package/dist/{query-feedback-BIaZTTFO.mjs → query-feedback-BPdapXKd.mjs} +2 -2
  78. package/dist/{query-feedback-BIaZTTFO.mjs.map → query-feedback-BPdapXKd.mjs.map} +1 -1
  79. package/dist/{reranker-DKv80KO5.mjs → reranker-3lnggwgq.mjs} +1 -1
  80. package/dist/{reranker-DKv80KO5.mjs.map → reranker-3lnggwgq.mjs.map} +1 -1
  81. package/dist/{reranker-CFiEzHQu.mjs → reranker-CZ2mP4cf.mjs} +1 -1
  82. package/dist/router-BK-hFeQ6.mjs +3 -0
  83. package/dist/{router-a5G7q8_3.mjs → router-BaTbc9VX.mjs} +2 -2
  84. package/dist/{router-a5G7q8_3.mjs.map → router-BaTbc9VX.mjs.map} +1 -1
  85. package/dist/{run-BCpbxqA5.mjs → run-D9YbWsnL.mjs} +599 -22
  86. package/dist/run-D9YbWsnL.mjs.map +1 -0
  87. package/dist/{run-env-DRcp7A8K.mjs → run-env-CQRnxNht.mjs} +3 -3
  88. package/dist/{run-env-DRcp7A8K.mjs.map → run-env-CQRnxNht.mjs.map} +1 -1
  89. package/dist/{runtime-paths-CHTg3ywb.mjs → runtime-paths-Bw3Jk8qJ.mjs} +4 -2
  90. package/dist/runtime-paths-Bw3Jk8qJ.mjs.map +1 -0
  91. package/dist/{search-BOSphCJ1.mjs → search-CNAGTiJP.mjs} +2 -2
  92. package/dist/{search-BOSphCJ1.mjs.map → search-CNAGTiJP.mjs.map} +1 -1
  93. package/dist/{sources-CijVso3n.mjs → sources-CM2g-CLT.mjs} +2 -2
  94. package/dist/{sources-CijVso3n.mjs.map → sources-CM2g-CLT.mjs.map} +1 -1
  95. package/dist/{stop-words-BdQuaE9K.mjs → stop-words-DtxaTWU_.mjs} +1 -1
  96. package/dist/{stop-words-BdQuaE9K.mjs.map → stop-words-DtxaTWU_.mjs.map} +1 -1
  97. package/dist/{utils-C9HsYDpO.mjs → utils-DddRwMsG.mjs} +2 -2
  98. package/dist/{utils-C9HsYDpO.mjs.map → utils-DddRwMsG.mjs.map} +1 -1
  99. package/dist/{zettelkasten-CtGHQWPU.mjs → zettelkasten-BRIaHzFm.mjs} +149 -7
  100. package/dist/zettelkasten-BRIaHzFm.mjs.map +1 -0
  101. package/dist/zettelkasten-G0-ZTybr.mjs +5 -0
  102. package/docs/commands/memory.md +2 -0
  103. package/docs/commands/worker.md +2 -0
  104. package/package.json +1 -1
  105. package/dist/async-C2Bm_Lal.mjs +0 -300
  106. package/dist/async-C2Bm_Lal.mjs.map +0 -1
  107. package/dist/auto-route-o0BOsXn7.mjs.map +0 -1
  108. package/dist/chunker-BH4i-F2b.mjs.map +0 -1
  109. package/dist/clusters-BCtD3fbe.mjs +0 -169
  110. package/dist/clusters-BCtD3fbe.mjs.map +0 -1
  111. package/dist/config-BbLFD7Uf.mjs.map +0 -1
  112. package/dist/context-handover-cache-pkHzmL3c.mjs.map +0 -1
  113. package/dist/daemon-DXlaVCkw.mjs +0 -1741
  114. package/dist/daemon-DXlaVCkw.mjs.map +0 -1
  115. package/dist/daemon-DyORmxZu.mjs +0 -19
  116. package/dist/detector-BEPFsINR.mjs +0 -3
  117. package/dist/detector-BRTtAWrM.mjs +0 -65
  118. package/dist/detector-BRTtAWrM.mjs.map +0 -1
  119. package/dist/embeddings-Bx3q0QOY.mjs.map +0 -1
  120. package/dist/factory-CNpPQQ-5.mjs +0 -249
  121. package/dist/factory-CNpPQQ-5.mjs.map +0 -1
  122. package/dist/factory-DY7x73mE.mjs +0 -3
  123. package/dist/fallback-CIiq2Raw.mjs.map +0 -1
  124. package/dist/federation-db-BTyoufBh.mjs +0 -139
  125. package/dist/federation-db-BTyoufBh.mjs.map +0 -1
  126. package/dist/federation-db-HfIFc7FG.mjs +0 -3
  127. package/dist/helpers-BRCJg0G3.mjs +0 -223
  128. package/dist/helpers-BRCJg0G3.mjs.map +0 -1
  129. package/dist/indexer-backend-DFF2FrYx.mjs +0 -5
  130. package/dist/indexer-backend-isSLg6yE.mjs +0 -1
  131. package/dist/kg-entity-DCOcsVFD.mjs +0 -29
  132. package/dist/kg-entity-DCOcsVFD.mjs.map +0 -1
  133. package/dist/latent-ideas-wwAXquGe.mjs +0 -191
  134. package/dist/latent-ideas-wwAXquGe.mjs.map +0 -1
  135. package/dist/link-boost-CnI7UVMJ.mjs +0 -34
  136. package/dist/link-boost-CnI7UVMJ.mjs.map +0 -1
  137. package/dist/main-resolver-BKz_OuEh.mjs +0 -7
  138. package/dist/merge-2gqRPFu2.mjs +0 -3
  139. package/dist/merge-DgU9OgZy.mjs +0 -6
  140. package/dist/merge-DgU9OgZy.mjs.map +0 -1
  141. package/dist/module-paths-DdRzbkUI.mjs +0 -44
  142. package/dist/module-paths-DdRzbkUI.mjs.map +0 -1
  143. package/dist/neighborhood-2FsmzoxG.mjs +0 -114
  144. package/dist/neighborhood-2FsmzoxG.mjs.map +0 -1
  145. package/dist/note-context-De63k8na.mjs +0 -106
  146. package/dist/note-context-De63k8na.mjs.map +0 -1
  147. package/dist/postgres-Ceqsa64C.mjs.map +0 -1
  148. package/dist/program-JJ0TwSHI.mjs.map +0 -1
  149. package/dist/query-feedback-B_iigYj-.mjs +0 -3
  150. package/dist/registry-db-C7voqML9.mjs +0 -213
  151. package/dist/registry-db-C7voqML9.mjs.map +0 -1
  152. package/dist/registry-db-JHPhA8vF.mjs +0 -3
  153. package/dist/registry-postgres-nOfyBSIW.mjs +0 -795
  154. package/dist/registry-postgres-nOfyBSIW.mjs.map +0 -1
  155. package/dist/registry-sqlite-DrCW1aRK.mjs +0 -593
  156. package/dist/registry-sqlite-DrCW1aRK.mjs.map +0 -1
  157. package/dist/router-CXUGsv85.mjs +0 -3
  158. package/dist/run-BCpbxqA5.mjs.map +0 -1
  159. package/dist/runtime-paths-CHTg3ywb.mjs.map +0 -1
  160. package/dist/search-Bf3Kub3F.mjs +0 -3
  161. package/dist/server-DgmAHyFK.mjs +0 -403
  162. package/dist/server-DgmAHyFK.mjs.map +0 -1
  163. package/dist/session-keepalive-DxjTHGcK.mjs +0 -582
  164. package/dist/session-keepalive-DxjTHGcK.mjs.map +0 -1
  165. package/dist/sqlite-CqsTy6xo.mjs +0 -936
  166. package/dist/sqlite-CqsTy6xo.mjs.map +0 -1
  167. package/dist/state-DW8zdweW.mjs +0 -3
  168. package/dist/state-HyjqTihC.mjs +0 -76
  169. package/dist/state-HyjqTihC.mjs.map +0 -1
  170. package/dist/themes-CTaOj3e1.mjs +0 -148
  171. package/dist/themes-CTaOj3e1.mjs.map +0 -1
  172. package/dist/tools-BcOKPBtg.mjs +0 -1489
  173. package/dist/tools-BcOKPBtg.mjs.map +0 -1
  174. package/dist/tools-C-_l0o8Z.mjs +0 -6
  175. package/dist/trace-CtV0RO6n.mjs +0 -137
  176. package/dist/trace-CtV0RO6n.mjs.map +0 -1
  177. package/dist/utils-BrX9FLeK.mjs +0 -3
  178. package/dist/vault-indexer-BG1sEVyM.mjs +0 -537
  179. package/dist/vault-indexer-BG1sEVyM.mjs.map +0 -1
  180. package/dist/wakeup-D8n33hYV.mjs +0 -335
  181. package/dist/wakeup-D8n33hYV.mjs.map +0 -1
  182. package/dist/work-queue-worker-4tLa5gpQ.mjs +0 -567
  183. package/dist/work-queue-worker-4tLa5gpQ.mjs.map +0 -1
  184. package/dist/work-queue-worker-rFZvTO3p.mjs +0 -11
  185. package/dist/zettelkasten-CtGHQWPU.mjs.map +0 -1
@@ -1,335 +0,0 @@
1
- import { i as getRegistryBackend } from "./factory-CNpPQQ-5.mjs";
2
- import { a as paiHomePath, r as migratePaiFile } from "./pai-home-Cm9rcJgX.mjs";
3
- import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
4
- import { homedir } from "node:os";
5
- import { basename, join, resolve } from "node:path";
6
-
7
- //#region src/cli/commands/detect.ts
8
- /**
9
- * Project detection logic for PAI.
10
- *
11
- * detectProject(cwd) — given a filesystem path, returns the best matching
12
- * project from the registry:
13
- * 1. Exact path match
14
- * 2. Longest parent match (project whose root_path is an ancestor of cwd)
15
- *
16
- * Exported for use by the CLI `pai project detect` command and the MCP
17
- * `project_detect` tool.
18
- */
19
- /**
20
- * Detect which registered project a filesystem path belongs to.
21
- *
22
- * @param cwd Absolute path to detect (defaults to process.cwd())
23
- * @returns The best matching project, or null if no match
24
- */
25
- async function detectProject(cwd) {
26
- const target = resolve(cwd ?? process.cwd());
27
- const backend = await getRegistryBackend();
28
- const projects = await backend.listProjectsByPathLengthDesc({ excludeArchived: true });
29
- let matched = null;
30
- let matchType = "exact";
31
- for (const p of projects) {
32
- const root = resolve(p.root_path);
33
- if (target === root) {
34
- matched = p;
35
- matchType = "exact";
36
- break;
37
- }
38
- if (!matched && target.startsWith(root + "/")) {
39
- matched = p;
40
- matchType = "parent";
41
- break;
42
- }
43
- }
44
- if (!matched) return null;
45
- const sessionCount = await backend.countSessionsForProject(matched.id);
46
- const lastDate = await backend.getMostRecentSessionDate(matched.id);
47
- const relative = matchType === "parent" ? target.slice(resolve(matched.root_path).length + 1) : null;
48
- return {
49
- id: matched.id,
50
- slug: matched.slug,
51
- display_name: matched.display_name,
52
- root_path: matched.root_path,
53
- encoded_dir: matched.encoded_dir,
54
- type: matched.type,
55
- status: matched.status,
56
- session_count: sessionCount,
57
- last_session_date: lastDate,
58
- match_type: matchType,
59
- relative_path: relative
60
- };
61
- }
62
- /**
63
- * Format a DetectedProject for human-readable CLI output.
64
- */
65
- function formatDetection(d) {
66
- const lines = [
67
- `slug: ${d.slug}`,
68
- `display_name: ${d.display_name}`,
69
- `root_path: ${d.root_path}`,
70
- `type: ${d.type}`,
71
- `status: ${d.status}`,
72
- `match: ${d.match_type}${d.relative_path ? ` (+${d.relative_path})` : ""}`,
73
- `sessions: ${d.session_count}`
74
- ];
75
- if (d.last_session_date) lines.push(`last_session: ${d.last_session_date}`);
76
- return lines.join("\n");
77
- }
78
- /**
79
- * Format a DetectedProject as JSON for machine consumption.
80
- */
81
- function formatDetectionJson(d) {
82
- return JSON.stringify({
83
- slug: d.slug,
84
- display_name: d.display_name,
85
- root_path: d.root_path,
86
- encoded_dir: d.encoded_dir,
87
- type: d.type,
88
- status: d.status,
89
- match_type: d.match_type,
90
- relative_path: d.relative_path,
91
- session_count: d.session_count,
92
- last_session_date: d.last_session_date
93
- }, null, 2);
94
- }
95
-
96
- //#endregion
97
- //#region src/memory/wakeup.ts
98
- /**
99
- * Wake-up context system — progressive context loading inspired by mempalace.
100
- *
101
- * Layers:
102
- * L0 Identity (~100 tokens) — user identity from ~/.pai/identity.txt. Always loaded.
103
- * L1 Essential Story (~500-800t) — top session notes for the project, key lines extracted.
104
- * L2 On-Demand — triggered by topic queries (handled by memory_search).
105
- * L3 Deep Search — unlimited federated memory search (memory_search tool).
106
- */
107
- /** Maximum tokens for the L1 essential story block. Approx 4 chars/token.
108
- * Measured 2026-09-20: 800 tokens was mostly stale bullets. Reduced to 300.
109
- */
110
- const L1_TOKEN_BUDGET = 300;
111
- L1_TOKEN_BUDGET * 4;
112
- /** Maximum session notes to scan when building L1. Reduced from 10 to 3 (2026-09-20). */
113
- const L1_MAX_NOTES = 3;
114
- /** Sections to extract from session notes (in priority order). */
115
- const EXTRACT_SECTIONS = [
116
- "Work Done",
117
- "Key Decisions",
118
- "Next Steps",
119
- "Checkpoint"
120
- ];
121
- /** Identity file location. */
122
- const IDENTITY_FILE = join(homedir(), ".pai", "identity.txt");
123
- /**
124
- * Load L0 identity. Prefers the PAI_HOME location (identity.txt migrated by
125
- * `pai config migrate`), falling back to the legacy ~/.pai/identity.txt so a
126
- * not-yet-migrated file keeps working. Returns "" if neither exists. Never
127
- * throws.
128
- */
129
- function loadL0Identity(opts = {}) {
130
- const newPath = opts.newPath ?? paiHomePath("identity.txt");
131
- const legacyPath = opts.legacyPath ?? IDENTITY_FILE;
132
- const path = existsSync(newPath) ? newPath : legacyPath;
133
- if (!existsSync(path)) return "";
134
- try {
135
- return readFileSync(path, "utf-8").trim();
136
- } catch {
137
- return "";
138
- }
139
- }
140
- /**
141
- * Move ~/.pai/identity.txt into PAI_HOME, following the same copy /
142
- * verify-byte-identical / rename-aside semantics as every other per-user
143
- * file (see migratePaiFile). `from`/`to` are overridable for tests.
144
- */
145
- function migrateIdentityFile(opts = {}) {
146
- return migratePaiFile(opts.to ?? paiHomePath("identity.txt"), [opts.from ?? IDENTITY_FILE], { dryRun: opts.dryRun });
147
- }
148
- /**
149
- * Find the Notes directory for a project given its root_path from the registry.
150
- * Checks local Notes/ first, then central ~/.claude/projects/... path.
151
- */
152
- function findNotesDirForProject(rootPath) {
153
- const localCandidates = [
154
- join(rootPath, "Notes"),
155
- join(rootPath, "notes"),
156
- join(rootPath, ".claude", "Notes")
157
- ];
158
- for (const p of localCandidates) if (existsSync(p)) return p;
159
- const encoded = rootPath.replace(/\//g, "-").replace(/\./g, "-").replace(/ /g, "-");
160
- const centralNotes = join(homedir(), ".claude", "projects", encoded, "Notes");
161
- if (existsSync(centralNotes)) return centralNotes;
162
- return null;
163
- }
164
- /**
165
- * Recursively find all .md session note files in a Notes directory.
166
- * Handles both flat layout (Notes/*.md) and month-subdirectory layout
167
- * (Notes/YYYY/MM/*.md). Returns files sorted newest-first by filename
168
- * (note numbers are monotonically increasing, so lexicographic = newest-last,
169
- * so we reverse).
170
- */
171
- function findSessionNotes(notesDir) {
172
- const result = [];
173
- const scanDir = (dir) => {
174
- if (!existsSync(dir)) return;
175
- let entries;
176
- try {
177
- entries = readdirSync(dir, { withFileTypes: true }).map((e) => ({
178
- name: e.name,
179
- isDir: e.isDirectory()
180
- }));
181
- } catch {
182
- return;
183
- }
184
- for (const entry of entries) {
185
- const fullPath = join(dir, entry.name);
186
- if (entry.isDir) scanDir(fullPath);
187
- else if (entry.name.match(/^\d{3,4}[\s_-].*\.md$/)) result.push(fullPath);
188
- }
189
- };
190
- scanDir(notesDir);
191
- const dateOf = (p) => basename(p).match(/(\d{4}-\d{2}-\d{2})/)?.[1] ?? "";
192
- const numberOf = (p) => parseInt(basename(p).match(/^(\d+)/)?.[1] ?? "0", 10);
193
- const mtimeOf = (p) => {
194
- try {
195
- return statSync(p).mtimeMs;
196
- } catch {
197
- return 0;
198
- }
199
- };
200
- result.sort((a, b) => {
201
- const dateA = dateOf(a);
202
- const dateB = dateOf(b);
203
- if (dateA !== dateB) {
204
- if (!dateA) return 1;
205
- if (!dateB) return -1;
206
- return dateB.localeCompare(dateA);
207
- }
208
- const byNumber = numberOf(b) - numberOf(a);
209
- if (byNumber !== 0) return byNumber;
210
- return mtimeOf(b) - mtimeOf(a);
211
- });
212
- return result;
213
- }
214
- /**
215
- * Extract the most important lines from a session note.
216
- * Prioritises: Work Done items, Key Decisions, Next Steps, Checkpoint headings.
217
- * Returns a condensed string under maxChars.
218
- */
219
- function normalizeLine(line) {
220
- return line.replace(/^(- \[[ x]\] |- |\* |\d+\.\s)/, "").trim().replace(/\s+/g, " ");
221
- }
222
- function extractKeyLines(content, maxChars, seen = /* @__PURE__ */ new Set(), seenLabels = /* @__PURE__ */ new Set()) {
223
- const lines = content.split("\n");
224
- const selected = [];
225
- let inTargetSection = false;
226
- let currentSection = "";
227
- let charCount = 0;
228
- let pendingLabel = null;
229
- for (const line of lines) {
230
- const h2Match = line.match(/^## (.+)$/);
231
- const h3Match = line.match(/^### (.+)$/);
232
- if (h2Match) {
233
- currentSection = h2Match[1];
234
- inTargetSection = EXTRACT_SECTIONS.some((s) => currentSection.toLowerCase().includes(s.toLowerCase()));
235
- pendingLabel = null;
236
- continue;
237
- }
238
- if (h3Match) {
239
- if (inTargetSection) pendingLabel = h3Match[1];
240
- continue;
241
- }
242
- if (!inTargetSection) continue;
243
- const trimmed = line.trim();
244
- if (!trimmed || trimmed.startsWith("<!--") || trimmed === "---") continue;
245
- if (trimmed.startsWith("- ") || trimmed.startsWith("* ") || trimmed.match(/^\d+\./) || trimmed.startsWith("**")) {
246
- const normalized = normalizeLine(trimmed);
247
- if (seen.has(normalized)) continue;
248
- let labelText = "";
249
- let labelKey = "";
250
- if (pendingLabel !== null) {
251
- labelKey = pendingLabel.toLowerCase().trim();
252
- if (!seenLabels.has(labelKey)) labelText = `[${pendingLabel}]`;
253
- }
254
- if (charCount + labelText.length + trimmed.length + 2 > maxChars) break;
255
- if (labelText) {
256
- selected.push(labelText);
257
- seenLabels.add(labelKey);
258
- charCount += labelText.length + 1;
259
- }
260
- pendingLabel = null;
261
- seen.add(normalized);
262
- selected.push(trimmed);
263
- charCount += trimmed.length + 1;
264
- }
265
- }
266
- return selected.join("\n");
267
- }
268
- /**
269
- * Build the L1 essential story block.
270
- *
271
- * Reads the most recent session notes for the project and extracts the key
272
- * lines (Work Done, Key Decisions, Next Steps) within the token budget.
273
- *
274
- * @param rootPath The project root path (from the registry).
275
- * @param tokenBudget Max tokens to consume. Default 800 (~3200 chars).
276
- * @returns Formatted L1 block, or empty string if no notes found.
277
- */
278
- function buildL1EssentialStory(rootPath, tokenBudget = L1_TOKEN_BUDGET) {
279
- const charBudget = tokenBudget * 4;
280
- const notesDir = findNotesDirForProject(rootPath);
281
- if (!notesDir) return "";
282
- const noteFiles = findSessionNotes(notesDir).slice(0, L1_MAX_NOTES);
283
- if (noteFiles.length === 0) return "";
284
- const sections = [];
285
- const seen = /* @__PURE__ */ new Set();
286
- const seenLabels = /* @__PURE__ */ new Set();
287
- let remaining = charBudget;
288
- for (const noteFile of noteFiles) {
289
- if (remaining <= 50) break;
290
- let content;
291
- try {
292
- content = readFileSync(noteFile, "utf-8");
293
- } catch {
294
- continue;
295
- }
296
- const name = basename(noteFile);
297
- const titleMatch = name.match(/^\d+ - (\d{4}-\d{2}-\d{2}) - (.+)\.md$/);
298
- const dateLabel = titleMatch ? titleMatch[1] : "";
299
- const titleLabel = titleMatch ? titleMatch[2] : name.replace(/^\d+ - /, "").replace(/\.md$/, "");
300
- const perNoteChars = Math.min(remaining, Math.floor(charBudget / noteFiles.length) + 200);
301
- const extracted = extractKeyLines(content, perNoteChars, seen, seenLabels);
302
- const hasContentLine = extracted.split("\n").some((l) => !/^\[.*\]$/.test(l));
303
- if (!extracted || !hasContentLine) continue;
304
- const noteBlock = `[${dateLabel} - ${titleLabel}]\n${extracted}`;
305
- sections.push(noteBlock);
306
- remaining -= noteBlock.length + 1;
307
- }
308
- if (sections.length === 0) return "";
309
- return sections.join("\n\n");
310
- }
311
- /**
312
- * Build the combined wake-up context block (L0 + L1).
313
- *
314
- * Returns a formatted string suitable for injection as a system-reminder,
315
- * or an empty string if both layers are empty.
316
- *
317
- * @param rootPath Project root path for L1 note lookup. Optional.
318
- * @param tokenBudget L1 token budget. Default 800.
319
- * @param opts.skipStory Skip the L1 essential story. Set when a handover
320
- * checkpoint was already injected — the story re-summarises the same note
321
- * the handover came from, at lower quality.
322
- */
323
- function buildWakeupContext(rootPath, tokenBudget = L1_TOKEN_BUDGET, opts = {}) {
324
- const identity = loadL0Identity();
325
- const essentialStory = rootPath && !opts.skipStory ? buildL1EssentialStory(rootPath, tokenBudget) : "";
326
- if (!identity && !essentialStory) return "";
327
- const parts = [];
328
- if (identity) parts.push(`## L0 Identity\n\n${identity}`);
329
- if (essentialStory) parts.push(`## L1 Essential Story\n\n${essentialStory}`);
330
- return parts.join("\n\n");
331
- }
332
-
333
- //#endregion
334
- export { formatDetectionJson as a, formatDetection as i, migrateIdentityFile as n, detectProject as r, buildWakeupContext as t };
335
- //# sourceMappingURL=wakeup-D8n33hYV.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"wakeup-D8n33hYV.mjs","names":[],"sources":["../src/cli/commands/detect.ts","../src/memory/wakeup.ts"],"sourcesContent":["/**\n * Project detection logic for PAI.\n *\n * detectProject(cwd) — given a filesystem path, returns the best matching\n * project from the registry:\n * 1. Exact path match\n * 2. Longest parent match (project whose root_path is an ancestor of cwd)\n *\n * Exported for use by the CLI `pai project detect` command and the MCP\n * `project_detect` tool.\n */\n\nimport { resolve } from \"node:path\";\nimport { getRegistryBackend } from \"../../storage/factory.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface DetectedProject {\n id: number;\n slug: string;\n display_name: string;\n root_path: string;\n encoded_dir: string;\n type: string;\n status: string;\n session_count: number;\n last_session_date: string | null;\n match_type: \"exact\" | \"parent\";\n /** Only set when match_type is 'parent' — the portion of cwd below root_path */\n relative_path: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Core detection function\n// ---------------------------------------------------------------------------\n\n/**\n * Detect which registered project a filesystem path belongs to.\n *\n * @param cwd Absolute path to detect (defaults to process.cwd())\n * @returns The best matching project, or null if no match\n */\nexport async function detectProject(cwd?: string): Promise<DetectedProject | null> {\n const target = resolve(cwd ?? process.cwd());\n const backend = await getRegistryBackend();\n\n // Load all active projects ordered by root_path length descending\n // so the longest (most specific) match wins in a linear scan.\n const projects = await backend.listProjectsByPathLengthDesc({ excludeArchived: true });\n\n let matched: (typeof projects)[number] | null = null;\n let matchType: \"exact\" | \"parent\" = \"exact\";\n\n for (const p of projects) {\n const root = resolve(p.root_path);\n if (target === root) {\n matched = p;\n matchType = \"exact\";\n break;\n }\n if (!matched && target.startsWith(root + \"/\")) {\n matched = p;\n matchType = \"parent\";\n // Keep scanning — a longer root_path match might exist (but shouldn't\n // since we sorted by length desc). Safety break anyway once found.\n break;\n }\n }\n\n if (!matched) return null;\n\n // Enrich with session stats\n const sessionCount = await backend.countSessionsForProject(matched.id);\n const lastDate = await backend.getMostRecentSessionDate(matched.id);\n\n const relative =\n matchType === \"parent\"\n ? target.slice(resolve(matched.root_path).length + 1)\n : null;\n\n return {\n id: matched.id,\n slug: matched.slug,\n display_name: matched.display_name,\n root_path: matched.root_path,\n encoded_dir: matched.encoded_dir,\n type: matched.type,\n status: matched.status,\n session_count: sessionCount,\n last_session_date: lastDate,\n match_type: matchType,\n relative_path: relative,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Format helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Format a DetectedProject for human-readable CLI output.\n */\nexport function formatDetection(d: DetectedProject): string {\n const lines: string[] = [\n `slug: ${d.slug}`,\n `display_name: ${d.display_name}`,\n `root_path: ${d.root_path}`,\n `type: ${d.type}`,\n `status: ${d.status}`,\n `match: ${d.match_type}${d.relative_path ? ` (+${d.relative_path})` : \"\"}`,\n `sessions: ${d.session_count}`,\n ];\n if (d.last_session_date) {\n lines.push(`last_session: ${d.last_session_date}`);\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * Format a DetectedProject as JSON for machine consumption.\n */\nexport function formatDetectionJson(d: DetectedProject): string {\n return JSON.stringify(\n {\n slug: d.slug,\n display_name: d.display_name,\n root_path: d.root_path,\n encoded_dir: d.encoded_dir,\n type: d.type,\n status: d.status,\n match_type: d.match_type,\n relative_path: d.relative_path,\n session_count: d.session_count,\n last_session_date: d.last_session_date,\n },\n null,\n 2\n );\n}\n","/**\n * Wake-up context system — progressive context loading inspired by mempalace.\n *\n * Layers:\n * L0 Identity (~100 tokens) — user identity from ~/.pai/identity.txt. Always loaded.\n * L1 Essential Story (~500-800t) — top session notes for the project, key lines extracted.\n * L2 On-Demand — triggered by topic queries (handled by memory_search).\n * L3 Deep Search — unlimited federated memory search (memory_search tool).\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { paiHomePath, migratePaiFile, type MigrateFileResult } from \"../config/pai-home.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Maximum tokens for the L1 essential story block. Approx 4 chars/token.\n * Measured 2026-09-20: 800 tokens was mostly stale bullets. Reduced to 300.\n */\nconst L1_TOKEN_BUDGET = 300;\nconst L1_CHAR_BUDGET = L1_TOKEN_BUDGET * 4; // ~1600 chars\n\n/** Maximum session notes to scan when building L1. Reduced from 10 to 3 (2026-09-20). */\nconst L1_MAX_NOTES = 3;\n\n/** Sections to extract from session notes (in priority order). */\nconst EXTRACT_SECTIONS = [\n \"Work Done\",\n \"Key Decisions\",\n \"Next Steps\",\n \"Checkpoint\",\n];\n\n/** Identity file location. */\nconst IDENTITY_FILE = join(homedir(), \".pai\", \"identity.txt\");\n\n// ---------------------------------------------------------------------------\n// L0: Identity\n// ---------------------------------------------------------------------------\n\n/**\n * Load L0 identity. Prefers the PAI_HOME location (identity.txt migrated by\n * `pai config migrate`), falling back to the legacy ~/.pai/identity.txt so a\n * not-yet-migrated file keeps working. Returns \"\" if neither exists. Never\n * throws.\n */\nexport function loadL0Identity(\n opts: { newPath?: string; legacyPath?: string } = {}\n): string {\n const newPath = opts.newPath ?? paiHomePath(\"identity.txt\");\n const legacyPath = opts.legacyPath ?? IDENTITY_FILE;\n const path = existsSync(newPath) ? newPath : legacyPath;\n if (!existsSync(path)) return \"\";\n try {\n return readFileSync(path, \"utf-8\").trim();\n } catch {\n return \"\";\n }\n}\n\n/**\n * Move ~/.pai/identity.txt into PAI_HOME, following the same copy /\n * verify-byte-identical / rename-aside semantics as every other per-user\n * file (see migratePaiFile). `from`/`to` are overridable for tests.\n */\nexport function migrateIdentityFile(\n opts: { dryRun?: boolean; from?: string; to?: string } = {}\n): MigrateFileResult {\n const toPath = opts.to ?? paiHomePath(\"identity.txt\");\n const fromPath = opts.from ?? IDENTITY_FILE;\n return migratePaiFile(toPath, [fromPath], { dryRun: opts.dryRun });\n}\n\n// ---------------------------------------------------------------------------\n// L1: Essential Story\n// ---------------------------------------------------------------------------\n\n/**\n * Find the Notes directory for a project given its root_path from the registry.\n * Checks local Notes/ first, then central ~/.claude/projects/... path.\n */\nfunction findNotesDirForProject(rootPath: string): string | null {\n // Check local Notes directories first\n const localCandidates = [\n join(rootPath, \"Notes\"),\n join(rootPath, \"notes\"),\n join(rootPath, \".claude\", \"Notes\"),\n ];\n for (const p of localCandidates) {\n if (existsSync(p)) return p;\n }\n\n // Fall back to central ~/.claude/projects/{encoded}/Notes\n const encoded = rootPath\n .replace(/\\//g, \"-\")\n .replace(/\\./g, \"-\")\n .replace(/ /g, \"-\");\n const centralNotes = join(\n homedir(),\n \".claude\",\n \"projects\",\n encoded,\n \"Notes\"\n );\n if (existsSync(centralNotes)) return centralNotes;\n\n return null;\n}\n\n/**\n * Recursively find all .md session note files in a Notes directory.\n * Handles both flat layout (Notes/*.md) and month-subdirectory layout\n * (Notes/YYYY/MM/*.md). Returns files sorted newest-first by filename\n * (note numbers are monotonically increasing, so lexicographic = newest-last,\n * so we reverse).\n */\nfunction findSessionNotes(notesDir: string): string[] {\n const result: string[] = [];\n\n const scanDir = (dir: string) => {\n if (!existsSync(dir)) return;\n let entries: Array<{ name: string; isDir: boolean }>;\n try {\n entries = readdirSync(dir, { withFileTypes: true })\n .map((e) => ({ name: e.name, isDir: e.isDirectory() }));\n } catch {\n return;\n }\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n if (entry.isDir) {\n // Recurse into YYYY/MM subdirectories\n scanDir(fullPath);\n } else if (entry.name.match(/^\\d{3,4}[\\s_-].*\\.md$/)) {\n result.push(fullPath);\n }\n }\n };\n\n scanDir(notesDir);\n\n // Sort newest-first by the DATE in the filename, not the note number.\n //\n // The note number is only monotonic within one uninterrupted numbering run.\n // It restarts — per month directory, and after a registry merge renumbers a\n // project — so \"highest number\" and \"most recent\" diverge. Observed live:\n // `0184 - 2026-02-22` outranked `0008 - 2026-08-01`, and the wake-up context\n // handed a resumed session five-month-old material as its recent history.\n //\n // `NNNN - YYYY-MM-DD - Title.md` is the enforced layout, so the date is the\n // reliable key. Number breaks ties within a day; mtime is the last resort\n // for notes that predate the naming convention.\n const dateOf = (p: string): string =>\n basename(p).match(/(\\d{4}-\\d{2}-\\d{2})/)?.[1] ?? \"\";\n const numberOf = (p: string): number =>\n parseInt(basename(p).match(/^(\\d+)/)?.[1] ?? \"0\", 10);\n const mtimeOf = (p: string): number => {\n try {\n return statSync(p).mtimeMs;\n } catch {\n return 0;\n }\n };\n\n result.sort((a, b) => {\n const dateA = dateOf(a);\n const dateB = dateOf(b);\n // Undated notes sort last rather than winning on an empty string.\n if (dateA !== dateB) {\n if (!dateA) return 1;\n if (!dateB) return -1;\n return dateB.localeCompare(dateA);\n }\n const byNumber = numberOf(b) - numberOf(a);\n if (byNumber !== 0) return byNumber;\n return mtimeOf(b) - mtimeOf(a);\n });\n\n return result;\n}\n\n/**\n * Extract the most important lines from a session note.\n * Prioritises: Work Done items, Key Decisions, Next Steps, Checkpoint headings.\n * Returns a condensed string under maxChars.\n */\nfunction normalizeLine(line: string): string {\n return line\n .replace(/^(- \\[[ x]\\] |- |\\* |\\d+\\.\\s)/, \"\")\n .trim()\n .replace(/\\s+/g, \" \");\n}\n\nfunction extractKeyLines(\n content: string,\n maxChars: number,\n seen: Set<string> = new Set(),\n seenLabels: Set<string> = new Set()\n): string {\n const lines = content.split(\"\\n\");\n const selected: string[] = [];\n let inTargetSection = false;\n let currentSection = \"\";\n let charCount = 0;\n // Heading text seen since the last line was emitted. Buffered rather than\n // pushed immediately: a heading with no surviving lines under it (every\n // line deduped against an earlier note) must not appear at all.\n let pendingLabel: string | null = null;\n\n // First pass: collect lines from priority sections\n for (const line of lines) {\n // Detect section headers\n const h2Match = line.match(/^## (.+)$/);\n const h3Match = line.match(/^### (.+)$/);\n if (h2Match) {\n currentSection = h2Match[1];\n inTargetSection = EXTRACT_SECTIONS.some((s) =>\n currentSection.toLowerCase().includes(s.toLowerCase())\n );\n pendingLabel = null;\n continue;\n }\n if (h3Match) {\n // Checkpoints / sub-sections — buffer as a candidate label.\n if (inTargetSection) {\n pendingLabel = h3Match[1];\n }\n continue;\n }\n\n if (!inTargetSection) continue;\n\n // Skip blank lines and HTML comments\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"<!--\") || trimmed === \"---\") continue;\n\n // Include checkbox items, bold text, and plain text lines\n if (\n trimmed.startsWith(\"- \") ||\n trimmed.startsWith(\"* \") ||\n trimmed.match(/^\\d+\\./) ||\n trimmed.startsWith(\"**\")\n ) {\n const normalized = normalizeLine(trimmed);\n if (seen.has(normalized)) continue;\n\n let labelText = \"\";\n let labelKey = \"\";\n if (pendingLabel !== null) {\n labelKey = pendingLabel.toLowerCase().trim();\n if (!seenLabels.has(labelKey)) {\n labelText = `[${pendingLabel}]`;\n }\n }\n\n if (charCount + labelText.length + trimmed.length + 2 > maxChars) break;\n\n if (labelText) {\n selected.push(labelText);\n seenLabels.add(labelKey);\n charCount += labelText.length + 1;\n }\n pendingLabel = null;\n\n seen.add(normalized);\n selected.push(trimmed);\n charCount += trimmed.length + 1;\n }\n }\n\n return selected.join(\"\\n\");\n}\n\n/**\n * Build the L1 essential story block.\n *\n * Reads the most recent session notes for the project and extracts the key\n * lines (Work Done, Key Decisions, Next Steps) within the token budget.\n *\n * @param rootPath The project root path (from the registry).\n * @param tokenBudget Max tokens to consume. Default 800 (~3200 chars).\n * @returns Formatted L1 block, or empty string if no notes found.\n */\nexport function buildL1EssentialStory(\n rootPath: string,\n tokenBudget = L1_TOKEN_BUDGET\n): string {\n const charBudget = tokenBudget * 4;\n const notesDir = findNotesDirForProject(rootPath);\n if (!notesDir) return \"\";\n\n const noteFiles = findSessionNotes(notesDir).slice(0, L1_MAX_NOTES);\n if (noteFiles.length === 0) return \"\";\n\n const sections: string[] = [];\n const seen = new Set<string>();\n const seenLabels = new Set<string>();\n let remaining = charBudget;\n\n for (const noteFile of noteFiles) {\n if (remaining <= 50) break;\n\n let content: string;\n try {\n content = readFileSync(noteFile, \"utf-8\");\n } catch {\n continue;\n }\n\n // Extract the note date and title from the filename\n const name = basename(noteFile);\n const titleMatch = name.match(/^\\d+ - (\\d{4}-\\d{2}-\\d{2}) - (.+)\\.md$/);\n const dateLabel = titleMatch ? titleMatch[1] : \"\";\n const titleLabel = titleMatch\n ? titleMatch[2]\n : name.replace(/^\\d+ - /, \"\").replace(/\\.md$/, \"\");\n\n // Skip if nothing useful extracted from this note\n const perNoteChars = Math.min(remaining, Math.floor(charBudget / noteFiles.length) + 200);\n const extracted = extractKeyLines(content, perNoteChars, seen, seenLabels);\n // A note whose every extracted line survived only as a label (all its\n // real lines deduped against an earlier, newer note) contributes nothing\n // — including its title.\n const hasContentLine = extracted\n .split(\"\\n\")\n .some((l) => !/^\\[.*\\]$/.test(l));\n if (!extracted || !hasContentLine) continue;\n\n const noteBlock = `[${dateLabel} - ${titleLabel}]\\n${extracted}`;\n sections.push(noteBlock);\n remaining -= noteBlock.length + 1;\n }\n\n if (sections.length === 0) return \"\";\n\n return sections.join(\"\\n\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Combined: buildWakeupContext\n// ---------------------------------------------------------------------------\n\n/**\n * Build the combined wake-up context block (L0 + L1).\n *\n * Returns a formatted string suitable for injection as a system-reminder,\n * or an empty string if both layers are empty.\n *\n * @param rootPath Project root path for L1 note lookup. Optional.\n * @param tokenBudget L1 token budget. Default 800.\n * @param opts.skipStory Skip the L1 essential story. Set when a handover\n * checkpoint was already injected — the story re-summarises the same note\n * the handover came from, at lower quality.\n */\nexport function buildWakeupContext(\n rootPath?: string,\n tokenBudget = L1_TOKEN_BUDGET,\n opts: { skipStory?: boolean } = {}\n): string {\n const identity = loadL0Identity();\n const essentialStory = rootPath && !opts.skipStory\n ? buildL1EssentialStory(rootPath, tokenBudget)\n : \"\";\n\n if (!identity && !essentialStory) return \"\";\n\n const parts: string[] = [];\n\n if (identity) {\n parts.push(`## L0 Identity\\n\\n${identity}`);\n }\n\n if (essentialStory) {\n parts.push(`## L1 Essential Story\\n\\n${essentialStory}`);\n }\n\n return parts.join(\"\\n\\n\");\n}\n"],"mappings":"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"}