Skip to content

Repository files navigation

Ankui

Local-only inventory of what your AI coding tools can access.

Landing page: ankui.vercel.app

What Ankui is

AI coding tools accumulate configuration, skills, rules, and MCP servers across your filesystem, and it is easy to lose track of what each one can access. Ankui is a local-first scanner and terminal UI that inventories those resources and surfaces access findings. It never executes user code, follows remote URLs, or sends scan data anywhere. Scans are read-only. Writes happen only after an explicit user action: managing Ankui-owned snapshots/bundles/settings, or applying a confirmed reversible skill enable/disable action. Supported tools: Claude, Codex, Cursor, Gemini, OpenCode, Antigravity, and skills.sh.

Community edition scope

This public open-source repository contains the local, single-device, local-first Ankui scanner/UI. It does not include a hosted control plane, centralized org inventory, endpoint fleet management, remote policy management, centralized audit/evidence retention, SSO, SIEM integrations, or runtime enforcement.

See SECURITY.md, CONTRIBUTING.md, and BRAND.md for project policy details.

Try it

npx -y ankui@latest

For a persistent install:

npm install -g ankui
ankui

Requires Node >= 20.

Quick start

Running ankui with no arguments opens the interactive terminal UI. Running ankui scan prints a summary to stdout:

Ankui scan complete

Detected tools: 6
MCP servers: 8 configured, 6 unique
Agent skills: 178
Commands/prompts/agents/rules/tools: 19
Memory files: 0
Access findings: 14
Warnings: 1

Tools:
✓ Claude    1 MCP · 49 agent skills · 3 rules · 7 plugins · 3 findings
✓ Codex     3 MCP · 58 agent skills · 1 rules · 5 findings
✓ Cursor    1 MCP · 1 findings
✓ Gemini    1 MCP · 47 agent skills · 4 plugins · 3 findings
✓ OpenCode  detected
✓ Antigravity 2 MCP · 24 agent skills · 4 plugins · 2 findings
- skills.sh not detected

Add --json to get the full sanitized scan result as JSON:

ankui --json | jq '[.tools[] | select(.id == "claude") | .skills[]] | length'

.tools is an array of { id, skills, findings, ... } objects, not an object keyed by tool id.

How counts work

Each item Ankui reports falls into exactly one kind, and the buckets do not overlap. The distinction worth knowing up front:

  • agent_skill counts your first-party SKILL.md directories — the ones living directly under ~/.{tool}/skills/ (and per-project .{tool}/skills/). One row per skill directory.
  • plugins counts the plugin/extension entries that are enabled — Claude plugins listed in settings.json, Gemini extensions under ~/.gemini/extensions/, Antigravity plugins under ~/.gemini/antigravity-cli/plugins/. Each plugin appears as exactly one row, regardless of how many SKILL.md files it bundles inside. Ankui treats a plugin as a single trust boundary — the decision you made was to enable the plugin; the skills it ships with are the plugin author's choice and belong under that one entry.

So a per-tool line like 49 agent skills · 7 plugins for Claude means 49 first-party skills under ~/.claude/skills/ plus 7 enabled plugins. The SKILL.md files inside those 7 plugins (often several hundred on a heavily-pluginned machine) are intentionally not added to the 49 — they live under each plugin entry. The same applies to Gemini extensions and Antigravity plugins.

The other kinds (mcp_server, custom_agents, custom_commands, custom_prompts, rules, memory_file) each map one-to-one to the files or config entries that define them.

Managing skills (enable / disable)

From the Actions tab — in the TUI or the ankui web browser UI — you can turn individual markdown-backed skills off or back on. Changes are staged in the UI and only written when you save them — nothing touches disk until then.

  • / selects a skill. [d] stages a disable, [e] stages an enable. The row marker flips immediately ( enabled / disabled) like a checkbox, and every staged change is listed under Pending (unsaved) in the right panel. Toggling a skill back to its on-disk state removes its pending entry.
  • [s] saves. Each pending change moves the skill directory between <skills>/<name>/ and <skills>/.disabled/<name>/. Saved skills move to Saved this session; any that fail stay pending with the reason shown. The move is a single directory rename and is refused if it would escape $HOME/$CWD, if the source is missing, or if it would overwrite an existing directory. Nothing is read, modified, or deleted.
  • Quitting with unsaved changes prompts a confirm: [s] save first, [q] discard & quit, [esc] cancel. On exit, Ankui prints a net summary of what was actually saved this session — toggles that cancel out, or staged changes you discarded, are omitted because nothing changed on disk.
  • Disabled skills still appear in scans (marked disabled), so the inventory stays honest.

