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.
- package/Claude/Skills/CN/CLAUDE.md +998 -0
- package/Claude/Skills/CN/skills/helloagents/analyze/SKILL.md +187 -0
- package/Claude/Skills/CN/skills/helloagents/design/SKILL.md +261 -0
- package/Claude/Skills/CN/skills/helloagents/develop/SKILL.md +352 -0
- package/Claude/Skills/CN/skills/helloagents/kb/SKILL.md +249 -0
- package/Claude/Skills/CN/skills/helloagents/templates/SKILL.md +451 -0
- package/Claude/Skills/EN/CLAUDE.md +998 -0
- package/Claude/Skills/EN/skills/helloagents/analyze/SKILL.md +187 -0
- package/Claude/Skills/EN/skills/helloagents/design/SKILL.md +261 -0
- package/Claude/Skills/EN/skills/helloagents/develop/SKILL.md +352 -0
- package/Claude/Skills/EN/skills/helloagents/kb/SKILL.md +249 -0
- package/Claude/Skills/EN/skills/helloagents/templates/SKILL.md +451 -0
- package/Codex/Skills/CN/AGENTS.md +998 -0
- package/Codex/Skills/CN/skills/helloagents/analyze/SKILL.md +187 -0
- package/Codex/Skills/CN/skills/helloagents/design/SKILL.md +261 -0
- package/Codex/Skills/CN/skills/helloagents/develop/SKILL.md +352 -0
- package/Codex/Skills/CN/skills/helloagents/kb/SKILL.md +249 -0
- package/Codex/Skills/CN/skills/helloagents/templates/SKILL.md +451 -0
- package/Codex/Skills/EN/AGENTS.md +998 -0
- package/Codex/Skills/EN/skills/helloagents/analyze/SKILL.md +187 -0
- package/Codex/Skills/EN/skills/helloagents/design/SKILL.md +261 -0
- package/Codex/Skills/EN/skills/helloagents/develop/SKILL.md +352 -0
- package/Codex/Skills/EN/skills/helloagents/kb/SKILL.md +249 -0
- package/Codex/Skills/EN/skills/helloagents/templates/SKILL.md +451 -0
- package/README.md +840 -0
- package/bin/cli.js +85 -0
- package/lib/args.js +106 -0
- package/lib/backup.js +81 -0
- package/lib/conflict.js +118 -0
- package/lib/copy.js +125 -0
- package/lib/defaults.js +47 -0
- package/lib/index.js +297 -0
- package/lib/output.js +220 -0
- package/lib/prompts.js +173 -0
- package/lib/utils.js +225 -0
- 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
|
+
};
|
package/lib/conflict.js
ADDED
|
@@ -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
|
+
};
|
package/lib/defaults.js
ADDED
|
@@ -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
|
+
};
|