assist

A CLI tool for streamlining development workflows with AI-generated commit messages, parallel verification checks, and code quality tooling · source · devlog · posts

  • v0.329 - Refining the comment gate and linking Jira

    Most of today refined yesterday’s comment gate. The first rough edge was that the gate blocked all comment edits, including removing them — so I relaxed it to allow trimming, meaning I can still delete or shrink existing comments freely while adding new ones stays gated. The bigger change was hardening the escape hatch: the pin that authorises an allowed comment is now delivered out-of-band (issuePin) rather than passed inline, so it can’t be forged in the same edit it’s meant to permit. With the gate carrying the policy properly, I dropped the old block-comments verify step, which had become redundant.

    The other feature ties backlog items to Jira. A new assist backlog associate-jira command — and a /associate-jira slash command in front of it — links a ticket to a backlog item: it validates the key shape, fetches the issue to confirm it actually exists, and stores the key, with --clear to remove the association. It’s the connective tissue I’d been missing between my own backlog and the tickets that drive some of the work.

    On the daemon side, I added a protocol-version handshake so the WSL and Windows daemons agree on a protocol version up front and a mismatch is caught cleanly at connect time rather than surfacing as confusing downstream failures. Three fixes rounded out the day: a --once session re-watches its done signal after a restore so it still auto-dismisses, the daemon no longer replays its whole log buffer to a reconnecting browser (which had been dumping a wall of stale lines on every reconnect), and the web UI now shows unmerged files in the git status.

  • v0.326 - Gating comments and unifying daemon logs

    The theme I’m most pleased with today is enforcing the self-documenting code policy at the hook level rather than hoping I remember it. A new comment edit gate is a PreToolUse hook (decideCommentGuard) that blocks Edit/Write from adding code comments, with an escape hatch: when a comment genuinely earns its place I can confirm it through codeComment and have it allowed. I also hardened the block-comments verify gate so the check can’t be quietly sidestepped. Alongside it, the maintainability gate got a proper override: a special marker comment exempts a file that legitimately can’t hit the index, and the edit-hook protects the marker itself — Edit/Write can’t add, change, or remove one — so the exemption is a deliberate act rather than something a refactor can flip. The complexity output now surfaces which files are running under an override.

    The other substantial piece was observability. Daemon logging is now unified into a single assist.log stream instead of scattered outputs, daemon session operations are logged as they happen, and I dropped a redundant lifecycle logger that was just adding noise. To go with it, a new assist daemon drain command clears all sessions from the local daemon for a clean slate, and a Windows-proxy create refusal now gets logged so I can see why a session didn’t start.

    The rest was hardening, much of it on the Windows path now that it carries real traffic. I capped the Windows connect probe at 2s and tightened the create timeout to 5s so a stuck host fails fast, surfaced a failed Windows create to the UI instead of leaving it silent, gated the Windows proxy off when assist is itself running on the Windows host, and stopped an unbounded w- growth in Windows session ids. A few general fixes too: a hard cap on session fan-out so a runaway can’t spawn endlessly, the web server now binds before the daemon is ready so the page is reachable during startup, the session-card prompt is clamped to five lines, and the 7-day usage row gets a tinted background to set it apart.

  • v0.318 - Phase names on cards and steadier status

    A small, focused day. Session cards now show the current phase name, so at a glance I can tell whether a run is planning, implementing, or in review without opening it. Paired with that, a card flips to “running” as soon as the session starts thinking rather than waiting for visible output, so the status reflects what’s actually happening instead of lagging behind.

    The remaining fixes tightened up two things from earlier in the week. I hardened the usage-peak reset detection so a reset is recognised reliably rather than occasionally being missed or double-counted, and I got the import sort to converge on oxfmt — the formatter and the linter had been disagreeing about import order, so a file could be “fixed” by one and immediately flagged by the other.

  • v0.317 - Scheduled backups and config-driven checks

    Yesterday’s backup command grew up into a scheduling system. assist backup schedule --every <duration> installs a marked crontab block that runs assist backup on a cadence — 5m becomes */5 * * * *, 6h becomes 0 */6 * * * — and re-running with a new duration replaces the block cleanly rather than piling up duplicates. Durations cron can’t represent evenly, like sub-minute or 90m, are rejected up front instead of silently misbehaving. There’s a schedule status to print the active cadence and a schedule remove to tear it down. The backup itself became self-discovering, so backup and restore locate the database on their own rather than needing the path spelled out, and the backups view now shows just the filename with the full path tucked into a tooltip.

    The verify pipeline got more configurable. A new assist verify forbidden-strings check reads rules from config — each names a JSON file, a set of dot-paths to inspect, and a disallowed wildcard — and fails if any matching value turns up, which lets me catch things like a stray dev URL in a committed config without hard-coding the check. I also fixed the generation of the auto-fix verify scripts so they come out runnable.

    The rest was cleanup. I finished the move off Biome by removing the last lingering references now that oxc is doing the work, and fixed URL detection in the web terminal so a link that wraps across lines is still recognised as one clickable URL.

  • v0.312 - Capturing browser traffic to extract LinkedIn posts

    The headline today is netcap, a new way to pull my own LinkedIn posts out of the browser. assist netcap starts a local receiver, paired with a raw Manifest V3 browser extension under netcap-extension/ that patches fetch and XMLHttpRequest to capture every request/response and forward it to the receiver. Doing the forwarding from the extension’s background context sidesteps the page’s CSP, which is what killed an earlier console-paste approach — and reading the response across every responseType rather than just responseText is what finally captured LinkedIn’s voyager GraphQL calls, which return JSON and expose no responseText. Then assist netcap extract parses the capture into structured posts — text, markdown with mentions linked, author, hashtags, related posts, and a postedAt decoded from the activity id — reading both the SDUI responses rendered on first paint and the voyager responses loaded on scroll, deduped by activity urn. Follow-ups added article-card link extraction and a permalink back to each original post. There’s a fair bit of WSL-specific plumbing too, since the browser runs on the Windows host and can’t reach a WSL localhost: the receiver copies the extension to a Windows path and targets the WSL VM’s IP.

    The other feature was a backup command and a backups view in the dashboard, so the assist database can be snapshotted and browsed rather than relying on ad-hoc exports. I also continued polishing the usage history from earlier in the week: segments now order by created_at with the newest on top, and a usage reset keeps the pre-reset peak instead of dropping it.

    A cluster of smaller improvements rounded out the day. assist verify traded its --timer flag for a clearer --measure, formats in place as part of the run, and gives clearer messages when it denies something. The session terminal now renders with WebGL for smoother output, a commit off the expected branch raises a warning before it goes through, and I fixed sessions occasionally reading from the wrong transcript by binding each session to its own.

  • v0.306 - Honest timers and a steadier proxy

    A shorter, tidy-up day. The session timer now counts only running time rather than wall-clock elapsed, so a session that’s been sitting idle or waiting doesn’t inflate its own duration — the number finally reflects how long Claude actually worked. I also pulled the edit action out of the backlog web view; with the launch and refine flows doing the heavy lifting, inline editing in the browser was more clutter than help.

    The rest was reliability. The WSL↔Windows proxy now sends a keepalive so the connection doesn’t quietly drop between the two hosts during a long-running session, the backlog web view polls for status so it stays current instead of going stale, and a restored update session now comes back with a clean done state instead of looking like it was still mid-run.

  • v0.305 - Hook-driven status and updating itself

    The most impactful change was making session status hook-driven. Instead of inferring whether a session is working, waiting, or done from the terminal output, the daemon now takes its cues from Claude Code’s own hooks — a far more reliable signal touching 41 files across the daemon and UI. On top of that foundation, a session flips to “waiting” when you hit ESC, the running chip only animates while it’s actually running, and restored session cards keep their metadata instead of coming back blank. A local daemon also labels itself by platform now, which matters once Windows and WSL daemons sit side by side.

    The other theme was letting assist update itself from the dashboard. There’s a new “Update assist” item in the web menu, and after an update the daemon restarts itself so you’re immediately running the new version. A couple of fixes made that path safe: assist commit’s pull now autostashes so a dirty tree doesn’t block it, the startup pull is skipped entirely on a dirty worktree, and daemon phases stay resumable across the restart so an in-flight run isn’t lost.

    On the workflow side, a backlog run can now pause at the review phase rather than always pushing straight through, the review UI labels its toggle “Dismiss” so the action is clear, and a new assist prs reply command lets me respond to PR review threads from the CLI. I also added a /strip-comments command that enforces a self-documenting code style — declaring the comment policy in CLAUDE.md and stripping comments that just restate the code. Rounding out the day: the terminal now honours Cmd+C/Cmd+V on macOS, and I continued the oxc migration by moving the project scaffolding over from Biome.

  • v0.300 - Swapping Biome for oxc, and remembering usage

    The headline change is a toolchain swap: I replaced Biome with oxc for linting and formatting. It touched 169 files — new .oxlintrc.json and .oxfmtrc.json configs in place of biome.json, the verify pipeline rewired to call the new tools, and the editor set up to use them. The one piece Biome had that oxc didn’t was my filename-convention rule, so I ported it as a custom plugin (oxlint-rules/filenameConvention.ts) rather than lose it. It’s a big mechanical diff, but the result is a faster lint/format step that fits the rest of the Rust-based tooling I’ve been leaning on.

    The features were about making the dashboard remember things across restarts. Peak usage history now persists, so the rate-limit indicator can show how usage has trended rather than just the current snapshot, and the selected session persists and is shared — reload the page or reconnect and you land back on the session you were looking at, and that selection is shared across connected views. A small touch on top: the badge for the currently-open session now animates, so it’s obvious at a glance which card is live.

    The rest was reliability, much of it on the Windows side now that sessions span both hosts. I bounded the Windows reconnect loop when there’s a version mismatch and bounded daemon spawn retries, so a wedged Windows host can’t spin forever; failed session restores now surface in the UI instead of failing silently; and forwarding a Windows session strips its host namespace so the path resolves cleanly on the WSL side. One backlog fix too: items without a plan now rewind correctly when sent back through review.

  • v0.297 - One database, and a lot of web polish

    The structural change today was consolidating storage. What used to be the “backlog database” is now the “assist database” — a rename that touched 71 files but reflects a real shift: it’s no longer just backlog items. The first thing I moved into it was handovers, which had been living as loose files; storing them in the shared DB means session handovers sit alongside the backlog as proper records I can query and relate. Both changes were mostly mechanical, but they set up the database as the single home for assist’s state rather than a backlog-only store.

    Most of the rest was web UI. I can now star backlog items and sessions — a star/unstar pair on the CLI backed by setStarred, plus StarAction and SessionStarButton in the UI, with starred sessions sorting to the top (sortSessionsByStar). Launching a new session now pops a toast so there’s clear feedback that something started, and I fixed a nagging slowdown when switching tabs in the web view. I also removed the old web add-item flow, which the launch buttons had made redundant.

    On the review and PR side, I added a guard against wall-of-text PR bodies — validatePrContent rejects bodies that are one giant unbroken paragraph, and the /pr command now spells out the structure it expects — so PRs come out readable instead of as a wall I’d have to reformat by hand. The complexity report now tells you how to improve a low maintainability score, explicitly nudging toward extracting functions into smaller modules rather than gaming the metric by stripping whitespace or comments. Two smaller fixes rounded it out: assist review runs codex from the repo’s working directory so it sees the right files, the Seq query now applies its time window correctly, and a backlog run commits before signalling done on its final phase so nothing is left uncommitted.

  • v0.291 - Driving Windows sessions from the WSL dashboard

    Yesterday’s TCP proxy was the pipe; today was about actually driving Windows sessions through it from the WSL dashboard. I can now launch, write to, and control a Windows-host session from the same web UI as the local ones, with paths translated across the boundary (windowsCwdToWslPath) so a session’s working directory makes sense on both sides. Two fixes kept that honest: the Windows daemon now auto-heals when it detects a version drift from the WSL side — spotting the mismatch (isWindowsCreate) and restarting itself so the two halves don’t silently run different code — and backlog origin resolution now handles the Windows case (getCurrentOrigin) so items get tagged to the right repo.

    The web nav got a few upgrades. Account rate limits are now surfaced right in the nav bar via a RateLimitsIndicator, with the daemon relaying the limits through (relayRateLimits) and a rateLimitLevel mapping them to a colour so I can see at a glance how much headroom is left before starting more work. I also added a hamburger menu that pulls the restart actions into the browser — HamburgerMenu, RestartConfirmDialog and a postRestart call — so I can restart the daemon or web server without reaching for the Ctrl+R terminal menu. With those in place I dropped the now-redundant “next” button from the web view.

    Finally, assist refactor rename file got noticeably faster. I split the work into a computeRenameRewrites planning step and an applyRename execution step, which trimmed the redundant passes that were making the rename feel sluggish on larger projects.

  • v0.286 - Reaching Windows sessions and reshaping the PR commands

    The bigger half of today went into reaching sessions that live on the Windows side of a WSL split. The daemon can now proxy Windows sessions over TCP — the WSL daemon talks to its Windows-host counterpart on a known port (windowsDaemonPort) instead of assuming everything runs locally — and a companion change lets it discover those Windows-origin sessions so they show up in the dashboard alongside the Linux ones. It’s the groundwork for treating a two-host setup as a single pool of sessions rather than two disconnected worlds.

    The other big piece was reshaping the PR commands. assist prs create became assist prs raise, and a new assist prs edit lets me rewrite an existing PR’s title and body — both built on shared helpers (buildPrBody, parsePrBody, editPrBody) so raising and editing produce the same well-formed output. To make the wrapper stick, I added a built-in cli-hook deny for gh pr edit (mirroring the existing gh pr create deny), so edits go through the validated assist path instead of raw gh. Alongside it, a new /prs-slack slash command posts a PR’s title and URL to a configured Slack channel — it reads the channel from prs.slack, refuses to post if that’s unset, and confirms the exact message before sending via the MCP Slack tools.

    A few smaller things rounded out the day. I hardened the cli-hook to fail closed when a deny is buried in a compound command rather than letting it slip through, fixed starting a session with an empty prompt, and refactored the news feeds out of their own standalone web app and into the sessions web UI and database — folding fetchFeeds, parseFeed, groupByDate and friends under sessions/web/ui/news so there’s one web server and one data store instead of two.

  • v0.274 - Taming browser pop-ups under WSL

    A short cleanup day, mostly about not having a browser tab fly open when I don’t want one. I added a --no-open flag to assist sessions, assist sessions web, and assist backlog web so the dashboard can start without launching a browser — handy when a tab is already open or when I’m starting the server from a script. The web server’s own restart now passes --no-open when it re-execs itself, so hitting Ctrl+R no longer spawns a fresh tab on top of the one that’s already reconnecting. I also fixed how the browser gets opened under WSL, which wasn’t reliably handing off to the Windows host.

    The other fix closed out yesterday’s copy/paste work: a paste in the web terminal could register twice, so I tightened handleClipboardKey to only fire once. Finally, a small piece of plumbing — the /jira slash command now fetches work items through the MCP Atlassian server (mcp__claude_ai_Atlassian__getJiraIssue) instead of shelling out to acli, with the assist jira CLI commands kept around for direct human use.

  • v0.281 - The web terminal grows up

    Most of today went into making the web sessions terminal feel like a real terminal rather than a read-only window. The headline is a restart menu for the web server itself: hit Ctrl+R and a little in-terminal menu appears, built out of small composable pieces — parseMenuKey, nextIndex, renderRestartMenu, createMenuState, createKeyHandler — that lets me restart or re-exec the running server without dropping back to the shell. I started it on a different shortcut and then moved it to Ctrl+R once it felt natural. Copy and paste landed too, with a handleClipboardKey handler wiring the usual shortcuts through to the terminal, plus a follow-up fix to stop a paste registering twice. Smaller terminal polish rounded it out: reconnecting cleanly after a server restart instead of going dead, and closing an annoying gap between the terminal and the pane beside it.

    The other big piece was a read-only history transcript view. Closed sessions now render their Claude transcript directly in the dashboard — I parse the session .jsonl (parseTranscript, findSessionJsonlPath, entryMessages, toolTarget) and render it through a new TranscriptPane/TranscriptRow, with derived history fields and chips (deriveHistoryFields, HistoryCardChips) summarising each run. It meant splitting the session area into a live TerminalArea and a TranscriptArea, and reworking the WebSocket plumbing (useSessionSocket, useWebSocket, WsDispatch) to feed both. So now I can scroll back through what a finished session actually did without resuming it.

    On the backlog side, run cards got an auto-advance toggle so a session can pause between phases instead of barrelling straight through — consumePause and setAutoAdvance carry the state through the daemon and into runPhases. Backlog cards also grew a mark-done button for clearing items straight from the dashboard, and the repo selector now orders projects by recency so the one I touched last is at the top.

    The rest was housekeeping with teeth. I added a built-in cli-hook deny for git commit, pointing it at assist commit "<message>" instead — moving the rule out of settings.json and into the hook’s own findBuiltinDeny list so it travels with the tool and gives a useful message rather than a bare denial. And I pinned overrides for a couple of vulnerable transitive dependencies to clear them out of the tree.

  • v0.273 - Resilient sessions and dashboard polish

    The daemon’s restart story got two solid fixes today. Restoring a session now resumes it through its backlog wrapper rather than a bare resume, so an interrupted run picks up where it left off instead of starting cold — I tore out the old discoverClaudeSessionId guesswork in favour of a watchClaudeSessionId watcher that reads the real session id as the process emits it. The companion fix makes a daemon restart resume the correct session, threading the Claude session id through executePhase and spawnClaude so a restart can’t reattach to the wrong terminal.

    Most of the day went into the web dashboard. The toolbar now shows git status counts — ahead/behind and changed-file tallies parsed out of git status (parseGitStatus, GitStatusCounts, useGitStatusCounts) — so you can see a repo’s state without leaving the page. Card selection got a proper going-over: it defaults to the top card on load, reselects the top card when one closes, and shows a loading state while a new card spins up, all reconciled through reconcileActiveId and a useActiveIdReconciler hook. I also cached backlog items across view switches (backlogItemsCache, itemsEqual, revalidateBacklog) so flipping between views no longer refetches and flickers, narrowed the session sidebar to 25%, evened out the top-nav spacing, reordered the toolbar actions, and fixed the cursor on the active card.

    The web prompt input got friendlier too — it submits on Enter, and the prompt dropdowns now handle multiline content properly via a shared FreePromptForm. The terminal renders clickable links. On the backlog side, a refine job can now be kicked off straight from a session card, auto-run reuses an existing draft or bug card instead of spawning a fresh one (reuseSessionForRun, respawnedIds), and refine cards auto-run correctly when marked done.

    Finally, assist review got more trustworthy: findings are now validated against the actual diff before they’re posted, so the reviewer can’t comment on lines that aren’t part of the change. I built out a diff line index (buildDiffLineIndex, annotateDiffWithLineNumbers) and a partitionFindingsByDiff split that keeps in-diff findings and warns about the rest. Reviews now persist under the home dir, and maintainability runs accept an --ignore glob to skip paths.

  • v0.264 - Session cards that show their work

    The dashboard’s session cards were opaque — a terminal and not much else. Today I gave them chips and live activity. A card now shows what kind of session it is (sessionType), a readable title derived from the command (sessionTitle), and a repo label, and it surfaces live activity emitted from the running process via a new watchActivity/emitActivity pipe, so you can see a session doing something without scrolling its terminal. The backlog chip links straight to the item’s page, and the review-phase chip highlights when a backlog run reaches review, so the card tells you where in the pipeline the work is at a glance. I also added a per-card auto-run toggle, so you can decide whether a given session chains automatically or waits for you.

    The repo selector got a proper pass too. It’s keyboard-searchable now — type to filter, arrow keys to move, enter to pick — built out of RepoFilterInput, RepoMenuList, and a useRepoKeyboardNav hook that replaced the old custom-path input. It also syncs to the active card: focus a session and the selector follows to that session’s repo (useSyncRepoToActiveCard + deriveActiveCwd), so the toolbar context tracks what you’re looking at. And next to the existing “open in VS Code” action, there’s now “open in GitHub”, which resolves the selected repo’s preferred remote and opens it in the browser.

    On the correctness side, I added a new verify check: assist verify comment-policy. It flags comments added on changed lines — staged and unstaged — unless they carry a justification marker, which enforces the “only comment unintuitive complexity” rule I keep reminding myself of, and it’s wired into the verify run set filtered to TypeScript files. The daemon also got a run of reliability fixes from yesterday’s restart work: backlog sessions now restore through their wrapper rather than a bare resume, session activity persists across a restore, the session lock is released on dismiss and review-comments launches in acceptEdits mode. I also moved the backlog lock and activity files under ~/.assist so they live in one place.

    The rest was performance and polish. The backlog list and web index were loading full items just to render summaries, so I split out lightweight summary loaders (loadBacklogSummaries, loadItemSummaries) and lightened both the whole-list and index paths. The maintainability-index formula now prints on every maintainability run rather than just the one report, the free-prompt button got renamed to plain “prompt”, and I extracted isRunLink into its own file to tidy up types.ts.

  • v0.258 - Launching backlog work straight from the web UI

    Yesterday’s daemon work made sessions durable; today was about driving them from the web UI instead of dropping back to the terminal. The headline is a Play button on backlog items: from an item card or its detail panel I can now kick off assist backlog run for that item and have the session appear in the dashboard, wired up through a new useSessionLaunchContext. The button only shows where it makes sense — canPlay gates it to todo items, including todo bugs, which I’d missed in the first pass. Alongside it, a Refine button on the item detail launches /refine for that item directly, so the two things I most often want to do with a backlog item are one click away.

    I also widened how sessions get launched. The draft and bug commands now take an optional description — assist draft "add a foo" forwards as /draft add a foo — and the web UI’s mode buttons grew a free-prompt dropdown so you can type that prompt text in the browser before spawning. There’s a new “open in VS Code” action in the toolbar that opens the selected repo in an editor without leaving the dashboard. And on the CLI side, a purely numeric assist run <n> that matches no configured command now falls through to assist backlog run <n>, forwarding the --write flags — so assist run 42 just runs backlog item 42.

    With more ways to start sessions, I cleaned up how they end and how the list behaves. --once sessions now auto-dismiss when their done signal arrives, via a small shouldAutoDismiss rule wired into the status-change path, so single-task sessions clear themselves out. The close button is always shown on a session card now rather than appearing conditionally, and the card’s elapsed timer freezes once the session is done instead of ticking forever. I also dropped the old sidebar run-command list — a chunk of UI (RunButtons, ModeSelector, NewSessionForm and friends) that the new launch flows made redundant.

    The backlog list itself got faster and more correct. Completed-item filtering moved server-side into loadVisibleItems so the browser isn’t pulling down done items just to hide them, and the web list now sorts newest-first. A clutch of fixes rounded it out: cancelling a stale backlog fetch when you switch projects so the previous repo’s items don’t flash in, refreshing session history on tab switch, and resuming a run correctly when its review phase had been rewound. Smaller still, the complexity report now prints the maintainability-index formula alongside the score so the number isn’t a black box.

  • v0.245 - Sessions that survive a server restart

    The big change today was pulling web sessions out of the web server and into a long-lived daemon. Until now, restarting assist sessions killed every running terminal with it — fine when the server never changed, painful the moment I was iterating on it. So I split the two: web sessions are now owned by a daemon process, and the server is a thin client that relays WebSocket traffic to it over a local IPC socket (a unix domain socket at ~/.assist/daemon/daemon.sock, or a named pipe on Windows). Restart the server and the sessions keep running with their scrollback intact. The daemon logs to its own file, auto-exits once no sessions remain and no client has connected for 60 seconds, and gets respawned on demand by the server when it’s needed again.

    This was a large move — SessionManager, createSession, wirePtyEvents, writeToSession and the rest of the session core all migrated from web/ into daemon/, with new pieces for connecting (connectToDaemon, handleConnection, greetClient), persistence (loadPersistedSessions, restoreSession), and lifecycle (runDaemon, restartDaemon, shutdownSessions). It comes with a small command surface: assist daemon run, status, stop, and restart. The mental model I landed on: web server changes only need assist sessions restarted and sessions survive, while daemon or session-core changes need assist daemon restart to load the new code — which kills the PTYs but auto-respawns claude sessions via claude --resume, with run sessions reappearing as not-restored tiles you can retry.

    Rearchitecture like that always shakes out races. assist daemon restart and concurrent assist sessions invocations could both try to spawn a daemon and orphan one of them, so I added a proper bring-up path — a PID-file watchdog (startPidFileWatchdog), socket-theft detection (reportStolenSocket), and an onListening handshake — so only one daemon ever owns the socket and the loser steps aside cleanly. I also fixed a PTY spawn failure that was crashing the whole server: the spawn helper now gets its executable bit ensured up front, and a spawn error surfaces as an error snackbar in the UI over the WebSocket instead of taking the process down with it.

    The other feature was --once. The launcher commands — assist next, draft, bug, refine — normally chain: finish one item and they prompt for the next. With --once they exit after the first completed run instead, which is what I want when I’m spawning a session for a single task and don’t want it sitting around waiting. That’s backed by a new assist signal done marker that --once sessions watch for and plain sessions ignore.

    A few smaller things rounded out the day. assist review now ensures .assist/reviews is gitignored before it writes there, so review artifacts don’t leak into commits. The web UI got a pointer cursor on buttons and links (a small thing that bugged me every time), and backlog items that are in progress now get a highlight and an InProgressChip so the active work stands out in the list. And I tightened the /draft and /refine prompts to enforce vertical slicing in the plans they produce.

  • v0.243 - Review by PR number, and an org leaderboard

    Most of today was on assist review, which orchestrates two LLM reviewers (Claude and Codex), synthesises their findings, and posts them as pending line comments. The breaking change: assist review <number> used to mean SHA mode — diff a single commit and write the review files to disk without touching GitHub. I almost never used it, and the surface cost was real: a whole second pipeline (reviewSha, gatherShaContext, buildShaRequest) shadowing the main one. So I ripped SHA mode out and repurposed the positional argument: assist review <number> now runs gh pr checkout <number> first and then reviews that branch exactly as it would the current one. If the checkout fails — dirty tree, unknown number — gh’s error surfaces and the review aborts. One pipeline now, and reviewing someone else’s PR is a single command.

    Two more review changes. I hit a wall reviewing a large PR: GitHub rejects gh pr diff with a 406 too_large once a PR crosses 300 files. fetchPrDiff now catches that specific error and falls back to fetching the base and head SHAs and running a local git diff base...head, so big PRs review instead of erroring out. And I added assist review --backlog, which after synthesis spawns Claude to file every finding as a single bug backlog item — one phase per finding, each named after the finding with its location, impact, and recommendation captured as tasks. It’s the natural bridge from “here are the problems” to “here’s the queue of work,” without me copying findings across by hand.

    Away from review, assist github commits <org> is a new org-wide commit leaderboard: it walks every repo in an organisation over a window (default 30 days, --since to override), filters to repos with activity, and reports commit counts per repo and aggregated per committer, with --top <n> and --json. The shared GraphQL helper moved from prs/ up to shared/ since both surfaces use it now. Finally, a chunky web UI refactor: the repo selector is now shared between the backlog and sessions toolbars via a useRepoSelectionContext, instead of each view carrying its own filter row — one place to pick which repo you’re looking at, consistent across tabs.

  • v0.239 - A PR review gate and some shortcuts

    The headline today is a review gate around PR creation. I kept ending up with PRs whose titles or bodies leaked a stray “Claude” reference, and I wanted a hard stop rather than a habit. So assist prs create --title <title> --body <body> is now the sanctioned path: it runs validatePrContent, which refuses any title or body mentioning Claude, then delegates to gh pr create with the assembled args. To make sure the wrapper actually gets used, the cli hook gained a findBuiltinDeny layer — gh pr create is now denied regardless of settings or per-project config, with a message redirecting to assist prs create. The /pr command instructions were updated to match, and they now require the full proposed title and body to be surfaced through AskUserQuestion — with the text in the approve option’s preview field — before anything runs, in every permission mode including bypass. The point is that I see exactly what’s about to be published, every time.

    A few shortcuts to cut keystrokes. There’s a top-level list (aliased ls) that forwards straight to backlog list with all its filter flags, so I can type assist ls from anywhere. assist review-comments [number] checks out the given PR (when supplied) and spawns Claude on /review-comments with edits enabled — it pulls the launcher logic out of index.ts, which was getting crowded. And backlog next now accepts an optional start id, so I can point it at a specific item to begin the run loop from instead of always letting it pick.

    Two more, both about reconciling state. assist backlog move-repo <old> [new] retags every item from one origin to another — the fix for items left stranded under a stale origin after a repository rename. It infers the destination from the current repo’s remote when omitted, accepts a bare repo name for the old origin when it unambiguously matches one stored origin, and confirms the count before updating. Finally, a refactor-extract bug: when a file had a leading biome-ignore suppression comment but no imports yet, ts-morph inserted the new import at index 0 — above the suppression — detaching it from the top of the file. addImportPreservingSuppressions now counts the leading suppression comments and inserts the import below them.

  • v0.234 - A day of backlog polish

    A grab-bag day, all of it sanding down rough edges on the backlog flow I’ve been living in. The command ergonomics got the most attention. backlog run and backlog next now default to auto permission mode — running an item almost always means I want Claude to edit, so -w/--write became the default and a --no-write opt-out was added rather than forcing the flag every time. I added a refine alias on the backlog command, and while I was in registerBacklog I pulled the long list of registerX(cmd) calls into a registrars array that gets iterated, so adding a command is one line instead of two. The other ergonomic win was pullIfConfigured — when commit.pull is set, backlog commands now run git pull --ff-only first and abort on failure, so I stop running an item against a stale tree.

    The data layer got a targeted-writes pass. After the Drizzle move, per-item commands were still loading the entire backlog into memory, mutating one item, and saving everything back. I replaced loadAndFindItem with findOneItem, which does a single targeted read by id and hands back the resolved orm so the caller can follow up with a targeted write. The whole-backlog saveBacklog is still there for import/restore tooling that genuinely needs to reconcile everything, but the common path no longer round-trips the world to touch one row.

    The web UI got some polish too: a loading spinner on the backlog list while a search is in flight (the body moved into its own ListBody component), backlog chips now derive their colours from the active theme’s contrast instead of hard-coded values, and a fix so the backlog tab navigates back to the list rather than getting stuck on whatever sub-view it last showed. Last, a fix to a phase-rewind edge case: if Claude appended a phase while sitting in the review slot — all authored phases done — the usual index shift kept currentPhase pointing at review, so the new phase got skipped on restart. insertPhaseAt now detects the review slot and rewinds currentPhase to the inserted phase so it actually runs before review.

  • v0.229 - Backlog moves to Drizzle

    Three changes today, all on the backlog data layer that moved to Postgres a couple of days ago. The first was assist backlog export / import. Now that everything lives in one shared Postgres database rather than per-repo SQLite files, I wanted a way to snapshot the whole thing and move it between machines. Both commands lean on Postgres’ COPY for speed: export streams each table out via COPY ... TO STDOUT and frames it into a single self-contained file, and import reads it back via COPY ... FROM STDIN. The dump format is a JSON header — { format, version, tables } — followed by per-table sections prefixed with @table <name> <byteLength>. The byte-length framing keeps each table’s COPY payload verbatim regardless of its contents, so the round-trip is faithful. import validates the header against the expected schema and confirms before replacing existing data.

    The bigger change was migrating the whole data layer from hand-written pg queries to Drizzle. The old BacklogDb interface wrapped raw SQL strings with ? placeholders; every loader and saver now builds queries through Drizzle’s query builder against a typed backlogSchema. The win that motivated it was batching — loadAllItems used to loop over every item firing separate queries for comments, links, and plan, which is fine for a handful of items but scales badly. With Drizzle’s relational queries those collapse into far fewer round-trips. The migration touched a lot of files (BacklogDbBacklogOrm, getBacklogDbgetBacklogOrm, and most of the backlog command surface) but the shape stayed the same. A nice side effect: tests now run against PGlite — real Postgres compiled to WASM — instead of mocking out SQLite, so the SQL is validated against the production dialect without needing an external database. The Drizzle client is structurally identical across the node-postgres and PGlite drivers, so the test client is just cast to the production type.

    The last one was cosmetic. The backlog list shows which repo each item came from, and after the Postgres move that label was the full normalized origin (github.com/org/repo). I trimmed it down — originDisplayLabels resolves the shortest unambiguous name across the whole list, so a remote shows just its bare repo name (repo) unless two listed remotes share that name, in which case both fall back to the disambiguating org/repo form. Local repos always show their final path segment. The decision is made across the full set being displayed, so a name only gets shortened when it’s genuinely unique in that view.

  • v0.226.1 - Granular backlog edits and mid-run phases

    Three changes today, all small, all driven by friction I hit in the past week. First: granular backlog list edits. assist backlog update could only replace the entire acceptance criteria or task list — which meant fixing a typo in AC #2 of a 6-item list required passing all six in. Added --add-ac, --edit-ac <n>, --remove-ac <n> and matching --add-task / --edit-task / --remove-task / --add-check / --edit-check / --remove-check for phase tasks and manual checks. Indexing is 1-based to match how the items are displayed. The all-replacing update got renamed to update-field so the command surface stays explicit about whether you’re replacing or mutating.

    Second: a bug I hit twice while running the new backlog flow. assist backlog run was capturing the plan once via prepareRun and looping over that frozen snapshot, so if Claude appended a phase during the last phase (which is a normal thing to do — discovering work that needs splitting out), the new phase got skipped in favour of jumping straight to the review phase. Fixed by re-resolving the plan from the DB after each executePhase via reloadPlan, so newly-added trailing phases extend the loop instead of being dropped. Pulled runPhases and runReview out into their own files in the process — run.ts is now a thin orchestrator.

    Third: a cli-hook tweak. I’d been piping assist complexity through grep/sed to narrow its output, which triggers permission prompts because the compound command isn’t on any allow list. But assist complexity already has maintainability/cyclomatic/halstead sub-commands that produce focused output without any post-processing. The hook now detects piped/chained assist complexity invocations and denies them with a specific reason pointing at the sub-commands, instead of falling through to a generic prompt. The general shape is reusable — SUBCOMMAND_READS is a list, more entries can be added when another command grows the same pattern.

  • v0.224 - Backlog moves to Postgres

    Cut the backlog over from per-repo SQLite + backlog.jsonl files to a global Postgres database. The motivation was the web UI bug from a few days back (openDb caching a single _db globally) plus the wider friction of every project carrying its own .assist/backlog.db: switching repos in the web UI cross-read each other’s data, and there was no good way to look at “everything I’m working on” across projects. Postgres is overkill for the storage volume, but the connection is shared across every assist invocation in any repo, and getCurrentOrigin tags rows with the originating remote so per-repo queries still work.

    The bulk of the change is a refactor — every loader/saver that used to read JSONL or hit better-sqlite3 now goes through a BacklogDb interface backed by a pg pool. Some _test files lost ~30% of their lines because the tests no longer need to set up a fresh on-disk SQLite per run; they go through createTestDb and reuse the schema. migrateLocalBacklog runs once per project on first invocation: backs up the existing .assist/backlog.db and backlog.jsonl, parses the JSONL, remaps item IDs so they don’t collide with already-migrated rows from other repos, and imports. After verifying the migration the jsonl file is gone from the tree.

    One catch found right after rollout — backlog commands were hanging after printing their output. The pg pool’s idle connections and timers keep the Node event loop alive, so the process never exits cleanly. Fixed by switching the CLI to parseAsync().finally(closeBacklogDb) so the pool drains on exit. Stuff that’s harder to fix once it bites: anything that holds the event loop open.

  • v0.223 - Gate backlog runs on pending handover

    After yesterday’s handover work, the obvious failure mode was: leave a .assist/HANDOVER.md from the last session sitting on disk, kick off assist backlog next in a new session, and have it cheerfully start work without ever reading the handover. So backlog run and backlog next now check for .assist/HANDOVER.md up front via blockedByHandover and refuse to proceed if one exists, telling the user to either start a fresh session (which surfaces the file via the SessionStart hook) or assist handover archive to set it aside intentionally.

    Also threw out the prior /recall plumbing — findRecentSessionJsonl, isSdkCliOnly, encodeCwdForProjects, and most of load.ts. The SessionStart hook now surfaces the handover directly, so the session-walking machinery underneath /recall is dead code. handover load is now a thin wrapper around reading the file.

  • v0.221 - handover and recall commands

    Added /handover and /recall Claude Code skills and the supporting assist handover machinery for cross-session continuity. /handover writes a structured .assist/HANDOVER.md with the sections I always forget to include myself — Current Task, Just Done, Next Steps, Key Files, Open Decisions, Watch Out For, Don’t Do — and archives any prior handover to .assist/handovers/archive/<iso-ts>.md first so re-running it can’t silently clobber notes. The file is gitignored. /recall is the read side: it locates the most recent prior session JSONL under ~/.claude/projects/<encoded-cwd>/, skips the current session and any sdk-cli-only transcripts (because those aren’t real interactive sessions worth recalling), then emits a terse # Recall block summarising what was being worked on so a fresh session can pick up.

    A bunch of the implementation work is in figuring out which session JSONL to actually load: encoding the cwd to the projects-dir naming scheme (/-), walking files newest-first by mtime, and the sdk-cli filter that classifies a transcript by inspecting entrypoint on every type:"user" entry. Pulled iterateUserEntries and parseUserLine out of the existing summarise code so handover and the session summary path share the same JSONL parsing.

  • v0.220.2 - assist review surfaces reviewer failure details

    When a reviewer in assist review failed — codex exiting non-zero, claude bailing early — the user got back a bare “codex CLI exited with code 1” with no context, which is useless for debugging. Added formatReviewerFailure that builds a structured diagnostic: exit code, elapsed time, trimmed stderr, and a “fast fail” hint when the process died in under a second (because that almost always means not-installed, not-authenticated, or misconfigured, and the suggested next steps — --version then login — short-circuit the usual flailing). If stderr is empty it falls back to the tail of stdout so the user has something to read.

    Bunch of small refactor work alongside it to thread stdout/elapsed time through the failure path: attachStdoutTail keeps a bounded tail of each reviewer’s stdout, finaliseReviewerRun and handleChildClose carry the new fields, and printReviewerFailures runs the formatter for any non-zero exit. Tests cover both the fast-fail hint and the stdout-fallback branch.

  • v0.220.1 - assist review handles missing codex CLI

    assist review was crashing with an unhandled ENOENT when the codex CLI wasn’t on PATH — not a friendly experience for a tool that’s only useful if both reviewers can be spawned. Added ensureCodexAvailable that checks for codex, and on miss prompts the user to npm install -g @openai/codex. If they decline (or the install fails), the run continues with the Claude reviewer alone instead of falling over. The codex result slot is populated with a “skipped” stub so the synthesis stage doesn’t need a special case for “one reviewer was absent”.

    While I was in there I broke the reviewer plumbing into smaller pieces — planCodexReviewer, resolveClaude/resolveCodex, runAndSynthesise, waitForChildExit, handleSpawnError — so the run-streaming code stops being one giant function. runReviewers.ts shrank by about 40 lines and the spawn-error path is now in one place instead of three.

  • v0.220 - SHA mode and codex guidelines for assist review

    Two refinements on top of assist review from earlier in the week. First, assist review now accepts an optional SHA argument and reviews sha^..sha instead of the current PR. When a SHA is passed there’s no PR lookup and no GitHub posting — output is just the local review folder. Useful for sanity-checking a single landed commit or a WIP before opening a PR. The PR and SHA paths share a runReviewPipeline but split context gathering (gatherShaContext vs the PR-based one) and request building so neither path has to special-case the other.

    Second, I folded a much stronger set of reviewer guidelines into the prompt that goes to both Claude and Codex. The old prompt asked for findings with severity/impact/recommendation but didn’t say much about when something is worth flagging — so reviewers were raising pre-existing issues, vague tangles, and style nits dressed up as bugs. The new rubric is explicit: must be discrete and actionable, introduced by this change, not pre-existing, named to a concrete code path, and clearly not intentional. There’s also direct guidance on tone (matter-of-fact, no flattery, no padding) and an instruction to return zero findings when nothing clears the bar rather than padding to look thorough.

    Smaller doc tweak: claude/CLAUDE.md now points Claude at the Atlassian MCP server for Jira context instead of describing assist jira shell calls.

  • v0.217 - Backlog shortcuts, skott, and repo selector

    A grab-bag of small improvements to the daily-driver flow, mostly around the backlog. /next [id] is now a shortcut to run a specific backlog item by id instead of having the orchestrator pick — handy when I already know which item I want to work on next. assist backlog stop lands as the inverse of start: reverts an in-progress item back to todo so I can park work without it sitting in a half-state. /draft was also fetched a lot of codebase context up front before even asking what the user wanted, so it now defers those reads until after the description is captured.

    On the tooling side I replaced madge with skott for the circular-dependency check in assist verify. madge was dragging in ~1.4MB of transitive deps for what should be a fast static check, and skott does the same job with a much smaller surface and faster startup, so the verify wall time drops on every run. Also fixed refactor rename symbol in monorepos — it was loading the wrong tsconfig.json when a project had multiple, so the rename only covered part of the source tree. findTsConfig got pulled out to share the walking logic between extract and rename.

    In progress on a branch: moving the repo selector from inside the sessions view up into the top toolbar so it’s shared across sessions and backlog. The wip commit pulls the selection state into a RepoSelectionProvider context, drops RepoFilterRow, and rewires the backlog web view to read the active repo from the same source. Not landed yet.

  • v0.215 - assist review orchestrates Claude and Codex

    Spent the day building assist review, a command that runs Claude Code and Codex CLI as two independent reviewers over the current PR’s diff and synthesises their findings into a single set of line-level pending comments. The shape is: kick off both children concurrently, stream their events through parseClaudeEvent / parseCodexEvent parsers, collect findings into a structured shape (parseFindings), then hand the merged set to a synthesis pass that dedupes and severity-rates before postFindings posts them via gh and optionally submits the review. Findings that couldn’t be located in the diff are warned about rather than dropped silently.

    Once the bones were in place the iteration loop got fast. The reviewers were initially diffing against the local base branch and missing PR-level context, so they now feed off gh pr diff and prior PR comments via fetchExistingComments + formatPriorComments — so a second review run doesn’t re-raise points the original reviewer already wrote. The streaming UI was a wall of log lines, so it’s now a set of stacked ora spinners that show each reviewer’s progress in place. And --refine and --apply flags landed on top: --refine drops into an interactive synthesis loop where I can tweak the merged findings before posting, and --apply spawns a Claude session pointed at the resulting findings so the fixes can be made and pushed in the same flow. The severity rubric in the synthesis prompt also got reworked after seeing it call too many things “high”.

  • v0.211 - /verify-new slash command

    Added a /verify-new Claude Code slash command that walks through registering a new verify:* run entry. The interesting step is the hunt-for-quietness one: because assist verify runs every verify:* entry in parallel and only surfaces output for failures, a noisy success drowns out the actual signal. The skill explicitly tells Claude to read the wrapped tool’s --help, look for --silent, --quiet, --reporter=dot-style options, and apply them before adding the entry, then verify with a real run. That’s the part I always forget to do manually.

  • v0.210 - Forward comments command and subdir fixes

    A few unrelated improvements today, all driven by friction I was hitting in real reviews. The headline is a new /forward-comments Claude Code slash command: paste in a coarse review someone gave outside GitHub (Slack message, in-person notes), pass the reviewer’s handle, and it splits the prose into atomic concerns and posts each one as a separate line-level review comment on the current branch’s PR, prefixed with via @handle. Framing prose and transitions get dropped — only actionable points become comments.

    Two fixes alongside it. assist backlog was bailing when run from a subdirectory because loadConfig only checked cwd; it now walks up looking for .claude/assist.yml or assist.yml and resolves the project root from wherever it finds the config. And the prs command’s gh calls were occasionally hitting upstream instead of origin on forks, which surfaced wrong PR data — added a getPreferredRemoteRepo helper that prefers origin whenever it exists, falling back to whatever single remote is configured.

  • v0.209 - assist sql for MSSQL

    Added an assist sql command for running queries against MSSQL — the missing piece for being able to spelunk through a database without dropping into SSMS or another GUI. Connections are configured up-front (sql set-connection) and stored alongside the existing seq connections, with named selection across calls. sql query runs SELECT statements and prints the result as a table; sql mutate runs INSERT/UPDATE/DELETE/MERGE statements and refuses anything that doesn’t look like a mutation via an isMutation check, so a careless paste of a wide SELECT can’t accidentally tear through a table. Both share the connection resolution, auth, and pool plumbing under src/commands/sql/.

    Also slipped in a small fix: assist run add verify:* was creating a claude/commands/<name>.md slash command for every verify run, which doesn’t make sense — verify scripts get invoked by assist verify, not as individual user commands. The slash-command file is now skipped when the run name starts with verify:.

  • v0.207 - assist mermaid export

    Added assist mermaid export to render the fenced mermaid blocks inside a markdown file out to SVGs. With a file argument it pulls every ```mermaid block and writes them next to each other as <stem>-<index>.svg; with no argument it scans *.md in the current directory. --out <dir> overrides the destination and --index <n> renders just the nth block, which is handy when iterating on a single diagram. The actual rendering goes through Kroki — POST the source, get back SVG — and the endpoint is configurable via mermaid.krokiUrl for anyone running a self-hosted instance.

  • v0.206.2 - Resolve Roam port file by channel

    The Claude icon stopped showing up in Roam when show-claude-code-icon was enabled. Root cause was that Roam now writes channel-specific port files (roam-local-api-beta.port, etc.), and assist was still reading the legacy roam-local-api.port — which on my machine was a stale file from a previous stable install pointing at a dead port. Updated postRoamActivity to glob roam-local-api*.port under %APPDATA%/Roam and pick the most recently modified file. A curl POST against the freshly resolved beta port comes back with {ok:true} and the icon is back.

  • v0.206.1 - cli-hook approves redirects to OS temp

    The cli-hook approval check refused any command containing > or >>, which made things like az containerapp logs show ... > $TMPDIR/ca-logs3.json unapprovable — even though redirecting log output into a temp file is a perfectly safe pattern. Loosened the parser to strip a redirect when its target sits under the OS temp directory and otherwise return a specific redirect target '...' is outside the OS temp directory error instead of the generic “unable to parse”. The temp-path matcher handles both POSIX and Windows path styles (case-insensitive, forward or back slashes, drive-letter required) so the check is consistent across machines.

    While in there I changed splitCompound to return a result object ({ ok, parts | error }) instead of string[] | undefined, which lets the hook surface the actual reason a command was rejected rather than just saying it couldn’t parse. Pulled groupByOperator, consumeRedirect, and isRedirectTargetAllowed out into their own files so the parser stays under control as more cases get added.

  • v0.206 - verify init defaults to assist.yml

    assist verify init used to look at the existing setup and write verify scripts to wherever it already had hooks — package.json scripts if package.json had them, assist.yml run entries otherwise. That heuristic was wrong: assist.yml is the canonical place for these commands now, so the default should always be assist.yml. Reversed it so verify init writes to assist.yml unconditionally, with a --package-json opt-out for projects that genuinely want the npm script form.

  • v0.205.1 - Windows fix for assist devlog next

    assist devlog next was crashing on Windows with '%h' is not recognized as an internal or external command. The cause was three git log --pretty=format:'%ad|%h|%s' calls running via execSynccmd.exe doesn’t strip single quotes the way bash does, so %h got expanded as an empty env var and the rest of the format string was parsed as a command. Switched all three call sites (devlog list, devlog next, getLastVersionInfo) from execSync with a shell string to execFileSync with an argv array, which sidesteps the shell entirely and works the same way on every platform.

  • v0.205 - Spike — auto-generating skills from session data

    Spent the day digging into how skills could be extracted automatically from Claude Code session transcripts instead of being hand-written. Sessions live as JSONL files under ~/.claude/projects/<sanitized-path>/<uuid>.jsonl — user and assistant entries with tool calls inline — and even with the tool-call noise stripped, each one only weighs in at a couple of thousand tokens of text. A whole project’s worth of sessions fits comfortably in context, which changes what’s feasible.

    I cloned AutoSkill and walked through its three-stage pipeline (plan → expand → compile) and the prompt engineering behind it. The extraction philosophy is the interesting part: only lift skills from concrete, reusable user requirements, treat assistant replies as untrusted evidence, and lean toward missing a one-off over saving a generic task. Worth porting. The runtime itself is not — it requires an external LLM API key, and its filesystem-backed storage plus embedding dedupe is overkill at the scale of <100 skill files. The recommendation written up in .assist/spike-75.md is to port the prompts and use Claude Code itself as the extraction LLM via an assist skill extract CLI command plus a /skill-extract skill that runs the approval flow. Zero external dependencies, and the cost is already inside the session.

  • v0.205 - Unified web UI, assist modes, global deny

    The two separate web UIs — sessions dashboard and backlog view — are now one MUI app with shared navigation. Having them as siblings always felt wrong given they read from the same project state, and merging them opens the door to cross-linking (e.g. jumping from a backlog item to the session that worked on it). The sessions tab also got mode buttons for picking between assist behaviours directly from the UI, and the prompt input now stays visible whether a session is running or idle instead of appearing and vanishing.

    On the CLI, I added roam activity commands targeting a Codex flow, and global deny config support so deny rules can live in a user-level config and apply across every project instead of being repeated in each assist.yml.

  • v0.200 - Web UI polish, session summarise, prompt analyzer

    The web dashboard got most of the attention today. I added run command execution and a retry button for failed run sessions, so I can trigger and re-trigger commands from the browser instead of dropping back to the terminal. Phase rewind landed in the backlog web view too, mirroring the CLI command. I also made assist with no arguments default to opening the sessions dashboard, and added hover states to card buttons so it’s clearer what’s clickable.

    On the CLI side, I built a prompt frequency analyzer for spotting which prompts I type most often — useful for figuring out what to turn into skills or aliases — and a session summarise command for capturing what a session accomplished. Suffix matching for assist run now resolves partial names and shows candidates on ambiguity.

    A cluster of fixes: cli-hook check was failing to parse PowerShell $null redirects, the caveman skill prefix needed removing after a rename, seq query timestamp filtering was silently dropping filters and lacked relative-time support, and cross-platform browser-open was misbehaving when the dashboard tried to pop the session URL.

  • v0.194 - Repo selector on new session form

    The new-session form in the web dashboard picked up a repo selector, so I can kick off a session against any tracked project without typing the path or remembering which directory I last started Claude in. I also softened a help-discovery hint in the backlog output that was over-confident about suggesting commands when the agent couldn’t find what it needed.

  • v0.193 - Web sessions dashboard with history and resume

    I built a web dashboard for Claude Code sessions — a browser-based harness for seeing what’s running, what’s queued, and what finished. The first cut landed with a sessions list, and the same day I added history browsing and resume so I can pick up a prior session from where it left off instead of starting fresh every time.

    Small fix earlier: the caveman skill invocation needed to use its fully qualified name to resolve correctly when spawned from a plugin-namespaced context.

  • v0.191 - Caveman prompts and cd approval

    Two features and a cluster of small fixes. The cli-hook allow-list now approves cd into any directory that Read is already allowed to touch — previously cd was gated separately and kept prompting even though the target was clearly in-scope. I also added a “caveman” config that prefixes every spawned Claude prompt with a short directive to keep replies terse. It’s silly, it works, and it saves me from typing “no yapping” into every session.

    Fixes: ensureGitignore was duplicating entries on CRLF files because the line-split didn’t normalise newlines, reindexPhases now defers FK checks so remove-phase doesn’t trip over the constraint mid-reindex, backlog add --desc was storing literal \n strings instead of newlines, and the cli-hook check was rejecting shell builtins true and false as unknown commands.

  • v0.189 - Linked run configs and gitignore cleanup

    Two features today. I can now pull in run configs from external assist.yml files — linking makes shared commands reusable across projects without copy-pasting every time I spin up a new repo. I also simplified .gitignore entries to a single .assist* pattern instead of listing each generated file individually, which is easier to reason about and one less thing to update when assist adds a new signal or lock file.

    A quick follow-up fix: linked run commands using bash -c with chained commands were hanging on Windows because the bash path and run prefix weren’t being resolved correctly. Both are now resolved before the command is spawned.

  • v0.187 - Position option for add-phase

    A tiny day. backlog add-phase gained a --position option so I can insert a phase at a specific index instead of always appending. Useful when I realise mid-flow that a plan needs an extra step somewhere in the middle, rather than having to rewind and rebuild.

  • v0.186 - Backlog refinements and run command expansion

    Big day — eleven minor releases. The backlog picked up the most surface area: a refine command for iterating on item details through conversation, a rewind-phase for walking items back when a phase is wrong, a delete-comment for pruning noise, and an auto-commit config toggle for people who don’t want every backlog add generating a commit. I also made phase numbering 1-based end-to-end so the CLI and the display finally agree, after a brief run where phase 0 in commands showed as phase 1 on screen.

    The run command grew a remove subcommand and a --cwd option on run add, and run help now lists the commands I’ve actually configured rather than just the built-in flags. I added an activity command with a weekly chart to get a feel for where I’ve been spending time, and followed up the same day with a date range in the chart and a label fix after I noticed it said “per week” when it was really per day. Rounding out the day: a /add-command slash command for adding new run commands conversationally, backtick formatting in PR bodies so code symbols render properly, a fix for the cli-hook deny-rule check when commands don’t parse cleanly, and a couple of signal-file fixes (scoping per session and widening the gitignore pattern) after /next started terminating the wrong Claude session.

  • v0.175 - Auto-pick single items and fd redirect matching

    A small but satisfying day. /next now auto-picks when there’s only one backlog item left, skipping the prompt that used to ask me to confirm the obvious. If there are multiple items the interactive selection is unchanged — this only short-circuits the single-item case on the first iteration of a session.

    On the fix side, the cli-hook allow-list matcher was failing on macOS when commands included file-descriptor redirects like 2>/dev/null. The matcher now strips fd redirects before comparing against the allow list, so commands that are legitimately allowed don’t get blocked just because of a trailing redirect.

  • v0.174 - SQLite backlog storage and search

    The big move today was switching backlog storage from YAML files to SQLite, with JSONL kept alongside as a synced mirror for diffing and git history. The migration path also updates .gitignore automatically when the DB is created, so the binary never accidentally ends up committed. A handful of follow-up fixes fell out of this — surfacing parse errors instead of silently dropping items, detecting the SQLite DB in backlog gates, handling null comment phases in the JSONL, and stripping unknown keys in planTaskSchema so legacy rows don’t crash backlog ls.

    On the feature side, backlog add now takes CLI options instead of reading from a file — a breaking change but much nicer for scripting and for the /draft flow. I added a wontdo status for items I’ve decided not to pursue, full-text search across item fields, and browser routing to the backlog web UI so deep links and back/forward finally work. A few UX fixes landed too: prompting before auto-starting a single item, skipping the phase prompt for terminal items, and allowing literal backticks in quoted args for splitCompound.

  • v0.169 - Permission system overhaul and backlog item linking

    I overhauled the permission system today. The old monolithic cli-reads permission group got split into separate reads and writes categories, and I added deny rules via cli-hook deny commands so specific patterns can be blocked. Allow and deny rules now merge across tools (Claude Code and any other configured tool), and I fixed a couple of edge cases — normalizing the ./ prefix in allow matchers and supporting entries without the :* suffix.

    Backlog items can now be linked to each other, which is useful for tracking related work and dependencies. I also added --dir to backlog commands for operating on backlogs in other directories, and backlog comments now get inlined directly into phase prompts so Claude Code has full context when executing a phase. The draft and bug modes gained edit mode support, and I added a global config option for sync auto-confirm so it stops prompting on every sync.

    On the seq command, I added a --from option for filtering by time range and switched historical queries to use /api/data instead of the events endpoint. I also replaced the --json stdin approach with a --file flag for passing structured input, which is cleaner and avoids control character issues.

  • v0.158 - Backlog workflow polish and CLI aliases

    I spent today polishing the backlog workflow to make it smoother for continuous use. The headline feature is backlog next now loops automatically when items remain, so I can kick off a session and let it chew through multiple backlog items without manual re-invocation. I also added draft and bug launch modes with /next chaining, so after filing a bug or drafting an item the session chains straight into the next piece of work.

    Backlog items now support comments and summaries — comments can be added during execution and summaries are saved when an item is marked done, giving better traceability on what was decided and why. Items also auto-commit after backlog add, removing the manual step.

    I added a backlog show command for viewing item details, and a few convenience aliases: ls for backlog list and a as a top-level CLI alias for assist. A couple of fixes improved reliability — graceful exit on Ctrl-C during prompts instead of crashing, and fixing summary persistence so it saves on done rather than phase-done.

  • v0.150 - Backlog orchestration and test coverage push

    I built out the backlog orchestration system today — the big addition is phased execution, where backlog items now progress through tracked phases with automatic advancement. Each phase can include manual checks, and sessions auto-kill on phase completion so Claude Code doesn’t wander off-task. A review phase is auto-generated for each item, and items are automatically marked done when their plan completes. I also added a /draft command for creating new backlog items with LLM-assisted questioning.

    On the refactoring side, I added assist refactor extract for pulling functions and their private dependencies into new files. Getting the import rewriting right took a few iterations — fixing stale import cleanup, rewriting paths in the destination file, and skipping barrel exports for non-public functions.

    I pushed test coverage from roughly nothing to 17.31%, converting existing tests to BDD style (describe/it blocks) and adding new coverage for splitCompound, collectDependencies, resolveImports, and the backlog run completion logic. I also added test-cover and test-review slash commands so Claude Code can incrementally improve coverage during sessions.

    The backlog web view got plan progress display, and the CLI picked up several UX improvements: a show command, numbered acceptance criteria, type display in the next list, comments and summaries on items, a -w/--write flag for backlog run/next, and aliases like next for backlog next -w and remove for backlog delete. I also added a /bug command for filing bugs with structured reproduction steps and a coverage command for reporting test coverage metrics.

  • v0.134 - Window screenshot capture command

    Added an assist screenshot command that captures a screenshot of a running application window. You pass it a process name like assist screenshot notepad and it grabs the visible window bounds using the DWM extended frame bounds API (which excludes the invisible shadow padding Windows adds), then BitBlts the region from the screen DC into a PNG.

    The heavy lifting is in a PowerShell script that gets written to a temp file and executed with -NoProfile -ExecutionPolicy Bypass. It handles DPI awareness, restoring minimised windows, and all the GDI cleanup. The output directory defaults to ./screenshots but is configurable via screenshot.outputDir in the assist config.

    I also added a /screenshot slash command so Claude Code can capture windows during debugging sessions — useful for visual verification of UI changes without leaving the conversation.

  • v0.132 - Inspect scope unification and issue filters

    Continued refining dotnet inspect today. I replaced the separate --ref and --base flags with a single --scope option that accepts all, base:<ref>, or commit:<ref> — defaulting to working copy diff when omitted. This cleaned up the getChangedCsFiles logic considerably, turning what was a pair of loosely-related flags into a proper discriminated union with a parseScope function.

    On the filtering side, I added --suppress to hide specific issue type IDs from the command line (merged with any suppression rules already in config), and --only to show exclusively the specified IDs. The --only filter is handy when you’re hunting for a specific category like CommentedCode across a solution without wading through everything else. Both support variadic arguments so you can pass multiple IDs in one go.

  • v0.133 - CLI reads refactor, Raven slash command, and Roam integration

    I moved all the shell read commands (cat, head, ls, grep, etc.) out of claude/settings.json and into the assist.cli-reads file where they belong. This meant updating findCliRead to support single-word command matching — previously it only matched two-word prefixes like git log, so bare commands like cat or head were falling through. Much cleaner having all the read-command allow-listing in one place.

    Added a /raven slash command so Claude Code can interact with RavenDB connections and collections conversationally — listing collections, querying documents, and managing connections without leaving the editor.

    The seq query default count went from 50 to 1000, which is a much more useful default for log exploration. It now also warns when results hit the limit so you know to increase --count if needed.

    Finally, I added assist roam show-claude-code-icon which forwards Claude Code hook activity to the Roam local API. It reads the Roam API port from the app data directory and POSTs activity events, failing silently if Roam isn’t running.

  • v0.127 - Dotnet inspect overhaul and Seq queries

    Big restructure of the .NET inspection tooling today. I replaced the old netframework command group with a unified dotnet namespace, consolidating code inspection, build tree, and dependency commands under one roof. The dotnet inspect command now runs JetBrains InspectCode by default with --swea as opt-in, filters out unreliable .Global rules that were producing false positives, and supports a --base flag to scope inspections to files changed since a given git ref. I also added a --roslyn flag that runs Roslyn-based analysis as an alternative engine, with results filtered to only changed .cs files.

    The rate-limit status line segment got smarter — it now colour-codes based on consumption pace rather than just the raw percentage, so the visual signal better reflects whether you’re likely to hit the cap.

    The other major addition was assist seq for querying Seq log servers. It reuses the same connection-auth pattern I’d built for RavenDB (which I extracted into a shared createConnectionAuth helper), so you can store named connections and query logs with filters directly from the CLI. I added a /seq Claude command alongside it so Claude Code can search logs during debugging sessions.

    Finally, I wired up /inspect as a Claude slash command so code inspections can be triggered conversationally without leaving the editor.

  • v0.120.1 - Config resolution from subdirectories

    Fixed config loading so assist finds assist.yml when invoked from a subdirectory of the repo. Previously loadConfig only looked in the current working directory, which meant commands failed silently when you were deeper in the tree. Now it walks up to the repo root using a shared findRepoRoot helper that was already in the .NET commands — extracted it to src/shared/ so both paths can reuse it.

    Also refined the /review-comments Claude command to group PR comments by thread rather than processing each comment individually. Follow-up comments (like a reviewer endorsing a bot suggestion) are now presented as context alongside the primary actionable comment, and fixed/wontfix is only called once per thread.

  • v0.118 - Rate limits in the status line

    Added rate limit visibility to the Claude Code status line. The status line now shows current API rate limit usage, and a follow-up improvement added time-to-reset countdowns so you can see at a glance when limits will refresh. Handy for knowing whether to keep working or take a break when approaching the cap.

  • v0.116 - Backlog web status dropdown

    Added a status dropdown to the backlog web UI so items can be moved between statuses directly from the detail view. The StatusPicker component shows available statuses and sends updates back to the server, which parses and writes the new status into the item body. This means I can now triage and update backlog items without leaving the browser.

  • v0.115 - RavenDB query commands and Jira view

    Added a full suite of RavenDB commands. ravendb auth handles authentication via 1Password secret references, ravendb set-connection saves named connection configs (URL, database, secret ref), and ravendb query runs RQL queries against a connection with automatic pagination. There’s also ravendb collections to list collections and ravendb search for full-text search across items. Connections are resolved interactively if not specified, and credentials are fetched from 1Password at runtime.

    Extended the Jira integration with a jira view command that prints the issue title and description, complementing the existing jira ac command. Refactored the shared fetch logic into fetchIssue.ts so both commands reuse the same API call. Also updated the Claude instructions so it knows when to use jira view vs jira ac.

    Narrowed the CLI hook discovery in permit-cli-reads to only scan files listed in a new assist.cli-reads manifest rather than discovering all readable commands dynamically. This gives more control over which commands get added to the allow list.

  • v0.112 - Madge circular dependency checks and Jira Claude command

    Added madge as a verify check for circular dependency detection. Selecting it during verify init installs the package and wires up a verify:madge script. While integrating it I found and fixed a handful of actual circular dependencies — extracted shared types in the backlog web UI components and pulled common logic into a shared.ts module in the restructure planner.

    Also added a /jira Claude command so Claude Code can fetch Jira issue context directly, and improved verify init to detect scripts already defined in assist.yml so it doesn’t offer to set up checks that are already configured. The init detection was refactored to pull setup handler registration into its own module and centralise the “install package and add script” pattern via setupVerifyRunEntry.

  • v0.109 - Refactor rename commands and madge integration

    Added two new refactor rename subcommands powered by ts-morph. refactor rename file moves a TypeScript file and updates all imports across the project, while refactor rename symbol renames a variable, function, class, or type and updates every reference. Both default to dry-run mode showing what would change, with an --apply flag to execute.

    Also added madge as an option in verify init for circular dependency detection — selecting it installs the package and wires up a verify:madge script automatically.

    Fixed a test that was failing on non-Windows platforms by skipping the MSYS path conversion test when not running on Windows.

  • v0.108 - RSS news reader with web UI

    Built an RSS news reader into the CLI. news add lets you add feed URLs to your global config, and news web spins up a local web server that fetches all configured feeds, parses both RSS and Atom formats, and renders items in a React UI grouped by date. Each feed gets a consistent color-coded badge, items show favicons and relative timestamps, and excerpts are extracted from descriptions with HTML stripped. Also extracted shared web server utilities into src/shared/web.ts to deduplicate the pattern already used by the backlog web view.

    Separately, fixed the prs fixed command to push the commit before resolving the review thread — previously it would resolve the thread with a commit link that didn’t exist on the remote yet. Also suppressed noisy JSON output from gh api calls when resolving PR comment threads.

  • v0.107 - Jira authentication and acceptance criteria

    Added Jira integration with two new commands. jira auth handles authentication via API token, prompting for site, email, and token then persisting the site and email to ~/.assist/jira.json for future use. jira ac fetches and prints acceptance criteria for a given issue key, converting Atlassian Document Format to readable markdown text with support for headings, ordered and bullet lists, and inline code.

    Also fixed gh api calls in the PR comment resolution flow to suppress JSON output that was being dumped to the terminal by piping stdout to pipe instead of inherit.

  • v0.106 - Devlog repos command and skip list relocation

    Added a devlog repos command that scans all tracked repositories and displays a summary table of their devlog status — showing the last version, date, and how many unversioned days remain. Handy for quickly seeing which repos have pending devlog entries.

    Moved the devlog skip days configuration out of each individual repo’s assist.yml and into the blog repo’s config instead. Skip days are now keyed by repo name in a single central location, which keeps per-repo configs cleaner and makes the skip list easier to manage. Updated devlog skip, devlog list, and devlog next to read from the new location.

  • v0.102 - .NET Framework dependency tree and run parameters

    Added a netframework deps command that builds and prints a dependency tree for .NET Framework projects. It finds containing solutions, resolves .csproj references, and displays the tree structure. Useful for understanding project dependencies in legacy codebases.

    Enhanced the run command with positional parameter support — run add can now define named parameters that get substituted at runtime, making custom commands more flexible. Also added elapsed time printing after each run completes, so you can see how long commands take.

    Fixed npm install detection to work through fnm and nvm version manager wrappers.

  • v0.98 - Compound command support in CLI hook

    Extended the CLI hook to handle compound commands — piped commands, && chains, and semicolons are now split and each part checked individually against the approved reads list. This means Claude Code can auto-approve things like git status && git diff without needing each compound form explicitly listed. Consolidated the cli-hook subcommands into a cleaner structure.

    Also switched the cli-reads lookup to pure JavaScript to avoid shell overhead on each hook invocation, and made assist update run npm install after pulling to ensure dependencies stay in sync.

  • v0.96 - CLI read permissions and auto-discovery

    Built a CLI permissions system that auto-discovers read-only commands from installed CLIs and generates a flat file (assist.cli-reads) of safe-to-approve patterns. The cli-discover command parses help output from CLIs to classify subcommands as read-only or mutating, and cli-hook integrates with Claude Code’s hook system to auto-approve matching commands. This replaces the manually-maintained allow list in settings.json for read operations.

    Also auto-approves read-only gh api calls by inspecting the HTTP method and endpoint, added a /sync slash command for syncing settings from the terminal, and fixed commit argument ordering to put the message first.

  • v0.93 - Commit workflow improvements and shell escaping

    Improved the commit command to accept a message directly without requiring file arguments, and updated new to automatically add VS Code configuration when scaffolding a Vite project. Fixed shell argument escaping across the tool and added a --verbose flag for debugging.

    Several fixes around packaging and release — made voice runtime dependencies optional so they don’t block installation, synced package-lock.json with the new ast alias, and ensured the lock file gets committed during releases. Also fixed isGitRepo to check the exact git root rather than matching parent directories, and used a shorter notification sound for WSL.

  • v0.91 - GPU speech-to-text and radon complexity checks

    Switched the voice daemon’s speech-to-text to use GPU inference for faster transcription, and fixed silence detection to properly reset after smart turn checks. Also added word-level diffing for more accurate voice typing output.

    Added radon-based complexity checks for Python projects — both cyclomatic complexity and maintainability index at the file level. This complements the existing TypeScript complexity checks and runs as part of verify.

    Introduced ast as a short alias for the assist command. Added context percentage colouring to the status line, made commit accept explicit file lists and status output, paginated the GitHub API in list-comments to handle large PRs, and hid completed items from backlog list by default.

  • v0.85 - Voice interaction daemon

    Built a voice interaction daemon that listens for a wake word and transcribes speech to type into Claude Code. The daemon runs as a background process with configurable wake and submit words — say the wake word to start listening, then the submit word to send. Iterated on it throughout the day, fixing word-level diff for more accurate typing, deduplicating daemon code, and routing debug output to a separate console window.

    Added prs comment for posting new review comments on PRs, and expanded verify with an all mode and diff-based filters so you can run all checks or only checks relevant to changed files. Also added run environment support and a no-venv verification check for Python projects.

  • v0.78 - Backlog web polish and verify list

    Spent the day polishing the backlog web UI. Migrated the forms to Base UI components and switched the styling to Tailwind for a cleaner look. Added a completed items toggle, a delete command, and story/bug type categorisation. The web UI now opens the browser automatically and is the default when running assist backlog with no arguments.

    On the CLI side, added verify list and run list commands so you can see what verification steps and run configs are available without actually running them. Also added hardcoded-colors to the ignore config for projects that need to opt out of colour literal checks.

  • v0.67 - React web UI for backlog management

    Built a web frontend for the backlog command using React. Running assist backlog web spins up a local server that serves a single-page app for managing backlog items — viewing, creating, and editing items with descriptions and acceptance criteria. The UI is bundled with tsup and served inline via an HTML template, keeping it self-contained without needing a separate dev server.

    Also fixed config saving to preserve unknown keys, so custom or future config values aren’t silently dropped when writing back to assist.yml.

  • v0.66 - Verify from run configs and command generation

    Updated verify to resolve its entries from both run configs and package scripts, so custom verification steps defined via assist run add are picked up automatically alongside the standard checks. Also made run add generate a command file when adding a new run config, giving each custom command a proper home rather than just an inline script.

  • v0.64 - Journal and standup commands

    Added /journal and /standup Claude commands. The journal command appends an entry summarising recent work, decisions, and notable observations. The standup command reads recent journal entries and generates a standup update. Both are Claude commands rather than CLI commands since they rely on Claude’s context and summarisation abilities.

    Also fixed the commit config to include proper defaults so it works correctly even when not explicitly configured.

  • v0.63 - Roam Research OAuth integration

    Added a roam auth command that handles the full OAuth authorization flow for Roam Research. It prompts for client credentials, opens the browser for authorization, spins up a local callback server to receive the auth code, and exchanges it for an access token. The token gets stored in the config for use by future Roam commands.

    Also added a few more git commands (git status, git grep, git log) to the permissions allow list so Claude Code can use them without prompting.

  • v0.58 - Review comment skipping and self-update

    Added a skip option to the /review-comments Claude command so individual comments can be passed over without needing to reply or resolve them. This is useful for comments that need more thought or aren’t actionable yet.

    Also added an assist update command for self-updating the tool via npm, and fixed the permissions allow list for complexity commands.

  • v0.56 - Backlog management and project scaffolding

    Added a backlog command for managing a simple task backlog — items can be listed (with a verbose flag for descriptions), added with an editor for longer descriptions, and picked off with /next-backlog-item which selects and starts implementing the next item. This gives a lightweight way to track what needs doing without leaving the terminal.

    Built an assist new command that scaffolds a new project — sets up a Vite React TypeScript template, initialises git, and gets everything ready to go. Also added tsup support to vscode init for projects using that bundler, and wired maintainability checks into verify init so new projects get code quality gates from the start.

  • v0.48 - Continued restructuring and complexity defaults

    Continued the maintainability-driven restructuring from yesterday, breaking apart more modules across the codebase — computeRewrites, planFileMoves, parseVtt, displayPaginated, and others were split into focused sub-modules. This was another round of using the restructure command on the codebase itself.

    Added a default action to the complexity command so running it without arguments gives useful output immediately. Also added SHA validation to prs fixed to match what wontfix already had, and allowed gh pr view and gh pr diff in the permissions allow list. Improved the maintainability error message to be clearer about what needs attention.

  • v0.46 - Restructure command and codebase reorganisation

    Added a refactor restructure command that analyses import graphs, clusters tightly-coupled files, and generates a plan to reorganise them into cohesive directories. It builds an import graph, clusters files by coupling, computes file moves, and can execute the plan — all without needing AI assistance. Also exposed it as a /restructure Claude command for interactive use.

    Then I ate my own dogfood and used it to restructure the assist codebase itself. Large modules like complexity/shared.ts, prs/listComments.ts, transcript/parseVtt.ts, and index.ts were broken apart into focused sub-modules. The main index.ts went from a monolithic command registration file to thin register*.ts files. This brought the codebase maintainability scores above 60 across the board.

    Also added a readme CLI command (previously only a Claude command) and capped wontfix reasons to 15 words.

  • v0.42 - Maintainability index as a verification step

    Expanded the complexity command into a proper verification step. The maintainability index can now run as part of verify, failing the build when files drop below a configurable threshold. When a file fails, it includes an explanation of what the maintainability index measures and what’s dragging the score down, which makes the output actionable rather than just a number.

    Added complexity.ignore to the config schema so specific files or patterns can be excluded from checks. Also added support for custom lint verify scripts, letting projects define their own lint commands alongside the built-in ones.

  • v0.37 - Config command and settings sync

    Added a config command backed by Zod schema validation. It lists current configuration values with their defaults applied from the schema, so you can see effective values even when they’re not explicitly set. This builds on the existing assist.yml config file and loadConfig() infrastructure.

    Introduced a sync subcommand that copies user-level Claude instructions (CLAUDE.md) to the home directory, keeping project-specific settings in sync across workspaces. Also made notify configurable via the new config system with a default-enabled flag.

    Fixed packaging issues where Claude commands weren’t being included in the published package, and added type checking to the CI release pipeline to catch build errors earlier.

  • v0.33 - Meeting transcript processing commands

    Added a suite of transcript commands for processing meeting recordings. The transcript format command parses VTT subtitle files into clean markdown transcripts, handling speaker identification and timestamp formatting. The transcript summarise command uses a staged workflow — it first generates a summary via a Claude command, then saves it alongside the original transcript with a link back to the source file. Iterated on the summary output a few times to get the file paths and links right, including URL encoding and using relative paths.

    Also improved the PR review workflow — prs list-comments now includes resolved comments (marked as such) for better context, and wontfix validates commit SHAs before responding. Updated the /review-comments Claude command docs to enforce backticks for code references and clarify the markdown link syntax.

  • v0.30 - Fixed and wontfix PR comment responses

    Continued building out the PR review workflow with prs fixed and prs wontfix commands. These replace the earlier reply and resolve commands with more opinionated actions — fixed replies with a message about the fix and resolves the thread, while wontfix explains why the comment won’t be addressed and resolves it. This better matches how I actually respond to review feedback.

    Improved the list-comments flow to handle the case where all review threads are already resolved — it now drops the comment cache and prints a clear message instead of showing an empty list.

  • v0.27 - PR review workflow and readme checks

    Built out the PR review workflow with prs reply and prs resolve commands, so I can respond to review comments and mark threads as resolved directly from the CLI. Updated the /review-comments Claude command to take advantage of these — it now filters out already-resolved threads so Claude only focuses on what still needs attention.

    Added a /readme Claude command that checks README.md for missing command documentation. This ties into the project convention of keeping the README in sync whenever CLI or Claude commands change.

    Small fix to use 7-character SHAs in commit output for consistency, and enforced LF line endings across the repo via .gitattributes.

  • v0.22 - Complexity analysis and PR review comments

    Added a complexity command that calculates maintainability metrics for source files — cyclomatic complexity, Halstead measures, and SLOC — giving a quick code health overview without needing external tools.

    Built out PR review comment support in two parts. First, a prs comments subcommand that fetches and displays review comments from GitHub PRs in a human-readable format, with proper error handling for missing repos. Second, a /review-comments Claude command that lets Claude Code process PR review comments interactively. Iterated on the comments output to handle Windows path compatibility and 404s gracefully.

    Tightened up the codebase with a lint rule to catch .js import extensions in TypeScript files and renamed source files to follow camelCase conventions enforced by Biome. Fixed knip to properly fail verification on config hints instead of silently passing. Removed the unused enable-ralph command and updated the README.

  • v0.16 - Desktop notifications and package.json versioning

    Added a notify command that integrates with Claude Code’s notification hooks to show desktop notifications when Claude needs attention. It handles different notification types like permission prompts and idle prompts, displaying the project name and relevant message. The command works across platforms - using node-notifier on native systems and calling SnoreToast directly from WSL to show Windows notifications. Fixed an issue on macOS where notifications would disappear immediately by setting a long timeout and enabling wait mode.

    Improved the devlog next command to read the version directly from package.json at the last commit when commit.conventional is enabled. Previously it relied on parsing devlog entries to determine the current version, but for repos using semantic-release the version is already tracked in package.json. This makes the version suggestions more accurate for projects that don’t have a devlog entry for every release.

  • v0.15 - Commit workflow, status line, and deploy features

    A productive day with several new features for the assist CLI.

    Added a status-line command that formats Claude Code’s status JSON into a readable status line showing the model name, token counts (input/output), and context window usage percentage. This integrates with Claude Code’s custom status line feature.

    Enhanced the commit command with conventional commit validation. When commit.conventional is enabled in assist.yml, messages must follow the standard format like feat: add feature or fix(scope): fix bug. Also added configurable pull and push options so commits can automatically sync with the remote.

    The deploy command gained a redirect subcommand that injects a trailing slash redirect script into index.html, useful for static site deployments. The deploy init command now prompts to install netlify-cli if it’s missing.

    Updated VS Code settings generation to configure Biome’s organize imports on save, replacing the per-language formatter overrides with a single global config.

    Fixed a WSL compatibility issue where enquirer’s confirm prompts displayed incorrectly. Replaced the Unicode symbols with ASCII [x] and [ ] markers. Also extracted the ESLint removal logic into a shared utility so both lint init and new commands can use it.

  • v0.12 - Run command for configured scripts

    Added a new run command that executes configured commands stored in assist.yml. You can define named commands with optional default arguments, then invoke them with assist run <name>. Any extra arguments passed get appended to the configured ones. There’s also assist run add to quickly register new configurations without editing the YAML manually.

    The config loader now checks .claude/assist.yml first before falling back to assist.yml in the project root, allowing the configuration to live alongside other Claude-related settings.

    Fixed an issue where run add would mangle arguments containing colons or other special characters due to Commander’s argument parsing. The fix bypasses Commander and parses process.argv directly to preserve the original argument values.

  • v0.11 - PR listing command and automated npm releases

    Added a new prs command that lists pull requests for the current repository using the GitHub CLI. It supports filtering by state (--open or --closed) and paginates results with interactive navigation when there are more than 10 PRs. Each entry shows the PR number, title, author, status with date, and changed file count.

    Also set up semantic-release for automated npm publishing. When commits land on main, the release workflow analyzes commit messages and automatically bumps the version, updates the changelog, publishes to npm, and creates a GitHub release. The package is now published as @staff0rd/assist with public access. Added conventional commit guidance to CLAUDE.md to ensure proper version bumping.

  • v0.10.2 - Lint init now propagates overrides

    Fixed a bug in the lint init command where biome overrides weren’t being copied to the target project’s biome.json. Previously only the linter rules were propagated, but now any overrides defined in the template (like allowing constants.ts files to use kebab-case naming) get copied over too.

    Also tweaked the verify:test script to use vitest’s dot reporter, which only shows output for failing tests rather than listing every test file. Makes the verify output much cleaner when everything passes.

    Did some internal refactoring as well, extracting each verify setup function into its own file to keep things organized.

  • v0.10.1 - Devlog command refinements

    Made several small improvements to the devlog workflow. Added support for overriding the project name in assist.yml via devlog.name, useful when the git repo name doesn’t match what I want in devlog entries.

    Also refined the Claude Code command prompt: improved tag selection guidance to prefer existing tags over creating new ones, added a diff check before skipping entries to ensure nothing meaningful gets missed, and reordered the tag fetch to happen later in the workflow.

  • v0.10 - New project scaffolding commands

    Added a new command for scaffolding fresh projects. It sets up the basic structure and automatically runs initialization commands to get a project ready to go. Also added vite base configuration to new projects so they’re ready for building out of the box.

    Created two new init commands: lint init sets up biome with my preferred linting configuration, and deploy init adds a GitHub Actions workflow for automated builds and deployments. The new project command chains these together so a freshly created project has linting and CI/CD configured from the start.

  • v0.9 - Major refactoring and filename conventions

    Spent the day on a significant refactoring pass. Extracted large modules into smaller, focused files - the verify init command alone went from a monolithic 377 lines down to several purpose-built modules like detectExistingSetup, setupKnip, and installPackage. Did the same for devlog, refactor, and vscode commands.

    Added filename convention enforcement via biome, requiring files to use either camelCase or be named index.ts. This prompted renaming several files like devlog.tsindex.ts and fileNameCheck.tsrunFileNameCheck.ts.

    Also improved jscpd integration with quieter output and detection of outdated configurations, plus deduplicated package.json reading logic into a shared utility.

  • v0.8 - Refactor check now runs verify scripts

    Enhanced the refactor check command to automatically run any verify:* scripts from package.json before reporting results. This catches issues that static analysis might miss by running the project’s actual verification suite. The verify scripts run quietly in the background and only show output on failure.

    Also added devlog as a Claude Code skill for easier access, and updated documentation.

  • v0.7 - New init and verify commands

    Added a suite of new initialization and verification commands today. The main addition is vscode init, which sets up VS Code configuration for a project. I also created a general init command framework to organize these setup utilities.

    On the verification side, I built verify hardcoded-colors to detect hardcoded color values in code (with example fixes), and significantly improved verify init. The init verification now properly handles package installation, supports vitest for testing, and works with vite-only builds that don’t use a separate build script.

  • v0.6 - Devlog command enhancements

    Continued refining the devlog workflow today. Added a --reverse option to devlog diff so I can view changes in chronological order instead of reverse-chronological, which is more natural when reviewing a sequence of work.

    Extended the config system with a skip day feature and git permissions, making it easier to manage which dates get skipped and controlling git operations. Also updated the README with documentation for all the new devlog commands.

  • v0.5 - New devlog command for tracking releases

    Built out a comprehensive devlog command system today. The main feature is devlog next, which shows commits since the last versioned devlog entry and suggests the next patch and minor version numbers. This makes it easy to see what needs documenting and pick the right version bump.

    Added devlog skip for marking dates as intentionally skipped (trivial changes that don’t warrant an entry), and devlog diff for reviewing changes with filtering options like --since and --verbose. The diff command also shows devlog status per date so I can see at a glance what’s been documented.

    Made several supporting improvements: the sync command now shows line-by-line diffs when copying settings, verify got a --timer option for performance tracking, and I added verify/commit to the global permissions list so they run without confirmation prompts.

    Also did some internal cleanup, extracting the devlog functionality into separate modules and adding VSCode settings for biome.

  • v0.4.1 - Fixed verify command path resolution

    Fixed an issue with the verify command where it would fail to find the package.json file when running from a subdirectory of the project. The command now walks up the directory tree to locate the nearest package.json, matching the behavior users expect when working in nested folders.

  • v0.4 - Configurable max-lines for refactor checks

    Added a --max-lines option to the refactor check command, making the file length threshold configurable rather than hardcoded. This lets me adjust the limit based on project needs or temporarily raise it when I’m not ready to split a file yet.

    Also updated the README to better clarify what the tool does.

  • v0.3 - Git filters for refactor check

    Added git filter options to the refactor check command. The new --modified, --staged, and --unstaged flags let me focus the check on files I’m currently working on rather than scanning the entire codebase. This makes the command much more useful during active development when I only care about files I’ve touched.

    Also removed the unused refactor check next subcommand that showed the single largest violation - the main check command with sorting already handles this use case well enough.

  • v0.2 - Refactor command improvements

    Spent the day polishing the refactor command. Fixed stderr handling in the verify command so errors don’t clutter the output, and made the refactor ignore system use the maxLines setting properly.

    Added a new static import linting check to catch cases where dynamic imports could be converted to static ones. Also introduced a refactor next subcommand that shows just the next violation to fix, making it easier to work through issues one at a time.

    Did some cleanup on the violation output, moving extraction tips inline and creating a shared logViolations helper to reduce duplication. Test files are now excluded from refactor checks since they often have legitimate reasons for patterns we’d normally flag.

  • v0.1 - Assist CLI genesis

    Started building a CLI tool called assist to streamline my development workflow. The core features include a commit helper that stages and commits with AI-generated messages, a sync command for updating global commands and settings, and a verify command that runs all project checks in parallel.

    Added code quality tooling with lint and refactor commands. The refactor command runs various checks (unused exports, file naming conventions, etc.) and supports pattern filtering for targeted analysis. There’s also an ignore mechanism for suppressing false positives.

    Switched from plain tsc to tsup for bundling, which should make the CLI easier to distribute. Wrote up documentation including installation instructions and a CLAUDE.md for AI context.