helloagents 3.0.7 → 3.0.9-beta.1
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/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +6 -6
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +65 -58
- package/README_CN.md +60 -53
- package/bootstrap-lite.md +46 -28
- package/bootstrap.md +51 -34
- package/gemini-extension.json +1 -1
- package/package.json +12 -2
- package/scripts/capability-registry.mjs +9 -9
- package/scripts/cli-codex-config.mjs +49 -55
- package/scripts/cli-codex.mjs +69 -77
- package/scripts/cli-doctor.mjs +26 -18
- package/scripts/cli-host-detect.mjs +18 -2
- package/scripts/cli-messages.mjs +1 -1
- package/scripts/cli-toml.mjs +30 -0
- package/scripts/delivery-gate.mjs +5 -4
- package/scripts/guard-rules.mjs +26 -1
- package/scripts/guard.mjs +43 -14
- package/scripts/notify-context.mjs +30 -33
- package/scripts/notify-route.mjs +5 -2
- package/scripts/notify-source.mjs +3 -60
- package/scripts/notify.mjs +43 -11
- package/scripts/project-storage.mjs +107 -15
- package/scripts/session-token.mjs +73 -0
- package/scripts/turn-state.mjs +173 -0
- package/scripts/workflow-core.mjs +19 -11
- package/scripts/workflow-plan-files.mjs +17 -6
- package/scripts/workflow-recommendation.mjs +14 -14
- package/scripts/workflow-state.mjs +14 -14
- package/skills/_meta/SKILL.md +1 -1
- package/skills/commands/auto/SKILL.md +24 -9
- package/skills/commands/build/SKILL.md +4 -4
- package/skills/commands/clean/SKILL.md +3 -3
- package/skills/commands/commit/SKILL.md +1 -1
- package/skills/commands/help/SKILL.md +3 -3
- package/skills/commands/idea/SKILL.md +2 -2
- package/skills/commands/init/SKILL.md +13 -8
- package/skills/commands/loop/SKILL.md +4 -4
- package/skills/commands/plan/SKILL.md +13 -11
- package/skills/commands/prd/SKILL.md +10 -8
- package/skills/commands/verify/SKILL.md +5 -5
- package/skills/commands/wiki/SKILL.md +9 -11
- package/skills/hello-review/SKILL.md +1 -1
- package/skills/hello-subagent/SKILL.md +3 -2
- package/skills/hello-ui/SKILL.md +13 -13
- package/skills/hello-verify/SKILL.md +6 -5
- package/skills/helloagents/SKILL.md +17 -12
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { dirname, join, normalize, resolve } from 'node:path'
|
|
3
|
+
import { homedir } from 'node:os'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
|
|
6
|
+
import { appendReplayEvent } from './replay-state.mjs'
|
|
7
|
+
|
|
8
|
+
const TURN_STATE_PATH = join(homedir(), '.helloagents', 'runtime', 'turn-state.json')
|
|
9
|
+
const TURN_STATE_TTL_MS = 30 * 60 * 1000
|
|
10
|
+
const VALID_KINDS = new Set(['complete', 'waiting', 'blocked', 'progress'])
|
|
11
|
+
const VALID_ROLES = new Set(['main', 'subagent'])
|
|
12
|
+
|
|
13
|
+
function normalizePath(filePath = '') {
|
|
14
|
+
return filePath ? normalize(resolve(filePath)) : ''
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function ensureRuntimeDir() {
|
|
18
|
+
mkdirSync(dirname(TURN_STATE_PATH), { recursive: true })
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function readStore() {
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(readFileSync(TURN_STATE_PATH, 'utf-8'))
|
|
24
|
+
} catch {
|
|
25
|
+
return {}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function writeStore(store) {
|
|
30
|
+
const keys = Object.keys(store)
|
|
31
|
+
if (keys.length === 0) {
|
|
32
|
+
rmSync(TURN_STATE_PATH, { force: true })
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
ensureRuntimeDir()
|
|
37
|
+
writeFileSync(TURN_STATE_PATH, `${JSON.stringify(store, null, 2)}\n`, 'utf-8')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getTurnStateKey(cwd = process.cwd()) {
|
|
41
|
+
return normalizePath(cwd)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function normalizeTurnState(input = {}) {
|
|
45
|
+
const kind = typeof input.kind === 'string' ? input.kind.trim().toLowerCase() : ''
|
|
46
|
+
const role = typeof input.role === 'string' ? input.role.trim().toLowerCase() : 'main'
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
kind: VALID_KINDS.has(kind) ? kind : '',
|
|
50
|
+
role: VALID_ROLES.has(role) ? role : 'main',
|
|
51
|
+
phase: typeof input.phase === 'string' ? input.phase.trim().toLowerCase() : '',
|
|
52
|
+
source: typeof input.source === 'string' && input.source.trim() ? input.source.trim() : 'manual',
|
|
53
|
+
requiresDeliveryGate: Boolean(input.requiresDeliveryGate),
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function pruneInvalidEntry(store, key) {
|
|
58
|
+
delete store[key]
|
|
59
|
+
writeStore(store)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function clearTurnState(cwd = process.cwd()) {
|
|
63
|
+
const key = getTurnStateKey(cwd)
|
|
64
|
+
if (!key) return false
|
|
65
|
+
const store = readStore()
|
|
66
|
+
if (!(key in store)) return false
|
|
67
|
+
delete store[key]
|
|
68
|
+
writeStore(store)
|
|
69
|
+
return true
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function readTurnState(cwd = process.cwd(), { now = Date.now() } = {}) {
|
|
73
|
+
const key = getTurnStateKey(cwd)
|
|
74
|
+
if (!key) return null
|
|
75
|
+
|
|
76
|
+
const store = readStore()
|
|
77
|
+
const entry = store[key]
|
|
78
|
+
if (!entry?.cwd || !entry?.kind || !entry?.updatedAt) {
|
|
79
|
+
if (entry) pruneInvalidEntry(store, key)
|
|
80
|
+
return null
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const updatedAt = Date.parse(entry.updatedAt)
|
|
84
|
+
if (!Number.isFinite(updatedAt) || (now - updatedAt > TURN_STATE_TTL_MS)) {
|
|
85
|
+
pruneInvalidEntry(store, key)
|
|
86
|
+
return null
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const normalized = normalizeTurnState(entry)
|
|
90
|
+
if (!normalized.kind) {
|
|
91
|
+
pruneInvalidEntry(store, key)
|
|
92
|
+
return null
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
cwd: normalizePath(entry.cwd),
|
|
97
|
+
updatedAt: entry.updatedAt,
|
|
98
|
+
...normalized,
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function writeTurnState(cwd = process.cwd(), input = {}) {
|
|
103
|
+
const key = getTurnStateKey(cwd)
|
|
104
|
+
const normalized = normalizeTurnState(input)
|
|
105
|
+
if (!key || !normalized.kind) {
|
|
106
|
+
throw new Error('turn-state requires cwd and a valid kind')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const store = readStore()
|
|
110
|
+
const payload = {
|
|
111
|
+
cwd: key,
|
|
112
|
+
updatedAt: new Date().toISOString(),
|
|
113
|
+
...normalized,
|
|
114
|
+
}
|
|
115
|
+
store[key] = payload
|
|
116
|
+
writeStore(store)
|
|
117
|
+
|
|
118
|
+
appendReplayEvent(cwd, {
|
|
119
|
+
event: 'turn_state_written',
|
|
120
|
+
source: normalized.source,
|
|
121
|
+
details: {
|
|
122
|
+
kind: normalized.kind,
|
|
123
|
+
role: normalized.role,
|
|
124
|
+
phase: normalized.phase,
|
|
125
|
+
requiresDeliveryGate: normalized.requiresDeliveryGate,
|
|
126
|
+
},
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
return payload
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function readStdinJson() {
|
|
133
|
+
try {
|
|
134
|
+
return JSON.parse(readFileSync(0, 'utf-8'))
|
|
135
|
+
} catch {
|
|
136
|
+
return {}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function main() {
|
|
141
|
+
const command = process.argv[2] || ''
|
|
142
|
+
const input = readStdinJson()
|
|
143
|
+
const cwd = input.cwd || process.cwd()
|
|
144
|
+
|
|
145
|
+
if (command === 'write') {
|
|
146
|
+
const payload = writeTurnState(cwd, input)
|
|
147
|
+
process.stdout.write(JSON.stringify({
|
|
148
|
+
suppressOutput: true,
|
|
149
|
+
path: TURN_STATE_PATH,
|
|
150
|
+
payload,
|
|
151
|
+
}))
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (command === 'clear') {
|
|
156
|
+
process.stdout.write(JSON.stringify({
|
|
157
|
+
suppressOutput: true,
|
|
158
|
+
cleared: clearTurnState(cwd),
|
|
159
|
+
}))
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (command === 'read') {
|
|
164
|
+
process.stdout.write(JSON.stringify({
|
|
165
|
+
suppressOutput: true,
|
|
166
|
+
state: readTurnState(cwd),
|
|
167
|
+
}))
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
172
|
+
main()
|
|
173
|
+
}
|
|
@@ -13,6 +13,13 @@ import { describeProjectStoreFile, getProjectDesignContractPath } from './projec
|
|
|
13
13
|
export function getTargetPlans(snapshot) {
|
|
14
14
|
return snapshot.activePlans.length > 0 ? snapshot.activePlans : snapshot.plans
|
|
15
15
|
}
|
|
16
|
+
|
|
17
|
+
function describeStateLabel(state) {
|
|
18
|
+
if (state.stateSessionMode === 'default') {
|
|
19
|
+
return '当前分支默认会话槽位的 `STATE.md`'
|
|
20
|
+
}
|
|
21
|
+
return '当前会话的 `STATE.md`'
|
|
22
|
+
}
|
|
16
23
|
export function classifyPlan(plan) {
|
|
17
24
|
if (!plan) {
|
|
18
25
|
return {
|
|
@@ -63,7 +70,7 @@ export function determineVerifyMode(plan) {
|
|
|
63
70
|
if (plan.contractIssues.length > 0) {
|
|
64
71
|
return {
|
|
65
72
|
mode: 'metadata-first',
|
|
66
|
-
reason: '
|
|
73
|
+
reason: '方案包缺少可信的结构化契约',
|
|
67
74
|
guidance: '验证分流:当前还不适合直接进入 reviewer / tester;先回到 ~plan / ~prd 补齐 `contract.json`,明确 `verifyMode`、`reviewerFocus` 和 `testerFocus`。',
|
|
68
75
|
}
|
|
69
76
|
}
|
|
@@ -73,7 +80,7 @@ export function determineVerifyMode(plan) {
|
|
|
73
80
|
const testerFocus = plan.contract.testerFocus.join(';')
|
|
74
81
|
return {
|
|
75
82
|
mode: 'review-first',
|
|
76
|
-
reason: '
|
|
83
|
+
reason: '方案契约已明确要求审查优先',
|
|
77
84
|
guidance: `验证分流:当前更适合审查优先;先执行 reviewer / hello-review 范围审查,再交给 tester / hello-verify 跑完整验证。${reviewerFocus ? ` reviewer 重点:${reviewerFocus}。` : ''}${testerFocus ? ` tester 重点:${testerFocus}。` : ''}`.trim(),
|
|
78
85
|
}
|
|
79
86
|
}
|
|
@@ -88,7 +95,7 @@ export function determineVerifyMode(plan) {
|
|
|
88
95
|
|
|
89
96
|
return {
|
|
90
97
|
mode: 'test-first',
|
|
91
|
-
reason: '
|
|
98
|
+
reason: '方案契约已明确验证主路径,且任务契约已完整',
|
|
92
99
|
guidance: `验证分流:当前更适合测试优先;先执行 tester / hello-verify 跑完整验证,再针对失败点或关键边界补充 hello-review。${plan.contract?.testerFocus?.length ? ` tester 重点:${plan.contract.testerFocus.join(';')}。` : ''}`.trim(),
|
|
93
100
|
}
|
|
94
101
|
}
|
|
@@ -97,27 +104,28 @@ function collectStateSyncIssues(snapshot) {
|
|
|
97
104
|
const issues = []
|
|
98
105
|
const hasPlans = snapshot.plans.length > 0
|
|
99
106
|
const state = snapshot.state
|
|
107
|
+
const stateLabel = describeStateLabel(state)
|
|
100
108
|
|
|
101
109
|
if (!hasPlans) {
|
|
102
110
|
return issues
|
|
103
111
|
}
|
|
104
112
|
|
|
105
113
|
if (!state.exists) {
|
|
106
|
-
issues.push(
|
|
114
|
+
issues.push(`当前已存在方案包,但${stateLabel} 缺失`)
|
|
107
115
|
return issues
|
|
108
116
|
}
|
|
109
117
|
|
|
110
118
|
if (!state.referencedPlanDir) {
|
|
111
|
-
issues.push(
|
|
119
|
+
issues.push(`${stateLabel} 未记录活跃方案路径`)
|
|
112
120
|
}
|
|
113
121
|
if (!state.sections['主线目标']) {
|
|
114
|
-
issues.push(
|
|
122
|
+
issues.push(`${stateLabel} 缺少“主线目标”`)
|
|
115
123
|
}
|
|
116
124
|
if (!state.sections['正在做什么']) {
|
|
117
|
-
issues.push(
|
|
125
|
+
issues.push(`${stateLabel} 缺少“正在做什么”`)
|
|
118
126
|
}
|
|
119
127
|
if (!state.sections['下一步']) {
|
|
120
|
-
issues.push(
|
|
128
|
+
issues.push(`${stateLabel} 缺少“下一步”`)
|
|
121
129
|
}
|
|
122
130
|
|
|
123
131
|
return issues
|
|
@@ -137,7 +145,7 @@ export function buildStateSyncHintFromSnapshot(snapshot) {
|
|
|
137
145
|
|
|
138
146
|
export function buildStateRoleHintFromSnapshot(snapshot) {
|
|
139
147
|
if (!snapshot.state.exists || snapshot.plans.length > 0) return ''
|
|
140
|
-
return
|
|
148
|
+
return `恢复约束:当前仅检测到${describeStateLabel(snapshot.state)};先以当前用户消息、显式命令和代码事实确认主线,STATE.md 只用于找回上次停在哪,不是当前任务的自动授权或唯一判断依据。`
|
|
141
149
|
}
|
|
142
150
|
|
|
143
151
|
export function buildUiContractHint(cwd, snapshot) {
|
|
@@ -154,10 +162,10 @@ export function buildUiContractHint(cwd, snapshot) {
|
|
|
154
162
|
|
|
155
163
|
const extraHints = []
|
|
156
164
|
if (styleAdvisorRequired) {
|
|
157
|
-
extraHints.push('若当前 UI
|
|
165
|
+
extraHints.push('若当前 UI 契约要求 style advisor,收尾前需复用 `.helloagents/.ralph-advisor.json` 留下独立复查证据')
|
|
158
166
|
}
|
|
159
167
|
if (visualValidationRequired) {
|
|
160
|
-
extraHints.push('若当前 UI
|
|
168
|
+
extraHints.push('若当前 UI 契约要求视觉验收,收尾前需写 `.helloagents/.ralph-visual.json` 记录关键视口、状态与结论')
|
|
161
169
|
}
|
|
162
170
|
return `UI 约束提示:如本次属于视觉/交互链路,设计决策优先级固定为:当前活跃 plan.md / prd/03-ui-design.md → ${describeProjectStoreFile(cwd, 'DESIGN.md')} → hello-ui。${extraHints.length > 0 ? ` ${extraHints.join(';')}。` : ''}`
|
|
163
171
|
}
|
|
@@ -2,7 +2,11 @@ import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
|
2
2
|
import { isAbsolute, join, normalize } from 'node:path'
|
|
3
3
|
|
|
4
4
|
import { getPlanContractIssues, readPlanContract } from './plan-contract.mjs'
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
getProjectPlansDir,
|
|
7
|
+
getProjectSessionStateScope,
|
|
8
|
+
resolveProjectPlanDir,
|
|
9
|
+
} from './project-storage.mjs'
|
|
6
10
|
|
|
7
11
|
const PLAN_TEMPLATE_MARKERS = {
|
|
8
12
|
'requirements.md': [
|
|
@@ -170,15 +174,22 @@ function comparePlanEntries(a, b) {
|
|
|
170
174
|
return a.planName.localeCompare(b.planName)
|
|
171
175
|
}
|
|
172
176
|
|
|
173
|
-
export function readStateSnapshot(cwd) {
|
|
174
|
-
const
|
|
177
|
+
export function readStateSnapshot(cwd, options = {}) {
|
|
178
|
+
const stateScope = getProjectSessionStateScope(cwd, options)
|
|
179
|
+
const statePath = stateScope.statePath
|
|
180
|
+
const exists = existsSync(statePath)
|
|
175
181
|
const content = readText(statePath)
|
|
176
182
|
const sections = parseMarkdownSections(content)
|
|
177
183
|
const referencedPlanDir = resolvePlanDir(cwd, sections['方案'])
|
|
178
184
|
|
|
179
185
|
return {
|
|
180
186
|
statePath,
|
|
181
|
-
|
|
187
|
+
stateScope: stateScope.stateScope,
|
|
188
|
+
stateSessionToken: stateScope.stateSessionToken,
|
|
189
|
+
stateSessionMode: stateScope.stateSessionMode,
|
|
190
|
+
stateBranch: stateScope.stateBranch,
|
|
191
|
+
sessionScoped: stateScope.stateScope === 'session',
|
|
192
|
+
exists,
|
|
182
193
|
content,
|
|
183
194
|
sections,
|
|
184
195
|
referencedPlanDir,
|
|
@@ -230,8 +241,8 @@ export function listPlanPackages(cwd) {
|
|
|
230
241
|
.sort(comparePlanEntries)
|
|
231
242
|
}
|
|
232
243
|
|
|
233
|
-
export function getWorkflowSnapshot(cwd) {
|
|
234
|
-
const state = readStateSnapshot(cwd)
|
|
244
|
+
export function getWorkflowSnapshot(cwd, options = {}) {
|
|
245
|
+
const state = readStateSnapshot(cwd, options)
|
|
235
246
|
const plans = listPlanPackages(cwd).map((entry) => ({
|
|
236
247
|
...entry,
|
|
237
248
|
referencedByState: state.referencedPlanDir ? normalize(entry.dirPath) === normalize(state.referencedPlanDir) : false,
|
|
@@ -59,7 +59,7 @@ function buildConsolidateAction(recommendation) {
|
|
|
59
59
|
phase: 'consolidate',
|
|
60
60
|
mode: recommendation.mode,
|
|
61
61
|
routeHint: recommendation.guidance,
|
|
62
|
-
gateHint: '
|
|
62
|
+
gateHint: '交付把关:审查与验证证据已满足;先写 `.helloagents/.ralph-closeout.json` 记录需求覆盖与交付清单,再完成 STATE.md / 归档后才可交付。',
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -67,7 +67,7 @@ function buildConsolidateAction(recommendation) {
|
|
|
67
67
|
phase: 'consolidate',
|
|
68
68
|
mode: recommendation.mode || 'ready',
|
|
69
69
|
routeHint: recommendation.guidance,
|
|
70
|
-
gateHint: '
|
|
70
|
+
gateHint: '交付把关:当前已具备收尾证据;完成 STATE.md、知识沉淀与归档后即可交付。',
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -88,7 +88,7 @@ function buildVerifyAction(plan, verifyMode) {
|
|
|
88
88
|
phase: 'verify',
|
|
89
89
|
mode: verifyMode.mode,
|
|
90
90
|
routeHint: verifyMode.guidance,
|
|
91
|
-
gateHint:
|
|
91
|
+
gateHint: `交付把关:进入 CONSOLIDATE 前,必须先完成 reviewer / hello-review 范围审查,再完成 tester / hello-verify 全量验证,并留下最新验证证据;两步都通过后才可交付。${gateSuffix}`.trim(),
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
if (verifyMode.mode === 'metadata-first') {
|
|
@@ -97,8 +97,8 @@ function buildVerifyAction(plan, verifyMode) {
|
|
|
97
97
|
mode: verifyMode.mode,
|
|
98
98
|
routeHint: verifyMode.guidance,
|
|
99
99
|
gateHint: plan.contractIssues.length > 0
|
|
100
|
-
? '
|
|
101
|
-
: '
|
|
100
|
+
? '交付把关:当前还不能进入 CONSOLIDATE;先补齐 `contract.json` 中的 `verifyMode`、`reviewerFocus`、`testerFocus`,再进入 reviewer / tester。'
|
|
101
|
+
: '交付把关:当前还不能进入 CONSOLIDATE;先补齐 tasks.md 中每个任务的“涉及文件”“完成标准”和“验证方式”,再进入 reviewer / tester。',
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
|
|
@@ -106,7 +106,7 @@ function buildVerifyAction(plan, verifyMode) {
|
|
|
106
106
|
phase: 'verify',
|
|
107
107
|
mode: verifyMode.mode,
|
|
108
108
|
routeHint: verifyMode.guidance,
|
|
109
|
-
gateHint:
|
|
109
|
+
gateHint: `交付把关:进入 CONSOLIDATE 前,先完成 tester / hello-verify 全量验证并留下最新验证证据,再针对失败点或关键边界补充 hello-review;确认通过后才可交付。${gateSuffix}`.trim(),
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -124,13 +124,13 @@ export function buildDeliveryActionFromSnapshot(snapshot, cwd, recommendation =
|
|
|
124
124
|
if (recommendation.nextCommand === 'build') {
|
|
125
125
|
return {
|
|
126
126
|
phase: 'build',
|
|
127
|
-
gateHint: '
|
|
127
|
+
gateHint: '交付把关:当前还不能报告完成;先回到 ~build 完成剩余任务,再进入 ~verify。',
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
if (recommendation.nextCommand === 'plan') {
|
|
131
131
|
return {
|
|
132
132
|
phase: 'plan',
|
|
133
|
-
gateHint: '
|
|
133
|
+
gateHint: '交付把关:当前还不能报告完成;先回到 ~plan 修复或补齐当前方案包,再进入 ~build / ~verify。',
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
|
|
@@ -153,7 +153,7 @@ function buildPlanRecommendation(scopeLabel, plan, classification) {
|
|
|
153
153
|
? `${scopeLabel} "${plan.planName}" 仍不完整(${classification.details.join(';')})。`
|
|
154
154
|
: `${scopeLabel} "${plan.planName}" 尚未形成可执行任务清单。`,
|
|
155
155
|
guidance: classification.status === 'incomplete'
|
|
156
|
-
? '优先先走 ~plan
|
|
156
|
+
? '优先先走 ~plan 修复或补全当前方案包,再进入实现或验证;不要把不完整的结构化产物直接当成可交付依据。'
|
|
157
157
|
: '先回到 ~plan 补齐 tasks.md 的原子任务,再进入实现、验证或收尾。',
|
|
158
158
|
}
|
|
159
159
|
}
|
|
@@ -180,7 +180,7 @@ function buildClosedRecommendation(scopeLabel, plan, cwd) {
|
|
|
180
180
|
status: 'closed',
|
|
181
181
|
nextCommand: 'verify',
|
|
182
182
|
nextPath: '~verify -> CONSOLIDATE',
|
|
183
|
-
summary: `${scopeLabel} "${plan.planName}"
|
|
183
|
+
summary: `${scopeLabel} "${plan.planName}" 的任务已全部闭合,但验证契约仍未结构化。`,
|
|
184
184
|
guidance: closedPlanEvidence.verifyMode.guidance,
|
|
185
185
|
}
|
|
186
186
|
}
|
|
@@ -197,7 +197,7 @@ function buildClosedRecommendation(scopeLabel, plan, cwd) {
|
|
|
197
197
|
status: 'closed',
|
|
198
198
|
nextCommand: 'verify',
|
|
199
199
|
nextPath: '~verify -> CONSOLIDATE',
|
|
200
|
-
summary: `${scopeLabel} "${plan.planName}" 的任务已闭合,但当前 UI
|
|
200
|
+
summary: `${scopeLabel} "${plan.planName}" 的任务已闭合,但当前 UI 契约仍要求独立 advisor 复查与视觉验收。`,
|
|
201
201
|
guidance: '先在 ~verify 阶段完成独立 advisor / style advisor 复查,并写入 `.helloagents/.ralph-advisor.json`;再完成视觉验收并写入 `.helloagents/.ralph-visual.json`,记录 reason、tooling、screensChecked、statesChecked、status 与 summary;两项都通过后再进入 CONSOLIDATE。',
|
|
202
202
|
}
|
|
203
203
|
}
|
|
@@ -209,7 +209,7 @@ function buildClosedRecommendation(scopeLabel, plan, cwd) {
|
|
|
209
209
|
status: 'closed',
|
|
210
210
|
nextCommand: 'verify',
|
|
211
211
|
nextPath: '~verify -> CONSOLIDATE',
|
|
212
|
-
summary: `${scopeLabel} "${plan.planName}"
|
|
212
|
+
summary: `${scopeLabel} "${plan.planName}" 的任务已闭合,但当前契约仍要求独立 advisor 复查。`,
|
|
213
213
|
guidance: '先在 ~verify 阶段完成独立 advisor / style advisor 复查,并写入 `.helloagents/.ralph-advisor.json` 记录复查原因、focus、来源与结论;advisor 通过后再进入 CONSOLIDATE。',
|
|
214
214
|
}
|
|
215
215
|
}
|
|
@@ -221,7 +221,7 @@ function buildClosedRecommendation(scopeLabel, plan, cwd) {
|
|
|
221
221
|
status: 'closed',
|
|
222
222
|
nextCommand: 'verify',
|
|
223
223
|
nextPath: '~verify -> CONSOLIDATE',
|
|
224
|
-
summary: `${scopeLabel} "${plan.planName}" 的任务已闭合,但当前 UI
|
|
224
|
+
summary: `${scopeLabel} "${plan.planName}" 的任务已闭合,但当前 UI 契约仍要求视觉验收。`,
|
|
225
225
|
guidance: '先在 ~verify 阶段完成视觉验收,并写入 `.helloagents/.ralph-visual.json` 记录 reason、tooling、screensChecked、statesChecked、status 与 summary;视觉验收通过后再进入 CONSOLIDATE。',
|
|
226
226
|
}
|
|
227
227
|
}
|
|
@@ -307,7 +307,7 @@ function buildBuildOrchestrationHint(plan) {
|
|
|
307
307
|
return `编排提示:检测到可并行的开放任务;如需提速,可先读取 hello-subagent 再按 tasks.md 分派。任务A:${describeTask(pair[0])};任务B:${describeTask(pair[1])}。`
|
|
308
308
|
}
|
|
309
309
|
if (openTasks.every((item) => item.files.length === 0)) {
|
|
310
|
-
return '编排提示:当前有多个开放任务,但 tasks.md
|
|
310
|
+
return '编排提示:当前有多个开放任务,但 tasks.md 尚未写清契约元数据;考虑子代理并行前先补足文件路径、完成标准与验证方式。'
|
|
311
311
|
}
|
|
312
312
|
return '编排提示:当前仍有多个开放任务,但文件范围存在重叠;暂不建议并行子代理,优先串行推进。'
|
|
313
313
|
}
|
|
@@ -16,27 +16,27 @@ import {
|
|
|
16
16
|
buildRecommendation,
|
|
17
17
|
} from './workflow-recommendation.mjs'
|
|
18
18
|
|
|
19
|
-
export function getDeliveryAction(cwd) {
|
|
20
|
-
const snapshot = getWorkflowSnapshot(cwd)
|
|
19
|
+
export function getDeliveryAction(cwd, options = {}) {
|
|
20
|
+
const snapshot = getWorkflowSnapshot(cwd, options)
|
|
21
21
|
const recommendation = buildRecommendation(snapshot, cwd)
|
|
22
22
|
return buildDeliveryActionFromSnapshot(snapshot, cwd, recommendation)
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export function getWorkflowRecommendation(cwd) {
|
|
26
|
-
return buildRecommendation(getWorkflowSnapshot(cwd), cwd)
|
|
25
|
+
export function getWorkflowRecommendation(cwd, options = {}) {
|
|
26
|
+
return buildRecommendation(getWorkflowSnapshot(cwd, options), cwd)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export function buildStateSyncHint(cwd) {
|
|
30
|
-
return buildStateSyncHintFromSnapshot(getWorkflowSnapshot(cwd))
|
|
29
|
+
export function buildStateSyncHint(cwd, options = {}) {
|
|
30
|
+
return buildStateSyncHintFromSnapshot(getWorkflowSnapshot(cwd, options))
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export function buildDeliveryGateHint(cwd) {
|
|
34
|
-
const snapshot = getWorkflowSnapshot(cwd)
|
|
33
|
+
export function buildDeliveryGateHint(cwd, options = {}) {
|
|
34
|
+
const snapshot = getWorkflowSnapshot(cwd, options)
|
|
35
35
|
return buildDeliveryGateHintFromSnapshot(snapshot, cwd, buildRecommendation(snapshot, cwd))
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export function buildWorkflowRouteHint(cwd) {
|
|
39
|
-
const snapshot = getWorkflowSnapshot(cwd)
|
|
38
|
+
export function buildWorkflowRouteHint(cwd, options = {}) {
|
|
39
|
+
const snapshot = getWorkflowSnapshot(cwd, options)
|
|
40
40
|
const recommendation = buildRecommendation(snapshot, cwd)
|
|
41
41
|
const stateSyncHint = buildStateSyncHintFromSnapshot(snapshot)
|
|
42
42
|
const stateRoleHint = buildStateRoleHintFromSnapshot(snapshot)
|
|
@@ -57,8 +57,8 @@ export function buildWorkflowRouteHint(cwd) {
|
|
|
57
57
|
function buildCommandRouteMessage(skillName, recommendation, verifyModeHint) {
|
|
58
58
|
if (skillName === 'auto') {
|
|
59
59
|
return recommendation.stage === 'consolidate'
|
|
60
|
-
? `当前工作流约束:${recommendation.summary} 当前建议下一阶段:CONSOLIDATE。${recommendation.guidance}
|
|
61
|
-
: `当前工作流约束:${recommendation.summary} 当前建议主路径:${recommendation.nextPath}。${recommendation.guidance}
|
|
60
|
+
? `当前工作流约束:${recommendation.summary} 当前建议下一阶段:CONSOLIDATE。${recommendation.guidance} 若本次明确使用 ~auto,则在未命中阻塞判定时直接完成当前收尾,不再额外停下询问。`
|
|
61
|
+
: `当前工作流约束:${recommendation.summary} 当前建议主路径:${recommendation.nextPath}。${recommendation.guidance} 若本次明确使用 ~auto,则命中主路径后继续衔接后续阶段,除非触发阻塞判定,否则不要在方案/PRD 阶段额外停下。`
|
|
62
62
|
}
|
|
63
63
|
if (skillName === 'plan') {
|
|
64
64
|
if (recommendation.stage === 'consolidate') {
|
|
@@ -87,8 +87,8 @@ function buildCommandRouteMessage(skillName, recommendation, verifyModeHint) {
|
|
|
87
87
|
return `当前工作流约束:${recommendation.summary} 当前建议下一命令:~${recommendation.nextCommand}。${recommendation.guidance}`
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
export function buildCommandRouteHint(skillName, cwd) {
|
|
91
|
-
const snapshot = getWorkflowSnapshot(cwd)
|
|
90
|
+
export function buildCommandRouteHint(skillName, cwd, options = {}) {
|
|
91
|
+
const snapshot = getWorkflowSnapshot(cwd, options)
|
|
92
92
|
const recommendation = buildRecommendation(snapshot, cwd)
|
|
93
93
|
const contextHints = [
|
|
94
94
|
buildStateRoleHintFromSnapshot(snapshot),
|
package/skills/_meta/SKILL.md
CHANGED
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ~auto
|
|
3
|
-
description:
|
|
3
|
+
description: 自动编排命令 — 自动选择并串联 ~idea / ~plan / ~build / ~verify / ~prd,默认持续推进直到交付完成(~auto 命令)
|
|
4
4
|
policy:
|
|
5
5
|
allow_implicit_invocation: false
|
|
6
6
|
---
|
|
7
7
|
Trigger: ~auto <任务描述>
|
|
8
8
|
|
|
9
|
-
`~auto`
|
|
10
|
-
`~auto`
|
|
9
|
+
`~auto` 是自动编排命令。它根据任务类型、复杂度、风险等级与项目状态,自动在 `~idea`、`~plan`、`~build`、`~verify`、`~prd` 之间选择合适主路径,并把这些阶段串成一条连续交付链路。
|
|
10
|
+
`~auto` 不止做一次选路;主路径一旦确定,就按需要继续衔接后续阶段,默认持续推进直到完成交付,只有命中 bootstrap 的阻塞判定时才停下。
|
|
11
11
|
|
|
12
12
|
## 铁律
|
|
13
13
|
- 不为了“自动化”而强行走重流程
|
|
14
14
|
- 复杂度与风险不足以支撑更重路径时,优先选更轻但能保证质量的路线
|
|
15
15
|
- `T3` 高风险或不可逆链路默认不直接进入 `~build`;优先先走 `~plan` 或 `~prd`,纯审查/纯验证请求才可先进入 `~verify`
|
|
16
|
-
-
|
|
16
|
+
- 主路径一旦确定,立即读取对应 command skill,并在阶段完成后继续衔接后续阶段,避免同一任务重复探索或重复等待
|
|
17
17
|
- 选路不替代授权;涉及外部副作用或高风险不可逆操作时,仍遵守 bootstrap 的阻塞判定与确认规则
|
|
18
|
-
-
|
|
18
|
+
- 用户显式使用 `~auto`,表示已授权在当前任务边界内沿选定主路径持续执行;`~plan` / `~prd` 作为中间阶段时,不再额外询问“是否开始执行”,除非仍有真实阻塞
|
|
19
|
+
- 优先消费当前上下文中已注入的 ROUTE / TIER、当前工作流约束与项目状态;不要在 `~auto` 内另建一套关键词路由表
|
|
19
20
|
|
|
20
21
|
## 流程
|
|
21
22
|
|
|
22
23
|
### 0. 当前工作流优先
|
|
23
24
|
|
|
24
|
-
-
|
|
25
|
+
- 若当前上下文中已注入“当前工作流提示”或“当前工作流约束”,优先服从其中的推荐下一命令 / 主路径
|
|
25
26
|
- 默认原则:
|
|
26
27
|
- 活跃方案包不完整或缺少任务清单 → 先 `~plan`
|
|
27
28
|
- 活跃方案包仍在执行 → 先 `~build`,完成当前实现后再 `~verify`
|
|
@@ -31,7 +32,7 @@ Trigger: ~auto <任务描述>
|
|
|
31
32
|
### 1. 选路
|
|
32
33
|
|
|
33
34
|
- 先按当前上下文里已注入的 ROUTE / TIER 语义约束判断,不依赖关键词命中做机械分流
|
|
34
|
-
-
|
|
35
|
+
- 若本轮没有足够的注入约束,再结合以下信号补足判断:影响范围、风险等级、是否需要结构化产物、是否已有活跃方案包、用户是否只想先比较方向
|
|
35
36
|
- 选路优先级:
|
|
36
37
|
- 纯探索 / 点子 / 方向比较 → `~idea`
|
|
37
38
|
- 明确要求验证 / 审查 / 跑检查 → `~verify`
|
|
@@ -43,10 +44,10 @@ Trigger: ~auto <任务描述>
|
|
|
43
44
|
|
|
44
45
|
- `T0` → 保持在 `~idea`,不创建项目文件
|
|
45
46
|
- `T1` → 在 `~build` / `~verify` 间选择最短可交付路径
|
|
46
|
-
- `T2` →
|
|
47
|
+
- `T2` → 需要结构化产物或范围未完全收敛时优先 `~plan`
|
|
47
48
|
- `T3` → 纯审查/验真走 `~verify`;其余默认 `~plan` 或 `~prd`,待方案与风险边界明确后再进入实现
|
|
48
49
|
|
|
49
|
-
### 3.
|
|
50
|
+
### 3. 读取对应命令并执行主路径
|
|
50
51
|
|
|
51
52
|
- 选中 `idea` → 读取 `skills/commands/idea/SKILL.md`
|
|
52
53
|
- 选中 `plan` → 读取 `skills/commands/plan/SKILL.md`
|
|
@@ -55,3 +56,17 @@ Trigger: ~auto <任务描述>
|
|
|
55
56
|
- 选中 `prd` → 读取 `skills/commands/prd/SKILL.md`
|
|
56
57
|
|
|
57
58
|
不要额外读取未选中的 command skill。
|
|
59
|
+
|
|
60
|
+
### 4. 自动衔接直到完成
|
|
61
|
+
|
|
62
|
+
- 若主路径是 `~build` → 完成实现后继续进入 `~verify`,再按当前链路进入收尾
|
|
63
|
+
- 若主路径是 `~plan` → 方案包落地后,若当前链路来自 `~auto` 且未命中阻塞判定,直接继续进入 `~build`,不要把“方案已形成”当作最终停点
|
|
64
|
+
- 若主路径是 `~prd` → PRD / 任务 / 契约落地后,若当前链路来自 `~auto` 且未命中阻塞判定,按收敛结果继续进入 `~build`,必要时先补一轮轻量 `~plan`
|
|
65
|
+
- 若主路径是 `~verify` → 完成审查 / 验证 / 收尾后结束
|
|
66
|
+
- 若主路径是 `~idea`,且用户本意就是探索/比较,则在探索输出后结束;若探索后已收敛成明确方向且当前任务仍要求落地,则继续进入 `~plan` 或 `~build`
|
|
67
|
+
|
|
68
|
+
### 5. 何时允许停下
|
|
69
|
+
|
|
70
|
+
- 仅在 bootstrap 的阻塞判定成立时停下:真实歧义、缺必需信息/文件/凭据、未授权外部副作用、高风险或不可逆操作
|
|
71
|
+
- 不得把 `~plan` / `~prd` 中“是否进入执行”的默认询问原样带入 `~auto` 链路
|
|
72
|
+
- 不得把“给出方案”“给出任务列表”“给出建议下一步”当作 `~auto` 的默认完成态;用户显式要求 `~auto` 时,默认目标是把当前任务推进到可交付完成
|
|
@@ -8,7 +8,7 @@ Trigger: ~build [description]
|
|
|
8
8
|
|
|
9
9
|
`~build` 是执行实现命令。它负责读取现有需求、方案包与项目上下文,完成实现、局部验证、修复循环,并把结果衔接到后续验证与收尾。
|
|
10
10
|
执行 `~build` 时,通用阶段边界按当前已加载 bootstrap 执行;本 skill 负责补充实现前定位、实现约束,以及进入 `~verify` / 收尾前的实现边界。
|
|
11
|
-
`.helloagents/` 在本 skill
|
|
11
|
+
`.helloagents/` 在本 skill 中统一按项目级存储路径理解:`STATE.md` 与 `.ralph-*.json` 保持项目本地;若当前上下文中的“当前项目存储”给出 `state_path`,本轮恢复快照统一读写该路径;若 `project_store_mode=repo-shared`,知识库、`DESIGN.md`、`verify.yaml` 与方案包按当前上下文中已注入的项目知识/方案目录解析。
|
|
12
12
|
|
|
13
13
|
## 铁律
|
|
14
14
|
- 默认先定位上下文与范围,再修改代码
|
|
@@ -20,15 +20,15 @@ Trigger: ~build [description]
|
|
|
20
20
|
|
|
21
21
|
### 1. 恢复与定位
|
|
22
22
|
|
|
23
|
-
- 优先按当前已加载 bootstrap 的“.helloagents/ 文件读取优先级”恢复当前链路;若当前消息显式继续既有链路,或会话刚经历恢复 / 压缩,先读取 `.helloagents/STATE.md`
|
|
23
|
+
- 优先按当前已加载 bootstrap 的“.helloagents/ 文件读取优先级”恢复当前链路;若当前消息显式继续既有链路,或会话刚经历恢复 / 压缩,先读取 `.helloagents/STATE.md` 作为恢复快照(若当前项目存储给出 `state_path`,则优先读取该当前会话的 `STATE.md`),再用当前用户消息、活跃方案包 / PRD 与代码事实校正主线
|
|
24
24
|
- 若存在最近的活跃方案包,读取对应的:
|
|
25
25
|
- `requirements.md`
|
|
26
26
|
- `plan.md`
|
|
27
27
|
- `tasks.md`
|
|
28
28
|
- `contract.json`
|
|
29
|
-
- 实现时优先把 `tasks.md`
|
|
29
|
+
- 实现时优先把 `tasks.md` 中每个任务的“完成标准”当作本轮实现约束,不要只按任务标题猜测范围
|
|
30
30
|
- `contract.json` 存在时,优先按其中的 `verifyMode`、`reviewerFocus`、`testerFocus` 理解后续验证边界
|
|
31
|
-
-
|
|
31
|
+
- 若当前上下文中已注入“当前工作流约束”或“当前建议下一命令”,先服从它;只有推荐仍为 `~build`,或用户明确提出新增实现范围时,才继续 `~build`
|
|
32
32
|
- 其余项目知识库与相关代码文件,按 bootstrap 的项目上下文规则按需读取
|
|
33
33
|
- 若任务涉及 UI,按以下优先级读取并遵循:当前活跃 `plan.md` / PRD 中的 UI 决策 > 逻辑 `.helloagents/DESIGN.md`(实际路径按当前项目存储模式解析) > `hello-ui` 通用规则
|
|
34
34
|
- 若已激活项目且当前任务属于整页新建、设计系统改造、或跨多个组件的视觉重做,但逻辑 `.helloagents/DESIGN.md` 不存在,先按模板创建最小设计契约,再继续大规模实现
|
|
@@ -7,13 +7,13 @@ policy:
|
|
|
7
7
|
Trigger: ~clean
|
|
8
8
|
|
|
9
9
|
执行 `~clean` 时,方案包归档、临时文件清理和 `STATE.md` 更新边界按当前已加载 bootstrap 执行;本命令只负责判定哪些方案包可以清理,以及输出清理摘要。
|
|
10
|
-
`.helloagents/` 在本 skill
|
|
10
|
+
`.helloagents/` 在本 skill 中统一按项目级存储路径理解:`STATE.md` 和临时运行态文件保持项目本地;若当前上下文中的“当前项目存储”给出 `state_path`,本轮恢复快照统一读写该路径;若 `project_store_mode=repo-shared`,`plans/` 与 `archive/` 按当前上下文中已注入的项目知识/方案目录解析。
|
|
11
11
|
|
|
12
12
|
## 流程
|
|
13
13
|
|
|
14
|
-
1.
|
|
14
|
+
1. 扫描 `.helloagents/plans/` 下的方案包(按当前项目存储模式解析;`project_store_mode=repo-shared` 时使用共享知识/方案目录)
|
|
15
15
|
2. 判定完成状态:优先以 tasks.md 中所有任务已标记 [√] 为准;只有任务清单无法判断时,才把 `STATE.md` 中与当前方案一致的“主线目标”+“正在做什么”作为辅助信号,避免把旧恢复快照误当当前主线
|
|
16
|
-
3. 已完成的方案包 → 按 bootstrap
|
|
16
|
+
3. 已完成的方案包 → 按 bootstrap 的归档规则移入 `.helloagents/archive/YYYY-MM/`(按当前项目存储模式解析),并同步更新 `.helloagents/archive/_index.md`
|
|
17
17
|
4. 清理 bootstrap 中定义的临时文件
|
|
18
18
|
5. 按 bootstrap 的流程状态规则更新 `STATE.md`;若当前状态指向已归档方案包,则清空对应方案路径
|
|
19
19
|
7. 输出清理摘要(归档了几个方案包、清理了哪些文件)
|
|
@@ -20,7 +20,7 @@ Trigger: ~commit [message]
|
|
|
20
20
|
- ""(空,默认)→ 不添加归属
|
|
21
21
|
- 有内容(如 "Co-Authored-By: HelloAGENTS")→ 添加该内容到 commit message
|
|
22
22
|
6. 执行 git commit
|
|
23
|
-
7.
|
|
23
|
+
7. 若当前 `STATE.md` 已存在(优先取当前项目存储中的 `state_path`,未注入时回退 `.helloagents/STATE.md`),按 bootstrap 的“已有则更新”规则同步当前已提交状态
|
|
24
24
|
|
|
25
25
|
## 知识库同步
|
|
26
26
|
提交后,继续复用上方已解析的同一份设置获取 `kb_create_mode`,不要再次读取 `~/.helloagents/helloagents.json`:
|