helloagents 3.0.9-beta.1 → 3.0.10

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.
Files changed (42) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/README.md +403 -649
  5. package/README_CN.md +405 -649
  6. package/bootstrap-lite.md +18 -18
  7. package/bootstrap.md +22 -22
  8. package/gemini-extension.json +1 -1
  9. package/package.json +2 -12
  10. package/scripts/cli-codex-config.mjs +8 -2
  11. package/scripts/cli-codex.mjs +5 -3
  12. package/scripts/cli-lifecycle.mjs +11 -0
  13. package/scripts/cli-messages.mjs +3 -3
  14. package/scripts/cli-toml-values.mjs +25 -0
  15. package/scripts/cli-toml.mjs +15 -0
  16. package/scripts/guard.mjs +2 -2
  17. package/scripts/notify-context.mjs +7 -7
  18. package/scripts/notify-events.mjs +0 -8
  19. package/scripts/notify-gates.mjs +128 -0
  20. package/scripts/notify-ui.mjs +3 -0
  21. package/scripts/notify.mjs +44 -76
  22. package/scripts/project-storage.mjs +5 -5
  23. package/scripts/workflow-core.mjs +5 -5
  24. package/scripts/workflow-recommendation.mjs +4 -4
  25. package/scripts/workflow-state.mjs +1 -1
  26. package/skills/commands/auto/SKILL.md +13 -13
  27. package/skills/commands/build/SKILL.md +6 -6
  28. package/skills/commands/clean/SKILL.md +6 -6
  29. package/skills/commands/commit/SKILL.md +2 -2
  30. package/skills/commands/help/SKILL.md +3 -3
  31. package/skills/commands/idea/SKILL.md +5 -5
  32. package/skills/commands/init/SKILL.md +4 -4
  33. package/skills/commands/loop/SKILL.md +14 -14
  34. package/skills/commands/plan/SKILL.md +13 -13
  35. package/skills/commands/prd/SKILL.md +13 -13
  36. package/skills/commands/verify/SKILL.md +3 -3
  37. package/skills/commands/wiki/SKILL.md +5 -5
  38. package/skills/hello-subagent/SKILL.md +1 -1
  39. package/skills/hello-ui/SKILL.md +14 -14
  40. package/skills/hello-verify/SKILL.md +2 -2
  41. package/skills/helloagents/SKILL.md +3 -2
  42. package/templates/plans/contract.json +2 -2
package/bootstrap-lite.md CHANGED
@@ -172,10 +172,10 @@
172
172
  - `T0` — 只读分析、创意探索、方案比较 → 自然响应或 `~idea`
173
173
  - `T1` — 低风险小改动、明确实现、显式验证 → 直接执行或 `~build` / `~verify`
174
174
  - `T2` — 多文件功能、新项目、需要结构化产物 → `~plan` 或 `~auto`
175
- - `T3` — 高风险或不可逆链路(权限、安全、支付、数据库、生产发布等)→ 先 `~plan` / `~prd`,再 `~build` / `~verify`
175
+ - `T3` — 高风险或不可逆操作(权限、安全、支付、数据库、生产发布等)→ 先 `~plan` / `~prd`,再 `~build` / `~verify`
176
176
 
177
177
  ## 完成约束
178
- - 未激活项目且未进入方案包 / `contract.json` / 证据链路时,声称完成前必须完成与任务类型匹配的必要检查;无法执行的检查必须明确说明,不得直接宣称完成
178
+ - 未激活项目且未进入方案包 / `contract.json` / 证据文件时,声称完成前必须完成与任务类型匹配的必要检查;无法执行的检查必须明确说明,不得直接宣称完成
179
179
  - 当前项目已激活,或已存在方案包 / `contract.json` / 证据文件时,以完整流程、对应 skill 与运行时交付约束为准,不得降级为本节
180
180
  - 只读分析、创意探索、方案比较、中间进度和阻塞汇报不适用本节
181
181
 
@@ -193,14 +193,14 @@
193
193
  所有文件的创建和更新必须按 templates/ 目录中对应模板的格式执行,不可自由发挥格式。
194
194
  说明:
195
195
  - `.helloagents/` 表示项目级存储路径,也是 standby 模式的激活信号
196
- - `STATE.md`、`.ralph-*.json`、`loop-results.tsv` 等运行态文件始终保留在项目本地 `.helloagents/`
197
- - 当前 `STATE.md` 只认当前项目存储中的 `state_path`;支持会话标识时,它会落在 `.helloagents/sessions/{branch}/{session}/STATE.md`,未提供稳定会话标识时则落到 `.helloagents/sessions/{branch}/default/STATE.md`
196
+ - `state_path` 指向的状态文件、`.ralph-*.json`、`loop-results.tsv` 等运行态文件始终保留在项目本地 `.helloagents/`
197
+ - `state_path` 是状态文件的唯一位置。宿主提供会话标识时,写入 `.helloagents/sessions/{branch}/{session}/STATE.md`;没有稳定会话标识时,写入 `.helloagents/sessions/{branch}/default/STATE.md`
198
198
  - 若 helloagents.json 中 `project_store_mode = "repo-shared"`,`context.md`、`guidelines.md`、`CHANGELOG.md`、`verify.yaml`、`DESIGN.md`、`modules/`、`plans/`、`archive/` 改按当前上下文中已注入的“当前项目存储”/“项目知识/方案目录”解析;未注入具体路径时,按当前存储模式自行解析,不要假定这些文件一定实际位于当前工作树中
199
199
  templates/ 查找路径(按优先级;首次确定模板根目录后,本轮复用):
200
200
  按上文 `~command` 路由中的相同技能根目录规则确定;确定根目录后读取其中的 `templates/`。
201
201
 
202
202
  ### 流程状态(不受 kb_create_mode 控制,始终可写)
