airail 0.1.3 → 0.1.4
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/index.js +3 -0
- package/dist/cli/version.js +43 -0
- package/package.json +1 -1
- package/src/templates/agents/code-reviewer.md +20 -5
- package/src/templates/commands/dev-frontend.md +38 -0
- package/src/templates/commands/dev.md +1 -1
- package/src/templates/hooks/skill-eval.cjs +96 -0
- package/src/templates/settings.json +4 -4
- package/src/templates/hooks/skill-eval.js +0 -48
- /package/src/templates/hooks/{guard.js → guard.cjs} +0 -0
- /package/src/templates/hooks/{inject.js → inject.cjs} +0 -0
- /package/src/templates/hooks/{stop.js → stop.cjs} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -42,6 +42,7 @@ const update_1 = require("./update");
|
|
|
42
42
|
const status_1 = require("./status");
|
|
43
43
|
const config_1 = require("./config");
|
|
44
44
|
const install_1 = require("./install");
|
|
45
|
+
const version_1 = require("./version");
|
|
45
46
|
function printBanner() {
|
|
46
47
|
console.log([
|
|
47
48
|
'',
|
|
@@ -65,6 +66,7 @@ function printHelp() {
|
|
|
65
66
|
['update', '更新已安装的规范包'],
|
|
66
67
|
['config [子命令]', '管理团队配置仓库 (setup / list / use)'],
|
|
67
68
|
['status', '查看已安装的技能'],
|
|
69
|
+
['ver', '查看 airail 版本'],
|
|
68
70
|
['exit', '退出交互模式'],
|
|
69
71
|
];
|
|
70
72
|
const dispWidth = (s) => [...s].reduce((n, c) => n + (c.charCodeAt(0) > 0x7f ? 2 : 1), 0);
|
|
@@ -81,6 +83,7 @@ async function executeCommand(cmd, args) {
|
|
|
81
83
|
update: update_1.cmdUpdate,
|
|
82
84
|
status: status_1.cmdStatus,
|
|
83
85
|
config: () => (0, config_1.cmdConfig)(args[0], ...args.slice(1)),
|
|
86
|
+
ver: version_1.cmdVersion,
|
|
84
87
|
};
|
|
85
88
|
if (cmd === 'exit') {
|
|
86
89
|
return { shouldContinue: false, success: true };
|
|
@@ -0,0 +1,43 @@
|
|
|
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.cmdVersion = cmdVersion;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
async function cmdVersion() {
|
|
40
|
+
const packageJsonPath = path.join(__dirname, '../../package.json');
|
|
41
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
42
|
+
console.log(`airail v${packageJson.version}`);
|
|
43
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: code-reviewer
|
|
3
|
-
description:
|
|
3
|
+
description: 自动代码审查助手,在完成功能开发后自动检查代码是否符合项目规范。当使用 /dev 命令完成代码生成后,或用户说"审查代码"、"检查代码"时自动调用。
|
|
4
|
+
model: opus
|
|
5
|
+
tools: Read, Grep, Glob
|
|
4
6
|
---
|
|
5
7
|
|
|
6
8
|
# 代码审查专家
|
|
@@ -9,13 +11,26 @@ description: 代码审查专家,在提交代码前进行全面的规范检查
|
|
|
9
11
|
|
|
10
12
|
你是一位严格的代码审查专家,熟悉本项目的所有规范。你的职责是在代码合并前发现问题,而不是帮助实现功能。
|
|
11
13
|
|
|
14
|
+
## 核心职责
|
|
15
|
+
|
|
16
|
+
在以下场景自动执行代码审查:
|
|
17
|
+
|
|
18
|
+
1. **`/dev` 命令完成后** - 审查新生成的完整业务模块
|
|
19
|
+
2. **用户手动触发** - 说"审查代码"、"检查代码"、"review"
|
|
20
|
+
|
|
21
|
+
## 审查原则
|
|
22
|
+
|
|
23
|
+
1. **严格但不死板** - 遵循规范,但理解特殊情况
|
|
24
|
+
2. **提供修复建议** - 不只指出问题,还要给解决方案
|
|
25
|
+
3. **优先级明确** - 区分必须修复和建议修复
|
|
26
|
+
4. **快速反馈** - 审查报告简洁明了
|
|
27
|
+
|
|
12
28
|
## 审查流程
|
|
13
29
|
|
|
14
|
-
1.
|
|
15
|
-
2.
|
|
16
|
-
3. 输出结构化审查报告
|
|
30
|
+
1. 根据审查清单逐文件检查,按严重程度分类问题
|
|
31
|
+
2. 输出结构化审查报告
|
|
17
32
|
|
|
18
|
-
##
|
|
33
|
+
## 审查清单
|
|
19
34
|
|
|
20
35
|
### 必须修复(阻塞合并)
|
|
21
36
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# /dev-frontend - 开发前端新功能
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
## 执行流程
|
|
6
|
+
|
|
7
|
+
1. **需求确认**:理解要开发的功能,列出关键点和交互流程
|
|
8
|
+
2. **重复检查**:扫描现有组件和页面,确认功能不重复
|
|
9
|
+
3. **接口文档检查**(强制):用户必须提供后端接口文档,包含:
|
|
10
|
+
- 接口地址和请求方法
|
|
11
|
+
- 请求参数结构
|
|
12
|
+
- 响应数据结构
|
|
13
|
+
- 错误码说明
|
|
14
|
+
- 支持格式:Swagger/OpenAPI、Apifox、手写文档、接口截图
|
|
15
|
+
4. **方案设计**:列出要创建/修改的文件清单(组件、页面、路由、状态),等待确认
|
|
16
|
+
5. **代码生成**:逐层实现(技能会根据开发内容自动激活)
|
|
17
|
+
6. **完成报告**:列出所有变更文件,提示测试和联调事项
|
|
18
|
+
|
|
19
|
+
## AI 执行规则
|
|
20
|
+
|
|
21
|
+
- **强制要求**:没有后端接口文档,不得开始开发。必须先向用户索要接口文档
|
|
22
|
+
- 开始写代码前必须先列出方案,等待用户确认
|
|
23
|
+
- 相关技能会根据开发内容自动激活:
|
|
24
|
+
- 前端开发 → frontend-development 技能
|
|
25
|
+
- API 对接 → api-integration 技能
|
|
26
|
+
- 组件设计 → component-design 技能
|
|
27
|
+
- 优先复用项目中已有的组件、工具函数和样式
|
|
28
|
+
- 遵循项目的状态管理方案(Vuex/Pinia/Redux 等)
|
|
29
|
+
- 禁止修改框架核心代码和公共组件库
|
|
30
|
+
|
|
31
|
+
## 技能自动激活说明
|
|
32
|
+
|
|
33
|
+
开发过程中,AI 会根据触发词自动激活相关技能:
|
|
34
|
+
- 提到"组件"、"页面"、"Vue"、"React" → 激活 frontend-development
|
|
35
|
+
- 提到"API"、"接口"、"请求" → 激活 api-integration
|
|
36
|
+
- 提到"样式"、"布局"、"响应式" → 激活 ui-design
|
|
37
|
+
|
|
38
|
+
无需手动调用技能,AI 会自动应用项目规范。
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Hook: UserPromptSubmit
|
|
4
|
+
* 强制技能激活 - 将激活率从 ~25% 提升到 90%+
|
|
5
|
+
* 来源:ruoyi-vue-plus skill-forced-eval.js 方案
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
// 从 stdin 读取用户输入
|
|
12
|
+
let inputData = '';
|
|
13
|
+
try {
|
|
14
|
+
inputData = fs.readFileSync(0, 'utf8');
|
|
15
|
+
} catch {
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let input;
|
|
20
|
+
try {
|
|
21
|
+
input = JSON.parse(inputData);
|
|
22
|
+
} catch {
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const prompt = (input.prompt || '').trim();
|
|
27
|
+
|
|
28
|
+
// 跳过斜杠命令
|
|
29
|
+
if (/^\/[^\s]/.test(prompt)) process.exit(0);
|
|
30
|
+
|
|
31
|
+
// 检测是否是恢复会话(防止上下文溢出死循环)
|
|
32
|
+
const skipPatterns = [
|
|
33
|
+
'continued from a previous conversation',
|
|
34
|
+
'ran out of context',
|
|
35
|
+
'No code restore',
|
|
36
|
+
'Conversation compacted',
|
|
37
|
+
'commands restored',
|
|
38
|
+
'context window',
|
|
39
|
+
'session is being continued',
|
|
40
|
+
'继续',
|
|
41
|
+
'恢复',
|
|
42
|
+
'上次'
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
const isRecoverySession = skipPatterns.some(pattern =>
|
|
46
|
+
prompt.toLowerCase().includes(pattern.toLowerCase())
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
if (isRecoverySession) {
|
|
50
|
+
process.exit(0);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 读取已安装技能列表
|
|
54
|
+
const claudeDir = path.join(process.cwd(), '.claude');
|
|
55
|
+
let skills = [];
|
|
56
|
+
try {
|
|
57
|
+
const skillsDir = path.join(claudeDir, 'skills');
|
|
58
|
+
for (const skill of fs.readdirSync(skillsDir)) {
|
|
59
|
+
const skillFile = path.join(skillsDir, skill, 'SKILL.md');
|
|
60
|
+
if (fs.existsSync(skillFile)) skills.push(skill);
|
|
61
|
+
}
|
|
62
|
+
} catch {}
|
|
63
|
+
|
|
64
|
+
if (skills.length === 0) process.exit(0);
|
|
65
|
+
|
|
66
|
+
// 输出强制激活指令
|
|
67
|
+
const instructions = `## 强制技能激活流程(必须执行)
|
|
68
|
+
|
|
69
|
+
### 步骤 1 - 评估(必须在响应中明确展示)
|
|
70
|
+
|
|
71
|
+
针对用户问题,列出匹配的技能:\`技能名: 理由\`,无匹配则写"无匹配技能"
|
|
72
|
+
|
|
73
|
+
可用技能 (${skills.length} 个):${skills.join(', ')}
|
|
74
|
+
|
|
75
|
+
### 步骤 2 - 激活(逐个调用,等待每个完成)
|
|
76
|
+
|
|
77
|
+
⚠️ **必须逐个调用 Skill() 工具,每次调用后等待返回再调用下一个**
|
|
78
|
+
- 有 N 个匹配技能 → 逐个发起 N 次 Skill() 调用(不要并行!)
|
|
79
|
+
- 无匹配技能 → 跳过此步骤
|
|
80
|
+
|
|
81
|
+
**调用顺序**:按列出顺序,先调用第一个,等返回后再调用第二个...
|
|
82
|
+
|
|
83
|
+
### 步骤 3 - 实现
|
|
84
|
+
|
|
85
|
+
只有在步骤 2 的所有 Skill() 调用完成后,才能开始实现。
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
**关键规则(违反将导致任务失败)**:
|
|
90
|
+
1. ⛔ 禁止:评估后跳过 Skill() 直接实现
|
|
91
|
+
2. ⛔ 禁止:只调用部分技能(必须全部调用)
|
|
92
|
+
3. ⛔ 禁止:并行调用多个 Skill()(必须串行,一个一个来)
|
|
93
|
+
4. ✅ 正确:评估 → 逐个调用 Skill() → 全部完成后实现`;
|
|
94
|
+
|
|
95
|
+
console.log(instructions);
|
|
96
|
+
process.exit(0);
|
|
@@ -4,25 +4,25 @@
|
|
|
4
4
|
"SessionStart": [
|
|
5
5
|
{
|
|
6
6
|
"matcher": "",
|
|
7
|
-
"hooks": [{ "type": "command", "command": "node .claude/hooks/inject.
|
|
7
|
+
"hooks": [{ "type": "command", "command": "node .claude/hooks/inject.cjs" }]
|
|
8
8
|
}
|
|
9
9
|
],
|
|
10
10
|
"UserPromptSubmit": [
|
|
11
11
|
{
|
|
12
12
|
"matcher": "",
|
|
13
|
-
"hooks": [{ "type": "command", "command": "node .claude/hooks/skill-eval.
|
|
13
|
+
"hooks": [{ "type": "command", "command": "node .claude/hooks/skill-eval.cjs" }]
|
|
14
14
|
}
|
|
15
15
|
],
|
|
16
16
|
"PreToolUse": [
|
|
17
17
|
{
|
|
18
18
|
"matcher": "Bash|Write",
|
|
19
|
-
"hooks": [{ "type": "command", "command": "node .claude/hooks/guard.
|
|
19
|
+
"hooks": [{ "type": "command", "command": "node .claude/hooks/guard.cjs", "timeout": 5000 }]
|
|
20
20
|
}
|
|
21
21
|
],
|
|
22
22
|
"Stop": [
|
|
23
23
|
{
|
|
24
24
|
"matcher": "",
|
|
25
|
-
"hooks": [{ "type": "command", "command": "node .claude/hooks/stop.
|
|
25
|
+
"hooks": [{ "type": "command", "command": "node .claude/hooks/stop.cjs" }]
|
|
26
26
|
}
|
|
27
27
|
]
|
|
28
28
|
},
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Hook: UserPromptSubmit
|
|
4
|
-
* 强制技能激活 - 将激活率从 ~25% 提升到 90%+
|
|
5
|
-
* 来源:ruoyi-vue-plus skill-forced-eval.js 方案
|
|
6
|
-
*/
|
|
7
|
-
const input = JSON.parse(process.argv[2] || '{}');
|
|
8
|
-
const prompt = (input.prompt || '').trim();
|
|
9
|
-
|
|
10
|
-
// 跳过斜杠命令
|
|
11
|
-
if (/^\/[^\s]/.test(prompt)) process.exit(0);
|
|
12
|
-
|
|
13
|
-
// 跳过恢复会话关键词
|
|
14
|
-
const skipWords = ['继续', 'continue', 'resume', '恢复', '上次'];
|
|
15
|
-
if (skipWords.some(w => prompt.toLowerCase().includes(w))) process.exit(0);
|
|
16
|
-
|
|
17
|
-
// 读取已安装技能列表
|
|
18
|
-
const fs = require('fs');
|
|
19
|
-
const path = require('path');
|
|
20
|
-
const claudeDir = path.join(process.cwd(), '.claude');
|
|
21
|
-
let skills = [];
|
|
22
|
-
try {
|
|
23
|
-
const skillsDir = path.join(claudeDir, 'skills');
|
|
24
|
-
for (const pack of fs.readdirSync(skillsDir)) {
|
|
25
|
-
const packDir = path.join(skillsDir, pack);
|
|
26
|
-
if (!fs.statSync(packDir).isDirectory()) continue;
|
|
27
|
-
for (const skill of fs.readdirSync(packDir)) {
|
|
28
|
-
const skillFile = path.join(packDir, skill, 'SKILL.md');
|
|
29
|
-
if (fs.existsSync(skillFile)) skills.push(`${pack}/${skill}`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
} catch {}
|
|
33
|
-
|
|
34
|
-
if (skills.length === 0) process.exit(0);
|
|
35
|
-
|
|
36
|
-
// 输出强制激活指令(写入 stdout,Claude Code 会将其注入到上下文)
|
|
37
|
-
process.stdout.write(JSON.stringify({
|
|
38
|
-
type: 'assistant_hint',
|
|
39
|
-
content: `## 强制技能激活(必须执行)
|
|
40
|
-
|
|
41
|
-
可用技能:${skills.join(', ')}
|
|
42
|
-
|
|
43
|
-
步骤 1 - 评估:针对用户问题,列出匹配的技能名称
|
|
44
|
-
步骤 2 - 激活:逐个调用 Skill() 工具,每次调用后等待返回再调用下一个
|
|
45
|
-
步骤 3 - 实现:所有 Skill() 调用完成后,才能开始实现
|
|
46
|
-
|
|
47
|
-
⚠️ 禁止:跳过步骤 2 直接实现`
|
|
48
|
-
}));
|
|
File without changes
|
|
File without changes
|
|
File without changes
|