metheus-governance-mcp-cli 0.2.101 → 0.2.102

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
@@ -58,8 +58,11 @@ TELEGRAM_BOT_TOKEN=
58
58
 
59
59
  ```env
60
60
  # ~/.metheus/telegram-bots/ryoai_bot.env
61
- TELEGRAM_BOT_NAME=ryoai_bot
62
- TELEGRAM_BOT_SERVER_BOT_ID=
61
+ TELEGRAM_BOT_SERVER_BOT_ID=<server_bot_uuid>
62
+ TELEGRAM_BOT_SERVER_NAME=RyoAI_bot
63
+ TELEGRAM_BOT_SERVER_ROLES=monitor,review,worker,approval
64
+ # Optional fallback only when server bot binding is unavailable
65
+ # TELEGRAM_BOT_USERNAME=ryoai_bot
63
66
  TELEGRAM_BOT_TOKEN=
64
67
  TELEGRAM_BOT_ROLE_PROFILE=
65
68
  TELEGRAM_BOT_AI_CLIENT=
@@ -223,15 +226,15 @@ Direct commands:
223
226
 
224
227
  ```bash
225
228
  metheus-governance-mcp-cli bot list
226
- metheus-governance-mcp-cli bot show --provider telegram --bot-key ryoai_bot
229
+ metheus-governance-mcp-cli bot show --provider telegram --bot-name RyoAI_bot
227
230
  metheus-governance-mcp-cli bot add --provider telegram
228
231
  metheus-governance-mcp-cli bot edit
229
232
  metheus-governance-mcp-cli bot edit --provider telegram
230
233
  metheus-governance-mcp-cli bot remove --provider telegram
231
- metheus-governance-mcp-cli bot set-default --provider telegram --bot-key ryoai_bot
232
- metheus-governance-mcp-cli bot migrate --provider telegram --bot-key ryoai_bot
234
+ metheus-governance-mcp-cli bot set-default --provider telegram --bot-name RyoAI_bot
235
+ metheus-governance-mcp-cli bot migrate --provider telegram --bot-name RyoAI_bot
233
236
  metheus-governance-mcp-cli bot global --provider telegram
234
- metheus-governance-mcp-cli bot verify --provider telegram --bot-key ryoai_bot
237
+ metheus-governance-mcp-cli bot verify --provider telegram --bot-name RyoAI_bot
235
238
  ```
236
239
 
237
240
  Behavior:
@@ -259,8 +262,10 @@ Behavior:
259
262
  - `bot verify` without flags starts a guided numbered flow: provider -> bot entry -> output format.
260
263
  - `bot remove` without flags starts a guided numbered flow: provider -> bot entry -> confirm removal.
261
264
  - Telegram stores one bot file per entry under `~/.metheus/telegram-bots/<server-bot-name>.env` with generic fields:
262
- - `TELEGRAM_BOT_NAME`
263
265
  - `TELEGRAM_BOT_SERVER_BOT_ID`
266
+ - `TELEGRAM_BOT_SERVER_NAME`
267
+ - `TELEGRAM_BOT_SERVER_ROLES`
268
+ - `TELEGRAM_BOT_USERNAME` only as a fallback when server bot binding is unavailable
264
269
  - `TELEGRAM_BOT_TOKEN`
265
270
  - `TELEGRAM_BOT_ROLE_PROFILE`
266
271
  - `TELEGRAM_BOT_AI_CLIENT`
@@ -270,6 +275,7 @@ Behavior:
270
275
  - Slack and KakaoTalk currently use a single local token entry per provider in this command flow.
271
276
  - `bot verify` checks the configured local token, cross-checks the server bot binding, prints the effective runtime role profile summary that the runner will use, and shows which local runner routes currently point at this bot entry.
272
277
  - `bot show` prints one local bot entry in detail, including grouped role summaries when one server bot name expands to multiple roles and any linked local runner routes.
278
+ - In `bot show` and `bot verify`, `stored_*` fields come from the local env file and `live_server_*` fields come from the current server `me/bots` response.
273
279
  - `bot global` edits Telegram-wide local settings such as API base URL, allowed updates, and default bot key.
274
280
  - `bot set-default` updates `TELEGRAM_DEFAULT_BOT_KEY`.
275
281
  - `bot migrate` moves legacy `TELEGRAM_BOT_TOKEN` into a named Telegram bot entry.
@@ -280,16 +286,16 @@ Non-interactive examples:
280
286
  metheus-governance-mcp-cli bot global --provider telegram --non-interactive true --api-base-url http://127.0.0.1:8999/telegram --auto-clear-webhook false --allowed-updates message,edited_message,channel_post
281
287
  metheus-governance-mcp-cli bot add --provider telegram --non-interactive true --server-bot-id <server_bot_uuid> --token <telegram_bot_token> --default true
282
288
  metheus-governance-mcp-cli bot add --provider telegram --non-interactive true --server-bot-id <server_bot_uuid> --token <telegram_bot_token> --ai-client gpt --ai-model "gpt-5.4" --ai-permission-mode read_only --ai-reasoning-effort low
283
- metheus-governance-mcp-cli bot edit --provider telegram --bot-key ryoai_bot --non-interactive true --ai-client claude --ai-model "Sonnet 4.6r" --ai-permission-mode danger_full_access --ai-reasoning-effort high
284
- metheus-governance-mcp-cli bot set-default --provider telegram --bot-key ryoai_bot --non-interactive true
285
- metheus-governance-mcp-cli bot migrate --provider telegram --bot-key ryoai_bot --server-bot-id <server_bot_uuid> --bot-name <telegram_username> --role-profile monitor --ai-client gpt --ai-permission-mode read_only --ai-reasoning-effort low --non-interactive true
286
- metheus-governance-mcp-cli bot remove --provider telegram --bot-key ryoai_bot --non-interactive true
287
- metheus-governance-mcp-cli bot verify --provider telegram --bot-key ryoai_bot --json true
289
+ metheus-governance-mcp-cli bot edit --provider telegram --bot-name RyoAI_bot --non-interactive true --ai-client claude --ai-model "Sonnet 4.6r" --ai-permission-mode danger_full_access --ai-reasoning-effort high
290
+ metheus-governance-mcp-cli bot set-default --provider telegram --bot-name RyoAI_bot --non-interactive true
291
+ metheus-governance-mcp-cli bot migrate --provider telegram --bot-name RyoAI_bot --server-bot-id <server_bot_uuid> --role-profile monitor --ai-client gpt --ai-permission-mode read_only --ai-reasoning-effort low --non-interactive true
292
+ metheus-governance-mcp-cli bot remove --provider telegram --bot-name RyoAI_bot --non-interactive true
293
+ metheus-governance-mcp-cli bot verify --provider telegram --bot-name RyoAI_bot --json true
288
294
  ```
