CtrlK
BlogDocsLog inGet started
Tessl Logo

tessleng/agent-insight-experiment

Scan a repository to surface actionable findings about agent performance. Analyzes source code, git history, GitHub data, agent logs, and agent context, then synthesizes cross-referenced findings with targeted actions informed by Tessl product awareness. Supports incremental multi-developer contributions and produces a self-contained HTML report.

70

Quality

88%

Does it follow best practices?

Impact

No eval scenarios have been run

SecuritybySnyk

Advisory

Suggest reviewing before use

Overview
Quality
Evals
Security
Files

SKILL.mdskills/analyze-context-inventory/

name:
analyze-context-inventory
description:
Produce a structured inventory of every markdown and configuration file that coding agents consume in a repository — entry points (AGENTS.md, CLAUDE.md), always-on rules (.cursor/rules), hooks, skills, and MCP servers. Classifies each file by which agent harness reads it (Claude / Cursor / cross-agent) and rates its usefulness. Use when running an insight scan's context inventory phase, or when you want a filterable tree view of the context agents see.

Analyze Context Inventory

Produce a structured, per-file inventory of the agent context surface of a repository. Unlike analyze-agent-context — which emits qualitative insights about context quality — this skill emits a catalogue: one entry per file with category, agent scope, and usefulness. The catalogue renders in the HTML report as a filterable file tree.

The skill also emits APEX insights (prefix INV) using the standard insight report schema, so it plugs into synthesis like the other analyzers.

Before You Start

Read the shared reference files:

  • APEX taxonomy for the insight categories
  • Insight report schema for the report structure

