helloagents 1.1.0 → 2.2.8

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 (38) hide show
  1. package/LICENSE.md +51 -0
  2. package/README.md +444 -841
  3. package/bin/cli.mjs +106 -0
  4. package/package.json +23 -38
  5. package/Claude/Skills/CN/CLAUDE.md +0 -998
  6. package/Claude/Skills/CN/skills/helloagents/analyze/SKILL.md +0 -187
  7. package/Claude/Skills/CN/skills/helloagents/design/SKILL.md +0 -261
  8. package/Claude/Skills/CN/skills/helloagents/develop/SKILL.md +0 -352
  9. package/Claude/Skills/CN/skills/helloagents/kb/SKILL.md +0 -249
  10. package/Claude/Skills/CN/skills/helloagents/templates/SKILL.md +0 -451
  11. package/Claude/Skills/EN/CLAUDE.md +0 -998
  12. package/Claude/Skills/EN/skills/helloagents/analyze/SKILL.md +0 -187
  13. package/Claude/Skills/EN/skills/helloagents/design/SKILL.md +0 -261
  14. package/Claude/Skills/EN/skills/helloagents/develop/SKILL.md +0 -352
  15. package/Claude/Skills/EN/skills/helloagents/kb/SKILL.md +0 -249
  16. package/Claude/Skills/EN/skills/helloagents/templates/SKILL.md +0 -451
  17. package/Codex/Skills/CN/AGENTS.md +0 -998
  18. package/Codex/Skills/CN/skills/helloagents/analyze/SKILL.md +0 -187
  19. package/Codex/Skills/CN/skills/helloagents/design/SKILL.md +0 -261
  20. package/Codex/Skills/CN/skills/helloagents/develop/SKILL.md +0 -352
  21. package/Codex/Skills/CN/skills/helloagents/kb/SKILL.md +0 -249
  22. package/Codex/Skills/CN/skills/helloagents/templates/SKILL.md +0 -451
  23. package/Codex/Skills/EN/AGENTS.md +0 -998
  24. package/Codex/Skills/EN/skills/helloagents/analyze/SKILL.md +0 -187
  25. package/Codex/Skills/EN/skills/helloagents/design/SKILL.md +0 -261
  26. package/Codex/Skills/EN/skills/helloagents/develop/SKILL.md +0 -352
  27. package/Codex/Skills/EN/skills/helloagents/kb/SKILL.md +0 -249
  28. package/Codex/Skills/EN/skills/helloagents/templates/SKILL.md +0 -451
  29. package/bin/cli.js +0 -85
  30. package/lib/args.js +0 -106
  31. package/lib/backup.js +0 -81
  32. package/lib/conflict.js +0 -118
  33. package/lib/copy.js +0 -125
  34. package/lib/defaults.js +0 -47
  35. package/lib/index.js +0 -297
  36. package/lib/output.js +0 -220
  37. package/lib/prompts.js +0 -173
  38. package/lib/utils.js +0 -225
