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