@michaelxwb/code-flow 0.6.5 → 1.0.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/README.md +75 -110
- package/bin/code-flow.js +12 -0
- package/docs/README.md +19 -0
- package/docs/USAGE.md +268 -0
- package/docs/v1-design.md +1858 -0
- package/docs/v1-implementation.md +186 -0
- package/lib/adapters.js +126 -0
- package/lib/cli.js +258 -0
- package/lib/config.js +85 -0
- package/lib/doctor.js +283 -0
- package/lib/frontmatter.js +42 -0
- package/lib/fs.js +141 -0
- package/lib/glob.js +23 -0
- package/lib/hook.js +99 -0
- package/lib/init.js +132 -0
- package/lib/learn.js +170 -0
- package/lib/locks.js +52 -0
- package/lib/markdown.js +13 -0
- package/lib/process.js +116 -0
- package/lib/render.js +115 -0
- package/lib/repo.js +48 -0
- package/lib/runs.js +155 -0
- package/lib/session.js +40 -0
- package/lib/spec.js +80 -0
- package/lib/task.js +258 -0
- package/lib/taskdoc.js +308 -0
- package/lib/toml.js +140 -0
- package/lib/verify.js +168 -0
- package/lib/worktree.js +930 -0
- package/package.json +21 -9
- package/templates/adapters/opencode.mjs +65 -0
- package/templates/config.json +39 -0
- package/templates/instructions.md +13 -0
- package/templates/launcher/bridge.cjs +295 -0
- package/templates/launcher/hook.cmd +15 -0
- package/templates/launcher/hook.ps1 +10 -0
- package/templates/launcher/hook.sh +20 -0
- package/templates/skills/cf-check.md +19 -0
- package/templates/skills/cf-init.md +16 -0
- package/templates/skills/cf-spec.md +20 -0
- package/templates/skills/cf-task-align.md +19 -0
- package/templates/skills/cf-task-archive.md +20 -0
- package/templates/skills/cf-task-plan.md +31 -0
- package/templates/skills/cf-task-prd.md +14 -0
- package/templates/skills/cf-task-start.md +41 -0
- package/{src/core/code-flow/specs/shared/design → templates/workflow}/design-frontend.md +22 -19
- package/{src/core/code-flow/specs/shared/design → templates/workflow}/design-full.md +36 -28
- package/{src/core/code-flow/specs/shared/design → templates/workflow}/design-lite.md +27 -17
- package/{src/core/code-flow/specs/shared/prd-template.md → templates/workflow/prd.md} +1 -1
- package/templates/workflow/project-guidelines.md +48 -0
- package/templates/workflow/scenarios.md +67 -0
- package/templates/workflow/task.md +56 -0
- package/docs/migrations/spec-workflow-0.6.0.md +0 -42
- package/src/adapters/claude/CLAUDE.md +0 -45
- package/src/adapters/claude/commands/cf-init.md +0 -264
- package/src/adapters/claude/commands/cf-learn.md +0 -291
- package/src/adapters/claude/commands/cf-spec.md +0 -67
- package/src/adapters/claude/commands/cf-stats.md +0 -86
- package/src/adapters/claude/commands/cf-sync.md +0 -25
- package/src/adapters/claude/commands/cf-task/align.md +0 -309
- package/src/adapters/claude/commands/cf-task/archive.md +0 -161
- package/src/adapters/claude/commands/cf-task/block.md +0 -36
- package/src/adapters/claude/commands/cf-task/graph.md +0 -80
- package/src/adapters/claude/commands/cf-task/note.md +0 -236
- package/src/adapters/claude/commands/cf-task/plan.md +0 -363
- package/src/adapters/claude/commands/cf-task/prd.md +0 -162
- package/src/adapters/claude/commands/cf-task/start.md +0 -304
- package/src/adapters/claude/commands/cf-task/status.md +0 -59
- package/src/adapters/claude/commands/cf-task/verify-e2e.md +0 -115
- package/src/adapters/claude/commands/cf-validate.md +0 -75
- package/src/adapters/claude/settings.local.json +0 -50
- package/src/adapters/codex/AGENTS.md +0 -45
- package/src/adapters/codex/config.toml +0 -3
- package/src/adapters/codex/hooks.json +0 -50
- package/src/adapters/codex/skills/cf-init/SKILL.md +0 -273
- package/src/adapters/codex/skills/cf-learn/SKILL.md +0 -296
- package/src/adapters/codex/skills/cf-spec/SKILL.md +0 -60
- package/src/adapters/codex/skills/cf-stats/SKILL.md +0 -87
- package/src/adapters/codex/skills/cf-sync/SKILL.md +0 -30
- package/src/adapters/codex/skills/cf-task-align/SKILL.md +0 -310
- package/src/adapters/codex/skills/cf-task-archive/SKILL.md +0 -162
- package/src/adapters/codex/skills/cf-task-block/SKILL.md +0 -37
- package/src/adapters/codex/skills/cf-task-graph/SKILL.md +0 -81
- package/src/adapters/codex/skills/cf-task-note/SKILL.md +0 -233
- package/src/adapters/codex/skills/cf-task-plan/SKILL.md +0 -324
- package/src/adapters/codex/skills/cf-task-prd/SKILL.md +0 -163
- package/src/adapters/codex/skills/cf-task-start/SKILL.md +0 -305
- package/src/adapters/codex/skills/cf-task-status/SKILL.md +0 -60
- package/src/adapters/codex/skills/cf-task-verify-e2e/SKILL.md +0 -111
- package/src/adapters/codex/skills/cf-validate/SKILL.md +0 -76
- package/src/adapters/costrict/CLAUDE.md +0 -45
- package/src/adapters/costrict/commands/cf-init.md +0 -264
- package/src/adapters/costrict/commands/cf-learn.md +0 -291
- package/src/adapters/costrict/commands/cf-spec.md +0 -67
- package/src/adapters/costrict/commands/cf-stats.md +0 -86
- package/src/adapters/costrict/commands/cf-sync.md +0 -25
- package/src/adapters/costrict/commands/cf-task/align.md +0 -309
- package/src/adapters/costrict/commands/cf-task/archive.md +0 -161
- package/src/adapters/costrict/commands/cf-task/block.md +0 -36
- package/src/adapters/costrict/commands/cf-task/graph.md +0 -80
- package/src/adapters/costrict/commands/cf-task/note.md +0 -236
- package/src/adapters/costrict/commands/cf-task/plan.md +0 -363
- package/src/adapters/costrict/commands/cf-task/prd.md +0 -162
- package/src/adapters/costrict/commands/cf-task/start.md +0 -304
- package/src/adapters/costrict/commands/cf-task/status.md +0 -59
- package/src/adapters/costrict/commands/cf-task/verify-e2e.md +0 -115
- package/src/adapters/costrict/commands/cf-validate.md +0 -75
- package/src/adapters/costrict/settings.local.json +0 -50
- package/src/adapters/opencode/AGENTS.md +0 -45
- package/src/adapters/opencode/commands/cf-init.md +0 -281
- package/src/adapters/opencode/commands/cf-learn.md +0 -295
- package/src/adapters/opencode/commands/cf-spec.md +0 -59
- package/src/adapters/opencode/commands/cf-stats.md +0 -90
- package/src/adapters/opencode/commands/cf-sync.md +0 -29
- package/src/adapters/opencode/commands/cf-task/align.md +0 -314
- package/src/adapters/opencode/commands/cf-task/archive.md +0 -166
- package/src/adapters/opencode/commands/cf-task/block.md +0 -40
- package/src/adapters/opencode/commands/cf-task/graph.md +0 -84
- package/src/adapters/opencode/commands/cf-task/note.md +0 -240
- package/src/adapters/opencode/commands/cf-task/plan.md +0 -363
- package/src/adapters/opencode/commands/cf-task/prd.md +0 -167
- package/src/adapters/opencode/commands/cf-task/start.md +0 -309
- package/src/adapters/opencode/commands/cf-task/status.md +0 -63
- package/src/adapters/opencode/commands/cf-task/verify-e2e.md +0 -115
- package/src/adapters/opencode/commands/cf-validate.md +0 -79
- package/src/adapters/opencode/opencode.json +0 -3
- package/src/adapters/opencode/plugins/code-flow/index.js +0 -213
- package/src/adapters/opencode/plugins/code-flow/package.json +0 -6
- package/src/cli.js +0 -1023
- package/src/core/code-flow/config.yml +0 -158
- package/src/core/code-flow/scripts/cf_acceptance_evidence.py +0 -100
- package/src/core/code-flow/scripts/cf_acceptance_manifest.py +0 -294
- package/src/core/code-flow/scripts/cf_acceptance_runner.py +0 -192
- package/src/core/code-flow/scripts/cf_acceptance_schema.py +0 -83
- package/src/core/code-flow/scripts/cf_checks.py +0 -336
- package/src/core/code-flow/scripts/cf_core.py +0 -787
- package/src/core/code-flow/scripts/cf_exec_base.py +0 -112
- package/src/core/code-flow/scripts/cf_feedback.py +0 -56
- package/src/core/code-flow/scripts/cf_log.py +0 -154
- package/src/core/code-flow/scripts/cf_post_hook.py +0 -196
- package/src/core/code-flow/scripts/cf_pre_tool_hook.py +0 -104
- package/src/core/code-flow/scripts/cf_scan.py +0 -295
- package/src/core/code-flow/scripts/cf_session_state.py +0 -37
- package/src/core/code-flow/scripts/cf_spec_context.py +0 -1746
- package/src/core/code-flow/scripts/cf_spec_gate.py +0 -264
- package/src/core/code-flow/scripts/cf_spec_metadata.py +0 -384
- package/src/core/code-flow/scripts/cf_spec_migrate.py +0 -512
- package/src/core/code-flow/scripts/cf_spec_resolver.py +0 -347
- package/src/core/code-flow/scripts/cf_spec_router.py +0 -244
- package/src/core/code-flow/scripts/cf_spec_session.py +0 -119
- package/src/core/code-flow/scripts/cf_spec_verify.py +0 -559
- package/src/core/code-flow/scripts/cf_stats.py +0 -506
- package/src/core/code-flow/scripts/cf_stop_hook.py +0 -381
- package/src/core/code-flow/scripts/cf_sync.py +0 -140
- package/src/core/code-flow/scripts/cf_task_index.py +0 -148
- package/src/core/code-flow/scripts/cf_task_parallel.py +0 -394
- package/src/core/code-flow/scripts/cf_task_runtime.py +0 -304
- package/src/core/code-flow/scripts/cf_task_state.py +0 -3
- package/src/core/code-flow/scripts/cf_task_workflow.py +0 -205
- package/src/core/code-flow/scripts/cf_user_prompt_hook.py +0 -178
- package/src/core/code-flow/scripts/cf_validation.py +0 -81
- package/src/core/code-flow/scripts/cf_workflow_service.py +0 -366
- package/src/core/code-flow/scripts/cf_workflow_transaction.py +0 -96
- package/src/core/code-flow/specs/backend/_map.md +0 -56
- package/src/core/code-flow/specs/backend/code-quality-performance.md +0 -59
- package/src/core/code-flow/specs/backend/database.md +0 -51
- package/src/core/code-flow/specs/backend/directory-structure.md +0 -54
- package/src/core/code-flow/specs/backend/logging.md +0 -50
- package/src/core/code-flow/specs/backend/platform-rules.md +0 -55
- package/src/core/code-flow/specs/frontend/_map.md +0 -52
- package/src/core/code-flow/specs/frontend/component-specs.md +0 -68
- package/src/core/code-flow/specs/frontend/directory-structure.md +0 -53
- package/src/core/code-flow/specs/frontend/quality-standards.md +0 -58
- package/src/core/code-flow/specs/shared/_map.md +0 -57
- package/src/core/code-flow/tasks/.gitkeep +0 -0
- package/src/core/code-flow/validation.yml +0 -53
- package/src/migrate/spec-workflow.js +0 -238
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: 根据变更文件自动匹配并执行验证规则
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# cf-validate
|
|
6
|
-
|
|
7
|
-
根据变更文件自动匹配并执行验证规则(测试、类型检查、lint),失败时自动尝试修复。
|
|
8
|
-
|
|
9
|
-
## 输入
|
|
10
|
-
|
|
11
|
-
- `/project:cf-validate` — 自动获取任务基线或 Git 全部变更文件
|
|
12
|
-
- `/project:cf-validate src/Foo.tsx` — 验证指定文件
|
|
13
|
-
- `/project:cf-validate --files=src/a.ts,src/b.ts` — 验证多个文件
|
|
14
|
-
|
|
15
|
-
## 执行步骤
|
|
16
|
-
|
|
17
|
-
### 1. 获取变更文件列表
|
|
18
|
-
|
|
19
|
-
用 Bash 执行:
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
python3 .code-flow/scripts/cf_validation.py --root "$PWD" --scope-only --json
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
有 active TASK 时使用冻结基线以来的全部任务变更(含已提交文件);无 active TASK 时包含暂存、未暂存和未跟踪文件。用户指定路径时用 `--files "src/a.py" "src/b.py"` 原样传递;若结果确实为空才输出“无变更需要验证”。
|
|
26
|
-
|
|
27
|
-
### 2. 读取验证规则
|
|
28
|
-
|
|
29
|
-
用 Read 读取 `.code-flow/validation.yml`。如果不存在,尝试读取 `package.json` 中的 `scripts.test` 和 `scripts.lint` 作为回退。
|
|
30
|
-
|
|
31
|
-
validation.yml 格式:
|
|
32
|
-
|
|
33
|
-
```yaml
|
|
34
|
-
validators:
|
|
35
|
-
- name: "验证器名称"
|
|
36
|
-
trigger: "**/*.{ts,tsx}"
|
|
37
|
-
command: "npx tsc --noEmit"
|
|
38
|
-
timeout: 30000
|
|
39
|
-
heavy: false # true = Stop 每轮跳过,finish 与 /cf-validate 执行
|
|
40
|
-
on_fail: "修复建议"
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
全量测试 / e2e 等慢命令标 `heavy: true`:Stop 每轮跳过,**任务 finish 自动执行一次**,也可显式运行 `/cf-validate`(或 CI),避免拖慢每个任务。
|
|
44
|
-
|
|
45
|
-
### 3. 匹配并执行验证
|
|
46
|
-
|
|
47
|
-
执行统一验证入口:
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
python3 .code-flow/scripts/cf_validation.py --root "$PWD" --json
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
显式范围追加 `--files "src/a.py" "src/b.py"`。程序匹配 trigger、解析 argv 并展开 `{files}`,不拼接 shell 命令;同一运行内相同 argv/cwd/timeout 去重,跨运行不复用。删除文件仍触发全局测试,但不传给文件级编译器。每条 timeout 仍按配置的毫秒值生效,总预算耗尽返回 incomplete/block。
|
|
54
|
-
|
|
55
|
-
### 4. 汇总结果
|
|
56
|
-
|
|
57
|
-
- **全部通过** → 输出确认信息
|
|
58
|
-
- **有失败** → 展示每个失败项的:
|
|
59
|
-
- 验证器名称
|
|
60
|
-
- 执行的命令
|
|
61
|
-
- 错误输出(截取关键部分)
|
|
62
|
-
- `on_fail` 修复建议
|
|
63
|
-
|
|
64
|
-
### 5. 自动修复
|
|
65
|
-
|
|
66
|
-
对于失败的验证项,根据错误输出分析问题根因,自动尝试修复代码。修复后提示用户再次运行 `/project:cf-validate` 确认。
|
|
67
|
-
|
|
68
|
-
## 安全设计
|
|
69
|
-
|
|
70
|
-
- `{files}` 直接展开为 argv 参数;带空格、引号的路径仍是一个参数,不手工拼接引号或执行 shell
|
|
71
|
-
- 范围来自任务基线、Git 状态(包括 untracked)或用户显式指定的路径
|
|
72
|
-
- 用户指定的路径必须位于项目根目录内
|
|
73
|
-
|
|
74
|
-
## 异常处理
|
|
75
|
-
|
|
76
|
-
- validation.yml 不存在 → 尝试检测 package.json scripts 中的 test/lint 命令
|
|
77
|
-
- 命令执行超时 → 输出超时提示,建议增大 timeout 或缩小验证范围
|
|
78
|
-
- 命令不存在或配置无效 → 返回 block 并提示所缺依赖,不当作验证通过
|
|
79
|
-
- 无变更文件 → 输出"无变更需要验证"
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import { execFile } from "child_process";
|
|
2
|
-
import { join } from "path";
|
|
3
|
-
import { appendFileSync, mkdirSync } from "fs";
|
|
4
|
-
|
|
5
|
-
// Note: intentionally no `import { Plugin } from "@opencode/plugin"`.
|
|
6
|
-
// The server does not auto-provide that package for local plugins, and
|
|
7
|
-
// `Plugin.define` is only an identity wrapper — a plain default export
|
|
8
|
-
// with `id` + `setup` satisfies the v2 plugin schema dependency-free,
|
|
9
|
-
// so `code-flow init` works offline without npm install.
|
|
10
|
-
|
|
11
|
-
const SCRIPT_DIR = ".code-flow/scripts";
|
|
12
|
-
const STOP_CHECK_TIMEOUT = 35000;
|
|
13
|
-
const HOOK_TIMEOUT = 5000;
|
|
14
|
-
|
|
15
|
-
// Per-session queued feedback. Single map, append-only: idle/stop-check and
|
|
16
|
-
// post-check feedback accumulate here and are consumed by the next context
|
|
17
|
-
// hook. Never overwrite — a prompt hook arriving between idle and context
|
|
18
|
-
// must not drop queued stop-check feedback.
|
|
19
|
-
const sessionContext = new Map();
|
|
20
|
-
|
|
21
|
-
export function mergePending(existing, incoming) {
|
|
22
|
-
if (!existing) return incoming;
|
|
23
|
-
if (!incoming) return existing;
|
|
24
|
-
return existing + "\n\n" + incoming;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function debugLog(projectRoot, msg) {
|
|
28
|
-
if (process.env.CF_DEBUG !== "1") return;
|
|
29
|
-
try {
|
|
30
|
-
const dir = join(projectRoot, ".code-flow");
|
|
31
|
-
mkdirSync(dir, { recursive: true });
|
|
32
|
-
const ts = new Date().toISOString().replace("T", " ").slice(0, 19);
|
|
33
|
-
appendFileSync(join(dir, ".debug.log"), `${ts} [opencode] ${msg}\n`);
|
|
34
|
-
} catch {}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function pythonPath(projectRoot, script) {
|
|
38
|
-
return join(projectRoot, SCRIPT_DIR, script);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function callHook(projectRoot, script, input, timeout = HOOK_TIMEOUT) {
|
|
42
|
-
// Async child process: never block the plugin event thread. opencode's
|
|
43
|
-
// idle event cannot interrupt a turn, so stop-check failures queue into
|
|
44
|
-
// sessionContext for the next context hook (documented platform gap
|
|
45
|
-
// vs. blocking Stop hooks).
|
|
46
|
-
return new Promise((resolve) => {
|
|
47
|
-
const child = execFile(
|
|
48
|
-
"python3",
|
|
49
|
-
[pythonPath(projectRoot, script)],
|
|
50
|
-
{ cwd: projectRoot, timeout, maxBuffer: 1024 * 1024 },
|
|
51
|
-
(error, stdout, stderr) => {
|
|
52
|
-
if (error) {
|
|
53
|
-
debugLog(projectRoot, `callHook ${script} failed: ${error.message || stderr}`);
|
|
54
|
-
resolve(null);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
const text = (stdout || "").trim();
|
|
58
|
-
if (!text) {
|
|
59
|
-
resolve(null);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
try {
|
|
63
|
-
resolve(JSON.parse(text));
|
|
64
|
-
} catch (e) {
|
|
65
|
-
debugLog(projectRoot, `callHook ${script} bad JSON: ${e.message}`);
|
|
66
|
-
resolve(null);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
);
|
|
70
|
-
if (input !== undefined && child.stdin) {
|
|
71
|
-
child.stdin.write(JSON.stringify(input));
|
|
72
|
-
child.stdin.end();
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function queueFeedback(sessionID, text) {
|
|
78
|
-
if (!sessionID || !text) return;
|
|
79
|
-
sessionContext.set(sessionID, mergePending(sessionContext.get(sessionID), text));
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function consumeFeedback(sessionID) {
|
|
83
|
-
const pending = sessionContext.get(sessionID);
|
|
84
|
-
if (pending) sessionContext.delete(sessionID);
|
|
85
|
-
return pending;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function toolInputFilePath(input) {
|
|
89
|
-
if (!input || typeof input !== "object") return "";
|
|
90
|
-
const args = input;
|
|
91
|
-
return args.filePath || args.file_path || args.path || "";
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async function runStopCheck(projectRoot, sessionID) {
|
|
95
|
-
const result = await callHook(projectRoot, "cf_stop_hook.py", { session_id: sessionID }, STOP_CHECK_TIMEOUT);
|
|
96
|
-
if (result?.reason) {
|
|
97
|
-
// idle 无法阻断,校验失败排队到下一轮 context hook
|
|
98
|
-
queueFeedback(sessionID, result.reason);
|
|
99
|
-
debugLog(projectRoot, `stop-check feedback queued`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
async function registerPromptHook(ctx, projectRoot) {
|
|
104
|
-
await ctx.session.hook("prompt", async (event) => {
|
|
105
|
-
const promptText = event.prompt?.text || "";
|
|
106
|
-
if (!promptText) return;
|
|
107
|
-
debugLog(projectRoot, `prompt sid=${event.sessionID} prompt_len=${promptText.length}`);
|
|
108
|
-
const result = await callHook(projectRoot, "cf_user_prompt_hook.py", {
|
|
109
|
-
prompt: promptText,
|
|
110
|
-
session_id: event.sessionID,
|
|
111
|
-
});
|
|
112
|
-
const additional = result?.hookSpecificOutput?.additionalContext;
|
|
113
|
-
if (additional) {
|
|
114
|
-
// Append, never overwrite: queued stop-check feedback survives.
|
|
115
|
-
queueFeedback(event.sessionID, additional);
|
|
116
|
-
debugLog(projectRoot, `hook matched — context ${additional.length} chars cached`);
|
|
117
|
-
} else {
|
|
118
|
-
debugLog(projectRoot, `hook returned no context`);
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
async function registerToolHook(ctx, projectRoot) {
|
|
124
|
-
await ctx.tool.hook("execute.after", async (event) => {
|
|
125
|
-
const tool = String(event.tool || "").toLowerCase();
|
|
126
|
-
if (!["edit", "write", "multiedit", "patch"].includes(tool)) return;
|
|
127
|
-
const filePath = toolInputFilePath(event.input);
|
|
128
|
-
if (!filePath) return;
|
|
129
|
-
const result = await callHook(projectRoot, "cf_post_hook.py", {
|
|
130
|
-
tool_name: tool === "write" ? "Write" : "Edit",
|
|
131
|
-
tool_input: { file_path: filePath },
|
|
132
|
-
session_id: event.sessionID || "",
|
|
133
|
-
});
|
|
134
|
-
const additional = result?.hookSpecificOutput?.additionalContext;
|
|
135
|
-
if (additional) {
|
|
136
|
-
// 反馈排队,下一轮 context hook 注入(opencode 无法当轮插话)
|
|
137
|
-
queueFeedback(event.sessionID, additional);
|
|
138
|
-
debugLog(projectRoot, `post-check feedback queued ${additional.length} chars`);
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
async function registerContextHook(ctx) {
|
|
144
|
-
await ctx.session.hook("context", (event) => {
|
|
145
|
-
const pending = consumeFeedback(event.sessionID);
|
|
146
|
-
if (pending) {
|
|
147
|
-
event.system.push({ type: "text", text: pending });
|
|
148
|
-
debugLog(ctx.location.directory, `context — injected ${pending.length} chars`);
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// 子会话(子 agent / worktree 并行任务)的 idle 不得触发主工作区 stop-check:
|
|
154
|
-
// 子会话的编辑发生在自己的工作区,主区既无 active marker 也没有对应文件,
|
|
155
|
-
// 触发只会重复执行主区 validators(全量测试)。用 session.created.parentID 标记子会话。
|
|
156
|
-
export function createSessionRegistry() {
|
|
157
|
-
const children = new Set();
|
|
158
|
-
return {
|
|
159
|
-
observe(event) {
|
|
160
|
-
if (!event || typeof event.type !== "string") return null;
|
|
161
|
-
const sid = event.data?.sessionID || "";
|
|
162
|
-
if (!sid) return null;
|
|
163
|
-
if (event.type === "session.created") {
|
|
164
|
-
if (event.data?.parentID) children.add(sid);
|
|
165
|
-
return { type: "created", sid };
|
|
166
|
-
}
|
|
167
|
-
if (event.type === "session.idle") {
|
|
168
|
-
return { type: "idle", sid, child: children.has(sid) };
|
|
169
|
-
}
|
|
170
|
-
return null;
|
|
171
|
-
},
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function startEventSubscription(ctx, projectRoot) {
|
|
176
|
-
const controller = new AbortController();
|
|
177
|
-
const registry = createSessionRegistry();
|
|
178
|
-
void (async () => {
|
|
179
|
-
try {
|
|
180
|
-
for await (const event of ctx.event.subscribe({ signal: controller.signal })) {
|
|
181
|
-
const observed = registry.observe(event);
|
|
182
|
-
if (!observed) continue;
|
|
183
|
-
if (observed.type === "created") {
|
|
184
|
-
debugLog(projectRoot, `session.created sid=${observed.sid}`);
|
|
185
|
-
sessionContext.delete(observed.sid);
|
|
186
|
-
} else if (observed.child) {
|
|
187
|
-
debugLog(projectRoot, `session.idle sid=${observed.sid} (child) — stop-check skipped`);
|
|
188
|
-
} else {
|
|
189
|
-
await runStopCheck(projectRoot, observed.sid);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
} catch (err) {
|
|
193
|
-
if (err?.name !== "AbortError") {
|
|
194
|
-
debugLog(projectRoot, `event subscription ended: ${err?.message || err}`);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
})();
|
|
198
|
-
return () => controller.abort();
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const CodeFlowPlugin = {
|
|
202
|
-
id: "code-flow",
|
|
203
|
-
async setup(ctx) {
|
|
204
|
-
const projectRoot = ctx.location.directory;
|
|
205
|
-
await registerPromptHook(ctx, projectRoot);
|
|
206
|
-
await registerToolHook(ctx, projectRoot);
|
|
207
|
-
await registerContextHook(ctx);
|
|
208
|
-
const stopSubscription = startEventSubscription(ctx, projectRoot);
|
|
209
|
-
return () => stopSubscription();
|
|
210
|
-
},
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
export default CodeFlowPlugin;
|