efaimo 0.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 (75) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +6 -0
  4. package/README.md +281 -0
  5. package/SKILL.md +57 -0
  6. package/dist/check/check.d.ts +31 -0
  7. package/dist/check/check.js +77 -0
  8. package/dist/cli.d.ts +2 -0
  9. package/dist/cli.js +372 -0
  10. package/dist/clients/introspect.d.ts +8 -0
  11. package/dist/clients/introspect.js +198 -0
  12. package/dist/clients/rawprobe.d.ts +69 -0
  13. package/dist/clients/rawprobe.js +597 -0
  14. package/dist/clients/stateless.d.ts +7 -0
  15. package/dist/clients/stateless.js +121 -0
  16. package/dist/core/engine.d.ts +6 -0
  17. package/dist/core/engine.js +17 -0
  18. package/dist/core/grade.d.ts +8 -0
  19. package/dist/core/grade.js +19 -0
  20. package/dist/core/types.d.ts +243 -0
  21. package/dist/core/types.js +1 -0
  22. package/dist/index.d.ts +16 -0
  23. package/dist/index.js +26 -0
  24. package/dist/mcp/server.d.ts +3 -0
  25. package/dist/mcp/server.js +88 -0
  26. package/dist/reporters/badge.d.ts +12 -0
  27. package/dist/reporters/badge.js +37 -0
  28. package/dist/reporters/json.d.ts +1 -0
  29. package/dist/reporters/json.js +10 -0
  30. package/dist/reporters/markdown.d.ts +7 -0
  31. package/dist/reporters/markdown.js +121 -0
  32. package/dist/reporters/pretty.d.ts +15 -0
  33. package/dist/reporters/pretty.js +221 -0
  34. package/dist/rules/injection.d.ts +20 -0
  35. package/dist/rules/injection.js +106 -0
  36. package/dist/rules/mcp/index.d.ts +4 -0
  37. package/dist/rules/mcp/index.js +643 -0
  38. package/dist/rules/skill/index.d.ts +2 -0
  39. package/dist/rules/skill/index.js +279 -0
  40. package/dist/skills/parse.d.ts +4 -0
  41. package/dist/skills/parse.js +113 -0
  42. package/dist/targets/clientConfigs.d.ts +17 -0
  43. package/dist/targets/clientConfigs.js +110 -0
  44. package/dist/targets/repoScan.d.ts +2 -0
  45. package/dist/targets/repoScan.js +0 -0
  46. package/dist/targets/resolve.d.ts +28 -0
  47. package/dist/targets/resolve.js +48 -0
  48. package/dist/testing/anthropicRunner.d.ts +6 -0
  49. package/dist/testing/anthropicRunner.js +33 -0
  50. package/dist/testing/harness.d.ts +45 -0
  51. package/dist/testing/harness.js +96 -0
  52. package/dist/testing/openaiRunner.d.ts +12 -0
  53. package/dist/testing/openaiRunner.js +40 -0
  54. package/dist/util/childEnv.d.ts +1 -0
  55. package/dist/util/childEnv.js +43 -0
  56. package/dist/util/dotenv.d.ts +12 -0
  57. package/dist/util/dotenv.js +53 -0
  58. package/dist/util/misc.d.ts +15 -0
  59. package/dist/util/misc.js +121 -0
  60. package/dist/version.d.ts +2 -0
  61. package/dist/version.js +2 -0
  62. package/dist/weigh/anthropicExact.d.ts +11 -0
  63. package/dist/weigh/anthropicExact.js +46 -0
  64. package/dist/weigh/diff.d.ts +14 -0
  65. package/dist/weigh/diff.js +19 -0
  66. package/dist/weigh/serializers.d.ts +21 -0
  67. package/dist/weigh/serializers.js +44 -0
  68. package/dist/weigh/tokens.d.ts +12 -0
  69. package/dist/weigh/tokens.js +23 -0
  70. package/dist/weigh/weigh.d.ts +6 -0
  71. package/dist/weigh/weigh.js +104 -0
  72. package/docs/INTEGRATIONS.md +119 -0
  73. package/docs/METHODOLOGY.md +83 -0
  74. package/docs/RULES.md +76 -0
  75. package/package.json +77 -0