203
- - STATE.md ≤70 行,项目级恢复快照。它只用于恢复“上次做到哪里”,不是主线任务的唯一判断依据;当前用户消息、显式命令、活跃方案包 / PRD、代码与验证证据优先于 STATE.md
203
+ - 状态文件(`state_path`)— ≤70 行,用来记录“上次做到哪里”。判断当前任务时,当前用户消息、显式命令、活跃方案包 / PRD、代码与验证证据优先于状态文件
204
204
  内容:主线目标、正在做什么、关键上下文(决策/变更/假设)、下一步(具体可执行动作含文件路径)、阻塞项
205
205
  适用边界:
206
206
  - 强制创建并持续更新:`~wiki`、`~init`、`~plan`、`~build`、`~auto`、`~prd`、`~loop`,以及进入统一执行流程/已激活项目的连续任务
@@ -208,14 +208,14 @@ templates/ 查找路径(按优先级;首次确定模板根目录后,本轮
208
208
  - 已有则更新:`~verify`、`~review`(兼容别名)、`~test`、`~commit`
209
209
  - 不创建:`~help`、`~idea`、普通问答、一次性只读任务、子代理自身执行过程、压缩/恢复钩子
210
210
  更新规则:
211
- - 属于“强制创建并持续更新”范围且文件不存在时,按 templates/STATE.md 创建
212
- - 每次更新是重写,不是追加。STATE.md 永远反映“此刻”的状态,不是历史
211
+ - 属于“强制创建并持续更新”范围且状态文件不存在时,按 templates/STATE.md 创建
212
+ - 每次更新是重写,不是追加。状态文件只记录当前状态,不记录历史
213
213
  - 更新时机:任务开始、关键决策落定、子任务完成、遇到/解除阻塞、任务完成
214
- - 长流程中 STATE.md 过时就立即重写,不等任务结束
215
- - 恢复时先看当前用户消息,确认仍是同一任务再按 STATE.md 接续;否则按当前消息、活跃方案包与代码事实重建主线,并立即重写 STATE.md
216
- - `.helloagents/` 里只有 `STATE.md` 时,它只是恢复锚点,不是项目规则或自动授权
217
- - 若宿主进入压缩/恢复前置阶段,且当前任务属于 STATE.md 适用范围,必须先确认 STATE.md 已同步到最新
218
- - 自检:如果现在上下文被压缩,下一轮能否凭 STATE.md 找回进度?不能 → 该更新了
214
+ - 长流程中状态文件过时就立即重写,不等任务结束
215
+ - 恢复时先看当前用户消息;如果仍是同一任务,再参考状态文件;否则按当前消息、活跃方案包与代码事实重新判断任务,并立即重写状态文件
216
+ - 当前项目只有状态文件时,它只是恢复参考,不是项目规则或自动授权
217
+ - 若宿主进入压缩/恢复前置阶段,且当前任务属于状态文件适用范围,必须先确认状态文件已同步到最新
218
+ - 自检:如果现在上下文被压缩,下一轮能否凭状态文件找回进度?不能 → 该更新了
219
219
  - “关键上下文”只保留恢复所需的信息,已不再相关的决策和变更移除
220
220
  - DESIGN.md — 项目级稳定 UI 契约(仅 UI 项目),`~plan` / `~auto` / `~prd` 创建或更新;不存在且当前任务涉及 UI → 按 templates/DESIGN.md 创建;不替代单次需求的 `plan.md`
221
221
  - plans/{feature}/ — 活跃方案包。`~plan` / `~auto` 生成:`requirements.md` + `plan.md` + `tasks.md` + `contract.json`;`~prd` 生成:`prd/` 目录(多维度文档)+ `tasks.md` + `decisions.md` + `contract.json`
@@ -223,7 +223,7 @@ templates/ 查找路径(按优先级;首次确定模板根目录后,本轮
223
223
  - archive/YYYY-MM/ — 已归档的方案包(整个 plans/{feature}/ 目录移入)
224
224
  - archive/_index.md — 归档索引
225
225
 
226
- ### 知识沉淀(受 kb_create_mode 控制,0=关闭/1=已激活项目或全局模式中编码自动/2=已激活项目或全局模式中始终)
226
+ ### 知识记录(受 kb_create_mode 控制,0=关闭/1=已激活项目或全局模式中编码自动/2=已激活项目或全局模式中始终)
227
227
  - context.md — 项目架构、技术栈、目录结构、模块索引
228
228
  - guidelines.md — 编码约定(仅含非显而易见的约定)
229
229
  - CHANGELOG.md — 变更历史
@@ -242,17 +242,17 @@ templates/ 查找路径(按优先级;首次确定模板根目录后,本轮
242
242
  主线判断依据优先级:
243
243
  1. 当前用户最新消息、显式 `~command`、本轮已确认的范围与结论
244
244
  2. 当前活跃方案包 / PRD、代码与验证证据
245
- 3. 当前 `STATE.md`(始终取当前项目存储中的 `state_path`;恢复快照,只用于补齐最近进度)
246
- 4. 其他知识沉淀与历史归档
245
+ 3. 当前状态文件(`state_path`,只用于补齐最近进度)
246
+ 4. 其他知识记录与历史归档
247
247
 
248
248
  ### .helloagents/ 文件读取优先级
249
249
  以下文件在任务需要时按需读取,按优先级分层:
250
250
  说明:
251
- - Tier 1 `STATE.md` 始终读取当前项目存储中的 `state_path`
251
+ - Tier 1 始终读取当前 `state_path`
252
252
  - Tier 2 / Tier 3 中的 `.helloagents/...` 路径默认按项目级存储路径解析;`project_store_mode=repo-shared` 时按共享知识/方案目录解析
253
253
 
254
- Tier 1 — 恢复当前链路时优先读取:
255
- - 当前 `STATE.md` → 恢复快照(始终取当前项目存储中的 `state_path`;先确认当前消息仍是同一任务,再用它找回最近进度)
254
+ Tier 1 — 恢复当前任务时优先读取:
255
+ - 当前状态文件(`state_path`)→ 先确认当前消息仍是同一任务,再用它找回最近进度
256
256
 
257
257
  Tier 2 — 理解项目时读取:
258
258
  - .helloagents/context.md → 项目架构、技术栈、目录结构、模块索引
package/bootstrap.md CHANGED
@@ -176,14 +176,14 @@
176
176
  - `T0` — 只读分析、创意探索、方案比较 → 自然响应或 `~idea`
177
177
  - `T1` — 低风险小改动、明确实现、显式验证 → 直接执行或 `~build` / `~verify`
178
178
  - `T2` — 多文件功能、新项目、需要结构化产物 → `~plan` 或 `~auto`
179
- - `T3` — 高风险或不可逆链路(权限、安全、支付、数据库、生产发布等)→ 先 `~plan` / `~prd`,再 `~build` / `~verify`,必要时单独确认
179
+ - `T3` — 高风险或不可逆操作(权限、安全、支付、数据库、生产发布等)→ 先 `~plan` / `~prd`,再 `~build` / `~verify`,必要时单独确认
180
180
  - 创意探索 / 方案比较 → `~idea`
181
181
  - 明确实现 / 小范围修复 → `~build`
182
182
  - 为指定模块编写测试 → `~test`
183
183
  - 结构化规划 / 新功能 / 新项目 → `~plan`
184
184
  - 完整产品规格 → `~prd`
185
185
  - 审查 / 执行验证 → `~verify`
186
- - 不确定或希望端到端自动推进时由 `~auto` 自动编排
186
+ - 不确定或希望端到端自动推进时使用 `~auto`
187
187
 
188
188
  当前项目只要已建立 `.helloagents/`(例如执行过 `~wiki`、`~init`,或已进入项目级连续流程),就按项目级完整流程执行。
189
189
 
@@ -209,9 +209,9 @@ hello-* 技能读取路径:`{HELLOAGENTS_READ_ROOT}/skills/{技能名}/SKILL.m
209
209
  - `~plan` 生成 `requirements.md`、`plan.md`、`tasks.md`、`contract.json`
210
210
  - `~prd` 生成 PRD 维度文档、`tasks.md`、`decisions.md`
211
211
  - `~build` 读取现有方案包并做定位,不重复发明方案
212
- - `contract.json` 是方案包的机器契约,至少明确 `verifyMode`、`reviewerFocus`、`testerFocus`;只有在 T3 / UI / 高风险链路确有收益时,才额外声明 `advisor`;进入验证或最终交付前,优先消费它而不是从自然语言描述里回推验证路径
212
+ - `contract.json` 是方案包的机器契约,至少明确 `verifyMode`、`reviewerFocus`、`testerFocus`;只有在 T3 / UI / 高风险流程确有收益时,才额外声明 `advisor`;进入验证或最终交付前,优先消费它而不是从自然语言描述里回推验证路径
213
213
  - 涉及 UI 时,设计约束优先级固定为:当前 `plan.md` / PRD UI 决策 → 逻辑 `.helloagents/DESIGN.md`(实际路径按当前项目存储模式解析) → 通用 UI 规则
214
- - `~idea` 在输出比较与推荐后结束,不进入实现,也不创建 `.helloagents/`、`STATE.md` 或方案包
214
+ - `~idea` 在输出比较与推荐后结束,不进入实现,也不创建 `.helloagents/`、状态文件或方案包
215
215
 
216
216
  ### 4. BUILD — 实现
217
217
  进入实现时,读取 PLAN 阶段标记的技能 SKILL.md(按上方 hello-* 技能查找路径读取 `skills/{技能名}/SKILL.md`),按其规范执行。
@@ -233,10 +233,10 @@ hello-* 技能读取路径:`{HELLOAGENTS_READ_ROOT}/skills/{技能名}/SKILL.m
233
233
  非编码任务(文档 / 方案 / 审查等):
234
234
  - 收集已激活技能的交付检查清单,逐项确认通过
235
235
 
236
- ### 6. CONSOLIDATE — 状态、沉淀与归档
236
+ ### 6. CONSOLIDATE — 状态、资料与归档
237
237
  所有任务:
238
238
  - 有方案包且准备报告完成 → 优先调用 `scripts/closeout-state.mjs write` 写 `.helloagents/.ralph-closeout.json`,记录“需求覆盖”和“交付清单”;每项写明 `PASS` / `BLOCKED` 与简要摘要,再进入最终交付
239
- - `STATE.md` 维护:按上文“流程状态”中的适用边界执行。属于“强制创建并持续更新”范围时,重写当前 `STATE.md`(始终使用当前项目存储中的 `state_path`;“正在做什么”更新为已完成,清空关键上下文 / 下一步 / 阻塞项);属于“已有则更新”范围时,仅在文件已存在时重写;属于“不创建”范围时不生成此文件
239
+ - 状态文件维护:按上文“流程状态”中的适用范围执行。属于“强制创建并持续更新”范围时,重写 `state_path` 指向的文件(“正在做什么”更新为已完成,清空关键上下文 / 下一步 / 阻塞项);属于“已有则更新”范围时,仅在文件已存在时重写;属于“不创建”范围时不生成此文件
240
240
  - 有方案包且任务已完成 → 将整个 `plans/{feature}/` 目录归档到 `.helloagents/archive/YYYY-MM/`,并更新 `archive/_index.md`。清理临时文件(`loop-results.tsv`、`.ralph-breaker.json`、`.ralph-verify.json`、`.ralph-review.json`、`.ralph-closeout.json`)
241
241
  - 按 `kb_create_mode` 同步知识库(0=关闭 / 1=已激活项目或全局模式中编码自动 / 2=已激活项目或全局模式中始终):
242
242
  - `.helloagents/` 不存在则按 templates/ 创建知识库文件(`context.md`、`guidelines.md`、`verify.yaml`、`CHANGELOG.md`、`modules/`)
@@ -262,14 +262,14 @@ hello-* 技能读取路径:`{HELLOAGENTS_READ_ROOT}/skills/{技能名}/SKILL.m
262
262
  所有文件的创建和更新必须按 templates/ 目录中对应模板的格式执行,不可自由发挥格式。
263
263
  说明:
264
264
  - `.helloagents/` 表示项目级存储路径,也是 standby 模式的激活信号
265
- - `STATE.md`、`.ralph-*.json`、`loop-results.tsv` 等运行态文件始终保留在项目本地 `.helloagents/`
266
- - 当前 `STATE.md` 只认当前项目存储中的 `state_path`;支持会话标识时,它会落在 `.helloagents/sessions/{branch}/{session}/STATE.md`,未提供稳定会话标识时则落到 `.helloagents/sessions/{branch}/default/STATE.md`
265
+ - `state_path` 指向的状态文件、`.ralph-*.json`、`loop-results.tsv` 等运行态文件始终保留在项目本地 `.helloagents/`
266
+ - `state_path` 是状态文件的唯一位置。宿主提供会话标识时,写入 `.helloagents/sessions/{branch}/{session}/STATE.md`;没有稳定会话标识时,写入 `.helloagents/sessions/{branch}/default/STATE.md`
267
267
  - 若 helloagents.json 中 `project_store_mode = "repo-shared"`,`context.md`、`guidelines.md`、`CHANGELOG.md`、`verify.yaml`、`DESIGN.md`、`modules/`、`plans/`、`archive/` 改按当前上下文中已注入的“当前项目存储”/“项目知识/方案目录”解析;未注入具体路径时,按当前存储模式自行解析,不要假定这些文件一定实际位于当前工作树中
268
268
  templates/ 查找路径(按优先级;首次确定模板根目录后,本轮复用):
269
269
  按上文相同的技能根目录规则确定;确定根目录后读取其中的 `templates/`。
270
270
 
271
271
  ### 流程状态(不受 kb_create_mode 控制,始终可写)
272
- - STATE.md ≤70 行,项目级恢复快照。它只用于恢复“上次做到哪里”,不是主线任务的唯一判断依据;当前用户消息、显式命令、活跃方案包 / PRD、代码与验证证据优先于 STATE.md
272
+ - 状态文件(`state_path`)— ≤70 行,用来记录“上次做到哪里”。判断当前任务时,当前用户消息、显式命令、活跃方案包 / PRD、代码与验证证据优先于状态文件
273
273
  内容:主线目标、正在做什么、关键上下文(决策/变更/假设)、下一步(具体可执行动作含文件路径)、阻塞项
274
274
  适用边界:
275
275
  - 强制创建并持续更新:`~wiki`、`~init`、`~plan`、`~build`、`~auto`、`~prd`、`~loop`,以及进入统一执行流程/已激活项目的连续任务
@@ -277,14 +277,14 @@ templates/ 查找路径(按优先级;首次确定模板根目录后,本轮
277
277
  - 已有则更新:`~verify`、`~review`(兼容别名)、`~test`、`~commit`
278
278
  - 不创建:`~help`、`~idea`、普通问答、一次性只读任务、子代理自身执行过程、压缩/恢复钩子
279
279
  更新规则:
280
- - 属于“强制创建并持续更新”范围且文件不存在时,按 templates/STATE.md 创建
281
- - 每次更新是重写,不是追加。STATE.md 永远反映“此刻”的状态,不是历史
280
+ - 属于“强制创建并持续更新”范围且状态文件不存在时,按 templates/STATE.md 创建
281
+ - 每次更新是重写,不是追加。状态文件只记录当前状态,不记录历史
282
282
  - 更新时机:任务开始、关键决策落定、子任务完成、遇到/解除阻塞、任务完成
283
- - 长流程中 STATE.md 过时就立即重写,不等任务结束
284
- - 恢复时先看当前用户消息,确认仍是同一任务再按 STATE.md 接续;否则按当前消息、活跃方案包与代码事实重建主线,并立即重写 STATE.md
285
- - `.helloagents/` 里只有 `STATE.md` 时,它只是恢复锚点,不是项目规则或自动授权
286
- - 若宿主进入压缩/恢复前置阶段,且当前任务属于 STATE.md 适用范围,必须先确认 STATE.md 已同步到最新
287
- - 自检:如果现在上下文被压缩,下一轮能否凭 STATE.md 找回进度?不能 → 该更新了
283
+ - 长流程中状态文件过时就立即重写,不等任务结束
284
+ - 恢复时先看当前用户消息;如果仍是同一任务,再参考状态文件;否则按当前消息、活跃方案包与代码事实重新判断任务,并立即重写状态文件
285
+ - 当前项目只有状态文件时,它只是恢复参考,不是项目规则或自动授权
286
+ - 若宿主进入压缩/恢复前置阶段,且当前任务属于状态文件适用范围,必须先确认状态文件已同步到最新
287
+ - 自检:如果现在上下文被压缩,下一轮能否凭状态文件找回进度?不能 → 该更新了
288
288
  - “关键上下文”只保留恢复所需的信息,已不再相关的决策和变更移除
289
289
  - DESIGN.md — 项目级稳定 UI 契约(仅 UI 项目),`~plan` / `~auto` / `~prd` 创建或更新;不存在且当前任务涉及 UI → 按 templates/DESIGN.md 创建;不替代单次需求的 `plan.md`
290
290
  - plans/{feature}/ — 活跃方案包。`~plan` / `~auto` 生成:`requirements.md` + `plan.md` + `tasks.md` + `contract.json`;`~prd` 生成:`prd/` 目录(多维度文档)+ `tasks.md` + `decisions.md` + `contract.json`
@@ -292,7 +292,7 @@ templates/ 查找路径(按优先级;首次确定模板根目录后,本轮
292
292
  - archive/YYYY-MM/ — 已归档的方案包(整个 plans/{feature}/ 目录移入)
293
293
  - archive/_index.md — 归档索引
294
294
 
295
- ### 知识沉淀(受 kb_create_mode 控制,0=关闭/1=已激活项目或全局模式中编码自动/2=已激活项目或全局模式中始终)
295
+ ### 知识记录(受 kb_create_mode 控制,0=关闭/1=已激活项目或全局模式中编码自动/2=已激活项目或全局模式中始终)
296
296
  - context.md — 项目架构、技术栈、目录结构、模块索引
297
297
  - guidelines.md — 编码约定(仅含非显而易见的约定)
298
298
  - CHANGELOG.md — 变更历史
@@ -311,17 +311,17 @@ templates/ 查找路径(按优先级;首次确定模板根目录后,本轮
311
311
  主线判断依据优先级:
312
312
  1. 当前用户最新消息、显式 `~command`、本轮已确认的范围与结论
313
313
  2. 当前活跃方案包 / PRD、代码与验证证据
314
- 3. 当前 `STATE.md`(始终取当前项目存储中的 `state_path`;恢复快照,只用于补齐最近进度)
315
- 4. 其他知识沉淀与历史归档
314
+ 3. 当前状态文件(`state_path`,只用于补齐最近进度)
315
+ 4. 其他知识记录与历史归档
316
316
 
317
317
  ### .helloagents/ 文件读取优先级
318
318
  以下文件在任务需要时按需读取,按优先级分层:
319
319
  说明:
320
- - Tier 1 `STATE.md` 始终读取当前项目存储中的 `state_path`
320
+ - Tier 1 始终读取当前 `state_path`
321
321
  - Tier 2 / Tier 3 中的 `.helloagents/...` 路径默认按项目级存储路径解析;`project_store_mode=repo-shared` 时按共享知识/方案目录解析
322
322
 
323
- Tier 1 — 恢复当前链路时优先读取:
324
- - 当前 `STATE.md` → 恢复快照(始终取当前项目存储中的 `state_path`;先确认当前消息仍是同一任务,再用它找回最近进度)
323
+ Tier 1 — 恢复当前任务时优先读取:
324
+ - 当前状态文件(`state_path`)→ 先确认当前消息仍是同一任务,再用它找回最近进度
325
325
 
326
326
  Tier 2 — 理解项目时读取:
327
327
  - .helloagents/context.md → 项目架构、技术栈、目录结构、模块索引
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helloagents",
3
- "version": "3.0.9-beta.1",
3
+ "version": "3.0.10",
4
4
  "description": "Quality-driven orchestration kernel for AI CLIs",
5
5
  "contextFileName": "bootstrap.md",
6
6
  "author": "HelloWind",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helloagents",
3
- "version": "3.0.9-beta.1",
3
+ "version": "3.0.10",
4
4
  "type": "module",
5
5
  "description": "HelloAGENTS — The orchestration kernel that makes any AI CLI smarter. Adds intelligent routing, quality verification (Ralph Loop), safety guards, and notifications.",
6
6
  "author": "HelloWind",
@@ -36,17 +36,7 @@
36
36
  ".claude-plugin/",
37
37
  ".codex-plugin/"
38
38
  ],
39
- "keywords": [
40
- "ai",
41
- "agent",
42
- "claude",
43
- "codex",
44
- "cli",
45
- "orchestration",
46
- "subagent",
47
- "hooks",
48
- "skills"
49
- ],
39
+ "keywords": ["ai", "agent", "claude", "codex", "cli", "orchestration", "subagent", "hooks", "skills"],
50
40
  "engines": {
51
41
  "node": ">=18"
52
42
  }
@@ -27,7 +27,13 @@ export function isManagedCodexModelInstruction(line = '') {
27
27
  }
28
28
 
29
29
  export function isManagedCodexNotify(line = '') {
30
- return line.includes('codex-notify')
30
+ const value = String(line || '').replace(/\\/g, '/')
31
+ return value.includes(CODEX_MANAGED_TOML_COMMENT)
32
+ || (
33
+ value.includes('codex-notify')
34
+ && value.includes('/scripts/notify.mjs')
35
+ && /(^|[/\\])helloagents([/\\]|-|$)|[/\\]plugins[/\\]helloagents[/\\]/i.test(value)
36
+ )
31
37
  }
32
38
 
33
39
  export function isManagedCodexBackupInstruction(line = '') {
@@ -51,7 +57,7 @@ function formatManagedCodexNotifyValue(notifyScriptPath) {
51
57
  }
52
58
 
53
59
  function formatManagedCodexNotifyLine(notifyScriptPath) {
54
- return `notify = ${formatManagedCodexNotifyValue(notifyScriptPath)}`
60
+ return `notify = ${formatManagedCodexNotifyValue(notifyScriptPath)} ${CODEX_MANAGED_TOML_COMMENT}`
55
61
  }
56
62
 
57
63
  function removeTopLevelLinesBeingReplaced(toml, lines) {
@@ -20,6 +20,7 @@ import {
20
20
  } from './cli-codex-config.mjs';
21
21
  import {
22
22
  readTopLevelTomlLine,
23
+ readTopLevelTomlBlock,
23
24
  readTomlKeyInSection,
24
25
  removeTomlKeyInSection,
25
26
  ensureTomlKeyInSection,
@@ -98,7 +99,8 @@ function removeCodexMarketplaceEntry(marketplaceFile) {
98
99
  }
99
100
  if (!removedHelloagents) return false;
100
101
  if (!nextPlugins.length) {
101
- removeIfExists(marketplaceFile);
102
+ marketplace.plugins = [];
103
+ safeWrite(marketplaceFile, JSON.stringify(marketplace, null, 2) + '\n');
102
104
  return true;
103
105
  }
104
106
  marketplace.plugins = nextPlugins;
@@ -130,7 +132,7 @@ function cleanupCodexManagedConfig(configPath, { removePluginConfig = false } =
130
132
  let toml = safeRead(configPath) || '';
131
133
 
132
134
  const currentModelInstructions = readTopLevelTomlLine(toml, 'model_instructions_file');
133
- const currentNotify = readTopLevelTomlLine(toml, 'notify');
135
+ const currentNotify = readTopLevelTomlBlock(toml, 'notify');
134
136
  const currentCodexHooks = readTomlKeyInSection(toml, '[features]', 'codex_hooks');
135
137
 
136
138
  const shouldRestoreModelInstructions = isManagedCodexModelInstruction(currentModelInstructions);
@@ -153,7 +155,7 @@ function cleanupCodexManagedConfig(configPath, { removePluginConfig = false } =
153
155
  }
154
156
 
155
157
  const backupModelInstructions = readTopLevelTomlLine(backupToml, 'model_instructions_file');
156
- const backupNotify = readTopLevelTomlLine(backupToml, 'notify');
158
+ const backupNotify = readTopLevelTomlBlock(backupToml, 'notify');
157
159
  const backupCodexHooks = readTomlKeyInSection(backupToml, '[features]', 'codex_hooks');
158
160
 
159
161
  toml = restoreCodexTopLevelConfig(toml, {
@@ -181,6 +181,17 @@ function runAllHostsLifecycle(action, explicitMode) {
181
181
  }
182
182
 
183
183
  const settings = readSettings(true)
184
+ if (action === 'update' && !explicitMode) {
185
+ for (const host of HOSTS) {
186
+ const mode = resolveHostMode(host, '', settings)
187
+ const result = runHostLifecycle(runtime, action, host, mode)
188
+ if (!result.skipped) setTrackedHostMode(settings, host, mode)
189
+ }
190
+ writeSettings(settings)
191
+ runtime.printInstallMsg(settings.install_mode || DEFAULTS.install_mode, 'refresh')
192
+ return
193
+ }
194
+
184
195
  const mode = resolveInstallMode(explicitMode, settings)
185
196
  if (explicitMode) settings.install_mode = explicitMode
186
197
  installAllHosts(runtime, mode)
@@ -44,8 +44,8 @@ function renderInstallMessage(context, mode, state) {
44
44
  }
45
45
  return msg(
46
46
  refresh
47
- ? ' global 模式已刷新。\n Claude Code / Gemini 请保持插件已安装;Codex 原生本地插件链路已重装并同步最新文件。'
48
- : ' 所有项目将自动启用完整 HelloAGENTS 规则。\n Claude Code / Gemini 请手动安装插件;Codex 已自动走原生本地插件链路。',
47
+ ? ' global 模式已刷新。\n Claude Code / Gemini 请保持插件已安装;Codex 原生本地插件已重装并同步最新文件。'
48
+ : ' 所有项目将自动启用完整 HelloAGENTS 规则。\n Claude Code / Gemini 请手动安装插件;Codex 已自动安装原生本地插件。',
49
49
  refresh
50
50
  ? ' Global mode refreshed.\n Keep Claude Code / Gemini plugins installed; Codex native local-plugin files were reinstalled and synced.'
51
51
  : ' All projects will use full HelloAGENTS rules.\n Install Claude Code / Gemini plugins manually; Codex now uses the native local-plugin path automatically.',
@@ -92,7 +92,7 @@ ${msg('单 CLI 管理', 'Scoped CLI management')}:
92
92
  ${msg('诊断', 'Diagnostics')}:
93
93
  helloagents doctor
94
94
  helloagents doctor codex --json
95
- ${msg('检查 carrier、链接、hooks、配置注入、Codex 插件链路、受管 model_instructions_file 指向与版本漂移', 'Checks carriers, links, hooks, config injections, the Codex plugin chain, managed model_instructions_file targeting, and version drift')}
95
+ ${msg('检查 carrier、链接、hooks、配置注入、Codex 插件安装、受管 model_instructions_file 指向与版本漂移', 'Checks carriers, links, hooks, config injections, Codex plugin installation, managed model_instructions_file targeting, and version drift')}
96
96
 
97
97
  ${msg('卸载', 'Uninstall')}:
98
98
  helloagents cleanup ${msg('(推荐先执行,显式清理所有 CLI 注入/链接)', '(recommended first, explicitly cleans CLI injections/links)')}
@@ -0,0 +1,25 @@
1
+ export function getTomlArrayDepthDelta(text) {
2
+ let depth = 0
3
+ let quoted = false
4
+ let escaped = false
5
+
6
+ for (const char of String(text || '')) {
7
+ if (escaped) {
8
+ escaped = false
9
+ continue
10
+ }
11
+ if (char === '\\' && quoted) {
12
+ escaped = true
13
+ continue
14
+ }
15
+ if (char === '"') {
16
+ quoted = !quoted
17
+ continue
18
+ }
19
+ if (quoted) continue
20
+ if (char === '[') depth += 1
21
+ if (char === ']') depth -= 1
22
+ }
23
+
24
+ return depth
25
+ }
@@ -3,6 +3,8 @@
3
3
  * Targets the small subset of TOML structures used by Codex CLI config.
4
4
  */
5
5
 
6
+ import { getTomlArrayDepthDelta } from './cli-toml-values.mjs'
7
+
6
8
  export function isTomlTableHeader(line) {
7
9
  const trimmed = String(line || '').trim();
8
10
  return trimmed.startsWith('[') && trimmed.endsWith(']');
@@ -62,6 +64,19 @@ function findTopLevelTomlBlock(text, key) {
62
64
  const closeIndex = normalized.indexOf('"""', openIndex + 3);
63
65
  end = closeIndex >= 0 ? closeIndex + 3 : normalized.length;
64
66
  }
67
+ if (value.startsWith('[')) {
68
+ let depth = getTomlArrayDepthDelta(firstLine.slice(firstLine.indexOf('=') + 1));
69
+ let lineStart = firstLineEnd + (normalized[firstLineEnd] === '\n' ? 1 : 0);
70
+
71
+ while (depth > 0 && lineStart < normalized.length) {
72
+ const lineEndIndex = normalized.indexOf('\n', lineStart);
73
+ const nextLineEnd = lineEndIndex >= 0 ? lineEndIndex : normalized.length;
74
+ const nextLine = normalized.slice(lineStart, nextLineEnd);
75
+ depth += getTomlArrayDepthDelta(nextLine);
76
+ end = nextLineEnd;
77
+ lineStart = nextLineEnd + 1;
78
+ }
79
+ }
65
80
 
66
81
  while (end < normalized.length && normalized[end] === '\n') {
67
82
  end += 1;
package/scripts/guard.mjs CHANGED
@@ -124,7 +124,7 @@ function buildPostWriteWarnings(data) {
124
124
  const filePath = data.tool_input?.file_path || ''
125
125
  return [
126
126
  ...(detectIdeaBoundaryContext(data)?.zeroSideEffect
127
- ? ['~idea 本轮要求只读探索;检测到写入工具落地,请回退到探索输出或升级到 ~plan / ~build / ~prd / ~auto 后再修改文件']
127
+ ? ['~idea 本轮要求只读探索;检测到写入文件的工具调用,请回到探索输出,或升级到 ~plan / ~build / ~prd / ~auto 后再修改文件']
128
128
  : []),
129
129
  ...scanUnrequestedFiles(filePath, data.tool_name),
130
130
  ...(content ? [...scanForSecrets(content), ...scanDangerousPackages(content, filePath)] : []),
@@ -195,7 +195,7 @@ function handleHighRiskCommand(data, command) {
195
195
  function emitShellWarnings(data, command, highRiskWarnings, shellSafetyWarnings) {
196
196
  const sections = []
197
197
  if (highRiskWarnings.length > 0) {
198
- sections.push(`⚠️ [HelloAGENTS 高风险链路提醒] 检测到高风险命令:\n${highRiskWarnings.map((warning) => ` - ${warning}`).join('\n')}\n请确认已完成相应规划/审查并获得必要授权。`)
198
+ sections.push(`⚠️ [HelloAGENTS 高风险操作提醒] 检测到高风险命令:\n${highRiskWarnings.map((warning) => ` - ${warning}`).join('\n')}\n请确认已完成相应规划/审查并获得必要授权。`)
199
199
  }
200
200
  if (shellSafetyWarnings.length > 0) {
201
201
  sections.push(`⚠️ [HelloAGENTS Shell 安全提醒] 检测到建议调整的命令写法:\n${shellSafetyWarnings.map((warning) => ` - ${warning}`).join('\n')}\n当前仅提示,不中断执行。`)
@@ -74,8 +74,8 @@ export function buildCompactionContext({ payload, pkgRoot, settings, bootstrapFi
74
74
  const stateSyncHint = buildStateSyncHint(cwd, workflowOptions);
75
75
  if (stateSnapshot.exists && stateSnapshot.content) {
76
76
  summaryParts.push('');
77
- summaryParts.push(`## 恢复快照(从 ${stateSnapshot.statePath.replace(/\\/g, '/')} 读取,只用于找回上次停在哪)`);
78
- summaryParts.push('恢复时先看当前用户消息,确认仍是同一任务再按 STATE.md 接续。');
77
+ summaryParts.push(`## 状态文件(从 ${stateSnapshot.statePath.replace(/\\/g, '/')} 读取,只用于找回上次停在哪)`);
78
+ summaryParts.push('恢复时先看当前用户消息;如果仍是同一任务,再参考状态文件。');
79
79
  summaryParts.push(stateSnapshot.content);
80
80
  }
81
81
 
@@ -109,7 +109,7 @@ export function buildCompactionContext({ payload, pkgRoot, settings, bootstrapFi
109
109
 
110
110
  if (stateSyncHint) {
111
111
  summaryParts.push('');
112
- summaryParts.push('## STATE.md 提醒');
112
+ summaryParts.push('## 状态文件提醒');
113
113
  summaryParts.push(stateSyncHint);
114
114
  }
115
115
 
@@ -140,10 +140,10 @@ export function buildInjectContext({ source, bootstrap, settings, pkgRoot, host,
140
140
  if (projectStorageBlock) context += `\n\n${projectStorageBlock}`;
141
141
  if (workflowHint) context += `\n\n## 当前工作流提示\n${workflowHint}`;
142
142
  if (capabilityHint) context += `\n\n## 当前按需能力\n${capabilityHint}`;
143
- if (stateSyncHint) context += `\n\n## STATE.md 提醒\n${stateSyncHint}`;
143
+ if (stateSyncHint) context += `\n\n## 状态文件提醒\n${stateSyncHint}`;
144
144
  context += settingsBlock;
145
145
  if (source === 'resume' || source === 'compact') {
146
- context += `\n\n> ⚠️ 会话已恢复/压缩,请先读取当前 \`state_path\` 指向的 \`${stateSnapshot.statePath.replace(/\\/g, '/')}\` 恢复工作状态;先看当前用户消息确认仍是同一任务,再按 STATE.md 接续。`;
146
+ context += `\n\n> ⚠️ 会话已恢复/压缩,请先读取 \`state_path\` 指向的 \`${stateSnapshot.statePath.replace(/\\/g, '/')}\`;先看当前用户消息,如果仍是同一任务,再参考状态文件。`;
147
147
  }
148
148
  return context;
149
149
  }
@@ -168,8 +168,8 @@ export function buildSemanticRouteInstruction(cwd, payload = {}) {
168
168
  return [
169
169
  '当前消息未使用 ~command。',
170
170
  '请根据用户请求的真实意图选路,不依赖关键词表。',
171
- 'Delivery Tier: T0=探索/比较;T1=低风险小改动或显式验证;T2=多文件功能/新项目/需要结构化产物;T3=高风险或不可逆链路。',
172
- '路由映射:~idea=只读探索,不创建文件;~build=明确实现;~verify=审查/验证;~plan=结构化规划;~prd=重型规格;~auto=自动编排并自动衔接后续阶段。',
171
+ 'Delivery Tier: T0=探索/比较;T1=低风险小改动或显式验证;T2=多文件功能/新项目/需要结构化产物;T3=高风险或不可逆操作。',
172
+ '路由映射:~idea=只读探索,不创建文件;~build=明确实现;~verify=审查/验证;~plan=结构化规划;~prd=重型规格;~auto=自动选择并继续执行后续阶段。',
173
173
  '若判定为 T3,默认先走 ~plan / ~prd;纯审查/验证请求才优先 ~verify。',
174
174
  `涉及 UI 任务时,设计决策优先级:当前活跃 plan / PRD → ${describeProjectStoreFile(cwd, 'DESIGN.md')} → 通用 UI 规则。`,
175
175
  projectStorageHint,
@@ -5,11 +5,3 @@ export function shouldIgnoreCodexNotifyClient(client) {
5
5
  export function shouldIgnoreFormattedSubagent(lastMsg, outputFormatEnabled) {
6
6
  return outputFormatEnabled && !lastMsg.includes('【HelloAGENTS】');
7
7
  }
8
-
9
- export function claimsTaskComplete(lastMsg) {
10
- if (!lastMsg) return false;
11
- if (/^✅【HelloAGENTS】- .*(当前任务已完成|任务已完成|已修复|完成交付|done|fixed|completed|finished)/im.test(lastMsg)) {
12
- return true;
13
- }
14
- return /(当前任务已完成|任务已完成|已全部完成|已修复|修复完成|\b(done|fixed|completed|finished)\b)/i.test(lastMsg);
15
- }
@@ -0,0 +1,128 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { spawnSync } from 'node:child_process'
3
+
4
+ function truncateText(value = '') {
5
+ const text = String(value || '').trim()
6
+ return text.length > 1000 ? `${text.slice(0, 1000)}\n...(truncated)` : text
7
+ }
8
+
9
+ function buildGateErrorReason(source, detail = '') {
10
+ return [
11
+ `[HelloAGENTS Runtime] ${source} 执行失败,已暂停完成通知。`,
12
+ detail ? `原因:${detail}` : '',
13
+ '请修复脚本或重新运行验证后再报告完成。',
14
+ ].filter(Boolean).join('\n')
15
+ }
16
+
17
+ function emitGateError({
18
+ payload,
19
+ host,
20
+ source,
21
+ reason,
22
+ appendReplayEvent,
23
+ output,
24
+ }) {
25
+ appendReplayEvent(payload.cwd || process.cwd(), {
26
+ host,
27
+ event: 'runtime_gate_error',
28
+ source,
29
+ reason,
30
+ })
31
+ output({
32
+ decision: 'block',
33
+ reason,
34
+ suppressOutput: true,
35
+ })
36
+ return true
37
+ }
38
+
39
+ export function runGateScript({
40
+ payload,
41
+ host,
42
+ scriptPath,
43
+ args = [],
44
+ source,
45
+ blockEvent,
46
+ timeout,
47
+ appendReplayEvent,
48
+ output,
49
+ }) {
50
+ if (!existsSync(scriptPath)) {
51
+ return emitGateError({
52
+ payload,
53
+ host,
54
+ source,
55
+ reason: buildGateErrorReason(source, `脚本不存在:${scriptPath}`),
56
+ appendReplayEvent,
57
+ output,
58
+ })
59
+ }
60
+
61
+ const result = spawnSync(process.execPath, [scriptPath, ...args], {
62
+ input: JSON.stringify(payload),
63
+ encoding: 'utf-8',
64
+ timeout,
65
+ })
66
+
67
+ if (result.error) {
68
+ return emitGateError({
69
+ payload,
70
+ host,
71
+ source,
72
+ reason: buildGateErrorReason(source, result.error.message),
73
+ appendReplayEvent,
74
+ output,
75
+ })
76
+ }
77
+
78
+ if (result.status !== 0) {
79
+ const detail = truncateText(`${result.stderr || ''}\n${result.stdout || ''}`) || `退出码 ${result.status}`
80
+ return emitGateError({
81
+ payload,
82
+ host,
83
+ source,
84
+ reason: buildGateErrorReason(source, detail),
85
+ appendReplayEvent,
86
+ output,
87
+ })
88
+ }
89
+
90
+ const stdout = String(result.stdout || '').trim()
91
+ if (!stdout) {
92
+ return emitGateError({
93
+ payload,
94
+ host,
95
+ source,
96
+ reason: buildGateErrorReason(source, '脚本未返回有效结果'),
97
+ appendReplayEvent,
98
+ output,
99
+ })
100
+ }
101
+
102
+ let gateOutput
103
+ try {
104
+ gateOutput = JSON.parse(stdout)
105
+ } catch {
106
+ return emitGateError({
107
+ payload,
108
+ host,
109
+ source,
110
+ reason: buildGateErrorReason(source, `脚本返回了无法解析的 JSON:${truncateText(stdout)}`),
111
+ appendReplayEvent,
112
+ output,
113
+ })
114
+ }
115
+
116
+ if (gateOutput.decision === 'block') {
117
+ appendReplayEvent(payload.cwd || process.cwd(), {
118
+ host,
119
+ event: blockEvent,
120
+ source,
121
+ reason: gateOutput.reason || '',
122
+ })
123
+ output(gateOutput)
124
+ return true
125
+ }
126
+
127
+ return false
128
+ }
@@ -18,6 +18,7 @@ const NOTIFY_MESSAGES = {
18
18
  };
19
19
 
20
20
  const WIN_APPID = 'HelloAgents.Notification';
21
+ const DISABLE_OS_NOTIFICATIONS = process.env.HELLOAGENTS_DISABLE_OS_NOTIFICATIONS === '1';
21
22
 
22
23
  function escapeToastText(value = '') {
23
24
  return String(value)
@@ -55,6 +56,7 @@ function resolveWav(pkgRoot, event) {
55
56
  }
56
57
 
57
58
  export function playSound(pkgRoot, event) {
59
+ if (DISABLE_OS_NOTIFICATIONS) return;
58
60
  const wav = resolveWav(pkgRoot, event);
59
61
  if (!wav) { process.stderr.write('\x07'); return; }
60
62
  try {
@@ -83,6 +85,7 @@ function ensureWinAppId(pkgRoot) {
83
85
  }
84
86
 
85
87
  export function desktopNotify(pkgRoot, event, extra) {
88
+ if (DISABLE_OS_NOTIFICATIONS) return;
86
89
  const notification = buildDesktopNotificationContent(event, extra);
87
90
  try {
88
91
  if (PLAT === 'win32') {