package/lib/conflict.js DELETED
@@ -1,118 +0,0 @@
1
- /**
2
- * Conflict - Conflict detection and resolution
3
- */
4
-
5
- 'use strict';
6
-
7
- const path = require('path');
8
- const fsPromises = require('fs').promises;
9
- const { fileExists, dirExists, readFileContent, getTimestamp } = require('./utils');
10
-
11
- /**
12
- * Check if config file exists
13
- * @param {string} targetPath - Path to config file
14
- * @param {Object} deps - Dependencies
15
- * @returns {Promise<boolean>}
16
- */
17
- async function checkConfigFile(targetPath, deps = {}) {
18
- return fileExists(targetPath, deps);
19
- }
20
-
21
- /**
22
- * Check if skills directory exists
23
- * @param {string} targetPath - Path to skills directory
24
- * @param {Object} deps - Dependencies
25
- * @returns {Promise<boolean>}
26
- */
27
- async function checkSkillsDir(targetPath, deps = {}) {
28
- return dirExists(targetPath, deps);
29
- }
30
-
31
- /**
32
- * Check if .new file exists and compare content
33
- * @param {string} newFilePath - Path to .new file
34
- * @param {string} sourceContent - Content to compare
35
- * @param {Object} deps - Dependencies
36
- * @returns {Promise<Object>} { exists, sameContent }
37
- */
38
- async function checkNewFile(newFilePath, sourceContent, deps = {}) {
39
- const exists = await fileExists(newFilePath, deps);
40
-
41
- if (!exists) {
42
- return { exists: false, sameContent: false };
43
- }
44
-
45
- try {
46
- const existingContent = await readFileContent(newFilePath, deps);
47
- const sameContent = existingContent === sourceContent;
48
- return { exists: true, sameContent };
49
- } catch (err) {
50
- return { exists: true, sameContent: false };
51
- }
52
- }
53
-
54
- /**
55
- * Backup existing .new file if content is different
56
- * @param {string} newFilePath - Path to .new file
57
- * @param {Object} deps - Dependencies
58
- * @returns {Promise<string|null>} Backup path or null if no backup needed
59
- */
60
- async function backupNewFileIfDifferent(newFilePath, deps = {}) {
61
- const fsModule = deps.fs || fsPromises;
62
- const timestamp = getTimestamp(deps);
63
-
64
- const dir = path.dirname(newFilePath);
65
- const name = path.basename(newFilePath);
66
- const backupPath = path.join(dir, `${name}.backup-${timestamp}`);
67
-
68
- await fsModule.rename(newFilePath, backupPath);
69
-
70
- return backupPath;
71
- }
72
-
73
- /**
74
- * Determine conflict resolution action
75
- * @param {Object} options - CLI options
76
- * @param {string} conflictType - 'config' or 'skills'
77
- * @param {string} userChoice - User's choice from prompt
78
- * @returns {string} Action to take
79
- */
80
- function resolveConflict(options, conflictType, userChoice = null) {
81
- // If user made a choice, use it
82
- if (userChoice) {
83
- return userChoice;
84
- }
85
-
86
- // -y mode: use defaults
87
- if (options.yes) {
88
- if (conflictType === 'config') {
89
- return options.overwrite ? 'overwrite' : 'new';
90
- }
91
- if (conflictType === 'skills') {
92
- return options.noBackup ? 'overwrite' : 'backup';
93
- }
94
- }
95
-
96
- // --overwrite flag for config
97
- if (conflictType === 'config' && options.overwrite) {
98
- return options.noBackup ? 'overwrite' : 'backup';
99
- }
100
-
101
- // Default actions
102
- if (conflictType === 'config') {
103
- return 'new';
104
- }
105
- if (conflictType === 'skills') {
106
- return options.noBackup ? 'overwrite' : 'backup';
107
- }
108
-
109
- return 'skip';
110
- }
111
-
112
- module.exports = {
113
- checkConfigFile,
114
- checkSkillsDir,
115
- checkNewFile,
116
- backupNewFileIfDifferent,
117
- resolveConflict,
118
- };
package/lib/copy.js DELETED
@@ -1,125 +0,0 @@
1
- /**
2
- * Copy - File copying with near-atomic writes
3
- */
4
-
5
- 'use strict';
6
-
7
- const path = require('path');
8
- const fsPromises = require('fs').promises;
9
- const { randomSuffix, ensureDir, readFileContent } = require('./utils');
10
-
11
- /**
12
- * Copy a file with near-atomic write
13
- * @param {string} src - Source file path
14
- * @param {string} dest - Destination file path
15
- * @param {Object} deps - Dependencies
16
- * @returns {Promise<void>}
17
- */
18
- async function copyFile(src, dest, deps = {}) {
19
- const fsModule = deps.fs || fsPromises;
20
- const suffix = randomSuffix(deps);
21
-
22
- // Ensure destination directory exists
23
- await ensureDir(path.dirname(dest), deps);
24
-
25
- // Write to temporary file first
26
- const tempPath = `${dest}.tmp-${suffix}`;
27
-
28
- try {
29
- // Copy to temp file
30
- await fsModule.copyFile(src, tempPath);
31
-
32
- // Verify copy by comparing sizes
33
- const srcStat = await fsModule.stat(src);
34
- const tempStat = await fsModule.stat(tempPath);
35
-
36
- if (srcStat.size !== tempStat.size) {
37
- throw new Error('File copy verification failed: size mismatch');
38
- }
39
-
40
- // Rename temp to final destination (atomic on most filesystems)
41
- await fsModule.rename(tempPath, dest);
42
-
43
- } catch (err) {
44
- // Clean up temp file on failure
45
- try {
46
- await fsModule.unlink(tempPath);
47
- } catch (cleanupErr) {
48
- // Ignore cleanup errors
49
- }
50
- throw err;
51
- }
52
- }
53
-
54
- /**
55
- * Copy a directory recursively
56
- * @param {string} src - Source directory path
57
- * @param {string} dest - Destination directory path
58
- * @param {Object} deps - Dependencies
59
- * @returns {Promise<void>}
60
- */
61
- async function copyDir(src, dest, deps = {}) {
62
- const fsModule = deps.fs || fsPromises;
63
-
64
- await ensureDir(dest, deps);
65
-
66
- const entries = await fsModule.readdir(src, { withFileTypes: true });
67
-
68
- for (const entry of entries) {
69
- const srcPath = path.join(src, entry.name);
70
- const destPath = path.join(dest, entry.name);
71
-
72
- if (entry.isDirectory()) {
73
- await copyDir(srcPath, destPath, deps);
74
- } else {
75
- await copyFile(srcPath, destPath, deps);
76
- }
77
- }
78
- }
79
-
80
- /**
81
- * Write content to .new file
82
- * @param {string} src - Source file path
83
- * @param {string} dest - Destination .new file path
84
- * @param {Object} deps - Dependencies
85
- * @returns {Promise<void>}
86
- */
87
- async function writeNewFile(src, dest, deps = {}) {
88
- await copyFile(src, dest, deps);
89
- }
90
-
91
- /**
92
- * Get list of files in a directory recursively
93
- * @param {string} dir - Directory path
94
- * @param {Object} deps - Dependencies
95
- * @returns {Promise<string[]>} List of relative file paths
96
- */
97
- async function listFiles(dir, deps = {}) {
98
- const fsModule = deps.fs || fsPromises;
99
- const files = [];
100
-
101
- async function walk(currentDir, relativePath = '') {
102
- const entries = await fsModule.readdir(currentDir, { withFileTypes: true });
103
-
104
- for (const entry of entries) {
105
- const fullPath = path.join(currentDir, entry.name);
106
- const relPath = path.join(relativePath, entry.name);
107
-
108
- if (entry.isDirectory()) {
109
- await walk(fullPath, relPath);
110
- } else {
111
- files.push(relPath);
112
- }
113
- }
114
- }
115
-
116
- await walk(dir);
117
- return files;
118
- }
119
-
120
- module.exports = {
121
- copyFile,
122
- copyDir,
123
- writeNewFile,
124
- listFiles,
125
- };
package/lib/defaults.js DELETED
@@ -1,47 +0,0 @@
1
- /**
2
- * Defaults - Default value detection for platform and language
3
- */
4
-
5
- 'use strict';
6
-
7
- const path = require('path');
8
- const { dirExists, getHomeDir } = require('./utils');
9
-
10
- /**
11
- * Detect default platform based on existing directories
12
- * @param {Object} deps - Dependencies
13
- * @returns {Promise<Object>} { platform, reason }
14
- */
15
- async function detectDefaultPlatform(deps = {}) {
16
- const homeDir = getHomeDir(deps);
17
-
18
- const claudeDir = path.join(homeDir, '.claude');
19
- const codexDir = path.join(homeDir, '.codex');
20
-
21
- const claudeExists = await dirExists(claudeDir, deps);
22
- const codexExists = await dirExists(codexDir, deps);
23
-
24
- if (codexExists && !claudeExists) {
25
- return { platform: 'codex', reason: '检测到 ~/.codex' };
26
- }
27
-
28
- if (claudeExists && !codexExists) {
29
- return { platform: 'claude', reason: '检测到 ~/.claude' };
30
- }
31
-
32
- // Both exist or neither exists -> default to claude
33
- return { platform: 'claude', reason: '默认选择' };
34
- }
35
-
36
- /**
37
- * Get default language
38
- * @returns {Object} { lang, reason }
39
- */
40
- function getDefaultLang() {
41
- return { lang: 'cn', reason: '默认选择' };
42
- }
43
-
44
- module.exports = {
45
- detectDefaultPlatform,
46
- getDefaultLang,
47
- };
package/lib/index.js DELETED
@@ -1,297 +0,0 @@
1
- /**
2
- * Index - Main flow implementation
3
- */
4
-
5
- 'use strict';
6
-
7
- const path = require('path');
8
- const fsPromises = require('fs').promises;
9
-
10
- const { getHomeDir, getSourcePaths, getTargetPaths, fileExists, dirExists, readFileContent, rmrf, ensureDir } = require('./utils');
11
- const { detectDefaultPlatform, getDefaultLang } = require('./defaults');
12
- const { createReadline, askPlatform, askLanguage, askConfigConflict, askSkillsConflict } = require('./prompts');
13
- const { backupFile, backupDir } = require('./backup');
14
- const { checkConfigFile, checkSkillsDir, checkNewFile, backupNewFileIfDifferent, resolveConflict } = require('./conflict');
15
- const { copyFile, copyDir, writeNewFile, listFiles } = require('./copy');
16
- const { createOutput, printWelcome, printSummary, printDryRunSummary, printRecoveryInstructions, info, success, warn, error, log, newline, header, divider, operation } = require('./output');
17
-
18
- /**
19
- * Main run function
20
- * @param {Object} options - CLI options
21
- * @param {Object} deps - Dependencies for testing
22
- * @returns {Promise<void>}
23
- */
24
- async function run(options, deps = {}) {
25
- const fsModule = deps.fs || fsPromises;
26
- const homeDir = getHomeDir(deps);
27
- const output = deps.output || createOutput(deps);
28
-
29
- // Track stats and backups
30
- const stats = { write: 0, backup: 0, newFile: 0, skip: 0 };
31
- const backups = [];
32
-
33
- // Readline interface (only created if needed)
34
- let rl = null;
35
-
36
- try {
37
- // Print welcome message
38
- output.printWelcome();
39
-
40
- // Determine platform
41
- let platform = options.platform;
42
- let platformReason = '';
43
-
44
- if (!platform) {
45
- if (options.yes) {
46
- // Auto-detect in -y mode
47
- const detected = await detectDefaultPlatform(deps);
48
- platform = detected.platform;
49
- platformReason = detected.reason;
50
- output.info(`[默认] ${platformReason},使用 ${platform === 'claude' ? 'Claude Code' : 'Codex'}`);
51
- } else {
52
- // Interactive mode
53
- rl = createReadline();
54
- const detected = await detectDefaultPlatform(deps);
55
- platform = await askPlatform(rl, detected.platform, detected.reason);
56
- }
57
- } else {
58
- output.info(`使用指定平台: ${platform === 'claude' ? 'Claude Code' : 'Codex'}`);
59
- }
60
-
61
- // Determine language
62
- let lang = options.lang;
63
-
64
- if (!lang) {
65
- if (options.yes) {
66
- // Use default in -y mode
67
- const defaultLang = getDefaultLang();
68
- lang = defaultLang.lang;
69
- output.info(`[默认] 使用语言: ${lang === 'cn' ? '中文' : 'English'}`);
70
- } else {
71
- // Interactive mode
72
- if (!rl) {
73
- rl = createReadline();
74
- }
75
- const defaultLang = getDefaultLang();
76
- lang = await askLanguage(rl, defaultLang.lang);
77
- }
78
- } else {
79
- output.info(`使用指定语言: ${lang === 'cn' ? '中文' : 'English'}`);
80
- }
81
-
82
- // Get source and target paths
83
- const sourcePaths = getSourcePaths(platform, lang);
84
- const targetPaths = getTargetPaths(platform, deps);
85
-
86
- // Validate source files exist
87
- const sourceConfigExists = await fileExists(sourcePaths.configFile, deps);
88
- const sourceSkillsExists = await dirExists(sourcePaths.skillsDir, deps);
89
-
90
- if (!sourceConfigExists) {
91
- throw new Error(`源配置文件不存在: ${sourcePaths.configFile}`);
92
- }
93
-
94
- if (!sourceSkillsExists) {
95
- throw new Error(`源技能目录不存在: ${sourcePaths.skillsDir}`);
96
- }
97
-
98
- // Ensure target directory exists
99
- await ensureDir(targetPaths.targetDir, deps);
100
-
101
- // Dry run mode
102
- if (options.dryRun) {
103
- output.header('[Dry Run] 以下操作不会实际执行:');
104
-
105
- // Check config file
106
- if (!options.skillsOnly) {
107
- const configExists = await checkConfigFile(targetPaths.configFile, deps);
108
- const configFileName = platform === 'claude' ? 'CLAUDE.md' : 'AGENTS.md';
109
-
110
- if (configExists) {
111
- if (options.overwrite) {
112
- output.operation(sourcePaths.configFile, targetPaths.configFile, '备份后覆盖');
113
- stats.backup++;
114
- stats.write++;
115
- } else {
116
- output.operation(sourcePaths.configFile, targetPaths.newFile, `生成 .helloagents.new(${configFileName} 已存在)`);
117
- stats.newFile++;
118
- }
119
- } else {
120
- output.operation(sourcePaths.configFile, targetPaths.configFile, '写入');
121
- stats.write++;
122
- }
123
- }
124
-
125
- // Check skills directory
126
- const skillsExists = await checkSkillsDir(targetPaths.skillsDir, deps);
127
-
128
- if (skillsExists) {
129
- if (options.noBackup) {
130
- output.operation(sourcePaths.skillsDir, targetPaths.skillsDir, '直接覆盖');
131
- } else {
132
- output.operation(sourcePaths.skillsDir, targetPaths.skillsDir, '备份后覆盖');
133
- stats.backup++;
134
- }
135
- stats.write++;
136
- } else {
137
- output.operation(sourcePaths.skillsDir, targetPaths.skillsDir, '写入');
138
- stats.write++;
139
- }
140
-
141
- output.printDryRunSummary(stats);
142
- return;
143
- }
144
-
145
- // Process config file (unless --skills-only)
146
- if (!options.skillsOnly) {
147
- const configExists = await checkConfigFile(targetPaths.configFile, deps);
148
- const configFileName = platform === 'claude' ? 'CLAUDE.md' : 'AGENTS.md';
149
-
150
- if (configExists) {
151
- // Determine action
152
- let action;
153
- if (options.yes) {
154
- action = resolveConflict(options, 'config');
155
- } else {
156
- if (!rl) {
157
- rl = createReadline();
158
- }
159
- action = await askConfigConflict(rl, configFileName);
160
- }
161
-
162
- switch (action) {
163
- case 'new': {
164
- // Check if .new file already exists
165
- const sourceContent = await readFileContent(sourcePaths.configFile, deps);
166
- const newFileStatus = await checkNewFile(targetPaths.newFile, sourceContent, deps);
167
-
168
- if (newFileStatus.exists) {
169
- if (newFileStatus.sameContent) {
170
- output.info(`.helloagents.new 已是最新,跳过`);
171
- stats.skip++;
172
- } else {
173
- // Backup existing .new file
174
- const newBackupPath = await backupNewFileIfDifferent(targetPaths.newFile, deps);
175
- backups.push(newBackupPath);
176
- output.info(`已备份旧的 .helloagents.new 至 ${path.basename(newBackupPath)}`);
177
- stats.backup++;
178
-
179
- // Write new .new file
180
- await writeNewFile(sourcePaths.configFile, targetPaths.newFile, deps);
181
- output.success(`已生成 .helloagents.new`);
182
- stats.newFile++;
183
- }
184
- } else {
185
- await writeNewFile(sourcePaths.configFile, targetPaths.newFile, deps);
186
- output.success(`已生成 .helloagents.new`);
187
- stats.newFile++;
188
- }
189
- break;
190
- }
191
-
192
- case 'backup': {
193
- const backupPath = await backupFile(targetPaths.configFile, deps);
194
- backups.push(backupPath);
195
- output.info(`已备份 ${configFileName} 至 ${path.basename(backupPath)}`);
196
- stats.backup++;
197
-
198
- await copyFile(sourcePaths.configFile, targetPaths.configFile, deps);
199
- output.success(`已覆盖 ${configFileName}`);
200
- stats.write++;
201
- break;
202
- }
203
-
204
- case 'overwrite': {
205
- await copyFile(sourcePaths.configFile, targetPaths.configFile, deps);
206
- output.success(`已覆盖 ${configFileName}`);
207
- stats.write++;
208
- break;
209
- }
210
-
211
- case 'skip': {
212
- output.info(`跳过 ${configFileName}`);
213
- stats.skip++;
214
- break;
215
- }
216
- }
217
- } else {
218
- // No conflict, just copy
219
- await copyFile(sourcePaths.configFile, targetPaths.configFile, deps);
220
- output.success(`已写入 ${configFileName}`);
221
- stats.write++;
222
- }
223
- }
224
-
225
- // Process skills directory
226
- const skillsExists = await checkSkillsDir(targetPaths.skillsDir, deps);
227
-
228
- if (skillsExists) {
229
- // Determine action
230
- let action;
231
- if (options.yes) {
232
- action = resolveConflict(options, 'skills');
233
- } else {
234
- if (!rl) {
235
- rl = createReadline();
236
- }
237
- action = await askSkillsConflict(rl);
238
- }
239
-
240
- switch (action) {
241
- case 'backup': {
242
- const backupPath = await backupDir(targetPaths.skillsDir, deps);
243
- backups.push(backupPath);
244
- output.info(`已备份 skills/helloagents 至 ${path.basename(backupPath)}`);
245
- stats.backup++;
246
-
247
- // Remove old directory and copy new
248
- await rmrf(targetPaths.skillsDir, deps);
249
- await copyDir(sourcePaths.skillsDir, targetPaths.skillsDir, deps);
250
- output.success(`已更新 skills/helloagents`);
251
- stats.write++;
252
- break;
253
- }
254
-
255
- case 'overwrite': {
256
- await rmrf(targetPaths.skillsDir, deps);
257
- await copyDir(sourcePaths.skillsDir, targetPaths.skillsDir, deps);
258
- output.success(`已覆盖 skills/helloagents`);
259
- stats.write++;
260
- break;
261
- }
262
-
263
- case 'skip': {
264
- output.info(`跳过 skills/helloagents`);
265
- stats.skip++;
266
- break;
267
- }
268
- }
269
- } else {
270
- // No conflict, ensure parent directory exists and copy
271
- await ensureDir(path.dirname(targetPaths.skillsDir), deps);
272
- await copyDir(sourcePaths.skillsDir, targetPaths.skillsDir, deps);
273
- output.success(`已写入 skills/helloagents`);
274
- stats.write++;
275
- }
276
-
277
- // Print summary
278
- output.printSummary(stats, backups);
279
-
280
- } catch (err) {
281
- // Print recovery instructions if we have backups
282
- if (backups.length > 0) {
283
- output.printRecoveryInstructions(err.message, backups, options.platform || 'claude');
284
- }
285
- throw err;
286
-
287
- } finally {
288
- // Close readline if opened
289
- if (rl) {
290
- rl.close();
291
- }
292
- }
293
- }
294
-
295
- module.exports = {
296
- run,
297
- };