npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ts-axi

v0.1.18

Published

AXI-compliant TypeScript symbol CLI for agents - definitions, references, callers, file outlines and change impact (blast radius) with token-efficient TOON output

Downloads

3,606

Readme

ts-axi

Agent-ergonomic TypeScript / JavaScript code CLI, built on the AXI principles: token-efficient TOON output, minimal schemas, pre-computed aggregates, definitive empty states, structured errors and next-step hints.

It is change-impact first. When an agent reviews a diff, the expensive question is "what else does this touch?" - normally answered by grepping and reading dozens of files. ts-axi impact answers it from the compiler's own symbol resolution: for every declaration the diff changes, it lists the references outside the change set, grouped by file, with test coverage counted separately.

# real output: test/integration/sample on a branch that adds a `locale` parameter
# to formatPrice and edits the body of Cart.total
$ ts-axi impact main...HEAD
base: 923fb4d7ddc8f4e5c4df9029be388f8cb29ce095
head: HEAD
changes: 2
changed[2]{symbol,change,at,refs_outside}:
  formatPrice,signature,"src/lib/format.ts:5",12
  Cart.total,body,"src/lib/cart.ts:32",8
refs[20]{symbol,file,line,context}:
  formatPrice,src/components/Counter.tsx,2,"import { formatPrice } from '../lib';"
  formatPrice,src/components/Counter.tsx,12,"{cups} cups = {formatPrice(cups * CUP_CENTS)}"
  formatPrice,src/lib/analytics.js,4,"return { event: 'purchase', value: formatPrice(cents, 'EUR') };"
  ...
  Cart.total,src/components/CartSummary.tsx,10,setTotal(cart.total());
  Cart.total,src/hooks/useCart.ts,35,"total: cart.total(),"
  ...
blind_spots[1]{symbol,file,line,reason}:
  Cart.total,src/components/CartSummary.tsx,16,string key 'total' on a type without that member
elapsed_ms: 679
help[6]:
  - "Run `ts-axi refs src/lib/format.ts:5` for every Reference of formatPrice"
  - "Run `ts-axi callers src/lib/format.ts:5` for its call sites with the enclosing function"
  - "Run `ts-axi def src/lib/format.ts:5` for the changed declaration"
  - Run `ts-axi impact main...HEAD --fields files,tests` for how many files and Test files reference each Symbol
  - Run `ts-axi impact main...HEAD --grep` for textual matches the checker cannot see
  - Run `ts-axi impact main...HEAD --depth 2` for who calls the callers

(... marks rows cut from this README; the command prints all 20. --fields all restores the full row: {symbol,kind,change,exported,at,refs_total,refs_outside,via,files,tests}.)

Read-only on your code: no lint, no refactor, no rename. The only files it writes are .ts-axi.json (config set) and, when you ask for it, your agents' hook config (setup hooks).

Status (v0.1.4)

Early, but already used in real reviews. What the numbers say so far:

  • Correctness on the test corpus. 163 unit and command tests over five synthetic fixtures (a TS project with an interface and three implementations, an Nx-style workspace with tsconfig.base.json paths and a pnpm workspace, a script-style JavaScript tree, a CommonJS package and an ESM package without tsconfig), plus 39 integration tests that run the built binary against a vendored Astro + React one-pager (test/integration/sample: barrel re-exports, JSX islands, custom hooks, a JS consumer of a TS export, a .ts-axi.json test glob). They pin exact line numbers, reference counts, test counts, change classes and the interface split.

  • A real monorepo (Nx, 142 projects, ~5.4k source files after gitignore): refs on a service method finds its two callers in another project in 3-5 s without any flag; impact on a 7-file commit reports 15 changes with cross-project references in ~5 s; callers on a method that implements an interface shared by seven classes reports the 1 direct call plus the spec calls, where the raw compiler API returned 8 unrelated implementations. Before the workspace scope, the same impact ran out of memory.

  • Against grep, in characters an agent has to read (same monorepo, measured by a reviewer session): callers 720 vs ~3.5k for the two greps a reviewer needs - and grep still leaves the interface ambiguity to the human; impact on a commit 4.7k vs ~7.3k for per-symbol greps plus the git show --stat needed to learn the symbol list. def on a very common name was worse than grep until nested git worktrees were excluded (96 matches, 92 of them copies); it is 4 now.

  • Plain JavaScript (no tsconfig, measured on two public packages): on p-limit (ESM, index.js beside index.d.ts) refs pLimit went from 1 to 48 References, 35 of them in test.js, which now counts as a Test file; on express (CommonJS, index.js = module.exports = require('./lib/express')) callers createApplication went from 2 call sites in 2 files to 678 in 94 files, 67 of them Test files (every one of the 22 test files that does require('..') included). The rule: References are semantic wherever the checker can follow them, and every Symbol declared in JavaScript (or in an implicit project) also prints textual: <n> matches in <m> files; when the text names it more often than the checker found, help points at --grep for the rest. refs app.render on express is the honest case: the method sits on an untyped expando object, the checker finds 0 References, and the output says textual: 132 matches in 21 files with the --grep hint instead of a bare 0; impact adds a textual column for such a Change rather than calling it local-only.

