sphica 0.6.35 → 0.6.36
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/README.md +1 -1
- package/db/migrations/0011.sql +26 -0
- package/db/schema.sql +16 -2
- package/dist/capture.js +3 -1
- package/dist/cli.js +5 -57
- package/dist/deliver.js +3 -1
- package/dist/mcp-record.js +74 -71
- package/dist/mcp.js +1 -1
- 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.36",
|
|
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/README.md
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
-- Revision 10 → 11 of generation 2: a harvest run keeps the sources it may cite, chosen when it begins (harvest_run_source). A running
|
|
2
|
+
-- harvest has none, so it is removed and its session gets "Begin again"; nothing cites a running run (a save also finishes it).
|
|
3
|
+
-- Every statement that stays is the same as in the schema at revision 11.
|
|
4
|
+
|
|
5
|
+
create temp table sphica_migration_note (rule text, item text, action text);
|
|
6
|
+
|
|
7
|
+
create table harvest_run_source (
|
|
8
|
+
run_id integer not null references extraction_run (id) on delete cascade,
|
|
9
|
+
source_id integer not null references source (id) on delete cascade,
|
|
10
|
+
primary key (run_id, source_id)
|
|
11
|
+
) strict;
|
|
12
|
+
create index harvest_run_source_source on harvest_run_source (source_id);
|
|
13
|
+
|
|
14
|
+
create trigger harvest_run_source_run before insert on harvest_run_source begin
|
|
15
|
+
select raise(abort, 'only a harvest run keeps sources, from its own project')
|
|
16
|
+
where (select origin from extraction_run where id = new.run_id) is not 'harvest'
|
|
17
|
+
or (select project_id from source where id = new.source_id) is not (select project_id from extraction_run where id = new.run_id);
|
|
18
|
+
end;
|
|
19
|
+
|
|
20
|
+
insert into sphica_migration_note
|
|
21
|
+
select 'a harvest run still running, begun before its sources were kept', 'run ' || id || ' ' || target || ' begun ' || started_at,
|
|
22
|
+
'removed; begin the harvest again'
|
|
23
|
+
from extraction_run where origin = 'harvest' and status = 'running' order by id;
|
|
24
|
+
delete from extraction_run where origin = 'harvest' and status = 'running';
|
|
25
|
+
|
|
26
|
+
pragma user_version = 11;
|
package/db/schema.sql
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
-- captured sources session, source, artifact_link, edit_observation: what was said or written, never rewritten
|
|
7
7
|
-- (the owner can forget chosen sources: forget_batch and source_forgotten keep what was removed, without its text)
|
|
8
8
|
-- extracted units unit and its option, evidence, adoption, link, state, anchor, alias tables: what was decided or implemented
|
|
9
|
-
-- processing extraction_run, source_processing: what has been looked at and saved, so gaps are counted
|
|
9
|
+
-- processing extraction_run, source_processing, harvest_run_source: what has been looked at and saved, so gaps are counted
|
|
10
10
|
-- work and delivery work, delivery, delivery_unit: the current work status and what the hooks injected
|
|
11
11
|
-- Every table is STRICT and every primary key is not null. Times are ISO 8601 UTC (`Date#toISOString()`); `strftime(...) is column` rejects others.
|
|
12
12
|
-- Paths (a source's, an edit observation's, an anchor's) are repository-relative with forward slashes, in one form, so one place
|
|
@@ -293,6 +293,15 @@ create table source_processing (
|
|
|
293
293
|
) strict;
|
|
294
294
|
create index source_processing_run on source_processing (run_id);
|
|
295
295
|
|
|
296
|
+
-- The sources a harvest run may show and cite, chosen when it begins, so another harvest of the same pull request cannot change them.
|
|
297
|
+
-- Ids only; a source the owner forgets leaves with its row
|
|
298
|
+
create table harvest_run_source (
|
|
299
|
+
run_id integer not null references extraction_run (id) on delete cascade,
|
|
300
|
+
source_id integer not null references source (id) on delete cascade,
|
|
301
|
+
primary key (run_id, source_id)
|
|
302
|
+
) strict;
|
|
303
|
+
create index harvest_run_source_source on harvest_run_source (source_id);
|
|
304
|
+
|
|
296
305
|
-- An extracted unit. Its text is never rewritten: corrections are successors, withdrawals, retractions, and anchor replacements.
|
|
297
306
|
-- extraction: supported (every evidence span was found in retained text) or quarantined (with reason).
|
|
298
307
|
-- lifecycle changes only through unit_state (its trigger sets this column). revision rises with every change to the unit's relations,
|
|
@@ -981,6 +990,11 @@ create trigger source_processing_project before insert on source_processing begi
|
|
|
981
990
|
select raise(abort, 'source and run belong to different projects')
|
|
982
991
|
where (select project_id from source where id = new.source_id) is not (select project_id from extraction_run where id = new.run_id);
|
|
983
992
|
end;
|
|
993
|
+
create trigger harvest_run_source_run before insert on harvest_run_source begin
|
|
994
|
+
select raise(abort, 'only a harvest run keeps sources, from its own project')
|
|
995
|
+
where (select origin from extraction_run where id = new.run_id) is not 'harvest'
|
|
996
|
+
or (select project_id from source where id = new.source_id) is not (select project_id from extraction_run where id = new.run_id);
|
|
997
|
+
end;
|
|
984
998
|
|
|
985
999
|
-- Every change to a unit's relations raises its revision (stale drafts are refused against it)
|
|
986
1000
|
create trigger unit_rev_evidence_i after insert on unit_evidence begin update unit set revision = revision + 1 where id = new.unit_id; end;
|
|
@@ -1211,4 +1225,4 @@ create trigger capture_delivery_prune_insert instead of insert on capture_delive
|
|
|
1211
1225
|
order by d.at, d.id limit 200);
|
|
1212
1226
|
end;
|
|
1213
1227
|
|
|
1214
|
-
pragma user_version =
|
|
1228
|
+
pragma user_version = 11;
|
package/dist/capture.js
CHANGED
|
@@ -11396,7 +11396,7 @@ var HOST_MARKS = new Set([".orphaned_at", ".in_use"]);
|
|
|
11396
11396
|
|
|
11397
11397
|
// server/src/sqlite.ts
|
|
11398
11398
|
var SCHEMA_GENERATION = 2;
|
|
11399
|
-
var SCHEMA_REVISION =
|
|
11399
|
+
var SCHEMA_REVISION = 11;
|
|
11400
11400
|
var sphicaHome = () => process.env.SPHICA_HOME || path2.join(os.homedir(), ".sphica");
|
|
11401
11401
|
var dbFile = () => process.env.SPHICA_DB || path2.join(sphicaHome(), "sphica.db");
|
|
11402
11402
|
function requireRuntime() {
|
|
@@ -11597,6 +11597,7 @@ var INGEST_INSERTS = new Set([
|
|
|
11597
11597
|
"ingest_source",
|
|
11598
11598
|
"extraction_run",
|
|
11599
11599
|
"source_processing",
|
|
11600
|
+
"harvest_run_source",
|
|
11600
11601
|
"unit",
|
|
11601
11602
|
"unit_option",
|
|
11602
11603
|
"unit_evidence",
|
|
@@ -11692,6 +11693,7 @@ var FORGET_WRITES = {
|
|
|
11692
11693
|
"unit_evidence",
|
|
11693
11694
|
"unit_adoption",
|
|
11694
11695
|
"source_processing",
|
|
11696
|
+
"harvest_run_source",
|
|
11695
11697
|
"source_fts",
|
|
11696
11698
|
"field_def",
|
|
11697
11699
|
"unit_field",
|
package/dist/cli.js
CHANGED
|
@@ -93,7 +93,6 @@ var require_src = __commonJS(function(exports, module) {
|
|
|
93
93
|
|
|
94
94
|
// server/src/cli.ts
|
|
95
95
|
import fs10 from "node:fs";
|
|
96
|
-
import os4 from "node:os";
|
|
97
96
|
import path10 from "node:path";
|
|
98
97
|
|
|
99
98
|
// server/node_modules/@clack/core/dist/index.mjs
|
|
@@ -4912,7 +4911,7 @@ ${indent}${caution(note)}` : ""}`);
|
|
|
4912
4911
|
|
|
4913
4912
|
// server/src/sqlite.ts
|
|
4914
4913
|
var SCHEMA_GENERATION = 2;
|
|
4915
|
-
var SCHEMA_REVISION =
|
|
4914
|
+
var SCHEMA_REVISION = 11;
|
|
4916
4915
|
var sphicaHome = () => process.env.SPHICA_HOME || path3.join(os2.homedir(), ".sphica");
|
|
4917
4916
|
var dbFile = () => process.env.SPHICA_DB || path3.join(sphicaHome(), "sphica.db");
|
|
4918
4917
|
function requireRuntime() {
|
|
@@ -15957,6 +15956,7 @@ var INGEST_INSERTS = new Set([
|
|
|
15957
15956
|
"ingest_source",
|
|
15958
15957
|
"extraction_run",
|
|
15959
15958
|
"source_processing",
|
|
15959
|
+
"harvest_run_source",
|
|
15960
15960
|
"unit",
|
|
15961
15961
|
"unit_option",
|
|
15962
15962
|
"unit_evidence",
|
|
@@ -16052,6 +16052,7 @@ var FORGET_WRITES = {
|
|
|
16052
16052
|
"unit_evidence",
|
|
16053
16053
|
"unit_adoption",
|
|
16054
16054
|
"source_processing",
|
|
16055
|
+
"harvest_run_source",
|
|
16055
16056
|
"source_fts",
|
|
16056
16057
|
"field_def",
|
|
16057
16058
|
"unit_field",
|
|
@@ -16120,7 +16121,6 @@ var sessionId = (projectId, host, externalId) => uuidFrom(String(projectId), hos
|
|
|
16120
16121
|
// server/src/project.ts
|
|
16121
16122
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
16122
16123
|
import fs6 from "node:fs";
|
|
16123
|
-
import os3 from "node:os";
|
|
16124
16124
|
import path6 from "node:path";
|
|
16125
16125
|
|
|
16126
16126
|
// server/src/anchors.ts
|
|
@@ -16348,48 +16348,6 @@ function nameLocal(dir, name) {
|
|
|
16348
16348
|
});
|
|
16349
16349
|
return { key: `local:${name}`, root, name };
|
|
16350
16350
|
}
|
|
16351
|
-
function underHome(p, homes, api = path6) {
|
|
16352
|
-
for (const h2 of homes) {
|
|
16353
|
-
const rel = api.relative(h2, p);
|
|
16354
|
-
if (rel === "")
|
|
16355
|
-
return "~";
|
|
16356
|
-
if (rel !== ".." && !rel.startsWith(`..${api.sep}`) && !api.isAbsolute(rel))
|
|
16357
|
-
return `~${api.sep}${rel}`;
|
|
16358
|
-
}
|
|
16359
|
-
return p;
|
|
16360
|
-
}
|
|
16361
|
-
var projectsDir = () => path6.join(os3.homedir(), "Projects");
|
|
16362
|
-
function localRoots(roots = [projectsDir()]) {
|
|
16363
|
-
const seen = new Map;
|
|
16364
|
-
const add = (p) => {
|
|
16365
|
-
if (p)
|
|
16366
|
-
seen.set(p.key, [...new Set([...seen.get(p.key) ?? [], p.root])]);
|
|
16367
|
-
};
|
|
16368
|
-
for (const r2 of roots) {
|
|
16369
|
-
let entries = [];
|
|
16370
|
-
try {
|
|
16371
|
-
entries = fs6.readdirSync(r2, { withFileTypes: true });
|
|
16372
|
-
} catch {
|
|
16373
|
-
continue;
|
|
16374
|
-
}
|
|
16375
|
-
for (const e of entries)
|
|
16376
|
-
if (e.isDirectory() && !e.name.startsWith("."))
|
|
16377
|
-
add(identify(path6.join(r2, e.name)));
|
|
16378
|
-
}
|
|
16379
|
-
for (const [root, name] of Object.entries(localMap())) {
|
|
16380
|
-
if (LOCAL_KEY.test(name) && fs6.existsSync(root))
|
|
16381
|
-
add({ key: `local:${name}`, root, name });
|
|
16382
|
-
}
|
|
16383
|
-
const found = new Map;
|
|
16384
|
-
const ambiguous = new Map;
|
|
16385
|
-
for (const [key, dirs] of seen) {
|
|
16386
|
-
if (dirs.length === 1 && dirs[0])
|
|
16387
|
-
found.set(key, dirs[0]);
|
|
16388
|
-
else
|
|
16389
|
-
ambiguous.set(key, dirs);
|
|
16390
|
-
}
|
|
16391
|
-
return { found, ambiguous };
|
|
16392
|
-
}
|
|
16393
16351
|
function relativeTo(root, file, cwd = root) {
|
|
16394
16352
|
const abs = path6.resolve(cwd, file);
|
|
16395
16353
|
const rel = path6.relative(root, abs);
|
|
@@ -18769,14 +18727,7 @@ async function doctor(cwd) {
|
|
|
18769
18727
|
if (twice.length)
|
|
18770
18728
|
say2("warn", "Records", `${plural(twice.length, "set")} of live records hold the same words. Read them with Sphica's read and withdraw the extra ones with /sphica:glean`);
|
|
18771
18729
|
await adoptionStops(db, q.waiting, say2);
|
|
18772
|
-
const { found, ambiguous } = localRoots();
|
|
18773
|
-
const homes = [os4.homedir()];
|
|
18774
|
-
try {
|
|
18775
|
-
homes.push(fs10.realpathSync.native(os4.homedir()));
|
|
18776
|
-
} catch {}
|
|
18777
|
-
const tilde = (p) => underHome(p, homes);
|
|
18778
18730
|
const rows = await db.selectFrom("project as p").select((eb) => [
|
|
18779
|
-
"p.key",
|
|
18780
18731
|
"p.name",
|
|
18781
18732
|
eb.selectFrom("unit as u").select((u3) => u3.fn.countAll().as("n")).whereRef("u.project_id", "=", "p.id").as("records"),
|
|
18782
18733
|
eb.selectFrom("extraction_run as r").select((r2) => r2.fn.max("r.finished_at").as("at")).whereRef("r.project_id", "=", "p.id").where("r.status", "=", "saved").where("r.origin", "<>", "migration").as("extracted")
|
|
@@ -18784,11 +18735,8 @@ async function doctor(cwd) {
|
|
|
18784
18735
|
if (rows.length)
|
|
18785
18736
|
console.log(section("Projects", true));
|
|
18786
18737
|
const column = Math.max(...rows.map((x) => width(inline(x.name)))) + 2;
|
|
18787
|
-
for (const x of rows)
|
|
18788
|
-
|
|
18789
|
-
const where = copies ? ` (${copies.length} copies: ${copies.map((c) => inline(tilde(c))).sort().join(", ")})` : found.get(x.key) ? "" : ` (not found in ${inline(tilde(projectsDir()))} or the named projects)`;
|
|
18790
|
-
console.log(indent(` ${mark("none")} ${pad(inline(x.name), column)}${plural(Number(x.records ?? 0), "record")}${x.extracted ? ` / last extraction ${new Date(x.extracted).toLocaleString("sv-SE")}` : ""}${where}`));
|
|
18791
|
-
}
|
|
18738
|
+
for (const x of rows)
|
|
18739
|
+
console.log(indent(` ${mark("none")} ${pad(inline(x.name), column)}${plural(Number(x.records ?? 0), "record")}${x.extracted ? ` / last extraction ${new Date(x.extracted).toLocaleString("sv-SE")}` : ""}`));
|
|
18792
18740
|
});
|
|
18793
18741
|
} catch (e) {
|
|
18794
18742
|
say2("fail", "DB", `cannot read: ${plain(reason(e))}`);
|
package/dist/deliver.js
CHANGED
|
@@ -11427,7 +11427,7 @@ var HOST_MARKS = new Set([".orphaned_at", ".in_use"]);
|
|
|
11427
11427
|
|
|
11428
11428
|
// server/src/sqlite.ts
|
|
11429
11429
|
var SCHEMA_GENERATION = 2;
|
|
11430
|
-
var SCHEMA_REVISION =
|
|
11430
|
+
var SCHEMA_REVISION = 11;
|
|
11431
11431
|
var sphicaHome = () => process.env.SPHICA_HOME || path3.join(os.homedir(), ".sphica");
|
|
11432
11432
|
var dbFile = () => process.env.SPHICA_DB || path3.join(sphicaHome(), "sphica.db");
|
|
11433
11433
|
function requireRuntime() {
|
|
@@ -11652,6 +11652,7 @@ var INGEST_INSERTS = new Set([
|
|
|
11652
11652
|
"ingest_source",
|
|
11653
11653
|
"extraction_run",
|
|
11654
11654
|
"source_processing",
|
|
11655
|
+
"harvest_run_source",
|
|
11655
11656
|
"unit",
|
|
11656
11657
|
"unit_option",
|
|
11657
11658
|
"unit_evidence",
|
|
@@ -11747,6 +11748,7 @@ var FORGET_WRITES = {
|
|
|
11747
11748
|
"unit_evidence",
|
|
11748
11749
|
"unit_adoption",
|
|
11749
11750
|
"source_processing",
|
|
11751
|
+
"harvest_run_source",
|
|
11750
11752
|
"source_fts",
|
|
11751
11753
|
"field_def",
|
|
11752
11754
|
"unit_field",
|
package/dist/mcp-record.js
CHANGED
|
@@ -46183,7 +46183,7 @@ var HOST_MARKS = new Set([".orphaned_at", ".in_use"]);
|
|
|
46183
46183
|
|
|
46184
46184
|
// server/src/sqlite.ts
|
|
46185
46185
|
var SCHEMA_GENERATION = 2;
|
|
46186
|
-
var SCHEMA_REVISION =
|
|
46186
|
+
var SCHEMA_REVISION = 11;
|
|
46187
46187
|
var sphicaHome = () => process.env.SPHICA_HOME || path2.join(os.homedir(), ".sphica");
|
|
46188
46188
|
var dbFile = () => process.env.SPHICA_DB || path2.join(sphicaHome(), "sphica.db");
|
|
46189
46189
|
function requireRuntime() {
|
|
@@ -46384,6 +46384,7 @@ var INGEST_INSERTS = new Set([
|
|
|
46384
46384
|
"ingest_source",
|
|
46385
46385
|
"extraction_run",
|
|
46386
46386
|
"source_processing",
|
|
46387
|
+
"harvest_run_source",
|
|
46387
46388
|
"unit",
|
|
46388
46389
|
"unit_option",
|
|
46389
46390
|
"unit_evidence",
|
|
@@ -46479,6 +46480,7 @@ var FORGET_WRITES = {
|
|
|
46479
46480
|
"unit_evidence",
|
|
46480
46481
|
"unit_adoption",
|
|
46481
46482
|
"source_processing",
|
|
46483
|
+
"harvest_run_source",
|
|
46482
46484
|
"source_fts",
|
|
46483
46485
|
"field_def",
|
|
46484
46486
|
"unit_field",
|
|
@@ -47787,6 +47789,7 @@ var item = (v) => ({
|
|
|
47787
47789
|
commit: null,
|
|
47788
47790
|
...v
|
|
47789
47791
|
});
|
|
47792
|
+
var said = (body) => body?.trim() ? body : "";
|
|
47790
47793
|
var sha = (s) => s && /^[0-9a-f]{40}$/.test(s) ? s : null;
|
|
47791
47794
|
var cleanPath = (p) => p && !p.startsWith("/") && !p.includes("\\") && !/\p{Cc}/u.test(p) && !p.split("/").some((x) => x === "" || x === "." || x === "..") ? p : null;
|
|
47792
47795
|
async function readPull(get, number5, repo = null) {
|
|
@@ -47807,52 +47810,49 @@ async function readPull(get, number5, repo = null) {
|
|
|
47807
47810
|
association: p.author_association ?? null,
|
|
47808
47811
|
url: p.html_url,
|
|
47809
47812
|
createdAt: p.created_at,
|
|
47810
|
-
text: p.body
|
|
47813
|
+
text: said(p.body)
|
|
47811
47814
|
}));
|
|
47812
47815
|
for (const c of comments)
|
|
47813
|
-
|
|
47814
|
-
|
|
47815
|
-
|
|
47816
|
-
|
|
47817
|
-
|
|
47818
|
-
|
|
47819
|
-
|
|
47820
|
-
|
|
47821
|
-
|
|
47822
|
-
|
|
47823
|
-
}));
|
|
47816
|
+
items.push(item({
|
|
47817
|
+
kind: "pr_comment",
|
|
47818
|
+
artifact,
|
|
47819
|
+
externalId: `comment:${c.id}`,
|
|
47820
|
+
author: c.user ?? null,
|
|
47821
|
+
association: c.author_association ?? null,
|
|
47822
|
+
url: c.html_url,
|
|
47823
|
+
createdAt: c.created_at,
|
|
47824
|
+
text: said(c.body)
|
|
47825
|
+
}));
|
|
47824
47826
|
for (const r of reviews)
|
|
47825
|
-
|
|
47826
|
-
|
|
47827
|
-
|
|
47828
|
-
|
|
47829
|
-
|
|
47830
|
-
|
|
47831
|
-
|
|
47832
|
-
|
|
47833
|
-
|
|
47834
|
-
|
|
47835
|
-
|
|
47836
|
-
|
|
47837
|
-
|
|
47838
|
-
|
|
47839
|
-
|
|
47840
|
-
|
|
47841
|
-
|
|
47842
|
-
|
|
47843
|
-
|
|
47844
|
-
|
|
47845
|
-
|
|
47846
|
-
|
|
47847
|
-
|
|
47848
|
-
|
|
47849
|
-
|
|
47850
|
-
|
|
47851
|
-
|
|
47852
|
-
|
|
47853
|
-
|
|
47854
|
-
}));
|
|
47855
|
-
}
|
|
47827
|
+
items.push(item({
|
|
47828
|
+
kind: "review",
|
|
47829
|
+
artifact,
|
|
47830
|
+
externalId: `review:${r.id}`,
|
|
47831
|
+
author: r.user ?? null,
|
|
47832
|
+
association: r.author_association ?? null,
|
|
47833
|
+
url: r.html_url,
|
|
47834
|
+
createdAt: r.submitted_at ?? p.created_at,
|
|
47835
|
+
text: said(r.body)
|
|
47836
|
+
}));
|
|
47837
|
+
for (const c of reviewComments) {
|
|
47838
|
+
const end = c.line ?? null;
|
|
47839
|
+
const start = c.start_line && c.start_line <= (end ?? 0) && (c.start_side ?? c.side) === c.side ? c.start_line : end;
|
|
47840
|
+
items.push(item({
|
|
47841
|
+
kind: "review_comment",
|
|
47842
|
+
artifact,
|
|
47843
|
+
externalId: `review_comment:${c.id}`,
|
|
47844
|
+
author: c.user ?? null,
|
|
47845
|
+
association: c.author_association ?? null,
|
|
47846
|
+
parent: c.in_reply_to_id ? `review_comment:${c.in_reply_to_id}` : null,
|
|
47847
|
+
url: c.html_url,
|
|
47848
|
+
createdAt: c.created_at,
|
|
47849
|
+
text: said(c.body),
|
|
47850
|
+
path: cleanPath(c.path),
|
|
47851
|
+
lines: cleanPath(c.path) && end && start ? [start, end] : null,
|
|
47852
|
+
hunk: c.diff_hunk ?? null,
|
|
47853
|
+
commit: sha(c.commit_id)
|
|
47854
|
+
}));
|
|
47855
|
+
}
|
|
47856
47856
|
for (const c of commits)
|
|
47857
47857
|
items.push(item({
|
|
47858
47858
|
kind: "commit_message",
|
|
@@ -47885,29 +47885,27 @@ async function readIssue(get, n) {
|
|
|
47885
47885
|
const issue2 = await get(`issues/${n}`);
|
|
47886
47886
|
if (issue2.pull_request)
|
|
47887
47887
|
return items;
|
|
47888
|
-
|
|
47888
|
+
items.push(item({
|
|
47889
|
+
kind: "issue_body",
|
|
47890
|
+
artifact: `issue:${n}`,
|
|
47891
|
+
externalId: `issue:${n}`,
|
|
47892
|
+
author: issue2.user ?? null,
|
|
47893
|
+
association: issue2.author_association ?? null,
|
|
47894
|
+
url: issue2.html_url,
|
|
47895
|
+
createdAt: issue2.created_at,
|
|
47896
|
+
text: said(issue2.body)
|
|
47897
|
+
}));
|
|
47898
|
+
for (const c of await get(`issues/${n}/comments?per_page=100`, true))
|
|
47889
47899
|
items.push(item({
|
|
47890
|
-
kind: "
|
|
47900
|
+
kind: "issue_comment",
|
|
47891
47901
|
artifact: `issue:${n}`,
|
|
47892
|
-
externalId: `
|
|
47893
|
-
author:
|
|
47894
|
-
association:
|
|
47895
|
-
url:
|
|
47896
|
-
createdAt:
|
|
47897
|
-
text:
|
|
47902
|
+
externalId: `comment:${c.id}`,
|
|
47903
|
+
author: c.user ?? null,
|
|
47904
|
+
association: c.author_association ?? null,
|
|
47905
|
+
url: c.html_url,
|
|
47906
|
+
createdAt: c.created_at,
|
|
47907
|
+
text: said(c.body)
|
|
47898
47908
|
}));
|
|
47899
|
-
for (const c of await get(`issues/${n}/comments?per_page=100`, true))
|
|
47900
|
-
if (c.body?.trim())
|
|
47901
|
-
items.push(item({
|
|
47902
|
-
kind: "issue_comment",
|
|
47903
|
-
artifact: `issue:${n}`,
|
|
47904
|
-
externalId: `comment:${c.id}`,
|
|
47905
|
-
author: c.user ?? null,
|
|
47906
|
-
association: c.author_association ?? null,
|
|
47907
|
-
url: c.html_url,
|
|
47908
|
-
createdAt: c.created_at,
|
|
47909
|
-
text: c.body
|
|
47910
|
-
}));
|
|
47911
47909
|
return items;
|
|
47912
47910
|
}
|
|
47913
47911
|
function githubTarget(repo, url2) {
|
|
@@ -47996,12 +47994,16 @@ async function linkIssues(db, projectId2, number5, closes) {
|
|
|
47996
47994
|
kind: "closes"
|
|
47997
47995
|
}).onConflict((oc) => oc.doNothing()).execute();
|
|
47998
47996
|
}
|
|
47999
|
-
async function
|
|
47997
|
+
async function pullSourceIds(db, projectId2, number5) {
|
|
48000
47998
|
const artifacts = [
|
|
48001
47999
|
`pr:${number5}`,
|
|
48002
48000
|
...(await db.selectFrom("artifact_link").select("to_artifact").where("project_id", "=", projectId2).where("from_artifact", "=", `pr:${number5}`).execute()).map((l) => l.to_artifact)
|
|
48003
48001
|
];
|
|
48004
|
-
|
|
48002
|
+
const rows = await db.selectFrom("source as s").where("s.project_id", "=", projectId2).where("s.artifact", "in", artifacts).where(({ not, exists, selectFrom }) => not(exists(selectFrom("source as n").select("n.id").whereRef("n.project_id", "=", "s.project_id").whereRef("n.kind", "=", "s.kind").whereRef("n.external_id", "=", "s.external_id").whereRef("n.revision", ">", "s.revision")))).where(({ not, exists, selectFrom }) => not(exists(selectFrom("source_forgotten as f").select("f.source_id").whereRef("f.project_id", "=", "s.project_id").whereRef("f.kind", "=", "s.kind").whereRef("f.external_id", "=", "s.external_id").whereRef("f.revision", ">", "s.revision")))).select("s.id").execute();
|
|
48003
|
+
return rows.map((r) => r.id);
|
|
48004
|
+
}
|
|
48005
|
+
async function runSources(db, runId) {
|
|
48006
|
+
return db.selectFrom("harvest_run_source as h").innerJoin("source as s", "s.id", "h.source_id").where("h.run_id", "=", runId).where(({ not, exists, selectFrom }) => not(exists(selectFrom("source_forgotten as f").select("f.source_id").whereRef("f.project_id", "=", "s.project_id").whereRef("f.kind", "=", "s.kind").whereRef("f.external_id", "=", "s.external_id").whereRef("f.revision", ">", "s.revision")))).select([
|
|
48005
48007
|
"s.id",
|
|
48006
48008
|
"s.kind",
|
|
48007
48009
|
"s.artifact",
|
|
@@ -48010,7 +48012,6 @@ async function pullSources(db, projectId2, number5) {
|
|
|
48010
48012
|
"s.author_login",
|
|
48011
48013
|
"s.author_association",
|
|
48012
48014
|
"s.created_at",
|
|
48013
|
-
"s.captured_at",
|
|
48014
48015
|
"s.path",
|
|
48015
48016
|
"s.line_start",
|
|
48016
48017
|
"s.text"
|
|
@@ -49495,8 +49496,8 @@ async function checkGlean(db, target, raw, facts = prepareGlean(target.root, raw
|
|
|
49495
49496
|
if (op.op === "replace_anchor") {
|
|
49496
49497
|
if (!repoPath(op.to.path))
|
|
49497
49498
|
errors3.push(`${what}: the path is not inside the repository`);
|
|
49498
|
-
const
|
|
49499
|
-
if (
|
|
49499
|
+
const said2 = await span(op.source, op.quote, what);
|
|
49500
|
+
if (said2 && said2.s.author_kind !== "owner")
|
|
49500
49501
|
errors3.push(`${what}: only the owner's words can move an anchor`);
|
|
49501
49502
|
const from = repoPath(op.from.path);
|
|
49502
49503
|
if (!from)
|
|
@@ -49974,7 +49975,7 @@ async function beginHarvest(db, projectId2, number5, get, beginCall) {
|
|
|
49974
49975
|
const kept = (await storeItems(trx, projectId2, pull.items)).filter((id) => id !== null).length;
|
|
49975
49976
|
await linkIssues(trx, projectId2, number5, pull.closes);
|
|
49976
49977
|
const run = newRunId();
|
|
49977
|
-
await openRun(trx, {
|
|
49978
|
+
const runId = await openRun(trx, {
|
|
49978
49979
|
projectId: projectId2,
|
|
49979
49980
|
origin: "harvest",
|
|
49980
49981
|
target: `pr:${number5}`,
|
|
@@ -49982,6 +49983,8 @@ async function beginHarvest(db, projectId2, number5, get, beginCall) {
|
|
|
49982
49983
|
draftId: run,
|
|
49983
49984
|
beginCall
|
|
49984
49985
|
});
|
|
49986
|
+
for (const sourceId of await pullSourceIds(trx, projectId2, number5))
|
|
49987
|
+
await trx.insertInto("harvest_run_source").values({ run_id: runId, source_id: sourceId }).execute();
|
|
49985
49988
|
return { run, sources: kept };
|
|
49986
49989
|
});
|
|
49987
49990
|
}
|
|
@@ -50036,7 +50039,7 @@ async function agentRun(db, run, call) {
|
|
|
50036
50039
|
async function scopeOf(db, run, root) {
|
|
50037
50040
|
if (run.origin === "harvest") {
|
|
50038
50041
|
const number5 = Number(run.target.slice("pr:".length));
|
|
50039
|
-
const sources2 =
|
|
50042
|
+
const sources2 = await runSources(db, run.id);
|
|
50040
50043
|
return {
|
|
50041
50044
|
target: {
|
|
50042
50045
|
projectId: run.project_id,
|
package/dist/mcp.js
CHANGED
|
@@ -46004,7 +46004,7 @@ var HOST_MARKS = new Set([".orphaned_at", ".in_use"]);
|
|
|
46004
46004
|
|
|
46005
46005
|
// server/src/sqlite.ts
|
|
46006
46006
|
var SCHEMA_GENERATION = 2;
|
|
46007
|
-
var SCHEMA_REVISION =
|
|
46007
|
+
var SCHEMA_REVISION = 11;
|
|
46008
46008
|
var sphicaHome = () => process.env.SPHICA_HOME || path2.join(os.homedir(), ".sphica");
|
|
46009
46009
|
var dbFile = () => process.env.SPHICA_DB || path2.join(sphicaHome(), "sphica.db");
|
|
46010
46010
|
function requireRuntime() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sphica",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.36",
|
|
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",
|