metheus-governance-mcp-cli 0.2.48 → 0.2.50

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,25 @@ 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 the local Telegram bot secret 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
+ - Do not put project `chat_id` in the local Telegram env file.
35
+
36
+ Example template:
37
+
38
+ ```env
39
+ TELEGRAM_BOT_TOKEN=
40
+ ```
41
+
23
42
  ## One command bootstrap (recommended)
24
43
 
25
44
  Run in your project folder:
@@ -36,16 +55,26 @@ If auth is not ready, login starts automatically, then MCP registration is compl
36
55
  metheus-governance-mcp-cli setup --project-id <project_uuid> --ctxpack-key "<ctxpack_key>" --base-url https://metheus.gesiaplatform.com
37
56
  ```
38
57
 
39
- `project-id` can be omitted if your current folder (or parent) has `.metheus_ctxpack_sync.json`.
58
+ Technical fallback: `project-id` can be auto-detected if your current folder (or parent) has `.metheus_ctxpack_sync.json`.
40
59
  `setup` defaults to `--workspace-dir auto` (dynamic workspace detection).
41
60
  Use an explicit path only when you intentionally want a fixed workspace.
42
61
 
62
+ Ops policy (recommended):
63
+ - Always pass explicit `--project-id <project_uuid>` during setup.
64
+ - Omit `--project-id` only in advanced cases where `.metheus_ctxpack_sync.json` is already present and verified.
65
+
43
66
  Recommended for Codex/Claude/Gemini/Antigravity/Cursor multi-workspace sessions:
44
67
 
45
68
  ```bash
46
69
  metheus-governance-mcp-cli setup --project-id <project_uuid> --ctxpack-key "<ctxpack_key>" --base-url https://metheus.gesiaplatform.com --workspace-dir auto
