cowork-flow 0.0.19 → 0.0.21

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.
Files changed (69) hide show
  1. package/README.md +65 -20
  2. package/package.json +1 -1
  3. package/src/cli.js +109 -2
  4. package/src/commands/init.js +61 -4
  5. package/src/commands/sync.js +10 -1
  6. package/src/lib/copy-template.js +38 -7
  7. package/src/lib/platforms.js +73 -0
  8. package/template/.agent/skills/brainstorming/SKILL.md +13 -9
  9. package/template/.agent/skills/finish-work/SKILL.md +3 -3
  10. package/template/.agent/skills/meta/SKILL.md +1 -1
  11. package/template/.agent/skills/start/SKILL.md +38 -14
  12. package/template/.agent/skills/writing-plans/SKILL.md +1 -1
  13. package/template/.claude/agents/cowork-check.md +41 -0
  14. package/template/.claude/agents/cowork-implement.md +40 -0
  15. package/template/.claude/agents/cowork-research.md +40 -0
  16. package/template/.claude/commands/cowork-check.md +27 -0
  17. package/template/.claude/commands/cowork-implement.md +27 -0
  18. package/template/.claude/commands/cowork-research.md +27 -0
  19. package/template/.claude/hooks/inject-workflow-state.py +298 -0
  20. package/template/.claude/settings.json +24 -0
  21. package/template/.claude/skills/before-dev/SKILL.md +31 -0
  22. package/template/.claude/skills/brainstorming/SKILL.md +32 -0
  23. package/template/.claude/skills/break-loop/SKILL.md +26 -0
  24. package/template/.claude/skills/check/SKILL.md +29 -0
  25. package/template/.claude/skills/continue/SKILL.md +29 -0
  26. package/template/.claude/skills/finish-work/SKILL.md +32 -0
  27. package/template/.claude/skills/meta/SKILL.md +30 -0
  28. package/template/.claude/skills/python-design/SKILL.md +27 -0
  29. package/template/.claude/skills/start/SKILL.md +78 -0
  30. package/template/.claude/skills/update-spec/SKILL.md +39 -0
  31. package/template/.claude/skills/writing-plans/SKILL.md +70 -0
  32. package/template/.codex/agents/cowork-check.toml +17 -22
  33. package/template/.codex/agents/cowork-implement.toml +18 -23
  34. package/template/.codex/agents/cowork-research.toml +14 -19
  35. package/template/.codex/agents/default.toml +27 -0
  36. package/template/.codex/agents/explorer.toml +28 -0
  37. package/template/.codex/agents/worker.toml +29 -0
  38. package/template/.codex/hooks/inject-workflow-state.py +172 -90
  39. package/template/.cowork-flow/.version +1 -1
  40. package/template/.cowork-flow/adapters/claude-code/adapter.yaml +35 -0
  41. package/template/.cowork-flow/adapters/codex/adapter.yaml +35 -0
  42. package/template/.cowork-flow/adapters/opencode/adapter.yaml +33 -0
  43. package/template/.cowork-flow/config.yaml +4 -1
  44. package/template/.cowork-flow/scripts/add_session.py +20 -7
  45. package/template/.cowork-flow/scripts/common/active_task.py +212 -1
  46. package/template/.cowork-flow/scripts/common/entry_classifier.py +122 -0
  47. package/template/.cowork-flow/scripts/common/execution_context.py +10 -17
  48. package/template/.cowork-flow/scripts/common/readiness.py +249 -0
  49. package/template/.cowork-flow/scripts/doctor.py +417 -29
  50. package/template/.cowork-flow/scripts/project_context.py +230 -0
  51. package/template/.cowork-flow/scripts/run.py +3 -0
  52. package/template/.cowork-flow/scripts/subagent.py +152 -238
  53. package/template/.cowork-flow/scripts/task.py +330 -17
  54. package/template/.cowork-flow/spec/adapter.schema.json +132 -0
  55. package/template/.cowork-flow/spec/capabilities.md +34 -0
  56. package/template/.cowork-flow/spec/entry-contract.md +45 -0
  57. package/template/.cowork-flow/spec/registry.json +54 -0
  58. package/template/.cowork-flow/spec/subagent-dispatch.md +77 -0
  59. package/template/.cowork-flow/spec/workflow-state-templates.md +66 -0
  60. package/template/.cowork-flow/workflow.md +154 -190
  61. package/template/.opencode/agents/cowork-check.md +46 -0
  62. package/template/.opencode/agents/cowork-implement.md +45 -0
  63. package/template/.opencode/agents/cowork-research.md +46 -0
  64. package/template/.opencode/commands/cowork-check.md +26 -0
  65. package/template/.opencode/commands/cowork-implement.md +26 -0
  66. package/template/.opencode/commands/cowork-research.md +26 -0
  67. package/template/.opencode/plugins/cowork-flow.js +317 -0
  68. package/template/CLAUDE.md +5 -0
  69. package/template/.agent/skills/entry-boundary/SKILL.md +0 -81
@@ -1,21 +1,31 @@
1
1
  #!/usr/bin/env python3
2
2
  # -*- coding: utf-8 -*-
3
- """Generic subagent scoped recovery state."""
3
+ """Runtime-context scoped subagent state."""
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
7
  import argparse
8
8
  import json
9
9
  import re
10
- import secrets
11
10
  import sys
12
11
  from datetime import datetime, timezone
13
12
  from pathlib import Path
14
13
 
14
+ from common.active_task import (
15
+ bind_runtime_context,
16
+ close_runtime_context,
17
+ read_runtime_context,
18
+ resolve_context_key,
19
+ runtime_context_path,
20
+ sessions_dir,
21
+ subagent_contexts_dir,
22
+ write_runtime_context,
23
+ write_subagent_logical_session,
24
+ )
15
25
  from common.execution_context import build_internal_execution_context_parser
16
- from common.paths import DIR_WORKFLOW, get_repo_root
26
+ from common.paths import get_repo_root
17
27
 
18
- VALID_STATUSES = {"active", "success", "needs_context", "blocked"}
28
+ VALID_STATUSES = {"pending", "bound", "active", "success", "needs_context", "blocked", "closed"}
19
29
  FIXED_AGENT_TYPES = {"cowork-research", "cowork-implement", "cowork-check"}
20
30
  GENERIC_AGENT_TYPE = "worker"
21
31
  ROLE_AGENT_TYPE_ALIASES = {
@@ -34,10 +44,6 @@ def _slug(value: str) -> str:
34
44
  return slug or "subagent"
35
45
 
36
46
 
37
- def _subagents_dir(repo_root: Path) -> Path:
38
- return repo_root / DIR_WORKFLOW / "subagents"
39
-
40
-
41
47
  def _relative(repo_root: Path, path: Path) -> str:
42
48
  try:
43
49
  return path.resolve().relative_to(repo_root.resolve()).as_posix()
@@ -45,28 +51,26 @@ def _relative(repo_root: Path, path: Path) -> str:
45
51
  return str(path)
46
52
 
47
53
 
48
- def _write_json(path: Path, data: object) -> None:
49
- path.parent.mkdir(parents=True, exist_ok=True)
50
- path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
51
-
52
-
53
- def _append_event(path: Path, event: dict[str, object]) -> None:
54
- path.parent.mkdir(parents=True, exist_ok=True)
55
- with path.open("a", encoding="utf-8") as handle:
56
- handle.write(json.dumps(event, ensure_ascii=False) + "\n")
57
-
58
-
59
54
  def _next_id(base_dir: Path, title: str) -> str:
60
- prefix = datetime.now().strftime("%Y%m%d-%H%M%S")
61
- base = f"{prefix}-{_slug(title)}"
55
+ prefix = datetime.now().strftime("rtx_%Y%m%d_%H%M%S")
56
+ base = f"{prefix}_{_slug(title)}"
62
57
  candidate = base
63
58
  counter = 2
64
- while (base_dir / candidate).exists():
65
- candidate = f"{base}-{counter}"
59
+ while (base_dir / f"{candidate}.json").exists():
60
+ candidate = f"{base}_{counter}"
66
61
  counter += 1
67
62
  return candidate
68
63
 
69
64
 
65
+ def _host_context_prefix(host: str) -> str:
66
+ normalized = host.strip().lower()
67
+ if normalized == "claude-code":
68
+ return "claude"
69
+ return re.sub(r"[^a-z0-9]+", "_", normalized).strip("_") or "host"
70
+
71
+ def _suggest_host_context_key(host: str, runtime_context_id: str) -> str:
72
+ return f"{_host_context_prefix(host)}_{runtime_context_id}"
73
+
70
74
  def _resolve_agent_type(role: str, agent_type: str | None) -> tuple[str, str]:
71
75
  normalized_role = role.strip()
72
76
  requested = agent_type.strip() if isinstance(agent_type, str) else ""
@@ -76,245 +80,119 @@ def _resolve_agent_type(role: str, agent_type: str | None) -> tuple[str, str]:
76
80
  raise ValueError(f"agent-type {requested} cannot use role {normalized_role}")
77
81
  if normalized_role in ROLE_AGENT_TYPE_ALIASES and ROLE_AGENT_TYPE_ALIASES[normalized_role] != requested:
78
82
  raise ValueError(f"agent-type {requested} cannot use role {normalized_role}")
79
- return requested, "fixed"
83
+ return requested, "formal"
80
84
  if requested == GENERIC_AGENT_TYPE:
81
85
  if normalized_role in FIXED_AGENT_TYPES or normalized_role in ROLE_AGENT_TYPE_ALIASES:
82
86
  raise ValueError("agent-type worker requires non-fixed role")
83
- return requested, "best-effort"
87
+ return requested, "advisory"
84
88
  raise ValueError("agent-type must be cowork-research, cowork-implement, cowork-check, or worker")
85
89
  if normalized_role in FIXED_AGENT_TYPES:
86
- return normalized_role, "fixed"
90
+ return normalized_role, "formal"
87
91
  if normalized_role in ROLE_AGENT_TYPE_ALIASES:
88
- return ROLE_AGENT_TYPE_ALIASES[normalized_role], "fixed"
89
- return GENERIC_AGENT_TYPE, "best-effort"
90
-
91
-
92
- def _build_dispatch_message(
93
- *,
94
- dispatch_id: str,
95
- agent_type: str,
96
- role: str,
97
- task_dir: str | None,
98
- context_file: Path,
99
- ack_token: str,
100
- dispatch_reliability: str,
101
- ) -> str:
102
- lines = [
103
- "COWORK_DISPATCH_V1",
104
- f"dispatch_id: {dispatch_id}",
105
- f"agent_type: {agent_type}",
106
- ]
107
- if task_dir:
108
- lines.append(f"task_dir: {task_dir}")
109
- lines.extend(
110
- [
111
- f"role: {role}",
112
- f"context_file: {_relative(get_repo_root(), context_file)}",
113
- f"ack_token: {ack_token}",
114
- f"dispatch_reliability: {dispatch_reliability}",
115
- "COWORK_DISPATCH_END",
116
- "",
117
- f"Return only: COWORK_ACK {dispatch_id} {ack_token}",
118
- ]
119
- )
120
- if dispatch_reliability == "best-effort":
121
- lines.insert(7, "This generic worker dispatch is best-effort only.")
122
- return "\n".join(lines)
123
-
124
-
125
- def _build_execute_message(dispatch_id: str, goal: str, dispatch_reliability: str) -> str:
126
- lines = [
127
- f"EXECUTE {dispatch_id}",
128
- "",
129
- goal,
130
- ]
131
- if dispatch_reliability == "best-effort":
132
- lines.extend(
133
- [
134
- "",
135
- "Treat this as best-effort.",
136
- ]
137
- )
138
- return "\n".join(lines)
92
+ return ROLE_AGENT_TYPE_ALIASES[normalized_role], "formal"
93
+ return GENERIC_AGENT_TYPE, "advisory"
139
94
 
140
95
 
141
96
  def cmd_init(args: argparse.Namespace) -> int:
142
97
  repo_root = get_repo_root()
143
- base_dir = _subagents_dir(repo_root)
144
- subagent_id = _next_id(base_dir, args.title)
145
- subagent_dir = base_dir / subagent_id
98
+ base_dir = subagent_contexts_dir(repo_root)
99
+ runtime_context_id = _next_id(base_dir, args.title)
146
100
  task_dir = getattr(args, "execution_task_dir", None)
147
101
  try:
148
- agent_type, dispatch_reliability = _resolve_agent_type(args.role, getattr(args, "agent_type", None))
102
+ agent_type, dispatch_kind = _resolve_agent_type(args.role, getattr(args, "agent_type", None))
149
103
  except ValueError as error:
150
104
  print(f"Error: {error}", file=sys.stderr)
151
105
  return 1
152
- if dispatch_reliability == "fixed" and not task_dir:
106
+ if dispatch_kind == "formal" and not task_dir:
153
107
  print("Error: fixed agent dispatch requires --execution-task-dir", file=sys.stderr)
154
108
  return 1
155
- subagent_dir.mkdir(parents=True, exist_ok=False)
156
- dispatch_id = subagent_id
157
- ack_token = f"ACK:{dispatch_id}:{secrets.token_hex(8)}"
109
+
158
110
  allowed_context = [{"file": item, "reason": "prompt-named context"} for item in args.allowed_context]
159
- status_file = subagent_dir / "status.json"
160
- events_file = subagent_dir / "events.jsonl"
161
- brief_file = subagent_dir / "brief.md"
162
- context_file = subagent_dir / "context.json"
163
- expected_ack = f"COWORK_ACK {dispatch_id} {ack_token}"
164
- dispatch_message = _build_dispatch_message(
165
- dispatch_id=dispatch_id,
166
- agent_type=agent_type,
167
- role=args.role,
168
- task_dir=task_dir,
169
- context_file=context_file,
170
- ack_token=ack_token,
171
- dispatch_reliability=dispatch_reliability,
172
- )
173
- execute_message = _build_execute_message(dispatch_id, args.goal or args.title, dispatch_reliability)
174
-
175
- brief_file.write_text(
176
- "\n".join(
177
- [
178
- dispatch_message,
179
- "",
180
- f"Do not execute the task until the coordinator sends EXECUTE {dispatch_id}.",
181
- "",
182
- f"# {args.title}",
183
- "",
184
- f"Role: {args.role}",
185
- f"Agent type: {agent_type}",
186
- f"Task directory: {task_dir or 'unknown'}",
187
- f"Dispatch reliability: {dispatch_reliability}",
188
- f"Source: {args.source}",
189
- "",
190
- "## Goal",
191
- "",
192
- args.goal or args.title,
193
- "",
194
- "## Scope Rules",
195
- "",
196
- "- Read only prompt-named files and allowed context unless more context is requested.",
197
- "- Do not run main-session start/resume or coordinator mutation commands.",
198
- "- Stop with success, needs_context, or blocked evidence.",
199
- "",
200
- ]
201
- ),
202
- encoding="utf-8",
203
- )
204
- status = {
205
- "id": subagent_id,
206
- "status": "active",
207
- "title": args.title,
208
- "role": args.role,
209
- "agentType": agent_type,
210
- "taskDir": task_dir,
211
- "dispatchReliability": dispatch_reliability,
212
- "source": args.source,
213
- "goal": args.goal or args.title,
214
- "updatedAt": _now(),
215
- "note": "initialized",
216
- "dispatchId": dispatch_id,
217
- "dispatchStatus": "planned",
218
- "ackToken": ack_token,
219
- "expectedAck": expected_ack,
220
- "dispatchMessage": dispatch_message,
221
- "executeMessage": execute_message,
222
- }
223
- _write_json(status_file, status)
111
+ parent_context_key = resolve_context_key()
224
112
  context = {
225
- "mode": "subagent",
226
- "kind": "generic-subagent",
227
- "id": subagent_id,
228
- "agentType": agent_type,
229
- "taskDir": task_dir,
230
- "dispatchReliability": dispatch_reliability,
231
- "dispatchId": dispatch_id,
232
- "ackToken": ack_token,
233
- "expectedAck": expected_ack,
234
- "dispatchMessage": dispatch_message,
235
- "executeMessage": execute_message,
236
- "dispatchStatus": "planned",
237
- "title": args.title,
113
+ "schema_version": 2,
114
+ "runtime_context_id": runtime_context_id,
115
+ "scope": "subagent",
116
+ "host": args.host,
117
+ "adapter": args.adapter,
118
+ "agent_type": agent_type,
238
119
  "role": args.role,
239
- "source": args.source,
240
- "goal": args.goal or args.title,
241
- "allowedContext": allowed_context,
242
- "forbiddenActions": [
243
- "full-start",
244
- "unscoped-resume",
245
- "task-start",
246
- "task-finish",
247
- "task-archive",
248
- "spawn-agent",
249
- ],
250
- "briefFile": _relative(repo_root, brief_file),
251
- "statusFile": _relative(repo_root, status_file),
252
- "eventsFile": _relative(repo_root, events_file),
120
+ "task_dir": task_dir,
121
+ "parent_context_key": parent_context_key,
122
+ "transport": {
123
+ "kind": "prompt",
124
+ "key": "cowork_runtime_context_id",
125
+ },
126
+ "assignment": {
127
+ "title": args.title,
128
+ "goal": args.goal or args.title,
129
+ "allowed_context": allowed_context,
130
+ "expected_output": args.expected_output,
131
+ "source": args.source,
132
+ },
133
+ "authority": {
134
+ "may_start_task": False,
135
+ "may_resume_main": False,
136
+ "may_archive": False,
137
+ "may_commit": False,
138
+ "may_spawn": False,
139
+ },
140
+ "status": "pending",
141
+ "dispatch_kind": dispatch_kind,
142
+ "created_at": _now(),
143
+ "bound_context_key": None,
144
+ "closed_at": None,
253
145
  }
254
- _write_json(context_file, context)
255
- _append_event(events_file, {"time": _now(), "event": "init", "status": "active", "title": args.title})
256
-
257
- index_path = base_dir / "index.json"
258
- if index_path.is_file():
259
- try:
260
- index = json.loads(index_path.read_text(encoding="utf-8"))
261
- except json.JSONDecodeError:
262
- index = {"subagents": []}
263
- else:
264
- index = {"subagents": []}
265
- subagents = index.setdefault("subagents", [])
266
- if isinstance(subagents, list):
267
- subagents.append(
268
- {
269
- "id": subagent_id,
270
- "dispatchId": dispatch_id,
271
- "agentType": agent_type,
272
- "taskDir": task_dir,
273
- "title": args.title,
274
- "contextFile": _relative(repo_root, context_file),
275
- "status": "active",
276
- "dispatchStatus": "planned",
277
- "dispatchReliability": dispatch_reliability,
278
- }
279
- )
280
- _write_json(index_path, index)
146
+ write_runtime_context(repo_root, runtime_context_id, context)
147
+ logical_context_key = write_subagent_logical_session(
148
+ repo_root,
149
+ runtime_context_id,
150
+ task_dir,
151
+ args.host,
152
+ )
153
+ host_context_key = _suggest_host_context_key(args.host, runtime_context_id)
281
154
 
282
155
  print(
283
156
  json.dumps(
284
- {
285
- "id": subagent_id,
286
- "agentType": agent_type,
287
- "taskDir": task_dir,
288
- "dispatchReliability": dispatch_reliability,
289
- "dispatchId": dispatch_id,
290
- "ackToken": ack_token,
291
- "expectedAck": expected_ack,
292
- "dispatchMessage": dispatch_message,
293
- "executeMessage": execute_message,
294
- "contextFile": _relative(repo_root, context_file),
295
- },
296
- ensure_ascii=False,
297
- )
157
+ {
158
+ "id": runtime_context_id,
159
+ "runtimeContextId": runtime_context_id,
160
+ "cowork_runtime_context_id": runtime_context_id,
161
+ "hostContextKey": host_context_key,
162
+ "cowork_host_context_key": host_context_key,
163
+ "agentType": agent_type,
164
+ "role": args.role,
165
+ "taskDir": task_dir,
166
+ "dispatchKind": dispatch_kind,
167
+ "runtimeContextFile": _relative(repo_root, runtime_context_path(repo_root, runtime_context_id)),
168
+ "logicalSessionFile": _relative(repo_root, sessions_dir(repo_root) / f"{logical_context_key}.json"),
169
+ "promptTransport": (
170
+ f"cowork_runtime_context_id: {runtime_context_id}\n"
171
+ f"cowork_host_context_key: {host_context_key}"
172
+ ),
173
+ "bindCommand": f".cowork-flow/run subagent bind {runtime_context_id} {host_context_key}",
174
+ },
175
+ ensure_ascii=False,
298
176
  )
177
+ )
299
178
  return 0
300
179
 
301
180
 
302
- def _find_subagent(repo_root: Path, subagent_id: str) -> Path:
303
- path = _subagents_dir(repo_root) / subagent_id
304
- if not path.is_dir():
305
- raise FileNotFoundError(subagent_id)
306
- return path
181
+ def _find_subagent(repo_root: Path, runtime_context_id: str) -> dict:
182
+ context = read_runtime_context(repo_root, runtime_context_id)
183
+ if not context:
184
+ raise FileNotFoundError(runtime_context_id)
185
+ return context
307
186
 
308
187
 
309
188
  def cmd_status(args: argparse.Namespace) -> int:
310
189
  repo_root = get_repo_root()
311
190
  try:
312
- subagent_dir = _find_subagent(repo_root, args.subagent_id)
191
+ context = _find_subagent(repo_root, args.subagent_id)
313
192
  except FileNotFoundError:
314
193
  print(f"Error: subagent not found: {args.subagent_id}", file=sys.stderr)
315
194
  return 1
316
- status_file = subagent_dir / "status.json"
317
- print(status_file.read_text(encoding="utf-8"), end="")
195
+ print(json.dumps(context, ensure_ascii=False, indent=2) + "\n", end="")
318
196
  return 0
319
197
 
320
198
 
@@ -324,48 +202,84 @@ def cmd_update(args: argparse.Namespace) -> int:
324
202
  return 1
