kombinat 0.0.0-stage → 0.2.2
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/README.md +217 -2
- package/bin/kombinat.js +12 -0
- package/dist/cli.js +4876 -0
- package/dist/web-app/assets/index-BV7O2RdG.css +2 -0
- package/dist/web-app/assets/index-BvFjiquo.js +134 -0
- package/dist/web-app/index.html +25 -0
- package/package.json +54 -4
package/README.md
CHANGED
|
@@ -1,3 +1,218 @@
|
|
|
1
|
-
|
|
1
|
+
<img src=".github/assets/banner.png" alt="Kombinat: routines in your repo, run by AI agents 24/7" width="100%" />
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Background AI agents that run routines defined in your repository, 24/7. They run on your machine under launchd with Claude Code, Codex or any other CLI agent, using your normal subscription login.
|
|
4
|
+
|
|
5
|
+
Ready to use in your organisation or at home.
|
|
6
|
+
|
|
7
|
+
1. **Version-controlled by design.** Merge a `.md` file and the change is picked up automatically.
|
|
8
|
+
2. **Minimal and lightweight.** Run one command on a machine that is on 24/7. Easy for non-engineers to contribute to.
|
|
9
|
+
3. **Your own integrations.** Agents use your local environment as is, including skills, MCP, browser control and more.
|
|
10
|
+
4. **Cheap.** Works with your existing Claude or Codex subscription. Don't burn your money on tokens.
|
|
11
|
+
5. **Remote control.** Debug and monitor your remote machine from your mobile on your commute.
|
|
12
|
+
6. **Webhooks.** Linear, GitHub or any other app can start a routine with a request, and the agent gets its body.
|
|
13
|
+
7. **Battle-tested.** Polished on real use cases across engineering, design, product management and GTM.
|
|
14
|
+
|
|
15
|
+
**Ideas:**
|
|
16
|
+
|
|
17
|
+
1. **Post-release monitoring.** Checks Intercom, Sentry, Grafana and the logs for problems in code shipped over the last 3 days, raises an alert and opens PRs with fixes.
|
|
18
|
+
2. **On-call engineer.** Investigates production incidents and opens PRs to fix them.
|
|
19
|
+
3. **Flaky E2E test fixer.**
|
|
20
|
+
4. **Dead code and feature flag clean-up.**
|
|
21
|
+
5. **Picking up bug fixes.**
|
|
22
|
+
6. **Monitoring support chats for product signals.**
|
|
23
|
+
7. **Delegating tasks to an agent from your task manager.**
|
|
24
|
+
|
|
25
|
+
**Quickstart:**
|
|
26
|
+
|
|
27
|
+
1. Add Kombinat to the repository that will hold your routines.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pnpm add kombinat # with npm: npm install kombinat
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
2. Add your routines as `.md` files in a `routines/` folder next to `package.json`, see [Writing a routine](#writing-a-routine). Changes are picked up on the next check, no restart needed. Agents run with your usual setup, so your global skills, MCP servers and `CLAUDE.md` are picked up.
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
my-routines-or-project/
|
|
37
|
+
├── routines/
|
|
38
|
+
│ └── fix-e2e-tests.md
|
|
39
|
+
└── package.json
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
3. Start from that folder.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pnpm kombinat serve # or: npx kombinat serve
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
It runs in this terminal. To keep it running in the background and after reboots, stop it and run `pnpm kombinat install` instead, see [Setup](#setup).
|
|
49
|
+
|
|
50
|
+
4. Monitor and debug runs in the dashboard at http://localhost:7433, or in the terminal with `pnpm kombinat tui`, which also works over ssh. See [Monitoring](#monitoring). To open them from another machine, see [Remote access](#remote-access).
|
|
51
|
+
|
|
52
|
+
## Monitoring
|
|
53
|
+
|
|
54
|
+
<img src=".github/assets/dashboard.png" alt="Kombinat dashboard: routines list, week calendar and a run transcript" width="100%" />
|
|
55
|
+
|
|
56
|
+
The daemon serves a dashboard at http://localhost:7433.
|
|
57
|
+
|
|
58
|
+
- **Routines.**
|
|
59
|
+
- **Calendar.**
|
|
60
|
+
- **Runs.**
|
|
61
|
+
- **Daemon status.**
|
|
62
|
+
|
|
63
|
+
### In the terminal
|
|
64
|
+
|
|
65
|
+
<img src=".github/assets/tui.png" alt="Kombinat in the terminal: routines, recent runs and a routine's run history" width="100%" />
|
|
66
|
+
|
|
67
|
+
`pnpm kombinat tui` shows the same in the terminal. It works over ssh. Press `?` for keys.
|
|
68
|
+
|
|
69
|
+
## Remote access
|
|
70
|
+
|
|
71
|
+
### Over SSH
|
|
72
|
+
|
|
73
|
+
Turn on Remote Login on the Mac that runs Kombinat, in System Settings → General → Sharing. Then, on your laptop:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npx kombinat connect admin@mac-mini # dashboard in the browser
|
|
77
|
+
npx kombinat connect admin@mac-mini --tui # dashboard in the terminal
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
It opens an SSH tunnel to the daemon, then the dashboard. SSH asks for the password if you have no key set up. Ctrl-C disconnects. Instead of `admin@mac-mini`, use anything `ssh` accepts: a host from `~/.ssh/config`, an IP address or a Tailscale machine name. If the daemon there uses another `KOMBINAT_PORT`, add `--port`.
|
|
81
|
+
|
|
82
|
+
You can also ssh in and run `pnpm kombinat tui` in the folder Kombinat runs in.
|
|
83
|
+
|
|
84
|
+
### With Tailscale Serve
|
|
85
|
+
|
|
86
|
+
To open the dashboard from any device in your tailnet, your phone included, run this on the Mac that runs Kombinat:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
tailscale serve --bg 7433
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The dashboard is then at `https://mac-mini.<your-tailnet>.ts.net`, and `npx kombinat tui --url https://mac-mini.<your-tailnet>.ts.net` shows it in the terminal. Serve needs HTTPS certificates in your tailnet, the command asks to turn them on. They put the machine name in public certificate logs. It stays on after a reboot, `tailscale serve --https=443 off` turns it off. If the Mac has no `tailscale` command, see [Using the Tailscale CLI](https://tailscale.com/kb/1080/cli).
|
|
93
|
+
|
|
94
|
+
Everyone who can reach the Mac in your tailnet can then read transcripts and start routines. To limit that, allow only some people to reach port 443 of the Mac in your [Tailscale access rules](https://tailscale.com/kb/1018/acls).
|
|
95
|
+
|
|
96
|
+
## Routines
|
|
97
|
+
|
|
98
|
+
The example routines of this repository, one row per file in [`routines/`](routines/). Put the same `routines:start` and `routines:end` comments in your own README, and `pnpm kombinat readme` keeps a table like this one up to date.
|
|
99
|
+
|
|
100
|
+
<!-- prettier-ignore-start -->
|
|
101
|
+
<!-- routines:start -->
|
|
102
|
+
| Routine | When | What it does |
|
|
103
|
+
| - | - | - |
|
|
104
|
+
| [weather-example-routine](routines/weather-example-routine.md) | Weekdays at 08:00 | Says if you need an umbrella today |
|
|
105
|
+
| [weather-webhook-example-routine](routines/weather-webhook-example-routine.md) | On webhook | Says if you need an umbrella in the city from the request |
|
|
106
|
+
<!-- routines:end -->
|
|
107
|
+
<!-- prettier-ignore-end -->
|
|
108
|
+
|
|
109
|
+
## How it works
|
|
110
|
+
|
|
111
|
+
- Each routine is a markdown file. The frontmatter sets the schedule and settings, the body is the prompt.
|
|
112
|
+
- A small daemon checks every 30 seconds which routines are due and starts their agent. It also serves a dashboard at http://localhost:7433.
|
|
113
|
+
- launchd keeps the daemon running and starts it again after a reboot. Runs only happen while the Mac is awake and you are logged in.
|
|
114
|
+
- Logs and run history are stored in `~/.kombinat/`.
|
|
115
|
+
- Claude and Codex use your subscription login. API key variables are removed from their environment.
|
|
116
|
+
|
|
117
|
+
## Setup
|
|
118
|
+
|
|
119
|
+
You need Node 24 and the agents your routines use, logged in: Claude Code (`claude auth status`) and Codex (`codex login`). From the folder with `routines/` and `package.json`:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pnpm add kombinat
|
|
123
|
+
pnpm kombinat install
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
This starts the daemon under launchd, in that folder. Open http://localhost:7433. There is one daemon per macOS user, so running `install` in another folder moves it there.
|
|
127
|
+
|
|
128
|
+
## Commands
|
|
129
|
+
|
|
130
|
+
Run them from the same folder. With npm, use `npx kombinat` instead of `pnpm kombinat`, with Yarn `yarn kombinat`.
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
pnpm kombinat list # routines, next run, last status
|
|
134
|
+
pnpm kombinat run <name> # run a routine now, in the foreground
|
|
135
|
+
pnpm kombinat validate # check all routine files
|
|
136
|
+
pnpm kombinat tui # dashboard in the terminal, works over ssh
|
|
137
|
+
pnpm kombinat status # daemon and scheduler state
|
|
138
|
+
pnpm kombinat readme # update the routines table in README.md
|
|
139
|
+
pnpm kombinat uninstall # stop and remove the daemon
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Writing a routine
|
|
143
|
+
|
|
144
|
+
```markdown
|
|
145
|
+
---
|
|
146
|
+
name: fix-e2e-tests
|
|
147
|
+
description: Finds the failing e2e test and fixes it
|
|
148
|
+
schedule: '30 11 * * *'
|
|
149
|
+
cwd: ~/projects/app
|
|
150
|
+
agent: claude
|
|
151
|
+
model: claude-opus-5-5
|
|
152
|
+
effort: high
|
|
153
|
+
timeoutMinutes: 55
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
1. Open the latest failed e2e run on GitHub ...
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
- `name` must match the file name.
|
|
160
|
+
- `schedule` is a 5-field cron in local time, or `manual` to run only on demand.
|
|
161
|
+
- `cwd` is where the agent starts.
|
|
162
|
+
- `agent` is `claude` (default), `codex` or `command`.
|
|
163
|
+
- `timeoutMinutes` stops the run after this time. Default 55.
|
|
164
|
+
- `before` is an optional shell command that runs first. If it fails or takes more than 3 minutes, the run fails.
|
|
165
|
+
- `enabled: false` keeps the file but stops the schedule.
|
|
166
|
+
- `webhook: true` lets other apps start the routine with a request, see [docs/webhook.md](docs/webhook.md).
|
|
167
|
+
|
|
168
|
+
Changes apply on the next check, no restart needed. Write the prompt so the agent never has to ask a question: nobody will answer.
|
|
169
|
+
|
|
170
|
+
### Agents
|
|
171
|
+
|
|
172
|
+
| Setting | `claude` | `codex` | `command` |
|
|
173
|
+
| ---------------- | --------------------------------------------------------------------------------- | -------------------------------------------------------------- | --------- |
|
|
174
|
+
| `model` | default `claude-opus-5-5` | default from your Codex config | - |
|
|
175
|
+
| `effort` | `low`, `medium`, `high` (default), `xhigh`, `max` | `minimal`, `low`, `medium`, `high`, `xhigh` | - |
|
|
176
|
+
| `permissionMode` | `auto` (default), `acceptEdits`, `dontAsk`, `bypassPermissions`, `plan`, `manual` | `read-only`, `workspace-write` (default), `danger-full-access` | - |
|
|
177
|
+
| `maxBudgetUsd` | optional | - | - |
|
|
178
|
+
| `command` | - | - | required |
|
|
179
|
+
|
|
180
|
+
- `claude` runs `claude -p`. Anything that needs approval is denied.
|
|
181
|
+
- `codex` runs `codex exec`. `permissionMode` sets its sandbox.
|
|
182
|
+
- `command` runs any other CLI, for example `command: gemini --yolo`. It gets the prompt on stdin, and its output is the result.
|
|
183
|
+
|
|
184
|
+
A setting the agent does not support fails validation.
|
|
185
|
+
|
|
186
|
+
## Scheduling
|
|
187
|
+
|
|
188
|
+
- If the Mac was asleep, each routine runs once for its latest missed time, up to 7 days back.
|
|
189
|
+
- A routine never runs twice at the same time. If it is still running at its next time, that run is skipped.
|
|
190
|
+
- At most 3 routines run at once.
|
|
191
|
+
|
|
192
|
+
## Webhooks
|
|
193
|
+
|
|
194
|
+
A routine with `webhook: true` also runs when another app, such as Linear or GitHub, sends it a request. The request body goes into the prompt, an optional `webhookGate` command checks and filters requests, and requests that arrive while the routine runs wait in a queue. See [docs/webhook.md](docs/webhook.md).
|
|
195
|
+
|
|
196
|
+
## Updating
|
|
197
|
+
|
|
198
|
+
The daemon runs `git pull` in the folder every 5 minutes, so merged changes go live on their own. It only pulls when the checkout is on `main` with no local changes.
|
|
199
|
+
|
|
200
|
+
To update Kombinat, bump `kombinat` in `package.json`, for example with a Renovate or Dependabot PR. When the lockfile changes, the daemon installs dependencies with pnpm, npm or Yarn and restarts. Running routines are not interrupted.
|
|
201
|
+
|
|
202
|
+
Up to 0.2.2, Kombinat was published as `@danilqa/kombinat`. To move to `kombinat`, replace the dependency in `package.json`, install it and run `pnpm kombinat install` again. Until then the launchd job of the daemon points into the old package folder, which the install removes, so the daemon does not pick up the new version or start again after a reboot.
|
|
203
|
+
|
|
204
|
+
## Configuration
|
|
205
|
+
|
|
206
|
+
| Variable | Default | What |
|
|
207
|
+
| ------------------------- | --------------------- | -------------------------------------- |
|
|
208
|
+
| `KOMBINAT_PORT` | `7433` | dashboard port |
|
|
209
|
+
| `KOMBINAT_STATE_DIR` | `~/.kombinat` | logs and history |
|
|
210
|
+
| `KOMBINAT_ROUTINES_DIR` | `./routines` | routine files |
|
|
211
|
+
| `KOMBINAT_MAX_CONCURRENT` | `3` | routines that can run at once |
|
|
212
|
+
| `KOMBINAT_TICK_SECONDS` | `30` | how often the daemon checks routines |
|
|
213
|
+
| `KOMBINAT_PULL_MINUTES` | `5` | auto `git pull` interval, 0 to disable |
|
|
214
|
+
| `KOMBINAT_PULL_BRANCH` | `main` | branch the daemon pulls |
|
|
215
|
+
| `CLAUDE_BIN` | `~/.local/bin/claude` | Claude Code binary |
|
|
216
|
+
| `CODEX_BIN` | `codex` | Codex binary |
|
|
217
|
+
|
|
218
|
+
Set them before `pnpm kombinat install`. They are saved in the launchd config.
|
package/bin/kombinat.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// A checkout runs the sources with tsx, the npm package ships dist/cli.js.
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
|
|
5
|
+
const source = new URL('../src/cli.ts', import.meta.url);
|
|
6
|
+
|
|
7
|
+
if (fs.existsSync(source)) {
|
|
8
|
+
await import('tsx');
|
|
9
|
+
await import(source.href);
|
|
10
|
+
} else {
|
|
11
|
+
await import('../dist/cli.js');
|
|
12
|
+
}
|