sphica 0.6.4 → 0.6.5
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/db/migrations/0002.sql +1 -1
- package/db/migrations/0003.sql +1 -1
- package/db/schema.sql +1 -1
- package/dist/deliver.js +22 -4
- package/dist/mcp-record.js +42 -16
- package/dist/mcp.js +35 -11
- package/package.json +1 -1
- package/skills/forget/SKILL.md +1 -1
- package/skills/glean/SKILL.md +1 -1
- package/skills/harvest/SKILL.md +1 -1
- package/skills/review/SKILL.md +1 -0
- package/skills/rules/SKILL.md +1 -1
- package/skills/trace/SKILL.md +3 -2
|
@@ -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.5",
|
|
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/db/migrations/0002.sql
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
-- Revision 1 → 2 of generation 2: the owner can forget chosen sources
|
|
1
|
+
-- Revision 1 → 2 of generation 2: the owner can forget chosen sources.
|
|
2
2
|
-- `sphica init` runs this in one transaction with foreign keys off (set outside the transaction), then checks foreign_key_check before
|
|
3
3
|
-- committing. Every statement matches db/schema.sql at revision 2; server/test/migrate.test.ts compares a migrated database with a fresh one.
|
|
4
4
|
|
package/db/migrations/0003.sql
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
-- Revision 2 → 3 of generation 2: a rejected option can carry a reconsider condition the owner stated, quoted as `reconsiders` evidence
|
|
1
|
+
-- Revision 2 → 3 of generation 2: a rejected option can carry a reconsider condition the owner stated, quoted as `reconsiders` evidence.
|
|
2
2
|
-- `sphica init` runs this in one transaction with foreign keys off (set outside the transaction), then checks foreign_key_check before
|
|
3
3
|
-- committing. Every statement matches db/schema.sql at revision 3; server/test/migrate.test.ts compares a migrated database with a fresh one.
|
|
4
4
|
|
package/db/schema.sql
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
-- The source of truth for Sphica's database (SQLite, `node:sqlite`): the memory of past implementation and decisions for one owner on one machine.
|
|
2
|
-
-- Generation 2
|
|
2
|
+
-- Generation 2. `sphica_generation` holds the generation; `pragma user_version` is the revision within it.
|
|
3
3
|
-- A database of another generation is refused without being changed.
|
|
4
4
|
--
|
|
5
5
|
-- Four boundaries:
|
package/dist/deliver.js
CHANGED
|
@@ -31689,10 +31689,28 @@ function localChange(root, args) {
|
|
|
31689
31689
|
return { base, files, digest: sha256(read.join("\x00")).toString("hex") };
|
|
31690
31690
|
}
|
|
31691
31691
|
|
|
31692
|
+
// server/src/trace.ts
|
|
31693
|
+
var PENDING_DAYS = 30;
|
|
31694
|
+
var pendingCutoff = (now) => new Date(now.getTime() - PENDING_DAYS * 86400000).toISOString();
|
|
31695
|
+
var untracedOwner = (eb) => eb.selectFrom("source as m").whereRef("m.session_id", "=", "s.id").where("m.author_kind", "=", "owner").where(({ not, exists, selectFrom }) => not(exists(selectFrom("source_processing as p").whereRef("p.source_id", "=", "m.id").select(sql`1`.as("x")))));
|
|
31696
|
+
var untracedSessions = (db, projectId2) => db.selectFrom("session as s").where("s.project_id", "=", projectId2).where((eb) => eb.exists(untracedOwner(eb).select(sql`1`.as("x")))).select((eb) => [
|
|
31697
|
+
"s.id",
|
|
31698
|
+
"s.host",
|
|
31699
|
+
"s.started_at",
|
|
31700
|
+
"s.branch",
|
|
31701
|
+
eb.selectFrom("source as o").whereRef("o.session_id", "=", "s.id").where("o.author_kind", "=", "owner").select((o) => o.fn.max("o.created_at").as("last")).as("last"),
|
|
31702
|
+
untracedOwner(eb).select((m) => m.fn.countAll().as("waiting")).as("waiting"),
|
|
31703
|
+
untracedOwner(eb).select((m) => m.fn.min("m.id").as("first")).as("first")
|
|
31704
|
+
]);
|
|
31705
|
+
|
|
31692
31706
|
// server/src/status.ts
|
|
31693
|
-
async function pendingCount(db, projectId2) {
|
|
31694
|
-
const
|
|
31695
|
-
|
|
31707
|
+
async function pendingCount(db, projectId2, now = new Date) {
|
|
31708
|
+
const cutoff = pendingCutoff(now);
|
|
31709
|
+
const r = await db.selectFrom(untracedSessions(db, projectId2).as("w")).select([
|
|
31710
|
+
sql`count(case when w.last >= ${cutoff} then 1 end)`.as("recent"),
|
|
31711
|
+
sql`count(case when w.last < ${cutoff} then 1 end)`.as("older")
|
|
31712
|
+
]).executeTakeFirst();
|
|
31713
|
+
return { recent: Number(r?.recent ?? 0), older: Number(r?.older ?? 0) };
|
|
31696
31714
|
}
|
|
31697
31715
|
|
|
31698
31716
|
// server/src/deliver.ts
|
|
@@ -31887,7 +31905,7 @@ async function onPrompt(db, projectId2, prompt) {
|
|
|
31887
31905
|
async function waiting(db, projectId2, place) {
|
|
31888
31906
|
if (!place.owner)
|
|
31889
31907
|
return "";
|
|
31890
|
-
const n = await pendingCount(db, projectId2);
|
|
31908
|
+
const n = (await pendingCount(db, projectId2)).recent;
|
|
31891
31909
|
if (!n)
|
|
31892
31910
|
return "";
|
|
31893
31911
|
const day = new Date().toLocaleDateString("sv-SE");
|
package/dist/mcp-record.js
CHANGED
|
@@ -48172,15 +48172,26 @@ ${c.errors.map((e) => ` ${e}`).join(`
|
|
|
48172
48172
|
}
|
|
48173
48173
|
|
|
48174
48174
|
// server/src/trace.ts
|
|
48175
|
-
|
|
48176
|
-
|
|
48177
|
-
|
|
48178
|
-
|
|
48179
|
-
|
|
48180
|
-
|
|
48181
|
-
|
|
48182
|
-
|
|
48183
|
-
|
|
48175
|
+
var PENDING_DAYS = 30;
|
|
48176
|
+
var pendingCutoff = (now) => new Date(now.getTime() - PENDING_DAYS * 86400000).toISOString();
|
|
48177
|
+
var untracedOwner = (eb) => eb.selectFrom("source as m").whereRef("m.session_id", "=", "s.id").where("m.author_kind", "=", "owner").where(({ not, exists, selectFrom }) => not(exists(selectFrom("source_processing as p").whereRef("p.source_id", "=", "m.id").select(sql`1`.as("x")))));
|
|
48178
|
+
var untracedSessions = (db, projectId2) => db.selectFrom("session as s").where("s.project_id", "=", projectId2).where((eb) => eb.exists(untracedOwner(eb).select(sql`1`.as("x")))).select((eb) => [
|
|
48179
|
+
"s.id",
|
|
48180
|
+
"s.host",
|
|
48181
|
+
"s.started_at",
|
|
48182
|
+
"s.branch",
|
|
48183
|
+
eb.selectFrom("source as o").whereRef("o.session_id", "=", "s.id").where("o.author_kind", "=", "owner").select((o) => o.fn.max("o.created_at").as("last")).as("last"),
|
|
48184
|
+
untracedOwner(eb).select((m) => m.fn.countAll().as("waiting")).as("waiting"),
|
|
48185
|
+
untracedOwner(eb).select((m) => m.fn.min("m.id").as("first")).as("first")
|
|
48186
|
+
]);
|
|
48187
|
+
async function pendingSessions(db, projectId2, group, now = new Date, limit = 20) {
|
|
48188
|
+
const cutoff = pendingCutoff(now);
|
|
48189
|
+
const base = db.selectFrom(untracedSessions(db, projectId2).as("w")).where("w.last", group === "recent" ? ">=" : "<", cutoff);
|
|
48190
|
+
const [rows, total] = await Promise.all([
|
|
48191
|
+
base.selectAll("w").orderBy("w.last", "desc").limit(limit).execute(),
|
|
48192
|
+
base.select((eb) => eb.fn.countAll().as("n")).executeTakeFirst()
|
|
48193
|
+
]);
|
|
48194
|
+
return { rows, total: Number(total?.n ?? 0) };
|
|
48184
48195
|
}
|
|
48185
48196
|
async function openRun(db, v) {
|
|
48186
48197
|
const r = await db.insertInto("extraction_run").values({
|
|
@@ -48219,15 +48230,30 @@ async function liveUnits(db, projectId2, limit = 40) {
|
|
|
48219
48230
|
|
|
48220
48231
|
// server/src/extract.ts
|
|
48221
48232
|
var newRunId = () => crypto2.randomBytes(9).toString("base64url");
|
|
48222
|
-
async function pendingText(db, projectId2) {
|
|
48233
|
+
async function pendingText(db, projectId2, now = new Date) {
|
|
48223
48234
|
await flush(undefined, TOOL_FLUSH_BUDGET_MS).catch(() => {});
|
|
48224
|
-
const
|
|
48225
|
-
|
|
48235
|
+
const [recent, older] = await Promise.all([
|
|
48236
|
+
pendingSessions(db, projectId2, "recent", now),
|
|
48237
|
+
pendingSessions(db, projectId2, "older", now)
|
|
48238
|
+
]);
|
|
48239
|
+
if (!recent.total && !older.total)
|
|
48226
48240
|
return "Every captured session has been traced.";
|
|
48227
|
-
const
|
|
48241
|
+
const ids = [...recent.rows, ...older.rows].map((r) => Number(r.first));
|
|
48242
|
+
const firsts = new Map(ids.length ? (await db.selectFrom("source").select(["id", "text"]).where("id", "in", ids).execute()).map((m) => [
|
|
48243
|
+
m.id,
|
|
48244
|
+
m.text
|
|
48245
|
+
]) : []);
|
|
48246
|
+
const group = (g) => [
|
|
48247
|
+
...g.rows.map((r) => `- ${r.id} ${r.host} ${r.started_at}: ${plural2(Number(r.waiting), "message")} waiting, starting "${inline(firsts.get(Number(r.first)) ?? "").slice(0, 100)}"`),
|
|
48248
|
+
...g.total > g.rows.length ? [`- and ${g.total - g.rows.length} more`] : []
|
|
48249
|
+
];
|
|
48228
48250
|
return [
|
|
48229
|
-
`${plural2(
|
|
48230
|
-
...
|
|
48251
|
+
recent.total ? `${plural2(recent.total, "session")} to trace (pass the id to trace_begin):` : "No recent session waits to be traced.",
|
|
48252
|
+
...group(recent),
|
|
48253
|
+
...older.total ? [
|
|
48254
|
+
`Older than ${PENDING_DAYS} days (not counted at session start), ${plural2(older.total, "session")}; trace_begin takes these ids too:`,
|
|
48255
|
+
...group(older)
|
|
48256
|
+
] : []
|
|
48231
48257
|
].join(`
|
|
48232
48258
|
`);
|
|
48233
48259
|
}
|
|
@@ -48637,7 +48663,7 @@ var READ = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, o
|
|
|
48637
48663
|
var RECORD = exports_external.record(exports_external.string(), exports_external.unknown()).describe("The record, as the Skill describes");
|
|
48638
48664
|
server.registerTool("trace_pending", {
|
|
48639
48665
|
title: "Sessions not traced yet",
|
|
48640
|
-
description: "Lists this project's captured sessions with owner messages no trace has looked at.",
|
|
48666
|
+
description: "Lists this project's captured sessions with owner messages no trace has looked at, then apart those whose last owner message is over 30 days old.",
|
|
48641
48667
|
inputSchema: { cwd: CWD },
|
|
48642
48668
|
annotations: READ
|
|
48643
48669
|
}, async (a, extra) => tool(async () => pendingText(conn(), (await projectOf(a.cwd, extra._meta)).projectId)));
|
package/dist/mcp.js
CHANGED
|
@@ -46932,19 +46932,37 @@ async function checkFindings(db, projectId2, files, raw) {
|
|
|
46932
46932
|
return problems;
|
|
46933
46933
|
}
|
|
46934
46934
|
|
|
46935
|
+
// server/src/trace.ts
|
|
46936
|
+
var PENDING_DAYS = 30;
|
|
46937
|
+
var pendingCutoff = (now) => new Date(now.getTime() - PENDING_DAYS * 86400000).toISOString();
|
|
46938
|
+
var untracedOwner = (eb) => eb.selectFrom("source as m").whereRef("m.session_id", "=", "s.id").where("m.author_kind", "=", "owner").where(({ not, exists, selectFrom }) => not(exists(selectFrom("source_processing as p").whereRef("p.source_id", "=", "m.id").select(sql`1`.as("x")))));
|
|
46939
|
+
var untracedSessions = (db, projectId2) => db.selectFrom("session as s").where("s.project_id", "=", projectId2).where((eb) => eb.exists(untracedOwner(eb).select(sql`1`.as("x")))).select((eb) => [
|
|
46940
|
+
"s.id",
|
|
46941
|
+
"s.host",
|
|
46942
|
+
"s.started_at",
|
|
46943
|
+
"s.branch",
|
|
46944
|
+
eb.selectFrom("source as o").whereRef("o.session_id", "=", "s.id").where("o.author_kind", "=", "owner").select((o) => o.fn.max("o.created_at").as("last")).as("last"),
|
|
46945
|
+
untracedOwner(eb).select((m) => m.fn.countAll().as("waiting")).as("waiting"),
|
|
46946
|
+
untracedOwner(eb).select((m) => m.fn.min("m.id").as("first")).as("first")
|
|
46947
|
+
]);
|
|
46948
|
+
|
|
46935
46949
|
// server/src/status.ts
|
|
46936
|
-
async function pendingCount(db, projectId2) {
|
|
46937
|
-
const
|
|
46938
|
-
|
|
46939
|
-
}
|
|
46940
|
-
|
|
46950
|
+
async function pendingCount(db, projectId2, now = new Date) {
|
|
46951
|
+
const cutoff = pendingCutoff(now);
|
|
46952
|
+
const r = await db.selectFrom(untracedSessions(db, projectId2).as("w")).select([
|
|
46953
|
+
sql`count(case when w.last >= ${cutoff} then 1 end)`.as("recent"),
|
|
46954
|
+
sql`count(case when w.last < ${cutoff} then 1 end)`.as("older")
|
|
46955
|
+
]).executeTakeFirst();
|
|
46956
|
+
return { recent: Number(r?.recent ?? 0), older: Number(r?.older ?? 0) };
|
|
46957
|
+
}
|
|
46958
|
+
async function coverage(db, projectId2, now) {
|
|
46941
46959
|
const count = async (q) => Number((await q)?.n ?? 0);
|
|
46942
46960
|
const units = (where) => count(where(unitBase()).select((eb) => eb.fn.countAll().as("n")).executeTakeFirst());
|
|
46943
46961
|
const unitBase = () => db.selectFrom("unit").where("project_id", "=", projectId2);
|
|
46944
46962
|
const processed = (outcome) => db.selectFrom("source_processing as p").innerJoin("source as m", "m.id", "p.source_id").where("m.project_id", "=", projectId2).where("p.outcome", "=", outcome).where("m.session_id", "is not", null).select("m.session_id");
|
|
46945
|
-
const [sessions,
|
|
46963
|
+
const [sessions, pending, emptySessions, sources, active, candidates, quarantined, work] = await Promise.all([
|
|
46946
46964
|
count(db.selectFrom("session").where("project_id", "=", projectId2).select((eb) => eb.fn.countAll().as("n")).executeTakeFirst()),
|
|
46947
|
-
pendingCount(db, projectId2),
|
|
46965
|
+
pendingCount(db, projectId2, now),
|
|
46948
46966
|
count(db.selectFrom("session").where("id", "in", processed("no_unit")).where("id", "not in", processed("units")).select((eb) => eb.fn.countAll().as("n")).executeTakeFirst()),
|
|
46949
46967
|
count(db.selectFrom("source").where("project_id", "=", projectId2).select((eb) => eb.fn.countAll().as("n")).executeTakeFirst()),
|
|
46950
46968
|
units((q) => q.where("lifecycle", "=", "active")),
|
|
@@ -46952,15 +46970,21 @@ async function coverage(db, projectId2) {
|
|
|
46952
46970
|
units((q) => q.where("extraction", "=", "quarantined")),
|
|
46953
46971
|
db.selectFrom("work").where("project_id", "=", projectId2).where("status", "in", ["active", "blocked", "paused"]).orderBy("updated_at", "desc").select(["title", "current", "status"]).limit(5).execute()
|
|
46954
46972
|
]);
|
|
46955
|
-
return { sessions,
|
|
46973
|
+
return { sessions, pending, emptySessions, sources, active, candidates, quarantined, work };
|
|
46956
46974
|
}
|
|
46957
|
-
async function status(db, projectId2, name) {
|
|
46958
|
-
const c = await coverage(db, projectId2);
|
|
46975
|
+
async function status(db, projectId2, name, now = new Date) {
|
|
46976
|
+
const c = await coverage(db, projectId2, now);
|
|
46959
46977
|
const lines = [
|
|
46960
46978
|
`${name}`,
|
|
46961
46979
|
`Captured: ${plural2(c.sessions, "session")}, ${plural2(c.sources, "source")}.`,
|
|
46962
46980
|
`Extracted: ${plural2(c.active, "active record")}, ${plural2(c.candidates, "candidate")} not active yet (waiting for adoption or evidence), ${plural2(c.quarantined, "quarantined record")}.`,
|
|
46963
|
-
c.
|
|
46981
|
+
...c.pending.recent ? [
|
|
46982
|
+
`${plural2(c.pending.recent, "session")} not traced yet: their decisions exist only as captured text (run /sphica:trace pending).`
|
|
46983
|
+
] : [],
|
|
46984
|
+
...c.pending.older ? [
|
|
46985
|
+
`${c.pending.older} older ${c.pending.older === 1 ? "session" : "sessions"} (last owner message over ${PENDING_DAYS} days ago) not traced; /sphica:trace pending lists them.`
|
|
46986
|
+
] : [],
|
|
46987
|
+
...c.pending.recent || c.pending.older ? [] : ["Every captured session has been traced."],
|
|
46964
46988
|
...c.emptySessions ? [`${plural2(c.emptySessions, "session")} traced with nothing to record.`] : [],
|
|
46965
46989
|
c.work.length ? `Work in progress:
|
|
46966
46990
|
${framed(c.work.map((w) => `- ${head(inline(w.title), 200)} (${w.status}): ${head(inline(w.current), 500)}`).join(`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sphica",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
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",
|
package/skills/forget/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: forget
|
|
|
3
3
|
description: Removes sources the owner chooses from Sphica (a message, a pull request item, a file excerpt), with their search index entries and the bytes left in the database file, and judges the records that cited them again. The owner confirms in a dialog before anything is removed. Use only when the user explicitly asks to forget or delete something Sphica captured.
|
|
4
4
|
argument-hint: "<what to forget>"
|
|
5
5
|
disable-model-invocation: true
|
|
6
|
-
allowed-tools: AskUserQuestion, mcp__plugin_sphica_sphica__search, mcp__plugin_sphica_sphica__read, mcp__plugin_sphica_record__forget_preview, mcp__plugin_sphica_record__forget_apply
|
|
6
|
+
allowed-tools: AskUserQuestion, mcp__plugin_sphica_sphica__search, mcp__plugin_sphica_sphica__read, mcp__plugin_sphica_record__forget_preview, mcp__plugin_sphica_record__forget_apply, mcp__plugin_sphica_sphica__status
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# forget — remove what should not have been kept
|
package/skills/glean/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: glean
|
|
|
3
3
|
description: Adds evidence and corrections to existing Sphica records, or keeps something the owner remembers, only from sources the owner points to (an issue or pull request, a file in the repository, the owner's own words now). Asks the owner for the source before saving anything. Use only when the user explicitly asks.
|
|
4
4
|
argument-hint: "<what to add or correct>"
|
|
5
5
|
disable-model-invocation: true
|
|
6
|
-
allowed-tools: AskUserQuestion, mcp__plugin_sphica_record__glean_begin, mcp__plugin_sphica_record__glean_fetch, mcp__plugin_sphica_record__record_context, mcp__plugin_sphica_record__record_check, mcp__plugin_sphica_record__record_save, mcp__plugin_sphica_sphica__search, mcp__plugin_sphica_sphica__read
|
|
6
|
+
allowed-tools: AskUserQuestion, mcp__plugin_sphica_record__glean_begin, mcp__plugin_sphica_record__glean_fetch, mcp__plugin_sphica_record__record_context, mcp__plugin_sphica_record__record_check, mcp__plugin_sphica_record__record_save, mcp__plugin_sphica_sphica__search, mcp__plugin_sphica_sphica__read, mcp__plugin_sphica_sphica__status
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# glean — add what was found later, with its source
|
package/skills/harvest/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: harvest
|
|
|
3
3
|
description: Reads one GitHub pull request of the current repository (its body, comments, reviews, review comments, commits, the merge, and the issues it closes), keeps them as sources, and extracts what it decided and implemented into records that quote them, in the same form as trace. Pass the PR number. Use only when the user explicitly asks.
|
|
4
4
|
argument-hint: "<PR number>"
|
|
5
5
|
disable-model-invocation: true
|
|
6
|
-
allowed-tools: mcp__plugin_sphica_record__harvest_begin, mcp__plugin_sphica_record__record_context, mcp__plugin_sphica_record__record_check, mcp__plugin_sphica_record__record_save, mcp__plugin_sphica_sphica__search, mcp__plugin_sphica_sphica__read
|
|
6
|
+
allowed-tools: mcp__plugin_sphica_record__harvest_begin, mcp__plugin_sphica_record__record_context, mcp__plugin_sphica_record__record_check, mcp__plugin_sphica_record__record_save, mcp__plugin_sphica_sphica__search, mcp__plugin_sphica_sphica__read, mcp__plugin_sphica_sphica__status
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# harvest — keep what a pull request decided and implemented
|
package/skills/review/SKILL.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: review
|
|
3
3
|
description: Reviews changes. Use it to review your own committed and uncommitted diff, to review someone else's PR, and to sweep for misses before a merge or release. It starts independent reviewers per aspect, and when Codex is available it runs the same aspects on the other model too, to catch defects only one model can see. Findings are ruled on by reproduction before they are returned. It does not handle formatting or naming inconsistencies, design preferences, or future extensibility.
|
|
4
|
+
allowed-tools: mcp__plugin_sphica_sphica__status, mcp__plugin_sphica_sphica__search, mcp__plugin_sphica_sphica__read, mcp__plugin_sphica_sphica__review_select, mcp__plugin_sphica_sphica__review_check
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# review — sweep changes with independent reviewers
|
package/skills/rules/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: rules
|
|
|
3
3
|
description: Drafts lines for CLAUDE.md, AGENTS.md, or .claude/rules from recorded constraints and decisions the owner picks, each line ending with a marker holding its record key, so Sphica's overview (view look) can flag the line once the record is replaced or withdrawn. It prints the draft and never edits a file. Use only when the user explicitly asks for rule text from Sphica's records.
|
|
4
4
|
argument-hint: "<which constraints, or empty to choose from the list>"
|
|
5
5
|
disable-model-invocation: true
|
|
6
|
-
allowed-tools: mcp__plugin_sphica_sphica__overview, mcp__plugin_sphica_sphica__search, mcp__plugin_sphica_sphica__read
|
|
6
|
+
allowed-tools: mcp__plugin_sphica_sphica__overview, mcp__plugin_sphica_sphica__search, mcp__plugin_sphica_sphica__read, mcp__plugin_sphica_sphica__status
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# rules — draft instruction lines from recorded constraints
|
package/skills/trace/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: trace
|
|
|
3
3
|
description: Extracts what a coding session decided and implemented (decisions and rejected options, constraints, implementations, findings, dead ends, open questions) into records whose every claim quotes the captured conversation, so a later session can find them. With "pending", lists this project's sessions not traced yet. Use only when the user explicitly asks.
|
|
4
4
|
argument-hint: "[pending]"
|
|
5
5
|
disable-model-invocation: true
|
|
6
|
-
allowed-tools: mcp__plugin_sphica_record__trace_pending, mcp__plugin_sphica_record__trace_begin, mcp__plugin_sphica_record__record_context, mcp__plugin_sphica_record__record_check, mcp__plugin_sphica_record__record_save, mcp__plugin_sphica_sphica__search, mcp__plugin_sphica_sphica__read
|
|
6
|
+
allowed-tools: AskUserQuestion, mcp__plugin_sphica_record__trace_pending, mcp__plugin_sphica_record__trace_begin, mcp__plugin_sphica_record__record_context, mcp__plugin_sphica_record__record_check, mcp__plugin_sphica_record__record_save, mcp__plugin_sphica_sphica__search, mcp__plugin_sphica_sphica__read, mcp__plugin_sphica_sphica__status
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# trace — keep what a session decided and implemented
|
|
@@ -30,7 +30,8 @@ Everything goes through Sphica's `record` MCP server (its tools are `trace_pendi
|
|
|
30
30
|
|
|
31
31
|
1. **Pick the session.** Without a target, it is this session: its id is `${CLAUDE_SESSION_ID}` in Claude Code; in Codex, read `CODEX_THREAD_ID`
|
|
32
32
|
from your shell environment. With `pending`, call `trace_pending`, show the owner the list, and ask which to trace (AskUserQuestion in
|
|
33
|
-
Claude Code).
|
|
33
|
+
Claude Code). Sessions whose last owner message is over 30 days old come last under their own heading: session start does not count
|
|
34
|
+
them, but they can still be traced. Trace one session at a time
|
|
34
35
|
2. **Begin**: `trace_begin` with that `session`. It returns a `run` id bound to that session and this project; the record never names them
|
|
35
36
|
3. **Read**: `record_context` with the run. It prints each captured message as `## s<N> owner|assistant <turn> <time>` followed by its text,
|
|
36
37
|
the edits observed, and the project's live records. `(traced before)` marks messages an earlier trace already looked at.
|