@opencodehub/mcp 0.1.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.
Files changed (210) hide show
  1. package/README.md +62 -0
  2. package/dist/analysis-bridge.d.ts +23 -0
  3. package/dist/analysis-bridge.d.ts.map +1 -0
  4. package/dist/analysis-bridge.js +83 -0
  5. package/dist/analysis-bridge.js.map +1 -0
  6. package/dist/connection-pool.d.ts +76 -0
  7. package/dist/connection-pool.d.ts.map +1 -0
  8. package/dist/connection-pool.js +179 -0
  9. package/dist/connection-pool.js.map +1 -0
  10. package/dist/error-envelope.d.ts +97 -0
  11. package/dist/error-envelope.d.ts.map +1 -0
  12. package/dist/error-envelope.js +75 -0
  13. package/dist/error-envelope.js.map +1 -0
  14. package/dist/group-resolver.d.ts +29 -0
  15. package/dist/group-resolver.d.ts.map +1 -0
  16. package/dist/group-resolver.js +100 -0
  17. package/dist/group-resolver.js.map +1 -0
  18. package/dist/index.d.ts +43 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +54 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/next-step-hints.d.ts +24 -0
  23. package/dist/next-step-hints.d.ts.map +1 -0
  24. package/dist/next-step-hints.js +41 -0
  25. package/dist/next-step-hints.js.map +1 -0
  26. package/dist/repo-resolver.d.ts +88 -0
  27. package/dist/repo-resolver.d.ts.map +1 -0
  28. package/dist/repo-resolver.js +211 -0
  29. package/dist/repo-resolver.js.map +1 -0
  30. package/dist/repo-uri-for-entry.d.ts +25 -0
  31. package/dist/repo-uri-for-entry.d.ts.map +1 -0
  32. package/dist/repo-uri-for-entry.js +64 -0
  33. package/dist/repo-uri-for-entry.js.map +1 -0
  34. package/dist/resources/repo-cluster.d.ts +19 -0
  35. package/dist/resources/repo-cluster.d.ts.map +1 -0
  36. package/dist/resources/repo-cluster.js +203 -0
  37. package/dist/resources/repo-cluster.js.map +1 -0
  38. package/dist/resources/repo-clusters.d.ts +14 -0
  39. package/dist/resources/repo-clusters.d.ts.map +1 -0
  40. package/dist/resources/repo-clusters.js +97 -0
  41. package/dist/resources/repo-clusters.js.map +1 -0
  42. package/dist/resources/repo-context.d.ts +12 -0
  43. package/dist/resources/repo-context.d.ts.map +1 -0
  44. package/dist/resources/repo-context.js +84 -0
  45. package/dist/resources/repo-context.js.map +1 -0
  46. package/dist/resources/repo-process.d.ts +19 -0
  47. package/dist/resources/repo-process.d.ts.map +1 -0
  48. package/dist/resources/repo-process.js +220 -0
  49. package/dist/resources/repo-process.js.map +1 -0
  50. package/dist/resources/repo-processes.d.ts +13 -0
  51. package/dist/resources/repo-processes.d.ts.map +1 -0
  52. package/dist/resources/repo-processes.js +99 -0
  53. package/dist/resources/repo-processes.js.map +1 -0
  54. package/dist/resources/repo-schema.d.ts +13 -0
  55. package/dist/resources/repo-schema.d.ts.map +1 -0
  56. package/dist/resources/repo-schema.js +99 -0
  57. package/dist/resources/repo-schema.js.map +1 -0
  58. package/dist/resources/repos.d.ts +20 -0
  59. package/dist/resources/repos.d.ts.map +1 -0
  60. package/dist/resources/repos.js +58 -0
  61. package/dist/resources/repos.js.map +1 -0
  62. package/dist/resources/store-helper.d.ts +28 -0
  63. package/dist/resources/store-helper.d.ts.map +1 -0
  64. package/dist/resources/store-helper.js +58 -0
  65. package/dist/resources/store-helper.js.map +1 -0
  66. package/dist/resources/yaml.d.ts +10 -0
  67. package/dist/resources/yaml.d.ts.map +1 -0
  68. package/dist/resources/yaml.js +16 -0
  69. package/dist/resources/yaml.js.map +1 -0
  70. package/dist/server.d.ts +46 -0
  71. package/dist/server.d.ts.map +1 -0
  72. package/dist/server.js +194 -0
  73. package/dist/server.js.map +1 -0
  74. package/dist/staleness.d.ts +19 -0
  75. package/dist/staleness.d.ts.map +1 -0
  76. package/dist/staleness.js +40 -0
  77. package/dist/staleness.js.map +1 -0
  78. package/dist/test-utils.d.ts +170 -0
  79. package/dist/test-utils.d.ts.map +1 -0
  80. package/dist/test-utils.js +473 -0
  81. package/dist/test-utils.js.map +1 -0
  82. package/dist/tools/api-impact.d.ts +47 -0
  83. package/dist/tools/api-impact.d.ts.map +1 -0
  84. package/dist/tools/api-impact.js +199 -0
  85. package/dist/tools/api-impact.js.map +1 -0
  86. package/dist/tools/confidence.d.ts +39 -0
  87. package/dist/tools/confidence.d.ts.map +1 -0
  88. package/dist/tools/confidence.js +58 -0
  89. package/dist/tools/confidence.js.map +1 -0
  90. package/dist/tools/context.d.ts +47 -0
  91. package/dist/tools/context.d.ts.map +1 -0
  92. package/dist/tools/context.js +577 -0
  93. package/dist/tools/context.js.map +1 -0
  94. package/dist/tools/dependencies.d.ts +29 -0
  95. package/dist/tools/dependencies.d.ts.map +1 -0
  96. package/dist/tools/dependencies.js +110 -0
  97. package/dist/tools/dependencies.js.map +1 -0
  98. package/dist/tools/detect-changes.d.ts +15 -0
  99. package/dist/tools/detect-changes.d.ts.map +1 -0
  100. package/dist/tools/detect-changes.js +78 -0
  101. package/dist/tools/detect-changes.js.map +1 -0
  102. package/dist/tools/group-contracts.d.ts +26 -0
  103. package/dist/tools/group-contracts.d.ts.map +1 -0
  104. package/dist/tools/group-contracts.js +251 -0
  105. package/dist/tools/group-contracts.js.map +1 -0
  106. package/dist/tools/group-cross-repo-links.d.ts +28 -0
  107. package/dist/tools/group-cross-repo-links.d.ts.map +1 -0
  108. package/dist/tools/group-cross-repo-links.js +128 -0
  109. package/dist/tools/group-cross-repo-links.js.map +1 -0
  110. package/dist/tools/group-list.d.ts +10 -0
  111. package/dist/tools/group-list.d.ts.map +1 -0
  112. package/dist/tools/group-list.js +74 -0
  113. package/dist/tools/group-list.js.map +1 -0
  114. package/dist/tools/group-query.d.ts +40 -0
  115. package/dist/tools/group-query.d.ts.map +1 -0
  116. package/dist/tools/group-query.js +209 -0
  117. package/dist/tools/group-query.js.map +1 -0
  118. package/dist/tools/group-status.d.ts +21 -0
  119. package/dist/tools/group-status.d.ts.map +1 -0
  120. package/dist/tools/group-status.js +121 -0
  121. package/dist/tools/group-status.js.map +1 -0
  122. package/dist/tools/group-sync.d.ts +23 -0
  123. package/dist/tools/group-sync.d.ts.map +1 -0
  124. package/dist/tools/group-sync.js +112 -0
  125. package/dist/tools/group-sync.js.map +1 -0
  126. package/dist/tools/impact.d.ts +36 -0
  127. package/dist/tools/impact.d.ts.map +1 -0
  128. package/dist/tools/impact.js +232 -0
  129. package/dist/tools/impact.js.map +1 -0
  130. package/dist/tools/license-audit.d.ts +34 -0
  131. package/dist/tools/license-audit.d.ts.map +1 -0
  132. package/dist/tools/license-audit.js +108 -0
  133. package/dist/tools/license-audit.js.map +1 -0
  134. package/dist/tools/list-dead-code.d.ts +26 -0
  135. package/dist/tools/list-dead-code.d.ts.map +1 -0
  136. package/dist/tools/list-dead-code.js +110 -0
  137. package/dist/tools/list-dead-code.js.map +1 -0
  138. package/dist/tools/list-findings-delta.d.ts +36 -0
  139. package/dist/tools/list-findings-delta.d.ts.map +1 -0
  140. package/dist/tools/list-findings-delta.js +274 -0
  141. package/dist/tools/list-findings-delta.js.map +1 -0
  142. package/dist/tools/list-findings.d.ts +30 -0
  143. package/dist/tools/list-findings.d.ts.map +1 -0
  144. package/dist/tools/list-findings.js +129 -0
  145. package/dist/tools/list-findings.js.map +1 -0
  146. package/dist/tools/list-repos.d.ts +17 -0
  147. package/dist/tools/list-repos.d.ts.map +1 -0
  148. package/dist/tools/list-repos.js +63 -0
  149. package/dist/tools/list-repos.js.map +1 -0
  150. package/dist/tools/owners.d.ts +23 -0
  151. package/dist/tools/owners.d.ts.map +1 -0
  152. package/dist/tools/owners.js +103 -0
  153. package/dist/tools/owners.js.map +1 -0
  154. package/dist/tools/pack-codebase.d.ts +76 -0
  155. package/dist/tools/pack-codebase.d.ts.map +1 -0
  156. package/dist/tools/pack-codebase.js +289 -0
  157. package/dist/tools/pack-codebase.js.map +1 -0
  158. package/dist/tools/project-profile.d.ts +28 -0
  159. package/dist/tools/project-profile.d.ts.map +1 -0
  160. package/dist/tools/project-profile.js +109 -0
  161. package/dist/tools/project-profile.js.map +1 -0
  162. package/dist/tools/query.d.ts +63 -0
  163. package/dist/tools/query.d.ts.map +1 -0
  164. package/dist/tools/query.js +662 -0
  165. package/dist/tools/query.js.map +1 -0
  166. package/dist/tools/remove-dead-code.d.ts +47 -0
  167. package/dist/tools/remove-dead-code.d.ts.map +1 -0
  168. package/dist/tools/remove-dead-code.js +258 -0
  169. package/dist/tools/remove-dead-code.js.map +1 -0
  170. package/dist/tools/rename.d.ts +21 -0
  171. package/dist/tools/rename.d.ts.map +1 -0
  172. package/dist/tools/rename.js +116 -0
  173. package/dist/tools/rename.js.map +1 -0
  174. package/dist/tools/risk-trends.d.ts +19 -0
  175. package/dist/tools/risk-trends.d.ts.map +1 -0
  176. package/dist/tools/risk-trends.js +73 -0
  177. package/dist/tools/risk-trends.js.map +1 -0
  178. package/dist/tools/route-map.d.ts +27 -0
  179. package/dist/tools/route-map.d.ts.map +1 -0
  180. package/dist/tools/route-map.js +119 -0
  181. package/dist/tools/route-map.js.map +1 -0
  182. package/dist/tools/scan.d.ts +27 -0
  183. package/dist/tools/scan.d.ts.map +1 -0
  184. package/dist/tools/scan.js +136 -0
  185. package/dist/tools/scan.js.map +1 -0
  186. package/dist/tools/shape-check.d.ts +53 -0
  187. package/dist/tools/shape-check.d.ts.map +1 -0
  188. package/dist/tools/shape-check.js +161 -0
  189. package/dist/tools/shape-check.js.map +1 -0
  190. package/dist/tools/shared.d.ts +101 -0
  191. package/dist/tools/shared.d.ts.map +1 -0
  192. package/dist/tools/shared.js +114 -0
  193. package/dist/tools/shared.js.map +1 -0
  194. package/dist/tools/signature.d.ts +38 -0
  195. package/dist/tools/signature.d.ts.map +1 -0
  196. package/dist/tools/signature.js +332 -0
  197. package/dist/tools/signature.js.map +1 -0
  198. package/dist/tools/sql.d.ts +34 -0
  199. package/dist/tools/sql.d.ts.map +1 -0
  200. package/dist/tools/sql.js +222 -0
  201. package/dist/tools/sql.js.map +1 -0
  202. package/dist/tools/tool-map.d.ts +24 -0
  203. package/dist/tools/tool-map.d.ts.map +1 -0
  204. package/dist/tools/tool-map.js +97 -0
  205. package/dist/tools/tool-map.js.map +1 -0
  206. package/dist/tools/verdict.d.ts +33 -0
  207. package/dist/tools/verdict.d.ts.map +1 -0
  208. package/dist/tools/verdict.js +102 -0
  209. package/dist/tools/verdict.js.map +1 -0
  210. package/package.json +76 -0
