metheus-governance-mcp-cli 0.2.17 → 0.2.19

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.
Files changed (3) hide show
  1. package/README.md +13 -2
  2. package/cli.mjs +138 -29
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -37,6 +37,8 @@ metheus-governance-mcp-cli setup --project-id <project_uuid> --ctxpack-key "<ctx
37
37
  ```
38
38
 
39
39
  `project-id` can be omitted if your current folder (or parent) has `.metheus_ctxpack_sync.json`.
40
+ `setup` defaults to `--workspace-dir auto` (dynamic workspace detection).
41
+ Use an explicit path only when you intentionally want a fixed workspace.
40
42
 
41
43
  Recommended for Codex/Claude multi-workspace sessions:
42
44
 
@@ -44,6 +46,15 @@ Recommended for Codex/Claude multi-workspace sessions:
44
46
  metheus-governance-mcp-cli setup --project-id <project_uuid> --ctxpack-key "<ctxpack_key>" --base-url https://metheus.gesiaplatform.com --workspace-dir auto
45
47
  ```
46
48
 
49
+ When a client does not send workspace metadata (for example some Codex sessions),
50
+ set a stable fallback root once:
51
+
52
+ ```bash
53
+ metheus-governance-mcp-cli setup --project-id <project_uuid> --ctxpack-key "<ctxpack_key>" --base-url https://metheus.gesiaplatform.com --workspace-dir auto --workspace-fallback-dir C:\code_test
54
+ ```
55
+
56
+ This registers `METHEUS_WORKSPACE_DIR` for Codex MCP so ctxpack sync still resolves safely.
57
+
47
58
  Guardrail note:
48
59
  - By default, CLI blocks reading/writing ctxpack sync metadata when workspace root resolves to the home directory.
49
60
  - Override only when intentional: `METHEUS_ALLOW_HOME_WORKSPACE=1`.
@@ -93,8 +104,8 @@ These tools accept `project_id` and return:
93
104
  - missing local cache -> download
94
105
  - same version -> keep current
95
106
  - newer server version -> update local cache
96
- - workspace path -> pinned to setup/proxy working directory by default
97
- - use `--workspace-dir auto` to follow active client workspace/root dynamically
107
+ - workspace path -> auto-detected from client metadata/env by default
108
+ - use `--workspace-fallback-dir <path>` when client metadata is unavailable
98
109
 
99
110
  Ctxpack merge safety flow:
100
111
  - call `ctxpack.merge.brief` first
