helloagents 3.0.12 → 3.0.16-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 +6 -4
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +182 -35
- package/README_CN.md +184 -37
- package/bootstrap-lite.md +32 -26
- package/bootstrap.md +35 -29
- package/cli.mjs +119 -11
- package/gemini-extension.json +1 -1
- package/install.ps1 +128 -0
- package/install.sh +121 -0
- package/package.json +23 -4
- package/scripts/advisor-state.mjs +36 -63
- package/scripts/capability-registry.mjs +4 -4
- package/scripts/cli-branch.mjs +84 -0
- package/scripts/cli-codex-config.mjs +14 -20
- package/scripts/cli-codex.mjs +32 -38
- package/scripts/cli-doctor-render.mjs +4 -0
- package/scripts/cli-doctor.mjs +40 -30
- package/scripts/cli-host-detect.mjs +0 -1
- package/scripts/cli-hosts.mjs +16 -8
- package/scripts/cli-lifecycle-hosts.mjs +119 -32
- package/scripts/cli-lifecycle.mjs +24 -13
- package/scripts/cli-messages.mjs +34 -16
- package/scripts/cli-runtime-carrier.mjs +15 -0
- package/scripts/cli-runtime-root.mjs +72 -0
- package/scripts/cli-toml.mjs +0 -79
- package/scripts/cli-utils.mjs +30 -4
- package/scripts/closeout-state.mjs +35 -62
- package/scripts/delivery-gate-messages.mjs +70 -0
- package/scripts/delivery-gate.mjs +9 -75
- package/scripts/guard-rules.mjs +42 -42
- package/scripts/guard.mjs +44 -24
- package/scripts/notify-context.mjs +19 -28
- package/scripts/notify-events.mjs +3 -1
- package/scripts/notify-gates.mjs +2 -0
- package/scripts/notify-route.mjs +9 -7
- package/scripts/notify-ui.mjs +42 -32
- package/scripts/notify.mjs +72 -36
- package/scripts/project-storage.mjs +35 -66
- package/scripts/ralph-loop.mjs +36 -31
- package/scripts/replay-state.mjs +31 -128
- package/scripts/review-state.mjs +34 -61
- package/scripts/runtime-artifacts.mjs +95 -0
- package/scripts/runtime-context.mjs +35 -29
- package/scripts/runtime-scope.mjs +313 -0
- package/scripts/session-capsule.mjs +202 -0
- package/scripts/turn-state-cli.mjs +17 -0
- package/scripts/turn-state.mjs +185 -66
- package/scripts/turn-stop-gate.mjs +24 -6
- package/scripts/verify-state.mjs +34 -85
- package/scripts/visual-state.mjs +38 -65
- package/scripts/workflow-core.mjs +3 -3
- package/scripts/workflow-plan-files.mjs +1 -1
- package/scripts/workflow-recommendation.mjs +17 -13
- package/scripts/workflow-state.mjs +5 -5
- package/skills/commands/build/SKILL.md +1 -1
- package/skills/commands/commit/SKILL.md +1 -1
- package/skills/commands/help/SKILL.md +5 -3
- package/skills/commands/loop/SKILL.md +1 -1
- package/skills/commands/plan/SKILL.md +8 -6
- package/skills/commands/prd/SKILL.md +5 -3
- package/skills/commands/verify/SKILL.md +5 -5
- package/skills/hello-debug/SKILL.md +20 -3
- package/skills/hello-review/SKILL.md +2 -2
- package/skills/hello-subagent/SKILL.md +2 -2
- package/skills/hello-test/SKILL.md +6 -2
- package/skills/hello-ui/SKILL.md +7 -7
- package/skills/hello-verify/SKILL.md +10 -7
- package/skills/helloagents/SKILL.md +14 -9
- package/templates/context.md +6 -0
- package/templates/plans/plan.md +3 -0
- package/templates/plans/tasks.md +8 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "helloagents",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.16-beta.1",
|
|
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",
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
},
|
|
13
13
|
"bin": {
|
|
14
14
|
"helloagents": "cli.mjs",
|
|
15
|
-
"helloagents-js": "cli.mjs"
|
|
15
|
+
"helloagents-js": "cli.mjs",
|
|
16
|
+
"helloagents-turn-state": "scripts/turn-state-cli.mjs"
|
|
16
17
|
},
|
|
17
18
|
"scripts": {
|
|
18
19
|
"test": "node --test",
|
|
@@ -20,7 +21,13 @@
|
|
|
20
21
|
"preuninstall": "node cli.mjs preuninstall",
|
|
21
22
|
"uninstall": "node cli.mjs preuninstall",
|
|
22
23
|
"postuninstall": "node cli.mjs preuninstall",
|
|
23
|
-
"prepublishOnly": "node cli.mjs sync-version"
|
|
24
|
+
"prepublishOnly": "node cli.mjs sync-version",
|
|
25
|
+
"deploy": "node cli.mjs install",
|
|
26
|
+
"deploy:standby": "node cli.mjs install --all --standby",
|
|
27
|
+
"deploy:global": "node cli.mjs install --all --global",
|
|
28
|
+
"sync-hosts": "node cli.mjs update",
|
|
29
|
+
"cleanup-hosts": "node cli.mjs cleanup",
|
|
30
|
+
"switch-branch": "node cli.mjs switch-branch"
|
|
24
31
|
},
|
|
25
32
|
"files": [
|
|
26
33
|
"cli.mjs",
|
|
@@ -32,11 +39,23 @@
|
|
|
32
39
|
"bootstrap.md",
|
|
33
40
|
"bootstrap-lite.md",
|
|
34
41
|
"README_CN.md",
|
|
42
|
+
"install.sh",
|
|
43
|
+
"install.ps1",
|
|
35
44
|
"gemini-extension.json",
|
|
36
45
|
".claude-plugin/",
|
|
37
46
|
".codex-plugin/"
|
|
38
47
|
],
|
|
39
|
-
"keywords": [
|
|
48
|
+
"keywords": [
|
|
49
|
+
"ai",
|
|
50
|
+
"agent",
|
|
51
|
+
"claude",
|
|
52
|
+
"codex",
|
|
53
|
+
"cli",
|
|
54
|
+
"orchestration",
|
|
55
|
+
"subagent",
|
|
56
|
+
"hooks",
|
|
57
|
+
"skills"
|
|
58
|
+
],
|
|
40
59
|
"engines": {
|
|
41
60
|
"node": ">=18"
|
|
42
61
|
}
|
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
|
-
import { join } from 'node:path'
|
|
4
3
|
|
|
5
4
|
import { appendReplayEvent } from './replay-state.mjs'
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
import {
|
|
6
|
+
captureWorkspaceFingerprint,
|
|
7
|
+
clearRuntimeEvidence,
|
|
8
|
+
getRuntimeEvidencePath,
|
|
9
|
+
getRuntimeEvidenceRelativePath,
|
|
10
|
+
readRuntimeEvidence,
|
|
11
|
+
validateEvidenceFingerprint,
|
|
12
|
+
validateEvidenceTimestamp,
|
|
13
|
+
writeRuntimeEvidence,
|
|
14
|
+
} from './runtime-artifacts.mjs'
|
|
15
|
+
|
|
16
|
+
export const ADVISOR_EVIDENCE_FILE_NAME = 'advisor.json'
|
|
10
17
|
const VALID_ADVISOR_OUTCOMES = new Set(['clean', 'findings'])
|
|
11
18
|
const VALID_SOURCES = new Set(['claude', 'codex', 'gemini'])
|
|
12
19
|
|
|
@@ -24,20 +31,16 @@ function normalizeOutcome(value) {
|
|
|
24
31
|
return VALID_ADVISOR_OUTCOMES.has(normalized) ? normalized : ''
|
|
25
32
|
}
|
|
26
33
|
|
|
27
|
-
export function getAdvisorEvidencePath(cwd) {
|
|
28
|
-
return
|
|
34
|
+
export function getAdvisorEvidencePath(cwd, options = {}) {
|
|
35
|
+
return getRuntimeEvidencePath(cwd, ADVISOR_EVIDENCE_FILE_NAME, options)
|
|
29
36
|
}
|
|
30
37
|
|
|
31
|
-
export function readAdvisorEvidence(cwd) {
|
|
32
|
-
|
|
33
|
-
return JSON.parse(readFileSync(getAdvisorEvidencePath(cwd), 'utf-8'))
|
|
34
|
-
} catch {
|
|
35
|
-
return null
|
|
36
|
-
}
|
|
38
|
+
export function readAdvisorEvidence(cwd, options = {}) {
|
|
39
|
+
return readRuntimeEvidence(cwd, ADVISOR_EVIDENCE_FILE_NAME, options)
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
export function clearAdvisorEvidence(cwd) {
|
|
40
|
-
|
|
42
|
+
export function clearAdvisorEvidence(cwd, options = {}) {
|
|
43
|
+
clearRuntimeEvidence(cwd, ADVISOR_EVIDENCE_FILE_NAME, options)
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
export function normalizeAdvisorEvidence(input = {}) {
|
|
@@ -55,19 +58,19 @@ export function normalizeAdvisorEvidence(input = {}) {
|
|
|
55
58
|
}
|
|
56
59
|
}
|
|
57
60
|
|
|
58
|
-
export function writeAdvisorEvidence(cwd, input = {}) {
|
|
59
|
-
mkdirSync(join(cwd, '.helloagents'), { recursive: true })
|
|
61
|
+
export function writeAdvisorEvidence(cwd, input = {}, options = {}) {
|
|
60
62
|
const normalized = normalizeAdvisorEvidence(input)
|
|
61
63
|
const payload = {
|
|
62
64
|
updatedAt: new Date().toISOString(),
|
|
63
65
|
...normalized,
|
|
64
66
|
fingerprint: captureWorkspaceFingerprint(cwd),
|
|
65
67
|
}
|
|
66
|
-
|
|
68
|
+
writeRuntimeEvidence(cwd, ADVISOR_EVIDENCE_FILE_NAME, payload, options)
|
|
67
69
|
appendReplayEvent(cwd, {
|
|
68
70
|
event: 'advisor_evidence_written',
|
|
69
71
|
source: normalized.source,
|
|
70
72
|
skillName: normalized.originCommand,
|
|
73
|
+
payload: options.payload || {},
|
|
71
74
|
details: {
|
|
72
75
|
reason: normalized.reason,
|
|
73
76
|
focus: normalized.focus,
|
|
@@ -75,12 +78,12 @@ export function writeAdvisorEvidence(cwd, input = {}) {
|
|
|
75
78
|
consultedSources: normalized.consultedSources,
|
|
76
79
|
outcome: normalized.outcome,
|
|
77
80
|
},
|
|
78
|
-
artifacts: [
|
|
81
|
+
artifacts: [getRuntimeEvidenceRelativePath(cwd, ADVISOR_EVIDENCE_FILE_NAME, options)],
|
|
79
82
|
})
|
|
80
83
|
return payload
|
|
81
84
|
}
|
|
82
85
|
|
|
83
|
-
function readRequiredAdvisorEvidence(cwd, required) {
|
|
86
|
+
function readRequiredAdvisorEvidence(cwd, required, options = {}) {
|
|
84
87
|
if (!required) {
|
|
85
88
|
return {
|
|
86
89
|
required: false,
|
|
@@ -88,53 +91,23 @@ function readRequiredAdvisorEvidence(cwd, required) {
|
|
|
88
91
|
}
|
|
89
92
|
}
|
|
90
93
|
|
|
91
|
-
const evidence = readAdvisorEvidence(cwd)
|
|
94
|
+
const evidence = readAdvisorEvidence(cwd, options)
|
|
92
95
|
if (evidence) return { evidence }
|
|
93
96
|
return {
|
|
94
97
|
error: {
|
|
95
98
|
required: true,
|
|
96
99
|
status: 'missing',
|
|
97
|
-
details: ['
|
|
100
|
+
details: ['缺少当前契约要求的 advisor 证据'],
|
|
98
101
|
},
|
|
99
102
|
}
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
function validateAdvisorTimestamp(evidence, now) {
|
|
103
|
-
|
|
104
|
-
if (!Number.isFinite(updatedAt)) {
|
|
105
|
-
return {
|
|
106
|
-
required: true,
|
|
107
|
-
status: 'invalid',
|
|
108
|
-
evidence,
|
|
109
|
-
details: ['advisor evidence timestamp is invalid'],
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
if (now - updatedAt > ADVISOR_EVIDENCE_MAX_AGE_MS) {
|
|
113
|
-
return {
|
|
114
|
-
required: true,
|
|
115
|
-
status: 'stale-time',
|
|
116
|
-
evidence,
|
|
117
|
-
details: ['advisor evidence is older than 30 minutes'],
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return null
|
|
106
|
+
return validateEvidenceTimestamp(evidence, now, 'advisor 证据')
|
|
121
107
|
}
|
|
122
108
|
|
|
123
109
|
function validateAdvisorFingerprint(cwd, evidence) {
|
|
124
|
-
|
|
125
|
-
if (
|
|
126
|
-
currentFingerprint.available
|
|
127
|
-
&& evidence.fingerprint?.available
|
|
128
|
-
&& currentFingerprint.combined !== evidence.fingerprint.combined
|
|
129
|
-
) {
|
|
130
|
-
return {
|
|
131
|
-
required: true,
|
|
132
|
-
status: 'stale-diff',
|
|
133
|
-
evidence,
|
|
134
|
-
details: ['workspace diff changed after the last advisor evidence'],
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return null
|
|
110
|
+
return validateEvidenceFingerprint(cwd, evidence, 'advisor 证据')
|
|
138
111
|
}
|
|
139
112
|
|
|
140
113
|
function validateAdvisorContent(evidence, focus = []) {
|
|
@@ -143,7 +116,7 @@ function validateAdvisorContent(evidence, focus = []) {
|
|
|
143
116
|
required: true,
|
|
144
117
|
status: 'invalid',
|
|
145
118
|
evidence,
|
|
146
|
-
details: ['advisor
|
|
119
|
+
details: ['advisor 证据必须记录明确的 outcome、reason 和 summary'],
|
|
147
120
|
}
|
|
148
121
|
}
|
|
149
122
|
if (normalizeSources(evidence.consultedSources).length === 0) {
|
|
@@ -151,7 +124,7 @@ function validateAdvisorContent(evidence, focus = []) {
|
|
|
151
124
|
required: true,
|
|
152
125
|
status: 'invalid',
|
|
153
126
|
evidence,
|
|
154
|
-
details: ['advisor
|
|
127
|
+
details: ['advisor 证据必须记录至少一个参考来源'],
|
|
155
128
|
}
|
|
156
129
|
}
|
|
157
130
|
if (normalizeStringArray(focus).length > 0 && normalizeStringArray(evidence.focus).length === 0) {
|
|
@@ -159,7 +132,7 @@ function validateAdvisorContent(evidence, focus = []) {
|
|
|
159
132
|
required: true,
|
|
160
133
|
status: 'invalid',
|
|
161
134
|
evidence,
|
|
162
|
-
details: ['advisor
|
|
135
|
+
details: ['advisor 证据必须保留已请求的 advisor focus'],
|
|
163
136
|
}
|
|
164
137
|
}
|
|
165
138
|
if (normalizeOutcome(evidence.outcome) !== 'clean') {
|
|
@@ -167,14 +140,14 @@ function validateAdvisorContent(evidence, focus = []) {
|
|
|
167
140
|
required: true,
|
|
168
141
|
status: 'blocked',
|
|
169
142
|
evidence,
|
|
170
|
-
details: ['
|
|
143
|
+
details: ['最新 advisor 证据仍记录阻塞问题'],
|
|
171
144
|
}
|
|
172
145
|
}
|
|
173
146
|
return null
|
|
174
147
|
}
|
|
175
148
|
|
|
176
|
-
export function getAdvisorEvidenceStatus(cwd, { required = false, focus = [], now = Date.now() } = {}) {
|
|
177
|
-
const requiredEvidence = readRequiredAdvisorEvidence(cwd, required)
|
|
149
|
+
export function getAdvisorEvidenceStatus(cwd, { required = false, focus = [], now = Date.now(), ...options } = {}) {
|
|
150
|
+
const requiredEvidence = readRequiredAdvisorEvidence(cwd, required, options)
|
|
178
151
|
if ('status' in requiredEvidence) return requiredEvidence
|
|
179
152
|
if (requiredEvidence.error) return requiredEvidence.error
|
|
180
153
|
|
|
@@ -209,10 +182,10 @@ function main() {
|
|
|
209
182
|
|
|
210
183
|
const input = readStdinJson()
|
|
211
184
|
const cwd = input.cwd || process.cwd()
|
|
212
|
-
const payload = writeAdvisorEvidence(cwd, input)
|
|
185
|
+
const payload = writeAdvisorEvidence(cwd, input, { payload: input })
|
|
213
186
|
process.stdout.write(JSON.stringify({
|
|
214
187
|
suppressOutput: true,
|
|
215
|
-
path: getAdvisorEvidencePath(cwd),
|
|
188
|
+
path: getAdvisorEvidencePath(cwd, { payload: input }),
|
|
216
189
|
payload,
|
|
217
190
|
}))
|
|
218
191
|
}
|
|
@@ -26,8 +26,8 @@ export function selectCapabilities({ cwd, skillName = '', options = {} }) {
|
|
|
26
26
|
capabilities.push({
|
|
27
27
|
id: 'advisor-artifact',
|
|
28
28
|
description: advisorRequirement.styleRequired
|
|
29
|
-
? '风格 advisor:当前 UI
|
|
30
|
-
: '独立 advisor
|
|
29
|
+
? '风格 advisor:当前 UI 契约要求进入收尾前复查设计方向,并写当前会话 `artifacts/advisor.json` 记录 reason、focus、consultedSources 与结论。'
|
|
30
|
+
: '独立 advisor:当前契约要求进入收尾前写当前会话 `artifacts/advisor.json`,记录 advisor reason、focus、consultedSources 与结论。',
|
|
31
31
|
})
|
|
32
32
|
}
|
|
33
33
|
if (plan?.contract?.verifyMode === 'review-first') {
|
|
@@ -39,13 +39,13 @@ export function selectCapabilities({ cwd, skillName = '', options = {} }) {
|
|
|
39
39
|
if (plan?.contract?.ui?.required || existsSync(getProjectDesignContractPath(cwd))) {
|
|
40
40
|
capabilities.push({
|
|
41
41
|
id: 'design-contract',
|
|
42
|
-
description: `UI 契约:仅在 UI 场景按需读取当前 plan.md / prd/03-ui-design.md、${describeProjectStoreFile(cwd, 'DESIGN.md')} 与 hello-ui
|
|
42
|
+
description: `UI 契约:仅在 UI 场景按需读取当前 plan.md / prd/03-ui-design.md、${describeProjectStoreFile(cwd, 'DESIGN.md')} 与 hello-ui,并与 UI 质量基线共同生效。`,
|
|
43
43
|
})
|
|
44
44
|
}
|
|
45
45
|
if (visualRequirement.required) {
|
|
46
46
|
capabilities.push({
|
|
47
47
|
id: 'visual-evaluator',
|
|
48
|
-
description: '视觉验收:当前 UI
|
|
48
|
+
description: '视觉验收:当前 UI 契约要求进入收尾前写当前会话 `artifacts/visual.json`,记录 tooling、screensChecked、statesChecked、status 与 summary。',
|
|
49
49
|
})
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process'
|
|
2
|
+
|
|
3
|
+
import { normalizeHost } from './cli-lifecycle.mjs'
|
|
4
|
+
|
|
5
|
+
const DEFAULT_REPO_SPEC = 'github:hellowind777/helloagents'
|
|
6
|
+
|
|
7
|
+
function runCommand(command, args) {
|
|
8
|
+
const needsShell = process.platform === 'win32' && /\.cmd$/i.test(command)
|
|
9
|
+
const result = spawnSync(command, args, {
|
|
10
|
+
encoding: 'utf-8',
|
|
11
|
+
errors: 'replace',
|
|
12
|
+
shell: needsShell,
|
|
13
|
+
stdio: 'inherit',
|
|
14
|
+
windowsHide: true,
|
|
15
|
+
})
|
|
16
|
+
if (result.error) throw result.error
|
|
17
|
+
if (result.status !== 0) {
|
|
18
|
+
throw new Error(`${command} ${args.join(' ')} 执行失败,退出码 ${result.status}`)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function parseModeFlag(args) {
|
|
23
|
+
const hasGlobal = args.includes('--global')
|
|
24
|
+
const hasStandby = args.includes('--standby')
|
|
25
|
+
if (hasGlobal && hasStandby) throw new Error('不能同时使用 --global 和 --standby')
|
|
26
|
+
if (hasGlobal) return 'global'
|
|
27
|
+
if (hasStandby) return 'standby'
|
|
28
|
+
return ''
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function parseTarget(args) {
|
|
32
|
+
const wantsAll = args.includes('--all')
|
|
33
|
+
const positionals = args.filter((arg) => !arg.startsWith('--'))
|
|
34
|
+
if (!positionals.length) throw new Error('缺少分支名或 npm ref')
|
|
35
|
+
if (wantsAll && positionals.length > 1) {
|
|
36
|
+
throw new Error('`--all` 不能和指定 CLI 同时使用')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const branch = positionals[0]
|
|
40
|
+
const host = wantsAll ? 'all' : normalizeHost(positionals[1] || 'all')
|
|
41
|
+
if (!host) throw new Error(`不支持的 CLI:${positionals[1]}`)
|
|
42
|
+
if (positionals.length > 2) throw new Error(`参数过多:${positionals.join(' ')}`)
|
|
43
|
+
return { branch, host }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function parseBranchArgs(args) {
|
|
47
|
+
const unknownFlags = args.filter((arg) => (
|
|
48
|
+
arg.startsWith('--') && !['--global', '--standby', '--all'].includes(arg)
|
|
49
|
+
))
|
|
50
|
+
if (unknownFlags.length) throw new Error(`未知参数:${unknownFlags.join(', ')}`)
|
|
51
|
+
return {
|
|
52
|
+
...parseTarget(args),
|
|
53
|
+
mode: parseModeFlag(args),
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function buildPackageSpec(ref) {
|
|
58
|
+
if (/^(github:|git\+|https?:|file:)/i.test(ref)) return ref
|
|
59
|
+
return `${DEFAULT_REPO_SPEC}#${ref}`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function buildSyncArgs({ host, mode }) {
|
|
63
|
+
return [
|
|
64
|
+
'explore',
|
|
65
|
+
'-g',
|
|
66
|
+
'helloagents',
|
|
67
|
+
'--',
|
|
68
|
+
'npm',
|
|
69
|
+
'run',
|
|
70
|
+
'sync-hosts',
|
|
71
|
+
'--',
|
|
72
|
+
host === 'all' ? '--all' : host,
|
|
73
|
+
...(mode ? [`--${mode}`] : []),
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function runBranchSwitch(args, options = {}) {
|
|
78
|
+
const parsed = parseBranchArgs(args)
|
|
79
|
+
const npmCommand = options.npmCommand || process.env.HELLOAGENTS_NPM_CMD || 'npm'
|
|
80
|
+
|
|
81
|
+
const packageSpec = buildPackageSpec(parsed.branch)
|
|
82
|
+
runCommand(npmCommand, ['install', '-g', packageSpec])
|
|
83
|
+
runCommand(npmCommand, buildSyncArgs(parsed))
|
|
84
|
+
}
|
|
@@ -6,6 +6,11 @@ import {
|
|
|
6
6
|
|
|
7
7
|
export const CODEX_PLUGIN_CONFIG_HEADER = '[plugins."helloagents@local-plugins"]'
|
|
8
8
|
export const CODEX_MANAGED_TOML_COMMENT = '# helloagents-managed'
|
|
9
|
+
export const CODEX_MANAGED_MODEL_INSTRUCTIONS_PATH = '~/.codex/AGENTS.md'
|
|
10
|
+
export const CODEX_MANAGED_NOTIFY_COMMAND = process.platform === 'win32'
|
|
11
|
+
? 'helloagents-js.cmd'
|
|
12
|
+
: 'helloagents-js'
|
|
13
|
+
export const CODEX_MANAGED_NOTIFY_VALUE = `["${CODEX_MANAGED_NOTIFY_COMMAND}", "codex-notify"]`
|
|
9
14
|
|
|
10
15
|
function normalizePath(value = '') {
|
|
11
16
|
return String(value || '').replace(/\\/g, '/')
|
|
@@ -30,9 +35,8 @@ export function isManagedCodexNotify(line = '') {
|
|
|
30
35
|
const value = String(line || '').replace(/\\/g, '/')
|
|
31
36
|
return value.includes(CODEX_MANAGED_TOML_COMMENT)
|
|
32
37
|
|| (
|
|
33
|
-
value.includes('
|
|
34
|
-
&& value.includes('
|
|
35
|
-
&& /(^|[/\\])helloagents([/\\]|-|$)|[/\\]plugins[/\\]helloagents[/\\]/i.test(value)
|
|
38
|
+
value.includes('helloagents-js')
|
|
39
|
+
&& value.includes('codex-notify')
|
|
36
40
|
)
|
|
37
41
|
}
|
|
38
42
|
|
|
@@ -40,24 +44,20 @@ export function isManagedCodexBackupInstruction(line = '') {
|
|
|
40
44
|
return line.includes(CODEX_MANAGED_TOML_COMMENT)
|
|
41
45
|
}
|
|
42
46
|
|
|
43
|
-
export function isManagedCodexHooks(line = '') {
|
|
44
|
-
return /^\s*codex_hooks\s*=\s*true(?:\s+#.*)?\s*$/i.test(String(line || ''))
|
|
45
|
-
}
|
|
46
|
-
|
|
47
47
|
function formatManagedCodexModelInstructionsValue(filePath) {
|
|
48
48
|
return `"${normalizePath(filePath)}" ${CODEX_MANAGED_TOML_COMMENT}`
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
function formatManagedCodexModelInstructionsLine(filePath) {
|
|
51
|
+
function formatManagedCodexModelInstructionsLine(filePath = CODEX_MANAGED_MODEL_INSTRUCTIONS_PATH) {
|
|
52
52
|
return `model_instructions_file = ${formatManagedCodexModelInstructionsValue(filePath)}`
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
function formatManagedCodexNotifyValue(
|
|
56
|
-
return
|
|
55
|
+
function formatManagedCodexNotifyValue() {
|
|
56
|
+
return CODEX_MANAGED_NOTIFY_VALUE
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
function formatManagedCodexNotifyLine(
|
|
60
|
-
return `notify = ${formatManagedCodexNotifyValue(
|
|
59
|
+
function formatManagedCodexNotifyLine() {
|
|
60
|
+
return `notify = ${formatManagedCodexNotifyValue()} ${CODEX_MANAGED_TOML_COMMENT}`
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
function removeTopLevelLinesBeingReplaced(toml, lines) {
|
|
@@ -79,16 +79,10 @@ function upsertOrderedCodexTopLevelLines(toml, lines) {
|
|
|
79
79
|
)
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
export function
|
|
83
|
-
return upsertOrderedCodexTopLevelLines(toml, [
|
|
84
|
-
formatManagedCodexModelInstructionsLine(filePath),
|
|
85
|
-
])
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function installCodexManagedTopLevelConfig(toml, { modelInstructionsPath, notifyScriptPath }) {
|
|
82
|
+
export function installCodexManagedTopLevelConfig(toml, { modelInstructionsPath } = {}) {
|
|
89
83
|
return upsertOrderedCodexTopLevelLines(toml, [
|
|
90
84
|
formatManagedCodexModelInstructionsLine(modelInstructionsPath),
|
|
91
|
-
formatManagedCodexNotifyLine(
|
|
85
|
+
formatManagedCodexNotifyLine(),
|
|
92
86
|
])
|
|
93
87
|
}
|
|
94
88
|
|
package/scripts/cli-codex.mjs
CHANGED
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
import { ensureTimestampedBackup, readCodexBackup, removeCodexBackup } from './cli-codex-backup.mjs';
|
|
9
9
|
import {
|
|
10
10
|
CODEX_MANAGED_TOML_COMMENT,
|
|
11
|
+
CODEX_MANAGED_MODEL_INSTRUCTIONS_PATH,
|
|
11
12
|
CODEX_PLUGIN_CONFIG_HEADER,
|
|
12
|
-
isManagedCodexHooks,
|
|
13
13
|
installCodexManagedTopLevelConfig,
|
|
14
14
|
isManagedCodexBackupInstruction,
|
|
15
15
|
isManagedCodexModelInstruction,
|
|
@@ -21,11 +21,9 @@ import {
|
|
|
21
21
|
import {
|
|
22
22
|
readTopLevelTomlLine,
|
|
23
23
|
readTopLevelTomlBlock,
|
|
24
|
-
readTomlKeyInSection,
|
|
25
|
-
removeTomlKeyInSection,
|
|
26
|
-
ensureTomlKeyInSection,
|
|
27
24
|
removeTopLevelTomlLines,
|
|
28
25
|
} from './cli-toml.mjs';
|
|
26
|
+
import { buildRuntimeCarrier, readCarrierSettings } from './cli-runtime-carrier.mjs';
|
|
29
27
|
|
|
30
28
|
export const CODEX_MARKETPLACE_NAME = 'local-plugins';
|
|
31
29
|
export const CODEX_PLUGIN_NAME = 'helloagents';
|
|
@@ -108,22 +106,17 @@ function removeCodexMarketplaceEntry(marketplaceFile) {
|
|
|
108
106
|
return true;
|
|
109
107
|
}
|
|
110
108
|
|
|
111
|
-
function
|
|
112
|
-
const normalized = String(bootstrapContent || '').trim();
|
|
113
|
-
return normalized ? `${normalized}\n` : '';
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function injectCodexRuntimeCarrier(filePath, bootstrapPath) {
|
|
109
|
+
function injectCodexRuntimeCarrier(filePath, bootstrapPath, settings) {
|
|
117
110
|
const bootstrapContent = safeRead(bootstrapPath);
|
|
118
111
|
if (!bootstrapContent) return false;
|
|
119
|
-
injectMarkedContent(filePath,
|
|
112
|
+
injectMarkedContent(filePath, buildRuntimeCarrier(bootstrapContent, settings).trimEnd());
|
|
120
113
|
return true;
|
|
121
114
|
}
|
|
122
115
|
|
|
123
|
-
function writeCodexRuntimeCarrier(filePath, bootstrapPath) {
|
|
116
|
+
function writeCodexRuntimeCarrier(filePath, bootstrapPath, settings) {
|
|
124
117
|
const bootstrapContent = safeRead(bootstrapPath);
|
|
125
118
|
if (!bootstrapContent) return false;
|
|
126
|
-
safeWrite(filePath,
|
|
119
|
+
safeWrite(filePath, buildRuntimeCarrier(bootstrapContent, settings));
|
|
127
120
|
return true;
|
|
128
121
|
}
|
|
129
122
|
|
|
@@ -133,11 +126,9 @@ function cleanupCodexManagedConfig(configPath, { removePluginConfig = false } =
|
|
|
133
126
|
|
|
134
127
|
const currentModelInstructions = readTopLevelTomlLine(toml, 'model_instructions_file');
|
|
135
128
|
const currentNotify = readTopLevelTomlBlock(toml, 'notify');
|
|
136
|
-
const currentCodexHooks = readTomlKeyInSection(toml, '[features]', 'codex_hooks');
|
|
137
129
|
|
|
138
130
|
const shouldRestoreModelInstructions = isManagedCodexModelInstruction(currentModelInstructions);
|
|
139
131
|
const shouldRestoreNotify = isManagedCodexNotify(currentNotify);
|
|
140
|
-
const shouldRestoreCodexHooks = isManagedCodexHooks(currentCodexHooks);
|
|
141
132
|
|
|
142
133
|
if (removePluginConfig) {
|
|
143
134
|
toml = removeCodexPluginConfig(toml);
|
|
@@ -150,13 +141,9 @@ function cleanupCodexManagedConfig(configPath, { removePluginConfig = false } =
|
|
|
150
141
|
toml = removeTopLevelTomlLines(toml, (line) =>
|
|
151
142
|
line.startsWith('notify =') && isManagedCodexNotify(line)).text;
|
|
152
143
|
}
|
|
153
|
-
if (shouldRestoreCodexHooks) {
|
|
154
|
-
toml = removeTomlKeyInSection(toml, '[features]', 'codex_hooks');
|
|
155
|
-
}
|
|
156
144
|
|
|
157
145
|
const backupModelInstructions = readTopLevelTomlLine(backupToml, 'model_instructions_file');
|
|
158
146
|
const backupNotify = readTopLevelTomlBlock(backupToml, 'notify');
|
|
159
|
-
const backupCodexHooks = readTomlKeyInSection(backupToml, '[features]', 'codex_hooks');
|
|
160
147
|
|
|
161
148
|
toml = restoreCodexTopLevelConfig(toml, {
|
|
162
149
|
modelInstructionsLine: shouldRestoreModelInstructions && !isManagedCodexBackupInstruction(backupModelInstructions)
|
|
@@ -166,14 +153,6 @@ function cleanupCodexManagedConfig(configPath, { removePluginConfig = false } =
|
|
|
166
153
|
? backupNotify
|
|
167
154
|
: '',
|
|
168
155
|
});
|
|
169
|
-
toml = ensureTomlKeyInSection(
|
|
170
|
-
toml,
|
|
171
|
-
'[features]',
|
|
172
|
-
'codex_hooks',
|
|
173
|
-
shouldRestoreCodexHooks && !isManagedCodexHooks(backupCodexHooks)
|
|
174
|
-
? backupCodexHooks
|
|
175
|
-
: '',
|
|
176
|
-
);
|
|
177
156
|
|
|
178
157
|
return toml;
|
|
179
158
|
}
|
|
@@ -183,16 +162,16 @@ export function installCodexStandby(home, pkgRoot) {
|
|
|
183
162
|
if (!existsSync(codexDir)) return false;
|
|
184
163
|
ensureDir(codexDir);
|
|
185
164
|
|
|
165
|
+
const settings = readCarrierSettings(home);
|
|
186
166
|
const codexAgentsPath = join(codexDir, CODEX_RUNTIME_CARRIER);
|
|
187
|
-
injectCodexRuntimeCarrier(codexAgentsPath, join(pkgRoot, 'bootstrap-lite.md'));
|
|
167
|
+
injectCodexRuntimeCarrier(codexAgentsPath, join(pkgRoot, 'bootstrap-lite.md'), settings);
|
|
188
168
|
|
|
189
169
|
const configPath = join(codexDir, 'config.toml');
|
|
190
170
|
let toml = safeRead(configPath) || '';
|
|
191
171
|
ensureTimestampedBackup(configPath, CODEX_CONFIG_BASENAME);
|
|
192
172
|
|
|
193
173
|
toml = installCodexManagedTopLevelConfig(toml, {
|
|
194
|
-
modelInstructionsPath:
|
|
195
|
-
notifyScriptPath: join(pkgRoot, 'scripts', 'notify.mjs'),
|
|
174
|
+
modelInstructionsPath: CODEX_MANAGED_MODEL_INSTRUCTIONS_PATH,
|
|
196
175
|
});
|
|
197
176
|
safeWrite(configPath, toml);
|
|
198
177
|
|
|
@@ -200,6 +179,24 @@ export function installCodexStandby(home, pkgRoot) {
|
|
|
200
179
|
return true;
|
|
201
180
|
}
|
|
202
181
|
|
|
182
|
+
export function cleanupCodexGlobalResidueForStandby(home) {
|
|
183
|
+
const codexDir = join(home, '.codex');
|
|
184
|
+
const pluginRoot = join(home, 'plugins', CODEX_PLUGIN_NAME);
|
|
185
|
+
const pluginCacheRoot = join(codexDir, 'plugins', 'cache', CODEX_MARKETPLACE_NAME, CODEX_PLUGIN_NAME);
|
|
186
|
+
const marketplaceFile = join(home, '.agents', 'plugins', 'marketplace.json');
|
|
187
|
+
const configPath = join(codexDir, 'config.toml');
|
|
188
|
+
|
|
189
|
+
removeIfExists(pluginRoot);
|
|
190
|
+
removeIfExists(pluginCacheRoot);
|
|
191
|
+
removeCodexMarketplaceEntry(marketplaceFile);
|
|
192
|
+
|
|
193
|
+
const toml = removeCodexPluginConfig(safeRead(configPath) || '');
|
|
194
|
+
if (toml.trim()) safeWrite(configPath, toml);
|
|
195
|
+
else removeIfExists(configPath);
|
|
196
|
+
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
|
|
203
200
|
export function uninstallCodexStandby(home) {
|
|
204
201
|
const codexDir = join(home, '.codex');
|
|
205
202
|
let changed = false;
|
|
@@ -213,15 +210,10 @@ export function uninstallCodexStandby(home) {
|
|
|
213
210
|
else removeIfExists(configPath);
|
|
214
211
|
changed = true;
|
|
215
212
|
removeCodexBackup(configPath, CODEX_CONFIG_BASENAME);
|
|
216
|
-
removeIfExists(join(codexDir, 'hooks.json'));
|
|
217
213
|
removeLink(join(codexDir, 'helloagents'));
|
|
218
214
|
changed = true;
|
|
219
215
|
}
|
|
220
216
|
|
|
221
|
-
for (const path of [join(codexDir, 'skills', 'helloagents'), join(home, '.agents', 'skills', 'helloagents')]) {
|
|
222
|
-
changed = removeLink(path) || changed;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
217
|
return changed;
|
|
226
218
|
}
|
|
227
219
|
|
|
@@ -248,19 +240,22 @@ export function installCodexGlobal(home, pkgRoot) {
|
|
|
248
240
|
ensureDir(join(home, 'plugins'));
|
|
249
241
|
ensureDir(installedPluginRoot);
|
|
250
242
|
|
|
243
|
+
const settings = readCarrierSettings(home);
|
|
251
244
|
copyEntries(pkgRoot, pluginRoot, CODEX_RUNTIME_ENTRIES);
|
|
252
245
|
copyEntries(pkgRoot, installedPluginRoot, CODEX_RUNTIME_ENTRIES);
|
|
253
246
|
createLink(pluginRoot, join(codexDir, 'helloagents'));
|
|
254
247
|
writeCodexRuntimeCarrier(
|
|
255
248
|
join(pluginRoot, CODEX_RUNTIME_CARRIER),
|
|
256
249
|
join(pluginRoot, 'bootstrap.md'),
|
|
250
|
+
settings,
|
|
257
251
|
);
|
|
258
252
|
writeCodexRuntimeCarrier(
|
|
259
253
|
join(installedPluginRoot, CODEX_RUNTIME_CARRIER),
|
|
260
254
|
join(installedPluginRoot, 'bootstrap.md'),
|
|
255
|
+
settings,
|
|
261
256
|
);
|
|
262
257
|
const homeCarrierPath = join(codexDir, CODEX_RUNTIME_CARRIER);
|
|
263
|
-
injectCodexRuntimeCarrier(homeCarrierPath, join(pkgRoot, 'bootstrap.md'));
|
|
258
|
+
injectCodexRuntimeCarrier(homeCarrierPath, join(pkgRoot, 'bootstrap.md'), settings);
|
|
264
259
|
|
|
265
260
|
ensureDir(join(home, '.agents', 'plugins'));
|
|
266
261
|
updateCodexMarketplace(marketplaceFile);
|
|
@@ -268,8 +263,7 @@ export function installCodexGlobal(home, pkgRoot) {
|
|
|
268
263
|
let toml = safeRead(configPath) || '';
|
|
269
264
|
ensureTimestampedBackup(configPath, CODEX_CONFIG_BASENAME);
|
|
270
265
|
toml = installCodexManagedTopLevelConfig(toml, {
|
|
271
|
-
modelInstructionsPath:
|
|
272
|
-
notifyScriptPath: join(pluginRoot, 'scripts', 'notify.mjs'),
|
|
266
|
+
modelInstructionsPath: CODEX_MANAGED_MODEL_INSTRUCTIONS_PATH,
|
|
273
267
|
});
|
|
274
268
|
toml = upsertCodexPluginConfig(toml);
|
|
275
269
|
safeWrite(configPath, toml);
|
|
@@ -4,6 +4,10 @@ export function printDoctorText(runtime, report) {
|
|
|
4
4
|
`配置:\n package_version: ${report.config.packageVersion}\n install_mode: ${report.config.installMode}\n tracked_host_modes: ${JSON.stringify(report.config.trackedHostModes)}`,
|
|
5
5
|
`Config:\n package_version: ${report.config.packageVersion}\n install_mode: ${report.config.installMode}\n tracked_host_modes: ${JSON.stringify(report.config.trackedHostModes)}`,
|
|
6
6
|
))
|
|
7
|
+
console.log(runtime.msg(
|
|
8
|
+
` runtime_root: ${report.config.runtimeRoot}`,
|
|
9
|
+
` runtime_root: ${report.config.runtimeRoot}`,
|
|
10
|
+
))
|
|
7
11
|
|
|
8
12
|
for (const entry of report.hosts) {
|
|
9
13
|
console.log(`\n${entry.label}:`)
|