cowork-flow 1.6.0 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +213 -213
- package/CONTRIBUTING.md +137 -0
- package/LICENSE +1 -1
- package/README.md +108 -369
- package/docs/architecture.md +122 -0
- package/docs/hosts.md +290 -0
- package/docs/index.md +32 -0
- package/docs/release.md +104 -0
- package/package.json +37 -7
- package/presets/claude-code/.claude-plugin/plugin.json +28 -0
- package/presets/claude-code/skills/cowork-flow-bootstrap/SKILL.md +35 -0
- package/presets/codex/.codex-plugin/plugin.json +37 -0
- package/presets/codex/assets/logo.svg +4 -0
- package/presets/codex/skills/cowork-flow-bootstrap/SKILL.md +35 -0
- package/presets/dsh/agent.cordis.yml +35 -12
- package/presets/dsh/plugins/workflow-state.js +88 -72
- package/presets/dsh/preset.yml +1 -1
- package/presets/kimi-code/.kimi-plugin/plugin.json +35 -0
- package/presets/kimi-code/hooks/cowork-flow-inject.mjs +14 -2
- package/presets/kimi-code/skills/cowork-flow-bootstrap/SKILL.md +35 -0
- package/presets/opencode/cowork-flow/plugin-core.js +1077 -0
- package/presets/opencode/cowork-flow/skills/cowork-flow-bootstrap/SKILL.md +35 -0
- package/presets/opencode/plugins/cowork-flow.js +44 -0
- package/presets/plugin-meta.json +52 -0
- package/presets/qoder/.qoder-plugin/plugin.json +29 -0
- package/presets/qoder/agents/cowork-check.md +29 -0
- package/presets/qoder/agents/cowork-implement.md +30 -0
- package/presets/qoder/agents/cowork-research.md +23 -0
- package/presets/qoder/hooks/hooks.json +50 -0
- package/presets/qoder/hooks/inject-context.mjs +150 -0
- package/presets/qoder/skills/cowork-flow-bootstrap/SKILL.md +35 -0
- package/{template/.zcode → presets/zcode}/.zcode-plugin/plugin.json +4 -2
- package/{template/.zcode → presets/zcode}/agents/cowork-check.md +4 -4
- package/{template/.zcode → presets/zcode}/agents/cowork-implement.md +4 -4
- package/{template/.zcode → presets/zcode}/agents/cowork-research.md +1 -1
- package/{template/.zcode → presets/zcode}/hooks/inject-context.js +36 -19
- package/presets/zcode/skills/cowork-flow-bootstrap/SKILL.md +35 -0
- package/src/cli.js +130 -130
- package/src/commands/host.js +182 -0
- package/src/commands/init.js +28 -43
- package/src/commands/install-claude-code-plugin.js +129 -0
- package/src/commands/install-codex-plugin.js +312 -0
- package/src/commands/install-dsh-hook.js +36 -43
- package/src/commands/install-dsh-preset.js +228 -49
- package/src/commands/install-kimi-code-plugin.js +142 -0
- package/src/commands/install-kimi-hook.js +142 -122
- package/src/commands/install-opencode-plugin.js +158 -0
- package/src/commands/install-qoder-plugin.js +211 -0
- package/src/commands/install-zcode-plugin.js +131 -82
- package/src/commands/mcp-state.js +7 -6
- package/src/commands/registry.js +345 -0
- package/src/commands/source-refresh.js +10 -11
- package/src/commands/sync.js +10 -13
- package/src/commands/update.js +5 -10
- package/src/lib/cli-flags.js +102 -0
- package/src/lib/copy-template.js +89 -75
- package/src/lib/host-assets.js +192 -57
- package/src/lib/install-support.js +50 -0
- package/src/lib/platforms.js +0 -15
- package/src/lib/plugin-metadata.js +187 -0
- package/src/lib/plugin-payload.js +70 -0
- package/template/.cowork-flow/.version +1 -1
- package/template/.cowork-flow/adapters/claude-code/adapter.yaml +0 -1
- package/template/.cowork-flow/adapters/dsh/adapter.yaml +0 -1
- package/template/.cowork-flow/adapters/kimi-code/adapter.yaml +0 -1
- package/template/.cowork-flow/adapters/qoder/adapter.yaml +32 -0
- package/template/.cowork-flow/adapters/zcode/adapter.yaml +2 -2
- package/template/.cowork-flow/config.yaml +6 -4
- package/template/.cowork-flow/scripts/adapters/cli/batch_mode.py +10 -12
- package/template/.cowork-flow/scripts/adapters/cli/spec_check.py +67 -35
- package/template/.cowork-flow/scripts/adapters/cli/subagent.py +84 -38
- package/template/.cowork-flow/scripts/adapters/cli/task.py +42 -0
- package/template/.cowork-flow/scripts/adapters/cli/task_create_command.py +1 -9
- package/template/.cowork-flow/scripts/adapters/cli/task_lifecycle_commands.py +6 -0
- package/template/.cowork-flow/scripts/adapters/cli/task_navigation.py +13 -10
- package/template/.cowork-flow/scripts/adapters/cli/task_next_runner.py +2 -1
- package/template/.cowork-flow/scripts/adapters/git/git_context.py +14 -82
- package/template/.cowork-flow/scripts/adapters/host/claude_code_policy.py +2 -3
- package/template/.cowork-flow/scripts/adapters/host/codex_policy.py +2 -5
- package/template/.cowork-flow/scripts/adapters/host/host_manifest.py +122 -10
- package/template/.cowork-flow/scripts/adapters/host/inject.py +49 -19
- package/template/.cowork-flow/scripts/adapters/host/kimi_code_policy.py +1 -1
- package/template/.cowork-flow/scripts/adapters/host/policy_base.py +58 -0
- package/template/.cowork-flow/scripts/adapters/host/qoder_policy.py +66 -0
- package/template/.cowork-flow/scripts/adapters/host/workflow_state_hook.py +136 -132
- package/template/.cowork-flow/scripts/adapters/host/zcode_policy.py +23 -24
- package/template/.cowork-flow/scripts/adapters/mcp/state_server.py +41 -56
- package/template/.cowork-flow/scripts/adapters/review/test_intent.py +2 -2
- package/template/.cowork-flow/scripts/infra/config.py +26 -23
- package/template/.cowork-flow/scripts/infra/developer_profile.py +4 -41
- package/template/.cowork-flow/scripts/infra/files.py +3 -7
- package/template/.cowork-flow/scripts/infra/git_snapshot.py +8 -7
- package/template/.cowork-flow/scripts/infra/paths.py +9 -69
- package/template/.cowork-flow/scripts/infra/process.py +9 -0
- package/template/.cowork-flow/scripts/infra/skill_manifest.py +262 -169
- package/template/.cowork-flow/scripts/infra/storage/operation_log.py +2 -0
- package/template/.cowork-flow/scripts/infra/storage/state_store.py +0 -2
- package/template/.cowork-flow/scripts/infra/storage/unit_of_work.py +163 -18
- package/template/.cowork-flow/scripts/kernel/workflow_route.py +6 -16
- package/template/.cowork-flow/scripts/run.py +14 -4
- package/template/.cowork-flow/scripts/runtime/host_identity.py +33 -15
- package/template/.cowork-flow/scripts/runtime/session_state.py +83 -26
- package/template/.cowork-flow/scripts/services/context_discovery.py +15 -3
- package/template/.cowork-flow/scripts/services/context_paths.py +4 -4
- package/template/.cowork-flow/scripts/services/fact_view.py +13 -10
- package/template/.cowork-flow/scripts/services/lifecycle_checks.py +47 -13
- package/template/.cowork-flow/scripts/services/lifecycle_policy.py +5 -2
- package/template/.cowork-flow/scripts/services/readiness.py +8 -3
- package/template/.cowork-flow/scripts/services/spec_check.py +23 -33
- package/template/.cowork-flow/scripts/services/task_archive.py +76 -41
- package/template/.cowork-flow/scripts/services/task_creation.py +90 -34
- package/template/.cowork-flow/scripts/services/task_lifecycle.py +210 -50
- package/template/.cowork-flow/scripts/services/task_routing.py +0 -8
- package/template/.cowork-flow/scripts/services/task_tree.py +181 -29
- package/template/.cowork-flow/scripts/services/task_utils.py +3 -20
- package/template/.cowork-flow/scripts/services/workflow_runtime.py +39 -22
- package/template/.cowork-flow/spec/contracts/context-injection.md +27 -2
- package/template/.cowork-flow/spec/contracts/fact-layer-access.md +7 -4
- package/template/.cowork-flow/spec/contracts/index.md +4 -0
- package/template/.cowork-flow/spec/contracts/spec-checks.md +13 -2
- package/template/.cowork-flow/spec/runtime/host-assets.json +155 -18
- package/template/.cowork-flow/spec/runtime/index.md +1 -1
- package/template/.cowork-flow/spec/schemas/host-assets.schema.json +69 -7
- package/template/.dsh/README.md +27 -7
- package/template/.opencode/cowork-flow/plugin-core.js +1077 -0
- package/template/.opencode/plugins/cowork-flow.js +23 -800
- package/template/skills/python-runtime-design/SKILL.md +6 -0
- package/template/skills/runtime-health/scripts/doctor.py +725 -14
- /package/{template/.zcode → presets/zcode}/hooks/hooks.json +0 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cowork-flow-bootstrap
|
|
3
|
+
description: Use only when a repository has no .cowork-flow/ runtime and the user wants to start or continue structured development work — guides installing cowork-flow into the project. Do not use when .cowork-flow/ exists; the project's own cowork-flow Skill owns that case.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Cowork Flow Bootstrap
|
|
7
|
+
|
|
8
|
+
This Skill is the plugin-side bootstrap. It applies to repositories that have no
|
|
9
|
+
`.cowork-flow/` runtime yet. Once `.cowork-flow/` exists, stop and use the
|
|
10
|
+
project's own Skills: after init, `./.cowork-flow/run task next` and the project's
|
|
11
|
+
skill copies are authoritative, not this Skill.
|
|
12
|
+
|
|
13
|
+
## Decide first
|
|
14
|
+
|
|
15
|
+
Check the repository root for the runtime directory `.cowork-flow/` (or the
|
|
16
|
+
workflow entry `./.cowork-flow/run`).
|
|
17
|
+
|
|
18
|
+
## No runtime: offer the install
|
|
19
|
+
|
|
20
|
+
Tell the user this repository has no cowork-flow runtime, then offer:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx cowork-flow project init .
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
After init the project carries its own Skills (`.agents/skills/`), the task
|
|
27
|
+
runtime (`.cowork-flow/`), and the workflow entry `./.cowork-flow/run task next`.
|
|
28
|
+
Resume the work through that entry.
|
|
29
|
+
|
|
30
|
+
## Never
|
|
31
|
+
|
|
32
|
+
- Do not reproduce lifecycle rules, gates, or a second workflow document here.
|
|
33
|
+
- Do not hand-create `.cowork-flow/` or copy files out of the plugin payload.
|
|
34
|
+
- Do not run task lifecycle commands: without a project runtime there is no task
|
|
35
|
+
to advance.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cowork-flow",
|
|
3
|
+
"version": "1.7.2",
|
|
4
|
+
"description": "AI 协作工作流引擎:任务生命周期、子代理调度、门禁与状态注入。将 cowork-flow 的完整工作流带入 Codex。",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "fishlikewater",
|
|
7
|
+
"email": "fishlikewater@126.com",
|
|
8
|
+
"url": "https://github.com/fishlikewater"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/fishlikewater/cowork-flow",
|
|
11
|
+
"repository": "https://github.com/fishlikewater/cowork-flow",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"ai-agent",
|
|
15
|
+
"workflow",
|
|
16
|
+
"task-lifecycle",
|
|
17
|
+
"subagents",
|
|
18
|
+
"codex",
|
|
19
|
+
"claude-code",
|
|
20
|
+
"opencode",
|
|
21
|
+
"zcode",
|
|
22
|
+
"qoder",
|
|
23
|
+
"kimi-code",
|
|
24
|
+
"dsh"
|
|
25
|
+
],
|
|
26
|
+
"skills": "./skills/",
|
|
27
|
+
"interface": {
|
|
28
|
+
"displayName": "Cowork Flow",
|
|
29
|
+
"shortDescription": "AI 协作工作流引擎:任务生命周期、子代理调度、门禁与状态注入。",
|
|
30
|
+
"longDescription": "cowork-flow 给 AI 编码宿主的 agent 提供可执行的工作流:任务生命周期、决策记录、规范契约与独立检查。安装后任务状态由运行时统一维护,多个宿主的会话从同一份事实起步。",
|
|
31
|
+
"developerName": "fishlikewater",
|
|
32
|
+
"category": "Developer Tools",
|
|
33
|
+
"websiteURL": "https://github.com/fishlikewater/cowork-flow",
|
|
34
|
+
"logo": "./assets/logo.svg",
|
|
35
|
+
"brandColor": "#2563EB"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" role="img" aria-label="Cowork Flow">
|
|
2
|
+
<title>Cowork Flow</title>
|
|
3
|
+
<path d="M3 18H8V12H14V6H21" fill="none" stroke="#2563EB" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cowork-flow-bootstrap
|
|
3
|
+
description: Use only when a repository has no .cowork-flow/ runtime and the user wants to start or continue structured development work — guides installing cowork-flow into the project. Do not use when .cowork-flow/ exists; the project's own cowork-flow Skill owns that case.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Cowork Flow Bootstrap
|
|
7
|
+
|
|
8
|
+
This Skill is the plugin-side bootstrap. It applies to repositories that have no
|
|
9
|
+
`.cowork-flow/` runtime yet. Once `.cowork-flow/` exists, stop and use the
|
|
10
|
+
project's own Skills: after init, `./.cowork-flow/run task next` and the project's
|
|
11
|
+
skill copies are authoritative, not this Skill.
|
|
12
|
+
|
|
13
|
+
## Decide first
|
|
14
|
+
|
|
15
|
+
Check the repository root for the runtime directory `.cowork-flow/` (or the
|
|
16
|
+
workflow entry `./.cowork-flow/run`).
|
|
17
|
+
|
|
18
|
+
## No runtime: offer the install
|
|
19
|
+
|
|
20
|
+
Tell the user this repository has no cowork-flow runtime, then offer:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx cowork-flow project init .
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
After init the project carries its own Skills (`.agents/skills/`), the task
|
|
27
|
+
runtime (`.cowork-flow/`), and the workflow entry `./.cowork-flow/run task next`.
|
|
28
|
+
Resume the work through that entry.
|
|
29
|
+
|
|
30
|
+
## Never
|
|
31
|
+
|
|
32
|
+
- Do not reproduce lifecycle rules, gates, or a second workflow document here.
|
|
33
|
+
- Do not hand-create `.cowork-flow/` or copy files out of the plugin payload.
|
|
34
|
+
- Do not run task lifecycle commands: without a project runtime there is no task
|
|
35
|
+
to advance.
|
|
@@ -1,17 +1,40 @@
|
|
|
1
1
|
# The `cowork-flow` agent preset: the standard coding agent plus the
|
|
2
2
|
# cowork-flow workflow framework (task gate, planning, fixed subagents, skills).
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# `
|
|
4
|
+
# It started from the 0.1.x `standard` preset: the persona carries the
|
|
5
|
+
# cowork-flow operating rules, and `skill-filesystem` keeps the workspace
|
|
6
|
+
# default roots (`<projectRoot>/.dsh/skills` rank 100,
|
|
7
|
+
# `<projectRoot>/.agents/skills` rank 200) plus the user roots, so skills come
|
|
8
|
+
# from the project copy that `cwf project init` / `sync` delivers — this preset
|
|
9
|
+
# ships none of its own.
|
|
8
10
|
#
|
|
9
11
|
# The `dsh` host adapter lives in the cowork-flow repository
|
|
10
12
|
# (template/.cowork-flow/adapters/dsh/adapter.yaml) and declares this preset's
|
|
11
13
|
# capability model; this file is the DSH-side composition it describes.
|
|
14
|
+
#
|
|
15
|
+
# Version basis: the 0.1.x line. This file is what DSH 0.1.x reads as a preset
|
|
16
|
+
# directory (`$DSH_HOME/.agent-presets/<id>/`), and the 0.2.0 bundle is
|
|
17
|
+
# generated from it. Each delivered form must name only the packages its own
|
|
18
|
+
# line ships and carry only the config keys that line's schema accepts — either
|
|
19
|
+
# mistake makes that line fail the whole preset, not just the row. The lines
|
|
20
|
+
# disagree in two places: 0.1.x mounts `dsh-workflow-worker-thread`, which 0.2.0
|
|
21
|
+
# dropped for `dsh-workflow-ptc`, and this `dsh-persona` row is a single
|
|
22
|
+
# `text`, where 0.2.0 requires `prefix` (plus optional `suffix`). The bundle
|
|
23
|
+
# generator performs both rewrites, and a third: the hook specifier becomes a
|
|
24
|
+
# `file:` URL of the installed copy.
|
|
25
|
+
#
|
|
26
|
+
# Deliberate drift from the 0.1.x `standard` (0.1.2-rc.1), besides the
|
|
27
|
+
# cowork-flow persona and the `workflow-state-hook` row: `tool-subagent` drops
|
|
28
|
+
# `modelSelectionSettings`, the codex / claude-code rows use
|
|
29
|
+
# `enableRunInBackground: false` where the standard takes the `backgroundMode`
|
|
30
|
+
# default, `tool-web` sets `fetch: false`, and `command-goal` is not mounted.
|
|
31
|
+
# Rows the 0.2.0 `standard` adds over its 0.1.x counterpart (`present`,
|
|
32
|
+
# `tool-plugin-manager`) are absent from this preset on either line.
|
|
12
33
|
|
|
13
34
|
# ── identity ────────────────────────────────────────────────────────────────
|
|
14
35
|
|
|
36
|
+
# 0.1.x's persona schema: one `text`. 0.2.0 takes `prefix`/`suffix`, so the
|
|
37
|
+
# bundle moves this wording to `prefix` unchanged (see the header).
|
|
15
38
|
- id: persona
|
|
16
39
|
name: '@deepseek-ai/dsh-persona'
|
|
17
40
|
config:
|
|
@@ -56,16 +79,13 @@
|
|
|
56
79
|
|
|
57
80
|
# ── skills ──────────────────────────────────────────────────────────────────
|
|
58
81
|
|
|
59
|
-
# The skill REGISTRY lives in the host composition and is layered per scope
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
82
|
+
# The skill REGISTRY lives in the host composition and is layered per scope.
|
|
83
|
+
# `skill-filesystem` discovers skills from the workspace's own roots
|
|
84
|
+
# (`<projectRoot>/.dsh/skills` at rank 100, `<projectRoot>/.agents/skills` at
|
|
85
|
+
# rank 200) plus the user roots, so cowork-flow skills come from the project
|
|
86
|
+
# copy that `init`/`sync` delivers — the preset ships none of its own.
|
|
64
87
|
- id: skill-filesystem
|
|
65
88
|
name: '@deepseek-ai/dsh-skill-filesystem'
|
|
66
|
-
config:
|
|
67
|
-
customSkillDirs:
|
|
68
|
-
- !!js "process.getBuiltinModule('node:url').fileURLToPath(new URL('skills/', baseUrl))"
|
|
69
89
|
|
|
70
90
|
- id: tool-skill
|
|
71
91
|
name: '@deepseek-ai/dsh-tool-skill'
|
|
@@ -175,6 +195,9 @@
|
|
|
175
195
|
enableRunInBackground: false
|
|
176
196
|
maxDepth: provider-managed
|
|
177
197
|
|
|
198
|
+
# The 0.1.x row: 0.2.0 no longer ships this package and its `standard`
|
|
199
|
+
# mounts `dsh-workflow-ptc` instead, so the generated bundle swaps the row.
|
|
200
|
+
# This file itself is read by 0.1.x, where `ptc` resolves nowhere.
|
|
178
201
|
- id: workflow-worker-thread
|
|
179
202
|
name: '@deepseek-ai/dsh-workflow-worker-thread'
|
|
180
203
|
config:
|
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
// cowork-flow DSH workflow-state hook.
|
|
2
2
|
//
|
|
3
|
-
// DSH-native equivalent of the Codex/Claude hooks
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// is structurally identical across hosts.
|
|
3
|
+
// DSH-native equivalent of the Codex/Claude hooks:
|
|
4
|
+
// registers ONE system-prompt section, rendered last
|
|
5
|
+
// (order 1000, after identity/persona/tool guidance),
|
|
6
|
+
// whose text is the `<workflow-state>` block produced by
|
|
7
|
+
// the SAME Python protocol the other hosts use
|
|
8
|
+
// (adapters/host/workflow_state_hook.py), so the injected
|
|
9
|
+
// content is structurally identical across hosts.
|
|
10
10
|
//
|
|
11
|
-
//
|
|
12
|
-
// `agent/inbox/claimed`
|
|
13
|
-
// (`tools/result`), then cached
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
// the
|
|
11
|
+
// Refreshed per user message (`agent/created` on 0.2.0, its former name
|
|
12
|
+
// `agent/session-start` on 0.1.x, `agent/inbox/claimed` for each new one)
|
|
13
|
+
// and after lifecycle commands settle (`tools/result`), then cached
|
|
14
|
+
// per agent; each prompt assembly re-renders the cached
|
|
15
|
+
// value in place, with replace semantics and no
|
|
16
|
+
// accumulation across turns or steps. Between refreshes
|
|
17
|
+
// the text is byte-stable, so the static prompt prefix
|
|
18
|
+
// stays cacheable.
|
|
18
19
|
//
|
|
19
|
-
// Degradation is silent: no `.cowork-flow` root, a missing
|
|
20
|
-
// runtime, or the `COWORK_FLOW_HOOKS=0` /
|
|
21
|
-
// switches all yield an
|
|
22
|
-
//
|
|
20
|
+
// Degradation is silent: no `.cowork-flow` root, a missing
|
|
21
|
+
// Python, a broken runtime, or the `COWORK_FLOW_HOOKS=0` /
|
|
22
|
+
// `COWORK_FLOW_DISABLE_HOOKS=1` switches all yield an
|
|
23
|
+
// empty section. The workspace AGENTS.md gate then falls
|
|
24
|
+
// back to running the navigator manually.
|
|
23
25
|
|
|
24
26
|
import { execFile } from 'node:child_process';
|
|
25
27
|
import { randomUUID } from 'node:crypto';
|
|
@@ -38,15 +40,17 @@ const EXEC_OPTIONS = {
|
|
|
38
40
|
maxBuffer: 1024 * 1024,
|
|
39
41
|
};
|
|
40
42
|
const NO_ROOT_MARKER = '__COWORK_FLOW_NO_ROOT__';
|
|
41
|
-
// How long a "no .cowork-flow root" verdict is trusted for
|
|
42
|
-
// on purpose: a project that installs
|
|
43
|
-
//
|
|
43
|
+
// How long a "no .cowork-flow root" verdict is trusted for
|
|
44
|
+
// one cwd. Bounded on purpose: a project that installs
|
|
45
|
+
// cowork-flow mid-session becomes visible after at most
|
|
46
|
+
// one TTL, at the cost of at most one interpreter probe.
|
|
44
47
|
const NO_ROOT_TTL_MS = 30_000;
|
|
45
48
|
const noRootCache = new Map();
|
|
46
49
|
|
|
47
|
-
// Python 3.9+ protocol reused from the host hook adapters.
|
|
48
|
-
// `.cowork-flow` root from the given cwd,
|
|
49
|
-
// workflow_state_hook module, and prints
|
|
50
|
+
// Python 3.9+ protocol reused from the host hook adapters.
|
|
51
|
+
// It resolves the `.cowork-flow` root from the given cwd,
|
|
52
|
+
// loads the shared workflow_state_hook module, and prints
|
|
53
|
+
// the full hook context block.
|
|
50
54
|
const PYTHON_PROTOCOL = `\
|
|
51
55
|
import sys
|
|
52
56
|
from pathlib import Path
|
|
@@ -83,9 +87,9 @@ print(build_hook_context(
|
|
|
83
87
|
))
|
|
84
88
|
`;
|
|
85
89
|
|
|
86
|
-
// The edit path sits on the finished tool result, so it
|
|
87
|
-
// a slow checker is dropped rather
|
|
88
|
-
// made.
|
|
90
|
+
// The edit path sits on the finished tool result, so it
|
|
91
|
+
// gets a short leash: a slow checker is dropped rather
|
|
92
|
+
// than delaying the edit the model already made.
|
|
89
93
|
const EDIT_TIMEOUT_MS = 2500;
|
|
90
94
|
const EDIT_EXEC_OPTIONS = {
|
|
91
95
|
timeout: EDIT_TIMEOUT_MS,
|
|
@@ -93,9 +97,10 @@ const EDIT_EXEC_OPTIONS = {
|
|
|
93
97
|
maxBuffer: 1024 * 1024,
|
|
94
98
|
};
|
|
95
99
|
|
|
96
|
-
// Editor-phase spec-check protocol: same root discovery,
|
|
97
|
-
// edit-check entry so DSH sessions receive
|
|
98
|
-
// the other hosts get from
|
|
100
|
+
// Editor-phase spec-check protocol: same root discovery,
|
|
101
|
+
// then the shared edit-check entry so DSH sessions receive
|
|
102
|
+
// the single-line violation hint the other hosts get from
|
|
103
|
+
// their PostToolUse hook. Always exits 0.
|
|
99
104
|
const PYTHON_EDIT_PROTOCOL = `\
|
|
100
105
|
import sys
|
|
101
106
|
from pathlib import Path
|
|
@@ -121,19 +126,21 @@ except Exception:
|
|
|
121
126
|
pass
|
|
122
127
|
`;
|
|
123
128
|
|
|
124
|
-
// The first interpreter that ran the protocol
|
|
125
|
-
//
|
|
129
|
+
// The first interpreter that ran the protocol
|
|
130
|
+
// successfully; a missing one is dropped so a later
|
|
131
|
+
// refresh can rediscover it after a PATH change.
|
|
126
132
|
let workingPython = null;
|
|
127
133
|
|
|
128
|
-
// Cowork-flow lifecycle commands executed through any
|
|
129
|
-
// command text (not the tool name)
|
|
130
|
-
// surface changes.
|
|
134
|
+
// Cowork-flow lifecycle commands executed through any
|
|
135
|
+
// tool. Matching the command text (not the tool name)
|
|
136
|
+
// keeps the trigger stable across tool surface changes.
|
|
131
137
|
const LIFECYCLE_COMMAND = /\.cowork-flow[\\/]run(?:\.cmd)?(?:\s+[^\s"']*)?\s+(task|subagent|resume)\b/;
|
|
132
138
|
|
|
133
139
|
|
|
134
140
|
/**
|
|
135
|
-
* True when a tool argument value embeds a cowork-flow
|
|
136
|
-
* Accepts strings and JSON-serializable
|
|
141
|
+
* True when a tool argument value embeds a cowork-flow
|
|
142
|
+
* lifecycle command. Accepts strings and JSON-serializable
|
|
143
|
+
* structures; never throws.
|
|
137
144
|
*/
|
|
138
145
|
export function isLifecycleCommand(value) {
|
|
139
146
|
try {
|
|
@@ -176,9 +183,10 @@ function tryPython(candidate, cwd, full = true) {
|
|
|
176
183
|
EXEC_OPTIONS,
|
|
177
184
|
(error, stdout) => {
|
|
178
185
|
if (error) {
|
|
179
|
-
// ENOENT = the interpreter does not exist (try
|
|
180
|
-
// Any other failure means the
|
|
181
|
-
//
|
|
186
|
+
// ENOENT = the interpreter does not exist (try
|
|
187
|
+
// the next one). Any other failure means the
|
|
188
|
+
// interpreter ran but the protocol failed — the
|
|
189
|
+
// candidate works, the runtime does not.
|
|
182
190
|
resolve({ missing: error.code === 'ENOENT', output: '' });
|
|
183
191
|
return;
|
|
184
192
|
}
|
|
@@ -198,7 +206,8 @@ function normalize(output) {
|
|
|
198
206
|
|
|
199
207
|
|
|
200
208
|
/**
|
|
201
|
-
* Test seam: forget the memoized interpreter so the next
|
|
209
|
+
* Test seam: forget the memoized interpreter so the next
|
|
210
|
+
* call rediscovers.
|
|
202
211
|
*/
|
|
203
212
|
export function resetWorkingPython() {
|
|
204
213
|
workingPython = null;
|
|
@@ -206,9 +215,10 @@ export function resetWorkingPython() {
|
|
|
206
215
|
|
|
207
216
|
|
|
208
217
|
/**
|
|
209
|
-
* Nearest ancestor of `cwd` whose `.cowork-flow` entry is
|
|
210
|
-
* `null` when no such ancestor exists.
|
|
211
|
-
* the
|
|
218
|
+
* Nearest ancestor of `cwd` whose `.cowork-flow` entry is
|
|
219
|
+
* a directory, or `null` when no such ancestor exists.
|
|
220
|
+
* Mirrors the root resolution inside the Python protocol,
|
|
221
|
+
* so a project without cowork-flow never pays for an
|
|
212
222
|
* interpreter spawn at all.
|
|
213
223
|
*/
|
|
214
224
|
export async function findCoworkRoot(cwd) {
|
|
@@ -232,11 +242,13 @@ export async function findCoworkRoot(cwd) {
|
|
|
232
242
|
|
|
233
243
|
|
|
234
244
|
/**
|
|
235
|
-
* Produce the `<workflow-state>` hook context block for a
|
|
236
|
-
* `full` selects the digest shape: true
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
245
|
+
* Produce the `<workflow-state>` hook context block for a
|
|
246
|
+
* workspace cwd. `full` selects the digest shape: true
|
|
247
|
+
* (default) for the full contract block on
|
|
248
|
+
* session-start-like refreshes, false for the single
|
|
249
|
+
* fingerprint line on intra-turn refreshes. Empty string
|
|
250
|
+
* means "contribute nothing" (no root, hooks disabled, no
|
|
251
|
+
* usable interpreter, or a broken runtime).
|
|
240
252
|
*/
|
|
241
253
|
export async function runWorkflowState(cwd, full = true) {
|
|
242
254
|
if (hooksDisabled() || !cwd) {
|
|
@@ -277,8 +289,9 @@ const EDIT_TOOL_NAMES = new Set(['write', 'edit']);
|
|
|
277
289
|
|
|
278
290
|
|
|
279
291
|
/**
|
|
280
|
-
* Path of the file a finished tool call edited, or '' for
|
|
281
|
-
* DSH's fs tools expose `file_path`; other
|
|
292
|
+
* Path of the file a finished tool call edited, or '' for
|
|
293
|
+
* anything else. DSH's fs tools expose `file_path`; other
|
|
294
|
+
* tool surfaces are ignored.
|
|
282
295
|
*/
|
|
283
296
|
export function editedFilePath(exec) {
|
|
284
297
|
const name = exec && typeof exec.name === 'string' ? exec.name : '';
|
|
@@ -292,9 +305,10 @@ export function editedFilePath(exec) {
|
|
|
292
305
|
|
|
293
306
|
|
|
294
307
|
/**
|
|
295
|
-
* Editor-phase spec check for one edited file. Returns the
|
|
296
|
-
* warning, or '' when there is nothing to say:
|
|
297
|
-
* declaration, a clean run, or
|
|
308
|
+
* Editor-phase spec check for one edited file. Returns the
|
|
309
|
+
* single-line warning, or '' when there is nothing to say:
|
|
310
|
+
* no root, no interpreter, no declaration, a clean run, or
|
|
311
|
+
* a timeout. Never throws.
|
|
298
312
|
*/
|
|
299
313
|
export async function runEditSpecCheck(cwd, filePath) {
|
|
300
314
|
if (hooksDisabled() || !cwd || !filePath) {
|
|
@@ -338,9 +352,10 @@ export async function runEditSpecCheck(cwd, filePath) {
|
|
|
338
352
|
|
|
339
353
|
|
|
340
354
|
/**
|
|
341
|
-
* One identified user message carrying a plugin notice.
|
|
342
|
-
* createUserMessage lives in a package this
|
|
343
|
-
* import, so the shape is
|
|
355
|
+
* One identified user message carrying a plugin notice.
|
|
356
|
+
* DSH's own createUserMessage lives in a package this
|
|
357
|
+
* preset deliberately does not import, so the shape is
|
|
358
|
+
* built here with node's uuid.
|
|
344
359
|
*/
|
|
345
360
|
function specNoticeMessage(text) {
|
|
346
361
|
return {
|
|
@@ -358,12 +373,10 @@ function specNoticeMessage(text) {
|
|
|
358
373
|
|
|
359
374
|
|
|
360
375
|
export function apply(ctx) {
|
|
361
|
-
// One cache entry per agent: { text, inflight, queued,
|
|
362
|
-
// `full`
|
|
363
|
-
//
|
|
364
|
-
//
|
|
365
|
-
// The WeakMap key is the live Agent object the events and the assembly
|
|
366
|
-
// context both carry.
|
|
376
|
+
// One cache entry per agent: { text, inflight, queued,
|
|
377
|
+
// full }. `full` picks the digest shape for the next
|
|
378
|
+
// refresh: true re-injects the contract block, false
|
|
379
|
+
// repeats the fingerprint. Key: the live Agent.
|
|
367
380
|
const states = new WeakMap();
|
|
368
381
|
|
|
369
382
|
const refresh = (agent, full = true) => {
|
|
@@ -378,9 +391,8 @@ export function apply(ctx) {
|
|
|
378
391
|
}
|
|
379
392
|
entry.full = full;
|
|
380
393
|
if (entry.inflight) {
|
|
381
|
-
// A refresh arrived
|
|
382
|
-
//
|
|
383
|
-
// on-disk state wins instead of being dropped.
|
|
394
|
+
// A refresh arrived mid-flight; re-run once after it
|
|
395
|
+
// settles so the latest on-disk state wins.
|
|
384
396
|
entry.queued = true;
|
|
385
397
|
return;
|
|
386
398
|
}
|
|
@@ -401,11 +413,15 @@ export function apply(ctx) {
|
|
|
401
413
|
});
|
|
402
414
|
};
|
|
403
415
|
|
|
416
|
+
// Session-start warm-up. 0.2.0 renamed the event to `agent/created`
|
|
417
|
+
// (payload `{ agent, source, signal? }` — same `payload.agent` shape), so
|
|
418
|
+
// both names are subscribed and each DSH line keeps a warm-up path.
|
|
404
419
|
ctx.on('agent/session-start', (payload) => refresh(payload && payload.agent, true));
|
|
420
|
+
ctx.on('agent/created', (payload) => refresh(payload && payload.agent, true));
|
|
405
421
|
ctx.on('agent/inbox/claimed', (payload) => refresh(payload && payload.agent, true));
|
|
406
|
-
// Intra-turn refresh: a lifecycle command just settled, so
|
|
407
|
-
//
|
|
408
|
-
//
|
|
422
|
+
// Intra-turn refresh: a lifecycle command just settled, so
|
|
423
|
+
// the next assembly sees the new state, not the stale
|
|
424
|
+
// block. Only the fingerprint line is repeated.
|
|
409
425
|
ctx.on('tools/result', (exec) => {
|
|
410
426
|
const agent = exec && exec.agent;
|
|
411
427
|
if (!agent || !isLifecycleCommand(exec && exec.arguments)) {
|
|
@@ -414,9 +430,9 @@ export function apply(ctx) {
|
|
|
414
430
|
refresh(agent, false);
|
|
415
431
|
});
|
|
416
432
|
|
|
417
|
-
// Editor-phase spec-check: the tool already ran, so this
|
|
418
|
-
// context for the next request and never blocks
|
|
419
|
-
//
|
|
433
|
+
// Editor-phase spec-check: the tool already ran, so this
|
|
434
|
+
// only adds context for the next request and never blocks
|
|
435
|
+
// the call. The waterfall awaits next() first.
|
|
420
436
|
ctx.on('tools/post-execute', async (exec, _result, next) => {
|
|
421
437
|
const downstream = await next();
|
|
422
438
|
const agent = exec && exec.agent;
|
package/presets/dsh/preset.yml
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
name: Cowork Flow
|
|
2
|
-
description: 接入 cowork-flow 流程框架的编码 Agent:任务门禁(task next)、计划绑定、runtime-context
|
|
2
|
+
description: 接入 cowork-flow 流程框架的编码 Agent:任务门禁(task next)、计划绑定、runtime-context 固定子代理;流程技能由项目级 `.agents/skills` 提供(brainstorming / task-planning / task-review / agent-dispatch 等)。
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cowork-flow",
|
|
3
|
+
"version": "1.7.2",
|
|
4
|
+
"description": "AI 协作工作流引擎:任务生命周期、子代理调度、门禁与状态注入。将 cowork-flow 的完整工作流带入 Kimi Code。",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "fishlikewater",
|
|
7
|
+
"email": "fishlikewater@126.com"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/fishlikewater/cowork-flow",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"ai-agent",
|
|
13
|
+
"workflow",
|
|
14
|
+
"task-lifecycle",
|
|
15
|
+
"subagents",
|
|
16
|
+
"codex",
|
|
17
|
+
"claude-code",
|
|
18
|
+
"opencode",
|
|
19
|
+
"zcode",
|
|
20
|
+
"qoder",
|
|
21
|
+
"kimi-code",
|
|
22
|
+
"dsh"
|
|
23
|
+
],
|
|
24
|
+
"skills": "./skills/",
|
|
25
|
+
"sessionStart": {
|
|
26
|
+
"skill": "cowork-flow-bootstrap"
|
|
27
|
+
},
|
|
28
|
+
"interface": {
|
|
29
|
+
"displayName": "Cowork Flow",
|
|
30
|
+
"shortDescription": "AI 协作工作流引擎:任务生命周期、子代理调度、门禁与状态注入。",
|
|
31
|
+
"longDescription": "cowork-flow 给 AI 编码宿主的 agent 提供可执行的工作流:任务生命周期、决策记录、规范契约与独立检查。安装后任务状态由运行时统一维护,多个宿主的会话从同一份事实起步。",
|
|
32
|
+
"developerName": "fishlikewater",
|
|
33
|
+
"websiteURL": "https://github.com/fishlikewater/cowork-flow"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
* exits 0 without spawning anything, so the user-level hook never
|
|
11
11
|
* touches unrelated repositories;
|
|
12
12
|
* 2. interpreter location (COWORK_FLOW_PYTHON → python3 → python → py -3);
|
|
13
|
-
* 3. stdin passthrough to inject.py, stdout
|
|
13
|
+
* 3. stdin passthrough to inject.py, stdout forwarded
|
|
14
|
+
* and a failing project runtime absorbed (exit 0,
|
|
15
|
+
* reason on stderr) — the hook row is machine-wide
|
|
16
|
+
* and may meet an older project copy.
|
|
14
17
|
*
|
|
15
18
|
* Fail-open by construction: a missing interpreter, a missing runtime, or a
|
|
16
19
|
* broken project copy all exit 0 with empty stdout instead of blocking the
|
|
@@ -103,7 +106,16 @@ function main() {
|
|
|
103
106
|
}
|
|
104
107
|
if (result.error || result.status === null) continue;
|
|
105
108
|
if (result.stdout) process.stdout.write(result.stdout);
|
|
106
|
-
|
|
109
|
+
// The interpreter ran; its exit status is the project runtime's verdict,
|
|
110
|
+
// not ours. Forwarding it would let a stale project copy abort the submit
|
|
111
|
+
// this hook only decorates.
|
|
112
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
113
|
+
if (result.status !== 0) {
|
|
114
|
+
process.stderr.write(
|
|
115
|
+
`cowork-flow kimi-code hook: inject.py exited ${result.status}; skipping injection\n`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
process.exit(0);
|
|
107
119
|
}
|
|
108
120
|
// No interpreter could run the entry: stay silent rather than break the
|
|
109
121
|
// user's submit (fail-open, same contract as the other host shims).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cowork-flow-bootstrap
|
|
3
|
+
description: Use only when a repository has no .cowork-flow/ runtime and the user wants to start or continue structured development work — guides installing cowork-flow into the project. Do not use when .cowork-flow/ exists; the project's own cowork-flow Skill owns that case.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Cowork Flow Bootstrap
|
|
7
|
+
|
|
8
|
+
This Skill is the plugin-side bootstrap. It applies to repositories that have no
|
|
9
|
+
`.cowork-flow/` runtime yet. Once `.cowork-flow/` exists, stop and use the
|
|
10
|
+
project's own Skills: after init, `./.cowork-flow/run task next` and the project's
|
|
11
|
+
skill copies are authoritative, not this Skill.
|
|
12
|
+
|
|
13
|
+
## Decide first
|
|
14
|
+
|
|
15
|
+
Check the repository root for the runtime directory `.cowork-flow/` (or the
|
|
16
|
+
workflow entry `./.cowork-flow/run`).
|
|
17
|
+
|
|
18
|
+
## No runtime: offer the install
|
|
19
|
+
|
|
20
|
+
Tell the user this repository has no cowork-flow runtime, then offer:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx cowork-flow project init .
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
After init the project carries its own Skills (`.agents/skills/`), the task
|
|
27
|
+
runtime (`.cowork-flow/`), and the workflow entry `./.cowork-flow/run task next`.
|
|
28
|
+
Resume the work through that entry.
|
|
29
|
+
|
|
30
|
+
## Never
|
|
31
|
+
|
|
32
|
+
- Do not reproduce lifecycle rules, gates, or a second workflow document here.
|
|
33
|
+
- Do not hand-create `.cowork-flow/` or copy files out of the plugin payload.
|
|
34
|
+
- Do not run task lifecycle commands: without a project runtime there is no task
|
|
35
|
+
to advance.
|