diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c686a8b..6059f56 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,9 +2,7 @@ # so a regression in capture/redact/memory only surfaces at a later empty /distill. name: test -# CI gates `main` and checks autonomous-agent pushes. The nightshift fleet opens one -# PR per task into `nightshift`, and its local merge gate already replicates the suite -# per branch — so nightshift task branches stay CI-free. `claude/**` branches (Claude +# CI gates `main` and checks autonomous-agent pushes. `claude/**` branches (Claude # Code web/agent sessions) have no local gate, so their pushes run the suite too and # failures wake the session that owns the branch. on: diff --git a/.gitignore b/.gitignore index f671a28..0ed5067 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ *.pglite .DS_Store -# nightshift runtime: dashboard status.json, served index.html, worker logs, pytest venv +# legacy fleet runtime dirs still present in older clones .nightshift/ # scratch artifacts (dogfood screenshots, throwaway evidence) — attach to PRs, never commit .context/ diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 03e6ac7..8ce3434 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -32,7 +32,6 @@ changelog: sort: asc filters: exclude: - - '^nightshift: merge ' - '^Merge ' - '^docs:' - '^test:' diff --git a/README.md b/README.md index 3948fe8..1556333 100644 --- a/README.md +++ b/README.md @@ -19,13 +19,11 @@ · Daily Use · - nightshift - · Design

- devbrain dashboard tour — the Board, the Profile, and the Nightshift cockpit + devbrain dashboard tour — the Board and the Profile

