helloagents 3.0.2-beta.1 → 3.0.7

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 +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/README.md +147 -45
  5. package/README_CN.md +148 -46
  6. package/bootstrap-lite.md +104 -46
  7. package/bootstrap.md +143 -112
  8. package/cli.mjs +80 -427
  9. package/gemini-extension.json +1 -1
  10. package/hooks/hooks-claude.json +10 -0
  11. package/hooks/hooks.json +10 -0
  12. package/package.json +2 -12
  13. package/scripts/advisor-state.mjs +222 -0
  14. package/scripts/capability-registry.mjs +59 -0
  15. package/scripts/cli-codex-backup.mjs +59 -0
  16. package/scripts/cli-codex-config.mjs +100 -0
  17. package/scripts/cli-codex.mjs +34 -156
  18. package/scripts/cli-config.mjs +1 -0
  19. package/scripts/cli-doctor-render.mjs +28 -0
  20. package/scripts/cli-doctor.mjs +367 -0
  21. package/scripts/cli-host-detect.mjs +94 -0
  22. package/scripts/cli-lifecycle-hosts.mjs +123 -0
  23. package/scripts/cli-lifecycle.mjs +213 -0
  24. package/scripts/cli-messages.mjs +76 -52
  25. package/scripts/closeout-state.mjs +213 -0
  26. package/scripts/delivery-gate.mjs +256 -0
  27. package/scripts/guard-rules.mjs +122 -0
  28. package/scripts/guard.mjs +190 -168
  29. package/scripts/notify-context.mjs +77 -17
  30. package/scripts/notify-events.mjs +5 -1
  31. package/scripts/notify-route.mjs +111 -0
  32. package/scripts/notify-shared.mjs +0 -2
  33. package/scripts/notify-source.mjs +113 -0
  34. package/scripts/notify-ui.mjs +40 -6
  35. package/scripts/notify.mjs +120 -59
  36. package/scripts/plan-contract.mjs +210 -0
  37. package/scripts/project-storage.mjs +235 -0
  38. package/scripts/ralph-loop.mjs +9 -58
  39. package/scripts/replay-state.mjs +210 -0
  40. package/scripts/review-state.mjs +220 -0
  41. package/scripts/runtime-context.mjs +74 -0
  42. package/scripts/verify-state.mjs +226 -0
  43. package/scripts/visual-state.mjs +244 -0
  44. package/scripts/workflow-core.mjs +165 -0
  45. package/scripts/workflow-plan-files.mjs +249 -0
  46. package/scripts/workflow-recommendation.mjs +335 -0
  47. package/scripts/workflow-state.mjs +113 -0
  48. package/skills/commands/auto/SKILL.md +37 -71
  49. package/skills/commands/build/SKILL.md +67 -0
  50. package/skills/commands/clean/SKILL.md +10 -8
  51. package/skills/commands/commit/SKILL.md +8 -4
  52. package/skills/commands/help/SKILL.md +19 -11
  53. package/skills/commands/idea/SKILL.md +55 -0
  54. package/skills/commands/init/SKILL.md +6 -3
  55. package/skills/commands/loop/SKILL.md +6 -5
  56. package/skills/commands/plan/SKILL.md +116 -0
  57. package/skills/commands/prd/SKILL.md +20 -15
  58. package/skills/commands/verify/SKILL.md +32 -9
  59. package/skills/commands/wiki/SKILL.md +59 -0
  60. package/skills/hello-review/SKILL.md +9 -0
  61. package/skills/hello-subagent/SKILL.md +4 -3
  62. package/skills/hello-ui/SKILL.md +36 -8
  63. package/skills/hello-verify/SKILL.md +10 -2
  64. package/skills/helloagents/SKILL.md +24 -13
  65. package/templates/DESIGN.md +25 -4
  66. package/templates/STATE.md +3 -0
  67. package/templates/plans/contract.json +48 -0
  68. package/templates/plans/plan.md +23 -0
  69. package/templates/plans/tasks.md +3 -3
  70. package/skills/commands/design/SKILL.md +0 -108
  71. package/skills/commands/review/SKILL.md +0 -16
  72. package/templates/plans/design.md +0 -14
@@ -1,37 +1,39 @@
1
1
  import { join, dirname } from 'node:path';
