@kontextmind/kxm 0.7.165 → 0.7.167
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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +16 -0
- package/docs/README.md +1 -0
- package/docs/architecture/hosted-direction.md +9 -4
- package/docs/architecture/inventory.md +1 -0
- package/docs/concepts/trust-model.md +1 -1
- package/docs/contributing/decisions.md +153 -0
- package/docs/contributing/learnings.md +54 -53
- package/docs/contributing/operating-rules.md +19 -6
- package/docs/glossary.md +2 -2
- package/docs/operations/troubleshooting.md +2 -2
- package/docs/operations.md +62 -8
- package/docs/reference/cli-reference.md +11 -11
- package/docs/reference/config-reference.md +36 -11
- package/docs/reference/configuration.md +2 -2
- package/docs/reference/tools.md +1 -1
- package/docs/start/quickstart-claude-code.md +2 -2
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/README.md +3 -3
- package/plugins/kxm/dist/claude-hook.js +417 -66
- package/plugins/kxm/dist/cli.js +1399 -900
- package/plugins/kxm/dist/extension.js +399 -64
- package/plugins/kxm/dist/mcp-server.js +622 -135
- package/plugins/kxm/dist/runtime-supervisor.js +668 -166
- package/plugins/kxm/dist/runtime.js +713 -211
- package/plugins/kxm/dist/server.js +466 -58
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +15 -7
- package/plugins/kxm/src/cli/hub.ts +80 -21
- package/plugins/kxm/src/cli/types.ts +8 -1
- package/plugins/kxm/src/cli.ts +41 -7
- package/plugins/kxm/src/config.ts +246 -3
- package/plugins/kxm/src/extension.ts +8 -7
- package/plugins/kxm/src/hub-autostart.ts +20 -0
- package/plugins/kxm/src/hub-env.ts +83 -31
- package/plugins/kxm/src/hub-identity.ts +337 -0
- package/plugins/kxm/src/mcp-server.ts +17 -15
- package/plugins/kxm/src/project-name.ts +6 -47
- package/plugins/kxm/src/server.ts +22 -4
- package/scripts/kxm-hub.mjs +5 -0
|
@@ -2635,7 +2635,7 @@ var require_stringify = __commonJS({
|
|
|
2635
2635
|
props.push(doc.directives.tagString(tag));
|
|
2636
2636
|
return props.join(" ");
|
|
2637
2637
|
}
|
|
2638
|
-
function
|
|
2638
|
+
function stringify3(item, ctx, onComment, onChompKeep) {
|
|
2639
2639
|
if (identity.isPair(item))
|
|
2640
2640
|
return item.toString(ctx, onComment, onChompKeep);
|
|
2641
2641
|
if (identity.isAlias(item)) {
|
|
@@ -2664,7 +2664,7 @@ var require_stringify = __commonJS({
|
|
|
2664
2664
|
${ctx.indent}${str}`;
|
|
2665
2665
|
}
|
|
2666
2666
|
exports.createStringifyContext = createStringifyContext;
|
|
2667
|
-
exports.stringify =
|
|
2667
|
+
exports.stringify = stringify3;
|
|
2668
2668
|
}
|
|
2669
2669
|
});
|
|
2670
2670
|
|
|
@@ -2674,7 +2674,7 @@ var require_stringifyPair = __commonJS({
|
|
|
2674
2674
|
"use strict";
|
|
2675
2675
|
var identity = require_identity();
|
|
2676
2676
|
var Scalar = require_Scalar();
|
|
2677
|
-
var
|
|
2677
|
+
var stringify3 = require_stringify();
|
|
2678
2678
|
var stringifyComment = require_stringifyComment();
|
|
2679
2679
|
function stringifyPair({ key, value }, ctx, onComment, onChompKeep) {
|
|
2680
2680
|
const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx;
|
|
@@ -2696,7 +2696,7 @@ var require_stringifyPair = __commonJS({
|
|
|
2696
2696
|
});
|
|
2697
2697
|
let keyCommentDone = false;
|
|
2698
2698
|
let chompKeep = false;
|
|
2699
|
-
let str =
|
|
2699
|
+
let str = stringify3.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
|
|
2700
2700
|
if (!explicitKey && !ctx.inFlow && str.length > 1024) {
|
|
2701
2701
|
if (simpleKeys)
|
|
2702
2702
|
throw new Error("With simple keys, single line scalar must not span more than 1024 characters");
|
|
@@ -2748,7 +2748,7 @@ ${indent}:`;
|
|
|
2748
2748
|
ctx.indent = ctx.indent.substring(2);
|
|
2749
2749
|
}
|
|
2750
2750
|
let valueCommentDone = false;
|
|
2751
|
-
const valueStr =
|
|
2751
|
+
const valueStr = stringify3.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
|
|
2752
2752
|
let ws = " ";
|
|
2753
2753
|
if (keyComment || vsb || vcb) {
|
|
2754
2754
|
ws = vsb ? "\n" : "";
|
|
@@ -2889,7 +2889,7 @@ var require_addPairToJSMap = __commonJS({
|
|
|
2889
2889
|
"use strict";
|
|
2890
2890
|
var log = require_log();
|
|
2891
2891
|
var merge = require_merge();
|
|
2892
|
-
var
|
|
2892
|
+
var stringify3 = require_stringify();
|
|
2893
2893
|
var identity = require_identity();
|
|
2894
2894
|
var toJS = require_toJS();
|
|
2895
2895
|
function addPairToJSMap(ctx, map, { key, value }) {
|
|
@@ -2925,7 +2925,7 @@ var require_addPairToJSMap = __commonJS({
|
|
|
2925
2925
|
if (typeof jsKey !== "object")
|
|
2926
2926
|
return String(jsKey);
|
|
2927
2927
|
if (identity.isNode(key) && ctx?.doc) {
|
|
2928
|
-
const strCtx =
|
|
2928
|
+
const strCtx = stringify3.createStringifyContext(ctx.doc, {});
|
|
2929
2929
|
strCtx.anchors = /* @__PURE__ */ new Set();
|
|
2930
2930
|
for (const node of ctx.anchors.keys())
|
|
2931
2931
|
strCtx.anchors.add(node.anchor);
|
|
@@ -2992,12 +2992,12 @@ var require_stringifyCollection = __commonJS({
|
|
|
2992
2992
|
"node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) {
|
|
2993
2993
|
"use strict";
|
|
2994
2994
|
var identity = require_identity();
|
|
2995
|
-
var
|
|
2995
|
+
var stringify3 = require_stringify();
|
|
2996
2996
|
var stringifyComment = require_stringifyComment();
|
|
2997
2997
|
function stringifyCollection(collection, ctx, options) {
|
|
2998
2998
|
const flow = ctx.inFlow ?? collection.flow;
|
|
2999
|
-
const
|
|
3000
|
-
return
|
|
2999
|
+
const stringify4 = flow ? stringifyFlowCollection : stringifyBlockCollection;
|
|
3000
|
+
return stringify4(collection, ctx, options);
|
|
3001
3001
|
}
|
|
3002
3002
|
function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) {
|
|
3003
3003
|
const { indent, options: { commentString } } = ctx;
|
|
@@ -3022,7 +3022,7 @@ var require_stringifyCollection = __commonJS({
|
|
|
3022
3022
|
}
|
|
3023
3023
|
}
|
|
3024
3024
|
chompKeep = false;
|
|
3025
|
-
let str2 =
|
|
3025
|
+
let str2 = stringify3.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
|
|
3026
3026
|
if (comment2)
|
|
3027
3027
|
str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2));
|
|
3028
3028
|
if (chompKeep && comment2)
|
|
@@ -3089,7 +3089,7 @@ ${indent}${line}` : "\n";
|
|
|
3089
3089
|
}
|
|
3090
3090
|
if (comment)
|
|
3091
3091
|
reqNewline = true;
|
|
3092
|
-
let str =
|
|
3092
|
+
let str = stringify3.stringify(item, itemCtx, () => comment = null);
|
|
3093
3093
|
reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n"));
|
|
3094
3094
|
if (i < items.length - 1) {
|
|
3095
3095
|
str += ",";
|
|
@@ -4450,7 +4450,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
4450
4450
|
"node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) {
|
|
4451
4451
|
"use strict";
|
|
4452
4452
|
var identity = require_identity();
|
|
4453
|
-
var
|
|
4453
|
+
var stringify3 = require_stringify();
|
|
4454
4454
|
var stringifyComment = require_stringifyComment();
|
|
4455
4455
|
function stringifyDocument(doc, options) {
|
|
4456
4456
|
const lines = [];
|
|
@@ -4465,7 +4465,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
4465
4465
|
}
|
|
4466
4466
|
if (hasDirectives)
|
|
4467
4467
|
lines.push("---");
|
|
4468
|
-
const ctx =
|
|
4468
|
+
const ctx = stringify3.createStringifyContext(doc, options);
|
|
4469
4469
|
const { commentString } = ctx.options;
|
|
4470
4470
|
if (doc.commentBefore) {
|
|
4471
4471
|
if (lines.length !== 1)
|
|
@@ -4487,7 +4487,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
4487
4487
|
contentComment = doc.contents.comment;
|
|
4488
4488
|
}
|
|
4489
4489
|
const onChompKeep = contentComment ? void 0 : () => chompKeep = true;
|
|
4490
|
-
let body =
|
|
4490
|
+
let body = stringify3.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
|
|
4491
4491
|
if (contentComment)
|
|
4492
4492
|
body += stringifyComment.lineComment(body, "", commentString(contentComment));
|
|
4493
4493
|
if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") {
|
|
@@ -4495,7 +4495,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
4495
4495
|
} else
|
|
4496
4496
|
lines.push(body);
|
|
4497
4497
|
} else {
|
|
4498
|
-
lines.push(
|
|
4498
|
+
lines.push(stringify3.stringify(doc.contents, ctx));
|
|
4499
4499
|
}
|
|
4500
4500
|
if (doc.directives?.docEnd) {
|
|
4501
4501
|
if (doc.comment) {
|
|
@@ -6631,7 +6631,7 @@ var require_cst_scalar = __commonJS({
|
|
|
6631
6631
|
var require_cst_stringify = __commonJS({
|
|
6632
6632
|
"node_modules/yaml/dist/parse/cst-stringify.js"(exports) {
|
|
6633
6633
|
"use strict";
|
|
6634
|
-
var
|
|
6634
|
+
var stringify3 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst);
|
|
6635
6635
|
function stringifyToken(token) {
|
|
6636
6636
|
switch (token.type) {
|
|
6637
6637
|
case "block-scalar": {
|
|
@@ -6684,7 +6684,7 @@ var require_cst_stringify = __commonJS({
|
|
|
6684
6684
|
res += stringifyToken(value);
|
|
6685
6685
|
return res;
|
|
6686
6686
|
}
|
|
6687
|
-
exports.stringify =
|
|
6687
|
+
exports.stringify = stringify3;
|
|
6688
6688
|
}
|
|
6689
6689
|
});
|
|
6690
6690
|
|
|
@@ -8395,7 +8395,7 @@ var require_public_api = __commonJS({
|
|
|
8395
8395
|
}
|
|
8396
8396
|
return doc;
|
|
8397
8397
|
}
|
|
8398
|
-
function
|
|
8398
|
+
function parse3(src, reviver, options) {
|
|
8399
8399
|
let _reviver = void 0;
|
|
8400
8400
|
if (typeof reviver === "function") {
|
|
8401
8401
|
_reviver = reviver;
|
|
@@ -8414,7 +8414,7 @@ var require_public_api = __commonJS({
|
|
|
8414
8414
|
}
|
|
8415
8415
|
return doc.toJS(Object.assign({ reviver: _reviver }, options));
|
|
8416
8416
|
}
|
|
8417
|
-
function
|
|
8417
|
+
function stringify3(value, replacer, options) {
|
|
8418
8418
|
let _replacer = null;
|
|
8419
8419
|
if (typeof replacer === "function" || Array.isArray(replacer)) {
|
|
8420
8420
|
_replacer = replacer;
|
|
@@ -8436,10 +8436,10 @@ var require_public_api = __commonJS({
|
|
|
8436
8436
|
return value.toString(options);
|
|
8437
8437
|
return new Document.Document(value, _replacer, options).toString(options);
|
|
8438
8438
|
}
|
|
8439
|
-
exports.parse =
|
|
8439
|
+
exports.parse = parse3;
|
|
8440
8440
|
exports.parseAllDocuments = parseAllDocuments;
|
|
8441
8441
|
exports.parseDocument = parseDocument;
|
|
8442
|
-
exports.stringify =
|
|
8442
|
+
exports.stringify = stringify3;
|
|
8443
8443
|
}
|
|
8444
8444
|
});
|
|
8445
8445
|
|
|
@@ -8844,34 +8844,331 @@ var init_memory = __esm({
|
|
|
8844
8844
|
}
|
|
8845
8845
|
});
|
|
8846
8846
|
|
|
8847
|
-
// plugins/kxm/src/
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
function
|
|
8855
|
-
|
|
8856
|
-
|
|
8857
|
-
|
|
8858
|
-
if (
|
|
8859
|
-
|
|
8860
|
-
|
|
8847
|
+
// plugins/kxm/src/config.ts
|
|
8848
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
|
|
8849
|
+
import { tmpdir, homedir as homedir3 } from "node:os";
|
|
8850
|
+
import { dirname as dirname3, join as join6, resolve as resolve5 } from "node:path";
|
|
8851
|
+
function isHubKeyEnvName(value) {
|
|
8852
|
+
return HUB_ENV_NAME.test(value);
|
|
8853
|
+
}
|
|
8854
|
+
function isHubOpReference(value) {
|
|
8855
|
+
return HUB_OP_REF.test(value);
|
|
8856
|
+
}
|
|
8857
|
+
function userConfigDirectory(overrideDir) {
|
|
8858
|
+
if (overrideDir) return resolve5(overrideDir);
|
|
8859
|
+
return resolve5(process.env.KXM_USER_CONFIG_DIR?.trim() || join6(homedir3(), ".config", "kxm"));
|
|
8860
|
+
}
|
|
8861
|
+
function repoConfigDirectory(repoRoot) {
|
|
8862
|
+
return resolve5(repoRoot, ".kxm");
|
|
8863
|
+
}
|
|
8864
|
+
function deepMerge(target, source) {
|
|
8865
|
+
const result = { ...target };
|
|
8866
|
+
for (const [key, val] of Object.entries(source)) {
|
|
8867
|
+
if (val && typeof val === "object" && !Array.isArray(val)) {
|
|
8868
|
+
const existing = result[key] && typeof result[key] === "object" && !Array.isArray(result[key]) ? result[key] : {};
|
|
8869
|
+
result[key] = deepMerge(existing, val);
|
|
8870
|
+
} else if (val !== void 0) {
|
|
8871
|
+
result[key] = val;
|
|
8872
|
+
}
|
|
8873
|
+
}
|
|
8874
|
+
return result;
|
|
8875
|
+
}
|
|
8876
|
+
function literalSecretMessage(path) {
|
|
8877
|
+
return `refusing literal hub token at ${path}: store an op:// reference (key.op) or an environment variable name (key.env), never the token`;
|
|
8878
|
+
}
|
|
8879
|
+
function rejectLiteralHubSecrets(value, path) {
|
|
8880
|
+
if (Array.isArray(value)) {
|
|
8881
|
+
value.forEach((item, index) => rejectLiteralHubSecrets(item, `${path}[${index}]`));
|
|
8882
|
+
return;
|
|
8883
|
+
}
|
|
8884
|
+
if (!value || typeof value !== "object") return;
|
|
8885
|
+
for (const [key, child] of Object.entries(value)) {
|
|
8886
|
+
const childPath = `${path}.${key}`;
|
|
8887
|
+
if (typeof child === "string" && (key === "key" || LITERAL_SECRET_KEY.test(key))) {
|
|
8888
|
+
throw new KxmHubConfigError(literalSecretMessage(childPath));
|
|
8889
|
+
}
|
|
8890
|
+
if (child && typeof child === "object") rejectLiteralHubSecrets(child, childPath);
|
|
8891
|
+
}
|
|
8892
|
+
}
|
|
8893
|
+
function parseKeyRef(raw, path) {
|
|
8894
|
+
if (raw === void 0) return void 0;
|
|
8895
|
+
if (typeof raw === "string") throw new KxmHubConfigError(literalSecretMessage(path));
|
|
8896
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
8897
|
+
throw new KxmHubConfigError(`${path} must be a key reference with op and/or env, never a token`);
|
|
8898
|
+
}
|
|
8899
|
+
const row = raw;
|
|
8900
|
+
for (const key of Object.keys(row)) {
|
|
8901
|
+
if (key !== "op" && key !== "env") {
|
|
8902
|
+
throw new KxmHubConfigError(literalSecretMessage(`${path}.${key}`));
|
|
8903
|
+
}
|
|
8904
|
+
}
|
|
8905
|
+
const ref = {};
|
|
8906
|
+
if (row.op !== void 0) {
|
|
8907
|
+
if (typeof row.op !== "string" || !isHubOpReference(row.op)) {
|
|
8908
|
+
throw new KxmHubConfigError(`${path}.op must be an op://vault/item/field reference, not a token`);
|
|
8909
|
+
}
|
|
8910
|
+
ref.op = row.op;
|
|
8911
|
+
}
|
|
8912
|
+
if (row.env !== void 0) {
|
|
8913
|
+
if (typeof row.env !== "string" || !isHubKeyEnvName(row.env)) {
|
|
8914
|
+
throw new KxmHubConfigError(`${path}.env must be an environment variable name, not a token`);
|
|
8915
|
+
}
|
|
8916
|
+
ref.env = row.env;
|
|
8917
|
+
}
|
|
8918
|
+
if (!ref.op && !ref.env) throw new KxmHubConfigError(`${path} needs key.op, key.env, or both`);
|
|
8919
|
+
return ref;
|
|
8920
|
+
}
|
|
8921
|
+
function parseEndpoint(raw, path) {
|
|
8922
|
+
if (raw === void 0) return void 0;
|
|
8923
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
8924
|
+
throw new KxmHubConfigError(`${path} must be an object with url, project, and key`);
|
|
8925
|
+
}
|
|
8926
|
+
const row = raw;
|
|
8927
|
+
const endpoint = {};
|
|
8928
|
+
if (row.url !== void 0) {
|
|
8929
|
+
if (typeof row.url !== "string" || !isHubUrl(row.url)) {
|
|
8930
|
+
throw new KxmHubConfigError(`${path}.url must be an http or https URL without credentials, query, or fragment`);
|
|
8931
|
+
}
|
|
8932
|
+
endpoint.url = row.url.replace(/\/$/, "");
|
|
8933
|
+
}
|
|
8934
|
+
if (row.project !== void 0) {
|
|
8935
|
+
if (typeof row.project !== "string" || !isHubProjectId(row.project)) {
|
|
8936
|
+
throw new KxmHubConfigError(`${path}.project must be a single-line project id, not a token`);
|
|
8937
|
+
}
|
|
8938
|
+
endpoint.project = row.project.trim();
|
|
8939
|
+
}
|
|
8940
|
+
const key = parseKeyRef(row.key, `${path}.key`);
|
|
8941
|
+
if (key) endpoint.key = key;
|
|
8942
|
+
for (const name of Object.keys(row)) {
|
|
8943
|
+
if (name !== "url" && name !== "project" && name !== "key") {
|
|
8944
|
+
if (typeof row[name] === "string") throw new KxmHubConfigError(literalSecretMessage(`${path}.${name}`));
|
|
8945
|
+
throw new KxmHubConfigError(`${path}.${name} is not a hub endpoint field`);
|
|
8946
|
+
}
|
|
8947
|
+
}
|
|
8948
|
+
return endpoint;
|
|
8949
|
+
}
|
|
8950
|
+
function isHubUrl(raw) {
|
|
8861
8951
|
try {
|
|
8862
|
-
const
|
|
8863
|
-
|
|
8952
|
+
const parsed = new URL(raw);
|
|
8953
|
+
return (parsed.protocol === "http:" || parsed.protocol === "https:") && parsed.username === "" && parsed.password === "" && parsed.search === "" && parsed.hash === "" && !raw.includes("?") && !raw.includes("#");
|
|
8864
8954
|
} catch {
|
|
8955
|
+
return false;
|
|
8865
8956
|
}
|
|
8866
|
-
return basename2(cwd);
|
|
8867
8957
|
}
|
|
8868
|
-
function
|
|
8958
|
+
function isHubProjectId(raw) {
|
|
8959
|
+
const value = raw.trim();
|
|
8960
|
+
return value.length > 0 && value.length <= 200 && !/[\s\r\n]/.test(value);
|
|
8961
|
+
}
|
|
8962
|
+
function parseProjectMap(raw) {
|
|
8963
|
+
if (raw === void 0) return void 0;
|
|
8964
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
8965
|
+
throw new KxmHubConfigError("hub.projects must be a map of project id to key reference, never a token");
|
|
8966
|
+
}
|
|
8967
|
+
const projects = {};
|
|
8968
|
+
for (const [project, value] of Object.entries(raw)) {
|
|
8969
|
+
if (!isHubProjectId(project)) {
|
|
8970
|
+
throw new KxmHubConfigError(`hub.projects key ${JSON.stringify(project)} must be a project id, not a token`);
|
|
8971
|
+
}
|
|
8972
|
+
const ref = parseKeyRef(value, `hub.projects.${project}`);
|
|
8973
|
+
if (!ref) throw new KxmHubConfigError(`hub.projects.${project} needs an op:// reference or an environment variable name`);
|
|
8974
|
+
projects[project.trim()] = ref;
|
|
8975
|
+
}
|
|
8976
|
+
return projects;
|
|
8977
|
+
}
|
|
8978
|
+
function normalizeHubConfig(raw) {
|
|
8979
|
+
const autoStart = raw?.autoStart;
|
|
8980
|
+
const hub = {
|
|
8981
|
+
autoStart: autoStart === "off" || autoStart === "background" ? autoStart : DEFAULT_KXM_CONFIG.hub.autoStart
|
|
8982
|
+
};
|
|
8983
|
+
if (raw === void 0) return hub;
|
|
8984
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
8985
|
+
throw new KxmHubConfigError("hub must be an object");
|
|
8986
|
+
}
|
|
8987
|
+
rejectLiteralHubSecrets(raw, "hub");
|
|
8988
|
+
const row = raw;
|
|
8989
|
+
if (row.mode !== void 0) {
|
|
8990
|
+
if (row.mode !== "local" && row.mode !== "cloud") {
|
|
8991
|
+
throw new KxmHubConfigError("hub.mode must be local or cloud");
|
|
8992
|
+
}
|
|
8993
|
+
hub.mode = row.mode;
|
|
8994
|
+
}
|
|
8995
|
+
const local = parseEndpoint(row.local, "hub.local");
|
|
8996
|
+
const cloud = parseEndpoint(row.cloud, "hub.cloud");
|
|
8997
|
+
const projects = parseProjectMap(row.projects);
|
|
8998
|
+
if (local) hub.local = local;
|
|
8999
|
+
if (cloud) hub.cloud = cloud;
|
|
9000
|
+
if (projects && Object.keys(projects).length > 0) hub.projects = projects;
|
|
9001
|
+
return hub;
|
|
9002
|
+
}
|
|
9003
|
+
function findConfigRoot(start) {
|
|
8869
9004
|
let dir = resolve5(start);
|
|
8870
9005
|
for (; ; ) {
|
|
8871
|
-
|
|
8872
|
-
|
|
9006
|
+
if (existsSync7(join6(dir, ".kxm", "project.yaml")) || existsSync7(join6(dir, ".kxm", "config.yaml"))) return dir;
|
|
9007
|
+
const parent = dirname3(dir);
|
|
9008
|
+
if (parent === dir) return resolve5(start);
|
|
9009
|
+
dir = parent;
|
|
9010
|
+
}
|
|
9011
|
+
}
|
|
9012
|
+
function userConfigDirForEnv(env) {
|
|
9013
|
+
const explicit = env.KXM_USER_CONFIG_DIR?.trim();
|
|
9014
|
+
if (explicit) return explicit;
|
|
9015
|
+
if (env === process.env) return void 0;
|
|
9016
|
+
return join6(tmpdir(), "kxm-no-user-config");
|
|
9017
|
+
}
|
|
9018
|
+
function loadHubSettings(start, env = process.env) {
|
|
9019
|
+
const userConfigDir = userConfigDirForEnv(env);
|
|
9020
|
+
return loadKxmConfig(findConfigRoot(start), userConfigDir === void 0 ? {} : { userConfigDir }).hub;
|
|
9021
|
+
}
|
|
9022
|
+
function recordOf(raw) {
|
|
9023
|
+
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
9024
|
+
}
|
|
9025
|
+
function finiteNumber(value) {
|
|
9026
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
9027
|
+
}
|
|
9028
|
+
function normalizeImprovementConfig(raw) {
|
|
9029
|
+
const value = recordOf(raw);
|
|
9030
|
+
const threshold = recordOf(value.autoThreshold);
|
|
9031
|
+
const policy = value.promotionPolicy;
|
|
9032
|
+
const halfLife = finiteNumber(value.telemetryHalfLifeDays);
|
|
9033
|
+
const minRuns = finiteNumber(threshold.minRuns);
|
|
9034
|
+
const minPassRate = finiteNumber(threshold.minPassRate);
|
|
9035
|
+
const minCostSavings = finiteNumber(threshold.minCostSavings);
|
|
9036
|
+
return {
|
|
9037
|
+
promotionPolicy: IMPROVEMENT_PROMOTION_POLICIES.includes(policy) ? policy : "manual_pr",
|
|
9038
|
+
telemetryHalfLifeDays: halfLife !== void 0 && halfLife > 0 && halfLife <= 3650 ? halfLife : 14,
|
|
9039
|
+
autoThreshold: {
|
|
9040
|
+
minRuns: minRuns !== void 0 && Number.isInteger(minRuns) && minRuns >= 1 && minRuns <= 1e6 ? minRuns : 10,
|
|
9041
|
+
minPassRate: minPassRate !== void 0 && minPassRate >= 0 && minPassRate <= 1 ? minPassRate : 0.95,
|
|
9042
|
+
minCostSavings: minCostSavings !== void 0 && minCostSavings >= 0 ? minCostSavings : 0.5
|
|
9043
|
+
}
|
|
9044
|
+
};
|
|
9045
|
+
}
|
|
9046
|
+
function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
|
|
9047
|
+
const userDir = userConfigDirectory(options.userConfigDir);
|
|
9048
|
+
const userConfigFile = join6(userDir, "config.yaml");
|
|
9049
|
+
const repoDir = repoConfigDirectory(repoRoot);
|
|
9050
|
+
const repoConfigFile = join6(repoDir, "config.yaml");
|
|
9051
|
+
let userRaw = {};
|
|
9052
|
+
let userLoadedPath;
|
|
9053
|
+
if (existsSync7(userConfigFile)) {
|
|
9054
|
+
try {
|
|
9055
|
+
const text = readFileSync7(userConfigFile, "utf8");
|
|
9056
|
+
userRaw = (0, import_yaml2.parse)(text) ?? {};
|
|
9057
|
+
userLoadedPath = userConfigFile;
|
|
9058
|
+
} catch (error) {
|
|
9059
|
+
throw new Error(`invalid user config YAML at ${userConfigFile}`, { cause: error });
|
|
9060
|
+
}
|
|
9061
|
+
}
|
|
9062
|
+
let repoRaw = {};
|
|
9063
|
+
let repoLoadedPath;
|
|
9064
|
+
if (existsSync7(repoConfigFile)) {
|
|
9065
|
+
try {
|
|
9066
|
+
const text = readFileSync7(repoConfigFile, "utf8");
|
|
9067
|
+
repoRaw = (0, import_yaml2.parse)(text) ?? {};
|
|
9068
|
+
repoLoadedPath = repoConfigFile;
|
|
9069
|
+
} catch (error) {
|
|
9070
|
+
throw new Error(`invalid project config YAML at ${repoConfigFile}`, { cause: error });
|
|
9071
|
+
}
|
|
9072
|
+
}
|
|
9073
|
+
const baseCopy = JSON.parse(JSON.stringify(DEFAULT_KXM_CONFIG));
|
|
9074
|
+
const mergedUser = deepMerge(baseCopy, userRaw);
|
|
9075
|
+
const mergedAll = deepMerge(mergedUser, repoRaw);
|
|
9076
|
+
return {
|
|
9077
|
+
schema: KXM_CONFIG_SCHEMA,
|
|
9078
|
+
user: mergedAll.user ?? {},
|
|
9079
|
+
defaults: mergedAll.defaults ?? {},
|
|
9080
|
+
dash: mergedAll.dash ?? {},
|
|
9081
|
+
sync: mergedAll.sync ?? {},
|
|
9082
|
+
hub: normalizeHubConfig(mergedAll.hub),
|
|
9083
|
+
improvement: normalizeImprovementConfig(mergedAll.improvement),
|
|
9084
|
+
routing: mergedAll.routing ?? DEFAULT_KXM_CONFIG.routing,
|
|
9085
|
+
telemetry: mergedAll.telemetry ?? DEFAULT_KXM_CONFIG.telemetry,
|
|
9086
|
+
loadedFrom: {
|
|
9087
|
+
userConfigPath: userLoadedPath,
|
|
9088
|
+
repoConfigPath: repoLoadedPath
|
|
9089
|
+
}
|
|
9090
|
+
};
|
|
9091
|
+
}
|
|
9092
|
+
var import_yaml2, KXM_CONFIG_SCHEMA, KxmHubConfigError, HUB_ENV_NAME, HUB_OP_REF, LITERAL_SECRET_KEY, IMPROVEMENT_PROMOTION_POLICIES, DEFAULT_KXM_CONFIG;
|
|
9093
|
+
var init_config = __esm({
|
|
9094
|
+
"plugins/kxm/src/config.ts"() {
|
|
9095
|
+
"use strict";
|
|
9096
|
+
import_yaml2 = __toESM(require_dist(), 1);
|
|
9097
|
+
KXM_CONFIG_SCHEMA = "kxm.config.v1";
|
|
9098
|
+
KxmHubConfigError = class extends Error {
|
|
9099
|
+
code = "hub_config_invalid";
|
|
9100
|
+
constructor(message) {
|
|
9101
|
+
super(message);
|
|
9102
|
+
this.name = "KxmHubConfigError";
|
|
9103
|
+
}
|
|
9104
|
+
};
|
|
9105
|
+
HUB_ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
|
|
9106
|
+
HUB_OP_REF = /^op:\/\/\S+\/\S+\/\S+$/;
|
|
9107
|
+
LITERAL_SECRET_KEY = /^(token|secret|password|authToken|auth_token|apiKey|api_key|api_token|bearer|credential|access_token)$/i;
|
|
9108
|
+
IMPROVEMENT_PROMOTION_POLICIES = ["manual_pr", "critic_quorum", "auto_threshold"];
|
|
9109
|
+
DEFAULT_KXM_CONFIG = {
|
|
9110
|
+
schema: KXM_CONFIG_SCHEMA,
|
|
9111
|
+
user: {
|
|
9112
|
+
theme: "dark",
|
|
9113
|
+
preferredCritics: ["reviewer-arch", "reviewer-cli"],
|
|
9114
|
+
tokenBudget: 16e3
|
|
9115
|
+
},
|
|
9116
|
+
defaults: {
|
|
9117
|
+
workflow: "software-engineering/feature-implementation",
|
|
9118
|
+
harness: "pi"
|
|
9119
|
+
},
|
|
9120
|
+
dash: {
|
|
9121
|
+
defaultScreen: "agents",
|
|
9122
|
+
refreshIntervalMs: 1e3,
|
|
9123
|
+
autoOpen: false
|
|
9124
|
+
},
|
|
9125
|
+
sync: {
|
|
9126
|
+
defaultTracker: "none"
|
|
9127
|
+
},
|
|
9128
|
+
hub: {
|
|
9129
|
+
autoStart: "background"
|
|
9130
|
+
},
|
|
9131
|
+
improvement: {
|
|
9132
|
+
promotionPolicy: "manual_pr",
|
|
9133
|
+
telemetryHalfLifeDays: 14,
|
|
9134
|
+
autoThreshold: {
|
|
9135
|
+
minRuns: 10,
|
|
9136
|
+
minPassRate: 0.95,
|
|
9137
|
+
minCostSavings: 0.5
|
|
9138
|
+
}
|
|
9139
|
+
},
|
|
9140
|
+
routing: {
|
|
9141
|
+
shadowExecution: {
|
|
9142
|
+
enabled: false,
|
|
9143
|
+
sampleRate: 0.05,
|
|
9144
|
+
candidateModels: []
|
|
9145
|
+
},
|
|
9146
|
+
circuitBreaker: {
|
|
9147
|
+
mode: "soft_demotion",
|
|
9148
|
+
failureThreshold: 3,
|
|
9149
|
+
windowSeconds: 3600,
|
|
9150
|
+
cooldownSeconds: 1800,
|
|
9151
|
+
penaltyMultiplier: 5
|
|
9152
|
+
}
|
|
9153
|
+
},
|
|
9154
|
+
telemetry: {
|
|
9155
|
+
federated: true,
|
|
9156
|
+
anonymize: true
|
|
9157
|
+
}
|
|
9158
|
+
};
|
|
9159
|
+
}
|
|
9160
|
+
});
|
|
9161
|
+
|
|
9162
|
+
// plugins/kxm/src/hub-identity.ts
|
|
9163
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8 } from "node:fs";
|
|
9164
|
+
import { basename as basename2, dirname as dirname4, join as join7, resolve as resolve6 } from "node:path";
|
|
9165
|
+
function projectYamlId(start) {
|
|
9166
|
+
let dir = resolve6(start);
|
|
9167
|
+
for (; ; ) {
|
|
9168
|
+
const file = join7(dir, ".kxm", "project.yaml");
|
|
9169
|
+
if (existsSync8(file)) {
|
|
8873
9170
|
try {
|
|
8874
|
-
const doc = (0,
|
|
9171
|
+
const doc = (0, import_yaml3.parse)(readFileSync8(file, "utf8"));
|
|
8875
9172
|
if (doc && typeof doc === "object" && !Array.isArray(doc) && typeof doc.id === "string" && doc.id.trim()) {
|
|
8876
9173
|
return doc.id.trim();
|
|
8877
9174
|
}
|
|
@@ -8879,16 +9176,70 @@ function readProjectYamlId(start) {
|
|
|
8879
9176
|
}
|
|
8880
9177
|
return void 0;
|
|
8881
9178
|
}
|
|
8882
|
-
const parent =
|
|
9179
|
+
const parent = dirname4(dir);
|
|
8883
9180
|
if (parent === dir) return void 0;
|
|
8884
9181
|
dir = parent;
|
|
8885
9182
|
}
|
|
8886
9183
|
}
|
|
8887
|
-
|
|
9184
|
+
function packageName(cwd) {
|
|
9185
|
+
try {
|
|
9186
|
+
const pkg = JSON.parse(readFileSync8(join7(cwd, "package.json"), "utf8"));
|
|
9187
|
+
if (typeof pkg.name === "string" && pkg.name.trim().length > 0) return pkg.name.trim();
|
|
9188
|
+
} catch {
|
|
9189
|
+
}
|
|
9190
|
+
return void 0;
|
|
9191
|
+
}
|
|
9192
|
+
function activeBinding(env) {
|
|
9193
|
+
try {
|
|
9194
|
+
return readHubBinding(env);
|
|
9195
|
+
} catch (error) {
|
|
9196
|
+
if (error instanceof HubBindingError) return void 0;
|
|
9197
|
+
throw error;
|
|
9198
|
+
}
|
|
9199
|
+
}
|
|
9200
|
+
function endpointFor(mode, settings) {
|
|
9201
|
+
return mode === "cloud" ? settings.cloud : settings.local;
|
|
9202
|
+
}
|
|
9203
|
+
function resolveProjectIdentity(cwd, env = process.env, explicit) {
|
|
9204
|
+
const fromExplicit = explicit?.trim();
|
|
9205
|
+
if (fromExplicit) return { project: fromExplicit, source: "flag", sourceLabel: "--project" };
|
|
9206
|
+
const fromEnv = env.KXM_PROJECT?.trim();
|
|
9207
|
+
if (fromEnv) return { project: fromEnv, source: "env", sourceLabel: "KXM_PROJECT" };
|
|
9208
|
+
const settings = loadHubSettings(cwd, env);
|
|
9209
|
+
const mode = settings.mode ?? (activeBinding(env)?.cloud ? "cloud" : "local");
|
|
9210
|
+
const fromConfig = endpointFor(mode, settings)?.project?.trim();
|
|
9211
|
+
if (fromConfig) {
|
|
9212
|
+
return { project: fromConfig, source: "config", sourceLabel: `.kxm/config.yaml hub.${mode}.project` };
|
|
9213
|
+
}
|
|
9214
|
+
const fromProject = projectYamlId(cwd);
|
|
9215
|
+
if (fromProject) return { project: fromProject, source: "project.yaml", sourceLabel: ".kxm/project.yaml id" };
|
|
9216
|
+
const fromPackage = packageName(cwd);
|
|
9217
|
+
if (fromPackage) return { project: fromPackage, source: "package.json", sourceLabel: "package.json name" };
|
|
9218
|
+
return { project: basename2(cwd), source: "directory", sourceLabel: "directory name" };
|
|
9219
|
+
}
|
|
9220
|
+
function defaultProjectName(cwd, env = process.env, explicit) {
|
|
9221
|
+
return resolveProjectIdentity(cwd, env, explicit).project;
|
|
9222
|
+
}
|
|
9223
|
+
var import_yaml3;
|
|
9224
|
+
var init_hub_identity = __esm({
|
|
9225
|
+
"plugins/kxm/src/hub-identity.ts"() {
|
|
9226
|
+
"use strict";
|
|
9227
|
+
import_yaml3 = __toESM(require_dist(), 1);
|
|
9228
|
+
init_config();
|
|
9229
|
+
init_hub_binding();
|
|
9230
|
+
}
|
|
9231
|
+
});
|
|
9232
|
+
|
|
9233
|
+
// plugins/kxm/src/project-name.ts
|
|
9234
|
+
var project_name_exports = {};
|
|
9235
|
+
__export(project_name_exports, {
|
|
9236
|
+
defaultProjectName: () => defaultProjectName,
|
|
9237
|
+
resolveProjectIdentity: () => resolveProjectIdentity
|
|
9238
|
+
});
|
|
8888
9239
|
var init_project_name = __esm({
|
|
8889
9240
|
"plugins/kxm/src/project-name.ts"() {
|
|
8890
9241
|
"use strict";
|
|
8891
|
-
|
|
9242
|
+
init_hub_identity();
|
|
8892
9243
|
}
|
|
8893
9244
|
});
|
|
8894
9245
|
|
|
@@ -9034,9 +9385,9 @@ __export(commands_exports, {
|
|
|
9034
9385
|
timingSafeStringCompare: () => timingSafeStringCompare
|
|
9035
9386
|
});
|
|
9036
9387
|
import { createHash, randomUUID as randomUUID3, timingSafeEqual } from "node:crypto";
|
|
9037
|
-
import { chmodSync, existsSync as
|
|
9038
|
-
import { homedir as
|
|
9039
|
-
import { dirname as
|
|
9388
|
+
import { chmodSync, existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync9, unlinkSync, writeFileSync as writeFileSync6 } from "node:fs";
|
|
9389
|
+
import { homedir as homedir4 } from "node:os";
|
|
9390
|
+
import { dirname as dirname5, join as join8, resolve as resolve7 } from "node:path";
|
|
9040
9391
|
function forwardedHops(handling) {
|
|
9041
9392
|
if (!handling?.length) return void 0;
|
|
9042
9393
|
return {
|
|
@@ -9218,18 +9569,18 @@ function parseSessionToken(token) {
|
|
|
9218
9569
|
return void 0;
|
|
9219
9570
|
}
|
|
9220
9571
|
function resolveUserConfigDirectory(overrideDir) {
|
|
9221
|
-
if (overrideDir) return
|
|
9222
|
-
return
|
|
9572
|
+
if (overrideDir) return resolve7(overrideDir);
|
|
9573
|
+
return resolve7(process.env.KXM_USER_CONFIG_DIR?.trim() || join8(homedir4(), ".config", "kxm"));
|
|
9223
9574
|
}
|
|
9224
9575
|
function sessionTokenPath(userConfigDir) {
|
|
9225
|
-
return
|
|
9576
|
+
return join8(resolveUserConfigDirectory(userConfigDir), "session.token");
|
|
9226
9577
|
}
|
|
9227
9578
|
function persistSessionTokenToDisk(token, options) {
|
|
9228
9579
|
const filePath = sessionTokenPath(options?.userConfigDir);
|
|
9229
|
-
const dir =
|
|
9230
|
-
|
|
9580
|
+
const dir = dirname5(filePath);
|
|
9581
|
+
mkdirSync7(dir, { recursive: true, mode: 448 });
|
|
9231
9582
|
const mode = options?.mode ?? 384;
|
|
9232
|
-
|
|
9583
|
+
writeFileSync6(filePath, `${token.trim()}
|
|
9233
9584
|
`, { encoding: "utf8", mode });
|
|
9234
9585
|
try {
|
|
9235
9586
|
chmodSync(filePath, mode);
|
|
@@ -9239,9 +9590,9 @@ function persistSessionTokenToDisk(token, options) {
|
|
|
9239
9590
|
}
|
|
9240
9591
|
function readSessionTokenFromDisk(options) {
|
|
9241
9592
|
const filePath = sessionTokenPath(options?.userConfigDir);
|
|
9242
|
-
if (!
|
|
9593
|
+
if (!existsSync9(filePath)) return void 0;
|
|
9243
9594
|
try {
|
|
9244
|
-
const token =
|
|
9595
|
+
const token = readFileSync9(filePath, "utf8").trim();
|
|
9245
9596
|
if (!token) return void 0;
|
|
9246
9597
|
const payload = parseSessionToken(token);
|
|
9247
9598
|
if (!payload) return void 0;
|
|
@@ -9252,7 +9603,7 @@ function readSessionTokenFromDisk(options) {
|
|
|
9252
9603
|
}
|
|
9253
9604
|
function clearSessionTokenFromDisk(options) {
|
|
9254
9605
|
const filePath = sessionTokenPath(options?.userConfigDir);
|
|
9255
|
-
if (!
|
|
9606
|
+
if (!existsSync9(filePath)) return false;
|
|
9256
9607
|
try {
|
|
9257
9608
|
unlinkSync(filePath);
|
|
9258
9609
|
return true;
|
|
@@ -9351,10 +9702,10 @@ function enforceToolPolicy(commandName, env = process.env, options) {
|
|
|
9351
9702
|
return { allowed: true };
|
|
9352
9703
|
}
|
|
9353
9704
|
const tokenFile = sessionTokenPath(env.KXM_USER_CONFIG_DIR);
|
|
9354
|
-
if (
|
|
9705
|
+
if (existsSync9(tokenFile)) {
|
|
9355
9706
|
let tokenRaw;
|
|
9356
9707
|
try {
|
|
9357
|
-
tokenRaw =
|
|
9708
|
+
tokenRaw = readFileSync9(tokenFile, "utf8").trim();
|
|
9358
9709
|
} catch {
|
|
9359
9710
|
return { allowed: false, error: "session_token_invalid", detail: "Session token file on disk could not be read" };
|
|
9360
9711
|
}
|
|
@@ -10040,8 +10391,8 @@ var init_commands = __esm({
|
|
|
10040
10391
|
});
|
|
10041
10392
|
|
|
10042
10393
|
// plugins/kxm/src/claude-hook.ts
|
|
10043
|
-
import { readFileSync as
|
|
10044
|
-
import { join as
|
|
10394
|
+
import { readFileSync as readFileSync10, statSync } from "node:fs";
|
|
10395
|
+
import { join as join9, resolve as resolve8 } from "node:path";
|
|
10045
10396
|
|
|
10046
10397
|
// plugins/kxm/src/session-token-hint.ts
|
|
10047
10398
|
var ENV_TEXT = "KXM_SESSION_TOKEN in the environment Claude Code was launched from is malformed or expired, so every kxm_* tool fails with tool_policy_denied. Ask the user to unset or replace KXM_SESSION_TOKEN in the environment Claude Code was launched from, then restart Claude Code.";
|
|
@@ -10114,15 +10465,15 @@ function truncate2(text, max) {
|
|
|
10114
10465
|
}
|
|
10115
10466
|
async function sessionStartContext(input, env) {
|
|
10116
10467
|
const inputCwd = typeof input.cwd === "string" ? input.cwd.trim() : "";
|
|
10117
|
-
const root =
|
|
10118
|
-
if (!isDirectory(
|
|
10468
|
+
const root = resolve8(env.CLAUDE_PROJECT_DIR?.trim() || inputCwd || process.cwd());
|
|
10469
|
+
if (!isDirectory(join9(root, ".kxm"))) return void 0;
|
|
10119
10470
|
const [
|
|
10120
10471
|
{ resolveSessionHubStatus: resolveSessionHubStatus2 },
|
|
10121
10472
|
{ loadLocalMeshSnapshot: loadLocalMeshSnapshot2, resolveKxmSnapshotPaths: resolveKxmSnapshotPaths2 },
|
|
10122
10473
|
{ formatMemoryBriefText: formatMemoryBriefText2, generateMemoryBrief: generateMemoryBrief2 },
|
|
10123
10474
|
{ defaultProjectName: defaultProjectName2 },
|
|
10124
10475
|
{ enforceToolPolicy: enforceToolPolicy2 },
|
|
10125
|
-
{ parse:
|
|
10476
|
+
{ parse: parse3 }
|
|
10126
10477
|
] = await Promise.all([
|
|
10127
10478
|
Promise.resolve().then(() => (init_session_work(), session_work_exports)),
|
|
10128
10479
|
Promise.resolve().then(() => (init_local_snapshot(), local_snapshot_exports)),
|
|
@@ -10136,7 +10487,7 @@ async function sessionStartContext(input, env) {
|
|
|
10136
10487
|
const url = (env.CLAUDE_PLUGIN_OPTION_SERVER_URL?.trim() || DEFAULT_SERVER_URL).replace(/\/$/, "");
|
|
10137
10488
|
let runtimeProjectId;
|
|
10138
10489
|
try {
|
|
10139
|
-
const project =
|
|
10490
|
+
const project = parse3(readFileSync10(join9(root, ".kxm", "project.yaml"), "utf8"));
|
|
10140
10491
|
if (typeof project?.id === "string" && project.id.trim()) runtimeProjectId = project.id.trim();
|
|
10141
10492
|
} catch {
|
|
10142
10493
|
}
|