Resolving reference paths: The links above use relative paths (../../references/...) that work when this skill is read from its tile directory. If those paths do not resolve (e.g. when activated via a .claude/skills/ symlink), find the shared references at .tessl/tiles/*/agent-insight-experiment/references/ relative to the repository root.

Your insight prefix is INV (e.g., INV-001, INV-002).

Sensitive Value Handling (REQUIRED)

Several files in scope — .mcp.json, .cursor/mcp.json, .claude/settings.json, .claude/settings.local.json — routinely contain secrets: API keys, bearer tokens, OAuth tokens, database URLs with embedded passwords, and env-var credentials. These values must never appear in the inventory output, including in the purpose field of mcp and hook entries, in quoted evidence inside INV insights, or in any log/console output you produce.

When emitting a purpose string or insight evidence derived from these files:

  • MCP server env blocks: do not read values. If you need to reference them, name the key only (e.g. "purpose": "GitHub MCP server (requires GITHUB_TOKEN env var)").
  • Command and args: include the binary/script path and non-secret flags only. Redact any positional value that follows a flag matching /token|key|secret|password|bearer|auth|credential/i, any KEY=value segment whose KEY matches that pattern, any JWT-shaped string, and any opaque string longer than ~20 characters that looks like random base64/hex. Replace with [REDACTED].
  • Hook command strings: apply the same redaction rules to the command before truncating to 80 characters.
  • Connection strings / URLs: strip user:password@ credentials and query-string tokens before storing.
  • When unsure, redact.

The bundled scripts/build-inventory.mjs applies these redactions automatically to the MCP and hook purpose fields it generates. If you build the inventory by hand (e.g. on a host without Node), you must apply the same rules yourself. In either path, review the finished report once more before saving to confirm no raw secret slipped through the linked-file content you quote in INV insights.

Building the inventory

The structured part of the output — context_inventory.files — is produced by the bundled script at scripts/build-inventory.mjs. Run it first; it handles glob discovery, link following, bidirectional reference tracking, classification, validation (symmetry + no self-loops), and writes the JSON report. This replaces hand-parsing and guarantees the link graph is correct on every run.

# From the target repo root:
node <skill-dir>/scripts/build-inventory.mjs

# Or specify explicit paths:
node <skill-dir>/scripts/build-inventory.mjs \
  --root /path/to/repo \
  --out  .tessl-insights-poc/reports/context-inventory.json

When an output file already exists, the script preserves the existing executive_summary, insights, and summary_statistics (the qualitative sections that you produce) and overwrites only the structured context_inventory.files and matching scope.metrics. After running the script:

  1. Read the produced JSON and review the context_inventory.files array.
  2. Do the Cross-Agent Gap Detection pass and write INV- insights.
  3. Write the executive_summary and summary_statistics sections.
  4. Re-write the merged JSON.

If Node is unavailable on the target host, use the Discovery and Classification sections below to build the inventory by hand — the script and the documentation implement the same rules.

Discovery

The goal is completeness: miss nothing that a coding agent would load. Monorepos typically have agent config at multiple depths (root and per-app under apps/*/, packages/*/), so every pattern below must be matched at any depth — never limit to the repo root.

Prefer the agent's Glob tool (cross-platform)

Use your harness's built-in file-search tool (e.g. Glob in Claude Code / Cursor). It is portable across macOS, Linux, and Windows and does not depend on the presence of Unix find. Run each of these patterns and union the results — no depth cap, ** is required so per-app configs are matched:

PatternWhat it captures
**/AGENTS.mdAgent entry points at any depth
**/CLAUDE.mdClaude Code entry points at any depth
**/.cursorrulesLegacy Cursor rules file
**/.github/copilot-instructions.mdCopilot entry point
**/tessl.jsonTessl manifests (root and per-app)
**/.mcp.jsonCross-tool MCP configs (root and per-app)
**/.cursor/mcp.jsonCursor-specific MCP configs
**/.cursor/settings.jsonCursor project settings
**/.cursor/rules/*.mdCursor rule files
**/.cursor/rules/*.mdcCursor rule files
**/.cursor/commands/*.mdCursor slash commands
**/.claude/settings.jsonClaude Code project settings (hook source)
**/.claude/settings.local.jsonClaude Code local settings (hook + MCP enablement)
**/.claude/commands/*.mdClaude Code custom slash commands
**/.claude/skills/*/SKILL.mdLocal Claude Code skills
**/.tessl/RULES.mdTessl-managed rule index
**/.tessl/tiles/**/SKILL.mdInstalled Tessl skills
**/.tessl/tiles/**/rules/*.mdInstalled Tessl rules

Ignore anything under **/node_modules/**, **/.git/**, **/dist/**, **/build/**, **/.next/**, **/.turbo/**, and any **/test-fixtures/** / golden-run directories — those aren't real agent context.

Fallback: POSIX find (Linux / macOS / WSL / Git Bash only)

If a Glob tool isn't available, fall back to this shell command. Do not use it directly on native Windows cmd.exe / PowerShell — Windows' find is a different utility. Use WSL, Git Bash, MSYS, or Cygwin.

find . \
  -path "*/node_modules/*" -prune -o \
  -path "*/.git/*" -prune -o \
  -path "*/dist/*" -prune -o \
  -path "*/build/*" -prune -o \
  -path "*/.next/*" -prune -o \
  -path "*/.turbo/*" -prune -o \
  -path "*/test-fixtures/*" -prune -o \
  -type f \( \
    -name "AGENTS.md" -o \
    -name "CLAUDE.md" -o \
    -name ".cursorrules" -o \
    -name "copilot-instructions.md" -o \
    -name "tessl.json" -o \
    -name ".mcp.json" -o \
    -path "*/.cursor/mcp.json" -o \
    -path "*/.cursor/settings.json" -o \
    -path "*/.cursor/rules/*.md" -o \
    -path "*/.cursor/rules/*.mdc" -o \
    -path "*/.cursor/commands/*.md" -o \
    -path "*/.claude/settings.json" -o \
    -path "*/.claude/settings.local.json" -o \
    -path "*/.claude/commands/*.md" -o \
    -path "*/.claude/skills/*/SKILL.md" -o \
    -path "*/.tessl/tiles/*/SKILL.md" -o \
    -path "*/.tessl/tiles/*/*/SKILL.md" -o \
    -path "*/.tessl/tiles/*/*/*/SKILL.md" -o \
    -path "*/.tessl/tiles/*/*/*/*/SKILL.md" -o \
    -path "*/.tessl/tiles/*/rules/*.md" -o \
    -path "*/.tessl/tiles/*/*/rules/*.md" -o \
    -path "*/.tessl/RULES.md" \
  \) -print