289
295
 
290
- For direct Telegram adds, the CLI can derive the local entry key from the matched server bot name. You no longer need `--bot-key` or `--username` in the normal server-bound path. Treat `--bot-key` as an advanced override only when you intentionally want a different local suffix.
296
+ For direct Telegram adds, the CLI derives the local file name from the matched server bot name. You normally do not need `--bot-key` or `--username`. Treat `--bot-key` as an advanced compatibility selector only when you intentionally need a different local file stem.
291
297
 
292
- For direct Telegram edits, `--bot-key` still identifies which saved local entry to update. If one server bot name expands to multiple roles such as `approval / worker / review / monitor`, prefer the guided `bot edit` flow so you can keep the current grouped settings, edit one role only, or walk every role in sequence instead of forcing one entry-level AI override.
298
+ For direct Telegram edits, prefer `--bot-name` or `--bot-id` because server bot identity is the source of truth. Use `--bot-key` only when you intentionally need the advanced local selector. If one server bot name expands to multiple roles such as `approval / worker / review / monitor`, prefer the guided `bot edit` flow so you can keep the current grouped settings, edit one role only, or walk every role in sequence instead of forcing one entry-level AI override.
293
299
 
294
300
  Current support status:
295
301
 
package/cli.mjs CHANGED
@@ -241,13 +241,13 @@ function printUsage() {
241
241
  ` ${cmd} setup [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--workspace-dir <path|auto>] [--workspace-fallback-dir <path>] [--name <server_name>]`,
242
242
  ` ${cmd} bot setup [--provider <telegram|slack|kakaotalk>] [--base-url <url>] [--timeout-seconds <n>]`,
243
243
  ` ${cmd} bot list [--provider <telegram|slack|kakaotalk>] [--json <true|false>]`,
244
- ` ${cmd} bot show [--provider <telegram|slack|kakaotalk>] [--bot-key <key>] [--bot-id <uuid>] [--bot-name <name>] [--json <true|false>]`,
244
+ ` ${cmd} bot show [--provider <telegram|slack|kakaotalk>] [--bot-name <server_name>] [--bot-id <uuid>] [--bot-key <advanced_key>] [--json <true|false>]`,
245
245
  ` ${cmd} bot add [--provider <telegram|slack|kakaotalk>] [--base-url <url>] [--timeout-seconds <n>]`,
246
246
  ` ${cmd} bot edit [--provider <telegram|slack|kakaotalk>] [--base-url <url>] [--timeout-seconds <n>]`,
247
247
  ` ${cmd} bot remove [--provider <telegram|slack|kakaotalk>]`,
248
- ` ${cmd} bot set-default --provider telegram [--bot-key <key>]`,
249
- ` ${cmd} bot migrate --provider telegram [--bot-key <key>] [--bot-id <uuid>] [--bot-name <name>]`,
250
- ` ${cmd} bot verify [--provider <telegram|slack|kakaotalk>] [--bot-key <key>] [--timeout-seconds <n>] [--json <true|false>]`,
248
+ ` ${cmd} bot set-default --provider telegram [--bot-name <server_name>] [--bot-id <uuid>] [--bot-key <advanced_key>]`,
249
+ ` ${cmd} bot migrate --provider telegram [--bot-name <server_name>] [--bot-id <uuid>] [--bot-key <advanced_key>]`,
250
+ ` ${cmd} bot verify [--provider <telegram|slack|kakaotalk>] [--bot-name <server_name>] [--bot-id <uuid>] [--bot-key <advanced_key>] [--timeout-seconds <n>] [--json <true|false>]`,
251
251
  ` ${cmd} bot global --provider telegram [--api-base-url <url>] [--auto-clear-webhook <true|false>] [--allowed-updates <csv>] [--default-bot-key <key>]`,
252
252
  ` ${cmd} doctor [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--timeout-seconds <n>] [--strict <true|false>]`,
253
253
  ` ${cmd} proxy [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--workspace-dir <path|auto>] [--include-drafts <true|false>] [--auto-pull-on-conflict <true|false>] [--timeout-seconds <n>]`,
@@ -759,7 +759,7 @@ function providerEnvTemplate(provider) {
759
759
  "# Keep this file on your machine only. Do not commit it.",
760
760
  "# Store Telegram-wide settings here.",
761
761
  "# This global file now lives in ~/.metheus/telegram-bots/_global.env.",
762
- "# Per-bot secrets and AI settings live in ~/.metheus/telegram-bots/<bot-key>.env.",
762
+ "# Per-bot secrets and AI settings live in ~/.metheus/telegram-bots/<server-bot-name>.env.",
763
763
  "# Project chat destinations must be managed on the Metheus server.",
764
764
  "",
765
765
  "TELEGRAM_API_BASE_URL=",
@@ -2778,10 +2778,13 @@ function parseTelegramBotEntryFile(filePath) {
2778
2778
  const raw = fs.readFileSync(filePath, "utf8");
2779
2779
  const parsed = parseSimpleEnvText(raw);
2780
2780
  const keyFromFile = normalizeTelegramBotEnvKey(path.basename(filePath, path.extname(filePath)), "telegram_bot");
2781
- const keyFromName = normalizeTelegramBotEnvKey(parsed.TELEGRAM_BOT_NAME || parsed.TELEGRAM_BOT_USERNAME || "", "");
2781
+ const keyFromName = normalizeTelegramBotEnvKey(
2782
+ parsed.TELEGRAM_BOT_USERNAME || parsed.TELEGRAM_BOT_NAME || parsed.TELEGRAM_BOT_SERVER_NAME || "",
2783
+ "",
2784
+ );
2782
2785
  return {
2783
- key: keyFromName || keyFromFile,
2784
- username: normalizeTelegramBotUsername(parsed.TELEGRAM_BOT_NAME || parsed.TELEGRAM_BOT_USERNAME || ""),
2786
+ key: keyFromFile || keyFromName,
2787
+ username: normalizeTelegramBotUsername(parsed.TELEGRAM_BOT_USERNAME || parsed.TELEGRAM_BOT_NAME || ""),
2785
2788
  serverBotID: String(parsed.TELEGRAM_BOT_SERVER_BOT_ID || "").trim(),
2786
2789
  serverBotName: String(parsed.TELEGRAM_BOT_SERVER_NAME || "").trim(),
2787
2790
  serverRoles: parseTelegramServerRoles(parsed.TELEGRAM_BOT_SERVER_ROLES || ""),
@@ -2933,7 +2936,7 @@ function renderNormalizedTelegramEnv(parsedEnv) {
2933
2936
  "# Keep this file on your machine only. Do not commit it.",
2934
2937
  "# Store Telegram-wide settings here.",
2935
2938
  "# This global file lives in ~/.metheus/telegram-bots/_global.env.",
2936
- "# Per-bot secrets and AI settings live in ~/.metheus/telegram-bots/<bot-key>.env.",
2939
+ "# Per-bot secrets and AI settings live in ~/.metheus/telegram-bots/<server-bot-name>.env.",
2937
2940
  "",
2938
2941
  `TELEGRAM_API_BASE_URL=${formatProviderEnvValue(parsed.TELEGRAM_API_BASE_URL || "")}`,
2939
2942
  `TELEGRAM_AUTO_CLEAR_WEBHOOK=${boolFromRaw(parsed.TELEGRAM_AUTO_CLEAR_WEBHOOK, true) ? "true" : "false"}`,
@@ -2970,12 +2973,19 @@ function renderTelegramBotEntryEnv(entryRaw) {
2970
2973
  const serverRoles = parseTelegramServerRoles(entry.serverRoles || "");
2971
2974
  const lines = [
2972
2975
  "# Metheus local Telegram bot entry",
2973
- `# Bot: ${botName}`,
2976
+ `# Server bot: ${botName}`,
2974
2977
  "",
2975
- `TELEGRAM_BOT_NAME=${formatProviderEnvValue(botName)}`,
2976
2978
  `TELEGRAM_BOT_SERVER_BOT_ID=${formatProviderEnvValue(entry.serverBotID || "")}`,
2977
2979
  `TELEGRAM_BOT_SERVER_NAME=${formatProviderEnvValue(entry.serverBotName || "")}`,
2978
2980
  `TELEGRAM_BOT_SERVER_ROLES=${formatProviderEnvValue(serverRoles.join(","))}`,
2981
+ ];
2982
+ if (!String(entry.serverBotID || "").trim() && !String(entry.serverBotName || "").trim() && String(entry.username || "").trim()) {
2983
+ lines.push(
2984
+ "# Fallback only when server bot binding is unavailable",
2985
+ `TELEGRAM_BOT_USERNAME=${formatProviderEnvValue(entry.username || "")}`,
2986
+ );
2987
+ }
2988
+ lines.push(
2979
2989
  `TELEGRAM_BOT_TOKEN=${formatProviderEnvValue(entry.token || "")}`,
2980
2990
  `TELEGRAM_BOT_ROLE_PROFILE=${formatProviderEnvValue(entry.roleProfile || "")}`,
2981
2991
  `TELEGRAM_BOT_AI_CLIENT=${formatProviderEnvValue(entry.client || "")}`,
@@ -2983,7 +2993,7 @@ function renderTelegramBotEntryEnv(entryRaw) {
2983
2993
  `TELEGRAM_BOT_AI_PERMISSION_MODE=${formatProviderEnvValue(entry.permissionMode || "")}`,
2984
2994
  `TELEGRAM_BOT_AI_REASONING_EFFORT=${formatProviderEnvValue(entry.reasoningEffort || "")}`,
2985
2995
  "",
2986
- ];
2996
+ );
2987
2997
  return `${lines.join("\n").trimEnd()}\n`;
2988
2998
  }
2989
2999
 
@@ -5523,8 +5533,8 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
5523
5533
  normalizedGlobalText.includes("TELEGRAM_DEFAULT_BOT_KEY=ryoai_bot")
5524
5534
  && !normalizedGlobalText.includes("TELEGRAM_BOT_RYOAI_BOT_TOKEN")
5525
5535
  && !normalizedGlobalText.includes("TELEGRAM_BOT_RYOAI_TOKEN")
5526
- && normalizedBotText.includes("TELEGRAM_BOT_NAME=ryoai_bot")
5527
5536
  && normalizedBotText.includes("TELEGRAM_BOT_SERVER_BOT_ID=bot-ryoai")
5537
+ && normalizedBotText.includes("TELEGRAM_BOT_SERVER_NAME=")
5528
5538
  && normalizedBotText.includes("TELEGRAM_BOT_TOKEN=ryoai-token"),
5529
5539
  `${normalizedGlobalText}\n---\n${normalizedBotText}`,
5530
5540
  );
@@ -216,20 +216,21 @@ function printBotUsage(deps) {
216
216
  "",
217
217
  ` ${cliName} bot setup`,
218
218
  ` ${cliName} bot list [--provider <telegram|slack|kakaotalk>] [--json <true|false>]`,
219
- ` ${cliName} bot show [--provider <telegram|slack|kakaotalk>] [--bot-key <key>] [--bot-id <uuid>] [--bot-name <name>] [--json <true|false>]`,
219
+ ` ${cliName} bot show [--provider <telegram|slack|kakaotalk>] [--bot-name <server_name>] [--bot-id <uuid>] [--bot-key <advanced_key>] [--json <true|false>]`,
220
220
  ` ${cliName} bot add [--provider <telegram|slack|kakaotalk>] [--base-url <url>] [--timeout-seconds <n>] [--non-interactive <true|false>] [--server-bot-id <uuid>] [--token <token>] [--default <true|false>] [--verify <true|false>] [--ai-client <name>] [--ai-model <name>] [--ai-permission-mode <mode>] [--ai-reasoning-effort <level>] [--bot-key <advanced_key>] [--username <fallback_name>] [--role-profile <name>]`,
221
- ` ${cliName} bot edit [--provider <telegram|slack|kakaotalk>] [--base-url <url>] [--timeout-seconds <n>] [--non-interactive <true|false>] [--bot-key <key>] [--server-bot-id <uuid>] [--username <name>] [--token <token>] [--role-profile <name>] [--ai-client <name>] [--ai-model <name>] [--ai-permission-mode <mode>] [--ai-reasoning-effort <level>]`,
222
- ` ${cliName} bot remove [--provider <telegram|slack|kakaotalk>] [--non-interactive <true|false>] [--bot-key <key>]`,
223
- ` ${cliName} bot set-default --provider telegram [--bot-key <key>] [--non-interactive <true|false>]`,
224
- ` ${cliName} bot migrate --provider telegram [--bot-key <key>] [--bot-id <uuid>] [--bot-name <name>] [--keep-legacy-token <true|false>] [--non-interactive <true|false>]`,
225
- ` ${cliName} bot verify [--provider <telegram|slack|kakaotalk>] [--bot-key <key>] [--timeout-seconds <n>] [--json <true|false>]`,
226
- ` ${cliName} bot global --provider telegram [--non-interactive <true|false>] [--api-base-url <url>] [--auto-clear-webhook <true|false>] [--allowed-updates <csv>] [--default-bot-key <key>]`,
221
+ ` ${cliName} bot edit [--provider <telegram|slack|kakaotalk>] [--base-url <url>] [--timeout-seconds <n>] [--non-interactive <true|false>] [--bot-name <server_name>] [--bot-id <uuid>] [--bot-key <advanced_key>] [--username <fallback_name>] [--token <token>] [--role-profile <name>] [--ai-client <name>] [--ai-model <name>] [--ai-permission-mode <mode>] [--ai-reasoning-effort <level>]`,
222
+ ` ${cliName} bot remove [--provider <telegram|slack|kakaotalk>] [--non-interactive <true|false>] [--bot-name <server_name>] [--bot-id <uuid>] [--bot-key <advanced_key>]`,
223
+ ` ${cliName} bot set-default --provider telegram [--bot-name <server_name>] [--bot-id <uuid>] [--bot-key <advanced_key>] [--non-interactive <true|false>]`,
224
+ ` ${cliName} bot migrate --provider telegram [--bot-name <server_name>] [--bot-id <uuid>] [--bot-key <advanced_key>] [--keep-legacy-token <true|false>] [--non-interactive <true|false>]`,
225
+ ` ${cliName} bot verify [--provider <telegram|slack|kakaotalk>] [--bot-name <server_name>] [--bot-id <uuid>] [--bot-key <advanced_key>] [--timeout-seconds <n>] [--json <true|false>]`,
226
+ ` ${cliName} bot global --provider telegram [--non-interactive <true|false>] [--api-base-url <url>] [--auto-clear-webhook <true|false>] [--allowed-updates <csv>] [--default-bot-key <advanced_key>]`,
227
227
  "",
228
228
  `Behavior:`,
229
229
  ` - bot setup asks for provider first, then shows a numbered action menu.`,
230
230
  ` - bot add without flags uses the shortest practical guided flow: provider -> token -> verify -> optional username fallback -> optional default bot.`,
231
231
  ` - in the normal Telegram path, bot add does not ask for a local bot key or a server role/profile choice.`,
232
- ` - server bot name/UUID is the source of truth; local key is auto-derived unless you intentionally override it with --bot-key.`,
232
+ ` - server bot name/UUID is the source of truth; --bot-key is an advanced local selector only.`,
233
+ ` - runner commands use --route-name, not bot name. Use bot verify/show first, then run the linked route.`,
233
234
  ` - bot edit without flags asks for provider, existing entry, then walks field-by-field in a numbered flow.`,
234
235
  ` - in the normal Telegram edit path, the CLI keeps or re-resolves the server bot binding automatically instead of asking you to pick a server role/profile UUID first.`,
235
236
  ` - bot set-default / bot verify / bot remove also support guided numbered selection when run without flags.`,
@@ -469,6 +470,8 @@ function persistTelegramUsername(entry) {
469
470
  const current = safeObject(entry);
470
471
  if (current.__preferServerIdentity) return "";
471
472
  if (String(current.serverBotID || "").trim()) return "";
473
+ if (String(current.serverBotName || "").trim()) return "";
474
+ if (ensureArray(current.serverRoles).length) return "";
472
475
  return normalizeServerBotIdentityText(current.username || "");
473
476
  }
474
477
 
@@ -631,12 +634,19 @@ function renderTelegramBotEntryFile(entryRaw) {
631
634
  const serverRoles = ensureArray(entry.serverRoles).join(",");
632
635
  const lines = [
633
636
  "# Metheus local Telegram bot entry",
634
- `# Bot: ${botName}`,
637
+ `# Server bot: ${botName}`,
635
638
  "",
636
- `TELEGRAM_BOT_NAME=${formatEnvValue(botName)}`,
637
639
  `TELEGRAM_BOT_SERVER_BOT_ID=${formatEnvValue(entry.serverBotID || "")}`,
638
640
  `TELEGRAM_BOT_SERVER_NAME=${formatEnvValue(entry.serverBotName || "")}`,
639
641
  `TELEGRAM_BOT_SERVER_ROLES=${formatEnvValue(serverRoles)}`,
642
+ ];
643
+ if (!String(entry.serverBotID || "").trim() && !String(entry.serverBotName || "").trim() && String(entry.username || "").trim()) {
644
+ lines.push(
645
+ "# Fallback only when server bot binding is unavailable",
646
+ `TELEGRAM_BOT_USERNAME=${formatEnvValue(entry.username || "")}`,
647
+ );
648
+ }
649
+ lines.push(
640
650
  `TELEGRAM_BOT_TOKEN=${formatEnvValue(entry.token || "")}`,
641
651
  `TELEGRAM_BOT_ROLE_PROFILE=${formatEnvValue(entry.roleProfile || "")}`,
642
652
  `TELEGRAM_BOT_AI_CLIENT=${formatEnvValue(entry.client || "")}`,
@@ -644,7 +654,7 @@ function renderTelegramBotEntryFile(entryRaw) {
644
654
  `TELEGRAM_BOT_AI_PERMISSION_MODE=${formatEnvValue(entry.permissionMode || "")}`,
645
655
  `TELEGRAM_BOT_AI_REASONING_EFFORT=${formatEnvValue(entry.reasoningEffort || "")}`,
646
656
  "",
647
- ];
657
+ );
648
658
  return `${lines.join("\n").trimEnd()}\n`;
649
659
  }
650
660
 
@@ -741,9 +751,9 @@ function formatRuntimeReasoningField(clientName, reasoningEffort, style = "equal
741
751
  function telegramEntryDisplayDescription(entry) {
742
752
  const current = safeObject(entry);
743
753
  const detail = [
744
- current.key ? `key:${current.key}` : "",
745
- current.serverBotID ? `server:${current.serverBotID}` : "",
746
- current.client ? `AI:${displayLocalAIClientName(current.client)}` : "",
754
+ current.key ? `local_selector:${current.key}` : "",
755
+ current.serverBotID ? `stored_server_bot_id:${current.serverBotID}` : "",
756
+ current.client ? `ai_client:${displayLocalAIClientName(current.client)}` : "",
747
757
  ].filter(Boolean);
748
758
  return detail.join(" ");
749
759
  }
@@ -752,7 +762,9 @@ function findTelegramEntryByFlags(parsedEnv, flags, deps) {
752
762
  const entries = telegramEntriesForDisplay(parsedEnv, deps);
753
763
  const requestedKey = String(flags["bot-key"] || "").trim();
754
764
  const requestedBotID = String(flags["bot-id"] || flags["server-bot-id"] || "").trim();
755
- const requestedName = requireDependency(deps, "normalizeTelegramBotUsername")(flags["bot-name"] || flags.username || "");
765
+ const requestedNameRaw = String(flags["bot-name"] || flags.username || "").trim();
766
+ const requestedUsername = requireDependency(deps, "normalizeTelegramBotUsername")(requestedNameRaw);
767
+ const requestedIdentity = normalizeServerBotIdentityText(requestedNameRaw);
756
768
  if (requestedKey) {
757
769
  const match = entries.find((entry) => entry.key === requestedKey);
758
770
  if (!match) {
@@ -767,16 +779,16 @@ function findTelegramEntryByFlags(parsedEnv, flags, deps) {
767
779
  }
768
780
  return match;
769
781
  }
770
- if (requestedName) {
782
+ if (requestedNameRaw) {
771
783
  const match = entries.find(
772
784
  (entry) => (
773
- entry.username === requestedName
774
- || normalizeServerBotIdentityText(entry.serverBotName) === requestedName
775
- || normalizeServerBotIdentityText(entry.key) === requestedName
785
+ entry.username === requestedUsername
786
+ || normalizeServerBotIdentityText(entry.serverBotName) === requestedIdentity
787
+ || normalizeServerBotIdentityText(entry.key) === requestedIdentity
776
788
  ),
777
789
  );
778
790
  if (!match) {
779
- throw new Error(`Telegram bot entry with server bot name "${requestedName}" was not found`);
791
+ throw new Error(`Telegram bot entry with server bot name "${requestedNameRaw}" was not found`);
780
792
  }
781
793
  return match;
782
794
  }
@@ -1198,9 +1210,9 @@ function printBotList(provider, state, deps) {
1198
1210
  process.stdout.write(
1199
1211
  [
1200
1212
  ` - ${telegramEntryDisplayName(entry)}${entry.isDefault ? " [default]" : ""}`,
1201
- ` key: ${entry.key || "-"}`,
1213
+ ` local_selector: ${entry.key || "-"}${entry.key ? " (advanced)" : ""}`,
1202
1214
  ` server_bot_id: ${entry.serverBotID || "-"}`,
1203
- ` username: ${entry.username ? `@${entry.username}` : "-"}`,
1215
+ ` fallback_username: ${entry.username ? `@${entry.username}` : "-"}`,
1204
1216
  ` token: ${entry.token ? maskSecret(entry.token) : "-"}`,
1205
1217
  ` role_profile: ${entry.roleProfile || "-"}`,
1206
1218
  ` ai_client: ${entry.client ? displayLocalAIClientName(entry.client) : "-"}`,
@@ -1224,11 +1236,13 @@ function printBotShow(provider, state, entry, deps, extras = {}) {
1224
1236
  const routeLinks = safeObject(extras.routeLinks);
1225
1237
  process.stdout.write(`${providerLabel(provider, deps)} bot\n`);
1226
1238
  process.stdout.write(` file: ${state.filePath}\n`);
1227
- process.stdout.write(` name: ${telegramEntryDisplayName(selectedEntry)}\n`);
1228
- process.stdout.write(` key: ${selectedEntry.key || "-"}\n`);
1239
+ process.stdout.write(` server_name: ${telegramEntryDisplayName(selectedEntry)}\n`);
1240
+ process.stdout.write(` local_selector: ${selectedEntry.key || "-"}${selectedEntry.key ? " (advanced)" : ""}\n`);
1229
1241
  process.stdout.write(` default: ${selectedEntry.isDefault ? "yes" : "no"}\n`);
1230
- process.stdout.write(` server_bot_id: ${selectedEntry.serverBotID || "-"}\n`);
1231
- process.stdout.write(` username: ${selectedEntry.username ? `@${selectedEntry.username}` : "-"}\n`);
1242
+ process.stdout.write(` stored_server_bot_id: ${selectedEntry.serverBotID || "-"}\n`);
1243
+ process.stdout.write(` stored_server_name: ${selectedEntry.serverBotName || "-"}\n`);
1244
+ process.stdout.write(` stored_server_roles: ${ensureArray(selectedEntry.serverRoles).join(", ") || "-" }\n`);
1245
+ process.stdout.write(` fallback_username: ${selectedEntry.username ? `@${selectedEntry.username}` : "-"}\n`);
1232
1246
  process.stdout.write(` token: ${selectedEntry.token ? maskSecret(selectedEntry.token) : "(not configured)"}\n`);
1233
1247
  process.stdout.write(` role_profile: ${selectedEntry.roleProfile || "-"}\n`);
1234
1248
  process.stdout.write(` ai_client: ${selectedEntry.client ? displayLocalAIClientName(selectedEntry.client) : "-"}\n`);
@@ -1237,17 +1251,17 @@ function printBotShow(provider, state, entry, deps, extras = {}) {
1237
1251
  process.stdout.write(` reasoning_effort: ${selectedEntry.reasoningEffort || "-"}\n`);
1238
1252
  if (Object.keys(serverBinding).length) {
1239
1253
  process.stdout.write(` server_binding: ${serverBinding.ok ? "OK" : "FAIL"}${serverBinding.detail ? ` (${serverBinding.detail})` : ""}\n`);
1240
- process.stdout.write(` server_binding_mode: ${serverBinding.mode || "-"}\n`);
1241
- process.stdout.write(` server_bot_name: ${serverBinding.name || "-"}\n`);
1242
- process.stdout.write(` server_bot_id: ${serverBinding.serverBotID || selectedEntry.serverBotID || "-" }\n`);
1254
+ process.stdout.write(` binding_mode: ${serverBinding.mode || "-"}\n`);
1255
+ process.stdout.write(` live_server_name: ${serverBinding.name || "-"}\n`);
1256
+ process.stdout.write(` live_server_bot_id: ${serverBinding.serverBotID || selectedEntry.serverBotID || "-" }\n`);
1243
1257
  if (serverBinding.mode === "group") {
1244
- process.stdout.write(` server_roles: ${ensureArray(serverBinding.roles).join(", ") || "-"}\n`);
1258
+ process.stdout.write(` live_server_roles: ${ensureArray(serverBinding.roles).join(", ") || "-"}\n`);
1245
1259
  const groupedProfiles = safeObject(serverBinding.effectiveRoleProfiles);
1246
1260
  Object.keys(groupedProfiles).forEach((role) => {
1247
1261
  process.stdout.write(` runtime_role_profile[${role}]: ${formatRoleProfileOutputLine(groupedProfiles[role])}\n`);
1248
1262
  });
1249
1263
  } else if (serverBinding.effectiveRoleProfile) {
1250
- process.stdout.write(` server_role: ${serverBinding.role || "-"}\n`);
1264
+ process.stdout.write(` live_server_role: ${serverBinding.role || "-"}\n`);
1251
1265
  process.stdout.write(` runtime_role_profile: ${formatRoleProfileOutputLine(serverBinding.effectiveRoleProfile)}\n`);
1252
1266
  }
1253
1267
  }
@@ -2105,9 +2119,9 @@ async function buildTelegramEntrySelectionRows(entries, flags, deps) {
2105
2119
  label = serverName;
2106
2120
  }
2107
2121
  const descriptionParts = [
2108
- current.key ? `local:${current.key}` : "",
2109
- current.serverBotID ? `server:${current.serverBotID}` : "",
2110
- current.client ? `AI:${displayLocalAIClientName(current.client)}` : "",
2122
+ current.key ? `local_selector:${current.key} (advanced)` : "",
2123
+ current.serverBotID ? `stored_server_bot_id:${current.serverBotID}` : "",
2124
+ current.client ? `ai_client:${displayLocalAIClientName(current.client)}` : "",
2111
2125
  ].filter(Boolean);
2112
2126
  return {
2113
2127
  value: current.key,
@@ -2588,6 +2602,25 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
2588
2602
  envConfig.serverBotName = desiredServerBotName;
2589
2603
  envConfig.serverRoles = desiredServerRoles;
2590
2604
  }
2605
+ const rewriteEntry = needsMetadataBackfill
2606
+ ? {
2607
+ ...selectedEntry,
2608
+ serverBotID: desiredServerBotID,
2609
+ serverBotName: desiredServerBotName,
2610
+ serverRoles: desiredServerRoles,
2611
+ }
2612
+ : selectedEntry;
2613
+ const currentEntryFilePath = String(
2614
+ rewriteEntry.entryFilePath
2615
+ || requireDependency(deps, "telegramBotEntryFilePath")(rewriteEntry.key),
2616
+ ).trim();
2617
+ const expectedEntryText = renderTelegramBotEntryFile(rewriteEntry);
2618
+ const currentEntryText = currentEntryFilePath && fs.existsSync(currentEntryFilePath)
2619
+ ? fs.readFileSync(currentEntryFilePath, "utf8")
2620
+ : "";
2621
+ if (currentEntryText !== expectedEntryText) {
2622
+ rewriteTelegramEntryFile(rewriteEntry, deps);
2623
+ }
2591
2624
  }
2592
2625
  }
2593
2626
  routeLinks = summarizeTelegramRouteLinks(state.parsed, {
@@ -2636,8 +2669,8 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
2636
2669
  const lines = [
2637
2670
  `${providerLabel(provider, deps)} verify: ${overallOK ? "OK" : "FAIL"}`,
2638
2671
  `file: ${envConfig.filePath}`,
2639
- provider === "telegram" ? `bot_key: ${envConfig.botKey || "-"}` : "",
2640
- provider === "telegram" ? `server_bot_id: ${envConfig.serverBotID || "-"}` : "",
2672
+ provider === "telegram" ? `local_selector: ${envConfig.botKey || "-"}${envConfig.botKey ? " (advanced)" : ""}` : "",
2673
+ provider === "telegram" ? `stored_server_bot_id: ${envConfig.serverBotID || "-"}` : "",
2641
2674
  provider === "telegram" ? `stored_server_name: ${envConfig.serverBotName || "-"}` : "",
2642
2675
  provider === "telegram" ? `stored_server_roles: ${ensureArray(envConfig.serverRoles).join(", ") || "-"}` : "",
2643
2676
  provider === "telegram" ? `role_profile: ${envConfig.roleProfile || "-"}` : "",
@@ -2650,9 +2683,9 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
2650
2683
  routeLinks ? `route_links: ${intFromRaw(routeLinks.total, 0)}` : "",
2651
2684
  ].filter(Boolean);
2652
2685
  if (provider === "telegram" && serverBinding) {
2653
- lines.push(`server_binding_mode: ${serverBinding.mode || "-"}`);
2654
- lines.push(`server_bot_name: ${serverBinding.name || "-"}`);
2655
- lines.push(`server_roles: ${ensureArray(serverBinding.roles).join(", ") || "-"}`);
2686
+ lines.push(`binding_mode: ${serverBinding.mode || "-"}`);
2687
+ lines.push(`live_server_name: ${serverBinding.name || "-"}`);
2688
+ lines.push(`live_server_roles: ${ensureArray(serverBinding.roles).join(", ") || "-"}`);
2656
2689
  if (serverBinding.mode === "group") {
2657
2690
  const groupedProfiles = safeObject(serverBinding.effectiveRoleProfiles);
2658
2691
  Object.keys(groupedProfiles).forEach((role) => {
@@ -352,8 +352,7 @@ export async function runSelftestBotCommands(push, deps) {
352
352
  const addGlobals = readTelegramGlobals();
353
353
  push(
354
354
  "bot_add_guided_creates_named_telegram_entry",
355
- String(addState.TELEGRAM_BOT_NAME || "").toLowerCase().includes("monitorselftestbot")
356
- && String(addState.TELEGRAM_BOT_SERVER_NAME || "") === "MonitorSelftestBot"
355
+ String(addState.TELEGRAM_BOT_SERVER_NAME || "") === "MonitorSelftestBot"
357
356
  && String(addState.TELEGRAM_BOT_SERVER_ROLES || "") === "monitor"
358
357
  && String(addState.TELEGRAM_BOT_TOKEN || "") === "selftest-main-token"
359
358
  && String(addState.TELEGRAM_BOT_ROLE_PROFILE || "") === "monitor"
@@ -403,14 +402,13 @@ export async function runSelftestBotCommands(push, deps) {
403
402
  const groupedState = readTelegramBotEntry("ryoai_bot");
404
403
  push(
405
404
  "bot_add_guided_autoresolves_server_bot_group_without_role_prompt",
406
- String(groupedState.TELEGRAM_BOT_NAME || "").toLowerCase() === "ryoai_bot"
407
- && String(groupedState.TELEGRAM_BOT_SERVER_BOT_ID || "") === ""
405
+ String(groupedState.TELEGRAM_BOT_SERVER_BOT_ID || "") === ""
408
406
  && String(groupedState.TELEGRAM_BOT_SERVER_NAME || "") === "RyoAI_bot"
409
407
  && String(groupedState.TELEGRAM_BOT_SERVER_ROLES || "") === "monitor,review,worker,approval"
410
408
  && String(groupedState.TELEGRAM_BOT_ROLE_PROFILE || "") === ""
411
409
  && String(groupedState.TELEGRAM_BOT_AI_CLIENT || "") === ""
412
410
  && String(groupedState.TELEGRAM_BOT_AI_PERMISSION_MODE || "") === "",
413
- `name=${String(groupedState.TELEGRAM_BOT_NAME || "")} server_name=${String(groupedState.TELEGRAM_BOT_SERVER_NAME || "")} roles=${String(groupedState.TELEGRAM_BOT_SERVER_ROLES || "")} server_bot_id=${String(groupedState.TELEGRAM_BOT_SERVER_BOT_ID || "")} role=${String(groupedState.TELEGRAM_BOT_ROLE_PROFILE || "")}`,
411
+ `server_name=${String(groupedState.TELEGRAM_BOT_SERVER_NAME || "")} roles=${String(groupedState.TELEGRAM_BOT_SERVER_ROLES || "")} server_bot_id=${String(groupedState.TELEGRAM_BOT_SERVER_BOT_ID || "")} role=${String(groupedState.TELEGRAM_BOT_ROLE_PROFILE || "")}`,
414
412
  );
415
413
 
416
414
  const groupedEditResult = await runCLI({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.101",
3
+ "version": "0.2.102",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [