cowork-flow 0.0.3 → 0.0.5
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 +39 -10
- package/package.json +6 -4
- package/src/lib/copy-template.js +15 -11
- package/src/lib/package-info.js +8 -2
- package/template/.agent/skills/agent-team-execution/SKILL.md +26 -0
- package/template/{.superpowers → .agent/skills}/brainstorming/SKILL.md +1 -1
- package/template/{.superpowers → .agent/skills}/brainstorming/scripts/start-server.sh +0 -0
- package/template/{.superpowers → .agent/skills}/brainstorming/scripts/stop-server.sh +0 -0
- package/template/.agent/skills/record-session/SKILL.md +8 -8
- package/template/.agent/skills/start/SKILL.md +29 -24
- package/template/.cowork-flow/.version +1 -1
- package/template/.cowork-flow/agent-team/adapters.yaml +15 -0
- package/template/.cowork-flow/agent-team/agents.yaml +68 -0
- package/template/.cowork-flow/agent-team/policy.yaml +23 -0
- package/template/.cowork-flow/config.yaml +9 -0
- package/template/.cowork-flow/run +166 -0
- package/template/.cowork-flow/run.cmd +198 -0
- package/template/.cowork-flow/scripts/add_session.py +2 -2
- package/template/.cowork-flow/scripts/agent_team.py +508 -0
- package/template/.cowork-flow/scripts/change.py +5 -11
- package/template/.cowork-flow/scripts/common/agent_team.py +241 -0
- package/template/.cowork-flow/scripts/common/config.py +18 -0
- package/template/.cowork-flow/scripts/common/developer.py +1 -1
- package/template/.cowork-flow/scripts/common/git_context.py +21 -4
- package/template/.cowork-flow/scripts/get_context.py +2 -2
- package/template/.cowork-flow/scripts/init_developer.py +1 -1
- package/template/.cowork-flow/scripts/task.py +45 -45
- package/template/.cowork-flow/workflow.md +61 -175
- package/template/.cowork-flow/workspace/index.md +3 -3
- package/template/.superpowers/executing-plans/SKILL.md +2 -0
- package/template/AGENTS.md +6 -3
- /package/template/{.superpowers → .agent/skills}/brainstorming/scripts/frame-template.html +0 -0
- /package/template/{.superpowers → .agent/skills}/brainstorming/scripts/helper.js +0 -0
- /package/template/{.superpowers → .agent/skills}/brainstorming/scripts/server.cjs +0 -0
- /package/template/{.superpowers → .agent/skills}/brainstorming/spec-document-reviewer-prompt.md +0 -0
- /package/template/{.superpowers → .agent/skills}/brainstorming/visual-companion.md +0 -0
- /package/template/{.superpowers → .agent/skills}/requesting-code-review/SKILL.md +0 -0
- /package/template/{.superpowers → .agent/skills}/requesting-code-review/code-reviewer.md +0 -0
- /package/template/{.superpowers → .agent/skills}/subagent-driven-development/SKILL.md +0 -0
- /package/template/{.superpowers → .agent/skills}/subagent-driven-development/code-quality-reviewer-prompt.md +0 -0
- /package/template/{.superpowers → .agent/skills}/subagent-driven-development/implementer-prompt.md +0 -0
- /package/template/{.superpowers → .agent/skills}/subagent-driven-development/spec-reviewer-prompt.md +0 -0
- /package/template/{.superpowers → .agent/skills}/writing-plans/SKILL.md +0 -0
- /package/template/{.superpowers → .agent/skills}/writing-plans/plan-document-reviewer-prompt.md +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""Helpers for the cowork-flow agent team runtime."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
TASK_RE = re.compile(r"^### Task\s+(\d+):\s*(.+?)\s*$")
|
|
11
|
+
FILE_RE = re.compile(r"^-\s+(Create|Modify|Test):\s+`([^`]+)`")
|
|
12
|
+
DEP_RE = re.compile(r"depends on Task\s+(\d+)", re.IGNORECASE)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def parse_plan(text: str) -> list[dict[str, object]]:
|
|
16
|
+
tasks: list[dict[str, object]] = []
|
|
17
|
+
current: dict[str, object] | None = None
|
|
18
|
+
|
|
19
|
+
for raw_line in text.splitlines():
|
|
20
|
+
line = raw_line.rstrip()
|
|
21
|
+
task_match = TASK_RE.match(line)
|
|
22
|
+
if task_match:
|
|
23
|
+
current = {
|
|
24
|
+
"number": int(task_match.group(1)),
|
|
25
|
+
"id": f"T{int(task_match.group(1)):03d}",
|
|
26
|
+
"title": task_match.group(2),
|
|
27
|
+
"files": [],
|
|
28
|
+
"steps": [],
|
|
29
|
+
"commands": [],
|
|
30
|
+
"explicit_dependencies": [],
|
|
31
|
+
}
|
|
32
|
+
tasks.append(current)
|
|
33
|
+
continue
|
|
34
|
+
|
|
35
|
+
if current is None:
|
|
36
|
+
continue
|
|
37
|
+
|
|
38
|
+
file_match = FILE_RE.match(line.strip())
|
|
39
|
+
if file_match:
|
|
40
|
+
current["files"].append(
|
|
41
|
+
{
|
|
42
|
+
"kind": file_match.group(1).lower(),
|
|
43
|
+
"path": file_match.group(2),
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
continue
|
|
47
|
+
|
|
48
|
+
stripped = line.strip()
|
|
49
|
+
if stripped.startswith("- [ ]"):
|
|
50
|
+
current["steps"].append(stripped)
|
|
51
|
+
continue
|
|
52
|
+
|
|
53
|
+
if stripped.startswith("Run:"):
|
|
54
|
+
current["commands"].append(stripped.removeprefix("Run:").strip())
|
|
55
|
+
|
|
56
|
+
dep_match = DEP_RE.search(stripped)
|
|
57
|
+
if dep_match:
|
|
58
|
+
current["explicit_dependencies"].append(f"T{int(dep_match.group(1)):03d}")
|
|
59
|
+
|
|
60
|
+
return tasks
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _task_files(task: dict[str, object]) -> set[str]:
|
|
64
|
+
files = task.get("files", [])
|
|
65
|
+
if not isinstance(files, list):
|
|
66
|
+
return set()
|
|
67
|
+
paths = set()
|
|
68
|
+
for item in files:
|
|
69
|
+
if isinstance(item, dict) and isinstance(item.get("path"), str):
|
|
70
|
+
paths.add(item["path"])
|
|
71
|
+
return paths
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _task_dependencies(tasks: list[dict[str, object]]) -> list[dict[str, str]]:
|
|
75
|
+
dependencies: list[dict[str, str]] = []
|
|
76
|
+
for index, task in enumerate(tasks):
|
|
77
|
+
task_id = str(task["id"])
|
|
78
|
+
task_files = _task_files(task)
|
|
79
|
+
|
|
80
|
+
explicit = task.get("explicit_dependencies", [])
|
|
81
|
+
if isinstance(explicit, list):
|
|
82
|
+
for dependency in explicit:
|
|
83
|
+
dependencies.append(
|
|
84
|
+
{
|
|
85
|
+
"task": task_id,
|
|
86
|
+
"depends_on_task": str(dependency),
|
|
87
|
+
"reason": "explicit",
|
|
88
|
+
}
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
for previous in tasks[:index]:
|
|
92
|
+
previous_files = _task_files(previous)
|
|
93
|
+
if task_files.intersection(previous_files):
|
|
94
|
+
dependencies.append(
|
|
95
|
+
{
|
|
96
|
+
"task": task_id,
|
|
97
|
+
"depends_on_task": str(previous["id"]),
|
|
98
|
+
"reason": "file-overlap",
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
return dependencies
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _role_agent(role: str) -> tuple[str, str]:
|
|
106
|
+
if role == "implementer":
|
|
107
|
+
return ("implementer", "worker")
|
|
108
|
+
if role == "spec-reviewer":
|
|
109
|
+
return ("spec-reviewer", "default")
|
|
110
|
+
return ("quality-reviewer", "default")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def build_dispatch_plan(tasks: list[dict[str, object]]) -> dict[str, object]:
|
|
114
|
+
assignments: list[dict[str, object]] = []
|
|
115
|
+
task_dependencies = _task_dependencies(tasks)
|
|
116
|
+
dependency_lookup: dict[str, list[str]] = {}
|
|
117
|
+
for dependency in task_dependencies:
|
|
118
|
+
dependency_lookup.setdefault(dependency["task"], []).append(dependency["depends_on_task"])
|
|
119
|
+
|
|
120
|
+
for task in tasks:
|
|
121
|
+
task_id = str(task["id"])
|
|
122
|
+
task_dependency_assignments = [
|
|
123
|
+
f"{dependency}-quality-reviewer"
|
|
124
|
+
for dependency in dependency_lookup.get(task_id, [])
|
|
125
|
+
]
|
|
126
|
+
chain = [
|
|
127
|
+
("implementer", task_dependency_assignments),
|
|
128
|
+
("spec-reviewer", [f"{task_id}-implementer"]),
|
|
129
|
+
("quality-reviewer", [f"{task_id}-spec-reviewer"]),
|
|
130
|
+
]
|
|
131
|
+
for role, depends_on in chain:
|
|
132
|
+
agent, codex_type = _role_agent(role)
|
|
133
|
+
assignments.append(
|
|
134
|
+
{
|
|
135
|
+
"id": f"{task_id}-{role}",
|
|
136
|
+
"task": task_id,
|
|
137
|
+
"title": task["title"],
|
|
138
|
+
"role": role,
|
|
139
|
+
"recommended_agent": agent,
|
|
140
|
+
"codex_type": codex_type,
|
|
141
|
+
"depends_on": depends_on,
|
|
142
|
+
"files": task.get("files", []),
|
|
143
|
+
"steps": task.get("steps", []),
|
|
144
|
+
"commands": task.get("commands", []),
|
|
145
|
+
}
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
"version": 1,
|
|
150
|
+
"adapter": "codex",
|
|
151
|
+
"tasks": tasks,
|
|
152
|
+
"task_dependencies": task_dependencies,
|
|
153
|
+
"assignments": assignments,
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def render_dispatch_plan(dispatch_plan: dict[str, object]) -> str:
|
|
158
|
+
lines = [
|
|
159
|
+
"version: 1",
|
|
160
|
+
f"adapter: {dispatch_plan['adapter']}",
|
|
161
|
+
"task_dependencies:",
|
|
162
|
+
]
|
|
163
|
+
for dependency in dispatch_plan["task_dependencies"]:
|
|
164
|
+
lines.extend(
|
|
165
|
+
[
|
|
166
|
+
f" - task: {dependency['task']}",
|
|
167
|
+
f" depends_on_task: {dependency['depends_on_task']}",
|
|
168
|
+
f" reason: {dependency['reason']}",
|
|
169
|
+
]
|
|
170
|
+
)
|
|
171
|
+
lines.append("assignments:")
|
|
172
|
+
for assignment in dispatch_plan["assignments"]:
|
|
173
|
+
lines.extend(
|
|
174
|
+
[
|
|
175
|
+
f" - id: {assignment['id']}",
|
|
176
|
+
f" task: {assignment['task']}",
|
|
177
|
+
f" role: {assignment['role']}",
|
|
178
|
+
f" recommended_agent: {assignment['recommended_agent']}",
|
|
179
|
+
f" codex_type: {assignment['codex_type']}",
|
|
180
|
+
" depends_on:",
|
|
181
|
+
]
|
|
182
|
+
)
|
|
183
|
+
depends_on = assignment.get("depends_on", [])
|
|
184
|
+
if depends_on:
|
|
185
|
+
for dependency in depends_on:
|
|
186
|
+
lines.append(f" - {dependency}")
|
|
187
|
+
else:
|
|
188
|
+
lines.append(" - none")
|
|
189
|
+
lines.append("")
|
|
190
|
+
return "\n".join(lines)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def write_json(path: Path, data: dict[str, object]) -> None:
|
|
194
|
+
path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def build_initial_status(dispatch_plan: dict[str, object]) -> dict[str, object]:
|
|
198
|
+
assignments: dict[str, dict[str, object]] = {}
|
|
199
|
+
for assignment in dispatch_plan["assignments"]:
|
|
200
|
+
depends_on = [
|
|
201
|
+
dependency
|
|
202
|
+
for dependency in assignment.get("depends_on", [])
|
|
203
|
+
if dependency != "none"
|
|
204
|
+
]
|
|
205
|
+
assignments[assignment["id"]] = {
|
|
206
|
+
"status": "ready" if not depends_on else "pending",
|
|
207
|
+
"attempts": 0,
|
|
208
|
+
"depends_on": depends_on,
|
|
209
|
+
"role": assignment["role"],
|
|
210
|
+
"task": assignment["task"],
|
|
211
|
+
"recommended_agent": assignment["recommended_agent"],
|
|
212
|
+
"codex_type": assignment["codex_type"],
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return {"version": 1, "current_batch": 1, "assignments": assignments}
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def build_initial_metrics(dispatch_plan: dict[str, object]) -> dict[str, object]:
|
|
219
|
+
return {
|
|
220
|
+
"assignments": len(dispatch_plan["assignments"]),
|
|
221
|
+
"attempts": 0,
|
|
222
|
+
"successfulAssignments": 0,
|
|
223
|
+
"failedAssignments": 0,
|
|
224
|
+
"reviewReworks": 0,
|
|
225
|
+
"agents": {},
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def render_assignment_prompt(assignment: dict[str, object]) -> str:
|
|
230
|
+
return "\n".join(
|
|
231
|
+
[
|
|
232
|
+
f"# {assignment['id']}",
|
|
233
|
+
"",
|
|
234
|
+
f"Role: {assignment['role']}",
|
|
235
|
+
f"Recommended agent: {assignment['recommended_agent']}",
|
|
236
|
+
f"Codex type: {assignment['codex_type']}",
|
|
237
|
+
"",
|
|
238
|
+
"You are not alone in this codebase. Respect the write boundary, do not revert other agents' edits, and report changed files.",
|
|
239
|
+
"",
|
|
240
|
+
]
|
|
241
|
+
)
|
|
@@ -15,6 +15,7 @@ from .paths import DIR_WORKFLOW, get_repo_root
|
|
|
15
15
|
# Defaults
|
|
16
16
|
DEFAULT_SESSION_COMMIT_MESSAGE = "chore: record journal"
|
|
17
17
|
DEFAULT_MAX_JOURNAL_LINES = 2000
|
|
18
|
+
DEFAULT_AGENT_TEAM_ENABLED = False
|
|
18
19
|
|
|
19
20
|
CONFIG_FILE = "config.yaml"
|
|
20
21
|
|
|
@@ -108,6 +109,23 @@ def get_max_journal_lines(repo_root: Path | None = None) -> int:
|
|
|
108
109
|
return DEFAULT_MAX_JOURNAL_LINES
|
|
109
110
|
|
|
110
111
|
|
|
112
|
+
def _is_true(value: object) -> bool:
|
|
113
|
+
if value is True:
|
|
114
|
+
return True
|
|
115
|
+
if isinstance(value, str):
|
|
116
|
+
return value.strip().lower() == "true"
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def get_agent_team_enabled(repo_root: Path | None = None) -> bool:
|
|
121
|
+
"""Return whether agent-team runtime commands are enabled."""
|
|
122
|
+
config = _load_config(repo_root)
|
|
123
|
+
agent_team = config.get("agent_team")
|
|
124
|
+
if not isinstance(agent_team, dict):
|
|
125
|
+
return DEFAULT_AGENT_TEAM_ENABLED
|
|
126
|
+
return _is_true(agent_team.get("enabled"))
|
|
127
|
+
|
|
128
|
+
|
|
111
129
|
def get_hooks(event: str, repo_root: Path | None = None) -> list[str]:
|
|
112
130
|
"""Get hook commands for a lifecycle event.
|
|
113
131
|
|
|
@@ -174,7 +174,7 @@ def ensure_developer(repo_root: Path | None = None) -> None:
|
|
|
174
174
|
|
|
175
175
|
if not check_developer(repo_root):
|
|
176
176
|
print("Error: developer identity has not been initialized.", file=sys.stderr)
|
|
177
|
-
print(f"Run:
|
|
177
|
+
print(f"Run: ./{DIR_WORKFLOW}/run init-developer <your-name>", file=sys.stderr)
|
|
178
178
|
sys.exit(1)
|
|
179
179
|
|
|
180
180
|
|
|
@@ -253,12 +253,19 @@ def _task_plan_references(repo_root: Path, snapshot: CurrentTaskSnapshot) -> lis
|
|
|
253
253
|
return plan_refs
|
|
254
254
|
|
|
255
255
|
|
|
256
|
+
def _task_has_agent_team_status(repo_root: Path, snapshot: CurrentTaskSnapshot) -> bool:
|
|
257
|
+
"""Return whether the current task has persisted agent-team runtime state."""
|
|
258
|
+
if not snapshot.path:
|
|
259
|
+
return False
|
|
260
|
+
return (repo_root / snapshot.path / "agent-team" / "status.json").is_file()
|
|
261
|
+
|
|
262
|
+
|
|
256
263
|
def _build_resume_checklist(
|
|
257
264
|
repo_root: Path,
|
|
258
265
|
snapshot: CurrentTaskSnapshot,
|
|
259
266
|
) -> dict[str, list[str]]:
|
|
260
267
|
"""构建最小恢复清单,只返回路径和命令,不展开文件内容。"""
|
|
261
|
-
commands = [f"
|
|
268
|
+
commands = [f"./{DIR_WORKFLOW}/run resume"]
|
|
262
269
|
read_files: list[str] = []
|
|
263
270
|
notes: list[str] = []
|
|
264
271
|
|
|
@@ -268,7 +275,10 @@ def _build_resume_checklist(
|
|
|
268
275
|
return {"commands": commands, "readFiles": read_files, "notes": notes}
|
|
269
276
|
|
|
270
277
|
current_task = snapshot.path
|
|
271
|
-
commands.append(f"
|
|
278
|
+
commands.append(f"./{DIR_WORKFLOW}/run task list-context {current_task}")
|
|
279
|
+
if _task_has_agent_team_status(repo_root, snapshot):
|
|
280
|
+
commands.append(f"./{DIR_WORKFLOW}/run agent-team status {current_task}")
|
|
281
|
+
commands.append(f"./{DIR_WORKFLOW}/run agent-team next {current_task}")
|
|
272
282
|
|
|
273
283
|
if snapshot.has_prd:
|
|
274
284
|
read_files.append(f"{current_task}/prd.md")
|
|
@@ -329,6 +339,13 @@ def _append_resume_checklist(
|
|
|
329
339
|
else:
|
|
330
340
|
lines.append("- No plan reference found in task context; do not search all plans unless needed.")
|
|
331
341
|
|
|
342
|
+
agent_team_commands = [
|
|
343
|
+
command for command in commands if f"./{DIR_WORKFLOW}/run agent-team " in command
|
|
344
|
+
]
|
|
345
|
+
if agent_team_commands:
|
|
346
|
+
lines.append(f"- Check agent-team status: {agent_team_commands[0]}")
|
|
347
|
+
lines.append(f"- Continue agent-team dispatch: {agent_team_commands[1]}")
|
|
348
|
+
|
|
332
349
|
lines.append("- Do not bulk-read .cowork-flow/spec/ or workspace journals; follow JSONL references on demand.")
|
|
333
350
|
lines.append("")
|
|
334
351
|
|
|
@@ -503,7 +520,7 @@ def get_context_text(repo_root: Path | None = None) -> str:
|
|
|
503
520
|
lines.append("## DEVELOPER")
|
|
504
521
|
if not developer:
|
|
505
522
|
lines.append(
|
|
506
|
-
f"ERROR: Not initialized. Run:
|
|
523
|
+
f"ERROR: Not initialized. Run: ./{DIR_WORKFLOW}/run init-developer <name>"
|
|
507
524
|
)
|
|
508
525
|
return "\n".join(lines)
|
|
509
526
|
|
|
@@ -670,7 +687,7 @@ def get_context_text_record(repo_root: Path | None = None) -> str:
|
|
|
670
687
|
developer = get_developer(repo_root)
|
|
671
688
|
if not developer:
|
|
672
689
|
lines.append(
|
|
673
|
-
f"ERROR: Not initialized. Run:
|
|
690
|
+
f"ERROR: Not initialized. Run: ./{DIR_WORKFLOW}/run init-developer <name>"
|
|
674
691
|
)
|
|
675
692
|
return "\n".join(lines)
|
|
676
693
|
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
Get Session Context for AI Agent.
|
|
4
4
|
|
|
5
5
|
Usage:
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
./.cowork-flow/run get-context Output context in text format
|
|
7
|
+
./.cowork-flow/run get-context --json Output context in JSON format
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
@@ -4,18 +4,18 @@
|
|
|
4
4
|
Task Management Script for cowork-flow workflow.
|
|
5
5
|
|
|
6
6
|
Usage:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
7
|
+
./.cowork-flow/run task create "<title>" [--slug <name>] [--assignee <dev>] [--priority P0|P1|P2|P3] [--parent <dir>]
|
|
8
|
+
./.cowork-flow/run task init-context <dir> <type> # Initialize jsonl files
|
|
9
|
+
./.cowork-flow/run task add-context <dir> <file> <path> [reason] # Add jsonl entry
|
|
10
|
+
./.cowork-flow/run task validate <dir> # Validate jsonl files
|
|
11
|
+
./.cowork-flow/run task list-context <dir> # List jsonl entries
|
|
12
|
+
./.cowork-flow/run task start <dir> # Set as current task
|
|
13
|
+
./.cowork-flow/run task finish # Clear current task
|
|
14
|
+
./.cowork-flow/run task archive <task-name> # Archive completed task
|
|
15
|
+
./.cowork-flow/run task list # List active tasks
|
|
16
|
+
./.cowork-flow/run task list-archive [month] # List archived tasks
|
|
17
|
+
./.cowork-flow/run task add-subtask <parent-dir> <child-dir> # Link child to parent
|
|
18
|
+
./.cowork-flow/run task remove-subtask <parent-dir> <child-dir> # Unlink child from parent
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
21
|
from __future__ import annotations
|
|
@@ -630,8 +630,8 @@ def cmd_create(args: argparse.Namespace) -> int:
|
|
|
630
630
|
print("", file=sys.stderr)
|
|
631
631
|
print(colored("Next steps:", Colors.BLUE), file=sys.stderr)
|
|
632
632
|
print(" 1. Create prd.md with requirements", file=sys.stderr)
|
|
633
|
-
print(" 2. Run:
|
|
634
|
-
print(" 3. Run:
|
|
633
|
+
print(" 2. Run: ./.cowork-flow/run task init-context <dir> <dev_type>", file=sys.stderr)
|
|
634
|
+
print(" 3. Run: ./.cowork-flow/run task start <dir>", file=sys.stderr)
|
|
635
635
|
print("", file=sys.stderr)
|
|
636
636
|
|
|
637
637
|
# Output relative path for script chaining
|
|
@@ -653,7 +653,7 @@ def cmd_init_context(args: argparse.Namespace) -> int:
|
|
|
653
653
|
|
|
654
654
|
if not dev_type:
|
|
655
655
|
print(colored("Error: Missing arguments", Colors.RED))
|
|
656
|
-
print("Usage:
|
|
656
|
+
print("Usage: ./.cowork-flow/run task init-context <task-dir> <dev_type>")
|
|
657
657
|
print(" dev_type: backend | frontend | fullstack | test | docs")
|
|
658
658
|
return 1
|
|
659
659
|
|
|
@@ -699,8 +699,8 @@ def cmd_init_context(args: argparse.Namespace) -> int:
|
|
|
699
699
|
print(colored("[OK] All context files created", Colors.GREEN))
|
|
700
700
|
print()
|
|
701
701
|
print(colored("Next steps:", Colors.BLUE))
|
|
702
|
-
print(" 1. Add task-specific specs:
|
|
703
|
-
print(" 2. Set as current:
|
|
702
|
+
print(" 1. Add task-specific specs: ./.cowork-flow/run task add-context <dir> <jsonl> <path>")
|
|
703
|
+
print(" 2. Set as current: ./.cowork-flow/run task start <dir>")
|
|
704
704
|
|
|
705
705
|
return 0
|
|
706
706
|
|
|
@@ -914,7 +914,7 @@ def cmd_start(args: argparse.Namespace) -> int:
|
|
|
914
914
|
for blocker in blockers:
|
|
915
915
|
print(f" - {blocker}", file=sys.stderr)
|
|
916
916
|
print(
|
|
917
|
-
"Hint: write prd.md, run
|
|
917
|
+
"Hint: write prd.md, run ./.cowork-flow/run task init-context <dir> <dev_type>, then retry",
|
|
918
918
|
file=sys.stderr,
|
|
919
919
|
)
|
|
920
920
|
return 1
|
|
@@ -925,14 +925,14 @@ def cmd_start(args: argparse.Namespace) -> int:
|
|
|
925
925
|
for issue in validation_issues:
|
|
926
926
|
print(f" - {issue}", file=sys.stderr)
|
|
927
927
|
print(
|
|
928
|
-
"Hint: run
|
|
928
|
+
"Hint: run ./.cowork-flow/run task validate <dir> and fix the reported issues",
|
|
929
929
|
file=sys.stderr,
|
|
930
930
|
)
|
|
931
931
|
return 1
|
|
932
932
|
|
|
933
933
|
# Convert to relative path for storage
|
|
934
934
|
try:
|
|
935
|
-
task_dir =
|
|
935
|
+
task_dir = full_path.relative_to(repo_root).as_posix()
|
|
936
936
|
except ValueError:
|
|
937
937
|
task_dir = str(full_path)
|
|
938
938
|
|
|
@@ -1311,19 +1311,19 @@ def show_usage() -> None:
|
|
|
1311
1311
|
print("""Task Management Script for cowork-flow workflow
|
|
1312
1312
|
|
|
1313
1313
|
Usage:
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1314
|
+
./.cowork-flow/run task create <title> Create new task directory
|
|
1315
|
+
./.cowork-flow/run task create <title> --parent <dir> Create task as child of parent
|
|
1316
|
+
./.cowork-flow/run task init-context <dir> <dev_type> Initialize jsonl files
|
|
1317
|
+
./.cowork-flow/run task add-context <dir> <jsonl> <path> [reason] Add entry to jsonl
|
|
1318
|
+
./.cowork-flow/run task validate <dir> Validate jsonl files
|
|
1319
|
+
./.cowork-flow/run task list-context <dir> List jsonl entries
|
|
1320
|
+
./.cowork-flow/run task start <dir> Set as current task
|
|
1321
|
+
./.cowork-flow/run task finish Clear current task
|
|
1322
|
+
./.cowork-flow/run task archive <task-name> Archive completed task
|
|
1323
|
+
./.cowork-flow/run task add-subtask <parent> <child> Link child task to parent
|
|
1324
|
+
./.cowork-flow/run task remove-subtask <parent> <child> Unlink child from parent
|
|
1325
|
+
./.cowork-flow/run task list [--mine] [--status <status>] List tasks
|
|
1326
|
+
./.cowork-flow/run task list-archive [YYYY-MM] List archived tasks
|
|
1327
1327
|
|
|
1328
1328
|
Arguments:
|
|
1329
1329
|
dev_type: backend | frontend | fullstack | test | docs
|
|
@@ -1333,18 +1333,18 @@ List options:
|
|
|
1333
1333
|
--status, -s <s> Filter by status (planning, in_progress, review, completed)
|
|
1334
1334
|
|
|
1335
1335
|
Examples:
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1336
|
+
./.cowork-flow/run task create "Add login feature" --slug add-login
|
|
1337
|
+
./.cowork-flow/run task create "Child task" --slug child --parent .cowork-flow/tasks/01-21-parent
|
|
1338
|
+
./.cowork-flow/run task init-context .cowork-flow/tasks/01-21-add-login backend
|
|
1339
|
+
./.cowork-flow/run task add-context <dir> implement .cowork-flow/spec/backend/auth.md "Auth guidelines"
|
|
1340
|
+
./.cowork-flow/run task start .cowork-flow/tasks/01-21-add-login
|
|
1341
|
+
./.cowork-flow/run task finish
|
|
1342
|
+
./.cowork-flow/run task archive add-login
|
|
1343
|
+
./.cowork-flow/run task add-subtask parent-task child-task # Link existing tasks
|
|
1344
|
+
./.cowork-flow/run task remove-subtask parent-task child-task
|
|
1345
|
+
./.cowork-flow/run task list # List all active tasks
|
|
1346
|
+
./.cowork-flow/run task list --mine # List my tasks only
|
|
1347
|
+
./.cowork-flow/run task list --mine --status in_progress # List my in-progress tasks
|
|
1348
1348
|
""")
|
|
1349
1349
|
|
|
1350
1350
|
|