sinscribe
v1.4.1
Published
A git-centric developer-workflow assistant CLI: PR descriptions, commit messages, branch names, project context briefs, and AI agent context files.
Downloads
349
Maintainers
Readme
Your git workflow, written for you — from the terminal.
Sinscribe is a git-centric developer-workflow assistant CLI. It reads your
actual git state — diffs, branch, ticket, session context — and writes the
prose around it: PR/MR descriptions, commit messages, branch names,
project-context briefs, documentation with mermaid diagrams, and AI agent
context files (CLAUDE.md / AGENTS.md). It runs in your terminal as a
one-shot command or an interactive chat agent.
[!NOTE] Stable (v1.0.0). Sinscribe is in daily use and its CLI surface — commands, flags, env vars, and config layout — is now covered by semver. Install it globally with
npm install -g sinscribe, or from source for development.
Features
- PR/MR descriptions from your local changes vs the target branch, measured from the merge base up — so it works before you commit.
- Conventional Commit + Gitmoji messages from your staged changes.
- Branch names and task prompts for your AI coding agent, generated from a short description or ticket.
- Project understanding — a structured context brief, full documentation
with mermaid diagrams, and
CLAUDE.md/AGENTS.mdscaffolding, produced by an agent that explores the repo. - Interactive chat over the current repository.
- Per-branch sessions that capture business context (feature, ticket, requirements, target branch) and feed it to every generation.
- Customizable templates — six built-in house styles plus your own, with typed placeholders filled deterministically from git or by the model.
- Deterministic
--dry-runon every command: no model call, no credentials read — useful for previewing detection and in CI.
Prerequisites
- Node.js >= 20
- git
- An API key for a supported provider (OpenCode Go by default — see
Configuration). The
kiro-cliprovider needs no key.
Install
npm install -g sinscribe
sinscribe --helpOr from source, for development:
pnpm install
pnpm build
node dist/cli.js --help # or: pnpm sinscribe --help
# optional: pnpm link --global → `sinscribe` on your PATHQuick start
The first command needs no API key, which makes it a good way to check that Sinscribe reads your repository correctly before you configure anything:
cd your-repo
sinscribe pr --dry-run # detected branch, base, ticket, diff + scaffoldThen set a key and generate for real:
export OPENCODE_API_KEY=... # or run `sinscribe` and let it ask
sinscribe pr # draft a PR description from your local changes
sinscribe commit # Conventional Commit message from staged changes
sinscribe # interactive menu + chat over the current repoThe first interactive run asks for your provider API key and stores it in
~/.sinscribe/.env.
Commands
| Command | What it does |
| ----------------------- | ------------------------------------------------------------------------------- |
| sinscribe | Interactive chat agent + menu over the current repo |
| sinscribe pr | PR/MR description from local changes vs the target branch |
| sinscribe commit | Conventional Commit + Gitmoji message from staged changes |
| sinscribe branch | Branch-name suggestions from a description/ticket |
| sinscribe prompt | Copy-ready feature/bugfix task prompt for your AI coding agent |
| sinscribe context | Structured project-context brief (markdown or JSON) |
| sinscribe docs | Project documentation with mermaid diagrams |
| sinscribe agents | Generate/refresh CLAUDE.md + AGENTS.md from the repo |
| sinscribe agent-setup | Analyze the project and write specialized agent definitions to .claude/agents |
| sinscribe template | Manage the template library (list / show / add / edit / path) |
Command options
| Command | Option | Effect |
| ------------- | --------------------- | ----------------------------------------------------------------------------------- |
| pr | --template <name> | Template to use (default: andersoftware) |
| | --base <ref> | Target branch to diff against (default: session, else detected) |
| | --staged | Diff only staged changes (default: all local changes) |
| | --ticket <id> | Ticket ID (default: parsed from the branch name) |
| | --out <file> | Write the description to a file |
| prompt | --type <type> | feature or bugfix (default: inferred from the description) |
| | --out <file> | Write the prompt to a file |
| | --handoff | Also write HANDOFF.md without asking |
| commit | --all, -a | Use all tracked changes, not only staged |
| | --scope <scope> | Force the Conventional Commit scope |
| | --no-gitmoji | Skip the gitmoji prefix (it is on by default) |
| branch | --type <type> | feat|fix|chore|docs|refactor|test|perf|build|ci|hotfix |
| context | --out <file> | Write the brief to a file |
| | --format <md\|json> | Output format (default: md) |
| docs | --out <file> | Write the documentation to a file |
| agents | --target <t> | claude|agents|both (default: both) |
| | --update | Surgically refresh existing files |
| agent-setup | — | No options; the interactive flow asks what it needs |
| template | add --from <file> | Seed a new user template from an existing file |
branch takes a required ticket ID and/or description as positional arguments.
prompt takes an optional description; without one it falls back to the saved
session context.
Global options
Accepted by every command, and — because they are parsed in one pass over the whole command line — they may appear anywhere in it.
| Flag | Effect |
| ------------------- | ------------------------------------------------------------------ |
| --dry-run | Deterministic scaffold: no LLM call, no credentials read |
| -p, --print | One-shot non-interactive run, result on stdout (default off a TTY) |
| --model-id <id> | Model override for this run |
| --provider <name> | Provider override for this run (not persisted) |
| --api-key <key> | API key override for this run (not persisted) |
| -v, --version | Print the version |
| -h, --help | Show usage |
-p/--print is also selected automatically when stdin is not a TTY, so
sinscribe behaves correctly in a pipeline or a CI job.
Examples
# Pull requests
sinscribe pr --template github --base origin/main --out PR.md
sinscribe pr --base develop --staged # only staged changes, vs develop
sinscribe pr --dry-run # branch/ticket/diff detection + scaffold
# Commits & branches
sinscribe commit --scope api --no-gitmoji
sinscribe branch ABC-123 add retry logic to uploader # → feat/ABC-123-... suggestions
# Prompts & project understanding
sinscribe prompt --type bugfix uploader crashes on empty files
sinscribe prompt --handoff -p "add retry logic" # also writes HANDOFF.md
sinscribe context --format json --out context.json
sinscribe agents --target claude --update
# Templates
sinscribe template list
sinscribe template show andersoftware
sinscribe template path
# Chat & per-run overrides
sinscribe -p "what changed on this branch?"
sinscribe pr --provider anthropic --api-key sk-ant-...Ticket IDs (ABC-123, #42) are auto-detected from the branch name for pr
and from the input for branch. When a branch session exists, its
feature/ticket/requirements are fed to the model as business context.
Session handoff (HANDOFF.md)
A prompting session normally ends with the useful part — what was decided,
what is still open — only in your head. After you approve a prompt,
sinscribe prompt offers to write a HANDOFF.md at the repo root: a
snapshot of where the branch stands, not an accumulated log.
## Where things stand
## What was done this session
## Key decisions
## Open questions
## Next steps
## Known issues / blockersThe next sinscribe prompt on that branch reads the file back and feeds it to
the model, so a second iteration starts warm instead of re-deriving settled
ground. A handoff written on a different branch is still passed along, but
labeled as such rather than presented as the current state.
--handoff writes the file without asking — the only route in -p/--print
and other non-TTY runs, which cannot ask. The file is yours to commit or
ignore; Sinscribe never adds it to .gitignore.
Configuration
On first interactive run, Sinscribe asks for your provider API key and stores it
in ~/.sinscribe/.env (directory 0700, file 0600). Real environment
variables always win over the file, and nothing secret is ever printed.
# ~/.sinscribe/.env (all optional; created by the CLI)
SINSCRIBE_PROVIDER="opencode-go" # opencode-go | openrouter | baseten | fireworks | openai | openai-compatible | anthropic | kiro-cli
SINSCRIBE_MODEL_ID="kimi-k2.7-code" # default model for the provider
OPENCODE_API_KEY="..."
ANTHROPIC_API_KEY="..." # if you switch to anthropic
# (kiro-cli needs no key — see below)
SINSCRIBE_TICKET_PATTERN="(T-\d+)" # optional custom ticket regex
SINSCRIBE_THEME="ayu-dark" # TUI color theme (set from the menu's Theme picker)
SINSCRIBE_REDUCED_MOTION="1" # freeze the loading animation (the timer keeps counting)Environment variables
| Variable | Purpose | Default |
| ---------------------------- | ------------------------------------------------------------------------------ | --------------------------------- |
| SINSCRIBE_PROVIDER | Which provider to use | opencode-go |
| SINSCRIBE_MODEL_ID | Model for all runs | the provider's first listed model |
| SINSCRIBE_TICKET_PATTERN | Custom ticket regex; the first capture group is used | ABC-123, then #123 |
| SINSCRIBE_THEME | Persisted TUI color scheme | shipped default |
| SINSCRIBE_REDUCED_MOTION | 1 or true renders a static spinner frame; the elapsed timer keeps counting | off |
| SINSCRIBE_DEBUG | 1 prints provider/model/thread lines to stderr | off |
| OPENCODE_API_KEY | Key for opencode-go — note the name has no GO | — |
| OPENROUTER_API_KEY | Key for openrouter | — |
| BASETEN_API_KEY | Key for baseten | — |
| FIREWORKS_API_KEY | Key for fireworks | — |
| OPENAI_API_KEY | Key for openai | — |
| ANTHROPIC_API_KEY | Key for anthropic | — |
| ANTHROPIC_BASE_URL | Base-URL override for anthropic | SDK default |
| OPENAI_COMPATIBLE_API_KEY | Key for openai-compatible | — |
| OPENAI_COMPATIBLE_BASE_URL | Required for openai-compatible — it has no default | — |
| EDITOR / VISUAL | Editor opened by template edit | vi |
| NO_COLOR | Disables color and the terminal background control | color on |
Resolution order for provider, model, and key: a per-run flag
(--provider / --model-id / --api-key), then the environment, then
~/.sinscribe/.env, then the built-in default. Per-run flags are never
persisted.
The default provider is OpenCode Go (an OpenAI-compatible endpoint at
https://opencode.ai/zen/go/v1) with Kimi K2.7 Code as the default model —
set OPENCODE_API_KEY and you're done. Other models on the same plan:
glm-5.2, glm-5.1, kimi-k2.6, deepseek-v4-pro, deepseek-v4-flash,
mimo-v2.5, mimo-v2.5-pro.
You can switch provider/model per run with --provider / --model-id /
--api-key, or persist a new choice from the TUI's AI settings item — which
also has a Test connection step that calls the provider's GET /models
endpoint (free, no tokens) to verify the key and model before saving.
Provider support
| Provider | Status |
| -------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| opencode-go | Recommended — the default; supported and regularly tested |
| kiro-cli | Recommended — drives AWS's official Kiro CLI; single-shot commands only |
| openrouter, anthropic, openai, baseten, fireworks, openai-compatible | Selectable — not actively maintained or regularly tested |
[!WARNING] Only the recommended providers (OpenCode Go and Kiro CLI) are exercised regularly. The others ship as-is and may lag behind their vendors' API changes — verify one with Test connection before relying on it.
Every provider except kiro-cli supports the full command set. kiro-cli is
limited to pr, commit, branch, and prompt; see below.
Amazon Q Developer setup (kiro-cli)
Use the Amazon Q subscription you already have, through AWS's own CLI:
- Install Kiro CLI —
brew install kiro-cli, or see kiro.dev/docs/cli — and runkiro-cli loginonce (IAM Identity Center, Builder ID, Google and GitHub all work). - Set
SINSCRIBE_PROVIDER=kiro-cli, or pick Amazon Q Developer (Kiro CLI) in the TUI's AI settings. There is no key to enter and nothing is stored:kiro-cliowns its own sign-in. - Run
pr/commit/branch/promptas usual.
Pick a model with --model-id or in the settings wizard; the labels carry
each model's credit multiplier, e.g. qwen3-coder-next (0.05x) up to
claude-sonnet-4.5 (1.30x). auto (the default) lets Kiro choose.
Why a subprocess and not the API? AWS restricts Q subscriptions to
approved applications: a third-party client that registers itself is
refused with AccessDeniedException: "Your subscription does not support
this application" however correct its request is. Rather than impersonate
an approved client, Sinscribe drives the official one — the approved client
makes the call, as itself — which also means the wire format stays AWS's
responsibility rather than something we reverse-engineer.
Tools are off, by construction. Sinscribe runs kiro-cli chat with a
generated agent that declares "tools": [], so the model can write text but
has no tool to touch your working tree — that is what keeps pr/commit/
branch/prompt single-shot. (The --trust-tools= flag does not do
this: it only governs auto-approval, and was verified to still let the model
read the filesystem.) The agent config lives under ~/.sinscribe/kiro-agent/
and never touches your own Kiro agents.
Limitation: agentic commands (context/docs/agents/agent-setup/chat) need
tool calling and exit with a clear message asking you to switch providers.
Reliability
- Every model call has a 120 s inactivity timeout (and a 10-minute overall cap for single-shot commands); a stalled connection reports a clear network error — with automatic retries on the single-shot path — instead of freezing the CLI.
- Ctrl+C always exits, and the process force-exits after finishing its work, so a lingering SDK socket can never hang the terminal.
- git subprocesses are capped at 30 s with
GIT_TERMINAL_PROMPT=0, so a credential or GPG prompt fails fast instead of blocking forever.
Templates
Templates are Markdown files with YAML frontmatter and typed {{placeholder}}
slots. There are three tiers; a later tier overrides an earlier one by name:
- Built-in (shipped):
andersoftware(default — Conventional Commits + Gitmoji title with full review sections),github,google,kubernetes,shopify,stripe - User:
~/.sinscribe/templates/*.md - Project:
<repo>/.sinscribe/templates/*.md
---
name: myteam
kind: pr
placeholders:
ticket: { type: string, required: true, from: branch } # filled from git, deterministic
summary: { type: markdown, required: true, from: llm } # produced by the model
changes: { type: list, required: true, from: llm } # rendered as bullets
---
## [{{ticket}}] {{summary}}from: git|branch slots are filled deterministically (also in --dry-run);
from: llm slots are requested from the model as validated JSON. Manage the
library with sinscribe template list | show | add | edit | path.
Sessions
The menu (bare sinscribe) is context-first: on a branch with no saved
context it opens straight into the context form, and the "Create PR
description", "Create branch name", and "Create feature or bugfix prompt" items
ask for a context before they run. A session captures business context per
branch — feature description, ticket ID, requirements, and the target branch
it merges into — stored in <repo>/.sinscribe/sessions/<branch>.json.
prdescribes your local changes vs the target branch, from the merge base up — so it works before you commit, and commits that landed on the target after you branched don't pollute the diff. By default it includes all tracked changes (staged + unstaged);--stagednarrows it to the index. On the next run for the same branch it enters update mode, revising the previous description with the fresh diff instead of starting over.- The target branch is resolved in order:
--base <ref>, then the session's saved target, then auto-detection (origin/HEAD,origin/main,origin/master,origin/develop,main,master,develop). - Create branch name generates suggestions from the session context and
creates the branch from the target (
git checkout -b <name> <target>), migrating the session soprworks there immediately. Once the branch differs from its target, the item becomes Rename branch (git branch -m).
Session files live beside the project template tier, so .sinscribe/.gitignore
ignores only sessions/ — .sinscribe/templates/ and .sinscribe/rules.md
stay committable.
Project rules
Free-text rules appended to every command's system prompt. Unlike templates, the two tiers are additive — both apply, each labeled by origin:
- Personal:
~/.sinscribe/rules.md— applies in every repository - Project:
<repo>/.sinscribe/rules.md— applies here, meant to be committed
Edit them from the menu's Project rules item. --dry-run reports which tiers
are active and how large they are, without sending them anywhere.
Troubleshooting
| Message | Cause and fix |
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| Not inside a git repository. | Every command except template needs a repo. cd into one. |
| Could not detect a target branch (tried origin/HEAD, origin/main, …) | No conventional default branch resolved. Pass --base <ref>, or save a target in the session context. |
| No local changes vs <ref>. Nothing to describe. | The branch matches its base. Commit or edit something, or check that --base points where you think. |
| No staged changes vs <ref>. | You passed --staged with an empty index. git add, or drop the flag. |
| Nothing staged. Stage changes with git add, or pass --all… | commit reads the index by default. Use -a for all tracked changes. |
| <KEY> is required to run sinscribe with <Provider>. | No API key for the selected provider. Set it in the environment, in ~/.sinscribe/.env, or pass --api-key. |
| Credentials are required for non-interactive runs. | -p/--print and non-TTY runs cannot open the setup wizard. Set the key in the environment first. |
| The <Provider> provider supports pr/commit/branch/prompt only for now… | You asked an agentic command of a provider without tool calling — today that means kiro-cli. Switch with --provider or SINSCRIBE_PROVIDER. |
| Template not found: <name>. Available pr templates: … | Typo, or the template is in a tier that is not being read. Check sinscribe template path and sinscribe template list. |
| Template <name> is a commit template, not a pr template. | A user or project template shadows a built-in of the same name with a different kind. Rename one of them. |
| Template <name> requires a ticket ID, but none was found… | The template has a required from: branch slot. Pass --ticket <id> or rename the branch. |
| Model did not produce a commit subject. / invalid JSON | The model broke format. pr and branch retry once automatically; otherwise re-run, or try another model with --model-id. |
| git … timed out after 30s — a credential or GPG prompt may be blocking. | A git subprocess is waiting on hidden input. Unlock your key, or configure a non-interactive credential helper. |
| Model call timed out — … | No output for 120 s, or 10 minutes total on a single-shot command. Usually a stalled connection; the single-shot path retries on its own. |
| Unknown option for <cmd>: <flag> (or Unknown option: <flag>) | The flag is not accepted there. sinscribe --help lists every command's options; note there is no per-command help. |
Two tools worth reaching for first:
sinscribe <cmd> --dry-run # what was detected, with no model call
SINSCRIBE_DEBUG=1 sinscribe <cmd> # provider, model, and thread on stderrHow it works
pr/commit/branch/promptare single-shot: the CLI computes the diff and context locally and makes one model call — the model never touches your repo. (Branch creation/rename is a plain git call the CLI makes after you pick a name; the model only suggests names.)context/docs/agents/agent-setup/ chat are agentic: a deepagents loop with read tools (and, for the write commands, scoped writes) rooted at the repository.- Sinscribe fails gracefully outside a git repository, strips every API key from the environment its agent's shell receives, and keeps secrets out of all output and logs.
Built with Ink,
LangChain /
LangGraph, and
deepagents. See
docs/ARCHITECTURE.md for the internals and
DESIGN.md for design decisions.
Development
pnpm dev pr --dry-run # run from source (tsx); note: no "--" separator
pnpm test # vitest
pnpm lint:check && pnpm format:check
pnpm buildCI (.github/workflows/ci.yml) runs the same checks
on push and PR, on Node 20 and 22. Setup, quality gates, house conventions, and
the release process live in
docs/CONTRIBUTING.md.
Why I built this
I basically live in the terminal, and lately it feels like every other tool shipping in tech is a CLI. I wanted to see what it actually takes to build one today — so I made something I'd use every day: a little assistant that shaves friction off my real workflow. The Pragmatic Programmer puts it well: invest in your tools, sharpen them, and let them make you faster. This is me taking that advice literally.
Credits
Inspired by openwiki, whose agentic-CLI skeleton (provider abstraction, config/secrets layer, agent loop) gave Sinscribe its starting point. The domain — git workflows, templates, and per-branch sessions — is Sinscribe's own.
