liutaio 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Narley Brittes
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,279 @@
1
+ # Liutaio
2
+
3
+ Let AI agents work through your tickets while you grab a coffee. Liutaio runs Claude Code in a Docker container, clones your repo, works through tasks one by one, and pushes the results. When it's done, you come back to completed work on your branch.
4
+
5
+ It works with any project — Node.js, Python, Go, Rust, Ruby, PHP — and handles authentication automatically so you can get started with zero setup. (The container ships with Node.js because Claude Code itself is a Node CLI tool, but your project can use any language.)
6
+
7
+ ## Why "Liutaio"?
8
+
9
+ *Liutaio* (pronounced lee-oo-TY-oh) is the Italian word for a luthier — a craftsman who builds and repairs string instruments like violins. A liutaio works methodically in a workshop, shaping raw materials into precise, finely tuned instruments through patience and skill.
10
+
11
+ That's the metaphor here: Liutaio is the workshop where AI agents craft code, working through tickets methodically in an isolated environment, producing something refined at the end.
12
+
13
+ The name was chosen to be tool-agnostic — while Liutaio currently works with Claude Code, the architecture is designed to support other AI coding agents in the future.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install -g liutaio
19
+ ```
20
+
21
+ ## Getting Started
22
+
23
+ You need three things:
24
+
25
+ 1. **Docker** installed and running
26
+ 2. **SSH keys** in `~/.ssh/` that can push to your repo
27
+ 3. An **agents.md** file in your repo describing the work to do (see [Writing Your agents.md](#writing-your-agentsmd))
28
+
29
+ Then just run:
30
+
31
+ ```bash
32
+ liutaio agents.md 10 my-feature-branch
33
+ ```
34
+
35
+ That's it. Liutaio figures out authentication, builds the container, clones your repo, installs dependencies, and starts working.
36
+
37
+ ## Authentication
38
+
39
+ Liutaio tries to authenticate automatically — you don't need to configure anything upfront. It checks these in order and uses the first one that works:
40
+
41
+ | Method | How to set it up |
42
+ |--------|-----------------|
43
+ | **macOS Keychain** | Run `claude auth login` on your Mac once. Liutaio picks it up. |
44
+ | **Credentials file** | Have `~/.claude/.credentials.json` on your machine (common on Linux/CI). |
45
+ | **API key** | Set `export ANTHROPIC_API_KEY=sk-ant-...` in your shell. |
46
+ | **Interactive OAuth** | No setup needed! Liutaio shows a URL, you open it, paste the code back. |
47
+
48
+ If you've ever used Claude Code on your machine, you're already set. If not, Liutaio will walk you through a one-time OAuth login right in the terminal.
49
+
50
+ ### Using OAuth (recommended for shared machines)
51
+
52
+ If you don't want to touch your host credentials, or you're on a machine where Claude Code isn't installed, use `--oauth`:
53
+
54
+ ```bash
55
+ liutaio agents.md 10 my-branch --oauth
56
+ ```
57
+
58
+ Liutaio will show you a URL to open in your browser. After authorising, you'll see a code on the page — paste it back into the terminal. Your credentials are then cached in a Docker volume, so you only need to do this once.
59
+
60
+ To re-authenticate later (e.g. switching accounts):
61
+
62
+ ```bash
63
+ liutaio agents.md 10 my-branch --fresh-login
64
+ ```
65
+
66
+ ### Cached OAuth credentials
67
+
68
+ When you log in via OAuth, your credentials are saved in a Docker volume (`liutaio-creds`). This means:
69
+
70
+ - They survive container removal — you won't be asked to log in again
71
+ - Token refresh happens automatically between iterations
72
+ - Use `--fresh-login` to clear the cache and start fresh
73
+ - To delete them entirely: `docker volume rm liutaio-creds`
74
+
75
+ ## How It Works
76
+
77
+ Here's what happens when you run Liutaio:
78
+
79
+ ```
80
+ Your machine Docker container
81
+ ──────────── ────────────────
82
+ 1. Detect auth method 5. Load credentials
83
+ 2. Build Docker image 6. Clone your repo
84
+ 3. Mount repo (read-only) 7. Create base branch from main
85
+ 4. Start container 8. Install dependencies
86
+ 9. Loop:
87
+ a. Refresh auth token
88
+ b. Run Claude Code on one ticket
89
+ c. Push to remote
90
+ d. Copy progress.md to host
91
+ e. Stop when all done
92
+ ```
93
+
94
+ Each iteration works on exactly one ticket. After completing it (code committed, tests passing, branch merged), Liutaio moves to the next one. When all tickets are done, the container exits.
95
+
96
+ If the container crashes or you stop it (`docker stop`), it pushes whatever work is on the base branch before shutting down — you never lose progress.
97
+
98
+ ## Writing Your agents.md
99
+
100
+ The `agents.md` file tells Claude Code what to do. Put it anywhere in your repo (you pass the path as the first argument). Here's a minimal example:
101
+
102
+ ```markdown
103
+ # My Feature
104
+
105
+ ## Tickets
106
+
107
+ Work through these tickets in order. Each ticket has a detailed spec
108
+ in the `tickets/` folder.
109
+
110
+ ### Execution Order
111
+
112
+ 1. **TICKET-001** — Add user validation
113
+ 2. **TICKET-002** — Update API endpoints
114
+ 3. **TICKET-003** — Write integration tests
115
+
116
+ ## Branch Workflow
117
+
118
+ - Create a feature branch from the base branch for each ticket
119
+ - Run lint and tests before committing
120
+ - Merge back into the base branch when done
121
+
122
+ ## Verification
123
+
124
+ Run these commands to verify your work:
125
+ - `npm run lint`
126
+ - `npm run test`
127
+
128
+ ## Progress Tracking
129
+
130
+ Maintain a `progress.md` file tracking what's been done.
131
+ When all tickets are complete, output: <promise>COMPLETE</promise>
132
+ ```
133
+
134
+ See `agents-template.md` for a more complete template.
135
+
136
+ ### Key conventions
137
+
138
+ - **One ticket per iteration**: Claude works on exactly one ticket, then stops. The next iteration picks up the next ticket.
139
+ - **Completion signal**: When all work is done, Claude outputs `<promise>COMPLETE</promise>` and the loop ends.
140
+ - **progress.md**: Claude updates this file after each ticket. It's copied to your host after every iteration so you can check progress without entering the container.
141
+ - **progress.md is not committed**: It stays outside of git — tracked via the `/output` mount instead.
142
+
143
+ ## Custom Dependency Installation
144
+
145
+ Liutaio auto-detects your project type and installs dependencies:
146
+
147
+ | What it finds | What it runs |
148
+ |--------------|-------------|
149
+ | `pnpm-lock.yaml` | `pnpm install` |
150
+ | `yarn.lock` | `yarn install` |
151
+ | `bun.lock` | `bun install` |
152
+ | `package-lock.json` | `npm install` |
153
+ | `requirements.txt` | `pip install -r requirements.txt` |
154
+ | `pyproject.toml` | `pip install -e .` |
155
+ | `go.sum` | `go mod download` |
156
+ | `Cargo.lock` | `cargo fetch` |
157
+ | `Gemfile.lock` | `bundle install` |
158
+ | `composer.json` | `composer install` |
159
+
160
+ For monorepos or projects with private registries, create a `liutaio.setup.sh` at your repo root. If this file exists, Liutaio runs it instead of auto-detection:
161
+
162
+ ```bash
163
+ #!/bin/bash
164
+ # Example: monorepo with specific install order and private registry
165
+ cat > packages/api/.npmrc << EOF
166
+ @myorg:registry=https://npm.pkg.github.com
167
+ //npm.pkg.github.com/:_authToken=${MY_NPM_TOKEN}
168
+ EOF
169
+
170
+ npm install --prefix packages/shared
171
+ npm install --prefix packages/api
172
+ npm install --prefix packages/client
173
+ ```
174
+
175
+ Pass private tokens with `--env`:
176
+
177
+ ```bash
178
+ liutaio agents.md 10 my-branch --env MY_NPM_TOKEN=ghp_xxxx
179
+ ```
180
+
181
+ ## CLI Reference
182
+
183
+ ```
184
+ liutaio <agents-file> <iterations> <base-branch> [options]
185
+ ```
186
+
187
+ ### Arguments
188
+
189
+ | Argument | Description |
190
+ |----------|-------------|
191
+ | `agents-file` | Path to your agents.md relative to repo root |
192
+ | `iterations` | Maximum number of loop iterations (one ticket per iteration) |
193
+ | `base-branch` | Name of the branch to create from main |
194
+
195
+ ### Options
196
+
197
+ | Option | Description |
198
+ |--------|-------------|
199
+ | `--interactive` | Run in foreground with attached terminal |
200
+ | `--oauth` | Force interactive OAuth login (skip host credentials) |
201
+ | `--fresh-login` | Clear cached OAuth and re-authenticate |
202
+ | `--rebuild` | Force rebuild the Docker image |
203
+ | `--dry-run` | Print the docker command without running it (secrets masked) |
204
+ | `--name NAME` | Custom container name (default: `liutaio-<base-branch>`) |
205
+ | `--node-version V` | Node.js version (default: 22) |
206
+ | `--repo PATH` | Path to git repo (default: auto-detect from current directory) |
207
+ | `--env KEY=VALUE` | Pass environment variable into the container (repeatable) |
208
+
209
+ ### Environment variables
210
+
211
+ | Variable | Description |
212
+ |----------|-------------|
213
+ | `ANTHROPIC_API_KEY` | API key for authentication (alternative to OAuth) |
214
+ | `LIUTAIO_NODE_VERSION` | Default Node.js version (overridden by `--node-version`) |
215
+ | `LIUTAIO_SSH_HOSTS` | Comma-separated SSH hosts for known_hosts (e.g. `gitlab.com,github.com`) |
216
+ | `LIUTAIO_OAUTH_CLIENT_ID` | Override Claude Code's OAuth client ID (only if Anthropic rotates it) |
217
+ | `GIT_USER_NAME` | Git committer name (default: your host's git config) |
218
+ | `GIT_USER_EMAIL` | Git committer email (default: your host's git config) |
219
+
220
+ ## Monitoring a Run
221
+
222
+ ```bash
223
+ # Tail the logs (Ctrl+C to detach — container keeps running)
224
+ docker logs -f liutaio-my-branch
225
+
226
+ # Shell into the running container
227
+ docker exec -it liutaio-my-branch bash
228
+
229
+ # Check progress on your host (updated after each ticket)
230
+ cat path/to/agents-dir/progress.md
231
+
232
+ # Stop gracefully (pushes work before exiting)
233
+ docker stop liutaio-my-branch
234
+
235
+ # Force remove
236
+ docker rm -f liutaio-my-branch
237
+ ```
238
+
239
+ ## Troubleshooting
240
+
241
+ ### "Not logged in" after container starts
242
+
243
+ Your access token expired during dependency installation. This usually resolves itself — the token refresh runs before each iteration. If it persists, try `--oauth` for an independent OAuth session that doesn't share tokens with your host.
244
+
245
+ ### Container gets killed mid-session
246
+
247
+ Likely an out-of-memory kill. Check with:
248
+
249
+ ```bash
250
+ docker inspect liutaio-my-branch --format='{{.State.OOMKilled}}'
251
+ ```
252
+
253
+ The default memory limit is 16GB. If your test suite needs more, edit `--memory=16g` in `run.sh`.
254
+
255
+ ### SSH push failures
256
+
257
+ Make sure your SSH keys are in `~/.ssh/` and can push to your remote. If you use SSH host aliases (e.g. `github-work` instead of `github.com`), Liutaio copies your `~/.ssh/config` into the container and resolves them automatically.
258
+
259
+ ### OAuth login shows "Invalid OAuth Request"
260
+
261
+ Check that you're copying the entire code from the callback page (including any `#` in the middle). The format is `{code}#{state}` — both parts are needed.
262
+
263
+ ### OAuth login suddenly stops working
264
+
265
+ Liutaio uses Claude Code's public OAuth client ID to authenticate. If Anthropic rotates this ID in a future update, OAuth login will fail. You can override it by passing the new client ID:
266
+
267
+ ```bash
268
+ liutaio agents.md 10 my-branch --env LIUTAIO_OAUTH_CLIENT_ID=new-client-id-here
269
+ ```
270
+
271
+ You can find the current client ID by running `claude auth login` on your host and inspecting the authorization URL it generates.
272
+
273
+ ### Loop doesn't stop after all work is done
274
+
275
+ The loop looks for `<promise>COMPLETE</promise>` in the session output. Make sure your agents.md instructs Claude to output this exact string when finished.
276
+
277
+ ## License
278
+
279
+ MIT
@@ -0,0 +1,152 @@
1
+ # Agent Operating Rules
2
+
3
+ This file defines how agents must behave in this repository.
4
+ These rules apply to all tickets and all work.
5
+
6
+ ---
7
+
8
+ ## Before Starting Any Work
9
+
10
+ 1. Read `progress.md` (create if missing).
11
+ 2. Determine the next incomplete ticket from the Execution Order below.
12
+ 3. Check if a branch already exists for that ticket.
13
+ 4. Resume from where `progress.md` left off.
14
+
15
+ ---
16
+
17
+ ## Discovery Before Coding
18
+
19
+ Before writing any code for a ticket:
20
+
21
+ 1. Read the ticket's reference files section (if present).
22
+ 2. Open each referenced file and study the pattern (naming, imports, structure).
23
+ 3. Only start coding after you can describe the pattern you will follow.
24
+
25
+ ---
26
+
27
+ ## Tickets
28
+
29
+ <!-- CUSTOMISE: List your tickets here in execution order -->
30
+ <!-- Each ticket should be a markdown file in a tickets/ directory -->
31
+
32
+ - Tickets live in the `tickets/` folder as markdown files.
33
+ - Each ticket is a PRD for one unit of work.
34
+ - Work on exactly ONE ticket at a time. Use this Execution Order:
35
+ 1. **ticket-1** — Description of first task
36
+ 2. **ticket-2** — Description of second task
37
+ 3. **ticket-3** — Description of third task
38
+ - Do not switch tickets unless explicitly instructed.
39
+
40
+ ---
41
+
42
+ ## Branches
43
+
44
+ - Branch name format: `<ticket-id>-<slug>` (example: `ticket-1-add-user-model`)
45
+ - Create feature branches from the base branch (the branch you are currently on).
46
+ - After completing a ticket, merge the feature branch back into the base branch.
47
+ - Never push to a remote unless explicitly instructed.
48
+
49
+ ### Branch Workflow
50
+
51
+ 1. From the base branch: `git checkout -b <ticket-branch>`
52
+ 2. Develop and commit on the feature branch.
53
+ 3. Run verification on the feature branch (dependencies are installed).
54
+ 4. When ready, merge back: `git checkout <base-branch> && git merge -m "merge: <ticket-branch> into <base-branch>" <ticket-branch>`
55
+ 5. Run verification again on the base branch after merge.
56
+
57
+ ---
58
+
59
+ ## Human-Assisted Tickets
60
+
61
+ - Some tickets may require manual operations that cannot be automated.
62
+ - When encountering these, skip the ticket, note it in progress.md, and continue to the next ticket.
63
+
64
+ ---
65
+
66
+ ## Change Discipline
67
+
68
+ - Prefer small, incremental changes.
69
+ - Make one focused change per iteration.
70
+ - Do not change behaviour unless explicitly stated in the ticket.
71
+ - Do not introduce new dependencies unless approved.
72
+
73
+ ---
74
+
75
+ ## Verification
76
+
77
+ <!-- CUSTOMISE: Replace with your project's actual commands -->
78
+
79
+ - Tests are the source of truth.
80
+ - Verification runs on the **feature branch** during development and on the **base branch** after merging.
81
+
82
+ ### Verification Commands
83
+
84
+ - **Lint:** `npm run lint`
85
+ - **Type check:** `npm run typecheck`
86
+ - **Tests:** `npm test`
87
+
88
+ ### Verification Workflow
89
+
90
+ 1. Write code and commit on the feature branch.
91
+ 2. Run verification on the feature branch.
92
+ 3. If verification passes: merge into the base branch and run verification again.
93
+ 4. If verification fails on the feature branch: fix, commit, and re-run. Max 3 attempts.
94
+ 5. After merging, if verification fails on the base branch: fix directly on the base branch, commit, and re-run. Max 3 attempts.
95
+ 6. If stuck after 3 attempts, stop and say:
96
+ "Blocked on [ticket]. Issue: [description]. Awaiting human input."
97
+
98
+ - Prefer running the smallest applicable verification first
99
+ (e.g. single-file test before full test suite).
100
+ - Never skip a failing test to proceed.
101
+
102
+ ---
103
+
104
+ ## Commits
105
+
106
+ - Commit frequently as you complete logical units of work.
107
+ - Format: `<prefix>: <what changed>`
108
+ - Allowed prefixes: `feat`, `fix`, `refactor`, `test`, `docs`, `chore`, `style`, `perf`, `ci`, `merge`, `wip`
109
+ - Example: `feat: add user authentication middleware`
110
+ - **Merge commits MUST also use the prefix format.** Git's default `Merge branch 'X' into Y` message may be rejected by pre-receive hooks.
111
+ - Use: `git merge -m "merge: <branch> into <target>" <branch>`
112
+ - Never push unless instructed.
113
+
114
+ ---
115
+
116
+ ## Progress Tracking
117
+
118
+ - A file named `progress.md` must exist.
119
+ - After every successful iteration (tests pass or verification succeeds),
120
+ update `progress.md` automatically.
121
+ - Each entry must include (briefly):
122
+ - What was changed
123
+ - Why it was changed
124
+ - Any important decisions
125
+ - Anything to avoid repeating
126
+ - **Always end `progress.md` with a `## Next Steps` section** that describes:
127
+ - Which ticket is next (or the current ticket if still in progress)
128
+ - The exact step to resume from
129
+ - Any blockers or prerequisites
130
+ - This section is critical for session continuity — a fresh session must be able to read `progress.md` and know exactly what to do next.
131
+ - Keep entries short, chronological, and easy to scan.
132
+
133
+ ---
134
+
135
+ ## Merging
136
+
137
+ - After a ticket passes all verification, merge its branch into the base branch.
138
+ - Since you are running in AFK (unattended) mode, auto-approve merges — do NOT wait for human confirmation.
139
+
140
+ ---
141
+
142
+ ## Completion
143
+
144
+ 1. When all code changes are committed on the feature branch, run verification.
145
+ 2. If verification passes, merge into the base branch.
146
+ 3. Run verification on the base branch.
147
+ 4. Update `progress.md` with a summary of the work done.
148
+ 5. **STOP here. Do not start the next ticket.** The next iteration of the loop will pick it up.
149
+
150
+ - When ALL tickets are complete and verified, output exactly:
151
+
152
+ <promise>COMPLETE</promise>
package/bin/liutaio ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Resolve the real path of this script (follows symlinks from npm global bin)
5
+ SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd)"
6
+ LIUTAIO_HOME="$(cd "$SCRIPT_PATH/.." && pwd)"
7
+
8
+ # Delegate to run.sh with all arguments
9
+ exec "$LIUTAIO_HOME/docker/run.sh" "$@"
@@ -0,0 +1,27 @@
1
+ ARG NODE_VERSION=22
2
+
3
+ FROM node:${NODE_VERSION}-slim
4
+
5
+ RUN apt-get update \
6
+ && apt-get install -y --no-install-recommends \
7
+ git \
8
+ openssh-client \
9
+ jq \
10
+ curl \
11
+ ca-certificates \
12
+ && rm -rf /var/lib/apt/lists/*
13
+
14
+ RUN npm install -g @anthropic-ai/claude-code
15
+
16
+ # Non-root user (Claude Code refuses --dangerously-skip-permissions as root)
17
+ RUN useradd -m -s /bin/bash liutaio \
18
+ && mkdir -p /workspace /credentials \
19
+ && chown liutaio:liutaio /workspace /credentials
20
+
21
+ COPY entrypoint.sh /usr/local/bin/liutaio-entrypoint
22
+ RUN chmod +x /usr/local/bin/liutaio-entrypoint
23
+
24
+ USER liutaio
25
+ WORKDIR /workspace
26
+
27
+ ENTRYPOINT ["/usr/local/bin/liutaio-entrypoint"]