{"slug": "show-hn-codelens-ai-augmented-code-review-tool", "title": "Show HN: CodeLens AI-Augmented Code Review Tool", "summary": "A developer released CodeLens, an AI-augmented code review tool distributed as the npm package @blong8334/codelens, which turns a pull request into a structured brief of risks and reading order and then walks reviewers through changes in a review queue. CodeLens parses 15 languages with Tree-sitter, adds TypeScript compiler type information for TypeScript and JavaScript, and supports Claude via a Claude Code subscription or API key, any OpenAI-compatible API, or a local model through Ollama, falling back to heuristics without a model. The tool requires Node.js 22.13 or later and git, and caches analysis in a content-keyed SQLite or Postgres store so re-reviewing after a push only pays for what changed.", "body_md": "**AI is writing more of our code. Someone still has to review it.**\n\nCoding agents can open a 40-file pull request in minutes. Reviewing it still means reading line diffs in alphabetical file order and reconstructing, in your head, what changed, what calls it, and what could break. Review has become the bottleneck, and the place where AI-written bugs slip through.\n\nCodeLens is built to make review keep up. It turns a pull request into something you can understand quickly and then check thoroughly. It opens the PR (or a local branch) as a **brief**: what the change does and why, concrete risks, things to check, and where to start reading. Then you work through it in a **review queue**, one change at a time. Each step shows:\n\n- the change's own diff, with comments inline;\n- who calls it, and which callers are outside the change;\n- which routes, CLIs and jobs reach it;\n- which databases and APIs it touches.\n\nPress `a` to accept and move on. The **map** of callers, callees and effects is one key away.\n\nThe model writes the explanations. The checks come from the code itself: the call graph, who depends on each change, broken signatures, and what each change touches. Your judgment is spent where it matters, and nothing is accepted until you accept it.\n\nIt also **explores** whole codebases: a written guide to an unfamiliar package, a map of its systems and modules, entry points, and hotspots.\n\n**How it works:**\n\n- **15 languages** , parsed with Tree-sitter. TypeScript and JavaScript also get type information from the TypeScript compiler.\n- **Bring your own model:** Claude (via your Claude Code subscription or the API), any OpenAI-compatible API, or a local model through Ollama. Without one it falls back to heuristics.\n- **A content-keyed SQLite or Postgres store** , so re-reviewing after a push only pays for what changed.\n\n|  **The brief** : the author's description beside the model's take, risks, things to watch, reading order, files to check off |  **The diff** , whole file, unified or side by side, comments inline, click any line to comment | \n|  **The map** : callers to the left, callees and external effects to the right, grouped by function, file, module or system |  **Explore** any package: a guide written from its README and code, with a reading order | \n\nReview any GitHub pull request, with no install. The brief, risks and explanations are written by the model you choose:\n\n```\n# With your Claude Code subscription (no API key needed)\nnpx @blong8334/codelens pr https://github.com/owner/repo/pull/123 -p claude-code\n\n# With an Anthropic API key\nANTHROPIC_API_KEY=… npx @blong8334/codelens pr <url> -p claude\n\n# With OpenAI, or any OpenAI-compatible endpoint such as a local Ollama\nOPENAI_API_KEY=… npx @blong8334/codelens pr <url> -p openai -m gpt-4.1-mini\nnpx @blong8334/codelens pr <url> -p openai -m llama3.1 --base-url http://localhost:11434/v1\n\n# No model: offline analysis only (call graph, contracts, risks), with plainer write-ups\nnpx @blong8334/codelens pr <url>\n```\n\nOr install the `codelens` command: `npm install -g @blong8334/codelens`, then for example `codelens explore ~/code/some-repo -p claude-code --open`.\n\nRequires Node.js 22.13 or later and git. The first run with a model takes a minute or two on a large PR. After that, everything is cached by content, so later runs only analyze what changed.\n\n```\nnpm install\nnpm run demo        # builds examples/demo-shop, analyzes its last commit, opens the canvas\n```\n\nThen open [http://localhost:5173](http://localhost:5173).\n\n| Gesture | Does | \n|---|---|\n| click | open the card in the side panel | \n| double-click a function | re-center the map on it (breadcrumbs track the path) | \n| double-click a group | open it in place; double-click again to fold it | \n| click `+N more` | expand a capped column | \n| double-click `+N more files in …` | show everything folded into that card (overview) | \n| `−` /`+` | zoom detail out / in | \n| `←` /`→` | previous / next change in review order (entry point in explore mode) | \n| `/` | search functions, files, and modules | \n| `o` | zoom out to the overview | \n| `1` –`3` ,`Esc` ,`Backspace` | switch tab, clear selection, go back | \n\n```\n# Map any package, repository, or plain folder of code and open it in the canvas\nnpm run explore -- ~/code/some-repo --open\n\n# Just one package of a monorepo (calls into the rest of the repo stay as one hop of context)\nnpm run explore -- ~/code/some-repo/services/billing --open\n\n# With AI descriptions through your Claude Code subscription (cached: you pay once per function)\nnpm run explore -- ~/code/some-repo -p claude-code --open\n\n# Your own model: any OpenAI-compatible API, or a local one (see Enrichment below)\nnpm run explore -- ~/code/some-repo -p openai -m llama3.1 --base-url http://localhost:11434/v1 --open\n\n# A graph to share: structure and descriptions only, no source code\nnpm run explore -- ~/code/some-repo --no-source -o some-repo.graph.json\n\n# Open the latest graph again (or a given one) without re-running anything\nnpm run codelens -- view\nnpm run codelens -- view some-repo.graph.json\n\n# A specific commit or branch instead of the files on disk\nnpm run explore -- ~/code/some-repo --ref v2.1.0 --open\n\n# What have I explored, and what is in the store?\nnpm run codelens -- history\n```\n\nIt opens on the **guide**, a landing page for the package:\n\n- **What it is** : a few sentences on what the package does, and**how it's organized** . These come from the README and the code (with`-p claude-code` or`-p claude` , one request per explore, kept in the store), or offline from the README's opening paragraph and the part summaries.\n- **Start here** : a suggested reading order, each step with why to start there. Click one to open the map centered on it.\n- **The parts** : systems, modules, or files with their summaries.\n- **Also on the page** : key functions, ways in, external effects, and what changed since your last look.\n\nSwitch between **Guide** and **Map** in the header (`g` / `m`). Links with `?fn=…` or `?view=map` open straight on the map.\n\nThe map opens on the **overview**: the package's systems or modules, connected by call volume, with the databases, network, queues and filesystems they touch. Use **Group by** to switch between System, Module, and File; double-click a part to open it in place, or a function to center on it. **Tests** toggles test code. The side panel lists:\n\n- **Start reading here** : entry points (mains, routes, CLI commands, jobs, public API).`←` /`→` steps through them.\n- **Hotspots** : the functions that hold the package together. They are ranked by callers, callees, how many modules they connect, and how much of the code reaches them.\n- **Talks to** : external effects.\n- **New or changed since last look** , when you have explored this scope before.\n\nPress `/` to search functions, files, and modules, and `o` to return to the overview.\n\n**Cards** caps how many cards any view draws: 25 by default, or 40, 80, or All, remembered per browser. In the overview, the least important parts fold into one \"+N more files in src/lang\" card per parent. Importance comes from the selection, entry points, hotspots, recent change, size, and connectivity. Folded cards keep their connections, so the map still shows where the hidden code is and what it talks to. Double-click one to show its contents, or use **show all**. In the focused view, the per-column cap shrinks so all columns together stay within the limit. Parts with no calls either way are packed into a grid beside the flow instead of one tall column.\n\nEverything CodeLens learns is kept in a store. It's one of two kinds:\n\n- **A SQLite file** (the default, using Node's built-in`node:sqlite` , no setup) at`~/.cache/codelens/store.db` .\n- **A Postgres database** , shared by a team, so descriptions are paid for once per team rather than once per person.\n\n```\n--store ~/somewhere/else.db                               # another SQLite file\n--store postgres://user:pass@db.internal/codelens         # Postgres, schema \"codelens\"\n--store 'postgres://user:pass@db.internal/codelens?schema=team_payments'\n```\n\n`CODELENS_STORE` sets the default, and `--no-store` bypasses it. In Postgres, each store lives in its own **schema**, so teams (or, hosted, customers) are fully separated. Keys are content hashes, so never share one schema between parties who must not learn about each other's code. Reads and writes are batched (a few round trips per run), so a remote database is fine.\n\n| Table | Keyed by | Holds | \n|---|---|---|\n| `parses` | git blob id + extractor version | the extracted functions, calls, and imports of one file's content | \n| `descriptions` | hash of provider + function (or group) content | *what / why* text | \n| `snapshots` | repo + scope, with commit id and tree hash | what was explored, when, at which commit | \n| `snapshot_fns` | snapshot + function id | each function's fingerprint, to compute \"new / changed since\" | \n\nBecause everything is keyed by **content**, the store works across branches, rebases, and uncommitted edits. A file that is unchanged since any earlier run is never read or parsed again, and an unchanged function is never described again. The commit id on each snapshot is what \"since your last look (abc1234)\" refers to. The extractor version is a hash of the parser code itself, so upgrading CodeLens automatically re-parses with the new rules. Old JSON description caches are imported on first use.\n\n```\n# Review the current branch the way a pull request would be (against the fork point of origin's default branch)\nnpm run codelens -- review\n\n# Against a specific base, with Claude, writing the map too\nnpm run codelens -- review . --base origin/develop -p claude-code -o review.md -g review.graph.json\nnpm run codelens -- view review.graph.json\n\n# In CI: fail when the overall risk is high\nnpm run codelens -- review --fail-on high\n```\n\nThe summary is Markdown written for a PR comment:\n\n- the overall risk;\n- functions in the order to review them, each linked to its lines on GitHub;\n- breaking contract changes as signature diffs;\n- invariants at risk;\n- which routes, CLIs, and jobs reach the change;\n- which external systems (databases, APIs, queues) it touches.\n\n`--json` or `--json-out` give the same facts as data for other tools.\n\nA review opens on the **brief**. **Start review →** opens the **review queue**: one change at a time, with nothing to arrange.\n\n- \n**Left:** the queue, in order:\n  1. the brief's suggested reading order, each with a reason;\n  2. the remaining changes by risk;\n  3. tests;\n  4. files that changed outside any function.\n Each item shows ✓ done, ↻ changed since you accepted it, or ○ open, with progress and *hide done* on top.\n- \n**Center:** the current change:\n  - what changed and why it matters;\n  - breaking signature changes, invariants at risk, and any of the brief's risks that name this function;\n  - **just this function's diff** (unified or side by side), with review comments inline. Click a line number to comment.\n- \n**Right:** context:\n  - what the function does and why;\n  - who calls it, with callers outside the change marked and test callers collapsed;\n  - what it calls;\n  - which entry points reach it;\n  - external systems it touches.\n\n| Key | Does | \n|---|---|\n| `a` | accept the change and move to the next open one | \n| `n` /`p` | next / previous open change | \n| `c` | comment on this function (pull requests) | \n| `d` | the whole file's diff, full screen | \n| `z` | this function on the map (callers, callees, effects) | \n| `r` | back to the queue (from the map, at the selected change) | \n| `b` /`m` | the brief / the map | \n| Backspace | back | \n\nThe map, Group by, and the invariant and diff tabs are still there for exploring, one key away. The queue is where the review itself happens.\n\n```\nnpm run pr -- 123                                              # a PR in the repo you're in\nnpm run pr -- https://github.com/expressjs/express/pull/7574   # any PR, cloned or not\nnpm run pr -- expressjs/express#7574 -p claude-code            # with Claude's analysis\n```\n\nThe command:\n\n1. looks up the PR (title, author, description, branches);\n2. fetches its code, using your clone when you're in one, else a code-only clone kept in `~/.cache/codelens/repos/` ;\n3. reviews it against the same fork point GitHub's diff uses;\n4. opens the canvas with the PR in the header and side panel, including **Open on GitHub** and**Files changed** .\n\nIt also writes the Markdown review next to the graph. Fork PRs work, because GitHub publishes every PR's head on the base repository.\n\n**The PR's discussion comes along.** Line comments are grouped into threads, and each thread is attached to the function it's about. That holds even for *outdated* comments made on an earlier push: CodeLens reads the file as it was at that commit and finds which function held the line.\n\nWhat you see in the canvas:\n\n- Cards show 💬 counts.\n- A function's panel shows its threads (Markdown rendered, suggested changes included), with **Reply** and**Comment on this function** .\n- The PR panel shows each reviewer's current verdict, general comments, and a review box with **Comment** ,**Approve** , and**Request changes** .\n\nPosting goes through the local `codelens view` server as your `gh` login (or `GITHUB_TOKEN`). A comment lands on the function's first changed line, or on the PR conversation (linked to the function) when the function isn't part of the diff. While the page is open, the discussion refreshes from GitHub every minute, and the graph file is updated so the next open starts current.\n\nAny website can send requests to `localhost`, so the server guards itself:\n\n- writes need a per-session token embedded only in the CodeLens page;\n- every API call must name the server as its host, which blocks DNS rebinding;\n- other origins are refused.\n\nGitHub Enterprise: set `CODELENS_GITHUB_API=https://github.example.com/api/v3` when not using `gh`.\n\nAuth: it uses your `gh` login when you're signed in (private repos and GitHub Enterprise), else `GITHUB_TOKEN`; public repos need neither.\n\n```\n# .github/workflows/codelens.yml\non: pull_request\npermissions: { contents: read, pull-requests: write }\njobs:\n  review:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with: { fetch-depth: 0 }\n      - uses: blong8334/codeLens@main\n        with:\n          provider: heuristic          # or claude + anthropic-api-key, or openai + openai-api-key / openai-base-url\n          # fail-on: high\n```\n\nOn each pull request, the Action:\n\n- posts the review as a comment and updates the same comment on every push;\n- adds it to the run's summary page;\n- attaches the map as an artifact (`codelens-map` , viewed with`codelens view` );\n- caches the store between runs, so each function is analyzed once rather than once per PR.\n\nInputs: `provider`, `model`, `anthropic-api-key`, `openai-api-key`, `openai-base-url`, `base`, `fail-on`, `comment`, `descriptions`, `include-source`. Output: `risk`.\n\n```\n# Uncommitted changes vs HEAD (default)\nnpm run codelens -- inspect ~/code/my-repo --enrich -o packages/web/public/codelens.graph.json\n\n# A commit range, enriched by Claude through your Claude Code subscription (no API key)\nnpm run codelens -- inspect ~/code/my-repo -b main -H feature-branch -o graph.json\nnpm run codelens -- enrich graph.json --provider claude-code\n\n# Same, through the Claude API (for CI; needs API credits)\nANTHROPIC_API_KEY=... npm run codelens -- enrich graph.json --provider claude\n\n# View any graph file\nCODELENS_GRAPH=graph.json npm run dev\n```\n\nYou can also drop a graph JSON onto the page, or deep-link with `?fn=<node id>&lod=0..3&layer=1..3`.\n\nCodeLens groups code automatically:\n\n- **file**\n- **module** : the directory\n- **system** : a nested package (any`package.json` ,`pyproject.toml` ,`go.mod` , or`Cargo.toml` ), otherwise the first meaningful directory (skipping`src` ,`lib` ,`app` , …)\n\nIt also flags **entry points**: routes, CLI commands, jobs (including Celery and cron decorators), tests, and exported functions with no callers. It detects **sinks** (external effects) from unresolved calls to known clients and packages: `pg`, `prisma`, `redis`, `fetch`, `axios`, `requests`, `fs`, `child_process`, S3, queues, and similar.\n\nOverride any of it with an optional `.codelens.json` at the repo root:\n\n```\n{\n  \"systems\":     { \"Payments\": [\"src/services/payments.ts\", \"src/lib/money.ts\"] },\n  \"modules\":     { \"Checkout flow\": [\"src/services/orders.ts\", \"src/services/inventory.ts\"] },\n  \"entryPoints\": [\"src/api/**\", \"worker/reconcile.py#reconcile\"],\n  \"sinks\":       { \"network\": [\"gateway\"], \"billing\": [\"stripe\"] },\n  \"ignore\":      [\"**/generated/**\"]\n}\n```\n\nPatterns are globs (`*`, `**`, `?`). `path#name` matches a function by qualified name.\n\n| `inspect` option | Meaning | \n|---|---|\n| `-b, --base <ref>` | Base revision (default `HEAD` ) | \n| `-H, --head <ref>` | Head revision, or `WORKING` for the uncommitted tree (default) | \n| `-d, --depth <n>` | Caller hops to keep around changes (default 4) | \n| `--full` | Emit the whole repo graph instead of the impacted subgraph | \n| `--enrich [provider]` | Run enrichment inline ( `heuristic` ,`claude-code` , or`claude` ) | \n\nTypeScript, TSX, JavaScript, Python, Java, Kotlin, Scala, C#, Go, Rust, C, C++, Swift, Ruby, and PHP. Run `npm run codelens -- languages` to see which file extensions each one claims.\n\nFor every language, CodeLens extracts:\n\n- functions, methods, and constructors, with qualified names (`Billing.Service.run` )\n- parameters, return types, `async` /`suspend` , and visibility\n- doc comments and docstrings\n- decorators, annotations, and attributes\n- calls with their receivers, imports, and throw/raise/panic sites\n\nEach language plugin also knows its ecosystem:\n\n- how imports map to files (relative paths, Java packages, Go modules, Rust `crate::` paths, C`#include` )\n- which code is visible without an import (same package or directory)\n- test-file conventions\n- route, test, and job annotations\n- which packages count as databases, HTTP clients, queues, and other sinks\n\nEach language is one self-contained file in `packages/cli/src/lang/`, registered in `lang/index.ts`. A plugin is a `LanguageSpec` (`lang/spec.ts`) made of:\n\n1. **A Tree-sitter query** that tags definitions and references with a small set of captures:`@fn` /`@fn.name` ,`@scope` /`@scope.name` (classes),`@namespace` ,`@call` /`@call.name` /`@call.receiver` ,`@import` /`@import.source` /`@import.name` , and`@throw` . The language-agnostic engine (`lang/engine.ts` ) does the rest. Calls are attributed to their innermost named function, so calls inside anonymous callbacks still count.\n2. **Optional hooks** for things grammars disagree on, such as parameters, visibility, return types, or docstrings. Generic defaults cover most grammars.\n3. **Ecosystem knowledge** :`resolveImport` ,`selfNames` ,`implicitThis` ,`sameDirectoryVisible` ,`testFile` ,`sinks` (effectful packages), and`sinkCalls` (effectful calls that have no telltale import, like C's`fopen` ).\n\nThe workflow:\n\n```\n# see the syntax tree (node types and field names) your query needs to match\nnode --liftoff-only packages/cli/scripts/ast.mjs <grammar> path/to/sample.ext\n# add a fixture in packages/cli/test/fixtures/<lang>/ and a CASES entry in test/languages.test.ts\nnpm test\n```\n\nThe test suite fails if a registered language has no fixture case. Any grammar bundled in `tree-sitter-wasms` can be used, including Dart, Elixir, Lua, OCaml, Zig, Solidity, Bash, and others.\n\n**Parsing** (`packages/cli/src/parse.ts`, `src/lang/`): Tree-sitter (WASM, via `web-tree-sitter@0.22` + `tree-sitter-wasms`) runs each language plugin's query over the file. Some grammars (Swift, Kotlin, C#) exhaust V8's optimizing WebAssembly compiler, so the CLI always runs Node with `--liftoff-only`. It relaunches itself with the flag if needed, and parsing is no slower.\n\n**Graph** (`packages/cli/src/graph.ts`, `resolve.ts`): each changed file is parsed at both revisions, and functions are matched by qualified name to classify them as added, modified, or deleted. Calls are resolved by name, without type information, in this order:\n\n1. the caller's own class (`this` /`self` / a Go receiver, or implicit`this` in Java, C#, and similar)\n2. the same file\n3. the same package\n4. imported modules, packages, or classes, including `Class.staticMethod()` and`new Foo().bar()`\n5. fields named after their class (`payments.charge()` →`Payments.charge` )\n6. a globally unique name, as a last resort. This step never applies to method names every standard library has (`get` ,`push` ,`close` , …), because`map.get()` is far likelier than your one`get` method.\n\n**TypeScript and JavaScript get type information on top.** The TypeScript compiler's checker resolves each call before name matching runs:\n\n- **a function in the repo** → an exact edge (`typed` ), even across`tsconfig` path aliases,`new Foo()` constructors, and dynamic`import()` ;\n- **a library or the standard library** (`map.get()` ,`fs.readFile()` ) → no edge, but the call still feeds effect detection;\n- **unknown** (`any` , untyped JavaScript, an interface with several implementations) → falls back to the name-based rules above.\n\nReviews type-check the reviewed commit, not your working tree. On CodeLens itself, the checker resolves about 93% of calls: 930 into the repo and 3,193 to libraries, out of 4,452. It takes under a second. `--no-types` turns it off. Calls from tests and example apps don't count against a function being public API.\n\nCalls outside any named function belong to a per-file **top level** pseudo-function. Examples are CLI wiring, `app.post('/x', …)` route registration, `test(...)` blocks, and `if __name__ == \"__main__\"`. In scripts, servers, and `bin/` files it counts as an entry point.\n\nLow-confidence matches are marked as such. Calls to a function deleted in the diff become **dangling** edges. Graphology runs a reverse BFS outward from each change to compute caller depth and the transitive caller count.\n\n**Enrichment** (`packages/cli/src/enrich.ts`, `claude.ts`):\n\n- `heuristic` runs offline and is deterministic. It diffs signatures to find contract breaks (required params added or removed, type changes, sync↔async), flags protective constructs the diff removed as invariants at risk (null guards, validation calls, bounds checks, try/catch, locks), and scores blast radius from reach, exported surface, breaking changes, and dangling callers.\n- `claude-code` runs`claude -p` with a JSON schema for each changed function. It uses your signed-in Claude Code subscription, so it needs no API key. Defaults to`--model opus` .\n- `claude` sends each changed function to Claude Opus 5.5 (`claude-opus-5-5` ), together with its callers, callees, and the heuristic findings. It uses structured outputs (Zod), server-side refusal fallback, and per-node fallback to the heuristic result on error. Pick another model with`--model` .\n- `openai` talks to**any OpenAI-compatible API** : OpenAI, Azure OpenAI, OpenRouter, Together, Groq, or a local server (Ollama, LM Studio, llama.cpp, vLLM). It uses strict structured outputs where supported, and tolerates models that wrap JSON in prose. It retries rate limits and server errors. Requires`--model` (or`CODELENS_MODEL` ), reads`OPENAI_API_KEY` and`OPENAI_BASE_URL` , or takes`--base-url` :\n\n```\nOPENAI_API_KEY=sk-… npm run explore -- . -p openai -m gpt-4.1-mini\nnpm run explore -- . -p openai -m llama3.1 --base-url http://localhost:11434/v1   # Ollama: nothing leaves your machine\n```\n\n**Descriptions** (`packages/cli/src/purpose.ts`): every function gets a *what / why*, written in batches of 10 per request. Files, modules, and systems are then summarized bottom-up from their members; a group with one child reuses that child's summary. Results are kept in the store by content hash, so re-runs only pay for new or edited code. Pass `--no-purpose` to skip this step. Without an LLM, descriptions fall back to doc comments and call context.\n\nProviders implement the small `EnrichmentProvider` interface, so you can add OpenAI, local models, or anything else without touching the pipeline.\n\n```\npackages/cli/src/lang   one plugin per language + the query engine\npackages/cli/src        graph builders (graph.ts review, explore.ts explore, build.ts shared), call resolution,\n                        snapshots (source.ts), the store (store.ts), enrichers, CLI (tsx, no build step)\npackages/cli/test       per-language fixtures and end-to-end repo tests (npm test)\npackages/web            Vite + React + @xyflow/react + dagre canvas\nscripts/                demo repo generator\n```\n\n- Call resolution is type-aware for TypeScript and JavaScript, and name-based for other languages. Dynamic dispatch, interfaces with many implementations, and same-named methods across classes can be missed there; these show as dotted \"inferred\" edges or are omitted. The next step is language servers for the other languages.\n- Visibility for C#, Swift, and Kotlin is approximated by directory, since namespaces and modules can span directories.\n- Renamed functions appear as one deletion plus one addition.\n- Member calls (`obj.method()` ) only resolve to methods, or to functions reached through a namespace import, so the tool never guesses across unrelated objects. Unresolved member calls feed sink detection instead.\n- The impact map shows edges between adjacent hops only; the panel lists every caller and callee.\n- The per-change risk analysis (`enrichNode` ) is not cached yet; parses and descriptions are.\n- A function's *why* is cached with its own source, so it can go stale when only its callers change.\n- Large graphs (thousands of nodes) need the planned PixiJS renderer. React Flow is comfortable up to a few hundred.\n\nApache-2.0. See [LICENSE](https://github.com/blong8334/codeLens/blob/main/LICENSE) and [NOTICE](https://github.com/blong8334/codeLens/blob/main/NOTICE).\n\n```\nnpm run pack       # builds packages/cli (bundle + canvas) and writes the .tgz npm would publish\nnpm publish -w @blong8334/codelens\n```\n\n`prepack` builds the CLI bundle and the canvas into `packages/cli/dist`, and copies README, LICENSE and NOTICE in. Local graph files are never included.", "url": "https://wpnews.pro/news/show-hn-codelens-ai-augmented-code-review-tool", "canonical_source": "https://github.com/blong8334/codeLens", "published_at": "2026-11-10 21:36:32+00:00", "updated_at": "2026-11-10 21:52:21.907300+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "large-language-models", "artificial-intelligence"], "entities": ["CodeLens", "Tree-sitter", "Claude", "Claude Code", "OpenAI", "Ollama", "Anthropic", "Node.js"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-codelens-ai-augmented-code-review-tool", "markdown": "https://wpnews.pro/news/show-hn-codelens-ai-augmented-code-review-tool.md", "text": "https://wpnews.pro/news/show-hn-codelens-ai-augmented-code-review-tool.txt", "jsonld": "https://wpnews.pro/news/show-hn-codelens-ai-augmented-code-review-tool.jsonld"}}