airail 0.1.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/bin/airail.js +3 -0
- package/dist/cli/add.js +136 -0
- package/dist/cli/clear.js +87 -0
- package/dist/cli/colors.js +18 -0
- package/dist/cli/config.js +232 -0
- package/dist/cli/index.js +144 -0
- package/dist/cli/init.js +74 -0
- package/dist/cli/status.js +66 -0
- package/dist/cli/update.js +51 -0
- package/dist/index.js +4 -0
- package/dist/utils.js +243 -0
- package/package.json +39 -0
- package/skills/universal/code-quality/SKILL.md +37 -0
- package/skills/universal/code-review/SKILL.md +37 -0
- package/skills/universal/error-handling/SKILL.md +40 -0
- package/skills/universal/git-workflow/SKILL.md +37 -0
- package/skills/universal/performance/SKILL.md +31 -0
- package/skills/universal/security/SKILL.md +33 -0
- package/skills/universal/skill-add/SKILL.md +241 -0
- package/skills/universal/testing/SKILL.md +39 -0
- package/src/templates/CLAUDE.md +11 -0
- package/src/templates/agents/code-reviewer.md +58 -0
- package/src/templates/agents/project-manager.md +45 -0
- package/src/templates/commands/add-todo.md +24 -0
- package/src/templates/commands/check.md +42 -0
- package/src/templates/commands/crud.md +27 -0
- package/src/templates/commands/dev.md +21 -0
- package/src/templates/commands/init-docs.md +80 -0
- package/src/templates/commands/next.md +31 -0
- package/src/templates/commands/progress.md +38 -0
- package/src/templates/commands/start.md +30 -0
- package/src/templates/commands/update-status.md +22 -0
- package/src/templates/docs/README.md +36 -0
- package/src/templates/docs//345/211/215/347/253/257/345/274/200/345/217/221/346/214/207/345/215/227.md +59 -0
- package/src/templates/docs//345/220/216/347/253/257/345/274/200/345/217/221/346/214/207/345/215/227.md +104 -0
- package/src/templates/docs//345/267/245/345/205/267/347/261/273/344/275/277/347/224/250/346/214/207/345/215/227.md +80 -0
- package/src/templates/docs//346/225/260/346/215/256/345/272/223/350/256/276/350/256/241/350/247/204/350/214/203.md +91 -0
- package/src/templates/docs//346/226/260/345/212/237/350/203/275/345/274/200/345/217/221/346/265/201/347/250/213.md +77 -0
- package/src/templates/docs//346/241/206/346/236/266/350/257/264/346/230/216.md +52 -0
- package/src/templates/hooks/guard.js +113 -0
- package/src/templates/hooks/inject.js +40 -0
- package/src/templates/hooks/skill-eval.js +48 -0
- package/src/templates/settings.json +24 -0
- package/src/templates/templates//345/276/205/345/212/236/346/270/205/345/215/225.md +22 -0
- package/src/templates/templates//351/234/200/346/261/202/346/226/207/346/241/243.md +47 -0
- package/src/templates/templates//351/241/271/347/233/256/347/212/266/346/200/201.md +50 -0
package/bin/airail.js
ADDED
package/dist/cli/add.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.cmdAdd = cmdAdd;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const child_process_1 = require("child_process");
|
|
41
|
+
const utils_1 = require("../utils");
|
|
42
|
+
const colors_1 = require("./colors");
|
|
43
|
+
async function cmdAdd(arg) {
|
|
44
|
+
const claudeDir = path.join(process.cwd(), '.claude');
|
|
45
|
+
const config = (0, utils_1.requireAirailJson)(claudeDir);
|
|
46
|
+
if (!arg) {
|
|
47
|
+
console.error((0, colors_1.err)('用法: airail add <包名[@版本]|路径>'));
|
|
48
|
+
process.exit(1);
|
|
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
|
+
// 本地路径(用于开发测试)
|
|
55
|
+
if (arg.startsWith('./') || arg.startsWith('../') || path.isAbsolute(arg)) {
|
|
56
|
+
const localPath = path.resolve(arg);
|
|
57
|
+
if (!fs.existsSync(localPath)) {
|
|
58
|
+
console.error((0, colors_1.err)(`路径不存在: ${localPath}`));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
const packName = readPackName(localPath);
|
|
62
|
+
(0, utils_1.installPackFromDir)(localPath, packName, claudeDir);
|
|
63
|
+
config.pack = packName;
|
|
64
|
+
(0, utils_1.writeAirailJson)(claudeDir, config);
|
|
65
|
+
console.log((0, colors_1.ok)(`已安装本地规范包: ${packName}`));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// 从配置仓库安装
|
|
69
|
+
const rc = (0, utils_1.readRc)();
|
|
70
|
+
if (!rc.configServer) {
|
|
71
|
+
console.error((0, colors_1.err)('未配置配置仓库,请先执行: airail config setup'));
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
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 { }
|
|
84
|
+
}
|
|
85
|
+
return path.basename(dir);
|
|
86
|
+
}
|
|
87
|
+
async function installGitPack(url, claudeDir, config) {
|
|
88
|
+
const [repoUrl, ref] = url.split('#');
|
|
89
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'airail-git-'));
|
|
90
|
+
try {
|
|
91
|
+
console.log((0, colors_1.info)(`正在克隆 ${repoUrl}${ref ? ` (${ref})` : ''}...`));
|
|
92
|
+
const cloneCmd = ref
|
|
93
|
+
? `git clone --depth 1 --branch ${ref} ${repoUrl} ${tmpDir}`
|
|
94
|
+
: `git clone --depth 1 ${repoUrl} ${tmpDir}`;
|
|
95
|
+
(0, child_process_1.execSync)(cloneCmd, { stdio: 'pipe' });
|
|
96
|
+
const packName = readPackName(tmpDir);
|
|
97
|
+
(0, utils_1.installPackFromDir)(tmpDir, packName, claudeDir);
|
|
98
|
+
config.pack = packName;
|
|
99
|
+
console.log((0, colors_1.ok)(`已安装: ${packName} (来自 ${repoUrl})`));
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function installPackFromConfigCenter(packNameWithVersion, claudeDir, config, rc) {
|
|
106
|
+
// 解析包名和版本号
|
|
107
|
+
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
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
const pack = packs.find(p => p.name === packName);
|
|
119
|
+
if (!pack) {
|
|
120
|
+
console.error((0, colors_1.err)(`规范包 "${packName}" 不存在。`));
|
|
121
|
+
console.log((0, colors_1.info)(`可用规范包: ${packs.map(p => p.name).join(', ')}`));
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
// 使用用户指定的版本,如果没有指定则使用配置仓库的版本
|
|
125
|
+
const gitRef = userVersion || pack.gitRef;
|
|
126
|
+
const gitUrl = gitRef ? `${pack.gitUrl}#${gitRef}` : pack.gitUrl;
|
|
127
|
+
const versionInfo = userVersion
|
|
128
|
+
? `v${userVersion} (用户指定)`
|
|
129
|
+
: pack.version
|
|
130
|
+
? `v${pack.version} (配置仓库默认)`
|
|
131
|
+
: '(默认分支)';
|
|
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
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.cmdClear = cmdClear;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
40
|
+
const colors_1 = require("./colors");
|
|
41
|
+
async function cmdClear() {
|
|
42
|
+
const cwd = process.cwd();
|
|
43
|
+
const claudeDir = path.join(cwd, '.claude');
|
|
44
|
+
if (!fs.existsSync(path.join(claudeDir, 'airail.json'))) {
|
|
45
|
+
console.log((0, colors_1.warn)('未初始化 airail,无需清理。'));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
console.log((0, colors_1.warn)('此操作将删除以下内容:'));
|
|
49
|
+
console.log(' - .claude/airail.json');
|
|
50
|
+
console.log(' - .claude/.airail/');
|
|
51
|
+
console.log(' - .claude/skills/');
|
|
52
|
+
console.log(' - .claude/commands/');
|
|
53
|
+
console.log(' - .claude/agents/');
|
|
54
|
+
console.log(' - .claude/hooks/');
|
|
55
|
+
console.log(' - .claude/templates/');
|
|
56
|
+
console.log(' - .claude/docs/');
|
|
57
|
+
console.log(' - CLAUDE.md');
|
|
58
|
+
console.log('');
|
|
59
|
+
console.log((0, colors_1.ok)('保留:settings.json 和 settings.local.json'));
|
|
60
|
+
console.log('');
|
|
61
|
+
const confirmed = await (0, prompts_1.confirm)({
|
|
62
|
+
message: '确认清理?',
|
|
63
|
+
default: false,
|
|
64
|
+
});
|
|
65
|
+
if (!confirmed) {
|
|
66
|
+
console.log((0, colors_1.warn)('已取消清理。'));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const toDelete = [
|
|
70
|
+
path.join(claudeDir, 'airail.json'),
|
|
71
|
+
path.join(claudeDir, '.airail'),
|
|
72
|
+
path.join(claudeDir, 'skills'),
|
|
73
|
+
path.join(claudeDir, 'commands'),
|
|
74
|
+
path.join(claudeDir, 'agents'),
|
|
75
|
+
path.join(claudeDir, 'hooks'),
|
|
76
|
+
path.join(claudeDir, 'templates'),
|
|
77
|
+
path.join(claudeDir, 'docs'),
|
|
78
|
+
path.join(cwd, 'CLAUDE.md'),
|
|
79
|
+
];
|
|
80
|
+
for (const p of toDelete) {
|
|
81
|
+
if (fs.existsSync(p)) {
|
|
82
|
+
fs.rmSync(p, { recursive: true, force: true });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
console.log((0, colors_1.ok)('已清理 airail 相关内容。'));
|
|
86
|
+
console.log((0, colors_1.warn)('可以重新执行 airail init 初始化。'));
|
|
87
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.info = exports.err = exports.warn = exports.ok = exports.bold = exports.gray = exports.cyan = exports.red = exports.yellow = exports.green = void 0;
|
|
4
|
+
const c = (code) => (s) => `\x1b[${code}m${s}\x1b[0m`;
|
|
5
|
+
exports.green = c(32);
|
|
6
|
+
exports.yellow = c(33);
|
|
7
|
+
exports.red = c(31);
|
|
8
|
+
exports.cyan = c(36);
|
|
9
|
+
exports.gray = c(90);
|
|
10
|
+
exports.bold = c(1);
|
|
11
|
+
const ok = (s) => `${(0, exports.green)('✔')} ${s}`;
|
|
12
|
+
exports.ok = ok;
|
|
13
|
+
const warn = (s) => `${(0, exports.yellow)('⚠')} ${s}`;
|
|
14
|
+
exports.warn = warn;
|
|
15
|
+
const err = (s) => `${(0, exports.red)('✘')} ${s}`;
|
|
16
|
+
exports.err = err;
|
|
17
|
+
const info = (s) => `${(0, exports.cyan)('→')} ${s}`;
|
|
18
|
+
exports.info = info;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.cmdConfig = cmdConfig;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const child_process_1 = require("child_process");
|
|
41
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
42
|
+
const utils_1 = require("../utils");
|
|
43
|
+
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
|
+
// ─── 子命令 ───────────────────────────────────────────────────────────────────
|
|
84
|
+
async function setup() {
|
|
85
|
+
const rc = (0, utils_1.readRc)();
|
|
86
|
+
const server = await (0, prompts_1.input)({
|
|
87
|
+
message: 'Git 仓库地址 (如: https://gitcode.com/user/repo.git):',
|
|
88
|
+
default: rc.configServer,
|
|
89
|
+
validate: (v) => v.startsWith('http') || '请输入有效的 Git 仓库 URL',
|
|
90
|
+
});
|
|
91
|
+
const token = await (0, prompts_1.input)({
|
|
92
|
+
message: '访问 Token (私有仓库需要,留空跳过):',
|
|
93
|
+
default: rc.configToken,
|
|
94
|
+
});
|
|
95
|
+
// 清理旧的本地仓库(如果 URL 变了)
|
|
96
|
+
if (rc.configServer && rc.configServer !== server && fs.existsSync(CONFIG_REPO_DIR)) {
|
|
97
|
+
console.log((0, colors_1.info)('检测到仓库地址变更,清理旧仓库...'));
|
|
98
|
+
fs.rmSync(CONFIG_REPO_DIR, { recursive: true, force: true });
|
|
99
|
+
}
|
|
100
|
+
(0, utils_1.writeRc)({ configServer: server.replace(/\/$/, ''), configToken: token || undefined });
|
|
101
|
+
console.log((0, colors_1.ok)('配置已保存到 ~/.airailrc'));
|
|
102
|
+
}
|
|
103
|
+
async function listConfigs() {
|
|
104
|
+
const rc = (0, utils_1.readRc)();
|
|
105
|
+
if (!rc.configServer) {
|
|
106
|
+
console.error((0, colors_1.err)('未配置配置仓库,请先执行: airail config setup'));
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
ensureConfigRepo(rc.configServer, rc.configToken);
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
console.error((0, colors_1.err)(e.message));
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
const indexPath = path.join(CONFIG_REPO_DIR, 'settings', 'index.json');
|
|
117
|
+
if (!fs.existsSync(indexPath)) {
|
|
118
|
+
console.error((0, colors_1.err)('配置仓库中未找到 settings/index.json'));
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
let entries;
|
|
122
|
+
try {
|
|
123
|
+
entries = JSON.parse(fs.readFileSync(indexPath, 'utf-8'));
|
|
124
|
+
}
|
|
125
|
+
catch (e) {
|
|
126
|
+
console.error((0, colors_1.err)(`读取配置列表失败: ${e.message}`));
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
if (entries.length === 0) {
|
|
130
|
+
console.log((0, colors_1.warn)('暂无可用配置。'));
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
console.log('\n可用配置:');
|
|
134
|
+
for (const entry of entries) {
|
|
135
|
+
console.log(` ${(0, colors_1.cyan)(entry.name.padEnd(20))} ${(0, colors_1.gray)(entry.description)}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function useConfig(name) {
|
|
139
|
+
const rc = (0, utils_1.readRc)();
|
|
140
|
+
if (!rc.configServer) {
|
|
141
|
+
console.error((0, colors_1.err)('未配置配置仓库,请先执行: airail config setup'));
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
ensureConfigRepo(rc.configServer, rc.configToken);
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
console.error((0, colors_1.err)(e.message));
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
// 获取配置清单
|
|
152
|
+
const indexPath = path.join(CONFIG_REPO_DIR, 'settings', 'index.json');
|
|
153
|
+
if (!fs.existsSync(indexPath)) {
|
|
154
|
+
console.error((0, colors_1.err)('配置仓库中未找到 settings/index.json'));
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
let entries;
|
|
158
|
+
try {
|
|
159
|
+
entries = JSON.parse(fs.readFileSync(indexPath, 'utf-8'));
|
|
160
|
+
}
|
|
161
|
+
catch (e) {
|
|
162
|
+
console.error((0, colors_1.err)(`读取配置列表失败: ${e.message}`));
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
if (entries.length === 0) {
|
|
166
|
+
console.log((0, colors_1.warn)('暂无可用配置。'));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
// 交互式选择或直接指定
|
|
170
|
+
let chosen;
|
|
171
|
+
if (name) {
|
|
172
|
+
if (!entries.find(e => e.name === name)) {
|
|
173
|
+
console.error((0, colors_1.err)(`配置 "${name}" 不存在,可用: ${entries.map(e => e.name).join(', ')}`));
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
chosen = name;
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
chosen = await (0, prompts_1.select)({
|
|
180
|
+
message: '选择要应用的配置',
|
|
181
|
+
choices: entries.map(e => ({
|
|
182
|
+
name: `${e.name.padEnd(20)} ${e.description}`,
|
|
183
|
+
value: e.name,
|
|
184
|
+
})),
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
// 读取配置文件
|
|
188
|
+
const settingsPath = path.join(CONFIG_REPO_DIR, 'settings', `${chosen}.json`);
|
|
189
|
+
if (!fs.existsSync(settingsPath)) {
|
|
190
|
+
console.error((0, colors_1.err)(`配置文件不存在: settings/${chosen}.json`));
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
let content;
|
|
194
|
+
try {
|
|
195
|
+
content = fs.readFileSync(settingsPath, 'utf-8');
|
|
196
|
+
JSON.parse(content); // 验证是合法 JSON
|
|
197
|
+
}
|
|
198
|
+
catch (e) {
|
|
199
|
+
console.error((0, colors_1.err)(`读取配置失败: ${e.message}`));
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
const globalClaudeDir = path.join(os.homedir(), '.claude');
|
|
203
|
+
if (!fs.existsSync(globalClaudeDir)) {
|
|
204
|
+
fs.mkdirSync(globalClaudeDir, { recursive: true });
|
|
205
|
+
}
|
|
206
|
+
fs.writeFileSync(path.join(globalClaudeDir, 'settings.json'), content);
|
|
207
|
+
console.log((0, colors_1.ok)(`已应用配置 "${chosen}" → ~/.claude/settings.json`));
|
|
208
|
+
}
|
|
209
|
+
// ─── 入口 ─────────────────────────────────────────────────────────────────────
|
|
210
|
+
async function cmdConfig(sub, ...args) {
|
|
211
|
+
if (sub === 'setup') {
|
|
212
|
+
return setup();
|
|
213
|
+
}
|
|
214
|
+
if (sub === 'list') {
|
|
215
|
+
return listConfigs();
|
|
216
|
+
}
|
|
217
|
+
if (sub === 'use') {
|
|
218
|
+
return useConfig(args[0]);
|
|
219
|
+
}
|
|
220
|
+
// 无子命令:显示当前配置状态
|
|
221
|
+
const rc = (0, utils_1.readRc)();
|
|
222
|
+
console.log([
|
|
223
|
+
'',
|
|
224
|
+
` 配置仓库: ${rc.configServer ? (0, colors_1.cyan)(rc.configServer) : (0, colors_1.gray)('未设置')}`,
|
|
225
|
+
` Token: ${rc.configToken ? (0, colors_1.ok)('已配置') : (0, colors_1.gray)('未设置')}`,
|
|
226
|
+
'',
|
|
227
|
+
'子命令:',
|
|
228
|
+
' setup 设置配置仓库地址和访问 token',
|
|
229
|
+
' list 列出可用配置',
|
|
230
|
+
' use [name] 选择并应用配置',
|
|
231
|
+
].join('\n'));
|
|
232
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runCLI = runCLI;
|
|
37
|
+
const readline = __importStar(require("readline"));
|
|
38
|
+
const init_1 = require("./init");
|
|
39
|
+
const add_1 = require("./add");
|
|
40
|
+
const clear_1 = require("./clear");
|
|
41
|
+
const update_1 = require("./update");
|
|
42
|
+
const status_1 = require("./status");
|
|
43
|
+
const config_1 = require("./config");
|
|
44
|
+
function printBanner() {
|
|
45
|
+
console.log([
|
|
46
|
+
'',
|
|
47
|
+
' \x1b[36m █████╗ ██╗ ██████╗ █████╗ ██╗ ██╗ \x1b[0m',
|
|
48
|
+
' \x1b[36m██╔══██╗ ██║ ██╔══██╗ ██╔══██╗ ██║ ██║ \x1b[0m',
|
|
49
|
+
' \x1b[36m███████║ ██║ ██████╔╝ ███████║ ██║ ██║ \x1b[0m',
|
|
50
|
+
' \x1b[36m██╔══██║ ██║ ██╔══██╗ ██╔══██║ ██║ ██║ \x1b[0m',
|
|
51
|
+
' \x1b[36m██║ ██║ ██║ ██║ ██║ ██║ ██║ ██║ ███████╗ \x1b[0m',
|
|
52
|
+
' \x1b[36m╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ \x1b[0m',
|
|
53
|
+
'',
|
|
54
|
+
' \x1b[90mAIRail 企业级 AI 编码助手框架 · 助你提高生产力、提升 AI 编码能力\x1b[0m',
|
|
55
|
+
'',
|
|
56
|
+
].join('\n'));
|
|
57
|
+
}
|
|
58
|
+
function printHelp() {
|
|
59
|
+
const cmds = [
|
|
60
|
+
['init', '在当前项目初始化 airail'],
|
|
61
|
+
['add <url|路径>', '安装规范包(git URL 或本地路径)'],
|
|
62
|
+
['clear', '清理 airail 相关内容'],
|
|
63
|
+
['update', '更新已安装的规范包'],
|
|
64
|
+
['config [子命令]', '管理团队配置仓库 (setup / list / use)'],
|
|
65
|
+
['status', '查看已安装的技能'],
|
|
66
|
+
['exit', '退出交互模式'],
|
|
67
|
+
];
|
|
68
|
+
const dispWidth = (s) => [...s].reduce((n, c) => n + (c.charCodeAt(0) > 0x7f ? 2 : 1), 0);
|
|
69
|
+
const col = 22;
|
|
70
|
+
const lines = cmds.map(([c, d]) => ` ${c}${' '.repeat(Math.max(1, col - dispWidth(c)))}${d}`);
|
|
71
|
+
console.log(['可用命令:', ...lines].join('\n'));
|
|
72
|
+
}
|
|
73
|
+
async function executeCommand(cmd, args) {
|
|
74
|
+
const commands = {
|
|
75
|
+
init: init_1.cmdInit,
|
|
76
|
+
add: () => (0, add_1.cmdAdd)(args[0]),
|
|
77
|
+
clear: clear_1.cmdClear,
|
|
78
|
+
update: update_1.cmdUpdate,
|
|
79
|
+
status: status_1.cmdStatus,
|
|
80
|
+
config: () => (0, config_1.cmdConfig)(args[0], ...args.slice(1)),
|
|
81
|
+
};
|
|
82
|
+
if (cmd === 'exit') {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
const handler = commands[cmd];
|
|
86
|
+
if (!handler) {
|
|
87
|
+
console.log(`未知命令: ${cmd}`);
|
|
88
|
+
printHelp();
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
await handler();
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
console.error(e.message);
|
|
96
|
+
}
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
function startInteractiveMode() {
|
|
100
|
+
printBanner();
|
|
101
|
+
printHelp();
|
|
102
|
+
console.log('');
|
|
103
|
+
const rl = readline.createInterface({
|
|
104
|
+
input: process.stdin,
|
|
105
|
+
output: process.stdout,
|
|
106
|
+
prompt: '\x1b[36mairail>\x1b[0m ',
|
|
107
|
+
});
|
|
108
|
+
rl.prompt();
|
|
109
|
+
rl.on('line', async (line) => {
|
|
110
|
+
const input = line.trim();
|
|
111
|
+
if (!input) {
|
|
112
|
+
rl.prompt();
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const [cmd, ...args] = input.split(/\s+/);
|
|
116
|
+
const shouldContinue = await executeCommand(cmd, args);
|
|
117
|
+
if (!shouldContinue) {
|
|
118
|
+
rl.close();
|
|
119
|
+
process.exit(0);
|
|
120
|
+
}
|
|
121
|
+
rl.prompt();
|
|
122
|
+
});
|
|
123
|
+
rl.on('SIGINT', () => {
|
|
124
|
+
console.log('\n再见!');
|
|
125
|
+
process.exit(0);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
function runCLI() {
|
|
129
|
+
const [, , cmd, ...args] = process.argv;
|
|
130
|
+
// 无参数:进入交互模式
|
|
131
|
+
if (!cmd) {
|
|
132
|
+
startInteractiveMode();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
// 有参数:直接执行命令(不显示 banner)
|
|
136
|
+
executeCommand(cmd, args).then((shouldContinue) => {
|
|
137
|
+
if (!shouldContinue) {
|
|
138
|
+
process.exit(0);
|
|
139
|
+
}
|
|
140
|
+
}).catch((e) => {
|
|
141
|
+
console.error(e.message);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
});
|
|
144
|
+
}
|