@kontextmind/kxm 0.7.153 → 0.7.155
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 +15 -2
- package/docs/contributing/ci-and-release.md +17 -12
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +7 -0
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime-supervisor.js +7 -0
- package/plugins/kxm/dist/runtime.js +7 -0
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/harness.ts +10 -0
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/scripts/ci-unit-shard.mjs +40 -10
package/CHANGELOG.md
CHANGED
|
@@ -156,6 +156,18 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
156
156
|
|
|
157
157
|
### Changed
|
|
158
158
|
|
|
159
|
+
- **Windows pull-request unit lanes finish inside the job limit.** The light
|
|
160
|
+
lane on `windows-latest` was cancelled at 20 minutes on #361, #362, and
|
|
161
|
+
#363 after it stopped reporting tests. `package-install.test.ts` (171s on
|
|
162
|
+
that lane, 67s on Linux) now runs with the serial files. The other light
|
|
163
|
+
files are split into `light-1` and `light-2`. POSIX `#!/bin/sh` shims in
|
|
164
|
+
the land tests are skipped on Windows. A missing Windows `.cmd` is not
|
|
165
|
+
reported as an installed harness: `cmd.exe` answers for a command that is
|
|
166
|
+
not on `PATH`, and that answer was counted as detection. Unix `0600` mode
|
|
167
|
+
checks and forward-slash path checks in the unit tests match what Windows
|
|
168
|
+
can report. `CI / required` still fails when a lane fails or is cancelled.
|
|
169
|
+
See [CI and release](docs/contributing/ci-and-release.md).
|
|
170
|
+
|
|
159
171
|
- **Workforce ids use one convention, and old ids still resolve.**
|
|
160
172
|
Role ids stay `planner`, `writer`, `reviewer-arch`, and `reviewer-cli`.
|
|
161
173
|
Agent ids and agent-step ids use those same names. Route ids are
|
|
@@ -494,8 +506,9 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
494
506
|
|
|
495
507
|
- **Pull-request CI runs the unit suite on Linux Node 24, and `CI / required` is the aggregate check.**
|
|
496
508
|
Docs and plan markdown skip the code jobs. `engine.test.ts` is split by
|
|
497
|
-
test name. `permission.test.ts
|
|
498
|
-
time, and every other unit file
|
|
509
|
+
test name. `permission.test.ts`, `runtime.test.ts`, and
|
|
510
|
+
`package-install.test.ts` run one file at a time, and every other unit file
|
|
511
|
+
runs in two light shards. Pushes to `main`
|
|
499
512
|
still run `validate:pr` on Linux and Windows for Node 22.19.0 and Node 24.
|
|
500
513
|
A pull request that touches path, process, shell, spawn, package, lockfile,
|
|
501
514
|
or workflow files also runs the unit lanes on Windows. Playwright stays on
|
|
@@ -39,8 +39,8 @@ together.
|
|
|
39
39
|
| Before you push | Local | `npm run verify` |
|
|
40
40
|
| Pull request, push to `main`, or manual | `ci.yml` (`required`) | Aggregates the lanes below into one pass/fail check named `CI / required` |
|
|
41
41
|
| Pull request and push | `ci.yml` (Docs lint) | `lint:docs` and `check:versions`, always |
|
|
42
|
-
| Code pull request and code push | `ci.yml` (Unit engine and Unit, Linux Node 24) | `engine.test.ts` split by test name; `permission.test.ts` and `
|
|
43
|
-
| Platform-sensitive pull request | `ci.yml` (Unit, Windows Node 24) | The same
|
|
42
|
+
| Code pull request and code push | `ci.yml` (Unit engine and Unit, Linux Node 24) | `engine.test.ts` split by test name; `permission.test.ts`, `runtime.test.ts`, and `package-install.test.ts` one file at a time; every other unit file in two light shards. Typecheck and `check-generated` run on `light-1` |
|
|
43
|
+
| Platform-sensitive pull request | `ci.yml` (Unit, Windows Node 24) | The same unit lanes on `windows-latest` |
|
|
44
44
|
| Push to `main`, or manual | `ci.yml` (Validate matrix) | `validate:pr` on Linux and Windows for Node 22.19.0 and Node 24; not on a pull request |
|
|
45
45
|
| Code pull request and code push | `ci.yml` (Plugin validation) | `claude plugin validate --strict` on the marketplace and the plugin |
|
|
46
46
|
| Daily at 04:00 UTC, or manual | `nightly.yml` | `test:coverage:complete`, `check`, `check:generated`, `npm pack --dry-run` |
|
|
@@ -54,7 +54,7 @@ The npm scripts behind those rows:
|
|
|
54
54
|
| Script | Composition |
|
|
55
55
|
|---|---|
|
|
56
56
|
| `verify` | `npm test` (core and package unit tests), `check`, `check:generated` |
|
|
57
|
-
| `test:ci-shard` | One unit lane: build, then `engine <index> <total>`, `serial`, or `light
|
|
57
|
+
| `test:ci-shard` | One unit lane: build, then `engine <index> <total>`, `serial`, `light`, or `light-<index>` over `test/core/*.test.ts` and `packages/core/*/tests/unit/*.test.ts` |
|
|
58
58
|
| `validate:pr` | `build`, `typecheck`, a compact contract and smoke set of nine `test/core` files, `check:versions`, and the generated-`dist` check |
|
|
59
59
|
| `validate:ci` | `test:coverage` (core and package tests, 91/80/92 floors), `check`, `npm pack --dry-run`; the Release workflow runs it, and it stays available locally |
|
|
60
60
|
| `test:coverage:complete` | Core, simulation and package tests with 93/80/93 floors |
|
|
@@ -81,15 +81,20 @@ blocker: it is the only place the simulation suite and coverage floors run.
|
|
|
81
81
|
### Lanes and the required check
|
|
82
82
|
|
|
83
83
|
Code pull requests run Docs lint, two Linux Node 24 engine shards, a serial
|
|
84
|
-
lane (`permission.test.ts
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
the
|
|
89
|
-
|
|
90
|
-
`
|
|
91
|
-
|
|
92
|
-
|
|
84
|
+
lane (`permission.test.ts`, then `runtime.test.ts`, then
|
|
85
|
+
`package-install.test.ts`), two light shards for every other unit file, and
|
|
86
|
+
Plugin validation. `light-1` typechecks and checks generated bundles.
|
|
87
|
+
`engine.test.ts` is split by test name because that file alone was 174
|
|
88
|
+
seconds; the serial lane keeps the next longest files off the light pool,
|
|
89
|
+
which was 268 seconds when every non-engine file shared one job.
|
|
90
|
+
`package-install.test.ts` is serial because on Windows it took 171 seconds
|
|
91
|
+
and the light job stopped reporting tests after that file finished (cancelled
|
|
92
|
+
at 20 minutes on pull requests #361, #362, and #363). The other light files
|
|
93
|
+
are round-robin split so each Windows job is about half of that pool. Linux
|
|
94
|
+
jobs use the npm cache from `actions/setup-node`. Restoring a
|
|
95
|
+
`node_modules` tarball was slower than `npm ci` on the Linux runners (about
|
|
96
|
+
24s versus 17s on 2026-09-24), so that cache stays on the Windows jobs,
|
|
97
|
+
where `npm ci` is the slow step.
|
|
93
98
|
|
|
94
99
|
The job `required` always runs. Its check name is `CI / required`. It fails
|
|
95
100
|
when a lane fails or is cancelled, and it passes when a lane was skipped
|
package/package.json
CHANGED
|
@@ -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.
|
|
5
|
+
"version": "0.7.155",
|
|
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",
|
package/plugins/kxm/dist/cli.js
CHANGED
|
@@ -18406,10 +18406,17 @@ function resolveDispatchStatus(entry, detected, authenticated, issues) {
|
|
|
18406
18406
|
}
|
|
18407
18407
|
return { status: "yes", supported: true };
|
|
18408
18408
|
}
|
|
18409
|
+
function windowsCmdReportsMissing(result) {
|
|
18410
|
+
if (result.ok) return false;
|
|
18411
|
+
const text = `${result.stdout}
|
|
18412
|
+
${result.stderr}`;
|
|
18413
|
+
return /not recognized as an internal or external command/i.test(text) || /the system cannot find the (?:file|path) specified/i.test(text);
|
|
18414
|
+
}
|
|
18409
18415
|
function tryHarnessCommand(candidate, entry, runCommand, timeoutMs) {
|
|
18410
18416
|
const result = runCommand(candidate, entry.versionArgs, timeoutMs);
|
|
18411
18417
|
if (result.error === "ENOENT") return void 0;
|
|
18412
18418
|
if (result.error && result.code === null && !result.stdout && !result.stderr) return void 0;
|
|
18419
|
+
if (isWindowsHarnessShim(candidate) && windowsCmdReportsMissing(result)) return void 0;
|
|
18413
18420
|
return {
|
|
18414
18421
|
command: candidate,
|
|
18415
18422
|
version: firstLine(result.stdout) ?? firstLine(result.stderr)
|
|
@@ -17313,7 +17313,7 @@ function sessionTokenFixHint(policy) {
|
|
|
17313
17313
|
}
|
|
17314
17314
|
|
|
17315
17315
|
// plugins/kxm/src/mcp-server.ts
|
|
17316
|
-
var VERSION = "0.7.
|
|
17316
|
+
var VERSION = "0.7.155";
|
|
17317
17317
|
var CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
|
|
17318
17318
|
var inbox = /* @__PURE__ */ new Map();
|
|
17319
17319
|
var notifiedInbox = /* @__PURE__ */ new Set();
|
|
@@ -16889,10 +16889,17 @@ function resolveDispatchStatus(entry, detected, authenticated, issues) {
|
|
|
16889
16889
|
}
|
|
16890
16890
|
return { status: "yes", supported: true };
|
|
16891
16891
|
}
|
|
16892
|
+
function windowsCmdReportsMissing(result) {
|
|
16893
|
+
if (result.ok) return false;
|
|
16894
|
+
const text = `${result.stdout}
|
|
16895
|
+
${result.stderr}`;
|
|
16896
|
+
return /not recognized as an internal or external command/i.test(text) || /the system cannot find the (?:file|path) specified/i.test(text);
|
|
16897
|
+
}
|
|
16892
16898
|
function tryHarnessCommand(candidate, entry, runCommand, timeoutMs) {
|
|
16893
16899
|
const result = runCommand(candidate, entry.versionArgs, timeoutMs);
|
|
16894
16900
|
if (result.error === "ENOENT") return void 0;
|
|
16895
16901
|
if (result.error && result.code === null && !result.stdout && !result.stderr) return void 0;
|
|
16902
|
+
if (isWindowsHarnessShim(candidate) && windowsCmdReportsMissing(result)) return void 0;
|
|
16896
16903
|
return {
|
|
16897
16904
|
command: candidate,
|
|
16898
16905
|
version: firstLine(result.stdout) ?? firstLine(result.stderr)
|
|
@@ -16891,10 +16891,17 @@ function resolveDispatchStatus(entry, detected, authenticated, issues) {
|
|
|
16891
16891
|
}
|
|
16892
16892
|
return { status: "yes", supported: true };
|
|
16893
16893
|
}
|
|
16894
|
+
function windowsCmdReportsMissing(result) {
|
|
16895
|
+
if (result.ok) return false;
|
|
16896
|
+
const text = `${result.stdout}
|
|
16897
|
+
${result.stderr}`;
|
|
16898
|
+
return /not recognized as an internal or external command/i.test(text) || /the system cannot find the (?:file|path) specified/i.test(text);
|
|
16899
|
+
}
|
|
16894
16900
|
function tryHarnessCommand(candidate, entry, runCommand, timeoutMs) {
|
|
16895
16901
|
const result = runCommand(candidate, entry.versionArgs, timeoutMs);
|
|
16896
16902
|
if (result.error === "ENOENT") return void 0;
|
|
16897
16903
|
if (result.error && result.code === null && !result.stdout && !result.stderr) return void 0;
|
|
16904
|
+
if (isWindowsHarnessShim(candidate) && windowsCmdReportsMissing(result)) return void 0;
|
|
16898
16905
|
return {
|
|
16899
16906
|
command: candidate,
|
|
16900
16907
|
version: firstLine(result.stdout) ?? firstLine(result.stderr)
|
package/plugins/kxm/package.json
CHANGED
|
@@ -941,6 +941,13 @@ export function resolveDispatchStatus(
|
|
|
941
941
|
return { status: "yes", supported: true };
|
|
942
942
|
}
|
|
943
943
|
|
|
944
|
+
function windowsCmdReportsMissing(result: HarnessCommandResult): boolean {
|
|
945
|
+
if (result.ok) return false;
|
|
946
|
+
const text = `${result.stdout}\n${result.stderr}`;
|
|
947
|
+
return /not recognized as an internal or external command/i.test(text)
|
|
948
|
+
|| /the system cannot find the (?:file|path) specified/i.test(text);
|
|
949
|
+
}
|
|
950
|
+
|
|
944
951
|
function tryHarnessCommand(
|
|
945
952
|
candidate: string,
|
|
946
953
|
entry: HarnessCatalogEntry,
|
|
@@ -950,6 +957,9 @@ function tryHarnessCommand(
|
|
|
950
957
|
const result = runCommand(candidate, entry.versionArgs, timeoutMs);
|
|
951
958
|
if (result.error === "ENOENT") return undefined;
|
|
952
959
|
if (result.error && result.code === null && !result.stdout && !result.stderr) return undefined;
|
|
960
|
+
// spawn with shell runs cmd.exe, which exists even when the shim does not.
|
|
961
|
+
// Its "not recognized" answer is a miss, not a detected harness.
|
|
962
|
+
if (isWindowsHarnessShim(candidate) && windowsCmdReportsMissing(result)) return undefined;
|
|
953
963
|
return {
|
|
954
964
|
command: candidate,
|
|
955
965
|
version: firstLine(result.stdout) ?? firstLine(result.stderr),
|
|
@@ -11,7 +11,7 @@ import { deliverInboxNotification } from "./inbox.ts";
|
|
|
11
11
|
import type { HubEvent, MessageRecord } from "./protocol.ts";
|
|
12
12
|
import { sessionTokenFixHint } from "./session-token-hint.ts";
|
|
13
13
|
|
|
14
|
-
const VERSION = "0.7.
|
|
14
|
+
const VERSION = "0.7.155";
|
|
15
15
|
const CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
|
|
16
16
|
const inbox = new Map<string, MessageRecord>();
|
|
17
17
|
const notifiedInbox = new Set<string>();
|
|
@@ -7,24 +7,33 @@
|
|
|
7
7
|
// sequentially, and running them in one process pool stretched the rest of
|
|
8
8
|
// the suite to 268s. engine.test.ts is split by test name across two jobs.
|
|
9
9
|
// permission and runtime run one file at a time in a serial job so they do
|
|
10
|
-
// not steal cores from each other.
|
|
11
|
-
//
|
|
10
|
+
// not steal cores from each other.
|
|
11
|
+
//
|
|
12
|
+
// package-install.test.ts is serial as well. On the Windows light lane for
|
|
13
|
+
// PRs #361, #362, and #363 it took 171s (67s on Linux) and was the file that
|
|
14
|
+
// finished immediately before that lane stopped reporting tests. The job
|
|
15
|
+
// then sat until the 20-minute cancel. npm pack/install stays off the light
|
|
16
|
+
// pool. The other light files are round-robin split into two shards so a
|
|
17
|
+
// Windows job is not one 120-file process.
|
|
12
18
|
//
|
|
13
19
|
// Dynamic `test(\`...\${...}\`)` names stay together as one pattern so a
|
|
14
20
|
// loop is not dropped. Run via `npm run test:ci-shard` so npm_execpath is
|
|
15
|
-
// set for the packed-install test: `engine <index> <total>`, `serial`,
|
|
16
|
-
// `light
|
|
21
|
+
// set for the packed-install test: `engine <index> <total>`, `serial`,
|
|
22
|
+
// `light`, or `light-<index>` (two shards).
|
|
17
23
|
|
|
18
24
|
import { spawn, spawnSync } from "node:child_process";
|
|
19
25
|
import { globSync, readFileSync } from "node:fs";
|
|
20
26
|
import { join } from "node:path";
|
|
21
27
|
|
|
22
28
|
export const SHARD_TOTAL = 2;
|
|
29
|
+
export const LIGHT_SHARD_TOTAL = 2;
|
|
23
30
|
export const ENGINE_FILE = "test/core/engine.test.ts";
|
|
24
|
-
// Solo Node 24 timings: permission 74s, runtime 53s.
|
|
31
|
+
// Solo Node 24 timings: permission 74s, runtime 53s. package-install is 171s
|
|
32
|
+
// on Windows CI and 67s on Linux. One file at a time.
|
|
25
33
|
export const SERIAL_FILES = [
|
|
26
34
|
"test/core/permission.test.ts",
|
|
27
35
|
"test/core/runtime.test.ts",
|
|
36
|
+
"test/core/package-install.test.ts",
|
|
28
37
|
];
|
|
29
38
|
const LIGHT_CONCURRENCY = 4;
|
|
30
39
|
|
|
@@ -125,6 +134,15 @@ export function planLight(root) {
|
|
|
125
134
|
return light;
|
|
126
135
|
}
|
|
127
136
|
|
|
137
|
+
export function planLightShard(root, index, total = LIGHT_SHARD_TOTAL) {
|
|
138
|
+
if (!Number.isInteger(index) || !Number.isInteger(total) || index < 1 || index > total) {
|
|
139
|
+
throw new Error(`light shard ${index}/${total} is outside 1..${total}`);
|
|
140
|
+
}
|
|
141
|
+
const shard = planLight(root).filter((_, fileIndex) => fileIndex % total === index - 1);
|
|
142
|
+
if (shard.length === 0) throw new Error(`light shard ${index}/${total} assigned no unit files`);
|
|
143
|
+
return shard;
|
|
144
|
+
}
|
|
145
|
+
|
|
128
146
|
export function coverageOfShards(root, total = SHARD_TOTAL) {
|
|
129
147
|
const heavy = new Map();
|
|
130
148
|
for (let index = 1; index <= total; index += 1) {
|
|
@@ -138,7 +156,15 @@ export function coverageOfShards(root, total = SHARD_TOTAL) {
|
|
|
138
156
|
|
|
139
157
|
function runNode(args, label, children) {
|
|
140
158
|
return new Promise((resolve) => {
|
|
141
|
-
const child = spawn(process.execPath, args, {
|
|
159
|
+
const child = spawn(process.execPath, args, {
|
|
160
|
+
env: {
|
|
161
|
+
...process.env,
|
|
162
|
+
// Git Credential Manager on windows-latest can wait on a prompt
|
|
163
|
+
// until the job is cancelled. Refuse that wait for every lane.
|
|
164
|
+
GIT_TERMINAL_PROMPT: process.env.GIT_TERMINAL_PROMPT || "0",
|
|
165
|
+
GCM_INTERACTIVE: process.env.GCM_INTERACTIVE || "Never",
|
|
166
|
+
},
|
|
167
|
+
});
|
|
142
168
|
children.add(child);
|
|
143
169
|
let stdout = "";
|
|
144
170
|
let stderr = "";
|
|
@@ -220,10 +246,14 @@ async function main() {
|
|
|
220
246
|
(children) => runNode(testArgs(1, files), "serial", children),
|
|
221
247
|
], 1));
|
|
222
248
|
}
|
|
223
|
-
|
|
224
|
-
|
|
249
|
+
const lightShard = /^light-(\d+)$/.exec(mode);
|
|
250
|
+
if (mode === "light" || lightShard) {
|
|
251
|
+
const files = lightShard
|
|
252
|
+
? planLightShard(root, Number(lightShard[1]), LIGHT_SHARD_TOTAL)
|
|
253
|
+
: planLight(root);
|
|
254
|
+
const label = lightShard ? mode : "light";
|
|
225
255
|
process.exit(await runPool([
|
|
226
|
-
(children) => runNode(testArgs(LIGHT_CONCURRENCY, files),
|
|
256
|
+
(children) => runNode(testArgs(LIGHT_CONCURRENCY, files), label, children),
|
|
227
257
|
], 1));
|
|
228
258
|
}
|
|
229
259
|
if (mode === "engine") {
|
|
@@ -234,7 +264,7 @@ async function main() {
|
|
|
234
264
|
(children) => runNode(testArgs(1, [plan.file], plan.patterns), "engine", children),
|
|
235
265
|
], 1));
|
|
236
266
|
}
|
|
237
|
-
process.stderr.write("usage: ci-unit-shard.mjs <serial|light|engine> [index total]\n");
|
|
267
|
+
process.stderr.write("usage: ci-unit-shard.mjs <serial|light|light-<index>|engine> [index total]\n");
|
|
238
268
|
process.exit(2);
|
|
239
269
|
}
|
|
240
270
|
|