helloagents 3.0.12 → 3.0.15-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.
Files changed (72) hide show
  1. package/.claude-plugin/marketplace.json +6 -4
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/README.md +169 -30
  5. package/README_CN.md +169 -30
  6. package/bootstrap-lite.md +27 -20
  7. package/bootstrap.md +30 -23
  8. package/cli.mjs +119 -11
  9. package/gemini-extension.json +1 -1
  10. package/install.ps1 +125 -0
  11. package/install.sh +118 -0
  12. package/package.json +23 -4
  13. package/scripts/advisor-state.mjs +36 -63
  14. package/scripts/capability-registry.mjs +3 -3
  15. package/scripts/cli-branch.mjs +84 -0
  16. package/scripts/cli-codex-config.mjs +11 -20
  17. package/scripts/cli-codex.mjs +32 -38
  18. package/scripts/cli-doctor-render.mjs +4 -0
  19. package/scripts/cli-doctor.mjs +40 -30
  20. package/scripts/cli-host-detect.mjs +0 -1
  21. package/scripts/cli-hosts.mjs +16 -8
  22. package/scripts/cli-lifecycle-hosts.mjs +92 -27
  23. package/scripts/cli-lifecycle.mjs +9 -7
  24. package/scripts/cli-messages.mjs +34 -16
  25. package/scripts/cli-runtime-carrier.mjs +36 -0
  26. package/scripts/cli-runtime-root.mjs +72 -0
  27. package/scripts/cli-toml.mjs +0 -79
  28. package/scripts/cli-utils.mjs +30 -4
  29. package/scripts/closeout-state.mjs +35 -62
  30. package/scripts/delivery-gate-messages.mjs +70 -0
  31. package/scripts/delivery-gate.mjs +9 -75
  32. package/scripts/guard-rules.mjs +42 -42
  33. package/scripts/guard.mjs +44 -24
  34. package/scripts/notify-context.mjs +19 -28
  35. package/scripts/notify-gates.mjs +2 -0
  36. package/scripts/notify-route.mjs +9 -7
  37. package/scripts/notify-ui.mjs +46 -33
  38. package/scripts/notify.mjs +60 -32
  39. package/scripts/project-storage.mjs +35 -66
  40. package/scripts/ralph-loop.mjs +36 -31
  41. package/scripts/replay-state.mjs +31 -128
  42. package/scripts/review-state.mjs +34 -61
  43. package/scripts/runtime-artifacts.mjs +95 -0
  44. package/scripts/runtime-context.mjs +35 -29
  45. package/scripts/runtime-scope.mjs +313 -0
  46. package/scripts/session-capsule.mjs +202 -0
  47. package/scripts/turn-state-cli.mjs +17 -0
  48. package/scripts/turn-state.mjs +185 -66
  49. package/scripts/turn-stop-gate.mjs +24 -6
  50. package/scripts/verify-state.mjs +34 -85
  51. package/scripts/visual-state.mjs +38 -65
  52. package/scripts/workflow-core.mjs +2 -2
  53. package/scripts/workflow-plan-files.mjs +1 -1
  54. package/scripts/workflow-recommendation.mjs +17 -13
  55. package/scripts/workflow-state.mjs +5 -5
  56. package/skills/commands/build/SKILL.md +1 -1
  57. package/skills/commands/commit/SKILL.md +1 -1
  58. package/skills/commands/help/SKILL.md +3 -3
  59. package/skills/commands/loop/SKILL.md +1 -1
  60. package/skills/commands/plan/SKILL.md +8 -6
  61. package/skills/commands/prd/SKILL.md +5 -3
  62. package/skills/commands/verify/SKILL.md +5 -5
  63. package/skills/hello-debug/SKILL.md +20 -3
  64. package/skills/hello-review/SKILL.md +2 -2
  65. package/skills/hello-subagent/SKILL.md +2 -2
  66. package/skills/hello-test/SKILL.md +6 -2
  67. package/skills/hello-ui/SKILL.md +4 -4
  68. package/skills/hello-verify/SKILL.md +10 -7
  69. package/skills/helloagents/SKILL.md +12 -7
  70. package/templates/context.md +6 -0
  71. package/templates/plans/plan.md +3 -0
  72. package/templates/plans/tasks.md +8 -3
@@ -1,12 +1,19 @@
1
- import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
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 { captureWorkspaceFingerprint } from './verify-state.mjs'
7
-
8
- export const ADVISOR_EVIDENCE_FILE_NAME = '.ralph-advisor.json'
9
- const ADVISOR_EVIDENCE_MAX_AGE_MS = 30 * 60 * 1000
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 join(cwd, '.helloagents', ADVISOR_EVIDENCE_FILE_NAME)
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
- try {
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
- rmSync(getAdvisorEvidencePath(cwd), { force: true })
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
- writeFileSync(getAdvisorEvidencePath(cwd), `${JSON.stringify(payload, null, 2)}\n`, 'utf-8')
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: ['.helloagents/.ralph-advisor.json'],
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: ['missing advisor evidence required by the active contract'],
100
+ details: ['缺少当前契约要求的 advisor 证据'],
98
101
  },
99
102
  }
100
103
  }
101
104
 
