airail 0.1.6 → 0.1.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.
package/dist/cli/add.js CHANGED
@@ -38,27 +38,24 @@ const fs = __importStar(require("fs"));
38
38
  const path = __importStar(require("path"));
39
39
  const os = __importStar(require("os"));
40
40
  const child_process_1 = require("child_process");
41
+ const prompts_1 = require("@inquirer/prompts");
41
42
  const utils_1 = require("../utils");
42
43
  const colors_1 = require("./colors");
43
44
  async function cmdAdd(arg) {
44
45
  const claudeDir = path.join(process.cwd(), '.claude');
45
46
  const config = (0, utils_1.requireAirailJson)(claudeDir);
46
- if (!arg) {
47
- console.error((0, colors_1.err)('用法: airail add <包名[@版本]|路径>'));
48
- throw new Error('');
49
- }
50
- if (config.pack) {
51
- console.log((0, colors_1.warn)(`当前已安装 pack: ${config.pack}`));
52
- console.log((0, colors_1.warn)('一个项目只能安装一个 pack,新 pack 将覆盖现有内容。'));
53
- }
54
47
  // 本地路径(用于开发测试)
55
- if (arg.startsWith('./') || arg.startsWith('../') || path.isAbsolute(arg)) {
48
+ if (arg && (arg.startsWith('./') || arg.startsWith('../') || path.isAbsolute(arg))) {
49
+ if (config.pack) {
50
+ console.log((0, colors_1.warn)(`当前已安装 pack: ${config.pack}`));
51
+ console.log((0, colors_1.warn)('一个项目只能安装一个 pack,新 pack 将覆盖现有内容。'));
52
+ }
56
53
  const localPath = path.resolve(arg);
57
54
  if (!fs.existsSync(localPath)) {
58
55
  console.error((0, colors_1.err)(`路径不存在: ${localPath}`));
59
56
  throw new Error('');
60
57
  }
61
- const packName = readPackName(localPath);
58
+ const packName = path.basename(localPath);
62
59
  (0, utils_1.installPackFromDir)(localPath, packName, claudeDir);
63
60
  config.pack = packName;
64
61
  (0, utils_1.writeAirailJson)(claudeDir, config);
@@ -71,29 +68,58 @@ async function cmdAdd(arg) {
71
68
  console.error((0, colors_1.err)('未配置配置仓库,请先执行: airail config setup'));
72
69
  throw new Error('');
73
70
  }
74
- await installPackFromConfigCenter(arg, claudeDir, config, rc);
75
- (0, utils_1.writeAirailJson)(claudeDir, config);
76
- }
77
- function readPackName(dir) {
78
- const p = path.join(dir, 'pack.json');
79
- if (fs.existsSync(p)) {
80
- try {
81
- return JSON.parse(fs.readFileSync(p, 'utf-8')).name || path.basename(dir);
82
- }
83
- catch { }
71
+ // 同步本地配置仓库(静默)
72
+ try {
73
+ (0, utils_1.ensureConfigRepo)(rc.configServer, rc.configToken);
84
74
  }
85
- return path.basename(dir);
75
+ catch (e) {
76
+ console.error((0, colors_1.err)(e.message));
77
+ throw new Error('');
78
+ }
79
+ // 读取本地 packs/index.json
80
+ const indexPath = path.join(utils_1.CONFIG_REPO_DIR, 'packs', 'index.json');
81
+ if (!fs.existsSync(indexPath)) {
82
+ console.error((0, colors_1.err)('配置仓库中未找到 packs/index.json'));
83
+ throw new Error('');
84
+ }
85
+ let packs;
86
+ try {
87
+ packs = JSON.parse(fs.readFileSync(indexPath, 'utf-8'));
88
+ }
89
+ catch (e) {
90
+ console.error((0, colors_1.err)(`读取规范包列表失败: ${e.message}`));
91
+ throw new Error('');
92
+ }
93
+ if (packs.length === 0) {
94
+ console.log((0, colors_1.warn)('暂无可用规范包。'));
95
+ return;
96
+ }
97
+ // 无包名时交互选择
98
+ let packNameWithVersion = arg;
99
+ if (!packNameWithVersion) {
100
+ packNameWithVersion = await (0, prompts_1.select)({
101
+ message: '选择要安装的规范包',
102
+ choices: packs.map(p => ({
103
+ name: `${p.name}${p.version ? ` (v${p.version})` : ''} ${p.description}`,
104
+ value: p.name,
105
+ })),
106
+ });
107
+ }
108
+ if (config.pack) {
109
+ console.log((0, colors_1.warn)(`当前已安装 pack: ${config.pack}`));
110
+ console.log((0, colors_1.warn)('一个项目只能安装一个 pack,新 pack 将覆盖现有内容。'));
111
+ }
112
+ await installPackFromConfigCenter(packNameWithVersion, claudeDir, config, packs);
113
+ (0, utils_1.writeAirailJson)(claudeDir, config);
86
114
  }
87
- async function installGitPack(url, claudeDir, config) {
115
+ async function installGitPack(url, packName, claudeDir, config) {
88
116
  const [repoUrl, ref] = url.split('#');
89
117
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'airail-git-'));
90
118
  try {
91
- console.log((0, colors_1.info)(`正在克隆 ${repoUrl}${ref ? ` (${ref})` : ''}...`));
92
119
  const cloneCmd = ref
93
- ? `git clone --depth 1 --branch ${ref} ${repoUrl} ${tmpDir}`
94
- : `git clone --depth 1 ${repoUrl} ${tmpDir}`;
120
+ ? `git clone --depth 1 --branch ${ref} "${repoUrl}" "${tmpDir}"`
121
+ : `git clone --depth 1 "${repoUrl}" "${tmpDir}"`;
95
122
  (0, child_process_1.execSync)(cloneCmd, { stdio: 'pipe' });
96
- const packName = readPackName(tmpDir);
97
123
  (0, utils_1.installPackFromDir)(tmpDir, packName, claudeDir);
98
124
  config.pack = packName;
99
125
  console.log((0, colors_1.ok)(`已安装: ${packName} (来自 ${repoUrl})`));
@@ -102,35 +128,34 @@ async function installGitPack(url, claudeDir, config) {
102
128
  fs.rmSync(tmpDir, { recursive: true, force: true });
103
129
  }
104
130
  }
105
- async function installPackFromConfigCenter(packNameWithVersion, claudeDir, config, rc) {
106
- // 解析包名和版本号
131
+ async function installPackFromConfigCenter(packNameWithVersion, claudeDir, config, packs) {
107
132
  const [packName, userVersion] = packNameWithVersion.split('@');
108
- const indexUrl = `${rc.configServer}/packs/index.json`;
109
- console.log((0, colors_1.info)(`正在从配置仓库获取规范包列表...`));
110
- let packs;
111
- try {
112
- packs = JSON.parse(await (0, utils_1.fetchText)(indexUrl, rc.configToken));
113
- }
114
- catch (e) {
115
- console.error((0, colors_1.err)(`获取规范包列表失败: ${e.message}`));
116
- throw new Error('');
117
- }
118
133
  const pack = packs.find(p => p.name === packName);
119
134
  if (!pack) {
120
135
  console.error((0, colors_1.err)(`规范包 "${packName}" 不存在。`));
121
136
  console.log((0, colors_1.info)(`可用规范包: ${packs.map(p => p.name).join(', ')}`));
122
137
  throw new Error('');
123
138
  }
124
- // 使用用户指定的版本,如果没有指定则使用配置仓库的版本
139
+ // 本地规范包(gitUrl 为空)
140
+ if (!pack.gitUrl) {
141
+ const packDir = path.join(utils_1.CONFIG_REPO_DIR, 'packs', packName);
142
+ if (!fs.existsSync(packDir)) {
143
+ console.error((0, colors_1.err)(`配置仓库中不存在本地规范包: packs/${packName}/`));
144
+ throw new Error('');
145
+ }
146
+ (0, utils_1.installPackFromDir)(packDir, packName, claudeDir);
147
+ config.pack = packName;
148
+ console.log((0, colors_1.ok)(`已安装: ${pack.name}${pack.version ? ` v${pack.version}` : ''} (来自配置仓库)`));
149
+ return;
150
+ }
151
+ // 第三方规范包(有 gitUrl)
125
152
  const gitRef = userVersion || pack.gitRef;
126
153
  const gitUrl = gitRef ? `${pack.gitUrl}#${gitRef}` : pack.gitUrl;
127
154
  const versionInfo = userVersion
128
155
  ? `v${userVersion} (用户指定)`
129
156
  : pack.version
130
- ? `v${pack.version} (配置仓库默认)`
157
+ ? `v${pack.version}`
131
158
  : '(默认分支)';
132
- console.log((0, colors_1.ok)(`找到规范包: ${pack.name} ${versionInfo}`));
133
- console.log((0, colors_1.info)(`${pack.description}`));
134
- console.log((0, colors_1.info)(`正在从 Git 仓库安装...`));
135
- await installGitPack(gitUrl, claudeDir, config);
159
+ console.log((0, colors_1.info)(`正在安装: ${pack.name} ${versionInfo}`));
160
+ await installGitPack(gitUrl, packName, claudeDir, config);
136
161
  }
@@ -37,49 +37,9 @@ exports.cmdConfig = cmdConfig;
37
37
  const fs = __importStar(require("fs"));
38
38
  const path = __importStar(require("path"));
39
39
  const os = __importStar(require("os"));
40
- const child_process_1 = require("child_process");
41
40
  const prompts_1 = require("@inquirer/prompts");
42
41
  const utils_1 = require("../utils");
43
42
  const colors_1 = require("./colors");
44
- // ─── 本地配置仓库管理 ──────────────────────────────────────────────────────────
45
- const CONFIG_REPO_DIR = path.join(os.homedir(), '.airail', 'config-repo');
46
- /** 确保本地配置仓库存在且是最新的 */
47
- function ensureConfigRepo(gitUrl, token) {
48
- // 构建带认证的 Git URL(如果有 token)
49
- let authUrl = gitUrl;
50
- if (token && gitUrl.startsWith('https://')) {
51
- authUrl = gitUrl.replace('https://', `https://oauth2:${token}@`);
52
- }
53
- if (!fs.existsSync(CONFIG_REPO_DIR)) {
54
- // 首次克隆
55
- console.log((0, colors_1.info)('正在克隆配置仓库...'));
56
- try {
57
- (0, child_process_1.execSync)(`git clone "${authUrl}" "${CONFIG_REPO_DIR}"`, {
58
- stdio: 'inherit',
59
- encoding: 'utf-8'
60
- });
61
- console.log((0, colors_1.ok)('配置仓库克隆成功'));
62
- }
63
- catch (e) {
64
- throw new Error(`克隆失败: ${e.message}`);
65
- }
66
- }
67
- else {
68
- // 更新现有仓库
69
- console.log((0, colors_1.info)('正在更新配置仓库...'));
70
- try {
71
- (0, child_process_1.execSync)('git pull', {
72
- cwd: CONFIG_REPO_DIR,
73
- stdio: 'inherit',
74
- encoding: 'utf-8'
75
- });
76
- console.log((0, colors_1.ok)('配置仓库已更新'));
77
- }
78
- catch (e) {
79
- console.log((0, colors_1.warn)(`更新失败,使用本地缓存: ${e.message}`));
80
- }
81
- }
82
- }
83
43
  // ─── 子命令 ───────────────────────────────────────────────────────────────────
84
44
  async function setup() {
85
45
  const rc = (0, utils_1.readRc)();
@@ -93,9 +53,9 @@ async function setup() {
93
53
  default: rc.configToken,
94
54
  });
95
55
  // 清理旧的本地仓库(如果 URL 变了)
96
- if (rc.configServer && rc.configServer !== server && fs.existsSync(CONFIG_REPO_DIR)) {
56
+ if (rc.configServer && rc.configServer !== server && fs.existsSync(utils_1.CONFIG_REPO_DIR)) {
97
57
  console.log((0, colors_1.info)('检测到仓库地址变更,清理旧仓库...'));
98
- fs.rmSync(CONFIG_REPO_DIR, { recursive: true, force: true });
58
+ fs.rmSync(utils_1.CONFIG_REPO_DIR, { recursive: true, force: true });
99
59
  }
100
60
  (0, utils_1.writeRc)({ configServer: server.replace(/\/$/, ''), configToken: token || undefined });
101
61
  console.log((0, colors_1.ok)('配置已保存到 ~/.airailrc'));
@@ -107,13 +67,13 @@ async function listConfigs() {
107
67
  throw new Error('');
108
68
  }
109
69
  try {
110
- ensureConfigRepo(rc.configServer, rc.configToken);
70
+ (0, utils_1.ensureConfigRepo)(rc.configServer, rc.configToken);
111
71
  }
112
72
  catch (e) {
113
73
  console.error((0, colors_1.err)(e.message));
114
74
  throw new Error('');
115
75
  }
116
- const indexPath = path.join(CONFIG_REPO_DIR, 'settings', 'index.json');
76
+ const indexPath = path.join(utils_1.CONFIG_REPO_DIR, 'settings', 'index.json');
117
77
  if (!fs.existsSync(indexPath)) {
118
78
  console.error((0, colors_1.err)('配置仓库中未找到 settings/index.json'));
119
79
  throw new Error('');
@@ -142,14 +102,14 @@ async function useConfig(name) {
142
102
  throw new Error('');
143
103
  }
144
104
  try {
145
- ensureConfigRepo(rc.configServer, rc.configToken);
105
+ (0, utils_1.ensureConfigRepo)(rc.configServer, rc.configToken);
146
106
  }
147
107
  catch (e) {
148
108
  console.error((0, colors_1.err)(e.message));
149
109
  throw new Error('');
150
110
  }
151
111
  // 获取配置清单
152
- const indexPath = path.join(CONFIG_REPO_DIR, 'settings', 'index.json');
112
+ const indexPath = path.join(utils_1.CONFIG_REPO_DIR, 'settings', 'index.json');
153
113
  if (!fs.existsSync(indexPath)) {
154
114
  console.error((0, colors_1.err)('配置仓库中未找到 settings/index.json'));
155
115
  throw new Error('');
@@ -185,7 +145,7 @@ async function useConfig(name) {
185
145
  });
186
146
  }
187
147
  // 读取配置文件
188
- const settingsPath = path.join(CONFIG_REPO_DIR, 'settings', `${chosen}.json`);
148
+ const settingsPath = path.join(utils_1.CONFIG_REPO_DIR, 'settings', `${chosen}.json`);
189
149
  if (!fs.existsSync(settingsPath)) {
190
150
  console.error((0, colors_1.err)(`配置文件不存在: settings/${chosen}.json`));
191
151
  throw new Error('');
@@ -203,7 +163,33 @@ async function useConfig(name) {
203
163
  if (!fs.existsSync(globalClaudeDir)) {
204
164
  fs.mkdirSync(globalClaudeDir, { recursive: true });
205
165
  }
206
- fs.writeFileSync(path.join(globalClaudeDir, 'settings.json'), content);
166
+ const settingsFilePath = path.join(globalClaudeDir, 'settings.json');
167
+ const settings = JSON.parse(content);
168
+ // 检查 ANTHROPIC_AUTH_TOKEN,为空时强制输入,有值时可选修改
169
+ if (settings?.env?.ANTHROPIC_AUTH_TOKEN !== undefined) {
170
+ const currentToken = settings.env.ANTHROPIC_AUTH_TOKEN;
171
+ if (!currentToken) {
172
+ console.log((0, colors_1.warn)('配置中 ANTHROPIC_AUTH_TOKEN 为空,需要设置 API Key 才能正常使用。'));
173
+ const apiKey = await (0, prompts_1.input)({
174
+ message: '请输入 API Key:',
175
+ validate: (v) => v.trim().length > 0 || '不能为空',
176
+ });
177
+ settings.env.ANTHROPIC_AUTH_TOKEN = apiKey.trim();
178
+ content = JSON.stringify(settings, null, 2);
179
+ }
180
+ else {
181
+ const masked = currentToken.slice(0, 8) + '****' + currentToken.slice(-4);
182
+ const newKey = await (0, prompts_1.input)({
183
+ message: `API Key (当前: ${masked},直接回车保留,输入新值则替换):`,
184
+ });
185
+ if (newKey.trim()) {
186
+ settings.env.ANTHROPIC_AUTH_TOKEN = newKey.trim();
187
+ content = JSON.stringify(settings, null, 2);
188
+ console.log((0, colors_1.ok)('API Key 已更新'));
189
+ }
190
+ }
191
+ }
192
+ fs.writeFileSync(settingsFilePath, content);
207
193
  console.log((0, colors_1.ok)(`已应用配置 "${chosen}" → ~/.claude/settings.json`));
208
194
  // 检查并处理 config.json
209
195
  const configPath = path.join(globalClaudeDir, 'config.json');
package/dist/cli/init.js CHANGED
@@ -52,7 +52,7 @@ async function cmdInit() {
52
52
  fs.mkdirSync(path.join(claudeDir, 'hooks'), { recursive: true });
53
53
  fs.mkdirSync(path.join(claudeDir, 'skills'), { recursive: true });
54
54
  (0, utils_1.copyHooks)(claudeDir);
55
- (0, utils_1.copyBuiltinSkills)('universal', claudeDir);
55
+ (0, utils_1.copyBuiltinSkills)(claudeDir);
56
56
  (0, utils_1.copyTemplateDir)('commands', claudeDir);
57
57
  (0, utils_1.copyTemplateDir)('agents', claudeDir);
58
58
  (0, utils_1.copyTemplateDir)('templates', claudeDir);
package/dist/utils.js CHANGED
@@ -33,8 +33,9 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.PKG_ROOT = void 0;
36
+ exports.CONFIG_REPO_DIR = exports.PKG_ROOT = void 0;
37
37
  exports.fetchText = fetchText;
38
+ exports.ensureConfigRepo = ensureConfigRepo;
38
39
  exports.readAirailJson = readAirailJson;
39
40
  exports.writeAirailJson = writeAirailJson;
40
41
  exports.requireAirailJson = requireAirailJson;
@@ -43,7 +44,7 @@ exports.writeRc = writeRc;
43
44
  exports.installPackFromDir = installPackFromDir;
44
45
  exports.installSkillsFromDir = installSkillsFromDir;
45
46
  exports.copyBuiltinSkills = copyBuiltinSkills;
46
- exports.removeNonUniversalSkills = removeNonUniversalSkills;
47
+ exports.removePackSkills = removePackSkills;
47
48
  exports.copyDir = copyDir;
48
49
  exports.copyHooks = copyHooks;
49
50
  exports.copyTemplateDir = copyTemplateDir;
@@ -55,6 +56,7 @@ const path = __importStar(require("path"));
55
56
  const os = __importStar(require("os"));
56
57
  const https = __importStar(require("https"));
57
58
  const http = __importStar(require("http"));
59
+ const child_process_1 = require("child_process");
58
60
  exports.PKG_ROOT = path.join(__dirname, '..');
59
61
  // ─── HTTP 工具 ────────────────────────────────────────────────────────────────
60
62
  function fetchText(url, token) {
@@ -83,6 +85,34 @@ function fetchText(url, token) {
83
85
  }).on('error', reject);
84
86
  });
85
87
  }
88
+ // ─── 配置仓库工具 ──────────────────────────────────────────────────────────────
89
+ exports.CONFIG_REPO_DIR = path.join(os.homedir(), '.airail', 'config-repo');
90
+ /**
91
+ * 确保本地配置仓库存在且是最新的(静默模式,不输出 git 日志)
92
+ * 克隆失败时抛出错误;pull 失败时静默使用本地缓存
93
+ */
94
+ function ensureConfigRepo(gitUrl, token) {
95
+ let authUrl = gitUrl;
96
+ if (token && gitUrl.startsWith('https://')) {
97
+ authUrl = gitUrl.replace('https://', `https://oauth2:${token}@`);
98
+ }
99
+ if (!fs.existsSync(exports.CONFIG_REPO_DIR)) {
100
+ try {
101
+ (0, child_process_1.execSync)(`git clone "${authUrl}" "${exports.CONFIG_REPO_DIR}"`, { stdio: 'pipe' });
102
+ }
103
+ catch (e) {
104
+ throw new Error(`克隆配置仓库失败: ${e.stderr?.toString().trim() || e.message}`);
105
+ }
106
+ }
107
+ else {
108
+ try {
109
+ (0, child_process_1.execSync)('git pull', { cwd: exports.CONFIG_REPO_DIR, stdio: 'pipe' });
110
+ }
111
+ catch {
112
+ // 更新失败时静默使用本地缓存
113
+ }
114
+ }
115
+ }
86
116
  function readAirailJson(claudeDir) {
87
117
  const p = path.join(claudeDir, 'airail.json');
88
118
  return JSON.parse(fs.readFileSync(p, 'utf-8'));
@@ -92,8 +122,7 @@ function writeAirailJson(claudeDir, data) {
92
122
  }
93
123
  function requireAirailJson(claudeDir) {
94
124
  if (!fs.existsSync(path.join(claudeDir, 'airail.json'))) {
95
- console.error('未初始化,请先执行 "airail init"。');
96
- process.exit(1);
125
+ throw new Error('未初始化,请先执行 "airail init"。');
97
126
  }
98
127
  return readAirailJson(claudeDir);
99
128
  }
@@ -121,7 +150,7 @@ function writeRc(patch) {
121
150
  */
122
151
  function installPackFromDir(packDir, packName, claudeDir) {
123
152
  // 删除旧 pack 的 skills
124
- removeNonUniversalSkills(claudeDir);
153
+ removePackSkills(claudeDir);
125
154
  // 安装新 pack 的 skills
126
155
  installSkillsFromDir(packDir, packName, claudeDir);
127
156
  // 覆盖 commands / agents / hooks / templates
@@ -135,7 +164,7 @@ function installPackFromDir(packDir, packName, claudeDir) {
135
164
  }
136
165
  }
137
166
  // ─── skills ───────────────────────────────────────────────────────────────────
138
- /** 将 pack/skills/ 平铺复制到 .claude/skills/(universal 不加前缀) */
167
+ /** 将 pack/skills/ 平铺复制到 .claude/skills/(带 packName 前缀) */
139
168
  function installSkillsFromDir(packDir, packName, claudeDir) {
140
169
  const skillsSrc = path.join(packDir, 'skills');
141
170
  if (!fs.existsSync(skillsSrc)) {
@@ -147,14 +176,14 @@ function installSkillsFromDir(packDir, packName, claudeDir) {
147
176
  for (const entry of fs.readdirSync(skillsSrc, { withFileTypes: true })) {
148
177
  if (!entry.isDirectory())
149
178
  continue;
150
- const destName = packName === 'universal' ? entry.name : `${packName}-${entry.name}`;
151
- copyDir(path.join(skillsSrc, entry.name), path.join(skillsDest, destName));
179
+ copyDir(path.join(skillsSrc, entry.name), path.join(skillsDest, `${packName}-${entry.name}`));
152
180
  }
153
181
  }
154
- function copyBuiltinSkills(packName, claudeDir) {
155
- const src = path.join(exports.PKG_ROOT, 'src/templates/skills', packName);
182
+ /** 复制内置 skills(templates/skills/ 下的所有子目录)到 .claude/skills/ */
183
+ function copyBuiltinSkills(claudeDir) {
184
+ const src = path.join(exports.PKG_ROOT, 'src/templates/skills');
156
185
  if (!fs.existsSync(src)) {
157
- console.warn(`内置规范包 "${packName}" 不存在。`);
186
+ console.warn('内置技能目录不存在。');
158
187
  return;
159
188
  }
160
189
  const skillsDest = path.join(claudeDir, 'skills');
@@ -162,12 +191,11 @@ function copyBuiltinSkills(packName, claudeDir) {
162
191
  for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
163
192
  if (!entry.isDirectory())
164
193
  continue;
165
- const destName = packName === 'universal' ? entry.name : `${packName}-${entry.name}`;
166
- copyDir(path.join(src, entry.name), path.join(skillsDest, destName));
194
+ copyDir(path.join(src, entry.name), path.join(skillsDest, entry.name));
167
195
  }
168
196
  }
169
- /** 删除所有非 universal skills(即带 - 前缀的) */
170
- function removeNonUniversalSkills(claudeDir) {
197
+ /** 删除所有 pack 安装的 skills(即带 - 前缀的) */
198
+ function removePackSkills(claudeDir) {
171
199
  const skillsDir = path.join(claudeDir, 'skills');
172
200
  if (!fs.existsSync(skillsDir))
173
201
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "airail",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "AI coding assistant framework - enforce coding standards via Claude hooks & skills",
5
5
  "bin": {
6
6
  "airail": "./bin/airail.js"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: bugfix-verify
3
- description: Bug 修复质量验证者。独立评估修复质量,只读代码库,不做任何修改。由 /bugfix 命令调用。
3
+ description: Read-only bug fix quality verifier. Use proactively after bugfix agent completes a fix to independently evaluate fix quality. Also use when user says "验证修复", "检查修复质量".
4
4
  tools: Read, Grep, Glob
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: bugfix
3
- description: Bug 修复执行者,分析根本原因并实现修复。由 /bugfix 命令调用,或用户说"修复bug""fix bug"时触发。
3
+ description: Bug fix specialist that analyzes root causes and implements fixes. Use when encountering bugs, errors, test failures, or when user says "修复bug", "fix bug", "修复问题".
4
4
  tools: Read, Edit, Write, Bash, Grep, Glob
5
5
  ---
6
6
 
@@ -1,16 +1,22 @@
1
1
  ---
2
2
  name: code-reviewer
3
- description: 自动代码审查助手,在完成功能开发后自动检查代码是否符合项目规范。当使用 /dev 命令完成代码生成后,或用户说"审查代码""检查代码"时自动调用。
3
+ description: Expert code review specialist. Use proactively after writing or modifying code, especially after /dev completes. Also use when user says "审查代码", "检查代码", or "review".
4
4
  model: opus
5
5
  tools: Read, Grep, Glob
6
6
  ---
7
7
 
8
8
  # 代码审查专家
9
9
 
10
- ## 角色定位
11
-
12
10
  你是一位严格的代码审查专家,熟悉本项目的所有规范。你的职责是在代码合并前发现问题,而不是帮助实现功能。
13
11
 
12
+ ## 执行工作流
13
+
14
+ 当被调用时,按以下步骤执行:
15
+
16
+ 1. **查看最近变更** - 运行 `git diff` 查看最近修改的文件
17
+ 2. **聚焦修改文件** - 只审查新增或修改的文件
18
+ 3. **立即开始审查** - 不需要等待,直接分析代码
19
+
14
20
  ## 核心职责
15
21
 
16
22
  在以下场景自动执行代码审查:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: debug
3
- description: 系统化调试协调器,通过多维度分析和验证方法进行问题诊断和修复
3
+ description: Systematic debugging coordinator for multi-dimensional problem diagnosis and resolution. Use when encountering complex issues requiring structured analysis, or when user says "调试", "debug", "排查问题".
4
4
  tools: Read, Edit, Write, Bash, Grep, Glob, WebFetch, TodoWrite
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: optimize
3
- description: 性能优化协调员,领导优化专家团队进行系统性能改进。由 /optimize 命令调用,或用户说"性能优化""优化性能"时触发。
3
+ description: Performance optimization coordinator for systematic performance improvement. Use when user says "性能优化", "优化性能", "optimize", or when performance bottlenecks are identified.
4
4
  tools: Read, Edit, Write, Bash, Grep, Glob
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: refactor
3
- description: 重构协调员,领导重构专家团队改善代码质量和架构。由 /refactor 命令调用,或用户说"重构代码""代码重构"时触发。
3
+ description: Refactoring coordinator for improving code quality and architecture. Use when user says "重构代码", "代码重构", "refactor", or when code smells and technical debt are identified.
4
4
  tools: Read, Edit, Write, Bash, Grep, Glob
5
5
  ---
6
6
 
@@ -27,10 +27,11 @@
27
27
 
28
28
  ### 2️⃣ 修复执行(bugfix agent)
29
29
 
30
- 调用 bugfix agent,传入:
31
- - Bug 描述:$ARGUMENTS
32
- - 收集到的上下文信息
33
- - 上轮验证反馈(迭代时)
30
+ 使用 Agent 工具委托 `bugfix` agent
31
+
32
+ ```
33
+ Agent(subagent_type="bugfix", prompt="修复以下 bug:<bug描述>。上下文信息:<收集到的信息>。[迭代时]上轮验证反馈:<反馈内容>")
34
+ ```
34
35
 
35
36
  期望输出:
36
37
  - 根本原因分析
@@ -41,10 +42,11 @@
41
42
 
42
43
  ### 3️⃣ 质量验证(bugfix-verify agent)
43
44
 
44
- 调用 bugfix-verify agent,传入:
45
- - 修复后的代码
46
- - 原始 bug 描述
47
- - bugfix agent 的输出
45
+ 使用 Agent 工具委托 `bugfix-verify` agent
46
+
47
+ ```
48
+ Agent(subagent_type="bugfix-verify", prompt="验证以下 bug 修复的质量。原始 bug:<bug描述>。修复方案:<bugfix agent 输出>。修复后的代码:<变更文件列表>")
49
+ ```
48
50
 
49
51
  期望输出:
50
52
  - 总体评估(通过/有条件通过/需要改进/失败)
@@ -16,11 +16,20 @@
16
16
  - 记录关键假设和未知因素
17
17
  - 生成 5-7 个可能的问题来源
18
18
 
19
- 2. **多维度调研**:
19
+ 2. **多维度调研(debug agent)**:
20
+
21
+ 使用 Agent 工具委托 `debug` agent:
22
+
23
+ ```
24
+ Agent(subagent_type="debug", prompt="调试以下问题:<问题描述>。复现步骤:<步骤>。相关日志:<日志信息>")
25
+ ```
26
+
27
+ 期望输出:
20
28
  - 架构层面:分析系统设计和模块交互
21
29
  - 代码层面:检查相关代码逻辑和数据流
22
30
  - 环境层面:检查配置、依赖和运行环境
23
31
  - 历史层面:查看相关代码变更历史
32
+ - 综合分析:精炼为 1-2 个最可能的原因
24
33
 
25
34
  3. **假设验证**:
26
35
  - 整合所有分析结果
@@ -19,7 +19,8 @@
19
19
  - 支持格式:Swagger/OpenAPI、Apifox、手写文档、接口截图
20
20
  4. **方案设计**:列出要创建/修改的文件清单(组件、页面、路由、状态),等待确认
21
21
  5. **代码生成**:逐层实现(技能会根据开发内容自动激活)
22
- 6. **完成报告**:列出所有变更文件,提示测试和联调事项
22
+ 6. **代码审查**:代码生成完成后,使用 Agent 工具委托 `code-reviewer` agent 对所有变更文件进行审查
23
+ 7. **完成报告**:列出所有变更文件,提示测试和联调事项
23
24
 
24
25
  ## AI 执行规则
25
26
 
@@ -33,6 +34,16 @@
33
34
  - 遵循项目的状态管理方案(Vuex/Pinia/Redux 等)
34
35
  - 禁止修改框架核心代码和公共组件库
35
36
 
37
+ ## Agent 委托
38
+
39
+ 代码生成完成后,**必须**调用 Agent 工具,按以下方式委托审查:
40
+
41
+ ```
42
+ Agent(subagent_type="code-reviewer", prompt="审查以下前端变更文件:<变更文件列表>。检查是否符合项目规范,是否有安全漏洞、代码重复、性能问题等。")
43
+ ```
44
+
45
+ 审查未通过的问题需在当前流程中修复后,再输出完成报告。
46
+
36
47
  ## 技能自动激活说明
37
48
 
38
49
  开发过程中,AI 会根据触发词自动激活相关技能:
@@ -14,7 +14,8 @@
14
14
  3. **数据库分析**:检查是否需要新建表或修改现有表
15
15
  4. **方案设计**:列出要创建/修改的文件清单,等待确认
16
16
  5. **代码生成**:逐层实现(技能会根据开发内容自动激活)
17
- 6. **完成报告**:列出所有变更文件,提示下一步操作
17
+ 6. **代码审查**:代码生成完成后,使用 Agent 工具委托 `code-reviewer` agent 对所有变更文件进行审查
18
+ 7. **完成报告**:列出所有变更文件,提示下一步操作
18
19
 
19
20
  ## AI 执行规则
20
21
 
@@ -26,6 +27,16 @@
26
27
  - 优先复用项目中已有的工具类和组件
27
28
  - 禁止修改框架核心代码
28
29
 
30
+ ## Agent 委托
31
+
32
+ 代码生成完成后,**必须**调用 Agent 工具,按以下方式委托审查:
33
+
34
+ ```
35
+ Agent(subagent_type="code-reviewer", prompt="审查以下变更文件:<变更文件列表>。检查是否符合项目规范,是否有安全漏洞、代码重复等问题。")
36
+ ```
37
+
38
+ 审查未通过的问题需在当前流程中修复后,再输出完成报告。
39
+
29
40
  ## 技能自动激活说明
30
41
 
31
42
  开发过程中,AI 会根据触发词自动激活相关技能: