metheus-governance-mcp-cli 0.2.48 → 0.2.49

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
@@ -20,6 +20,27 @@ Compatibility note: legacy command alias `metheus-governance-mcp` is still suppo
20
20
  npm install -g metheus-governance-mcp-cli@latest
21
21
  ```
22
22
 
23
+ Install creates a local Telegram settings template here:
24
+
25
+ - `~/.metheus/telegram.env`
26
+
27
+ This file is for local bot secrets only.
28
+
29
+ - Store locally:
30
+ - `TELEGRAM_BOT_TOKEN`
31
+ - Server-side Metheus stores project Telegram destination metadata separately:
32
+ - `chat_id`
33
+ - label / active state
34
+
35
+ Example template:
36
+
37
+ ```env
38
+ TELEGRAM_BOT_TOKEN=
39
+ TELEGRAM_DEFAULT_CHAT_ID=
40
+ TELEGRAM_ALLOWED_CHAT_IDS=
41
+ TELEGRAM_DEFAULT_PARSE_MODE=
42
+ ```
43
+
23
44
  ## One command bootstrap (recommended)
24
45
 
25
46
  Run in your project folder:
@@ -36,16 +57,26 @@ If auth is not ready, login starts automatically, then MCP registration is compl
36
57
  metheus-governance-mcp-cli setup --project-id <project_uuid> --ctxpack-key "<ctxpack_key>" --base-url https://metheus.gesiaplatform.com
37
58
  ```
38
59
 
39
- `project-id` can be omitted if your current folder (or parent) has `.metheus_ctxpack_sync.json`.
60
+ Technical fallback: `project-id` can be auto-detected if your current folder (or parent) has `.metheus_ctxpack_sync.json`.
40
61
  `setup` defaults to `--workspace-dir auto` (dynamic workspace detection).
41
62
  Use an explicit path only when you intentionally want a fixed workspace.
42
63
 
64
+ Ops policy (recommended):
65
+ - Always pass explicit `--project-id <project_uuid>` during setup.
66
+ - Omit `--project-id` only in advanced cases where `.metheus_ctxpack_sync.json` is already present and verified.
67
+
43
68
  Recommended for Codex/Claude/Gemini/Antigravity/Cursor multi-workspace sessions:
44
69
 
45
70
  ```bash
46
71
  metheus-governance-mcp-cli setup --project-id <project_uuid> --ctxpack-key "<ctxpack_key>" --base-url https://metheus.gesiaplatform.com --workspace-dir auto
47
72
  ```
48
73
 
74
+ `setup` also ensures the local Telegram template exists:
75
+
76
+ - `~/.metheus/telegram.env`
77
+
78
+ Fill only the bot token locally. Project Telegram `chat_id` destinations should be managed on the Metheus server.
79
+
49
80
  Gemini CLI note:
50
81
  - `gemini mcp` commands require Gemini auth to be configured first (`GEMINI_API_KEY` or `~/.gemini/settings.json` auth).
51
82
  - `setup` registers Gemini in both `user` and `project` scopes to improve auto-discovery across folders.
@@ -76,6 +107,7 @@ metheus-governance-mcp-cli doctor --project-id <project_uuid> --base-url https:/
76
107
 
77
108
  Checks:
78
109
  - auth token status (+ auto refresh attempt)
110
+ - local Telegram env template presence
79
111
  - codex/claude/gemini/antigravity/cursor registration state
80
112
  - gateway `tools/list` reachability
81
113
  - `project.summary` access
@@ -126,6 +158,7 @@ These tools accept `project_id` and return:
126
158
  Project-ID first-call rule:
127
159
  - when user gives only `Project ID`, call `project.summary` first.
128
160
  - call `ctxpack.ensure` after `project.summary` only when extra ctxpack refresh/export context is needed.
161
+ - for support/ops workflows, do not run ctxpack sync/download before a successful `project.summary`.
129
162
 
130
163
  Ctxpack merge safety flow:
131
164
  - call `ctxpack.merge.brief` first
package/cli.mjs CHANGED
@@ -15,6 +15,7 @@ const DEFAULT_SITE_URL = "https://metheus.gesiaplatform.com";
15
15
  const DEFAULT_BASE_URL = `${DEFAULT_SITE_URL}/governance/mcp`;
