@tekmidian/pai 0.5.7 → 0.6.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/ARCHITECTURE.md +72 -1
- 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
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/hooks/ts/pre-compact/context-compression-hook.ts
|
|
4
|
-
import { existsSync as
|
|
5
|
-
import { basename as
|
|
4
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
5
|
+
import { basename as basename3, dirname, join as join6 } from "path";
|
|
6
6
|
import { tmpdir } from "os";
|
|
7
7
|
|
|
8
|
-
// src/hooks/ts/lib/project-utils.ts
|
|
9
|
-
import { existsSync as existsSync2, mkdirSync, readdirSync,
|
|
8
|
+
// src/hooks/ts/lib/project-utils/paths.ts
|
|
9
|
+
import { existsSync as existsSync2, mkdirSync, readdirSync, renameSync } from "fs";
|
|
10
10
|
import { join as join2, basename } from "path";
|
|
11
|
-
import { homedir as homedir2 } from "os";
|
|
12
11
|
|
|
13
12
|
// src/hooks/ts/lib/pai-paths.ts
|
|
14
13
|
import { homedir } from "os";
|
|
@@ -68,7 +67,8 @@ function validatePAIStructure() {
|
|
|
68
67
|
}
|
|
69
68
|
validatePAIStructure();
|
|
70
69
|
|
|
71
|
-
// src/hooks/ts/lib/project-utils.ts
|
|
70
|
+
// src/hooks/ts/lib/project-utils/paths.ts
|
|
71
|
+
var PROJECTS_DIR = join2(PAI_DIR, "projects");
|
|
72
72
|
var PROBE_CWD_PATTERNS = [
|
|
73
73
|
"/CodexBar/ClaudeProbe",
|
|
74
74
|
"/ClaudeProbe"
|
|
@@ -77,7 +77,6 @@ function isProbeSession(cwd) {
|
|
|
77
77
|
const dir = cwd || process.cwd();
|
|
78
78
|
return PROBE_CWD_PATTERNS.some((pattern) => dir.includes(pattern));
|
|
79
79
|
}
|
|
80
|
-
var PROJECTS_DIR = join2(PAI_DIR, "projects");
|
|
81
80
|
function encodePath(path) {
|
|
82
81
|
return path.replace(/\//g, "-").replace(/\./g, "-").replace(/ /g, "-");
|
|
83
82
|
}
|
|
@@ -105,10 +104,27 @@ function findNotesDir(cwd) {
|
|
|
105
104
|
}
|
|
106
105
|
return { path: getNotesDir(cwd), isLocal: false };
|
|
107
106
|
}
|
|
107
|
+
function findTodoPath(cwd) {
|
|
108
|
+
const localPaths = [
|
|
109
|
+
join2(cwd, "TODO.md"),
|
|
110
|
+
join2(cwd, "notes", "TODO.md"),
|
|
111
|
+
join2(cwd, "Notes", "TODO.md"),
|
|
112
|
+
join2(cwd, ".claude", "TODO.md")
|
|
113
|
+
];
|
|
114
|
+
for (const path of localPaths) {
|
|
115
|
+
if (existsSync2(path)) return path;
|
|
116
|
+
}
|
|
117
|
+
return join2(getNotesDir(cwd), "TODO.md");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// src/hooks/ts/lib/project-utils/notify.ts
|
|
121
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
122
|
+
import { join as join3 } from "path";
|
|
123
|
+
import { homedir as homedir2 } from "os";
|
|
108
124
|
function isWhatsAppEnabled() {
|
|
109
125
|
try {
|
|
110
|
-
const settingsPath =
|
|
111
|
-
if (!
|
|
126
|
+
const settingsPath = join3(homedir2(), ".claude", "settings.json");
|
|
127
|
+
if (!existsSync3(settingsPath)) return false;
|
|
112
128
|
const settings = JSON.parse(readFileSync2(settingsPath, "utf-8"));
|
|
113
129
|
const enabled = settings.enabledMcpjsonServers || [];
|
|
114
130
|
return enabled.includes("aibroker") || enabled.includes("whazaa") || enabled.includes("telex");
|
|
@@ -152,22 +168,24 @@ async function sendNtfyNotification(message, retries = 2) {
|
|
|
152
168
|
console.error("ntfy.sh notification failed after all retries");
|
|
153
169
|
return false;
|
|
154
170
|
}
|
|
171
|
+
|
|
172
|
+
// src/hooks/ts/lib/project-utils/session-notes.ts
|
|
173
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync3, writeFileSync, renameSync as renameSync2 } from "fs";
|
|
174
|
+
import { join as join4, basename as basename2 } from "path";
|
|
155
175
|
function getMonthDir(notesDir) {
|
|
156
176
|
const now = /* @__PURE__ */ new Date();
|
|
157
177
|
const year = String(now.getFullYear());
|
|
158
178
|
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
159
|
-
const monthDir =
|
|
160
|
-
if (!
|
|
161
|
-
|
|
179
|
+
const monthDir = join4(notesDir, year, month);
|
|
180
|
+
if (!existsSync4(monthDir)) {
|
|
181
|
+
mkdirSync2(monthDir, { recursive: true });
|
|
162
182
|
}
|
|
163
183
|
return monthDir;
|
|
164
184
|
}
|
|
165
185
|
function getNextNoteNumber(notesDir) {
|
|
166
186
|
const monthDir = getMonthDir(notesDir);
|
|
167
|
-
const files =
|
|
168
|
-
if (files.length === 0)
|
|
169
|
-
return "0001";
|
|
170
|
-
}
|
|
187
|
+
const files = readdirSync2(monthDir).filter((f) => f.match(/^\d{3,4}[\s_-]/)).filter((f) => f.endsWith(".md")).sort();
|
|
188
|
+
if (files.length === 0) return "0001";
|
|
171
189
|
let maxNumber = 0;
|
|
172
190
|
for (const file of files) {
|
|
173
191
|
const digitMatch = file.match(/^(\d+)/);
|
|
@@ -179,29 +197,27 @@ function getNextNoteNumber(notesDir) {
|
|
|
179
197
|
return String(maxNumber + 1).padStart(4, "0");
|
|
180
198
|
}
|
|
181
199
|
function getCurrentNotePath(notesDir) {
|
|
182
|
-
if (!
|
|
183
|
-
return null;
|
|
184
|
-
}
|
|
200
|
+
if (!existsSync4(notesDir)) return null;
|
|
185
201
|
const findLatestIn = (dir) => {
|
|
186
|
-
if (!
|
|
187
|
-
const files =
|
|
202
|
+
if (!existsSync4(dir)) return null;
|
|
203
|
+
const files = readdirSync2(dir).filter((f) => f.match(/^\d{3,4}[\s_-].*\.md$/)).sort((a, b) => {
|
|
188
204
|
const numA = parseInt(a.match(/^(\d+)/)?.[1] || "0", 10);
|
|
189
205
|
const numB = parseInt(b.match(/^(\d+)/)?.[1] || "0", 10);
|
|
190
206
|
return numA - numB;
|
|
191
207
|
});
|
|
192
208
|
if (files.length === 0) return null;
|
|
193
|
-
return
|
|
209
|
+
return join4(dir, files[files.length - 1]);
|
|
194
210
|
};
|
|
195
211
|
const now = /* @__PURE__ */ new Date();
|
|
196
212
|
const year = String(now.getFullYear());
|
|
197
213
|
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
198
|
-
const currentMonthDir =
|
|
214
|
+
const currentMonthDir = join4(notesDir, year, month);
|
|
199
215
|
const found = findLatestIn(currentMonthDir);
|
|
200
216
|
if (found) return found;
|
|
201
217
|
const prevDate = new Date(now.getFullYear(), now.getMonth() - 1, 1);
|
|
202
218
|
const prevYear = String(prevDate.getFullYear());
|
|
203
219
|
const prevMonth = String(prevDate.getMonth() + 1).padStart(2, "0");
|
|
204
|
-
const prevMonthDir =
|
|
220
|
+
const prevMonthDir = join4(notesDir, prevYear, prevMonth);
|
|
205
221
|
const prevFound = findLatestIn(prevMonthDir);
|
|
206
222
|
if (prevFound) return prevFound;
|
|
207
223
|
return findLatestIn(notesDir);
|
|
@@ -209,10 +225,9 @@ function getCurrentNotePath(notesDir) {
|
|
|
209
225
|
function createSessionNote(notesDir, description) {
|
|
210
226
|
const noteNumber = getNextNoteNumber(notesDir);
|
|
211
227
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
212
|
-
const safeDescription = "New Session";
|
|
213
228
|
const monthDir = getMonthDir(notesDir);
|
|
214
|
-
const filename = `${noteNumber} - ${date} -
|
|
215
|
-
const filepath =
|
|
229
|
+
const filename = `${noteNumber} - ${date} - New Session.md`;
|
|
230
|
+
const filepath = join4(monthDir, filename);
|
|
216
231
|
const content = `# Session ${noteNumber}: ${description}
|
|
217
232
|
|
|
218
233
|
**Date:** ${date}
|
|
@@ -239,14 +254,12 @@ function createSessionNote(notesDir, description) {
|
|
|
239
254
|
return filepath;
|
|
240
255
|
}
|
|
241
256
|
function appendCheckpoint(notePath, checkpoint) {
|
|
242
|
-
if (!
|
|
257
|
+
if (!existsSync4(notePath)) {
|
|
243
258
|
console.error(`Note file not found, recreating: ${notePath}`);
|
|
244
259
|
try {
|
|
245
|
-
const parentDir =
|
|
246
|
-
if (!
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
const noteFilename = basename(notePath);
|
|
260
|
+
const parentDir = join4(notePath, "..");
|
|
261
|
+
if (!existsSync4(parentDir)) mkdirSync2(parentDir, { recursive: true });
|
|
262
|
+
const noteFilename = basename2(notePath);
|
|
250
263
|
const numberMatch = noteFilename.match(/^(\d+)/);
|
|
251
264
|
const noteNumber = numberMatch ? numberMatch[1] : "0000";
|
|
252
265
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -278,7 +291,7 @@ function appendCheckpoint(notePath, checkpoint) {
|
|
|
278
291
|
return;
|
|
279
292
|
}
|
|
280
293
|
}
|
|
281
|
-
const content =
|
|
294
|
+
const content = readFileSync3(notePath, "utf-8");
|
|
282
295
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
283
296
|
const checkpointText = `
|
|
284
297
|
### Checkpoint ${timestamp}
|
|
@@ -286,28 +299,21 @@ function appendCheckpoint(notePath, checkpoint) {
|
|
|
286
299
|
${checkpoint}
|
|
287
300
|
`;
|
|
288
301
|
const nextStepsIndex = content.indexOf("## Next Steps");
|
|
289
|
-
|
|
290
|
-
if (nextStepsIndex !== -1) {
|
|
291
|
-
newContent = content.substring(0, nextStepsIndex) + checkpointText + content.substring(nextStepsIndex);
|
|
292
|
-
} else {
|
|
293
|
-
newContent = content + checkpointText;
|
|
294
|
-
}
|
|
302
|
+
const newContent = nextStepsIndex !== -1 ? content.substring(0, nextStepsIndex) + checkpointText + content.substring(nextStepsIndex) : content + checkpointText;
|
|
295
303
|
writeFileSync(notePath, newContent);
|
|
296
|
-
console.error(`Checkpoint added to: ${
|
|
304
|
+
console.error(`Checkpoint added to: ${basename2(notePath)}`);
|
|
297
305
|
}
|
|
298
306
|
function addWorkToSessionNote(notePath, workItems, sectionTitle) {
|
|
299
|
-
if (!
|
|
307
|
+
if (!existsSync4(notePath)) {
|
|
300
308
|
console.error(`Note file not found: ${notePath}`);
|
|
301
309
|
return;
|
|
302
310
|
}
|
|
303
|
-
let content =
|
|
311
|
+
let content = readFileSync3(notePath, "utf-8");
|
|
304
312
|
let workText = "";
|
|
305
|
-
if (sectionTitle)
|
|
306
|
-
workText += `
|
|
313
|
+
if (sectionTitle) workText += `
|
|
307
314
|
### ${sectionTitle}
|
|
308
315
|
|
|
309
316
|
`;
|
|
310
|
-
}
|
|
311
317
|
for (const item of workItems) {
|
|
312
318
|
const checkbox = item.completed !== false ? "[x]" : "[ ]";
|
|
313
319
|
workText += `- ${checkbox} **${item.title}**
|
|
@@ -330,30 +336,24 @@ function addWorkToSessionNote(notePath, workItems, sectionTitle) {
|
|
|
330
336
|
}
|
|
331
337
|
}
|
|
332
338
|
writeFileSync(notePath, content);
|
|
333
|
-
console.error(`Added ${workItems.length} work item(s) to: ${
|
|
339
|
+
console.error(`Added ${workItems.length} work item(s) to: ${basename2(notePath)}`);
|
|
334
340
|
}
|
|
335
341
|
function renameSessionNote(notePath, meaningfulName) {
|
|
336
|
-
if (!meaningfulName || !
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
const dir = join2(notePath, "..");
|
|
340
|
-
const oldFilename = basename(notePath);
|
|
342
|
+
if (!meaningfulName || !existsSync4(notePath)) return notePath;
|
|
343
|
+
const dir = join4(notePath, "..");
|
|
344
|
+
const oldFilename = basename2(notePath);
|
|
341
345
|
const correctMatch = oldFilename.match(/^(\d{3,4}) - (\d{4}-\d{2}-\d{2}) - .*\.md$/);
|
|
342
346
|
const legacyMatch = oldFilename.match(/^(\d{3,4})_(\d{4}-\d{2}-\d{2})_.*\.md$/);
|
|
343
347
|
const match = correctMatch || legacyMatch;
|
|
344
|
-
if (!match)
|
|
345
|
-
return notePath;
|
|
346
|
-
}
|
|
348
|
+
if (!match) return notePath;
|
|
347
349
|
const [, noteNumber, date] = match;
|
|
348
350
|
const titleCaseName = meaningfulName.split(/[\s_-]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ").trim();
|
|
349
351
|
const paddedNumber = noteNumber.padStart(4, "0");
|
|
350
352
|
const newFilename = `${paddedNumber} - ${date} - ${titleCaseName}.md`;
|
|
351
|
-
const newPath =
|
|
352
|
-
if (newFilename === oldFilename)
|
|
353
|
-
return notePath;
|
|
354
|
-
}
|
|
353
|
+
const newPath = join4(dir, newFilename);
|
|
354
|
+
if (newFilename === oldFilename) return notePath;
|
|
355
355
|
try {
|
|
356
|
-
|
|
356
|
+
renameSync2(notePath, newPath);
|
|
357
357
|
console.error(`Renamed note: ${oldFilename} \u2192 ${newFilename}`);
|
|
358
358
|
return newPath;
|
|
359
359
|
} catch (error) {
|
|
@@ -361,12 +361,13 @@ function renameSessionNote(notePath, meaningfulName) {
|
|
|
361
361
|
return notePath;
|
|
362
362
|
}
|
|
363
363
|
}
|
|
364
|
+
|
|
365
|
+
// src/hooks/ts/lib/project-utils/tokens.ts
|
|
366
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
364
367
|
function calculateSessionTokens(jsonlPath) {
|
|
365
|
-
if (!
|
|
366
|
-
return 0;
|
|
367
|
-
}
|
|
368
|
+
if (!existsSync5(jsonlPath)) return 0;
|
|
368
369
|
try {
|
|
369
|
-
const content =
|
|
370
|
+
const content = readFileSync4(jsonlPath, "utf-8");
|
|
370
371
|
const lines = content.trim().split("\n");
|
|
371
372
|
let totalTokens = 0;
|
|
372
373
|
for (const line of lines) {
|
|
@@ -388,27 +389,15 @@ function calculateSessionTokens(jsonlPath) {
|
|
|
388
389
|
return 0;
|
|
389
390
|
}
|
|
390
391
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
join2(cwd, "Notes", "TODO.md"),
|
|
396
|
-
join2(cwd, ".claude", "TODO.md")
|
|
397
|
-
];
|
|
398
|
-
for (const path of localPaths) {
|
|
399
|
-
if (existsSync2(path)) {
|
|
400
|
-
return path;
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
return join2(getNotesDir(cwd), "TODO.md");
|
|
404
|
-
}
|
|
392
|
+
|
|
393
|
+
// src/hooks/ts/lib/project-utils/todo.ts
|
|
394
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
395
|
+
import { join as join5 } from "path";
|
|
405
396
|
function ensureTodoMd(cwd) {
|
|
406
397
|
const todoPath = findTodoPath(cwd);
|
|
407
|
-
if (!
|
|
408
|
-
const parentDir =
|
|
409
|
-
if (!
|
|
410
|
-
mkdirSync(parentDir, { recursive: true });
|
|
411
|
-
}
|
|
398
|
+
if (!existsSync6(todoPath)) {
|
|
399
|
+
const parentDir = join5(todoPath, "..");
|
|
400
|
+
if (!existsSync6(parentDir)) mkdirSync3(parentDir, { recursive: true });
|
|
412
401
|
const content = `# TODO
|
|
413
402
|
|
|
414
403
|
## Current Session
|
|
@@ -423,14 +412,14 @@ function ensureTodoMd(cwd) {
|
|
|
423
412
|
|
|
424
413
|
*Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}*
|
|
425
414
|
`;
|
|
426
|
-
|
|
415
|
+
writeFileSync2(todoPath, content);
|
|
427
416
|
console.error(`Created TODO.md: ${todoPath}`);
|
|
428
417
|
}
|
|
429
418
|
return todoPath;
|
|
430
419
|
}
|
|
431
420
|
function updateTodoContinue(cwd, noteFilename, state, tokenDisplay) {
|
|
432
421
|
const todoPath = ensureTodoMd(cwd);
|
|
433
|
-
let content =
|
|
422
|
+
let content = readFileSync5(todoPath, "utf-8");
|
|
434
423
|
content = content.replace(/## Continue\n[\s\S]*?\n---\n+/, "");
|
|
435
424
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
436
425
|
const stateLines = state ? state.split("\n").filter((l) => l.trim()).slice(0, 10).map((l) => `> ${l}`).join("\n") : `> Working directory: ${cwd}. Check the latest session note for details.`;
|
|
@@ -458,7 +447,7 @@ ${stateLines}
|
|
|
458
447
|
|
|
459
448
|
*Last updated: ${now}*
|
|
460
449
|
`;
|
|
461
|
-
|
|
450
|
+
writeFileSync2(todoPath, content);
|
|
462
451
|
console.error("TODO.md ## Continue section updated");
|
|
463
452
|
}
|
|
464
453
|
|
|
@@ -477,7 +466,7 @@ function contentToText(content) {
|
|
|
477
466
|
}
|
|
478
467
|
function getTranscriptStats(transcriptPath) {
|
|
479
468
|
try {
|
|
480
|
-
const content =
|
|
469
|
+
const content = readFileSync6(transcriptPath, "utf-8");
|
|
481
470
|
const lines = content.trim().split("\n");
|
|
482
471
|
let userMessages = 0;
|
|
483
472
|
let assistantMessages = 0;
|
|
@@ -506,7 +495,7 @@ function parseTranscript(transcriptPath) {
|
|
|
506
495
|
workItems: []
|
|
507
496
|
};
|
|
508
497
|
try {
|
|
509
|
-
const raw =
|
|
498
|
+
const raw = readFileSync6(transcriptPath, "utf-8");
|
|
510
499
|
const lines = raw.trim().split("\n");
|
|
511
500
|
const seenSummaries = /* @__PURE__ */ new Set();
|
|
512
501
|
for (const line of lines) {
|
|
@@ -623,7 +612,7 @@ function deriveTitle(data) {
|
|
|
623
612
|
}
|
|
624
613
|
if (!title && data.filesModified.length > 0) {
|
|
625
614
|
const basenames = data.filesModified.slice(-5).map((f) => {
|
|
626
|
-
const b =
|
|
615
|
+
const b = basename3(f);
|
|
627
616
|
return b.replace(/\.[^.]+$/, "");
|
|
628
617
|
});
|
|
629
618
|
const unique = [...new Set(basenames)];
|
|
@@ -634,9 +623,9 @@ function deriveTitle(data) {
|
|
|
634
623
|
var CUMULATIVE_STATE_FILE = ".compact-state.json";
|
|
635
624
|
function loadCumulativeState(notesDir) {
|
|
636
625
|
try {
|
|
637
|
-
const filePath =
|
|
638
|
-
if (!
|
|
639
|
-
const raw = JSON.parse(
|
|
626
|
+
const filePath = join6(notesDir, CUMULATIVE_STATE_FILE);
|
|
627
|
+
if (!existsSync7(filePath)) return null;
|
|
628
|
+
const raw = JSON.parse(readFileSync6(filePath, "utf-8"));
|
|
640
629
|
return {
|
|
641
630
|
userMessages: raw.userMessages || [],
|
|
642
631
|
summaries: raw.summaries || [],
|
|
@@ -668,8 +657,8 @@ function mergeTranscriptData(accumulated, current) {
|
|
|
668
657
|
}
|
|
669
658
|
function saveCumulativeState(notesDir, data, notePath) {
|
|
670
659
|
try {
|
|
671
|
-
const filePath =
|
|
672
|
-
|
|
660
|
+
const filePath = join6(notesDir, CUMULATIVE_STATE_FILE);
|
|
661
|
+
writeFileSync3(filePath, JSON.stringify({
|
|
673
662
|
...data,
|
|
674
663
|
notePath,
|
|
675
664
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -710,9 +699,9 @@ async function main() {
|
|
|
710
699
|
const data = parseTranscript(hookInput.transcript_path);
|
|
711
700
|
let notesInfo;
|
|
712
701
|
try {
|
|
713
|
-
notesInfo = hookInput.cwd ? findNotesDir(hookInput.cwd) : { path:
|
|
702
|
+
notesInfo = hookInput.cwd ? findNotesDir(hookInput.cwd) : { path: join6(dirname(hookInput.transcript_path), "Notes"), isLocal: false };
|
|
714
703
|
} catch {
|
|
715
|
-
notesInfo = { path:
|
|
704
|
+
notesInfo = { path: join6(dirname(hookInput.transcript_path), "Notes"), isLocal: false };
|
|
716
705
|
}
|
|
717
706
|
const accumulated = loadCumulativeState(notesInfo.path);
|
|
718
707
|
const merged = mergeTranscriptData(accumulated, data);
|
|
@@ -728,9 +717,9 @@ async function main() {
|
|
|
728
717
|
notePath = createSessionNote(notesInfo.path, "Recovered Session");
|
|
729
718
|
} else {
|
|
730
719
|
try {
|
|
731
|
-
const noteContent =
|
|
720
|
+
const noteContent = readFileSync6(notePath, "utf-8");
|
|
732
721
|
if (noteContent.includes("**Status:** Completed") || noteContent.includes("**Completed:**")) {
|
|
733
|
-
console.error(`Latest note is completed (${
|
|
722
|
+
console.error(`Latest note is completed (${basename3(notePath)}) \u2014 creating new one`);
|
|
734
723
|
notePath = createSessionNote(notesInfo.path, "Continued Session");
|
|
735
724
|
}
|
|
736
725
|
} catch {
|
|
@@ -749,26 +738,26 @@ ${state}` : `Context compression triggered at ~${tokenDisplay} tokens with ${sta
|
|
|
749
738
|
const newPath = renameSessionNote(notePath, title);
|
|
750
739
|
if (newPath !== notePath) {
|
|
751
740
|
try {
|
|
752
|
-
let noteContent =
|
|
741
|
+
let noteContent = readFileSync6(newPath, "utf-8");
|
|
753
742
|
noteContent = noteContent.replace(
|
|
754
743
|
/^(# Session \d+:)\s*.*$/m,
|
|
755
744
|
`$1 ${title}`
|
|
756
745
|
);
|
|
757
|
-
|
|
746
|
+
writeFileSync3(newPath, noteContent);
|
|
758
747
|
console.error(`Updated note H1 to match rename`);
|
|
759
748
|
} catch {
|
|
760
749
|
}
|
|
761
750
|
notePath = newPath;
|
|
762
751
|
}
|
|
763
752
|
}
|
|
764
|
-
console.error(`Rich checkpoint saved: ${
|
|
753
|
+
console.error(`Rich checkpoint saved: ${basename3(notePath)}`);
|
|
765
754
|
} catch (noteError) {
|
|
766
755
|
console.error(`Could not save checkpoint: ${noteError}`);
|
|
767
756
|
}
|
|
768
757
|
saveCumulativeState(notesInfo.path, merged, notePath);
|
|
769
758
|
if (hookInput.cwd && notePath) {
|
|
770
759
|
try {
|
|
771
|
-
const noteFilename =
|
|
760
|
+
const noteFilename = basename3(notePath);
|
|
772
761
|
updateTodoContinue(hookInput.cwd, noteFilename, state, tokenDisplay);
|
|
773
762
|
console.error("TODO.md ## Continue section updated");
|
|
774
763
|
} catch (todoError) {
|
|
@@ -795,8 +784,8 @@ rename it based on actual work done and add a rich summary.` : "";
|
|
|
795
784
|
"</system-reminder>"
|
|
796
785
|
].join("\n");
|
|
797
786
|
try {
|
|
798
|
-
const stateFile =
|
|
799
|
-
|
|
787
|
+
const stateFile = join6(tmpdir(), `pai-compact-state-${hookInput.session_id}.txt`);
|
|
788
|
+
writeFileSync3(stateFile, injection, "utf-8");
|
|
800
789
|
console.error(`Session state saved to ${stateFile} (${injection.length} chars)`);
|
|
801
790
|
} catch (err) {
|
|
802
791
|
console.error(`Failed to save state file: ${err}`);
|