Repository navigation
Expand file tree
/
Copy pathindex.html
More file actions
320 lines (303 loc) · 27.2 KB
/
Copy pathindex.html
File metadata and controls
320 lines (303 loc) · 27.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
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
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
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>nightqueue — issues in, pull requests with proof out</title>
<meta name="description" content="A local queue of coding agents that fixes and proves each job — reproducing and root-causing the risky ones. Runs on Claude Code, on your machine.">
<meta property="og:title" content="nightqueue — issues in, pull requests with proof out">
<meta property="og:description" content="A local queue of coding agents that fixes and proves each job — reproducing and root-causing the risky ones. Runs on Claude Code, on your machine.">
<meta property="og:image" content="https://nightqueue.github.io/og.png">
<meta property="og:url" content="https://nightqueue.github.io/">
<meta name="twitter:card" content="summary_large_image">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='6' fill='%230b0d14'/%3E%3Ctext x='16' y='22' font-family='monospace' font-size='16' font-weight='700' fill='%238cc8a0' text-anchor='middle'%3Enq%3C/text%3E%3C/svg%3E">
<style>
:root{--bg:#0b0d14;--bg2:#12151e;--line:#252a38;--fg:#f5f5f0;--muted:#a0a4b0;--dim:#6b7080;--acc:#8cc8a0;--acc2:#5aa07a}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;background:var(--bg);color:var(--fg);font:16px/1.6 ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;-webkit-font-smoothing:antialiased}
code,pre,.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
a{color:var(--acc);text-decoration:none}a:hover{text-decoration:underline}
.wrap{max-width:960px;margin:0 auto;padding:0 20px}
header{display:flex;align-items:center;justify-content:space-between;padding:22px 0}
.brand{font-weight:700;font-size:20px;color:var(--fg)}
nav a{color:var(--muted);margin-left:22px;font-size:15px}nav a:hover{color:var(--fg);text-decoration:none}
.hero{padding:72px 0 48px}
.eyebrow{display:inline-block;font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--acc);border:1px solid var(--line);border-radius:999px;padding:4px 12px;margin-bottom:22px}
h1{font-size:clamp(34px,6vw,58px);line-height:1.08;margin:0 0 18px;letter-spacing:-.02em}
.lead{font-size:20px;color:var(--muted);max-width:640px;margin:0 0 34px}
.lead-2{font-size:16px;color:var(--dim);max-width:640px;margin:-18px 0 34px}
.surfaces{margin-top:28px}
.surfaces .grid{grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px}
.surfaces .card{padding:16px 18px}
.surfaces .card h3{font-size:15px}.surfaces .card p{font-size:14px}
.surfaces .note{color:var(--dim);font-size:14px;margin:12px 0 0}
.proof{white-space:pre-wrap;overflow-wrap:anywhere}
.proof-src{color:var(--muted);font-size:15px;margin:0 0 14px}
.install{background:var(--bg2);border:1px solid var(--line);border-radius:10px;max-width:560px}
.install .tabs{display:flex;gap:2px;padding:6px 8px 0;border-bottom:1px solid var(--line)}
.install .tabs button{background:transparent;border:0;border-bottom:2px solid transparent;color:var(--dim);padding:6px 10px 8px;margin-bottom:-1px;cursor:pointer;font:13px ui-monospace,Menlo,monospace}
.install .tabs button:hover{color:var(--muted)}
.install .tabs button[aria-selected="true"]{color:var(--fg);border-bottom-color:var(--acc)}
.install .row{display:flex;align-items:center;gap:12px;padding:14px 16px}
.install code{flex:1;color:var(--acc);font-size:16px;overflow:auto;white-space:nowrap}
.install .row button{background:transparent;border:1px solid var(--line);color:var(--muted);border-radius:6px;padding:6px 10px;cursor:pointer;font-size:13px}
.install .row button:hover{color:var(--fg);border-color:var(--dim)}
.req{color:var(--dim);font-size:14px;margin-top:12px}
.pipe{margin:56px 0 0;background:var(--bg2);border:1px solid var(--line);border-radius:12px;padding:14px 8px 18px}
.pipe-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch}
.pipeline{display:block;width:100%;min-width:900px;height:auto}
.pipe p{padding:0 16px}
.pipeline .spin{transition:opacity .2s}.pipeline .spin.on{animation:spin 1.1s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.pipeline text{transition:opacity .2s}
.brand{display:flex;align-items:center;gap:10px}.brand img{width:28px;height:28px;border-radius:7px;display:block}
.pipe p{margin:14px 0 0;color:var(--muted);font-size:15px}
section{padding:56px 0;border-top:1px solid var(--line)}
h2{font-size:26px;margin:0 0 26px;letter-spacing:-.01em}
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:18px}
.card{background:var(--bg2);border:1px solid var(--line);border-radius:12px;padding:22px}
.card h3{margin:0 0 8px;font-size:17px}.card p{margin:0;color:var(--muted);font-size:15px}
table{width:100%;border-collapse:collapse;font-size:15px}
th,td{text-align:left;padding:12px 14px;border-bottom:1px solid var(--line);vertical-align:top}
th{color:var(--dim);font-weight:600;font-size:13px;text-transform:uppercase;letter-spacing:.06em}
td:first-child{color:var(--muted)}td:last-child{color:var(--fg)}
pre{background:var(--bg2);border:1px solid var(--line);border-radius:10px;padding:18px 20px;overflow:auto;font-size:14px;line-height:1.7;color:var(--fg);margin:0}
pre .c{color:var(--dim)}pre .k{color:var(--acc)}
.status{background:var(--bg2);border:1px solid var(--line);border-left:3px solid var(--acc2);border-radius:10px;padding:18px 20px;color:var(--muted);font-size:15px}
footer{padding:36px 0 56px;color:var(--dim);font-size:14px;border-top:1px solid var(--line)}
footer a{color:var(--muted)}
@media (max-width:600px){nav a{margin-left:14px;font-size:14px}.hero{padding:44px 0 36px}.install .row code{white-space:normal;font-size:15px}}
</style>
</head>
<body>
<div class="wrap">
<header>
<a class="brand mono" href="/"><img src="avatar.png" alt="" width="28" height="28">nightqueue</a>
<nav>
<a href="https://github.com/nightqueue/nightqueue">GitHub</a>
<a href="https://www.npmjs.com/package/@nightqueue/nq">npm</a>
<a href="https://github.com/nightqueue/nightqueue/tree/main/docs">Docs</a>
</nav>
</header>
<div class="hero">
<div class="eyebrow">Runs on Claude Code · local · BUSL-1.1</div>
<h1>Issues in. Pull requests with proof out.</h1>
<p class="lead">Every job is proved before it ships. Bugs and risky changes are reproduced first, root-caused, fixed and attacked by adversarial QA, then checked at runtime; a docs or config change just runs your project's own checks. The pull request ships with Report → Cause → Changes → QA. A job that can't prove its fix stops at a gate with a written notice instead of opening a PR.</p>
<p class="lead-2">Queue the work during the day. Start the batch when you step away. Come back to pull requests — and to an agent that remembers what it learned last night.</p>
<div class="install">
<div class="tabs" role="tablist" aria-label="package manager">
<button type="button" role="tab" data-pm="npm" aria-selected="true">npm</button>
<button type="button" role="tab" data-pm="pnpm" aria-selected="false">pnpm</button>
<button type="button" role="tab" data-pm="yarn" aria-selected="false">yarn</button>
</div>
<div class="row">
<code id="cmd">npx @nightqueue/nq init</code>
<button type="button" id="copy" onclick="navigator.clipboard.writeText(document.getElementById('cmd').textContent).then(()=>{this.textContent='copied';setTimeout(()=>this.textContent='copy',1400)})">copy</button>
</div>
</div>
<p class="req">Needs <a href="https://claude.com/claude-code">Claude Code</a> with your own subscription, Node ≥ 22 and <code>gh</code>. Nothing leaves your machine.</p>
<div class="surfaces">
<div class="grid">
<div class="card"><h3>CLI</h3><p><code>nq</code> in your terminal</p></div>
<div class="card"><h3>Studio</h3><p>the queue, jobs and settings in your browser — <code>nq studio</code></p></div>
<div class="card"><h3>MCP</h3><p>talk to it from Claude Code and Claude Desktop</p></div>
</div>
<p class="note">Same queue, same pipeline, same memory underneath.</p>
</div>
<div class="pipe">
<div class="pipe-scroll">
<svg class="pipeline" viewBox="0 0 1180 210" width="1180" height="210" role="img" aria-label="A job moves through the pipeline: queue add, triage, explore, architect, implement, adversarial QA, verify, runtime check, pull request. Each stage works, then approves, then hands the job on." xmlns="http://www.w3.org/2000/svg">
<line x1="70.0" y1="78" x2="1110.0" y2="78" stroke="#252a38" stroke-width="2"/>
<line id="pp" x1="70.0" y1="78" x2="70.0" y2="78" stroke="#3a4050" stroke-width="2" stroke-linecap="round"/>
<line id="pg" x1="70.0" y1="78" x2="70.0" y2="78" stroke="#8cc8a0" stroke-width="2" stroke-linecap="round"/>
<circle id="ptok" cx="70.0" cy="78" r="7" fill="#c9ccd4" style="filter:drop-shadow(0 0 5px #c9ccd4)"/>
<text id="plog" x="70.0" y="36" text-anchor="middle" font-size="12" fill="#a0a4b0" font-family="ui-monospace,Menlo,monospace" opacity="0"></text>
<g class="node" data-i="0" data-lines="nq queue add "fix the flaky worker"|job #7 queued · tier: simple">
<circle class="ring" cx="70.0" cy="78" r="26" fill="#12151e" stroke="#252a38" stroke-width="2"/>
<circle class="spin" cx="70.0" cy="78" r="31" fill="none" stroke="#7d8598" stroke-width="2" stroke-dasharray="40 160" stroke-linecap="round" opacity="0" style="transform-origin:70.0px 78px"/>
<text x="70.0" y="86" text-anchor="middle" font-size="22">📥</text>
<g class="check" opacity="0"><circle cx="90.0" cy="58" r="10" fill="#8cc8a0"/><path d="M 85.0 58 l 4 4 l 7 -8" fill="none" stroke="#0b0d14" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></g>
<text x="70.0" y="128" text-anchor="middle" font-size="13" fill="#f5f5f0" font-family="ui-sans-serif,system-ui,sans-serif">queue add</text>
<text x="70.0" y="146" text-anchor="middle" font-size="12" fill="#6b7080" font-family="ui-monospace,Menlo,monospace">you</text>
</g>
<g class="node" data-i="1" data-lines="reading the request|grep -rn "worker" src/|reproducing on main|brief: root cause found">
<circle class="ring" cx="200.0" cy="78" r="26" fill="#12151e" stroke="#252a38" stroke-width="2"/>
<circle class="spin" cx="200.0" cy="78" r="31" fill="none" stroke="#7d8598" stroke-width="2" stroke-dasharray="40 160" stroke-linecap="round" opacity="0" style="transform-origin:200.0px 78px"/>
<text x="200.0" y="86" text-anchor="middle" font-size="22">🔎</text>
<g class="check" opacity="0"><circle cx="220.0" cy="58" r="10" fill="#8cc8a0"/><path d="M 215.0 58 l 4 4 l 7 -8" fill="none" stroke="#0b0d14" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></g>
<text x="200.0" y="128" text-anchor="middle" font-size="13" fill="#f5f5f0" font-family="ui-sans-serif,system-ui,sans-serif">triage</text>
<text x="200.0" y="146" text-anchor="middle" font-size="12" fill="#6b7080" font-family="ui-monospace,Menlo,monospace">triager</text>
</g>
<g class="node" data-i="2" data-lines="mapping src/queue/|index_recall runner|reading runner.mjs|file map: 14 files">
<circle class="ring" cx="330.0" cy="78" r="26" fill="#12151e" stroke="#252a38" stroke-width="2"/>
<circle class="spin" cx="330.0" cy="78" r="31" fill="none" stroke="#7d8598" stroke-width="2" stroke-dasharray="40 160" stroke-linecap="round" opacity="0" style="transform-origin:330.0px 78px"/>
<text x="330.0" y="86" text-anchor="middle" font-size="22">🧭</text>
<g class="check" opacity="0"><circle cx="350.0" cy="58" r="10" fill="#8cc8a0"/><path d="M 345.0 58 l 4 4 l 7 -8" fill="none" stroke="#0b0d14" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></g>
<text x="330.0" y="128" text-anchor="middle" font-size="13" fill="#f5f5f0" font-family="ui-sans-serif,system-ui,sans-serif">explore</text>
<text x="330.0" y="146" text-anchor="middle" font-size="12" fill="#6b7080" font-family="ui-monospace,Menlo,monospace">explore</text>
</g>
<g class="node" data-i="3" data-lines="decision_recall lease|plan: 3 steps|risk: lease race|03-plan.md written">
<circle class="ring" cx="460.0" cy="78" r="26" fill="#12151e" stroke="#252a38" stroke-width="2"/>
<circle class="spin" cx="460.0" cy="78" r="31" fill="none" stroke="#7d8598" stroke-width="2" stroke-dasharray="40 160" stroke-linecap="round" opacity="0" style="transform-origin:460.0px 78px"/>
<text x="460.0" y="86" text-anchor="middle" font-size="22">📐</text>
<g class="check" opacity="0"><circle cx="480.0" cy="58" r="10" fill="#8cc8a0"/><path d="M 475.0 58 l 4 4 l 7 -8" fill="none" stroke="#0b0d14" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></g>
<text x="460.0" y="128" text-anchor="middle" font-size="13" fill="#f5f5f0" font-family="ui-sans-serif,system-ui,sans-serif">architect</text>
<text x="460.0" y="146" text-anchor="middle" font-size="12" fill="#6b7080" font-family="ui-monospace,Menlo,monospace">architect</text>
</g>
<g class="node" data-i="4" data-lines="edit src/queue/runner.mjs|sed -i 's/lease/claim/'|npm test → 2104 pass|changes staged">
<circle class="ring" cx="590.0" cy="78" r="26" fill="#12151e" stroke="#252a38" stroke-width="2"/>
<circle class="spin" cx="590.0" cy="78" r="31" fill="none" stroke="#7d8598" stroke-width="2" stroke-dasharray="40 160" stroke-linecap="round" opacity="0" style="transform-origin:590.0px 78px"/>
<text x="590.0" y="86" text-anchor="middle" font-size="22">🛠️</text>
<g class="check" opacity="0"><circle cx="610.0" cy="58" r="10" fill="#8cc8a0"/><path d="M 605.0 58 l 4 4 l 7 -8" fill="none" stroke="#0b0d14" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></g>
<text x="590.0" y="128" text-anchor="middle" font-size="13" fill="#f5f5f0" font-family="ui-sans-serif,system-ui,sans-serif">implement</text>
<text x="590.0" y="146" text-anchor="middle" font-size="12" fill="#6b7080" font-family="ui-monospace,Menlo,monospace">coder</text>
</g>
<g class="node" data-i="5" data-lines="fuzz: empty queue|attack: killed runner|lease expiry ×3|verdict: APPROVED">
<circle class="ring" cx="720.0" cy="78" r="26" fill="#12151e" stroke="#252a38" stroke-width="2"/>
<circle class="spin" cx="720.0" cy="78" r="31" fill="none" stroke="#7d8598" stroke-width="2" stroke-dasharray="40 160" stroke-linecap="round" opacity="0" style="transform-origin:720.0px 78px"/>
<text x="720.0" y="86" text-anchor="middle" font-size="22">🛡️</text>
<g class="check" opacity="0"><circle cx="740.0" cy="58" r="10" fill="#8cc8a0"/><path d="M 735.0 58 l 4 4 l 7 -8" fill="none" stroke="#0b0d14" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></g>
<text x="720.0" y="128" text-anchor="middle" font-size="13" fill="#f5f5f0" font-family="ui-sans-serif,system-ui,sans-serif">adversarial QA</text>
<text x="720.0" y="146" text-anchor="middle" font-size="12" fill="#6b7080" font-family="ui-monospace,Menlo,monospace">qa-guardian</text>
</g>
<g class="node" data-i="6" data-lines="npm test|checks: 3/3 green|evidence collected|VERIFIED">
<circle class="ring" cx="850.0" cy="78" r="26" fill="#12151e" stroke="#252a38" stroke-width="2"/>
<circle class="spin" cx="850.0" cy="78" r="31" fill="none" stroke="#7d8598" stroke-width="2" stroke-dasharray="40 160" stroke-linecap="round" opacity="0" style="transform-origin:850.0px 78px"/>
<text x="850.0" y="86" text-anchor="middle" font-size="22">✅</text>
<g class="check" opacity="0"><circle cx="870.0" cy="58" r="10" fill="#8cc8a0"/><path d="M 865.0 58 l 4 4 l 7 -8" fill="none" stroke="#0b0d14" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></g>
<text x="850.0" y="128" text-anchor="middle" font-size="13" fill="#f5f5f0" font-family="ui-sans-serif,system-ui,sans-serif">verify</text>
<text x="850.0" y="146" text-anchor="middle" font-size="12" fill="#6b7080" font-family="ui-monospace,Menlo,monospace">verifier</text>
</g>
<g class="node" data-i="7" data-lines="real run: nq queue run|runner claims job|no gate raised|ok">
<circle class="ring" cx="980.0" cy="78" r="26" fill="#12151e" stroke="#252a38" stroke-width="2"/>
<circle class="spin" cx="980.0" cy="78" r="31" fill="none" stroke="#7d8598" stroke-width="2" stroke-dasharray="40 160" stroke-linecap="round" opacity="0" style="transform-origin:980.0px 78px"/>
<text x="980.0" y="86" text-anchor="middle" font-size="22">⚙️</text>
<g class="check" opacity="0"><circle cx="1000.0" cy="58" r="10" fill="#8cc8a0"/><path d="M 995.0 58 l 4 4 l 7 -8" fill="none" stroke="#0b0d14" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></g>
<text x="980.0" y="128" text-anchor="middle" font-size="13" fill="#f5f5f0" font-family="ui-sans-serif,system-ui,sans-serif">runtime check</text>
<text x="980.0" y="146" text-anchor="middle" font-size="12" fill="#6b7080" font-family="ui-monospace,Menlo,monospace">runtime</text>
</g>
<g class="node" data-i="8" data-lines="git push -u origin|gh pr create|PR #42 opened|lesson saved">
<circle class="ring" cx="1110.0" cy="78" r="26" fill="#12151e" stroke="#252a38" stroke-width="2"/>
<circle class="spin" cx="1110.0" cy="78" r="31" fill="none" stroke="#7d8598" stroke-width="2" stroke-dasharray="40 160" stroke-linecap="round" opacity="0" style="transform-origin:1110.0px 78px"/>
<text x="1110.0" y="86" text-anchor="middle" font-size="22">🔀</text>
<g class="check" opacity="0"><circle cx="1130.0" cy="58" r="10" fill="#8cc8a0"/><path d="M 1125.0 58 l 4 4 l 7 -8" fill="none" stroke="#0b0d14" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></g>
<text x="1110.0" y="128" text-anchor="middle" font-size="13" fill="#f5f5f0" font-family="ui-sans-serif,system-ui,sans-serif">pull request</text>
<text x="1110.0" y="146" text-anchor="middle" font-size="12" fill="#6b7080" font-family="ui-monospace,Menlo,monospace">done</text>
</g>
</svg>
</div>
<p>Every phase is a dedicated subagent with its own instructions, running in a git worktree it cannot leave. Each phase writes an artifact the next one is gated on; a job that needs a decision stops with a written notice, and one command sends it back.</p>
</div>
</div>
<section id="proof">
<h2>What a job hands back</h2>
<p class="proof-src">The body of a merged pull request opened by nightqueue, trimmed (…): <a href="https://github.com/nightqueue/nightqueue/pull/73">nightqueue/nightqueue#73</a>.</p>
<pre class="proof"><span class="k">## Report</span>
The close of PR `#72` stopped at preflight with `checks-red`. CI had two failures unrelated to that diff, each in a different check, and both were clock-sensitive flakes (CI run 37814924665):
- `test-worktree`: "queue_cancel with stop … release_worktree needs stop" failed with "the refused stop wrote to the row", because `attempt_rows[0].elapsed_s` went from 0 to 1.
…
<span class="k">## Cause</span>
- **B1:** `ATTEMPT_ROWS` derives `elapsed_s`/`end_epoch` from `datetime('now')` while an attempt is open. The test claims a job, which leaves an open attempt, and compares `getJob` before and after the refused cancel. The comparison breaks whenever a one-second boundary falls between the two reads.
…
<span class="k">## Changes</span>
- `test/mcp/server.test.mjs`: a `rowAtRest` helper drops only `elapsed_s`/`end_epoch` from `attempt_rows` before the before/after comparisons. Every other field is still compared, and the product SQL is untouched.
…
<span class="k">## QA</span>
<span class="k">### Automated</span>
- `npm test` (full suite) — PASSED ✅
- `node --test test/queue/close-steps.test.mjs` (85 tests, the close engine against the fake gh, covering re-run then merge, failed twice, non-Actions red, aborted re-run and `--force`) — PASSED ✅
…
Opened by nightqueue</pre>
</section>
<section id="what">
<h2>What it does</h2>
<div class="grid">
<div class="card"><h3>Root cause, with proof</h3><p>Your project's own checks run on every job. Higher risk tiers also reproduce the problem first, fix the cause, then face adversarial QA and a runtime check; the lowest tier runs implementation and verification only. The pull request carries the evidence, not a promise.</p></div>
<div class="card"><h3>From Linear</h3><p>Queue from a Linear issue link or <code>--origin linear:MK-42</code>. The runner reads the issue at claim. When the pull request merges, the close comments on the issue with the pull request, and moves it to the team's completed state only if it is not already completed or canceled and the team has a completed-type state.</p></div>
<div class="card"><h3>A backlog, not a chat</h3><p><code>nq queue add</code> records a deliverable; <code>nq queue run</code> works through the whole backlog unattended and comes back with one pull request per job.</p></div>
<div class="card"><h3>A memory that compounds</h3><p>Lessons, project decisions and structural indexes survive between runs — hybrid recall (BM25 + optional local embeddings), scoped per project and per org. Every run makes the next one cheaper.</p></div>
<div class="card"><h3>Human gates, not silent failures</h3><p>A job that needs a call stops with a written notice. <code>nq queue retry <id> --note "…"</code> answers it and the job goes back to the queue.</p></div>
<div class="card"><h3>Orgs and projects</h3><p>One home, many repositories, grouped by org — each with its own decisions log and roadmap, exposed to Claude Code and Claude Desktop through an MCP server.</p></div>
<div class="card"><h3>Inspectable, resumable</h3><p>Every phase hands off through a file. A run can be read, resumed and audited after the fact; <code>nq queue session <id></code> reopens the conversation that did the work.</p></div>
</div>
</section>
<section id="why">
<h2>Why not just a chat session</h2>
<table>
<thead><tr><th></th><th>A chat session</th><th>nightqueue</th></tr></thead>
<tbody>
<tr><td>Who drives</td><td>you, prompt by prompt</td><td>the pipeline, phase by phase</td></tr>
<tr><td>Root cause</td><td>fixes what you described</td><td>reproduces first and fixes the cause on bugs and risky changes; proves every job</td></tr>
<tr><td>Memory</td><td>gone when the window closes</td><td>lessons and decisions persist per org</td></tr>
<tr><td>Quality gate</td><td>whatever you remember to ask</td><td>your project's own checks on every run; adversarial QA on bugs and risky changes</td></tr>
<tr><td>Output</td><td>a diff in a terminal</td><td>a reviewable pull request with a report</td></tr>
<tr><td>When it runs</td><td>while you watch</td><td>while you sleep</td></tr>
</tbody>
</table>
</section>
<section id="start">
<h2>A first night</h2>
<pre><span class="c"># once</span>
<span class="pm-cmd">npx @nightqueue/nq init</span> <span class="c"># runtime, commands, MCP server, Claude Code plugin</span>
cd your-repo && nightqueue setup <span class="c"># register the project</span>
<span class="c"># the day</span>
nq queue add "fix the flaky worker"
nq queue add "add the retry to the uploader"
nq queue run <span class="c"># start the batch, detached, when you step away</span>
<span class="c"># the morning</span>
nq queue status <span class="c"># what each job became</span>
nq queue retry 7 --note "rename the column" <span class="c"># answer a gate</span>
nq queue close 8 <span class="c"># merge its pull request through the closing pipeline</span></pre>
<p style="color:var(--muted);margin-top:16px">Inside Claude Code, <code>/nightqueue:queue</code> turns the plan under discussion into a job and <code>/nightqueue:resolve</code> runs the pipeline on the spot. <code>nightqueue open</code> starts the operator: investigate, triage and prepare a job without knowing any of the above.</p>
</section>
<section id="status">
<h2>Status</h2>
<div class="status">
Experimental, pre-1.0, used daily on real repositories by its author. The CLI, the MCP tools and the on-disk formats can still change between minor versions; every change a user would notice is in the <a href="https://github.com/nightqueue/nightqueue/blob/main/CHANGELOG.md">changelog</a>. Free and local. A hosted <strong>Team</strong> tier — one shared queue and memory per org, runners on your team's machines — is planned; the local product stays free and is never degraded to push it.
</div>
</section>
<footer>
<div><a href="https://github.com/nightqueue/nightqueue">Source</a> · <a href="https://github.com/nightqueue/nightqueue/issues">Issues</a> · <a href="https://github.com/nightqueue/nightqueue/blob/main/CONTRIBUTING.md">Contributing</a> · <a href="https://github.com/nightqueue/nightqueue/blob/main/SECURITY.md">Security</a> · <a href="https://github.com/nightqueue/nightqueue/blob/main/LICENSE">BUSL-1.1</a>, Apache 2.0 on 2029-09-04</div>
</footer>
</div>
<script>
(function(){
const CMD={npm:'npx @nightqueue/nq init',pnpm:'pnpm dlx @nightqueue/nq init',yarn:'yarn dlx @nightqueue/nq init'};
const tabs=[...document.querySelectorAll('.install .tabs [data-pm]')], cmd=document.getElementById('cmd'), pres=[...document.querySelectorAll('.pm-cmd')];
function pick(pm){ if(!CMD[pm]) pm='npm';
tabs.forEach(t=>t.setAttribute('aria-selected',String(t.dataset.pm===pm)));
cmd.textContent=CMD[pm]; pres.forEach(el=>el.textContent=CMD[pm]);
try{localStorage.setItem('nq-pm',pm)}catch(e){} }
tabs.forEach(t=>t.addEventListener('click',()=>pick(t.dataset.pm)));
let saved=null; try{saved=localStorage.getItem('nq-pm')}catch(e){}
if(saved&&saved!=='npm') pick(saved);
})();
</script>
<script>
(function(){
const svg=document.querySelector('svg.pipeline'); if(!svg) return;
const nodes=[...svg.querySelectorAll('.node')], tok=svg.querySelector('#ptok'), pn=svg.querySelector('#pp'), pg=svg.querySelector('#pg'), log=svg.querySelector('#plog');
const xs=nodes.map(n=>+n.querySelector('.ring').getAttribute('cx'));
const NEUTRAL='#7d8598', GREEN='#8cc8a0', IDLE='#252a38';
const reduce=matchMedia('(prefers-reduced-motion: reduce)').matches;
const sleep=ms=>new Promise(r=>setTimeout(r,ms));
function setNode(n,state){ const ring=n.querySelector('.ring'),spin=n.querySelector('.spin'),chk=n.querySelector('.check');
ring.setAttribute('stroke', state==='work'?NEUTRAL: state==='done'?GREEN:IDLE);
spin.setAttribute('opacity', state==='work'?'1':'0'); spin.classList.toggle('on',state==='work');
chk.setAttribute('opacity', state==='done'?'1':'0'); }
function place(x){ tok.setAttribute('cx',x); pn.setAttribute('x2',x); log.setAttribute('x',x); }
if(reduce){ nodes.forEach(n=>setNode(n,'done')); place(xs[xs.length-1]); pg.setAttribute('x2',xs[xs.length-1]); return; }
for(const el of [tok,pn,pg]) el.style.transition=(el===tok?'cx':'x2')+' .55s cubic-bezier(.4,0,.2,1)';
log.style.transition='opacity .2s';
async function run(){ for(;;){ nodes.forEach(n=>setNode(n,'idle')); place(xs[0]); pg.setAttribute('x2',xs[0]);
for(let i=0;i<nodes.length;i++){ const n=nodes[i]; place(xs[i]); await sleep(i?600:200);
setNode(n,'work'); const lines=n.dataset.lines.split('|');
for(const l of lines){ log.setAttribute('opacity','0'); await sleep(120); log.textContent=l; log.setAttribute('opacity','1'); await sleep(520); }
setNode(n,'done'); pg.setAttribute('x2',xs[i]); await sleep(450); log.setAttribute('opacity','0'); }
await sleep(1600); } }
run();
})();
</script>
</body>
</html>