cowork-flow 1.9.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -1
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/docs/architecture.md +1 -34
- package/docs/direction.md +4 -3
- package/docs/hosts.md +14 -10
- package/docs/index.md +1 -1
- package/docs/release.md +1 -1
- package/package.json +6 -6
- package/presets/claude-code/.claude-plugin/plugin.json +1 -1
- package/presets/codex/.codex-plugin/plugin.json +1 -1
- package/presets/dsh/plugins/workflow-state.js +72 -24
- package/presets/kimi-code/.kimi-plugin/plugin.json +1 -1
- package/presets/kimi-code/hooks/cowork-flow-inject.mjs +28 -2
- package/presets/opencode/cowork-flow/plugin-core.js +70 -88
- package/presets/opencode/plugins/cowork-flow.js +7 -9
- package/presets/qoder/.qoder-plugin/plugin.json +1 -1
- package/presets/qoder/hooks/hooks.json +1 -1
- package/presets/qoder/hooks/inject-context.mjs +28 -2
- package/presets/zcode/.zcode-plugin/plugin.json +1 -1
- package/presets/zcode/hooks/inject-context.mjs +28 -3
- package/src/commands/host.js +23 -2
- package/src/commands/install-dsh-hook.js +1 -6
- package/src/commands/install-dsh-preset.js +1 -6
- package/src/commands/install-zcode-plugin.js +2 -3
- package/src/commands/registry.js +4 -2
- package/src/lib/asset-plan.js +1 -1
- package/src/lib/dsh-home.js +6 -0
- package/src/lib/plan-applier.js +1 -6
- package/template/.claude/agents/cowork-check.md +1 -1
- package/template/.claude/agents/cowork-implement.md +1 -1
- package/template/.claude/agents/cowork-research.md +1 -1
- package/template/.codex/agents/cowork-check.toml +1 -1
- package/template/.codex/agents/cowork-implement.toml +1 -1
- package/template/.codex/agents/cowork-research.toml +1 -1
- package/template/.codex/agents/default.toml +1 -1
- package/template/.codex/agents/explorer.toml +1 -1
- package/template/.codex/agents/worker.toml +1 -1
- package/template/.cowork-flow/.version +1 -1
- package/template/.cowork-flow/adapters/claude-code/adapter.yaml +1 -1
- package/template/.cowork-flow/adapters/codex/adapter.yaml +1 -1
- package/template/.cowork-flow/adapters/dsh/adapter.yaml +1 -1
- package/template/.cowork-flow/adapters/opencode/adapter.yaml +1 -1
- package/template/.cowork-flow/adapters/qoder/adapter.yaml +1 -1
- package/template/.cowork-flow/run +2 -2
- package/template/.cowork-flow/run.cmd +3 -3
- package/template/.cowork-flow/scripts/adapters/cli/task_lifecycle_commands.py +0 -1
- package/template/.cowork-flow/scripts/adapters/cli/task_parser.py +0 -7
- package/template/.cowork-flow/scripts/adapters/host/claude_code_policy.py +6 -4
- package/template/.cowork-flow/scripts/adapters/host/codex_policy.py +8 -6
- package/template/.cowork-flow/scripts/adapters/host/edit_scope.py +113 -0
- package/template/.cowork-flow/scripts/adapters/host/opencode_policy.py +12 -0
- package/template/.cowork-flow/scripts/adapters/host/qoder_policy.py +7 -5
- package/template/.cowork-flow/scripts/adapters/host/workflow_state_hook.py +0 -91
- package/template/.cowork-flow/scripts/adapters/host/zcode_policy.py +15 -77
- package/template/.cowork-flow/scripts/run.py +0 -2
- package/template/.cowork-flow/scripts/services/context_paths.py +2 -2
- package/template/.cowork-flow/scripts/services/fact_view.py +49 -21
- package/template/.cowork-flow/scripts/services/lifecycle_checks.py +1 -216
- package/template/.cowork-flow/scripts/services/lifecycle_policy.py +0 -5
- package/template/.cowork-flow/scripts/services/task_gate_profiles.py +1 -1
- package/template/.cowork-flow/scripts/services/task_lifecycle.py +14 -31
- package/template/.cowork-flow/spec/backend/error-handling.md +0 -14
- package/template/.cowork-flow/spec/contracts/context-injection.md +53 -26
- package/template/.cowork-flow/spec/contracts/decision-anchor.md +3 -3
- package/template/.cowork-flow/spec/contracts/error-codes.md +1 -3
- package/template/.cowork-flow/spec/contracts/evidence-completion.md +6 -8
- package/template/.cowork-flow/spec/contracts/index.md +0 -1
- package/template/.cowork-flow/spec/contracts/plan-binding.md +3 -2
- package/template/.cowork-flow/spec/contracts/subagent-dispatch.md +6 -21
- package/template/.cowork-flow/spec/contracts/task-adopt.md +1 -1
- package/template/.cowork-flow/spec/runtime/contract-registry.json +0 -13
- package/template/.cowork-flow/spec/runtime/host-assets.json +10 -7
- package/template/.cowork-flow/spec/runtime/scope-rules.json +2 -2
- package/template/.kimi-code/agents/cowork-check.md +1 -1
- package/template/.kimi-code/agents/cowork-implement.md +1 -1
- package/template/.kimi-code/agents/cowork-research.md +1 -1
- package/template/.opencode/agents/cowork-check.md +1 -1
- package/template/.opencode/agents/cowork-implement.md +1 -1
- package/template/.opencode/agents/cowork-research.md +1 -1
- package/template/.opencode/cowork-flow/plugin-core.js +70 -88
- package/template/.opencode/plugins/cowork-flow.js +7 -9
- package/template/AGENTS.md +0 -1
- package/template/skills/agent-dispatch/SKILL.md +0 -15
- package/template/skills/runtime-health/scripts/doctor.py +83 -58
- package/template/skills/spec-sync/SKILL.md +0 -1
- package/template/skills/task-planning/SKILL.md +1 -1
- package/template/skills/task-review/SKILL.md +3 -3
- package/template/.cowork-flow/scripts/adapters/cli/spec_check.py +0 -178
- package/template/.cowork-flow/scripts/services/spec_check.py +0 -573
- package/template/.cowork-flow/spec/contracts/spec-checks.md +0 -281
|
@@ -52,7 +52,7 @@ translated heading would simply stop being read.
|
|
|
52
52
|
fact view (`services/fact_view.parse_decision_anchor`). Line-format conventions:
|
|
53
53
|
|
|
54
54
|
- `## 验证命令`: one command per line (an optional `- ` prefix), truncated to 120
|
|
55
|
-
characters, at most
|
|
55
|
+
characters, at most 10 lines; the implementing agent runs them itself
|
|
56
56
|
(self-declared, self-checked) and the reviewer verifies the execution evidence.
|
|
57
57
|
- `## 范围边界`: only the first non-empty line is taken, truncated to 160
|
|
58
58
|
characters.
|
|
@@ -110,14 +110,14 @@ After merging (5 files): `decision-anchor.md` + `implement.jsonl` + `check.jsonl
|
|
|
110
110
|
component.
|
|
111
111
|
A **consequence** (expected cost, not a defect): **any byte change in
|
|
112
112
|
`implement.jsonl`** (appending or removing a scope entry, rewording a `reason`,
|
|
113
|
-
a domain-guide entry
|
|
113
|
+
a domain-guide entry) **or a rewrite of a
|
|
114
114
|
referenced spec's prose invalidates an existing receipt**, requiring one more
|
|
115
115
|
review run against the final state. Appending evidence, ticking ACs, and runtime
|
|
116
116
|
metadata are not in that set.
|
|
117
117
|
- **Light profile `meta.taskType: "tiny"`**: applies to `strict` tasks only
|
|
118
118
|
(`legacy` + `tiny` stays a compatibility path). A light task **is still strict**
|
|
119
119
|
— what it waives are three things: the review receipt, AC evidence, and
|
|
120
|
-
`dev_type`. It does **not** waive
|
|
120
|
+
`dev_type`. It does **not** waive the file-scope gate or "AC rows
|
|
121
121
|
are non-empty" (`LIFECYCLE-AC-003`/`004` still apply). Plan-file binding is not
|
|
122
122
|
among the three, but the pre-existing field-level exemption for tiny applies, so
|
|
123
123
|
a light task does not require `planFile` either. `tiny` **can only be declared at
|
|
@@ -44,6 +44,4 @@ to them:
|
|
|
44
44
|
## Exemptions
|
|
45
45
|
|
|
46
46
|
`LIFECYCLE-AC-002` is the only code in this set a flag can waive
|
|
47
|
-
(`--allow-missing-evidence`)
|
|
48
|
-
declarations, not a `LIFECYCLE-*` code. Details are in `evidence-completion.md` and
|
|
49
|
-
`spec-checks.md`.
|
|
47
|
+
(`--allow-missing-evidence`). Details are in `evidence-completion.md`.
|
|
@@ -40,7 +40,7 @@ direct lifecycle checks:
|
|
|
40
40
|
| `LIFECYCLE-AC-001` | an unchecked AC row exists | none — do the work, or tick the completed rows |
|
|
41
41
|
| `LIFECYCLE-AC-002` | a declared AC has no valid evidence record | `--allow-missing-evidence` (recorded in task.json `meta.evidenceExempt`, with `at`, `flag`, `by`, and the `missing` detail list) |
|
|
42
42
|
| `LIFECYCLE-AC-003` | `gateProfile=strict` and decision-anchor yields no AC row | none — a strict task was already required to declare ACs at start, so reading none at completion has only three explanations: the anchor was rewritten, the task was created wrongly, or the anchor is undecodable (corrupted encoding) |
|
|
43
|
-
| `LIFECYCLE-AC-004` | `gateProfile=strict` and the ac_evidence module cannot be imported | none —
|
|
43
|
+
| `LIFECYCLE-AC-004` | `gateProfile=strict` and the ac_evidence module cannot be imported | none — a broken module is not "nothing to check"; the message carries the original exception, and you rerun once the runtime install is fixed |
|
|
44
44
|
|
|
45
45
|
- The first two codes can appear together; `--allow-missing-evidence` waives
|
|
46
46
|
`LIFECYCLE-AC-002` only, never `003`/`004`.
|
|
@@ -51,8 +51,8 @@ direct lifecycle checks:
|
|
|
51
51
|
- **Strict path**: a `gateProfile=strict` task re-verifies AC existence and
|
|
52
52
|
evidence-module availability at completion — the start-time AC declaration
|
|
53
53
|
requirement does not lapse because completion cannot read it.
|
|
54
|
-
- The `meta.evidenceExempt` trace
|
|
55
|
-
|
|
54
|
+
- The `meta.evidenceExempt` trace does not accumulate: retries leave only the
|
|
55
|
+
latest completion result.
|
|
56
56
|
- **An exemption record must name which items were waived and who approved it**:
|
|
57
57
|
`meta.evidenceExempt` = `{"at", "flag": "--allow-missing-evidence",
|
|
58
58
|
"by": <actor or null>, "missing": [<AC id>…]}`. `by` is the resolved session
|
|
@@ -60,9 +60,7 @@ direct lifecycle checks:
|
|
|
60
60
|
`creator`/`assignee`. Both the completion output and the archive output print
|
|
61
61
|
this record item by item (the completion output reads the `task.json` just
|
|
62
62
|
written), so "what was recorded" and "what was printed" cannot tell two different
|
|
63
|
-
stories; with no exemption, no exemption rows are printed at all.
|
|
64
|
-
matches the `meta.specCheckExempt` detail rules on the spec-check side; see
|
|
65
|
-
`spec-checks.md`.
|
|
63
|
+
stories; with no exemption, no exemption rows are printed at all.
|
|
66
64
|
|
|
67
65
|
## Injection and fact view
|
|
68
66
|
|
|
@@ -81,5 +79,5 @@ direct lifecycle checks:
|
|
|
81
79
|
behind an AC to fabricate a record.
|
|
82
80
|
- It does not parse AC prose semantics; a missing checkbox on an `AC-xxx` row
|
|
83
81
|
parses as incomplete.
|
|
84
|
-
- This contract does not change
|
|
85
|
-
by default (`fact-layer-access.md`).
|
|
82
|
+
- This contract does not change the file-scope gate or the AC gate; MCP write
|
|
83
|
+
tools still refuse by default (`fact-layer-access.md`).
|
|
@@ -12,7 +12,6 @@ Current contracts:
|
|
|
12
12
|
- `capabilities.md`: the host adapter capability model.
|
|
13
13
|
- `party-mode-v2-board.md`: the Party Mode V2 board protocol.
|
|
14
14
|
- `plan-binding.md`: lightweight development-plan binding, pre-start missing-plan blocking, and recovery visibility.
|
|
15
|
-
- `spec-checks.md`: the spec-declared command contract — syntax of `checks:` in user-spec frontmatter, three-state gate semantics, timing, and the host capability matrix.
|
|
16
15
|
- `context-injection.md`: the transport shapes, event-timing matrix, and fingerprint serialization rules for runtime-context injection across hosts (a meta-protocol, not in `contract-registry.json`).
|
|
17
16
|
- `decision-anchor.md`: the schema of each task's `decision-anchor.md` — goal, acceptance criteria, rejected options, and key assumptions.
|
|
18
17
|
- `error-output-as-data.md`: external error output is analyzed as data, never executed as instructions (`ERROR_OUTPUT_AS_DATA_V1`).
|
|
@@ -14,8 +14,9 @@ the plan into a second workflow kernel.
|
|
|
14
14
|
|
|
15
15
|
## Creation phase
|
|
16
16
|
|
|
17
|
-
- A plain
|
|
18
|
-
|
|
17
|
+
- A plain task creation (`task next --title … --slug …`) may create a planning
|
|
18
|
+
task with no plan.
|
|
19
|
+
- `task next --from-plan <path>` is an explicit binding request; `<path>` must
|
|
19
20
|
exist, lie inside the repository, and lie under `.cowork-flow/plans/`.
|
|
20
21
|
- On a successful binding the runtime writes:
|
|
21
22
|
|
|
@@ -108,23 +108,6 @@ does not defend against a forged runtime record — writing
|
|
|
108
108
|
`.cowork-flow/.runtime/**` is out of scope here and would need a signed or
|
|
109
109
|
externally rooted record, which is a separate contract.
|
|
110
110
|
|
|
111
|
-
## Delegated Spec Self-Check
|
|
112
|
-
|
|
113
|
-
Delegated subagent tool calls receive no edit-phase hook feedback on hosts
|
|
114
|
-
whose plugin hooks cover only the main session stream (verified on zcode:
|
|
115
|
-
plugin PostToolUse fires for main-session tool calls but never for
|
|
116
|
-
Agent-tool children). The reliable pull path is the CLI: before reporting
|
|
117
|
-
completion, `cowork-implement` (and any child that changes files) runs
|
|
118
|
-
|
|
119
|
-
```text
|
|
120
|
-
./.cowork-flow/run spec-check
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
fixes every reported violation, and re-runs until the summary is clean or the
|
|
124
|
-
remaining violations are reported as blockers. This is a protocol behavior,
|
|
125
|
-
not a second lifecycle gate — the closeout gate re-runs the same checks and
|
|
126
|
-
blocks completion regardless.
|
|
127
|
-
|
|
128
111
|
## Return Acceptance And Closeout
|
|
129
112
|
|
|
130
113
|
- Converge on the child result before acceptance — via the wait primitive or
|
|
@@ -133,10 +116,12 @@ blocks completion regardless.
|
|
|
133
116
|
- Confirm no stray running children with the adapter list primitive.
|
|
134
117
|
- Verify the child report by checking files, commands, and results; do not
|
|
135
118
|
trust completed text alone.
|
|
136
|
-
- Before accepting delegated output,
|
|
137
|
-
the
|
|
138
|
-
|
|
139
|
-
|
|
119
|
+
- Before accepting delegated output, verify the child's changed paths against
|
|
120
|
+
the task scope and the applicable `spec/` requirements from the diff
|
|
121
|
+
(`git diff --stat` / `task next <dir> --validate`): child sessions receive no
|
|
122
|
+
edit-phase hook feedback on hosts whose plugin hooks cover only the main
|
|
123
|
+
session stream, so acceptance-time review is the earliest reliable checkpoint
|
|
124
|
+
for delegated work.
|
|
140
125
|
- After completion or failure, close the child with the adapter cancel/close
|
|
141
126
|
primitive and clean the runtime context sessions.
|
|
142
127
|
- The child remains a leaf executor and must not dispatch, wait for, list, or
|
|
@@ -74,7 +74,7 @@ that does not advance status); `check_result` is `null` (no check is exempted).
|
|
|
74
74
|
## Session binding
|
|
75
75
|
|
|
76
76
|
On successful adoption the CLI binds the adopter's session to that task (the same
|
|
77
|
-
shape as a rebind by `task next <dir>` or the binding
|
|
77
|
+
shape as a rebind by `task next <dir>` or the binding at task creation), so a
|
|
78
78
|
later `task next <dir> --run` can omit the directory and continue the
|
|
79
79
|
review/complete/archive chain.
|
|
80
80
|
|
|
@@ -115,19 +115,6 @@
|
|
|
115
115
|
"when changing task start readiness",
|
|
116
116
|
"when changing resume context for active tasks"
|
|
117
117
|
]
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
"id": "SPEC_CHECKS_V1",
|
|
121
|
-
"path": ".cowork-flow/spec/contracts/spec-checks.md",
|
|
122
|
-
"digest": [
|
|
123
|
-
"Specs may declare `checks:` commands in frontmatter; the mechanism runs declarations, never prose.",
|
|
124
|
-
"A declared command that cannot be found counts as unchecked and blocks completion."
|
|
125
|
-
],
|
|
126
|
-
"readWhen": [
|
|
127
|
-
"before adding or editing spec files",
|
|
128
|
-
"when a spec clause can be judged by a command exit code",
|
|
129
|
-
"when spec-check reports violations or unchecked"
|
|
130
|
-
]
|
|
131
118
|
}
|
|
132
119
|
],
|
|
133
120
|
"references": [
|
|
@@ -155,7 +155,7 @@
|
|
|
155
155
|
"runtimeContextDispatch": "native",
|
|
156
156
|
"runtimeContextBinding": "shim",
|
|
157
157
|
"runtimeContextCleanup": "native",
|
|
158
|
-
"editScopeWarning": "
|
|
158
|
+
"editScopeWarning": "plugin"
|
|
159
159
|
},
|
|
160
160
|
"commandTargets": [
|
|
161
161
|
{
|
|
@@ -204,7 +204,7 @@
|
|
|
204
204
|
"runtimeContextDispatch": "native",
|
|
205
205
|
"runtimeContextBinding": "shim",
|
|
206
206
|
"runtimeContextCleanup": "native",
|
|
207
|
-
"editScopeWarning": "
|
|
207
|
+
"editScopeWarning": "plugin"
|
|
208
208
|
},
|
|
209
209
|
"commandTargets": [],
|
|
210
210
|
"payload": {
|
|
@@ -251,7 +251,7 @@
|
|
|
251
251
|
"runtimeContextDispatch": "native",
|
|
252
252
|
"runtimeContextBinding": "shim",
|
|
253
253
|
"runtimeContextCleanup": "native",
|
|
254
|
-
"editScopeWarning": "
|
|
254
|
+
"editScopeWarning": "plugin"
|
|
255
255
|
},
|
|
256
256
|
"commandTargets": [
|
|
257
257
|
{
|
|
@@ -300,7 +300,7 @@
|
|
|
300
300
|
"runtimeContextDispatch": "native",
|
|
301
301
|
"runtimeContextBinding": "shim",
|
|
302
302
|
"runtimeContextCleanup": "shim",
|
|
303
|
-
"editScopeWarning": "
|
|
303
|
+
"editScopeWarning": "plugin"
|
|
304
304
|
},
|
|
305
305
|
"commandTargets": [],
|
|
306
306
|
"payload": null
|
|
@@ -431,7 +431,7 @@
|
|
|
431
431
|
"runtimeContextDispatch": "native",
|
|
432
432
|
"runtimeContextBinding": "shim",
|
|
433
433
|
"runtimeContextCleanup": "native",
|
|
434
|
-
"editScopeWarning": "
|
|
434
|
+
"editScopeWarning": "plugin"
|
|
435
435
|
},
|
|
436
436
|
"commandTargets": [],
|
|
437
437
|
"payload": {
|
|
@@ -460,8 +460,8 @@
|
|
|
460
460
|
".cowork-flow/.version",
|
|
461
461
|
".cowork-flow/run",
|
|
462
462
|
".cowork-flow/run.cmd",
|
|
463
|
-
".cowork-flow/spec/contracts/
|
|
464
|
-
".cowork-flow/spec/
|
|
463
|
+
".cowork-flow/spec/contracts/workflow-state-templates.md",
|
|
464
|
+
".cowork-flow/spec/runtime/scope-rules.json"
|
|
465
465
|
],
|
|
466
466
|
"safePrefixes": [
|
|
467
467
|
".cowork-flow/"
|
|
@@ -515,6 +515,7 @@
|
|
|
515
515
|
".cowork-flow/scripts/application",
|
|
516
516
|
".cowork-flow/scripts/services/batch_execution.py",
|
|
517
517
|
".cowork-flow/scripts/services/decision_review.py",
|
|
518
|
+
".cowork-flow/scripts/services/spec_check.py",
|
|
518
519
|
".cowork-flow/scripts/commands",
|
|
519
520
|
".cowork-flow/scripts/common",
|
|
520
521
|
".cowork-flow/scripts/common/__init__.py",
|
|
@@ -567,6 +568,7 @@
|
|
|
567
568
|
".cowork-flow/scripts/subagent.py",
|
|
568
569
|
".cowork-flow/scripts/task.py",
|
|
569
570
|
".cowork-flow/spec/contracts/entry-contract.md",
|
|
571
|
+
".cowork-flow/spec/contracts/spec-checks.md",
|
|
570
572
|
".cowork-flow/spec/runtime/rules.json",
|
|
571
573
|
".cowork-flow/spec/schemas/rules.schema.json",
|
|
572
574
|
".cowork-flow/spec/runtime/skill-registry.json",
|
|
@@ -575,6 +577,7 @@
|
|
|
575
577
|
".cowork-flow/scripts/adapters/cli/add_session.py",
|
|
576
578
|
".cowork-flow/workspace/index.md",
|
|
577
579
|
".cowork-flow/scripts/adapters/cli/change.py",
|
|
580
|
+
".cowork-flow/scripts/adapters/cli/spec_check.py",
|
|
578
581
|
".cowork-flow/changes/",
|
|
579
582
|
".cowork-flow/skills/adversarial-review",
|
|
580
583
|
".cowork-flow/skills/agent-dispatch",
|
|
@@ -24,6 +24,6 @@ Execution:
|
|
|
24
24
|
Rules:
|
|
25
25
|
|
|
26
26
|
- Do not invoke subagents.
|
|
27
|
-
- Do not run task
|
|
27
|
+
- Do not run task lifecycle transitions, unscoped resume, commit, or push.
|
|
28
28
|
- Treat backend/frontend natural-language specs as checklist context, not dynamic hard validators.
|
|
29
29
|
- Do not accept shallow tests that would still pass when target behavior breaks.
|
|
@@ -25,6 +25,6 @@ Execution:
|
|
|
25
25
|
Rules:
|
|
26
26
|
|
|
27
27
|
- Do not invoke subagents.
|
|
28
|
-
- Do not run task
|
|
28
|
+
- Do not run task lifecycle transitions, unscoped resume, commit, or push.
|
|
29
29
|
- Do not create task-local review artifacts, `tdd.jsonl`, or TDD evidence records in `check.jsonl`.
|
|
30
30
|
- Do not claim completion without current verification.
|
|
@@ -28,6 +28,6 @@ Execution:
|
|
|
28
28
|
Rules:
|
|
29
29
|
|
|
30
30
|
- Do not use the `task` tool or invoke subagents.
|
|
31
|
-
- Do not run task
|
|
31
|
+
- Do not run task lifecycle transitions, unscoped resume, commit, or push.
|
|
32
32
|
- Treat backend/frontend natural-language specs as checklist context, not dynamic hard validators.
|
|
33
33
|
- Do not accept shallow tests that would still pass when target behavior breaks.
|
|
@@ -29,6 +29,6 @@ Execution:
|
|
|
29
29
|
Rules:
|
|
30
30
|
|
|
31
31
|
- Do not use the `task` tool or invoke subagents.
|
|
32
|
-
- Do not run task
|
|
32
|
+
- Do not run task lifecycle transitions, unscoped resume, commit, or push.
|
|
33
33
|
- Do not create task-local review artifacts, `tdd.jsonl`, or TDD evidence records in `check.jsonl`.
|
|
34
34
|
- Do not claim completion without current verification.
|
|
@@ -26,4 +26,4 @@ Rules:
|
|
|
26
26
|
|
|
27
27
|
- Do not use the `task` tool or invoke subagents.
|
|
28
28
|
- Do not edit code, specs, task state, or git state.
|
|
29
|
-
- Do not run task
|
|
29
|
+
- Do not run task lifecycle transitions, unscoped resume, commit, or push.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// Transport shim behind the opencode plugin. Every workflow fact
|
|
2
2
|
// (contract digest, workflow-state, decision anchor, stage contract,
|
|
3
|
-
// runtime-context bind) is rendered by the Python
|
|
4
|
-
// <scripts>/adapters/host/inject.py --host opencode; this module
|
|
5
|
-
// bytes only. What stays here is what the Python side cannot
|
|
6
|
-
// the config-hook skills registration, the shell.env context-key
|
|
7
|
-
// export, and the
|
|
8
|
-
//
|
|
3
|
+
// runtime-context bind, edit-scope warning) is rendered by the Python
|
|
4
|
+
// source <scripts>/adapters/host/inject.py --host opencode; this module
|
|
5
|
+
// moves bytes only. What stays here is what the Python side cannot
|
|
6
|
+
// reach: the config-hook skills registration, the shell.env context-key
|
|
7
|
+
// export, and the tool-shape translation for the tool.execute.after
|
|
8
|
+
// edit-scope delegation.
|
|
9
9
|
//
|
|
10
10
|
// This module must NOT live under plugins/: opencode
|
|
11
11
|
// treats every file directly inside plugins/ as a plugin
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// install, so the adapter's relative import resolves in
|
|
17
17
|
// both.
|
|
18
18
|
|
|
19
|
-
import { spawn } from "node:child_process"
|
|
19
|
+
import { spawn, spawnSync } from "node:child_process"
|
|
20
20
|
import { existsSync } from "node:fs"
|
|
21
21
|
import { dirname, resolve } from "node:path"
|
|
22
22
|
import { fileURLToPath } from "node:url"
|
|
@@ -24,6 +24,27 @@ import { fileURLToPath } from "node:url"
|
|
|
24
24
|
const DIR_WORKFLOW = ".cowork-flow"
|
|
25
25
|
// One prompt assembly must not wait longer than a host hook event.
|
|
26
26
|
const INJECT_TIMEOUT_MS = 4800
|
|
27
|
+
// The .cowork-flow/run floor, reused here: a candidate that cannot run
|
|
28
|
+
// Python 3.10+ is skipped like a missing binary, so a stale `python3`
|
|
29
|
+
// never hides a working `python` further down the list.
|
|
30
|
+
const MIN_PYTHON_CHECK =
|
|
31
|
+
"import sys; raise SystemExit(0 if sys.version_info >= (3, 10) else 1)"
|
|
32
|
+
|
|
33
|
+
function interpreterIsUsable(candidate) {
|
|
34
|
+
try {
|
|
35
|
+
const probe = spawnSync(candidate[0], [...candidate.slice(1), "-c", MIN_PYTHON_CHECK], {
|
|
36
|
+
timeout: INJECT_TIMEOUT_MS,
|
|
37
|
+
windowsHide: true,
|
|
38
|
+
})
|
|
39
|
+
// A probe killed by its own timeout reached no verdict — a loaded machine,
|
|
40
|
+
// not a bad interpreter — so let the real spawn decide; it carries the
|
|
41
|
+
// same timeout and its own diagnostics.
|
|
42
|
+
if (probe.error && probe.error.code === "ETIMEDOUT") return true
|
|
43
|
+
return !probe.error && probe.status === 0
|
|
44
|
+
} catch {
|
|
45
|
+
return false
|
|
46
|
+
}
|
|
47
|
+
}
|
|
27
48
|
|
|
28
49
|
// Last-resort repository root: this module lives at
|
|
29
50
|
// <payload>/cowork-flow/plugin-core.js, next to plugins/,
|
|
@@ -175,8 +196,9 @@ function runInject(candidate, script, payload) {
|
|
|
175
196
|
// inject.py and hand back whatever it printed. The payload spreads the
|
|
176
197
|
// host input so every session/runtime-context key the Python identity
|
|
177
198
|
// table knows arrives unchanged; hook_event_name and cwd are the two
|
|
178
|
-
// fields the hook contract requires.
|
|
179
|
-
|
|
199
|
+
// fields the hook contract requires. `overrides` swaps the event name
|
|
200
|
+
// and adds the tool fields the PostToolUse contract needs.
|
|
201
|
+
async function renderViaInject(input, overrides = null) {
|
|
180
202
|
if (
|
|
181
203
|
process.env.COWORK_FLOW_HOOKS === "0" ||
|
|
182
204
|
process.env.COWORK_FLOW_DISABLE_HOOKS === "1"
|
|
@@ -188,21 +210,57 @@ async function renderViaInject(input) {
|
|
|
188
210
|
if (!script) {
|
|
189
211
|
return ""
|
|
190
212
|
}
|
|
213
|
+
const extra = overrides && typeof overrides === "object" ? overrides : {}
|
|
191
214
|
const payload = {
|
|
192
215
|
...(input && typeof input === "object" ? input : {}),
|
|
193
|
-
|
|
216
|
+
...extra,
|
|
217
|
+
hook_event_name: extra.hook_event_name || "UserPromptSubmit",
|
|
194
218
|
cwd: inputCwd(input) || (root === pluginRoot ? null : root) || process.cwd(),
|
|
195
219
|
}
|
|
196
220
|
for (const candidate of pythonCandidates()) {
|
|
221
|
+
if (!interpreterIsUsable(candidate)) {
|
|
222
|
+
continue
|
|
223
|
+
}
|
|
197
224
|
const result = await runInject(candidate, script, payload)
|
|
198
225
|
if (!result.ran) {
|
|
199
226
|
continue
|
|
200
227
|
}
|
|
201
228
|
return result.stdout.trim()
|
|
202
229
|
}
|
|
230
|
+
console.error(
|
|
231
|
+
"cowork-flow opencode plugin: no Python 3.10+ interpreter found; skipping injection"
|
|
232
|
+
)
|
|
203
233
|
return ""
|
|
204
234
|
}
|
|
205
235
|
|
|
236
|
+
// Editor-phase scope warning: opencode sessions get the same
|
|
237
|
+
// single-line out-of-scope advisory the other hosts receive on
|
|
238
|
+
// PostToolUse. Best-effort — a missing runtime, a timeout, or any
|
|
239
|
+
// error stays silent and never blocks the edit that already happened.
|
|
240
|
+
const EDIT_TOOL_NAMES = new Set(["edit", "write"])
|
|
241
|
+
|
|
242
|
+
function editedFilePath(input) {
|
|
243
|
+
const tool = typeof input?.tool === "string" ? input.tool : ""
|
|
244
|
+
if (!EDIT_TOOL_NAMES.has(tool)) {
|
|
245
|
+
return ""
|
|
246
|
+
}
|
|
247
|
+
const args = input?.args
|
|
248
|
+
const candidate = args && typeof args === "object" ? args.filePath : null
|
|
249
|
+
return typeof candidate === "string" && candidate.trim() ? candidate.trim() : ""
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async function renderEditScopeWarning(input) {
|
|
253
|
+
const filePath = editedFilePath(input)
|
|
254
|
+
if (!filePath) {
|
|
255
|
+
return ""
|
|
256
|
+
}
|
|
257
|
+
return renderViaInject(input, {
|
|
258
|
+
hook_event_name: "PostToolUse",
|
|
259
|
+
tool_name: typeof input?.tool === "string" ? input.tool : "edit",
|
|
260
|
+
tool_input: { file_path: filePath },
|
|
261
|
+
})
|
|
262
|
+
}
|
|
263
|
+
|
|
206
264
|
function sanitize(value) {
|
|
207
265
|
return String(value ?? "")
|
|
208
266
|
.trim()
|
|
@@ -262,82 +320,6 @@ function injectShellEnv(input, output) {
|
|
|
262
320
|
}
|
|
263
321
|
}
|
|
264
322
|
|
|
265
|
-
// Editor-phase spec-check: opencode sessions get the same
|
|
266
|
-
// single-line violation hint zcode and claude-code
|
|
267
|
-
// receive. Best-effort — a missing runtime, a timeout, or
|
|
268
|
-
// any error stays silent and never blocks the edit that
|
|
269
|
-
// already happened.
|
|
270
|
-
const EDIT_TOOL_NAMES = new Set(["edit", "write"])
|
|
271
|
-
const EDIT_CHECK_TIMEOUT_MS = 2500
|
|
272
|
-
|
|
273
|
-
function editedFilePath(input) {
|
|
274
|
-
const tool = typeof input?.tool === "string" ? input.tool : ""
|
|
275
|
-
if (!EDIT_TOOL_NAMES.has(tool)) {
|
|
276
|
-
return ""
|
|
277
|
-
}
|
|
278
|
-
const args = input?.args
|
|
279
|
-
const candidate = args && typeof args === "object" ? args.filePath : null
|
|
280
|
-
return typeof candidate === "string" && candidate.trim() ? candidate.trim() : ""
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function runEditSpecCheck(root, filePath) {
|
|
284
|
-
return new Promise((resolvePromise) => {
|
|
285
|
-
let settled = false
|
|
286
|
-
const finish = (value) => {
|
|
287
|
-
if (!settled) {
|
|
288
|
-
settled = true
|
|
289
|
-
resolvePromise(value)
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
const entry = resolve(
|
|
293
|
-
root,
|
|
294
|
-
DIR_WORKFLOW,
|
|
295
|
-
process.platform === "win32" ? "run.cmd" : "run"
|
|
296
|
-
)
|
|
297
|
-
if (!existsSync(entry)) {
|
|
298
|
-
finish("")
|
|
299
|
-
return
|
|
300
|
-
}
|
|
301
|
-
let child
|
|
302
|
-
try {
|
|
303
|
-
child = spawn(
|
|
304
|
-
entry,
|
|
305
|
-
["spec-check", "--phase", "edit", "--file", filePath, "--throttled"],
|
|
306
|
-
{
|
|
307
|
-
cwd: root,
|
|
308
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
309
|
-
shell: process.platform === "win32",
|
|
310
|
-
}
|
|
311
|
-
)
|
|
312
|
-
} catch {
|
|
313
|
-
finish("")
|
|
314
|
-
return
|
|
315
|
-
}
|
|
316
|
-
const timer = setTimeout(() => {
|
|
317
|
-
try {
|
|
318
|
-
child.kill()
|
|
319
|
-
} catch {
|
|
320
|
-
// the edit must not be held up by a stuck checker
|
|
321
|
-
}
|
|
322
|
-
finish("")
|
|
323
|
-
}, EDIT_CHECK_TIMEOUT_MS)
|
|
324
|
-
let stdout = ""
|
|
325
|
-
if (child.stdout) {
|
|
326
|
-
child.stdout.on("data", (chunk) => {
|
|
327
|
-
stdout += String(chunk)
|
|
328
|
-
})
|
|
329
|
-
}
|
|
330
|
-
child.on("error", () => {
|
|
331
|
-
clearTimeout(timer)
|
|
332
|
-
finish("")
|
|
333
|
-
})
|
|
334
|
-
child.on("close", () => {
|
|
335
|
-
clearTimeout(timer)
|
|
336
|
-
finish(stdout.trim())
|
|
337
|
-
})
|
|
338
|
-
})
|
|
339
|
-
}
|
|
340
|
-
|
|
341
323
|
// The payload's own skills directory, located from this
|
|
342
324
|
// module's file path and not from the repository root: the
|
|
343
325
|
// machine install puts this file in
|
|
@@ -382,11 +364,11 @@ export function registerPayloadSkills(config) {
|
|
|
382
364
|
|
|
383
365
|
// The plugin adapter imports the hook helpers; these are
|
|
384
366
|
// the unit-test surface (transport, fail-open, shell env,
|
|
385
|
-
// edit-
|
|
367
|
+
// edit-scope delegation). opencode never reads this module.
|
|
386
368
|
export {
|
|
387
369
|
renderViaInject,
|
|
388
370
|
editedFilePath,
|
|
371
|
+
renderEditScopeWarning,
|
|
389
372
|
findRepoRoot,
|
|
390
373
|
injectShellEnv,
|
|
391
|
-
runEditSpecCheck,
|
|
392
374
|
}
|
|
@@ -7,12 +7,10 @@
|
|
|
7
7
|
// re-exported helper would be called as a plugin and break
|
|
8
8
|
// host bootstrap.
|
|
9
9
|
import {
|
|
10
|
-
editedFilePath,
|
|
11
|
-
findRepoRoot,
|
|
12
10
|
injectShellEnv,
|
|
13
11
|
registerPayloadSkills,
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
renderEditScopeWarning,
|
|
13
|
+
renderViaInject
|
|
16
14
|
} from "../cowork-flow/plugin-core.js"
|
|
17
15
|
|
|
18
16
|
export const CoworkFlowPlugin = async () => {
|
|
@@ -37,12 +35,12 @@ export const CoworkFlowPlugin = async () => {
|
|
|
37
35
|
output.system.push(text)
|
|
38
36
|
}
|
|
39
37
|
},
|
|
38
|
+
// Editor-phase scope advisory: the tool already ran, so
|
|
39
|
+
// the single out-of-scope line is appended to its output
|
|
40
|
+
// and the call is never blocked. Non-edit tools and a
|
|
41
|
+
// missing/failing runtime stay silent.
|
|
40
42
|
"tool.execute.after": async (input, output) => {
|
|
41
|
-
const
|
|
42
|
-
if (!filePath) {
|
|
43
|
-
return
|
|
44
|
-
}
|
|
45
|
-
const warning = await runEditSpecCheck(findRepoRoot(input), filePath)
|
|
43
|
+
const warning = await renderEditScopeWarning(input)
|
|
46
44
|
if (warning) {
|
|
47
45
|
output.output = [output.output, warning].filter(Boolean).join("\n")
|
|
48
46
|
}
|
package/template/AGENTS.md
CHANGED
|
@@ -144,5 +144,4 @@
|
|
|
144
144
|
|
|
145
145
|
<!-- COWORK-FLOW:START -->
|
|
146
146
|
项目流程以 `task next`、runtime gate、Skill command manifests 和 `.cowork-flow/spec/` 硬契约为准;不要维护第二套流程文档或 Skill 注册控制面。
|
|
147
|
-
规范声明的检查以 `.cowork-flow/spec/contracts/spec-checks.md` 为准;交付或收口前运行 `./.cowork-flow/run spec-check`。
|
|
148
147
|
<!-- COWORK-FLOW:END -->
|
|
@@ -30,21 +30,6 @@ Use this Skill only inside fixed subagents such as `cowork-implement`,
|
|
|
30
30
|
- Treat runtime gates as CLI adapters backed by kernel services; this Skill may
|
|
31
31
|
explain when to run or report them, but does not enforce them.
|
|
32
32
|
|
|
33
|
-
## Spec Self-Check
|
|
34
|
-
|
|
35
|
-
Delegated subagent tool calls receive no edit-phase hook feedback on hosts
|
|
36
|
-
whose plugin hooks cover only the main session stream — a `spec-check[...]
|
|
37
|
-
violation` line never reaches this session, even when the edit violates a
|
|
38
|
-
declared check. Before reporting completion after any file change, run:
|
|
39
|
-
|
|
40
|
-
```text
|
|
41
|
-
./.cowork-flow/run spec-check
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Fix every reported violation and re-run until the summary is clean, or report
|
|
45
|
-
the remaining violations as blockers. Honor any hook feedback the host does
|
|
46
|
-
deliver; the self-check exists because delivery is not guaranteed.
|
|
47
|
-
|
|
48
33
|
## Role Outputs
|
|
49
34
|
|
|
50
35
|
- `cowork-implement`: report changed files, acceptance IDs, verification
|