@staff0rd/assist
v0.623.1
Published
A CLI tool for enforcing determinism in LLM development workflow automation.
Readme
assist
A CLI tool for enforcing determinism in LLM development workflow automation.
See devlog for latest features.
Installation
You can install assist globally using npm:
npm install -g @staff0rd/assist
assist syncUpdating
assist updateLocal Development
# Clone the repository
git clone [email protected]:staff0rd/assist.git
cd assist
# Install dependencies
npm install
# Build the project
npm run build
# Install globally
npm install -g .After installation, the assist command will be available globally. You can also use the shorter ast alias.
Claude Commands
/add-command- Add a new run command to assist.yml/add-rule- Capture a newCLAUDE.mdrule from a review comment/branch <description> [--jira KEY]- Create a branch off the fresh remote default, deriving a kebab-case slug from the description/bug- File a bug with reproduction steps, expected and actual behavior/close- Judge from the conversation alone whether the work is finished and nothing awaits the user, then runassist sessions close; otherwise report what is outstanding and leave the session running/comment- Add pending review comments to the current PR/commit- Commit only relevant files from the session/devlog- Generate devlog entry for the next unversioned day/draft- Draft a new backlog item with LLM-assisted questioning/fix-conflict [--rebase]- Resolve the current PR branch's conflicts against the remote default, verify, then push; merges by default,--rebasereplays the branch and pushes with--force-with-lease/fix-rules [dir]- Put existing rules into the## Rulesformatassist rulesreads/forward-comments- Split a coarse PR comment into per-line review comments, attributed to the original reviewer/handover- Write a session handover note for the next conversation/pr- Raise a PR with a concise description, then watch CI in the background/prs-slack <number> [--no-confirm]- Post a PR's title and URL to the Slack channel configured inprs.slack;--no-confirmskips the confirmation and posts straight away (used by chained announces)/refactor- Run refactoring checks for code quality/prompts- Analyze denied tool calls and suggest settings changes to auto-allow recurring prompts/recall- Recall the most recent handover note for this repo/refine- Refine an existing backlog item through conversation/rename [title]- Retitle this session's dashboard card viaassist sessions rename; uses the argument verbatim, or infers a short title from the conversation when given none/restructure- Analyze and restructure tightly-coupled files/review-pr-comments- Process PR review comments one by one/jira [action] [KEY] [args]- Jira actions:view,associate,update,started,done,help.[KEY]is optional — it resolves from the session's backlog item/github [action] [ref] [args]- GitHub issue actions:view,edit,associate,update,started,done,help.[ref]is optional — it resolves from the session's backlog item. A bare/github <ref>runsedit, which opens the issue in the web preview pane; outside a web session the command prints the issue to chat instead/journal- Append a journal entry summarising recent work/next [id]- Signal completion and chain into the next backlog item/slack-post [channel] [--thread <ts-or-permalink>] <what to say>- Compose a markdown message, preview it in the web pane viaassist slack post, then post the approved body to that Slack channel with the Slack MCP connector and report the permalink. The channel falls back toslack.channel;--threadposts the message as a reply in that thread/standup- Summarise recent journal entries as a standup update/subtask <text>- Add a sub-task to the session's current backlog item/strip-code-comments- Strip redundant comments from tracked source files/sync- Sync commands and settings to ~/.claude/design <prompt>- Apply the vendored design system prompt to a design task/test-cover- Incrementally increase test coverage by identifying and testing uncovered files/test-review- Review existing tests for quality, coverage gaps, and conventions/inspect- Run .NET code inspections on changed files/screenshot- Capture a screenshot of a running application window/raven- Query and manage RavenDB connections and collections/miro [board url | extract name]- Dump a Miro frame's rawboard_list_itemspages and extract its boxes as an ordered YAML list viaassist miro extract/seq- Query Seq logs from a URL or filter expression/sql- Query a MSSQL database via assist sql/verify- Run all verification commands in parallel/verify-new- Add a new verify:* run command to assist.yml/transcripts- Format and summarise meeting transcripts end to end/voice-setup- Download required voice models (VAD, STT)/voice-start- Start the voice interaction daemon/voice-stop- Stop the voice interaction daemon/voice-status- Check voice daemon status/voice-logs- Show recent voice daemon logs
CLI Commands
Every command supports --help for full detail on its flags and behaviour.
Database
assist backup [-o, --out <dir>]- Dump the entire backlog database to<dir>/backup-<timestamp>.dump(default~/.assist/backups, orbackup.dir)assist backup schedule --every <duration>- Install or update a crontab block runningassist backupon a cadence (e.g.5m,6h)assist backup schedule status- Print the active backup cadence and cron expressionassist backup schedule remove- Remove the backup schedule block from the crontabassist db migrate- Apply pending backlog database migrations in orderassist db status- Report whether the database is in sync with the build's bundled migrations
Git and GitHub
assist sync [--prune] [--force]- Copy commands, settings,CLAUDE.mdand design assets to~/.claude(plus~/.codexand~/.piwhen those CLIs are on PATH). With--prune, also lists commands in the target dirs that sync did not write — anything whose name is not in the repo'sclaude/commands/*.mdset — and adding--forceremoves them.~/.codex/skillsand~/.pi/agent/promptsare only inspected when those CLIs are detected; an orphaned codex skill directory is removed only whenSKILL.mdis its sole content, and any other is left in place with a reason. Subdirectories and non-.mdfiles are listed separately and never removed.--forcewithout--pruneis an errorassist activity [--since <date>]- Chart GitHub commit activity per day (defaults to last 30 days)assist commit status- Show git status and diffassist commit <message> [files...] [--ref <ref>]- Stage files and create a git commit with validation. The message must be a single line under 50 characters;--refis the only way to give the commit a body. Each--refvalue is free text containing a URL (e.g.--ref "rationale for removing it https://…") and becomes oneRef:trailer line, verbatim — commas are not separators, so repeat the flag to pass several. A value carrying nohttp/httpsURL is rejected before anything is stagedassist branch <slug> [--jira <key>] [--from <ref>]- Create and switch to a new branch off the fresh remote default (or--from <ref>); name is[<prefix>/][<JIRA>-]<slug>, long slugs shortened by LLMassist watch wait [--interval <d>] [--timeout <d>|none] [--pull] [--build [entry]]- Block until the current branch's upstream gains commits, then exit. Fetches once at startup, so commits already on the remote are picked up without waiting out an interval.--timeoutdefaults tonone, so a quiet branch waits indefinitely rather than exiting 2. With--pull, fast-forwards (recovering a dirty tree or a merely-behind branch) and prints the build report (seeassist watch report) baselined on the pre-pull SHA. With--build, runs theauto-buildrun entry — or[entry]if named — after a successful pull, then, when the report's Sync section names something, shells out toassist sync --yesso the freshly built binary installs the changed commands, skills and settings into~/.claude. Exit codes:0moved (and cleanly pulled, built and synced),2timed out on an explicit finite--timeout,3the branch has genuinely diverged,4the build or the post-build sync failed,1cannot wait,130interruptedassist watch report [--from <sha>]- Print the built version frompackage.json, the last 10 commits as a markdown SHA/When/Subject table newest-first, a Restarts section naming the restarts the new commits make necessary, and a Sync section naming what changed under the pathsassist syncinstalls (claude/commands/,claude/skills/,claude/settings.json,claude/CLAUDE.md,claude/design-system-prompt.md,codex/,pi/), or- not needed. With--from, commits reachable fromHEADbut not<sha>are marked← newand both the restart and sync advice come from the files they changed. Exit codes:0printed,1git could not resolve the rangeassist prs- List pull requests for the current repositoryassist prs raise --title <t> --what <w> --why <y> [--how <h>] [--resolves <key>] [--force] [--draft|--no-draft]- Raise a PR, assembling the body from What/Why/How. Whether the PR is created as a draft comes fromprs.draftunless--draftor--no-draftis passed, which always wins. In a web session the draft is previewed for approve/reject (with inline comments and pasted screenshots, hosted via thegh-imagegh extension) before the PR is created. The preview pane carries two independent chain checkboxes, both default on: Review chains areview --no-prompt --submit <n> --address-commentssession once the PR is raised (review → post findings → Address Comments), and Post announces the PR in Slack at the tail of that chain — or directly via/prs-slack <n> --no-confirmwhen Review is off. With both on, approving runs raise → Review + Post → Address Comments → Slack announce end to endassist prs edit [--title <t>] [--what <w>] [--why <y>] [--how <h>] [--resolves <key>]- Update only the supplied sections of the current PR's body. In a web session the resulting title and body are previewed for approve/reject first (with inline comments and pasted screenshots, as forraise); on approval the edit is applied with any screenshots appended, on rejection the reviewer's comments are printed and nothing is changed. Outside a session the edit applies directlyassist prs list-comments- List all comments on the current branch's pull requestassist prs fixed <comment-id> <sha>- Reply with commit link and resolve threadassist prs wontfix <comment-id> <reason>- Reply with reason and resolve thread. In a web session the reason is previewed for approve/reject first (with inline comments); on rejection nothing is posted, the reviewer's comments are printed and the command exits non-zero. Pass-as the reason to read it from stdin. A body containing markdown — backticks around identifiers,$(...),$VAR— must be piped in rather than passed as an argument, or the calling shell expands it before assist sees it:assist prs wontfix 3718677497 - <<'EOF' Deferring to #197, which renames `query_duckdb` to `query_data`. EOFassist prs reply <comment-id> <body>- Reply to a comment thread without resolving it. In a web session the body is previewed for approve/reject first (with inline comments), as forwontfix. Pass-as the body to read it from stdinassist prs comment <path> <line> <body>- Add a line comment to the pending review. In a web session the comment is previewed for approve/reject first (with inline comments), headedComment on <path>:<line>; nothing is posted until it is approved. Pass-as the body to read it from stdinassist review [number]- Run Claude and Codex in parallel to review the current branch's PR, then post line-bound comments. The diff comes from GitHub, so stale local base branches don't pollute the review; cachedclaude.md/codex.md/synthesis.mdare reused when present[number]-gh pr checkout <number>first, placed by the worktree allocator on a repo with parallel work enabled (see docs/parallel-work.md)--no-prompt- Skip all confirmations--submit- Default the submit prompt to yes--force- Clear all cached files and re-run every phase--refine- Skip posting; walk throughsynthesis.mdinteractively and edit it in place--apply- Skip posting; walk through each finding asking apply/skip. Applied findings are fixed in the working tree--backlog- Skip posting; file all findings as a single bug backlog item with one phase per finding--checkout-only- Skip the review entirely; check the PR out and leave an idle interactive Claude session running in the checkout tree. Requires a PR number, and cannot be combined with--refine,--apply,--backlogor--submit--address-comments- After the review posts comments and submits, start an Address Comments session (assist review-pr-comments <n>) for the PR. Only fires inside an assist session, and only when at least one comment was posted and the review was submitted--announce- Announce the PR in Slack (/prs-slack <n> --no-confirm) at the tail of the chain: the Address Comments session announces once every thread is processed when one was started, otherwise a/prs-slacksession is started directly. Announces exactly once, and only inside an assist session--verbose- Per-line log output instead of the stacked-spinner UI (automatic in CI)
assist github commits <org> [--since <date>] [--top <n>] [--json]- Report commit activity across a GitHub organisation: repos ranked by commits, top committers, and a per-repo author breakdownassist github issue create --title <title> --body <body> [-R <owner>/<repo>] [--type <name>] [--parent <issue>] [--project <number>] [--status <name>] [--label <name>]- Create a GitHub issue on the current repo (or-R's). There is no What/Why/How template — an issue reports a problem, and the target repo's own issue template is unknowable from here. The title and body are rejected if they reference Claude or an assist backlog item, and in a web session they are previewed for approve/reject first (with inline comments and pasted screenshots, as forprs raise, appended under## Screenshotson approval); nothing is created until it is approved.--typesets the native issue type after creation,--parentfiles the new issue as a sub-issue of the referenced issue (owner/repo#number, a github.com issue URL, or a bare number read against--repoor the current repo; a parent in another repository is allowed),--projectadds the issue to the repo owner's project of that number,--statussets that project item's Status, and--labelapplies repo labels (repeat the flag or pass a comma-separated list). All five are resolved before the preview — including the token'sprojectOAuth scope, whose remediation isgh auth refresh -h github.com -s project— so an unknown name, an unreadable parent, a missing scope, or--statuswithout--projectcreates nothing. The preview pane names the repo, type, parent, project, status and labels above the body without adding them to the posted body. Rawgh issue createis denied in favour of this commandassist github issue edit <number> [-R <owner>/<repo>] [--fresh]- Rework an existing GitHub issue's body in the web preview pane. Fetches the issue's currenttitle,bodyandupdatedAtwithgh issue view, writes the body to a working file under~/.assist/github-issues/, and previews it for approve/reject. Approving pushes the pane's markdown back withgh issue edit --body-file; nothing is pushed if the issue was updated on GitHub after it was fetched (the working file is named instead, so nobody else's edit is clobbered) or if the preview is rejected. Rejecting writes the pane's markdown — collapses included — to the working file and names it in the output, so the revision is made in that file rather than composed from scratch; a re-run resumes from the working file while the issue has not moved on GitHub, and--freshdiscards it and re-fetches. Outside a web session there is no pane to edit in, so the command just prints the issue — callers never need to detect the session themselves. Only the body is touched — the title, labels, assignees and state are left alone. Rawgh issue editis denied in favour of this commandassist github issue comment <number> --body <body> [-R <owner>/<repo>]- Comment on a GitHub issue on the current repo (or-R's); a body of-reads it from stdin. The body is rejected if it references Claude or an assist backlog item, and in a web session it is previewed for approve/reject first (with inline comments); nothing is posted until it is approved. Rawgh issue commentis denied in favour of this commandassist github issue edit-comment <comment-id> --body <body> [-R <owner>/<repo>]- Replace the body of a comment that is already posted on the current repo (or-R's); a body of-reads it from stdin.<comment-id>is the numeric comment id — the one in the comment's API url or its#issuecomment-<id>anchor — not the issue number. The replacement runs through the same gate as posting one: it is rejected if it references Claude or an assist backlog item, and in a web session it is previewed for approve/reject first (with inline comments); the published comment is untouched until it is approved. Approving sends the whole body as aPATCH, so what is there now is overwritten rather than appended to. Rawgh apiwrites to issue endpoints are denied in favour of this commandassist github issue fix-structure <target> [-R <owner>/<repo>] [--level <level>] [--type-chain <names>] [--strip-label <label>...] [--apply]- Normalise the issue types across one issue subtree, reading and writing nothing outside it.<target>isowner/repo#number, a github.com issue URL, or a bare number with-R; a bare number with no repo is refused rather than guessed. Walks the subtree via sub-issues level by level (a single deep query blows the GraphQL node limit) and reports the type each issue should carry: every level below the target is typed to the next level down the chain, matching type names loosely soSubtaskandSub-taskboth bind to the leaf. The chain defaults toEpic>Story>Subtask;--type-chain Initiative,Feature,Taskreplaces it, parent level first, and every level named must already exist as an issue type on the organisation or the run fails listing the ones that do. Untyped issues are typed rather than skipped, and cross-repo children are handled in the one run. The target's own level is inferred from its issue type, so aiming at a story types its children as subtasks; when its type is not in the chain the level cannot be inferred and the command exits non-zero naming the type it has.--levelasserts the position instead, which also types the target itself. No label is touched unless--strip-labelnames it; it is repeatable, matched case-insensitively, and each label is removed by the id found on that issue, since label ids differ per repository. Anything nested below the leaf level fails the run before a single write, naming the offender and its parent; nothing is ever re-parented. Without--applynothing is written.--applyannounces each write before it is issued and flushes it, so a long run shows progress, then re-walks the subtree and fails with a non-zero exit if any drift remainsassist news add [url]- Add an RSS feed URL (rendered in the sessions web News tab)
Backlog
Backlog data is stored in a global Postgres database (shared across all repos, scoped per repository by git origin), so a connection string is required. Set it via the ASSIST_DATABASE_URL environment variable or the database.url key in assist.yml; the environment variable takes precedence. Without one, every assist backlog command exits with a setup message. Commands default to the current repository's items; pass --all-repos to span every repository.
Backlog item ids are written and displayed in an a-prefixed form (e.g. item 555 is a555) to disambiguate them from GitHub PR/issue numbers (#42) and Jira keys. Commands and web API routes that take an <id> accept either form.
assist backlog [--dir <path>]- Open the backlog tab in the web dashboard (same asbacklog web)assist backlog list [--status <type>] [-a, --all] [--all-repos] [-v]- List backlog items with status icons (alias:ls; alsoassist list/assist ls)assist backlog add- Add a new backlog item interactively (human CLI use only; agents must usepropose)assist backlog add --name <n> --type <t> --desc <d> --ac <criterion...>- Add a backlog item from CLI optionsassist backlog propose --json <file|-> [--confirmed]- Create an agent-authored item from a JSON payload, previewed for approval in a web session. Outside a web session an agent invocation prints the draft and writes nothing until it is re-run with--confirmed;--confirmedis rejected in a web session, where the pane is the gate. Used by/draftand/bug. See docs/backlog-item-preview.mdassist backlog show <id> [--all-commits]- Display full detail for a backlog item (alias:view). Activity lists the newest 10 commits;--all-commitsprints every commitassist backlog plan <id>- Display the phased plan for a backlog itemassist backlog update-field <id> [--name <n>] [--desc <d>] [--type <t>] [--ac <criterion...>]- Update fields on a backlog itemassist backlog update-field <id> [--add-ac <text>] [--edit-ac <n> <text>] [--remove-ac <n>]- Granular 1-based acceptance-criteria editsassist backlog update-field <id> --origin [url-or-key]- Retag a single item to a different repoassist backlog add-phase <id> <name> --task <t...> [--manual-check <c...>] [--position <pos>]- Add a phase to an existing itemassist backlog update-phase <id> <phase> [--name <n>] [--task <t...>] [--manual-check <c...>]- Modify a plan phase (alias:edit-phase)assist backlog update-phase <id> <phase> [--add-task <t>] [--edit-task <n> <t>] [--remove-task <n>] [--add-check <c>] [--edit-check <n> <c>] [--remove-check <n>]- Granular 1-based task and manual-check editsassist backlog remove-phase <id> <phase>- Remove a plan phase from a backlog itemassist backlog move-phase <id> <from> <to>- Reorder a plan phase between 1-based positionsassist backlog update-plan <id> --json <file|->- Replace an item's whole plan from a JSON payload, previewed as a single diff for approval. The path/refineand agent sessions use for every plan changeassist backlog add-subtask <id> --title <t> [--desc <d>]- Add a sub-task. Sub-tasks under thesubtaskskey inassist.yml/~/.assist.ymlare auto-applied to every new itemassist backlog edit-subtask <id> <idx> [--title <t>] [--desc <d>] [--status <s>]- Edit a sub-task by its 1-based indexassist backlog remove-subtask <id> <idx>- Remove a sub-task by its 1-based indexassist backlog subtask-status <id> <idx> <status>- Set a sub-task's status (todo,in-progress,done)assist backlog start <id>- Set a backlog item to in-progressassist backlog stop- Revert all in-progress items to todo and reset their phase to 1assist backlog done <id>- Set a backlog item to done (blocked while any sub-task is not done)assist backlog wontdo <id> [reason]- Set a backlog item to won't doassist backlog set-status <id> <status>- Set status (todo,in-progress,done,wontdo)assist backlog star <id>/assist backlog unstar <id>- Pin an item ahead of unstarred items in the web viewassist backlog delete <id>- Delete a backlog itemassist backlog comment <id> <text>- Add a comment to a backlog item. Setbacklog.previewCommentstotrueto have the comment shown in the web preview pane and only written once approved; by default it is written immediatelyassist backlog comments <id>- List comments and summaries for a backlog itemassist backlog delete-comment <id> <comment-id>- Delete a comment (summaries cannot be deleted)assist backlog phase-done <id> <phase> <summary>- Signal that a plan phase is completeassist backlog rewind <id> <phase> --reason <reason>- Rewind an item to an earlier phaseassist backlog next [id] [--once]- Pick and run the next backlog item, or open/draftif none remainassist backlog refine [id] [--once] [--harness <claude|codex|pi>]- Alias forrefineassist backlog run <id> [--harness <claude|codex|pi>] [--write|--no-write]- Run a backlog item's plan phase-by-phase with the selected harness, defaulting toharness.engine; for Codex, write access uses theworkspace-writesandbox and--no-writeusesread-onlyassist backlog export [file]- Export every table in the backlog database to a file, or stdoutassist backlog import [file]- Restore every table present in a dump back into the database (-y, --yesskips the prompt)assist backlog associate-jira <id> [key]- Associate a Jira ticket (bare key or browse URL); clears any GitHub issue on the item.--clearremoves itassist backlog associate-github <id> [issue]- Associate a GitHub issue (URL orowner/repo#number); clears any Jira key on the item.--clearremoves itassist backlog add-activity <id> <kind> <ref>- Attach an activity ref (branch,commit,commit-parent,pr,slack,session);--title,--url,--stateoverride metadataassist backlog record-slack <url>- Attach a Slack thread permalink to the current session's item; used by/prs-slackassist backlog record-session <id>- Attach the Claude session the command runs inside to an item;--session <sessionId>overrides detectionassist backlog move-repo <old-origin> [new-origin]- Retag all items from one origin to another after a repo rename (-y, --yesskips the prompt)assist backlog clone <origin>- Clone a repo over SSH intoclone.baseDir(default~/git)assist backlog web [-p, --port <number>] [--no-open]- Open the backlog tab in the web dashboard (default port 3100)
Config and run commands
assist run <name> [params...]- Run a configured command from assist.yml. A backlog item id (a555/555) with no matching command forwards toassist backlog runassist run add- Add a new run configuration to assist.yml and create a Claude command fileassist run link <path> --prefix <prefix>- Link run configurations from another project's assist.ymlassist run remove <name>- Remove a run configuration and delete its Claude command file
A run entry's relative cwd (and a link path) resolves against the repo root - the directory holding assist.yml or .claude/, or the enclosing git repository when the repo has no project config at all (entries coming only from a repos: override in ~/.assist.yml). The base does not shift with which config file the entry came from. A resolved cwd that does not exist fails with run config "<name>": cwd <path> does not exist rather than a spawn <command> ENOENT, and the daemon logs that reason when a run: session errors.
assist config keys [filter]- List every key in the config schema with its type, schema default, what it does and theassist config setline that sets it. The optional filter narrows to keys containing it (case-insensitive), soassist config keys worktreeshows just the worktree block. The listing is derived fromassistConfigSchemaand theconfigHelpregistry thatassist verify config-keysforces to cover every key, so no key can go missing from itassist config get <key>- Get a config value. Secret values (database.url,roam.*tokens,sql.connections[].password,seq.connections[].apiToken) print as<hidden>;--revealprints the raw value undecorated for command substitution and always needs an explicit permission prompt (the CLI hook never auto-approves it). An unset key exits non-zero and reportsKey "<key>" is not set; when the key is a valid schema key that message carries its schema default (orhas no schema default) plus the key's note and setterassist config list- List the config values that are set, with secret values shown as<hidden>(no reveal option). Unset optional blocks are omitted entirely, so the output leads with a comment pointing atassist config keysfor the full schemaassist config set <key> <value>- Set a config value.--globalwrites to~/.assist.yml;-g --repo [name]writes a per-repo override there. The confirmation line and any validation error mask secret valuesassist config unset <key>- Remove a config value so the key falls back to the global value or schema default (-gtargets~/.assist.yml;-g --repo [name]removes it from a per-repo override there)
The Config tab of the sessions web dashboard never receives secret values: GET /api/config replaces each one with a set-or-unset marker, so a configured secret renders as a mask and an unset one as not set, both keeping their project/global/default chip. Secret fields edit write-only - the mask clears on focus, typing a value replaces the stored one, and leaving the field untouched keeps it.
Verify and lint
assist verify- Run all verify:* commands in parallel (from assist.yml run configs and package.json scripts)assist verify all- Run all checks, ignoring diff-based filtersassist verify --measure- Print a summary table of each command's status and durationassist verify init [--package-json]- Add verify scripts to a projectassist verify hardcoded-colors- Check for hardcoded hex colors in src/ (hardcodedColors.ignore)assist verify block-code-comments- Fail on any comment on a changed line (blockCodeComments.ignore); machine directives exemptassist verify forbidden-strings- Check configured JSON files for disallowed values (forbiddenStringsrules)assist verify config-keys- Check every leaf key inassistConfigSchemais surfaced in some command's--helpviaconfigHelpassist verify migrations- Check bundled DB migrations are sequentially numbered, append-only, and free of unacknowledged destructive DDLassist lint [-f, --fix]- Run lint checks for conventions not enforced by oxlintassist lint init- Initialize oxlint with baseline linter config
Refactoring
assist refactor check [pattern]- Check for files that exceed the maximum line countassist refactor ignore <file>- Add a file to the refactor ignore listassist refactor rename file <source> <destination>- Rename/move a TypeScript file and update all imports (--applyto execute)assist refactor rename symbol <file> <oldName> <newName>- Rename a symbol across the project (--applyto execute)assist refactor extract <file> <functionName> <destination>- Extract a function and its private dependencies to a new file (--applyto execute)assist refactor restructure [pattern]- Analyze the import graph and restructure tightly-coupled files into nested directories
Rules
assist rules list [path] [--full]- List the rules in scope for a path (default: cwd), read from the## Rulessection of everyCLAUDE.mdfrom that path's directory up to the repo root, nearest scope first, grouped by the file each rule came from. Shows each rule's title, or its description where it has no title;--fulladds the description under every title. Rules are- **<code>** — **<title>** — <text>bullets, the title optionalassist rules add <text> [--title <title>] [--scope <path>]- Add a rule to the## Rulessection of the scope'sCLAUDE.md, creating the section when absent and allocating the next repo-wide code.--titleis the few-word summary the rule picker shows in place of the description.--scopetakes a file or directory (resolved to the nearest existingCLAUDE.mdat or above it, defaulting to cwd) or aCLAUDE.mdpath written to directly and created if absent. After writing, the rootCLAUDE.mdrecords the directories that carry their own## Rulesso scoped rules stay discoverable from the rootassist rules index- Record the directories that carry their own## Rulesin the repo root'sCLAUDE.md, rewriting the line in place rather than duplicating it. Always repo-wide, resolved from the cwd — it takes no path, since the index lives at the root by definition and cannot be narrowed.rules adddoes this on every add; run it directly after hand-editing a## Rulessection (e.g. renaming an existing heading)
Devlog
assist devlog list- Group git commits by dateassist devlog next- Show commits for the day after the last versioned entryassist devlog repos- Show which github.com/staff0rd repos are missing devlog entriesassist devlog skip <date>- Add a date to the skip listassist devlog version- Show current repo name and version info
Hooks
assist cli-hook- PreToolUse hook auto-approving CLI commands fromallowed.cli-reads/allowed.cli-writes(plus read-onlygh api), checking each sub-command of a compound command independently; also deniesRead/Grep/Globcalls targeting~/.assist/restrictedassist cli-hook add <cli>- Discover a CLI's commands and auto-permit read-only onesassist cli-hook check <command> [--tool <tool>]- Check whether a command would be auto-approvedassist cli-hook deny- List all deny rulesassist cli-hook deny add <pattern> <message>- Add a deny rule for a command patternassist cli-hook deny remove <pattern>- Remove a deny rule by patternassist codex-hook- Codex hook that auto-approves read-only commands (PreToolUse/PermissionRequest) reusing thecli-hookallowlist, and reports session status to the sessions daemon (UserPromptSubmit/PreToolUse/PostToolUse→ running,Stopand an undecidedPermissionRequest→ waiting) so a Codex session card shows live running/waiting; installed byassist syncwhencodexis on PATHassist pi-hook- pi permission-gate adapter reusing thecli-hookallowlist, emittingallow/deny/gate; installed byassist syncwhenpiis on PATHassist edit-hook- PreToolUse hook that blocksEdit/Write/MultiEditcalls from adding, changing, or removing a// assist-maintainability-overridemarker, or from introducing a code comment (usecode-comment set/confirmfor the rare comment that belongs)assist code-comment set <file> <line> <text>- Validate a comment (max 50 chars, single-line) and issue a pin authorising its insertionassist code-comment confirm <pin>- Insert the pinned comment at its file/line and clear the pin stateassist db-migration unlock- Page a human to approve creating the next new migration module, issuing a pin via desktop notificationassist db-migration confirm <pin>- Confirm a pin fromdb-migration unlock, letting that migration's file write through onceassist notify- Show desktop notification from JSON stdin (macOS, Windows, WSL)assist status-line- Format Claude Code status line from JSON stdin
.NET
assist dotnet inspect [sln]- Run JetBrains inspections on changed .cs files to find dead code--scope all|base:<ref>|commit:<ref>- Inspect the whole solution, everything changed since a base ref, or one commit--only <ids...>/--suppress <ids...>- Show only, or suppress, specific issue type IDs--roslyn- Use Roslyn analyzers via msbuild instead of JetBrains--swea- Enable solution-wide error analysis (slower but more thorough)
assist dotnet check-locks- Check if build output files are locked by a debuggerassist dotnet deps <csproj>- Show .csproj project dependency tree and solution membershipassist dotnet in-sln <csproj>- Check whether a .csproj is referenced by any .sln file
Data sources
assist jira auth- Authenticate with Jira via API tokenassist jira ac <issue-key>- Print acceptance criteria for a Jira issueassist jira view <issue-key>- Print the title and description of a Jira issue- Note: Claude fetches Jira context via the MCP Atlassian server, so
/jiraand Jira-key mentions go through MCP. These CLI commands remain for direct human use.
- Note: Claude fetches Jira context via the MCP Atlassian server, so
assist miro extract [name] [--items <file>] [--top-left <id|link> --bottom-right <id|link>] [--ignore <file>] [--out <file>] [--board <id>] [--frame <id>] [--save <name>] [-g] [-r [repo]]- Print the text of every box inside a rectangle on a Miro board as a YAML list, leftmost box edge first (topmost edge breaks ties).--itemsis a file of rawboard_list_itemsresponse pages; anchors accept a bare widget id or a?moveToWidget=<id>link. Omit both anchors to pick them by clicking the top-left then the bottom-right box in the assist web UI preview pane, which echoes the pair back as flags for later runs.--ignore <file>is a YAML list of box texts to drop, warning about entries that matched nothing; repeated identical text is listed once at its highest-priority position.--out <file>writes the YAML to a file with a header recording the board, frame, anchors and computed rectangle instead of printing to stdout;--board/--frameoverride the ids read from the items for that header. After a pick the selection can be saved as a named extract undermiro.extracts—--save <name>saves without asking, and-g/-r [repo]choose the config file as they do forassist config set(the projectassist.ymlby default).assist miro extract <name>then replays that extract with no flags, resolving its paths from the repo root and reporting the config file it came from; any flag overrides the matching fieldassist ravendb auth add- Add a new RavenDB connectionassist ravendb auth list- List configured RavenDB connectionsassist ravendb auth remove <name>- Remove a configured connectionassist ravendb set-connection <name>- Set the default connectionassist ravendb query [connection] [collection]- Query a RavenDB collection (--page-size,--sort,--query,--limit)assist ravendb collections [connection]- List collections and document countsassist seq auth add- Add a new Seq connectionassist seq auth list- List configured Seq connectionsassist seq auth remove <name>- Remove a configured connectionassist seq set-connection <name>- Set the default Seq connectionassist seq query <filter>- Query Seq events (-c <connection>,--json,-n <count>,--from <date>,--to <date>)assist slack post [channel] --body <body|-> [--thread <ts-or-permalink>]- Preview a markdown message bound for a Slack channel;--body -reads it from stdin. The channel falls back toslack.channelwhen the argument is omitted, and the command errors naming theassist config set slack.channelsetter when neither is given.--threadtakes a message ts (1712345678.123456) or a Slack archives permalink (.../archives/C012AB3CD/p1712345678123456, whosethread_tsquery parameter wins when present, so a link to a reply resolves to its parent) and resolves it to thethread_tsthe reply is posted under; anything else is a usage error. Posting is MCP-only, so the command never posts: in an assist web session it renders the markdown in the preview pane for approve/reject (with inline comments), and on approval writes the approved body to a working file under~/.assist/slack/, printing its path on the last line — preceded by the resolvedthread_ts— for/slack-postto send. On rejection it exits non-zero with the reason and any inline comments, leaving the previewed markdown in that working file to revise in place and re-preview. Outside a web session there is no preview and the body passes straight throughassist sql auth add- Add a new MSSQL connectionassist sql auth list- List configured SQL connectionsassist sql auth remove <name>- Remove a configured connectionassist sql set-connection <name>- Set the default SQL connectionassist sql query "<sql>" [connection]- Execute a read-only SQL statement and print a table (rejects mutating statements)assist sql mutate "<sql>" [connection]- Execute a mutating SQL statement and print rows affectedassist sql tables [connection]- List tables in the connected databaseassist sql columns <table> [connection]- List columns for a table (schema.tablefor a non-default schema)
Other
assist netcap [-p, --port <port>] [-o, --out <dir>] [-f, --filter <pattern>]- Capture browser network traffic tocapture.jsonlunder--out(default~/.assist/netcap), paired with the netcap browser extensionassist netcap extract-linkedin-posts [file]- Parse a netcap capture into structured LinkedIn posts, written toposts.jsonbeside the captureassist criteria-extension [--sign]- Print the directory to load the acceptance criteria outliner extension unpacked from (copies toC:\tools\criteria-extensionunder WSL);--signsigns it on AMO's unlisted channel and prints the.xpifor a permanent Firefox installassist screenshot <process>- Capture a screenshot of a running application window (screenshot.outputDir, default./screenshots)assist handover save --summary <s>- Save a session handover note (content from stdin), scoped by the repo's git originassist handover list- List unrecalled handovers for this repo, most recent firstassist handover recall [id]- Print an unrecalled handover and mark it recalled (most recent by default)assist handover load- SessionStart hook entry point advising how many unrecalled handovers existassist mermaid export [file.md]- Render each fenced mermaid block to<stem>-<index>.svgvia Kroki (--out,--index,mermaid.krokiUrl)assist prompts- Show top 10 denied tool calls by frequency with count and repo breakdown
Project setup
assist init- Initialize project with VS Code and verify configurationsassist new vite- Initialize a new Vite React TypeScript projectassist new cli- Initialize a new tsup CLI projectassist update- Update assist to the latest version and sync commandsassist vscode init- Add VS Code configuration filesassist deploy init- Initialize Netlify project and configure deploymentassist deploy redirect- Add trailing slash redirect script to index.htmlassist roam auth- Authenticate with Roam via OAuthassist roam show-claude-code-icon- Forward Claude Code hook activity to Roam local API
Complexity
assist coverage- Print global statement coverage percentageassist complexity <pattern>- Analyze a file (all metrics if single match, maintainability if multiple)assist complexity cyclomatic [pattern]- Calculate cyclomatic complexity per functionassist complexity halstead [pattern]- Calculate Halstead metrics per functionassist complexity maintainability [pattern]- Calculate maintainability index per file (--ignore <glob>, pluscomplexity.ignore). A file can declare its own threshold with a// assist-maintainability-override: Ncomment in its first ~10 lines, replacing--thresholdfor that file onlyassist complexity sloc [pattern]- Count source lines of code per file
Transcripts and voice
assist transcript configure- Configure transcript directoriesassist transcript clean <path>- Clean any .vtt file and write the result to stdout (--format <md|vtt>, defaultmd). Markdown chat log:assist transcript clean ./raw.vtt > clean.md; cleaned WebVTT with timings preserved:assist transcript clean ./raw.vtt --format vtt > fixed.vtt.--timestampsprefixes each markdown speaker turn with[hh:mm:ss], so passages can be cited as ranges formerge --select:assist transcript clean ./raw.vtt --format md --timestampsassist transcript list- List raw .vtt filenames waiting in the pick-up directoryassist transcript merge <path...>- Collapse several .vtt files into one transcript withNOTEprovenance, rebasing cue times onto a continuous timeline (--out <path>to write a file instead of stdout):assist transcript merge ./a.vtt ./b.vtt --out ./refinement.vtt.--select <file|->takes keep/removed JSON (-reads it from stdin) naming the ranges to keep, so only those passages survive and the dropped ones are counted in the header:assist transcript merge ./a.vtt ./b.vtt --select ./selection.json.--no-provenanceomits everyNOTE— the Collapsed-from header, the per-passage source marks and the removed count — for an output going somewhere the source names and cut points should not follow:assist transcript merge ./a.vtt ./b.vtt --select ./selection.json --no-provenance.--strip-profanitydeletes the profanity that carries no meaning — an intensifier before a word,the fuck/the hellafter a wh-word, a standalone expletive interjection — and drops any cue that is nothing but an expletive, leaving load-bearing uses (verb, idiom, decision marker, predicate adjective, noun, reported speech) alone:assist transcript merge ./a.vtt ./b.vtt --no-provenance --strip-profanityassist transcript move <file>- Convert a raw .vtt to a dated markdown transcript and archive the originalassist voice setup- Download required voice models (VAD, STT)assist voice start [--foreground]- Start the voice daemon (always-on, listens for wake word)assist voice stop- Stop the voice daemonassist voice status- Check voice daemon status and recent eventsassist voice devices- List available audio input devicesassist voice logs [-n <count>]- Show recent voice daemon log entries
Sessions
assist sessions- Start the web dashboard (same assessions web)assist sessions web [-p, --port <number>] [--no-open]- Start the web dashboard with Sessions, Backlog and News tabs (default port 3100). Ctrl+R in the foreground terminal opens a restart menu; Ctrl+. in the browser jumps to the next session waiting on inputassist sessions summarise [-f, --force] [-n, --limit <count>]- Generate one-line summaries for unsummarised Claude sessionsassist sessions close- Dismiss the current daemon-managed session: kills its process tree, removes its card from the dashboard and reaps its worktree. Outside such a session it reports there is nothing to close and exits 0assist sessions rename <title>- Retitle the current daemon-managed session: the given title replaces the generated title and the backlog item name on its dashboard card for the rest of its life. Outside such a session it reports there is nothing to rename and exits 0assist sessions set-status <status>- Report the current session's status (running/waiting) to the daemon; invoked by the Claude Code hooks the daemon wires into each sessionassist daemon run- Run the sessions daemon in the foreground (normally auto-spawned detached)assist daemon status- Show daemon status, live sessions, and any stray processes or stolen socketassist daemon stop- Stop the sessions daemon; running claude sessions resume on next startassist daemon restart- Restart the sessions daemon, resuming previously running claude sessionsassist daemon drain [--yes]- Remove all sessions from the local daemon for a clean slate; a session holding unpushed work is stopped, not removed
Session launchers
assist next [id] [--once]- Alias forbacklog next [id];--onceexits after the first completed item runassist draft [description] [--once](alias:feat) - Launch Claude in/draftmode, chain into next on/nextsignalassist bug [description] [--once]- Launch Claude in/bugmode, chain into next on/nextsignalassist refine [id] [--once] [--harness <claude|codex|pi>]- Launch a coding harness in/refinemode;--harnesspicks the engine, defaulting to the configuredharness.engine(Claude)assist review-pr-comments [number] [--announce] [--resume-session <id>]- Launch Claude in/review-pr-commentsmode; a PR number is checked out first viagh pr checkout.--announce(requires a number) announces the PR in Slack via/prs-slack <number> --no-confirmonce every comment thread has been processedassist fix-conflict [number] [--rebase] [--resume-session <id>]- Launch Claude in/fix-conflictmode to resolve the branch's conflicts against the remote default; a PR number is checked out first viagh pr checkout.--rebaserebases onto the remote default instead of merging it inassist signal next [id]- Write a next signal to chain intoassist nextassist signal done [id]- Write a done signal marking the session's initial task complete; an optionalidsurfaces the backlog item the session created onto its card
draft, bug, refine, review-pr-comments, fix-conflict and backlog run accept --resume-session <id> to resume an interrupted Claude session (used by the daemon when it restarts or restores a running item). Launchers without the flag — next among them — are respawned without it rather than being handed an option they would reject.
Sessions dashboard
Web sessions are owned by a long-lived daemon process, not the web server: the server is a thin client relaying WebSocket traffic to the daemon over a local IPC socket (~/.assist/daemon/daemon.sock; named pipe \\.\pipe\assist-sessions-daemon on Windows). Restarting the web server leaves sessions running with scrollback intact. The daemon logs to ~/.assist/daemon/daemon.log and auto-exits once no sessions remain and no client has connected for 60 seconds. See docs/session-lifecycle.md.
The topnav has a Design dropdown: submitting a prompt launches an interactive claude session with the vendored design system prompt appended via --append-system-prompt.
Every live session card carries an add-agent button (👥) that starts a second agent inside that session's existing workspace rather than allocating a new one. While several agents share a workspace, only the last one to leave triggers teardown.
A run: entry in assist.yml flagged server: true (with an optional display-only port:) is a singleton dev server: at most one may be live per normalised git remote, i.e. across a clone and all its sibling clones. Session cards for such a repo show a ▶ start button; the daemon rejects a second server run for that remote, and the web UI turns the conflict into a "replace running server?" prompt. The serving card shows a serving :<port> chip and a ⏹ stop button, and the slot frees whenever that session stops. Non-server runs are unconstrained.
Windows-host repos (from WSL)
Requires assist installed on the Windows host.
sessions.windowsProjectsRoot— the Windows.claude/projectsdirectory as seen from WSL (e.g./mnt/c/Users/<user>/.claude/projects); enables discovery of Windows-host repos, tagged with aWindowsbadge.sessions.windowsDaemonHost/sessions.windowsDaemonPort— where the WSL daemon reaches the native Windows daemon (defaults127.0.0.1/51764; set the host to the Windows IP on WSL2 NAT-mode networking).sessions.windowsVersionCheck— reaction to a protocol-version mismatch in the WSL↔Windows handshake:block(default) refuses creates and auto-heals the host,warnproceeds anyway,offskips the check.
Session config keys
sessions.includeCommittedChanges— defaults to true: the card's change counts, the/diffview and its scope picker cover the commits recorded against the session's backlog item as well as uncommitted work, so the change link survives the agent committing. Each committed path is diffed against the parent of the earliest of those commits that touched it, so nothing outside the item's own commits is shown. Set it false to count and diff only uncommitted changes. A session whose item has no recorded commits and a clean tree still shows nothing either way.sessions.topBar— defaults to true: a sticky top bar inside the terminal panel carrying the session's ids, backlog chip and story name, the phase caption, elapsed time, the Continue/Auto-run/Dismiss switches and the session actions. Set it false to keep all of that on the card instead.sessions.floatWaiting— defaults to true: sessions that have beenwaitingon input for longer than the threshold float above the other cards, longest waiting first. Set it false to keep the star-only ordering; starred sessions still sort above everything.sessions.floatWaitingAfterMs— defaults to 5000: how long a session must have beenwaitingon input beforesessions.floatWaitingfloats it.sessions.maxLive— defaults to 24: the ceiling on concurrent live sessions one daemon holds. Spawning past it is refused (session ceiling of N reached) and a daemon birth respawns at most this many persisted sessions, deferring the rest to stopped cards. The daemon serves every repo, so set it globally:assist config set sessions.maxLive 32 -g.
Parallel work
Concurrent sessions in one repo can be isolated with native git worktrees instead of keeping multiple physical clones: see docs/parallel-work.md. All of these flags default off:
worktree.enabled(parallel work) — spill concurrent sessions into adjacent<clone>-Nworktrees. Off means every session on the repo shares its single working copy.worktree.watcher— starting a backlog run also ensures one starred watcher session in the clone itself: a claude session whose prompt is/watch, run with--permission-mode auto, so the clone keeps fast-forwarding and rebuilding while the run works in its worktree. The watcher's cwd is always the clone, never a worktree. One watcher per clone — a run (fresh or chained) that finds a live one spawns no second, and the reason lands indaemon.log; a watcher that has stopped or errored is replaced by the next run. Needsworktree.enabled, a/watchcommand in the repo, and anauto-buildrun entry in itsassist.yml(whatassist watch wait --buildinvokes).worktree.trunk(trunk-based) — on, a spilled worktree's branch tracksorigin/<trunk>so commits land on the mainline. Off, it starts off the remote default branch with no mainline tracking, leaving the session to raise its own branch and PR.While it is on, a job that commits never runs in the clone:
backlog run <id>(spawned fresh or chained into from a session already sitting in the clone) and PR checkouts (review <n>,review-pr-comments <n>) always allocate a<clone>-N, even when the clone is idle and clean. Committing there would land the work on the local mainline and leave every later worktree starting from that HEAD. Plain prompts,spawnInTreesessions,draft/bug/refineand every other command keep the normal clone-preferring placement, and a session pinned in place stays where it was launched. There is no fallback — if the worktree can't be created the spawn fails with the reason indaemon.lograther than dropping the job in the clone. Non-trunk repos are unaffected.worktree.includeDrafts— give draft, bug and refine sessions their own<clone>-N. They change no code, so by default they run in the clone's working copy at no worktree, dep-install or teardown cost.
Neither flag leaves permanent state on the clone: nothing writes to the clone's .git/config (assist commit derives its push refspec from the current branch), so turning parallel work back off leaves the repo as it was.
Iterating on assist itself
Web server changes only need the assist sessions process restarted — sessions survive. Daemon/session-core changes need assist daemon restart: claude sessions are auto-respawned via claude --resume with scrollback starting fresh, while run sessions reappear as not-restored tiles that can be retried.
A restart kills every managed session's pty, which also kills any background task running inside it. daemon.log names each session it kills, and the shutdown records the reason against them in sessions.json before the ptys die. On restore, a session the restart caught mid-turn is resumed with a prompt naming the restart instead of the generic one, and a session that was idle only because it was waiting on a background task — a /watch loop, say — is woken with the same prompt, naming the task ids that died. An idle session with no background work in flight is left idle, as before.
Other config keys
slack.channel— the Slack channel (e.g.#example) thatassist slack postand/slack-posttarget when no channel argument is givenprs.slack— the Slack channel (e.g.#example) that/prs-slackposts pull requests to via the Slack MCP connectorprs.required— whentrue(defaultfalse),assist backlog runcuts and records a fresh branch for a story that has no recorded branch at run start, so a new story never inherits the previous one's branchprs.promptJira— whentrue(defaultfalse), theassist prs raise --help--resolvesguidance instructs asking the user for a Jira keyprs.draft— whentrue(defaultfalse),assist prs raisecreates a draft PR.--draftand--no-draftoverride it in either direction; only the create path is affected,assist prs editnever changes an existing PR's draft statecommit.pull— when enabled,assist draft,bug,refine,nextandbacklog runrungit pull --ff-onlyfirst and abort if it fails (nextpulls once per invocation, not per item)commit.expectedBranch— when set (e.g.main),assist commitprints a non-blocking warning if HEAD is on any other branch, so work on a stray branch isn't silently orphanedbranch.prefix— when set (e.g.sw),assist branch <slug>prepends<prefix>/to the branch namebranch.defaultBranch— override the base branch, which is otherwise resolved live from the remote (git ls-remote --symref origin HEAD), falling back tomain
Acceptance criteria outliner extension
assist criteria-extension prints the directory to load unpacked; nothing talks to assist at runtime. The extension is a single Manifest V3 content script scoped to https://github.com/*, built by npm run build from src/commands/criteriaExtension/criteriaContentScript.ts into criteria-extension/content.js (gitignored). It adds an Outline criteria toggle to the markdown toolbar of an issue's body editor and the new-issue form, re-attaching after client-side navigation; pressing it hides the textarea and mounts the same AcceptanceCriteriaOutline control the web preview panel uses, in a shadow root with its own emotion cache and MUI theme. A body whose acceptance criteria are bullets, checkboxes or prose gets a Convert to outline button instead, and a body with no acceptance criteria heading gets Add acceptance criteria. Each edit is written back through writeAcceptanceCriteria into the textarea and dispatched as an input event, so GitHub's own Save pushes it.
- Run
assist criteria-extension— it prints the extension directory. Under WSL it copies the extension toC:\tools\criteria-extensionand prints that Windows path instead; re-run and reload the extension after a rebuild. - Load the unpacked extension:
- Chrome: open
chrome://extensions, enable Developer mode, click Load unpacked, and select the extension directory. - Firefox: open
about:debugging#/runtime/this-firefox→ Load Temporary Add-on… → pickmanifest.jsoninside the printed extension directory. Firefox drops a temporary add-on on every restart; see permanent Firefox install to avoid that.
- Chrome: open
- Open an issue, press Edit on the body, and press Outline criteria. The new-issue form works the same way.
Permanent Firefox install
Firefox release and beta only install signed add-ons, so a permanent install means having Mozilla sign the package. Signing uses AMO's unlisted (self-distribution) channel: Mozilla signs the .xpi and hands it back, with no public store listing and no human review queue.
Signing runs in CI, so the AMO key lives only as repo secrets and never on a developer machine. One-time setup: create an AMO API key at addons.mozilla.org/developers/addon/api/key and add its two halves as the repo secrets AMO_JWT_ISSUER (the user:12345678:123 issuer) and AMO_JWT_SECRET. The Criteria extension workflow maps them onto the WEB_EXT_API_KEY / WEB_EXT_API_SECRET that web-ext reads.
- Run the Criteria extension workflow from the Actions tab (
gh workflow run criteria-extension.yml). It checks outmain, builds, signs the extension at assist's current version, and uploads the signed add-on to that version's GitHub release ascriteria-extension.xpi. A failed AMO submission fails the job and attaches nothing. - Download
criteria-extension.xpifrom the release — the URL follows from the version,https://github.com/staff0rd/assist/releases/download/v<version>/criteria-extension.xpi. - Open the
.xpiin Firefox (drag it onto a window, orabout:addons→ gear → Install Add-on From File…). It survives restarts.
AMO refuses a version it has already signed, which is why the staged manifest carries assist's version instead of the manifest's 1.0.0 placeholder — sign once per release, not twice at the same version. Unlisted add-ons get no updates from AMO, so a new build means installing the new .xpi; wiring up self-hosted update_url auto-updates is not done.
assist criteria-extension --sign still signs locally for someone who holds an AMO key: export WEB_EXT_API_KEY and WEB_EXT_API_SECRET, run npm run build (to refresh content.js), then the command. It stages a copy of the extension with assist's version stamped into manifest.json, shells out to npx web-ext sign, and writes the signed add-on to ~/.assist/criteria-extension/criteria-extension.xpi. Under WSL it copies that to C:\tools\criteria-extension.xpi and prints the Windows path.
Two alternatives need no signing: Firefox Developer Edition or Nightly honour xpinstall.signatures.required = false in about:config and will then install an unsigned local .xpi permanently, and Chrome's Load unpacked already survives restarts, so the unpacked directory is one-time setup there.
netcap browser extension
assist netcap only runs the receiver; the browser side is a raw Manifest V3 extension (no build step) under netcap-extension/. A MAIN-world content script patches fetch/XMLHttpRequest to capture {url, method, status, requestBody, responseBody, timestamp} and relays each entry to the background service worker, which POSTs it to the receiver. Forwarding happens in the background context, so the page's CSP (connect-src) never blocks it.
- Run
assist netcap— it prints the receiver URL, the capture file path, and the extension directory to load. The receiver host/port and the optional--filtersubstring are baked into the extension'sbackground.jsat this point. Under WSL it copies the extension toC:\tools\netcap-extensionand targets the WSL VM's IP, printing that Windows path instead; re-run after a reboot (the WSL IP can change) and reload the extension. - Load the unpacked extension:
- Firefox: open
about:debugging#/runtime/this-firefox→ Load Temporary Add-on… → pickmanifest.jsoninside the printed extension directory. (Requires Firefox 128+ for MAIN-world content scripts.) - Chrome: open
chrome://extensions, enable Developer mode, click Load unpacked, and select the extension directory.
- Firefox: open
- On load the background worker pings the receiver;
ping from extensionappears in theassist netcaplog, confirming browser→server connectivity. - Browse a site; matching requests append to the capture file live and survive page refreshes. Press Ctrl-C to stop the receiver; it prints how many entries were captured.