47
70
  ```
48
71
 
72
+ `setup` also ensures the local Telegram template exists:
73
+
74
+ - `~/.metheus/telegram.env`
75
+
76
+ Fill only the bot token locally. Project Telegram `chat_id` destinations should be managed on the Metheus server as project Telegram destinations, not as local env values and not inside Chat Hooks.
77
+
49
78
  Gemini CLI note:
50
79
  - `gemini mcp` commands require Gemini auth to be configured first (`GEMINI_API_KEY` or `~/.gemini/settings.json` auth).
51
80
  - `setup` registers Gemini in both `user` and `project` scopes to improve auto-discovery across folders.
@@ -76,6 +105,7 @@ metheus-governance-mcp-cli doctor --project-id <project_uuid> --base-url https:/
76
105
 
77
106
  Checks:
78
107
  - auth token status (+ auto refresh attempt)
108
+ - local Telegram env template presence
79
109
  - codex/claude/gemini/antigravity/cursor registration state
80
110
  - gateway `tools/list` reachability
81
111
  - `project.summary` access
@@ -126,6 +156,7 @@ These tools accept `project_id` and return:
126
156
  Project-ID first-call rule:
127
157
  - when user gives only `Project ID`, call `project.summary` first.
128
158
  - call `ctxpack.ensure` after `project.summary` only when extra ctxpack refresh/export context is needed.
159
+ - for support/ops workflows, do not run ctxpack sync/download before a successful `project.summary`.
129
160
 
130
161
  Ctxpack merge safety flow:
131
162
  - 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,36 @@ 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
+ "# Store only the Telegram bot token locally.",
491
+ "# Project chat_id must be managed on the Metheus server as a project Telegram destination.",
492
+ "",
493
+ "TELEGRAM_BOT_TOKEN=",
494
+ "",
495
+ ].join("\n");
496
+ }
497
+
498
+ function ensureTelegramEnvTemplate() {
499
+ const filePath = telegramEnvFilePath();
500
+ try {
501
+ if (fs.existsSync(filePath)) {
502
+ return { filePath, created: false, existed: true };
503
+ }
504
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
505
+ fs.writeFileSync(filePath, telegramEnvTemplate(), "utf8");
506
+ return { filePath, created: true, existed: false };
507
+ } catch (err) {
508
+ return { filePath, created: false, existed: false, error: String(err?.message || err) };
509
+ }
510
+ }
511
+
473
512
  function resolveWorkspaceDir(rawPath) {
474
513
  const input = String(rawPath || "").trim();
475
514
  if (input) {
@@ -757,9 +796,9 @@ function resolveProjectIDForRequest({
757
796
  }) {
758
797
  const resolvedWorkspaceDir = resolveWorkspaceDir(workspaceDir || process.cwd());
759
798
  const workspaceMeta = loadWorkspaceMeta(resolvedWorkspaceDir);
799
+ const toolProjectID = resolveProjectIDFromToolArgs(toolArgs);
760
800
  return firstNonEmptyString([
761
- toolArgs?.project_id,
762
- toolArgs?.projectID,
801
+ toolProjectID,
763
802
  responseProjectID,
764
803
  envelopeProjectID,
765
804
  args?.projectID,
@@ -795,7 +834,7 @@ function loadStoredAuth() {
795
834
  return { filePath, token: "", refreshToken: "", baseURL: "", updatedAt: "" };
796
835
  }
797
836
  const raw = fs.readFileSync(filePath, "utf8");
798
- const parsed = JSON.parse(raw);
837
+ const parsed = JSON.parse(stripUTF8BOM(raw));
799
838
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
800
839
  return { filePath, token: "", refreshToken: "", baseURL: "", updatedAt: "" };
801
840
  }
@@ -1461,7 +1500,7 @@ function currentAccessTokenSource() {
1461
1500
 
1462
1501
  function tryJsonParse(raw) {
1463
1502
  try {
1464
- return JSON.parse(raw);
1503
+ return JSON.parse(stripUTF8BOM(raw));
1465
1504
  } catch {
1466
1505
  return null;
1467
1506
  }
@@ -1505,7 +1544,7 @@ function loadWorkspaceMeta(startDir) {
1505
1544
  if (!metaFile) return {};
1506
1545
  try {
1507
1546
  const raw = fs.readFileSync(metaFile, "utf8");
1508
- const parsed = JSON.parse(raw);
1547
+ const parsed = JSON.parse(stripUTF8BOM(raw));
1509
1548
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
1510
1549
  return parsed;
1511
1550
  }
@@ -1552,6 +1591,40 @@ function isUUID(raw) {
1552
1591
  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
1592
  }
1554
1593
 
1594
+ function extractUUIDFromText(raw) {
1595
+ const text = String(raw || "").trim();
1596
+ if (!text) return "";
1597
+ 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);
1598
+ return String(match?.[0] || "").trim();
1599
+ }
1600
+
1601
+ function resolveProjectIDFromToolArgs(rawToolArgs) {
1602
+ const toolArgs = safeObject(rawToolArgs);
1603
+ const direct = firstNonEmptyString([
1604
+ toolArgs.project_id,
1605
+ toolArgs.projectID,
1606
+ toolArgs.projectId,
1607
+ toolArgs["project-id"],
1608
+ toolArgs.project_uuid,
1609
+ toolArgs.projectUUID,
1610
+ toolArgs.projectUuid,
1611
+ ]);
1612
+ if (isUUID(direct)) return direct;
1613
+
1614
+ const hinted = firstNonEmptyString([
1615
+ extractUUIDFromText(toolArgs.project),
1616
+ extractUUIDFromText(toolArgs.project_id_text),
1617
+ extractUUIDFromText(toolArgs.projectIdText),
1618
+ extractUUIDFromText(toolArgs.input),
1619
+ extractUUIDFromText(toolArgs.text),
1620
+ extractUUIDFromText(toolArgs.query),
1621
+ extractUUIDFromText(toolArgs.prompt),
1622
+ ]);
1623
+ if (isUUID(hinted)) return hinted;
1624
+
1625
+ return "";
1626
+ }
1627
+
1555
1628
  async function runAuthStatus() {
1556
1629
  const resolved = resolveCurrentAccessToken();
1557
1630
  const token = resolved.token;
@@ -2067,6 +2140,18 @@ async function runDoctor(flags) {
2067
2140
  });
2068
2141
  const rows = [];
2069
2142
 
2143
+ const telegramEnv = ensureTelegramEnvTemplate();
2144
+ if (telegramEnv.error) {
2145
+ addDoctorCheck(rows, "warn", "local telegram env", `unavailable (${telegramEnv.error})`);
2146
+ } else {
2147
+ addDoctorCheck(
2148
+ rows,
2149
+ telegramEnv.created ? "warn" : "ok",
2150
+ "local telegram env",
2151
+ `${telegramEnv.created ? "created template" : "ready"} (${telegramEnv.filePath})`,
2152
+ );
2153
+ }
2154
+
2070
2155
  const resolved = await resolveAccessTokenForCommand(context.baseURL, timeoutSeconds);
2071
2156
  const token = resolved.token;
2072
2157
  if (!token) {
@@ -2720,7 +2805,7 @@ function loadCtxpackMeta(metaPath) {
2720
2805
  try {
2721
2806
  if (!fs.existsSync(metaPath)) return null;
2722
2807
  const raw = fs.readFileSync(metaPath, "utf8");
2723
- const parsed = JSON.parse(raw);
2808
+ const parsed = JSON.parse(stripUTF8BOM(raw));
2724
2809
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
2725
2810
  return parsed;
2726
2811
  } catch {
@@ -3730,6 +3815,7 @@ function appendProjectHintToInitialize(responseObj, args, options = {}) {
3730
3815
  ]
3731
3816
  : []),
3732
3817
  `- MUST call \`${projectSummaryTool}\` first when the user provides only a Project ID or asks project overview/agenda.`,
