helloagents 1.0.0

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 (36) hide show
  1. package/Claude/Skills/CN/CLAUDE.md +998 -0
  2. package/Claude/Skills/CN/skills/helloagents/analyze/SKILL.md +187 -0
  3. package/Claude/Skills/CN/skills/helloagents/design/SKILL.md +261 -0
  4. package/Claude/Skills/CN/skills/helloagents/develop/SKILL.md +352 -0
  5. package/Claude/Skills/CN/skills/helloagents/kb/SKILL.md +249 -0
  6. package/Claude/Skills/CN/skills/helloagents/templates/SKILL.md +451 -0
  7. package/Claude/Skills/EN/CLAUDE.md +998 -0
  8. package/Claude/Skills/EN/skills/helloagents/analyze/SKILL.md +187 -0
  9. package/Claude/Skills/EN/skills/helloagents/design/SKILL.md +261 -0
  10. package/Claude/Skills/EN/skills/helloagents/develop/SKILL.md +352 -0
  11. package/Claude/Skills/EN/skills/helloagents/kb/SKILL.md +249 -0
  12. package/Claude/Skills/EN/skills/helloagents/templates/SKILL.md +451 -0
  13. package/Codex/Skills/CN/AGENTS.md +998 -0
  14. package/Codex/Skills/CN/skills/helloagents/analyze/SKILL.md +187 -0
  15. package/Codex/Skills/CN/skills/helloagents/design/SKILL.md +261 -0
  16. package/Codex/Skills/CN/skills/helloagents/develop/SKILL.md +352 -0
  17. package/Codex/Skills/CN/skills/helloagents/kb/SKILL.md +249 -0
  18. package/Codex/Skills/CN/skills/helloagents/templates/SKILL.md +451 -0
  19. package/Codex/Skills/EN/AGENTS.md +998 -0
  20. package/Codex/Skills/EN/skills/helloagents/analyze/SKILL.md +187 -0
  21. package/Codex/Skills/EN/skills/helloagents/design/SKILL.md +261 -0
  22. package/Codex/Skills/EN/skills/helloagents/develop/SKILL.md +352 -0
  23. package/Codex/Skills/EN/skills/helloagents/kb/SKILL.md +249 -0
  24. package/Codex/Skills/EN/skills/helloagents/templates/SKILL.md +451 -0
  25. package/README.md +840 -0
  26. package/bin/cli.js +85 -0
  27. package/lib/args.js +106 -0
  28. package/lib/backup.js +81 -0
  29. package/lib/conflict.js +118 -0
  30. package/lib/copy.js +125 -0
  31. package/lib/defaults.js +47 -0
  32. package/lib/index.js +297 -0
  33. package/lib/output.js +220 -0
  34. package/lib/prompts.js +173 -0
  35. package/lib/utils.js +225 -0
  36. package/package.json +38 -0
