@kontextmind/kxm 0.7.92 → 0.7.93

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.
Files changed (91) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.kxm/workflows/default.yaml +1 -1
  3. package/CHANGELOG.md +204 -0
  4. package/README.md +3 -0
  5. package/docs/README.md +3 -0
  6. package/docs/agent-skills.md +123 -60
  7. package/docs/architecture.md +5 -2
  8. package/docs/cli-reference.md +3527 -0
  9. package/docs/config-reference.md +1943 -0
  10. package/docs/configuration.md +29 -3
  11. package/docs/continuous-improvement.md +122 -10
  12. package/docs/contracts/routing.md +95 -11
  13. package/docs/harness-routing.md +616 -0
  14. package/docs/kxm-handbook.md +106 -19
  15. package/docs/templates/README.md +1 -1
  16. package/docs/test-matrix.md +12 -6
  17. package/docs/troubleshooting.md +2 -2
  18. package/examples/project/.kxm/workflows/fix.yaml +1 -1
  19. package/examples/project/.kxm/workflows/improve.yaml +1 -1
  20. package/package.json +1 -1
  21. package/plugins/kxm/.claude-plugin/plugin.json +9 -10
  22. package/plugins/kxm/README.md +238 -56
  23. package/plugins/kxm/dist/claude-hook.js +10083 -0
  24. package/plugins/kxm/dist/cli.js +3068 -2446
  25. package/plugins/kxm/dist/client.js +64 -0
  26. package/plugins/kxm/dist/core.js +102 -9
  27. package/plugins/kxm/dist/extension.js +210 -68
  28. package/plugins/kxm/dist/mcp-server.js +217 -40
  29. package/plugins/kxm/dist/runtime-supervisor.js +1628 -157
  30. package/plugins/kxm/dist/runtime.js +1874 -298
  31. package/plugins/kxm/dist/server.js +416 -82
  32. package/plugins/kxm/package.json +1 -1
  33. package/plugins/kxm/skills/hints.json +1 -1
  34. package/plugins/kxm/skills/kxm/SKILL.md +48 -24
  35. package/plugins/kxm/skills/kxm/references/protocol.md +3 -3
  36. package/plugins/kxm/skills/kxm-context-memory/SKILL.md +61 -21
  37. package/plugins/kxm/skills/kxm-definitions/SKILL.md +9 -0
  38. package/plugins/kxm/skills/kxm-harness-auth/SKILL.md +82 -16
  39. package/plugins/kxm/skills/kxm-harvest/SKILL.md +1 -1
  40. package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +55 -27
  41. package/plugins/kxm/skills/kxm-insights/SKILL.md +1 -1
  42. package/plugins/kxm/skills/kxm-mind/SKILL.md +2 -2
  43. package/plugins/kxm/skills/{kxm-setup → kxm-mind-setup}/SKILL.md +4 -4
  44. package/plugins/kxm/skills/kxm-peer/SKILL.md +68 -93
  45. package/plugins/kxm/skills/kxm-project-setup/SKILL.md +156 -23
  46. package/plugins/kxm/skills/kxm-projects/SKILL.md +1 -1
  47. package/plugins/kxm/skills/kxm-protocol/SKILL.md +1 -1
  48. package/plugins/kxm/skills/kxm-query/SKILL.md +1 -1
  49. package/plugins/kxm/skills/kxm-routing-improve/SKILL.md +74 -15
  50. package/plugins/kxm/skills/kxm-runs/SKILL.md +46 -17
  51. package/plugins/kxm/skills/kxm-session/SKILL.md +64 -36
  52. package/plugins/kxm/skills/kxm-skill-lifecycle/SKILL.md +44 -15
  53. package/plugins/kxm/skills/kxm-tasks/SKILL.md +16 -4
  54. package/plugins/kxm/skills/kxm-triage/SKILL.md +1 -1
  55. package/plugins/kxm/skills/kxm-work/SKILL.md +1 -1
  56. package/plugins/kxm/skills/kxm-workflow/SKILL.md +60 -19
  57. package/plugins/kxm/src/arbiter.ts +67 -22
  58. package/plugins/kxm/src/autocomplete.ts +1 -1
  59. package/plugins/kxm/src/claude-hook.ts +192 -0
  60. package/plugins/kxm/src/cli/project.ts +11 -5
  61. package/plugins/kxm/src/cli/system.ts +85 -13
  62. package/plugins/kxm/src/cli/types.ts +4 -1
  63. package/plugins/kxm/src/cli/workflows.ts +18 -16
  64. package/plugins/kxm/src/cli.ts +23 -13
  65. package/plugins/kxm/src/client.ts +15 -4
  66. package/plugins/kxm/src/commands.ts +19 -9
  67. package/plugins/kxm/src/config.ts +42 -7
  68. package/plugins/kxm/src/context-packet.ts +14 -2
  69. package/plugins/kxm/src/context.ts +16 -5
  70. package/plugins/kxm/src/dispatch-context.ts +286 -0
  71. package/plugins/kxm/src/engine-plan.ts +40 -0
  72. package/plugins/kxm/src/engine.ts +138 -6
  73. package/plugins/kxm/src/hub-env.ts +17 -1
  74. package/plugins/kxm/src/hub.ts +92 -29
  75. package/plugins/kxm/src/improve-sources.ts +228 -0
  76. package/plugins/kxm/src/improve.ts +325 -140
  77. package/plugins/kxm/src/local-snapshot.ts +101 -42
  78. package/plugins/kxm/src/mcp-server.ts +129 -30
  79. package/plugins/kxm/src/project-config.ts +25 -0
  80. package/plugins/kxm/src/protocol.ts +11 -0
  81. package/plugins/kxm/src/relevance.ts +138 -0
  82. package/plugins/kxm/src/retrospective.ts +16 -10
  83. package/plugins/kxm/src/runtime-service.ts +8 -1
  84. package/plugins/kxm/src/runtime-supervisor.ts +16 -2
  85. package/plugins/kxm/src/session-token-hint.ts +17 -0
  86. package/plugins/kxm/src/suggest.ts +7 -7
  87. package/plugins/kxm/src/workflow-manager.ts +80 -78
  88. package/plugins/kxm/src/workflow.ts +202 -12
  89. package/scripts/build-runtime.mjs +7 -1
  90. package/scripts/check-generated.mjs +1 -0
  91. package/scripts/emit-codex-artifacts.mjs +1 -1
