@kontextmind/kxm 0.7.89 → 0.7.91

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.
@@ -11,7 +11,7 @@
11
11
  "name": "kxm",
12
12
  "source": "./plugins/kxm",
13
13
  "description": "Durable workflows, peer agents, and kxm tui",
14
- "version": "0.7.89",
14
+ "version": "0.7.91",
15
15
  "category": "development",
16
16
  "tags": ["kxm", "multi-agent", "workflows", "mcp"]
17
17
  }
package/CHANGELOG.md CHANGED
@@ -174,6 +174,17 @@ All notable user-facing changes are documented here. The project follows [Semant
174
174
 
175
175
  ### Fixed
176
176
 
177
+ - **`kxm memory sync` no longer writes this repository's agent policy into other
178
+ projects.** A missing `CLAUDE.md` or `GEMINI.md` used to be created from a header
179
+ copied from KXM's own instruction files — the planner/architecture-critic role, the
180
+ Grok writer rotation, and Tracking admission rules — so every project that ran sync
181
+ inherited another project's development policy. Sync now touches only the
182
+ `AGENTS.md`, `CLAUDE.md` and `GEMINI.md` a project already has, replaces or appends
183
+ just the `<!-- kxm:memory:start -->`…`<!-- kxm:memory:end -->` block, reports
184
+ `updated`, `unchanged` and `missing` files, and exits non-zero without writing when
185
+ none exist. The block is placed at the end of a file that has no markers yet, rather
186
+ than before a `## Do not` heading that only this repository uses.
187
+
177
188
  - **Release version surfaces cover workspace packages:** a merged PR no longer
178
189
  breaks the release pipeline. `scripts/kxm-bump-version.mjs` now writes the
179
190
  version into every package manifest under `packages/`, using the same package
@@ -303,6 +303,7 @@ The current hub command groups are `agent`, `session`, `workflow`, `gate`, `hub`
303
303
  | `kxm improve` | Bucket `.kxm/logs/telemetry.jsonl` events into a proposed-only improvement report; does not read the workflow journal |
304
304
  | `kxm context get \| recall \| state \| episode \| promote \| explain \| wiki-compile \| wiki-lint` | Context operating system: role-aware packets, metadata search, temporal state, episodes, evidence-backed lineage (`explain`), wiki compile/lint |
305
305
  | `kxm skills` | Governed skill candidate lifecycle |
306
+ | `kxm memory sync` | Regenerate the read-only project-memory block — authored facts from `.kxm/memory/` only — between `<!-- kxm:memory:start -->` and `<!-- kxm:memory:end -->` in whichever of `AGENTS.md`, `CLAUDE.md` and `GEMINI.md` already exist in the current directory. Text outside the markers is never rewritten; a file without markers gets the block appended. Missing files are reported as `missing` and **not created** — which harness a project uses is its own choice — and with none present sync exits 1 without writing. `--json` reports `updated`, `unchanged` and `missing` |
306
307
 
307
308
  Improvement telemetry is classified as `project` whenever a project or workflow identity is present, and as `cli` for unscoped operator behavior. Set `KXM_IMPROVE_TARGET=cli` or `KXM_IMPROVE_TARGET=project` only when an operator needs to override that generic classification; this changes report bucketing, not workflow state.
308
309
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontextmind/kxm",
3
- "version": "0.7.89",
3
+ "version": "0.7.91",
4
4
  "description": "KXM local-first multi-agent orchestration and operator dashboard",
5
5
  "type": "module",
6
6
  "author": "KontextMind",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "kxm",
4
4
  "displayName": "KXM",
5
- "version": "0.7.89",
5
+ "version": "0.7.91",
6
6
  "description": "Headless multi-agent orchestration, durable workflows, and a live operator dashboard for Pi and Claude Code",
7
7
  "author": {
8
8
  "name": "KontextMind",
@@ -33617,34 +33617,20 @@ function formatHarnessMemoryBlock(records) {
33617
33617
  contentLines.push(MEMORY_MARKER_END);
33618
33618
  return contentLines.join("\n");
33619
33619
  }
33620
- function updateHarnessDocument(filePath, block, defaultHeader, dryRun = false) {
33621
- let original = "";
33622
- if (existsSync26(filePath)) {
33623
- original = readFileSync24(filePath, "utf8");
33624
- }
33625
- let updated = "";
33620
+ var HARNESS_INSTRUCTION_FILES = ["AGENTS.md", "CLAUDE.md", "GEMINI.md"];
33621
+ function updateHarnessDocument(filePath, block, dryRun = false) {
33622
+ const original = readFileSync24(filePath, "utf8");
33623
+ let updated;
33626
33624
  if (original.includes(MEMORY_MARKER_START) && original.includes(MEMORY_MARKER_END)) {
33627
33625
  const startIdx = original.indexOf(MEMORY_MARKER_START);
33628
33626
  const endIdx = original.indexOf(MEMORY_MARKER_END) + MEMORY_MARKER_END.length;
33629
33627
  updated = original.slice(0, startIdx) + block + original.slice(endIdx);
33630
- } else if (original.length > 0) {
33631
- const doNotIdx = original.indexOf("## Do not");
33632
- if (doNotIdx !== -1) {
33633
- updated = `${original.slice(0, doNotIdx).trimEnd()}
33634
-
33635
- ${block}
33636
-
33637
- ${original.slice(doNotIdx)}`;
33638
- } else {
33639
- updated = `${original.trimEnd()}
33640
-
33641
- ${block}
33642
- `;
33643
- }
33644
33628
  } else {
33645
- updated = `${defaultHeader.trimEnd()}
33629
+ const head = original.trimEnd();
33630
+ updated = head ? `${head}
33646
33631
 
33647
33632
  ${block}
33633
+ ` : `${block}
33648
33634
  `;
33649
33635
  }
33650
33636
  if (updated !== original) {
@@ -33655,51 +33641,20 @@ ${block}
33655
33641
  }
33656
33642
  function syncHarnessMemory(repoRoot, options = {}) {
33657
33643
  const root = resolve22(repoRoot);
33658
- const records = loadAuthoredMemory(root);
33659
- const block = formatHarnessMemoryBlock(records);
33660
- const updated = [];
33661
- const created = [];
33662
- const agentsPath = join28(root, "AGENTS.md");
33663
- const agentsHeader = "# AGENTS\n\nFollow project instructions.\n";
33664
- const agentsExisted = existsSync26(agentsPath);
33665
- if (updateHarnessDocument(agentsPath, block, agentsHeader, options.dryRun)) {
33666
- if (agentsExisted) updated.push("AGENTS.md");
33667
- else created.push("AGENTS.md");
33668
- }
33669
- const claudePath = join28(root, "CLAUDE.md");
33670
- const claudeHeader = `# KXM (Claude)
33671
-
33672
- Follow [\`AGENTS.md\`](AGENTS.md). Official phase tracking:
33673
- [\`plans/implementation-plan.md\`](plans/implementation-plan.md#tracking-working-tree-not-a-release).
33674
-
33675
- You are the **planner / architecture critic** unless the human explicitly asks
33676
- you to implement. Default writer is native Grok CLI (\`grok --model grok-4.6\`) \u2014 a starting
33677
- rotation, not a sole writer. If \`grok\` is logged out, never bill Grok through another harness;
33678
- use a relief route Tracking **admits**, or stop and name the limits hit. Your reviews are
33679
- artifacts, not hub \`peer-reply\` evidence.
33680
- `;
33681
- const claudeExisted = existsSync26(claudePath);
33682
- if (updateHarnessDocument(claudePath, block, claudeHeader, options.dryRun)) {
33683
- if (claudeExisted) updated.push("CLAUDE.md");
33684
- else created.push("CLAUDE.md");
33644
+ const present = HARNESS_INSTRUCTION_FILES.filter((name) => existsSync26(join28(root, name)));
33645
+ const missing = HARNESS_INSTRUCTION_FILES.filter((name) => !present.includes(name));
33646
+ if (present.length === 0) {
33647
+ throw new Error(
33648
+ `none of ${HARNESS_INSTRUCTION_FILES.join(", ")} exists in ${root}; sync updates the instruction files a project already has and does not create them`
33649
+ );
33685
33650
  }
33686
- const geminiPath = join28(root, "GEMINI.md");
33687
- const geminiHeader = `# KXM (Gemini / Antigravity)
33688
-
33689
- Follow [\`AGENTS.md\`](AGENTS.md). Official phase tracking:
33690
- [\`plans/implementation-plan.md\`](plans/implementation-plan.md#tracking-working-tree-not-a-release).
33691
-
33692
- Google goes through the \`antigravity\` **Pi provider** (Tracking \u2192 Decided,
33693
- 2026-09-15); \`agy\` stays a harness catalog/helper entry, not the admission path.
33694
- Current admissions come from Tracking and \`kxm harness list\`. Starting rotation
33695
- remains Grok.
33696
- `;
33697
- const geminiExisted = existsSync26(geminiPath);
33698
- if (updateHarnessDocument(geminiPath, block, geminiHeader, options.dryRun)) {
33699
- if (geminiExisted) updated.push("GEMINI.md");
33700
- else created.push("GEMINI.md");
33651
+ const block = formatHarnessMemoryBlock(loadAuthoredMemory(root));
33652
+ const updated = [];
33653
+ const unchanged = [];
33654
+ for (const name of present) {
33655
+ (updateHarnessDocument(join28(root, name), block, options.dryRun) ? updated : unchanged).push(name);
33701
33656
  }
33702
- return { updated, created };
33657
+ return { updated, unchanged, missing };
33703
33658
  }
33704
33659
 
33705
33660
  // plugins/kxm/src/skills.ts
@@ -34460,16 +34415,22 @@ async function cmdMemorySync(runtime) {
34460
34415
  printPlan(
34461
34416
  runtime,
34462
34417
  { command: "memory sync", ...result },
34463
- [...result.created, ...result.updated].map((file) => ({ action: "write", target: join31(runtime.cwd, file) })),
34464
- "regenerate project memory blocks in AGENTS.md, CLAUDE.md, and GEMINI.md"
34418
+ result.updated.map((file) => ({ action: "write", target: join31(runtime.cwd, file) })),
34419
+ "regenerate the project memory block in the instruction files this project already has"
34465
34420
  );
34466
34421
  return 0;
34467
34422
  }
34423
+ const lines = [
34424
+ ...result.updated.length > 0 ? [`updated: ${result.updated.join(", ")}`] : [],
34425
+ ...result.unchanged.length > 0 ? [`unchanged: ${result.unchanged.join(", ")}`] : [],
34426
+ ...result.missing.length > 0 ? [`not present, not created: ${result.missing.join(", ")}`] : []
34427
+ ];
34468
34428
  print(
34469
34429
  runtime.io,
34470
34430
  runtime.json,
34471
34431
  { ok: true, command: "memory sync", ...result },
34472
- `Synced project memory across AGENTS.md, CLAUDE.md, and GEMINI.md`
34432
+ `Synced project memory
34433
+ ${lines.join("\n")}`
34473
34434
  );
34474
34435
  return 0;
34475
34436
  } catch (error) {
@@ -48574,7 +48535,7 @@ function createProgram(ctx, result) {
48574
48535
  addGlobalOptions(memory.command("note").description("Record an evidence-based memory candidate (promoted via PR)")).argument("<fact>", "Summary of the observed fact or learning").option("--scope <scope>", "Scope: agent, project, run, or operator (default: project)").option("--kind <kind>", "Kind: decision, architecture, convention, policy, learning (default: learning)").option("--body <text>", "Detailed markdown context for the fact").action(async function memoryNoteAction(fact, options) {
48575
48536
  result.code = await cmdMemoryNote(runtimeFrom(ctx, this), fact, options);
48576
48537
  });
48577
- addGlobalOptions(memory.command("sync").description("Regenerate memory projection blocks across AGENTS.md, CLAUDE.md, and GEMINI.md")).action(async function memorySyncAction() {
48538
+ addGlobalOptions(memory.command("sync").description("Regenerate the memory block in whichever of AGENTS.md, CLAUDE.md, and GEMINI.md exist; never creates them")).action(async function memorySyncAction() {
48578
48539
  result.code = await cmdMemorySync(runtimeFrom(ctx, this));
48579
48540
  });
48580
48541
  const routing = addGlobalOptions(program2.command("routing").description("Model/harness routing telemetry and behavioral comparisons"));
@@ -17182,7 +17182,7 @@ async function deliverInboxNotification(messageId, delivered, notify) {
17182
17182
  }
17183
17183
 
17184
17184
  // plugins/kxm/src/mcp-server.ts
17185
- var VERSION = "0.7.89";
17185
+ var VERSION = "0.7.91";
17186
17186
  var inbox = /* @__PURE__ */ new Map();
17187
17187
  var notifiedInbox = /* @__PURE__ */ new Set();
17188
17188
  var meshClient;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-plugin",
3
- "version": "0.7.89",
3
+ "version": "0.7.91",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "engines": {
@@ -38,7 +38,7 @@ kxm context state my-project "configuration.authority" --json
38
38
  |---|---|---|
39
39
  | `kxm memory brief` | Read active project memory facts | `--json` |
40
40
  | `kxm memory note <fact>` | Record a candidate for reviewed promotion | `--scope`, `--kind`, `--body`, `--json` |
41
- | `kxm memory sync` | Regenerate harness instruction memory projections | `--json` |
41
+ | `kxm memory sync` | Regenerate the memory block in existing `AGENTS.md`, `CLAUDE.md`, `GEMINI.md` | `--json` |
42
42
 
43
43
  Scopes are `agent`, `project`, `run`, or `operator`. Kinds are `decision`,
44
44
  `architecture`, `convention`, `policy`, or `learning`. Scope and kind labels
@@ -51,6 +51,12 @@ never elevate the candidate's authority.
51
51
  4. Regenerate projections only as an authorized write, inspect their diff,
52
52
  and run the existing verification gate.
53
53
 
54
+ `kxm memory sync` rewrites only the text between `<!-- kxm:memory:start -->`
55
+ and `<!-- kxm:memory:end -->` (appending that block when a file has none) and
56
+ never creates an instruction file. If the project has none of the three, sync
57
+ refuses; create the file your harness reads yourself rather than expecting KXM
58
+ to author it.
59
+
54
60
  Do not invent search, save, delete, list, or clear subcommands under memory.
55
61
  Use context retrieval for discovery and reviewed authored changes for
56
62
  corrections, respecting provenance and historical records.
@@ -398,16 +398,21 @@ export async function cmdMemorySync(runtime: Runtime): Promise<number> {
398
398
  printPlan(
399
399
  runtime,
400
400
  { command: "memory sync", ...result },
401
- [...result.created, ...result.updated].map((file) => ({ action: "write", target: join(runtime.cwd, file) })),
402
- "regenerate project memory blocks in AGENTS.md, CLAUDE.md, and GEMINI.md",
401
+ result.updated.map((file) => ({ action: "write", target: join(runtime.cwd, file) })),
402
+ "regenerate the project memory block in the instruction files this project already has",
403
403
  );
404
404
  return 0;
405
405
  }
406
+ const lines = [
407
+ ...(result.updated.length > 0 ? [`updated: ${result.updated.join(", ")}`] : []),
408
+ ...(result.unchanged.length > 0 ? [`unchanged: ${result.unchanged.join(", ")}`] : []),
409
+ ...(result.missing.length > 0 ? [`not present, not created: ${result.missing.join(", ")}`] : []),
410
+ ];
406
411
  print(
407
412
  runtime.io,
408
413
  runtime.json,
409
414
  { ok: true, command: "memory sync", ...result },
410
- `Synced project memory across AGENTS.md, CLAUDE.md, and GEMINI.md`,
415
+ `Synced project memory\n${lines.join("\n")}`,
411
416
  );
412
417
  return 0;
413
418
  } catch (error) {
@@ -1040,7 +1040,7 @@ function createProgram(ctx: CliContext, result: { code: number }): Command {
1040
1040
  .action(async function memoryNoteAction(this: Command, fact: string, options: { scope?: string; kind?: string; body?: string }) {
1041
1041
  result.code = await cmdMemoryNote(runtimeFrom(ctx, this), fact, options);
1042
1042
  });
1043
- addGlobalOptions(memory.command("sync").description("Regenerate memory projection blocks across AGENTS.md, CLAUDE.md, and GEMINI.md"))
1043
+ addGlobalOptions(memory.command("sync").description("Regenerate the memory block in whichever of AGENTS.md, CLAUDE.md, and GEMINI.md exist; never creates them"))
1044
1044
  .action(async function memorySyncAction(this: Command) {
1045
1045
  result.code = await cmdMemorySync(runtimeFrom(ctx, this));
1046
1046
  });
@@ -8,7 +8,7 @@ import { AGENT_COMMANDS_MAP, enforceToolPolicy, getMcpTools, reconcileInbox } fr
8
8
  import { deliverInboxNotification } from "./inbox.ts";
9
9
  import type { HubEvent, MessageRecord } from "./protocol.ts";
10
10
 
11
- const VERSION = "0.7.89";
11
+ const VERSION = "0.7.91";
12
12
  const inbox = new Map<string, MessageRecord>();
13
13
  const notifiedInbox = new Set<string>();
14
14
  let meshClient: HubClient | undefined;
@@ -318,28 +318,24 @@ export function formatHarnessMemoryBlock(records: MemoryRecord[]): string {
318
318
  return contentLines.join("\n");
319
319
  }
320
320
 
321
- /** Returns whether the projection block changed the file; `dryRun` answers without writing. */
322
- export function updateHarnessDocument(filePath: string, block: string, defaultHeader: string, dryRun = false): boolean {
323
- let original = "";
324
- if (existsSync(filePath)) {
325
- original = readFileSync(filePath, "utf8");
326
- }
327
-
328
- let updated = "";
321
+ /** Instruction files `kxm memory sync` projects memory into. Sync never creates one:
322
+ * which harness a project uses is the project's choice, and a file KXM wrote would
323
+ * carry KXM's words instead of the project's. */
324
+ const HARNESS_INSTRUCTION_FILES = ["AGENTS.md", "CLAUDE.md", "GEMINI.md"] as const;
325
+
326
+ /** Replace the marker-delimited block, or append one when the file has none. Nothing
327
+ * outside the markers is rewritten. Returns whether the block changed the file; `dryRun`
328
+ * answers without writing. */
329
+ export function updateHarnessDocument(filePath: string, block: string, dryRun = false): boolean {
330
+ const original = readFileSync(filePath, "utf8");
331
+ let updated: string;
329
332
  if (original.includes(MEMORY_MARKER_START) && original.includes(MEMORY_MARKER_END)) {
330
333
  const startIdx = original.indexOf(MEMORY_MARKER_START);
331
334
  const endIdx = original.indexOf(MEMORY_MARKER_END) + MEMORY_MARKER_END.length;
332
335
  updated = original.slice(0, startIdx) + block + original.slice(endIdx);
333
- } else if (original.length > 0) {
334
- // If "## Do not" exists, insert before it; else append to end
335
- const doNotIdx = original.indexOf("## Do not");
336
- if (doNotIdx !== -1) {
337
- updated = `${original.slice(0, doNotIdx).trimEnd()}\n\n${block}\n\n${original.slice(doNotIdx)}`;
338
- } else {
339
- updated = `${original.trimEnd()}\n\n${block}\n`;
340
- }
341
336
  } else {
342
- updated = `${defaultHeader.trimEnd()}\n\n${block}\n`;
337
+ const head = original.trimEnd();
338
+ updated = head ? `${head}\n\n${block}\n` : `${block}\n`;
343
339
  }
344
340
 
345
341
  if (updated !== original) {
@@ -349,40 +345,24 @@ export function updateHarnessDocument(filePath: string, block: string, defaultHe
349
345
  return false;
350
346
  }
351
347
 
352
- export function syncHarnessMemory(repoRoot: string, options: { dryRun?: boolean } = {}): { updated: string[]; created: string[] } {
348
+ export function syncHarnessMemory(
349
+ repoRoot: string,
350
+ options: { dryRun?: boolean } = {},
351
+ ): { updated: string[]; unchanged: string[]; missing: string[] } {
353
352
  const root = resolve(repoRoot);
354
- const records = loadAuthoredMemory(root);
355
- const block = formatHarnessMemoryBlock(records);
356
-
357
- const updated: string[] = [];
358
- const created: string[] = [];
359
-
360
- // 1. AGENTS.md
361
- const agentsPath = join(root, "AGENTS.md");
362
- const agentsHeader = "# AGENTS\n\nFollow project instructions.\n";
363
- const agentsExisted = existsSync(agentsPath);
364
- if (updateHarnessDocument(agentsPath, block, agentsHeader, options.dryRun)) {
365
- if (agentsExisted) updated.push("AGENTS.md");
366
- else created.push("AGENTS.md");
367
- }
368
-
369
- // 2. CLAUDE.md
370
- const claudePath = join(root, "CLAUDE.md");
371
- const claudeHeader = `# KXM (Claude)\n\nFollow [\`AGENTS.md\`](AGENTS.md). Official phase tracking:\n[\`plans/implementation-plan.md\`](plans/implementation-plan.md#tracking-working-tree-not-a-release).\n\nYou are the **planner / architecture critic** unless the human explicitly asks\nyou to implement. Default writer is native Grok CLI (\`grok --model grok-4.6\`) — a starting\nrotation, not a sole writer. If \`grok\` is logged out, never bill Grok through another harness;\nuse a relief route Tracking **admits**, or stop and name the limits hit. Your reviews are\nartifacts, not hub \`peer-reply\` evidence.\n`;
372
- const claudeExisted = existsSync(claudePath);
373
- if (updateHarnessDocument(claudePath, block, claudeHeader, options.dryRun)) {
374
- if (claudeExisted) updated.push("CLAUDE.md");
375
- else created.push("CLAUDE.md");
353
+ const present = HARNESS_INSTRUCTION_FILES.filter((name) => existsSync(join(root, name)));
354
+ const missing = HARNESS_INSTRUCTION_FILES.filter((name) => !present.includes(name));
355
+ if (present.length === 0) {
356
+ throw new Error(
357
+ `none of ${HARNESS_INSTRUCTION_FILES.join(", ")} exists in ${root}; sync updates the instruction files a project already has and does not create them`,
358
+ );
376
359
  }
377
360
 
378
- // 3. GEMINI.md
379
- const geminiPath = join(root, "GEMINI.md");
380
- const geminiHeader = `# KXM (Gemini / Antigravity)\n\nFollow [\`AGENTS.md\`](AGENTS.md). Official phase tracking:\n[\`plans/implementation-plan.md\`](plans/implementation-plan.md#tracking-working-tree-not-a-release).\n\nGoogle goes through the \`antigravity\` **Pi provider** (Tracking \u2192 Decided,\n2026-09-15); \`agy\` stays a harness catalog/helper entry, not the admission path.\nCurrent admissions come from Tracking and \`kxm harness list\`. Starting rotation\nremains Grok.\n`;
381
- const geminiExisted = existsSync(geminiPath);
382
- if (updateHarnessDocument(geminiPath, block, geminiHeader, options.dryRun)) {
383
- if (geminiExisted) updated.push("GEMINI.md");
384
- else created.push("GEMINI.md");
361
+ const block = formatHarnessMemoryBlock(loadAuthoredMemory(root));
362
+ const updated: string[] = [];
363
+ const unchanged: string[] = [];
364
+ for (const name of present) {
365
+ (updateHarnessDocument(join(root, name), block, options.dryRun) ? updated : unchanged).push(name);
385
366
  }
386
-
387
- return { updated, created };
367
+ return { updated, unchanged, missing };
388
368
  }