102
105
  function validateAdvisorTimestamp(evidence, now) {
103
- const updatedAt = Date.parse(evidence.updatedAt || '')
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
- const currentFingerprint = captureWorkspaceFingerprint(cwd)
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 evidence must record explicit outcome, reason, and summary'],
119
+ details: ['advisor 证据必须记录明确的 outcomereason 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 evidence must record at least one consulted source'],
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 evidence must retain the requested advisor focus'],
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: ['latest advisor evidence still records blocking findings'],
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 契约要求进入收尾前复查设计方向,并复用 `.helloagents/.ralph-advisor.json` 记录 reason、focus、consultedSources 与结论。'
30
- : '独立 advisor:当前契约要求进入收尾前写 `.helloagents/.ralph-advisor.json`,记录 advisor reason、focus、consultedSources 与结论。',
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') {
@@ -45,7 +45,7 @@ export function selectCapabilities({ cwd, skillName = '', options = {} }) {
45
45
  if (visualRequirement.required) {
46
46
  capabilities.push({
47
47
  id: 'visual-evaluator',
48
- description: '视觉验收:当前 UI 契约要求进入收尾前写 `.helloagents/.ralph-visual.json`,记录 tooling、screensChecked、statesChecked、status 与 summary。',
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,8 @@ 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_VALUE = '["helloagents-js", "codex-notify"]'
9
11
 
10
12
  function normalizePath(value = '') {
11
13
  return String(value || '').replace(/\\/g, '/')
@@ -30,9 +32,8 @@ export function isManagedCodexNotify(line = '') {
30
32
  const value = String(line || '').replace(/\\/g, '/')
31
33
  return value.includes(CODEX_MANAGED_TOML_COMMENT)
32
34
  || (
33
- value.includes('codex-notify')
34
- && value.includes('/scripts/notify.mjs')
35
- && /(^|[/\\])helloagents([/\\]|-|$)|[/\\]plugins[/\\]helloagents[/\\]/i.test(value)
35
+ value.includes('helloagents-js')
36
+ && value.includes('codex-notify')
36
37
  )
37
38
  }
38
39
 
@@ -40,24 +41,20 @@ export function isManagedCodexBackupInstruction(line = '') {
40
41
  return line.includes(CODEX_MANAGED_TOML_COMMENT)
41
42
  }
42
43
 
43
- export function isManagedCodexHooks(line = '') {
44
- return /^\s*codex_hooks\s*=\s*true(?:\s+#.*)?\s*$/i.test(String(line || ''))
45
- }
46
-
47
44
  function formatManagedCodexModelInstructionsValue(filePath) {
48
45
  return `"${normalizePath(filePath)}" ${CODEX_MANAGED_TOML_COMMENT}`
49
46
  }
50
47
 
51
- function formatManagedCodexModelInstructionsLine(filePath) {
48
+ function formatManagedCodexModelInstructionsLine(filePath = CODEX_MANAGED_MODEL_INSTRUCTIONS_PATH) {
52
49
  return `model_instructions_file = ${formatManagedCodexModelInstructionsValue(filePath)}`
53
50
  }
54
51
 
55
- function formatManagedCodexNotifyValue(notifyScriptPath) {
56
- return `["node", "${normalizePath(notifyScriptPath)}", "codex-notify"]`
52
+ function formatManagedCodexNotifyValue() {
53
+ return CODEX_MANAGED_NOTIFY_VALUE
57
54
  }
58
55
 
59
- function formatManagedCodexNotifyLine(notifyScriptPath) {
60
- return `notify = ${formatManagedCodexNotifyValue(notifyScriptPath)} ${CODEX_MANAGED_TOML_COMMENT}`
56
+ function formatManagedCodexNotifyLine() {
57
+ return `notify = ${formatManagedCodexNotifyValue()} ${CODEX_MANAGED_TOML_COMMENT}`
61
58
  }
62
59
 
63
60
  function removeTopLevelLinesBeingReplaced(toml, lines) {
@@ -79,16 +76,10 @@ function upsertOrderedCodexTopLevelLines(toml, lines) {
79
76
  )
80
77
  }
81
78
 
82
- export function installCodexModelInstructions(toml, filePath) {
83
- return upsertOrderedCodexTopLevelLines(toml, [
84
- formatManagedCodexModelInstructionsLine(filePath),
85
- ])
86
- }
87
-
88
- export function installCodexManagedTopLevelConfig(toml, { modelInstructionsPath, notifyScriptPath }) {
79
+ export function installCodexManagedTopLevelConfig(toml, { modelInstructionsPath } = {}) {
89
80
  return upsertOrderedCodexTopLevelLines(toml, [
90
81
  formatManagedCodexModelInstructionsLine(modelInstructionsPath),
91
- formatManagedCodexNotifyLine(notifyScriptPath),
82
+ formatManagedCodexNotifyLine(),
92
83
  ])
93
84
  }
94
85
 
@@ -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 buildCodexRuntimeCarrier(bootstrapContent) {
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, buildCodexRuntimeCarrier(bootstrapContent).trimEnd());
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, buildCodexRuntimeCarrier(bootstrapContent));
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: codexAgentsPath,
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: homeCarrierPath,
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}:`)