@chimix/agentscan 0.2.0 → 0.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/README.md +11 -13
- package/dist/cli.js +53 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -88,7 +88,8 @@ Run it against any project. It reads that project, writes nothing, and never
|
|
|
88
88
|
leaves your machine:
|
|
89
89
|
|
|
90
90
|
```bash
|
|
91
|
-
|
|
91
|
+
cd ~/your-project
|
|
92
|
+
npx @chimix/agentscan@latest
|
|
92
93
|
```
|
|
93
94
|
|
|
94
95
|
Works on Node 20.11+ or Bun — the published bin is a single bundled file with
|
|
@@ -175,21 +176,18 @@ Flags for `check`:
|
|
|
175
176
|
## Sample output
|
|
176
177
|
|
|
177
178
|
```text
|
|
178
|
-
agentscan v0.
|
|
179
|
+
agentscan v0.3.0 — touchagency
|
|
179
180
|
|
|
180
|
-
|
|
181
|
+
Scanned: 46 deps · 54 skills · 1 mcp · 2 agents · packageManager=bun
|
|
181
182
|
|
|
182
|
-
WARN hook
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
rule:hook.missing-script
|
|
189
|
-
PreToolUse hook points at a script that does not exist: .claude/hooks/protect-artists-json.js
|
|
190
|
-
evidence: hook PreToolUse @ …/.claude/settings.json · script .claude/hooks/protect-artists-json.js
|
|
183
|
+
WARN rule:hook.missing-script ×6
|
|
184
|
+
PreToolUse hook points at a script that does not exist
|
|
185
|
+
PostToolUse @ .claude/settings.json · .claude/hooks/notify-related-tests.js
|
|
186
|
+
PreToolUse @ .claude/settings.json · .claude/hooks/guard-destructive-bash.js
|
|
187
|
+
PreToolUse @ .claude/settings.json · .claude/hooks/protect-artists-json.js
|
|
188
|
+
PreToolUse @ .claude/settings.json · .claude/hooks/protect-env.js
|
|
191
189
|
|
|
192
|
-
Summary: 6 warn · 4 info hidden (--verbose) · score 40/100
|
|
190
|
+
Summary: 6 warn · 4 info hidden (--verbose) · score 40/100 broken
|
|
193
191
|
```
|
|
194
192
|
|
|
195
193
|
The `Stack:` line is orientation only — which project, how big. The summary lists
|
package/dist/cli.js
CHANGED
|
@@ -12534,6 +12534,21 @@ function score(findings) {
|
|
|
12534
12534
|
}
|
|
12535
12535
|
return Math.max(0, 100 - deduction);
|
|
12536
12536
|
}
|
|
12537
|
+
function scoreLabel(points) {
|
|
12538
|
+
if (points === 100) {
|
|
12539
|
+
return "clean";
|
|
12540
|
+
}
|
|
12541
|
+
if (points >= 90) {
|
|
12542
|
+
return "good";
|
|
12543
|
+
}
|
|
12544
|
+
if (points >= 70) {
|
|
12545
|
+
return "needs work";
|
|
12546
|
+
}
|
|
12547
|
+
if (points >= 50) {
|
|
12548
|
+
return "poor";
|
|
12549
|
+
}
|
|
12550
|
+
return "broken";
|
|
12551
|
+
}
|
|
12537
12552
|
|
|
12538
12553
|
// src/report/exit-code.ts
|
|
12539
12554
|
function exitCode(findings, failOn, failUnder) {
|
|
@@ -12685,10 +12700,10 @@ function renderText(args) {
|
|
|
12685
12700
|
lines.push(` no package.json in ${safe(args.resolvedFrom)} — scanned ${safe(facts.root)}`);
|
|
12686
12701
|
}
|
|
12687
12702
|
lines.push("");
|
|
12688
|
-
lines.push(`
|
|
12703
|
+
lines.push(`Scanned: ${formatStack(facts)}`);
|
|
12689
12704
|
lines.push("");
|
|
12690
|
-
for (const
|
|
12691
|
-
lines.push(...
|
|
12705
|
+
for (const group of groupByRule(visible)) {
|
|
12706
|
+
lines.push(...formatGroup(group, facts.root));
|
|
12692
12707
|
lines.push("");
|
|
12693
12708
|
}
|
|
12694
12709
|
lines.push(summary);
|
|
@@ -12712,17 +12727,38 @@ function formatStack(facts) {
|
|
|
12712
12727
|
`packageManager=${facts.packageManager}`
|
|
12713
12728
|
].join(" · ");
|
|
12714
12729
|
}
|
|
12715
|
-
function
|
|
12730
|
+
function groupByRule(findings) {
|
|
12731
|
+
const byRule = new Map;
|
|
12732
|
+
for (const f of findings) {
|
|
12733
|
+
const bucket = byRule.get(f.ruleId);
|
|
12734
|
+
if (bucket === undefined) {
|
|
12735
|
+
byRule.set(f.ruleId, [f]);
|
|
12736
|
+
} else {
|
|
12737
|
+
bucket.push(f);
|
|
12738
|
+
}
|
|
12739
|
+
}
|
|
12740
|
+
return [...byRule.values()];
|
|
12741
|
+
}
|
|
12742
|
+
function shortPath(value, root) {
|
|
12743
|
+
return value.split(`${root}/`).join("");
|
|
12744
|
+
}
|
|
12745
|
+
function formatGroup(group, root) {
|
|
12746
|
+
const first = group[0];
|
|
12747
|
+
const count = group.length;
|
|
12716
12748
|
const out = [];
|
|
12717
|
-
out.push(`${actionColumn(
|
|
12718
|
-
out.push(`
|
|
12719
|
-
|
|
12720
|
-
|
|
12721
|
-
const
|
|
12722
|
-
out.push(`
|
|
12749
|
+
out.push(`${actionColumn(first.action)} rule:${safe(first.ruleId)}${count > 1 ? ` ×${count}` : ""}`);
|
|
12750
|
+
out.push(` ${safe(count === 1 ? first.message : ruleHeadline(first))}`);
|
|
12751
|
+
for (const f of group) {
|
|
12752
|
+
const where = f.evidence.filter((e) => e.kind !== "count" && e.kind !== "threshold").map((e) => shortPath(e.value, root));
|
|
12753
|
+
const location = where.length > 0 ? where.join(" · ") : f.subject;
|
|
12754
|
+
out.push(` ${safe(shortPath(location, root))}`);
|
|
12723
12755
|
}
|
|
12724
12756
|
return out;
|
|
12725
12757
|
}
|
|
12758
|
+
function ruleHeadline(f) {
|
|
12759
|
+
const cut = f.message.indexOf(": ");
|
|
12760
|
+
return cut === -1 ? f.message : f.message.slice(0, cut);
|
|
12761
|
+
}
|
|
12726
12762
|
function formatSummary(findings, verbose) {
|
|
12727
12763
|
const counts = {
|
|
12728
12764
|
keep: 0,
|
|
@@ -12746,14 +12782,15 @@ function formatSummary(findings, verbose) {
|
|
|
12746
12782
|
parts.push(`${infoHidden} info hidden (--verbose)`);
|
|
12747
12783
|
}
|
|
12748
12784
|
const points = score(findings);
|
|
12785
|
+
const scored = `score ${points}/100 ${scoreLabel(points)}`;
|
|
12749
12786
|
if (parts.length === 0) {
|
|
12750
|
-
return `Summary: no findings ·
|
|
12787
|
+
return `Summary: no findings · ${scored}`;
|
|
12751
12788
|
}
|
|
12752
|
-
return `Summary: ${parts.join(" · ")} ·
|
|
12789
|
+
return `Summary: ${parts.join(" · ")} · ${scored}`;
|
|
12753
12790
|
}
|
|
12754
12791
|
|
|
12755
12792
|
// src/version.ts
|
|
12756
|
-
var VERSION = "0.
|
|
12793
|
+
var VERSION = "0.3.0";
|
|
12757
12794
|
|
|
12758
12795
|
// src/commands/check.ts
|
|
12759
12796
|
async function runCheck(options) {
|
|
@@ -12936,6 +12973,7 @@ function printHelp() {
|
|
|
12936
12973
|
const text = `agentscan v${VERSION}
|
|
12937
12974
|
|
|
12938
12975
|
Usage:
|
|
12976
|
+
agentscan scan the current directory
|
|
12939
12977
|
agentscan check [dir] [options]
|
|
12940
12978
|
agentscan explain <findingId> [dir]
|
|
12941
12979
|
agentscan rules [dir]
|
|
@@ -13004,15 +13042,11 @@ async function main(argv) {
|
|
|
13004
13042
|
`);
|
|
13005
13043
|
return 0;
|
|
13006
13044
|
}
|
|
13007
|
-
if (values.help
|
|
13008
|
-
printHelp();
|
|
13009
|
-
return 0;
|
|
13010
|
-
}
|
|
13011
|
-
const command = positionals[0];
|
|
13012
|
-
if (command === undefined) {
|
|
13045
|
+
if (values.help) {
|
|
13013
13046
|
printHelp();
|
|
13014
13047
|
return 0;
|
|
13015
13048
|
}
|
|
13049
|
+
const command = positionals[0] ?? "check";
|
|
13016
13050
|
try {
|
|
13017
13051
|
switch (command) {
|
|
13018
13052
|
case "check": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chimix/agentscan",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Find broken agent configuration in a project — hooks whose scripts are gone, MCP servers that cannot start, skills that disagree with their lockfile",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|