AI coding agents forget: close the chat, switch machines, or change tools, and everything @@ -44,8 +42,7 @@ Brain durable project memory, git-synced across every session and machine ├─ Capture every prompt → raw markdown log automatic, model-free · source of truth ├─ Distill /distill → brain pages + queue searchable, rebuildable ├─ Queue what should happen next one markdown file per task - ├─ Sync many agents, one brain keyed by git remote - └─ Nightshift work the queue while you're away grounded in current context + └─ Sync many agents, one brain keyed by git remote ``` `devbrain install` wires *this machine*: a one-minute timer sweeps every new Claude Code @@ -69,12 +66,11 @@ No Homebrew? `go install github.com/TheWeiHu/devbrain/cmd/devbrain@latest` or gr `devbrain install` is idempotent and wires only *this machine*. In a terminal it asks y/n per component; non-interactive runs take every default. When it finishes it opens -the browser dashboard (`devbrain dashboard` — Board · Nightshift · Profile). Common flags: +the browser dashboard (`devbrain dashboard` — Board · Profile). Common flags: ```bash devbrain install --dry-run # preview every path it would touch; write nothing devbrain install --explain # dry-run plus a one-line why per action -devbrain install --without nightshift # skip the overnight loop devbrain install --only capture # just capture (sweep + gbrain trace) devbrain install --no-open # don't auto-open the dashboard DEVBRAIN_DATA=~/path devbrain install # store the brain elsewhere @@ -163,9 +159,6 @@ ownership is tracked per project, so a mixed-project session can span brains. | *(automatic)* | every prompt swept from agent transcripts each minute; committed and pushed every 15 | | **`/distill`** | fold new log → brain pages **and** queue tasks | | **`/continue`** | resume from memory, then work the top queue task into a small PR | -| **`/work`** | work the top task into a small PR without refreshing memory first (for `/loop` + nightshift) | -| **`/loop /work`** | headless drain: one small PR per task | -| **`/loop /continue`** | interactive resume+drain: same, but folds new log into the brain each turn | | **`/reconcile`** | mark brain facts the live repo contradicts (auto-runs ~daily) | | **`/audit`** | spot-check recent delegated runs for protocol drift (auto-runs ~daily) | | **`/journal`** | dated recap of the last N days across every project (cached per day) | @@ -177,24 +170,7 @@ ownership is tracked per project, so a mixed-project session can span brains. The queue is one markdown file per task, priority-ranked; a task isn't `done` until its PR merges. Agents without slash commands run the same workflows as skills (`$distill`, -`$continue`, `$work`, `$reconcile`, `$audit`). - -## nightshift - -Runs several workers in parallel against the queue — `claude` by default, or a mixed -fleet with `--agents claude=2,codex=2` (codex is headless-only) — each in its own -worktree, grounded in current project memory, auto-merging green work onto a throwaway -`nightshift` branch — you wake to one `git diff main...nightshift`. - -```bash -devbrain nightshift start ~/nightshift/myrepo # launch the fleet (runs until stopped) -devbrain nightshift watch # live browser dashboard -devbrain nightshift stop # stop the fleet -``` - -It never merges to `main`. Installing it spawns nothing — the fleet runs only when you -start it, and it does autonomous git ops and spends real tokens, so point the first runs -at a throwaway. You stay the only `nightshift → main` gate. +`$continue`, `$reconcile`, `$audit`). ## More diff --git a/assets/dashboard.css b/assets/dashboard.css index 27ce692..2308511 100644 --- a/assets/dashboard.css +++ b/assets/dashboard.css @@ -271,120 +271,6 @@ small.k{color:var(--muted)} .viewseg button:hover{color:var(--text);background:transparent} .viewseg button.active{background:#3A3A3C;color:var(--text);box-shadow:0 1px 2px rgba(0,0,0,.4)} -/* nightshift monitor — regressed toward the original: token chart, agent terminals, logs, merges */ -#monitor{font-family:var(--mono)} -.ns-run{margin-bottom:22px} -.ns-head{display:flex;flex-direction:column;align-items:center;gap:8px;margin:6px 0 14px;padding-bottom:14px;border-bottom:1px solid var(--line2);font-family:system-ui;text-align:center} -.ns-head-row{display:flex;align-items:baseline;justify-content:center;gap:10px;flex-wrap:wrap} -.ns-head-row.ns-controls{align-items:center} -.ns-head h2{font-size:20px;margin:0;font-weight:700;letter-spacing:-.01em} -/* Header controls are one chip family — same height/radius/border/bg on the - status pill, the stepper, and Stop; color shows up only as text + dot. */ -.ns-stop{height:26px;box-sizing:border-box;padding:0 12px;border-radius:8px;font-size:12px;font-weight:600;cursor:pointer;color:var(--danger);background:var(--panel2);border:1px solid var(--line);transition:all .12s} -.ns-stop:hover{background:color-mix(in srgb,var(--danger) 12%,var(--panel2));border-color:color-mix(in srgb,var(--danger) 45%,var(--line))} -.ns-stop:disabled{opacity:.6;cursor:default} -.ns-scale{display:inline-flex;align-items:stretch;height:26px;box-sizing:border-box;border:1px solid var(--line);border-radius:8px;background:var(--panel2);overflow:hidden} -.ns-scale-btn{width:30px;border:none;background:transparent;color:var(--muted);font-size:16px;line-height:1;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;transition:background .12s,color .12s} -.ns-scale-btn:hover:not(:disabled){background:var(--line);color:var(--text)} -.ns-scale-btn:disabled{opacity:.3;cursor:default} -.ns-scale-n{display:inline-flex;align-items:center;justify-content:center;min-width:74px;font-size:13px;font-weight:600;color:var(--text);border-left:1px solid var(--line);border-right:1px solid var(--line);font-variant-numeric:tabular-nums} -.ns-scale-u{color:var(--muted);font-size:11px;font-weight:400;margin-left:3px} -.ns-scale-fixed{align-items:center;padding:0 12px;font-size:13px;font-weight:600;color:var(--muted);cursor:default} -.ns-title{align-items:center;gap:9px} -.ns-started{color:var(--muted);font-size:11px;font-variant-numeric:tabular-nums} -.ns-caption{margin-top:-4px} -.ns-backoff{margin:0 auto 12px;padding:8px 13px;border:1px solid var(--warn);border-radius:8px;background:var(--panel2);color:var(--warn);font-size:12px;width:fit-content;max-width:100%} -.ns-backoff .when{color:var(--muted);margin-left:6px;font-variant-numeric:tabular-nums} -.ns-upd{display:inline-flex;align-items:center;gap:6px;min-height:26px;box-sizing:border-box;padding:2px 11px;border-radius:8px;line-height:1.4;font-size:11px;font-weight:600;font-variant-numeric:tabular-nums;color:var(--muted);background:var(--panel2);border:1px solid var(--line);transition:color .2s} -.ns-upd.live{color:var(--ok)} -.ns-upd.stale{color:var(--danger)} -.ns-live-dot{width:6px;height:6px;border-radius:50%;background:var(--muted);flex:0 0 auto;transition:background .2s} -.ns-live-dot.live{background:var(--ok)} -.ns-live-dot.stale{background:var(--danger)} -.ns-stats{display:flex;flex-wrap:wrap;width:fit-content;max-width:100%;margin:0 auto 12px;border:1px solid var(--line);border-radius:8px;overflow:hidden;background:var(--panel2)} -.ns-stat{border-right:1px solid var(--line2);padding:8px 16px;min-width:92px;text-align:center} -.ns-stat:last-child{border-right:0} -.ns-stat .n{font-size:17px;font-weight:600;font-family:var(--mono);font-variant-numeric:tabular-nums} -.ns-stat .l{color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.04em;margin-top:2px} -.ns-panel{background:var(--panel);border:1px solid var(--line);border-radius:12px;margin-bottom:12px} -.ns-panel>h3,.ns-term>h3{margin:0;padding:9px 13px;border-bottom:1px solid var(--line);font-family:system-ui} -.ns-panel>h3{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--muted)} -.ns-chart{width:100%;display:block;height:180px;padding:8px} -.ns-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(420px,1fr));gap:12px;margin-bottom:12px} -.ns-term{background:var(--panel);border:1px solid var(--line);border-radius:12px;overflow:hidden} -.ns-term>h3{font-size:12px;display:flex;align-items:center;gap:8px;flex-wrap:nowrap;min-width:0} -.ns-dot{width:8px;height:8px;border-radius:50%;background:var(--muted);flex:0 0 auto} -.ns-dot.working{background:var(--ok);box-shadow:0 0 8px var(--ok);animation:nspulse 1.4s infinite} -@keyframes nspulse{50%{opacity:.4}} -.ns-task{color:var(--accent);font-family:var(--mono);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0} -.ns-rate{color:var(--accent);font-size:11px;margin-left:auto;white-space:nowrap;flex:0 0 auto} -.ns-state{color:var(--muted);font-size:11px;white-space:nowrap;flex:0 0 auto} -.ns-msgs{max-height:440px;overflow:auto;background:var(--bg)} -.ns-msg{padding:7px 13px;border-bottom:1px solid var(--line2);white-space:pre-wrap;word-break:break-word;font-size:12px;line-height:1.5} -.ns-msg:last-child{border-bottom:none} -.ns-ts{color:var(--muted);font-size:10px;margin-right:8px;user-select:none} -.ns-mtext{color:var(--text)} -.ns-sep{display:flex;align-items:center;gap:8px;padding:3px 13px;color:var(--muted);font-size:9px;letter-spacing:.6px;text-transform:uppercase;user-select:none} -.ns-sep::before,.ns-sep::after{content:"";flex:1;height:1px;background:var(--line2)} -.ns-empty{padding:14px 13px;color:var(--muted);font-style:italic} -.ns-log{margin:0;padding:11px 13px;max-height:260px;overflow:auto;white-space:pre-wrap;word-break:break-word; - color:#d2d2d7;font-size:12px;background:var(--bg);font-family:var(--mono)} -.ns-merge{color:var(--ok)} -.ns-cols{display:flex;gap:12px;flex-wrap:wrap} -.ns-cols .ns-panel{flex:1 1 360px;margin-bottom:0} - -/* ── select tasks → drag to the 🌙 → run nightshift on just those ───────────── */ -.card{position:relative} -.card .chips{padding-right:28px} /* leave room for the select dot */ -/* Select dot: a no-modifier toggle on every card. Faint hollow circle, brightens on hover, - fills amber with a ✓ when picked. Click it to select; click the card body still opens edit. - 22px visual, but ::before extends an invisible ~44px hit area so it's easy to hit (Fitts's law). */ -.card .seldot{position:absolute;top:6px;right:6px;width:22px;height:22px;border-radius:50%;padding:0;z-index:3; - border:1.5px solid var(--line2);background:rgba(20,20,22,.6);cursor:pointer; - display:flex;align-items:center;justify-content:center;font:700 13px/1 var(--mono);color:transparent; - opacity:.45;transition:opacity .12s,background .12s,border-color .12s,transform .1s} -.card .seldot::before{content:"";position:absolute;inset:-11px} /* enlarges the clickable target to ~44px */ -.card:hover .seldot,.card:focus-within .seldot{opacity:1} -.card .seldot:hover{border-color:var(--taken);background:rgba(245,158,11,.22);transform:scale(1.12)} -.card.sel{border-color:var(--taken);box-shadow:0 0 0 1px var(--taken),0 1px 3px rgba(0,0,0,.3);cursor:grab} -.card.sel:active{cursor:grabbing} -.card.sel .seldot{opacity:1;background:var(--taken);border-color:var(--taken);color:#1c1c1e} -/* Drop catch-zone: a big invisible corner region that only activates WHILE dragging a - selection, so you can release anywhere near the moon instead of hitting the 60px circle. */ -#moonzone{position:fixed;right:0;bottom:0;width:260px;height:260px;z-index:49;pointer-events:none;opacity:0; - border-radius:260px 0 0 0;transition:opacity .15s; - background:radial-gradient(125% 125% at 100% 100%,rgba(245,158,11,.15),rgba(245,158,11,0) 68%)} -body.dragsel #moonzone{opacity:1;pointer-events:auto} -body.dragsel #moon{transform:scale(1.1)} /* moon grows during a drag — a bigger, obvious anchor */ -#moonzone.over{background:radial-gradient(125% 125% at 100% 100%,rgba(245,158,11,.34),rgba(245,158,11,.05) 70%)} -/* Floating moon: a drop target + click-to-launch. Dimmed until something is selected. */ -#moon{position:fixed;right:22px;bottom:22px;z-index:50;width:60px;height:60px;border-radius:50%; - display:flex;align-items:center;justify-content:center;font-size:27px;cursor:pointer;user-select:none; - background:var(--panel);border:1px solid var(--line);opacity:.4;transition:opacity .15s,transform .12s,box-shadow .15s; - box-shadow:0 4px 14px rgba(0,0,0,.4)} -@keyframes moonpulse{0%,100%{box-shadow:0 0 0 1px rgba(245,158,11,.5),0 4px 18px rgba(245,158,11,.28)} - 50%{box-shadow:0 0 0 5px rgba(245,158,11,.16),0 4px 24px rgba(245,158,11,.45)}} -#moon.armed{opacity:1;animation:moonpulse 1.8s ease-in-out infinite} /* selection exists → invite the drop */ -#moon.over{transform:scale(1.14);animation:none;box-shadow:0 0 0 2px var(--taken),0 6px 22px rgba(245,158,11,.5)} -#moon:hover{opacity:1} -#moon .badge{position:absolute;top:-4px;right:-4px;min-width:20px;height:20px;padding:0 5px;border-radius:10px; - background:var(--taken);color:#1c1c1e;font:700 11px/20px var(--mono);text-align:center;box-shadow:0 1px 4px rgba(0,0,0,.4)} -.lx-list{max-height:230px;overflow:auto;margin:6px 0 2px;border:1px solid var(--line);border-radius:8px} -.lx-row{display:flex;gap:8px;align-items:baseline;padding:6px 10px;border-bottom:1px solid var(--line2);font-size:13px} -.lx-row:last-child{border-bottom:0} -.lx-row .lx-id{color:var(--muted);font-family:var(--mono);font-size:11px;flex:0 0 auto} -.lx-row .lx-t{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} -.lx-warn{color:var(--muted);font-size:12px;margin:8px 0 2px} -.lx-warn b{color:var(--taken)} -.lx-repo{color:var(--muted);font-size:11px;margin:6px 0 2px} -.lx-repo code{font-family:var(--mono);color:var(--text);background:var(--panel2);padding:1px 5px;border-radius:5px} -.lx-err{color:var(--danger);font-size:13px;margin:8px 0} -.ns-boot{display:flex;flex-direction:column;align-items:center;gap:10px;padding:48px 20px;color:var(--text);font-size:15px} -.ns-boot-sub{color:var(--muted);font-size:12px;text-align:center} -.ns-boot-sub code{font-family:var(--mono);background:var(--panel2);padding:1px 5px;border-radius:5px} -.ns-spin{width:22px;height:22px;border-radius:50%;border:2px solid var(--line2);border-top-color:var(--taken);animation:nsspin .8s linear infinite} -@keyframes nsspin{to{transform:rotate(360deg)}} - @media(max-width:600px){ header:has(#pf-controls[style*="inline-flex"]){padding:8px 12px;gap:6px} #pf-controls{width:100%;gap:8px;flex-wrap:wrap} diff --git a/assets/dashboard.html b/assets/dashboard.html index a6ec169..4fe7e51 100644 --- a/assets/dashboard.html +++ b/assets/dashboard.html @@ -7,14 +7,13 @@
-