@chimix/agentscan 1.1.0 → 1.3.0

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 (4) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/README.md +83 -26
  3. package/dist/cli.js +757 -194
  4. package/package.json +3 -2
package/dist/cli.js CHANGED
@@ -6962,7 +6962,7 @@ var require_public_api = __commonJS(function(exports) {
6962
6962
  import { parseArgs } from "node:util";
6963
6963
 
6964
6964
  // src/analyze.ts
6965
- import { resolve as resolve8 } from "node:path";
6965
+ import { resolve as resolve9 } from "node:path";
6966
6966
 
6967
6967
  // src/checks/aliases.ts
6968
6968
  var RULE_ALIASES = {
@@ -7091,6 +7091,7 @@ var AGENT_CONFIG_SIGNALS = [
7091
7091
  ".gemini",
7092
7092
  ".kiro",
7093
7093
  ".windsurf",
7094
+ ".devin",
7094
7095
  ".cline",
7095
7096
  ".roo",
7096
7097
  ".kilo",
@@ -7634,6 +7635,9 @@ function schemaProfileFromSkillsDir(dir) {
7634
7635
  if (normalized.includes("/.agents/skills") || normalized.endsWith("/.agents/skills") || normalized.includes("/.cursor/skills") || normalized.endsWith("/.cursor/skills") || normalized.includes("/.codex/skills") || normalized.endsWith("/.codex/skills") || normalized.includes("/.commandcode/skills") || normalized.endsWith("/.commandcode/skills")) {
7635
7636
  return "agent-skills";
7636
7637
  }
7638
+ if (normalized.includes("/.grok/skills") || normalized.endsWith("/.grok/skills")) {
7639
+ return "grok";
7640
+ }
7637
7641
  return "claude";
7638
7642
  }
7639
7643
  function assertNever(value, message) {
@@ -7656,6 +7660,9 @@ function providerFromSkillsDir(dir) {
7656
7660
  if (normalized.includes("/.commandcode/skills") || normalized.endsWith("/.commandcode/skills")) {
7657
7661
  return "commandcode";
7658
7662
  }
7663
+ if (normalized.includes("/.grok/skills") || normalized.endsWith("/.grok/skills")) {
7664
+ return "grok";
7665
+ }
7659
7666
  return "unknown";
7660
7667
  }
7661
7668
  function mcpProfileFromPath(filePath) {
@@ -7687,6 +7694,12 @@ function mcpProfileFromPath(filePath) {
7687
7694
  if (/(?:^|\/)(?:\.opencode\/)?opencode\.jsonc?$/.test(normalized)) {
7688
7695
  return "opencode-json";
7689
7696
  }
7697
+ if (normalized.endsWith("/.grok/config.toml") || normalized.endsWith(".grok/config.toml")) {
7698
+ return "grok-toml";
7699
+ }
7700
+ if (/(?:^|\/)\.codeium\/windsurf\/mcp_config\.json$/.test(normalized)) {
7701
+ return "windsurf-json";
7702
+ }
7690
7703
  return "claude-json";
7691
7704
  }
7692
7705
  function sourceProviderForMcpProfile(profile) {
@@ -7711,6 +7724,10 @@ function sourceProviderForMcpProfile(profile) {
7711
7724
  return "opencode";
7712
7725
  case "continue-yaml":
7713
7726
  return "continue";
7727
+ case "grok-toml":
7728
+ return "grok";
7729
+ case "windsurf-json":
7730
+ return "windsurf";
7714
7731
  default: {
7715
7732
  return assertNever(profile, `unhandled MCP schema profile: ${profile}`);
7716
7733
  }
@@ -7738,6 +7755,10 @@ function consumedByForMcpProfile(profile) {
7738
7755
  return ["opencode"];
7739
7756
  case "continue-yaml":
7740
7757
  return ["continue"];
7758
+ case "grok-toml":
7759
+ return ["grok"];
7760
+ case "windsurf-json":
7761
+ return ["windsurf"];
7741
7762
  default: {
7742
7763
  return assertNever(profile, `unhandled MCP schema profile: ${profile}`);
7743
7764
  }
@@ -7929,6 +7950,37 @@ function checkConfigErrors(facts) {
7929
7950
  });
7930
7951
  }
7931
7952
 
7953
+ // src/facts/grok.ts
7954
+ import { homedir } from "node:os";
7955
+ import { join as join2 } from "node:path";
7956
+ var GROK_HOOK_EVENTS = new Set([
7957
+ "SessionStart",
7958
+ "SessionEnd",
7959
+ "UserPromptSubmit",
7960
+ "PreToolUse",
7961
+ "PostToolUse",
7962
+ "PostToolUseFailure",
7963
+ "PermissionDenied",
7964
+ "Stop",
7965
+ "StopFailure",
7966
+ "Notification",
7967
+ "SubagentStart",
7968
+ "SubagentStop",
7969
+ "PreCompact",
7970
+ "PostCompact"
7971
+ ]);
7972
+ var GROK_HOOK_HANDLER_TYPES = new Set(["command", "http"]);
7973
+ function grokHomeDir() {
7974
+ const override = process.env.GROK_HOME?.trim();
7975
+ if (override !== undefined && override.length > 0) {
7976
+ return override;
7977
+ }
7978
+ return join2(homedir(), ".grok");
7979
+ }
7980
+ function isShadowedGrok(effective) {
7981
+ return effective === false;
7982
+ }
7983
+
7932
7984
  // src/facts/hook-schema.ts
7933
7985
  var KNOWN_HOOK_EVENTS = new Set([
7934
7986
  "SessionStart",
@@ -8041,13 +8093,14 @@ function inferHookSchemaProfile(sourceProvider, explicit) {
8041
8093
  switch (sourceProvider) {
8042
8094
  case "commandcode":
8043
8095
  return "commandcode";
8096
+ case "grok":
8097
+ return "grok";
8044
8098
  case "vscode":
8045
8099
  return "vscode-native";
8046
8100
  case "claude":
8047
8101
  case "agent-skills":
8048
8102
  case "codex":
8049
8103
  case "cursor":
8050
- case "grok":
8051
8104
  case "antigravity":
8052
8105
  case "gemini":
8053
8106
  case "windsurf":
@@ -8108,6 +8161,8 @@ function eventsFor(profile) {
8108
8161
  return COPILOT_HOOK_EVENTS;
8109
8162
  case "commandcode":
8110
8163
  return COMMANDCODE_HOOK_EVENTS;
8164
+ case "grok":
8165
+ return GROK_HOOK_EVENTS;
8111
8166
  default: {
8112
8167
  return assertNever(profile, `unhandled hook schema profile: ${profile}`);
8113
8168
  }
@@ -8123,6 +8178,8 @@ function unknownEventRuleId(profile) {
8123
8178
  return "copilot.hook.unknown-event";
8124
8179
  case "commandcode":
8125
8180
  return "commandcode.hook.unknown-event";
8181
+ case "grok":
8182
+ return "grok.hook.unknown-event";
8126
8183
  default: {
8127
8184
  return assertNever(profile, `unhandled hook schema profile: ${profile}`);
8128
8185
  }
@@ -8138,6 +8195,8 @@ function missingScriptRuleId(profile) {
8138
8195
  return "copilot.hook.missing-script";
8139
8196
  case "commandcode":
8140
8197
  return "commandcode.hook.missing-script";
8198
+ case "grok":
8199
+ return "grok.hook.missing-script";
8141
8200
  default: {
8142
8201
  return assertNever(profile, `unhandled hook schema profile: ${profile}`);
8143
8202
  }
@@ -8261,6 +8320,21 @@ function defectRuleId(profile, defect) {
8261
8320
  return assertNever(defect, `unhandled hook defect: ${defect}`);
8262
8321
  }
8263
8322
  }
8323
+ case "grok":
8324
+ switch (defect) {
8325
+ case "invalid-group":
8326
+ case "command-without-command":
8327
+ case "http-without-url":
8328
+ case "unknown-handler-type":
8329
+ return `grok.hook.${defect}`;
8330
+ case "mcp-tool-without-server-or-tool":
8331
+ case "prompt-without-prompt":
8332
+ case "incompatible-handler":
8333
+ return;
8334
+ default: {
8335
+ return assertNever(defect, `unhandled hook defect: ${defect}`);
8336
+ }
8337
+ }
8264
8338
  default: {
8265
8339
  return assertNever(profile, `unhandled hook schema profile: ${profile}`);
8266
8340
  }
@@ -8395,7 +8469,7 @@ var SECRET_PATTERNS = [
8395
8469
  { label: "Slack token", re: /\bxox[baprs]-[A-Za-z0-9-]{10,}/ },
8396
8470
  { label: "AWS access key id", re: /\bAKIA[0-9A-Z]{16}\b/ }
8397
8471
  ];
8398
- var INTERPOLATED = /\$\{[A-Za-z_][A-Za-z0-9_]*(?::-[^}]*)?\}|\$\{env:[A-Za-z_][A-Za-z0-9_]*\}|\$\{input:[A-Za-z0-9_-]+\}|\$[A-Za-z_][A-Za-z0-9_]*/;
8472
+ var INTERPOLATED = /\$\{[A-Za-z_][A-Za-z0-9_]*(?::-[^}]*)?\}|\$\{env:[A-Za-z_][A-Za-z0-9_]*\}|\$\{file:[^}]+\}|\$\{input:[A-Za-z0-9_-]+\}|\$[A-Za-z_][A-Za-z0-9_]*/;
8399
8473
  function profileOf(server) {
8400
8474
  return server.schemaProfile ?? "claude-json";
8401
8475
  }
@@ -8407,12 +8481,15 @@ function isLaunchable(server) {
8407
8481
  switch (profile) {
8408
8482
  case "antigravity-json":
8409
8483
  return server.hasCommand || server.hasServerUrl === true;
8484
+ case "windsurf-json":
8485
+ return server.hasCommand || server.hasUrl || server.hasServerUrl === true;
8410
8486
  case "claude-json":
8411
8487
  case "mcp-json":
8412
8488
  case "commandcode-json":
8413
8489
  case "vscode-json":
8414
8490
  case "cursor-json":
8415
8491
  case "codex-toml":
8492
+ case "grok-toml":
8416
8493
  case "opencode-json":
8417
8494
  case "continue-yaml":
8418
8495
  return server.hasCommand || server.hasUrl;
@@ -8438,12 +8515,16 @@ function noLaunchId(profile) {
8438
8515
  return "antigravity.mcp.no-launch";
8439
8516
  case "codex-toml":
8440
8517
  return "codex.mcp.no-launch";
8518
+ case "grok-toml":
8519
+ return "grok.mcp.no-launch";
8441
8520
  case "gemini-json":
8442
8521
  return "gemini.mcp.no-launch";
8443
8522
  case "opencode-json":
8444
8523
  return "opencode.mcp.no-launch";
8445
8524
  case "continue-yaml":
8446
8525
  return "continue.mcp.no-launch";
8526
+ case "windsurf-json":
8527
+ return "windsurf.mcp.no-launch";
8447
8528
  default: {
8448
8529
  return assertNever(profile, `unhandled MCP schema profile: ${profile}`);
8449
8530
  }
@@ -8454,6 +8535,8 @@ function noLaunchMessage(server) {
8454
8535
  switch (profile) {
8455
8536
  case "antigravity-json":
8456
8537
  return `MCP server "${server.name}" declares neither command nor serverUrl`;
8538
+ case "windsurf-json":
8539
+ return `MCP server "${server.name}" declares neither command, serverUrl, nor url`;
8457
8540
  case "gemini-json":
8458
8541
  return `MCP server "${server.name}" declares neither command, url, nor httpUrl`;
8459
8542
  case "continue-yaml":
@@ -8464,6 +8547,7 @@ function noLaunchMessage(server) {
8464
8547
  case "vscode-json":
8465
8548
  case "cursor-json":
8466
8549
  case "codex-toml":
8550
+ case "grok-toml":
8467
8551
  case "opencode-json":
8468
8552
  return `MCP server "${server.name}" declares neither command nor url`;
8469
8553
  default: {
@@ -8476,6 +8560,8 @@ function noLaunchSuggest(server) {
8476
8560
  switch (profile) {
8477
8561
  case "antigravity-json":
8478
8562
  return `Add a command (or serverUrl) for "${server.name}", or remove it`;
8563
+ case "windsurf-json":
8564
+ return `Add a command, serverUrl, or url for "${server.name}", or remove it`;
8479
8565
  case "gemini-json":
8480
8566
  return `Add a command, url, or httpUrl for "${server.name}", or remove it`;
8481
8567
  case "continue-yaml":
@@ -8486,6 +8572,7 @@ function noLaunchSuggest(server) {
8486
8572
  case "vscode-json":
8487
8573
  case "cursor-json":
8488
8574
  case "codex-toml":
8575
+ case "grok-toml":
8489
8576
  case "opencode-json":
8490
8577
  return `Add a command or url for "${server.name}", or remove it`;
8491
8578
  default: {
@@ -8619,7 +8706,7 @@ function claudeUrlNeedsType(server) {
8619
8706
  function checkMcp(facts) {
8620
8707
  const out = [];
8621
8708
  for (const server of facts.mcp) {
8622
- const shadowed = isShadowedCommandcode(server.commandcodeEffective);
8709
+ const shadowed = isShadowedCommandcode(server.commandcodeEffective) || isShadowedGrok(server.grokEffective);
8623
8710
  if (shadowed) {} else if (server.inventoryOnly === true) {} else if (server.opencodeInherit === true) {} else if (server.opencodeDefect !== undefined) {
8624
8711
  const defect = server.opencodeDefect;
8625
8712
  out.push(make(openCodeDefectRule(defect), `mcp:${server.name}@${server.path}`, {
@@ -8737,6 +8824,8 @@ function checkMcp(facts) {
8737
8824
 
8738
8825
  // src/checks/rules.ts
8739
8826
  var CURSOR_RULE_LINE_LIMIT = 500;
8827
+ var WINDSURF_WORKSPACE_CHAR_LIMIT = 12000;
8828
+ var WINDSURF_GLOBAL_CHAR_LIMIT = 6000;
8740
8829
  function lineLimitFor(provider) {
8741
8830
  switch (provider) {
8742
8831
  case "cursor":
@@ -8798,10 +8887,17 @@ function checkRules(facts) {
8798
8887
  if (finding !== undefined) {
8799
8888
  out.push(finding);
8800
8889
  }
8890
+ const missing = missingTriggerFinding(rule);
8891
+ if (missing !== undefined) {
8892
+ out.push(missing);
8893
+ }
8801
8894
  }
8802
8895
  return out;
8803
8896
  }
8804
8897
  function tooLargeFinding(rule) {
8898
+ if (rule.sourceProvider === "windsurf") {
8899
+ return windsurfTooLargeFinding(rule);
8900
+ }
8805
8901
  const limit = lineLimitFor(rule.sourceProvider);
8806
8902
  const ruleId = tooLargeRuleId(rule.sourceProvider);
8807
8903
  if (limit === undefined || ruleId === undefined || rule.lineCount <= limit) {
@@ -8820,6 +8916,42 @@ function tooLargeFinding(rule) {
8820
8916
  suggest: "Split or shorten the .mdc file, or ignore this info finding if the length is deliberate"
8821
8917
  });
8822
8918
  }
8919
+ function windsurfTooLargeFinding(rule) {
8920
+ const global = rule.windsurfScope === "global";
8921
+ const limit = global ? WINDSURF_GLOBAL_CHAR_LIMIT : WINDSURF_WORKSPACE_CHAR_LIMIT;
8922
+ if (rule.charCount <= limit) {
8923
+ return;
8924
+ }
8925
+ const ruleId = global ? "windsurf.rule.global-too-large" : "windsurf.rule.too-large";
8926
+ return make(ruleId, `rule:${rule.path}`, {
8927
+ action: "warn",
8928
+ severity: "info",
8929
+ message: global ? `Windsurf global rules file is long (${rule.charCount} characters > ${limit})` : `Windsurf workspace rule is long (${rule.charCount} characters > ${limit})`,
8930
+ reason: global ? "Windsurf limits ~/.codeium/windsurf/memories/global_rules.md to 6,000 characters. That is a vendor recommendation, not a load failure. See docs/spec/windsurf-rules.md." : "Windsurf limits workspace rule files to 12,000 characters each. That is a vendor recommendation, not a load failure. See docs/spec/windsurf-rules.md.",
8931
+ evidence: [
8932
+ { kind: "rule", value: rule.path },
8933
+ { kind: "count", value: `chars=${rule.charCount}` },
8934
+ { kind: "threshold", value: String(limit) }
8935
+ ],
8936
+ suggest: global ? "Shorten global_rules.md, or ignore this info finding if the length is deliberate" : "Split or shorten the rule file, or ignore this info finding if the length is deliberate"
8937
+ });
8938
+ }
8939
+ function missingTriggerFinding(rule) {
8940
+ if (rule.sourceProvider !== "windsurf" || rule.windsurfScope !== "workspace") {
8941
+ return;
8942
+ }
8943
+ if (rule.windsurfHasTrigger === true) {
8944
+ return;
8945
+ }
8946
+ return make("windsurf.rule.missing-trigger", `rule:${rule.path}`, {
8947
+ action: "warn",
8948
+ severity: "warning",
8949
+ message: "Windsurf workspace rule is missing a frontmatter trigger",
8950
+ reason: "Each workspace rule declares an activation mode in its frontmatter via the `trigger` field (`always_on`, `glob`, `model_decision`, or `manual`). See docs/spec/windsurf-rules.md.",
8951
+ evidence: [{ kind: "rule", value: rule.path }],
8952
+ suggest: "Add a `trigger` field to the YAML frontmatter"
8953
+ });
8954
+ }
8823
8955
 
8824
8956
  // src/checks/skills.ts
8825
8957
  import { basename as basename3, dirname as dirname2, isAbsolute, resolve as resolve3, sep as sep2 } from "node:path";
@@ -8843,11 +8975,14 @@ function skillSchema(skill) {
8843
8975
  if (skill.sourceProvider === "agent-skills" || skill.sourceProvider === "cursor" || skill.sourceProvider === "codex" || skill.sourceProvider === "commandcode") {
8844
8976
  return "agent-skills";
8845
8977
  }
8978
+ if (skill.sourceProvider === "grok") {
8979
+ return "grok";
8980
+ }
8846
8981
  return "claude";
8847
8982
  }
8848
8983
  function skillRoot(skill) {
8849
8984
  const path = skill.path.replaceAll("\\", "/");
8850
- for (const marker of ["/.claude/skills", "/.agents/skills", "/.cursor/skills", "/.codex/skills", "/.commandcode/skills"]) {
8985
+ for (const marker of ["/.claude/skills", "/.agents/skills", "/.cursor/skills", "/.codex/skills", "/.commandcode/skills", "/.grok/skills"]) {
8851
8986
  const index = path.lastIndexOf(marker);
8852
8987
  if (index !== -1) {
8853
8988
  return path.slice(0, index + marker.length);
@@ -8889,6 +9024,15 @@ function checkSkillStructure(facts) {
8889
9024
  evidence: skillEvidence(skill, `${skill.path}/SKILL.md`),
8890
9025
  suggest: "Add a --- delimited block with name and description"
8891
9026
  }));
9027
+ } else if (schema === "grok") {
9028
+ out.push(make("grok.skill.missing-frontmatter", skillSubject(skill), {
9029
+ action: "warn",
9030
+ severity: "error",
9031
+ message: "SKILL.md has no YAML frontmatter block",
9032
+ reason: "Grok Build says SKILL.md starts with YAML frontmatter. Extra keys are ignored; name and description are optional. See docs/spec/grok-skills.md.",
9033
+ evidence: skillEvidence(skill, `${skill.path}/SKILL.md`),
9034
+ suggest: "Add a --- delimited frontmatter block"
9035
+ }));
8892
9036
  } else {
8893
9037
  out.push(make("claude.skill.missing-frontmatter", skillSubject(skill), {
8894
9038
  action: "warn",
@@ -8919,7 +9063,7 @@ function checkSkillStructure(facts) {
8919
9063
  }
8920
9064
  if (schema === "agent-skills") {
8921
9065
  out.push(...checkAgentSkillsFrontmatter(skill));
8922
- } else if (skill.description === undefined && skill.firstMarkdownParagraph === undefined) {
9066
+ } else if (schema === "grok") {} else if (skill.description === undefined && skill.firstMarkdownParagraph === undefined) {
8923
9067
  out.push(make("claude.skill.missing-description", skillSubject(skill), {
8924
9068
  action: "warn",
8925
9069
  severity: "info",
@@ -9182,7 +9326,7 @@ function checkDescriptionBudget(facts, options) {
9182
9326
  return [];
9183
9327
  }
9184
9328
  const perEntryCap = options.skillListingMaxDescChars ?? 1536;
9185
- const project = facts.skills.filter((s) => s.source === "project" && s.hasSkillMd && skillSchema(s) !== "agent-skills" && listingText(s).length > 0);
9329
+ const project = facts.skills.filter((s) => s.source === "project" && s.hasSkillMd && skillSchema(s) !== "agent-skills" && skillSchema(s) !== "grok" && listingText(s).length > 0);
9186
9330
  const byRoot = new Map;
9187
9331
  for (const s of project) {
9188
9332
  const root = skillRoot(s);
@@ -9677,6 +9821,54 @@ var STRUCTURAL_CHECKS = [
9677
9821
  provenance: "spec-required",
9678
9822
  lastVerified: "2026-08-31"
9679
9823
  },
9824
+ {
9825
+ id: "grok.mcp.no-launch",
9826
+ description: "Grok MCP server declares neither command nor url",
9827
+ provenance: "spec-required",
9828
+ lastVerified: "2026-08-31"
9829
+ },
9830
+ {
9831
+ id: "grok.hook.unknown-event",
9832
+ description: "Grok hook registered under an event that is never dispatched",
9833
+ provenance: "spec-required",
9834
+ lastVerified: "2026-08-31"
9835
+ },
9836
+ {
9837
+ id: "grok.hook.missing-script",
9838
+ description: "Grok hook points at a script that does not exist, so it never runs",
9839
+ provenance: "internal-consistency",
9840
+ lastVerified: "2026-08-31"
9841
+ },
9842
+ {
9843
+ id: "grok.hook.invalid-group",
9844
+ description: "Grok hook group is missing the required nested hooks array, or matcher is not a string",
9845
+ provenance: "spec-required",
9846
+ lastVerified: "2026-08-31"
9847
+ },
9848
+ {
9849
+ id: "grok.hook.command-without-command",
9850
+ description: "Grok command hook is missing the command field",
9851
+ provenance: "spec-required",
9852
+ lastVerified: "2026-08-31"
9853
+ },
9854
+ {
9855
+ id: "grok.hook.http-without-url",
9856
+ description: "Grok http hook is missing the url field",
9857
+ provenance: "spec-required",
9858
+ lastVerified: "2026-08-31"
9859
+ },
9860
+ {
9861
+ id: "grok.hook.unknown-handler-type",
9862
+ description: "Grok hook uses a handler type other than command or http, or omits the required type",
9863
+ provenance: "spec-required",
9864
+ lastVerified: "2026-08-31"
9865
+ },
9866
+ {
9867
+ id: "grok.skill.missing-frontmatter",
9868
+ description: "Grok SKILL.md has no YAML frontmatter block",
9869
+ provenance: "spec-required",
9870
+ lastVerified: "2026-08-31"
9871
+ },
9680
9872
  {
9681
9873
  id: "mcp.command-missing",
9682
9874
  description: "MCP server command is a path that does not exist on disk",
@@ -9742,6 +9934,30 @@ var STRUCTURAL_CHECKS = [
9742
9934
  description: "Cursor project rule exceeds the 500-line recommendation",
9743
9935
  provenance: "vendor-recommendation",
9744
9936
  lastVerified: "2026-08-31"
9937
+ },
9938
+ {
9939
+ id: "windsurf.rule.too-large",
9940
+ description: "Windsurf workspace rule exceeds the 12,000-character limit",
9941
+ provenance: "vendor-recommendation",
9942
+ lastVerified: "2026-08-31"
9943
+ },
9944
+ {
9945
+ id: "windsurf.rule.global-too-large",
9946
+ description: "Windsurf global_rules.md exceeds the 6,000-character limit",
9947
+ provenance: "vendor-recommendation",
9948
+ lastVerified: "2026-08-31"
9949
+ },
9950
+ {
9951
+ id: "windsurf.rule.missing-trigger",
9952
+ description: "Windsurf workspace rule is missing frontmatter trigger",
9953
+ provenance: "spec-required",
9954
+ lastVerified: "2026-08-31"
9955
+ },
9956
+ {
9957
+ id: "windsurf.mcp.no-launch",
9958
+ description: "Windsurf MCP server declares neither command, serverUrl, nor url",
9959
+ provenance: "spec-required",
9960
+ lastVerified: "2026-08-31"
9745
9961
  }
9746
9962
  ];
9747
9963
 
@@ -9764,7 +9980,7 @@ function runChecks(facts, options = {}) {
9764
9980
 
9765
9981
  // src/config/load.ts
9766
9982
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
9767
- import { join as join2 } from "node:path";
9983
+ import { join as join3 } from "node:path";
9768
9984
 
9769
9985
  // node_modules/zod/v3/external.js
9770
9986
  var exports_external = {};
@@ -13749,7 +13965,7 @@ var defaultThresholds = {
13749
13965
  agents: 8
13750
13966
  };
13751
13967
  var defaultConfig = {
13752
- skillPaths: [".agents/skills", ".claude/skills", "skills", ".cursor/skills", ".codex/skills", ".commandcode/skills"],
13968
+ skillPaths: [".agents/skills", ".claude/skills", "skills", ".cursor/skills", ".codex/skills", ".commandcode/skills", ".grok/skills"],
13753
13969
  mcpPaths: [
13754
13970
  ".mcp.json",
13755
13971
  ".claude/mcp.json",
@@ -13764,7 +13980,8 @@ var defaultConfig = {
13764
13980
  ".opencode/opencode.json",
13765
13981
  ".opencode/opencode.jsonc",
13766
13982
  ".continue/config.yaml",
13767
- ".continue/mcpServers"
13983
+ ".continue/mcpServers",
13984
+ ".grok/config.toml"
13768
13985
  ],
13769
13986
  policyFiles: ["AGENTS.md", "CLAUDE.md"],
13770
13987
  ignoreSkills: [],
@@ -13800,7 +14017,7 @@ var configSchema = exports_external.object({
13800
14017
 
13801
14018
  // src/config/load.ts
13802
14019
  function loadConfig(root, configPath) {
13803
- const path = configPath ?? join2(root, ".agentscanrc.json");
14020
+ const path = configPath ?? join3(root, ".agentscanrc.json");
13804
14021
  if (!existsSync2(path)) {
13805
14022
  return configSchema.parse({});
13806
14023
  }
@@ -13816,22 +14033,22 @@ function loadConfig(root, configPath) {
13816
14033
  }
13817
14034
 
13818
14035
  // src/discover/index.ts
13819
- import { homedir as homedir6 } from "node:os";
13820
- import { join as join12, resolve as resolve7 } from "node:path";
14036
+ import { homedir as homedir9 } from "node:os";
14037
+ import { join as join17, resolve as resolve8 } from "node:path";
13821
14038
 
13822
14039
  // src/discover/agents.ts
13823
14040
  import { existsSync as existsSync6, readdirSync as readdirSync5, statSync as statSync4 } from "node:fs";
13824
- import { dirname as dirname4, join as join7 } from "node:path";
14041
+ import { dirname as dirname4, join as join8 } from "node:path";
13825
14042
 
13826
14043
  // src/discover/commandcode.ts
13827
14044
  import { existsSync as existsSync5, readdirSync as readdirSync4, statSync as statSync3 } from "node:fs";
13828
- import { homedir as homedir4 } from "node:os";
13829
- import { basename as basename6, dirname as dirname3, isAbsolute as isAbsolute4, join as join6, resolve as resolve4, sep as sep3 } from "node:path";
14045
+ import { homedir as homedir5 } from "node:os";
14046
+ import { basename as basename6, dirname as dirname3, isAbsolute as isAbsolute4, join as join7, resolve as resolve4, sep as sep3 } from "node:path";
13830
14047
 
13831
14048
  // src/discover/mcp.ts
13832
14049
  import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync } from "node:fs";
13833
- import { homedir as homedir2 } from "node:os";
13834
- import { basename as basename5, isAbsolute as isAbsolute2, join as join4 } from "node:path";
14050
+ import { homedir as homedir3 } from "node:os";
14051
+ import { basename as basename5, isAbsolute as isAbsolute2, join as join5 } from "node:path";
13835
14052
 
13836
14053
  // node_modules/smol-toml/dist/date.js
13837
14054
  /*!
@@ -15567,8 +15784,8 @@ function parseJsonc(text) {
15567
15784
  }
15568
15785
 
15569
15786
  // src/discover/launch.ts
15570
- import { homedir } from "node:os";
15571
- import { basename as basename4, join as join3, posix, win32 } from "node:path";
15787
+ import { homedir as homedir2 } from "node:os";
15788
+ import { basename as basename4, join as join4, posix, win32 } from "node:path";
15572
15789
  var UNRESOLVED_CWD = /\$\{[^}]+\}|\$[A-Za-z_][A-Za-z0-9_]*|%[A-Za-z0-9_]+%/;
15573
15790
  var INTERPRETERS = new Set([
15574
15791
  "bash",
@@ -15772,7 +15989,7 @@ function resolveLaunchCwd(cwd, projectRoot, hostPlatform = process.platform) {
15772
15989
  }
15773
15990
  let expanded = cwd;
15774
15991
  if (expanded.startsWith("~/")) {
15775
- expanded = join3(homedir(), expanded.slice(2));
15992
+ expanded = join4(homedir2(), expanded.slice(2));
15776
15993
  }
15777
15994
  const pathApi = pathApiForHost(hostPlatform);
15778
15995
  const abs = pathApi.isAbsolute(expanded) ? expanded : pathApi.join(projectRoot, expanded);
@@ -15844,7 +16061,7 @@ function formatLaunch(launch) {
15844
16061
 
15845
16062
  // src/discover/mcp.ts
15846
16063
  var SHELL_METACHARS2 = /[|;&`]|\$\(|\|\||&&|\s/;
15847
- var INTERPOLATED2 = /\$\{[A-Za-z_][A-Za-z0-9_]*(?::-[^}]*)?\}|\$\{env:[A-Za-z_][A-Za-z0-9_]*\}|\$\{input:[A-Za-z0-9_-]+\}|\$\{(?:userHome|workspaceFolder|workspaceFolderBasename|pathSeparator|\/)\}|\{env:[A-Za-z_][A-Za-z0-9_]*\}|\$\{\{\s*secrets\.[A-Za-z0-9_.]+\s*\}\}|\$[A-Za-z_][A-Za-z0-9_]*/;
16064
+ var INTERPOLATED2 = /\$\{[A-Za-z_][A-Za-z0-9_]*(?::-[^}]*)?\}|\$\{env:[A-Za-z_][A-Za-z0-9_]*\}|\$\{file:[^}]+\}|\$\{input:[A-Za-z0-9_-]+\}|\$\{(?:userHome|workspaceFolder|workspaceFolderBasename|pathSeparator|\/)\}|\{env:[A-Za-z_][A-Za-z0-9_]*\}|\$\{\{\s*secrets\.[A-Za-z0-9_.]+\s*\}\}|\$[A-Za-z_][A-Za-z0-9_]*/;
15848
16065
  function mcpCommandPath(command) {
15849
16066
  const trimmed = command.trim();
15850
16067
  if (trimmed.length === 0 || SHELL_METACHARS2.test(trimmed)) {
@@ -15871,11 +16088,11 @@ function resolveMcpCommand(root, raw) {
15871
16088
  }
15872
16089
  let expanded = extracted;
15873
16090
  if (expanded.startsWith("~/")) {
15874
- expanded = join4(homedir2(), expanded.slice(2));
16091
+ expanded = join5(homedir3(), expanded.slice(2));
15875
16092
  } else if (expanded.startsWith("$")) {
15876
16093
  expanded = expanded.replace(/^\$(?:CLAUDE_PROJECT_DIR|\{CLAUDE_PROJECT_DIR\}|COMMANDCODE_PROJECT_DIR|\{COMMANDCODE_PROJECT_DIR\}|COMMANDCODE_CWD|\{COMMANDCODE_CWD\})\/?/, "");
15877
16094
  }
15878
- const abs = isAbsolute2(expanded) ? expanded : join4(root, expanded);
16095
+ const abs = isAbsolute2(expanded) ? expanded : join5(root, expanded);
15879
16096
  try {
15880
16097
  return { command: extracted, exists: statSync(abs).isFile() };
15881
16098
  } catch {
@@ -16150,12 +16367,13 @@ function readVscodeConfig(path, errors3) {
16150
16367
  return;
16151
16368
  }
16152
16369
  }
16153
- function parseCodexToml(raw, filePath, root, errors3) {
16370
+ function parseTomlMcpServers(raw, filePath, root, errors3, profile) {
16371
+ const owner = profile === "codex-toml" ? "Codex" : "Grok";
16154
16372
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
16155
16373
  errors3.push({
16156
16374
  path: filePath,
16157
16375
  kind: "unexpected-shape",
16158
- detail: "Codex config root is not a TOML table"
16376
+ detail: `${owner} config root is not a TOML table`
16159
16377
  });
16160
16378
  return [];
16161
16379
  }
@@ -16172,7 +16390,31 @@ function parseCodexToml(raw, filePath, root, errors3) {
16172
16390
  });
16173
16391
  return [];
16174
16392
  }
16175
- return parseMcpServers({ mcpServers: servers }, filePath, root, errors3, "codex-toml");
16393
+ return parseMcpServers({ mcpServers: servers }, filePath, root, errors3, profile);
16394
+ }
16395
+ function parseCodexToml(raw, filePath, root, errors3) {
16396
+ return parseTomlMcpServers(raw, filePath, root, errors3, "codex-toml");
16397
+ }
16398
+ function parseCodexTomlFile(filePath, root, errors3) {
16399
+ const raw = readTomlConfig(filePath, errors3);
16400
+ if (raw === undefined) {
16401
+ return [];
16402
+ }
16403
+ return parseCodexToml(raw, filePath, root, errors3);
16404
+ }
16405
+ function parseGrokTomlFile(filePath, root, errors3) {
16406
+ const raw = readTomlConfig(filePath, errors3);
16407
+ if (raw === undefined) {
16408
+ return [];
16409
+ }
16410
+ return parseTomlMcpServers(raw, filePath, root, errors3, "grok-toml");
16411
+ }
16412
+ function parseWindsurfJsonFile(filePath, root, errors3) {
16413
+ const raw = readJsonConfig(filePath, errors3);
16414
+ if (raw === undefined) {
16415
+ return [];
16416
+ }
16417
+ return parseMcpServers(raw, filePath, root, errors3, "windsurf-json");
16176
16418
  }
16177
16419
  function parseOpenCode(raw, filePath, root, errors3) {
16178
16420
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
@@ -16401,6 +16643,9 @@ function parseMcpFile(filePath, root, errors3) {
16401
16643
  ...knobs
16402
16644
  };
16403
16645
  }
16646
+ if (profile === "grok-toml") {
16647
+ return { facts: parseGrokTomlFile(filePath, root, errors3) };
16648
+ }
16404
16649
  if (profile === "continue-yaml") {
16405
16650
  const raw2 = filePath.endsWith(".json") || filePath.endsWith(".jsonc") ? readVscodeConfig(filePath, errors3) : readYamlConfig(filePath, errors3);
16406
16651
  if (raw2 === undefined) {
@@ -16465,7 +16710,7 @@ function discoverContinueMcpDir(dir, root, errors3, seen) {
16465
16710
  if (!continueMcpFilename(name)) {
16466
16711
  continue;
16467
16712
  }
16468
- const filePath = join4(dir, name);
16713
+ const filePath = join5(dir, name);
16469
16714
  if (isDirectory(filePath)) {
16470
16715
  continue;
16471
16716
  }
@@ -16487,7 +16732,7 @@ function discoverMcpSurface(root, mcpPaths, errors3) {
16487
16732
  let codexProjectDocFallbackFilenames;
16488
16733
  let codexProjectRootMarkers;
16489
16734
  for (const rel of mcpPaths) {
16490
- const filePath = isAbsolute2(rel) ? rel : join4(root, rel);
16735
+ const filePath = isAbsolute2(rel) ? rel : join5(root, rel);
16491
16736
  if (!existsSync3(filePath)) {
16492
16737
  continue;
16493
16738
  }
@@ -16545,8 +16790,8 @@ function discoverNestedContinueMcp(root, errors3, seen) {
16545
16790
 
16546
16791
  // src/discover/hooks.ts
16547
16792
  import { existsSync as existsSync4, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
16548
- import { homedir as homedir3 } from "node:os";
16549
- import { isAbsolute as isAbsolute3, join as join5 } from "node:path";
16793
+ import { homedir as homedir4 } from "node:os";
16794
+ import { isAbsolute as isAbsolute3, join as join6 } from "node:path";
16550
16795
  var PROJECT_DIR2 = /^\$(?:CLAUDE_PROJECT_DIR\b|\{CLAUDE_PROJECT_DIR\})/;
16551
16796
  var PLUGIN_ROOT2 = /^\$(?:CLAUDE_PLUGIN_ROOT\b|\{CLAUDE_PLUGIN_ROOT\})/;
16552
16797
  var CLAUDE_HANDLER_TYPES = new Set(["command", "http", "mcp_tool", "mcp-tool", "prompt", "agent"]);
@@ -16562,26 +16807,26 @@ function resolveHookScript(bases, extracted, cwdAbs) {
16562
16807
  if (extracted.startsWith("~/")) {
16563
16808
  return {
16564
16809
  scriptPath: extracted,
16565
- exists: isFile(join5(homedir3(), extracted.slice(2)))
16810
+ exists: isFile(join6(homedir4(), extracted.slice(2)))
16566
16811
  };
16567
16812
  }
16568
16813
  if (PROJECT_DIR2.test(extracted) || COMMANDCODE_PROJECT_DIR.test(extracted)) {
16569
16814
  const rel = extracted.replace(/^\$(?:CLAUDE_PROJECT_DIR|\{CLAUDE_PROJECT_DIR\}|COMMANDCODE_PROJECT_DIR|\{COMMANDCODE_PROJECT_DIR\}|COMMANDCODE_CWD|\{COMMANDCODE_CWD\})\/?/, "");
16570
- return { scriptPath: extracted, exists: isFile(join5(bases.project, rel)) };
16815
+ return { scriptPath: extracted, exists: isFile(join6(bases.project, rel)) };
16571
16816
  }
16572
16817
  if (bases.plugin !== undefined && PLUGIN_ROOT2.test(extracted)) {
16573
16818
  const rel = extracted.replace(/^\$(?:CLAUDE_PLUGIN_ROOT|\{CLAUDE_PLUGIN_ROOT\})\/?/, "");
16574
- return { scriptPath: extracted, exists: isFile(join5(bases.plugin, rel)) };
16819
+ return { scriptPath: extracted, exists: isFile(join6(bases.plugin, rel)) };
16575
16820
  }
16576
16821
  if (isAbsolute3(extracted)) {
16577
16822
  return { scriptPath: extracted, exists: isFile(extracted) };
16578
16823
  }
16579
16824
  if (cwdAbs !== undefined) {
16580
- return { scriptPath: extracted, exists: isFile(join5(cwdAbs, extracted)) };
16825
+ return { scriptPath: extracted, exists: isFile(join6(cwdAbs, extracted)) };
16581
16826
  }
16582
16827
  const candidates = [
16583
- ...bases.own === undefined ? [] : [join5(bases.own, extracted)],
16584
- join5(bases.project, extracted)
16828
+ ...bases.own === undefined ? [] : [join6(bases.own, extracted)],
16829
+ join6(bases.project, extracted)
16585
16830
  ];
16586
16831
  return { scriptPath: extracted, exists: candidates.some(isFile) };
16587
16832
  }
@@ -16900,6 +17145,52 @@ function copilotHookFromEntry(item, ctx) {
16900
17145
  });
16901
17146
  return [attachLaunch(fact, launch, ctx, choice.skipExists)];
16902
17147
  }
17148
+ function timeoutFromGrokEntry(item) {
17149
+ if (!("timeout" in item)) {
17150
+ return {};
17151
+ }
17152
+ const value = item.timeout;
17153
+ if (typeof value === "number" && Number.isFinite(value)) {
17154
+ return { timeout: value };
17155
+ }
17156
+ return {};
17157
+ }
17158
+ function grokHookFromEntry(item, ctx) {
17159
+ const timeoutFacts = timeoutFromGrokEntry(item);
17160
+ const typeRaw = typeof item.type === "string" ? item.type : undefined;
17161
+ if (typeRaw === undefined) {
17162
+ return [
17163
+ baseFact(ctx, {
17164
+ defect: "unknown-handler-type",
17165
+ unknownHandlerType: "(missing)",
17166
+ ...timeoutFacts
17167
+ })
17168
+ ];
17169
+ }
17170
+ if (!GROK_HOOK_HANDLER_TYPES.has(typeRaw)) {
17171
+ return [
17172
+ baseFact(ctx, {
17173
+ defect: "unknown-handler-type",
17174
+ unknownHandlerType: typeRaw,
17175
+ ...timeoutFacts
17176
+ })
17177
+ ];
17178
+ }
17179
+ if (typeRaw === "http") {
17180
+ const url = nonemptyString(item.url);
17181
+ return [
17182
+ baseFact(ctx, {
17183
+ handlerType: "http",
17184
+ ...timeoutFacts,
17185
+ ...url === undefined ? { defect: "http-without-url" } : {}
17186
+ })
17187
+ ];
17188
+ }
17189
+ return commandFactsFromLaunches(ctx, item, "command-without-command").map((fact) => ({
17190
+ ...fact,
17191
+ ...timeoutFacts
17192
+ }));
17193
+ }
16903
17194
  function hooksFromProfile(item, ctx) {
16904
17195
  switch (ctx.schemaProfile) {
16905
17196
  case "claude":
@@ -16910,6 +17201,8 @@ function hooksFromProfile(item, ctx) {
16910
17201
  return copilotHookFromEntry(item, ctx);
16911
17202
  case "commandcode":
16912
17203
  return [commandcodeHookFromEntry(item, ctx)];
17204
+ case "grok":
17205
+ return grokHookFromEntry(item, ctx);
16913
17206
  default: {
16914
17207
  const neverProfile = ctx.schemaProfile;
16915
17208
  return neverProfile;
@@ -16926,7 +17219,7 @@ function hooksFromObject(hooks, filePath, source, bases, errors3, sourceProvider
16926
17219
  return [];
16927
17220
  }
16928
17221
  const profile = inferHookSchemaProfile(sourceProvider, schemaProfile);
16929
- const nestedOnly = profile === "claude" || profile === "commandcode";
17222
+ const nestedOnly = profile === "claude" || profile === "commandcode" || profile === "grok";
16930
17223
  const facts = [];
16931
17224
  for (const [event, groups] of Object.entries(hooks)) {
16932
17225
  if (!Array.isArray(groups)) {
@@ -16984,8 +17277,8 @@ function hooksFromObject(hooks, filePath, source, bases, errors3, sourceProvider
16984
17277
  }
16985
17278
  function discoverHooks(root, errors3) {
16986
17279
  const files = [
16987
- join5(root, ".claude", "settings.json"),
16988
- join5(root, ".claude", "settings.local.json")
17280
+ join6(root, ".claude", "settings.json"),
17281
+ join6(root, ".claude", "settings.local.json")
16989
17282
  ];
16990
17283
  const facts = [];
16991
17284
  for (const filePath of files) {
@@ -17032,7 +17325,7 @@ function discoverHookDir(dir, root, errors3, defaultProfile) {
17032
17325
  if (!name.endsWith(".json") || name.startsWith(".")) {
17033
17326
  continue;
17034
17327
  }
17035
- const filePath = join5(dir, name);
17328
+ const filePath = join6(dir, name);
17036
17329
  const raw = readJsonConfig(filePath, errors3);
17037
17330
  if (raw === undefined) {
17038
17331
  continue;
@@ -17056,12 +17349,53 @@ function discoverHookDir(dir, root, errors3, defaultProfile) {
17056
17349
  return facts;
17057
17350
  }
17058
17351
  function discoverVscodeHooks(root, errors3) {
17059
- return discoverHookDir(join5(root, ".github", "hooks"), root, errors3, "vscode-native");
17352
+ return discoverHookDir(join6(root, ".github", "hooks"), root, errors3, "vscode-native");
17060
17353
  }
17061
17354
  function discoverCopilotUserHooks(errors3) {
17062
- const dir = join5(homedir3(), ".copilot", "hooks");
17355
+ const dir = join6(homedir4(), ".copilot", "hooks");
17063
17356
  return discoverHookDir(dir, dir, errors3, "vscode-native");
17064
17357
  }
17358
+ function discoverGrokHooks(dir, projectRoot, errors3) {
17359
+ if (!existsSync4(dir)) {
17360
+ return [];
17361
+ }
17362
+ let names;
17363
+ try {
17364
+ names = readdirSync3(dir);
17365
+ } catch (err) {
17366
+ errors3.push({
17367
+ path: dir,
17368
+ kind: "unreadable",
17369
+ detail: err instanceof Error ? err.message : String(err)
17370
+ });
17371
+ return [];
17372
+ }
17373
+ const facts = [];
17374
+ for (const name of names) {
17375
+ if (!name.endsWith(".json") || name.startsWith(".")) {
17376
+ continue;
17377
+ }
17378
+ const filePath = join6(dir, name);
17379
+ const raw = readJsonConfig(filePath, errors3);
17380
+ if (raw === undefined) {
17381
+ continue;
17382
+ }
17383
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
17384
+ errors3.push({
17385
+ path: filePath,
17386
+ kind: "unexpected-shape",
17387
+ detail: "hook file is not a JSON object"
17388
+ });
17389
+ continue;
17390
+ }
17391
+ const hooks = raw.hooks;
17392
+ if (hooks === undefined) {
17393
+ continue;
17394
+ }
17395
+ facts.push(...hooksFromObject(hooks, filePath, "grok-hooks", { project: projectRoot, own: dir }, errors3, "grok", process.platform, "grok"));
17396
+ }
17397
+ return facts;
17398
+ }
17065
17399
 
17066
17400
  // src/discover/commandcode.ts
17067
17401
  var SETTINGS_LAYER_RANK = {
@@ -17117,9 +17451,9 @@ function layerFromObject(filePath, kind, obj) {
17117
17451
  function readCommandcodeSettingsLayers(commandcodeProjectRoot, errors3, includeGlobal) {
17118
17452
  const layers = [];
17119
17453
  const candidates = [
17120
- { path: join6(commandcodeProjectRoot, ".commandcode", "settings.local.json"), kind: "local" },
17121
- { path: join6(commandcodeProjectRoot, ".commandcode", "settings.json"), kind: "project" },
17122
- ...includeGlobal ? [{ path: join6(homedir4(), ".commandcode", "settings.json"), kind: "user" }] : []
17454
+ { path: join7(commandcodeProjectRoot, ".commandcode", "settings.local.json"), kind: "local" },
17455
+ { path: join7(commandcodeProjectRoot, ".commandcode", "settings.json"), kind: "project" },
17456
+ ...includeGlobal ? [{ path: join7(homedir5(), ".commandcode", "settings.json"), kind: "user" }] : []
17123
17457
  ];
17124
17458
  for (const candidate of candidates) {
17125
17459
  const obj = readSettingsObject(candidate.path, errors3);
@@ -17141,12 +17475,12 @@ function winningSkillsExtraDirs(layers) {
17141
17475
  function resolveSkillLocation(commandcodeProjectRoot, declared) {
17142
17476
  const trimmed = declared.trim();
17143
17477
  if (trimmed.startsWith("~/")) {
17144
- return join6(homedir4(), trimmed.slice(2));
17478
+ return join7(homedir5(), trimmed.slice(2));
17145
17479
  }
17146
17480
  if (isAbsolute4(trimmed)) {
17147
17481
  return trimmed;
17148
17482
  }
17149
- return join6(commandcodeProjectRoot, trimmed);
17483
+ return join7(commandcodeProjectRoot, trimmed);
17150
17484
  }
17151
17485
  function winningCommandcodeModel(layers) {
17152
17486
  for (const layer of layers) {
@@ -17177,7 +17511,7 @@ function discoverCommandcodeInlineMcp(commandcodeProjectRoot, layers, errors3) {
17177
17511
  return facts;
17178
17512
  }
17179
17513
  function discoverCommandcodeUserMcp(commandcodeProjectRoot, errors3) {
17180
- return discoverMcp(commandcodeProjectRoot, [join6(homedir4(), ".commandcode", "mcp.json")], errors3);
17514
+ return discoverMcp(commandcodeProjectRoot, [join7(homedir5(), ".commandcode", "mcp.json")], errors3);
17181
17515
  }
17182
17516
  function discoverCommandcodeMods(layers) {
17183
17517
  const out = [];
@@ -17232,9 +17566,9 @@ function commandFiles(dir, errors3) {
17232
17566
  if (name.startsWith(".")) {
17233
17567
  continue;
17234
17568
  }
17235
- const abs = join6(dir, name);
17569
+ const abs = join7(dir, name);
17236
17570
  if (isDirectory2(abs)) {
17237
- out.push(...commandFiles(abs, errors3).map((rel) => join6(name, rel)));
17571
+ out.push(...commandFiles(abs, errors3).map((rel) => join7(name, rel)));
17238
17572
  continue;
17239
17573
  }
17240
17574
  if (name.endsWith(".md") && isFile2(abs)) {
@@ -17246,25 +17580,25 @@ function commandFiles(dir, errors3) {
17246
17580
  function discoverCommandcodeCommands(commandcodeProjectRoot, includeGlobal, errors3) {
17247
17581
  const facts = [];
17248
17582
  const seen = new Set;
17249
- const projectDir = join6(commandcodeProjectRoot, ".commandcode", "commands");
17583
+ const projectDir = join7(commandcodeProjectRoot, ".commandcode", "commands");
17250
17584
  if (existsSync5(projectDir)) {
17251
17585
  seen.add(projectDir);
17252
17586
  for (const rel of commandFiles(projectDir, errors3)) {
17253
17587
  facts.push({
17254
17588
  name: basename6(rel, ".md"),
17255
- path: join6(projectDir, rel),
17589
+ path: join7(projectDir, rel),
17256
17590
  source: "project",
17257
17591
  sourceProvider: "commandcode"
17258
17592
  });
17259
17593
  }
17260
17594
  }
17261
17595
  if (includeGlobal) {
17262
- const userDir = join6(homedir4(), ".commandcode", "commands");
17596
+ const userDir = join7(homedir5(), ".commandcode", "commands");
17263
17597
  if (existsSync5(userDir) && !seen.has(userDir)) {
17264
17598
  for (const rel of commandFiles(userDir, errors3)) {
17265
17599
  facts.push({
17266
17600
  name: basename6(rel, ".md"),
17267
- path: join6(userDir, rel),
17601
+ path: join7(userDir, rel),
17268
17602
  source: "global",
17269
17603
  sourceProvider: "commandcode"
17270
17604
  });
@@ -17391,7 +17725,7 @@ function discoverCommandcodeAgentsDir(dir, errors3) {
17391
17725
  if (name.startsWith(".") || !name.endsWith(".md")) {
17392
17726
  continue;
17393
17727
  }
17394
- const filePath = join6(dir, name);
17728
+ const filePath = join7(dir, name);
17395
17729
  if (!isFile2(filePath)) {
17396
17730
  continue;
17397
17731
  }
@@ -17401,9 +17735,9 @@ function discoverCommandcodeAgentsDir(dir, errors3) {
17401
17735
  }
17402
17736
  function discoverCommandcodeAgents(commandcodeProjectRoot, includeGlobal, errors3) {
17403
17737
  const facts = [];
17404
- facts.push(...discoverCommandcodeAgentsDir(join6(commandcodeProjectRoot, ".commandcode", "agents"), errors3));
17738
+ facts.push(...discoverCommandcodeAgentsDir(join7(commandcodeProjectRoot, ".commandcode", "agents"), errors3));
17405
17739
  if (includeGlobal) {
17406
- facts.push(...discoverCommandcodeAgentsDir(join6(homedir4(), ".commandcode", "agents"), errors3));
17740
+ facts.push(...discoverCommandcodeAgentsDir(join7(homedir5(), ".commandcode", "agents"), errors3));
17407
17741
  }
17408
17742
  return facts;
17409
17743
  }
@@ -17416,7 +17750,7 @@ function commandcodeAgentsSkillsRoots(startDir, scanBoundary, home) {
17416
17750
  if (dir === homeDir) {
17417
17751
  break;
17418
17752
  }
17419
- out.push(join6(dir, ".agents", "skills"));
17753
+ out.push(join7(dir, ".agents", "skills"));
17420
17754
  if (dir === stop) {
17421
17755
  break;
17422
17756
  }
@@ -17437,11 +17771,11 @@ function mcpCommandcodeScore(entry, commandcodeProjectRoot, home) {
17437
17771
  return;
17438
17772
  }
17439
17773
  const p = resolve4(entry.path);
17440
- const projectMcp = resolve4(join6(commandcodeProjectRoot, ".mcp.json"));
17441
- const userMcp = resolve4(join6(home, ".commandcode", "mcp.json"));
17442
- const localSettings = resolve4(join6(commandcodeProjectRoot, ".commandcode", "settings.local.json"));
17443
- const projectSettings = resolve4(join6(commandcodeProjectRoot, ".commandcode", "settings.json"));
17444
- const userSettings = resolve4(join6(home, ".commandcode", "settings.json"));
17774
+ const projectMcp = resolve4(join7(commandcodeProjectRoot, ".mcp.json"));
17775
+ const userMcp = resolve4(join7(home, ".commandcode", "mcp.json"));
17776
+ const localSettings = resolve4(join7(commandcodeProjectRoot, ".commandcode", "settings.local.json"));
17777
+ const projectSettings = resolve4(join7(commandcodeProjectRoot, ".commandcode", "settings.json"));
17778
+ const userSettings = resolve4(join7(home, ".commandcode", "settings.json"));
17445
17779
  if (p === projectMcp) {
17446
17780
  return 20;
17447
17781
  }
@@ -17463,7 +17797,7 @@ function mcpCommandcodeScore(entry, commandcodeProjectRoot, home) {
17463
17797
  return;
17464
17798
  }
17465
17799
  function applyCommandcodeMcpPrecedence(mcp, commandcodeProjectRoot) {
17466
- const home = homedir4();
17800
+ const home = homedir5();
17467
17801
  const ranked = [];
17468
17802
  for (const entry of mcp) {
17469
17803
  const score = mcpCommandcodeScore(entry, commandcodeProjectRoot, home);
@@ -17559,9 +17893,9 @@ function applyCommandcodeHookPrecedence(hooks) {
17559
17893
  }
17560
17894
  }
17561
17895
  function applyCommandcodeAgentPrecedence(agents, commandcodeProjectRoot) {
17562
- const home = homedir4();
17563
- const userDir = resolve4(join6(home, ".commandcode", "agents"));
17564
- const projectDir = resolve4(join6(commandcodeProjectRoot, ".commandcode", "agents"));
17896
+ const home = homedir5();
17897
+ const userDir = resolve4(join7(home, ".commandcode", "agents"));
17898
+ const projectDir = resolve4(join7(commandcodeProjectRoot, ".commandcode", "agents"));
17565
17899
  const personal = [];
17566
17900
  const project = [];
17567
17901
  for (const agent of agents) {
@@ -17585,10 +17919,10 @@ function applyCommandcodeAgentPrecedence(agents, commandcodeProjectRoot) {
17585
17919
  }
17586
17920
  }
17587
17921
  function applyCommandcodeSkillPrecedence(skills, ctx) {
17588
- const home = homedir4();
17589
- const projectCc = resolve4(join6(ctx.commandcodeProjectRoot, ".commandcode", "skills"));
17590
- const userCc = resolve4(join6(home, ".commandcode", "skills"));
17591
- const userAgents = resolve4(join6(home, ".agents", "skills"));
17922
+ const home = homedir5();
17923
+ const projectCc = resolve4(join7(ctx.commandcodeProjectRoot, ".commandcode", "skills"));
17924
+ const userCc = resolve4(join7(home, ".commandcode", "skills"));
17925
+ const userAgents = resolve4(join7(home, ".agents", "skills"));
17592
17926
  const projectAgents = ctx.agentsSkillsRoots.map((dir) => resolve4(dir));
17593
17927
  const extras = ctx.extraDirs.map((dir) => resolve4(dir));
17594
17928
  const rankOf = (skill) => {
@@ -17656,7 +17990,7 @@ function agentFiles(dir, errors3) {
17656
17990
  continue;
17657
17991
  }
17658
17992
  if (entry.endsWith("/")) {
17659
- out.push(...agentFiles(join7(dir, entry.slice(0, -1)), errors3).map((p) => join7(entry.slice(0, -1), p)));
17993
+ out.push(...agentFiles(join8(dir, entry.slice(0, -1)), errors3).map((p) => join8(entry.slice(0, -1), p)));
17660
17994
  continue;
17661
17995
  }
17662
17996
  out.push(entry);
@@ -17707,7 +18041,7 @@ function discoverClaudeAgentsDir(dir, root, errors3) {
17707
18041
  if (!name.endsWith(".md")) {
17708
18042
  continue;
17709
18043
  }
17710
- const filePath = join7(dir, name);
18044
+ const filePath = join8(dir, name);
17711
18045
  try {
17712
18046
  const st = statSync4(filePath);
17713
18047
  if (!st.isFile()) {
@@ -17721,7 +18055,7 @@ function discoverClaudeAgentsDir(dir, root, errors3) {
17721
18055
  return facts;
17722
18056
  }
17723
18057
  function discoverVscodeAgents(root, errors3) {
17724
- const dir = join7(root, ".github", "agents");
18058
+ const dir = join8(root, ".github", "agents");
17725
18059
  if (!existsSync6(dir)) {
17726
18060
  return [];
17727
18061
  }
@@ -17744,7 +18078,7 @@ function discoverVscodeAgents(root, errors3) {
17744
18078
  if (!name.endsWith(".agent.md") && !name.endsWith(".md")) {
17745
18079
  continue;
17746
18080
  }
17747
- const filePath = join7(dir, name);
18081
+ const filePath = join8(dir, name);
17748
18082
  try {
17749
18083
  if (!statSync4(filePath).isFile()) {
17750
18084
  continue;
@@ -17792,7 +18126,7 @@ function discoverAgents(root, errors3, startDir = root, includeGlobal = false, c
17792
18126
  const facts = [];
17793
18127
  const seenDirs = new Set;
17794
18128
  for (const dir of ancestorDirsInclusive(startDir, root)) {
17795
- const agentsDir = join7(dir, ".claude", "agents");
18129
+ const agentsDir = join8(dir, ".claude", "agents");
17796
18130
  if (seenDirs.has(agentsDir)) {
17797
18131
  continue;
17798
18132
  }
@@ -17804,12 +18138,83 @@ function discoverAgents(root, errors3, startDir = root, includeGlobal = false, c
17804
18138
  return facts;
17805
18139
  }
17806
18140
 
18141
+ // src/discover/codex.ts
18142
+ import { existsSync as existsSync7 } from "node:fs";
18143
+ import { join as join10 } from "node:path";
18144
+
18145
+ // src/facts/codex.ts
18146
+ import { homedir as homedir6 } from "node:os";
18147
+ import { join as join9 } from "node:path";
18148
+ function codexHomeDir() {
18149
+ const override = process.env.CODEX_HOME?.trim();
18150
+ if (override !== undefined && override.length > 0) {
18151
+ return override;
18152
+ }
18153
+ return join9(homedir6(), ".codex");
18154
+ }
18155
+
18156
+ // src/discover/codex.ts
18157
+ function discoverCodexUserMcp(errors3) {
18158
+ const home = codexHomeDir();
18159
+ const filePath = join10(home, "config.toml");
18160
+ if (!existsSync7(filePath)) {
18161
+ return [];
18162
+ }
18163
+ return parseCodexTomlFile(filePath, home, errors3);
18164
+ }
18165
+
18166
+ // src/discover/grok.ts
18167
+ import { existsSync as existsSync8 } from "node:fs";
18168
+ import { dirname as dirname5, join as join11, resolve as resolve5 } from "node:path";
18169
+ function discoverGrokUserMcp(errors3) {
18170
+ const home = grokHomeDir();
18171
+ const filePath = join11(home, "config.toml");
18172
+ if (!existsSync8(filePath)) {
18173
+ return [];
18174
+ }
18175
+ return parseGrokTomlFile(filePath, home, errors3);
18176
+ }
18177
+ function applyGrokMcpPrecedence(mcp, startDir) {
18178
+ const userFile = resolve5(join11(grokHomeDir(), "config.toml"));
18179
+ const ranked = [];
18180
+ for (const entry of mcp) {
18181
+ if (entry.schemaProfile !== "grok-toml") {
18182
+ continue;
18183
+ }
18184
+ ranked.push({ entry, score: grokMcpScore(entry, startDir, userFile) });
18185
+ }
18186
+ const best = new Map;
18187
+ for (const item of ranked) {
18188
+ const current = best.get(item.entry.name);
18189
+ if (current === undefined || item.score > current) {
18190
+ best.set(item.entry.name, item.score);
18191
+ }
18192
+ }
18193
+ const seenWinner = new Set;
18194
+ for (const item of ranked) {
18195
+ const winning = best.get(item.entry.name) ?? item.score;
18196
+ if (item.score === winning && !seenWinner.has(item.entry.name)) {
18197
+ item.entry.grokEffective = true;
18198
+ seenWinner.add(item.entry.name);
18199
+ } else {
18200
+ item.entry.grokEffective = false;
18201
+ }
18202
+ }
18203
+ }
18204
+ function grokMcpScore(entry, startDir, userFile) {
18205
+ if (resolve5(entry.path) === userFile) {
18206
+ return 0;
18207
+ }
18208
+ const projectDir = dirname5(dirname5(entry.path));
18209
+ return 1000 - hopsFrom(startDir, projectDir);
18210
+ }
18211
+
17807
18212
  // src/discover/plugins.ts
17808
- import { existsSync as existsSync7, readdirSync as readdirSync6 } from "node:fs";
17809
- import { join as join8 } from "node:path";
18213
+ import { existsSync as existsSync9, readdirSync as readdirSync6 } from "node:fs";
18214
+ import { join as join12 } from "node:path";
17810
18215
  function findPluginRoots(root, errors3) {
17811
18216
  const found = [];
17812
- if (existsSync7(join8(root, "hooks", "hooks.json"))) {
18217
+ if (existsSync9(join12(root, "hooks", "hooks.json"))) {
17813
18218
  found.push(root);
17814
18219
  }
17815
18220
  const walk = (dir, depth) => {
@@ -17831,8 +18236,8 @@ function findPluginRoots(root, errors3) {
17831
18236
  if (entry.name.startsWith(".") && entry.name !== ".claude") {
17832
18237
  continue;
17833
18238
  }
17834
- const child = join8(dir, entry.name);
17835
- if (existsSync7(join8(child, ".claude-plugin", "plugin.json"))) {
18239
+ const child = join12(dir, entry.name);
18240
+ if (existsSync9(join12(child, ".claude-plugin", "plugin.json"))) {
17836
18241
  found.push(child);
17837
18242
  continue;
17838
18243
  }
@@ -17848,8 +18253,8 @@ function findPluginRoots(root, errors3) {
17848
18253
  function discoverPluginHooks(root, errors3) {
17849
18254
  const facts = [];
17850
18255
  for (const pluginRoot of findPluginRoots(root, errors3)) {
17851
- const filePath = join8(pluginRoot, "hooks", "hooks.json");
17852
- if (!existsSync7(filePath)) {
18256
+ const filePath = join12(pluginRoot, "hooks", "hooks.json");
18257
+ if (!existsSync9(filePath)) {
17853
18258
  continue;
17854
18259
  }
17855
18260
  const raw = readJsonConfig(filePath, errors3);
@@ -17878,11 +18283,11 @@ function discoverPluginHooks(root, errors3) {
17878
18283
  }
17879
18284
 
17880
18285
  // src/discover/policy.ts
17881
- import { existsSync as existsSync8 } from "node:fs";
17882
- import { homedir as homedir5 } from "node:os";
17883
- import { basename as basename7, dirname as dirname5, join as join9, resolve as resolve5 } from "node:path";
18286
+ import { existsSync as existsSync10 } from "node:fs";
18287
+ import { homedir as homedir7 } from "node:os";
18288
+ import { basename as basename7, dirname as dirname6, join as join13, resolve as resolve6 } from "node:path";
17884
18289
  function readPolicyFile(filePath, errors3, meta) {
17885
- if (!existsSync8(filePath)) {
18290
+ if (!existsSync10(filePath)) {
17886
18291
  return;
17887
18292
  }
17888
18293
  try {
@@ -17900,7 +18305,7 @@ function readPolicyFile(filePath, errors3, meta) {
17900
18305
  text,
17901
18306
  sourceProvider: meta.sourceProvider,
17902
18307
  kind: meta.kind,
17903
- hopsFromStart: hopsFrom(meta.startDir, meta.hopsDir ?? dirname5(filePath))
18308
+ hopsFromStart: hopsFrom(meta.startDir, meta.hopsDir ?? dirname6(filePath))
17904
18309
  };
17905
18310
  } catch (err) {
17906
18311
  errors3.push({
@@ -17915,7 +18320,7 @@ function addUnique(out, seen, fact) {
17915
18320
  if (fact === undefined) {
17916
18321
  return;
17917
18322
  }
17918
- const key = resolve5(fact.path);
18323
+ const key = resolve6(fact.path);
17919
18324
  if (seen.has(key)) {
17920
18325
  return;
17921
18326
  }
@@ -17923,46 +18328,61 @@ function addUnique(out, seen, fact) {
17923
18328
  out.push(fact);
17924
18329
  }
17925
18330
  function resolveCodexProjectRoot(startDir, scanBoundary, markers) {
17926
- const start = resolve5(startDir);
17927
- const boundary = resolve5(scanBoundary);
18331
+ const start = resolve6(startDir);
18332
+ const boundary = resolve6(scanBoundary);
17928
18333
  if (markers !== undefined && markers.length === 0) {
17929
18334
  return start;
17930
18335
  }
17931
18336
  const names = markers ?? [".git"];
17932
18337
  for (const dir of ancestorDirsInclusive(start, boundary)) {
17933
- if (names.some((name) => existsSync8(join9(dir, name)))) {
18338
+ if (names.some((name) => existsSync10(join13(dir, name)))) {
17934
18339
  return dir;
17935
18340
  }
17936
18341
  }
17937
18342
  return boundary;
17938
18343
  }
17939
18344
  function isCommandcodeMemoryFallback(abs) {
17940
- return basename7(abs) === "AGENTS.md" && basename7(dirname5(abs)) === ".commandcode";
18345
+ return basename7(abs) === "AGENTS.md" && basename7(dirname6(abs)) === ".commandcode";
17941
18346
  }
17942
18347
  function discoverPolicyFiles(root, policyFiles, errors3, startDir = root, includeGlobal = false, _commandcodeProjectRoot = root, fallbackFilenames = []) {
17943
18348
  const out = [];
17944
18349
  const seen = new Set;
17945
- const start = resolve5(startDir);
18350
+ const start = resolve6(startDir);
17946
18351
  for (const rel of policyFiles) {
17947
18352
  const kind = basename7(rel) === "CLAUDE.md" || basename7(rel) === "CLAUDE.local.md" ? "claude-md" : basename7(rel).endsWith(".instructions.md") || basename7(rel) === "copilot-instructions.md" ? "vscode-instructions" : "agents-md";
17948
18353
  const sourceProvider = kind === "claude-md" ? "claude" : kind === "vscode-instructions" ? "vscode" : "unknown";
17949
- addUnique(out, seen, readPolicyFile(join9(root, rel), errors3, { sourceProvider, kind, startDir: start }));
18354
+ addUnique(out, seen, readPolicyFile(join13(root, rel), errors3, { sourceProvider, kind, startDir: start }));
17950
18355
  }
17951
18356
  for (const dir of ancestorDirsInclusive(start, root)) {
17952
- addUnique(out, seen, readPolicyFile(join9(dir, "AGENTS.md"), errors3, {
18357
+ addUnique(out, seen, readPolicyFile(join13(dir, "AGENTS.md"), errors3, {
18358
+ sourceProvider: "unknown",
18359
+ kind: "agents-md",
18360
+ startDir: start
18361
+ }));
18362
+ addUnique(out, seen, readPolicyFile(join13(dir, "agents.md"), errors3, {
17953
18363
  sourceProvider: "unknown",
17954
18364
  kind: "agents-md",
17955
18365
  startDir: start
17956
18366
  }));
17957
- if (!existsSync8(join9(dir, "AGENTS.md"))) {
17958
- addUnique(out, seen, readPolicyFile(join9(dir, ".commandcode", "AGENTS.md"), errors3, {
18367
+ addUnique(out, seen, readPolicyFile(join13(dir, "Agents.md"), errors3, {
18368
+ sourceProvider: "grok",
18369
+ kind: "agents-md",
18370
+ startDir: start
18371
+ }));
18372
+ addUnique(out, seen, readPolicyFile(join13(dir, "AGENT.md"), errors3, {
18373
+ sourceProvider: "grok",
18374
+ kind: "agents-md",
18375
+ startDir: start
18376
+ }));
18377
+ if (!existsSync10(join13(dir, "AGENTS.md"))) {
18378
+ addUnique(out, seen, readPolicyFile(join13(dir, ".commandcode", "AGENTS.md"), errors3, {
17959
18379
  sourceProvider: "commandcode",
17960
18380
  kind: "agents-md",
17961
18381
  startDir: start,
17962
18382
  hopsDir: dir
17963
18383
  }));
17964
18384
  }
17965
- addUnique(out, seen, readPolicyFile(join9(dir, "AGENTS.override.md"), errors3, {
18385
+ addUnique(out, seen, readPolicyFile(join13(dir, "AGENTS.override.md"), errors3, {
17966
18386
  sourceProvider: "codex",
17967
18387
  kind: "agents-md",
17968
18388
  startDir: start
@@ -17971,23 +18391,23 @@ function discoverPolicyFiles(root, policyFiles, errors3, startDir = root, includ
17971
18391
  if (name === "AGENTS.md" || name === "AGENTS.override.md" || basename7(name) !== name) {
17972
18392
  continue;
17973
18393
  }
17974
- addUnique(out, seen, readPolicyFile(join9(dir, name), errors3, {
18394
+ addUnique(out, seen, readPolicyFile(join13(dir, name), errors3, {
17975
18395
  sourceProvider: "codex",
17976
18396
  kind: "agents-md",
17977
18397
  startDir: start
17978
18398
  }));
17979
18399
  }
17980
- addUnique(out, seen, readPolicyFile(join9(dir, "CLAUDE.md"), errors3, {
18400
+ addUnique(out, seen, readPolicyFile(join13(dir, "CLAUDE.md"), errors3, {
17981
18401
  sourceProvider: "claude",
17982
18402
  kind: "claude-md",
17983
18403
  startDir: start
17984
18404
  }));
17985
- addUnique(out, seen, readPolicyFile(join9(dir, ".claude", "CLAUDE.md"), errors3, {
18405
+ addUnique(out, seen, readPolicyFile(join13(dir, ".claude", "CLAUDE.md"), errors3, {
17986
18406
  sourceProvider: "claude",
17987
18407
  kind: "claude-md",
17988
18408
  startDir: start
17989
18409
  }));
17990
- addUnique(out, seen, readPolicyFile(join9(dir, "CLAUDE.local.md"), errors3, {
18410
+ addUnique(out, seen, readPolicyFile(join13(dir, "CLAUDE.local.md"), errors3, {
17991
18411
  sourceProvider: "claude",
17992
18412
  kind: "claude-md",
17993
18413
  startDir: start
@@ -17996,14 +18416,14 @@ function discoverPolicyFiles(root, policyFiles, errors3, startDir = root, includ
17996
18416
  for (const abs of walkFiles(root, {
17997
18417
  maxDepth: NESTED_DISCOVERY_MAX_DEPTH,
17998
18418
  match: (abs2, name) => {
17999
- if (name === "AGENTS.override.md") {
18419
+ if (name === "AGENTS.override.md" || name === "Agents.md" || name === "AGENT.md" || name === "agents.md") {
18000
18420
  return true;
18001
18421
  }
18002
18422
  if (name !== "AGENTS.md") {
18003
18423
  return false;
18004
18424
  }
18005
- if (basename7(dirname5(abs2)) === ".commandcode") {
18006
- return !existsSync8(join9(dirname5(dirname5(abs2)), "AGENTS.md"));
18425
+ if (basename7(dirname6(abs2)) === ".commandcode") {
18426
+ return !existsSync10(join13(dirname6(dirname6(abs2)), "AGENTS.md"));
18007
18427
  }
18008
18428
  return true;
18009
18429
  },
@@ -18011,19 +18431,19 @@ function discoverPolicyFiles(root, policyFiles, errors3, startDir = root, includ
18011
18431
  })) {
18012
18432
  const commandcodeMemory = isCommandcodeMemoryFallback(abs);
18013
18433
  addUnique(out, seen, readPolicyFile(abs, errors3, {
18014
- sourceProvider: commandcodeMemory ? "commandcode" : basename7(abs) === "AGENTS.override.md" ? "codex" : "unknown",
18434
+ sourceProvider: commandcodeMemory ? "commandcode" : basename7(abs) === "AGENTS.override.md" ? "codex" : basename7(abs) === "Agents.md" || basename7(abs) === "AGENT.md" ? "grok" : "unknown",
18015
18435
  kind: "agents-md",
18016
18436
  startDir: start,
18017
- ...commandcodeMemory ? { hopsDir: dirname5(dirname5(abs)) } : {}
18437
+ ...commandcodeMemory ? { hopsDir: dirname6(dirname6(abs)) } : {}
18018
18438
  }));
18019
18439
  }
18020
- addUnique(out, seen, readPolicyFile(join9(root, ".github", "copilot-instructions.md"), errors3, {
18440
+ addUnique(out, seen, readPolicyFile(join13(root, ".github", "copilot-instructions.md"), errors3, {
18021
18441
  sourceProvider: "vscode",
18022
18442
  kind: "vscode-instructions",
18023
18443
  startDir: start
18024
18444
  }));
18025
- const instructionsDir = join9(root, ".github", "instructions");
18026
- if (existsSync8(instructionsDir)) {
18445
+ const instructionsDir = join13(root, ".github", "instructions");
18446
+ if (existsSync10(instructionsDir)) {
18027
18447
  for (const abs of walkFiles(instructionsDir, {
18028
18448
  maxDepth: NESTED_DISCOVERY_MAX_DEPTH,
18029
18449
  match: (_abs, name) => name.endsWith(".instructions.md"),
@@ -18037,17 +18457,17 @@ function discoverPolicyFiles(root, policyFiles, errors3, startDir = root, includ
18037
18457
  }
18038
18458
  }
18039
18459
  if (includeGlobal) {
18040
- addUnique(out, seen, readPolicyFile(join9(homedir5(), ".codex", "AGENTS.override.md"), errors3, {
18460
+ addUnique(out, seen, readPolicyFile(join13(homedir7(), ".codex", "AGENTS.override.md"), errors3, {
18041
18461
  sourceProvider: "codex",
18042
18462
  kind: "agents-md",
18043
18463
  startDir: start
18044
18464
  }));
18045
- addUnique(out, seen, readPolicyFile(join9(homedir5(), ".codex", "AGENTS.md"), errors3, {
18465
+ addUnique(out, seen, readPolicyFile(join13(homedir7(), ".codex", "AGENTS.md"), errors3, {
18046
18466
  sourceProvider: "codex",
18047
18467
  kind: "agents-md",
18048
18468
  startDir: start
18049
18469
  }));
18050
- addUnique(out, seen, readPolicyFile(join9(homedir5(), ".commandcode", "AGENTS.md"), errors3, {
18470
+ addUnique(out, seen, readPolicyFile(join13(homedir7(), ".commandcode", "AGENTS.md"), errors3, {
18051
18471
  sourceProvider: "commandcode",
18052
18472
  kind: "agents-md",
18053
18473
  startDir: start
@@ -18087,7 +18507,7 @@ function parseSkillsLockFile(filePath, errors3) {
18087
18507
  });
18088
18508
  return { entries: [], invalid: true };
18089
18509
  }
18090
- const lockRoot = dirname5(resolve5(filePath));
18510
+ const lockRoot = dirname6(resolve6(filePath));
18091
18511
  const locked = [];
18092
18512
  for (const [id, value] of Object.entries(skills)) {
18093
18513
  const entry = { id, lockRoot, lockPath: filePath };
@@ -18114,8 +18534,8 @@ function discoverSkillsLocks(scanBoundary, projectRoot, errors3) {
18114
18534
  let present = false;
18115
18535
  let validCount = 0;
18116
18536
  const consider = (filePath) => {
18117
- const resolved = resolve5(filePath);
18118
- if (seen.has(resolved) || !existsSync8(resolved)) {
18537
+ const resolved = resolve6(filePath);
18538
+ if (seen.has(resolved) || !existsSync10(resolved)) {
18119
18539
  return;
18120
18540
  }
18121
18541
  seen.add(resolved);
@@ -18125,10 +18545,10 @@ function discoverSkillsLocks(scanBoundary, projectRoot, errors3) {
18125
18545
  return;
18126
18546
  }
18127
18547
  validCount += 1;
18128
- lockRoots.push(dirname5(resolved));
18548
+ lockRoots.push(dirname6(resolved));
18129
18549
  locked.push(...parsed.entries);
18130
18550
  };
18131
- consider(join9(projectRoot, "skills-lock.json"));
18551
+ consider(join13(projectRoot, "skills-lock.json"));
18132
18552
  for (const abs of walkFiles(scanBoundary, {
18133
18553
  maxDepth: NESTED_DISCOVERY_MAX_DEPTH,
18134
18554
  match: (_abs, name) => name === "skills-lock.json",
@@ -18145,12 +18565,37 @@ function discoverSkillsLocks(scanBoundary, projectRoot, errors3) {
18145
18565
  }
18146
18566
 
18147
18567
  // src/discover/rules.ts
18148
- import { existsSync as existsSync9 } from "node:fs";
18149
- import { join as join10 } from "node:path";
18568
+ import { existsSync as existsSync11 } from "node:fs";
18569
+ import { basename as basename8, dirname as dirname7, join as join14 } from "node:path";
18150
18570
  function lineCount2(text) {
18151
18571
  return text.length === 0 ? 0 : text.replace(/\r?\n$/, "").split(/\r?\n/).length;
18152
18572
  }
18153
- function readRule(path, sourceProvider, errors3) {
18573
+ function windsurfWorkspaceHasTrigger(text) {
18574
+ let body = text;
18575
+ if (body.charCodeAt(0) === 65279) {
18576
+ body = body.slice(1);
18577
+ }
18578
+ body = body.replace(/\r\n/g, `
18579
+ `);
18580
+ if (!body.startsWith("---")) {
18581
+ return false;
18582
+ }
18583
+ const fence = /\n(?:---|\.\.\.)[ \t]*(?:\n|$)/.exec(body.slice(3));
18584
+ if (fence === null) {
18585
+ return false;
18586
+ }
18587
+ try {
18588
+ const block = $parse(body.slice(3, 3 + fence.index));
18589
+ if (block === null || typeof block !== "object" || Array.isArray(block)) {
18590
+ return false;
18591
+ }
18592
+ const trigger = block.trigger;
18593
+ return typeof trigger === "string" && trigger.trim().length > 0;
18594
+ } catch {
18595
+ return false;
18596
+ }
18597
+ }
18598
+ function readRuleFile(path, sourceProvider, errors3, extras) {
18154
18599
  try {
18155
18600
  const result = readCapped(path, POLICY_CAP);
18156
18601
  const text = result.buf.subarray(0, POLICY_CAP).toString("utf8");
@@ -18161,11 +18606,15 @@ function readRule(path, sourceProvider, errors3) {
18161
18606
  detail: `file exceeds ${POLICY_CAP} byte scan cap`
18162
18607
  });
18163
18608
  }
18609
+ const windsurfScope = extras?.windsurfScope;
18164
18610
  return {
18165
18611
  path,
18166
18612
  sourceProvider,
18167
18613
  lineCount: lineCount2(text),
18168
- byteLength: result.buf.length
18614
+ byteLength: result.buf.length,
18615
+ charCount: text.length,
18616
+ ...windsurfScope === undefined ? {} : { windsurfScope },
18617
+ ...windsurfScope === "workspace" ? { windsurfHasTrigger: windsurfWorkspaceHasTrigger(text) } : {}
18169
18618
  };
18170
18619
  } catch (err) {
18171
18620
  errors3.push({
@@ -18176,40 +18625,111 @@ function readRule(path, sourceProvider, errors3) {
18176
18625
  return;
18177
18626
  }
18178
18627
  }
18628
+ function collectMdRules(dir, sourceProvider, errors3, extras) {
18629
+ const out = [];
18630
+ if (!existsSync11(dir)) {
18631
+ return out;
18632
+ }
18633
+ for (const abs of walkFiles(dir, {
18634
+ maxDepth: NESTED_DISCOVERY_MAX_DEPTH,
18635
+ match: (_abs, name) => name.endsWith(".md"),
18636
+ errors: errors3
18637
+ })) {
18638
+ const fact = readRuleFile(abs, sourceProvider, errors3, extras);
18639
+ if (fact !== undefined) {
18640
+ out.push(fact);
18641
+ }
18642
+ }
18643
+ return out;
18644
+ }
18645
+ function isWindsurfWorkspaceRulePath(abs, name) {
18646
+ if (!name.endsWith(".md")) {
18647
+ return false;
18648
+ }
18649
+ const normalized = abs.replaceAll("\\", "/");
18650
+ return normalized.includes("/.devin/rules/") || normalized.includes("/.windsurf/rules/");
18651
+ }
18179
18652
  function discoverRules(root, errors3) {
18180
18653
  const out = [];
18181
- const cursorRules = join10(root, ".cursor", "rules");
18182
- if (existsSync9(cursorRules)) {
18654
+ const cursorRules = join14(root, ".cursor", "rules");
18655
+ if (existsSync11(cursorRules)) {
18183
18656
  for (const abs of walkFiles(cursorRules, {
18184
18657
  maxDepth: NESTED_DISCOVERY_MAX_DEPTH,
18185
18658
  match: (_abs, name) => name.endsWith(".mdc"),
18186
18659
  errors: errors3
18187
18660
  })) {
18188
- const fact = readRule(abs, "cursor", errors3);
18661
+ const fact = readRuleFile(abs, "cursor", errors3);
18189
18662
  if (fact !== undefined) {
18190
18663
  out.push(fact);
18191
18664
  }
18192
18665
  }
18193
18666
  }
18194
- const claudeRules = join10(root, ".claude", "rules");
18195
- if (existsSync9(claudeRules)) {
18196
- for (const abs of walkFiles(claudeRules, {
18197
- maxDepth: NESTED_DISCOVERY_MAX_DEPTH,
18198
- match: (_abs, name) => name.endsWith(".md"),
18199
- errors: errors3
18200
- })) {
18201
- const fact = readRule(abs, "claude", errors3);
18202
- if (fact !== undefined) {
18203
- out.push(fact);
18204
- }
18667
+ out.push(...collectMdRules(join14(root, ".grok", "rules"), "grok", errors3));
18668
+ out.push(...collectMdRules(join14(root, ".claude", "rules"), "claude", errors3));
18669
+ out.push(...collectMdRules(join14(root, ".devin", "rules"), "windsurf", errors3, {
18670
+ windsurfScope: "workspace"
18671
+ }));
18672
+ out.push(...collectMdRules(join14(root, ".windsurf", "rules"), "windsurf", errors3, {
18673
+ windsurfScope: "workspace"
18674
+ }));
18675
+ const legacy = join14(root, ".windsurfrules");
18676
+ if (existsSync11(legacy) && basename8(legacy) === ".windsurfrules") {
18677
+ const fact = readRuleFile(legacy, "windsurf", errors3, { windsurfScope: "legacy" });
18678
+ if (fact !== undefined) {
18679
+ out.push(fact);
18205
18680
  }
18206
18681
  }
18207
18682
  return out;
18208
18683
  }
18684
+ function discoverNestedWindsurfRules(scanBoundary, errors3) {
18685
+ const out = [];
18686
+ for (const abs of walkFiles(scanBoundary, {
18687
+ maxDepth: NESTED_DISCOVERY_MAX_DEPTH,
18688
+ match: isWindsurfWorkspaceRulePath,
18689
+ errors: errors3
18690
+ })) {
18691
+ const fact = readRuleFile(abs, "windsurf", errors3, { windsurfScope: "workspace" });
18692
+ if (fact !== undefined) {
18693
+ out.push(fact);
18694
+ }
18695
+ }
18696
+ return out;
18697
+ }
18698
+ function discoverWindsurfGlobalRules(filePath, errors3) {
18699
+ if (!existsSync11(filePath) || basename8(filePath) !== "global_rules.md") {
18700
+ return [];
18701
+ }
18702
+ if (basename8(dirname7(filePath)) !== "memories") {
18703
+ return [];
18704
+ }
18705
+ const fact = readRuleFile(filePath, "windsurf", errors3, { windsurfScope: "global" });
18706
+ return fact === undefined ? [] : [fact];
18707
+ }
18708
+
18709
+ // src/discover/windsurf.ts
18710
+ import { existsSync as existsSync12 } from "node:fs";
18711
+ import { homedir as homedir8 } from "node:os";
18712
+ import { dirname as dirname8, join as join15 } from "node:path";
18713
+ function windsurfUserMcpPath(home = homedir8()) {
18714
+ return join15(home, ".codeium", "windsurf", "mcp_config.json");
18715
+ }
18716
+ function windsurfGlobalRulesPath(home = homedir8()) {
18717
+ return join15(home, ".codeium", "windsurf", "memories", "global_rules.md");
18718
+ }
18719
+ function discoverWindsurfUserMcp(errors3) {
18720
+ const filePath = windsurfUserMcpPath();
18721
+ if (!existsSync12(filePath)) {
18722
+ return [];
18723
+ }
18724
+ return parseWindsurfJsonFile(filePath, dirname8(filePath), errors3);
18725
+ }
18726
+ function discoverWindsurfUserRules(errors3) {
18727
+ return discoverWindsurfGlobalRules(windsurfGlobalRulesPath(), errors3);
18728
+ }
18209
18729
 
18210
18730
  // src/discover/skills.ts
18211
- import { existsSync as existsSync10, readdirSync as readdirSync7, statSync as statSync5 } from "node:fs";
18212
- import { basename as basename8, join as join11, resolve as resolve6 } from "node:path";
18731
+ import { existsSync as existsSync13, readdirSync as readdirSync7, statSync as statSync5 } from "node:fs";
18732
+ import { basename as basename9, join as join16, resolve as resolve7 } from "node:path";
18213
18733
  var BUNDLED = "scripts|references|assets|templates|examples";
18214
18734
  var REFERENCE_RE = new RegExp(`(?<![\\w/.-])(?:\\./)?((?:${BUNDLED})/[A-Za-z0-9_./-]+\\.[A-Za-z0-9]{1,10})(?![A-Za-z0-9_.-])`, "g");
18215
18735
  var FENCE_BLOCK = /^[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?(?:^[ \t]*\1[ \t]*$|$)/gm;
@@ -18251,13 +18771,13 @@ function brokenReferences(body, skillDir, root, skillRootOnly) {
18251
18771
  }
18252
18772
  };
18253
18773
  return skillReferences(body).filter((rel) => {
18254
- if (isFilePath(join11(skillDir, rel))) {
18774
+ if (isFilePath(join16(skillDir, rel))) {
18255
18775
  return false;
18256
18776
  }
18257
18777
  if (skillRootOnly) {
18258
18778
  return true;
18259
18779
  }
18260
- return !isFilePath(join11(root, rel));
18780
+ return !isFilePath(join16(root, rel));
18261
18781
  });
18262
18782
  }
18263
18783
  function readDirNames(dir, errors3) {
@@ -18291,7 +18811,7 @@ function findSkillMdDirs(dir, errors3, depth = 0) {
18291
18811
  if (names === undefined) {
18292
18812
  return [];
18293
18813
  }
18294
- if (isFile3(join11(dir, "SKILL.md"))) {
18814
+ if (isFile3(join16(dir, "SKILL.md"))) {
18295
18815
  return [dir];
18296
18816
  }
18297
18817
  if (depth >= NESTED_DISCOVERY_MAX_DEPTH) {
@@ -18302,7 +18822,7 @@ function findSkillMdDirs(dir, errors3, depth = 0) {
18302
18822
  if (name.startsWith(".") || name === "node_modules" || NESTED_DISCOVERY_SKIP.has(name)) {
18303
18823
  continue;
18304
18824
  }
18305
- const child = join11(dir, name);
18825
+ const child = join16(dir, name);
18306
18826
  if (!isDirectory3(child)) {
18307
18827
  continue;
18308
18828
  }
@@ -18362,10 +18882,10 @@ function skillFactFromDir(skillDir, source, skillsRoot, errors3, root, opts) {
18362
18882
  const normalizedRoot = skillsRoot.replaceAll("\\", "/");
18363
18883
  const sourceProvider = providerFromSkillsDir(normalizedRoot);
18364
18884
  const schemaProfile = schemaProfileFromSkillsDir(normalizedRoot);
18365
- const skillMd = join11(skillDir, "SKILL.md");
18885
+ const skillMd = join16(skillDir, "SKILL.md");
18366
18886
  const fm = opts.hasSkillMd ? readFrontmatter(skillMd, errors3) : { hasFrontmatter: false };
18367
18887
  const fact = {
18368
- id: basename8(skillDir),
18888
+ id: basename9(skillDir),
18369
18889
  sourceProvider,
18370
18890
  schemaProfile,
18371
18891
  path: skillDir,
@@ -18422,7 +18942,7 @@ function skillFactFromDir(skillDir, source, skillsRoot, errors3, root, opts) {
18422
18942
  return fact;
18423
18943
  }
18424
18944
  function discoverSkillsInDir(dir, source, errors3, root) {
18425
- if (!existsSync10(dir)) {
18945
+ if (!existsSync13(dir)) {
18426
18946
  return [];
18427
18947
  }
18428
18948
  const names = readDirNames(dir, errors3);
@@ -18430,18 +18950,18 @@ function discoverSkillsInDir(dir, source, errors3, root) {
18430
18950
  return [];
18431
18951
  }
18432
18952
  const found = findSkillMdDirs(dir, errors3);
18433
- const foundSet = new Set(found.map((path) => resolve6(path)));
18953
+ const foundSet = new Set(found.map((path) => resolve7(path)));
18434
18954
  const skills = found.map((skillDir) => skillFactFromDir(skillDir, source, dir, errors3, root, { hasSkillMd: true }));
18435
18955
  for (const name of names) {
18436
18956
  if (name.startsWith(".") || name === "node_modules") {
18437
18957
  continue;
18438
18958
  }
18439
- const child = join11(dir, name);
18959
+ const child = join16(dir, name);
18440
18960
  if (!isDirectory3(child)) {
18441
18961
  continue;
18442
18962
  }
18443
- const resolved = resolve6(child);
18444
- if (foundSet.has(resolved) || found.some((path) => resolve6(path).startsWith(`${resolved}/`))) {
18963
+ const resolved = resolve7(child);
18964
+ if (foundSet.has(resolved) || found.some((path) => resolve7(path).startsWith(`${resolved}/`))) {
18445
18965
  continue;
18446
18966
  }
18447
18967
  let dirReadable = true;
@@ -18481,8 +19001,8 @@ function discoverNestedClaudeSkills(root, configuredRoots, errors3, options = {}
18481
19001
  for (const entry of entries) {
18482
19002
  if (!entry.isDirectory() || entry.name !== "skills")
18483
19003
  continue;
18484
- const child = join11(dir, entry.name);
18485
- if (configuredRoots.has(resolve6(child)))
19004
+ const child = join16(dir, entry.name);
19005
+ if (configuredRoots.has(resolve7(child)))
18486
19006
  continue;
18487
19007
  out.push(...discoverSkillsInDir(child, "project", errors3, root));
18488
19008
  }
@@ -18496,7 +19016,7 @@ function discoverNestedClaudeSkills(root, configuredRoots, errors3, options = {}
18496
19016
  continue;
18497
19017
  if (entry.name.startsWith(".") && !NESTED_SKILL_PARENTS.has(entry.name))
18498
19018
  continue;
18499
- const child = join11(dir, entry.name);
19019
+ const child = join16(dir, entry.name);
18500
19020
  if (NESTED_SKILL_PARENTS.has(entry.name)) {
18501
19021
  inspectSkillsParent(child);
18502
19022
  continue;
@@ -18525,6 +19045,19 @@ function disambiguateSkills(skills, root) {
18525
19045
  function mcpKey(fact) {
18526
19046
  return `${fact.name}@${fact.path}${fact.platform === undefined ? "" : `:${fact.platform}`}`;
18527
19047
  }
19048
+ function uniqueRules(facts) {
19049
+ const seen = new Set;
19050
+ const out = [];
19051
+ for (const fact of facts) {
19052
+ const key = resolve8(fact.path);
19053
+ if (seen.has(key)) {
19054
+ continue;
19055
+ }
19056
+ seen.add(key);
19057
+ out.push(fact);
19058
+ }
19059
+ return out;
19060
+ }
18528
19061
  function discoverAgentSurface(root, config, opts) {
18529
19062
  const startDir = opts.startDir ?? root;
18530
19063
  const scanBoundary = opts.scanBoundary ?? root;
@@ -18538,12 +19071,12 @@ function discoverAgentSurface(root, config, opts) {
18538
19071
  if (isCommandcodeSkillsRel(rel)) {
18539
19072
  continue;
18540
19073
  }
18541
- const abs = resolve7(dir, rel);
19074
+ const abs = resolve8(dir, rel);
18542
19075
  configuredRoots.add(abs);
18543
19076
  skills.push(...discoverSkillsInDir(abs, "project", configErrors, root));
18544
19077
  }
18545
19078
  }
18546
- const commandcodeSkills = resolve7(commandcodeProjectRoot, ".commandcode", "skills");
19079
+ const commandcodeSkills = resolve8(commandcodeProjectRoot, ".commandcode", "skills");
18547
19080
  if (!configuredRoots.has(commandcodeSkills)) {
18548
19081
  configuredRoots.add(commandcodeSkills);
18549
19082
  skills.push(...discoverSkillsInDir(commandcodeSkills, "project", configErrors, root));
@@ -18554,27 +19087,28 @@ function discoverAgentSurface(root, config, opts) {
18554
19087
  const ccLayers = readCommandcodeSettingsLayers(commandcodeProjectRoot, configErrors, opts.includeGlobal);
18555
19088
  const extraDirs = [];
18556
19089
  for (const declared of winningSkillsExtraDirs(ccLayers)) {
18557
- const abs = resolve7(resolveSkillLocation(commandcodeProjectRoot, declared));
19090
+ const abs = resolve8(resolveSkillLocation(commandcodeProjectRoot, declared));
18558
19091
  extraDirs.push(abs);
18559
19092
  if (configuredRoots.has(abs)) {
18560
19093
  continue;
18561
19094
  }
18562
19095
  configuredRoots.add(abs);
18563
- const homeRoot = resolve7(homedir6());
19096
+ const homeRoot = resolve8(homedir9());
18564
19097
  const source = abs === homeRoot || abs.startsWith(`${homeRoot}/`) ? "global" : "project";
18565
19098
  const extra = discoverSkillsInDir(abs, source, configErrors, root);
18566
19099
  skills.push(...extra.map((skill) => skill.schemaProfile === "agent-skills" ? skill : { ...skill, schemaProfile: "agent-skills" }));
18567
19100
  }
18568
19101
  if (opts.includeGlobal) {
18569
- const home = homedir6();
19102
+ const home = homedir9();
18570
19103
  const globalSkillDirs = [
18571
- join12(home, ".claude", "skills"),
18572
- join12(home, ".codex", "skills"),
18573
- join12(home, ".commandcode", "skills"),
18574
- join12(home, ".agents", "skills")
19104
+ join17(home, ".claude", "skills"),
19105
+ join17(home, ".codex", "skills"),
19106
+ join17(home, ".commandcode", "skills"),
19107
+ join17(home, ".agents", "skills"),
19108
+ join17(grokHomeDir(), "skills")
18575
19109
  ];
18576
19110
  for (const abs of globalSkillDirs) {
18577
- const resolved = resolve7(abs);
19111
+ const resolved = resolve8(abs);
18578
19112
  if (configuredRoots.has(resolved)) {
18579
19113
  continue;
18580
19114
  }
@@ -18582,7 +19116,7 @@ function discoverAgentSurface(root, config, opts) {
18582
19116
  skills.push(...discoverSkillsInDir(abs, "global", configErrors, root));
18583
19117
  }
18584
19118
  }
18585
- const agentsSkillsRoots = commandcodeAgentsSkillsRoots(startDir, scanBoundary, homedir6());
19119
+ const agentsSkillsRoots = commandcodeAgentsSkillsRoots(startDir, scanBoundary, homedir9());
18586
19120
  applyCommandcodeSkillPrecedence(skills, {
18587
19121
  commandcodeProjectRoot,
18588
19122
  extraDirs,
@@ -18602,6 +19136,7 @@ function discoverAgentSurface(root, config, opts) {
18602
19136
  for (const dir of dirs) {
18603
19137
  hooks.push(...discoverHooks(dir, configErrors));
18604
19138
  hooks.push(...discoverVscodeHooks(dir, configErrors));
19139
+ hooks.push(...discoverGrokHooks(join17(dir, ".grok", "hooks"), dir, configErrors));
18605
19140
  const discovered = discoverMcpSurface(dir, config.mcpPaths, configErrors);
18606
19141
  if (discovered.codexProjectDocMaxBytes !== undefined && codexProjectDocMaxBytes === undefined) {
18607
19142
  codexProjectDocMaxBytes = discovered.codexProjectDocMaxBytes;
@@ -18622,6 +19157,7 @@ function discoverAgentSurface(root, config, opts) {
18622
19157
  }
18623
19158
  rules.push(...discoverRules(dir, configErrors));
18624
19159
  }
19160
+ rules.push(...discoverNestedWindsurfRules(scanBoundary, configErrors));
18625
19161
  mcp.push(...discoverNestedContinueMcp(scanBoundary, configErrors, mcpSeen));
18626
19162
  hooks.push(...discoverPluginHooks(scanBoundary, configErrors));
18627
19163
  hooks.push(...discoverCommandcodeHooks(commandcodeProjectRoot, ccLayers, configErrors));
@@ -18635,6 +19171,23 @@ function discoverAgentSurface(root, config, opts) {
18635
19171
  }
18636
19172
  if (opts.includeGlobal) {
18637
19173
  hooks.push(...discoverCopilotUserHooks(configErrors));
19174
+ hooks.push(...discoverGrokHooks(join17(grokHomeDir(), "hooks"), grokHomeDir(), configErrors));
19175
+ for (const fact of discoverGrokUserMcp(configErrors)) {
19176
+ const key = mcpKey(fact);
19177
+ if (mcpSeen.has(key)) {
19178
+ continue;
19179
+ }
19180
+ mcpSeen.add(key);
19181
+ mcp.push(fact);
19182
+ }
19183
+ for (const fact of discoverCodexUserMcp(configErrors)) {
19184
+ const key = mcpKey(fact);
19185
+ if (mcpSeen.has(key)) {
19186
+ continue;
19187
+ }
19188
+ mcpSeen.add(key);
19189
+ mcp.push(fact);
19190
+ }
18638
19191
  for (const fact of discoverCommandcodeUserMcp(commandcodeProjectRoot, configErrors)) {
18639
19192
  const key = mcpKey(fact);
18640
19193
  if (mcpSeen.has(key)) {
@@ -18643,8 +19196,18 @@ function discoverAgentSurface(root, config, opts) {
18643
19196
  mcpSeen.add(key);
18644
19197
  mcp.push(fact);
18645
19198
  }
19199
+ for (const fact of discoverWindsurfUserMcp(configErrors)) {
19200
+ const key = mcpKey(fact);
19201
+ if (mcpSeen.has(key)) {
19202
+ continue;
19203
+ }
19204
+ mcpSeen.add(key);
19205
+ mcp.push(fact);
19206
+ }
19207
+ rules.push(...discoverWindsurfUserRules(configErrors));
18646
19208
  }
18647
19209
  applyCommandcodeMcpPrecedence(mcp, commandcodeProjectRoot);
19210
+ applyGrokMcpPrecedence(mcp, startDir);
18648
19211
  const slashCommands = discoverCommandcodeCommands(commandcodeProjectRoot, opts.includeGlobal, configErrors);
18649
19212
  const mods = discoverCommandcodeMods(ccLayers);
18650
19213
  const winningModel = winningCommandcodeModel(ccLayers);
@@ -18661,7 +19224,7 @@ function discoverAgentSurface(root, config, opts) {
18661
19224
  hooks: allHooks,
18662
19225
  mcp,
18663
19226
  policyFiles: discoverPolicyFiles(scanBoundary, config.policyFiles, configErrors, startDir, opts.includeGlobal, commandcodeProjectRoot, codexProjectDocFallbackFilenames ?? []),
18664
- rules,
19227
+ rules: uniqueRules(rules),
18665
19228
  lockedSkills: lock.locked,
18666
19229
  hasSkillsLock: lock.present,
18667
19230
  skillsLockInvalid: lock.invalid,
@@ -18679,11 +19242,11 @@ function discoverAgentSurface(root, config, opts) {
18679
19242
  }
18680
19243
 
18681
19244
  // src/facts/extract.ts
18682
- import { existsSync as existsSync11, readFileSync as readFileSync4 } from "node:fs";
18683
- import { join as join13 } from "node:path";
19245
+ import { existsSync as existsSync14, readFileSync as readFileSync4 } from "node:fs";
19246
+ import { join as join18 } from "node:path";
18684
19247
  function readPackageJson(root, errors3) {
18685
- const path = join13(root, "package.json");
18686
- if (!existsSync11(path)) {
19248
+ const path = join18(root, "package.json");
19249
+ if (!existsSync14(path)) {
18687
19250
  return {};
18688
19251
  }
18689
19252
  let text;
@@ -18781,16 +19344,16 @@ function inferPackageManager(root, pkg) {
18781
19344
  return prefix;
18782
19345
  }
18783
19346
  }
18784
- if (existsSync11(join13(root, "bun.lock")) || existsSync11(join13(root, "bun.lockb"))) {
19347
+ if (existsSync14(join18(root, "bun.lock")) || existsSync14(join18(root, "bun.lockb"))) {
18785
19348
  return "bun";
18786
19349
  }
18787
- if (existsSync11(join13(root, "pnpm-lock.yaml"))) {
19350
+ if (existsSync14(join18(root, "pnpm-lock.yaml"))) {
18788
19351
  return "pnpm";
18789
19352
  }
18790
- if (existsSync11(join13(root, "yarn.lock"))) {
19353
+ if (existsSync14(join18(root, "yarn.lock"))) {
18791
19354
  return "yarn";
18792
19355
  }
18793
- if (existsSync11(join13(root, "package-lock.json"))) {
19356
+ if (existsSync14(join18(root, "package-lock.json"))) {
18794
19357
  return "npm";
18795
19358
  }
18796
19359
  return "unknown";
@@ -18867,7 +19430,7 @@ function compareAction(a, b) {
18867
19430
 
18868
19431
  // src/analyze.ts
18869
19432
  function analyze(options = {}) {
18870
- const requested = resolve8(options.dir ?? process.cwd());
19433
+ const requested = resolve9(options.dir ?? process.cwd());
18871
19434
  const ctx = resolveScanContext(requested);
18872
19435
  const root = ctx.projectRoot;
18873
19436
  const loaded = loadConfig(root, options.configPath);
@@ -18998,7 +19561,7 @@ function renderJson(args) {
18998
19561
  }
18999
19562
 
19000
19563
  // src/report/prompt.ts
19001
- import { basename as basename9 } from "node:path";
19564
+ import { basename as basename10 } from "node:path";
19002
19565
 
19003
19566
  // src/report/safe.ts
19004
19567
  var MAX_RENDERED = 400;
@@ -19033,7 +19596,7 @@ function safe(value) {
19033
19596
  function renderPrompt(args) {
19034
19597
  const { version, facts } = args;
19035
19598
  const actionable = sortFindings(args.findings).filter((f) => f.severity !== "info");
19036
- const project = basename9(facts.root) || facts.root;
19599
+ const project = basename10(facts.root) || facts.root;
19037
19600
  if (actionable.length === 0) {
19038
19601
  return [
19039
19602
  `# agentscan v${version} — ${safe(project)}`,
@@ -19083,7 +19646,7 @@ function headingFor(severity) {
19083
19646
  }
19084
19647
 
19085
19648
  // src/report/text.ts
19086
- import { basename as basename10 } from "node:path";
19649
+ import { basename as basename11 } from "node:path";
19087
19650
 
19088
19651
  // src/report/ansi.ts
19089
19652
  var CODES = {
@@ -19184,7 +19747,7 @@ function formatHeaderBox(findings, verbose, name, ink) {
19184
19747
  ];
19185
19748
  }
19186
19749
  function projectLabel(root) {
19187
- const base = basename10(root);
19750
+ const base = basename11(root);
19188
19751
  return base.length > 0 ? base : root;
19189
19752
  }
19190
19753
  function formatStack(facts) {
@@ -19295,7 +19858,7 @@ function formatSummary(findings, verbose, ink) {
19295
19858
  }
19296
19859
 
19297
19860
  // src/version.ts
19298
- var VERSION = "1.1.0";
19861
+ var VERSION = "1.3.0";
19299
19862
 
19300
19863
  // src/commands/check.ts
19301
19864
  async function runCheck(options) {
@@ -19342,13 +19905,13 @@ import {
19342
19905
  writeFileSync
19343
19906
  } from "node:fs";
19344
19907
  import { tmpdir } from "node:os";
19345
- import { join as join14 } from "node:path";
19908
+ import { join as join19 } from "node:path";
19346
19909
  async function runDemo() {
19347
- const root = mkdtempSync(join14(tmpdir(), "agentscan-demo-"));
19910
+ const root = mkdtempSync(join19(tmpdir(), "agentscan-demo-"));
19348
19911
  try {
19349
- const claude = join14(root, ".claude");
19912
+ const claude = join19(root, ".claude");
19350
19913
  mkdirSync(claude, { recursive: true });
19351
- writeFileSync(join14(claude, "settings.json"), `${JSON.stringify({
19914
+ writeFileSync(join19(claude, "settings.json"), `${JSON.stringify({
19352
19915
  hooks: {
19353
19916
  PreToolUse: [
19354
19917
  {
@@ -19422,12 +19985,12 @@ function formatExplain(f) {
19422
19985
  }
19423
19986
 
19424
19987
  // src/commands/init.ts
19425
- import { existsSync as existsSync12, writeFileSync as writeFileSync2 } from "node:fs";
19426
- import { join as join15, resolve as resolve9 } from "node:path";
19988
+ import { existsSync as existsSync15, writeFileSync as writeFileSync2 } from "node:fs";
19989
+ import { join as join20, resolve as resolve10 } from "node:path";
19427
19990
  async function runInit(dir, options = {}) {
19428
- const root = resolve9(dir ?? process.cwd());
19429
- const path = join15(root, ".agentscanrc.json");
19430
- if (existsSync12(path) && !options.force) {
19991
+ const root = resolve10(dir ?? process.cwd());
19992
+ const path = join20(root, ".agentscanrc.json");
19993
+ if (existsSync15(path) && !options.force) {
19431
19994
  return {
19432
19995
  exitCode: 2,
19433
19996
  stdout: "",
@@ -19463,7 +20026,7 @@ async function runRulesCommand(options = {}) {
19463
20026
  // src/report/clipboard.ts
19464
20027
  import { spawn } from "node:child_process";
19465
20028
  import { accessSync, constants } from "node:fs";
19466
- import { delimiter, join as join16 } from "node:path";
20029
+ import { delimiter, join as join21 } from "node:path";
19467
20030
  var TOOLS = [
19468
20031
  ["wl-copy"],
19469
20032
  ["xclip", "-selection", "clipboard"],
@@ -19472,15 +20035,15 @@ var TOOLS = [
19472
20035
  ["clip.exe"]
19473
20036
  ];
19474
20037
  var TIMEOUT_MS = 3000;
19475
- function clipboardCandidates(onPath = (name) => resolve10(name) !== null) {
20038
+ function clipboardCandidates(onPath = (name) => resolve11(name) !== null) {
19476
20039
  return TOOLS.filter((t) => onPath(t[0])).map((t) => [...t]);
19477
20040
  }
19478
- function resolve10(name) {
20041
+ function resolve11(name) {
19479
20042
  for (const dir of (process.env.PATH ?? "").split(delimiter)) {
19480
20043
  if (dir.length === 0) {
19481
20044
  continue;
19482
20045
  }
19483
- const candidate = join16(dir, name);
20046
+ const candidate = join21(dir, name);
19484
20047
  try {
19485
20048
  accessSync(candidate, constants.X_OK);
19486
20049
  return candidate;
@@ -19499,7 +20062,7 @@ async function copyToClipboard(text) {
19499
20062
  const failures = [];
19500
20063
  for (const argv of candidates) {
19501
20064
  const tool = argv[0];
19502
- const bin = resolve10(tool);
20065
+ const bin = resolve11(tool);
19503
20066
  if (bin === null) {
19504
20067
  continue;
19505
20068
  }