16
16
  const DEFAULT_SERVER_NAME = "metheus-governance-mcp";
17
17
  const AUTH_STORE_RELATIVE_PATH = path.join(".metheus", "governance-mcp-auth.json");
18
+ const TELEGRAM_ENV_RELATIVE_PATH = path.join(".metheus", "telegram.env");
18
19
  const SELF_UPDATE_STATE_RELATIVE_PATH = path.join(".metheus", "governance-mcp-cli-update.json");
19
20
  const CTXPACK_CACHE_RELATIVE_DIR = path.join(".metheus", "ctxpack-cache");
20
21
  const CTXPACK_META_FILENAME = ".metheus_ctxpack_sync.json";
@@ -57,6 +58,8 @@ function printUsage() {
57
58
  ` ${ALLOW_HOME_WORKSPACE_ENV_KEY}=1 to allow using home directory as workspace root (disabled by default).`,
58
59
  " If env is missing, stored token file is used:",
59
60
  ` ${AUTH_STORE_RELATIVE_PATH}`,
61
+ " Local Telegram bot token template is stored at:",
62
+ ` ${TELEGRAM_ENV_RELATIVE_PATH}`,
60
63
  "",
61
64
  ].join("\n"),
62
65
  );
@@ -66,7 +69,7 @@ function loadCLIMeta() {
66
69
  try {
67
70
  const dir = path.dirname(fileURLToPath(import.meta.url));
68
71
  const raw = fs.readFileSync(path.join(dir, "package.json"), "utf8");
69
- const parsed = JSON.parse(raw);
72
+ const parsed = JSON.parse(stripUTF8BOM(raw));
70
73
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
71
74
  return {};
72
75
  }
@@ -80,6 +83,12 @@ function printVersion() {
80
83
  process.stdout.write(`${CLI_NAME} ${CLI_VERSION}\n`);
81
84
  }
82
85
 
86
+ function stripUTF8BOM(rawText) {
87
+ const text = String(rawText || "");
88
+ if (!text) return "";
89
+ return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
90
+ }
91
+
83
92
  function resolveHomeFilePath(relativePath) {
84
93
  const home = String(process.env.USERPROFILE || process.env.HOME || "").trim();
85
94
  if (!home) {
@@ -284,7 +293,7 @@ function loadSelfUpdateState() {
284
293
  const filePath = updateStateFilePath();
285
294
  try {
286
295
  const raw = fs.readFileSync(filePath, "utf8");
287
- const parsed = JSON.parse(raw);
296
+ const parsed = JSON.parse(stripUTF8BOM(raw));
288
297
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
289
298
  return { filePath, checkedAt: "", latestVersion: "", installedVersion: "", updatedAt: "" };
290
299
  }
@@ -470,6 +479,41 @@ function authStoreFilePath() {
470
479
  return resolveHomeFilePath(AUTH_STORE_RELATIVE_PATH);
471
480
  }
472
481
 
482
+ function telegramEnvFilePath() {
483
+ return resolveHomeFilePath(TELEGRAM_ENV_RELATIVE_PATH);
484
+ }
485
+
486
+ function telegramEnvTemplate() {
487
+ return [
488
+ "# Metheus local Telegram bot settings",
489
+ "# Keep this file on your machine only. Do not commit it.",
490
+ "# Server-side project Telegram destinations store chat_id metadata separately.",
491
+ "",
492
+ "TELEGRAM_BOT_TOKEN=",
493
+ "# Optional local default for quick tests. Project chat_id is normally resolved from Metheus server.",
494
+ "TELEGRAM_DEFAULT_CHAT_ID=",
495
+ "# Optional CSV allowlist to avoid accidental sends to the wrong chats.",
496
+ "TELEGRAM_ALLOWED_CHAT_IDS=",
497
+ "# Optional parse mode: Markdown, MarkdownV2, HTML",
498
+ "TELEGRAM_DEFAULT_PARSE_MODE=",
499
+ "",
500
+ ].join("\n");
501
+ }
502
+
503
+ function ensureTelegramEnvTemplate() {
504
+ const filePath = telegramEnvFilePath();
505
+ try {
506
+ if (fs.existsSync(filePath)) {
507
+ return { filePath, created: false, existed: true };
508
+ }
509
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
510
+ fs.writeFileSync(filePath, telegramEnvTemplate(), "utf8");
511
+ return { filePath, created: true, existed: false };
512
+ } catch (err) {
513
+ return { filePath, created: false, existed: false, error: String(err?.message || err) };
514
+ }
515
+ }
516
+
473
517
  function resolveWorkspaceDir(rawPath) {
474
518
  const input = String(rawPath || "").trim();
475
519
  if (input) {
@@ -757,9 +801,9 @@ function resolveProjectIDForRequest({
757
801
  }) {
758
802
  const resolvedWorkspaceDir = resolveWorkspaceDir(workspaceDir || process.cwd());
759
803
  const workspaceMeta = loadWorkspaceMeta(resolvedWorkspaceDir);
804
+ const toolProjectID = resolveProjectIDFromToolArgs(toolArgs);
760
805
  return firstNonEmptyString([
761
- toolArgs?.project_id,
762
- toolArgs?.projectID,
806
+ toolProjectID,
763
807
  responseProjectID,
764
808
  envelopeProjectID,
765
809
  args?.projectID,
@@ -795,7 +839,7 @@ function loadStoredAuth() {
795
839
  return { filePath, token: "", refreshToken: "", baseURL: "", updatedAt: "" };
796
840
  }
797
841
  const raw = fs.readFileSync(filePath, "utf8");
798
- const parsed = JSON.parse(raw);
842
+ const parsed = JSON.parse(stripUTF8BOM(raw));
799
843
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
800
844
  return { filePath, token: "", refreshToken: "", baseURL: "", updatedAt: "" };
801
845
  }
@@ -1461,7 +1505,7 @@ function currentAccessTokenSource() {
1461
1505
 
1462
1506
  function tryJsonParse(raw) {
1463
1507
  try {
1464
- return JSON.parse(raw);
1508
+ return JSON.parse(stripUTF8BOM(raw));
1465
1509
  } catch {
1466
1510
  return null;
1467
1511
  }
@@ -1505,7 +1549,7 @@ function loadWorkspaceMeta(startDir) {
1505
1549
  if (!metaFile) return {};
1506
1550
  try {
1507
1551
  const raw = fs.readFileSync(metaFile, "utf8");
1508
- const parsed = JSON.parse(raw);
1552
+ const parsed = JSON.parse(stripUTF8BOM(raw));
1509
1553
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
1510
1554
  return parsed;
1511
1555
  }
@@ -1552,6 +1596,40 @@ function isUUID(raw) {
1552
1596
  return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
1553
1597
  }
1554
1598
 
1599
+ function extractUUIDFromText(raw) {
1600
+ const text = String(raw || "").trim();
1601
+ if (!text) return "";
1602
+ const match = text.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i);
1603
+ return String(match?.[0] || "").trim();
1604
+ }
1605
+
1606
+ function resolveProjectIDFromToolArgs(rawToolArgs) {
1607
+ const toolArgs = safeObject(rawToolArgs);
1608
+ const direct = firstNonEmptyString([
1609
+ toolArgs.project_id,
1610
+ toolArgs.projectID,
1611
+ toolArgs.projectId,
1612
+ toolArgs["project-id"],
1613
+ toolArgs.project_uuid,
1614
+ toolArgs.projectUUID,
1615
+ toolArgs.projectUuid,
1616
+ ]);
1617
+ if (isUUID(direct)) return direct;
1618
+
1619
+ const hinted = firstNonEmptyString([
1620
+ extractUUIDFromText(toolArgs.project),
1621
+ extractUUIDFromText(toolArgs.project_id_text),
1622
+ extractUUIDFromText(toolArgs.projectIdText),
1623
+ extractUUIDFromText(toolArgs.input),
1624
+ extractUUIDFromText(toolArgs.text),
1625
+ extractUUIDFromText(toolArgs.query),
1626
+ extractUUIDFromText(toolArgs.prompt),
1627
+ ]);
1628
+ if (isUUID(hinted)) return hinted;
1629
+
1630
+ return "";
1631
+ }
1632
+
1555
1633
  async function runAuthStatus() {
1556
1634
  const resolved = resolveCurrentAccessToken();
1557
1635
  const token = resolved.token;
@@ -2067,6 +2145,18 @@ async function runDoctor(flags) {
2067
2145
  });
2068
2146
  const rows = [];
2069
2147
 
2148
+ const telegramEnv = ensureTelegramEnvTemplate();
2149
+ if (telegramEnv.error) {
2150
+ addDoctorCheck(rows, "warn", "local telegram env", `unavailable (${telegramEnv.error})`);
2151
+ } else {
2152
+ addDoctorCheck(
2153
+ rows,
2154
+ telegramEnv.created ? "warn" : "ok",
2155
+ "local telegram env",
2156
+ `${telegramEnv.created ? "created template" : "ready"} (${telegramEnv.filePath})`,
2157
+ );
2158
+ }
2159
+
2070
2160
  const resolved = await resolveAccessTokenForCommand(context.baseURL, timeoutSeconds);
2071
2161
  const token = resolved.token;
2072
2162
  if (!token) {
@@ -2720,7 +2810,7 @@ function loadCtxpackMeta(metaPath) {
2720
2810
  try {
2721
2811
  if (!fs.existsSync(metaPath)) return null;
2722
2812
  const raw = fs.readFileSync(metaPath, "utf8");
2723
- const parsed = JSON.parse(raw);
2813
+ const parsed = JSON.parse(stripUTF8BOM(raw));
2724
2814
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
2725
2815
  return parsed;
2726
2816
  } catch {
@@ -3730,6 +3820,7 @@ function appendProjectHintToInitialize(responseObj, args, options = {}) {
3730
3820
  ]
3731
3821
  : []),
3732
3822
  `- MUST call \`${projectSummaryTool}\` first when the user provides only a Project ID or asks project overview/agenda.`,
3823
+ `- If user enters a Project ID in text (e.g., "Project ID <uuid>"), pass that exact UUID as \`project_id\` argument when calling \`${projectSummaryTool}\`.`,
3733
3824
  `- \`${projectDescribeTool}\` and \`${projectGetTool}\` are aliases of \`${projectSummaryTool}\`.`,
3734
3825
  `- Never handle a bare Project ID by local file/transcript search before \`${projectSummaryTool}\` succeeds.`,
3735
3826
  `- Run \`${ctxpackEnsureTool}\` only after \`${projectSummaryTool}\`, and only when additional ctxpack refresh/export context is needed.`,
@@ -4505,7 +4596,13 @@ async function runProxy(flags) {
4505
4596
  }),
4506
4597
  ).trim();
4507
4598
  if (!projectID) {
4508
- writeProxyJson(jsonRpcError(requestObj, -32001, "project_id is required (or set --project-id during setup)"));
4599
+ writeProxyJson(
4600
+ jsonRpcError(
4601
+ requestObj,
4602
+ -32001,
4603
+ 'project_id is required. Enter a valid UUID (e.g., {"project_id":"00000000-0000-0000-0000-000000000000"}) or set --project-id during setup.',
4604
+ ),
4605
+ );
4509
4606
  return;
4510
4607
  }
4511
4608
  if (!isUUID(projectID)) {
@@ -4561,7 +4658,13 @@ async function runProxy(flags) {
4561
4658
  }),
4562
4659
  ).trim();
4563
4660
  if (!projectID) {
4564
- writeProxyJson(jsonRpcError(requestObj, -32001, "project_id is required (or set --project-id during setup)"));
4661
+ writeProxyJson(
4662
+ jsonRpcError(
4663
+ requestObj,
4664
+ -32001,
4665
+ 'project_id is required. Enter a valid UUID (e.g., {"project_id":"00000000-0000-0000-0000-000000000000"}) or set --project-id during setup.',
4666
+ ),
4667
+ );
4565
4668
  return;
4566
4669
  }
4567
4670
  if (!isUUID(projectID)) {
@@ -5198,6 +5301,7 @@ function runSetupInternal(flags, options = {}) {
5198
5301
  const context = resolveSetupContext(flags);
5199
5302
  const clients = [...MCP_CLIENTS];
5200
5303
  const results = [];
5304
+ const telegramEnv = ensureTelegramEnvTemplate();
5201
5305
 
5202
5306
  for (const cliBin of clients) {
5203
5307
  if (!commandExists(cliBin)) continue;
@@ -5247,6 +5351,13 @@ function runSetupInternal(flags, options = {}) {
5247
5351
  process.stdout.write(`Fallback: ${context.workspaceFallbackDir} (METHEUS_WORKSPACE_DIR)\n`);
5248
5352
  }
5249
5353
  process.stdout.write(`Project: ${context.projectID || "auto-detect from .metheus_ctxpack_sync.json"}\n`);
5354
+ if (telegramEnv.error) {
5355
+ process.stdout.write(`Telegram: template unavailable (${telegramEnv.error})\n`);
5356
+ } else {
5357
+ process.stdout.write(
5358
+ `Telegram: ${telegramEnv.created ? "template created" : "template ready"} (${telegramEnv.filePath})\n`,
5359
+ );
5360
+ }
5250
5361
  if (context.ctxpackKey) {
5251
5362
  process.stdout.write(`Ctxpack: ${context.ctxpackKey}\n`);
5252
5363
  }
package/package.json CHANGED
@@ -1,21 +1,23 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.48",
3
+ "version": "0.2.49",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "bin/metheus-governance-mcp.js",
8
8
  "cli.mjs",
9
+ "postinstall.mjs",
9
10
  "README.md",
10
11
  ".env.npm.local.example"
11
12
  ],
12
13
  "scripts": {
13
- "check": "node --check cli.mjs && node --check release.mjs",
14
+ "check": "node --check cli.mjs && node --check postinstall.mjs && node --check release.mjs",
14
15
  "test:compat": "node cli.mjs selftest --json",
15
16
  "smoke:proxy": "node scripts/smoke-proxy.mjs",
16
17
  "pack:dry": "npm pack --dry-run",
17
18
  "publish:dry": "node release.mjs --dry-run",
18
- "publish:public": "node release.mjs"
19
+ "publish:public": "node release.mjs",
20
+ "postinstall": "node postinstall.mjs"
19
21
  },
20
22
  "bin": {
21
23
  "metheus-governance-mcp": "bin/metheus-governance-mcp.js",
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+
7
+ const RELATIVE_PATH = path.join(".metheus", "telegram.env");
8
+
9
+ function resolveTargetPath() {
10
+ const home = String(process.env.USERPROFILE || process.env.HOME || os.homedir() || "").trim();
11
+ if (!home) return "";
12
+ return path.join(home, RELATIVE_PATH);
13
+ }
14
+
15
+ function template() {
16
+ return [
17
+ "# Metheus local Telegram bot settings",
18
+ "# Keep this file on your machine only. Do not commit it.",
19
+ "# Server-side project Telegram destinations store chat_id metadata separately.",
20
+ "",
21
+ "TELEGRAM_BOT_TOKEN=",
22
+ "# Optional local default for quick tests. Project chat_id is normally resolved from Metheus server.",
23
+ "TELEGRAM_DEFAULT_CHAT_ID=",
24
+ "# Optional CSV allowlist to avoid accidental sends to the wrong chats.",
25
+ "TELEGRAM_ALLOWED_CHAT_IDS=",
26
+ "# Optional parse mode: Markdown, MarkdownV2, HTML",
27
+ "TELEGRAM_DEFAULT_PARSE_MODE=",
28
+ "",
29
+ ].join("\n");
30
+ }
31
+
32
+ function main() {
33
+ const filePath = resolveTargetPath();
34
+ if (!filePath) return;
35
+ try {
36
+ if (fs.existsSync(filePath)) return;
37
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
38
+ fs.writeFileSync(filePath, template(), "utf8");
39
+ process.stdout.write(`[metheus-governance-mcp-cli] created ${filePath}\n`);
40
+ } catch (err) {
41
+ process.stderr.write(
42
+ `[metheus-governance-mcp-cli] could not create ${filePath}: ${String(err?.message || err)}\n`,
43
+ );
44
+ }
45
+ }
46
+
47
+ main();