calllint 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +731 -45
  2. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { readFileSync as readFileSync12 } from "node:fs";
4
+ import { readFileSync as readFileSync17 } from "node:fs";
5
5
  import { execFileSync } from "node:child_process";
6
6
 
7
7
  // src/args.ts
@@ -58,16 +58,23 @@ USAGE
58
58
 
59
59
  COMMANDS
60
60
  check [target] Compact safety decision for an MCP config or install snippet
61
+ inventory List all discovered agent configs (auto-discovery, no scan)
61
62
  scan-all Scan every agent-tool surface in the repo (compact table)
62
63
  explain <server> Explain the verdict for one server from the last scan
63
64
  verify [target] Compare a fresh scan against the baseline (drift / rug-pull)
64
65
 
65
66
  Advanced:
66
67
  scan [target] Full ScanReport for an MCP config / npm:<pkg> / github:<repo>
68
+ scan --auto Discover and scan all agent configs (auto-discovery)
69
+ scan --agent <type> Discover and scan a specific agent (cursor, claude-code, claude-desktop, vscode, windsurf)
70
+ action inspect <f> Preflight a planned external action (calllint.action.v0)
71
+ inbox inspect <f> Preflight a normalized agent inbox event
67
72
  diagnostics [target] Emit editor/agent-host diagnostics JSON (calllint.diagnostics.v0)
68
73
  baseline [target] Record the approved risk surface as a baseline
69
74
  approve Record the repo-wide capability surface as approved state (L4)
70
- receipt verify <f> Structurally validate a local calllint.receipt.v0 file
75
+ receipt verify <f> Validate a calllint.receipt.v0 (structure + signature if present)
76
+ receipt sign <f> Sign a receipt with a local key (--key; development/testing)
77
+ receipt keygen Generate a local ed25519 keypair (--out; development/testing)
71
78
  gen-rule --host <h> Emit the CallLint agent-safety rule for a host (CLAUDE.md, etc.)
72
79
  policy init Write a default calllint.policy.json
73
80
  policy explain Show the effective policy
@@ -85,6 +92,8 @@ TARGETS
85
92
  github:<owner/repo>[@ref] A GitHub repo (requires --online)
86
93
 
87
94
  SCAN OPTIONS
95
+ --auto Discover and scan all agent configs (P0+P1: Cursor, Claude Code, Claude Desktop, VS Code, Windsurf)
96
+ --agent <type> Discover and scan a specific agent type
88
97
  --changed Scan only the agent-tool configs changed in the git diff
89
98
  --json Emit the ScanReport JSON (stable, emoji-free)
90
99
  --compact One line per server
@@ -93,7 +102,7 @@ SCAN OPTIONS
93
102
  --markdown Emit Markdown for PR comments / GitHub Step Summary
94
103
  --html Emit a self-contained HTML report
95
104
  --badge Emit a shields.io endpoint badge JSON (SAFE/REVIEW/UNKNOWN/BLOCK)
96
- --receipt Also write a local calllint.receipt.v0 (offline reporting layer)
105
+ --receipt Generate a cryptographically-verifiable audit receipt (for CI/compliance)
97
106
  --receipt-out <f> Receipt output path (default: calllint-receipt.json)
98
107
  --policy <file> Use a policy file (default: built-in defaults)
99
108
  --stdin Read config JSON from stdin
@@ -108,6 +117,9 @@ VERIFY OPTIONS
108
117
  --json Emit the drift report JSON
109
118
 
110
119
  EXAMPLES
120
+ calllint inventory # List discovered agent configs
121
+ calllint scan --auto # Discover and scan all agents
122
+ calllint scan --agent cursor # Scan only Cursor configs
111
123
  calllint check .cursor/mcp.json
112
124
  calllint check npm:mcp-weather@1.0.0
113
125
  echo "npx -y demo-mcp@1.2.3" | calllint check --stdin
@@ -115,7 +127,10 @@ EXAMPLES
115
127
  calllint check ./mcp.json --json
116
128
  calllint scan .cursor/mcp.json --markdown
117
129
  calllint scan .cursor/mcp.json --badge > calllint-badge.json
118
- calllint scan .cursor/mcp.json --receipt && calllint receipt verify calllint-receipt.json
130
+ calllint scan .cursor/mcp.json --receipt # Generate audit receipt
131
+ calllint receipt verify calllint-receipt.json # Verify receipt integrity
132
+ calllint action inspect payment.json
133
+ calllint inbox inspect gmail-reply.normalized.json
119
134
  calllint verify ./mcp.json --ci
120
135
  calllint approve && calllint verify --approved --ci
121
136
  calllint explain filesystem
@@ -125,7 +140,7 @@ function helpCommand() {
125
140
  }
126
141
 
127
142
  // src/commands/scan.ts
128
- import { join as join6, resolve as resolve2 } from "node:path";
143
+ import { join as join11, resolve as resolve3 } from "node:path";
129
144
 
130
145
  // ../../packages/policy/src/defaultPolicy.ts