Never apply a -maxdepth cap — per-app .claude/ and .cursor/ configs commonly sit at depth 3+ (e.g. apps/frontend/.claude/settings.json).

Per-file reads

For every discovered .claude/settings.json and .claude/settings.local.jsoneach one, including the nested per-app ones — parse the JSON and enumerate one hook entry per (hooks.<Event>, command) pair. A monorepo may easily have three or more of these settings files; missing any of them means missing hooks that actually run on that app.

Similarly, for every .mcp.json and .cursor/mcp.json found at any depth, parse and enumerate one mcp entry per server. Never assume the root config is canonical — sub-apps often add their own servers (e.g. apps/frontend/.cursor/mcp.json with a Figma server).

Apply the redaction rules from Sensitive Value Handling to the command strings and args you store in each entry's purpose field. Do not read or record MCP server env values.

Link following

Glob-based discovery alone misses context pulled in by reference — a repo may link to docs/architecture.md, CONTRIBUTING.md, or a shared rules/*.md bundle from inside AGENTS.md or a skill, and the agent that reads the referrer will follow or inline that content. Miss those, and the inventory under-reports the context surface.

After the initial glob pass, for every discovered markdown-style file (.md, .mdc, and the body of SKILL.md files), parse out two kinds of links and follow them:

  1. @path imports — Claude-style context inclusion (e.g. @.tessl/RULES.md, @AGENTS.md). Content at the target is pulled into the model's context at load time, so linked targets behave like always-on rules.
    • Regex: (^|[\s(])@([^\s)]+) — a @ preceded by start-of-line or whitespace, followed by a path-like token.
    • Ignore email addresses (target contains @ after the path segment), decorators / mentions in code fences, and anything that isn't a plausible filesystem path.
  2. Markdown links[text](path) style references. Content is not auto-loaded, but the agent follows them when it decides it needs them, so they are part of the reachable context.
    • Regex: \[[^\]]*\]\(([^)\s]+)(?:\s+"[^"]*")?\) — captures the URL portion and tolerates an optional title.
    • Skip http://, https://, mailto:, anchor-only fragments (#...), javascript:, image references (.png, .jpg, .gif, .svg, .webp), and any target that clearly isn't a file path.

Skip fenced code blocks (``` ... ```) and inline code (`...`) when scanning — examples in docs are not real references.

Path resolution:

  • Absolute-looking paths (/foo/bar.md) resolve against the repo root.
  • Anything else resolves relative to the directory of the file being scanned.
  • Normalize ./ and ../ segments; drop any URL query (?...) or fragment (#...) suffix.
  • Only keep targets that exist on disk and are text/config files (.md, .mdc, .json, .jsonc, .toml, .yaml, .yml, .txt, or a bare file with no extension that contains text). Binary targets and missing paths are dropped.

Follow transitively: add each resolved target to a work queue, parse its links, and repeat until no new files are discovered. Track visited paths so cycles (A → B → A) terminate.

Classification for link-discovered files: if the target already matches a glob-category pattern (e.g. a linked SKILL.md), use that category. Otherwise:

  • Reached via @always_on_rule (the content is inlined into the agent's context).
  • Reached only via [text](path)referenced_doc (available on demand).
  • If a file is reached via both @ and [text](path) from different sources, prefer always_on_rule.

Record both directions of every edge so the UI can show "X links to these" and "X is linked from these". See the links / linked_from fields in the per-file schema below.

Ignore globs still apply. Do not follow links into node_modules/, .git/, dist/, build/, .next/, .turbo/, or test-fixtures/ — those targets are excluded from the inventory even if referenced.

Tessl CLI augmentation

If tessl doctor is available, run it once and capture the installed tile + skill list — it is authoritative for skill entries and supplements what you find on disk.

Self-check before classifying

After the initial pass, verify you have not missed multi-location configs. Run these as Glob patterns (preferred) or the equivalent find (fallback):

  • **/.claude/settings.json
  • **/.claude/settings.local.json
  • **/.mcp.json
  • **/.cursor/mcp.json

