Releases: DFKHelper/token-goat
Release list
v2.6.12
Full Changelog: v2.6.11...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
v2.6.5
Added
- Copilot CLI hook bridge (
--copilotoninstall/uninstall) — a Node shim translates token-goat's generic hook JSON into Copilot CLI'spreToolUse/postToolUse/preCompact/agentStopwire shapes token-goat mcp-serve— an MCP server over stdio exposingread,symbol,section,outline,skeleton, andsemanticas real MCP tools, for harnesses that support MCP but not hook interception
Fixed
stripCstyleComments/stripHashCommentsweren't quote-aware, letting a stray comment marker inside a string literal swallow real codeBanditFilterbuffered issue lines out of order, scrambling or leaking source-context linesgo_test'sOK_PKG_REmissed(cached)packages, undercounting pass totals- C#'s
CONSTRUCTOR_RErequired an explicit access modifier, dropping implicitly-private constructors from the index - PowerShell adapter counted braces inside
<# #>block-comment prose, desyncing brace depth and dropping subsequent top-level symbols - 11 additional correctness bugs across hooks, git/pytest output parsing, session tracking, Kotlin/markdown parsing, and cache eviction (see CHANGELOG)
Full changelog: https://github.com/DFKHelper/token-goat/blob/main/CHANGELOG.md#265---2026-07-07
v2.6.4
Added
token-goat brief "file::symbol"— symbol body + resolved callers + containing doc section in one round-trip--statsonoutline/skeleton— per-symbol reference count and doc-coverage flag- WebFetch HTML-to-text extraction,
token-goat pdf-extract,token-goat csv-query,token-goat screenshot - tool_filters collapse for PowerShell's CommandNotFoundException noise ($_ bash pre-expansion)
Fixed
- withFileLock could steal a lock from a still-legitimately-running holder
- pre_compact's manifest could be silently suppressed by a future non-pass handler
- runBrief's caller count hid its own truncation
- runFind silently dropped matches beyond its scan limit
- config-get rejected section headers with a trailing inline comment
- csv-query's table formatter had no RFC 4180 quoting
- config boolean coercion silently defaulted invalid values to false
- dirty-reindex queue had no retry cap on a permanently stuck file
- chunkFile mislabeled a folded chunk's kind
- MCP dedup cache ignored state-changing input flags (clear/createIfEmpty/save_to_disk)
Removed
- The dead
session_starthook event
Full changelog: https://github.com/DFKHelper/token-goat/blob/main/CHANGELOG.md#264---2026-07-06
v2.6.3
[2.6.3] - 2026-07-05
Added
-
token-goat grepnow accepts multiple paths and a-C, --context <n>flag.grep <pattern> [paths...]takes zero or more paths (variadic, matching the existingpack/tokens/budgetconvention): zero still walks cwd, one behaves exactly as before, and two-plus are searched in argument order with all hits merged into one list before the existing--max-linescap runs once across the combined set, never truncated per file.-C, --context <n>capturesnlines before and after each match (clamped to the file's real range); JSON output includes a structuredcontextarray per hit, plain text renders it with grep's own:/-convention, and the field is entirely absent — output byte-identical to before — when-Cis omitted. See src/read_commands.ts and src/cli.ts; regression-tested in tests/read_commands.test.ts. -
token-goat config-getresolves keys from YAML frontmatter in Markdown files. A.mdfile's leading----fenced frontmatter block (Jekyll/Hugo/SKILL.md-stylekey: valuelines) previously fell into the TOML/INI fallback, which expectskey = valueand never matched — locating a version marker in a SKILL.md required a manual grep plus an offset/limit read instead.runConfigGetnow checks for a fence pair first, before extension dispatch, and runs the existing YAML lookup against just the fenced slice; a.mdfile with no frontmatter, or an unclosed fence, falls through unchanged to the normal TOML/INI lookup. See src/read_commands.ts; regression-tested in tests/read_commands.test.ts. -
token-goat read,skeleton, andoutlinegained a--force-refreshflag. Files touched by git operations, external tools, or direct filesystem writes bypass the normal post-edit indexing hook and were served stale symbols until the background worker's next drain cycle.--force-refreshsynchronously re-parses the target file from disk and updates the index before querying, guaranteeing the result reflects current file content regardless of index staleness. See src/cli.ts and src/read_commands.ts; regression-tested in tests/read_commands.test.ts. -
Embedding chunk boundaries now snap to symbols and headings instead of blind byte windows. A fixed-byte window could land mid-function, diluting or splitting a symbol's embedding across two chunks and degrading
semanticsearch relevance. See src/embeddings.ts. -
Every remaining harness now has real
install/uninstallwiring:--codex,--pi(plus--pi --localfor a project-only install),--gemini,--opencode,--hermes, and--openclaw. Most of these were previously documented in the README with no correspondingcli.tscommand at all, or shipped a bridge file that was never referenced byinstall. Highlights: the pi bridge (bridges/pi.ts) and opencode bridge (bridges/opencode.ts) are real, hook-tested TypeScript templates written against each harness's actual event vocabulary;--hermesis verification-only, since Hermes delegates to Claude Code viaclaude -pand inherits~/.claude/settings.jsondirectly;--openclaw(src/bridges/openclaw.ts,openclaw_install.ts) is the first fully-implemented OpenClaw plugin writer, built against OpenClaw's documentedbefore_tool_call/after_tool_callshape anddefinePluginEntryregistration (not yet dogfooded against a live gateway). A single canonicaldetectHarness()inbridges/registry.tsreplaces two previously conflicting implementations that disagreed on environment-variable names and which harnesses they recognized. See src/bridges/registry.ts, src/bridges/pi.ts, src/bridges/opencode.ts, src/bridges/openclaw.ts, and src/install.ts; regression-tested across the correspondingtests/bridges_*.test.tsandtests/install*.test.tsfiles. -
Base
installnow writes theCLAUDE.mdblock and the token-goat skill, as the README always documented. Both were real in the Python-era install but silently dropped during the TypeScript port; base install had been leaving every new user without the skill or the CLAUDE.md nudge the rest of the docs assume is present. See src/install.ts; regression-tested in tests/install.test.ts — fixing this also closed a test-isolation gap where two e2e cases would otherwise have written into a real~/.claude.
Fixed
-
Three harness bridges spoke hook protocols that didn't exist, so their integrations were complete no-ops once installed. pi's shipped bridge called
token-goat hook <event>with fabricated per-tool event names ("pre-read","post-bash", etc.);relay()silently returns"{}"for anything it doesn't recognize, so bash compression, re-read denial, image shrinking, post-edit indexing, and the compaction manifest all silently did nothing after a--piinstall. openclaw's bridge subscribed to an inventedsession_endhook (not a realHOOK_EVENTSmember) and both opencode and openclaw kept a deadGlobentry inPRE_HOOK_TOOLSwith no handler behind it. Gemini's input-key remap table was keyed backwards, so it never fired, and separately never remappedlist_directory's realdir_pathargument tofile_pathat all — everylist_directorycall lefttool_inputuntouched, so node_modules/large-file denies, hints, and dedup tracking silently never applied to that tool. All three bridges now use each harness's verified real event names and argument shapes. See src/bridges/pi.ts, src/bridges/openclaw.ts, src/bridges/opencode.ts, and src/hooks_cli.ts; regression-tested in the correspondingtests/bridges_*.test.tsand tests/hooks_cli.test.ts. -
pinever resolved throughdetectHarness().'pi'was missing fromHarnessName/KNOWN_HARNESS_NAMESentirely, so every pi session misdetected as'generic'and lost harness-specific behavior (auto-compact multiplier, per-harness README claims) regardless of the bridge fixes above. See src/bridges/registry.ts. -
Per-harness auto-compact multipliers were fully implemented and configured but never actually consulted.
getContextPressure's window computation now callsgetAutoTriggerMultiplier, so each harness's documented multiplier changes its effective auto-compact trigger point instead of every harness being treated identically. Verified end-to-end against the built binary at three multiplier values. See src/compact.ts. -
Base install/uninstall and Codex/Gemini's marker-based safety checks had several gaps.
installHooks/uninstallHooksnever backed upsettings.jsonbefore writing to it (every other bridge writer did); an existing-but-unparseablesettings.jsonwas silently clobbered instead of aborting;isCodexTokenGoatCommand/isGeminiTokenGoatCommandused a plain substring match, so an unrelated user hook whose command happened to contain the marker text could be wrongly stripped on uninstall;installHooks/isInstalledtreated a leftover legacy hook entry as "already installed," silently skipping the real hook wiring on upgrade; and Codex's own shim used a snake_casehookEventNamefallback instead of matching Claude Code's PascalCase convention. See src/install.ts and src/bridges/codex.ts; regression-tested in tests/install.test.ts. -
A dozen-plus case-folding gaps let differently-cased but identical paths silently miss each other on Windows and macOS. The rest of the codebase already follows an established
foldPath/isCaseInsensitiveFspattern (parser.ts,index_prune.ts,sql_path.ts,walk_index.ts,worker.ts); this batch closed the remaining stragglers one at a time as each was found: the three read-dedup caches and manifest merge insession.ts/compact.ts,session_store.ts's concurrent-state merge,paths.ts's duplicated drive-letter lowercasing andisNodeModulesPath(previously gated onisWindows()alone, missing macOS),project.ts's temp-boundary guard andprojectHash,session.ts's_fileLineRangeskeys,hooks_read.ts's cross-session manifest scan and reread-count lookups,snapshots.ts'spathKey()hash input, UNC host/share segments inpaths.ts, import-to-file resolution ingraph_commands.ts'sarchcommand,memory_prune.ts's duplicate-target detection,openclaw_install.ts's plugin-path membership checks, andtext_commands.ts'sisProjectFrame(regressed by routing through a drive-letter-onlycanonicalize()). See src/paths.ts, src/project.ts, src/session.ts, src/session_store.ts, src/compact.ts, src/hooks_read.ts, src/snapshots.ts, src/graph_commands.ts, src/memory_prune.ts, src/bridges/openclaw_install.ts, and src/text_commands.ts; regression-tested across the corresponding test files for each. -
Several worker/indexing paths silently produced wrong results instead of failing loudly. Two near-simultaneous
worker startcalls could both pass the startup pre-check and spawn duplicate daemons (a TOCTOU race, now closed, with the deadworker_daemon.tsremoved);indexFileSynccounted any read failure — not just a missing file — as successfully indexed, so a transient AV/editor lock silently stored a stale SHA with no error;cmdIndex's walk had no per-file try/catch, ...