package/cli.mjs CHANGED
@@ -38,7 +38,7 @@ function printUsage() {
38
38
  "Usage:",
39
39
  ` ${cmd} [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--workspace-dir <path|auto>] [--flow <auto|device|callback|manual>]`,
40
40
  ` ${cmd} init [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--flow <auto|device|callback|manual>]`,
41
- ` ${cmd} setup [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--workspace-dir <path|auto>] [--name <server_name>]`,
41
+ ` ${cmd} setup [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--workspace-dir <path|auto>] [--workspace-fallback-dir <path>] [--name <server_name>]`,
42
42
  ` ${cmd} doctor [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--timeout-seconds <n>]`,
43
43
  ` ${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>]`,
44
44
  ` ${cmd} ctxpack pull [--project-id <uuid>] [--base-url <url>] [--workspace-dir <path|auto>] [--paths <csv>] [--timeout-seconds <n>]`,
@@ -365,7 +365,7 @@ function extractWorkspaceCandidateFromFolders(rawFolders) {
365
365
  return "";
366
366
  }
367
367
 
368
- function extractWorkspaceCandidateFromRequest(requestObj, toolArgs) {
368
+ function extractStrongWorkspaceCandidateFromRequest(requestObj, toolArgs) {
369
369
  const params = safeObject(requestObj?.params);
370
370
  const meta = safeObject(params._meta);
371
371
  const workspaceFromFolders = firstNonEmptyString([
@@ -381,13 +381,28 @@ function extractWorkspaceCandidateFromRequest(requestObj, toolArgs) {
381
381
  args.workspaceDir,
382
382
  params.workspace_dir,
383
383
  params.workspaceDir,
384
+ meta.workspace_dir,
385
+ meta.workspaceDir,
386
+ ]);
387
+ return sanitizeWorkspaceCandidate(rawCandidate);
388
+ }
389
+
390
+ function extractWorkspaceCandidateFromRequest(requestObj, toolArgs) {
391
+ return firstNonEmptyString([
392
+ extractStrongWorkspaceCandidateFromRequest(requestObj, toolArgs),
393
+ extractWeakWorkspaceCandidateFromRequest(requestObj),
394
+ ]);
395
+ }
396
+
397
+ function extractWeakWorkspaceCandidateFromRequest(requestObj) {
398
+ const params = safeObject(requestObj?.params);
399
+ const meta = safeObject(params._meta);
400
+ const rawCandidate = firstNonEmptyString([
384
401
  params.cwd,
385
402
  params.root_path,
386
403
  params.rootPath,
387
404
  params.root_uri,
388
405
  params.rootUri,
389
- meta.workspace_dir,
390
- meta.workspaceDir,
391
406
  meta.cwd,
392
407
  meta.root_path,
393
408
  meta.rootPath,
@@ -397,13 +412,28 @@ function extractWorkspaceCandidateFromRequest(requestObj, toolArgs) {
397
412
  return sanitizeWorkspaceCandidate(rawCandidate);
398
413
  }
399
414
 
400
- function extractWorkspaceCandidateFromEnv() {
415
+ function extractStrongWorkspaceCandidateFromEnv() {
401
416
  const rawCandidate = firstNonEmptyString([
402
417
  process.env.METHEUS_WORKSPACE_DIR,
418
+ process.env.METHEUS_WORKSPACE_URI,
419
+ process.env.CODEX_WORKSPACE_DIR,
420
+ process.env.CODEX_WORKSPACE_URI,
403
421
  process.env.CLAUDE_WORKSPACE_DIR,
404
422
  process.env.CLAUDE_WORKSPACE_URI,
405
423
  process.env.CLAUDE_PROJECT_DIR,
406
- process.env.CODEX_WORKSPACE_DIR,
424
+ ]);
425
+ return sanitizeWorkspaceCandidate(rawCandidate);
426
+ }
427
+
428
+ function extractWorkspaceCandidateFromEnv() {
429
+ return firstNonEmptyString([
430
+ extractStrongWorkspaceCandidateFromEnv(),
431
+ extractWeakWorkspaceCandidateFromEnv(),
432
+ ]);
433
+ }
434
+
435
+ function extractWeakWorkspaceCandidateFromEnv() {
436
+ const rawCandidate = firstNonEmptyString([
407
437
  process.env.WORKSPACE_DIR,
408
438
  process.env.WORKSPACE_FOLDER,
409
439
  process.env.VSCODE_WORKSPACE_FOLDER,
@@ -415,10 +445,20 @@ function extractWorkspaceCandidateFromEnv() {
415
445
  }
416
446
 
417
447
  function resolveWorkspaceDirForRequest(defaultWorkspaceDir, requestObj, toolArgs) {
418
- const requestCandidate = extractWorkspaceCandidateFromRequest(requestObj, toolArgs);
419
- const envCandidate = extractWorkspaceCandidateFromEnv();
448
+ const strongRequestCandidate = extractStrongWorkspaceCandidateFromRequest(requestObj, toolArgs);
449
+ const strongEnvCandidate = extractStrongWorkspaceCandidateFromEnv();
450
+ const weakRequestCandidate = extractWeakWorkspaceCandidateFromRequest(requestObj);
451
+ const weakEnvCandidate = extractWeakWorkspaceCandidateFromEnv();
420
452
  const homeCandidate = firstNonEmptyString([process.env.USERPROFILE, process.env.HOME]);
421
- for (const rawCandidate of [requestCandidate, envCandidate, defaultWorkspaceDir, process.cwd(), homeCandidate]) {
453
+ for (const rawCandidate of [
454
+ strongRequestCandidate,
455
+ strongEnvCandidate,
456
+ weakRequestCandidate,
457
+ weakEnvCandidate,
458
+ defaultWorkspaceDir,
459
+ process.cwd(),
460
+ homeCandidate,
461
+ ]) {
422
462
  const resolved = sanitizeWorkspaceCandidate(rawCandidate);
423
463
  if (resolved) return resolved;
424
464
  }
@@ -2436,7 +2476,13 @@ function hasAllCtxpackFiles(baseDir, files) {
2436
2476
  return true;
2437
2477
  }
2438
2478
 
2439
- function syncCtxpackToLocalCache({ siteBaseURL, projectID, ctxpack, workspaceDir }) {
2479
+ function syncCtxpackToLocalCache({
2480
+ siteBaseURL,
2481
+ projectID,
2482
+ ctxpack,
2483
+ workspaceDir,
2484
+ workspaceSignalTrusted = true,
2485
+ }) {
2440
2486
  const ctxpackID = String(ctxpack?.ctxpack_id || "").trim();
2441
2487
  const version = String(ctxpack?.version || "").trim();
2442
2488
  const versionID = String(ctxpack?.version_id || ctxpack?.current_version_id || "").trim();
@@ -2458,6 +2504,17 @@ function syncCtxpackToLocalCache({ siteBaseURL, projectID, ctxpack, workspaceDir
2458
2504
  };
2459
2505
  }
2460
2506
 
2507
+ if (!workspaceSignalTrusted) {
2508
+ return {
2509
+ sync_status: "guarded",
2510
+ sync_message:
2511
+ "Workspace signal is missing in auto mode. Guardrail blocked ctxpack local write to avoid wrong folder sync.",
2512
+ local_path: cacheDir,
2513
+ workspace_path: resolvedWorkspaceDir,
2514
+ local_file_count: 0,
2515
+ };
2516
+ }
2517
+
2461
2518
  if (!ctxpackID || !version || files.length === 0) {
2462
2519
  return {
2463
2520
  sync_status: "not_available",
@@ -2542,6 +2599,7 @@ async function loadProjectSummaryForTool({
2542
2599
  syncCtxpackLocal,
2543
2600
  workspaceDir,
2544
2601
  ctxpackPaths,
2602
+ workspaceSignalTrusted = true,
2545
2603
  }) {
2546
2604
  const encodedProjectID = encodeURIComponent(projectID);
2547
2605
  let projectRaw = null;
@@ -2607,6 +2665,7 @@ async function loadProjectSummaryForTool({
2607
2665
  projectID,
2608
2666
  ctxpack,
2609
2667
  workspaceDir,
2668
+ workspaceSignalTrusted,
2610
2669
  });
2611
2670
  ctxpackSyncStatus = syncResult.sync_status || "error";
2612
2671
  ctxpackSyncMessage = String(syncResult.sync_message || "").trim();
@@ -3267,6 +3326,7 @@ async function maybeAutoSyncCtxpackForCall({
3267
3326
  args,
3268
3327
  token,
3269
3328
  workspaceDir,
3329
+ workspaceSignalTrusted = true,
3270
3330
  }) {
3271
3331
  if (!isJsonRpcMethod(requestObj, "tools/call")) return null;
3272
3332
  if (!token) return null;
@@ -3298,6 +3358,7 @@ async function maybeAutoSyncCtxpackForCall({
3298
3358
  includeCtxpack: true,
3299
3359
  syncCtxpackLocal: true,
3300
3360
  workspaceDir: workspacePath,
3361
+ workspaceSignalTrusted,
3301
3362
  });
3302
3363
  } catch {
3303
3364
  return null;
@@ -3329,7 +3390,7 @@ function appendAutoCtxpackSyncHint(responseObj, summary) {
3329
3390
  return responseObj;
3330
3391
  }
3331
3392
 
3332
- async function appendWorkitemListHints(responseObj, args, toolArgs, token) {
3393
+ async function appendWorkitemListHints(responseObj, args, toolArgs, token, workspaceSignalTrusted = true) {
3333
3394
  const result = safeObject(responseObj.result);
3334
3395
  const content = ensureArray(result.content);
3335
3396
  if (!content.length) return responseObj;
@@ -3388,6 +3449,7 @@ async function appendWorkitemListHints(responseObj, args, toolArgs, token) {
3388
3449
  includeCtxpack: isEmptyBody,
3389
3450
  syncCtxpackLocal: isEmptyBody,
3390
3451
  workspaceDir: args.workspaceDir,
3452
+ workspaceSignalTrusted,
3391
3453
  });
3392
3454
  if (String(summary.access || "") === "granted") {
3393
3455
  nextLines.push("Project context:");
@@ -3425,7 +3487,7 @@ async function appendWorkitemListHints(responseObj, args, toolArgs, token) {
3425
3487
  return responseObj;
3426
3488
  }
3427
3489
 
3428
- function appendCtxpackEnsureSyncHints(responseObj, args, toolArgs, requestObj) {
3490
+ function appendCtxpackEnsureSyncHints(responseObj, args, toolArgs, requestObj, workspaceSignalTrusted = true) {
3429
3491
  const result = safeObject(responseObj.result);
3430
3492
  const content = ensureArray(result.content);
3431
3493
  if (!content.length) return responseObj;
@@ -3459,6 +3521,7 @@ function appendCtxpackEnsureSyncHints(responseObj, args, toolArgs, requestObj) {
3459
3521
  projectID,
3460
3522
  ctxpack: body,
3461
3523
  workspaceDir,
3524
+ workspaceSignalTrusted,
3462
3525
  });
3463
3526
  const baseVersionID = firstNonEmptyString([body.base_version_id, body.version_id, body.current_version_id]);
3464
3527
  const currentVersionID = firstNonEmptyString([body.current_version_id, body.version_id, body.base_version_id]);
@@ -3616,7 +3679,15 @@ async function injectCtxpackPreflightToken(requestObj, toolName, toolArgs, args,
3616
3679
  }
3617
3680
  }
3618
3681
 
3619
- async function appendCtxpackConflictHintToErrorResponse(responseObj, args, toolName, toolArgs, token, workspaceDir) {
3682
+ async function appendCtxpackConflictHintToErrorResponse(
3683
+ responseObj,
3684
+ args,
3685
+ toolName,
3686
+ toolArgs,
3687
+ token,
3688
+ workspaceDir,
3689
+ workspaceSignalTrusted = true,
3690
+ ) {
3620
3691
  const out = safeObject(responseObj);
3621
3692
  const errObj = safeObject(out.error);
3622
3693
  const message = String(errObj.message || "").trim();
@@ -3648,6 +3719,7 @@ async function appendCtxpackConflictHintToErrorResponse(responseObj, args, toolN
3648
3719
  projectID: projectIDHint,
3649
3720
  ctxpack: safeObject(ctxpackRaw),
3650
3721
  workspaceDir,
3722
+ workspaceSignalTrusted,
3651
3723
  });
3652
3724
  autoPullHint = ` auto-pull: ${String(syncResult.sync_status || "error")}${
3653
3725
  syncResult.local_path ? ` (${String(syncResult.local_path)})` : ""
@@ -3684,6 +3756,7 @@ async function runProxy(flags) {
3684
3756
  let lastRefreshAttemptAtMs = 0;
3685
3757
  let lastRefreshError = "";
3686
3758
  let sessionWorkspaceDir = "";
3759
+ let sessionWorkspaceTrusted = false;
3687
3760
 
3688
3761
  const rl = readline.createInterface({
3689
3762
  input: process.stdin,
@@ -3741,15 +3814,31 @@ async function runProxy(flags) {
3741
3814
  }
3742
3815
 
3743
3816
  const { name: toolName, args: toolArgs } = extractToolCall(requestObj);
3817
+ let strongRequestWorkspaceCandidate = "";
3818
+ let weakRequestWorkspaceCandidate = "";
3819
+ let strongEnvWorkspaceCandidate = "";
3820
+ let weakEnvWorkspaceCandidate = "";
3744
3821
  if (!args.explicitPinnedWorkspace) {
3745
- const requestWorkspaceCandidate = extractWorkspaceCandidateFromRequest(requestObj, toolArgs);
3746
- const envWorkspaceCandidate = extractWorkspaceCandidateFromEnv();
3747
- if (requestWorkspaceCandidate) {
3748
- sessionWorkspaceDir = requestWorkspaceCandidate;
3749
- } else if (envWorkspaceCandidate) {
3750
- sessionWorkspaceDir = envWorkspaceCandidate;
3822
+ strongRequestWorkspaceCandidate = extractStrongWorkspaceCandidateFromRequest(requestObj, toolArgs);
3823
+ weakRequestWorkspaceCandidate = extractWeakWorkspaceCandidateFromRequest(requestObj);
3824
+ strongEnvWorkspaceCandidate = extractStrongWorkspaceCandidateFromEnv();
3825
+ weakEnvWorkspaceCandidate = extractWeakWorkspaceCandidateFromEnv();
3826
+ if (strongRequestWorkspaceCandidate) {
3827
+ sessionWorkspaceDir = strongRequestWorkspaceCandidate;
3828
+ sessionWorkspaceTrusted = true;
3829
+ } else if (strongEnvWorkspaceCandidate) {
3830
+ sessionWorkspaceDir = strongEnvWorkspaceCandidate;
3831
+ sessionWorkspaceTrusted = true;
3832
+ } else if (weakRequestWorkspaceCandidate) {
3833
+ sessionWorkspaceDir = weakRequestWorkspaceCandidate;
3834
+ } else if (weakEnvWorkspaceCandidate) {
3835
+ sessionWorkspaceDir = weakEnvWorkspaceCandidate;
3751
3836
  }
3752
3837
  }
3838
+ const workspaceSignalTrusted =
3839
+ args.explicitPinnedWorkspace ||
3840
+ sessionWorkspaceTrusted ||
3841
+ Boolean(strongRequestWorkspaceCandidate || strongEnvWorkspaceCandidate);
3753
3842
  const requestWorkspaceDir = args.explicitPinnedWorkspace
3754
3843
  ? resolveWorkspaceDir(args.workspaceDir || process.cwd())
3755
3844
  : resolveWorkspaceDirForRequest(
@@ -3766,6 +3855,7 @@ async function runProxy(flags) {
3766
3855
  args,
3767
3856
  token,
3768
3857
  workspaceDir: requestWorkspaceDir,
3858
+ workspaceSignalTrusted,
3769
3859
  });
3770
3860
  }
3771
3861
  if (isJsonRpcMethod(requestObj, "tools/call") && LOCAL_PROJECT_TOOL_NAMES.includes(toolName)) {
@@ -3809,6 +3899,7 @@ async function runProxy(flags) {
3809
3899
  includeCtxpack,
3810
3900
  syncCtxpackLocal,
3811
3901
  workspaceDir: requestWorkspaceDir,
3902
+ workspaceSignalTrusted,
3812
3903
  });
3813
3904
  const text = buildProjectSummaryText(summary);
3814
3905
  process.stdout.write(
@@ -3962,6 +4053,7 @@ async function runProxy(flags) {
3962
4053
  toolArgs,
3963
4054
  token,
3964
4055
  requestWorkspaceDir,
4056
+ workspaceSignalTrusted,
3965
4057
  );
3966
4058
  process.stdout.write(`${JSON.stringify(patched)}\n`);
3967
4059
  return;
@@ -3971,9 +4063,15 @@ async function runProxy(flags) {
3971
4063
  } else if (isJsonRpcMethod(requestObj, "initialize")) {
3972
4064
  patched = appendProjectHintToInitialize(patched, args);
3973
4065
  } else if (isJsonRpcMethod(requestObj, "tools/call") && toolName === "workitem.list") {
3974
- patched = await appendWorkitemListHints(patched, args, toolArgs, token);
4066
+ patched = await appendWorkitemListHints(patched, args, toolArgs, token, workspaceSignalTrusted);
3975
4067
  } else if (isJsonRpcMethod(requestObj, "tools/call") && toolName === "ctxpack.ensure") {
3976
- patched = appendCtxpackEnsureSyncHints(patched, args, toolArgs, requestObj);
4068
+ patched = appendCtxpackEnsureSyncHints(
4069
+ patched,
4070
+ args,
4071
+ toolArgs,
4072
+ requestObj,
4073
+ workspaceSignalTrusted,
4074
+ );
3977
4075
  }
3978
4076
  if (autoSyncSummary) {
3979
4077
  patched = appendAutoCtxpackSyncHint(patched, autoSyncSummary);
@@ -4070,18 +4168,23 @@ function resolveSetupContext(flags) {
4070
4168
  const ctxpackKey = String(flags["ctxpack-key"] || buildCtxpackKeyFromMeta(workspaceMeta) || "").trim();
4071
4169
  const baseURL = String(flags["base-url"] || DEFAULT_SITE_URL).trim().replace(/\/+$/, "");
4072
4170
  const workspaceDirRaw = String(flags["workspace-dir"] || "").trim();
4073
- const hasWorkspaceDirFlag = workspaceDirRaw.length > 0;
4074
- const workspaceAutoMode = hasWorkspaceDirFlag && isAutoWorkspaceMode(workspaceDirRaw);
4075
- // Default: pin to current working directory.
4076
- // Use --workspace-dir auto for dynamic runtime workspace detection.
4077
- const shouldPinWorkspaceDir = !workspaceAutoMode;
4171
+ const workspaceFallbackDirRaw = String(flags["workspace-fallback-dir"] || "").trim();
4172
+ const hasWorkspaceDirFlag = Object.prototype.hasOwnProperty.call(flags, "workspace-dir");
4173
+ // Default to auto workspace mode for safer multi-project Codex sessions.
4174
+ const workspaceAutoMode = !hasWorkspaceDirFlag || isAutoWorkspaceMode(workspaceDirRaw);
4175
+ // Pin only when user explicitly set a non-auto workspace-dir.
4176
+ const shouldPinWorkspaceDir = hasWorkspaceDirFlag && !workspaceAutoMode;
4177
+ const workspaceFallbackDir = workspaceFallbackDirRaw
4178
+ ? resolveWorkspaceDir(workspaceFallbackDirRaw)
4179
+ : "";
4078
4180
  const workspaceDir = resolveWorkspaceDir(
4079
4181
  shouldPinWorkspaceDir ? workspaceDirRaw || process.cwd() : process.cwd(),
4080
4182
  );
4081
4183
  const serverName = String(flags.name || DEFAULT_SERVER_NAME).trim() || DEFAULT_SERVER_NAME;
4082
4184
  const proxyArgs = ["--base-url", `${baseURL}/governance/mcp`];
4083
- // Pin workspace by default to avoid spreading ctxpack cache across mixed client roots.
4084
- if (shouldPinWorkspaceDir) {
4185
+ if (workspaceAutoMode) {
4186
+ proxyArgs.push("--workspace-dir", "auto");
4187
+ } else if (shouldPinWorkspaceDir) {
4085
4188
  proxyArgs.push("--workspace-dir", workspaceDir);
4086
4189
  }
4087
4190
  if (projectID) proxyArgs.push("--project-id", projectID);
@@ -4091,6 +4194,7 @@ function resolveSetupContext(flags) {
4091
4194
  ctxpackKey,
4092
4195
  baseURL,
4093
4196
  workspaceDir,
4197
+ workspaceFallbackDir,
4094
4198
  shouldPinWorkspaceDir,
4095
4199
  workspaceAutoMode,
4096
4200
  serverName,
@@ -4111,7 +4215,9 @@ function runSetupInternal(flags, options = {}) {
4111
4215
  runRemove(cliBin, context.serverName);
4112
4216
  }
4113
4217
  const ok = tryRegister(cliBin, context.serverName, context.proxyArgs, {
4114
- workspaceDir: context.workspaceDir,
4218
+ workspaceDir: context.shouldPinWorkspaceDir
4219
+ ? context.workspaceDir
4220
+ : context.workspaceFallbackDir,
4115
4221
  });
4116
4222
  const action = ensureOnly
4117
4223
  ? alreadyRegistered
@@ -4127,6 +4233,9 @@ function runSetupInternal(flags, options = {}) {
4127
4233
  process.stdout.write(`Server: ${context.serverName}\n`);
4128
4234
  process.stdout.write(`Gateway: ${context.baseURL}/governance/mcp\n`);
4129
4235
  process.stdout.write(`Workspace: ${context.shouldPinWorkspaceDir ? context.workspaceDir : "auto (client current folder)"}\n`);
4236
+ if (!context.shouldPinWorkspaceDir && context.workspaceFallbackDir) {
4237
+ process.stdout.write(`Fallback: ${context.workspaceFallbackDir} (METHEUS_WORKSPACE_DIR)\n`);
4238
+ }
4130
4239
  process.stdout.write(`Project: ${context.projectID || "auto-detect from .metheus_ctxpack_sync.json"}\n`);
4131
4240
  if (context.ctxpackKey) {
4132
4241
  process.stdout.write(`Ctxpack: ${context.ctxpackKey}\n`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.17",
3
+ "version": "0.2.19",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [