Tags: kagan-sh/kagan
Tags
fix: board UX fixes, intake dialog polish (#25) * refactor: gate src complexity before test support files Replace the logic/TUI two-tier split with src at MI 52 first and test/ support files at MI 40 second so the fast verify path scores all source before slower test fixtures. * refactor: restore logic/TUI complexity tiers and cohesive layout Score non-TUI logic at MI 52 and the TUI surface at 50, then fold artificial verifyx micro-splits back into cohesive modules so the gate matches the architecture instead of forcing file fragmentation. * feat: render intake gates with markdown two-column dialogs Replace host DialogSelect/Prompt/Confirm for the intake chain so assumptions, questions, and mode rationale are readable as markdown, with short actions beside the body (answer step stays full-width stacked). * fix: tolerate trailing commas in OpenCode plugin config Hand-edited opencode.json often keeps a trailing comma; strict JSON parse made plugin:install:prod fail with an opaque SyntaxError. * fix: make board keyboard selection reliable and add Tab cycling Keep selectedColumn synced with the highlighted card, auto-select on refresh, blur leftover host focus on open, and cycle root tasks with Tab/Shift+Tab. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: clarify intake decision action labels Approve/Reject were ambiguous when a clarifying question was on screen; Accept assumption / Override with answer name the object of the choice. * feat: lead Review menu with approve * fix: scroll task details overflow * refactor: remove test-only DI seams and dead flexibility - delete CreateTaskDependencies bag and createUpdateController's optional check/confirm/runCommand/now; tests mock modules instead - dedupe patch.ts lock/read/merge/update skeleton into withKaganUpdate - delete shellGitRunner; server uses bunGitRunner (now a const, not a factory) - board passes store instead of drilling cap/sendBackStopThreshold/checkCommand - inline buildEditorContext/listEditorSignals/listEditorDialogControls into hook - drop dead code: setupCommand field, unused checkCommand hook prop, test:ci script, HOST_DIALOG_WIDTH size param, oxfmt pass in install-plugin - fold eligible predicate into spawnHelper; store parses options once - delete update-launch.test.ts (re-implemented wiring) and duplicate gateBadges mode tests; simplify git-shell fixture (preload already isolates) * fix: strip trailing commas with string-aware scanner Raw regex fallback also removed ", }" / ", ]" sequences inside quoted config values, silently rewriting them on the reparse-and-write path.
fix: simplify verifyx gate (#19) * fix: enforce all verifyx checks and check-only pre-commit Install knip, skott, and jscpd so verifyx all runs every built-in gate, dedupe status resolution and break git/domain layering cycles flagged by the new checks, and make the pre-commit hook check-only so staged content is never rewritten. * test: make git isolation structural via test preload (#20) * test: make git hermeticity structural via preload * test: rename hermeticity wording to git isolation * test: wait for the merge-conflict notice instead of asserting notice order * test: allow slow CI git spawns to deliver the merge-conflict notice * fix: make update promotion crash-safe and trim speculative machinery (#21) * fix: make update promotion crash-safe and trim speculative machinery Restore interrupted promotions on launch, self-heal stale cache state, surface local update failures in the footer, and remove unused concurrency/path checks. * fix: harden update cleanup against unvalidated deletes and false ready Finding 1: removeStaleMarker only deletes marker.prepared when it is a kagan@<x.y.z> sibling of the current wrapper in the same scope cache; a corrupted marker naming any other path now leaves that path alone. Finding 2: removed dead restoreCurrentFromBackup / interruptedPromotion branch (the host re-downloads latest before the plugin loads, so it can never run), aligned R18.8 and design.md to the real contract, and closed the prepared-dir leak in the matched-marker cleanup path. Finding 3: prepare failure on a ready check now sets broken status and suppresses the ready toast instead of promising a restart that applies nothing; a failed cleanup now returns broken before the network check. * test: skip the host-dedupe pin when the vendored source is absent references/opencode is git-ignored, so CI checkouts never have it. --------- * refactor: simplify update marker and cleanup machinery * refactor: merge single-importer modules * refactor: extract duplicated TUI blocks * chore: drop redundant verify overrides * ci: run check workflow on pull requests only * refactor: inline single-use list-editor row wrapper renderListEditorRows only forwarded its five arguments to <ListEditorRows> and had a single caller; render it directly and drop the now-unused JSX import. * test: make merge-dialog command tests deterministic across file order The two "approving …" tests in test/tui/board/commands.test.ts flaked only on CI. Root cause: bun's mock.module is process-global and persistent, and test/tui/session/tasks.test.ts mocks src/git/runner (currentBranch -> "kagan/x") and src/git/merge (mergeTaskBranch -> ok:true). ESM imports hoist above mock.module, so whichever test file loads first wins; on the Linux runner tasks.test.ts loaded first, so the merge-dialog tests saw the leaked mocks (wrong branch, no conflict) and failed. macOS load order hid it locally. - commands.test.ts now declares its own git/runner + git/merge mocks and drives the three merge-dialog tests through reset-per-test vars (currentBranchValue, localBranches, mergeResult), so its values win for its own tests regardless of which file ran first. - tasks.test.ts's git/runner mock was incomplete (missing listLocalBranches and baseBranchFreshness); stub them so binding those exports elsewhere while the mock is active no longer throws "Export not found". - The merge dialog's onSelect returns its handler promise so tests await the real work instead of racing a render/timer. Reproduced the exact leak order locally (bun test tasks.test.ts commands.test.ts) and via a Linux-container full-suite run; green there, commands-only, and reverse. * fix: surface both promotion and restore errors on update rollback promotePreparedUpdate runs in api.lifecycle.onDispose, whose thrown errors the host logs via console.error rather than discarding. Swallowing a failed restore therefore hid it: a lost kagan@latest wrapper logged only as a promotion failure. Rethrow an AggregateError carrying both, and add a test covering the double-failure path (the single-failure restore was already tested).
fix: self-align remediation gaps (#12) * fix: remediate self-align audit findings Close merge TOCTOU on squash failure, extend push-guard path matching, emit check/setup partial and skipped badges, add filterable model and branch pickers, align R6.2 with handoff behavior, and clean up docs, dead exports, and duplicated helpers from the triage pass. * fix: close remaining self-align audit gaps Align code and specs on custom scope command matching, trust packet title/status, merge dirty-worktree guards, push-guard subshell detection, absolute cwd rejection, prompt evidence isolation, and status schema reads. Add R3.8/R17.11–13 requirements, docs corrections, and OptionBoundsSchema for settings validation. * refactor: remove trust packet export/import, keep task details view Trust packet file I/O was misleading portable handoff without restore semantics; the in-app View details summary is the only retained path. * fix: abort helper sessions before deleting a board task Deleting a task while intake was still running left the child agent writing parts after the parent session was removed, triggering OpenCode DB errors. * fix: address branch review gaps * fix: wire board settings shortcut
fix: npm keyboard, Enter submit, and duplicate helper spawn (#11) * fix: ship compiled dist, make Enter submit, dedupe intake spawn npm keyboard (Bug 1): the host's Solid compile transform skips any path under node_modules, so raw src/*.tsx published to npm never got compiled and no onMount/keymap handlers ran — every hotkey was dead. Add a per-file Babel build (scripts/build.ts) that pre-compiles src -> dist with OpenTUI's transformSolidSource, keeping @opentui/* and solid-js as external bare imports. Publish dist instead of src, move those runtime libs to optional peerDependencies, and add a prepack hook so npm pack / publish always builds first. create-task Enter (Bug 2): most terminals send bare CR for both Enter and Ctrl+Enter, so Ctrl+Enter could never submit. Plain Enter now submits from every field; Ctrl+J inserts a newline in the description. double intake (Bug 3): a duplicate plugin load in a worktree gave each module copy its own in-memory guard, so two session.updated events could both spawn intake. Share helperEntryClaims and sessionLocks on globalThis and claim the spawn (intakeOutcome: "pending") inside the serialized metadata lock before creating the child; a failed spawn clears the claim so retries still work. * fix: apply durable claim to validator spawn; build log to stderr Mirror the intake dedupe onto the validator path: onEnterReview now claims the spawn via claimHelperSpawn("validator") inside the serialized metadata lock, and spawnValidator no longer writes validatorOutcome itself, so a duplicate plugin load can't spawn two validators. Route build.ts's summary log to stderr so the prepack build during npm pack --dry-run --json doesn't corrupt the JSON package:check parses. * fix: clear helper claim on pre-spawn errors, kitty newline, robust build Adversarial-review follow-ups on the npm/keyboard/intake fixes: - server.ts: widen the intake and validator try/catch to cover resolveTaskRefs, worktreeDiffs, and the check commands. A throw in that pre-spawn work previously left {role}Outcome:"pending" with no session, so every later event returned early and the helper never retried (permanent lockout, flagged by Greptile). Any pre-spawn throw now routes through handleHelperFailure, which clears the claim. Regression tests for both paths. - create-task.tsx: Ctrl+J now inserts a newline in the description on Kitty terminals too. Kitty reports Ctrl+J as {name:"j",ctrl}, not linefeed, so the old fall-through did nothing there; insert via descriptionRef.newLine() for ctrl+j, linefeed, and shift+return. - build.ts: compile with public babel presets instead of importing OpenTUI's private scripts/solid-transform.js, which could vanish on any OpenTUI upgrade. - package.json: peer floor >=0.3.4 (matches @opencode-ai/plugin's own range); dev pins aligned to the host's @OpenTui 0.4.3 / solid-js 1.9.10. * fix: support packed production plugin install * fix: raise opentui peer floors
feat: flag when a newer Kagan release is available (#10) OpenCode installs a plugin once and caches it forever — it never re-checks npm, so users are never told a newer Kagan exists. Check the npm dist-tags endpoint at most once a day (cached in api.kv across restarts), and show `→ vX.Y.Z available` in the board footer when a newer release is out.
fix: renderer crash fix, option-parsing refactor, CI, and docs (#7) * refactor: centralize plugin option parsing in a single schema Replace the scattered per-field typeof/Number.isInteger option guards with one OptionsSchema in src/options.ts and a parseOptions() reader; task.ts, intake.ts, and validator.ts derive option values from it. Defaults and accept/reject behavior are unchanged. The complex commands/validatorModels readers are left as-is (they transform rather than validate). * ci: cancel superseded check runs and add workflow linting Add a cancel-in-progress concurrency guard to check.yml so a newer push supersedes an in-flight run, and add a path-filtered lint-workflows.yml that runs actionlint (SHA-pinned) on workflow changes. * docs: add CONTRIBUTING guide A short, plain-language starting point: prerequisites, local setup and dev-install, the bun run check gate, conventional-commit/PR conventions, and pointers to AGENTS.md and .specs. * fix: route renderer, keyboard, and keymap through TuiPluginApi The npm-installed plugin bundles its own @opentui/solid and @opentui/keymap/solid copies, whose Solid context the host never populates. useTerminalDimensions/useKeyboard/useBindings therefore threw "No renderer found" / "Keymap not found" on install (the Ubuntu crash). Route all renderer dimensions, keyboard input, and keymap registration through the host-provided TuiPluginApi instead — the one object identical across module copies. - board keys: api.keymap.registerLayer(mode "base"), matching tui.tsx - dialog keys: api.keymap.intercept via useKeyIntercept - dimensions: useRendererDimensions over api.renderer resize events - reactivity-guard test fails the build if a context hook reappears - create-task: let Enter reach the description textarea as a newline (ctrl+enter still submits) instead of consuming it
PreviousNext