325
203
  repo_root = get_repo_root()
326
204
  try:
327
- subagent_dir = _find_subagent(repo_root, args.subagent_id)
205
+ context = _find_subagent(repo_root, args.subagent_id)
328
206
  except FileNotFoundError:
329
207
  print(f"Error: subagent not found: {args.subagent_id}", file=sys.stderr)
330
208
  return 1
331
- status_file = subagent_dir / "status.json"
332
- status = json.loads(status_file.read_text(encoding="utf-8"))
333
- status["status"] = args.status
334
- status["updatedAt"] = _now()
209
+ context["status"] = args.status
210
+ context["updated_at"] = _now()
335
211
  if args.note:
336
- status["note"] = args.note
337
- _write_json(status_file, status)
338
- _append_event(subagent_dir / "events.jsonl", {"time": _now(), "event": "update", "status": args.status, "note": args.note})
212
+ context["note"] = args.note
213
+ write_runtime_context(repo_root, args.subagent_id, context)
339
214
  print(f"subagent {args.subagent_id} status={args.status}")
340
215
  return 0
341
216
 
342
217
 
218
+ def cmd_bind(args: argparse.Namespace) -> int:
219
+ repo_root = get_repo_root()
220
+ existing = read_runtime_context(repo_root, args.subagent_id)
221
+ existing_key = existing.get("bound_context_key") if existing else None
222
+ if isinstance(existing_key, str) and existing_key.strip() and existing_key != args.context_key:
223
+ print(
224
+ f"Error: runtime context {args.subagent_id} already bound to {existing_key}",
225
+ file=sys.stderr,
226
+ )
227
+ return 1
228
+ context = bind_runtime_context(repo_root, args.subagent_id, args.context_key)
229
+ if context is None:
230
+ print(f"Error: cannot bind runtime context: {args.subagent_id}", file=sys.stderr)
231
+ return 1
232
+ print(json.dumps(context, ensure_ascii=False, indent=2) + "\n", end="")
233
+ return 0
234
+
235
+
236
+ def cmd_close(args: argparse.Namespace) -> int:
237
+ repo_root = get_repo_root()
238
+ if not close_runtime_context(repo_root, args.subagent_id):
239
+ print(f"Error: subagent not found: {args.subagent_id}", file=sys.stderr)
240
+ return 1
241
+ print(f"subagent {args.subagent_id} closed")
242
+ return 0
243
+
244
+
343
245
  def build_parser() -> argparse.ArgumentParser:
