airail 0.1.0 → 0.1.2
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/dist/cli/add.js +5 -5
- package/dist/cli/config.js +12 -12
- package/dist/cli/index.js +14 -8
- package/dist/cli/install.js +43 -0
- package/dist/cli/update.js +2 -2
- package/package.json +1 -1
- package/src/templates/CLAUDE.md +4 -6
- package/src/templates/hooks/stop.js +31 -0
- package/src/templates/settings.json +6 -0
package/dist/cli/add.js
CHANGED
|
@@ -45,7 +45,7 @@ async function cmdAdd(arg) {
|
|
|
45
45
|
const config = (0, utils_1.requireAirailJson)(claudeDir);
|
|
46
46
|
if (!arg) {
|
|
47
47
|
console.error((0, colors_1.err)('用法: airail add <包名[@版本]|路径>'));
|
|
48
|
-
|
|
48
|
+
throw new Error('');
|
|
49
49
|
}
|
|
50
50
|
if (config.pack) {
|
|
51
51
|
console.log((0, colors_1.warn)(`当前已安装 pack: ${config.pack}`));
|
|
@@ -56,7 +56,7 @@ async function cmdAdd(arg) {
|
|
|
56
56
|
const localPath = path.resolve(arg);
|
|
57
57
|
if (!fs.existsSync(localPath)) {
|
|
58
58
|
console.error((0, colors_1.err)(`路径不存在: ${localPath}`));
|
|
59
|
-
|
|
59
|
+
throw new Error('');
|
|
60
60
|
}
|
|
61
61
|
const packName = readPackName(localPath);
|
|
62
62
|
(0, utils_1.installPackFromDir)(localPath, packName, claudeDir);
|
|
@@ -69,7 +69,7 @@ async function cmdAdd(arg) {
|
|
|
69
69
|
const rc = (0, utils_1.readRc)();
|
|
70
70
|
if (!rc.configServer) {
|
|
71
71
|
console.error((0, colors_1.err)('未配置配置仓库,请先执行: airail config setup'));
|
|
72
|
-
|
|
72
|
+
throw new Error('');
|
|
73
73
|
}
|
|
74
74
|
await installPackFromConfigCenter(arg, claudeDir, config, rc);
|
|
75
75
|
(0, utils_1.writeAirailJson)(claudeDir, config);
|
|
@@ -113,13 +113,13 @@ async function installPackFromConfigCenter(packNameWithVersion, claudeDir, confi
|
|
|
113
113
|
}
|
|
114
114
|
catch (e) {
|
|
115
115
|
console.error((0, colors_1.err)(`获取规范包列表失败: ${e.message}`));
|
|
116
|
-
|
|
116
|
+
throw new Error('');
|
|
117
117
|
}
|
|
118
118
|
const pack = packs.find(p => p.name === packName);
|
|
119
119
|
if (!pack) {
|
|
120
120
|
console.error((0, colors_1.err)(`规范包 "${packName}" 不存在。`));
|
|
121
121
|
console.log((0, colors_1.info)(`可用规范包: ${packs.map(p => p.name).join(', ')}`));
|
|
122
|
-
|
|
122
|
+
throw new Error('');
|
|
123
123
|
}
|
|
124
124
|
// 使用用户指定的版本,如果没有指定则使用配置仓库的版本
|
|
125
125
|
const gitRef = userVersion || pack.gitRef;
|
package/dist/cli/config.js
CHANGED
|
@@ -89,7 +89,7 @@ async function setup() {
|
|
|
89
89
|
validate: (v) => v.startsWith('http') || '请输入有效的 Git 仓库 URL',
|
|
90
90
|
});
|
|
91
91
|
const token = await (0, prompts_1.input)({
|
|
92
|
-
message: '访问 Token (
|
|
92
|
+
message: '访问 Token (私有仓库需要,如果 Git 已存储凭据可留空):',
|
|
93
93
|
default: rc.configToken,
|
|
94
94
|
});
|
|
95
95
|
// 清理旧的本地仓库(如果 URL 变了)
|
|
@@ -104,19 +104,19 @@ async function listConfigs() {
|
|
|
104
104
|
const rc = (0, utils_1.readRc)();
|
|
105
105
|
if (!rc.configServer) {
|
|
106
106
|
console.error((0, colors_1.err)('未配置配置仓库,请先执行: airail config setup'));
|
|
107
|
-
|
|
107
|
+
throw new Error('');
|
|
108
108
|
}
|
|
109
109
|
try {
|
|
110
110
|
ensureConfigRepo(rc.configServer, rc.configToken);
|
|
111
111
|
}
|
|
112
112
|
catch (e) {
|
|
113
113
|
console.error((0, colors_1.err)(e.message));
|
|
114
|
-
|
|
114
|
+
throw new Error('');
|
|
115
115
|
}
|
|
116
116
|
const indexPath = path.join(CONFIG_REPO_DIR, 'settings', 'index.json');
|
|
117
117
|
if (!fs.existsSync(indexPath)) {
|
|
118
118
|
console.error((0, colors_1.err)('配置仓库中未找到 settings/index.json'));
|
|
119
|
-
|
|
119
|
+
throw new Error('');
|
|
120
120
|
}
|
|
121
121
|
let entries;
|
|
122
122
|
try {
|
|
@@ -124,7 +124,7 @@ async function listConfigs() {
|
|
|
124
124
|
}
|
|
125
125
|
catch (e) {
|
|
126
126
|
console.error((0, colors_1.err)(`读取配置列表失败: ${e.message}`));
|
|
127
|
-
|
|
127
|
+
throw new Error('');
|
|
128
128
|
}
|
|
129
129
|
if (entries.length === 0) {
|
|
130
130
|
console.log((0, colors_1.warn)('暂无可用配置。'));
|
|
@@ -139,20 +139,20 @@ async function useConfig(name) {
|
|
|
139
139
|
const rc = (0, utils_1.readRc)();
|
|
140
140
|
if (!rc.configServer) {
|
|
141
141
|
console.error((0, colors_1.err)('未配置配置仓库,请先执行: airail config setup'));
|
|
142
|
-
|
|
142
|
+
throw new Error('');
|
|
143
143
|
}
|
|
144
144
|
try {
|
|
145
145
|
ensureConfigRepo(rc.configServer, rc.configToken);
|
|
146
146
|
}
|
|
147
147
|
catch (e) {
|
|
148
148
|
console.error((0, colors_1.err)(e.message));
|
|
149
|
-
|
|
149
|
+
throw new Error('');
|
|
150
150
|
}
|
|
151
151
|
// 获取配置清单
|
|
152
152
|
const indexPath = path.join(CONFIG_REPO_DIR, 'settings', 'index.json');
|
|
153
153
|
if (!fs.existsSync(indexPath)) {
|
|
154
154
|
console.error((0, colors_1.err)('配置仓库中未找到 settings/index.json'));
|
|
155
|
-
|
|
155
|
+
throw new Error('');
|
|
156
156
|
}
|
|
157
157
|
let entries;
|
|
158
158
|
try {
|
|
@@ -160,7 +160,7 @@ async function useConfig(name) {
|
|
|
160
160
|
}
|
|
161
161
|
catch (e) {
|
|
162
162
|
console.error((0, colors_1.err)(`读取配置列表失败: ${e.message}`));
|
|
163
|
-
|
|
163
|
+
throw new Error('');
|
|
164
164
|
}
|
|
165
165
|
if (entries.length === 0) {
|
|
166
166
|
console.log((0, colors_1.warn)('暂无可用配置。'));
|
|
@@ -171,7 +171,7 @@ async function useConfig(name) {
|
|
|
171
171
|
if (name) {
|
|
172
172
|
if (!entries.find(e => e.name === name)) {
|
|
173
173
|
console.error((0, colors_1.err)(`配置 "${name}" 不存在,可用: ${entries.map(e => e.name).join(', ')}`));
|
|
174
|
-
|
|
174
|
+
throw new Error('');
|
|
175
175
|
}
|
|
176
176
|
chosen = name;
|
|
177
177
|
}
|
|
@@ -188,7 +188,7 @@ async function useConfig(name) {
|
|
|
188
188
|
const settingsPath = path.join(CONFIG_REPO_DIR, 'settings', `${chosen}.json`);
|
|
189
189
|
if (!fs.existsSync(settingsPath)) {
|
|
190
190
|
console.error((0, colors_1.err)(`配置文件不存在: settings/${chosen}.json`));
|
|
191
|
-
|
|
191
|
+
throw new Error('');
|
|
192
192
|
}
|
|
193
193
|
let content;
|
|
194
194
|
try {
|
|
@@ -197,7 +197,7 @@ async function useConfig(name) {
|
|
|
197
197
|
}
|
|
198
198
|
catch (e) {
|
|
199
199
|
console.error((0, colors_1.err)(`读取配置失败: ${e.message}`));
|
|
200
|
-
|
|
200
|
+
throw new Error('');
|
|
201
201
|
}
|
|
202
202
|
const globalClaudeDir = path.join(os.homedir(), '.claude');
|
|
203
203
|
if (!fs.existsSync(globalClaudeDir)) {
|
package/dist/cli/index.js
CHANGED
|
@@ -41,6 +41,7 @@ const clear_1 = require("./clear");
|
|
|
41
41
|
const update_1 = require("./update");
|
|
42
42
|
const status_1 = require("./status");
|
|
43
43
|
const config_1 = require("./config");
|
|
44
|
+
const install_1 = require("./install");
|
|
44
45
|
function printBanner() {
|
|
45
46
|
console.log([
|
|
46
47
|
'',
|
|
@@ -59,6 +60,7 @@ function printHelp() {
|
|
|
59
60
|
const cmds = [
|
|
60
61
|
['init', '在当前项目初始化 airail'],
|
|
61
62
|
['add <url|路径>', '安装规范包(git URL 或本地路径)'],
|
|
63
|
+
['install [工具名]', '安装 CLI 工具(如 claude-code)'],
|
|
62
64
|
['clear', '清理 airail 相关内容'],
|
|
63
65
|
['update', '更新已安装的规范包'],
|
|
64
66
|
['config [子命令]', '管理团队配置仓库 (setup / list / use)'],
|
|
@@ -74,27 +76,31 @@ async function executeCommand(cmd, args) {
|
|
|
74
76
|
const commands = {
|
|
75
77
|
init: init_1.cmdInit,
|
|
76
78
|
add: () => (0, add_1.cmdAdd)(args[0]),
|
|
79
|
+
install: () => (0, install_1.cmdInstall)(args[0]),
|
|
77
80
|
clear: clear_1.cmdClear,
|
|
78
81
|
update: update_1.cmdUpdate,
|
|
79
82
|
status: status_1.cmdStatus,
|
|
80
83
|
config: () => (0, config_1.cmdConfig)(args[0], ...args.slice(1)),
|
|
81
84
|
};
|
|
82
85
|
if (cmd === 'exit') {
|
|
83
|
-
return false;
|
|
86
|
+
return { shouldContinue: false, success: true };
|
|
84
87
|
}
|
|
85
88
|
const handler = commands[cmd];
|
|
86
89
|
if (!handler) {
|
|
87
90
|
console.log(`未知命令: ${cmd}`);
|
|
88
91
|
printHelp();
|
|
89
|
-
return true;
|
|
92
|
+
return { shouldContinue: true, success: false };
|
|
90
93
|
}
|
|
91
94
|
try {
|
|
92
95
|
await handler();
|
|
96
|
+
return { shouldContinue: true, success: true };
|
|
93
97
|
}
|
|
94
98
|
catch (e) {
|
|
95
|
-
|
|
99
|
+
if (e.message) {
|
|
100
|
+
console.error(e.message);
|
|
101
|
+
}
|
|
102
|
+
return { shouldContinue: true, success: false };
|
|
96
103
|
}
|
|
97
|
-
return true;
|
|
98
104
|
}
|
|
99
105
|
function startInteractiveMode() {
|
|
100
106
|
printBanner();
|
|
@@ -113,7 +119,7 @@ function startInteractiveMode() {
|
|
|
113
119
|
return;
|
|
114
120
|
}
|
|
115
121
|
const [cmd, ...args] = input.split(/\s+/);
|
|
116
|
-
const shouldContinue = await executeCommand(cmd, args);
|
|
122
|
+
const { shouldContinue } = await executeCommand(cmd, args);
|
|
117
123
|
if (!shouldContinue) {
|
|
118
124
|
rl.close();
|
|
119
125
|
process.exit(0);
|
|
@@ -133,9 +139,9 @@ function runCLI() {
|
|
|
133
139
|
return;
|
|
134
140
|
}
|
|
135
141
|
// 有参数:直接执行命令(不显示 banner)
|
|
136
|
-
executeCommand(cmd, args).then((shouldContinue) => {
|
|
137
|
-
if (!shouldContinue) {
|
|
138
|
-
process.exit(0);
|
|
142
|
+
executeCommand(cmd, args).then(({ shouldContinue, success }) => {
|
|
143
|
+
if (!shouldContinue || !success) {
|
|
144
|
+
process.exit(success ? 0 : 1);
|
|
139
145
|
}
|
|
140
146
|
}).catch((e) => {
|
|
141
147
|
console.error(e.message);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cmdInstall = cmdInstall;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
6
|
+
const CLI_TOOLS = {
|
|
7
|
+
'claude-code': {
|
|
8
|
+
name: 'Claude Code',
|
|
9
|
+
package: '@anthropic-ai/claude-code',
|
|
10
|
+
description: 'Anthropic 官方 Claude CLI 工具'
|
|
11
|
+
},
|
|
12
|
+
// 预留扩展
|
|
13
|
+
// 'codex': {
|
|
14
|
+
// name: 'Codex',
|
|
15
|
+
// package: 'codex-cli',
|
|
16
|
+
// description: 'OpenAI Codex CLI 工具'
|
|
17
|
+
// }
|
|
18
|
+
};
|
|
19
|
+
async function cmdInstall(toolName) {
|
|
20
|
+
try {
|
|
21
|
+
let selectedTool = toolName;
|
|
22
|
+
if (!selectedTool) {
|
|
23
|
+
selectedTool = await (0, prompts_1.select)({
|
|
24
|
+
message: '选择要安装的 CLI 工具:',
|
|
25
|
+
choices: Object.entries(CLI_TOOLS).map(([key, tool]) => ({
|
|
26
|
+
value: key,
|
|
27
|
+
name: `${tool.name} - ${tool.description}`
|
|
28
|
+
}))
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const tool = CLI_TOOLS[selectedTool];
|
|
32
|
+
if (!tool) {
|
|
33
|
+
console.log(`\x1b[31m✘ 未知工具: ${selectedTool}\x1b[0m`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
console.log(`\n→ 安装 ${tool.name}...`);
|
|
37
|
+
(0, child_process_1.execSync)(`npm install -g ${tool.package}`, { stdio: 'inherit' });
|
|
38
|
+
console.log(`\x1b[32m✔ ${tool.name} 安装成功\x1b[0m\n`);
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
console.error(`\x1b[31m✘ 安装失败: ${e.message}\x1b[0m`);
|
|
42
|
+
}
|
|
43
|
+
}
|
package/dist/cli/update.js
CHANGED
|
@@ -42,10 +42,10 @@ async function cmdUpdate() {
|
|
|
42
42
|
const config = (0, utils_1.requireAirailJson)(claudeDir);
|
|
43
43
|
if (!config.pack) {
|
|
44
44
|
console.error((0, colors_1.err)('未安装任何 pack,无需更新。'));
|
|
45
|
-
|
|
45
|
+
throw new Error('');
|
|
46
46
|
}
|
|
47
47
|
// 从 airail.json 读取 pack 来源信息(需要扩展 AirailJson 结构)
|
|
48
48
|
// 暂时简化:提示用户重新 add
|
|
49
49
|
console.error((0, colors_1.err)('update 功能开发中,请使用 airail add 重新安装。'));
|
|
50
|
-
|
|
50
|
+
throw new Error('');
|
|
51
51
|
}
|
package/package.json
CHANGED
package/src/templates/CLAUDE.md
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 项目开发规范
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> 本项目使用 AIRail 管理编码规范,技能位于 `.claude/skills/`
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 项目约定
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
[在此添加项目特定的开发规范、架构约定、命名规范等]
|
|
8
8
|
|
|
9
|
-
## Adding Custom Rules
|
|
10
9
|
|
|
11
|
-
Add project-specific rules to this file. They will be loaded at session start.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Stop Hook - Claude 回答结束时触发
|
|
4
|
+
* 功能: 清理误创建的 nul 文件(Windows 下 > nul 可能创建该文件)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
// 清理可能误创建的 nul 文件
|
|
11
|
+
const findAndDeleteNul = (dir, depth = 0) => {
|
|
12
|
+
if (depth > 5) return;
|
|
13
|
+
try {
|
|
14
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
15
|
+
for (const entry of entries) {
|
|
16
|
+
const fullPath = path.join(dir, entry.name);
|
|
17
|
+
if (entry.isFile() && entry.name === 'nul') {
|
|
18
|
+
fs.unlinkSync(fullPath);
|
|
19
|
+
console.error(`🧹 已清理: ${fullPath}`);
|
|
20
|
+
} else if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
|
|
21
|
+
findAndDeleteNul(fullPath, depth + 1);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
} catch {
|
|
25
|
+
// 访问失败时静默忽略
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
findAndDeleteNul(process.cwd());
|
|
30
|
+
|
|
31
|
+
process.exit(0);
|
|
@@ -18,6 +18,12 @@
|
|
|
18
18
|
"matcher": "Bash|Write",
|
|
19
19
|
"hooks": [{ "type": "command", "command": "node .claude/hooks/guard.js", "timeout": 5000 }]
|
|
20
20
|
}
|
|
21
|
+
],
|
|
22
|
+
"Stop": [
|
|
23
|
+
{
|
|
24
|
+
"matcher": "",
|
|
25
|
+
"hooks": [{ "type": "command", "command": "node .claude/hooks/stop.js" }]
|
|
26
|
+
}
|
|
21
27
|
]
|
|
22
28
|
},
|
|
23
29
|
"mcpServers": {}
|