@neat.is/core 0.7.5 → 0.7.7

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/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  resolveHost,
7
7
  resolveNeatVersion,
8
8
  writeDaemonRecord
9
- } from "./chunk-MYLUXVXE.js";
9
+ } from "./chunk-YVZRBT4C.js";
10
10
  import {
11
11
  buildSearchIndex
12
12
  } from "./chunk-BIY46Q6U.js";
@@ -74,10 +74,10 @@ import {
74
74
  startStalenessLoop,
75
75
  upsertConnectorEntry,
76
76
  validateConnectorEntry
77
- } from "./chunk-KB4HRB6N.js";
77
+ } from "./chunk-UI3AFJAF.js";
78
78
  import {
79
79
  startOtelGrpcReceiver
80
- } from "./chunk-6H757ZNM.js";
80
+ } from "./chunk-4GCV46AP.js";
81
81
  import {
82
82
  __dirname,
83
83
  __require,
@@ -85,7 +85,7 @@ import {
85
85
  buildOtelReceiver,
86
86
  listenSteppingOtlp,
87
87
  readAuthEnv
88
- } from "./chunk-P2ZEKJ35.js";
88
+ } from "./chunk-Y43UCVZS.js";
89
89
 
90
90
  // src/cli.ts
91
91
  import path13 from "path";
