speccore 8.3.285 → 8.3.295
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/.agents/skills/spec-run/SKILL.md +16 -0
- package/.agents/skills/spec-task-create/SKILL.md +15 -0
- package/.agents/skills/spec-unitize/SKILL.md +16 -0
- package/README.en.md +16 -3
- package/README.md +23 -8
- package/dist/cli.js +59 -12
- package/dist/cli.js.map +1 -1
- package/dist/commands/about.d.ts.map +1 -1
- package/dist/commands/about.js +5 -0
- package/dist/commands/about.js.map +1 -1
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +77 -5
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/ask.js +2 -2
- package/dist/commands/ask.js.map +1 -1
- package/dist/commands/code-index.d.ts.map +1 -1
- package/dist/commands/code-index.js +20 -9
- package/dist/commands/code-index.js.map +1 -1
- package/dist/commands/doc2spec.js +32 -60
- package/dist/commands/doc2spec.js.map +1 -1
- package/dist/commands/execute.d.ts +1 -0
- package/dist/commands/execute.d.ts.map +1 -1
- package/dist/commands/execute.js +15 -1
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/help.js +14 -5
- package/dist/commands/help.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +14 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/iteration/taskify.d.ts.map +1 -1
- package/dist/commands/iteration/taskify.js +132 -7
- package/dist/commands/iteration/taskify.js.map +1 -1
- package/dist/commands/run.d.ts +1 -0
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +193 -25
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/status-panel.d.ts +1 -0
- package/dist/commands/status-panel.d.ts.map +1 -1
- package/dist/commands/status-panel.js +180 -54
- package/dist/commands/status-panel.js.map +1 -1
- package/dist/commands/task/new.d.ts +16 -0
- package/dist/commands/task/new.d.ts.map +1 -1
- package/dist/commands/task/new.js +232 -14
- package/dist/commands/task/new.js.map +1 -1
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +78 -1
- package/dist/commands/verify.js.map +1 -1
- package/dist/core/analyze-engine.d.ts.map +1 -1
- package/dist/core/analyze-engine.js +6 -10
- package/dist/core/analyze-engine.js.map +1 -1
- package/dist/core/ask-llm.d.ts +7 -0
- package/dist/core/ask-llm.d.ts.map +1 -1
- package/dist/core/ask-llm.js +45 -0
- package/dist/core/ask-llm.js.map +1 -1
- package/dist/core/code-graph/incremental-build.d.ts +2 -0
- package/dist/core/code-graph/incremental-build.d.ts.map +1 -1
- package/dist/core/code-graph/incremental-build.js +6 -5
- package/dist/core/code-graph/incremental-build.js.map +1 -1
- package/dist/core/code-graph/index.d.ts +2 -0
- package/dist/core/code-graph/index.d.ts.map +1 -1
- package/dist/core/code-graph/index.js +12 -5
- package/dist/core/code-graph/index.js.map +1 -1
- package/dist/core/code-scanner.d.ts +19 -4
- package/dist/core/code-scanner.d.ts.map +1 -1
- package/dist/core/code-scanner.js +193 -77
- package/dist/core/code-scanner.js.map +1 -1
- package/dist/core/excel-extract.d.ts +16 -2
- package/dist/core/excel-extract.d.ts.map +1 -1
- package/dist/core/excel-extract.js +54 -12
- package/dist/core/excel-extract.js.map +1 -1
- package/dist/core/git-integration.d.ts +2 -1
- package/dist/core/git-integration.d.ts.map +1 -1
- package/dist/core/git-integration.js +38 -14
- package/dist/core/git-integration.js.map +1 -1
- package/dist/core/global-counters.d.ts +3 -0
- package/dist/core/global-counters.d.ts.map +1 -1
- package/dist/core/global-counters.js +21 -7
- package/dist/core/global-counters.js.map +1 -1
- package/dist/core/intent-recognition.d.ts.map +1 -1
- package/dist/core/intent-recognition.js +173 -27
- package/dist/core/intent-recognition.js.map +1 -1
- package/dist/core/plan-store.d.ts +1 -0
- package/dist/core/plan-store.d.ts.map +1 -1
- package/dist/core/plan-store.js.map +1 -1
- package/dist/core/prompt-builder.d.ts.map +1 -1
- package/dist/core/prompt-builder.js +168 -140
- package/dist/core/prompt-builder.js.map +1 -1
- package/dist/core/spec-paths.d.ts.map +1 -1
- package/dist/core/spec-paths.js +3 -0
- package/dist/core/spec-paths.js.map +1 -1
- package/dist/core/spec-skeleton.d.ts.map +1 -1
- package/dist/core/spec-skeleton.js +6 -2
- package/dist/core/spec-skeleton.js.map +1 -1
- package/dist/core/stage-aliases.d.ts +40 -0
- package/dist/core/stage-aliases.d.ts.map +1 -0
- package/dist/core/stage-aliases.js +110 -0
- package/dist/core/stage-aliases.js.map +1 -0
- package/dist/core/state.d.ts +11 -0
- package/dist/core/state.d.ts.map +1 -1
- package/dist/core/state.js +33 -2
- package/dist/core/state.js.map +1 -1
- package/dist/core/structured-extractor.d.ts.map +1 -1
- package/dist/core/structured-extractor.js +7 -0
- package/dist/core/structured-extractor.js.map +1 -1
- package/dist/core/ui-verify/index.d.ts +2 -2
- package/dist/core/ui-verify/index.d.ts.map +1 -1
- package/dist/core/ui-verify/index.js +3 -1
- package/dist/core/ui-verify/index.js.map +1 -1
- package/dist/core/ui-verify/smoke-engine.d.ts.map +1 -1
- package/dist/core/ui-verify/smoke-engine.js +36 -0
- package/dist/core/ui-verify/smoke-engine.js.map +1 -1
- package/dist/core/ui-verify/spec-generator.d.ts +32 -1
- package/dist/core/ui-verify/spec-generator.d.ts.map +1 -1
- package/dist/core/ui-verify/spec-generator.js +205 -21
- package/dist/core/ui-verify/spec-generator.js.map +1 -1
- package/dist/core/ui-verify/types.d.ts +2 -0
- package/dist/core/ui-verify/types.d.ts.map +1 -1
- package/dist/utils/synonyms.d.ts.map +1 -1
- package/dist/utils/synonyms.js +15 -0
- package/dist/utils/synonyms.js.map +1 -1
- package/package.json +3 -2
- package/templates/api-design-example.md +338 -0
- package/templates/ci/github-actions.yml +44 -0
- package/templates/database-example.md +223 -0
- package/templates/deploy-docker/README.md +284 -0
- package/templates/deploy-docker/deploy-remote.sh +79 -0
- package/templates/deploy-docker/docker-compose.yml +113 -0
- package/templates/deploy-docker/frontend.Dockerfile +43 -0
- package/templates/deploy-docker/nginx.conf +30 -0
- package/templates/deploy-examples.yaml +436 -0
- package/templates/deploy-java/Dockerfile +50 -0
- package/templates/deploy-java/README.md +212 -0
- package/templates/deploy-java/deploy.sh +120 -0
- package/templates/deploy-java/spring-boot.service +35 -0
- package/templates/html/speccore-ask-explain.html +10 -0
- package/templates/html/speccore-ask-guide.html +10 -0
- package/templates/html/speccore-ask-match.html +10 -0
- package/templates/html/speccore-ask-pipeline.html +1 -0
- package/templates/html/speccore-ask-result.html +10 -0
- package/templates/html/speccore-help.html +241 -0
- package/templates/html/speccore-setup-guide.html +289 -0
- package/templates/security-example.md +226 -0
- package/templates/verify-spec-example.yaml +138 -0
|
@@ -69,6 +69,7 @@ const content_delivery_1 = require("./content-delivery");
|
|
|
69
69
|
// v8.3.183+: HTML 原型渲染(Playwright 截图 + accessibility tree,替代纯文本)
|
|
70
70
|
const html_prototype_1 = require("./html-prototype");
|
|
71
71
|
// v8.3.122+: execute 时深入读取关联源码
|
|
72
|
+
// v8.3.3xx+: 消费前兜底建索引(ensureCodeIndexFresh)+ 迭代源码 scope 解析
|
|
72
73
|
const code_scanner_1 = require("./code-scanner");
|
|
73
74
|
// v8.3.125+: 同义词扩展,解决中英文/缩写关键词匹配失败
|
|
74
75
|
const synonyms_1 = require("../utils/synonyms");
|
|
@@ -1560,12 +1561,13 @@ function getInstruction(command, context) {
|
|
|
1560
1561
|
'## 前端实现要求(Layer 3)',
|
|
1561
1562
|
'如果任务涉及前端各端(admin/H5/小程序/App),还需:',
|
|
1562
1563
|
'1. 按 UI_SPEC.md 中的字段→UI 映射实现每个页面',
|
|
1563
|
-
'2.
|
|
1564
|
-
'3.
|
|
1565
|
-
'4.
|
|
1566
|
-
'5.
|
|
1567
|
-
'6.
|
|
1568
|
-
'7.
|
|
1564
|
+
'2. **按 UI_SPEC.md「元素标识约定」给组件添加 data-testid 属性**(命名遵循约定的 {模块}-{页面}-{元素} 格式,页面容器以 xxx-page 结尾)——冒烟测试的 VERIFY_SPEC 断言依赖这些属性定位元素,缺失会导致冒烟失败',
|
|
1565
|
+
'3. 按路由表创建页面组件和路由配置',
|
|
1566
|
+
'4. 实现状态枚举的前端展示(与后端数据模型一致)',
|
|
1567
|
+
'5. 实现表单校验规则(与后端校验规则一致)',
|
|
1568
|
+
'6. 实现页面四态:加载中/正常/空态/错误态',
|
|
1569
|
+
'7. 实现响应式适配(按 UI_SPEC.md 中的断点和布局策略)',
|
|
1570
|
+
'8. 前端组件必须与后端 API 响应字段一一对应,不能硬编码',
|
|
1569
1571
|
'',
|
|
1570
1572
|
'## 🤖 自动模式指令',
|
|
1571
1573
|
'',
|
|
@@ -1682,23 +1684,64 @@ async function buildPrompt(command, options) {
|
|
|
1682
1684
|
try {
|
|
1683
1685
|
const iterDir = await (0, context_1.getIterationDir)(options.iteration);
|
|
1684
1686
|
if (iterDir) {
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1687
|
+
// 优先级:010-requirements/INDEX.md → features/INDEX.md → 需求文档标题(跳过 README.md 规范说明)
|
|
1688
|
+
// 否则 README.md 的「目录规范」标题会污染查询词,导致源码匹配全空;
|
|
1689
|
+
// 泛化标题(功能单元索引/目录规范等)同样丢弃,回退迭代名(含业务语义)
|
|
1690
|
+
const reqIndexCandidates = [
|
|
1691
|
+
(0, path_1.join)(iterDir, '010-requirements', 'INDEX.md'),
|
|
1692
|
+
(0, path_1.join)(iterDir, '010-requirements', 'features', 'INDEX.md'),
|
|
1693
|
+
];
|
|
1694
|
+
const isGenericTitle = (t) => /(目录|索引|规范|说明|README|指南|总览|报告|审查|清单|质量)/i.test(t);
|
|
1695
|
+
let found = false;
|
|
1696
|
+
for (const idxPath of reqIndexCandidates) {
|
|
1697
|
+
if (await (0, fs_extra_1.pathExists)(idxPath)) {
|
|
1698
|
+
const idxContent = await (0, fs_extra_1.readFile)(idxPath, 'utf-8');
|
|
1699
|
+
const titleMatch = idxContent.match(/^#\s+(.+)$/m);
|
|
1700
|
+
if (titleMatch) {
|
|
1701
|
+
const t = titleMatch[1].trim();
|
|
1702
|
+
if (!isGenericTitle(t)) {
|
|
1703
|
+
searchQuery = t;
|
|
1704
|
+
found = true;
|
|
1705
|
+
break;
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1691
1709
|
}
|
|
1692
|
-
|
|
1693
|
-
//
|
|
1710
|
+
if (!found) {
|
|
1711
|
+
// 兜底:features/ 下第一个 SOURCE.md 的标题(业务语义最强,如「预订管理」)
|
|
1712
|
+
const featuresDir = (0, path_1.join)(iterDir, '010-requirements', 'features');
|
|
1713
|
+
const featureDirs = (await (0, fs_extra_1.readdir)(featuresDir, { withFileTypes: true }).catch(() => []))
|
|
1714
|
+
.filter((e) => e.isDirectory())
|
|
1715
|
+
.map((e) => e.name);
|
|
1716
|
+
for (const fd of featureDirs) {
|
|
1717
|
+
const sourcePath = (0, path_1.join)(featuresDir, fd, 'SOURCE.md');
|
|
1718
|
+
if (!(await (0, fs_extra_1.pathExists)(sourcePath)))
|
|
1719
|
+
continue;
|
|
1720
|
+
const sourceContent = await (0, fs_extra_1.readFile)(sourcePath, 'utf-8');
|
|
1721
|
+
const titleMatch = sourceContent.match(/^#\s+(.+)$/m);
|
|
1722
|
+
if (titleMatch) {
|
|
1723
|
+
const t = titleMatch[1].trim();
|
|
1724
|
+
if (!isGenericTitle(t)) {
|
|
1725
|
+
searchQuery = t;
|
|
1726
|
+
found = true;
|
|
1727
|
+
break;
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
if (!found) {
|
|
1733
|
+
// 尝试读取第一个需求文档(跳过 README.md 目录规范说明与报告类文档)
|
|
1694
1734
|
const reqDir = (0, path_1.join)(iterDir, '010-requirements');
|
|
1695
1735
|
const reqFiles = (await (0, fs_extra_1.readdir)(reqDir).catch(() => []))
|
|
1696
|
-
.filter(f => f.endsWith('.md') && !f.startsWith('.'));
|
|
1736
|
+
.filter(f => f.endsWith('.md') && !f.startsWith('.') && !/^(README|QUALITY_REPORT|CLARIFY_REPORT)$/i.test(f.replace(/\.md$/, '')));
|
|
1697
1737
|
if (reqFiles.length > 0) {
|
|
1698
1738
|
const firstReq = await (0, fs_extra_1.readFile)((0, path_1.join)(reqDir, reqFiles[0]), 'utf-8');
|
|
1699
1739
|
const titleMatch = firstReq.match(/^#\s+(.+)$/m);
|
|
1700
|
-
if (titleMatch)
|
|
1701
|
-
|
|
1740
|
+
if (titleMatch) {
|
|
1741
|
+
const t = titleMatch[1].trim();
|
|
1742
|
+
if (!isGenericTitle(t))
|
|
1743
|
+
searchQuery = t;
|
|
1744
|
+
}
|
|
1702
1745
|
}
|
|
1703
1746
|
}
|
|
1704
1747
|
}
|
|
@@ -1768,7 +1811,6 @@ async function buildPrompt(command, options) {
|
|
|
1768
1811
|
// 这是最高优先级的改进:用结构化卡片替代原始源码,Token 效率提升 10-20 倍
|
|
1769
1812
|
let structuredDataPath = (0, path_1.join)(cwd, '.speccore', 'cache', 'structured-data.json');
|
|
1770
1813
|
let structured = null;
|
|
1771
|
-
let structuredCardsInjected = false;
|
|
1772
1814
|
// 优先尝试平台分段文件
|
|
1773
1815
|
if (options.platform) {
|
|
1774
1816
|
const platformPath = (0, path_1.join)(cwd, '.speccore', 'cache', `structured-data.${options.platform}.json`);
|
|
@@ -1980,140 +2022,123 @@ async function buildPrompt(command, options) {
|
|
|
1980
2022
|
});
|
|
1981
2023
|
const cardChars = cardLines.join('\n').length;
|
|
1982
2024
|
logger_1.logger?.info?.(` 📇 结构化卡片: ${matchedApis.length} API + ${matchedEntities.length} Entity + ${matchedComponents.length} Component + ${matchedRoutes.length} Route + ${matchedDtos.length} DTO + ${matchedServices.length} Service (${Math.round(cardChars / 1000)}K 字符)`);
|
|
1983
|
-
structuredCardsInjected = true;
|
|
1984
2025
|
}
|
|
1985
2026
|
}
|
|
1986
2027
|
catch { /* 结构化数据解析失败不阻断 */ }
|
|
1987
2028
|
}
|
|
1988
|
-
|
|
2029
|
+
// v8.3.3xx+: 消费前兜底——索引缺失/过期时自动重建 + 无索引告警
|
|
2030
|
+
// 按迭代解析源码 scope,与 analyze-engine 消费路径保持一致;
|
|
2031
|
+
// 否则兜底构建会落到 DEFAULT_SCOPE(项目根 src/ 等),索引空壳、查询无结果
|
|
2032
|
+
const codeIndexScope = options.iteration
|
|
2033
|
+
? (await (0, code_scanner_1.resolveIterationScope)(options.iteration).catch(() => undefined))
|
|
2034
|
+
: undefined;
|
|
2035
|
+
await (0, code_scanner_1.ensureCodeIndexFresh)(codeIndexScope);
|
|
2036
|
+
const codeMatches = await (0, code_scanner_1.findRelevantCode)(searchQuery, 20, codeIndexScope, options.iteration, options.task);
|
|
1989
2037
|
if (codeMatches.length > 0) {
|
|
1990
|
-
// v8.3.
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
ctxLines.push(
|
|
2010
|
-
if (ctx.downstreamTasks.length > 0)
|
|
2011
|
-
ctxLines.push(`- **下游任务**(依赖本任务): ${ctx.downstreamTasks.map(d => d.title).join(', ')}`);
|
|
2012
|
-
if (ctx.relatedSpecs.length > 0) {
|
|
2013
|
-
ctxLines.push(`- **关联规格文档**:`);
|
|
2014
|
-
for (const spec of ctx.relatedSpecs) {
|
|
2015
|
-
ctxLines.push(` - ${spec.title}: \`${spec.file}\``);
|
|
2016
|
-
}
|
|
2017
|
-
}
|
|
2018
|
-
if (ctx.dependencyChain.length > 0) {
|
|
2019
|
-
ctxLines.push(`- **依赖链路**: ${ctx.dependencyChain.map(c => c.taskName).join(' → ')}`);
|
|
2038
|
+
// v8.3.122+: execute 时注入知识图谱任务上下文
|
|
2039
|
+
// v8.3.125+: 自动检查过期并刷新
|
|
2040
|
+
if (command === 'execute' && options.iteration && options.task) {
|
|
2041
|
+
try {
|
|
2042
|
+
const kg = await (0, knowledge_graph_1.loadFreshKnowledgeGraph)(cwd, options.iteration);
|
|
2043
|
+
if (kg) {
|
|
2044
|
+
const ctx = (0, knowledge_graph_1.getFullTaskContext)(kg, options.task);
|
|
2045
|
+
const ctxLines = ['## 🔗 任务关联上下文(知识图谱)', ''];
|
|
2046
|
+
if (ctx.requirement)
|
|
2047
|
+
ctxLines.push(`- **上游需求**: ${ctx.requirement.title} (${ctx.requirement.file})`);
|
|
2048
|
+
if (ctx.parentTask)
|
|
2049
|
+
ctxLines.push(`- **父任务**: ${ctx.parentTask.title}`);
|
|
2050
|
+
if (ctx.dependsOn.length > 0)
|
|
2051
|
+
ctxLines.push(`- **依赖任务**(需先完成): ${ctx.dependsOn.map(d => d.title).join(', ')}`);
|
|
2052
|
+
if (ctx.downstreamTasks.length > 0)
|
|
2053
|
+
ctxLines.push(`- **下游任务**(依赖本任务): ${ctx.downstreamTasks.map(d => d.title).join(', ')}`);
|
|
2054
|
+
if (ctx.relatedSpecs.length > 0) {
|
|
2055
|
+
ctxLines.push(`- **关联规格文档**:`);
|
|
2056
|
+
for (const spec of ctx.relatedSpecs) {
|
|
2057
|
+
ctxLines.push(` - ${spec.title}: \`${spec.file}\``);
|
|
2020
2058
|
}
|
|
2021
|
-
extraSpecs.push({
|
|
2022
|
-
name: '🔗 任务关联上下文',
|
|
2023
|
-
path: 'kg-context.md',
|
|
2024
|
-
content: ctxLines.join('\n'),
|
|
2025
|
-
});
|
|
2026
2059
|
}
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
}
|
|
2030
|
-
// v8.3.123+: analyze 时注入知识图谱迭代级上下文
|
|
2031
|
-
// v8.3.125+: 自动检查过期并刷新
|
|
2032
|
-
if (command === 'analyze' && options.iteration) {
|
|
2033
|
-
try {
|
|
2034
|
-
const kg = await (0, knowledge_graph_1.loadFreshKnowledgeGraph)(cwd, options.iteration);
|
|
2035
|
-
if (kg) {
|
|
2036
|
-
const compactCtx = (0, context_builder_1.buildCompactContext)(kg, {});
|
|
2037
|
-
if (compactCtx) {
|
|
2038
|
-
extraSpecs.push({
|
|
2039
|
-
name: '🧠 知识图谱摘要',
|
|
2040
|
-
path: 'kg-summary.md',
|
|
2041
|
-
content: compactCtx,
|
|
2042
|
-
});
|
|
2043
|
-
}
|
|
2060
|
+
if (ctx.dependencyChain.length > 0) {
|
|
2061
|
+
ctxLines.push(`- **依赖链路**: ${ctx.dependencyChain.map(c => c.taskName).join(' → ')}`);
|
|
2044
2062
|
}
|
|
2063
|
+
extraSpecs.push({
|
|
2064
|
+
name: '🔗 任务关联上下文',
|
|
2065
|
+
path: 'kg-context.md',
|
|
2066
|
+
content: ctxLines.join('\n'),
|
|
2067
|
+
});
|
|
2045
2068
|
}
|
|
2046
|
-
catch { /* 知识图谱加载失败不阻断 */ }
|
|
2047
2069
|
}
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
const
|
|
2055
|
-
if (
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
reasons.push(`关键词匹配 (score:${match.score})`);
|
|
2065
|
-
if (match.exports.length > 0)
|
|
2066
|
-
reasons.push(`导出: ${match.exports.join(', ')}`);
|
|
2067
|
-
if (match.apis.length > 0)
|
|
2068
|
-
reasons.push(`API: ${match.apis.join(', ')}`);
|
|
2069
|
-
extraSpecs.push({
|
|
2070
|
-
name: `🔍 ${(0, path_1.basename)(match.file)}`,
|
|
2071
|
-
path: match.file,
|
|
2072
|
-
content: `## 关联源码: ${match.file}\n> **匹配原因**: ${reasons.join(' | ')}\n\n\`\`\`${match.file.split('.').pop() || 'ts'}\n${content}\n\`\`\``,
|
|
2073
|
-
});
|
|
2074
|
-
}
|
|
2075
|
-
logger_1.logger?.info?.(` 💻 深入源码: ${sourceKeys.length} 个完整文件已注入 (${Math.round(Object.values(sourceContents).reduce((a, c) => a + c.length, 0) / 1000)}K 字符)`);
|
|
2076
|
-
// v8.3.125+: 可选阅读清单 — 让宿主 AI 按需深入读取未预加载的关联文件
|
|
2077
|
-
// 解决 "8文件/60KB 限制导致大型功能读不全" 的问题
|
|
2078
|
-
const unreadMatches = sortedMatches.slice(sourceMaxFiles);
|
|
2079
|
-
if (unreadMatches.length > 0) {
|
|
2080
|
-
const optionalLines = [
|
|
2081
|
-
'## 📖 可选阅读清单(宿主 AI 按需读取)',
|
|
2082
|
-
'> 以下文件与当前任务高度相关,但因 Token 预算限制未完整预加载到上下文中。',
|
|
2083
|
-
'> 如果你的 IDE(Cursor / Claude Code / Windsurf)支持文件读取,可按需打开以下文件深入理解业务逻辑:',
|
|
2084
|
-
'',
|
|
2085
|
-
];
|
|
2086
|
-
for (const match of unreadMatches.slice(0, 15)) {
|
|
2087
|
-
const reasons = [];
|
|
2088
|
-
if (match.score >= 50)
|
|
2089
|
-
reasons.push('@spec 注释直接关联');
|
|
2090
|
-
else if (match.score >= 40)
|
|
2091
|
-
reasons.push('知识图谱关联');
|
|
2092
|
-
else if (match.score >= 35)
|
|
2093
|
-
reasons.push('业务模块关联');
|
|
2094
|
-
else if (match.score >= 25)
|
|
2095
|
-
reasons.push('技术关键词匹配');
|
|
2096
|
-
else
|
|
2097
|
-
reasons.push('关键词匹配');
|
|
2098
|
-
if (match.exports.length > 0)
|
|
2099
|
-
reasons.push(`导出 ${match.exports.join(', ')}`);
|
|
2100
|
-
if (match.apis.length > 0)
|
|
2101
|
-
reasons.push(`API ${match.apis.join(', ')}`);
|
|
2102
|
-
optionalLines.push(`- \`${match.file}\` — ${reasons.join(' | ')} (score:${match.score})`);
|
|
2103
|
-
}
|
|
2104
|
-
if (unreadMatches.length > 15) {
|
|
2105
|
-
optionalLines.push(`\n... 还有 ${unreadMatches.length - 15} 个关联文件未列出`);
|
|
2070
|
+
catch { /* 知识图谱加载失败不阻断 */ }
|
|
2071
|
+
}
|
|
2072
|
+
// v8.3.123+: analyze 时注入知识图谱迭代级上下文
|
|
2073
|
+
// v8.3.125+: 自动检查过期并刷新
|
|
2074
|
+
if (command === 'analyze' && options.iteration) {
|
|
2075
|
+
try {
|
|
2076
|
+
const kg = await (0, knowledge_graph_1.loadFreshKnowledgeGraph)(cwd, options.iteration);
|
|
2077
|
+
if (kg) {
|
|
2078
|
+
const compactCtx = (0, context_builder_1.buildCompactContext)(kg, {});
|
|
2079
|
+
if (compactCtx) {
|
|
2080
|
+
extraSpecs.push({
|
|
2081
|
+
name: '🧠 知识图谱摘要',
|
|
2082
|
+
path: 'kg-summary.md',
|
|
2083
|
+
content: compactCtx,
|
|
2084
|
+
});
|
|
2085
|
+
}
|
|
2106
2086
|
}
|
|
2107
|
-
optionalLines.push('\n---');
|
|
2108
|
-
optionalLines.push('💡 **使用方式**: 如果上述预加载源码不足以理解实现细节,请打开对应文件查看完整代码。');
|
|
2109
|
-
extraSpecs.push({
|
|
2110
|
-
name: '📖 可选阅读清单',
|
|
2111
|
-
path: 'optional-reading.md',
|
|
2112
|
-
content: optionalLines.join('\n'),
|
|
2113
|
-
});
|
|
2114
|
-
logger_1.logger?.info?.(` 📖 可选阅读清单: ${unreadMatches.length} 个关联文件(宿主 AI 可按需读取)`);
|
|
2115
2087
|
}
|
|
2088
|
+
catch { /* 知识图谱加载失败不阻断 */ }
|
|
2089
|
+
}
|
|
2090
|
+
// v8.3.3xx+: 第三层——CLI 只导航不拼全文。
|
|
2091
|
+
// 注入「相关源码阅读清单 + 宿主 AI 按需深读协议」,
|
|
2092
|
+
// 全文阅读交给宿主 AI 的 Read / Grep / LSP 工具按需执行。
|
|
2093
|
+
const sortedMatches = [...codeMatches].sort((a, b) => b.score - a.score);
|
|
2094
|
+
const listLines = [
|
|
2095
|
+
'## 📖 相关源码阅读清单(CLI 导航 + 宿主 AI 按需深读)',
|
|
2096
|
+
'',
|
|
2097
|
+
'> 以下文件由代码索引相关性导航得出,按匹配得分降序排列。',
|
|
2098
|
+
'> **CLI 不拼入源码全文**——一个端动辄几百个文件,全量拼入会撑爆上下文或截断失真。',
|
|
2099
|
+
'> 请用你的 Read / Grep / LSP 工具按需打开深读。',
|
|
2100
|
+
'',
|
|
2101
|
+
'| 优先级 | 文件 | 匹配原因 | 代码线索 |',
|
|
2102
|
+
'|:---|:---|:---|:---|',
|
|
2103
|
+
];
|
|
2104
|
+
for (const match of sortedMatches.slice(0, 15)) {
|
|
2105
|
+
const reasons = [];
|
|
2106
|
+
if (match.score >= 50)
|
|
2107
|
+
reasons.push('@spec 注释直接关联');
|
|
2108
|
+
else if (match.score >= 40)
|
|
2109
|
+
reasons.push('知识图谱 source-file 实体关联');
|
|
2110
|
+
else if (match.score >= 35)
|
|
2111
|
+
reasons.push('业务模块 codeEntities 关联');
|
|
2112
|
+
else if (match.score >= 25)
|
|
2113
|
+
reasons.push('Spec 技术关键词匹配');
|
|
2114
|
+
else
|
|
2115
|
+
reasons.push('关键词匹配');
|
|
2116
|
+
const clues = [];
|
|
2117
|
+
if (match.exports.length > 0)
|
|
2118
|
+
clues.push(`导出 \`${match.exports.slice(0, 4).join(' ')}\``);
|
|
2119
|
+
if (match.apis.length > 0)
|
|
2120
|
+
clues.push(`API \`${match.apis.slice(0, 3).join(' ')}\``);
|
|
2121
|
+
const priority = match.score >= 40 ? 'P0' : (match.score >= 25 ? 'P1' : 'P2');
|
|
2122
|
+
listLines.push(`| ${priority} | \`${match.file}\` | ${reasons.join(' / ')} (score:${match.score}) | ${clues.join(';') || '—'} |`);
|
|
2123
|
+
}
|
|
2124
|
+
if (sortedMatches.length > 15) {
|
|
2125
|
+
listLines.push(`| … | 其余 ${sortedMatches.length - 15} 个关联文件 | 运行 \`speccore code-index --show\` 查看全量索引 | — |`);
|
|
2116
2126
|
}
|
|
2127
|
+
listLines.push('');
|
|
2128
|
+
listLines.push('### 🔍 深读协议(宿主 AI 执行)');
|
|
2129
|
+
listLines.push('');
|
|
2130
|
+
listLines.push('1. **先读 P0**:用 Read 打开优先级最高的 2-3 个文件,理解核心实现与数据结构');
|
|
2131
|
+
listLines.push('2. **符号追踪**:对清单中的导出符号用 LSP(goToDefinition / findReferences)追踪定义与调用点');
|
|
2132
|
+
listLines.push('3. **路径检索**:对 API 路径 / 表名用 Grep 全仓检索关联实现');
|
|
2133
|
+
listLines.push('4. **按需扩展**:从已读文件的 import 出发追踪依赖链;任务涉及哪个文件,就深读哪个文件');
|
|
2134
|
+
listLines.push('5. **禁止要求拼全文**:不要要求 CLI 把源码全文塞进 prompt——CLI 的职责是导航,全文阅读由你按需执行');
|
|
2135
|
+
listLines.push('');
|
|
2136
|
+
extraSpecs.push({
|
|
2137
|
+
name: '📖 相关源码阅读清单',
|
|
2138
|
+
path: 'reading-list.md',
|
|
2139
|
+
content: listLines.join('\n'),
|
|
2140
|
+
});
|
|
2141
|
+
logger_1.logger?.info?.(` 📖 相关源码阅读清单: ${sortedMatches.length} 个关联文件(CLI 导航,宿主 AI 按需深读)`);
|
|
2117
2142
|
}
|
|
2118
2143
|
}
|
|
2119
2144
|
catch (e) {
|
|
@@ -2214,6 +2239,9 @@ async function buildPrompt(command, options) {
|
|
|
2214
2239
|
feature: options.analyzeOptions.feature,
|
|
2215
2240
|
};
|
|
2216
2241
|
const rawPrompt = await buildMultiDocPrompt('analyze', ctx, options.analyzeOptions);
|
|
2242
|
+
// v8.3.3xx+: rawPrompt 路由会丢弃常规 extraSpecs,阅读清单(CLI 导航 + 宿主 AI 深读)单独追加
|
|
2243
|
+
const readingList = extraSpecs.find(s => s.name === '📖 相关源码阅读清单');
|
|
2244
|
+
const finalRawPrompt = readingList ? `${rawPrompt}\n\n${readingList.content}` : rawPrompt;
|
|
2217
2245
|
return {
|
|
2218
2246
|
marker: '[SPECCORE_PROMPT]',
|
|
2219
2247
|
version: '1.0',
|
|
@@ -2228,7 +2256,7 @@ async function buildPrompt(command, options) {
|
|
|
2228
2256
|
extraSpecs: [],
|
|
2229
2257
|
instruction: '',
|
|
2230
2258
|
outputHint: '请返回 Markdown 格式的分析结果',
|
|
2231
|
-
rawPrompt,
|
|
2259
|
+
rawPrompt: finalRawPrompt,
|
|
2232
2260
|
subagent: options.subagent || DEFAULT_SUBAGENT_MAP[command],
|
|
2233
2261
|
};
|
|
2234
2262
|
}
|