@skein-code/cli 0.2.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.
@@ -0,0 +1,133 @@
1
+ # Architecture
2
+
3
+ ```text
4
+ ┌──────────────────────────────────────────────────────────────┐
5
+ │ CLI / interactive TUI │
6
+ │ prompt · stdin · JSONL · permissions · sessions · telemetry │
7
+ └─────────────────────────────┬────────────────────────────────┘
8
+
9
+ ┌─────────────────────────────▼────────────────────────────────┐
10
+ │ Agent runner │
11
+ │ stable rules → dynamic context → model → tools → verify │
12
+ │ → persist / summarize │
13
+ └──────────────┬──────────────────────┬────────────────────────┘
14
+ │ │
15
+ ┌──────────────▼─────────────┐ ┌─────▼────────────────────────┐
16
+ │ Context fabric │ │ Trust layer │
17
+ │ ContextEngine CLI │ │ workspace boundary │
18
+ │ local BM25/path/symbol │ │ allow / ask / deny │
19
+ │ @file resolver │ │ command policy · hooks │
20
+ │ token-budgeted packer │ │ checkpoints · audit events │
21
+ └──────────────┬─────────────┘ └─────┬────────────────────────┘
22
+ │ │
23
+ ┌──────────────▼──────────────────────▼────────────────────────┐
24
+ │ Capability tools │
25
+ │ read · list · search · write · patch · shell · git · tasks │
26
+ └─────────────────────────────┬────────────────────────────────┘
27
+
28
+ ┌─────────────────────────────▼────────────────────────────────┐
29
+ │ Model gateway │
30
+ │ OpenAI · Anthropic · Gemini · compatible HTTP │
31
+ └──────────────────────────────────────────────────────────────┘
32
+ ```
33
+
34
+ ## Agent turn
35
+
36
+ 1. Resolve `@path` mentions inside configured workspace roots.
37
+ 2. Ask the selected context engine for task-relevant spans under the configured
38
+ token budget.
39
+ 3. Combine product rules, project rules, retrieved spans, mentions, current plan,
40
+ and conversation history.
41
+ 4. Call the model with the tools allowed by the current mode.
42
+ 5. Evaluate every requested tool against workspace and permission policy.
43
+ 6. Create a checkpoint before the first mutation in a tool batch.
44
+ 7. Execute tools, emit events, and append their grounded results to the model
45
+ conversation.
46
+ 8. Continue until the model returns a final response or the turn limit is hit.
47
+ 9. Run configured verification commands after changes and persist the session.
48
+
49
+ ### Prompt layers
50
+
51
+ The runner keeps the cacheable system prefix separate from mutable task state:
52
+
53
+ 1. Stable prefix: safety rules, workspace roots, trusted project rules, and the
54
+ selected expert role.
55
+ 2. Dynamic turn state: intent directive, current plan, working memory, compacted
56
+ handoff, workflow instructions, activated Skills, retrieved memories, and
57
+ current code evidence.
58
+ 3. Conversation: recent user/assistant/tool messages, with old tool output
59
+ replaced by structured receipts when context pressure rises.
60
+
61
+ This lets providers reuse stable prompt prefixes while ensuring a changed plan
62
+ or newly retrieved file is visible on the next turn. Every retrieved or generated
63
+ state block is marked as untrusted context and cannot authorize a tool call.
64
+
65
+ ## Context selection
66
+
67
+ `context.engine: auto` is the recommended setting.
68
+
69
+ ```text
70
+ contextengine executable healthy?
71
+ / \
72
+ yes no
73
+ / \
74
+ hybrid external retrieval local incremental index
75
+ symbols + vectors + graph BM25 + path + symbol boosts
76
+ \ /
77
+ token-budget pack
78
+ ```
79
+
80
+ The fallback is intentional. A missing database, embedding endpoint, or external
81
+ binary should reduce retrieval quality, not make the coding agent unusable.
82
+
83
+ ## Storage
84
+
85
+ Project-local data is kept in `.mosaic/` and ignored by default:
86
+
87
+ - `config.json` — project overrides;
88
+ - `index.json` — local fallback index;
89
+ - `sessions/` — auditable conversation and tool state;
90
+ - `checkpoints/` — pre-mutation file snapshots and manifests.
91
+
92
+ No source content is sent anywhere except the model endpoint selected by the
93
+ user. With local-compatible model and local retrieval endpoints, the complete
94
+ stack can remain self-hosted.
95
+
96
+ Durable memory uses SQLite in WAL mode with FTS5 and bounded lexical fallback;
97
+ it does not require a hosted vector service. Records carry scope, kind,
98
+ confidence, provenance, revision, supersession, verification, and expiry. Model
99
+ inferences enter `memory_candidates` first. Approval promotes a candidate into
100
+ the durable table and can archive a conflicting older fact. Rejected or expired
101
+ candidates never enter retrieval. This write → manage → read loop keeps memory
102
+ useful without silently accumulating guesses.
103
+
104
+ ## Security boundaries
105
+
106
+ - File tools resolve and validate paths against configured workspace roots.
107
+ - Read, write, shell, Git, and network have independent policies.
108
+ - Repository-local configuration is treated as data-only by default: hooks,
109
+ custom executables, verification commands, checkpoint overrides, and
110
+ permission changes require `--trust-project-config` or an explicit config.
111
+ - Project API keys and remote provider/endpoint overrides also require explicit
112
+ trust; loopback compatible settings are retained for local-model workflows.
113
+ - `skein init` stores only a path-bound SHA-256 fingerprint in user-owned
114
+ Skein state. It allows those model routing fields while invalidating the
115
+ narrow trust after any model-setting edit; hook and permission trust is never
116
+ persisted.
117
+ - Destructive commands are denied before ordinary approval rules are evaluated.
118
+ - Allow-listed commands cannot contain shell control or substitution syntax.
119
+ - Allow rules do not override derived write or network permission categories.
120
+ - Ask and Plan modes remove mutating capabilities from autonomous execution.
121
+ Plan mode additionally injects a read-only, approval-oriented planning
122
+ directive; Build mode is required before workspace mutation is possible.
123
+ - Hooks are bounded subprocesses and receive structured environment metadata.
124
+ - Checkpoint restore validates paths before writing snapshots back.
125
+ - Session and checkpoint directories reject symlinked `.mosaic` storage paths;
126
+ local index files are schema-checked and out-of-root entries are discarded.
127
+ - Project-declared workspace roots must be existing, non-symlink directories
128
+ whose real paths remain inside the primary project.
129
+ - Git execution uses a subcommand allow-list, disables repository hooks and
130
+ external config overrides, resolves its executable outside workspace-owned
131
+ `PATH` entries, and reports non-zero exits as failed tool results. Operations
132
+ that can invoke transport, signing, merge, or checkout helpers also require
133
+ shell permission.
@@ -0,0 +1,342 @@
1
+ # Multi-Model Team Cockpit
2
+
3
+ Skein's team mode treats models as replaceable specialists, not fixed brand
4
+ stereotypes. A project can route `frontend`, `backend`, `architect`, `research`,
5
+ `security`, `tester`, and `reviewer` profiles to different providers. The main
6
+ agent remains the only writer; specialists inspect independently, exchange
7
+ bounded reports, and a reviewer accepts or requests one revision round.
8
+
9
+ ## Why This Shape
10
+
11
+ Current products validate parts of the experience:
12
+
13
+ - [Claude Code agent teams](https://code.claude.com/docs/en/agent-teams) use a
14
+ lead, independent context windows, a shared task list, direct teammate
15
+ messages, and optional split panes. Anthropic still labels the feature
16
+ experimental and documents coordination and shutdown limitations.
17
+ - [Aider Architect mode](https://aider.chat/2024/09/26/architect.html) reports
18
+ better editing results when planning/reasoning and editing are assigned to
19
+ separate model calls.
20
+ - [Microsoft AutoGen Selector Group Chat](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/selector-group-chat.html)
21
+ demonstrates model-selected speakers and bounded termination conditions.
22
+ - [Codeg](https://github.com/xintaofei/codeg),
23
+ [ufoo](https://github.com/Icyoung/ufoo),
24
+ [agtx](https://github.com/fynnfluegge/agtx), and
25
+ [comux](https://github.com/BunsDev/comux) explore multi-CLI aggregation,
26
+ shared blackboards, visible terminals, tmux, and worktree isolation.
27
+
28
+ The product gap is not opening the maximum number of terminals. It is making
29
+ routing, authority, evidence, cost, cancellation, disagreement, and acceptance
30
+ understandable in one place.
31
+
32
+ Official model claims also change quickly. OpenAI describes current reasoning
33
+ models as suitable for complex coding and multi-step agentic work in its
34
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning),
35
+ while provider-specific releases publish their own benchmarks. Those claims are
36
+ useful priors, not permanent role assignments. Skein should eventually learn
37
+ workspace-specific routing from accepted/rejected outcomes, latency, cost, and
38
+ test results.
39
+
40
+ ## Interaction Contract
41
+
42
+ In the TUI:
43
+
44
+ ```text
45
+ /team Design and validate session sharing across worktrees
46
+ ```
47
+
48
+ The main transcript remains on the left. At 100 columns or wider, a Team
49
+ Cockpit appears on the right with the active profile, provider/model route,
50
+ phase (`work`, `review`, or `revision`), state, and recent peer handoffs. Narrow
51
+ terminals keep the same information in the normal timeline.
52
+
53
+ Press `Ctrl+T` or run `/workbench` to focus the interactive Team Workbench. It
54
+ switches between `Agents`, `Tasks`, and `Messages`; arrow keys select an item,
55
+ `Enter` expands the selected report and observable alerts, `s` stops a running
56
+ Agent, `r` requests a fresh attempt for a running Agent, and `Esc` returns to
57
+ the transcript. The focused view uses the full available width, including on
58
+ narrow terminals, so the same run summary and safe telemetry remain available
59
+ without exposing hidden chain-of-thought. Completed Agents are immutable in
60
+ this first control pass; their persisted reports remain available through the
61
+ Team Run inspection commands.
62
+
63
+ The workflow is:
64
+
65
+ ```mermaid
66
+ flowchart LR
67
+ U["User objective"] --> L["Main agent / lead"]
68
+ L --> A["Independent specialists"]
69
+ A --> B["Bounded shared reports"]
70
+ B --> R["Reviewer model"]
71
+ R -->|"ACCEPT"| L
72
+ R -->|"REVISE, max N rounds"| A
73
+ L --> W["Single writer implementation"]
74
+ W --> V["Deterministic tests and checks"]
75
+ ```
76
+
77
+ This is intentionally not a free-form infinite group chat. Every run has an
78
+ objective, bounded specialists, a reviewer, a revision cap, cancellation
79
+ propagation, and a deterministic return value. By default, Skein persists a
80
+ local Team Run manifest under the active namespace's `team-runs/` directory.
81
+ Reports and peer handoffs are content-addressed blobs; the manifest stores
82
+ hashes, phases, providers, models, and acceptance status.
83
+
84
+ Inspect or remove runs with:
85
+
86
+ ```bash
87
+ skein agents runs
88
+ skein agents show <run-id-or-prefix>
89
+ skein agents delete <run-id-or-prefix> --yes
90
+ ```
91
+
92
+ Set `agents.persistBoard` to `false` when a session must not retain team
93
+ reports. The normal default is local persistence because it makes interrupted
94
+ runs, reviewer disagreements, and delivery audits recoverable without sending
95
+ the blackboard to a hosted service.
96
+
97
+ ## Configuration
98
+
99
+ Credentials are referenced by environment-variable name. They are never stored
100
+ inside the project config.
101
+
102
+ ### Guided setup
103
+
104
+ The shortest setup path is the interactive user-level wizard:
105
+
106
+ ```bash
107
+ skein agents setup
108
+ ```
109
+
110
+ It asks for a connection name, provider, endpoint, credential environment
111
+ variable, and default model. The wizard writes only the environment-variable
112
+ name and saves shared settings under the user Skein namespace, so the same
113
+ connection is available in every workspace. The non-interactive equivalent is
114
+ useful for provisioning:
115
+
116
+ ```bash
117
+ skein agents setup --yes \
118
+ --name team-relay \
119
+ --provider compatible \
120
+ --base-url https://relay.example/v1 \
121
+ --api-key-env TEAM_RELAY_API_KEY \
122
+ --model openai/coding-model
123
+ ```
124
+
125
+ Run this command outside an active TUI session; `/connections setup` displays
126
+ the same next action without placing a secret in the session transcript.
127
+
128
+ ### Authentication paths
129
+
130
+ Skein keeps subscription login and API routing separate because they have
131
+ different ownership and billing semantics:
132
+
133
+ - Subscription users authenticate once in each installed official CLI. Codex
134
+ supports ChatGPT subscription login, Claude Code supports Claude.ai/Teams
135
+ login, and Gemini CLI supports Google account login. A Skein route with
136
+ `runtime: "codex"`, `"claude"`, or `"grok"` reuses that CLI's local login;
137
+ Skein does not read, copy, or persist its tokens.
138
+ - Direct API users reference the provider's environment variable on each
139
+ route, or let a compatible parent route inherit the same endpoint.
140
+ - Relay/gateway users define one named `connection` and reuse it across model
141
+ routes. This matches gateways such as OpenRouter or LiteLLM that expose many
142
+ models behind one OpenAI-compatible endpoint and bearer key.
143
+
144
+ Official authentication references: [Codex](https://developers.openai.com/codex/auth),
145
+ [Claude Code](https://code.claude.com/docs/en/authentication), and
146
+ [Gemini CLI](https://geminicli.com/docs/get-started/authentication/). Unified
147
+ gateway examples: [OpenRouter](https://openrouter.ai/docs/quickstart) and
148
+ [LiteLLM](https://docs.litellm.ai/docs/learn/gateway_quickstart).
149
+
150
+ ```json
151
+ {
152
+ "agents": {
153
+ "enabled": true,
154
+ "maxConcurrent": 3,
155
+ "maxDelegations": 6,
156
+ "reviewerProfile": "reviewer",
157
+ "maxReviewRounds": 1,
158
+ "cockpit": true,
159
+ "budgetMode": "observe",
160
+ "routes": {
161
+ "research": {
162
+ "runtime": "grok",
163
+ "provider": "compatible",
164
+ "model": "your-grok-model"
165
+ },
166
+ "frontend": {
167
+ "runtime": "claude",
168
+ "provider": "anthropic",
169
+ "model": "your-frontend-model"
170
+ },
171
+ "backend": {
172
+ "runtime": "codex",
173
+ "provider": "openai",
174
+ "model": "your-reasoning-model"
175
+ },
176
+ "reviewer": {
177
+ "provider": "gemini",
178
+ "model": "your-review-model",
179
+ "apiKeyEnv": "GEMINI_API_KEY"
180
+ }
181
+ }
182
+ }
183
+ }
184
+ ```
185
+
186
+ For a relay that exposes many model families through one key, configure the
187
+ credential once in the user environment:
188
+
189
+ ```bash
190
+ export TEAM_RELAY_API_KEY="..."
191
+ ```
192
+
193
+ For the common case, set one team default. Every profile inherits this
194
+ connection and model, so there is no need to repeat the same block for each
195
+ role. Add a profile entry only when that role needs a different model:
196
+
197
+ ```json
198
+ {
199
+ "agents": {
200
+ "defaultConnection": "team-relay",
201
+ "defaultModel": "openai/coding-model",
202
+ "connections": {
203
+ "team-relay": {
204
+ "provider": "compatible",
205
+ "baseUrl": "https://relay.example/v1",
206
+ "apiKeyEnv": "TEAM_RELAY_API_KEY"
207
+ }
208
+ },
209
+ "routes": {
210
+ "frontend": {"model": "anthropic/frontend-model"},
211
+ "reviewer": {"model": "openai/reviewer-model"}
212
+ }
213
+ }
214
+ }
215
+ ```
216
+
217
+ The equivalent fully explicit form is still supported when different roles
218
+ need different connections:
219
+
220
+ ```json
221
+ {
222
+ "agents": {
223
+ "connections": {
224
+ "team-relay": {
225
+ "provider": "compatible",
226
+ "baseUrl": "https://relay.example/v1",
227
+ "apiKeyEnv": "TEAM_RELAY_API_KEY"
228
+ }
229
+ },
230
+ "routes": {
231
+ "architect": {
232
+ "connection": "team-relay",
233
+ "model": "anthropic/architecture-model"
234
+ },
235
+ "backend": {
236
+ "connection": "team-relay",
237
+ "model": "openai/coding-model"
238
+ },
239
+ "frontend": {
240
+ "connection": "team-relay",
241
+ "model": "google/frontend-model"
242
+ },
243
+ "reviewer": {
244
+ "connection": "team-relay",
245
+ "model": "openai/reviewer-model"
246
+ }
247
+ }
248
+ }
249
+ }
250
+ ```
251
+
252
+ Run `skein agents connections` or `/connections` to inspect the resolved
253
+ endpoint, environment-variable reference, and route count without revealing
254
+ the key. For compatible/OpenAI connections, run
255
+ `skein agents models team-relay` to inspect the provider's `/models` catalog
256
+ before choosing route IDs. Discovery is read-only and does not rewrite config;
257
+ native Anthropic/Gemini subscription catalogs remain owned by their official
258
+ CLIs. Named connections are best kept in user-level configuration. A
259
+ repository-owned connection or route remains disabled until the project config
260
+ is explicitly trusted, preventing a cloned repository from redirecting a
261
+ developer's key and source context to an attacker-controlled endpoint.
262
+
263
+ Routing precedence is explicit and predictable: a profile route overrides team
264
+ defaults; a route that specifies `provider` without `connection` bypasses the
265
+ default connection; otherwise the current parent model is used when no team
266
+ default is configured. `skein agents list`, `/agents`, and `/team` show whether
267
+ each profile uses the parent, team default, or a profile override.
268
+
269
+ `runtime` defaults to `api`. The initial external adapters invoke installed
270
+ `codex`, `claude`, or `grok` binaries without a shell and enforce each CLI's
271
+ read-only/plan mode, bounded output, parent cancellation, and non-persistent
272
+ session option. Their existing login/config owns credentials. External output
273
+ is normalized into the same peer-report protocol, so API and CLI teammates can
274
+ participate in one council.
275
+
276
+ Routes loaded from repository-owned config are ignored until the project is
277
+ trusted because a malicious endpoint could exfiltrate environment credentials
278
+ or source context.
279
+
280
+ Budget thresholds are opt-in policy, not a default task-size limit:
281
+
282
+ - `observe` is the default. Skein records token, tool, and elapsed-time
283
+ telemetry but does not warn or stop a worker. Configured thresholds are
284
+ ignored for enforcement in this mode.
285
+ - `guard` compares telemetry with configured thresholds, emits a soft warning,
286
+ and lets the worker continue.
287
+ - `strict` enforces configured thresholds and may stop a worker. Use it only
288
+ when a user or an automation explicitly needs a hard ceiling.
289
+
290
+ Team-wide thresholds use `maxAgentTokens`, `maxAgentToolCalls`, and
291
+ `agentTimeoutMs`. Each route may override them with `tokenBudget`,
292
+ `maxToolCalls`, `timeoutMs`, and its own `budgetMode`. For example:
293
+
294
+ ```json
295
+ {
296
+ "agents": {
297
+ "budgetMode": "guard",
298
+ "maxAgentTokens": 120000,
299
+ "maxAgentToolCalls": 120,
300
+ "agentTimeoutMs": 600000,
301
+ "routes": {
302
+ "reviewer": {
303
+ "budgetMode": "strict",
304
+ "tokenBudget": 30000
305
+ }
306
+ }
307
+ }
308
+ }
309
+ ```
310
+
311
+ These task thresholds are separate from a model's context window and Skein's
312
+ session compaction/context limits. A context boundary still exists because a
313
+ provider cannot accept an unlimited prompt; it is not treated as a user task
314
+ budget.
315
+
316
+ The Team Cockpit shows observable phase, current tool, elapsed time, token
317
+ usage, tool count, soft warnings, and final acceptance state. It deliberately
318
+ does not show hidden chain-of-thought; model reports, peer handoffs, tool
319
+ activity, and reviewer decisions are the explainable artifacts.
320
+
321
+ ## Current Safety Boundary
322
+
323
+ - Specialist agents are read-only and cannot recursively delegate.
324
+ - Only the main agent may mutate the active workspace.
325
+ - Peer messages are summaries capped before entering another context.
326
+ - Review rounds are capped at three by schema and default to one.
327
+ - Cancellation uses the parent abort signal.
328
+ - Model routes inherit a credential only when provider and endpoint match the
329
+ parent; otherwise an explicit `apiKeyEnv` is required.
330
+
331
+ ## Next Increments
332
+
333
+ 1. Add provider-native search/tool adapters so a research route can use live
334
+ search without granting arbitrary shell/network authority.
335
+ 2. Persist a content-addressed team blackboard and compact provenance bundle.
336
+ 3. Add per-route cost accounting and user-confirmed spend controls.
337
+ 4. Add worktree-isolated writer agents with explicit merge/review gates.
338
+ 5. Score routes from project-local eval outcomes instead of relying on model
339
+ brand assumptions.
340
+ 6. Add Gemini CLI and optional tmux/iTerm visible-pane hosts. Codex, Claude,
341
+ and Grok headless adapters already use the shared event and acceptance
342
+ protocol.