Minimal, responsive, transparent LLM coding assistant. Not for vibe coding.
Website and getting-started guide: yact.dev
Not affiliated with Anthropic. YACT is an independent, open-source, third-party tool. It is not made, endorsed, or supported by Anthropic PBC. "Claude" and "Anthropic" are trademarks of Anthropic PBC; they are used here only to describe which API this tool calls. For Anthropic's own products and documentation, go to anthropic.com and docs.claude.com.
- Design principles
- How YACT works
- Installation
- Quick start
- The prompt file
- Prompt commands
- Plan, then act
- Commands
- Global flags
- Configuration
- Storage
- Running YACT in GitHub Actions
- Troubleshooting
- Security and privacy
- Contributing
- Assisted, not autonomous. YACT supports AI assisted software development, not AI driven software development. You decide what goes into the context and what the model is asked to do.
- Responsive. Each command is a single API call, so you get results in seconds rather than waiting on a multi-step agent loop.
- Reactive. YACT does what it is explicitly asked to do, and nothing more. It never explores your codebase or runs commands on its own initiative.
- Transparent. The full task prompt lives in a plain Markdown file you can read and edit, and every response is logged verbatim.
YACT is a CLI tool, invoked as y. You assemble a task by hand, send it once, and get one
response back.
The task lives in a plain Markdown file, yact-prompt.md, in the root of your project. It
holds your instructions (or a plan) and links to the files you want to send. When you
run a prompt command such as ask, plan or act, YACT:
- builds one request from the prompt file, the linked files and a few optional message files,
- sends it to Claude with the system prompt that belongs to the command,
- saves the raw response to
.yact/temp/buffer.txt, - and then, depending on the command, prints the response, saves it as a plan, or writes the files it contains to disk.
There is no tool calling, no MCP and no multi-turn loop.
YACT asks the model to emit complete source files rather than incremental edits. This keeps the LLM interface simple — no editing tools, no patch formats to get wrong, no partial-apply failures — and keeps each request self-contained.
The trade-off is that an editable file must fit inside the model's output token limit. YACT requests up to 128000 output tokens per call; see Anthropic's model documentation for per-model limits. YACT warns before the call about any linked file longer than 300 lines, and the official prompts ask the model to keep files under about 200 lines.
A code block in a response is written to disk when its first line is a comment holding the
file path (// path, # path, /* path */, -- path or <!-- path -->, optionally after
a shebang line). A block with a path comment and no content deletes that file. Absolute
paths and paths that leave the project directory are rejected.
Every prompt command and query sends one user message made of these parts, in order:
.yact/domain.txt— an optional description of your project, written by you.yact/temp/index.txt— the code index, when the call asks for one.yact/temp/output.txt— command output captured withy output- Skills attached with
--skill - Every linked project file, as a code block whose first line is the file path
- The prompt itself
Use --verbose to print every file and the prompt, and --dry to build and check the
request without calling the API.
- You select the context yourself. YACT will not go looking for the files a task needs, so you need to know your codebase. The code index and keyword search help.
- File sizes are bounded by the model's output token limit.
- One provider only: Anthropic's API. There is no support for other providers or local models.
YACT is written in Go (1.26 or newer). Building from source means you run code you can read:
git clone https://github.com/agabor/yact.git
cd yact
go build -o y .
Then move y somewhere on your PATH — ~/.local/bin needs no elevated privileges:
mkdir -p ~/.local/bin
mv y ~/.local/bin/
There is also an install.sh script that builds the binary and moves it to
/usr/local/bin using sudo.
Release binaries are built by GitHub Actions from tagged commits and published on the releases page:
| Platform | Asset |
|---|---|
| Linux (x86-64) | yact-linux-amd64 |
| Linux (ARM64) | yact-linux-arm64 |
| Windows (x86-64) | yact-windows-amd64.exe |
| macOS (Apple silicon) | yact-darwin-arm64 |
| macOS (Intel) | yact-darwin-amd64 |
On Linux and macOS:
chmod +x yact-linux-amd64
mkdir -p ~/.local/bin
mv yact-linux-amd64 ~/.local/bin/y
On Windows, place yact-windows-amd64.exe in a directory on your PATH (renaming it to
y.exe is convenient). Releases do not currently include checksum files; if you need to
verify what you run, build from source.
Check the installed version with y --version.
Create an API key in your own account at
console.anthropic.com and store it. YACT reads the key
only from its own config file, not from the ANTHROPIC_API_KEY environment variable:
y config anthropic_api_key <your-own-anthropic-key>
y config
Then, inside a git working tree:
y new # start an empty task
y read main.go # link a file into yact-prompt.md
y ask "how are flags parsed?" # one call, answer printed
y act "add a --version flag" # one call, files written to disk
git diff # review what was written
For anything bigger than a small change, plan first:
y new
y plan "add a --version flag" # writes a plan to yact-prompt.md
$EDITOR yact-prompt.md # review or edit the plan
y act # implement the plan, tick off its steps
System prompts are downloaded automatically from this repository the first time you use a command.
yact-prompt.md is both the prompt and the list of context files.
Add a --version flag that prints the version set at build time.
Keep the output format of ((help text)) unchanged.
-----
[main.go](main.go)
[[commands/*.go]]
Only explicit links select files. Plain mentions of a path in prose are not sent.
- Markdown links
[text](path)send exactly that file. Wrap paths with spaces in angle brackets:[my file.go](<my file.go>). - Double-bracket links
[[pattern]]send every project file matching a glob pattern, for example[[commands/*.go]]. Patterns follow Go'sfilepath.Match, so*does not cross/. - Keywords
((phrase))send every file whose content contains the phrase as whole words, ignoring case and separators:((plan step))matchesPlanStep,plan_stepandplan step, but notexplanation. Only files with a configured extension are searched. The parentheses are removed before the prompt is sent; the phrase itself stays.
Each file is sent once. Hidden files and folders are never sent, except the .github
folder so workflows can be edited. Folders listed in .yact/ignore.txt (by default
vendor and node_modules) are skipped, and so is yact-prompt.md itself. URLs are never
fetched.
A line made only of dashes (such as -----) splits the file into a body and a
footer. Links in the footer select files, but the footer text is not sent to the
model. y read and y keyword add their links to the footer, creating it if needed, so
the instructions you write stay clean.
Note that any dash-only line counts, including a Markdown horizontal rule (---).
- With a prompt argument (
y act "..."), the argument is appended to the body ofyact-prompt.mdand the combined text is sent. The combined text is also written back toyact-prompt.md, so instructions accumulate across calls until you runy newory compact. - Without a prompt argument (
y act),yact-prompt.mdis the prompt. If it holds a plan, only some of its steps may be sent (see Plan, then act). A missing or empty file is an error.
Every system prompt file in ~/.yact/systemprompts/ becomes a command: act.txt gives you
y act. If a command's prompt is missing, YACT downloads it from the systemprompts/
folder of this repository's main branch. Pass -d / --download to download it again
and pick up the latest version. y help lists the prompts you have.
The official prompts:
| Command | Defaults | What it does |
|---|---|---|
act |
c1vi1 |
Writes code. Responds with complete files only; fails on any free text |
plan |
c1pi2 |
Writes or updates a step-by-step plan in yact-prompt.md |
ask |
c2n |
Answers questions about the code in plain text |
analyze |
c2n |
Reports where the code breaks the code principles, with fixes |
review |
c2n |
Reviews a diff sent with y output against the task |
bash |
c1n |
Prints a plain list of shell commands for a task |
act, plan, analyze and review share one set of code principles
(systemprompts/include/principles.txt), so code is
planned, written and reviewed against the same rules.
You can edit the official prompts or write your own. Prompt names may not contain /,
\ or ...
A prompt file may start with a line flags: <letters>. That line is not sent to the model.
Each letter turns on a flag whenever the command runs:
c<level>— Claude model level 1 to 4 (c1is Haiku). A barecor an invalid level selects 2 (Sonnet). Ignored when you pass-cyourself.i<level>— index level 1 or 2. A bareiselects 1. Ignored when you pass-iyourself, so-i 0turns the index off.n—--no-writev—--validate-codep—--pland—--download
Other characters are ignored. The n, v, p and d defaults are always added to the
flags you pass, and -n, -v and -p exclude each other, so a combination such as
y -n act (act defaults to v) fails with an error. The effective flags are printed as
Executing: y <flags> <command> before the call.
A prompt file can include shared text with [[ name ]], which is replaced by the content
of ~/.yact/systemprompts/include/<name>. Includes may include other files; cycles are
reported as errors. Includes are downloaded together with the prompt.
--skill <name> attaches ~/.yact/skills/<name>.md verbatim to the request, after the
message files and before the context files. It can be repeated. Missing skills are
downloaded from the skills/ folder of this repository, and -d downloads them again.
The repository ships workflow, which teaches the model how to write
YACT GitHub workflows:
y --skill workflow act "add a workflow that runs y analyze on every pull request"
y plan "add authentication"
plan defaults to -p, so the response is saved to yact-prompt.md, replacing its
previous content (including links added with y read); nothing is written to your source
tree. The plan looks like this:
# Goal
One or two sentences.
## Steps
### [ ] 1. Create the User model
**File:** [src/models/user.go](src/models/user.go) (new)
- Add `type User struct { ... }`
**Context:** [src/db/connection.go](src/db/connection.go)
### [ ] 2. ...
Each step changes one file and links the files it needs, so the plan also selects the context for each step. Review and edit it before you run it.
If the planner had to guess the content of a file it was not given, it lists that file
under ## Missing context. A plan with that section cannot be run: act fails with
Files needed as context are missing; run with --plan to update the plan. Run y plan
again — the files linked in the section are now sent — and the planner revises the plan
and removes the section.
y act
Without a prompt argument and without --steps, the goal and every step that is not
ticked off yet are sent; ticked steps, and files linked only in them, are left out. After
the files are written, the sent steps are ticked off in yact-prompt.md:
### [ ] 1. Title becomes ### [x] 1. Title. Untick a step by hand to run it again.
Steps are ticked only when the response was written to disk — not with -n, not when the
call fails, and not for batch jobs. When every step is ticked, further calls fail with
no steps remain; run with --plan to update the plan.
With -s / --steps <selector>, only the selected steps are sent, together with the goal:
N— a single step, for example2N-M— a range, for example2-4- a comma separated list, for example
1,3,5-6 next— the first step that is not ticked off yet0— the whole plan as it is, without step selection; no step is ticked off
y -s next act
y -s 1,3 act
--steps cannot be combined with a prompt argument or with --plan. Selectors naming
steps the plan does not contain, and plans with duplicate step numbers, are rejected.
Selecting a step that is already done prints a warning and sends it again.
Links in the footer of a plan are part of its last step, so they are sent only when the last step is.
Run y plan without an argument to update an existing plan: the planner checks which
steps are already implemented in the linked files, drops them and renumbers the rest. Add
an argument to change the plan:
y plan "also add a logout endpoint"
y [flags] <command> [arguments]
| Command | Description |
|---|---|
<prompt command> [prompt] |
Call the LLM with the command's system prompt |
query [prompt] |
Call the LLM without a system prompt; the response is printed |
new |
Start a new task |
read <file>... |
Link files into yact-prompt.md (glob patterns supported) |
keyword <keyword> |
Link every file containing the keyword |
output |
Save standard input to be sent with every call |
index <level> |
Write or remove the code index without calling the LLM |
compact |
Reduce yact-prompt.md to its links |
commit |
Write the code blocks of the last response to disk |
batch |
Fetch the result of a pending batch job |
config [key value] |
Show or change configuration |
help |
Show help and the available prompt commands |
y new
Deletes yact-prompt.md (including any plan in it), .yact/temp/index.txt and
.yact/temp/output.txt. .yact/domain.txt is kept.
y read main.go
y read "commands/*.go"
y keyword Transaction
y read appends [path](path) links to the footer of yact-prompt.md. Quote glob
patterns so the shell does not expand them. Directories are skipped and files already
linked are not added twice.
y keyword links every project file whose content contains the keyword as whole
words (see Selecting context files). Use ((keyword)) in the
prompt instead to search at call time without adding links.
go test ./... 2>&1 | y output
git diff | y output
Saves standard input to .yact/temp/output.txt. It is sent with every call until it
is overwritten by the next y output or removed by y new.
YACT can generate a compact index of the declarations in your project: file paths followed
by function, method, type and class signatures, without bodies. Supported languages are
Go, PHP, JavaScript (.js, .mjs, .cjs), C#, Swift and Kotlin. Hidden and ignored
folders are skipped.
y -i 1 ask "where is the config loaded?"
0— no index1— public index: only the public interface2— private index: all declarations
The index is written to .yact/temp/index.txt and sent only in the call that asks for it;
every other call deletes it. act defaults to level 1 and plan to level 2. To write or
remove the index without calling the LLM:
y index 2
y index 0
y act "implement Foo"
y ask "is Foo buggy?"
y bash "find all TODO comments in Go files"
The response is always saved to .yact/temp/buffer.txt. What happens next depends on the
output mode:
- default — code blocks are written to disk, and any text outside them is printed
-v/--validate-code— the same, but the call fails and nothing is written if the response contains free text, an incomplete code block or a rejected path-n/--no-write— the response is printed-p/--plan— the response is saved toyact-prompt.md
Run YACT inside a version-controlled working tree so you can review and revert what it writes.
y query "explain what the keyword command does"
The prompt and context are built the same way as for prompt commands. The response is
always printed and never written, so -p and -v cannot be used with query. With no
argument, the plan in yact-prompt.md is the prompt and -s selects steps, but no step
is ticked off.
y compact
Replaces yact-prompt.md with just its links, once each, in a footer. The instructions are
dropped. Useful for reusing the context of a plan or a pull request description for a new
instruction.
y commit
y -v commit
Writes the code blocks of the last response in .yact/temp/buffer.txt to disk and prints
any text around them. Use it after -n, or after a validated call that failed only because
the model added a sentence of prose. With -v, the commit fails if the buffer contains
free text or an incomplete code block.
y -b act "implement Foo"
y batch
With -b / --batch, the request is submitted to Anthropic's Message Batches API instead
of being streamed. Batches cost half as much but may take a while. Only one batch can be
pending; it is stored in .yact/batch_job_id.txt together with the output mode.
y batch prints the status while the batch is processing. Once it has ended, it prints
token usage and the discounted cost, saves the response to the buffer, delivers it with the
output mode the batch was submitted with (writing files, validating, printing, or saving
the plan), and clears the pending job. Plan steps are not ticked off for batch jobs.
Flags come before the command.
-c, --claude <level> Claude model: 1 Haiku, 2 Sonnet, 3 Opus, 4 Fable
-n, --no-write Print the response instead of writing files
-v, --validate-code Fail if the response (or, for commit, the buffer) contains
free text or incomplete code blocks
-p, --plan Write the response to yact-prompt.md
-s, --steps <selector> Send only the selected plan steps: N, N-M, 1,3,5-6, next, or 0
-i, --index <level> Code index for this call: 0 none, 1 public, 2 private
-b, --batch Submit the request as a batch job
--skill <name> Attach ~/.yact/skills/<name>.md; can be repeated
-d, --download Download the command's system prompt and skills again
--dry Build and check the request, skip the LLM call
--verbose Print the files and the prompt sent to the LLM
--no-progress Hide the progress indicator
--version Print the version and exit
-h, --help Show help
-n, -v and -p exclude each other.
y -c 3 ask "how does the parser work?"
y -i 2 ask "where is the config loaded?"
y -i 0 act "rename Foo to Bar"
y --dry --verbose act
y -b plan "add authentication"
y config # show settings
y config anthropic_api_key <key> # required
y config claude_model sonnet # default model
y config ext vue # add a file extension for keyword search
y config ignore dist # skip a root-level folder
Settings in ~/.yact/config:
anthropic_api_key— your own Anthropic API keyclaude_model—haiku(default),sonnet,opusorfable. The-cflag and a prompt'scdefault override it
The maximum output token count per call is fixed at 128000.
Keyword search (y keyword and ((keyword))) only looks at files with the extensions in
.yact/extensions.txt, which is created with a default set of common source extensions the
first time it is needed. Add one with y config ext <extension>. An empty file means the
defaults.
.yact/ignore.txt lists root-level folders that are never linked, searched or indexed. It
is created with vendor and node_modules. Add one with y config ignore <folder>. An
empty file means no folder is ignored.
Write a short description of your project — what it does, its main concepts and conventions
— into .yact/domain.txt. It is sent first in every call.
Every call prints the model, duration, number of files sent, input and output tokens and an estimated cost in dollars. The estimate is calculated locally from hard-coded rates and is informational only. A warning is shown if the response hit the output token limit and may be incomplete.
Global, in ~/.yact/:
config— API key and default model (JSON)systemprompts/— system prompts, andinclude/for shared prompt partsskills/— skill files
Per project:
yact-prompt.md— the task: instructions, links and plans.yact/domain.txt— optional project description, sent with every call.yact/extensions.txt— extensions searched by keyword.yact/ignore.txt— root-level folders to skip.yact/batch_job_id.txt— the pending batch job and its output mode.yact/temp/buffer.txt— the last LLM response.yact/temp/index.txt— the code index, when a call asks for one.yact/temp/output.txt— output captured byy output
A good .gitignore setup commits the .yact/*.txt settings so the whole team shares them,
and ignores the rest:
.yact/temp
.yact/batch_job_id.txt
yact-prompt.md
Whether you keep yact-prompt.md is up to you. A reviewed and ticked-off plan makes a good
pull request description.
All state is in files and every call is one command, so YACT scripts easily. This
repository is developed with these workflows, triggered by labels whose suffix picks the
model (-haiku, -sonnet, -opus):
| Workflow | Label | What it does |
|---|---|---|
yact-issue.yml |
implement-* on an issue |
Plans the issue, refines the plan if context is missing, implements it, commits with a generated message and opens a pull request with the plan as its description |
yact-update.yml |
update-* on a PR |
Updates the plan from the PR description and the latest comment, implements it and pushes |
yact-fix.yml |
fix-* on a PR |
Feeds failing go build, go test and go vet output to act and pushes the fixes |
yact-review.yml |
review-* on a PR |
Sends the diff, the changed files and the linked issues to review and posts the result as a comment |
They need an ANTHROPIC_API_KEY repository secret, and the runner needs access to
raw.githubusercontent.com to download the system prompts. Tips for CI:
- Pass
--no-progress. - Post
.yact/temp/buffer.txtrather than scraping standard output. - Debug a workflow without spending tokens with
--dry --verbose.
To write your own workflow, use the workflow skill:
y --skill workflow act "...".
"Claude API key not configured"
- Set your key:
y config anthropic_api_key <your-own-anthropic-key>.
"--no-write and --validate-code cannot be combined" (or --plan with either)
- The command's prompt file already turns one of these on (
actuses-v,planuses-p,askuses-n). To see whatactwould write without writing it, use--dry --verboseto inspect the request, or rely ongit diffafterwards.
"response contains free text outside code blocks" / "incomplete code block"
- These come from
-v. Nothing was written. Rerun the command, or apply the response anyway withy commitafter checking.yact/temp/buffer.txt.
"no plan found in yact-prompt.md, provide a prompt or create a plan with --plan"
- You ran a call without a prompt argument and
yact-prompt.mdis missing or empty. Pass a prompt, or create a plan withy plan "<task>".
"Files needed as context are missing; run with --plan to update the plan"
- The plan has a
## Missing contextsection. Runy planonce more.
"no steps remain; run with --plan to update the plan"
- Every step is ticked. Untick a step to rerun it, or update the plan with
y plan "...".
"step range ... does not match the plan" / "invalid step selector item"
- Use
N,N-M, a comma separated list,nextor0, and check the numbers inyact-prompt.md.
"a batch is already pending"
- Fetch the pending result with
y batchfirst.
Files are not sent, or unexpected files are sent
- Only links select files. Run with
--dry --verboseto see what would be sent. Check that the path is relative to the project root and not in a hidden or ignored folder. Remember that.yact/temp/output.txtis sent with every call untily new.
The model keeps seeing old instructions
- Prompt arguments are appended to
yact-prompt.md. Edit the file, or runy compactory new.
"no system prompt found for command"
- The prompt could not be downloaded. Check the command name and your connection, or create
the file in
~/.yact/systemprompts/yourself.
"Warning: ... has N lines, more than 300" / truncated responses
- Split large files: the model has to write them back in full.
API errors
- Check that your API key is valid in the Anthropic console, that your account has credits, and that the selected model is available to you.
YACT is a local command-line program. It has no server component.
- Your API key stays on your machine. It is stored in
~/.yact/configas plain JSON and sent only to Anthropic's API, as thex-api-keyheader it requires. - You get your own key. YACT never issues, requests, brokers or collects API keys.
- No telemetry. The only network connections YACT makes are the API calls you trigger,
and downloads of system prompts and skills from
raw.githubusercontent.comwhen they are missing locally or you pass-d. URLs in prompts are never fetched. - No background activity. YACT runs when you invoke it and exits. It modifies nothing
outside
~/.yact/, the.yact/folder andyact-prompt.mdof your project, and the files written from responses. - Writes stay in the project. File paths in responses are parsed before anything is written or deleted; absolute paths and paths that leave the project directory are rejected.
- You choose what is sent. Only linked files, the message files in
.yact/, attached skills and the prompt are sent. The code index is generated only when a call asks for it. - Auditable. The source is public, releases are built from tagged commits, and every
response is saved verbatim to
.yact/temp/buffer.txt.
Open an issue at github.com/agabor/yact/issues. For anything security-sensitive, please report it privately through the repository's security advisory page rather than in a public issue.
The codebase is small and split into focused packages:
api— the Claude clientcommands— one handler per commandconfig— settings, flags and prompt loadingindex— the code indexerlogic— prompt files, links, plans and response parsing
Run the tests with go test ./.... The code follows the same
principles that the prompts ask the model to
follow. Contributions are welcome: open an issue to discuss an idea, or send a pull
request.
You can also change what YACT does without touching the code: every *.txt file in
~/.yact/systemprompts/ becomes a command.
The website in docs/ is plain HTML, CSS and JavaScript with no build step.
docs/assets/yact-scene.js draws the hero animation.