@kontextmind/kxm 0.7.177 → 0.7.179
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +21 -0
- package/docs/architecture/index.md +2 -1
- package/docs/architecture/inventory.md +8 -6
- package/docs/contributing/decisions.md +27 -1
- package/docs/contributing/development.md +4 -2
- package/docs/contributing/test-matrix.md +1 -0
- package/docs/contributing/tui-components.md +22 -4
- package/docs/reference/cli-reference.md +63 -6
- package/docs/reference/config-reference.md +17 -3
- package/docs/reference/tools.md +6 -4
- package/package.json +2 -2
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/claude-hook.js +40 -48
- package/plugins/kxm/dist/cli.js +2118 -276
- package/plugins/kxm/dist/extension.js +11252 -1492
- package/plugins/kxm/dist/mcp-server.js +3 -2
- package/plugins/kxm/dist/runtime-supervisor.js +2 -1
- package/plugins/kxm/dist/runtime.js +2 -1
- package/plugins/kxm/dist/server.js +2 -1
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/autocomplete.ts +3 -3
- package/plugins/kxm/src/cli/plugins.ts +193 -25
- package/plugins/kxm/src/cli/system.ts +37 -0
- package/plugins/kxm/src/cli.ts +9 -2
- package/plugins/kxm/src/config-editor.ts +479 -0
- package/plugins/kxm/src/config.ts +3 -2
- package/plugins/kxm/src/extension.ts +9 -2
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/session-work.ts +36 -51
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,27 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
6
6
|
|
|
7
7
|
### Added
|
|
8
8
|
|
|
9
|
+
- **KXM configuration now has one owner-validated terminal editor.**
|
|
10
|
+
`kxm config edit --scope project|user` and OMP/Pi `/kxm config` publish the
|
|
11
|
+
same `kxm.config.v1` surface. Accepted writes are saved and re-read; rejected
|
|
12
|
+
values leave file bytes unchanged; clear removes only the selected scope's
|
|
13
|
+
override. Non-TTY and dry-run calls render one plain frame without writing.
|
|
14
|
+
The editor excludes endpoints, key references, credentials, arrays, dynamic
|
|
15
|
+
maps, roster, routes, gates, roles, and workflow definitions. Pi/OMP package
|
|
16
|
+
discovery now loads the tracked extension bundle, so the private TUI
|
|
17
|
+
workspace is bundled rather than becoming an unresolved runtime dependency.
|
|
18
|
+
- **OMP can opt into a focused KXM status band with live subscription limits.**
|
|
19
|
+
`kxm plugin install --omp --status-band` installs the shared extension and
|
|
20
|
+
selects an OMP custom layout with native active-subscription usage, metered
|
|
21
|
+
cost, context, model, mode, branch, pull request, and subagent signals. The KXM hook line
|
|
22
|
+
omits idle, zero-count, clean-ship, cost, and normal local-hub noise while
|
|
23
|
+
retaining degraded hub, active work, inbox, waiting, dirty/ahead, and update
|
|
24
|
+
signals. The work widget retains known metered cost and suppresses unknown
|
|
25
|
+
cost. The flag is explicit because it changes OMP user configuration; restart
|
|
26
|
+
OMP after applying it.
|
|
27
|
+
- Plugin installation now treats an interrupted harness process with no exit
|
|
28
|
+
status as a failure for Pi, OMP, and Claude Code instead of reporting success.
|
|
29
|
+
|
|
9
30
|
- **Hub project identity and its access key live in `.kxm/config.yaml`.**
|
|
10
31
|
`hub.local` and `hub.cloud` hold the URL, the project id to present, and a
|
|
11
32
|
key reference (`key.op` is an `op://` reference, `key.env` is an environment
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Read from `plans/kxm-roadmap/state.json` and the pages linked below.
|
|
4
4
|
|
|
5
|
-
Verified date:
|
|
5
|
+
Verified date: 2026-10-11. The roadmap review checked every page listed here
|
|
6
|
+
against the current checkout.
|
|
6
7
|
|
|
7
8
|
| Page | What it covers |
|
|
8
9
|
| --- | --- |
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
# Surfaces
|
|
2
2
|
|
|
3
|
-
Read from `plugins/kxm/src/cli.ts`, `
|
|
4
|
-
`plugins/kxm/src/
|
|
5
|
-
`plugins/kxm/src/
|
|
6
|
-
`plugins/kxm/src/
|
|
7
|
-
|
|
8
|
-
`
|
|
3
|
+
Read from `plugins/kxm/src/cli.ts`, `plugins/kxm/src/config-editor.ts`,
|
|
4
|
+
`plugins/kxm/src/extension.ts`, `scripts/kxm-hub.mjs`,
|
|
5
|
+
`plugins/kxm/src/hub.ts`, `plugins/kxm/src/runtime-supervisor.ts`,
|
|
6
|
+
`plugins/kxm/src/runtime-paths.ts`, `plugins/kxm/src/mcp-server.ts`,
|
|
7
|
+
`plugins/kxm/src/oneshot-producer.ts`, `plugins/kxm/src/studio-layout.ts`,
|
|
8
|
+
`plugins/kxm/src/cli/types.ts` (`workspaceDirs`),
|
|
9
|
+
`plugins/kxm/src/local-snapshot.ts`, and `scripts/kxm-worker.mjs`.
|
|
9
10
|
|
|
10
11
|
| Surface | Started by | Listener or path | Store it owns | Defining file |
|
|
11
12
|
| --- | --- | --- | --- | --- |
|
|
12
13
|
| `kxm` CLI | `node scripts/kxm.mjs` or the installed bin | no listener | none | `plugins/kxm/src/cli.ts` |
|
|
14
|
+
| Configuration editor | `kxm config edit --scope project\|user`; OMP/Pi `/kxm config` uses project scope | `.kxm/config.yaml` or the user `config.yaml` | the selected config file; only bounded non-secret scalar fields | `plugins/kxm/src/config-editor.ts` |
|
|
13
15
|
| Hub | `kxm hub start` via `scripts/kxm-hub.mjs` | `127.0.0.1:7331` | `.kxm/state/kxm.db` under the workspace state dir | `plugins/kxm/src/hub.ts` |
|
|
14
16
|
| Hub credentials | written when the hub starts | `hub-env.json` under the user state root | that JSON file, not SQLite | `plugins/kxm/src/hub-env.ts` |
|
|
15
17
|
| Hub cloud binding | `kxm hub bind <url> --cloud --token-env/--token-command` | no listener; writes the binding record and never stores the token | binding record; token stays in the named env or command | `plugins/kxm/src/cli/hub.ts` |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
title: "Decisions"
|
|
3
3
|
description: "Decisions of medium significance or higher made by automated KXM work in this repository. One entry per decision: the options evaluated with their weights, and why the selected option won. Recorded the turn the decision is taken."
|
|
4
4
|
audience: "agents and maintainers"
|
|
5
|
-
updated: "2026-10-
|
|
5
|
+
updated: "2026-10-11"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Decisions
|
|
@@ -172,3 +172,29 @@ Evidence: Evidence: merge-base 77beda4 predates the CLI restructure; merge-tree
|
|
|
172
172
|
- Selected: stash-apply onto a fresh lane off `origin/main`, take upstream
|
|
173
173
|
for claude-bridge, `dist`, and `package.json`, and merge the rest.
|
|
174
174
|
Evidence: Evidence: `git diff HEAD origin/main` on the stale branch showed upstream's claude-bridge fix and curl downloader already landed; the lane port keeps both those and the npm/`--all` behavior.
|
|
175
|
+
|
|
176
|
+
## 2026-10-11 — OMP subscription status integration (extension)
|
|
177
|
+
|
|
178
|
+
- Context: KXM's extension already owns a hook status line while OMP can
|
|
179
|
+
render OAuth subscription quota through its native cached `usage`
|
|
180
|
+
segment. Adding quota to KXM's line would duplicate OMP data; changing
|
|
181
|
+
the OMP layout during every plugin install would unexpectedly overwrite
|
|
182
|
+
an operator's chosen status line.
|
|
183
|
+
- Options:
|
|
184
|
+
- Run `omp usage` from the KXM extension — weight 0.1. Direct, but adds
|
|
185
|
+
process work to refreshes, duplicates OMP's cache and rendering, and
|
|
186
|
+
expands the credential-bearing boundary.
|
|
187
|
+
- Configure the native OMP segment on every install — weight 0.3.
|
|
188
|
+
Discoverable, but silently mutates unrelated user configuration.
|
|
189
|
+
- Add explicit `kxm plugin install --omp --status-band` opt-in and use
|
|
190
|
+
OMP's native `usage` segment — weight 0.9. No duplicate quota
|
|
191
|
+
implementation; configuration mutation is deliberate and verifiable.
|
|
192
|
+
- Selected: explicit opt-in. The installer snapshots the three global
|
|
193
|
+
status-line entries, writes the focused layout, verifies effective
|
|
194
|
+
values in the current working directory, restores prior values, and
|
|
195
|
+
resets entries that were previously absent or `null`.
|
|
196
|
+
- Solid recommendation: keep subscription quota in OMP's native `usage`
|
|
197
|
+
segment and KXM workflow state in the separate hook status line.
|
|
198
|
+
- Interim recommendation: use the opt-in installer until OMP exposes a
|
|
199
|
+
first-class transactional status-preset API.
|
|
200
|
+
Evidence: `plugins/kxm/src/cli/plugins.ts`, `plugins/kxm/src/session-work.ts`, and `test/core/cli.test.ts`.
|
|
@@ -170,8 +170,10 @@ see the [plugin README](../../plugins/kxm/README.md).
|
|
|
170
170
|
### Pi package
|
|
171
171
|
|
|
172
172
|
The root `package.json` carries the `pi-package` keyword and a `pi` block:
|
|
173
|
-
`pi.extensions` lists `./plugins/kxm/
|
|
174
|
-
`./plugins/kxm/skills`.
|
|
173
|
+
`pi.extensions` lists the tracked `./plugins/kxm/dist/extension.js` bundle and
|
|
174
|
+
`pi.skills` lists `./plugins/kxm/skills`. Development can still load
|
|
175
|
+
`plugins/kxm/src/extension.ts` explicitly; published installs use the bundle so
|
|
176
|
+
private workspace packages are never unresolved runtime dependencies.
|
|
175
177
|
|
|
176
178
|
### Agent Skills
|
|
177
179
|
|
|
@@ -135,6 +135,7 @@ The context suites in detail:
|
|
|
135
135
|
|---|---|
|
|
136
136
|
| `init`, `validate`, `export` and `watch` in isolated workspaces | `cli.test.ts`, `github-watch.test.ts` |
|
|
137
137
|
| Every mutating command under `--dry-run` leaves the workspace, state root and hub untouched | `cli-experience.test.ts` ("every mutating command under --dry-run leaves the workspace, state root, and hub untouched") |
|
|
138
|
+
| Owner-validated config editor: persisted writes, exact-byte refusals, scope inheritance, scalar-only publication, CLI frames, and OMP/Pi lifecycle | `config-editor.test.ts` |
|
|
138
139
|
| Hub auto-start reuses a healthy or claimed hub, persists one admin key, and fails closed on bad claims | `hub-autostart.test.ts` |
|
|
139
140
|
| Hub-local session brief from SQLite without message bodies; `hub bind` reports on, off or unknown | `session-work.test.ts`, `cli.test.ts` |
|
|
140
141
|
| Session manifests, fail-closed rosters, worker and result envelopes, hub-owned envelope fields | `session.test.ts`, `cli.test.ts`, `envelope.test.ts`, `envelope-contract.test.ts` |
|
|
@@ -8,10 +8,10 @@ The kit is the workspace package
|
|
|
8
8
|
[`packages/core/tui`](../../packages/core/tui) (`@kontextmind/tui`), also
|
|
9
9
|
published on the product as `@kontextmind/kxm/tui`. It is one declarative
|
|
10
10
|
surface model, one renderer, one input decoder, one contribution registry, and
|
|
11
|
-
thin host adapters.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
convention
|
|
11
|
+
thin host adapters. `kxm config edit --scope project|user` and OMP/Pi
|
|
12
|
+
`/kxm config` ship the first production panel on this kit. `kxm dash` uses its
|
|
13
|
+
ANSI theme but keeps its existing hub and SSE loop. See
|
|
14
|
+
[Packages and workspaces](packages.md) for the layout convention the kit follows.
|
|
15
15
|
|
|
16
16
|
## Package layout
|
|
17
17
|
|
|
@@ -99,6 +99,24 @@ The reducer is pure
|
|
|
99
99
|
effects; the component layer runs them. That is the same contract that keeps
|
|
100
100
|
`kxm dash` navigation testable, and it is why no key handling needs a terminal.
|
|
101
101
|
|
|
102
|
+
## Shipped configuration editor
|
|
103
|
+
|
|
104
|
+
The `kxm/config` contribution in
|
|
105
|
+
[`config-editor.ts`](../../plugins/kxm/src/config-editor.ts) owns the supported
|
|
106
|
+
`kxm.config.v1` fields. Both `kxm config edit` and `/kxm config` register that
|
|
107
|
+
same contribution; the CLI uses the standalone adapter, while the OMP/Pi
|
|
108
|
+
extension uses the generic Pi custom panel.
|
|
109
|
+
|
|
110
|
+
Project scope shows built-in, user, then project values. User scope deliberately
|
|
111
|
+
shows only built-in and user values, so a project override cannot make a
|
|
112
|
+
successful user edit look unchanged. Clear deletes the selected scope's key;
|
|
113
|
+
the registry then re-reads the inherited value from disk.
|
|
114
|
+
|
|
115
|
+
The descriptor exposes only bounded scalar preferences. It does not expose hub
|
|
116
|
+
URLs, key references, credentials, arrays, dynamic maps, roster, routes, gates,
|
|
117
|
+
roles, or workflow definitions. Unknown section/field pairs and invalid values
|
|
118
|
+
are rejected before the config writer runs.
|
|
119
|
+
|
|
102
120
|
## Adding a surface
|
|
103
121
|
|
|
104
122
|
1. Write the descriptor table. Each row is one setting: id, key path, label,
|
|
@@ -13,7 +13,7 @@ Output shown under examples was captured from a source checkout, inside a throwa
|
|
|
13
13
|
- [Task to command](#task-to-command)
|
|
14
14
|
- Setup: [`init`](#kxm-init), [`config`](#kxm-config), [`completion`](#kxm-completion), [`trust`](#kxm-trust)
|
|
15
15
|
- Hub and sessions: [`hub`](#kxm-hub-view), [`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)
|
|
16
|
+
- Harnesses, models, and roles: [`harness`](#kxm-harness), [`plugin`](#kxm-plugin), [`auth`](#kxm-auth), [`update`](#kxm-update), [`models`](#kxm-models), [`routes`](#kxm-routes), [`role`](#kxm-role)
|
|
17
17
|
- Running work: [`lane`](#kxm-lane), [`land`](#kxm-land), [`assign`](#kxm-assign), [`run`](#kxm-run), [`runs`](#kxm-runs), [`docs`](#kxm-docs), [`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
18
|
- Context and learning: [`context`](#kxm-context), [`memory`](#kxm-memory), [`skills`](#kxm-skills), [`improve`](#kxm-improve), [`routing`](#kxm-routing), [`prices`](#kxm-prices)
|
|
19
19
|
- Operations: [`backup`](#kxm-backup), [`restore`](#kxm-restore), [`tenant`](#kxm-tenant), [`ssh`](#kxm-ssh), [`help`](#kxm-help)
|
|
@@ -101,7 +101,7 @@ kxm -V
|
|
|
101
101
|
`--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.
|
|
102
102
|
|
|
103
103
|
- Plan with `planned`: `backup`, `restore`, `config set`, `role add|remove|modify|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).
|
|
104
|
-
- Plan in their own shape (described in each section): `init`, `run`, `runs drive|cancel`, `docs build|serve`, `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 `
|
|
104
|
+
- Plan in their own shape (described in each section): `init`, `run`, `runs drive|cancel`, `docs build|serve`, `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`, `improve report`, and `config edit`. `config edit --dry-run` prints one plain frame and writes nothing. `gate github watch --dry-run` still polls GitHub but does not post the signal.
|
|
105
105
|
- 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.
|
|
106
106
|
- Refused: `kxm models` (the interactive screen) and `kxm prices acknowledge` exit 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.
|
|
107
107
|
|
|
@@ -339,6 +339,34 @@ Stop harness extensions from starting a hub (Not run):
|
|
|
339
339
|
kxm config set hub.autoStart off --scope user
|
|
340
340
|
```
|
|
341
341
|
|
|
342
|
+
### `kxm config edit`
|
|
343
|
+
|
|
344
|
+
```text
|
|
345
|
+
kxm config edit --scope project|user
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Opens the terminal editor for one fixed list of scalar `kxm.config.v1` preferences. The same editor is `/kxm config` inside the OMP/Pi TUI, and that command mounts only when the session mode is `tui`.
|
|
349
|
+
|
|
350
|
+
| Option | Argument | Default | Description |
|
|
351
|
+
|---|---|---|---|
|
|
352
|
+
| `--scope` | `project` or `user` | none | Which file a write or clear changes. Required |
|
|
353
|
+
|
|
354
|
+
- `--scope project` writes `.kxm/config.yaml` and shows built-in, then user, then project. `--scope user` writes `$KXM_USER_CONFIG_DIR/config.yaml` (default `~/.config/kxm/config.yaml`) and shows built-in, then user. The project file does not mask the user view.
|
|
355
|
+
- A write checks the section and field together, then checks the value. A refusal leaves that file's bytes unchanged. Clear deletes only that scope's key.
|
|
356
|
+
- The editor accepts workflow and harness ids that contain `@`. Whitespace and control characters are refused.
|
|
357
|
+
- A stored scalar whose runtime type does not match the field is shown with `stored type does not match this field`. The text `true` is not a boolean.
|
|
358
|
+
- It does not edit hub URLs, key references, credentials, arrays, dynamic maps, roster, routes, gates, roles, or workflow YAML. `kxm config get`, `set`, and `list` are unchanged and still accept keys this editor does not.
|
|
359
|
+
- No TTY, and `--dry-run`, each print one plain frame and write nothing. `--json`, or a scope other than `project` or `user`, exits 2 and writes nothing.
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
kxm config edit --scope project --dry-run
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
```text
|
|
366
|
+
KXM configuration
|
|
367
|
+
...
|
|
368
|
+
```
|
|
369
|
+
|
|
342
370
|
## `kxm completion`
|
|
343
371
|
|
|
344
372
|
```text
|
|
@@ -671,13 +699,14 @@ Prints recent tasks (workflow runs) and plans (journal entries) from the local h
|
|
|
671
699
|
- 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.
|
|
672
700
|
- The default text output ends with the session token, and `--token` prints only the token. Treat the output as a credential.
|
|
673
701
|
- JSON (`kxm.session-brief.v1`) keys: `generatedAt`, `staleSeconds`, `source`, `hub` (`state`, `evidence`, `online`, `url`, `scope`), `stats`, `tasks`, `plans`, `statusLine`, `widgetLines`, `sessionToken`.
|
|
702
|
+
- The status line omits cost because harness-native bands already own that signal. The work widget includes known metered cost and suppresses unknown cost.
|
|
674
703
|
|
|
675
704
|
```bash
|
|
676
705
|
kxm session brief
|
|
677
706
|
```
|
|
678
707
|
|
|
679
708
|
```text
|
|
680
|
-
kxm
|
|
709
|
+
kxm · dirty
|
|
681
710
|
|
|
682
711
|
No recent tasks or plans.
|
|
683
712
|
|
|
@@ -689,7 +718,7 @@ kxm session brief --status
|
|
|
689
718
|
```
|
|
690
719
|
|
|
691
720
|
```text
|
|
692
|
-
kxm
|
|
721
|
+
kxm
|
|
693
722
|
```
|
|
694
723
|
|
|
695
724
|
```bash
|
|
@@ -697,7 +726,7 @@ kxm session brief --json
|
|
|
697
726
|
```
|
|
698
727
|
|
|
699
728
|
```text
|
|
700
|
-
{"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
|
|
729
|
+
{"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",...,"sessionToken":"<token>"}
|
|
701
730
|
```
|
|
702
731
|
|
|
703
732
|
Before the first token exists:
|
|
@@ -707,7 +736,7 @@ kxm session brief --status --dry-run
|
|
|
707
736
|
```
|
|
708
737
|
|
|
709
738
|
```text
|
|
710
|
-
kxm
|
|
739
|
+
kxm · dirty
|
|
711
740
|
dry run: would write ~/.config/kxm/session.token
|
|
712
741
|
dry run: would write /work/proj/.kxm/state/session-brief.json
|
|
713
742
|
```
|
|
@@ -924,6 +953,34 @@ kxm harness list --json
|
|
|
924
953
|
{"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":[]},...]}
|
|
925
954
|
```
|
|
926
955
|
|
|
956
|
+
## `kxm plugin`
|
|
957
|
+
|
|
958
|
+
### `kxm plugin install`
|
|
959
|
+
|
|
960
|
+
```text
|
|
961
|
+
kxm plugin install [--all | --pi | --omp | --claude] [--status-band]
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
Installs the KXM extension into every discovered supported harness, or only the explicitly selected harnesses. `--status-band` is an explicit OMP-only opt-in: after installing the extension, it switches OMP to a focused custom status layout with model, mode, branch, pull request, subagent, active-subscription usage, metered cost, and context signals. It uses OMP's native background usage fetch and does not call `omp usage`. To support safe rollback, KXM parses OMP's global config file and retains only the three existing `statusLine` entries for the duration of the command; it does not use credential values.
|
|
965
|
+
|
|
966
|
+
| Option | Argument | Default | Description |
|
|
967
|
+
|---|---|---|---|
|
|
968
|
+
| `--all` | none | on when no harness flag is present | Install into every discovered supported harness |
|
|
969
|
+
| `--pi` | none | off | Install into Pi |
|
|
970
|
+
| `--omp` | none | off | Install into OMP |
|
|
971
|
+
| `--claude` | none | off | Install into Claude Code |
|
|
972
|
+
| `--status-band` | none | off | Replace OMP's status layout with the focused subscription layout |
|
|
973
|
+
|
|
974
|
+
- Mutates the selected harness installations. `--status-band` asks OMP for its global config directory, snapshots the three global status-line entries from `config.yml` or `config.yaml`, writes through `omp config set`, and verifies the effective values in the current working directory. On failure it restores prior values and resets entries that were previously absent or `null`. Restart OMP afterward.
|
|
975
|
+
- `--dry-run` reports the install and status-band intent without invoking a harness command.
|
|
976
|
+
- `--status-band` fails when OMP is not selected or detected. A failed or locally shadowed OMP configuration is reported as a failed OMP result rather than a successful install; verification covers the directory where the command runs, not every other project's OMP overrides.
|
|
977
|
+
|
|
978
|
+
```bash
|
|
979
|
+
kxm plugin install --omp --status-band
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
Not run in this reference: installs a plugin and changes OMP user configuration.
|
|
983
|
+
|
|
927
984
|
## `kxm auth`
|
|
928
985
|
|
|
929
986
|
### `kxm auth token`
|
|
@@ -1315,6 +1315,14 @@ every reader found in `plugins/kxm/src`, `scripts/`, and `packages/`; the
|
|
|
1315
1315
|
| `telemetry.anonymize` | Boolean | `true` | Not read by any code path yet |
|
|
1316
1316
|
| `telemetry.userTelemetryDir` | Path | none | Not read by any code path yet |
|
|
1317
1317
|
|
|
1318
|
+
The terminal editor exposes a fixed non-secret scalar subset of this table:
|
|
1319
|
+
theme and token budget; default workflow and harness; dashboard settings;
|
|
1320
|
+
default tracker; hub auto-start; improvement policy and thresholds; shadow
|
|
1321
|
+
execution and circuit-breaker scalars; and federated/anonymized telemetry
|
|
1322
|
+
flags. It deliberately excludes arrays, dynamic maps, paths, sync-provider
|
|
1323
|
+
details, hub endpoints and key references, credentials, and trusted roster,
|
|
1324
|
+
route, gate, role, or workflow-definition files.
|
|
1325
|
+
|
|
1318
1326
|
Under `auto_threshold`, `minRuns`, `minPassRate` and `minCostSavings` are the
|
|
1319
1327
|
distinct runs, the accepted share, and the mean recorded cost per attempt a
|
|
1320
1328
|
candidate needs before `kxm improve` reports it ready for review. A group with
|
|
@@ -1376,9 +1384,15 @@ Commands:
|
|
|
1376
1384
|
- `kxm config list` prints `user`, `defaults`, `dash`, `sync`, and
|
|
1377
1385
|
`loadedFrom`; `--json` prints every section. An empty `loadedFrom` means no
|
|
1378
1386
|
file was found.
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
the
|
|
1387
|
+
- `kxm config edit --scope project|user` opens the owner-validated terminal
|
|
1388
|
+
editor. Project scope shows Base → User → Project; user scope shows Base →
|
|
1389
|
+
User so a project override cannot mask the edit. Clear deletes that scope's
|
|
1390
|
+
leaf and re-reads the inherited value. Non-TTY and `--dry-run` calls print one
|
|
1391
|
+
plain frame without writing. Invalid scope and `--json` are refused.
|
|
1392
|
+
|
|
1393
|
+
There is no standalone `kxm config unset` command. The editor's clear action
|
|
1394
|
+
deletes its supported key from the selected scope; for other keys, delete the
|
|
1395
|
+
key from the file. Setting `null` stores `null` and does not mean inherit.
|
|
1382
1396
|
|
|
1383
1397
|
### Configuration layers
|
|
1384
1398
|
|
package/docs/reference/tools.md
CHANGED
|
@@ -323,11 +323,11 @@ Proposes a change to one authoritative state key. Despite its name, it only prop
|
|
|
323
323
|
|
|
324
324
|
Returns `{ "proposalId" }`. Blocked by the `read-only` preset; under an attempt token it needs an explicit grant.
|
|
325
325
|
|
|
326
|
-
## Pi extension commands
|
|
326
|
+
## Pi and OMP extension commands
|
|
327
327
|
|
|
328
|
-
The Pi extension connects the session to the hub at start, registers the 19 tools, and adds two slash commands. It also keeps a `kxm` status line and `kxm-work` and `kxm-progress` widgets, refreshed after every turn.
|
|
328
|
+
The shared Pi-compatible extension connects the session to the hub at start, registers the 19 tools, and adds two slash commands. It also keeps a concise `kxm` status line and `kxm-work` and `kxm-progress` widgets, refreshed after every turn.
|
|
329
329
|
|
|
330
|
-
In Pi:
|
|
330
|
+
In Pi or OMP:
|
|
331
331
|
|
|
332
332
|
```text
|
|
333
333
|
/kxm
|
|
@@ -344,7 +344,9 @@ In Pi:
|
|
|
344
344
|
| `/kxm memory` | Shows the project memory brief by running `kxm memory brief` (or `$KXM_BIN`) |
|
|
345
345
|
| `/kxm help` | Lists the subcommands. Any unknown subcommand shows the same help |
|
|
346
346
|
|
|
347
|
-
The task picker, `Continue KXM work?`, lists recent tasks and plans and puts the chosen prompt in the editor. It also appears when a Pi TUI session starts, begins a new session or forks, unless `KXM_SESSION_BRIEF=off`.
|
|
347
|
+
The task picker, `Continue KXM work?`, lists recent tasks and plans and puts the chosen prompt in the editor. It also appears when a Pi or OMP TUI session starts, begins a new session or forks, unless `KXM_SESSION_BRIEF=off`.
|
|
348
|
+
|
|
349
|
+
OMP can also show the active OAuth subscription's quota windows in its native status band. Install the extension and opt into KXM's focused layout with `kxm plugin install --omp --status-band`, then restart OMP. The layout keeps model, mode, branch, pull request, subagent, subscription usage, metered cost, and context signals; it does not add OMP's inline `status` segment because OMP already renders the KXM hook status on its own line. If `statusLine.showHookStatus` is disabled, the native usage and cost segments remain but the separate KXM line stays hidden.
|
|
348
350
|
|
|
349
351
|
Inbound requests arrive as a displayed message that starts a model turn: `steer` at the next decision boundary, `followUp` (and `nextTurn`, which an unattended worker cannot wait on) after the current work. The final response is returned as the reply. If the model provider fails, the request stays `delivered` so a supervised worker can recover it. The extension also registers model providers; see [Harness routing](harness-routing.md) and [Nous providers](../guides/nous-providers.md).
|
|
350
352
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kontextmind/kxm",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.179",
|
|
4
4
|
"description": "KXM local-first multi-agent orchestration and operator dashboard",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "KontextMind",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
},
|
|
71
71
|
"pi": {
|
|
72
72
|
"extensions": [
|
|
73
|
-
"./plugins/kxm/
|
|
73
|
+
"./plugins/kxm/dist/extension.js"
|
|
74
74
|
],
|
|
75
75
|
"skills": [
|
|
76
76
|
"./plugins/kxm/skills"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "kxm",
|
|
4
4
|
"displayName": "KXM",
|
|
5
|
-
"version": "0.7.
|
|
5
|
+
"version": "0.7.179",
|
|
6
6
|
"description": "Headless multi-agent orchestration, durable workflows, and a live operator dashboard for Pi and Claude Code",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "KontextMind",
|
|
@@ -886,12 +886,12 @@ import { spawnSync } from "node:child_process";
|
|
|
886
886
|
import { randomUUID } from "node:crypto";
|
|
887
887
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
888
888
|
import { join as join4 } from "node:path";
|
|
889
|
-
function
|
|
889
|
+
function hubStatusPart(hub) {
|
|
890
890
|
const suffix = hub?.scope === "remote" ? "/remote" : "";
|
|
891
|
-
if (hub?.state === "
|
|
892
|
-
if (hub?.state === "
|
|
893
|
-
if (hub?.
|
|
894
|
-
return
|
|
891
|
+
if (hub?.state === "off" || hub?.state === void 0 && hub?.online === false) return `hub:off${suffix}`;
|
|
892
|
+
if (hub?.state === "unknown") return `hub:unknown${suffix}`;
|
|
893
|
+
if (hub?.scope === "remote") return "hub:remote";
|
|
894
|
+
return void 0;
|
|
895
895
|
}
|
|
896
896
|
function truncate(value, width) {
|
|
897
897
|
const text = value.replace(/\s+/g, " ").trim();
|
|
@@ -969,17 +969,17 @@ function readGitShip(cwd) {
|
|
|
969
969
|
}
|
|
970
970
|
function formatCostLine(input) {
|
|
971
971
|
if (!input) return void 0;
|
|
972
|
-
if (input.sessionCostUsd === void 0 && input.runCostUsd === void 0) return
|
|
972
|
+
if (input.sessionCostUsd === void 0 && input.runCostUsd === void 0) return void 0;
|
|
973
973
|
const sess = input.sessionCostUsd !== void 0 ? `$${input.sessionCostUsd.toFixed(2)} sess` : void 0;
|
|
974
974
|
const run = input.runCostUsd !== void 0 ? `$${input.runCostUsd.toFixed(2)} run` : void 0;
|
|
975
975
|
const route = input.harness && input.model ? `${input.harness}/${input.model}` : input.model ?? void 0;
|
|
976
976
|
const parts = [sess, run, route].filter(Boolean);
|
|
977
|
-
return parts.length > 0 ? parts.join(" \xB7 ") :
|
|
977
|
+
return parts.length > 0 ? parts.join(" \xB7 ") : void 0;
|
|
978
978
|
}
|
|
979
|
-
function renderStatusLine(statsOrBrief, current, hub, ship, updateLatest
|
|
979
|
+
function renderStatusLine(statsOrBrief, current, hub, ship, updateLatest) {
|
|
980
980
|
if ("schema" in statsOrBrief && statsOrBrief.schema === "kxm.session-brief.v1") {
|
|
981
981
|
const brief = statsOrBrief;
|
|
982
|
-
if (!current && !hub && !ship && !updateLatest
|
|
982
|
+
if (!current && !hub && !ship && !updateLatest) {
|
|
983
983
|
return brief.statusLine;
|
|
984
984
|
}
|
|
985
985
|
return formatSessionStatusLine(
|
|
@@ -987,46 +987,39 @@ function renderStatusLine(statsOrBrief, current, hub, ship, updateLatest, cost)
|
|
|
987
987
|
current,
|
|
988
988
|
hub ?? brief.hub,
|
|
989
989
|
ship,
|
|
990
|
-
updateLatest
|
|
991
|
-
cost ?? brief.cost
|
|
990
|
+
updateLatest
|
|
992
991
|
);
|
|
993
992
|
}
|
|
994
|
-
return formatSessionStatusLine(statsOrBrief, current, hub, ship, updateLatest
|
|
995
|
-
}
|
|
996
|
-
function formatSessionStatusLine(stats, current, hub, ship, updateLatest
|
|
997
|
-
const
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
return finalLine.length <= 80 ? finalLine : `${finalLine.slice(0, 79)}\u2026`;
|
|
1004
|
-
}
|
|
1005
|
-
const parts = [];
|
|
1006
|
-
if (current?.kind === "task") parts.push(`${head} ${current.detail}`);
|
|
1007
|
-
else if (current?.kind === "plan") parts.push(`${head} plan ${truncate(current.detail, 36)}`);
|
|
1008
|
-
else parts.push(head);
|
|
1009
|
-
parts.push(`${stats.activeTasks} task${stats.activeTasks === 1 ? "" : "s"}`);
|
|
993
|
+
return formatSessionStatusLine(statsOrBrief, current, hub, ship, updateLatest);
|
|
994
|
+
}
|
|
995
|
+
function formatSessionStatusLine(stats, current, hub, ship, updateLatest) {
|
|
996
|
+
const parts = ["kxm"];
|
|
997
|
+
const hubPart = hubStatusPart(hub);
|
|
998
|
+
if (hubPart) parts.push(hubPart);
|
|
999
|
+
if (current?.kind === "task") parts.push(current.detail);
|
|
1000
|
+
else if (current?.kind === "plan") parts.push(`plan ${truncate(current.detail, 36)}`);
|
|
1001
|
+
if (stats.activeTasks > 0) parts.push(`${stats.activeTasks} task${stats.activeTasks === 1 ? "" : "s"}`);
|
|
1010
1002
|
if (stats.waitingTasks > 0) parts.push(`${stats.waitingTasks} waiting`);
|
|
1011
|
-
parts.push(`${stats.planCount} plan${stats.planCount === 1 ? "" : "s"}`);
|
|
1003
|
+
if (stats.planCount > 0) parts.push(`${stats.planCount} plan${stats.planCount === 1 ? "" : "s"}`);
|
|
1012
1004
|
if (stats.inbox > 0) parts.push(`inbox ${stats.inbox}`);
|
|
1013
1005
|
if (ship?.dirty) parts.push("dirty");
|
|
1014
1006
|
else if (ship && ship.ahead > 0) parts.push(`${ship.ahead} local`);
|
|
1015
|
-
if (cost) parts.push(cost);
|
|
1016
1007
|
if (updateLatest) parts.push(`upd ${updateLatest}`);
|
|
1017
|
-
|
|
1018
|
-
return line.length <= 80 ? line : `${line.slice(0, 79)}\u2026`;
|
|
1008
|
+
return truncate(parts.join(" \xB7 "), 80);
|
|
1019
1009
|
}
|
|
1020
1010
|
function formatSessionWidget(stats, current, hub, ship, updateLatest, cost) {
|
|
1021
|
-
const
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
|
|
1011
|
+
const suffix = hub?.scope === "remote" ? "/remote" : "";
|
|
1012
|
+
const hubMark = hub?.state === "on" || hub?.state === void 0 && hub?.online === true ? `hub:on${suffix}` : hub?.state === "off" || hub?.state === void 0 && hub?.online === false ? `hub:off${suffix}` : hub?.state === "unknown" ? `hub:unknown${suffix}` : void 0;
|
|
1013
|
+
const summary = ["KXM", hubMark];
|
|
1014
|
+
if (stats.activeTasks > 0) summary.push(`${stats.activeTasks} task${stats.activeTasks === 1 ? "" : "s"}`);
|
|
1015
|
+
if (stats.waitingTasks > 0) summary.push(`${stats.waitingTasks} waiting`);
|
|
1016
|
+
if (stats.planCount > 0) summary.push(`${stats.planCount} plan${stats.planCount === 1 ? "" : "s"}`);
|
|
1017
|
+
if (stats.inbox > 0) summary.push(`inbox ${stats.inbox}`);
|
|
1018
|
+
const lines = [summary.filter(Boolean).join(" \xB7 ")];
|
|
1025
1019
|
if (current) lines.push(`now ${current.kind} ${truncate(current.detail, 56)}`);
|
|
1026
|
-
else if (stats.latestPlan) lines.push(`plan
|
|
1027
|
-
|
|
1028
|
-
lines.push(
|
|
1029
|
-
if (cost) lines.push(`cost ${cost}`);
|
|
1020
|
+
else if (stats.latestPlan) lines.push(`plan ${truncate(stats.latestPlan, 60)}`);
|
|
1021
|
+
if (ship?.dirty || ship && ship.ahead > 0) lines.push(formatShipLine(ship));
|
|
1022
|
+
if (cost && cost !== "unknown") lines.push(`cost ${cost}`);
|
|
1030
1023
|
if (updateLatest) lines.push(`update ${updateLatest} available \xB7 kxm update --kxm`);
|
|
1031
1024
|
return lines;
|
|
1032
1025
|
}
|
|
@@ -1046,7 +1039,7 @@ function buildSessionBrief(snapshot, current, hub, ship, updateLatest, cost, ses
|
|
|
1046
1039
|
const tasks = uniqueLabels(recentTasks(snapshot.runs).map(taskItem));
|
|
1047
1040
|
const plans = uniqueLabels(snapshot.plans.slice(0, MAX_SESSION_BRIEF_PLANS).map(planItem));
|
|
1048
1041
|
const resolvedHub = hub ?? { state: "off", evidence: "unconfigured", online: false };
|
|
1049
|
-
const statusLine = formatSessionStatusLine(stats, current, resolvedHub, ship, updateLatest
|
|
1042
|
+
const statusLine = formatSessionStatusLine(stats, current, resolvedHub, ship, updateLatest);
|
|
1050
1043
|
const widgetLines = formatSessionWidget(stats, current, resolvedHub, ship, updateLatest, cost);
|
|
1051
1044
|
return {
|
|
1052
1045
|
schema: SESSION_BRIEF_SCHEMA,
|
|
@@ -1110,7 +1103,7 @@ function estimateSessionCost(stateDir) {
|
|
|
1110
1103
|
latestModel = r.effectiveModel ?? r.requestedModel ?? latestModel;
|
|
1111
1104
|
latestHarness = r.harness ?? latestHarness;
|
|
1112
1105
|
}
|
|
1113
|
-
if (!hasMetered) return
|
|
1106
|
+
if (!hasMetered) return void 0;
|
|
1114
1107
|
const route = latestHarness && latestModel ? `${latestHarness}/${latestModel}` : latestModel;
|
|
1115
1108
|
return `$${totalCost.toFixed(2)} sess${route ? ` \xB7 ${route}` : ""}`;
|
|
1116
1109
|
} catch {
|
|
@@ -1147,8 +1140,7 @@ function loadSessionBrief(cwd, env = process.env, current, hub, options = {}) {
|
|
|
1147
1140
|
current,
|
|
1148
1141
|
effectiveHub,
|
|
1149
1142
|
options.ship,
|
|
1150
|
-
options.updateLatest
|
|
1151
|
-
effectiveCost
|
|
1143
|
+
options.updateLatest
|
|
1152
1144
|
);
|
|
1153
1145
|
const widgetLines = formatSessionWidget(
|
|
1154
1146
|
cached.stats,
|
|
@@ -1213,8 +1205,7 @@ async function loadSessionBriefAsync(cwd, env = process.env, current, hub, optio
|
|
|
1213
1205
|
current,
|
|
1214
1206
|
effectiveHub,
|
|
1215
1207
|
options.ship,
|
|
1216
|
-
void 0
|
|
1217
|
-
effectiveCost
|
|
1208
|
+
void 0
|
|
1218
1209
|
);
|
|
1219
1210
|
const widgetLines = formatSessionWidget(
|
|
1220
1211
|
cached.stats,
|
|
@@ -1275,7 +1266,7 @@ function sessionBriefPickerEnabled(input) {
|
|
|
1275
1266
|
function parseKxmSlashArgs(args) {
|
|
1276
1267
|
const raw = String(args ?? "").trim().toLowerCase();
|
|
1277
1268
|
if (raw === "" || raw === "brief") return "brief";
|
|
1278
|
-
if (raw === "status" || raw === "hub" || raw === "memory" || raw === "progress" || raw === "workflow" || raw === "help") return raw;
|
|
1269
|
+
if (raw === "status" || raw === "hub" || raw === "memory" || raw === "progress" || raw === "workflow" || raw === "config" || raw === "help") return raw;
|
|
1279
1270
|
return "help";
|
|
1280
1271
|
}
|
|
1281
1272
|
function kxmSlashCompletions(prefix) {
|
|
@@ -1295,7 +1286,7 @@ var init_session_work = __esm({
|
|
|
1295
1286
|
MAX_SESSION_BRIEF_PLANS = 5;
|
|
1296
1287
|
SESSION_BRIEF_SCHEMA = "kxm.session-brief.v1";
|
|
1297
1288
|
DEFAULT_SESSION_BRIEF_STALE_SECONDS = 5;
|
|
1298
|
-
KXM_SLASH_SUBCOMMANDS = ["status", "hub", "memory", "progress", "workflow", "help"];
|
|
1289
|
+
KXM_SLASH_SUBCOMMANDS = ["status", "hub", "memory", "progress", "workflow", "config", "help"];
|
|
1299
1290
|
}
|
|
1300
1291
|
});
|
|
1301
1292
|
|
|
@@ -9182,6 +9173,7 @@ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
|
|
|
9182
9173
|
const userConfigFile = join6(userDir, "config.yaml");
|
|
9183
9174
|
const repoDir = repoConfigDirectory(repoRoot);
|
|
9184
9175
|
const repoConfigFile = join6(repoDir, "config.yaml");
|
|
9176
|
+
const includeProject = options.layers !== "user";
|
|
9185
9177
|
let userRaw = {};
|
|
9186
9178
|
let userLoadedPath;
|
|
9187
9179
|
if (existsSync7(userConfigFile)) {
|
|
@@ -9195,7 +9187,7 @@ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
|
|
|
9195
9187
|
}
|
|
9196
9188
|
let repoRaw = {};
|
|
9197
9189
|
let repoLoadedPath;
|
|
9198
|
-
if (existsSync7(repoConfigFile)) {
|
|
9190
|
+
if (includeProject && existsSync7(repoConfigFile)) {
|
|
9199
9191
|
try {
|
|
9200
9192
|
const text = readFileSync7(repoConfigFile, "utf8");
|
|
9201
9193
|
repoRaw = (0, import_yaml2.parse)(text) ?? {};
|