344
246
  parser = argparse.ArgumentParser(
345
- description="Generic subagent scoped recovery",
247
+ description="Runtime-context scoped subagent state",
346
248
  parents=[build_internal_execution_context_parser()],
347
249
  )
348
250
  subparsers = parser.add_subparsers(dest="command", required=True)
349
251
 
350
- init = subparsers.add_parser("init", help="Create generic subagent context")
252
+ init = subparsers.add_parser("init", help="Create runtime subagent context")
351
253
  init.add_argument("--title", required=True)
352
254
  init.add_argument("--role", default="subagent")
353
255
  init.add_argument("--agent-type")
354
256
  init.add_argument("--source", default="auto")
355
257
  init.add_argument("--goal")
258
+ init.add_argument("--expected-output", default="Files changed, validation commands, and blockers.")
356
259
  init.add_argument("--allowed-context", action="append", default=[])
260
+ init.add_argument("--host", default="codex")
261
+ init.add_argument("--adapter", default="codex.spawn_agent")
357
262
  init.set_defaults(func=cmd_init)
358
263
 
359
- status = subparsers.add_parser("status", help="Print subagent status")
264
+ status = subparsers.add_parser("status", help="Print subagent runtime context")
360
265
  status.add_argument("subagent_id")
361
266
  status.set_defaults(func=cmd_status)
362
267
 
363
- update = subparsers.add_parser("update", help="Update subagent status")
268
+ update = subparsers.add_parser("update", help="Update subagent runtime status")
364
269
  update.add_argument("subagent_id")
365
270
  update.add_argument("--status", required=True)
366
271
  update.add_argument("--note")
367
272
  update.set_defaults(func=cmd_update)
368
273
 
274
+ bind = subparsers.add_parser("bind", help="Bind host session to runtime context")
275
+ bind.add_argument("subagent_id")
276
+ bind.add_argument("context_key")
277
+ bind.set_defaults(func=cmd_bind)
278
+
279
+ close = subparsers.add_parser("close", help="Close subagent runtime context")
280
+ close.add_argument("subagent_id")
281
+ close.set_defaults(func=cmd_close)
282
+
369
283
  return parser
370
284
 
371
285