calllint 0.9.0 → 0.9.2

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 +592 -4
  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 readFileSync10 } from "node:fs";
4
+ import { readFileSync as readFileSync11 } from "node:fs";
5
5
  import { execFileSync } from "node:child_process";
6
6
 
7
7
  // src/args.ts
@@ -4472,6 +4472,592 @@ function receiptCommand(args, deps) {
4472
4472
  return { stdout, exitCode: EXIT.OK };
4473
4473
  }
4474
4474
 
4475
+ // src/commands/action.ts
4476
+ import { readFileSync as readFileSync9 } from "node:fs";
4477
+ import { resolve as resolve5 } from "node:path";
4478
+
4479
+ // ../../packages/action-analyzer/src/types.ts
4480
+ var KIND_RISK_PROFILES = {
4481
+ "email.reply": ["ACTION", "SECRETS", "PROMPT"],
4482
+ "email.forward": ["ACTION", "SECRETS", "FILES", "PROMPT"],
4483
+ "message.post": ["ACTION", "SECRETS"],
4484
+ "a2a.delegate": ["ACTION", "NETWORK", "PROMPT"],
4485
+ "payment.authorize": ["MONEY", "SECRETS"],
4486
+ "account.register": ["ACTION", "SECRETS", "SUPPLY"],
4487
+ "github.write": ["ACTION", "SUPPLY"],
4488
+ "npm.publish": ["ACTION", "SUPPLY", "MONEY"],
4489
+ "cloud.modify": ["ACTION", "EXEC", "MONEY"]
4490
+ };
4491
+
4492
+ // ../../packages/action-analyzer/src/analyzeAction.ts
4493
+ function analyzeAction(descriptor) {
4494
+ const findings = [];
4495
+ if (descriptor.schema_version !== "calllint.action.v0") {
4496
+ findings.push({
4497
+ id: "action.unknown-schema-version",
4498
+ title: "Unknown action schema version",
4499
+ severity: "medium",
4500
+ blocker: false,
4501
+ symbol: "ACTION",
4502
+ riskClass: "S3",
4503
+ mode: "OBSERVED",
4504
+ confidence: "high",
4505
+ detectionMethod: "config-analysis",
4506
+ evidence: [{
4507
+ type: "config",
4508
+ path: "schema_version",
4509
+ value: descriptor.schema_version
4510
+ }],
4511
+ impact: `Unknown action schema version: ${descriptor.schema_version}`,
4512
+ fix: "Use calllint.action.v0 schema version"
4513
+ });
4514
+ return findings;
4515
+ }
4516
+ const riskProfile = KIND_RISK_PROFILES[descriptor.kind];
4517
+ if (!riskProfile) {
4518
+ findings.push({
4519
+ id: "action.unknown-kind",
4520
+ title: "Unknown action kind",
4521
+ severity: "medium",
4522
+ blocker: false,
4523
+ symbol: "ACTION",
4524
+ riskClass: "S3",
4525
+ mode: "OBSERVED",
4526
+ confidence: "high",
4527
+ detectionMethod: "config-analysis",
4528
+ evidence: [{
4529
+ type: "config",
4530
+ path: "kind",
4531
+ value: descriptor.kind
4532
+ }],
4533
+ impact: `Unknown action kind: ${descriptor.kind}`,
4534
+ fix: "Use one of the 9 defined action kinds (email.reply, email.forward, message.post, a2a.delegate, payment.authorize, account.register, github.write, npm.publish, cloud.modify)"
4535
+ });
4536
+ return findings;
4537
+ }
4538
+ switch (descriptor.kind) {
4539
+ case "email.reply":
4540
+ case "email.forward":
4541
+ findings.push(...analyzeEmail(descriptor));
4542
+ break;
4543
+ case "message.post":
4544
+ findings.push(...analyzeMessaging(descriptor));
4545
+ break;
4546
+ case "a2a.delegate":
4547
+ findings.push(...analyzeDelegate(descriptor));
4548
+ break;
4549
+ case "payment.authorize":
4550
+ findings.push(...analyzePayment(descriptor));
4551
+ break;
4552
+ case "account.register":
4553
+ findings.push(...analyzeRegistration(descriptor));
4554
+ break;
4555
+ case "github.write":
4556
+ findings.push(...analyzeGitHub(descriptor));
4557
+ break;
4558
+ case "npm.publish":
4559
+ findings.push(...analyzeNpmPublish(descriptor));
4560
+ break;
4561
+ case "cloud.modify":
4562
+ findings.push(...analyzeCloud(descriptor));
4563
+ break;
4564
+ }
4565
+ findings.push(...analyzeSecretShapedHeaders(descriptor));
4566
+ return findings;
4567
+ }
4568
+ function analyzeEmail(descriptor) {
4569
+ const findings = [];
4570
+ const metadata = descriptor.metadata || {};
4571
+ const hasAttachments = descriptor.parameters.has_attachments;
4572
+ const attachmentHashes = metadata.attachment_hashes || [];
4573
+ if (hasAttachments && attachmentHashes.length === 0) {
4574
+ findings.push({
4575
+ id: "action.unverified-attachment",
4576
+ title: "Unverified email attachments",
4577
+ severity: "high",
4578
+ blocker: false,
4579
+ symbol: "FILES",
4580
+ riskClass: "S2",
4581
+ mode: "OBSERVED",
4582
+ confidence: "high",
4583
+ detectionMethod: "config-analysis",
4584
+ evidence: [{
4585
+ type: "config",
4586
+ path: "parameters.has_attachments",
4587
+ value: String(hasAttachments)
4588
+ }],
4589
+ impact: "Email action has attachments but no attachment_hashes provided",
4590
+ fix: "Provide SHA-256 hashes for all attachments in metadata.attachment_hashes"
4591
+ });
4592
+ }
4593
+ return findings;
4594
+ }
4595
+ function analyzeMessaging(descriptor) {
4596
+ const findings = [];
4597
+ return findings;
4598
+ }
4599
+ function analyzeDelegate(descriptor) {
4600
+ const findings = [];
4601
+ const metadata = descriptor.metadata || {};
4602
+ const delegateTarget = metadata.delegate_target;
4603
+ if (!delegateTarget) {
4604
+ findings.push({
4605
+ id: "action.missing-delegate-target",
4606
+ title: "Missing delegate target",
4607
+ severity: "high",
4608
+ blocker: false,
4609
+ symbol: "NETWORK",
4610
+ riskClass: "S3",
4611
+ mode: "OBSERVED",
4612
+ confidence: "high",
4613
+ detectionMethod: "config-analysis",
4614
+ evidence: [{
4615
+ type: "config",
4616
+ path: "metadata.delegate_target",
4617
+ value: "null"
4618
+ }],
4619
+ impact: "Agent delegation missing delegate_target in metadata",
4620
+ fix: "Specify the delegate target (agent ID, API endpoint, or service name)"
4621
+ });
4622
+ } else if (typeof delegateTarget === "string" && delegateTarget.startsWith("http://")) {
4623
+ findings.push({
4624
+ id: "action.insecure-delegate-target",
4625
+ title: "Insecure delegate target",
4626
+ severity: "medium",
4627
+ blocker: false,
4628
+ symbol: "NETWORK",
4629
+ riskClass: "S3",
4630
+ mode: "OBSERVED",
4631
+ confidence: "high",
4632
+ detectionMethod: "config-analysis",
4633
+ evidence: [{
4634
+ type: "config",
4635
+ path: "metadata.delegate_target",
4636
+ value: delegateTarget
4637
+ }],
4638
+ impact: "Delegate target uses insecure HTTP",
4639
+ fix: "Use HTTPS for delegate targets"
4640
+ });
4641
+ }
4642
+ return findings;
4643
+ }
4644
+ function analyzePayment(descriptor) {
4645
+ const findings = [];
4646
+ const metadata = descriptor.metadata || {};
4647
+ const amount = metadata.amount;
4648
+ if (typeof amount === "number" && amount > 0) {
4649
+ findings.push({
4650
+ id: "action.financial-observed",
4651
+ title: "Financial transaction observed",
4652
+ severity: "high",
4653
+ blocker: false,
4654
+ symbol: "MONEY",
4655
+ riskClass: "S5",
4656
+ mode: "OBSERVED",
4657
+ confidence: "high",
4658
+ detectionMethod: "config-analysis",
4659
+ evidence: [{
4660
+ type: "config",
4661
+ path: "metadata.amount",
4662
+ value: String(amount)
4663
+ }],
4664
+ impact: `Payment authorization for ${metadata.currency || "unknown currency"} ${amount}`,
4665
+ fix: "Verify payment amount and recipient before authorizing"
4666
+ });
4667
+ }
4668
+ return findings;
4669
+ }
4670
+ function analyzeRegistration(descriptor) {
4671
+ const findings = [];
4672
+ const metadata = descriptor.metadata || {};
4673
+ const serviceVerified = metadata.service_verified;
4674
+ if (serviceVerified === false) {
4675
+ findings.push({
4676
+ id: "action.unverified-service",
4677
+ title: "Unverified service registration",
4678
+ severity: "high",
4679
+ blocker: false,
4680
+ symbol: "SUPPLY",
4681
+ riskClass: "S3",
4682
+ mode: "OBSERVED",
4683
+ confidence: "high",
4684
+ detectionMethod: "config-analysis",
4685
+ evidence: [{
4686
+ type: "config",
4687
+ path: "metadata.service_verified",
4688
+ value: "false"
4689
+ }],
4690
+ impact: "Account registration on unverified service",
4691
+ fix: "Verify the service domain and reputation before registering"
4692
+ });
4693
+ }
4694
+ const scopes = metadata.oauth_scopes || [];
4695
+ const dangerousScopes = ["admin", "delete_account", "financial_data", "admin:org"];
4696
+ const foundDangerous = scopes.filter(
4697
+ (s) => dangerousScopes.some((d) => s.toLowerCase().includes(d.toLowerCase()))
4698
+ );
4699
+ if (foundDangerous.length > 0) {
4700
+ findings.push({
4701
+ id: "action.excessive-oauth-scopes",
4702
+ title: "Excessive OAuth scopes requested",
4703
+ severity: "medium",
4704
+ blocker: false,
4705
+ symbol: "SECRETS",
4706
+ riskClass: "S2",
4707
+ mode: "OBSERVED",
4708
+ confidence: "high",
4709
+ detectionMethod: "config-analysis",
4710
+ evidence: [{
4711
+ type: "config",
4712
+ path: "metadata.oauth_scopes",
4713
+ value: foundDangerous.join(", ")
4714
+ }],
4715
+ impact: `Registration requests sensitive scopes: ${foundDangerous.join(", ")}`,
4716
+ fix: "Request only the minimum necessary OAuth scopes"
4717
+ });
4718
+ }
4719
+ return findings;
4720
+ }
4721
+ function analyzeGitHub(descriptor) {
4722
+ const findings = [];
4723
+ const metadata = descriptor.metadata || {};
4724
+ const repoVerified = metadata.repository_verified;
4725
+ if (repoVerified === false) {
4726
+ findings.push({
4727
+ id: "action.unverified-repository",
4728
+ title: "Unverified repository target",
4729
+ severity: "medium",
4730
+ blocker: false,
4731
+ symbol: "SUPPLY",
4732
+ riskClass: "S3",
4733
+ mode: "OBSERVED",
4734
+ confidence: "high",
4735
+ detectionMethod: "config-analysis",
4736
+ evidence: [{
4737
+ type: "config",
4738
+ path: "metadata.repository_verified",
4739
+ value: "false"
4740
+ }],
4741
+ impact: "GitHub write operation to unverified repository",
4742
+ fix: "Verify repository ownership before writing"
4743
+ });
4744
+ }
4745
+ const scopes = metadata.oauth_scopes || [];
4746
+ const dangerousScopes = ["delete_repo", "admin:org", "admin:repo_hook"];
4747
+ const foundDangerous = scopes.filter(
4748
+ (s) => dangerousScopes.some((d) => s.toLowerCase().includes(d.toLowerCase()))
4749
+ );
4750
+ if (foundDangerous.length > 0) {
4751
+ findings.push({
4752
+ id: "action.excessive-github-scopes",
4753
+ title: "Excessive GitHub OAuth scopes",
4754
+ severity: "medium",
4755
+ blocker: false,
4756
+ symbol: "SECRETS",
4757
+ riskClass: "S3",
4758
+ mode: "OBSERVED",
4759
+ confidence: "high",
4760
+ detectionMethod: "config-analysis",
4761
+ evidence: [{
4762
+ type: "config",
4763
+ path: "metadata.oauth_scopes",
4764
+ value: foundDangerous.join(", ")
4765
+ }],
4766
+ impact: `GitHub operation requests dangerous scopes: ${foundDangerous.join(", ")}`,
4767
+ fix: "Request only necessary OAuth scopes for the operation"
4768
+ });
4769
+ }
4770
+ const externalLinks = metadata.external_links || [];
4771
+ if (externalLinks.length > 0) {
4772
+ findings.push({
4773
+ id: "action.external-links",
4774
+ title: "External links in GitHub content",
4775
+ severity: "low",
4776
+ blocker: false,
4777
+ symbol: "NETWORK",
4778
+ riskClass: "S2",
4779
+ mode: "OBSERVED",
4780
+ confidence: "medium",
4781
+ detectionMethod: "config-analysis",
4782
+ evidence: [{
4783
+ type: "config",
4784
+ path: "metadata.external_links",
4785
+ value: externalLinks.join(", ")
4786
+ }],
4787
+ impact: `Issue/PR contains external links: ${externalLinks.length} found`,
4788
+ fix: "Review external links for safety"
4789
+ });
4790
+ }
4791
+ return findings;
4792
+ }
4793
+ function analyzeNpmPublish(descriptor) {
4794
+ const findings = [];
4795
+ const metadata = descriptor.metadata || {};
4796
+ const params = descriptor.parameters || {};
4797
+ const similarTo = metadata.similar_to_popular || [];
4798
+ if (similarTo.length > 0) {
4799
+ findings.push({
4800
+ id: "supply.name-squatting",
4801
+ title: "Potential name squatting",
4802
+ severity: "high",
4803
+ blocker: false,
4804
+ symbol: "SUPPLY",
4805
+ riskClass: "S3",
4806
+ mode: "INFERRED",
4807
+ confidence: "medium",
4808
+ detectionMethod: "config-analysis",
4809
+ evidence: [{
4810
+ type: "config",
4811
+ path: "metadata.similar_to_popular",
4812
+ value: similarTo.join(", ")
4813
+ }],
4814
+ impact: `Package name similar to popular packages: ${similarTo.join(", ")}`,
4815
+ fix: "Verify this is not a typosquatting attempt"
4816
+ });
4817
+ }
4818
+ const versionPinned = metadata.version_pinned;
4819
+ const versionRange = metadata.version_range;
4820
+ if (versionPinned === false || versionRange === true) {
4821
+ findings.push({
4822
+ id: "supply.version-float",
4823
+ title: "Version not pinned",
4824
+ severity: "medium",
4825
+ blocker: false,
4826
+ symbol: "SUPPLY",
4827
+ riskClass: "S2",
4828
+ mode: "OBSERVED",
4829
+ confidence: "high",
4830
+ detectionMethod: "config-analysis",
4831
+ evidence: [{
4832
+ type: "config",
4833
+ path: "parameters.version",
4834
+ value: String(params.version || "unknown")
4835
+ }],
4836
+ impact: "Publishing with floating version range instead of pinned version",
4837
+ fix: 'Use pinned version (e.g., "1.2.3" not "^1.2.3")'
4838
+ });
4839
+ }
4840
+ return findings;
4841
+ }
4842
+ function analyzeCloud(descriptor) {
4843
+ const findings = [];
4844
+ const metadata = descriptor.metadata || {};
4845
+ const monthlyCost = metadata.estimated_monthly_cost;
4846
+ if (typeof monthlyCost === "number" && monthlyCost > 1e3) {
4847
+ findings.push({
4848
+ id: "action.expensive-cloud-resource",
4849
+ title: "Expensive cloud resource",
4850
+ severity: "high",
4851
+ blocker: false,
4852
+ symbol: "MONEY",
4853
+ riskClass: "S5",
4854
+ mode: "OBSERVED",
4855
+ confidence: "high",
4856
+ detectionMethod: "config-analysis",
4857
+ evidence: [{
4858
+ type: "config",
4859
+ path: "metadata.estimated_monthly_cost",
4860
+ value: String(monthlyCost)
4861
+ }],
4862
+ impact: `Cloud resource estimated at $${monthlyCost}/month`,
4863
+ fix: "Verify cost estimate and budget before creating"
4864
+ });
4865
+ }
4866
+ const opensAllPorts = metadata.opens_all_ports;
4867
+ if (opensAllPorts === true) {
4868
+ findings.push({
4869
+ id: "action.insecure-security-group",
4870
+ title: "Insecure security group configuration",
4871
+ severity: "high",
4872
+ blocker: false,
4873
+ symbol: "NETWORK",
4874
+ riskClass: "S4",
4875
+ mode: "OBSERVED",
4876
+ confidence: "high",
4877
+ detectionMethod: "config-analysis",
4878
+ evidence: [{
4879
+ type: "config",
4880
+ path: "metadata.opens_all_ports",
4881
+ value: "true"
4882
+ }],
4883
+ impact: "Security group opens all ports to the internet (0.0.0.0/0)",
4884
+ fix: "Restrict ports and source IPs to minimum necessary"
4885
+ });
4886
+ }
4887
+ return findings;
4888
+ }
4889
+ function analyzeSecretShapedHeaders(descriptor) {
4890
+ const findings = [];
4891
+ const metadata = descriptor.metadata || {};
4892
+ const headerKeys = metadata.header_keys || [];
4893
+ const secretPatterns = ["authorization", "api-key", "api_key", "token", "secret", "password", "bearer"];
4894
+ for (const key of headerKeys) {
4895
+ const lowerKey = key.toLowerCase();
4896
+ if (secretPatterns.some((pattern) => lowerKey.includes(pattern))) {
4897
+ findings.push({
4898
+ id: "secrets.env-key",
4899
+ title: "Secret-shaped header key",
4900
+ severity: "medium",
4901
+ blocker: false,
4902
+ symbol: "SECRETS",
4903
+ riskClass: "S2",
4904
+ mode: "OBSERVED",
4905
+ confidence: "medium",
4906
+ detectionMethod: "config-analysis",
4907
+ evidence: [{
4908
+ type: "config",
4909
+ path: "metadata.header_keys",
4910
+ value: key
4911
+ }],
4912
+ impact: `Header key resembles a secret: ${key}`,
4913
+ fix: "Verify that secret headers are properly secured and not logged"
4914
+ });
4915
+ break;
4916
+ }
4917
+ }
4918
+ return findings;
4919
+ }
4920
+
4921
+ // src/commands/action.ts
4922
+ function actionCommand(args, deps) {
4923
+ const subcommand = args.positionals[0];
4924
+ if (!subcommand || subcommand === "help") {
4925
+ return {
4926
+ stdout: actionHelp(),
4927
+ stderr: "",
4928
+ exitCode: 0
4929
+ };
4930
+ }
4931
+ if (subcommand === "inspect") {
4932
+ return actionInspect(args, deps);
4933
+ }
4934
+ return {
4935
+ stdout: "",
4936
+ stderr: `Unknown action subcommand: ${subcommand}
4937
+ Run \`calllint action help\`.`,
4938
+ exitCode: 2
4939
+ };
4940
+ }
4941
+ function actionInspect(args, deps) {
4942
+ const actionFile = args.positionals[1];
4943
+ if (!actionFile) {
4944
+ return {
4945
+ stdout: "",
4946
+ stderr: "Error: Missing action file\nUsage: calllint action inspect <file.json>",
4947
+ exitCode: 2
4948
+ };
4949
+ }
4950
+ try {
4951
+ const absolutePath = resolve5(deps.cwd, actionFile);
4952
+ const content = readFileSync9(absolutePath, "utf-8");
4953
+ const descriptor = JSON.parse(content);
4954
+ if (descriptor.schema_version !== "calllint.action.v0") {
4955
+ return {
4956
+ stdout: "",
4957
+ stderr: `Error: Unsupported schema version: ${descriptor.schema_version}
4958
+ Expected: calllint.action.v0`,
4959
+ exitCode: 2
4960
+ };
4961
+ }
4962
+ const policyPath = args.flags["policy"];
4963
+ const policy = loadPolicyOrDefault(policyPath ? resolve5(deps.cwd, policyPath) : void 0);
4964
+ const findings = analyzeAction(descriptor);
4965
+ const verdict = computeActionVerdict(findings, policy);
4966
+ let stdout = "";
4967
+ if (args.flags["json"]) {
4968
+ const report = {
4969
+ schema_version: "calllint.action-report.v0",
4970
+ verdict,
4971
+ findings,
4972
+ target: {
4973
+ type: "action",
4974
+ kind: descriptor.kind,
4975
+ schema_version: descriptor.schema_version
4976
+ },
4977
+ policy_applied: "default",
4978
+ scan_timestamp: (/* @__PURE__ */ new Date()).toISOString()
4979
+ };
4980
+ stdout = JSON.stringify(report, null, 2);
4981
+ } else {
4982
+ const header = `
4983
+ \u{1F50D} Action: ${descriptor.kind}
4984
+ `;
4985
+ const verdictEmoji = verdict === "SAFE" ? "\u2705" : verdict === "BLOCK" ? "\u26D4" : verdict === "REVIEW" ? "\u26A0\uFE0F" : "\u25C7";
4986
+ const verdictLine = `Verdict: ${verdictEmoji} ${verdict}
4987
+ `;
4988
+ const findingsLine = `Findings: ${findings.length}
4989
+ `;
4990
+ let body = verdictLine + findingsLine;
4991
+ if (findings.length > 0) {
4992
+ body += "\n";
4993
+ for (const f of findings) {
4994
+ body += ` \u2022 ${f.id}: ${f.impact}
4995
+ `;
4996
+ }
4997
+ }
4998
+ stdout = header + body;
4999
+ }
5000
+ return {
5001
+ stdout,
5002
+ stderr: "",
5003
+ exitCode: verdict === "SAFE" ? 0 : 1
5004
+ };
5005
+ } catch (error) {
5006
+ const err = error;
5007
+ let stderr = "";
5008
+ if (err.code === "ENOENT") {
5009
+ stderr = `Error: File not found: ${actionFile}`;
5010
+ } else if (error instanceof SyntaxError) {
5011
+ stderr = `Error: Invalid JSON in ${actionFile}
5012
+ ${error.message}`;
5013
+ } else {
5014
+ stderr = `Error: ${err.message}`;
5015
+ }
5016
+ return {
5017
+ stdout: "",
5018
+ stderr,
5019
+ exitCode: 2
5020
+ };
5021
+ }
5022
+ }
5023
+ function actionHelp() {
5024
+ return `calllint action \u2014 Inspect planned external actions
5025
+
5026
+ USAGE
5027
+ calllint action inspect <file.json>
5028
+ calllint action help
5029
+
5030
+ COMMANDS
5031
+ inspect <file> Analyze a calllint.action.v0 descriptor
5032
+ help Show this help
5033
+
5034
+ OPTIONS
5035
+ --json Output JSON report
5036
+ --policy <file> Use custom policy file
5037
+ --no-emoji Disable emoji in output
5038
+
5039
+ EXAMPLE
5040
+ calllint action inspect payment.json
5041
+ calllint action inspect email-reply.json --json
5042
+
5043
+ See: https://calllint.com/docs/action-inspect (ADR 0029)
5044
+ `;
5045
+ }
5046
+ function computeActionVerdict(findings, policy) {
5047
+ if (findings.length === 0) {
5048
+ return "SAFE";
5049
+ }
5050
+ const hasBlocker = findings.some((f) => f.blocker || f.severity === "critical");
5051
+ if (hasBlocker) {
5052
+ return "BLOCK";
5053
+ }
5054
+ const hasHigh = findings.some((f) => f.severity === "high");
5055
+ if (hasHigh) {
5056
+ return "REVIEW";
5057
+ }
5058
+ return "REVIEW";
5059
+ }
5060
+
4475
5061
  // src/run.ts