2
- import { existsSync, copyFileSync, readdirSync } from 'node:fs';
2
+ import { existsSync } from 'node:fs';
3
3
  import {
4
4
  ensureDir, safeRead, safeWrite, removeIfExists,
5
5
  readJsonOrThrow, copyEntries,
6
6
  createLink, removeLink, injectMarkedContent, removeMarkedContent,
7
7
  } from './cli-utils.mjs';
8
+ import { ensureTimestampedBackup, readCodexBackup, removeCodexBackup } from './cli-codex-backup.mjs';
9
+ import {
10
+ CODEX_DEVELOPER_INSTRUCTIONS,
11
+ CODEX_MANAGED_TOML_COMMENT,
12
+ CODEX_PLUGIN_CONFIG_HEADER,
13
+ installCodexDeveloperInstructions,
14
+ isManagedCodexBackupInstruction,
15
+ isManagedCodexModelInstruction,
16
+ isManagedCodexNotify,
17
+ removeCodexPluginConfig,
18
+ uninstallCodexDeveloperInstructions,
19
+ upsertCodexPluginConfig,
20
+ } from './cli-codex-config.mjs';
8
21
  import {
9
22
  upsertTopLevelTomlKey,
10
- upsertTopLevelTomlBlock,
11
23
  readTopLevelTomlLine,
12
- readTopLevelTomlBlock,
13
24
  ensureTopLevelTomlLine,
14
- ensureTopLevelTomlBlock,
15
25
  readTomlKeyInSection,
16
26
  removeTomlKeyInSection,
17
- removeTopLevelTomlBlock,
18
27
  ensureTomlKeyInSection,
19
- stripTomlSection,
20
28
  removeTopLevelTomlLines,
21
29
  } from './cli-toml.mjs';
22
30
 
23
31
  export const CODEX_MARKETPLACE_NAME = 'local-plugins';
24
32
  export const CODEX_PLUGIN_NAME = 'helloagents';
25
33
  export const CODEX_PLUGIN_KEY = `${CODEX_PLUGIN_NAME}@${CODEX_MARKETPLACE_NAME}`;
26
- export const CODEX_PLUGIN_CONFIG_HEADER = `[plugins."${CODEX_PLUGIN_KEY}"]`;
27
- export const CODEX_MANAGED_TOML_COMMENT = '# helloagents-managed';
34
+ export { CODEX_DEVELOPER_INSTRUCTIONS, CODEX_MANAGED_TOML_COMMENT, CODEX_PLUGIN_CONFIG_HEADER };
28
35
  export const CODEX_RUNTIME_CARRIER = 'AGENTS.md';
29
- const CODEX_BACKUP_TIMESTAMP_RE = /^\d{8}-\d{6}$/;
30
36
  const CODEX_CONFIG_BASENAME = 'config.toml';
