@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
|
@@ -1,147 +1,193 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Opens a Linear issue in a Nook workspace, in a new terminal tab.
|
|
3
|
-
// Linear runs this file through ~/.linear/coding-tools.json
|
|
4
|
-
// (Work on issue -> Custom script). See README.md next to it.
|
|
5
|
-
//
|
|
6
|
-
// node open-issue.mjs [agent]
|
|
7
|
-
// node open-issue.mjs --run <file> ... inside the new tab
|
|
8
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
9
|
-
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
10
|
-
import { homedir, tmpdir } from "node:os";
|
|
11
|
-
import { delimiter, dirname, join, resolve } from "node:path";
|
|
12
|
-
import { fileURLToPath } from "node:url";
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
*
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
*/
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (!
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
if (
|
|
128
|
-
//
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Opens a Linear issue in a Nook workspace, in a new terminal tab.
|
|
3
|
+
// Linear runs this file through ~/.linear/coding-tools.json
|
|
4
|
+
// (Work on issue -> Custom script). See README.md next to it.
|
|
5
|
+
//
|
|
6
|
+
// node open-issue.mjs [--agent a] [--terminal wt|warp|orca] started by Linear
|
|
7
|
+
// node open-issue.mjs --run <file> ... inside the new tab
|
|
8
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
9
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { homedir, tmpdir } from "node:os";
|
|
11
|
+
import { delimiter, dirname, join, resolve } from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
import { agentCommand, windowsDirect } from "../../src/agents.mjs";
|
|
14
|
+
import { normalizeName } from "../../src/names.mjs";
|
|
15
|
+
import crossSpawn from "cross-spawn";
|
|
16
|
+
|
|
17
|
+
const self = fileURLToPath(import.meta.url);
|
|
18
|
+
const nk = fileURLToPath(new URL("../../bin/nk.mjs", import.meta.url));
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Puts this Node's folder first on PATH. With a version manager such as fnm
|
|
22
|
+
* or nvm, Linear starts without the shell setup that adds it, and global npm
|
|
23
|
+
* commands (the agent, pnpm for hooks) live next to node.exe.
|
|
24
|
+
*/
|
|
25
|
+
function withNodeOnPath(env = process.env) {
|
|
26
|
+
const key = Object.keys(env).find((name) => name.toUpperCase() === "PATH") ?? "PATH";
|
|
27
|
+
return { ...env, [key]: [dirname(process.execPath), env[key]].filter(Boolean).join(delimiter) };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Runs nk with this Node, so the prompt never passes through cmd.exe. */
|
|
31
|
+
function runNk(args, opts = {}) {
|
|
32
|
+
return spawnSync(process.execPath, [nk, ...args], { stdio: "inherit", env: withNodeOnPath(), ...opts });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The issue's workspace from `nk ls --json`, if it exists. */
|
|
36
|
+
function findWorkspace(branch) {
|
|
37
|
+
const listed = spawnSync(process.execPath, [nk, "ls", "--json"], { encoding: "utf8", env: withNodeOnPath() });
|
|
38
|
+
return listed.status === 0 ? JSON.parse(listed.stdout).find((ws) => !ws.main && (ws.branch === branch || ws.name === normalizeName(branch))) : null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Creates the issue's workspace and starts the agent, or reopens it. */
|
|
42
|
+
function startWork({ branch, agent, prompt }) {
|
|
43
|
+
const existing = findWorkspace(branch);
|
|
44
|
+
// Clicked again for the same issue: reopen the workspace without restarting the task.
|
|
45
|
+
// -y: the branch name Linear suggests is converted without asking.
|
|
46
|
+
const result = existing ? runNk([existing.name, agent]) : runNk(["-b", branch, agent, "-y", "-p", prompt]);
|
|
47
|
+
process.exitCode = result.status ?? 1;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Inside a tab another terminal opened: the prompt comes in a file. */
|
|
51
|
+
export function insideTab([file, branch, agent, ...more]) {
|
|
52
|
+
const prompt = readFileSync(file, "utf8");
|
|
53
|
+
rmSync(file, { force: true });
|
|
54
|
+
// A Warp tab config written only for this launch.
|
|
55
|
+
if (more[0] === "--cleanup" && more[1]) rmSync(more[1], { force: true });
|
|
56
|
+
startWork({ branch, agent, prompt });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Windows Terminal: -w 0 opens a tab in the most recent window (or a new window). */
|
|
60
|
+
function openInWindowsTerminal({ dir, id, inner }) {
|
|
61
|
+
spawn("wt.exe", ["-w", "0", "new-tab", "-d", dir, "--title", id, ...inner], { detached: true, stdio: "ignore" }).unref();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** A PowerShell argument in single quotes. */
|
|
65
|
+
const psQuote = (text) => `'${String(text).replaceAll("'", "''")}'`;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Warp: its URI scheme cannot run a command, so write a tab config that
|
|
69
|
+
* does and open it with warp://tab_config/<name>, as Warp's docs advise.
|
|
70
|
+
* The tab deletes the config once it starts.
|
|
71
|
+
*/
|
|
72
|
+
export function openInWarp({ dir, id, inner, env = process.env, start = openUri }) {
|
|
73
|
+
const folder = join(env.APPDATA ?? join(homedir(), "AppData", "Roaming"), "warp", "Warp", "data", "tab_configs");
|
|
74
|
+
mkdirSync(folder, { recursive: true });
|
|
75
|
+
const name = `nk-${id.toLowerCase().replace(/[^a-z0-9-]/g, "-")}-${process.pid}`;
|
|
76
|
+
const config = join(folder, `${name}.toml`);
|
|
77
|
+
const [node, ...args] = [...inner, "--cleanup", config];
|
|
78
|
+
const shell = which("pwsh") ? "pwsh" : "powershell";
|
|
79
|
+
const command = `& ${[node, ...args].map(psQuote).join(" ")}`;
|
|
80
|
+
writeFileSync(
|
|
81
|
+
config,
|
|
82
|
+
[`name = ${JSON.stringify(name)}`, `title = ${JSON.stringify(id)}`, "", "[[panes]]", 'id = "main"', 'type = "terminal"', `shell = ${JSON.stringify(shell)}`, `directory = ${JSON.stringify(dir)}`, `commands = [${JSON.stringify(command)}]`, ""].join("\n"),
|
|
83
|
+
);
|
|
84
|
+
start(`warp://tab_config/${name}`);
|
|
85
|
+
return config;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Opens a URI with its registered app, like PowerShell's Start-Process. */
|
|
89
|
+
function openUri(uri) {
|
|
90
|
+
spawn("cmd.exe", ["/d", "/c", "start", '""', uri], { detached: true, stdio: "ignore", windowsVerbatimArguments: true }).unref();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** A command on PATH, without running it. */
|
|
94
|
+
function which(command) {
|
|
95
|
+
const result = spawnSync("where.exe", [command], { encoding: "utf8", windowsHide: true });
|
|
96
|
+
return result.status === 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const TERMINALS = { wt: openInWindowsTerminal, warp: openInWarp };
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Orca: nk creates the workspace (hooks, ports, database), then Orca starts
|
|
103
|
+
* the agent in it, so Orca keeps its accounts and sessions. Orca shows
|
|
104
|
+
* worktrees it did not create. A new workspace gets Linear's prompt; one
|
|
105
|
+
* that exists only gets a new agent session.
|
|
106
|
+
*/
|
|
107
|
+
export function workInOrca({ branch, agent, prompt, orca = "orca", run = runOrca }) {
|
|
108
|
+
const existing = findWorkspace(branch);
|
|
109
|
+
if (!existing) {
|
|
110
|
+
const created = runNk(["-b", branch, "-y"]);
|
|
111
|
+
if (created.status !== 0) return created.status ?? 1;
|
|
112
|
+
}
|
|
113
|
+
const ws = existing ?? findWorkspace(branch);
|
|
114
|
+
if (!ws) throw new Error(`nk created no workspace for ${branch}`);
|
|
115
|
+
run(orca, ["open", "--json"]);
|
|
116
|
+
const args = ["agent", "launch", "--agent", agent, "--worktree", `path:${ws.path}`, "--focus", "--json"];
|
|
117
|
+
if (!existing && prompt) args.push("--prompt", prompt);
|
|
118
|
+
return run(orca, args);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Runs the Orca CLI, never through cmd.exe, which would cut the prompt. */
|
|
122
|
+
function runOrca(orca, args) {
|
|
123
|
+
const env = withNodeOnPath();
|
|
124
|
+
const spec = windowsDirect({ command: agentCommand({}, orca, {}, env).command, args });
|
|
125
|
+
// cross-spawn: a .cmd shim still runs when the arguments have no line breaks.
|
|
126
|
+
const result = crossSpawn.sync(spec.command, spec.args, { stdio: ["ignore", "pipe", "inherit"], encoding: "utf8", env });
|
|
127
|
+
if (result.error) throw new Error(`could not run ${orca}: ${result.error.message}. Is Orca's command-line tool installed? Pass its path with --orca`);
|
|
128
|
+
// Orca answers in JSON; show it only when something went wrong.
|
|
129
|
+
if (result.status !== 0) process.stdout.write(result.stdout ?? "");
|
|
130
|
+
return result.status ?? 1;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Closes the terminal window Linear opened for this script, once the work
|
|
135
|
+
* runs in another terminal: its shell is this process's parent.
|
|
136
|
+
*/
|
|
137
|
+
function closeLinearWindow() {
|
|
138
|
+
const parent = spawnSync("tasklist.exe", ["/FI", `PID eq ${process.ppid}`, "/FO", "CSV", "/NH"], { encoding: "utf8", windowsHide: true });
|
|
139
|
+
if (/^"(powershell|pwsh|cmd)\.exe"/i.test(parent.stdout ?? "")) process.kill(process.ppid);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The agent named by a label in Linear's prompt, which lists the issue's
|
|
144
|
+
* labels as <label>Group › Name</label>: with group "Agent", an issue
|
|
145
|
+
* labelled "Agent › Claude" gets `claude`.
|
|
146
|
+
*/
|
|
147
|
+
export function agentFromLabels(prompt, group) {
|
|
148
|
+
if (!group) return null;
|
|
149
|
+
for (const [, text] of prompt.matchAll(/<label>([^<]*)<\/label>/g)) {
|
|
150
|
+
const [labelGroup, name] = text.split("›").map((part) => part.trim());
|
|
151
|
+
if (name && labelGroup === group) return name.toLowerCase().replace(/\s+/g, "-");
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** `--name value` from the arguments Linear passes. */
|
|
157
|
+
function option(argv, name) {
|
|
158
|
+
const at = argv.indexOf(name);
|
|
159
|
+
return at >= 0 ? argv[at + 1] : undefined;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function fromLinear(argv) {
|
|
163
|
+
const terminal = option(argv, "--terminal");
|
|
164
|
+
const agent = agentFromLabels(process.env.LINEAR_PROMPT ?? "", option(argv, "--agent-label")) ?? option(argv, "--agent") ?? "codex";
|
|
165
|
+
const { LINEAR_ISSUE_BRANCH_NAME: branch, LINEAR_WORK_DIR: dir, LINEAR_ISSUE_IDENTIFIER: id = "issue", LINEAR_PROMPT: prompt = "" } = process.env;
|
|
166
|
+
if (!branch || !dir) throw new Error("LINEAR_ISSUE_BRANCH_NAME and LINEAR_WORK_DIR are missing; list them under env in coding-tools.json");
|
|
167
|
+
// Default: work in the terminal Linear opened for the script.
|
|
168
|
+
if (!terminal) {
|
|
169
|
+
process.chdir(dir);
|
|
170
|
+
return startWork({ branch, agent, prompt });
|
|
171
|
+
}
|
|
172
|
+
if (terminal === "orca") {
|
|
173
|
+
process.chdir(dir);
|
|
174
|
+
process.exitCode = workInOrca({ branch, agent, prompt, orca: option(argv, "--orca") ?? "orca" });
|
|
175
|
+
// Keep Linear's window open when something failed, so its output can be read.
|
|
176
|
+
if (process.exitCode === 0 && process.platform === "win32") closeLinearWindow();
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const open = TERMINALS[terminal];
|
|
180
|
+
if (!open) throw new Error(`Unknown terminal "${terminal}"; use ${[...Object.keys(TERMINALS), "orca"].join(", ")}, or leave --terminal out`);
|
|
181
|
+
if (process.platform !== "win32") throw new Error("--terminal only works on Windows so far");
|
|
182
|
+
// The prompt is long Markdown with quotes and semicolons, so it travels in a file.
|
|
183
|
+
const file = join(tmpdir(), `nk-linear-${id}-${process.pid}.md`);
|
|
184
|
+
writeFileSync(file, prompt);
|
|
185
|
+
open({ dir, id, inner: [process.execPath, self, "--run", file, branch, agent] });
|
|
186
|
+
closeLinearWindow();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (process.argv[1] && resolve(process.argv[1]) === self) {
|
|
190
|
+
const [first, ...rest] = process.argv.slice(2);
|
|
191
|
+
if (first === "--run") insideTab(rest);
|
|
192
|
+
else fromLinear(process.argv.slice(2));
|
|
193
|
+
}
|
package/package.json
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@ree-auz/nook",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "One home folder for your git worktrees: create, open and remove workspaces with shared files, ports and hooks.",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"bin": {
|
|
8
|
-
"nk": "bin/nk.mjs",
|
|
9
|
-
"nook": "bin/nk.mjs"
|
|
10
|
-
},
|
|
11
|
-
"files": [
|
|
12
|
-
"bin",
|
|
13
|
-
"docs",
|
|
14
|
-
"integrations",
|
|
15
|
-
"skills",
|
|
16
|
-
"src"
|
|
17
|
-
],
|
|
18
|
-
"engines": {
|
|
19
|
-
"node": ">=20.12"
|
|
20
|
-
},
|
|
21
|
-
"scripts": {
|
|
22
|
-
"test": "node --test --test-concurrency=1",
|
|
23
|
-
"corpus": "node scripts/corpus.mjs"
|
|
24
|
-
},
|
|
25
|
-
"dependencies": {
|
|
26
|
-
"cross-spawn": "7.0.6",
|
|
27
|
-
"smol-toml": "1.9.1",
|
|
28
|
-
"yaml": "2.9.1"
|
|
29
|
-
},
|
|
30
|
-
"repository": {
|
|
31
|
-
"type": "git",
|
|
32
|
-
"url": "git+https://github.com/ree-auz/nook.git"
|
|
33
|
-
}
|
|
34
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@ree-auz/nook",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "One home folder for your git worktrees: create, open and remove workspaces with shared files, ports and hooks.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"nk": "bin/nk.mjs",
|
|
9
|
+
"nook": "bin/nk.mjs"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin",
|
|
13
|
+
"docs",
|
|
14
|
+
"integrations",
|
|
15
|
+
"skills",
|
|
16
|
+
"src"
|
|
17
|
+
],
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=20.12"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"test": "node --test --test-concurrency=1",
|
|
23
|
+
"corpus": "node scripts/corpus.mjs"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"cross-spawn": "7.0.6",
|
|
27
|
+
"smol-toml": "1.9.1",
|
|
28
|
+
"yaml": "2.9.1"
|
|
29
|
+
},
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/ree-auz/nook.git"
|
|
33
|
+
}
|
|
34
|
+
}
|
package/skills/nook/SKILL.md
CHANGED
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: nook
|
|
3
|
-
description: Use when working in a git worktree managed by nk (Nook), when asked to create, list, open, remove or clean up workspaces with nk, or to set up a repository's .nook hooks.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Working with Nook (`nk`)
|
|
7
|
-
|
|
8
|
-
Nook keeps one git worktree per task under `~/nook/workspace/<repo>/<name>`.
|
|
9
|
-
The workspace name is also the branch name and the folder name.
|
|
10
|
-
|
|
11
|
-
## Am I in a workspace?
|
|
12
|
-
|
|
13
|
-
`NOOK_WORKSPACE` is set when nk started you. These variables are also set:
|
|
14
|
-
|
|
15
|
-
| Variable | Meaning |
|
|
16
|
-
| --- | --- |
|
|
17
|
-
| `NOOK_WORKSPACE` | workspace and branch name |
|
|
18
|
-
| `NOOK_PATH` | this workspace's folder |
|
|
19
|
-
| `NOOK_REPO` | the main checkout of the repository |
|
|
20
|
-
| `NOOK_SHARED` | the repository's shared folder (holds files such as `.env`) |
|
|
21
|
-
| `NOOK_PORT` | first of ten ports reserved for this workspace (`NOOK_PORT` to `NOOK_PORT + 9`) |
|
|
22
|
-
|
|
23
|
-
Without the variables, `nk ls --json` lists every workspace with its `path`;
|
|
24
|
-
you are in one when your working directory is inside that path.
|
|
25
|
-
|
|
26
|
-
## Rules
|
|
27
|
-
|
|
28
|
-
- Work and commit only inside your own workspace folder. Do not edit the main
|
|
29
|
-
checkout (`NOOK_REPO`) or another workspace.
|
|
30
|
-
- Use ports from `NOOK_PORT` to `NOOK_PORT + 9` for servers you start, so
|
|
31
|
-
workspaces do not collide.
|
|
32
|
-
- Files copied from the shared folder (such as `.env`) are local settings.
|
|
33
|
-
Do not commit them and do not print their contents.
|
|
34
|
-
- Do not run `nk rm` or `nk clean` on your own workspace: nk refuses while
|
|
35
|
-
you are inside it. Leave removal to the person or the tool that started you.
|
|
36
|
-
- Hooks in `.nook/` run with the person's approval. Do not edit them to get
|
|
37
|
-
past a failure; report the failure and the log path nk printed. Never pass
|
|
38
|
-
`--allow-hooks` yourself: the person reads and allows the hooks.
|
|
39
|
-
|
|
40
|
-
## Setting up hooks
|
|
41
|
-
|
|
42
|
-
When asked to set up Nook hooks: if you were started by `nk hooks write
|
|
43
|
-
<agent>`, nk has already generated hooks from CI and the Compose file; finish what its prompt lists
|
|
44
|
-
as not translated. Otherwise run `nk hooks write` first. Follow
|
|
45
|
-
[docs/setup-hooks.md](https://github.com/ree-auz/nook/blob/main/docs/setup-hooks.md),
|
|
46
|
-
run `nk hooks test` in the folder you work in and fix the hooks until it
|
|
47
|
-
passes. Delete the first line of a generated hook you edit. Do not commit or
|
|
48
|
-
push unless asked.
|
|
49
|
-
|
|
50
|
-
## Commands
|
|
51
|
-
|
|
52
|
-
| Command | Use |
|
|
53
|
-
| --- | --- |
|
|
54
|
-
| `nk -b <name> [agent] [-p <prompt>]` | Create a workspace from the default branch (or an existing branch of that name) |
|
|
55
|
-
| `nk <name>` | Print a workspace's path; with an agent, start it there |
|
|
56
|
-
| `nk ls --json` | Every workspace of this repository: `name`, `repo`, `path`, `port`, `changes`, `ahead`, `behind`, `pr` (`number`, `state`, `ci`) |
|
|
57
|
-
| `nk rm <name>... -n` | Show what removing would back up and delete, change nothing |
|
|
58
|
-
| `nk clean -n` | Show which workspaces are merged and would be removed |
|
|
59
|
-
| `nk hooks test` | Run this folder's hooks in a temporary workspace; fails on leftovers |
|
|
60
|
-
| `nk hooks` | The repository's hooks: allowed, tested |
|
|
61
|
-
| `nk doctor` | Check nk's tools; exit 1 when something is broken |
|
|
62
|
-
|
|
63
|
-
Pass `-y` to `nk rm` or `nk clean` only when the person asked for the
|
|
64
|
-
removal. Both back up uncommitted changes and unpushed commits to
|
|
65
|
-
`~/nook/backup/<repo>/` first.
|
|
66
|
-
|
|
67
|
-
## Reading results
|
|
68
|
-
|
|
69
|
-
Exit code 0 means success. On failure, stderr starts with `✗ what failed`,
|
|
70
|
-
followed by `○` lines saying why and what to do next. Follow those lines
|
|
71
|
-
instead of guessing.
|
|
1
|
+
---
|
|
2
|
+
name: nook
|
|
3
|
+
description: Use when working in a git worktree managed by nk (Nook), when asked to create, list, open, remove or clean up workspaces with nk, or to set up a repository's .nook hooks.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Working with Nook (`nk`)
|
|
7
|
+
|
|
8
|
+
Nook keeps one git worktree per task under `~/nook/workspace/<repo>/<name>`.
|
|
9
|
+
The workspace name is also the branch name and the folder name.
|
|
10
|
+
|
|
11
|
+
## Am I in a workspace?
|
|
12
|
+
|
|
13
|
+
`NOOK_WORKSPACE` is set when nk started you. These variables are also set:
|
|
14
|
+
|
|
15
|
+
| Variable | Meaning |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `NOOK_WORKSPACE` | workspace and branch name |
|
|
18
|
+
| `NOOK_PATH` | this workspace's folder |
|
|
19
|
+
| `NOOK_REPO` | the main checkout of the repository |
|
|
20
|
+
| `NOOK_SHARED` | the repository's shared folder (holds files such as `.env`) |
|
|
21
|
+
| `NOOK_PORT` | first of ten ports reserved for this workspace (`NOOK_PORT` to `NOOK_PORT + 9`) |
|
|
22
|
+
|
|
23
|
+
Without the variables, `nk ls --json` lists every workspace with its `path`;
|
|
24
|
+
you are in one when your working directory is inside that path.
|
|
25
|
+
|
|
26
|
+
## Rules
|
|
27
|
+
|
|
28
|
+
- Work and commit only inside your own workspace folder. Do not edit the main
|
|
29
|
+
checkout (`NOOK_REPO`) or another workspace.
|
|
30
|
+
- Use ports from `NOOK_PORT` to `NOOK_PORT + 9` for servers you start, so
|
|
31
|
+
workspaces do not collide.
|
|
32
|
+
- Files copied from the shared folder (such as `.env`) are local settings.
|
|
33
|
+
Do not commit them and do not print their contents.
|
|
34
|
+
- Do not run `nk rm` or `nk clean` on your own workspace: nk refuses while
|
|
35
|
+
you are inside it. Leave removal to the person or the tool that started you.
|
|
36
|
+
- Hooks in `.nook/` run with the person's approval. Do not edit them to get
|
|
37
|
+
past a failure; report the failure and the log path nk printed. Never pass
|
|
38
|
+
`--allow-hooks` yourself: the person reads and allows the hooks.
|
|
39
|
+
|
|
40
|
+
## Setting up hooks
|
|
41
|
+
|
|
42
|
+
When asked to set up Nook hooks: if you were started by `nk hooks write
|
|
43
|
+
<agent>`, nk has already generated hooks from CI and the Compose file; finish what its prompt lists
|
|
44
|
+
as not translated. Otherwise run `nk hooks write` first. Follow
|
|
45
|
+
[docs/setup-hooks.md](https://github.com/ree-auz/nook/blob/main/docs/setup-hooks.md),
|
|
46
|
+
run `nk hooks test` in the folder you work in and fix the hooks until it
|
|
47
|
+
passes. Delete the first line of a generated hook you edit. Do not commit or
|
|
48
|
+
push unless asked.
|
|
49
|
+
|
|
50
|
+
## Commands
|
|
51
|
+
|
|
52
|
+
| Command | Use |
|
|
53
|
+
| --- | --- |
|
|
54
|
+
| `nk -b <name> [agent] [-p <prompt>]` | Create a workspace from the default branch (or an existing branch of that name) |
|
|
55
|
+
| `nk <name>` | Print a workspace's path; with an agent, start it there |
|
|
56
|
+
| `nk ls --json` | Every workspace of this repository: `name`, `repo`, `path`, `port`, `changes`, `ahead`, `behind`, `pr` (`number`, `state`, `ci`) |
|
|
57
|
+
| `nk rm <name>... -n` | Show what removing would back up and delete, change nothing |
|
|
58
|
+
| `nk clean -n` | Show which workspaces are merged and would be removed |
|
|
59
|
+
| `nk hooks test` | Run this folder's hooks in a temporary workspace; fails on leftovers |
|
|
60
|
+
| `nk hooks` | The repository's hooks: allowed, tested |
|
|
61
|
+
| `nk doctor` | Check nk's tools; exit 1 when something is broken |
|
|
62
|
+
|
|
63
|
+
Pass `-y` to `nk rm` or `nk clean` only when the person asked for the
|
|
64
|
+
removal. Both back up uncommitted changes and unpushed commits to
|
|
65
|
+
`~/nook/backup/<repo>/` first.
|
|
66
|
+
|
|
67
|
+
## Reading results
|
|
68
|
+
|
|
69
|
+
Exit code 0 means success. On failure, stderr starts with `✗ what failed`,
|
|
70
|
+
followed by `○` lines saying why and what to do next. Follow those lines
|
|
71
|
+
instead of guessing.
|