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.
Files changed (43) hide show
  1. package/README.md +279 -195
  2. package/dist/board/App.js +225 -87
  3. package/dist/board/components/ActionButtons.js +28 -8
  4. package/dist/board/components/CreateForm.js +230 -70
  5. package/dist/board/components/CreateProjectModal.js +104 -0
  6. package/dist/board/components/DeleteProjectConfirm.js +53 -0
  7. package/dist/board/components/EditProjectModal.js +106 -0
  8. package/dist/board/components/FilterBar.js +5 -4
  9. package/dist/board/components/Footer.js +19 -6
  10. package/dist/board/components/Header.js +33 -3
  11. package/dist/board/components/HelpModal.js +11 -10
  12. package/dist/board/components/Inspector.js +1 -1
  13. package/dist/board/components/LogModal.js +39 -8
  14. package/dist/board/components/Navigator.js +30 -14
  15. package/dist/board/components/ProjectsModal.js +70 -0
  16. package/dist/board/components/ProjectsPage.js +105 -0
  17. package/dist/board/components/RunHistory.js +48 -9
  18. package/dist/board/components/TaskBrowser.js +96 -0
  19. package/dist/board/components/TaskFilterBar.js +6 -0
  20. package/dist/board/components/TaskForm.js +175 -0
  21. package/dist/board/daemon.js +56 -0
  22. package/dist/board/format.js +19 -12
  23. package/dist/board/hooks/useBoardKeybindings.js +264 -156
  24. package/dist/board/hooks/useTaskKeybindings.js +130 -0
  25. package/dist/board/router.js +16 -0
  26. package/dist/board/state.js +18 -14
  27. package/dist/cli.js +61 -6
  28. package/dist/client/commands.js +157 -1
  29. package/dist/config/constants.js +15 -0
  30. package/dist/config/paths.js +6 -0
  31. package/dist/core/command-runner.js +2 -0
  32. package/dist/core/foreground-loop.js +4 -1
  33. package/dist/core/loop-controller.js +210 -32
  34. package/dist/daemon/index.js +5 -2
  35. package/dist/daemon/manager.js +113 -34
  36. package/dist/daemon/projects.js +104 -0
  37. package/dist/daemon/server.js +177 -6
  38. package/dist/daemon/state.js +32 -1
  39. package/dist/daemon/task-manager.js +58 -0
  40. package/dist/i18n/en.json +140 -21
  41. package/dist/loop-config.js +13 -4
  42. package/dist/shared/clipboard.js +19 -0
  43. package/package.json +13 -3
