kontexta-mcp 2.0.7 → 2.0.10
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/agent-rules/rules-block.md +1 -0
- package/dist/index.js +257 -184
- package/package.json +3 -3
|
@@ -161,4 +161,5 @@ The matrix below is grouped by intent. For each tool: when to reach for it, the
|
|
|
161
161
|
|---|---|---|---|
|
|
162
162
|
| `register_project` | Register a new project root with kontexta | Already registered | `list_projects` |
|
|
163
163
|
| `onboard_agent` | Write/update the rules block in CLAUDE.md / AGENTS.md / etc. | Editing regular project content | `update_file` |
|
|
164
|
+
| `transfer_agent_context` | Copy CLAUDE.md / AGENTS.md / `.cursor/rules/*.mdc` etc. from the project repo into Kontexta's KB. Originals are NEVER deleted by this tool. | User wants files to stay in the repo (default) | `register_project` (which keeps files in place) |
|
|
164
165
|
<!-- END kontexta:rules v{{VERSION}} -->
|
package/dist/index.js
CHANGED
|
@@ -57,6 +57,14 @@ function defaultDataDir() {
|
|
|
57
57
|
function isWebContext() {
|
|
58
58
|
return process.env.npm_package_name === "kxta-web" || !!process.env.NEXT_RUNTIME || !!process.env.__NEXT_PAGES_DIR;
|
|
59
59
|
}
|
|
60
|
+
function isTempOrTestPath(p) {
|
|
61
|
+
const lower = p.toLowerCase();
|
|
62
|
+
const tmp = os.tmpdir().toLowerCase();
|
|
63
|
+
return lower.startsWith("/tmp") || lower.startsWith(tmp) || lower.includes(`${path.sep}tmp${path.sep}`) || lower.includes("test") || lower.includes("-tmp-") || process.platform === "win32" && lower.includes("\\temp\\");
|
|
64
|
+
}
|
|
65
|
+
function resetDataDirCache() {
|
|
66
|
+
_resolvedDataDir = null;
|
|
67
|
+
}
|
|
60
68
|
function getDataDir() {
|
|
61
69
|
if (_resolvedDataDir)
|
|
62
70
|
return _resolvedDataDir;
|
|
@@ -66,31 +74,40 @@ function getDataDir() {
|
|
|
66
74
|
const isWeb = isWebContext();
|
|
67
75
|
if (isWeb && envOverride) {
|
|
68
76
|
_resolvedDataDir = path.resolve(envOverride);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
77
|
+
if (!isTempOrTestPath(_resolvedDataDir)) {
|
|
78
|
+
try {
|
|
79
|
+
fs.writeFileSync(cacheFile, _resolvedDataDir, "utf8");
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return _resolvedDataDir;
|
|
84
|
+
}
|
|
85
|
+
if (envOverride) {
|
|
86
|
+
_resolvedDataDir = path.resolve(envOverride);
|
|
87
|
+
const isTestEnv2 = !!process.env.VITEST || process.env.NODE_ENV === "test";
|
|
88
|
+
if (!isTempOrTestPath(_resolvedDataDir) && !isTestEnv2) {
|
|
89
|
+
try {
|
|
90
|
+
fs.writeFileSync(cacheFile, _resolvedDataDir, "utf8");
|
|
91
|
+
} catch {
|
|
92
|
+
}
|
|
72
93
|
}
|
|
73
94
|
return _resolvedDataDir;
|
|
74
95
|
}
|
|
75
|
-
const
|
|
76
|
-
if (!
|
|
96
|
+
const isTestEnv = !!process.env.VITEST || process.env.NODE_ENV === "test";
|
|
97
|
+
if (!isTestEnv && fs.existsSync(cacheFile)) {
|
|
77
98
|
try {
|
|
78
99
|
const cached = fs.readFileSync(cacheFile, "utf8").trim();
|
|
79
|
-
if (cached && path.isAbsolute(cached)) {
|
|
100
|
+
if (cached && path.isAbsolute(cached) && !isTempOrTestPath(cached)) {
|
|
80
101
|
_resolvedDataDir = cached;
|
|
81
102
|
return _resolvedDataDir;
|
|
82
103
|
}
|
|
104
|
+
try {
|
|
105
|
+
fs.unlinkSync(cacheFile);
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
83
108
|
} catch {
|
|
84
109
|
}
|
|
85
110
|
}
|
|
86
|
-
if (envOverride) {
|
|
87
|
-
_resolvedDataDir = path.resolve(envOverride);
|
|
88
|
-
try {
|
|
89
|
-
fs.writeFileSync(cacheFile, _resolvedDataDir, "utf8");
|
|
90
|
-
} catch {
|
|
91
|
-
}
|
|
92
|
-
return _resolvedDataDir;
|
|
93
|
-
}
|
|
94
111
|
_resolvedDataDir = defaultDataDir();
|
|
95
112
|
return _resolvedDataDir;
|
|
96
113
|
}
|
|
@@ -149,7 +166,7 @@ __export(safety_exports, {
|
|
|
149
166
|
track: () => track,
|
|
150
167
|
withLock: () => withLock
|
|
151
168
|
});
|
|
152
|
-
import { resolve as resolve2, sep, isAbsolute as isAbsolute2 } from "path";
|
|
169
|
+
import { resolve as resolve2, sep as sep2, isAbsolute as isAbsolute2 } from "path";
|
|
153
170
|
import { AsyncLocalStorage } from "async_hooks";
|
|
154
171
|
function assertPathInside(base3, name50) {
|
|
155
172
|
if (typeof name50 !== "string" || name50.length === 0) {
|
|
@@ -163,7 +180,7 @@ function assertPathInside(base3, name50) {
|
|
|
163
180
|
}
|
|
164
181
|
const baseResolved = resolve2(base3);
|
|
165
182
|
const target = resolve2(baseResolved, name50);
|
|
166
|
-
if (target !== baseResolved && !target.startsWith(baseResolved +
|
|
183
|
+
if (target !== baseResolved && !target.startsWith(baseResolved + sep2)) {
|
|
167
184
|
throw new Error("Invalid path: escapes base directory");
|
|
168
185
|
}
|
|
169
186
|
return target;
|
|
@@ -336,6 +353,10 @@ function getDatabase() {
|
|
|
336
353
|
}
|
|
337
354
|
function closeDatabase() {
|
|
338
355
|
if (db) {
|
|
356
|
+
try {
|
|
357
|
+
db.pragma("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
358
|
+
} catch {
|
|
359
|
+
}
|
|
339
360
|
db.close();
|
|
340
361
|
db = null;
|
|
341
362
|
globalThis.__kontextaDb = null;
|
|
@@ -416,8 +437,8 @@ var init_extensions = __esm({
|
|
|
416
437
|
// ../../packages/core/dist/git/index.js
|
|
417
438
|
import { createHash } from "crypto";
|
|
418
439
|
import simpleGit from "simple-git";
|
|
419
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, copyFileSync, mkdirSync as mkdirSync3, existsSync as existsSync3, rmSync, readdirSync as readdirSync2, statSync, lstatSync, unlinkSync, renameSync } from "fs";
|
|
420
|
-
import { join as join3, relative, dirname as dirname2, isAbsolute as isAbsolute3, resolve as resolve3, sep as
|
|
440
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, copyFileSync, mkdirSync as mkdirSync3, existsSync as existsSync3, rmSync, readdirSync as readdirSync2, statSync, lstatSync, unlinkSync as unlinkSync2, renameSync } from "fs";
|
|
441
|
+
import { join as join3, relative, dirname as dirname2, isAbsolute as isAbsolute3, resolve as resolve3, sep as sep3 } from "path";
|
|
421
442
|
import { spawnSync } from "child_process";
|
|
422
443
|
function redactCredentials(s3) {
|
|
423
444
|
return s3.replace(/([a-z][a-z0-9+.-]*:\/\/)([^:@\/\s]+:[^@\/\s]+)@/gi, "$1***:***@");
|
|
@@ -536,7 +557,7 @@ async function restoreVersion(repoDir, filePath, commitHash) {
|
|
|
536
557
|
renameSync(tmpPath, filePath);
|
|
537
558
|
} catch (e3) {
|
|
538
559
|
try {
|
|
539
|
-
|
|
560
|
+
unlinkSync2(tmpPath);
|
|
540
561
|
} catch {
|
|
541
562
|
}
|
|
542
563
|
throw e3;
|
|
@@ -814,7 +835,7 @@ async function _syncBackupLocked(projectId, dataDir2, onStage) {
|
|
|
814
835
|
}
|
|
815
836
|
} else if (!expectedBackupPaths.has(fullPath)) {
|
|
816
837
|
try {
|
|
817
|
-
|
|
838
|
+
unlinkSync2(fullPath);
|
|
818
839
|
} catch {
|
|
819
840
|
}
|
|
820
841
|
}
|
|
@@ -884,7 +905,7 @@ async function _syncBackupLocked(projectId, dataDir2, onStage) {
|
|
|
884
905
|
if (project.path) {
|
|
885
906
|
const localAbsolutePath = join3(project.path, relativePath);
|
|
886
907
|
const projectResolved = resolve3(project.path);
|
|
887
|
-
if (resolve3(localAbsolutePath) !== projectResolved && !resolve3(localAbsolutePath).startsWith(projectResolved +
|
|
908
|
+
if (resolve3(localAbsolutePath) !== projectResolved && !resolve3(localAbsolutePath).startsWith(projectResolved + sep3)) {
|
|
888
909
|
continue;
|
|
889
910
|
}
|
|
890
911
|
mkdirSync3(dirname2(localAbsolutePath), { recursive: true });
|
|
@@ -950,12 +971,12 @@ async function _syncBackupLocked(projectId, dataDir2, onStage) {
|
|
|
950
971
|
continue;
|
|
951
972
|
const localAbsolutePath = join3(project.path, insideBackup);
|
|
952
973
|
const resolvedLocal = resolve3(localAbsolutePath);
|
|
953
|
-
if (resolvedLocal !== projectResolved && !resolvedLocal.startsWith(projectResolved +
|
|
974
|
+
if (resolvedLocal !== projectResolved && !resolvedLocal.startsWith(projectResolved + sep3)) {
|
|
954
975
|
continue;
|
|
955
976
|
}
|
|
956
977
|
try {
|
|
957
978
|
if (existsSync3(localAbsolutePath)) {
|
|
958
|
-
|
|
979
|
+
unlinkSync2(localAbsolutePath);
|
|
959
980
|
}
|
|
960
981
|
} catch (e3) {
|
|
961
982
|
console.warn(`syncBackup: failed to remove ${localAbsolutePath}:`, e3);
|
|
@@ -997,8 +1018,8 @@ var init_git = __esm({
|
|
|
997
1018
|
|
|
998
1019
|
// ../../packages/core/dist/files/index.js
|
|
999
1020
|
import { createHash as createHash2 } from "crypto";
|
|
1000
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, unlinkSync as
|
|
1001
|
-
import { join as join4, dirname as dirname3, resolve as resolve4, sep as
|
|
1021
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, unlinkSync as unlinkSync3, renameSync as renameSync2, mkdirSync as mkdirSync4, readdirSync as readdirSync3, lstatSync as lstatSync2, existsSync as existsSync4, rmSync as rmSync2 } from "fs";
|
|
1022
|
+
import { join as join4, dirname as dirname3, resolve as resolve4, sep as sep4, isAbsolute as isAbsolute4 } from "path";
|
|
1002
1023
|
function listProjectFolders(projectPath) {
|
|
1003
1024
|
const folders = [];
|
|
1004
1025
|
function scan(dir, currentRel) {
|
|
@@ -1133,7 +1154,7 @@ async function createFile(opts) {
|
|
|
1133
1154
|
}
|
|
1134
1155
|
} else {
|
|
1135
1156
|
try {
|
|
1136
|
-
|
|
1157
|
+
unlinkSync3(filePath);
|
|
1137
1158
|
} catch {
|
|
1138
1159
|
}
|
|
1139
1160
|
}
|
|
@@ -1251,11 +1272,11 @@ function deleteFile(id, dataDir2) {
|
|
|
1251
1272
|
if (file && dataDir2) {
|
|
1252
1273
|
const knowledgeRoot = resolve4(dataDir2, "knowledge");
|
|
1253
1274
|
const filePathResolved = isAbsolute4(file.path) ? file.path : resolve4(dataDir2, file.path);
|
|
1254
|
-
const inKnowledge = filePathResolved === knowledgeRoot || filePathResolved.startsWith(knowledgeRoot +
|
|
1275
|
+
const inKnowledge = filePathResolved === knowledgeRoot || filePathResolved.startsWith(knowledgeRoot + sep4);
|
|
1255
1276
|
if (file.project_id === null && inKnowledge) {
|
|
1256
1277
|
try {
|
|
1257
1278
|
if (existsSync4(filePathResolved)) {
|
|
1258
|
-
|
|
1279
|
+
unlinkSync3(filePathResolved);
|
|
1259
1280
|
}
|
|
1260
1281
|
} catch (e3) {
|
|
1261
1282
|
console.error("Failed to delete Knowledge Base file from disk:", e3);
|
|
@@ -1858,8 +1879,8 @@ var init_settings = __esm({
|
|
|
1858
1879
|
});
|
|
1859
1880
|
|
|
1860
1881
|
// ../../packages/core/dist/agent-rules/index.js
|
|
1861
|
-
import { lstatSync as lstatSync4, readdirSync as readdirSync5, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4, readFileSync as readFileSync6, renameSync as renameSync3, unlinkSync as
|
|
1862
|
-
import { join as join6, sep as
|
|
1882
|
+
import { lstatSync as lstatSync4, readdirSync as readdirSync5, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4, readFileSync as readFileSync6, renameSync as renameSync3, unlinkSync as unlinkSync4, existsSync as existsSync6 } from "fs";
|
|
1883
|
+
import { join as join6, sep as sep5, dirname as dirname4, isAbsolute as isAbsolute5 } from "path";
|
|
1863
1884
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1864
1885
|
function loadCorePackageVersion() {
|
|
1865
1886
|
try {
|
|
@@ -1922,7 +1943,7 @@ function isRegularNonSymlink(absPath) {
|
|
|
1922
1943
|
}
|
|
1923
1944
|
}
|
|
1924
1945
|
function toForwardSlashes(p) {
|
|
1925
|
-
return
|
|
1946
|
+
return sep5 === "/" ? p : p.split(sep5).join("/");
|
|
1926
1947
|
}
|
|
1927
1948
|
function detectAgentContextFiles(projectPath) {
|
|
1928
1949
|
const out = [];
|
|
@@ -1971,8 +1992,8 @@ function parseMarker(content) {
|
|
|
1971
1992
|
function injectOrUpdate(content, block, version2) {
|
|
1972
1993
|
const parsed = parseMarker(content);
|
|
1973
1994
|
if (parsed === null) {
|
|
1974
|
-
const
|
|
1975
|
-
return { action: "updated", content: content +
|
|
1995
|
+
const sep10 = content.length === 0 ? "" : content.endsWith("\n\n") ? "" : content.endsWith("\n") ? "\n" : "\n\n";
|
|
1996
|
+
return { action: "updated", content: content + sep10 + block };
|
|
1976
1997
|
}
|
|
1977
1998
|
if (parsed.kind === "malformed") {
|
|
1978
1999
|
throw new InjectError("malformed", "kontexta rules marker is malformed (BEGIN with no matching END)");
|
|
@@ -1995,7 +2016,7 @@ function atomicWrite(absPath, content) {
|
|
|
1995
2016
|
renameSync3(tmp, absPath);
|
|
1996
2017
|
} catch (e3) {
|
|
1997
2018
|
try {
|
|
1998
|
-
|
|
2019
|
+
unlinkSync4(tmp);
|
|
1999
2020
|
} catch {
|
|
2000
2021
|
}
|
|
2001
2022
|
throw e3;
|
|
@@ -137499,7 +137520,7 @@ var require_util7 = __commonJS({
|
|
|
137499
137520
|
return path3;
|
|
137500
137521
|
});
|
|
137501
137522
|
exports.normalize = normalize3;
|
|
137502
|
-
function
|
|
137523
|
+
function join17(aRoot, aPath) {
|
|
137503
137524
|
if (aRoot === "") {
|
|
137504
137525
|
aRoot = ".";
|
|
137505
137526
|
}
|
|
@@ -137531,7 +137552,7 @@ var require_util7 = __commonJS({
|
|
|
137531
137552
|
}
|
|
137532
137553
|
return joined;
|
|
137533
137554
|
}
|
|
137534
|
-
exports.join =
|
|
137555
|
+
exports.join = join17;
|
|
137535
137556
|
exports.isAbsolute = function(aPath) {
|
|
137536
137557
|
return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
|
|
137537
137558
|
};
|
|
@@ -137745,7 +137766,7 @@ var require_util7 = __commonJS({
|
|
|
137745
137766
|
parsed.path = parsed.path.substring(0, index + 1);
|
|
137746
137767
|
}
|
|
137747
137768
|
}
|
|
137748
|
-
sourceURL =
|
|
137769
|
+
sourceURL = join17(urlGenerate(parsed), sourceURL);
|
|
137749
137770
|
}
|
|
137750
137771
|
return normalize3(sourceURL);
|
|
137751
137772
|
}
|
|
@@ -329703,17 +329724,17 @@ var init_whats_new = __esm({
|
|
|
329703
329724
|
});
|
|
329704
329725
|
|
|
329705
329726
|
// ../../packages/core/dist/project-map/index.js
|
|
329706
|
-
import { sep as
|
|
329727
|
+
import { sep as sep6, join as join8, basename as basename2 } from "path";
|
|
329707
329728
|
function emptyNode() {
|
|
329708
329729
|
return { dirs: /* @__PURE__ */ new Map(), files: [] };
|
|
329709
329730
|
}
|
|
329710
329731
|
function stripRoot(filePath, root2) {
|
|
329711
|
-
const rootWithSep = root2.endsWith(
|
|
329732
|
+
const rootWithSep = root2.endsWith(sep6) ? root2 : root2 + sep6;
|
|
329712
329733
|
if (filePath === root2)
|
|
329713
329734
|
return [];
|
|
329714
329735
|
if (!filePath.startsWith(rootWithSep))
|
|
329715
329736
|
return null;
|
|
329716
|
-
return filePath.slice(rootWithSep.length).split(
|
|
329737
|
+
return filePath.slice(rootWithSep.length).split(sep6).filter(Boolean);
|
|
329717
329738
|
}
|
|
329718
329739
|
function locateFile(file, projectsById, dataDir2) {
|
|
329719
329740
|
if (file.project_id == null) {
|
|
@@ -329856,73 +329877,6 @@ var init_project_map = __esm({
|
|
|
329856
329877
|
}
|
|
329857
329878
|
});
|
|
329858
329879
|
|
|
329859
|
-
// ../../packages/core/dist/compat/env-shim.js
|
|
329860
|
-
function migrateEnvVars() {
|
|
329861
|
-
const migrated = [];
|
|
329862
|
-
for (const [oldKey, newKey] of RENAMED_VARS) {
|
|
329863
|
-
if (process.env[oldKey] && !process.env[newKey]) {
|
|
329864
|
-
process.env[newKey] = process.env[oldKey];
|
|
329865
|
-
migrated.push(oldKey);
|
|
329866
|
-
}
|
|
329867
|
-
}
|
|
329868
|
-
if (migrated.length > 0) {
|
|
329869
|
-
console.warn(`[Kontexta] Deprecated env vars migrated: ${migrated.join(", ")}. Rename to KONTEXTA_* prefix. Support removed in v2.0.`);
|
|
329870
|
-
}
|
|
329871
|
-
return migrated;
|
|
329872
|
-
}
|
|
329873
|
-
var RENAMED_VARS;
|
|
329874
|
-
var init_env_shim = __esm({
|
|
329875
|
-
"../../packages/core/dist/compat/env-shim.js"() {
|
|
329876
|
-
"use strict";
|
|
329877
|
-
RENAMED_VARS = [
|
|
329878
|
-
["MNEXIS_DATA_DIR", "KONTEXTA_DATA_DIR"],
|
|
329879
|
-
["MNEXIS_DB_PATH", "KONTEXTA_DB_PATH"],
|
|
329880
|
-
["MNEXIS_WS_HOST", "KONTEXTA_WS_HOST"],
|
|
329881
|
-
["MNEXIS_WS_ORIGINS", "KONTEXTA_WS_ORIGINS"],
|
|
329882
|
-
["MNEXIS_WS_TOKEN", "KONTEXTA_WS_TOKEN"],
|
|
329883
|
-
["MNEXIS_EXPORT_MAX_BYTES", "KONTEXTA_EXPORT_MAX_BYTES"],
|
|
329884
|
-
["MNEXIS_INSTALL_HINT", "KONTEXTA_INSTALL_HINT"],
|
|
329885
|
-
["MNEXIS_PROJECT_TOKEN_WARN", "KONTEXTA_PROJECT_TOKEN_WARN"],
|
|
329886
|
-
["MNEXIS_SHUTDOWN_DRAIN_MS", "KONTEXTA_SHUTDOWN_DRAIN_MS"]
|
|
329887
|
-
];
|
|
329888
|
-
}
|
|
329889
|
-
});
|
|
329890
|
-
|
|
329891
|
-
// ../../packages/core/dist/compat/file-migration.js
|
|
329892
|
-
import { existsSync as existsSync7, renameSync as renameSync4, copyFileSync as copyFileSync2 } from "fs";
|
|
329893
|
-
import { join as join9 } from "path";
|
|
329894
|
-
function migrateDataFiles(dataDir2) {
|
|
329895
|
-
for (const [oldName, newName] of DB_RENAMES) {
|
|
329896
|
-
const oldPath = join9(dataDir2, oldName);
|
|
329897
|
-
const newPath = join9(dataDir2, newName);
|
|
329898
|
-
if (existsSync7(oldPath) && !existsSync7(newPath)) {
|
|
329899
|
-
copyFileSync2(oldPath, oldPath + ".bak");
|
|
329900
|
-
renameSync4(oldPath, newPath);
|
|
329901
|
-
console.warn(`[Kontexta] Migrated ${oldName} \u2192 ${newName} (backup: ${oldName}.bak). Automatic migration removed in v2.0.`);
|
|
329902
|
-
}
|
|
329903
|
-
}
|
|
329904
|
-
}
|
|
329905
|
-
function migrateProjectConfig(projectRoot) {
|
|
329906
|
-
const oldConfig = join9(projectRoot, "mnexis.json");
|
|
329907
|
-
const newConfig = join9(projectRoot, "kontexta.json");
|
|
329908
|
-
if (existsSync7(oldConfig) && !existsSync7(newConfig)) {
|
|
329909
|
-
copyFileSync2(oldConfig, oldConfig + ".bak");
|
|
329910
|
-
renameSync4(oldConfig, newConfig);
|
|
329911
|
-
console.warn(`[Kontexta] Migrated mnexis.json \u2192 kontexta.json in ${projectRoot} (backup: mnexis.json.bak). Automatic migration removed in v2.0.`);
|
|
329912
|
-
}
|
|
329913
|
-
}
|
|
329914
|
-
var DB_RENAMES;
|
|
329915
|
-
var init_file_migration = __esm({
|
|
329916
|
-
"../../packages/core/dist/compat/file-migration.js"() {
|
|
329917
|
-
"use strict";
|
|
329918
|
-
DB_RENAMES = [
|
|
329919
|
-
["mnexis.db", "kontexta.db"],
|
|
329920
|
-
["mnexis.db-wal", "kontexta.db-wal"],
|
|
329921
|
-
["mnexis.db-shm", "kontexta.db-shm"]
|
|
329922
|
-
];
|
|
329923
|
-
}
|
|
329924
|
-
});
|
|
329925
|
-
|
|
329926
329880
|
// ../../packages/core/dist/journal/types.js
|
|
329927
329881
|
var init_types2 = __esm({
|
|
329928
329882
|
"../../packages/core/dist/journal/types.js"() {
|
|
@@ -329932,7 +329886,7 @@ var init_types2 = __esm({
|
|
|
329932
329886
|
|
|
329933
329887
|
// ../../packages/core/dist/journal/writer.js
|
|
329934
329888
|
import { mkdirSync as mkdirSync6, openSync, closeSync, writeSync, fsyncSync } from "fs";
|
|
329935
|
-
import { join as
|
|
329889
|
+
import { join as join9, dirname as dirname5 } from "path";
|
|
329936
329890
|
var JournalWriter;
|
|
329937
329891
|
var init_writer = __esm({
|
|
329938
329892
|
"../../packages/core/dist/journal/writer.js"() {
|
|
@@ -329942,7 +329896,7 @@ var init_writer = __esm({
|
|
|
329942
329896
|
currentDay = null;
|
|
329943
329897
|
rawDir;
|
|
329944
329898
|
constructor(opts) {
|
|
329945
|
-
this.rawDir =
|
|
329899
|
+
this.rawDir = join9(opts.baseDir, opts.projectSlug, "raw");
|
|
329946
329900
|
}
|
|
329947
329901
|
append(event) {
|
|
329948
329902
|
const day = event.ts.slice(0, 10);
|
|
@@ -329964,7 +329918,7 @@ var init_writer = __esm({
|
|
|
329964
329918
|
closeSync(this.fd);
|
|
329965
329919
|
this.fd = null;
|
|
329966
329920
|
}
|
|
329967
|
-
const path3 =
|
|
329921
|
+
const path3 = join9(this.rawDir, `${day}.jsonl`);
|
|
329968
329922
|
mkdirSync6(dirname5(path3), { recursive: true });
|
|
329969
329923
|
this.fd = openSync(path3, "a");
|
|
329970
329924
|
this.currentDay = day;
|
|
@@ -330019,14 +329973,14 @@ var init_redact = __esm({
|
|
|
330019
329973
|
});
|
|
330020
329974
|
|
|
330021
329975
|
// ../../packages/core/dist/journal/high-water.js
|
|
330022
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, renameSync as
|
|
330023
|
-
import { join as
|
|
329976
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, renameSync as renameSync4, mkdirSync as mkdirSync7, existsSync as existsSync7 } from "fs";
|
|
329977
|
+
import { join as join10 } from "path";
|
|
330024
329978
|
function pathFor(baseDir, projectSlug2) {
|
|
330025
|
-
return
|
|
329979
|
+
return join10(baseDir, projectSlug2, ".distilled-up-to.json");
|
|
330026
329980
|
}
|
|
330027
329981
|
function readHighWater(baseDir, projectSlug2) {
|
|
330028
329982
|
const p = pathFor(baseDir, projectSlug2);
|
|
330029
|
-
if (!
|
|
329983
|
+
if (!existsSync7(p))
|
|
330030
329984
|
return null;
|
|
330031
329985
|
try {
|
|
330032
329986
|
const raw = readFileSync7(p, "utf8");
|
|
@@ -330040,12 +329994,12 @@ function readHighWater(baseDir, projectSlug2) {
|
|
|
330040
329994
|
}
|
|
330041
329995
|
}
|
|
330042
329996
|
function writeHighWater(baseDir, projectSlug2, hw) {
|
|
330043
|
-
const dir =
|
|
329997
|
+
const dir = join10(baseDir, projectSlug2);
|
|
330044
329998
|
mkdirSync7(dir, { recursive: true });
|
|
330045
329999
|
const final = pathFor(baseDir, projectSlug2);
|
|
330046
330000
|
const tmp = `${final}.tmp`;
|
|
330047
330001
|
writeFileSync5(tmp, JSON.stringify(hw, null, 2));
|
|
330048
|
-
|
|
330002
|
+
renameSync4(tmp, final);
|
|
330049
330003
|
}
|
|
330050
330004
|
var init_high_water = __esm({
|
|
330051
330005
|
"../../packages/core/dist/journal/high-water.js"() {
|
|
@@ -330665,12 +330619,12 @@ var init_repository = __esm({
|
|
|
330665
330619
|
});
|
|
330666
330620
|
|
|
330667
330621
|
// ../../packages/core/dist/journal/cooldown.js
|
|
330668
|
-
import { existsSync as
|
|
330669
|
-
import { join as
|
|
330622
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync6, unlinkSync as unlinkSync5, mkdirSync as mkdirSync8 } from "fs";
|
|
330623
|
+
import { join as join11 } from "path";
|
|
330670
330624
|
function acquireCooldown(baseDir, projectSlug2, cooldownSeconds) {
|
|
330671
|
-
mkdirSync8(
|
|
330672
|
-
const lockPath =
|
|
330673
|
-
if (
|
|
330625
|
+
mkdirSync8(join11(baseDir, projectSlug2), { recursive: true });
|
|
330626
|
+
const lockPath = join11(baseDir, projectSlug2, ".distill.lock");
|
|
330627
|
+
if (existsSync8(lockPath)) {
|
|
330674
330628
|
try {
|
|
330675
330629
|
const ts = Number(readFileSync8(lockPath, "utf8"));
|
|
330676
330630
|
if (!isNaN(ts) && Date.now() - ts < cooldownSeconds * 1e3)
|
|
@@ -330682,10 +330636,10 @@ function acquireCooldown(baseDir, projectSlug2, cooldownSeconds) {
|
|
|
330682
330636
|
return true;
|
|
330683
330637
|
}
|
|
330684
330638
|
function releaseCooldown(baseDir, projectSlug2) {
|
|
330685
|
-
const lockPath =
|
|
330686
|
-
if (
|
|
330639
|
+
const lockPath = join11(baseDir, projectSlug2, ".distill.lock");
|
|
330640
|
+
if (existsSync8(lockPath)) {
|
|
330687
330641
|
try {
|
|
330688
|
-
|
|
330642
|
+
unlinkSync5(lockPath);
|
|
330689
330643
|
} catch {
|
|
330690
330644
|
}
|
|
330691
330645
|
}
|
|
@@ -330697,16 +330651,16 @@ var init_cooldown = __esm({
|
|
|
330697
330651
|
});
|
|
330698
330652
|
|
|
330699
330653
|
// ../../packages/core/dist/journal/distill.js
|
|
330700
|
-
import { readFileSync as readFileSync9, readdirSync as readdirSync6, existsSync as
|
|
330701
|
-
import { join as
|
|
330654
|
+
import { readFileSync as readFileSync9, readdirSync as readdirSync6, existsSync as existsSync9, mkdirSync as mkdirSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
330655
|
+
import { join as join12 } from "path";
|
|
330702
330656
|
function rawDir(opts) {
|
|
330703
|
-
return
|
|
330657
|
+
return join12(opts.dataDir, ...REL_BASE, opts.projectSlug, "raw");
|
|
330704
330658
|
}
|
|
330705
330659
|
function distilledDir(opts, ts) {
|
|
330706
|
-
return
|
|
330660
|
+
return join12(opts.dataDir, ...REL_BASE, opts.projectSlug, ts.slice(0, 4), ts.slice(5, 7), ts.slice(8, 10));
|
|
330707
330661
|
}
|
|
330708
330662
|
async function distillJournal(opts) {
|
|
330709
|
-
const cooldownBase =
|
|
330663
|
+
const cooldownBase = join12(opts.dataDir, ...REL_BASE);
|
|
330710
330664
|
const cooldownSec = opts.cooldownSeconds ?? 0;
|
|
330711
330665
|
if (!acquireCooldown(cooldownBase, opts.projectSlug, cooldownSec)) {
|
|
330712
330666
|
return {
|
|
@@ -330718,7 +330672,7 @@ async function distillJournal(opts) {
|
|
|
330718
330672
|
};
|
|
330719
330673
|
}
|
|
330720
330674
|
try {
|
|
330721
|
-
const hw = readHighWater(
|
|
330675
|
+
const hw = readHighWater(join12(opts.dataDir, ...REL_BASE), opts.projectSlug);
|
|
330722
330676
|
const since = hw?.last_event_ts ?? "0000-01-01T00:00:00Z";
|
|
330723
330677
|
const cutoff = new Date(opts.now.getTime() - opts.inFlightWindowSeconds * 1e3).toISOString();
|
|
330724
330678
|
const events = readRawEvents(opts, since, cutoff, opts.maxEvents);
|
|
@@ -330734,7 +330688,7 @@ async function distillJournal(opts) {
|
|
|
330734
330688
|
const dir = distilledDir(opts, lastEvent.ts);
|
|
330735
330689
|
mkdirSync9(dir, { recursive: true });
|
|
330736
330690
|
const filename = `task-${bucket.task_slug}.md`;
|
|
330737
|
-
const filePath =
|
|
330691
|
+
const filePath = join12(dir, filename);
|
|
330738
330692
|
const fm = buildFrontmatter2(bucket, opts.projectSlug);
|
|
330739
330693
|
const entry = renderMechanicalEntry({
|
|
330740
330694
|
task_slug: bucket.task_slug,
|
|
@@ -330742,7 +330696,7 @@ async function distillJournal(opts) {
|
|
|
330742
330696
|
now: lastEvent.ts,
|
|
330743
330697
|
extraPatterns: opts.extraPatterns
|
|
330744
330698
|
});
|
|
330745
|
-
if (
|
|
330699
|
+
if (existsSync9(filePath)) {
|
|
330746
330700
|
const existing = readFileSync9(filePath, "utf8");
|
|
330747
330701
|
writeFileSync7(filePath, replaceOrAppendEntry(existing, fm, entry));
|
|
330748
330702
|
} else {
|
|
@@ -330764,7 +330718,7 @@ async function distillJournal(opts) {
|
|
|
330764
330718
|
});
|
|
330765
330719
|
}
|
|
330766
330720
|
const newHw = events[events.length - 1].ts;
|
|
330767
|
-
writeHighWater(
|
|
330721
|
+
writeHighWater(join12(opts.dataDir, ...REL_BASE), opts.projectSlug, {
|
|
330768
330722
|
last_event_ts: newHw,
|
|
330769
330723
|
last_distilled_at: opts.now.toISOString(),
|
|
330770
330724
|
events_processed: (hw?.events_processed ?? 0) + events.length
|
|
@@ -330782,17 +330736,17 @@ async function distillJournal(opts) {
|
|
|
330782
330736
|
}
|
|
330783
330737
|
function readRawEvents(opts, sinceTs, untilTs, max2) {
|
|
330784
330738
|
const dirs = [rawDir(opts)];
|
|
330785
|
-
const defaultDir =
|
|
330786
|
-
if (defaultDir !== dirs[0] &&
|
|
330739
|
+
const defaultDir = join12(opts.dataDir, ...REL_BASE, "default", "raw");
|
|
330740
|
+
if (defaultDir !== dirs[0] && existsSync9(defaultDir)) {
|
|
330787
330741
|
dirs.push(defaultDir);
|
|
330788
330742
|
}
|
|
330789
330743
|
const out = [];
|
|
330790
330744
|
for (const dir of dirs) {
|
|
330791
|
-
if (!
|
|
330745
|
+
if (!existsSync9(dir))
|
|
330792
330746
|
continue;
|
|
330793
330747
|
const files = readdirSync6(dir).filter((f3) => f3.endsWith(".jsonl")).sort();
|
|
330794
330748
|
for (const f3 of files) {
|
|
330795
|
-
const lines = readFileSync9(
|
|
330749
|
+
const lines = readFileSync9(join12(dir, f3), "utf8").split("\n").filter(Boolean);
|
|
330796
330750
|
for (const line of lines) {
|
|
330797
330751
|
try {
|
|
330798
330752
|
const ev = JSON.parse(line);
|
|
@@ -330979,17 +330933,17 @@ var init_git_watcher = __esm({
|
|
|
330979
330933
|
});
|
|
330980
330934
|
|
|
330981
330935
|
// ../../packages/core/dist/journal/presence.js
|
|
330982
|
-
import { readdirSync as readdirSync7, statSync as statSync3, existsSync as
|
|
330983
|
-
import { join as
|
|
330936
|
+
import { readdirSync as readdirSync7, statSync as statSync3, existsSync as existsSync10 } from "fs";
|
|
330937
|
+
import { join as join13 } from "path";
|
|
330984
330938
|
function isMcpActive(baseDir, projectSlug2, windowSec) {
|
|
330985
|
-
const rawDir2 =
|
|
330986
|
-
if (!
|
|
330939
|
+
const rawDir2 = join13(baseDir, projectSlug2, "raw");
|
|
330940
|
+
if (!existsSync10(rawDir2))
|
|
330987
330941
|
return false;
|
|
330988
330942
|
const cutoff = Date.now() - windowSec * 1e3;
|
|
330989
330943
|
for (const f3 of readdirSync7(rawDir2)) {
|
|
330990
330944
|
if (!f3.endsWith(".jsonl"))
|
|
330991
330945
|
continue;
|
|
330992
|
-
if (statSync3(
|
|
330946
|
+
if (statSync3(join13(rawDir2, f3)).mtimeMs >= cutoff)
|
|
330993
330947
|
return true;
|
|
330994
330948
|
}
|
|
330995
330949
|
return false;
|
|
@@ -331001,8 +330955,8 @@ var init_presence = __esm({
|
|
|
331001
330955
|
});
|
|
331002
330956
|
|
|
331003
330957
|
// ../../packages/core/dist/journal/housekeep.js
|
|
331004
|
-
import { readdirSync as readdirSync8, statSync as statSync4, unlinkSync as
|
|
331005
|
-
import { join as
|
|
330958
|
+
import { readdirSync as readdirSync8, statSync as statSync4, unlinkSync as unlinkSync6, renameSync as renameSync5, mkdirSync as mkdirSync10, existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
|
|
330959
|
+
import { join as join14 } from "path";
|
|
331006
330960
|
function lastEventTs(path3) {
|
|
331007
330961
|
try {
|
|
331008
330962
|
const content = readFileSync10(path3, "utf8");
|
|
@@ -331026,12 +330980,12 @@ function housekeepJournal(cfg) {
|
|
|
331026
330980
|
};
|
|
331027
330981
|
if (cfg.retention.raw_days > 0) {
|
|
331028
330982
|
const cutoff = now.getTime() - cfg.retention.raw_days * 864e5;
|
|
331029
|
-
const rawDir2 =
|
|
331030
|
-
if (
|
|
330983
|
+
const rawDir2 = join14(cfg.baseDir, cfg.projectSlug, "raw");
|
|
330984
|
+
if (existsSync11(rawDir2)) {
|
|
331031
330985
|
const hw = readHighWater(cfg.baseDir, cfg.projectSlug);
|
|
331032
330986
|
const highWaterTs = hw?.last_event_ts ?? null;
|
|
331033
330987
|
for (const f3 of readdirSync8(rawDir2).filter((n3) => n3.endsWith(".jsonl"))) {
|
|
331034
|
-
const p =
|
|
330988
|
+
const p = join14(rawDir2, f3);
|
|
331035
330989
|
if (statSync4(p).mtimeMs >= cutoff)
|
|
331036
330990
|
continue;
|
|
331037
330991
|
const fileLastTs = lastEventTs(p);
|
|
@@ -331039,7 +330993,7 @@ function housekeepJournal(cfg) {
|
|
|
331039
330993
|
result.raw_files_skipped_undistilled++;
|
|
331040
330994
|
continue;
|
|
331041
330995
|
}
|
|
331042
|
-
|
|
330996
|
+
unlinkSync6(p);
|
|
331043
330997
|
result.raw_files_pruned++;
|
|
331044
330998
|
}
|
|
331045
330999
|
}
|
|
@@ -331053,12 +331007,12 @@ function housekeepJournal(cfg) {
|
|
|
331053
331007
|
JOIN projects p ON p.id = jm.project_id
|
|
331054
331008
|
WHERE p.slug = ? AND jm.last_active_at < ?
|
|
331055
331009
|
`).all(cfg.projectSlug, archiveCutoff);
|
|
331056
|
-
const archiveDir =
|
|
331010
|
+
const archiveDir = join14(cfg.baseDir, cfg.projectSlug, "_archive");
|
|
331057
331011
|
mkdirSync10(archiveDir, { recursive: true });
|
|
331058
331012
|
for (const row of cold) {
|
|
331059
|
-
const dest =
|
|
331060
|
-
if (!
|
|
331061
|
-
|
|
331013
|
+
const dest = join14(archiveDir, row.path.split("/").pop() ?? `task-${row.file_id}.md`);
|
|
331014
|
+
if (!existsSync11(dest) && existsSync11(row.path)) {
|
|
331015
|
+
renameSync5(row.path, dest);
|
|
331062
331016
|
db2.prepare(`UPDATE files SET path = ? WHERE id = ?`).run(dest, row.file_id);
|
|
331063
331017
|
result.archived_tasks++;
|
|
331064
331018
|
}
|
|
@@ -331215,9 +331169,6 @@ __export(dist_exports2, {
|
|
|
331215
331169
|
listTags: () => listTags,
|
|
331216
331170
|
loadExtraPatterns: () => loadExtraPatterns,
|
|
331217
331171
|
markUpgradeApplied: () => markUpgradeApplied,
|
|
331218
|
-
migrateDataFiles: () => migrateDataFiles,
|
|
331219
|
-
migrateEnvVars: () => migrateEnvVars,
|
|
331220
|
-
migrateProjectConfig: () => migrateProjectConfig,
|
|
331221
331172
|
moveFile: () => moveFile,
|
|
331222
331173
|
openTasksForProject: () => openTasksForProject,
|
|
331223
331174
|
parseMarker: () => parseMarker,
|
|
@@ -331232,6 +331183,7 @@ __export(dist_exports2, {
|
|
|
331232
331183
|
removeTags: () => removeTags,
|
|
331233
331184
|
renderMechanicalEntry: () => renderMechanicalEntry,
|
|
331234
331185
|
replaceSection: () => replaceSection,
|
|
331186
|
+
resetDataDirCache: () => resetDataDirCache,
|
|
331235
331187
|
resolveSince: () => resolveSince,
|
|
331236
331188
|
restoreVersion: () => restoreVersion,
|
|
331237
331189
|
runPatterns: () => runPatterns,
|
|
@@ -331274,8 +331226,6 @@ var init_dist7 = __esm({
|
|
|
331274
331226
|
init_clip();
|
|
331275
331227
|
init_whats_new();
|
|
331276
331228
|
init_project_map();
|
|
331277
|
-
init_env_shim();
|
|
331278
|
-
init_file_migration();
|
|
331279
331229
|
init_journal();
|
|
331280
331230
|
}
|
|
331281
331231
|
});
|
|
@@ -331372,7 +331322,7 @@ __export(executor_exports, {
|
|
|
331372
331322
|
resolveCwd: () => resolveCwd
|
|
331373
331323
|
});
|
|
331374
331324
|
import { spawn } from "child_process";
|
|
331375
|
-
import { resolve as resolvePath, sep as
|
|
331325
|
+
import { resolve as resolvePath, sep as sep8 } from "path";
|
|
331376
331326
|
import { realpathSync } from "fs";
|
|
331377
331327
|
function resolveArgv(command, params, defs, argSeparator) {
|
|
331378
331328
|
const resolvedValues = {};
|
|
@@ -331428,7 +331378,7 @@ function resolveCwd(projectRoot, workingDir) {
|
|
|
331428
331378
|
} catch {
|
|
331429
331379
|
throw new Error(`workingDir does not exist: ${workingDir}`);
|
|
331430
331380
|
}
|
|
331431
|
-
if (real !== rootReal && !real.startsWith(rootReal +
|
|
331381
|
+
if (real !== rootReal && !real.startsWith(rootReal + sep8)) {
|
|
331432
331382
|
throw new Error(`workingDir resolved outside project root`);
|
|
331433
331383
|
}
|
|
331434
331384
|
return real;
|
|
@@ -331596,8 +331546,9 @@ import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mc
|
|
|
331596
331546
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
331597
331547
|
import { z as z6 } from "zod";
|
|
331598
331548
|
import RE22 from "re2";
|
|
331599
|
-
import { isAbsolute as isAbsolute7, join as
|
|
331600
|
-
import { statSync as statSync5, openSync as openSync2, readSync, closeSync as closeSync2, readFileSync as readFileSync12, existsSync as
|
|
331549
|
+
import { isAbsolute as isAbsolute7, join as join16, resolve as resolve7, sep as sep9, dirname as dirname6 } from "path";
|
|
331550
|
+
import { statSync as statSync5, lstatSync as lstatSync5, openSync as openSync2, readSync, closeSync as closeSync2, readFileSync as readFileSync12, existsSync as existsSync13, realpathSync as realpathSync2 } from "fs";
|
|
331551
|
+
import { createHash as createHash3 } from "crypto";
|
|
331601
331552
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
331602
331553
|
|
|
331603
331554
|
// src/hands/registry.ts
|
|
@@ -331605,9 +331556,8 @@ import { z as z2 } from "zod";
|
|
|
331605
331556
|
|
|
331606
331557
|
// src/hands/loader.ts
|
|
331607
331558
|
init_sanitizer();
|
|
331608
|
-
|
|
331609
|
-
import {
|
|
331610
|
-
import { join as join16, isAbsolute as isAbsolute6, normalize as normalize2, sep as sep6 } from "path";
|
|
331559
|
+
import { readFileSync as readFileSync11, existsSync as existsSync12 } from "fs";
|
|
331560
|
+
import { join as join15, isAbsolute as isAbsolute6, normalize as normalize2, sep as sep7 } from "path";
|
|
331611
331561
|
var TOOL_NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
331612
331562
|
var PARAM_NAME_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
331613
331563
|
var FORBIDDEN_ENV = /* @__PURE__ */ new Set([
|
|
@@ -331624,9 +331574,8 @@ var TIMEOUT_DEFAULT = 6e4;
|
|
|
331624
331574
|
var OUTPUT_MAX = 1e6;
|
|
331625
331575
|
var OUTPUT_DEFAULT = 1e5;
|
|
331626
331576
|
function loadProjectConfig(projectRoot) {
|
|
331627
|
-
|
|
331628
|
-
|
|
331629
|
-
if (!existsSync13(file)) {
|
|
331577
|
+
const file = join15(projectRoot, "kontexta.json");
|
|
331578
|
+
if (!existsSync12(file)) {
|
|
331630
331579
|
return { found: false, tools: {}, disabled: [], warnings: [], errors: [] };
|
|
331631
331580
|
}
|
|
331632
331581
|
let raw;
|
|
@@ -331676,7 +331625,7 @@ function validateTool(name50, def, projectRoot) {
|
|
|
331676
331625
|
}
|
|
331677
331626
|
if (!isLiteralArgv0(def.command[0])) return reject("argv[0] must be literal (no {{param}})");
|
|
331678
331627
|
const argv0 = def.command[0];
|
|
331679
|
-
if (!isAbsolute6(argv0) && (argv0.includes("/") || argv0.includes(
|
|
331628
|
+
if (!isAbsolute6(argv0) && (argv0.includes("/") || argv0.includes(sep7))) {
|
|
331680
331629
|
return reject("argv[0] must be absolute or a bare command name (no relative paths)");
|
|
331681
331630
|
}
|
|
331682
331631
|
const paramDefs = def.params ?? {};
|
|
@@ -331709,7 +331658,7 @@ function validateTool(name50, def, projectRoot) {
|
|
|
331709
331658
|
if (typeof def.workingDir !== "string") return reject("workingDir must be string");
|
|
331710
331659
|
if (isAbsolute6(def.workingDir)) return reject("workingDir must be relative");
|
|
331711
331660
|
const norm = normalize2(def.workingDir);
|
|
331712
|
-
if (norm.startsWith("..") || norm.split(
|
|
331661
|
+
if (norm.startsWith("..") || norm.split(sep7).includes("..")) return reject("workingDir must not contain ..");
|
|
331713
331662
|
}
|
|
331714
331663
|
if (def.env !== void 0) {
|
|
331715
331664
|
if (typeof def.env !== "object" || def.env === null) return reject("env must be object");
|
|
@@ -332547,7 +332496,7 @@ function getAgentRulesWarning(projectId) {
|
|
|
332547
332496
|
}
|
|
332548
332497
|
const outdatedProjects = [];
|
|
332549
332498
|
for (const p of projects) {
|
|
332550
|
-
if (!p || !p.path || !
|
|
332499
|
+
if (!p || !p.path || !existsSync13(p.path)) continue;
|
|
332551
332500
|
const contextFiles = detectAgentContextFiles(p.path);
|
|
332552
332501
|
if (contextFiles.length === 0) continue;
|
|
332553
332502
|
const statuses = checkAgentRulesStatus(p.path, contextFiles);
|
|
@@ -332616,8 +332565,8 @@ var pkgVersionFound = false;
|
|
|
332616
332565
|
try {
|
|
332617
332566
|
let currentDir = dirname6(fileURLToPath3(import.meta.url));
|
|
332618
332567
|
while (currentDir !== dirname6(currentDir)) {
|
|
332619
|
-
const p =
|
|
332620
|
-
if (
|
|
332568
|
+
const p = join16(currentDir, "package.json");
|
|
332569
|
+
if (existsSync13(p)) {
|
|
332621
332570
|
const pkg = JSON.parse(readFileSync12(p, "utf8"));
|
|
332622
332571
|
if (pkg.name === "kontexta-mcp" && pkg.version) {
|
|
332623
332572
|
pkgVersion = pkg.version;
|
|
@@ -332638,7 +332587,7 @@ var server = new McpServer({
|
|
|
332638
332587
|
version: pkgVersion
|
|
332639
332588
|
});
|
|
332640
332589
|
var handsRegistry = new HandsRegistry(server);
|
|
332641
|
-
var baseJournalDir =
|
|
332590
|
+
var baseJournalDir = join16(dataDir, "knowledge", "journal");
|
|
332642
332591
|
var projectSlug = process.env.KONTEXTA_DEFAULT_PROJECT_SLUG ?? "default";
|
|
332643
332592
|
var agent = process.env.KONTEXTA_AGENT ?? "unknown";
|
|
332644
332593
|
var sid = `${process.pid}-${Date.now().toString(36)}`;
|
|
@@ -332696,8 +332645,8 @@ server.tool(
|
|
|
332696
332645
|
const journalFolder = "journal";
|
|
332697
332646
|
const title = `journal-${today}`;
|
|
332698
332647
|
const db2 = getDatabase();
|
|
332699
|
-
const knowledgeDir =
|
|
332700
|
-
const expectedPath =
|
|
332648
|
+
const knowledgeDir = join16(dataDir, "knowledge");
|
|
332649
|
+
const expectedPath = join16(knowledgeDir, journalFolder, `${title}.md`);
|
|
332701
332650
|
const existingRow = db2.prepare("SELECT id, path FROM files WHERE path = ? AND project_id IS NULL").get(expectedPath);
|
|
332702
332651
|
if (existingRow) {
|
|
332703
332652
|
const existing = readFile(existingRow.id);
|
|
@@ -332827,7 +332776,7 @@ server.tool(
|
|
|
332827
332776
|
folder = parts.length > 1 ? parts.slice(0, -1).join("/") : null;
|
|
332828
332777
|
}
|
|
332829
332778
|
} else {
|
|
332830
|
-
const knowledgeRoot =
|
|
332779
|
+
const knowledgeRoot = join16(dataDir, "knowledge");
|
|
332831
332780
|
if (file.path?.startsWith(knowledgeRoot)) {
|
|
332832
332781
|
const rel = file.path.slice(knowledgeRoot.length).replace(/^[\/\\]+/, "");
|
|
332833
332782
|
const parts = rel.split(/[\/\\]/);
|
|
@@ -333276,7 +333225,7 @@ server.tool(
|
|
|
333276
333225
|
async () => {
|
|
333277
333226
|
const result = listProjects();
|
|
333278
333227
|
const augmented = result.map((p) => {
|
|
333279
|
-
const has_hands = !!(p.path &&
|
|
333228
|
+
const has_hands = !!(p.path && existsSync13(p.path) && existsSync13(`${p.path}/kontexta.json`));
|
|
333280
333229
|
const contextFiles = p.path ? detectAgentContextFiles(p.path) : [];
|
|
333281
333230
|
const statuses = p.path ? checkAgentRulesStatus(p.path, contextFiles) : [];
|
|
333282
333231
|
const outdated = statuses.filter((s3) => !s3.upToDate);
|
|
@@ -333484,6 +333433,130 @@ RETURNS: { written: [{ path, action: created|updated|skipped, version }], skippe
|
|
|
333484
333433
|
}
|
|
333485
333434
|
}
|
|
333486
333435
|
);
|
|
333436
|
+
server.tool(
|
|
333437
|
+
"transfer_agent_context",
|
|
333438
|
+
`COPY existing agent context files (CLAUDE.md, AGENTS.md, .cursor/rules/*.mdc, etc.) from a project's repo into Kontexta's per-project knowledge base so they're indexed by FTS5 and can be git-synced through Kontexta's own backup engine.
|
|
333439
|
+
|
|
333440
|
+
This tool ONLY COPIES. It never deletes or modifies the originals in your repo. After a successful transfer, the response includes the list of source paths so the user can manually remove them if desired. No tool argument, no flag, and no code path in this tool ever calls a destructive filesystem operation against \`project.path\`.
|
|
333441
|
+
|
|
333442
|
+
MANDATORY: This tool writes new files into Kontexta's data dir. You MUST seek explicit user consent before calling. Set 'confirm: true' only after the user has agreed.
|
|
333443
|
+
|
|
333444
|
+
PARAMETERS:
|
|
333445
|
+
- project_id: number, required. Project ID returned from register_project.
|
|
333446
|
+
- confirm: boolean, required. Must be true.
|
|
333447
|
+
- files: string[], optional. Project-relative paths to transfer. Omit or pass [] to transfer all detected agent context files (uses the same detection list as register_project / onboard_agent).
|
|
333448
|
+
|
|
333449
|
+
RETURNS: { transferred: [{ source_path, kb_id, kb_path, est_tokens }], skipped: [{ source_path, reason }], next_action }
|
|
333450
|
+
Skip reasons: "missing" | "symlink" | "outside_project" | "already_transferred_same_content" | "read_error" | "write_error".
|
|
333451
|
+
|
|
333452
|
+
IDEMPOTENT: re-running with the same files copies nothing if the content is unchanged \u2014 duplicate transfers are detected via SHA-256 hash comparison against existing project KB rows.`,
|
|
333453
|
+
{
|
|
333454
|
+
project_id: z6.number().describe("Project ID returned from register_project"),
|
|
333455
|
+
confirm: z6.boolean().describe("MANDATORY: Set to true only after obtaining explicit user consent."),
|
|
333456
|
+
files: z6.array(z6.string()).optional().describe("Project-relative paths to transfer. Omit to transfer all detected context files.")
|
|
333457
|
+
},
|
|
333458
|
+
async ({ project_id, confirm, files }) => {
|
|
333459
|
+
try {
|
|
333460
|
+
if (confirm !== true) {
|
|
333461
|
+
return {
|
|
333462
|
+
isError: true,
|
|
333463
|
+
content: [{
|
|
333464
|
+
type: "text",
|
|
333465
|
+
text: JSON.stringify({
|
|
333466
|
+
error: "User consent required",
|
|
333467
|
+
details: "This tool copies project files into Kontexta's KB. Explain the proposed transfer to the user and obtain explicit consent, then re-run with 'confirm: true'. Originals in the project repo are NOT touched."
|
|
333468
|
+
}, null, 2)
|
|
333469
|
+
}]
|
|
333470
|
+
};
|
|
333471
|
+
}
|
|
333472
|
+
const db2 = getDatabase();
|
|
333473
|
+
const project = db2.prepare("SELECT id, name, slug, path FROM projects WHERE id = ?").get(project_id);
|
|
333474
|
+
if (!project || !project.path) {
|
|
333475
|
+
return {
|
|
333476
|
+
isError: true,
|
|
333477
|
+
content: [{ type: "text", text: JSON.stringify({ error: `Project ${project_id} not found or has no path` }, null, 2) }]
|
|
333478
|
+
};
|
|
333479
|
+
}
|
|
333480
|
+
const projectPath = project.path;
|
|
333481
|
+
const targetRels = files && files.length > 0 ? files : detectAgentContextFiles(projectPath);
|
|
333482
|
+
const existingHashes = new Set(
|
|
333483
|
+
db2.prepare("SELECT content_hash FROM files WHERE project_id = ? AND storage_type = 'local' AND content_hash IS NOT NULL").all(project.id).map((r5) => r5.content_hash)
|
|
333484
|
+
);
|
|
333485
|
+
const transferred = [];
|
|
333486
|
+
const skipped = [];
|
|
333487
|
+
for (const rel of targetRels) {
|
|
333488
|
+
let abs2;
|
|
333489
|
+
try {
|
|
333490
|
+
abs2 = assertPathInside(projectPath, rel);
|
|
333491
|
+
} catch {
|
|
333492
|
+
skipped.push({ source_path: rel, reason: "outside_project" });
|
|
333493
|
+
continue;
|
|
333494
|
+
}
|
|
333495
|
+
let stat;
|
|
333496
|
+
try {
|
|
333497
|
+
stat = lstatSync5(abs2);
|
|
333498
|
+
} catch {
|
|
333499
|
+
stat = null;
|
|
333500
|
+
}
|
|
333501
|
+
if (!stat) {
|
|
333502
|
+
skipped.push({ source_path: rel, reason: "missing" });
|
|
333503
|
+
continue;
|
|
333504
|
+
}
|
|
333505
|
+
if (stat.isSymbolicLink()) {
|
|
333506
|
+
skipped.push({ source_path: rel, reason: "symlink" });
|
|
333507
|
+
continue;
|
|
333508
|
+
}
|
|
333509
|
+
if (!stat.isFile()) {
|
|
333510
|
+
skipped.push({ source_path: rel, reason: "missing" });
|
|
333511
|
+
continue;
|
|
333512
|
+
}
|
|
333513
|
+
let content;
|
|
333514
|
+
try {
|
|
333515
|
+
content = readFileSync12(abs2, "utf8");
|
|
333516
|
+
} catch {
|
|
333517
|
+
skipped.push({ source_path: rel, reason: "read_error" });
|
|
333518
|
+
continue;
|
|
333519
|
+
}
|
|
333520
|
+
const hash = createHash3("sha256").update(content, "utf8").digest("hex");
|
|
333521
|
+
if (existingHashes.has(hash)) {
|
|
333522
|
+
skipped.push({ source_path: rel, reason: "already_transferred_same_content" });
|
|
333523
|
+
continue;
|
|
333524
|
+
}
|
|
333525
|
+
const titleSource = rel.replace(/\.[^./]+$/, "");
|
|
333526
|
+
const title = titleSource || rel;
|
|
333527
|
+
try {
|
|
333528
|
+
const created = await createFile({
|
|
333529
|
+
title,
|
|
333530
|
+
content,
|
|
333531
|
+
destination: "kontexta",
|
|
333532
|
+
projectId: project.id,
|
|
333533
|
+
folder: "agent-context",
|
|
333534
|
+
dataDir,
|
|
333535
|
+
sourcePath: abs2
|
|
333536
|
+
});
|
|
333537
|
+
existingHashes.add(hash);
|
|
333538
|
+
transferred.push({
|
|
333539
|
+
source_path: rel,
|
|
333540
|
+
kb_id: created.id,
|
|
333541
|
+
kb_path: created.path,
|
|
333542
|
+
est_tokens: estimateTokensFromBuffer(Buffer.from(content, "utf8"))
|
|
333543
|
+
});
|
|
333544
|
+
} catch (e3) {
|
|
333545
|
+
skipped.push({ source_path: rel, reason: `write_error: ${e3?.message ?? String(e3)}` });
|
|
333546
|
+
}
|
|
333547
|
+
}
|
|
333548
|
+
const next_action = transferred.length > 0 ? `Copied ${transferred.length} file(s) into Kontexta's KB. Originals in your repo are unchanged. To remove them yourself: ${transferred.map((t3) => `rm "${join16(projectPath, t3.source_path)}"`).join(" && ")}` : "No files were transferred. See skipped[] for reasons.";
|
|
333549
|
+
return {
|
|
333550
|
+
content: [{ type: "text", text: JSON.stringify({ transferred, skipped, next_action }, null, 2) }]
|
|
333551
|
+
};
|
|
333552
|
+
} catch (e3) {
|
|
333553
|
+
return {
|
|
333554
|
+
isError: true,
|
|
333555
|
+
content: [{ type: "text", text: JSON.stringify({ error: e3?.message ?? String(e3) }, null, 2) }]
|
|
333556
|
+
};
|
|
333557
|
+
}
|
|
333558
|
+
}
|
|
333559
|
+
);
|
|
333487
333560
|
server.tool(
|
|
333488
333561
|
"commit_backup",
|
|
333489
333562
|
"SIDE-EFFECTFUL \u2014 TOUCHES THE NETWORK. Sync the project's KB data into its git backup directory, create a commit, and `git push` to `origin`. AUTH: relies on the local user's git credentials (SSH agent, credential helper, etc.) \u2014 there is no in-server auth. Kontexta does not rate-limit, but the remote may. Idempotent in steady state: a no-op commit is skipped, but the push still runs. Throws if the project has no configured backup repo or if push fails (network, auth, conflict). Returns `{success, copied_files_count, copied_paths}`. Use after a batch of KB writes to get changes off-machine.",
|
|
@@ -333757,7 +333830,7 @@ server.tool(
|
|
|
333757
333830
|
);
|
|
333758
333831
|
function resolveFolderBase(projectId) {
|
|
333759
333832
|
if (projectId === void 0 || projectId === null) {
|
|
333760
|
-
return
|
|
333833
|
+
return join16(dataDir, "knowledge");
|
|
333761
333834
|
}
|
|
333762
333835
|
const project = getDatabase().prepare("SELECT path FROM projects WHERE id = ?").get(projectId);
|
|
333763
333836
|
if (!project?.path) {
|
|
@@ -333882,7 +333955,7 @@ server.tool(
|
|
|
333882
333955
|
if (!project?.path) throw new Error(`Project not found for file ${file_id}`);
|
|
333883
333956
|
base3 = project.path;
|
|
333884
333957
|
} else {
|
|
333885
|
-
base3 =
|
|
333958
|
+
base3 = join16(dataDir, "knowledge");
|
|
333886
333959
|
}
|
|
333887
333960
|
let baseResolved;
|
|
333888
333961
|
try {
|
|
@@ -333898,8 +333971,8 @@ server.tool(
|
|
|
333898
333971
|
} catch {
|
|
333899
333972
|
throw new Error(`Destination parent directory does not exist: ${destParent}`);
|
|
333900
333973
|
}
|
|
333901
|
-
const destResolved =
|
|
333902
|
-
if (destResolved !== baseResolved && !destResolved.startsWith(baseResolved +
|
|
333974
|
+
const destResolved = join16(destParentReal, destAbs.slice(destParent.length + (destParent.endsWith(sep9) ? 0 : 1)));
|
|
333975
|
+
if (destResolved !== baseResolved && !destResolved.startsWith(baseResolved + sep9)) {
|
|
333903
333976
|
throw new Error(`new_path must be inside ${base3}`);
|
|
333904
333977
|
}
|
|
333905
333978
|
let srcResolved;
|
|
@@ -333908,7 +333981,7 @@ server.tool(
|
|
|
333908
333981
|
} catch {
|
|
333909
333982
|
srcResolved = resolve7(file.path);
|
|
333910
333983
|
}
|
|
333911
|
-
if (srcResolved !== baseResolved && !srcResolved.startsWith(baseResolved +
|
|
333984
|
+
if (srcResolved !== baseResolved && !srcResolved.startsWith(baseResolved + sep9)) {
|
|
333912
333985
|
throw new Error(`source path ${file.path} is no longer inside ${base3}; refusing to move`);
|
|
333913
333986
|
}
|
|
333914
333987
|
const updated = moveFile(file_id, new_path);
|
|
@@ -334603,7 +334676,7 @@ server.tool(
|
|
|
334603
334676
|
"Re-scan every registered project's `kontexta.json` and rebuild the live Hands tool registry \u2014 newly-declared tools become callable immediately, removed tools disappear from `tools/list`. SIDE EFFECT is on the running MCP session's tool inventory only (no disk writes). Idempotent. No external auth or rate limits. Takes no parameters. Returns per-project load results (counts of registered/disabled tools and any validation warnings). Use after editing a `kontexta.json` mid-session; for the schema see `describe_hands_schema`.",
|
|
334604
334677
|
{},
|
|
334605
334678
|
async () => {
|
|
334606
|
-
const projects = listProjects().filter((p) => p.path &&
|
|
334679
|
+
const projects = listProjects().filter((p) => p.path && existsSync13(p.path)).map((p) => ({ name: p.name, root: p.path }));
|
|
334607
334680
|
const r5 = handsRegistry.reloadAll(projects);
|
|
334608
334681
|
return { content: [{ type: "text", text: JSON.stringify(r5, null, 2) }] };
|
|
334609
334682
|
}
|
|
@@ -334634,7 +334707,7 @@ server.tool(
|
|
|
334634
334707
|
async function main() {
|
|
334635
334708
|
const transport = new StdioServerTransport();
|
|
334636
334709
|
{
|
|
334637
|
-
const projects = listProjects().filter((p) => p.path &&
|
|
334710
|
+
const projects = listProjects().filter((p) => p.path && existsSync13(p.path)).map((p) => ({ name: p.name, root: p.path }));
|
|
334638
334711
|
const r5 = handsRegistry.reloadAll(projects);
|
|
334639
334712
|
console.error(
|
|
334640
334713
|
`Kontexta Hands: loaded ${r5.perProject.length} projects, registered ${r5.totalRegistered} tools (${r5.totalDisabled} disabled)`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kontexta-mcp",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.10",
|
|
4
4
|
"description": "The local Brain and Hands for AI coding agents (MCP Server)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
64
|
-
"better-sqlite3": "^
|
|
64
|
+
"better-sqlite3": "^12.10.0",
|
|
65
65
|
"chokidar": "^4.0.0",
|
|
66
66
|
"kxta-core": "workspace:*",
|
|
67
67
|
"re2": "^1.24.1",
|
|
@@ -73,5 +73,5 @@
|
|
|
73
73
|
"tsup": "^8.3.0",
|
|
74
74
|
"typescript": "^5.7.0"
|
|
75
75
|
},
|
|
76
|
-
"rulesVersion": "2.
|
|
76
|
+
"rulesVersion": "2.1.0"
|
|
77
77
|
}
|