Each result must produce at least one inventory entry. If any are missing, fix the inventory before saving.

Classification

For each discovered file, emit one entry with these fields:

FieldTypeMeaning
pathstringRepo-relative path; the renderer groups by this path's directory and shows the basename in the tree
categoryenumentry_point | always_on_rule | hook | skill | mcp | referenced_doc
agentsstring[]Subset of ["claude", "cursor"], or ["cross-agent"]
usefulnessenumhigh | medium | low
usefulness_reasoningstringShort (≤ 1 sentence) justification
scopestring[]Optional tags from path: backend, frontend, etc. (omit root)
purposestringOptional short description (required for hooks, skills, MCP servers)
linksstring[]Optional. Repo-relative paths this file references (forward edges). Include both @ imports and [text](path) markdown links, normalized and deduplicated. Omit or use [] when the file references nothing.
linked_fromstring[]Optional. Repo-relative paths of files that reference this one (reverse edges). Must be the inverse of links — every entry in links implies a matching entry in the target's linked_from.
tokensnumberOptional. Rough token estimate (ceil(content_length / 4)) for the file this entry represents. Only emitted for .md / .mdc files — JSON/YAML/TOML config files are structured data whose character count is not a meaningful proxy for the prose-like context the agent reads. Also omitted on hook and mcp entries (synthetic records that sit inside a config file).

Path conventions

The renderer treats path as a real filesystem-style path and groups by dirname(path). This produces a natural file tree without any hardcoded category buckets.

  • Real files (markdown, mdc, json): use the actual repo-relative path. E.g. apps/backend/AGENTS.md, .cursor/rules/typescript.mdc.
  • Hooks (defined inside .claude/settings.json): use <settings-file>/<HookEvent> — e.g. .claude/settings.json/SessionStart. This puts each hook as a "leaf" under the settings file in the tree.
  • MCP servers (defined inside .mcp.json / .cursor/mcp.json): use <config-file>/<server-name> — e.g. .mcp.json/tessl, apps/frontend/.cursor/mcp.json/figma.
  • Installed Tessl skills: use the real SKILL.md path under .tessl/tiles/<workspace>/<tile>/skills/<skill>/SKILL.md.

Do not invent synthetic group directories like hooks/, skills/, or mcp-servers/ — those obscure where context actually lives in the repo.

Category rules

  • entry_point — files that are auto-loaded by an agent at session start:
    • AGENTS.md (any depth)
    • CLAUDE.md (any depth)
    • .cursorrules
    • .github/copilot-instructions.md
  • always_on_rule — rule files that are injected into every prompt:
    • .cursor/rules/*.mdc with alwaysApply: true in frontmatter
    • Rule files referenced via @path imports from an entry_point (scan for @ import syntax, e.g. .tessl/RULES.md pattern)
  • hook — each entry under hooks.* in .claude/settings.json / .claude/settings.local.json. One entry per (event, command) pair. The path is the command string; the group directory is hooks/.
  • skill — entries under:
    • .claude/skills/*/SKILL.md
    • .cursor/commands/**
    • Tessl-installed skills under .tessl/tiles/**/skills/**/SKILL.md
    • Skills the repo ships in tiles/**/skills/**/SKILL.md
  • mcp — each server entry in .mcp.json or .cursor/mcp.json. The path is the server name; the group directory is mcp-servers/.
  • referenced_doc — a text/markdown file that is only reachable via a [text](path) markdown link from another inventoried file (never via @ import, never via a glob category pattern). These are consulted on demand by the agent and are part of the reachable context surface even though they aren't auto-loaded.

