@reposkein/mcp 0.1.0 → 0.1.2

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 (56) hide show
  1. package/README.md +135 -0
  2. package/dist/SKILL.md +31 -0
  3. package/dist/cli/doctor.d.ts +19 -0
  4. package/dist/cli/doctor.js +92 -0
  5. package/dist/cli/doctor.js.map +1 -0
  6. package/dist/embed/cache.d.ts +51 -0
  7. package/dist/embed/cache.js +163 -0
  8. package/dist/embed/cache.js.map +1 -0
  9. package/dist/embed/hybrid.d.ts +43 -0
  10. package/dist/embed/hybrid.js +106 -0
  11. package/dist/embed/hybrid.js.map +1 -0
  12. package/dist/embed/provider.d.ts +34 -0
  13. package/dist/embed/provider.js +32 -0
  14. package/dist/embed/provider.js.map +1 -0
  15. package/dist/embed/providers/http.d.ts +35 -0
  16. package/dist/embed/providers/http.js +93 -0
  17. package/dist/embed/providers/http.js.map +1 -0
  18. package/dist/embed/providers/voyage.d.ts +32 -0
  19. package/dist/embed/providers/voyage.js +93 -0
  20. package/dist/embed/providers/voyage.js.map +1 -0
  21. package/dist/index.js +61 -9
  22. package/dist/index.js.map +1 -1
  23. package/dist/profile/impact.d.ts +43 -0
  24. package/dist/profile/impact.js +112 -0
  25. package/dist/profile/impact.js.map +1 -0
  26. package/dist/search/bm25f.d.ts +31 -0
  27. package/dist/search/bm25f.js +151 -0
  28. package/dist/search/bm25f.js.map +1 -0
  29. package/dist/store/GraphStore.d.ts +14 -0
  30. package/dist/store/GraphStore.js.map +1 -1
  31. package/dist/store/JsonlGraphStore.d.ts +2 -1
  32. package/dist/store/JsonlGraphStore.js +25 -0
  33. package/dist/store/JsonlGraphStore.js.map +1 -1
  34. package/dist/store/Neo4jGraphStore.d.ts +2 -1
  35. package/dist/store/Neo4jGraphStore.js +18 -0
  36. package/dist/store/Neo4jGraphStore.js.map +1 -1
  37. package/dist/store/UnconfiguredStore.d.ts +2 -1
  38. package/dist/store/UnconfiguredStore.js +3 -0
  39. package/dist/store/UnconfiguredStore.js.map +1 -1
  40. package/dist/temporal/gitlog.d.ts +65 -0
  41. package/dist/temporal/gitlog.js +254 -0
  42. package/dist/temporal/gitlog.js.map +1 -0
  43. package/dist/temporal/temporal.d.ts +22 -0
  44. package/dist/temporal/temporal.js +185 -0
  45. package/dist/temporal/temporal.js.map +1 -0
  46. package/dist/tools/impact.d.ts +11 -0
  47. package/dist/tools/impact.js +63 -0
  48. package/dist/tools/impact.js.map +1 -0
  49. package/dist/tools/semanticFind.d.ts +37 -0
  50. package/dist/tools/semanticFind.js +155 -0
  51. package/dist/tools/semanticFind.js.map +1 -0
  52. package/dist/tools/temporalContext.d.ts +12 -0
  53. package/dist/tools/temporalContext.js +55 -0
  54. package/dist/tools/temporalContext.js.map +1 -0
  55. package/package.json +1 -1
  56. package/scripts/bundle-skill.mjs +1 -1
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Hybrid retrieval primitives for semantic_find.
3
+ *
4
+ * cosineRank: brute-force cosine similarity over cached vectors (in-process, no vector DB).
5
+ * rrf: Reciprocal Rank Fusion (k=60) over lexical + cosine ranked lists.
6
+ *
7
+ * Both functions are pure + deterministic given identical inputs. Ties are broken by
8
+ * ascending node id (same ordering convention as the rest of the store).
9
+ *
10
+ * Architecture note (design §3):
11
+ * - Lexical BM25F (always first) → lexical ranked list.
12
+ * - Cosine similarity over corpus vectors → cosine ranked list.
13
+ * - RRF fuses both lists → fused order.
14
+ * - On any embedding error → caller uses the lexical list as-is (fallback).
15
+ */
16
+ /**
17
+ * Brute-force cosine similarity between a query vector and all corpus vectors.
18
+ * Returns items with cosine score, sorted descending by score, then ascending by id (ties).
19
+ */
20
+ export function cosineRank(queryVec, corpusVecs) {
21
+ if (queryVec.length === 0 || corpusVecs.size === 0)
22
+ return [];
23
+ const queryNorm = l2norm(queryVec);
24
+ if (queryNorm === 0)
25
+ return [];
26
+ const results = [];
27
+ for (const [id, vec] of corpusVecs) {
28
+ // M1: skip corpus vectors with a different dimensionality than the query vector.
29
+ // A dim mismatch should produce no match (not a plausible-but-wrong similarity).
30
+ if (vec.length !== queryVec.length)
31
+ continue;
32
+ const vecNorm = l2norm(vec);
33
+ if (vecNorm === 0)
34
+ continue;
35
+ const dot = dotProduct(queryVec, vec);
36
+ const cosine = dot / (queryNorm * vecNorm);
37
+ results.push({ id, score: cosine });
38
+ }
39
+ // Sort: descending score, ties broken by ascending id
40
+ results.sort((a, b) => {
41
+ const diff = b.score - a.score;
42
+ if (diff !== 0)
43
+ return diff;
44
+ return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
45
+ });
46
+ return results;
47
+ }
48
+ /**
49
+ * Reciprocal Rank Fusion (RRF) over two ranked lists.
50
+ *
51
+ * fused_score(node) = sum over lists of: weight / (k + rank(node))
52
+ * where rank is 1-based.
53
+ *
54
+ * k=60 (standard RRF default; robust to rank position).
55
+ * Default weights: 1.0 for each list (equal fusion).
56
+ * Scale-free: no score normalization needed (BM25F unbounded + cosine [-1,1]).
57
+ * Ties broken by ascending id.
58
+ *
59
+ * @param lexicalList Items ranked by BM25F (any items, even those not in cosineList).
60
+ * @param cosineList Items ranked by cosine similarity (any items, even those not in lexicalList).
61
+ * @returns Items sorted by fused RRF score, descending. Includes UNION of both lists.
62
+ */
63
+ export function rrf(lexicalList, cosineList, k = 60) {
64
+ const fusedScores = new Map();
65
+ // Add contributions from lexical list (1-based rank)
66
+ for (let i = 0; i < lexicalList.length; i++) {
67
+ const item = lexicalList[i];
68
+ const rank = i + 1;
69
+ fusedScores.set(item.id, (fusedScores.get(item.id) ?? 0) + 1.0 / (k + rank));
70
+ }
71
+ // Add contributions from cosine list (1-based rank)
72
+ for (let i = 0; i < cosineList.length; i++) {
73
+ const item = cosineList[i];
74
+ const rank = i + 1;
75
+ fusedScores.set(item.id, (fusedScores.get(item.id) ?? 0) + 1.0 / (k + rank));
76
+ }
77
+ // Build result array and sort: descending fused score, ties by ascending id
78
+ const result = [];
79
+ for (const [id, score] of fusedScores) {
80
+ result.push({ id, score });
81
+ }
82
+ result.sort((a, b) => {
83
+ const diff = b.score - a.score;
84
+ if (diff !== 0)
85
+ return diff;
86
+ return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
87
+ });
88
+ return result;
89
+ }
90
+ // ——— Vector math helpers ———
91
+ function dotProduct(a, b) {
92
+ // Callers (cosineRank) must have already verified a.length === b.length.
93
+ const len = a.length; // both lengths are equal at this point
94
+ let sum = 0;
95
+ for (let i = 0; i < len; i++) {
96
+ sum += (a[i] ?? 0) * (b[i] ?? 0);
97
+ }
98
+ return sum;
99
+ }
100
+ function l2norm(v) {
101
+ let sum = 0;
102
+ for (const x of v)
103
+ sum += x * x;
104
+ return Math.sqrt(sum);
105
+ }
106
+ //# sourceMappingURL=hybrid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hybrid.js","sourceRoot":"","sources":["../../src/embed/hybrid.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Pluggable EmbeddingProvider interface for semantic_find's optional embedding tier.
3
+ *
4
+ * Default (REPOSKEIN_EMBED_PROVIDER unset or "none") → providerFromEnv returns null
5
+ * → semantic_find runs pure-lexical BM25F (byte-identical to baseline).
6
+ *
7
+ * Config env vars:
8
+ * REPOSKEIN_EMBED_PROVIDER none | voyage | http (default: none)
9
+ * REPOSKEIN_EMBED_MODEL model id (provider default when absent)
10
+ * REPOSKEIN_EMBED_DIMS output dimension (model default when absent)
11
+ * VOYAGE_API_KEY API key for voyage provider
12
+ * REPOSKEIN_EMBED_URL base URL for http provider
13
+ */
14
+ export type EmbedKind = "document" | "query";
15
+ export interface EmbeddingProvider {
16
+ /** Stable provider id, e.g. "voyage". Forms part of the cache key. */
17
+ id(): string;
18
+ /** Stable model id, e.g. "voyage-code-3". Part of the cache key. */
19
+ modelId(): string;
20
+ /** Output vector dimensionality, e.g. 1024. Part of the cache key. */
21
+ dims(): number;
22
+ /**
23
+ * Embed a batch of texts.
24
+ * kind maps to Voyage's input_type ("document" vs "query") — Voyage uses
25
+ * asymmetric embeddings so this materially changes the vectors.
26
+ * Returns one number[] per input, in order.
27
+ */
28
+ embed(texts: string[], kind: EmbedKind): Promise<number[][]>;
29
+ }
30
+ /**
31
+ * Build a provider from env vars, or null if embeddings are disabled (default).
32
+ * Returns null when REPOSKEIN_EMBED_PROVIDER is unset or "none".
33
+ */
34
+ export declare function providerFromEnv(env?: NodeJS.ProcessEnv): Promise<EmbeddingProvider | null>;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Pluggable EmbeddingProvider interface for semantic_find's optional embedding tier.
3
+ *
4
+ * Default (REPOSKEIN_EMBED_PROVIDER unset or "none") → providerFromEnv returns null
5
+ * → semantic_find runs pure-lexical BM25F (byte-identical to baseline).
6
+ *
7
+ * Config env vars:
8
+ * REPOSKEIN_EMBED_PROVIDER none | voyage | http (default: none)
9
+ * REPOSKEIN_EMBED_MODEL model id (provider default when absent)
10
+ * REPOSKEIN_EMBED_DIMS output dimension (model default when absent)
11
+ * VOYAGE_API_KEY API key for voyage provider
12
+ * REPOSKEIN_EMBED_URL base URL for http provider
13
+ */
14
+ /**
15
+ * Build a provider from env vars, or null if embeddings are disabled (default).
16
+ * Returns null when REPOSKEIN_EMBED_PROVIDER is unset or "none".
17
+ */
18
+ export async function providerFromEnv(env = process.env) {
19
+ const providerName = (env["REPOSKEIN_EMBED_PROVIDER"] ?? "none").toLowerCase();
20
+ if (providerName === "none" || providerName === "")
21
+ return null;
22
+ if (providerName === "voyage") {
23
+ const { VoyageEmbeddingProvider } = await import("./providers/voyage.js");
24
+ return new VoyageEmbeddingProvider(env);
25
+ }
26
+ if (providerName === "http") {
27
+ const { HttpEmbeddingProvider } = await import("./providers/http.js");
28
+ return new HttpEmbeddingProvider(env);
29
+ }
30
+ throw new Error(`Unknown REPOSKEIN_EMBED_PROVIDER: "${providerName}". Supported: none, voyage, http`);
31
+ }
32
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/embed/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAoBH;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAyB,OAAO,CAAC,GAAG;IACxE,MAAM,YAAY,GAAG,CAAC,GAAG,CAAC,0BAA0B,CAAC,IAAI,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;IAC/E,IAAI,YAAY,KAAK,MAAM,IAAI,YAAY,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAEhE,IAAI,YAAY,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,EAAE,uBAAuB,EAAE,GAAG,MAAM,MAAM,CAAC,uBAAuB,CAAC,CAAC;QAC1E,OAAO,IAAI,uBAAuB,CAAC,GAAG,CAAC,CAAC;IAC1C,CAAC;IAED,IAAI,YAAY,KAAK,MAAM,EAAE,CAAC;QAC5B,MAAM,EAAE,qBAAqB,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;QACtE,OAAO,IAAI,qBAAqB,CAAC,GAAG,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,IAAI,KAAK,CACb,sCAAsC,YAAY,kCAAkC,CACrF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Generic HTTP/local EmbeddingProvider implementation.
3
+ *
4
+ * POSTs to REPOSKEIN_EMBED_URL using an OpenAI-compatible request/response shape.
5
+ * This covers any locally-hosted embedding server, including:
6
+ * - voyage-4-nano (open-weight, HuggingFace: huggingface.co/voyageai/voyage-4-nano)
7
+ * when run via a local HTTP server (e.g. text-embeddings-inference, FastEmbed, etc.)
8
+ * - Any sentence-transformers / HuggingFace model via local server
9
+ * - Ollama embeddings endpoint
10
+ *
11
+ * Request body (OpenAI-compatible):
12
+ * { input: string[], model: string, input_type: "document"|"query" }
13
+ * Response body (OpenAI-compatible):
14
+ * { data: [{ embedding: number[] }, ...] }
15
+ * OR Voyage-style: { embeddings: number[][] }
16
+ * (tries .data[].embedding first, then .embeddings)
17
+ *
18
+ * Config env vars:
19
+ * REPOSKEIN_EMBED_PROVIDER=http
20
+ * REPOSKEIN_EMBED_URL (required; e.g. http://127.0.0.1:8080/v1/embeddings)
21
+ * REPOSKEIN_EMBED_MODEL (required; e.g. "voyage-4-nano")
22
+ * REPOSKEIN_EMBED_DIMS (required; output dimension of the local model)
23
+ */
24
+ import type { EmbeddingProvider, EmbedKind } from "../provider.js";
25
+ export declare class HttpEmbeddingProvider implements EmbeddingProvider {
26
+ private readonly _id;
27
+ private readonly _url;
28
+ private readonly _modelId;
29
+ private readonly _dims;
30
+ constructor(env?: NodeJS.ProcessEnv);
31
+ id(): string;
32
+ modelId(): string;
33
+ dims(): number;
34
+ embed(texts: string[], kind: EmbedKind): Promise<number[][]>;
35
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Generic HTTP/local EmbeddingProvider implementation.
3
+ *
4
+ * POSTs to REPOSKEIN_EMBED_URL using an OpenAI-compatible request/response shape.
5
+ * This covers any locally-hosted embedding server, including:
6
+ * - voyage-4-nano (open-weight, HuggingFace: huggingface.co/voyageai/voyage-4-nano)
7
+ * when run via a local HTTP server (e.g. text-embeddings-inference, FastEmbed, etc.)
8
+ * - Any sentence-transformers / HuggingFace model via local server
9
+ * - Ollama embeddings endpoint
10
+ *
11
+ * Request body (OpenAI-compatible):
12
+ * { input: string[], model: string, input_type: "document"|"query" }
13
+ * Response body (OpenAI-compatible):
14
+ * { data: [{ embedding: number[] }, ...] }
15
+ * OR Voyage-style: { embeddings: number[][] }
16
+ * (tries .data[].embedding first, then .embeddings)
17
+ *
18
+ * Config env vars:
19
+ * REPOSKEIN_EMBED_PROVIDER=http
20
+ * REPOSKEIN_EMBED_URL (required; e.g. http://127.0.0.1:8080/v1/embeddings)
21
+ * REPOSKEIN_EMBED_MODEL (required; e.g. "voyage-4-nano")
22
+ * REPOSKEIN_EMBED_DIMS (required; output dimension of the local model)
23
+ */
24
+ const DEFAULT_DIMS = 1024;
25
+ export class HttpEmbeddingProvider {
26
+ _id = "http";
27
+ _url;
28
+ _modelId;
29
+ _dims;
30
+ constructor(env = process.env) {
31
+ const url = env["REPOSKEIN_EMBED_URL"];
32
+ if (!url) {
33
+ throw new Error("REPOSKEIN_EMBED_URL is required for REPOSKEIN_EMBED_PROVIDER=http");
34
+ }
35
+ const model = env["REPOSKEIN_EMBED_MODEL"];
36
+ if (!model) {
37
+ throw new Error("REPOSKEIN_EMBED_MODEL is required for REPOSKEIN_EMBED_PROVIDER=http");
38
+ }
39
+ this._url = url;
40
+ this._modelId = model;
41
+ const rawDims = env["REPOSKEIN_EMBED_DIMS"];
42
+ if (rawDims !== undefined) {
43
+ const d = Number(rawDims);
44
+ if (!Number.isInteger(d) || d <= 0) {
45
+ throw new Error(`Invalid REPOSKEIN_EMBED_DIMS="${rawDims}" — must be a positive integer (e.g. 256, 512, 1024, 2048)`);
46
+ }
47
+ this._dims = d;
48
+ }
49
+ else {
50
+ this._dims = DEFAULT_DIMS;
51
+ }
52
+ }
53
+ id() { return this._id; }
54
+ modelId() { return this._modelId; }
55
+ dims() { return this._dims; }
56
+ async embed(texts, kind) {
57
+ if (texts.length === 0)
58
+ return [];
59
+ const body = {
60
+ input: texts,
61
+ model: this._modelId,
62
+ input_type: kind,
63
+ };
64
+ const res = await fetch(this._url, {
65
+ method: "POST",
66
+ headers: { "Content-Type": "application/json" },
67
+ body: JSON.stringify(body),
68
+ });
69
+ if (!res.ok) {
70
+ const text = await res.text().catch(() => "(no body)");
71
+ throw new Error(`HTTP embedding server error ${res.status}: ${text}`);
72
+ }
73
+ const json = (await res.json());
74
+ // OpenAI-compatible shape: { data: [{ embedding: [...] }, ...] }
75
+ if ("data" in json && Array.isArray(json.data)) {
76
+ const out = json.data.map((d) => d.embedding);
77
+ if (out.length !== texts.length) {
78
+ throw new Error(`HTTP embedding server returned ${out.length} embeddings for ${texts.length} input texts — count mismatch`);
79
+ }
80
+ return out;
81
+ }
82
+ // Voyage-style shape: { embeddings: [[...], ...] }
83
+ if ("embeddings" in json && Array.isArray(json.embeddings)) {
84
+ const out = json.embeddings;
85
+ if (out.length !== texts.length) {
86
+ throw new Error(`HTTP embedding server returned ${out.length} embeddings for ${texts.length} input texts — count mismatch`);
87
+ }
88
+ return out;
89
+ }
90
+ throw new Error(`Unrecognized embedding server response shape: ${JSON.stringify(json)}`);
91
+ }
92
+ }
93
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/embed/providers/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Voyage AI EmbeddingProvider implementation.
3
+ *
4
+ * Endpoint: POST https://api.voyageai.com/v1/embeddings
5
+ * Response shape: { embeddings: number[][], total_tokens: number }
6
+ * (NOTE: response field is "embeddings" not "data[].embedding" — confirmed from Voyage docs)
7
+ *
8
+ * Models:
9
+ * - voyage-code-3: code-specialized, 1024 dims default (Matryoshka 256/512/1024/2048),
10
+ * 32K token context, 120K tokens/request batch limit.
11
+ * - voyage-4-nano: open-weight on HuggingFace (huggingface.co/voyageai/voyage-4-nano),
12
+ * 1024 dims default, 32K context. Covered by the `http` provider when run locally
13
+ * via any OpenAI-compatible embedding server.
14
+ *
15
+ * Config env vars:
16
+ * REPOSKEIN_EMBED_PROVIDER=voyage
17
+ * REPOSKEIN_EMBED_MODEL (default: voyage-code-3)
18
+ * REPOSKEIN_EMBED_DIMS (optional; 256|512|1024|2048; default: 1024)
19
+ * VOYAGE_API_KEY (required)
20
+ */
21
+ import type { EmbeddingProvider, EmbedKind } from "../provider.js";
22
+ export declare class VoyageEmbeddingProvider implements EmbeddingProvider {
23
+ private readonly _id;
24
+ private readonly _modelId;
25
+ private readonly _dims;
26
+ private readonly _apiKey;
27
+ constructor(env?: NodeJS.ProcessEnv);
28
+ id(): string;
29
+ modelId(): string;
30
+ dims(): number;
31
+ embed(texts: string[], kind: EmbedKind): Promise<number[][]>;
32
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Voyage AI EmbeddingProvider implementation.
3
+ *
4
+ * Endpoint: POST https://api.voyageai.com/v1/embeddings
5
+ * Response shape: { embeddings: number[][], total_tokens: number }
6
+ * (NOTE: response field is "embeddings" not "data[].embedding" — confirmed from Voyage docs)
7
+ *
8
+ * Models:
9
+ * - voyage-code-3: code-specialized, 1024 dims default (Matryoshka 256/512/1024/2048),
10
+ * 32K token context, 120K tokens/request batch limit.
11
+ * - voyage-4-nano: open-weight on HuggingFace (huggingface.co/voyageai/voyage-4-nano),
12
+ * 1024 dims default, 32K context. Covered by the `http` provider when run locally
13
+ * via any OpenAI-compatible embedding server.
14
+ *
15
+ * Config env vars:
16
+ * REPOSKEIN_EMBED_PROVIDER=voyage
17
+ * REPOSKEIN_EMBED_MODEL (default: voyage-code-3)
18
+ * REPOSKEIN_EMBED_DIMS (optional; 256|512|1024|2048; default: 1024)
19
+ * VOYAGE_API_KEY (required)
20
+ */
21
+ const VOYAGE_ENDPOINT = "https://api.voyageai.com/v1/embeddings";
22
+ const DEFAULT_MODEL = "voyage-code-3";
23
+ const DEFAULT_DIMS = 1024;
24
+ /** Max texts per request (Voyage API limit). */
25
+ const BATCH_SIZE = 1000;
26
+ export class VoyageEmbeddingProvider {
27
+ _id = "voyage";
28
+ _modelId;
29
+ _dims;
30
+ _apiKey;
31
+ constructor(env = process.env) {
32
+ const apiKey = env["VOYAGE_API_KEY"];
33
+ if (!apiKey) {
34
+ throw new Error("VOYAGE_API_KEY is required for REPOSKEIN_EMBED_PROVIDER=voyage");
35
+ }
36
+ this._apiKey = apiKey;
37
+ this._modelId = env["REPOSKEIN_EMBED_MODEL"] ?? DEFAULT_MODEL;
38
+ const rawDims = env["REPOSKEIN_EMBED_DIMS"];
39
+ if (rawDims !== undefined) {
40
+ const d = Number(rawDims);
41
+ if (!Number.isInteger(d) || d <= 0) {
42
+ throw new Error(`Invalid REPOSKEIN_EMBED_DIMS="${rawDims}" — must be a positive integer (e.g. 256, 512, 1024, 2048)`);
43
+ }
44
+ this._dims = d;
45
+ }
46
+ else {
47
+ this._dims = DEFAULT_DIMS;
48
+ }
49
+ }
50
+ id() { return this._id; }
51
+ modelId() { return this._modelId; }
52
+ dims() { return this._dims; }
53
+ async embed(texts, kind) {
54
+ if (texts.length === 0)
55
+ return [];
56
+ const results = [];
57
+ // Batch up to BATCH_SIZE texts per request
58
+ for (let i = 0; i < texts.length; i += BATCH_SIZE) {
59
+ const batch = texts.slice(i, i + BATCH_SIZE);
60
+ const body = {
61
+ input: batch,
62
+ model: this._modelId,
63
+ input_type: kind,
64
+ output_dimension: this._dims,
65
+ };
66
+ const res = await fetch(VOYAGE_ENDPOINT, {
67
+ method: "POST",
68
+ headers: {
69
+ "Content-Type": "application/json",
70
+ "Authorization": `Bearer ${this._apiKey}`,
71
+ },
72
+ body: JSON.stringify(body),
73
+ });
74
+ if (!res.ok) {
75
+ const text = await res.text().catch(() => "(no body)");
76
+ throw new Error(`Voyage API error ${res.status}: ${text}`);
77
+ }
78
+ const json = (await res.json());
79
+ if (!Array.isArray(json.embeddings)) {
80
+ throw new Error(`Voyage API response missing embeddings array: ${JSON.stringify(json)}`);
81
+ }
82
+ if (json.embeddings.length !== batch.length) {
83
+ throw new Error(`Voyage API returned ${json.embeddings.length} embeddings for batch of ${batch.length} texts — batch count mismatch`);
84
+ }
85
+ results.push(...json.embeddings);
86
+ }
87
+ if (results.length !== texts.length) {
88
+ throw new Error(`Voyage API returned ${results.length} total embeddings for ${texts.length} input texts — total count mismatch`);
89
+ }
90
+ return results;
91
+ }
92
+ }
93
+ //# sourceMappingURL=voyage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voyage.js","sourceRoot":"","sources":["../../../src/embed/providers/voyage.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { pathToFileURL } from "node:url";
2
2
  import { runInit } from "./cli/init.js";
3
+ import { runDoctor } from "./cli/doctor.js";
3
4
  import { existsSync } from "node:fs";
4
5
  import { join } from "node:path";
5
6
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -12,6 +13,9 @@ import { makeReadCypher } from "./tools/readCypher.js";
12
13
  import { makeGetContextProfile } from "./tools/getContextProfile.js";
13
14
  import { makeWriteSemanticSummary } from "./tools/writeSemanticSummary.js";
14
15
  import { makeInitCpgSkeleton, makeReindexFile } from "./tools/indexerTools.js";
16
+ import { makeSemanticFind } from "./tools/semanticFind.js";
17
+ import { makeTemporalContext } from "./tools/temporalContext.js";
18
+ import { makeImpact } from "./tools/impact.js";
15
19
  import { resolveRepoId } from "./store/repoId.js";
16
20
  /** Selects the store backend.
17
21
  * REPOSKEIN_STORE = "jsonl" | "neo4j" | "auto" (default "auto").
@@ -117,6 +121,51 @@ export async function main() {
117
121
  }
118
122
  return reindexFile(args);
119
123
  });
124
+ const semanticFind = repoId ? makeSemanticFind(store, repoId, repoPath ?? ".") : null;
125
+ server.registerTool("semantic_find", {
126
+ title: "Find code by meaning",
127
+ description: "Rank functions/classes by a lexical match over their qualified names, signatures, and agent-written summaries — the entry point to seed get_context_profile when you don't know where to start. Returns ranked node_ids. federated:true spans nested repos.",
128
+ inputSchema: {
129
+ query: z.string(),
130
+ limit: z.number().int().min(1).max(25).optional(),
131
+ kind: z.enum(["Function", "Class", "Interface", "Enum"]).optional(),
132
+ federated: z.boolean().optional(),
133
+ },
134
+ }, async (args) => {
135
+ if (!semanticFind) {
136
+ return { content: [{ type: "text", text: REPO_REQUIRED_MSG }], isError: true };
137
+ }
138
+ return semanticFind(args);
139
+ });
140
+ // get_temporal_context is gated on repoPath (not the store — it reads from .git directly).
141
+ const temporalContext = repoPath ? makeTemporalContext(repoPath) : null;
142
+ server.registerTool("get_temporal_context", {
143
+ title: "Git temporal context",
144
+ description: "Git-derived signals for a file: how often/recently it changes, who owns it, and which files most often change together with it (co-change) — answers \"what else should I touch?\". Advisory (derived from git history, not the committed graph). Before a cross-cutting change, use this to find files that historically change together.",
145
+ inputSchema: { path: z.string() },
146
+ }, async (args) => {
147
+ if (!temporalContext) {
148
+ return { content: [{ type: "text", text: REPO_REQUIRED_MSG }], isError: true };
149
+ }
150
+ return temporalContext(args);
151
+ });
152
+ const impact = repoId ? makeImpact(store, repoId) : null;
153
+ server.registerTool("impact", {
154
+ title: "Change impact + covering tests",
155
+ description: "Given a function/class, return its transitive callers (what could break if you change it) split into impacted code vs the tests that cover it (what to run). Resolves by node_id, file_path+name, or name. federated:true spans nested repos.",
156
+ inputSchema: {
157
+ node_id: z.string().optional(),
158
+ file_path: z.string().optional(),
159
+ name: z.string().optional(),
160
+ depth: z.number().int().min(1).max(5).optional(),
161
+ federated: z.boolean().optional(),
162
+ },
163
+ }, async (args) => {
164
+ if (!impact) {
165
+ return { content: [{ type: "text", text: REPO_REQUIRED_MSG }], isError: true };
166
+ }
167
+ return impact(args);
168
+ });
120
169
  if (!repoId) {
121
170
  console.error("[reposkein-mcp] REPOSKEIN_REPO_PATH not set; repo-scoped tools will return an error until it is configured");
122
171
  }
@@ -125,19 +174,22 @@ export async function main() {
125
174
  }
126
175
  // Run when invoked as the binary (not when imported by tests).
127
176
  if (import.meta.url === pathToFileURL(process.argv[1]).href) {
128
- if (process.argv[2] === "init") {
177
+ const sub = process.argv[2];
178
+ if (sub === "init") {
129
179
  runInit(process.argv[3] ?? ".")
130
180
  .then((code) => process.exit(code))
131
- .catch((err) => {
132
- console.error(err);
133
- process.exit(1);
134
- });
181
+ .catch((err) => { console.error(err); process.exit(1); });
182
+ }
183
+ else if (sub === "doctor") {
184
+ const rest = process.argv.slice(3);
185
+ const json = rest.includes("--json");
186
+ const path = rest.find((a) => !a.startsWith("-")) ?? process.env.REPOSKEIN_REPO_PATH ?? ".";
187
+ runDoctor(path, json)
188
+ .then((code) => process.exit(code))
189
+ .catch((err) => { console.error(err); process.exit(1); });
135
190
  }
136
191
  else {
137
- main().catch((err) => {
138
- console.error(err);
139
- process.exit(1);
140
- });
192
+ main().catch((err) => { console.error(err); process.exit(1); });
141
193
  }
142
194
  }
143
195
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import type { GraphStore } from "../store/GraphStore.js";
2
+ /**
3
+ * Mirrors the Rust indexer's `role_for` test classification (classify.rs).
4
+ * A file is a test file if:
5
+ * - any lowercased path segment equals "test" or "tests", or ends with ".tests"
6
+ * - the lowercased basename starts with "test_"
7
+ * - the basename contains "_test.", ".test.", ".spec.", or ".tests."
8
+ * - the original (case-preserving) basename ends with "Test.<ext>", "Tests.<ext>"
9
+ *
10
+ * "contest_results.py" → false (basename "contest_results.py" doesn't start with "test_").
11
+ */
12
+ export declare function isTestPath(filePath: string): boolean;
13
+ export interface ImpactRow {
14
+ node_id: string;
15
+ qualified_name: string;
16
+ file_path: string;
17
+ depth: number;
18
+ is_test: boolean;
19
+ }
20
+ export interface ImpactResult {
21
+ target: {
22
+ node_id: string;
23
+ qualified_name: string;
24
+ file_path: string;
25
+ };
26
+ depth: number;
27
+ impacted: ImpactRow[];
28
+ covering_tests: ImpactRow[];
29
+ counts: {
30
+ impacted: number;
31
+ covering_tests: number;
32
+ truncated: boolean;
33
+ };
34
+ }
35
+ /**
36
+ * BFS over callers to bounded depth with a maxNodes cap.
37
+ * NeighborRow does not carry file_path, so we fetch it via store.getNode().
38
+ * Deterministic ordering: depth asc, then node_id asc.
39
+ */
40
+ export declare function computeImpact(store: GraphStore, repoIds: string[], targetId: string, targetQN: string, targetFile: string, opts: {
41
+ depth: number;
42
+ maxNodes: number;
43
+ }): Promise<ImpactResult>;