kontexta-mcp 2.0.8 → 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 +253 -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;
|
|
@@ -420,8 +437,8 @@ var init_extensions = __esm({
|
|
|
420
437
|
// ../../packages/core/dist/git/index.js
|
|
421
438
|
import { createHash } from "crypto";
|
|
422
439
|
import simpleGit from "simple-git";
|
|
423
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, copyFileSync, mkdirSync as mkdirSync3, existsSync as existsSync3, rmSync, readdirSync as readdirSync2, statSync, lstatSync, unlinkSync, renameSync } from "fs";
|
|
424
|
-
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";
|
|
425
442
|
import { spawnSync } from "child_process";
|
|
426
443
|
function redactCredentials(s3) {
|
|
427
444
|
return s3.replace(/([a-z][a-z0-9+.-]*:\/\/)([^:@\/\s]+:[^@\/\s]+)@/gi, "$1***:***@");
|
|
@@ -540,7 +557,7 @@ async function restoreVersion(repoDir, filePath, commitHash) {
|
|
|
540
557
|
renameSync(tmpPath, filePath);
|
|
541
558
|
} catch (e3) {
|
|
542
559
|
try {
|
|
543
|
-
|
|
560
|
+
unlinkSync2(tmpPath);
|
|
544
561
|
} catch {
|
|
545
562
|
}
|
|
546
563
|
throw e3;
|
|
@@ -818,7 +835,7 @@ async function _syncBackupLocked(projectId, dataDir2, onStage) {
|
|
|
818
835
|
}
|
|
819
836
|
} else if (!expectedBackupPaths.has(fullPath)) {
|
|
820
837
|
try {
|
|
821
|
-
|
|
838
|
+
unlinkSync2(fullPath);
|
|
822
839
|
} catch {
|
|
823
840
|
}
|
|
824
841
|
}
|
|
@@ -888,7 +905,7 @@ async function _syncBackupLocked(projectId, dataDir2, onStage) {
|
|
|
888
905
|
if (project.path) {
|
|
889
906
|
const localAbsolutePath = join3(project.path, relativePath);
|
|
890
907
|
const projectResolved = resolve3(project.path);
|
|
891
|
-
if (resolve3(localAbsolutePath) !== projectResolved && !resolve3(localAbsolutePath).startsWith(projectResolved +
|
|
908
|
+
if (resolve3(localAbsolutePath) !== projectResolved && !resolve3(localAbsolutePath).startsWith(projectResolved + sep3)) {
|
|
892
909
|
continue;
|
|
893
910
|
}
|
|
894
911
|
mkdirSync3(dirname2(localAbsolutePath), { recursive: true });
|
|
@@ -954,12 +971,12 @@ async function _syncBackupLocked(projectId, dataDir2, onStage) {
|
|
|
954
971
|
continue;
|
|
955
972
|
const localAbsolutePath = join3(project.path, insideBackup);
|
|
956
973
|
const resolvedLocal = resolve3(localAbsolutePath);
|
|
957
|
-
if (resolvedLocal !== projectResolved && !resolvedLocal.startsWith(projectResolved +
|
|
974
|
+
if (resolvedLocal !== projectResolved && !resolvedLocal.startsWith(projectResolved + sep3)) {
|
|
958
975
|
continue;
|
|
959
976
|
}
|
|
960
977
|
try {
|
|
961
978
|
if (existsSync3(localAbsolutePath)) {
|
|
962
|
-
|
|
979
|
+
unlinkSync2(localAbsolutePath);
|
|
963
980
|
}
|
|
964
981
|
} catch (e3) {
|
|
965
982
|
console.warn(`syncBackup: failed to remove ${localAbsolutePath}:`, e3);
|
|
@@ -1001,8 +1018,8 @@ var init_git = __esm({
|
|
|
1001
1018
|
|
|
1002
1019
|
// ../../packages/core/dist/files/index.js
|
|
1003
1020
|
import { createHash as createHash2 } from "crypto";
|
|
1004
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, unlinkSync as
|
|
1005
|
-
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";
|
|
1006
1023
|
function listProjectFolders(projectPath) {
|
|
1007
1024
|
const folders = [];
|
|
1008
1025
|
function scan(dir, currentRel) {
|
|
@@ -1137,7 +1154,7 @@ async function createFile(opts) {
|
|
|
1137
1154
|
}
|
|
1138
1155
|
} else {
|
|
1139
1156
|
try {
|
|
1140
|
-
|
|
1157
|
+
unlinkSync3(filePath);
|
|
1141
1158
|
} catch {
|
|
1142
1159
|
}
|
|
1143
1160
|
}
|
|
@@ -1255,11 +1272,11 @@ function deleteFile(id, dataDir2) {
|
|
|
1255
1272
|
if (file && dataDir2) {
|
|
1256
1273
|
const knowledgeRoot = resolve4(dataDir2, "knowledge");
|
|
1257
1274
|
const filePathResolved = isAbsolute4(file.path) ? file.path : resolve4(dataDir2, file.path);
|
|
1258
|
-
const inKnowledge = filePathResolved === knowledgeRoot || filePathResolved.startsWith(knowledgeRoot +
|
|
1275
|
+
const inKnowledge = filePathResolved === knowledgeRoot || filePathResolved.startsWith(knowledgeRoot + sep4);
|
|
1259
1276
|
if (file.project_id === null && inKnowledge) {
|
|
1260
1277
|
try {
|
|
1261
1278
|
if (existsSync4(filePathResolved)) {
|
|
1262
|
-
|
|
1279
|
+
unlinkSync3(filePathResolved);
|
|
1263
1280
|
}
|
|
1264
1281
|
} catch (e3) {
|
|
1265
1282
|
console.error("Failed to delete Knowledge Base file from disk:", e3);
|
|
@@ -1862,8 +1879,8 @@ var init_settings = __esm({
|
|
|
1862
1879
|
});
|
|
1863
1880
|
|
|
1864
1881
|
// ../../packages/core/dist/agent-rules/index.js
|
|
1865
|
-
import { lstatSync as lstatSync4, readdirSync as readdirSync5, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4, readFileSync as readFileSync6, renameSync as renameSync3, unlinkSync as
|
|
1866
|
-
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";
|
|
1867
1884
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1868
1885
|
function loadCorePackageVersion() {
|
|
1869
1886
|
try {
|
|
@@ -1926,7 +1943,7 @@ function isRegularNonSymlink(absPath) {
|
|
|
1926
1943
|
}
|
|
1927
1944
|
}
|
|
1928
1945
|
function toForwardSlashes(p) {
|
|
1929
|
-
return
|
|
1946
|
+
return sep5 === "/" ? p : p.split(sep5).join("/");
|
|
1930
1947
|
}
|
|
1931
1948
|
function detectAgentContextFiles(projectPath) {
|
|
1932
1949
|
const out = [];
|
|
@@ -1975,8 +1992,8 @@ function parseMarker(content) {
|
|
|
1975
1992
|
function injectOrUpdate(content, block, version2) {
|
|
1976
1993
|
const parsed = parseMarker(content);
|
|
1977
1994
|
if (parsed === null) {
|
|
1978
|
-
const
|
|
1979
|
-
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 };
|
|
1980
1997
|
}
|
|
1981
1998
|
if (parsed.kind === "malformed") {
|
|
1982
1999
|
throw new InjectError("malformed", "kontexta rules marker is malformed (BEGIN with no matching END)");
|
|
@@ -1999,7 +2016,7 @@ function atomicWrite(absPath, content) {
|
|
|
1999
2016
|
renameSync3(tmp, absPath);
|
|
2000
2017
|
} catch (e3) {
|
|
2001
2018
|
try {
|
|
2002
|
-
|
|
2019
|
+
unlinkSync4(tmp);
|
|
2003
2020
|
} catch {
|
|
2004
2021
|
}
|
|
2005
2022
|
throw e3;
|
|
@@ -137503,7 +137520,7 @@ var require_util7 = __commonJS({
|
|
|
137503
137520
|
return path3;
|
|
137504
137521
|
});
|
|
137505
137522
|
exports.normalize = normalize3;
|
|
137506
|
-
function
|
|
137523
|
+
function join17(aRoot, aPath) {
|
|
137507
137524
|
if (aRoot === "") {
|
|
137508
137525
|
aRoot = ".";
|
|
137509
137526
|
}
|
|
@@ -137535,7 +137552,7 @@ var require_util7 = __commonJS({
|
|
|
137535
137552
|
}
|
|
137536
137553
|
return joined;
|
|
137537
137554
|
}
|
|
137538
|
-
exports.join =
|
|
137555
|
+
exports.join = join17;
|
|
137539
137556
|
exports.isAbsolute = function(aPath) {
|
|
137540
137557
|
return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
|
|
137541
137558
|
};
|
|
@@ -137749,7 +137766,7 @@ var require_util7 = __commonJS({
|
|
|
137749
137766
|
parsed.path = parsed.path.substring(0, index + 1);
|
|
137750
137767
|
}
|
|
137751
137768
|
}
|
|
137752
|
-
sourceURL =
|
|
137769
|
+
sourceURL = join17(urlGenerate(parsed), sourceURL);
|
|
137753
137770
|
}
|
|
137754
137771
|
return normalize3(sourceURL);
|
|
137755
137772
|
}
|
|
@@ -329707,17 +329724,17 @@ var init_whats_new = __esm({
|
|
|
329707
329724
|
});
|
|
329708
329725
|
|
|
329709
329726
|
// ../../packages/core/dist/project-map/index.js
|
|
329710
|
-
import { sep as
|
|
329727
|
+
import { sep as sep6, join as join8, basename as basename2 } from "path";
|
|
329711
329728
|
function emptyNode() {
|
|
329712
329729
|
return { dirs: /* @__PURE__ */ new Map(), files: [] };
|
|
329713
329730
|
}
|
|
329714
329731
|
function stripRoot(filePath, root2) {
|
|
329715
|
-
const rootWithSep = root2.endsWith(
|
|
329732
|
+
const rootWithSep = root2.endsWith(sep6) ? root2 : root2 + sep6;
|
|
329716
329733
|
if (filePath === root2)
|
|
329717
329734
|
return [];
|
|
329718
329735
|
if (!filePath.startsWith(rootWithSep))
|
|
329719
329736
|
return null;
|
|
329720
|
-
return filePath.slice(rootWithSep.length).split(
|
|
329737
|
+
return filePath.slice(rootWithSep.length).split(sep6).filter(Boolean);
|
|
329721
329738
|
}
|
|
329722
329739
|
function locateFile(file, projectsById, dataDir2) {
|
|
329723
329740
|
if (file.project_id == null) {
|
|
@@ -329860,73 +329877,6 @@ var init_project_map = __esm({
|
|
|
329860
329877
|
}
|
|
329861
329878
|
});
|
|
329862
329879
|
|
|
329863
|
-
// ../../packages/core/dist/compat/env-shim.js
|
|
329864
|
-
function migrateEnvVars() {
|
|
329865
|
-
const migrated = [];
|
|
329866
|
-
for (const [oldKey, newKey] of RENAMED_VARS) {
|
|
329867
|
-
if (process.env[oldKey] && !process.env[newKey]) {
|
|
329868
|
-
process.env[newKey] = process.env[oldKey];
|
|
329869
|
-
migrated.push(oldKey);
|
|
329870
|
-
}
|
|
329871
|
-
}
|
|
329872
|
-
if (migrated.length > 0) {
|
|
329873
|
-
console.warn(`[Kontexta] Deprecated env vars migrated: ${migrated.join(", ")}. Rename to KONTEXTA_* prefix. Support removed in v2.0.`);
|
|
329874
|
-
}
|
|
329875
|
-
return migrated;
|
|
329876
|
-
}
|
|
329877
|
-
var RENAMED_VARS;
|
|
329878
|
-
var init_env_shim = __esm({
|
|
329879
|
-
"../../packages/core/dist/compat/env-shim.js"() {
|
|
329880
|
-
"use strict";
|
|
329881
|
-
RENAMED_VARS = [
|
|
329882
|
-
["MNEXIS_DATA_DIR", "KONTEXTA_DATA_DIR"],
|
|
329883
|
-
["MNEXIS_DB_PATH", "KONTEXTA_DB_PATH"],
|
|
329884
|
-
["MNEXIS_WS_HOST", "KONTEXTA_WS_HOST"],
|
|
329885
|
-
["MNEXIS_WS_ORIGINS", "KONTEXTA_WS_ORIGINS"],
|
|
329886
|
-
["MNEXIS_WS_TOKEN", "KONTEXTA_WS_TOKEN"],
|
|
329887
|
-
["MNEXIS_EXPORT_MAX_BYTES", "KONTEXTA_EXPORT_MAX_BYTES"],
|
|
329888
|
-
["MNEXIS_INSTALL_HINT", "KONTEXTA_INSTALL_HINT"],
|
|
329889
|
-
["MNEXIS_PROJECT_TOKEN_WARN", "KONTEXTA_PROJECT_TOKEN_WARN"],
|
|
329890
|
-
["MNEXIS_SHUTDOWN_DRAIN_MS", "KONTEXTA_SHUTDOWN_DRAIN_MS"]
|
|
329891
|
-
];
|
|
329892
|
-
}
|
|
329893
|
-
});
|
|
329894
|
-
|
|
329895
|
-
// ../../packages/core/dist/compat/file-migration.js
|
|
329896
|
-
import { existsSync as existsSync7, renameSync as renameSync4, copyFileSync as copyFileSync2 } from "fs";
|
|
329897
|
-
import { join as join9 } from "path";
|
|
329898
|
-
function migrateDataFiles(dataDir2) {
|
|
329899
|
-
for (const [oldName, newName] of DB_RENAMES) {
|
|
329900
|
-
const oldPath = join9(dataDir2, oldName);
|
|
329901
|
-
const newPath = join9(dataDir2, newName);
|
|
329902
|
-
if (existsSync7(oldPath) && !existsSync7(newPath)) {
|
|
329903
|
-
copyFileSync2(oldPath, oldPath + ".bak");
|
|
329904
|
-
renameSync4(oldPath, newPath);
|
|
329905
|
-
console.warn(`[Kontexta] Migrated ${oldName} \u2192 ${newName} (backup: ${oldName}.bak). Automatic migration removed in v2.0.`);
|
|
329906
|
-
}
|
|
329907
|
-
}
|
|
329908
|
-
}
|
|
329909
|
-
function migrateProjectConfig(projectRoot) {
|
|
329910
|
-
const oldConfig = join9(projectRoot, "mnexis.json");
|
|
329911
|
-
const newConfig = join9(projectRoot, "kontexta.json");
|
|
329912
|
-
if (existsSync7(oldConfig) && !existsSync7(newConfig)) {
|
|
329913
|
-
copyFileSync2(oldConfig, oldConfig + ".bak");
|
|
329914
|
-
renameSync4(oldConfig, newConfig);
|
|
329915
|
-
console.warn(`[Kontexta] Migrated mnexis.json \u2192 kontexta.json in ${projectRoot} (backup: mnexis.json.bak). Automatic migration removed in v2.0.`);
|
|
329916
|
-
}
|
|
329917
|
-
}
|
|
329918
|
-
var DB_RENAMES;
|
|
329919
|
-
var init_file_migration = __esm({
|
|
329920
|
-
"../../packages/core/dist/compat/file-migration.js"() {
|
|
329921
|
-
"use strict";
|
|
329922
|
-
DB_RENAMES = [
|
|
329923
|
-
["mnexis.db", "kontexta.db"],
|
|
329924
|
-
["mnexis.db-wal", "kontexta.db-wal"],
|
|
329925
|
-
["mnexis.db-shm", "kontexta.db-shm"]
|
|
329926
|
-
];
|
|
329927
|
-
}
|
|
329928
|
-
});
|
|
329929
|
-
|
|
329930
329880
|
// ../../packages/core/dist/journal/types.js
|
|
329931
329881
|
var init_types2 = __esm({
|
|
329932
329882
|
"../../packages/core/dist/journal/types.js"() {
|
|
@@ -329936,7 +329886,7 @@ var init_types2 = __esm({
|
|
|
329936
329886
|
|
|
329937
329887
|
// ../../packages/core/dist/journal/writer.js
|
|
329938
329888
|
import { mkdirSync as mkdirSync6, openSync, closeSync, writeSync, fsyncSync } from "fs";
|
|
329939
|
-
import { join as
|
|
329889
|
+
import { join as join9, dirname as dirname5 } from "path";
|
|
329940
329890
|
var JournalWriter;
|
|
329941
329891
|
var init_writer = __esm({
|
|
329942
329892
|
"../../packages/core/dist/journal/writer.js"() {
|
|
@@ -329946,7 +329896,7 @@ var init_writer = __esm({
|
|
|
329946
329896
|
currentDay = null;
|
|
329947
329897
|
rawDir;
|
|
329948
329898
|
constructor(opts) {
|
|
329949
|
-
this.rawDir =
|
|
329899
|
+
this.rawDir = join9(opts.baseDir, opts.projectSlug, "raw");
|
|
329950
329900
|
}
|
|
329951
329901
|
append(event) {
|
|
329952
329902
|
const day = event.ts.slice(0, 10);
|
|
@@ -329968,7 +329918,7 @@ var init_writer = __esm({
|
|
|
329968
329918
|
closeSync(this.fd);
|
|
329969
329919
|
this.fd = null;
|
|
329970
329920
|
}
|
|
329971
|
-
const path3 =
|
|
329921
|
+
const path3 = join9(this.rawDir, `${day}.jsonl`);
|
|
329972
329922
|
mkdirSync6(dirname5(path3), { recursive: true });
|
|
329973
329923
|
this.fd = openSync(path3, "a");
|
|
329974
329924
|
this.currentDay = day;
|
|
@@ -330023,14 +329973,14 @@ var init_redact = __esm({
|
|
|
330023
329973
|
});
|
|
330024
329974
|
|
|
330025
329975
|
// ../../packages/core/dist/journal/high-water.js
|
|
330026
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, renameSync as
|
|
330027
|
-
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";
|
|
330028
329978
|
function pathFor(baseDir, projectSlug2) {
|
|
330029
|
-
return
|
|
329979
|
+
return join10(baseDir, projectSlug2, ".distilled-up-to.json");
|
|
330030
329980
|
}
|
|
330031
329981
|
function readHighWater(baseDir, projectSlug2) {
|
|
330032
329982
|
const p = pathFor(baseDir, projectSlug2);
|
|
330033
|
-
if (!
|
|
329983
|
+
if (!existsSync7(p))
|
|
330034
329984
|
return null;
|
|
330035
329985
|
try {
|
|
330036
329986
|
const raw = readFileSync7(p, "utf8");
|
|
@@ -330044,12 +329994,12 @@ function readHighWater(baseDir, projectSlug2) {
|
|
|
330044
329994
|
}
|
|
330045
329995
|
}
|
|
330046
329996
|
function writeHighWater(baseDir, projectSlug2, hw) {
|
|
330047
|
-
const dir =
|
|
329997
|
+
const dir = join10(baseDir, projectSlug2);
|
|
330048
329998
|
mkdirSync7(dir, { recursive: true });
|
|
330049
329999
|
const final = pathFor(baseDir, projectSlug2);
|
|
330050
330000
|
const tmp = `${final}.tmp`;
|
|
330051
330001
|
writeFileSync5(tmp, JSON.stringify(hw, null, 2));
|
|
330052
|
-
|
|
330002
|
+
renameSync4(tmp, final);
|
|
330053
330003
|
}
|
|
330054
330004
|
var init_high_water = __esm({
|
|
330055
330005
|
"../../packages/core/dist/journal/high-water.js"() {
|
|
@@ -330669,12 +330619,12 @@ var init_repository = __esm({
|
|
|
330669
330619
|
});
|
|
330670
330620
|
|
|
330671
330621
|
// ../../packages/core/dist/journal/cooldown.js
|
|
330672
|
-
import { existsSync as
|
|
330673
|
-
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";
|
|
330674
330624
|
function acquireCooldown(baseDir, projectSlug2, cooldownSeconds) {
|
|
330675
|
-
mkdirSync8(
|
|
330676
|
-
const lockPath =
|
|
330677
|
-
if (
|
|
330625
|
+
mkdirSync8(join11(baseDir, projectSlug2), { recursive: true });
|
|
330626
|
+
const lockPath = join11(baseDir, projectSlug2, ".distill.lock");
|
|
330627
|
+
if (existsSync8(lockPath)) {
|
|
330678
330628
|
try {
|
|
330679
330629
|
const ts = Number(readFileSync8(lockPath, "utf8"));
|
|
330680
330630
|
if (!isNaN(ts) && Date.now() - ts < cooldownSeconds * 1e3)
|
|
@@ -330686,10 +330636,10 @@ function acquireCooldown(baseDir, projectSlug2, cooldownSeconds) {
|
|
|
330686
330636
|
return true;
|
|
330687
330637
|
}
|
|
330688
330638
|
function releaseCooldown(baseDir, projectSlug2) {
|
|
330689
|
-
const lockPath =
|
|
330690
|
-
if (
|
|
330639
|
+
const lockPath = join11(baseDir, projectSlug2, ".distill.lock");
|
|
330640
|
+
if (existsSync8(lockPath)) {
|
|
330691
330641
|
try {
|
|
330692
|
-
|
|
330642
|
+
unlinkSync5(lockPath);
|
|
330693
330643
|
} catch {
|
|
330694
330644
|
}
|
|
330695
330645
|
}
|
|
@@ -330701,16 +330651,16 @@ var init_cooldown = __esm({
|
|
|
330701
330651
|
});
|
|
330702
330652
|
|
|
330703
330653
|
// ../../packages/core/dist/journal/distill.js
|
|
330704
|
-
import { readFileSync as readFileSync9, readdirSync as readdirSync6, existsSync as
|
|
330705
|
-
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";
|
|
330706
330656
|
function rawDir(opts) {
|
|
330707
|
-
return
|
|
330657
|
+
return join12(opts.dataDir, ...REL_BASE, opts.projectSlug, "raw");
|
|
330708
330658
|
}
|
|
330709
330659
|
function distilledDir(opts, ts) {
|
|
330710
|
-
return
|
|
330660
|
+
return join12(opts.dataDir, ...REL_BASE, opts.projectSlug, ts.slice(0, 4), ts.slice(5, 7), ts.slice(8, 10));
|
|
330711
330661
|
}
|
|
330712
330662
|
async function distillJournal(opts) {
|
|
330713
|
-
const cooldownBase =
|
|
330663
|
+
const cooldownBase = join12(opts.dataDir, ...REL_BASE);
|
|
330714
330664
|
const cooldownSec = opts.cooldownSeconds ?? 0;
|
|
330715
330665
|
if (!acquireCooldown(cooldownBase, opts.projectSlug, cooldownSec)) {
|
|
330716
330666
|
return {
|
|
@@ -330722,7 +330672,7 @@ async function distillJournal(opts) {
|
|
|
330722
330672
|
};
|
|
330723
330673
|
}
|
|
330724
330674
|
try {
|
|
330725
|
-
const hw = readHighWater(
|
|
330675
|
+
const hw = readHighWater(join12(opts.dataDir, ...REL_BASE), opts.projectSlug);
|
|
330726
330676
|
const since = hw?.last_event_ts ?? "0000-01-01T00:00:00Z";
|
|
330727
330677
|
const cutoff = new Date(opts.now.getTime() - opts.inFlightWindowSeconds * 1e3).toISOString();
|
|
330728
330678
|
const events = readRawEvents(opts, since, cutoff, opts.maxEvents);
|
|
@@ -330738,7 +330688,7 @@ async function distillJournal(opts) {
|
|
|
330738
330688
|
const dir = distilledDir(opts, lastEvent.ts);
|
|
330739
330689
|
mkdirSync9(dir, { recursive: true });
|
|
330740
330690
|
const filename = `task-${bucket.task_slug}.md`;
|
|
330741
|
-
const filePath =
|
|
330691
|
+
const filePath = join12(dir, filename);
|
|
330742
330692
|
const fm = buildFrontmatter2(bucket, opts.projectSlug);
|
|
330743
330693
|
const entry = renderMechanicalEntry({
|
|
330744
330694
|
task_slug: bucket.task_slug,
|
|
@@ -330746,7 +330696,7 @@ async function distillJournal(opts) {
|
|
|
330746
330696
|
now: lastEvent.ts,
|
|
330747
330697
|
extraPatterns: opts.extraPatterns
|
|
330748
330698
|
});
|
|
330749
|
-
if (
|
|
330699
|
+
if (existsSync9(filePath)) {
|
|
330750
330700
|
const existing = readFileSync9(filePath, "utf8");
|
|
330751
330701
|
writeFileSync7(filePath, replaceOrAppendEntry(existing, fm, entry));
|
|
330752
330702
|
} else {
|
|
@@ -330768,7 +330718,7 @@ async function distillJournal(opts) {
|
|
|
330768
330718
|
});
|
|
330769
330719
|
}
|
|
330770
330720
|
const newHw = events[events.length - 1].ts;
|
|
330771
|
-
writeHighWater(
|
|
330721
|
+
writeHighWater(join12(opts.dataDir, ...REL_BASE), opts.projectSlug, {
|
|
330772
330722
|
last_event_ts: newHw,
|
|
330773
330723
|
last_distilled_at: opts.now.toISOString(),
|
|
330774
330724
|
events_processed: (hw?.events_processed ?? 0) + events.length
|
|
@@ -330786,17 +330736,17 @@ async function distillJournal(opts) {
|
|
|
330786
330736
|
}
|
|
330787
330737
|
function readRawEvents(opts, sinceTs, untilTs, max2) {
|
|
330788
330738
|
const dirs = [rawDir(opts)];
|
|
330789
|
-
const defaultDir =
|
|
330790
|
-
if (defaultDir !== dirs[0] &&
|
|
330739
|
+
const defaultDir = join12(opts.dataDir, ...REL_BASE, "default", "raw");
|
|
330740
|
+
if (defaultDir !== dirs[0] && existsSync9(defaultDir)) {
|
|
330791
330741
|
dirs.push(defaultDir);
|
|
330792
330742
|
}
|
|
330793
330743
|
const out = [];
|
|
330794
330744
|
for (const dir of dirs) {
|
|
330795
|
-
if (!
|
|
330745
|
+
if (!existsSync9(dir))
|
|
330796
330746
|
continue;
|
|
330797
330747
|
const files = readdirSync6(dir).filter((f3) => f3.endsWith(".jsonl")).sort();
|
|
330798
330748
|
for (const f3 of files) {
|
|
330799
|
-
const lines = readFileSync9(
|
|
330749
|
+
const lines = readFileSync9(join12(dir, f3), "utf8").split("\n").filter(Boolean);
|
|
330800
330750
|
for (const line of lines) {
|
|
330801
330751
|
try {
|
|
330802
330752
|
const ev = JSON.parse(line);
|
|
@@ -330983,17 +330933,17 @@ var init_git_watcher = __esm({
|
|
|
330983
330933
|
});
|
|
330984
330934
|
|
|
330985
330935
|
// ../../packages/core/dist/journal/presence.js
|
|
330986
|
-
import { readdirSync as readdirSync7, statSync as statSync3, existsSync as
|
|
330987
|
-
import { join as
|
|
330936
|
+
import { readdirSync as readdirSync7, statSync as statSync3, existsSync as existsSync10 } from "fs";
|
|
330937
|
+
import { join as join13 } from "path";
|
|
330988
330938
|
function isMcpActive(baseDir, projectSlug2, windowSec) {
|
|
330989
|
-
const rawDir2 =
|
|
330990
|
-
if (!
|
|
330939
|
+
const rawDir2 = join13(baseDir, projectSlug2, "raw");
|
|
330940
|
+
if (!existsSync10(rawDir2))
|
|
330991
330941
|
return false;
|
|
330992
330942
|
const cutoff = Date.now() - windowSec * 1e3;
|
|
330993
330943
|
for (const f3 of readdirSync7(rawDir2)) {
|
|
330994
330944
|
if (!f3.endsWith(".jsonl"))
|
|
330995
330945
|
continue;
|
|
330996
|
-
if (statSync3(
|
|
330946
|
+
if (statSync3(join13(rawDir2, f3)).mtimeMs >= cutoff)
|
|
330997
330947
|
return true;
|
|
330998
330948
|
}
|
|
330999
330949
|
return false;
|
|
@@ -331005,8 +330955,8 @@ var init_presence = __esm({
|
|
|
331005
330955
|
});
|
|
331006
330956
|
|
|
331007
330957
|
// ../../packages/core/dist/journal/housekeep.js
|
|
331008
|
-
import { readdirSync as readdirSync8, statSync as statSync4, unlinkSync as
|
|
331009
|
-
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";
|
|
331010
330960
|
function lastEventTs(path3) {
|
|
331011
330961
|
try {
|
|
331012
330962
|
const content = readFileSync10(path3, "utf8");
|
|
@@ -331030,12 +330980,12 @@ function housekeepJournal(cfg) {
|
|
|
331030
330980
|
};
|
|
331031
330981
|
if (cfg.retention.raw_days > 0) {
|
|
331032
330982
|
const cutoff = now.getTime() - cfg.retention.raw_days * 864e5;
|
|
331033
|
-
const rawDir2 =
|
|
331034
|
-
if (
|
|
330983
|
+
const rawDir2 = join14(cfg.baseDir, cfg.projectSlug, "raw");
|
|
330984
|
+
if (existsSync11(rawDir2)) {
|
|
331035
330985
|
const hw = readHighWater(cfg.baseDir, cfg.projectSlug);
|
|
331036
330986
|
const highWaterTs = hw?.last_event_ts ?? null;
|
|
331037
330987
|
for (const f3 of readdirSync8(rawDir2).filter((n3) => n3.endsWith(".jsonl"))) {
|
|
331038
|
-
const p =
|
|
330988
|
+
const p = join14(rawDir2, f3);
|
|
331039
330989
|
if (statSync4(p).mtimeMs >= cutoff)
|
|
331040
330990
|
continue;
|
|
331041
330991
|
const fileLastTs = lastEventTs(p);
|
|
@@ -331043,7 +330993,7 @@ function housekeepJournal(cfg) {
|
|
|
331043
330993
|
result.raw_files_skipped_undistilled++;
|
|
331044
330994
|
continue;
|
|
331045
330995
|
}
|
|
331046
|
-
|
|
330996
|
+
unlinkSync6(p);
|
|
331047
330997
|
result.raw_files_pruned++;
|
|
331048
330998
|
}
|
|
331049
330999
|
}
|
|
@@ -331057,12 +331007,12 @@ function housekeepJournal(cfg) {
|
|
|
331057
331007
|
JOIN projects p ON p.id = jm.project_id
|
|
331058
331008
|
WHERE p.slug = ? AND jm.last_active_at < ?
|
|
331059
331009
|
`).all(cfg.projectSlug, archiveCutoff);
|
|
331060
|
-
const archiveDir =
|
|
331010
|
+
const archiveDir = join14(cfg.baseDir, cfg.projectSlug, "_archive");
|
|
331061
331011
|
mkdirSync10(archiveDir, { recursive: true });
|
|
331062
331012
|
for (const row of cold) {
|
|
331063
|
-
const dest =
|
|
331064
|
-
if (!
|
|
331065
|
-
|
|
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);
|
|
331066
331016
|
db2.prepare(`UPDATE files SET path = ? WHERE id = ?`).run(dest, row.file_id);
|
|
331067
331017
|
result.archived_tasks++;
|
|
331068
331018
|
}
|
|
@@ -331219,9 +331169,6 @@ __export(dist_exports2, {
|
|
|
331219
331169
|
listTags: () => listTags,
|
|
331220
331170
|
loadExtraPatterns: () => loadExtraPatterns,
|
|
331221
331171
|
markUpgradeApplied: () => markUpgradeApplied,
|
|
331222
|
-
migrateDataFiles: () => migrateDataFiles,
|
|
331223
|
-
migrateEnvVars: () => migrateEnvVars,
|
|
331224
|
-
migrateProjectConfig: () => migrateProjectConfig,
|
|
331225
331172
|
moveFile: () => moveFile,
|
|
331226
331173
|
openTasksForProject: () => openTasksForProject,
|
|
331227
331174
|
parseMarker: () => parseMarker,
|
|
@@ -331236,6 +331183,7 @@ __export(dist_exports2, {
|
|
|
331236
331183
|
removeTags: () => removeTags,
|
|
331237
331184
|
renderMechanicalEntry: () => renderMechanicalEntry,
|
|
331238
331185
|
replaceSection: () => replaceSection,
|
|
331186
|
+
resetDataDirCache: () => resetDataDirCache,
|
|
331239
331187
|
resolveSince: () => resolveSince,
|
|
331240
331188
|
restoreVersion: () => restoreVersion,
|
|
331241
331189
|
runPatterns: () => runPatterns,
|
|
@@ -331278,8 +331226,6 @@ var init_dist7 = __esm({
|
|
|
331278
331226
|
init_clip();
|
|
331279
331227
|
init_whats_new();
|
|
331280
331228
|
init_project_map();
|
|
331281
|
-
init_env_shim();
|
|
331282
|
-
init_file_migration();
|
|
331283
331229
|
init_journal();
|
|
331284
331230
|
}
|
|
331285
331231
|
});
|
|
@@ -331376,7 +331322,7 @@ __export(executor_exports, {
|
|
|
331376
331322
|
resolveCwd: () => resolveCwd
|
|
331377
331323
|
});
|
|
331378
331324
|
import { spawn } from "child_process";
|
|
331379
|
-
import { resolve as resolvePath, sep as
|
|
331325
|
+
import { resolve as resolvePath, sep as sep8 } from "path";
|
|
331380
331326
|
import { realpathSync } from "fs";
|
|
331381
331327
|
function resolveArgv(command, params, defs, argSeparator) {
|
|
331382
331328
|
const resolvedValues = {};
|
|
@@ -331432,7 +331378,7 @@ function resolveCwd(projectRoot, workingDir) {
|
|
|
331432
331378
|
} catch {
|
|
331433
331379
|
throw new Error(`workingDir does not exist: ${workingDir}`);
|
|
331434
331380
|
}
|
|
331435
|
-
if (real !== rootReal && !real.startsWith(rootReal +
|
|
331381
|
+
if (real !== rootReal && !real.startsWith(rootReal + sep8)) {
|
|
331436
331382
|
throw new Error(`workingDir resolved outside project root`);
|
|
331437
331383
|
}
|
|
331438
331384
|
return real;
|
|
@@ -331600,8 +331546,9 @@ import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mc
|
|
|
331600
331546
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
331601
331547
|
import { z as z6 } from "zod";
|
|
331602
331548
|
import RE22 from "re2";
|
|
331603
|
-
import { isAbsolute as isAbsolute7, join as
|
|
331604
|
-
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";
|
|
331605
331552
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
331606
331553
|
|
|
331607
331554
|
// src/hands/registry.ts
|
|
@@ -331609,9 +331556,8 @@ import { z as z2 } from "zod";
|
|
|
331609
331556
|
|
|
331610
331557
|
// src/hands/loader.ts
|
|
331611
331558
|
init_sanitizer();
|
|
331612
|
-
|
|
331613
|
-
import {
|
|
331614
|
-
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";
|
|
331615
331561
|
var TOOL_NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
331616
331562
|
var PARAM_NAME_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
331617
331563
|
var FORBIDDEN_ENV = /* @__PURE__ */ new Set([
|
|
@@ -331628,9 +331574,8 @@ var TIMEOUT_DEFAULT = 6e4;
|
|
|
331628
331574
|
var OUTPUT_MAX = 1e6;
|
|
331629
331575
|
var OUTPUT_DEFAULT = 1e5;
|
|
331630
331576
|
function loadProjectConfig(projectRoot) {
|
|
331631
|
-
|
|
331632
|
-
|
|
331633
|
-
if (!existsSync13(file)) {
|
|
331577
|
+
const file = join15(projectRoot, "kontexta.json");
|
|
331578
|
+
if (!existsSync12(file)) {
|
|
331634
331579
|
return { found: false, tools: {}, disabled: [], warnings: [], errors: [] };
|
|
331635
331580
|
}
|
|
331636
331581
|
let raw;
|
|
@@ -331680,7 +331625,7 @@ function validateTool(name50, def, projectRoot) {
|
|
|
331680
331625
|
}
|
|
331681
331626
|
if (!isLiteralArgv0(def.command[0])) return reject("argv[0] must be literal (no {{param}})");
|
|
331682
331627
|
const argv0 = def.command[0];
|
|
331683
|
-
if (!isAbsolute6(argv0) && (argv0.includes("/") || argv0.includes(
|
|
331628
|
+
if (!isAbsolute6(argv0) && (argv0.includes("/") || argv0.includes(sep7))) {
|
|
331684
331629
|
return reject("argv[0] must be absolute or a bare command name (no relative paths)");
|
|
331685
331630
|
}
|
|
331686
331631
|
const paramDefs = def.params ?? {};
|
|
@@ -331713,7 +331658,7 @@ function validateTool(name50, def, projectRoot) {
|
|
|
331713
331658
|
if (typeof def.workingDir !== "string") return reject("workingDir must be string");
|
|
331714
331659
|
if (isAbsolute6(def.workingDir)) return reject("workingDir must be relative");
|
|
331715
331660
|
const norm = normalize2(def.workingDir);
|
|
331716
|
-
if (norm.startsWith("..") || norm.split(
|
|
331661
|
+
if (norm.startsWith("..") || norm.split(sep7).includes("..")) return reject("workingDir must not contain ..");
|
|
331717
331662
|
}
|
|
331718
331663
|
if (def.env !== void 0) {
|
|
331719
331664
|
if (typeof def.env !== "object" || def.env === null) return reject("env must be object");
|
|
@@ -332551,7 +332496,7 @@ function getAgentRulesWarning(projectId) {
|
|
|
332551
332496
|
}
|
|
332552
332497
|
const outdatedProjects = [];
|
|
332553
332498
|
for (const p of projects) {
|
|
332554
|
-
if (!p || !p.path || !
|
|
332499
|
+
if (!p || !p.path || !existsSync13(p.path)) continue;
|
|
332555
332500
|
const contextFiles = detectAgentContextFiles(p.path);
|
|
332556
332501
|
if (contextFiles.length === 0) continue;
|
|
332557
332502
|
const statuses = checkAgentRulesStatus(p.path, contextFiles);
|
|
@@ -332620,8 +332565,8 @@ var pkgVersionFound = false;
|
|
|
332620
332565
|
try {
|
|
332621
332566
|
let currentDir = dirname6(fileURLToPath3(import.meta.url));
|
|
332622
332567
|
while (currentDir !== dirname6(currentDir)) {
|
|
332623
|
-
const p =
|
|
332624
|
-
if (
|
|
332568
|
+
const p = join16(currentDir, "package.json");
|
|
332569
|
+
if (existsSync13(p)) {
|
|
332625
332570
|
const pkg = JSON.parse(readFileSync12(p, "utf8"));
|
|
332626
332571
|
if (pkg.name === "kontexta-mcp" && pkg.version) {
|
|
332627
332572
|
pkgVersion = pkg.version;
|
|
@@ -332642,7 +332587,7 @@ var server = new McpServer({
|
|
|
332642
332587
|
version: pkgVersion
|
|
332643
332588
|
});
|
|
332644
332589
|
var handsRegistry = new HandsRegistry(server);
|
|
332645
|
-
var baseJournalDir =
|
|
332590
|
+
var baseJournalDir = join16(dataDir, "knowledge", "journal");
|
|
332646
332591
|
var projectSlug = process.env.KONTEXTA_DEFAULT_PROJECT_SLUG ?? "default";
|
|
332647
332592
|
var agent = process.env.KONTEXTA_AGENT ?? "unknown";
|
|
332648
332593
|
var sid = `${process.pid}-${Date.now().toString(36)}`;
|
|
@@ -332700,8 +332645,8 @@ server.tool(
|
|
|
332700
332645
|
const journalFolder = "journal";
|
|
332701
332646
|
const title = `journal-${today}`;
|
|
332702
332647
|
const db2 = getDatabase();
|
|
332703
|
-
const knowledgeDir =
|
|
332704
|
-
const expectedPath =
|
|
332648
|
+
const knowledgeDir = join16(dataDir, "knowledge");
|
|
332649
|
+
const expectedPath = join16(knowledgeDir, journalFolder, `${title}.md`);
|
|
332705
332650
|
const existingRow = db2.prepare("SELECT id, path FROM files WHERE path = ? AND project_id IS NULL").get(expectedPath);
|
|
332706
332651
|
if (existingRow) {
|
|
332707
332652
|
const existing = readFile(existingRow.id);
|
|
@@ -332831,7 +332776,7 @@ server.tool(
|
|
|
332831
332776
|
folder = parts.length > 1 ? parts.slice(0, -1).join("/") : null;
|
|
332832
332777
|
}
|
|
332833
332778
|
} else {
|
|
332834
|
-
const knowledgeRoot =
|
|
332779
|
+
const knowledgeRoot = join16(dataDir, "knowledge");
|
|
332835
332780
|
if (file.path?.startsWith(knowledgeRoot)) {
|
|
332836
332781
|
const rel = file.path.slice(knowledgeRoot.length).replace(/^[\/\\]+/, "");
|
|
332837
332782
|
const parts = rel.split(/[\/\\]/);
|
|
@@ -333280,7 +333225,7 @@ server.tool(
|
|
|
333280
333225
|
async () => {
|
|
333281
333226
|
const result = listProjects();
|
|
333282
333227
|
const augmented = result.map((p) => {
|
|
333283
|
-
const has_hands = !!(p.path &&
|
|
333228
|
+
const has_hands = !!(p.path && existsSync13(p.path) && existsSync13(`${p.path}/kontexta.json`));
|
|
333284
333229
|
const contextFiles = p.path ? detectAgentContextFiles(p.path) : [];
|
|
333285
333230
|
const statuses = p.path ? checkAgentRulesStatus(p.path, contextFiles) : [];
|
|
333286
333231
|
const outdated = statuses.filter((s3) => !s3.upToDate);
|
|
@@ -333488,6 +333433,130 @@ RETURNS: { written: [{ path, action: created|updated|skipped, version }], skippe
|
|
|
333488
333433
|
}
|
|
333489
333434
|
}
|
|
333490
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
|
+
);
|
|
333491
333560
|
server.tool(
|
|
333492
333561
|
"commit_backup",
|
|
333493
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.",
|
|
@@ -333761,7 +333830,7 @@ server.tool(
|
|
|
333761
333830
|
);
|
|
333762
333831
|
function resolveFolderBase(projectId) {
|
|
333763
333832
|
if (projectId === void 0 || projectId === null) {
|
|
333764
|
-
return
|
|
333833
|
+
return join16(dataDir, "knowledge");
|
|
333765
333834
|
}
|
|
333766
333835
|
const project = getDatabase().prepare("SELECT path FROM projects WHERE id = ?").get(projectId);
|
|
333767
333836
|
if (!project?.path) {
|
|
@@ -333886,7 +333955,7 @@ server.tool(
|
|
|
333886
333955
|
if (!project?.path) throw new Error(`Project not found for file ${file_id}`);
|
|
333887
333956
|
base3 = project.path;
|
|
333888
333957
|
} else {
|
|
333889
|
-
base3 =
|
|
333958
|
+
base3 = join16(dataDir, "knowledge");
|
|
333890
333959
|
}
|
|
333891
333960
|
let baseResolved;
|
|
333892
333961
|
try {
|
|
@@ -333902,8 +333971,8 @@ server.tool(
|
|
|
333902
333971
|
} catch {
|
|
333903
333972
|
throw new Error(`Destination parent directory does not exist: ${destParent}`);
|
|
333904
333973
|
}
|
|
333905
|
-
const destResolved =
|
|
333906
|
-
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)) {
|
|
333907
333976
|
throw new Error(`new_path must be inside ${base3}`);
|
|
333908
333977
|
}
|
|
333909
333978
|
let srcResolved;
|
|
@@ -333912,7 +333981,7 @@ server.tool(
|
|
|
333912
333981
|
} catch {
|
|
333913
333982
|
srcResolved = resolve7(file.path);
|
|
333914
333983
|
}
|
|
333915
|
-
if (srcResolved !== baseResolved && !srcResolved.startsWith(baseResolved +
|
|
333984
|
+
if (srcResolved !== baseResolved && !srcResolved.startsWith(baseResolved + sep9)) {
|
|
333916
333985
|
throw new Error(`source path ${file.path} is no longer inside ${base3}; refusing to move`);
|
|
333917
333986
|
}
|
|
333918
333987
|
const updated = moveFile(file_id, new_path);
|
|
@@ -334607,7 +334676,7 @@ server.tool(
|
|
|
334607
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`.",
|
|
334608
334677
|
{},
|
|
334609
334678
|
async () => {
|
|
334610
|
-
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 }));
|
|
334611
334680
|
const r5 = handsRegistry.reloadAll(projects);
|
|
334612
334681
|
return { content: [{ type: "text", text: JSON.stringify(r5, null, 2) }] };
|
|
334613
334682
|
}
|
|
@@ -334638,7 +334707,7 @@ server.tool(
|
|
|
334638
334707
|
async function main() {
|
|
334639
334708
|
const transport = new StdioServerTransport();
|
|
334640
334709
|
{
|
|
334641
|
-
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 }));
|
|
334642
334711
|
const r5 = handsRegistry.reloadAll(projects);
|
|
334643
334712
|
console.error(
|
|
334644
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
|
}
|