speccore 8.3.137 → 8.3.147
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.js +24 -11
- package/dist/cli.js.map +1 -1
- package/dist/commands/about.d.ts.map +1 -1
- package/dist/commands/about.js +4 -0
- package/dist/commands/about.js.map +1 -1
- package/dist/commands/completion.js +2 -2
- package/dist/commands/completion.js.map +1 -1
- package/dist/commands/execute.d.ts.map +1 -1
- package/dist/commands/execute.js +23 -2
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/help.js +13 -13
- package/dist/commands/help.js.map +1 -1
- package/dist/commands/review.d.ts +26 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +369 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/task/new.d.ts +1 -0
- package/dist/commands/task/new.d.ts.map +1 -1
- package/dist/commands/task/new.js +34 -24
- package/dist/commands/task/new.js.map +1 -1
- package/dist/commands/trace.js +1 -1
- package/dist/commands/trace.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +4 -0
- package/dist/commands/update.js.map +1 -1
- package/dist/core/ask-engine.d.ts.map +1 -1
- package/dist/core/ask-engine.js +50 -4
- package/dist/core/ask-engine.js.map +1 -1
- package/dist/core/intent-recognition.js +5 -5
- package/dist/core/intent-recognition.js.map +1 -1
- package/dist/core/prompt-builder.d.ts.map +1 -1
- package/dist/core/prompt-builder.js +79 -44
- package/dist/core/prompt-builder.js.map +1 -1
- package/dist/core/rag-engine.d.ts +14 -0
- package/dist/core/rag-engine.d.ts.map +1 -1
- package/dist/core/rag-engine.js +58 -5
- package/dist/core/rag-engine.js.map +1 -1
- package/package.json +1 -1
|
@@ -321,7 +321,7 @@ async function loadExtraSpecs(cwd, taskDir, platform, iteration, options) {
|
|
|
321
321
|
// v8.3.93+: Markdown 链接自动展开 + 图片提取
|
|
322
322
|
if (fullPath.endsWith('.md')) {
|
|
323
323
|
content = await processMarkdownContent(content, fullPath, seenPaths, visionConfig, {
|
|
324
|
-
maxLinkDepth:
|
|
324
|
+
maxLinkDepth: 3, maxLinkChars: 2000, maxSvgChars: 1500,
|
|
325
325
|
});
|
|
326
326
|
}
|
|
327
327
|
// 跳过空文件或纯占位符文件
|
|
@@ -361,7 +361,7 @@ async function loadExtraSpecs(cwd, taskDir, platform, iteration, options) {
|
|
|
361
361
|
// v8.3.93+: Markdown 链接自动展开 + 图片提取
|
|
362
362
|
if (fullPath.endsWith('.md')) {
|
|
363
363
|
content = await processMarkdownContent(content, fullPath, seenPaths, visionConfig, {
|
|
364
|
-
maxLinkDepth:
|
|
364
|
+
maxLinkDepth: 3, maxLinkChars: 2000, maxSvgChars: 1500,
|
|
365
365
|
});
|
|
366
366
|
}
|
|
367
367
|
if (content.trim().length <= 50 || content.trim().match(/^#+\s*待填充|^<!--\s*AI-FILL\s*-->$/m)) {
|
|
@@ -536,7 +536,7 @@ async function loadAllTaskContext(cwd, taskDir, platform, iteration, graph) {
|
|
|
536
536
|
// v8.3.93+: Markdown 链接自动展开 + 图片提取
|
|
537
537
|
if (fullPath.endsWith('.md')) {
|
|
538
538
|
content = await processMarkdownContent(content, fullPath, seen, visionConfig, {
|
|
539
|
-
maxLinkDepth:
|
|
539
|
+
maxLinkDepth: 3, maxLinkChars: 2000, maxSvgChars: 2000,
|
|
540
540
|
});
|
|
541
541
|
}
|
|
542
542
|
if (content.trim().length <= 50 || content.trim().match(/^#+\s*待填充|^<!--\s*AI-FILL\s*-->$/m))
|
|
@@ -741,13 +741,17 @@ function extractSummary(content) {
|
|
|
741
741
|
}
|
|
742
742
|
/**
|
|
743
743
|
* 从路径和内容推断涉及的端
|
|
744
|
+
* v8.3.144+: 使用项目实际端名(工程标识),不再硬编码 backend/frontend 等
|
|
744
745
|
*/
|
|
745
|
-
function extractPlatforms(path, content) {
|
|
746
|
+
function extractPlatforms(path, content, projectPlatforms) {
|
|
746
747
|
const platforms = new Set();
|
|
747
|
-
|
|
748
|
+
// 优先使用项目 CONSTITUTION.md 中定义的工程标识,fallback 到常见端名
|
|
749
|
+
const knownPlatforms = projectPlatforms?.length
|
|
750
|
+
? projectPlatforms
|
|
751
|
+
: ['backend', 'admin', 'h5', 'miniapp', 'app', 'web', 'ios', 'android'];
|
|
748
752
|
// 从路径推断
|
|
749
753
|
for (const p of knownPlatforms) {
|
|
750
|
-
if (path.toLowerCase().includes(p)) {
|
|
754
|
+
if (path.toLowerCase().includes(p.toLowerCase())) {
|
|
751
755
|
platforms.add(p);
|
|
752
756
|
}
|
|
753
757
|
}
|
|
@@ -759,7 +763,7 @@ function extractPlatforms(path, content) {
|
|
|
759
763
|
// 从内容中扫描(前 2000 字)
|
|
760
764
|
const head = content.slice(0, 2000).toLowerCase();
|
|
761
765
|
for (const p of knownPlatforms) {
|
|
762
|
-
if (head.includes(p)) {
|
|
766
|
+
if (head.includes(p.toLowerCase())) {
|
|
763
767
|
platforms.add(p);
|
|
764
768
|
}
|
|
765
769
|
}
|
|
@@ -796,15 +800,16 @@ function extractTags(headings) {
|
|
|
796
800
|
}
|
|
797
801
|
/**
|
|
798
802
|
* 构建单个 TOC 条目(含摘要/端/行数/标签)
|
|
803
|
+
* v8.3.144+: 异步化,支持传入项目实际端名(工程标识)
|
|
799
804
|
*/
|
|
800
|
-
function buildTOCEntry(path, description, content, maxSections) {
|
|
805
|
+
async function buildTOCEntry(path, description, content, maxSections, projectPlatforms) {
|
|
801
806
|
const sections = extractHeadings(content);
|
|
802
807
|
return {
|
|
803
808
|
path,
|
|
804
809
|
description,
|
|
805
810
|
sections: maxSections ? sections.slice(0, maxSections) : sections,
|
|
806
811
|
summary: extractSummary(content) || undefined,
|
|
807
|
-
platforms: extractPlatforms(path, content) || undefined,
|
|
812
|
+
platforms: extractPlatforms(path, content, projectPlatforms) || undefined,
|
|
808
813
|
lineCount: content.split(/\r?\n/).length,
|
|
809
814
|
tags: extractTags(sections) || undefined,
|
|
810
815
|
};
|
|
@@ -816,13 +821,15 @@ function buildTOCEntry(path, description, content, maxSections) {
|
|
|
816
821
|
async function buildGlobalTOC(globalDir) {
|
|
817
822
|
const toc = [];
|
|
818
823
|
const speccoreDir = (0, path_1.join)(globalDir, '..'); // .speccore/
|
|
824
|
+
// v8.3.144+: 读取项目实际端名(工程标识),不再硬编码 backend/frontend
|
|
825
|
+
const projectPlatforms = await (0, spec_paths_1.parsePlatformList)();
|
|
819
826
|
// 1. synthesis/ 下的综合文档
|
|
820
827
|
const synthesisDir = (0, path_1.join)(globalDir, 'synthesis');
|
|
821
828
|
if (await (0, fs_extra_1.pathExists)(synthesisDir)) {
|
|
822
829
|
const files = await (0, fs_extra_1.readdir)(synthesisDir);
|
|
823
830
|
for (const f of files.filter(f => f.endsWith('.md') && !(0, task_utils_1.isTimestampBackup)(f))) {
|
|
824
831
|
const content = await (0, fs_extra_1.readFile)((0, path_1.join)(synthesisDir, f), 'utf-8');
|
|
825
|
-
toc.push(buildTOCEntry(`synthesis/${f}`, FILE_DESC[f] || f.replace('.md', ''), content));
|
|
832
|
+
toc.push(await buildTOCEntry(`synthesis/${f}`, FILE_DESC[f] || f.replace('.md', ''), content, undefined, projectPlatforms));
|
|
826
833
|
}
|
|
827
834
|
}
|
|
828
835
|
// 2. platforms/ 下各端文档
|
|
@@ -836,7 +843,7 @@ async function buildGlobalTOC(globalDir) {
|
|
|
836
843
|
const subFiles = await (0, fs_extra_1.readdir)((0, path_1.join)(platformsDir, platformName));
|
|
837
844
|
for (const f of subFiles.filter(f => f.endsWith('.md') && !(0, task_utils_1.isTimestampBackup)(f))) {
|
|
838
845
|
const content = await (0, fs_extra_1.readFile)((0, path_1.join)(platformsDir, platformName, f), 'utf-8');
|
|
839
|
-
toc.push(buildTOCEntry(`platforms/${platformName}/${f}`, `${platformName} 端 — ${f.replace('.md', '')}`, content));
|
|
846
|
+
toc.push(await buildTOCEntry(`platforms/${platformName}/${f}`, `${platformName} 端 — ${f.replace('.md', '')}`, content, undefined, projectPlatforms));
|
|
840
847
|
}
|
|
841
848
|
}
|
|
842
849
|
}
|
|
@@ -851,7 +858,7 @@ async function buildGlobalTOC(globalDir) {
|
|
|
851
858
|
const subFiles = await (0, fs_extra_1.readdir)((0, path_1.join)(projectsDir, projectName));
|
|
852
859
|
for (const f of subFiles.filter(f => f.endsWith('.md') && !(0, task_utils_1.isTimestampBackup)(f))) {
|
|
853
860
|
const content = await (0, fs_extra_1.readFile)((0, path_1.join)(projectsDir, projectName, f), 'utf-8');
|
|
854
|
-
toc.push(buildTOCEntry(`PROJECTS/${projectName}/${f}`, `${projectName} — ${f.replace('.md', '')}`, content));
|
|
861
|
+
toc.push(await buildTOCEntry(`PROJECTS/${projectName}/${f}`, `${projectName} — ${f.replace('.md', '')}`, content, undefined, projectPlatforms));
|
|
855
862
|
}
|
|
856
863
|
}
|
|
857
864
|
}
|
|
@@ -865,7 +872,7 @@ async function buildGlobalTOC(globalDir) {
|
|
|
865
872
|
return true;
|
|
866
873
|
})) {
|
|
867
874
|
const content = await (0, fs_extra_1.readFile)((0, path_1.join)(globalDir, f), 'utf-8');
|
|
868
|
-
toc.push(buildTOCEntry(`GLOBAL:${f}`, FILE_DESC[f] || f.replace('.md', ''), content));
|
|
875
|
+
toc.push(await buildTOCEntry(`GLOBAL:${f}`, FILE_DESC[f] || f.replace('.md', ''), content, undefined, projectPlatforms));
|
|
869
876
|
}
|
|
870
877
|
// 5. PATTERNS/ 可复用模式(含 TEMPLATES/ 写作模板)
|
|
871
878
|
const patternsDir = (0, path_1.join)(speccoreDir, 'PATTERNS');
|
|
@@ -882,7 +889,7 @@ async function buildGlobalTOC(globalDir) {
|
|
|
882
889
|
const label = isTemplate
|
|
883
890
|
? entry.name.replace('-template.md', '').toUpperCase() + ' 写作模板'
|
|
884
891
|
: (prefix ? prefix.slice(0, -1).replace(/\//g, ' › ') + ' › ' : '') + entry.name.replace('.md', '');
|
|
885
|
-
toc.push(buildTOCEntry(`PATTERNS:${prefix}${entry.name}`, label, content, isTemplate ? 6 : 4));
|
|
892
|
+
toc.push(await buildTOCEntry(`PATTERNS:${prefix}${entry.name}`, label, content, isTemplate ? 6 : 4, projectPlatforms));
|
|
886
893
|
}
|
|
887
894
|
}
|
|
888
895
|
};
|
|
@@ -894,7 +901,7 @@ async function buildGlobalTOC(globalDir) {
|
|
|
894
901
|
const files = await (0, fs_extra_1.readdir)(rulesDir);
|
|
895
902
|
for (const f of files.filter(f => f.endsWith('.md') && !(0, task_utils_1.isTimestampBackup)(f))) {
|
|
896
903
|
const content = await (0, fs_extra_1.readFile)((0, path_1.join)(rulesDir, f), 'utf-8');
|
|
897
|
-
toc.push(buildTOCEntry(`RULES:${f}`, RULES_DESC[f] || f.replace('.md', ''), content));
|
|
904
|
+
toc.push(await buildTOCEntry(`RULES:${f}`, RULES_DESC[f] || f.replace('.md', ''), content, undefined, projectPlatforms));
|
|
898
905
|
}
|
|
899
906
|
}
|
|
900
907
|
// 7. SKILLS/ 技术能力库(v8.3.134+ 四层架构:TOC 可见性)
|
|
@@ -903,7 +910,7 @@ async function buildGlobalTOC(globalDir) {
|
|
|
903
910
|
const files = await (0, fs_extra_1.readdir)(skillsDir);
|
|
904
911
|
for (const f of files.filter(f => f.endsWith('.md') && !(0, task_utils_1.isTimestampBackup)(f))) {
|
|
905
912
|
const content = await (0, fs_extra_1.readFile)((0, path_1.join)(skillsDir, f), 'utf-8');
|
|
906
|
-
toc.push(buildTOCEntry(`SKILLS:${f}`, SKILLS_DESC[f] || f.replace('.md', ''), content));
|
|
913
|
+
toc.push(await buildTOCEntry(`SKILLS:${f}`, SKILLS_DESC[f] || f.replace('.md', ''), content, undefined, projectPlatforms));
|
|
907
914
|
}
|
|
908
915
|
}
|
|
909
916
|
return toc;
|
|
@@ -2403,7 +2410,9 @@ function outputNeedsInfo(req) {
|
|
|
2403
2410
|
// ═══════════════════════════════════════════════════════════
|
|
2404
2411
|
// Markdown 链接自动展开 + 图片提取(v8.3.93+)
|
|
2405
2412
|
// ═══════════════════════════════════════════════════════════
|
|
2406
|
-
/** 提取 Markdown 文本链接 [text](path),排除图片链接
|
|
2413
|
+
/** 提取 Markdown 文本链接 [text](path),排除图片链接
|
|
2414
|
+
* v8.3.125+: 不再跳过外部链接 — 由调用方决定如何处理(收集到外链清单)
|
|
2415
|
+
*/
|
|
2407
2416
|
function extractMarkdownLinks(content) {
|
|
2408
2417
|
const links = [];
|
|
2409
2418
|
const regex = /\[([^\]]+)\]\(([^)]+)\)/g;
|
|
@@ -2414,8 +2423,10 @@ function extractMarkdownLinks(content) {
|
|
|
2414
2423
|
// 跳过图片链接  — 检查前一个字符
|
|
2415
2424
|
if (match.index > 0 && content.charAt(match.index - 1) === '!')
|
|
2416
2425
|
continue;
|
|
2417
|
-
//
|
|
2418
|
-
if (/^(
|
|
2426
|
+
// 跳过锚点和危险协议(保留 http/https/ftp 外链,由调用方处理)
|
|
2427
|
+
if (/^(mailto:|javascript:)/i.test(path))
|
|
2428
|
+
continue;
|
|
2429
|
+
if (path.startsWith('#'))
|
|
2419
2430
|
continue;
|
|
2420
2431
|
links.push({ text, path });
|
|
2421
2432
|
}
|
|
@@ -2441,19 +2452,22 @@ function resolveLinkPath(baseDir, linkPath) {
|
|
|
2441
2452
|
return linkPath;
|
|
2442
2453
|
return (0, path_1.join)(baseDir, linkPath);
|
|
2443
2454
|
}
|
|
2444
|
-
/** 递归展开 Markdown 链接指向的文件内容(防循环,限深度)
|
|
2445
|
-
|
|
2455
|
+
/** 递归展开 Markdown 链接指向的文件内容(防循环,限深度)
|
|
2456
|
+
* v8.3.138+: HTML 文件独立阈值(8KB),避免原型文件被过度截断
|
|
2457
|
+
* maxDepth 默认 3:支持文档 A → B → C 三级展开
|
|
2458
|
+
*/
|
|
2459
|
+
async function expandMarkdownLinks(content, baseDir, seenPaths, maxDepth = 3, currentDepth = 0, maxChars = 1500) {
|
|
2446
2460
|
if (currentDepth >= maxDepth)
|
|
2447
2461
|
return content;
|
|
2448
2462
|
const links = extractMarkdownLinks(content);
|
|
2449
2463
|
if (links.length === 0)
|
|
2450
2464
|
return content;
|
|
2451
2465
|
const expansions = [];
|
|
2452
|
-
// v8.3.125+: 收集外链资源,供宿主 AI
|
|
2453
|
-
const externalLinks =
|
|
2466
|
+
// v8.3.125+: 收集外链资源,供宿主 AI 按需读取(用 Map 按 URL 去重)
|
|
2467
|
+
const externalLinks = new Map(); // path -> text
|
|
2454
2468
|
for (const link of links) {
|
|
2455
2469
|
if (isExternalLink(link.path)) {
|
|
2456
|
-
externalLinks.
|
|
2470
|
+
externalLinks.set(link.path, link.text);
|
|
2457
2471
|
continue;
|
|
2458
2472
|
}
|
|
2459
2473
|
const resolved = resolveLinkPath(baseDir, link.path);
|
|
@@ -2467,15 +2481,32 @@ async function expandMarkdownLinks(content, baseDir, seenPaths, maxDepth = 2, cu
|
|
|
2467
2481
|
if (!st.isFile())
|
|
2468
2482
|
continue;
|
|
2469
2483
|
let linkContent = await (0, fs_extra_1.readFile)(resolved, 'utf-8');
|
|
2484
|
+
if (linkContent.trim().length <= 30)
|
|
2485
|
+
continue;
|
|
2486
|
+
// v8.3.138+: HTML/原型文件使用独立阈值(8KB),避免原型文件被过度截断。
|
|
2487
|
+
// 超过阈值不截断 inline(避免结构残缺误导 AI),而是提示宿主 AI 直接打开查看。
|
|
2488
|
+
const isHtml = resolved.endsWith('.html') || resolved.endsWith('.htm');
|
|
2489
|
+
const htmlMaxChars = 8192; // 8KB,足够覆盖大多数 HTML 原型
|
|
2490
|
+
const isLarge = isHtml ? linkContent.length > htmlMaxChars : linkContent.length > maxChars;
|
|
2491
|
+
if (isHtml && isLarge) {
|
|
2492
|
+
expansions.push(`\n\n---\n📄 **HTML 原型文件**(内容过大未展开,宿主 AI 请务必直接打开查看):\n` +
|
|
2493
|
+
`- 文件: ${resolved}\n` +
|
|
2494
|
+
`- 大小: ${(linkContent.length / 1024).toFixed(1)} KB\n` +
|
|
2495
|
+
`- 描述: ${link.text || '无描述'}\n` +
|
|
2496
|
+
`> ⚠️ **请打开此 HTML 文件查看完整内容**。此为页面原型/设计稿,与需求理解高度相关,CLI 因文件过大未完整展开,需要你直接打开文件查看。`);
|
|
2497
|
+
continue;
|
|
2498
|
+
}
|
|
2470
2499
|
// v8.3.122+: HTML 原型文件保留完整内容,不再提取纯文本
|
|
2471
|
-
if (
|
|
2500
|
+
if (isHtml) {
|
|
2472
2501
|
// 保留原始 HTML(包含 CSS/JS/结构),让 AI 完整解析原型
|
|
2473
2502
|
}
|
|
2474
|
-
|
|
2475
|
-
continue;
|
|
2476
|
-
const isTruncated = linkContent.length > maxChars;
|
|
2503
|
+
const isTruncated = isHtml ? linkContent.length > htmlMaxChars : linkContent.length > maxChars;
|
|
2477
2504
|
if (isTruncated) {
|
|
2478
|
-
linkContent = linkContent.slice(0, maxChars) +
|
|
2505
|
+
linkContent = linkContent.slice(0, maxChars) +
|
|
2506
|
+
`\n\n---\n📄 **文件内容已截断**(宿主 AI 请务必打开查看完整内容):\n` +
|
|
2507
|
+
`- 文件: ${resolved}\n` +
|
|
2508
|
+
`- 大小: ${(await (0, fs_extra_1.stat)(resolved)).size} 字节\n` +
|
|
2509
|
+
`> ⚠️ **请打开此文件查看完整内容**。此文件与需求理解高度相关,CLI 因长度限制已截断,需要你直接打开文件查看。`;
|
|
2479
2510
|
}
|
|
2480
2511
|
expansions.push(`\n\n<!-- 展开链接: ${link.path} -->\n**[链接展开] ${link.text}** (${link.path}):\n\n${linkContent}`);
|
|
2481
2512
|
}
|
|
@@ -2483,11 +2514,11 @@ async function expandMarkdownLinks(content, baseDir, seenPaths, maxDepth = 2, cu
|
|
|
2483
2514
|
}
|
|
2484
2515
|
let expanded = content + expansions.join('');
|
|
2485
2516
|
// v8.3.125+: 在内容末尾附加外链资源清单,提示宿主 AI 按需读取
|
|
2486
|
-
if (externalLinks.
|
|
2517
|
+
if (externalLinks.size > 0) {
|
|
2487
2518
|
const externalSection = [
|
|
2488
2519
|
'\n\n---',
|
|
2489
|
-
'📎 **外链资源清单**(CLI 无法直接获取内容,宿主 AI
|
|
2490
|
-
...externalLinks.map(
|
|
2520
|
+
'📎 **外链资源清单**(CLI 无法直接获取内容,宿主 AI 请务必按需访问):',
|
|
2521
|
+
...Array.from(externalLinks.entries()).map(([path, text]) => `- [${text || '链接'}](${path})`),
|
|
2491
2522
|
].join('\n');
|
|
2492
2523
|
expanded += externalSection;
|
|
2493
2524
|
}
|
|
@@ -2505,11 +2536,11 @@ async function inlineMarkdownImages(content, baseDir, seenPaths, visionConfig, m
|
|
|
2505
2536
|
const inlines = [];
|
|
2506
2537
|
let visionCallCount = 0;
|
|
2507
2538
|
const maxVisionCalls = visionConfig?.maxImagesPerPrompt ?? 10;
|
|
2508
|
-
// v8.3.125+: 收集外链图片,供宿主 AI
|
|
2509
|
-
const externalImages =
|
|
2539
|
+
// v8.3.125+: 收集外链图片,供宿主 AI 按需查看(用 Map 按 URL 去重)
|
|
2540
|
+
const externalImages = new Map(); // path -> alt
|
|
2510
2541
|
for (const img of images) {
|
|
2511
2542
|
if (isExternalLink(img.path)) {
|
|
2512
|
-
externalImages.
|
|
2543
|
+
externalImages.set(img.path, img.alt);
|
|
2513
2544
|
inlines.push(`\n<!-- 外链图片: ${img.alt || '无描述'} | URL: ${img.path} -->`);
|
|
2514
2545
|
continue;
|
|
2515
2546
|
}
|
|
@@ -2526,7 +2557,7 @@ async function inlineMarkdownImages(content, baseDir, seenPaths, visionConfig, m
|
|
|
2526
2557
|
inlines.push(`\n<!-- SVG 图片: ${img.path} -->\n${svgContent}`);
|
|
2527
2558
|
}
|
|
2528
2559
|
else {
|
|
2529
|
-
inlines.push(`\n
|
|
2560
|
+
inlines.push(`\n\n---\n🖼️ **SVG 图片文件**(内容过长已省略,宿主 AI 请务必直接打开查看):\n- ${img.path}\n- 大小: ${svgContent.length} 字符(超出 ${maxSvgChars} 字符限制)\n> ⚠️ **请打开此 SVG 文件查看完整内容**。此 SVG 与需求理解高度相关,因内容过长被省略,需要你直接打开文件查看。`);
|
|
2530
2561
|
}
|
|
2531
2562
|
}
|
|
2532
2563
|
}
|
|
@@ -2541,31 +2572,35 @@ async function inlineMarkdownImages(content, baseDir, seenPaths, visionConfig, m
|
|
|
2541
2572
|
inlines.push(`\n<!-- 图片描述 (视觉模型: ${visionConfig.provider}): ${img.path} -->\n**[图片内容]** ${img.alt || '无描述'}\n\n${result.description}`);
|
|
2542
2573
|
}
|
|
2543
2574
|
else {
|
|
2544
|
-
|
|
2575
|
+
// v8.3.138+: Vision 描述失败时,明确提示宿主 AI 打开文件查看
|
|
2576
|
+
inlines.push(`\n\n---\n🖼️ **图片文件**(视觉模型描述失败,宿主 AI 请务必直接打开查看):\n- ${img.alt || '无描述'}: ${resolved}\n> ⚠️ **请打开此图片文件查看内容**。此图片与需求理解高度相关,视觉模型未能成功描述,需要你直接打开文件查看。`);
|
|
2545
2577
|
}
|
|
2546
2578
|
}
|
|
2547
2579
|
catch (e) {
|
|
2548
|
-
|
|
2580
|
+
// v8.3.138+: 描述异常时,同样提示宿主 AI 打开查看
|
|
2581
|
+
inlines.push(`\n\n---\n🖼️ **图片文件**(视觉模型异常,宿主 AI 请务必直接打开查看):\n- ${img.alt || '无描述'}: ${resolved}\n> ⚠️ **请打开此图片文件查看内容**。此图片与需求理解高度相关,视觉模型异常未能描述,需要你直接打开文件查看。`);
|
|
2549
2582
|
}
|
|
2550
2583
|
}
|
|
2551
2584
|
else {
|
|
2552
|
-
//
|
|
2553
|
-
inlines.push(`\n
|
|
2585
|
+
// v8.3.138+: 无视觉模型时,明确提示宿主 AI 打开图片查看(不要用 HTML 注释,AI 会忽略)
|
|
2586
|
+
inlines.push(`\n\n---\n🖼️ **图片文件**(CLI 无法解析图片内容,宿主 AI 请务必直接打开查看):`);
|
|
2587
|
+
inlines.push(`- ${img.alt || '无描述'}: ${resolved}`);
|
|
2554
2588
|
try {
|
|
2555
2589
|
if (await (0, fs_extra_1.pathExists)(resolved)) {
|
|
2556
2590
|
const st = await (0, fs_extra_1.stat)(resolved);
|
|
2557
|
-
inlines.push(
|
|
2591
|
+
inlines.push(`- 文件大小: ${(st.size / 1024).toFixed(1)} KB`);
|
|
2558
2592
|
}
|
|
2559
2593
|
}
|
|
2560
2594
|
catch { /* ignore */ }
|
|
2595
|
+
inlines.push(`> ⚠️ **请打开此图片文件查看内容**。此图片与需求理解高度相关,CLI 无法自动解析图片内容,需要你直接打开文件查看。`);
|
|
2561
2596
|
}
|
|
2562
2597
|
}
|
|
2563
2598
|
// v8.3.125+: 在内容末尾附加外链图片清单,提示宿主 AI 按需查看
|
|
2564
|
-
if (externalImages.
|
|
2599
|
+
if (externalImages.size > 0) {
|
|
2565
2600
|
inlines.push([
|
|
2566
2601
|
'\n\n---',
|
|
2567
|
-
'🖼️ **外链图片清单**(CLI 无法直接获取内容,宿主 AI
|
|
2568
|
-
...externalImages.map(
|
|
2602
|
+
'🖼️ **外链图片清单**(CLI 无法直接获取内容,宿主 AI 请务必按需查看):',
|
|
2603
|
+
...Array.from(externalImages.entries()).map(([path, alt]) => `- ${alt || '无描述'}: ${path}`),
|
|
2569
2604
|
].join('\n'));
|
|
2570
2605
|
}
|
|
2571
2606
|
return content + inlines.join('');
|