@tekmidian/pai 0.14.2 → 0.16.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/dist/{auto-route-CLYcToZ5.mjs → auto-route-CdvjEWs5.mjs} +4 -4
- package/dist/{auto-route-CLYcToZ5.mjs.map → auto-route-CdvjEWs5.mjs.map} +1 -1
- package/dist/{kg-extraction-C8DEUHTS.mjs → checkpoint-block-Bl0HD3U2.mjs} +442 -13
- package/dist/checkpoint-block-Bl0HD3U2.mjs.map +1 -0
- package/dist/cli/index.mjs +14 -12
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/program.d.mts.map +1 -1
- package/dist/cli/program.mjs +31 -16
- package/dist/cli/program.mjs.map +1 -1
- package/dist/{clusters-BYPw7vfW.mjs → clusters-CKWDwcMX.mjs} +2 -2
- package/dist/{clusters-BYPw7vfW.mjs.map → clusters-CKWDwcMX.mjs.map} +1 -1
- package/dist/{config-C8m-tPhP.mjs → config-DGOfoGfm.mjs} +1 -1
- package/dist/config-DGOfoGfm.mjs.map +1 -0
- package/dist/daemon/index.mjs +18 -16
- package/dist/daemon/index.mjs.map +1 -1
- package/dist/{daemon-COSvHTNu.mjs → daemon-1rKbN-fd.mjs} +31 -30
- package/dist/{daemon-COSvHTNu.mjs.map → daemon-1rKbN-fd.mjs.map} +1 -1
- package/dist/daemon-mcp/index.mjs +24 -2
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/db-BtuN768f.mjs +206 -0
- package/dist/db-BtuN768f.mjs.map +1 -0
- package/dist/{db-CmYbAVCD.mjs → db-CYmBWcjh.mjs} +1 -1
- package/dist/{db-CmYbAVCD.mjs.map → db-CYmBWcjh.mjs.map} +1 -1
- package/dist/{detect-KjycLtXM.mjs → detect-Bf2z-oKB.mjs} +1 -1
- package/dist/{detect-KjycLtXM.mjs.map → detect-Bf2z-oKB.mjs.map} +1 -1
- package/dist/{detector-ZiOhHszd.mjs → detector-rq9JGTmS.mjs} +2 -2
- package/dist/{detector-ZiOhHszd.mjs.map → detector-rq9JGTmS.mjs.map} +1 -1
- package/dist/{embeddings-BJPOcbik.mjs → embeddings-DGRAPAYb.mjs} +1 -1
- package/dist/{embeddings-BJPOcbik.mjs.map → embeddings-DGRAPAYb.mjs.map} +1 -1
- package/dist/{factory-Q88X1bAN.mjs → factory-HcQsQqh0.mjs} +8 -5
- package/dist/{factory-Q88X1bAN.mjs.map → factory-HcQsQqh0.mjs.map} +1 -1
- package/dist/{helpers-IjZkXBhj.mjs → helpers-crDEr6S2.mjs} +1 -1
- package/dist/{helpers-IjZkXBhj.mjs.map → helpers-crDEr6S2.mjs.map} +1 -1
- package/dist/hooks/capture-all-events.mjs.map +1 -1
- package/dist/hooks/capture-session-summary.mjs.map +1 -1
- package/dist/hooks/capture-tool-output.mjs.map +1 -1
- package/dist/hooks/cleanup-session-files.mjs.map +1 -1
- package/dist/hooks/context-compression-hook.mjs +304 -48
- package/dist/hooks/context-compression-hook.mjs.map +4 -4
- package/dist/hooks/initialize-session.mjs.map +1 -1
- package/dist/hooks/inject-observations.mjs.map +1 -1
- package/dist/hooks/load-core-context.mjs.map +1 -1
- package/dist/hooks/load-project-context.mjs.map +1 -1
- package/dist/hooks/observe.mjs.map +1 -1
- package/dist/hooks/post-compact-inject.mjs.map +1 -1
- package/dist/hooks/security-validator.mjs.map +1 -1
- package/dist/hooks/session-commands.mjs.map +1 -1
- package/dist/hooks/stop-hook.mjs +299 -43
- package/dist/hooks/stop-hook.mjs.map +4 -4
- package/dist/hooks/subagent-stop-hook.mjs.map +1 -1
- package/dist/hooks/sync-todo-to-md.mjs.map +1 -1
- package/dist/hooks/update-tab-on-action.mjs.map +1 -1
- package/dist/hooks/update-tab-titles.mjs.map +1 -1
- package/dist/hooks/whisper-rules.mjs.map +1 -1
- package/dist/index.d.mts +0 -9
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +10 -9
- package/dist/indexer-AEcT8wHf.mjs +1 -0
- package/dist/{indexer-backend-Cm5RS7y0.mjs → indexer-backend-BQZXoarl.mjs} +3 -3
- package/dist/{indexer-backend-Cm5RS7y0.mjs.map → indexer-backend-BQZXoarl.mjs.map} +1 -1
- package/dist/{ipc-client-CoyUHPod.mjs → ipc-client-vO2GV325.mjs} +1 -1
- package/dist/{ipc-client-CoyUHPod.mjs.map → ipc-client-vO2GV325.mjs.map} +1 -1
- package/dist/{kg-entity-LXblD7LZ.mjs → kg-entity-DbOMPdF9.mjs} +1 -1
- package/dist/{kg-entity-LXblD7LZ.mjs.map → kg-entity-DbOMPdF9.mjs.map} +1 -1
- package/dist/{latent-ideas-RyC6eyqI.mjs → latent-ideas-C6IFFT0-.mjs} +4 -4
- package/dist/{latent-ideas-RyC6eyqI.mjs.map → latent-ideas-C6IFFT0-.mjs.map} +1 -1
- package/dist/{main-resolver-uFxNiDy7.mjs → main-resolver-DimnS2qP.mjs} +22 -22
- package/dist/{main-resolver-uFxNiDy7.mjs.map → main-resolver-DimnS2qP.mjs.map} +1 -1
- package/dist/{migrate-B02wDDgS.mjs → migrate-fLD6rAdO.mjs} +2 -2
- package/dist/{migrate-B02wDDgS.mjs.map → migrate-fLD6rAdO.mjs.map} +1 -1
- package/dist/{neighborhood-CklGIB8r.mjs → neighborhood-CzRNG2Oy.mjs} +2 -2
- package/dist/{neighborhood-CklGIB8r.mjs.map → neighborhood-CzRNG2Oy.mjs.map} +1 -1
- package/dist/{note-context-qvZlVXVO.mjs → note-context-CrfMbr4R.mjs} +1 -1
- package/dist/{note-context-qvZlVXVO.mjs.map → note-context-CrfMbr4R.mjs.map} +1 -1
- package/dist/{pai-marker-HVBwwBW-.mjs → pai-marker-B20KqhA8.mjs} +44 -2
- package/dist/pai-marker-B20KqhA8.mjs.map +1 -0
- package/dist/{pick-gne3kOJG.mjs → pick-B3zEYjin.mjs} +2316 -483
- package/dist/pick-B3zEYjin.mjs.map +1 -0
- package/dist/{postgres-B451Hnkm.mjs → postgres-zjKqPl4X.mjs} +2 -2
- package/dist/{postgres-B451Hnkm.mjs.map → postgres-zjKqPl4X.mjs.map} +1 -1
- package/dist/{query-feedback-fapbqRgh.mjs → query-feedback-BoY8_Dbb.mjs} +1 -1
- package/dist/{query-feedback-fapbqRgh.mjs.map → query-feedback-BoY8_Dbb.mjs.map} +1 -1
- package/dist/{reranker-C08R99zn.mjs → reranker-CMNZcfVx.mjs} +1 -1
- package/dist/{reranker-C08R99zn.mjs.map → reranker-CMNZcfVx.mjs.map} +1 -1
- package/dist/{search-HcdKtMla.mjs → search-i2nlQ-JM.mjs} +3 -3
- package/dist/{search-HcdKtMla.mjs.map → search-i2nlQ-JM.mjs.map} +1 -1
- package/dist/skills/End/SKILL.md +78 -0
- package/dist/skills/Pause/SKILL.md +77 -0
- package/dist/skills/Tasks/SKILL.md +22 -0
- package/dist/{sqlite-DQpY1Esi.mjs → sqlite-ChSCYwiC.mjs} +3 -3
- package/dist/{sqlite-DQpY1Esi.mjs.map → sqlite-ChSCYwiC.mjs.map} +1 -1
- package/dist/{state-BIlxNRUn.mjs → state-BXIdbxDs.mjs} +1 -1
- package/dist/{state-BIlxNRUn.mjs.map → state-BXIdbxDs.mjs.map} +1 -1
- package/dist/{stop-words-BwplsQ3z.mjs → stop-words-BaMEGVeY.mjs} +1 -1
- package/dist/{stop-words-BwplsQ3z.mjs.map → stop-words-BaMEGVeY.mjs.map} +1 -1
- package/dist/{sync-uPR4g438.mjs → sync-CmBKOL3K.mjs} +6 -205
- package/dist/sync-CmBKOL3K.mjs.map +1 -0
- package/dist/{themes-Cg8oG9Ra.mjs → themes-nvRM8v84.mjs} +3 -3
- package/dist/{themes-Cg8oG9Ra.mjs.map → themes-nvRM8v84.mjs.map} +1 -1
- package/dist/{tools-Op3C2Nm6.mjs → tools-CznjzfYz.mjs} +26 -26
- package/dist/{tools-Op3C2Nm6.mjs.map → tools-CznjzfYz.mjs.map} +1 -1
- package/dist/{trace-CLK-NPkb.mjs → trace-B8oz1ok0.mjs} +1 -1
- package/dist/{trace-CLK-NPkb.mjs.map → trace-B8oz1ok0.mjs.map} +1 -1
- package/dist/{utils-CqqgB0dH.mjs → utils-BAxjW3j8.mjs} +1 -1
- package/dist/{utils-CqqgB0dH.mjs.map → utils-BAxjW3j8.mjs.map} +1 -1
- package/dist/{vault-indexer-Dn_BccVK.mjs → vault-indexer-DrttVeEk.mjs} +2 -2
- package/dist/{vault-indexer-Dn_BccVK.mjs.map → vault-indexer-DrttVeEk.mjs.map} +1 -1
- package/dist/{work-queue-worker-BwouhVkb.mjs → work-queue-worker-BPXLkEik.mjs} +47 -30
- package/dist/work-queue-worker-BPXLkEik.mjs.map +1 -0
- package/dist/{zettelkasten-NwBD1NsT.mjs → zettelkasten-CTlt1dGv.mjs} +4 -4
- package/dist/{zettelkasten-NwBD1NsT.mjs.map → zettelkasten-CTlt1dGv.mjs.map} +1 -1
- package/docs/commands/README.md +17 -0
- package/docs/commands/backup.md +1 -1
- package/docs/commands/clear-names.md +1 -1
- package/docs/commands/daemon.md +1 -1
- package/docs/commands/db.md +1 -1
- package/docs/commands/end.md +4 -1
- package/docs/commands/help.md +1 -1
- package/docs/commands/kg.md +1 -1
- package/docs/commands/mcp.md +1 -1
- package/docs/commands/memory.md +1 -1
- package/docs/commands/notify.md +1 -1
- package/docs/commands/observation.md +1 -1
- package/docs/commands/obsidian.md +1 -1
- package/docs/commands/pause.md +4 -1
- package/docs/commands/project.md +1 -1
- package/docs/commands/projects.md +1 -1
- package/docs/commands/registry.md +18 -1
- package/docs/commands/restore.md +1 -1
- package/docs/commands/session.md +271 -0
- package/docs/commands/sessions.md +1 -1
- package/docs/commands/setup.md +1 -1
- package/docs/commands/shell-init.md +1 -1
- package/docs/commands/skill.md +1 -1
- package/docs/commands/task.md +2 -1
- package/docs/commands/topic.md +1 -1
- package/docs/commands/update.md +1 -1
- package/docs/commands/zettel.md +1 -1
- package/package.json +1 -1
- package/scripts/build-hooks.mjs +32 -3
- package/src/hooks/ts/lib/project-utils/todo.test.ts +132 -0
- package/src/hooks/ts/lib/project-utils/todo.ts +60 -34
- package/dist/config-C8m-tPhP.mjs.map +0 -1
- package/dist/kg-extraction-C8DEUHTS.mjs.map +0 -1
- package/dist/pai-marker-HVBwwBW-.mjs.map +0 -1
- package/dist/pick-gne3kOJG.mjs.map +0 -1
- package/dist/sync-uPR4g438.mjs.map +0 -1
- package/dist/work-queue-worker-BwouhVkb.mjs.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
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 basename3, dirname, join as
|
|
4
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
5
|
+
import { basename as basename3, dirname, join as join7 } from "path";
|
|
6
6
|
import { tmpdir } from "os";
|
|
7
7
|
import { connect } from "net";
|
|
8
8
|
import { randomUUID } from "crypto";
|
|
@@ -420,13 +420,260 @@ function calculateSessionTokens(jsonlPath) {
|
|
|
420
420
|
}
|
|
421
421
|
|
|
422
422
|
// src/hooks/ts/lib/project-utils/todo.ts
|
|
423
|
-
import { existsSync as
|
|
424
|
-
import { join as
|
|
423
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
424
|
+
import { join as join6 } from "path";
|
|
425
|
+
|
|
426
|
+
// src/session/checkpoint-block.ts
|
|
427
|
+
import {
|
|
428
|
+
existsSync as existsSync6,
|
|
429
|
+
readFileSync as readFileSync5,
|
|
430
|
+
writeFileSync as writeFileSync2,
|
|
431
|
+
readdirSync as readdirSync3,
|
|
432
|
+
renameSync as renameSync3,
|
|
433
|
+
mkdirSync as mkdirSync3,
|
|
434
|
+
realpathSync
|
|
435
|
+
} from "node:fs";
|
|
436
|
+
import { join as join5 } from "node:path";
|
|
437
|
+
var TODO_LOCATIONS = [
|
|
438
|
+
"Notes/TODO.md",
|
|
439
|
+
".claude/Notes/TODO.md",
|
|
440
|
+
"tasks/todo.md",
|
|
441
|
+
"TODO.md"
|
|
442
|
+
];
|
|
443
|
+
var MARKER_OPEN = "<!-- pai:checkpoint";
|
|
444
|
+
var MARKER_CLOSE = "<!-- /pai:checkpoint -->";
|
|
445
|
+
var CONTINUE_HEADING = "## Continue";
|
|
446
|
+
function findProjectTodo(rootPath) {
|
|
447
|
+
for (const rel of TODO_LOCATIONS) {
|
|
448
|
+
const full = join5(rootPath, rel);
|
|
449
|
+
if (existsSync6(full)) {
|
|
450
|
+
try {
|
|
451
|
+
return { path: full, content: readFileSync5(full, "utf8") };
|
|
452
|
+
} catch {
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
function resolveTodoTarget(rootPath, opts = {}) {
|
|
459
|
+
const found = findProjectTodo(rootPath);
|
|
460
|
+
if (found) return found;
|
|
461
|
+
const notesDir = join5(rootPath, "Notes");
|
|
462
|
+
if (opts.create !== false) {
|
|
463
|
+
try {
|
|
464
|
+
if (!existsSync6(notesDir)) mkdirSync3(notesDir, { recursive: true });
|
|
465
|
+
} catch {
|
|
466
|
+
return null;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return { path: join5(notesDir, "TODO.md"), content: "" };
|
|
470
|
+
}
|
|
471
|
+
function parseMarker(line) {
|
|
472
|
+
const trimmed = line.trim();
|
|
473
|
+
if (!trimmed.startsWith(MARKER_OPEN)) return null;
|
|
474
|
+
const attrs = {};
|
|
475
|
+
for (const m of trimmed.matchAll(/([a-zA-Z][\w-]*)="([^"]*)"/g)) {
|
|
476
|
+
attrs[m[1]] = m[2];
|
|
477
|
+
}
|
|
478
|
+
return {
|
|
479
|
+
authored: attrs.authored === "model" ? "model" : "auto",
|
|
480
|
+
session: attrs.session || void 0,
|
|
481
|
+
sessionId: attrs["session-id"] || void 0,
|
|
482
|
+
ts: attrs.ts || void 0
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
var BOILERPLATE_PATTERNS = [
|
|
486
|
+
/^##\s+Continue$/,
|
|
487
|
+
/^<!--\s*\/?pai:checkpoint/,
|
|
488
|
+
/^>\s*\*\*Last session:\*\*/,
|
|
489
|
+
/^>\s*\*\*Paused at:\*\*/,
|
|
490
|
+
/^>\s*Working directory:/,
|
|
491
|
+
/^>\s*Resume with:/,
|
|
492
|
+
/^>\s*_No checkpoint body was recorded/,
|
|
493
|
+
/^>?\s*$/,
|
|
494
|
+
/^-{3,}$/
|
|
495
|
+
];
|
|
496
|
+
function isBoilerplateOnly(lines) {
|
|
497
|
+
return lines.every((line) => {
|
|
498
|
+
const t = line.trim();
|
|
499
|
+
return BOILERPLATE_PATTERNS.some((re) => re.test(t));
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
function extractSectionContent(lines) {
|
|
503
|
+
const kept = [];
|
|
504
|
+
for (const line of lines) {
|
|
505
|
+
const t = line.trim();
|
|
506
|
+
if (BOILERPLATE_PATTERNS.some((re) => re.test(t))) continue;
|
|
507
|
+
kept.push(line);
|
|
508
|
+
}
|
|
509
|
+
return kept.join("\n").trim();
|
|
510
|
+
}
|
|
511
|
+
function locateContinue(content) {
|
|
512
|
+
const lines = content.split("\n");
|
|
513
|
+
const startIdx = lines.findIndex((l) => l.trim() === CONTINUE_HEADING);
|
|
514
|
+
if (startIdx === -1) return null;
|
|
515
|
+
let meta = null;
|
|
516
|
+
let markerIdx = -1;
|
|
517
|
+
for (let i = startIdx + 1; i < Math.min(startIdx + 6, lines.length); i++) {
|
|
518
|
+
const parsed = parseMarker(lines[i]);
|
|
519
|
+
if (parsed) {
|
|
520
|
+
meta = parsed;
|
|
521
|
+
markerIdx = i;
|
|
522
|
+
break;
|
|
523
|
+
}
|
|
524
|
+
if (lines[i].trim() !== "") break;
|
|
525
|
+
}
|
|
526
|
+
let endIdx = lines.length;
|
|
527
|
+
if (markerIdx !== -1) {
|
|
528
|
+
const closeIdx = lines.findIndex(
|
|
529
|
+
(l, i) => i > markerIdx && l.trim() === MARKER_CLOSE
|
|
530
|
+
);
|
|
531
|
+
if (closeIdx !== -1) {
|
|
532
|
+
endIdx = closeIdx + 1;
|
|
533
|
+
} else {
|
|
534
|
+
endIdx = heuristicEnd(lines, startIdx);
|
|
535
|
+
}
|
|
536
|
+
} else {
|
|
537
|
+
endIdx = heuristicEnd(lines, startIdx);
|
|
538
|
+
}
|
|
539
|
+
let trailingEnd = endIdx;
|
|
540
|
+
while (trailingEnd < lines.length && lines[trailingEnd].trim() === "") {
|
|
541
|
+
trailingEnd += 1;
|
|
542
|
+
}
|
|
543
|
+
if (trailingEnd < lines.length && lines[trailingEnd].trim() === "---") {
|
|
544
|
+
trailingEnd += 1;
|
|
545
|
+
} else {
|
|
546
|
+
trailingEnd = endIdx;
|
|
547
|
+
}
|
|
548
|
+
return {
|
|
549
|
+
startIdx,
|
|
550
|
+
endIdx: trailingEnd,
|
|
551
|
+
meta,
|
|
552
|
+
lines: lines.slice(startIdx, trailingEnd)
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
function heuristicEnd(lines, startIdx) {
|
|
556
|
+
for (let i = startIdx + 1; i < lines.length; i++) {
|
|
557
|
+
const trimmed = lines[i].trim();
|
|
558
|
+
if (trimmed === "---" || /^##(?!#)/.test(trimmed) && trimmed !== CONTINUE_HEADING) {
|
|
559
|
+
return i;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return lines.length;
|
|
563
|
+
}
|
|
564
|
+
function stripContinue(content) {
|
|
565
|
+
const found = locateContinue(content);
|
|
566
|
+
if (!found) return content;
|
|
567
|
+
const lines = content.split("\n");
|
|
568
|
+
const before = lines.slice(0, found.startIdx);
|
|
569
|
+
const after = lines.slice(found.endIdx);
|
|
570
|
+
while (after.length > 0 && after[0].trim() === "") after.shift();
|
|
571
|
+
return [...before, ...after].join("\n");
|
|
572
|
+
}
|
|
573
|
+
function escapeAttr(value) {
|
|
574
|
+
return value.replace(/"/g, "'");
|
|
575
|
+
}
|
|
576
|
+
function buildContinueBlock(opts) {
|
|
577
|
+
const ts = opts.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
578
|
+
const attrs = [
|
|
579
|
+
`authored="${opts.authored}"`,
|
|
580
|
+
`session="${escapeAttr(opts.sessionLine)}"`,
|
|
581
|
+
opts.sessionId ? `session-id="${escapeAttr(opts.sessionId)}"` : null,
|
|
582
|
+
`ts="${ts}"`
|
|
583
|
+
].filter(Boolean).join(" ");
|
|
584
|
+
const header = [
|
|
585
|
+
`> **Last session:** ${opts.sessionLine}`,
|
|
586
|
+
`> **Paused at:** ${ts}`,
|
|
587
|
+
">",
|
|
588
|
+
`> Working directory: ${opts.cwd}`
|
|
589
|
+
];
|
|
590
|
+
if (opts.sessionId) {
|
|
591
|
+
header.push(">", `> Resume with: \`claude --resume ${opts.sessionId}\``);
|
|
592
|
+
}
|
|
593
|
+
const body = (opts.body ?? "").trim();
|
|
594
|
+
const parts = [
|
|
595
|
+
CONTINUE_HEADING,
|
|
596
|
+
"",
|
|
597
|
+
`${MARKER_OPEN} ${attrs} -->`,
|
|
598
|
+
"",
|
|
599
|
+
...header
|
|
600
|
+
];
|
|
601
|
+
if (body) {
|
|
602
|
+
parts.push("", body);
|
|
603
|
+
} else {
|
|
604
|
+
parts.push(
|
|
605
|
+
">",
|
|
606
|
+
"> _No checkpoint body was recorded \u2014 see the latest session note._"
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
parts.push("", MARKER_CLOSE, "", "---", "");
|
|
610
|
+
return parts.join("\n");
|
|
611
|
+
}
|
|
612
|
+
function applyContinue(opts) {
|
|
613
|
+
const target = resolveTodoTarget(opts.rootPath, { create: !opts.dryRun });
|
|
614
|
+
if (!target) {
|
|
615
|
+
return {
|
|
616
|
+
action: "failed",
|
|
617
|
+
path: null,
|
|
618
|
+
block: buildContinueBlock(opts),
|
|
619
|
+
error: "Could not resolve or create a TODO.md target"
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
const existing = locateContinue(target.content);
|
|
623
|
+
let carriedForward = false;
|
|
624
|
+
let effectiveBody = opts.body;
|
|
625
|
+
if (opts.authored === "auto" && existing) {
|
|
626
|
+
if (existing.meta?.authored === "model" && existing.meta.session === opts.sessionLine) {
|
|
627
|
+
return {
|
|
628
|
+
action: "preserved",
|
|
629
|
+
path: target.path,
|
|
630
|
+
block: buildContinueBlock(opts),
|
|
631
|
+
preservedMeta: existing.meta
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
if (!existing.meta && !isBoilerplateOnly(existing.lines)) {
|
|
635
|
+
const salvaged = extractSectionContent(existing.lines);
|
|
636
|
+
if (salvaged) {
|
|
637
|
+
effectiveBody = [
|
|
638
|
+
"_Carried forward from the previous checkpoint (author unknown \u2014 this",
|
|
639
|
+
"block predates checkpoint authorship markers):_",
|
|
640
|
+
"",
|
|
641
|
+
salvaged
|
|
642
|
+
].join("\n");
|
|
643
|
+
carriedForward = true;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
const block = buildContinueBlock({ ...opts, body: effectiveBody });
|
|
648
|
+
if (opts.dryRun) {
|
|
649
|
+
return { action: "written", path: target.path, block, carriedForward };
|
|
650
|
+
}
|
|
651
|
+
const newContent = block + stripContinue(target.content).trimStart();
|
|
652
|
+
const tmpPath = `${target.path}.continue.tmp`;
|
|
653
|
+
try {
|
|
654
|
+
writeFileSync2(tmpPath, newContent, "utf8");
|
|
655
|
+
renameSync3(tmpPath, target.path);
|
|
656
|
+
} catch (err) {
|
|
657
|
+
try {
|
|
658
|
+
if (existsSync6(tmpPath)) renameSync3(tmpPath, `${tmpPath}.dead`);
|
|
659
|
+
} catch {
|
|
660
|
+
}
|
|
661
|
+
return {
|
|
662
|
+
action: "failed",
|
|
663
|
+
path: target.path,
|
|
664
|
+
block,
|
|
665
|
+
error: String(err)
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
return { action: "written", path: target.path, block, carriedForward };
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// src/hooks/ts/lib/project-utils/todo.ts
|
|
425
672
|
function ensureTodoMd(cwd) {
|
|
426
673
|
const todoPath = findTodoPath(cwd);
|
|
427
|
-
if (!
|
|
428
|
-
const parentDir =
|
|
429
|
-
if (!
|
|
674
|
+
if (!existsSync7(todoPath)) {
|
|
675
|
+
const parentDir = join6(todoPath, "..");
|
|
676
|
+
if (!existsSync7(parentDir)) mkdirSync4(parentDir, { recursive: true });
|
|
430
677
|
const content = `# TODO
|
|
431
678
|
|
|
432
679
|
## Current Session
|
|
@@ -441,43 +688,52 @@ function ensureTodoMd(cwd) {
|
|
|
441
688
|
|
|
442
689
|
*Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}*
|
|
443
690
|
`;
|
|
444
|
-
|
|
691
|
+
writeFileSync3(todoPath, content);
|
|
445
692
|
console.error(`Created TODO.md: ${todoPath}`);
|
|
446
693
|
}
|
|
447
694
|
return todoPath;
|
|
448
695
|
}
|
|
449
696
|
function updateTodoContinue(cwd, noteFilename, state, tokenDisplay) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
content = titleMatch[1] + continueSection + content.substring(titleMatch[0].length);
|
|
469
|
-
} else {
|
|
470
|
-
content = continueSection + content;
|
|
697
|
+
ensureTodoMd(cwd);
|
|
698
|
+
const result = applyContinue({
|
|
699
|
+
rootPath: cwd,
|
|
700
|
+
authored: "auto",
|
|
701
|
+
// The hooks identify a session by its note filename; `pai pause` resolves
|
|
702
|
+
// the same string from the registry (and falls back to this filename when
|
|
703
|
+
// the registry has no session row). They must agree, because this string
|
|
704
|
+
// is the key that decides whether an authored checkpoint belongs to the
|
|
705
|
+
// current session and must be preserved.
|
|
706
|
+
sessionLine: noteFilename.replace(/\.md$/, ""),
|
|
707
|
+
cwd,
|
|
708
|
+
body: state?.trim() || void 0
|
|
709
|
+
});
|
|
710
|
+
if (result.action === "preserved") {
|
|
711
|
+
console.error(
|
|
712
|
+
"TODO.md ## Continue left intact \u2014 authored checkpoint for this session"
|
|
713
|
+
);
|
|
714
|
+
return;
|
|
471
715
|
}
|
|
472
|
-
|
|
473
|
-
|
|
716
|
+
if (result.action === "failed") {
|
|
717
|
+
console.error(`TODO.md ## Continue update failed: ${result.error}`);
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
try {
|
|
721
|
+
const todoPath = result.path;
|
|
722
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
723
|
+
let content = readFileSync6(todoPath, "utf-8");
|
|
724
|
+
content = content.replace(/(\n---\s*)*(\n\*Last updated:.*\*\s*)+$/g, "");
|
|
725
|
+
content = content.trimEnd() + `
|
|
474
726
|
|
|
475
727
|
---
|
|
476
728
|
|
|
477
729
|
*Last updated: ${now}*
|
|
478
730
|
`;
|
|
479
|
-
|
|
480
|
-
|
|
731
|
+
writeFileSync3(todoPath, content);
|
|
732
|
+
} catch {
|
|
733
|
+
}
|
|
734
|
+
console.error(
|
|
735
|
+
result.carriedForward ? "TODO.md ## Continue section updated (previous content carried forward)" : "TODO.md ## Continue section updated"
|
|
736
|
+
);
|
|
481
737
|
}
|
|
482
738
|
|
|
483
739
|
// src/hooks/ts/pre-compact/context-compression-hook.ts
|
|
@@ -497,7 +753,7 @@ function contentToText(content) {
|
|
|
497
753
|
}
|
|
498
754
|
function getTranscriptStats(transcriptPath) {
|
|
499
755
|
try {
|
|
500
|
-
const content =
|
|
756
|
+
const content = readFileSync7(transcriptPath, "utf-8");
|
|
501
757
|
const lines = content.trim().split("\n");
|
|
502
758
|
let userMessages = 0;
|
|
503
759
|
let assistantMessages = 0;
|
|
@@ -526,7 +782,7 @@ function parseTranscript(transcriptPath) {
|
|
|
526
782
|
workItems: []
|
|
527
783
|
};
|
|
528
784
|
try {
|
|
529
|
-
const raw =
|
|
785
|
+
const raw = readFileSync7(transcriptPath, "utf-8");
|
|
530
786
|
const lines = raw.trim().split("\n");
|
|
531
787
|
const seenSummaries = /* @__PURE__ */ new Set();
|
|
532
788
|
for (const line of lines) {
|
|
@@ -662,9 +918,9 @@ function deriveTitle(data) {
|
|
|
662
918
|
var CUMULATIVE_STATE_FILE = ".compact-state.json";
|
|
663
919
|
function loadCumulativeState(notesDir) {
|
|
664
920
|
try {
|
|
665
|
-
const filePath =
|
|
666
|
-
if (!
|
|
667
|
-
const raw = JSON.parse(
|
|
921
|
+
const filePath = join7(notesDir, CUMULATIVE_STATE_FILE);
|
|
922
|
+
if (!existsSync8(filePath)) return null;
|
|
923
|
+
const raw = JSON.parse(readFileSync7(filePath, "utf-8"));
|
|
668
924
|
return {
|
|
669
925
|
userMessages: raw.userMessages || [],
|
|
670
926
|
summaries: raw.summaries || [],
|
|
@@ -696,8 +952,8 @@ function mergeTranscriptData(accumulated, current) {
|
|
|
696
952
|
}
|
|
697
953
|
function saveCumulativeState(notesDir, data, notePath) {
|
|
698
954
|
try {
|
|
699
|
-
const filePath =
|
|
700
|
-
|
|
955
|
+
const filePath = join7(notesDir, CUMULATIVE_STATE_FILE);
|
|
956
|
+
writeFileSync4(filePath, JSON.stringify({
|
|
701
957
|
...data,
|
|
702
958
|
notePath,
|
|
703
959
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -840,9 +1096,9 @@ async function main() {
|
|
|
840
1096
|
const data = parseTranscript(hookInput.transcript_path);
|
|
841
1097
|
let notesInfo;
|
|
842
1098
|
try {
|
|
843
|
-
notesInfo = hookInput.cwd ? findNotesDir(hookInput.cwd) : { path:
|
|
1099
|
+
notesInfo = hookInput.cwd ? findNotesDir(hookInput.cwd) : { path: join7(dirname(hookInput.transcript_path), "Notes"), isLocal: false };
|
|
844
1100
|
} catch {
|
|
845
|
-
notesInfo = { path:
|
|
1101
|
+
notesInfo = { path: join7(dirname(hookInput.transcript_path), "Notes"), isLocal: false };
|
|
846
1102
|
}
|
|
847
1103
|
const accumulated = loadCumulativeState(notesInfo.path);
|
|
848
1104
|
const merged = mergeTranscriptData(accumulated, data);
|
|
@@ -858,10 +1114,10 @@ async function main() {
|
|
|
858
1114
|
notePath = createSessionNote(notesInfo.path, "Untitled Session");
|
|
859
1115
|
} else {
|
|
860
1116
|
try {
|
|
861
|
-
let noteContent =
|
|
1117
|
+
let noteContent = readFileSync7(notePath, "utf-8");
|
|
862
1118
|
if (noteContent.includes("**Status:** Completed")) {
|
|
863
1119
|
noteContent = noteContent.replace("**Status:** Completed", "**Status:** In Progress");
|
|
864
|
-
|
|
1120
|
+
writeFileSync4(notePath, noteContent);
|
|
865
1121
|
console.error(`Reopened completed note for continued session: ${basename3(notePath)}`);
|
|
866
1122
|
}
|
|
867
1123
|
} catch {
|
|
@@ -880,12 +1136,12 @@ ${state}` : `Context compression triggered at ~${tokenDisplay} tokens with ${sta
|
|
|
880
1136
|
const newPath = renameSessionNote(notePath, title);
|
|
881
1137
|
if (newPath !== notePath) {
|
|
882
1138
|
try {
|
|
883
|
-
let noteContent =
|
|
1139
|
+
let noteContent = readFileSync7(newPath, "utf-8");
|
|
884
1140
|
noteContent = noteContent.replace(
|
|
885
1141
|
/^(# Session \d+:)\s*.*$/m,
|
|
886
1142
|
`$1 ${title}`
|
|
887
1143
|
);
|
|
888
|
-
|
|
1144
|
+
writeFileSync4(newPath, noteContent);
|
|
889
1145
|
console.error(`Updated note H1 to match rename`);
|
|
890
1146
|
} catch {
|
|
891
1147
|
}
|
|
@@ -926,8 +1182,8 @@ rename it based on actual work done and add a rich summary.` : "";
|
|
|
926
1182
|
"</system-reminder>"
|
|
927
1183
|
].join("\n");
|
|
928
1184
|
try {
|
|
929
|
-
const stateFile =
|
|
930
|
-
|
|
1185
|
+
const stateFile = join7(tmpdir(), `pai-compact-state-${hookInput.session_id}.txt`);
|
|
1186
|
+
writeFileSync4(stateFile, injection, "utf-8");
|
|
931
1187
|
console.error(`Session state saved to ${stateFile} (${injection.length} chars)`);
|
|
932
1188
|
} catch (err) {
|
|
933
1189
|
console.error(`Failed to save state file: ${err}`);
|