metheus-governance-mcp-cli 0.2.65 → 0.2.66

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/README.md CHANGED
@@ -227,6 +227,9 @@ Behavior:
227
227
  - `bot setup` asks for `Telegram / Slack / KakaoTalk` first, then prompts with numbered actions.
228
228
  - `bot add` without flags starts a guided question flow: provider -> server bot -> local bot key -> token -> verify -> role/AI binding -> default bot choice.
229
229
  - `bot edit` without flags starts a guided numbered flow: provider -> bot entry -> guided edit -> step-by-step field choices.
230
+ - `bot set-default` without flags starts a guided numbered flow: provider -> bot entry -> confirm default change.
231
+ - `bot verify` without flags starts a guided numbered flow: provider -> bot entry -> output format.
232
+ - `bot remove` without flags starts a guided numbered flow: provider -> bot entry -> confirm removal.
230
233
  - Telegram supports named local bot entries with:
231
234
  - `SERVER_BOT_ID`
232
235
  - `USERNAME`
@@ -182,6 +182,26 @@ async function promptYesNo(ui, promptText, defaultValue = true) {
182
182
  }
183
183
  }
184
184
 
185
+ async function promptConfirmChoice(
186
+ ui,
187
+ title,
188
+ {
189
+ confirmLabel = "Confirm",
190
+ confirmDescription = "",
191
+ cancelLabel = "Cancel",
192
+ cancelDescription = "",
193
+ defaultValue = "confirm",
194
+ } = {},
195
+ ) {
196
+ const options = [
197
+ { value: "confirm", label: confirmLabel, description: confirmDescription },
198
+ { value: "cancel", label: cancelLabel, description: cancelDescription },
199
+ ];
200
+ const defaultIndex = defaultValue === "cancel" ? 1 : 0;
201
+ const selected = await promptChoice(ui, title, options, { defaultIndex });
202
+ return selected?.value === "confirm";
203
+ }
204
+
185
205
  function formatChoiceLabel(option) {
186
206
  return `${String(option.label || option.value || "").trim()}${option.description ? ` - ${option.description}` : ""}`;
187
207
  }
@@ -1262,7 +1282,13 @@ async function removeTelegramBot(ui, deps) {
1262
1282
  const parsed = { ...state.parsed };
1263
1283
  const selected = await chooseTelegramEntry(ui, parsed, deps, "Select Telegram bot entry to remove");
1264
1284
  if (!selected) return;
1265
- if (!await promptYesNo(ui, `Remove Telegram bot "${selected.key}"?`, false)) {
1285
+ if (!await promptConfirmChoice(ui, `Remove Telegram bot "${selected.key}"?`, {
1286
+ confirmLabel: "Remove bot",
1287
+ confirmDescription: "delete this local Telegram bot entry",
1288
+ cancelLabel: "Cancel",
1289
+ cancelDescription: "keep the current local bot entry",
1290
+ defaultValue: "cancel",
1291
+ })) {
1266
1292
  process.stdout.write("Cancelled.\n");
1267
1293
  return;
1268
1294
  }
@@ -1338,7 +1364,23 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
1338
1364
  }
1339
1365
  }
1340
1366
  }
