fastcoder
v0.13.1
Published
Fastcoder — CLI tool to register, inspect and report on local development projects.
Maintainers
Readme
Fastcoder
CLI tool to register, inspect and report on local development projects.
Features
- Register and remove projects (persisted in SQLite at
~/.fastcoder/fastcoder.db) - Read each project's tasks folder (default
.docs/, configurable viaTASKS_FOLDERin.envor the global settings — e.g..docs/config.jsonwith achecksarray of shell commands) - Run shell commands inside any registered project
- Run periodic check passes and store reports
- Drive coding tasks through stage folders (
todo → doing → validate → test → document → done) and implement them with Claude Code - Run a single
dothat executes one task through the full flow (continue, specification, do, validate, test, document, periodic) in priority order, across one project or every project - Track every Claude Code run as an execution (status, tokens, usage-limit headroom)
- Retry failed tasks automatically and quarantine repeat failures in a
failures/stage - Record exceptions for interrupted runs and an action log of every step
- Full web interface (React + Vite + Express, TypeScript) exposing every feature — manage projects, the task board, executions, exceptions, logs, reports, the scheduled job and settings
- Install/remove a system
crontabentry that runs the checks on a schedule
Install (global CLI)
Prerequisites
- Node.js ≥ 22 (Node 22 LTS or Node 24 LTS recommended). Older Node
versions are unsupported — Node 18 reached end-of-life in April 2025 and
better-sqlite3no longer ships prebuilt binaries for them. - A C/C++ toolchain only if no prebuilt binary exists for your Node version/platform (see Troubleshooting below). Prebuilt binaries are shipped for the recommended Node LTS versions, so most users do not need a compiler.
Install
cd /Users/username/projects/fastcoder
npm install
npm install -g .Now fastcoder is on your PATH.
Update the local CLI (without publishing to npm)
To test your local working copy as the global fastcoder command — without
ever pushing to npm and reinstalling from there — rebuild and reinstall from the
project directory. Pick whichever workflow fits how often you iterate:
One-off update (recommended). Reinstall the current local code over the
global install. --force ensures npm overwrites the existing global package even
when the version number in package.json has not changed:
cd /Users/username/projects/fastcoder
npm run build:all # compile dist/ AND web-ui/dist (full version)
npm install -g . --forceOr, equivalently, via make:
make update-global-full # build web UI + CLI, then reinstall globally (--force)make update-global-full rebuilds the web UI and the CLI before
reinstalling. If you only changed CLI/src/ code, use make update-global
(rebuilds the CLI only). The individual steps are also available as
make build-web and make build.
Why
build:all/build:web?npm install -g .runs thepreparescript, which only compiles the TypeScript CLI intodist/. It does not rebuild the React web interface. If you changed anything underweb-ui/and want the full local version, build the web bundle first (npm run build:web) so the packagedweb-ui/distis up to date. If you only changed CLI/src/code,npm install -g . --forcealone is enough.
Live-linked update (best for frequent iteration). npm link symlinks the
global fastcoder to this folder, so you only need to recompile — no reinstall:
cd /Users/username/projects/fastcoder
npm link # link once; global `fastcoder` now points here
npm run build:all # recompile after each change (re-run as needed)Re-run npm run build (CLI) or npm run build:web (web UI) after edits and the
global fastcoder immediately reflects the rebuilt output. Run npm unlink -g
fastcoder (or make uninstall-global) to undo the link.
No-install run. To execute the local code directly without touching the global install at all, run it straight from source or the compiled output:
make dev # run the CLI from TypeScript via ts-node
node bin/fastcoder.js # run the compiled dist/ build
npm start # same as `node bin/fastcoder.js`Troubleshooting install
If npm install (or npm i -g fastcoder) fails while building
better-sqlite3 with an error like make: cc: No such file or directory
or prebuild-install warn install No prebuilt binaries found, the native
SQLite addon is falling back to compiling from source because no prebuilt
binary matches your Node/OS combination — and the system has no C compiler.
Fixes (pick one):
Use a Node version with a prebuilt binary (cheapest). Switch to Node 22 LTS or Node 24 LTS:
nvm install 22 && nvm use 22 npm i -g fastcoderInstall the build toolchain so the source compile succeeds:
- Linux / WSL (Debian/Ubuntu):
sudo apt-get update && sudo apt-get install -y build-essential python3 - macOS:
xcode-select --install - Windows (PowerShell as admin):
npm i -g windows-build-tools(or install "Desktop development with C++" from the Visual Studio Build Tools)
Then re-run
npm i -g fastcoder.- Linux / WSL (Debian/Ubuntu):
Lint & format
npm run lint:all # ESLint check, root + web-ui
npm run lint-fix:all # ESLint --fix, root + web-ui
npm run cs-check # Prettier --check
npm run cs-fix # Prettier --write
npm --prefix web-ui run typecheck # tsc -b, no emitSee .docs/guidelines/code-linting.md for the full script table, config
locations, and severity policy.
Quick start
fastcoder register --name my-app --path /path/to/repo
fastcoder list
fastcoder check
fastcoder serve # http://localhost:4787
fastcoder cron:install # every 15 minutes by default (macOS/Linux/WSL/Windows)
# first checks `claude` is runnable from a cron-like
# environment (warn-only; skip with --no-preflight)
fastcoder run my-app "git status"Run without arguments for an interactive menu:
fastcoderTask workflow
Fastcoder drives coding tasks through a set of stage folders inside each
project's tasks folder (default .docs/tasks/; change the name via the
TASKS_FOLDER variable in .env or in the global settings). Create the
structure once:
fastcoder init my-appThis creates tasks/{backlog,ai-backlog,specification,todo,doing,blocked,validate,test,document,done,failures,garbage,periodic}.
Drop a Markdown task file (one feature per file) into tasks/todo/ describing
what you want done.
init also installs the fastcoder Claude helpers into the project (skipping any
already present): the fastcoder-* slash commands into .claude/commands/, the
bundled periodic task files into <tasks-folder>/tasks/periodic/, and the
bundled skills flat at .claude/skills/<skill>/SKILL.md — the only layout
Claude Code discovers (currently fastcoder-usability, sonarqube-setup,
sonarqube-todo-tasks, fix-lint, makefile-setup and
ai-backlog-evaluation). The ai-backlog-evaluation skill scores each
ai-backlog/ idea and then discards, promotes, splits or parks it for human
approval; a promoted file is renamed {timestamp}_ai-promoted-{slug}[.step-1].md
with a fresh timestamp, so AI-promoted tasks stand out from hand-written ones. It
is driven by the bundled
005-ai-backlog-auto-evaluation(1Xday).md periodic task, which runs in
manage-tasks mode (see below).
init never overwrites an existing file, so it cannot bring an older project up
to date. Use update for that:
fastcoder update my-app # refresh the bundled fastcoder assets
fastcoder update my-app --dry-run # report what would change, write nothingIt refreshes the fastcoder-owned assets — the fastcoder-* commands and the
bundled skills (including their references/) — to the version shipped with the
installed fastcoder, creates any missing stage folder, and adds missing starter
templates. Project-owned files (periodic task files, makefile modules, the root
Makefile, the guidelines README.md/index.md) are only ever added, never
overwritten, and nothing is ever deleted. The same action is available in the
interactive project menu as "Update structure", as the "Update structure" button
on the web UI project page, and as "Update structure on all projects" on the
Settings page (POST /api/projects/:id/update and
POST /api/settings/tasks-folder/update).
It also seeds <tasks-folder>/guidelines/ with a README.md and an index.md
(one line per guideline, saying why it exists) — neither is overwritten on
re-init. The document step keeps both, and the project README, up to date; it
never writes changelogs or step logs.
Tasks flow through the stage folders in this order:
ai-backlog → backlog → specification → todo → doing (.do) → validate → doing (.validate) → test → doing (.test) → document → doing (.document) → donedoing/ is the single active-processing folder for every step. While a file
sits there its name carries a .{step} tag (.do, .test, .validate,
.document) recording which step owns it; the tag is stripped when the step
finishes and the file moves to the next queue folder. See
.docs/guidelines/fastcoder-flow.md for
the complete reference.
do — run the next ready task through the flow
fastcoder do my-app # scope the flow to one project
fastcoder do # global: span every projectdo runs one task through the complete fastcoder flow. Each invocation
tries every step in priority order and executes the first one that has work:
| Priority | Step | What it does |
| -------- | --------------- | ------------------------------------------------------------------------------------------------------------------------ |
| 1 | continue | Retries a task that failed on its first do attempt |
| 2 | specification | Turns a raw brief in specification/*.md into todo/*.step-N.md plans, and archives the brief to done/*.specified.md |
| 3 | do | Implements the next *.step-N.md from todo/ → moves to validate/ |
| 4 | validate | Validates a task from validate/ → moves to test/ |
| 5 | test | Writes/runs tests for a task from test/ → moves to document/ |
| 6 | document | Generates docs for a task from document/ → moves to done/ |
| 7 | periodic | Runs one due health-check task from periodic/ (stays in place) |
Periodic task files are permanent, read-only definitions — a run never writes
into them. Each run prepends a newest-first entry to
<tasks-folder>/logs/periodic-tasks-log.md (seeded by fastcoder init, never
overwritten on re-init).
By default a periodic run is report-only (it never fixes anything, it only files
ai-backlog entries). A periodic file that contains the line
Periodic-mode: manage-tasks opts into the second mode instead: the run may
create, move, rename and delete markdown files inside <tasks-folder>/tasks/,
but still must not touch source code or tests. The marker lives in the file
content — there is no CLI flag or setting — and the periodic file itself is
never moved or renamed in either mode.
Scheduling rule: project priority → step priority → project rotation
tie-break. Projects marked priority are served first: the whole step chain is
attempted against them, and only when none of their steps has work do the other
projects run — so a priority project's document task beats a normal project's
todo task, and while a priority project always has work the others stay
idle (intended; unprioritize it to let them resume). Inside a group, steps have
a fixed priority and the highest-priority step with work always runs; project
rotation only breaks ties between otherwise-equal projects. A paused project
never runs, priority or not, and the global pause stops every project at
once — toggle it from the Pause Fastcoder / Resume Fastcoder button in the
web UI header (Projects and Dashboard pages), the Global pause switch on the
Settings page, the CLI config menu, or the globalPause field on
GET/PUT /api/settings. See
.docs/guidelines/fastcoder-flow.md for
the full definition.
Technical blackout. A hold that stops every Claude-invoking step until a
chosen end time, leaving only the Claude-free unblock recovery running. Declare
or end it from the Technical blackout card on the web UI Settings page (end
date, reason, description, Save / Clear) or through
PUT/DELETE /api/settings/technical-blackout; the same card holds
TIMEOUT_FOR_ERRORS, the blackout length applied when Claude returns a service
error. fastcoder config prints the current blackout (or not set) and the
Timeout for errors: {n} min line for a terminal-only check. While one is
active — or while the global pause is on — a red banner at the top of every web
UI page says so. The blackout ends by itself at its end time. See
.docs/guidelines/technical-blackout.md.
Per-task model override. A task file can pin the Claude model it runs on by
adding a .{model} tag to its name — add-login.opus.step-1.md runs on opus
regardless of the configured default. {model} is one of sonnet, opus,
haiku, fable. The tag stays on the file through every stage, so the same
model handles the task's do, test, validate and document steps.
The do step itself:
- Runs a check pass to inspect task status.
- If the project has a task that failed on a previous
do, that task is retried first (see below). Otherwise it moves the first file intasks/todo/intotasks/doing/tagged.do. - Runs Claude Code against the task file in
tasks/doing/to implement it. - On success, strips the tag and moves the file to
tasks/validate/. - On failure, the file stays in
tasks/doing/and an exception is recorded —pendingwhen the run can be resumed (e.g. a usage limit was hit) orignoredwhen it cannot.
Failure retries. The next time the flow runs for a project with a failed
task, the continue step resumes that task to analyse and fix the failure.
If the retry succeeds the task moves to tasks/validate/; if it fails again
the task is moved to tasks/failures/ and the exception is marked
failed-solving.
Global do. With no project, fastcoder do considers every registered
project, using Priority scheduling with round-robin tie-breaking: it walks
projects starting from the one after the project executed last time, and stops
at the first step with work, executes it and exits — so a scheduled
fastcoder do makes steady progress across every project and every stage of
the flow.
check — inspect status and preview the next do
fastcoder check my-app # one project
fastcoder check # all projectscheck runs the project's configured check commands and reports task counts
per stage. It also lists, in flow priority order, the files that are ready to
execute in the next fastcoder do cycle — the one the next run will actually
pick is marked ▶ (runs next), and steps disabled in settings are shown with a
[step disabled] note:
report #1 my-app (project #1) — .docs found · files: 0 · checks run: 0 · tasks: … · next: do → add-login.step-1.md
Ready for next `do` cycle:
▶ do todo/add-login.step-1.md (runs next)
validate validate/fix-header.step-1.md
test test/cache-layer.step-1.md
periodic periodic/daily-project-health.mdexecutions — track Claude Code runs
Every Claude Code run started by do is recorded as an execution. An execution
is in-progress while Claude runs and becomes success or fail when it
ends, capturing token usage and remaining usage-limit headroom when available.
Each execution also records the account's last known Claude Code utilization at
the moment it started — the 5-hour and 7-day percentages, shown as
limits {short}%/{long}% in the list and a Limits: line in the detail view
(n/a when no snapshot is stored). The web UI shows the same pair in a Limits
column of the executions table (— for a missing side):
fastcoder executions # recent executions
fastcoder executions -p my-app -s in-progress # what is still running
fastcoder executions 42 # full detail of one executionExecution statuses: in-progress, success, fail.
If a run is killed with its host process (machine sleep, reboot, crash) its
in-progress row would otherwise stay open forever and keep blocking the flow.
Every flow tick reconciles such rows: after 60 minutes with no live Claude
process for that task, the execution is finished as fail with the reason
abandoned: process no longer running (the row is kept, never deleted).
continue — resume interrupted runs
fastcoder continue my-app # one project
fastcoder continue # every projectcontinue re-runs Claude Code for each pending exception. A resumed run that
completes flips the exception to solved and moves the task to tasks/test/;
a run that fails again flips it to failed-solving.
exceptions and logs — inspect what happened
Every do / continue step is recorded in an action log, and failed runs are
recorded as exceptions. Both can be listed and filtered:
fastcoder exceptions # all exceptions
fastcoder exceptions -p my-app -s pending # filter by project and status
fastcoder logs -p my-app -a do # logs for the `do` action
fastcoder logs --level error # only error-level logsException statuses: pending, solved, ignored, failed-solving.
Log levels: info, warn, error.
Web interface
fastcoder serve starts an Express server that exposes every application
feature as a JSON API under /api and serves a React single-page app for the
UI. The web app lives in web-ui/ (React 18 + Vite + TypeScript, PrimeReact
components, Tailwind CSS, React Router, TanStack Query for state, and
react-hook-form + zod for validation).
npm run build:web # install web-ui deps and build the SPA into web-ui/dist
fastcoder serve # serves the built SPA + API at http://localhost:4787The interface has a sidebar with menu navigation, a header and a footer, and
covers the whole feature surface: a Dashboard overview, full project CRUD, a
per-project workspace with a kanban-style task board (create / edit / move /
delete task files) and the do / check / continue / run-command actions,
plus pages for executions, exceptions, action logs, reports, the scheduled job
and global settings.
For UI development, run the API and the Vite dev server side by side — Vite
proxies /api to the running fastcoder serve:
fastcoder serve # API on :4787
npm --prefix web-ui run dev # Vite dev server on :4788, proxying /apiUsing make
A Makefile (with one sub-file per command group under ./make/) wraps the
CLI so you can drive everything with make:
make help # list every target
make install # npm install + tsc build
make build # tsc only
make build-web # build the web UI bundle (web-ui/dist)
make watch # tsc --watch
make dev # run CLI from TS via ts-node
make install-global # npm install -g . (auto-compiles)
make update-global # rebuild CLI + reinstall locally (--force)
make update-global-full # rebuild CLI + web UI + reinstall (--force)
make uninstall-global # npm uninstall -g fastcoder
make register NAME=my-app DIR=/path/to/repo
make list
make remove TARGET=my-app
make check # all projects
make check TARGET=my-app # one project
make reports LIMIT=10
make serve # default port 4787
make serve PORT=8080
make cron-install # */15 * * * * by default
make cron-install SCHEDULE="*/30 * * * *"
make cron-uninstall
make cron-status
make run TARGET=my-app CMD="git status"
make menu # interactiveSub-makefiles:
make/install.mk install, build, build-web, watch, dev, install-global, update-global, update-global-full, uninstall-global, clean, menu
make/npm.mk ci, build-all, deps-update, pack, publish, full-publish (npm package/release)
make/projects.mk register, list, remove
make/check.mk check, reports
make/serve.mk serve
make/cron.mk cron-install, cron-uninstall, cron-status
make/run.mk run<tasks-folder>/config.json example
Place this file inside a registered project (the folder defaults to .docs/,
override with TASKS_FOLDER or the global settings):
{
"checks": ["git status --porcelain", "npm outdated --json"]
}Each command is executed inside the project folder during fastcoder check,
and its stdout/stderr/exit code are stored in the report.
Releasing
- Bump the version in
package.json(e.g.npm version patch). - Run
make release— it tagsv<version>and pushes the tag toorigin. - The tag push triggers the
Publish npm packageGitHub Actions workflow (.github/workflows/publish-npm.yml), which builds, tests, and publishes the package to npm. - The workflow requires an
NPM_TOKENrepository secret with publish access to the package on npm.
Layout (TypeScript)
src/
cli.ts # entry: runs runMain() when invoked directly
program.ts # buildProgram() + runCommand() (commander wiring)
config.ts # paths/constants
cli/ # one file per CLI command (commander actions)
domain/ # Project, Report, TaskException, ActionLog, Execution (typed)
application/ # use-case services
actions/ # single-responsibility actions (run-claude-task)
infrastructure/ # better-sqlite3, fs, shell, system-cron, express
tsconfig.json # target ES2020, module commonjs, strict, outDir dist
dist/ # compiled output (created by `make build`)
bin/fastcoder.js # thin shim: require('../dist/cli').runMain()npm install runs the prepare script which compiles to dist/. Both
fastcoder (compiled) and make dev (via ts-node src/cli.ts) work.
