sphica 0.6.27 → 0.6.29
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/dist/capture.js +106 -36
- package/dist/cli.js +103 -36
- package/dist/deliver.js +103 -36
- package/dist/mcp-record.js +105 -39
- package/hooks/hooks.json +2 -2
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://anthropic.com/claude-code/plugin.schema.json",
|
|
3
3
|
"name": "sphica",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.29",
|
|
5
5
|
"description": "Records Claude Code and Codex sessions on your machine and keeps past implementation and decisions, with their sources, for your agent to find.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "iroha924",
|
package/dist/capture.js
CHANGED
|
@@ -11774,32 +11774,103 @@ function captureNotice(file = dbFile()) {
|
|
|
11774
11774
|
return panel(`sphica: the database rejected ${plural(s.rejected, "record")}`, [rejectedDir(), `Move them back to ${spoolDir()} to resend`], "Fix them first, then check with sphica doctor");
|
|
11775
11775
|
return null;
|
|
11776
11776
|
}
|
|
11777
|
-
var
|
|
11778
|
-
|
|
11777
|
+
var turnDir = (host, session) => path6.join(baselineDir(), digest(`${host}\x00${session}`));
|
|
11778
|
+
var startFile = (dir, turn) => path6.join(dir, `${digest(turn)}.json`);
|
|
11779
|
+
var isStart = (v) => {
|
|
11780
|
+
const s = v;
|
|
11781
|
+
return !!s && typeof s === "object" && typeof s.turn === "string" && typeof s.running === "boolean" && (s.seq === null || Number.isSafeInteger(s.seq)) && (s.entries === null || typeof s.entries === "object" && !Array.isArray(s.entries)) && (s.head === null || typeof s.head === "string");
|
|
11782
|
+
};
|
|
11783
|
+
function readStart(file) {
|
|
11779
11784
|
try {
|
|
11780
|
-
|
|
11785
|
+
const s = JSON.parse(fs5.readFileSync(file, "utf8"));
|
|
11786
|
+
return isStart(s) ? s : null;
|
|
11781
11787
|
} catch {
|
|
11782
11788
|
return null;
|
|
11783
11789
|
}
|
|
11784
11790
|
}
|
|
11785
|
-
function
|
|
11786
|
-
|
|
11787
|
-
|
|
11788
|
-
|
|
11791
|
+
function readStarts(dir) {
|
|
11792
|
+
let names;
|
|
11793
|
+
try {
|
|
11794
|
+
names = fs5.readdirSync(dir);
|
|
11795
|
+
} catch (e) {
|
|
11796
|
+
return e.code === "ENOENT" ? [] : null;
|
|
11797
|
+
}
|
|
11798
|
+
const starts = [];
|
|
11799
|
+
for (const name of names) {
|
|
11800
|
+
if (name.startsWith("."))
|
|
11801
|
+
continue;
|
|
11802
|
+
const s = readStart(path6.join(dir, name));
|
|
11803
|
+
if (!s)
|
|
11804
|
+
return null;
|
|
11805
|
+
starts.push(s);
|
|
11806
|
+
}
|
|
11807
|
+
return starts;
|
|
11808
|
+
}
|
|
11809
|
+
function newestTurn(starts) {
|
|
11810
|
+
if (!starts?.length || starts.some((s) => s.seq === null))
|
|
11811
|
+
return null;
|
|
11812
|
+
const top = Math.max(...starts.map((s) => s.seq));
|
|
11813
|
+
const at = starts.filter((s) => s.seq === top);
|
|
11814
|
+
return at.length === 1 ? at[0]?.turn ?? null : null;
|
|
11815
|
+
}
|
|
11816
|
+
function writeStart(dir, s, file = startFile(dir, s.turn)) {
|
|
11817
|
+
fs5.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
11818
|
+
const tmp = path6.join(dir, `.${path6.basename(file)}.${process.pid}.tmp`);
|
|
11819
|
+
fs5.writeFileSync(tmp, JSON.stringify(s), { mode: 384 });
|
|
11789
11820
|
fs5.renameSync(tmp, file);
|
|
11790
11821
|
}
|
|
11822
|
+
function openTurn(dir, turn, root) {
|
|
11823
|
+
if (readStart(startFile(dir, turn))?.running)
|
|
11824
|
+
return null;
|
|
11825
|
+
const starts = readStarts(dir);
|
|
11826
|
+
const seq = starts && !starts.some((s) => s.seq === null) ? Math.max(0, ...starts.map((s) => s.seq)) + 1 : null;
|
|
11827
|
+
const now = snapshot(root);
|
|
11828
|
+
return () => writeStart(dir, { head: now?.head ?? null, entries: now?.entries ?? null, running: true, turn, seq });
|
|
11829
|
+
}
|
|
11830
|
+
function closeTurn(dir, turn, root) {
|
|
11831
|
+
const own = readStart(startFile(dir, turn));
|
|
11832
|
+
if (!own?.entries)
|
|
11833
|
+
return null;
|
|
11834
|
+
const before = { head: own.head, entries: own.entries };
|
|
11835
|
+
const now = snapshot(root);
|
|
11836
|
+
return () => {
|
|
11837
|
+
if (newestTurn(readStarts(dir)) !== turn || JSON.stringify(readStart(startFile(dir, turn))) !== JSON.stringify(own))
|
|
11838
|
+
return [];
|
|
11839
|
+
writeStart(dir, { ...own, ...now ?? {}, running: false });
|
|
11840
|
+
return now ? changed(root, before, now) : [];
|
|
11841
|
+
};
|
|
11842
|
+
}
|
|
11843
|
+
function stopTurn(dir, turn) {
|
|
11844
|
+
const own = readStart(startFile(dir, turn));
|
|
11845
|
+
if (own?.entries || own?.running)
|
|
11846
|
+
writeStart(dir, { ...own, head: null, entries: null, running: false });
|
|
11847
|
+
}
|
|
11791
11848
|
function pruneBaselines() {
|
|
11792
11849
|
const cutoff = Date.now() - HOLD_DAYS * 24 * 60 * 60 * 1000;
|
|
11793
|
-
|
|
11850
|
+
const fresh = (file) => (fs5.statSync(file, { throwIfNoEntry: false })?.mtimeMs ?? Date.now()) >= cutoff;
|
|
11851
|
+
let names;
|
|
11794
11852
|
try {
|
|
11795
|
-
|
|
11853
|
+
names = fs5.readdirSync(baselineDir());
|
|
11796
11854
|
} catch {
|
|
11797
11855
|
return;
|
|
11798
11856
|
}
|
|
11799
|
-
for (const
|
|
11800
|
-
const
|
|
11801
|
-
if (
|
|
11802
|
-
|
|
11857
|
+
for (const name of names) {
|
|
11858
|
+
const at = path6.join(baselineDir(), name);
|
|
11859
|
+
if (!fs5.statSync(at, { throwIfNoEntry: false })?.isDirectory()) {
|
|
11860
|
+
if (!fresh(at))
|
|
11861
|
+
fs5.rmSync(at, { force: true });
|
|
11862
|
+
} else {
|
|
11863
|
+
const files = fs5.readdirSync(at);
|
|
11864
|
+
if (files.length && !files.some((f) => fresh(path6.join(at, f))))
|
|
11865
|
+
fs5.rmSync(at, { recursive: true, force: true });
|
|
11866
|
+
}
|
|
11867
|
+
}
|
|
11868
|
+
}
|
|
11869
|
+
function attempt(fn) {
|
|
11870
|
+
try {
|
|
11871
|
+
return fn();
|
|
11872
|
+
} catch {
|
|
11873
|
+
return;
|
|
11803
11874
|
}
|
|
11804
11875
|
}
|
|
11805
11876
|
function onHook(host, input) {
|
|
@@ -11813,17 +11884,17 @@ function onHook(host, input) {
|
|
|
11813
11884
|
fs5.appendFileSync(file, `export SPHICA_PARENT_SESSION=${input.session_id}
|
|
11814
11885
|
`);
|
|
11815
11886
|
}
|
|
11816
|
-
|
|
11817
|
-
const now = place2 && snapshot(place2.root);
|
|
11818
|
-
if (now)
|
|
11819
|
-
writeBaseline(baselineFile(host, String(input.session_id)), { ...now, running: false });
|
|
11820
|
-
pruneBaselines();
|
|
11887
|
+
attempt(pruneBaselines);
|
|
11821
11888
|
return { flush: false, notice: captureNotice() };
|
|
11822
11889
|
}
|
|
11823
11890
|
if (!owner())
|
|
11824
11891
|
return { flush: false };
|
|
11825
|
-
if (event === "Interrupt")
|
|
11892
|
+
if (event === "Interrupt") {
|
|
11893
|
+
const turn2 = input.prompt_id ?? input.turn_id;
|
|
11894
|
+
if (turn2)
|
|
11895
|
+
attempt(() => stopTurn(turnDir(host, String(input.session_id)), turn2));
|
|
11826
11896
|
return { flush: true };
|
|
11897
|
+
}
|
|
11827
11898
|
const place = identify(input.cwd ?? process.cwd());
|
|
11828
11899
|
if (!place)
|
|
11829
11900
|
return { flush: false };
|
|
@@ -11847,30 +11918,26 @@ function onHook(host, input) {
|
|
|
11847
11918
|
const id = `${key}:${digest(kept.body)}`;
|
|
11848
11919
|
spool({ ...base, at: when, kind: "message", id, speaker, ...kept });
|
|
11849
11920
|
};
|
|
11850
|
-
const
|
|
11851
|
-
if (event === "UserPromptSubmit") {
|
|
11852
|
-
if (!readBaseline(baseline)?.running) {
|
|
11853
|
-
const now = snapshot(place.root);
|
|
11854
|
-
if (now)
|
|
11855
|
-
writeBaseline(baseline, { ...now, running: true });
|
|
11856
|
-
}
|
|
11857
|
-
}
|
|
11921
|
+
const dir = turnDir(host, base.session);
|
|
11858
11922
|
if (event === "UserPromptSubmit" && input.prompt) {
|
|
11859
11923
|
const prompt = input.prompt.trimStart();
|
|
11860
11924
|
if (!INJECTED.some((r) => r.test(prompt)))
|
|
11861
11925
|
say(`${turn}:owner`, "owner", prompt);
|
|
11862
11926
|
}
|
|
11927
|
+
if (event === "UserPromptSubmit")
|
|
11928
|
+
attempt(() => openTurn(dir, turn, place.root)?.());
|
|
11863
11929
|
if (event === "Stop") {
|
|
11864
11930
|
if (input.last_assistant_message)
|
|
11865
11931
|
say(`${turn}:assistant`, "assistant", input.last_assistant_message);
|
|
11866
|
-
const
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
|
|
11871
|
-
|
|
11872
|
-
|
|
11873
|
-
|
|
11932
|
+
const paths = attempt(() => {
|
|
11933
|
+
const finish = closeTurn(dir, turn, place.root);
|
|
11934
|
+
if (finish)
|
|
11935
|
+
return finish();
|
|
11936
|
+
stopTurn(dir, turn);
|
|
11937
|
+
return [];
|
|
11938
|
+
});
|
|
11939
|
+
for (const p of paths ?? [])
|
|
11940
|
+
spool({ ...base, kind: "edit", event: null, path: p, via: "status" });
|
|
11874
11941
|
return { flush: true };
|
|
11875
11942
|
}
|
|
11876
11943
|
if (event === "PostToolUse") {
|
|
@@ -11930,7 +11997,7 @@ function readState() {
|
|
|
11930
11997
|
function lock() {
|
|
11931
11998
|
const file = path6.join(spoolDir(), ".lock");
|
|
11932
11999
|
fs5.mkdirSync(spoolDir(), { recursive: true, mode: 448 });
|
|
11933
|
-
for (let
|
|
12000
|
+
for (let attempt2 = 0;attempt2 < 2; attempt2++) {
|
|
11934
12001
|
try {
|
|
11935
12002
|
fs5.writeFileSync(file, String(process.pid), { flag: "wx", mode: 384 });
|
|
11936
12003
|
return () => {
|
|
@@ -12228,15 +12295,18 @@ export {
|
|
|
12228
12295
|
answersOf,
|
|
12229
12296
|
branchOf,
|
|
12230
12297
|
captureNotice,
|
|
12298
|
+
closeTurn,
|
|
12231
12299
|
current,
|
|
12232
12300
|
fit,
|
|
12233
12301
|
flush,
|
|
12234
12302
|
isOwnerTurn,
|
|
12235
12303
|
onHook,
|
|
12304
|
+
openTurn,
|
|
12236
12305
|
readInput,
|
|
12237
12306
|
readState,
|
|
12238
12307
|
rejectedDir,
|
|
12239
12308
|
spoolDir,
|
|
12309
|
+
turnDir,
|
|
12240
12310
|
unregisteredDir,
|
|
12241
12311
|
write
|
|
12242
12312
|
};
|
package/dist/cli.js
CHANGED
|
@@ -15237,32 +15237,103 @@ function captureNotice(file = dbFile()) {
|
|
|
15237
15237
|
return panel(`sphica: the database rejected ${plural(s.rejected, "record")}`, [rejectedDir(), `Move them back to ${spoolDir()} to resend`], "Fix them first, then check with sphica doctor");
|
|
15238
15238
|
return null;
|
|
15239
15239
|
}
|
|
15240
|
-
var
|
|
15241
|
-
|
|
15240
|
+
var turnDir = (host, session) => path8.join(baselineDir(), digest(`${host}\x00${session}`));
|
|
15241
|
+
var startFile = (dir, turn) => path8.join(dir, `${digest(turn)}.json`);
|
|
15242
|
+
var isStart = (v) => {
|
|
15243
|
+
const s = v;
|
|
15244
|
+
return !!s && typeof s === "object" && typeof s.turn === "string" && typeof s.running === "boolean" && (s.seq === null || Number.isSafeInteger(s.seq)) && (s.entries === null || typeof s.entries === "object" && !Array.isArray(s.entries)) && (s.head === null || typeof s.head === "string");
|
|
15245
|
+
};
|
|
15246
|
+
function readStart(file) {
|
|
15242
15247
|
try {
|
|
15243
|
-
|
|
15248
|
+
const s = JSON.parse(fs7.readFileSync(file, "utf8"));
|
|
15249
|
+
return isStart(s) ? s : null;
|
|
15244
15250
|
} catch {
|
|
15245
15251
|
return null;
|
|
15246
15252
|
}
|
|
15247
15253
|
}
|
|
15248
|
-
function
|
|
15249
|
-
|
|
15250
|
-
|
|
15251
|
-
|
|
15254
|
+
function readStarts(dir) {
|
|
15255
|
+
let names;
|
|
15256
|
+
try {
|
|
15257
|
+
names = fs7.readdirSync(dir);
|
|
15258
|
+
} catch (e) {
|
|
15259
|
+
return e.code === "ENOENT" ? [] : null;
|
|
15260
|
+
}
|
|
15261
|
+
const starts = [];
|
|
15262
|
+
for (const name of names) {
|
|
15263
|
+
if (name.startsWith("."))
|
|
15264
|
+
continue;
|
|
15265
|
+
const s = readStart(path8.join(dir, name));
|
|
15266
|
+
if (!s)
|
|
15267
|
+
return null;
|
|
15268
|
+
starts.push(s);
|
|
15269
|
+
}
|
|
15270
|
+
return starts;
|
|
15271
|
+
}
|
|
15272
|
+
function newestTurn(starts) {
|
|
15273
|
+
if (!starts?.length || starts.some((s) => s.seq === null))
|
|
15274
|
+
return null;
|
|
15275
|
+
const top = Math.max(...starts.map((s) => s.seq));
|
|
15276
|
+
const at = starts.filter((s) => s.seq === top);
|
|
15277
|
+
return at.length === 1 ? at[0]?.turn ?? null : null;
|
|
15278
|
+
}
|
|
15279
|
+
function writeStart(dir, s, file = startFile(dir, s.turn)) {
|
|
15280
|
+
fs7.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
15281
|
+
const tmp = path8.join(dir, `.${path8.basename(file)}.${process.pid}.tmp`);
|
|
15282
|
+
fs7.writeFileSync(tmp, JSON.stringify(s), { mode: 384 });
|
|
15252
15283
|
fs7.renameSync(tmp, file);
|
|
15253
15284
|
}
|
|
15285
|
+
function openTurn(dir, turn, root) {
|
|
15286
|
+
if (readStart(startFile(dir, turn))?.running)
|
|
15287
|
+
return null;
|
|
15288
|
+
const starts = readStarts(dir);
|
|
15289
|
+
const seq = starts && !starts.some((s) => s.seq === null) ? Math.max(0, ...starts.map((s) => s.seq)) + 1 : null;
|
|
15290
|
+
const now = snapshot(root);
|
|
15291
|
+
return () => writeStart(dir, { head: now?.head ?? null, entries: now?.entries ?? null, running: true, turn, seq });
|
|
15292
|
+
}
|
|
15293
|
+
function closeTurn(dir, turn, root) {
|
|
15294
|
+
const own = readStart(startFile(dir, turn));
|
|
15295
|
+
if (!own?.entries)
|
|
15296
|
+
return null;
|
|
15297
|
+
const before = { head: own.head, entries: own.entries };
|
|
15298
|
+
const now = snapshot(root);
|
|
15299
|
+
return () => {
|
|
15300
|
+
if (newestTurn(readStarts(dir)) !== turn || JSON.stringify(readStart(startFile(dir, turn))) !== JSON.stringify(own))
|
|
15301
|
+
return [];
|
|
15302
|
+
writeStart(dir, { ...own, ...now ?? {}, running: false });
|
|
15303
|
+
return now ? changed(root, before, now) : [];
|
|
15304
|
+
};
|
|
15305
|
+
}
|
|
15306
|
+
function stopTurn(dir, turn) {
|
|
15307
|
+
const own = readStart(startFile(dir, turn));
|
|
15308
|
+
if (own?.entries || own?.running)
|
|
15309
|
+
writeStart(dir, { ...own, head: null, entries: null, running: false });
|
|
15310
|
+
}
|
|
15254
15311
|
function pruneBaselines() {
|
|
15255
15312
|
const cutoff = Date.now() - HOLD_DAYS * 24 * 60 * 60 * 1000;
|
|
15256
|
-
|
|
15313
|
+
const fresh = (file) => (fs7.statSync(file, { throwIfNoEntry: false })?.mtimeMs ?? Date.now()) >= cutoff;
|
|
15314
|
+
let names;
|
|
15257
15315
|
try {
|
|
15258
|
-
|
|
15316
|
+
names = fs7.readdirSync(baselineDir());
|
|
15259
15317
|
} catch {
|
|
15260
15318
|
return;
|
|
15261
15319
|
}
|
|
15262
|
-
for (const
|
|
15263
|
-
const
|
|
15264
|
-
if (
|
|
15265
|
-
|
|
15320
|
+
for (const name of names) {
|
|
15321
|
+
const at = path8.join(baselineDir(), name);
|
|
15322
|
+
if (!fs7.statSync(at, { throwIfNoEntry: false })?.isDirectory()) {
|
|
15323
|
+
if (!fresh(at))
|
|
15324
|
+
fs7.rmSync(at, { force: true });
|
|
15325
|
+
} else {
|
|
15326
|
+
const files = fs7.readdirSync(at);
|
|
15327
|
+
if (files.length && !files.some((f) => fresh(path8.join(at, f))))
|
|
15328
|
+
fs7.rmSync(at, { recursive: true, force: true });
|
|
15329
|
+
}
|
|
15330
|
+
}
|
|
15331
|
+
}
|
|
15332
|
+
function attempt(fn) {
|
|
15333
|
+
try {
|
|
15334
|
+
return fn();
|
|
15335
|
+
} catch {
|
|
15336
|
+
return;
|
|
15266
15337
|
}
|
|
15267
15338
|
}
|
|
15268
15339
|
function onHook(host, input) {
|
|
@@ -15276,17 +15347,17 @@ function onHook(host, input) {
|
|
|
15276
15347
|
fs7.appendFileSync(file, `export SPHICA_PARENT_SESSION=${input.session_id}
|
|
15277
15348
|
`);
|
|
15278
15349
|
}
|
|
15279
|
-
|
|
15280
|
-
const now = place2 && snapshot(place2.root);
|
|
15281
|
-
if (now)
|
|
15282
|
-
writeBaseline(baselineFile(host, String(input.session_id)), { ...now, running: false });
|
|
15283
|
-
pruneBaselines();
|
|
15350
|
+
attempt(pruneBaselines);
|
|
15284
15351
|
return { flush: false, notice: captureNotice() };
|
|
15285
15352
|
}
|
|
15286
15353
|
if (!owner())
|
|
15287
15354
|
return { flush: false };
|
|
15288
|
-
if (event === "Interrupt")
|
|
15355
|
+
if (event === "Interrupt") {
|
|
15356
|
+
const turn2 = input.prompt_id ?? input.turn_id;
|
|
15357
|
+
if (turn2)
|
|
15358
|
+
attempt(() => stopTurn(turnDir(host, String(input.session_id)), turn2));
|
|
15289
15359
|
return { flush: true };
|
|
15360
|
+
}
|
|
15290
15361
|
const place = identify(input.cwd ?? process.cwd());
|
|
15291
15362
|
if (!place)
|
|
15292
15363
|
return { flush: false };
|
|
@@ -15310,30 +15381,26 @@ function onHook(host, input) {
|
|
|
15310
15381
|
const id = `${key}:${digest(kept.body)}`;
|
|
15311
15382
|
spool({ ...base, at: when, kind: "message", id, speaker, ...kept });
|
|
15312
15383
|
};
|
|
15313
|
-
const
|
|
15314
|
-
if (event === "UserPromptSubmit") {
|
|
15315
|
-
if (!readBaseline(baseline)?.running) {
|
|
15316
|
-
const now = snapshot(place.root);
|
|
15317
|
-
if (now)
|
|
15318
|
-
writeBaseline(baseline, { ...now, running: true });
|
|
15319
|
-
}
|
|
15320
|
-
}
|
|
15384
|
+
const dir = turnDir(host, base.session);
|
|
15321
15385
|
if (event === "UserPromptSubmit" && input.prompt) {
|
|
15322
15386
|
const prompt = input.prompt.trimStart();
|
|
15323
15387
|
if (!INJECTED.some((r2) => r2.test(prompt)))
|
|
15324
15388
|
say(`${turn}:owner`, "owner", prompt);
|
|
15325
15389
|
}
|
|
15390
|
+
if (event === "UserPromptSubmit")
|
|
15391
|
+
attempt(() => openTurn(dir, turn, place.root)?.());
|
|
15326
15392
|
if (event === "Stop") {
|
|
15327
15393
|
if (input.last_assistant_message)
|
|
15328
15394
|
say(`${turn}:assistant`, "assistant", input.last_assistant_message);
|
|
15329
|
-
const
|
|
15330
|
-
|
|
15331
|
-
|
|
15332
|
-
|
|
15333
|
-
|
|
15334
|
-
|
|
15335
|
-
|
|
15336
|
-
|
|
15395
|
+
const paths = attempt(() => {
|
|
15396
|
+
const finish = closeTurn(dir, turn, place.root);
|
|
15397
|
+
if (finish)
|
|
15398
|
+
return finish();
|
|
15399
|
+
stopTurn(dir, turn);
|
|
15400
|
+
return [];
|
|
15401
|
+
});
|
|
15402
|
+
for (const p of paths ?? [])
|
|
15403
|
+
spool({ ...base, kind: "edit", event: null, path: p, via: "status" });
|
|
15337
15404
|
return { flush: true };
|
|
15338
15405
|
}
|
|
15339
15406
|
if (event === "PostToolUse") {
|
|
@@ -15393,7 +15460,7 @@ function readState() {
|
|
|
15393
15460
|
function lock() {
|
|
15394
15461
|
const file = path8.join(spoolDir(), ".lock");
|
|
15395
15462
|
fs7.mkdirSync(spoolDir(), { recursive: true, mode: 448 });
|
|
15396
|
-
for (let
|
|
15463
|
+
for (let attempt2 = 0;attempt2 < 2; attempt2++) {
|
|
15397
15464
|
try {
|
|
15398
15465
|
fs7.writeFileSync(file, String(process.pid), { flag: "wx", mode: 384 });
|
|
15399
15466
|
return () => {
|
package/dist/deliver.js
CHANGED
|
@@ -11806,32 +11806,103 @@ function captureNotice(file = dbFile()) {
|
|
|
11806
11806
|
return panel(`sphica: the database rejected ${plural(s.rejected, "record")}`, [rejectedDir(), `Move them back to ${spoolDir()} to resend`], "Fix them first, then check with sphica doctor");
|
|
11807
11807
|
return null;
|
|
11808
11808
|
}
|
|
11809
|
-
var
|
|
11810
|
-
|
|
11809
|
+
var turnDir = (host, session) => path6.join(baselineDir(), digest(`${host}\x00${session}`));
|
|
11810
|
+
var startFile = (dir, turn) => path6.join(dir, `${digest(turn)}.json`);
|
|
11811
|
+
var isStart = (v) => {
|
|
11812
|
+
const s = v;
|
|
11813
|
+
return !!s && typeof s === "object" && typeof s.turn === "string" && typeof s.running === "boolean" && (s.seq === null || Number.isSafeInteger(s.seq)) && (s.entries === null || typeof s.entries === "object" && !Array.isArray(s.entries)) && (s.head === null || typeof s.head === "string");
|
|
11814
|
+
};
|
|
11815
|
+
function readStart(file) {
|
|
11811
11816
|
try {
|
|
11812
|
-
|
|
11817
|
+
const s = JSON.parse(fs5.readFileSync(file, "utf8"));
|
|
11818
|
+
return isStart(s) ? s : null;
|
|
11813
11819
|
} catch {
|
|
11814
11820
|
return null;
|
|
11815
11821
|
}
|
|
11816
11822
|
}
|
|
11817
|
-
function
|
|
11818
|
-
|
|
11819
|
-
|
|
11820
|
-
|
|
11823
|
+
function readStarts(dir) {
|
|
11824
|
+
let names;
|
|
11825
|
+
try {
|
|
11826
|
+
names = fs5.readdirSync(dir);
|
|
11827
|
+
} catch (e) {
|
|
11828
|
+
return e.code === "ENOENT" ? [] : null;
|
|
11829
|
+
}
|
|
11830
|
+
const starts = [];
|
|
11831
|
+
for (const name of names) {
|
|
11832
|
+
if (name.startsWith("."))
|
|
11833
|
+
continue;
|
|
11834
|
+
const s = readStart(path6.join(dir, name));
|
|
11835
|
+
if (!s)
|
|
11836
|
+
return null;
|
|
11837
|
+
starts.push(s);
|
|
11838
|
+
}
|
|
11839
|
+
return starts;
|
|
11840
|
+
}
|
|
11841
|
+
function newestTurn(starts) {
|
|
11842
|
+
if (!starts?.length || starts.some((s) => s.seq === null))
|
|
11843
|
+
return null;
|
|
11844
|
+
const top = Math.max(...starts.map((s) => s.seq));
|
|
11845
|
+
const at = starts.filter((s) => s.seq === top);
|
|
11846
|
+
return at.length === 1 ? at[0]?.turn ?? null : null;
|
|
11847
|
+
}
|
|
11848
|
+
function writeStart(dir, s, file = startFile(dir, s.turn)) {
|
|
11849
|
+
fs5.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
11850
|
+
const tmp = path6.join(dir, `.${path6.basename(file)}.${process.pid}.tmp`);
|
|
11851
|
+
fs5.writeFileSync(tmp, JSON.stringify(s), { mode: 384 });
|
|
11821
11852
|
fs5.renameSync(tmp, file);
|
|
11822
11853
|
}
|
|
11854
|
+
function openTurn(dir, turn, root) {
|
|
11855
|
+
if (readStart(startFile(dir, turn))?.running)
|
|
11856
|
+
return null;
|
|
11857
|
+
const starts = readStarts(dir);
|
|
11858
|
+
const seq = starts && !starts.some((s) => s.seq === null) ? Math.max(0, ...starts.map((s) => s.seq)) + 1 : null;
|
|
11859
|
+
const now = snapshot(root);
|
|
11860
|
+
return () => writeStart(dir, { head: now?.head ?? null, entries: now?.entries ?? null, running: true, turn, seq });
|
|
11861
|
+
}
|
|
11862
|
+
function closeTurn(dir, turn, root) {
|
|
11863
|
+
const own = readStart(startFile(dir, turn));
|
|
11864
|
+
if (!own?.entries)
|
|
11865
|
+
return null;
|
|
11866
|
+
const before = { head: own.head, entries: own.entries };
|
|
11867
|
+
const now = snapshot(root);
|
|
11868
|
+
return () => {
|
|
11869
|
+
if (newestTurn(readStarts(dir)) !== turn || JSON.stringify(readStart(startFile(dir, turn))) !== JSON.stringify(own))
|
|
11870
|
+
return [];
|
|
11871
|
+
writeStart(dir, { ...own, ...now ?? {}, running: false });
|
|
11872
|
+
return now ? changed(root, before, now) : [];
|
|
11873
|
+
};
|
|
11874
|
+
}
|
|
11875
|
+
function stopTurn(dir, turn) {
|
|
11876
|
+
const own = readStart(startFile(dir, turn));
|
|
11877
|
+
if (own?.entries || own?.running)
|
|
11878
|
+
writeStart(dir, { ...own, head: null, entries: null, running: false });
|
|
11879
|
+
}
|
|
11823
11880
|
function pruneBaselines() {
|
|
11824
11881
|
const cutoff = Date.now() - HOLD_DAYS * 24 * 60 * 60 * 1000;
|
|
11825
|
-
|
|
11882
|
+
const fresh = (file) => (fs5.statSync(file, { throwIfNoEntry: false })?.mtimeMs ?? Date.now()) >= cutoff;
|
|
11883
|
+
let names;
|
|
11826
11884
|
try {
|
|
11827
|
-
|
|
11885
|
+
names = fs5.readdirSync(baselineDir());
|
|
11828
11886
|
} catch {
|
|
11829
11887
|
return;
|
|
11830
11888
|
}
|
|
11831
|
-
for (const
|
|
11832
|
-
const
|
|
11833
|
-
if (
|
|
11834
|
-
|
|
11889
|
+
for (const name of names) {
|
|
11890
|
+
const at = path6.join(baselineDir(), name);
|
|
11891
|
+
if (!fs5.statSync(at, { throwIfNoEntry: false })?.isDirectory()) {
|
|
11892
|
+
if (!fresh(at))
|
|
11893
|
+
fs5.rmSync(at, { force: true });
|
|
11894
|
+
} else {
|
|
11895
|
+
const files = fs5.readdirSync(at);
|
|
11896
|
+
if (files.length && !files.some((f) => fresh(path6.join(at, f))))
|
|
11897
|
+
fs5.rmSync(at, { recursive: true, force: true });
|
|
11898
|
+
}
|
|
11899
|
+
}
|
|
11900
|
+
}
|
|
11901
|
+
function attempt(fn) {
|
|
11902
|
+
try {
|
|
11903
|
+
return fn();
|
|
11904
|
+
} catch {
|
|
11905
|
+
return;
|
|
11835
11906
|
}
|
|
11836
11907
|
}
|
|
11837
11908
|
function onHook(host, input) {
|
|
@@ -11845,17 +11916,17 @@ function onHook(host, input) {
|
|
|
11845
11916
|
fs5.appendFileSync(file, `export SPHICA_PARENT_SESSION=${input.session_id}
|
|
11846
11917
|
`);
|
|
11847
11918
|
}
|
|
11848
|
-
|
|
11849
|
-
const now = place2 && snapshot(place2.root);
|
|
11850
|
-
if (now)
|
|
11851
|
-
writeBaseline(baselineFile(host, String(input.session_id)), { ...now, running: false });
|
|
11852
|
-
pruneBaselines();
|
|
11919
|
+
attempt(pruneBaselines);
|
|
11853
11920
|
return { flush: false, notice: captureNotice() };
|
|
11854
11921
|
}
|
|
11855
11922
|
if (!owner())
|
|
11856
11923
|
return { flush: false };
|
|
11857
|
-
if (event === "Interrupt")
|
|
11924
|
+
if (event === "Interrupt") {
|
|
11925
|
+
const turn2 = input.prompt_id ?? input.turn_id;
|
|
11926
|
+
if (turn2)
|
|
11927
|
+
attempt(() => stopTurn(turnDir(host, String(input.session_id)), turn2));
|
|
11858
11928
|
return { flush: true };
|
|
11929
|
+
}
|
|
11859
11930
|
const place = identify(input.cwd ?? process.cwd());
|
|
11860
11931
|
if (!place)
|
|
11861
11932
|
return { flush: false };
|
|
@@ -11879,30 +11950,26 @@ function onHook(host, input) {
|
|
|
11879
11950
|
const id = `${key}:${digest(kept.body)}`;
|
|
11880
11951
|
spool({ ...base, at: when, kind: "message", id, speaker, ...kept });
|
|
11881
11952
|
};
|
|
11882
|
-
const
|
|
11883
|
-
if (event === "UserPromptSubmit") {
|
|
11884
|
-
if (!readBaseline(baseline)?.running) {
|
|
11885
|
-
const now = snapshot(place.root);
|
|
11886
|
-
if (now)
|
|
11887
|
-
writeBaseline(baseline, { ...now, running: true });
|
|
11888
|
-
}
|
|
11889
|
-
}
|
|
11953
|
+
const dir = turnDir(host, base.session);
|
|
11890
11954
|
if (event === "UserPromptSubmit" && input.prompt) {
|
|
11891
11955
|
const prompt = input.prompt.trimStart();
|
|
11892
11956
|
if (!INJECTED.some((r) => r.test(prompt)))
|
|
11893
11957
|
say(`${turn}:owner`, "owner", prompt);
|
|
11894
11958
|
}
|
|
11959
|
+
if (event === "UserPromptSubmit")
|
|
11960
|
+
attempt(() => openTurn(dir, turn, place.root)?.());
|
|
11895
11961
|
if (event === "Stop") {
|
|
11896
11962
|
if (input.last_assistant_message)
|
|
11897
11963
|
say(`${turn}:assistant`, "assistant", input.last_assistant_message);
|
|
11898
|
-
const
|
|
11899
|
-
|
|
11900
|
-
|
|
11901
|
-
|
|
11902
|
-
|
|
11903
|
-
|
|
11904
|
-
|
|
11905
|
-
|
|
11964
|
+
const paths = attempt(() => {
|
|
11965
|
+
const finish = closeTurn(dir, turn, place.root);
|
|
11966
|
+
if (finish)
|
|
11967
|
+
return finish();
|
|
11968
|
+
stopTurn(dir, turn);
|
|
11969
|
+
return [];
|
|
11970
|
+
});
|
|
11971
|
+
for (const p of paths ?? [])
|
|
11972
|
+
spool({ ...base, kind: "edit", event: null, path: p, via: "status" });
|
|
11906
11973
|
return { flush: true };
|
|
11907
11974
|
}
|
|
11908
11975
|
if (event === "PostToolUse") {
|
|
@@ -11962,7 +12029,7 @@ function readState() {
|
|
|
11962
12029
|
function lock() {
|
|
11963
12030
|
const file = path6.join(spoolDir(), ".lock");
|
|
11964
12031
|
fs5.mkdirSync(spoolDir(), { recursive: true, mode: 448 });
|
|
11965
|
-
for (let
|
|
12032
|
+
for (let attempt2 = 0;attempt2 < 2; attempt2++) {
|
|
11966
12033
|
try {
|
|
11967
12034
|
fs5.writeFileSync(file, String(process.pid), { flag: "wx", mode: 384 });
|
|
11968
12035
|
return () => {
|
package/dist/mcp-record.js
CHANGED
|
@@ -46723,32 +46723,103 @@ function captureNotice(file2 = dbFile()) {
|
|
|
46723
46723
|
return panel(`sphica: the database rejected ${plural2(s.rejected, "record")}`, [rejectedDir(), `Move them back to ${spoolDir()} to resend`], "Fix them first, then check with sphica doctor");
|
|
46724
46724
|
return null;
|
|
46725
46725
|
}
|
|
46726
|
-
var
|
|
46727
|
-
|
|
46726
|
+
var turnDir = (host, session) => path6.join(baselineDir(), digest(`${host}\x00${session}`));
|
|
46727
|
+
var startFile = (dir, turn) => path6.join(dir, `${digest(turn)}.json`);
|
|
46728
|
+
var isStart = (v) => {
|
|
46729
|
+
const s = v;
|
|
46730
|
+
return !!s && typeof s === "object" && typeof s.turn === "string" && typeof s.running === "boolean" && (s.seq === null || Number.isSafeInteger(s.seq)) && (s.entries === null || typeof s.entries === "object" && !Array.isArray(s.entries)) && (s.head === null || typeof s.head === "string");
|
|
46731
|
+
};
|
|
46732
|
+
function readStart(file2) {
|
|
46728
46733
|
try {
|
|
46729
|
-
|
|
46734
|
+
const s = JSON.parse(fs6.readFileSync(file2, "utf8"));
|
|
46735
|
+
return isStart(s) ? s : null;
|
|
46730
46736
|
} catch {
|
|
46731
46737
|
return null;
|
|
46732
46738
|
}
|
|
46733
46739
|
}
|
|
46734
|
-
function
|
|
46735
|
-
|
|
46736
|
-
|
|
46737
|
-
|
|
46740
|
+
function readStarts(dir) {
|
|
46741
|
+
let names;
|
|
46742
|
+
try {
|
|
46743
|
+
names = fs6.readdirSync(dir);
|
|
46744
|
+
} catch (e) {
|
|
46745
|
+
return e.code === "ENOENT" ? [] : null;
|
|
46746
|
+
}
|
|
46747
|
+
const starts2 = [];
|
|
46748
|
+
for (const name of names) {
|
|
46749
|
+
if (name.startsWith("."))
|
|
46750
|
+
continue;
|
|
46751
|
+
const s = readStart(path6.join(dir, name));
|
|
46752
|
+
if (!s)
|
|
46753
|
+
return null;
|
|
46754
|
+
starts2.push(s);
|
|
46755
|
+
}
|
|
46756
|
+
return starts2;
|
|
46757
|
+
}
|
|
46758
|
+
function newestTurn(starts2) {
|
|
46759
|
+
if (!starts2?.length || starts2.some((s) => s.seq === null))
|
|
46760
|
+
return null;
|
|
46761
|
+
const top = Math.max(...starts2.map((s) => s.seq));
|
|
46762
|
+
const at = starts2.filter((s) => s.seq === top);
|
|
46763
|
+
return at.length === 1 ? at[0]?.turn ?? null : null;
|
|
46764
|
+
}
|
|
46765
|
+
function writeStart(dir, s, file2 = startFile(dir, s.turn)) {
|
|
46766
|
+
fs6.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
46767
|
+
const tmp = path6.join(dir, `.${path6.basename(file2)}.${process.pid}.tmp`);
|
|
46768
|
+
fs6.writeFileSync(tmp, JSON.stringify(s), { mode: 384 });
|
|
46738
46769
|
fs6.renameSync(tmp, file2);
|
|
46739
46770
|
}
|
|
46771
|
+
function openTurn(dir, turn, root) {
|
|
46772
|
+
if (readStart(startFile(dir, turn))?.running)
|
|
46773
|
+
return null;
|
|
46774
|
+
const starts2 = readStarts(dir);
|
|
46775
|
+
const seq = starts2 && !starts2.some((s) => s.seq === null) ? Math.max(0, ...starts2.map((s) => s.seq)) + 1 : null;
|
|
46776
|
+
const now = snapshot(root);
|
|
46777
|
+
return () => writeStart(dir, { head: now?.head ?? null, entries: now?.entries ?? null, running: true, turn, seq });
|
|
46778
|
+
}
|
|
46779
|
+
function closeTurn(dir, turn, root) {
|
|
46780
|
+
const own2 = readStart(startFile(dir, turn));
|
|
46781
|
+
if (!own2?.entries)
|
|
46782
|
+
return null;
|
|
46783
|
+
const before = { head: own2.head, entries: own2.entries };
|
|
46784
|
+
const now = snapshot(root);
|
|
46785
|
+
return () => {
|
|
46786
|
+
if (newestTurn(readStarts(dir)) !== turn || JSON.stringify(readStart(startFile(dir, turn))) !== JSON.stringify(own2))
|
|
46787
|
+
return [];
|
|
46788
|
+
writeStart(dir, { ...own2, ...now ?? {}, running: false });
|
|
46789
|
+
return now ? changed(root, before, now) : [];
|
|
46790
|
+
};
|
|
46791
|
+
}
|
|
46792
|
+
function stopTurn(dir, turn) {
|
|
46793
|
+
const own2 = readStart(startFile(dir, turn));
|
|
46794
|
+
if (own2?.entries || own2?.running)
|
|
46795
|
+
writeStart(dir, { ...own2, head: null, entries: null, running: false });
|
|
46796
|
+
}
|
|
46740
46797
|
function pruneBaselines() {
|
|
46741
46798
|
const cutoff = Date.now() - HOLD_DAYS * 24 * 60 * 60 * 1000;
|
|
46742
|
-
|
|
46799
|
+
const fresh = (file2) => (fs6.statSync(file2, { throwIfNoEntry: false })?.mtimeMs ?? Date.now()) >= cutoff;
|
|
46800
|
+
let names;
|
|
46743
46801
|
try {
|
|
46744
|
-
|
|
46802
|
+
names = fs6.readdirSync(baselineDir());
|
|
46745
46803
|
} catch {
|
|
46746
46804
|
return;
|
|
46747
46805
|
}
|
|
46748
|
-
for (const
|
|
46749
|
-
const
|
|
46750
|
-
if (
|
|
46751
|
-
|
|
46806
|
+
for (const name of names) {
|
|
46807
|
+
const at = path6.join(baselineDir(), name);
|
|
46808
|
+
if (!fs6.statSync(at, { throwIfNoEntry: false })?.isDirectory()) {
|
|
46809
|
+
if (!fresh(at))
|
|
46810
|
+
fs6.rmSync(at, { force: true });
|
|
46811
|
+
} else {
|
|
46812
|
+
const files = fs6.readdirSync(at);
|
|
46813
|
+
if (files.length && !files.some((f) => fresh(path6.join(at, f))))
|
|
46814
|
+
fs6.rmSync(at, { recursive: true, force: true });
|
|
46815
|
+
}
|
|
46816
|
+
}
|
|
46817
|
+
}
|
|
46818
|
+
function attempt(fn) {
|
|
46819
|
+
try {
|
|
46820
|
+
return fn();
|
|
46821
|
+
} catch {
|
|
46822
|
+
return;
|
|
46752
46823
|
}
|
|
46753
46824
|
}
|
|
46754
46825
|
function onHook(host, input2) {
|
|
@@ -46762,17 +46833,17 @@ function onHook(host, input2) {
|
|
|
46762
46833
|
fs6.appendFileSync(file2, `export SPHICA_PARENT_SESSION=${input2.session_id}
|
|
46763
46834
|
`);
|
|
46764
46835
|
}
|
|
46765
|
-
|
|
46766
|
-
const now = place2 && snapshot(place2.root);
|
|
46767
|
-
if (now)
|
|
46768
|
-
writeBaseline(baselineFile(host, String(input2.session_id)), { ...now, running: false });
|
|
46769
|
-
pruneBaselines();
|
|
46836
|
+
attempt(pruneBaselines);
|
|
46770
46837
|
return { flush: false, notice: captureNotice() };
|
|
46771
46838
|
}
|
|
46772
46839
|
if (!owner())
|
|
46773
46840
|
return { flush: false };
|
|
46774
|
-
if (event === "Interrupt")
|
|
46841
|
+
if (event === "Interrupt") {
|
|
46842
|
+
const turn2 = input2.prompt_id ?? input2.turn_id;
|
|
46843
|
+
if (turn2)
|
|
46844
|
+
attempt(() => stopTurn(turnDir(host, String(input2.session_id)), turn2));
|
|
46775
46845
|
return { flush: true };
|
|
46846
|
+
}
|
|
46776
46847
|
const place = identify(input2.cwd ?? process.cwd());
|
|
46777
46848
|
if (!place)
|
|
46778
46849
|
return { flush: false };
|
|
@@ -46796,30 +46867,26 @@ function onHook(host, input2) {
|
|
|
46796
46867
|
const id = `${key}:${digest(kept.body)}`;
|
|
46797
46868
|
spool({ ...base, at: when, kind: "message", id, speaker, ...kept });
|
|
46798
46869
|
};
|
|
46799
|
-
const
|
|
46800
|
-
if (event === "UserPromptSubmit") {
|
|
46801
|
-
if (!readBaseline(baseline)?.running) {
|
|
46802
|
-
const now = snapshot(place.root);
|
|
46803
|
-
if (now)
|
|
46804
|
-
writeBaseline(baseline, { ...now, running: true });
|
|
46805
|
-
}
|
|
46806
|
-
}
|
|
46870
|
+
const dir = turnDir(host, base.session);
|
|
46807
46871
|
if (event === "UserPromptSubmit" && input2.prompt) {
|
|
46808
46872
|
const prompt = input2.prompt.trimStart();
|
|
46809
46873
|
if (!INJECTED.some((r) => r.test(prompt)))
|
|
46810
46874
|
say(`${turn}:owner`, "owner", prompt);
|
|
46811
46875
|
}
|
|
46876
|
+
if (event === "UserPromptSubmit")
|
|
46877
|
+
attempt(() => openTurn(dir, turn, place.root)?.());
|
|
46812
46878
|
if (event === "Stop") {
|
|
46813
46879
|
if (input2.last_assistant_message)
|
|
46814
46880
|
say(`${turn}:assistant`, "assistant", input2.last_assistant_message);
|
|
46815
|
-
const
|
|
46816
|
-
|
|
46817
|
-
|
|
46818
|
-
|
|
46819
|
-
|
|
46820
|
-
|
|
46821
|
-
|
|
46822
|
-
|
|
46881
|
+
const paths = attempt(() => {
|
|
46882
|
+
const finish = closeTurn(dir, turn, place.root);
|
|
46883
|
+
if (finish)
|
|
46884
|
+
return finish();
|
|
46885
|
+
stopTurn(dir, turn);
|
|
46886
|
+
return [];
|
|
46887
|
+
});
|
|
46888
|
+
for (const p of paths ?? [])
|
|
46889
|
+
spool({ ...base, kind: "edit", event: null, path: p, via: "status" });
|
|
46823
46890
|
return { flush: true };
|
|
46824
46891
|
}
|
|
46825
46892
|
if (event === "PostToolUse") {
|
|
@@ -46879,7 +46946,7 @@ function readState() {
|
|
|
46879
46946
|
function lock() {
|
|
46880
46947
|
const file2 = path6.join(spoolDir(), ".lock");
|
|
46881
46948
|
fs6.mkdirSync(spoolDir(), { recursive: true, mode: 448 });
|
|
46882
|
-
for (let
|
|
46949
|
+
for (let attempt2 = 0;attempt2 < 2; attempt2++) {
|
|
46883
46950
|
try {
|
|
46884
46951
|
fs6.writeFileSync(file2, String(process.pid), { flag: "wx", mode: 384 });
|
|
46885
46952
|
return () => {
|
|
@@ -47810,7 +47877,6 @@ async function checkRecord(db, target, raw, facts = prepareRecord(target.root, r
|
|
|
47810
47877
|
];
|
|
47811
47878
|
const others = new Map((linked.length ? await db.selectFrom("unit").select(["id", "key", "kind", "lifecycle"]).where("project_id", "=", target.projectId).where("key", "in", linked).execute() : []).map((u) => [u.key, u]));
|
|
47812
47879
|
const holders = new Map((others.size ? await db.selectFrom("unit_link as l").innerJoin("unit as n", "n.id", "l.from_unit").select(["l.to_unit", "n.key", "n.lifecycle"]).where("l.kind", "=", "supersedes").where("l.to_unit", "in", [...others.values()].map((o) => o.id)).where("n.lifecycle", "<>", "withdrawn").where("n.extraction", "=", "supported").where("n.unsourced", "=", 0).execute() : []).map((h) => [h.to_unit, h]));
|
|
47813
|
-
const maintainers = new Set(record3.units.some((u) => u.supersedes || u.conflicts.length) ? (await db.selectFrom("source").select("author_login").distinct().where("project_id", "=", target.projectId).where("author_association", "in", [...MAINTAINERS]).execute()).flatMap((r) => r.author_login ? [r.author_login] : []) : []);
|
|
47814
47880
|
const units = [];
|
|
47815
47881
|
const claimed = new Set;
|
|
47816
47882
|
for (const [i, u] of record3.units.entries()) {
|
|
@@ -47866,7 +47932,7 @@ async function checkRecord(db, target, raw, facts = prepareRecord(target.root, r
|
|
|
47866
47932
|
if (target.origin !== "trace" && (u.supersedes || u.conflicts.length)) {
|
|
47867
47933
|
const trusted = [...u.evidence, ...u.adoption].some((q) => {
|
|
47868
47934
|
const s = sources.get(Number(q.source.slice(1)));
|
|
47869
|
-
return s && (s.kind === "session_message" || s.author_kind === "owner" || MAINTAINERS.has(s.author_association ?? "")
|
|
47935
|
+
return s && (s.kind === "session_message" || s.author_kind === "owner" || MAINTAINERS.has(s.author_association ?? ""));
|
|
47870
47936
|
});
|
|
47871
47937
|
if (!trusted)
|
|
47872
47938
|
errors3.push(`${key}: supersedes and conflicts from ${target.origin} need the owner's or a maintainer's words, or the owner's session`);
|
|
@@ -49488,7 +49554,7 @@ var reply = (t, isError = false) => ({
|
|
|
49488
49554
|
...isError ? { isError: true } : {}
|
|
49489
49555
|
});
|
|
49490
49556
|
async function projectOf(cwd, meta3) {
|
|
49491
|
-
const workspace = process.env.CLAUDE_PROJECT_DIR
|
|
49557
|
+
const workspace = hostWorkspace(meta3) || process.env.CLAUDE_PROJECT_DIR;
|
|
49492
49558
|
if (!workspace)
|
|
49493
49559
|
throw new Error("The host did not say which workspace this session is in, so nothing is written (update Claude Code or Codex)");
|
|
49494
49560
|
const place = writePlace(workspace, cwd);
|
package/hooks/hooks.json
CHANGED
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"args": [
|
|
50
50
|
"${CLAUDE_PLUGIN_ROOT}/dist/capture.js"
|
|
51
51
|
],
|
|
52
|
-
"
|
|
52
|
+
"timeout": 30
|
|
53
53
|
}
|
|
54
54
|
]
|
|
55
55
|
},
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"args": [
|
|
91
91
|
"${CLAUDE_PLUGIN_ROOT}/dist/capture.js"
|
|
92
92
|
],
|
|
93
|
-
"
|
|
93
|
+
"timeout": 30
|
|
94
94
|
}
|
|
95
95
|
]
|
|
96
96
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sphica",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.29",
|
|
4
4
|
"description": "Records Claude Code and Codex sessions on your machine and keeps past implementation and decisions, with their sources, for your agent to find.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|