@openthink/team 0.0.1 → 0.0.2

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 CHANGED
@@ -24,35 +24,60 @@ 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
- ## Vault setup
27
+ ## Quick start
28
28
 
29
- `open-team` reads from a `product-vault` directory. Layout:
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
- product-vault/
33
- ├── 00-meta/templates/ticket.md
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-vault setup, leave `~/Documents/product-vault` in place or set `PRODUCT_VAULT_PATH`. For multiple vaults (personal + work, etc.) see [Config & multiple vaults](#config--multiple-vaults).
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> # ingest external item → tickets/triage/
47
- oteam pull --project <name> ... # tag the new ticket with a project
48
- oteam assign <ticket-or-id> # drive role pipeline (full path or AGT-NNN)
49
- oteam assign --inline <path> # … or run inline in current terminal
50
- oteam assign --no-stamp <id> # bypass the stamp gate (not recommended)
51
- oteam list [--state <state>] # list active tickets
52
- oteam list --project <name> # filter by project frontmatter
53
- oteam archive <ticket-id> # move done ticket to archive/YYYY-MM/
54
- oteam config vault add <path> # register a vault under a name
55
- oteam config vault list # show registered vaults + default
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
56
81
  ```
57
82
 
58
83
  Most commands accept `--vault <name-or-path>` to operate on a specific vault.
@@ -104,16 +129,67 @@ claude --dangerously-skip-permissions --model claude-opus-4-7 "/assign-ticket <p
104
129
 
105
130
  Requires the `claude` CLI on PATH (https://claude.com/claude-code).
106
131
 
107
- ### Spawn-time stamp gate
132
+ ### Spawn-time clone modes (stamp integration)
133
+
134
+ 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.
135
+
136
+ Where the clone comes from is governed by oteam config (`~/.open-team/config.json`, `stamp` block):
108
137
 
109
- For repo-bound tickets (`repo:` frontmatter set), `oteam assign` clones an isolated agent worktree from the stamp server before spawning, and points the spawned session's cwd at it. The clone is the gate: success means the repo is registered on the stamp server (`~/.stamp/server.yml` is read for host + port, and the URL is built as `ssh://git@<host>:<port>/srv/git/<basename>.git`); failure exits non-zero before any spawn. The cloned worktree has exactly one remote — `origin → <stamp-url>` — and shares no `.git/objects` with any clone you keep elsewhere on disk. That's by design: a stamp-signed merge made inside the worktree can only be pushed back to stamp, never pushed direct to GitHub by accident.
138
+ | `stamp` config | Mode | Clone source | Behaviour |
139
+ |------------------------------------------------|-----------|--------------------------------------------------------|-------------------------------------------------------------------------------------------------|
140
+ | absent / `null` | no-stamp | `git@github.com:<repo>.git` | Default. No stamp config files are read. `oteam` works against any git repo. |
141
+ | `{ host, enforce: false }` | soft | `git@github.com:<repo>.git` | Stamp host is recorded for tooling that asks for it; `oteam assign` does not gate. |
142
+ | `{ 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
143
 
111
- The trade is a few seconds of SSH clone time per spawn instead of a near-instant `git worktree add`. For agent flows that immediately spend tens of seconds in an LLM thinking phase, the difference is noise.
144
+ `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
145
 
113
- Pass `--no-stamp` to bypass the gate and clone from `git@github.com:<repo>.git` instead. This is loud (you'll see a stderr line on the spawn) and is **not recommended** — the stamp gate is the safeguard against agents pushing direct to GitHub, so use it only when you've decided the repo is intentionally not stamp-governed (e.g. a public OSS clone, or a one-off scratch repo). Repos that fit this shape need to be told so on every assign; there's no per-repo config to make `--no-stamp` sticky on purpose.
146
+ You can edit the stamp config any time after init:
147
+
148
+ ```sh
149
+ oteam config stamp show # print current host + enforce
150
+ oteam config stamp set --host <url> # set or update the stamp host
151
+ oteam config stamp set --enforce on # turn the per-repo gate on (host required)
152
+ oteam config stamp set --enforce off # … or back off
153
+ oteam config stamp clear # remove the stamp block entirely
154
+ ```
155
+
156
+ `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.
157
+
158
+ > **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
159
 
115
160
  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
161
 
162
+ ## Per-phase model selection
163
+
164
+ 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:`:
165
+
166
+ | ticket state | phase |
167
+ |----------------|------------------|
168
+ | `triage` | `product` |
169
+ | `refined` | `spike` |
170
+ | `in-progress` | `implementation` |
171
+ | `qa` | `qa` |
172
+
173
+ `oteam init` seeds these defaults if no `models` block exists yet:
174
+
175
+ | phase | default model | rationale |
176
+ |------------------|----------------------|-----------------------------------------------------------------|
177
+ | `product` | `claude-sonnet-4-6` | synthesis when the input is rough; cheaper than Opus |
178
+ | `spike` | `claude-opus-4-7` | design judgment, gap-spotting, scope rating |
179
+ | `implementation` | `claude-sonnet-4-6` | multi-file edits + stamp round-trips |
180
+ | `qa` | `claude-sonnet-4-6` | catching AC mismatches against the running system |
181
+
182
+ Inspect the current state with `oteam config models show`. Override with:
183
+
184
+ ```sh
185
+ oteam config models set spike claude-opus-4-7
186
+ oteam config models set implementation claude-sonnet-4-6
187
+ oteam config models show # one phase per line; "(unset)" for unpinned phases
188
+ oteam config models clear spike # falls back to the role-pipeline default
189
+ ```
190
+
191
+ 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).
192
+
117
193
  ## Config & multiple vaults
118
194
 
119
195
  `open-team` supports any number of named vaults via `~/.open-team/config.json`. Register them with:
@@ -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 by default (or from GitHub when invoked with `--no-stamp`); 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.
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 by default, or from GitHub when invoked with `--no-stamp`) 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`.
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