helloloop 0.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.
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "helloloop",
3
+ "version": "0.1.0",
4
+ "description": "Backlog-driven continuous repository execution for Codex with verification loops and explicit skill and CLI entrypoints.",
5
+ "author": {
6
+ "name": "HelloWind",
7
+ "url": "https://github.com/hellowind777"
8
+ },
9
+ "license": "MIT",
10
+ "keywords": [
11
+ "codex",
12
+ "helloloop",
13
+ "automation",
14
+ "backlog",
15
+ "verification"
16
+ ],
17
+ "skills": "./skills/",
18
+ "interface": {
19
+ "displayName": "HelloLoop",
20
+ "shortDescription": "Continuous repo execution with backlog and Ralph Loop guards",
21
+ "longDescription": "HelloLoop packages backlog-driven continuous development for Codex as a pure official plugin bundle. Because current Codex runtime does not load Hook manifests from plugins, this plugin intentionally drops `~helloloop` Hook mode and uses explicit skill and CLI entrypoints instead.",
22
+ "developerName": "HelloWind",
23
+ "category": "Coding",
24
+ "capabilities": [
25
+ "Interactive",
26
+ "Write"
27
+ ],
28
+ "defaultPrompt": [
29
+ "Use HelloLoop to inspect backlog state and keep this repo moving with explicit run-loop commands."
30
+ ],
31
+ "brandColor": "#0F766E"
32
+ }
33
+ }
package/README.md ADDED
@@ -0,0 +1,334 @@
1
+ # HelloLoop
2
+
3
+ `HelloLoop` 是一个面向 Codex 的 backlog 驱动开发插件。
4
+
5
+ 它把持续开发所需的任务队列、执行策略、验证命令和运行记录集中到目标仓库的 `.helloloop/` 目录里,并通过显式 CLI / skill 入口推动任务逐步落地。
6
+
7
+ ## 目录
8
+
9
+ - [HelloLoop 是什么](#helloloop-是什么)
10
+ - [核心能力](#核心能力)
11
+ - [适用场景](#适用场景)
12
+ - [项目结构](#项目结构)
13
+ - [安装](#安装)
14
+ - [快速开始](#快速开始)
15
+ - [命令说明](#命令说明)
16
+ - [状态目录](#状态目录)
17
+ - [任务与执行模型](#任务与执行模型)
18
+ - [发布到 npm](#发布到-npm)
19
+ - [验证](#验证)
20
+ - [相关文档](#相关文档)
21
+
22
+ ## HelloLoop 是什么
23
+
24
+ `HelloLoop` 解决的是“让 Codex 在同一个仓库里持续推进任务”这个问题。
25
+
26
+ 与一次性执行单个改动不同,它围绕一份 backlog 运行:
27
+
28
+ - 先从 backlog 里选择当前最合适的任务
29
+ - 根据项目状态、必读文档和约束生成执行提示
30
+ - 调用 Codex 完成实现
31
+ - 运行验证命令确认结果
32
+ - 失败时按 Ralph Loop 思路重试或换路
33
+ - 把状态、结果和运行产物写回目标仓库
34
+
35
+ 这意味着你可以把 `HelloLoop` 当成一个“面向仓库持续推进”的执行层,而不是单次脚本集合。
36
+
37
+ ## 核心能力
38
+
39
+ - **插件安装**:把运行时 bundle 安装到 `~/.codex/plugins/helloloop`
40
+ - **仓库初始化**:为目标仓库生成 `.helloloop/` 初始状态文件
41
+ - **任务选择**:基于优先级、依赖和风险等级选择下一任务
42
+ - **干跑预览**:先生成下一任务提示词和验证列表,再决定是否执行
43
+ - **单轮执行**:执行一个任务并回写状态
44
+ - **循环执行**:连续执行多个任务,直到达到上限或遇到阻塞
45
+ - **验证联动**:优先读取任务自身验证命令,否则回退到仓库 `.helloagents/verify.yaml`
46
+ - **运行留痕**:把每次执行的提示词、日志、验证输出保存到 `runs/`
47
+
48
+ ## 适用场景
49
+
50
+ - 你已经有 backlog,希望 Codex 按队列持续推进
51
+ - 你希望每个任务执行前都自动带上项目状态和文档约束
52
+ - 你希望执行失败后自动进入重试或换路,而不是停在一次失败
53
+ - 你希望把任务状态、验证结果和运行痕迹沉淀在仓库内
54
+ - 你希望通过 npm 分发这个插件,并在 GitHub Tag 发布时自动同步到 npm
55
+
56
+ ## 项目结构
57
+
58
+ ```text
59
+ helloloop/
60
+ ├── .codex-plugin/ # Codex 插件 manifest
61
+ ├── bin/ # npm bin 入口
62
+ ├── docs/ # 说明文档
63
+ ├── scripts/ # CLI 安装脚本与入口
64
+ ├── skills/ # 插件技能
65
+ ├── src/ # 核心实现
66
+ ├── templates/ # 初始化模板
67
+ └── tests/ # 回归测试
68
+ ```
69
+
70
+ 源码仓库包含 `docs/` 和 `tests/`,但安装到 Codex Home 时只会复制运行时必需文件。
71
+
72
+ ## 安装
73
+
74
+ ### 方式一:通过 npm / npx 安装
75
+
76
+ ```powershell
77
+ npx helloloop install --codex-home C:\Users\hellowind\.codex
78
+ ```
79
+
80
+ ### 方式二:从源码仓库安装
81
+
82
+ ```powershell
83
+ pwsh -NoLogo -NoProfile -File .\scripts\install-home-plugin.ps1 -CodexHome C:\Users\hellowind\.codex
84
+ ```
85
+
86
+ 如果已存在同名安装目录,追加 `-Force` 或 `--force` 覆盖即可。
87
+
88
+ 安装完成后,运行时文件会位于:
89
+
90
+ ```text
91
+ C:\Users\hellowind\.codex\plugins\helloloop
92
+ ```
93
+
94
+ 同时会自动更新:
95
+
96
+ ```text
97
+ C:\Users\hellowind\.codex\.agents\plugins\marketplace.json
98
+ ```
99
+
100
+ ## 快速开始
101
+
102
+ ### 1. 安装插件
103
+
104
+ ```powershell
105
+ npx helloloop install --codex-home C:\Users\hellowind\.codex
106
+ ```
107
+
108
+ ### 2. 在目标仓库初始化状态目录
109
+
110
+ ```powershell
111
+ node C:\Users\hellowind\.codex\plugins\helloloop\scripts\helloloop.mjs init --repo D:\GitHub\dev\your-repo
112
+ ```
113
+
114
+ ### 3. 检查运行条件
115
+
116
+ ```powershell
117
+ node C:\Users\hellowind\.codex\plugins\helloloop\scripts\helloloop.mjs doctor --repo D:\GitHub\dev\your-repo
118
+ ```
119
+
120
+ ### 4. 查看当前状态或下一任务
121
+
122
+ ```powershell
123
+ node C:\Users\hellowind\.codex\plugins\helloloop\scripts\helloloop.mjs status --repo D:\GitHub\dev\your-repo
124
+ node C:\Users\hellowind\.codex\plugins\helloloop\scripts\helloloop.mjs next --repo D:\GitHub\dev\your-repo
125
+ ```
126
+
127
+ ### 5. 执行一个任务或连续执行
128
+
129
+ ```powershell
130
+ node C:\Users\hellowind\.codex\plugins\helloloop\scripts\helloloop.mjs run-once --repo D:\GitHub\dev\your-repo
131
+ node C:\Users\hellowind\.codex\plugins\helloloop\scripts\helloloop.mjs run-loop --repo D:\GitHub\dev\your-repo --max-tasks 2
132
+ ```
133
+
134
+ ## 命令说明
135
+
136
+ | 命令 | 作用 |
137
+ | --- | --- |
138
+ | `install` | 安装插件到 Codex Home,并更新 marketplace |
139
+ | `init` | 初始化目标仓库 `.helloloop/` |
140
+ | `doctor` | 检查 Codex CLI、模板、配置文件和插件文件是否齐备 |
141
+ | `status` | 查看 backlog 汇总与下一任务 |
142
+ | `next` | 干跑生成下一任务预览,不真正调用 Codex |
143
+ | `run-once` | 执行一个任务 |
144
+ | `run-loop` | 连续执行多个任务 |
145
+
146
+ ### 常用选项
147
+
148
+ | 选项 | 说明 |
149
+ | --- | --- |
150
+ | `--repo <dir>` | 目标仓库根目录 |
151
+ | `--codex-home <dir>` | 指定 Codex Home |
152
+ | `--config-dir <dir>` | 自定义状态目录,默认 `.helloloop` |
153
+ | `--dry-run` | 只生成提示词和预览,不真正调用 Codex |
154
+ | `--task-id <id>` | 指定执行某个任务 |
155
+ | `--max-tasks <n>` | `run-loop` 最多执行多少个任务 |
156
+ | `--max-attempts <n>` | 每种策略内最大重试次数 |
157
+ | `--max-strategies <n>` | 单任务最大换路次数 |
158
+ | `--allow-high-risk` | 允许执行 `medium` / `high` / `critical` 风险任务 |
159
+ | `--required-doc <path>` | 追加全局必读文档 |
160
+ | `--constraint <text>` | 追加全局实现约束 |
161
+ | `--force` | 覆盖安装目录 |
162
+
163
+ ### Skill 名称
164
+
165
+ 安装为 Codex 插件后,可通过插件命名空间使用:
166
+
167
+ ```text
168
+ helloloop:helloloop
169
+ ```
170
+
171
+ ## 状态目录
172
+
173
+ `HelloLoop` 在目标仓库内默认使用 `.helloloop/` 保存执行状态。
174
+
175
+ 典型结构如下:
176
+
177
+ ```text
178
+ .helloloop/
179
+ ├── backlog.json
180
+ ├── policy.json
181
+ ├── project.json
182
+ ├── status.json
183
+ ├── STATE.md
184
+ └── runs/
185
+ ```
186
+
187
+ ### 各文件作用
188
+
189
+ - `backlog.json`:任务列表、优先级、风险、验收条件、依赖关系
190
+ - `policy.json`:循环上限、重试次数、Codex 执行参数
191
+ - `project.json`:全局必读文档、实现约束和 planner 配置
192
+ - `status.json`:机器可读的最近运行状态
193
+ - `STATE.md`:给人看的当前状态摘要
194
+ - `runs/`:每次任务执行的提示词、stdout、stderr 和验证记录
195
+
196
+ 如果旧仓库仍保留 `.helloagents/helloloop/`,当前版本也会自动识别;你也可以显式传 `--config-dir .helloagents/helloloop`。
197
+
198
+ ## 任务与执行模型
199
+
200
+ ### backlog 任务字段
201
+
202
+ `backlog.json` 中的任务通常包含这些字段:
203
+
204
+ - `id`:任务唯一标识
205
+ - `title`:任务标题
206
+ - `status`:`pending` / `in_progress` / `done` / `failed` / `blocked`
207
+ - `priority`:`P0` 到 `P3`
208
+ - `risk`:`low` / `medium` / `high` / `critical`
209
+ - `goal`:任务目标
210
+ - `docs`:执行前必读文档
211
+ - `paths`:主要涉及目录
212
+ - `acceptance`:验收条件
213
+ - `dependsOn`:依赖的上游任务
214
+ - `verify`:任务专属验证命令(可选)
215
+
216
+ ### 执行流程
217
+
218
+ `HelloLoop` 的核心执行逻辑如下:
219
+
220
+ 1. 从 backlog 中筛出可执行任务
221
+ 2. 按优先级和依赖关系选择下一任务
222
+ 3. 读取仓库状态、必读文档和实现约束
223
+ 4. 生成 Codex 执行提示词
224
+ 5. 调用 `codex exec`
225
+ 6. 运行验证命令
226
+ 7. 成功则把任务标记为完成
227
+ 8. 失败则按 Ralph Loop 记录失败历史并继续重试或换路
228
+
229
+ ### 风险与阻塞规则
230
+
231
+ - 默认只自动执行 `low` 风险任务
232
+ - `medium` 及以上风险任务需要显式传入 `--allow-high-risk`
233
+ - 如果存在未收束的 `in_progress` 任务,新的自动执行会被阻塞
234
+ - 如果存在 `failed` 或 `blocked` 任务,执行会停下来等待处理
235
+ - 如果依赖任务未完成,相关任务不会被挑选执行
236
+
237
+ ## 发布到 npm
238
+
239
+ 仓库已加入自动发布工作流:
240
+
241
+ ```text
242
+ .github/workflows/publish.yml
243
+ ```
244
+
245
+ 这个工作流借鉴了 `helloagents` 的发布结构,并做了适合 `HelloLoop` 的简化与对齐:
246
+
247
+ - 以 Git Tag 作为发布触发器
248
+ - 发布前校验 `package.json` 版本与 Tag 是否一致
249
+ - 自动运行 `npm test`
250
+ - 自动运行 `npm pack --dry-run`
251
+ - 自动发布到 npm
252
+ - 自动创建 GitHub Release
253
+
254
+ ### 推荐发布方式
255
+
256
+ #### 正式版
257
+
258
+ 1. 修改 `package.json` 中的 `version`
259
+ 2. 提交并推送代码
260
+ 3. 创建同版本 Tag
261
+
262
+ 示例:
263
+
264
+ ```powershell
265
+ npm version patch --no-git-tag-version
266
+ git add package.json
267
+ git commit -m "chore: release v0.1.1"
268
+ git push origin main
269
+ git tag v0.1.1
270
+ git push origin v0.1.1
271
+ ```
272
+
273
+ 推送 `v0.1.1` 后,Action 会把 `0.1.1` 发布到 npm 的 `latest` 通道。
274
+
275
+ #### Beta 版
276
+
277
+ Beta Tag 采用:
278
+
279
+ ```text
280
+ v0.2.0-beta.1
281
+ ```
282
+
283
+ 这类 Tag 会发布到 npm 的 `beta` 通道。工作流会要求:
284
+
285
+ - `package.json` 中的基础版本为 `0.2.0`
286
+ - 实际发布版本为 `0.2.0-beta.1`
287
+
288
+ ### npm Trusted Publishing 配置
289
+
290
+ 为了让 GitHub Actions 无需 `NPM_TOKEN` 直接发布到 npm,建议使用 npm Trusted Publishing。
291
+
292
+ 在 npm 包设置中把以下信息绑定为 Trusted Publisher:
293
+
294
+ - GitHub 仓库:`hellowind777/helloloop`
295
+ - Workflow 文件:`publish.yml`
296
+ - 触发来源:GitHub-hosted runner
297
+
298
+ 发布工作流已经按 Trusted Publishing 方式准备好:
299
+
300
+ - `id-token: write`
301
+ - Node 24
302
+ - 升级到最新 npm CLI
303
+ - 直接执行 `npm publish`
304
+
305
+ ### 工作流触发规则
306
+
307
+ - `push tags: v*`:标准自动发布入口
308
+ - `workflow_dispatch`:手动输入 Tag 重新发布某个版本
309
+
310
+ ## 验证
311
+
312
+ ### 本地回归
313
+
314
+ ```powershell
315
+ npm test
316
+ ```
317
+
318
+ ### 打包预检
319
+
320
+ ```powershell
321
+ npm pack --dry-run
322
+ ```
323
+
324
+ ### 安装烟测
325
+
326
+ ```powershell
327
+ node .\bin\helloloop.mjs install --codex-home C:\Users\hellowind\AppData\Local\Temp\helloloop-smoke --force
328
+ ```
329
+
330
+ ## 相关文档
331
+
332
+ - 安装说明:`docs/install.md`
333
+ - 插件主说明:`docs/README.md`
334
+ - 官方标准映射:`docs/plugin-standard.md`
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { runCli } from "../src/cli.mjs";
4
+
5
+ runCli(process.argv.slice(2)).catch((error) => {
6
+ console.error(String(error?.stack || error || ""));
7
+ process.exitCode = 1;
8
+ });
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "helloloop",
3
+ "version": "0.1.0",
4
+ "description": "Standalone Codex plugin bundle for backlog-driven repository execution with Ralph Loop guards",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/hellowind777/helloloop.git"
9
+ },
10
+ "homepage": "https://github.com/hellowind777/helloloop#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/hellowind777/helloloop/issues"
13
+ },
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "bin": {
18
+ "helloloop": "./bin/helloloop.mjs"
19
+ },
20
+ "files": [
21
+ ".codex-plugin",
22
+ "README.md",
23
+ "bin",
24
+ "package.json",
25
+ "scripts",
26
+ "skills",
27
+ "src",
28
+ "templates"
29
+ ],
30
+ "scripts": {
31
+ "test": "node --test tests/cli_surface.test.mjs tests/install_script.test.mjs tests/ralph_loop.test.mjs tests/plugin_bundle.test.mjs"
32
+ },
33
+ "engines": {
34
+ "node": ">=20"
35
+ }
36
+ }
37
+
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { runCli } from "../src/cli.mjs";
4
+
5
+ runCli(process.argv.slice(2)).catch((error) => {
6
+ console.error(String(error?.stack || error || ""));
7
+ process.exitCode = 1;
8
+ });
@@ -0,0 +1,30 @@
1
+ [CmdletBinding()]
2
+ param(
3
+ [string]$CodexHome = (Join-Path $HOME ".codex"),
4
+ [switch]$Force
5
+ )
6
+
7
+ $sourceRoot = Split-Path -Parent $PSScriptRoot
8
+ $cliEntry = Join-Path $sourceRoot "scripts\helloloop.mjs"
9
+
10
+ if (-not (Test-Path $cliEntry)) {
11
+ throw "未找到 CLI 入口:$cliEntry"
12
+ }
13
+
14
+ if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
15
+ throw "未找到 node,可先安装 Node.js 20+ 后再运行。"
16
+ }
17
+
18
+ $arguments = @(
19
+ $cliEntry,
20
+ "install",
21
+ "--codex-home",
22
+ $CodexHome
23
+ )
24
+
25
+ if ($Force) {
26
+ $arguments += "--force"
27
+ }
28
+
29
+ & node @arguments
30
+ exit $LASTEXITCODE
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: helloloop
3
+ description: Use when the user wants Codex to keep shipping repo work across turns, bootstrap HelloLoop, or inspect backlog-driven execution state through the pure official plugin bundle.
4
+ ---
5
+
6
+ # HelloLoop
7
+
8
+ Use this plugin when the task is continuous repository execution rather than a single chat-turn change.
9
+
10
+ ## Official Plugin Boundary
11
+
12
+ - This bundle root is the official plugin surface for HelloLoop.
13
+ - It ships standard Codex plugin metadata through `.codex-plugin/plugin.json` and a plugin skill through `skills/`.
14
+ - Current Codex runtime does not auto-load Hook handlers from plugins.
15
+ - This plugin therefore does not support `~helloloop` or automatic Stop-hook continuation.
16
+
17
+ ## Setup
18
+
19
+ 1. Install with `npx helloloop install --codex-home <CODEX_HOME>` or use `scripts/install-home-plugin.ps1`.
20
+ 2. From the target repository, run `node <helloloop-plugin-root>/scripts/helloloop.mjs doctor --repo <repo-root>`.
21
+ 3. From the target repository, run `node <helloloop-plugin-root>/scripts/helloloop.mjs init --repo <repo-root>`.
22
+
23
+ ## Operating Model
24
+
25
+ - `.helloloop/` is the CLI and backlog state directory.
26
+ - The installed plugin bundle keeps runtime files such as `src/`, `templates/`, `bin/`, `scripts/`, and `skills/`.
27
+ - Source-only materials such as `docs/` and `tests/` stay in the development repository.
28
+
29
+ ## Invocation
30
+
31
+ - In official Codex plugin mode, plugin skills are namespaced with `plugin_name:`.
32
+ - For this plugin, the unambiguous skill name is `helloloop:helloloop`.
33
+ - Explicitly naming the `helloloop` plugin should also bias Codex toward this skill.
34
+ - Do not promise bare `$helloloop` is the official invocation form.
35
+
36
+ ## Primary Commands
37
+
38
+ - `node <helloloop-plugin-root>/scripts/helloloop.mjs status --repo <repo-root>`
39
+ - `node <helloloop-plugin-root>/scripts/helloloop.mjs next --repo <repo-root>`
40
+ - `node <helloloop-plugin-root>/scripts/helloloop.mjs run-once --repo <repo-root>`
41
+ - `node <helloloop-plugin-root>/scripts/helloloop.mjs run-loop --repo <repo-root>`
42
+
43
+ ## Reporting Rules
44
+
45
+ - State explicitly that the plugin runs in pure official plugin mode.
46
+ - Call out any request that depends on Hook-only behavior such as `UserPromptSubmit` or `Stop`, because those are intentionally unsupported here.
47
+ - Keep Ralph Loop guardrails and repo verification intact.
48
+
49
+ ## Docs
50
+
51
+ - Source repo main guide: `docs/README.md`
52
+ - Source repo installation note: `docs/install.md`
53
+ - Source repo official standard and runtime boundary: `docs/plugin-standard.md`
@@ -0,0 +1,170 @@
1
+ const priorityRank = {
2
+ P0: 0,
3
+ P1: 1,
4
+ P2: 2,
5
+ P3: 3,
6
+ };
7
+
8
+ const riskRank = {
9
+ low: 0,
10
+ medium: 1,
11
+ high: 2,
12
+ critical: 3,
13
+ };
14
+
15
+ function rankPriority(priority) {
16
+ return priorityRank[String(priority || "P2").toUpperCase()] ?? priorityRank.P2;
17
+ }
18
+
19
+ function sortTasks(backlog, tasks) {
20
+ return [...tasks].sort((left, right) => {
21
+ const byPriority = rankPriority(left.priority) - rankPriority(right.priority);
22
+ if (byPriority !== 0) return byPriority;
23
+ return backlog.tasks.findIndex((item) => item.id === left.id)
24
+ - backlog.tasks.findIndex((item) => item.id === right.id);
25
+ });
26
+ }
27
+
28
+ export function summarizeBacklog(backlog) {
29
+ const summary = {
30
+ total: backlog.tasks.length,
31
+ pending: 0,
32
+ inProgress: 0,
33
+ done: 0,
34
+ failed: 0,
35
+ blocked: 0,
36
+ };
37
+
38
+ for (const task of backlog.tasks) {
39
+ const status = String(task.status || "pending");
40
+ if (status === "pending") summary.pending += 1;
41
+ if (status === "in_progress") summary.inProgress += 1;
42
+ if (status === "done") summary.done += 1;
43
+ if (status === "failed") summary.failed += 1;
44
+ if (status === "blocked") summary.blocked += 1;
45
+ }
46
+
47
+ return summary;
48
+ }
49
+
50
+ export function dependenciesSatisfied(backlog, task) {
51
+ const deps = Array.isArray(task.dependsOn) ? task.dependsOn : [];
52
+ if (!deps.length) return true;
53
+ return deps.every((depId) => backlog.tasks.some((item) => item.id === depId && item.status === "done"));
54
+ }
55
+
56
+ export function unresolvedDependencies(backlog, task) {
57
+ const deps = Array.isArray(task.dependsOn) ? task.dependsOn : [];
58
+ return deps.filter(
59
+ (depId) => !backlog.tasks.some((item) => item.id === depId && item.status === "done"),
60
+ );
61
+ }
62
+
63
+ export function isHighRiskTask(task) {
64
+ return ["medium", "high", "critical"].includes(String(task.risk || "low"));
65
+ }
66
+
67
+ function withinRiskThreshold(task, threshold = "low") {
68
+ const taskRisk = String(task.risk || "low").toLowerCase();
69
+ const normalizedThreshold = String(threshold || "low").toLowerCase();
70
+ return (riskRank[taskRisk] ?? riskRank.low) <= (riskRank[normalizedThreshold] ?? riskRank.low);
71
+ }
72
+
73
+ export function analyzeExecution(backlog, options = {}) {
74
+ const allowHighRisk = Boolean(options.allowHighRisk);
75
+ const maxRisk = options.maxRisk || "low";
76
+ const pendingTasks = backlog.tasks.filter((task) => String(task.status || "pending") === "pending");
77
+ const readyTasks = pendingTasks.filter((task) => dependenciesSatisfied(backlog, task));
78
+ const executableTasks = readyTasks.filter((task) => allowHighRisk || withinRiskThreshold(task, maxRisk));
79
+
80
+ if (executableTasks.length) {
81
+ return {
82
+ state: "ready",
83
+ task: sortTasks(backlog, executableTasks)[0],
84
+ blockedTask: null,
85
+ blockedReason: "",
86
+ unresolved: [],
87
+ };
88
+ }
89
+
90
+ const activeTask = backlog.tasks.find((task) => String(task.status || "pending") === "in_progress");
91
+ if (activeTask) {
92
+ return {
93
+ state: "blocked_in_progress",
94
+ task: null,
95
+ blockedTask: activeTask,
96
+ blockedReason: `存在未收束的进行中任务:${activeTask.title}`,
97
+ unresolved: [],
98
+ };
99
+ }
100
+
101
+ const failedTask = backlog.tasks.find((task) => ["failed", "blocked"].includes(String(task.status || "pending")));
102
+ if (failedTask) {
103
+ return {
104
+ state: "blocked_failed",
105
+ task: null,
106
+ blockedTask: failedTask,
107
+ blockedReason: `存在需要人工介入的失败/阻塞任务:${failedTask.title}`,
108
+ unresolved: [],
109
+ };
110
+ }
111
+
112
+ if (readyTasks.length) {
113
+ const riskBlockedTask = sortTasks(backlog, readyTasks)[0];
114
+ return {
115
+ state: "blocked_risk",
116
+ task: null,
117
+ blockedTask: riskBlockedTask,
118
+ blockedReason: `后续任务风险超过自动驾驶阈值:${riskBlockedTask.title}`,
119
+ unresolved: [],
120
+ };
121
+ }
122
+
123
+ if (pendingTasks.length) {
124
+ const dependencyBlockedTask = sortTasks(backlog, pendingTasks)[0];
125
+ const unresolved = unresolvedDependencies(backlog, dependencyBlockedTask);
126
+ return {
127
+ state: "blocked_dependencies",
128
+ task: null,
129
+ blockedTask: dependencyBlockedTask,
130
+ blockedReason: unresolved.length
131
+ ? `任务 ${dependencyBlockedTask.title} 仍依赖未完成任务:${unresolved.join(", ")}`
132
+ : `任务 ${dependencyBlockedTask.title} 当前不可执行,请检查依赖与状态。`,
133
+ unresolved,
134
+ };
135
+ }
136
+
137
+ return {
138
+ state: "done",
139
+ task: null,
140
+ blockedTask: null,
141
+ blockedReason: "",
142
+ unresolved: [],
143
+ };
144
+ }
145
+
146
+ export function selectNextTask(backlog, options = {}) {
147
+ return analyzeExecution(backlog, options).task;
148
+ }
149
+
150
+ export function getTask(backlog, taskId) {
151
+ return backlog.tasks.find((task) => task.id === taskId) || null;
152
+ }
153
+
154
+ export function updateTask(backlog, taskId, patch) {
155
+ backlog.tasks = backlog.tasks.map((task) => (
156
+ task.id === taskId
157
+ ? { ...task, ...patch }
158
+ : task
159
+ ));
160
+ }
161
+
162
+ export function renderTaskSummary(task) {
163
+ return [
164
+ `任务:${task.title}`,
165
+ `编号:${task.id}`,
166
+ `状态:${task.status || "pending"}`,
167
+ `优先级:${task.priority || "P2"}`,
168
+ `风险:${task.risk || "low"}`,
169
+ ].join("\n");
170
+ }