3818
+ `- 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
3819
  `- \`${projectDescribeTool}\` and \`${projectGetTool}\` are aliases of \`${projectSummaryTool}\`.`,
3734
3820
  `- Never handle a bare Project ID by local file/transcript search before \`${projectSummaryTool}\` succeeds.`,
3735
3821
  `- Run \`${ctxpackEnsureTool}\` only after \`${projectSummaryTool}\`, and only when additional ctxpack refresh/export context is needed.`,
@@ -4505,7 +4591,13 @@ async function runProxy(flags) {
4505
4591
  }),
4506
4592
  ).trim();
4507
4593
  if (!projectID) {
4508
- writeProxyJson(jsonRpcError(requestObj, -32001, "project_id is required (or set --project-id during setup)"));
4594
+ writeProxyJson(
4595
+ jsonRpcError(
4596
+ requestObj,
4597
+ -32001,
4598
+ 'project_id is required. Enter a valid UUID (e.g., {"project_id":"00000000-0000-0000-0000-000000000000"}) or set --project-id during setup.',
4599
+ ),
4600
+ );
4509
4601
  return;
4510
4602
  }
4511
4603
  if (!isUUID(projectID)) {
@@ -4561,7 +4653,13 @@ async function runProxy(flags) {
4561
4653
  }),
4562
4654
  ).trim();
4563
4655
  if (!projectID) {
4564
- writeProxyJson(jsonRpcError(requestObj, -32001, "project_id is required (or set --project-id during setup)"));
4656
+ writeProxyJson(
4657
+ jsonRpcError(
4658
+ requestObj,
4659
+ -32001,
4660
+ 'project_id is required. Enter a valid UUID (e.g., {"project_id":"00000000-0000-0000-0000-000000000000"}) or set --project-id during setup.',
4661
+ ),
4662
+ );
4565
4663
  return;
4566
4664
  }
4567
4665
  if (!isUUID(projectID)) {
@@ -5198,6 +5296,7 @@ function runSetupInternal(flags, options = {}) {
5198
5296
  const context = resolveSetupContext(flags);
5199
5297
  const clients = [...MCP_CLIENTS];
5200
5298
  const results = [];
5299
+ const telegramEnv = ensureTelegramEnvTemplate();
5201
5300
 
5202
5301
  for (const cliBin of clients) {
5203
5302
  if (!commandExists(cliBin)) continue;
@@ -5247,6 +5346,13 @@ function runSetupInternal(flags, options = {}) {
5247
5346
  process.stdout.write(`Fallback: ${context.workspaceFallbackDir} (METHEUS_WORKSPACE_DIR)\n`);
5248
5347
  }
5249
5348
  process.stdout.write(`Project: ${context.projectID || "auto-detect from .metheus_ctxpack_sync.json"}\n`);
5349
+ if (telegramEnv.error) {
5350
+ process.stdout.write(`Telegram: template unavailable (${telegramEnv.error})\n`);
5351
+ } else {
5352
+ process.stdout.write(
5353
+ `Telegram: ${telegramEnv.created ? "template created" : "template ready"} (${telegramEnv.filePath})\n`,
5354
+ );
5355
+ }
5250
5356
  if (context.ctxpackKey) {
5251
5357
  process.stdout.write(`Ctxpack: ${context.ctxpackKey}\n`);
5252
5358
  }
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.50",
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,42 @@
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
+ "# Store only the Telegram bot token locally.",
20
+ "# Project chat_id must be managed on the Metheus server as a project Telegram destination.",
21
+ "",
22
+ "TELEGRAM_BOT_TOKEN=",
23
+ "",
24
+ ].join("\n");
25
+ }
26
+
27
+ function main() {
28
+ const filePath = resolveTargetPath();
29
+ if (!filePath) return;
30
+ try {
31
+ if (fs.existsSync(filePath)) return;
32
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
33
+ fs.writeFileSync(filePath, template(), "utf8");
34
+ process.stdout.write(`[metheus-governance-mcp-cli] created ${filePath}\n`);
35
+ } catch (err) {
36
+ process.stderr.write(
37
+ `[metheus-governance-mcp-cli] could not create ${filePath}: ${String(err?.message || err)}\n`,
38
+ );
39
+ }
40
+ }
41
+
42
+ main();