@@ -0,0 +1,104 @@
1
+ import { countClaudeToolTokens } from "./anthropicExact.js";
2
+ import { serializeClaudeStyle, serializeOpenAITools, serializeRawJson, serializeSingle } from "./serializers.js";
3
+ import { loadTokenizer } from "./tokens.js";
4
+ import { isBinaryPath, readTextSafe } from "../util/misc.js";
5
+ export async function weighServer(intro, opts = {}) {
6
+ const count = await loadTokenizer();
7
+ const perTool = [];
8
+ for (const tool of intro.tools) {
9
+ const s = serializeSingle(tool);
10
+ perTool.push({
11
+ name: tool.name,
12
+ tokens: {
13
+ rawJson: count(s.rawJson),
14
+ claudeStyle: count(s.claudeStyle),
15
+ openaiTools: count(s.openaiTools),
16
+ },
17
+ descriptionTokens: count(tool.description ?? ""),
18
+ schemaTokens: count(JSON.stringify(tool.inputSchema ?? {})),
19
+ });
20
+ }
21
+ perTool.sort((a, b) => b.tokens.claudeStyle - a.tokens.claudeStyle);
22
+ const totals = {
23
+ rawJson: count(serializeRawJson(intro.tools)),
24
+ claudeStyle: count(serializeClaudeStyle(intro.tools)),
25
+ openaiTools: count(serializeOpenAITools(intro.tools)),
26
+ };
27
+ // Per-tool rows count each tool's bare definition line; the total counts the
28
+ // <functions> block around them. Report the wrapper explicitly so the rows
29
+ // reconcile with the total (clamped: tokenizer merges across boundaries can
30
+ // in principle make the residual negative on other serializations).
31
+ const framingTokens = Math.max(0, totals.claudeStyle - perTool.reduce((s, t) => s + t.tokens.claudeStyle, 0));
32
+ const notes = [
33
+ "token counts are estimates using the o200k_base tokenizer; hosts add fixed framing text on top (see docs/METHODOLOGY.md)",
34
+ ];
35
+ let anthropicExactTotal;
36
+ if (opts.anthropicApiKey && intro.tools.length > 0) {
37
+ anthropicExactTotal = await countClaudeToolTokens(intro.tools, {
38
+ apiKey: opts.anthropicApiKey,
39
+ model: opts.anthropicModel,
40
+ });
41
+ if (anthropicExactTotal !== undefined) {
42
+ notes.push("anthropic-exact measured via /v1/messages/count_tokens (tools delta)");
43
+ }
44
+ else {
45
+ notes.push("anthropic-exact measurement failed; showing o200k estimates only");
46
+ }
47
+ }
48
+ return {
49
+ kind: "mcp",
50
+ label: intro.targetLabel,
51
+ toolCount: intro.tools.length,
52
+ resourceCount: intro.resources.length,
53
+ promptCount: intro.prompts.length,
54
+ perTool,
55
+ totals,
56
+ framingTokens,
57
+ instructionsTokens: count(intro.instructions ?? ""),
58
+ anthropicExactTotal,
59
+ notes,
60
+ };
61
+ }
62
+ export async function weighSkills(set) {
63
+ const count = await loadTokenizer();
64
+ const perSkill = [];
65
+ for (const skill of set.skills) {
66
+ const name = skill.name ?? "(unnamed)";
67
+ const metadataTokens = count(`${name}: ${skill.description ?? ""}`);
68
+ const bodyTokens = count(skill.body);
69
+ let refFileTokens = 0;
70
+ let refFileCount = 0;
71
+ for (const ref of skill.referencedPaths) {
72
+ if (!ref.exists || isBinaryPath(ref.resolved))
73
+ continue;
74
+ const text = readTextSafe(ref.resolved);
75
+ if (text === undefined)
76
+ continue;
77
+ refFileCount++;
78
+ refFileTokens += count(text);
79
+ }
80
+ perSkill.push({
81
+ name,
82
+ dir: skill.dir,
83
+ metadataTokens,
84
+ bodyTokens,
85
+ bodyLines: skill.bodyLines,
86
+ refFileCount,
87
+ refFileTokens,
88
+ });
89
+ }
90
+ return {
91
+ kind: "skill",
92
+ label: set.root,
93
+ perSkill,
94
+ totals: {
95
+ metadata: perSkill.reduce((s, x) => s + x.metadataTokens, 0),
96
+ body: perSkill.reduce((s, x) => s + x.bodyTokens, 0),
97
+ refFiles: perSkill.reduce((s, x) => s + x.refFileTokens, 0),
98
+ },
99
+ notes: [
100
+ "metadata loads at session start for every installed skill; body loads on trigger; referenced files load on demand",
101
+ "token counts are o200k_base estimates (see docs/METHODOLOGY.md)",
102
+ ],
103
+ };
104
+ }
@@ -0,0 +1,119 @@
1
+ # Integrating efaimo
2
+
3
+ efaimo is a plain CLI, so it drops into anything that can run a command. This is the
4
+ practical guide, from local dev to CI to calling it from an agent.
5
+
6
+ ## Local: pre-commit
7
+
8
+ Stop a broken skill before it lands. `.pre-commit-config.yaml`:
9
+
10
+ ```yaml
11
+ repos:
12
+ - repo: local
13
+ hooks:
14
+ - id: efaimo-skills
15
+ name: efaimo check --skill
16
+ entry: npx efaimo check --skill ./skills --strict
17
+ language: system
18
+ pass_filenames: false
19
+ files: ^skills/.*SKILL\.md$
20
+ ```
21
+
22
+ ## CI: GitHub Actions
23
+
24
+ Fail the build on skill regressions or a context-budget blowout.
25
+
26
+ ```yaml
27
+ - uses: efaimo-ai/efaimo@v0
28
+ with:
29
+ command: check --skill ./skills --strict
30
+ ```
31
+
32
+ Ratchet MCP tool-definition cost with the budget gate (commit `base.json` once with
33
+ `npx efaimo weigh "npx -y your-server" --out base.json`):
34
+
35
+ ```yaml
36
+ - run: npx efaimo weigh "npx -y your-server" --diff base.json --allow-increase 10
37
+ ```
38
+
39
+ Post the grade on the PR with `--md` (pipe into any comment action):
40
+
41
+ ```yaml
42
+ - run: npx efaimo check --skill ./skills --md >> "$GITHUB_STEP_SUMMARY"
43
+ ```
44
+
45
+ ## CI: GitLab
46
+
47
+ ```yaml
48
+ efaimo:
49
+ image: node:24
50
+ script:
51
+ - npx efaimo check --skill ./skills --strict
52
+ - npx efaimo weigh "npx -y your-server" --diff base.json --allow-increase 10
53
+ ```
54
+
55
+ ## Audit what an editor already loads
56
+
57
+ efaimo reads the MCP config of the tools you already use and weighs everything they
58
+ load, so you can see the context tax before you open a chat:
59
+
60
+ ```bash
61
+ npx efaimo weigh --client claude-code # also: claude-desktop, cursor, vscode
62
+ ```
63
+
64
+ ## Use it as an Agent Skill
65
+
66
+ efaimo ships a `SKILL.md`, so an agent that supports Agent Skills can install it and
67
+ run the checks itself:
68
+
69
+ ```bash
70
+ npx skills add efaimo-ai/efaimo
71
+ ```
72
+
73
+ The skill is a thin trigger; the `npx efaimo` binary does the work. It pairs with
74
+ `skills validate` (metadata) as the deeper tier (quality, cost, collisions, and the
75
+ `test` A/B harness): `npx skills validate ./skill && npx efaimo check --skill ./skill`.
76
+
77
+ ## Cursor / VS Code / Windsurf
78
+
79
+ Any editor that runs tasks can run efaimo. Add a task or a keybinding to
80
+ `npx efaimo check --skill ${workspaceFolder}/skills`, and use `weigh --client cursor`
81
+ (or `vscode`) to audit the MCP servers that editor loads.
82
+
83
+ ## Calling efaimo from an agent (efaimo mcp)
84
+
85
+ `efaimo mcp` runs efaimo as a small, read-only MCP server over stdio. It exposes two
86
+ tools, `efaimo_check_skill` and `efaimo_weigh_skill`, so an agent can lint or weigh a
87
+ skill mid-session, before it commits it to context. The tools read files only: they
88
+ spawn no process, open no socket, and `test` (which spends tokens) is not exposed, so
89
+ the server is safe to call unattended.
90
+
91
+ Add it to a client config (Claude Desktop, Claude Code, Cursor, ...):
92
+
93
+ ```json
94
+ {
95
+ "mcpServers": {
96
+ "efaimo": { "command": "npx", "args": ["-y", "efaimo", "mcp"] }
97
+ }
98
+ }
99
+ ```
100
+
101
+ Both tools take a `path` to a SKILL.md or a folder of skills and return Markdown.
102
+
103
+ efaimo holds its own server to the same bar. `efaimo check --mcp "npx -y efaimo mcp"`
104
+ grades it a quality A with a three-item 2026-07-28 migration diff: a fully
105
+ stateless server wants the 2.x MCP SDK for `server/discover`, cache fields, and
106
+ `resultType`. That SDK upgrade is the roadmap item; the server is useful today on
107
+ the current SDK.
108
+
109
+ ## Programmatic use
110
+
111
+ Every capability is also a typed function:
112
+
113
+ ```ts
114
+ import { weighServer, introspectServer, checkSkillSet } from "efaimo";
115
+
116
+ const intro = await introspectServer({ kind: "stdio", command: "npx", args: ["-y", "my-server"], label: "my-server" });
117
+ const cost = await weighServer(intro);
118
+ console.log(cost.totals.claudeStyle);
119
+ ```
@@ -0,0 +1,83 @@
1
+ # How efaimo counts tokens
2
+
3
+ efaimo's numbers are meant to be **reproducible and honest**, not authoritative
4
+ down to the token. This document is the whole method so you can check it.
5
+
6
+ ## The tokenizer
7
+
8
+ Estimates use **OpenAI's `o200k_base`** tokenizer (via `gpt-tokenizer`), because
9
+ it is public, deterministic, runs offline, and tracks the vocabularies of current
10
+ frontier models closely on English and JSON. Every estimated number in efaimo's
11
+ output is labeled `o200k` / `estimated`.
12
+
13
+ Different hosts use different tokenizers, and no vendor tokenizer but OpenAI's is
14
+ public, so the estimate carries an **unmeasured, consistent bias** against any
15
+ specific host. That makes it reliable for **comparison** (bias cancels in a
16
+ `--diff`, so budget gates are trustworthy) but only **approximate in absolute
17
+ terms**: the absolute thresholds (E127, E128) and the "percent of window" figure
18
+ can be off by more than a few percent, and should be read as order-of-magnitude,
19
+ not billing. When you need Claude-exact numbers, pass `--anthropic` (see below).
20
+ A calibration study across real schemas is future work; until then the default is
21
+ labeled an estimate precisely because its absolute accuracy is unverified.
22
+
23
+ ## What we serialize
24
+
25
+ A tool definition's real cost depends on how a host serializes it into the model's
26
+ context. efaimo reports three serializations so you can see the spread:
27
+
28
+ - **raw JSON** - the `tools/list` payload, minified. A neutral lower bound.
29
+ - **Claude-style** - each tool as `{"description","name","parameters"}` wrapped in
30
+ a `<functions>` block, mirroring how Claude-family harnesses present tools. This
31
+ is efaimo's **primary metric** (the one used for badges, diffs, and budgets)
32
+ because it is the closest public approximation of a real system-prompt injection.
33
+ - **OpenAI tools** - the Chat Completions `tools` array shape.
34
+
35
+ Hosts wrap these in a small amount of fixed framing text (headers, instructions on
36
+ how to call tools). That framing is **per-host constant** and independent of your
37
+ server, so efaimo excludes it: it would add the same number to every server and
38
+ wash out of any comparison or diff.
39
+
40
+ ### Per-tool numbers vs the total
41
+
42
+ Per-tool numbers count each tool's **bare definition line**; the Claude-style
43
+ total counts the whole `<functions>` block, including the `<function>` tags and
44
+ newlines around every line. That wrapper is reported as its own **block framing**
45
+ line item, so per-tool numbers plus framing equal the Claude-style total. The
46
+ per-tool threshold rule (E127) uses the bare line; the total rule (E128) uses the
47
+ wrapped total. For the other two serializations no wrapper exists, and summing
48
+ per-tool counts may differ from the total by a few tokens, because a tokenizer
49
+ can merge characters across element boundaries when tools are concatenated.
50
+
51
+ ## Skills
52
+
53
+ For Agent Skills efaimo reports the three progressive-disclosure levels defined by
54
+ the spec separately, because they load at different times:
55
+
56
+ - **metadata** (`name` + `description`) - loaded at session start for *every*
57
+ installed skill. This is the always-on cost; the spec targets ~100 tokens.
58
+ - **body** - the rest of `SKILL.md`, loaded when the skill triggers.
59
+ - **referenced files** - `references/`, `scripts/`, `assets/` linked from the body,
60
+ loaded only on demand.
61
+
62
+ ## Claude-exact mode
63
+
64
+ With `--anthropic` (and `ANTHROPIC_API_KEY` set), efaimo also calls Anthropic's
65
+ `POST /v1/messages/count_tokens`, which accepts a `tools` array, and measures the
66
+ **delta** between a request with your tools and the same request without them. That
67
+ delta is the exact number of tokens your tool definitions add for the chosen Claude
68
+ model. This requires network and a key, so it is opt-in; the o200k estimate is
69
+ always shown alongside.
70
+
71
+ ## Determinism
72
+
73
+ Given the same server output, efaimo produces the same numbers every run: the
74
+ tokenizer is deterministic and no sampling is involved. Server responses can change
75
+ between runs (a server may reorder or add tools); efaimo's `check` flags
76
+ nondeterministic `tools/list` ordering (E112) precisely because it affects both
77
+ prompt-cache hits and reproducibility.
78
+
79
+ ## What this is not
80
+
81
+ efaimo estimates the **cost of tool/skill definitions sitting in context**. It does
82
+ not measure per-call argument/result tokens, host framing, or model-side reasoning.
83
+ For end-to-end token accounting, instrument your agent run directly.
package/docs/RULES.md ADDED
@@ -0,0 +1,76 @@
1
+ # efaimo rules
2
+
3
+ Every finding carries a stable rule id so you can suppress, track, or link it.
4
+ Severities: **error** (will bite you), **warn** (should fix), **info** (worth knowing).
5
+
6
+ Grade starts at 100; each error costs 15, each warning 5, each info 1.
7
+ `A >= 90, B >= 80, C >= 70, D >= 60, else F`.
8
+
9
+ MCP checks split into **2026-07-28 readiness** (E101-E118) and **quality**
10
+ (E121-E130); skill checks are S1xx. **Only quality and skill findings are
11
+ graded.** Readiness findings are reported as a separate, ungraded **migration
12
+ diff**: the stateless revision they target is a locked Release Candidate
13
+ (2026-05-21) that does not finalize until 2026-07-28, and not yet migrating to
14
+ an unreleased spec is a to-do, not a defect. Readiness severities still order
15
+ the diff by urgency, but the diff never affects the grade, the badge, or the
16
+ exit code (including `--strict`). Once the spec ratifies, expect readiness to
17
+ start counting in a minor release. Source-pattern rules are heuristics and say
18
+ so in their detail line.
19
+
20
+ ## MCP readiness (E101-E118)
21
+
22
+ | id | sev | what it catches | source of truth |
23
+ |---|---|---|---|
24
+ | E101 | warn | depends on a pre-`2026-07-28` SDK line (TS `@modelcontextprotocol/sdk` 1.x, Python `mcp` 1.x) instead of the 2.x stateless line | changelog / SDK releases |
25
+ | E102 | warn | source uses **Sampling** (`sampling/createMessage`), deprecated (SEP-2577) | draft changelog |
26
+ | E103 | warn | source uses **Roots** (`roots/list`), deprecated (SEP-2577) | draft changelog |
27
+ | E104 | warn | server declares the **logging** capability or source uses MCP Logging, deprecated (SEP-2577); `logging/setLevel` is removed | draft changelog |
28
+ | E105 | warn | the server does not answer a bare stateless `tools/list` (timeout, crash, or a not-initialized error), so it requires the removed `initialize` handshake. A server that answers (even with an error) is judged by E106/E107/E118 instead | SEP-2567 / stateless core |
29
+ | E106 | warn | `server/discover` is not implemented (MUST in the RC, SEP-2575) | draft changelog |
30
+ | E107 | info | results omit the required `resultType` field (`"complete"` \| `"input_required"`) | draft changelog |
31
+ | E108 | info | source relies on removed SSE resumability (`Last-Event-ID`) | draft changelog |
32
+ | E109 | info | auth advertises deprecated DCR (RFC 7591) with no detected CIMD support | PR #2858 / CIMD |
33
+ | E110 | warn | source uses legacy elicitation (`elicitation/create`); replaced by MRTR `input_required` results (SEP-2322) | draft changelog |
34
+ | E111 | info | in-process session-state patterns; statelessness expects server-minted handles in tool args | stateless core |
35
+ | E112 | warn | `tools/list` order is nondeterministic across connections (hurts prompt-cache hits) | draft changelog |
36
+ | E113 | info | no Server Card found at `/.well-known/mcp` (discovery metadata; heuristic path) | roadmap / Server Card WG |
37
+ | E114 | info | source uses the removed `ping` utility | draft changelog |
38
+ | E115 | info | source uses `resources/subscribe`, replaced by `subscriptions/listen` | draft changelog |
39
+ | E116 | warn | server prints non-JSON noise on stdout (breaks stdio framing) | transport basics |
40
+ | E117 | warn | only the deprecated HTTP+SSE transport worked; Streamable HTTP failed | draft changelog |
41
+ | E118 | warn | `tools/list` result omits the now-required `ttlMs`/`cacheScope` cache fields (SEP-2549, CacheableResult; also required on prompts/list, resources/list, resources/read, resources/templates/list, server/discover) | draft changelog |
42
+
43
+ ## MCP quality (E121-E130)
44
+
45
+ | id | sev | what it catches |
46
+ |---|---|---|
47
+ | E121 | error/warn | tool description missing, a placeholder, or under ~20 chars |
48
+ | E122 | warn | description misses 3+ of 4 quality axes (length, when-to-use, params documented, mentions result), mirrors Glama's tool-definition-quality dimensions, computed locally |
49
+ | E123 | warn/info | no tools declare annotations; a destructive-looking tool lacks `destructiveHint` |
50
+ | E124 | warn | many undocumented parameters, or an oversized `enum` inflating every prompt |
51
+ | E125 | warn | tool count high enough (30+, 60+) to degrade routing and inflate context |
52
+ | E126 | warn/info | tool names mix conventions or exceed 64 chars |
53
+ | E127 | warn | a single tool definition costs 800+ tokens (needs `weigh` data) |
54
+ | E128 | warn | total tool-definition tax over ~10k / ~25k tokens, estimated (needs `weigh` data); hard CI failure is left to the `--max-tokens`/`--diff` gate |
55
+ | E130 | info | instruction-injection patterns in a tool description or server instructions (shallow heuristic, never a security verdict; use a dedicated scanner for depth) |
56
+
57
+ ## Skills (S101-S106)
58
+
59
+ | id | sev | what it catches | spec |
60
+ |---|---|---|---|
61
+ | S101 | error/warn/info | frontmatter invalid: missing `name`/`description`, `name` not matching the directory, over length limits, `metadata` not a string map, non-standard fields | agentskills.io |
62
+ | S102 | warn/info | description too thin or never says when to use the skill | agentskills.io |
63
+ | S103 | error/warn | duplicate skill names, or two skills with heavily overlapping descriptions (trigger collision) | internal |
64
+ | S104 | warn/info | metadata over ~100 tokens, body over ~5k tokens or 500 lines, or long body with no progressive disclosure | agentskills.io |
65
+ | S105 | info | instruction-injection patterns in the skill body or description (shallow heuristic, never a security verdict) | internal |
66
+ | S106 | error/warn/info | referenced file missing, escaping the skill dir (`..`), or nested more than one level deep | agentskills.io |
67
+
68
+ ## Notes on heuristics
69
+
70
+ Rules that scan **source code** (E101-E104, E108, E110, E111, E114, E115) are
71
+ pattern matches over your repo and can produce false positives; each finding says
72
+ so and points at the file:line so you can confirm. Rules that scan **a live
73
+ server** (E105-E107, E112, E116, E117) and the token rules (E127, E128) reflect
74
+ what the server actually did. `efaimo` is a linter, not a security scanner:
75
+ E130/S105 are surface heuristics, for real supply-chain security use a dedicated
76
+ tool such as Snyk agent-scan or the Cisco MCP Scanner.
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "efaimo",
3
+ "version": "0.1.0",
4
+ "description": "Audit what your agent loads: quality and context cost for MCP servers and Agent Skills",
5
+ "keywords": [
6
+ "mcp",
7
+ "model-context-protocol",
8
+ "agent-skills",
9
+ "skills",
10
+ "audit",
11
+ "lint",
12
+ "tokens",
13
+ "context-window",
14
+ "claude",
15
+ "ai-agents",
16
+ "cli"
17
+ ],
18
+ "license": "Apache-2.0",
19
+ "author": "efaimo ai <hello@efaimo.ai> (https://efaimo.ai)",
20
+ "homepage": "https://github.com/efaimo-ai/efaimo#readme",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/efaimo-ai/efaimo.git"
24
+ },
25
+ "bugs": "https://github.com/efaimo-ai/efaimo/issues",
26
+ "type": "module",
27
+ "sideEffects": false,
28
+ "engines": {
29
+ "node": ">=22"
30
+ },
31
+ "bin": {
32
+ "efaimo": "dist/cli.js"
33
+ },
34
+ "main": "./dist/index.js",
35
+ "types": "./dist/index.d.ts",
36
+ "exports": {
37
+ ".": {
38
+ "types": "./dist/index.d.ts",
39
+ "import": "./dist/index.js"
40
+ },
41
+ "./package.json": "./package.json"
42
+ },
43
+ "publishConfig": {
44
+ "provenance": true,
45
+ "access": "public"
46
+ },
47
+ "files": [
48
+ "dist",
49
+ "SKILL.md",
50
+ "NOTICE",
51
+ "CHANGELOG.md",
52
+ "docs/METHODOLOGY.md",
53
+ "docs/RULES.md",
54
+ "docs/INTEGRATIONS.md"
55
+ ],
56
+ "scripts": {
57
+ "build": "tsc -p tsconfig.build.json",
58
+ "typecheck": "tsc --noEmit",
59
+ "test": "vitest run",
60
+ "test:watch": "vitest",
61
+ "prepublishOnly": "npm run typecheck && npm run build && npm run test"
62
+ },
63
+ "dependencies": {
64
+ "@modelcontextprotocol/sdk": "^1.29.0",
65
+ "commander": "^15.0.0",
66
+ "cross-spawn": "^7.0.6",
67
+ "gpt-tokenizer": "^3.4.0",
68
+ "picocolors": "^1.1.1",
69
+ "yaml": "^2.9.0"
70
+ },
71
+ "devDependencies": {
72
+ "@types/cross-spawn": "^6.0.6",
73
+ "@types/node": "^26.1.1",
74
+ "typescript": "^7.0.2",
75
+ "vitest": "^4.1.10"
76
+ }
77
+ }