@openthink/team 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +130 -19
- package/dist/assign-ticket.md +2 -2
- package/dist/index.js +1031 -151
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,35 +24,64 @@ npm link
|
|
|
24
24
|
|
|
25
25
|
`v0` is **private and not yet published to npm** (`package.json` has `"private": true`). Flip when the source repo goes public.
|
|
26
26
|
|
|
27
|
-
##
|
|
27
|
+
## Quick start
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
```sh
|
|
30
|
+
oteam init
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Creates `~/openteam/` with the workspace tree below, drops a `.oteam-workspace` sentinel, registers it in `~/.open-team/config.json` (promoting it to default if no default is set), seeds per-phase model defaults (see [Per-phase model selection](#per-phase-model-selection) for the table), and writes the oteam guidance block to `~/AGENTS.md` and `~/CLAUDE.md`. Re-running `oteam init` against an already-initialised path is a clean no-op; running against a non-empty unmarked directory exits non-zero rather than silently merging. Existing per-phase model customisation is preserved across re-runs — defaults are only seeded when the `models` block is absent or empty (run `oteam config models show` to see the current state).
|
|
34
|
+
|
|
35
|
+
Flags:
|
|
36
|
+
|
|
37
|
+
- `oteam init --dir <path>` (or `-w, --workspace <path>`) — workspace location, default `~/openteam/`.
|
|
38
|
+
- `oteam init --docs-dir <path>` — where to write `AGENTS.md` / `CLAUDE.md`, default `$HOME`.
|
|
39
|
+
- `oteam init -y` — skip the interactive workspace-path prompt.
|
|
40
|
+
|
|
41
|
+
> **Breaking change vs. earlier `oteam` builds:** `--dir` used to mean "where to write `AGENTS.md`/`CLAUDE.md`". It now means the workspace location. Use the new `--docs-dir` flag for the previous behaviour.
|
|
42
|
+
|
|
43
|
+
## Workspace setup
|
|
44
|
+
|
|
45
|
+
`open-team` reads from a workspace directory ("vault" is Obsidian's word; oteam doesn't depend on Obsidian). Layout:
|
|
30
46
|
|
|
31
47
|
```
|
|
32
|
-
|
|
33
|
-
├──
|
|
48
|
+
openteam/
|
|
49
|
+
├── .oteam-workspace # sentinel — written by `oteam init`
|
|
50
|
+
├── 00-meta/README.md
|
|
34
51
|
├── tickets/
|
|
35
52
|
│ ├── triage/ refined/ in-progress/ qa/ blocked/
|
|
53
|
+
├── projects/
|
|
36
54
|
└── archive/<YYYY-MM>/
|
|
37
55
|
```
|
|
38
56
|
|
|
39
57
|
A ticket's `state:` frontmatter must always match its containing folder under `tickets/`.
|
|
40
58
|
|
|
41
|
-
For the simplest single-
|
|
59
|
+
For the simplest single-workspace setup, run `oteam init` (creates and registers `~/openteam/`). To use an existing tree, register it via `oteam config vault add <path>` or set `PRODUCT_VAULT_PATH`. For multiple workspaces (personal + work, etc.) see [Config & multiple vaults](#config--multiple-vaults).
|
|
42
60
|
|
|
43
61
|
## Subcommands
|
|
44
62
|
|
|
45
63
|
```sh
|
|
46
|
-
oteam pull <source> <ref>
|
|
47
|
-
oteam pull --project <name> ...
|
|
48
|
-
oteam assign <ticket-or-id>
|
|
49
|
-
oteam assign --inline <path>
|
|
50
|
-
oteam assign --no-stamp <id>
|
|
51
|
-
oteam list [--state <state>]
|
|
52
|
-
oteam list --project <name>
|
|
53
|
-
oteam archive <ticket-id>
|
|
54
|
-
oteam config vault add <path>
|
|
55
|
-
oteam config vault list
|
|
64
|
+
oteam pull <source> <ref> # ingest external item → tickets/triage/
|
|
65
|
+
oteam pull --project <name> ... # tag the new ticket with a project
|
|
66
|
+
oteam assign <ticket-or-id> # drive role pipeline (full path or AGT-NNN)
|
|
67
|
+
oteam assign --inline <path> # … or run inline in current terminal
|
|
68
|
+
oteam assign --no-stamp <id> # one-shot override of stamp.enforce (clones from GitHub)
|
|
69
|
+
oteam list [--state <state>] # list active tickets
|
|
70
|
+
oteam list --project <name> # filter by project frontmatter
|
|
71
|
+
oteam archive <ticket-id> # move done ticket to archive/YYYY-MM/
|
|
72
|
+
oteam config vault add <path> # register a vault under a name
|
|
73
|
+
oteam config vault list # show registered vaults + default
|
|
74
|
+
oteam config stamp set --host <url> # configure stamp host post-init
|
|
75
|
+
oteam config stamp set --enforce on # require repos be stamp-registered
|
|
76
|
+
oteam config stamp clear # remove the stamp block
|
|
77
|
+
oteam config stamp show # print current stamp config
|
|
78
|
+
oteam config models set <phase> <id> # pin a model per role-pipeline phase
|
|
79
|
+
oteam config models clear <phase> # remove a per-phase override
|
|
80
|
+
oteam config models show # print current per-phase overrides
|
|
81
|
+
oteam config telemetry set on|off # toggle per-phase telemetry recording
|
|
82
|
+
oteam config telemetry show # print telemetry on/off state
|
|
83
|
+
oteam telemetry summary [--days N] [--phase X] [--model Y]
|
|
84
|
+
oteam telemetry tail [-n 20] # last N telemetry lines (raw JSONL)
|
|
56
85
|
```
|
|
57
86
|
|
|
58
87
|
Most commands accept `--vault <name-or-path>` to operate on a specific vault.
|
|
@@ -104,16 +133,98 @@ claude --dangerously-skip-permissions --model claude-opus-4-7 "/assign-ticket <p
|
|
|
104
133
|
|
|
105
134
|
Requires the `claude` CLI on PATH (https://claude.com/claude-code).
|
|
106
135
|
|
|
107
|
-
### Spawn-time stamp
|
|
136
|
+
### Spawn-time clone modes (stamp integration)
|
|
137
|
+
|
|
138
|
+
For repo-bound tickets (`repo:` frontmatter set), `oteam assign` clones an isolated agent worktree before spawning and points the spawned session's cwd at it. The cloned worktree has exactly one remote — `origin` — and shares no `.git/objects` with any clone you keep elsewhere on disk, so the agent can never push back into your daily checkout by accident.
|
|
139
|
+
|
|
140
|
+
Where the clone comes from is governed by oteam config (`~/.open-team/config.json`, `stamp` block):
|
|
108
141
|
|
|
109
|
-
|
|
142
|
+
| `stamp` config | Mode | Clone source | Behaviour |
|
|
143
|
+
|------------------------------------------------|-----------|--------------------------------------------------------|-------------------------------------------------------------------------------------------------|
|
|
144
|
+
| absent / `null` | no-stamp | `git@github.com:<repo>.git` | Default. No stamp config files are read. `oteam` works against any git repo. |
|
|
145
|
+
| `{ host, enforce: false }` | soft | `git@github.com:<repo>.git` | Stamp host is recorded for tooling that asks for it; `oteam assign` does not gate. |
|
|
146
|
+
| `{ host, enforce: true }` | enforce | `<host>/srv/git/<basename>.git` (the stamp server) | The clone IS the gate: clone failure exits non-zero before any spawn. AGT-050 behaviour. |
|
|
110
147
|
|
|
111
|
-
|
|
148
|
+
`oteam init` walks you through setting `stamp.host` and `stamp.enforce` interactively. Re-running `oteam init` pre-fills the prompts; press enter to keep current values. Pass `oteam init --skip-stamp` to skip the prompts on a re-run when you only want to refresh the workspace tree or docs blocks.
|
|
112
149
|
|
|
113
|
-
|
|
150
|
+
You can edit the stamp config any time after init:
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
oteam config stamp show # print current host + enforce
|
|
154
|
+
oteam config stamp set --host <url> # set or update the stamp host
|
|
155
|
+
oteam config stamp set --enforce on # turn the per-repo gate on (host required)
|
|
156
|
+
oteam config stamp set --enforce off # … or back off
|
|
157
|
+
oteam config stamp clear # remove the stamp block entirely
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`oteam assign --no-stamp` is a per-run override: it forces the github clone path even when `stamp.enforce: true` is set. The persistent setting is `oteam config stamp set --enforce off`; `--no-stamp` is convenient when you want to spawn a one-off agent without touching config.
|
|
161
|
+
|
|
162
|
+
> **Migration note.** Earlier `oteam` builds read `~/.stamp/server.yml` directly. This version does not — to keep the AGT-050 stamp gate in place after upgrade, run `oteam init` and paste the host (or `oteam config stamp set --host <url> --enforce on`).
|
|
114
163
|
|
|
115
164
|
Stale workspaces from prior assigns are GC'd at spawn time: any `/tmp/open-team-issues/agt-N/` directory whose ticket id has no matching ticket in the active vault is `rm -rf`'d before the new clone. The current run's workspace is also `rm -rf`'d before its clone, so re-assigns are hermetic.
|
|
116
165
|
|
|
166
|
+
## Per-phase model selection
|
|
167
|
+
|
|
168
|
+
Each role-pipeline phase can run on a different Claude model. The runner reads `models[phase]` from `~/.open-team/config.json` before each spawn and passes it as `claude --model <id>`. Phase resolution from the ticket's `state:`:
|
|
169
|
+
|
|
170
|
+
| ticket state | phase |
|
|
171
|
+
|----------------|------------------|
|
|
172
|
+
| `triage` | `product` |
|
|
173
|
+
| `refined` | `spike` |
|
|
174
|
+
| `in-progress` | `implementation` |
|
|
175
|
+
| `qa` | `qa` |
|
|
176
|
+
|
|
177
|
+
`oteam init` seeds these defaults if no `models` block exists yet:
|
|
178
|
+
|
|
179
|
+
| phase | default model | rationale |
|
|
180
|
+
|------------------|----------------------|-----------------------------------------------------------------|
|
|
181
|
+
| `product` | `claude-sonnet-4-6` | synthesis when the input is rough; cheaper than Opus |
|
|
182
|
+
| `spike` | `claude-opus-4-7` | design judgment, gap-spotting, scope rating |
|
|
183
|
+
| `implementation` | `claude-sonnet-4-6` | multi-file edits + stamp round-trips |
|
|
184
|
+
| `qa` | `claude-sonnet-4-6` | catching AC mismatches against the running system |
|
|
185
|
+
|
|
186
|
+
Inspect the current state with `oteam config models show`. Override with:
|
|
187
|
+
|
|
188
|
+
```sh
|
|
189
|
+
oteam config models set spike claude-opus-4-7
|
|
190
|
+
oteam config models set implementation claude-sonnet-4-6
|
|
191
|
+
oteam config models show # one phase per line; "(unset)" for unpinned phases
|
|
192
|
+
oteam config models clear spike # falls back to the role-pipeline default
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Each field is independent. Unset phases fall back to the role-pipeline default (currently `claude-opus-4-7`); validation is "non-empty string", and the SDK rejects unknown ids at spawn time. Re-running `oteam init` against a config that already has any per-phase model set leaves the entire `models` block alone — your customisation wins. A spike that auto-proceeds to implementation in the same session keeps the spike-phase model (one model per spawn).
|
|
196
|
+
|
|
197
|
+
## Telemetry
|
|
198
|
+
|
|
199
|
+
Every role-pipeline spawn records one JSON line capturing wall-clock + token usage to `~/.open-team/telemetry/runs.jsonl`. The intent is data-driven model tuning — the per-phase defaults above are educated guesses, and the only way to know whether Sonnet QA actually catches what Opus QA does is to measure both.
|
|
200
|
+
|
|
201
|
+
Each line looks like:
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
{ "ticket": "AGT-108", "phase": "implementation", "model": "claude-sonnet-4-6",
|
|
205
|
+
"started-at": "2026-05-04T10:00:00.000Z", "ended-at": "2026-05-04T10:05:42.000Z",
|
|
206
|
+
"wall-clock-ms": 342000,
|
|
207
|
+
"tokens": { "input": 230, "output": 120, "cache-read": 18100, "cache-write": 50 },
|
|
208
|
+
"outcome": "done" }
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
`outcome` is one of `done` / `paused` / `failed` / `unknown`, classified from the STOP banner the role-pipeline body emits (`✅ DONE` / `⏸️ PAUSED` / `🛑 BLOCKED`). A non-zero `claude` exit pins the outcome to `failed` regardless of marker.
|
|
212
|
+
|
|
213
|
+
Inspect:
|
|
214
|
+
|
|
215
|
+
```sh
|
|
216
|
+
oteam telemetry tail # last 20 lines (raw JSONL)
|
|
217
|
+
oteam telemetry summary # aggregate by phase × model
|
|
218
|
+
oteam telemetry summary --days 7
|
|
219
|
+
oteam telemetry summary --phase implementation --model claude-sonnet-4-6
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Knobs:
|
|
223
|
+
|
|
224
|
+
- `OTEAM_TELEMETRY_DIR=<path>` overrides the default `~/.open-team/telemetry/` location.
|
|
225
|
+
- `oteam config telemetry set off` opts out — no JSONL writes occur. Re-enable with `oteam config telemetry set on`. Default is on.
|
|
226
|
+
- Telemetry is best-effort: a write failure (read-only dir, missing session file, malformed log) writes one line to stderr and does not fail the role-pipeline phase. Token fields the agent SDK doesn't expose are omitted from the line rather than recorded as zero.
|
|
227
|
+
|
|
117
228
|
## Config & multiple vaults
|
|
118
229
|
|
|
119
230
|
`open-team` supports any number of named vaults via `~/.open-team/config.json`. Register them with:
|
package/dist/assign-ticket.md
CHANGED
|
@@ -13,7 +13,7 @@ You are working a `product-vault` ticket. The user invoked `oteam assign <path>`
|
|
|
13
13
|
3. **No commits, no PRs, no Linear.** Vault tickets do not necessarily map to a code repo. Only act on code if the ticket's `repo:` field is set AND the work demands it.
|
|
14
14
|
4. **STOP at every role-handoff boundary.** When your role is done, write a STOP marker (visual banner per Output discipline below) and let the human decide whether to continue.
|
|
15
15
|
5. **3-attempt cap on any failing operation.** If a step fails (e.g., file mv fails, frontmatter parse fails, build/test fails), you get 3 tries before STOPPing.
|
|
16
|
-
6. **Never read or write inside `$HOME/Development/<repo>`.** That tree may have uncommitted in-flight work; entangling with it is a sterile-field violation. For repo-bound tickets, the `oteam` runner has already prepared an isolated agent worktree at `/tmp/open-team-issues/<ticket-id-lowercased>/repo` and spawned you cd'd into it — that's your only valid working directory. The runner clones from the stamp server
|
|
16
|
+
6. **Never read or write inside `$HOME/Development/<repo>`.** That tree may have uncommitted in-flight work; entangling with it is a sterile-field violation. For repo-bound tickets, the `oteam` runner has already prepared an isolated agent worktree at `/tmp/open-team-issues/<ticket-id-lowercased>/repo` and spawned you cd'd into it — that's your only valid working directory. The runner clones from the stamp server when `stamp.enforce: true` is set in `~/.open-team/config.json`, otherwise from GitHub directly; either way, the worktree is isolated from your primary, so AC-shaped requirements like "primary's `git remote -v` is byte-equal before/after a spawn" are satisfied by construction. If your `$PWD` is not the prepared workspace (e.g. you invoked the slash command by hand outside of `oteam assign`), set up the workspace yourself before reading any repo file — see Phase 3 Step 0.
|
|
17
17
|
|
|
18
18
|
## Phase 0 — Read the ticket
|
|
19
19
|
|
|
@@ -63,7 +63,7 @@ Write the comment in this shape:
|
|
|
63
63
|
|
|
64
64
|
## Phase 3 — Engineering agent (state: refined → spike phase)
|
|
65
65
|
|
|
66
|
-
**Step 0 — Workspace is already prepared.** When `oteam assign` spawned you against a repo-bound ticket, it already cloned `/tmp/open-team-issues/<ticket-id-lowercased>/repo` (from the stamp server
|
|
66
|
+
**Step 0 — Workspace is already prepared.** When `oteam assign` spawned you against a repo-bound ticket, it already cloned `/tmp/open-team-issues/<ticket-id-lowercased>/repo` (from the stamp server when `stamp.enforce: true` is set in `~/.open-team/config.json`; from GitHub otherwise, or when `--no-stamp` was passed) and set your cwd to it. Confirm with `pwd` and `git remote -v`; for stamp-governed repos you should see exactly one remote, `origin`, pointing at `ssh://git@<stamp-host>:<port>/srv/git/<basename>.git`.
|
|
67
67
|
|
|
68
68
|
Cost trade: a fresh stamp clone adds a few seconds vs. the older `git worktree add` fast path. That's an intentional trade for AC-grade isolation — the agent worktree shares no `.git/objects` and no remotes with your primary, and removing or renaming any remote inside the worktree cannot leak back to your daily flow.
|
|
69
69
|
|