Releases: DFKHelper/token-goat
Releases · DFKHelper/token-goat
Release list
v2.6.15
v2.6.14
Fixed
- Bumped the declared Node.js floor from
>=18.0.0to>=22.0.0(and the esbuild target fromnode18tonode22), matching what CI, publish, and the tarball smoke test have actually exercised all along. token-goat statsrendered the colorized rich box-table in CI instead of plain text. Non-interactive CI runners were misread as Claude Code's own terminal by the TTY heuristic; now excluded via theCIenv var.saveSessionStatecould silently drop a concurrent hook process's update under real machine load: a lock-wait timeout fell back to an unprotected write, recreating the exact clobber the lock exists to prevent. The lock-wait budget for this hot, contended call site is now much larger, so only a genuinely wedged holder or a hard failure reaches the unprotected fallback.
See CHANGELOG.md for full details.
v2.6.13
[2.6.13] - 2026-07-10 — CHANGELOG.md:7-42 (redirected from: '[2.6.13]')
[2.6.13] - 2026-07-10
Security
xlsx(SheetJS) has unpatched HIGH-severity CVEs (prototype pollution / ReDoS). Replaced withexceljsfor thexlsx-sheets/xlsx-head/xlsx-range/xlsx-querycommands. Output format (CSV-formatted sheet/range/query text) is unchanged. The module doc comment's claim of.xls/.odssupport was dropped — ExcelJS, like the SheetJS build actually in use, only reads.xlsx. See src/xlsx_extract.ts; regression-tested in tests/xlsx_extract.test.ts and tests/command_matrix_e2e.test.ts.global.dbis a single machine-wide index shared across every project ever indexed, and five read paths queried it with no project scope — leaking or misattributing data across unrelated projects sharing the same machine.token-goat map(fetchTopSymbolsinbaseline.ts),token-goat semantic(searchSemantic's sqlite-vec KNN scan),token-goat find(querySymbols),token-goat dead/types/coverage-gaps(runDead/runTypes/runCoverageGapsingraph_commands.ts), andoutline --stats/skeleton --stats(queryRefCounts) all ran unscoped:token-goat semantic/map/findrun from project A could surface chunks, symbols, or files from unrelated project B;deadcould misreport a function as ALIVE off a same-named symbol's reference in a different project; and--statsreference counts could be inflated by cross-project name collisions. All five now scope to the invoking project root via a new sharedprojectScopeClausehelper — a LIKE-based path-prefix predicate with escaped wildcards and boundary correctness (/projno longer matches/proj-other/file.ts). sqlite-vec'schunk_vectorsvec0 table has no partition column to scope the ANN query itself against, sosearchSemanticover-fetches KNN candidates, post-filters each candidate by project root, and retries once with a largerkif too few survive (fetchScopedHits,BACKFILL_MULTIPLIER) — a deliberate latency/correctness tradeoff: at most one extra query round-trip, in exchange for never leaking a chunk across projects. See src/sql_path.ts, src/baseline.ts, src/embeddings.ts, src/read_commands.ts, src/index_reader.ts, src/graph_commands.ts; regression-tested in tests/sql_path.test.ts, tests/baseline.test.ts, tests/semantic_project_scope.test.ts, tests/find_project_scope.test.ts, tests/graph_commands.test.ts, tests/index_reader.test.ts, tests/read_commands.test.ts, and tests/semantic_overflow_guard.test.ts.
Changed
- Removed the unused
USE_COLORexport fromsrc/render/ansi.ts. Computed once at module load and never read anywhere in the codebase; live callers already checkcolorStdout()/colorStderr()directly for the stream in question. See src/render/ansi.ts, src/render/index.ts. - BREAKING (JSON output):
symbol/refs/skeleton/outline --jsonnow share one envelope shape. All four commands previously emitted inconsistent JSON — a bare array in some cases, an object wrapped under differently-named keys (symbols,references) in others, depending on whether overflow_guard truncated the result. They now always return{items, truncated, totalCount}, whether or not truncation occurred, so scripts consuming--jsonoutput no longer need to branch on shape. See src/read_commands.ts; updated in tests/read_commands.test.ts. sharpand@modelcontextprotocol/sdkmoved tooptionalDependencies. Both were already lazy-imported with a try/catch degrade path (image shrinking silently passes through whensharpis unavailable;mcp-servenow reports a clear "install @modelcontextprotocol/sdk" error and exits instead of crashing the whole CLI when the SDK is missing), but sat independencies, forcing every install to pull@modelcontextprotocol/sdk's ~17 transitive packages even for users who never touchmcp-serve.@modelcontextprotocol/sdkis now lazy-import()ed only inside themcp-servecommand handler via type-only imports, so no other CLI command's startup path loads it. See src/cli.ts, package.json; regression-tested in tests/cli_mcp_server_lazy_load.test.ts and tests/image_shrink_sharp_unavailable.test.ts.
Added
token-goat doctornow warns when the index has indexed files but zero symbols. Guards against the worker-draining-to-a-stub-callback failure mode documented in this repo's own CLAUDE.md ("Critical path" section): files can be marked indexed in thefilestable while the parser never actually ran, silently leavingsymbol/read/skeleton/outline/semanticpermanently empty with no other signal. See src/cli_doctor.ts; regression-tested in tests/cli_doctor.test.ts.semantic's query embedding now carries BGE's retrieval instruction prefix. Thebge-small-en-v1.5model this project embeds with is asymmetric: query text is meant to carry a"Represent this sentence for searching relevant passages: "prefix that document/chunk text never gets.searchSemanticnow applies it to the query side only, leaving chunk/document embedding unprefixed. See src/embeddings.ts; regression-tested in tests/semantic_project_scope.test.ts.image_shrink.enabledconfig toggle. Defaults totrue; setting it tofalse(token-goat config set image_shrink.enabled false) skips the shrink-on-Read intercept entirely and passes large images through unshrunk, for users who want the raw image regardless of size. See src/config.ts, src/image_shrink.ts; regression-tested in tests/image_shrink.test.ts.- Diagnostic for languages with no symbol extractor at all. Swift, Scala, Lua, Elixir, Dart, and Zig/R files previously indexed to zero symbols indistinguishably from a genuinely empty file.
outline/skeletonnow report e.g. "Swift has no symbol extractor yet, so this file always indexes to 0 symbols regardless of its contents" instead of the generic "no symbols found" message for these languages. See src/parser_types.ts, src/read_commands.ts; regression-tested in tests/read_commands.test.ts.
Fixed
write-file/replacedropped the destination file's mode on every rewrite.atomicWriteBuffer(cli.ts) andatomicWriteCore(util.ts) both created their temp file at0o600and renamed it over the destination, so the new inode always carried0o600regardless of the file's actual mode -- on POSIX, rewriting a committed executable (achmod +xhook script, a binary) viawrite-fileorreplacesilently dropped the exec bit, and git would record it as a100755->100644mode change. Both nowstatthe existing destination andchmodthe temp file to match before the rename, falling back to the0o600default only when the destination doesn't exist yet. No effect on Windows, which has no POSIX mode bits. See src/cli.ts, src/util.ts; regression-tested (platform-gated) in tests/cli.test.ts.replacecould silently clobber a concurrent write to the rest of the file.cmdReplaceread the whole target file, computed the snippet replacement in memory, then rewrote the WHOLE file viaatomicWriteBuffer. The snippet match only protected the matched region -- a concurrent write to any OTHER part of the file between the initial read and the final rename was silently lost (last-writer-wins over the entire file), even though the snippet match itself succeeded and reported no error.cmdReplacenow captures the target'smtimeMs/sizeright after the initial read and re-checks them immediately before the rename, aborting with a clear error ("changed on disk ... replace was NOT applied") instead of overwriting the concurrent change. Best-effort, not a lock: a race between the re-check and the rename itself is accepted residual risk, consistent with this codebase's other optimistic-concurrency patterns (e.g.config set'swithFileLock). See src/cli.ts; regression-tested in tests/cli.test.ts.- The MCP
semantictool had no way to override the server process's own (often opaque) cwd.semanticis the only MCP-exposed tool among the just-scoped project-root commands (map/semantic/find/dead/stats-- the rest of that set are CLI-only, not MCP tools); it always scoped searches toprocess.cwd(), which for an MCP client can be a cwd unrelated to the actual workspace root, silently returning empty or wrong-project results. The tool now accepts an optionalprojectRootargument (matchingrunChanged's existingprojectRootnaming convention) that overrides the defaultprocess.cwd()scope. See src/mcp_server.ts, src/read_commands.ts; regression-tested against the real MCP protocol layer in tests/mcp_server_semantic_scope.test.ts. - The markdown large-file hint suggested sections that don't exist in the file. The per-basename "well-known sections" shortcut list (e.g. README → Installation/Us...
v2.6.12
v2.6.11
See CHANGELOG.md for full details.
Highlights
- Indexer/worker critical-path fixes: BOM handling on the worker's real shipping path,
indexexit-code correctness, embedding pipeline memoization, unchanged-file skip on bulk index, embed-freshness gate, folded-path SQL indexes (full-table-scan fix), and a cross-process retry-count persistence fix. - Multi-line string masking fix across the C#, PHP, Kotlin, and PowerShell language adapters.
- Session/hooks correctness: harness-detection priority fix, build-command cache fingerprinting, curl re-download existence check, Grok session-id fallback, overflow-guard coverage on symbol/refs/skeleton/outline, multi-file
catdeny, cross-agent false-deny fix, task-output size gating, and scoped git status/diff recall-dedup. - Gemini bridge deny-contract verified against its own hook docs (previously assumed, now tested).
- New Windows CI job, unified env pins between pre-push and CI, and a real tarball smoke test gating npm publish.
token-goat stats(bare command) now renders with the same rich TTY header/KPI section as--full.
v2.6.10
Added
- Salesforce Apex and metadata-XML symbol indexing. New regex adapters index
.cls/.triggerApex source (classes, interfaces, triggers, methods) and Salesforce metadata XML (.object,.field-meta.xml,.validationRule-meta.xml,.permissionset-meta.xml,.profile-meta.xml,.md-meta.xml, and related suffixes) for CustomObject, CustomField, ValidationRule, PermissionSet, Profile, and CustomMetadata symbols. Metadata files above 512 KB, and all.profile-meta.xmlfiles, are excluded from embeddings to avoid low-signal vector noise. Contributed by @krzysztofwojt.
Fixed
token-goat symbolreturned an empty body preview for Salesforce metadata symbols. Fixed by mirroringtoken-goat read's disk-reconstruction fallback inrunSymbol. Also replaced a 19-branchif/else ifdispatch chain for non-tree-sitter language extraction with a lookup table, so adding a new regex adapter is one map entry instead of a new branch.
Full changelog: https://github.com/DFKHelper/token-goat/blob/main/CHANGELOG.md#2610---2026-07-09
v2.6.9
Fixes 16 bugs found and verified across three rounds of independent bugcheck plus a Codex CLI peer review, spanning the core symbol resolver, SQL/JS path case-folding, MCP tool classification, and Windows hook invocation across the Gemini/OpenClaw/OpenCode bridges.
See CHANGELOG.md for the full list.
v2.6.8
Fixed
- Overflow-guard hint for line-range reads suggested wrong syntax (
::instead of@). Fixed by updating the hint text to use the correct separator (file.py@100-150). skeleton/outline --min-lineswith a non-numeric value silently returned zero symbols instead of erroring. Now validated withrequireNonNegativeIntlike other numeric flags.parseDiffHunksmisattributed a deleted file's hunk to the previous file.currentFileis now reset on+++ /dev/null.extractMarkdownHeadingsadvertised sections inside fenced code blocks as real headings. Now skips fence content viaeachUnfencedLine, matching its sibling extractors.- The Codex CLI and pi bridges had the same bare-PATH single-point-of-failure the Copilot CLI bridge was fixed for. Both now resolve the token-goat entry via
process.execPathand a baked-in entry path instead of depending on PATH resolution, with a PATH-based fallback for older cached installs.
Full changelog: https://github.com/DFKHelper/token-goat/blob/main/CHANGELOG.md#268---2026-07-07
v2.6.7
Fixed
token-goat statsshowed events as happening "tomorrow" once UTC crossed midnight, ahead of the local calendar day.summarize()'s day-bucket key and the rich stats renderer's header date were both derived viatoISOString(), which always renders in UTC -- so any event recorded in the evening in a negative-UTC-offset zone (where UTC has already rolled to the next day) got bucketed and displayed under tomorrow's date. Both now derive the calendar day from localDategetters via a sharedtoLocalDateKeyhelper;skill-history's timestamp column got the same local-time treatment via a newformatLocalTimestamphelper. See src/stats.ts, src/render/stats_renderer.ts, and src/cli.ts; regression-tested in tests/stats.test.ts against a fixed UTC-5 timezone so the day-boundary crossing is deterministic regardless of the host machine's real timezone.- The Copilot CLI hook shim's inner call still depended on PATH resolution, unlike its already-hardened outer command. The outer hook command invokes the shim via a baked
process.execPath, but the shim's own innertoken-goat hook <event>call shelled out to a baretoken-goat(shell: true), which depends on the npm global bin being on whatever PATH Copilot spawns the hook subprocess with -- and Copilot'spreToolUsefails closed on a non-zero exit or crash, denying every tool call for the rest of the session with no mid-session recovery (Copilot caches hook configs at startup, so renaming/reinstalling the config has no effect until a full restart).token-goat install --copilotnow bakes the running token-goat entry's absolute path into the generated hook command as a third argument, and the shim invokes it directly viaprocess.execPathfor the inner call too, falling back to the old PATH-based lookup only when that argument is absent (an older cached config). Every generated hook entry also now sets an explicittimeoutSec: 60, doubling Copilot's own 30s default as headroom for a cold first invocation (bundle load + DB open) on the four events that lackpreToolUse's documented fail-open-on-timeout carve-out -- per GitHub's hooks reference, apreToolUsetimeout itself already fails open, so this is not what causes the deny-all class, just cheap insurance for the other events.token-goat doctorgained acheckCopilotClicheck that runs the exact installedpreToolUsecommand end-to-end with a synthetic payload (through a shell, the same path Copilot uses) and separately verifies the baked node binary still exists on disk, since an nvm/fnm/volta node upgrade can silently make it stale. See src/bridges/copilot_cli.ts, src/bridges/copilot_cli_install.ts, src/cli_doctor.ts, and README.md (documents the rename-and-restart recovery path); regression-tested in tests/install_copilot_cli.test.ts and tests/cli_doctor.test.ts.
Full Changelog: https://github.com/DFKHelper/token-goat/blob/main/CHANGELOG.md
v2.6.6
Fixed
- Three indexer bugs found by a Fable bugcheck: PowerShell block comments could be opened mid-line by a
<#sitting inside a#line comment;token-goat read/briefcouldn't resolve a symbol whose indexed name literally contains a dot (TOML sections, CSS selectors); the SQL/Protobuf block-comment stripper could close a comment against its own opening asterisk. - Copilot CLI recorded zero read/edit activity in any session. The
view/edit/createtools send the file path underpath, but every token-goat handler these tools reach only looks forfile_path. The shim now addsfile_pathalongside the original key.
Full changelog: https://github.com/DFKHelper/token-goat/blob/main/CHANGELOG.md#266---2026-07-07