If a file matches multiple categories (rare), pick the most specific — e.g. a .mdc with alwaysApply: true is always_on_rule, not entry_point. A file matched by both a glob pattern and a link always uses the glob category (e.g. a SKILL.md that is also linked from AGENTS.md is skill, not referenced_doc).

Tessl-vendored files

Any path containing the substring tessl__ (e.g. .claude/skills/tessl__tile-creator/**, .cursor/rules/tessl__rule__*.mdc) is excluded from the inventory entirely. These are vendored skills, rules, and references installed by tessl install — they are not part of the repo's own agent surface and they inflate the inventory with noise. Both the script and the manual path must drop them. Link-following must also stop at a tessl__ boundary: if AGENTS.md links into .claude/skills/tessl__foo/references/bar.md, neither the link edge nor the target file appears in the inventory.

.tessl/tiles/** paths (installed tile contents that aren't under a tessl__ directory) remain in the inventory — those are the tile-level skills and rules the repo has opted into and wants visible.

Agent rules

  • claude only: CLAUDE.md, .claude/**, hooks, .claude/skills/**
  • cursor only: .cursor/**, .cursorrules, .cursor/commands/**, MCP servers that appear only in .cursor/mcp.json
  • cross-agent: AGENTS.md, root .mcp.json servers, .tessl/** loaded via @ from AGENTS.md, copilot-instructions.md (read by multiple tools)

A .mdc rule under .cursor/rules/ is always ["cursor"], even if the same topic is covered in AGENTS.md — record both entries and flag the duplication as an INV insight.

A referenced_doc inherits its agents from the set of files that link to it — if it is only reached from AGENTS.md, it is ["cross-agent"]; if only from a .cursor/rules/*.mdc, it is ["cursor"]; if reached from multiple harnesses, union them.

Usefulness rubric

  • high — specific, actionable, concrete examples or conventions; current references; ≥ ~20 non-trivial lines. Example: an AGENTS.md with build commands, conventions, and architecture diagram.
  • medium — exists and relevant but generic, short, or partially stale. Example: a rule that restates general TypeScript advice without project-specific detail.
  • low — near-empty (< 10 lines of substance), clearly abandoned, superseded, or contradicted by another higher-usefulness file.

Use judgment. The goal is actionable signal, not a strict formula. Record a short usefulness_reasoning so a human can audit.

Scope tags

Derive from the file's path:

  • apps/backend/**["backend"]
  • apps/frontend/**["frontend"]
  • Repo root → omit or ["root"]
  • .cursor/rules/<area>.mdc where <area> matches a known app → tag with that area

Cross-Agent Gap Detection (for INV insights)

While building the inventory, look for:

  • Asymmetry: topic X covered in Cursor rules but not in AGENTS.md / CLAUDE.md (or vice versa). Emit an INV insight with subcategory TCG-5 (cross-tool inconsistency).
  • Duplication: the same content present verbatim in two places (e.g. CLAUDE.md copies AGENTS.md). Emit INV insight with subcategory KCG-4 (redundant context wastes the context window).
  • Dead skills/MCP: an MCP server configured but the agent that reads that config file doesn't actually use it (check logs or tessl doctor). Emit INV insight with subcategory TCG-2.
  • Low-usefulness files: clusters of low usefulness rules suggest cleanup. Emit INV insight with subcategory KCG-5 (stale documentation).

Keep INV insights focused on facts the inventory makes visible. Don't repeat insights that analyze-agent-context already emits.

Output

Produce a JSON report conforming to the insight report schema with one additional top-level field: context_inventory.

{
  "metadata": {
    "scan_id": "...",
    "data_source": "context_inventory",
    "repository": "...",
    "analysis_timestamp": "...",
    "analyzer_model": "...",
    "scope": {
      "description": "Inventory of agent context files: entry points, always-on rules, hooks, skills, MCP servers.",
      "metrics": {
        "files_inventoried": 38,
        "by_category": {
          "entry_point": 5,
          "always_on_rule": 24,
          "hook": 4,
          "skill": 1,
          "mcp": 1,
          "referenced_doc": 3
        },
        "link_edges": 12,
        "total_tokens": 42800
      }
    }
  },
  "executive_summary": "...",
  "summary_statistics": {
    /* standard */
  },
  "insights": [
    /* INV-001, INV-002, ... */
  ],

  "context_inventory": {
    "file_tokens": {
      "AGENTS.md": 705,
      "CLAUDE.md": 15,
      "docs/architecture.md": 312
    },
    "files": [
      {
        "path": "AGENTS.md",
        "category": "entry_point",
        "agents": ["claude", "cursor"],
        "usefulness": "high",
        "usefulness_reasoning": "~80 lines covering build commands, architecture, and cross-app type sync",
        "purpose": "Top-level agent entry point with monorepo conventions",
        "tokens": 705,
        "links": [".tessl/RULES.md", "docs/architecture.md"],
        "linked_from": ["CLAUDE.md"]
      },
      {
        "path": "docs/architecture.md",
        "category": "referenced_doc",
        "agents": ["cross-agent"],
        "usefulness": "high",
        "usefulness_reasoning": "Referenced from AGENTS.md; describes service boundaries and data flow",
        "purpose": "Architecture overview agents follow on demand",
        "linked_from": ["AGENTS.md"]
      },
      {
        "path": ".cursor/rules/typescript.mdc",
        "category": "always_on_rule",
        "agents": ["cursor"],
        "usefulness": "medium",
        "usefulness_reasoning": "Generic TS guidance, not project-specific",
        "scope": ["backend"]
      },
      {
        "path": ".claude/settings.json/PostToolUse",
        "category": "hook",
        "agents": ["claude"],
        "usefulness": "high",
        "usefulness_reasoning": "Auto-formats on Write/Edit — prevents format-related CI failures",
        "purpose": "auto-formats on Write/Edit"
      },
      {
        "path": ".mcp.json/tessl",
        "category": "mcp",
        "agents": ["cross-agent"],
        "usefulness": "medium",
        "usefulness_reasoning": "Stdio server via `tessl mcp start`",
        "purpose": "Tessl MCP (tile docs, skill lookup)"
      }
    ]
  }
}