@@ -0,0 +1,3527 @@
1
+ # KXM CLI reference
2
+
3
+ This page documents every command and subcommand the `kxm` operator CLI registers in KXM 0.7.1 (`@kontextmind/kxm`). For each command it states what the command does, which files and services it reads and writes, whether it needs a running hub or the KXM Runtime supervisor, what `--json` returns, and the exit codes and refusal codes you are likely to see. It supersedes the "Complete CLI guide" section of the [KXM Handbook](kxm-handbook.md). Environment variables are described in [Configuration](configuration.md); this page names them only where a command reads them directly.
4
+
5
+ Output shown under examples was captured from KXM 0.7.1 run from a source checkout, inside a throwaway Git repository, with `HOME`, `KXM_STATE_HOME`, `KXM_USER_CONFIG_DIR`, and the XDG directories pointed at a temporary directory, no harness CLIs on `PATH`, and (where a hub was needed) a disposable hub on a random loopback port. Paths are shortened to `/work/proj` (the project), `/work/kxm` (the KXM checkout), `/state` (the user state root), and `~/.config/kxm` (the user config directory); session tokens are replaced with `<token>`, the machine's host name with `host.local`, and long JSON is trimmed with `...`. Every command either plans under `--dry-run` without changing anything or refuses the flag (see [Dry runs](#dry-runs)); the dry-run examples were captured from the current source tree, with a digest of the throwaway tree taken before and after to confirm that nothing was written. An example captioned "Not run" was not executed for this reference because it starts a long-lived process, writes durable state, stores credentials, or calls an external service; its output is not shown.
6
+
7
+ ## Contents
8
+
9
+ - [Invoking the CLI](#invoking-the-cli)
10
+ - [Global options](#global-options)
11
+ - [Output conventions](#output-conventions)
12
+ - [Where commands read and write](#where-commands-read-and-write)
13
+ - [Task to command](#task-to-command)
14
+ - Setup: [`init`](#kxm-init), [`config`](#kxm-config), [`completion`](#kxm-completion), [`trust`](#kxm-trust)
15
+ - Hub and sessions: [`hub`](#hub-commands), [`session`](#kxm-session), [`dash`](#kxm-dash), [`studio`](#kxm-studio)
16
+ - Harnesses, models, and roles: [`harness`](#kxm-harness), [`auth`](#kxm-auth), [`update`](#kxm-update), [`models`](#kxm-models), [`routes`](#kxm-routes), [`role`](#kxm-role)
17
+ - Running work: [`run`](#kxm-run), [`runs`](#kxm-runs), [`runtime`](#kxm-runtime), [`agent`](#kxm-agent), [`workflow`](#kxm-workflow), [`gate`](#kxm-gate), [`peer`](#kxm-peer), [`task`](#kxm-task), [`goal`](#kxm-goal), [`suggest`](#kxm-suggest), [`explain`](#kxm-explain)
18
+ - Context and learning: [`context`](#kxm-context), [`memory`](#kxm-memory), [`skills`](#kxm-skills), [`improve`](#kxm-improve), [`routing`](#kxm-routing)
19
+ - Operations: [`backup`](#kxm-backup), [`restore`](#kxm-restore), [`tenant`](#kxm-tenant), [`ssh`](#kxm-ssh), [`help`](#kxm-help)
20
+ - [Known behavior gaps in 0.7.1](#known-behavior-gaps-in-071)
21
+
22
+ ## Invoking the CLI
23
+
24
+ An installed CLI is on `PATH` as `kxm`. Install it from the versioned release tarball as described in the [KXM Handbook](kxm-handbook.md#install-the-kxm-operator-cli).
25
+
26
+ ```bash
27
+ kxm --help
28
+ ```
29
+
30
+ ```text
31
+ Usage: kxm [options] [command]
32
+
33
+ KXM local-first orchestration CLI
34
+ ...
35
+ ```
36
+
37
+ From a source checkout, run the wrapper script instead. It executes the committed `plugins/kxm/dist/cli.js`, so run `npm run build` after changing CLI source.
38
+
39
+ ```bash
40
+ node scripts/kxm.mjs --help
41
+ ```
42
+
43
+ Everywhere this page shows `kxm`, a source checkout can substitute `node scripts/kxm.mjs`.
44
+
45
+ Help is available at every level and exits 0:
46
+
47
+ ```bash
48
+ kxm workflow --help
49
+ ```
50
+
51
+ ```bash
52
+ kxm help workflow
53
+ ```
54
+
55
+ ```bash
56
+ kxm workflow help start
57
+ ```
58
+
59
+ Running a command group without a subcommand prints that group's help and exits 2. Five groups have a default subcommand instead: `kxm config` runs `config list`, `kxm role` runs `role list`, `kxm goal` runs `goal list`, `kxm task` runs `task list`, and `kxm improve` runs `improve report`. `kxm models` opens an interactive screen.
60
+
61
+ ## Global options
62
+
63
+ | Option | Argument | Default | Description |
64
+ |---|---|---|---|
65
+ | `--json` | none | off | Print machine-readable JSON |
66
+ | `--dry-run` | none | off | Plan without making changes |
67
+ | `--workspace` | `<dir>` | `.kxm` (or `KXM_WORKSPACE_DIR`) | Workspace directory |
68
+ | `-V`, `--version` | none | none | Print the installed kxm version |
69
+ | `-h`, `--help` | none | none | display help for command |
70
+
71
+ - `--json`, `--dry-run`, and `--workspace` are registered on the root, on every group, and on every subcommand, so `kxm --json config list`, `kxm config --json list`, and `kxm config list --json` are equivalent.
72
+ - `kxm init` registers only `--json` and `--dry-run`. It, `kxm trust diff`, `kxm trust check`, `kxm run`, and `kxm task run` refuse `--workspace` (including a root-level `--workspace`) with exit 2 and error `workspace_option_unsupported`, because they discover the project from the current directory.
73
+ - `--workspace <dir>` resolves against `KXM_WORKDIR` (or the current directory) and derives `config/`, `logs/`, `assets/`, and `state/` under it, overriding `KXM_CONFIG_DIR`, `KXM_LOGS_DIR`, `KXM_ASSETS_DIR`, and `KXM_STATE_DIR`. Only commands that use workspace directories are affected; see [Where commands read and write](#where-commands-read-and-write).
74
+ - `--version` ignores `--json` and prints the bare version.
75
+
76
+ ```bash
77
+ kxm -V
78
+ ```
79
+
80
+ ```text
81
+ 0.7.1
82
+ ```
83
+
84
+ ## Output conventions
85
+
86
+ ### JSON results
87
+
88
+ - Every JSON result carries a `schema` field. Most commands print `kxm.cli-result.v1` with `ok` and `command` fields plus command-specific keys.
89
+ - Gate-style commands print a `kxm.worker-result.v1` envelope, which adds `worker`, `createdAt`, `outcome` (`passed`, `warning`, or `failed`), and `summary`: `gate validate`, `gate artifacts-exist`, `gate degrade`, `gate signal`, `workflow signal`, `gate github watch`, and `agent worker --dry-run`. Outside `--dry-run`, the `gate` subcommands and `workflow signal` also append the envelope to `.kxm/logs/telemetry.jsonl`.
90
+ - `session brief` prints `kxm.session-brief.v1` and `tenant status` prints `kxm.tenant-status.v1`.
91
+ - The `command` field is not always the words you typed: `hub stop` and `session stop` report `stop`, `session token` reports `auth token`, `routes admit` and `routes disable` report `routes admitted` and `routes disabled`, `models inventory-refresh` reports `models inventory refresh`, `workflow export` reports `retrospective export`, `gate degrade` reports `workflow degrade`, `gate signal` and `workflow signal` report `signal`, `gate github watch` reports `github watch`, `agent worker` reports `worker`, and `improve report` reports `improve`.
92
+ - A result with `ok: false` is written to stderr in both text and JSON mode; everything else goes to stdout. `runtime status` is the exception: when the supervisor is down it prints `ok: true, running: false` on stdout and exits 1.
93
+ - `peer` subcommands and `workflow checkpoint|record|wait` print the hub's result object as returned, tagged with `schema` but without `ok` or `command`. Their failures print `{"ok":false,"error":"command_failed","detail":"..."}`. Text mode prints the same JSON.
94
+ - Several error paths ignore `--json` and print one plain line on stderr: argument checks in `agent worker`, `session start`, `workflow start`, `gate degrade`, `gate signal`, and `gate github watch`; every error from `config`, `role`, `workflow definitions|add|remove|modify` (except the three `workflow add --template` refusals, which honor `--json`), `goal`, `task`, `memory`, `skills` (other than `skills create --dry-run`), `suggest`, `studio`, and `completion`. Check the exit code before parsing stdout.
95
+ - Every `context` subcommand exits 1 with a Node.js stack trace and no JSON when the hub cannot be reached.
96
+ - Output is redacted. Values of environment variables whose names contain `TOKEN`, `SECRET`, `KEY`, or `PASSWORD` are replaced with `[redacted]`, and 64-character hex strings are replaced unless they appear in a known digest field such as `configRevision` or `sha256`. `session brief` and `auth token` print the session token itself; treat their output as a credential.
97
+
98
+ ### Dry runs
99
+
100
+ `--dry-run` changes nothing: no file is written, deleted, or moved, no request that changes hub or Runtime state is sent, no process is started, and no remote command runs. A command that cannot say what it would do without doing some of it refuses the flag instead of acting.
101
+
102
+ - Plan with `planned`: `backup`, `restore`, `config set`, `role add|remove|modify|set-host|resume`, `workflow add|remove|modify`, `goal create`, `task create|run|sync`, `memory note|sync`, `skills evaluate|promote|reject`, `context promote`, `context wiki-compile --out`, `auth token`, `session token`, `session brief`, and `ssh run|file|close`. Each prints its normal result plus `dryRun: true` and `planned`, a list of `{action, target}` entries whose `action` is `write`, `delete`, `move`, `request`, or `ssh`. Text mode prints `dry run: <summary>` and one indented `would <action> <target>` line per entry (`session brief` appends `dry run: would write <file>` lines to the brief instead).
103
+ - Plan in their own shape (described in each section): `init`, `run`, `runs drive|cancel`, `runtime start|stop|sync-retry`, `hub start|stop|bind|unbind`, `session start|stop`, `agent worker`, `dash`, `studio serve`, `models inventory-refresh`, `routes admit|disable`, `update`, `completion install`, `workflow start|signal|export|checkpoint|record|wait`, every `peer` subcommand, `gate degrade|signal`, `skills create`, and `improve report`. `gate github watch --dry-run` still polls GitHub but does not post the signal.
104
+ - Read-only commands run as usual, without leaving a trace: a local SQLite store is opened without creating `-wal` or `-shm` files, and `runs status|list|receipt` only attach to a running Runtime supervisor. With no supervisor running they exit 2 with `dry_run_unsupported` instead of starting one. `context wiki-compile --dry-run` still asks the hub to compile, which is a read.
105
+ - Refused: `kxm models` (the interactive screen) exits 2 with `dry_run_unsupported`. The CLI keeps one list of the commands that answer `--dry-run` and refuses every other command the same way before it runs, so a command added without dry-run support fails closed.
106
+
107
+ ```bash
108
+ kxm config set user.theme light --dry-run
109
+ ```
110
+
111
+ ```text
112
+ dry run: set user.theme = light in project config
113
+ would write /work/proj/.kxm/config.yaml
114
+ ```
115
+
116
+ ```bash
117
+ kxm models --dry-run --json
118
+ ```
119
+
120
+ ```text
121
+ {"schema":"kxm.cli-result.v1","ok":false,"command":"models","dryRun":true,"error":"dry_run_unsupported","detail":"this command cannot plan without making changes; rerun without --dry-run"}
122
+ ```
123
+
124
+ For this page, 63 `--dry-run` invocations (every command in the first list, the refusals, and a sample of the others) ran in a throwaway project seeded with a role, a global workflow, a task, a goal, a skill candidate with passing evaluations, a WAL-mode hub store, and a backup, with no harness CLIs on `PATH`. A digest of every file under the temporary root before and after showed nothing created, changed, or removed; no Runtime supervisor was started; and the hub saw only the wiki compile. Harness probes (`update`, `suggest`, `harness list`) run the installed harness CLIs to read their versions and login state, and those CLIs can write their own files: Codex creates `~/.codex/tmp/` whenever it runs.
125
+
126
+ ### Exit codes
127
+
128
+ | Code | Meaning |
129
+ |---|---|
130
+ | 0 | Success, including `--help`, `--version`, and dry-run plans. |
131
+ | 1 | The command ran and failed or found a problem: an `ok: false` result, an unreachable hub for `hub view`, permission expansions for `trust check`, a stopped supervisor for `runtime status`, missing local state, or a planning-only `init`. |
132
+ | 2 | Usage error: unknown command or option, a missing argument or required option, a value KXM rejects before acting, `--workspace` where unsupported, conflicting flags, a group run without a subcommand, a removed command (`removed_command`), or `--dry-run` on a command that cannot plan (`dry_run_unsupported`). |
133
+ | 4 | `gate github watch` timed out and posted (or, under `--dry-run`, would have posted) a signed `failed` signal. |
134
+ | other | `hub start` and `agent worker` return the exit code of the foreground process; `ssh run` returns the remote command's exit code. |
135
+
136
+ ## Where commands read and write
137
+
138
+ | Location | Contents | Used by |
139
+ |---|---|---|
140
+ | Project files under `<project>/.kxm/` (reviewed in Git) | `project.yaml`, `agents/`, `workflows/`, `gates.yaml`, `repo/`, `template-provenance.yaml`, `routes.yaml`, `models/inventory.yaml`, `roles/`, `role-hosts.yaml`, `modes.yaml`, `prices.yaml` | `init`, `trust`, `run`, `models`, `routes`, `role`, `workflow definitions\|add\|remove\|modify`, `explain`, `studio` |
141
+ | Local project records under `<project>/.kxm/` | `config.yaml` (project scope), `goals/`, `tasks/`, `memory/`, `skills/`, `candidates/`, `backups/`, `run/ssh-sockets/` | `config`, `goal`, `task`, `memory`, `skills`, `improve`, `backup`, `ssh` |
142
+ | Workspace directories (`.kxm/state`, `.kxm/logs`, `.kxm/assets`, `.kxm/config`; moved by `--workspace` or `KXM_*_DIR`) | hub SQLite store `state/kxm.db` (or `KXM_DATA_PATH`), `state/hub.pid`, `state/session-brief.json`, `logs/telemetry.jsonl`, `logs/kxm-hub.jsonl`, `assets/sessions/`, `assets/workflows/`, `assets/improvements/`, `assets/retrospectives/`, legacy `config/agents.json` and `config/gates.json` | `hub`, `session`, `dash`, `agent worker`, `workflow list\|get\|export`, `gate`, `improve`, `routing report` |
143
+ | User config directory (`KXM_USER_CONFIG_DIR`, default `~/.config/kxm`) | `config.yaml` (user scope), `session.token`, global `roles/` and `workflows/`, `role-hosts.yaml`, `completions/` | `config --scope user`, `auth token`, `session brief\|token`, `role`/`workflow` with `--scope global`, `studio serve`, `completion install` |
144
+ | User state root (`KXM_STATE_HOME`; macOS `~/Library/Application Support/KXM`; Linux `$XDG_STATE_HOME/kxm` or `~/.local/state/kxm`; Windows `%LOCALAPPDATA%\KXM`) | `hub-env.json` (persisted hub credentials), `hub-binding.json`, `runtime/` (Runtime supervisor registry and per-project run stores), `update.yaml`, repository bindings | `hub start\|bind\|unbind`, every hub client, `run`, `runs`, `runtime`, `tenant status`, `update`, `init --repository`, and (read-only, the project's run store) `improve` and `routing report` |
145
+
146
+ `init`, `trust`, `run`, `runs`, `runtime sync-retry`, `tenant status`, and `studio layout` find the project root by walking up from the current directory. `improve` and `routing report` use the current directory's Git root when it holds `.kxm/project.yaml`, to find the project's Runtime run store (and, for `improve`, its configuration and default candidate directory). `config`, `role`, `workflow definitions|add|remove|modify`, `goal`, `task`, `memory`, `skills`, `backup`, `restore`, `studio serve`, and `ssh` (socket directory) use `.kxm` in the current directory. Run those from the project root.
147
+
148
+ ## Task to command
149
+
150
+ | Task | Commands |
151
+ |---|---|
152
+ | Set up a project | [`kxm init`](#kxm-init), [`kxm trust check`](#kxm-trust-check), [`kxm config set`](#kxm-config-set) |
153
+ | Start or bind a hub | [`kxm hub start`](#kxm-hub-start), [`kxm hub bind`](#kxm-hub-bind), [`kxm hub view`](#kxm-hub-view) |
154
+ | Check harness installs and authentication | [`kxm harness list`](#kxm-harness-list), [`kxm update --dry-run`](#kxm-update) |
155
+ | Create and drive a run | [`kxm run`](#kxm-run), [`kxm runs drive`](#kxm-runs-drive), [`kxm runtime status`](#kxm-runtime-status) |
156
+ | Inspect runs | [`kxm runs list`](#kxm-runs-list), [`kxm runs status`](#kxm-runs-status), [`kxm runs receipt`](#kxm-runs-receipt), [`kxm tenant status`](#kxm-tenant-status), [`kxm workflow list`](#kxm-workflow-list) |
157
+ | Message peers | [`kxm peer list`](#kxm-peer-list), [`kxm peer send`](#kxm-peer-send), [`kxm peer await`](#kxm-peer-await), [`kxm peer fanout`](#kxm-peer-fanout) |
158
+ | Operate gates and evidence | [`kxm gate validate`](#kxm-gate-validate), [`kxm gate artifacts-exist`](#kxm-gate-artifacts-exist), [`kxm gate signal`](#kxm-gate-signal), [`kxm workflow checkpoint`](#kxm-workflow-checkpoint) |
159
+ | Query context and memory | [`kxm context get`](#kxm-context-get), [`kxm context recall`](#kxm-context-recall), [`kxm memory brief`](#kxm-memory-brief), [`kxm memory note`](#kxm-memory-note) |
160
+ | Read improvement and routing reports | [`kxm improve report`](#kxm-improve-report), [`kxm routing report`](#kxm-routing-report) |
161
+ | Back up and restore | [`kxm backup`](#kxm-backup), [`kxm restore`](#kxm-restore) |
162
+ | Update KXM and harnesses | [`kxm update --check`](#kxm-update), [`kxm update --kxm`](#kxm-update) |
163
+
164
+ ## `kxm init`
165
+
166
+ ```text
167
+ kxm init [--name <name>] [--project-id <id>] [--repository <id=absolute-path>]...
168
+ ```
169
+
170
+ Creates, validates, repairs, resumes, or joins a KXM project at the Git root that contains the current directory. A new project gets a minimal configuration: one coordinator agent, one implementer agent, a `test` command gate, and a `default` plan-implement-verify workflow. An existing project is validated without rewriting. Conflict-free template updates to non-authority fields are applied; authority changes, overlapping edits, and provenance-free or legacy state stay planning-only. `init` does not start a hub or the Runtime.
171
+
172
+ | Option | Argument | Default | Description |
173
+ |---|---|---|---|
174
+ | `--name` | `<name>` | Git root directory name | Project display name for a new project |
175
+ | `--project-id` | `<id>` | generated | Stable project ID for controlled provisioning |
176
+ | `--repository` | `<id=absolute-path>` | `[]` | Bind a member repository outside Git configuration |
177
+
178
+ - Global options: `--json` and `--dry-run` only; `--workspace` exits 2 with `workspace_option_unsupported`.
179
+ - `--project-id` must match `prj_` followed by 6 to 128 letters, digits, `_`, or `-`. `--repository` is repeatable; each value must be `<id>=<absolute path>`, and a repeated ID fails with `repository_binding_argument_duplicate`.
180
+ - Needs a Git repository. Does not need a hub or the Runtime.
181
+ - Writes `.kxm/project.yaml`, `.kxm/agents/coordinator.yaml`, `.kxm/agents/implementer.yaml`, `.kxm/gates.yaml`, `.kxm/repo/repo.yaml`, `.kxm/workflows/default.yaml`, and `.kxm/template-provenance.yaml`, using a `.kxm-init-transaction` directory at the Git root while a create or repair is in flight. Repository bindings are written under the user state root, never into Git. `--dry-run` writes nothing.
182
+ - On an interactive terminal without `--json` or `--dry-run`, a successful create or join offers to install shell completion (suppress with `KXM_SKIP_COMPLETION_PROMPT=1`) and to write workflow-guide agents for authenticated harnesses (suppress with `KXM_SKIP_GUIDE_SETUP_PROMPT=1`).
183
+ - JSON keys: `action` (`planned`, `created`, `joined`, `repaired`, `resumed`, or `validated`), `mode`, `inspectedFrom`, `projectRoot`, `changesRequired`, `legacyInputs`, `issues`, `configRevision`, `files`, `plannedOnly`, and, when relevant, `localBindingFile`, `bindingsChanged`, `repairPlan`, `resumePending`, `transactionKind`.
184
+ - Exit 0 for every completed action and every dry-run plan. Exit 1 when the result is planning-only (legacy state, blocked repair, partial state without provenance) or for `initialization_failed` (with `issues`) and `initialization_io_failed`. A planning-only text result prints the reason and then one `<file>: <code>: <message>` line per validation issue, for example `.kxm/workflows/first.yaml: gate_outcome_impossible: ...`.
185
+
186
+ Preview what a new project would contain:
187
+
188
+ ```bash
189
+ kxm init --dry-run --json
190
+ ```
191
+
192
+ ```text
193
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"init","action":"planned","mode":"create","inspectedFrom":"/work/proj","projectRoot":"/work/proj","changesRequired":true,"legacyInputs":[],"issues":[],"files":[".kxm/agents/coordinator.yaml",".kxm/agents/implementer.yaml",".kxm/gates.yaml",".kxm/project.yaml",".kxm/repo/repo.yaml",".kxm/template-provenance.yaml",".kxm/workflows/default.yaml"],"plannedOnly":true}
194
+ ```
195
+
196
+ Create the project:
197
+
198
+ ```bash
199
+ kxm init --name "Demo"
200
+ ```
201
+
202
+ ```text
203
+ initialized KXM project at /work/proj
204
+ ```
205
+
206
+ Validate it again later:
207
+
208
+ ```bash
209
+ kxm init --json
210
+ ```
211
+
212
+ ```text
213
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"init","action":"validated","mode":"ready",...,"configRevision":"sha256:80457232cbfc...","files":[],"plannedOnly":false}
214
+ ```
215
+
216
+ Join a cloned multi-repository project (Not run):
217
+
218
+ ```bash
219
+ kxm init --repository api=/absolute/path/to/api
220
+ ```
221
+
222
+ ## `kxm config`
223
+
224
+ Reads and writes personalization settings (`kxm.config.v1`). The merged view layers built-in defaults, the user file `<KXM_USER_CONFIG_DIR>/config.yaml`, and the project file `.kxm/config.yaml` in the current directory. `kxm config` with no subcommand runs `config list`. None of the subcommands needs a hub.
225
+
226
+ ### `kxm config list`
227
+
228
+ ```text
229
+ kxm config list
230
+ ```
231
+
232
+ Prints the resolved configuration. Text output shows `schema`, `user`, `defaults`, `dash`, `sync`, and `loadedFrom`; the JSON `config` object also contains `hub`, `improvement`, `routing`, and `telemetry`. An empty `loadedFrom` means no file supplies values yet.
233
+
234
+ No command-specific options.
235
+
236
+ - Reads only. JSON keys: `config`.
237
+ - Exit 0; exit 1 with a plain `config list failed:` line when a file cannot be parsed.
238
+
239
+ ```bash
240
+ kxm config list
241
+ ```
242
+
243
+ ```text
244
+ schema: kxm.config.v1
245
+ user:
246
+ theme: dark
247
+ preferredCritics:
248
+ - reviewer-arch
249
+ - reviewer-cli
250
+ tokenBudget: 16000
251
+ defaults:
252
+ workflow: software-engineering/feature-implementation
253
+ harness: pi
254
+ dash:
255
+ defaultScreen: agents
256
+ refreshIntervalMs: 1000
257
+ autoOpen: false
258
+ sync:
259
+ defaultTracker: none
260
+ loadedFrom: {}
261
+ ```
262
+
263
+ ```bash
264
+ kxm config list --json
265
+ ```
266
+
267
+ ```text
268
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"config list","config":{"schema":"kxm.config.v1",...,"hub":{"autoStart":"background"},...,"loadedFrom":{}}}
269
+ ```
270
+
271
+ ### `kxm config get`
272
+
273
+ ```text
274
+ kxm config get <key>
275
+ ```
276
+
277
+ Prints one value by dotted key from the merged view.
278
+
279
+ - Arguments: `<key>`, a dotted path such as `hub.autoStart`.
280
+ - No command-specific options. Reads only.
281
+ - An unknown key prints `(undefined)` (JSON: no `value` key) and exits 0.
282
+ - JSON keys: `key`, `value`.
283
+
284
+ ```bash
285
+ kxm config get hub.autoStart
286
+ ```
287
+
288
+ ```text
289
+ background
290
+ ```
291
+
292
+ ```bash
293
+ kxm config get defaults.harness --json
294
+ ```
295
+
296
+ ```text
297
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"config get","key":"defaults.harness","value":"pi"}
298
+ ```
299
+
300
+ ### `kxm config set`
301
+
302
+ ```text
303
+ kxm config set <key> <value> [--scope user|project]
304
+ ```
305
+
306
+ Writes one value into exactly one scope file.
307
+
308
+ | Option | Argument | Default | Description |
309
+ |---|---|---|---|
310
+ | `--scope` | `<scope>` | `project` | Configuration scope: user or project (default: project) |
311
+
312
+ - Arguments: `<key>` (dotted path) and `<value>`. The value is parsed as JSON when it parses (`true`, `5`, `"text"`, `{"a":1}`); otherwise it is stored as a string. Keys are not validated.
313
+ - `--scope user` writes `<KXM_USER_CONFIG_DIR>/config.yaml`; any other value writes `.kxm/config.yaml` in the current directory.
314
+ - Mutates. `--dry-run` names the file it would write and writes nothing.
315
+ - JSON keys: `key`, `value`, `scope` (plus `dryRun` and `planned` under `--dry-run`). Exit 1 with a plain `config set failed:` line on error.
316
+
317
+ ```bash
318
+ kxm config set user.theme light --dry-run --json
319
+ ```
320
+
321
+ ```text
322
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"config set","key":"user.theme","value":"light","scope":"project","dryRun":true,"planned":[{"action":"write","target":"/work/proj/.kxm/config.yaml"}]}
323
+ ```
324
+
325
+ Stop harness extensions from starting a hub (Not run):
326
+
327
+ ```bash
328
+ kxm config set hub.autoStart off --scope user
329
+ ```
330
+
331
+ ## `kxm completion`
332
+
333
+ ```text
334
+ kxm completion <bash|zsh|fish>
335
+ ```
336
+
337
+ Prints a shell completion script to stdout. With no shell, prints `usage: kxm completion <bash|zsh|fish> | kxm completion install [--shell <shell>] [--no-path]` on stderr and exits 2; an unsupported shell exits 1. The script is printed as text even with `--json`. The generated command list lags the CLI in 0.7.1 (see [Known behavior gaps](#known-behavior-gaps-in-071)).
338
+
339
+ - Arguments: `[shell]`, one of `bash`, `zsh`, `fish` (or the `install` subcommand).
340
+ - Reads only.
341
+
342
+ Print the bash script:
343
+
344
+ ```bash
345
+ kxm completion bash
346
+ ```
347
+
348
+ ```text
349
+ #!/usr/bin/env bash
350
+ # Bash completion for kxm
351
+ ...
352
+ ```
353
+
354
+ Save the zsh script into a directory on your `fpath` (Not run):
355
+
356
+ ```bash
357
+ kxm completion zsh > ~/.zfunc/_kxm
358
+ ```
359
+
360
+ ### `kxm completion install`
361
+
362
+ ```text
363
+ kxm completion install [--shell <shell>] [--no-path]
364
+ ```
365
+
366
+ Installs tab completion for the detected or given shell and, unless `--no-path` is set, adds the directory containing `kxm` to `PATH` in the shell's rc file.
367
+
368
+ | Option | Argument | Default | Description |
369
+ |---|---|---|---|
370
+ | `--shell` | `<shell>` | detected from `$SHELL` | Shell to install for (bash, zsh, fish; default: detect from $SHELL) |
371
+ | `--no-path` | none | off | Only install completion; do not add a PATH entry |
372
+
373
+ - Writes `<KXM_USER_CONFIG_DIR>/completions/kxm.<shell>` and a source stanza in `~/.bashrc` (or `~/.bash_profile`) or `$ZDOTDIR/.zshrc` (or `~/.zshrc`); fish writes `~/.config/fish/completions/kxm.fish` (or under `XDG_CONFIG_HOME`) and needs no rc edit. Idempotent. Honors `--dry-run`.
374
+ - JSON keys: `shell`, `scriptPath`, `rcFile`, `rcModified`, `alreadyInstalled`, `path`, `dryRun`. Exit 1 with `shell_not_detected` when the shell cannot be determined.
375
+
376
+ Preview the change (Not run):
377
+
378
+ ```bash
379
+ kxm completion install --shell zsh --dry-run
380
+ ```
381
+
382
+ ## `kxm trust`
383
+
384
+ Compares the authority-bearing fields of the project configuration against a base Git revision. The base is materialized into a temporary shadow with a sanitized environment; nothing in the project is written. Both subcommands refuse `--workspace` (exit 2) and need no hub.
385
+
386
+ ### `kxm trust diff`
387
+
388
+ ```text
389
+ kxm trust diff [--base <revision>]
390
+ ```
391
+
392
+ Prints the structured `kxm.permission-diff.v1` report: every authority-bearing change classified as an expansion, narrowing, or neutral change, with per-field value hashes.
393
+
394
+ | Option | Argument | Default | Description |
395
+ |---|---|---|---|
396
+ | `--base` | `<revision>` | `HEAD` | Base Git revision (default: HEAD) |
397
+
398
+ - Reads only. Exit 0 even when expansions exist.
399
+ - JSON keys: `baseRevision`, `candidateRevision`, `requiresReview`, `expansions`, `narrowings`, `neutralChanges` (counts), `changes` (each with `resource`, `path`, `field`, `direction`, `summary`, `baseValueSha256`, `candidateValueSha256`).
400
+ - Errors: `trust_diff_failed` with `issues` (for example `resource_missing` when the base revision has no `.kxm/project.yaml`), `trust_diff_io_failed`. Both exit 1.
401
+
402
+ After widening the coordinator's repository access from `read` to `write`:
403
+
404
+ ```bash
405
+ kxm trust diff
406
+ ```
407
+
408
+ ```text
409
+ permission diff: sha256:80457232cbfc… -> sha256:7428fde55ca4…
410
+ EXPANSION .kxm/agents/coordinator.yaml /repositories/control repository-access changed (expansion)
411
+ 1 expansion(s) require explicit reviewed trust action
412
+ ```
413
+
414
+ Compare against an older commit:
415
+
416
+ ```bash
417
+ kxm trust diff --base main --json
418
+ ```
419
+
420
+ Not run against `main`; a base without `.kxm/project.yaml` fails with `resource_missing`.
421
+
422
+ ### `kxm trust check`
423
+
424
+ ```text
425
+ kxm trust check [--base <revision>]
426
+ ```
427
+
428
+ Same comparison as `trust diff`, but exits 1 when any expansion exists so an authority-widening change cannot merge without review. Formatting and description-only changes never fail the check.
429
+
430
+ | Option | Argument | Default | Description |
431
+ |---|---|---|---|
432
+ | `--base` | `<revision>` | `HEAD` | Base Git revision (default: HEAD) |
433
+
434
+ - Reads only. JSON keys are the same as `trust diff`; `ok` is `false` when review is required.
435
+ - Exit 0 with no expansions, 1 with expansions or on failure.
436
+
437
+ ```bash
438
+ kxm trust check
439
+ ```
440
+
441
+ ```text
442
+ permission diff: sha256:80457232cbfc… -> sha256:7428fde55ca4…
443
+ EXPANSION .kxm/agents/coordinator.yaml /repositories/control repository-access changed (expansion)
444
+ 1 expansion(s) require explicit reviewed trust action
445
+ trust check failed: review every expansion above before merging
446
+ ```
447
+
448
+ ```bash
449
+ kxm trust check --json
450
+ ```
451
+
452
+ ```text
453
+ {"schema":"kxm.cli-result.v1","ok":false,"command":"trust check",...,"requiresReview":true,"expansions":1,"narrowings":0,"neutralChanges":0,"changes":[{"resource":".kxm/agents/coordinator.yaml","path":"/repositories/control","field":"repository-access","direction":"expansion",...}]}
454
+ ```
455
+
456
+ <a id="hub-commands"></a>
457
+
458
+ ## `kxm hub`
459
+
460
+ Starts, inspects, and stops the local KXM hub, and binds this machine to a hub. Hub clients pick their target in this order: `KXM_SERVER_URL`, the binding written by `kxm hub bind`, then `http://127.0.0.1:7331`.
461
+
462
+ ### `kxm hub view`
463
+
464
+ ```text
465
+ kxm hub view
466
+ ```
467
+
468
+ Calls `GET /health` and `GET /ready` on the target hub and reports whether the URL is loopback or remote.
469
+
470
+ No command-specific options.
471
+
472
+ - Needs a hub to succeed. Reads only.
473
+ - JSON keys: `target` (`url`, `scope`, and `source: "env"` when `KXM_SERVER_URL` overrides a binding), `health`, `ready`. An unreachable hub reports `{"error":"hub_unreachable"}` for both probes.
474
+ - Exit 0 when both probes succeed, 1 otherwise.
475
+
476
+ ```bash
477
+ kxm hub view
478
+ ```
479
+
480
+ ```text
481
+ hub health=true ready=true · loopback hub
482
+ ```
483
+
484
+ ```bash
485
+ kxm hub view --json
486
+ ```
487
+
488
+ ```text
489
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"hub view","target":{"url":"http://127.0.0.1:46315","scope":"loopback"},"health":{"ok":true,"agents":0},"ready":{"ok":true,"storage":"sqlite"}}
490
+ ```
491
+
492
+ ### `kxm hub start`
493
+
494
+ ```text
495
+ kxm hub start
496
+ ```
497
+
498
+ Runs the hub in the foreground through `scripts/kxm-hub.mjs`, passing the workspace directories and `KXM_SERVER_URL` to it. The hub listens on `KXM_HOST` (default `127.0.0.1`) and `KXM_PORT` (default `7331`) and refuses a non-loopback bind without `KXM_AUTH_TOKEN`. When `KXM_AUTH_TOKEN` is unset, the admin token is read from, or generated once into, `hub-env.json` under the user state root.
499
+
500
+ No command-specific options.
501
+
502
+ - Writes `.kxm/state/hub.pid`, the SQLite store `.kxm/state/kxm.db` (or `KXM_DATA_PATH`), and `.kxm/logs/kxm-hub.jsonl`.
503
+ - On installs other than a source checkout, prints a cached update notice and refreshes it from the release source.
504
+ - `--dry-run` prints the plan (JSON key `workspace`) and starts nothing. Otherwise there is no JSON result; the exit code is the hub process's exit code.
505
+
506
+ Start a hub on a chosen port:
507
+
508
+ ```bash
509
+ KXM_PORT=46315 KXM_AUTH_TOKEN="$ADMIN_TOKEN" kxm hub start
510
+ ```
511
+
512
+ ```text
513
+ kxm hub listening at http://127.0.0.1:46315; storage=/work/proj/.kxm/state/kxm.db; auth=token
514
+ ```
515
+
516
+ ```bash
517
+ kxm hub start --dry-run --json
518
+ ```
519
+
520
+ ```text
521
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"hub start","dryRun":true,"workspace":"/work/proj/.kxm"}
522
+ ```
523
+
524
+ ### `kxm hub stop`
525
+
526
+ ```text
527
+ kxm hub stop [--wait-ms <ms>]
528
+ ```
529
+
530
+ Requests a generation-matched shutdown of every managed hub and worker in the workspace by writing `hub.stop` and `worker-*.stop` control files next to the live `*.pid` claims, then waits for the claims to clear. An orphaned hub server whose wrapper died is sent `SIGTERM`, then `SIGKILL` if it lingers.
531
+
532
+ | Option | Argument | Default | Description |
533
+ |---|---|---|---|
534
+ | `--wait-ms` | `<ms>` | `5000` | How long to wait for PID files to clear |
535
+
536
+ - `--wait-ms` is clamped to 100 through 30000.
537
+ - Mutates process state. `--dry-run` lists the PID files (JSON key `pidFiles`) and signals nothing.
538
+ - JSON keys: `requested`, `stopped`, `timedOut`, `orphans` (when any), `ignored`.
539
+ - Exit 1 when the state directory has no claims (`no_pid_files`), when no claim belongs to a live process, or when the wait times out.
540
+
541
+ ```bash
542
+ kxm hub stop --dry-run --json
543
+ ```
544
+
545
+ ```text
546
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"stop","dryRun":true,"pidFiles":["hub.pid"]}
547
+ ```
548
+
549
+ ```bash
550
+ kxm hub stop --wait-ms 8000 --json
551
+ ```
552
+
553
+ ```text
554
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"stop","requested":["hub.pid"],"stopped":["hub.pid"],"timedOut":[],"ignored":[]}
555
+ ```
556
+
557
+ ### `kxm hub bind`
558
+
559
+ ```text
560
+ kxm hub bind <url>
561
+ ```
562
+
563
+ Binds this machine to a running hub by writing `hub-binding.json` under the user state root, then probes the hub's health for up to 300 ms. Every hub client uses the binding when `KXM_SERVER_URL` is unset.
564
+
565
+ - Arguments: `<url>`, Hub base URL (http or https).
566
+ - A URL with credentials, a query, a fragment, or a scheme other than http or https fails with `hub_url_invalid` (exit 2).
567
+ - A remote (non-loopback) URL is refused with `hub_bind_unauthenticated` (exit 2, `nextAction: "export_kxm_auth_token"`) unless a credential for the current project resolves from `KXM_AUTH_TOKEN` or the persisted `hub-env.json`. A malformed record fails with `hub_credential_unreadable` (exit 2).
568
+ - Mutates the binding file. `--dry-run` validates and prints the plan without writing.
569
+ - JSON keys: `url`, `scope`, `file`, `health` (`on`, `off`, or `unknown`), `probeMs`.
570
+
571
+ ```bash
572
+ kxm hub bind http://127.0.0.1:7331 --dry-run
573
+ ```
574
+
575
+ ```text
576
+ would bind hub http://127.0.0.1:7331 (loopback)
577
+ ```
578
+
579
+ ```bash
580
+ kxm hub bind https://hub.example.com --dry-run --json
581
+ ```
582
+
583
+ ```text
584
+ {"schema":"kxm.cli-result.v1","ok":false,"command":"hub bind","error":"hub_bind_unauthenticated","url":"https://hub.example.com","scope":"remote","project":"proj","nextAction":"export_kxm_auth_token","hint":"export KXM_AUTH_TOKEN (or point KXM_STATE_HOME at the hub-env record that already holds one), then re-run; the hub itself requires a token beyond loopback (needs a token for project proj)"}
585
+ ```
586
+
587
+ ### `kxm hub unbind`
588
+
589
+ ```text
590
+ kxm hub unbind
591
+ ```
592
+
593
+ Removes this machine's hub binding, including a malformed binding record.
594
+
595
+ No command-specific options.
596
+
597
+ - Mutates the binding file. `--dry-run` prints the file it would remove.
598
+ - JSON keys: `url`, `file`. Exit 1 with `hub_not_bound` when there is no binding.
599
+
600
+ ```bash
601
+ kxm hub unbind --dry-run --json
602
+ ```
603
+
604
+ ```text
605
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"hub unbind","dryRun":true,"file":"/state/hub-binding.json"}
606
+ ```
607
+
608
+ ## `kxm session`
609
+
610
+ Session manifests, process claims, the session-start briefing, and local session tokens. None of these commands launches an agent.
611
+
612
+ ### `kxm session status`
613
+
614
+ ```text
615
+ kxm session status
616
+ ```
617
+
618
+ Lists the `*.pid` claim files and `worker-recovery-*.json` envelopes in the workspace state directory and whether each claimed process is alive. It does not read session manifests.
619
+
620
+ No command-specific options.
621
+
622
+ - Reads only. No hub needed.
623
+ - JSON keys: `claims` (`file`, `role`, `pid`, `startedAt`, `live`), `recoveries` (`file`, `reason`, `agentName`, `project`, `createdAt`, `runId`, `stageId`, `freshSession`).
624
+
625
+ ```bash
626
+ kxm session status
627
+ ```
628
+
629
+ ```text
630
+ 0 session claim(s), 0 recovery envelope(s)
631
+ ```
632
+
633
+ With a hub running:
634
+
635
+ ```bash
636
+ kxm session status --json
637
+ ```
638
+
639
+ ```text
640
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"session status","claims":[{"file":"hub.pid","role":"hub","pid":81619,"startedAt":"2026-09-23T13:53:08.896Z","live":true}],"recoveries":[]}
641
+ ```
642
+
643
+ ### `kxm session brief`
644
+
645
+ ```text
646
+ kxm session brief [--status] [--token]
647
+ ```
648
+
649
+ Prints recent tasks (workflow runs) and plans (journal entries) from the local hub store, with a status line for harness chrome. It probes the hub at `KXM_SERVER_URL` or the bound URL for up to 300 ms; with neither set it reports the hub as off (`evidence: "unconfigured"`). It never starts a hub and never prints message bodies.
650
+
651
+ | Option | Argument | Default | Description |
652
+ |---|---|---|---|
653
+ | `--status` | none | off | Print only the status line |
654
+ | `--token` | none | off | Issue interactive session token with operator policy |
655
+
656
+ - Side effects: on first use it mints a 24-hour operator session token and writes it to `<KXM_USER_CONFIG_DIR>/session.token` (mode 0600), and it caches the brief in `.kxm/state/session-brief.json`. Under `--dry-run` it still probes the hub and prints the brief, but it mints no token, writes neither file, and appends a `dry run: would write <file>` line for each (JSON: `dryRun` and `planned`); with no saved token the output carries no token. `--token --dry-run` plans only the token file.
657
+ - The default text output ends with the session token, and `--token` prints only the token. Treat the output as a credential.
658
+ - JSON (`kxm.session-brief.v1`) keys: `generatedAt`, `staleSeconds`, `source`, `hub` (`state`, `evidence`, `online`, `url`, `scope`), `stats`, `tasks`, `plans`, `statusLine`, `widgetLines`, `sessionToken`.
659
+
660
+ ```bash
661
+ kxm session brief
662
+ ```
663
+
664
+ ```text
665
+ kxm hub:on · idle · dirty
666
+
667
+ No recent tasks or plans.
668
+
669
+ Session token: <token>
670
+ ```
671
+
672
+ ```bash
673
+ kxm session brief --status
674
+ ```
675
+
676
+ ```text
677
+ kxm hub:on · idle
678
+ ```
679
+
680
+ ```bash
681
+ kxm session brief --json
682
+ ```
683
+
684
+ ```text
685
+ {"schema":"kxm.session-brief.v1","generatedAt":"2026-09-23T13:49:55.253Z","staleSeconds":5,"source":"legacy","hub":{"state":"off","evidence":"probed","online":false,"url":"http://127.0.0.1:59998","scope":"loopback"},"stats":{"activeTasks":0,"waitingTasks":0,"planCount":0,"inbox":0,"runTotal":0},"tasks":[],"plans":[],"statusLine":"kxm hub:off · idle",...,"sessionToken":"<token>"}
686
+ ```
687
+
688
+ Before the first token exists:
689
+
690
+ ```bash
691
+ kxm session brief --status --dry-run
692
+ ```
693
+
694
+ ```text
695
+ kxm hub:on · idle · dirty
696
+ dry run: would write ~/.config/kxm/session.token
697
+ dry run: would write /work/proj/.kxm/state/session-brief.json
698
+ ```
699
+
700
+ ### `kxm session token`
701
+
702
+ ```text
703
+ kxm session token [--status] [--clear] [--issue]
704
+ ```
705
+
706
+ Identical to [`kxm auth token`](#kxm-auth-token), including its JSON (`command` is `auth token`).
707
+
708
+ | Option | Argument | Default | Description |
709
+ |---|---|---|---|
710
+ | `--status` | none | off | Check status of the active session token |
711
+ | `--clear` | none | off | Clear persisted disk session token |
712
+ | `--issue` | none | off | Force issuing a fresh session token |
713
+
714
+ ```bash
715
+ kxm session token --status --json
716
+ ```
717
+
718
+ ```text
719
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"auth token","source":"disk","valid":true,"sessionId":"session-280238cf-867f-4852-a09a-0ae88fe5d01c","issuedAt":"2026-09-23T13:49:55.195Z","expiresAt":"2026-09-24T13:49:55.195Z","path":"~/.config/kxm/session.token"}
720
+ ```
721
+
722
+ ### `kxm session start`
723
+
724
+ ```text
725
+ kxm session start (--workflow <id> | --mix <names>) [--id <id>]
726
+ ```
727
+
728
+ Writes a `kxm.session.v1` manifest and creates asset directories. It does not start any process or dispatch a workflow.
729
+
730
+ | Option | Argument | Default | Description |
731
+ |---|---|---|---|
732
+ | `--id` | `<id>` | `session_<12 hex>` | Session id |
733
+ | `--workflow` | `<id>` | none | Workflow definition id |
734
+ | `--mix` | `<names>` | none | Comma-separated agent and gate names |
735
+
736
+ - Exactly one of `--workflow` or `--mix` is required (exit 2 otherwise).
737
+ - Names are resolved against the legacy workspace files `.kxm/config/agents.json` and `.kxm/config/gates.json`. A project created by `kxm init` has neither, so `--mix` fails there with `unknown worker name in session roster` (exit 2) and `--workflow` records an empty roster.
738
+ - Writes `.kxm/assets/sessions/<id>/session.json` with `inputs/` and `outputs/`; `--workflow` also creates `.kxm/assets/workflows/<id>/{inputs,outputs,generated}`. `--dry-run` writes nothing.
739
+ - JSON keys: `session` (`schema`, `id`, `host`, `mode`, `workers`, `createdAt`, `assetDir`, `workflowId`), `created`, `dryRun`.
740
+
741
+ ```bash
742
+ kxm session start --id demo --workflow default --dry-run
743
+ ```
744
+
745
+ ```text
746
+ session demo (workflow)
747
+ ```
748
+
749
+ ```bash
750
+ kxm session start --id review-pass --mix reviewer,test-gate
751
+ ```
752
+
753
+ Not run: needs `.kxm/config/agents.json` and `gates.json` entries with those names.
754
+
755
+ ### `kxm session stop`
756
+
757
+ ```text
758
+ kxm session stop [--wait-ms <ms>]
759
+ ```
760
+
761
+ The same operation as [`kxm hub stop`](#kxm-hub-stop): it stops every managed hub and worker in the workspace and is not scoped to one session.
762
+
763
+ | Option | Argument | Default | Description |
764
+ |---|---|---|---|
765
+ | `--wait-ms` | `<ms>` | `5000` | How long to wait for PID files to clear |
766
+
767
+ ```bash
768
+ kxm session stop --dry-run
769
+ ```
770
+
771
+ ```text
772
+ would signal pid files
773
+ ```
774
+
775
+ ## `kxm dash`
776
+
777
+ ```text
778
+ kxm dash [--screen <name>]
779
+ ```
780
+
781
+ Opens the live, read-only dashboard over the hub's server-sent events and the local hub store. On a terminal it is interactive (`1`–`7` switch tabs, `h` help, `q` quit). Without a terminal it prints one plain snapshot and exits.
782
+
783
+ | Option | Argument | Default | Description |
784
+ |---|---|---|---|
785
+ | `--screen` | `<name>` | `agents` | agents, tasks, workflows, plans, inbox, procs, or spend |
786
+
787
+ - Needs a hub for live data. Reads only.
788
+ - `--json` is refused (exit 2); use `kxm hub view`. An unknown screen exits 2 with `unknown_screen`. `--dry-run` prints `serverUrl`, `transport`, and `screen`.
789
+
790
+ ```bash
791
+ kxm dash --screen workflows
792
+ ```
793
+
794
+ ```text
795
+ kxm dash ● hub ok ● ready live ops 0/5 online updated 13:53:46 UTC · http://127.0.0.1:46315
796
+ 1 Agents 0/5 2 Tasks 0 [3 Workflows 0] 4 Plans 0 5 Inbox 0 6 Procs 1/1 7 Spend 0
797
+ list detail
798
+ Nothing here yet No run selected
799
+ tab Workflows · list · 1–7 tabs · h help · a/r/d/s/c · q quit · live
800
+ ```
801
+
802
+ ```bash
803
+ kxm dash --dry-run --json
804
+ ```
805
+
806
+ ```text
807
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"dash","dryRun":true,"serverUrl":"http://127.0.0.1:59998","transport":"sse"}
808
+ ```
809
+
810
+ ## `kxm studio`
811
+
812
+ Web Studio layout utilities. Neither subcommand needs a hub.
813
+
814
+ ### `kxm studio layout`
815
+
816
+ ```text
817
+ kxm studio layout [workflowPath]
818
+ ```
819
+
820
+ Compiles a workflow definition and prints the Studio layout JSON (`kxm.studio-layout.v1`): a stepper, a DAG of nodes and edges, and role swimlanes. Without a path it reads `.kxm/workflows/default.yaml` under the discovered project root, or a built-in sample workflow when that file does not exist. Swimlane timings are placeholders, and `workflowId` is always reported as `default`.
821
+
822
+ - Arguments: `[workflowPath]`, a workflow YAML file.
823
+ - No command-specific options. Reads only. Text mode prints the same layout as indented JSON.
824
+ - JSON keys: `layout` (`schema`, `workflowId`, `generatedAt`, `stepper`, `dag`, `temporalSwimlanes`).
825
+
826
+ ```bash
827
+ kxm studio layout --json
828
+ ```
829
+
830
+ ```text
831
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"studio layout","layout":{"schema":"kxm.studio-layout.v1","workflowId":"default",...,"stepper":[{"id":"plan","label":"plan","kind":"agent","status":"pending"},{"id":"implement",...},{"id":"verify","label":"verify","kind":"gate","status":"pending"}],...}}
832
+ ```
833
+
834
+ ### `kxm studio serve`
835
+
836
+ ```text
837
+ kxm studio serve [-p <port>] [--host <host>] [--token <token>]
838
+ ```
839
+
840
+ Serves the Web Studio on `http://127.0.0.1:4242` until interrupted. It serves `/`, `/health`, `GET /api/layout`, and `POST /api/mutate`; mutations require the session token. The plan comes from `.kxm/workflows/default.yaml` in the current directory, or the first YAML file in `.kxm/workflows/`.
841
+
842
+ | Option | Argument | Default | Description |
843
+ |---|---|---|---|
844
+ | `-p`, `--port` | `<port>` | `4242` | Port to bind (default: 4242) |
845
+ | `--host` | `<host>` | `127.0.0.1` | Host address to bind |
846
+ | `--token` | `<token>` | `KXM_SESSION_TOKEN`, then the on-disk session token | Session token for mutation authentication |
847
+
848
+ - Honors `--dry-run` (JSON keys `port`, `host`). A live server prints `port`, `host`, `url` and runs until `SIGINT` or `SIGTERM`.
849
+
850
+ ```bash
851
+ kxm studio serve --dry-run
852
+ ```
853
+
854
+ ```text
855
+ would start studio server on http://127.0.0.1:4242
856
+ ```
857
+
858
+ ```bash
859
+ kxm studio serve --port 5000
860
+ ```
861
+
862
+ Not run: starts a long-lived server.
863
+
864
+ ## `kxm harness`
865
+
866
+ ### `kxm harness list`
867
+
868
+ ```text
869
+ kxm harness list
870
+ ```
871
+
872
+ Probes the built-in harness catalog (`pi`, `claude`, `kimi`, `codex`, `deepseek`, `grok`, `agy`) and reports which are installed, which are authenticated, whether KXM can dispatch to them, and which native updaters exist. Detection runs `<harness> --version`; authentication runs the harness's own status command (`claude auth status`, `kimi provider list`, `codex login status`, `grok models`, `agy models`), which may contact that harness's service.
873
+
874
+ No command-specific options.
875
+
876
+ - Reads only. No hub needed. Always exits 0.
877
+ - JSON keys: `defaultHarness`, `harnesses` (each with `id`, `label`, `default`, `mode`, `detected`, `authenticated`, `dispatch` (`status`, `supported`, `reason`), `canUpdate` (`self`, `extensions`, `models`), `issues`).
878
+
879
+ Captured with no harness CLIs on `PATH`:
880
+
881
+ ```bash
882
+ kxm harness list
883
+ ```
884
+
885
+ ```text
886
+ default harness: pi (omit agent harness: to use headless Pi)
887
+ enable/disable = Git YAML (.kxm/agents, .kxm/models) or the harness's own plugin CLI
888
+ governed kxm skills are not auto-updated
889
+ id default detected auth dispatch updates
890
+ pi yes no no no (not_detected) self,extensions,models
891
+ claude no no no no (not_detected) self,extensions
892
+ kimi no no no no (not_detected) self
893
+ codex no no no no (not_detected) self
894
+ deepseek no no no no (not_detected) self
895
+ grok no no no no (not_detected) self
896
+ agy no no no no (not_detected) self
897
+ ```
898
+
899
+ ```bash
900
+ kxm harness list --json
901
+ ```
902
+
903
+ ```text
904
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"harness list","defaultHarness":"pi","harnesses":[{"id":"pi","label":"Pi","default":true,"mode":"headless","detected":false,"authenticated":false,"dispatch":{"status":"no","supported":false,"reason":"not_detected"},"canUpdate":{"self":true,"extensions":true,"models":true},"issues":[]},...]}
905
+ ```
906
+
907
+ ## `kxm auth`
908
+
909
+ ### `kxm auth token`
910
+
911
+ ```text
912
+ kxm auth token [--status] [--clear] [--issue]
913
+ ```
914
+
915
+ Inspects, issues, or clears the local session token that scopes CLI agent-tool commands (`peer`, `workflow checkpoint|record|wait`). A token is a `kxm.session-token.v1` record with an operator tool policy and a 24-hour expiry, stored in `<KXM_USER_CONFIG_DIR>/session.token`.
916
+
917
+ | Option | Argument | Default | Description |
918
+ |---|---|---|---|
919
+ | `--status` | none | off | Check status of the active session token |
920
+ | `--clear` | none | off | Clear persisted disk session token |
921
+ | `--issue` | none | off | Force issuing a fresh session token |
922
+
923
+ - With no flag, prints the valid on-disk token, minting and saving one if none exists. `--issue` always mints and saves a new one. `--clear` deletes the file. `--status` checks `KXM_SESSION_TOKEN` first, then the file.
924
+ - Mutates (except `--status`). Under `--dry-run`, `--issue` (or no flag with no saved token) plans the token file write and prints no token, `--clear` plans the deletion (`cleared` reports whether the file exists), and no flag with a saved token prints that token as usual. No hub needed.
925
+ - JSON keys: `token`; `cleared`; or for `--status`: `source` (`env` or `disk`), `valid`, `sessionId`, `issuedAt`, `expiresAt`, `path`. Dry runs add `dryRun` and `planned`.
926
+ - `--status` exits 1 with `no_token` when neither source has a token, and 1 when the environment token is invalid or expired.
927
+ - A malformed or expired `KXM_SESSION_TOKEN` or token file makes agent-tool commands fail with `session_token_invalid`; a policy that excludes a tool fails with `tool_policy_denied`.
928
+
929
+ ```bash
930
+ kxm auth token --status
931
+ ```
932
+
933
+ ```text
934
+ No active session token found in env or disk
935
+ ```
936
+
937
+ ```bash
938
+ kxm auth token --issue --dry-run --json
939
+ ```
940
+
941
+ ```text
942
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"auth token","dryRun":true,"planned":[{"action":"write","target":"~/.config/kxm/session.token"}]}
943
+ ```
944
+
945
+ ```bash
946
+ kxm auth token --issue --json
947
+ ```
948
+
949
+ Not run: stores a credential.
950
+
951
+ ## `kxm update`
952
+
953
+ ```text
954
+ kxm update [harness] [--check | --kxm] [--self | --extensions | --models]
955
+ ```
956
+
957
+ Checks for or applies a KXM operator package update, and runs the native updaters of detected harnesses.
958
+
959
+ | Option | Argument | Default | Description |
960
+ |---|---|---|---|
961
+ | `--check` | none | off | Check for a kxm package update without applying |
962
+ | `--kxm` | none | off | Apply the kxm operator package update (GitHub release tarball or npm) |
963
+ | `--self` | none | off | Update only the harness CLI |
964
+ | `--extensions` | none | off | Update only extensions/plugins (Pi packages, Claude kxm) |
965
+ | `--models` | none | off | Refresh model catalogs where the harness supports it |
966
+
967
+ - Arguments: `[harness]`, Harness id (default: every detected harness).
968
+ - `--check` cannot be combined with any other update flag or a harness (exit 2, `scope_conflict`), and at most one of `--self`, `--extensions`, `--models` is allowed (exit 2).
969
+ - From a source checkout, `--check` reports the running version without network access, and `--kxm` is refused (exit 2, `install_kind_source`) with an instruction to `git pull`. Only npm-global installs can apply `--kxm`; other install kinds exit 2 with `install_kind_<kind>`. A GitHub release must publish a sha256 digest for `kxm-<version>.tgz` or the install fails closed (`release_digest_missing`, `release_digest_mismatch`).
970
+ - Settings come only from `update.yaml` under the user state root (`auto: true` enables auto-apply); a project `.kxm/update.yaml` is ignored with a warning.
971
+ - Without `--check` or a lone `--kxm`, KXM probes harnesses (as `harness list` does) and runs each updater for the selected scope. An unknown harness id exits 2 (`unknown_harness` step); a failed step exits 1.
972
+ - Honors `--dry-run`: steps are planned, not run, and the cached update notice is not refreshed.
973
+ - JSON keys: `--check` gives `current`, `available`, `auto`, `source`, `latest`, `message`, `installKind`, `root`; otherwise `dryRun`, `scope`, `notice`, `kxm` (when applying), `steps` (`harness`, `scope`, `command`, `args`, `outcome`, `detail`), `installKind`, `root`.
974
+
975
+ ```bash
976
+ kxm update --check --dry-run --json
977
+ ```
978
+
979
+ ```text
980
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"update check","current":"0.7.1","available":false,"auto":false,"source":"github","installKind":"source","root":"/work/kxm","message":"kxm 0.7.1 (running from source at /work/kxm)"}
981
+ ```
982
+
983
+ ```bash
984
+ kxm update --dry-run
985
+ ```
986
+
987
+ ```text
988
+ nothing to update
989
+ ```
990
+
991
+ ```bash
992
+ kxm update --kxm --dry-run --json
993
+ ```
994
+
995
+ ```text
996
+ {"schema":"kxm.cli-result.v1","ok":false,"command":"update","error":"install_kind_source","installKind":"source","root":"/work/kxm","instruction":"kxm is running from source at /work/kxm; update it with git pull there, not kxm update --kxm"}
997
+ ```
998
+
999
+ ## `kxm models`
1000
+
1001
+ ```text
1002
+ kxm models
1003
+ ```
1004
+
1005
+ Opens an interactive screen over `.kxm/models/inventory.yaml` that shows each model's route state and role bindings. Keys: `a` admit, `d` disable, `r` add a role binding, `x` remove a role binding, `q` quit. Changes are written to `.kxm/routes.yaml` and `.kxm/roles/<role>.yaml` in `KXM_WORKDIR` or the current directory.
1006
+
1007
+ - Needs an interactive terminal. With `--json` or without a TTY it exits 2 with `interactive_tty_required`.
1008
+
1009
+ ```bash
1010
+ kxm models --json
1011
+ ```
1012
+
1013
+ ```text
1014
+ {"schema":"kxm.cli-result.v1","ok":false,"command":"models","error":"interactive_tty_required"}
1015
+ ```
1016
+
1017
+ ### `kxm models inventory-refresh`
1018
+
1019
+ ```text
1020
+ kxm models inventory-refresh
1021
+ kxm models refresh
1022
+ ```
1023
+
1024
+ Refreshes the YAML model inventory from the OpenRouter (`https://openrouter.ai/api/v1/models`) and Nous (`https://inference-api.nousresearch.com/v1/models`) catalogs, recording standard and discount prices. `OPENROUTER_API_KEY` and `NOUS_API_KEY` are sent when set; `KXM_OPENROUTER_MODELS_URL` and `KXM_NOUS_MODELS_URL` override the endpoints. `refresh` is an alias.
1025
+
1026
+ No command-specific options.
1027
+
1028
+ - Writes `.kxm/models/inventory.yaml`. Calls external services. Honors `--dry-run`.
1029
+ - JSON keys: `output`, `models`, `sources` (per source: `url`, `ok`, `error`). Exit 1 when any source failed.
1030
+
1031
+ ```bash
1032
+ kxm models refresh --dry-run
1033
+ ```
1034
+
1035
+ ```text
1036
+ would refresh .kxm/models/inventory.yaml
1037
+ ```
1038
+
1039
+ ## `kxm routes`
1040
+
1041
+ Admits or disables model routes recorded in `.kxm/routes.yaml` (`kxm.routes.v2`) in `KXM_WORKDIR` or the current directory. A retired `.kxm/producers.yaml` makes these commands fail. None needs a hub.
1042
+
1043
+ ### `kxm routes list`
1044
+
1045
+ ```text
1046
+ kxm routes list
1047
+ ```
1048
+
1049
+ Lists admitted and disabled routes.
1050
+
1051
+ No command-specific options.
1052
+
1053
+ - Reads only. JSON keys: `policy` (`schema`, `updatedAt`, `admitted`, `disabled`, `roles`).
1054
+
1055
+ ```bash
1056
+ kxm routes list
1057
+ ```
1058
+
1059
+ ```text
1060
+ no route decisions
1061
+ ```
1062
+
1063
+ ```bash
1064
+ kxm routes list --json
1065
+ ```
1066
+
1067
+ ```text
1068
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"routes list","policy":{"schema":"kxm.routes.v2","updatedAt":"2026-09-23T13:50:08.663Z","admitted":[],"disabled":[],"roles":{}}}
1069
+ ```
1070
+
1071
+ ### `kxm routes count`
1072
+
1073
+ ```text
1074
+ kxm routes count
1075
+ ```
1076
+
1077
+ Counts admitted and disabled routes.
1078
+
1079
+ No command-specific options.
1080
+
1081
+ - Reads only. JSON keys: `admitted`, `disabled`, `summary`.
1082
+
1083
+ ```bash
1084
+ kxm routes count
1085
+ ```
1086
+
1087
+ ```text
1088
+ 0 admitted / 0 disabled
1089
+ ```
1090
+
1091
+ ### `kxm routes admit`
1092
+
1093
+ ```text
1094
+ kxm routes admit [--model <id>]
1095
+ ```
1096
+
1097
+ Admits a model route from the inventory.
1098
+
1099
+ | Option | Argument | Default | Description |
1100
+ |---|---|---|---|
1101
+ | `--model` | `<id>` | interactive picker | Exact model id; omit to choose interactively |
1102
+
1103
+ - The model must exist in `.kxm/models/inventory.yaml` (case-insensitive match). Without `--model`, a terminal shows a numbered picker of up to 100 models. With no inventory, no TTY, or `--json`, it exits 2 with `model_selection_required`.
1104
+ - Writes `.kxm/routes.yaml`. Honors `--dry-run`.
1105
+ - JSON keys: `model`, `policy` (or `dryRun`).
1106
+
1107
+ ```bash
1108
+ kxm routes admit --model x-ai/grok-4.6 --dry-run
1109
+ ```
1110
+
1111
+ Not run with an inventory; without one it exits 2 with `model_selection_required`.
1112
+
1113
+ ### `kxm routes disable`
1114
+
1115
+ ```text
1116
+ kxm routes disable [--model <id>]
1117
+ ```
1118
+
1119
+ Disables a model route from the inventory. Same selection rules, output, and errors as `routes admit`.
1120
+
1121
+ | Option | Argument | Default | Description |
1122
+ |---|---|---|---|
1123
+ | `--model` | `<id>` | interactive picker | Exact model id; omit to choose interactively |
1124
+
1125
+ ```bash
1126
+ kxm routes disable --dry-run --json
1127
+ ```
1128
+
1129
+ ```text
1130
+ {"schema":"kxm.cli-result.v1","ok":false,"command":"routes disabled","error":"model_selection_required"}
1131
+ ```
1132
+
1133
+ ## `kxm role`
1134
+
1135
+ Manages role definitions (`kxm.role.v1`) and role-seat host bindings (`kxm.role-hosts.v1`). Local scope is `.kxm/roles/` and `.kxm/role-hosts.yaml` in the current directory; global scope is `<KXM_USER_CONFIG_DIR>/roles/` and `<KXM_USER_CONFIG_DIR>/role-hosts.yaml`. A local role with the same ID overrides a global one. `kxm role` with no subcommand runs `role list`. For `--pick` without a value on a non-interactive shell, set `KXM_PICK_SELECT` to an index or ID. Errors from this group are plain text on stderr, even with `--json`.
1136
+
1137
+ ### `kxm role list`
1138
+
1139
+ ```text
1140
+ kxm role list [--scope all|global|local]
1141
+ ```
1142
+
1143
+ Lists configured roles across scopes.
1144
+
1145
+ | Option | Argument | Default | Description |
1146
+ |---|---|---|---|
1147
+ | `--scope` | `<scope>` | `all` | Filter by scope: all, global, or local |
1148
+
1149
+ - Reads only. JSON keys: `roles`.
1150
+
1151
+ ```bash
1152
+ kxm role list
1153
+ ```
1154
+
1155
+ ```text
1156
+ No roles configured.
1157
+ ```
1158
+
1159
+ ### `kxm role get`
1160
+
1161
+ ```text
1162
+ kxm role get <roleId> [--scope all|global|local]
1163
+ ```
1164
+
1165
+ Prints a role definition as YAML.
1166
+
1167
+ | Option | Argument | Default | Description |
1168
+ |---|---|---|---|
1169
+ | `--scope` | `<scope>` | `all` | Filter by scope: all, global, or local |
1170
+
1171
+ - Reads only. JSON keys: `roleId`, `scope`, `filePath`, `role`. A missing role prints `kxm: role '<id>' not found` and exits 1.
1172
+
1173
+ ```bash
1174
+ kxm role get writer --scope local
1175
+ ```
1176
+
1177
+ Not run with a configured role; in a fresh project it exits 1 with `kxm: role 'writer' not found`.
1178
+
1179
+ ### `kxm role add`
1180
+
1181
+ ```text
1182
+ kxm role add [roleId] [--file <path>] [--description <text>] [--skills <skills>] [--harness <harness>] [--model <model>] [--scope global|local] [--overwrite] [--pick [selection]]
1183
+ ```
1184
+
1185
+ Adds a role definition. Without a role ID, or with `--pick`, you choose from the built-in templates (`writer`, `planner`, `critic-arch`, `critic-cli`, `verifier`) and, for local scope, existing global roles. With `--file`, the YAML file is used and its `id` is replaced by the role ID.
1186
+
1187
+ | Option | Argument | Default | Description |
1188
+ |---|---|---|---|
1189
+ | `--file` | `<path>` | none | Path to YAML role definition file |
1190
+ | `--description` | `<text>` | `Role <id>` | Role description |
1191
+ | `--skills` | `<skills>` | none | Comma-separated skills list |
1192
+ | `--harness` | `<harness>` | `pi` when `--model` is set | Primary harness name (e.g. grok, claude, agy, pi) |
1193
+ | `--model` | `<model>` | none | Primary model identifier (e.g. grok-4.6, fable, gemini-3.8-flash-high) |
1194
+ | `--scope` | `<scope>` | `local` | Configuration scope: global or local (default: local) |
1195
+ | `--overwrite` | none | off | Overwrite existing role definition if present |
1196
+ | `--pick` | `[selection]` | none | Pick from available role templates (index or id) |
1197
+
1198
+ - Writes `<scope dir>/roles/<id>.yaml`. `--dry-run` plans the write and writes nothing.
1199
+ - JSON keys: `roleId`, `id`, `filePath`, `scope`.
1200
+
1201
+ ```bash
1202
+ kxm role add demo-role --description "Demo role" --dry-run --json
1203
+ ```
1204
+
1205
+ ```text
1206
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"role add","roleId":"demo-role","id":"demo-role","filePath":"/work/proj/.kxm/roles/demo-role.yaml","scope":"local","dryRun":true,"planned":[{"action":"write","target":"/work/proj/.kxm/roles/demo-role.yaml"}]}
1207
+ ```
1208
+
1209
+ Add a reviewer role with a Claude model, and copy a built-in template into global scope (Not run):
1210
+
1211
+ ```bash
1212
+ kxm role add reviewer --description "Independent reviewer" --harness claude --model fable --skills kxm
1213
+ ```
1214
+
1215
+ ```bash
1216
+ kxm role add --pick critic-arch --scope global
1217
+ ```
1218
+
1219
+ ### `kxm role remove`
1220
+
1221
+ ```text
1222
+ kxm role remove [roleId] [--scope global|local] [--pick [selection]]
1223
+ ```
1224
+
1225
+ Removes a role definition file.
1226
+
1227
+ | Option | Argument | Default | Description |
1228
+ |---|---|---|---|
1229
+ | `--scope` | `<scope>` | `local` | Configuration scope: global or local (default: local) |
1230
+ | `--pick` | `[selection]` | none | Pick a role to remove (index or id) |
1231
+
1232
+ - Deletes a file. `--dry-run` plans the deletion, deletes nothing, and reports `removed: false`.
1233
+ - JSON keys: `roleId`, `id`, `removed`, `filePath`, `scope`.
1234
+
1235
+ ```bash
1236
+ kxm role remove demo-role --dry-run --json
1237
+ ```
1238
+
1239
+ ```text
1240
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"role remove","roleId":"demo-role","id":"demo-role","removed":false,"filePath":"/work/proj/.kxm/roles/demo-role.yaml","scope":"local","dryRun":true,"planned":[{"action":"delete","target":"/work/proj/.kxm/roles/demo-role.yaml"}]}
1241
+ ```
1242
+
1243
+ ### `kxm role modify`
1244
+
1245
+ ```text
1246
+ kxm role modify [roleId] [--description <text>] [--add-skill <skill>] [--remove-skill <skill>] [--add-model <harness:model>] [--remove-model <model>] [--scope global|local] [--pick [selection]]
1247
+ ```
1248
+
1249
+ Updates an existing role's description, skills, or model roster and rewrites its file.
1250
+
1251
+ | Option | Argument | Default | Description |
1252
+ |---|---|---|---|
1253
+ | `--description` | `<text>` | unchanged | Updated description |
1254
+ | `--add-skill` | `<skill>` | none | Skill to add |
1255
+ | `--remove-skill` | `<skill>` | none | Skill to remove |
1256
+ | `--add-model` | `<harness:model>` | none | Model to add to roster |
1257
+ | `--remove-model` | `<model>` | none | Model to remove from roster |
1258
+ | `--scope` | `<scope>` | first match | Configuration scope: global or local |
1259
+ | `--pick` | `[selection]` | none | Pick a role to modify (index or id) |
1260
+
1261
+ - `--add-model` without a colon uses harness `pi`. `--dry-run` returns the modified role and plans the write without making it.
1262
+ - JSON keys: `roleId`, `id`, `role`, `filePath`, `scope`.
1263
+
1264
+ ```bash
1265
+ kxm role modify demo-role --add-skill kxm --dry-run --json
1266
+ ```
1267
+
1268
+ ```text
1269
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"role modify","roleId":"demo-role","id":"demo-role","role":{"schema":"kxm.role.v1","id":"demo-role","description":"Demo role","skills":["kxm"],"roster":[]},"filePath":"/work/proj/.kxm/roles/demo-role.yaml","scope":"local","dryRun":true,"planned":[{"action":"write","target":"/work/proj/.kxm/roles/demo-role.yaml"}]}
1270
+ ```
1271
+
1272
+ Add a Claude model to a role's roster (Not run):
1273
+
1274
+ ```bash
1275
+ kxm role modify reviewer --add-model claude:fable --add-skill kxm-peer
1276
+ ```
1277
+
1278
+ ### `kxm role hosts`
1279
+
1280
+ ```text
1281
+ kxm role hosts [--scope all|global|local]
1282
+ ```
1283
+
1284
+ Lists role seats (`critic-arch`, `critic-cli`, `planner`, `verifier`, `writer`, plus any configured seat) and the host, model, and effort each resolves to, with the source of the decision (`override`, `role-hosts`, `seat-default`, `role-roster`, or `fallback`).
1285
+
1286
+ | Option | Argument | Default | Description |
1287
+ |---|---|---|---|
1288
+ | `--scope` | `<scope>` | `all` | Filter by scope: all, global, or local |
1289
+
1290
+ - Reads only. JSON keys: `scope`, `filePath`, `seats` (`seatId`, `host`, `model`, `provider`, `effort`, `source`, `configuredHost`, `configuredModel`), `hostProviders`.
1291
+
1292
+ ```bash
1293
+ kxm role hosts
1294
+ ```
1295
+
1296
+ ```text
1297
+ ROLE SEATS (default):
1298
+ critic-arch -> host: pi [anthropic/claude-fable-5.1] (via seat-default)
1299
+ critic-cli -> host: pi [openai/gpt-5.6-sol] (via seat-default)
1300
+ planner -> host: pi [anthropic/claude-fable-5.1] (via seat-default)
1301
+ verifier -> host: pi [evaluator] (via seat-default)
1302
+ writer -> host: grok [x-ai/grok-4.6] (via seat-default)
1303
+ ```
1304
+
1305
+ ### `kxm role set-host`
1306
+
1307
+ ```text
1308
+ kxm role set-host <seatId> <host> [--model <model>] [--effort low|medium|high|xhigh] [--scope global|local]
1309
+ ```
1310
+
1311
+ Binds a role seat to a host in `role-hosts.yaml`.
1312
+
1313
+ | Option | Argument | Default | Description |
1314
+ |---|---|---|---|
1315
+ | `--model` | `<model>` | none | Model identifier for this seat |
1316
+ | `--effort` | `<effort>` | none | Effort level: low, medium, high, xhigh |
1317
+ | `--scope` | `<scope>` | `local` | Configuration scope: global or local (default: local) |
1318
+
1319
+ - Writes `.kxm/role-hosts.yaml` (or the global file). `--dry-run` plans the write and writes nothing.
1320
+ - JSON keys: `seatId`, `host`, `binding`, `filePath`, `scope`.
1321
+
1322
+ ```bash
1323
+ kxm role set-host writer claude --model fable --effort high --dry-run --json
1324
+ ```
1325
+
1326
+ ```text
1327
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"role set-host","seatId":"writer","host":"claude","binding":{"host":"claude","model":"fable","effort":"high"},"filePath":"/work/proj/.kxm/role-hosts.yaml","scope":"local","dryRun":true,"planned":[{"action":"write","target":"/work/proj/.kxm/role-hosts.yaml"}]}
1328
+ ```
1329
+
1330
+ ### `kxm role resume`
1331
+
1332
+ ```text
1333
+ kxm role resume <runId> [ruling]
1334
+ ```
1335
+
1336
+ Resumes an audit-escalated run with an operator directive. The default ruling is `operator_ruling: waived and resumed`.
1337
+
1338
+ - Arguments: `<runId>`; `[ruling]`, free text recorded with the decision.
1339
+ - For a KXM run ID (`run_` followed by 32 hex digits) inside a project, posts an `audit_escalation` signal with action `unblock` to the Runtime, starting the supervisor if needed. `--dry-run` plans the request without starting the supervisor. JSON keys: `runId`, `ruling`, `unblocked`.
1340
+ - For any other ID, updates the hub store at `.kxm/state/kxm.db` in the current directory directly (ignoring `--workspace` and `KXM_DATA_PATH`) and adds a `decision` journal entry. `--dry-run` reads the store read-only, reports the stage it would resume and the resulting `status`, and plans the write. JSON keys: `runId`, `stageId`, `ruling`, `status`.
1341
+ - Errors: `resume_failed` (exit 1), or a plain `not found` line (exit 1).
1342
+
1343
+ ```bash
1344
+ kxm role resume run_0123456789abcdef0123456789abcdef "waive the audit" --dry-run --json
1345
+ ```
1346
+
1347
+ ```text
1348
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"role resume","runId":"run_0123456789abcdef0123456789abcdef","ruling":"waive the audit","dryRun":true,"planned":[{"action":"request","target":"POST kxm-runtime /v1/runs/run_0123456789abcdef0123456789abcdef/signal (audit_escalation unblock)"}]}
1349
+ ```
1350
+
1351
+ A hub workflow run waiting on an audit escalation:
1352
+
1353
+ ```bash
1354
+ kxm role resume wf_dry_run "carry on" --dry-run
1355
+ ```
1356
+
1357
+ ```text
1358
+ dry run: resume workflow run wf_dry_run (stage: review)
1359
+ would write /work/proj/.kxm/state/kxm.db (workflow_runs wf_dry_run, one workflow_journal decision)
1360
+ ```
1361
+
1362
+ ## `kxm run`
1363
+
1364
+ ```text
1365
+ kxm run <workflow> [prompt...]
1366
+ ```
1367
+
1368
+ Create a KXM run (offline-first; `kxm runs drive <runId> --simulated` executes it model-free). The run is immutable and pins the project's `homeRuntimeId`, config revision, and executor and tool policy revisions, and it stores only a hash of the prompt. The Runtime supervisor is started first if it is not running. No steps execute until the run is driven (see [`kxm runs drive`](#kxm-runs-drive)); the text output's second line prints the command that drives the new run model-free and the one that cancels it.
1369
+
1370
+ - Arguments: `<workflow>`, Workflow id to run (a file under `.kxm/workflows/`); `[prompt...]`, Run prompt (hashed, never stored raw).
1371
+ - No command-specific options. Refuses `--workspace` (exit 2).
1372
+ - Needs a KXM project. Starts and uses the Runtime; no hub needed. Honors `--dry-run`, which validates the project and prints the plan without starting the supervisor.
1373
+ - JSON keys: `phase`, `idempotent`, `run` (`runId`, `homeRuntimeId`, `status`, `configRevision`), `supervisor` (`runtimeId`, `port`, `started`). Dry run: `projectRoot`, `workflowId`, `configRevision`. The JSON result does not carry the drive command.
1374
+ - Errors: `workflow_required` (exit 2), `project_required`, `run_workflow_unknown`, `run_failed` with `issues` (any invalid file in the project fails the load, for example `gate_outcome_impossible`), `run_io_failed` (exit 1).
1375
+ - The `default` workflow that `kxm init` writes sets `limits.maxAgentTimeMs`, which the Runtime does not enforce yet, so a run of it is created but `kxm runs drive` refuses it with `run_handoff_required`. A workflow written by `kxm workflow add <id> --template implement-and-verify` omits that limit.
1376
+
1377
+ ```bash
1378
+ kxm run default "Fix the flaky login test" --dry-run
1379
+ ```
1380
+
1381
+ ```text
1382
+ run plan: workflow default at sha256:80457232cbfc… (no run created)
1383
+ ```
1384
+
1385
+ ```bash
1386
+ kxm run default "Fix the flaky login test" --dry-run --json
1387
+ ```
1388
+
1389
+ ```text
1390
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"run","dryRun":true,"projectRoot":"/work/proj","workflowId":"default","configRevision":"sha256:80457232cbfc0d1a88c53a2b693061083c83df6e95409e3ae7f75ccfc9f93cf4"}
1391
+ ```
1392
+
1393
+ Create a run (this starts the Runtime supervisor; stop it afterwards with `kxm runtime stop`):
1394
+
1395
+ ```bash
1396
+ kxm run default "Fix the flaky login test"
1397
+ ```
1398
+
1399
+ ```text
1400
+ run created: run_a80e84c98f514299b82f0157f4537ea3 (home rtm_1a42e069…, config sha256:b45f8f51a506…)
1401
+ drive it model-free: kxm runs drive run_a80e84c98f514299b82f0157f4537ea3 --simulated --wait (or cancel: kxm runs cancel run_a80e84c98f514299b82f0157f4537ea3)
1402
+ ```
1403
+
1404
+ ## `kxm runs`
1405
+
1406
+ Reads and drives runs through the Runtime supervisor. Every subcommand needs a KXM project, and every subcommand except a dry run starts the supervisor if it is not running. Under `--dry-run`, `status`, `list`, and `receipt` read from a supervisor that is already running and otherwise exit 2 with `dry_run_unsupported`; `drive` and `cancel` print a plan without contacting it. The run ID is the `runId` printed by `kxm run`. Errors carry `issues` from the Runtime, for example `run_unknown`.
1407
+
1408
+ ### `kxm runs status`
1409
+
1410
+ ```text
1411
+ kxm runs status <runId>
1412
+ ```
1413
+
1414
+ Show the projected status of a run, including durable drive receipt state (open / receipt verified / unsettled / orphaned).
1415
+
1416
+ - Arguments: `<runId>`, Run id. No command-specific options. Reads run state, but starts the supervisor if needed (not under `--dry-run`).
1417
+ - Text: `run <id>: <status> (workflow <id>, updated <time>)`, plus a drive line such as `drive <id>: open`, `completed (receipt verified)`, `unsettled <reason>`, `handoff`, `cancelled (<reason>)`, or `no receipt (orphaned)`.
1418
+ - JSON keys: `run` (`runId`, `status`, `workflowId`, `configRevision`, `updatedAt`, ...), `drive` (`driveId`, `mode`, `openedAt`, `receipt`, `verified`, `divergence`).
1419
+ - Errors: `project_required`, `run_status_failed`, `run_status_io_failed` (exit 1).
1420
+
1421
+ ```bash
1422
+ kxm runs status run_a80e84c98f514299b82f0157f4537ea3
1423
+ ```
1424
+
1425
+ ```text
1426
+ run run_a80e84c98f514299b82f0157f4537ea3: preparing (workflow default, updated 2026-09-23T17:47:29.682Z)
1427
+ ```
1428
+
1429
+ With no supervisor running:
1430
+
1431
+ ```bash
1432
+ kxm runs status run_0123456789abcdef0123456789abcdef --dry-run
1433
+ ```
1434
+
1435
+ ```text
1436
+ kxm runs status --dry-run refused: the Runtime supervisor is not running and --dry-run will not start it
1437
+ ```
1438
+
1439
+ ### `kxm runs drive`
1440
+
1441
+ ```text
1442
+ kxm runs drive <runId> [--simulated] [--wait] [--timeout-ms <n>]
1443
+ ```
1444
+
1445
+ Opens a drive of the run. With `--simulated`, a model-free producer reports every agent step as passed. Without `--simulated` the drive runs in live mode: each agent step invokes its harness through a one-shot producer, and the agent's model must be an admitted route (otherwise `producer_route_not_admitted`).
1446
+
1447
+ | Option | Argument | Default | Description |
1448
+ |---|---|---|---|
1449
+ | `--simulated` | none | off | Use the model-free simulation producer |
1450
+ | `--wait` | none | off | Wait until a drive receipt is recorded; exits 0 only for a VERIFIED COMPLETED settlement |
1451
+ | `--timeout-ms` | `<n>` | `60000` | Wait timeout in milliseconds (default 60000, max 600000) |
1452
+
1453
+ - Arguments: `<runId>`, Run id.
1454
+ - `--timeout-ms` applies only with `--wait` and must be an integer from 1 to 600000 (`run_drive_timeout_invalid`, exit 1).
1455
+ - Mutates run state. Honors `--dry-run`.
1456
+ - JSON keys without `--wait`: `runId`, `driveId`, `poll`, `mode`, `status` (`accepted`). With `--wait`: `receipt`, `verified`; a timeout prints `error: "timeout"`.
1457
+ - Exit 0 when accepted, or with `--wait` only for a verified completed settlement; otherwise 1. Runtime refusals include `run_handoff_required` and `run_busy`, printed as `run drive failed: <request path>: <code>: <message>` (JSON: `error: "run_drive_failed"` with the code in `issues`). A `run_handoff_required` message ends with `(handoff reason <reason>; field <field>; detail <detail>)`, each part capped at 200 characters, so the refusal names what to change.
1458
+
1459
+ ```bash
1460
+ kxm runs drive run_0123456789abcdef0123456789abcdef --simulated --dry-run
1461
+ ```
1462
+
1463
+ ```text
1464
+ drive plan: run run_0123456789abcdef0123456789abcdef in simulated mode (no events written)
1465
+ ```
1466
+
1467
+ The `default` workflow from `kxm init` is handed off (see [`kxm run`](#kxm-run)):
1468
+
1469
+ ```bash
1470
+ kxm runs drive run_a80e84c98f514299b82f0157f4537ea3 --simulated
1471
+ ```
1472
+
1473
+ ```text
1474
+ run drive failed: /v1/runs/run_a80e84c98f514299b82f0157f4537ea3/drive?projectRoot=%2Fwork%2Fproj: run_handoff_required: runtime request failed with HTTP 409 (handoff reason limit_unsupported; field limits.maxAgentTimeMs; detail agent-time budget enforcement is not available in this slice)
1475
+ ```
1476
+
1477
+ ```bash
1478
+ kxm runs drive run_0123456789abcdef0123456789abcdef --simulated --wait --timeout-ms 120000
1479
+ ```
1480
+
1481
+ Not run: starts the Runtime and writes run events.
1482
+
1483
+ ### `kxm runs receipt`
1484
+
1485
+ ```text
1486
+ kxm runs receipt <runId> [--all]
1487
+ ```
1488
+
1489
+ Print the newest drive receipt for a run.
1490
+
1491
+ | Option | Argument | Default | Description |
1492
+ |---|---|---|---|
1493
+ | `--all` | none | off | Print the capped receipt list for the run |
1494
+
1495
+ - Arguments: `<runId>`, Run id. Text mode prints the newest receipt's settlement as JSON; `--all` prints the list. Starts the supervisor if needed (not under `--dry-run`).
1496
+ - JSON keys: `receipt`, or `receipts` with `--all`. Exit 1 with `no_receipts` when the run was never driven.
1497
+
1498
+ ```bash
1499
+ kxm runs receipt run_0123456789abcdef0123456789abcdef --all --json
1500
+ ```
1501
+
1502
+ Not run: starts the Runtime supervisor.
1503
+
1504
+ ### `kxm runs cancel`
1505
+
1506
+ ```text
1507
+ kxm runs cancel <runId>
1508
+ ```
1509
+
1510
+ Durably request cancellation of a run: records `run.cancel_requested` then `run.status_changed`. Cancelling a terminal run is an idempotent no-op.
1511
+
1512
+ - Arguments: `<runId>`, Run id. No command-specific options.
1513
+ - Mutates run state. Honors `--dry-run`.
1514
+ - JSON keys: `idempotent`, `run` (`runId`, `status`).
1515
+
1516
+ ```bash
1517
+ kxm runs cancel run_0123456789abcdef0123456789abcdef --dry-run
1518
+ ```
1519
+
1520
+ ```text
1521
+ cancel plan: run run_0123456789abcdef0123456789abcdef (no events written)
1522
+ ```
1523
+
1524
+ ### `kxm runs list`
1525
+
1526
+ ```text
1527
+ kxm runs list
1528
+ ```
1529
+
1530
+ List recent runs for the current project. A run whose event log could not be folded is marked `[state unverified: <reason>]`.
1531
+
1532
+ No command-specific options.
1533
+
1534
+ - JSON keys: `runs` (`runId`, `status`, `workflowId`, `createdAt`, `projectionError`). Text prints `no runs` when empty. Starts the supervisor if needed (not under `--dry-run`).
1535
+
1536
+ ```bash
1537
+ kxm runs list
1538
+ ```
1539
+
1540
+ Not run: starts the Runtime supervisor.
1541
+
1542
+ ## `kxm runtime`
1543
+
1544
+ Manages the detached KXM Runtime supervisor: a token-authenticated API on a random `127.0.0.1` port with a stable logical runtime identity, state under `<state root>/runtime/`, and an outbox that syncs run state to the hub.
1545
+
1546
+ ### `kxm runtime start`
1547
+
1548
+ ```text
1549
+ kxm runtime start
1550
+ ```
1551
+
1552
+ Start the Runtime supervisor if not running.
1553
+
1554
+ No command-specific options.
1555
+
1556
+ - Starts a detached process. Honors `--dry-run`.
1557
+ - JSON keys: `runtimeId`, `port`, `started`.
1558
+
1559
+ ```bash
1560
+ kxm runtime start --dry-run
1561
+ ```
1562
+
1563
+ ```text
1564
+ runtime supervisor would auto-start
1565
+ ```
1566
+
1567
+ ### `kxm runtime status`
1568
+
1569
+ ```text
1570
+ kxm runtime status
1571
+ ```
1572
+
1573
+ Show Runtime supervisor liveness, judged by its heartbeat as well as its PID. When it is running, also shows each project's outbox sync state (`pending`, `acked`, `refused` counts and the refusal codes).
1574
+
1575
+ No command-specific options.
1576
+
1577
+ - Reads only. Never starts the supervisor.
1578
+ - JSON keys: `running`, `runtimeId`, `pid`, `port`, `state`, `heartbeatAt`, `startedAt`, `sync`.
1579
+ - Exit 0 when running, 1 when not (the JSON still says `ok: true`).
1580
+
1581
+ ```bash
1582
+ kxm runtime status
1583
+ ```
1584
+
1585
+ ```text
1586
+ runtime supervisor is not running
1587
+ ```
1588
+
1589
+ ```bash
1590
+ kxm runtime status --json
1591
+ ```
1592
+
1593
+ ```text
1594
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"runtime status","running":false}
1595
+ ```
1596
+
1597
+ ### `kxm runtime sync-retry`
1598
+
1599
+ ```text
1600
+ kxm runtime sync-retry
1601
+ ```
1602
+
1603
+ Re-queue outbox rows the hub durably refused, after the hub-side state is corrected.
1604
+
1605
+ No command-specific options.
1606
+
1607
+ - Needs a running supervisor (it never starts one) and a KXM project. Honors `--dry-run`, but still checks the supervisor first.
1608
+ - JSON keys: `retried`, `projectId`, and the Runtime's result fields.
1609
+ - Errors: `runtime_not_running`, `project_required` (exit 1).
1610
+
1611
+ ```bash
1612
+ kxm runtime sync-retry --dry-run --json
1613
+ ```
1614
+
1615
+ ```text
1616
+ {"schema":"kxm.cli-result.v1","ok":false,"command":"runtime sync-retry","error":"runtime_not_running"}
1617
+ ```
1618
+
1619
+ ### `kxm runtime stop`
1620
+
1621
+ ```text
1622
+ kxm runtime stop
1623
+ ```
1624
+
1625
+ Gracefully stop the Runtime supervisor.
1626
+
1627
+ No command-specific options.
1628
+
1629
+ - Honors `--dry-run`. When nothing is running it prints `stopped: false` and exits 0.
1630
+ - JSON keys: `stopped`.
1631
+
1632
+ With no supervisor running (the not-running check happens before the dry-run check):
1633
+
1634
+ ```bash
1635
+ kxm runtime stop --dry-run --json
1636
+ ```
1637
+
1638
+ ```text
1639
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"runtime stop","stopped":false}
1640
+ ```
1641
+
1642
+ ## `kxm agent`
1643
+
1644
+ ### `kxm agent worker`
1645
+
1646
+ ```text
1647
+ kxm agent worker --name <name> --project <project> [--model <id>] [--fallback-models <ids>] [--tools <names>] [--session-isolation workflow|off] [--no-continue] [--fresh-start]
1648
+ ```
1649
+
1650
+ Starts a long-lived, supervised Pi RPC worker in the foreground through `scripts/kxm-worker.mjs`. The worker does not read models, tools, roles, or ownership from any workspace file; pass them explicitly.
1651
+
1652
+ | Option | Argument | Default | Description |
1653
+ |---|---|---|---|
1654
+ | `--name` | `<name>` | `KXM_AGENT_NAME` | Agent name |
1655
+ | `--project` | `<project>` | `KXM_PROJECT` | Hub project |
1656
+ | `--model` | `<id>` | `KXM_WORKER_MODEL`, else the Pi default | Primary model |
1657
+ | `--fallback-models` | `<ids>` | `KXM_WORKER_FALLBACK_MODELS` | Comma-separated fallback models |
1658
+ | `--tools` | `<names>` | `KXM_WORKER_TOOLS`, else Pi defaults | Comma-separated Pi tool allowlist |
1659
+ | `--session-isolation` | `<mode>` | `off` | Pi session isolation: workflow or off (default: off for upgrade compatibility) |
1660
+ | `--no-continue` | none | continue on | Disable every session resume |
1661
+ | `--fresh-start` | none | off | Skip only the initial session resume |
1662
+
1663
+ - Needs Pi and a reachable hub. Runs until stopped (`kxm hub stop` stops managed workers too). The exit code is the worker's.
1664
+ - A name and project are required (exit 2 otherwise); an invalid isolation mode exits 2.
1665
+ - `--dry-run` prints a `kxm.worker-result.v1` envelope with `workspace`, `name`, `project`, `model`, `fallbackModels`, `tools`, `sessionIsolation`, `continue`, `freshStart`.
1666
+ - The remaining worker variables are described in [Configuration](configuration.md#long-lived-worker-settings).
1667
+
1668
+ ```bash
1669
+ kxm agent worker --name reviewer --project demo --model xai/grok-4.6 --tools read,grep,find,ls --session-isolation workflow --fresh-start --dry-run
1670
+ ```
1671
+
1672
+ ```text
1673
+ would start worker
1674
+ ```
1675
+
1676
+ ```bash
1677
+ kxm agent worker --name coordinator --project demo --model antigravity/claude-sonnet-4-6 --fallback-models xai/grok-4.6 --session-isolation workflow
1678
+ ```
1679
+
1680
+ Not run: starts a long-lived Pi worker.
1681
+
1682
+ ## `kxm workflow`
1683
+
1684
+ Hub workflow runs (signed-webhook workflows with stages, evidence, waits, and a journal) and workflow definition files. `list`, `get`, and `export` read the local hub store, not a remote hub. `checkpoint`, `record`, and `wait` call the hub as an agent, like the [`peer`](#kxm-peer) commands, and share their policy checks and output shape. `definitions`, `add`, `remove`, and `modify` edit `kxm.workflow.v1` files in `.kxm/workflows/` (local) or `<KXM_USER_CONFIG_DIR>/workflows/` (global).
1685
+
1686
+ ### `kxm workflow list`
1687
+
1688
+ ```text
1689
+ kxm workflow list
1690
+ ```
1691
+
1692
+ List local workflow runs: the newest 200 runs in `.kxm/state/kxm.db` (or `KXM_DATA_PATH`), opened read-only.
1693
+
1694
+ No command-specific options.
1695
+
1696
+ - JSON keys: `runs`. Exit 1 with `state_unavailable` when the store does not exist.
1697
+
1698
+ ```bash
1699
+ kxm workflow list --json
1700
+ ```
1701
+
1702
+ ```text
1703
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"workflow list","runs":[]}
1704
+ ```
1705
+
1706
+ ### `kxm workflow get`
1707
+
1708
+ ```text
1709
+ kxm workflow get <runId>
1710
+ ```
1711
+
1712
+ Show one local workflow run with its stages, evidence, waits, and journal.
1713
+
1714
+ - Arguments: `<runId>`, Workflow run ID. No command-specific options. Reads only.
1715
+ - JSON keys: `run`, `journal`. Exit 1 with `workflow_not_found` or `state_unavailable`.
1716
+
1717
+ ```bash
1718
+ kxm workflow get wf_missing --json
1719
+ ```
1720
+
1721
+ ```text
1722
+ {"schema":"kxm.cli-result.v1","ok":false,"command":"workflow get","error":"workflow_not_found"}
1723
+ ```
1724
+
1725
+ ### `kxm workflow checkpoint`
1726
+
1727
+ ```text
1728
+ kxm workflow checkpoint [runId] [stageId] [status] [summary] [--evidence <json>] [--evidence-refs <json>]
1729
+ ```
1730
+
1731
+ Record a workflow stage checkpoint with evidence. Warnings and failures require another attempt until the stage passes or its attempts are exhausted. Peer-reply requirements must cite durable message IDs through `--evidence-refs`; caller-written evidence strings cannot satisfy them.
1732
+
1733
+ | Option | Argument | Default | Description |
1734
+ |---|---|---|---|
1735
+ | `--run-id` | `<id>` | none | Workflow run ID |
1736
+ | `--stage-id` | `<id>` | none | Active stage ID |
1737
+ | `--status` | `<status>` | none | passed, warning, or failed |
1738
+ | `--summary` | `<text>` | none | Stage summary |
1739
+ | `--evidence` | `<json>` | none | Key-value evidence JSON |
1740
+ | `--evidence-refs` | `<json>` | none | Peer evidence references JSON |
1741
+ | `--payload` | `<json>` | none | JSON payload |
1742
+
1743
+ - Positional arguments and options are interchangeable; `--payload` supplies any of the fields as one JSON object, and explicit flags override it. `--evidence` is an object of up to 64 string values; `--evidence-refs` maps each requirement to `{"messageIds":[...]}` (1 to 16 IDs).
1744
+ - Needs a hub. Mutates the run. Honors `--dry-run` (prints the parsed `args`).
1745
+ - Output is the hub's checkpoint result. Policy refusals: `tool_policy_denied`, `session_token_invalid`, `attempt_token_invalid`.
1746
+
1747
+ ```bash
1748
+ kxm workflow checkpoint wf_123 implement passed "Tests pass" --evidence '{"implementation-diff":"commit abc123"}' --dry-run --json
1749
+ ```
1750
+
1751
+ ```text
1752
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"workflow checkpoint","dryRun":true,"args":{"evidence":{"implementation-diff":"commit abc123"},"runId":"wf_123","stageId":"implement","status":"passed","summary":"Tests pass"}}
1753
+ ```
1754
+
1755
+ ```bash
1756
+ kxm workflow checkpoint --run-id wf_123 --stage-id review --status passed --summary "Two reviews agree" --evidence-refs '{"independent peer reviews":{"messageIds":["msg_a","msg_b"]}}'
1757
+ ```
1758
+
1759
+ Not run: writes to a hub workflow.
1760
+
1761
+ ### `kxm workflow record`
1762
+
1763
+ ```text
1764
+ kxm workflow record [runId] [category] [area] [summary] [--stage-id <id>] [--severity <level>] [--details <text>] [--evidence <items...>] [--related-entry-ids <ids...>]
1765
+ ```
1766
+
1767
+ Record workflow journal knowledge in one of ten categories: plan, decision, contradiction, error, lesson, observation, hypothesis, experiment, state-change, or skill-candidate.
1768
+
1769
+ | Option | Argument | Default | Description |
1770
+ |---|---|---|---|
1771
+ | `--run-id` | `<id>` | none | Workflow run ID |
1772
+ | `--category` | `<category>` | none | plan, decision, contradiction, error, lesson, observation, hypothesis, experiment, state-change, skill-candidate |
1773
+ | `--area` | `<area>` | the stage's area with `--stage-id` | harness, gates, implementation, workflow, documentation, security, other (defaults to the stage area with --stage-id) |
1774
+ | `--stage-id` | `<id>` | none | Stage the entry is about; the hub derives attempt and default area |
1775
+ | `--severity` | `<level>` | `info` | info, warning, error |
1776
+ | `--summary` | `<text>` | none | Entry summary |
1777
+ | `--details` | `<text>` | none | Detailed text |
1778
+ | `--evidence` | `<items...>` | none | Evidence strings |
1779
+ | `--related-entry-ids` | `<ids...>` | none | Related entry IDs |
1780
+ | `--payload` | `<json>` | none | JSON payload |
1781
+
1782
+ - `--evidence` (up to 32) and `--related-entry-ids` (up to 16) take space-separated values. `lesson` and `skill-candidate` entries require evidence.
1783
+ - Area is optional. With three positionals and no `--summary`, the third positional is the summary: `record <runId> <category> <summary>`. The four-positional form `record <runId> <category> <area> <summary>` still works.
1784
+ - `--stage-id` binds the entry to that stage. The hub derives the attempt (the current attempt for an in-progress or waiting stage, the last attempt consumed for a finished stage); callers cannot set it. Without `--area`, the entry takes the stage's declared area. With neither an area nor a stage that declares one, the hub answers 400 `invalid_improvement_area`; a stage that is not part of the run answers `invalid_journal_relation`.
1785
+ - The journal covers hub webhook runs only. A `kxm run` ID answers `workflow_not_found`.
1786
+ - Needs a hub. Mutates the journal. Honors `--dry-run`.
1787
+
1788
+ ```bash
1789
+ kxm workflow record wf_123 lesson gates "Flaky test hid a race" --severity warning --evidence https://ci.example.com/run/42 --dry-run --json
1790
+ ```
1791
+
1792
+ ```text
1793
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"workflow record","dryRun":true,"args":{"severity":"warning","evidence":["https://ci.example.com/run/42"],"runId":"wf_123","category":"lesson","area":"gates","summary":"Flaky test hid a race"}}
1794
+ ```
1795
+
1796
+ Bound to a stage, with the area taken from the stage:
1797
+
1798
+ ```bash
1799
+ kxm workflow record wf_123 lesson "Flaky test hid a race" --stage-id verify --evidence https://ci.example.com/run/42 --dry-run --json
1800
+ ```
1801
+
1802
+ ```text
1803
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"workflow record","dryRun":true,"args":{"stageId":"verify","evidence":["https://ci.example.com/run/42"],"runId":"wf_123","category":"lesson","summary":"Flaky test hid a race"}}
1804
+ ```
1805
+
1806
+ ### `kxm workflow wait`
1807
+
1808
+ ```text
1809
+ kxm workflow wait [runId] [stageId] [signalKey] [summary] [--evidence <json>] [--evidence-refs <json>] [--timeout-ms <ms>]
1810
+ ```
1811
+
1812
+ Wait for a workflow signal callback: pauses the active stage until a signed external callback checkpoints it. For a KXM run ID (`run_` followed by 32 hex digits) inside a project, the wait is registered with the Runtime instead of the hub (the supervisor starts if needed).
1813
+
1814
+ | Option | Argument | Default | Description |
1815
+ |---|---|---|---|
1816
+ | `--run-id` | `<id>` | none | Workflow run ID |
1817
+ | `--stage-id` | `<id>` | none | Active stage ID |
1818
+ | `--signal-key` | `<key>` | none | Wait signal key |
1819
+ | `--summary` | `<text>` | none | Expected result summary |
1820
+ | `--evidence` | `<json>` | none | Evidence JSON |
1821
+ | `--evidence-refs` | `<json>` | none | Peer evidence refs JSON |
1822
+ | `--timeout-ms` | `<ms>` | hub default (24 hours) | Wait timeout in milliseconds |
1823
+ | `--payload` | `<json>` | none | JSON payload |
1824
+
1825
+ - `--timeout-ms` accepts 1000 through 2592000000 (30 days).
1826
+ - Needs a hub (or the Runtime for KXM runs). Mutates the run. Honors `--dry-run`.
1827
+
1828
+ ```bash
1829
+ kxm workflow wait wf_123 verify github-pr-42-checks "Waiting on CI" --timeout-ms 3600000 --dry-run --json
1830
+ ```
1831
+
1832
+ ```text
1833
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"workflow wait","dryRun":true,"args":{"timeoutMs":3600000,"runId":"wf_123","stageId":"verify","signalKey":"github-pr-42-checks","summary":"Waiting on CI"}}
1834
+ ```
1835
+
1836
+ ### `kxm workflow signal`
1837
+
1838
+ ```text
1839
+ kxm workflow signal <runId> <signalKey> <status> <summary> [evidence...] [--delivery-id <id>]
1840
+ ```
1841
+
1842
+ Post a signed workflow callback or unblock a KXM run. It is [`kxm gate signal`](#kxm-gate-signal) without `--recovery-action`; see that section for credentials, output, and errors.
1843
+
1844
+ | Option | Argument | Default | Description |
1845
+ |---|---|---|---|
1846
+ | `--delivery-id` | `<id>` | `cli-signal:<uuid>` | Stable callback delivery ID |
1847
+
1848
+ - Arguments: `<runId>` Workflow run ID; `<signalKey>` Wait signal key; `<status>` passed, warning, or failed; `<summary>` Callback summary; `[evidence...]` required-key=evidence pairs.
1849
+
1850
+ ```bash
1851
+ KXM_WORKFLOW_ID=provenance-review KXM_WORKFLOW_SIGNAL_SECRET="$SIGNAL_SECRET" kxm workflow signal wf_123 ci-checks passed "CI green" --dry-run --json
1852
+ ```
1853
+
1854
+ ```text
1855
+ {"schema":"kxm.worker-result.v1","ok":true,"command":"signal","runId":"wf_123","signalKey":"ci-checks","status":"passed","evidence":{},...,"outcome":"passed","summary":"would post signed signal"}
1856
+ ```
1857
+
1858
+ ### `kxm workflow start`
1859
+
1860
+ ```text
1861
+ kxm workflow start [definitionId] [--payload <json|@file>] [--delivery-id <id>] [--event <name>]
1862
+ ```
1863
+
1864
+ POST a signed workflow-start webhook to `<hub>/v1/webhooks/<definitionId>`. The body is HMAC-SHA256 signed (`x-hub-signature-256`) and carries a delivery ID (`x-kxm-delivery-id`) so the hub deduplicates retries.
1865
+
1866
+ | Option | Argument | Default | Description |
1867
+ |---|---|---|---|
1868
+ | `--payload` | `<json>` | `{}` | JSON object or @file |
1869
+ | `--delivery-id` | `<id>` | `cli-<uuid>` | Stable provider delivery ID |
1870
+ | `--event` | `<name>` | none | Optional provider event name |
1871
+
1872
+ - Arguments: `[definitionId]`, Workflow definition ID (default `KXM_WORKFLOW_ID`).
1873
+ - The secret comes from the definition's `secretEnv` when `KXM_WEBHOOK_WORKFLOWS` or `KXM_WEBHOOK_WORKFLOWS_FILE` is configured (never both), otherwise from `KXM_WORKFLOW_SECRET`. `--event` is sent as `x-github-event` and added to the payload as `event` when the payload has none.
1874
+ - Needs a hub. Creates a run. Honors `--dry-run`.
1875
+ - JSON keys: `definitionId`, `deliveryId`, `status`, `runId`, `duplicate` (dry run: `definitionId`, `deliveryId`, `event`).
1876
+ - Errors: missing definition ID or secret (plain text, exit 2), `invalid_payload` (exit 2, the payload must be a JSON object), `workflow_start_failed` (exit 1).
1877
+
1878
+ ```bash
1879
+ KXM_WORKFLOW_SECRET="$SECRET" kxm workflow start provenance-review --payload '{"task":{"id":"T-1","summary":"Review auth change"}}' --dry-run
1880
+ ```
1881
+
1882
+ ```text
1883
+ would POST a signed workflow webhook
1884
+ ```
1885
+
1886
+ ```bash
1887
+ KXM_WEBHOOK_WORKFLOWS_FILE=workflows.json kxm workflow start provenance-review --payload @payload.json --delivery-id jira-T-1 --event issue_updated
1888
+ ```
1889
+
1890
+ Not run: posts to a hub and starts a workflow.
1891
+
1892
+ ### `kxm workflow export`
1893
+
1894
+ ```text
1895
+ kxm workflow export <runId> [--input <file>] [--out-dir <dir>]
1896
+ ```
1897
+
1898
+ Export a proposed retrospective: writes `<runId>.json` and `<runId>.md` built from the run and its journal. The source is the local hub store, or an offline snapshot (`{"run":...,"journal":[...]}`) with `--input`.
1899
+
1900
+ | Option | Argument | Default | Description |
1901
+ |---|---|---|---|
1902
+ | `--input` | `<file>` | local store | Offline snapshot JSON |
1903
+ | `--out-dir` | `<dir>` | `.kxm/assets/retrospectives` | Directory under workspace assets |
1904
+
1905
+ - Arguments: `<runId>`, Workflow run ID.
1906
+ - `--out-dir` must resolve inside the workspace assets directory (`output_outside_workspace_assets`, exit 2).
1907
+ - Writes two files. Honors `--dry-run`.
1908
+ - JSON keys: `jsonPath`, `mdPath`, `reviewDecision`. Errors (exit 1): `state_database_not_found`, `workflow_not_found`, `snapshot_missing`, `run_id_mismatch`.
1909
+
1910
+ ```bash
1911
+ kxm workflow export wf_missing --dry-run --json
1912
+ ```
1913
+
1914
+ ```text
1915
+ {"schema":"kxm.cli-result.v1","ok":false,"command":"retrospective export","error":"workflow_not_found"}
1916
+ ```
1917
+
1918
+ ### `kxm workflow definitions`
1919
+
1920
+ ```text
1921
+ kxm workflow definitions [--scope all|global|local]
1922
+ ```
1923
+
1924
+ List workflow definitions across scopes.
1925
+
1926
+ | Option | Argument | Default | Description |
1927
+ |---|---|---|---|
1928
+ | `--scope` | `<scope>` | `all` | Filter by scope: all, global, or local |
1929
+
1930
+ - Reads only. JSON keys: `workflows` (`id`, `description`, `scope`, `filePath`, `stepCount`, `roles`).
1931
+
1932
+ ```bash
1933
+ kxm workflow definitions
1934
+ ```
1935
+
1936
+ ```text
1937
+ WORKFLOW DEFINITIONS:
1938
+ default [local] 3 steps (roles: coordinator, implementer) Plan, implement, and verify a local change.
1939
+ ```
1940
+
1941
+ ### `kxm workflow add`
1942
+
1943
+ ```text
1944
+ kxm workflow add [workflowId] [--template <name> | --file <path> | --pick [selection]] [--description <text>] [--scope global|local] [--overwrite]
1945
+ ```
1946
+
1947
+ Add a workflow definition to global or local configuration. With `--template <name>`, the named built-in template is written under the workflow ID. Without a workflow ID, or with `--pick`, you choose from the built-in templates and, for local scope, existing global definitions. With `--file`, the YAML file is copied as-is. Otherwise a one-step scaffold is written: one `implementer` agent step with write access to `control` that ends the run `completed` on `passed` and `failed` on `failed`.
1948
+
1949
+ | Option | Argument | Default | Description |
1950
+ |---|---|---|---|
1951
+ | `--file` | `<path>` | none | Path to YAML workflow definition file |
1952
+ | `--description` | `<text>` | `Workflow <id>`, or the template's | Workflow description |
1953
+ | `--scope` | `<scope>` | `local` | Configuration scope: global or local (default: local) |
1954
+ | `--overwrite` | none | off | Overwrite existing workflow definition if present |
1955
+ | `--pick` | `[selection]` | none | Pick from available workflow templates (index or id) |
1956
+ | `--template` | `<name>` | none | Start from a built-in template: `implement-and-verify`, `dual-critic-review`, or `spec-and-plan` |
1957
+
1958
+ - Templates: `implement-and-verify` runs the `implementer` agent, then the project's `test` gate, and a failing gate (`implementation-failure`) sends the work back to `implement` at most twice. `dual-critic-review` adds two review steps between them, both run as the `coordinator` agent with read access; point `review-arch` and `review-cli` at your own agents for independent critics. `spec-and-plan` plans and then reviews the plan, both as `coordinator`, reading the repository only.
1959
+ - The templates and the scaffold are valid `kxm.workflow.v1` definitions that use only what `kxm init` creates: the `coordinator` and `implementer` agents, the `control` repository, and the `test` gate. Each was checked with `kxm init --json` and `kxm run <id> --dry-run` for this page. A new file under `.kxm/workflows/` is a permission expansion that `kxm trust check` asks you to review before you commit it.
1960
+ - Writes `<scope dir>/workflows/<id>.yaml`. `kxm run` loads only `.kxm/workflows/`, so a `--scope global` definition is not runnable until it is copied into a project. `--dry-run` plans the write and writes nothing.
1961
+ - `--template` refusals exit 2 and honor `--json`: `workflow_template_unknown` (the text names the three templates), `workflow_id_required` (no workflow ID), and `workflow_add_conflict` (combined with `--file` or `--pick`). An existing definition without `--overwrite` exits 1 with a plain `workflow add failed: workflow_already_exists: ...` line, also under `--dry-run`.
1962
+ - JSON keys: `workflowId`, `id`, `filePath`, `scope`.
1963
+
1964
+ Start a first workflow from a template:
1965
+
1966
+ ```bash
1967
+ kxm workflow add implement --template implement-and-verify
1968
+ ```
1969
+
1970
+ ```text
1971
+ Added workflow 'implement' to local (/work/proj/.kxm/workflows/implement.yaml)
1972
+ ```
1973
+
1974
+ ```bash
1975
+ kxm workflow add demo-flow --description "Demo" --dry-run --json
1976
+ ```
1977
+
1978
+ ```text
1979
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"workflow add","workflowId":"demo-flow","id":"demo-flow","filePath":"/work/proj/.kxm/workflows/demo-flow.yaml","scope":"local","dryRun":true,"planned":[{"action":"write","target":"/work/proj/.kxm/workflows/demo-flow.yaml"}]}
1980
+ ```
1981
+
1982
+ ```bash
1983
+ kxm workflow add implement --template nope
1984
+ ```
1985
+
1986
+ ```text
1987
+ workflow add failed: unknown template nope; choose implement-and-verify, dual-critic-review, spec-and-plan
1988
+ ```
1989
+
1990
+ Add a definition you wrote yourself (Not run):
1991
+
1992
+ ```bash
1993
+ kxm workflow add release-check --file ./release-check.yaml
1994
+ ```
1995
+
1996
+ ### `kxm workflow remove`
1997
+
1998
+ ```text
1999
+ kxm workflow remove [workflowId] [--scope global|local] [--pick [selection]]
2000
+ ```
2001
+
2002
+ Remove a workflow definition.
2003
+
2004
+ | Option | Argument | Default | Description |
2005
+ |---|---|---|---|
2006
+ | `--scope` | `<scope>` | `local` | Configuration scope: global or local (default: local) |
2007
+ | `--pick` | `[selection]` | none | Pick a workflow to remove (index or id) |
2008
+
2009
+ - Deletes a file. `--dry-run` plans the deletion, deletes nothing, and reports `removed: false`. JSON keys: `workflowId`, `id`, `removed`, `filePath`, `scope`.
2010
+
2011
+ ```bash
2012
+ kxm workflow remove release-check --dry-run
2013
+ ```
2014
+
2015
+ ```text
2016
+ dry run: remove workflow 'release-check' from local
2017
+ would delete /work/proj/.kxm/workflows/release-check.yaml
2018
+ ```
2019
+
2020
+ ### `kxm workflow modify`
2021
+
2022
+ ```text
2023
+ kxm workflow modify [workflowId] [--description <text>] [--scope global|local] [--pick [selection]]
2024
+ ```
2025
+
2026
+ Modify a workflow definition's description and rewrite its file.
2027
+
2028
+ | Option | Argument | Default | Description |
2029
+ |---|---|---|---|
2030
+ | `--description` | `<text>` | unchanged | Updated description |
2031
+ | `--scope` | `<scope>` | first match | Configuration scope: global or local |
2032
+ | `--pick` | `[selection]` | none | Pick a workflow to modify (index or id) |
2033
+
2034
+ - Rewrites the file (re-serialized YAML). `--dry-run` returns the modified definition and plans the write without making it. JSON keys: `workflowId`, `id`, `workflow`, `filePath`, `scope`.
2035
+
2036
+ ```bash
2037
+ kxm workflow modify default --description "Changed" --dry-run --json
2038
+ ```
2039
+
2040
+ ```text
2041
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"workflow modify","workflowId":"default","id":"default","workflow":{"schema":"kxm.workflow.v1","description":"Changed","coordinator":"coordinator",...},"filePath":"/work/proj/.kxm/workflows/default.yaml","scope":"local","dryRun":true,"planned":[{"action":"write","target":"/work/proj/.kxm/workflows/default.yaml"}]}
2042
+ ```
2043
+
2044
+ ## `kxm gate`
2045
+
2046
+ Validates workflow definitions and operates evidence gates. The group has exactly five gates; names declared in a workspace `gates.json` that do not map to one of them have no runner, and there is no generic subcommand that runs a gate by name. Results use the `kxm.worker-result.v1` envelope and, outside `--dry-run`, are appended to `.kxm/logs/telemetry.jsonl`.
2047
+
2048
+ ### `kxm gate validate`
2049
+
2050
+ ```text
2051
+ kxm gate validate [--file <path>]
2052
+ ```
2053
+
2054
+ Parse workflow definitions without printing secrets, using the same single source the hub loads: `--file`, else `KXM_WEBHOOK_WORKFLOWS_FILE`, else inline `KXM_WEBHOOK_WORKFLOWS`.
2055
+
2056
+ | Option | Argument | Default | Description |
2057
+ |---|---|---|---|
2058
+ | `--file` | `<path>` | configured source | Workflow definition file |
2059
+
2060
+ - Each definition's `secretEnv` must name a set variable holding at least 16 characters (likewise `signalSecretEnv` when declared); otherwise parsing fails, for example with `workflow.secret must be a string`.
2061
+ - Reads definitions; appends telemetry. No hub needed.
2062
+ - JSON keys: `source`, `file`, `workflows` (`id`, `secretConfigured`, `signalSecretConfigured`), `warnings`; `outcome` is `warning` when warnings exist.
2063
+ - Exit 2 for `workflow_source_required` or `ambiguous_workflow_source` (both variables set); exit 1 for `file_not_found` or a parse error.
2064
+
2065
+ ```bash
2066
+ KXM_WORKFLOW_SECRET="$SECRET" kxm gate validate --file workflows.json
2067
+ ```
2068
+
2069
+ ```text
2070
+ validated 1 workflow(s) from file with 1 warning(s)
2071
+ ```
2072
+
2073
+ ```bash
2074
+ KXM_WORKFLOW_SECRET="$SECRET" kxm gate validate --file workflows.json --json
2075
+ ```
2076
+
2077
+ ```text
2078
+ {"schema":"kxm.worker-result.v1","ok":true,"command":"validate","source":"file","file":"/work/proj/workflows.json","workflows":[{"id":"provenance-review","secretConfigured":true,"signalSecretConfigured":false}],"warnings":["workflow provenance-review stage review evidence policy independent peer reviews: degradation.minProducers is 1 (< 2); a single producer can satisfy the degraded peer-reply quorum"],...,"outcome":"warning",...}
2079
+ ```
2080
+
2081
+ ### `kxm gate artifacts-exist`
2082
+
2083
+ ```text
2084
+ kxm gate artifacts-exist --path <file>
2085
+ ```
2086
+
2087
+ Verify a non-empty file under workspace assets. The path resolves against the current directory and must be a regular, non-empty file inside the workspace assets directory both lexically and after resolving links.
2088
+
2089
+ | Option | Argument | Default | Description |
2090
+ |---|---|---|---|
2091
+ | `--path` | `<file>` | required | Artifact file under workspace assets |
2092
+
2093
+ - Reads the file; appends telemetry.
2094
+ - JSON keys: `path`, `bytes`. Exit 1 with `artifact_missing`, `artifact_empty`, `artifact_not_file`, `artifact_unreadable`, or `artifact_outside_workspace_assets`; a missing `--path` exits 2.
2095
+
2096
+ ```bash
2097
+ kxm gate artifacts-exist --path .kxm/assets/reports/summary.md
2098
+ ```
2099
+
2100
+ ```text
2101
+ artifact exists and is non-empty under workspace assets
2102
+ ```
2103
+
2104
+ ```bash
2105
+ kxm gate artifacts-exist --path README.md --json
2106
+ ```
2107
+
2108
+ ```text
2109
+ {"schema":"kxm.worker-result.v1","ok":false,"command":"artifacts-exist","error":"artifact_outside_workspace_assets","path":"/work/proj/README.md",...,"outcome":"failed","summary":"artifact verification failed: artifact_outside_workspace_assets"}
2110
+ ```
2111
+
2112
+ ### `kxm gate degrade`
2113
+
2114
+ ```text
2115
+ kxm gate degrade <runId> <stageId> --requirement <key> --reason <text>
2116
+ ```
2117
+
2118
+ Approve a configured lower peer quorum for the current attempt of a stage, as allowed by that requirement's evidence policy. Uses the administrative token in `KXM_AUTH_TOKEN`; do not put secrets in the reason.
2119
+
2120
+ | Option | Argument | Default | Description |
2121
+ |---|---|---|---|
2122
+ | `--requirement` | `<key>` | none | Canonical requirement key |
2123
+ | `--reason` | `<text>` | none | Non-secret operator reason |
2124
+
2125
+ - Arguments: `<runId>` Workflow run ID; `<stageId>` Active stage ID.
2126
+ - Both options and `KXM_AUTH_TOKEN` are required (plain text, exit 2).
2127
+ - Needs a hub. Mutates the run. Honors `--dry-run`.
2128
+ - JSON keys: `runId`, `stageId`, `requirementKey`, `status`, `duplicate`, `approvalId`. Failure: `workflow_degradation_failed` (exit 1).
2129
+
2130
+ ```bash
2131
+ KXM_AUTH_TOKEN="$ADMIN_TOKEN" kxm gate degrade wf_123 review --requirement "independent peer reviews" --reason "one reviewer offline" --dry-run
2132
+ ```
2133
+
2134
+ ```text
2135
+ would approve configured degraded quorum for wf_123/review/independent peer reviews
2136
+ ```
2137
+
2138
+ ### `kxm gate signal`
2139
+
2140
+ ```text
2141
+ kxm gate signal <runId> <signalKey> <status> <summary> [evidence...] [--delivery-id <id>] [--recovery-action <action>]
2142
+ ```
2143
+
2144
+ Post a signed workflow callback that checkpoints a waiting stage.
2145
+
2146
+ | Option | Argument | Default | Description |
2147
+ |---|---|---|---|
2148
+ | `--delivery-id` | `<id>` | `cli-signal:<uuid>` | Stable callback delivery ID |
2149
+ | `--recovery-action` | `<action>` | none | KXM recovery action: retry, fail, cancel, unblock |
2150
+
2151
+ - Arguments: `<runId>` Workflow run ID; `<signalKey>` Wait signal key; `<status>` passed, warning, or failed; `<summary>` Callback summary; `[evidence...]` required-key=evidence pairs.
2152
+ - For a KXM run ID (`run_` followed by 32 hex digits) inside a project, the signal goes to the Runtime (the supervisor starts if needed) and `--recovery-action` is passed through. JSON keys: `runId`, `signalKey`, `status`, `unblocked`, `deliveryId`.
2153
+ - Otherwise it is a hub webhook callback: `KXM_WORKFLOW_ID` names the definition, and the secret is the definition's `signalSecretEnv` (falling back to `secretEnv`) when a definition source is configured, else `KXM_WORKFLOW_SIGNAL_SECRET`. JSON keys: `duplicate`, `deliveryId`.
2154
+ - Reuse `--delivery-id` to retry one unchanged callback without a duplicate.
2155
+ - Honors `--dry-run`. Exit 2 for a missing argument, an invalid status, malformed or duplicate evidence keys, or missing `KXM_WORKFLOW_ID` or secret; exit 1 for `signal_failed`.
2156
+
2157
+ ```bash
2158
+ KXM_WORKFLOW_ID=provenance-review KXM_WORKFLOW_SIGNAL_SECRET="$SIGNAL_SECRET" kxm gate signal wf_123 ci-checks passed "CI green" "tests=https://ci.example.com/run/42" --dry-run --json
2159
+ ```
2160
+
2161
+ ```text
2162
+ {"schema":"kxm.worker-result.v1","ok":true,"command":"signal","runId":"wf_123","signalKey":"ci-checks","status":"passed","evidence":{"tests":"https://ci.example.com/run/42"},...,"outcome":"passed","summary":"would post signed signal"}
2163
+ ```
2164
+
2165
+ ```bash
2166
+ kxm gate signal run_0123456789abcdef0123456789abcdef verify passed "operator verified" --recovery-action unblock --dry-run
2167
+ ```
2168
+
2169
+ ```text
2170
+ would post signal to KXM run
2171
+ ```
2172
+
2173
+ ### `kxm gate github`
2174
+
2175
+ GitHub adapters. The only adapter is `watch`.
2176
+
2177
+ ### `kxm gate github watch`
2178
+
2179
+ ```text
2180
+ kxm gate github watch --run-id <id> --stage-id <id> --signal-key <key> --repo <owner/name> --pr <number> [--required <names>] [--timeout-ms <ms>] [--interval-ms <ms>] [--delivery-id <id>]
2181
+ ```
2182
+
2183
+ Poll required checks and post the signed signal. The adapter polls the pull request's head commit check runs on `api.github.com` until every required check concludes or the timeout expires, then posts a signed `passed`, `warning`, or `failed` callback. On timeout it posts `failed` with summary `github_watch_timeout` and exits 4.
2184
+
2185
+ | Option | Argument | Default | Description |
2186
+ |---|---|---|---|
2187
+ | `--run-id` | `<id>` | required | Workflow run ID |
2188
+ | `--stage-id` | `<id>` | required | Waiting stage ID |
2189
+ | `--signal-key` | `<key>` | required | Wait signal key |
2190
+ | `--repo` | `<owner/name>` | required | GitHub repository |
2191
+ | `--pr` | `<number>` | required | Pull request number |
2192
+ | `--required` | `<names>` | none | Comma-separated required check names |
2193
+ | `--timeout-ms` | `<ms>` | `1800000` | Watch timeout |
2194
+ | `--interval-ms` | `<ms>` | `15000` | Poll interval |
2195
+ | `--delivery-id` | `<id>` | generated per wait | Stable callback delivery ID |
2196
+
2197
+ - Needs `KXM_WORKFLOW_ID`, a signal secret (as for `gate signal`), and `GITHUB_TOKEN` or `GH_TOKEN`. Without a GitHub token it exits 1 with summary `github_auth_unavailable` and `skipped: true`, without calling GitHub.
2198
+ - Calls GitHub and the hub. `--dry-run` still polls GitHub but does not post.
2199
+ - JSON keys: `posted`, `status`, `summary`, `evidence`, `deliveryId`, `skipped`. Failure summaries include `github_pr_unavailable`, `github_head_unavailable`, `github_checks_unavailable`, `workflow_not_waiting`, `signal_failed`. A payload that would contain a secret is refused with `redaction_failure`.
2200
+
2201
+ ```bash
2202
+ KXM_WORKFLOW_ID=provenance-review KXM_WORKFLOW_SIGNAL_SECRET="$SIGNAL_SECRET" kxm gate github watch --run-id wf_123 --stage-id review --signal-key github-pr-42-checks --repo acme/web --pr 42 --required build,test --dry-run --json
2203
+ ```
2204
+
2205
+ ```text
2206
+ {"schema":"kxm.worker-result.v1","ok":false,"command":"github watch","posted":false,"evidence":{},"skipped":true,...,"outcome":"failed","summary":"github_auth_unavailable"}
2207
+ ```
2208
+
2209
+ Captured without a GitHub token. With `GITHUB_TOKEN` set, the same command polls GitHub (Not run).
2210
+
2211
+ ## `kxm peer`
2212
+
2213
+ Peer agent messaging through the hub. Each invocation connects to the hub as a short-lived agent named `KXM_AGENT_NAME` (default `cli-<pid>`) in project `KXM_PROJECT` (default: the `package.json` name, else the directory name), using `KXM_AUTH_TOKEN`, a project token, or the persisted `hub-env.json` credential. That agent appears in `peer list` and the dashboard. Every subcommand accepts `--payload <json>` with the tool's fields as one object; explicit flags override it. Tool policy from `KXM_ATTEMPT_TOKEN`, `KXM_SESSION_TOKEN`, or the on-disk session token is enforced first (`tool_policy_denied`, `session_token_invalid`, `attempt_token_invalid`).
2214
+
2215
+ All subcommands need a hub, honor `--dry-run` (printing the parsed `args` without connecting), print the hub's result object on success, and print `{"ok":false,"error":"command_failed","detail":"..."}` with exit 1 on failure. Malformed `--payload` exits 2 with `invalid_payload`.
2216
+
2217
+ ### `kxm peer list`
2218
+
2219
+ ```text
2220
+ kxm peer list [--include-offline]
2221
+ ```
2222
+
2223
+ List peer agents in this project's hub pool with host and presence.
2224
+
2225
+ | Option | Argument | Default | Description |
2226
+ |---|---|---|---|
2227
+ | `--include-offline` | none | off | Also list registered peers whose hub lease has expired |
2228
+ | `--payload` | `<json>` | none | JSON payload |
2229
+
2230
+ - Reads the roster (the call itself registers the CLI agent). Output keys: `agents` (`id`, `name`, `purpose`, `project`, `connectedAt`, `lastSeenAt`, `online`, `host`, `leaseExpiresAt`, `presence`).
2231
+
2232
+ ```bash
2233
+ kxm peer list --json
2234
+ ```
2235
+
2236
+ ```text
2237
+ {"schema":"kxm.cli-result.v1","agents":[{"id":"agt_0c91aef519734fc1bd36518d2fc1cf4e","name":"cli-85922","purpose":"CLI agent client","project":"proj","connectedAt":"2026-09-23T13:53:24.142Z","lastSeenAt":"2026-09-23T13:53:24.156Z","online":true,"host":"host.local","leaseExpiresAt":"2026-09-23T13:53:54.156Z","presence":"online"}]}
2238
+ ```
2239
+
2240
+ ### `kxm peer send`
2241
+
2242
+ ```text
2243
+ kxm peer send [target] [content] [--delivery <mode>] [--correlation-id <id>] [--idempotency-key <key>] [--workflow-context <json>] [--ttl-ms <ms>] [--allow-offline]
2244
+ ```
2245
+
2246
+ Send a focused request to a peer agent. Returns a message ID for `peer get` and `peer await`.
2247
+
2248
+ | Option | Argument | Default | Description |
2249
+ |---|---|---|---|
2250
+ | `--target` | `<name>` | none | Peer name or agent ID |
2251
+ | `--content` | `<text>` | none | Focused request content |
2252
+ | `--delivery` | `<mode>` | `followUp` | steer, followUp, or nextTurn |
2253
+ | `--correlation-id` | `<id>` | none | Task grouping ID |
2254
+ | `--idempotency-key` | `<key>` | none | Deduplication key |
2255
+ | `--workflow-context` | `<json>` | none | Workflow context JSON |
2256
+ | `--ttl-ms` | `<ms>` | hub default (24 hours) | Message TTL in milliseconds |
2257
+ | `--allow-offline` | none | off | Queue the request if the target is registered but offline |
2258
+ | `--payload` | `<json>` | none | JSON payload |
2259
+
2260
+ - `--workflow-context` is `{"runId","stageId","requirementKey","attempt"}` and is the only way a reply can count as peer evidence. `--ttl-ms` accepts 1000 through 604800000.
2261
+ - Mutates (queues a message). Output keys: `messageId`, `status`, `target`.
2262
+
2263
+ ```bash
2264
+ kxm peer send reviewer "Review the diff in PR 42 and list blocking issues" --dry-run --json
2265
+ ```
2266
+
2267
+ ```text
2268
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"peer send","dryRun":true,"args":{"target":"reviewer","content":"Review the diff in PR 42 and list blocking issues"}}
2269
+ ```
2270
+
2271
+ ```bash
2272
+ kxm peer send reviewer "Independent review of the auth change" --workflow-context '{"runId":"wf_123","stageId":"review","requirementKey":"independent peer reviews","attempt":1}' --idempotency-key wf_123:review:1
2273
+ ```
2274
+
2275
+ Not run: sends a message through a hub.
2276
+
2277
+ ### `kxm peer get`
2278
+
2279
+ ```text
2280
+ kxm peer get [messageId]
2281
+ ```
2282
+
2283
+ Check a peer request status and reply.
2284
+
2285
+ | Option | Argument | Default | Description |
2286
+ |---|---|---|---|
2287
+ | `--message-id` | `<id>` | none | Message ID |
2288
+ | `--payload` | `<json>` | none | JSON payload |
2289
+
2290
+ - Reads only. Output is the stored message.
2291
+
2292
+ ```bash
2293
+ kxm peer get msg_missing --json
2294
+ ```
2295
+
2296
+ ```text
2297
+ {"schema":"kxm.cli-result.v1","ok":false,"error":"command_failed","detail":"message not found"}
2298
+ ```
2299
+
2300
+ ### `kxm peer await`
2301
+
2302
+ ```text
2303
+ kxm peer await [messageId] [--timeout-ms <ms>]
2304
+ ```
2305
+
2306
+ Wait for a peer request reply (capped at 60 seconds). Longer waits belong in a workflow wait step.
2307
+
2308
+ | Option | Argument | Default | Description |
2309
+ |---|---|---|---|
2310
+ | `--message-id` | `<id>` | none | Message ID |
2311
+ | `--timeout-ms` | `<ms>` | `60000` | Timeout in milliseconds (max 60000) |
2312
+ | `--payload` | `<json>` | none | JSON payload |
2313
+
2314
+ - Reads only. Output is the reply or a terminal error.
2315
+
2316
+ ```bash
2317
+ kxm peer await msg_abc --timeout-ms 30000 --dry-run --json
2318
+ ```
2319
+
2320
+ ```text
2321
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"peer await","dryRun":true,"args":{"timeoutMs":30000,"messageId":"msg_abc"}}
2322
+ ```
2323
+
2324
+ ### `kxm peer cancel`
2325
+
2326
+ ```text
2327
+ kxm peer cancel [messageId]
2328
+ ```
2329
+
2330
+ Cancel a sent peer request. Only the agent that sent the request can cancel it, so run with the sender's `KXM_AGENT_NAME`.
2331
+
2332
+ | Option | Argument | Default | Description |
2333
+ |---|---|---|---|
2334
+ | `--message-id` | `<id>` | none | Message ID |
2335
+ | `--payload` | `<json>` | none | JSON payload |
2336
+
2337
+ - Mutates the message.
2338
+
2339
+ ```bash
2340
+ kxm peer cancel msg_abc --dry-run --json
2341
+ ```
2342
+
2343
+ ```text
2344
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"peer cancel","dryRun":true,"args":{"messageId":"msg_abc"}}
2345
+ ```
2346
+
2347
+ ### `kxm peer fanout`
2348
+
2349
+ ```text
2350
+ kxm peer fanout --targets <name>... --content <text> [--correlation-id <id>] [--idempotency-key-prefix <prefix>] [--workflow-context <json>] [--ttl-ms <ms>] [--timeout-ms <ms>]
2351
+ ```
2352
+
2353
+ Send the same request to one through three peers and return their replies for comparison. A local timeout returns pending entries with durable message IDs; repeat the exact command (same correlation ID and prefix) to retry without duplicates.
2354
+
2355
+ | Option | Argument | Default | Description |
2356
+ |---|---|---|---|
2357
+ | `--targets` | `<items...>` | none | Target peer names (1-3) |
2358
+ | `--content` | `<text>` | none | Request content |
2359
+ | `--correlation-id` | `<id>` | none | Task grouping ID |
2360
+ | `--idempotency-key-prefix` | `<prefix>` | none | Idempotency prefix |
2361
+ | `--workflow-context` | `<json>` | none | Workflow context JSON |
2362
+ | `--ttl-ms` | `<ms>` | hub default (24 hours) | Message TTL in milliseconds |
2363
+ | `--timeout-ms` | `<ms>` | `1800000` (30 minutes) | Timeout in milliseconds |
2364
+ | `--payload` | `<json>` | none | JSON payload |
2365
+
2366
+ - `--targets` takes space-separated names (a comma-joined value is sent as one name). `--timeout-ms` accepts 100 through 1800000.
2367
+ - Mutates (queues messages). Output keys: `responses`.
2368
+
2369
+ ```bash
2370
+ kxm peer fanout --targets reviewer critic --content "Independent review of PR 42" --correlation-id wf_123 --idempotency-key-prefix pr42 --dry-run --json
2371
+ ```
2372
+
2373
+ ```text
2374
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"peer fanout","dryRun":true,"args":{"targets":["reviewer","critic"],"content":"Independent review of PR 42","correlationId":"wf_123","idempotencyKeyPrefix":"pr42"}}
2375
+ ```
2376
+
2377
+ ### `kxm peer inbox`
2378
+
2379
+ ```text
2380
+ kxm peer inbox
2381
+ ```
2382
+
2383
+ List inbound peer requests. From the CLI this always returns `{"messages":[]}`: the inbox lives in a long-running harness session, which a one-shot CLI call does not have. Use `kxm dash --screen inbox` to see pending requests.
2384
+
2385
+ | Option | Argument | Default | Description |
2386
+ |---|---|---|---|
2387
+ | `--payload` | `<json>` | none | JSON payload |
2388
+
2389
+ ```bash
2390
+ kxm peer inbox --json
2391
+ ```
2392
+
2393
+ ```text
2394
+ {"schema":"kxm.cli-result.v1","messages":[]}
2395
+ ```
2396
+
2397
+ ### `kxm peer reply`
2398
+
2399
+ ```text
2400
+ kxm peer reply [messageId] [content]
2401
+ ```
2402
+
2403
+ Reply to an inbound request. Only the request's recipient may reply (`message_forbidden` otherwise), so run with the recipient's `KXM_AGENT_NAME`.
2404
+
2405
+ | Option | Argument | Default | Description |
2406
+ |---|---|---|---|
2407
+ | `--message-id` | `<id>` | none | Message ID |
2408
+ | `--content` | `<text>` | none | Reply content |
2409
+ | `--payload` | `<json>` | none | JSON payload |
2410
+
2411
+ - Mutates the message. Output keys: `messageId`, `status`, `recipient`.
2412
+
2413
+ ```bash
2414
+ kxm peer reply msg_abc "LGTM with one nit" --dry-run --json
2415
+ ```
2416
+
2417
+ ```text
2418
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"peer reply","dryRun":true,"args":{"messageId":"msg_abc","content":"LGTM with one nit"}}
2419
+ ```
2420
+
2421
+ ## `kxm task`
2422
+
2423
+ Project tasks stored as `kxm.task.v1` YAML in `.kxm/tasks/` in the current directory. `kxm task` with no subcommand runs `task list`. None needs a hub. Errors are plain text on stderr, even with `--json`.
2424
+
2425
+ ### `kxm task create`
2426
+
2427
+ ```text
2428
+ kxm task create <title> [--goal <goalId>] [--objective <text>] [--workflow <id>] [--tracker github|jira] [--issue <key>]
2429
+ ```
2430
+
2431
+ Create a task with status `todo`.
2432
+
2433
+ | Option | Argument | Default | Description |
2434
+ |---|---|---|---|
2435
+ | `--goal` | `<goalId>` | none | Parent goal ID |
2436
+ | `--objective` | `<text>` | the title | Task objective |
2437
+ | `--workflow` | `<id>` | none | Assigned workflow ID |
2438
+ | `--tracker` | `<tracker>` | none | Issue tracker (github or jira) |
2439
+ | `--issue` | `<key>` | none | Issue number or Jira key |
2440
+
2441
+ - A tracker link is recorded only when both `--tracker` and `--issue` are given.
2442
+ - Writes `.kxm/tasks/<id>.yaml`. `--dry-run` shows the task it would create and plans the write; the ID is assigned when the task is created, so a dry run's ID is not the one a real run gets. JSON keys: `task`.
2443
+
2444
+ ```bash
2445
+ kxm task create "Fix flaky test" --objective "Stabilize CI" --workflow default --dry-run --json
2446
+ ```
2447
+
2448
+ ```text
2449
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"task create","task":{"schema":"kxm.task.v1","id":"task_0e77833bc462","title":"Fix flaky test","objective":"Stabilize CI","acceptanceCriteria":[],"status":"todo","assignedWorkflow":"default",...},"dryRun":true,"planned":[{"action":"write","target":"/work/proj/.kxm/tasks/task_0e77833bc462.yaml"}]}
2450
+ ```
2451
+
2452
+ ### `kxm task list`
2453
+
2454
+ ```text
2455
+ kxm task list [--goal <goalId>] [--status <status>]
2456
+ ```
2457
+
2458
+ List project tasks.
2459
+
2460
+ | Option | Argument | Default | Description |
2461
+ |---|---|---|---|
2462
+ | `--goal` | `<goalId>` | none | Filter by goal ID |
2463
+ | `--status` | `<status>` | none | Filter by status: todo, in_progress, blocked, in_review, done |
2464
+
2465
+ - Reads only. JSON keys: `count`, `tasks`.
2466
+
2467
+ ```bash
2468
+ kxm task list
2469
+ ```
2470
+
2471
+ ```text
2472
+ [todo] task_4f79c0833e41: Fix flaky test -> default
2473
+ ```
2474
+
2475
+ ### `kxm task get`
2476
+
2477
+ ```text
2478
+ kxm task get <taskId>
2479
+ ```
2480
+
2481
+ Get task details and linked workflow status.
2482
+
2483
+ - Reads only. JSON keys: `task`. A missing task prints `Task <id> not found` and exits 1.
2484
+
2485
+ ```bash
2486
+ kxm task get task_4f79c0833e41
2487
+ ```
2488
+
2489
+ ```text
2490
+ Task: task_4f79c0833e41
2491
+ Title: Fix flaky test
2492
+ Status: todo
2493
+ Objective: Stabilize CI
2494
+ Workflow: default
2495
+ ```
2496
+
2497
+ ### `kxm task run`
2498
+
2499
+ ```text
2500
+ kxm task run <taskId>
2501
+ ```
2502
+
2503
+ Launch a workflow run driven by this task: runs `kxm run <workflow> <objective>` with the task's assigned workflow (default `default`), then sets the task status to `in_progress` when that succeeds.
2504
+
2505
+ - Output, requirements, and errors are those of [`kxm run`](#kxm-run), including the `--workspace` refusal.
2506
+ - Mutates the task file. `--dry-run` validates the project and the workflow as `kxm run --dry-run` does, then plans the run request and the task file write without making either; the status stays unchanged. Dry-run JSON keys: `taskId`, `projectRoot`, `workflowId`, `configRevision`, `status` (the status it would set), `dryRun`, `planned`.
2507
+
2508
+ ```bash
2509
+ kxm task run task_4f79c0833e41 --dry-run
2510
+ ```
2511
+
2512
+ ```text
2513
+ dry run: run workflow default for task task_4f79c0833e41, then mark it in_progress
2514
+ would request POST kxm-runtime /v1/runs (starts the Runtime supervisor if it is not running)
2515
+ would write /work/proj/.kxm/tasks/task_4f79c0833e41.yaml
2516
+ ```
2517
+
2518
+ ### `kxm task sync`
2519
+
2520
+ ```text
2521
+ kxm task sync <taskId>
2522
+ ```
2523
+
2524
+ Sync task status and evidence with its linked issue board. In 0.7.1 this is local only: it marks the task's tracker link `synced` and updates timestamps without contacting GitHub or Jira.
2525
+
2526
+ - Writes the task file. `--dry-run` returns the synced task and plans the write without making it. JSON keys: `task`.
2527
+ - Exit 1 when the task does not exist or has no tracker link.
2528
+
2529
+ ```bash
2530
+ kxm task sync task_4f79c0833e41 --json
2531
+ ```
2532
+
2533
+ ```text
2534
+ task sync failed: Task task_4f79c0833e41 does not have an associated issue board tracker
2535
+ ```
2536
+
2537
+ ## `kxm goal`
2538
+
2539
+ Project goals stored as `kxm.goal.v1` YAML in `.kxm/goals/` in the current directory. `kxm goal` with no subcommand runs `goal list`. No hub needed.
2540
+
2541
+ ### `kxm goal create`
2542
+
2543
+ ```text
2544
+ kxm goal create <title> [--area <area>] [--metric <metric...>] [--target-date <date>]
2545
+ ```
2546
+
2547
+ Create a project goal with status `active`.
2548
+
2549
+ | Option | Argument | Default | Description |
2550
+ |---|---|---|---|
2551
+ | `--area` | `<area>` | none | Workflow area (e.g. software-engineering, security-reliability) |
2552
+ | `--metric` | `<metric...>` | none | Success metrics for this goal |
2553
+ | `--target-date` | `<date>` | none | Target achievement date (ISO-8601 or YYYY-MM-DD) |
2554
+
2555
+ - Writes `.kxm/goals/<id>.yaml`. `--dry-run` shows the goal it would create and plans the write; the ID is assigned when the goal is created. JSON keys: `goal`.
2556
+
2557
+ ```bash
2558
+ kxm goal create "Ship v1" --area software-engineering --dry-run --json
2559
+ ```
2560
+
2561
+ ```text
2562
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"goal create","goal":{"schema":"kxm.goal.v1","id":"goal_47c05405528d","title":"Ship v1","area":"software-engineering","status":"active","successMetrics":[],"createdAt":"2026-09-23T17:51:23.667Z","updatedAt":"2026-09-23T17:51:23.667Z"},"dryRun":true,"planned":[{"action":"write","target":"/work/proj/.kxm/goals/goal_47c05405528d.yaml"}]}
2563
+ ```
2564
+
2565
+ With metrics and a target date (Not run):
2566
+
2567
+ ```bash
2568
+ kxm goal create "Ship v1" --area software-engineering --metric "p95 < 300ms" "zero sev1" --target-date 2026-12-01
2569
+ ```
2570
+
2571
+ ### `kxm goal list`
2572
+
2573
+ ```text
2574
+ kxm goal list
2575
+ ```
2576
+
2577
+ List project goals.
2578
+
2579
+ No command-specific options.
2580
+
2581
+ - Reads only. JSON keys: `count`, `goals`.
2582
+
2583
+ ```bash
2584
+ kxm goal list
2585
+ ```
2586
+
2587
+ ```text
2588
+ [active] goal_cebbbf714ed4: Ship v1 (software-engineering)
2589
+ ```
2590
+
2591
+ ## `kxm suggest`
2592
+
2593
+ ```text
2594
+ kxm suggest <prompt...>
2595
+ ```
2596
+
2597
+ Recommends a workflow, area, roles, and skills for a prompt or issue description using keyword matching and the detected harnesses. The suggested workflow ID comes from a built-in catalog and may not exist in your project; check with `kxm workflow definitions` before running the suggested command.
2598
+
2599
+ - Arguments: `<prompt...>`, the task description.
2600
+ - No command-specific options. Probes harnesses as `harness list` does; reads nothing else. No hub needed.
2601
+ - Suggested skills are always KXM command skills shipped in `plugins/kxm/skills` (for example `kxm-workflow`, `kxm-runs`, `kxm-peer`, `kxm-context-memory`), never the KontextMind knowledge-plane skills.
2602
+ - JSON keys: `prompt`, `workflowId`, `area`, `confidence`, `reasons`, `suggestedSkills`, `roles` (`planner`, `writer`, `critics`, `verifier`), `suggestedCommand`.
2603
+
2604
+ ```bash
2605
+ kxm suggest "Add retry with backoff to the payment webhook handler"
2606
+ ```
2607
+
2608
+ ```text
2609
+ Suggested Workflow: software-engineering/feature-implementation (software-engineering)
2610
+ Confidence: 65%
2611
+ Reasons: Matched keywords: add
2612
+ Suggested Skills: kxm-workflow, kxm-peer, kxm-context-memory
2613
+ Roles:
2614
+ Planner: claude (fable)
2615
+ Writer: grok (grok-4.6)
2616
+ Critics: claude:fable, codex:gpt-5.6-sol
2617
+ Verifier: npm run verify
2618
+
2619
+ Execute with:
2620
+ kxm run software-engineering/feature-implementation "Add retry with backoff to the payment webhook handler"
2621
+ ```
2622
+
2623
+ ## `kxm explain`
2624
+
2625
+ ```text
2626
+ kxm explain [--mode <name>] [--domains <list>] [--model <id>]
2627
+ ```
2628
+
2629
+ Pre-flight context footprint and token cost inspection for workflow modes. The estimate uses a fixed base prompt size, the length of each configured context file (1500 characters when a file is missing), domain prompt snippets, and 650 characters per active tool, at about 3.8 characters per token. Modes come from `.kxm/modes.yaml` or the built-in defaults; costs come from the project price catalog and stay unknown when it is missing or unverified.
2630
+
2631
+ | Option | Argument | Default | Description |
2632
+ |---|---|---|---|
2633
+ | `--mode` | `<name>` | `coder` | Major mode (coder, planner, auditor, browser) |
2634
+ | `--domains` | `<list>` | none | Comma-separated domain modules (git, k8s, database, browser) |
2635
+ | `--model` | `<id>` | mode default | Target model identifier (e.g. grok/grok-4.6, claude/fable) |
2636
+
2637
+ - Reads only. No hub needed.
2638
+ - JSON keys: `majorMode`, `enabledDomains`, `model`, `breakdown` (`name`, `chars`, `estimatedTokens`), `totalChars`, `totalTokens`, `contextWindowRatio`, `projectedCost`, `catalogStatus`, `catalogReason`.
2639
+
2640
+ ```bash
2641
+ kxm explain
2642
+ ```
2643
+
2644
+ ```text
2645
+ ════════════════════════════════════════════════════════════
2646
+ KXM PRE-FLIGHT CONTEXT EXPLAIN
2647
+ ════════════════════════════════════════════════════════════
2648
+ Major Mode: coder
2649
+ Enabled Domains: (none)
2650
+ Target Model: grok/grok-4.6
2651
+ Catalog status: price catalog missing
2652
+
2653
+ CONTEXT BREAKDOWN:
2654
+ • Base System Prompt (coder) 843 tokens
2655
+ • Context File: AGENTS.md 395 tokens
2656
+ • Tool Schemas (4 active tools) 685 tokens
2657
+ ────────────────────────────────────────────────────────────
2658
+ TOTAL PROMPT FOOTPRINT: 1,923 tokens (1% of 200k window)
2659
+ ...
2660
+ ```
2661
+
2662
+ ```bash
2663
+ kxm explain --mode planner --domains git,k8s --model grok/grok-4.6 --json
2664
+ ```
2665
+
2666
+ ```text
2667
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"explain","majorMode":"planner","enabledDomains":["git","k8s"],"model":"grok/grok-4.6","breakdown":[...],"totalChars":10022,"totalTokens":2640,"contextWindowRatio":1.3,"projectedCost":{"inputCostUsd":null,"cacheReadCostUsd":null,"outputCostEstimateUsd":null},"catalogStatus":"missing","catalogReason":"price catalog missing"}
2668
+ ```
2669
+
2670
+ ## `kxm context`
2671
+
2672
+ KXM context operating-system queries. Every subcommand POSTs to the hub's `/v1/context/*` API as the control plane, authenticating only with `KXM_AUTH_TOKEN` (the persisted `hub-env.json` credential is not used; without the variable the hub answers 401 `invalid_auth`). The first argument is the project scope. Results carry the hub's HTTP `status` and response fields. Exit 0 on a 2xx response, 1 otherwise. An unreachable hub crashes the command with a stack trace (exit 1, no JSON). Agents reach the same data through the `kxm_context`, `kxm_recall`, `kxm_state`, `kxm_episode`, and `kxm_promote` tools; there is no `kxm_explain` tool.
2673
+
2674
+ ### `kxm context get`
2675
+
2676
+ ```text
2677
+ kxm context get <project> --role <role> --task <task> [--run <runId>] [--stage <stageId>] [--budget <tokens>] [--kinds <kinds>]
2678
+ ```
2679
+
2680
+ Assemble a role-aware context packet within a token budget.
2681
+
2682
+ | Option | Argument | Default | Description |
2683
+ |---|---|---|---|
2684
+ | `--role` | `<role>` | required | Requesting role (repro, planner, critic, implementer, verifier, or custom) |
2685
+ | `--task` | `<task>` | required | What the role is trying to do |
2686
+ | `--run` | `<runId>` | none | Workflow run scope |
2687
+ | `--stage` | `<stageId>` | none | Workflow stage scope |
2688
+ | `--budget` | `<tokens>` | hub default | Token budget for the packet |
2689
+ | `--kinds` | `<kinds>` | all | Comma-separated item kinds to include |
2690
+
2691
+ - `--budget` must be an integer from 512 to 200000 (exit 2).
2692
+ - Reads only. Output keys: `status`, `packet` (`workingState`, `currentState`, `knowledge`, `evidence`, `episodes`, `skills`, `contradictions`, `unresolvedGaps`, `provenanceSummary`, `estimatedTokens`), `audit`.
2693
+ - Selection is deterministic. Eligible items are ordered by open contradiction, project before `_shared`, task-matched before unmatched, role kind priority, lexical BM25 relevance to `--task`, confidence, authority, recency (newest first), then id. The budget is filled first-fit: an item that does not fit is skipped and smaller ones still fill it.
2694
+ - `audit.relevance` holds numbers only: `taskTokens` (distinct task words after stopword removal), `matchedCandidates` (eligible items sharing a task word) and `selected` (each selected item's rounded score, in `selectedIds` order).
2695
+
2696
+ ```bash
2697
+ KXM_AUTH_TOKEN="$ADMIN_TOKEN" kxm context get proj --role planner --task "Plan the auth refactor" --budget 4000 --json
2698
+ ```
2699
+
2700
+ ```text
2701
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"context get","status":200,"packet":{"workingState":{},"currentState":[],"knowledge":[],"evidence":[],"episodes":[],"skills":[],"contradictions":[],"unresolvedGaps":["no context records exist for this project yet"],"provenanceSummary":{},"estimatedTokens":0},"audit":{"request":{"project":"proj","role":"planner","task":"Plan the auth refactor"},"selectedIds":[],"provenanceSummary":{},"estimatedTokens":0,"budgetTokens":4000,"candidateCount":0,"excludedSuperseded":0,"unresolvedGaps":["no context records exist for this project yet"],"relevance":{"taskTokens":3,"matchedCandidates":0,"selected":[]}}}
2702
+ ```
2703
+
2704
+ ```bash
2705
+ KXM_AUTH_TOKEN="$ADMIN_TOKEN" kxm context get proj --role planner --task "Plan the auth refactor"
2706
+ ```
2707
+
2708
+ ```text
2709
+ context get assembled
2710
+ ```
2711
+
2712
+ Without `KXM_AUTH_TOKEN`:
2713
+
2714
+ ```bash
2715
+ kxm context get proj --role planner --task "Plan the auth refactor" --json
2716
+ ```
2717
+
2718
+ ```text
2719
+ {"schema":"kxm.cli-result.v1","ok":false,"command":"context get","status":401,"error":"invalid administrative authentication token","code":"invalid_auth",...,"nextAction":"check_project_token"}
2720
+ ```
2721
+
2722
+ ### `kxm context recall`
2723
+
2724
+ ```text
2725
+ kxm context recall <project> [--query <text>] [--kinds <kinds>] [--limit <n>]
2726
+ ```
2727
+
2728
+ Search durable context records (metadata only).
2729
+
2730
+ | Option | Argument | Default | Description |
2731
+ |---|---|---|---|
2732
+ | `--query` | `<text>` | none | Query against summaries and state keys |
2733
+ | `--kinds` | `<kinds>` | all | Comma-separated item kinds to include |
2734
+ | `--limit` | `<n>` | hub default | Maximum results (1-100) |
2735
+
2736
+ - Ranking: items whose summary or state key contains the whole query (ignoring case) come first, then items that share a word with it, by BM25 relevance, then id. Items with neither are left out; an empty query returns every item in id order.
2737
+ - Reads only. Output keys: `status`, `items` (metadata plus a numeric `relevance`; never summaries), `unresolvedGaps`.
2738
+
2739
+ ```bash
2740
+ KXM_AUTH_TOKEN="$ADMIN_TOKEN" kxm context recall proj --query auth --limit 5 --json
2741
+ ```
2742
+
2743
+ ```text
2744
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"context recall","status":200,"items":[],"unresolvedGaps":["no matching context records"]}
2745
+ ```
2746
+
2747
+ ### `kxm context state`
2748
+
2749
+ ```text
2750
+ kxm context state <project> <key> [--as-of <iso>]
2751
+ ```
2752
+
2753
+ Current or historical value for one state key.
2754
+
2755
+ | Option | Argument | Default | Description |
2756
+ |---|---|---|---|
2757
+ | `--as-of` | `<iso>` | now | Historical timestamp query |
2758
+
2759
+ - Reads only. Output keys: `status`, `state` (`null` when unset), `key`.
2760
+
2761
+ ```bash
2762
+ KXM_AUTH_TOKEN="$ADMIN_TOKEN" kxm context state proj release.freeze --json
2763
+ ```
2764
+
2765
+ ```text
2766
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"context state","status":200,"state":null,"key":"release.freeze"}
2767
+ ```
2768
+
2769
+ ### `kxm context episode`
2770
+
2771
+ ```text
2772
+ kxm context episode <project> [--run <runId>]
2773
+ ```
2774
+
2775
+ Episodic learning records from workflow journals.
2776
+
2777
+ | Option | Argument | Default | Description |
2778
+ |---|---|---|---|
2779
+ | `--run` | `<runId>` | all runs | Limit to one workflow run |
2780
+
2781
+ - Reads only. Output keys: `status`, `episodes`.
2782
+
2783
+ ```bash
2784
+ KXM_AUTH_TOKEN="$ADMIN_TOKEN" kxm context episode proj --json
2785
+ ```
2786
+
2787
+ ```text
2788
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"context episode","status":200,"episodes":[]}
2789
+ ```
2790
+
2791
+ ### `kxm context promote`
2792
+
2793
+ ```text
2794
+ kxm context promote <project> <proposalId> --evidence <refs>
2795
+ ```
2796
+
2797
+ Promote an approved state proposal (control plane).
2798
+
2799
+ | Option | Argument | Default | Description |
2800
+ |---|---|---|---|
2801
+ | `--evidence` | `<refs>` | required | Comma-separated durable evidence references |
2802
+
2803
+ - `--evidence` must contain at least one reference (exit 2).
2804
+ - Mutates hub state. `--dry-run` sends nothing and plans the request (`POST <hub>/v1/context/state/promote`).
2805
+ - Errors from the hub include `state_proposal_not_found` (404).
2806
+
2807
+ ```bash
2808
+ kxm context promote proj prop_1 --evidence wf_1:journal:1 --dry-run
2809
+ ```
2810
+
2811
+ ```text
2812
+ dry run: promote proposal prop_1 in proj
2813
+ would request POST http://127.0.0.1:7331/v1/context/state/promote
2814
+ ```
2815
+
2816
+ ```bash
2817
+ KXM_AUTH_TOKEN="$ADMIN_TOKEN" kxm context promote proj prop_missing --evidence wf_1:journal:1 --json
2818
+ ```
2819
+
2820
+ ```text
2821
+ {"schema":"kxm.cli-result.v1","ok":false,"command":"context promote","status":404,"error":"state proposal prop_missing not found","code":"state_proposal_not_found",...}
2822
+ ```
2823
+
2824
+ ### `kxm context explain`
2825
+
2826
+ ```text
2827
+ kxm context explain <project> <itemId>
2828
+ ```
2829
+
2830
+ Explain which evidence and lineage back a context item.
2831
+
2832
+ - Arguments: `<project>` Project scope; `<itemId>` Context item ID. No command-specific options. Reads only.
2833
+ - Output keys: `status`, `found`, `lineage`, `evidenceRefs`, `sources`.
2834
+
2835
+ ```bash
2836
+ KXM_AUTH_TOKEN="$ADMIN_TOKEN" kxm context explain proj ctx_missing --json
2837
+ ```
2838
+
2839
+ ```text
2840
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"context explain","status":200,"found":false,"lineage":[],"evidenceRefs":[],"sources":[]}
2841
+ ```
2842
+
2843
+ ### `kxm context wiki-compile`
2844
+
2845
+ ```text
2846
+ kxm context wiki-compile <project> [--out <dir>]
2847
+ ```
2848
+
2849
+ Compile the Karpathy-style knowledge wiki for review. Without `--out` the pages are listed but not written.
2850
+
2851
+ | Option | Argument | Default | Description |
2852
+ |---|---|---|---|
2853
+ | `--out` | `<dir>` | dry-run output only | Workspace root to write .kxm/knowledge/wiki into (default: dry-run output only) |
2854
+
2855
+ - With `--out`, writes the pages under `<dir>/.kxm/knowledge/wiki/`. With `--out` and `--dry-run`, the hub still compiles (a read) and the pages are listed as `planned` writes; nothing is written.
2856
+ - Output keys: `project`, `pages`, `openContradictions`, and `written` and `outDir` or `dryRun: true` (plus `outDir` and `planned` for `--out --dry-run`).
2857
+
2858
+ ```bash
2859
+ KXM_AUTH_TOKEN="$ADMIN_TOKEN" kxm context wiki-compile proj --json
2860
+ ```
2861
+
2862
+ ```text
2863
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"context wiki-compile","project":"proj","pages":[".kxm/knowledge/wiki/architecture/proj-state.md",".kxm/knowledge/wiki/contradictions/proj.md",".kxm/knowledge/wiki/index.md"],"openContradictions":0,"dryRun":true}
2864
+ ```
2865
+
2866
+ ### `kxm context wiki-lint`
2867
+
2868
+ ```text
2869
+ kxm context wiki-lint <project>
2870
+ ```
2871
+
2872
+ Lint a compiled wiki for broken refs, orphans, and stale state. Compiles on the hub and reports the lint findings without writing files.
2873
+
2874
+ - Reads only. Output keys: `project`, `issues` (`severity`, `rule`, `path`, `message`), `audit`.
2875
+ - Exit 1 when any issue has severity `error`.
2876
+
2877
+ ```bash
2878
+ KXM_AUTH_TOKEN="$ADMIN_TOKEN" kxm context wiki-lint proj
2879
+ ```
2880
+
2881
+ Captured as JSON:
2882
+
2883
+ ```text
2884
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"context wiki-lint","project":"proj","issues":[],"audit":{"project":"proj","pages":[...],"stateItems":0,"contextItems":0,"contradictions":0,"compiledAt":"2026-09-23T13:53:26.436Z"}}
2885
+ ```
2886
+
2887
+ ## `kxm memory`
2888
+
2889
+ Harness-agnostic Git memory. Active facts are Markdown files with `kxm.memory.v1` front matter in `.kxm/memory/`; candidates live in `.kxm/memory/candidates/` and become facts when a reviewed PR moves them. All paths are relative to the current directory. No hub needed.
2890
+
2891
+ ### `kxm memory brief`
2892
+
2893
+ ```text
2894
+ kxm memory brief
2895
+ ```
2896
+
2897
+ Show active project memory facts for harness context.
2898
+
2899
+ No command-specific options.
2900
+
2901
+ - Reads only. JSON keys: `brief` (`schema`, `generatedAt`, `count`, `facts`).
2902
+
2903
+ ```bash
2904
+ kxm memory brief
2905
+ ```
2906
+
2907
+ ```text
2908
+ No active project memory facts.
2909
+ ```
2910
+
2911
+ ```bash
2912
+ kxm memory brief --json
2913
+ ```
2914
+
2915
+ ```text
2916
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"memory brief","brief":{"schema":"kxm.memory-brief.v1","generatedAt":"2026-09-23T13:52:20.033Z","count":0,"facts":[]}}
2917
+ ```
2918
+
2919
+ ### `kxm memory note`
2920
+
2921
+ ```text
2922
+ kxm memory note <fact> [--scope <scope>] [--kind <kind>] [--body <text>]
2923
+ ```
2924
+
2925
+ Record an evidence-based memory candidate (promoted via PR).
2926
+
2927
+ | Option | Argument | Default | Description |
2928
+ |---|---|---|---|
2929
+ | `--scope` | `<scope>` | `project` | Scope: agent, project, run, or operator (default: project) |
2930
+ | `--kind` | `<kind>` | `learning` | Kind: decision, architecture, convention, policy, learning (default: learning) |
2931
+ | `--body` | `<text>` | none | Detailed markdown context for the fact |
2932
+
2933
+ - Arguments: `<fact>`, Summary of the observed fact or learning.
2934
+ - Writes `.kxm/memory/candidates/<id>.md`. `--dry-run` shows the candidate and plans the write; the ID is assigned when the candidate is recorded.
2935
+ - JSON keys: `candidate`, `path`.
2936
+
2937
+ ```bash
2938
+ kxm memory note "Use pnpm, not npm" --kind convention --dry-run --json
2939
+ ```
2940
+
2941
+ ```text
2942
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"memory note","candidate":{"schema":"kxm.memory.v1","id":"cand_project_bf337cc76dda","scope":"project","kind":"convention","summary":"Use pnpm, not npm",...,"lifecycle":"active","evidenceRefs":[]},"path":".kxm/memory/candidates/cand_project_bf337cc76dda.md","dryRun":true,"planned":[{"action":"write","target":"/work/proj/.kxm/memory/candidates/cand_project_bf337cc76dda.md"}]}
2943
+ ```
2944
+
2945
+ ### `kxm memory sync`
2946
+
2947
+ ```text
2948
+ kxm memory sync
2949
+ ```
2950
+
2951
+ Regenerate memory projection blocks across AGENTS.md, CLAUDE.md, and GEMINI.md: rewrites the section between `<!-- kxm:memory:start -->` and `<!-- kxm:memory:end -->` in each file from the active facts.
2952
+
2953
+ No command-specific options.
2954
+
2955
+ - Writes up to three files in the current directory. `--dry-run` reports which files it would update or create and plans the writes without making them.
2956
+ - Warning: a missing `CLAUDE.md` or `GEMINI.md` is created with a header copied from the KXM repository's own agent instructions (planner role, Grok as default writer, links to `plans/implementation-plan.md`). Review or replace the header before committing in another project.
2957
+ - JSON keys: `updated`, `created`.
2958
+
2959
+ In a project without these files:
2960
+
2961
+ ```bash
2962
+ kxm memory sync --dry-run --json
2963
+ ```
2964
+
2965
+ ```text
2966
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"memory sync","updated":[],"created":["AGENTS.md","CLAUDE.md","GEMINI.md"],"dryRun":true,"planned":[{"action":"write","target":"/work/proj/AGENTS.md"},{"action":"write","target":"/work/proj/CLAUDE.md"},{"action":"write","target":"/work/proj/GEMINI.md"}]}
2967
+ ```
2968
+
2969
+ ## `kxm skills`
2970
+
2971
+ Governed skill candidate lifecycle under `.kxm/skills/` in `KXM_WORKDIR` or the current directory: `candidates/`, `promoted/`, `quarantined/`, `rejected/`, `history/<id>.jsonl`, and `patches/<id>.patch`. No hub needed. Errors are plain text on stderr.
2972
+
2973
+ ### `kxm skills create`
2974
+
2975
+ ```text
2976
+ kxm skills create --file <path> --name <name> --created-by <id> --harness <name> --models <models> [--description <text>] [--run <ids>] [--journal <ids>] [--receipt <refs>] [--supersedes <id>]
2977
+ ```
2978
+
2979
+ Submit a skill candidate from verified episodes.
2980
+
2981
+ | Option | Argument | Default | Description |
2982
+ |---|---|---|---|
2983
+ | `--file` | `<path>` | required | SKILL.md content file |
2984
+ | `--name` | `<name>` | required | Skill name |
2985
+ | `--description` | `<text>` | empty | Short description |
2986
+ | `--created-by` | `<id>` | required | Author identity |
2987
+ | `--run` | `<ids>` | none | Comma-separated source run IDs |
2988
+ | `--journal` | `<ids>` | none | Comma-separated source journal entry IDs |
2989
+ | `--receipt` | `<refs>` | none | Comma-separated evidence receipts |
2990
+ | `--harness` | `<name>` | required | Harness compatibility (pi, claude-code, ...) |
2991
+ | `--models` | `<models>` | required | Comma-separated compatible models |
2992
+ | `--supersedes` | `<id>` | none | Prior skill this candidate supersedes |
2993
+
2994
+ - Writes a candidate. Honors `--dry-run`. JSON keys: `metadata` (or `name` for a dry run).
2995
+
2996
+ ```bash
2997
+ kxm skills create --file SKILL.md --name retry-backoff --created-by alice --harness pi --models xai/grok-4.6 --run wf_123 --dry-run --json
2998
+ ```
2999
+
3000
+ ```text
3001
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"skills create","dryRun":true,"name":"retry-backoff"}
3002
+ ```
3003
+
3004
+ ### `kxm skills evaluate`
3005
+
3006
+ ```text
3007
+ kxm skills evaluate <skillId> --kind <kind> --evaluator <version> [--fail] [--score <n>] [--details <text>]
3008
+ ```
3009
+
3010
+ Record a protected evaluation for a candidate. A failed evaluation can quarantine the candidate.
3011
+
3012
+ | Option | Argument | Default | Description |
3013
+ |---|---|---|---|
3014
+ | `--kind` | `<kind>` | required | static-review, sandbox, functional, safety, or optimization |
3015
+ | `--evaluator` | `<version>` | required | Evaluator version |
3016
+ | `--fail` | none | off | Record a failed evaluation |
3017
+ | `--score` | `<n>` | none | Numeric score |
3018
+ | `--details` | `<text>` | none | Bounded evaluation details |
3019
+
3020
+ - Arguments: `<skillId>`, Skill candidate ID. Mutates. `--dry-run` returns the evaluation and whether it would quarantine the candidate, and plans the history write (and the move to `quarantined/`) without making them.
3021
+ - JSON keys: `skillId`, `quarantined`, `evaluation`.
3022
+
3023
+ ```bash
3024
+ kxm skills evaluate skill_x --kind static-review --evaluator v1 --json
3025
+ ```
3026
+
3027
+ ```text
3028
+ skills evaluate failed: skill skill_x not found in candidate
3029
+ ```
3030
+
3031
+ ### `kxm skills promote`
3032
+
3033
+ ```text
3034
+ kxm skills promote <skillId> --decided-by <id> --evidence <refs> [--reason <text>]
3035
+ ```
3036
+
3037
+ Promote a candidate that passed all protected evaluations (`static-review`, `sandbox`, `functional`, `safety`). The promoter must differ from the author. Writes the promoted skill and a patch that adds `.kxm/skills/promoted/<id>/SKILL.md` for review.
3038
+
3039
+ | Option | Argument | Default | Description |
3040
+ |---|---|---|---|
3041
+ | `--decided-by` | `<id>` | required | Promoter identity (must differ from the author) |
3042
+ | `--evidence` | `<refs>` | required | Comma-separated durable evidence references |
3043
+ | `--reason` | `<text>` | `passed protected evaluation` | Decision reason |
3044
+
3045
+ - Mutates. `--dry-run` checks the evaluations and the promoter as a real promotion does, returns the metadata with the patch it would write, and plans the promoted files, the patch, and the history entry without writing them. JSON keys: `skillId`, `metadata`, `patchPath`.
3046
+
3047
+ ```bash
3048
+ kxm skills promote seed-skill.af5155e66be7 --decided-by promoter --evidence receipt:seed --dry-run
3049
+ ```
3050
+
3051
+ ```text
3052
+ dry run: promote skill seed-skill.af5155e66be7
3053
+ would write /work/proj/.kxm/skills/promoted/seed-skill.af5155e66be7/SKILL.md
3054
+ would write /work/proj/.kxm/skills/promoted/seed-skill.af5155e66be7/metadata.json
3055
+ would write /work/proj/.kxm/skills/patches/seed-skill.af5155e66be7.patch
3056
+ would write /work/proj/.kxm/skills/history/seed-skill.af5155e66be7.jsonl
3057
+ ```
3058
+
3059
+ ```bash
3060
+ kxm skills promote skill_x --decided-by bob --evidence wf_123:journal:4
3061
+ ```
3062
+
3063
+ Not run: needs a candidate that passed every required evaluation.
3064
+
3065
+ ### `kxm skills reject`
3066
+
3067
+ ```text
3068
+ kxm skills reject <skillId> --decided-by <id> [--reason <text>]
3069
+ ```
3070
+
3071
+ Reject a candidate; history is retained for learning.
3072
+
3073
+ | Option | Argument | Default | Description |
3074
+ |---|---|---|---|
3075
+ | `--decided-by` | `<id>` | required | Decider identity |
3076
+ | `--reason` | `<text>` | `rejected` | Decision reason |
3077
+
3078
+ - Mutates. `--dry-run` plans the move to `rejected/` and the history entry without making them. JSON keys: `skillId`, `metadata`.
3079
+
3080
+ ```bash
3081
+ kxm skills reject skill_x --decided-by bob --reason "duplicates an existing skill"
3082
+ ```
3083
+
3084
+ Not run: needs an existing candidate.
3085
+
3086
+ ### `kxm skills list`
3087
+
3088
+ ```text
3089
+ kxm skills list [--state <state>]
3090
+ ```
3091
+
3092
+ List skills by state.
3093
+
3094
+ | Option | Argument | Default | Description |
3095
+ |---|---|---|---|
3096
+ | `--state` | `<state>` | `promoted` | candidate, promoted, quarantined, or rejected |
3097
+
3098
+ - Reads only. JSON keys: `state`, `skills` (`id`, `name`, `version`, `createdBy`, `createdAt`, `models`). An invalid state exits 1.
3099
+
3100
+ ```bash
3101
+ kxm skills list --state candidate
3102
+ ```
3103
+
3104
+ ```text
3105
+ 0 candidate skill(s)
3106
+ ```
3107
+
3108
+ ### `kxm skills verify`
3109
+
3110
+ ```text
3111
+ kxm skills verify <skillId> [--state <state>]
3112
+ ```
3113
+
3114
+ Verify a stored skill against its pinned content hash.
3115
+
3116
+ | Option | Argument | Default | Description |
3117
+ |---|---|---|---|
3118
+ | `--state` | `<state>` | `promoted` | candidate, promoted, quarantined, or rejected |
3119
+
3120
+ - Reads only. JSON keys: `skillId`, `state`, `contentSha256`. A missing skill or a hash mismatch exits 1.
3121
+
3122
+ ```bash
3123
+ kxm skills verify skill_x --json
3124
+ ```
3125
+
3126
+ ```text
3127
+ skills verify failed: skill skill_x not found in promoted
3128
+ ```
3129
+
3130
+ ## `kxm improve`
3131
+
3132
+ Proposes coded-repeat candidates from this project's Runtime routing records and telemetry. `kxm improve` with no subcommand runs `improve report`.
3133
+
3134
+ ### `kxm improve report`
3135
+
3136
+ ```text
3137
+ kxm improve report [--file <path>] [--out-dir <path>]
3138
+ ```
3139
+
3140
+ Generate the improvement report and candidates from routing records. Inside a KXM project (the current directory's Git root holds `.kxm/project.yaml`) it reads the project's Runtime event store, then `telemetry.jsonl` in the workspace logs directory; `--file` reads only the named file. Routing records (`kxm.routing-record.v1` and `v2`, bare or nested under `routing`, `envelope.routing`, or a `routing.attempt.recorded` event) are grouped by workflow, step, agent role, and ask. Groups that qualify become proposed candidates, each written as a `.diff` and a `.json` file. Nothing is applied.
3141
+
3142
+ | Option | Argument | Default | Description |
3143
+ |---|---|---|---|
3144
+ | `--file` | `<path>` | the Runtime store, then `.kxm/logs/telemetry.jsonl` | Read only this routing-record JSONL instead of the project's Runtime store and telemetry |
3145
+ | `--out-dir` | `<path>` | `<project>/.kxm/candidates` | Directory for candidates (default .kxm/candidates) |
3146
+
3147
+ - The Runtime store is `<state root>/runtime/projects/<key>/run-events.db`, with the key derived from the checkout's real path, so each checkout and worktree reads only its own. It is opened read-only for one query over its events table and is never created, written, or migrated. Outside a KXM project only telemetry is read, and the text output says `Runtime store not read`.
3148
+ - A telemetry record whose `attemptId` the store already supplied is dropped (`duplicatesDropped`). Attempts from simulated drives are excluded (`excludedSimulated`). Each Runtime attempt's outcome is resolved from the event log and never written back: `accepted` when the run completed and the step was not re-entered, `reworked` when the step was entered again, `failed` when the run failed, undecided (`undecided`) when the run was cancelled or is still running.
3149
+ - A group becomes a candidate only when the same objective was decided in at least 2 runs, at least 0.75 of its decided records were accepted, and its step writes no repository. A group that passes but misses shows `no (writes-repository)` or `no (ask-not-repeated)`. `Weighted` is the recency-weighted record count (`improvement.telemetryHalfLifeDays`); it orders rows and never decides candidacy.
3150
+ - Reads `improvement.*` from the project and user configuration (see [`kxm.config.v1`](config-reference.md#personalization-settings-kxmconfigv1)) and reports each candidate's promotion readiness under `improvement.promotionPolicy`. Readiness never authorizes anything. Under `critic_quorum` every candidate reports not ready, because this command cites no critic receipts. A configuration that cannot be loaded exits 1 with `config_invalid`.
3151
+ - A Runtime store that exists but cannot be read exits 1 with `improve_source_unreadable`; `detail` names the path.
3152
+ - Writes candidate files under `--out-dir` (relative to the current directory) and the report at `.kxm/assets/improvements/<timestamp>.json`. Honors `--dry-run` (writes neither). No hub needed.
3153
+ - JSON keys: `path`, `events`, `recordsCount`, `groupsCount`, `candidatesCount`, `candidates`, `report` (`schema`, `createdAt`, `reviewDecision`, `recordsCount`, `groups`, `candidates`, `promotionPolicy`, `promotion`), `sources`, `projectRoot` (`null` outside a project).
3154
+ - Each `sources` entry has `kind` (`engine`, `telemetry`, or `file`), `path`, `exists`, `records`, and `duplicatesDropped`; the `engine` entry also has `skippedInvalid`, `excludedSimulated`, and `undecided`.
3155
+ - Each `report.groups` row has `workflowHash`, `workflowId` (Runtime records), `stepId`, `agentRole`, `promptHash`, `recurrence`, `distinctRuns`, `askRecurrence`, `undecidedRecords`, `meanCost`, `meanLatency`, `verifyPassRate` (accepted share of decided records), `rework`, `weightedRecurrence`, `undatedRecords`, `costSamples`, `writesRepository`, `evidenceRefs`, `isCandidate`, and, when they apply, `excludedReason`, `candidateKind`, and `candidateId`.
3156
+ - Each `report.promotion` entry has `candidateId`, `policy`, `readyForReview`, and `reason`.
3157
+
3158
+ With no routing records yet, from the project root:
3159
+
3160
+ ```bash
3161
+ kxm improve --dry-run
3162
+ ```
3163
+
3164
+ ```text
3165
+ Sources:
3166
+ engine /home/me/.local/state/kxm/runtime/projects/605a33e70739a298f89939ca/run-events.db (exists=false, records=0, skippedInvalid=0, excludedSimulated=0, undecided=0, duplicatesDropped=0)
3167
+ telemetry /work/proj/.kxm/logs/telemetry.jsonl (exists=false, records=0, duplicatesDropped=0)
3168
+ Project root: /work/proj
3169
+
3170
+ Improvement Report (0 record(s), 0 group(s), 0 candidate(s); promotion policy manual_pr)
3171
+
3172
+ Workflow Step Role Prompt Records Runs Asks Weighted Cost ($) Latency (ms) Accepted Rework Candidate
3173
+ ------------------------------------------------------------------------------------------------------------------------------------------
3174
+ ```
3175
+
3176
+ ```bash
3177
+ kxm improve report --dry-run --json
3178
+ ```
3179
+
3180
+ ```text
3181
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"improve","dryRun":true,"path":"/work/proj/.kxm/assets/improvements/2026-09-23T16-42-18-170Z.json","events":0,"recordsCount":0,"groupsCount":0,"candidatesCount":0,"candidates":[],"report":{"schema":"kxm.improvement-report.v2","createdAt":"2026-09-23T16:42:18.170Z","reviewDecision":"proposed","recordsCount":0,"groups":[],"candidates":[],"promotionPolicy":"manual_pr","promotion":[]},"sources":[{"kind":"engine","path":"/home/me/.local/state/kxm/runtime/projects/605a33e70739a298f89939ca/run-events.db","exists":false,"records":0,"skippedInvalid":0,"excludedSimulated":0,"undecided":0,"duplicatesDropped":0},{"kind":"telemetry","path":"/work/proj/.kxm/logs/telemetry.jsonl","exists":false,"records":0,"duplicatesDropped":0}],"projectRoot":"/work/proj"}
3182
+ ```
3183
+
3184
+ When the Runtime store exists but is not a readable database:
3185
+
3186
+ ```bash
3187
+ kxm improve report --json
3188
+ ```
3189
+
3190
+ ```text
3191
+ {"schema":"kxm.cli-result.v1","ok":false,"command":"improve","error":"improve_source_unreadable","detail":"/home/me/.local/state/kxm/runtime/projects/605a33e70739a298f89939ca/run-events.db: file is not a database"}
3192
+ ```
3193
+
3194
+ ## `kxm routing`
3195
+
3196
+ Model and harness routing telemetry. No hub needed.
3197
+
3198
+ ### `kxm routing report`
3199
+
3200
+ ```text
3201
+ kxm routing report [-f <path>] [-l] [--prices <path>]
3202
+ ```
3203
+
3204
+ Compare verified completion, cost, and rework per behavioral configuration. The table ranks routes quality-first with columns Harness, Model, Effort, Role, Att, Pass%, Rwk%, p50(ms), p95(ms), CtxTok, Metered($), $/Acc, Unm, Unk, Quota, and optionally ListEquiv($).
3205
+
3206
+ Without `--file` it reads the same sources as [`kxm improve report`](#kxm-improve-report): the current project's Runtime event store (read-only), then the workspace `telemetry.jsonl`, dropping a telemetry copy of an attempt the store already supplied and excluding simulated drives. A Runtime attempt counts toward Pass% only when the event log shows its run completed without the step being re-entered.
3207
+
3208
+ | Option | Argument | Default | Description |
3209
+ |---|---|---|---|
3210
+ | `-f`, `--file` | `<path>` | the Runtime store, then workspace telemetry | Telemetry or event log JSONL file (default: workspace telemetry) |
3211
+ | `-l`, `--equivalent-list-cost` | none | off | Include equivalent list price column using price catalog |
3212
+ | `--list-prices` | none | off | Alias for --equivalent-list-cost |
3213
+ | `--prices` | `<path>` | `.kxm/prices.yaml` | Path to price catalog (default: .kxm/prices.yaml) |
3214
+
3215
+ - Reads only. A price catalog that cannot be loaded is skipped silently.
3216
+ - The `--file` help text still says `(default: workspace telemetry)`; without `--file` the Runtime store is read first, as described above. A Runtime store that exists but cannot be read exits 1 with `improve_source_unreadable`.
3217
+ - The text output does not list the sources, and prints `no routing records in telemetry` when no source holds a record. The Rwk% column counts records with `transitions` greater than 0, which Runtime records never set.
3218
+ - JSON keys: `file` (the telemetry path, also when the Runtime store was read), `sources` (without `--file`; the same shape as in `kxm improve`), `configurations` (per behavioral hash for v1 records), `report` (`schema`, `generatedAt`, `totalAttempts`, `rows`).
3219
+
3220
+ With no routing records yet:
3221
+
3222
+ ```bash
3223
+ kxm routing report
3224
+ ```
3225
+
3226
+ ```text
3227
+ no routing records in telemetry
3228
+ ```
3229
+
3230
+ ```bash
3231
+ kxm routing report --json
3232
+ ```
3233
+
3234
+ ```text
3235
+ {"schema":"kxm.cli-result.v1","ok":true,"command":"routing report","file":"/work/proj/.kxm/logs/telemetry.jsonl","sources":[{"kind":"engine","path":"/home/me/.local/state/kxm/runtime/projects/605a33e70739a298f89939ca/run-events.db","exists":false,"records":0,"skippedInvalid":0,"excludedSimulated":0,"undecided":0,"duplicatesDropped":0},{"kind":"telemetry","path":"/work/proj/.kxm/logs/telemetry.jsonl","exists":false,"records":0,"duplicatesDropped":0}],"configurations":[],"report":{"schema":"kxm.routing-report.v1","generatedAt":"2026-09-23T16:42:18.482Z","totalAttempts":0,"rows":[]}}
3236
+ ```
3237
+
3238
+ Read an explicit file and add the list-price column (the missing catalog is skipped):
3239
+
3240
+ ```bash
3241
+ kxm routing report --file .kxm/logs/telemetry.jsonl -l --prices .kxm/prices.yaml
3242
+ ```
3243
+
3244
+ ```text
3245
+ no routing records in telemetry
3246
+ ```
3247
+
3248
+ ### `kxm routing benchmark`
3249
+
3250
+ ```text
3251
+ kxm routing benchmark [--task <fixture>] [--arms <models>] [--runs <count>]
3252
+ ```
3253
+
3254
+ Dedicated offline benchmark for side-by-side model comparison (Decision Q12). In 0.7.1 this prints fixed placeholder figures: it does not run any model, read the task, or measure anything. Latency, tokens, cost, and outcome are constants chosen from the model name. Do not use its output for routing decisions.
3255
+
3256
+ | Option | Argument | Default | Description |
3257
+ |---|---|---|---|
3258
+ | `--task` | `<fixture>` | `Deterministic benchmark task` | Task prompt or fixture path for benchmark comparison |
3259
+ | `--arms` | `<models>` | `grok/grok-4.6,claude/fable,pi/qwen3-coder-plus` | Comma-separated model routes to benchmark (e.g. grok/grok-4.6,claude/fable) |
3260
+ | `--runs` | `<count>` | `1` | Benchmark runs per arm |
3261
+
3262
+ - Reads nothing. JSON keys: `task`, `runs`, `timestamp`, `arms` (`harness`, `model`, `latencyMs`, `tokensIn`, `tokensOut`, `costUsd`, `outcome`).
3263
+
3264
+ ```bash
3265
+ kxm routing benchmark
3266
+ ```
3267
+
3268
+ ```text
3269
+ Routing Benchmark Results (task: Deterministic benchmark task, runs: 1)
3270
+ Harness Model Latency(ms) TokensIn TokensOut Cost($) Outcome
3271
+ grok grok-4.6 420 1200 450 $0.17 passed
3272
+ claude fable 680 1200 450 $0.45 passed
3273
+ pi qwen3-coder-plus 560 1200 450 $0.12 passed
3274
+ ```
3275
+
3276
+ ## `kxm backup`
3277
+
3278
+ ```text
3279
+ kxm backup [--out <dir>]
3280
+ ```
3281
+
3282
+ Creates a verified SQLite backup with a hashed `kxm.backup-manifest.v1` manifest. It discovers stores relative to the current directory: the hub store `.kxm/state/kxm.db`, and `registry.db`, `bindings.db`, and `events/*.db` under `.kxm/runtime/`. It ignores `--workspace` and `KXM_DATA_PATH`, and it does not include the Runtime supervisor's stores under the user state root.
3283
+
3284
+ | Option | Argument | Default | Description |
3285
+ |---|---|---|---|
3286
+ | `--out` | `<dir>` | `.kxm/backups/backup-<timestamp>` | Directory to write backup and manifest |
3287
+
3288
+ - Writes a copy of each store and `manifest.json`. `--dry-run` lists the stores it found and the files it would write without opening any store, so no WAL is checkpointed (JSON: `outDir`, `stores` with `storeId`, `sourcePath`, `backupFile`, plus `dryRun` and `planned`).
3289
+ - JSON keys: `backupId`, `outDir`, `manifest` (`schema`, `backupId`, `createdAt`, `projectRoot`, `stores` with `storeId`, `sourcePath`, `backupFile`, `schemaVersion`, `sha256`, `bytes`, `integrity`; `manifestSha256`).
3290
+ - Exit 1 with `backup_failed`; `issues` carry codes such as `backup_no_stores` and `database_corrupted`.
3291
+
3292
+ ```bash
3293
+ kxm backup --out ../bk
3294
+ ```
3295
+
3296
+ ```text
3297
+ Created SQLite backup with 1 store(s):
3298
+ - hub-store: /work/proj/.kxm/state/kxm.db -> kxm.db (schema v5, 110592 bytes, sha256 sha256:56c6d...)
3299
+ Manifest: /work/bk/manifest.json
3300
+ ```
3301
+
3302
+ In a project with a hub store:
3303
+
3304
+ ```bash
3305
+ kxm backup --dry-run
3306
+ ```
3307
+
3308
+ ```text
3309
+ dry run: back up 1 store(s) to /work/proj/.kxm/backups/backup-2026-09-23T17-44-51-277Z (sources are not opened, so their WAL is not checkpointed)
3310
+ would write /work/proj/.kxm/backups/backup-2026-09-23T17-44-51-277Z/kxm.db
3311
+ would write /work/proj/.kxm/backups/backup-2026-09-23T17-44-51-277Z/manifest.json
3312
+ ```
3313
+
3314
+ In a project without a hub store yet:
3315
+
3316
+ ```bash
3317
+ kxm backup --json
3318
+ ```
3319
+
3320
+ ```text
3321
+ {"schema":"kxm.cli-result.v1","ok":false,"command":"backup","error":"backup_failed","issues":[{"phase":"semantic","code":"backup_no_stores","file":"/work/proj","message":"no existing SQLite stores found to backup"}]}
3322
+ ```
3323
+
3324
+ ## `kxm restore`
3325
+
3326
+ ```text
3327
+ kxm restore <manifest>
3328
+ ```
3329
+
3330
+ Restores SQLite stores from a verified backup manifest. It checks the manifest schema, that every backup file is present, and each file's digest, then restores each store to its recorded source path (rebased onto the current directory when the manifest came from another project root).
3331
+
3332
+ - Arguments: `<manifest>`, path to `manifest.json`.
3333
+ - No command-specific options.
3334
+ - Overwrites live stores. Stop the hub and the Runtime before restoring.
3335
+ - Before overwriting anything, a restore checks every store's recorded schema version against the ceiling for that store, so a store newer than this build is refused (`runtime_schema_newer`) before the first file is replaced. `--dry-run` runs the same manifest, file, digest, and schema checks and plans each target it would overwrite (and any `-wal` or `-shm` sidecar it would delete) without touching them. Dry-run JSON keys: `backupId`, `manifestPath`, `stores` (`storeId`, `targetPath`, `schemaVersion`), `dryRun`, `planned`.
3336
+ - JSON keys: `backupId`, `manifestPath`, `restoredStores` (`storeId`, `sourcePath`, `backupFile`, `schemaVersion`, `integrity`).
3337
+ - Exit 1 with `restore_failed`; `issues` carry codes such as `runtime_path_invalid`, `restore_manifest_invalid`, `restore_file_missing`, `restore_manifest_digest_mismatch`, and `runtime_schema_newer`.
3338
+
3339
+ ```bash
3340
+ kxm restore ../bk/manifest.json --dry-run
3341
+ ```
3342
+
3343
+ ```text
3344
+ dry run: restore 1 store(s) from /work/bk/manifest.json; digests verified against the manifest
3345
+ would write /work/proj/.kxm/state/kxm.db
3346
+ ```
3347
+
3348
+ A missing manifest:
3349
+
3350
+ ```bash
3351
+ kxm restore nope.json --json
3352
+ ```
3353
+
3354
+ ```text
3355
+ {"schema":"kxm.cli-result.v1","ok":false,"command":"restore","error":"restore_failed","issues":[{"phase":"semantic","code":"runtime_path_invalid","file":"/work/proj/nope.json","message":"manifest path /work/proj/nope.json does not exist"}]}
3356
+ ```
3357
+
3358
+ ## `kxm tenant`
3359
+
3360
+ ### `kxm tenant status`
3361
+
3362
+ ```text
3363
+ kxm tenant status
3364
+ ```
3365
+
3366
+ Read hub metadata and authoritative Runtime run state as one labeled view, for machine clients such as the portal backend. It reads both sources concurrently (5 second hub deadline). The hub part covers the agent roster, message queue, plans, and the hub's own workflow runs; the Runtime part covers event-log-folded runs with their `homeRuntimeId`. It attaches to a running supervisor and never starts one, and it resolves only the administrative hub credential.
3367
+
3368
+ No command-specific options.
3369
+
3370
+ - Needs a KXM project. Reads only.
3371
+ - An unreadable source is reported as `unavailable` with a reason (`hub_unreachable`, `hub_timeout`, `hub_unauthorized`, `hub_response_invalid`, `hub_credential_unreadable`, `runtime_supervisor_not_running`), and `degraded` is `true`. Hub and Runtime runs have separate ID spaces, so a comparison with no shared ID is `unverified` (`run_identity_link_absent`), never agreement.
3372
+ - JSON (`kxm.tenant-status.v1`) keys: `project`, `generatedAt`, `hubUrl`, `bindingScope`, `hub` (`state`, `observedAt`, `value` or `reason`), `runtime`, `runComparison`, `degraded`.
3373
+ - Exit 0 when at least one source was read; 1 with `tenant_status_no_source` when neither was, or `project_required`.
3374
+
3375
+ ```bash
3376
+ kxm tenant status
3377
+ ```
3378
+
3379
+ ```text
3380
+ tenant prj_83994a28aa6b472992394a5936c6b7c2 @ http://127.0.0.1:46315 (loopback)
3381
+ hub 0/0 agents online, 0 hub runs, 0 open messages
3382
+ runtime unavailable (runtime_supervisor_not_running)
3383
+ cross-check: unavailable (runtime_runtime_supervisor_not_running)
3384
+ ```
3385
+
3386
+ ```bash
3387
+ kxm tenant status --json
3388
+ ```
3389
+
3390
+ ```text
3391
+ {"schema":"kxm.tenant-status.v1","ok":true,"command":"tenant status","project":"prj_83994a28aa6b472992394a5936c6b7c2","generatedAt":"2026-09-23T13:53:27.391Z","hubUrl":"http://127.0.0.1:46315","bindingScope":"loopback","hub":{"state":"ok",...},"runtime":{"state":"unavailable","observedAt":"2026-09-23T13:53:27.384Z","reason":"runtime_supervisor_not_running"},"runComparison":{"state":"unavailable","reason":"runtime_runtime_supervisor_not_running"},"degraded":true}
3392
+ ```
3393
+
3394
+ ## `kxm ssh`
3395
+
3396
+ Multiplexed remote SSH execution. Commands reuse an OpenSSH ControlMaster socket in `.kxm/run/ssh-sockets/` in the current directory (`ControlPersist=10m`, `BatchMode=yes`, `StrictHostKeyChecking=yes`, 120 second timeout). JSON results carry `ok`, `action`, `host`, and the fields below, but no `command` field except `ssh close`. Under `--dry-run`, `ssh run`, `ssh file`, and `ssh close` connect to nothing: they print a plan (`command`, `host`, the remote command or path, `dryRun`, and `planned` with action `ssh`) instead. `ssh info` reads only, with or without the flag.
3397
+
3398
+ ### `kxm ssh info`
3399
+
3400
+ ```text
3401
+ kxm ssh info [host]
3402
+ ```
3403
+
3404
+ Discover SSH host aliases and parameters safely without opening sockets. Without a host it lists the aliases in `~/.ssh/config`; with a host it resolves that host's effective settings with `ssh -G`.
3405
+
3406
+ - Arguments: `[host]`. No command-specific options. Reads only; no network connection.
3407
+ - JSON keys: `action`, `hosts` (`alias`, `hostName`, `user`, `port`), `durationMs`, and for one host `host`, `resolvedHost`, `user`, `port`.
3408
+
3409
+ ```bash
3410
+ kxm ssh info
3411
+ ```
3412
+
3413
+ ```text
3414
+ No configured SSH host aliases found in ~/.ssh/config
3415
+ ```
3416
+
3417
+ ```bash
3418
+ kxm ssh info --json
3419
+ ```
3420
+
3421
+ ```text
3422
+ {"schema":"kxm.cli-result.v1","ok":true,"action":"info","hosts":[],"durationMs":0}
3423
+ ```
3424
+
3425
+ ### `kxm ssh run`
3426
+
3427
+ ```text
3428
+ kxm ssh run <host> <command...> [--sudo]
3429
+ ```
3430
+
3431
+ Execute a command on a remote SSH host via multiplexed ControlMaster socket. Commands that match KXM's destructive-command patterns are refused before connecting.
3432
+
3433
+ | Option | Argument | Default | Description |
3434
+ |---|---|---|---|
3435
+ | `--sudo` | none | off | Execute remote command with sudo privileges |
3436
+
3437
+ - Connects to the remote host and runs the command. `--dry-run` connects to nothing and prints the command it would run.
3438
+ - JSON keys: `exitCode`, `stdout`, `stderr`, `truncated`, `socketReused`, `durationMs`, `error`. The exit code is the remote command's.
3439
+
3440
+ ```bash
3441
+ kxm ssh run build-01 uptime --dry-run
3442
+ ```
3443
+
3444
+ ```text
3445
+ dry run: run a command on build-01
3446
+ would ssh build-01: uptime
3447
+ ```
3448
+
3449
+ ```bash
3450
+ kxm ssh run build-01 uptime
3451
+ ```
3452
+
3453
+ Not run: connects to a remote host.
3454
+
3455
+ ### `kxm ssh file`
3456
+
3457
+ ```text
3458
+ kxm ssh file <host> <path> (--read | --content <text> [--append]) [--sudo]
3459
+ ```
3460
+
3461
+ Read or write remote files over SSH.
3462
+
3463
+ | Option | Argument | Default | Description |
3464
+ |---|---|---|---|
3465
+ | `--content` | `<text>` | empty | Content to write to remote file |
3466
+ | `--read` | none | off | Read remote file content |
3467
+ | `--append` | none | off | Append content to remote file |
3468
+ | `--sudo` | none | off | Use sudo privileges on remote file |
3469
+
3470
+ - Warning: without `--read` or `--append` the command overwrites the remote file, with empty content when `--content` is omitted.
3471
+ - Connects to the remote host; `--dry-run` connects to nothing and names the read or write it would make. JSON keys include `stdout` for reads and `bytesProcessed` for writes.
3472
+
3473
+ ```bash
3474
+ kxm ssh file build-01 /etc/hostname --read
3475
+ ```
3476
+
3477
+ Not run: connects to a remote host.
3478
+
3479
+ ### `kxm ssh close`
3480
+
3481
+ ```text
3482
+ kxm ssh close <host>
3483
+ ```
3484
+
3485
+ Close active ControlMaster socket for an SSH host (`ssh -O stop`).
3486
+
3487
+ - Always exits 0. JSON keys: `closed`, `command`, `host`.
3488
+
3489
+ ```bash
3490
+ kxm ssh close build-01
3491
+ ```
3492
+
3493
+ Not run: talks to a local SSH control socket for a real host.
3494
+
3495
+ ## `kxm help`
3496
+
3497
+ ```text
3498
+ kxm help [command]
3499
+ ```
3500
+
3501
+ Shows help for the CLI or one command, like `--help`. Every group also has a `help` subcommand (`kxm runs help status`). Exits 0.
3502
+
3503
+ ```bash
3504
+ kxm help runs
3505
+ ```
3506
+
3507
+ ```text
3508
+ Usage: kxm runs [options] [command]
3509
+
3510
+ Inspect KXM runs
3511
+ ...
3512
+ ```
3513
+
3514
+ ## Known behavior gaps in 0.7.1
3515
+
3516
+ These are behaviors of the current build that differ from what the help text or the flag names suggest. Each is also noted in the command's section.
3517
+
3518
+ - The `default` workflow that `kxm init` writes sets `limits.maxAgentTimeMs`, so `kxm runs drive` hands every run of it off with `run_handoff_required` (`limit_unsupported`). Use a `kxm workflow add --template` workflow, or remove the limit, to drive a first run.
3519
+ - `kxm memory sync` creates `CLAUDE.md` and `GEMINI.md` with headers taken from the KXM repository's own instructions.
3520
+ - `kxm routing benchmark` prints constant placeholder figures.
3521
+ - `kxm task sync` does not contact GitHub or Jira.
3522
+ - `kxm runs drive` without `--simulated` runs live harness calls, although its description says "model-free simulation".
3523
+ - `kxm peer inbox` always returns an empty list from the CLI.
3524
+ - `kxm context` subcommands crash with a stack trace when the hub is unreachable, and they ignore the persisted hub credential.
3525
+ - `kxm completion <shell>` generates a command list that includes a nonexistent `plan` command, omits `models`, `routes`, `explain`, and `ssh`, lists a nonexistent `goal get`, and omits `runs drive`, `runs receipt`, `runtime sync-retry`, and `improve report`.
3526
+ - `kxm backup` does not include the Runtime supervisor's stores under the user state root, and its JSON shows `manifestSha256` redacted.
3527
+ - `--help` after an unknown subcommand (for example `kxm hub nope --help`) prints the parent group's help and exits 0, so `--help` cannot be used to test whether a subcommand exists; compare the `Usage:` line instead.