diff --git a/.eslintrc.cjs b/.eslintrc.cjs index c4712a723..15f154c0d 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -167,8 +167,12 @@ module.exports = { // ts-rs 生成绑定:不接受 eslint --fix 二次改写,必须与原始输出逐字节一致 'packages/shared/src/contracts/generated/**', 'apps/ai-game-creator-shell/src/view/project-development/chat/generated/**', + 'apps/ai-game-creator-shell/src/view/project-development/export/generated/**', 'apps/ai-game-creator-shell/src/services/generated/**', 'apps/ai-game-creator-shell/src/features/project-workspace/generated/**', + // 审核 Skill Pack:内容按 SHA-256 定址并经 include_bytes! 编译进客户端, + // 不接受 eslint --fix 改写(.prettierignore 同样忽略该目录,避免指纹漂移) + 'apps/ai-game-creator-shell/src-tauri/resources/agc-skills/**', 'target', 'src/main.tsx', 'src/App.tsx', diff --git a/.gitattributes b/.gitattributes index a63eef88a..16eded8f0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -30,4 +30,5 @@ packages/shared/src/contracts/generated/** linguist-generated=true whitespace=-trailing-space apps/ai-game-creator-shell/src/features/ui-editor/types/** linguist-generated=true whitespace=-trailing-space apps/ai-game-creator-shell/src/view/project-development/chat/generated/** linguist-generated=true whitespace=-trailing-space +apps/ai-game-creator-shell/src/view/project-development/export/generated/** linguist-generated=true whitespace=-trailing-space apps/ai-game-creator-shell/src/services/generated/** linguist-generated=true whitespace=-trailing-space diff --git a/.prettierignore b/.prettierignore index 446fa63d9..0561423f2 100644 --- a/.prettierignore +++ b/.prettierignore @@ -8,6 +8,7 @@ packages/shared/src/contracts/generated/ public/Icons apps/ai-game-creator-shell/src/features/ui-editor/types/ apps/ai-game-creator-shell/src/view/project-development/chat/generated/ +apps/ai-game-creator-shell/src/view/project-development/export/generated/ apps/ai-game-creator-shell/src/services/generated/ apps/ai-game-creator-shell/src-tauri/resources/agc-skills/ # 预览桥脚本:随包注入浏览器的资源,按字节搬运自 Rust 内联字符串,禁止 prettier 二次改写 diff --git a/apps/ai-game-creator-shell/scripts/check-skill-pack.test.mjs b/apps/ai-game-creator-shell/scripts/check-skill-pack.test.mjs index b92b38e7c..c04f75f56 100644 --- a/apps/ai-game-creator-shell/scripts/check-skill-pack.test.mjs +++ b/apps/ai-game-creator-shell/scripts/check-skill-pack.test.mjs @@ -7,8 +7,10 @@ import test from 'node:test'; import { fileURLToPath } from 'node:url'; import { + collectBundledFiles, computeSkillContentFingerprint, inspectSkillPack, + isSafeSkillRelativePath, } from './skill-pack-manifest.mjs'; test('bundled skill pack manifest is synchronized', () => { @@ -16,6 +18,37 @@ test('bundled skill pack manifest is synchronized', () => { assert.deepEqual(result.mismatches, []); }); +test('hidden skill paths are rejected and never declared', () => { + assert.equal(isSafeSkillRelativePath('SKILL.md'), true); + assert.equal(isSafeSkillRelativePath('references/contract.md'), true); + assert.equal(isSafeSkillRelativePath('.selective_rule.txt'), false); + assert.equal(isSafeSkillRelativePath('scripts/.pack.test.mjs'), false); + assert.equal(isSafeSkillRelativePath('.hidden/SKILL.md'), false); +}); + +test('skill pack collector ignores hidden files and directories', () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agc-skill-hidden-')); + try { + fs.mkdirSync(path.join(root, 'demo', 'scripts'), { recursive: true }); + fs.writeFileSync(path.join(root, 'demo', 'SKILL.md'), '# demo\n'); + fs.writeFileSync(path.join(root, 'demo', '.selective_rule.txt'), 'local\n'); + fs.writeFileSync( + path.join(root, 'demo', 'scripts', 'pack.mjs'), + 'export {};\n', + ); + fs.writeFileSync( + path.join(root, 'demo', 'scripts', '.pack.test.mjs'), + 'test\n', + ); + assert.deepEqual(collectBundledFiles(root), [ + 'demo/SKILL.md', + 'demo/scripts/pack.mjs', + ]); + } finally { + fs.rmSync(root, { recursive: true, force: true }); + } +}); + test('skill content fingerprint canonicalizes CRLF', () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agc-skill-pack-')); try { diff --git a/apps/ai-game-creator-shell/scripts/skill-pack-manifest.mjs b/apps/ai-game-creator-shell/scripts/skill-pack-manifest.mjs index 88c717208..c03be2ede 100644 --- a/apps/ai-game-creator-shell/scripts/skill-pack-manifest.mjs +++ b/apps/ai-game-creator-shell/scripts/skill-pack-manifest.mjs @@ -13,7 +13,9 @@ export const EXPECTED_SKILL_NAMES = Object.freeze([ 'agc-project-structure', 'agc-unity-editor', 'agc-web-game-development', + 'platform-abstract', 'taonier-art-assets', + 'vite-export-xhs-minitool', ]); const utf8Decoder = new TextDecoder('utf-8', { fatal: true }); @@ -27,6 +29,17 @@ function canonicalTextBytes(filePath) { return Buffer.from(decoded.replaceAll('\r\n', '\n'), 'utf8'); } +/** + * 隐藏文件/目录名(以 `.` 开头)永远不进入审核 Skill Pack。 + * + * 约定:Skill 目录里以 `.` 开头的文件和目录是本地开发辅助(例如 + * `.selective_rule.txt`、`scripts/.pack.test.mjs`),既不写进 manifest, + * 也不做指纹、不安装、不可通过 `agc_read_skill_resource` 读取。 + */ +export function isHiddenSkillEntryName(name) { + return typeof name === 'string' && name.startsWith('.'); +} + export function isSafeSkillRelativePath(value) { if ( typeof value !== 'string' || @@ -40,7 +53,11 @@ export function isSafeSkillRelativePath(value) { return value .split('/') .every( - (segment) => segment.length > 0 && segment !== '.' && segment !== '..', + (segment) => + segment.length > 0 && + segment !== '.' && + segment !== '..' && + !isHiddenSkillEntryName(segment), ); } @@ -70,10 +87,14 @@ export function computeSkillContentFingerprint(rootDir, entry) { return digest.digest('hex'); } -function collectBundledFiles(rootDir) { +export function collectBundledFiles(rootDir) { const files = []; const walk = (directory, prefix) => { for (const entry of fs.readdirSync(directory, { withFileTypes: true })) { + // 隐藏文件/目录不参与审核:它们是本地开发辅助,不声明、不指纹、不安装。 + if (isHiddenSkillEntryName(entry.name)) { + continue; + } const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name; const absolutePath = path.join(directory, entry.name); if (entry.isSymbolicLink()) { diff --git a/apps/ai-game-creator-shell/src-tauri/prompts/runtime/texts/direct-tools.json b/apps/ai-game-creator-shell/src-tauri/prompts/runtime/texts/direct-tools.json index 0437256ed..b51fa82bd 100644 --- a/apps/ai-game-creator-shell/src-tauri/prompts/runtime/texts/direct-tools.json +++ b/apps/ai-game-creator-shell/src-tauri/prompts/runtime/texts/direct-tools.json @@ -4,6 +4,8 @@ "conversation.list.description": "按序读取当前项目已记录的 Codex 返回摘要。", "conversation.read.description": "读取当前项目的一条已记录 Codex 返回;只能使用 conversation.list 返回的 recordId。", "agc_read_skill_resource.description": "读取审核通过的 AGC Skill 指导文件;仅允许清单内 skillName 和相对文件名。", + "agc_install_skill_resource.description": "把审核通过的 AGC Skill 自带文件原样复制到当前项目相对路径;宿主直接读内置字节,不会像读取那样被大文件截断,也不需要原生 cp 或审批,一次一个文件。仍走 agc_write_file 的合同与租约门。", + "agc_install_skill_resource.parameters.destinationPath": "当前项目根下的目标相对路径,例如 scripts/validate.mjs", "agc_write_file.description": "把文本写入当前 AGC 项目的相对路径,用于代码、配置、资源依赖或说明文件。", "agc_apply_patch.description": "使用官方 apply_patch 语法修改当前项目,支持 Add/Delete/Update/Move。固定当前项目为工作目录;一次最多64KiB UTF-8、256个操作,并受实际平台参数上限约束。完整检查全部源与目标后执行;失败可能已部分修改,先读取当前文件再提出新补丁。该工具可与独立的读取、生成和计划调用并行;同文件修改与依赖其结果的构建、检查须等待补丁回执。超时、取消或 needsReconciliation=true 时停止,不自动重放。", "agc_update_plan.description": "更新当前回合的进度计划,字段与 update_plan 相同:可选 explanation,以及 plan 中的 step/status(pending、in_progress、completed)。它可与其它独立工具并行;同一计划的连续更新按依赖顺序提交。计划完成只表示进度,不代替宿主交付验收。", diff --git a/apps/ai-game-creator-shell/src-tauri/prompts/runtime/texts/direct.json b/apps/ai-game-creator-shell/src-tauri/prompts/runtime/texts/direct.json index 5116ddcbe..d0fc2ea62 100644 --- a/apps/ai-game-creator-shell/src-tauri/prompts/runtime/texts/direct.json +++ b/apps/ai-game-creator-shell/src-tauri/prompts/runtime/texts/direct.json @@ -2,7 +2,7 @@ "identity": "对外身份:你是“陶泥儿”,是 Genarrative 的游戏创作助手。用户询问名称或能力时,以陶泥儿的身份回答。用户明确询问底层实现时可如实说明 Codex app-server 的作用。", "hostDelivery": "交付要求:当用户要求你制作、完成或交付游戏(例如“做一个游戏”“做一个可运行的游戏”“交付游戏”)时,调用 agc_register_delivery_contract 登记本轮 scope、changeKind 和 requirements,由你结合用户输入理解意图。只生图、普通修改、读取、咨询或不操作无需合同。文件写入、命令、生成和验证不以合同为前提。每项有唯一ID,仅支持visual或gameplay(scenario);登记后不能扩项。新Web合同由宿主补充现有双端视觉和固定玩法要求,以登记回包为准;跑酷选择runner-v1,俄罗斯方块选择tetris-v1,其余按真实能力选择固定场景。按实际需要构建和测试,构建命令返回值不是合同验收项。不能提交passed或改写证据。agc_delivery_status只返回当前评估,不会结束执行;完成本轮用户要求并正常回复后,宿主才复核已登记合同。", "deliveryFeedback": "本轮已登记合同的验收尚未通过。读取agc_delivery_status,针对未满足的视觉/玩法要求修复并提供真实证据;不得扩项、提交passed或改写证据。按实际需要构建和测试,再执行必要的固定双端场景。原用户目标与本轮登记要求保持不变。\n\n未满足项:\n{detail}", - "engineering": "AGC 工程要求:当前 cwd 是用户选择的项目目录。先读取适用的 AGENTS.md、README 或项目说明,识别实际引擎与工程结构。用户明确指定编辑器或引擎,而当前目录缺少对应工程结构时,先说明不匹配并澄清;用户确认继续当前工程或提供匹配目录后再执行。Cocos Creator 项目优先通过 `agc_cocos_execute` 或 `cocos.editor.execute` 操作已打开的编辑器。新 Web 游戏使用 npm + Vite;二维游戏使用 Phaser 4.2.1,以 `import Phaser from 'phaser'` 导入;三维游戏自行选择合适的三维技术栈。依赖统一使用 npm 包。Phaser 迁移:读取已有 game/index.html,将状态、输入、敌人/守卫、波次、胜负、重开和画布绘制迁移到 Phaser Scene/GameObject/update;写入 game/package.json、package-lock.json、vite.config.js(输出 game/dist)、game/game.js、game/style.css,先调用 project.bootstrap {cwd:game},再调用 project.verify {cwd:game,script:build,expectedCommand:从 game/package.json 原样读取},确认 game/dist/index.html 后启动 preview.start,并分别 preview.validate 桌面与移动视口。Phaser 画布由单一机制居中:使用 Scale.FIT 与 autoCenter CENTER_BOTH 时,canvas 直接父容器使用尺寸明确的普通 block;使用 CSS 居中时,Phaser autoCenter 设为 NO_CENTER。外围布局可使用 flex/grid。预览偏移先检查并修正项目自身的 CSS 与 Phaser 配置。布局修改后按项目 scripts 构建 dist,在桌面、移动视口和 resize 后确认 canvas 相对父容器的中心误差不超过 1 CSS px、无溢出。简单修改聚焦用户要求及不可替代的最小验证;安装依赖、构建和试玩按此范围执行。源码和命令优先使用 cwd 相对路径,依赖安装与构建使用项目 npm scripts;原生文件读取、搜索、命令和图片查看按当前工具目录使用。源码局部补丁调用 `agc_apply_patch`,支持官方 Add/Delete/Update/Move 语法并固定当前项目目录;多步骤进度调用 `agc_update_plan`,计划状态不代替验收证据。完整文本写入可使用 `agc_write_file`,content 仅填写目标文件的完整原始 UTF-8 正文。可用能力包括原生文件、搜索、命令、图片查看、Skill、`agc_tools` 和用户已启用的第三方 MCP;用户指定工具时先查当前可用工具并调用,缺失时如实说明。资源工具按当前 schema 使用;Skill references 按需读取。完整新游戏或按策划案实现时执行 agc-game-production-workflow,依次完成“策划定界 → 项目/资源盘点 → 美术生成或复用 → 游戏实现 → 构建验证 → 桌面/移动试玩 → 交付报告”。需要视觉素材时执行 taonier-art-assets:检查已登记资源,缺少或不适用时调用生图/编辑工具,读取结果的相对路径和登记身份,将真实素材接入源码并验证显示后再交付。你负责推进任务和按范围试玩。", + "engineering": "AGC 工程要求:当前 cwd 是用户选择的项目目录。先读取适用的 AGENTS.md、README 或项目说明,识别实际引擎与工程结构。用户明确指定编辑器或引擎,而当前目录缺少对应工程结构时,先说明不匹配并澄清;用户确认继续当前工程或提供匹配目录后再执行。Cocos Creator 项目优先通过 `agc_cocos_execute` 或 `cocos.editor.execute` 操作已打开的编辑器。新 Web 游戏使用 npm + Vite;二维游戏使用 Phaser 4.2.1,以 `import Phaser from 'phaser'` 导入;三维游戏自行选择合适的三维技术栈。依赖统一使用 npm 包。Phaser 迁移:读取已有 game/index.html,将状态、输入、敌人/守卫、波次、胜负、重开和画布绘制迁移到 Phaser Scene/GameObject/update;写入 game/package.json、package-lock.json、vite.config.js(输出 game/dist)、game/game.js、game/style.css,先调用 project.bootstrap {cwd:game},再调用 project.verify {cwd:game,script:build,expectedCommand:从 game/package.json 原样读取},确认 game/dist/index.html 后启动 preview.start,并分别 preview.validate 桌面与移动视口。Phaser 画布由单一机制居中:使用 Scale.FIT 与 autoCenter CENTER_BOTH 时,canvas 直接父容器使用尺寸明确的普通 block;使用 CSS 居中时,Phaser autoCenter 设为 NO_CENTER。外围布局可使用 flex/grid。预览偏移先检查并修正项目自身的 CSS 与 Phaser 配置。布局修改后按项目 scripts 构建 dist,在桌面、移动视口和 resize 后确认 canvas 相对父容器的中心误差不超过 1 CSS px、无溢出。简单修改聚焦用户要求及不可替代的最小验证;安装依赖、构建和试玩按此范围执行。源码和命令优先使用 cwd 相对路径,依赖安装与构建使用项目 npm scripts;原生文件读取、搜索、命令和图片查看按当前工具目录使用。源码局部补丁调用 `agc_apply_patch`,支持官方 Add/Delete/Update/Move 语法并固定当前项目目录;多步骤进度调用 `agc_update_plan`,计划状态不代替验收证据。完整文本写入可使用 `agc_write_file`,content 仅填写目标文件的完整原始 UTF-8 正文。复制 Skill 自带文件(脚本、配置、参考文档)进项目时改用 `agc_install_skill_resource`,一次一个文件、`destinationPath` 为目标相对路径;不要用 `agc_write_file` 重抄大文件正文,也不要依赖原生 `cp`。可用能力包括原生文件、搜索、命令、图片查看、Skill、`agc_tools` 和用户已启用的第三方 MCP;用户指定工具时先查当前可用工具并调用,缺失时如实说明。资源工具按当前 schema 使用;Skill references 按需读取。完整新游戏或按策划案实现时执行 agc-game-production-workflow,依次完成“策划定界 → 项目/资源盘点 → 美术生成或复用 → 游戏实现 → 构建验证 → 桌面/移动试玩 → 交付报告”。需要视觉素材时执行 taonier-art-assets:检查已登记资源,缺少或不适用时调用生图/编辑工具,读取结果的相对路径和登记身份,将真实素材接入源码并验证显示后再交付。你负责推进任务和按范围试玩。", "unityPlugin": "Unity 编辑器能力由 agc_unity_execute(Runtime 工具名 unity.editor.execute)提供。当前工程是 Unity 时使用该工具执行 C#,先读取实际场景与对象再修改。仅提交 code;缺少工具时报告该能力不可用,不要自行安装或改写插件。结果待人工核对、超时或断线时,禁止自动重发、重启插件或切换项目以绕过阻断。只有真实 completed 回执才可报告成功。", "godotPlugin": "Godot 编辑器能力由 agc_godot_execute(Runtime 工具名 godot.editor.execute)提供。当前工程是 Godot 时使用该工具执行支持 return/await 的 GDScript 函数体,先读取真实场景再修改;不改写为 Phaser。不要自行安装插件、写入库文件或描述文件。仅提交 code,不提供项目、进程、端口、令牌或库路径;缺少工具时报告该能力不可用。编译或确定运行失败可修正代码;结果待人工核对、超时或断线时禁止自动重发、重启插件或切换项目绕过阻断。只有真实 completed 回执才可报告成功。", "editorGuide": "常用编辑器操作:Unity 先读 agc-unity-editor,Godot 先读 agc-godot-editor。可用原生 Skill 读取,或调用 agc_read_skill_resource,skillName 为对应名称、relativePath 为 SKILL.md,再按入口读取操作参考。指南提供场景、对象/节点、资源、UI、保存和撤销示例;只读说明不代表编辑器工具已可用,实际执行仍检查当前工具。", diff --git a/apps/ai-game-creator-shell/src-tauri/resources/agc-skills/manifest.json b/apps/ai-game-creator-shell/src-tauri/resources/agc-skills/manifest.json index 142e16b6d..0b6b975d8 100644 --- a/apps/ai-game-creator-shell/src-tauri/resources/agc-skills/manifest.json +++ b/apps/ai-game-creator-shell/src-tauri/resources/agc-skills/manifest.json @@ -1,6 +1,6 @@ { "schemaVersion": "agc-skill-pack.v1", - "version": "2026-08-26.42", + "version": "2026-08-26.70", "skills": [ { "name": "agc-unity-editor", @@ -166,6 +166,45 @@ "references/projection-contract.md" ], "sha256": "77c69762910891e0eef5557480a5e46e743f5dec78ee958679e8d6f1e7990531" + }, + { + "name": "platform-abstract", + "purpose": "把环境相关的调用从 core 里收口,让同一份逻辑能跑在多个 target 上", + "triggers": [ + "抽离平台相关能力", + "同一份逻辑要跑在多个 target", + "新增非 Web 构建目标" + ], + "requiredTools": [], + "files": [ + "SKILL.md" + ], + "sha256": "26e5b0b1d2fac6321eff0c834e15bce83ce70a435965c510254e40f3c16370e2" + }, + { + "name": "vite-export-xhs-minitool", + "purpose": "把 vite 项目导出并打包为符合小红书小工具规范的 zip 制品", + "triggers": [ + "导出小红书小工具制品", + "校验或打包 xhs minitool 产物", + "适配小红书小工具运行环境" + ], + "requiredTools": [], + "files": [ + "SKILL.md", + "references/cross-platform-h5.md", + "references/css-compatibility.md", + "references/device-capabilities.md", + "references/js-api.md", + "references/js-compatibility.md", + "references/manual-checks.md", + "references/performance-budget.md", + "references/zip-artifact-spec.md", + "scripts/pack.mjs", + "scripts/validate.mjs", + "scripts/vite.config.xhs-minitool.mjs" + ], + "sha256": "fd869242fdd40bb46fc3fbf30d1509c79d306e769e87dad3430b2ab0e5449b12" } ] } diff --git a/apps/ai-game-creator-shell/src-tauri/resources/agc-skills/platform-abstract/SKILL.md b/apps/ai-game-creator-shell/src-tauri/resources/agc-skills/platform-abstract/SKILL.md new file mode 100644 index 000000000..25268ac5b --- /dev/null +++ b/apps/ai-game-creator-shell/src-tauri/resources/agc-skills/platform-abstract/SKILL.md @@ -0,0 +1,84 @@ +--- +name: platform-abstract +description: 把环境相关的调用从 core 里收口,让同一份逻辑能跑在多个 target 上。 +--- + +# 抽出平台抽象 + +core 不直接调环境 API;每个 target 给一份**同名导出**的实现,构建时选一份。 + +## 例子 + +```ts +// platform/web.ts +export async function save(score: number) { + const res = await fetch('/api/score', { + method: 'POST', + body: JSON.stringify({ score }), + }); + if (!res.ok) throw new Error('save failed'); +} + +// platform/platform-b.ts —— 同名同参,实现不同 +export async function save(score: number) { + await host.request({ url: '/api/score', method: 'POST', data: { score } }); +} + +// caller —— 不认识 fetch / host +import { save } from '#platform'; +export async function saveScore(score: number) { + await save(score); +} +``` + +「接口」就是两边的同名导出,靠 TS 结构化类型在**各自的 target build** 里校验。两份实现漂移只会在各自构建时暴露——口头约定即可,不需要单点类型。`#platform` 怎么指到具体实现见「选实现」。 + +## 结构 + +``` +src/ +├── .../ +│ └── use-case.ts # 只 import '#platform' +└── platform/ # 每个 target 一份同名导出 + ├── web.ts + └── platform-b.ts +``` + +## vite.config + +`vite.config.ts` 就是**默认(web)**的那份,保持不动;新 target 单独一份,用 `mergeConfig` 在默认之上叠差异。不要再加 `vite.config.base.ts`。 + +```ts +// vite.config.ts —— 默认 = web +import path from 'node:path'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + resolve: { + alias: { '#platform': path.resolve(__dirname, 'src/platform/web.ts') }, + }, +}); +``` + +```ts +// vite.config.platform-b.ts —— 只写差异 +import path from 'node:path'; +import { defineConfig, mergeConfig } from 'vite'; +import a from './vite.config'; + +export default mergeConfig( + a, + defineConfig({ + build: { outDir: 'dist-platform-b' }, + resolve: { + alias: { '#platform': path.resolve(__dirname, 'src/platform/platform-b.ts') }, + }, + }), +); +``` + +## 选实现 + +只有 alias 一种:caller 只 import `'#platform'`,每份 config 指向自己的实现。 + +alias 必须用**对象**写法——`mergeConfig` 才会按 key 覆盖;写成数组会拼接,两份实现同时留着。`#platform` 是打包期 alias,TS 侧还要在 tsconfig 里配一条同名 `paths`,否则类型检查找不到模块。 diff --git a/apps/ai-game-creator-shell/src-tauri/resources/agc-skills/vite-export-xhs-minitool/.selective_rule.txt b/apps/ai-game-creator-shell/src-tauri/resources/agc-skills/vite-export-xhs-minitool/.selective_rule.txt new file mode 100644 index 000000000..359267b98 --- /dev/null +++ b/apps/ai-game-creator-shell/src-tauri/resources/agc-skills/vite-export-xhs-minitool/.selective_rule.txt @@ -0,0 +1,59 @@ +【取舍记录】minitool-zip-builder MUST-only 清理 + +原则 +- references/ 只保留 MUST(必须 / 须 / 禁止 / 不得),非 MUST 的建议、可选、工具用法全部删除。 +- 脚本能确定性判定的内容,不再在 skill 里重复,避免两处漂移。 + +已删除的文件 +- scripts/audit_artifact.mjs + - 原因:体积审计已折进 scripts/validate.mjs。 + - 影响:目录或 zip 的体积门禁改由 validate 检查;pack 打包后会自动复跑。 + +逐文件删除与去处 +- SKILL.md + - 步骤 9:删掉 tsc / Vite 已保证项(语法错误、未定义函数、加载顺序),改为指向 references/MANUAL_CHECKS.md。 + - 步骤 10:删掉 Node / Python / 人工分支,改为运行 validate.mjs 与 pack.mjs。 + - 步骤 3 与 Reference 表:删掉已不存在的「目录结构 / 路径规则 / 打包自检」描述。 + - 被外部精简重写为 vite-export-xhs-minitool,只剩 Reference 表,原步骤 9 / 10 已不存在;Reference 表指向「index.html 要求」。 +- zip-artifact-spec.md + - 删 §1 目录结构与打包方式:pack.mjs 保证 zip 根 index.html、不多套层。 + - 删 §4 路径与引用规则:base:'./' 与 pack.mjs 保证相对路径。 + - 删 §6 打包前自检:validate / pack / MANUAL_CHECKS 已覆盖。 + - 删 §5 表格里「资源全为相对路径」「脚本外置」两行(前者 Vite 保证,后者 §3 CSP 已声明)。 + - 删 §5 的「index.html 模板」整段 HTML 骨架与其中的 reset / 字体 CSS;标题改为「index.html 要求」,只留要求表与