@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.
@@ -23948,6 +23948,7 @@ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
23948
23948
  const userConfigFile = join(userDir, "config.yaml");
23949
23949
  const repoDir = repoConfigDirectory(repoRoot);
23950
23950
  const repoConfigFile = join(repoDir, "config.yaml");
23951
+ const includeProject = options.layers !== "user";
23951
23952
  let userRaw = {};
23952
23953
  let userLoadedPath;
23953
23954
  if (existsSync(userConfigFile)) {
@@ -23961,7 +23962,7 @@ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
23961
23962
  }
23962
23963
  let repoRaw = {};
23963
23964
  let repoLoadedPath;
23964
- if (existsSync(repoConfigFile)) {
23965
+ if (includeProject && existsSync(repoConfigFile)) {
23965
23966
  try {
23966
23967
  const text = readFileSync(repoConfigFile, "utf8");
23967
23968
  repoRaw = (0, import_yaml.parse)(text) ?? {};
@@ -25461,7 +25462,7 @@ function sessionTokenFixHint(policy) {
25461
25462
  }
25462
25463
 
25463
25464
  // plugins/kxm/src/mcp-server.ts
25464
- var VERSION = "0.7.177";
25465
+ var VERSION = "0.7.179";
25465
25466
  var CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
25466
25467
  var inbox = /* @__PURE__ */ new Map();
25467
25468
  var notifiedInbox = /* @__PURE__ */ new Set();
@@ -25121,6 +25121,7 @@ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
25121
25121
  const userConfigFile = join11(userDir, "config.yaml");
25122
25122
  const repoDir = repoConfigDirectory(repoRoot);
25123
25123
  const repoConfigFile = join11(repoDir, "config.yaml");
25124
+ const includeProject = options.layers !== "user";
25124
25125
  let userRaw = {};
25125
25126
  let userLoadedPath;
25126
25127
  if (existsSync10(userConfigFile)) {
@@ -25134,7 +25135,7 @@ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
25134
25135
  }
25135
25136
  let repoRaw = {};
25136
25137
  let repoLoadedPath;
25137
- if (existsSync10(repoConfigFile)) {
25138
+ if (includeProject && existsSync10(repoConfigFile)) {
25138
25139
  try {
25139
25140
  const text = readFileSync8(repoConfigFile, "utf8");
25140
25141
  repoRaw = (0, import_yaml5.parse)(text) ?? {};
@@ -25865,6 +25865,7 @@ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
25865
25865
  const userConfigFile = join11(userDir, "config.yaml");
25866
25866
  const repoDir = repoConfigDirectory(repoRoot);
25867
25867
  const repoConfigFile = join11(repoDir, "config.yaml");
25868
+ const includeProject = options.layers !== "user";
25868
25869
  let userRaw = {};
25869
25870
  let userLoadedPath;
25870
25871
  if (existsSync10(userConfigFile)) {
@@ -25878,7 +25879,7 @@ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
25878
25879
  }
25879
25880
  let repoRaw = {};
25880
25881
  let repoLoadedPath;
25881
- if (existsSync10(repoConfigFile)) {
25882
+ if (includeProject && existsSync10(repoConfigFile)) {
25882
25883
  try {
25883
25884
  const text = readFileSync8(repoConfigFile, "utf8");
25884
25885
  repoRaw = (0, import_yaml5.parse)(text) ?? {};
@@ -23447,6 +23447,7 @@ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
23447
23447
  const userConfigFile = join7(userDir, "config.yaml");
23448
23448
  const repoDir = repoConfigDirectory(repoRoot);
23449
23449
  const repoConfigFile = join7(repoDir, "config.yaml");
23450
+ const includeProject = options.layers !== "user";
23450
23451
  let userRaw = {};
23451
23452
  let userLoadedPath;
23452
23453
  if (existsSync7(userConfigFile)) {
@@ -23460,7 +23461,7 @@ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
23460
23461
  }
23461
23462
  let repoRaw = {};
23462
23463
  let repoLoadedPath;
23463
- if (existsSync7(repoConfigFile)) {
23464
+ if (includeProject && existsSync7(repoConfigFile)) {
23464
23465
  try {
23465
23466
  const text2 = readFileSync5(repoConfigFile, "utf8");
23466
23467
  repoRaw = (0, import_yaml6.parse)(text2) ?? {};
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-plugin",
3
- "version": "0.7.177",
3
+ "version": "0.7.179",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "engines": {
@@ -53,7 +53,7 @@ const SUBCOMMANDS: Record<string, string[]> = {
53
53
  memory: ["brief", "note", "sync"],
54
54
  routing: ["report", "benchmark"],
55
55
  hub: ["view", "start", "stop", "bind", "unbind"],
56
- config: ["get", "set", "list"],
56
+ config: ["get", "set", "edit", "list"],
57
57
  goal: ["create", "list", "get"],
58
58
  task: ["create", "list", "get", "run", "sync"],
59
59
  studio: ["layout", "serve"],
@@ -179,7 +179,7 @@ _kxm() {
179
179
  _arguments '1:shell:(bash zsh fish)'
180
180
  ;;
181
181
  config)
182
- _arguments '1:action:(get set list)'
182
+ _arguments '1:action:(get set edit list)'
183
183
  ;;
184
184
  runs)
185
185
  _arguments '1:subcommand:(status cancel list)'
@@ -224,7 +224,7 @@ complete -c kxm -n "__fish_seen_subcommand_from completion" -a "bash zsh fish"
224
224
  complete -c kxm -n "__fish_seen_subcommand_from dash" -l screen -a "agents tasks workflows plans inbox procs spend"
225
225
 
226
226
  # Config subcommand
227
- complete -c kxm -n "__fish_seen_subcommand_from config" -a "get set list"
227
+ complete -c kxm -n "__fish_seen_subcommand_from config" -a "get set edit list"
228
228
 
229
229
  # Runs subcommand
230
230
  complete -c kxm -n "__fish_seen_subcommand_from runs" -a "status cancel list"
@@ -1,7 +1,10 @@
1
1
  import { spawnSync } from "node:child_process";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { isAbsolute, join } from "node:path";
4
+ import { parse as parseYaml } from "yaml";
2
5
  import { findKxmRepoRoot } from "../repo-root.ts";
3
6
  import { probeHarnesses } from "../harness.ts";
4
- import { print, type Runtime } from "./types.ts";
7
+ import { print, type CliSpawnResult, type Runtime } from "./types.ts";
5
8
 
6
9
  export type PluginHarnessId = "pi" | "omp" | "claude";
7
10
 
@@ -12,6 +15,7 @@ export interface PluginInstallOptions {
12
15
  claude?: boolean | undefined;
13
16
  omp?: boolean | undefined;
14
17
  pi?: boolean | undefined;
18
+ statusBand?: boolean | undefined;
15
19
  }
16
20
 
17
21
  export interface PluginInstallHarnessResult {
@@ -23,6 +27,149 @@ export interface PluginInstallHarnessResult {
23
27
  error?: string | undefined;
24
28
  }
25
29
 
30
+ const OMP_STATUS_SETTINGS = [
31
+ { key: "statusLine.leftSegments", configKey: "leftSegments", value: ["model", "mode", "git", "pr", "subagents"] },
32
+ { key: "statusLine.rightSegments", configKey: "rightSegments", value: ["usage", "cost", "context_pct"] },
33
+ { key: "statusLine.preset", configKey: "preset", value: "custom" },
34
+ ] as const;
35
+
36
+ function spawnHarness(runtime: Runtime, command: string, args: readonly string[]): CliSpawnResult {
37
+ return runtime.io.spawnSync
38
+ ? runtime.io.spawnSync(command, args)
39
+ : spawnSync(command, args, { encoding: "utf8", windowsHide: true, env: runtime.env });
40
+ }
41
+
42
+ function spawnFailed(run: CliSpawnResult): boolean {
43
+ return Boolean(run.error) || run.status !== 0;
44
+ }
45
+
46
+ function spawnFailureDetail(run: CliSpawnResult, fallback: string): string {
47
+ return (run.stderr || run.stdout || run.error?.message || fallback).trim();
48
+ }
49
+
50
+ interface OmpStatusBandFailure {
51
+ args: readonly string[];
52
+ run: CliSpawnResult;
53
+ rollbackAttempted: boolean;
54
+ rollbackFailed: boolean;
55
+ }
56
+
57
+ interface OmpGlobalSettingSnapshot {
58
+ present: boolean;
59
+ value?: unknown;
60
+ }
61
+
62
+ function ompConfigValueArg(value: unknown): string {
63
+ return typeof value === "string" ? value : JSON.stringify(value);
64
+ }
65
+
66
+ function readOmpGlobalStatusSettings(
67
+ runtime: Runtime,
68
+ command: string,
69
+ ): { previous: Record<string, OmpGlobalSettingSnapshot> } | OmpStatusBandFailure {
70
+ const args = ["config", "path"];
71
+ const run = spawnHarness(runtime, command, args);
72
+ if (spawnFailed(run)) return { args, run, rollbackAttempted: false, rollbackFailed: false };
73
+
74
+ const agentDir = run.stdout.trim();
75
+ if (!agentDir || !isAbsolute(agentDir) || !existsSync(agentDir)) {
76
+ return {
77
+ args,
78
+ run: { status: 1, stdout: run.stdout, stderr: "omp config path did not return an existing absolute directory" },
79
+ rollbackAttempted: false,
80
+ rollbackFailed: false,
81
+ };
82
+ }
83
+
84
+ const configPath = ["config.yml", "config.yaml"].map((name) => join(agentDir, name)).find(existsSync);
85
+ let document: unknown = {};
86
+ try {
87
+ if (configPath) document = parseYaml(readFileSync(configPath, "utf8"));
88
+ } catch {
89
+ return {
90
+ args,
91
+ run: { status: 1, stdout: run.stdout, stderr: `could not read OMP global config at ${configPath}` },
92
+ rollbackAttempted: false,
93
+ rollbackFailed: false,
94
+ };
95
+ }
96
+
97
+ const root = document && typeof document === "object" && !Array.isArray(document) ? document : {};
98
+ const rawStatusLine = "statusLine" in root ? root.statusLine : undefined;
99
+ const statusLine = rawStatusLine && typeof rawStatusLine === "object" && !Array.isArray(rawStatusLine)
100
+ ? rawStatusLine
101
+ : {};
102
+ const previous: Record<string, OmpGlobalSettingSnapshot> = {};
103
+ for (const setting of OMP_STATUS_SETTINGS) {
104
+ const value = statusLine[setting.configKey as keyof typeof statusLine];
105
+ previous[setting.key] = Object.hasOwn(statusLine, setting.configKey) && value !== null && value !== undefined
106
+ ? { present: true, value }
107
+ : { present: false };
108
+ }
109
+ return { previous };
110
+ }
111
+
112
+ function rollbackOmpStatusBand(
113
+ runtime: Runtime,
114
+ command: string,
115
+ settings: readonly (typeof OMP_STATUS_SETTINGS)[number][],
116
+ previous: Readonly<Record<string, OmpGlobalSettingSnapshot>>,
117
+ ): boolean {
118
+ let failed = false;
119
+ for (const setting of [...settings].reverse()) {
120
+ const snapshot = previous[setting.key];
121
+ const args = snapshot?.present
122
+ ? ["config", "set", setting.key, ompConfigValueArg(snapshot.value), "--json"]
123
+ : ["config", "reset", setting.key, "--json"];
124
+ if (spawnFailed(spawnHarness(runtime, command, args))) failed = true;
125
+ }
126
+ return failed;
127
+ }
128
+
129
+ function configureOmpStatusBand(runtime: Runtime, command: string): OmpStatusBandFailure | undefined {
130
+ const snapshot = readOmpGlobalStatusSettings(runtime, command);
131
+ if ("run" in snapshot) return snapshot;
132
+
133
+ const written: (typeof OMP_STATUS_SETTINGS)[number][] = [];
134
+ for (const setting of OMP_STATUS_SETTINGS) {
135
+ const args = ["config", "set", setting.key, ompConfigValueArg(setting.value), "--json"];
136
+ written.push(setting);
137
+ const run = spawnHarness(runtime, command, args);
138
+ if (spawnFailed(run)) {
139
+ return {
140
+ args,
141
+ run,
142
+ rollbackAttempted: true,
143
+ rollbackFailed: rollbackOmpStatusBand(runtime, command, written, snapshot.previous),
144
+ };
145
+ }
146
+ }
147
+
148
+ for (const setting of OMP_STATUS_SETTINGS) {
149
+ const args = ["config", "get", setting.key, "--json"];
150
+ const run = spawnHarness(runtime, command, args);
151
+ let actual: unknown;
152
+ try {
153
+ const parsed: unknown = JSON.parse(run.stdout);
154
+ actual = parsed && typeof parsed === "object" && "value" in parsed ? parsed.value : undefined;
155
+ } catch {
156
+ actual = undefined;
157
+ }
158
+ if (spawnFailed(run) || JSON.stringify(actual) !== JSON.stringify(setting.value)) {
159
+ const mismatch = spawnFailed(run)
160
+ ? run
161
+ : { status: 1, stdout: run.stdout, stderr: `effective ${setting.key} is shadowed or did not persist` };
162
+ return {
163
+ args,
164
+ run: mismatch,
165
+ rollbackAttempted: true,
166
+ rollbackFailed: rollbackOmpStatusBand(runtime, command, written, snapshot.previous),
167
+ };
168
+ }
169
+ }
170
+ return undefined;
171
+ }
172
+
26
173
  export async function cmdPluginInstall(
27
174
  runtime: Runtime,
28
175
  options: PluginInstallOptions,
@@ -41,6 +188,17 @@ export async function cmdPluginInstall(
41
188
  ? requestedHarnesses
42
189
  : PLUGIN_SUPPORTED_HARNESSES;
43
190
 
191
+ if (options.statusBand && isExplicit && !options.omp) {
192
+ const detail = "--status-band requires OMP to be among the selected harnesses";
193
+ print(
194
+ runtime.io,
195
+ runtime.json,
196
+ { ok: false, command: "plugin install", error: "omp_status_band_requires_omp", results: [] },
197
+ detail,
198
+ );
199
+ return 1;
200
+ }
201
+
44
202
  const results: PluginInstallHarnessResult[] = [];
45
203
 
46
204
  for (const harnessId of targetHarnesses) {
@@ -48,7 +206,7 @@ export async function cmdPluginInstall(
48
206
  const detected = Boolean(status?.detected && status?.command);
49
207
 
50
208
  if (!detected) {
51
- if (isExplicit) {
209
+ if (isExplicit || (options.statusBand && harnessId === "omp")) {
52
210
  results.push({
53
211
  harness: harnessId,
54
212
  status: "failed",
@@ -72,17 +230,15 @@ export async function cmdPluginInstall(
72
230
  detail: `would install kxm plugin into pi: ${command} ${args.join(" ")}`,
73
231
  });
74
232
  } else {
75
- const run = runtime.io.spawnSync
76
- ? runtime.io.spawnSync(command, args)
77
- : spawnSync(command, args, { encoding: "utf8", windowsHide: true, env: runtime.env });
78
- if (run.error || (run.status !== 0 && run.status !== null)) {
233
+ const run = spawnHarness(runtime, command, args);
234
+ if (spawnFailed(run)) {
79
235
  results.push({
80
236
  harness: "pi",
81
237
  status: "failed",
82
238
  command,
83
239
  args,
84
240
  error: run.error?.message || "install_failed",
85
- detail: (run.stderr || run.stdout || "pi install exited with error").trim(),
241
+ detail: spawnFailureDetail(run, "pi install exited with error"),
86
242
  });
87
243
  } else {
88
244
  results.push({
@@ -102,29 +258,43 @@ export async function cmdPluginInstall(
102
258
  status: "would",
103
259
  command,
104
260
  args,
105
- detail: `would install kxm plugin into omp: ${command} ${args.join(" ")}`,
261
+ detail: options.statusBand
262
+ ? `would install kxm plugin into omp and configure the focused subscription status band: ${command} ${args.join(" ")}`
263
+ : `would install kxm plugin into omp: ${command} ${args.join(" ")}`,
106
264
  });
107
265
  } else {
108
- const run = runtime.io.spawnSync
109
- ? runtime.io.spawnSync(command, args)
110
- : spawnSync(command, args, { encoding: "utf8", windowsHide: true, env: runtime.env });
111
- if (run.error || (run.status !== 0 && run.status !== null)) {
266
+ const run = spawnHarness(runtime, command, args);
267
+ if (spawnFailed(run)) {
112
268
  results.push({
113
269
  harness: "omp",
114
270
  status: "failed",
115
271
  command,
116
272
  args,
117
273
  error: run.error?.message || "install_failed",
118
- detail: (run.stderr || run.stdout || "omp plugin install exited with error").trim(),
274
+ detail: spawnFailureDetail(run, "omp plugin install exited with error"),
119
275
  });
120
276
  } else {
121
- results.push({
122
- harness: "omp",
123
- status: "passed",
124
- command,
125
- args,
126
- detail: "installed kxm plugin into omp",
127
- });
277
+ const configFailure = options.statusBand ? configureOmpStatusBand(runtime, command) : undefined;
278
+ if (configFailure) {
279
+ results.push({
280
+ harness: "omp",
281
+ status: "failed",
282
+ command,
283
+ args: configFailure.args,
284
+ error: "status_band_config_failed",
285
+ detail: `installed kxm plugin into omp, but ${configFailure.args.slice(0, 3).join(" ")} failed: ${spawnFailureDetail(configFailure.run, "omp status band configuration exited with error")}${configFailure.rollbackAttempted ? (configFailure.rollbackFailed ? "; previous settings could not be fully restored" : "; previous settings restored") : ""}`,
286
+ });
287
+ } else {
288
+ results.push({
289
+ harness: "omp",
290
+ status: "passed",
291
+ command,
292
+ args,
293
+ detail: options.statusBand
294
+ ? "installed kxm plugin into omp and configured the focused subscription status band; restart omp"
295
+ : "installed kxm plugin into omp",
296
+ });
297
+ }
128
298
  }
129
299
  }
130
300
  } else if (harnessId === "claude") {
@@ -144,17 +314,15 @@ export async function cmdPluginInstall(
144
314
  } else {
145
315
  spawnSync(command, marketplaceArgs, { encoding: "utf8", windowsHide: true, env: runtime.env });
146
316
  }
147
- const run = runtime.io.spawnSync
148
- ? runtime.io.spawnSync(command, installArgs)
149
- : spawnSync(command, installArgs, { encoding: "utf8", windowsHide: true, env: runtime.env });
150
- if (run.error || (run.status !== 0 && run.status !== null)) {
317
+ const run = spawnHarness(runtime, command, installArgs);
318
+ if (spawnFailed(run)) {
151
319
  results.push({
152
320
  harness: "claude",
153
321
  status: "failed",
154
322
  command,
155
323
  args: installArgs,
156
324
  error: run.error?.message || "install_failed",
157
- detail: (run.stderr || run.stdout || "claude plugin install exited with error").trim(),
325
+ detail: spawnFailureDetail(run, "claude install exited with error"),
158
326
  });
159
327
  } else {
160
328
  results.push({
@@ -48,7 +48,9 @@ import {
48
48
  setKxmConfigValue,
49
49
  getKxmConfigValue,
50
50
  formatKxmConfig,
51
+ userConfigDirForEnv,
51
52
  } from "../config.ts";
53
+ import { runKxmConfigEditor } from "../config-editor.ts";
52
54
  import { generateShellCompletion, type SupportedShell } from "../autocomplete.ts";
53
55
  import {
54
56
  completionRcTarget,
@@ -736,6 +738,41 @@ export async function cmdConfigSet(
736
738
  }
737
739
  }
738
740
 
741
+ export async function cmdConfigEdit(
742
+ runtime: Runtime,
743
+ options: { scope?: string },
744
+ isTty = Boolean(process.stdin.isTTY && process.stdout.isTTY),
745
+ ): Promise<number> {
746
+ const scope = options.scope;
747
+ if (scope !== "project" && scope !== "user") {
748
+ const detail = "config edit requires --scope project or --scope user";
749
+ print(runtime.io, runtime.json, { ok: false, command: "config edit", error: "config_edit_unsupported", detail }, detail);
750
+ return 2;
751
+ }
752
+ if (runtime.json) {
753
+ const detail = "config edit does not print machine-readable output";
754
+ print(runtime.io, runtime.json, { ok: false, command: "config edit", error: "config_edit_unsupported", detail }, detail);
755
+ return 2;
756
+ }
757
+ try {
758
+ const userConfigDir = userConfigDirForEnv(runtime.env);
759
+ return await runKxmConfigEditor({
760
+ target: {
761
+ cwd: runtime.cwd,
762
+ scope,
763
+ ...(userConfigDir === undefined ? {} : { userConfigDir }),
764
+ },
765
+ stdout: (text) => runtime.io.stdout(text),
766
+ isTty,
767
+ dryRun: runtime.dryRun,
768
+ });
769
+ } catch (error) {
770
+ const message = error instanceof Error ? error.message : String(error);
771
+ runtime.io.stderr(`config edit failed: ${message}\n`);
772
+ return 1;
773
+ }
774
+ }
775
+
739
776
  export async function cmdConfigList(runtime: Runtime): Promise<number> {
740
777
  try {
741
778
  const config = loadKxmConfig(runtime.cwd);
@@ -165,6 +165,7 @@ import {
165
165
  cmdImprove,
166
166
  cmdConfigGet,
167
167
  cmdConfigSet,
168
+ cmdConfigEdit,
168
169
  cmdConfigList,
169
170
  cmdCompletion,
170
171
  cmdCompletionInstall,
@@ -228,7 +229,7 @@ const DRY_RUN_COMMANDS: ReadonlySet<string> = new Set([
228
229
  "routing report", "routing benchmark",
229
230
  "ssh info", "ssh run", "ssh file", "ssh close",
230
231
  "hub view", "hub start", "hub stop", "hub bind", "hub unbind",
231
- "config get", "config set", "config list",
232
+ "config get", "config set", "config edit", "config list",
232
233
  "goal create", "goal list",
233
234
  "task create", "task list", "task get", "task run", "task sync",
234
235
  "studio layout", "studio serve",
@@ -772,7 +773,8 @@ function createProgram(ctx: CliContext, result: { code: number }, argv: readonly
772
773
  .option("--claude", "Install plugin for Claude Code")
773
774
  .option("--omp", "Install plugin for Oh My Pi (OMP)")
774
775
  .option("--pi", "Install plugin for Pi")
775
- .action(async function pluginInstallAction(this: Command, options: { all?: boolean; claude?: boolean; omp?: boolean; pi?: boolean }) {
776
+ .option("--status-band", "Configure OMP's focused status band with live subscription usage")
777
+ .action(async function pluginInstallAction(this: Command, options: { all?: boolean; claude?: boolean; omp?: boolean; pi?: boolean; statusBand?: boolean }) {
776
778
  result.code = await cmdPluginInstall(runtimeFrom(ctx, this), options);
777
779
  });
778
780
 
@@ -1430,6 +1432,11 @@ function createProgram(ctx: CliContext, result: { code: number }, argv: readonly
1430
1432
  .action(async function configSetAction(this: Command, key: string, value: string, options: { scope: string }) {
1431
1433
  result.code = await cmdConfigSet(runtimeFrom(ctx, this), key, value, options);
1432
1434
  });
1435
+ addGlobalOptions(configCmd.command("edit").description("Edit supported non-secret settings in the terminal panel"))
1436
+ .option("--scope <scope>", "Write scope: project or user")
1437
+ .action(async function configEditAction(this: Command, options: { scope?: string }) {
1438
+ result.code = await cmdConfigEdit(runtimeFrom(ctx, this), options);
1439
+ });
1433
1440
  addGlobalOptions(configCmd.command("list", { isDefault: true }).description("List resolved configuration values"))
1434
1441
  .action(async function configListAction(this: Command) {
1435
1442
  result.code = await cmdConfigList(runtimeFrom(ctx, this));