beadcyte 0.4.0
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/CHANGELOG.md +73 -0
- package/LICENSE +661 -0
- package/README.md +386 -0
- package/THIRD_PARTY_NOTICES.md +56 -0
- package/bin/beadcyte.mjs +60 -0
- package/package.json +77 -0
- package/src/changelog-cli.mjs +105 -0
- package/src/changelog.mjs +196 -0
- package/src/cli.mjs +577 -0
- package/src/estimator.mjs +314 -0
- package/src/format.mjs +22 -0
- package/src/history-walk.mjs +170 -0
- package/src/index.mjs +5 -0
- package/src/mutate.mjs +193 -0
- package/src/projects.mjs +120 -0
- package/src/provenance.mjs +75 -0
- package/src/review-hours.mjs +117 -0
- package/src/roster-path.mjs +24 -0
- package/src/scheduler.mjs +424 -0
- package/src/serve.mjs +411 -0
- package/src/server-state.mjs +105 -0
- package/src/ship.mjs +178 -0
- package/src/stage-waits.mjs +69 -0
- package/src/start.mjs +111 -0
- package/src/stop.mjs +66 -0
- package/src/velocity.mjs +209 -0
- package/src/web/App.vue +691 -0
- package/src/web/app.css +54 -0
- package/src/web/assets/favicon.svg +12 -0
- package/src/web/avatar.ts +53 -0
- package/src/web/bead-detail.ts +240 -0
- package/src/web/changelog-view.ts +41 -0
- package/src/web/components/BeadDrawer.vue +1883 -0
- package/src/web/components/BeadSubGraph.vue +326 -0
- package/src/web/components/BeadSubGraphOverlay.vue +192 -0
- package/src/web/components/BeadTooltip.vue +516 -0
- package/src/web/components/BeadcyteMark.vue +64 -0
- package/src/web/components/BeadsGantt.vue +2125 -0
- package/src/web/components/BeadsGrid.vue +468 -0
- package/src/web/components/BeadsIncytes.vue +567 -0
- package/src/web/components/BeadsMine.vue +325 -0
- package/src/web/components/BeadsTable.vue +335 -0
- package/src/web/components/ChangelogOverlay.vue +198 -0
- package/src/web/components/ContextMenu.vue +386 -0
- package/src/web/components/ControlsPanel.vue +476 -0
- package/src/web/components/CostTrend.vue +206 -0
- package/src/web/components/FilterPopover.vue +245 -0
- package/src/web/components/GroupProgress.vue +274 -0
- package/src/web/components/LoadMeter.vue +144 -0
- package/src/web/components/MineRow.vue +28 -0
- package/src/web/components/OptionsMenu.vue +825 -0
- package/src/web/components/PriorityChip.vue +105 -0
- package/src/web/components/ScoreStrip.vue +131 -0
- package/src/web/components/SearchPalette.vue +210 -0
- package/src/web/components/ShipTrend.vue +510 -0
- package/src/web/components/ShortcutsOverlay.vue +164 -0
- package/src/web/components/Term.vue +177 -0
- package/src/web/components/Toast.vue +50 -0
- package/src/web/components/TriageMeters.vue +426 -0
- package/src/web/components/TypeChip.vue +96 -0
- package/src/web/components/Walkthrough.vue +209 -0
- package/src/web/components/WhatIfPanel.vue +206 -0
- package/src/web/components/WipBullets.vue +191 -0
- package/src/web/components/filter-option.ts +9 -0
- package/src/web/composables/url-codec.ts +136 -0
- package/src/web/composables/useBeadTooltip.ts +148 -0
- package/src/web/composables/useKeyboard.ts +97 -0
- package/src/web/composables/useLiveRefresh.ts +69 -0
- package/src/web/composables/useTheme.ts +125 -0
- package/src/web/composables/useUrlState.ts +208 -0
- package/src/web/controls-scope.ts +83 -0
- package/src/web/cost.ts +251 -0
- package/src/web/dep-headings.ts +62 -0
- package/src/web/economics.ts +440 -0
- package/src/web/env.d.ts +85 -0
- package/src/web/frontier.ts +208 -0
- package/src/web/gantt-viewport.ts +99 -0
- package/src/web/highlights.ts +124 -0
- package/src/web/index.html +46 -0
- package/src/web/insights.ts +107 -0
- package/src/web/keybindings.ts +200 -0
- package/src/web/load-meter.ts +72 -0
- package/src/web/main.ts +20 -0
- package/src/web/markdown.ts +14 -0
- package/src/web/mine.ts +137 -0
- package/src/web/mutations.ts +21 -0
- package/src/web/person.ts +102 -0
- package/src/web/projects-text.ts +15 -0
- package/src/web/projects.ts +188 -0
- package/src/web/refresh.ts +47 -0
- package/src/web/search.ts +50 -0
- package/src/web/shortcuts.ts +113 -0
- package/src/web/status-filter.ts +48 -0
- package/src/web/store.ts +1378 -0
- package/src/web/style-audit.mjs +346 -0
- package/src/web/styles-alt.css +111 -0
- package/src/web/styles-ported.css +270 -0
- package/src/web/subgraph.ts +362 -0
- package/src/web/table.ts +201 -0
- package/src/web/theme.ts +88 -0
- package/src/web/tokens.css +168 -0
- package/src/web/triage.ts +914 -0
- package/src/web/view-model.ts +717 -0
- package/src/web/walkthrough.ts +133 -0
- package/src/web/watchlist.ts +47 -0
- package/src/web/whatif.ts +291 -0
- package/src/web/window.ts +73 -0
- package/src/web/wip.ts +83 -0
package/README.md
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
<img src="docs/beadcyte-mark.png" alt="beadcyte" width="380">
|
|
2
|
+
|
|
3
|
+
<!-- hand-maintained: start -->
|
|
4
|
+
<!-- Written by the maintainer, not by agents. See CLAUDE.md. -->
|
|
5
|
+
|
|
6
|
+
A local web-based history and projected future, for any
|
|
7
|
+
[beads](https://github.com/gastownhall/beads) project.
|
|
8
|
+
|
|
9
|
+
Pairing agents with beads has changed how our team works: far more is in flight than before. However, velocity in one place exposes bottlenecks in another. **If production of ideas becomes cheap, managing the flow of ideas becomes critical**. This tool is for teams using beads to coordinate with agents over long-horizon tasks. [The feature tour](#the-tour) below walks through each view.
|
|
10
|
+
|
|
11
|
+
beadcyte reads your beads DB with `bd list --all --json` and draws two things that look alike and are not: what **happened**, which is measured, and what it **expects to happen**, which is a model's opinion. Three models drive the projection: a historical-median duration estimator, a greedy WIP-cap scheduler calibrated against your real throughput, and an eight-signal triage score. Each is shown with its own uncertainty rather than as a flat number.
|
|
12
|
+
|
|
13
|
+
beadcyte is made by [Incyte Studios](https://incytestudios.com).
|
|
14
|
+
|
|
15
|
+
<!-- hand-maintained: end -->
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
cd /path/to/your/beads/project
|
|
23
|
+
npx beadcyte start
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
That opens an interactive app on `127.0.0.1:4173` and gives you the terminal
|
|
27
|
+
back; `npx beadcyte stop` ends it. Localhost only, no auth, no build step —
|
|
28
|
+
same threat model as `bv`.
|
|
29
|
+
|
|
30
|
+
Or install it once; `bcyte` is a short alias for the same command:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm i -g beadcyte
|
|
34
|
+
beadcyte start
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For a single self-contained SVG, with no server:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
beadcyte --out gantt.svg
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The SVG has a system font stack and no external references, so it embeds
|
|
44
|
+
anywhere: MR comments, wiki pages, dashboards.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## The tour
|
|
49
|
+
|
|
50
|
+
### Mine — what should I be doing, what is going on
|
|
51
|
+
|
|
52
|
+

|
|
53
|
+
|
|
54
|
+
The first tab, and the one the app opens on. Everything on it is about one
|
|
55
|
+
person: what is ready for you (assigned to you, approved with `spec:ready`,
|
|
56
|
+
not already moving, every blocker closed), what you have in progress and in
|
|
57
|
+
review, which of your beads are waiting on someone else and who that is,
|
|
58
|
+
whose beads are waiting on you, and what you are watching. Unassigned beads
|
|
59
|
+
the scheduler would route to you appear as suggestions, marked as inferred.
|
|
60
|
+
|
|
61
|
+
Who "you" are is set once, in the options menu, from the assignees present in
|
|
62
|
+
the data, and remembered by the browser. A shared link carrying `?me=` shows
|
|
63
|
+
that person's view for one load without changing your own choice. Until a
|
|
64
|
+
user is chosen, the view asks.
|
|
65
|
+
|
|
66
|
+
The same two questions are one click away on the Gantt and grid too: the
|
|
67
|
+
MINE buttons in the controls row set the filters to *my ready work* or *my
|
|
68
|
+
work in review* outright, so the result never depends on what was selected
|
|
69
|
+
before, and clicking the active one clears back to the defaults. The
|
|
70
|
+
`unblocked` term they rely on is an ordinary filter as well.
|
|
71
|
+
|
|
72
|
+
### Gantt — history and projection on one timeline
|
|
73
|
+
|
|
74
|
+

|
|
75
|
+
|
|
76
|
+
Past bars are real `started_at → closed_at`. Future bars are the scheduler's
|
|
77
|
+
projection, and their length is a bucketed median of how long comparable work
|
|
78
|
+
has actually taken. A dashed red rule marks today, group headers carry a
|
|
79
|
+
progress chip, and dependency arrows connect `blocks` edges between visible
|
|
80
|
+
bars.
|
|
81
|
+
|
|
82
|
+
Bar styling distinguishes the cases that matter: shipped work carries a ship
|
|
83
|
+
marker, closed-without-shipping is dim, backlog beads that are not
|
|
84
|
+
`spec:ready` are faint, and a bar whose assignee the scheduler **guessed** by
|
|
85
|
+
affinity is hatched — with the lane header counting how many of its beads are
|
|
86
|
+
guesses — rather than presented as a decision. In the
|
|
87
|
+
screenshot ten of this repo's 40 closed beads are dim for a real reason: they
|
|
88
|
+
were closed without ship evidence, so nothing says they shipped.
|
|
89
|
+
|
|
90
|
+
The footer under the chart counts bars by kind, and each name explains
|
|
91
|
+
itself on hover: `shipped` and `closedNoShip` are history; `inProgressLive`
|
|
92
|
+
is the spent part of work under way; `future`, `futureBacklog` (not
|
|
93
|
+
`spec:ready`, scheduled after the ready work) and `unassigned` (from the pool
|
|
94
|
+
nobody owns) are the projection. A projected bar is the **work**, to scale.
|
|
95
|
+
The waiting around it — unclaimed time before an affinity pick, review time
|
|
96
|
+
before the landing — is drawn dull, hatched and cut short at the date it ends,
|
|
97
|
+
with a dotted connector for what was cut: its length is not to scale, its
|
|
98
|
+
end is. The tooltip gives both numbers.
|
|
99
|
+
|
|
100
|
+
Status is one control, not two: the filter starts with every status except
|
|
101
|
+
closed and deferred selected, and the panel says `3 selected` rather than
|
|
102
|
+
leaving you to infer a default from an empty chip.
|
|
103
|
+
|
|
104
|
+
Hovering a bar gives the bead's detail and a projected finish **date** — not a
|
|
105
|
+
day offset — including whether that date is a projection or just the end of the
|
|
106
|
+
scheduling window.
|
|
107
|
+
|
|
108
|
+
A window that would squeeze below six pixels a day scrolls instead: the plot
|
|
109
|
+
shows a readable span centred on today, with a scrollbar under it. Drag the
|
|
110
|
+
axis to pan, wheel over the axis to zoom, swipe or shift-wheel over the plot
|
|
111
|
+
to pan, and `t` brings today back to the centre. A window that fits is drawn
|
|
112
|
+
whole, as before.
|
|
113
|
+
|
|
114
|
+
### Grid — browse without the time axis
|
|
115
|
+
|
|
116
|
+

|
|
117
|
+
|
|
118
|
+
The same filtered set as cards: title, chips, assignee, labels. Better than the
|
|
119
|
+
Gantt for reading titles and worse for anything about time, which is why both
|
|
120
|
+
exist.
|
|
121
|
+
|
|
122
|
+
### Table — every derived number in one sortable place
|
|
123
|
+
|
|
124
|
+

|
|
125
|
+
|
|
126
|
+
The same filtered set as dense rows, one per bead, with everything the app
|
|
127
|
+
knows in columns: type, priority, status, assignee (marked when the scheduler
|
|
128
|
+
inferred it), labels, estimate, projected finish, triage score, group. Every
|
|
129
|
+
column sorts; the default is triage score, highest first, because the table's
|
|
130
|
+
job is "what next". Unlike the Gantt it never drops rows to keep a group
|
|
131
|
+
short, so it is also the readable equivalent of the chart — nothing in the app
|
|
132
|
+
is reachable only by reading a bar.
|
|
133
|
+
|
|
134
|
+
### Incytes — the project, not the beads
|
|
135
|
+
|
|
136
|
+

|
|
137
|
+
|
|
138
|
+
Ships per week over the velocity window, per-group completion sorted by size
|
|
139
|
+
so the outlier is findable, and Allocation: everyone's work in flight against
|
|
140
|
+
their calibrated cap on one shared scale, with the run past the cap drawn at
|
|
141
|
+
its real length. That block used to sit under the controls on every view; it
|
|
142
|
+
is a project-wide fact, so it lives here.
|
|
143
|
+
|
|
144
|
+
Beside the ship trend, **Cost per week**: seat hours stacked by seat, with
|
|
145
|
+
tokens in and out in the figures and the table, in the same weekly buckets.
|
|
146
|
+
Weeks behind today are what closed beads recorded; weeks ahead are projected
|
|
147
|
+
from the median cost of comparable shipped work, placed where the schedule
|
|
148
|
+
lands each bead, and drawn dulled and dashed so they cannot be read as
|
|
149
|
+
measured. The same block appears on a person's pane for their beads. See
|
|
150
|
+
[docs/economics.md](docs/economics.md#cost-trajectory) for exactly how.
|
|
151
|
+
|
|
152
|
+
**This screenshot is beadcyte's own repo, and the spike is the honest shape
|
|
153
|
+
of recovered data.** Nothing here recorded a ship while the work happened; the
|
|
154
|
+
evidence was read back out of `Closes <id>` commit trailers afterwards, so
|
|
155
|
+
every ship lands in the week the trailers exist and the seven weeks before it
|
|
156
|
+
are structurally zero — the repo did not exist.
|
|
157
|
+
|
|
158
|
+
Which is why the headline says _"first week of history — no baseline yet"_
|
|
159
|
+
rather than `+700%`. With one shipping week the mean **is** that week divided
|
|
160
|
+
by the window length, so a percentage against it is decided by the window and
|
|
161
|
+
not by the work: identical beads report +700% at ±90d and +300% at ±30d. A
|
|
162
|
+
number that moves when you change the axis is an artefact with a percent sign
|
|
163
|
+
on it, so the chart declines to print one.
|
|
164
|
+
|
|
165
|
+
If your own trend looks empty rather than spiky, nothing is broken and nothing
|
|
166
|
+
was recorded — see [what to record](docs/economics.md#what-counts-as-ship-evidence).
|
|
167
|
+
|
|
168
|
+
Pick a person — the chips at the top of the view, a swimlane header when
|
|
169
|
+
grouped by assignee, a handle on a grid card or in the Allocation block — and
|
|
170
|
+
the same two charts re-scope to them: their ships per week in the project's
|
|
171
|
+
weeks, and the progress of the beads recorded as theirs. Below that, their
|
|
172
|
+
roster line (the raw cap, the calibrated cap and how it was derived, or a
|
|
173
|
+
plain statement that no roster was written and the cap is a default), what is
|
|
174
|
+
in flight, what is ready for them, what the scheduler would route to them
|
|
175
|
+
next — marked as a projection, because nobody decided it — and their recent
|
|
176
|
+
ships. `?who=` carries the selection in the link.
|
|
177
|
+
|
|
178
|
+
Two more blocks on that pane are the plan's edge for the person. **Frontier**
|
|
179
|
+
is a ruler of the coming days — the window's forward span, 7 to 60 — with a
|
|
180
|
+
hollow mark where a bead starts for them and a filled one where it lands,
|
|
181
|
+
read off the same schedule the Gantt draws; dashed marks are assignments the
|
|
182
|
+
scheduler inferred. **Proposed next** ranks what they could take, with the
|
|
183
|
+
reasons stated beside each: the scheduler's own routing, roster affinity,
|
|
184
|
+
triage score, what it unblocks, priority, whether they have room today — and,
|
|
185
|
+
where someone else is over cap, that person's not-yet-started beads offered
|
|
186
|
+
across. Stage puts a proposal into the what-if (below), where the diff shows
|
|
187
|
+
what moves; nothing is written until it is confirmed.
|
|
188
|
+
|
|
189
|
+
### The drawer — why a bead ranks where it does
|
|
190
|
+
|
|
191
|
+

|
|
192
|
+
|
|
193
|
+
Triage's eight signals as contribution meters. The track length is the
|
|
194
|
+
signal's declared weight, so a signal that could never matter much is visibly
|
|
195
|
+
short. The fill is what this bead actually contributed. A tick marks what a
|
|
196
|
+
high value looks like on **your** repo, and a cut-off track marks weight the
|
|
197
|
+
signal cannot reach at all.
|
|
198
|
+
|
|
199
|
+
Signals that cannot separate anything on your data are labelled `FLAT` — one
|
|
200
|
+
of them carries the second-largest weight and is frequently non-zero on
|
|
201
|
+
exactly one bead, because shallow dependency graphs give it nothing to measure.
|
|
202
|
+
An authoritative-looking number that means nothing is worse than no number, so
|
|
203
|
+
the drawer says which is which. Below that: the dependency subgraph — for an
|
|
204
|
+
epic, the graph of all its children and the blocks edges between them, since
|
|
205
|
+
an epic's own neighbourhood is just the epic, with a full-screen view for the
|
|
206
|
+
large ones — the dossier with projected dates, and where the bead's time went.
|
|
207
|
+
|
|
208
|
+
That last one distinguishes what was **measured** from what beadcyte
|
|
209
|
+
**computed**. The queue wait is `started_at − created_at`, arithmetic on
|
|
210
|
+
fields `bd` maintains for free, so it is derived wherever nobody recorded it —
|
|
211
|
+
hatched, and its legend row tagged `DERIVED`, because a reconstructed number
|
|
212
|
+
that looks measured is one you can never separate out again. Worked hours are
|
|
213
|
+
never derived: the estimator prefers them over wall-clock, so an invented
|
|
214
|
+
figure would override the one real signal rather than diluting it.
|
|
215
|
+
|
|
216
|
+
Every derived figure in the drawer explains itself. A dotted underline marks
|
|
217
|
+
a term that will: the score, each of the eight signal names, the estimate's
|
|
218
|
+
source (`(feature), n=8` — n is the sample count behind the median, and a
|
|
219
|
+
small one deserves less trust), the projected finish and why it sometimes says
|
|
220
|
+
_finishes after_, the phase names, the tense of the dependency headings, and
|
|
221
|
+
the synthetic-assignee marker. Hover shows the sentence; Tab reaches it; a
|
|
222
|
+
click or tap pins it; Escape, a scroll or a tap elsewhere dismisses it. The
|
|
223
|
+
sentences come from the modules that compute the figures, so a formula cannot
|
|
224
|
+
change without its explanation changing beside it.
|
|
225
|
+
|
|
226
|
+
### First run
|
|
227
|
+
|
|
228
|
+
The first visit opens a short walkthrough in the corner — the bar kinds, the
|
|
229
|
+
projection being a model rather than a plan, triage, the views, the controls.
|
|
230
|
+
It is not a modal and takes no focus; skip or finish it and it never returns
|
|
231
|
+
unasked. The options menu re-opens it, and nothing in it exists only there.
|
|
232
|
+
|
|
233
|
+
### What if
|
|
234
|
+
|
|
235
|
+
Stage changes without writing them: reassign, reprioritise or mark
|
|
236
|
+
`spec:ready` from a bead's drawer, change someone's cap from their roster
|
|
237
|
+
line on incytes, or add an idea with a rough size from the what-if panel.
|
|
238
|
+
Every plan-reading view then shows the plan as it would be — the Gantt draws
|
|
239
|
+
each moved bead's old work as a dashed ghost with the delta beside it — and
|
|
240
|
+
the panel lists what moves and by how much, where ideas land, and the
|
|
241
|
+
frontier: how many beads land for each person in the next two weeks, before
|
|
242
|
+
and after. Discard drops it all. Confirm shows the `bd` commands the change
|
|
243
|
+
set amounts to, then runs them one after another through the same
|
|
244
|
+
whitelisted endpoint the quick actions use, and reports what happened to
|
|
245
|
+
each; a cap is a roster.json edit and is reported rather than written.
|
|
246
|
+
|
|
247
|
+
### Acting on a bead
|
|
248
|
+
|
|
249
|
+
Right-click a bar, a card, a table row or a mine row — or press `a` with the
|
|
250
|
+
drawer open — for the quick actions the allocate ritual otherwise runs through
|
|
251
|
+
the CLI: claim, assign to someone on the roster, mark or remove `spec:ready`,
|
|
252
|
+
add a label, defer. Each runs one
|
|
253
|
+
`bd` command on the server; [How beadcyte uses bd](docs/bd-usage.md) lists
|
|
254
|
+
every one of them and how to reverse it by hand. Nothing else writes to your
|
|
255
|
+
tracker; there is no bulk action and no client-side undo, by design.
|
|
256
|
+
|
|
257
|
+
### Options — projects, current user, theme, refresh
|
|
258
|
+
|
|
259
|
+

|
|
260
|
+
|
|
261
|
+
Switch between local beads projects, say who you are for the mine view, pick
|
|
262
|
+
light/dark/system and one of twelve styles with live previews — including
|
|
263
|
+
ports of Catppuccin, Dracula, GitHub, Gruvbox, Nord, One, Solarized and Tokyo
|
|
264
|
+
Night, and a High Contrast original — and set the poll interval. A project the current server
|
|
265
|
+
cannot read is shown as unavailable with the reason, rather than failing when
|
|
266
|
+
you click it.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
### Keyboard
|
|
271
|
+
|
|
272
|
+
Press `?` for the shortcuts. The convention is single letters with no
|
|
273
|
+
modifier — `j`/`k` walk the beads in view, `1`–`5` switch views, `g` and
|
|
274
|
+
`w` cycle group-by and window, `p` pins, `,` opens options — and nothing
|
|
275
|
+
fires while you are typing in a field. The keyboard cursor is the open drawer:
|
|
276
|
+
there is no separate focused row, so exactly one thing ever looks selected.
|
|
277
|
+
The overlay is generated from the same table that binds the keys, so it cannot
|
|
278
|
+
drift from them.
|
|
279
|
+
|
|
280
|
+
## Flags
|
|
281
|
+
|
|
282
|
+
`beadcyte [flags]` — SVG mode:
|
|
283
|
+
|
|
284
|
+
| Flag | Default | Meaning |
|
|
285
|
+
| -------------------- | ----------------------- | -------------------------------------- |
|
|
286
|
+
| `--from -Nd` | `-90d` | Window start, relative to today |
|
|
287
|
+
| `--to +Nd` | `+90d` | Window end, relative to today |
|
|
288
|
+
| `--group-by MODE` | `epic` | `epic` / `assignee` / `seat` / `track` |
|
|
289
|
+
| `--max-per-group N` | `20` | Rows per swimlane before truncation |
|
|
290
|
+
| `--include-deferred` | `false` | Include `status=deferred` beads |
|
|
291
|
+
| `--roster PATH` | `.beadcyte/roster.json` | Per-project WIP caps and affinities |
|
|
292
|
+
| `--out PATH` | `./beadcyte.svg` | SVG output path |
|
|
293
|
+
|
|
294
|
+
`beadcyte changelog [flags]` — regenerate `CHANGELOG.md` from closed beads:
|
|
295
|
+
|
|
296
|
+
| Flag | Meaning |
|
|
297
|
+
| ---------- | ------------------------------------------- |
|
|
298
|
+
| _(none)_ | Write `CHANGELOG.md` at the repo root |
|
|
299
|
+
| `--check` | Exit 2 if the committed file is out of date |
|
|
300
|
+
| `--stdout` | Print instead of writing |
|
|
301
|
+
|
|
302
|
+
An entry appears when a closed bead carries ship evidence. Spikes, `upstream:`
|
|
303
|
+
bugs, chores and epics are excluded, and releases are cut by git tag — the
|
|
304
|
+
file states both rules in its own header, so a reader can tell "nothing
|
|
305
|
+
shipped" from "this file does not list that".
|
|
306
|
+
|
|
307
|
+
`beadcyte start [flags]` — interactive mode. Starts the server in the
|
|
308
|
+
background, prints its address and the log path, and returns; `beadcyte stop`
|
|
309
|
+
ends it, from the same directory. `beadcyte serve [flags]` runs the same server
|
|
310
|
+
in the foreground instead, with Ctrl-C to stop. Both take the same flags:
|
|
311
|
+
|
|
312
|
+
| Flag | Default | Meaning |
|
|
313
|
+
| ---------------- | ----------------------- | ----------------------------------- |
|
|
314
|
+
| `--port N` | `4173` | Listen port; falls through if taken |
|
|
315
|
+
| `--roster PATH` | `.beadcyte/roster.json` | Same roster convention |
|
|
316
|
+
| `--cache-ttl MS` | `30000` | `bd` shell-out cache TTL |
|
|
317
|
+
|
|
318
|
+
The version tag in the header opens the changelog, rendered in the app from
|
|
319
|
+
the copy shipped with the build. Set `BEADCYTE_CHANGELOG_URL` to add a link out
|
|
320
|
+
to the same file on your repo; it is absent when unset.
|
|
321
|
+
|
|
322
|
+
Most of the interactive app's state lives in the URL — view, grouping,
|
|
323
|
+
filters, window — so a link shares what you were looking at. Links written
|
|
324
|
+
before the status filter absorbed the old `showClosed` / `includeDeferred`
|
|
325
|
+
toggles still resolve to the same view.
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## Documentation
|
|
330
|
+
|
|
331
|
+
| | |
|
|
332
|
+
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
333
|
+
| [What to record](docs/economics.md) | What beadcyte reads out of your beads, what breaks when it is missing, and how to record it. **Start here if your charts look empty.** |
|
|
334
|
+
| [The model](docs/model.md) | How the estimator, the scheduler and the triage score work, what they are calibrated against, and where they are known to be wrong. |
|
|
335
|
+
| [How beadcyte uses bd](docs/bd-usage.md) | Every `bd` command the app runs — the reads, the background history walk, each quick action and how to reverse it — and whose name a claim carries. |
|
|
336
|
+
| [Roster](docs/roster.md) | Per-project WIP caps and affinity routing. |
|
|
337
|
+
| [Programmatic use](docs/api.md) | Using the estimator and scheduler as libraries. |
|
|
338
|
+
| [Theming](docs/theming.md) | The token layer, light/dark, named styles, and the audit that keeps a restyle from relabelling the data. |
|
|
339
|
+
| [CHANGELOG](CHANGELOG.md) | What has shipped. Generated from the tracker, not written by hand. |
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Development
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
npm install
|
|
347
|
+
npm run dev # beadcyte serve against this repo's own beads (port 4173)
|
|
348
|
+
npm test # unit + parity + perf suites
|
|
349
|
+
npm run typecheck # vue-tsc --noEmit
|
|
350
|
+
npm run build # static bundle → src/web/dist
|
|
351
|
+
npm run changelog # regenerate CHANGELOG.md
|
|
352
|
+
npm run changelog:check # fail if the committed CHANGELOG.md is stale
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
`npm run dev` points the dev server at beadcyte's own beads DB, so the app is
|
|
356
|
+
always dogfooding itself.
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## Non-goals
|
|
361
|
+
|
|
362
|
+
- **Resource-constrained scheduling.** Greedy dispatch, no solver. No
|
|
363
|
+
cross-human load balancing and no auto-reassignment: the chart is a
|
|
364
|
+
projection of current allocation, not a proposal for a better one.
|
|
365
|
+
- **Drag-editing the plan.** Dates come from the model; changing them means
|
|
366
|
+
changing the beads.
|
|
367
|
+
- **Non-`bd` trackers.** The input shape is `bd list --json`; anything else
|
|
368
|
+
needs an adapter.
|
|
369
|
+
|
|
370
|
+
## Licence
|
|
371
|
+
|
|
372
|
+
AGPL-3.0-only. See [LICENSE](LICENSE).
|
|
373
|
+
|
|
374
|
+
beadcyte is copyright [Incyte Studios, LLC](https://incytestudios.com) and released under the GNU Affero
|
|
375
|
+
General Public License, version 3. You may use, change and redistribute it
|
|
376
|
+
under the same terms. If you run a modified version as a network service,
|
|
377
|
+
section 13 obliges you to offer its users the corresponding source.
|
|
378
|
+
|
|
379
|
+
Incyte Studios stays the sole copyright holder so that it can also offer the
|
|
380
|
+
project under other terms. That is why contributions are accepted under a
|
|
381
|
+
permissive inbound licence rather than the AGPL; see
|
|
382
|
+
[CONTRIBUTING.md](CONTRIBUTING.md).
|
|
383
|
+
|
|
384
|
+
Dependencies are MIT, ISC, BSD and Apache-2.0 licensed and carry their own
|
|
385
|
+
notices. The triage score is a local reimplementation of a formula documented
|
|
386
|
+
by another tool, not a copy of its code; see [docs/model.md](docs/model.md).
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
beadcyte is released under AGPL-3.0-only (see [LICENSE](LICENSE)). The named
|
|
4
|
+
colour styles in `src/web/styles-ported.css` reproduce palette values published
|
|
5
|
+
by the projects below, each under the MIT licence. Palette values are facts
|
|
6
|
+
rather than copyrightable expression, but the values were copied from licensed
|
|
7
|
+
files, so the copyright line and permission notice of each are reproduced here
|
|
8
|
+
as that licence asks. Theme names belong to their owners and are used only to
|
|
9
|
+
identify the palette a style reproduces.
|
|
10
|
+
|
|
11
|
+
The Default, Synthwave, Terminal and High Contrast styles are beadcyte's own.
|
|
12
|
+
|
|
13
|
+
## MIT License
|
|
14
|
+
|
|
15
|
+
The following applies to each project in the table, with the copyright line
|
|
16
|
+
shown for it:
|
|
17
|
+
|
|
18
|
+
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
19
|
+
> of this software and associated documentation files (the "Software"), to deal
|
|
20
|
+
> in the Software without restriction, including without limitation the rights
|
|
21
|
+
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
22
|
+
> copies of the Software, and to permit persons to whom the Software is
|
|
23
|
+
> furnished to do so, subject to the following conditions:
|
|
24
|
+
>
|
|
25
|
+
> The above copyright notice and this permission notice shall be included in
|
|
26
|
+
> all copies or substantial portions of the Software.
|
|
27
|
+
>
|
|
28
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
29
|
+
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
30
|
+
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
31
|
+
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
32
|
+
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
33
|
+
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
34
|
+
> SOFTWARE.
|
|
35
|
+
|
|
36
|
+
| Style | Project | Copyright | Licence source | Palette source |
|
|
37
|
+
| --- | --- | --- | --- | --- |
|
|
38
|
+
| Catppuccin | [catppuccin/palette](https://github.com/catppuccin/palette) | Copyright (c) 2021 Catppuccin | [LICENSE](https://github.com/catppuccin/catppuccin/blob/main/LICENSE) | `palette.json`, Mocha and Latte flavours |
|
|
39
|
+
| Dracula | [dracula/dracula-theme](https://github.com/dracula/dracula-theme) | Copyright (c) 2023 Dracula Theme | [LICENSE](https://github.com/dracula/dracula-theme/blob/master/LICENSE) | README colour palette tables, Dracula and Alucard |
|
|
40
|
+
| GitHub | [primer/primitives](https://github.com/primer/primitives) 7.11.13 | Copyright (c) 2018 GitHub Inc. | [LICENSE](https://github.com/primer/primitives/blob/main/LICENSE) | `dist/json/colors/light.json` and `dark.json` |
|
|
41
|
+
| Gruvbox | [morhetz/gruvbox](https://github.com/morhetz/gruvbox) | Pavel Pertsev | `"license": "MIT"` in [package.json](https://raw.githubusercontent.com/morhetz/gruvbox/master/package.json); the repository ships no LICENSE file | `colors/gruvbox.vim` |
|
|
42
|
+
| Nord | [nordtheme/nord](https://github.com/nordtheme/nord) | Copyright (c) 2016-present Sven Greb | [license](https://github.com/nordtheme/nord/blob/develop/license) | `src/nord.css`, `--nord0` to `--nord15` |
|
|
43
|
+
| One | [atom/one-dark-syntax](https://github.com/atom/one-dark-syntax) and [one-light-syntax](https://github.com/atom/one-light-syntax) | Copyright (c) 2016 GitHub Inc. | [LICENSE.md](https://github.com/atom/one-dark-syntax/blob/master/LICENSE.md) | `styles/colors.less` in each, authored as HSL |
|
|
44
|
+
| Solarized | [altercation/solarized](https://github.com/altercation/solarized) | Copyright (c) 2011 Ethan Schoonover | [LICENSE](https://github.com/altercation/solarized/blob/master/LICENSE) | README palette table |
|
|
45
|
+
| Tokyo Night | [enkia/tokyo-night-vscode-theme](https://github.com/enkia/tokyo-night-vscode-theme) | Copyright (c) 2018-present Enkia | [LICENSE.txt](https://github.com/enkia/tokyo-night-vscode-theme/blob/master/LICENSE.txt) | `themes/tokyo-night-color-theme.json` and the light variant |
|
|
46
|
+
|
|
47
|
+
## What was changed
|
|
48
|
+
|
|
49
|
+
A port keeps each theme's hues. Where a published colour fell short of
|
|
50
|
+
beadcyte's legibility floors it was moved in lightness only, and the lightness
|
|
51
|
+
of every colour-only encoding token was then placed by
|
|
52
|
+
`scripts/place-encoding-lightness.mjs` so each pair stays distinguishable under
|
|
53
|
+
simulated colour-vision deficiency. Two named substitutions go further and are
|
|
54
|
+
noted above their blocks in `src/web/styles-ported.css`: Nord's bar hues carry
|
|
55
|
+
scaled chroma, and Solarized's future bars use its magenta rather than its
|
|
56
|
+
violet. The derivation rules are in [docs/theming.md](docs/theming.md).
|
package/bin/beadcyte.mjs
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// beadcyte — CLI dispatcher. The package is published unscoped as `beadcyte`
|
|
3
|
+
// so `npx beadcyte start` works from any beads project. A global install puts
|
|
4
|
+
// two names on PATH for the same file: `beadcyte`, and `bcyte` for short.
|
|
5
|
+
//
|
|
6
|
+
// Subcommands:
|
|
7
|
+
// beadcyte → default SVG mode (see src/cli.mjs)
|
|
8
|
+
// beadcyte export [args] → alias for the default SVG mode
|
|
9
|
+
// beadcyte start [args] → serve in the background, print the address, return
|
|
10
|
+
// beadcyte stop → end the background server for this directory
|
|
11
|
+
// beadcyte serve [args] → the same server in the foreground (see src/serve.mjs)
|
|
12
|
+
// beadcyte changelog → generate CHANGELOG.md from closed beads
|
|
13
|
+
// beadcyte --help → passes to whichever subcommand's --help
|
|
14
|
+
// beadcyte --version → prints the version from package.json
|
|
15
|
+
//
|
|
16
|
+
// A subcommand is the FIRST positional arg with no leading `--`. Flags stay
|
|
17
|
+
// in their subcommand's argv slice; `beadcyte --from -30d` is the default subcommand
|
|
18
|
+
// with `--from -30d`, not a subcommand called `--from`.
|
|
19
|
+
|
|
20
|
+
import { readFileSync } from "node:fs";
|
|
21
|
+
import { fileURLToPath } from "node:url";
|
|
22
|
+
import { dirname, join } from "node:path";
|
|
23
|
+
|
|
24
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
25
|
+
const __dirname = dirname(__filename);
|
|
26
|
+
const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8'));
|
|
27
|
+
|
|
28
|
+
const argv = process.argv.slice(2);
|
|
29
|
+
const first = argv[0];
|
|
30
|
+
const KNOWN_SUBCOMMANDS = new Set(["start", "stop", "serve", "export", "changelog"]);
|
|
31
|
+
|
|
32
|
+
// Handle version flag first
|
|
33
|
+
if (first === "--version" || first === "-v") {
|
|
34
|
+
console.log(packageJson.version);
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (first === "serve") {
|
|
39
|
+
process.argv = [process.argv[0], process.argv[1], ...argv.slice(1)];
|
|
40
|
+
await import("../src/serve.mjs");
|
|
41
|
+
} else if (first === "start") {
|
|
42
|
+
process.argv = [process.argv[0], process.argv[1], ...argv.slice(1)];
|
|
43
|
+
await import("../src/start.mjs");
|
|
44
|
+
} else if (first === "stop") {
|
|
45
|
+
process.argv = [process.argv[0], process.argv[1], ...argv.slice(1)];
|
|
46
|
+
await import("../src/stop.mjs");
|
|
47
|
+
} else if (first === "changelog") {
|
|
48
|
+
process.argv = [process.argv[0], process.argv[1], ...argv.slice(1)];
|
|
49
|
+
await import("../src/changelog-cli.mjs");
|
|
50
|
+
} else if (first === "export" || first === undefined || first.startsWith("--")) {
|
|
51
|
+
// default: SVG mode (drop `export` if present)
|
|
52
|
+
if (first === "export")
|
|
53
|
+
process.argv = [process.argv[0], process.argv[1], ...argv.slice(1)];
|
|
54
|
+
await import("../src/cli.mjs");
|
|
55
|
+
} else {
|
|
56
|
+
console.error(
|
|
57
|
+
`beadcyte: unknown subcommand "${first}". Try one of: ${[...KNOWN_SUBCOMMANDS].join(", ")}, or omit for the default SVG mode.`,
|
|
58
|
+
);
|
|
59
|
+
process.exit(2);
|
|
60
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "beadcyte",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Gantt of bead history + projected future — historical-median estimator + greedy WIP-cap scheduler + self-contained SVG",
|
|
5
|
+
"author": "Incyte Studios, LLC <hello@incytestudios.com> (https://incytestudios.com)",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"beadcyte": "bin/beadcyte.mjs",
|
|
9
|
+
"bcyte": "bin/beadcyte.mjs"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": "./src/index.mjs",
|
|
13
|
+
"./estimator": "./src/estimator.mjs",
|
|
14
|
+
"./scheduler": "./src/scheduler.mjs",
|
|
15
|
+
"./cli": "./src/cli.mjs",
|
|
16
|
+
"./format": "./src/format.mjs",
|
|
17
|
+
"./changelog": "./src/changelog.mjs"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"bin",
|
|
21
|
+
"src",
|
|
22
|
+
"!src/**/*.test.mjs",
|
|
23
|
+
"!src/web/__fixtures__",
|
|
24
|
+
"!src/web/dist",
|
|
25
|
+
"README.md",
|
|
26
|
+
"CHANGELOG.md",
|
|
27
|
+
"LICENSE",
|
|
28
|
+
"THIRD_PARTY_NOTICES.md"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"dev": "node bin/beadcyte.mjs serve",
|
|
32
|
+
"build": "vite build",
|
|
33
|
+
"preview": "vite preview",
|
|
34
|
+
"typecheck": "vue-tsc --noEmit",
|
|
35
|
+
"test": "node --test 'src/**/*.test.mjs'",
|
|
36
|
+
"changelog": "node src/changelog-cli.mjs",
|
|
37
|
+
"changelog:check": "node src/changelog-cli.mjs --check"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@incytestudios/ddv": "^1.0.0-rc.4",
|
|
41
|
+
"@lucide/vue": "^1.39.0",
|
|
42
|
+
"@vitejs/plugin-vue": "^5.2.1",
|
|
43
|
+
"@vueuse/core": "^10.11.0",
|
|
44
|
+
"@vueuse/gesture": "^2.0.0",
|
|
45
|
+
"date-fns": "^3.6.0",
|
|
46
|
+
"downsample": "^1.4.0",
|
|
47
|
+
"marked": "^18.0.11",
|
|
48
|
+
"pinia": "^2.3.0",
|
|
49
|
+
"vite": "^5.4.11",
|
|
50
|
+
"vue": "^3.5.13"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=20"
|
|
54
|
+
},
|
|
55
|
+
"keywords": [
|
|
56
|
+
"beads",
|
|
57
|
+
"bd",
|
|
58
|
+
"gantt",
|
|
59
|
+
"scheduling",
|
|
60
|
+
"svg",
|
|
61
|
+
"planning"
|
|
62
|
+
],
|
|
63
|
+
"repository": {
|
|
64
|
+
"type": "git",
|
|
65
|
+
"url": "git+ssh://git@gitlab.com/incytestudios/beadcyte.git"
|
|
66
|
+
},
|
|
67
|
+
"homepage": "https://gitlab.com/incytestudios/beadcyte",
|
|
68
|
+
"bugs": {
|
|
69
|
+
"url": "https://gitlab.com/incytestudios/beadcyte/-/issues"
|
|
70
|
+
},
|
|
71
|
+
"license": "AGPL-3.0-only",
|
|
72
|
+
"devDependencies": {
|
|
73
|
+
"@tsconfig/node20": "^20.1.10",
|
|
74
|
+
"typescript": "~5.6.3",
|
|
75
|
+
"vue-tsc": "~2.1.10"
|
|
76
|
+
}
|
|
77
|
+
}
|