@getmarrow/install 0.1.42 → 0.1.43

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
@@ -71,7 +71,7 @@ npx -y @getmarrow/install@latest activate
71
71
  npx -y @getmarrow/install@latest doctor
72
72
 
73
73
  # Measured API read health and local backlog
74
- npx -y @getmarrow/mcp@latest ping
74
+ npx -y --package=@getmarrow/mcp@latest marrow-mcp ping
75
75
 
76
76
  # Use only when doctor reports drift
77
77
  npx -y @getmarrow/install@latest --repair
@@ -91,16 +91,21 @@ npx @getmarrow/install controller stop
91
91
 
92
92
  Persistent controller lifecycle is currently Linux-only. On macOS or Windows, activation still installs and verifies the supported hooks without starting or signaling a background process; run `npx @getmarrow/install sidecar` under an owner-managed service and pass `--no-controller`. The controller does not silently upgrade packages, change governance policy, rotate credentials, or modify unrelated project configuration.
93
93
 
94
- ## What's New in v0.1.42
94
+ ## What's New in v0.1.43
95
95
 
96
- v0.1.42 keeps stale client detection and recovery deterministic even when other Marrow MCP clients are running on the installation host:
96
+ v0.1.43 hardens the MCP control path and makes stale client recovery explicit:
97
97
 
98
- - `doctor` detects active stale, mixed, or version-unknown Marrow MCP processes, including direct `node_modules/.bin/marrow-mcp` launches, without exposing command lines or credentials;
99
- - when repair is needed, `doctor` reports the executable pinned setup command, the separate owning-harness restart requirement, and the executable verification command; it does not terminate harness processes itself;
100
- - certified hooks pin MCP `3.9.57` and SDK `3.7.56` so the installed runtime matches the advertised control contract;
98
+ - `doctor` detects active and configured stale, mixed, or version-unknown Marrow MCP clients without exposing command lines, file paths, configuration contents, or credentials;
99
+ - every generated MCP launch and hook uses the package-explicit `npx --package ... marrow-mcp` form so npm can resolve the executable reliably;
100
+ - when repair is needed, `doctor` reports the pinned setup command, the separate owning-harness restart requirement, and a self-test verification command; it does not terminate harness processes itself;
101
+ - certified hooks pin MCP `3.9.59` and SDK `3.7.56` so the installed runtime matches the advertised control contract;
101
102
  - existing harnesses retain their honest coverage level: native hooks where supported, MCP calls where available, and governed wrappers or event contracts elsewhere;
102
103
  - package upgrades remain operator-approved and never rotate keys or rewrite unrelated configuration.
103
104
 
105
+ ## Previous: v0.1.42
106
+
107
+ v0.1.42 added deterministic active-process detection, including direct `node_modules/.bin/marrow-mcp` launches, while keeping repair operator-approved.
108
+
104
109
  ## Previous: v0.1.40
105
110
 
106
111
  v0.1.40 binds governed runs to a privacy-safe workspace fingerprint and separates observed execution from verified completion:
@@ -108,7 +113,7 @@ v0.1.40 binds governed runs to a privacy-safe workspace fingerprint and separate
108
113
  - ordinary prompts receive one compact context read; risky or mutating prompts receive one fresh runtime gate instead;
109
114
  - passive prompt telemetry is buffered locally rather than delaying the agent turn;
110
115
  - transient read failures can use clearly labeled owner-only last-known guidance, while authentication failures never use cache;
111
- - `doctor` prints the exact `npx -y @getmarrow/mcp@latest ping` command for measured current/p50/p99 latency, last success, and backlog health;
116
+ - `doctor` prints the exact `npx -y --package=@getmarrow/mcp@latest marrow-mcp ping` command for measured current/p50/p99 latency, last success, and backlog health;
112
117
  - certified hook commands pin MCP `3.9.56` and SDK `3.7.55` so advertised behavior matches that release's deployed server contract;
113
118
  - governed runtime requests attach a stable privacy-safe project fingerprint and harness label without sending the raw working-directory path;