Known limits: .astro / .vue / .svelte files are not analysed (they appear under skipped, and --grep searches them textually); every semantic command starts cold (0.3 s on a small project, 2-5 s on the monorepo, dominated by the library .d.ts files that barrels pull in); import specifiers are extracted textually, so computed specifiers and bundler-only aliases are missed.

Token cost

test/integration/token-compare.test.ts does six agent tasks twice: with ts-axi (plus one followed hint where that is the normal next step), and the way a careful agent does them without it - at most 3 git grep -n calls, then a ±40-line numbered read around each relevant match (overlaps merged), with git diff first for impact. Tokens are characters / 4. A recall gate fails when a grep hit in code (not a comment, a string or a doc file) is neither listed by ts-axi nor covered by its textual / templates count.

| task | repo | ts-axi tokens | grep tokens | ratio | recall | | --- | --- | ---: | ---: | ---: | --- | | def of a class method | astro sample | 117 | 266 | 0.44 | 100% (1/1) | | callers of a function | astro sample | 227 | 1169 | 0.19 | 100% (8/8) | | refs of a class member | astro sample | 216 | 1133 | 0.19 | 100% (8/8) | | impact of a signature change | astro sample | 737 | 1304 | 0.56 | 100% (14/14) | | template uses of a directive | legacy-js | 409 | 289 | 1.42 | 100% (5/5) | | refs of an untyped CommonJS member | legacy-js | 387 | 401 | 0.96 | 100% (5/5) |

  • def of a class method is over 0.3 - the class file has 36 lines, so the grep side reads all of it once.
  • impact of a signature change is over 0.3 - the followed hint repeats the first output with the extra rows (refs on the changed function); impact alone is 0.33.
  • template uses of a directive is over 0.3 - the followed hint repeats the first output with the extra rows (--grep); the two matching files have 21 and 4 lines.
  • refs of an untyped CommonJS member is over 0.3 - the followed hint repeats the first output with the extra rows (--grep); the two matching files have 24 and 12 lines.

The fixture files are 5 to 40 lines long, so a ±40-line read is the whole file: the cheapest case for grep, and where a followed hint repeating the first output weighs most. On a real Nx monorepo (143 projects, ~5.4k source files; scripts/token-compare.ts, run locally):

| task | ts-axi tokens | grep tokens | ratio | recall | | --- | ---: | ---: | ---: | --- | | def of a service method | 496 | 1256 | 0.40 | 1/1 | | callers of a service method (96 call sites) | 6587 | 54314 | 0.12 | 96/96 | | refs of a service method with a common name | 4653 | 147944 | 0.03 | 77/77 (244 more hits are other declarations of the name) | | impact of a two-commit fix (4 changed symbols) | 2255 | 23948 | 0.09 | 44/44 (5 in the diff) |

The ts-axi side follows the --limit hint when a list is cut at 50 rows, as an agent would. Most rows past the first 50 are member accesses through a deep-mock mapped type (DeepMocked<Service>) in test files: findReferences returns them as extra groups defined at the method itself, and they count as References (before 0.1.15 they were dropped, and recall was 17/96 and 18/77).

Speed

The same runs count tool calls (each ts-axi command, git grep, git diff and file read is one call) and time the commands. An agent's task time is mostly model round-trips, so the estimate adds an assumed 3 s per call to the measured command time (TURN_MS in test/compare.ts). On the Nx monorepo:

| task | ts-axi calls | grep calls | ts-axi command time | grep command time | est. task time ts-axi | est. task time grep | | --- | ---: | ---: | ---: | ---: | ---: | ---: | | def of a service method | 1 | 3 | 1.4 s | 0.94 s | 4.4 s | 9.9 s | | callers of a service method | 2 | 28 | 11.1 s | 0.20 s | 17.1 s | 84.2 s | | refs of a service method with a common name | 2 | 100 | 6.3 s | 0.27 s | 12.3 s | 300.3 s | | impact of a two-commit fix | 2 | 19 | 8.1 s | 0.22 s | 14.1 s | 57.2 s |

