cowork-flow 0.0.28 → 0.0.29
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 +40 -12
- package/package.json +1 -1
- package/src/commands/init.js +1 -1
- package/src/commands/update.js +2 -4
- package/src/lib/copy-template.js +50 -3
- package/src/lib/platforms.js +0 -3
- package/template/.claude/agents/cowork-check.md +3 -3
- package/template/.claude/agents/cowork-implement.md +3 -3
- package/template/.claude/agents/cowork-research.md +3 -3
- package/template/.claude/hooks/inject-workflow-state.py +2 -11
- package/template/.codex/hooks/inject-workflow-state.py +3 -12
- package/template/.cowork-flow/.version +1 -1
- package/template/.cowork-flow/adapters/claude-code/adapter.yaml +0 -1
- package/template/.cowork-flow/adapters/codex/adapter.yaml +0 -1
- package/template/.cowork-flow/adapters/opencode/adapter.yaml +0 -1
- package/template/.cowork-flow/scripts/commands/__init__.py +1 -0
- package/template/.cowork-flow/scripts/commands/_bootstrap.py +10 -0
- package/template/.cowork-flow/scripts/{add_session.py → commands/add_session.py} +8 -10
- package/template/.cowork-flow/scripts/{change.py → commands/change.py} +6 -2
- package/template/.cowork-flow/scripts/{doctor.py → commands/doctor.py} +11 -42
- package/template/.cowork-flow/scripts/{get_context.py → commands/get_context.py} +5 -1
- package/template/.cowork-flow/scripts/{get_developer.py → commands/get_developer.py} +6 -2
- package/template/.cowork-flow/scripts/{init_developer.py → commands/init_developer.py} +6 -2
- package/template/.cowork-flow/scripts/{party_mode_v2.py → commands/party_mode_v2.py} +6 -2
- package/template/.cowork-flow/scripts/{resume.py → commands/resume.py} +6 -2
- package/template/.cowork-flow/scripts/{subagent.py → commands/subagent.py} +7 -3
- package/template/.cowork-flow/scripts/{task.py → commands/task.py} +34 -29
- package/template/.cowork-flow/scripts/common/__init__.py +1 -1
- package/template/.cowork-flow/scripts/common/core/__init__.py +1 -0
- package/template/.cowork-flow/scripts/common/{developer.py → core/developer.py} +1 -1
- package/template/.cowork-flow/scripts/common/gates/__init__.py +1 -0
- package/template/.cowork-flow/scripts/common/{coding_standards.py → gates/coding_standards.py} +3 -2
- package/template/.cowork-flow/scripts/common/{tdd_evidence.py → gates/tdd_evidence.py} +75 -11
- package/template/.cowork-flow/scripts/common/{test_intent.py → gates/test_intent.py} +2 -1
- package/template/.cowork-flow/scripts/common/{validate_coding_standards.py → gates/validate_coding_standards.py} +1 -1
- package/template/.cowork-flow/scripts/common/{validate_rules.py → gates/validate_rules.py} +1 -1
- package/template/.cowork-flow/scripts/common/git/__init__.py +1 -0
- package/template/.cowork-flow/scripts/common/{git_context.py → git/git_context.py} +3 -3
- package/template/.cowork-flow/scripts/common/task/__init__.py +1 -0
- package/template/.cowork-flow/scripts/common/{active_task.py → task/active_task.py} +1 -1
- package/template/.cowork-flow/scripts/common/{readiness.py → task/readiness.py} +1 -1
- package/template/.cowork-flow/scripts/common/{task_utils.py → task/task_utils.py} +2 -2
- package/template/.cowork-flow/scripts/run.py +32 -25
- package/template/.cowork-flow/spec/contracts/index.md +0 -1
- package/template/.cowork-flow/spec/runtime/contract-registry.json +0 -12
- package/template/.cowork-flow/spec/schemas/adapter.schema.json +1 -5
- package/template/.cowork-flow/workflow.md +7 -7
- package/template/.opencode/agents/cowork-check.md +3 -3
- package/template/.opencode/agents/cowork-implement.md +3 -3
- package/template/.opencode/agents/cowork-research.md +3 -3
- package/template/.opencode/plugins/cowork-flow.js +0 -9
- package/template/AGENTS.md +4 -3
- package/template/.cowork-flow/scripts/project_context.py +0 -230
- package/template/.cowork-flow/spec/contracts/entry-contract.md +0 -45
- /package/template/.cowork-flow/scripts/common/{config.py → core/config.py} +0 -0
- /package/template/.cowork-flow/scripts/common/{execution_context.py → core/execution_context.py} +0 -0
- /package/template/.cowork-flow/scripts/common/{files.py → core/files.py} +0 -0
- /package/template/.cowork-flow/scripts/common/{paths.py → core/paths.py} +0 -0
- /package/template/.cowork-flow/scripts/common/{gates.py → gates/gates.py} +0 -0
- /package/template/.cowork-flow/scripts/common/{validate_implementation.py → gates/validate_implementation.py} +0 -0
- /package/template/.cowork-flow/scripts/common/{git_snapshot.py → git/git_snapshot.py} +0 -0
- /package/template/.cowork-flow/scripts/common/{archive_utils.py → task/archive_utils.py} +0 -0
- /package/template/.cowork-flow/scripts/common/{state_machine.py → task/state_machine.py} +0 -0
package/README.md
CHANGED
|
@@ -99,7 +99,7 @@ OpenCode 项目级固定 agent、slash command 和系统上下文注入插件。
|
|
|
99
99
|
npx cowork-flow init ./my-project --platform codex
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
-
`init` 会显式要求平台和开发者身份。平台可选 `codex`、`opencode`、`claude-code`、`all
|
|
102
|
+
`init` 会显式要求平台和开发者身份。平台可选 `codex`、`opencode`、`claude-code`、`all`。交互式终端会提示输入;脚本或 CI 请传入:
|
|
103
103
|
|
|
104
104
|
```bash
|
|
105
105
|
npx cowork-flow init ./my-project --platform codex --developer <your-name>
|
|
@@ -134,7 +134,7 @@ npx cowork-flow --help
|
|
|
134
134
|
cowork-flow init ./my-project --platform codex
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
-
`init` 会直接复制模板中的通用 `.cowork-flow/`,并按 `--platform` 只复制对应 host 资产:`codex` 复制 `.codex/`、`.agents/skills/` 与 `.cowork-flow/adapters/codex/`,`opencode` 复制 `.opencode/`、`.agents/skills/` 与 `.cowork-flow/adapters/opencode/`,`claude-code` 复制 `CLAUDE.md`、`.claude/`(含 `.claude/settings.json`、`.claude/hooks/`、`.claude/skills/`)与 `.cowork-flow/adapters/claude-code/`,不复制 `.agents/skills/`。`all` 或 `--platform codex,opencode,claude-code` 同时复制三者和 `.agents/skills
|
|
137
|
+
`init` 会直接复制模板中的通用 `.cowork-flow/`,并按 `--platform` 只复制对应 host 资产:`codex` 复制 `.codex/`、`.agents/skills/` 与 `.cowork-flow/adapters/codex/`,`opencode` 复制 `.opencode/`、`.agents/skills/` 与 `.cowork-flow/adapters/opencode/`,`claude-code` 复制 `CLAUDE.md`、`.claude/`(含 `.claude/settings.json`、`.claude/hooks/`、`.claude/skills/`)与 `.cowork-flow/adapters/claude-code/`,不复制 `.agents/skills/`。`all` 或 `--platform codex,opencode,claude-code` 同时复制三者和 `.agents/skills/`。它还会初始化 `.cowork-flow/.developer` 与 `.cowork-flow/workspace/<developer>/`。交互式终端会先显示 checkbox 多选界面选择平台,再提示开发者名称;非交互式环境必须传入 `--platform <codex|opencode|claude-code|all>` 与 `--developer <name>`。`init` 不会复制额外技能包。
|
|
138
138
|
|
|
139
139
|
初始化到当前项目:
|
|
140
140
|
|
|
@@ -185,6 +185,16 @@ cowork-flow sync . --dry-run
|
|
|
185
185
|
下面示例使用 macOS / Linux 写法;Windows 原生命令行中把
|
|
186
186
|
`./.cowork-flow/run` 替换为 `.\.cowork-flow\run.cmd`。
|
|
187
187
|
|
|
188
|
+
当前主流程以 `.cowork-flow/workflow.md` 为准。默认路径是:
|
|
189
|
+
|
|
190
|
+
```text
|
|
191
|
+
changes -> brainstorming -> read spec -> plan -> tasks -> implement -> check -> complete
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
L0 文档、格式、小范围重构或测试补充可简化为
|
|
195
|
+
`brainstorming -> read spec -> implement -> check -> complete`;L1/L2 在完成后继续
|
|
196
|
+
`archive -> add session`。
|
|
197
|
+
|
|
188
198
|
初始化或查看开发者身份:
|
|
189
199
|
|
|
190
200
|
```bash
|
|
@@ -196,7 +206,6 @@ cowork-flow sync . --dry-run
|
|
|
196
206
|
|
|
197
207
|
```bash
|
|
198
208
|
./.cowork-flow/run get-context
|
|
199
|
-
./.cowork-flow/run project-context refresh
|
|
200
209
|
./.cowork-flow/run task list
|
|
201
210
|
./.cowork-flow/run task next
|
|
202
211
|
```
|
|
@@ -216,18 +225,35 @@ cowork-flow sync . --dry-run
|
|
|
216
225
|
./.cowork-flow/run task start <task-dir>
|
|
217
226
|
```
|
|
218
227
|
|
|
219
|
-
`task next`
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
`project-context refresh` 会创建或刷新 `.cowork-flow/project-context.md`。生成块由
|
|
224
|
-
本地项目文件确定,`Manual Notes` 区保留人工补充;该文件是项目索引,不替代
|
|
225
|
-
`AGENTS.md`、`.cowork-flow/workflow.md` 或 `.cowork-flow/spec/`。
|
|
228
|
+
`task next` 只读,不推进状态。它根据当前会话任务和 `task.json.status`
|
|
229
|
+
报告当前任务、阻塞项和下一条安全命令;主会话在启动工作、派发固定代理、
|
|
230
|
+
进入检查、归档或记录 session 前都应先看它。
|
|
226
231
|
|
|
227
232
|
L2 任务的 readiness gate 会在 `task start` 前阻塞缺失的 proposal/spec/design、
|
|
228
233
|
计划、任务链接、边界、假设、验收标准或 verification commands;同一 blocker
|
|
229
234
|
会出现在 `task next` 输出中。
|
|
230
235
|
|
|
236
|
+
任务状态流转:
|
|
237
|
+
|
|
238
|
+
```mermaid
|
|
239
|
+
stateDiagram-v2
|
|
240
|
+
[*] --> NoTask
|
|
241
|
+
state "无当前任务" as NoTask
|
|
242
|
+
state "planning" as Planning
|
|
243
|
+
state "in_progress" as InProgress
|
|
244
|
+
state "review" as Review
|
|
245
|
+
state "completed" as Completed
|
|
246
|
+
state "已归档" as Archived
|
|
247
|
+
|
|
248
|
+
NoTask --> Planning: task create
|
|
249
|
+
Planning --> InProgress: task start
|
|
250
|
+
InProgress --> Review: task review
|
|
251
|
+
Review --> Completed: task complete
|
|
252
|
+
Completed --> Archived: task archive
|
|
253
|
+
Completed --> NoTask: task finish
|
|
254
|
+
Archived --> [*]
|
|
255
|
+
```
|
|
256
|
+
|
|
231
257
|
任务状态由阶段命令推进,`task next` 只读取状态:
|
|
232
258
|
|
|
233
259
|
| 阶段 | 命令 | `task.json.status` |
|
|
@@ -236,11 +262,13 @@ L2 任务的 readiness gate 会在 `task start` 前阻塞缺失的 proposal/spec
|
|
|
236
262
|
| 开始执行 | `task start <task-dir>` | `in_progress` |
|
|
237
263
|
| 进入检查 | `task review [task-dir]` | `review` |
|
|
238
264
|
| 检查完成 | `task complete [task-dir]` | `completed` |
|
|
265
|
+
| 归档任务 | `task archive <task-name>` | 归档副本保持 `completed` |
|
|
239
266
|
| 清会话指针 | `task finish` | 不改变状态 |
|
|
240
267
|
|
|
241
268
|
执行 plan 时使用固定 cowork agents:
|
|
242
269
|
|
|
243
|
-
|
|
270
|
+
主会话负责创建/启动任务、维护计划与收口验证;实现和检查默认通过当前 Host Adapter
|
|
271
|
+
派发给固定角色 agent。固定 agent 是叶子执行者,不能再派发其他 agent,也不能替代主会话完成归档、记录 session 或提交。
|
|
244
272
|
|
|
245
273
|
```text
|
|
246
274
|
./.cowork-flow/run subagent init --role implement --agent-type cowork-implement --execution-task-dir <task-dir> --title "<title>"
|
|
@@ -255,7 +283,7 @@ cowork_host_context_key: <host_context_key>
|
|
|
255
283
|
|
|
256
284
|
子代理首步执行 `./.cowork-flow/run subagent bind <runtime_context_id> <host_context_key>`;主会话验收前必须确认 runtime context 中 `status=bound` 且 `bound_context_key` 匹配。默认角色为 `cowork-research`、`cowork-implement`、`cowork-check`,任务上下文来自已绑定 runtime context,而不是 prompt 形状。
|
|
257
285
|
|
|
258
|
-
|
|
286
|
+
Host hook/plugin 启用后,每轮会自动注入 workflow state,其中包含入口分类、当前任务、状态来源和下一步流程提示。状态文本来自 `.cowork-flow/spec/contracts/workflow-state-templates.md`;`workflow.md` 只描述流程,不内联状态片段。hook 不替代主会话验收;它只负责把 task 状态和 workflow gate 放进上下文。
|
|
259
287
|
|
|
260
288
|
记录 session:
|
|
261
289
|
|
package/package.json
CHANGED
package/src/commands/init.js
CHANGED
|
@@ -195,7 +195,7 @@ function initialIndexContent(name) {
|
|
|
195
195
|
- Sessions are appended to the journal file
|
|
196
196
|
- A new journal file is created automatically after the current file exceeds 2000 lines
|
|
197
197
|
- Use \`add_session.py\` to record sessions
|
|
198
|
-
- New records use English text;
|
|
198
|
+
- New records use English text; existing records can remain as they are
|
|
199
199
|
`;
|
|
200
200
|
}
|
|
201
201
|
|
package/src/commands/update.js
CHANGED
|
@@ -6,10 +6,8 @@ import {
|
|
|
6
6
|
} from '../lib/package-info.js';
|
|
7
7
|
|
|
8
8
|
function validateUpdateArgs(args) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
throw new Error(`Unknown update option: ${arg}`);
|
|
12
|
-
}
|
|
9
|
+
if (args.length > 0) {
|
|
10
|
+
throw new Error(`Unknown update option: ${args[0]}`);
|
|
13
11
|
}
|
|
14
12
|
}
|
|
15
13
|
|
package/src/lib/copy-template.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { constants } from 'node:fs';
|
|
2
|
-
import { access, chmod, copyFile, mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { access, chmod, copyFile, mkdir, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { dirname, join, relative } from 'node:path';
|
|
4
4
|
|
|
5
5
|
import { templateRoot } from './paths.js';
|
|
@@ -99,6 +99,41 @@ const SAFE_SYNC_FILES = new Set([
|
|
|
99
99
|
'.cowork-flow/spec/contracts/workflow-state-templates.md'
|
|
100
100
|
]);
|
|
101
101
|
|
|
102
|
+
const OBSOLETE_SYNC_FILES = new Set([
|
|
103
|
+
'.cowork-flow/project-context.md',
|
|
104
|
+
'.cowork-flow/scripts/add_session.py',
|
|
105
|
+
'.cowork-flow/scripts/change.py',
|
|
106
|
+
'.cowork-flow/scripts/doctor.py',
|
|
107
|
+
'.cowork-flow/scripts/get_context.py',
|
|
108
|
+
'.cowork-flow/scripts/get_developer.py',
|
|
109
|
+
'.cowork-flow/scripts/init_developer.py',
|
|
110
|
+
'.cowork-flow/scripts/party_mode_v2.py',
|
|
111
|
+
'.cowork-flow/scripts/project_context.py',
|
|
112
|
+
'.cowork-flow/scripts/resume.py',
|
|
113
|
+
'.cowork-flow/scripts/subagent.py',
|
|
114
|
+
'.cowork-flow/scripts/task.py',
|
|
115
|
+
'.cowork-flow/scripts/common/active_task.py',
|
|
116
|
+
'.cowork-flow/scripts/common/archive_utils.py',
|
|
117
|
+
'.cowork-flow/scripts/common/coding_standards.py',
|
|
118
|
+
'.cowork-flow/scripts/common/config.py',
|
|
119
|
+
'.cowork-flow/scripts/common/developer.py',
|
|
120
|
+
'.cowork-flow/scripts/common/execution_context.py',
|
|
121
|
+
'.cowork-flow/scripts/common/files.py',
|
|
122
|
+
'.cowork-flow/scripts/common/gates.py',
|
|
123
|
+
'.cowork-flow/scripts/common/git_context.py',
|
|
124
|
+
'.cowork-flow/scripts/common/git_snapshot.py',
|
|
125
|
+
'.cowork-flow/scripts/common/paths.py',
|
|
126
|
+
'.cowork-flow/scripts/common/readiness.py',
|
|
127
|
+
'.cowork-flow/scripts/common/state_machine.py',
|
|
128
|
+
'.cowork-flow/scripts/common/task_utils.py',
|
|
129
|
+
'.cowork-flow/scripts/common/tdd_evidence.py',
|
|
130
|
+
'.cowork-flow/scripts/common/test_intent.py',
|
|
131
|
+
'.cowork-flow/scripts/common/validate_coding_standards.py',
|
|
132
|
+
'.cowork-flow/scripts/common/validate_implementation.py',
|
|
133
|
+
'.cowork-flow/scripts/common/validate_rules.py',
|
|
134
|
+
'.cowork-flow/spec/contracts/entry-contract.md'
|
|
135
|
+
]);
|
|
136
|
+
|
|
102
137
|
const COWORK_FLOW_START = '<!-- COWORK-FLOW:START -->';
|
|
103
138
|
const COWORK_FLOW_END = '<!-- COWORK-FLOW:END -->';
|
|
104
139
|
|
|
@@ -200,6 +235,13 @@ export async function buildSyncPlan(targetDir, options = {}) {
|
|
|
200
235
|
}
|
|
201
236
|
}
|
|
202
237
|
|
|
238
|
+
for (const file of OBSOLETE_SYNC_FILES) {
|
|
239
|
+
const destination = join(targetDir, file);
|
|
240
|
+
if (await pathExists(destination)) {
|
|
241
|
+
actions.push({ action: 'delete', source: null, destination, relativePath: file });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
203
245
|
actions.push({
|
|
204
246
|
action: 'update',
|
|
205
247
|
source: null,
|
|
@@ -226,7 +268,7 @@ export async function detectInstalledPlatforms(targetDir) {
|
|
|
226
268
|
}
|
|
227
269
|
|
|
228
270
|
export function summarizePlan(actions, dryRun = false) {
|
|
229
|
-
const counts = { create: 0, update: 0, skip: 0, protected: 0 };
|
|
271
|
+
const counts = { create: 0, update: 0, skip: 0, protected: 0, delete: 0 };
|
|
230
272
|
for (const action of actions) {
|
|
231
273
|
counts[action.action] += 1;
|
|
232
274
|
}
|
|
@@ -234,7 +276,8 @@ export function summarizePlan(actions, dryRun = false) {
|
|
|
234
276
|
const prefix = dryRun ? 'dry-run ' : '';
|
|
235
277
|
const createLabel = dryRun ? 'would-create' : 'created';
|
|
236
278
|
const updateLabel = dryRun ? 'would-update' : 'updated';
|
|
237
|
-
|
|
279
|
+
const deleteLabel = dryRun ? 'would-delete' : 'deleted';
|
|
280
|
+
return `${prefix}${createLabel}=${counts.create} ${updateLabel}=${counts.update} ${deleteLabel}=${counts.delete} skipped=${counts.skip} protected=${counts.protected}\n`;
|
|
238
281
|
}
|
|
239
282
|
|
|
240
283
|
export async function applyPlan(actions, options = {}) {
|
|
@@ -246,6 +289,10 @@ export async function applyPlan(actions, options = {}) {
|
|
|
246
289
|
if (item.action === 'skip' || item.action === 'protected') {
|
|
247
290
|
continue;
|
|
248
291
|
}
|
|
292
|
+
if (item.action === 'delete') {
|
|
293
|
+
await rm(item.destination, { force: true });
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
249
296
|
|
|
250
297
|
await mkdir(dirname(item.destination), { recursive: true });
|
|
251
298
|
if (item.content !== undefined) {
|
package/src/lib/platforms.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
export const SUPPORTED_PLATFORMS = ['codex', 'opencode', 'claude-code'];
|
|
2
2
|
|
|
3
|
-
const LEGACY_BOTH_PLATFORMS = ['codex', 'opencode'];
|
|
4
|
-
|
|
5
3
|
const PLATFORM_ALIASES = new Map([
|
|
6
4
|
['all', SUPPORTED_PLATFORMS],
|
|
7
|
-
['both', LEGACY_BOTH_PLATFORMS],
|
|
8
5
|
['codex', ['codex']],
|
|
9
6
|
['opencode', ['opencode']],
|
|
10
7
|
['claude', ['claude-code']],
|
|
@@ -24,9 +24,9 @@ state is injected. The first child step must still run:
|
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
If the explicit bind fails, or if the bound context is missing, closed, invalid,
|
|
27
|
-
or names another agent type, report `needs_context` and stop. Do not
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
or names another agent type, report `needs_context` and stop. Do not infer
|
|
28
|
+
subagent identity from prompt shape; runtime context binding is the only formal
|
|
29
|
+
signal.
|
|
30
30
|
|
|
31
31
|
Rules:
|
|
32
32
|
|
|
@@ -24,9 +24,9 @@ state is injected. The first child step must still run:
|
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
If the explicit bind fails, or if the bound context is missing, closed, invalid,
|
|
27
|
-
or names another agent type, report `needs_context` and stop. Do not
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
or names another agent type, report `needs_context` and stop. Do not infer
|
|
28
|
+
subagent identity from prompt shape; runtime context binding is the only formal
|
|
29
|
+
signal.
|
|
30
30
|
|
|
31
31
|
---
|
|
32
32
|
|
|
@@ -24,9 +24,9 @@ state is injected. The first child step must still run:
|
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
If the explicit bind fails, or if the bound context is missing, closed, invalid,
|
|
27
|
-
or names another agent type, report `needs_context` and stop. Do not
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
or names another agent type, report `needs_context` and stop. Do not infer
|
|
28
|
+
subagent identity from prompt shape; runtime context binding is the only formal
|
|
29
|
+
signal.
|
|
30
30
|
|
|
31
31
|
Rules:
|
|
32
32
|
|
|
@@ -17,15 +17,6 @@ TAG_RE = re.compile(
|
|
|
17
17
|
)
|
|
18
18
|
DEFAULT_CONTRACT_REGISTRY = {
|
|
19
19
|
"contracts": [
|
|
20
|
-
{
|
|
21
|
-
"id": "COWORK_ENTRY_CONTRACT_V1",
|
|
22
|
-
"path": ".cowork-flow/spec/contracts/entry-contract.md",
|
|
23
|
-
"digest": [
|
|
24
|
-
"Classify main-session requests before task start, resume, archive, or commit.",
|
|
25
|
-
"Runtime context, not prompt labels, identifies formal subagent sessions.",
|
|
26
|
-
],
|
|
27
|
-
"readWhen": ["before task start/resume/archive", "when prompt and bootstrap text conflict"],
|
|
28
|
-
},
|
|
29
20
|
{
|
|
30
21
|
"id": "RUNTIME_CONTEXT_DISPATCH_V2",
|
|
31
22
|
"path": ".cowork-flow/spec/contracts/subagent-dispatch.md",
|
|
@@ -170,7 +161,7 @@ def _load_common(root: Path) -> None:
|
|
|
170
161
|
def _get_active_task(root: Path, hook_input: dict[str, Any]) -> tuple[str | None, str, str]:
|
|
171
162
|
_load_common(root)
|
|
172
163
|
try:
|
|
173
|
-
from common.active_task import get_active_task # type: ignore[import-not-found]
|
|
164
|
+
from common.task.active_task import get_active_task # type: ignore[import-not-found]
|
|
174
165
|
except Exception:
|
|
175
166
|
return None, "no_task", "unavailable"
|
|
176
167
|
|
|
@@ -194,7 +185,7 @@ def _get_active_task(root: Path, hook_input: dict[str, Any]) -> tuple[str | None
|
|
|
194
185
|
def _resolve_runtime_context(root: Path, hook_input: dict[str, Any]) -> tuple[dict[str, Any] | None, str | None]:
|
|
195
186
|
_load_common(root)
|
|
196
187
|
try:
|
|
197
|
-
from common.active_task import ( # type: ignore[import-not-found]
|
|
188
|
+
from common.task.active_task import ( # type: ignore[import-not-found]
|
|
198
189
|
bind_runtime_context,
|
|
199
190
|
read_runtime_context,
|
|
200
191
|
resolve_runtime_context_id,
|
|
@@ -18,15 +18,6 @@ TAG_RE = re.compile(
|
|
|
18
18
|
)
|
|
19
19
|
DEFAULT_CONTRACT_REGISTRY = {
|
|
20
20
|
"contracts": [
|
|
21
|
-
{
|
|
22
|
-
"id": "COWORK_ENTRY_CONTRACT_V1",
|
|
23
|
-
"path": ".cowork-flow/spec/contracts/entry-contract.md",
|
|
24
|
-
"digest": [
|
|
25
|
-
"Classify main-session requests before task start, resume, archive, or commit.",
|
|
26
|
-
"Runtime context, not prompt labels, identifies formal subagent sessions.",
|
|
27
|
-
],
|
|
28
|
-
"readWhen": ["before task start/resume/archive", "when prompt and bootstrap text conflict"],
|
|
29
|
-
},
|
|
30
21
|
{
|
|
31
22
|
"id": "RUNTIME_CONTEXT_DISPATCH_V2",
|
|
32
23
|
"path": ".cowork-flow/spec/contracts/subagent-dispatch.md",
|
|
@@ -154,7 +145,7 @@ def _load_common(root: Path) -> None:
|
|
|
154
145
|
def _get_dispatch_mode(root: Path) -> str:
|
|
155
146
|
_load_common(root)
|
|
156
147
|
try:
|
|
157
|
-
from common.config import get_codex_dispatch_mode # type: ignore[import-not-found]
|
|
148
|
+
from common.core.config import get_codex_dispatch_mode # type: ignore[import-not-found]
|
|
158
149
|
except Exception:
|
|
159
150
|
return "sub-agent"
|
|
160
151
|
try:
|
|
@@ -166,7 +157,7 @@ def _get_dispatch_mode(root: Path) -> str:
|
|
|
166
157
|
def _get_active_task(root: Path, hook_input: dict[str, Any]) -> tuple[str | None, str, str]:
|
|
167
158
|
_load_common(root)
|
|
168
159
|
try:
|
|
169
|
-
from common.active_task import get_active_task # type: ignore[import-not-found]
|
|
160
|
+
from common.task.active_task import get_active_task # type: ignore[import-not-found]
|
|
170
161
|
except Exception:
|
|
171
162
|
return None, "no_task", "unavailable"
|
|
172
163
|
|
|
@@ -190,7 +181,7 @@ def _get_active_task(root: Path, hook_input: dict[str, Any]) -> tuple[str | None
|
|
|
190
181
|
def _resolve_runtime_context(root: Path, hook_input: dict[str, Any]) -> tuple[dict[str, Any] | None, str | None]:
|
|
191
182
|
_load_common(root)
|
|
192
183
|
try:
|
|
193
|
-
from common.active_task import ( # type: ignore[import-not-found]
|
|
184
|
+
from common.task.active_task import ( # type: ignore[import-not-found]
|
|
194
185
|
bind_runtime_context,
|
|
195
186
|
read_runtime_context,
|
|
196
187
|
resolve_runtime_context_id,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.0.
|
|
1
|
+
0.0.29
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Command entrypoints for cowork-flow."""
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Allow command scripts to import the sibling common package when run directly."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
SCRIPTS_DIR = Path(__file__).resolve().parents[1]
|
|
9
|
+
if str(SCRIPTS_DIR) not in sys.path:
|
|
10
|
+
sys.path.insert(0, str(SCRIPTS_DIR))
|
|
@@ -17,7 +17,11 @@ import sys
|
|
|
17
17
|
from datetime import datetime
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
if __package__:
|
|
21
|
+
from . import _bootstrap as _bootstrap # noqa: F401
|
|
22
|
+
else:
|
|
23
|
+
import _bootstrap # noqa: F401
|
|
24
|
+
from common.core.paths import (
|
|
21
25
|
DIR_TASKS,
|
|
22
26
|
DIR_WORKFLOW,
|
|
23
27
|
DIR_WORKSPACE,
|
|
@@ -27,8 +31,8 @@ from common.paths import (
|
|
|
27
31
|
get_developer,
|
|
28
32
|
get_workspace_dir,
|
|
29
33
|
)
|
|
30
|
-
from common.developer import ensure_developer
|
|
31
|
-
from common.config import get_session_commit_message, get_max_journal_lines
|
|
34
|
+
from common.core.developer import ensure_developer
|
|
35
|
+
from common.core.config import get_session_commit_message, get_max_journal_lines
|
|
32
36
|
|
|
33
37
|
MARKER_CURRENT_STATUS_START = "@@@auto:current-status"
|
|
34
38
|
MARKER_CURRENT_STATUS_END = "@@@/auto:current-status"
|
|
@@ -428,12 +432,6 @@ def main() -> int:
|
|
|
428
432
|
action="store_true",
|
|
429
433
|
help="Auto git commit after recording workspace metadata",
|
|
430
434
|
)
|
|
431
|
-
parser.add_argument(
|
|
432
|
-
"--no-commit",
|
|
433
|
-
action="store_true",
|
|
434
|
-
help="Deprecated no-op; add-session no longer commits by default",
|
|
435
|
-
)
|
|
436
|
-
|
|
437
435
|
args = parser.parse_args()
|
|
438
436
|
|
|
439
437
|
extra_content = "(add details)"
|
|
@@ -446,7 +444,7 @@ def main() -> int:
|
|
|
446
444
|
|
|
447
445
|
return add_session(
|
|
448
446
|
args.title, args.commit, args.summary, extra_content,
|
|
449
|
-
auto_commit=args.auto_commit
|
|
447
|
+
auto_commit=args.auto_commit,
|
|
450
448
|
)
|
|
451
449
|
|
|
452
450
|
|
|
@@ -11,8 +11,12 @@ from datetime import datetime
|
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
from typing import Iterable
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
from
|
|
14
|
+
if __package__:
|
|
15
|
+
from . import _bootstrap as _bootstrap # noqa: F401
|
|
16
|
+
else:
|
|
17
|
+
import _bootstrap # noqa: F401
|
|
18
|
+
from common.task.archive_utils import archive_directory_resumable
|
|
19
|
+
from common.core.paths import (
|
|
16
20
|
DIR_ARCHIVE,
|
|
17
21
|
DIR_CHANGES,
|
|
18
22
|
DIR_WORKFLOW,
|
|
@@ -13,7 +13,11 @@ try:
|
|
|
13
13
|
except ModuleNotFoundError: # pragma: no cover - Python < 3.11
|
|
14
14
|
tomllib = None # type: ignore[assignment]
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
if __package__:
|
|
17
|
+
from . import _bootstrap as _bootstrap # noqa: F401
|
|
18
|
+
else:
|
|
19
|
+
import _bootstrap # noqa: F401
|
|
20
|
+
from common.core.paths import get_repo_root
|
|
17
21
|
|
|
18
22
|
|
|
19
23
|
ENTRY_BOUNDARY_DIR = "entry" + "-boundary"
|
|
@@ -101,22 +105,12 @@ REQUIRED_WORKFLOW_STATE_TEMPLATE_SNIPPETS = [
|
|
|
101
105
|
"UNKNOWN is not a delegated",
|
|
102
106
|
]
|
|
103
107
|
|
|
104
|
-
REQUIRED_ENTRY_CONTRACT_SNIPPETS = [
|
|
105
|
-
"COWORK_ENTRY_CONTRACT_V1",
|
|
106
|
-
"main-session prompts",
|
|
107
|
-
"resolved by runtime context binding",
|
|
108
|
-
"`UNKNOWN` is not subagent evidence",
|
|
109
|
-
"Runtime context binding overrides project bootstrap text",
|
|
110
|
-
]
|
|
111
|
-
|
|
112
108
|
REQUIRED_CONTRACT_REGISTRY_SNIPPETS = [
|
|
113
109
|
'"schemaVersion": 1',
|
|
114
|
-
'"COWORK_ENTRY_CONTRACT_V1"',
|
|
115
110
|
'"RUNTIME_CONTEXT_DISPATCH_V2"',
|
|
116
111
|
'"HOST_ADAPTER_CAPABILITIES_V1"',
|
|
117
112
|
'"HOST_ADAPTER_SCHEMA_V1"',
|
|
118
113
|
'"readWhen"',
|
|
119
|
-
'".cowork-flow/spec/contracts/entry-contract.md"',
|
|
120
114
|
'".cowork-flow/spec/contracts/subagent-dispatch.md"',
|
|
121
115
|
'".cowork-flow/spec/contracts/capabilities.md"',
|
|
122
116
|
'".cowork-flow/spec/schemas/adapter.schema.json"',
|
|
@@ -216,11 +210,6 @@ def _check_toml_parseable(path: Path, errors: list[str]) -> dict | None:
|
|
|
216
210
|
|
|
217
211
|
|
|
218
212
|
def _check_common_contracts(repo_root: Path, errors: list[str]) -> None:
|
|
219
|
-
for rel in (
|
|
220
|
-
".cowork-flow/spec/contracts/entry-contract.md",
|
|
221
|
-
"template/.cowork-flow/spec/contracts/entry-contract.md",
|
|
222
|
-
):
|
|
223
|
-
_check_file_contains(repo_root / rel, REQUIRED_ENTRY_CONTRACT_SNIPPETS, errors)
|
|
224
213
|
for rel in (
|
|
225
214
|
".cowork-flow/spec/runtime/contract-registry.json",
|
|
226
215
|
"template/.cowork-flow/spec/runtime/contract-registry.json",
|
|
@@ -273,23 +262,6 @@ def _check_host_adapters(repo_root: Path, errors: list[str]) -> None:
|
|
|
273
262
|
)
|
|
274
263
|
|
|
275
264
|
|
|
276
|
-
def cmd_entry_contract(_: argparse.Namespace) -> int:
|
|
277
|
-
repo_root = get_repo_root()
|
|
278
|
-
errors: list[str] = []
|
|
279
|
-
_check_common_contracts(repo_root, errors)
|
|
280
|
-
for rel in (
|
|
281
|
-
f".agents/skills/{ENTRY_BOUNDARY_DIR}/SKILL.md",
|
|
282
|
-
f"template/.agents/skills/{ENTRY_BOUNDARY_DIR}/SKILL.md",
|
|
283
|
-
):
|
|
284
|
-
_check_file_absent(repo_root / rel, errors)
|
|
285
|
-
if errors:
|
|
286
|
-
for error in errors:
|
|
287
|
-
print(f"ERROR: {error}", file=sys.stderr)
|
|
288
|
-
return 1
|
|
289
|
-
print("entry contract checks passed")
|
|
290
|
-
return 0
|
|
291
|
-
|
|
292
|
-
|
|
293
265
|
def cmd_host_adapters(_: argparse.Namespace) -> int:
|
|
294
266
|
repo_root = get_repo_root()
|
|
295
267
|
errors: list[str] = []
|
|
@@ -459,12 +431,12 @@ def cmd_subagent_safety(_: argparse.Namespace) -> int:
|
|
|
459
431
|
):
|
|
460
432
|
_check_file_contains(repo_root / rel, REQUIRED_RUNTIME_HOOK_SNIPPETS, errors)
|
|
461
433
|
for rel in (
|
|
462
|
-
".cowork-flow/scripts/subagent.py",
|
|
463
|
-
"template/.cowork-flow/scripts/subagent.py",
|
|
464
|
-
".cowork-flow/scripts/common/execution_context.py",
|
|
465
|
-
"template/.cowork-flow/scripts/common/execution_context.py",
|
|
466
|
-
".cowork-flow/scripts/common/active_task.py",
|
|
467
|
-
"template/.cowork-flow/scripts/common/active_task.py",
|
|
434
|
+
".cowork-flow/scripts/commands/subagent.py",
|
|
435
|
+
"template/.cowork-flow/scripts/commands/subagent.py",
|
|
436
|
+
".cowork-flow/scripts/common/core/execution_context.py",
|
|
437
|
+
"template/.cowork-flow/scripts/common/core/execution_context.py",
|
|
438
|
+
".cowork-flow/scripts/common/task/active_task.py",
|
|
439
|
+
"template/.cowork-flow/scripts/common/task/active_task.py",
|
|
468
440
|
):
|
|
469
441
|
if not (repo_root / rel).is_file():
|
|
470
442
|
errors.append(f"missing file: {rel}")
|
|
@@ -479,7 +451,6 @@ def cmd_subagent_safety(_: argparse.Namespace) -> int:
|
|
|
479
451
|
def build_parser() -> argparse.ArgumentParser:
|
|
480
452
|
parser = argparse.ArgumentParser(description="cowork-flow diagnostics")
|
|
481
453
|
parser.add_argument("--subagent-safety", action="store_true", help="Check subagent safety wiring")
|
|
482
|
-
parser.add_argument("--entry-contract", action="store_true", help="Check entry classification contract")
|
|
483
454
|
parser.add_argument("--host-adapters", action="store_true", help="Check host adapter declarations")
|
|
484
455
|
return parser
|
|
485
456
|
|
|
@@ -489,8 +460,6 @@ def main() -> int:
|
|
|
489
460
|
args = parser.parse_args()
|
|
490
461
|
if args.subagent_safety:
|
|
491
462
|
return cmd_subagent_safety(args)
|
|
492
|
-
if args.entry_contract:
|
|
493
|
-
return cmd_entry_contract(args)
|
|
494
463
|
if args.host_adapters:
|
|
495
464
|
return cmd_host_adapters(args)
|
|
496
465
|
parser.print_help()
|
|
@@ -9,7 +9,11 @@ Usage:
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
if __package__:
|
|
13
|
+
from . import _bootstrap as _bootstrap # noqa: F401
|
|
14
|
+
else:
|
|
15
|
+
import _bootstrap # noqa: F401
|
|
16
|
+
from common.git.git_context import main
|
|
13
17
|
|
|
14
18
|
|
|
15
19
|
if __name__ == "__main__":
|
|
@@ -2,14 +2,18 @@
|
|
|
2
2
|
"""
|
|
3
3
|
Get current developer name.
|
|
4
4
|
|
|
5
|
-
This is a wrapper that uses common/paths.py
|
|
5
|
+
This is a wrapper that uses common/core/paths.py
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import sys
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
if __package__:
|
|
13
|
+
from . import _bootstrap as _bootstrap # noqa: F401
|
|
14
|
+
else:
|
|
15
|
+
import _bootstrap # noqa: F401
|
|
16
|
+
from common.core.paths import get_developer
|
|
13
17
|
|
|
14
18
|
|
|
15
19
|
def main() -> None:
|
|
@@ -14,12 +14,16 @@ from __future__ import annotations
|
|
|
14
14
|
|
|
15
15
|
import sys
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
if __package__:
|
|
18
|
+
from . import _bootstrap as _bootstrap # noqa: F401
|
|
19
|
+
else:
|
|
20
|
+
import _bootstrap # noqa: F401
|
|
21
|
+
from common.core.paths import (
|
|
18
22
|
DIR_WORKFLOW,
|
|
19
23
|
FILE_DEVELOPER,
|
|
20
24
|
get_developer,
|
|
21
25
|
)
|
|
22
|
-
from common.developer import init_developer
|
|
26
|
+
from common.core.developer import init_developer
|
|
23
27
|
|
|
24
28
|
|
|
25
29
|
def main() -> None:
|
|
@@ -18,8 +18,12 @@ from datetime import datetime, timezone
|
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
from typing import Any
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
from
|
|
21
|
+
if __package__:
|
|
22
|
+
from . import _bootstrap as _bootstrap # noqa: F401
|
|
23
|
+
else:
|
|
24
|
+
import _bootstrap # noqa: F401
|
|
25
|
+
from common.core.config import get_party_mode_v2_config
|
|
26
|
+
from common.core.paths import DIR_WORKFLOW, get_repo_root
|
|
23
27
|
|
|
24
28
|
RUNTIME_DIR = ".runtime"
|
|
25
29
|
MODE_DIR = "party-mode-v2"
|