114
119
  - successful command exit remains observed execution, not verified business completion, unless a verification command or explicit proof file supplies evidence;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getmarrow/install",
3
- "version": "0.1.42",
3
+ "version": "0.1.43",
4
4
  "description": "Universal installer and governed runner for Marrow agent fleets.",
5
5
  "bin": {
6
6
  "marrow-install": "bin/marrow-install.js"
@@ -1387,11 +1387,11 @@ const GENERIC_GOVERNED_COMMAND = 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/ins
1387
1387
  function localSupportedHarnesses() {
1388
1388
  const harnesses = [
1389
1389
  { display_name: 'OpenAI Codex', client_label: 'codex', category: 'agent_harness', support_level: 'governed_runner', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install run --agent codex-prod -- codex' },
1390
- { display_name: 'Claude Code', client_label: 'claude-code', category: 'agent_harness', support_level: 'native_mcp_or_sdk', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/mcp setup' },
1391
- { display_name: 'Cursor', client_label: 'cursor', category: 'ide_agent', support_level: 'native_mcp_or_sdk', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/mcp setup' },
1392
- { display_name: 'Cursor Composer', client_label: 'composer', category: 'ide_agent', support_level: 'native_mcp_or_sdk', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/mcp setup' },
1393
- { display_name: 'Windsurf', client_label: 'windsurf', category: 'ide_agent', support_level: 'native_mcp_or_sdk', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/mcp setup' },
1394
- { display_name: 'Cline', client_label: 'cline', category: 'ide_agent', support_level: 'native_mcp_or_sdk', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/mcp setup' },
1390
+ { display_name: 'Claude Code', client_label: 'claude-code', category: 'agent_harness', support_level: 'native_mcp_or_sdk', install_command: 'MARROW_API_KEY=mrw_live_xxx npx -y --package=@getmarrow/mcp@latest marrow-mcp setup' },
1391
+ { display_name: 'Cursor', client_label: 'cursor', category: 'ide_agent', support_level: 'native_mcp_or_sdk', install_command: 'MARROW_API_KEY=mrw_live_xxx npx -y --package=@getmarrow/mcp@latest marrow-mcp setup' },
1392
+ { display_name: 'Cursor Composer', client_label: 'composer', category: 'ide_agent', support_level: 'native_mcp_or_sdk', install_command: 'MARROW_API_KEY=mrw_live_xxx npx -y --package=@getmarrow/mcp@latest marrow-mcp setup' },
1393
+ { display_name: 'Windsurf', client_label: 'windsurf', category: 'ide_agent', support_level: 'native_mcp_or_sdk', install_command: 'MARROW_API_KEY=mrw_live_xxx npx -y --package=@getmarrow/mcp@latest marrow-mcp setup' },
1394
+ { display_name: 'Cline', client_label: 'cline', category: 'ide_agent', support_level: 'native_mcp_or_sdk', install_command: 'MARROW_API_KEY=mrw_live_xxx npx -y --package=@getmarrow/mcp@latest marrow-mcp setup' },
1395
1395
  { display_name: 'OpenCode', client_label: 'opencode', category: 'agent_harness', support_level: 'governed_runner', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install run --agent opencode-prod -- opencode' },
1396
1396
  { display_name: 'Hermes Agent', client_label: 'hermes', category: 'agent_harness', support_level: 'first_class_addon', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install hermes' },
1397
1397
  { display_name: 'OpenClaw', client_label: 'openclaw', category: 'agent_harness', support_level: 'first_class_addon', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install openclaw' },
@@ -1402,7 +1402,7 @@ function localSupportedHarnesses() {
1402
1402
  { display_name: 'Kimi', client_label: 'kimi', category: 'model_cli', support_level: 'governed_runner', install_command: GENERIC_GOVERNED_COMMAND },
1403
1403
  { display_name: 'MiniMax', client_label: 'minimax', category: 'model_cli', support_level: 'governed_runner', install_command: GENERIC_GOVERNED_COMMAND },
1404
1404
  { display_name: 'GLM', client_label: 'glm', category: 'model_cli', support_level: 'governed_runner', install_command: GENERIC_GOVERNED_COMMAND },
1405
- { display_name: 'MCP-compatible clients', client_label: 'mcp', category: 'mcp_client', support_level: 'native_mcp_or_sdk', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/mcp setup' },
1405
+ { display_name: 'MCP-compatible clients', client_label: 'mcp', category: 'mcp_client', support_level: 'native_mcp_or_sdk', install_command: 'MARROW_API_KEY=mrw_live_xxx npx -y --package=@getmarrow/mcp@latest marrow-mcp setup' },
1406
1406
  { display_name: 'CI scripts and deploy runners', client_label: 'ci', category: 'ci_runner', support_level: 'governed_runner', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install run --agent ci-release --profile production --policy enforce -- <ci-or-deploy-command>' },
1407
1407
  { display_name: 'Custom shell/API harness', client_label: 'custom', category: 'custom_runner', support_level: 'event_contract', install_command: 'POST /v1/agent/integrations/events with harness, event_type, agent_id, and action' },
1408
1408
  ];
package/src/installer.js CHANGED
@@ -8,15 +8,15 @@ const { controllerStatus, controllerSupportedPlatform, ensureGovernanceControlle
8
8
  const DEFAULT_BASE_URL = 'https://api.getmarrow.ai';
9
9
  const MARROW_BLOCK_START = '<!-- marrow:passive-start -->';
10
10
  const MARROW_BLOCK_END = '<!-- marrow:passive-end -->';
11
- const MCP_ADAPTER_VERSION = '3.9.57';
11
+ const MCP_ADAPTER_VERSION = '3.9.59';
12
12
  const SDK_ADAPTER_VERSION = '3.7.56';
13
13
  const SDK_ADAPTER_INTEGRITY = 'sha512-5htliY4wfn8a1mbLT9N4OWXqhp9fWzMHuAQgUetc3RUKjOes5mWB3t91/leRKFRRIgCnEczJN6jHXg7Aw489Mw==';
14
14
  const SDK_ADAPTER_TARBALL = `https://registry.npmjs.org/@getmarrow/sdk/-/sdk-${SDK_ADAPTER_VERSION}.tgz`;
15
15
  const MCP_PACKAGE_SPEC = `@getmarrow/mcp@${MCP_ADAPTER_VERSION}`;
16
- const MCP_CONTEXT_HOOK_COMMAND = `npx -y ${MCP_PACKAGE_SPEC} context-hook`;
17
- const MCP_PRE_ACTION_HOOK_COMMAND = `npx -y ${MCP_PACKAGE_SPEC} pre-action-hook`;
18
- const MCP_ACTION_RESULT_HOOK_COMMAND = `npx -y ${MCP_PACKAGE_SPEC} hook`;
19
- const MCP_SESSION_END_HOOK_COMMAND = `npx -y ${MCP_PACKAGE_SPEC} session-hook`;
16
+ const MCP_CONTEXT_HOOK_COMMAND = `npx -y --package=${MCP_PACKAGE_SPEC} marrow-mcp context-hook`;
17
+ const MCP_PRE_ACTION_HOOK_COMMAND = `npx -y --package=${MCP_PACKAGE_SPEC} marrow-mcp pre-action-hook`;
18
+ const MCP_ACTION_RESULT_HOOK_COMMAND = `npx -y --package=${MCP_PACKAGE_SPEC} marrow-mcp hook`;
19
+ const MCP_SESSION_END_HOOK_COMMAND = `npx -y --package=${MCP_PACKAGE_SPEC} marrow-mcp session-hook`;
20
20
  const NATIVE_HOOK_MATCHER = 'Bash|Edit|Write|MultiEdit|mcp__(?!marrow_).*';
21
21
  const NATIVE_EXPECTED_HOOKS = ['prompt', 'pre_action', 'action_result', 'session_end'];
22
22
  const SOURCE_CLIENTS = new Set(['claude-code', 'cursor', 'composer', 'windsurf', 'openclaw', 'codex', 'gemini', 'grok', 'deepseek', 'qwen', 'kimi', 'minimax', 'cline', 'opencode', 'hermes', 'glm', 'mcp', 'ci', 'custom', 'unknown']);
@@ -101,7 +101,7 @@ function isMcpProcessCommand(command) {
101
101
  const packageManagers = new Set(['bun', 'bunx', 'npm', 'npm-cli.js', 'npx', 'npx-cli.js', 'pnpm', 'pnpx', 'yarn']);
102
102
  const runner = executable === 'node' && args[1] ? path.basename(args[1]) : executable;
103
103
  return packageManagers.has(runner)
104
- && args.some((arg) => /^@getmarrow\/mcp(?:@[^\s]+)?$/.test(arg));
104
+ && args.some((arg) => /^(?:--package=)?@getmarrow\/mcp(?:@[^\s]+)?$/.test(arg));
105
105
  }
106
106
 
107
107
  function readLinuxProcessCommands(procRoot = '/proc') {
@@ -135,7 +135,7 @@ function inspectMcpProcesses(options = {}) {
135
135
  const mixedVersions = versions.length > 1 || (versions.length > 0 && unknownVersionProcesses > 0);
136
136
  const stale = staleVersions.length > 0;
137
137
  const needsRepair = stale || mixedVersions || unknownVersionProcesses > 0;
138
- const repairCommand = `npx -y @getmarrow/mcp@${MCP_ADAPTER_VERSION} setup`;
138
+ const repairCommand = `npx -y --package=@getmarrow/mcp@${MCP_ADAPTER_VERSION} marrow-mcp setup`;
139
139
  return {
140
140
  available: process.platform === 'linux' || Array.isArray(options.commands),
141
141
  expected_version: MCP_ADAPTER_VERSION,
@@ -148,7 +148,59 @@ function inspectMcpProcesses(options = {}) {
148
148
  exact_fix: needsRepair ? repairCommand : null,
149
149
  restart_required: needsRepair,
150
150
  restart_instruction: needsRepair ? 'Restart every owning harness to replace its active Marrow MCP process.' : null,
151
- verification_command: needsRepair ? 'npx -y @getmarrow/install@latest doctor' : null,
151
+ verification_command: needsRepair ? 'npx -y @getmarrow/install@latest doctor --self-test' : null,
152
+ };
153
+ }
154
+
155
+ function inspectMcpConfigurations(detection, options = {}) {
156
+ const home = options.home || process.env.HOME || process.env.USERPROFILE || os.homedir();
157
+ const configuredPaths = Array.isArray(options.paths) ? options.paths : [
158
+ detection?.paths?.claudeSettings,
159
+ detection?.paths?.cursorMcp,
160
+ detection?.paths?.mcpJson,
161
+ path.join(home, '.claude', 'settings.json'),
162
+ path.join(home, '.claude.json'),
163
+ path.join(home, '.cursor', 'mcp.json'),
164
+ path.join(home, '.mcp.json'),
165
+ ];
166
+ const versions = [];
167
+ let filesChecked = 0;
168
+ let configurationsFound = 0;
169
+ let unknownVersionConfigurations = 0;
170
+ for (const filePath of [...new Set(configuredPaths.filter(Boolean).map((entry) => path.resolve(String(entry))))]) {
171
+ try {
172
+ if (!fs.existsSync(filePath)) continue;
173
+ const stat = fs.lstatSync(filePath);
174
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 2 * 1024 * 1024) continue;
175
+ filesChecked += 1;
176
+ const raw = fs.readFileSync(filePath, 'utf8');
177
+ const specs = [...raw.matchAll(/@getmarrow\/mcp@(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)/g)]
178
+ .map((match) => match[1]);
179
+ if (/@getmarrow\/mcp(?:@|["'\s])/.test(raw)) {
180
+ configurationsFound += 1;
181
+ if (specs.length === 0) unknownVersionConfigurations += 1;
182
+ }
183
+ versions.push(...specs);
184
+ } catch {
185
+ // Doctor remains read-only and ignores inaccessible or malformed owner configuration.
186
+ }
187
+ }
188
+ const configuredVersions = [...new Set(versions)].sort();
189
+ const staleVersions = configuredVersions.filter((version) => version !== MCP_ADAPTER_VERSION);
190
+ const mixedVersions = configuredVersions.length > 1
191
+ || (configuredVersions.length > 0 && unknownVersionConfigurations > 0);
192
+ const healthy = staleVersions.length === 0 && !mixedVersions && unknownVersionConfigurations === 0;
193
+ return {
194
+ expected_version: MCP_ADAPTER_VERSION,
195
+ files_checked: filesChecked,
196
+ configurations_found: configurationsFound,
197
+ configured_versions: configuredVersions,
198
+ unknown_version_configurations: unknownVersionConfigurations,
199
+ stale_versions: staleVersions,
200
+ mixed_versions: mixedVersions,
201
+ healthy,
202
+ exact_fix: healthy ? null : `Run npx -y --package=@getmarrow/mcp@${MCP_ADAPTER_VERSION} marrow-mcp setup in each owning workspace, update its MCP launch to npx -y --package=@getmarrow/mcp@${MCP_ADAPTER_VERSION} marrow-mcp, then restart that harness.`,
203
+ verification_command: healthy ? null : 'npx -y @getmarrow/install@latest doctor --self-test',
152
204
  };
153
205
  }
154
206
 
@@ -549,7 +601,7 @@ function passiveInstructions() {
549
601
 
550
602
  Marrow should run passively after install:
551
603
 
552
- - Use MCP hooks when available: \`npx -y @getmarrow/mcp setup\`.
604
+ - Use MCP hooks when available: \`npx -y --package=@getmarrow/mcp@latest marrow-mcp setup\`.
553
605
  - Use SDK passive runtime in owned Node processes: \`createPassiveRuntime().install()\`.
554
606
  - Keep passive token/model usage proof enabled so Marrow can show token, cost, latency, and workflow savings after real work completes.
555
607
  - Before risky work, use Marrow's decision brief or passive prompt hook.
@@ -674,7 +726,7 @@ function exactHookDescriptors(settings, eventName, command, matcher) {
674
726
  function marrowHookSubcommand(command) {
675
727
  if (typeof command !== 'string') return null;
676
728
  const match = command.trim().match(
677
- /^npx\s+(?:-y\s+)?@getmarrow\/mcp(?:@[^\s]+)?\s+(context-hook|pre-action-hook|hook|session-hook)$/,
729
+ /^npx\s+(?:-y\s+)?(?:--package=@getmarrow\/mcp(?:@[^\s]+)?\s+marrow-mcp|@getmarrow\/mcp(?:@[^\s]+)?)\s+(context-hook|pre-action-hook|hook|session-hook)$/,
678
730
  );
679
731
  return match?.[1] || null;
680
732
  }
@@ -839,7 +891,7 @@ function activationProfile(detection, plan, changes, client) {
839
891
  if (capabilityLevel === 'mcp' && mcpConfigs.some((config) => (
840
892
  config?.mcpServers?.marrow?.command === 'npx'
841
893
  && Array.isArray(config.mcpServers.marrow.args)
842
- && config.mcpServers.marrow.args.join(' ') === `-y ${MCP_PACKAGE_SPEC}`
894
+ && config.mcpServers.marrow.args.join(' ') === `-y --package=${MCP_PACKAGE_SPEC} marrow-mcp`
843
895
  ))) observedHooks.push('mcp_tool_calls');
844
896
  const fingerprintMaterial = changes
845
897
  .filter((change) => change.applied || change.already_present)
@@ -877,7 +929,7 @@ function upsertMcpServerConfig(filePath) {
877
929
  : {};
878
930
  servers.marrow = {
879
931
  command: 'npx',
880
- args: ['-y', MCP_PACKAGE_SPEC],
932
+ args: ['-y', `--package=${MCP_PACKAGE_SPEC}`, 'marrow-mcp'],
881
933
  env: {
882
934
  MARROW_API_KEY: '${MARROW_API_KEY}',
883
935
  MARROW_BASE_URL: '${MARROW_BASE_URL}',
@@ -1647,6 +1699,11 @@ function printReport(report) {
1647
1699
  if (processes.restart_instruction) process.stdout.write(`- restart required: ${processes.restart_instruction}\n`);
1648
1700
  if (processes.verification_command) process.stdout.write(`- verify repair: ${processes.verification_command}\n`);
1649
1701
  }
1702
+ if (report.doctor.mcpConfigurations) {
1703
+ const configurations = report.doctor.mcpConfigurations;
1704
+ process.stdout.write(`- configured MCP versions: ${configurations.configured_versions.length ? configurations.configured_versions.join(', ') : 'none pinned'}\n`);
1705
+ process.stdout.write(`- stale/mixed/version-unknown MCP configuration: ${configurations.healthy ? 'no' : 'yes'}\n`);
1706
+ }
1650
1707
  if (report.doctor.recommendedFix) process.stdout.write(`- recommended fix: ${report.doctor.recommendedFix}\n`);
1651
1708
  process.stdout.write(`- live health: ${report.doctor.healthCommand}\n`);
1652
1709
  }
@@ -1708,6 +1765,7 @@ async function install(options) {
1708
1765
  : [];
1709
1766
  const envHints = options.apiKey ? [] : findLikelyEnvFiles(detection);
1710
1767
  const mcpProcesses = inspectMcpProcesses({ commands: options.processCommands });
1768
+ const mcpConfigurations = inspectMcpConfigurations(detection, { paths: options.mcpConfigPaths });
1711
1769
  let selfTest;
1712
1770
  try {
1713
1771
  selfTest = await runSelfTest(options);
@@ -1800,12 +1858,13 @@ async function install(options) {
1800
1858
  envHints,
1801
1859
  missingHooks: changes.filter((change) => change.changed).map((change) => change.label),
1802
1860
  mcpProcesses,
1803
- recommendedFix: mcpProcesses.exact_fix || configDiagnostics.npm_token.recommended_fix || (!options.apiKey
1861
+ mcpConfigurations,
1862
+ recommendedFix: mcpProcesses.exact_fix || mcpConfigurations.exact_fix || configDiagnostics.npm_token.recommended_fix || (!options.apiKey
1804
1863
  ? envHints.length
1805
1864
  ? `MARROW_API_KEY was found in a likely env file at ${envHints[0]}. Load that key from trusted secret storage, export only MARROW_API_KEY, then run npx @getmarrow/install --repair.`
1806
1865
  : 'Set MARROW_API_KEY, then run npx @getmarrow/install --repair.'
1807
1866
  : controller.exact_fix || selfTest.recommended_fix || null),
1808
- healthCommand: 'npx -y @getmarrow/mcp@latest ping',
1867
+ healthCommand: 'npx -y --package=@getmarrow/mcp@latest marrow-mcp ping',
1809
1868
  },
1810
1869
  remediation,
1811
1870
  configDiagnostics,
@@ -1820,6 +1879,9 @@ async function install(options) {
1820
1879
  ...(!mcpProcesses.healthy
1821
1880
  ? ['Stale, mixed, or version-unknown Marrow MCP clients are active. Run the exact repair command, then restart every owning harness.']
1822
1881
  : []),
1882
+ ...(!mcpConfigurations.healthy
1883
+ ? ['Stale, mixed, or version-unknown Marrow MCP versions remain in owner configuration. Repair each owning workspace and restart its harness.']
1884
+ : []),
1823
1885
  ],
1824
1886
  };
1825
1887
  }
@@ -1850,6 +1912,7 @@ module.exports = {
1850
1912
  inspectNpmTokenConfig,
1851
1913
  inspectSdkDependency,
1852
1914
  inspectMcpProcesses,
1915
+ inspectMcpConfigurations,
1853
1916
  buildInstallValueMoment,
1854
1917
  buildTokenValueProof,
1855
1918
  stableAgentId,