1341
- if (boolFromRaw(flags.json, false)) {
1367
+ const interactiveJsonChoice = (
1368
+ !boolFromRaw(flags.json, false)
1369
+ && !boolFromRaw(flags["non-interactive"] ?? flags.yes, false)
1370
+ )
1371
+ ? await promptChoice(
1372
+ ui,
1373
+ "Verification output format",
1374
+ [
1375
+ { value: "text", label: "Text output (Recommended)", description: "human-readable summary" },
1376
+ { value: "json", label: "JSON output", description: "machine-readable verification payload" },
1377
+ ],
1378
+ { defaultIndex: 0 },
1379
+ )
1380
+ : null;
1381
+ const outputAsJson = boolFromRaw(flags.json, false) || interactiveJsonChoice?.value === "json";
1382
+
1383
+ if (outputAsJson) {
1342
1384
  process.stdout.write(
1343
1385
  `${JSON.stringify({
1344
1386
  ok: overallOK,
@@ -1594,9 +1636,7 @@ async function runBotVerify(ui, flags, deps) {
1594
1636
  }
1595
1637
 
1596
1638
  async function runBotSetDefault(ui, flags, deps) {
1597
- const provider = String(flags.provider || "").trim()
1598
- ? requireDependency(deps, "normalizeBotProvider")(flags.provider)
1599
- : "telegram";
1639
+ const provider = await selectProvider(ui, flags.provider, deps);
1600
1640
  if (provider !== "telegram") {
1601
1641
  throw new Error("bot set-default currently supports only --provider telegram");
1602
1642
  }
@@ -1609,6 +1649,16 @@ async function runBotSetDefault(ui, flags, deps) {
1609
1649
  if (!selected) {
1610
1650
  throw new Error("Telegram bot selector is required for set-default");
1611
1651
  }
1652
+ if (!nonInteractive && !await promptConfirmChoice(ui, `Set "${selected.key}" as TELEGRAM_DEFAULT_BOT_KEY?`, {
1653
+ confirmLabel: "Set default bot",
1654
+ confirmDescription: "make this the default local Telegram bot entry",
1655
+ cancelLabel: "Cancel",
1656
+ cancelDescription: "leave the current default unchanged",
1657
+ defaultValue: "confirm",
1658
+ })) {
1659
+ process.stdout.write("Cancelled.\n");
1660
+ return;
1661
+ }
1612
1662
  parsed.TELEGRAM_DEFAULT_BOT_KEY = selected.key;
1613
1663
  const filePath = writeProviderEnvState("telegram", parsed, deps);
1614
1664
  process.stdout.write(`Set TELEGRAM_DEFAULT_BOT_KEY=${selected.key} in ${filePath}\n`);
@@ -45,6 +45,45 @@ function readJSON(rawText) {
45
45
  return JSON.parse(String(rawText || "").trim() || "{}");
46
46
  }
47
47
 
48
+ function readTrailingJSON(rawText) {
49
+ const text = String(rawText || "");
50
+ const start = text.indexOf("{");
51
+ if (start < 0) {
52
+ return readJSON(text);
53
+ }
54
+ let depth = 0;
55
+ let inString = false;
56
+ let escaping = false;
57
+ for (let index = start; index < text.length; index += 1) {
58
+ const char = text[index];
59
+ if (inString) {
60
+ if (escaping) {
61
+ escaping = false;
62
+ } else if (char === "\\") {
63
+ escaping = true;
64
+ } else if (char === "\"") {
65
+ inString = false;
66
+ }
67
+ continue;
68
+ }
69
+ if (char === "\"") {
70
+ inString = true;
71
+ continue;
72
+ }
73
+ if (char === "{") {
74
+ depth += 1;
75
+ continue;
76
+ }
77
+ if (char === "}") {
78
+ depth -= 1;
79
+ if (depth === 0) {
80
+ return readJSON(text.slice(start, index + 1));
81
+ }
82
+ }
83
+ }
84
+ return readJSON(text.slice(start));
85
+ }
86
+
48
87
  function createMockServer() {
49
88
  const serverBots = [
50
89
  {
@@ -338,6 +377,50 @@ export async function runSelftestBotCommands(push, deps) {
338
377
  `client=${String(editedState.TELEGRAM_BOT_MAIN_TEST_AI_CLIENT || "")} model=${String(editedState.TELEGRAM_BOT_MAIN_TEST_AI_MODEL || "")}`,
339
378
  );
340
379
 
380
+ await runCLI({
381
+ cliPath,
382
+ args: ["bot", "set-default"],
383
+ env: {
384
+ ...env,
385
+ METHEUS_SCRIPTED_PROMPT_ANSWERS: JSON.stringify([
386
+ "1", // provider: telegram
387
+ "1", // bot entry: main_test
388
+ "1", // confirm set default
389
+ ]),
390
+ },
391
+ });
392
+ const guidedDefaultState = parseSimpleEnvText(fs.readFileSync(telegramEnvPath, "utf8"));
393
+ push(
394
+ "bot_set_default_guided_selects_entry",
395
+ String(guidedDefaultState.TELEGRAM_DEFAULT_BOT_KEY || "") === "main_test",
396
+ `default=${String(guidedDefaultState.TELEGRAM_DEFAULT_BOT_KEY || "")}`,
397
+ );
398
+
399
+ const guidedVerifyResult = await runCLI({
400
+ cliPath,
401
+ args: [
402
+ "bot", "verify",
403
+ "--base-url", baseURL,
404
+ "--timeout-seconds", "5",
405
+ ],
406
+ env: {
407
+ ...env,
408
+ METHEUS_SCRIPTED_PROMPT_ANSWERS: JSON.stringify([
409
+ "1", // provider: telegram
410
+ "1", // bot entry: main_test
411
+ "2", // output format: json
412
+ ]),
413
+ },
414
+ });
415
+ const guidedVerifyPayload = readTrailingJSON(guidedVerifyResult.stdout);
416
+ push(
417
+ "bot_verify_guided_can_emit_json",
418
+ guidedVerifyPayload.ok === true
419
+ && safeObject(guidedVerifyPayload.serverBinding).ok === true
420
+ && String(guidedVerifyPayload.client || "") === "claude",
421
+ `verify=${String(guidedVerifyPayload.ok)} server=${String(safeObject(guidedVerifyPayload.serverBinding).detail || "")}`,
422
+ );
423
+
341
424
  await runCLI({
342
425
  cliPath,
343
426
  args: [
@@ -435,16 +518,49 @@ export async function runSelftestBotCommands(push, deps) {
435
518
  `client=${String(aliasAddState.TELEGRAM_BOT_EXPLICIT_TEST_AI_CLIENT || "")} model=${String(aliasAddState.TELEGRAM_BOT_EXPLICIT_TEST_AI_MODEL || "")}`,
436
519
  );
437
520
 
521
+ const guidedRemoveBeforeList = await runCLI({
522
+ cliPath,
523
+ args: [
524
+ "bot", "list",
525
+ "--provider", "telegram",
526
+ "--json", "true",
527
+ ],
528
+ env,
529
+ });
530
+ const guidedRemoveBeforePayload = ensureArray(readJSON(guidedRemoveBeforeList.stdout));
531
+ const guidedRemoveBeforeEntry = safeObject(guidedRemoveBeforePayload[0]);
532
+ const guidedRemoveEntries = ensureArray(guidedRemoveBeforeEntry.entries);
533
+ const explicitEntryIndex = guidedRemoveEntries.findIndex((entry) => String(safeObject(entry).key || "") === "explicit_test");
534
+
438
535
  await runCLI({
536
+ cliPath,
537
+ args: ["bot", "remove"],
538
+ env: {
539
+ ...env,
540
+ METHEUS_SCRIPTED_PROMPT_ANSWERS: JSON.stringify([
541
+ "1", // provider: telegram
542
+ String(explicitEntryIndex + 1), // bot entry: explicit_test
543
+ "1", // confirm remove
544
+ ]),
545
+ },
546
+ });
547
+ const guidedRemoveList = await runCLI({
439
548
  cliPath,
440
549
  args: [
441
- "bot", "remove",
550
+ "bot", "list",
442
551
  "--provider", "telegram",
443
- "--bot-key", "explicit_test",
444
- "--non-interactive", "true",
552
+ "--json", "true",
445
553
  ],
446
554
  env,
447
555
  });
556
+ const guidedRemovePayload = ensureArray(readJSON(guidedRemoveList.stdout));
557
+ const guidedRemoveEntry = safeObject(guidedRemovePayload[0]);
558
+ push(
559
+ "bot_remove_guided_deletes_selected_entry",
560
+ explicitEntryIndex >= 0
561
+ && ensureArray(guidedRemoveEntry.entries).length === 0,
562
+ `entries=${String(ensureArray(guidedRemoveEntry.entries).length)} selected=${String(explicitEntryIndex + 1)}`,
563
+ );
448
564
 
449
565
  const migratedEnv = parseSimpleEnvText(fs.readFileSync(telegramEnvPath, "utf8"));
450
566
  migratedEnv.TELEGRAM_BOT_TOKEN = "legacy-selftest-token";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.65",
3
+ "version": "0.2.66",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [