@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.
- package/CHANGELOG.md +91 -0
- package/README.md +83 -26
- package/dist/cli.js +757 -194
- 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
|
|
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
|
|
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 ??
|
|
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
|
|
13820
|
-
import { join as
|
|
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
|
|
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
|
|
13829
|
-
import { basename as basename6, dirname as dirname3, isAbsolute as isAbsolute4, join as
|
|
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
|
|
13834
|
-
import { basename as basename5, isAbsolute as isAbsolute2, join as
|
|
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
|
|
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 =
|
|
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 =
|
|
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 :
|
|
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
|
|
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:
|
|
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,
|
|
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 =
|
|
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 :
|
|
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
|
|
16549
|
-
import { isAbsolute as isAbsolute3, join as
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
16825
|
+
return { scriptPath: extracted, exists: isFile(join6(cwdAbs, extracted)) };
|
|
16581
16826
|
}
|
|
16582
16827
|
const candidates = [
|
|
16583
|
-
...bases.own === undefined ? [] : [
|
|
16584
|
-
|
|
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
|
-
|
|
16988
|
-
|
|
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 =
|
|
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(
|
|
17352
|
+
return discoverHookDir(join6(root, ".github", "hooks"), root, errors3, "vscode-native");
|
|
17060
17353
|
}
|
|
17061
17354
|
function discoverCopilotUserHooks(errors3) {
|
|
17062
|
-
const dir =
|
|
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:
|
|
17121
|
-
{ path:
|
|
17122
|
-
...includeGlobal ? [{ path:
|
|
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
|
|
17478
|
+
return join7(homedir5(), trimmed.slice(2));
|
|
17145
17479
|
}
|
|
17146
17480
|
if (isAbsolute4(trimmed)) {
|
|
17147
17481
|
return trimmed;
|
|
17148
17482
|
}
|
|
17149
|
-
return
|
|
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, [
|
|
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 =
|
|
17569
|
+
const abs = join7(dir, name);
|
|
17236
17570
|
if (isDirectory2(abs)) {
|
|
17237
|
-
out.push(...commandFiles(abs, errors3).map((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 =
|
|
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:
|
|
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 =
|
|
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:
|
|
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 =
|
|
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(
|
|
17738
|
+
facts.push(...discoverCommandcodeAgentsDir(join7(commandcodeProjectRoot, ".commandcode", "agents"), errors3));
|
|
17405
17739
|
if (includeGlobal) {
|
|
17406
|
-
facts.push(...discoverCommandcodeAgentsDir(
|
|
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(
|
|
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(
|
|
17441
|
-
const userMcp = resolve4(
|
|
17442
|
-
const localSettings = resolve4(
|
|
17443
|
-
const projectSettings = resolve4(
|
|
17444
|
-
const userSettings = resolve4(
|
|
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 =
|
|
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 =
|
|
17563
|
-
const userDir = resolve4(
|
|
17564
|
-
const projectDir = resolve4(
|
|
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 =
|
|
17589
|
-
const projectCc = resolve4(
|
|
17590
|
-
const userCc = resolve4(
|
|
17591
|
-
const userAgents = resolve4(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
17809
|
-
import { join as
|
|
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 (
|
|
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 =
|
|
17835
|
-
if (
|
|
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 =
|
|
17852
|
-
if (!
|
|
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
|
|
17882
|
-
import { homedir as
|
|
17883
|
-
import { basename as basename7, dirname as
|
|
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 (!
|
|
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 ??
|
|
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 =
|
|
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 =
|
|
17927
|
-
const boundary =
|
|
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) =>
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
-
|
|
17958
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
18006
|
-
return !
|
|
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:
|
|
18437
|
+
...commandcodeMemory ? { hopsDir: dirname6(dirname6(abs)) } : {}
|
|
18018
18438
|
}));
|
|
18019
18439
|
}
|
|
18020
|
-
addUnique(out, seen, readPolicyFile(
|
|
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 =
|
|
18026
|
-
if (
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
18118
|
-
if (seen.has(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(
|
|
18548
|
+
lockRoots.push(dirname6(resolved));
|
|
18129
18549
|
locked.push(...parsed.entries);
|
|
18130
18550
|
};
|
|
18131
|
-
consider(
|
|
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
|
|
18149
|
-
import { join as
|
|
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
|
|
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 =
|
|
18182
|
-
if (
|
|
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 =
|
|
18661
|
+
const fact = readRuleFile(abs, "cursor", errors3);
|
|
18189
18662
|
if (fact !== undefined) {
|
|
18190
18663
|
out.push(fact);
|
|
18191
18664
|
}
|
|
18192
18665
|
}
|
|
18193
18666
|
}
|
|
18194
|
-
|
|
18195
|
-
|
|
18196
|
-
|
|
18197
|
-
|
|
18198
|
-
|
|
18199
|
-
|
|
18200
|
-
|
|
18201
|
-
|
|
18202
|
-
|
|
18203
|
-
|
|
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
|
|
18212
|
-
import { basename as
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
18885
|
+
const skillMd = join16(skillDir, "SKILL.md");
|
|
18366
18886
|
const fm = opts.hasSkillMd ? readFrontmatter(skillMd, errors3) : { hasFrontmatter: false };
|
|
18367
18887
|
const fact = {
|
|
18368
|
-
id:
|
|
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 (!
|
|
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) =>
|
|
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 =
|
|
18959
|
+
const child = join16(dir, name);
|
|
18440
18960
|
if (!isDirectory3(child)) {
|
|
18441
18961
|
continue;
|
|
18442
18962
|
}
|
|
18443
|
-
const resolved =
|
|
18444
|
-
if (foundSet.has(resolved) || found.some((path) =>
|
|
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 =
|
|
18485
|
-
if (configuredRoots.has(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
19102
|
+
const home = homedir9();
|
|
18570
19103
|
const globalSkillDirs = [
|
|
18571
|
-
|
|
18572
|
-
|
|
18573
|
-
|
|
18574
|
-
|
|
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 =
|
|
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,
|
|
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
|
|
18683
|
-
import { join as
|
|
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 =
|
|
18686
|
-
if (!
|
|
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 (
|
|
19347
|
+
if (existsSync14(join18(root, "bun.lock")) || existsSync14(join18(root, "bun.lockb"))) {
|
|
18785
19348
|
return "bun";
|
|
18786
19349
|
}
|
|
18787
|
-
if (
|
|
19350
|
+
if (existsSync14(join18(root, "pnpm-lock.yaml"))) {
|
|
18788
19351
|
return "pnpm";
|
|
18789
19352
|
}
|
|
18790
|
-
if (
|
|
19353
|
+
if (existsSync14(join18(root, "yarn.lock"))) {
|
|
18791
19354
|
return "yarn";
|
|
18792
19355
|
}
|
|
18793
|
-
if (
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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.
|
|
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
|
|
19908
|
+
import { join as join19 } from "node:path";
|
|
19346
19909
|
async function runDemo() {
|
|
19347
|
-
const root = mkdtempSync(
|
|
19910
|
+
const root = mkdtempSync(join19(tmpdir(), "agentscan-demo-"));
|
|
19348
19911
|
try {
|
|
19349
|
-
const claude =
|
|
19912
|
+
const claude = join19(root, ".claude");
|
|
19350
19913
|
mkdirSync(claude, { recursive: true });
|
|
19351
|
-
writeFileSync(
|
|
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
|
|
19426
|
-
import { join as
|
|
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 =
|
|
19429
|
-
const path =
|
|
19430
|
-
if (
|
|
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
|
|
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) =>
|
|
20038
|
+
function clipboardCandidates(onPath = (name) => resolve11(name) !== null) {
|
|
19476
20039
|
return TOOLS.filter((t) => onPath(t[0])).map((t) => [...t]);
|
|
19477
20040
|
}
|
|
19478
|
-
function
|
|
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 =
|
|
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 =
|
|
20065
|
+
const bin = resolve11(tool);
|
|
19503
20066
|
if (bin === null) {
|
|
19504
20067
|
continue;
|
|
19505
20068
|
}
|