Only agent_skill and skills.sh skills (a directory with SKILL.md) at user scope are eligible. MCP servers, config entries, and built-in defaults are never written.

Managing dev roots (Settings)

The Settings tab — in the TUI (bottom row, alongside Actions) and the ankui web browser UI — manages the dev-root list in ~/.config/ankui/config.json that powers ankui scan-all and the per-project rows on every other tab. Add a new root by typing its path; remove one with [d] in the TUI (or the row's delete control in web). The panel also shows last scan · YYYY-MM-DD HH:MM · N skills. Changes are written atomically via POST /api/config (web) or directly to the config file (TUI); the next scan picks up the new roots immediately. The web endpoint uses optimistic concurrency — if the on-disk config has drifted (e.g. a CLI run rewrote it), the request gets a 409 with the fresh scan and the UI re-applies your edit on top of it.

Privacy and safety

Ankui sends no data anywhere and calls no external APIs.

Scans are read-only. Ankui writes only after an explicit user action: its own settings, tracked bundle state, and semantic snapshots live under ~/.config/ankui/ or ~/.ankui/; confirmed Actions-tab changes move an eligible skill directory into or out of a sibling .disabled/ folder. Snapshot files never contain raw previews or secret values. Skill moves remain reversible, never overwrite, and are refused if they would escape $HOME/$CWD, clobber an existing directory, or act on a missing source.

The ankui web server is loopback-only. It binds 127.0.0.1, generates a fresh per-session token, and rejects any /api/* request without it; write requests additionally require an Origin header matching the server's own origin, and every request must carry a Host header pointing at a loopback alias of the bound origin (any mismatch is rejected with 421 Misdirected Request, which defeats DNS-rebinding attacks). It serves only local files and calls no external APIs.

Sensitive files skipped. Any file matching these patterns is skipped with a sensitive_file_skipped warning rather than read:

  • .env and any file starting with .env
  • Files containing token, secret, or credential in the name
  • Files starting with auth, cookies, or session
  • Files ending with .pem or .key
  • Files containing apikey or api_key
  • Files starting with private_key

Sensitive directories skipped. Any path containing these directory segments is never entered:

  • Common across all tools: sessions, session, history, histories, conversation, conversations
  • Additional for OpenCode paths: auth, log, logs, share, cache, database, databases, db, runtime

For Antigravity specifically, the adapter opens only known files (settings.json, per-plugin plugin.json / mcp_config.json, SKILL.md files inside skills/ directories, mcp_config.json in the legacy location). The sensitive sibling files inside ~/.gemini/antigravity-cli/conversations/, brain/, implicit/, knowledge/, log/, cache/, bin/, updater/, cli.log, history.jsonl — are never enumerated or read.

File size cap. Files larger than 1 MB (MAX_SAFE_FILE_BYTES = 1024 * 1024) are skipped with a file_too_large warning.

Symlinks. Symlinks whose resolved targets fall inside $HOME or $CWD are followed and reported with details.linked: true and details.linkTarget. Symlinks pointing outside those roots, or whose resolved target hits a sensitive path segment, produce a symlink_skipped warning and are not read.

Secret masking. MCP server env blocks have all values replaced with ....... Any value under a key matching auth, authorization, token, secret, credential, password, passwd, apikey, private_key, access_token, refresh_token, auth_token, or client_secret is masked before the result is returned. Credential URLs (Basic auth username/password) are masked in MCP command args.

Session, history, auth, log, and database files are never read. The directory exclusions above cover OpenCode's runtime database, session store, share store, auth store, and log directories. No conversation data or model response history is ever accessed.

Supported tools

Tool User-scope paths Project-scope paths
Claude ~/.claude/ (settings, skills, commands, agents), ~/.claude.json .claude/ (settings, skills, commands, agents), CLAUDE.md, CLAUDE.local.md, .mcp.json
Codex ~/.codex/config.toml (MCP servers), ~/.codex/prompts/ (custom prompts), ~/.codex/skills/ (agent skills), ~/.codex/rules/ (permission rules) .codex/config.toml, .codex/prompts/, .codex/skills/, AGENTS.md
Cursor ~/.cursor/mcp.json (MCP servers), ~/.cursor/rules/ (.mdc rules files) .cursor/mcp.json, .cursor/rules/, .mcp.json, .cursorrules
Gemini ~/.gemini/settings.json (MCP servers), ~/.gemini/commands/ (custom commands), ~/.gemini/skills/ (agent skills), ~/.gemini/extensions/ (extensions) .gemini/commands/, .gemini/skills/, GEMINI.md
OpenCode ~/.config/opencode/ (agents, commands, tools, skills) opencode.json, opencode.jsonc (MCP servers, plugins, tool permissions), .opencode/ (agents, commands, tools, skills), AGENTS.md
Antigravity ~/.antigravity/skills/ (IDE skills), ~/.gemini/antigravity-cli/settings.json (MCP servers), ~/.gemini/antigravity-cli/plugins/<name>/{plugin.json, mcp_config.json, skills/}, ~/.gemini/antigravity/mcp_config.json (legacy) AGENTS.md
skills.sh ~/.skills/, ~/.config/skills/ .skills/

Each tool adapter reads only from the paths listed above. All access goes through the safety layer described in the previous section.

CLI commands

Global flags that apply to all commands:

  • --json — print the full sanitized result as JSON instead of human-readable output.
  • --no-color — disable ANSI colour codes (useful in CI or when piping output).

ankui scan

Run a local scan and print a summary.

Usage: ankui scan [options]

Run a local scan and print a summary.

Options:
  --show-builtins  include CLI-bundled built-in skill defaults

--show-builtins injects the opt-in built-in skill registry — the CLI-bundled defaults that ship inside Claude, Codex, and Gemini. Without the flag, no built-in records appear; this flag exists only on ankui scan. Otherwise the command reads user-scope and project-scope paths for all six tools and prints the counts shown in the Quick start section above.


ankui tui

Open the interactive terminal UI.

Usage: ankui tui [options]

Open the interactive terminal UI.

ankui with no arguments is equivalent to ankui tui. On first launch (before ~/.config/ankui/config.json exists), the first-run wizard runs to let you pick dev roots. After that, the full multi-project TUI opens. See the TUI keybindings section below.


ankui watch

Open the TUI and live-rescan when config files change.

Usage: ankui watch [options]

Open the TUI and live-rescan when config files change.

Uses chokidar to watch known tool directories and AI-project files under registered dev roots. File changes are debounced (300 ms stabilisation threshold). Sensitive directories are excluded from the watch list. Press q or Ctrl-C to quit.


ankui web

Open a local browser UI.

Usage: ankui web [options]

Open a local browser UI.

Options:
  --port <port>  preferred port (default: 7373)
  --no-open      do not open the browser automatically

Starts a loopback HTTP server bound to 127.0.0.1 and opens your default browser at it. The single-page app has eight tabs — Overview, Changes, Tools, MCPs, Access, Doctor, Actions, Settings. Changes manages local semantic snapshots; Actions applies skill enable/disable through the same staged-changes + save flow as the TUI. --port sets the preferred port; if it is taken the server tries the next free port up to +20. --no-open skips launching the browser. Runs until Ctrl-C.

$ ankui web

  Ankui web UI  http://127.0.0.1:7373
  local files only · scans are read-only · saved skill toggles are reversible · Ctrl+C to stop

The server binds 127.0.0.1 only and generates a fresh per-session token. Every /api/* request must carry that token in an x-ankui-token header, and write requests (POST /api/actions, POST /api/config) additionally require an Origin header matching the server's own origin — this defeats localhost CSRF from other browser tabs. A DNS-rebinding guard rejects any request whose Host header isn't a loopback alias of the bound origin (421 Misdirected Request). It serves only local files; nothing is sent anywhere.


ankui access

Print findings and review recommendations from the scan.

Usage: ankui access [options]

Print findings and review recommendations from the scan.

Findings are grouped by category and ordered by severity: broad_access_capability (broad-access MCP servers), dangerous_pattern (skills containing curl | sh, rm -rf, or similar patterns), secret_reference (MCP servers with secret-bearing env keys), unknown_capability (MCP servers not in the built-in catalog), and duplicate_mcp (the same MCP server configured across multiple tools).

Every finding also carries a high, medium, or low severity so Access views can put the most review-worthy items first. High covers dangerous command patterns and broad-access MCPs; medium covers secret references and uncatalogued MCPs; low covers duplicate MCP configurations.

Example output structure:

Ankui Access Review
───────────────────
Findings      10
Severity      6 high · 2 medium · 2 low
Mix           6 dangerous_pattern · 2 duplicate_mcp · 1 secret_reference · 1 unknown_capability

Duplicate MCP servers (2)
─────────────────────────
○ [LOW] Reddit MCP is configured in 2 tools
  Scope: cross_tool · Tools: codex, gemini
  ...

ankui mcp

Print a cross-tool MCP server overview.

Usage: ankui mcp [options]

Print a cross-tool MCP server overview.

Groups all configured MCP servers by canonical name. Shows capability category, access level, which tools have the server configured, and whether any configuration carries secret-bearing env keys. Cross-tool duplicates are flagged with a warning line.


ankui caps

Print MCP capability categories overview.

Usage: ankui caps [options]

Print MCP capability categories overview.

Lists classified MCP servers grouped by capability category (network, communication, filesystem, etc.) in descending order of count, then prints a footer with the count of markdown-backed skills that are not categorised in this view. Unknown MCP servers (not in the built-in catalog) are not included in the category groups.


ankui doctor

Print detection status and scanner warnings.

Usage: ankui doctor [options]

Print detection status and scanner warnings.

Shows which tools were detected and which paths each tool found at user scope and project scope. After the tool list, warnings are grouped by reason (sensitive_file_skipped, symlink_skipped, file_too_large, permission_denied, parse_error, timeout). A clean machine ends with No warnings.


ankui scan-all

Run scans across every project in every registered dev root.

Usage: ankui scan-all [options]

Run scans across every project in every registered dev root.

Reads dev roots from ~/.config/ankui/config.json (written by ankui discover --apply or the first-run TUI wizard). Scans up to 10 projects in parallel with a 5-second per-project timeout. Output shows user-scope skill counts followed by a per-project table.

Example:

Ankui multi-project scan — 16 projects across 3 dev roots, 178 user-scope skills

User scope
──────────
✓ Claude    63 skills
✓ Codex     62 skills
...

Projects (16)
─────────────
~/Developer/Ceto's Projects/ankui    1 skills · 0 findings
...

ankui snapshot and ankui diff

Capture and compare local semantic inventory baselines.

ankui snapshot --label "before tool upgrade"
ankui snapshot list
ankui diff
ankui diff --from <snapshot-id> --to <snapshot-id-or-current>
ankui snapshot delete <snapshot-id> --yes

Snapshots cover user scope and every project under registered dev roots. They are explicit — normal scans, watch mode, and UI refreshes never move the baseline. ankui diff defaults to the latest snapshot versus a fresh current scan and reports added, removed, and field-level modified tools, skills, MCPs, findings, and warnings. IDs may be supplied in full or as an unambiguous prefix.

Snapshot files live under ~/.ankui/snapshots/; only the newest 30 valid snapshots are retained. They store normalized semantic fields and masked MCP configuration details, but never raw skill previews, raw file contents, or secret values. Adapter timeouts and other transient warnings are reported separately as scan-health changes and do not count as configuration drift. Pass global --json for machine-readable metadata or diff output.

The TUI and ankui web Changes views expose the same baseline picker and semantic diff. TUI shortcuts are Tab to switch From/To, / to select, [n] to capture, and [d] followed by [y] to delete.


ankui report

Export a shareable sanitized Markdown report.

Usage: ankui report [options]

Export a shareable sanitized Markdown report.

Options:
  --output <file>  write the report to a new file instead of stdout

The report uses the same multi-project scan path as ankui scan-all, then renders an executive Markdown summary with tool counts, MCP counts, finding severity totals, access findings, grouped warnings, and recommendations. It is designed for sharing: absolute paths, home/cwd/dev-root/project names, raw skill previews/details, and secret-like environment variable names are omitted or replaced with stable placeholders such as <HOME>, <PROJECT_1>, and <PATH_1>.

Pass global --json to emit the sanitized report model instead of raw scan JSON:

ankui --json report
ankui report --output ankui-report.md

The ankui web Overview screen also includes an Export report button that downloads the same Markdown format from the currently loaded scan.


ankui list

List skills, optionally filtered by --kind and --tool.

Usage: ankui list [options]

List skills, optionally filtered by --kind and --tool.

Options:
  --kind <kind>  filter by skill kind (e.g., mcp_server, agent_skill)
  --tool <tool>  filter by tool id (e.g., claude, codex)

Valid skill kinds include mcp_server, agent_skill, custom_agents, custom_commands, custom_prompts, custom_tools, memory_file, rules, plugins, skills_sh_skill. Valid tool ids are claude, codex, cursor, gemini, opencode, antigravity, skills-sh. An invalid filter value exits with code 1 and a usage message.


ankui show <tool>

Print one tool's detected paths and skills.

Usage: ankui show [options] <tool>

Print one tool's detected paths and skills.

Prints detected paths grouped by scope (user vs project), then all skills grouped by kind in canonical order. An unknown tool id exits with code 1 and lists the valid ids.

Example:

Ankui — claude

Detected at:
  user:
    ~/.claude
    ~/.claude.json
  project:
    ./.claude
    ./.claude/settings.local.json

mcp_server (1)
──────────────
  expo-mcp    ~/.claude.json

agent_skill (49)
────────────────
  autoplan    ~/.claude/skills/autoplan/SKILL.md
  ...

ankui discover

Crawl ~ for AI projects and propose dev roots for ~/.config/ankui/config.json.

Usage: ankui discover [options]

Crawl ~ for AI projects and propose dev roots for ~/.config/ankui/config.json.

Options:
  --apply  write the default-ON dev roots into the config file (default: false)

Crawls your home directory (max depth 6, concurrency 16) for directories that contain AI-tool marker files or directories. Groups found projects by parent directory into dev-root candidates. Parents with 3 or more AI projects are marked default-ON. Prints a dry-run summary by default; pass --apply to merge the selected roots into ~/.config/ankui/config.json. A second --apply with the same roots is a no-op.


TUI keybindings

The sidebar has a Tools section (Overview plus one entry per tool) and a Views section containing MCPs · Access · Doctor · Actions · Bundles · Settings · Changes. Use / while the sidebar is focused, then or Enter to focus the selected panel.

Key Action
/ Move between sidebar and active panel
/ Scroll the skill list in a drill-in screen
Enter Drill into the selected item
Esc / Backspace Drill out (return to the previous screen)
/ Open incremental search (drill-in screens only)
[d] / [e] Stage a disable / enable for the selected skill — UI only until [s] (Actions tab)
[s] Save staged skill changes to disk (Actions tab)
Tab Switch From/To selector (Changes tab)
[n] / [d] Create / request deletion of a snapshot (Changes tab)
r Rescan (refresh data from disk)
q Quit (if there are unsaved staged changes, prompts [s] save / [q] discard / [esc] cancel; prints the net saved-this-session summary)

Tab is scoped to the Changes panel and does not cycle application tabs.

Local development

npm install
npm run typecheck      # TypeScript strict-mode check (no emit)
npm test               # runs the node:test + tsx suite
npm run build          # two-stage build (see below)
node dist/cli.js scan  # smoke test against your real local config

Tests use the built-in node:test runner via tsx. Do not add Jest or Vitest unless explicitly required.

npm run build runs two stages: build:cli (tsc — the Node CLI, emitted to dist/) then build:web (Vite — the ankui web single-page app, emitted to dist/web-ui/). For front-end work, npm run dev:web runs the Vite dev server with hot reload.

After a build, use node dist/cli.js <command> for local testing. All commands support --json for machine-readable output.

Contributing an adapter

Adapters live in src/scanner/adapters/. Each adapter exports a ScannerAdapter object with a toolId and an async scan(context) method.

Invariants for any new adapter:

  • Use safeReadOptions(filePath, context) from shared.ts for every file read. This enforces the symlink allowlist and file size cap consistently across all adapters.
  • Never throw. Wrap all I/O in try/catch or rely on the safe helpers; produce Warning objects for every failure and attach them to the adapter result. The adapter runner isolates each adapter with a 1-second timeout, but adapters should still handle their own errors gracefully.
  • Respect the 1 MB file size cap (MAX_SAFE_FILE_BYTES from safety.ts) and the 1-second per-adapter time budget.
  • For markdown-backed skill paths (skills with SKILL.md or similar), call await buildLinkDetails(filePath, context) from shared.ts and spread the result into the details object. This ensures symlink metadata (linked, linkTarget) is recorded on every markdown skill.
  • Add the adapter to src/scanner/adapters/index.ts so the scanner runner picks it up.

The scanner invariant is: every file or directory access goes through src/scanner/safety.ts. No adapter should call fs.readFile or fs.readdir directly.

MIT

About

Remember what your AI agents can access

Resources

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages