airail 0.1.7 → 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,22 +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);
71
+ // 同步本地配置仓库(静默)
72
+ try {
73
+ (0, utils_1.ensureConfigRepo)(rc.configServer, rc.configToken);
74
+ }
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);
75
113
  (0, utils_1.writeAirailJson)(claudeDir, config);
76
114
  }
77
- function readPackName(dir) {
78
- return path.basename(dir);
79
- }
80
- async function installGitPack(url, claudeDir, config) {
115
+ async function installGitPack(url, packName, claudeDir, config) {
81
116
  const [repoUrl, ref] = url.split('#');
82
117
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'airail-git-'));
83
118
  try {
84
- console.log((0, colors_1.info)(`正在克隆 ${repoUrl}${ref ? ` (${ref})` : ''}...`));
85
119
  const cloneCmd = ref
86
- ? `git clone --depth 1 --branch ${ref} ${repoUrl} ${tmpDir}`
87
- : `git clone --depth 1 ${repoUrl} ${tmpDir}`;
120
+ ? `git clone --depth 1 --branch ${ref} "${repoUrl}" "${tmpDir}"`
121
+ : `git clone --depth 1 "${repoUrl}" "${tmpDir}"`;
88
122
  (0, child_process_1.execSync)(cloneCmd, { stdio: 'pipe' });
89
- const packName = readPackName(tmpDir);
90
123
  (0, utils_1.installPackFromDir)(tmpDir, packName, claudeDir);
91
124
  config.pack = packName;
92
125
  console.log((0, colors_1.ok)(`已安装: ${packName} (来自 ${repoUrl})`));
@@ -95,19 +128,8 @@ async function installGitPack(url, claudeDir, config) {
95
128
  fs.rmSync(tmpDir, { recursive: true, force: true });
96
129
  }
97
130
  }
98
- async function installPackFromConfigCenter(packNameWithVersion, claudeDir, config, rc) {
99
- // 解析包名和版本号
131
+ async function installPackFromConfigCenter(packNameWithVersion, claudeDir, config, packs) {
100
132
  const [packName, userVersion] = packNameWithVersion.split('@');
101
- const indexUrl = `${rc.configServer}/packs/index.json`;
102
- console.log((0, colors_1.info)(`正在从配置仓库获取规范包列表...`));
103
- let packs;
104
- try {
105
- packs = JSON.parse(await (0, utils_1.fetchText)(indexUrl, rc.configToken));
106
- }
107
- catch (e) {
108
- console.error((0, colors_1.err)(`获取规范包列表失败: ${e.message}`));
109
- throw new Error('');
110
- }
111
133
  const pack = packs.find(p => p.name === packName);
112
134
  if (!pack) {
113
135
  console.error((0, colors_1.err)(`规范包 "${packName}" 不存在。`));
@@ -116,44 +138,24 @@ async function installPackFromConfigCenter(packNameWithVersion, claudeDir, confi
116
138
  }
117
139
  // 本地规范包(gitUrl 为空)
118
140
  if (!pack.gitUrl) {
119
- console.log((0, colors_1.ok)(`找到本地规范包: ${pack.name} v${pack.version}`));
120
- console.log((0, colors_1.info)(`${pack.description}`));
121
- await installLocalPackFromConfigRepo(packName, claudeDir, config, rc);
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}` : ''} (来自配置仓库)`));
122
149
  return;
123
150
  }
124
- // 第三方规范包
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);
136
- }
137
- async function installLocalPackFromConfigRepo(packName, claudeDir, config, rc) {
138
- const configRepoDir = path.join(os.homedir(), '.airail', 'config-repo');
139
- // 确保配置仓库已克隆
140
- if (!fs.existsSync(configRepoDir)) {
141
- console.log((0, colors_1.info)('正在克隆配置仓库...'));
142
- const cloneCmd = rc.configToken
143
- ? `git clone https://oauth2:${rc.configToken}@${rc.configServer.replace(/^https?:\/\//, '')} ${configRepoDir}`
144
- : `git clone ${rc.configServer} ${configRepoDir}`;
145
- (0, child_process_1.execSync)(cloneCmd, { stdio: 'pipe' });
146
- }
147
- else {
148
- console.log((0, colors_1.info)('正在更新配置仓库...'));
149
- (0, child_process_1.execSync)('git pull', { cwd: configRepoDir, stdio: 'pipe' });
150
- }
151
- const packDir = path.join(configRepoDir, 'packs', packName);
152
- if (!fs.existsSync(packDir)) {
153
- console.error((0, colors_1.err)(`配置仓库中不存在本地规范包: packs/${packName}/`));
154
- throw new Error('');
155
- }
156
- (0, utils_1.installPackFromDir)(packDir, packName, claudeDir);
157
- config.pack = packName;
158
- console.log((0, colors_1.ok)(`已安装本地规范包: ${packName} (来自配置仓库)`));
159
+ console.log((0, colors_1.info)(`正在安装: ${pack.name} ${versionInfo}`));
160
+ await installGitPack(gitUrl, packName, claudeDir, config);
159
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('');
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;
@@ -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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "airail",
3
- "version": "0.1.7",
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"