autosnippet 1.5.3 → 1.5.5
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/README.md +26 -32
- package/bin/asnip.js +38 -7
- package/bin/ui.js +31 -0
- package/dashboard/dist/assets/index-Cn4bj_KW.js +44 -0
- package/dashboard/dist/index.html +1 -1
- package/lib/ai/AiFactory.js +14 -5
- package/lib/context/IndexingPipeline.js +15 -1
- package/lib/context/adapters/JsonAdapter.js +9 -1
- package/lib/context/adapters/LanceAdapter.js +28 -3
- package/lib/recipe/parseRecipeMd.js +1 -1
- package/lib/search/searchService.js +7 -4
- package/lib/watch/fileWatcher.js +22 -3
- package/package.json +1 -1
- package/bin/native-ui +0 -0
- package/dashboard/dist/assets/index-ClGNFmFz.js +0 -44
package/README.md
CHANGED
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
|
|
12
12
|
| 角色 | 职责 | 能力 |
|
|
13
13
|
|------|------|------|
|
|
14
|
-
| **开发者** | 审核与决策;维护项目标准 |
|
|
15
|
-
| **Cursor Agent** |
|
|
16
|
-
| **项目内 AI** | 提取、摘要、扫描、审查 | `asd ais` 扫描;Use Copied Code 分析填充;Guard 审查;Dashboard RAG
|
|
17
|
-
| **知识库** | 存储与提供项目标准 | Recipes、Snippets、语义向量索引;Guard
|
|
14
|
+
| **开发者** | 审核与决策;维护项目标准 | Dashboard 审核 Candidate,保存 Recipe;使用 Snippet 补全、`// as:search` 插入;运行 `asd embed`、`asd ui` |
|
|
15
|
+
| **Cursor Agent** | 按规范生成代码;检索与提交 | Skills 理解规范;MCP 按需检索、打开新建 Recipe 页;`autosnippet_submit_candidates` 批量提交候选供人工审核;不直接改 Knowledge |
|
|
16
|
+
| **项目内 AI** | 提取、摘要、扫描、审查 | `asd ais` 扫描;Use Copied Code 分析填充;Guard 审查;Dashboard RAG;深度扫描结果可算相似度。由 `.env` 配置 |
|
|
17
|
+
| **知识库** | 存储与提供项目标准 | Recipes、Snippets、语义向量索引;Guard、搜索、质量评估、相似度分析;两种 AI 的上下文均依赖此 |
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
@@ -44,7 +44,7 @@ asd ui # 启动 Dashboard(建议常驻)
|
|
|
44
44
|
|
|
45
45
|
### 闭环
|
|
46
46
|
|
|
47
|
-
**扫描 → 审核 → 沉淀 → Cursor/AI 使用 → 再沉淀**:项目 AI 通过扫描
|
|
47
|
+
**扫描 → 审核 → 沉淀 → Cursor/AI 使用 → 再沉淀**:项目 AI 通过扫描 Target 批量提交候选,Cursor 完成的代码通过 Skill 提交候选,开发者完成的代码通过剪切板提交候选,Dashboard 中的候选经过人工审核进入知识库;知识库内 Recipe 为第一公民,拥有最高优先级。
|
|
48
48
|
|
|
49
49
|
开发者通过 Snippet 获取 Recipe 内容插入编辑器, Cursor 通过 Skills 把 Recipe 产生的 context 当做上下文使用,对向量库进行查询;AI 用知识库产生的代码,过审后添加到知识库,成为了 AI 新的上下文,使得 AI 的开发趋于标准化。
|
|
50
50
|
|
|
@@ -57,7 +57,7 @@ asd ui # 启动 Dashboard(建议常驻)
|
|
|
57
57
|
| 指令 | 作用 |
|
|
58
58
|
|------|------|
|
|
59
59
|
| `// as:create` / `// as:c` | 无选项时只打开 Dashboard(路径已填),由用户点 Scan File 或 Use Copied Code。`-c` 强制用剪切板(静默创建或打开);`-f` 强制用路径(打开 Dashboard 并自动执行 Scan File) |
|
|
60
|
-
| `// as:guard` / `// as:g` [关键词或规模] | 按知识库 AI 审查;无后缀时仅检查当前文件;后缀 **file** / **target** / **project** 可扩大范围(target=当前
|
|
60
|
+
| `// as:guard` / `// as:g` [关键词或规模] | 按知识库 AI 审查;无后缀时仅检查当前文件;后缀 **file** / **target** / **project** 可扩大范围(target=当前 Target 内所有源文件,project=项目内所有源文件);其他为检索关键词 |
|
|
61
61
|
| `// as:search` / `// as:s` [关键词] | 从知识库检索并插入 Recipe/Snippet |
|
|
62
62
|
| `// as:include` / `// as:import` | Snippet 内头文件/模块标记,保存时自动注入 |
|
|
63
63
|
|
|
@@ -68,15 +68,16 @@ asd ui # 启动 Dashboard(建议常驻)
|
|
|
68
68
|
|
|
69
69
|
| 命令 | 说明 |
|
|
70
70
|
|------|------|
|
|
71
|
+
| `asd setup` | 初始化项目根(创建 AutoSnippetRoot.boxspec.json) |
|
|
71
72
|
| `asd ui` | 启动 Dashboard + watch |
|
|
72
|
-
| `asd status` |
|
|
73
|
+
| `asd status` | 环境自检(含项目根、AI、索引、Dashboard/Watch、Native UI) |
|
|
73
74
|
| `asd create --clipboard` | 从剪贴板创建 Recipe/Snippet |
|
|
74
75
|
| `asd candidate` | 从剪贴板创建候选(Dashboard 审核) |
|
|
75
76
|
| `asd install` / `asd i` | 同步 Snippets 到 Xcode |
|
|
76
77
|
| `asd ais [Target]` | AI 扫描 Target → Candidates |
|
|
77
78
|
| `asd search [keyword] --copy` | 搜索并复制第一条到剪贴板 |
|
|
78
79
|
| `asd search [keyword] --pick` | 交互选择后复制/插入 |
|
|
79
|
-
| `asd install:cursor-skill --mcp` | 安装 Skills、Cursor 规则(`.cursor/rules/*.mdc`)并配置 MCP
|
|
80
|
+
| `asd install:cursor-skill --mcp` | 安装 Skills、Cursor 规则(`.cursor/rules/*.mdc`)并配置 MCP。配置时可运行;MCP 工具使用时需 `asd ui` 已启动 |
|
|
80
81
|
| `asd install:full` | 全量安装;`--parser` 含 Swift 解析器;`--lancedb` 仅 LanceDB |
|
|
81
82
|
| `asd embed` | 手动构建语义向量索引(`asd ui` 启动时也会自动执行) |
|
|
82
83
|
| `asd spm-map` | 刷新 SPM 依赖映射(依赖关系图数据来源) |
|
|
@@ -85,7 +86,7 @@ asd ui # 启动 Dashboard(建议常驻)
|
|
|
85
86
|
|
|
86
87
|
除 `asd ais [Target]`(项目内 AI)外,可用 **Cursor 作为批量扫描工具**:在 Cursor 里让 Agent 通过 **MCP 工具**(`autosnippet_get_targets` → `autosnippet_get_target_files` → 按文件提取 → `autosnippet_submit_candidates`)扫描指定 Target,用 Cursor 模型提取候选并提交到 Dashboard,再到 **Candidates** 页审核入库。
|
|
87
88
|
|
|
88
|
-
简单一句:「扫描 BDNetwork
|
|
89
|
+
简单一句:「扫描 BDNetwork ,生成 Recipes 到候选」。AutoSnippet 将所有能力都通过语义交给 Cursor 了。
|
|
89
90
|
|
|
90
91
|
## 全量安装与可选依赖
|
|
91
92
|
|
|
@@ -99,36 +100,29 @@ asd install:full --lancedb # 仅安装 LanceDB(向量检索更快)
|
|
|
99
100
|
|
|
100
101
|
**Swift 解析器**:默认回退 `dump-package`;`--parser` 构建 ParsePackage 后 SPM 解析更准确,需本机已装 Swift。
|
|
101
102
|
|
|
102
|
-
### Postinstall 脚本说明(npm install 时)
|
|
103
|
-
|
|
104
|
-
`npm install` 会执行以下**可选**脚本,均为本包内本地构建,**不访问网络、不执行动态代码**:
|
|
105
|
-
|
|
106
|
-
| 脚本 | 作用 |
|
|
107
|
-
|------|------|
|
|
108
|
-
| `scripts/ensure-parse-package.js` | 仅当 `ASD_BUILD_SWIFT_PARSER=1` 时构建 Swift 解析器并打印「正在安装…」;否则打印跳过说明并退出。 |
|
|
109
|
-
| `scripts/build-native-ui.js` | 仅在 macOS 上用本机 Swift 编译 `resources/native-ui/main.swift` → `resources/native-ui/native-ui`;失败则静默跳过。 |
|
|
110
|
-
|
|
111
|
-
未安装或跳过不影响核心功能。详见 [npm lifecycle scripts](https://docs.npmjs.com/cli/v10/using-npm/scripts#life-cycle-scripts)。
|
|
112
|
-
|
|
113
103
|
## 配置
|
|
114
104
|
|
|
115
|
-
- **AI**:项目根 `.env
|
|
105
|
+
- **AI**:项目根 `.env`,参考 `.env.example` 配置 `ASD_GOOGLE_API_KEY` 等。可选 `ASD_AI_PROVIDER`、代理等。
|
|
116
106
|
- **LanceDB**:`asd install:full --lancedb`,在 boxspec 的 `context.storage.adapter` 中配置 `"lance"`。
|
|
117
107
|
- **Native UI**(可选):macOS 上 `npm install` 会尝试构建 `resources/native-ui/native-ui`(需本机 Swift);未构建时回退到 AppleScript/inquirer,功能正常。
|
|
118
108
|
|
|
119
|
-
## Recipe 格式
|
|
120
|
-
|
|
121
|
-
完整 Recipe 为 Markdown 文件,需包含:
|
|
122
|
-
|
|
123
|
-
- **Frontmatter**(`---` 包裹的 YAML):`title`、`trigger` 必填;可选 `category`、`language`、`headers` 等
|
|
124
|
-
- **Snippet / Code Reference**:下接代码块,供 Snippet 与检索使用
|
|
125
|
-
- **AI Context / Usage Guide**:使用说明,供 AI 与 Guard 检索
|
|
126
|
-
|
|
127
109
|
## 术语
|
|
128
110
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
111
|
+
| 术语 | 说明 |
|
|
112
|
+
|------|------|
|
|
113
|
+
| **Recipe** | `Knowledge/recipes/` 下的 Markdown 知识(配方):含代码块 + 使用说明,供 AI 检索、Guard、搜索 |
|
|
114
|
+
| **Snippet** | Xcode 代码片段,通过 trigger(默认 `@`)补全,可与 Recipe 关联 |
|
|
115
|
+
| **Candidate(候选)** | 待审核入库的项;来自 `as:create`、MCP 提交、`asd ais` 扫描等,经 Dashboard 审核后保存为 Recipe/Snippet |
|
|
116
|
+
| **Knowledge** | 项目知识库目录,包含 `recipes/`、`.autosnippet/`(索引、candidates、guard 配置等);Snippet 配置在 root spec 的 list 中 |
|
|
117
|
+
| **Dashboard** | Web 管理后台(`asd ui` 启动),含 Recipes、Candidates、Guard、Snippets 等页面 |
|
|
118
|
+
| **watch** | 文件监听进程(`asd ui` 或 `asd watch` 启动),保存时触发 `as:create`、`as:guard`、`as:search` |
|
|
119
|
+
| **Guard** | 按 Recipe 知识库对代码做 AI 审查;`// as:guard` 触发 |
|
|
120
|
+
| **embed** | 语义向量索引构建;`asd embed` 或 `asd ui` 启动时自动执行,供语义检索与 MCP 使用 |
|
|
121
|
+
| **MCP** | Model Context Protocol;Cursor 通过 MCP 调用 `autosnippet_context_search` 等工具 |
|
|
122
|
+
| **Skills** | Cursor Agent Skills(`.cursor/skills/`),描述何时用、如何用 AutoSnippet 能力 |
|
|
123
|
+
| **trigger** | Snippet 触发前缀,默认 `@`,输入后 Xcode 联想补全 |
|
|
124
|
+
| **项目根** | 含 `AutoSnippetRoot.boxspec.json` 的目录 |
|
|
125
|
+
| **Target** | SPM 模块/编译单元;`asd ais <Target>` 扫描该 Target 下的源码提取候选 |
|
|
132
126
|
|
|
133
127
|
**详细介绍**:启动 `asd ui` 后访问 Dashboard → **使用说明** 页;或参阅 [使用文档](docs/使用文档.md)(含 Skills 一览、AI 配置、闭环详解等)。
|
|
134
128
|
|
package/bin/asnip.js
CHANGED
|
@@ -1012,7 +1012,7 @@ commander
|
|
|
1012
1012
|
try {
|
|
1013
1013
|
const AiFactory = require('../lib/ai/AiFactory');
|
|
1014
1014
|
const config = AiFactory.getConfigSync(projectRoot);
|
|
1015
|
-
const hasKey =
|
|
1015
|
+
const hasKey = config.hasKey;
|
|
1016
1016
|
console.log(` ${hasKey ? ok : fail} AI 配置: ${hasKey ? `provider=${config.provider}` : '未配置 API Key'}`);
|
|
1017
1017
|
} catch (_) {
|
|
1018
1018
|
console.log(` ${fail} AI 配置: 无法读取`);
|
|
@@ -1021,13 +1021,17 @@ commander
|
|
|
1021
1021
|
console.log(`${fail} .env: 不存在,请从 .env.example 复制并填写 API Key`);
|
|
1022
1022
|
}
|
|
1023
1023
|
|
|
1024
|
-
// 3.
|
|
1025
|
-
const
|
|
1026
|
-
const
|
|
1027
|
-
|
|
1024
|
+
// 3. 语义索引(JsonAdapter: context/index/vector_index.json,LanceDB: context/index/lancedb/,manifest 由 embed 写入)
|
|
1025
|
+
const paths = require('../lib/infra/paths');
|
|
1026
|
+
const indexPath = paths.getContextIndexPath(projectRoot);
|
|
1027
|
+
const manifestPath = path.join(paths.getContextStoragePath(projectRoot), 'manifest.json');
|
|
1028
|
+
const hasContext = fs.existsSync(path.join(indexPath, 'vector_index.json')) ||
|
|
1029
|
+
fs.existsSync(path.join(indexPath, 'lancedb')) ||
|
|
1030
|
+
fs.existsSync(manifestPath);
|
|
1028
1031
|
console.log(`${hasContext ? ok : fail} 语义索引: ${hasContext ? '已构建' : '未构建,运行 asd embed'}`);
|
|
1029
1032
|
|
|
1030
1033
|
// 4. watch / ui
|
|
1034
|
+
let uiRunning = false;
|
|
1031
1035
|
try {
|
|
1032
1036
|
const net = require('net');
|
|
1033
1037
|
const port = 3000;
|
|
@@ -1035,8 +1039,12 @@ commander
|
|
|
1035
1039
|
const s = net.connect(port, '127.0.0.1', () => { s.destroy(); res(true); });
|
|
1036
1040
|
s.on('error', () => res(false));
|
|
1037
1041
|
});
|
|
1038
|
-
|
|
1039
|
-
|
|
1042
|
+
uiRunning = await Promise.race([check(), new Promise(r => setTimeout(() => r(false), 500))]);
|
|
1043
|
+
const uiIcon = uiRunning ? ok : 'ℹ️';
|
|
1044
|
+
console.log(`${uiIcon} Dashboard/Watch: ${uiRunning ? 'http://localhost:3000 已运行' : '未运行,需时请执行 asd ui'}`);
|
|
1045
|
+
if (!uiRunning) {
|
|
1046
|
+
console.log(` as:create、as:guard、as:search 依赖 watch,需时请执行: asd ui`);
|
|
1047
|
+
}
|
|
1040
1048
|
} catch (_) {
|
|
1041
1049
|
console.log(`${fail} Dashboard: 无法检测`);
|
|
1042
1050
|
}
|
|
@@ -1046,6 +1054,23 @@ commander
|
|
|
1046
1054
|
const hasNative = !!nativeUi.getNativeUiPath();
|
|
1047
1055
|
console.log(`${hasNative ? ok : fail} Native UI: ${hasNative ? '已就绪 (Swift Helper)' : '未构建,执行 npm run build:native-ui (macOS)'}`);
|
|
1048
1056
|
|
|
1057
|
+
// 6. 下一步建议
|
|
1058
|
+
console.log('\n--- 下一步建议 ---');
|
|
1059
|
+
const suggestions = [];
|
|
1060
|
+
if (!projectRoot) {
|
|
1061
|
+
suggestions.push('asd root 或 asd setup — 初始化项目根');
|
|
1062
|
+
} else {
|
|
1063
|
+
if (!uiRunning) suggestions.push('asd ui — 启动 Dashboard 与 watch(编辑器内指令才能生效)');
|
|
1064
|
+
if (!hasContext) suggestions.push('asd embed — 构建语义索引(as:search 语义检索、MCP 需要)');
|
|
1065
|
+
if (!fs.existsSync(envPath)) suggestions.push('复制 .env.example 为 .env 并填写 API Key(AI 功能需要)');
|
|
1066
|
+
const cursorSkillDir = path.join(projectRoot, '.cursor', 'skills', 'autosnippet-recipes');
|
|
1067
|
+
if (!fs.existsSync(cursorSkillDir)) suggestions.push('asd install:cursor-skill --mcp — 安装 Cursor Skills 与 MCP');
|
|
1068
|
+
}
|
|
1069
|
+
if (suggestions.length > 0) {
|
|
1070
|
+
suggestions.forEach((s, i) => console.log(` ${i + 1}. ${s}`));
|
|
1071
|
+
} else {
|
|
1072
|
+
console.log(' 环境就绪,可以正常使用。');
|
|
1073
|
+
}
|
|
1049
1074
|
console.log('');
|
|
1050
1075
|
});
|
|
1051
1076
|
|
|
@@ -1063,6 +1088,12 @@ commander
|
|
|
1063
1088
|
if (res.map && res.map.ok && res.map.created) {
|
|
1064
1089
|
console.log(`✅ 已创建 SPM 映射文件: ${res.map.path}`);
|
|
1065
1090
|
}
|
|
1091
|
+
console.log('\n下一步建议:');
|
|
1092
|
+
console.log(' 1. asd ui — 启动 Dashboard 与 watch(编辑器内 as:create、as:search、as:guard 需要)');
|
|
1093
|
+
console.log(' 2. 复制 .env.example 为 .env 并填写 API Key(AI 功能需要)');
|
|
1094
|
+
console.log(' 3. asd embed — 构建语义索引');
|
|
1095
|
+
console.log(' 4. asd install:cursor-skill --mcp — 安装 Cursor Skills 与 MCP');
|
|
1096
|
+
console.log('');
|
|
1066
1097
|
});
|
|
1067
1098
|
|
|
1068
1099
|
commander
|
package/bin/ui.js
CHANGED
|
@@ -545,6 +545,37 @@ function launch(projectRoot, port = 3000, options = {}) {
|
|
|
545
545
|
}
|
|
546
546
|
});
|
|
547
547
|
|
|
548
|
+
// API: AI 翻译(中文 → 英文,用于 Recipe summary/usageGuide)
|
|
549
|
+
app.post('/api/ai/translate', async (req, res) => {
|
|
550
|
+
try {
|
|
551
|
+
const { summary, usageGuide } = req.body;
|
|
552
|
+
if (!summary && !usageGuide) {
|
|
553
|
+
return res.json({ summary_en: '', usageGuide_en: '' });
|
|
554
|
+
}
|
|
555
|
+
const ai = await AiFactory.getProvider(projectRoot);
|
|
556
|
+
const sys = 'You are a technical translator. Translate the following from Chinese to English. Keep technical terms (e.g. API names, class names) unchanged. Return ONLY valid JSON: { "summary_en": "...", "usageGuide_en": "..." }. Use empty string for missing input. Preserve Markdown in usageGuide.';
|
|
557
|
+
const parts = [];
|
|
558
|
+
if (summary) parts.push(`summary (摘要):\n${summary}`);
|
|
559
|
+
if (usageGuide) parts.push(`usageGuide (使用指南):\n${usageGuide}`);
|
|
560
|
+
const prompt = parts.join('\n\n');
|
|
561
|
+
const text = await ai.chat(prompt, [], sys);
|
|
562
|
+
const raw = (text || '').replace(/```json?\s*/gi, '').replace(/```\s*$/g, '').trim();
|
|
563
|
+
let out = { summary_en: '', usageGuide_en: '' };
|
|
564
|
+
try {
|
|
565
|
+
const parsed = JSON.parse(raw);
|
|
566
|
+
if (parsed.summary_en != null) out.summary_en = String(parsed.summary_en);
|
|
567
|
+
if (parsed.usageGuide_en != null) out.usageGuide_en = String(parsed.usageGuide_en);
|
|
568
|
+
} catch (_) {
|
|
569
|
+
// 若解析失败,尝试提取第一段作为 summary_en
|
|
570
|
+
if (summary) out.summary_en = raw.split('\n')[0] || summary;
|
|
571
|
+
}
|
|
572
|
+
res.json(out);
|
|
573
|
+
} catch (err) {
|
|
574
|
+
console.error(`[API Error]`, err);
|
|
575
|
+
res.status(500).json({ error: err.message });
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
|
|
548
579
|
// API: AI 聊天
|
|
549
580
|
app.post('/api/ai/chat', async (req, res) => {
|
|
550
581
|
try {
|