@reposkein/mcp 0.3.0 → 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 +19 -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 +51 -6
- package/dist/cli/doctor.js.map +1 -1
- 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/index.d.ts +8 -0
- package/dist/index.js +286 -123
- package/dist/index.js.map +1 -1
- 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 +0 -2
- package/dist/indexer/runIndexer.js +8 -4
- package/dist/indexer/runIndexer.js.map +1 -1
- package/dist/store/JsonlGraphStore.d.ts +13 -3
- package/dist/store/JsonlGraphStore.js +55 -17
- package/dist/store/JsonlGraphStore.js.map +1 -1
- package/dist/store/decisions.d.ts +20 -2
- package/dist/store/decisions.js +35 -2
- package/dist/store/decisions.js.map +1 -1
- 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/indexerTools.d.ts +14 -2
- package/dist/tools/indexerTools.js +17 -5
- package/dist/tools/indexerTools.js.map +1 -1
- package/dist/tools/recordDecision.js +6 -1
- package/dist/tools/recordDecision.js.map +1 -1
- 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
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;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/cli/doctor.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;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { readIndexedAtSha } from "../store/indexedAt.js";
|
|
5
|
+
/** Marker `init --hooks` (the Rust indexer) writes into every hook it
|
|
6
|
+
* installs — see indexer/crates/cli/src/main.rs `write_hook`. Doctor uses
|
|
7
|
+
* the same marker to tell "RepoSkein installed this hook" from "the user
|
|
8
|
+
* has their own pre-commit hook and never ran `reposkein-mcp init`". */
|
|
9
|
+
const HOOK_MARKER = "# reposkein-managed";
|
|
10
|
+
const warn = (id, label, ok, detail, fix) => ({
|
|
11
|
+
id,
|
|
12
|
+
label,
|
|
13
|
+
ok,
|
|
14
|
+
critical: false, // these checks degrade the workflow; they never block startup
|
|
15
|
+
detail,
|
|
16
|
+
fix: ok ? undefined : fix,
|
|
17
|
+
});
|
|
18
|
+
/** Non-critical by default: a repo can be perfectly usable without hooks
|
|
19
|
+
* (an agent can always run `reposkein-mcp index` by hand). `doctor --ci`
|
|
20
|
+
* promotes this id to a failing exit code — see CI_FAIL_IDS in doctor.ts. */
|
|
21
|
+
export function hooksCheck(repoPath) {
|
|
22
|
+
const hookPath = join(repoPath, ".git", "hooks", "pre-commit");
|
|
23
|
+
if (!existsSync(hookPath)) {
|
|
24
|
+
return warn("hooks_installed", "git hooks installed", false, "no .git/hooks/pre-commit", "run `reposkein-mcp init` to install the pre-commit/post-merge hooks");
|
|
25
|
+
}
|
|
26
|
+
let content = "";
|
|
27
|
+
try {
|
|
28
|
+
content = readFileSync(hookPath, "utf8");
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
/* unreadable — treat like missing below */
|
|
32
|
+
}
|
|
33
|
+
const managed = content.includes(HOOK_MARKER);
|
|
34
|
+
return warn("hooks_installed", "git hooks installed", managed, managed
|
|
35
|
+
? "pre-commit hook installed"
|
|
36
|
+
: ".git/hooks/pre-commit exists but was not installed by RepoSkein", managed ? undefined : "run `reposkein-mcp init` to install the pre-commit/post-merge hooks");
|
|
37
|
+
}
|
|
38
|
+
/** `git -C repoPath rev-parse HEAD`, or null on any failure (no git, not a
|
|
39
|
+
* repo, no commits yet). */
|
|
40
|
+
function currentHeadSha(repoPath) {
|
|
41
|
+
try {
|
|
42
|
+
const out = execFileSync("git", ["-C", repoPath, "rev-parse", "HEAD"], {
|
|
43
|
+
encoding: "utf8",
|
|
44
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
45
|
+
}).trim();
|
|
46
|
+
return out || null;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Content-based staleness: the committed graph is fresh iff the commit SHA
|
|
53
|
+
* recorded the last time it was (re)built (`.reposkein/local/indexed-at` —
|
|
54
|
+
* see mcp/src/store/indexedAt.ts) equals the current git HEAD. The marker
|
|
55
|
+
* is kept current two ways: the installed git hooks maintain it on their
|
|
56
|
+
* own (`post-commit` after every commit, `post-merge`/`post-checkout` —
|
|
57
|
+
* which also re-index — after a merge/pull/branch switch), and
|
|
58
|
+
* `reposkein-mcp index`/`init` write it directly. A mismatch therefore
|
|
59
|
+
* means one of: the hooks were never installed (or were removed), or
|
|
60
|
+
* nobody's indexed this repo at all yet.
|
|
61
|
+
*
|
|
62
|
+
* Replaces an earlier mtime-based heuristic (mtime(nodes.jsonl) vs. the last
|
|
63
|
+
* commit's timestamp) that turned out to be structurally blind after any
|
|
64
|
+
* fresh checkout: `git clone`/`checkout` — or a CI cache restoring
|
|
65
|
+
* `.reposkein/` from a prior run — stamps every file it writes with
|
|
66
|
+
* "now", which is always ≥ any past commit's timestamp, so
|
|
67
|
+
* `mtime < lastCommitMs` could never be true right when it mattered most.
|
|
68
|
+
* A recorded SHA has no such blind spot: it's compared by value, not by
|
|
69
|
+
* filesystem timestamp.
|
|
70
|
+
*
|
|
71
|
+
* Deliberately a **plain SHA mismatch**, not a diff filtered to
|
|
72
|
+
* indexed-language extensions: filtering would need to stay in sync with
|
|
73
|
+
* `config.toml`'s `[languages] enabled` list and the indexer's own
|
|
74
|
+
* extension→language table, which is one more thing to drift. The cost of
|
|
75
|
+
* getting it wrong is asymmetric — a false "stale" just costs a redundant
|
|
76
|
+
* `reposkein-mcp index` (cheap), while a false "fresh" is exactly the bug
|
|
77
|
+
* being fixed — so the simpler, conservative rule wins here.
|
|
78
|
+
*
|
|
79
|
+
* Two distinct failure shapes, both reported under the same check id:
|
|
80
|
+
* - no recorded SHA at all → never indexed via a hook or the mcp-side path
|
|
81
|
+
* (a fresh clone's local/ never carries over, since it's gitignored) —
|
|
82
|
+
* FAIL.
|
|
83
|
+
* - a recorded SHA that doesn't match HEAD → the hooks are missing (or
|
|
84
|
+
* were bypassed, e.g. `git commit --no-verify`) and nobody's re-indexed
|
|
85
|
+
* by hand either — FAIL.
|
|
86
|
+
*
|
|
87
|
+
* Non-critical by default; `doctor --ci` promotes it (CI_FAIL_IDS in
|
|
88
|
+
* doctor.ts). */
|
|
89
|
+
export function graphStaleCheck(repoPath) {
|
|
90
|
+
const nodesFile = join(repoPath, ".reposkein", "nodes.jsonl");
|
|
91
|
+
if (!existsSync(nodesFile)) {
|
|
92
|
+
return warn("graph_stale", "graph freshness", false, "no .reposkein/nodes.jsonl (never indexed)", "run `reposkein-mcp index`");
|
|
93
|
+
}
|
|
94
|
+
const recordedSha = readIndexedAtSha(repoPath);
|
|
95
|
+
if (!recordedSha) {
|
|
96
|
+
return warn("graph_stale", "graph freshness", false, "nodes.jsonl exists but no recorded indexed-at commit (.reposkein/local/indexed-at) — " +
|
|
97
|
+
"the post-commit/post-merge/post-checkout hooks maintain this automatically, so its " +
|
|
98
|
+
"absence means the hooks aren't installed (or local/ never carried over a clone) and " +
|
|
99
|
+
"nobody's run `reposkein-mcp index`/`init` by hand either", "run `reposkein-mcp index`");
|
|
100
|
+
}
|
|
101
|
+
const headSha = currentHeadSha(repoPath);
|
|
102
|
+
if (headSha === null) {
|
|
103
|
+
return warn("graph_stale", "graph freshness", true, "no git HEAD to compare against (skipped)");
|
|
104
|
+
}
|
|
105
|
+
if (recordedSha === headSha) {
|
|
106
|
+
return warn("graph_stale", "graph freshness", true, `graph matches HEAD (${headSha.slice(0, 7)})`);
|
|
107
|
+
}
|
|
108
|
+
return warn("graph_stale", "graph freshness", false, `graph was indexed at ${recordedSha.slice(0, 7)}, but HEAD is now ${headSha.slice(0, 7)} — commits landed since ` +
|
|
109
|
+
"the last index without the marker advancing (hooks missing/bypassed, or the graph was never re-indexed)", "run `reposkein-mcp index`");
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=doctorFreshness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctorFreshness.js","sourceRoot":"","sources":["../../src/cli/doctorFreshness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { countRecordedConflicts, isShardFileName, loadSummaryShards, summariesDir, } from "../store/summaryShards.js";
|
|
5
|
+
/** Health of the committed authored summaries, `.reposkein/summaries/<xx>.jsonl`.
|
|
6
|
+
*
|
|
7
|
+
* Every check is non-critical. Summaries are the one part of `.reposkein/`
|
|
8
|
+
* nothing can regenerate, so the failure mode that matters is *silent* loss:
|
|
9
|
+
* prose that exists on disk but will never be committed, or a merge that
|
|
10
|
+
* dropped a record. Each check below names one way that happens.
|
|
11
|
+
*
|
|
12
|
+
* Returns [] when the repo has no committed summaries and no legacy file —
|
|
13
|
+
* a repo that has never had an agent write one has nothing to report. */
|
|
14
|
+
const warn = (id, label, ok, detail, fix) => ({
|
|
15
|
+
id,
|
|
16
|
+
label,
|
|
17
|
+
ok,
|
|
18
|
+
critical: false,
|
|
19
|
+
detail,
|
|
20
|
+
fix: ok ? undefined : fix,
|
|
21
|
+
});
|
|
22
|
+
/** `git check-ignore -v <path>`, or null when the path is not ignored (or git
|
|
23
|
+
* is unavailable / this is not a repo). The verbose form names the source:
|
|
24
|
+
* `<file>:<line>:<pattern>\t<path>`. */
|
|
25
|
+
function checkIgnoreSource(repoPath, relPath) {
|
|
26
|
+
try {
|
|
27
|
+
const out = execFileSync("git", ["-C", repoPath, "check-ignore", "-v", "--no-index", relPath], {
|
|
28
|
+
encoding: "utf8",
|
|
29
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
30
|
+
});
|
|
31
|
+
const first = out.split("\n")[0]?.trim();
|
|
32
|
+
return first ? first.split("\t")[0] : null;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// exit 1 = not ignored; anything else = no git. Both mean "no finding".
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function summaryChecks(repoPath) {
|
|
40
|
+
const dir = summariesDir(repoPath);
|
|
41
|
+
let shardCount = 0;
|
|
42
|
+
try {
|
|
43
|
+
shardCount = readdirSync(dir).filter(isShardFileName).length;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
shardCount = 0;
|
|
47
|
+
}
|
|
48
|
+
const legacyPresent = existsSync(join(repoPath, ".reposkein", "summaries.jsonl"));
|
|
49
|
+
if (shardCount === 0 && !legacyPresent)
|
|
50
|
+
return [];
|
|
51
|
+
const checks = [];
|
|
52
|
+
const loaded = loadSummaryShards(repoPath);
|
|
53
|
+
// 1) The pre-sharding single file is still there.
|
|
54
|
+
//
|
|
55
|
+
// One committed summaries.jsonl is exactly the artifact that made every
|
|
56
|
+
// branch conflict — the #35 lesson. It is read for one release so nothing
|
|
57
|
+
// is stranded, but leaving it is a conflict waiting to happen.
|
|
58
|
+
checks.push(warn("summaries_unsplit", "summaries sharded", !legacyPresent, legacyPresent
|
|
59
|
+
? `.reposkein/summaries.jsonl is still present (${loaded.summaries.size} record(s) readable); every branch that writes a summary will conflict on it`
|
|
60
|
+
: `${shardCount} shard(s), ${loaded.summaries.size} summar${loaded.summaries.size === 1 ? "y" : "ies"}`, "run `reposkein-indexer index .` — it folds the file into .reposkein/summaries/<xx>.jsonl and retires it; commit the result"));
|
|
61
|
+
// 2) A .gitignore pattern is masking the shards.
|
|
62
|
+
//
|
|
63
|
+
// A repo-root rule like `.reposkein/*` (or a blanket `*.jsonl`) silently
|
|
64
|
+
// keeps the shards out of every commit. Nothing errors — the summaries
|
|
65
|
+
// simply never reach a teammate, and are lost with the machine.
|
|
66
|
+
const probe = shardCount > 0 ? firstShardRelPath(repoPath) : ".reposkein/summaries/00.jsonl";
|
|
67
|
+
const ignoredBy = checkIgnoreSource(repoPath, probe);
|
|
68
|
+
checks.push(warn("summaries_committable", "summary shards committable", ignoredBy === null, ignoredBy === null
|
|
69
|
+
? "not git-ignored"
|
|
70
|
+
: `${probe} is ignored by ${ignoredBy} — authored summaries would never be committed`, "add a negation after that rule, e.g. `!**/.reposkein/summaries/`, then `git add .reposkein/summaries`"));
|
|
71
|
+
// 3) Damaged shard content: conflict markers or unparseable lines.
|
|
72
|
+
checks.push(warn("summaries_readable", "summary shards parse", loaded.warnings.length === 0, loaded.warnings.length === 0
|
|
73
|
+
? `${loaded.summaries.size} record(s) read cleanly`
|
|
74
|
+
: loaded.warnings.join("; "), "run `reposkein-indexer index .` to rewrite the shards canonically, then commit them"));
|
|
75
|
+
// 4) Divergent records preserved from a merge. Not an error — the resolution
|
|
76
|
+
// is deterministic — but a human should read the loser before it is
|
|
77
|
+
// forgotten, because nothing can regenerate it.
|
|
78
|
+
const conflicts = countRecordedConflicts(repoPath);
|
|
79
|
+
checks.push(warn("summaries_conflicts", "summary merge conflicts", conflicts === 0, conflicts === 0
|
|
80
|
+
? "none recorded"
|
|
81
|
+
: `${conflicts} record(s) lost a merge tiebreak and were preserved in .reposkein/local/conflicts.jsonl`, "read .reposkein/local/conflicts.jsonl; re-write any summary worth keeping, then delete the file"));
|
|
82
|
+
return checks;
|
|
83
|
+
}
|
|
84
|
+
function firstShardRelPath(repoPath) {
|
|
85
|
+
try {
|
|
86
|
+
const names = readdirSync(summariesDir(repoPath)).filter(isShardFileName).sort();
|
|
87
|
+
if (names[0])
|
|
88
|
+
return `.reposkein/summaries/${names[0]}`;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// fall through
|
|
92
|
+
}
|
|
93
|
+
return ".reposkein/summaries/00.jsonl";
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=doctorSummaries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctorSummaries.js","sourceRoot":"","sources":["../../src/cli/doctorSummaries.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface FederatedRepo {
|
|
2
|
+
/** repo_id from the nested repo's meta.json (or its directory name if the
|
|
3
|
+
* meta.json is missing/unreadable). */
|
|
4
|
+
repoId: string;
|
|
5
|
+
/** Path of the nested repo root, relative to the scanned root (posix-style). */
|
|
6
|
+
rootPath: string;
|
|
7
|
+
/** Absolute path of the nested repo root. */
|
|
8
|
+
absPath: string;
|
|
9
|
+
}
|
|
10
|
+
/** Scans descendants of `repoPath` for nested repos (directories containing a
|
|
11
|
+
* `.reposkein/` marker), excluding `repoPath` itself. Does not recurse past a
|
|
12
|
+
* hit — a repo nested inside another nested repo is out of scope for a single
|
|
13
|
+
* export pass. Bounded to `maxDepth` levels (default 6) so a pathological
|
|
14
|
+
* tree can't cause an unbounded scan. Deterministic (sorted by rootPath).
|
|
15
|
+
* Best-effort: unreadable directories are skipped, never thrown. */
|
|
16
|
+
export declare function discoverFederatedRepos(repoPath: string, maxDepth?: number): FederatedRepo[];
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { readdirSync, existsSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { join, relative, resolve } from "node:path";
|
|
3
|
+
/** Directories never descended into while scanning for nested repos: build
|
|
4
|
+
* output, VCS internals, other worktrees/tool state — mirrors the skip set
|
|
5
|
+
* used by store/resolveRepoPath.ts's walk-down discovery. */
|
|
6
|
+
const SKIP_DIR_NAMES = new Set([
|
|
7
|
+
"node_modules",
|
|
8
|
+
".git",
|
|
9
|
+
"target",
|
|
10
|
+
"dist",
|
|
11
|
+
".worktrees",
|
|
12
|
+
".claude",
|
|
13
|
+
]);
|
|
14
|
+
/** Scans descendants of `repoPath` for nested repos (directories containing a
|
|
15
|
+
* `.reposkein/` marker), excluding `repoPath` itself. Does not recurse past a
|
|
16
|
+
* hit — a repo nested inside another nested repo is out of scope for a single
|
|
17
|
+
* export pass. Bounded to `maxDepth` levels (default 6) so a pathological
|
|
18
|
+
* tree can't cause an unbounded scan. Deterministic (sorted by rootPath).
|
|
19
|
+
* Best-effort: unreadable directories are skipped, never thrown. */
|
|
20
|
+
export function discoverFederatedRepos(repoPath, maxDepth = 6) {
|
|
21
|
+
const root = resolve(repoPath);
|
|
22
|
+
const hits = [];
|
|
23
|
+
function isReposkeinDir(dir) {
|
|
24
|
+
try {
|
|
25
|
+
return statSync(join(dir, ".reposkein")).isDirectory();
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function recurse(dir, depth) {
|
|
32
|
+
if (depth > maxDepth)
|
|
33
|
+
return;
|
|
34
|
+
let entries;
|
|
35
|
+
try {
|
|
36
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return; // unreadable dir — skip, don't fail the export
|
|
40
|
+
}
|
|
41
|
+
for (const entry of entries) {
|
|
42
|
+
if (!entry.isDirectory())
|
|
43
|
+
continue;
|
|
44
|
+
if (SKIP_DIR_NAMES.has(entry.name))
|
|
45
|
+
continue;
|
|
46
|
+
if (entry.name === ".reposkein")
|
|
47
|
+
continue;
|
|
48
|
+
const full = join(dir, entry.name);
|
|
49
|
+
if (isReposkeinDir(full)) {
|
|
50
|
+
let repoId = entry.name;
|
|
51
|
+
const metaPath = join(full, ".reposkein", "meta.json");
|
|
52
|
+
if (existsSync(metaPath)) {
|
|
53
|
+
try {
|
|
54
|
+
const meta = JSON.parse(readFileSync(metaPath, "utf8"));
|
|
55
|
+
if (typeof meta.repo_id === "string" && meta.repo_id)
|
|
56
|
+
repoId = meta.repo_id;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// fall back to the directory name
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
hits.push({
|
|
63
|
+
repoId,
|
|
64
|
+
rootPath: relative(root, full).split("\\").join("/") || ".",
|
|
65
|
+
absPath: full,
|
|
66
|
+
});
|
|
67
|
+
continue; // don't descend further into a hit
|
|
68
|
+
}
|
|
69
|
+
recurse(full, depth + 1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
recurse(root, 1);
|
|
73
|
+
return hits.sort((a, b) => (a.rootPath < b.rootPath ? -1 : a.rootPath > b.rootPath ? 1 : 0));
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=federatedDiscovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"federatedDiscovery.js","sourceRoot":"","sources":["../../src/cli/federatedDiscovery.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/init.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AgentId, type WriteAgentConfigsOptions } from "./agentAdapters.js";
|
|
1
2
|
/** Where the navigation skill is installed for a repo (Claude project skills). */
|
|
2
3
|
export declare function skillTargetPath(repoPath: string): string;
|
|
3
4
|
/** The bundled SKILL.md inside the installed package (dist/SKILL.md). */
|
|
@@ -7,13 +8,75 @@ export declare function mcpConfigSnippet(repoPath: string): string;
|
|
|
7
8
|
/** Idempotently add the per-machine MCP config files to the repo's .gitignore
|
|
8
9
|
* (creating it if absent), so `reposkein-mcp init` never leaves them tracked. */
|
|
9
10
|
export declare function ensureLocalConfigGitignored(repoPath: string): void;
|
|
11
|
+
/** True when `.reposkein/meta.json` is tracked by git — this clone joined a
|
|
12
|
+
* repo that already has RepoSkein set up (someone else ran `init` and
|
|
13
|
+
* committed it), rather than being the first machine to set it up (whose
|
|
14
|
+
* meta.json exists on disk but isn't committed yet). Join mode skips
|
|
15
|
+
* first-time scaffolding entirely and instead wires up the local agent(s)
|
|
16
|
+
* automatically (REP-16 T5) — see `runInit`.
|
|
17
|
+
*
|
|
18
|
+
* Falls back to plain existence when git itself is unavailable (no `git`
|
|
19
|
+
* binary, or `repoPath` isn't a git repo yet) so `init` still behaves
|
|
20
|
+
* sensibly outside a git repo rather than throwing. */
|
|
21
|
+
export declare function detectJoinMode(repoPath: string): boolean;
|
|
22
|
+
/** Where `init --ci` writes the Pages-publish workflow in the target repo. */
|
|
23
|
+
export declare function ciWorkflowTargetPath(repoPath: string): string;
|
|
24
|
+
/** Template written by `reposkein-mcp init --ci`: a thin caller of RepoSkein's
|
|
25
|
+
* reusable publish workflow (see docs/HOSTING.md). Consumers get released
|
|
26
|
+
* indexer binaries (no Rust toolchain) + a self-contained static export
|
|
27
|
+
* published to GitHub Pages. Repo owners must still flip Settings → Pages →
|
|
28
|
+
* "Build and deployment: GitHub Actions" once. */
|
|
29
|
+
export declare function ciWorkflowTemplate(): string;
|
|
30
|
+
/** `reposkein-mcp init --ci`: writes the Pages-publish workflow template into
|
|
31
|
+
* the target repo's `.github/workflows/`. Non-destructive: skips (with a
|
|
32
|
+
* message) if the file already exists, so a re-run never clobbers a repo
|
|
33
|
+
* owner's edits. Returns whether it wrote the file. */
|
|
34
|
+
export declare function writeCiWorkflow(repoPath: string): {
|
|
35
|
+
written: boolean;
|
|
36
|
+
path: string;
|
|
37
|
+
};
|
|
10
38
|
/** `reposkein-mcp index [path]`: (re)build the code graph at `repoPath` using the
|
|
11
39
|
* native indexer. The package-friendly way to index — the `reposkein-indexer`
|
|
12
|
-
* binary is fetched into the package (not on PATH), so end users run this.
|
|
40
|
+
* binary is fetched into the package (not on PATH), so end users run this.
|
|
41
|
+
* On success, also records the current git HEAD to `.reposkein/local/indexed-at`
|
|
42
|
+
* — `doctor --ci`'s `graph_stale` check reads it back (see doctorFreshness.ts).
|
|
43
|
+
* The installed git hooks maintain the same marker themselves (`post-commit`
|
|
44
|
+
* after every commit, `post-merge`/`post-checkout` — which also re-index —
|
|
45
|
+
* after a merge/pull/branch switch; see the hook templates in
|
|
46
|
+
* indexer/crates/cli/src/main.rs), so this mcp-side write is the "manual
|
|
47
|
+
* index" case, not the only path that keeps the marker current. */
|
|
13
48
|
export declare function runIndex(repoPath?: string): Promise<number>;
|
|
14
|
-
|
|
15
|
-
* build the initial graph, install the navigation skill, and print the MCP config +
|
|
16
|
-
* next steps. Pass `index: false` to skip the initial index. */
|
|
17
|
-
export declare function runInit(repoPath?: string, opts?: {
|
|
49
|
+
export interface RunInitOptions {
|
|
18
50
|
index?: boolean;
|
|
19
|
-
|
|
51
|
+
ci?: boolean;
|
|
52
|
+
/** Explicit `--agents` override for join mode; detected when omitted. */
|
|
53
|
+
agents?: AgentId[];
|
|
54
|
+
/** `--dry-run`: plan agent config writes without touching disk. */
|
|
55
|
+
dryRun?: boolean;
|
|
56
|
+
/** Force join-mode on/off; auto-detected (committed .reposkein/meta.json)
|
|
57
|
+
* when omitted — see `detectJoinMode`. */
|
|
58
|
+
join?: boolean;
|
|
59
|
+
/** Test-only injection point for the agent-adapter step (fake `exec`/`now`). */
|
|
60
|
+
agentWriteOpts?: Pick<WriteAgentConfigsOptions, "exec" | "now">;
|
|
61
|
+
}
|
|
62
|
+
/** `reposkein-mcp init [path] [--no-index] [--ci] [--agents <list>] [--dry-run]`:
|
|
63
|
+
* ensure the indexer, install git hooks, build the initial graph, install
|
|
64
|
+
* the navigation skill, then either (join mode) auto-write the local
|
|
65
|
+
* agent(s) MCP config, or (first-time) print the config snippet to paste in
|
|
66
|
+
* by hand. Pass `index: false` to skip the initial index; `ci: true` to
|
|
67
|
+
* also write the GitHub Pages publish workflow (see docs/HOSTING.md).
|
|
68
|
+
*
|
|
69
|
+
* Join mode (REP-16 T5): auto-detected when `.reposkein/meta.json` is
|
|
70
|
+
* already committed (`detectJoinMode`) — i.e. someone cloned a repo that
|
|
71
|
+
* already has RepoSkein set up. Skips nothing structural (hooks/index/skill
|
|
72
|
+
* still run so a stale local checkout gets caught up) but additionally
|
|
73
|
+
* writes `.mcp.json` / `opencode.json` / `.cursor/mcp.json` for whichever
|
|
74
|
+
* agent(s) are detected present (or `opts.agents`), then prints a doctor
|
|
75
|
+
* summary instead of a config snippet to copy by hand.
|
|
76
|
+
*
|
|
77
|
+
* Unsupported platform (darwin-x64, win32-arm64, no REPOSKEIN_INDEXER_BIN):
|
|
78
|
+
* prints cargo-install fallback instructions and CONTINUES instead of
|
|
79
|
+
* crashing — hooks + the initial index are skipped (nothing to run them
|
|
80
|
+
* with), but the skill, CI workflow, and (in join mode) agent config are
|
|
81
|
+
* still written, and the function returns 0. */
|
|
82
|
+
export declare function runInit(repoPath?: string, opts?: RunInitOptions): Promise<number>;
|