@ree-auz/nook 0.1.0 → 0.1.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/LICENSE +21 -21
- package/README.md +422 -410
- package/bin/nk.mjs +3 -3
- package/docs/setup-hooks.md +107 -107
- package/integrations/linear/README.md +94 -84
- package/integrations/linear/coding-tools.json +7 -7
- package/integrations/linear/open-issue.mjs +193 -147
- package/package.json +34 -34
- package/skills/nook/SKILL.md +71 -71
- package/src/agents.mjs +92 -92
- package/src/backup.mjs +74 -74
- package/src/ci.mjs +378 -378
- package/src/cli.mjs +151 -151
- package/src/commands/check-hooks.mjs +128 -128
- package/src/commands/clean.mjs +175 -175
- package/src/commands/doctor.mjs +100 -91
- package/src/commands/forget.mjs +57 -57
- package/src/commands/hooks.mjs +34 -27
- package/src/commands/init.mjs +154 -154
- package/src/commands/ls.mjs +107 -107
- package/src/commands/rm.mjs +190 -190
- package/src/commands/workspace.mjs +163 -163
- package/src/commands/write-hooks.mjs +185 -185
- package/src/compose-plan.mjs +228 -228
- package/src/compose.mjs +123 -123
- package/src/config.mjs +115 -115
- package/src/context.mjs +189 -189
- package/src/errors.mjs +8 -8
- package/src/forge.mjs +75 -75
- package/src/generate-hooks.mjs +367 -367
- package/src/git.mjs +86 -86
- package/src/home.mjs +48 -48
- package/src/hooks.mjs +135 -135
- package/src/names.mjs +65 -65
- package/src/occupancy-windows.ps1 +59 -59
- package/src/occupancy.mjs +131 -131
- package/src/ports.mjs +89 -45
- package/src/prompt.mjs +100 -100
- package/src/shared.mjs +97 -97
- package/src/status.mjs +30 -30
- package/src/tools.mjs +23 -23
- package/src/ui.mjs +60 -60
package/README.md
CHANGED
|
@@ -1,410 +1,422 @@
|
|
|
1
|
-
# Nook
|
|
2
|
-
|
|
3
|
-
`nk` keeps every git worktree you work in under one home folder, `~/nook/`.
|
|
4
|
-
It creates a workspace for a branch, copies the ignored files the project needs
|
|
5
|
-
(such as `.env`), gives each workspace its own block of ports, runs the
|
|
6
|
-
repository's setup hooks, and can start a command-line agent inside it.
|
|
7
|
-
|
|
8
|
-
> Version 0.1: an early release. Commands and config may still change.
|
|
9
|
-
|
|
10
|
-
## Install
|
|
11
|
-
|
|
12
|
-
```
|
|
13
|
-
npm install -g @ree-auz/nook
|
|
14
|
-
nk doctor
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Needs Node.js 20.12 or later and git. Docker is used when a repository's hooks
|
|
18
|
-
start services, and the [GitHub CLI](https://cli.github.com/) (`gh`) for pull
|
|
19
|
-
requests and CI in `nk ls` and `nk clean`; both are optional. `nk doctor`
|
|
20
|
-
checks them and changes nothing.
|
|
21
|
-
|
|
22
|
-
## Quick start
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
cd ~/code/my-app any git repository
|
|
26
|
-
nk init register it (once)
|
|
27
|
-
nk hooks write setup hooks from its CI and Compose file
|
|
28
|
-
nk hooks test try them in a temporary workspace
|
|
29
|
-
nk -b fix-login codex new workspace on branch fix-login, with an agent
|
|
30
|
-
cd "$(nk fix-login)" or just go there
|
|
31
|
-
nk ls every workspace, with its PR and CI
|
|
32
|
-
nk rm fix-login back up and remove it when you are done
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Commands
|
|
36
|
-
|
|
37
|
-
| Command | What it does |
|
|
38
|
-
|---|---|
|
|
39
|
-
| `nk init` | Register the current repository (once per repository; safe to rerun) |
|
|
40
|
-
| `nk -b <name> [agent] [-p <prompt>] [-y]` | Create a workspace; start the agent if given (`-y` keeps a converted name without asking) |
|
|
41
|
-
| `nk <name> [agent] [-p <prompt>]` | Open a workspace; start the agent if given |
|
|
42
|
-
| `nk ls [--json]` | List workspaces: uncommitted changes, commits ahead of and behind the default branch, PR and CI |
|
|
43
|
-
| `nk rm <name>... [-n] [-y]` | Back up and remove one or more workspaces, asking once (`-n` shows what would happen, `-y` skips the question) |
|
|
44
|
-
| `nk clean [-n] [-y]` | Remove every workspace whose work is merged, and folders left from earlier removals |
|
|
45
|
-
| `nk hooks` | The repository's `.nook` hooks: allowed, and tested since they last changed |
|
|
46
|
-
| `nk hooks write [agent]` | Write the hooks from the repository's CI; with an agent, also starts it in the same folder to finish what CI could not tell |
|
|
47
|
-
| `nk hooks test` | Run the hooks of the folder you are in, in a temporary workspace; fails if remove leaves anything behind |
|
|
48
|
-
| `nk forget [<short-name>]` | Stop knowing a repository: remove its registration and shared files; its folder and backups stay |
|
|
49
|
-
| `nk doctor` | Check nk's tools (node, git, gh, docker, agents) without changing anything |
|
|
50
|
-
|
|
51
|
-
Like git, every command acts on the repository you are in: its main
|
|
52
|
-
checkout or one of its workspaces. Outside a repository, `nk -b`, opening,
|
|
53
|
-
`nk rm` and `nk clean` refuse to guess; pass `--repo <short-name>` to name
|
|
54
|
-
one. `nk ls` only reads, so outside a repository it lists all of them.
|
|
55
|
-
|
|
56
|
-
Without an agent, `nk -b` and `nk <name>` print the workspace path, so
|
|
57
|
-
`cd "$(nk my-branch)"` works. Arguments after `--` go to the agent.
|
|
58
|
-
|
|
59
|
-
`nk init` records the agents it finds on PATH and asks nothing about them:
|
|
60
|
-
each one starts with its own default command. To always pass extra
|
|
61
|
-
arguments to one agent, set `args` under `[agents.<name>]` in `config.toml`.
|
|
62
|
-
|
|
63
|
-
## Home folder
|
|
64
|
-
|
|
65
|
-
```
|
|
66
|
-
~/nook/ (set NOOK_HOME to move it)
|
|
67
|
-
config.toml repositories, agents, workspaces, allowed hooks
|
|
68
|
-
workspace/<repo>/<name>/ one git worktree per workspace
|
|
69
|
-
shared/<repo>/ files copied into every new workspace
|
|
70
|
-
backup/<repo>/ what nk rm saved before deleting
|
|
71
|
-
logs/ output of every hook run
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## Names
|
|
75
|
-
|
|
76
|
-
A workspace name is also its branch name and folder name. It uses lowercase
|
|
77
|
-
letters, digits, `-` and `.` only, and cannot be a command name (`init`, `ls`,
|
|
78
|
-
`rm`, `clean`, `hooks`, `forget`, `doctor`, `help`, `version`).
|
|
79
|
-
|
|
80
|
-
You can pass any branch text; `nk` turns it into a valid name and says so:
|
|
81
|
-
uppercase becomes lowercase, and `/`, spaces, non-ASCII and other characters
|
|
82
|
-
become `-`. For example `REE-12-修复登录/Login` becomes `ree-12-login`. This
|
|
83
|
-
way any tool that calls `nk` can pass its own branch name as it is.
|
|
84
|
-
|
|
85
|
-
When a branch with exactly the text you passed already exists (locally or on
|
|
86
|
-
`origin`), `nk -b` checks out that branch and only the workspace and folder use
|
|
87
|
-
the converted name: `nk -b feat/Login` makes workspace `feat-login` on branch
|
|
88
|
-
`feat/Login`, the same way worktrunk sanitizes `/` in folder names. `nk ls`,
|
|
89
|
-
`nk rm` and `nk clean` then use that branch for git and pull requests.
|
|
90
|
-
|
|
91
|
-
When `nk -b` runs in a terminal and had to change the name, it shows the
|
|
92
|
-
converted name ready to edit; press Enter to keep it. Without a terminal it uses
|
|
93
|
-
the converted name.
|
|
94
|
-
|
|
95
|
-
## Shared files
|
|
96
|
-
|
|
97
|
-
New worktrees do not contain ignored files such as `.env`. `nk init` prepares
|
|
98
|
-
`~/nook/shared/<repo>/` once, and every new workspace copies it.
|
|
99
|
-
|
|
100
|
-
- Files are picked with `.worktreeinclude` (gitignore syntax) when the
|
|
101
|
-
repository has one, otherwise with `.env*`.
|
|
102
|
-
- Only files git ignores are used, so a shared file never replaces a tracked one.
|
|
103
|
-
- Every committed template for an ignored file starts a shared file: init
|
|
104
|
-
copies `X.example`, `X.sample`, `X.template` or `X.dist` to the shared `X`
|
|
105
|
-
(in any folder) and opens it for you to fill in once. Examples: `.env.example`,
|
|
106
|
-
`.dev.vars.example`, `config/database.yml.dist`. The same file in the main
|
|
107
|
-
checkout is not copied then, since it may be production.
|
|
108
|
-
- A template written for running the app inside Docker names Compose services:
|
|
109
|
-
`DATABASE_URL=...@postgres:5432/app`. When the Compose file publishes that
|
|
110
|
-
service on this machine (`127.0.0.1:5432:5432`), init points the value at
|
|
111
|
-
`127.0.0.1:5432` and leaves the user, password and database name as they
|
|
112
|
-
are; each workspace then moves the port to its own. Secrets stay for you to
|
|
113
|
-
fill in.
|
|
114
|
-
- Other ignored files in the main checkout are copied only if you say yes.
|
|
115
|
-
- Running `nk init` again never replaces a shared file that is already there;
|
|
116
|
-
it only adds new ones. Edit the files in `~/nook/shared/<repo>/` directly.
|
|
117
|
-
|
|
118
|
-
## Hooks
|
|
119
|
-
|
|
120
|
-
Put scripts in `.nook/` in the repository and commit them. Nook reads them from
|
|
121
|
-
the main checkout and runs them inside the workspace, so a workspace on an old
|
|
122
|
-
branch that predates the hooks still gets them (worktrunk likewise reads its
|
|
123
|
-
project config from where the command runs). The file name is the moment it
|
|
124
|
-
runs:
|
|
125
|
-
|
|
126
|
-
| File | Runs | On failure |
|
|
127
|
-
|---|---|---|
|
|
128
|
-
| `create` | after a workspace is created and shared files are copied | workspace is kept, agent is not started |
|
|
129
|
-
| `open` | every time a workspace is opened (and once after `create`) | agent is not started |
|
|
130
|
-
| `remove` | before a workspace is deleted | nothing is deleted |
|
|
131
|
-
|
|
132
|
-
Nook picks one file per moment: on Windows `.ps1`, then `.cmd`; on macOS and
|
|
133
|
-
Linux `.sh`; on every system `.mjs` (run with Node) and `.py`. One `.mjs` file
|
|
134
|
-
works everywhere.
|
|
135
|
-
|
|
136
|
-
Hooks see `NOOK_WORKSPACE`, `NOOK_PATH`, `NOOK_REPO`, `NOOK_SHARED` and
|
|
137
|
-
`NOOK_PORT` (the first of 10 ports reserved for the workspace).
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
hooks
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
nk
|
|
153
|
-
nk
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
nk
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
|
167
|
-
|
|
168
|
-
|
|
|
169
|
-
| `
|
|
170
|
-
| `
|
|
171
|
-
| `
|
|
172
|
-
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
|
186
|
-
|
|
187
|
-
|
|
|
188
|
-
| a
|
|
189
|
-
| a
|
|
190
|
-
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
workspace's
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
copied `.env`
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
`nk hooks
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
`
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
`nk
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
`
|
|
294
|
-
|
|
295
|
-
and
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
the
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
checkout
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
`
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
remove
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
##
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
`
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
`
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
1
|
+
# Nook
|
|
2
|
+
|
|
3
|
+
`nk` keeps every git worktree you work in under one home folder, `~/nook/`.
|
|
4
|
+
It creates a workspace for a branch, copies the ignored files the project needs
|
|
5
|
+
(such as `.env`), gives each workspace its own block of ports, runs the
|
|
6
|
+
repository's setup hooks, and can start a command-line agent inside it.
|
|
7
|
+
|
|
8
|
+
> Version 0.1: an early release. Commands and config may still change.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
npm install -g @ree-auz/nook
|
|
14
|
+
nk doctor
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Needs Node.js 20.12 or later and git. Docker is used when a repository's hooks
|
|
18
|
+
start services, and the [GitHub CLI](https://cli.github.com/) (`gh`) for pull
|
|
19
|
+
requests and CI in `nk ls` and `nk clean`; both are optional. `nk doctor`
|
|
20
|
+
checks them and changes nothing.
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
cd ~/code/my-app any git repository
|
|
26
|
+
nk init register it (once)
|
|
27
|
+
nk hooks write setup hooks from its CI and Compose file
|
|
28
|
+
nk hooks test try them in a temporary workspace
|
|
29
|
+
nk -b fix-login codex new workspace on branch fix-login, with an agent
|
|
30
|
+
cd "$(nk fix-login)" or just go there
|
|
31
|
+
nk ls every workspace, with its PR and CI
|
|
32
|
+
nk rm fix-login back up and remove it when you are done
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Commands
|
|
36
|
+
|
|
37
|
+
| Command | What it does |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `nk init` | Register the current repository (once per repository; safe to rerun) |
|
|
40
|
+
| `nk -b <name> [agent] [-p <prompt>] [-y]` | Create a workspace; start the agent if given (`-y` keeps a converted name without asking) |
|
|
41
|
+
| `nk <name> [agent] [-p <prompt>]` | Open a workspace; start the agent if given |
|
|
42
|
+
| `nk ls [--json]` | List workspaces: uncommitted changes, commits ahead of and behind the default branch, PR and CI |
|
|
43
|
+
| `nk rm <name>... [-n] [-y]` | Back up and remove one or more workspaces, asking once (`-n` shows what would happen, `-y` skips the question) |
|
|
44
|
+
| `nk clean [-n] [-y]` | Remove every workspace whose work is merged, and folders left from earlier removals |
|
|
45
|
+
| `nk hooks` | The repository's `.nook` hooks: allowed, and tested since they last changed |
|
|
46
|
+
| `nk hooks write [agent]` | Write the hooks from the repository's CI; with an agent, also starts it in the same folder to finish what CI could not tell |
|
|
47
|
+
| `nk hooks test` | Run the hooks of the folder you are in, in a temporary workspace; fails if remove leaves anything behind |
|
|
48
|
+
| `nk forget [<short-name>]` | Stop knowing a repository: remove its registration and shared files; its folder and backups stay |
|
|
49
|
+
| `nk doctor` | Check nk's tools (node, git, gh, docker, agents) without changing anything |
|
|
50
|
+
|
|
51
|
+
Like git, every command acts on the repository you are in: its main
|
|
52
|
+
checkout or one of its workspaces. Outside a repository, `nk -b`, opening,
|
|
53
|
+
`nk rm` and `nk clean` refuse to guess; pass `--repo <short-name>` to name
|
|
54
|
+
one. `nk ls` only reads, so outside a repository it lists all of them.
|
|
55
|
+
|
|
56
|
+
Without an agent, `nk -b` and `nk <name>` print the workspace path, so
|
|
57
|
+
`cd "$(nk my-branch)"` works. Arguments after `--` go to the agent.
|
|
58
|
+
|
|
59
|
+
`nk init` records the agents it finds on PATH and asks nothing about them:
|
|
60
|
+
each one starts with its own default command. To always pass extra
|
|
61
|
+
arguments to one agent, set `args` under `[agents.<name>]` in `config.toml`.
|
|
62
|
+
|
|
63
|
+
## Home folder
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
~/nook/ (set NOOK_HOME to move it)
|
|
67
|
+
config.toml repositories, agents, workspaces, allowed hooks
|
|
68
|
+
workspace/<repo>/<name>/ one git worktree per workspace
|
|
69
|
+
shared/<repo>/ files copied into every new workspace
|
|
70
|
+
backup/<repo>/ what nk rm saved before deleting
|
|
71
|
+
logs/ output of every hook run
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Names
|
|
75
|
+
|
|
76
|
+
A workspace name is also its branch name and folder name. It uses lowercase
|
|
77
|
+
letters, digits, `-` and `.` only, and cannot be a command name (`init`, `ls`,
|
|
78
|
+
`rm`, `clean`, `hooks`, `forget`, `doctor`, `help`, `version`).
|
|
79
|
+
|
|
80
|
+
You can pass any branch text; `nk` turns it into a valid name and says so:
|
|
81
|
+
uppercase becomes lowercase, and `/`, spaces, non-ASCII and other characters
|
|
82
|
+
become `-`. For example `REE-12-修复登录/Login` becomes `ree-12-login`. This
|
|
83
|
+
way any tool that calls `nk` can pass its own branch name as it is.
|
|
84
|
+
|
|
85
|
+
When a branch with exactly the text you passed already exists (locally or on
|
|
86
|
+
`origin`), `nk -b` checks out that branch and only the workspace and folder use
|
|
87
|
+
the converted name: `nk -b feat/Login` makes workspace `feat-login` on branch
|
|
88
|
+
`feat/Login`, the same way worktrunk sanitizes `/` in folder names. `nk ls`,
|
|
89
|
+
`nk rm` and `nk clean` then use that branch for git and pull requests.
|
|
90
|
+
|
|
91
|
+
When `nk -b` runs in a terminal and had to change the name, it shows the
|
|
92
|
+
converted name ready to edit; press Enter to keep it. Without a terminal it uses
|
|
93
|
+
the converted name.
|
|
94
|
+
|
|
95
|
+
## Shared files
|
|
96
|
+
|
|
97
|
+
New worktrees do not contain ignored files such as `.env`. `nk init` prepares
|
|
98
|
+
`~/nook/shared/<repo>/` once, and every new workspace copies it.
|
|
99
|
+
|
|
100
|
+
- Files are picked with `.worktreeinclude` (gitignore syntax) when the
|
|
101
|
+
repository has one, otherwise with `.env*`.
|
|
102
|
+
- Only files git ignores are used, so a shared file never replaces a tracked one.
|
|
103
|
+
- Every committed template for an ignored file starts a shared file: init
|
|
104
|
+
copies `X.example`, `X.sample`, `X.template` or `X.dist` to the shared `X`
|
|
105
|
+
(in any folder) and opens it for you to fill in once. Examples: `.env.example`,
|
|
106
|
+
`.dev.vars.example`, `config/database.yml.dist`. The same file in the main
|
|
107
|
+
checkout is not copied then, since it may be production.
|
|
108
|
+
- A template written for running the app inside Docker names Compose services:
|
|
109
|
+
`DATABASE_URL=...@postgres:5432/app`. When the Compose file publishes that
|
|
110
|
+
service on this machine (`127.0.0.1:5432:5432`), init points the value at
|
|
111
|
+
`127.0.0.1:5432` and leaves the user, password and database name as they
|
|
112
|
+
are; each workspace then moves the port to its own. Secrets stay for you to
|
|
113
|
+
fill in.
|
|
114
|
+
- Other ignored files in the main checkout are copied only if you say yes.
|
|
115
|
+
- Running `nk init` again never replaces a shared file that is already there;
|
|
116
|
+
it only adds new ones. Edit the files in `~/nook/shared/<repo>/` directly.
|
|
117
|
+
|
|
118
|
+
## Hooks
|
|
119
|
+
|
|
120
|
+
Put scripts in `.nook/` in the repository and commit them. Nook reads them from
|
|
121
|
+
the main checkout and runs them inside the workspace, so a workspace on an old
|
|
122
|
+
branch that predates the hooks still gets them (worktrunk likewise reads its
|
|
123
|
+
project config from where the command runs). The file name is the moment it
|
|
124
|
+
runs:
|
|
125
|
+
|
|
126
|
+
| File | Runs | On failure |
|
|
127
|
+
|---|---|---|
|
|
128
|
+
| `create` | after a workspace is created and shared files are copied | workspace is kept, agent is not started |
|
|
129
|
+
| `open` | every time a workspace is opened (and once after `create`) | agent is not started |
|
|
130
|
+
| `remove` | before a workspace is deleted | nothing is deleted |
|
|
131
|
+
|
|
132
|
+
Nook picks one file per moment: on Windows `.ps1`, then `.cmd`; on macOS and
|
|
133
|
+
Linux `.sh`; on every system `.mjs` (run with Node) and `.py`. One `.mjs` file
|
|
134
|
+
works everywhere.
|
|
135
|
+
|
|
136
|
+
Hooks see `NOOK_WORKSPACE`, `NOOK_PATH`, `NOOK_REPO`, `NOOK_SHARED` and
|
|
137
|
+
`NOOK_PORT` (the first of 10 ports reserved for the workspace).
|
|
138
|
+
Blocks start at 20000 (`port_base` in `config.toml` changes that) and skip
|
|
139
|
+
ports something already listens on, as well as the range the system hands to
|
|
140
|
+
outgoing connections, since any program could take one of those at any time.
|
|
141
|
+
Some software moves Windows' range down from 49152; `nk doctor` says so.
|
|
142
|
+
|
|
143
|
+
### Writing the hooks from CI and Compose
|
|
144
|
+
|
|
145
|
+
CI already says, step by step, how a clean machine gets the project running,
|
|
146
|
+
and it is kept working because it runs on every change. Nook turns it into
|
|
147
|
+
hooks without AI, and tests them.
|
|
148
|
+
|
|
149
|
+
The whole cycle:
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
nk init once per repository
|
|
153
|
+
nk hooks write hooks from CI (GitHub Actions) and the Compose file
|
|
154
|
+
nk hooks test run them in a temporary workspace; nothing may be left behind
|
|
155
|
+
nk -b <name> codex new workspace; nk <name> codex reopens one
|
|
156
|
+
nk ls PR and CI of every workspace; merge or close on GitHub
|
|
157
|
+
nk clean | nk rm <name> remove merged or chosen workspaces
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
nk picks the CI job most like "set up and test the project": it runs on pull
|
|
161
|
+
requests or pushes, runs tests, and has the most setup steps and services. Jobs
|
|
162
|
+
that release, deploy, lint or only build are passed over; the report lists the
|
|
163
|
+
others. Local composite actions (`uses: ./.github/actions/setup`) are read
|
|
164
|
+
step by step, as if their steps were in the job. What it takes from that job:
|
|
165
|
+
|
|
166
|
+
| In CI | In the hooks |
|
|
167
|
+
|---|---|
|
|
168
|
+
| `actions/setup-node`, `setup-python`, `pnpm/action-setup`, `astral-sh/setup-uv`... | create checks the tool is installed and warns about another version |
|
|
169
|
+
| `corepack enable` | also a tool check: Node 25 and later no longer include corepack (`npm install -g corepack`) |
|
|
170
|
+
| `run:` steps before the first test, lint or build step | create runs them in the workspace, and stops with a plain message when a step's program is not installed; an install CI skips on a cache hit (`if: steps.cache.outputs.cache-hit != 'true'`) runs, since a new workspace has no cache |
|
|
171
|
+
| a job that `needs:` another and downloads its artifact | the other job's installs come first |
|
|
172
|
+
| a setup action nk cannot read (`pnpm/setup`, a project's own) followed by `pnpm exec ...` or `npx ...` | create runs the install first (`pnpm install --frozen-lockfile`, `npm ci`...), since the action most likely did it |
|
|
173
|
+
| `services:` (a database, say) | create starts one container per workspace on the workspace's own ports; open starts it again; remove deletes it with its volumes |
|
|
174
|
+
| `env:` | added to the workspace's `.env`, with `localhost:<CI port>` changed to the workspace's port |
|
|
175
|
+
| `run:` steps with `if: always()` (cleanup) | remove runs them |
|
|
176
|
+
| checkout, caches, uploads, tests, lint, builds | left out |
|
|
177
|
+
|
|
178
|
+
When the repository has a Compose file (`compose.yaml`, `docker-compose.yml`...),
|
|
179
|
+
nk also reads it, together with its override file (`compose.override.yaml`...)
|
|
180
|
+
as `docker compose` does, since many projects start their database there rather than
|
|
181
|
+
in CI. A base file that cannot run on its own (a service with neither an image
|
|
182
|
+
nor a build) is read with its development file instead
|
|
183
|
+
(`docker-compose.yml` + `docker-compose.dev.yml`), as such projects document:
|
|
184
|
+
|
|
185
|
+
| In the Compose file | In the hooks |
|
|
186
|
+
|---|---|
|
|
187
|
+
| a database or similar service used as it is (`postgres`, `redis`, `rabbitmq`, `minio`, `mailpit`...) | create starts it in the workspace's own Compose project, `nook-<repo>-<workspace>`; open starts it again; remove runs `docker compose down --volumes` for that project |
|
|
188
|
+
| a host port written as a variable, `${API_PORT:-3001}:3001` | the variable gets one of the workspace's ports in its `.env`, so you can run that service too without a clash |
|
|
189
|
+
| a value it needs, `${POSTGRES_PASSWORD:?...}` | create stops and names it when `.env` does not have it; set it once in the shared `.env` |
|
|
190
|
+
| a service built from the repository, the project's own image (the app), or one under a profile | not started; start it yourself when you need it |
|
|
191
|
+
| more services than the workspace's ten ports hold | the last ones are not started, and listed |
|
|
192
|
+
| a fixed host port, `5432:5432` | moved to one of the workspace's ports (`127.0.0.1:<port>:5432`) by a small Compose file nk writes into the worktree's git folder; `COMPOSE_FILE` in `.env` lists it, so a `docker compose` you run there uses it too |
|
|
193
|
+
| a fixed `container_name`, or a network or volume with a fixed `name` | given the workspace's own name in that same file, so two workspaces never share a container, network or data; `external` ones are shared on purpose and stay |
|
|
194
|
+
| `network_mode: host` | the service runs on the workspace's own network instead, its ports moved to the workspace's, since on the host network it would take the main checkout's ports |
|
|
195
|
+
|
|
196
|
+
When the chosen CI job has services of its own, nk starts those and none from
|
|
197
|
+
Compose, so the same database is not started twice.
|
|
198
|
+
|
|
199
|
+
The workspace's `.env` also gets `COMPOSE_PROJECT_NAME` set to the
|
|
200
|
+
workspace's project, replacing whatever the copied file said, so a
|
|
201
|
+
`docker compose` you run there never reaches the main checkout's project.
|
|
202
|
+
Values in the copied `.env` that point at a moved port are pointed at the
|
|
203
|
+
workspace's port: `DATABASE_URL=postgres://...@localhost:5432/app` becomes
|
|
204
|
+
`localhost:<workspace port>`, and so does `DB_PORT=5432`. A service's own
|
|
205
|
+
port counts too: when CI publishes MySQL's 3306 on 32768, `DB_PORT=3306` in the
|
|
206
|
+
copied `.env` is moved as well.
|
|
207
|
+
Other env files at the root that many projects load after `.env`, such as
|
|
208
|
+
`.env.development` or `.env.test`, are pointed at the workspace's ports the
|
|
209
|
+
same way; templates (`.env.example`, `.env.sample`) are left alone. When the
|
|
210
|
+
repository commits one of these files, the workspace's copy comes from git and
|
|
211
|
+
is changed in place; nk marks it `skip-worktree`, so these changes never show
|
|
212
|
+
in `git status` or reach a commit.
|
|
213
|
+
|
|
214
|
+
CI runs on a throwaway machine; a developer machine also has the real
|
|
215
|
+
database and other workspaces. So nk copies less than CI does, never more:
|
|
216
|
+
|
|
217
|
+
- Only commands that install dependencies into the folder (`pnpm install`,
|
|
218
|
+
`npm ci`, `uv sync`, `pip install`...) are copied. A script, a `docker`
|
|
219
|
+
command or a migration is listed for review instead, since nk cannot tell
|
|
220
|
+
what it would touch here.
|
|
221
|
+
- An `env:` value that points at a local port that is not one of CI's
|
|
222
|
+
services (say `localhost:5432` when CI starts Postgres itself), or at a
|
|
223
|
+
database elsewhere, is left out: on this machine it may be the real one.
|
|
224
|
+
- Services get containers named after the workspace, on the workspace's own
|
|
225
|
+
ports, bound to 127.0.0.1. Every step runs with `COMPOSE_PROJECT_NAME` set
|
|
226
|
+
to the workspace's own project.
|
|
227
|
+
- Generated hooks refuse to run unless nk runs them in a workspace, so they
|
|
228
|
+
never run in the main checkout.
|
|
229
|
+
|
|
230
|
+
What it cannot translate (a CI expression such as `${{ secrets.X }}`, a step
|
|
231
|
+
that runs only under a condition, an action it does not know) is listed too,
|
|
232
|
+
not guessed. `nk hooks write <agent>` does the same, then starts the agent in
|
|
233
|
+
the same folder (usually the main checkout) to finish those parts; the agent
|
|
234
|
+
runs `nk hooks test` until it passes and leaves the changes for you to review.
|
|
235
|
+
To keep the agent out of the main checkout, run it inside a workspace instead
|
|
236
|
+
(`nk -b hooks`, then `nk hooks write <agent>` there). Generated hooks start with a
|
|
237
|
+
line saying so; `nk hooks write` replaces only those, never hooks written by
|
|
238
|
+
hand.
|
|
239
|
+
|
|
240
|
+
`nk hooks test` runs the hooks of the folder you are in (the agent's branch,
|
|
241
|
+
before it is merged) in a temporary workspace: create, open twice, then
|
|
242
|
+
remove. It fails when a hook fails, when open cannot run twice, when the
|
|
243
|
+
hooks change files git would commit (files a fresh checkout already shows as
|
|
244
|
+
changed, such as line endings `.gitattributes` would change, are the
|
|
245
|
+
repository's and do not count), or when remove leaves a Docker
|
|
246
|
+
container, volume, network or built image behind.
|
|
247
|
+
|
|
248
|
+
A passing test records the hooks, the CI files and the Compose file. If any of them changes, `nk -b`
|
|
249
|
+
stops before creating a workspace (pass `--untested` to start anyway), and
|
|
250
|
+
`nk <name>`, `nk hooks`, `nk doctor` and `nk rm` say so: run `nk hooks write`
|
|
251
|
+
again for a CI or Compose change, then `nk hooks test`.
|
|
252
|
+
|
|
253
|
+
[docs/setup-hooks.md](docs/setup-hooks.md) has the rules and examples for
|
|
254
|
+
pnpm and npm, uv, and a Docker Compose project per workspace.
|
|
255
|
+
|
|
256
|
+
Before hooks run for the first time, and again after one changes, Nook lists
|
|
257
|
+
them (event and file, not the contents) and asks once: "Allow and remember?".
|
|
258
|
+
Rerunning `nk init` also asks. `nk init --yes` skips the other questions but
|
|
259
|
+
not this one; `nk init --allow-hooks` allows them without asking, for a person
|
|
260
|
+
who has read them (in a script, say). Each run's output is saved in `~/nook/logs/`.
|
|
261
|
+
|
|
262
|
+
Output lines start with ◎ (running now), ✓ (done), ▲ (needs attention) or
|
|
263
|
+
○ (information), as in worktrunk. Colors are used on a terminal unless
|
|
264
|
+
`NO_COLOR` is set.
|
|
265
|
+
|
|
266
|
+
## Removing a workspace
|
|
267
|
+
|
|
268
|
+
`nk rm <name>` refuses while you are inside the workspace. It lists
|
|
269
|
+
uncommitted changes, unpushed commits, an unmerged branch and programs still
|
|
270
|
+
running in the folder (terminals, dev servers, agents: any process whose
|
|
271
|
+
working directory is inside it), asks once, then:
|
|
272
|
+
|
|
273
|
+
1. stops those programs and their child processes;
|
|
274
|
+
2. saves changes and unpushed commits to `~/nook/backup/<repo>/<name>-<time>/`
|
|
275
|
+
(`changes.patch`, `untracked/`, `commits.bundle`);
|
|
276
|
+
3. runs the `remove` hook, if the repository has one;
|
|
277
|
+
4. removes the Docker Compose projects started inside the folder: their
|
|
278
|
+
containers, volumes, networks and built images;
|
|
279
|
+
5. deletes the folder and the branch.
|
|
280
|
+
|
|
281
|
+
Nothing has to be written down when the workspace is created. Compose labels
|
|
282
|
+
every container with the folder it was started from, so nk asks Docker at
|
|
283
|
+
removal time and also finds services an agent started later. A project that
|
|
284
|
+
also has containers started elsewhere (the main checkout's, for example) is
|
|
285
|
+
kept. `nk doctor` lists Compose projects left behind by workspaces that no
|
|
286
|
+
longer exist.
|
|
287
|
+
|
|
288
|
+
This is a safety net: the `remove` hook should still undo everything `create`
|
|
289
|
+
did, and `nk hooks test` checks that it does.
|
|
290
|
+
|
|
291
|
+
## Listing and cleaning up
|
|
292
|
+
|
|
293
|
+
`nk ls` shows each workspace's uncommitted changes, its commits ahead (↑) and
|
|
294
|
+
behind (↓) the default branch, and its pull request with the CI result
|
|
295
|
+
(`#12 open running`, `#12 merged`). PR and CI come from the
|
|
296
|
+
[GitHub CLI](https://cli.github.com/) (`gh`), one request per repository. When
|
|
297
|
+
`gh` is missing or not logged in, the PR column stays empty and a ▲ line says
|
|
298
|
+
why. Merged rows are dimmed. `nk ls --json` prints the same data for scripts
|
|
299
|
+
and agents.
|
|
300
|
+
|
|
301
|
+
Git is the source of truth, as in worktrunk: `nk ls` shows every worktree of
|
|
302
|
+
the repository, including ones made by other tools (Orca, Conductor, plain
|
|
303
|
+
`git worktree add`). Those have no port, and are named by their branch with
|
|
304
|
+
the same conversion as `nk -b` (`ree-auz/REE-228` becomes `ree-auz-ree-228`).
|
|
305
|
+
The first column marks the main checkout `^`, the worktree you are in `@`, and
|
|
306
|
+
the others `+`. `nk rm`, `nk <name>` and `nk clean` work on all of them; a name
|
|
307
|
+
matches the branch or the folder. The main checkout and worktrees locked with
|
|
308
|
+
`git worktree lock` are never removed, but `nk main [agent]` opens the main
|
|
309
|
+
checkout like any workspace.
|
|
310
|
+
|
|
311
|
+
`nk clean` removes workspaces whose work is merged, like worktrunk's
|
|
312
|
+
`wt step prune` and gh-poi. It first fetches the default branch, then counts a
|
|
313
|
+
workspace as merged when:
|
|
314
|
+
|
|
315
|
+
- its pull request is merged and the local branch has nothing the PR did not
|
|
316
|
+
have (so squash merges count), or
|
|
317
|
+
- it has no pull request, its branch is already in the default branch, and it
|
|
318
|
+
is at least a day old (so a workspace you just created is kept).
|
|
319
|
+
|
|
320
|
+
It keeps locked worktrees, workspaces with uncommitted changes, with commits made after the PR
|
|
321
|
+
was merged, with files git ignores that are not rebuilt or shared (a local
|
|
322
|
+
database, say; `nk rm` removes them after listing them), and the one you are inside. It lists what it would remove, what it
|
|
323
|
+
keeps, and the programs running in the ones it removes, asks once, then stops
|
|
324
|
+
those programs and removes each workspace the way `nk rm` does (backup,
|
|
325
|
+
`remove` hook, folder and branch). `-n` only shows
|
|
326
|
+
the list; `-y` removes without asking.
|
|
327
|
+
|
|
328
|
+
It also deletes folders left from earlier removals: on Windows, git can
|
|
329
|
+
remove a worktree and still leave ignored folders such as `node_modules` or
|
|
330
|
+
`.venv` behind. Such a folder is neither a workspace nor a git worktree any
|
|
331
|
+
more; `nk clean` deletes it, with any Compose project started in it, unless a
|
|
332
|
+
program is still running there. `nk rm` itself now deletes whatever git leaves
|
|
333
|
+
and stops with the reason when Windows keeps a file open.
|
|
334
|
+
|
|
335
|
+
## Forgetting a repository
|
|
336
|
+
|
|
337
|
+
`nk forget` (in the repository, or `nk forget <short-name>` from anywhere)
|
|
338
|
+
undoes `nk init`: it removes the repository's registration and its shared
|
|
339
|
+
files in `~/nook/shared/<short-name>/`, after one confirmation. The repository
|
|
340
|
+
folder and the backups `nk rm` made stay.
|
|
341
|
+
|
|
342
|
+
It refuses while the repository has workspaces, so their work is backed up by
|
|
343
|
+
`nk rm` first. If the repository folder is already deleted, git can no longer
|
|
344
|
+
remove its workspaces; then `nk forget` lists what is left in
|
|
345
|
+
`~/nook/workspace/<short-name>/` and deletes it too, with the containers,
|
|
346
|
+
volumes, networks and built images started there (Compose projects, and the
|
|
347
|
+
service containers generated hooks start). Their remove hooks cannot run, since
|
|
348
|
+
they were in the deleted folder.
|
|
349
|
+
|
|
350
|
+
## Uninstalling
|
|
351
|
+
|
|
352
|
+
`npm uninstall -g @ree-auz/nook` removes the `nk` command. What nk made stays:
|
|
353
|
+
workspaces, backups and `~/nook/`. To remove those too, first run, in each
|
|
354
|
+
repository, `nk rm` for its workspaces (or `nk clean` for the merged ones)
|
|
355
|
+
and then `nk forget`. Then delete `~/nook/`; it holds the backups, so look
|
|
356
|
+
in `~/nook/backup/` first.
|
|
357
|
+
|
|
358
|
+
## Linear
|
|
359
|
+
|
|
360
|
+
To open a Linear issue in a new workspace with one click, see
|
|
361
|
+
[integrations/linear](integrations/linear/README.md).
|
|
362
|
+
|
|
363
|
+
## For agents
|
|
364
|
+
|
|
365
|
+
[`skills/nook/SKILL.md`](skills/nook/SKILL.md) tells a coding agent how to work
|
|
366
|
+
inside a workspace: the `NOOK_*` variables, which ports to use, which files not
|
|
367
|
+
to commit, and which commands are safe to run. Copy the `skills/nook` folder
|
|
368
|
+
into your agent's skills folder (for example `~/.claude/skills/`).
|
|
369
|
+
|
|
370
|
+
## Development
|
|
371
|
+
|
|
372
|
+
```
|
|
373
|
+
npm ci
|
|
374
|
+
npm test
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
CI runs the tests on Linux, macOS and Windows.
|
|
378
|
+
|
|
379
|
+
To release, set the new `version` in `package.json` on main, then publish a
|
|
380
|
+
GitHub Release tagged `v<version>`. `.github/workflows/release.yml` checks the
|
|
381
|
+
tag against `package.json`, runs the tests and publishes to npm with trusted
|
|
382
|
+
publishing (no npm token is stored). npm lets only an existing package trust a
|
|
383
|
+
workflow, so the first version was published by hand; a release whose version
|
|
384
|
+
is already on npm is not published again.
|
|
385
|
+
|
|
386
|
+
`npm run corpus` checks what `nk hooks write` makes of real repositories
|
|
387
|
+
(`tests/corpus/repos.json`, each at a pinned commit; only their CI, Compose and
|
|
388
|
+
env template files are fetched, and nothing in them is run) against the
|
|
389
|
+
reviewed results in `tests/corpus/expected/`. CI runs it too. After a change to
|
|
390
|
+
how hooks are written, review the lines it prints and accept them with
|
|
391
|
+
`npm run corpus -- --update`.
|
|
392
|
+
|
|
393
|
+
The repositories marked `"full": true`, plus about ten popular ones nk has not
|
|
394
|
+
seen (`scripts/real-explore.mjs` picks different ones each time, each with a
|
|
395
|
+
root Compose file or CI services), also get a real run every Monday
|
|
396
|
+
(`.github/workflows/real-repos.yml`; start it by hand from the Actions tab with
|
|
397
|
+
Run workflow, optionally for some repositories only). On a fresh GitHub runner
|
|
398
|
+
with the tool versions its CI uses, `scripts/real-run.mjs` runs `nk init`,
|
|
399
|
+
`nk hooks write` and `nk hooks test`. Then, with the ports a main checkout's
|
|
400
|
+
services usually take (5432, 3306, 6379 and others) held as if they were
|
|
401
|
+
running, and a stand-in for the main checkout's own database container and
|
|
402
|
+
volume, it creates two workspaces, `try1` and `try2`. Each must be clean in
|
|
403
|
+
git, have no address or port in its `.env` files that still points at the
|
|
404
|
+
usual port of a service it runs itself (5432 when it has its own Postgres),
|
|
405
|
+
and have its own services answering. Then:
|
|
406
|
+
|
|
407
|
+
- the two share no port, container or volume, and `try1` still answers once
|
|
408
|
+
`try2` runs;
|
|
409
|
+
- a mark written into each workspace's volumes stays in that workspace;
|
|
410
|
+
- with its services stopped (as after a restart), `nk try1` brings them back
|
|
411
|
+
on the same ports with their data;
|
|
412
|
+
- `nk rm try1` leaves `try2` answering with its data, and the main checkout's
|
|
413
|
+
container and volume untouched;
|
|
414
|
+
- after `nk rm try2`, no container, volume, network or folder is left.
|
|
415
|
+
|
|
416
|
+
The run's summary is one table: each repository, the step it failed at, and
|
|
417
|
+
that step's last lines. New repositories that failed are added to
|
|
418
|
+
`tests/corpus/repos.json` once fixed, so the fixed list keeps growing.
|
|
419
|
+
|
|
420
|
+
## License
|
|
421
|
+
|
|
422
|
+
MIT
|