Repo hygiene: dropped dead React-hooks dir, locked the package manager to npm, made
npm run buildrebuilddist/from scratch, added a command registry as the single source of truth for autocomplete +/help+ dispatch validation, lifted ACP test coverage from 0% to ~45% on the testable surface, removed allas anycasts from the settings screen, upgradedconfto v13, fixed the tsconfig sotsc --noEmitis now genuinely clean (was silently emitting 445+ errors), patched the high-severityviteadvisory, switched CI from bun to npm, addedCONTRIBUTING.md+SECURITY.md, extracted the synchronous ACP handlers + pure helpers out of thestartAcpServer()closure into a testableserverHandlers.tsmodule (89.7% line coverage on the extracted surface; 0 → 25.7% onserver.ts), migratedrelease-binaries.ymlfrom bun to npm (keeping bun only for thebun build --compilebinary step), removed the deprecated + vulnerablepkgdevDependency (0 high/critical advisories remaining), and hardened the build withnoEmitOnError: true.
Removed
src/hooks/deleted. This directory (index.ts+useAgent.ts) was a
leftover from the old Ink-based TUI: it importedreact(which is not in
package.json) and was kept out oftscvia anexcludeintsconfig.json,
so the project type-checked only because the dead code was hidden. Nobody
imported it (grep "from.*hooks/" src/returned zero hits), and it referenced
arunAgentsignature that no longer exists. Thesrc/hooks/**/*exclude
entry is removed fromtsconfig.json—tsc --noEmitis now clean with
nothing hidden.
Changed
- Single package manager: npm.
bun.lockis removed andpackage-lock.json
is now the canonical lockfile (previously both were git-ignored and drifted).
.gitignoreno longer ignorespackage-lock.json; other lockfiles
(yarn.lock,pnpm-lock.yaml,bun.lock*) stay ignored. /autocomplete trimmed. Five rarely-tab-completed utility/legacy
commands were dropped from the/dropdown —/clear,/exit,
/context-save,/context-load,/context-clear— to cut clutter. They
remain fully dispatchable and listed in/help. (/sessionswas kept in
the dropdown as a primary navigation command.)npm run buildnow wipesdist/first (rm -rf dist && tsc …). The
bun build --compilebinary step reads fromdist/, so a staledist/
(e.g. leftover output from the now-deletedsrc/hooks/dir, or a renamed
module) could ship outdated JS. A clean rebuild on every build closes that
hole.src/acp/**/*.tsadded to the Vitest coverage scope so the editor-
integration layer is measured alongsideutils/,api/, andconfig/.confupgraded 12 → 13 (^13.1.0). BringsrootSchema/ajvOptions
options, a.delete()dot-notation typing fix, and updated transitive deps
(dot-prop8→9,ajvpatch bumps). Held below v14 pending a separate
engine/compat review — the v13 bump was all this release needed.tsconfig.jsonnow declares"types": ["node"]. Without it,tsc --noEmitwas silently emitting 445+TS2591: Cannot find name 'fs'/'path'/ 'process'/…errors across 86 files —npm run buildappeared to succeed
only because the config doesn't setnoEmitOnError, so the broken JS was
written todist/alongside the errors. Withtypes: ["node"]the type
check is genuinely clean (0 errors). This was a long-standing latent issue
masked by the emit-on-error behaviour.npm audit fixpatched the high-severityviteadvisory
(GHSA-v6wh-96g9-6wx3 / GHSA-fx2h-pf6j-xcff —launch-editorUNC path +
server.fs.denybypass on Windows) by bumping the transitivevite
brought in byvitest. One advisory remains: a single low inesbuild
(GHSA-g7r4-m6w7-qqqr — dev-server-only arbitrary file read on Windows),
pulled in transitively byvitest/tsx, with no fix short of a major bump
of those dev tools. Removingpkg(below) cleared the prior moderate
advisory, sonpm auditnow reports 0 high/critical (1 low total).
Added
- Command registry — single source of truth for slash-command metadata
(src/renderer/commands/registry.ts). Previously three places carried the
same data and drifted: theCOMMAND_DESCRIPTIONSrecord inApp.ts(123
hand-typed rows driving/autocomplete), thehelpCategoriesarray in
components/Help.ts(hand-typed/helprows), and thecaselabels in
renderer/commands.ts+acp/commands.ts. NowApp.tsandHelp.tsboth
derive from the registry, and two new tests (registry.test.ts) enforce the
invariant at build time: every top-levelcaselabel in either dispatcher
must exist in the registry — adding acase 'foo':without a registry
entry now fails CI. The registry also exposesresolveCommand(),
ALL_COMMAND_NAMES, andALL_ALIASESso the dispatchers themselves can
move to registry lookups incrementally. - ACP layer test coverage. Two new test files lift the previously
untested ACP adapter from 0% to meaningful coverage:src/acp/session.test.ts(35 tests) coversbuildProjectContext,
toolCallMeta(every tool → ACP kind mapping), andbuildRawOutput
(diff formatting for edits, stdout surfacing for commands, error paths).
toolCallMetaandbuildRawOutputwere promoted from module-private to
exported specifically so they can be tested in isolation.src/acp/commands.test.ts(10 tests) coversinitWorkspace— the
filesystem bootstrap that runs on every ACPsession/new:.codeep/
creation, project initialization, the read+write permission grant, and
the informed-consent banners for custom slash commands and lifecycle
hooks. Uses an isolated tmpdir per test (same pattern as
checkpoints.test.ts).- Together they catch regressions in the data-shaping surface and the
onboarding flow without mocking the agent loop, which is exercised
end-to-end bytoolExecution.test.ts.
- Settings screen: all 7
as anycasts removed. Previously every
config.set/config.getcall inSettings.tsusedsetting.key as any
becauseSettingItem.keywas typed asstringrather thankeyof ConfigSchema. Now:ConfigSchemais exported fromconfig/index.tsso consumers can
reference its keys.SettingItem.keyis typed askeyof ConfigSchema— a typo'd or unknown
setting key is now a compile error, not a silent runtime no-op.- All writes go through a single typed
writeSetting(setting, value)
helper. The unavoidableas ConfigSchema[K]cast lives in exactly one
audited spot instead of seven call sites, andconfig.get(setting.key)
no longer needs any cast. - 13 new tests (
Settings.test.ts) pin the write path: number editing +
clamping, select cycling (forward/backward/wrap), escape-abort, and the
updateRateLimits()side effect for the rate-limit settings.
CONTRIBUTING.md+SECURITY.mdadded. Previously the repo had
neither — the README's "Contributing" section was one sentence pointing
at GitHub issues. The new files cover:CONTRIBUTING.md: the npm-based setup, the test/build/type-check loop
(including "what gets a test" guidance), code style, an architecture
reading order for new contributors, the provider-integration flow, and
the release pipeline.SECURITY.md: how to report a vulnerability (GitHub Security Advisories
preferred), what's in/out of scope for an agent that can edit files and
run shell commands, and a list of the hardening features already in place
(project permissions, confirmation modes, hook trust gate, keychain
storage, telemetry opt-out) so contributors don't regress them.- The README's "Contributing" section now links to both.
- CI workflow switched from bun to npm (
.github/workflows/ci.yml).
The workflow usedbun install --frozen-lockfile, butbun.lockwas
removed and.gitignoredwhen npm became canonical (#2), so the gate
would fail on a clean checkout. Now usesactions/setup-node@v5with
Node 20 +npm ci+npx tsc --noEmit+npm test, consistent with
CONTRIBUTING.md.release-binaries.ymlstill uses bun (its
bun build --compilestep produces the cross-platform binaries — that's
tracked as a separate migration, not safe to flip blindly). server.tsrefactor: extracted handlers + pure helpers to module scope.
startAcpServer()was a ~1300-line closure whose 12+ request handlers
(session/set_mode,session/set_config_option,session/list,
session/delete, …) were unreachable by unit tests because they captured
the live stdio transport. The refactor lifts them into two testable
surfaces:server.tsmodule-scope exports — the pure helpers
(formatToolInputForPermission,resolveLocalPath,
collectEmbeddedContext,providerHasKey,buildConfigOptions,
AGENT_MODES) now carryexportand are covered byserver.test.ts.- New
serverHandlers.tsmodule — the four synchronous session
handlers (handleSetMode,handleSetConfigOption,handleSessionList,
handleSessionDelete) plus a pureapplyConfigOptionhelper, each
taking an explicit(msg, deps)pair wheredeps = { transport, sessions }. The transport is stubbed in tests via a recorded-call
array, which is what makes the assertions readable. startAcpServer()now constructs onehandlerDepsobject and delegates
to the extracted functions, keeping the dispatch loop intact.- The async handlers (
handleSessionPrompt,handleSessionNew,
handleSessionLoad,handleSessionResume, image/vision, agent loop)
stay in the closure for now — they reach into MCP spawning, the
keychain, andrunAgentSession, each of which needs its own extraction
pass. Tracked as a follow-up. - Coverage:
serverHandlers.tsat 89.7% lines / 87% statements;
server.tsup from 0% to 25.7%. 56 new tests acrossserver.test.ts
(34) andserverHandlers.test.ts(22).
release-binaries.ymlmigrated from bun to npm (build + publish-npm
jobs).bun install→npm ci,bun run→npm run,
setup-bun→setup-node@v5(Node 20 build, Node 24 publish). Only the
bun build --compilestep keeps bun — it produces the distributed
standalone binaries and has no npm-side equivalent (the deprecatedpkg
was never wired into CI). This makes CI + release consistent with the
canonical npm toolchain (#8 fixed the same drift inci.yml).- Removed deprecated
pkgdevDependency.pkgwas unmaintained since
2023 (archived by Vercel), carried a moderate-severity advisory, and its
build:binaryscript was never invoked by any workflow — the release
pipeline usesbun build --compileinstead. Removedpkg, the
build:binaryscript, and the top-level"pkg"config block from
package.json.npm auditnow reports 0 high/critical (was 1 high).
@yao-pkg/pkg(the active fork) stays available if we ever need an
npm-side path to standalone binaries. - Added
noEmitOnError: truetotsconfig.json. Now thattsc --noEmit
is clean (0 errors), this flag hardens the build:tscwill refuse to emit
dist/if a type error slips in, so a broken build can no longer be masked
by a staledist/. Prevents regressions of the kind that #7 fixed (445+
errors silently emitted for months). - Lifted
handleListProviders+buildProviderListinto
serverHandlers.ts. The provider-catalog handler was the simplest
remaining async-safe handler — pure shape mapping overPROVIDERS+ a
singletransport.respond. Extracted asbuildProviderList()(pure,
testable shape) +handleListProviders(msg, deps)(thin wrapper). 5 new
tests pin the shape includingdynamicModelsflagging for open-ended
providers (OpenRouter, Ollama).serverHandlers.tscoverage now at
89.7% lines.