helloagents 3.0.32 → 3.0.35
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/.claude-plugin/plugin.json +2 -2
- package/.codex-plugin/plugin.json +3 -4
- package/README.md +72 -73
- package/README_CN.md +72 -73
- package/bootstrap-lite.md +10 -12
- package/bootstrap.md +22 -24
- package/gemini-extension.json +1 -1
- package/install.ps1 +21 -3
- package/install.sh +19 -2
- package/package.json +2 -2
- package/scripts/capability-registry.mjs +5 -3
- package/scripts/cli-doctor-codex.mjs +150 -1
- package/scripts/cli-doctor-render.mjs +2 -1
- package/scripts/cli-lifecycle-hosts.mjs +76 -34
- package/scripts/cli-lifecycle.mjs +50 -15
- package/scripts/cli-messages.mjs +5 -5
- package/scripts/delivery-gate-messages.mjs +5 -4
- package/scripts/delivery-gate.mjs +11 -22
- package/scripts/guard.mjs +1 -1
- package/scripts/notify-closeout.mjs +61 -22
- package/scripts/notify-context.mjs +6 -6
- package/scripts/notify-payload.mjs +8 -0
- package/scripts/notify-route.mjs +1 -1
- package/scripts/notify-ui.mjs +14 -1
- package/scripts/notify.mjs +80 -4
- package/scripts/plan-contract.mjs +10 -14
- package/scripts/project-session-cleanup.mjs +45 -31
- package/scripts/qa-review-state.mjs +313 -0
- package/scripts/ralph-loop.mjs +86 -14
- package/scripts/runtime-scope.mjs +1 -3
- package/scripts/session-capsule.mjs +51 -13
- package/scripts/state-document.mjs +77 -0
- package/scripts/workflow-core.mjs +13 -19
- package/scripts/workflow-plan-files.mjs +1 -1
- package/scripts/workflow-recommendation.mjs +55 -67
- package/scripts/workflow-state.mjs +8 -8
- package/skills/commands/auto/SKILL.md +12 -12
- package/skills/commands/build/SKILL.md +9 -10
- package/skills/commands/commit/SKILL.md +1 -1
- package/skills/commands/help/SKILL.md +11 -13
- package/skills/commands/init/SKILL.md +18 -9
- package/skills/commands/loop/SKILL.md +70 -96
- package/skills/commands/plan/SKILL.md +7 -8
- package/skills/commands/prd/SKILL.md +3 -3
- package/skills/commands/qa/SKILL.md +49 -0
- package/skills/hello-ui/SKILL.md +3 -3
- package/skills/helloagents/SKILL.md +12 -15
- package/skills/qa-review/SKILL.md +92 -0
- package/templates/plans/contract.json +4 -7
- package/templates/plans/plan.md +1 -1
- package/templates/plans/tasks.md +1 -1
- package/templates/verify.yaml +1 -1
- package/scripts/review-state.mjs +0 -193
- package/scripts/verify-state.mjs +0 -175
- package/skills/commands/global/SKILL.md +0 -71
- package/skills/commands/verify/SKILL.md +0 -46
- package/skills/commands/wiki/SKILL.md +0 -57
- package/skills/hello-review/SKILL.md +0 -42
- package/skills/hello-verify/SKILL.md +0 -144
|
@@ -6,7 +6,7 @@ description: 按任务类型适用 — 建立质量驱动工作流,通过技
|
|
|
6
6
|
# HelloAGENTS
|
|
7
7
|
|
|
8
8
|
主代理触发或读取任意 skill 时,只有直接面向最终用户、且当前对话已经结束的终局交付,才按通用输出格式包装;流式内容、进度或状态汇报、中间文本,以及任何仍将继续执行或会交回上级代理继续消费的文本,都保持自然输出。最终回复中的 `🔄 下一步` 写真实动作,不写当前状态;等待用户授权时使用等待输入态收尾,已获授权且可继续执行时不得收尾。同一条最终回复只包装一次;若需要分段,在同一个外层块内展开,不在正文里再次输出 `【HelloAGENTS】` 或第二个 `🔄 下一步`。
|
|
9
|
-
|
|
9
|
+
若当前输出会交回上级代理、控制器或其他代理继续汇总、决策、复述或等待后续动作,或当前任务由宿主协作/委派机制创建(包括 spawn_agent、worker / explorer、并行代理、Codex agent/delegate/wait 等),则一律按子代理处理:只豁免输出格式、交互确认与停顿、统一执行流程、任务分层、完成判定、命令路由和流程状态,直接执行任务;安全、质量、验证和失败处理规则仍持续生效。子代理只返回结果、证据或阻塞项;不得包装 HelloAGENTS 外层输出格式,不写 `🔄 下一步:`,不做面向最终用户的收尾。
|
|
10
10
|
只有运行时必须识别当前对话“完成 / 等待输入 / 阻塞”时,主代理才写 turn-state;普通问候、普通问答、T0 只读分析和一次性解释不调用。必须调用场景:显式 `~auto` / `~loop`、非只读任务完成验证并进入收尾、需要让运行时识别当前对话已完成、等待输入或已阻塞时、已进入项目连续流程或方案包闭环。首选 `helloagents-turn-state write --kind complete --role main`;等待或阻塞时写 `kind=waiting` / `kind=blocked`,并同时写 `reasonCategory` 与 `reason`。显式 `~auto` / `~loop` 下,还必须写入 `blocker.target`、`blocker.evidence`、`blocker.requiredAction`。不要查找、读取或拼接 `turn-state.mjs` 源码路径。子代理不得写 turn-state。
|
|
11
11
|
普通问答、解释、分析、改写、邮件回复和其他一次性交付虽然不进入完整实现、验证或收尾流程,但仍属于交付:默认只交付与当前请求直接对应的一版最终结果;请求已满足时直接结束,不主动追加无执行价值的延伸、派生版本、不同写法、第二版或邀约式收尾,除非用户明确要求。
|
|
12
12
|
|
|
@@ -22,17 +22,17 @@ description: 按任务类型适用 — 建立质量驱动工作流,通过技
|
|
|
22
22
|
|
|
23
23
|
### 流程纪律(执行时)
|
|
24
24
|
- 执行 command skill 时,公共阶段边界以当前已加载的 HelloAGENTS 规则为准;command skill 只补充该命令的专属动作和边界
|
|
25
|
-
- 统一执行流程的六个阶段(ROUTE/TIER→SPEC→PLAN→BUILD→
|
|
26
|
-
- 所有 UI 任务先受当前已加载的 HelloAGENTS UI
|
|
27
|
-
- 方案包存在 `contract.json`
|
|
25
|
+
- 统一执行流程的六个阶段(ROUTE/TIER→SPEC→PLAN→BUILD→QA→CONSOLIDATE)按当前 Delivery Tier 和实际任务推进;未进入的阶段不强行补齐,已进入的阶段不可跳过
|
|
26
|
+
- 所有 UI 任务先受当前已加载的 HelloAGENTS UI 质量基线约束;已初始化项目、宿主全局模式或显式 UI 工作流中的设计约束优先级固定为:当前 `plan.md` / PRD UI 决策 → `.helloagents/DESIGN.md`(按当前项目存储模式解析) → 已读取的 `hello-ui` 具体规则
|
|
27
|
+
- 方案包存在 `contract.json` 时,`qaMode`、`qaFocus`、可选 style advisor / visual validation 与交付检查优先按它执行,不再从自然语言总结里回推
|
|
28
28
|
- 因阻塞判定而必须等待用户输入时,按当前已加载的 HelloAGENTS 规则处理,不得把等待输入包装成完成态
|
|
29
29
|
- ~plan 的需求澄清与方案确认不可跳过,不可一个问题就出方案
|
|
30
30
|
- ~prd 的维度探索不可跳过,每个激活维度必须经过讨论或用户明确跳过
|
|
31
31
|
- ~auto 的复杂度判断不可省略
|
|
32
|
-
-
|
|
32
|
+
- qa-review 的质量铁律:没有实际审查、没有运行验证、没有留下证据 = 不能说完成
|
|
33
33
|
|
|
34
34
|
### 检查清单把关(完成时)
|
|
35
|
-
任务完成后,必须执行以下检查流程(详见
|
|
35
|
+
任务完成后,必须执行以下检查流程(详见 qa-review):
|
|
36
36
|
1. 运行验证命令(lint/test/build)→ 循环直到通过
|
|
37
37
|
2. 收集所有已激活技能的交付检查清单
|
|
38
38
|
3. 逐项验证。仅在交付检查清单、验收记录和验证结果中使用 [√] / [-] 标记,并附带证据;普通说明、方案解释、状态汇报不用这些标记
|
|
@@ -48,7 +48,7 @@ description: 按任务类型适用 — 建立质量驱动工作流,通过技
|
|
|
48
48
|
- 技能引用的 `templates/`、`modules/*.md` 等文件,只在技能明确要求时再读
|
|
49
49
|
|
|
50
50
|
禁止行为:
|
|
51
|
-
- 禁止在 ROUTE / TIER / SPEC 阶段读取实现类技能(hello-ui/hello-test/
|
|
51
|
+
- 禁止在 ROUTE / TIER / SPEC 阶段读取实现类技能(hello-ui/hello-test/qa-review 等)
|
|
52
52
|
- 禁止因为"可能用到"就提前读取技能文件——等到真正需要时再读
|
|
53
53
|
- 同一会话内,同一路径的配置文件、模块、SKILL、模板只读一次并跨轮复用;缺少所需内容、读取失败、用户要求刷新或本次修改后才重新读取
|
|
54
54
|
- ~command 命令只读取对应的 command SKILL.md,不连带读取其他技能
|
|
@@ -63,7 +63,7 @@ description: 按任务类型适用 — 建立质量驱动工作流,通过技
|
|
|
63
63
|
- 若当前上下文未注入,则使用稳定运行根目录 `~/.helloagents/helloagents`
|
|
64
64
|
- 宿主固定链接(Codex `~/.codex/helloagents`、Claude `~/.claude/helloagents`、Gemini `~/.gemini/helloagents`)只作为兼容别名,不作为优先探测路径
|
|
65
65
|
- 仍无法确定时,明确说明缺少 HelloAGENTS 读取根目录;不要递归扫描 `$HOME`、`Downloads`、项目目录或旧版本目录
|
|
66
|
-
-
|
|
66
|
+
- 宿主全局模式或已初始化项目时,技能是否需要使用由当前已加载 AGENTS 规则决定;不要因此额外探测项目目录里的 HelloAGENTS skills 路径
|
|
67
67
|
|
|
68
68
|
### hello-* 技能
|
|
69
69
|
读取 `{HELLOAGENTS_READ_ROOT}/skills/{技能名}/SKILL.md`
|
|
@@ -91,10 +91,9 @@ description: 按任务类型适用 — 建立质量驱动工作流,通过技
|
|
|
91
91
|
- hello-debug — 调试错误/修复 bug/排查失败时
|
|
92
92
|
- hello-subagent — 使用子代理执行任务时
|
|
93
93
|
- hello-write — 撰写文档/报告/方案等非编码文本时
|
|
94
|
-
-
|
|
94
|
+
- qa-review — 统一质量审查、验证、修复与收尾时
|
|
95
95
|
|
|
96
|
-
### 完成时(
|
|
97
|
-
- hello-verify — 声称完成前(必定读取)
|
|
96
|
+
### 完成时(QA / CONSOLIDATE 阶段读取)
|
|
98
97
|
- hello-reflect — 符合触发条件时(详见 hello-reflect SKILL.md)
|
|
99
98
|
|
|
100
99
|
## 命令路由
|
|
@@ -106,11 +105,9 @@ description: 按任务类型适用 — 建立质量驱动工作流,通过技
|
|
|
106
105
|
- `~build`
|
|
107
106
|
- `~prd`
|
|
108
107
|
- `~loop`
|
|
109
|
-
- `~global`
|
|
110
|
-
- `~wiki`
|
|
111
108
|
- `~init`
|
|
112
109
|
- `~test`
|
|
113
|
-
- `~
|
|
110
|
+
- `~qa`
|
|
114
111
|
- `~commit`
|
|
115
112
|
- `~clean`
|
|
116
113
|
- `~help`
|
|
@@ -118,6 +115,6 @@ description: 按任务类型适用 — 建立质量驱动工作流,通过技
|
|
|
118
115
|
兼容别名:
|
|
119
116
|
- `~do` → 直接按 `~build` 的 command skill 路径读取并执行
|
|
120
117
|
- `~design` → 直接按 `~plan` 的 command skill 路径读取并执行
|
|
121
|
-
- `~review` → 直接按 `~
|
|
118
|
+
- `~review` → 直接按 `~qa` 的 command skill 路径读取并执行
|
|
122
119
|
|
|
123
120
|
只有当对应 command skill 明确要求再读取 hello-* 技能时,才按上方“hello-* 技能”规则继续读取。
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: qa-review
|
|
3
|
+
description: 统一质量审查、命令验证、阻断修复与交付前质量闭环。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`qa-review` 是新的统一质量入口。
|
|
7
|
+
它取代旧的“先 review、再 verify”双路径,统一负责:
|
|
8
|
+
|
|
9
|
+
1. 识别当前范围与风险边界
|
|
10
|
+
2. 做代码与架构层面的质量审查
|
|
11
|
+
3. 运行验证命令
|
|
12
|
+
4. 修复阻断项并回归验证
|
|
13
|
+
5. 写当前会话 `artifacts/qa-review.json`
|
|
14
|
+
6. 若当前契约仍要求 advisor / visual / closeout,再继续补齐对应证据
|
|
15
|
+
|
|
16
|
+
## 质量模式
|
|
17
|
+
|
|
18
|
+
- `standard`:默认模式。聚焦当前变更、相关配置和真实风险边界,避免无关扩查
|
|
19
|
+
- `deep`:高风险或长任务收尾模式。按 12 维做更完整的阻断性审查,并优先补齐证据链
|
|
20
|
+
|
|
21
|
+
若 `contract.json` 提供 `qaMode` 与 `qaFocus`,优先服从它。
|
|
22
|
+
|
|
23
|
+
## 审查维度
|
|
24
|
+
|
|
25
|
+
所有模式至少覆盖以下维度:
|
|
26
|
+
|
|
27
|
+
- 功能正确性:边界条件、空值、错误路径、真实数据流
|
|
28
|
+
- 安全性:鉴权、注入、敏感信息、权限绕过
|
|
29
|
+
- 可靠性:超时、资源释放、异常恢复、一致性
|
|
30
|
+
- 性能与容量:重复计算、低效查询、大循环 I/O、构建产物体积
|
|
31
|
+
- 可维护性:职责边界、重复逻辑、命名、死代码、过度抽象
|
|
32
|
+
- 交付契约:requirements / tasks / contract 是否真实满足
|
|
33
|
+
|
|
34
|
+
`deep` 模式下,再补查:
|
|
35
|
+
|
|
36
|
+
- 兼容性
|
|
37
|
+
- 可观测与运维
|
|
38
|
+
- 测试有效性
|
|
39
|
+
- 架构与依赖边界
|
|
40
|
+
- 易用性
|
|
41
|
+
- 可演进性
|
|
42
|
+
|
|
43
|
+
## 证据要求
|
|
44
|
+
|
|
45
|
+
阻断问题必须给出:
|
|
46
|
+
|
|
47
|
+
- 文件定位:`{file}:{line}`
|
|
48
|
+
- 观察到的现象
|
|
49
|
+
- 为什么构成阻断
|
|
50
|
+
- 具体修复方向
|
|
51
|
+
|
|
52
|
+
不要只给泛泛评价。
|
|
53
|
+
|
|
54
|
+
## 验证命令
|
|
55
|
+
|
|
56
|
+
验证命令来源:
|
|
57
|
+
|
|
58
|
+
- `.helloagents/verify.yaml`
|
|
59
|
+
- `package.json` 的 `lint` / `typecheck` / `test` / `build`
|
|
60
|
+
- `pyproject.toml` 的 `ruff` / `mypy` / `pytest`
|
|
61
|
+
|
|
62
|
+
命令失败时:
|
|
63
|
+
|
|
64
|
+
1. 先说明根因
|
|
65
|
+
2. 修复阻断项
|
|
66
|
+
3. 重新运行相关命令
|
|
67
|
+
4. 直到通过,或命中真实阻塞
|
|
68
|
+
|
|
69
|
+
## 结构化证据
|
|
70
|
+
|
|
71
|
+
完成本次质量闭环后,立即调用:
|
|
72
|
+
|
|
73
|
+
`scripts/qa-review-state.mjs write`
|
|
74
|
+
|
|
75
|
+
写当前会话 `artifacts/qa-review.json`,至少记录:
|
|
76
|
+
|
|
77
|
+
- `qaMode`
|
|
78
|
+
- `scope`
|
|
79
|
+
- `outcome`
|
|
80
|
+
- `conclusion`
|
|
81
|
+
- `findings`
|
|
82
|
+
- `fileReferences`
|
|
83
|
+
- `commands`
|
|
84
|
+
|
|
85
|
+
若仍有阻断问题,`outcome` 必须写为 `findings`。
|
|
86
|
+
不要让运行时从自然语言里猜结论。
|
|
87
|
+
|
|
88
|
+
## 交付要求
|
|
89
|
+
|
|
90
|
+
- 没有看到验证输出,不能声称完成
|
|
91
|
+
- 没有写 `qa-review.json`,不能把当前结果当成可信质量闭环
|
|
92
|
+
- 若当前契约要求 `advisor.json` / `visual.json` / `closeout.json`,必须继续补齐
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version":
|
|
2
|
+
"version": 2,
|
|
3
3
|
"source": "{~plan | ~prd}",
|
|
4
4
|
"originCommand": "{plan | prd}",
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"{
|
|
8
|
-
],
|
|
9
|
-
"testerFocus": [
|
|
10
|
-
"{tester 需要重点验证的边界}"
|
|
5
|
+
"qaMode": "{standard | deep}",
|
|
6
|
+
"qaFocus": [
|
|
7
|
+
"{qa-review 需要重点检查或回归验证的边界}"
|
|
11
8
|
],
|
|
12
9
|
"ui": {
|
|
13
10
|
"required": false,
|
package/templates/plans/plan.md
CHANGED
package/templates/plans/tasks.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
- [ ] 任务3(AFK/HITL):端到端行为描述(依赖:...;涉及文件:...;预期变更:...;完成标准:...;验证方式:...)
|
|
13
13
|
|
|
14
14
|
## Codex /goal 执行入口
|
|
15
|
-
[可选。需要长程执行时复制到 Codex:/goal 按 `.helloagents/plans/{feature}/tasks.md` 执行本方案;遵守 `requirements.md`、`plan.md`、`contract.json
|
|
15
|
+
[可选。需要长程执行时复制到 Codex:/goal 按 `.helloagents/plans/{feature}/tasks.md` 执行本方案;遵守 `requirements.md`、`plan.md`、`contract.json`。默认主执行命令是 `~auto`;按顺序完成所有 AFK 任务;HITL 仅在缺少外部决策、凭据或人工验收时暂停。不要把完整 PRD 原文直接当作 `/goal` 目标。全部 AFK 任务完成后必须进入 `~qa`,写最新质量证据并完成 HelloAGENTS 收尾,再标记 goal complete。]
|
|
16
16
|
|
|
17
17
|
## 进度
|
|
18
18
|
[执行过程中更新]
|
package/templates/verify.yaml
CHANGED
package/scripts/review-state.mjs
DELETED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs'
|
|
2
|
-
import { fileURLToPath } from 'node:url'
|
|
3
|
-
import { appendReplayEvent } from './replay-state.mjs'
|
|
4
|
-
import {
|
|
5
|
-
captureWorkspaceFingerprint,
|
|
6
|
-
clearRuntimeEvidence,
|
|
7
|
-
getRuntimeEvidencePath,
|
|
8
|
-
getRuntimeEvidenceRelativePath,
|
|
9
|
-
readRuntimeEvidence,
|
|
10
|
-
validateEvidenceFingerprint,
|
|
11
|
-
validateEvidenceTimestamp,
|
|
12
|
-
writeRuntimeEvidence,
|
|
13
|
-
} from './runtime-artifacts.mjs'
|
|
14
|
-
|
|
15
|
-
export const REVIEW_EVIDENCE_FILE_NAME = 'review.json'
|
|
16
|
-
const VALID_REVIEW_OUTCOMES = new Set(['clean', 'findings'])
|
|
17
|
-
|
|
18
|
-
function normalizeStringArray(values) {
|
|
19
|
-
if (!Array.isArray(values)) return []
|
|
20
|
-
return [...new Set(values
|
|
21
|
-
.map((value) => (typeof value === 'string' ? value.trim() : ''))
|
|
22
|
-
.filter(Boolean))]
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function normalizeReviewOutcome(value) {
|
|
26
|
-
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : ''
|
|
27
|
-
return VALID_REVIEW_OUTCOMES.has(normalized) ? normalized : ''
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function getReviewEvidencePath(cwd, options = {}) {
|
|
31
|
-
return getRuntimeEvidencePath(cwd, REVIEW_EVIDENCE_FILE_NAME, options)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function readReviewEvidence(cwd, options = {}) {
|
|
35
|
-
return readRuntimeEvidence(cwd, REVIEW_EVIDENCE_FILE_NAME, options)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function clearReviewEvidence(cwd, options = {}) {
|
|
39
|
-
clearRuntimeEvidence(cwd, REVIEW_EVIDENCE_FILE_NAME, options)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function normalizeReviewEvidence(input = {}) {
|
|
43
|
-
return {
|
|
44
|
-
source: typeof input.source === 'string' && input.source.trim() ? input.source.trim() : 'manual',
|
|
45
|
-
originCommand: typeof input.originCommand === 'string' ? input.originCommand.trim() : '',
|
|
46
|
-
reviewMode: typeof input.reviewMode === 'string' ? input.reviewMode.trim() : '',
|
|
47
|
-
outcome: normalizeReviewOutcome(input.outcome),
|
|
48
|
-
conclusion: typeof input.conclusion === 'string' ? input.conclusion.trim() : '',
|
|
49
|
-
findings: normalizeStringArray(input.findings),
|
|
50
|
-
fileReferences: normalizeStringArray(input.fileReferences),
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function writeReviewEvidence(cwd, {
|
|
55
|
-
source = 'stop',
|
|
56
|
-
originCommand = '',
|
|
57
|
-
reviewMode = '',
|
|
58
|
-
outcome = '',
|
|
59
|
-
conclusion = '',
|
|
60
|
-
findings = [],
|
|
61
|
-
fileReferences = [],
|
|
62
|
-
} = {}, options = {}) {
|
|
63
|
-
const normalized = normalizeReviewEvidence({
|
|
64
|
-
source,
|
|
65
|
-
originCommand,
|
|
66
|
-
reviewMode,
|
|
67
|
-
outcome,
|
|
68
|
-
conclusion,
|
|
69
|
-
findings,
|
|
70
|
-
fileReferences,
|
|
71
|
-
})
|
|
72
|
-
const payload = {
|
|
73
|
-
updatedAt: new Date().toISOString(),
|
|
74
|
-
source: normalized.source,
|
|
75
|
-
originCommand: normalized.originCommand,
|
|
76
|
-
reviewMode: normalized.reviewMode,
|
|
77
|
-
conclusion: normalized.conclusion,
|
|
78
|
-
outcome: normalized.outcome,
|
|
79
|
-
findings: normalized.findings,
|
|
80
|
-
fileReferences: normalized.fileReferences,
|
|
81
|
-
fingerprint: captureWorkspaceFingerprint(cwd),
|
|
82
|
-
}
|
|
83
|
-
writeRuntimeEvidence(cwd, REVIEW_EVIDENCE_FILE_NAME, payload, options)
|
|
84
|
-
appendReplayEvent(cwd, {
|
|
85
|
-
event: 'review_evidence_written',
|
|
86
|
-
source: normalized.source,
|
|
87
|
-
skillName: normalized.originCommand,
|
|
88
|
-
payload: options.payload || {},
|
|
89
|
-
details: {
|
|
90
|
-
reviewMode: normalized.reviewMode,
|
|
91
|
-
outcome: normalized.outcome,
|
|
92
|
-
conclusion: normalized.conclusion,
|
|
93
|
-
findings: normalized.findings,
|
|
94
|
-
fileReferences: normalized.fileReferences,
|
|
95
|
-
},
|
|
96
|
-
artifacts: [getRuntimeEvidenceRelativePath(cwd, REVIEW_EVIDENCE_FILE_NAME, options)],
|
|
97
|
-
})
|
|
98
|
-
return payload
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function readRequiredReviewEvidence(cwd, options = {}) {
|
|
102
|
-
const evidence = readReviewEvidence(cwd, options)
|
|
103
|
-
if (evidence) return { evidence }
|
|
104
|
-
return {
|
|
105
|
-
error: {
|
|
106
|
-
required: true,
|
|
107
|
-
status: 'missing',
|
|
108
|
-
details: ['缺少 review-first 收尾所需的成功审查证据'],
|
|
109
|
-
},
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function validateReviewTimestamp(evidence, now) {
|
|
114
|
-
return validateEvidenceTimestamp(evidence, now, '审查证据')
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function validateReviewFingerprint(cwd, evidence) {
|
|
118
|
-
return validateEvidenceFingerprint(cwd, evidence, '成功审查证据')
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function validateReviewOutcome(evidence) {
|
|
122
|
-
if (!normalizeReviewOutcome(evidence.outcome) || !String(evidence.conclusion || '').trim()) {
|
|
123
|
-
return {
|
|
124
|
-
required: true,
|
|
125
|
-
status: 'invalid',
|
|
126
|
-
evidence,
|
|
127
|
-
details: ['审查证据必须记录明确的 outcome 和 conclusion'],
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
if (normalizeReviewOutcome(evidence.outcome) !== 'clean') {
|
|
131
|
-
return {
|
|
132
|
-
required: true,
|
|
133
|
-
status: 'blocked',
|
|
134
|
-
evidence,
|
|
135
|
-
details: ['最新审查证据仍记录阻塞问题'],
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return null
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export function getReviewEvidenceStatus(cwd, { required = false, now = Date.now(), ...options } = {}) {
|
|
142
|
-
if (!required) {
|
|
143
|
-
return {
|
|
144
|
-
required: false,
|
|
145
|
-
status: 'not-applicable',
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const requiredEvidence = readRequiredReviewEvidence(cwd, options)
|
|
150
|
-
if (requiredEvidence.error) return requiredEvidence.error
|
|
151
|
-
|
|
152
|
-
const { evidence } = requiredEvidence
|
|
153
|
-
const timestampError = validateReviewTimestamp(evidence, now)
|
|
154
|
-
if (timestampError) return timestampError
|
|
155
|
-
|
|
156
|
-
const fingerprintError = validateReviewFingerprint(cwd, evidence)
|
|
157
|
-
if (fingerprintError) return fingerprintError
|
|
158
|
-
|
|
159
|
-
const outcomeError = validateReviewOutcome(evidence)
|
|
160
|
-
if (outcomeError) return outcomeError
|
|
161
|
-
|
|
162
|
-
return {
|
|
163
|
-
required: true,
|
|
164
|
-
status: 'valid',
|
|
165
|
-
evidence,
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function readStdinJson() {
|
|
170
|
-
try {
|
|
171
|
-
return JSON.parse(readFileSync(0, 'utf-8'))
|
|
172
|
-
} catch {
|
|
173
|
-
return {}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function main() {
|
|
178
|
-
const command = process.argv[2] || ''
|
|
179
|
-
if (command !== 'write') return
|
|
180
|
-
|
|
181
|
-
const input = readStdinJson()
|
|
182
|
-
const cwd = input.cwd || process.cwd()
|
|
183
|
-
const payload = writeReviewEvidence(cwd, input, { payload: input })
|
|
184
|
-
process.stdout.write(JSON.stringify({
|
|
185
|
-
suppressOutput: true,
|
|
186
|
-
path: getReviewEvidencePath(cwd, { payload: input }),
|
|
187
|
-
payload,
|
|
188
|
-
}))
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
192
|
-
main()
|
|
193
|
-
}
|
package/scripts/verify-state.mjs
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs'
|
|
2
|
-
import { join } from 'node:path'
|
|
3
|
-
import { appendReplayEvent } from './replay-state.mjs'
|
|
4
|
-
import {
|
|
5
|
-
getProjectVerifyYamlPath,
|
|
6
|
-
} from './project-storage.mjs'
|
|
7
|
-
import {
|
|
8
|
-
captureWorkspaceFingerprint,
|
|
9
|
-
clearRuntimeEvidence,
|
|
10
|
-
getRuntimeEvidencePath,
|
|
11
|
-
getRuntimeEvidenceRelativePath,
|
|
12
|
-
readRuntimeEvidence,
|
|
13
|
-
validateEvidenceFingerprint,
|
|
14
|
-
validateEvidenceTimestamp,
|
|
15
|
-
writeRuntimeEvidence,
|
|
16
|
-
} from './runtime-artifacts.mjs'
|
|
17
|
-
|
|
18
|
-
export const VERIFY_EVIDENCE_FILE_NAME = 'verify.json'
|
|
19
|
-
const SHELL_OPERATORS = /[;&|`$(){}\n\r]/
|
|
20
|
-
|
|
21
|
-
export function getVerifyEvidencePath(cwd, options = {}) {
|
|
22
|
-
return getRuntimeEvidencePath(cwd, VERIFY_EVIDENCE_FILE_NAME, options)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function readVerifyEvidence(cwd, options = {}) {
|
|
26
|
-
return readRuntimeEvidence(cwd, VERIFY_EVIDENCE_FILE_NAME, options)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function clearVerifyEvidence(cwd, options = {}) {
|
|
30
|
-
clearRuntimeEvidence(cwd, VERIFY_EVIDENCE_FILE_NAME, options)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function loadVerifyYaml(cwd) {
|
|
34
|
-
const f = getProjectVerifyYamlPath(cwd)
|
|
35
|
-
if (!existsSync(f)) return null
|
|
36
|
-
try {
|
|
37
|
-
const content = readFileSync(f, 'utf-8')
|
|
38
|
-
const cmds = []
|
|
39
|
-
let inCmds = false
|
|
40
|
-
for (const line of content.split('\n')) {
|
|
41
|
-
const s = line.trim()
|
|
42
|
-
if (s.startsWith('commands:')) { inCmds = true; continue }
|
|
43
|
-
if (inCmds) {
|
|
44
|
-
if (s.startsWith('- ') && !s.startsWith('# ')) {
|
|
45
|
-
const cmd = s.slice(2).trim().replace(/^["']|["']$/g, '')
|
|
46
|
-
if (cmd && !cmd.startsWith('#')) cmds.push(cmd)
|
|
47
|
-
} else if (s && !s.startsWith('#')) {
|
|
48
|
-
break
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return cmds.length ? cmds : null
|
|
53
|
-
} catch {
|
|
54
|
-
return null
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function detectFromPackageJson(cwd) {
|
|
59
|
-
const f = join(cwd, 'package.json')
|
|
60
|
-
if (!existsSync(f)) return []
|
|
61
|
-
try {
|
|
62
|
-
const scripts = JSON.parse(readFileSync(f, 'utf-8')).scripts || {}
|
|
63
|
-
return ['lint', 'typecheck', 'type-check', 'test', 'build']
|
|
64
|
-
.filter((k) => k in scripts)
|
|
65
|
-
.map((k) => `npm run ${k}`)
|
|
66
|
-
} catch {
|
|
67
|
-
return []
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function detectFromPyproject(cwd) {
|
|
72
|
-
const f = join(cwd, 'pyproject.toml')
|
|
73
|
-
if (!existsSync(f)) return []
|
|
74
|
-
try {
|
|
75
|
-
const content = readFileSync(f, 'utf-8')
|
|
76
|
-
const cmds = []
|
|
77
|
-
if (content.includes('[tool.ruff')) cmds.push('ruff check .')
|
|
78
|
-
if (content.includes('[tool.mypy')) cmds.push('mypy .')
|
|
79
|
-
if (content.includes('[tool.pytest')) cmds.push('pytest --tb=short -q')
|
|
80
|
-
return cmds
|
|
81
|
-
} catch {
|
|
82
|
-
return []
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function detectCommands(cwd) {
|
|
87
|
-
const yaml = loadVerifyYaml(cwd)
|
|
88
|
-
if (yaml?.length) return yaml
|
|
89
|
-
const pkg = detectFromPackageJson(cwd)
|
|
90
|
-
if (pkg.length) return pkg
|
|
91
|
-
return detectFromPyproject(cwd)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function hasUnsafeVerifyCommand(commands = []) {
|
|
95
|
-
return commands.some((cmd) => SHELL_OPERATORS.test(cmd))
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export function writeVerifyEvidence(cwd, { commands = [], fastOnly = false, source = 'ralph-loop' } = {}, options = {}) {
|
|
99
|
-
const payload = {
|
|
100
|
-
updatedAt: new Date().toISOString(),
|
|
101
|
-
commands,
|
|
102
|
-
fastOnly,
|
|
103
|
-
source,
|
|
104
|
-
fingerprint: captureWorkspaceFingerprint(cwd),
|
|
105
|
-
}
|
|
106
|
-
writeRuntimeEvidence(cwd, VERIFY_EVIDENCE_FILE_NAME, payload, options)
|
|
107
|
-
appendReplayEvent(cwd, {
|
|
108
|
-
event: 'verify_evidence_written',
|
|
109
|
-
source,
|
|
110
|
-
payload: options.payload || {},
|
|
111
|
-
details: {
|
|
112
|
-
commands,
|
|
113
|
-
fastOnly,
|
|
114
|
-
},
|
|
115
|
-
artifacts: [getRuntimeEvidenceRelativePath(cwd, VERIFY_EVIDENCE_FILE_NAME, options)],
|
|
116
|
-
})
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function validateVerifyEvidencePresence(commands, evidence) {
|
|
120
|
-
if (evidence) return null
|
|
121
|
-
return {
|
|
122
|
-
required: true,
|
|
123
|
-
status: 'missing',
|
|
124
|
-
commands,
|
|
125
|
-
details: ['缺少当前工作流的成功验证证据'],
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function validateVerifyEvidenceFreshness(cwd, commands, evidence, now) {
|
|
130
|
-
if (evidence.fastOnly) {
|
|
131
|
-
return {
|
|
132
|
-
required: true,
|
|
133
|
-
status: 'fast-only',
|
|
134
|
-
commands,
|
|
135
|
-
evidence,
|
|
136
|
-
details: ['最新验证证据只覆盖子代理快速检查'],
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const timestampError = validateEvidenceTimestamp(evidence, now, '验证证据')
|
|
141
|
-
return timestampError ? { ...timestampError, commands } : null
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function validateVerifyFingerprint(cwd, commands, evidence) {
|
|
145
|
-
const fingerprintError = validateEvidenceFingerprint(cwd, evidence, '成功验证证据')
|
|
146
|
-
return fingerprintError ? { ...fingerprintError, commands } : null
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export function getVerifyEvidenceStatus(cwd, { now = Date.now(), ...options } = {}) {
|
|
150
|
-
const commands = detectCommands(cwd)
|
|
151
|
-
if (!commands.length) {
|
|
152
|
-
return {
|
|
153
|
-
required: false,
|
|
154
|
-
status: 'not-applicable',
|
|
155
|
-
commands,
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const evidence = readVerifyEvidence(cwd, options)
|
|
160
|
-
const missingError = validateVerifyEvidencePresence(commands, evidence)
|
|
161
|
-
if (missingError) return missingError
|
|
162
|
-
|
|
163
|
-
const freshnessError = validateVerifyEvidenceFreshness(cwd, commands, evidence, now)
|
|
164
|
-
if (freshnessError) return freshnessError
|
|
165
|
-
|
|
166
|
-
const fingerprintError = validateVerifyFingerprint(cwd, commands, evidence)
|
|
167
|
-
if (fingerprintError) return fingerprintError
|
|
168
|
-
|
|
169
|
-
return {
|
|
170
|
-
required: true,
|
|
171
|
-
status: 'valid',
|
|
172
|
-
commands,
|
|
173
|
-
evidence,
|
|
174
|
-
}
|
|
175
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: ~global
|
|
3
|
-
description: 初始化项目级全局模式(~global 命令)
|
|
4
|
-
policy:
|
|
5
|
-
allow_implicit_invocation: false
|
|
6
|
-
---
|
|
7
|
-
Trigger: ~global
|
|
8
|
-
|
|
9
|
-
`~global` 是用户显式命令,用来初始化项目级全局模式。
|
|
10
|
-
|
|
11
|
-
`~global` 不受 `kb_create_mode` 限制。
|
|
12
|
-
执行 `~global` 时,`.helloagents/` 目录结构、模板格式和状态文件规则按当前已加载的 HelloAGENTS 规则执行;本命令额外负责项目级规则文件和各宿主项目级 HelloAGENTS 包根链接。
|
|
13
|
-
`.helloagents/` 在本 skill 中统一按项目级存储路径理解:项目本地 `.helloagents/` 继续承担项目本地存储目录;状态文件只使用 `state_path`;若 `project_store_mode=repo-shared`,知识库、`DESIGN.md` 与方案包按当前上下文中已注入的项目知识/方案目录写入。
|
|
14
|
-
|
|
15
|
-
## 流程
|
|
16
|
-
|
|
17
|
-
### 阶段 1:环境搭建(必做)
|
|
18
|
-
|
|
19
|
-
1. 创建 `.helloagents/` 目录 + `state_path`(按 templates/STATE.md 格式,初始“主线目标”写当前初始化任务,初始状态为空闲)
|
|
20
|
-
2. 定位插件根目录:优先读取当前上下文中已注入的“当前 HelloAGENTS 包根目录”;若上下文未提供,再根据当前已加载的 HelloAGENTS 规则来源反推,禁止猜测其他目录
|
|
21
|
-
3. 刷新各宿主项目级 HelloAGENTS 包根链接(删除旧的重建):
|
|
22
|
-
- `.claude/skills/helloagents` symlink → `{插件根目录}/`
|
|
23
|
-
- `.gemini/skills/helloagents` symlink → `{插件根目录}/`
|
|
24
|
-
- `.codex/skills/helloagents` symlink → `{插件根目录}/`
|
|
25
|
-
这些链接用于项目级规则定位 HelloAGENTS 的 `skills/`、`templates/` 和 `scripts/`;宿主若支持递归发现 `SKILL.md`,也可直接识别包内 skills。
|
|
26
|
-
4. 读取 `{插件根目录}` 中的全量规则模板,在受管内容第一行写入 `<!-- HELLOAGENTS_PROFILE: full -->`,再用 `<!-- HELLOAGENTS_START -->` / `<!-- HELLOAGENTS_END -->` 标记包裹后写入:
|
|
27
|
-
- `AGENTS.md`(项目根目录,Codex 读取)
|
|
28
|
-
- `CLAUDE.md`(项目根目录,Claude Code 读取)
|
|
29
|
-
- `.gemini/GEMINI.md`(Gemini CLI 读取,需先创建 .gemini/ 目录)
|
|
30
|
-
注意:如果文件已存在且包含标记,替换标记内的内容;如果文件已存在但无标记,追加到末尾;如果文件不存在,创建新文件
|
|
31
|
-
5. 追加 `.gitignore`(如果对应行不存在):
|
|
32
|
-
```
|
|
33
|
-
.helloagents/
|
|
34
|
-
.claude/skills/helloagents
|
|
35
|
-
.gemini/skills/helloagents
|
|
36
|
-
.codex/skills/helloagents
|
|
37
|
-
AGENTS.md
|
|
38
|
-
CLAUDE.md
|
|
39
|
-
.gemini/GEMINI.md
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### 阶段 2:知识库创建(条件性)
|
|
43
|
-
|
|
44
|
-
检查项目是否有实际代码文件(非空项目):
|
|
45
|
-
- 有代码文件 → 执行完整知识库创建(下方流程)
|
|
46
|
-
- 空项目 → 跳过,告知用户"项目为空,知识库将在后续开发中创建"
|
|
47
|
-
|
|
48
|
-
知识库创建流程(与原 ~global 一致;逻辑写入 `.helloagents/`,`project_store_mode=repo-shared` 时实际落在共享知识目录):
|
|
49
|
-
1. 按 templates/ 目录的模板格式,分析项目代码库后生成:
|
|
50
|
-
- context.md — 按 templates/context.md 格式,填入项目概述、技术栈、架构、目录结构、模块链接
|
|
51
|
-
- guidelines.md — 按 templates/guidelines.md 格式,从现有代码推断编码约定
|
|
52
|
-
- verify.yaml — 验证命令(从 package.json/pyproject.toml 检测)
|
|
53
|
-
- CHANGELOG.md — 按 templates/CHANGELOG.md 格式,初始版本
|
|
54
|
-
- DESIGN.md — 如果项目包含 UI 代码,按 templates/DESIGN.md 格式提取项目级设计契约(产品表面、设计 token、组件与模式、状态覆盖、无障碍要求、禁止事项等)
|
|
55
|
-
2. 创建 modules/ 目录,按 templates/modules/module.md 格式为主要模块生成文档
|
|
56
|
-
3. 不覆盖已存在的文件
|
|
57
|
-
|
|
58
|
-
## verify.yaml 格式
|
|
59
|
-
```yaml
|
|
60
|
-
commands:
|
|
61
|
-
- npm run lint
|
|
62
|
-
- npm run test
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## 幂等性
|
|
66
|
-
重复执行 ~global 是安全的:
|
|
67
|
-
- 已存在的 .helloagents/ 文件不覆盖
|
|
68
|
-
- `state_path` 只记录当前初始化任务;后续进入其他任务时必须按新任务重写
|
|
69
|
-
- 各宿主项目级 HelloAGENTS 包根链接会刷新(删除旧的重建)
|
|
70
|
-
- AGENTS.md/CLAUDE.md/GEMINI.md 中标记内容替换更新
|
|
71
|
-
- .gitignore 只追加缺失行
|