sandstream-kit 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -13
- package/dist/cli.js +458 -2
- package/dist/cli.js.map +1 -1
- package/dist/database.d.ts +2 -2
- package/dist/database.js +9 -14
- package/dist/database.js.map +1 -1
- package/dist/lock.js +5 -3
- package/dist/lock.js.map +1 -1
- package/dist/memory/backup 2.d.ts +6 -0
- package/dist/memory/backup 2.js +80 -0
- package/dist/memory/backup 2.js.map +1 -0
- package/dist/memory/backup.d.ts +6 -0
- package/dist/memory/backup.js +80 -0
- package/dist/memory/backup.js.map +1 -0
- package/dist/memory/db 2.d.ts +40 -0
- package/dist/memory/db 2.js +233 -0
- package/dist/memory/db 2.js.map +1 -0
- package/dist/memory/db.d.ts +40 -0
- package/dist/memory/db.js +233 -0
- package/dist/memory/db.js.map +1 -0
- package/dist/memory/hook 2.d.ts +6 -0
- package/dist/memory/hook 2.js +51 -0
- package/dist/memory/hook 2.js.map +1 -0
- package/dist/memory/hook.d.ts +6 -0
- package/dist/memory/hook.js +51 -0
- package/dist/memory/hook.js.map +1 -0
- package/dist/memory/hook.test 2.d.ts +1 -0
- package/dist/memory/hook.test 2.js +35 -0
- package/dist/memory/hook.test 2.js.map +1 -0
- package/dist/memory/install 2.d.ts +8 -0
- package/dist/memory/install 2.js +72 -0
- package/dist/memory/install 2.js.map +1 -0
- package/dist/memory/install.d.ts +8 -0
- package/dist/memory/install.js +72 -0
- package/dist/memory/install.js.map +1 -0
- package/dist/memory/install.test 2.d.ts +1 -0
- package/dist/memory/install.test 2.js +59 -0
- package/dist/memory/install.test 2.js.map +1 -0
- package/dist/memory/pal 2.d.ts +47 -0
- package/dist/memory/pal 2.js +154 -0
- package/dist/memory/pal 2.js.map +1 -0
- package/dist/memory/pal.d.ts +47 -0
- package/dist/memory/pal.js +154 -0
- package/dist/memory/pal.js.map +1 -0
- package/dist/memory/parser.d.ts +25 -0
- package/dist/memory/parser.js +164 -0
- package/dist/memory/parser.js.map +1 -0
- package/dist/memory/project 2.d.ts +1 -0
- package/dist/memory/project 2.js +24 -0
- package/dist/memory/project 2.js.map +1 -0
- package/dist/memory/project.d.ts +1 -0
- package/dist/memory/project.js +24 -0
- package/dist/memory/project.js.map +1 -0
- package/dist/memory/scan 2.d.ts +15 -0
- package/dist/memory/scan 2.js +94 -0
- package/dist/memory/scan 2.js.map +1 -0
- package/dist/memory/scan.d.ts +15 -0
- package/dist/memory/scan.js +94 -0
- package/dist/memory/scan.js.map +1 -0
- package/dist/memory/scan.test 2.d.ts +1 -0
- package/dist/memory/scan.test 2.js +55 -0
- package/dist/memory/scan.test 2.js.map +1 -0
- package/dist/memory/shared 2.d.ts +33 -0
- package/dist/memory/shared 2.js +120 -0
- package/dist/memory/shared 2.js.map +1 -0
- package/dist/memory/shared.d.ts +33 -0
- package/dist/memory/shared.js +120 -0
- package/dist/memory/shared.js.map +1 -0
- package/dist/memory/threads 2.d.ts +37 -0
- package/dist/memory/threads 2.js +50 -0
- package/dist/memory/threads 2.js.map +1 -0
- package/dist/memory/threads.d.ts +37 -0
- package/dist/memory/threads.js +50 -0
- package/dist/memory/threads.js.map +1 -0
- package/dist/memory/threads.test 2.d.ts +1 -0
- package/dist/memory/threads.test 2.js +66 -0
- package/dist/memory/threads.test 2.js.map +1 -0
- package/dist/memory/types 2.d.ts +52 -0
- package/dist/memory/types 2.js +5 -0
- package/dist/memory/types 2.js.map +1 -0
- package/dist/memory/types.d.ts +52 -0
- package/dist/memory/types.js +5 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/secrets-pull.d.ts +1 -1
- package/dist/secrets-pull.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, it, before, after, beforeEach } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync, rmSync, writeFileSync, readFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { installMemoryHooks, uninstallMemoryHooks } from "./install.js";
|
|
7
|
+
describe("memory hook installer", () => {
|
|
8
|
+
let tmp;
|
|
9
|
+
let settingsPath;
|
|
10
|
+
const prev = process.env.KIT_CLAUDE_SETTINGS;
|
|
11
|
+
before(() => {
|
|
12
|
+
tmp = mkdtempSync(join(tmpdir(), "kit-install-"));
|
|
13
|
+
settingsPath = join(tmp, "settings.json");
|
|
14
|
+
process.env.KIT_CLAUDE_SETTINGS = settingsPath;
|
|
15
|
+
});
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
// Start each test from a settings file with an unrelated, pre-existing hook.
|
|
18
|
+
writeFileSync(settingsPath, JSON.stringify({
|
|
19
|
+
hooks: {
|
|
20
|
+
UserPromptSubmit: [{ hooks: [{ type: "command", command: "some-other-tool" }] }],
|
|
21
|
+
},
|
|
22
|
+
}));
|
|
23
|
+
});
|
|
24
|
+
after(() => {
|
|
25
|
+
if (prev === undefined)
|
|
26
|
+
delete process.env.KIT_CLAUDE_SETTINGS;
|
|
27
|
+
else
|
|
28
|
+
process.env.KIT_CLAUDE_SETTINGS = prev;
|
|
29
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
30
|
+
});
|
|
31
|
+
it("installs both hooks without clobbering existing ones", () => {
|
|
32
|
+
const res = installMemoryHooks();
|
|
33
|
+
assert.deepEqual(res.added.sort(), ["SessionEnd", "UserPromptSubmit"]);
|
|
34
|
+
const s = JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
35
|
+
const ups = s.hooks.UserPromptSubmit.flatMap((g) => g.hooks.map((h) => h.command));
|
|
36
|
+
assert.ok(ups.includes("some-other-tool"), "preserves the pre-existing hook");
|
|
37
|
+
assert.ok(ups.includes("kit memory hook user-prompt-submit"));
|
|
38
|
+
assert.ok(s.hooks.SessionEnd.some((g) => g.hooks.some((h) => h.command === "kit memory hook session-end")));
|
|
39
|
+
});
|
|
40
|
+
it("is idempotent — re-install adds nothing and creates no duplicates", () => {
|
|
41
|
+
installMemoryHooks();
|
|
42
|
+
const res2 = installMemoryHooks();
|
|
43
|
+
assert.deepEqual(res2.added, []);
|
|
44
|
+
assert.deepEqual(res2.alreadyPresent.sort(), ["SessionEnd", "UserPromptSubmit"]);
|
|
45
|
+
const s = JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
46
|
+
const ours = s.hooks.UserPromptSubmit.filter((g) => g.hooks.some((h) => h.command === "kit memory hook user-prompt-submit"));
|
|
47
|
+
assert.equal(ours.length, 1);
|
|
48
|
+
});
|
|
49
|
+
it("uninstall removes only our hooks, leaving others intact", () => {
|
|
50
|
+
installMemoryHooks();
|
|
51
|
+
const res = uninstallMemoryHooks();
|
|
52
|
+
assert.deepEqual(res.removed.sort(), ["SessionEnd", "UserPromptSubmit"]);
|
|
53
|
+
const s = JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
54
|
+
const ups = s.hooks.UserPromptSubmit.flatMap((g) => g.hooks.map((h) => h.command));
|
|
55
|
+
assert.ok(ups.includes("some-other-tool"), "unrelated hook survives uninstall");
|
|
56
|
+
assert.ok(!ups.includes("kit memory hook user-prompt-submit"));
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=install.test%202.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.test 2.js","sourceRoot":"","sources":["../../src/memory/install.test 2.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
+
export interface PendingAction {
|
|
3
|
+
id: string;
|
|
4
|
+
status: string;
|
|
5
|
+
title: string;
|
|
6
|
+
detail: string | null;
|
|
7
|
+
scope: string | null;
|
|
8
|
+
kind: string;
|
|
9
|
+
verify_cmd: string | null;
|
|
10
|
+
created_at: string | null;
|
|
11
|
+
next_check: string | null;
|
|
12
|
+
snooze_until: string | null;
|
|
13
|
+
closed_at: string | null;
|
|
14
|
+
verify_passes: number;
|
|
15
|
+
}
|
|
16
|
+
export interface PalAddInput {
|
|
17
|
+
title: string;
|
|
18
|
+
detail?: string;
|
|
19
|
+
scope?: string;
|
|
20
|
+
kind?: "manual" | "auto";
|
|
21
|
+
verifyCmd?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function palAdd(db: DatabaseSync, input: PalAddInput): string;
|
|
24
|
+
export interface PalListOptions {
|
|
25
|
+
status?: string;
|
|
26
|
+
/** Restrict to this scope plus globally-scoped (NULL) items. Omit = every scope. */
|
|
27
|
+
scope?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function palList(db: DatabaseSync, opts?: PalListOptions): PendingAction[];
|
|
30
|
+
export declare function palDone(db: DatabaseSync, id: string): boolean;
|
|
31
|
+
export declare function palSnooze(db: DatabaseSync, id: string, days: number): boolean;
|
|
32
|
+
export interface AutoVerifyResult {
|
|
33
|
+
checked: number;
|
|
34
|
+
closed: string[];
|
|
35
|
+
reopened: string[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Auto-verify `auto` items. An OPEN item that passes `confirmPasses` consecutive
|
|
39
|
+
* times is closed (a pass increments the streak, a fail resets it). A CLOSED item
|
|
40
|
+
* whose verify now FAILS is reopened (reopen-on-regress). No-info leaves it alone.
|
|
41
|
+
*/
|
|
42
|
+
export declare function palAutoVerify(db: DatabaseSync, confirmPasses?: number): AutoVerifyResult;
|
|
43
|
+
export declare function getLegacyLedgerPath(): string;
|
|
44
|
+
/** Import the old `~/.claude/pal/ledger.jsonl` into pending_actions. Idempotent (by id). */
|
|
45
|
+
export declare function importLegacyLedger(db: DatabaseSync, path?: string): {
|
|
46
|
+
imported: number;
|
|
47
|
+
};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kit memory — PAL (Pending Action Ledger), folded into the memory store.
|
|
3
|
+
*
|
|
4
|
+
* PAL is the STRUCTURED, actionable layer on top of raw conversation memory:
|
|
5
|
+
* "blocked-on-you" items that survive sessions and auto-close when their verify
|
|
6
|
+
* command starts passing. It lives in the `pending_actions` table of the same
|
|
7
|
+
* SQLite store. Deterministic; the only side effect is running operator-defined
|
|
8
|
+
* verify commands (local shell, with a timeout). Fail-open / no-info aware.
|
|
9
|
+
*/
|
|
10
|
+
import { randomBytes } from "node:crypto";
|
|
11
|
+
import { execSync } from "node:child_process";
|
|
12
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
function newId(db) {
|
|
16
|
+
for (let i = 0; i < 100; i++) {
|
|
17
|
+
const id = randomBytes(2).toString("hex"); // 4 hex chars, e.g. "ec95"
|
|
18
|
+
if (!db.prepare("SELECT 1 FROM pending_actions WHERE id = ?").get(id))
|
|
19
|
+
return id;
|
|
20
|
+
}
|
|
21
|
+
throw new Error("could not allocate a unique pending-action id");
|
|
22
|
+
}
|
|
23
|
+
export function palAdd(db, input) {
|
|
24
|
+
const id = newId(db);
|
|
25
|
+
const kind = input.kind ?? (input.verifyCmd ? "auto" : "manual");
|
|
26
|
+
db.prepare(`INSERT INTO pending_actions (id, status, title, detail, scope, kind, verify_cmd)
|
|
27
|
+
VALUES (?, 'open', ?, ?, ?, ?, ?)`).run(id, input.title, input.detail ?? null, input.scope ?? null, kind, input.verifyCmd ?? null);
|
|
28
|
+
return id;
|
|
29
|
+
}
|
|
30
|
+
export function palList(db, opts = {}) {
|
|
31
|
+
const status = opts.status ?? "open";
|
|
32
|
+
if (opts.scope !== undefined) {
|
|
33
|
+
return db
|
|
34
|
+
.prepare("SELECT * FROM pending_actions WHERE status = ? AND (scope = ? OR scope IS NULL) ORDER BY created_at, id")
|
|
35
|
+
.all(status, opts.scope);
|
|
36
|
+
}
|
|
37
|
+
return db
|
|
38
|
+
.prepare("SELECT * FROM pending_actions WHERE status = ? ORDER BY created_at, id")
|
|
39
|
+
.all(status);
|
|
40
|
+
}
|
|
41
|
+
export function palDone(db, id) {
|
|
42
|
+
const res = db
|
|
43
|
+
.prepare("UPDATE pending_actions SET status='closed', closed_at=datetime('now') WHERE id=? AND status!='closed'")
|
|
44
|
+
.run(id);
|
|
45
|
+
return Number(res.changes) > 0;
|
|
46
|
+
}
|
|
47
|
+
export function palSnooze(db, id, days) {
|
|
48
|
+
const d = Math.max(1, Math.floor(days));
|
|
49
|
+
const res = db
|
|
50
|
+
.prepare("UPDATE pending_actions SET status='snoozed', snooze_until=datetime('now', ?) WHERE id=?")
|
|
51
|
+
.run(`+${d} days`, id);
|
|
52
|
+
return Number(res.changes) > 0;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Run a verify command. true = pass (exit 0), false = ran but failed, null = no-info.
|
|
56
|
+
*
|
|
57
|
+
* SECURITY: this runs `cmd` through a shell on purpose — verify commands routinely
|
|
58
|
+
* need pipes / `&&` (e.g. `curl -fsS … | grep 200`). The trust boundary: `verify_cmd`
|
|
59
|
+
* is OPERATOR-AUTHORED and lives only in the PERSONAL store (~/.kit/memory.db); running
|
|
60
|
+
* it is equivalent to the operator running their own command — no untrusted data is
|
|
61
|
+
* interpolated, so this is not a command-injection sink. INVARIANT for Track D (shared
|
|
62
|
+
* memory): shared/synced items must NEVER carry an executable `verify_cmd` that runs
|
|
63
|
+
* unreviewed — only manual items or review-gated verifies cross the sharing boundary.
|
|
64
|
+
*/
|
|
65
|
+
function runVerify(cmd) {
|
|
66
|
+
try {
|
|
67
|
+
execSync(cmd, { stdio: "ignore", timeout: 15_000 });
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
const status = err.status;
|
|
72
|
+
if (typeof status === "number")
|
|
73
|
+
return false; // ran, non-zero exit
|
|
74
|
+
return null; // spawn error / timeout → no-info, leave state unchanged
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Auto-verify `auto` items. An OPEN item that passes `confirmPasses` consecutive
|
|
79
|
+
* times is closed (a pass increments the streak, a fail resets it). A CLOSED item
|
|
80
|
+
* whose verify now FAILS is reopened (reopen-on-regress). No-info leaves it alone.
|
|
81
|
+
*/
|
|
82
|
+
export function palAutoVerify(db, confirmPasses = 2) {
|
|
83
|
+
const out = { checked: 0, closed: [], reopened: [] };
|
|
84
|
+
const rows = db
|
|
85
|
+
.prepare("SELECT * FROM pending_actions WHERE kind='auto' AND verify_cmd IS NOT NULL AND status IN ('open','closed')")
|
|
86
|
+
.all();
|
|
87
|
+
for (const r of rows) {
|
|
88
|
+
const result = runVerify(r.verify_cmd);
|
|
89
|
+
if (result === null)
|
|
90
|
+
continue; // no-info
|
|
91
|
+
out.checked++;
|
|
92
|
+
if (r.status === "open") {
|
|
93
|
+
if (result) {
|
|
94
|
+
const passes = r.verify_passes + 1;
|
|
95
|
+
if (passes >= confirmPasses) {
|
|
96
|
+
db.prepare("UPDATE pending_actions SET status='closed', closed_at=datetime('now'), verify_passes=? WHERE id=?").run(passes, r.id);
|
|
97
|
+
out.closed.push(r.id);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
db.prepare("UPDATE pending_actions SET verify_passes=? WHERE id=?").run(passes, r.id);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (r.verify_passes !== 0) {
|
|
104
|
+
db.prepare("UPDATE pending_actions SET verify_passes=0 WHERE id=?").run(r.id);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else if (r.status === "closed" && !result) {
|
|
108
|
+
db.prepare("UPDATE pending_actions SET status='open', verify_passes=0, closed_at=NULL WHERE id=?").run(r.id);
|
|
109
|
+
out.reopened.push(r.id);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
// ── Migration from the legacy python PAL ledger ───────────────────────────────
|
|
115
|
+
export function getLegacyLedgerPath() {
|
|
116
|
+
return process.env.KIT_PAL_LEDGER ?? join(homedir(), ".claude", "pal", "ledger.jsonl");
|
|
117
|
+
}
|
|
118
|
+
/** Import the old `~/.claude/pal/ledger.jsonl` into pending_actions. Idempotent (by id). */
|
|
119
|
+
export function importLegacyLedger(db, path = getLegacyLedgerPath()) {
|
|
120
|
+
if (!existsSync(path))
|
|
121
|
+
return { imported: 0 };
|
|
122
|
+
let raw;
|
|
123
|
+
try {
|
|
124
|
+
raw = readFileSync(path, "utf8");
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return { imported: 0 };
|
|
128
|
+
}
|
|
129
|
+
const insert = db.prepare(`INSERT OR IGNORE INTO pending_actions
|
|
130
|
+
(id, status, title, detail, scope, kind, verify_cmd, created_at, next_check, verify_passes)
|
|
131
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
132
|
+
let imported = 0;
|
|
133
|
+
for (const line of raw.split("\n")) {
|
|
134
|
+
const t = line.trim();
|
|
135
|
+
if (!t)
|
|
136
|
+
continue;
|
|
137
|
+
let e;
|
|
138
|
+
try {
|
|
139
|
+
e = JSON.parse(t);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (!e.id || !e.title)
|
|
145
|
+
continue;
|
|
146
|
+
const status = e.status === "done" ? "closed" : (e.status ?? "open");
|
|
147
|
+
const kind = e.verify ? "auto" : "manual";
|
|
148
|
+
const res = insert.run(e.id, status, e.title, e.why ?? null, e.repo ?? null, kind, e.verify ?? null, e.ts ?? null, e.next_check ?? null, e.pass_streak ?? 0);
|
|
149
|
+
if (Number(res.changes) > 0)
|
|
150
|
+
imported++;
|
|
151
|
+
}
|
|
152
|
+
return { imported };
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=pal%202.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pal 2.js","sourceRoot":"","sources":["../../src/memory/pal 2.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
+
export interface PendingAction {
|
|
3
|
+
id: string;
|
|
4
|
+
status: string;
|
|
5
|
+
title: string;
|
|
6
|
+
detail: string | null;
|
|
7
|
+
scope: string | null;
|
|
8
|
+
kind: string;
|
|
9
|
+
verify_cmd: string | null;
|
|
10
|
+
created_at: string | null;
|
|
11
|
+
next_check: string | null;
|
|
12
|
+
snooze_until: string | null;
|
|
13
|
+
closed_at: string | null;
|
|
14
|
+
verify_passes: number;
|
|
15
|
+
}
|
|
16
|
+
export interface PalAddInput {
|
|
17
|
+
title: string;
|
|
18
|
+
detail?: string;
|
|
19
|
+
scope?: string;
|
|
20
|
+
kind?: "manual" | "auto";
|
|
21
|
+
verifyCmd?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function palAdd(db: DatabaseSync, input: PalAddInput): string;
|
|
24
|
+
export interface PalListOptions {
|
|
25
|
+
status?: string;
|
|
26
|
+
/** Restrict to this scope plus globally-scoped (NULL) items. Omit = every scope. */
|
|
27
|
+
scope?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function palList(db: DatabaseSync, opts?: PalListOptions): PendingAction[];
|
|
30
|
+
export declare function palDone(db: DatabaseSync, id: string): boolean;
|
|
31
|
+
export declare function palSnooze(db: DatabaseSync, id: string, days: number): boolean;
|
|
32
|
+
export interface AutoVerifyResult {
|
|
33
|
+
checked: number;
|
|
34
|
+
closed: string[];
|
|
35
|
+
reopened: string[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Auto-verify `auto` items. An OPEN item that passes `confirmPasses` consecutive
|
|
39
|
+
* times is closed (a pass increments the streak, a fail resets it). A CLOSED item
|
|
40
|
+
* whose verify now FAILS is reopened (reopen-on-regress). No-info leaves it alone.
|
|
41
|
+
*/
|
|
42
|
+
export declare function palAutoVerify(db: DatabaseSync, confirmPasses?: number): AutoVerifyResult;
|
|
43
|
+
export declare function getLegacyLedgerPath(): string;
|
|
44
|
+
/** Import the old `~/.claude/pal/ledger.jsonl` into pending_actions. Idempotent (by id). */
|
|
45
|
+
export declare function importLegacyLedger(db: DatabaseSync, path?: string): {
|
|
46
|
+
imported: number;
|
|
47
|
+
};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kit memory — PAL (Pending Action Ledger), folded into the memory store.
|
|
3
|
+
*
|
|
4
|
+
* PAL is the STRUCTURED, actionable layer on top of raw conversation memory:
|
|
5
|
+
* "blocked-on-you" items that survive sessions and auto-close when their verify
|
|
6
|
+
* command starts passing. It lives in the `pending_actions` table of the same
|
|
7
|
+
* SQLite store. Deterministic; the only side effect is running operator-defined
|
|
8
|
+
* verify commands (local shell, with a timeout). Fail-open / no-info aware.
|
|
9
|
+
*/
|
|
10
|
+
import { randomBytes } from "node:crypto";
|
|
11
|
+
import { execSync } from "node:child_process";
|
|
12
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
function newId(db) {
|
|
16
|
+
for (let i = 0; i < 100; i++) {
|
|
17
|
+
const id = randomBytes(2).toString("hex"); // 4 hex chars, e.g. "ec95"
|
|
18
|
+
if (!db.prepare("SELECT 1 FROM pending_actions WHERE id = ?").get(id))
|
|
19
|
+
return id;
|
|
20
|
+
}
|
|
21
|
+
throw new Error("could not allocate a unique pending-action id");
|
|
22
|
+
}
|
|
23
|
+
export function palAdd(db, input) {
|
|
24
|
+
const id = newId(db);
|
|
25
|
+
const kind = input.kind ?? (input.verifyCmd ? "auto" : "manual");
|
|
26
|
+
db.prepare(`INSERT INTO pending_actions (id, status, title, detail, scope, kind, verify_cmd)
|
|
27
|
+
VALUES (?, 'open', ?, ?, ?, ?, ?)`).run(id, input.title, input.detail ?? null, input.scope ?? null, kind, input.verifyCmd ?? null);
|
|
28
|
+
return id;
|
|
29
|
+
}
|
|
30
|
+
export function palList(db, opts = {}) {
|
|
31
|
+
const status = opts.status ?? "open";
|
|
32
|
+
if (opts.scope !== undefined) {
|
|
33
|
+
return db
|
|
34
|
+
.prepare("SELECT * FROM pending_actions WHERE status = ? AND (scope = ? OR scope IS NULL) ORDER BY created_at, id")
|
|
35
|
+
.all(status, opts.scope);
|
|
36
|
+
}
|
|
37
|
+
return db
|
|
38
|
+
.prepare("SELECT * FROM pending_actions WHERE status = ? ORDER BY created_at, id")
|
|
39
|
+
.all(status);
|
|
40
|
+
}
|
|
41
|
+
export function palDone(db, id) {
|
|
42
|
+
const res = db
|
|
43
|
+
.prepare("UPDATE pending_actions SET status='closed', closed_at=datetime('now') WHERE id=? AND status!='closed'")
|
|
44
|
+
.run(id);
|
|
45
|
+
return Number(res.changes) > 0;
|
|
46
|
+
}
|
|
47
|
+
export function palSnooze(db, id, days) {
|
|
48
|
+
const d = Math.max(1, Math.floor(days));
|
|
49
|
+
const res = db
|
|
50
|
+
.prepare("UPDATE pending_actions SET status='snoozed', snooze_until=datetime('now', ?) WHERE id=?")
|
|
51
|
+
.run(`+${d} days`, id);
|
|
52
|
+
return Number(res.changes) > 0;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Run a verify command. true = pass (exit 0), false = ran but failed, null = no-info.
|
|
56
|
+
*
|
|
57
|
+
* SECURITY: this runs `cmd` through a shell on purpose — verify commands routinely
|
|
58
|
+
* need pipes / `&&` (e.g. `curl -fsS … | grep 200`). The trust boundary: `verify_cmd`
|
|
59
|
+
* is OPERATOR-AUTHORED and lives only in the PERSONAL store (~/.kit/memory.db); running
|
|
60
|
+
* it is equivalent to the operator running their own command — no untrusted data is
|
|
61
|
+
* interpolated, so this is not a command-injection sink. INVARIANT for Track D (shared
|
|
62
|
+
* memory): shared/synced items must NEVER carry an executable `verify_cmd` that runs
|
|
63
|
+
* unreviewed — only manual items or review-gated verifies cross the sharing boundary.
|
|
64
|
+
*/
|
|
65
|
+
function runVerify(cmd) {
|
|
66
|
+
try {
|
|
67
|
+
execSync(cmd, { stdio: "ignore", timeout: 15_000 });
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
const status = err.status;
|
|
72
|
+
if (typeof status === "number")
|
|
73
|
+
return false; // ran, non-zero exit
|
|
74
|
+
return null; // spawn error / timeout → no-info, leave state unchanged
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Auto-verify `auto` items. An OPEN item that passes `confirmPasses` consecutive
|
|
79
|
+
* times is closed (a pass increments the streak, a fail resets it). A CLOSED item
|
|
80
|
+
* whose verify now FAILS is reopened (reopen-on-regress). No-info leaves it alone.
|
|
81
|
+
*/
|
|
82
|
+
export function palAutoVerify(db, confirmPasses = 2) {
|
|
83
|
+
const out = { checked: 0, closed: [], reopened: [] };
|
|
84
|
+
const rows = db
|
|
85
|
+
.prepare("SELECT * FROM pending_actions WHERE kind='auto' AND verify_cmd IS NOT NULL AND status IN ('open','closed')")
|
|
86
|
+
.all();
|
|
87
|
+
for (const r of rows) {
|
|
88
|
+
const result = runVerify(r.verify_cmd);
|
|
89
|
+
if (result === null)
|
|
90
|
+
continue; // no-info
|
|
91
|
+
out.checked++;
|
|
92
|
+
if (r.status === "open") {
|
|
93
|
+
if (result) {
|
|
94
|
+
const passes = r.verify_passes + 1;
|
|
95
|
+
if (passes >= confirmPasses) {
|
|
96
|
+
db.prepare("UPDATE pending_actions SET status='closed', closed_at=datetime('now'), verify_passes=? WHERE id=?").run(passes, r.id);
|
|
97
|
+
out.closed.push(r.id);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
db.prepare("UPDATE pending_actions SET verify_passes=? WHERE id=?").run(passes, r.id);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (r.verify_passes !== 0) {
|
|
104
|
+
db.prepare("UPDATE pending_actions SET verify_passes=0 WHERE id=?").run(r.id);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else if (r.status === "closed" && !result) {
|
|
108
|
+
db.prepare("UPDATE pending_actions SET status='open', verify_passes=0, closed_at=NULL WHERE id=?").run(r.id);
|
|
109
|
+
out.reopened.push(r.id);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
// ── Migration from the legacy python PAL ledger ───────────────────────────────
|
|
115
|
+
export function getLegacyLedgerPath() {
|
|
116
|
+
return process.env.KIT_PAL_LEDGER ?? join(homedir(), ".claude", "pal", "ledger.jsonl");
|
|
117
|
+
}
|
|
118
|
+
/** Import the old `~/.claude/pal/ledger.jsonl` into pending_actions. Idempotent (by id). */
|
|
119
|
+
export function importLegacyLedger(db, path = getLegacyLedgerPath()) {
|
|
120
|
+
if (!existsSync(path))
|
|
121
|
+
return { imported: 0 };
|
|
122
|
+
let raw;
|
|
123
|
+
try {
|
|
124
|
+
raw = readFileSync(path, "utf8");
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return { imported: 0 };
|
|
128
|
+
}
|
|
129
|
+
const insert = db.prepare(`INSERT OR IGNORE INTO pending_actions
|
|
130
|
+
(id, status, title, detail, scope, kind, verify_cmd, created_at, next_check, verify_passes)
|
|
131
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
132
|
+
let imported = 0;
|
|
133
|
+
for (const line of raw.split("\n")) {
|
|
134
|
+
const t = line.trim();
|
|
135
|
+
if (!t)
|
|
136
|
+
continue;
|
|
137
|
+
let e;
|
|
138
|
+
try {
|
|
139
|
+
e = JSON.parse(t);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (!e.id || !e.title)
|
|
145
|
+
continue;
|
|
146
|
+
const status = e.status === "done" ? "closed" : (e.status ?? "open");
|
|
147
|
+
const kind = e.verify ? "auto" : "manual";
|
|
148
|
+
const res = insert.run(e.id, status, e.title, e.why ?? null, e.repo ?? null, kind, e.verify ?? null, e.ts ?? null, e.next_check ?? null, e.pass_streak ?? 0);
|
|
149
|
+
if (Number(res.changes) > 0)
|
|
150
|
+
imported++;
|
|
151
|
+
}
|
|
152
|
+
return { imported };
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=pal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pal.js","sourceRoot":"","sources":["../../src/memory/pal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
+
export interface IndexResult {
|
|
3
|
+
files: number;
|
|
4
|
+
sessions: number;
|
|
5
|
+
messages: number;
|
|
6
|
+
toolUses: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function getClaudeProjectsDir(): string;
|
|
9
|
+
interface ContentBlock {
|
|
10
|
+
type?: string;
|
|
11
|
+
text?: string;
|
|
12
|
+
name?: string;
|
|
13
|
+
input?: unknown;
|
|
14
|
+
}
|
|
15
|
+
type Content = string | ContentBlock[] | undefined;
|
|
16
|
+
/** Flatten transcript content (string or block array) into searchable plain text. */
|
|
17
|
+
export declare function extractText(content: Content): string;
|
|
18
|
+
/** Extract tool_use blocks from a message's content. */
|
|
19
|
+
export declare function extractToolUses(content: Content): {
|
|
20
|
+
name: string;
|
|
21
|
+
input: string;
|
|
22
|
+
}[];
|
|
23
|
+
/** Walk ~/.claude/projects and index every transcript. Idempotent. */
|
|
24
|
+
export declare function indexClaudeTranscripts(db: DatabaseSync): IndexResult;
|
|
25
|
+
export {};
|