@dotdotgod/opencode 0.6.6 → 0.6.7
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/mcp/cli.mjs +270 -126
- package/mcp/server.mjs +1 -1
- package/package.json +2 -2
package/mcp/cli.mjs
CHANGED
|
@@ -3937,12 +3937,196 @@ function runConfig(argv) {
|
|
|
3937
3937
|
// packages/cli/src/commands/query.mjs
|
|
3938
3938
|
import { resolve as resolve11 } from "node:path";
|
|
3939
3939
|
|
|
3940
|
+
// packages/cli/src/query/cache-session.mjs
|
|
3941
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
3942
|
+
import { mkdirSync as mkdirSync6, openSync, closeSync, readFileSync as readFileSync16, writeFileSync as writeFileSync7, renameSync as renameSync3, rmSync as rmSync2 } from "node:fs";
|
|
3943
|
+
import { join as join16 } from "node:path";
|
|
3944
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
3945
|
+
|
|
3946
|
+
// packages/cli/src/query/store.mjs
|
|
3947
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
3948
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync15, renameSync as renameSync2, rmSync, writeFileSync as writeFileSync6 } from "node:fs";
|
|
3949
|
+
import { join as join15 } from "node:path";
|
|
3950
|
+
var VECTOR_SCHEMA_VERSION = 2;
|
|
3951
|
+
var VECTOR_DIMENSIONS = 384;
|
|
3952
|
+
function profileFingerprint(identity2) {
|
|
3953
|
+
return createHash6("sha256").update(JSON.stringify(identity2)).digest("hex");
|
|
3954
|
+
}
|
|
3955
|
+
function isValidNormalizedVectorValues(values, dimensions = VECTOR_DIMENSIONS) {
|
|
3956
|
+
if (!Number.isInteger(dimensions) || dimensions < 1 || !values || values.length % dimensions !== 0) return false;
|
|
3957
|
+
for (let offset = 0; offset < values.length; offset += dimensions) {
|
|
3958
|
+
let squaredNorm = 0;
|
|
3959
|
+
for (let index = 0; index < dimensions; index += 1) {
|
|
3960
|
+
const value = values[offset + index];
|
|
3961
|
+
if (!Number.isFinite(value) || value < -1 || value > 1) return false;
|
|
3962
|
+
squaredNorm += value * value;
|
|
3963
|
+
}
|
|
3964
|
+
if (Math.abs(Math.sqrt(squaredNorm) - 1) > 0.01) return false;
|
|
3965
|
+
}
|
|
3966
|
+
return true;
|
|
3967
|
+
}
|
|
3968
|
+
function vectorCachePaths(root) {
|
|
3969
|
+
const directory = join15(root, ".dotdotgod", "vectors");
|
|
3970
|
+
return { directory, manifest: join15(directory, "manifest.json"), chunks: join15(directory, "chunks.jsonl"), embeddings: join15(directory, "embeddings.f32") };
|
|
3971
|
+
}
|
|
3972
|
+
function readVectorCache(root, identity2 = null, diagnostics = {}) {
|
|
3973
|
+
diagnostics.reason = "missing";
|
|
3974
|
+
const paths = vectorCachePaths(root);
|
|
3975
|
+
if (!existsSync16(paths.manifest) || !existsSync16(paths.chunks) || !existsSync16(paths.embeddings)) return null;
|
|
3976
|
+
try {
|
|
3977
|
+
const manifest = JSON.parse(readFileSync15(paths.manifest, "utf8"));
|
|
3978
|
+
diagnostics.reason = "schema-mismatch";
|
|
3979
|
+
if (manifest.schemaVersion !== VECTOR_SCHEMA_VERSION || !Number.isInteger(manifest.dimensions) || manifest.dimensions < 1) return null;
|
|
3980
|
+
diagnostics.reason = "profile-mismatch";
|
|
3981
|
+
if (identity2 && manifest.profileFingerprint !== profileFingerprint(identity2)) return null;
|
|
3982
|
+
diagnostics.reason = "corrupt";
|
|
3983
|
+
const chunks = readFileSync15(paths.chunks, "utf8").split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
3984
|
+
const buffer = readFileSync15(paths.embeddings);
|
|
3985
|
+
if (buffer.byteLength !== chunks.length * manifest.dimensions * 4) return null;
|
|
3986
|
+
const vectors = new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 4);
|
|
3987
|
+
if (!isValidNormalizedVectorValues(vectors, manifest.dimensions)) return null;
|
|
3988
|
+
diagnostics.reason = "valid";
|
|
3989
|
+
return { manifest, chunks, vectors: new Float32Array(vectors) };
|
|
3990
|
+
} catch {
|
|
3991
|
+
diagnostics.reason = "corrupt";
|
|
3992
|
+
return null;
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
function atomicWrite(path, data) {
|
|
3996
|
+
const temporary = `${path}.tmp-${process.pid}`;
|
|
3997
|
+
writeFileSync6(temporary, data);
|
|
3998
|
+
renameSync2(temporary, path);
|
|
3999
|
+
}
|
|
4000
|
+
function writeVectorCache(root, chunks, vectors, metadata = {}) {
|
|
4001
|
+
const paths = vectorCachePaths(root);
|
|
4002
|
+
const dimensions = metadata.dimensions;
|
|
4003
|
+
if (!Number.isInteger(dimensions) || dimensions < 1) throw new Error("Vector dimensions must be a positive integer.");
|
|
4004
|
+
mkdirSync5(paths.directory, { recursive: true });
|
|
4005
|
+
if (vectors.length !== chunks.length * dimensions) throw new Error("Vector count does not match chunk metadata.");
|
|
4006
|
+
if (!isValidNormalizedVectorValues(vectors, dimensions)) throw new Error("Vectors must contain finite normalized values.");
|
|
4007
|
+
const manifest = { schemaVersion: VECTOR_SCHEMA_VERSION, chunks: chunks.length, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), ...metadata };
|
|
4008
|
+
atomicWrite(paths.chunks, `${chunks.map((chunk) => JSON.stringify(chunk)).join("\n")}
|
|
4009
|
+
`);
|
|
4010
|
+
atomicWrite(paths.embeddings, Buffer.from(vectors.buffer, vectors.byteOffset, vectors.byteLength));
|
|
4011
|
+
atomicWrite(paths.manifest, `${JSON.stringify(manifest, null, 2)}
|
|
4012
|
+
`);
|
|
4013
|
+
return manifest;
|
|
4014
|
+
}
|
|
4015
|
+
|
|
4016
|
+
// packages/cli/src/query/cache-session.mjs
|
|
4017
|
+
async function vectorCacheSession(root, identity2, action2) {
|
|
4018
|
+
const directory = join16(root, ".dotdotgod", "vectors");
|
|
4019
|
+
mkdirSync6(directory, { recursive: true });
|
|
4020
|
+
const lock = join16(directory, "build.lock");
|
|
4021
|
+
const token = randomUUID2();
|
|
4022
|
+
const started = Date.now();
|
|
4023
|
+
let waited = false;
|
|
4024
|
+
while (true) {
|
|
4025
|
+
try {
|
|
4026
|
+
const fd = openSync(lock, "wx");
|
|
4027
|
+
try {
|
|
4028
|
+
writeFileSync7(fd, JSON.stringify({ pid: process.pid, token }));
|
|
4029
|
+
} finally {
|
|
4030
|
+
closeSync(fd);
|
|
4031
|
+
}
|
|
4032
|
+
break;
|
|
4033
|
+
} catch (error) {
|
|
4034
|
+
if (error.code !== "EEXIST") throw error;
|
|
4035
|
+
waited = true;
|
|
4036
|
+
if (Date.now() - started >= 3e4) throw new Error("Vector cache lock timed out; another build may be running or the lock needs inspection.");
|
|
4037
|
+
try {
|
|
4038
|
+
const owner = JSON.parse(readFileSync16(lock, "utf8"));
|
|
4039
|
+
if (!Number.isInteger(owner.pid) || owner.pid < 1) throw new Error("Invalid lock owner.");
|
|
4040
|
+
try {
|
|
4041
|
+
process.kill(owner.pid, 0);
|
|
4042
|
+
} catch (probe) {
|
|
4043
|
+
if (probe.code !== "ESRCH") throw probe;
|
|
4044
|
+
const recovery = `${lock}.recovery`;
|
|
4045
|
+
try {
|
|
4046
|
+
mkdirSync6(recovery);
|
|
4047
|
+
} catch (claim) {
|
|
4048
|
+
if (claim.code === "EEXIST") {
|
|
4049
|
+
await sleep(50);
|
|
4050
|
+
continue;
|
|
4051
|
+
}
|
|
4052
|
+
throw claim;
|
|
4053
|
+
}
|
|
4054
|
+
try {
|
|
4055
|
+
const current = JSON.parse(readFileSync16(lock, "utf8"));
|
|
4056
|
+
if (current.token === owner.token && current.pid === owner.pid) rmSync2(lock);
|
|
4057
|
+
} finally {
|
|
4058
|
+
rmSync2(recovery, { recursive: true, force: true });
|
|
4059
|
+
}
|
|
4060
|
+
continue;
|
|
4061
|
+
}
|
|
4062
|
+
} catch (probe) {
|
|
4063
|
+
if (probe.code === "ENOENT") continue;
|
|
4064
|
+
}
|
|
4065
|
+
await sleep(50);
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
try {
|
|
4069
|
+
const path = join16(directory, "snapshots.json");
|
|
4070
|
+
const fingerprint2 = profileFingerprint(identity2);
|
|
4071
|
+
let state;
|
|
4072
|
+
let reason = "missing";
|
|
4073
|
+
try {
|
|
4074
|
+
state = JSON.parse(readFileSync16(path, "utf8"));
|
|
4075
|
+
if (state.schema !== 1) {
|
|
4076
|
+
reason = "schema-mismatch";
|
|
4077
|
+
state = null;
|
|
4078
|
+
} else if (state.profile !== fingerprint2) {
|
|
4079
|
+
reason = "profile-mismatch";
|
|
4080
|
+
state = null;
|
|
4081
|
+
} else {
|
|
4082
|
+
if (state.dimensions !== null && (!Number.isInteger(state.dimensions) || state.dimensions < 1)) throw new Error("Invalid cache dimensions.");
|
|
4083
|
+
for (const rows of [state.docs, state.code, state.pending]) {
|
|
4084
|
+
if (!Array.isArray(rows) || rows.some((row) => typeof row.chunk?.fingerprint !== "string" || !Array.isArray(row.vector) || row.vector.length !== state.dimensions || !isValidNormalizedVectorValues(row.vector, state.dimensions))) throw new Error("Invalid cache rows.");
|
|
4085
|
+
}
|
|
4086
|
+
reason = "valid";
|
|
4087
|
+
}
|
|
4088
|
+
} catch (error) {
|
|
4089
|
+
state = null;
|
|
4090
|
+
reason = error.code === "ENOENT" ? "missing" : "corrupt";
|
|
4091
|
+
}
|
|
4092
|
+
if (!state) {
|
|
4093
|
+
state = { schema: 1, profile: fingerprint2, dimensions: null, docs: [], code: [], pending: [] };
|
|
4094
|
+
if (reason === "missing") {
|
|
4095
|
+
const legacyDiagnostics = {};
|
|
4096
|
+
const legacy = readVectorCache(root, identity2, legacyDiagnostics);
|
|
4097
|
+
reason = legacyDiagnostics.reason;
|
|
4098
|
+
if (legacy) {
|
|
4099
|
+
state.dimensions = legacy.manifest.dimensions;
|
|
4100
|
+
legacy.chunks.forEach((chunk, i) => state[chunk.kind === "symbol" ? "code" : "docs"].push({ chunk, vector: Array.from(legacy.vectors.subarray(i * state.dimensions, (i + 1) * state.dimensions)) }));
|
|
4101
|
+
reason = "legacy-migrated";
|
|
4102
|
+
}
|
|
4103
|
+
}
|
|
4104
|
+
}
|
|
4105
|
+
const save = () => {
|
|
4106
|
+
const temporary = `${path}.tmp-${token}`;
|
|
4107
|
+
try {
|
|
4108
|
+
writeFileSync7(temporary, JSON.stringify(state));
|
|
4109
|
+
renameSync3(temporary, path);
|
|
4110
|
+
} finally {
|
|
4111
|
+
rmSync2(temporary, { force: true });
|
|
4112
|
+
}
|
|
4113
|
+
};
|
|
4114
|
+
return await action2(state, save, { reason, lockWaitMs: waited ? Date.now() - started : 0 });
|
|
4115
|
+
} finally {
|
|
4116
|
+
try {
|
|
4117
|
+
if (JSON.parse(readFileSync16(lock, "utf8")).token === token) rmSync2(lock);
|
|
4118
|
+
} catch (error) {
|
|
4119
|
+
if (error.code !== "ENOENT") throw error;
|
|
4120
|
+
}
|
|
4121
|
+
}
|
|
4122
|
+
}
|
|
4123
|
+
|
|
3940
4124
|
// packages/cli/src/query/embedding-runtime.mjs
|
|
3941
|
-
import { existsSync as
|
|
4125
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync7, readFileSync as readFileSync17, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "node:fs";
|
|
3942
4126
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
3943
4127
|
import { createRequire as createRequire2 } from "node:module";
|
|
3944
4128
|
import { homedir as homedir3 } from "node:os";
|
|
3945
|
-
import { join as
|
|
4129
|
+
import { join as join17 } from "node:path";
|
|
3946
4130
|
import { pathToFileURL } from "node:url";
|
|
3947
4131
|
var EMBEDDING_RUNTIME_PACKAGE = "@huggingface/transformers";
|
|
3948
4132
|
var EMBEDDING_RUNTIME_VERSION = "4.2.0";
|
|
@@ -3954,12 +4138,12 @@ var EmbeddingRuntimeMissingError = class extends Error {
|
|
|
3954
4138
|
}
|
|
3955
4139
|
};
|
|
3956
4140
|
function embeddingRuntimeRoot(options = {}) {
|
|
3957
|
-
return
|
|
4141
|
+
return join17(options.home ?? homedir3(), ".dotdotgod", "runtime", "embedding");
|
|
3958
4142
|
}
|
|
3959
4143
|
function resolvePersistentTransformers(options = {}) {
|
|
3960
4144
|
const root = embeddingRuntimeRoot(options);
|
|
3961
4145
|
try {
|
|
3962
|
-
const require3 = createRequire2(
|
|
4146
|
+
const require3 = createRequire2(join17(root, "package.json"));
|
|
3963
4147
|
return require3.resolve(EMBEDDING_RUNTIME_PACKAGE);
|
|
3964
4148
|
} catch {
|
|
3965
4149
|
throw new EmbeddingRuntimeMissingError();
|
|
@@ -3968,10 +4152,10 @@ function resolvePersistentTransformers(options = {}) {
|
|
|
3968
4152
|
function embeddingRuntimeStatus(options = {}) {
|
|
3969
4153
|
const root = embeddingRuntimeRoot(options);
|
|
3970
4154
|
try {
|
|
3971
|
-
const require3 = createRequire2(
|
|
4155
|
+
const require3 = createRequire2(join17(root, "package.json"));
|
|
3972
4156
|
require3.resolve(EMBEDDING_RUNTIME_PACKAGE);
|
|
3973
|
-
const packagePath =
|
|
3974
|
-
const version = JSON.parse(
|
|
4157
|
+
const packagePath = join17(root, "node_modules", ...EMBEDDING_RUNTIME_PACKAGE.split("/"), "package.json");
|
|
4158
|
+
const version = JSON.parse(readFileSync17(packagePath, "utf8")).version;
|
|
3975
4159
|
return { ok: true, installed: true, installAvailable: true, package: EMBEDDING_RUNTIME_PACKAGE, requiredVersion: EMBEDDING_RUNTIME_VERSION, packageVersion: version, location: root };
|
|
3976
4160
|
} catch {
|
|
3977
4161
|
return { ok: true, installed: false, installAvailable: true, package: EMBEDDING_RUNTIME_PACKAGE, requiredVersion: EMBEDDING_RUNTIME_VERSION, packageVersion: null, location: root };
|
|
@@ -3980,10 +4164,10 @@ function embeddingRuntimeStatus(options = {}) {
|
|
|
3980
4164
|
function installEmbeddingRuntime(options = {}) {
|
|
3981
4165
|
if (options.confirm !== true) throw new Error("Embedding runtime installation requires explicit confirmation.");
|
|
3982
4166
|
const root = embeddingRuntimeRoot(options);
|
|
3983
|
-
const lock =
|
|
3984
|
-
if (
|
|
3985
|
-
|
|
3986
|
-
|
|
4167
|
+
const lock = join17(root, ".installing");
|
|
4168
|
+
if (existsSync17(lock)) throw new Error("Embedding runtime installation is already in progress.");
|
|
4169
|
+
mkdirSync7(root, { recursive: true });
|
|
4170
|
+
writeFileSync8(lock, `${process.pid}
|
|
3987
4171
|
`, { flag: "wx" });
|
|
3988
4172
|
try {
|
|
3989
4173
|
const spawn = options.spawnImpl ?? spawnSync2;
|
|
@@ -3993,7 +4177,7 @@ function installEmbeddingRuntime(options = {}) {
|
|
|
3993
4177
|
if (!status.installed) throw new Error("Embedding runtime installation could not be verified.");
|
|
3994
4178
|
return { ...status, command: "embedding install", modelDownloadPending: true };
|
|
3995
4179
|
} finally {
|
|
3996
|
-
|
|
4180
|
+
rmSync3(lock, { force: true });
|
|
3997
4181
|
}
|
|
3998
4182
|
}
|
|
3999
4183
|
async function importTransformers(options = {}) {
|
|
@@ -4103,70 +4287,6 @@ async function resolveEmbedder(root, options = {}) {
|
|
|
4103
4287
|
return { ...resolved, identity: embeddingProfileIdentity(resolved.profile), embed: options.embed ? withInputFormat(options.embed, resolved.profile) : await createEmbedder(resolved.profile) };
|
|
4104
4288
|
}
|
|
4105
4289
|
|
|
4106
|
-
// packages/cli/src/query/store.mjs
|
|
4107
|
-
import { createHash as createHash6 } from "node:crypto";
|
|
4108
|
-
import { existsSync as existsSync17, mkdirSync as mkdirSync6, readFileSync as readFileSync16, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "node:fs";
|
|
4109
|
-
import { join as join16 } from "node:path";
|
|
4110
|
-
var VECTOR_SCHEMA_VERSION = 2;
|
|
4111
|
-
var VECTOR_DIMENSIONS = 384;
|
|
4112
|
-
function profileFingerprint(identity2) {
|
|
4113
|
-
return createHash6("sha256").update(JSON.stringify(identity2)).digest("hex");
|
|
4114
|
-
}
|
|
4115
|
-
function isValidNormalizedVectorValues(values, dimensions = VECTOR_DIMENSIONS) {
|
|
4116
|
-
if (!Number.isInteger(dimensions) || dimensions < 1 || !values || values.length % dimensions !== 0) return false;
|
|
4117
|
-
for (let offset = 0; offset < values.length; offset += dimensions) {
|
|
4118
|
-
let squaredNorm = 0;
|
|
4119
|
-
for (let index = 0; index < dimensions; index += 1) {
|
|
4120
|
-
const value = values[offset + index];
|
|
4121
|
-
if (!Number.isFinite(value) || value < -1 || value > 1) return false;
|
|
4122
|
-
squaredNorm += value * value;
|
|
4123
|
-
}
|
|
4124
|
-
if (Math.abs(Math.sqrt(squaredNorm) - 1) > 0.01) return false;
|
|
4125
|
-
}
|
|
4126
|
-
return true;
|
|
4127
|
-
}
|
|
4128
|
-
function vectorCachePaths(root) {
|
|
4129
|
-
const directory = join16(root, ".dotdotgod", "vectors");
|
|
4130
|
-
return { directory, manifest: join16(directory, "manifest.json"), chunks: join16(directory, "chunks.jsonl"), embeddings: join16(directory, "embeddings.f32") };
|
|
4131
|
-
}
|
|
4132
|
-
function readVectorCache(root, identity2 = null) {
|
|
4133
|
-
const paths = vectorCachePaths(root);
|
|
4134
|
-
if (!existsSync17(paths.manifest) || !existsSync17(paths.chunks) || !existsSync17(paths.embeddings)) return null;
|
|
4135
|
-
try {
|
|
4136
|
-
const manifest = JSON.parse(readFileSync16(paths.manifest, "utf8"));
|
|
4137
|
-
if (manifest.schemaVersion !== VECTOR_SCHEMA_VERSION || !Number.isInteger(manifest.dimensions) || manifest.dimensions < 1) return null;
|
|
4138
|
-
if (identity2 && manifest.profileFingerprint !== profileFingerprint(identity2)) return null;
|
|
4139
|
-
const chunks = readFileSync16(paths.chunks, "utf8").split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
4140
|
-
const buffer = readFileSync16(paths.embeddings);
|
|
4141
|
-
if (buffer.byteLength !== chunks.length * manifest.dimensions * 4) return null;
|
|
4142
|
-
const vectors = new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 4);
|
|
4143
|
-
if (!isValidNormalizedVectorValues(vectors, manifest.dimensions)) return null;
|
|
4144
|
-
return { manifest, chunks, vectors: new Float32Array(vectors) };
|
|
4145
|
-
} catch {
|
|
4146
|
-
return null;
|
|
4147
|
-
}
|
|
4148
|
-
}
|
|
4149
|
-
function atomicWrite(path, data) {
|
|
4150
|
-
const temporary = `${path}.tmp-${process.pid}`;
|
|
4151
|
-
writeFileSync7(temporary, data);
|
|
4152
|
-
renameSync2(temporary, path);
|
|
4153
|
-
}
|
|
4154
|
-
function writeVectorCache(root, chunks, vectors, metadata = {}) {
|
|
4155
|
-
const paths = vectorCachePaths(root);
|
|
4156
|
-
const dimensions = metadata.dimensions;
|
|
4157
|
-
if (!Number.isInteger(dimensions) || dimensions < 1) throw new Error("Vector dimensions must be a positive integer.");
|
|
4158
|
-
mkdirSync6(paths.directory, { recursive: true });
|
|
4159
|
-
if (vectors.length !== chunks.length * dimensions) throw new Error("Vector count does not match chunk metadata.");
|
|
4160
|
-
if (!isValidNormalizedVectorValues(vectors, dimensions)) throw new Error("Vectors must contain finite normalized values.");
|
|
4161
|
-
const manifest = { schemaVersion: VECTOR_SCHEMA_VERSION, chunks: chunks.length, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), ...metadata };
|
|
4162
|
-
atomicWrite(paths.chunks, `${chunks.map((chunk) => JSON.stringify(chunk)).join("\n")}
|
|
4163
|
-
`);
|
|
4164
|
-
atomicWrite(paths.embeddings, Buffer.from(vectors.buffer, vectors.byteOffset, vectors.byteLength));
|
|
4165
|
-
atomicWrite(paths.manifest, `${JSON.stringify(manifest, null, 2)}
|
|
4166
|
-
`);
|
|
4167
|
-
return manifest;
|
|
4168
|
-
}
|
|
4169
|
-
|
|
4170
4290
|
// packages/cli/src/query/hybrid.mjs
|
|
4171
4291
|
var resultKey = (chunk) => chunk.kind === "symbol" ? chunk.id : chunk.path;
|
|
4172
4292
|
var terms = (text) => [...new Set(text.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean))];
|
|
@@ -4284,38 +4404,62 @@ async function buildVectorIndex(root, embedOrOptions = {}) {
|
|
|
4284
4404
|
const config = readMemoryConfig(root);
|
|
4285
4405
|
const exclude = config.load?.documentationSummary?.exclude ?? ["docs/plan", "docs/archive"];
|
|
4286
4406
|
const chunks = options.chunks ?? collectDocumentationChunks(root, exclude, config.documentation?.root ?? "docs");
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
const
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
rows[batch[index].index] = vector;
|
|
4407
|
+
return vectorCacheSession(root, resolved.identity, async (state, save, diagnostics) => {
|
|
4408
|
+
const selectedFingerprints = new Set(chunks.map((chunk) => chunk.fingerprint));
|
|
4409
|
+
state.pending = state.pending.filter((row) => selectedFingerprints.has(row.chunk.fingerprint));
|
|
4410
|
+
const recovered = new Set(state.pending.map((row) => row.chunk.fingerprint));
|
|
4411
|
+
const cacheRows = [...state.docs, ...state.code, ...state.pending];
|
|
4412
|
+
const cached = state.dimensions ? { chunks: cacheRows.map((row) => row.chunk), vectors: Float32Array.from(cacheRows.flatMap((row) => row.vector)), manifest: { dimensions: state.dimensions } } : null;
|
|
4413
|
+
const cachedOffsets = new Map((cached?.chunks ?? []).map((chunk, index) => [chunk.fingerprint, index]));
|
|
4414
|
+
const rows = new Array(chunks.length);
|
|
4415
|
+
const missing = [];
|
|
4416
|
+
let reused = 0;
|
|
4417
|
+
chunks.forEach((chunk, index) => {
|
|
4418
|
+
const oldIndex = cachedOffsets.get(chunk.fingerprint);
|
|
4419
|
+
if (oldIndex === void 0 || !cached) missing.push({ chunk, index });
|
|
4420
|
+
else {
|
|
4421
|
+
const d = cached.manifest.dimensions;
|
|
4422
|
+
rows[index] = Array.from(cached.vectors.subarray(oldIndex * d, (oldIndex + 1) * d));
|
|
4423
|
+
reused += 1;
|
|
4424
|
+
}
|
|
4306
4425
|
});
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4426
|
+
for (let offset = 0; offset < missing.length; offset += EMBED_BATCH_SIZE) {
|
|
4427
|
+
const batch = missing.slice(offset, offset + EMBED_BATCH_SIZE);
|
|
4428
|
+
const embedded = await resolved.embed(batch.map(({ chunk }) => chunk.passage), "document");
|
|
4429
|
+
if (!Array.isArray(embedded) || embedded.length !== batch.length) throw new Error("Embedder returned an unexpected vector count.");
|
|
4430
|
+
embedded.forEach((vector, index) => {
|
|
4431
|
+
const dimensions2 = state.dimensions ?? vector.length;
|
|
4432
|
+
assertVector(vector, dimensions2, "Passage vector");
|
|
4433
|
+
state.dimensions = dimensions2;
|
|
4434
|
+
rows[batch[index].index] = vector;
|
|
4435
|
+
const { passage, ...chunk } = batch[index].chunk;
|
|
4436
|
+
state.pending.push({ chunk, vector: Array.from(vector) });
|
|
4437
|
+
});
|
|
4438
|
+
save();
|
|
4439
|
+
}
|
|
4440
|
+
const dimensions = rows[0]?.length ?? cached?.manifest?.dimensions;
|
|
4441
|
+
if (!dimensions && chunks.length) throw new Error("No documentation chunks were available to establish vector dimensions.");
|
|
4442
|
+
if (rows.some((row) => !row || row.length !== dimensions)) throw new Error("Embedder returned inconsistent passage-vector dimensions.");
|
|
4443
|
+
const vectors = new Float32Array(chunks.length * dimensions);
|
|
4444
|
+
rows.forEach((row, index) => {
|
|
4445
|
+
assertVector(row, dimensions, "Passage vector");
|
|
4446
|
+
vectors.set(row, index * dimensions);
|
|
4447
|
+
});
|
|
4448
|
+
const storedChunks = chunks.map(({ passage, ...chunk }) => chunk);
|
|
4449
|
+
const refreshed = options.corpora ?? (options.chunks ? [...new Set(chunks.map((chunk) => chunk.kind === "symbol" ? "code" : "docs"))] : ["docs"]);
|
|
4450
|
+
const counters = {};
|
|
4451
|
+
for (const corpus of refreshed) {
|
|
4452
|
+
const positions = chunks.map((chunk, index) => ({ chunk, index })).filter(({ chunk }) => (chunk.kind === "symbol" ? "code" : "docs") === corpus);
|
|
4453
|
+
state[corpus] = positions.map(({ index }) => ({ chunk: storedChunks[index], vector: Array.from(rows[index]) }));
|
|
4454
|
+
counters[corpus] = { chunks: positions.length, embedded: missing.filter(({ chunk }) => (chunk.kind === "symbol" ? "code" : "docs") === corpus).length, recovered: positions.filter(({ chunk }) => recovered.has(chunk.fingerprint)).length };
|
|
4455
|
+
counters[corpus].reused = positions.length - counters[corpus].embedded;
|
|
4456
|
+
}
|
|
4457
|
+
state.pending = [];
|
|
4458
|
+
save();
|
|
4459
|
+
const metadata = { provider: resolved.profile.provider, model: resolved.profile.model, dimensions, profileFingerprint: profileFingerprint(resolved.identity), excluded: exclude, embedded: missing.length, reused, diagnostics: { ...diagnostics, corpora: counters } };
|
|
4460
|
+
const manifest = dimensions ? writeVectorCache(root, storedChunks, vectors, metadata) : { chunks: 0, ...metadata };
|
|
4461
|
+
return { chunks: storedChunks, vectors, manifest, embedding: resolved };
|
|
4315
4462
|
});
|
|
4316
|
-
const storedChunks = chunks.map(({ passage, ...chunk }) => chunk);
|
|
4317
|
-
const manifest = writeVectorCache(root, storedChunks, vectors, { provider: resolved.profile.provider, model: resolved.profile.model, dimensions, profileFingerprint: profileFingerprint(resolved.identity), excluded: exclude, embedded: missing.length, reused });
|
|
4318
|
-
return { chunks: storedChunks, vectors, manifest, embedding: resolved };
|
|
4319
4463
|
}
|
|
4320
4464
|
async function queryProject(root, query, options = {}) {
|
|
4321
4465
|
const scope = options.scope ?? "all";
|
|
@@ -4336,10 +4480,10 @@ async function queryProject(root, query, options = {}) {
|
|
|
4336
4480
|
}
|
|
4337
4481
|
if (!configured && !vectorError && search === "vector") throw new Error("Embedding is not configured. Set embedding in project or global configuration to use vector search.");
|
|
4338
4482
|
}
|
|
4339
|
-
if (search !== "keyword" && configured
|
|
4483
|
+
if (search !== "keyword" && configured) {
|
|
4340
4484
|
try {
|
|
4341
|
-
index = await buildVectorIndex(root, { ...options, chunks: corpus.chunks });
|
|
4342
|
-
const [queryVector] = await index.embedding.embed([query], "query");
|
|
4485
|
+
index = await buildVectorIndex(root, { ...options, chunks: corpus.chunks, corpora: scope === "all" ? ["docs", "code"] : [scope] });
|
|
4486
|
+
const [queryVector] = index.chunks.length ? await index.embedding.embed([query], "query") : [];
|
|
4343
4487
|
vector = index.chunks.map((chunk, position) => ({ ...chunk, vectorScore: cosineScore(queryVector, index.vectors, position * index.manifest.dimensions, index.manifest.dimensions) })).sort((a, b) => b.vectorScore - a.vectorScore || a.id.localeCompare(b.id));
|
|
4344
4488
|
} catch (error) {
|
|
4345
4489
|
if (search === "vector") throw error;
|
|
@@ -4430,7 +4574,7 @@ import { resolve as resolve14 } from "node:path";
|
|
|
4430
4574
|
|
|
4431
4575
|
// packages/cli/src/memory/documentation-map.mjs
|
|
4432
4576
|
import { existsSync as existsSync18, readdirSync as readdirSync4, statSync as statSync7 } from "node:fs";
|
|
4433
|
-
import { join as
|
|
4577
|
+
import { join as join18, resolve as resolve13 } from "node:path";
|
|
4434
4578
|
var SKIPPED_DIRECTORIES2 = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", ".git", ".dotdotgod"]);
|
|
4435
4579
|
function node() {
|
|
4436
4580
|
return { directories: /* @__PURE__ */ new Map(), files: [] };
|
|
@@ -4493,7 +4637,7 @@ function discoverMarkdown(root, documentationRoot, exclude) {
|
|
|
4493
4637
|
const walk = (relativeDirectory) => {
|
|
4494
4638
|
let entries;
|
|
4495
4639
|
try {
|
|
4496
|
-
entries = readdirSync4(
|
|
4640
|
+
entries = readdirSync4(join18(root, relativeDirectory), { withFileTypes: true, encoding: "utf8" });
|
|
4497
4641
|
} catch {
|
|
4498
4642
|
return;
|
|
4499
4643
|
}
|
|
@@ -5942,7 +6086,7 @@ function compareImpactItems(seeds) {
|
|
|
5942
6086
|
}
|
|
5943
6087
|
|
|
5944
6088
|
// packages/cli/src/impact/references.mjs
|
|
5945
|
-
import { readFileSync as
|
|
6089
|
+
import { readFileSync as readFileSync18, statSync as statSync8 } from "node:fs";
|
|
5946
6090
|
import { extname as extname7, resolve as resolve15 } from "node:path";
|
|
5947
6091
|
var WARNING_LIMIT = 20;
|
|
5948
6092
|
var edgeKey = (source, target, relation) => `${source}\0${target}\0${relation}`;
|
|
@@ -5958,7 +6102,7 @@ function prepareImpactReferences(index, root, changedPaths) {
|
|
|
5958
6102
|
const absolute = resolve15(root, path);
|
|
5959
6103
|
const stat = statSync8(absolute);
|
|
5960
6104
|
result = { status: "exists" };
|
|
5961
|
-
if (stat.isFile() && extname7(absolute) === ".md") result.anchors = extractAnchors(
|
|
6105
|
+
if (stat.isFile() && extname7(absolute) === ".md") result.anchors = extractAnchors(readFileSync18(absolute, "utf8"));
|
|
5962
6106
|
} catch (error) {
|
|
5963
6107
|
result = { status: ["ENOENT", "ENOTDIR"].includes(error.code) ? "missing" : "unavailable" };
|
|
5964
6108
|
}
|
|
@@ -6020,7 +6164,7 @@ function prepareImpactReferences(index, root, changedPaths) {
|
|
|
6020
6164
|
}
|
|
6021
6165
|
|
|
6022
6166
|
// packages/cli/src/impact/vector-profile.mjs
|
|
6023
|
-
import { lstatSync as lstatSync4, readFileSync as
|
|
6167
|
+
import { lstatSync as lstatSync4, readFileSync as readFileSync19, realpathSync as realpathSync3 } from "node:fs";
|
|
6024
6168
|
import { extname as extname8, relative as relative7, resolve as resolve16, sep } from "node:path";
|
|
6025
6169
|
var MAX_VECTOR_PROFILE_FILE_BYTES = 64 * 1024;
|
|
6026
6170
|
var MAX_VECTOR_PROFILE_CHARS = 4e3;
|
|
@@ -6079,7 +6223,7 @@ function safeTextPrefix(root, path) {
|
|
|
6079
6223
|
if (canonicalPath !== path || unsafeProfilePath(canonicalPath) || !TEXT_EXTENSIONS.has(extname8(canonicalPath).toLowerCase())) return "";
|
|
6080
6224
|
const stats = lstatSync4(canonicalAbsolute);
|
|
6081
6225
|
if (!stats.isFile() || stats.isSymbolicLink() || stats.size > MAX_VECTOR_PROFILE_FILE_BYTES) return "";
|
|
6082
|
-
const buffer =
|
|
6226
|
+
const buffer = readFileSync19(canonicalAbsolute);
|
|
6083
6227
|
if (buffer.includes(0)) return "";
|
|
6084
6228
|
const text = new TextDecoder("utf-8", { fatal: true }).decode(buffer);
|
|
6085
6229
|
return text.slice(0, MAX_VECTOR_PROFILE_CHARS);
|
|
@@ -6530,15 +6674,15 @@ function runExpand(argv) {
|
|
|
6530
6674
|
}
|
|
6531
6675
|
|
|
6532
6676
|
// packages/cli/src/commands/traceability.mjs
|
|
6533
|
-
import { existsSync as existsSync19, readFileSync as
|
|
6534
|
-
import { join as
|
|
6677
|
+
import { existsSync as existsSync19, readFileSync as readFileSync20, readdirSync as readdirSync5, writeFileSync as writeFileSync9 } from "node:fs";
|
|
6678
|
+
import { join as join19, resolve as resolve18 } from "node:path";
|
|
6535
6679
|
function collectDocsMarkdownFiles(root) {
|
|
6536
|
-
const docs =
|
|
6680
|
+
const docs = join19(root, "docs");
|
|
6537
6681
|
const files = [];
|
|
6538
6682
|
const walk = (dir) => {
|
|
6539
6683
|
if (!existsSync19(dir)) return;
|
|
6540
6684
|
for (const entry of readdirSync5(dir, { withFileTypes: true })) {
|
|
6541
|
-
const path =
|
|
6685
|
+
const path = join19(dir, entry.name);
|
|
6542
6686
|
if (entry.isDirectory()) walk(path);
|
|
6543
6687
|
else if (entry.isFile() && entry.name.endsWith(".md")) files.push(path);
|
|
6544
6688
|
}
|
|
@@ -6574,7 +6718,7 @@ function runTraceability(argv) {
|
|
|
6574
6718
|
const errors = [...config.errors ?? []];
|
|
6575
6719
|
for (const file of files) {
|
|
6576
6720
|
if (options.write && errors.length > 0) break;
|
|
6577
|
-
const content =
|
|
6721
|
+
const content = readFileSync20(file, "utf8");
|
|
6578
6722
|
for (const error of validateTraceabilityLinksRegion(content, options.root, file)) errors.push(error);
|
|
6579
6723
|
const blocks = extractDotdotgodTraceabilityBlocks(content).filter((block) => !block.error);
|
|
6580
6724
|
if (blocks.length === 0) continue;
|
|
@@ -6583,7 +6727,7 @@ function runTraceability(argv) {
|
|
|
6583
6727
|
for (const error of result.errors ?? []) errors.push(error);
|
|
6584
6728
|
continue;
|
|
6585
6729
|
}
|
|
6586
|
-
if (options.write && result.changed)
|
|
6730
|
+
if (options.write && result.changed) writeFileSync9(file, result.content);
|
|
6587
6731
|
if (result.changed) results.push({ file: rel(options.root, file), changed: true });
|
|
6588
6732
|
}
|
|
6589
6733
|
const ok = errors.length === 0 && (!options.check || results.length === 0);
|
|
@@ -6604,8 +6748,8 @@ function runTraceability(argv) {
|
|
|
6604
6748
|
|
|
6605
6749
|
// packages/cli/src/graph-view/server.mjs
|
|
6606
6750
|
import { createServer } from "node:http";
|
|
6607
|
-
import { readFileSync as
|
|
6608
|
-
import { dirname as dirname13, extname as extname10, join as
|
|
6751
|
+
import { readFileSync as readFileSync21 } from "node:fs";
|
|
6752
|
+
import { dirname as dirname13, extname as extname10, join as join20 } from "node:path";
|
|
6609
6753
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
6610
6754
|
|
|
6611
6755
|
// packages/cli/src/graph-view/payload.mjs
|
|
@@ -6717,7 +6861,7 @@ function buildImpactGraphPayload(index, impact) {
|
|
|
6717
6861
|
}
|
|
6718
6862
|
|
|
6719
6863
|
// packages/cli/src/graph-view/server.mjs
|
|
6720
|
-
var ASSET_ROOT =
|
|
6864
|
+
var ASSET_ROOT = join20(dirname13(fileURLToPath2(import.meta.url)), "assets");
|
|
6721
6865
|
var CONTENT_TYPES = { ".css": "text/css; charset=utf-8", ".html": "text/html; charset=utf-8", ".js": "text/javascript; charset=utf-8", ".map": "application/json; charset=utf-8" };
|
|
6722
6866
|
var VENDOR_FILES = {
|
|
6723
6867
|
"/vendor/sigma.js": new URL("../../node_modules/sigma/dist/sigma.min.js", import.meta.url),
|
|
@@ -6730,7 +6874,7 @@ function json(response, status, value) {
|
|
|
6730
6874
|
}
|
|
6731
6875
|
function asset(response, file) {
|
|
6732
6876
|
try {
|
|
6733
|
-
const body =
|
|
6877
|
+
const body = readFileSync21(file);
|
|
6734
6878
|
response.writeHead(200, { "content-type": CONTENT_TYPES[extname10(file)] ?? "application/octet-stream", "cache-control": "no-store" });
|
|
6735
6879
|
response.end(body);
|
|
6736
6880
|
} catch {
|
|
@@ -6772,7 +6916,7 @@ async function startImpactGraphServer({ root, changed, host = "127.0.0.1", port
|
|
|
6772
6916
|
response.writeHead(404);
|
|
6773
6917
|
return response.end("Not found");
|
|
6774
6918
|
}
|
|
6775
|
-
return asset(response,
|
|
6919
|
+
return asset(response, join20(ASSET_ROOT, relative8));
|
|
6776
6920
|
});
|
|
6777
6921
|
await new Promise((resolve19, reject) => {
|
|
6778
6922
|
server.once("error", reject);
|
package/mcp/server.mjs
CHANGED
|
@@ -23545,7 +23545,7 @@ function getJobs() {
|
|
|
23545
23545
|
jobRunner ??= new IngestionJobRunner(getStore());
|
|
23546
23546
|
return jobRunner;
|
|
23547
23547
|
}
|
|
23548
|
-
var server = new McpServer({ name: "dotdotgod-context", version: "0.6.
|
|
23548
|
+
var server = new McpServer({ name: "dotdotgod-context", version: "0.6.7" });
|
|
23549
23549
|
var scopeSchema = external_exports.enum(["transient", "session", "project"]).optional();
|
|
23550
23550
|
var outputModeSchema = external_exports.enum(["auto", "direct", "indexed", "discard"]).optional();
|
|
23551
23551
|
var commandSchema = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotdotgod/opencode",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"description": "OpenCode V2 project memory, automatic project plans and advisory impact tracking.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
],
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"esbuild": "0.27.3",
|
|
30
|
-
"@dotdotgod/context": "0.6.
|
|
30
|
+
"@dotdotgod/context": "0.6.7"
|
|
31
31
|
},
|
|
32
32
|
"repository": {
|
|
33
33
|
"type": "git",
|