package/bin/cli.js ADDED
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * HelloAGENTS CLI - Entry Point
5
+ *
6
+ * CLI tool to configure HelloAGENTS for Claude Code and Codex
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { parseArgs } = require('../lib/args');
12
+ const { run } = require('../lib/index');
13
+ const { error, info } = require('../lib/output');
14
+ const pkg = require('../package.json');
15
+
16
+ const HELP_TEXT = `
17
+ HelloAGENTS - AI编程模块化技能系统配置工具
18
+
19
+ Usage: npx helloagents [options]
20
+
21
+ Options:
22
+ --platform <claude|codex> 目标平台(跳过交互)
23
+ --lang <cn|en> 语言版本(跳过交互)
24
+ --yes, -y 全部使用默认选项(非交互)
25
+ --dry-run 仅显示将执行的操作,不实际写入
26
+ --skills-only 仅更新 skills/helloagents,跳过顶层配置文件
27
+ --overwrite 强制覆盖顶层配置文件(不生成 .new)
28
+ --no-backup 跳过备份步骤
29
+ --no-color 禁用 ANSI 颜色输出
30
+ --help, -h 显示帮助
31
+ --version, -v 显示版本
32
+
33
+ Examples:
34
+ npx helloagents # 交互式安装
35
+ npx helloagents -y # 使用默认选项安装
36
+ npx helloagents --platform claude --lang cn # 指定平台和语言
37
+ npx helloagents --dry-run # 预览将执行的操作
38
+ npx helloagents --skills-only # 仅更新技能模块
39
+
40
+ Documentation: https://github.com/hellowind777/helloagents
41
+ `;
42
+
43
+ async function main() {
44
+ try {
45
+ // Parse command line arguments
46
+ const options = parseArgs(process.argv.slice(2));
47
+
48
+ // Handle --help
49
+ if (options.help) {
50
+ console.log(HELP_TEXT);
51
+ process.exit(0);
52
+ }
53
+
54
+ // Handle --version
55
+ if (options.version) {
56
+ console.log(`helloagents v${pkg.version}`);
57
+ process.exit(0);
58
+ }
59
+
60
+ // Run main logic
61
+ await run(options);
62
+
63
+ } catch (err) {
64
+ // Handle known error types
65
+ if (err.code === 'MUTEX_PARAMS') {
66
+ error(err.message);
67
+ process.exit(1);
68
+ }
69
+
70
+ if (err.code === 'INVALID_PARAM') {
71
+ error(err.message);
72
+ process.exit(1);
73
+ }
74
+
75
+ // Handle unexpected errors
76
+ error(`发生错误: ${err.message}`);
77
+ if (process.env.DEBUG) {
78
+ console.error(err.stack);
79
+ }
80
+ process.exit(1);
81
+ }
82
+ }
83
+
84
+ // Run CLI
85
+ main();
package/lib/args.js ADDED
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Args - Command line argument parsing
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ /**
8
+ * Parse command line arguments
9
+ * @param {string[]} argv - Command line arguments (without node and script)
10
+ * @returns {Object} Parsed options
11
+ * @throws {Error} On invalid arguments
12
+ */
13
+ function parseArgs(argv) {
14
+ const options = {
15
+ platform: null,
16
+ lang: null,
17
+ yes: false,
18
+ dryRun: false,
19
+ skillsOnly: false,
20
+ overwrite: false,
21
+ noBackup: false,
22
+ noColor: false,
23
+ help: false,
24
+ version: false,
25
+ };
26
+
27
+ for (let i = 0; i < argv.length; i++) {
28
+ const arg = argv[i];
29
+
30
+ switch (arg) {
31
+ case '--platform':
32
+ options.platform = argv[++i];
33
+ if (!options.platform || !['claude', 'codex'].includes(options.platform)) {
34
+ const err = new Error(`无效的平台参数: ${options.platform}。有效值: claude, codex`);
35
+ err.code = 'INVALID_PARAM';
36
+ throw err;
37
+ }
38
+ break;
39
+
40
+ case '--lang':
41
+ options.lang = argv[++i];
42
+ if (!options.lang || !['cn', 'en'].includes(options.lang)) {
43
+ const err = new Error(`无效的语言参数: ${options.lang}。有效值: cn, en`);
44
+ err.code = 'INVALID_PARAM';
45
+ throw err;
46
+ }
47
+ break;
48
+
49
+ case '--yes':
50
+ case '-y':
51
+ options.yes = true;
52
+ break;
53
+
54
+ case '--dry-run':
55
+ options.dryRun = true;
56
+ break;
57
+
58
+ case '--skills-only':
59
+ options.skillsOnly = true;
60
+ break;
61
+
62
+ case '--overwrite':
63
+ options.overwrite = true;
64
+ break;
65
+
66
+ case '--no-backup':
67
+ options.noBackup = true;
68
+ break;
69
+
70
+ case '--no-color':
71
+ options.noColor = true;
72
+ break;
73
+
74
+ case '--help':
75
+ case '-h':
76
+ options.help = true;
77
+ break;
78
+
79
+ case '--version':
80
+ case '-v':
81
+ options.version = true;
82
+ break;
83
+
84
+ default:
85
+ if (arg.startsWith('-')) {
86
+ const err = new Error(`未知参数: ${arg}`);
87
+ err.code = 'INVALID_PARAM';
88
+ throw err;
89
+ }
90
+ break;
91
+ }
92
+ }
93
+
94
+ // Check for mutually exclusive options
95
+ if (options.skillsOnly && options.overwrite) {
96
+ const err = new Error('--skills-only 与 --overwrite 不能同时使用');
97
+ err.code = 'MUTEX_PARAMS';
98
+ throw err;
99
+ }
100
+
101
+ return options;
102
+ }
103
+
104
+ module.exports = {
105
+ parseArgs,
106
+ };
package/lib/backup.js ADDED
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Backup - File and directory backup functionality
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const path = require('path');
8
+ const fsPromises = require('fs').promises;
9
+ const { getTimestamp, ensureDir } = require('./utils');
10
+
11
+ /**
12
+ * Backup a single file
13
+ * @param {string} filePath - Path to file to backup
14
+ * @param {Object} deps - Dependencies
15
+ * @returns {Promise<string>} Backup path
16
+ */
17
+ async function backupFile(filePath, deps = {}) {
18
+ const fsModule = deps.fs || fsPromises;
19
+ const timestamp = getTimestamp(deps);
20
+
21
+ const dir = path.dirname(filePath);
22
+ const ext = path.extname(filePath);
23
+ const base = path.basename(filePath, ext);
24
+ const backupPath = path.join(dir, `${base}${ext}.backup-${timestamp}`);
25
+
26
+ await fsModule.copyFile(filePath, backupPath);
27
+
28
+ return backupPath;
29
+ }
30
+
31
+ /**
32
+ * Backup a directory
33
+ * @param {string} dirPath - Path to directory to backup
34
+ * @param {Object} deps - Dependencies
35
+ * @returns {Promise<string>} Backup path
36
+ */
37
+ async function backupDir(dirPath, deps = {}) {
38
+ const fsModule = deps.fs || fsPromises;
39
+ const timestamp = getTimestamp(deps);
40
+
41
+ const parent = path.dirname(dirPath);
42
+ const name = path.basename(dirPath);
43
+ const backupPath = path.join(parent, `${name}.backup-${timestamp}`);
44
+
45
+ // Recursively copy directory
46
+ await copyDirRecursive(dirPath, backupPath, deps);
47
+
48
+ return backupPath;
49
+ }
50
+
51
+ /**
52
+ * Recursively copy a directory
53
+ * @param {string} src - Source directory
54
+ * @param {string} dest - Destination directory
55
+ * @param {Object} deps - Dependencies
56
+ * @returns {Promise<void>}
57
+ */
58
+ async function copyDirRecursive(src, dest, deps = {}) {
59
+ const fsModule = deps.fs || fsPromises;
60
+
61
+ await ensureDir(dest, deps);
62
+
63
+ const entries = await fsModule.readdir(src, { withFileTypes: true });
64
+
65
+ for (const entry of entries) {
66
+ const srcPath = path.join(src, entry.name);
67
+ const destPath = path.join(dest, entry.name);
68
+
69
+ if (entry.isDirectory()) {
70
+ await copyDirRecursive(srcPath, destPath, deps);
71
+ } else {
72
+ await fsModule.copyFile(srcPath, destPath);
73
+ }
74
+ }
75
+ }
76
+
77
+ module.exports = {
78
+ backupFile,
79
+ backupDir,
80
+ copyDirRecursive,
81
+ };
@@ -0,0 +1,118 @@
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 ADDED
@@ -0,0 +1,125 @@
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
+ };
@@ -0,0 +1,47 @@
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
+ };