Output path

If output_file is provided, use it exactly. Otherwise save to .tessl-insights-poc/reports/context-inventory.json.

Validation Before Saving

The scripts/build-inventory.mjs script enforces the structural invariants and exits non-zero on violation; if you take the manual path, apply the same checks by hand.

  • Every discovered file appears exactly once in context_inventory.files
  • Every entry has path, category, agents, usefulness, usefulness_reasoning
  • category and agents only use the allowed values
  • scope.metrics.by_category counts match context_inventory.files counts
  • No tessl__ paths: no entry's path, links, or linked_from contains the substring tessl__ in any segment.
  • Link edges are symmetric: for every target in a file's links, the target file's linked_from must contain the source path. Drop edges whose target doesn't exist on disk or was filtered out by the ignore globs — do not emit dangling references.
  • No self-loops: a file never appears in its own links or linked_from.
  • Multi-location sanity check: for every .claude/settings.json / .claude/settings.local.json returned by the self-check find, there is at least one hook entry whose path starts with that file. Same for every .mcp.json / .cursor/mcp.json and mcp entries. A nested per-app settings file with zero inventory entries means discovery silently failed — re-run before saving.
  • At least 3 INV insights if the inventory is non-trivial (≥ 10 files); fewer is acceptable for small repos if justified
  • Mark data_source_exclusive: true on INV insights that only this skill could surface (e.g. cross-tool asymmetries grounded in file-by-file inventory)

skills

analyze-context-inventory

README.md

tile.json