@tablation/crew 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +301 -2
- package/bin/crew +29 -0
- package/crew.example.yaml +132 -0
- package/dist/cli.js +10221 -0
- package/package.json +52 -3
- package/prompts/default/personas/common.md +752 -0
- package/prompts/default/personas/lane-design.md +107 -0
- package/prompts/default/personas/lane-dev.md +27 -0
- package/prompts/default/personas/lane-pair.md +103 -0
- package/prompts/default/personas/lane-qa.md +138 -0
- package/prompts/default/personas/lane-triage.md +94 -0
- package/prompts/default/skills/grill-me.md +133 -0
- package/prompts/dev-qa/personas/common.md +722 -0
- package/prompts/dev-qa/personas/lane-design.md +18 -0
- package/prompts/dev-qa/personas/lane-dev.md +26 -0
- package/prompts/dev-qa/personas/lane-pair.md +103 -0
- package/prompts/dev-qa/personas/lane-qa.md +134 -0
- package/prompts/dev-qa/personas/lane-triage.md +87 -0
- package/prompts/dev-qa/skills/grill-me.md +133 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Brad Choate
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,302 @@
|
|
|
1
|
-
#
|
|
1
|
+
# crew
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A standing team of headless agents that picks work off a
|
|
4
|
+
[Tablation](https://tablation.com) board, does it on a machine you control,
|
|
5
|
+
and reports back on the board.
|
|
6
|
+
|
|
7
|
+
**Documentation: <https://crew.tablation.dev>** — start with
|
|
8
|
+
[Getting started](https://crew.tablation.dev/getting-started). The source is in
|
|
9
|
+
`docs/`.
|
|
10
|
+
|
|
11
|
+
Each **seat** is a row in the board's Crew table that happens to be a robot,
|
|
12
|
+
with a brief (`prompts/<promptSet>/`, see "Prompt sets" below) and a slice
|
|
13
|
+
of the queue. A seat is named in that table and answers to that name, in the
|
|
14
|
+
queue digest and in the comments it writes on tickets. Everything the crew
|
|
15
|
+
knows about a run — what to build, what is blocked, what has shipped — is
|
|
16
|
+
table data, and every step it takes is visible as a status change or a
|
|
17
|
+
comment on the ticket. There is no hidden state and no queue but the board.
|
|
18
|
+
|
|
19
|
+
Every agent is handed a roster at the top of its prompt: who else is aboard,
|
|
20
|
+
what each of them is called, and which rows are **holds** — the people using
|
|
21
|
+
this ship, and the interactive sessions they work through. A ticket assigned
|
|
22
|
+
to a hold is off limits to every seat, whatever its status, which is how a
|
|
23
|
+
person takes something over without racing the crew for it.
|
|
24
|
+
|
|
25
|
+
Today's crew has four polled seats, described below under the `default`
|
|
26
|
+
prompt set (the three-lane dev/design/QA policy this repo ships with — see
|
|
27
|
+
"Prompt sets" below for others, and for writing your own):
|
|
28
|
+
|
|
29
|
+
| Seat | Owns | Brief |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| **dev** | approved tickets that don't need design work | `prompts/default/personas/lane-dev.md` |
|
|
32
|
+
| **design** | approved tickets flagged *Needs design* | `prompts/default/personas/lane-design.md` |
|
|
33
|
+
| **qa** | everything at `fixed` or `qa`, whoever built it | `prompts/default/personas/lane-qa.md` |
|
|
34
|
+
| **triage** | tickets assigned to the triage seat | `prompts/default/personas/lane-triage.md` |
|
|
35
|
+
|
|
36
|
+
A fifth persona, **pair**, rides `crew agents sync` alongside these four but
|
|
37
|
+
is not polled: it is the identity a live, interactive session (an IDE
|
|
38
|
+
conversation, not a scheduled run) uses when it touches the tracker. It has
|
|
39
|
+
no Crew-table seat, is never assigned a ticket by the poll loop, and its
|
|
40
|
+
brief (`prompts/default/personas/lane-pair.md`) is not prefixed with
|
|
41
|
+
`common.md` — none of the polling loop's shared policy describes it. `crew
|
|
42
|
+
agents prompt pair` prints its current persona text (a workspace admin's
|
|
43
|
+
live edit if there is one, else the local default) for something like a
|
|
44
|
+
`SessionStart` hook to feed into a session at start.
|
|
45
|
+
|
|
46
|
+
Alongside personas, a prompt set can also ship **skills** —
|
|
47
|
+
`prompts/<promptSet>/skills/*.md`, each one YAML frontmatter (`name`,
|
|
48
|
+
`description`) plus a markdown prompt body. A skill isn't a seat: it's not
|
|
49
|
+
polled and not concatenated into every run's prompt, just synced into the
|
|
50
|
+
workspace's `Agent Skills` table so any session can fetch it by name on
|
|
51
|
+
demand. `crew agents sync [route]` pushes both personas and skills in one
|
|
52
|
+
run — "agents" here means every agent-shaped resource crew owns, not just
|
|
53
|
+
the Agents table; `crew skills sync [route]` is the same create/update/
|
|
54
|
+
diverged sync narrowed to skills alone, for when you've only touched
|
|
55
|
+
`skills/`. Example use — a Pair session invoked via an Epic's
|
|
56
|
+
`grill_link` looks up the `Grill-Me` skill through the Tablation MCP tool
|
|
57
|
+
`agent_skills_controller_get_skill` and follows it. `prompts/default/skills/
|
|
58
|
+
grill-me.md` is the shipped example.
|
|
59
|
+
|
|
60
|
+
One role runs per cycle, whichever holds the most urgent actionable ticket
|
|
61
|
+
(`src/priority.ts` decides, and the same module sorts the digest that agent is
|
|
62
|
+
handed, so the two can never disagree). QA outranks the building roles whenever
|
|
63
|
+
it has anything to check. If the winning role turns out to have nothing it can
|
|
64
|
+
actually do, the cycle falls through to the runner-up rather than idling.
|
|
65
|
+
|
|
66
|
+
Merging and deploying are **not** an agent's job. After the agent phase, a
|
|
67
|
+
release phase takes whatever the remote has, squash-merges every QA-verified
|
|
68
|
+
branch, bumps the version, writes the changelog and runs the repo's deploy
|
|
69
|
+
hook. A headless session is never handed permission to push to production.
|
|
70
|
+
|
|
71
|
+
A branch that will not merge is **handed back**, not skipped. The release
|
|
72
|
+
rewinds it, merges the base into the branch in its own worktree, and returns
|
|
73
|
+
the ticket to the dev lane with the conflict left in place to look at — so the
|
|
74
|
+
seat that resolves it is one with the ticket's context, and QA checks the
|
|
75
|
+
resolution like any other change. A branch that was merely stale merges
|
|
76
|
+
cleanly at that point and nobody is woken at all. Conflicting a second time
|
|
77
|
+
stops at `needs_info` rather than looping.
|
|
78
|
+
|
|
79
|
+
## Prompt sets
|
|
80
|
+
|
|
81
|
+
The seats above and their briefs are policy, not mechanism — a fact about
|
|
82
|
+
*how* one particular workspace likes to work, not about what crew itself
|
|
83
|
+
can do. `prompts/` ships more than one of these as complete, ready-to-run
|
|
84
|
+
policies:
|
|
85
|
+
|
|
86
|
+
| Directory | Policy |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| `prompts/default/` | Three lanes — dev, design (gated by a *Needs design* flag), QA. |
|
|
89
|
+
| `prompts/dev-qa/` | Two lanes — dev builds everything, QA checks it. No design gate. |
|
|
90
|
+
|
|
91
|
+
A route picks one with `promptSet:` in `crew.yaml` (see
|
|
92
|
+
`crew.example.yaml`) — a bare name resolves under this repo's own
|
|
93
|
+
`prompts/`, defaulting to `default` when omitted. Different routes on the
|
|
94
|
+
same ship can run different prompt sets, since which workflow a workspace
|
|
95
|
+
wants is a fact about that workspace, not about the machine running it.
|
|
96
|
+
|
|
97
|
+
**Writing your own** is the expected path once a shipped preset doesn't
|
|
98
|
+
fit: copy a preset directory (`cp -r prompts/default ~/my-crew-policy`),
|
|
99
|
+
edit its prose, and point `promptSet` at the copy (`~/my-crew-policy` or a
|
|
100
|
+
relative path) — no fork of this repo required. A prompt set is a complete,
|
|
101
|
+
self-contained fork of `personas/common.md` + one `personas/lane-<role>.md`
|
|
102
|
+
per seat, plus whatever `skills/*.md` it wants, not a diff against another
|
|
103
|
+
one; each persona file is read as plain prose concatenated ahead of the
|
|
104
|
+
per-run roster/environment/queue sections
|
|
105
|
+
(`src/agent.ts`'s `assemblePrompt`), so there's no templating layer to
|
|
106
|
+
learn. `prompts/dev-qa/` is a worked example of trimming a lane out of
|
|
107
|
+
`default` cleanly, including the unused `lane-design.md` stub every
|
|
108
|
+
preset still needs today — see the next paragraph for why.
|
|
109
|
+
|
|
110
|
+
One current limit: **the role set itself is fixed** — `src/config.ts`'s
|
|
111
|
+
`RoleName` (`dev`, `design`, `qa`, `triage`, `pair`) — so even a prompt set
|
|
112
|
+
with no real use for a polled seat (`dev-qa`'s `design`) still needs a
|
|
113
|
+
`lane-<role>.md` file for every one of them, `pair` included, or `crew
|
|
114
|
+
agents sync` and `planAgentRun` throw looking for it. `prompts/dev-qa/
|
|
115
|
+
lane-design.md` handles this by being a brief that just says "do nothing,
|
|
116
|
+
you shouldn't be staffed" — copy that pattern for any polled seat your own
|
|
117
|
+
policy doesn't use (`pair` is never polled, so its own brief just needs to
|
|
118
|
+
exist, not disclaim itself). Making the seat set itself policy-defined, so
|
|
119
|
+
a prompt set could add or drop a role outright, is a larger change than
|
|
120
|
+
prompt sets took and hasn't been done.
|
|
121
|
+
|
|
122
|
+
## What a workspace has to provide
|
|
123
|
+
|
|
124
|
+
The crew reads its meaning from the board, not from its own source. Statuses,
|
|
125
|
+
the priority order, and which column plays which role are **per-workspace
|
|
126
|
+
facts**, because a ship can be connected to several boards that each made
|
|
127
|
+
different choices. `docs/CONTRACT.md` documents the defaults and what a
|
|
128
|
+
workspace has to override if it names things differently.
|
|
129
|
+
|
|
130
|
+
## Install
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
git clone <this repo> crew
|
|
134
|
+
cd crew
|
|
135
|
+
cp crew.example.yaml ~/.config/crew/crew.yaml
|
|
136
|
+
chmod 600 ~/.config/crew/crew.yaml # it holds an API key
|
|
137
|
+
$EDITOR ~/.config/crew/crew.yaml
|
|
138
|
+
bin/crew doctor # read-only preflight
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The config lives in `~/.config/crew/` rather than in the checkout: it describes
|
|
142
|
+
the **machine**, so reinstalling or replacing the checkout does not lose it.
|
|
143
|
+
`crew` looks in `$CREW_CONFIG`, then `$XDG_CONFIG_HOME/crew`, then
|
|
144
|
+
`~/.config/crew/crew.yaml`, and only then beside the checkout.
|
|
145
|
+
|
|
146
|
+
`crew connect` resolves a workspace/project's ids into the state tree so you do
|
|
147
|
+
not have to look them up by hand. `doctor` then checks the config against
|
|
148
|
+
reality: the checkouts, the tools on PATH, the agent binary, the repo's hooks,
|
|
149
|
+
and whether the board answers. Nothing writes, wakes an agent or deploys until
|
|
150
|
+
a route has `enabled: true`.
|
|
151
|
+
|
|
152
|
+
Then install it as a real service:
|
|
153
|
+
|
|
154
|
+
```sh
|
|
155
|
+
bin/crew install # writes and loads this platform's own units
|
|
156
|
+
bin/crew install --dry-run # see what it would do first
|
|
157
|
+
bin/crew uninstall # unload and remove them
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`install` writes three independent units — `run`, `release` and
|
|
161
|
+
`passengers` — and picks the mechanism itself per host: launchd on macOS, a
|
|
162
|
+
systemd **user** unit on Linux (falling back to a crontab line where
|
|
163
|
+
`systemctl` is not usable), or an error on Windows (not built yet). It always
|
|
164
|
+
uses an absolute interpreter path (`process.execPath`) and points each unit's
|
|
165
|
+
own log at a *different* file than the crew's own — pointing both at one file
|
|
166
|
+
was hit for real, and doubles every line.
|
|
167
|
+
|
|
168
|
+
`run` is a **persistent service**: `crew daemon`, the long-running loop that
|
|
169
|
+
chains cycles immediately when there's work and backs off when there isn't
|
|
170
|
+
(launchd `KeepAlive`/systemd `Restart=on-failure` restart it if it crashes).
|
|
171
|
+
Installing it starts it, the same as installing any other service would.
|
|
172
|
+
`bin/crew daemon start|stop|status|restart` control that same service
|
|
173
|
+
directly — useful without a full reinstall — except on the plain-crontab
|
|
174
|
+
fallback, which cannot supervise a persistent process and so keeps the old
|
|
175
|
+
one-shot-per-fire `crew run` instead. `release` and `passengers` stay on
|
|
176
|
+
their own fixed timers/crontab lines, one-shot per fire same as always: the
|
|
177
|
+
first run of each happens one interval after `install`, and each fire is a
|
|
178
|
+
few API calls — a full agent session only starts when that cheap check finds
|
|
179
|
+
something worth waking for.
|
|
180
|
+
|
|
181
|
+
The daemon also updates itself: on an idle pass (never mid-cycle) it checks
|
|
182
|
+
whether its own installed files (`src`/`dist`/`bin`, or the binary itself for
|
|
183
|
+
a compiled install) have changed since it started, and if so exits cleanly —
|
|
184
|
+
the same crash-recovery restart above then relaunches it against whatever is
|
|
185
|
+
now on disk. `crew daemon restart` forces this immediately, for a manual
|
|
186
|
+
`git pull && crew daemon restart` instead of waiting for the next idle tick.
|
|
187
|
+
|
|
188
|
+
`launchd/com.tablation.crew.plist` is kept only as a reference for what
|
|
189
|
+
`install` generates — hand-editing and loading it directly still works, but
|
|
190
|
+
`crew install`/`crew uninstall` is the supported path now.
|
|
191
|
+
|
|
192
|
+
## Commands
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
crew poll [route] decide a cycle and report it; writes nothing
|
|
196
|
+
crew run [route] [--role R] run the winning role's session, then release
|
|
197
|
+
crew daemon [route] run the persistent supervisor loop in the foreground
|
|
198
|
+
crew daemon start|stop|status control the installed `run` service directly
|
|
199
|
+
crew daemon restart stop then start it (force-picks-up an update)
|
|
200
|
+
crew release [route] merge what QA verified, version it, ship it
|
|
201
|
+
crew merge [route] merge verified branches and stop
|
|
202
|
+
crew deploy [route] release now, even with nothing new to merge
|
|
203
|
+
crew watch [route] live view of what the crew is doing
|
|
204
|
+
crew status [route] paused/running state
|
|
205
|
+
crew doctor [route] [--fix] preflight; --fix enables clean routes, offers crew install
|
|
206
|
+
crew ports [route] which checkout owns which ports, and what is up
|
|
207
|
+
crew reap [route] kill servers left behind by removed worktrees
|
|
208
|
+
crew drop [route] NNN remove a merged ticket's worktree and branch
|
|
209
|
+
crew sync [route] fast-forward the checkout and its worktrees from the remote
|
|
210
|
+
crew pause|resume [route] [R] pause everything, or one role
|
|
211
|
+
crew log [route] tail the log
|
|
212
|
+
crew inbox [--member NAME] your tickets across every workspace
|
|
213
|
+
crew connect WS[/PROJECT] resolve a workspace/project's ids into the state tree
|
|
214
|
+
crew repos add ROUTE PATH attach a local checkout to a route in crew.yaml (--dry-run previews)
|
|
215
|
+
crew repos list ROUTE the checkouts a route has, and the tracker Repos row each matches
|
|
216
|
+
crew install write and load this platform's scheduler unit
|
|
217
|
+
crew uninstall unload and remove it
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Every command that could change something takes `--dry-run`.
|
|
221
|
+
|
|
222
|
+
## One ship, many boards
|
|
223
|
+
|
|
224
|
+
A ship connects to several workspaces at once, and ranks their queues against
|
|
225
|
+
each other — a P0 on one board outranks a P2 on another. The ship is identified
|
|
226
|
+
by `ship.name` matching a row in each board's Ships table; a crew member is
|
|
227
|
+
identified by email, so the same person is recognised across workspaces.
|
|
228
|
+
|
|
229
|
+
An area of development spans **several repositories**, so a checkout is a
|
|
230
|
+
property of the ticket, not of the route: `repos:` maps each name in the
|
|
231
|
+
board's Repos table to a directory on this machine. Naming even one repo
|
|
232
|
+
there makes it a closed list — several ships can divide a multi-repo area's
|
|
233
|
+
work between them, and anything not named is deliberately another ship's,
|
|
234
|
+
marked `NO CHECKOUT` in the digest rather than left blank (blank would read
|
|
235
|
+
as "work it here", which is the wrong directory).
|
|
236
|
+
|
|
237
|
+
Leaving `repos:` out entirely means the opposite: this ship serves
|
|
238
|
+
*everything* the board's Repos table lists, each checkout defaulting to
|
|
239
|
+
`<reposBasePath>/<workspace>/<repoName>` — `reposBasePath` is `~/Crew`
|
|
240
|
+
unless a `ship:` or route-level `reposBasePath:` says otherwise. A repo
|
|
241
|
+
that doesn't exist yet at its derived path is cloned there automatically
|
|
242
|
+
the first time a ticket actually needs it (from the Repos table's own
|
|
243
|
+
`remote` column, `owner/repo` over SSH) — not up front on `crew connect`,
|
|
244
|
+
which only discovers names and remotes, never fetches anything itself.
|
|
245
|
+
|
|
246
|
+
## What lives where
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
bin/crew the entry point
|
|
250
|
+
src/ the implementation (Node, run directly via type stripping)
|
|
251
|
+
test/ its tests, including a fake board for integration runs
|
|
252
|
+
prompts/ prompt sets — prompts/<name>/personas/{common.md,lane-<role>.md}
|
|
253
|
+
+ prompts/<name>/skills/*.md
|
|
254
|
+
docs/ CONTRACT.md, REPO_SPEC.md, MIGRATION.md
|
|
255
|
+
launchd/ timer templates (edit the paths before installing)
|
|
256
|
+
crew.example.yaml copy to ~/.config/crew/crew.yaml
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
The crew carries no ids, no absolute paths, no project commands and no device
|
|
260
|
+
names. Anything specific to a **machine** belongs in `crew.yaml`; anything
|
|
261
|
+
specific to a **repository** belongs in that repository's own `.crew.yaml`
|
|
262
|
+
(`docs/REPO_SPEC.md`), which declares its platform, hooks, branch naming,
|
|
263
|
+
versioning and release mode:
|
|
264
|
+
|
|
265
|
+
| hook | required | what it is for |
|
|
266
|
+
| --- | --- | --- |
|
|
267
|
+
| `test` | yes | run the suite before a release |
|
|
268
|
+
| `build` | yes | build it |
|
|
269
|
+
| `deploy` | when `release.mode: local` | ship it |
|
|
270
|
+
| `setup` | no | prepare a fresh worktree |
|
|
271
|
+
| `ports` | no | print this checkout's port assignments |
|
|
272
|
+
| `version` | no | print the current version |
|
|
273
|
+
| `bump` | no | produce and print the next one |
|
|
274
|
+
| `merged` | no | ask the forge whether a branch actually landed |
|
|
275
|
+
| `released` | no | confirm a commit is live (a sha, or an exact version) |
|
|
276
|
+
|
|
277
|
+
The crew carries no commands of its own: everything it runs to test, build,
|
|
278
|
+
version or ship a repository comes from that repository's own file.
|
|
279
|
+
|
|
280
|
+
One hook is the exception and lives in `crew.yaml` instead: `notify`, which is
|
|
281
|
+
handed a level (`ok` / `warn` / `fail`), a headline and a detail line whenever a
|
|
282
|
+
release ships or is blocked. Where that should be *shown* — a desktop
|
|
283
|
+
notification, a status widget, a webhook, a push — is a fact about the machine
|
|
284
|
+
and the person watching it, not about the code. Define none and release state
|
|
285
|
+
simply goes to the log.
|
|
286
|
+
|
|
287
|
+
A repo's own `.crew.yaml` wins over anything repeated in `crew.yaml`, and
|
|
288
|
+
`crew doctor` reports the duplication as drift. The client can still configure
|
|
289
|
+
a repo that has no `.crew.yaml` of its own.
|
|
290
|
+
|
|
291
|
+
## Status
|
|
292
|
+
|
|
293
|
+
Live on macOS, driving this project's own board. Triage is a **seat**, not a
|
|
294
|
+
second process — there is one timer to install, not two.
|
|
295
|
+
|
|
296
|
+
The bash implementation this replaces has been deleted. Where its behaviour was
|
|
297
|
+
the only specification of something — the priority matrix, the QA digest, the
|
|
298
|
+
crew labels — it was captured as a fixture under `test/fixtures/` first, so the
|
|
299
|
+
tests still hold the port to what the original did.
|
|
300
|
+
|
|
301
|
+
Outstanding: Linux has not been exercised end to end, and the API key sits in
|
|
302
|
+
`crew.yaml` in plaintext until the device-code flow and keychain storage land.
|
package/bin/crew
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// crew — the runner. See src/cli.ts.
|
|
3
|
+
//
|
|
4
|
+
// Runs the built output when it exists, and falls back to running TypeScript
|
|
5
|
+
// directly so a checkout works without a build step. The bash runner this
|
|
6
|
+
// replaces is kept as bin/crew-legacy.sh until cutover is done.
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
import { dirname, join } from 'node:path';
|
|
9
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
10
|
+
|
|
11
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const built = join(here, '..', 'dist', 'cli.js');
|
|
13
|
+
const source = join(here, '..', 'src', 'cli.ts');
|
|
14
|
+
|
|
15
|
+
// CREW_FROM_SOURCE=1 skips the bundle even when one exists. crew's own test
|
|
16
|
+
// suite sets it on every CLI it spawns (test/helpers/crew-bin.ts): dist/ is
|
|
17
|
+
// rebuilt by the release's build hook, which runs AFTER the test gate, so
|
|
18
|
+
// without this the gate exercises the previous release's bundle (CREW-988).
|
|
19
|
+
const fromSource = process.env.CREW_FROM_SOURCE === '1' && existsSync(source);
|
|
20
|
+
|
|
21
|
+
if (existsSync(built) && !fromSource) {
|
|
22
|
+
await import(pathToFileURL(built).href);
|
|
23
|
+
} else if (existsSync(source)) {
|
|
24
|
+
// Node >=22.18 strips types natively; older needs --experimental-strip-types.
|
|
25
|
+
await import(pathToFileURL(source).href);
|
|
26
|
+
} else {
|
|
27
|
+
console.error('crew: no dist/cli.js and no src/cli.ts — is this a complete checkout?');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# crew.yaml — what this ship is, and what it is connected to.
|
|
2
|
+
#
|
|
3
|
+
# Copy to ~/.config/crew/crew.yaml (NOT into the checkout): it describes this
|
|
4
|
+
# MACHINE, so replacing or reinstalling the crew checkout must not lose it.
|
|
5
|
+
# `crew` looks in $CREW_CONFIG, then $XDG_CONFIG_HOME/crew, then
|
|
6
|
+
# ~/.config/crew/crew.yaml, and only then beside the checkout.
|
|
7
|
+
#
|
|
8
|
+
# It holds an API key in plaintext, so: chmod 600.
|
|
9
|
+
#
|
|
10
|
+
# Unknown keys are an ERROR, not a warning. A typo that silently did nothing
|
|
11
|
+
# would be indistinguishable from a setting that does not work.
|
|
12
|
+
|
|
13
|
+
ship:
|
|
14
|
+
# Must match a row in the board's Ships table — that row is how the crew
|
|
15
|
+
# knows which seats are aboard here rather than on someone else's machine.
|
|
16
|
+
name: "My Mac"
|
|
17
|
+
|
|
18
|
+
# Detected from the host. Set it only to override.
|
|
19
|
+
# platform: macos | linux
|
|
20
|
+
|
|
21
|
+
agent:
|
|
22
|
+
bin: "/usr/local/bin/claude" # absolute: a timer runs with a minimal PATH
|
|
23
|
+
model: "claude-sonnet-5"
|
|
24
|
+
# Extended thinking budget, passed as MAX_THINKING_TOKENS. Defaults to
|
|
25
|
+
# 4096 when omitted — without it the CLI never emits `thinking` stream
|
|
26
|
+
# blocks, so Agent Log Cycles reporting has nothing to report. Set to 0
|
|
27
|
+
# to disable (less latency/cost, no cycles reported).
|
|
28
|
+
# maxThinkingTokens: 4096
|
|
29
|
+
|
|
30
|
+
# shell: bash # defaults per platform
|
|
31
|
+
extraPath: "/opt/homebrew/bin" # prepended for hooks
|
|
32
|
+
useNvm: true # source the repo's .nvmrc before hooks
|
|
33
|
+
# stateDir: "state" # defaults to "state", relative to THIS file
|
|
34
|
+
logFile: "/tmp/crew.log" # give each runner its own file — a shared one interleaves
|
|
35
|
+
# Defaults to "Mozilla/5.0 CrewAgent/<this checkout's version>". The
|
|
36
|
+
# Mozilla/5.0 prefix is load-bearing: Cloudflare blocks a bare tool
|
|
37
|
+
# User-Agent before it reaches the tracker API. Set this only to identify
|
|
38
|
+
# a specific ship in the tracker's own request logs.
|
|
39
|
+
# userAgent: "Mozilla/5.0 CrewAgent/1.0"
|
|
40
|
+
|
|
41
|
+
# Where a repo's checkout lives when a route's own `repos:` doesn't say so
|
|
42
|
+
# explicitly (see `repos:` below) — defaults to "~/Crew" if omitted here
|
|
43
|
+
# and at every route. A route-level `reposBasePath:` overrides this one.
|
|
44
|
+
# reposBasePath: "~/Crew"
|
|
45
|
+
|
|
46
|
+
# One ship, many boards. Each route is one workspace/project pair + one area
|
|
47
|
+
# of development within it — `crew run my-workspace/my-product`, the same
|
|
48
|
+
# string `crew connect` takes, addresses it; there is no separate `name:` to
|
|
49
|
+
# keep in sync with that. Keys are workspace-scoped in general, so a route
|
|
50
|
+
# normally carries its own — but if one key happens to be valid for all of
|
|
51
|
+
# them (a personal account key), declare it once as `ship.apiKey` instead and
|
|
52
|
+
# every route that omits its own picks it up.
|
|
53
|
+
routes:
|
|
54
|
+
- route: my-workspace/my-product
|
|
55
|
+
enabled: false # nothing writes, wakes an agent or deploys until this is true
|
|
56
|
+
area: "My Product" # the row of its Projects table this route works
|
|
57
|
+
|
|
58
|
+
# An area spans several repositories, and each needs its own checkout on
|
|
59
|
+
# this machine. Keyed by the name the board's Repos table uses. Naming
|
|
60
|
+
# even one repo here makes this a CLOSED list — a ticket for a repo not
|
|
61
|
+
# listed is one this ship deliberately doesn't work (another ship may),
|
|
62
|
+
# and the queue digest marks it NO CHECKOUT rather than leaving it blank.
|
|
63
|
+
repos:
|
|
64
|
+
My Product: "/home/me/src/my-product"
|
|
65
|
+
My Product CLI: "/home/me/src/my-product-cli"
|
|
66
|
+
|
|
67
|
+
# Leave `repos:` out entirely instead to serve every repo the board's
|
|
68
|
+
# Repos table lists, each one defaulting to
|
|
69
|
+
# `<reposBasePath>/my-workspace/<repoName>` and cloned there
|
|
70
|
+
# automatically (from that repo's own `remote` column) the first time a
|
|
71
|
+
# ticket actually needs it — nothing to list by hand, nothing fetched
|
|
72
|
+
# up front by `crew connect`.
|
|
73
|
+
|
|
74
|
+
# A single-repo route may use `dir:` instead of `repos:`.
|
|
75
|
+
# dir: "/home/me/src/my-product"
|
|
76
|
+
|
|
77
|
+
worktreePrefix: "myproject-issue-"
|
|
78
|
+
|
|
79
|
+
# Opt this route's ship into Host Passengers for this route's workspace
|
|
80
|
+
# (ISSUE-644): `crew connect` syncs this onto that workspace's own Ships
|
|
81
|
+
# row. Config-truth lives here, on the machine — never edit a Ships row
|
|
82
|
+
# by hand expecting it to stick. Defaults to false (not hosting).
|
|
83
|
+
# hostPassengers: true
|
|
84
|
+
|
|
85
|
+
# Which lane-behavior policy this workspace's agents follow — the
|
|
86
|
+
# prose in prompts/<name>/personas/{common.md,lane-<role>.md}, plus
|
|
87
|
+
# whatever prompts/<name>/skills/*.md it ships. Bare name ("dev-qa")
|
|
88
|
+
# picks a preset shipped under crew's own prompts/; a path
|
|
89
|
+
# ("./my-policy" or "~/my-crew-policy") points at a fully custom set
|
|
90
|
+
# you copied and edited yourself, so adopting your own workflow never
|
|
91
|
+
# requires forking crew. Defaults to "default" (the three-lane
|
|
92
|
+
# dev/design/QA policy this repo ships with) when omitted. See
|
|
93
|
+
# prompts/default/ for the shape a custom set needs to have.
|
|
94
|
+
# promptSet: "dev-qa"
|
|
95
|
+
# What a host must be to build a repo lives in THAT repo's own .crew.yaml
|
|
96
|
+
# (any | unix | macos | linux | windows) — never here. A route can span
|
|
97
|
+
# repos with different needs; the ship refuses a route only when it can
|
|
98
|
+
# satisfy none of them.
|
|
99
|
+
# Defaults to https://app.tablation.com when omitted here and on `ship:`.
|
|
100
|
+
# Only needed for a self-hosted tracker.
|
|
101
|
+
# baseUrl: "https://app.tablation.com"
|
|
102
|
+
apiKey: "REPLACE_ME" # or omit if `ship.apiKey` covers this workspace too
|
|
103
|
+
|
|
104
|
+
# Where release state should be SHOWN is a fact about this machine and the
|
|
105
|
+
# person watching it, not about the code — so unlike every other hook,
|
|
106
|
+
# notify lives here rather than in the repo's .crew.yaml. It receives
|
|
107
|
+
# CREW_LEVEL (ok|warn|fail), CREW_HEADLINE, CREW_DETAIL, CREW_ROUTE
|
|
108
|
+
# and CREW_SHIP, and decides everything else. Define none and release state
|
|
109
|
+
# simply goes to the log.
|
|
110
|
+
#
|
|
111
|
+
# hooks:
|
|
112
|
+
# notify: 'terminal-notifier -title "$CREW_HEADLINE" -message "$CREW_DETAIL"'
|
|
113
|
+
|
|
114
|
+
# Prefer putting hooks, labels and release settings in the REPO's own
|
|
115
|
+
# .crew.yaml (docs/REPO_SPEC.md) rather than here. A repo that carries one
|
|
116
|
+
# shadows anything repeated here, and `crew doctor` reports that as drift.
|
|
117
|
+
# These exist for a repo you cannot add a .crew.yaml to:
|
|
118
|
+
#
|
|
119
|
+
# hooks:
|
|
120
|
+
# test: "npm test"
|
|
121
|
+
# build: "npm run build"
|
|
122
|
+
# deploy: "./deploy.sh"
|
|
123
|
+
|
|
124
|
+
# Discovered from the board, not authored by hand, and NOT in this file:
|
|
125
|
+
# `crew connect my-workspace/my-product` (or a matching hand-filled JSON)
|
|
126
|
+
# writes state/resolved/my-workspace/my-product.json — workspaceId,
|
|
127
|
+
# projectId, areaModelId/areaId, the Issues/Comments/Crew model ids, each
|
|
128
|
+
# role's seat, the operator (the human this ship belongs to — set by
|
|
129
|
+
# hand; there is no way to derive it), and any hold (a row a person works
|
|
130
|
+
# through; a ticket assigned to one is off limits to every seat,
|
|
131
|
+
# whatever its status). Without it every cycle would re-resolve the same
|
|
132
|
+
# ids over the network instead of reading a local cache.
|