ls/cat/grep/sed/find/rm/cp/comm/diff 都正常);
早期会话的「shim 报 dirname not found」已不复现。仍优先用 Glob/Grep/Read 读文件。... | Out-File -FilePath <f> -Encoding utf8;> 会写成 UTF-16,Read 工具会当二进制拒绝。Remove-Item 被 safe-delete 钩子拦截 → 删文件用 [System.IO.File]::Delete('绝对路径');bash rm -f 可以。E:\workspace\zsjz-ai(git common dir);当前 worktree 是
C:\Users\cc\WorkBuddy\Worktrees\zsjz-ai\dev-3-*。worktree 只有被跟踪的文件,
被 gitignore 的本地目录不会带过来(见下),缺东西时去主仓库拷。frontend/ 是 pnpm 项目:pnpm install 会因 esbuild postinstall 报
spawnSync node.exe EBUSY(沙箱里孙进程 spawn node 被拦),而且失败后 node_modules/.bin
不会生成,于是 pnpm type:check / build 全报「不是内部或外部命令」。
→ 解法:pnpm install --ignore-scripts。esbuild 真二进制在 @esbuild/win32-x64,
postinstall 只是 --version 校验,跳过无害(实测 require('esbuild').version 正常)。ai-electron/ 是 npm 项目(只有 package-lock.json,node_modules 是扁平结构、无 .pnpm):
不要用 pnpm 装(会因 esbuild@^0.28.0 解析失败 —— 镜像元数据里 latest 还是 0.27.4)。
正确姿势:从主仓库拷 ai-electron/package-lock.json(被 gitignore 了),再
npm install --ignore-scripts --no-audit --no-fund(约 23s / 710 包)。
校验命令:npx tsc --noEmit(= npm run typecheck)、npm test(vitest,91 用例)、
npm run build-electron(产出 public/electron/main.js)。cp -r 过来,不会污染 git status):
ai-electron/frontend/build/(根 .gitignore:22 的 build/ 命中;vite.config.ts 要 ./build)ai-electron/frontend/src/case/views/data/(ai-electron/.gitignore:7 的 data/ 命中,
把源码目录也误伤了;路由 routes/index.ts 直接引用,51 个文件)ai-electron/package-lock.jsondata/、out/、logs/),不用管。ai-electron/frontend/)<script setup lang="ts"> + ant-design-vue + vite + less + pnpm。ai-frontend/):
pnpm build(= vite build --mode production,不做类型检查,约 2 分钟)pnpm type:check(= vue-tsc --noEmit --skipLibCheck,约 25 秒)pnpm type:check 本来就过不了:仓库有约 89 条既有报错,集中在
src/call/、src/trans/、src/graph/、src/otg/、src/person/ 等无关文件。
判断自己有没有写坏,看的是「报错文件里有没有自己动的文件」,不是退出码。tsconfig.json:isolatedModules: true,无 verbatimModuleSyntax;noUnusedLocals: false(所以多余的 import 不报错)。vite-plugin-theme-vite3 生成的
dist/assets/app-antd-dark-theme-style.*.css 里,自定义属性的 -- 前缀被抹掉
(搜 --codex-bg / --ai-text 都是 0 处,只有 codex-bg / ai-text)。
即暗色模式下 --ai-* / --codex-* / --jeesite-* 都不会被覆盖,页面沿用浅色 :root 值
(antd 类名那部分正常)。全项目同等影响,v2.1 之前就存在;修它要动构建期主题插件,未动。WARN warnings when minifying css: Invalid character(s) '@charset UTF-8"; 是同一个
主题插件的产物拼接问题,同样与业务代码无关。@/* → src/*;路由表在 src/core/router/routes/index.ts。build/theme/modifyVars.ts 里的
hack: 'true; @import (reference) "src/core/design/var/index.less";' 注入,
所以任意 .vue style 块和任意 .less 文件都能直接用 @modal-header-bg-color 这类变量,
SFC 的 style 抽成独立 .less 再 @import 回来是安全的。wrapClassName + 全局类名命中。src/core/store/modules/menu.json(permission.ts 直接 import 它当 RootRoute)。
加页面 = 往对应父分组 children 里塞一条(path/component/url 三者同值,name 形如 ViewsXxxYyy,id 用递增号)。src/core/router/helper/routeHelper.ts 的 explicitDynamicViewMap(显式登记优先于 glob 兜底),
格式:'/xxx/yyy': () => import('@/xxx/views/yyy/index.vue')。.ai-page 那套反冲手法:width: calc(100% + 20px); margin: 0 -10px;,留 2px 灰底 gutter。src/codex/,2026-09-24 起)src/ai/ 聊天代码、所有文件 codex 前缀(含视图入口 views/codexAnalysis/codexAnalysis.vue)。codex-analysis-plan.md(v1.0.0 → v1.9.0,逐版本停下 review)。codex 前缀;主进程新增
codexArtifactService.ts + codexArtifactCtl.ts(root 白名单落盘 + 路径穿越校验 + 只读不删),
codexCtl.ts 增量:threadStart 返回 cwd、turnRun 登记可选 cwd、新增 defaultWorkspace。echarts/graph/table/json/report,解析失败回退成代码块;导出 md/html/xlsx/长图(png);
HTML 报告里的图表靠 utils/codexVisualRegistry.ts(内容哈希 → 图片提供者)内嵌真实图片。cwdOverrides) → 案件工作空间 → 客户端默认。
案件工作空间 = <user.dir>/QingJian/workspace/<案件ID>,由后端 CaseInfo.workspacePath 回传
(@TableField(exist = false),CaseInfoService.fillWorkspacePath 在 open/current/list 返回前置值)。pnpm type:check(89 条基线、本包 0 报错)+ pnpm build(exit 0);
主进程 npx tsc --noEmit + npm test(91 用例)+ npm run build-electron;
后端 mvn -o -DskipTests compile(仓库根,多模块,ai-server 是子模块)。electron/controller/codexCtl.ts + service/codex/* 已全量可用;IPC 封装现在
唯一实现在 src/codex/api/codexApi.ts,src/ai/api/codexApi.ts 只剩一行 export *。渲染唯一输入是
AgentEvent(映射见 service/codex/eventMapper.ts)。https://developers.openai.com/codex/app-server;本地 schema 全集在
E:\workspace\ai\Noobi.ai\schemas\codex\v2\*.json。布局可参考 Noobi.ai(React),但样式必须用本项目主题。src/case/views/data/cleaning.vue)当前进度(2026-09-18):12029 → 944 行(−92%),30+ 新文件,type:check 0 报错 + build 通过。
依赖顺序(单向、无环):useCaseDataCleaningShared(底层状态)→ useCleaningRules / useFieldMapping / useRecognitionRules / useTemplateLibrary
→ useCaseDataCleaningActions(叶子动作域,放在最后所以能直接用上游产物)。
剩余(压到 ≤400):① 把顶层 watch([...]) 移进 composable(watcher 在 composable 内注册仍绑定页面实例)→ 21 个退回声明可搬走;
② 清未使用 import(匹配标识符要允许 ... 展开);③ useCaseDataCleaningActions(1310) 可按域再拆成 useFileTree / usePreviewData / useColumnOps。
生成器可复用件(写这类脚本直接照搬):
watch( / provide( / onMounted( 等非声明的顶层语句,否则上一个声明的范围会吞掉它们{{ }} + : / v- / @ / ref 绑定值 + 标签名;表达式剥掉 '…'/"…" 但保留 ${...} 插值.ts 补 export;跨文件相对路径按新层级重算;deps 类型显式写 interface(否则类型循环 → Object.entries(any) → [string, unknown][])provide 放在 setup 末尾;
wiring 插在「最后一个依赖声明」之后(锚点是依赖声明的最后一行)方案文档:仓库根 cleaning-refactor-plan.md。已拍板:路由不动(cleaning.vue 留作入口)、
通信走 provide/inject + 领域 composable、样式保持全局 less 非 scoped、业务逻辑一律不许动、
每阶段停下来 review。
目录:cleaning/{types,constants,context}.ts + cleaning/{utils,composables,components,styles}/
沟通模式(Phase 3 起确定的主力模式,后续阶段照这个来):
composables/useXxx.ts,由页面在 setup 里实例化const { a, b } = useXxx(...))→ 页面原有代码与模板零改动useXxx 实例放进 provide(CleaningContextKey, { ..., xxx }),子组件从 context 取provide() 放在 setup 末尾(避开 const 声明顺序的 TDZ);composable 的调用位置 = 「最后一个依赖声明之后」Record<string, any>,而 Object.entries(<any>) 会产出 [string, unknown][] 引发一片 unknown 报错验证必须 type:check + build 双跑:vue-tsc 抓不到 Vue 模板标签不平衡,
抽块边界写错(多删一个 </div>)时 type:check 是 0 报错、pnpm build 才报 Element is missing end tag。
抽模板块时边界要取「配对的 </div> 行」,不要凭缩进猜(这个文件缩进本身不严格)。
已踩实的坑:
.ts 必须补 export(无 import/export 的 .ts 会被 TS 当全局脚本 → TS2306)@/ 别名例外)let/var 标量不能注入:跨文件是按值快照,两边各改一份(请求序号守卫之类会失效)ref<\n Record<...> 会让深度中途归零、把声明切一半)${...} 插值,只剥 '…' / "…"(整段 strip 会把 `is-${x}` 里的 x 抹掉)provide(...) 也是顶层语句,会假阳性);
watch([...refs]) 整条都算立即求值;「可搬集合 ↔ wiring 位置」要迭代到收敛const { x } = someComposable; 解构出来的,类型要写成 ReturnType<typeof someComposable>['x'](否则退化成 any → Object.entries(any) → unknown);
依赖类型文本里出现的类型名(Ref<TemplateTab> 里的 TemplateTab)也要补 import验证「重构没改业务逻辑」的可靠做法:拿 git show HEAD:<原文件> 当基准,和「当前入口 + 抽出目录整棵树」做
逐行多集合比对(归一化:去 export 前缀 / 折叠空白 / 去行尾 ,;,跳过注释与 import),
判定条件 = 原始每行在当前树里的出现次数 ≥ 原始次数。本项目已验证 script 丢失/被改动 = 0。
注意:扫描范围只圈自己那一棵(别把同目录其它页面算进来)、SFC 的 template 结束点是 <script 不是 <style、
取原始文件用 node execSync(...,{encoding:'buffer'})(PowerShell > 会写成 UTF-16,且 cmd /c 被拦)。