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 CHANGED
@@ -11,10 +11,10 @@
11
11
 
12
12
  | 角色 | 职责 | 能力 |
13
13
  |------|------|------|
14
- | **开发者** | 审核与决策;维护项目标准 | Dashboard 审核 Candidate、保存 Recipe;使用 Snippet 补全、`// as:search` 插入;运行 `asd embed`、`asd ui` |
15
- | **Cursor Agent** | 按规范生成代码;检索知识库 | 通过 Skills 理解规范;MCP 按需检索、打开新建 Recipe 页;起草内容供人工审核,不直接改 Knowledge |
16
- | **项目内 AI** | 提取、摘要、扫描、审查 | `asd ais` 扫描;Use Copied Code 分析填充;Guard 审查;Dashboard RAG。由 `.env` 配置 |
17
- | **知识库** | 存储与提供项目标准 | Recipes、Snippets、语义向量索引;Guard、搜索、两种 AI 的上下文均依赖此 |
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 通过扫描 target 批量提交候选,Cursor 完成的代码通过 Skill 提交候选,开发者完成的代码通过剪切板提交候选,Dashboard 中的候选经过人工审核进入知识库;知识库内 Recipe 为第一公民,拥有最高优先级。
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=当前 target 内所有源文件,project=项目内所有源文件);其他为检索关键词 |
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` | 环境自检(项目根、AI、索引、Native UI) |
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(需 `asd ui` 运行) |
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 ,生产 Recipes 到候选」。AutoSnippet 将所有能力都通过语义交给 Cursor 了。
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`,设置 `ASD_GOOGLE_API_KEY` 等(见 `.env.example`)。可选 `ASD_AI_PROVIDER`、代理等。
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
- - **Recipe**:`Knowledge/recipes/` 下的 Markdown 知识,供 AI 检索、Guard、搜索
130
- - **Snippet**:Xcode 代码片段,通过 trigger(默认 `@`)补全
131
- - **项目根**:含 `AutoSnippetRoot.boxspec.json` 的目录
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 = !!(config.apiKey || config.googleApiKey);
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 contextDir = path.join(projectRoot, 'Knowledge', '.autosnippet');
1026
- const hasContext = fs.existsSync(path.join(contextDir, 'context.json')) ||
1027
- fs.existsSync(path.join(contextDir, 'embeddings'));
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
- const uiRunning = await Promise.race([check(), new Promise(r => setTimeout(() => r(false), 500))]);
1039
- console.log(`${uiRunning ? ok : fail} Dashboard: ${uiRunning ? 'http://localhost:3000 已运行' : '未运行,执行 asd ui'}`);
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 {