4476
5062
  function run(argv, deps) {
4477
5063
  const args = parseArgs(argv);
@@ -4540,6 +5126,8 @@ function run(argv, deps) {
4540
5126
  return explainCommand(args, { cwd: deps.cwd });
4541
5127
  case "receipt":
4542
5128
  return receiptCommand(args, { cwd: deps.cwd });
5129
+ case "action":
5130
+ return actionCommand(args, { cwd: deps.cwd });
4543
5131
  case "gen-rule":
4544
5132
  return genRuleCommand(args, { cwd: deps.cwd });
4545
5133
  case "policy":
@@ -4813,13 +5401,13 @@ async function breathe(argv, deps = {}) {
4813
5401
  }
4814
5402
 
4815
5403
  // src/version.ts
4816
- import { readFileSync as readFileSync9 } from "node:fs";
5404
+ import { readFileSync as readFileSync10 } from "node:fs";
4817
5405
  import { fileURLToPath } from "node:url";
4818
5406
  import { dirname as dirname4, join as join11 } from "node:path";
4819
5407
  function resolveToolVersion() {
4820
5408
  try {
4821
5409
  const pkgPath = join11(dirname4(fileURLToPath(import.meta.url)), "..", "package.json");
4822
- const pkg = JSON.parse(readFileSync9(pkgPath, "utf8"));
5410
+ const pkg = JSON.parse(readFileSync10(pkgPath, "utf8"));
4823
5411
  return typeof pkg.version === "string" && pkg.version.length > 0 ? pkg.version : "unknown";
4824
5412
  } catch {
4825
5413
  return "unknown";
@@ -4829,7 +5417,7 @@ function resolveToolVersion() {
4829
5417
  // src/index.ts
4830
5418
  function readStdin() {
4831
5419
  try {
4832
- return readFileSync10(0, "utf8");
5420
+ return readFileSync11(0, "utf8");
4833
5421
  } catch {
4834
5422
  return "";
4835
5423
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "calllint",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
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",
@@ -49,6 +49,7 @@
49
49
  },
50
50
  "dependencies": {},
51
51
  "devDependencies": {
52
+ "@calllint/action-analyzer": "workspace:*",
52
53
  "@calllint/core": "workspace:*",
53
54
  "@calllint/fixtures": "workspace:*",
54
55
  "@calllint/online": "workspace:*",