Each ts-axi command is slower than a git grep (every call starts cold and builds a program: 3-6 s per semantic call here), but it replaces dozens of reads. The integration test checks that ts-axi never needs more tool calls than the grep workflow; times are printed, not asserted.

Install

Requires Node.js 20+.

npm install -g ts-axi

Then tell your agent about it - one line in CLAUDE.md / AGENTS.md:

Use `ts-axi` to find definitions, references and callers in TypeScript/JavaScript code, and `ts-axi impact` to see what a diff affects before reviewing it.

Or install one of the two integrations (you only need one):

ts-axi setup hooks                           # SessionStart hook: the project overview as ambient context (Claude Code, Codex, OpenCode)
npx skills add cmosgh/ts-axi --skill ts-axi  # on-demand Agent Skill, no per-session cost

The hook runs ts-axi with no arguments: it reads only the file tree and git status (no compiler run, no network) and exits 0 in any directory. ts-axi setup hooks --project installs it for the current repository only, ts-axi setup status shows what is installed, and ts-axi setup uninstall removes only ts-axi's own entries.

Commands

| Command | What it does | | --- | --- | | ts-axi | Home view: project root, tsconfig used (or implicit), source file count, test-file glob and match counts, git branch and dirty count, next steps. Under a second, exit 0 everywhere | | def <target> | Declarations of a symbol by name (save, User.save, path:line): one match prints detail and body, several print a compact list, none prints did_you_mean and, when the name still occurs in the code, the refs <name> --grep that lists those textual matches. Before saying none, a name that is only a function-local Declaration (local_to: Comp) or only declared in a file the tsconfig leaves out (project: outside (Deno edge/deno.json)) is found syntactically and shown like a match | | refs <target> | References of one symbol, grouped by file, with test counts. A class member that implements an interface (or overrides a base) member counts only its direct References; calls through the interface are via_interface: n (Iface.member), sibling implementations are dropped, and an interface member reports implementations: n. --limit, --kind calls, --via (via rows with a via column), --implementations | | callers <target> | Incoming calls of a function-like symbol, split the same way (via_interface, --via). --depth 1..3 | | outline <file> | Top-level declarations in source order, class members nested: {line,kind,name,signature} ({at,kind,name,signature} over several files). --exports, --fields exported | | impact [<git-range> \| -] | Declarations changed by the working tree (untracked files included; --staged), a git range (main...HEAD) or a unified diff on stdin (bb-axi pr diff 42 --raw \| ts-axi impact -), riskiest first: change class (signature, removed, type, added, body, modified), References outside the change set (direct only), a via count of calls through an interface or base member (--via lists them), test-file counts, notes (a signature change of a member that implements an interface member), blind_spots, local_only, file_level and skipped (non-TS files in the diff). --limit, --depth 2..3, --grep. Rows default to {symbol,change,at,refs_outside}; --fields kind,exported,refs_total,via,files,tests (or all) adds columns. --size adds a size block (files, hunks at zero context, lines added and removed, patch characters, exported signature changes, removed symbols) for routing a review by diff size. Analyses the current checkout | | anchor [<git-range> \| - \| --staged] <file:line[-end]>... | Checks review-comment anchors: a line is commentable when the diff shows it (added or context; removed or context with --side old). Rows are {anchor,ok,reason,symbol}: a reason with the nearest hunk when not, and the enclosing Declaration (Cart.total#method) as a stable key for the finding. Git sources diff at --context 3 (what Bitbucket and GitHub show); a stdin diff is used as given. --fields in_diff,in_file,nearest,change,other_line (or all). Exit 3 when any anchor is not ok, with the full output | | config | Show effective settings with their source; config set <key> <value> and config unset <key> write .ts-axi.json | | setup <hooks\|status\|uninstall> | Opt-in SessionStart hook for Claude Code, Codex and OpenCode that runs the home view at session start. --project for the current repository only |

ts-axi <command> --help prints a concise, complete flag reference. -P <tsconfig> selects a project on any command that may analyse types; --ref <rev> answers def, refs, callers and outline at a git revision (below); --json prints the decoded payload.

A review, end to end

ts-axi                                  # which project, how many files, how many are tests
ts-axi impact main...HEAD               # what the branch changes and who depends on it
ts-axi callers src/lib/format.ts:5      # the risky symbol: every call site with its enclosing function
ts-axi def src/lib/cart.ts:32           # read one declaration, numbered for inline comments
ts-axi outline src/lib/cart.ts          # the rest of the file, without reading it

Real output from the sample project (test/integration/sample):

$ ts-axi callers formatPrice
symbol: formatPrice
kind: function
declared_at: "src/lib/format.ts:5"
refs_total: 7
files: 6
tests: 2
refs[7]{file,line,caller,context}:
  src/components/Counter.tsx,12,Counter,"{cups} cups = {formatPrice(cups * CUP_CENTS)}"
  src/components/ProductCard.tsx,13,ProductCard,"<p>{formatPrice(item.priceCents)}</p>"
  src/lib/analytics.js,4,trackPurchase,"return { event: 'purchase', value: formatPrice(cents, 'EUR') };"
  src/lib/cart.it.ts,8,(top-level),expect(cart.total()).toBe(formatPrice(3700));
  src/lib/cart.ts,34,Cart.total,return formatPrice(cents);
  src/lib/format.test.ts,6,(top-level),expect(formatPrice(1850)).toBe('18.50 USD');
  src/lib/format.test.ts,9,(top-level),"expect(formatPrice(305, 'EUR')).toBe('3.05 EUR');"
elapsed_ms: 549
$ ts-axi def Cart.total
file: src/lib/cart.ts
line: 32
kind: method
exported: true
signature: "total(): string"
body:
---
  32 |   total(): string {
  33 |     const cents = this.items.reduce((sum, i) => sum + i.priceCents * i.qty, 0);
  34 |     return formatPrice(cents);
  35 |   }
---

The line numbers in front of the body are file lines, so an agent can cite or comment on them directly. An ambiguous name lists the candidates and the exact commands that narrow them:

$ ts-axi def parse
symbol: parse
matches[2]{at,kind,signature}:
  "src/lib/parse.ts:1",function,"export function parse(input: string): number"
  "src/lib/query.ts:1",function,"export function parse(q: string): Record<string, string>"
help[4]:
  - "Run `ts-axi def src/lib/parse.ts:1` for the one in src/lib/parse.ts"
  - "Run `ts-axi def src/lib/query.ts:1` for the one in src/lib/query.ts"
  ...

Anchoring review comments

An inline comment only lands when the review tool shows its line in the diff. Check the anchors against the diff the tool renders before posting:

bb-axi pr diff 42 --raw | ts-axi anchor - src/a.ts:12 src/b.ts:40-44
base: stdin diff
head: working tree
side: new
anchors[2]{anchor,ok,reason,symbol}:
  "src/a.ts:12",true,"-",Cart.total#method
  "src/b.ts:40-44",false,not in diff - nearest 51-58,formatPrice#function
invalid: 1
help[3]:
  - "Move src/b.ts:40-44 into src/b.ts:51-58 or post it as a general comment"
  - exit 3 (ANCHOR_INVALID) - 1 of 2 anchors cannot be commented; fix or drop them before posting
  - "Run `<diff> | ts-axi anchor - src/b.ts:40-44 --fields all` for in_diff, in_file, nearest, change and other_line"

The exit code is 3 when any anchor is not commentable, so a script can gate on it. --side old checks lines of the before file (comments on removed lines); a new-side anchor on a removed line says removed line - use --side old. With a stdin diff, symbol and in_file come from the working tree, so check out the PR head first; a mismatch is reported as a warning.

Interfaces are not conflated

findReferences in the compiler resolves a call like parser.parse(raw) (with parser: FieldParser) to every implementation of parse, and lists the other implementations' declarations as references. For a review that inflates the blast radius badly. ts-axi keeps the split:

$ ts-axi callers DateParser.parse
symbol: DateParser.parse
refs_total: 1
via_interface: 1 (FieldParser.parse)
refs[1]{file,line,caller,context}:
  src/services/dates.ts,4,dayOf,return new DateParser().parse(x);

$ ts-axi refs FieldParser.parse
refs_total: 1
via_implementations: 1 (DateParser.parse)
implementations: 3
refs[1]{file,line,context}:
  src/parsers/registry.ts,5,return parser.parse(raw);

Script-style JavaScript

$ ts-axi outline client/app.js --fields exported
decls[4]{line,kind,name,exported,signature}:
  6,function,CartCtrl,true,"controller CartCtrl($scope, priceService)"
  12,function,priceService,true,service priceService()
  13,method,priceService.sum,true,this.sum = function (items)
  17,function,cartBadge,true,directive cartBadge()

An edit inside CartCtrl is reported by impact as CartCtrl body, not as a file-level change. Names registered by a string have no identifier the checker can follow; refs says so and offers --grep. A registration that names a same-file function (.directive('fancyBox', fancyBox), ['$uibModal', fancyBox]) links the two, so refs on the function reports the registration's template usages too. Function-valued members of an object a function returns (return { canReadItem: async () => ... }) are Declarations (makeItemDeps.canReadItem), so def canReadItem finds them; their textual: count covers the whole project, while a real local (a parameter, an IIFE helper, a CommonJS binding no module.exports hands out) says in this file. ui-router and ngRoute states are Declarations of kind state (states.cart, with callbacks as states.cart.resolve.items), so an edit inside a resolve names that callback; state objects in exported arrays (module.exports = [{ name: `${base}.init`, url, resolve }]) count too, their template-literal names filled from same-file consts. Directives, components, their scope/bindings keys, controllers and filters also get templates: <n> usages in <m> files (kebab-case elements and attributes, ng-controller, | filter in .html/.htm/.ejs/.hbs/.njk/.pug), a templates column in impact, and a changed template that uses a changed directive or binding is listed under template_changes instead of skipped.

Monorepos

No configuration and no new files in your repo: ts-axi detects the layout from what is already there - nx.json (projects = project.json dirs, or a legacy workspace.json), pnpm-workspace.yaml, package.json workspaces, or a root tsconfig.json that only holds references. The home view prints workspace: nx (14 projects).

Without -P, refs, callers and impact do not type-check the whole repo. They build the program from a subgraph: the declaring file (for impact, the touched files) plus every file that imports it, found by a fast text scan of import specifiers that follows tsconfig.base.json paths, workspace package names and export * from barrels. A caller in another project is found; the cost tracks the subgraph, not the repo. Each result says what was analysed:

scope: nx workspace - 30 of 5433 files (seeds 1; importers 29)

scope_partial: true means the importer scan stopped at its cap (50k files or 3 s); the help line says what is missing. -P <tsconfig> still analyses exactly one project; when it finds nothing, found_outside_project lists the importers outside it. A -P tsconfig that only holds references (an Nx project's tsconfig.json) is expanded to the configs it references: tsconfig: libs/shared/tsconfig.json (2 referenced configs).

Files are listed with git ls-files (tracked plus untracked, gitignore applied), so nested worktrees and ignored copies are never analysed. Outside git, a directory walk skips node_modules, build output, .claude, .cursor, .idea, .vscode and any nested checkout.

Reading another revision (--ref)

def, refs, callers and outline take --ref <rev> (a branch, tag, origin/main, HEAD~3, a SHA) and answer from that commit's tree, read straight from git objects. Nothing is checked out, written or fetched, and the working tree has no effect on the answer: a triage agent can ask about the deployed code while the checkout is on another branch or dirty.

ts-axi callers Cart.total --ref origin/main
ref: origin/main @ 5474e0f
symbol: Cart.total
...
help[1]:
  - Run `ts-axi refs src/lib/cart.ts:32 --ref origin/main` for every Reference (not only calls)
  • The rev is resolved once to a SHA (ref: <rev> @ <sha>), so a git fetch during the run cannot mix versions. Paths in the output look as they do without --ref, and every follow-up hint keeps --ref.
  • A file that is not in the revision is absent even if it exists locally, and a file deleted locally is still there: file not in origin/main: src/new.ts.
  • node_modules is not in git, so dependency types come from the working tree; the output says node_modules: working tree (not in git) when it read any.
  • A partial clone never fetches: a blob that is not available locally is a GIT_ERROR (git fetch or a full clone fixes it). Fetch the ref yourself first if it may be stale.
  • .ts-axi.json is read from the working tree, like --test-glob.
  • impact already compares git revisions (ts-axi impact origin/main...HEAD) and rejects --ref, as do config and setup.

Cost: the tree listing and one batched blob read add about 0.1 s on a small repo and 0.1-0.3 s on a ~5k-file Nx monorepo.

Test files

A file counts as a test by path pattern. Defaults: **/*.test.*, **/*.spec.*, **/__tests__/**, **/test/**, **/tests/**, **/e2e/**, **/spec/**, **/test.*, **/*_test.*. The home view shows test_globs and test_files so a wrong rule is visible.

.ts-axi.json

Optional, at the git root (or the current directory outside a repository). Manage it with ts-axi config, or edit it by hand:

{ "testGlobs": ["**/*.it.ts", "packages/*/qa/**"], "testGlobsReplace": false, "project": "tsconfig.app.json" }

| Key | Meaning | | --- | --- | | testGlobs | Extra test-file globs, added to the defaults | | testGlobsReplace | true makes testGlobs replace the defaults | | project | Default tsconfig, relative to the repo root; -P overrides it |

Any other key is a VALIDATION_ERROR naming the valid ones. --test-glob <glob> adds a glob for one run.

How it works

One dependency: the typescript compiler API (ADR in docs/adr/0001-typescript-language-service-engine.md).

  • outline, def and the home view only parse files (ts.createSourceFile): no tsconfig needed, well under a second.
  • refs, callers and impact build a LanguageService over the nearest tsconfig.json (or an implicit project of every source file when none exists, with a warning; in a monorepo or an implicit project of more than 3000 files, over the importer subgraph described above) and ask it for references and call hierarchy. Symbol identity comes from the checker, so user.save resolves to User.save through imports and re-exports.
  • impact reads the "before" version of touched files with git show and compares signatures to classify each change (added, removed, signature, body, type, modified). Uses it cannot follow (access through any, dynamic string keys) are reported as blind_spots, never silently dropped.
  • Module resolution keeps TypeScript's rules with two substitutions so References reach implementations: a .d.ts that has a JavaScript twin in the Project (index.d.ts beside index.js) resolves to the twin, and a CommonJS shim whose only statement is module.exports = require('./x') resolves to ./x, so require('..') reaches the real function. A .d.ts twin that still ends up in the program is queried too and its References merged.
  • Script-style JavaScript (AngularJS registrations with their this / var self = this members and factory objects, ui-router states, Meteor methods and helpers, module.exports objects, prototype methods, IIFE helpers) gets nested declarations with qualified names, so outline lists them and impact names the function an edit touches instead of a file-level change.
  • Type errors in the analysed project never block anything; they are reported as a diagnostics count.

Every semantic command starts cold and prints elapsed_ms.

Development

corepack pnpm install
corepack pnpm run check   # typecheck, tests, build, skill freshness

skills/ts-axi/SKILL.md is generated: corepack pnpm run build:skill. See PLAN.md for the spec, GLOSSARY.md for the vocabulary and docs/adr/ for the design decisions (TypeScript LanguageService over ts-morph / tree-sitter; importer subgraph over a configured project list; --ref over git objects instead of a worktree).

Tests

| Suite | Runs | Covers | | --- | --- | --- | | test/unit.test.ts, test/workspace.test.ts | pnpm test | target parsing, signatures, change classification, diff parsing, settings, glob matching, reference classification, workspace detection, import resolution | | test/commands.test.ts | pnpm test | the whole CLI through main() over five fixtures copied to temp git repos: test/fixtures/project (TS, interface + implementations, barrel, JS consumer, any access), test/fixtures/workspace (Nx + pnpm workspace, paths, references-only tsconfigs), test/fixtures/legacy-js (AngularJS, Meteor, prototype, IIFE, CommonJS), test/fixtures/commonjs (require('..') shim, expando methods, .d.ts twin), test/fixtures/esm-js ("type": "module", index.js + index.d.ts) | | test/ref.test.ts, test/ref-leak.test.ts, test/gittree.test.ts | pnpm test | --ref: answers on a poisoned working tree equal a clean checkout (four fixtures), no read below the repo root bypasses the git tree (node:fs and ts.sys spied), tree listing, blob batches, symlinks, submodules, bad revisions | | test/integration/*.test.ts | pnpm run test:integration | the built binary against the vendored Astro + React sample in test/integration/sample (installs its own dependencies once; copied to a temp git repo per run, see test/integration/README.md) |

CI runs the first two on Node 22 and 24, the built CLI on Node 20, and the integration suite on Node 24.

Other AXI tools

  • atl-axi: Atlassian Jira Cloud CLI for agents
  • bb-axi: Bitbucket Cloud CLI for agents
  • hetzner-axi: Hetzner Cloud CLI for agents
  • mongo-axi: Read-only MongoDB CLI for agents
  • mongo-rw-axi: Write-capable MongoDB CLI for agents, loopback hosts only
  • mysql-axi: Read-only MySQL CLI for agents
  • mysql-rw-axi: Write-capable MySQL CLI for agents, loopback hosts only

License

MIT