131
146
  function defaultPolicy() {
@@ -3883,11 +3898,437 @@ function readDocumentSurfaces(dir) {
3883
3898
  }
3884
3899
 
3885
3900
  // src/commands/scan.ts
3886
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "node:fs";
3901
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync3 } from "node:fs";
3902
+
3903
+ // ../../packages/discovery/src/registry.ts
3904
+ var ExtractorRegistry = class {
3905
+ extractors = /* @__PURE__ */ new Map();
3906
+ /**
3907
+ * Register an agent extractor.
3908
+ */
3909
+ register(extractor) {
3910
+ if (this.extractors.has(extractor.agentType)) {
3911
+ throw new Error(`Extractor already registered for agent type: ${extractor.agentType}`);
3912
+ }
3913
+ this.extractors.set(extractor.agentType, extractor);
3914
+ }
3915
+ /**
3916
+ * Get extractor for a specific agent type.
3917
+ */
3918
+ get(agentType) {
3919
+ return this.extractors.get(agentType);
3920
+ }
3921
+ /**
3922
+ * Get all registered extractors.
3923
+ */
3924
+ getAll() {
3925
+ return Array.from(this.extractors.values());
3926
+ }
3927
+ /**
3928
+ * Get extractors filtered by agent types.
3929
+ */
3930
+ getByTypes(agentTypes) {
3931
+ return agentTypes.map((type) => this.extractors.get(type)).filter((e) => e !== void 0);
3932
+ }
3933
+ /**
3934
+ * Get extractors filtered by priority tier.
3935
+ */
3936
+ getByPriority(priority) {
3937
+ return this.getAll().filter((e) => e.priority === priority);
3938
+ }
3939
+ /**
3940
+ * Get all extractors sorted by priority (P0 first, P3 last).
3941
+ */
3942
+ getAllSortedByPriority() {
3943
+ const priorityOrder = {
3944
+ P0: 0,
3945
+ P1: 1,
3946
+ P2: 2,
3947
+ P3: 3
3948
+ };
3949
+ return this.getAll().sort((a, b) => {
3950
+ return priorityOrder[a.priority] - priorityOrder[b.priority];
3951
+ });
3952
+ }
3953
+ /**
3954
+ * Clear all registered extractors (for testing).
3955
+ */
3956
+ clear() {
3957
+ this.extractors.clear();
3958
+ }
3959
+ };
3960
+ var registry = new ExtractorRegistry();
3961
+
3962
+ // ../../packages/discovery/src/extractors/base.ts
3963
+ var BaseAgentExtractor = class {
3964
+ /**
3965
+ * Resolve home directory (~) to absolute path.
3966
+ */
3967
+ resolveHome() {
3968
+ const home = process.env.HOME || process.env.USERPROFILE;
3969
+ if (!home) {
3970
+ throw new Error("Could not resolve home directory (HOME/USERPROFILE not set)");
3971
+ }
3972
+ return home;
3973
+ }
3974
+ /**
3975
+ * Get platform-specific application data directory.
3976
+ *
3977
+ * Windows: %APPDATA% (Roaming)
3978
+ * macOS: ~/Library/Application Support
3979
+ * Linux: ~/.config (XDG_CONFIG_HOME or default)
3980
+ */
3981
+ getAppDataDir() {
3982
+ const platform = process.platform;
3983
+ if (platform === "win32") {
3984
+ const appData = process.env.APPDATA;
3985
+ if (!appData) {
3986
+ throw new Error("Could not resolve APPDATA directory on Windows");
3987
+ }
3988
+ return appData;
3989
+ }
3990
+ if (platform === "darwin") {
3991
+ return `${this.resolveHome()}/Library/Application Support`;
3992
+ }
3993
+ return process.env.XDG_CONFIG_HOME || `${this.resolveHome()}/.config`;
3994
+ }
3995
+ };
3996
+
3997
+ // ../../packages/discovery/src/path-resolver.ts
3998
+ import { resolve as resolve2, join as join6, isAbsolute } from "node:path";
3999
+ import { existsSync as existsSync6, statSync as statSync3 } from "node:fs";
4000
+ function resolvePath(path, cwd) {
4001
+ if (path.startsWith("~/") || path === "~") {
4002
+ const home = process.env.HOME || process.env.USERPROFILE;
4003
+ if (!home) {
4004
+ throw new Error("Could not resolve home directory (HOME/USERPROFILE not set)");
4005
+ }
4006
+ return join6(home, path.slice(2));
4007
+ }
4008
+ if (isAbsolute(path)) {
4009
+ return path;
4010
+ }
4011
+ return resolve2(cwd, path);
4012
+ }
4013
+ function isRegularFile(path) {
4014
+ try {
4015
+ if (!existsSync6(path)) {
4016
+ return false;
4017
+ }
4018
+ const stats = statSync3(path);
4019
+ return stats.isFile();
4020
+ } catch {
4021
+ return false;
4022
+ }
4023
+ }
4024
+ function isReasonableSize(path) {
4025
+ try {
4026
+ const stats = statSync3(path);
4027
+ const maxSize = 10 * 1024 * 1024;
4028
+ return stats.size < maxSize;
4029
+ } catch {
4030
+ return false;
4031
+ }
4032
+ }
4033
+ function validateConfigPath(path) {
4034
+ return isRegularFile(path) && isReasonableSize(path);
4035
+ }
4036
+
4037
+ // ../../packages/discovery/src/extractors/cursor.ts
4038
+ import { readFileSync as readFileSync7 } from "node:fs";
4039
+ var CursorExtractor = class extends BaseAgentExtractor {
4040
+ agentType = "cursor";
4041
+ priority = "P0";
4042
+ discover(cwd) {
4043
+ const configs = [];
4044
+ const projectPath = resolvePath(".cursor/mcp.json", cwd);
4045
+ configs.push(this.createConfig(projectPath));
4046
+ try {
4047
+ const home = this.resolveHome();
4048
+ const userPath = resolvePath(".cursor/mcp.json", home);
4049
+ if (userPath !== projectPath) {
4050
+ configs.push(this.createConfig(userPath));
4051
+ }
4052
+ } catch {
4053
+ }
4054
+ return configs;
4055
+ }
4056
+ createConfig(configPath) {
4057
+ const exists = this.isValidConfig(configPath);
4058
+ return {
4059
+ agentType: this.agentType,
4060
+ configPath,
4061
+ exists,
4062
+ kind: "cursor-mcp-config",
4063
+ priority: this.priority
4064
+ };
4065
+ }
4066
+ /**
4067
+ * Check if path is a valid Cursor config.
4068
+ * Must exist, be regular file, reasonable size, and contain mcpServers key.
4069
+ */
4070
+ isValidConfig(path) {
4071
+ if (!validateConfigPath(path)) {
4072
+ return false;
4073
+ }
4074
+ try {
4075
+ const content = readFileSync7(path, "utf8");
4076
+ const json = JSON.parse(content);
4077
+ return typeof json === "object" && json !== null && "mcpServers" in json;
4078
+ } catch {
4079
+ return false;
4080
+ }
4081
+ }
4082
+ };
4083
+
4084
+ // ../../packages/discovery/src/extractors/claude-code.ts
4085
+ import { readFileSync as readFileSync8 } from "node:fs";
4086
+ import { join as join7 } from "node:path";
4087
+ var ClaudeCodeExtractor = class extends BaseAgentExtractor {
4088
+ agentType = "claude-code";
4089
+ priority = "P0";
4090
+ discover(cwd) {
4091
+ const configs = [];
4092
+ const projectPath = resolvePath(".claude/settings.json", cwd);
4093
+ configs.push(this.createConfig(projectPath));
4094
+ try {
4095
+ const userPath = this.getUserSettingsPath();
4096
+ if (userPath !== projectPath) {
4097
+ configs.push(this.createConfig(userPath));
4098
+ }
4099
+ } catch {
4100
+ }
4101
+ return configs;
4102
+ }
4103
+ getUserSettingsPath() {
4104
+ const appDataDir = this.getAppDataDir();
4105
+ return join7(appDataDir, "Claude", "settings.json");
4106
+ }
4107
+ createConfig(configPath) {
4108
+ const exists = this.isValidConfig(configPath);
4109
+ return {
4110
+ agentType: this.agentType,
4111
+ configPath,
4112
+ exists,
4113
+ kind: "claude-settings",
4114
+ priority: this.priority
4115
+ };
4116
+ }
4117
+ /**
4118
+ * Check if path is a valid Claude Code config.
4119
+ * Must exist, be regular file, reasonable size, and contain mcpServers key.
4120
+ */
4121
+ isValidConfig(path) {
4122
+ if (!validateConfigPath(path)) {
4123
+ return false;
4124
+ }
4125
+ try {
4126
+ const content = readFileSync8(path, "utf8");
4127
+ const json = JSON.parse(content);
4128
+ return typeof json === "object" && json !== null && "mcpServers" in json;
4129
+ } catch {
4130
+ return false;
4131
+ }
4132
+ }
4133
+ };
4134
+
4135
+ // ../../packages/discovery/src/extractors/claude-desktop.ts
4136
+ import { readFileSync as readFileSync9 } from "node:fs";
4137
+ import { join as join8 } from "node:path";
4138
+ var ClaudeDesktopExtractor = class extends BaseAgentExtractor {
4139
+ agentType = "claude-desktop";
4140
+ priority = "P0";
4141
+ discover(_cwd) {
4142
+ const configs = [];
4143
+ try {
4144
+ const userPath = this.getUserConfigPath();
4145
+ configs.push(this.createConfig(userPath));
4146
+ } catch {
4147
+ }
4148
+ return configs;
4149
+ }
4150
+ getUserConfigPath() {
4151
+ const appDataDir = this.getAppDataDir();
4152
+ return join8(appDataDir, "Claude", "claude_desktop_config.json");
4153
+ }
4154
+ createConfig(configPath) {
4155
+ const exists = this.isValidConfig(configPath);
4156
+ return {
4157
+ agentType: this.agentType,
4158
+ configPath,
4159
+ exists,
4160
+ kind: "claude-settings",
4161
+ priority: this.priority
4162
+ };
4163
+ }
4164
+ /**
4165
+ * Check if path is a valid Claude Desktop config.
4166
+ * Must exist, be regular file, reasonable size, and contain mcpServers key.
4167
+ */
4168
+ isValidConfig(path) {
4169
+ if (!validateConfigPath(path)) {
4170
+ return false;
4171
+ }
4172
+ try {
4173
+ const content = readFileSync9(path, "utf8");
4174
+ const json = JSON.parse(content);
4175
+ return typeof json === "object" && json !== null && "mcpServers" in json;
4176
+ } catch {
4177
+ return false;
4178
+ }
4179
+ }
4180
+ };
4181
+
4182
+ // ../../packages/discovery/src/extractors/vscode.ts
4183
+ import { readFileSync as readFileSync10 } from "node:fs";
4184
+ import { join as join9 } from "node:path";
4185
+ var VSCodeExtractor = class extends BaseAgentExtractor {
4186
+ agentType = "vscode";
4187
+ priority = "P1";
4188
+ discover(cwd) {
4189
+ const configs = [];
4190
+ try {
4191
+ const userPath = this.getUserConfigPath();
4192
+ configs.push(this.createConfig(userPath));
4193
+ } catch {
4194
+ }
4195
+ return configs;
4196
+ }
4197
+ getUserConfigPath() {
4198
+ const appDataDir = this.getAppDataDir();
4199
+ return join9(appDataDir, "Code", "User", "mcp.json");
4200
+ }
4201
+ createConfig(configPath) {
4202
+ const exists = this.isValidConfig(configPath);
4203
+ return {
4204
+ agentType: this.agentType,
4205
+ configPath,
4206
+ exists,
4207
+ kind: "vscode-mcp-config",
4208
+ priority: this.priority
4209
+ };
4210
+ }
4211
+ /**
4212
+ * Check if path is a valid VS Code MCP config.
4213
+ * Must exist, be regular file, reasonable size, and contain mcpServers key.
4214
+ */
4215
+ isValidConfig(path) {
4216
+ if (!validateConfigPath(path)) {
4217
+ return false;
4218
+ }
4219
+ try {
4220
+ const content = readFileSync10(path, "utf8");
4221
+ const json = JSON.parse(content);
4222
+ return typeof json === "object" && json !== null && "mcpServers" in json && json.mcpServers !== null;
4223
+ } catch {
4224
+ return false;
4225
+ }
4226
+ }
4227
+ };
4228
+
4229
+ // ../../packages/discovery/src/extractors/windsurf.ts
4230
+ import { readFileSync as readFileSync11 } from "node:fs";
4231
+ import { join as join10 } from "node:path";
4232
+ var WindsurfExtractor = class extends BaseAgentExtractor {
4233
+ agentType = "windsurf";
4234
+ priority = "P1";
4235
+ discover(cwd) {
4236
+ const configs = [];
4237
+ try {
4238
+ const userPath = this.getUserConfigPath();
4239
+ configs.push(this.createConfig(userPath));
4240
+ } catch {
4241
+ }
4242
+ return configs;
4243
+ }
4244
+ getUserConfigPath() {
4245
+ const appDataDir = this.getAppDataDir();
4246
+ return join10(appDataDir, "Windsurf", "mcp.json");
4247
+ }
4248
+ createConfig(configPath) {
4249
+ const exists = this.isValidConfig(configPath);
4250
+ return {
4251
+ agentType: this.agentType,
4252
+ configPath,
4253
+ exists,
4254
+ kind: "windsurf-mcp-config",
4255
+ priority: this.priority
4256
+ };
4257
+ }
4258
+ /**
4259
+ * Check if path is a valid Windsurf MCP config.
4260
+ * Must exist, be regular file, reasonable size, and contain mcpServers key.
4261
+ */
4262
+ isValidConfig(path) {
4263
+ if (!validateConfigPath(path)) {
4264
+ return false;
4265
+ }
4266
+ try {
4267
+ const content = readFileSync11(path, "utf8");
4268
+ const json = JSON.parse(content);
4269
+ return typeof json === "object" && json !== null && "mcpServers" in json && json.mcpServers !== null;
4270
+ } catch {
4271
+ return false;
4272
+ }
4273
+ }
4274
+ };
4275
+
4276
+ // ../../packages/discovery/src/bootstrap.ts
4277
+ function bootstrapExtractors() {
4278
+ registry.register(new CursorExtractor());
4279
+ registry.register(new ClaudeCodeExtractor());
4280
+ registry.register(new ClaudeDesktopExtractor());
4281
+ registry.register(new VSCodeExtractor());
4282
+ registry.register(new WindsurfExtractor());
4283
+ }
4284
+ bootstrapExtractors();
4285
+
4286
+ // ../../packages/discovery/src/discovery-engine.ts
4287
+ function discoverConfigs(options) {
4288
+ const { cwd, agentTypes, includeMissing = false } = options;
4289
+ const extractors = agentTypes ? registry.getByTypes(agentTypes) : registry.getAllSortedByPriority();
4290
+ const results = extractors.map((extractor) => {
4291
+ try {
4292
+ return extractor.discover(cwd);
4293
+ } catch (error) {
4294
+ console.error(`[discovery] Extractor ${extractor.agentType} failed:`, error);
4295
+ return [];
4296
+ }
4297
+ });
4298
+ const allDiscovered = results.flat();
4299
+ const discovered = includeMissing ? allDiscovered : allDiscovered.filter((config) => config.exists);
4300
+ const searchedPaths = allDiscovered.map((config) => config.configPath);
4301
+ return {
4302
+ cwd,
4303
+ discovered,
4304
+ searchedPaths
4305
+ };
4306
+ }
4307
+ function discoverAgent(agentType, options) {
4308
+ const extractor = registry.get(agentType);
4309
+ if (!extractor) {
4310
+ throw new Error(`Unknown agent type: ${agentType}`);
4311
+ }
4312
+ try {
4313
+ return extractor.discover(options.cwd);
4314
+ } catch (error) {
4315
+ console.error(`[discovery] Failed to discover ${agentType}:`, error);
4316
+ return [];
4317
+ }
4318
+ }
4319
+
4320
+ // src/commands/scan.ts
3887
4321
  function scanCommand(args, deps) {
3888
4322
  if (flagBool(args.flags, "changed")) {
3889
4323
  return scanChangedCommand(args, deps);
3890
4324
  }
4325
+ if (flagBool(args.flags, "auto")) {
4326
+ return scanAutoCommand(args, deps);
4327
+ }
4328
+ const agentType = flagStr(args.flags, "agent");
4329
+ if (agentType) {
4330
+ return scanAgentCommand(agentType, args, deps);
4331
+ }
3891
4332
  const policyPath = flagStr(args.flags, "policy");
3892
4333
  let policy;
3893
4334
  try {
@@ -3908,7 +4349,7 @@ function scanCommand(args, deps) {
3908
4349
  }
3909
4350
  function scanOneConfig(text, configPath, policy, args, deps, allowReceipt = true) {
3910
4351
  const surfaceDir = flagStr(args.flags, "surface-dir");
3911
- const surfaces = surfaceDir ? readDocumentSurfaces(resolve2(deps.cwd, surfaceDir)) : void 0;
4352
+ const surfaces = surfaceDir ? readDocumentSurfaces(resolve3(deps.cwd, surfaceDir)) : void 0;
3912
4353
  let summary;
3913
4354
  try {
3914
4355
  summary = scanConfigText(text, configPath, {
@@ -3930,7 +4371,7 @@ function scanOneConfig(text, configPath, policy, args, deps, allowReceipt = true
3930
4371
  }
3931
4372
  if (deps.writeCacheFile !== false) {
3932
4373
  try {
3933
- writeCache(summary, join6(deps.cwd, ".calllint", "last-scan.json"));
4374
+ writeCache(summary, join11(deps.cwd, ".calllint", "last-scan.json"));
3934
4375
  } catch {
3935
4376
  }
3936
4377
  }
@@ -3956,7 +4397,7 @@ function writeReceiptFile(summary, text, configPath, policy, args, deps) {
3956
4397
  },
3957
4398
  deps.generatedAt
3958
4399
  );
3959
- const outPath = resolve2(
4400
+ const outPath = resolve3(
3960
4401
  deps.cwd,
3961
4402
  flagStr(args.flags, "receipt-out") ?? "calllint-receipt.json"
3962
4403
  );
@@ -4004,7 +4445,7 @@ function scanChangedCommand(args, deps) {
4004
4445
  };
4005
4446
  }
4006
4447
  const results = paths.map((p) => {
4007
- const text = readFileSync7(p, "utf8");
4448
+ const text = readFileSync12(p, "utf8");
4008
4449
  return scanOneConfig(text, p, policy, args, deps);
4009
4450
  });
4010
4451
  const exitCode = results.reduce((worst, r) => Math.max(worst, r.exitCode), EXIT.OK);
@@ -4017,6 +4458,95 @@ function scanChangedCommand(args, deps) {
4017
4458
  const stderr = results.map((r) => r.stderr).filter(Boolean).join("\n");
4018
4459
  return { stdout, exitCode, ...stderr ? { stderr } : {} };
4019
4460
  }
4461
+ function scanAutoCommand(args, deps) {
4462
+ const policyPath = flagStr(args.flags, "policy");
4463
+ let policy;
4464
+ try {
4465
+ policy = loadPolicyOrDefault(policyPath);
4466
+ } catch (err2) {
4467
+ return {
4468
+ stdout: "",
4469
+ stderr: err2 instanceof Error ? err2.message : String(err2),
4470
+ exitCode: EXIT.ERROR
4471
+ };
4472
+ }
4473
+ const discovered = discoverConfigs({ cwd: deps.cwd });
4474
+ if (discovered.discovered.length === 0) {
4475
+ return {
4476
+ stdout: "",
4477
+ stderr: "No agent configs discovered. Try: calllint inventory\n",
4478
+ exitCode: EXIT.ERROR
4479
+ };
4480
+ }
4481
+ const results = [];
4482
+ for (const config of discovered.discovered) {
4483
+ let text;
4484
+ try {
4485
+ text = readFileSync12(config.configPath, "utf8");
4486
+ } catch (err2) {
4487
+ results.push({
4488
+ stdout: "",
4489
+ stderr: `Could not read ${config.configPath}: ${err2 instanceof Error ? err2.message : String(err2)}`,
4490
+ exitCode: EXIT.ERROR
4491
+ });
4492
+ continue;
4493
+ }
4494
+ const result = scanOneConfig(text, config.configPath, policy, args, deps, false);
4495
+ results.push(result);
4496
+ }
4497
+ const isJson = flagBool(args.flags, "json");
4498
+ const stdout = isJson ? JSON.stringify(results.map((r) => JSON.parse(r.stdout)), null, 2) + "\n" : results.map((r) => r.stdout).join("\n---\n\n");
4499
+ const exitCode = flagBool(args.flags, "ci") ? Math.max(...results.map((r) => r.exitCode)) : EXIT.OK;
4500
+ const stderr = results.map((r) => r.stderr).filter(Boolean).join("\n");
4501
+ return { stdout, exitCode, ...stderr ? { stderr } : {} };
4502
+ }
4503
+ function scanAgentCommand(agentType, args, deps) {
4504
+ const policyPath = flagStr(args.flags, "policy");
4505
+ let policy;
4506
+ try {
4507
+ policy = loadPolicyOrDefault(policyPath);
4508
+ } catch (err2) {
4509
+ return {
4510
+ stdout: "",
4511
+ stderr: err2 instanceof Error ? err2.message : String(err2),
4512
+ exitCode: EXIT.ERROR
4513
+ };
4514
+ }
4515
+ const discovered = discoverAgent(agentType, { cwd: deps.cwd });
4516
+ const existing = discovered.filter((c) => c.exists);
4517
+ if (existing.length === 0) {
4518
+ return {
4519
+ stdout: "",
4520
+ stderr: `No config found for agent '${agentType}'. Try: calllint inventory
4521
+ `,
4522
+ exitCode: EXIT.ERROR
4523
+ };
4524
+ }
4525
+ const results = [];
4526
+ for (const config of existing) {
4527
+ let text;
4528
+ try {
4529
+ text = readFileSync12(config.configPath, "utf8");
4530
+ } catch (err2) {
4531
+ results.push({
4532
+ stdout: "",
4533
+ stderr: `Could not read ${config.configPath}: ${err2 instanceof Error ? err2.message : String(err2)}`,
4534
+ exitCode: EXIT.ERROR
4535
+ });
4536
+ continue;
4537
+ }
4538
+ const result = scanOneConfig(text, config.configPath, policy, args, deps, true);
4539
+ results.push(result);
4540
+ }
4541
+ if (results.length === 1) {
4542
+ return results[0];
4543
+ }
4544
+ const isJson = flagBool(args.flags, "json");
4545
+ const stdout = isJson ? JSON.stringify(results.map((r) => JSON.parse(r.stdout)), null, 2) + "\n" : results.map((r) => r.stdout).join("\n---\n\n");
4546
+ const exitCode = flagBool(args.flags, "ci") ? Math.max(...results.map((r) => r.exitCode)) : EXIT.OK;
4547
+ const stderr = results.map((r) => r.stderr).filter(Boolean).join("\n");
4548
+ return { stdout, exitCode, ...stderr ? { stderr } : {} };
4549
+ }
4020
4550
 
4021
4551
  // src/commands/check.ts
4022
4552
  function checkCommand(args, deps) {
@@ -4106,7 +4636,7 @@ function worstExit2(decisions) {
4106
4636
 
4107
4637
  // src/commands/genRule.ts
4108
4638
  import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "node:fs";
4109
- import { dirname as dirname3, join as join7, resolve as resolve3 } from "node:path";
4639
+ import { dirname as dirname3, join as join12, resolve as resolve4 } from "node:path";
4110
4640
  function isRuleHost(v) {
4111
4641
  return v !== void 0 && RULE_HOSTS.includes(v);
4112
4642
  }
@@ -4137,7 +4667,7 @@ Run \`calllint gen-rule\` to list hosts.`,
4137
4667
  return { stdout: content, exitCode: EXIT.OK };
4138
4668
  }
4139
4669
  const rel = flagStr(args.flags, "out") ?? RULE_TARGETS[host].path;
4140
- const abs = resolve3(deps.cwd, rel);
4670
+ const abs = resolve4(deps.cwd, rel);
4141
4671
  const write = deps.writeFile ?? defaultWrite;
4142
4672
  try {
4143
4673
  write(abs, content);
@@ -4203,7 +4733,7 @@ function diagnosticsCommand(args, deps) {
4203
4733
  }
4204
4734
 
4205
4735
  // src/commands/explain.ts
4206
- import { join as join8 } from "node:path";
4736
+ import { join as join13 } from "node:path";
4207
4737
  function explainCommand(args, deps) {
4208
4738
  const serverName = args.positionals[0];
4209
4739
  if (!serverName) {
@@ -4213,7 +4743,7 @@ function explainCommand(args, deps) {
4213
4743
  exitCode: EXIT.USAGE
4214
4744
  };
4215
4745
  }
4216
- const summary = readCache(join8(deps.cwd, ".calllint", "last-scan.json"));
4746
+ const summary = readCache(join13(deps.cwd, ".calllint", "last-scan.json"));
4217
4747
  if (!summary) {
4218
4748
  return {
4219
4749
  stdout: "",
@@ -4235,13 +4765,13 @@ function explainCommand(args, deps) {
4235
4765
  }
4236
4766
 
4237
4767
  // src/commands/policy.ts
4238
- import { existsSync as existsSync6, writeFileSync as writeFileSync5 } from "node:fs";
4239
- import { join as join9 } from "node:path";
4768
+ import { existsSync as existsSync9, writeFileSync as writeFileSync5 } from "node:fs";
4769
+ import { join as join14 } from "node:path";
4240
4770
  function policyCommand(args, deps) {
4241
4771
  const sub = args.positionals[0];
4242
4772
  if (sub === "init") {
4243
- const path = join9(deps.cwd, "calllint.policy.json");
4244
- if (existsSync6(path) && !flagBool(args.flags, "force")) {
4773
+ const path = join14(deps.cwd, "calllint.policy.json");
4774
+ if (existsSync9(path) && !flagBool(args.flags, "force")) {
4245
4775
  return {
4246
4776
  stdout: "",
4247
4777
  stderr: `${path} already exists. Use --force to overwrite.`,
@@ -4271,7 +4801,7 @@ function policyCommand(args, deps) {
4271
4801
  }
4272
4802
 
4273
4803
  // src/commands/verify.ts
4274
- import { join as join10 } from "node:path";
4804
+ import { join as join15 } from "node:path";
4275
4805
  function loadPolicy(args) {
4276
4806
  try {
4277
4807
  return loadPolicyOrDefault(flagStr(args.flags, "policy"));
@@ -4280,7 +4810,7 @@ function loadPolicy(args) {
4280
4810
  }
4281
4811
  }
4282
4812
  function baselinePathFor(args, cwd) {
4283
- return flagStr(args.flags, "baseline") ?? join10(cwd, ".calllint", "baseline.json");
4813
+ return flagStr(args.flags, "baseline") ?? join15(cwd, ".calllint", "baseline.json");
4284
4814
  }
4285
4815
  function baselineCommand(args, deps) {
4286
4816
  const policy = loadPolicy(args);
@@ -4406,8 +4936,8 @@ function approveCommand(args, deps) {
4406
4936
  }
4407
4937
 
4408
4938
  // src/commands/receipt.ts
4409
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
4410
- import { resolve as resolve4 } from "node:path";
4939
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync6 } from "node:fs";
4940
+ import { resolve as resolve5 } from "node:path";
4411
4941
 
4412
4942
  // ../../packages/signature/src/ed25519.ts
4413
4943
  import { createHash as createHash2 } from "node:crypto";
@@ -5016,7 +5546,7 @@ function receiptVerify(args, deps) {
5016
5546
  }
5017
5547
  let raw;
5018
5548
  try {
5019
- raw = readFileSync8(resolve4(deps.cwd, file), "utf8");
5549
+ raw = readFileSync13(resolve5(deps.cwd, file), "utf8");
5020
5550
  } catch (e) {
5021
5551
  return {
5022
5552
  stdout: "",
@@ -5069,6 +5599,43 @@ function receiptVerify(args, deps) {
5069
5599
  exitCode: EXIT.OK
5070
5600
  };
5071
5601
  }
5602
+ if (flagBool(args.flags, "verbose")) {
5603
+ const stdout2 = [
5604
+ "\u2713 CallLint receipt: valid",
5605
+ "",
5606
+ "Receipt Information:",
5607
+ ` ID: ${receipt.receipt_id}`,
5608
+ ` Schema: ${receipt.schema_version}`,
5609
+ ` Created: ${receipt.created_at}`,
5610
+ "",
5611
+ "Tool:",
5612
+ ` Name: ${receipt.tool.name}`,
5613
+ ` Version: ${receipt.tool.version}`,
5614
+ "",
5615
+ "Subject:",
5616
+ ` Type: ${receipt.subject.type}`,
5617
+ ` Target: ${receipt.subject.target}`,
5618
+ "",
5619
+ `Verdict: ${receipt.verdict}`,
5620
+ "",
5621
+ "Hashes:",
5622
+ ` Input: ${receipt.hashes.input_hash}`,
5623
+ ` Policy: ${receipt.hashes.policy_hash}`,
5624
+ ` Report: ${receipt.hashes.report_hash}`,
5625
+ ` Ruleset: ${receipt.hashes.ruleset_hash}`,
5626
+ "",
5627
+ "Risk Summary:",
5628
+ ` BLOCK: ${receipt.risk_counts.block ?? 0}`,
5629
+ ` REVIEW: ${receipt.risk_counts.review ?? 0}`,
5630
+ ` UNKNOWN: ${receipt.risk_counts.unknown ?? 0}`,
5631
+ ` SAFE: ${receipt.risk_counts.safe ?? 0}`,
5632
+ "",
5633
+ `Network: ${receipt.trust_boundaries.network_used ? "Used" : "Not used"}`,
5634
+ "",
5635
+ "Signature: unsigned local receipt"
5636
+ ].join("\n");
5637
+ return { stdout: stdout2, exitCode: EXIT.OK };
5638
+ }
5072
5639
  const stdout = [
5073
5640
  "\u2713 CallLint receipt: valid",
5074
5641
  ` Receipt ID: ${receipt.receipt_id}`,
@@ -5098,7 +5665,7 @@ function verifySignature(receipt, args, deps) {
5098
5665
  }
5099
5666
  let publicKey;
5100
5667
  try {
5101
- const keyJson = JSON.parse(readFileSync8(resolve4(deps.cwd, publicKeyFlag), "utf8"));
5668
+ const keyJson = JSON.parse(readFileSync13(resolve5(deps.cwd, publicKeyFlag), "utf8"));
5102
5669
  publicKey = keyJson.public_key;
5103
5670
  if (typeof publicKey !== "string") {
5104
5671
  throw new Error("key file has no string 'public_key' field");
@@ -5143,6 +5710,47 @@ function verifySignature(receipt, args, deps) {
5143
5710
  ];
5144
5711
  return { stdout: "", stderr: lines.join("\n"), exitCode: 1 };
5145
5712
  }
5713
+ if (flagBool(args.flags, "verbose")) {
5714
+ const stdout2 = [
5715
+ "\u2713 CallLint receipt: valid",
5716
+ "",
5717
+ "Receipt Information:",
5718
+ ` ID: ${receipt.receipt_id}`,
5719
+ ` Schema: ${receipt.schema_version}`,
5720
+ ` Created: ${receipt.created_at}`,
5721
+ "",
5722
+ "Tool:",
5723
+ ` Name: ${receipt.tool.name}`,
5724
+ ` Version: ${receipt.tool.version}`,
5725
+ "",
5726
+ "Subject:",
5727
+ ` Type: ${receipt.subject.type}`,
5728
+ ` Target: ${receipt.subject.target}`,
5729
+ "",
5730
+ `Verdict: ${receipt.verdict}`,
5731
+ "",
5732
+ "Hashes:",
5733
+ ` Input: ${receipt.hashes.input_hash}`,
5734
+ ` Policy: ${receipt.hashes.policy_hash}`,
5735
+ ` Report: ${receipt.hashes.report_hash}`,
5736
+ ` Ruleset: ${receipt.hashes.ruleset_hash}`,
5737
+ "",
5738
+ "Risk Summary:",
5739
+ ` BLOCK: ${receipt.risk_counts.block ?? 0}`,
5740
+ ` REVIEW: ${receipt.risk_counts.review ?? 0}`,
5741
+ ` UNKNOWN: ${receipt.risk_counts.unknown ?? 0}`,
5742
+ ` SAFE: ${receipt.risk_counts.safe ?? 0}`,
5743
+ "",
5744
+ `Network: ${receipt.trust_boundaries.network_used ? "Used" : "Not used"}`,
5745
+ "",
5746
+ "Signature:",
5747
+ ` Status: valid`,
5748
+ ` Key ID: ${sig.key_id}`,
5749
+ ` Algorithm: ${sig.algorithm}`,
5750
+ ` Signed at: ${sig.signed_at}`
5751
+ ].join("\n");
5752
+ return { stdout: stdout2, exitCode: EXIT.OK };
5753
+ }
5146
5754
  const stdout = [
5147
5755
  "\u2713 CallLint receipt: valid",
5148
5756
  ` Receipt ID: ${receipt.receipt_id}`,
@@ -5171,7 +5779,7 @@ function receiptSign(args, deps) {
5171
5779
  }
5172
5780
  let unsignedReceipt;
5173
5781
  try {
5174
- unsignedReceipt = JSON.parse(readFileSync8(resolve4(deps.cwd, receiptFile), "utf8"));
5782
+ unsignedReceipt = JSON.parse(readFileSync13(resolve5(deps.cwd, receiptFile), "utf8"));
5175
5783
  } catch (e) {
5176
5784
  return {
5177
5785
  stdout: "",
@@ -5188,7 +5796,7 @@ function receiptSign(args, deps) {
5188
5796
  }
5189
5797
  let keypair;
5190
5798
  try {
5191
- const keyJson = JSON.parse(readFileSync8(resolve4(deps.cwd, keyFile), "utf8"));
5799
+ const keyJson = JSON.parse(readFileSync13(resolve5(deps.cwd, keyFile), "utf8"));
5192
5800
  keypair = importKeypair(keyJson);
5193
5801
  } catch (e) {
5194
5802
  return {
@@ -5200,7 +5808,7 @@ function receiptSign(args, deps) {
5200
5808
  try {
5201
5809
  const signature = signReceipt(unsignedReceipt, keypair);
5202
5810
  const signedReceipt = { ...unsignedReceipt, signature };
5203
- writeFileSync6(resolve4(deps.cwd, outFile), JSON.stringify(signedReceipt, null, 2) + "\n", "utf8");
5811
+ writeFileSync6(resolve5(deps.cwd, outFile), JSON.stringify(signedReceipt, null, 2) + "\n", "utf8");
5204
5812
  const stdout = [
5205
5813
  "\u2713 Receipt signed successfully",
5206
5814
  ` Key ID: ${signature.key_id}`,
@@ -5235,7 +5843,7 @@ function receiptKeygen(args, deps) {
5235
5843
  try {
5236
5844
  const keypair = generateKeypair(keyId);
5237
5845
  const exported = exportKeypair(keypair);
5238
- writeFileSync6(resolve4(deps.cwd, outFile), JSON.stringify(exported, null, 2) + "\n", "utf8");
5846
+ writeFileSync6(resolve5(deps.cwd, outFile), JSON.stringify(exported, null, 2) + "\n", "utf8");
5239
5847
  const stdout = [
5240
5848
  "\u2713 Keypair generated successfully",
5241
5849
  ` Key ID: ${keyId}`,
@@ -5255,8 +5863,8 @@ function receiptKeygen(args, deps) {
5255
5863
  }
5256
5864
 
5257
5865
  // src/commands/action.ts
5258
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "node:fs";
5259
- import { resolve as resolve5 } from "node:path";
5866
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "node:fs";
5867
+ import { resolve as resolve6 } from "node:path";
5260
5868
 
5261
5869
  // ../../packages/action-analyzer/src/types.ts
5262
5870
  var KIND_RISK_PROFILES = {
@@ -5730,8 +6338,8 @@ function actionInspect(args, deps) {
5730
6338
  };
5731
6339
  }
5732
6340
  try {
5733
- const absolutePath = resolve5(deps.cwd, actionFile);
5734
- const content = readFileSync9(absolutePath, "utf-8");
6341
+ const absolutePath = resolve6(deps.cwd, actionFile);
6342
+ const content = readFileSync14(absolutePath, "utf-8");
5735
6343
  const descriptor = JSON.parse(content);
5736
6344
  if (descriptor.schema_version !== "calllint.action.v0") {
5737
6345
  return {
@@ -5742,7 +6350,7 @@ Expected: calllint.action.v0`,
5742
6350
  };
5743
6351
  }
5744
6352
  const policyPath = args.flags["policy"];
5745
- const policy = loadPolicyOrDefault(policyPath ? resolve5(deps.cwd, policyPath) : void 0);
6353
+ const policy = loadPolicyOrDefault(policyPath ? resolve6(deps.cwd, policyPath) : void 0);
5746
6354
  const findings = analyzeAction(descriptor);
5747
6355
  const verdict = computeActionVerdict(findings, policy);
5748
6356
  const actionReport = {
@@ -5869,7 +6477,7 @@ function writeActionReceipt(descriptor, rawContent, actionReport, policy, args,
5869
6477
  },
5870
6478
  deps.generatedAt || (/* @__PURE__ */ new Date()).toISOString()
5871
6479
  );
5872
- const outPath = resolve5(
6480
+ const outPath = resolve6(
5873
6481
  deps.cwd,
5874
6482
  args.flags["receipt-out"] ?? "calllint-action-receipt.json"
5875
6483
  );
@@ -5882,8 +6490,8 @@ function writeActionReceipt(descriptor, rawContent, actionReport, policy, args,
5882
6490
  }
5883
6491
 
5884
6492
  // src/commands/inbox.ts
5885
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "node:fs";
5886
- import { resolve as resolve6 } from "node:path";
6493
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "node:fs";
6494
+ import { resolve as resolve7 } from "node:path";
5887
6495
  function inboxCommand(args, deps) {
5888
6496
  const subcommand = args.positionals[0];
5889
6497
  if (!subcommand || subcommand === "help") {
@@ -5913,8 +6521,8 @@ function inboxInspect(args, deps) {
5913
6521
  };
5914
6522
  }
5915
6523
  try {
5916
- const absolutePath = resolve6(deps.cwd, eventFile);
5917
- const content = readFileSync10(absolutePath, "utf-8");
6524
+ const absolutePath = resolve7(deps.cwd, eventFile);
6525
+ const content = readFileSync15(absolutePath, "utf-8");
5918
6526
  const event = JSON.parse(content);
5919
6527
  if (event.schema_version !== "calllint.agent-inbox-event.v0") {
5920
6528
  return {
@@ -5932,7 +6540,7 @@ Expected: calllint.agent-inbox-event.v0`,
5932
6540
  };
5933
6541
  }
5934
6542
  const policyPath = args.flags["policy"];
5935
- const policy = loadPolicyOrDefault(policyPath ? resolve6(deps.cwd, policyPath) : void 0);
6543
+ const policy = loadPolicyOrDefault(policyPath ? resolve7(deps.cwd, policyPath) : void 0);
5936
6544
  const actionCandidate = event.action_candidate;
5937
6545
  let verdict;
5938
6546
  let findings;
@@ -6080,7 +6688,7 @@ function writeInboxReceipt(event, rawContent, verdict, findings, policy, args, d
6080
6688
  deps.generatedAt || (/* @__PURE__ */ new Date()).toISOString()
6081
6689
  );
6082
6690
  const receiptOutPath = args.flags["receipt-out"] || "calllint-inbox-receipt.json";
6083
- const absoluteReceiptPath = resolve6(deps.cwd, receiptOutPath);
6691
+ const absoluteReceiptPath = resolve7(deps.cwd, receiptOutPath);
6084
6692
  writeFileSync8(absoluteReceiptPath, JSON.stringify(receipt, null, 2) + "\n", "utf-8");
6085
6693
  return null;
6086
6694
  } catch (error) {
@@ -6135,6 +6743,82 @@ SEE ALSO
6135
6743
  `;
6136
6744
  }
6137
6745
 
6746
+ // src/commands/inventory.ts
6747
+ function inventoryCommand(args, deps) {
6748
+ try {
6749
+ const result = discoverConfigs({ cwd: deps.cwd });
6750
+ if (args.flags.json) {
6751
+ return {
6752
+ exitCode: 0,
6753
+ stdout: JSON.stringify(result, null, 2) + "\n",
6754
+ stderr: ""
6755
+ };
6756
+ }
6757
+ const lines = [];
6758
+ if (result.discovered.length === 0) {
6759
+ lines.push("No agent configs discovered.");
6760
+ lines.push("");
6761
+ lines.push("Searched agents: Cursor, Claude Code, Claude Desktop");
6762
+ lines.push("To scan a specific config: calllint scan --config <path>");
6763
+ } else {
6764
+ lines.push(`Discovered ${result.discovered.length} agent config(s):`);
6765
+ lines.push("");
6766
+ const byAgent = /* @__PURE__ */ new Map();
6767
+ for (const config of result.discovered) {
6768
+ const existing = byAgent.get(config.agentType) || [];
6769
+ existing.push(config);
6770
+ byAgent.set(config.agentType, existing);
6771
+ }
6772
+ const sortedAgents = Array.from(byAgent.keys()).sort((a, b) => {
6773
+ const priorityA = result.discovered.find((c) => c.agentType === a)?.priority || "P3";
6774
+ const priorityB = result.discovered.find((c) => c.agentType === b)?.priority || "P3";
6775
+ return priorityA.localeCompare(priorityB);
6776
+ });
6777
+ for (const agentType of sortedAgents) {
6778
+ const configs = byAgent.get(agentType);
6779
+ const priority = configs[0].priority;
6780
+ const agentLabel = formatAgentType(agentType);
6781
+ lines.push(`${agentLabel} (${priority}):`);
6782
+ for (const config of configs) {
6783
+ lines.push(` ${config.configPath}`);
6784
+ }
6785
+ lines.push("");
6786
+ }
6787
+ lines.push(`To scan all: calllint scan --auto`);
6788
+ lines.push(`To scan one: calllint scan --agent ${result.discovered[0].agentType}`);
6789
+ }
6790
+ return {
6791
+ exitCode: 0,
6792
+ stdout: lines.join("\n") + "\n",
6793
+ stderr: ""
6794
+ };
6795
+ } catch (error) {
6796
+ const message = error instanceof Error ? error.message : String(error);
6797
+ return {
6798
+ exitCode: 1,
6799
+ stdout: "",
6800
+ stderr: `Error discovering configs: ${message}
6801
+ `
6802
+ };
6803
+ }
6804
+ }
6805
+ function formatAgentType(agentType) {
6806
+ switch (agentType) {
6807
+ case "cursor":
6808
+ return "Cursor";
6809
+ case "claude-code":
6810
+ return "Claude Code";
6811
+ case "claude-desktop":
6812
+ return "Claude Desktop";
6813
+ case "vscode":
6814
+ return "VS Code";
6815
+ case "windsurf":
6816
+ return "Windsurf";
6817
+ default:
6818
+ return agentType;
6819
+ }
6820
+ }
6821
+
6138
6822
  // src/run.ts
6139
6823
  function run(argv, deps) {
6140
6824
  const args = parseArgs(argv);
@@ -6207,6 +6891,8 @@ function run(argv, deps) {
6207
6891
  return actionCommand(args, { cwd: deps.cwd, toolVersion: deps.toolVersion, generatedAt: deps.generatedAt });
6208
6892
  case "inbox":
6209
6893
  return inboxCommand(args, { cwd: deps.cwd, toolVersion: deps.toolVersion, generatedAt: deps.generatedAt });
6894
+ case "inventory":
6895
+ return inventoryCommand(args, { cwd: deps.cwd });
6210
6896
  case "gen-rule":
6211
6897
  return genRuleCommand(args, { cwd: deps.cwd });
6212
6898
  case "policy":
@@ -6480,13 +7166,13 @@ async function breathe(argv, deps = {}) {
6480
7166
  }
6481
7167
 
6482
7168
  // src/version.ts
6483
- import { readFileSync as readFileSync11 } from "node:fs";
7169
+ import { readFileSync as readFileSync16 } from "node:fs";
6484
7170
  import { fileURLToPath } from "node:url";
6485
- import { dirname as dirname4, join as join11 } from "node:path";
7171
+ import { dirname as dirname4, join as join16 } from "node:path";
6486
7172
  function resolveToolVersion() {
6487
7173
  try {
6488
- const pkgPath = join11(dirname4(fileURLToPath(import.meta.url)), "..", "package.json");
6489
- const pkg = JSON.parse(readFileSync11(pkgPath, "utf8"));
7174
+ const pkgPath = join16(dirname4(fileURLToPath(import.meta.url)), "..", "package.json");
7175
+ const pkg = JSON.parse(readFileSync16(pkgPath, "utf8"));
6490
7176
  return typeof pkg.version === "string" && pkg.version.length > 0 ? pkg.version : "unknown";
6491
7177
  } catch {
6492
7178
  return "unknown";
@@ -6496,7 +7182,7 @@ function resolveToolVersion() {
6496
7182
  // src/index.ts
6497
7183
  function readStdin() {
6498
7184
  try {
6499
- return readFileSync12(0, "utf8");
7185
+ return readFileSync17(0, "utf8");
6500
7186
  } catch {
6501
7187
  return "";
6502
7188
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "calllint",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Evidence-backed security verdicts for MCP servers and agent tools. Lint agent tool-call risk before tools run — SAFE / REVIEW / BLOCK / UNKNOWN, with evidence. Never executes the server it judges.",
5
5
  "keywords": [
6
6
  "mcp",
@@ -51,6 +51,7 @@
51
51
  "devDependencies": {
52
52
  "@calllint/action-analyzer": "workspace:*",
53
53
  "@calllint/core": "workspace:*",
54
+ "@calllint/discovery": "workspace:*",
54
55
  "@calllint/fixtures": "workspace:*",
55
56
  "@calllint/online": "workspace:*",
56
57
  "@calllint/policy": "workspace:*",