@reposkein/mcp 0.2.7 → 0.4.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 +73 -1
- package/binary-digests.json +4 -4
- package/dist/SKILL.md +63 -1
- package/dist/cli/adr.d.ts +37 -0
- package/dist/cli/adr.js +231 -0
- package/dist/cli/adr.js.map +1 -0
- package/dist/cli/agentAdapters.d.ts +68 -0
- package/dist/cli/agentAdapters.js +223 -0
- package/dist/cli/agentAdapters.js.map +1 -0
- package/dist/cli/ansi.d.ts +24 -0
- package/dist/cli/ansi.js +40 -0
- package/dist/cli/ansi.js.map +1 -0
- package/dist/cli/doctor.d.ts +34 -2
- package/dist/cli/doctor.js +53 -5
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/doctorDecisions.d.ts +5 -0
- package/dist/cli/doctorDecisions.js +83 -0
- package/dist/cli/doctorDecisions.js.map +1 -0
- package/dist/cli/doctorFreshness.d.ts +43 -0
- package/dist/cli/doctorFreshness.js +111 -0
- package/dist/cli/doctorFreshness.js.map +1 -0
- package/dist/cli/doctorSummaries.d.ts +2 -0
- package/dist/cli/doctorSummaries.js +95 -0
- package/dist/cli/doctorSummaries.js.map +1 -0
- package/dist/cli/federatedDiscovery.d.ts +16 -0
- package/dist/cli/federatedDiscovery.js +75 -0
- package/dist/cli/federatedDiscovery.js.map +1 -0
- package/dist/cli/init.d.ts +69 -6
- package/dist/cli/init.js +194 -28
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/sourceSlices.d.ts +17 -0
- package/dist/cli/sourceSlices.js +59 -0
- package/dist/cli/sourceSlices.js.map +1 -0
- package/dist/cli/stats.d.ts +29 -0
- package/dist/cli/stats.js +180 -0
- package/dist/cli/stats.js.map +1 -0
- package/dist/cli/view.d.ts +71 -3
- package/dist/cli/view.js +147 -12
- package/dist/cli/view.js.map +1 -1
- package/dist/guard/decisionValidation.d.ts +29 -0
- package/dist/guard/decisionValidation.js +51 -0
- package/dist/guard/decisionValidation.js.map +1 -0
- package/dist/guard/summaryValidation.d.ts +3 -0
- package/dist/guard/summaryValidation.js +5 -3
- package/dist/guard/summaryValidation.js.map +1 -1
- package/dist/index.d.ts +48 -0
- package/dist/index.js +355 -78
- package/dist/index.js.map +1 -1
- package/dist/indexer/decisionsAffected.d.ts +42 -0
- package/dist/indexer/decisionsAffected.js +149 -0
- package/dist/indexer/decisionsAffected.js.map +1 -0
- package/dist/indexer/fetchBinary.d.ts +14 -0
- package/dist/indexer/fetchBinary.js +81 -40
- package/dist/indexer/fetchBinary.js.map +1 -1
- package/dist/indexer/runIndexer.d.ts +3 -2
- package/dist/indexer/runIndexer.js +8 -4
- package/dist/indexer/runIndexer.js.map +1 -1
- package/dist/profile/decisions.d.ts +37 -0
- package/dist/profile/decisions.js +112 -0
- package/dist/profile/decisions.js.map +1 -0
- package/dist/profile/types.d.ts +8 -0
- package/dist/store/GraphStore.d.ts +5 -0
- package/dist/store/JsonlGraphStore.d.ts +14 -3
- package/dist/store/JsonlGraphStore.js +66 -19
- package/dist/store/JsonlGraphStore.js.map +1 -1
- package/dist/store/decisions.d.ts +131 -0
- package/dist/store/decisions.js +321 -0
- package/dist/store/decisions.js.map +1 -0
- package/dist/store/indexedAt.d.ts +18 -0
- package/dist/store/indexedAt.js +56 -0
- package/dist/store/indexedAt.js.map +1 -0
- package/dist/store/instrumentTool.d.ts +40 -0
- package/dist/store/instrumentTool.js +82 -0
- package/dist/store/instrumentTool.js.map +1 -0
- package/dist/store/repoContextCache.d.ts +15 -0
- package/dist/store/repoContextCache.js +26 -0
- package/dist/store/repoContextCache.js.map +1 -0
- package/dist/store/repoSession.d.ts +63 -0
- package/dist/store/repoSession.js +123 -0
- package/dist/store/repoSession.js.map +1 -0
- package/dist/store/resolveRepoPath.d.ts +31 -0
- package/dist/store/resolveRepoPath.js +85 -0
- package/dist/store/resolveRepoPath.js.map +1 -0
- package/dist/store/sessionLog.d.ts +98 -0
- package/dist/store/sessionLog.js +216 -0
- package/dist/store/sessionLog.js.map +1 -0
- package/dist/store/sessionStats.d.ts +55 -0
- package/dist/store/sessionStats.js +140 -0
- package/dist/store/sessionStats.js.map +1 -0
- package/dist/store/sidecar.d.ts +39 -3
- package/dist/store/sidecar.js +89 -5
- package/dist/store/sidecar.js.map +1 -1
- package/dist/store/summaryShards.d.ts +117 -0
- package/dist/store/summaryShards.js +291 -0
- package/dist/store/summaryShards.js.map +1 -0
- package/dist/store/teamConfig.d.ts +13 -0
- package/dist/store/teamConfig.js +49 -0
- package/dist/store/teamConfig.js.map +1 -0
- package/dist/tools/getContextProfile.d.ts +1 -1
- package/dist/tools/getContextProfile.js +9 -1
- package/dist/tools/getContextProfile.js.map +1 -1
- package/dist/tools/getDecision.d.ts +6 -0
- package/dist/tools/getDecision.js +59 -0
- package/dist/tools/getDecision.js.map +1 -0
- package/dist/tools/impact.d.ts +1 -1
- package/dist/tools/impact.js +13 -1
- package/dist/tools/impact.js.map +1 -1
- package/dist/tools/indexerTools.d.ts +16 -2
- package/dist/tools/indexerTools.js +44 -5
- package/dist/tools/indexerTools.js.map +1 -1
- package/dist/tools/listDecisions.d.ts +19 -0
- package/dist/tools/listDecisions.js +93 -0
- package/dist/tools/listDecisions.js.map +1 -0
- package/dist/tools/reaffirmDecision.d.ts +11 -0
- package/dist/tools/reaffirmDecision.js +55 -0
- package/dist/tools/reaffirmDecision.js.map +1 -0
- package/dist/tools/recordDecision.d.ts +24 -0
- package/dist/tools/recordDecision.js +135 -0
- package/dist/tools/recordDecision.js.map +1 -0
- package/dist/tools/semanticFind.d.ts +9 -2
- package/dist/tools/semanticFind.js +44 -2
- package/dist/tools/semanticFind.js.map +1 -1
- package/dist/tools/setDecisionStatus.d.ts +8 -0
- package/dist/tools/setDecisionStatus.js +34 -0
- package/dist/tools/setDecisionStatus.js.map +1 -0
- package/dist/viz/assets/{graph.worker-CC7BeE-8.js → graph.worker-DkH6-lMU.js} +1 -1
- package/dist/viz/assets/index-CxqeGuRS.css +1 -0
- package/dist/viz/assets/index-DiNZyrXx.js +9 -0
- package/dist/viz/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/dist/viz/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
- package/dist/viz/assets/jetbrains-mono-latin-700-normal-BYuf6tUa.woff2 +0 -0
- package/dist/viz/assets/r3f-BZY3QR6_.js +336 -0
- package/dist/viz/assets/tanstack-CjXr-L-s.js +62 -0
- package/dist/viz/assets/three-Cpu5RUEM.js +4116 -0
- package/dist/viz/index.html +5 -4
- package/package.json +5 -4
- package/dist/viz/assets/index-C_8UkiJ8.js +0 -9
- package/dist/viz/assets/r3f-Cku1OhB-.js +0 -277
- package/dist/viz/assets/tanstack-DyXcNRWk.js +0 -77
- package/dist/viz/assets/three-Z8XaLGmf.js +0 -3839
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/** Per-agent MCP config adapters for `reposkein-mcp init`'s join mode
|
|
2
|
+
* (REP-16 T5). A "join" is: someone cloned a repo that already has RepoSkein
|
|
3
|
+
* set up (committed `.reposkein/meta.json`) — the one thing left to do is
|
|
4
|
+
* wire the local agent(s) up, which today means hand-copying a JSON snippet
|
|
5
|
+
* from docs/INSTALL.md §6. These adapters do that automatically.
|
|
6
|
+
*
|
|
7
|
+
* Schemas match docs/INSTALL.md §6 exactly — that doc is the spec:
|
|
8
|
+
* - Claude Code: `<repo>/.mcp.json`, `mcpServers` key. Prefers `claude mcp
|
|
9
|
+
* add` (scope "project", which itself targets .mcp.json) when the CLI is
|
|
10
|
+
* on PATH, so Claude's own approval/health-check flow sees the entry;
|
|
11
|
+
* falls back to writing the file directly.
|
|
12
|
+
* - OpenCode (+omo): `<repo>/opencode.json`, `mcp` key (NOT `mcpServers`).
|
|
13
|
+
* - Cursor: `<repo>/.cursor/mcp.json`, `mcpServers` key.
|
|
14
|
+
* - Unknown/no agent detected: falls back to `.mcp.json` (§6.6 "generic").
|
|
15
|
+
*
|
|
16
|
+
* Every write is idempotent (re-run = no-op, byte-identical file), dry-run
|
|
17
|
+
* aware (never touches disk when `dryRun: true`), and backs up any file it
|
|
18
|
+
* actually modifies to a timestamped `.bak` sibling first. */
|
|
19
|
+
import { existsSync, readFileSync, writeFileSync, copyFileSync, mkdirSync } from "node:fs";
|
|
20
|
+
import { execFileSync } from "node:child_process";
|
|
21
|
+
import { dirname, join, resolve } from "node:path";
|
|
22
|
+
export const ALL_AGENT_IDS = ["claude", "opencode", "cursor"];
|
|
23
|
+
export const defaultExec = (cmd, args, opts) => {
|
|
24
|
+
try {
|
|
25
|
+
const stdout = execFileSync(cmd, args, { cwd: opts.cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
26
|
+
return { status: 0, stdout, stderr: "" };
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
const e = err;
|
|
30
|
+
return {
|
|
31
|
+
status: typeof e.status === "number" ? e.status : 1,
|
|
32
|
+
stdout: e.stdout ? e.stdout.toString() : "",
|
|
33
|
+
stderr: e.stderr ? e.stderr.toString() : String(e.message ?? err),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
function commandAvailable(exec, cmd, cwd) {
|
|
38
|
+
return exec(cmd, ["--version"], { cwd }).status === 0;
|
|
39
|
+
}
|
|
40
|
+
/** Detects which agents are "present": their CLI is on PATH, or their config
|
|
41
|
+
* file already exists (someone opted in previously — keep writing there).
|
|
42
|
+
* Falls back to `["claude"]` (→ `.mcp.json`, the generic/broadest-compat
|
|
43
|
+
* target) when nothing is detected at all, matching docs/INSTALL.md §6.6. */
|
|
44
|
+
export function detectAgents(repoPath, exec = defaultExec) {
|
|
45
|
+
const present = [];
|
|
46
|
+
if (existsSync(join(repoPath, ".mcp.json")) || commandAvailable(exec, "claude", repoPath))
|
|
47
|
+
present.push("claude");
|
|
48
|
+
if (existsSync(join(repoPath, "opencode.json")) || commandAvailable(exec, "opencode", repoPath))
|
|
49
|
+
present.push("opencode");
|
|
50
|
+
if (existsSync(join(repoPath, ".cursor")))
|
|
51
|
+
present.push("cursor");
|
|
52
|
+
if (present.length === 0)
|
|
53
|
+
present.push("claude");
|
|
54
|
+
return present;
|
|
55
|
+
}
|
|
56
|
+
/** Parses a comma/space-separated `--agents` value into AgentId[], ignoring
|
|
57
|
+
* unknown tokens (never throws — an unrecognized agent name just writes
|
|
58
|
+
* nothing rather than crashing `init`). */
|
|
59
|
+
export function parseAgentsFlag(value) {
|
|
60
|
+
const known = new Set(ALL_AGENT_IDS);
|
|
61
|
+
return value
|
|
62
|
+
.split(/[,\s]+/)
|
|
63
|
+
.map((s) => s.trim().toLowerCase())
|
|
64
|
+
.filter((s) => known.has(s));
|
|
65
|
+
}
|
|
66
|
+
function timestampSuffix(now) {
|
|
67
|
+
return now.toISOString().replace(/[:.]/g, "-");
|
|
68
|
+
}
|
|
69
|
+
/** Copies `path` to `<path>.<timestamp>.bak` before it's modified. */
|
|
70
|
+
function backupFile(path, now) {
|
|
71
|
+
const backupPath = `${path}.${timestampSuffix(now)}.bak`;
|
|
72
|
+
copyFileSync(path, backupPath);
|
|
73
|
+
return backupPath;
|
|
74
|
+
}
|
|
75
|
+
const MCP_JSON_SPEC = {
|
|
76
|
+
relPath: ".mcp.json",
|
|
77
|
+
topKey: "mcpServers",
|
|
78
|
+
buildEntry: (absRepoPath) => ({ command: "reposkein-mcp", env: { REPOSKEIN_REPO_PATH: absRepoPath } }),
|
|
79
|
+
};
|
|
80
|
+
const OPENCODE_JSON_SPEC = {
|
|
81
|
+
relPath: "opencode.json",
|
|
82
|
+
topKey: "mcp",
|
|
83
|
+
buildEntry: (absRepoPath) => ({
|
|
84
|
+
type: "local",
|
|
85
|
+
command: ["reposkein-mcp"],
|
|
86
|
+
environment: { REPOSKEIN_REPO_PATH: absRepoPath },
|
|
87
|
+
enabled: true,
|
|
88
|
+
}),
|
|
89
|
+
};
|
|
90
|
+
const CURSOR_JSON_SPEC = {
|
|
91
|
+
relPath: join(".cursor", "mcp.json"),
|
|
92
|
+
topKey: "mcpServers",
|
|
93
|
+
buildEntry: (absRepoPath) => ({ command: "reposkein-mcp", env: { REPOSKEIN_REPO_PATH: absRepoPath } }),
|
|
94
|
+
};
|
|
95
|
+
/** Idempotently upserts `doc[topKey].reposkein = entry` in a JSON config
|
|
96
|
+
* file, preserving every other key untouched (other MCP servers, `$schema`,
|
|
97
|
+
* etc.). Never guesses at a corrupt/hand-edited existing file — see the
|
|
98
|
+
* early return below: it backs the file up (real run only) and reports an
|
|
99
|
+
* "error" result instead of overwriting it with a reposkein-only doc,
|
|
100
|
+
* which would silently destroy whatever else was in there. */
|
|
101
|
+
function upsertJsonAdapter(repoPath, spec, dryRun, now) {
|
|
102
|
+
const path = join(repoPath, spec.relPath);
|
|
103
|
+
const exists = existsSync(path);
|
|
104
|
+
let doc = {};
|
|
105
|
+
if (exists) {
|
|
106
|
+
let raw;
|
|
107
|
+
try {
|
|
108
|
+
raw = readFileSync(path, "utf8");
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
return {
|
|
112
|
+
path,
|
|
113
|
+
action: "error",
|
|
114
|
+
message: `could not read ${spec.relPath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
doc = JSON.parse(raw);
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
const parseMessage = err instanceof Error ? err.message : String(err);
|
|
122
|
+
if (dryRun) {
|
|
123
|
+
return {
|
|
124
|
+
path,
|
|
125
|
+
action: "error",
|
|
126
|
+
message: `${spec.relPath} is not valid JSON (${parseMessage}) — left untouched. ` +
|
|
127
|
+
"Fix the JSON and re-run, or delete the file.",
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const backupPath = backupFile(path, now);
|
|
131
|
+
return {
|
|
132
|
+
path,
|
|
133
|
+
action: "error",
|
|
134
|
+
backupPath,
|
|
135
|
+
message: `${spec.relPath} is not valid JSON (${parseMessage}) — left untouched, backed up to ${backupPath}. ` +
|
|
136
|
+
"Fix the JSON and re-run, or delete the file.",
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const section = doc[spec.topKey] ?? {};
|
|
141
|
+
const entry = spec.buildEntry(resolve(repoPath));
|
|
142
|
+
if (JSON.stringify(section.reposkein ?? null) === JSON.stringify(entry)) {
|
|
143
|
+
return { path, action: "unchanged", message: `${spec.relPath} already has an up-to-date reposkein entry` };
|
|
144
|
+
}
|
|
145
|
+
if (dryRun) {
|
|
146
|
+
return {
|
|
147
|
+
path,
|
|
148
|
+
action: "dry-run",
|
|
149
|
+
message: `would ${exists ? "update" : "create"} ${spec.relPath} (${spec.topKey}.reposkein)`,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
let backupPath;
|
|
153
|
+
if (exists)
|
|
154
|
+
backupPath = backupFile(path, now);
|
|
155
|
+
doc[spec.topKey] = { ...section, reposkein: entry };
|
|
156
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
157
|
+
writeFileSync(path, JSON.stringify(doc, null, 2) + "\n", "utf8");
|
|
158
|
+
return { path, action: exists ? "updated" : "created", backupPath, message: `${exists ? "updated" : "created"} ${spec.relPath}` };
|
|
159
|
+
}
|
|
160
|
+
function claudeCliEntryExists(exec, repoPath) {
|
|
161
|
+
return exec("claude", ["mcp", "get", "reposkein"], { cwd: repoPath }).status === 0;
|
|
162
|
+
}
|
|
163
|
+
/** Writes the Claude Code entry via `claude mcp add -s project` (which
|
|
164
|
+
* itself targets `.mcp.json`), so Claude's own health-check/approval UI
|
|
165
|
+
* picks it up immediately. Idempotent via `claude mcp get` — a re-run
|
|
166
|
+
* no-ops instead of erroring on a duplicate name. */
|
|
167
|
+
function writeViaClaudeCli(repoPath, dryRun, exec) {
|
|
168
|
+
const path = join(repoPath, ".mcp.json");
|
|
169
|
+
if (claudeCliEntryExists(exec, repoPath)) {
|
|
170
|
+
return { agent: "claude", path, action: "unchanged", message: "`claude mcp get reposkein` already resolves — nothing to add" };
|
|
171
|
+
}
|
|
172
|
+
const args = ["mcp", "add", "reposkein", "-s", "project", "-e", `REPOSKEIN_REPO_PATH=${resolve(repoPath)}`, "--", "reposkein-mcp"];
|
|
173
|
+
if (dryRun) {
|
|
174
|
+
return { agent: "claude", path, action: "dry-run", message: `would run: claude ${args.join(" ")}` };
|
|
175
|
+
}
|
|
176
|
+
const r = exec("claude", args, { cwd: repoPath });
|
|
177
|
+
if (r.status !== 0) {
|
|
178
|
+
return { agent: "claude", path, action: "error", message: `claude mcp add failed: ${(r.stderr || r.stdout).trim()}` };
|
|
179
|
+
}
|
|
180
|
+
return { agent: "claude", path, action: "created", message: "added via `claude mcp add` (project scope → .mcp.json)" };
|
|
181
|
+
}
|
|
182
|
+
/** Writes (or plans, under `dryRun`) the local agent MCP config for every
|
|
183
|
+
* detected/requested agent. Pure w.r.t. its inputs beyond `exec`/`now`/fs —
|
|
184
|
+
* same repo state + same options always produces the same plan, which is
|
|
185
|
+
* what makes idempotence and dry-run trustworthy. */
|
|
186
|
+
export function writeAgentConfigs(repoPath, opts = {}) {
|
|
187
|
+
const exec = opts.exec ?? defaultExec;
|
|
188
|
+
const now = (opts.now ?? (() => new Date()))();
|
|
189
|
+
const dryRun = !!opts.dryRun;
|
|
190
|
+
const agents = opts.agents ?? detectAgents(repoPath, exec);
|
|
191
|
+
const results = [];
|
|
192
|
+
for (const agent of agents) {
|
|
193
|
+
if (agent === "claude") {
|
|
194
|
+
if (commandAvailable(exec, "claude", repoPath)) {
|
|
195
|
+
results.push(writeViaClaudeCli(repoPath, dryRun, exec));
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
const r = upsertJsonAdapter(repoPath, MCP_JSON_SPEC, dryRun, now);
|
|
199
|
+
results.push({ agent: "claude", ...r });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else if (agent === "opencode") {
|
|
203
|
+
const r = upsertJsonAdapter(repoPath, OPENCODE_JSON_SPEC, dryRun, now);
|
|
204
|
+
results.push({ agent: "opencode", ...r });
|
|
205
|
+
}
|
|
206
|
+
else if (agent === "cursor") {
|
|
207
|
+
const r = upsertJsonAdapter(repoPath, CURSOR_JSON_SPEC, dryRun, now);
|
|
208
|
+
results.push({ agent: "cursor", ...r });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return results;
|
|
212
|
+
}
|
|
213
|
+
/** One line per adapter result, for `init`'s stderr summary. */
|
|
214
|
+
export function formatAdapterResult(r) {
|
|
215
|
+
const verb = r.action === "created" ? "wrote" :
|
|
216
|
+
r.action === "updated" ? "updated" :
|
|
217
|
+
r.action === "unchanged" ? "unchanged" :
|
|
218
|
+
r.action === "dry-run" ? "[dry-run]" :
|
|
219
|
+
"ERROR";
|
|
220
|
+
const backup = r.backupPath ? ` (backup: ${r.backupPath})` : "";
|
|
221
|
+
return `reposkein: [${r.agent}] ${verb} — ${r.message}${backup}`;
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=agentAdapters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentAdapters.js","sourceRoot":"","sources":["../../src/cli/agentAdapters.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Dependency-free ANSI styling for `reposkein-mcp stats` — raw escape codes,
|
|
2
|
+
* no chalk/picocolors. Auto-disables when stdout isn't a TTY or `NO_COLOR`
|
|
3
|
+
* is set (https://no-color.org), per the zero-infra/deterministic-tooling
|
|
4
|
+
* convention: never assume a terminal, never require a dependency for a
|
|
5
|
+
* cosmetic feature.
|
|
6
|
+
*
|
|
7
|
+
* Colors: teal `#2DD4BF` ≈ ANSI 256 color 44 (`38;5;44`), amber ≈ 256 color
|
|
8
|
+
* 214 (`38;5;214`) — same palette as the project's README banners. Falls
|
|
9
|
+
* back to the portable 16-color codes (36 cyan, 33 yellow) is intentionally
|
|
10
|
+
* NOT done: 256-color support is effectively universal on anything that
|
|
11
|
+
* reports `isTTY`, and matching the brand hex matters more here than
|
|
12
|
+
* supporting truly ancient terminals for a purely cosmetic CLI report. */
|
|
13
|
+
export declare function colorEnabled(stream?: {
|
|
14
|
+
isTTY?: boolean;
|
|
15
|
+
}, env?: NodeJS.ProcessEnv): boolean;
|
|
16
|
+
export interface Styler {
|
|
17
|
+
teal(s: string): string;
|
|
18
|
+
amber(s: string): string;
|
|
19
|
+
bold(s: string): string;
|
|
20
|
+
dim(s: string): string;
|
|
21
|
+
}
|
|
22
|
+
/** Returns the styling functions to use, given whether color is enabled —
|
|
23
|
+
* callers pass `colorEnabled()`'s result so this stays pure/testable. */
|
|
24
|
+
export declare function styler(enabled: boolean): Styler;
|
package/dist/cli/ansi.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** Dependency-free ANSI styling for `reposkein-mcp stats` — raw escape codes,
|
|
2
|
+
* no chalk/picocolors. Auto-disables when stdout isn't a TTY or `NO_COLOR`
|
|
3
|
+
* is set (https://no-color.org), per the zero-infra/deterministic-tooling
|
|
4
|
+
* convention: never assume a terminal, never require a dependency for a
|
|
5
|
+
* cosmetic feature.
|
|
6
|
+
*
|
|
7
|
+
* Colors: teal `#2DD4BF` ≈ ANSI 256 color 44 (`38;5;44`), amber ≈ 256 color
|
|
8
|
+
* 214 (`38;5;214`) — same palette as the project's README banners. Falls
|
|
9
|
+
* back to the portable 16-color codes (36 cyan, 33 yellow) is intentionally
|
|
10
|
+
* NOT done: 256-color support is effectively universal on anything that
|
|
11
|
+
* reports `isTTY`, and matching the brand hex matters more here than
|
|
12
|
+
* supporting truly ancient terminals for a purely cosmetic CLI report. */
|
|
13
|
+
const RESET = "\x1b[0m";
|
|
14
|
+
const BOLD = "\x1b[1m";
|
|
15
|
+
const DIM = "\x1b[2m";
|
|
16
|
+
const TEAL = "\x1b[38;5;44m";
|
|
17
|
+
const AMBER = "\x1b[38;5;214m";
|
|
18
|
+
export function colorEnabled(stream = process.stdout, env = process.env) {
|
|
19
|
+
if (env.NO_COLOR !== undefined)
|
|
20
|
+
return false;
|
|
21
|
+
return !!stream.isTTY;
|
|
22
|
+
}
|
|
23
|
+
const identity = {
|
|
24
|
+
teal: (s) => s,
|
|
25
|
+
amber: (s) => s,
|
|
26
|
+
bold: (s) => s,
|
|
27
|
+
dim: (s) => s,
|
|
28
|
+
};
|
|
29
|
+
const styled = {
|
|
30
|
+
teal: (s) => `${TEAL}${s}${RESET}`,
|
|
31
|
+
amber: (s) => `${AMBER}${s}${RESET}`,
|
|
32
|
+
bold: (s) => `${BOLD}${s}${RESET}`,
|
|
33
|
+
dim: (s) => `${DIM}${s}${RESET}`,
|
|
34
|
+
};
|
|
35
|
+
/** Returns the styling functions to use, given whether color is enabled —
|
|
36
|
+
* callers pass `colorEnabled()`'s result so this stays pure/testable. */
|
|
37
|
+
export function styler(enabled) {
|
|
38
|
+
return enabled ? styled : identity;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=ansi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ansi.js","sourceRoot":"","sources":["../../src/cli/ansi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;2EAW2E;AAE3E,MAAM,KAAK,GAAG,SAAS,CAAC;AACxB,MAAM,IAAI,GAAG,SAAS,CAAC;AACvB,MAAM,GAAG,GAAG,SAAS,CAAC;AACtB,MAAM,IAAI,GAAG,eAAe,CAAC;AAC7B,MAAM,KAAK,GAAG,gBAAgB,CAAC;AAE/B,MAAM,UAAU,YAAY,CAAC,SAA8B,OAAO,CAAC,MAAM,EAAE,MAAyB,OAAO,CAAC,GAAG;IAC7G,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7C,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;AACxB,CAAC;AASD,MAAM,QAAQ,GAAW;IACvB,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACd,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACf,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACd,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,GAAW;IACrB,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,KAAK,EAAE;IAClC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,KAAK,EAAE;IACpC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,KAAK,EAAE;IAClC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,KAAK,EAAE;CACjC,CAAC;AAEF;0EAC0E;AAC1E,MAAM,UAAU,MAAM,CAAC,OAAgB;IACrC,OAAO,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;AACrC,CAAC"}
|
package/dist/cli/doctor.d.ts
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
export interface DoctorPathResolution {
|
|
2
|
+
path: string;
|
|
3
|
+
/** Set when walk-down found 2+ candidate repos and neither an explicit
|
|
4
|
+
* path nor REPOSKEIN_REPO_PATH picked one — `path` falls back to `cwd`
|
|
5
|
+
* in this case so the caller can still print a real (failing) report. */
|
|
6
|
+
error?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Resolves the repo path for `reposkein-mcp doctor [path]`: explicit arg >
|
|
9
|
+
* REPOSKEIN_REPO_PATH > walk-up (nearest ancestor with .reposkein/) >
|
|
10
|
+
* walk-down (workspace mode). Verifies doctor works from a subdirectory
|
|
11
|
+
* cwd — the common case of running it somewhere other than the repo root. */
|
|
12
|
+
export declare function resolveDoctorRepoPath(explicitPath: string | undefined, cwd: string, envRepoPath: string | undefined): DoctorPathResolution;
|
|
1
13
|
export interface Check {
|
|
2
14
|
id: string;
|
|
3
15
|
label: string;
|
|
@@ -15,5 +27,25 @@ export interface DoctorReport {
|
|
|
15
27
|
* Does NOT check MCP host registration — a CLI can't see that; the
|
|
16
28
|
* reposkein-setup skill verifies reachability via a probe tool call. */
|
|
17
29
|
export declare function runChecks(repoPath: string): Promise<DoctorReport>;
|
|
18
|
-
/**
|
|
19
|
-
|
|
30
|
+
/** Check ids that `doctor --ci` treats as build-breaking even though they're
|
|
31
|
+
* non-critical for interactive use (degrade gracefully for a human at the
|
|
32
|
+
* keyboard, but should fail a CI job so drift gets caught before it's
|
|
33
|
+
* merged): missing/foreign git hooks, a stale committed graph, and an
|
|
34
|
+
* unsplit legacy `summaries.jsonl` (see docs/INSTALL.md §4.2 — every branch
|
|
35
|
+
* that writes a summary would conflict on it). */
|
|
36
|
+
export declare const CI_FAIL_IDS: Set<string>;
|
|
37
|
+
/** Checks (by id) that `doctor --ci` additionally fails on, beyond the
|
|
38
|
+
* normal critical-check gate. */
|
|
39
|
+
export declare function ciFailingChecks(report: DoctorReport): Check[];
|
|
40
|
+
export declare function renderDoctorReport(report: DoctorReport): string;
|
|
41
|
+
export interface RunDoctorOptions {
|
|
42
|
+
json?: boolean;
|
|
43
|
+
/** `doctor --ci`: additionally fail (non-zero exit) when any CI_FAIL_IDS
|
|
44
|
+
* check is non-ok, even though those checks are non-critical for
|
|
45
|
+
* interactive use. Doesn't change what's printed — only the exit code. */
|
|
46
|
+
ci?: boolean;
|
|
47
|
+
}
|
|
48
|
+
/** Entry point for `reposkein-mcp doctor [path] [--json] [--ci]`. Returns the
|
|
49
|
+
* process exit code. Accepts a bare boolean for `json` for backwards
|
|
50
|
+
* compatibility with the pre-`--ci` call signature. */
|
|
51
|
+
export declare function runDoctor(repoPath?: string, opts?: RunDoctorOptions | boolean): Promise<number>;
|
package/dist/cli/doctor.js
CHANGED
|
@@ -3,6 +3,27 @@ import { join } from "node:path";
|
|
|
3
3
|
import { ensureIndexerBinary } from "../indexer/fetchBinary.js";
|
|
4
4
|
import { spawnIndexer } from "../indexer/runIndexer.js";
|
|
5
5
|
import { resolveRepoId } from "../store/repoId.js";
|
|
6
|
+
import { resolveRepoPath } from "../store/resolveRepoPath.js";
|
|
7
|
+
import { decisionChecks } from "./doctorDecisions.js";
|
|
8
|
+
import { summaryChecks } from "./doctorSummaries.js";
|
|
9
|
+
import { hooksCheck, graphStaleCheck } from "./doctorFreshness.js";
|
|
10
|
+
/** Resolves the repo path for `reposkein-mcp doctor [path]`: explicit arg >
|
|
11
|
+
* REPOSKEIN_REPO_PATH > walk-up (nearest ancestor with .reposkein/) >
|
|
12
|
+
* walk-down (workspace mode). Verifies doctor works from a subdirectory
|
|
13
|
+
* cwd — the common case of running it somewhere other than the repo root. */
|
|
14
|
+
export function resolveDoctorRepoPath(explicitPath, cwd, envRepoPath) {
|
|
15
|
+
const resolution = resolveRepoPath({ cwd, envRepoPath, explicit: explicitPath });
|
|
16
|
+
if (resolution.repoPath)
|
|
17
|
+
return { path: resolution.repoPath };
|
|
18
|
+
if (resolution.candidates && resolution.candidates.length > 0) {
|
|
19
|
+
return {
|
|
20
|
+
path: cwd,
|
|
21
|
+
error: `multiple RepoSkein repos found under ${cwd}: ${resolution.candidates.join(", ")}. ` +
|
|
22
|
+
"Pass one explicitly: `reposkein-mcp doctor <path>` (or set REPOSKEIN_REPO_PATH).",
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return { path: cwd };
|
|
26
|
+
}
|
|
6
27
|
/** Counts non-empty lines (≈ node count) without parsing every row. */
|
|
7
28
|
function countLines(text) {
|
|
8
29
|
let n = 0;
|
|
@@ -65,10 +86,30 @@ export async function runChecks(repoPath) {
|
|
|
65
86
|
detail: repoId ?? "could not resolve a repo id",
|
|
66
87
|
fix: repoId ? undefined : "set REPOSKEIN_REPO_PATH (or REPOSKEIN_REPO_ID) for the MCP server",
|
|
67
88
|
});
|
|
89
|
+
// 4) Committed summary shards (all non-critical: degrade, don't block).
|
|
90
|
+
checks.push(...summaryChecks(repoPath));
|
|
91
|
+
// 5) Decision log validation (all non-critical: degrade, don't block).
|
|
92
|
+
checks.push(...decisionChecks(repoPath));
|
|
93
|
+
// 6) Git hooks installed + graph freshness (non-critical here; `doctor
|
|
94
|
+
// --ci` promotes both to a failing exit code — see CI_FAIL_IDS below).
|
|
95
|
+
checks.push(hooksCheck(repoPath));
|
|
96
|
+
checks.push(graphStaleCheck(repoPath));
|
|
68
97
|
const ok = checks.filter((c) => c.critical).every((c) => c.ok);
|
|
69
98
|
return { repoPath, ok, checks };
|
|
70
99
|
}
|
|
71
|
-
|
|
100
|
+
/** Check ids that `doctor --ci` treats as build-breaking even though they're
|
|
101
|
+
* non-critical for interactive use (degrade gracefully for a human at the
|
|
102
|
+
* keyboard, but should fail a CI job so drift gets caught before it's
|
|
103
|
+
* merged): missing/foreign git hooks, a stale committed graph, and an
|
|
104
|
+
* unsplit legacy `summaries.jsonl` (see docs/INSTALL.md §4.2 — every branch
|
|
105
|
+
* that writes a summary would conflict on it). */
|
|
106
|
+
export const CI_FAIL_IDS = new Set(["hooks_installed", "graph_stale", "summaries_unsplit"]);
|
|
107
|
+
/** Checks (by id) that `doctor --ci` additionally fails on, beyond the
|
|
108
|
+
* normal critical-check gate. */
|
|
109
|
+
export function ciFailingChecks(report) {
|
|
110
|
+
return report.checks.filter((c) => CI_FAIL_IDS.has(c.id) && !c.ok);
|
|
111
|
+
}
|
|
112
|
+
export function renderDoctorReport(report) {
|
|
72
113
|
const lines = [`reposkein doctor — ${report.repoPath}`, ""];
|
|
73
114
|
for (const c of report.checks) {
|
|
74
115
|
lines.push(`${c.ok ? "✓" : "✗"} ${c.label}: ${c.detail}`);
|
|
@@ -80,13 +121,20 @@ function render(report) {
|
|
|
80
121
|
lines.push("(Note: this checks prerequisites only. To confirm the MCP server is wired into your agent, ask it to call get_context_profile — see the reposkein-setup skill.)");
|
|
81
122
|
return lines.join("\n");
|
|
82
123
|
}
|
|
83
|
-
/** Entry point for `reposkein-mcp doctor [path] [--json]`. Returns
|
|
84
|
-
|
|
124
|
+
/** Entry point for `reposkein-mcp doctor [path] [--json] [--ci]`. Returns the
|
|
125
|
+
* process exit code. Accepts a bare boolean for `json` for backwards
|
|
126
|
+
* compatibility with the pre-`--ci` call signature. */
|
|
127
|
+
export async function runDoctor(repoPath = ".", opts = {}) {
|
|
128
|
+
const { json = false, ci = false } = typeof opts === "boolean" ? { json: opts } : opts;
|
|
85
129
|
const report = await runChecks(repoPath);
|
|
86
130
|
if (json)
|
|
87
131
|
console.log(JSON.stringify(report, null, 2));
|
|
88
132
|
else
|
|
89
|
-
console.error(
|
|
90
|
-
|
|
133
|
+
console.error(renderDoctorReport(report));
|
|
134
|
+
const ciFailures = ci ? ciFailingChecks(report) : [];
|
|
135
|
+
if (ci && ciFailures.length > 0 && !json) {
|
|
136
|
+
console.error(`\n--ci: failing on ${ciFailures.length} additional check(s): ${ciFailures.map((c) => c.id).join(", ")}`);
|
|
137
|
+
}
|
|
138
|
+
return report.ok && ciFailures.length === 0 ? 0 : 1;
|
|
91
139
|
}
|
|
92
140
|
//# sourceMappingURL=doctor.js.map
|
package/dist/cli/doctor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Check } from "./doctor.js";
|
|
2
|
+
/** Decision-log validation for `reposkein-mcp doctor`. Every check is
|
|
3
|
+
* non-critical (exit 0 with warnings): a damaged decision log must degrade,
|
|
4
|
+
* not block the graph. Returns [] only when there is no decisions dir. */
|
|
5
|
+
export declare function decisionChecks(repoPath: string): Check[];
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { computeBodyHash, loadDecisionFiles, } from "../store/decisions.js";
|
|
2
|
+
/** ~Zimmermann ceiling: past this many active records, recall quality and the
|
|
3
|
+
* profile budget both degrade — time to deprecate/archive. */
|
|
4
|
+
const ACTIVE_BUDGET = 100;
|
|
5
|
+
const warn = (id, label, ok, detail, fix) => ({
|
|
6
|
+
id,
|
|
7
|
+
label,
|
|
8
|
+
ok,
|
|
9
|
+
critical: false, // decision-log problems degrade; they never block startup
|
|
10
|
+
detail,
|
|
11
|
+
fix: ok ? undefined : fix,
|
|
12
|
+
});
|
|
13
|
+
function findCycle(records) {
|
|
14
|
+
// DFS along supersedes edges, tracking the actual path so the report names
|
|
15
|
+
// exactly the cycle's members — not everything reachable along the way.
|
|
16
|
+
for (const start of records.keys()) {
|
|
17
|
+
const stack = [{ id: start, path: [start] }];
|
|
18
|
+
const visited = new Set();
|
|
19
|
+
while (stack.length > 0) {
|
|
20
|
+
const { id, path } = stack.pop();
|
|
21
|
+
for (const target of records.get(id)?.supersedes ?? []) {
|
|
22
|
+
if (target === start)
|
|
23
|
+
return [...path, target];
|
|
24
|
+
if (!visited.has(target)) {
|
|
25
|
+
visited.add(target);
|
|
26
|
+
stack.push({ id: target, path: [...path, target] });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
/** Decision-log validation for `reposkein-mcp doctor`. Every check is
|
|
34
|
+
* non-critical (exit 0 with warnings): a damaged decision log must degrade,
|
|
35
|
+
* not block the graph. Returns [] only when there is no decisions dir. */
|
|
36
|
+
export function decisionChecks(repoPath) {
|
|
37
|
+
const entries = loadDecisionFiles(repoPath);
|
|
38
|
+
if (entries.length === 0)
|
|
39
|
+
return [];
|
|
40
|
+
const checks = [];
|
|
41
|
+
// 1) Every file parses.
|
|
42
|
+
const malformed = entries.filter((e) => e.record === null).map((e) => e.file);
|
|
43
|
+
checks.push(warn("decisions_parse", "decision files parse", malformed.length === 0, malformed.length === 0
|
|
44
|
+
? `${entries.length} files`
|
|
45
|
+
: `malformed: ${malformed.join(", ")}`, "fix or remove the malformed files (git conflict markers are the usual cause)"));
|
|
46
|
+
const parsed = entries.filter((e) => e.record !== null);
|
|
47
|
+
// 2) No duplicate ids across files (cross-branch same-day same-slug merges).
|
|
48
|
+
const byId = new Map();
|
|
49
|
+
const dupes = new Set();
|
|
50
|
+
for (const { record } of parsed) {
|
|
51
|
+
if (byId.has(record.id))
|
|
52
|
+
dupes.add(record.id);
|
|
53
|
+
else
|
|
54
|
+
byId.set(record.id, record);
|
|
55
|
+
}
|
|
56
|
+
checks.push(warn("decisions_duplicates", "decision ids unique", dupes.size === 0, dupes.size === 0 ? `${byId.size} decisions` : `duplicate ids: ${[...dupes].join(", ")}`, "two branches minted the same id — keep one file per id (merge the bodies by hand if they differ)"));
|
|
57
|
+
// 3) Bodies unmodified since signing (immutability as invariant, not
|
|
58
|
+
// convention — reaffirm/supersede re-sign, hand edits don't).
|
|
59
|
+
const tampered = parsed
|
|
60
|
+
.filter(({ record }) => record.body_hash !== computeBodyHash(record))
|
|
61
|
+
.map(({ record }) => record.id);
|
|
62
|
+
checks.push(warn("decisions_integrity", "decision bodies unmodified (body_hash)", tampered.length === 0, tampered.length === 0 ? "all bodies verify" : `hand-edited: ${tampered.join(", ")}`, "decision bodies are immutable — record a superseding decision instead of editing; revert the edit or re-record"));
|
|
63
|
+
// 4) Supersession references resolve, in both directions.
|
|
64
|
+
const dangling = [];
|
|
65
|
+
for (const rec of byId.values()) {
|
|
66
|
+
for (const sid of rec.supersedes) {
|
|
67
|
+
if (!byId.has(sid))
|
|
68
|
+
dangling.push(`${rec.id} → ${sid}`);
|
|
69
|
+
}
|
|
70
|
+
if (rec.superseded_by && !byId.has(rec.superseded_by)) {
|
|
71
|
+
dangling.push(`${rec.id} ← ${rec.superseded_by}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
checks.push(warn("decisions_refs", "supersession references resolve", dangling.length === 0, dangling.length === 0 ? "no dangling references" : `dangling: ${dangling.join(", ")}`, "a referenced decision file is missing — restore it or correct the reference"));
|
|
75
|
+
// 5) No supersession cycles.
|
|
76
|
+
const cycle = findCycle(byId);
|
|
77
|
+
checks.push(warn("decisions_cycles", "supersession chain acyclic", cycle === null, cycle === null ? "no cycles" : `cycle: ${cycle.join(" → ")}`, "break the cycle — a decision cannot (transitively) supersede itself"));
|
|
78
|
+
// 6) Active-record budget.
|
|
79
|
+
const active = [...byId.values()].filter((r) => r.status === "accepted" || r.status === "proposed").length;
|
|
80
|
+
checks.push(warn("decisions_budget", `active decisions within budget (≤${ACTIVE_BUDGET})`, active <= ACTIVE_BUDGET, `${active} active records`, "the log is past the point where recall stays sharp — deprecate or supersede aged-out decisions"));
|
|
81
|
+
return checks;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=doctorDecisions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctorDecisions.js","sourceRoot":"","sources":["../../src/cli/doctorDecisions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Check } from "./doctor.js";
|
|
2
|
+
/** Non-critical by default: a repo can be perfectly usable without hooks
|
|
3
|
+
* (an agent can always run `reposkein-mcp index` by hand). `doctor --ci`
|
|
4
|
+
* promotes this id to a failing exit code — see CI_FAIL_IDS in doctor.ts. */
|
|
5
|
+
export declare function hooksCheck(repoPath: string): Check;
|
|
6
|
+
/** Content-based staleness: the committed graph is fresh iff the commit SHA
|
|
7
|
+
* recorded the last time it was (re)built (`.reposkein/local/indexed-at` —
|
|
8
|
+
* see mcp/src/store/indexedAt.ts) equals the current git HEAD. The marker
|
|
9
|
+
* is kept current two ways: the installed git hooks maintain it on their
|
|
10
|
+
* own (`post-commit` after every commit, `post-merge`/`post-checkout` —
|
|
11
|
+
* which also re-index — after a merge/pull/branch switch), and
|
|
12
|
+
* `reposkein-mcp index`/`init` write it directly. A mismatch therefore
|
|
13
|
+
* means one of: the hooks were never installed (or were removed), or
|
|
14
|
+
* nobody's indexed this repo at all yet.
|
|
15
|
+
*
|
|
16
|
+
* Replaces an earlier mtime-based heuristic (mtime(nodes.jsonl) vs. the last
|
|
17
|
+
* commit's timestamp) that turned out to be structurally blind after any
|
|
18
|
+
* fresh checkout: `git clone`/`checkout` — or a CI cache restoring
|
|
19
|
+
* `.reposkein/` from a prior run — stamps every file it writes with
|
|
20
|
+
* "now", which is always ≥ any past commit's timestamp, so
|
|
21
|
+
* `mtime < lastCommitMs` could never be true right when it mattered most.
|
|
22
|
+
* A recorded SHA has no such blind spot: it's compared by value, not by
|
|
23
|
+
* filesystem timestamp.
|
|
24
|
+
*
|
|
25
|
+
* Deliberately a **plain SHA mismatch**, not a diff filtered to
|
|
26
|
+
* indexed-language extensions: filtering would need to stay in sync with
|
|
27
|
+
* `config.toml`'s `[languages] enabled` list and the indexer's own
|
|
28
|
+
* extension→language table, which is one more thing to drift. The cost of
|
|
29
|
+
* getting it wrong is asymmetric — a false "stale" just costs a redundant
|
|
30
|
+
* `reposkein-mcp index` (cheap), while a false "fresh" is exactly the bug
|
|
31
|
+
* being fixed — so the simpler, conservative rule wins here.
|
|
32
|
+
*
|
|
33
|
+
* Two distinct failure shapes, both reported under the same check id:
|
|
34
|
+
* - no recorded SHA at all → never indexed via a hook or the mcp-side path
|
|
35
|
+
* (a fresh clone's local/ never carries over, since it's gitignored) —
|
|
36
|
+
* FAIL.
|
|
37
|
+
* - a recorded SHA that doesn't match HEAD → the hooks are missing (or
|
|
38
|
+
* were bypassed, e.g. `git commit --no-verify`) and nobody's re-indexed
|
|
39
|
+
* by hand either — FAIL.
|
|
40
|
+
*
|
|
41
|
+
* Non-critical by default; `doctor --ci` promotes it (CI_FAIL_IDS in
|
|
42
|
+
* doctor.ts). */
|
|
43
|
+
export declare function graphStaleCheck(repoPath: string): Check;
|