@@ -4749,24 +4749,42 @@ async function runCodexCommand(args) {
4749
4749
  import path11 from "path";
4750
4750
  import os3 from "os";
4751
4751
  import { promises as fs10 } from "fs";
4752
+ import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
4753
+ import * as jsonc from "jsonc-parser";
4752
4754
  var NEAT_MCP_SERVER = {
4753
4755
  command: "npx",
4754
4756
  args: ["-y", "@neat.is/mcp"]
4755
4757
  };
4758
+ var NEAT_OPENCODE_SERVER = {
4759
+ type: "local",
4760
+ command: ["npx", "-y", "@neat.is/mcp"],
4761
+ enabled: true
4762
+ };
4763
+ var NEAT_CRUSH_SERVER = {
4764
+ type: "stdio",
4765
+ command: "npx",
4766
+ args: ["-y", "@neat.is/mcp"]
4767
+ };
4756
4768
  var GRAPH_FIRST_MARKER_OPEN = "<!-- neat:graph-first -->";
4757
4769
  var GRAPH_FIRST_MARKER_CLOSE = "<!-- /neat:graph-first -->";
4758
4770
  function homeDir() {
4759
4771
  return process.env.HOME ?? process.env.USERPROFILE ?? os3.homedir();
4760
4772
  }
4773
+ function xdgConfigDir() {
4774
+ const xdg = process.env.XDG_CONFIG_HOME;
4775
+ return xdg && xdg.length > 0 ? path11.resolve(xdg) : path11.join(homeDir(), ".config");
4776
+ }
4777
+ function envOverride(name) {
4778
+ const v = process.env[name];
4779
+ return v && v.length > 0 ? path11.resolve(v) : void 0;
4780
+ }
4761
4781
  var CURSOR_CLIENT = {
4762
4782
  id: "cursor",
4763
4783
  label: "Cursor",
4764
4784
  docsUrl: "https://docs.cursor.com/context/mcp",
4765
- mcpConfigPath: () => {
4766
- const override = process.env.NEAT_CURSOR_CONFIG;
4767
- if (override && override.length > 0) return path11.resolve(override);
4768
- return path11.join(homeDir(), ".cursor", "mcp.json");
4769
- },
4785
+ mcpConfigPath: () => envOverride("NEAT_CURSOR_CONFIG") ?? path11.join(homeDir(), ".cursor", "mcp.json"),
4786
+ mcpContainerKey: "mcpServers",
4787
+ format: "json",
4770
4788
  // Cursor still reads a single `.cursorrules` at the project root (the modern
4771
4789
  // `.cursor/rules/*.mdc` split is one-rule-per-file with frontmatter — a worse
4772
4790
  // fit for a marker-fenced block). GRAPH_FIRST.md names this file directly.
@@ -4776,22 +4794,116 @@ var DEVIN_CLIENT = {
4776
4794
  id: "devin",
4777
4795
  label: "Devin Desktop (Cascade)",
4778
4796
  docsUrl: "https://docs.devin.ai/desktop/cascade/mcp",
4797
+ mcpConfigPath: () => envOverride("NEAT_DEVIN_CONFIG") ?? path11.join(homeDir(), ".codeium", "windsurf", "mcp_config.json"),
4798
+ mcpContainerKey: "mcpServers",
4799
+ format: "json",
4800
+ rulesFileName: ".windsurfrules"
4801
+ };
4802
+ var GEMINI_CLIENT = {
4803
+ id: "gemini",
4804
+ label: "Gemini CLI",
4805
+ docsUrl: "https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md",
4806
+ mcpConfigPath: () => envOverride("NEAT_GEMINI_CONFIG") ?? path11.join(homeDir(), ".gemini", "settings.json"),
4807
+ mcpContainerKey: "mcpServers",
4808
+ format: "json",
4809
+ rulesFileName: "GEMINI.md"
4810
+ };
4811
+ var QWEN_CLIENT = {
4812
+ id: "qwen",
4813
+ label: "Qwen Code",
4814
+ docsUrl: "https://qwenlm.github.io/qwen-code-docs/en/users/features/mcp/",
4815
+ mcpConfigPath: () => envOverride("NEAT_QWEN_CONFIG") ?? path11.join(homeDir(), ".qwen", "settings.json"),
4816
+ mcpContainerKey: "mcpServers",
4817
+ format: "json",
4818
+ rulesFileName: "QWEN.md"
4819
+ };
4820
+ var AMAZONQ_CLIENT = {
4821
+ id: "amazonq",
4822
+ label: "Amazon Q Developer CLI",
4823
+ docsUrl: "https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-mcp-configuration.html",
4824
+ mcpConfigPath: () => envOverride("NEAT_AMAZONQ_CONFIG") ?? path11.join(homeDir(), ".aws", "amazonq", "mcp.json"),
4825
+ mcpContainerKey: "mcpServers",
4826
+ format: "json"
4827
+ };
4828
+ var ROOCODE_CLIENT = {
4829
+ id: "roocode",
4830
+ label: "Roo Code",
4831
+ docsUrl: "https://roocodeinc.github.io/Roo-Code/features/mcp/using-mcp-in-roo",
4832
+ mcpConfigPath: () => envOverride("NEAT_ROOCODE_CONFIG") ?? path11.join(process.cwd(), ".roo", "mcp.json"),
4833
+ mcpContainerKey: "mcpServers",
4834
+ format: "json"
4835
+ };
4836
+ var ZED_CLIENT = {
4837
+ id: "zed",
4838
+ label: "Zed",
4839
+ docsUrl: "https://zed.dev/docs/ai/mcp",
4779
4840
  mcpConfigPath: () => {
4780
- const override = process.env.NEAT_DEVIN_CONFIG;
4781
- if (override && override.length > 0) return path11.resolve(override);
4782
- return path11.join(homeDir(), ".codeium", "windsurf", "mcp_config.json");
4841
+ const override = envOverride("NEAT_ZED_CONFIG");
4842
+ if (override) return override;
4843
+ if (process.platform === "win32") {
4844
+ const appData = process.env.APPDATA;
4845
+ if (appData && appData.length > 0) return path11.join(appData, "Zed", "settings.json");
4846
+ }
4847
+ return path11.join(homeDir(), ".config", "zed", "settings.json");
4783
4848
  },
4784
- rulesFileName: ".windsurfrules"
4849
+ mcpContainerKey: "context_servers",
4850
+ format: "jsonc",
4851
+ rulesFileName: ".rules"
4852
+ };
4853
+ var OPENCODE_CLIENT = {
4854
+ id: "opencode",
4855
+ label: "OpenCode",
4856
+ docsUrl: "https://opencode.ai/docs/mcp-servers/",
4857
+ mcpConfigPath: () => envOverride("NEAT_OPENCODE_CONFIG") ?? path11.join(xdgConfigDir(), "opencode", "opencode.json"),
4858
+ mcpContainerKey: "mcp",
4859
+ format: "json",
4860
+ serverEntry: NEAT_OPENCODE_SERVER,
4861
+ rulesFileName: "AGENTS.md"
4862
+ };
4863
+ var CRUSH_CLIENT = {
4864
+ id: "crush",
4865
+ label: "Crush",
4866
+ docsUrl: "https://charmbracelet-crush.mintlify.app/configuration/mcp",
4867
+ mcpConfigPath: () => envOverride("NEAT_CRUSH_CONFIG") ?? path11.join(xdgConfigDir(), "crush", "crush.json"),
4868
+ mcpContainerKey: "mcp",
4869
+ format: "json",
4870
+ serverEntry: NEAT_CRUSH_SERVER,
4871
+ rulesFileName: "AGENTS.md"
4785
4872
  };
4786
- function mergeMcpConfig(existing) {
4787
- const servers = existing.mcpServers ?? {};
4788
- const already = JSON.stringify(servers.neat) === JSON.stringify(NEAT_MCP_SERVER);
4873
+ var CLIENTS = {
4874
+ cursor: CURSOR_CLIENT,
4875
+ devin: DEVIN_CLIENT,
4876
+ gemini: GEMINI_CLIENT,
4877
+ qwen: QWEN_CLIENT,
4878
+ amazonq: AMAZONQ_CLIENT,
4879
+ roocode: ROOCODE_CLIENT,
4880
+ zed: ZED_CLIENT,
4881
+ opencode: OPENCODE_CLIENT,
4882
+ crush: CRUSH_CLIENT
4883
+ };
4884
+ function mergeJsonMcp(existing, containerKey, serverEntry) {
4885
+ const servers = existing[containerKey] ?? {};
4886
+ const already = isDeepStrictEqual2(servers.neat, serverEntry);
4789
4887
  const merged = {
4790
4888
  ...existing,
4791
- mcpServers: { ...servers, neat: NEAT_MCP_SERVER }
4889
+ [containerKey]: { ...servers, neat: serverEntry }
4792
4890
  };
4793
4891
  return { merged, changed: !already };
4794
4892
  }
4893
+ function mergeJsoncMcp(raw, containerKey, serverEntry) {
4894
+ const base = raw.trim().length > 0 ? raw : "{}";
4895
+ const parsed = jsonc.parse(base) ?? {};
4896
+ const servers = parsed[containerKey] ?? {};
4897
+ if (isDeepStrictEqual2(servers.neat, serverEntry)) {
4898
+ return { text: raw, changed: false };
4899
+ }
4900
+ const edits = jsonc.modify(base, [containerKey, "neat"], serverEntry, {
4901
+ formattingOptions: { tabSize: 2, insertSpaces: true }
4902
+ });
4903
+ let text = jsonc.applyEdits(base, edits);
4904
+ if (!text.endsWith("\n")) text += "\n";
4905
+ return { text, changed: text !== raw };
4906
+ }
4795
4907
  function escapeRegExp(s) {
4796
4908
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4797
4909
  }
@@ -4811,66 +4923,104 @@ function mergeRulesFile(existing, block) {
4811
4923
 
4812
4924
  ${block}`;
4813
4925
  }
4814
- async function runEditorInstall(client, opts) {
4815
- const mcpPath = client.mcpConfigPath();
4816
- const rulesPath = path11.join(opts.projectDir, client.rulesFileName);
4817
- let existingMcp = {};
4926
+ async function planMcp(client, mcpPath) {
4927
+ const serverEntry = client.serverEntry ?? NEAT_MCP_SERVER;
4928
+ let raw = "";
4818
4929
  try {
4819
- existingMcp = JSON.parse(await fs10.readFile(mcpPath, "utf8"));
4930
+ raw = await fs10.readFile(mcpPath, "utf8");
4820
4931
  } catch (err) {
4821
4932
  const e = err;
4822
4933
  if (e.code === "ENOENT") {
4823
- existingMcp = {};
4824
- } else if (err instanceof SyntaxError) {
4825
- console.error(
4826
- `neat ${client.id}: ${mcpPath} is not valid JSON \u2014 ${e.message}. Fix it (or move it aside) and re-run; nothing was written.`
4827
- );
4828
- return { exitCode: 1 };
4934
+ raw = "";
4829
4935
  } else {
4830
4936
  console.error(`neat ${client.id}: failed to read ${mcpPath} \u2014 ${e.message}`);
4831
- return { exitCode: 1 };
4937
+ return null;
4832
4938
  }
4833
4939
  }
4940
+ if (client.format === "jsonc") {
4941
+ if (raw.trim().length > 0) {
4942
+ const errors = [];
4943
+ jsonc.parse(raw, errors, { allowTrailingComma: true });
4944
+ if (errors.length > 0) {
4945
+ const first = errors[0];
4946
+ console.error(
4947
+ `neat ${client.id}: ${mcpPath} is not valid JSONC \u2014 ${jsonc.printParseErrorCode(first.error)} at offset ${first.offset}. Fix it (or move it aside) and re-run; nothing was written.`
4948
+ );
4949
+ return null;
4950
+ }
4951
+ }
4952
+ return mergeJsoncMcp(raw, client.mcpContainerKey, serverEntry);
4953
+ }
4954
+ let existing = {};
4955
+ if (raw.trim().length > 0) {
4956
+ try {
4957
+ existing = JSON.parse(raw);
4958
+ } catch (err) {
4959
+ console.error(
4960
+ `neat ${client.id}: ${mcpPath} is not valid JSON \u2014 ${err.message}. Fix it (or move it aside) and re-run; nothing was written.`
4961
+ );
4962
+ return null;
4963
+ }
4964
+ }
4965
+ const { merged, changed } = mergeJsonMcp(existing, client.mcpContainerKey, serverEntry);
4966
+ return { text: JSON.stringify(merged, null, 2) + "\n", changed };
4967
+ }
4968
+ async function runEditorInstall(client, opts) {
4969
+ const mcpPath = client.mcpConfigPath();
4970
+ const serverEntry = client.serverEntry ?? NEAT_MCP_SERVER;
4971
+ const hasRules = typeof client.rulesFileName === "string";
4972
+ const rulesPath = hasRules ? path11.join(opts.projectDir, client.rulesFileName) : "";
4973
+ const mcp = await planMcp(client, mcpPath);
4974
+ if (mcp === null) return { exitCode: 1 };
4834
4975
  let existingRules = "";
4835
- try {
4836
- existingRules = await fs10.readFile(rulesPath, "utf8");
4837
- } catch (err) {
4838
- if (err.code !== "ENOENT") {
4839
- console.error(`neat ${client.id}: failed to read ${rulesPath} \u2014 ${err.message}`);
4840
- return { exitCode: 1 };
4976
+ let newRules = "";
4977
+ let rulesChanged = false;
4978
+ let block = "";
4979
+ if (hasRules) {
4980
+ try {
4981
+ existingRules = await fs10.readFile(rulesPath, "utf8");
4982
+ } catch (err) {
4983
+ if (err.code !== "ENOENT") {
4984
+ console.error(`neat ${client.id}: failed to read ${rulesPath} \u2014 ${err.message}`);
4985
+ return { exitCode: 1 };
4986
+ }
4841
4987
  }
4988
+ const guide = await readSkillAsset(GUIDE_FILENAME);
4989
+ block = buildGuidanceBlock(guide);
4990
+ newRules = mergeRulesFile(existingRules, block);
4991
+ rulesChanged = newRules !== existingRules;
4842
4992
  }
4843
- const guide = await readSkillAsset(GUIDE_FILENAME);
4844
- const block = buildGuidanceBlock(guide);
4845
- const { merged, changed: mcpChanged } = mergeMcpConfig(existingMcp);
4846
- const mcpJson = JSON.stringify(merged, null, 2) + "\n";
4847
- const newRules = mergeRulesFile(existingRules, block);
4848
- const rulesChanged = newRules !== existingRules;
4849
4993
  if (!opts.apply) {
4850
4994
  console.log(`neat ${client.id} \u2014 wire NEAT into ${client.label} (plan; nothing written)`);
4851
4995
  console.log("");
4852
4996
  console.log(`MCP server \u2192 ${mcpPath}`);
4853
4997
  console.log(
4854
- mcpChanged ? " would add mcpServers.neat:" : " mcpServers.neat already present and current \u2014 no change:"
4998
+ mcp.changed ? ` would add ${client.mcpContainerKey}.neat:` : ` ${client.mcpContainerKey}.neat already present and current \u2014 no change:`
4855
4999
  );
4856
- console.log(indent(JSON.stringify({ neat: NEAT_MCP_SERVER }, null, 2)));
5000
+ console.log(indent(JSON.stringify({ neat: serverEntry }, null, 2)));
5001
+ if (hasRules) {
5002
+ console.log("");
5003
+ console.log(`Graph-first guidance \u2192 ${rulesPath}`);
5004
+ console.log(
5005
+ rulesChanged ? existingRules.includes(GRAPH_FIRST_MARKER_OPEN) ? " would refresh the neat:graph-first block:" : " would add the neat:graph-first block:" : " neat:graph-first block already present and current \u2014 no change."
5006
+ );
5007
+ if (rulesChanged) console.log(indent(block.trimEnd()));
5008
+ }
4857
5009
  console.log("");
4858
- console.log(`Graph-first guidance \u2192 ${rulesPath}`);
4859
5010
  console.log(
4860
- rulesChanged ? existingRules.includes(GRAPH_FIRST_MARKER_OPEN) ? " would refresh the neat:graph-first block:" : " would add the neat:graph-first block:" : " neat:graph-first block already present and current \u2014 no change."
5011
+ hasRules ? `Re-run with --apply to write both files. Existing servers and rules are kept.` : `Re-run with --apply to write the config. Existing servers are kept.`
4861
5012
  );
4862
- if (rulesChanged) console.log(indent(block.trimEnd()));
4863
- console.log("");
4864
- console.log(`Re-run with --apply to write both files. Existing servers and rules are kept.`);
4865
5013
  return { exitCode: 0 };
4866
5014
  }
4867
5015
  await fs10.mkdir(path11.dirname(mcpPath), { recursive: true });
4868
- await fs10.writeFile(mcpPath, mcpJson, "utf8");
4869
- await fs10.mkdir(path11.dirname(rulesPath), { recursive: true });
4870
- await fs10.writeFile(rulesPath, newRules, "utf8");
5016
+ await fs10.writeFile(mcpPath, mcp.text, "utf8");
5017
+ if (hasRules) {
5018
+ await fs10.mkdir(path11.dirname(rulesPath), { recursive: true });
5019
+ await fs10.writeFile(rulesPath, newRules, "utf8");
5020
+ }
4871
5021
  console.log(`neat ${client.id}: wired NEAT into ${client.label}`);
4872
- console.log(` MCP server: ${mcpPath} (mcpServers.neat \u2192 npx -y @neat.is/mcp)`);
4873
- console.log(` guidance: ${rulesPath} (neat:graph-first block)`);
5022
+ console.log(` MCP server: ${mcpPath} (${client.mcpContainerKey}.neat \u2192 npx -y @neat.is/mcp)`);
5023
+ if (hasRules) console.log(` guidance: ${rulesPath} (neat:graph-first block)`);
4874
5024
  console.log("");
4875
5025
  console.log(`restart ${client.label} to pick up the MCP server. Point it at a non-default`);
4876
5026
  console.log(`daemon by setting NEAT_CORE_URL in the neat server's env in that config.`);
@@ -4880,19 +5030,28 @@ function indent(text) {
4880
5030
  return text.split("\n").map((line) => line.length > 0 ? ` ${line}` : line).join("\n");
4881
5031
  }
4882
5032
  function usage3(client) {
4883
- console.log(`neat ${client.id} \u2014 install NEAT's MCP server + graph-first guidance into ${client.label}`);
5033
+ const hasRules = typeof client.rulesFileName === "string";
5034
+ console.log(
5035
+ hasRules ? `neat ${client.id} \u2014 install NEAT's MCP server + graph-first guidance into ${client.label}` : `neat ${client.id} \u2014 install NEAT's MCP server into ${client.label}`
5036
+ );
4884
5037
  console.log("");
4885
- console.log(" --apply write the MCP config and the rules file (default: plan only)");
5038
+ console.log(
5039
+ hasRules ? " --apply write the MCP config and the rules file (default: plan only)" : " --apply write the MCP config (default: plan only)"
5040
+ );
4886
5041
  console.log("");
4887
5042
  console.log("Writes NEAT's stdio MCP server (npx -y @neat.is/mcp) into");
4888
5043
  console.log(` ${client.mcpConfigPath()}`);
4889
- console.log(`and the graph-first guidance block into ./${client.rulesFileName}, both`);
4890
- console.log("additively \u2014 existing servers and rules are preserved, a re-run is a no-op.");
5044
+ if (hasRules) {
5045
+ console.log(`and the graph-first guidance block into ./${client.rulesFileName}, both`);
5046
+ console.log("additively \u2014 existing servers and rules are preserved, a re-run is a no-op.");
5047
+ } else {
5048
+ console.log("additively \u2014 existing servers are preserved, a re-run is a no-op.");
5049
+ }
4891
5050
  console.log("");
4892
5051
  console.log(`See ${client.docsUrl} for ${client.label}'s MCP config format.`);
4893
5052
  }
4894
5053
  async function runEditorCommand(clientId, args, projectDir = process.cwd()) {
4895
- const client = clientId === "cursor" ? CURSOR_CLIENT : DEVIN_CLIENT;
5054
+ const client = CLIENTS[clientId];
4896
5055
  let apply4 = false;
4897
5056
  for (const arg of args) {
4898
5057
  switch (arg) {
@@ -6067,6 +6226,39 @@ function usage4() {
6067
6226
  console.log(" to ./.windsurfrules. Plan by default.");
6068
6227
  console.log(" Flags:");
6069
6228
  console.log(" --apply write the MCP config + rules file (default: plan)");
6229
+ console.log(" gemini Wire NEAT into the Gemini CLI: add the MCP server to");
6230
+ console.log(" ~/.gemini/settings.json and the graph-first guidance to");
6231
+ console.log(" ./GEMINI.md. Plan by default.");
6232
+ console.log(" Flags:");
6233
+ console.log(" --apply write the MCP config + rules file (default: plan)");
6234
+ console.log(" qwen Wire NEAT into Qwen Code: add the MCP server to");
6235
+ console.log(" ~/.qwen/settings.json and the graph-first guidance to");
6236
+ console.log(" ./QWEN.md. Plan by default.");
6237
+ console.log(" Flags:");
6238
+ console.log(" --apply write the MCP config + rules file (default: plan)");
6239
+ console.log(" amazonq Wire NEAT into the Amazon Q Developer CLI: add the MCP server to");
6240
+ console.log(" ~/.aws/amazonq/mcp.json. Plan by default.");
6241
+ console.log(" Flags:");
6242
+ console.log(" --apply write the MCP config (default: plan)");
6243
+ console.log(" roocode Wire NEAT into Roo Code: add the MCP server to the project's");
6244
+ console.log(" ./.roo/mcp.json. Plan by default.");
6245
+ console.log(" Flags:");
6246
+ console.log(" --apply write the MCP config (default: plan)");
6247
+ console.log(" zed Wire NEAT into Zed: add the MCP server under context_servers in");
6248
+ console.log(" ~/.config/zed/settings.json (comments preserved) and the");
6249
+ console.log(" graph-first guidance to ./.rules. Plan by default.");
6250
+ console.log(" Flags:");
6251
+ console.log(" --apply write the MCP config + rules file (default: plan)");
6252
+ console.log(" opencode Wire NEAT into OpenCode: add the MCP server under mcp in");
6253
+ console.log(" ~/.config/opencode/opencode.json and the graph-first guidance");
6254
+ console.log(" to ./AGENTS.md. Plan by default.");
6255
+ console.log(" Flags:");
6256
+ console.log(" --apply write the MCP config + rules file (default: plan)");
6257
+ console.log(" crush Wire NEAT into Crush: add the MCP server under mcp in");
6258
+ console.log(" ~/.config/crush/crush.json and the graph-first guidance");
6259
+ console.log(" to ./AGENTS.md. Plan by default.");
6260
+ console.log(" Flags:");
6261
+ console.log(" --apply write the MCP config + rules file (default: plan)");
6070
6262
  console.log(" deploy Detect the deploy substrate, generate NEAT_AUTH_TOKEN,");
6071
6263
  console.log(" emit a docker-compose / systemd / docker run artifact, and");
6072
6264
  console.log(" print the OTel env-vars block to paste into your platform.");
@@ -6536,7 +6728,18 @@ async function main() {
6536
6728
  if (code !== 0) process.exit(code);
6537
6729
  return;
6538
6730
  }
6539
- if (cmd0 === "cursor" || cmd0 === "devin") {
6731
+ const EDITOR_VERBS = [
6732
+ "cursor",
6733
+ "devin",
6734
+ "gemini",
6735
+ "qwen",
6736
+ "amazonq",
6737
+ "roocode",
6738
+ "zed",
6739
+ "opencode",
6740
+ "crush"
6741
+ ];
6742
+ if (typeof cmd0 === "string" && EDITOR_VERBS.includes(cmd0)) {
6540
6743
  const code = await runEditorCommand(cmd0, argv.slice(1));
6541
6744
  if (code !== 0) process.exit(code);
6542
6745
  return;