autosnippet 3.1.0 → 3.1.2

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.
@@ -5,12 +5,12 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>AutoSnippet Dashboard</title>
8
- <script type="module" crossorigin src="/assets/index-Bnm26ulL.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-D9fV5GGQ.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/yaml-qRaU8Ldn.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/vendor-CEnWn7aV.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/axios-C0Zqfgkc.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/icons-eQ_rWCus.js">
13
- <link rel="stylesheet" crossorigin href="/assets/index-D13KZZ6B.css">
13
+ <link rel="stylesheet" crossorigin href="/assets/index-C-eKKKJx.css">
14
14
  </head>
15
15
  <body>
16
16
  <div id="root"></div>
@@ -29,10 +29,15 @@ export class Relations {
29
29
  this._b = {};
30
30
  for (const k of RELATION_BUCKETS) {
31
31
  const vals = buckets[k] || [];
32
- this._b[k] = vals.map((r) => ({
33
- target: r.target || '',
34
- description: r.description || '',
35
- }));
32
+ this._b[k] = vals
33
+ .map((r) => {
34
+ // 兼容字符串数组:AI prompt 可能返回 ["recipeName"] 而非 [{target,description}]
35
+ if (typeof r === 'string') {
36
+ return r.trim() ? { target: r.trim(), description: '' } : null;
37
+ }
38
+ return { target: r.target || '', description: r.description || '' };
39
+ })
40
+ .filter(Boolean);
36
41
  }
37
42
  }
38
43
 
@@ -1087,6 +1087,29 @@ export async function fillDimensionsV3(fillContext) {
1087
1087
  const wikiContainer = getWikiContainer();
1088
1088
  const { WikiGenerator } = await import('../../../../../service/wiki/WikiGenerator.js');
1089
1089
 
1090
+ // 同步 wiki 路由的任务状态,让前端轮询 /wiki/status 能看到进度
1091
+ let patchWikiTask = null;
1092
+ let realtimeService = null;
1093
+ try {
1094
+ const wikiRoute = await import('../../../../../http/routes/wiki.js');
1095
+ patchWikiTask = wikiRoute.patchWikiTask;
1096
+ } catch { /* ok */ }
1097
+ try {
1098
+ realtimeService = wikiContainer.singletons?.realtimeService || null;
1099
+ } catch { /* ok */ }
1100
+
1101
+ // 标记任务开始
1102
+ patchWikiTask?.({
1103
+ status: 'running',
1104
+ startedAt: Date.now(),
1105
+ phase: null,
1106
+ progress: 0,
1107
+ message: 'Bootstrap Wiki 生成中...',
1108
+ finishedAt: null,
1109
+ result: null,
1110
+ error: null,
1111
+ });
1112
+
1090
1113
  let moduleService = null,
1091
1114
  knowledgeService = null,
1092
1115
  codeEntityGraph = null;
@@ -1113,6 +1136,18 @@ export async function fillDimensionsV3(fillContext) {
1113
1136
  projectGraph, // 来自 Step 0.5 构建的 ProjectGraph
1114
1137
  codeEntityGraph,
1115
1138
  aiProvider: wikiContainer.singletons?.aiProvider || null,
1139
+ onProgress: (phase, progress, message) => {
1140
+ // 同步到 wiki 路由的任务状态
1141
+ patchWikiTask?.({ phase, progress, message });
1142
+ // 通过 Socket.io 推送进度
1143
+ if (realtimeService) {
1144
+ try {
1145
+ realtimeService.broadcastEvent('wiki:progress', {
1146
+ phase, progress, message, timestamp: Date.now(),
1147
+ });
1148
+ } catch { /* non-critical */ }
1149
+ }
1150
+ },
1116
1151
  options: { language: process.env.ASD_WIKI_LANG || 'zh' },
1117
1152
  });
1118
1153
  const wikiResult = await wiki.generate();
@@ -1123,8 +1158,34 @@ export async function fillDimensionsV3(fillContext) {
1123
1158
  `Dedup removed: ${wikiResult.dedup?.removed?.length || 0}`
1124
1159
  );
1125
1160
  }
1161
+
1162
+ // 标记任务完成
1163
+ patchWikiTask?.({
1164
+ status: wikiResult.success ? 'done' : 'error',
1165
+ finishedAt: Date.now(),
1166
+ result: wikiResult,
1167
+ error: wikiResult.success ? null : (wikiResult.error || 'Unknown error'),
1168
+ progress: 100,
1169
+ });
1170
+ if (realtimeService) {
1171
+ try {
1172
+ realtimeService.broadcastEvent('wiki:completed', {
1173
+ success: wikiResult.success,
1174
+ filesGenerated: wikiResult.filesGenerated,
1175
+ duration: wikiResult.duration,
1176
+ });
1177
+ } catch { /* non-critical */ }
1178
+ }
1126
1179
  } catch (wikiErr) {
1127
1180
  logger.warn(`[Bootstrap-v3] Wiki generation failed (non-blocking): ${wikiErr.message}`);
1181
+ try {
1182
+ const wikiRoute = await import('../../../../../http/routes/wiki.js');
1183
+ wikiRoute.patchWikiTask?.({
1184
+ status: 'error',
1185
+ finishedAt: Date.now(),
1186
+ error: wikiErr.message,
1187
+ });
1188
+ } catch { /* ok */ }
1128
1189
  }
1129
1190
  }
1130
1191
 
@@ -171,9 +171,10 @@ ${refineInstruction}
171
171
 
172
172
  1. 只修改需要改进的字段,未涉及的必须原样返回。
173
173
  2. tags 采用合并策略(保留原有 + 补充新建议),不要删除已有标签。