@@ -0,0 +1,128 @@
1
+ /**
2
+ * `group_cross_repo_links` — sourced cross-repo link graph for Phase E.
3
+ *
4
+ * The `codehub-document` skill calls this during its Phase E assembler
5
+ * (group mode) and embeds the returned `links[]` verbatim into the
6
+ * `.docmeta.json` v2 `cross_repo_links[]` field. The skill does the
7
+ * Markdown rendering; this tool only emits data.
8
+ *
9
+ * Data path: loads the persisted ContractRegistry written by `group_sync`
10
+ * (at `<home>/.codehub/groups/<name>/contracts.json`), maps each
11
+ * repo name to its stable `repo_uri` via `deriveRepoUri`, and hands off
12
+ * to the pure analysis helper `computeCrossRepoLinks`. The helper does
13
+ * the sort + dedup + relation inference; the tool only wires I/O.
14
+ *
15
+ * Annotations: readOnlyHint, idempotentHint, openWorldHint:false — the
16
+ * tool reads two files (group descriptor + persisted registry) and
17
+ * computes from them. Never writes.
18
+ */
19
+ import { readFile } from "node:fs/promises";
20
+ import { computeCrossRepoLinks } from "@opencodehub/analysis";
21
+ import { z } from "zod";
22
+ import { toolError, toolErrorFromUnknown } from "../error-envelope.js";
23
+ import { readGroup } from "../group-resolver.js";
24
+ import { withNextSteps } from "../next-step-hints.js";
25
+ import { deriveRepoUri, readRegistry } from "../repo-resolver.js";
26
+ import { resolveGroupContractsPath } from "./group-sync.js";
27
+ import { fromToolResult, toToolResult } from "./shared.js";
28
+ const GroupCrossRepoLinksInput = {
29
+ groupName: z.string().min(1).describe("Name of the group to compute links for."),
30
+ docPathScheme: z
31
+ .enum(["default", "per-repo-landing-only"])
32
+ .optional()
33
+ .describe("Doc-path scheme. Defaults to `per-repo-landing-only` (one link per repo-pair pointing at the target repo's architecture landing page)."),
34
+ };
35
+ /**
36
+ * Load `<home>/.codehub/groups/<name>/contracts.json`. Returns `null`
37
+ * when the file does not exist or fails to parse. Callers surface a
38
+ * friendly hint to run `group_sync` in that case.
39
+ */
40
+ async function loadPersistedRegistry(groupName, home) {
41
+ const path = resolveGroupContractsPath(groupName, home);
42
+ try {
43
+ const raw = await readFile(path, "utf8");
44
+ return JSON.parse(raw);
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ }
50
+ export async function runGroupCrossRepoLinks(ctx, args) {
51
+ try {
52
+ const opts = ctx.home !== undefined ? { home: ctx.home } : {};
53
+ const group = await readGroup(args.groupName, opts);
54
+ if (!group) {
55
+ return toToolResult(toolError("NOT_FOUND", `Group ${args.groupName} is not defined.`, "Run `codehub group list` to see defined groups."));
56
+ }
57
+ const persisted = await loadPersistedRegistry(args.groupName, ctx.home);
58
+ if (!persisted) {
59
+ return toToolResult(withNextSteps(`No persisted contract registry for group ${args.groupName}. Run \`group_sync\` first — no cross-repo links can be computed until the registry materializes.`, {
60
+ groupName: args.groupName,
61
+ links: [],
62
+ registryPath: null,
63
+ registryComputedAt: null,
64
+ }, [
65
+ `call \`group_sync\` with groupName="${args.groupName}" to materialize the cross-link registry`,
66
+ `after \`group_sync\`, call \`group_cross_repo_links\` with groupName="${args.groupName}" again`,
67
+ ]));
68
+ }
69
+ // Build repo → repo_uri map from the registry. Repos that are in the
70
+ // group descriptor but not in the registry are silently skipped — the
71
+ // helper treats "unknown repo" as "drop from graph" so the output stays
72
+ // consistent even when a group member is not yet indexed.
73
+ const registry = await readRegistry(opts);
74
+ const repoUriByName = new Map();
75
+ for (const repo of group.repos) {
76
+ const entry = registry[repo.name];
77
+ if (!entry)
78
+ continue;
79
+ repoUriByName.set(repo.name, deriveRepoUri(entry));
80
+ }
81
+ const links = computeCrossRepoLinks({
82
+ groupName: args.groupName,
83
+ crossLinks: persisted.crossLinks,
84
+ repoUriByName,
85
+ ...(args.docPathScheme !== undefined ? { docPathScheme: args.docPathScheme } : {}),
86
+ });
87
+ const header = `group_cross_repo_links: ${links.length} sourced link(s) across ${group.repos.length} repo(s) in ${group.name}.`;
88
+ const body = links.length === 0
89
+ ? "(no cross-repo links — either no contracts matched or repos are unregistered)"
90
+ : links
91
+ .slice(0, 50)
92
+ .map((l) => `- [${l.source_repo_uri}] ${l.source_doc_path} → [${l.target_repo_uri}] ${l.target_doc_path} (${l.relation})`)
93
+ .join("\n");
94
+ const tail = links.length > 50 ? `\n… and ${links.length - 50} more` : "";
95
+ const next = links.length === 0
96
+ ? [
97
+ `call \`group_contracts\` with groupName="${group.name}" to inspect producer↔consumer pairs`,
98
+ `call \`group_sync\` with groupName="${group.name}" to refresh the cross-link registry`,
99
+ ]
100
+ : [
101
+ `embed the \`links\` array verbatim into .docmeta.json \`cross_repo_links[]\` (schema v2)`,
102
+ `call \`group_contracts\` with groupName="${group.name}" to see the underlying contract rows`,
103
+ ];
104
+ return toToolResult(withNextSteps(`${header}\n${body}${tail}`, {
105
+ groupName: group.name,
106
+ links,
107
+ registryPath: resolveGroupContractsPath(group.name, ctx.home),
108
+ registryComputedAt: persisted.computedAt,
109
+ }, next));
110
+ }
111
+ catch (err) {
112
+ return toToolResult(toolErrorFromUnknown(err));
113
+ }
114
+ }
115
+ export function registerGroupCrossRepoLinksTool(server, ctx) {
116
+ server.registerTool("group_cross_repo_links", {
117
+ title: "Sourced cross-repo link graph for `.docmeta.json` v2",
118
+ description: "Emit the sourced, alpha-sorted cross-repo link graph for a named group. Loads the persisted ContractRegistry from `group_sync` and emits a `CrossRepoLink[]` with `depends_on` (consumer → producer) and `consumer_of` (producer → consumer) relations per matched contract. The `codehub-document` skill embeds this array verbatim into `.docmeta.json` v2's `cross_repo_links[]` field during Phase E; the skill also renders the `## See also (other repos in group)` footer from it. If `group_sync` has not run, `links` is empty and the hint directs the caller to run it first.",
119
+ inputSchema: GroupCrossRepoLinksInput,
120
+ annotations: {
121
+ readOnlyHint: true,
122
+ destructiveHint: false,
123
+ idempotentHint: true,
124
+ openWorldHint: false,
125
+ },
126
+ }, async (args) => fromToolResult(await runGroupCrossRepoLinks(ctx, args)));
127
+ }
128
+ //# sourceMappingURL=group-cross-repo-links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group-cross-repo-links.js","sourceRoot":"","sources":["../../src/tools/group-cross-repo-links.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `group_list` — enumerate every cross-repo group under
3
+ * `~/.codehub/groups/*.json`. Read-only. Deterministic output (groups and
4
+ * repos both sorted by name).
5
+ */
6
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import { type ToolContext, type ToolResult } from "./shared.js";
8
+ export declare function runGroupList(ctx: ToolContext): Promise<ToolResult>;
9
+ export declare function registerGroupListTool(server: McpServer, ctx: ToolContext): void;
10
+ //# sourceMappingURL=group-list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group-list.d.ts","sourceRoot":"","sources":["../../src/tools/group-list.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAMzE,OAAO,EAAkB,KAAK,WAAW,EAAE,KAAK,UAAU,EAAgB,MAAM,aAAa,CAAC;AAoB9F,wBAAsB,YAAY,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAqDxE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAgB/E"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * `group_list` — enumerate every cross-repo group under
3
+ * `~/.codehub/groups/*.json`. Read-only. Deterministic output (groups and
4
+ * repos both sorted by name).
5
+ */
6
+ import { toolErrorFromUnknown } from "../error-envelope.js";
7
+ import { listGroups } from "../group-resolver.js";
8
+ import { withNextSteps } from "../next-step-hints.js";
9
+ import { deriveRepoUri, readRegistry } from "../repo-resolver.js";
10
+ import { repoUriForEntry } from "../repo-uri-for-entry.js";
11
+ import { fromToolResult, toToolResult } from "./shared.js";
12
+ export async function runGroupList(ctx) {
13
+ try {
14
+ const opts = ctx.home !== undefined ? { home: ctx.home } : {};
15
+ const raw = await listGroups(opts);
16
+ const registry = await readRegistry(opts);
17
+ const groups = [];
18
+ for (const g of raw) {
19
+ const repos = [];
20
+ for (const r of g.repos) {
21
+ const entry = registry[r.name];
22
+ // Prefer the graph-backed RepoNode.repoUri when the repo
23
+ // is registered; otherwise fall back to deriveRepoUri against a
24
+ // synthetic entry built from the group record so orphan references
25
+ // still receive a stable `local:<hash>`.
26
+ const repo_uri = entry
27
+ ? await repoUriForEntry(entry, ctx.pool)
28
+ : deriveRepoUri({
29
+ name: r.name,
30
+ path: r.path,
31
+ indexedAt: "",
32
+ nodeCount: 0,
33
+ edgeCount: 0,
34
+ });
35
+ repos.push({ name: r.name, path: r.path, repo_uri });
36
+ }
37
+ groups.push({
38
+ name: g.name,
39
+ createdAt: g.createdAt,
40
+ repos,
41
+ ...(g.description !== undefined ? { description: g.description } : {}),
42
+ });
43
+ }
44
+ const header = `Groups (${groups.length}):`;
45
+ const body = groups.length === 0
46
+ ? "(none — create one with `codehub group create <name> <repo> <repo> ...`)"
47
+ : groups
48
+ .map((g) => `- ${g.name} (${g.repos.length} repo${g.repos.length === 1 ? "" : "s"}): ${g.repos.map((r) => r.name).join(", ")}`)
49
+ .join("\n");
50
+ const next = groups.length === 0
51
+ ? ["run `codehub group create <name> <repo1> <repo2>` to define a group"]
52
+ : [
53
+ `call \`group_status\` with groupName="${groups[0]?.name ?? ""}" to see per-repo freshness`,
54
+ `call \`group_query\` with groupName + query to fan out BM25 across the group`,
55
+ ];
56
+ return toToolResult(withNextSteps(`${header}\n${body}`, { groups }, next));
57
+ }
58
+ catch (err) {
59
+ return toToolResult(toolErrorFromUnknown(err));
60
+ }
61
+ }
62
+ export function registerGroupListTool(server, ctx) {
63
+ server.registerTool("group_list", {
64
+ title: "List cross-repo groups",
65
+ description: "Enumerate every cross-repo group defined under ~/.codehub/groups. Groups bundle already-indexed repos so an agent can run one query across a whole stack (web-client + api-server + shared libs). Returns each group's name, creation timestamp, optional description, and constituent repos.",
66
+ annotations: {
67
+ readOnlyHint: true,
68
+ destructiveHint: false,
69
+ idempotentHint: true,
70
+ openWorldHint: false,
71
+ },
72
+ }, async () => fromToolResult(await runGroupList(ctx)));
73
+ }
74
+ //# sourceMappingURL=group-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group-list.js","sourceRoot":"","sources":["../../src/tools/group-list.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * `group_query` — BM25 on each repo in a group, fused with RRF (k=60).
3
+ *
4
+ * Response contract:
5
+ * {
6
+ * group: string,
7
+ * query: string,
8
+ * results: [{ _repo, _rrf_score, nodeId, name, kind, filePath }],
9
+ * per_repo: [{ repo, count, error? }],
10
+ * warnings: string[],
11
+ * }
12
+ *
13
+ * Determinism:
14
+ * - Repo iteration is the alphabetical sort of `group.repos[*].name`.
15
+ * - BM25 ties are broken by `id ASC` in the DuckDB adapter.
16
+ * - RRF tiebreak falls back to lex `(_repo, nodeId)` ordering (the
17
+ * underlying `rrf()` breaks ties by first-run / first-rank; we do the
18
+ * final lex pass ourselves to keep cross-run order stable).
19
+ * - Per-repo BM25 limit scales with the outer `limit` so small groups
20
+ * don't silently truncate and large groups don't pull unnecessarily.
21
+ *
22
+ * Graceful degradation:
23
+ * - A repo missing from the registry, a missing DB file, or a DuckDB
24
+ * open error all emit a `per_repo[]` row with an `error` string and a
25
+ * human-readable entry in `warnings[]`. The fan-out continues; the
26
+ * tool never aborts unless the group itself is unknown.
27
+ */
28
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
29
+ import { type ToolContext, type ToolResult } from "./shared.js";
30
+ interface GroupQueryArgs {
31
+ readonly groupName: string;
32
+ readonly query: string;
33
+ readonly limit?: number | undefined;
34
+ readonly subgroup?: readonly string[] | undefined;
35
+ readonly kinds?: readonly string[] | undefined;
36
+ }
37
+ export declare function runGroupQuery(ctx: ToolContext, args: GroupQueryArgs): Promise<ToolResult>;
38
+ export declare function registerGroupQueryTool(server: McpServer, ctx: ToolContext): void;
39
+ export {};
40
+ //# sourceMappingURL=group-query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group-query.d.ts","sourceRoot":"","sources":["../../src/tools/group-query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AASzE,OAAO,EAAkB,KAAK,WAAW,EAAE,KAAK,UAAU,EAAgB,MAAM,aAAa,CAAC;AAkE9F,UAAU,cAAc;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAClD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CAChD;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CAmJ/F;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAiBhF"}
@@ -0,0 +1,209 @@
1
+ /**
2
+ * `group_query` — BM25 on each repo in a group, fused with RRF (k=60).
3
+ *
4
+ * Response contract:
5
+ * {
6
+ * group: string,
7
+ * query: string,
8
+ * results: [{ _repo, _rrf_score, nodeId, name, kind, filePath }],
9
+ * per_repo: [{ repo, count, error? }],
10
+ * warnings: string[],
11
+ * }
12
+ *
13
+ * Determinism:
14
+ * - Repo iteration is the alphabetical sort of `group.repos[*].name`.
15
+ * - BM25 ties are broken by `id ASC` in the DuckDB adapter.
16
+ * - RRF tiebreak falls back to lex `(_repo, nodeId)` ordering (the
17
+ * underlying `rrf()` breaks ties by first-run / first-rank; we do the
18
+ * final lex pass ourselves to keep cross-run order stable).
19
+ * - Per-repo BM25 limit scales with the outer `limit` so small groups
20
+ * don't silently truncate and large groups don't pull unnecessarily.
21
+ *
22
+ * Graceful degradation:
23
+ * - A repo missing from the registry, a missing DB file, or a DuckDB
24
+ * open error all emit a `per_repo[]` row with an `error` string and a
25
+ * human-readable entry in `warnings[]`. The fan-out continues; the
26
+ * tool never aborts unless the group itself is unknown.
27
+ */
28
+ // biome-ignore-all lint/complexity/useLiteralKeys: dot-access disallowed on Record index signatures
29
+ import { resolve } from "node:path";
30
+ import { bm25Search, DEFAULT_RRF_K, DEFAULT_RRF_TOP_K, rrf } from "@opencodehub/search";
31
+ import { resolveDbPath } from "@opencodehub/storage";
32
+ import { z } from "zod";
33
+ import { toolError, toolErrorFromUnknown } from "../error-envelope.js";
34
+ import { readGroup } from "../group-resolver.js";
35
+ import { withNextSteps } from "../next-step-hints.js";
36
+ import { readRegistry } from "../repo-resolver.js";
37
+ import { repoUriForEntry } from "../repo-uri-for-entry.js";
38
+ import { fromToolResult, toToolResult } from "./shared.js";
39
+ const GroupQueryInput = {
40
+ groupName: z.string().min(1).describe("Name of the group to query."),
41
+ query: z.string().min(1).describe("Free-text BM25 search phrase."),
42
+ limit: z
43
+ .number()
44
+ .int()
45
+ .positive()
46
+ .max(100)
47
+ .optional()
48
+ .describe("Maximum merged results to return (default 20, max 100)."),
49
+ subgroup: z
50
+ .array(z.string().min(1))
51
+ .optional()
52
+ .describe("Optional filter — only these repo names (by registry name) from the group are queried. Unknown names are silently ignored."),
53
+ kinds: z
54
+ .array(z.string().min(1))
55
+ .optional()
56
+ .describe('Optional node-kind filter passed through to BM25 on every member repo (e.g. ["Function", "Method"]).'),
57
+ };
58
+ /** Soft caps so the per-repo limit stays bounded regardless of caller input. */
59
+ const PER_REPO_BM25_MIN = 20;
60
+ const PER_REPO_BM25_MAX = 200;
61
+ /**
62
+ * Scale per-repo BM25 limit with outer limit + group cardinality.
63
+ * Heuristic: each member contributes up to `2 * limit / numRepos` hits, floor
64
+ * at the outer limit (so a single-repo group still gets `limit` candidates),
65
+ * cap at 200 to protect the RRF merge loop.
66
+ */
67
+ function perRepoBm25Limit(outerLimit, numRepos) {
68
+ if (numRepos <= 0)
69
+ return outerLimit;
70
+ const heuristic = Math.ceil((outerLimit * 2) / numRepos);
71
+ return Math.max(PER_REPO_BM25_MIN, Math.min(PER_REPO_BM25_MAX, Math.max(outerLimit, heuristic)));
72
+ }
73
+ export async function runGroupQuery(ctx, args) {
74
+ try {
75
+ const limit = args.limit ?? 20;
76
+ const opts = ctx.home !== undefined ? { home: ctx.home } : {};
77
+ const group = await readGroup(args.groupName, opts);
78
+ if (!group) {
79
+ return toToolResult(toolError("NOT_FOUND", `Group ${args.groupName} is not defined.`, "Run `codehub group list` to see defined groups."));
80
+ }
81
+ const registry = await readRegistry(opts);
82
+ // Stable iteration order: alphabetical by registry name.
83
+ const sortedRepos = [...group.repos].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
84
+ // Optional subgroup filter — skip members not in the subgroup set.
85
+ const subgroupFilter = args.subgroup && args.subgroup.length > 0 ? new Set(args.subgroup) : undefined;
86
+ const targetRepos = subgroupFilter
87
+ ? sortedRepos.filter((r) => subgroupFilter.has(r.name))
88
+ : sortedRepos;
89
+ const perRepoLimit = perRepoBm25Limit(limit, Math.max(targetRepos.length, 1));
90
+ const meta = new Map();
91
+ const runs = [];
92
+ const perRepo = [];
93
+ const warnings = [];
94
+ for (const repo of targetRepos) {
95
+ const hit = registry[repo.name];
96
+ if (!hit) {
97
+ perRepo.push({ repo: repo.name, count: 0, error: "not_in_registry" });
98
+ warnings.push(`${repo.name}: not in registry — run \`codehub analyze\` in that repo, then retry.`);
99
+ continue;
100
+ }
101
+ // Additive field — resolve once per repo so every result row from
102
+ // this repo receives the same `_repo_uri`. Best-effort: the
103
+ // helper falls back to `deriveRepoUri` on any DB failure.
104
+ const repoUri = await repoUriForEntry(hit, ctx.pool);
105
+ const repoPath = resolve(hit.path);
106
+ const dbPath = resolveDbPath(repoPath);
107
+ let store;
108
+ try {
109
+ store = await ctx.pool.acquire(repoPath, dbPath);
110
+ }
111
+ catch (err) {
112
+ const msg = err instanceof Error ? err.message : String(err);
113
+ perRepo.push({ repo: repo.name, count: 0, error: "open_failed" });
114
+ warnings.push(`${repo.name}: failed to open index (${msg}).`);
115
+ continue;
116
+ }
117
+ try {
118
+ const bm25Query = args.kinds && args.kinds.length > 0
119
+ ? { text: args.query, kinds: args.kinds, limit: perRepoLimit }
120
+ : { text: args.query, limit: perRepoLimit };
121
+ const results = await bm25Search(store.graph, bm25Query);
122
+ const ranked = [];
123
+ for (const r of results) {
124
+ const id = `${repo.name}::${r.nodeId}`;
125
+ ranked.push({ id });
126
+ if (!meta.has(id)) {
127
+ meta.set(id, {
128
+ _repo: repo.name,
129
+ _repo_uri: repoUri,
130
+ _rrf_score: 0,
131
+ nodeId: r.nodeId,
132
+ name: r.name,
133
+ kind: r.kind,
134
+ filePath: r.filePath,
135
+ score: r.score,
136
+ });
137
+ }
138
+ }
139
+ runs.push(ranked);
140
+ perRepo.push({ repo: repo.name, count: results.length });
141
+ }
142
+ catch (err) {
143
+ const msg = err instanceof Error ? err.message : String(err);
144
+ perRepo.push({ repo: repo.name, count: 0, error: "query_failed" });
145
+ warnings.push(`${repo.name}: BM25 query failed (${msg}).`);
146
+ }
147
+ finally {
148
+ await ctx.pool.release(repoPath);
149
+ }
150
+ }
151
+ const fused = rrf(runs, DEFAULT_RRF_K, Math.max(limit, DEFAULT_RRF_TOP_K));
152
+ const rows = [];
153
+ for (const f of fused) {
154
+ const row = meta.get(f.id);
155
+ if (!row)
156
+ continue;
157
+ rows.push({ ...row, _rrf_score: f.score });
158
+ }
159
+ // Deterministic final ordering: _rrf_score desc, then (_repo, nodeId) lex asc.
160
+ rows.sort((a, b) => {
161
+ if (a._rrf_score !== b._rrf_score)
162
+ return b._rrf_score - a._rrf_score;
163
+ if (a._repo !== b._repo)
164
+ return a._repo < b._repo ? -1 : 1;
165
+ if (a.nodeId !== b.nodeId)
166
+ return a.nodeId < b.nodeId ? -1 : 1;
167
+ return 0;
168
+ });
169
+ const top = rows.slice(0, limit);
170
+ const reposQueried = perRepo.filter((r) => r.error === undefined).map((r) => r.repo);
171
+ const header = `Top ${top.length} match(es) for "${args.query}" in group ${group.name} (${reposQueried.length} repo${reposQueried.length === 1 ? "" : "s"} queried, ${warnings.length} warning${warnings.length === 1 ? "" : "s"}):`;
172
+ const body = top.length === 0
173
+ ? "(no matches — try a broader phrase)"
174
+ : top
175
+ .map((r, i) => `${i + 1}. [${r._repo}] ${r.name} [${r.kind}] — ${r.filePath} (rrf ${r._rrf_score.toFixed(4)})`)
176
+ .join("\n");
177
+ const warnLines = warnings.length > 0 ? `\n\nWarnings:\n- ${warnings.join("\n- ")}` : "";
178
+ const next = top.length === 0
179
+ ? ["broaden the query or review group composition with `group_status`"]
180
+ : [
181
+ `call \`context\` with symbol="${top[0]?.name ?? ""}" and repo="${top[0]?._repo ?? ""}" to inspect the top hit`,
182
+ `call \`group_status\` with groupName="${group.name}" if results look stale`,
183
+ ];
184
+ return toToolResult(withNextSteps(`${header}\n${body}${warnLines}`, {
185
+ group: group.name,
186
+ query: args.query,
187
+ results: top,
188
+ per_repo: perRepo,
189
+ warnings,
190
+ }, next));
191
+ }
192
+ catch (err) {
193
+ return toToolResult(toolErrorFromUnknown(err));
194
+ }
195
+ }
196
+ export function registerGroupQueryTool(server, ctx) {
197
+ server.registerTool("group_query", {
198
+ title: "Cross-repo search",
199
+ description: "Run BM25 against every repo in a named group and fuse the per-repo rankings with Reciprocal Rank Fusion (RRF, k=60). Useful when a concept spans client and server repos. Repos are visited in alphabetical order so ties are deterministic. Per-repo errors are reported in `per_repo[].error` and `warnings[]` — the fan-out never aborts on a single-repo failure.",
200
+ inputSchema: GroupQueryInput,
201
+ annotations: {
202
+ readOnlyHint: true,
203
+ destructiveHint: false,
204
+ idempotentHint: true,
205
+ openWorldHint: false,
206
+ },
207
+ }, async (args) => fromToolResult(await runGroupQuery(ctx, args)));
208
+ }
209
+ //# sourceMappingURL=group-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group-query.js","sourceRoot":"","sources":["../../src/tools/group-query.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * `group_status` — per-repo index freshness within a named group.
3
+ *
4
+ * For each repo referenced by the group we emit:
5
+ * - name, path
6
+ * - nodeCount / edgeCount from the registry at group-create time
7
+ * - indexedAt from the registry (ISO8601)
8
+ * - staleness envelope via @opencodehub/analysis.computeStaleness when
9
+ * the on-disk meta.json is readable
10
+ * - `inRegistry: false` for orphan references (repo was removed but the
11
+ * group still points at it)
12
+ */
13
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
14
+ import { type ToolContext, type ToolResult } from "./shared.js";
15
+ interface GroupStatusArgs {
16
+ readonly groupName: string;
17
+ }
18
+ export declare function runGroupStatus(ctx: ToolContext, args: GroupStatusArgs): Promise<ToolResult>;
19
+ export declare function registerGroupStatusTool(server: McpServer, ctx: ToolContext): void;
20
+ export {};
21
+ //# sourceMappingURL=group-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group-status.d.ts","sourceRoot":"","sources":["../../src/tools/group-status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAUzE,OAAO,EAAkB,KAAK,WAAW,EAAE,KAAK,UAAU,EAAgB,MAAM,aAAa,CAAC;AAwB9F,UAAU,eAAe;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAgGjG;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAiBjF"}