@kontextmind/kxm 0.7.53 → 0.7.54
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 +18 -1
- package/docs/agent-skills.md +1 -1
- package/docs/configuration.md +0 -3
- package/docs/contracts/migration.md +50 -258
- package/docs/contracts/validation.md +15 -33
- package/docs/test-matrix.md +1 -1
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +376 -2133
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime-supervisor.js +18 -98
- package/plugins/kxm/dist/runtime.js +18 -98
- package/plugins/kxm/dist/server.js +1 -1
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/kxm/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-project-setup/SKILL.md +4 -7
- package/plugins/kxm/src/autocomplete.ts +0 -3
- package/plugins/kxm/src/cli/project.ts +2 -133
- package/plugins/kxm/src/cli.ts +3 -24
- package/plugins/kxm/src/database.ts +1 -1
- package/plugins/kxm/src/init.ts +10 -6
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/project-config.ts +54 -173
- package/schemas/README.md +0 -3
- package/plugins/kxm/src/migrate.ts +0 -1848
- package/schemas/migration-decision.schema.json +0 -26
- package/schemas/migration-plan.schema.json +0 -123
- package/schemas/migration-receipt.schema.json +0 -52
|
@@ -17121,7 +17121,7 @@ async function deliverInboxNotification(messageId, delivered, notify) {
|
|
|
17121
17121
|
}
|
|
17122
17122
|
|
|
17123
17123
|
// plugins/kxm/src/mcp-server.ts
|
|
17124
|
-
var VERSION = "0.7.
|
|
17124
|
+
var VERSION = "0.7.54";
|
|
17125
17125
|
var inbox = /* @__PURE__ */ new Map();
|
|
17126
17126
|
var notifiedInbox = /* @__PURE__ */ new Set();
|
|
17127
17127
|
var meshClient;
|
|
@@ -16256,9 +16256,6 @@ var KxmSchemaRegistry = class {
|
|
|
16256
16256
|
localBindingsValidator;
|
|
16257
16257
|
templateProvenanceValidator;
|
|
16258
16258
|
initOperationValidator;
|
|
16259
|
-
migrationPlanValidator;
|
|
16260
|
-
migrationDecisionValidator;
|
|
16261
|
-
migrationReceiptValidator;
|
|
16262
16259
|
permissionDiffValidator;
|
|
16263
16260
|
runEventValidator;
|
|
16264
16261
|
driveReceiptValidator;
|
|
@@ -16275,9 +16272,6 @@ var KxmSchemaRegistry = class {
|
|
|
16275
16272
|
const localBindingsFile = "local-repository-bindings.schema.json";
|
|
16276
16273
|
const templateProvenanceFile = "template-provenance.schema.json";
|
|
16277
16274
|
const initOperationFile = "init-operation.schema.json";
|
|
16278
|
-
const migrationPlanFile = "migration-plan.schema.json";
|
|
16279
|
-
const migrationDecisionFile = "migration-decision.schema.json";
|
|
16280
|
-
const migrationReceiptFile = "migration-receipt.schema.json";
|
|
16281
16275
|
const permissionDiffFile = "permission-diff.schema.json";
|
|
16282
16276
|
const runEventFile = "run-event.schema.json";
|
|
16283
16277
|
const driveReceiptFile = "drive-receipt.schema.json";
|
|
@@ -16286,9 +16280,6 @@ var KxmSchemaRegistry = class {
|
|
|
16286
16280
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, localBindingsFile)));
|
|
16287
16281
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, templateProvenanceFile)));
|
|
16288
16282
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, initOperationFile)));
|
|
16289
|
-
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, migrationPlanFile)));
|
|
16290
|
-
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, migrationDecisionFile)));
|
|
16291
|
-
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, migrationReceiptFile)));
|
|
16292
16283
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, permissionDiffFile)));
|
|
16293
16284
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, runEventFile)));
|
|
16294
16285
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, driveReceiptFile)));
|
|
@@ -16302,9 +16293,6 @@ var KxmSchemaRegistry = class {
|
|
|
16302
16293
|
const localBindingsValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${localBindingsFile}`);
|
|
16303
16294
|
const templateProvenanceValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${templateProvenanceFile}`);
|
|
16304
16295
|
const initOperationValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${initOperationFile}`);
|
|
16305
|
-
const migrationPlanValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${migrationPlanFile}`);
|
|
16306
|
-
const migrationDecisionValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${migrationDecisionFile}`);
|
|
16307
|
-
const migrationReceiptValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${migrationReceiptFile}`);
|
|
16308
16296
|
const permissionDiffValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${permissionDiffFile}`);
|
|
16309
16297
|
const runEventValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${runEventFile}`);
|
|
16310
16298
|
const driveReceiptValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${driveReceiptFile}`);
|
|
@@ -16313,9 +16301,6 @@ var KxmSchemaRegistry = class {
|
|
|
16313
16301
|
if (!localBindingsValidator) throw new Error(`schema did not compile: ${localBindingsFile}`);
|
|
16314
16302
|
if (!templateProvenanceValidator) throw new Error(`schema did not compile: ${templateProvenanceFile}`);
|
|
16315
16303
|
if (!initOperationValidator) throw new Error(`schema did not compile: ${initOperationFile}`);
|
|
16316
|
-
if (!migrationPlanValidator) throw new Error(`schema did not compile: ${migrationPlanFile}`);
|
|
16317
|
-
if (!migrationDecisionValidator) throw new Error(`schema did not compile: ${migrationDecisionFile}`);
|
|
16318
|
-
if (!migrationReceiptValidator) throw new Error(`schema did not compile: ${migrationReceiptFile}`);
|
|
16319
16304
|
if (!permissionDiffValidator) throw new Error(`schema did not compile: ${permissionDiffFile}`);
|
|
16320
16305
|
if (!runEventValidator) throw new Error(`schema did not compile: ${runEventFile}`);
|
|
16321
16306
|
if (!driveReceiptValidator) throw new Error(`schema did not compile: ${driveReceiptFile}`);
|
|
@@ -16324,9 +16309,6 @@ var KxmSchemaRegistry = class {
|
|
|
16324
16309
|
this.localBindingsValidator = localBindingsValidator;
|
|
16325
16310
|
this.templateProvenanceValidator = templateProvenanceValidator;
|
|
16326
16311
|
this.initOperationValidator = initOperationValidator;
|
|
16327
|
-
this.migrationPlanValidator = migrationPlanValidator;
|
|
16328
|
-
this.migrationDecisionValidator = migrationDecisionValidator;
|
|
16329
|
-
this.migrationReceiptValidator = migrationReceiptValidator;
|
|
16330
16312
|
this.permissionDiffValidator = permissionDiffValidator;
|
|
16331
16313
|
this.runEventValidator = runEventValidator;
|
|
16332
16314
|
this.driveReceiptValidator = driveReceiptValidator;
|
|
@@ -16352,15 +16334,6 @@ var KxmSchemaRegistry = class {
|
|
|
16352
16334
|
validateInitOperation(value, file) {
|
|
16353
16335
|
return this.validateAuxiliary(value, file, "kxm.init-operation.v1", this.initOperationValidator);
|
|
16354
16336
|
}
|
|
16355
|
-
validateMigrationPlan(value, file) {
|
|
16356
|
-
return this.validateAuxiliary(value, file, "kxm.migration-plan.v1", this.migrationPlanValidator);
|
|
16357
|
-
}
|
|
16358
|
-
validateMigrationDecision(value, file) {
|
|
16359
|
-
return this.validateAuxiliary(value, file, "kxm.migration-decision.v1", this.migrationDecisionValidator);
|
|
16360
|
-
}
|
|
16361
|
-
validateMigrationReceipt(value, file) {
|
|
16362
|
-
return this.validateAuxiliary(value, file, "kxm.migration-receipt.v1", this.migrationReceiptValidator);
|
|
16363
|
-
}
|
|
16364
16337
|
validatePermissionDiff(value, file) {
|
|
16365
16338
|
return this.validateAuxiliary(value, file, "kxm.permission-diff.v1", this.permissionDiffValidator);
|
|
16366
16339
|
}
|
|
@@ -17178,11 +17151,14 @@ function assertNoRegisteredGates(options) {
|
|
|
17178
17151
|
function loadKxmProject(projectRoot, options = {}) {
|
|
17179
17152
|
assertNoRegisteredGates(options);
|
|
17180
17153
|
const root = resolve(projectRoot);
|
|
17181
|
-
|
|
17182
|
-
if (
|
|
17183
|
-
|
|
17184
|
-
|
|
17185
|
-
|
|
17154
|
+
const legacyPresent = legacyConfigFilesAt(root);
|
|
17155
|
+
if (legacyPresent.length > 0) {
|
|
17156
|
+
throw new KxmConfigError(legacyPresent.map((file) => issue2(
|
|
17157
|
+
"semantic",
|
|
17158
|
+
"legacy_state_unsupported",
|
|
17159
|
+
file,
|
|
17160
|
+
"legacy JSON configuration is present and this build does not migrate it: delete these files once their YAML replacements exist in .kxm/, or initialise a fresh project"
|
|
17161
|
+
)));
|
|
17186
17162
|
}
|
|
17187
17163
|
const registry = new KxmSchemaRegistry(options.schemasDir);
|
|
17188
17164
|
const project = readResource(registry, root, join2(root, ".kxm", "project.yaml"), ".kxm/project.yaml", "project");
|
|
@@ -17195,9 +17171,6 @@ function loadKxmProject(projectRoot, options = {}) {
|
|
|
17195
17171
|
earlyIssues.push(issue2("semantic", "template_provenance_project_mismatch", ".kxm/template-provenance.yaml", "template provenance belongs to a different project identity"));
|
|
17196
17172
|
}
|
|
17197
17173
|
}
|
|
17198
|
-
if (migrationReceipt && stringValue(migrationReceipt.projectId) !== stringValue(project.value.id)) {
|
|
17199
|
-
earlyIssues.push(issue2("semantic", "migration_project_mismatch", KXM_MIGRATION_RECEIPT_PATH, "migration receipt belongs to a different project identity"));
|
|
17200
|
-
}
|
|
17201
17174
|
const declaredRepositoryIds = new Set(valuesOf(project.value, "repositories").map((candidate) => stringValue(objectValue(candidate)?.id)).filter((candidate) => candidate !== void 0));
|
|
17202
17175
|
for (const repositoryId of Object.keys(options.repositoryBindings ?? {}).sort(compareCodeUnits3)) {
|
|
17203
17176
|
if (!resourceIdentifier(repositoryId)) {
|
|
@@ -17274,6 +17247,15 @@ function loadKxmProject(projectRoot, options = {}) {
|
|
|
17274
17247
|
continue;
|
|
17275
17248
|
}
|
|
17276
17249
|
}
|
|
17250
|
+
const memberLegacy = binding === root ? [] : legacyConfigFilesAt(binding);
|
|
17251
|
+
if (memberLegacy.length > 0) {
|
|
17252
|
+
throw new KxmConfigError(memberLegacy.map((file) => issue2(
|
|
17253
|
+
"semantic",
|
|
17254
|
+
"legacy_state_unsupported",
|
|
17255
|
+
`${repositoryId}/${file}`,
|
|
17256
|
+
`repository ${repositoryId} holds legacy JSON configuration and this build does not migrate it: delete those files once their YAML replacements exist, or bind a clean worktree`
|
|
17257
|
+
)));
|
|
17258
|
+
}
|
|
17277
17259
|
const foldedBinding = canonicalHostPath(binding).toLocaleLowerCase("en-US");
|
|
17278
17260
|
const priorBinding = seenBindings.get(foldedBinding);
|
|
17279
17261
|
if (priorBinding && priorBinding !== repositoryId) {
|
|
@@ -17327,12 +17309,10 @@ function loadKxmProject(projectRoot, options = {}) {
|
|
|
17327
17309
|
environments,
|
|
17328
17310
|
...gateRegistry ? { gateRegistry } : {},
|
|
17329
17311
|
...templateProvenance === void 0 ? {} : { templateProvenance },
|
|
17330
|
-
...migrationReceipt === void 0 ? {} : { migrationReceipt },
|
|
17331
17312
|
resources,
|
|
17332
17313
|
configRevision: bundleRevision(resources)
|
|
17333
17314
|
};
|
|
17334
17315
|
}
|
|
17335
|
-
var KXM_MIGRATION_RECEIPT_PATH = ".kxm/migration-receipt.yaml";
|
|
17336
17316
|
var LEGACY_CONFIG_FILES = [".kxm/config/agents.json", ".kxm/config/gates.json"];
|
|
17337
17317
|
function legacyConfigFilesAt(root) {
|
|
17338
17318
|
const files = [];
|
|
@@ -17358,66 +17338,6 @@ function legacyConfigFilesAt(root) {
|
|
|
17358
17338
|
}
|
|
17359
17339
|
return files;
|
|
17360
17340
|
}
|
|
17361
|
-
function hashFileRecord(root, relativePath) {
|
|
17362
|
-
const absolute = join2(root, ...relativePath.split("/"));
|
|
17363
|
-
if (!existsSync3(absolute)) return void 0;
|
|
17364
|
-
const bytes = readFileSync(absolute);
|
|
17365
|
-
return {
|
|
17366
|
-
path: relativePath,
|
|
17367
|
-
sha256: `sha256:${createHash2("sha256").update(bytes).digest("hex")}`,
|
|
17368
|
-
bytes: bytes.byteLength
|
|
17369
|
-
};
|
|
17370
|
-
}
|
|
17371
|
-
function migrationReceiptSelfHash(receipt) {
|
|
17372
|
-
const { receiptSha256: _ignored, ...unsigned } = receipt;
|
|
17373
|
-
return `sha256:${createHash2("sha256").update(kxmCanonicalJson(unsigned), "utf8").digest("hex")}`;
|
|
17374
|
-
}
|
|
17375
|
-
function readKxmMigrationReceipt(root, options = {}) {
|
|
17376
|
-
const receiptPath = join2(root, ...KXM_MIGRATION_RECEIPT_PATH.split("/"));
|
|
17377
|
-
const legacyFiles = legacyConfigFilesAt(root);
|
|
17378
|
-
if (!existsSync3(receiptPath)) {
|
|
17379
|
-
return {
|
|
17380
|
-
issues: legacyFiles.map((file) => issue2("semantic", "legacy_kxm_conflict", file, "legacy and KXM configuration cannot coexist before an accepted migration receipt"))
|
|
17381
|
-
};
|
|
17382
|
-
}
|
|
17383
|
-
const receiptStat = lstatSync(receiptPath);
|
|
17384
|
-
if (receiptStat.isSymbolicLink() || !receiptStat.isFile()) {
|
|
17385
|
-
return { issues: [issue2("path", "migration_receipt_invalid", KXM_MIGRATION_RECEIPT_PATH, "migration receipt must be a regular file, not a link")] };
|
|
17386
|
-
}
|
|
17387
|
-
let receipt;
|
|
17388
|
-
try {
|
|
17389
|
-
receipt = parseRestrictedYaml2(readFileSync(receiptPath), KXM_MIGRATION_RECEIPT_PATH);
|
|
17390
|
-
} catch (error) {
|
|
17391
|
-
if (error instanceof KxmConfigError) return { issues: [...error.issues] };
|
|
17392
|
-
throw error;
|
|
17393
|
-
}
|
|
17394
|
-
const registry = new KxmSchemaRegistry(options.schemasDir);
|
|
17395
|
-
const schemaIssues = registry.validateMigrationReceipt(receipt, KXM_MIGRATION_RECEIPT_PATH);
|
|
17396
|
-
if (schemaIssues.length > 0) return { issues: schemaIssues };
|
|
17397
|
-
if (receipt.receiptSha256 !== migrationReceiptSelfHash(receipt)) {
|
|
17398
|
-
return { issues: [issue2("semantic", "migration_receipt_hash_mismatch", KXM_MIGRATION_RECEIPT_PATH, "receipt self-hash does not match its content")] };
|
|
17399
|
-
}
|
|
17400
|
-
const sources = (Array.isArray(receipt.sources) ? receipt.sources : []).map((candidate) => candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate.path : void 0).filter((candidate) => typeof candidate === "string").sort(compareCodeUnits3);
|
|
17401
|
-
const issues = [];
|
|
17402
|
-
const declared = new Set(sources);
|
|
17403
|
-
for (const file of legacyFiles) {
|
|
17404
|
-
if (!declared.has(file)) {
|
|
17405
|
-
issues.push(issue2("semantic", "migration_source_unmigrated", file, "legacy configuration file is not covered by the migration receipt"));
|
|
17406
|
-
continue;
|
|
17407
|
-
}
|
|
17408
|
-
const record2 = (Array.isArray(receipt.sources) ? receipt.sources : []).map((candidate) => candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : void 0).find((candidate) => candidate?.path === file);
|
|
17409
|
-
const current = hashFileRecord(root, file);
|
|
17410
|
-
if (!current || current.sha256 !== record2?.sha256 || current.bytes !== record2?.bytes) {
|
|
17411
|
-
issues.push(issue2("semantic", "migration_source_changed", file, "legacy configuration changed after the migration receipt was issued"));
|
|
17412
|
-
}
|
|
17413
|
-
}
|
|
17414
|
-
for (const file of sources) {
|
|
17415
|
-
if (!legacyFiles.includes(file)) {
|
|
17416
|
-
issues.push(issue2("semantic", "migration_source_missing", file, "receipt covers a legacy configuration file that no longer exists"));
|
|
17417
|
-
}
|
|
17418
|
-
}
|
|
17419
|
-
return issues.length > 0 ? { issues } : { receipt, issues: [] };
|
|
17420
|
-
}
|
|
17421
17341
|
|
|
17422
17342
|
// plugins/kxm/src/runtime-store.ts
|
|
17423
17343
|
import { createHash as createHash3, randomUUID } from "node:crypto";
|
|
@@ -17615,7 +17535,7 @@ function openDatabase(file, description, spec) {
|
|
|
17615
17535
|
throw databaseError(
|
|
17616
17536
|
"runtime_schema_outdated",
|
|
17617
17537
|
file,
|
|
17618
|
-
`${description} is schema version ${version}; this build requires ${spec.version}. Delete the state file (
|
|
17538
|
+
`${description} is schema version ${version}; this build requires ${spec.version}. Delete the state file to start fresh and let its owning process recreate it (\`kxm hub start\` for hub state, the Runtime for registry/event stores); \`kxm init\` is project-only and rebuilds no database \u2014 upgrading old state in place is deliberately unsupported`
|
|
17619
17539
|
);
|
|
17620
17540
|
}
|
|
17621
17541
|
if (spec.tables) {
|
|
@@ -16426,9 +16426,6 @@ var KxmSchemaRegistry = class {
|
|
|
16426
16426
|
localBindingsValidator;
|
|
16427
16427
|
templateProvenanceValidator;
|
|
16428
16428
|
initOperationValidator;
|
|
16429
|
-
migrationPlanValidator;
|
|
16430
|
-
migrationDecisionValidator;
|
|
16431
|
-
migrationReceiptValidator;
|
|
16432
16429
|
permissionDiffValidator;
|
|
16433
16430
|
runEventValidator;
|
|
16434
16431
|
driveReceiptValidator;
|
|
@@ -16445,9 +16442,6 @@ var KxmSchemaRegistry = class {
|
|
|
16445
16442
|
const localBindingsFile = "local-repository-bindings.schema.json";
|
|
16446
16443
|
const templateProvenanceFile = "template-provenance.schema.json";
|
|
16447
16444
|
const initOperationFile = "init-operation.schema.json";
|
|
16448
|
-
const migrationPlanFile = "migration-plan.schema.json";
|
|
16449
|
-
const migrationDecisionFile = "migration-decision.schema.json";
|
|
16450
|
-
const migrationReceiptFile = "migration-receipt.schema.json";
|
|
16451
16445
|
const permissionDiffFile = "permission-diff.schema.json";
|
|
16452
16446
|
const runEventFile = "run-event.schema.json";
|
|
16453
16447
|
const driveReceiptFile = "drive-receipt.schema.json";
|
|
@@ -16456,9 +16450,6 @@ var KxmSchemaRegistry = class {
|
|
|
16456
16450
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, localBindingsFile)));
|
|
16457
16451
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, templateProvenanceFile)));
|
|
16458
16452
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, initOperationFile)));
|
|
16459
|
-
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, migrationPlanFile)));
|
|
16460
|
-
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, migrationDecisionFile)));
|
|
16461
|
-
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, migrationReceiptFile)));
|
|
16462
16453
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, permissionDiffFile)));
|
|
16463
16454
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, runEventFile)));
|
|
16464
16455
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, driveReceiptFile)));
|
|
@@ -16472,9 +16463,6 @@ var KxmSchemaRegistry = class {
|
|
|
16472
16463
|
const localBindingsValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${localBindingsFile}`);
|
|
16473
16464
|
const templateProvenanceValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${templateProvenanceFile}`);
|
|
16474
16465
|
const initOperationValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${initOperationFile}`);
|
|
16475
|
-
const migrationPlanValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${migrationPlanFile}`);
|
|
16476
|
-
const migrationDecisionValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${migrationDecisionFile}`);
|
|
16477
|
-
const migrationReceiptValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${migrationReceiptFile}`);
|
|
16478
16466
|
const permissionDiffValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${permissionDiffFile}`);
|
|
16479
16467
|
const runEventValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${runEventFile}`);
|
|
16480
16468
|
const driveReceiptValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${driveReceiptFile}`);
|
|
@@ -16483,9 +16471,6 @@ var KxmSchemaRegistry = class {
|
|
|
16483
16471
|
if (!localBindingsValidator) throw new Error(`schema did not compile: ${localBindingsFile}`);
|
|
16484
16472
|
if (!templateProvenanceValidator) throw new Error(`schema did not compile: ${templateProvenanceFile}`);
|
|
16485
16473
|
if (!initOperationValidator) throw new Error(`schema did not compile: ${initOperationFile}`);
|
|
16486
|
-
if (!migrationPlanValidator) throw new Error(`schema did not compile: ${migrationPlanFile}`);
|
|
16487
|
-
if (!migrationDecisionValidator) throw new Error(`schema did not compile: ${migrationDecisionFile}`);
|
|
16488
|
-
if (!migrationReceiptValidator) throw new Error(`schema did not compile: ${migrationReceiptFile}`);
|
|
16489
16474
|
if (!permissionDiffValidator) throw new Error(`schema did not compile: ${permissionDiffFile}`);
|
|
16490
16475
|
if (!runEventValidator) throw new Error(`schema did not compile: ${runEventFile}`);
|
|
16491
16476
|
if (!driveReceiptValidator) throw new Error(`schema did not compile: ${driveReceiptFile}`);
|
|
@@ -16494,9 +16479,6 @@ var KxmSchemaRegistry = class {
|
|
|
16494
16479
|
this.localBindingsValidator = localBindingsValidator;
|
|
16495
16480
|
this.templateProvenanceValidator = templateProvenanceValidator;
|
|
16496
16481
|
this.initOperationValidator = initOperationValidator;
|
|
16497
|
-
this.migrationPlanValidator = migrationPlanValidator;
|
|
16498
|
-
this.migrationDecisionValidator = migrationDecisionValidator;
|
|
16499
|
-
this.migrationReceiptValidator = migrationReceiptValidator;
|
|
16500
16482
|
this.permissionDiffValidator = permissionDiffValidator;
|
|
16501
16483
|
this.runEventValidator = runEventValidator;
|
|
16502
16484
|
this.driveReceiptValidator = driveReceiptValidator;
|
|
@@ -16522,15 +16504,6 @@ var KxmSchemaRegistry = class {
|
|
|
16522
16504
|
validateInitOperation(value, file) {
|
|
16523
16505
|
return this.validateAuxiliary(value, file, "kxm.init-operation.v1", this.initOperationValidator);
|
|
16524
16506
|
}
|
|
16525
|
-
validateMigrationPlan(value, file) {
|
|
16526
|
-
return this.validateAuxiliary(value, file, "kxm.migration-plan.v1", this.migrationPlanValidator);
|
|
16527
|
-
}
|
|
16528
|
-
validateMigrationDecision(value, file) {
|
|
16529
|
-
return this.validateAuxiliary(value, file, "kxm.migration-decision.v1", this.migrationDecisionValidator);
|
|
16530
|
-
}
|
|
16531
|
-
validateMigrationReceipt(value, file) {
|
|
16532
|
-
return this.validateAuxiliary(value, file, "kxm.migration-receipt.v1", this.migrationReceiptValidator);
|
|
16533
|
-
}
|
|
16534
16507
|
validatePermissionDiff(value, file) {
|
|
16535
16508
|
return this.validateAuxiliary(value, file, "kxm.permission-diff.v1", this.permissionDiffValidator);
|
|
16536
16509
|
}
|
|
@@ -17348,11 +17321,14 @@ function assertNoRegisteredGates(options) {
|
|
|
17348
17321
|
function loadKxmProject(projectRoot, options = {}) {
|
|
17349
17322
|
assertNoRegisteredGates(options);
|
|
17350
17323
|
const root = resolve(projectRoot);
|
|
17351
|
-
|
|
17352
|
-
if (
|
|
17353
|
-
|
|
17354
|
-
|
|
17355
|
-
|
|
17324
|
+
const legacyPresent = legacyConfigFilesAt(root);
|
|
17325
|
+
if (legacyPresent.length > 0) {
|
|
17326
|
+
throw new KxmConfigError(legacyPresent.map((file) => issue2(
|
|
17327
|
+
"semantic",
|
|
17328
|
+
"legacy_state_unsupported",
|
|
17329
|
+
file,
|
|
17330
|
+
"legacy JSON configuration is present and this build does not migrate it: delete these files once their YAML replacements exist in .kxm/, or initialise a fresh project"
|
|
17331
|
+
)));
|
|
17356
17332
|
}
|
|
17357
17333
|
const registry = new KxmSchemaRegistry(options.schemasDir);
|
|
17358
17334
|
const project = readResource(registry, root, join2(root, ".kxm", "project.yaml"), ".kxm/project.yaml", "project");
|
|
@@ -17365,9 +17341,6 @@ function loadKxmProject(projectRoot, options = {}) {
|
|
|
17365
17341
|
earlyIssues.push(issue2("semantic", "template_provenance_project_mismatch", ".kxm/template-provenance.yaml", "template provenance belongs to a different project identity"));
|
|
17366
17342
|
}
|
|
17367
17343
|
}
|
|
17368
|
-
if (migrationReceipt && stringValue(migrationReceipt.projectId) !== stringValue(project.value.id)) {
|
|
17369
|
-
earlyIssues.push(issue2("semantic", "migration_project_mismatch", KXM_MIGRATION_RECEIPT_PATH, "migration receipt belongs to a different project identity"));
|
|
17370
|
-
}
|
|
17371
17344
|
const declaredRepositoryIds = new Set(valuesOf(project.value, "repositories").map((candidate) => stringValue(objectValue(candidate)?.id)).filter((candidate) => candidate !== void 0));
|
|
17372
17345
|
for (const repositoryId of Object.keys(options.repositoryBindings ?? {}).sort(compareCodeUnits3)) {
|
|
17373
17346
|
if (!resourceIdentifier(repositoryId)) {
|
|
@@ -17444,6 +17417,15 @@ function loadKxmProject(projectRoot, options = {}) {
|
|
|
17444
17417
|
continue;
|
|
17445
17418
|
}
|
|
17446
17419
|
}
|
|
17420
|
+
const memberLegacy = binding === root ? [] : legacyConfigFilesAt(binding);
|
|
17421
|
+
if (memberLegacy.length > 0) {
|
|
17422
|
+
throw new KxmConfigError(memberLegacy.map((file) => issue2(
|
|
17423
|
+
"semantic",
|
|
17424
|
+
"legacy_state_unsupported",
|
|
17425
|
+
`${repositoryId}/${file}`,
|
|
17426
|
+
`repository ${repositoryId} holds legacy JSON configuration and this build does not migrate it: delete those files once their YAML replacements exist, or bind a clean worktree`
|
|
17427
|
+
)));
|
|
17428
|
+
}
|
|
17447
17429
|
const foldedBinding = canonicalHostPath(binding).toLocaleLowerCase("en-US");
|
|
17448
17430
|
const priorBinding = seenBindings.get(foldedBinding);
|
|
17449
17431
|
if (priorBinding && priorBinding !== repositoryId) {
|
|
@@ -17497,12 +17479,10 @@ function loadKxmProject(projectRoot, options = {}) {
|
|
|
17497
17479
|
environments,
|
|
17498
17480
|
...gateRegistry ? { gateRegistry } : {},
|
|
17499
17481
|
...templateProvenance === void 0 ? {} : { templateProvenance },
|
|
17500
|
-
...migrationReceipt === void 0 ? {} : { migrationReceipt },
|
|
17501
17482
|
resources,
|
|
17502
17483
|
configRevision: bundleRevision(resources)
|
|
17503
17484
|
};
|
|
17504
17485
|
}
|
|
17505
|
-
var KXM_MIGRATION_RECEIPT_PATH = ".kxm/migration-receipt.yaml";
|
|
17506
17486
|
var LEGACY_CONFIG_FILES = [".kxm/config/agents.json", ".kxm/config/gates.json"];
|
|
17507
17487
|
function legacyConfigFilesAt(root) {
|
|
17508
17488
|
const files = [];
|
|
@@ -17528,66 +17508,6 @@ function legacyConfigFilesAt(root) {
|
|
|
17528
17508
|
}
|
|
17529
17509
|
return files;
|
|
17530
17510
|
}
|
|
17531
|
-
function hashFileRecord(root, relativePath) {
|
|
17532
|
-
const absolute = join2(root, ...relativePath.split("/"));
|
|
17533
|
-
if (!existsSync3(absolute)) return void 0;
|
|
17534
|
-
const bytes = readFileSync(absolute);
|
|
17535
|
-
return {
|
|
17536
|
-
path: relativePath,
|
|
17537
|
-
sha256: `sha256:${createHash2("sha256").update(bytes).digest("hex")}`,
|
|
17538
|
-
bytes: bytes.byteLength
|
|
17539
|
-
};
|
|
17540
|
-
}
|
|
17541
|
-
function migrationReceiptSelfHash(receipt) {
|
|
17542
|
-
const { receiptSha256: _ignored, ...unsigned } = receipt;
|
|
17543
|
-
return `sha256:${createHash2("sha256").update(kxmCanonicalJson(unsigned), "utf8").digest("hex")}`;
|
|
17544
|
-
}
|
|
17545
|
-
function readKxmMigrationReceipt(root, options = {}) {
|
|
17546
|
-
const receiptPath = join2(root, ...KXM_MIGRATION_RECEIPT_PATH.split("/"));
|
|
17547
|
-
const legacyFiles = legacyConfigFilesAt(root);
|
|
17548
|
-
if (!existsSync3(receiptPath)) {
|
|
17549
|
-
return {
|
|
17550
|
-
issues: legacyFiles.map((file) => issue2("semantic", "legacy_kxm_conflict", file, "legacy and KXM configuration cannot coexist before an accepted migration receipt"))
|
|
17551
|
-
};
|
|
17552
|
-
}
|
|
17553
|
-
const receiptStat = lstatSync(receiptPath);
|
|
17554
|
-
if (receiptStat.isSymbolicLink() || !receiptStat.isFile()) {
|
|
17555
|
-
return { issues: [issue2("path", "migration_receipt_invalid", KXM_MIGRATION_RECEIPT_PATH, "migration receipt must be a regular file, not a link")] };
|
|
17556
|
-
}
|
|
17557
|
-
let receipt;
|
|
17558
|
-
try {
|
|
17559
|
-
receipt = parseRestrictedYaml2(readFileSync(receiptPath), KXM_MIGRATION_RECEIPT_PATH);
|
|
17560
|
-
} catch (error) {
|
|
17561
|
-
if (error instanceof KxmConfigError) return { issues: [...error.issues] };
|
|
17562
|
-
throw error;
|
|
17563
|
-
}
|
|
17564
|
-
const registry = new KxmSchemaRegistry(options.schemasDir);
|
|
17565
|
-
const schemaIssues = registry.validateMigrationReceipt(receipt, KXM_MIGRATION_RECEIPT_PATH);
|
|
17566
|
-
if (schemaIssues.length > 0) return { issues: schemaIssues };
|
|
17567
|
-
if (receipt.receiptSha256 !== migrationReceiptSelfHash(receipt)) {
|
|
17568
|
-
return { issues: [issue2("semantic", "migration_receipt_hash_mismatch", KXM_MIGRATION_RECEIPT_PATH, "receipt self-hash does not match its content")] };
|
|
17569
|
-
}
|
|
17570
|
-
const sources = (Array.isArray(receipt.sources) ? receipt.sources : []).map((candidate) => candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate.path : void 0).filter((candidate) => typeof candidate === "string").sort(compareCodeUnits3);
|
|
17571
|
-
const issues = [];
|
|
17572
|
-
const declared = new Set(sources);
|
|
17573
|
-
for (const file of legacyFiles) {
|
|
17574
|
-
if (!declared.has(file)) {
|
|
17575
|
-
issues.push(issue2("semantic", "migration_source_unmigrated", file, "legacy configuration file is not covered by the migration receipt"));
|
|
17576
|
-
continue;
|
|
17577
|
-
}
|
|
17578
|
-
const record2 = (Array.isArray(receipt.sources) ? receipt.sources : []).map((candidate) => candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : void 0).find((candidate) => candidate?.path === file);
|
|
17579
|
-
const current = hashFileRecord(root, file);
|
|
17580
|
-
if (!current || current.sha256 !== record2?.sha256 || current.bytes !== record2?.bytes) {
|
|
17581
|
-
issues.push(issue2("semantic", "migration_source_changed", file, "legacy configuration changed after the migration receipt was issued"));
|
|
17582
|
-
}
|
|
17583
|
-
}
|
|
17584
|
-
for (const file of sources) {
|
|
17585
|
-
if (!legacyFiles.includes(file)) {
|
|
17586
|
-
issues.push(issue2("semantic", "migration_source_missing", file, "receipt covers a legacy configuration file that no longer exists"));
|
|
17587
|
-
}
|
|
17588
|
-
}
|
|
17589
|
-
return issues.length > 0 ? { issues } : { receipt, issues: [] };
|
|
17590
|
-
}
|
|
17591
17511
|
|
|
17592
17512
|
// plugins/kxm/src/runtime-store.ts
|
|
17593
17513
|
import { createHash as createHash4, randomUUID } from "node:crypto";
|
|
@@ -17786,7 +17706,7 @@ function openDatabase(file, description, spec) {
|
|
|
17786
17706
|
throw databaseError(
|
|
17787
17707
|
"runtime_schema_outdated",
|
|
17788
17708
|
file,
|
|
17789
|
-
`${description} is schema version ${version}; this build requires ${spec.version}. Delete the state file (
|
|
17709
|
+
`${description} is schema version ${version}; this build requires ${spec.version}. Delete the state file to start fresh and let its owning process recreate it (\`kxm hub start\` for hub state, the Runtime for registry/event stores); \`kxm init\` is project-only and rebuilds no database \u2014 upgrading old state in place is deliberately unsupported`
|
|
17790
17710
|
);
|
|
17791
17711
|
}
|
|
17792
17712
|
if (spec.tables) {
|
|
@@ -11507,7 +11507,7 @@ function openDatabase(file, description, spec) {
|
|
|
11507
11507
|
throw databaseError(
|
|
11508
11508
|
"runtime_schema_outdated",
|
|
11509
11509
|
file,
|
|
11510
|
-
`${description} is schema version ${version}; this build requires ${spec.version}. Delete the state file (
|
|
11510
|
+
`${description} is schema version ${version}; this build requires ${spec.version}. Delete the state file to start fresh and let its owning process recreate it (\`kxm hub start\` for hub state, the Runtime for registry/event stores); \`kxm init\` is project-only and rebuilds no database \u2014 upgrading old state in place is deliberately unsupported`
|
|
11511
11511
|
);
|
|
11512
11512
|
}
|
|
11513
11513
|
if (spec.tables) {
|
package/plugins/kxm/package.json
CHANGED
|
@@ -14,7 +14,7 @@ authority, admit writers, or replace `.kxm/roster.yaml` trusted policy.
|
|
|
14
14
|
|
|
15
15
|
The KXM Agent Skills suite is organized by functional areas:
|
|
16
16
|
|
|
17
|
-
- **Project Setup**: Use `kxm-project-setup` for `init`, `
|
|
17
|
+
- **Project Setup**: Use `kxm-project-setup` for `init`, `trust`, `config`, `completion`
|
|
18
18
|
- **Harness & Auth**: Use `kxm-harness-auth` for `harness`, `auth`, `update`, `runtime`, `agent`
|
|
19
19
|
- **Hub Operations**: Use `kxm-hub-ops` for `hub`, `backup`, `restore`
|
|
20
20
|
- **Session Management**: Use `kxm-session` for `session`, `dash`, `studio`
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: kxm-project-setup
|
|
3
|
-
description: Initialize,
|
|
3
|
+
description: Initialize, review permission changes, configure, and add shell completion for KXM projects.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# KXM Project Setup
|
|
7
7
|
|
|
8
8
|
Use the current CLI. Inspect `kxm <command> --help` before mutations. Do not
|
|
9
|
-
invent `force`, domain-trust, or
|
|
9
|
+
invent `force`, domain-trust, or legacy-migration verbs: this build converts
|
|
10
|
+
nothing.
|
|
10
11
|
|
|
11
12
|
## Commands
|
|
12
13
|
|
|
13
14
|
| Command | Purpose | Options / arguments |
|
|
14
15
|
|---|---|---|
|
|
15
|
-
| `kxm init` | Create, validate, or
|
|
16
|
-
| `kxm migrate plan` | Compute the legacy-to-KXM plan without writes | `--json` |
|
|
17
|
-
| `kxm migrate apply` | Install a reviewed migration with a hash-linked receipt | `--decisions <file>`, `--project-id`, `--name` |
|
|
18
|
-
| `kxm migrate verify` | Verify a migration receipt | `--json` |
|
|
16
|
+
| `kxm init` | Create, validate, repair, or join a KXM project (a legacy tree is reported as `mode: "legacy"` and never converted) | `--json`, `--dry-run`, `--name`, `--project-id`, `--repository <id=absolute-path>` |
|
|
19
17
|
| `kxm trust diff` | Structured permission diff against a Git revision | `--base <revision>` |
|
|
20
18
|
| `kxm trust check` | Fail when the working tree expands permissions | `--base <revision>` |
|
|
21
19
|
| `kxm config get <key>` | Get a configuration value | `--json` |
|
|
@@ -25,7 +23,6 @@ invent `force`, domain-trust, or extra migrate verbs.
|
|
|
25
23
|
|
|
26
24
|
```bash
|
|
27
25
|
kxm init --dry-run --json
|
|
28
|
-
kxm migrate plan --json
|
|
29
26
|
kxm trust diff --base HEAD --json
|
|
30
27
|
kxm config list --json
|
|
31
28
|
kxm completion install
|
|
@@ -2,7 +2,6 @@ export type SupportedShell = "bash" | "zsh" | "fish";
|
|
|
2
2
|
|
|
3
3
|
const TOP_LEVEL_COMMANDS = [
|
|
4
4
|
"init",
|
|
5
|
-
"migrate",
|
|
6
5
|
"backup",
|
|
7
6
|
"restore",
|
|
8
7
|
"run",
|
|
@@ -36,7 +35,6 @@ const TOP_LEVEL_COMMANDS = [
|
|
|
36
35
|
|
|
37
36
|
const SUBCOMMANDS: Record<string, string[]> = {
|
|
38
37
|
runs: ["status", "cancel", "list"],
|
|
39
|
-
migrate: ["plan", "apply", "verify"],
|
|
40
38
|
harness: ["list"],
|
|
41
39
|
runtime: ["start", "status", "stop"],
|
|
42
40
|
trust: ["diff", "check"],
|
|
@@ -132,7 +130,6 @@ _kxm() {
|
|
|
132
130
|
local -a commands
|
|
133
131
|
commands=(
|
|
134
132
|
'init:Create or validate a KXM project'
|
|
135
|
-
'migrate:Plan, apply, and verify legacy configuration migration'
|
|
136
133
|
'backup:Create a verified SQLite backup manifest'
|
|
137
134
|
'restore:Restore SQLite stores from a backup manifest'
|
|
138
135
|
'run:Create a KXM workflow run'
|