ProjScan / Changelog
What shipped, version by version.
What shipped in each ProjScan release, mirrored from the public repository.
[3.0.9] — 2026-06-01 — "Opt-in Product Telemetry"
Added
- Added explicit
projscan telemetry status|enable|disable|explaincontrols with default-off anonymous product-health telemetry. - Added fixed-allowlist telemetry events for command category, success/failure, duration buckets, version/platform, setup booleans, repeat-use buckets, and optional feedback buckets without source code, file paths, repo names, branch names, package names, usernames, raw findings, or secrets.
- Added
TELEMETRY.mdand updated onboarding soprojscan init teamasks teams to make a clear telemetry choice.
Changed
projscan feedback addcan now contribute sanitized usefulness buckets to opted-in telemetry while keeping the structured feedback artifact local and explicit.- Privacy, README, stability, guide, first-10-minutes, adoption proof, roadmap, and website prompt docs now describe the opt-in telemetry boundary.
[3.0.8] — 2026-06-01 — "Legal and Trust Hardening"
Added
- Added public security, privacy, disclaimer, trademark, and third-party notice documents so teams can review vulnerability reporting, local-first data handling, mark usage, warranty limits, and redistributed dependency notices before adoption.
- Added a DCO-backed pull request template and contribution guidance to make contribution provenance explicit for future community changes.
- Added the official projscan icon to the npm package allowlist so downstream documentation and websites can use the canonical asset.
Changed
- README and issue templates now point developers to the legal and vulnerability-reporting surfaces instead of leaving those expectations implicit.
- The npm package allowlist now ships the legal/trust documents alongside the CLI and existing public documentation.
[3.0.7] — 2026-05-31 — "Trial Adoption Report"
Added
- Added
projscan trial, a local adoption-readiness report that combines onboarding checks, multi-repo validation, reviewer feedback, trust signals, and website-ready proof into one verdict. - Added
projscan feedbackwithinit,add, andsummarysubcommands so teams can capture minutes saved, prevented bad edits, false positives, owner clarity, next-command clarity, and repeat PR use as structured evidence.
Changed
projscan dogfood --feedbacknow marks market validation asprovenonly when repo coverage, three or more useful reviewer responses, measured value, false-positive balance, and repeat PR feedback are all present.- Updated adoption, first-10-minutes, market-validation, guide, README, and website prompt docs around the measured feedback workflow.
[3.0.6] — 2026-05-31 — "Market Validation Loop"
Added
- Added structured market-validation evidence to
projscan dogfood --feedback, including reviewer usefulness, minutes saved, prevented bad edits, false-positive tracking, and website-ready proof markdown. - Added market-validation docs with the first-PR feedback schema and proof loop.
- Added Baseframe Labs umbrella brand assets, README family-brand footer, and a web-ready RFC 9116 security disclosure file using support@baseframelabs.com.
Changed
- First-run adoption guidance and PR comments now point teams to the feedback-backed dogfood loop so repeat use is measured instead of assumed.
[3.0.5] — 2026-05-28 — "Proof of Usefulness"
Added
- Added an end-to-end team adoption harness that proves
projscan init teamthrough baseline, generated GitHub Action, PR evidence comment, preflight, and owner routing as one workflow. - Added a five-scenario PR comment benchmark suite covering docs-only, auth/API ownership, dataflow/security, large release, and generated-code PRs.
- Added
projscan dogfoodto run adoption proof across real repos, reporting PR-comment readiness, repeat-use readiness, MCP readiness, and first-PR feedback questions. - Added explicit fix-first recommendations across bug-hunt, workplan, start, quality-scorecard, and evidence-pack PR comments so agents know what to fix first, who owns it, and which command proves it.
- Baseline trend memory now records risk direction, quality-score before/after, new/resolved issue counts, and a compact "changed since baseline" summary for PR comments.
- Framework-aware dataflow now recognizes Express request body/query/params/header/cookie sources and keeps JavaScript DB query sinks receiver-sensitive to reduce cache/query false positives.
Changed
- Evidence-pack PR comments now include required "First Fix" and "Developer Feedback" sections and route changed files through CODEOWNERS/package ownership even when the top risk list is clean.
projscan startnow includes an every-PR repeat-use loop with adoption metrics and next commands.- Large release preflight calibration now labels configured scale-threshold risk as manual release sign-off when no concrete taint, dataflow, health, plugin, or supply-chain blocker exists, including before-merge summaries.
- The generated GitHub Action PR-comment validator now requires the first-fix and suggested-action sections plus at least one exact actionable command.
Fixed
- Generated-code PR comment benchmarks guard against default generated taint/dataflow anxiety while preserving custom source/sink visibility.
- Dataflow DB sink detection avoids treating unrelated cache-style
querycalls as database sinks unless the receiver/import context is database-like.
[3.0.4] — 2026-05-28 — "Team Adoption Loop"
Added
projscan init teambootstraps team policy, GitHub PR evidence workflow, CODEOWNERS starter, baseline memory, a firstprojscan startreport, and an onboarding checklist for the first team PR.projscan mcp doctorverifies client setup and returns paste-ready MCP config for Codex, Claude, Cursor, Continue, Windsurf, Cline, Zed, Gemini, or all clients.- Evidence-pack PR comments now lead with a reviewer-facing verdict, trust calibration, baseline trend, top risks, team routing, verification commands, exact next commands, and suggested next actions.
- Evidence-pack PR comments now include a validator for required sections, GitHub comment size, render sanity, and actionable commands; generated GitHub workflows run it before posting.
- Baseline diffs now include trend memory for score direction, new hotspots, and recurring noisy rules.
- Workplans now route tasks and top risks through CODEOWNERS or package-owner metadata when ownership is available.
- Added practical analyzer plugin examples for API route ownership, security-sensitive file review, and monorepo boundary checks.
projscan start/projscan_startprovide a read-only first-60-seconds repo orientation with setup diagnostics, recommended workflow, top risks, adoption gaps, next commands, and optional handoff payload.projscan init policywrites team policy starter kits for frontend, platform, security, and monorepo teams with conservative overwrite protection.projscan handoff --write <file>persists the next-agent handoff as a markdown artifact.projscan evidence-pack --pr-commentrenders approval evidence as a concise GitHub PR comment with suggested next actions.projscan init github-actionwrites a pull-request workflow that runs projscan, posts PR evidence automatically, and fails only when preflight returnsblock.projscan recipesnow includes team-bootstrap and PR-automation playbooks so teams can install policy, CI, and first-run habits from one catalog.projscan preflight --mode before_commitnow treats scale-only review blocks as manual sign-off cautions while preserving hard blocks for concrete taint, dataflow, health, plugin, supply-chain, and before-merge release gates.
Fixed
- Release-scale review blocks in evidence-pack PR comments are now labeled as manual release gates instead of actual-defect blockers when preflight reports no concrete taint, dataflow, health, plugin, or supply-chain blocker.
- Bug-hunt now treats pure hotspot churn as a watchlist/top-suspect signal instead of an immediate fix queue when doctor, preflight, and session evidence are clean.
[3.0.3] — 2026-05-27 — "Agent Review Precision"
Added
- Package-scoped review now runs inside
computeReviewbefore verdict calculation, so CLI and MCP callers get scoped cycles, taint, dataflow, contract, graph evidence, summaries, and verdicts consistently. - Dataflow now recognizes Next-style route request body readers (
request.json(),request.formData(),request.text(),request.arrayBuffer()) as framework request sources only inside route handlers. projscan dataflow/projscan_dataflownow expose an explicit generated-code opt-in while suppressing default generated/codegen risks by default.- Ownership lookup now falls back to workspace package owner metadata when CODEOWNERS does not match.
Fixed
- Package-scoped review no longer blocks on cycles or taint/dataflow risks introduced in other workspace packages, and no longer leaks unrelated package entrypoint contract changes.
- Framework request-source detection now requires a route handler request parameter receiver, avoiding false positives from response helpers such as
Response.json(). - Graph caches now rebuild for receiver-sensitive function metadata so framework request-source detection does not use stale bare-call data.
- Review-time generated-code filtering now matches dataflow filtering: default generated risks are quiet, while custom source/sink risks remain visible.
- CI and release workflows use
actions/checkout@v5andactions/setup-node@v5, addressing the Node 20 action runtime deprecation warning.
[3.0.2] — 2026-05-27 — "Agent Graph Readiness"
Added
- Release readiness, CI, and the tag-triggered release workflow now run the graph corpus baseline gate so parser, semantic-graph, and dataflow fixture regressions are caught before publish.
- Cross-repo impact boundary summaries now prefer CODEOWNERS-derived owners when sibling repositories expose ownership metadata, falling back to the repo name when no owner matches.
projscan_release_train/projscan release-trainnow understand3.0.xgraph-readiness and3.1.xgraph-expansion product lines.
Fixed
- Custom dataflow sources and sinks are no longer hidden by the broad default file-I/O filter; user-configured flows stay visible unless callers explicitly filter them downstream.
npm run release:checknow blocks when an existing remote version tag resolves to a commit other thanHEAD, including annotated tags via peeled refs.
[3.0.1] — 2026-05-26 — "Graph Operations Platform"
Added
projscan_reviewnow includes compactgraphEvidenceso agents can see changed-file count, changed functions, call-edge count, package count, and dataflow-risk count without fetching the full semantic graph.- Workplans and agent briefs now consume graph/dataflow context directly, including semantic-graph follow-up commands and compact graph metrics for next-agent handoff.
computeImpactnow includes cross-repoboundarySummaryevidence for package and ownership boundaries when sibling repository graphs import target-matching packages.- Analyzer plugins now receive an optional third
contextargument with lazy read-onlygetCodeGraph(),getSemanticGraph(), andgetDataflow()helpers while keeping manifest schema v1. - Added
computeGraphCorpusto produce deterministic semantic-graph/dataflow quality metrics over bundled language fixtures. - Added
npm run check:graph-corpusplusdocs/graph-corpus-baseline.jsonso parser/dataflow fixture regressions fail only when graph coverage drops or risk counts increase. - Added a graph-context analyzer plugin example that consumes
context.getSemanticGraph()andcontext.getDataflow(). - Preflight now includes optional
evidence.releaseScalefor large platform changes so agents can distinguish expected scale/complexity sign-off from concrete health, taint, dataflow, plugin, or supply-chain blockers.
Changed
- Split dataflow and review-time dataflow filtering into smaller internal modules while preserving the public CLI, MCP, and npm surfaces.
Fixed
- Dataflow bridge detection no longer joins collision-prone generic names such as
parseandexecacross unrelated files, fixing false review blockers from impossibleenv -> execpaths. - Default dataflow scans now suppress test-file paths, broad file-IO noise, and misidentified JavaScript child-process sinks unless callers explicitly opt into the broader scan.
[3.0.0] — 2026-05-23 — "Deep Graph Platform"
Added
projscan_semantic_graphandprojscan semantic-graphexpose a stable v3 semantic graph contract with file, function, package, and symbol nodes plus imports, package imports, exports, definitions, and calls edges.projscan_dataflowandprojscan dataflowreport direct, propagated, and bridge source-to-sink risks over the function graph.projscan_reviewnow surfacesnewDataflowRisksfor bridge-helper patterns and blocks PRs that introduce a new bridge from a source reader to a dangerous sink.- The npm public API now exports
buildSemanticGraph,computeDataflow, and the associated semantic graph, dataflow, and review dataflow risk types.
Changed
- Stability docs now define the v3 semantic graph contract, the dataflow risk contract, and the 39-tool MCP surface.
- README, Guide, Roadmap, MCP Registry metadata, and website prompt copy now focus the major release on graph/dataflow adoption by AI coding agents.
Show 43 older releases
[2.9.0] — 2026-05-23 — "Adoption Layer"
Added
projscan init mcpnow prints ready-to-paste MCP client configuration for Claude Desktop, Claude Code, Cursor, Codex, Continue, Windsurf, Cline, Zed, Gemini, or all supported clients.projscan recipesnow prints adoption workflow recipes for before-edit, bug-hunt, release approval, handoff, and pre-merge agent loops.projscan first-runnow diagnoses first-run setup across Node.js, package metadata, Git state,.projscanrc, Tree-sitter runtime presence, local plugin manifests, and MCP stdio startup.projscan_adoptionexposes the same MCP configuration snippets, workflow recipes, and first-run diagnostics to agents through one structured MCP tool.- Added a packaged Plugin Gallery with policy, team health, security radar, and release readiness examples.
Changed
projscan doctorconsole output now ends with adoption-oriented next commands for preflight, bug-hunt, and recipe discovery while keeping machine-readable formats unchanged.projscan preflightconsole output now shows required checks and an agent-workflow follow-up while keeping the JSON report shape unchanged.- README, Guide, Stability, package smoke tests, and website prompt copy now describe the adoption-layer workflows and 37-tool MCP surface.
[2.8.0] — 2026-05-22 — "Agent Mission Control"
Added
projscan_workplannow composes preflight, review, session, hotspot, plugin, and supply-chain signals into an ordered agent execution plan with priorities, evidence, suggested tools, verification commands, and short handoff text.projscan workplanexposes the same planner through the CLI with modes forbefore_edit,before_commit,before_merge,refactor,release,bug_hunt, andhardening.projscan handoffprints a concise next-agent handoff from the current workplan, including the next tasks and coordination recommendation.projscan_release_train/projscan release-trainplan upcoming product lines in one readiness view with version, preflight, scope, and next-action evidence.projscan_bug_hunt/projscan bug-huntcompose doctor, preflight, hotspot, and session signals into a prioritized fix queue with verification commands for each target.projscan_evidence_pack/projscan evidence-packassemble planning, bug-hunt, workplan, preflight, changelog, and optional website prompt evidence into one approval packet.projscan_regression_plan/projscan regression-planbuild smoke, focused, or full regression matrices from bug-hunt, preflight, and product risk.projscan_agent_brief/projscan agent-briefcreate compact next-agent context packets with focus items, guardrails, repo context, and suggested next actions.projscan_quality_scorecard/projscan quality-scorecardsummarize health, security, tests, maintainability, coordination, top risks, and verification commands.
Fixed
- Full-suite test stability now uses a 60s Vitest test/hook budget and guards git-heavy temp-repo cleanup paths, preventing false negatives under subprocess load.
[2.2.0] — 2026-05-21 — "Supply-Chain Trust Gate"
Added
projscan doctor,projscan preflight, and MCPprojscan_preflightnow surface supply-chain findings for known malicious TanStack Mini Shai-Hulud indicators, hidden editor persistence hooks, risky install lifecycle scripts, pinned GitHub commit dependencies, and large obfuscated JavaScript payloads.projscan preflightnow includesevidence.supplyChain, supply-chain reason sources, and a dedicated required check so agents can distinguish dependency trust issues from general health or plugin policy failures.- Release validation now runs a built-artifact supply-chain gate,
npm audit, npm signature verification, and emits a CycloneDX SBOM attached to GitHub releases. npm run release:checknow gives maintainers a local release-readiness report covering version metadata, changelog presence, git worktree state, local/remote tag state, release gates, and the exact next action.
Fixed
- Lockfile IOC scanning now catches malicious
resolvedURLs even when a package entry has no nested dependency map. - Lockfile IOC scanning now reads normal large npm lockfiles instead of silently skipping supply-chain checks after the first 2 MB.
- Root
node_modules/<pkg>package-lock entries are parsed correctly for both supply-chain analysis and SBOM component names. projscan_reviewnow short-circuits identical base/head refs instead of doing a full head scan, hotspot pass, and temporary base worktree for an empty diff.preparelifecycle filtering now flags direct package execution such asnpx,npm exec,pnpm dlx, andyarn dlxwhile preserving the existingnpm run buildallowance.- Updated the
brace-expansionandprotobufjsoverrides and lockfile to the safe transitive ranges used by the release gate.
[2.1.0] — 2026-05-18 — "Agent Trust"
Added
projscan preflightand MCPprojscan_preflightnow return an agent-sizedproceed/caution/blockverdict with evidence, required checks, and suggested next tool calls.- Added MCP resources
projscan://session/summary,projscan://handoff, andprojscan://risk-nowfor multi-agent coordination over the shared session. projscan plugin initandprojscan plugin testnow scaffold local analyzer/reporter plugins and validate their runtime behavior with structured diagnostics.projscan_reviewnow includes optionalcontractChangesfor exported symbol renames/additions/removals and Node package entrypoint/public export changes.projscan analyze --format htmlnow emits a self-contained HTML analysis report instead of falling through to console output.npm run smoke:packed-installnow installs the packed npm tarball into a fresh temp project and verifies the CLI, MCP stdio startup, plugin loading, and core JSON commands outside the repo checkout.- Command format support is now machine-readable from one shared source and rendered by
projscan help. - Added a website update prompt for downstream site refreshes after npm/GitHub/MCP Registry releases.
Fixed
- Unsupported CLI
--formatcombinations now fail with a clear command-specific diagnostic instead of silently rendering console output. - Default repository scanning now ignores projscan's own local state directories (
.projscan-cache/,.projscan-memory/) and OS metadata files (.DS_Store,Thumbs.db). - Local plugin modules now load through Node's runtime importer so file-based plugins continue to work under Vite/Vitest-backed environments.
- Local plugin load failures now explain missing modules, syntax errors, missing analyzer/reporter exports, and unsupported reporter commands with actionable manifest/module hints.
- Upgraded the dev Vitest/Vite test stack to a Node 18-compatible safe range so
npm auditno longer reports the esbuild dev-server advisory.
Documentation
- Refreshed CLI help, README, Guide, and Stability docs so command-dependent format support includes HTML and reflects the 2.0 Plugin Platform release.
- Audited README trust copy, MCP Registry metadata, and registry republish instructions for the 2.0 release line.
[2.0.0] — 2026-05-18 — "Plugin Platform"
projscan 2.0 stabilizes the local plugin platform and uses the major-version boundary to clean up deferred 1.x public API surfaces. MCP tool names and input schemas remain stable.
Added
- Stable local analyzer/reporter plugin contract for
.projscan-plugins/*.projscan-plugin.jsonmanifests withschemaVersion: 1. - Machine-readable plugin manifest schema at
docs/plugin.schema.json, plus tested analyzer and reporter examples underdocs/examples/plugins/. - CLI JSON
schemaVersion: 2metadata on built-in JSON reporters. BuiltinLanguageIdfor the closed set of bundled adapters while keepingLanguageIdextensible for plugin-provided languages.
Changed
- CLI/MCP file explanations now use the multi-language AST graph path instead of deprecated JS/TS-only regex helpers.
- Custom presentation, white-label reports, and team-branded output are documented as reporter-plugin responsibilities rather than core HTML theming flags.
- Release validation now opts GitHub Actions JavaScript actions into the Node 24 runtime and keeps MCP Registry descriptor constraints under test.
- Stability baseline refreshed for the 2.x public CLI/MCP surface.
Removed
- Deprecated regex
extractImportsandextractExportshelpers from the npm public API.
Migration
- See 2.0 Migration Guide.
[1.11.0] — 2026-05-18 — "Reporter Plugins"
Reporter plugin preview for CLI output, docs, and demo media. No MCP wire breakage.
Added — reporter plugin preview
- Local plugin manifests may now declare
kind: "reporter"withcommands: ["doctor" | "analyze" | "ci"]. Reporter modules exportrender(context), returning the CLI output string for a supported command. - New CLI flag
--reporter <name>onprojscan doctor,projscan analyze, andprojscan ci. Reporters stay behindPROJSCAN_PLUGINS_PREVIEW=1, matching the analyzer preview gate. - Reporter execution is isolated: unsupported commands, missing exports, non-string returns, and thrown render errors produce clear CLI failures without changing the default console or JSON output paths.
Changed
projscan_pluginandprojscan plugin list | validatenow understand analyzer and reporter manifests. The MCP tool remains structured and validation-oriented; reporter rendering is intentionally CLI-only.- Plugin diagnostics now distinguish analyzer-specific
categoryrequirements from reporter-specificcommandsrequirements. projscan ci --reporter <name>preserves CI policy behavior: reporter output can customize the console view, but threshold failures still exit non-zero.
Documentation
- README now leads with a generated macOS-style reporter plugin demo (
docs/projscan-reporter-plugin.png/.gif) and includes a reporter quick-start. - Plugin Authoring now documents reporter manifests,
render(context), supported commands, and CLI-only behavior.
[1.10.0] — 2026-05-13 — "RC for 2.0"
Analyzer plugin API preview, live cost-summary streaming, and five fixes.
Added — analyzer plugin API preview
- New MCP tool
projscan_plugin+ CLIprojscan plugin list | validate <manifest>. Discovers and validates.projscan-plugins/*.projscan-plugin.jsonmanifests against the 1.10 schema. The tool is always registered (so agents can probe for it), but plugins only load whenPROJSCAN_PLUGINS_PREVIEW=1is set in the environment — without the flag,enabled: falseis returned and the issue stream is unchanged. - Plugin shape:
{ schemaVersion: 1, name, kind: "analyzer", module, category, description? }. Modules export{ check: (rootPath, files) => Promise<Issue[]> }. Issue ids from plugins are prefixedplugin:<name>:so two plugins emitting the same local rule id can't collide. - One plugin crashing or returning malformed
Issuerecords is isolated — other plugins still load and contribute. - Path-traversal guard on
module: absolute paths and..segments are rejected at validation time. - The schema may shift before 2.0 — that's what the preview gate is for. 2.0 will commit it under the stability contract.
Added — live cost-summary streaming
projscan_cost_summarygainsaction: "start_stream" | "stop_stream" | "list_streams". The existing snapshot mode isaction: "snapshot"and remains the default, so existing callers see no change.start_streamregisters a watch; everyinterval_seconds(default 10, range 2–600) the server polls the session log and emitsnotifications/projscan/cost_deltawhen new tool calls have accrued. Ticks with no new activity are silent.- Notification payload:
{ streamId, sessionId, perTool: [{ tool, callsAdded, tokensAdded, cumulativeCalls, cumulativeTokens }], cumulative: { totalCalls, totalEstimatedTokens } }.
Fixed
projscan_apply_fixrollback now removes parent dirs that the forwardop:'create'brought into existence, deepest-first viarmdir(which silently no-ops on non-empty dirs, so unrelated siblings survive). The rollback record carries a new optionalcreatedParentDirsfield; older records written by 1.9 and earlier still roll back the file the old way.projscan_apply_fixrollback ofop:'delete'mkdir -p's the parent dir before re-creating the file. Previously, if a separate process had pruned the now-empty parent dir between apply and rollback,atomicWriteFile'sfs.open(tmp, 'wx')wouldENOENTand the rollback would partial-fail silently.incrementallyUpdateGraphno longer derives package-root context from a pre-update graph view. Adapter contexts are now computed after the parse pass, so a newly-added manifest (pyproject.toml,Cargo.toml,go.mod) batched with source files influences that batch's import resolution instead of staying stale until the next tick.git diff --name-onlyoutput larger than the 10MB buffer is surfaced with a specific reason (git diff against "X" exceeded the 10MB output buffer ... use --base-ref to pin a closer ref) instead of falling through to a misleading "no usable base ref found".projscan_taintBFS caps the per-step frontier at 5000 candidates. Wide-fan-out graphs (Java/TS with prevalentget/set/toStringbare-name collisions) previously could balloon the frontier exponentially when each bare-name callee resolved to thousands of same-named functions. The source is surfaced intruncatedSourceswhen the cap fires, matching howMAX_DEPTHtruncation is reported.startWatcherno longer firesonChangeafterclose(). An in-flight debounce flush that raced past the top-of-functionclosedcheck now re-checks before invoking the callback, so a stopped watcher never delivers a stale event. A newWatchHandle.closedpromise lets orderly-shutdown callers await full quiet afterclose()returns.
Changed
ApplyChangegains optionalcreatedParentDirs?: string[]forop:'create'records.WatchHandlegainsclosed: Promise<void>alongside the existingclose(): void.
[1.9.0] — 2026-05-12 — "Intent + Polish"
Headline: intent-grounded review — agents can hand projscan a free-text PR description and get each finding labelled expected / unexpected / out-of-scope. No LLM involved.
Added — intent-grounded review (projscan_review + intent)
projscan_reviewaccepts a new optionalintentstring argument. projscan parses it rule-driven — no LLM — into:- an action type (
feature/fix/refactor/perf/test/docs/chore/remove/unknown), and - a list of scope tokens (identifiers, file paths, module names) extracted from the prose with English stopwords + generic path components (
src,lib,dist, ...) + action keywords filtered out.
- an action type (
- Every changed file, risky function, new cycle, new taint flow, and dependency-change record gets an
intentAlignmentfield:expected(in scope + typical for the action),unexpected(in scope but atypical — e.g. docs PR that introduces a new taint flow),out-of-scope(outside the area the agent named), orunknown(action could not be classified). - The report carries an
intentecho (raw + parsed action + scope tokens) and anintentAnalysisblock with per-alignment totals and up to 5 "notable" findings biased toward unexpected. - The verdict (
ok/review/block) is deliberately unaffected by intent — verdict stays structural. Intent is an extra narration layer on top so the agent can say "you intended X, you also got Y." - Path-boundary scope matching. Token "auth" matches
src/auth/index.tsanduse_auth_hook(boundary on each side) but NOTauthority/database.ts(no boundary). A naive substring match would have declared nearly every file in-scope. - Type-rooted intents:
docs+ a docs path (README,.md,docs/**) is intrinsically in scope even without an explicit token hit. Same fortest+ a test path. Agents don't have to enumerate every file. - The summary array picks up one or two extra bullets when intent is set:
Intent: "feature" (scope: auth, session).plus aN finding(s) unexpected for this intent — e.g. …when applicable. - Pathological-input cap: intent argument is capped at 8K chars before any regex passes run, to prevent catastrophic backtracking on hostile input.
Added — Project Memory loop #4: per-rule severity drift
- New
computeSeverityDrift(memory, ruleId)returning'stable' | 'noisy' | 'cry-wolf':- cry-wolf: rule surfaced ≥ 10 runs with zero fixes. Driving agents should drop the rule's severity one level (error → warning, warning → info, info → drop).
- noisy: rule surfaced ≥ 5 runs with fix-rate < 0.2. De-emphasize.
- stable: any other case.
- Surfaced via
projscan_memory action: confidence— each tracked rule now carries adriftfield and the response includes adriftCountssummary alongside the existingcounts. - Suppressed-in-config rules (
.projscanrc disableRules) are excluded — that's a deliberate signal, not drift.
Added — projscan_review_watch signature deepening
- The change-detection signature was previously a flat string of verdict + SHAs + counts. Replaced with a structured
WatchSnapshotthat fingerprints cycles (sorted file lists), risky functions (file + name keys), taint flows (source-fn::sink-fn keys), and dependency changes split into adds / removes / bumps. - The
notifications/projscan/pr_changedpayload now carries adeltafield with:changeKinds: which buckets actually moved (verdict/baseSha/headSha/changedFiles/cycles/risky/taint/deps)- per-bucket counts:
cycles: { added, removed },risky: { added, removed },taint: { added, removed },deps: { added, removed, bumped }
- Lets agents react to a specific dimension without re-reading the full report. A dep-only change can trigger a
projscan_auditre-run; a verdict change can short-circuit to "rebuild the review summary." - Bucket semantics: each counter reports records that NEWLY appeared. Reverts (records that vanished) trigger
changeKinds.push('deps')so the agent knows something moved, but they don't increment counters — counting them would conflate "PR newly adds foo" with "PR no longer removes foo," which mean different things to a reviewer.
Added — cross-arch CI (macOS leg)
- CI matrix now includes
macos-lateston Node 22 alongsideubuntu-latest× Node {20, 22, 24}. Catches the wasi-sdk + tree-sitter wasm build chain differences across darwin and linux without bloating the matrix (the macOS leg exists to validate the grammar-build chain, which one Node version exercises as well as three).
Fixed
projscan_doctorno longer crashes with EMFILE on large repos.securityCheck.tswas using unboundedPromise.allover filtered files; a 50K-file repo opened 50K concurrentfs.readFileand tripped macOS's default 256 ulimit. Routed through the existingmapWithConcurrency(128)helper.gitinvocations now have a 30s default timeout. Previously a hung git operation (credential prompt against a dead remote, blocking git hook, slow NFS) could hang the MCP server forever. AddedDEFAULT_GIT_TIMEOUT_MSto bothprDiff.runGitandreview.runGit. Stdin is now also detached (stdio: ['ignore', 'pipe', 'pipe']) so hooks/prompts see EOF and exit instead of waiting.- Watcher noise from atomic writes. The
shouldSkipfilter now matches.projscan-tmp-soatomicWriteFilesiblings don't generate redundant rename/create/unlink events. Previously everyprojscan_apply_fixwrite in the repo body fired three watcher events per file. - Rollback record is now atomic. The recovery oracle (
.projscan-cache/rollbacks/<id>.json) was being written with non-atomicfs.writeFile. A crash mid-write left corrupt JSON thatrollbackparsed as null, stranding the user with applied changes and no way to revert. Now usesatomicWriteFilelike the rest of the apply pipeline. repositoryScanner.totalDirectoriesoff-by-one on some walkers. Empty-stringdirectorykeys (from file walkers that emit''instead of'.'for root files) inflated the count by one. Now filtered.computeFanOutperf hoist. The inner-loopbareName(fn.name)was re-computed per callee — on a 50K-file repo with average fan-out, ~30M redundant string-slice ops per scan. Hoisted to per-function constant.
Changed
ReviewReporttype gains optionalintentandintentAnalysisfields. Absent when nointentarg was passed — strict addition, no break.ReviewFile/ReviewFunction/ReviewCycle/ReviewTaintFlow/ReviewDependencyChangeeach gain an optionalintentAlignmentfield. Same — absent when no intent.reviewWatchTooldescription updated to mention the deepened signature +deltapayload.memoryTooldescription updated to mention the newdriftfield surfaced inconfidenceaction.
[1.8.1] — 2026-05-08
Documentation patch. Surfaces the MCP clients we already work with but didn't document.
Added
- README integration sections for Codex CLI (OpenAI) and Gemini CLI (Google). projscan implements the MCP 2025-03-26 spec; both Codex and Gemini are conformant MCP clients, so the protocol-level support has been there since 1.0.0 — only the setup snippet was missing. No code changes.
- Tagline + commands table now mention both alongside Claude Code, Cursor, Windsurf, Cline, Continue, and Zed.
[1.8.0] — 2026-05-08 — "Resilience + Live"
Adds the Swift language adapter and the projscan_review_watch long-running PR review tool.
Added — Swift adapter (.swift)
- Full tree-sitter integration. Imports (incl.
import struct Foo.Bar,@testable import), visibility-aware exports (private / fileprivate hidden; public / internal / open visible), per-function CC withswitch-arm counting (default not counted) and guard / for / while / do / catch handling, call-site extraction across navigation expressions, package-root detection from SwiftPMPackage.swift/Sources/layout. 11 languages now. - Build chain: tree-sitter-swift's
parser.c(540k LoC) trips a hardyou must have emcc/docker/podman on PATHcheck inside thetree-sittercli even when wasi-sdk is locally available.scripts/copy-wasm.mjsnow bypasses the cli and invokes wasi-sdk's clang directly with the same flags the cli uses internally (-Os -fPIC -shared -nostdlib -Wl,--no-entry --target=wasm32-unknown-wasi). The path probe honorsTREE_SITTER_WASI_SDK_PATHand falls back to the OS-conventional cache (~/.cache/...on Unix;%LOCALAPPDATA%\tree-sitter\wasi-sdkon Windows).
Added — projscan_review_watch (long-running PR review)
- New MCP tool. Polls a
base + headref pair on a configurable interval (default 30s, range 5–600s) and emitsnotifications/projscan/pr_changedwhenever the review verdict, base/head SHAs, changed-file count, new-taint-flow count, or risky-function set changes. The capstone for the agent-substrate arc —projscan_reviewis a snapshot, this is the stream. - Actions:
start(returns initial review + watchId),stop(cancels by watchId),list(enumerate active watches). - Server-side lifecycle: each watch is registered with the MCP server's tool-watch registry;
close()cancels all timers so polling can't outlive the server. Single-flight per watch — overlapping ticks during a slowcomputeRevieware dropped, not queued. - Tool-side context plumbing:
McpToolHandlersignature is now(args, rootPath, context?: McpToolContext). Existing tools ignore the third arg and continue to operate as before.
Added — Project Memory + cost analytics deepening
- Taint depth reporting:
projscan_taintnow returnstruncated: boolean,truncatedSources: string[], andmaxDepth: numberso agents know when the BFS hit its cap. The cap is also raised from 8 → 12 (real user repos average 10–11 hops between an HTTP handler and a shell-exec sink); the BFS data structures and visited-set are unchanged. - Cost p95 saturation guard:
projscan_cost_summaryreturnsobservedP95Tokens: nullplusobservedP95InsufficientSamples: truewhen fewer than 20 samples are available. Saturating at the observed max for tiny samples misled agents into budgeting on a worst-case spike rather than a representative high-water mark.
Fixed — bug-hunt round (audit-driven)
- Atomic session save.
session.saveSessionnow uses the sameatomicWriteFile(tmp + fsync + rename + parent-dir fsync) thatapplyFixhas used since 1.6.0. Crashes mid-write can no longer truncate.projscan-cache/session.jsonto zero bytes, and concurrent writers can't half-clobber each other. The atomic-write helper is extracted tosrc/utils/atomicWrite.tsand shared. Cleanup on rename failure unlinks the abandoned tmp file so retries don't accumulate disk droppings. - Embeddings pipeline LRU. The pipeline cache is now bounded at 2 cached models with LRU eviction. Long-running MCP servers that switch models (or test runs that thrash) no longer accumulate ~200 MB per model indefinitely.
- Templated C++ qualified-id translation.
translateScopeOperatorwalks chars while tracking angle-bracket depth, soFoo<std::pair<int,int>>::barnow correctly emitsFoo<std::pair<int,int>>.barinstead ofFoo<std.pair<int,int>>.bar. The previous barereplace(/::/g, '.')would corrupt template-argument types in declarators. - review_watch race conditions caught in the bug hunt: (1)
watchIdis now assigned BEFORE thesetIntervalis armed (was: timer fires capturing anullwatchId in the closure for ≥ 5000 ms); (2)runTickis single-flight per watch (was: overlapping ticks during slowcomputeReviewcould deliver out-of-order notifications); (3)stopalways cleans the module-levelwatchesmap regardless of whether the server registry knew about it (was: orphaned state across cross-transport stops).
[1.7.0] — 2026-05-07 — "Reach + Visibility"
Adds Kotlin and C++ language adapters, Project Memory's per-rule confidence loop, and a cost-summary MCP tool.
Added — language adapters
- Kotlin (
.kt,.kts) — full tree-sitter adapter: imports (incl. wildcards + aliases), visibility-aware exports (private / internal hidden), per-function CC withwhen-arm counting (else not counted), call-site extraction, package-root detection from Gradle / Maven layouts. Closes the JVM gap that previously stopped at Java. Bumps the supported-languages count from 9 to 10. - C++ (
.cpp,.cc,.cxx,.c,.h,.hpp,.hxx) — full tree-sitter adapter:#includeresolution (quoted relative-to-importer + project include roots), top-level decl exports (functions, classes, structs, enums, type aliases), per-function CC withcase-label counting (default not counted) and out-of-line method qualified-name handling (Foo::bar→Foo.bar), call-site extraction across qualified, field, and template expressions. Closes the systems-languages gap. - Build chain:
tree-sitter-kotlindoesn't ship a prebuilt wasm;scripts/copy-wasm.mjsnow invokestree-sitter build --wasmat install time using the bundled wasi-sdk.tree-sitter-cliis a new devDep — runtime users pay nothing. - Swift adapter is deferred to 1.7.1: tree-sitter-swift's parser size requires emcc / docker that we couldn't bring online from this build environment. Design is preserved and will ship in the next patch.
Added — Project Memory loop #3 (per-rule confidence)
- New
computeRuleConfidence(memory, ruleId)andcomputeRuleConfidenceScore(...)insrc/core/memory.ts. Returns'high'when the user has actively fixed instances of a rule (fixedCount > 0),'low'when the rule has surfaced over ≥ STABLE_RULE_RUN_COUNT runs spanning ≥ STABLE_RULE_DAYS days without ever being fixed, and'medium'otherwise. Numeric score in[0, 1]for ranking. projscan_fix_suggestMCP tool now attaches aconfidence: { level, score }sidecar to every result so agents can deprioritise rules the user has historically tolerated.projscan_memorygains a newconfidenceaction that returns every tracked rule labelled and ranked, plus per-level counts (high / medium / low). Pairs with the existingstableandacceptedviews.
Added — cost visibility (aggregate analytics)
- New MCP tool
projscan_cost_summarythat aggregates the_costsidecar (1.5+) across the session event log: total tokens, top spenders, per-tool typical / p95 token estimates, plus a staticexpectedTokenscatalog so an agent can budget pre-call. - The MCP server now folds the post-budget
estimatedTokensinto eachtool-call:*session event (1.7+), so the cost summary can derive per-tool stats automatically. Older sessions without cost data are handled gracefully (typical / p95 read as 0). Aware of the bounded log (last 500 calls) — documented in the tool description.
Fixed — bug-hunt round (audit-driven)
- Kotlin / C++ branch detection —
when_entryelse-arm andcase_statementdefault-arm detection switched from text-regex (fragile around comments / whitespace) to structural checks (when_conditionchild presence; tree-sitter-cpp'svaluefield). Caught a real CC-by-one in the kotlin classify integration test. - Future-dated timestamps in confidence math —
computeRuleConfidencenow requiresageMs > 0before classifying a rule as'low', so clock skew or corrupt memory falls back to'medium'instead of silently ageing into a low-confidence cliff. - Memory tool error-message drift — the unknown-action error now lists
confidencealongside the other actions; matches the input-schema enum. - Concurrent ensureSession race — multi-request bursts could each load and save their own session copy, last-write-wins dropping touches and events. The MCP server now gates
ensureSession()behind a single in-flightPromise, so concurrent callers share the loaded session. - Session event-log overflow race —
recordEventnow bounds-then-pushes (rather than push-then-bound), so two interleaved calls can't briefly leave the array one-over MAX_EVENTS or drop a different entry depending on order. looksLikePathsubstring..check — the session touch scanner rejected legitimate filenames likebefore..after.txtand..hiddenwith a substring check; switched to a segment-based check matchingsession.normalizeFileandapplyFix.isSafeRelativePath.
[1.6.2] — 2026-05-06
Hardening release fixing seven bugs surfaced after 1.6.0.
Fixed
projscan_reviewno longer emits false-positive risky-functions on JS files with multiple anonymous arrows.findRiskyFunctionskeyedbaseByNamebyfn.name, so all<anonymous>callbacks (the dominant real-world case — every unbound arrow gets that name fromast.ts:nameForFunctionNode) collapsed into a single map entry. Every head<anonymous>then compared against the LAST base<anonymous>'s CC, producing false-positivecrossed-threshold/jumpedrows on essentially every file with ≥2 anonymous arrows of differing CC. Live since 0.13.0. Fixed by switching to a multimap and skipping the CC-delta check when EITHER side has more than one function with that name.projscan_apply_fixrejects path-traversal inrollback_id.readRollbackRecordjoined an unvalidatedrollback_idinto a path, so a hostile MCP client passingrollback_id: "../../foo"could read any*.jsonunder cwd throughpath.join's relative-segment collapse. Fix: strict UUID v4 regex validation (pinned to v4 sincecrypto.randomUUID()always emits v4) before the read.projscan_sessionaccepts legitimate filenames containing..substrings.normalizeFilerejected any file whose path contained the literal..substring anywhere — wrong shape of guard. Names likebefore..after.txtand..hiddenwere silently dropped. Fix: segment-based check matchingapplyFix.isSafeRelativePath.projscan_filerejects absolute paths and resolves symlinks before the inside-root check. The MCP tool's docs claim "relative to project root" but the prior implementation silently honored absolute paths. Worse: a symlink under the repo (e.g.cache/keys.pem→/etc/passwd) passed the prefix check but read attacker-chosen content. Fix: reject absolute paths up front; canonicalize both root and target viarealpathbefore the inside-root check (which also fixes a subtle macOS regression wheremkdtempreturns/var/folders/...but the canonical form is/private/var/folders/...).atomicWriteis now crash-durable and TOCTOU-resistant. Three hardenings toapplyFix.atomicWrite: tmp filename usescrypto.randomUUID()instead ofpid + Date.now()(was predictable to ~µs); open with'wx'(O_CREAT | O_EXCL) so a planted symlink at the tmp path is rejected with EEXIST;fsyncthe file before rename and best-effortfsyncthe parent dir after — the docstring claimed partial-state-not-allowed, but without fsync the rename can survive a crash with empty content.git worktree addargv adds--separator. Defensive:baseShais verified throughrev-parse --verify ... ^{commit}upstream, so refs starting with-can't slip through today, but the separator means a future caller passing an unverified ref can't argument-inject (e.g.--upload-pack=evil).applyFix.ts:147.reverse()mutation. Latent bug —[...completedIdx].reverse()instead ofcompletedIdx.reverse()in the rollback loop. Only iterated once today, but a footgun for any future maintainer.src/mcp/prompts.ts:472lint cleanup. Stray escaped backticks inside a single-quoted string emittingno-useless-escape. CI onmainhad been red since the 1.5.0 security commit; nobody noticed because lint isn't part of the local pre-commit gate. Fixed and added to the gate.
Added — performance
- Bounded file-I/O concurrency.
buildCodeGraph,buildSearchIndex, andattachExcerptspreviously didPromise.all(files.map(async))— an unbounded fan-out that opened an FD per file. On a 10K-file repo this issued 10K concurrentfs.stat + fs.readFile, far exceeding macOS's default 256 open-files ulimit and tripping EMFILE on cold scans. New helpermapWithConcurrency(default 128) batches the work. Zero behavior change for small repos; correctness fix on large ones. - Skipped redundant
JSON.stringifyper non-truncated MCP tool call. Every tool result re-stringified the post-budget payload to compute the_costsidecar — butapplyBudgetalready produced exactly that number for the non-truncated case. Now reuses the cached value. For a 200KB response that's ~4MB of throwaway string work saved per call.
Added — release pipeline
- Tag-triggered Release workflow. Pushing
vX.Y.Znow: validatespackage.json+server.jsonversions match the tag; runs the full build/test/lint/stability gate; slices the matching CHANGELOG entry; publishes to npm with provenance; creates the GitHub Release withdist/tool-manifest.jsonattached. Replaces the old release-published-triggered workflow. Manual surfaces left: MCP Registry republish (interactive OAuth, can't run safely in CI today) and the website edits. SeeCONTRIBUTING.mdfor the new ritual.
[1.6.1] — 2026-05-06
Patch release. Two fixes that surfaced after 1.6.0 went out, plus the release pipeline that should have been part of 1.6.0.
Fixed
projscan_reviewno longer false-positive-blocks safe PRs when the base graph fails to parse. 1.6.0's new-taint-flow detection diffed(sourceFn, sinkFn)pairs between base and head; if the base graph couldn't be parsed (worktree problem, transient adapter failure),baseFlowKeyswas empty and every head flow was flagged as new — the verdict avalanched toblock. A flow is now only "new" if at least one file in its path is in the PR's changed-file set. Strictly tighter — any genuinely-introduced flow must touch a modified file by construction (the new source-fn, sink-fn, or intermediate hop), so this never drops a real flow. Thanks to a post-1.6.0 review pass for catching it.src/mcp/prompts.tslint cleanup. Stray\\\``-style escapes inside a single-quoted string were emittingno-useless-escape` errors that CI had been failing on silently since 1.5.0. Real bug: not user-visible, but main was red.
Added
- Tag-triggered Release workflow (
.github/workflows/release.yml). Replaces the oldpublish.yml(which fired onrelease.published). PushingvX.Y.Znow: validatespackage.json+server.jsonversions match the tag, runs the full build/test/lint/stability gate, slices the matching CHANGELOG entry, publishes to npm with provenance, and creates the GitHub Release withdist/tool-manifest.jsonattached. Idempotent on re-run viaworkflow_dispatch. Strict-semver gate rejects prerelease tags up front. The MCP Registry republish stays manual (mcp-publisher needs interactive OAuth).
[1.6.0] — 2026-05-06 — "Operator"
projscan grows from a report-and-suggest tool into a report-and-act tool, and learns to look across repository boundaries. Three pillars: cross-repo intelligence over registered sibling repos, an apply layer that mechanically executes the safest fix templates with rollback support, and a security-aware review that surfaces newly-introduced source-to-sink taint flows.
Added — cross-repo intelligence (Pillar 1)
projscan workspace add | list | removeCLI. Register sibling repository paths under a workspace root. State persists at<root>/.projscan-workspace.json(auto-gitignored), schema-versioned for forward evolution. Each registered repo gets a stable name (defaults to its directory name) and an absolute path; duplicates are detected up-front.projscan_workspace_graphMCP tool. Cross-repo intelligence over the registered siblings. Three subactions:list— registered repos with parsed-file counts and exported-symbol counts.graph— every symbol exported by ≥ 2 registered repos (a candidate refactor / API contract surface).file_importers— given a file in one registered repo, list every other repo whose graph imports it.
projscan_impactcross-repo mode. Newcross_repo: truearg (CLI:--cross-repo) folds the registered sibling graphs into impact analysis: the response gains arepofield per node and atotalReachableByRepoaggregate, so an agent can answer "if I rename this exported function, what changes across every registered repo?"projscan coverage --changed-only+--base-ref <ref>. Restrict the coverage report to files changed against a git base ref (auto-detected when omitted). Speeds up the per-PR loop where you only want hotspots in the diff.
Naming note: projscan_workspaces (plural) remains the intra-repo monorepo tool — workspace packages within a single codebase. projscan_workspace_graph (singular concept, "the workspace graph") is the new cross-repo tool. The split is deliberate; the docs call it out so agents pick the right one.
Added — apply layer (Pillar 2)
The diagnose-fix loop closes mechanically for the safe templates.
projscan_apply_fixMCP tool +projscan apply-fix <issue_id>CLI. Default is dry-run — returns the would-change list without writing. Passconfirm: true(--confirm) to actually mutate disk. Atomic writes (write-to-tmp + rename), per-apply rollback record persisted at.projscan-cache/rollbacks/<id>.json. Reverse withaction: "rollback", rollback_id: ...orprojscan apply-fix --rollback <id>. Refusal guards reject absolute paths,..traversal, create-over-existing, and modify-non-existent.- Apply support for six mechanical fix templates.
unused-dependency-*(patches the rightpackage.json),missing-test-framework(vitest config + smoke test),missing-eslint(eslint.config.js),missing-prettier(.prettierrc),missing-editorconfig(.editorconfig),missing-readme(README skeleton). Templates without apply support returnapplicable: falseand point atprojscan_fix_suggestfor the structured guidance — no codemods, no semantic rename, no inference. projscan initCLI. Scaffolds.projscanrc.jsonwith sensible defaults ({minScore: 70, hotspots: {limit: 10}, ignore: [], disableRules: []}). Idempotent — refuses to overwrite an existing config without--force.projscan install-hook --threshold <n>CLI. Writes.git/hooks/pre-commitrunningnpx projscan ci --changed-only --min-score <n>. One-line CI gate without touching CI config.
Added — security-aware review (Pillar 3)
projscan_taintMCP tool +projscan taintCLI. Source-to-sink reachability over the per-function call graph. Each flow listssourceFn,sinkFn, the matched source/sink names, the path, and the files it traverses. BFS over per-functioncallSites+ member-expression reads, capped at 8 hops, deduped by(sourceFn, sinkFn).- Built-in source/sink defaults. Common JS / Python sources (
process.env,req.body,req.query,req.params,req.headers,req.cookies,readFile,readFileSync,process.stdin,getInput) and sinks (exec,execSync,spawn,spawnSync,eval,Function,writeFile,writeFileSync,unlink,rm,rmSync,query,execute,os.system,subprocess,innerHTML). .projscanrc.jsontaintblock.taint.sourcesandtaint.sinksarrays MERGE with the defaults — they don't replace them. Use this to add project-specific names:runRawSql,dangerouslyEval,customSecretReader, etc. To suppress a default, listtaint-flow-detectedunderdisableRules.projscan_reviewtaint integration. The review now diffs taint flows between base and head: any(sourceFn, sinkFn)pair that exists at head and didn't at base surfaces asnewTaintFlows. A new taint flow forces the verdict toblock— the strongest signal in the review verdict. Surfaced in thesummaryline as the lead concern (e.g.2 new taint flow(s) detected: env→exec (run), body→query (handler), …).review_this_prprompt updated. Taint flows are now the lead concern in the PR review template — agents are asked to name the flow, explain the exploit shape, and demand neutralization or justification before approving.
Changed
- MCP tool count: 22 → 25 (added
projscan_workspace_graph,projscan_apply_fix,projscan_taint). - CLI commands: 24 → 28 (added
projscan workspace,projscan apply-fix,projscan init,projscan install-hook,projscan taint). ReviewReportgains requirednewTaintFlows: ReviewTaintFlow[]field (1.6+). New typeReviewTaintFlowexported.ImpactReportgains optionaltotalReachableByRepo?: Record<string, number>(1.6+, present only whencross_repo: true).ImpactNodegains optionalrepo?: string(1.6+).ProjscanConfiggains optionaltaint?: { sources?: string[]; sinks?: string[] }(1.6+).FunctionInfogains optionalreferences?: string[](1.6+) — rightmost identifiers from member-expression reads in non-callee position. Powers taint source detection (e.g.process.env.Xregistersenv). JavaScript / TypeScript only at this release; other adapters omit it and taint matches call-shaped sources only for those files.- Cache version bump v4 → v5.
.projscan-cache/graph.jsonwritten by 1.5 is discarded on first 1.6 run so the newreferencesfield is populated for every parsed function. - New public exports from
src/core/taint.ts:computeTaint,TaintConfig,TaintFlow,TaintReport,DEFAULT_TAINT_SOURCES,DEFAULT_TAINT_SINKS. - New public exports from
src/core/applyFix.ts:executePlan,rollback,ApplyResult,ApplyPlan,ApplyChange. - New public exports from
src/core/workspace.ts:loadWorkspace,loadOrCreateWorkspace,addRepo,removeRepo,saveWorkspace,Workspace,WorkspaceRepo. buildApplyPlanForIssue(issue, rootPath)andpickManifestPath(rootPath, issue)exported fromsrc/core/fixSuggest.ts.
Notes
- Taint analysis is intentionally heuristic: it answers "does some call chain reach from a function reading a source to a function calling a sink?" not "is this variable actually tainted at the sink?" False positives are expected for functions that launder taint safely; false negatives happen for flows through
eval'd strings or plugin loaders. Tune by adding sinks under.projscanrc.jsontaint.sinksand silencing rules underdisableRules.
[1.5.0] — 2026-05-05
Theme: "Budgeted by default" — every tool reports a token-cost estimate, projscan_review adapts its response shape to the budget the caller asks for, a new set of specialist prompts lets agents invoke a tested composition of tools by name, and projscan now learns from how you use it on this specific repo and quiets down the noise over time.
Added — cost-aware tool composition
_costsidecar on every tool result. The MCP server attaches_cost: { estimatedTokens: N }to everytools/callresponse automatically. Agents can see what they paid for a call without counting tokens themselves, which makes it cheap to budget tool sequences. Cost is the chars-divided-by-4 approximation of the serialized payload — within roughly ±15% of GPT/Claude tokenizers for code-shaped output.max_cost_tokensarg onprojscan_review— adaptive shape budget. The tool picks a tier and reshapes the response before serializing, so an agent on a tight budget gets a response sized to fit instead of a truncated full one. Three tiers:- full (no budget, or budget ≥ 7000): everything — full structural diff, per-changed-file lists, all cycles, all risky functions, all dependency changes.
- summary (3000-6999): verdict + summary + top-5 changed files + top-3 of each list (cycles, risky functions, deps), with the heavy per-file expansion arrays (exports added/removed, imports added/removed, calls added/removed) stripped. Aggregate
totalsincluded. - verdict-only (<3000): verdict + summary + base/head + aggregate
totals. Roughly 500 tokens, suitable for very tight budgets. The chosen tier is surfaced as a top-leveltierfield on the response and lifted into_cost.tierso an agent sees it in one place.
- Coexistence with
max_tokens.max_cost_tokensshapes;max_tokenstruncates. Agents can use either, both, or neither. When both fire, the shaped result is also truncated, and both_costand_budgetsidecars appear on the response.
Added — specialist prompts
Four new MCP prompts that compose existing tools into a single agent-callable recipe. Each returns a templated user message pre-filled with live project data, so the agent gets a primed prompt instead of having to orchestrate the underlying tools itself:
refactor_hotspot— given a hotspot file, produces a step-by-step refactor plan. Pulls the file detail (purpose, risk score, ownership, per-function CC, related issues) and asks for ordered changes plus risk acknowledgement. Args:file(required).triage_doctor_issues— orders the open health issues by what to fix first. Groups by category, surfaces score impact, and asks for a "critical / important / backlog" plan with a concrete next-action per item. Args:severity(optional:error/warning/info/all).review_this_pr— primes the agent with the structural diff, per-file risk, new cycles, risky function additions, and the verdict fromprojscan_review. Asks for a PR-comment-ready review in priority order with an approve / request-changes / comment recommendation. Args:base,head,package(all optional).safely_rename_symbol— produces an ordered safe-rename checklist for an exported symbol. Pulls the definition site(s), every direct caller, and the transitive blast radius viaprojscan_impact, then asks for a sequenced plan that minimizes risk. Args:symbol(required),to(optional new name).
Added — Project Memory
A local feedback loop that learns which analyzer rules this specific repo has been carrying across many runs and surfaces them as candidates to silence — without phoning home, without an LLM, without ever leaving your machine.
- Persistent on-disk store at
.projscan-memory/memory.json(auto-gitignored). Records, per analyzer rule id: when it first surfaced, when it was last seen, how many runs surfaced it, how many runs fixed it (rule appeared then disappeared), and whether the user explicitly suppressed it via.projscanrc disableRules. Schema is versioned for forward evolution. - Auto-recorded on every
projscan doctor/projscan ci/projscan analyzerun. The issue engine folds the run's rule ids into memory as a best-effort side effect; transient disk failures are swallowed so memory never breaks the analyzer pipeline. Stale rules (unseen for ≥ 90 days) are aged out automatically. projscan_memoryMCP tool +projscan memoryCLI. Subactions:current— aggregate counts (total runs, rules tracked, stable-rule count, last update).stable— rules surfaced across ≥ 3 runs over ≥ 7 days without ever being fixed and not already suppressed. Returns the list plus a ready-to-paste.projscanrc.jsondisableRulessnippet so you can quiet them in one move.runs— every tracked rule with its full observation history.forget— drop a single rule's history. Useful when you genuinely want a rule to start over.
- Privacy-preserving by design. Memory only stores rule ids and timestamps. No source content, no agent identity, no machine identifiers. The store is a sibling of
.projscan-cache/and follows the same privacy posture: local-only, gitignored, deletable.
Changed
ReviewReportgains optionaltierfield (1.5+; absent for legacy callers that don't passmax_cost_tokens).- New public functions
selectReviewTier(maxCostTokens)andshapeReviewForTier(report, tier)exported from the review module. - New public functions
loadMemory(rootPath),saveMemory(rootPath, memory),recordRun(memory, ids, suppressed),findStableRules(memory),forgetRule(memory, ruleId)exported from the memory module. - MCP tool count: 21 → 22 (added
projscan_memory). - CLI commands: 23 → 24 (added
projscan memorywith three subcommands). - MCP prompt count: 2 → 6.
Added — extending the loop
projscan_doctoradaptive shaping. Same three-tier pattern thatprojscan_reviewshipped with: passmax_cost_tokensand the doctor reshapes its response before serializing. <3000 returns verdict-only (score + grade + per-severity counts); <7000 returns a summary (top-5 issues by severity, no descriptions); otherwise the full issue list. The chosen tier is surfaced astierand lifted into_cost.tier.- Doctor surfaces stable-rule tip from Project Memory. When memory has accumulated ≥ 1 stable rules, the console doctor output includes a one-line tip: "N rules have been open across enough runs to count as accepted. Run
projscan memory stableto review and silence them." Closes the feedback loop without requiring the agent to know aboutprojscan_memory. quiet_the_doctorspecialist prompt (prompt #7). Reads Project Memory's stable-rule list, frames a PR-ready proposal: per-rule rationale, the exact.projscanrc.jsonpatch, a verification command, and a rollback note. Single MCP call → committable change.- Hotspot acceptance memory (Project Memory's second loop).
projscan hotspotsnow records the top-K into memory on every run. Files that have ranked top-K for ≥ 5 runs over ≥ 7 days without their CC/churn improving are markedaccepted— the hotspot reporter tags them as[accepted]instead of repeated noise. Surfaced via the newprojscan_memory { action: "accepted" }subaction.
Security
- Pulled in CVE patches via
package.jsonoverrides. Five transitive vulnerabilities patched without bumping any direct dependency:protobufjs6.11.5 → 7.5.6 (CVE-2026-41242, RCE in protobuf decoders),picomatch2.3.1 → 2.3.2 (ReDoS in extglob),brace-expansion5.0.4 → 5.0.5 (ReDoS via zero-step),flatted3.4.1 → 3.4.2 (prototype pollution),postcss8.5.8 → 8.5.10 (XSS via stringify). Five remainingnpm auditalerts are all in the vitest 2.1 dev chain — dev-only, never ships to end users.
Changed
ReviewReportgains optionaltierfield (1.5+; absent for legacy callers that don't passmax_cost_tokens).FileHotspotgains optionalaccepted: booleanfield (1.5+).ProjectMemorygains optionalhotspotsfield (1.5+; backward-compatible — older saves are migrated on load).- New public functions
selectReviewTier(maxCostTokens),shapeReviewForTier(report, tier),recordHotspots(memory, top),findAcceptedHotspots(memory),forgetHotspot(memory, file). - New public functions
loadMemory(rootPath),saveMemory(rootPath, memory),recordRun(memory, ids, suppressed),findStableRules(memory),forgetRule(memory, ruleId)exported from the memory module. - MCP tool count: 21 → 22 (added
projscan_memory). - CLI commands: 23 → 24 (added
projscan memorywith three subcommands). - MCP prompt count: 2 → 7.
[1.4.0] — 2026-05-05
Theme: "Session" — durable cross-invocation state so multiple agent calls (or multiple agents) can see what's been touched in the current session without re-querying git.
Added
projscan_sessionMCP tool +projscan sessionCLI. New durable session, persisted at.projscan-cache/session.json. A new session starts when no previous session exists or when the previous session has been idle for more than 1 hour (configurable). Multiple agents working in the same project share the same session. Subactions:current— session metadata (id, started/last-activity timestamps, touched-file count, event count).touched— list of files touched in this session, sorted by last-touched descending. Filterable by source (tool-result,fs-watch,explicit). Cursor-paginated.events— chronological event log, newest first. Bounded to the most recent 500 entries.reset— discard the current session and start a fresh one.
- Auto-touch from tool results. Every MCP
tools/callresponse is scanned for repo-relative file paths (under fields likefile,relativePath,paths,filePath,definitions,importers,reachable). Found paths land in the session'stouchedFilesmap with sourcetool-result. Theprojscan_sessiontool itself is excluded so reading the session doesn't pollute it. - Auto-touch from
notifications/file_changed. Whenprojscan mcp --watchis on, every debounced batch from the file watcher also records the changed paths into the session with sourcefs-watch. Agents can now ask "what's changed on disk during my session?" viaprojscan_session { action: "touched", source: "fs-watch" }. - CLI mirror.
projscan session(default subcommand:current),projscan session touched,projscan session events,projscan session reset. Supports--format jsonfor scripting and--limit Non the list views.
Changed
- MCP tool count: 20 → 21 (added
projscan_session). - CLI commands: 22 → 23 (added
projscan sessionwith four subcommands).
[1.3.0] — 2026-05-05
Theme: "Push, Don't Poll" — long-running agents stop polling for repo state; the MCP server pushes file-change notifications instead.
Added
- MCP
notifications/file_changed. Runprojscan mcp --watchand the server starts a debounced file watcher over the repo. On every batch it emits a JSON-RPC notification with the changed paths, post-update graph size, and a timestamp. Capability advertised underexperimental.fileChangedoninitialize. Off by default. projscan upgrade --check-registry. Optional network fetch fromregistry.npmjs.org/<pkg>/latestso the preview's "latest" reflects what's actually current, not just what's installed. Default stays offline; failures fall back gracefully with aregistryErrorfield. Same opt-in works through MCP viaprojscan_upgrade { check_registry: true }.
Changed
runMcpServer(rootPath, options)accepts{ watch?: boolean }. Backwards-compatible.createMcpServerreturns aclose()method to stop active watchers.
[1.2.1] — 2026-05-05
Changed
- Replaced the nine command-demo screenshots in the README with animated GIFs.
[1.2.0] — 2026-05-05
Theme: "Reporter Parity" — two new languages, HTML reporters across diff and coverage, and per-function fan-out.
Added
- PHP as a first-class language. AST analysis for
.phpfiles: imports (use, brace lists, aliases,require/include), public exports (function,class,interface,trait,enum), file-level and per-function cyclomatic complexity, and call sites. Resolves namespaces viacomposer.jsonPSR-4 autoload (longest-prefix-match). - C# as a first-class language. AST analysis for
.csfiles with the same primitives. Imports coverusing, dotted, aliased, andusing static. Exports are public top-level types (class,record,struct,interface,enum,delegate). Reads.csprojfiles; the project's filename stem is treated as the root namespace and stripped from imports before mapping to a path. - HTML reporter for
projscan pr-diff(--format html). Self-contained page with a sortable table of changed files plus the diff hotlist. Suitable for CI artifact uploads. - HTML reporter for
projscan coverage(--format html). Highlights "scariest untested files" — rows where coverage < 50% AND risk > 50 surface as adangerrow. - Per-function fan-out across all language adapters.
FunctionInfo.callSitescarries the bare names of internal callees from each function body (deduped, nested functions excluded).FunctionInfo.fanOutis the count of those callees that resolve to a function defined elsewhere in the graph.
Changed
- Languages with full AST: 7 → 9 (PHP and C# added).
[1.1.1] — 2026-05-04
Fixed
unusedDependencyCheckno longer flagstree-sitter-*packages. These ship a.wasmgrammar that consumers vendor via a build script rather thanimport, so the analyzer couldn't see usage. Affects every project depending on tree-sitter through the wasm-vendor pattern.
[1.1.0] — 2026-05-04
Theme: "On the Map" — closes the highest-leverage parity gaps.
Added
- Rust as a first-class language. AST analysis for
.rsfiles via tree-sitter-rust. Imports cover plainuse, brace lists, aliases, glob (use foo::*), and re-exports (pub use). Exports are public-by-keyword forfn,struct,enum,union,trait,type,const,static,mod. Per-function CC names methods insideimpl Type { fn m() }asType.m. ReadsCargo.toml, including[workspace]member resolution.crate::,self::,super::paths resolve into the repo; standard-library and crates.io paths classify as external. projscan_fix_suggesttemplate foreslint-*issue ids. Pulls the rule name out of the id (eslint-no-unused-vars→no-unused-vars) and links to the canonicalhttps://eslint.org/docs/latest/rules/<rule>page. Instruction covers fix-per-docs, scopedeslint-disable-next-linewith rationale, or a config change in priority order.projscan_fix_suggesttemplate forpython-type-error-*issue ids. Covers mypy and pyright output with annotation refinement, type narrowing (isinstance,is not None), and the typed-ignore form# type: ignore[<error-code>](preferring pinned codes over bare ignores).
Changed
- Languages with full AST: 6 → 7 (Rust added).
[1.0.0] — 2026-05-04
The public no-break commitment release.
The stable surface — MCP tool names + input schemas, CLI command names + documented flags, exit codes, and JSON output keys — is now under semver protection. Breaking it requires a 2.0 with a deprecation cycle (one minor with a stderr warning, then removal in the next major).
This is a label release: the git tree at v1.0.0 is identical to v0.17.0 except for the version field and declarative-language touch-ups in the README.
[0.17.0] — 2026-05-02
Added
- Documentation reorganized around the agent journey: diagnose → review → fix → reach → live.
Deprecated
extractImports/extractExportsregex helpers infileInspectorare now annotated@deprecated. They remain in place because twoprojscan_explaincallers still use them as a JS/TS-only fallback when a code graph isn't supplied. The graph-based path is strictly better and is already the primary path. Scheduled for removal in a future release.
[0.16.0] — 2026-04-30
Theme: "Live" — keeps the index fresh while the agent works, and unblocks PR-comment / CI-artifact sharing with a standalone HTML report.
Added
projscan watchCLI command. Long-running watcher over the repo usingnode:fs.watch(no new runtime dependency). On change, debounces 200 ms then runs the incremental graph update and re-runsdoctor, printing a one-line status. Filters.git,node_modules,dist,.projscan-cache, and similar so noise doesn't trigger re-scans. Clean shutdown onSIGINT/SIGTERM.incrementallyUpdateGraph(graph, rootPath, changedPaths[])public API. Targeted re-parse of the listed paths followed by an O(N) rebuild of the cross-file derived indexes. Returns the same graph reference (in-place update).- HTML report export (
--format html). Renderers fordoctor,hotspots,coupling,review, andimpact. Single self-contained HTML document with inline CSS, no external assets, and aprefers-color-schemeaware palette.
Changed
ReportFormatwidened from'console' | 'json' | 'markdown' | 'sarif'to also include'html'.
[0.15.0] — 2026-04-27
Theme: "Reach" — answers the question what breaks if I change this? before the agent commits to a refactor.
Added
projscan_impactMCP tool +projscan impactCLI. Transitive blast-radius analysis. Two modes:- File mode: pass a repo-relative path; returns every file that transitively imports it, ranked by BFS distance.
- Symbol mode: pass a symbol name; returns the file(s) that define it, the files that directly call it, and the transitive importers of those callers.
Cycle-safe; depth-bounded by
max_distance(default 10) with atruncatedflag when the limit is hit. Use this BEFORE renaming or deleting an export.
- Per-function fan-in.
FunctionInfoandFunctionDetailgain optionalfanIn?: number. Counts how many other files include the function's bare name in their call sites. Useful as a "is anyone using this?" signal. - Sub-file embeddings. Opt-in semantic-search mode that embeds each function separately instead of each file. Set
sub_file: trueonprojscan_search(or--sub-fileon the CLI) when running in semantic mode. Hits return afunction: { name, startLine, endLine }field pointing at the matched function.
Changed
- MCP tool count: 19 → 20 (added
projscan_impact). - Semantic-search cache version bumped; old caches are discarded silently and rebuilt on first run.
[0.14.0] — 2026-04-26
Theme: "Agent Fix Loop" — closes the diagnose → fix half of the agent's loop. projscan was already great at telling agents what's wrong; now it tells them what to do about it, in structured form.
Added
projscan_fix_suggestMCP tool +projscan fix-suggestCLI. Rule-driven action prompt for any open issue. Input: anissue_id(fromprojscan_doctor/projscan_analyze) OR afile+rulepair. Output: a structuredFixSuggestionwithheadline,why,where,instruction, and optionalsuggestedTest/relatedFiles/references. Hand-tuned templates for ~12 common issue id families plus a severity-anchored generic fallback. No LLM inside projscan — the driving agent is the LLM, and projscan supplies the structured prompt.projscan_explain_issueMCP tool +projscan explain-issueCLI. Deep-dive on one open issue: severity, surrounding code excerpt, other open issues touching the same file, similar past commits viagit log --grep=<rule>, plus the structuredFixSuggestion.- Inline
suggestedActionon issues. Each issue fromprojscan_doctorandprojscan_analyzecarries an optionalsuggestedAction: { summary }field. Console and markdown reporters surface it inline (→ <hint> (projscan fix-suggest <id>)). - Cross-package import policy analyzer for monorepos. Reads
.projscanrcmonorepo.importPolicy: [{from, allow?, deny?}]and walks cross-package edges. Each violation surfaces as across-package-violation-Nissue (severity warning, category architecture). Glob support:*,pkg/*,*/sub. Off by default; capped at 50 violations per run.
Changed
- MCP tool count: 17 → 19 (added
projscan_fix_suggest,projscan_explain_issue).
[0.13.0] — 2026-04-26
Theme: "Agent Review"
Added
projscan_reviewMCP tool +projscan reviewCLI. One-call PR review for the agent: composes the structural diff with per-changed-file risk scores, new/expanded import cycles, risky function additions (high-CC adds or significant CC jumps), and dependency changes across the root and every workspace manifest. Returns a verdict (ok|review|block) and a one-line summary. Defaults:base=origin/main(falls back to main/master/HEAD~1),head=HEAD.--package <name>(orpackageMCP arg) scopes to a single workspace. Markdown reporter output is suitable for posting as a PR comment.- Per-function cyclomatic complexity.
LanguageAdapter.parse()returnsfunctions: [{name, line, endLine, cyclomaticComplexity}]for every adapter. Names are qualified for methods (Class.method), constructors (Class.<init>for Java), and Go methods (Receiver.Method). Surfaced viaprojscan_fileand a newview: "functions"arg onprojscan_hotspotsthat flattens results into the top-N riskiest functions. - Cycle promotion to
projscan_doctor. Tarjan-detected circular imports lift from coupling output into the doctor issue list ascycle-detected-Nissues (severity warning, category architecture). Each cycle yields one issue with up to 8 file locations; capped at 20 cycles per run. - Workspace-aware
dependenciesandaudit.--package <name>flag (CLI) andpackagearg (MCP) scope to a single workspace.DependencyReportgains an optionalbyWorkspacefield;DependencyRiskgains an optionalworkspacefield. Backwards-compatible — both absent for single-package repos.
Changed
- MCP tool count: 16 → 17 (added
projscan_review). - Cache version bumped to persist per-function CC; old caches discarded on first run.
[0.12.0] — 2026-04-25
Added
- Java as a first-class language. AST analysis for
.javafiles via tree-sitter-java. Imports cover typed (import java.util.List;), wildcard (import java.util.*;), and static (import static java.lang.Math.PI;) forms. Exports are public top-level types (class,interface,enum,record,annotation_type). Source-root resolution prefers conventional Maven/Gradle layouts (src/main/java,src/test/java). - Ruby as a first-class language. AST analysis for
.rbfiles via tree-sitter-ruby. Imports coverrequire,require_relative,load,autoload. Exports are top-levelclass,module,def. Project layout detection covers gem (Gemfile/*.gemspec→lib/), Rails (config/application.rb→app/,lib/,config/), and plain. callSitesextraction for Python and Go. "Who callsfoo()?" now works on Python and Go repos.- Workspace-aware
outdated. Per-package result entries;--package <name>flag (CLI) andpackagearg (MCP) to scope. - Workspace-aware unused-dependency check. Each manifest is checked against imports under that package's directory.
- Semantic-search discoverability hint.
projscan searchprints a one-line tip on stderr when the optional semantic peer is missing.
Removed
- Telemetry subsystem.
projscan_telemetry,projscan telemetry, the.projscanrctelemetryblock, and thePROJSCAN_TELEMETRYenv override are gone. The opt-in local JSONL writer was paying maintenance cost without an aggregation pipeline behind it.- Migration: nothing required if telemetry was off (the default). If enabled, the config key is now silently ignored. Delete any accumulated JSONL events at
~/.projscan/telemetry.jsonlto reclaim the space.
- Migration: nothing required if telemetry was off (the default). If enabled, the config key is now silently ignored. Delete any accumulated JSONL events at
Changed
- MCP tool count: 17 → 16 (dropped
projscan_telemetry). - Languages with full AST: 4 → 6 (Java, Ruby added).
[0.11.0] — 2026-04-25
Added
- AST-derived cyclomatic complexity for JS/TS and Python. Per-file CC is persisted in the code graph and the index cache. Counted decision points:
if,else if/elif,for/for-in/for-of,while/do-while,case(default does not count),catch/except,?:,&&/||/??,and/or, comprehensionif. Optional chaining andelsedo not count. - CC replaces LOC in the hotspot risk score. Files outside the language-adapter set keep the LOC fallback so behavior degrades gracefully.
projscan_couplingMCP tool +projscan couplingCLI. Per-file fan-in / fan-out / instability (Bob Martin's I = Ce / (Ca + Ce)) and circular-import cycles (iterative Tarjan SCC, size ≥ 2). Filters:--cycles-only,--high-fan-in,--high-fan-out,--file <path>. Cross-package edges surface in monorepos.projscan_pr_diffMCP tool +projscan pr-diffCLI. Structural diff between two refs. Per file: added / removed / modified plus explicit lists of exports added/removed, imports added/removed, call sites added/removed, ΔCC, Δfan-in. Greedy similarity-based rename detection on exports.- Monorepo workspace detection. Handles npm/yarn workspaces, pnpm (
pnpm-workspace.yaml), Lerna, modern and legacy Nx, and apackages/*+apps/*+libs/*fallback. Turbo is treated as a marker on top of npm/yarn/pnpm. projscan_workspacesMCP tool +projscan workspacesCLI. Lists every package (name, relative path, version, root flag).--package <name>flag onhotspots,coupling,analyze,doctor,structure,coverage,search, andpr-diff(CLI flag and MCPpackagearg). Scopes results to a single workspace.- Go as a first-class language. AST analysis for
.gofiles via tree-sitter-go. Single-line and parenthesized import blocks including aliased forms. Go's mechanical export rule (leading uppercase Unicode letter) forfunc,method,var,const, plus struct/interface/type.go.modprovides the module path; matching imports resolve into the repo, everything else is external.
Changed
- MCP tool count: 13 → 17.
- Cache bumped to v3; old caches discarded on first 0.11 run.
Migration note on hotspot scores
CC is much smaller than LOC for the same file (a 200-line file might have CC of 10–20 vs LOC of 200). Absolute hotspot scores will drop for adapter-parsed files (JS/TS, Python, Go), even though rankings improve. If your CI uses a hard threshold against riskScore, recalibrate it after the first 0.11 run.
[0.10.0] — 2026-04-24
Theme: "Beyond JS" — Python is now a first-class language. The import graph, code search, hotspot analysis, dead-code detection, and MCP tools all work on Python repos.
Added
LanguageAdapterinterface. Abstraction that lets every core primitive (parse, resolve imports, detect packages) be implemented per-language. The existing Babel-based code is wrapped as thejavascriptadapter; the new tree-sitter-based Python implementation is thepythonadapter. Third parties can add new languages by implementing the interface and callingregisterAdapter.- Python parser via tree-sitter.
web-tree-sitter0.26.8 runtime plustree-sitter-python0.25.0 grammar. Both wasm artifacts are vendored at build time; zero network at runtime. - Python imports / exports / resolver. Captures
import,from ... import, relative imports (from .,from ..mod), aliased imports,from x import *, conditional imports insidetry/except ImportError.__future__imports are filtered. Honors__all__as the authoritative export allowlist when declared as a literal list/tuple. - Python package-root detection. Reads
pyproject.toml(PEP 621, Poetry, setuptools),setup.py,setup.cfg,requirements*.txt. Falls back to__init__.pyplacement, then the repo root. - Four new Python analyzers.
pythonTestCheck(pytest / unittest / nose / ward),pythonLinterCheck(ruff / flake8 / pylint and black / ruff-format / autopep8 / yapf),pythonDependencyRiskCheck(deprecated, soft-deprecated, heavy, unpinned, missing-lockfile),pythonUnusedDependencyCheck(with PEP 503 name normalization). - Default ignore list extended for Python noise:
venv/,.venv/,env/,__pycache__/,.tox/,.pytest_cache/,.mypy_cache/,.ruff_cache/,.eggs/,*.egg-info/.
Changed
deadCodeCheckrewritten language-agnostic.__init__.pyis treated as a barrel equivalent (likeindex.ts); pytest test-file conventions are skipped.codeGraphresolution order flipped to local-first. Every adapter'sresolveImportgets a shot at local resolution before the specifier is classified as a third-party package.
[0.9.2] — 2026-04-20
Security
Fixes a path traversal / arbitrary file read in the projscan_upgrade MCP tool.
Severity: HIGH. Users who expose projscan mcp to an AI agent that processes untrusted content should upgrade.
What was wrong. The package argument to projscan_upgrade was forwarded to previewUpgrade without validation. The implementation called path.join(rootPath, 'node_modules', name, ...) which normalizes .. segments. A name like ../../../other-project escaped node_modules/ and caused the tool to return the contents of an arbitrary CHANGELOG.md / History.md file plus the version from any package.json in the traversed directory.
Exploit model. An AI agent using projscan over MCP processes untrusted content (README, issue body, web page) that contains a prompt-injection payload instructing it to call projscan_upgrade with an attacker-chosen package argument. Without the fix, the returned changelogExcerpt exfiltrates files outside the project root.
Fix (defense in depth).
isValidPackageName(name)rejects anything not matching the npm package-name grammar:^(?:@[a-z0-9][\w.-]*\/)?[a-z0-9][\w.-]*$. Rejects..,/(except the single scope separator),\, whitespace, null bytes, absolute paths, and overlong names.- Even if a future regression let a bad name through,
readInstalledVersionandreadChangelognow resolve the target againstnode_modules/and refuse any path that escapes it.
Scope. Only previewUpgrade (and the projscan_upgrade MCP tool / CLI) was affected. Other MCP tools (projscan_file, projscan_explain) already enforced root containment.
isValidPackageName is exported for downstream users who want the same check.
[0.9.1] — 2026-04-20
Changed
- Removed em dashes from all public-facing surfaces (documentation,
package.jsondescription, CLI banner/help, MCP prompt text). Replaced with hyphens, colons, or periods depending on context.
[0.9.0] — 2026-04-20
Theme: "True Semantic Search (opt-in)" — embeddings-based search via an optional peer dependency. Default install stays small.
Added
@xenova/transformersdeclared as an optional peer dependency. Default installs are unaffected; users who want semantic search opt in.- File-level embeddings via
Xenova/all-MiniLM-L6-v2(384-dim, quantized, ~25 MB). Disk cache at.projscan-cache/embeddings.binkeyed by model + mtime + content hash; invalidates on any change. projscan_searchgains amodeargument:lexical(default, BM25 only — no peer needed),semantic(embeddings only — requires peer),hybrid(BM25 + semantic via Reciprocal Rank Fusion).- CLI:
projscan search --mode <m>and the--semanticshortcut.
Fixed
- Progress emitter context could leak between concurrent tool calls. Previous implementation stored the current emitter on a module-level variable; under MCP pipelining, call A's progress events would route to call B's client. Rewrote using
AsyncLocalStorageso everywithProgresscall gets an isolated context.
Migration
If you just want the CLI, do nothing — projscan still works end to end.
If you want semantic search:
npm install @xenova/transformers
projscan search "which file implements auth" --mode semantic
The first run downloads the model (~25 MB) into the local HuggingFace cache. All queries stay offline after that.
[0.8.0] — 2026-04-20
Theme: "Streaming & Pagination" — MCP agents can now consume large responses incrementally.
Added
- MCP protocol 2025-03-26 with version negotiation. Clients on 2024-11-05 still work — the server echoes their version when supported.
- Cursor-based pagination on list-returning MCP tools:
projscan_hotspots,projscan_search,projscan_audit,projscan_outdated,projscan_coverage. Acceptcursor+page_size; returnnextCursorwhen more results exist. - Progress notifications (
notifications/progress) during long-running tools. Agents that set_meta.progressTokenon the request get per-milestone updates. - Opt-in response chunking. When the caller sets
stream: true, tool output is split into multiple MCPcontentblocks. Default behavior unchanged. createMcpServergains anotifyoption for transports that want to emit out-of-band JSON-RPC notifications.
Fixed
--changed-onlysilently dropped issues without file locations. Now emits a stderr message:"N issue(s) filtered out; X had no file location".- Hotspot substring fallback had incomplete path-boundary chars. Added
.,?,!,>,<so cases like "see src/a.ts." (sentence end) correctly link tosrc/a.ts.
[0.7.0] — 2026-04-20
Theme: "Smart Search" — ranked local search across content, symbols, and paths. No embeddings, no API calls.
Added
- BM25-ranked inverted index over source files. Indexes content, exported symbol names, and path tokens separately, each with its own weight.
- Query expansion. camelCase / snake_case / digit splitting, light stemming (strip trailing
-s/-ing/-ed), stopword + keyword filtering.userAuthTokenindexes asuser,auth,token. - Symbol-match boost. Files that export a name matching the query rank higher than files that merely mention it.
projscan_searchgains theautoscope (default, BM25-ranked content + excerpt) joining the existingsymbols/files/contentscopes.projscan search <query>CLI command. Supports--scope,--limit, and all output formats.
Fixed
- MCP budget sidecar corrupted array responses. When a handler returned an array and the budget truncated it, the server spread the array into
{ ...value, _budget }— producing{ "0": …, "1": …, _budget }garbage. Now wraps non-object values as{ value, _budget }. - Hotspot ↔ issue linking used fragile substring matching. Issues about
src/ab.tscould falsely attach tosrc/a.ts. Now prefersissue.locationswhen present.
[0.6.0] — 2026-04-20
Theme: "Agent-First" — projscan repositions as an MCP-native code-intelligence tool. The CLI still works identically.
Added
- Real AST parsing via
@babel/parser— replaces regex infileInspector. Handles JS/TS/JSX/MJS/CTS with decorator, dynamic-import, top-level-await, and error-recovery support. - Code graph primitive. Files + exports + imports + call sites with bidirectional edges, built from real ASTs. Relative-import resolution covers extension inference, barrel files (
foo/index.ts), and.jsspecifiers that resolve to.tsunder NodeNext. - Incremental index cache. mtime-keyed parse cache at
.projscan-cache/graph.json(auto-gitignored). First run populates; subsequent runs re-parse only changed files. - MCP context-token budgeter. Every MCP tool call accepts an optional
max_tokensargument. Over-budget responses are truncated record-by-record with a_budgetsidecar. projscan_graphMCP tool. Query the code graph directly. Directions:imports,exports,importers,symbol_defs,package_importers.projscan_searchMCP tool. Fast structural search. Scopes:symbols(exports),files(path substring),content(source substring with line + excerpt).
Changed
buildImportGraphis now backed by the AST-basedbuildCodeGraphinternally. API unchanged; accuracy improves.- Two new runtime dependencies:
@babel/parserand@babel/types.
Fixed (from the AST migration)
import type { X }now captured everywhere. Was silently dropped by the old regex.- Dynamic
import('./lazy.js')now captured. export * as ns from './foo.js'and other re-export shapes now captured.
[0.5.0] — 2026-04-20
Theme: "Deeper Signal"
Added
projscan coveragecommand. Parses test coverage fromcoverage/lcov.info,coverage/coverage-final.json, orcoverage/coverage-summary.json. Joins coverage with the hotspot ranking to surface the scariest untested files: high-risk × low-coverage. Works with Vitest, Jest, c8, Istanbul.- Coverage-weighted hotspot risk. Uncovered churning files bubble up the ranking; fully covered files see no change.
- Dead-code analyzer. Builds the full import graph; flags non-barrel / non-test source files whose exports nothing imports. Respects
package.json#main,#exports,#bin,#types. projscan_coverageMCP tool.
Fixed
extractImportsregex was missing type-only imports (import type { X } from './foo.js'), dynamic imports, and re-export shapes (export { x } from ...,export * as y from ...). Now handled.
[0.4.0] — 2026-04-20
Theme: "Dependency Health"
Added
projscan outdated. Offline outdated check. Compares declared versions inpackage.jsonagainst versions installed undernode_modules/and classifies drift (patch / minor / major / same / unknown). No network calls.projscan audit. Runsnpm audit --jsonand normalizes the output into a projscan-shaped report. SARIF output routes findings into GitHub Code Scanning. Graceful messages for yarn/pnpm projects.projscan upgrade <pkg>. Preview the impact of upgrading a package, fully offline. Reports semver drift, extracts the relevant CHANGELOG section fromnode_modules/<pkg>/, highlights breaking-change markers (BREAKING CHANGE,deprecated,removed support), and lists every file that imports the package.- Unused-dependency analyzer. Builds an import graph (ES imports + CommonJS requires); diffs against declared dependencies; emits
unused-dependency-<name>issues anchored to the exact line inpackage.json. Implicit-use allowlist for typescript, eslint/prettier/vite plugins, types packages, and packages invoked viapackage.jsonscripts. package.jsonline-level locations on every dependency-related issue. SARIF upload to GitHub Code Scanning annotates the offending dependency line directly in PR review.- Three new MCP tools:
projscan_outdated,projscan_audit,projscan_upgrade.
[0.3.1] — 2026-04-20
Changed
- Documentation pass for 0.3.0 features (hotspots, file, mcp, SARIF output,
.projscanrcconfig,--changed-only). - CLI banner and help text refreshed.
[0.3.0] — 2026-04-20
Added
SARIF output (
--format sarif) foranalyze,doctor, andci. Feeds directly into GitHub Code Scanning, so projscan findings show up in the Security tab as annotated results with file/line locations.--changed-onlymode. Restrictsanalyze,doctor, andcito issues in files changed vs. a base ref.--base-ref <ref>overrides the default (auto-detectsorigin/main→origin/master→main→master→HEAD~1). Makes PR CI runs ~10× faster..projscanrcconfig. Loads project-wide defaults from.projscanrc.json,.projscanrc, or a"projscan"key inpackage.json. Supports:minScore— default threshold forci.baseRef— default base ref for--changed-only.hotspots.limit,hotspots.since— defaults forhotspots.ignore— extra glob patterns layered onto the built-in ignore list.disableRules— silence rules by id (supportsrule-idor wildcardprefix-*).severityOverrides— remap a rule's severity (info/warning/error).
CLI flags always win over config; use
--config <path>to load a specific file.First-party GitHub Action (
action.yml). Composite action that installs projscan, runsprojscan ci --format sarif(optionally--changed-only), uploads SARIF to GitHub Code Scanning, and exposesscore/gradeoutputs plus a Job Summary.Issue locations.
Issuecarries optionallocations: IssueLocation[](file, line, column). Security checks populate real file/line locations (including line numbers for hardcoded secrets).
Changed
scanRepository(rootPath, { ignore })accepts optional ignore globs that layer onto the built-in list.projscan cino longer hard-codes--min-score 70; missing flag falls back toconfig.minScore, then to 70.
[0.2.0] — 2026-04-19
Added
projscan hotspots. Ranks files by risk usinggit logchurn × complexity (lines of code) × open issues × recency. Turns a flat health score into a prioritized "fix these first" list. Graceful fallback when the project is not a git repository.projscan file <path>. Per-file drill-down combining purpose, imports, exports, hotspot risk data, ownership, and the health issues that reference it.projscan mcp. Runs projscan as an MCP server over stdio. Exposes 7 tools (projscan_analyze,projscan_doctor,projscan_hotspots,projscan_file,projscan_explain,projscan_structure,projscan_dependencies), 2 prompts (prioritize_refactoring,investigate_file), and 3 resources (projscan://health,projscan://hotspots,projscan://structure).- Ownership / bus-factor analysis on hotspots.
primaryAuthor,primaryAuthorShare,topAuthors, and abusFactorOneflag (single-author + high churn ⇒ organizational risk). - Hotspot trend tracking.
.projscan-baseline.jsonsnapshots top hotspots;projscan diffreports hotspots that rose, fell, appeared, or were resolved since the baseline.
Changed
projscan diff --save-baselinenow captures a hotspot snapshot, enabling trend analysis on subsequent diffs.
[0.1.3] — 2026-03-11
Added
- Health scoring. Every
projscan doctorrun shows an A/B/C/D/F grade (0–100 score). projscan badge. Generates shields.io badge URL and markdown for READMEs.- Score integrated into all output formats (console, JSON, markdown).
[0.1.0] — 2026-03-11
Initial release.
projscan analyze— full project analysis (languages, frameworks, dependencies, issues).projscan doctor— project health check with actionable recommendations.projscan fix— auto-fix for missing ESLint, Prettier, Vitest, and.editorconfig.projscan explain <file>— file-level explanation (purpose, imports, exports).projscan diagram— ASCII architecture diagram.projscan structure— directory tree visualization.projscan dependencies— dependency audit and risk analysis.- Output formats: console, JSON, markdown.
- Detection for 30+ languages and 15+ frameworks.