loop-task 1.3.0 → 1.4.1
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 +279 -195
- package/dist/board/App.js +225 -87
- package/dist/board/components/ActionButtons.js +28 -8
- package/dist/board/components/CreateForm.js +230 -70
- package/dist/board/components/CreateProjectModal.js +104 -0
- package/dist/board/components/DeleteProjectConfirm.js +53 -0
- package/dist/board/components/EditProjectModal.js +106 -0
- package/dist/board/components/FilterBar.js +5 -4
- package/dist/board/components/Footer.js +19 -6
- package/dist/board/components/Header.js +33 -3
- package/dist/board/components/HelpModal.js +11 -10
- package/dist/board/components/Inspector.js +1 -1
- package/dist/board/components/LogModal.js +39 -8
- package/dist/board/components/Navigator.js +30 -14
- package/dist/board/components/ProjectsModal.js +70 -0
- package/dist/board/components/ProjectsPage.js +105 -0
- package/dist/board/components/RunHistory.js +48 -9
- package/dist/board/components/TaskBrowser.js +96 -0
- package/dist/board/components/TaskFilterBar.js +6 -0
- package/dist/board/components/TaskForm.js +175 -0
- package/dist/board/daemon.js +56 -0
- package/dist/board/format.js +19 -12
- package/dist/board/hooks/useBoardKeybindings.js +264 -156
- package/dist/board/hooks/useTaskKeybindings.js +130 -0
- package/dist/board/router.js +16 -0
- package/dist/board/state.js +18 -14
- package/dist/cli.js +61 -6
- package/dist/client/commands.js +157 -1
- package/dist/config/constants.js +15 -0
- package/dist/config/paths.js +6 -0
- package/dist/core/command-runner.js +2 -0
- package/dist/core/foreground-loop.js +4 -1
- package/dist/core/loop-controller.js +210 -32
- package/dist/daemon/index.js +5 -2
- package/dist/daemon/manager.js +113 -34
- package/dist/daemon/projects.js +104 -0
- package/dist/daemon/server.js +177 -6
- package/dist/daemon/state.js +32 -1
- package/dist/daemon/task-manager.js +58 -0
- package/dist/i18n/en.json +140 -21
- package/dist/loop-config.js +13 -4
- package/dist/shared/clipboard.js +19 -0
- package/package.json +13 -3
package/README.md
CHANGED
|
@@ -1,195 +1,279 @@
|
|
|
1
|
-
<div align="center">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
[: instead of running a task by hand every time, you schedule it once and let it run.
|
|
9
|
+
|
|
10
|
+
[](https://www.npmjs.com/package/loop-task)
|
|
11
|
+
[](https://www.npmjs.com/package/loop-task)
|
|
12
|
+
[](./LICENSE)
|
|
13
|
+
[](https://nodejs.org)
|
|
14
|
+
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
## Loop engineering
|
|
18
|
+
|
|
19
|
+
**Loop engineering** is designing systems that run work on a cadence instead of triggering each run yourself. A *loop* is a recurring goal: you define a purpose, give it an interval, and let it iterate. It applies to ordinary engineering work just as much as to AI agents: health checks, sync jobs, test watches, data pulls, deploy polls, and report generation are all loops.
|
|
20
|
+
|
|
21
|
+
`loop-task` is that heartbeat as a tiny local primitive. Some examples:
|
|
22
|
+
|
|
23
|
+
<div align="center">
|
|
24
|
+
<img src="https://raw.githubusercontent.com/CKGrafico/opencode-task/refs/heads/main/demo.gif" alt="opencode-task demo" width="700" />
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Run the test suite every 30 minutes
|
|
29
|
+
loop-task new 30m -- npm test
|
|
30
|
+
|
|
31
|
+
# Poll a deploy every 10 seconds until you stop it
|
|
32
|
+
loop-task new 10s -- curl -sf https://example.com/health
|
|
33
|
+
|
|
34
|
+
# Re-sync a data export once an hour, scoped to a project
|
|
35
|
+
loop-task new 1h --project etl -- ./scripts/sync.sh
|
|
36
|
+
|
|
37
|
+
# Have a coding agent chip away at a backlog every 30 minutes
|
|
38
|
+
loop-task new 30m -- opencode run "find missing translations and translate them, 3 max"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
No cron files to maintain and no daemon to babysit: loops persist across reboots, run in the background, and you watch them from a terminal board. The idea is described well in Addy Osmani's [Loop Engineering](https://addyosmani.com/blog/loop-engineering/), where scheduled automations are the first of the five pieces of a working loop.
|
|
42
|
+
|
|
43
|
+
> **Stay in control.** A loop running unattended is also a loop failing unattended. Use `--max-runs`, watch the run history on the board, and review what each loop produces. The leverage moves to the loop; the responsibility stays with you.
|
|
44
|
+
|
|
45
|
+
## Quick start
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install -g loop-task
|
|
49
|
+
loop-task # open the board (requires Bun)
|
|
50
|
+
loop-task start # start the daemon, restore persisted loops
|
|
51
|
+
loop-task new 30m -- npm test # create a background loop
|
|
52
|
+
loop-task run --now 10s -- echo hi # run a loop in the foreground
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or run it directly:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npx loop-task
|
|
59
|
+
npx loop-task new 30m -- npm test
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Requirements
|
|
63
|
+
|
|
64
|
+
- **Node.js >= 20** - required for all commands
|
|
65
|
+
- **Bun >= 1.2** - required for the interactive board only
|
|
66
|
+
|
|
67
|
+
Install Bun:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm install -g bun
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`start`, `new`, and `run` work with Node alone. The board auto-delegates to Bun when needed.
|
|
74
|
+
|
|
75
|
+
## Concepts
|
|
76
|
+
|
|
77
|
+
### Loops
|
|
78
|
+
|
|
79
|
+
A **loop** is a schedule - it defines *when* something runs. Loops trigger **tasks**.
|
|
80
|
+
|
|
81
|
+
| Field | Description |
|
|
82
|
+
| ----- | ----------- |
|
|
83
|
+
| **Interval** | How often to run (`30s`, `5m`, `1h`, `1d`, `1w`) |
|
|
84
|
+
| **Task** | Inline command or a reference to a previously defined task |
|
|
85
|
+
| **Description** | Optional label shown in the list; defaults to the task name |
|
|
86
|
+
| **Run immediately?** | Run once now, then every interval, or wait the first interval |
|
|
87
|
+
| **Max runs** | Stop after N runs, or leave blank to run forever |
|
|
88
|
+
|
|
89
|
+
### Tasks
|
|
90
|
+
|
|
91
|
+
A **task** is an executable unit - it defines *what* runs. Tasks can chain to other tasks on success or failure.
|
|
92
|
+
|
|
93
|
+
| Field | Description |
|
|
94
|
+
| ----- | ----------- |
|
|
95
|
+
| **Name** | A short label for the task |
|
|
96
|
+
| **Command** | The full command line |
|
|
97
|
+
| **On success** | Optional task to run when this one exits with code 0 |
|
|
98
|
+
| **On failure** | Optional task to run when this one exits with a non-zero code |
|
|
99
|
+
|
|
100
|
+
Tasks are reusable - the same task can be referenced by multiple loops or by other tasks' success/failure chains.
|
|
101
|
+
|
|
102
|
+
### Projects
|
|
103
|
+
|
|
104
|
+
A **project** is an organizational scope for loops. Every loop belongs to exactly one project. The board shows only loops in the currently selected project.
|
|
105
|
+
|
|
106
|
+
| Field | Description |
|
|
107
|
+
| ----- | ----------- |
|
|
108
|
+
| **Name** | A short label for the project |
|
|
109
|
+
| **Color** | One of six colors: white, cyan, orange, green, red, yellow |
|
|
110
|
+
|
|
111
|
+
Key behaviors:
|
|
112
|
+
- **Default project** - always present, cannot be renamed or deleted. New loops are assigned here when no other project is selected.
|
|
113
|
+
- **Color bullets** - each loop in the navigator displays a colored bullet (●) matching its project color.
|
|
114
|
+
- **Project filter** - the board shows only loops belonging to the currently active project. The selection persists across sessions via `localStorage`.
|
|
115
|
+
|
|
116
|
+
To use projects from the board:
|
|
117
|
+
- Press `c` to open the **Project selector** (switch between projects)
|
|
118
|
+
- Click **Manage Projects** in the filter bar (or use the keyboard shortcut) to open the **Manage Projects** page
|
|
119
|
+
- From the Manage Projects page: `n` creates a new project, `e` renames the selected project, `d` deletes it, `Esc` returns to the board
|
|
120
|
+
|
|
121
|
+
From the CLI:
|
|
122
|
+
- `loop-task project list` - list all projects
|
|
123
|
+
- `loop-task project new <name> [--color <color>]` - create a project
|
|
124
|
+
- `loop-task project rename <id|name> <new-name>` - rename a project
|
|
125
|
+
- `loop-task project color <id|name> <color>` - change a project's color
|
|
126
|
+
- `loop-task project delete <id|name>` - delete a project (loops move to Default)
|
|
127
|
+
- `loop-task new <interval> --project <name> -- <command>` - create a loop assigned to a project
|
|
128
|
+
|
|
129
|
+
Colors can be a name (`white`, `cyan`, `green`, `yellow`, `orange`, `pink`) or a `#rrggbb` hex value.
|
|
130
|
+
|
|
131
|
+
## Commands
|
|
132
|
+
|
|
133
|
+
| Command | Description |
|
|
134
|
+
| ------- | ----------- |
|
|
135
|
+
| `loop-task` | Open the interactive board (requires Bun) |
|
|
136
|
+
| `loop-task start` | Start the background daemon, restore persisted loops |
|
|
137
|
+
| `loop-task new <interval> -- <command>` | Create a background loop (creates an inline task) |
|
|
138
|
+
| `loop-task new <interval> --project <name> -- <command>` | Create a loop assigned to a project |
|
|
139
|
+
| `loop-task run <interval> -- <command>` | Run a loop in the foreground |
|
|
140
|
+
| `loop-task project list` | List all projects |
|
|
141
|
+
| `loop-task project new <name> [--color <color>]` | Create a project |
|
|
142
|
+
| `loop-task project rename <id\|name> <new-name>` | Rename a project |
|
|
143
|
+
| `loop-task project color <id\|name> <color>` | Change project color |
|
|
144
|
+
| `loop-task project delete <id\|name>` | Delete a project (loops move to Default) |
|
|
145
|
+
|
|
146
|
+
### Options (for `new` and `run`)
|
|
147
|
+
|
|
148
|
+
| Option | Description |
|
|
149
|
+
| ------ | ----------- |
|
|
150
|
+
| `--now` | Run immediately before waiting |
|
|
151
|
+
| `--max-runs <n>` | Stop after N executions |
|
|
152
|
+
| `--cwd <dir>` | Working directory for the command |
|
|
153
|
+
| `--verbose` | Show execution details |
|
|
154
|
+
| `-h, --help` | Display help |
|
|
155
|
+
| `-V, --version` | Display version |
|
|
156
|
+
|
|
157
|
+
## Examples
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# Run tests every 30 minutes
|
|
161
|
+
loop-task new 30m -- npm test
|
|
162
|
+
|
|
163
|
+
# Run immediately, then every hour
|
|
164
|
+
loop-task new --now 1h -- npm test
|
|
165
|
+
|
|
166
|
+
# Run up to 5 times, then stop
|
|
167
|
+
loop-task run --max-runs 5 5m -- npm test
|
|
168
|
+
|
|
169
|
+
# Agent workflow - schedule an AI task every 30 minutes
|
|
170
|
+
loop-task new 30m --now -- opencode run "search missing translations and translate them, 3 maximum" --model "opencode/big-pickle"
|
|
171
|
+
# Run in a specific directory
|
|
172
|
+
loop-task new 30m --cwd ./packages/api -- npm test
|
|
173
|
+
|
|
174
|
+
# Verbose mode
|
|
175
|
+
loop-task run --verbose 30m -- npm test
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
When the command has its own flags, use `--` to stop argument parsing:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
loop-task new 30m -- node -e "console.log('hello')"
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## The board
|
|
185
|
+
|
|
186
|
+
The board is the primary way to manage loops and tasks. It shows all loops, their status, run history, and logs in a single terminal interface.
|
|
187
|
+
|
|
188
|
+
### Board controls
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
↑/↓, j/k move selection
|
|
192
|
+
Enter edit selected loop
|
|
193
|
+
e edit loop
|
|
194
|
+
d/del delete loop
|
|
195
|
+
p pause (when waiting) / play (when idle/paused)
|
|
196
|
+
s stop loop (resets schedule)
|
|
197
|
+
n create a new loop
|
|
198
|
+
t create a new task
|
|
199
|
+
o cycle sort mode (order by)
|
|
200
|
+
←/→ switch between panels
|
|
201
|
+
/ search loops
|
|
202
|
+
h toggle help
|
|
203
|
+
esc quit
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Destructive actions (pause, force run, delete) prompt a confirmation before executing.
|
|
207
|
+
|
|
208
|
+
### Pause vs Stop
|
|
209
|
+
|
|
210
|
+
- **Pause** (`p`) - temporarily halts the loop. Resuming continues the original schedule (e.g., a loop that runs every 6h at :00 paused at 12:00 and resumed at 14:00 will still fire at 16:00).
|
|
211
|
+
- **Stop** (`s`) - halts the loop and clears the schedule. Playing starts a fresh interval from now (e.g., the same loop stopped at 12:00 and played at 14:00 will fire at 20:00).
|
|
212
|
+
|
|
213
|
+
## How it works
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
loop-task (board) ──IPC──► daemon ──► loop 1 ──► task (command)
|
|
217
|
+
├──► loop 2 ──► task ──► on-success task
|
|
218
|
+
└──► loop 3 ──► task ──► on-failure task
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
- The **daemon** is a background process that manages all loops and tasks. It starts automatically when you run `loop-task start` or any command that needs it.
|
|
222
|
+
- The **board** is a terminal UI that connects to the daemon via IPC.
|
|
223
|
+
- **Loops** define schedules and reference tasks. **Tasks** define commands and optional success/failure chains.
|
|
224
|
+
- Loops and tasks **persist to disk** - they survive daemon restarts and system reboots. When the daemon starts, it restores all loops and accounts for elapsed time.
|
|
225
|
+
|
|
226
|
+
### Lifecycle
|
|
227
|
+
|
|
228
|
+
1. `loop-task start` or `loop-task new ...` spawns the daemon if not running
|
|
229
|
+
2. The daemon creates a loop and a task, and persists their state to disk
|
|
230
|
+
3. `loop-task` opens the board for interactive management
|
|
231
|
+
4. Closing the board or terminal does **not** stop loops - the daemon keeps running
|
|
232
|
+
5. After a reboot, `loop-task start` restores all persisted loops with correct timing
|
|
233
|
+
|
|
234
|
+
## Supported intervals
|
|
235
|
+
|
|
236
|
+
| Format | Description |
|
|
237
|
+
| ------ | ----------- |
|
|
238
|
+
| `10s` | 10 seconds |
|
|
239
|
+
| `5m` | 5 minutes |
|
|
240
|
+
| `1h` | 1 hour |
|
|
241
|
+
| `1d` | 1 day |
|
|
242
|
+
| `1w` | 1 week |
|
|
243
|
+
|
|
244
|
+
## Behavior
|
|
245
|
+
|
|
246
|
+
- **No overlapping** - waits for the command to finish before starting the next interval
|
|
247
|
+
- **Resilient** - continues looping even if a command exits with a non-zero code
|
|
248
|
+
- **Persistent** - loop and task state is saved after every run; survives restarts
|
|
249
|
+
- **Graceful shutdown** - background loops are daemon-managed; foreground loops finish the current execution on Ctrl+C
|
|
250
|
+
|
|
251
|
+
## Development
|
|
252
|
+
|
|
253
|
+
Requires [Bun](https://bun.sh) >= 1.2 for package management and the board, and [Node.js](https://nodejs.org) >= 20 for the CLI and daemon.
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
bun install
|
|
257
|
+
npm run build
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Run locally:
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
bun run dev # board
|
|
264
|
+
node dist/entry.js new --now 30m -- npm test # background loop
|
|
265
|
+
node dist/entry.js run --now --max-runs 1 10s -- echo hello # foreground
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Quality gates:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
bun run typecheck
|
|
272
|
+
bun run lint
|
|
273
|
+
bun run test
|
|
274
|
+
npm run build
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## License
|
|
278
|
+
|
|
279
|
+
MIT
|