174
- 3. relations 为 object 格式,key 为关系类型(如 inherits/implements/calls/depends_on/extends/related),value 为 string[]。
174
+ 3. relations 为 object 格式,key 为关系类型(如 inherits/implements/calls/depends_on/extends/related),value 为 Array<{target: string, description: string}>。示例: {"related": [{"target": "某 Recipe 标题", "description": "关联原因"}]}
175
175
  4. relations 只能指向已发布的 Recipe,不能在候选之间建立关联。如果没有已发布的 Recipe,relations 应保持为空 {}。
176
- 5. 每个 key 都必须存在,key 名称必须与上述完全一致。
176
+ 5. relations 必须精准:只在候选与某个 Recipe 有明确的技术依赖、继承、调用或扩展关系时才添加。仅仅因为属于同一项目或使用相同框架不构成关联。如果没有强关联,related 应为空数组。
177
+ 6. 每个 key 都必须存在,key 名称必须与上述完全一致。
177
178
 
178
179
  仅返回 JSON,不要添加任何其他文字或代码块标记。`;
179
180
 
@@ -285,8 +285,7 @@ ${userPrompt}
285
285
  2. **未涉及的字段必须原样返回**,不得做任何改写、改善、优化或翻译。
286
286
  3. 如果不确定用户指的是哪个字段,优先修改 description(摘要)、pattern(代码)、markdown(文档)、rationale(设计原理)。
287
287
  4. **翻译/语言转换类指令**(如“翻译为中文”): 翻译 description、pattern、markdown、rationale、aiInsight、agentNotes 等文本字段,但 tags/relations/confidence 保持原样。
288
- 5. **tags 和 relations** 只在用户明确提及“标签”或“关联”时才修改,其他情况一律原样返回。
289
-
288
+ 5. **tags 和 relations** 只在用户明确提及“标签”或“关联”时才修改,其他情况一律原样返回。6. **relations 格式**: object,key 为关系类型,value 为 Array<{target: string, description: string}>。示例: {"related": [{"target": "某 Recipe", "description": "原因"}]}。
290
289
  ## 输出格式
291
290
 
292
291
  返回严格符合以下结构的 JSON,不要添加任何其他文字或代码块标记:
@@ -54,6 +54,22 @@ function resetWikiTask() {
54
54
  currentGenerator = null;
55
55
  }
56
56
 
57
+ /**
58
+ * 外部读取 wikiTask 状态(供 bootstrap orchestrator 等外部流程同步使用)
59
+ * @returns {typeof wikiTask}
60
+ */
61
+ export function getWikiTask() {
62
+ return wikiTask;
63
+ }
64
+
65
+ /**
66
+ * 外部设置 wikiTask 状态(供 bootstrap orchestrator 等外部流程同步使用)
67
+ * @param {Partial<typeof wikiTask>} patch
68
+ */
69
+ export function patchWikiTask(patch) {
70
+ Object.assign(wikiTask, patch);
71
+ }
72
+
57
73
  /**
58
74
  * 创建 WikiGenerator 实例
59
75
  */
@@ -639,14 +639,19 @@ export class WikiGenerator {
639
639
  });
640
640
  }
641
641
 
642
- // ── 7. 文件夹画像文档 (AST 稀疏项目的降级策略) ──
643
- // AST 无法提取足够的 类/函数/协议 时,转为文件夹级分析
642
+ // ── 7. 文件夹画像文档 ──
643
+ // 触发条件 (满足任一即启用):
644
+ // a) AST 稀疏: 类/协议 < 5 且无模块文档
645
+ // b) generic monolith: 仅 generic discoverer + 单 target + 多目录
646
+ // c) 核心文章过少: 当前主题 ≤ 4 篇 → 用文件夹分析补充内容丰富度
644
647
  const astEntityCount = (astInfo.classes?.length || 0) + (astInfo.protocols?.length || 0);
645
648
  const hasModuleDocs = topics.some((t) => t.type === 'module');
646
649
  const astSparse = astEntityCount < 5 && !hasModuleDocs;
647
650
  const shouldProfileForGenericMonolith =
648
651
  genericOnlyDiscovery && monolithSingleTarget && sourceModuleKeys.length >= 2;
649
- const shouldEnableFolderProfiling = astSparse || shouldProfileForGenericMonolith;
652
+ const tooFewCoreArticles = topics.length <= 4 && sourceModuleKeys.length >= 2;
653
+ const shouldEnableFolderProfiling =
654
+ astSparse || shouldProfileForGenericMonolith || tooFewCoreArticles;
650
655
 
651
656
  if (shouldEnableFolderProfiling) {
652
657
  const rawFolderProfiles = profileFolders(projectInfo, {
@@ -712,7 +717,11 @@ export class WikiGenerator {
712
717
  folderDocCount++;
713
718
  }
714
719
 
715
- const folderProfileReason = astSparse ? 'AST sparse' : 'generic monolith';
720
+ const folderProfileReason = astSparse
721
+ ? 'AST sparse'
722
+ : tooFewCoreArticles
723
+ ? `few core articles (${topics.length - topics.filter((t) => t.type === 'folder-overview' || t.type === 'folder-profile').length} core)`
724
+ : 'generic monolith';
716
725
  logger.info(
717
726
  `[WikiGenerator] Folder profiling (${folderProfileReason}): ${folderProfiles.length} folders analyzed, ` +
718
727
  `${topics.filter((t) => t.type === 'folder-profile').length} folder docs planned`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autosnippet",
3
- "version": "3.1.0",
3
+ "version": "3.1.2",
4
4
  "description": "Extract code patterns into a knowledge base for AI coding assistants",
5
5
  "type": "module",
6
6
  "main": "lib/bootstrap.js",