lanekeeper 0.1.5 → 0.1.7
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 +4 -34
- package/dist/bin/lanekeeper.js +20 -6
- package/dist/lib/wire-hooks.d.ts +13 -0
- package/dist/lib/wire-hooks.js +39 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,30 +49,6 @@ GitHub already ships a merge queue. Two things it costs you that this doesn't:
|
|
|
49
49
|
Same idea — serialize landings, test before merge, keep history clean — run
|
|
50
50
|
locally instead of in someone else's billed cloud. 💸
|
|
51
51
|
|
|
52
|
-
## 🧭 Prior art (so you don't have to wonder)
|
|
53
|
-
|
|
54
|
-
Nothing here was invented in isolation — worth naming what already existed
|
|
55
|
-
before you find it yourself:
|
|
56
|
-
|
|
57
|
-
- **[block/agent-task-queue](https://github.com/block/agent-task-queue)**
|
|
58
|
-
already solves "concurrent agents thrash your machine running simultaneous
|
|
59
|
-
builds," standalone, with no git or worktree awareness at all.
|
|
60
|
-
`build-lock` here does the same narrow job, just wired into `land` and
|
|
61
|
-
lanes instead of running on its own.
|
|
62
|
-
- **[Overstory](https://github.com/jayminwest/overstory)** (now archived)
|
|
63
|
-
built a FIFO merge queue plus a liveness-based watchdog for a fleet of
|
|
64
|
-
coding agents — the closest prior match to the landing queue and `prune`
|
|
65
|
-
here. It's a bigger, multi-runtime orchestration framework with its own
|
|
66
|
-
agent/worktree layer, not something that plugs into Claude Code's *native*
|
|
67
|
-
`--worktree`/hook system the way this does; its stated successor moved to
|
|
68
|
-
a hosted, cloud control-plane model.
|
|
69
|
-
|
|
70
|
-
What I couldn't find shipped anywhere else, as of this writing: this
|
|
71
|
-
specific combination — sitting on top of Claude Code's own worktree hook
|
|
72
|
-
instead of reimplementing it, a landing queue enforced at the git pre-push
|
|
73
|
-
hook layer, and liveness-aware auto-pruning — as one small, zero-cost, local
|
|
74
|
-
package. If you know of one, open an issue.
|
|
75
|
-
|
|
76
52
|
## 🧰 What's in the box
|
|
77
53
|
|
|
78
54
|
| Command | What it does |
|
|
@@ -126,17 +102,11 @@ This does the whole setup, not just the config file:
|
|
|
126
102
|
- **`.husky/pre-push`** — created or appended to, *if* you already have
|
|
127
103
|
Husky. If you don't, `init` tells you so instead of silently writing to
|
|
128
104
|
the untracked, not-shared-with-your-team `.git/hooks/pre-push`.
|
|
105
|
+
- **`package.json` scripts** — `land`, `sync`, `promote`, `preview`, and
|
|
106
|
+
`preview:restore` added, skipping any you've already defined yourself.
|
|
129
107
|
|
|
130
|
-
**Commit everything it wrote
|
|
131
|
-
|
|
132
|
-
"scripts": {
|
|
133
|
-
"land": "lanekeeper land",
|
|
134
|
-
"sync": "lanekeeper sync",
|
|
135
|
-
"promote": "lanekeeper promote",
|
|
136
|
-
"preview": "lanekeeper preview",
|
|
137
|
-
"preview:restore": "lanekeeper preview --restore"
|
|
138
|
-
}
|
|
139
|
-
```
|
|
108
|
+
**Commit everything it wrote**, then you're running. Two steps, not a setup
|
|
109
|
+
guide.
|
|
140
110
|
|
|
141
111
|
If `init` couldn't detect a `checkCommand` (no matching script in
|
|
142
112
|
package.json), every push is **blocked** until you set one — see 🧰 What's
|
package/dist/bin/lanekeeper.js
CHANGED
|
@@ -16,7 +16,7 @@ import { runWorktreeCreateHook } from "../hooks/worktree-create.js";
|
|
|
16
16
|
import { lanePort } from "../lib/lane-port.js";
|
|
17
17
|
import { claudeMdSnippet, MARKER } from "../lib/claude-md-snippet.js";
|
|
18
18
|
import { detectCheckCommand, runCheckCommand } from "../lib/check-command.js";
|
|
19
|
-
import { wireClaudeSettings, wireHuskyPrePush, ensureHooksPath } from "../lib/wire-hooks.js";
|
|
19
|
+
import { wireClaudeSettings, wireHuskyPrePush, ensureHooksPath, wirePackageJsonScripts } from "../lib/wire-hooks.js";
|
|
20
20
|
import { resolveMainCheckout } from "../lib/main-checkout.js";
|
|
21
21
|
import { pruneLandedLanes } from "../lib/prune-lanes.js";
|
|
22
22
|
const [, , command, ...rest] = process.argv;
|
|
@@ -139,13 +139,27 @@ export default ${JSON.stringify(generated, null, 2)};
|
|
|
139
139
|
break;
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
|
+
const scriptsResult = wirePackageJsonScripts(root);
|
|
143
|
+
switch (scriptsResult.result) {
|
|
144
|
+
case "added":
|
|
145
|
+
console.log(`lanekeeper init: added "${scriptsResult.added.join('", "')}" to package.json scripts.`);
|
|
146
|
+
break;
|
|
147
|
+
case "already-wired":
|
|
148
|
+
console.log("lanekeeper init: package.json already has all five scripts — leaving them alone.");
|
|
149
|
+
break;
|
|
150
|
+
case "no-package-json":
|
|
151
|
+
console.log("lanekeeper init: no package.json found — scripts NOT wired automatically.");
|
|
152
|
+
console.log(' Add "land"/"sync"/"promote"/"preview"/"preview:restore" -> "lanekeeper <name>" yourself.');
|
|
153
|
+
break;
|
|
154
|
+
case "unparseable":
|
|
155
|
+
console.log("lanekeeper init: package.json exists but isn't valid JSON — left untouched. Wire the scripts manually.");
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
142
158
|
console.log("");
|
|
143
159
|
console.log("Next steps:");
|
|
144
|
-
console.log(" 1.
|
|
145
|
-
console.log("
|
|
146
|
-
console.log(
|
|
147
|
-
console.log(" 3. Commit lanekeeper.config.mjs, CLAUDE.md, .claude/settings.json, and .husky/pre-push.");
|
|
148
|
-
console.log(" 4. claude --worktree <name> — the agent takes it from there.");
|
|
160
|
+
console.log(" 1. Commit everything it wrote — lanekeeper.config.mjs, CLAUDE.md, .claude/settings.json,");
|
|
161
|
+
console.log(" .husky/pre-push, and package.json.");
|
|
162
|
+
console.log(" 2. claude --worktree <name> — the agent takes it from there.");
|
|
149
163
|
}
|
|
150
164
|
async function main() {
|
|
151
165
|
switch (command) {
|
package/dist/lib/wire-hooks.d.ts
CHANGED
|
@@ -24,3 +24,16 @@ export type HooksPathResult = "set" | "already-set" | "custom-path";
|
|
|
24
24
|
* husky's own next real install corrects it to `.husky/_`.
|
|
25
25
|
*/
|
|
26
26
|
export declare function ensureHooksPath(root: string): HooksPathResult;
|
|
27
|
+
export type ScriptsWireResult = "added" | "already-wired" | "unparseable" | "no-package-json";
|
|
28
|
+
/**
|
|
29
|
+
* The last "copy this yourself" step `init` used to leave on the table:
|
|
30
|
+
* Quickstart told you to hand-add five scripts to package.json instead of
|
|
31
|
+
* just adding them. Same additive/idempotent contract as the rest of this
|
|
32
|
+
* file — only ever fills in scripts that don't exist yet, never overwrites
|
|
33
|
+
* one you've customized (e.g. if `land` already runs something of yours
|
|
34
|
+
* first), and does nothing if all five are already there.
|
|
35
|
+
*/
|
|
36
|
+
export declare function wirePackageJsonScripts(root: string): {
|
|
37
|
+
result: ScriptsWireResult;
|
|
38
|
+
added: string[];
|
|
39
|
+
};
|
package/dist/lib/wire-hooks.js
CHANGED
|
@@ -15,6 +15,13 @@ import { dirname, join } from "node:path";
|
|
|
15
15
|
import { fileURLToPath } from "node:url";
|
|
16
16
|
const HOOK_COMMAND = "npx lanekeeper hook worktree-create";
|
|
17
17
|
const PRE_PUSH_MARKER = "lanekeeper check-push";
|
|
18
|
+
const PACKAGE_SCRIPTS = {
|
|
19
|
+
land: "lanekeeper land",
|
|
20
|
+
sync: "lanekeeper sync",
|
|
21
|
+
promote: "lanekeeper promote",
|
|
22
|
+
preview: "lanekeeper preview",
|
|
23
|
+
"preview:restore": "lanekeeper preview --restore",
|
|
24
|
+
};
|
|
18
25
|
export function wireClaudeSettings(root) {
|
|
19
26
|
const dir = join(root, ".claude");
|
|
20
27
|
const path = join(dir, "settings.json");
|
|
@@ -121,3 +128,35 @@ export function ensureHooksPath(root) {
|
|
|
121
128
|
execFileSync("git", ["config", "core.hooksPath", ".husky"], { cwd: root });
|
|
122
129
|
return "set";
|
|
123
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* The last "copy this yourself" step `init` used to leave on the table:
|
|
133
|
+
* Quickstart told you to hand-add five scripts to package.json instead of
|
|
134
|
+
* just adding them. Same additive/idempotent contract as the rest of this
|
|
135
|
+
* file — only ever fills in scripts that don't exist yet, never overwrites
|
|
136
|
+
* one you've customized (e.g. if `land` already runs something of yours
|
|
137
|
+
* first), and does nothing if all five are already there.
|
|
138
|
+
*/
|
|
139
|
+
export function wirePackageJsonScripts(root) {
|
|
140
|
+
const path = join(root, "package.json");
|
|
141
|
+
if (!existsSync(path))
|
|
142
|
+
return { result: "no-package-json", added: [] };
|
|
143
|
+
let pkg;
|
|
144
|
+
try {
|
|
145
|
+
pkg = JSON.parse(readFileSync(path, "utf8"));
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return { result: "unparseable", added: [] };
|
|
149
|
+
}
|
|
150
|
+
pkg.scripts ??= {};
|
|
151
|
+
const added = [];
|
|
152
|
+
for (const [name, command] of Object.entries(PACKAGE_SCRIPTS)) {
|
|
153
|
+
if (!(name in pkg.scripts)) {
|
|
154
|
+
pkg.scripts[name] = command;
|
|
155
|
+
added.push(name);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (added.length === 0)
|
|
159
|
+
return { result: "already-wired", added: [] };
|
|
160
|
+
writeFileSync(path, JSON.stringify(pkg, null, 2) + "\n");
|
|
161
|
+
return { result: "added", added };
|
|
162
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lanekeeper",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "The local, zero-cost merge queue for parallel Claude Code agents. Plugs into Claude Code's native worktree isolation; one build at a time, one landing at a time, zero races.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|