31
- const CODEX_DEVELOPER_INSTRUCTIONS_BACKUP_BASENAME = 'developer_instructions';
32
- export const CODEX_DEVELOPER_INSTRUCTIONS = `CRITICAL: These are HelloAGENTS global defaults for Codex. Use them as the baseline for main-agent behavior. Spawned sub-agents should focus on the delegated task unless they are explicitly required to follow main-agent-only workflow.
33
- If the current workspace contains a project-level AGENTS.md or other repo-specific instructions, treat those as the more specific and authoritative instructions. Use these global defaults only where they do not conflict. Standby/global behavior is determined by the active workspace instructions, not by this global default block.
34
- If work was already in progress and earlier context was compressed, first restore the active project state from the most relevant project state files or other project-local context artifacts, then continue from the actual interruption point without restarting the workflow or repeating completed steps.`;
35
37
  export const CODEX_RUNTIME_ENTRIES = [
36
38
  '.codex-plugin',
37
39
  'assets',
@@ -46,142 +48,6 @@ export const CODEX_RUNTIME_ENTRIES = [
46
48
  'templates',
47
49
  ];
48
50
 
49
- function formatBackupTimestamp(date = new Date()) {
50
- const pad = (value, size = 2) => String(value).padStart(size, '0');
51
- return `${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}-${pad(date.getHours())}${pad(date.getMinutes())}${pad(date.getSeconds())}`;
52
- }
53
-
54
- function getTimestampedBackupPath(filePath, backupBaseName) {
55
- return join(dirname(filePath), `${backupBaseName}_${formatBackupTimestamp()}.bak`);
56
- }
57
-
58
- function listTimestampedBackups(directory, backupBaseName) {
59
- if (!existsSync(directory)) return [];
60
- return readdirSync(directory)
61
- .filter((name) => name.startsWith(`${backupBaseName}_`) && name.endsWith('.bak'))
62
- .filter((name) => CODEX_BACKUP_TIMESTAMP_RE.test(name.slice(backupBaseName.length + 1, -4)))
63
- .sort();
64
- }
65
-
66
- function getLatestTimestampedBackupPath(filePath, backupBaseName) {
67
- const directory = dirname(filePath);
68
- const backups = listTimestampedBackups(directory, backupBaseName);
69
- const latest = backups.at(-1);
70
- return latest ? join(directory, latest) : '';
71
- }
72
-
73
- function readLatestTimestampedBackup(filePath, backupBaseName) {
74
- const backupPath = getLatestTimestampedBackupPath(filePath, backupBaseName);
75
- return backupPath ? safeRead(backupPath) || '' : '';
76
- }
77
-
78
- function removeLatestTimestampedBackup(filePath, backupBaseName) {
79
- const backupPath = getLatestTimestampedBackupPath(filePath, backupBaseName);
80
- if (backupPath) removeIfExists(backupPath);
81
- }
82
-
83
- function ensureTimestampedBackup(filePath, backupBaseName) {
84
- if (!existsSync(filePath)) return '';
85
- const existingBackup = getLatestTimestampedBackupPath(filePath, backupBaseName);
86
- if (existingBackup) return existingBackup;
87
- const backupPath = getTimestampedBackupPath(filePath, backupBaseName);
88
- copyFileSync(filePath, backupPath);
89
- return backupPath;
90
- }
91
-
92
- function readCodexBackup(filePath, backupBaseName) {
93
- const latest = readLatestTimestampedBackup(filePath, backupBaseName);
94
- if (latest) return latest;
95
- const legacyPath = `${filePath}.bak`;
96
- return safeRead(legacyPath) || '';
97
- }
98
-
99
- function removeCodexBackup(filePath, backupBaseName) {
100
- removeLatestTimestampedBackup(filePath, backupBaseName);
101
- removeIfExists(`${filePath}.bak`);
102
- }
103
-
104
- function isManagedCodexStandbyInstructionPath(normalized = '') {
105
- return /\/\.codex\/AGENTS\.md/i.test(normalized)
106
- || /\/\.codex\/helloagents\/bootstrap-lite\.md/i.test(normalized);
107
- }
108
-
109
- function isManagedCodexGlobalInstructionPath(normalized = '') {
110
- return /\/plugins\/helloagents\/AGENTS\.md/i.test(normalized)
111
- || /\/plugins\/helloagents\/bootstrap\.md/i.test(normalized);
112
- }
113
-
114
- function upsertCodexPluginConfig(text) {
115
- const stripped = stripTomlSection(text, CODEX_PLUGIN_CONFIG_HEADER).text.trimEnd();
116
- const block = `${CODEX_PLUGIN_CONFIG_HEADER}\nenabled = true`;
117
- return stripped ? `${stripped}\n\n${block}\n` : `${block}\n`;
118
- }
119
-
120
- function removeCodexPluginConfig(text) {
121
- return stripTomlSection(text, CODEX_PLUGIN_CONFIG_HEADER).text;
122
- }
123
-
124
- function isManagedCodexModelInstruction(line = '') {
125
- const normalized = String(line || '').replace(/\\/g, '/');
126
- return line.includes('model_instructions_file')
127
- && (
128
- line.includes(CODEX_MANAGED_TOML_COMMENT)
129
- || isManagedCodexStandbyInstructionPath(normalized)
130
- || isManagedCodexGlobalInstructionPath(normalized)
131
- );
132
- }
133
-
134
- function isManagedCodexNotify(line = '') {
135
- return line.includes('codex-notify') || (line.includes('helloagents') && line.includes('notify'));
136
- }
137
-
138
- function isManagedCodexBackupInstruction(line = '') {
139
- return line.includes(CODEX_MANAGED_TOML_COMMENT);
140
- }
141
-
142
- function formatManagedCodexDeveloperInstructions() {
143
- return `"""\n${CODEX_DEVELOPER_INSTRUCTIONS}\n"""`;
144
- }
145
-
146
- function backupUserCodexDeveloperInstructions(configPath, existingBlock) {
147
- if (!existingBlock || existingBlock.includes('HelloAGENTS')) return;
148
- const backupPath = getTimestampedBackupPath(configPath, CODEX_DEVELOPER_INSTRUCTIONS_BACKUP_BASENAME);
149
- safeWrite(backupPath, `${existingBlock}\n`);
150
- }
151
-
152
- function sanitizeCodexDeveloperInstructionsBackup(block = '') {
153
- const normalized = String(block || '').trim();
154
- if (!normalized.startsWith('developer_instructions =')) return '';
155
- if (normalized.includes(CODEX_DEVELOPER_INSTRUCTIONS)) return '';
156
- return normalized;
157
- }
158
-
159
- function readCodexDeveloperInstructionsBackup(configPath) {
160
- return sanitizeCodexDeveloperInstructionsBackup(
161
- readCodexBackup(configPath, CODEX_DEVELOPER_INSTRUCTIONS_BACKUP_BASENAME),
162
- );
163
- }
164
-
165
- function removeCodexDeveloperInstructionsBackup(configPath) {
166
- removeCodexBackup(configPath, CODEX_DEVELOPER_INSTRUCTIONS_BACKUP_BASENAME);
167
- }
168
-
169
- function installCodexDeveloperInstructions(configPath, toml) {
170
- const existing = readTopLevelTomlBlock(toml, 'developer_instructions');
171
- backupUserCodexDeveloperInstructions(configPath, existing);
172
- return upsertTopLevelTomlBlock(toml, 'developer_instructions', formatManagedCodexDeveloperInstructions());
173
- }
174
-
175
- function uninstallCodexDeveloperInstructions(configPath, toml) {
176
- const existing = readTopLevelTomlBlock(toml, 'developer_instructions');
177
- if (!existing.includes('HelloAGENTS')) return toml;
178
- let next = removeTopLevelTomlBlock(toml, 'developer_instructions');
179
- const backupDeveloperInstructions = readCodexDeveloperInstructionsBackup(configPath);
180
- next = ensureTopLevelTomlBlock(next, 'developer_instructions', backupDeveloperInstructions);
181
- removeCodexDeveloperInstructionsBackup(configPath);
182
- return next;
183
- }
184
-
185
51
  function getDefaultCodexMarketplace() {
186
52
  return {
187
53
  name: CODEX_MARKETPLACE_NAME,
@@ -251,6 +117,13 @@ function buildCodexRuntimeCarrier(bootstrapContent) {
251
117
  return normalized ? `${normalized}\n` : '';
252
118
  }
253
119
 
120
+ function injectCodexRuntimeCarrier(filePath, bootstrapPath) {
121
+ const bootstrapContent = safeRead(bootstrapPath);
122
+ if (!bootstrapContent) return false;
123
+ injectMarkedContent(filePath, buildCodexRuntimeCarrier(bootstrapContent).trimEnd());
124
+ return true;
125
+ }
126
+
254
127
  function writeCodexRuntimeCarrier(filePath, bootstrapPath) {
255
128
  const bootstrapContent = safeRead(bootstrapPath);
256
129
  if (!bootstrapContent) return false;
@@ -258,24 +131,26 @@ function writeCodexRuntimeCarrier(filePath, bootstrapPath) {
258
131
  return true;
259
132
  }
260
133
 
134
+ function stripCodexModelInstructions(toml = '') {
135
+ return removeTopLevelTomlLines(
136
+ toml,
137
+ (line) => line.startsWith('model_instructions_file ='),
138
+ ).text;
139
+ }
140
+
261
141
  export function installCodexStandby(home, pkgRoot) {
262
142
  const codexDir = join(home, '.codex');
263
143
  if (!existsSync(codexDir)) return false;
264
144
  ensureDir(codexDir);
265
145
 
266
146
  const codexAgentsPath = join(codexDir, CODEX_RUNTIME_CARRIER);
267
- const bootstrapContent = safeRead(join(pkgRoot, 'bootstrap-lite.md'));
268
- if (bootstrapContent) {
269
- injectMarkedContent(
270
- codexAgentsPath,
271
- buildCodexRuntimeCarrier(bootstrapContent).trimEnd(),
272
- );
273
- }
147
+ injectCodexRuntimeCarrier(codexAgentsPath, join(pkgRoot, 'bootstrap-lite.md'));
274
148
 
275
149
  const configPath = join(codexDir, 'config.toml');
276
150
  let toml = safeRead(configPath) || '';
277
151
  ensureTimestampedBackup(configPath, CODEX_CONFIG_BASENAME);
278
152
 
153
+ toml = stripCodexModelInstructions(toml);
279
154
  toml = upsertTopLevelTomlKey(toml, 'notify', `["node", "${normalizePath(join(pkgRoot, 'scripts', 'notify.mjs'))}", "codex-notify"]`);
280
155
  toml = installCodexDeveloperInstructions(configPath, toml);
281
156
  safeWrite(configPath, toml);
@@ -366,12 +241,14 @@ export function installCodexGlobal(home, pkgRoot) {
366
241
  join(installedPluginRoot, CODEX_RUNTIME_CARRIER),
367
242
  join(installedPluginRoot, 'bootstrap.md'),
368
243
  );
244
+ injectCodexRuntimeCarrier(join(codexDir, CODEX_RUNTIME_CARRIER), join(pkgRoot, 'bootstrap.md'));
369
245
 
370
246
  ensureDir(join(home, '.agents', 'plugins'));
371
247
  updateCodexMarketplace(marketplaceFile);
372
248
 
373
249
  let toml = safeRead(configPath) || '';
374
250
  ensureTimestampedBackup(configPath, CODEX_CONFIG_BASENAME);
251
+ toml = stripCodexModelInstructions(toml);
375
252
  toml = upsertTopLevelTomlKey(
376
253
  toml,
377
254
  'notify',
@@ -395,6 +272,7 @@ export function uninstallCodexGlobal(home) {
395
272
  removeIfExists(pluginRoot);
396
273
  removeIfExists(pluginCacheRoot);
397
274
  removeCodexMarketplaceEntry(marketplaceFile);
275
+ removeMarkedContent(join(codexDir, 'AGENTS.md'));
398
276
 
399
277
  const backupToml = readCodexBackup(configPath, CODEX_CONFIG_BASENAME);
400
278
  let toml = safeRead(configPath) || '';
@@ -7,6 +7,7 @@ export const DEFAULTS = {
7
7
  ralph_loop_enabled: true,
8
8
  guard_enabled: true,
9
9
  kb_create_mode: 1,
10
+ project_store_mode: 'local',
10
11
  commit_attribution: '',
11
12
  install_mode: 'standby',
12
13
  };
@@ -0,0 +1,28 @@
1
+ export function printDoctorText(runtime, report) {
2
+ console.log(`\nHelloAGENTS doctor\n`)
3
+ console.log(runtime.msg(
4
+ `配置:\n package_version: ${report.config.packageVersion}\n install_mode: ${report.config.installMode}\n tracked_host_modes: ${JSON.stringify(report.config.trackedHostModes)}`,
5
+ `Config:\n package_version: ${report.config.packageVersion}\n install_mode: ${report.config.installMode}\n tracked_host_modes: ${JSON.stringify(report.config.trackedHostModes)}`,
6
+ ))
7
+
8
+ for (const entry of report.hosts) {
9
+ console.log(`\n${entry.label}:`)
10
+ console.log(` status: ${entry.status}`)
11
+ console.log(` detected_mode: ${entry.detectedMode}`)
12
+ console.log(` tracked_mode: ${entry.trackedMode}`)
13
+ for (const [key, value] of Object.entries(entry.checks)) {
14
+ console.log(` ${key}: ${value ? 'ok' : 'missing'}`)
15
+ }
16
+ for (const note of entry.notes) {
17
+ console.log(` note: ${note}`)
18
+ }
19
+ for (const issue of entry.issues) {
20
+ console.log(` issue[${issue.code}]: ${issue.message}`)
21
+ }
22
+ if (entry.suggestedFix) {
23
+ console.log(` suggested_fix: ${entry.suggestedFix}`)
24
+ }
25
+ }
26
+
27
+ console.log(`\nSummary: ok=${report.summary.ok} drift=${report.summary.drift} manual-plugin=${report.summary['manual-plugin']} not-installed=${report.summary['not-installed']} issues=${report.summary.issueCount}\n`)
28
+ }