kontex-core 1.0.0 → 1.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.
- package/dist/index.js +329 -307
- package/dist/keytar-f6bnxfss.node +0 -0
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -16,41 +16,6 @@ var __export = (target, all) => {
|
|
|
16
16
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
17
|
var __require = import.meta.require;
|
|
18
18
|
|
|
19
|
-
// src/secrets.ts
|
|
20
|
-
function buildExtraPatterns(extraPatterns) {
|
|
21
|
-
const result = [];
|
|
22
|
-
for (const pattern of extraPatterns) {
|
|
23
|
-
try {
|
|
24
|
-
result.push({ name: `custom:${pattern.slice(0, 30)}`, regex: new RegExp(pattern) });
|
|
25
|
-
} catch {}
|
|
26
|
-
}
|
|
27
|
-
return result;
|
|
28
|
-
}
|
|
29
|
-
function scanForSecrets(content, extraPatterns = []) {
|
|
30
|
-
const allPatterns = [...SECRET_PATTERNS, ...buildExtraPatterns(extraPatterns)];
|
|
31
|
-
for (const { name, regex } of allPatterns) {
|
|
32
|
-
if (regex.test(content)) {
|
|
33
|
-
return { blocked: true, pattern: name };
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return { blocked: false };
|
|
37
|
-
}
|
|
38
|
-
var SECRET_PATTERNS;
|
|
39
|
-
var init_secrets = __esm(() => {
|
|
40
|
-
SECRET_PATTERNS = [
|
|
41
|
-
{ name: "generic-long-token", regex: /['"]\w{32,}['"]/ },
|
|
42
|
-
{ name: "api-key-assignment", regex: /api[_-]?key\s*[:=]\s*['"]?\w+/i },
|
|
43
|
-
{ name: "secret-key-assignment", regex: /secret[_-]?key\s*[:=]\s*['"]?\w+/i },
|
|
44
|
-
{ name: "password-assignment", regex: /password\s*[:=]\s*['"]?[^\s'"]{8,}/i },
|
|
45
|
-
{ name: "postgres-connection", regex: /postgres:\/\/[^@]+:[^@]+@/ },
|
|
46
|
-
{ name: "mysql-connection", regex: /mysql:\/\/[^@]+:[^@]+@/ },
|
|
47
|
-
{ name: "mongodb-connection", regex: /mongodb\+srv:\/\/[^@]+:[^@]+@/ },
|
|
48
|
-
{ name: "openai-key", regex: /sk-[a-zA-Z0-9]{40,}/ },
|
|
49
|
-
{ name: "github-personal-token", regex: /ghp_[a-zA-Z0-9]{36}/ },
|
|
50
|
-
{ name: "aws-access-key", regex: /AKIA[A-Z0-9]{16}/ }
|
|
51
|
-
];
|
|
52
|
-
});
|
|
53
|
-
|
|
54
19
|
// src/storage/db.ts
|
|
55
20
|
var exports_db = {};
|
|
56
21
|
__export(exports_db, {
|
|
@@ -63,8 +28,9 @@ import { join as join2 } from "path";
|
|
|
63
28
|
import { existsSync as existsSync2, mkdirSync } from "fs";
|
|
64
29
|
import * as sqliteVec from "sqlite-vec";
|
|
65
30
|
function getDatabase(workspaceRoot) {
|
|
66
|
-
|
|
67
|
-
|
|
31
|
+
const existing = dbInstances.get(workspaceRoot);
|
|
32
|
+
if (existing)
|
|
33
|
+
return existing;
|
|
68
34
|
const indexDir = join2(workspaceRoot, INDEX_DIR);
|
|
69
35
|
if (!existsSync2(indexDir))
|
|
70
36
|
mkdirSync(indexDir, { recursive: true });
|
|
@@ -73,13 +39,20 @@ function getDatabase(workspaceRoot) {
|
|
|
73
39
|
db.exec("PRAGMA synchronous=NORMAL");
|
|
74
40
|
tryLoadVecExtension(db);
|
|
75
41
|
runMigrations(db);
|
|
76
|
-
|
|
42
|
+
dbInstances.set(workspaceRoot, db);
|
|
77
43
|
return db;
|
|
78
44
|
}
|
|
79
|
-
function closeDatabase() {
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
-
|
|
45
|
+
function closeDatabase(workspaceRoot) {
|
|
46
|
+
if (workspaceRoot) {
|
|
47
|
+
const db = dbInstances.get(workspaceRoot);
|
|
48
|
+
if (db) {
|
|
49
|
+
db.close();
|
|
50
|
+
dbInstances.delete(workspaceRoot);
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
for (const db of dbInstances.values())
|
|
54
|
+
db.close();
|
|
55
|
+
dbInstances.clear();
|
|
83
56
|
}
|
|
84
57
|
}
|
|
85
58
|
function runMigrations(db) {
|
|
@@ -131,12 +104,240 @@ function tryLoadVecExtension(db) {
|
|
|
131
104
|
console.warn(`kontex: sqlite-vec extension not available (${message}). Semantic search will use keyword matching.`);
|
|
132
105
|
}
|
|
133
106
|
}
|
|
134
|
-
var INDEX_DIR = ".kontex-index", DB_FILENAME = "index.db", EMBEDDING_DIM = 384,
|
|
135
|
-
var init_db = () => {
|
|
107
|
+
var INDEX_DIR = ".kontex-index", DB_FILENAME = "index.db", EMBEDDING_DIM = 384, dbInstances;
|
|
108
|
+
var init_db = __esm(() => {
|
|
109
|
+
dbInstances = new Map;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// src/llm.ts
|
|
113
|
+
var exports_llm = {};
|
|
114
|
+
__export(exports_llm, {
|
|
115
|
+
createLLMModel: () => createLLMModel
|
|
116
|
+
});
|
|
117
|
+
async function createLLMModel(config, token) {
|
|
118
|
+
const { createOpenAI } = await import("@ai-sdk/openai");
|
|
119
|
+
switch (config.llm.provider) {
|
|
120
|
+
case "github-models":
|
|
121
|
+
return createOpenAI({
|
|
122
|
+
baseURL: "https://models.inference.ai.azure.com",
|
|
123
|
+
apiKey: token ?? ""
|
|
124
|
+
})(config.llm.model);
|
|
125
|
+
case "openai":
|
|
126
|
+
return createOpenAI({
|
|
127
|
+
apiKey: config.llm.apiKey ?? process.env.OPENAI_API_KEY ?? ""
|
|
128
|
+
})(config.llm.model);
|
|
129
|
+
case "ollama":
|
|
130
|
+
return createOpenAI({
|
|
131
|
+
baseURL: "http://localhost:11434/v1",
|
|
132
|
+
apiKey: "ollama"
|
|
133
|
+
})(config.llm.model);
|
|
134
|
+
case "anthropic":
|
|
135
|
+
return null;
|
|
136
|
+
case "none":
|
|
137
|
+
return null;
|
|
138
|
+
default:
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/config.ts
|
|
144
|
+
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
145
|
+
import { join } from "path";
|
|
146
|
+
var DEFAULT_CONFIG = {
|
|
147
|
+
compile: {
|
|
148
|
+
tokenBudget: 3000,
|
|
149
|
+
alwaysInclude: ["memory/project.md"],
|
|
150
|
+
excludePaths: ["memory/sessions/archive/"]
|
|
151
|
+
},
|
|
152
|
+
embedding: {
|
|
153
|
+
provider: "local",
|
|
154
|
+
model: "Xenova/all-MiniLM-L6-v2"
|
|
155
|
+
},
|
|
156
|
+
llm: {
|
|
157
|
+
provider: "github-models",
|
|
158
|
+
model: "gpt-4o-mini"
|
|
159
|
+
},
|
|
160
|
+
quality: {
|
|
161
|
+
minConfidence: 0.6,
|
|
162
|
+
autoVerifyThreshold: 0.85,
|
|
163
|
+
deduplicateThreshold: 0.92,
|
|
164
|
+
contradictionThreshold: 0.75
|
|
165
|
+
},
|
|
166
|
+
hooks: {
|
|
167
|
+
postCommitExtract: true,
|
|
168
|
+
postMergeRecompile: true,
|
|
169
|
+
maxBackgroundRetries: 2
|
|
170
|
+
},
|
|
171
|
+
secrets: {
|
|
172
|
+
scan: true,
|
|
173
|
+
extraPatterns: []
|
|
174
|
+
},
|
|
175
|
+
decay: {
|
|
176
|
+
sessionArchiveDays: 7,
|
|
177
|
+
unverifiedExpireDays: 30,
|
|
178
|
+
maxSessionsDirKB: 500
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
var CONFIG_FILENAME = "kontex.config.json";
|
|
182
|
+
function resolveEnvVars(value) {
|
|
183
|
+
return value.replace(/\$\{(\w+)\}/g, (_, envKey) => {
|
|
184
|
+
return process.env[envKey] ?? "";
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
function deepMerge(defaults, overrides) {
|
|
188
|
+
const result = { ...defaults };
|
|
189
|
+
for (const key of Object.keys(overrides)) {
|
|
190
|
+
const overrideVal = overrides[key];
|
|
191
|
+
const defaultVal = defaults[key];
|
|
192
|
+
if (overrideVal !== undefined && typeof overrideVal === "object" && !Array.isArray(overrideVal) && typeof defaultVal === "object" && !Array.isArray(defaultVal) && defaultVal !== null) {
|
|
193
|
+
result[key] = deepMerge(defaultVal, overrideVal);
|
|
194
|
+
} else if (overrideVal !== undefined) {
|
|
195
|
+
result[key] = overrideVal;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
function loadConfig(workspaceRoot) {
|
|
201
|
+
const configPath = join(workspaceRoot, CONFIG_FILENAME);
|
|
202
|
+
if (!existsSync(configPath)) {
|
|
203
|
+
return { ...DEFAULT_CONFIG };
|
|
204
|
+
}
|
|
205
|
+
try {
|
|
206
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
207
|
+
const parsed = JSON.parse(raw);
|
|
208
|
+
const merged = deepMerge(DEFAULT_CONFIG, parsed);
|
|
209
|
+
if (merged.llm && merged.llm.apiKey) {
|
|
210
|
+
merged.llm.apiKey = resolveEnvVars(merged.llm.apiKey);
|
|
211
|
+
}
|
|
212
|
+
return merged;
|
|
213
|
+
} catch (err) {
|
|
214
|
+
console.warn(`kontex: Failed to parse ${configPath} \u2014 using defaults. Fix the JSON syntax to apply your settings.
|
|
215
|
+
Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
216
|
+
return { ...DEFAULT_CONFIG };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function writeConfig(workspaceRoot, config) {
|
|
220
|
+
const configPath = join(workspaceRoot, CONFIG_FILENAME);
|
|
221
|
+
const serializable = { ...config };
|
|
222
|
+
writeFileSync(configPath, JSON.stringify(serializable, null, 2) + `
|
|
223
|
+
`, "utf-8");
|
|
224
|
+
}
|
|
225
|
+
// src/secrets.ts
|
|
226
|
+
var SECRET_PATTERNS = [
|
|
227
|
+
{ name: "generic-long-token", regex: /['"]\w{32,}['"]/ },
|
|
228
|
+
{ name: "api-key-assignment", regex: /api[_-]?key\s*[:=]\s*['"]?\w+/i },
|
|
229
|
+
{ name: "secret-key-assignment", regex: /secret[_-]?key\s*[:=]\s*['"]?\w+/i },
|
|
230
|
+
{ name: "password-assignment", regex: /password\s*[:=]\s*['"]?[^\s'"]{8,}/i },
|
|
231
|
+
{ name: "postgres-connection", regex: /postgres:\/\/[^@]+:[^@]+@/ },
|
|
232
|
+
{ name: "mysql-connection", regex: /mysql:\/\/[^@]+:[^@]+@/ },
|
|
233
|
+
{ name: "mongodb-connection", regex: /mongodb\+srv:\/\/[^@]+:[^@]+@/ },
|
|
234
|
+
{ name: "openai-key", regex: /sk-[a-zA-Z0-9]{40,}/ },
|
|
235
|
+
{ name: "github-personal-token", regex: /ghp_[a-zA-Z0-9]{36}/ },
|
|
236
|
+
{ name: "aws-access-key", regex: /AKIA[A-Z0-9]{16}/ }
|
|
237
|
+
];
|
|
238
|
+
var extraPatternCache = new Map;
|
|
239
|
+
function buildExtraPatterns(extraPatterns) {
|
|
240
|
+
if (extraPatterns.length === 0)
|
|
241
|
+
return [];
|
|
242
|
+
const cacheKey = extraPatterns.join("\x00");
|
|
243
|
+
const cached = extraPatternCache.get(cacheKey);
|
|
244
|
+
if (cached)
|
|
245
|
+
return cached;
|
|
246
|
+
const result = [];
|
|
247
|
+
for (const pattern of extraPatterns) {
|
|
248
|
+
try {
|
|
249
|
+
result.push({ name: `custom:${pattern.slice(0, 30)}`, regex: new RegExp(pattern) });
|
|
250
|
+
} catch {}
|
|
251
|
+
}
|
|
252
|
+
extraPatternCache.set(cacheKey, result);
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
255
|
+
function scanForSecrets(content, extraPatterns = []) {
|
|
256
|
+
const allPatterns = [...SECRET_PATTERNS, ...buildExtraPatterns(extraPatterns)];
|
|
257
|
+
for (const { name, regex } of allPatterns) {
|
|
258
|
+
if (regex.test(content)) {
|
|
259
|
+
return { blocked: true, pattern: name };
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return { blocked: false };
|
|
263
|
+
}
|
|
264
|
+
// src/auth.ts
|
|
265
|
+
var KEYCHAIN_SERVICE = "kontex";
|
|
266
|
+
var KEYCHAIN_ACCOUNT = "github-oauth";
|
|
267
|
+
async function getKeytar() {
|
|
268
|
+
const mod = await import("keytar");
|
|
269
|
+
return mod.default;
|
|
270
|
+
}
|
|
271
|
+
var GITHUB_CLIENT_ID = process.env.KONTEX_GITHUB_CLIENT_ID ?? "Ov23liMXcybhETe03nNJ";
|
|
272
|
+
async function login() {
|
|
273
|
+
const { createOAuthDeviceAuth } = await import("@octokit/auth-oauth-device");
|
|
274
|
+
const auth = createOAuthDeviceAuth({
|
|
275
|
+
clientType: "oauth-app",
|
|
276
|
+
clientId: GITHUB_CLIENT_ID,
|
|
277
|
+
scopes: [],
|
|
278
|
+
onVerification: (verification) => {
|
|
279
|
+
console.log(`
|
|
280
|
+
Visit: ${verification.verification_uri}`);
|
|
281
|
+
console.log(`Code: ${verification.user_code}
|
|
282
|
+
`);
|
|
283
|
+
console.log(`Waiting for authorization...
|
|
284
|
+
`);
|
|
285
|
+
openBrowser(verification.verification_uri).catch(() => {});
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
const { token } = await auth({ type: "oauth" });
|
|
289
|
+
const keytar = await getKeytar();
|
|
290
|
+
await keytar.setPassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT, token);
|
|
291
|
+
const username = await fetchGitHubUsername(token);
|
|
292
|
+
console.log(`\u2713 Authenticated as @${username}`);
|
|
293
|
+
console.log("\u2713 GitHub Models access confirmed");
|
|
294
|
+
return username;
|
|
295
|
+
}
|
|
296
|
+
async function logout() {
|
|
297
|
+
const keytar = await getKeytar();
|
|
298
|
+
const deleted = await keytar.deletePassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT);
|
|
299
|
+
console.log(deleted ? "\u2713 Token removed from keychain" : "No token found in keychain");
|
|
300
|
+
}
|
|
301
|
+
async function getToken() {
|
|
302
|
+
const keytar = await getKeytar();
|
|
303
|
+
return keytar.getPassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT);
|
|
304
|
+
}
|
|
305
|
+
async function isAuthenticated() {
|
|
306
|
+
const token = await getToken();
|
|
307
|
+
return token !== null;
|
|
308
|
+
}
|
|
309
|
+
async function fetchGitHubUsername(token) {
|
|
310
|
+
const response = await fetch("https://api.github.com/user", {
|
|
311
|
+
headers: { Authorization: `Bearer ${token}`, Accept: "application/vnd.github+json" }
|
|
312
|
+
});
|
|
313
|
+
if (!response.ok)
|
|
314
|
+
throw new Error(`GitHub API returned ${response.status}`);
|
|
315
|
+
const data = await response.json();
|
|
316
|
+
return data.login;
|
|
317
|
+
}
|
|
318
|
+
async function openBrowser(url) {
|
|
319
|
+
const { execFile } = await import("child_process");
|
|
320
|
+
const [cmd, ...args] = process.platform === "darwin" ? ["open", url] : process.platform === "win32" ? ["cmd", "/c", "start", "", url] : ["xdg-open", url];
|
|
321
|
+
return new Promise((resolve, reject) => {
|
|
322
|
+
execFile(cmd, args, (error) => {
|
|
323
|
+
if (error)
|
|
324
|
+
reject(error);
|
|
325
|
+
else
|
|
326
|
+
resolve();
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// src/index.ts
|
|
332
|
+
init_db();
|
|
136
333
|
|
|
137
334
|
// src/storage/embeddings.ts
|
|
138
335
|
import { join as join3 } from "path";
|
|
139
336
|
import { homedir } from "os";
|
|
337
|
+
var DEFAULT_MODEL = "Xenova/all-MiniLM-L6-v2";
|
|
338
|
+
var CACHE_DIR = join3(homedir(), ".cache", "kontex", "models");
|
|
339
|
+
var pipeline = null;
|
|
340
|
+
var modelLoading = null;
|
|
140
341
|
async function initEmbeddingModel(modelName = DEFAULT_MODEL) {
|
|
141
342
|
if (pipeline)
|
|
142
343
|
return;
|
|
@@ -157,21 +358,11 @@ async function loadPipeline(modelName) {
|
|
|
157
358
|
const pipe = await createPipeline("feature-extraction", modelName);
|
|
158
359
|
return pipe;
|
|
159
360
|
}
|
|
160
|
-
var DEFAULT_MODEL = "Xenova/all-MiniLM-L6-v2", CACHE_DIR, pipeline = null, modelLoading = null;
|
|
161
|
-
var init_embeddings = __esm(() => {
|
|
162
|
-
CACHE_DIR = join3(homedir(), ".cache", "kontex", "models");
|
|
163
|
-
});
|
|
164
|
-
|
|
165
361
|
// src/memory/write.ts
|
|
166
|
-
var exports_write = {};
|
|
167
|
-
__export(exports_write, {
|
|
168
|
-
writeMemory: () => writeMemory,
|
|
169
|
-
logDecision: () => logDecision,
|
|
170
|
-
invalidateMemory: () => invalidateMemory
|
|
171
|
-
});
|
|
172
362
|
import { writeFileSync as writeFileSync2, readFileSync as readFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync2, appendFileSync } from "fs";
|
|
173
363
|
import { join as join4, dirname } from "path";
|
|
174
364
|
import matter from "gray-matter";
|
|
365
|
+
init_db();
|
|
175
366
|
async function writeMemory(entry, workspaceRoot, config) {
|
|
176
367
|
const logPath = join4(workspaceRoot, ".kontex-log", "quality.log");
|
|
177
368
|
const secretResult = scanForSecrets(entry.content, config.secrets.extraPatterns);
|
|
@@ -233,7 +424,7 @@ ${l1}
|
|
|
233
424
|
if (!existsSync3(fileDir))
|
|
234
425
|
mkdirSync2(fileDir, { recursive: true });
|
|
235
426
|
writeFileSync2(filePath, fileContent, "utf-8");
|
|
236
|
-
await indexMemoryEntry(uri, entry.content, entry.type, verified, entry.confidence, entry.affected_paths ?? [], db, l0, l1, "");
|
|
427
|
+
await indexMemoryEntry(uri, entry.content, entry.type, verified, entry.confidence, entry.affected_paths ?? [], db, l0, l1, "", author, frontmatter.tags, frontmatter.global, frontmatter.stale, frontmatter.ref_count);
|
|
237
428
|
logQualityEvent(logPath, "WRITTEN", `${uri} (verified: ${verified})`);
|
|
238
429
|
return { success: true, uri, verified };
|
|
239
430
|
}
|
|
@@ -257,7 +448,11 @@ async function logDecision(adr, workspaceRoot, _config) {
|
|
|
257
448
|
mkdirSync2(decisionsDir, { recursive: true });
|
|
258
449
|
const { readdirSync } = await import("fs");
|
|
259
450
|
const existing = readdirSync(decisionsDir).filter((f) => f.endsWith(".md"));
|
|
260
|
-
const
|
|
451
|
+
const highestNum = existing.reduce((max, filename) => {
|
|
452
|
+
const match = filename.match(/^(\d+)-/);
|
|
453
|
+
return match ? Math.max(max, parseInt(match[1], 10)) : max;
|
|
454
|
+
}, 0);
|
|
455
|
+
const nextNum = String(highestNum + 1).padStart(3, "0");
|
|
261
456
|
const slug = slugify(adr.title);
|
|
262
457
|
const uri = `memory/decisions/${nextNum}-${slug}`;
|
|
263
458
|
const now = new Date().toISOString();
|
|
@@ -307,7 +502,7 @@ ${adr.rationale}${alternativesSection}${consequencesSection}
|
|
|
307
502
|
writeFileSync2(filePath, fileContent, "utf-8");
|
|
308
503
|
const db = getDatabase(workspaceRoot);
|
|
309
504
|
const adrContent = `${adr.title} ${adr.decision} ${adr.context}`;
|
|
310
|
-
await indexMemoryEntry(uri, adrContent, "decision", true, 0.95, adr.affected_paths ?? [], db, l0, body, "");
|
|
505
|
+
await indexMemoryEntry(uri, adrContent, "decision", true, 0.95, adr.affected_paths ?? [], db, l0, body, "", author, frontmatter.tags, frontmatter.global, frontmatter.stale, frontmatter.ref_count);
|
|
311
506
|
return { success: true, uri, verified: true };
|
|
312
507
|
}
|
|
313
508
|
async function dedupCheck(content, db, config) {
|
|
@@ -327,21 +522,28 @@ async function dedupCheck(content, db, config) {
|
|
|
327
522
|
if (similarity > config.quality.contradictionThreshold)
|
|
328
523
|
return { status: "conflict", existing_uri: top.uri, existing_content: top.content, message: `Similar memory exists at ${top.uri}. If this supersedes it, call kontex_invalidate first.` };
|
|
329
524
|
return { status: "clear" };
|
|
330
|
-
} catch {
|
|
525
|
+
} catch (err) {
|
|
526
|
+
console.warn(`kontex: dedup check failed (embedding or DB error) \u2014 skipping duplicate detection.
|
|
527
|
+
Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
331
528
|
return { status: "clear" };
|
|
332
529
|
}
|
|
333
530
|
}
|
|
334
|
-
async function indexMemoryEntry(uri, content, type, verified, confidence, affectedPaths, db, l0 = "", l1 = "", l2 = "") {
|
|
335
|
-
db.prepare(`INSERT OR REPLACE INTO memories (uri, content, type, l0, l1, l2, verified, confidence, affected_paths, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))`).run(uri, content, type, l0, l1, l2, verified ? 1 : 0, confidence, JSON.stringify(affectedPaths));
|
|
531
|
+
async function indexMemoryEntry(uri, content, type, verified, confidence, affectedPaths, db, l0 = "", l1 = "", l2 = "", author = "", tags = [], global = false, stale = false, refCount = 0) {
|
|
532
|
+
db.prepare(`INSERT OR REPLACE INTO memories (uri, content, type, l0, l1, l2, verified, confidence, affected_paths, author, tags, global, stale, ref_count, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))`).run(uri, content, type, l0, l1, l2, verified ? 1 : 0, confidence, JSON.stringify(affectedPaths), author, JSON.stringify(tags), global ? 1 : 0, stale ? 1 : 0, refCount);
|
|
336
533
|
try {
|
|
337
534
|
const embedding = await embed(content);
|
|
338
535
|
db.prepare(`INSERT OR REPLACE INTO memory_embeddings (uri, embedding) VALUES (?, ?)`).run(uri, Buffer.from(embedding.buffer));
|
|
339
|
-
} catch {
|
|
536
|
+
} catch (err) {
|
|
537
|
+
console.warn(`kontex: failed to index embedding for ${uri} \u2014 semantic search will not find this entry.
|
|
538
|
+
Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
539
|
+
}
|
|
340
540
|
}
|
|
541
|
+
var uriCounter = 0;
|
|
341
542
|
function generateUri(type, content) {
|
|
342
543
|
const slug = slugify(content.split(`
|
|
343
544
|
`)[0]?.slice(0, 60) ?? "entry");
|
|
344
|
-
|
|
545
|
+
const uniqueSuffix = `${Date.now().toString(36)}-${(uriCounter++).toString(36)}`;
|
|
546
|
+
return `memory/${type}s/${slug}-${uniqueSuffix}`;
|
|
345
547
|
}
|
|
346
548
|
function slugify(text) {
|
|
347
549
|
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 50);
|
|
@@ -371,199 +573,11 @@ function logQualityEvent(logPath, event, message) {
|
|
|
371
573
|
function logSecurityEvent(workspaceRoot, pattern) {
|
|
372
574
|
logQualityEvent(join4(workspaceRoot, ".kontex-log", "security.log"), "SECRET_BLOCKED", pattern);
|
|
373
575
|
}
|
|
374
|
-
var init_write = __esm(() => {
|
|
375
|
-
init_secrets();
|
|
376
|
-
init_embeddings();
|
|
377
|
-
init_db();
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
// src/llm.ts
|
|
381
|
-
var exports_llm = {};
|
|
382
|
-
__export(exports_llm, {
|
|
383
|
-
createLLMModel: () => createLLMModel
|
|
384
|
-
});
|
|
385
|
-
async function createLLMModel(config, token) {
|
|
386
|
-
const { createOpenAI } = await import("@ai-sdk/openai");
|
|
387
|
-
switch (config.llm.provider) {
|
|
388
|
-
case "github-models":
|
|
389
|
-
return createOpenAI({
|
|
390
|
-
baseURL: "https://models.inference.ai.azure.com",
|
|
391
|
-
apiKey: token ?? ""
|
|
392
|
-
})(config.llm.model);
|
|
393
|
-
case "openai":
|
|
394
|
-
return createOpenAI({
|
|
395
|
-
apiKey: config.llm.apiKey ?? process.env.OPENAI_API_KEY ?? ""
|
|
396
|
-
})(config.llm.model);
|
|
397
|
-
case "ollama":
|
|
398
|
-
return createOpenAI({
|
|
399
|
-
baseURL: "http://localhost:11434/v1",
|
|
400
|
-
apiKey: "ollama"
|
|
401
|
-
})(config.llm.model);
|
|
402
|
-
case "anthropic":
|
|
403
|
-
return null;
|
|
404
|
-
case "none":
|
|
405
|
-
return null;
|
|
406
|
-
default:
|
|
407
|
-
return null;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// src/config.ts
|
|
412
|
-
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
413
|
-
import { join } from "path";
|
|
414
|
-
var DEFAULT_CONFIG = {
|
|
415
|
-
compile: {
|
|
416
|
-
tokenBudget: 3000,
|
|
417
|
-
alwaysInclude: ["memory/project.md"],
|
|
418
|
-
excludePaths: ["memory/sessions/archive/"]
|
|
419
|
-
},
|
|
420
|
-
embedding: {
|
|
421
|
-
provider: "local",
|
|
422
|
-
model: "Xenova/all-MiniLM-L6-v2"
|
|
423
|
-
},
|
|
424
|
-
llm: {
|
|
425
|
-
provider: "github-models",
|
|
426
|
-
model: "gpt-4o-mini"
|
|
427
|
-
},
|
|
428
|
-
quality: {
|
|
429
|
-
minConfidence: 0.6,
|
|
430
|
-
autoVerifyThreshold: 0.85,
|
|
431
|
-
deduplicateThreshold: 0.92,
|
|
432
|
-
contradictionThreshold: 0.75
|
|
433
|
-
},
|
|
434
|
-
hooks: {
|
|
435
|
-
postCommitExtract: true,
|
|
436
|
-
postMergeRecompile: true,
|
|
437
|
-
maxBackgroundRetries: 2
|
|
438
|
-
},
|
|
439
|
-
secrets: {
|
|
440
|
-
scan: true,
|
|
441
|
-
extraPatterns: []
|
|
442
|
-
},
|
|
443
|
-
decay: {
|
|
444
|
-
sessionArchiveDays: 7,
|
|
445
|
-
unverifiedExpireDays: 30,
|
|
446
|
-
maxSessionsDirKB: 500
|
|
447
|
-
}
|
|
448
|
-
};
|
|
449
|
-
var CONFIG_FILENAME = "kontex.config.json";
|
|
450
|
-
function resolveEnvVars(value) {
|
|
451
|
-
return value.replace(/\$\{(\w+)\}/g, (_, envKey) => {
|
|
452
|
-
return process.env[envKey] ?? "";
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
function deepMerge(defaults, overrides) {
|
|
456
|
-
const result = { ...defaults };
|
|
457
|
-
for (const key of Object.keys(overrides)) {
|
|
458
|
-
const overrideVal = overrides[key];
|
|
459
|
-
const defaultVal = defaults[key];
|
|
460
|
-
if (overrideVal !== undefined && typeof overrideVal === "object" && !Array.isArray(overrideVal) && typeof defaultVal === "object" && !Array.isArray(defaultVal) && defaultVal !== null) {
|
|
461
|
-
result[key] = deepMerge(defaultVal, overrideVal);
|
|
462
|
-
} else if (overrideVal !== undefined) {
|
|
463
|
-
result[key] = overrideVal;
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
return result;
|
|
467
|
-
}
|
|
468
|
-
function loadConfig(workspaceRoot) {
|
|
469
|
-
const configPath = join(workspaceRoot, CONFIG_FILENAME);
|
|
470
|
-
if (!existsSync(configPath)) {
|
|
471
|
-
return { ...DEFAULT_CONFIG };
|
|
472
|
-
}
|
|
473
|
-
try {
|
|
474
|
-
const raw = readFileSync(configPath, "utf-8");
|
|
475
|
-
const parsed = JSON.parse(raw);
|
|
476
|
-
const merged = deepMerge(DEFAULT_CONFIG, parsed);
|
|
477
|
-
if (merged.llm && merged.llm.apiKey) {
|
|
478
|
-
merged.llm.apiKey = resolveEnvVars(merged.llm.apiKey);
|
|
479
|
-
}
|
|
480
|
-
return merged;
|
|
481
|
-
} catch {
|
|
482
|
-
return { ...DEFAULT_CONFIG };
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
function writeConfig(workspaceRoot, config) {
|
|
486
|
-
const configPath = join(workspaceRoot, CONFIG_FILENAME);
|
|
487
|
-
const serializable = { ...config };
|
|
488
|
-
writeFileSync(configPath, JSON.stringify(serializable, null, 2) + `
|
|
489
|
-
`, "utf-8");
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// src/index.ts
|
|
493
|
-
init_secrets();
|
|
494
|
-
|
|
495
|
-
// src/auth.ts
|
|
496
|
-
import keytar from "keytar";
|
|
497
|
-
var KEYCHAIN_SERVICE = "kontex";
|
|
498
|
-
var KEYCHAIN_ACCOUNT = "github-oauth";
|
|
499
|
-
var GITHUB_CLIENT_ID = "Ov23liMXcybhETe03nNJ";
|
|
500
|
-
async function login() {
|
|
501
|
-
const { createOAuthDeviceAuth } = await import("@octokit/auth-oauth-device");
|
|
502
|
-
const auth = createOAuthDeviceAuth({
|
|
503
|
-
clientType: "oauth-app",
|
|
504
|
-
clientId: GITHUB_CLIENT_ID,
|
|
505
|
-
scopes: [],
|
|
506
|
-
onVerification: (verification) => {
|
|
507
|
-
console.log(`
|
|
508
|
-
Visit: ${verification.verification_uri}`);
|
|
509
|
-
console.log(`Code: ${verification.user_code}
|
|
510
|
-
`);
|
|
511
|
-
console.log(`Waiting for authorization...
|
|
512
|
-
`);
|
|
513
|
-
openBrowser(verification.verification_uri).catch(() => {});
|
|
514
|
-
}
|
|
515
|
-
});
|
|
516
|
-
const { token } = await auth({ type: "oauth" });
|
|
517
|
-
await keytar.setPassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT, token);
|
|
518
|
-
const username = await fetchGitHubUsername(token);
|
|
519
|
-
console.log(`\u2713 Authenticated as @${username}`);
|
|
520
|
-
console.log("\u2713 GitHub Models access confirmed");
|
|
521
|
-
return username;
|
|
522
|
-
}
|
|
523
|
-
async function logout() {
|
|
524
|
-
const deleted = await keytar.deletePassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT);
|
|
525
|
-
console.log(deleted ? "\u2713 Token removed from keychain" : "No token found in keychain");
|
|
526
|
-
}
|
|
527
|
-
async function getToken() {
|
|
528
|
-
return keytar.getPassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT);
|
|
529
|
-
}
|
|
530
|
-
async function isAuthenticated() {
|
|
531
|
-
const token = await getToken();
|
|
532
|
-
return token !== null;
|
|
533
|
-
}
|
|
534
|
-
async function fetchGitHubUsername(token) {
|
|
535
|
-
const response = await fetch("https://api.github.com/user", {
|
|
536
|
-
headers: { Authorization: `Bearer ${token}`, Accept: "application/vnd.github+json" }
|
|
537
|
-
});
|
|
538
|
-
if (!response.ok)
|
|
539
|
-
throw new Error(`GitHub API returned ${response.status}`);
|
|
540
|
-
const data = await response.json();
|
|
541
|
-
return data.login;
|
|
542
|
-
}
|
|
543
|
-
async function openBrowser(url) {
|
|
544
|
-
const { exec } = await import("child_process");
|
|
545
|
-
const command = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "${url}"` : `xdg-open "${url}"`;
|
|
546
|
-
return new Promise((resolve, reject) => {
|
|
547
|
-
exec(command, (error) => {
|
|
548
|
-
if (error)
|
|
549
|
-
reject(error);
|
|
550
|
-
else
|
|
551
|
-
resolve();
|
|
552
|
-
});
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
// src/index.ts
|
|
557
|
-
init_db();
|
|
558
|
-
init_embeddings();
|
|
559
|
-
init_write();
|
|
560
|
-
|
|
561
576
|
// src/memory/read.ts
|
|
562
|
-
init_embeddings();
|
|
563
|
-
init_db();
|
|
564
577
|
import { readFileSync as readFileSync3, readdirSync, existsSync as existsSync4 } from "fs";
|
|
565
578
|
import { join as join5, relative } from "path";
|
|
566
579
|
import matter2 from "gray-matter";
|
|
580
|
+
init_db();
|
|
567
581
|
async function findMemories(query, limit = 5, workspaceRoot) {
|
|
568
582
|
const db = getDatabase(workspaceRoot);
|
|
569
583
|
try {
|
|
@@ -682,49 +696,52 @@ function splitTiers(content) {
|
|
|
682
696
|
import { writeFileSync as writeFileSync3 } from "fs";
|
|
683
697
|
import { join as join6 } from "path";
|
|
684
698
|
import { encoding_for_model } from "tiktoken";
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
} catch {
|
|
691
|
-
return { encode: (text) => new Uint32Array(Math.ceil(text.length / 4)) };
|
|
692
|
-
}
|
|
699
|
+
function createEncoder() {
|
|
700
|
+
try {
|
|
701
|
+
return encoding_for_model("gpt-4o-mini");
|
|
702
|
+
} catch {
|
|
703
|
+
return null;
|
|
693
704
|
}
|
|
694
|
-
return cachedEncoder;
|
|
695
705
|
}
|
|
696
706
|
async function compile(workspaceRoot, config) {
|
|
697
|
-
const
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
const
|
|
712
|
-
const
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
707
|
+
const encoder = createEncoder();
|
|
708
|
+
try {
|
|
709
|
+
const allEntries = loadAllEntries(workspaceRoot);
|
|
710
|
+
const recentFiles = await getRecentlyModifiedFiles(workspaceRoot, 7);
|
|
711
|
+
let tokenCount = 0;
|
|
712
|
+
const sections = [];
|
|
713
|
+
const systemPrompt = buildSystemPrompt();
|
|
714
|
+
sections.push(systemPrompt);
|
|
715
|
+
tokenCount += estimateTokens(systemPrompt, encoder);
|
|
716
|
+
const verifiedEntries = allEntries.filter((e) => e.verified && !e.stale);
|
|
717
|
+
const l0Section = buildL0Index(verifiedEntries);
|
|
718
|
+
sections.push(l0Section);
|
|
719
|
+
tokenCount += estimateTokens(l0Section, encoder);
|
|
720
|
+
const relevant = verifiedEntries.filter((e) => e.global || isRecentlyTouched(e, recentFiles) || isRecentlyCreated(e, 7)).sort((a, b) => b.ref_count - a.ref_count);
|
|
721
|
+
const includedUris = new Set;
|
|
722
|
+
for (const entry of relevant) {
|
|
723
|
+
const l1Content = formatL1Section(entry);
|
|
724
|
+
const tokens = estimateTokens(l1Content, encoder);
|
|
725
|
+
if (tokenCount + tokens > config.compile.tokenBudget)
|
|
726
|
+
break;
|
|
727
|
+
sections.push(l1Content);
|
|
728
|
+
tokenCount += tokens;
|
|
729
|
+
includedUris.add(entry.uri);
|
|
730
|
+
}
|
|
731
|
+
const l2Available = allEntries.filter((e) => !e.stale && !includedUris.has(e.uri));
|
|
732
|
+
if (l2Available.length > 0)
|
|
733
|
+
sections.push(buildL2Footer(l2Available));
|
|
734
|
+
const output = sections.join(`
|
|
723
735
|
|
|
724
736
|
---
|
|
725
737
|
|
|
726
738
|
`);
|
|
727
|
-
|
|
739
|
+
writeFileSync3(join6(workspaceRoot, ".context", "KONTEX.md"), output, "utf-8");
|
|
740
|
+
} finally {
|
|
741
|
+
try {
|
|
742
|
+
encoder?.free();
|
|
743
|
+
} catch {}
|
|
744
|
+
}
|
|
728
745
|
}
|
|
729
746
|
function buildSystemPrompt() {
|
|
730
747
|
return `## Project memory (kontex)
|
|
@@ -796,12 +813,12 @@ async function getRecentlyModifiedFiles(workspaceRoot, days) {
|
|
|
796
813
|
return [];
|
|
797
814
|
}
|
|
798
815
|
}
|
|
799
|
-
function estimateTokens(text) {
|
|
816
|
+
function estimateTokens(text, encoder) {
|
|
800
817
|
try {
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
818
|
+
if (encoder)
|
|
819
|
+
return encoder.encode(text).length;
|
|
820
|
+
} catch {}
|
|
821
|
+
return Math.ceil(text.length / 4);
|
|
805
822
|
}
|
|
806
823
|
// src/memory/decay.ts
|
|
807
824
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, readdirSync as readdirSync2, statSync, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
@@ -864,12 +881,14 @@ async function archiveSessions(memoryDir, archiveDays, result) {
|
|
|
864
881
|
const filePath = join7(sessionsDir, file.name);
|
|
865
882
|
try {
|
|
866
883
|
if (statSync(filePath).mtime < cutoff) {
|
|
867
|
-
|
|
884
|
+
const archiveDest = join7(archiveDir, file.name);
|
|
885
|
+
const finalDest = existsSync5(archiveDest) ? join7(archiveDir, file.name.replace(".md", `-${Date.now()}.md`)) : archiveDest;
|
|
886
|
+
writeFileSync4(finalDest, readFileSync4(filePath, "utf-8"), "utf-8");
|
|
868
887
|
writeFileSync4(filePath, `---
|
|
869
888
|
archived: true
|
|
870
889
|
archived_at: ${new Date().toISOString()}
|
|
871
890
|
---
|
|
872
|
-
Archived to archive/${
|
|
891
|
+
Archived to archive/${basename(finalDest)}
|
|
873
892
|
`, "utf-8");
|
|
874
893
|
result.archived.push(file.name);
|
|
875
894
|
}
|
|
@@ -908,7 +927,9 @@ async function enforceSessionsSizeCap(memoryDir, maxSizeKB, result) {
|
|
|
908
927
|
if (totalKB <= maxSizeKB)
|
|
909
928
|
break;
|
|
910
929
|
totalKB -= statSync(file.path).size / 1024;
|
|
911
|
-
|
|
930
|
+
const archiveDest = join7(archiveDir, file.name);
|
|
931
|
+
const finalDest = existsSync5(archiveDest) ? join7(archiveDir, file.name.replace(".md", `-${Date.now()}.md`)) : archiveDest;
|
|
932
|
+
writeFileSync4(finalDest, readFileSync4(file.path, "utf-8"), "utf-8");
|
|
912
933
|
writeFileSync4(file.path, `---
|
|
913
934
|
archived: true
|
|
914
935
|
---
|
|
@@ -1009,7 +1030,6 @@ Do not guess what memory contains \u2014 search it.`,
|
|
|
1009
1030
|
];
|
|
1010
1031
|
|
|
1011
1032
|
// src/mcp/handlers.ts
|
|
1012
|
-
init_write();
|
|
1013
1033
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync6 } from "fs";
|
|
1014
1034
|
import { join as join8 } from "path";
|
|
1015
1035
|
import matter4 from "gray-matter";
|
|
@@ -1154,7 +1174,6 @@ function findAffectedMemories(stagedFiles, workspaceRoot) {
|
|
|
1154
1174
|
import { writeFileSync as writeFileSync7, existsSync as existsSync8, unlinkSync, mkdirSync as mkdirSync4, appendFileSync as appendFileSync2 } from "fs";
|
|
1155
1175
|
import { join as join10, dirname as dirname2 } from "path";
|
|
1156
1176
|
import { createHash } from "crypto";
|
|
1157
|
-
init_write();
|
|
1158
1177
|
async function handlePostCommit(commitSha, authorEmail, workspaceRoot) {
|
|
1159
1178
|
const config = loadConfig(workspaceRoot);
|
|
1160
1179
|
const logPath = join10(workspaceRoot, ".kontex-log", "hooks.log");
|
|
@@ -1184,10 +1203,8 @@ async function handlePostCommit(commitSha, authorEmail, workspaceRoot) {
|
|
|
1184
1203
|
await writeMemory({ content: m.content, type: m.type, why_memorable: m.why_memorable, confidence: m.confidence, affected_paths: m.affected_paths }, workspaceRoot, config);
|
|
1185
1204
|
}
|
|
1186
1205
|
await writeSessionFile(commitSha, authorEmail, extraction, workspaceRoot);
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
for (const uri of extraction.stale_uris)
|
|
1190
|
-
await invalidateMemory2(uri, `Flagged stale by commit ${commitSha.slice(0, 7)}`, workspaceRoot);
|
|
1206
|
+
for (const uri of extraction.stale_uris) {
|
|
1207
|
+
await invalidateMemory(uri, `Flagged stale by commit ${commitSha.slice(0, 7)}`, workspaceRoot);
|
|
1191
1208
|
}
|
|
1192
1209
|
}
|
|
1193
1210
|
await compileAndCommit(workspaceRoot, config);
|
|
@@ -1273,6 +1290,10 @@ async function getCommitDiff(sha, workspaceRoot) {
|
|
|
1273
1290
|
return "";
|
|
1274
1291
|
}
|
|
1275
1292
|
}
|
|
1293
|
+
function isGitOperationInProgress(workspaceRoot) {
|
|
1294
|
+
const gitDir = join10(workspaceRoot, ".git");
|
|
1295
|
+
return ["REBASE_HEAD", "MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "BISECT_LOG"].some((f) => existsSync8(join10(gitDir, f)));
|
|
1296
|
+
}
|
|
1276
1297
|
async function compileAndCommit(workspaceRoot, config) {
|
|
1277
1298
|
if (!existsSync8(join10(workspaceRoot, ".context")))
|
|
1278
1299
|
return;
|
|
@@ -1280,8 +1301,12 @@ async function compileAndCommit(workspaceRoot, config) {
|
|
|
1280
1301
|
try {
|
|
1281
1302
|
Bun.spawnSync(["git", "add", ".context/"], { cwd: workspaceRoot });
|
|
1282
1303
|
const status = Bun.spawnSync(["git", "diff", "--cached", "--quiet", ".context/"], { cwd: workspaceRoot });
|
|
1283
|
-
if (status.exitCode !== 0)
|
|
1304
|
+
if (status.exitCode !== 0) {
|
|
1305
|
+
if (isGitOperationInProgress(workspaceRoot)) {
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1284
1308
|
Bun.spawnSync(["git", "commit", "--no-verify", "-m", "chore(kontex): update memory [skip ci]"], { cwd: workspaceRoot });
|
|
1309
|
+
}
|
|
1285
1310
|
} catch {}
|
|
1286
1311
|
}
|
|
1287
1312
|
function logHookEvent(logPath, event, message) {
|
|
@@ -1295,7 +1320,6 @@ function logHookEvent(logPath, event, message) {
|
|
|
1295
1320
|
import { existsSync as existsSync9 } from "fs";
|
|
1296
1321
|
import { join as join11 } from "path";
|
|
1297
1322
|
init_db();
|
|
1298
|
-
init_embeddings();
|
|
1299
1323
|
async function handlePostMerge(workspaceRoot) {
|
|
1300
1324
|
if (!existsSync9(join11(workspaceRoot, ".context")))
|
|
1301
1325
|
return;
|
|
@@ -1323,10 +1347,8 @@ async function rebuildIndex(workspaceRoot) {
|
|
|
1323
1347
|
import { existsSync as existsSync11, readFileSync as readFileSync7, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5, appendFileSync as appendFileSync3, chmodSync } from "fs";
|
|
1324
1348
|
import { join as join13, resolve } from "path";
|
|
1325
1349
|
import { homedir as homedir2 } from "os";
|
|
1326
|
-
init_embeddings();
|
|
1327
1350
|
|
|
1328
1351
|
// src/memory/extract.ts
|
|
1329
|
-
init_write();
|
|
1330
1352
|
import { readFileSync as readFileSync6, existsSync as existsSync10 } from "fs";
|
|
1331
1353
|
import { join as join12 } from "path";
|
|
1332
1354
|
async function runInitAI(workspaceRoot) {
|
|
@@ -1619,7 +1641,7 @@ function registerInAITools(root) {
|
|
|
1619
1641
|
const home = homedir2();
|
|
1620
1642
|
const entry = { command: "bunx", args: ["kontex", "mcp"], env: { KONTEX_WORKSPACE: root } };
|
|
1621
1643
|
const tools = [
|
|
1622
|
-
{ name: "Claude Code", configPath: join13(home, ".claude
|
|
1644
|
+
{ name: "Claude Code", configPath: join13(home, ".claude.json"), key: "mcpServers" },
|
|
1623
1645
|
{ name: "Cursor", configPath: join13(root, ".cursor", "mcp.json"), key: "mcpServers" },
|
|
1624
1646
|
{ name: "Windsurf", configPath: join13(home, ".codeium", "windsurf", "mcp_config.json"), key: "mcpServers" },
|
|
1625
1647
|
{ name: "Zed", configPath: join13(home, ".config", "zed", "settings.json"), key: "mcpServers" }
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kontex-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Core engine for kontex — quality gate, MCP server, compilation, embedding, storage, hooks, auth",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"engines": {
|
|
8
|
+
"bun": ">=1.1.0"
|
|
9
|
+
},
|
|
7
10
|
"repository": {
|
|
8
11
|
"type": "git",
|
|
9
12
|
"url": "git+https://github.com/ArekBee/kontex.git",
|