package/README.md CHANGED
@@ -1,195 +1,279 @@
1
- <div align="center">
2
-
3
- # loop-task
4
-
5
- **Run commands on repeat. Manage them from a terminal board.**
6
-
7
- `loop-task` is a cross-platform CLI that runs shell commands at human-readable intervals. Create loops in the background, manage them from an interactive TUI board, or run them in the foreground.
8
-
9
- [![npm version](https://img.shields.io/npm/v/loop-task?style=flat-square&color=black)](https://www.npmjs.com/package/loop-task)
10
- [![npm downloads](https://img.shields.io/npm/dm/loop-task?style=flat-square&color=black)](https://www.npmjs.com/package/loop-task)
11
- [![license](https://img.shields.io/npm/l/loop-task?style=flat-square&color=black)](./LICENSE)
12
- [![node](https://img.shields.io/node/v/loop-task?style=flat-square&color=black)](https://nodejs.org)
13
-
14
- </div>
15
-
16
- ## Quick start
17
-
18
- ```bash
19
- npm install -g loop-task
20
- loop-task # open the board (requires Bun)
21
- loop-task start # start the daemon, restore persisted loops
22
- loop-task new 30m -- npm test # create a background loop
23
- loop-task run --now 10s -- echo hi # run a loop in the foreground
24
- ```
25
-
26
- Or run it directly:
27
-
28
- ```bash
29
- npx loop-task
30
- npx loop-task new 30m -- npm test
31
- ```
32
-
33
- ## Requirements
34
-
35
- - **Node.js >= 20** required for all commands
36
- - **Bun >= 1.2** — required for the interactive board only
37
-
38
- Install Bun:
39
-
40
- ```bash
41
- npm install -g bun
42
- ```
43
-
44
- `start`, `new`, and `run` work with Node alone. The board auto-delegates to Bun when needed.
45
-
46
- ## Commands
47
-
48
- | Command | Description |
49
- | ------- | ----------- |
50
- | `loop-task` | Open the interactive board (requires Bun) |
51
- | `loop-task start` | Start the background daemon, restore persisted loops |
52
- | `loop-task new <interval> -- <command>` | Create a background loop |
53
- | `loop-task run <interval> -- <command>` | Run a loop in the foreground |
54
-
55
- ### Options (for `new` and `run`)
56
-
57
- | Option | Description |
58
- | ------ | ----------- |
59
- | `--now` | Run immediately before waiting |
60
- | `--max-runs <n>` | Stop after N executions |
61
- | `--cwd <dir>` | Working directory for the command |
62
- | `--verbose` | Show execution details |
63
- | `-h, --help` | Display help |
64
- | `-V, --version` | Display version |
65
-
66
- ## Examples
67
-
68
- ```bash
69
- # Run tests every 30 minutes
70
- loop-task new 30m -- npm test
71
-
72
- # Run immediately, then every hour
73
- loop-task new --now 1h -- npm test
74
-
75
- # Run up to 5 times, then stop
76
- loop-task run --max-runs 5 5m -- npm test
77
-
78
- # Agent workflow — schedule an AI task every 30 minutes
79
- loop-task new 30m --now -- opencode run "find and fix lint issues"
80
-
81
- # Run in a specific directory
82
- loop-task new 30m --cwd ./packages/api -- npm test
83
-
84
- # Verbose mode
85
- loop-task run --verbose 30m -- npm test
86
- ```
87
-
88
- When the command has its own flags, use `--` to stop argument parsing:
89
-
90
- ```bash
91
- loop-task new 30m -- node -e "console.log('hello')"
92
- ```
93
-
94
- ## The board
95
-
96
- The board is the primary way to manage loops. It shows all loops, their status, run history, and logs in a single terminal interface.
97
-
98
- ### Board controls
99
-
100
- ```
101
- ↑/↓, j/k move selection
102
- Enter edit selected loop
103
- n create a new loop
104
- p pause / resume selected loop
105
- r force run selected loop now
106
- del delete selected loop
107
- / search loops
108
- f cycle status filter
109
- s cycle sort mode
110
- ←/→ switch between panels
111
- h toggle help
112
- Esc quit
113
- ```
114
-
115
- Destructive actions (pause, force run, delete) prompt a confirmation before executing.
116
-
117
- ### Loop fields
118
-
119
- When creating or editing a loop:
120
-
121
- | Field | Description |
122
- | ----- | ----------- |
123
- | **Interval** | How often to run (`30s`, `5m`, `1h`, `1d`, `1w`) |
124
- | **Command** | The full command line |
125
- | **Description** | Optional label shown in the list; defaults to the command |
126
- | **Working dir** | Directory the command runs in; defaults to current directory |
127
- | **Run immediately?** | Run once now, then every interval, or wait the first interval |
128
- | **Max runs** | Stop after N runs, or leave blank to run forever |
129
-
130
- ## How it works
131
-
132
- ```
133
- loop-task (board) ──IPC──► daemon ──► loop 1 (background process)
134
- ├──► loop 2
135
- └──► loop 3
136
- ```
137
-
138
- - The **daemon** is a background process that manages all loops. It starts automatically when you run `loop-task start` or any command that needs it.
139
- - The **board** is a terminal UI that connects to the daemon via IPC.
140
- - Loops **persist to disk** — they survive daemon restarts and system reboots. When the daemon starts, it restores all loops and accounts for elapsed time.
141
-
142
- ### Lifecycle
143
-
144
- 1. `loop-task start` or `loop-task new ...` spawns the daemon if not running
145
- 2. The daemon creates a loop and persists its state to disk
146
- 3. `loop-task` opens the board for interactive management
147
- 4. Closing the board or terminal does **not** stop loops — the daemon keeps running
148
- 5. After a reboot, `loop-task start` restores all persisted loops with correct timing
149
-
150
- ## Supported intervals
151
-
152
- | Format | Description |
153
- | ------ | ----------- |
154
- | `10s` | 10 seconds |
155
- | `5m` | 5 minutes |
156
- | `1h` | 1 hour |
157
- | `1d` | 1 day |
158
- | `1w` | 1 week |
159
-
160
- ## Behavior
161
-
162
- - **No overlapping** — waits for the command to finish before starting the next interval
163
- - **Resilient** continues looping even if a command exits with a non-zero code
164
- - **Persistent** loop state is saved after every run; survives restarts
165
- - **Graceful shutdown** — background loops are daemon-managed; foreground loops finish the current execution on Ctrl+C
166
-
167
- ## Development
168
-
169
- 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.
170
-
171
- ```bash
172
- bun install
173
- npm run build
174
- ```
175
-
176
- Run locally:
177
-
178
- ```bash
179
- bun run dev # board
180
- node dist/entry.js new --now 30m -- npm test # background loop
181
- node dist/entry.js run --now --max-runs 1 10s -- echo hello # foreground
182
- ```
183
-
184
- Quality gates:
185
-
186
- ```bash
187
- bun run typecheck
188
- bun run lint
189
- bun run test
190
- npm run build
191
- ```
192
-
193
- ## License
194
-
195
- MIT
1
+ <div align="center">
2
+
3
+
4
+ # loop-task
5
+
6
+ **Loop engineering for your terminal. Run any command on a cadence.**
7
+
8
+ `loop-task` is a cross-platform CLI that runs shell commands at human-readable intervals. Create loops in the background, manage them from an interactive TUI board, or run them in the foreground. It is the **heartbeat** primitive for [loop engineering](#loop-engineering): instead of running a task by hand every time, you schedule it once and let it run.
9
+
10
+ [![npm version](https://img.shields.io/npm/v/loop-task?style=flat-square&color=black)](https://www.npmjs.com/package/loop-task)
11
+ [![npm downloads](https://img.shields.io/npm/dm/loop-task?style=flat-square&color=black)](https://www.npmjs.com/package/loop-task)
12
+ [![license](https://img.shields.io/npm/l/loop-task?style=flat-square&color=black)](./LICENSE)
13
+ [![node](https://img.shields.io/node/v/loop-task?style=flat-square&color=black)](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