sidecar-cli
v0.1.6
Published
Local-first project memory and recording tool
Readme
[■]─[▪] sidecar
project memory for your work
Sidecar CLI
Sidecar is a local-first, CLI-first project memory and recording tool for human developers and AI coding agents.
Documentation website: usesidecar.dev
Why Sidecar exists
- Keep project memory structured and local.
- Make session handoffs easier for humans and agents.
- Record decisions, work logs, tasks, notes, sessions, and artifacts in one stable CLI.
- Generate deterministic context and summary outputs from local project data.
Install
Install globally (stable):
npm install -g sidecar-cliInstall beta:
npm install -g sidecar-cli@betaInstall rc:
npm install -g sidecar-cli@rcHomebrew:
Homebrew automation exists in the release workflow, but the public tap/formula is not currently published. For now, install via npm.
Or run without install:
npx sidecar-cli --help
npx sidecar-cli@beta --helpRequirements:
- Node.js 22+ (Sidecar uses the built-in
node:sqlitemodule) - npm
Install dependencies:
npm installBuild:
npm run buildRun locally in dev mode:
npm run dev -- --helpRelease channels
Sidecar ships features through beta → rc → stable as version bumps. There are no per-feature channel flags.
latestis the promoted stable CLIrcis the current release-candidate line when one is openbetais the current preview line
To see what is actually published right now from your machine:
sidecar release statusThe optional UI is installed on demand by sidecar ui. Its source lives in packages/ui, but it is not currently published as a standalone npm package.
Per-version release notes live at github.com/karlhills/sidecar-cli/releases.
Quick start
- Initialize in a project directory:
sidecar init- (Optional) Define shared instruction templates once:
mkdir -p ~/.sidecar-cli/instructionsCreate template files such as:
~/.sidecar-cli/instructions/web-app.md~/.sidecar-cli/instructions/desktop-app.md
- Initialize with a shared template (writes project
instructions.md):
sidecar init --instructions-template web-appOr load directly from a specific file:
sidecar init --instructions-file /absolute/path/to/instructions.mdNotes:
--instructions-template <name>resolves to~/.sidecar-cli/instructions/<name>.md.- Use either
--instructions-templateor--instructions-file(not both). - If
instructions.mdalready exists, Sidecar will not overwrite it unless--forceis used.
This creates:
.sidecar/sidecar.db.sidecar/config.json.sidecar/preferences.json.sidecar/AGENTS.md.sidecar/summary.mdAGENTS.md(repo root)CLAUDE.md(repo root)instructions.md(repo root, only when--instructions-templateor--instructions-fileis provided)
Use --force to overwrite Sidecar-managed files.
Two namespaces: log and work
Sidecar is one CLI that does two jobs: capture local project memory, and run AI coding agents against that memory. Each job has a namespace:
sidecar log <cmd>— memory:worklog,decision,note,recent,context,summary,session,event,artifactsidecar work <cmd>— runner:task,run,prompt,hooks
The underlying verbs still work directly, so sidecar worklog record ... and sidecar log worklog record ... are equivalent. Pick the form that reads best in your workflow.
sidecar log worklog record --done "..." --files src/a.ts
sidecar log decision record --title "..." --summary "..."
sidecar log context --format markdown
sidecar log summary refresh
sidecar work task create --title "..."
sidecar work run T-001
sidecar work prompt compile ./prompt.yamlRun sidecar log --help or sidecar work --help for the full listing.
Core commands
Global:
sidecar init [--force] [--name <project-name>] [--instructions-template <name>] [--instructions-file <path>] [--json]sidecar demo [--cleanup] [--json]sidecar status [--json]sidecar preferences show [--json]sidecar ui [--no-open] [--port <port>] [--install-only] [--project <path>] [--reinstall]sidecar capabilities --jsonsidecar event add ... [--json]sidecar export [--format json|jsonl] [--output <path>]sidecar help
Runner and prompts:
sidecar run <task-id> [--runner codex|claude|codex,claude] [--agent-role <role>] [--dry-run] [--json]sidecar run replay <run-id> [--runner <r>] [--agent-role <role>] [--reason <text>] [--edit-prompt] [--dry-run] [--json]sidecar run list [--task <task-id>] [--json]·sidecar run show <run-id> [--json]sidecar run queue [--json]·sidecar run start-ready [--dry-run] [--json]sidecar prompt compile <task-or-file> [--runner <r>] [--agent-role <role>] [--budget <n>] [--section-policy ...] [--explain] [--format json] [-o <path>]sidecar hooks print·sidecar hook <session-start|session-end|file-edit|user-prompt> [--actor-name <name>] [--json]
Context and summary:
sidecar context [--limit <n>] [--format text|markdown|json] [--json]sidecar summary refresh [--limit <n>] [--json]sidecar recent [--type <event-type>] [--limit <n>] [--json]
Notes, decisions, worklogs:
sidecar note "<text>" [--title <title>] [--by human|agent] [--session <id>] [--json]sidecar decision record --title <title> --summary <summary> [--details <details>] [--by human|agent] [--session <id>] [--json]sidecar worklog record --done <summary> [--goal <goal>] [--files a,b] [--risks <text>] [--next <text>] [--by human|agent] [--session <id>] [--json]
Tasks:
sidecar task create --title "<title>" --summary "<summary>" --trigger "<condition>" --entry-points <path1,path2> --done-condition "<done>" --validate-cmd "<cmd>" [--trigger-check <command>] [--depends-on <task-ids>] [--status active|blocked|done] [--priority low|medium|high] [--json]sidecar task set-status <task-id> --to active|blocked|done --reason "<text>" [--by human|agent] [--session <id>] [--json]sidecar task list [--status active|blocked|done|all] [--json]
Sessions:
sidecar session start [--actor human|agent] [--name <actor-name>] [--json]sidecar session end [--summary <text>] [--json]sidecar session current [--json]sidecar session verify [--json]sidecar doctor [--json](alias)
Artifacts:
sidecar artifact add <path> [--kind file|doc|screenshot|other] [--note <text>] [--json]sidecar artifact list [--json]
Validation and auto-approve
Queue tasks now store one required validation_command that agents run to
verify the done condition.
sidecar task create \
--title "Add import flow" \
--summary "..." \
--trigger "After T-012 lands" --depends-on T-012 \
--entry-points src/cli.ts,src/tasks/task-packet.ts \
--done-condition "Task packets persist in active/blocked/done folders" \
--validate-cmd "npm run build"Auto-approve on all-green
When every validation step passes for a run, Sidecar can auto-approve the run so you don't have to click through the review queue for a strictly-green outcome. It's opt-in:
// .sidecar/preferences.json
{ "review": { "autoApproveOnAllGreen": true } }Behavior when enabled:
- The run's
review_stateflips toapproved,reviewed_byis set tosidecar:auto, and the review note records how many steps passed. - Runs with zero configured validation steps are not auto-approved — a runner-only success still requires a human click.
- Any failing step blocks the run as today (task moves to
blocked, blocker message includes the kind).
Dual-runner pipelines
Pass a comma-separated list to --runner and Sidecar runs each runner sequentially on the same task, feeding the previous run's summary into the next runner's compiled prompt as linked context.
sidecar run T-001 --runner codex,claude
sidecar run T-001 --runner codex,claude --agent-role builder-app --dry-runEach step produces its own run record linked back to the first run in the pipeline via parent_run_id. The CLI prints a one-line summary per step (runner, agent role, run id, status, duration, changed file count), and the full pipeline envelope is available with --json. Use this to pair a planner/builder runner with a reviewer runner, or to compare runners head-to-head on the same task.
Replay a run
When a run finishes (whether ok, blocked, or failed) you can kick off a fresh run with the same task and a link back to the original via sidecar run replay. It's the fastest way to try a different runner, re-run after fixing a blocker, or fork a green run into an experiment without losing the audit trail.
sidecar run replay R-001 --reason "retry with claude after codex blocker"
sidecar run replay R-001 --runner claude --agent-role builder-app --edit-prompt
sidecar run replay R-001 --reason "dry-run with new validation" --dry-runFlags:
--runner codex|claude— override the parent's runner (defaults to parent's runner).--agent-role <role>— override the parent's agent role.--reason "<text>"— stored on the new run asreplay_reason; surfaced in CLI and UI.--edit-prompt— opens the compiled prompt in$VISUAL/$EDITORbefore the runner starts so you can tweak it.--dry-run— compile the prompt and create the run record without executing the runner.--json— machine-readable envelope output.
The new run record carries parent_run_id: "R-001" plus your replay_reason. sidecar run show <id> renders the lineage both ways:
- The parent shows Replayed as: with each child run id.
- Each child shows Replay of: with the parent id and reason.
The UI's Run Detail panel mirrors this: Replay of: links back to the parent; Replays: lists every child with its status. Both are clickable for one-hop navigation through a lineage.
Ambient capture via Claude Code hooks
Sidecar can capture an ongoing Claude Code session into worklog/session records without any explicit sidecar worklog record calls. Once you wire Claude Code's hook system to sidecar hook <event>, every session start, file edit, and session end flows into your project memory automatically.
Print the ready-to-paste settings block:
sidecar hooks printPaste the hooks object into .claude/settings.json (project) or ~/.claude/settings.json (user). Claude Code merges hook arrays across scopes, so user-level and project-level hooks compose.
The template wires four events:
| Claude Code event | Sidecar hook | Effect |
| ------------------ | ---------------------- | ------------------------------------------------------------------ |
| SessionStart | sidecar hook session-start | Opens a Sidecar session (actor=agent, name claude-code:<sid>). Idempotent. |
| SessionEnd | sidecar hook session-end | Closes the active session. Safe to call when none is open. |
| PostToolUse (Edit|Write|MultiEdit|NotebookEdit) | sidecar hook file-edit | Records a worklog "Edited <path> via <tool>" and links the file as an artifact. Lazy-opens a session if none is active. |
| UserPromptSubmit | sidecar hook user-prompt | Records the first 200 chars of the prompt as a note. |
Each hook:
- Reads its payload JSON from stdin (Claude Code supplies it automatically).
- Always exits 0 — hooks never block the caller, even on internal errors.
- Accepts
--actor-name <name>to override the default actor name. - Accepts
--jsonto emit a structured envelope (useful when testing).
Quick manual smoke test:
echo '{"session_id":"abc"}' | sidecar hook session-start
echo '{"tool_name":"Edit","tool_input":{"file_path":"'"$PWD"'/README.md"}}' | sidecar hook file-edit
sidecar hook session-end
sidecar recent --type worklog --limit 3Codex users can invoke the same CLI from any shell hook or wrapper script — the payload schema is permissive, so a minimal {"tool_input":{"file_path":"..."}} works.
Automatic prompt token budgeting
When Sidecar compiles task prompts (sidecar prompt compile and run execution flows), it automatically applies a token budget to reduce context size without degrading execution quality.
Current behavior:
- Keeps required sections intact (task, objective, constraints, validation, definition of done).
- Deduplicates repeated list items.
- Trims only optional high-volume sections when needed (for example: in-scope lists, linked notes/decisions, long file lists).
- Adds compact overflow lines such as
+ N more ... (see task packet for full list).
Current defaults:
- Target budget: ~1200 estimated tokens
- Safety ceiling: ~1500 estimated tokens
Prompt optimization data is included in compile output and stored on run records:
prompt_tokens_estimated_beforeprompt_tokens_estimated_afterprompt_budget_targetprompt_trimmed_sections
Freestanding prompt specs
sidecar prompt compile also accepts a .yaml/.yml/.json spec file — no TaskPacket required. This lets you iterate on prompts directly, or compose them programmatically from another tool.
sidecar prompt compile ./my-prompt.yaml
sidecar prompt compile ./my-prompt.yaml --explain
sidecar prompt compile ./my-prompt.yaml --budget 1500 --budget-max 2000
sidecar prompt compile ./my-prompt.yaml --section-policy notes=drop,examples=trim-last
sidecar prompt compile ./my-prompt.yaml -o out.md
sidecar prompt compile ./my-prompt.yaml --format jsonSpec schema:
header:
- "# My Prompt"
- "Optional preamble rendered verbatim."
sections:
- id: objective # optional — auto-slugified from title if omitted
title: Objective
required: true # forces policy=keep (never trimmed or dropped)
content: | # text section: string or string[]
Describe the goal in one or two sentences.
- title: Notes
list: # list section
- First note
- Second note
- Third note
empty_placeholder: "- no notes yet"
trim:
policy: trim-last # keep | trim-last | drop
limit: 2 # target-pass cap
limit_strict: 1 # safety-valve cap
overflow_label: notes # renders "+ N more notes (see task packet for full list)"
budget:
target: 1200 # soft target
max: 1500 # hard ceiling before strict pass
policy_overrides:
notes: keep # override per-section trim policies by idTrim policies:
keep— never trim or drop (default for text sections andrequired: true)trim-last— applylimiton the target pass,limit_stricton the strict pass (lists only)drop— remove the whole section on the strict pass when still over budget
--explain prints a per-section trace (policy applied, tokens, items kept/total) to stderr. --format json emits the standard envelope with markdown + full metadata.sections trace for programmatic use.
Example workflow
sidecar context --format markdown
sidecar session start --actor agent --name codex
sidecar decision record --title "Use SQLite" --summary "Local-first persistence"
sidecar worklog record --goal "init flow" --done "Implemented schema and command surface" --files src/cli.ts,src/db/schema.ts
sidecar task create --title "Add integration tests" --summary "Add integration coverage for init flow" --trigger "When test matrix is finalized" --entry-points src/cli.ts --done-condition "Integration suite covers init flow" --validate-cmd "npm test" --priority medium
sidecar summary refresh
sidecar session end --summary "Initialization and recording flow implemented"AI agent usage
Sidecar generates .sidecar/AGENTS.md during init.
This repo also includes a root AGENTS.md so the policy is visible before any .sidecar lookup.
Required minimum for any code change:
sidecar context --format markdownsidecar worklog record --done "<what changed>" --files <paths> --by agent- if behavior/design changed:
sidecar decision record ... - if follow-up exists:
sidecar task create ... sidecar summary refresh
Optional local enforcement:
npm run install:hooksThis is optional and per-repository clone. sidecar init does not install git hooks automatically.
This installs a pre-commit guard that checks staged non-doc code changes. If staged code changes are present, commit is blocked unless both are recorded since the last commit:
- a
worklogevent - a
summary refreshevent
The guard command is:
npm run sidecar:reminder -- --staged --enforce
If a pre-commit hook already exists, Sidecar will not overwrite it unless you run:
npm run install:hooks -- --forceAgents can discover the CLI surface programmatically with:
sidecar capabilities --jsonRepo policy
When changes are made in this repo, document them in Sidecar:
sidecar context --format markdownsidecar worklog record --done "<what changed>" --files <paths> --by human|agentsidecar decision record ...when behavior/design changessidecar task create ...for follow-up worksidecar summary refresh
Local storage details
All data is local in .sidecar/sidecar.db (SQLite).
Primary tables:
projectseventstaskssessionsartifacts
No network dependency is required for normal operation.
Optional local UI
sidecar ui launches a local browser UI for the selected Sidecar project.
Lazy-install behavior:
sidecar uiresolves the nearest.sidecarproject root (or uses--project).- Sidecar checks for
@sidecar/uiin~/.sidecar/ui. - If missing/incompatible, Sidecar installs or updates it automatically.
- Sidecar starts a local UI server and opens the browser (unless
--no-open).
UI runtime location:
~/.sidecar/ui- the CLI installs
@sidecar/uihere (not in your project repo)
Version compatibility rule:
- CLI and UI must share the same major version.
- If majors differ,
sidecar uiauto-reinstalls/updates UI.
Common examples:
sidecar ui
sidecar ui --no-open --port 4311
sidecar ui --install-only
sidecar ui --project ../other-repo
sidecar ui --reinstallUI screens:
- Mission Control: open tasks, ready queue, runs in flight, pipelines, and run review queue — the main operational surface.
- Run Detail: compiled prompt and runner log viewers, typed validation results (kind badges, exit code, duration, snippet), replay lineage (
Replay of:/Replays:), and auto-approval markers. - Overview: project info, active session, recent decisions/worklogs, open tasks, recent notes, and counts.
- Timeline: paginated event stream (load-more).
- Tasks and Decisions: list views with summary and timestamps.
- Preferences: edit
.sidecar/preferences.jsonand view.sidecar/summary.md.
UI write support:
- Add notes and open tasks from Overview.
- Trigger
run replaywith optional reason, runner override, and agent-role override from Run Detail. - Edit
.sidecar/preferences.jsonfrom Preferences.output.humanTimecontrols timestamp style in human-readable CLI output:true: friendly local times (for example3/18/2026, 11:51 AM)false: raw ISO-style timestamps
review.autoApproveOnAllGreen— auto-approve runs when every validation step passes (see "Validation kinds and auto-approve").
See packages/ui/UI.md for the full UI reference (state model, views, modals, HTTP API, accessibility).
JSON output
Most commands support --json and return structured output:
okcommanddataerrors
This makes Sidecar easy to automate from scripts and AI agents.
Integration API
Sidecar CLI is the first integration API for scripts, agents, and local tooling.
Standard JSON envelope:
{
"ok": true,
"version": "1.0",
"command": "task create",
"data": {},
"errors": []
}Failure envelope:
{
"ok": false,
"version": "1.0",
"command": "task create",
"data": null,
"errors": ["..."]
}Generic event ingest:
sidecar event add --type decision --title "Use SQLite" --summary "Simple local storage for v1" --created-by agent --source cli --json
sidecar event add --json-input '{"type":"note","summary":"Captured context","created_by":"agent"}' --json
cat event.json | sidecar event add --stdin --jsonCapabilities metadata:
sidecar capabilities --jsonIncludes:
cli_versionjson_contract_versionfeatures- command and option metadata
Export project memory:
sidecar export --format json
sidecar export --format json --output ./exports/sidecar.json
sidecar export --format jsonl > sidecar-events.jsonlJSONL note:
- JSONL export currently emits events only, one JSON object per line.
- each JSONL line includes
"version": "1.0"and"record_type": "event".
Release and distribution
See RELEASE.md for publishing/release details. See CONTRIBUTING.md for code structure and contribution guidelines.
