metheus-governance-mcp-cli 0.2.29 → 0.2.31

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 +1 -0
  2. package/cli.mjs +81 -8
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -60,6 +60,7 @@ metheus-governance-mcp-cli setup --project-id <project_uuid> --ctxpack-key "<ctx
60
60
  This sets fallback workspace context for clients that do not pass workspace metadata:
61
61
  - Codex/Antigravity/Cursor: `METHEUS_WORKSPACE_DIR` env
62
62
  - Gemini: pinned `--workspace-dir <fallback>` and `METHEUS_WORKSPACE_DIR` in MCP registration
63
+ - fallback env is used only when workspace cannot be resolved (or resolves to home); when current `cwd` is a real project folder, sync uses that folder.
63
64
 
64
65
  Guardrail note:
65
66
  - By default, CLI blocks reading/writing ctxpack sync metadata when workspace root resolves to the home directory.
package/cli.mjs CHANGED
@@ -639,18 +639,30 @@ function resolveWorkspaceDirForRequest(defaultWorkspaceDir, requestObj, toolArgs
639
639
  const weakRequestCandidate = extractWeakWorkspaceCandidateFromRequest(requestObj);
640
640
  const weakEnvCandidate = extractWeakWorkspaceCandidateFromEnv();
641
641
  const homeCandidate = firstNonEmptyString([process.env.USERPROFILE, process.env.HOME]);
642
- for (const rawCandidate of [
642
+ const localPreferredCandidates = [
643
643
  strongRequestCandidate,
644
- strongEnvCandidate,
645
644
  weakRequestCandidate,
646
- weakEnvCandidate,
647
645
  defaultWorkspaceDir,
648
646
  process.cwd(),
649
- homeCandidate,
650
- ]) {
647
+ ];
648
+ for (const rawCandidate of localPreferredCandidates) {
649
+ const resolved = sanitizeWorkspaceCandidate(rawCandidate);
650
+ if (!resolved) continue;
651
+ // If local candidate points to home directory, prefer explicit fallback env when available.
652
+ if (isHomeWorkspaceRoot(resolved)) {
653
+ const strongEnvResolved = sanitizeWorkspaceCandidate(strongEnvCandidate);
654
+ if (strongEnvResolved && !isHomeWorkspaceRoot(strongEnvResolved)) {
655
+ return strongEnvResolved;
656
+ }
657
+ }
658
+ return resolved;
659
+ }
660
+
661
+ for (const rawCandidate of [strongEnvCandidate, weakEnvCandidate, homeCandidate]) {
651
662
  const resolved = sanitizeWorkspaceCandidate(rawCandidate);
652
663
  if (resolved) return resolved;
653
664
  }
665
+
654
666
  return resolveWorkspaceDir(process.cwd());
655
667
  }
656
668
 
@@ -3573,6 +3585,49 @@ async function maybeAutoSyncCtxpackForCall({
3573
3585
  }
3574
3586
  }
3575
3587
 
3588
+ async function maybeAutoSyncCtxpackForSessionRequest({
3589
+ requestObj,
3590
+ args,
3591
+ token,
3592
+ workspaceDir,
3593
+ workspaceSignalTrusted = true,
3594
+ }) {
3595
+ if (!token) return null;
3596
+ const method = String(requestObj?.method || "").trim().toLowerCase();
3597
+ if (method !== "initialize" && method !== "tools/list") return null;
3598
+
3599
+ const projectID = resolveProjectIDForRequest({
3600
+ toolArgs: {},
3601
+ args,
3602
+ workspaceDir,
3603
+ });
3604
+ if (!isUUID(projectID)) return null;
3605
+
3606
+ const workspacePath = resolveWorkspaceDir(workspaceDir || process.cwd());
3607
+ const trackerKey = `${workspacePath}::${projectID}::session-bootstrap`;
3608
+ const now = Date.now();
3609
+ const lastAt = Number(autoCtxpackSyncTracker.get(trackerKey) || 0);
3610
+ if (lastAt > 0 && now - lastAt < AUTO_CTXPACK_SYNC_INTERVAL_MS) {
3611
+ return null;
3612
+ }
3613
+ autoCtxpackSyncTracker.set(trackerKey, now);
3614
+
3615
+ try {
3616
+ return await loadProjectSummaryForTool({
3617
+ siteBaseURL: normalizeSiteBaseURL(args.baseURL),
3618
+ projectID,
3619
+ token,
3620
+ timeoutSeconds: args.timeoutSeconds,
3621
+ includeCtxpack: true,
3622
+ syncCtxpackLocal: true,
3623
+ workspaceDir: workspacePath,
3624
+ workspaceSignalTrusted,
3625
+ });
3626
+ } catch {
3627
+ return null;
3628
+ }
3629
+ }
3630
+
3576
3631
  function appendAutoCtxpackSyncHint(responseObj, summary) {
3577
3632
  const status = String(summary?.ctxpack_sync_status || "").trim();
3578
3633
  if (!status || status === "disabled" || status === "not_attempted" || status === "current") {
@@ -4042,14 +4097,23 @@ async function runProxy(flags) {
4042
4097
  if (strongRequestWorkspaceCandidate) {
4043
4098
  sessionWorkspaceDir = strongRequestWorkspaceCandidate;
4044
4099
  sessionWorkspaceTrusted = true;
4045
- } else if (strongEnvWorkspaceCandidate) {
4046
- sessionWorkspaceDir = strongEnvWorkspaceCandidate;
4047
- sessionWorkspaceTrusted = true;
4048
4100
  } else if (weakRequestWorkspaceCandidate) {
4049
4101
  sessionWorkspaceDir = weakRequestWorkspaceCandidate;
4050
4102
  } else if (weakEnvWorkspaceCandidate) {
4051
4103
  sessionWorkspaceDir = weakEnvWorkspaceCandidate;
4052
4104
  }
4105
+ if (!sessionWorkspaceDir) {
4106
+ const currentCwdCandidate = sanitizeWorkspaceCandidate(process.cwd());
4107
+ if (currentCwdCandidate && !isHomeWorkspaceRoot(currentCwdCandidate)) {
4108
+ sessionWorkspaceDir = currentCwdCandidate;
4109
+ }
4110
+ }
4111
+ if (strongEnvWorkspaceCandidate) {
4112
+ sessionWorkspaceTrusted = true;
4113
+ if (!sessionWorkspaceDir || isHomeWorkspaceRoot(sessionWorkspaceDir)) {
4114
+ sessionWorkspaceDir = strongEnvWorkspaceCandidate;
4115
+ }
4116
+ }
4053
4117
  }
4054
4118
  const workspaceSignalTrusted =
4055
4119
  args.explicitPinnedWorkspace ||
@@ -4074,6 +4138,15 @@ async function runProxy(flags) {
4074
4138
  workspaceSignalTrusted,
4075
4139
  });
4076
4140
  }
4141
+ if (!autoSyncSummary) {
4142
+ autoSyncSummary = await maybeAutoSyncCtxpackForSessionRequest({
4143
+ requestObj,
4144
+ args,
4145
+ token,
4146
+ workspaceDir: requestWorkspaceDir,
4147
+ workspaceSignalTrusted,
4148
+ });
4149
+ }
4077
4150
  if (isJsonRpcMethod(requestObj, "tools/call") && LOCAL_PROJECT_TOOL_NAMES.includes(toolName)) {
4078
4151
  const projectID = String(
4079
4152
  resolveProjectIDForRequest({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.29",
3
+ "version": "0.2.31",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [