09-10 工作台:配置收敛、复盘字段契约修复、原创选题分镜支持、榜单期号区间显示

配置收敛:
- 端口/DB名/产出根/榜单目录单源化到 mcn-work-shop/config.json,代码层统一经 load-config.js 读取
- 新增 get-port.js 供 start.bat 读端口(bat 内嵌 node -e 会被 cmd 截断,读到的是兜底值)
- start.bat 修复失效的 managed node 路径(原写死 22.22.2 已不存在),改为遍历 versions 自动探测

复盘诊断页(真 bug 修复):
- 问题清单严重度全部显示成绿色 + 修改建议不显示:模型输出 {sev:'high',fix} 与前端读取 {severity:'高',suggestion} 字段名与取值均不匹配
- 前端新增 normIssue 归一化,兼容 sev/severity/level 与中英取值,并回补所属维度显示
- 在 submit-review-tasks 与诊断/复盘 prompt 中固化 issues_json 字段契约,从源头防 schema 漂移

分镜提示词:
- 原创选题(video_id 为空)原本无生成入口(分镜按 video_id 查询)
- listStoryboards 支持按 rewrite_id 查询,前端放开入口、切版本重查,并补落库指令(此前入库靠模型自由发挥)

榜单展示:
- 期号改为显示区间「08/31 ~ 09/06」,避免被误读为单日(期号=该周周一)
- 后端新增 collectRanges,从落盘文件读真实 dateStart/dateEnd 随 API 返回(日榜 start=end 不显区间)

其他:
- 清理 dsh 环境废弃后的历史叙述类废话(16 文件约 18 处),文末变更记录区保留
- 新增 docs/工作台UI规范.md(设计 token / 布局 / 8 页面路由 / 12 类组件 / 9 条已知坑)
This commit is contained in:
maogeigei committed 2026-09-10 18:28:42 +08:00
1 parent 6cb29a9877
commit c48902f063
58 files changed
+895 -263

No files matched your search

+148
View File
@@ -0,0 +1,148 @@
# 2026-09-10
## 打开工作台 + 仓库路径迁移发现
- 启动 MCN工作台:`D:\AI技能\mcn-short-video\project\短视频脚本创作\V1.0\mcn-work-shop\server.js 8900` → http://127.0.0.1:8900 返回 200,正常。
- **★仓库本地路径已变更**:`D:\AgentSkill\mcn-video-script` → `D:\AI技能\mcn-short-video`(`D:\AgentSkill` 已不存在)。
- **技能软链失效并已修复**:`C:\Users\maidou\.workbuddy\skills\短视频工作台` 原 junction → 失效旧路径 `D:\AgentSkill\mcn-video-script\project\短视频脚本创作\V1.0`(文件系统残留、目标不存在)。
- 修法:node `fs.unlinkSync` 删旧链 → PowerShell `New-Item -ItemType Junction -Path <link> -Target "D:\AI技能\mcn-short-video\project\短视频脚本创作\V1.0"`。
- 验证:isSymbolicLink=true、children=7、SKILL.md 存在。
- **待办(未执行,等用户确认)**:仓库内 30+ 文件仍写死 `D:\AgentSkill\...` 旧路径(含 V1.0/SKILL.md、references-add/路径配置.md、V1.0/references/规则/路径引用规范.md、Lite1.0、各 subskills、mcn-work-shop/server.js、browser-harness venv 配置等),需做全量路径迁移(注意豁免三类:禁止性规则示例行、变更记录历史行、references-add/路径配置.md)。
## 路径治本:环境判定锚点重构为「仓库特征判定」+ 全树写死路径清理
- **先分类再动手**:全树扫描命中 77 处 `AgentSkill`,分三类——A 环境判定锚点 37 处(规范明确豁免)、B 真实读写路径 8 处(真违规)、C 历史/审计文本 8 处(豁免)。用户确认方案后执行。
- **新判据(v2 · 两步)**:① 技能路径含 `.dsh` 路径段 → dsh 部署环境;② 不含 `.dsh` → `git -C {技能目录} rev-parse --show-toplevel` 返回仓库根 = 开发机,失败 = 用户环境(无 git 兜底向上找 `.git`)。**必须 `.dsh` 先判**(`~/.dsh` 自身是 git 仓库)。产出根目录三环境结果不变。
- **实测验证**:从软链 `C:\Users\maidou\.workbuddy\skills\短视频工作台` 执行 `git rev-parse --show-toplevel` → `D:/AI技能/mcn-short-video`,正确识别开发机。
- **改动 21 文件**:规范层(`references/规则/路径引用规范.md` + 4 份 subskills 副本 + 4 份 `路径配置.md` + README);判定节(dou-analysis / mcn-video-prompt 的 SKILL.md 与 references(-add));MCP 规范 2 份;Lite1.0(SKILL.md + 路径配置 + README)。
- **B 类修复**:`server.js` 新增 `BROWSER_LOCK_FILE = path.join(ROOT,'.browser-lock')` / `SESSION_CWD = ROOT`;`SESSION_CWD_PATTERN` 由 `'%mcn-workshop%'` 放宽为 `'%mcn-work%'`(兼容历史+新建会话);2 个 `.cjs` prompt 内 DB 路径改用既有 `DB` 常量;`V1.0/SKILL.md` 运行时 `cwds` 改 `<工作台目录>`。
- **★Lite1.0 结构事实**:`Lite1.0/` 下**无 `subskills/`**(实测),未内置 mcn-dou-analysis。原 SKILL.md 依赖段落既写死 `D:\AgentSkill\mcn-dou-analysis` 又未同步 09-02 结构变更 → 改为「未内置 + 需用请改用 V1.0,或先同步进 subskills 后按相对路径调用」。
- **BROWSER_LOCK 契约同步**:dou-analysis `references/浏览器搜索抖音账号操作规范.md` 5.6 节 8 处盘符硬编码 → 相对 `.browser-lock`(会话 cwd 即工作台目录)。
- **残留核对**:`AgentSkill` 仅剩 13 处,全为豁免三类;`mcn-video-script` 1 处、`MCNVideo AI` 3 处均属历史文本。当前规则行零残留。
- **验证**:3 个 JS `--check` 通过;工作台重启后 8900 单实例监听、HTTP 200。
- **沉淀**:V1.0 SKILL.md frontmatter(updated_at 09-10 + last_change)、`references-add/变更日志.md`、`失误与规避记录.md #017`。
- **★B 类违规的实锤后果(取证)**:`D:\AI技能\mcn-workshop\` 下发现 3 个 **0 字节**垃圾文件,文件名是**被剥掉反斜杠的完整路径**:
- `AgentSkillmcn-video-scriptproject短视频脚本创作V1.0mcn-work-shopmcn-plugin.db`(09-01 16:24)← 对应 `build-create-prompts.cjs` / `submit-review-tasks.cjs` prompt 里写死的 DB 绝对路径
- `AgentSkillmcn-workshopmcn-plugin.db`(09-01 18:31)← 对应旧 `cwd` + `mcn-plugin.db`
- `mcn-plugin.db`(09-01 12:26)
真实 DB 为 `mcn-work-shop/mcn-plugin.db`(4.2 MB,09-07 16:29)。→ 证明写死路径曾导致 AI 会话**落库写偏**(0 字节文件),与失误记录 #015「0 字节误建文件」同类。**未删除,待用户确认**。
- **未处理/待确认**:① `D:\AI技能\mcn-workshop\` 3 个 0 字节垃圾文件是否清理(该目录还有 `outputs/` 09-01~09-04 产物,勿整体删);② `D:\AgentSkill` 整目录已不存在,用户级 MEMORY.md 里 `ThirdPartySkills` / `AgentPlatform` 两个仓库的本地路径也一并失效(maxdepth 3 未搜到迁移后位置)。
## dsh 部署环境废弃:三环境 → 两环境(判定简化为一步)
- **触发**:用户纠正「第一步 技能路径含 .dsh 路径段 ② dsh 部署环境 —— 没有这个环境了」。实测:`~/.dsh/skills/` 不存在、`~/.dsh/mcn-plugin.db` 不存在、`~/.dsh` 仅剩 profiles/、`D:\dshworkspace` 整个目录不存在。
- **判据 v3(一步)**:删 `.dsh` 路径段判定步骤,仅保留 `git -C {技能目录} rev-parse --show-toplevel` = 仓库根=开发机 / 失败=用户环境(无 git 兜底向上找 .git)。三环境 → 两环境。产出结果不变(两环境本就同构桌面 MCNSkill项目/)。
- **改动范围(20+ 文件,续 v2)**:
- 判定层:4 份 路径配置.md(V1.0/Lite1.0 + dou-analysis + video-prompt)
- SKILL 层:V1.0/Lite1.0/dou-analysis/video-prompt/data-insight 的 SKILL.md
- 标准库:5 份 路径引用规范.md(三种机器→两种机器)+ 创作流程规范 + 11_脚本检查和诊断 + 4_账号设定解析和确认 + MCP_工具调用规范 + 自动化任务清理规范
- 数据源层:fetch_week_ranks.py(删 .dsh 分支代码)、dsh-data.js(删 isDshDeploy 分支)、data-pages.js、douyin-{rise-ranking,weekly-surge,content-surge,daily-hot}/SKILL.md、抖音数据规则.md
- 账号分析层:账号数据分析方法 + feature/01,03,04,06 + 铁律避坑规则/账号信息获取执行避坑 + 浏览器搜索抖音账号操作规范(159/175)
- README:references-add/README + dou-analysis/video-prompt 的 references(-add)/README
- **清理「dsh 原库」过时描述**:dou-analysis SKILL.md 52 行 + 铁律避坑 32/34 行 + feature/01 16 行 + dsh-data.js 13 行注释(dsh 原库已下线,工作台 DB 唯一读写副本)。
- **残留核对**:全树扫描 .dsh/dshworkspace/dsh 部署/dsh 环境/dsh_MCNProject/②dsh/③用户环境 —— 当前有效规则零残留,剩余全为豁免(变更记录历史行 +「09-10 起 dsh 环境已废弃」说明 + 路径引用规范用户级路径示例 {主目录}/.dsh/...)。
- **验证**:dsh-data.js `--check` OK;fetch_week_ranks.py `py_compile` OK;工作台重启 HTTP 200。
- **沉淀**:变更日志新增 09-10 dsh 废弃条目;V1.0 SKILL.md frontmatter last_change 更新。
- **踩坑**:并行 Edit 同一文件(浏览器搜索抖音账号操作规范.md 159/175 行)发生写竞态,175 行修改丢失 → 串行补做。教训同 #016「同文件多处修改必须串行」。
## 工作台配置收敛:端口/DB/目录单源化(config.json + load-config.js)
- **触发**:用户问「这些设定能否用配置,所有文件和脚本都引用,避免有调整时到处修改」。确认:只收敛代码层 + config.json 放 mcn-work-shop 内。
- **新增**:`mcn-work-shop/config.json`(port/dbName/outputRoot/businessDataDir/extraRoots)+ `mcn-work-shop/load-config.js`(读取唯一入口 + 默认值唯一来源)。
- **改动**:
- `server.js`:`CFG = loadConfig()`;`PORT_BASE = argv[2] || CFG.port`(argv 仍优先);`DEFAULT_ROOT` 用 `CFG.outputRoot`;DB 用 `CFG.dbName`;`saveConfig` 合并写盘保留其它字段;错误提示 dbName 动态化。
- `dsh-data.js`:删本地 loadCfg,`DSH_DB` / 榜单目录改 CFG。
- 4 个辅助 .cjs(build-create-prompts / submit-review-tasks / poll-create-tasks / poll-review-tasks):DB / API 端口 / prompt 内 dbName 字面量 → CFG 动态。
- `public/data-pages.js`:新增 `const BASE = location.origin`,SKILL_HINT_VIDEO_PARSE / SKILL_HINT_ANALYZE / saveHintOf 里 3 处 `http://localhost:8900` 改 BASE(前端读不到 server 端 config.json,用 location.origin 让回调端口自动跟随当前访问地址)。
- `start.bat`:端口用 `for /f` + load-config 读 config.json,不再写死 8900。
- **★踩坑(重要)**:`load-config.cjs` 用 `.cjs` 扩展名时 `require('./load-config')` 无扩展名解析 MODULE_NOT_FOUND(Node 不把 .cjs 当无扩展 require 目标)→ 改名 `load-config.js`(无 package.json,默认 CommonJS)即自动生效。**node --check 只查语法不执行 require,此坑运行时才暴露**。
- **验证**:8 文件 `--check` 全通过;`loadConfig()` 正确读回全字段;工作台重启 HTTP 200(端口/产出根均从 config 读)。
- **沉淀**:变更日志新增配置收敛条目;V1.0 SKILL.md frontmatter last_change 更新。
## 配置收敛后功能验证 + start.bat 修复
- **实测功能正常**:重启后逐个探测依赖 config 的接口——`/api/roots` 返回 `defaultRoot=C:\Users\maidou\Desktop\MCNSkill项目`(CFG.outputRoot 生效)、`/api/dsh/stats`(accounts 7 / analyses 12 / rewrites 27 / reviews 11,CFG.dbName 生效)、`/api/dsh/accounts`、`/api/dsh/rewrites`、`/api/dsh/ranking`(250 条榜单,CFG.businessDataDir 生效)全部 200 且数据正确。前端 index.html 引用 5 个静态资源(echarts/app.js/help-guide.js/data-pages.js/style.css)**5/5 可用**;`/data-pages.js` 200、含 `const BASE = location.origin`、无 `localhost:8900` 残留。**★静态资源是根映射 `/data-pages.js`,不是 `/public/data-pages.js`(后者 404,曾误判为故障)**。
- **★start.bat 既有 bug(非本次引入,顺手修复)**:写死的 managed node 路径 `...\versions\22.22.2\node.exe` **已不存在**(实际 `22.22.2-3`)→ 原逻辑静默回退系统 node。改为 `for /d` 遍历 `%USERPROFILE%\.workbuddy\binaries\node\versions\*` 自动探测,版本升级免改脚本。
- **★bat 内嵌 JS 的坑**:`for /f ... in (`node -e "console.log(require('./load-config')...)"`)` 被 cmd 解析截断报错(引号/括号/单引号嵌套)→ 端口读到的是**兜底值而非真实值**(表面 8900 看着对,实则没读 config)。改为独立脚本 `get-port.js`(`console.log(require('./load-config').loadConfig().port)`),bat 只执行简单命令 `"%NODE_EXE%" get-port.js`。
- **实证方法**:临时把 config.json port 改 8905 → start.bat 输出 `PORT=8905`(证明真读 config,非兜底)→ 改回 8900。区分技巧:测试脚本里把兜底值写成 `FALLBACK8900`,输出 8900 才说明是真读。
- **踩坑(工具层)**:用 node `-e` 生成 .bat 时,路径里的 `\n`(如 `\binaries\node`)**被 JS 当换行转义**,生成坏 bat 报「命令语法不正确」。生成 bat 要用 Write 工具写后,再用 node 把 LF 统一转 CRLF(`t.replace(/\r?\n/g,'\r\n')`)——cmd 对 LF 换行解析异常。
## 工作台 UI 规范文档梳理
- **产出**:`mcn-work-shop/docs/工作台UI规范.md`(新建)。全部从 style.css(824 行)/app.js/data-pages.js/index.html **提取现状基线**,非凭空制定。
- **结构**:技术基线 → 设计语言 → 设计 token → 布局框架 → 页面路由(8 个) → 组件规范(12 类) → 关键交互约定 → 已知坑(9 条) → 迭代流程 → 文件索引。
- **★关键事实(后续 UI 迭代直接查文档,此处留索引)**:
- 色彩:10 个 `:root` 变量(bg/panel/border/text/dim/primary/primary-soft/accent/danger/shadow)+ 扩展色(表头 #f6f8fa、链接 #4a90e2、主色 hover #2a5fd0、文件类型色 md紫/json红/xlsx绿/txt灰)
- 圆角阶梯 6/8/10/12/14px;z-index:顶栏 10 / 弹窗遮罩 100 / Toast 200(表头 sticky 为 1)
- **滚动铁律**:`html,body{overflow:hidden}` + `main#view{height:calc(100vh - 55px)}`——**55px 是实测值**(padding20+行高34+边框1),写 54px 底部溢出 1px 触发第二层滚动条
- **榜单表格必须 `table-layout:fixed`**:auto 布局下 100% 表格富余空间按比例摊薄,**min-width 压不住**
- **榜单三 tab 行高必须统一**:表格 td 固定 42px(功能列 btn-sm 撑高,其他 tab 本仅 34.7px)、首页预览行 min-height 40px,否则切换整表跳动
- **两套同名 `.tabs/.tab` 并存**:顶栏胶囊式(L38-45,active 蓝底白字)vs 数据页下划线式(L520-523,active 蓝字+蓝下划线,**后者覆盖前者**)→ 新增页须确认拿到哪套
- `.modal-mask[hidden]` 必须显式 `display:none`(`.modal-mask` 是 flex,会覆盖 HTML hidden 属性→弹窗关不掉)
- 数值列:右对齐 + `font-variant-numeric: tabular-nums`(不加会导致翻页时数字左右跳)
- 图标:`.ic{vertical-align:-0.18em}`(去掉会与文字基线错开);`svgIcon()` Lucide 24×24 stroke2 + `ICON_COLORS` 功能色映射
- 字段对齐用 **grid 列轨道共享**(R44/R45:每行独立 flex 会被内容长短顶偏;超宽字段如「账号定位」要移出共享网格,否则把轨道撑成等宽 232.5px 大片留白)
- 静态资源**根映射** `/data-pages.js`(`/public/xxx` 一律 404,曾误判为故障)
## 榜单更新「周不全」问题诊断(未修复,待用户确认)
- **用户现象**:点「更新榜单」后,周榜数据期不全(相关周缺)。
- **★真相:本次更新本身是成功的**——会话 `624d7a3e`(标题「更新榜单数据」,cwd=mcn-work-shop)实际执行 8 次 API、落盘 8 个 JSON:视频热榜/点赞榜各 08-31(2 次,`fetch_week_ranks.py --board both`)、账号日榜 09-08(1 次,`fetch_rank.py --period day`)、账号周榜 5 赛道各 08-31(5 次)。无缓存跳过、无失败。
- **根因(设计缺口,非执行失败)**:`SKILL_HINT_RANKING` 的语义是**只抓「最新一期」+ 幂等跳过已存在**,**没有任何回溯补齐缺失期次的逻辑** → 历史缺口永远补不上。
- **实测期次现状(`/api/dsh/ranking` 返回的 dates)**:
| board | 日期列表 | 说明 |
|---|---|---|
| week 账号周榜 | 08-31/08-24/08-17/08-10 | 4 周完整(历史累积) |
| hot 视频热榜 | 08-31/08-24 | **缺 08-10、08-17**(09-03 才上线该榜) |
| likes 点赞榜 | 08-31/08-24 | **缺 08-10、08-17** |
| day 账号日榜 | 09-08/09-02 | **缺 09-03~09-07 共 5 天**(期间没点更新) |
→ 用户切 tab 时看到 week 有 4 周而 hot/likes 只有 2 周,即"没有所有相关周"。
- **脚本能力是够的(没用上)**:`fetch_week_ranks.py` 有 `--date`(周标号周一)与 `--force`;`fetch_rank.py` 有 `--date`(目标日期)。完全可指定历史期次补齐,但 prompt 里没要求。
- **补齐成本估算**:hot 2 周 + likes 2 周 + day 5 天 = **9 次红狐 API**(按次计费,需用户确认)。
- **附带发现(非 bug,设计如此,易被误认为数据少)**:`dsh-data.js` 的 `RANK_KEEP=[生活记录,生活vlog,小剧场,亲子,美食,旅行]`,`board!=='week'`(即 day/hot/likes)在**条目层**做白名单过滤→文件 50 条被筛成:hot 28、likes 24、**day 仅 10**。日榜只剩 10 条最反直觉。修改口径需动 `dsh-data.js:490-495`。
- **★★最终结论(红狐官方规则,账号周榜无问题)**:用户追问「为什么没有 9 月 7 号周榜」。实测 `fetch_rank.py --period week --date 2026-09-07` → **API 报错 code=3203**,红狐返回原文:
> 查询结果为空…常见原因:①日期未到更新时间——**日榜每日 20:00 后可查昨日、周榜每周一 20:00 后可查上周**、月榜每月 1 号后可查上月;②周榜应传该周周一…
→ **账号周榜(week)最新可用期就是上一周**,09-07 周要等 **09-14(下周一)20:00** 后才出榜。所以 09-10 时最新一期为 08-31 是**正确的**,此前会话「本周未走完」的解释**结论正确**(措辞可更精确为"红狐周榜周一 20:00 后才出上周完整榜")。
- **热榜/点赞榜是另一套 API,口径不同(可滚动)**:`fetch_week_ranks.py --date 2026-09-07` 实测**能返回完整 50 条**(dateStart=09-07/dateEnd=09-13,首条发布 09-07 22:59)。因其为作品榜滚动聚合,未结束的周也有数据;但这是**本周至今的滚动值**,明天再取会变。
- **默认统一取上一周是刻意对齐(非 bug)**:四个榜若 hot/likes 取本周、week 取上周,会出现**同期不同期号**(热榜 09-07 / 账号榜 08-31)造成困惑。故 `last_full_week()` 与 `_get_smart_offset()=1` 统一回退一期,是与账号榜(受 API 硬限制)对齐的结果。
- **结论:功能本身没问题**,是红狐数据规则所致。若用户坚持要热榜"本周滚动值",需接受与账号榜不同期,可单独加(用 `--date <本周一>`)。
- 用 `fetch_week_ranks.py --board hot --date 2026-09-07`(输出到临时目录 `D:/tmp/ranktest-0907`)→ **成功返回完整 50 条**(72.3KB,dateStart=2026-09-07 / dateEnd=2026-09-13,首条发布时间 2026-09-07 22:59:51)。
- 即**红狐 API 对「进行中的本周」照样返回完整 50 条**,不是"没出榜"。
- 真正原因:**脚本默认策略主动回退一期**——`fetch_week_ranks.py` 的 `last_full_week()`(本周一 −7 天)与 `fetch_rank.py` 的 `_get_date_range(offset=1)`(注释明写"默认 1(上一期)")。两者都支持抓本周:`fetch_week_ranks.py --date <本周一>`;`fetch_rank.py` 关键词映射里 `("本周", 0, ...)` 即 offset=0,或 `--date 2026-09-07`。
- **待用户决策**:是否把默认口径从「上一完整周」改为「本周」。注意本周数据是**滚动的**(每天都在变,今天抓的 09-07 周榜明天会更新),原设计选上一完整周可能是为归档稳定性。可选:①本周优先(新,滚动)②保留上周为归档 + 额外抓本周标「进行中」③保持现状由用户手动 `--date` 补。
- **赛道筛选正常**(用户已确认不是问题):`RANK_KEEP` 白名单过滤符合设计,勿动。
## 榜单期号显示改为区间(已实施)
- **诉求**:用户理解 08-31 已是最新周后,要求**改展示方式**——期号 `2026-08-31` 易被误读成"仅 8/31 单日",实际覆盖 08-31~09-06。
- **改动**:
- `dsh-data.js`:新增 `collectRanges(board, files)`(读落盘文件的 `dateStart/dateEnd`,按 date 去重取首个文件)→ `rankingMeta` 与 `getRanking` 均返回 `ranges: {date:{start,end}}`(empty 也补 `ranges:{}`)。**不前端推算周日**,用文件真实区间更准(日榜 start=end)。
- `app.js`:新增全局 `fmtRankPeriod(date, ranges)`(定义在 `fmtSize` 后,data-pages.js 可直接用——同全局脚本作用域);`_rankPreview` 加 `ranges`,首页预览日期标签改用它。
- `data-pages.js`:`mkState` 加 `ranges`、`loadMeta` 存 `s.ranges`、榜单页 toolbar 日期改 `fmtRankPeriod` 并加 `rank-period` 类。
- `style.css`:`.rank-date-label` min-width 62→**104px**(容纳"08-31 ~ 09-06",防切期抖动);新增 `.rank-period{min-width:104px;text-align:center;display:inline-block;white-space:nowrap}`。
- **显示结果**:周榜「08-31 ~ 09-06」、日榜「09-08」(单日不显区间)、缺 ranges 时兜底原期号。
- **格式微调(同日)**:用户偏好斜杠 → `mm` 增加 `.replace(/-/g,'/')`,最终显示**「08/31 ~ 09/06」/ 日榜「09/08」**。app.js、style.css 版本号 → `20260910b`。
## 复盘诊断页两个问题定位(未修,待确认)
- **问题①:叙事逻辑 60 分标黄,但问题清单全绿**(真 bug,且连带一个)。
- **根因=落库字段契约不匹配**:`submit-review-tasks.cjs` 的 prompt 只写 `issues_json=问题清单 JSON`,**未规定 schema** → 模型自由发挥。
- 实测 `script_review.id=13`(total=60,用户所看)原始 issue:
`{"sev":"high","dim":"叙事逻辑","loc":"场8…","issue":"时间线矛盾…","fix":"改成…","step":"S7…"}`
- 前端 `data-pages.js:2046/2108/2201` 读的是 `{severity:'高'/'中', issue, suggestion}`:
- `sev` vs `severity` → 读不到 → **兜底绿色**(故全绿)
- `"high"` vs `'高'` → 取值语言也不同
- `fix` vs `suggestion` → **修改建议同样读不到、没显示**(连带 bug,用户尚未提及)
- 数据佐证:6 条记录里 id=13/11/9/8 的 severity 全空,仅 id=12("低")、id=10("中"2/"低"6) 有中文值 → **模型输出不稳定**,schema 必须固化。
- 评分条颜色是另一套:`diagScoreColor = v>=75?'#3a9e6d':v>=60?'#d99a2b':'#d9534f'`(60→黄),与 severity 无关,故出现"60分黄+问题全绿"的矛盾观感。
- **问题②:分镜提示词没有生成按钮**(非 bug,设计限制)。
- 该脚本 `rewrite_log.id=38` 的 **video_id=null(原创选题)**。
- `data-pages.js:2290-2295`:`vid>0` 才渲染「生成分镜」按钮;否则显示"该脚本为原创选题(无关联视频),暂不支持分镜提示词"。
- 后端 `dsh-data.js:302` 查询 `storyboard_log WHERE video_id=?` —— **只按 video_id**,原创选题查不到。
- 若要支持:`storyboard_log` 本身有 `rewrite_id` 字段,需改后端查询支持 rewriteId + 前端入口放开 + 生成 prompt 改落 rewrite_id。
- **修复方向(待用户定)**:①在 `submit-review-tasks.cjs`(及复盘弹窗 prompt)固化 issues_json schema(建议沿用技能规范中文「高/中/低」+ 字段名 severity/issue/suggestion/dim/loc/step);②前端加字段容错(同时兼容 sev/fix/英文值),让已有脏数据也能正确显色与显示建议。
- **验证**:3 文件 `--check` 通过;四榜 API 均返回 ranges(week 4 期/hot·likes 2 期/day 2 期,区间正确);版本号 bump style.css+app.js→`20260910a`、data-pages.js→`20260910c`;重启 HTTP 200。
## 清理「dsh 已废弃」类废话(用户指出)
- **触发**:用户指出 `SKILL_HINT_RANKING` 里「09-10 起 dsh 环境已废弃,无 dsh 分支)」属于废话——环境都没了,prompt/规则里再讲"何时废弃、曾有几个环境"对当前执行毫无价值。
- **★分类处置原则(可复用)**:
1. **prompt / 规则正文里的历史叙述 → 全删**(模型与执行者只需要"现在怎么做")
2. **代码注释里描述变更动作**(如"09-10 去掉 dsh 分支:…")→ 删,只留定稿日期
3. **"无 dsh 分支"这类陈述不存在的东西** → 删(陈述一个不存在的事物=噪音)
4. **文末「变更记录」历史区 / 变更日志.md → 保留**(append-only 归档,是正常文档元数据,不是噪音)
5. **禁止性护栏**(如"不要再按 .dsh 路径段做分支判断")→ **精简保留**:去掉日期与"已下線"经过叙述,只留"禁止做什么"
- **改动 16 个文件约 18 处**:data-pages.js(prompt)、dsh-data.js(注释)、3 份 路径配置.md(V1.0/Lite1.0/video-prompt,删变更公告+"无 dsh 分支"+护栏精简)、V1.0+Lite1.0 的 references-add/README.md、mcn-video-prompt 的 SKILL.md + references/README.md + references-add/README.md、mcn-dou-analysis SKILL.md + references-add/README.md(2)、douyin-weekly-surge SKILL.md、MCP_工具调用规范.md、mcn-data-insight SKILL.md、创作流程规范.md。
- **验证**:dsh-data.js / data-pages.js `--check` 通过;版本号 bump 至 `?v=20260910b`;工作台重启后接口全 200(榜单日期新增 2026-08-31 一期,读取正常);`/data-pages.js` 字符数 145080→145053(正好少 27 = 删掉那句)。
- **注意**:data-pages.js 里剩余 44 处 `dsh` 全是 `/api/dsh/*` 接口路径与代码标识符(工作台 API 命名空间,与"dsh 部署环境"无关),**不可动**。
+15 -7
View File
@@ -1,14 +1,19 @@
# MCNVideo AI 项目长期记忆(09-03 三次压缩;权威源=SKILL.md+references,此处仅索引+红线)
# MCNVideo AI 项目长期记忆(09-10 四次压缩;权威源=SKILL.md+references,此处仅索引+红线)
## 环境判定 + 产出落盘(08-26 定稿)
- 两步:①本机无 `D:\AgentSkill`=③用户环境;有→②技能在 `D:\AgentSkill\` 下=①开发机、含 `.dsh`=②dsh。单一 `.dsh` 锚点会把用户环境误判为开发机(P0 已修)
## ★仓库路径(09-10 迁移)
- **本地路径:`D:\AI技能\mcn-short-video`**(原 `D:\AgentSkill\mcn-video-script`,`D:\AgentSkill` 已不存在);远程 `[email protected]:maogeigei/mcn-short-video.git`
- 技能软链 `C:\Users\maidou\.workbuddy\skills\短视频工作台` = junction → `D:\AI技能\mcn-short-video\project\短视频脚本创作\V1.0`
## 环境判定 + 产出落盘(09-10 判据重构 v2 · 仓库特征判定)
- **两步**:①技能路径含 `.dsh` 路径段 → ②dsh 部署环境;②不含 `.dsh` → `git -C {技能目录} rev-parse --show-toplevel` 返回仓库根 = ①开发机、失败 = ③用户环境(无 git 兜底向上找 `.git`)。**必须 `.dsh` 先判**(`~/.dsh` 自身是 git 仓库)。**锚点禁写死盘符**(原 `D:\AgentSkill` 锚点随仓库迁移失效,曾致开发机误判为用户环境)
- 产出:①③=桌面 `MCNSkill项目/{账号名}/`;②dsh=`D:\dshworkspace\`。权威=`references-add/路径配置.md`(运行时优先)
## 核心技能
- **短视频工作台**(09-03 由「短视频脚本创作」更名,对外统一名)源=`project/短视频脚本创作/{V1.0,Lite1.0}`(**repo 目录保留旧名**,改的只是注册/显示名);V1.0 name=短视频工作台 含 MCP/Dify,Lite1.0 name=短视频工作台 Lite 去接口。**★软链事实(09-03)**:`C:\Users\maidou\.workbuddy\skills\短视频工作台`=junction→`D:\...\project\短视频脚本创作\V1.0\`(同一文件 md5 一致,改源即自动同步;**所有 SKILL_HINT_*/skills 参数用新注册名**)。**★09-03 junction 两次失效**:被替换成空目录(11:11,疑似宿主扫描/迁移)致 SKILL_HINT_* 加载失败;征兆=技能加载失败先查此路径 children==0;修法=node `unlinkSync` 删旧链/空目录 → PowerShell `New-Item -ItemType Junction`(目标路径必须已不存在否则 exit 1;junction 不能用 rmdir,报 Not a directory)。**同步策略(09-07 更新)**:只改 V1.0 源;Lite1.0 默认不动,术语统一类不同步(如需同步由用户手动发起);dsh 技能副本(short-video-script)**项目不存在(09-07 用户确认),已废弃,勿再考虑**。🧹清理自动化按 `references/接口调用/自动化任务清理规范.md`(禁 recurring/未来/running/QUEUED,走宿主软删)
- **MCN工作台(Web 品牌=短视频工作台;09-04 由 R43 曾用名 MCN热点雷达 改回)**(仅 V1.0)`V1.0/mcn-work-shop/`(零依赖 Node,**端口 8900**,argv 可指定;8899 旧值错误)。AI 任务=`POST /api/run` 写 automations(once+显式 next_run_at+5s)→轮询 `/api/run/status`;调度实测=工作台 `自动化任务调度机制.md`(scheduled_at 须 +60s、并发≈3 QUEUED、改 server.js 须重启)。**★创作/改写类 prompt 必须带 SKILL_HINT_CREATE 前缀**(漏=模型跳步直出;新增入口必查)。AI选题 `/api/ai/topics`(Dify recall~50s;key 内置 `scripts/MCN_CYLG_API.py`;curl 中文被 CDN 拦用 Python)。复盘页 prompt 按 mcn-script-review。UI 改完必重开页面带 `?v=YYYYMMDDx`
- **短视频工作台**(09-03 由「短视频脚本创作」更名,对外统一名)源=`project/短视频脚本创作/{V1.0,Lite1.0}`(**repo 目录保留旧名**,改的只是注册/显示名);V1.0 name=短视频工作台 含 MCP/Dify,Lite1.0 name=短视频工作台 Lite 去接口。**★软链事实(09-10 更新)**:`C:\Users\maidou\.workbuddy\skills\短视频工作台`=junction→`D:\AI技能\mcn-short-video\project\短视频脚本创作\V1.0\`(09-10 因仓库迁移重建;同一文件 md5 一致,改源即自动同步;**所有 SKILL_HINT_*/skills 参数用新注册名**)。**★09-03 junction 两次失效**:被替换成空目录(11:11,疑似宿主扫描/迁移)致 SKILL_HINT_* 加载失败;征兆=技能加载失败先查此路径 children==0;修法=node `unlinkSync` 删旧链/空目录 → PowerShell `New-Item -ItemType Junction`(目标路径必须已不存在否则 exit 1;junction 不能用 rmdir,报 Not a directory)。**同步策略(09-10 更新)**:只改 V1.0 源;Lite1.0 默认不动,如需同步由用户发起(09-10 用户授权同步了环境判定口径 + dou-analysis 依赖修正);**★Lite1.0 目录下无 `subskills/`(实测)→ 未内置 mcn-dou-analysis**;dsh 技能副本(short-video-script)**项目不存在(09-07 用户确认),已废弃,勿再考虑**。🧹清理自动化按 `references/接口调用/自动化任务清理规范.md`(禁 recurring/未来/running/QUEUED,走宿主软删)
- **MCN工作台(Web 品牌=短视频工作台;09-04 由 R43 曾用名 MCN热点雷达 改回)**(仅 V1.0)`V1.0/mcn-work-shop/`(零依赖 Node,**端口 8900**,argv 可指定;8899 旧值错误)。AI 任务=`POST /api/run` 写 automations(once+显式 next_run_at+5s)→轮询 `/api/run/status`;调度实测=工作台 `自动化任务调度机制.md`(scheduled_at 须 +60s、并发≈3 QUEUED、改 server.js 须重启)。**★创作/改写类 prompt 必须带 SKILL_HINT_CREATE 前缀**(漏=模型跳步直出;新增入口必查)。**★UI 规范文档=`mcn-work-shop/docs/工作台UI规范.md`**(09-10 梳理:设计 token/布局/8 页面路由/12 类组件/9 条已知坑;UI 迭代先查此文件)。关键铁律:`main#view` 高 `calc(100vh-55px)`(写 54 会溢出 1px 触发第二层滚动条)、榜单表格必须 `table-layout:fixed`、榜单三 tab 行高统一(表格 42px/预览 40px)、两套同名 `.tabs/.tab`(数据页下划线式覆盖顶栏胶囊式)、`.modal-mask[hidden]` 须显式 `display:none`、数值列 `tabular-nums`、`.ic` 的 `vertical-align:-0.18em`。AI选题 `/api/ai/topics`(Dify recall~50s;key 内置 `scripts/MCN_CYLG_API.py`;curl 中文被 CDN 拦用 Python)。复盘页 prompt 按 mcn-script-review。UI 改完必重开页面带 `?v=YYYYMMDDx`。**★配置收敛(09-10)**:端口/DB名/产出根/榜单目录单源化到 `mcn-work-shop/config.json`,代码层统一经 `load-config.js`(唯一入口+默认值)读,改设定只改 config.json;前端 data-pages.js 用 `location.origin` 动态取回调地址(前端读不到 server 端 config.json);start.bat 经 `get-port.js` 读端口(`for /f` 内嵌 node -e 会被 cmd 截断,必须走独立脚本);**start.bat 的 managed node 用 `for /d` 遍历 versions 自动探测**(写死版本号会随升级失效)。**坑1**:`.cjs` 的 `require('./x')` 无扩展名会 MODULE_NOT_FOUND → 配置模块须 `.js`。**坑2**:静态资源是根映射 `/data-pages.js`,`/public/xxx` 一律 404-config 读端口。**坑**:`.cjs` 文件 `require('./load-config')` 无扩展名会 MODULE_NOT_FOUND(Node 不把 .cjs 当无扩展 require 目标)→ 配置模块须用 `.js` 扩展名
- **mcn-dou-analysis** 源=`V1.0/subskills/mcn-dou-analysis/`、dsh 副本同构。F1信息/F2视频表(13列xlsx)/F3设定/F4分析/F5解析/F6设定卡片SVG。红线:达人表15列、TOP6(点赞+分享降序)、≥60条停、29赛道枚举、浏览器 9223 调试实例+标签页复用、**身份标识禁从 DB 回填**(留空待浏览器回填)、源技能只读单向同步。F6 配色按调性分深浅两系(深=深沉催泪;浅=明快甜美如玫瑰金 #D6A3A9→#9A5563;「治愈」回读情绪锚点)。browser-harness 执行 F1/F2 前必检。**★产物入库(09-01)**:落盘后须 `POST /api/import/account` 直写 mcn-plugin.db(analysis.json→account_video_analysis、content.json→account_video_source、设定/分析 md→account_persona/account_analysis、xlsx→account_videos);aweme_id 匹配删全角标点+下划线转空格、幂等删旧保新;选题读最新 3 条解析拼 prompt
- **mcn-data-insight(09-03)**:纯 11 个红狐系数据获取子技能包;`dou-analysis/subskills/nuwa-skill-main/` 是另一账号方法论勿混。**榜单即查即缓存落盘**(红狐计费+同日快照→先 ls 命中免 API);桌面 `MCNSkill项目\三方数据\红狐数据\抖音榜单\`、dsh=`D:\dshworkspace\抖音榜单\`;文件名不加数据源前缀
- **mcn-data-insight(09-03)**:纯 11 个红狐系数据获取子技能包;`dou-analysis/subskills/nuwa-skill-main/` 是另一账号方法论勿混。**榜单即查即缓存落盘**(红狐计费+同日快照→先 ls 命中免 API);桌面 `MCNSkill项目\三方数据\红狐数据\抖音榜单\`、dsh=`D:\dshworkspace\抖音榜单\`;文件名不加数据源前缀。**★红狐周期规则(09-10 实测,code=3203 原文)**:日榜每日 20:00 后可查昨日、**周榜每周一 20:00 后可查上周**、月榜每月 1 号后可查上月 → 账号周榜(week,`fetch_rank.py`)最新可用期恒为上一周,本周须等下周一 20:00,故"刷新拿最新周期"得到上周**属正常非 bug**。热榜/点赞榜(hot/likes,`fetch_week_ranks.py`)为另一套作品榜 API,**未结束的周也能返回 50 条滚动值**(`--date <本周一>` 可取,明天再取会变);脚本 `last_full_week()` 默认统一回退一期是为与账号榜对齐、避免"同期不同期号",**勿随意改默认**
- **短视频分镜脚本** `project/短视频提示词生成/`:F1反推图/F2反推视频/F3生图/F4分镜(>5s 时间戳分段;模板 A剧情/B Vlog/C旁白/D产品/E氛围)
## 创作红线(权威=SKILL.md+references/创作流程规范.md)
@@ -18,6 +23,8 @@
## 工作方式红线
- 治本原则:从根因修机制→重执行验证;禁补数据/临时绕过
- **★锚点禁写死盘符(09-10)**:环境/路径判定一律用**仓库特征**(`.dsh` 路径段、git 检出),禁用 `D:\AgentSkill` 类盘符地标;「允许项」≠不会坏,仓库/环境迁移时须主动复核豁免写法
- **盘点类任务**:先确认扫描范围无遗漏(09-10 首轮 `skipDirs` 误含 `references` → 漏扫 24 处);下「违规」结论前必先读对应规范文件按规范分类(09-10 曾把 37 处豁免锚点误报为违规)
- 改完必重开(`?v=` 防缓存);排查顺序=先重开排除旧缓存再改代码
- 逐条修复:读一条→改一条→验证一条,禁批量读;批量后复查式修复
- git:2h push 一次本地随时 commit;push 前 fetch;代理 socks5://127.0.0.1:10800 破坏连接需清 local proxy;杀 node 用 PowerShell `Stop-Process`;push 后 origin 跟踪引用可能被沙箱吞需重建 refs/remotes;**★`git rm -r` 大范围被 SIGTERM 中断=只删盘不写索引+index.lock**→删 lock+checkout 恢复+纯 `rm -rf`+`git add -A -- 精确路径`
@@ -26,7 +33,8 @@
- **Chrome 不主动关闭**(9223/用户 Chrome/daemon 连接保留;只回收自启临时实例)
- .dsh 单向只读:禁反向写回(除非用户明确授权)
- 三层沉淀:治本(技能文件)→失误(append 日志)→记忆;日志 append-only;frontmatter updated_at/last_change
- **★清理「XX已废弃」类废话的判据(09-10)**:环境/规则废弃后,prompt 与规则正文里"何时废弃、曾有几个分支、路径已不存在"这类历史叙述=噪音→**全删**(执行者只需"现在怎么做");代码注释里的变更动作叙述→删,只留定稿日期;"无 X 分支"这类陈述不存在事物的→删。**保留两类**:①文末「变更记录」历史区/变更日志(append-only 归档);②禁止性护栏(精简为"禁止做什么",去掉日期与下线经过)。例:dsh 环境废弃后清理 16 文件 18 处(注意 `/api/dsh/*` 是工作台 API 命名空间,与 dsh 环境无关,不可动)
- 生成网页必先读 taste-skill 与 impeccable
## 历史事件(摘要)
07-17 大炮方法;07-29 短剧库 V5.0;07-30 短视频库 V2.0;08-11/12 素材库扁平化+产物链铁律;08-16 赛道 8→27;08-17 三态创作;08-20 分镜落库;08-21 Lite1.0;08-22 .dsh 只读;08-25 更名 mcn-video-script;08-26 路径重构+两步模型;08-27 dou-analysis+分镜解耦+工作台;08-31 数据预览条/调度器结论;09-01 产物入库+清理规范;09-02 AI创作弹窗 v2-v4+Chrome 不关红线;09-03 data-insight 瘦身、软链事实+junction 二次失效修复+git rm 坑、技能更名「短视频脚本创作→短视频工作台」(repo 路径不变);09-04 主 SKILL.md 子技能索引(6 子技能相对路径)+browser-harness 环境修复+**全量路径审计**:源技能 maidou/MCNVideo AI/废弃目录名(当前规则行)零残留;豁免三类=禁止性规则示例行、变更记录历史行、references-add/路径配置.md;dsh short-video-script SKILL.md 为刻意差异化(324 行无 subskills)不同步
07-17 大炮方法;07-29 短剧库 V5.0;07-30 短视频库 V2.0;08-11/12 素材库扁平化+产物链铁律;08-16 赛道 8→27;08-17 三态创作;08-20 分镜落库;08-21 Lite1.0;08-22 .dsh 只读;08-25 更名 mcn-video-script;08-26 路径重构+两步模型;08-27 dou-analysis+分镜解耦+工作台;08-31 数据预览条/调度器结论;09-01 产物入库+清理规范;09-02 AI创作弹窗 v2-v4+Chrome 不关红线;09-03 data-insight 瘦身、软链事实+junction 二次失效修复+git rm 坑、技能更名「短视频脚本创作→短视频工作台」(repo 路径不变);09-04 主 SKILL.md 子技能索引(6 子技能相对路径)+browser-harness 环境修复+**全量路径审计**:源技能 maidou/MCNVideo AI/废弃目录名(当前规则行)零残留;豁免三类=禁止性规则示例行、变更记录历史行、references-add/路径配置.md;dsh short-video-script SKILL.md 为刻意差异化(324 行无 subskills)不同步;**09-10 仓库迁移至 `D:\AI技能\mcn-short-video`+软链重建+环境判定锚点重构为「仓库特征判定」(21 文件统一口径)+工作台 cwd/BROWSER_LOCK 改 ROOT 动态推导 + `SESSION_CWD_PATTERN` 放宽为 `%mcn-work%` + Lite1.0 dou-analysis 依赖修正(Lite 无 subskills,未内置)+**dsh 部署环境废弃(三环境→两环境,判据 v3 一步 git 检出,20+ 文件去 dsh 分支)+工作台配置收敛(config.json 单源化,端口/DB/目录经 load-config.js 统一读取)**
@@ -60,13 +60,13 @@ agent_created: true
## 外部技能依赖:账号解析(mcn-dou-analysis)
账号解析功能(达人账号信息获取与登记、视频列表抓取、爆款视频拆解分析、人设卡生成、账号数据分析)由独立技能 **mcn-dou-analysis** 提供,按三环境两步判定解析路径:
账号解析功能(达人账号信息获取与登记、视频列表抓取、爆款视频拆解分析、人设卡生成、账号数据分析)由独立技能 **mcn-dou-analysis** 提供(09-10 修正:原「盘符锚点两步判定 + 技能包外绝对路径」规则**已废弃**——独立仓库不存在,且写死盘符的锚点随仓库迁移必然失效):
- **第一步:查本机是否存在 `D:\AgentSkill`**
- **不存在 → 用户环境**(技能装在用户机器上,路径不限、不一定含 `.dsh`)→ 在本技能 `subskills/` 目录中寻找 `mcn-dou-analysis`
- **存在 → 第二步:看本技能自身路径**
- 不含 `.dsh`(源技能在 `D:\AgentSkill` 下)→ **开发机** → 使用 `D:\AgentSkill\mcn-dou-analysis`
- 含 `.dsh`(如 `C:\Users\{用户名}\.dsh\skills\…`)→ **dsh 部署环境** → 使用 `D:\AgentSkill\mcn-dou-analysis`
- **Lite1.0 当前未内置该技能**:本版本目录下无 `subskills/`(`ls` 实测),故 `subskills/mcn-dou-analysis` 在本版本**不存在**
- **需要账号解析时**:
- 优先改用完整版技能 **短视频工作台**(V1.0)——其 `subskills/mcn-dou-analysis/` 随技能分发,路径相对技能根,跨机器一致
- 若确需在 Lite1.0 内提供该能力,应先把 `mcn-dou-analysis` 同步进 `Lite1.0/subskills/`,再按相对路径 `subskills/mcn-dou-analysis/SKILL.md` 调用
- **禁止**再用任何**盘符绝对路径**定位该技能(锚点统一改用仓库特征判定)
---
@@ -249,7 +249,7 @@ agent_created: true
**用户确认:** Gate-6(最终确认),确认后完成交付。
**最终交付物(确认后自动生成):** 创作回顾.html — 整合全流程各阶段产出的可视化回顾页,命名规则 `脚本{编号}_{脚本标题}_创作回顾.html`,存放于产出根目录下 `{账号名}/` 账号文件夹根目录(dsh 部署环境:`D:\dshworkspace\创作任务\`;开发机与用户环境:桌面 `MCNSkill项目/`,以 `references-add/路径配置.md` 为准)。如有分镜则一并纳入。
**最终交付物(确认后自动生成):** 创作回顾.html — 整合全流程各阶段产出的可视化回顾页,命名规则 `脚本{编号}_{脚本标题}_创作回顾.html`,存放于产出根目录下 `{账号名}/` 账号文件夹根目录(开发机与用户环境同构:桌面 `MCNSkill项目/`,以 `references-add/路径配置.md` 为准)。如有分镜则一并纳入。
> 详细提示词和输出模板见:[11_脚本检查和诊断.md](references/创作流程/11_脚本检查和诊断.md)
@@ -295,7 +295,7 @@ Skill部署到非原始开发路径时,所有用户新增的创作流程/创
### 六、硬编码禁止规则(P0)
创作流程、知识库、素材库标准库中禁止硬编码任何具体账号信息(账号名/角色名/专属机制/专属路径)。素材库存具体物品是设计目的不算硬编码;产出目录(dsh 部署环境 `D:\dshworkspace\创作任务\`、开发机与用户环境桌面 `MCNSkill项目/`,或用户自定义路径)全允许。
创作流程、知识库、素材库标准库中禁止硬编码任何具体账号信息(账号名/角色名/专属机制/专属路径)。素材库存具体物品是设计目的不算硬编码;产出目录(开发机与用户环境同构:桌面 `MCNSkill项目/`,或用户自定义路径)全允许。
> 判定标准+替换对照表+验证命令见:[创作流程规范.md](references/创作流程规范.md) 六
@@ -320,6 +320,6 @@ Skill部署到非原始开发路径时,所有用户新增的创作流程/创
```
> 知识库+素材库详细索引 → [索引_知识素材库.md](references/索引_知识素材库.md)
> 产出路径规范(dsh 部署环境 `D:\dshworkspace\创作任务\`、开发机与用户环境桌面 `MCNSkill项目/`(`{账号名}/脚本{编号}/` 直挂,无技能文件夹层),Windows/macOS 按系统解析;**运行时以 `references-add/路径配置.md` 为唯一权威**) → [创作流程规范.md](references/创作流程规范.md) 五
> 产出路径规范(开发机与用户环境同构:桌面 `MCNSkill项目/`(`{账号名}/脚本{编号}/` 直挂,无技能文件夹层),Windows/macOS 按系统解析;**运行时以 `references-add/路径配置.md` 为唯一权威**) → [创作流程规范.md](references/创作流程规范.md) 五
> references-add增量目录结构 → [references-add/README.md](references-add/README.md)
> 快速启动模板 → [帮助文档.md](帮助文档.md)
@@ -85,7 +85,7 @@ references-add/
|:---|:---|
| 文件命名 | `{编号}_{类型名}_{细分}.md`(如 `01_美食库_川菜.md`) |
| 字段结构 | 与 `references/素材库/` 同类型文件保持一致(四维标签:极致维度×适用场景×镜头维度×画面维度) |
| 04_对话库 | 账号专属,不设通用增量,仍在产出根目录 `{账号名}/`(dsh 部署环境 `D:\dshworkspace\创作任务\`;开发机与用户环境桌面 `MCNSkill项目/`,或用户自定义路径)下管理 |
| 04_对话库 | 账号专属,不设通用增量,仍在产出根目录 `{账号名}/`(开发机与用户环境同构:桌面 `MCNSkill项目/`,或用户自定义路径)下管理 |
| 分块定义 | 不设增量,统一使用 `references/素材库/分块定义/` |
## 与 references/ 的关系
@@ -118,4 +118,4 @@ references/(标准) + references-add/(增量) → 合并结果
| **分赛道知识** | 创作前先确定赛道+叙事形态,然后检索 `references-add/分赛道知识/{赛道}/{内容形式}/` 获取赛道专属方法论。与 `references/知识库/` 的通用方法论合并使用 |
| **素材库** | 调取素材时同时检索 `references/素材库/` 和 `references-add/素材库/`。取并集,标准库优先,增量作为补充 |
| **铁律避坑** | 创作完成前,**必须**检索 `铁律避坑规则/通用/` + `铁律避坑规则/分赛道/{当前赛道}/{当前内容形式}/`,逐条检查是否违反铁律 |
| **路径配置** | 每次执行创作流程前,**先查** `路径配置.md`。有「用户自定义路径」记录 → 以用户指定为准(**覆盖标准库默认桌面路径**);无记录 → 按三环境两步判定(本机无 `D:\AgentSkill` → 用户环境 → 桌面 `MCNSkill项目/`;本机有且技能路径含 `.dsh` → dsh 部署环境 → `D:\dshworkspace\创作任务/`;本机有且不含 `.dsh` → 开发机 → 桌面 `MCNSkill项目/`),再按系统解析桌面路径。此维度为增量优先特例 |
| **路径配置** | 每次执行创作流程前,**先查** `路径配置.md`。有「用户自定义路径」记录 → 以用户指定为准(**覆盖标准库默认桌面路径**);无记录 → 按两环境判定(技能处于源技能 git 检出内 → 开发机 → 桌面 `MCNSkill项目/`;不在任何 git 检出内 → 用户环境 → 桌面 `MCNSkill项目/`。**两环境产出同构**),再按系统解析桌面路径。此维度为增量优先特例 |
@@ -5,13 +5,12 @@
## 一、默认路径(用户未自定义时生效)
**先判断运行环境(三环境两步判定),再按运行系统解析桌面路径:**
**先判断运行环境(两环境判定 · 仓库特征判定,v3),再按运行系统解析桌面路径:**
| 运行环境 | 判断依据 | 默认产出根目录 |
|:---|:---|:---|
| dsh 部署环境 | 技能所在路径含 `.dsh`,且本机存在 `D:\AgentSkill`(如 `C:\Users\{用户名}\.dsh\skills\…`) | **优先** `D:\dshworkspace\创作任务\`(主工作区「创作任务」目录) |
| 开发机 | 技能所在路径不含 `.dsh`(源技能在 `D:\AgentSkill` 下) | 用户桌面 `MCNSkill项目/`(`{账号名}/脚本{编号}/` 直挂,无技能文件夹层) |
| 用户环境 | 本机不存在 `D:\AgentSkill`(技能装在用户机器上,路径不限、不一定含 `.dsh`) | 用户桌面 `MCNSkill项目/`(与开发机同构) |
| 开发机 | 技能**处于源技能 git 检出内**(`git -C {技能目录} rev-parse --show-toplevel` 能返回仓库根) | 用户桌面 `MCNSkill项目/`(`{账号名}/脚本{编号}/` 直挂,无技能文件夹层) |
| 用户环境 | 技能**不在任何 git 检出内**(技能装在用户机器上,路径不限,如 `~/.workbuddy/skills/…`) | 用户桌面 `MCNSkill项目/`(与开发机同构) |
桌面路径按运行系统自动解析:
@@ -21,7 +20,10 @@
| macOS | `~/Desktop/`(即 `/Users/{用户名}/Desktop/`) |
**AI 执行规则:**
1. 先做三环境两步判定:① 本机是否存在 `D:\AgentSkill`——不存在即**用户环境** → 桌面 `MCNSkill项目/`;② 本机存在时再看技能自身路径——含 `.dsh` 即 **dsh 部署环境** → **优先操作** `D:\dshworkspace\创作任务\`(「创作任务」子目录不存在则创建);不含 `.dsh` 即**开发机** → 桌面 `MCNSkill项目/`(开发机与用户环境同构:`MCNSkill项目/{账号名}/脚本{编号}/`)
1. 先做两环境判定(**仓库特征判定**):
- 判断技能是否处于**源技能 git 检出内**——执行 `git -C "{技能目录}" rev-parse --show-toplevel`,返回仓库根即 **① 开发机**;命令失败(或返回 NOT_IN_REPO)即 **② 用户环境**。无 git 环境时兜底:从技能目录向上逐级查找,任一级存在 `.git` 目录/文件即开发机,查到盘符根仍未命中即用户环境
- 开发机与用户环境产出**同构** → 桌面 `MCNSkill项目/{账号名}/脚本{编号}/`
- ⚠️ 禁止按 `.dsh` 路径段或 `D:\dshworkspace` 做任何分支判断(该部署环境已不存在)
2. 再判断当前运行系统(Windows / macOS),拼接桌面路径
3. 目录不存在则自动创建
4. 产出结构:`{产出根目录}/{账号名}/脚本{编号}/…`(内部文件结构不变,仅根目录可变)
@@ -49,7 +51,7 @@
## 三、解析规则(优先级从高到低)
1. 本文件「用户自定义路径」记录 → 用自定义路径(最新一条生效)
2. 本文件默认路径 → 按三环境两步判定确定产出根目录(本机无 `D:\AgentSkill` → 桌面 `MCNSkill项目/`;有且技能路径含 `.dsh` → `D:\dshworkspace\创作任务/`;有且不含 `.dsh` → 桌面 `MCNSkill项目/`),再按系统拼接桌面路径
2. 本文件默认路径 → 按两环境判定确定产出根目录(在源技能 git 检出内 = 开发机 → 桌面 `MCNSkill项目/`;不在任何 git 检出内 = 用户环境 → 桌面 `MCNSkill项目/`。**两者同构**),再按系统拼接桌面路径
3. 路径含 `~` 时自动展开为用户主目录
4. 路径含 `{用户名}` 时替换为当前系统用户名
5. 目录不存在时自动创建
@@ -61,3 +63,5 @@
- 2026-08-26(再修订):dsh 部署环境改为**优先操作** `D:\dshworkspace\创作任务\`(主工作区「创作任务」目录,与 .dsh 部署副本既有自定义路径记录一致;用户指定:"dsh 中优先操作 D:\dshworkspace\创作任务\");主工作区不存在时兜底桌面 `DSHSkill项目/`。产出结构不变;双向同步自 V1.0。
- 2026-08-26(三环境重构):环境判定由两级改为**三环境两步判定**(①本机无 `D:\AgentSkill`=用户环境;②有且技能路径含 `.dsh`=dsh 部署环境;③有且不含 `.dsh`=开发机);`DSHSkill项目/` 根目录**整体废弃**(用户确认),原"dsh 异机兜底 DSHSkill项目/"场景并入用户环境 → 桌面 `MCNSkill项目/脚本创作/`;dsh 部署环境产出 `D:\dshworkspace\创作任务\` 不变。产出结构不变;双向同步自 V1.0。
- 2026-08-26(层级精简):开发机与用户环境产出根目录由 `MCNSkill项目/脚本创作/` 改为 `MCNSkill项目/`——去掉技能文件夹层,`{账号名}/脚本{编号}/` 直挂(用户指定:"不论1还是3,都是 桌面\MCNSkill项目\{账号名}\{脚本名}\对应文件");dsh 部署环境不变;双向同步自 V1.0。
- 2026-09-10(判定锚点重构 v2 · 仓库特征判定):环境判定锚点由「本机是否存在 `D:\AgentSkill`」改为**仓库特征判定**——第一步查技能路径是否含 `.dsh`(含 = dsh 部署环境),第二步用 `git -C {技能目录} rev-parse --show-toplevel` 判断技能是否处于源技能 git 检出内(在 = 开发机,不在 = 用户环境;无 git 时兜底为向上逐级查找 `.git`)。产出根目录三环境结果**完全不变**,仅判定依据改变。**根因**:仓库本地路径迁移导致写死盘符锚点失效 → 开发机被误判为「用户环境」。(用户确认:"改为仓库特征判定";同源同步自 V1.0)
- 2026-09-10(环境模型精简 v3 · 三环境 → 两环境):**dsh 部署环境整体废弃**(用户确认:"第一步 技能路径含 .dsh 路径段 ② dsh 部署环境 没有这个环境了")。实测依据:`~/.dsh/skills/` 不存在、`~/.dsh/mcn-plugin.db` 不存在、`~/.dsh` 下仅剩 `profiles/`、`D:\dshworkspace` 整个目录不存在。故删除 `.dsh` 判定分支与 `D:\dshworkspace\创作任务\` 产出路径,判定简化为**一步**。剩余两环境产出本就同构(桌面 `MCNSkill项目/`),产出结果不变。(同源同步自 V1.0)
+9 -11
View File
@@ -8,8 +8,8 @@ description: >
触发场景:写脚本、创作短视频、生成脚本、拆解对标视频、分析爆款视频、
诊断脚本质量、设计账号人设、选题策划、生成分镜、广告植入设计、生成创作回顾。
version: "1.0"
updated_at: "2026-09-07"
last_change: "09-07 产物文件名改名:01_需求拆分/02_需求拆解 → 01_需求理解/02_需求补全(对齐产物语义名体系,V1.0 内 9 处引用零残留;明细见 references-add/变更日志.md)"
updated_at: "2026-09-10"
last_change: "09-10 工作台配置收敛:端口/DB/产出根/榜单目录单源化到 mcn-work-shop/config.json,代码层(server.js/dsh-data.js/4 辅助脚本/data-pages.js/start.bat)统一经 load-config.js 读取,前端用 location.origin 动态取回调地址(明细见 references-add/变更日志.md)"
deployment: "source"
agent_created: true
---
@@ -68,15 +68,15 @@ agent_created: true
2. 用内置浏览器打开 `http://localhost:8900`。**注意端口**:本机 8899 可能被 `MiPCAudio.exe`(小米 PC 管家)占用系统级 0.0.0.0:8899 且杀后自愈,工作台默认端口已迁移为 **8900**;如需指定端口用 `node server.js <端口>`(`PORT_BASE = parseInt(process.argv[2],10) || 8900`)
3. 工作台采用 hash 路由,页面结构:
- **首页(#/)**:数据功能卡片(抖音周榜/热门账号/AI写脚本/AI写作复盘)+ 5 大技能功能块(问法点击复制)+ 三种创作模式
- **📈 抖音周榜(#/ranking)**:读 `D:\dshworkspace\抖音榜单\*.json`(环境变量 `MCN_RANKING_DIR` 可覆盖),赛道筛选 + 日期切换 + 数值排序
- **📈 抖音周榜(#/ranking)**:读桌面 `MCNSkill项目\三方数据\红狐数据\抖音榜单\*.json`(运行时解析真实桌面、兼容 OneDrive 重定向;环境变量 `MCN_RANKING_DIR` 可覆盖),赛道筛选 + 日期切换 + 数值排序
- **👥 热门账号(#/accounts)**:读工作台自有数据库副本 `mcn-work-shop/mcn-plugin.db`(只读),分页/搜索/赛道/类型筛选,账号点击进详情
- **账号详情(#/account/:id)**:信息卡 + 账号信息/账号设定/数据分析 3 tab + 视频列表分页;解析/改写/提炼按钮直接提交 AI 任务执行
- **视频详情(#/video/:id)**:信息卡 + 视频脚本/视频拆解/视频分析 3 tab
- **✍️ AI写脚本(#/rewrites)**:爆款视频列表 + 源脚本 vs AI脚本双栏对比 + 分镜提示词分块渲染
- **🧪 AI写作复盘(#/reviews)**:评分维度列表 + 复盘详情(评分条/问题清单/报告)
- **📁 产出浏览(#/files)**:默认读桌面 `MCNSkill项目/`,支持 ⚙️ 设置添加其他项目地址;md/json 在线预览
4. **数据边界**:数据库读工作台自有副本 `mcn-work-shop/mcn-plugin.db`(由 dsh 库复制而来,与 dsh 原库 `~/.dsh/mcn-plugin.db` 完全隔离,两边各访问各的互不影响);周榜读 `D:\dshworkspace\抖音榜单\*.json` 文件
5. **AI 任务触发(Automation 方案)**:各数据页「导入/创作/解析/改写/提炼/复盘」按钮点击后 → 工作台后端 `POST /api/run` → 向 `~/.workbuddy/workbuddy.db` 的 `automations` 表写入一条**一次性任务**(`schedule_type='once'`、**`scheduled_at=now+5s`(带秒)且显式写 `next_run_at=now+5000`**、`cwds=["D:\\AgentSkill\\mcn-workshop"]`)→ WorkBuddy 客户端调度器按 `next_run_at` 扫描拾取(周期≤30s)→ 创建 AI 会话执行 → **会话归入 `mcn-workshop` 空间分组**(左侧会话栏按 cwd 分组,可在该分组查看任务执行过程与结果)。参考 dsh 插件 `dsh-plugin-mcn` 的 `/mcn/api/creative` 模式(固定会话 followup 触发);前端 5 秒防连点。**调度机制/状态机/并发上限/排查命令详见同目录 `自动化任务调度机制.md`(09-01 实测定稿,勿再按旧认知 `scheduled_at=now` 直写——会因 next_run_at 为空而卡死)**
4. **数据边界**:数据库读工作台自有副本 `mcn-work-shop/mcn-plugin.db`(**dsh 侧已整套下线(09-10):`~/.dsh/mcn-plugin.db` 与 `D:\dshworkspace` 均不存在**,工作台 DB 为唯一读写副本);周榜读桌面 `MCNSkill项目\三方数据\红狐数据\抖音榜单\*.json` 文件
5. **AI 任务触发(Automation 方案)**:各数据页「导入/创作/解析/改写/提炼/复盘」按钮点击后 → 工作台后端 `POST /api/run` → 向 `~/.workbuddy/workbuddy.db` 的 `automations` 表写入一条**一次性任务**(`schedule_type='once'`、**`scheduled_at=now+5s`(带秒)且显式写 `next_run_at=now+5000`**、`cwds=[<工作台目录>]`(由 `server.js` 从 `ROOT` 动态推导,值 = `<技能根>/mcn-work-shop`,**禁止写死盘符**))→ WorkBuddy 客户端调度器按 `next_run_at` 扫描拾取(周期≤30s)→ 创建 AI 会话执行 → **会话归入 `mcn-work-shop` 空间分组**(左侧会话栏按 cwd 分组,可在该分组查看任务执行过程与结果)。参考 dsh 插件 `dsh-plugin-mcn` 的 `/mcn/api/creative` 模式(固定会话 followup 触发);前端 5 秒防连点。**调度机制/状态机/并发上限/排查命令详见同目录 `自动化任务调度机制.md`(09-01 实测定稿,勿再按旧认知 `scheduled_at=now` 直写——会因 next_run_at 为空而卡死)**
6. **工作台任务执行方式铁律(P0,09-02 固化)**:凡工作台场景的**创作/改写/重写/复盘/解析**类任务(含"重新执行历史任务"),必须通过 `POST /api/run` 提交 AI 会话任务执行(automation 触发 → 独立 AI 会话按技能 S1-S11 执行 → 写库 rewrite_log/入库),**禁止在会话内直接产出脚本草稿**(不会入库、不在左侧会话栏可见、不走技能会话,等于"影子脚本")。**重新执行历史任务 = 复用原任务的 prompt(人设卡+视频选题+创作要求+落库字段)+ `skills:['短视频工作台']` 提交新任务**——新任务自动加载当前技能版本(含最新优化),无需手工搬内容。执行方式判定细则见「关键规则 〇·执行方式判定」。
7. **AI 任务入口技能契约(P0,09-02 固化)**:工作台每个触发 AI 会话任务的入口都必须遵守「功能入口 × 技能调用契约」——任务 prompt = **固定技能声明前缀常量**(SKILL_HINT_CREATE/EXTRACT/STORYBOARD/REVIEW/ACCOUNT,分别对应 创作/提炼/分镜/复盘/账号抓取)+ **业务正文**,并传对应 `skills` 数组(参数级技能挂载)。**新增/修改任何入口时先读 `mcn-work-shop/docs/AI会话任务输入输出对照.md`(契约总表 + 新增入口检查清单 7 项),再动手**——防裸 prompt 跳步(09-02 三入口缺约束根因)、防技能声明混入业务串、防非抓取任务命中 server.js BROWSER_HINT 正则被误注入浏览器锁。
8. 本功能仅 V1.0 源技能环境提供,**不同步 dsh/Lite 副本**
@@ -98,7 +98,7 @@ agent_created: true
## 子技能索引(subskills/,09-04 登记)
本技能下设 6 个子技能,随源仓库分发(WorkBuddy 用户技能经软链自动同步、dsh 副本随 dsh_MCNProject 分发)。**引用一律用相对路径**(相对本文件:`subskills/<目录>/SKILL.md`),禁止绝对路径。
本技能下设 6 个子技能,随源仓库分发(WorkBuddy 用户技能经软链自动同步)。**引用一律用相对路径**(相对本文件:`subskills/<目录>/SKILL.md`),禁止绝对路径。
| 目录名 | 技能名 | 功能与触发场景 | 相对路径 |
|--------|--------|----------------|----------|
@@ -118,7 +118,6 @@ agent_created: true
账号解析功能(达人账号信息获取与登记、视频列表抓取、爆款视频拆解分析、人设卡生成、账号数据分析)由独立技能 **mcn-dou-analysis** 提供(09-04 修正:原「D:\AgentSkill 两步判定」规则已废弃——独立仓库不存在,该技能 09-02 起挂在本技能 subskills/ 下随源仓库分发):
- **技能本体**:`subskills/mcn-dou-analysis/`(相对本文件;WorkBuddy 用户技能经软链自动同步)
- **dsh 部署环境**:`~/.dsh/skills/mcn-dou-analysis/`(dsh 部署为平级独立技能目录,随 dsh_MCNProject 仓库分发)
## 外部技能依赖:浏览器工具(browser-harness)
@@ -126,7 +125,6 @@ agent_created: true
- **技能本体**:`subskills/browser-harness/`(相对本文件;含 `envs/browser-harness/` 自带环境与 `src/` 源码,随源仓库分发)
- **⚠️ 占位清理(09-04)**:上游 repo 自带 2 个 14B `../../SKILL.md` 占位(`skills/browser-harness/SKILL.md`、`src/browser_harness/SKILL.md`,Claude Code 分发入口),宿主递归扫描会将其误注册成坏技能变体 → **已删除**。**重拷上游整包时必须排除这 2 个文件**(只保留顶层 `SKILL.md`),否则重复注册复发
- **dsh 部署环境**:`~/.dsh/skills/browser-harness/`(随 dsh_MCNProject 仓库分发,换机器自带)
- **可执行文件**:`subskills/browser-harness/envs/browser-harness/Scripts/browser-harness.exe`(Windows;macOS/Linux 为 `envs/browser-harness/bin/browser-harness`)——**技能自带 venv**(09-04 修复重建,含 python.exe/pyvenv.cfg/全部依赖),随源仓库分发、无需 PATH/uv 工具安装;原「uv 工具安装 ~/.local/bin/browser-harness.exe」已废弃(uv 工具残留 09-04 已清理)
- **进程生命周期(红线,09-02 用户明确)**:已开启的 Chrome(9223 调试实例/用户手动开启的 Chrome)一律不主动关闭,除非用户明确要求;任务结束/失败都保留,承载登录态与标签页复用
- 调用前必做环境检查(三要素:工具/daemon/Chrome),详见 `subskills/mcn-dou-analysis/references/浏览器搜索抖音账号操作规范.md`
@@ -305,7 +303,7 @@ S1 意图识别 → S2 需求完善 → [G0 分析确认]
**用户确认:** Gate-6(最终确认),确认后完成交付。
**最终交付物(确认后自动生成):** 创作回顾.html — 整合全流程各阶段产出的可视化回顾页,命名规则 `脚本{编号}_{脚本标题}_创作回顾.html`,存放于产出根目录下 `{账号名}/` 账号文件夹根目录(dsh 部署环境:`D:\dshworkspace\创作任务\`;开发机与用户环境:桌面 `MCNSkill项目/`,以 `references-add/路径配置.md` 为准)。如有分镜则一并纳入。
**最终交付物(确认后自动生成):** 创作回顾.html — 整合全流程各阶段产出的可视化回顾页,命名规则 `脚本{编号}_{脚本标题}_创作回顾.html`,存放于产出根目录下 `{账号名}/` 账号文件夹根目录(开发机与用户环境同构:桌面 `MCNSkill项目/`,以 `references-add/路径配置.md` 为准)。如有分镜则一并纳入。
> 详细提示词和输出模板见:[11_脚本检查和诊断.md](references/创作流程/11_脚本检查和诊断.md)
@@ -365,7 +363,7 @@ Skill部署到非原始开发路径时,所有用户新增的创作流程/接
### 六、硬编码禁止规则(P0)
创作流程、知识库、素材库标准库中禁止硬编码任何具体账号信息(账号名/角色名/专属机制/专属路径)。素材库存具体物品是设计目的不算硬编码;产出目录(dsh 部署环境 `D:\dshworkspace\创作任务\`、开发机与用户环境桌面 `MCNSkill项目/`,或用户自定义路径)全允许。
创作流程、知识库、素材库标准库中禁止硬编码任何具体账号信息(账号名/角色名/专属机制/专属路径)。素材库存具体物品是设计目的不算硬编码;产出目录(开发机与用户环境同构:桌面 `MCNSkill项目/`,或用户自定义路径)全允许。
> 判定标准+替换对照表+验证命令见:[创作流程规范.md](references/创作流程规范.md) 六
> **🔴 技能内一切文件引用必须用相对路径**(绝对路径随部署机器变化失效;md 链接相对自身文件解析、禁止越级出技能根)→ [路径引用规范.md](references/规则/路径引用规范.md)
@@ -397,6 +395,6 @@ Skill部署到非原始开发路径时,所有用户新增的创作流程/接
```
> 知识库+素材库详细索引 → [索引_知识素材库.md](references/索引_知识素材库.md)
> 产出路径规范(dsh 部署环境 `D:\dshworkspace\创作任务\`、开发机与用户环境桌面 `MCNSkill项目/`(`{账号名}/脚本{编号}/` 直挂,无技能文件夹层),Windows/macOS 按系统解析;**运行时以 `references-add/路径配置.md` 为唯一权威**) → [创作流程规范.md](references/创作流程规范.md) 五
> 产出路径规范(开发机与用户环境同构:桌面 `MCNSkill项目/`(`{账号名}/脚本{编号}/` 直挂,无技能文件夹层),Windows/macOS 按系统解析;**运行时以 `references-add/路径配置.md` 为唯一权威**) → [创作流程规范.md](references/创作流程规范.md) 五
> references-add增量目录结构 → [references-add/README.md](references-add/README.md)
> 快速启动模板 → [帮助文档.md](帮助文档.md)
@@ -1,9 +1,11 @@
/* build-create-prompts.mjs — 构造 6 个视频的 AI 创作任务 prompt(与前端 AI创作 按钮完全一致:人设卡+视频选题块+创作指令),并提交 /api/run */
const { DatabaseSync } = require('node:sqlite');
const path = require('path');
const { loadConfig } = require('./load-config');
const DB = path.join(__dirname, 'mcn-plugin.db');
const API = 'http://localhost:8900/api/run';
const CFG = loadConfig();
const DB = path.join(__dirname, CFG.dbName);
const API = `http://localhost:${CFG.port}/api/run`;
const ACCOUNT_NAME = '俊希';
const ACCOUNT_ID = 3;
const VIDEO_IDS = [309, 313, 315, 316, 318, 320];
@@ -72,7 +74,7 @@ for (const vid of VIDEO_IDS) {
请为达人「${ACCOUNT_NAME}」创作一条全新的短视频故事脚本。${personaBlock}${noPersonaNote}${block}${createTail}
【任务完成后必须执行】
将完整脚本保存到工作台数据库 mcn-plugin.db 的 rewrite_log 表(数据库路径:D:\\AgentSkill\\mcn-video-script\\project\\短视频脚本创作\\V1.0\\mcn-work-shop\\mcn-plugin.db),字段:
将完整脚本保存到工作台数据库 ${CFG.dbName} 的 rewrite_log 表(数据库路径:${DB},即本会话 cwd 下的 ${CFG.dbName}),字段:
- video_id=${v.id}(整数)
- aweme_id=${v.aweme_id}
- account_id=${ACCOUNT_ID}
@@ -0,0 +1,7 @@
{
"port": 8900,
"dbName": "mcn-plugin.db",
"outputRoot": "MCNSkill项目",
"businessDataDir": ["MCNSkill项目", "三方数据", "红狐数据", "抖音榜单"],
"extraRoots": []
}
@@ -6,7 +6,7 @@
## 触发链路(所有入口共用)
按钮点击 → 确认弹窗(confirmRun / confirmAiScript / confirmSaveAccount)→ `executeTask()` → `POST /api/run`(server.js 写 automations 表,scheduledAt=now+5s + next_run_at=now+5000,`skills` 数组→`skills_json` 参数级技能挂载)→ 客户端调度器拾取 → 左侧会话栏「mcn-workshop」空间后台会话执行 → 轮询 `/api/run/status`(done/error/review)→ 完成刷新页面。
按钮点击 → 确认弹窗(confirmRun / confirmAiScript / confirmSaveAccount)→ `executeTask()` → `POST /api/run`(server.js 写 automations 表,scheduledAt=now+5s + next_run_at=now+5000,`skills` 数组→`skills_json` 参数级技能挂载)→ 客户端调度器拾取 → 左侧会话栏「mcn-work-shop」空间后台会话执行(cwd = 工作台目录,由 `server.js` 从 `ROOT` 动态推导)→ 轮询 `/api/run/status`(done/error/review)→ 完成刷新页面。
## 功能入口 × 技能调用契约总表
@@ -0,0 +1,263 @@
# 短视频工作台 UI 规范
> **定位**:本文件是前端 UI 的**设计规范基线**(配色 / 字体 / 圆角 / 布局 / 组件 / 交互),供新增页面、调整样式时对照,避免重复踩坑。
---
## 1. 设计语言
亮色主题、工具类后台风格。三条主线:
1. **信息密度优先**——表格/列表是主体,行高紧凑(表格 td `7px 10px`,字号 15px)。
2. **克制配色**——蓝(`#2f6fed`)作主色承担交互反馈,橙(`#e8590c`)只用于强调数值/警示,其余靠灰度拉开层次。
3. **轻量质感**——圆角 8/12px、阴影 `0 1px 3px rgba(0,0,0,.08)`,不用重投影和渐变。
---
## 2. 设计 Token
### 2.1 色彩
| 变量 | 值 | 用途 |
|---|---|---|
| `--bg` | `#f5f6f8` | 页面底色 |
| `--panel` | `#ffffff` | 卡片/表格/弹窗面板 |
| `--border` | `#e3e6ea` | 统一描边 |
| `--text` | `#24292f` | 主文本 |
| `--dim` | `#6b7280` | 次要文本、说明、占位 |
| `--primary` | `#2f6fed` | 主色(按钮/激活态/链接 hover) |
| `--primary-soft` | `#eaf1fe` | 主色浅底(hover 行、激活 tab 底) |
| `--accent` | `#e8590c` | 强调(榜单得分、警示) |
| `--danger` | `#d93026` | 危险操作(删除) |
| `--shadow` | `0 1px 3px rgba(0,0,0,.08)` | 卡片/顶栏阴影 |
**扩展色(未进变量,直接写在规则里)**
| 场景 | 值 |
|---|---|
| 表头底色 | `#f6f8fa` |
| 链接蓝 | `#4a90e2` |
| 主色 hover 深一档 | `#2a5fd0` |
| 图标垂直微调 | `vertical-align: -0.18em` |
| 文件类型色 | md `#8250df` / json `#cf222e` / xlsx `#1a7f37` / txt·csv `#57606a` |
| 半透明主色底 | `rgba(47,111,237,.07)`(首页统计条)、`.16`(分隔线) |
**功能图标亮色映射**(未列出的跟随 currentColor):
`trending-up` 橙 · `users` 蓝 · `pen` 粉 `#ec4899` · `flask` 青 `#0ea5e9` · `lightbulb` 黄 `#d99a2b` · `refresh` 青绿 `#0f7f8f` · `chart` 玫红 `#d6336c` · `wrench` 灰 `#5b6470`
### 2.2 字体
```css
font-family: "Microsoft YaHei", "PingFang SC", "Segoe UI", sans-serif;
font-size: 16px; /* body 基准 */
```
代码/JSON 预览:`Consolas, monospace`。
**字号阶梯**(实测归纳,不是硬 token):
| 级别 | 字号 | 用例 |
|---|---|---|
| 特大 | 26px / 21px | 首页统计数字、面包屑主标题 |
| 标题 | 18px / 17px | 弹窗标题、区块标题、卡片标题 |
| 正文 | 16px / 15px | 正文、按钮、表格、输入框 |
| 次要 | 14px / 13px | 说明文字、数值列、徽章、tab(小) |
### 2.3 圆角 / 阴影 / 层级
| 类型 | 值 | 用例 |
|---|---|---|
| 圆角 | 6px | 小控件(btn-sm、侧栏切换组、树节点) |
| | **8px** | 默认(按钮、输入框、表格容器、信息卡、评分块) |
| | 10px | 徽章、榜单容器、卡片 |
| | **12px** | 大卡片(首页 data-card、func-card、统计条) |
| | 14px | 弹窗 |
| 阴影 | `var(--shadow)` | 卡片、顶栏 |
| | `0 8px 30px rgba(0,0,0,.18)` | 弹窗 |
| | `0 4px 12px rgba(47,111,237,.15)` | 卡片 hover(主色扩散) |
| z-index | 10 顶栏 / 100 弹窗遮罩 / 200 Toast | 表头 sticky 为 1 |
### 2.4 间距
没有 token 化,实际使用序列:**2 / 4 / 6 / 8 / 10 / 12 / 14 / 16 / 18 / 20 / 24px**。常用组合:
- 卡片内边距 `16px 18px`;信息卡 `14px 16px`;表格单元 `7px 10px`;表头 `8px 10px`
- 页面外边距 `20px 24px`;首页/详情内容 `24px`;区块间距 14~24px
- 工具条内部 gap 8~10px;表单行 gap 10px
---
## 3. 布局框架
```
┌─ header.topbar(55px,sticky,白底 + 底部 1px 边框 + shadow,z-index 10)
│ brand(logo) · crumbs(面包屑) · top-actions(首页/帮助/设置,右侧)
├─ main#view height: calc(100vh - 55px) overflow: auto ← 唯一滚动容器
│ .home(max-width 960) / .page(max-width 1440)
└─ 弹窗层 .modal-mask(fixed inset 0,z-index 100)
```
**滚动(坑点,勿改)**
| 规则 | 原因 |
|---|---|
| `html, body { overflow: hidden }` | 消除顶栏 1px 边框引发的第二层滚动条 |
| `main#view { height: calc(100vh - 55px) }` | **55px 是实测值**(padding 20 + 行高 34 + 边框 1)。写成 54px 底部会溢出 1px,触发第二层滚动条 |
| 长内容滚动交给内部容器(`.rw-sec-body`、`.rw-content`、`.file-tree`) | 保持页面级不滚 |
**内容宽度**:首页 `.home` max-width **960px**;功能页 `.page` max-width **1440px**(padding 20 24);弹窗 `.rw-modal` 宽 `min(1600px, 100% - 100px)`、高 `calc(100vh - 60px)`。
---
## 4. 组件规范
### 4.1 按钮
| 类 | 外观 | 用例 |
|---|---|---|
| `.btn` | 白底 + border + `6px 14px` + r8 + 15px;hover 边框/文字变 primary;`:disabled` opacity .5 | 默认 |
| `.btn-primary` | primary 底白字;hover `#2a5fd0` | 主行动 |
| `.btn-ghost` | 透明底 + border | 次要/弹窗内 |
| `.btn-danger` | danger 系 | 删除 |
| `.btn-sm` | `3px 10px` / 14px / r6 | 表格功能列 |
| `.btn-icon` | `5px 9px`,纯图标 | 图标按钮 |
| `.btn-busy` | primary-soft 底 + primary 字 + opacity .8,内含图标旋转动画 | 异步进行中 |
| `.btn-import` | 白底 + `#d0d4da` 边 + `#495057` 字 | **保存类**(浅灰底深字) |
| `.btn-view` | 白底 + primary 边 + primary 字 | **查看类** |
> 功能列成对出现时:**保存=浅灰底深字(.btn-import),查看=白底蓝字(.btn-view)**,靠底色深浅区分而非都用蓝。
顶栏三按钮用 `.ghost`(白底 + border,`6px 14px`,r8)。
### 4.2 输入框
`.input`(`6px 10px`,min-width 200px)· `.input-select`(`6px 8px`,筛选下拉更窄)。均 1px border + r8 + 15px + 白底。
### 4.3 表格
```css
.table-wrap overflow auto + 1px border + r8 + 白底
.table width 100%, border-collapse collapse, 15px
.table th sticky top 0, 背景 #f6f8fa, 8px 10px, 600, nowrap, z-index 1
.table td 7px 10px, nowrap, 底部 1px border
tbody tr:hover 背景 var(--primary-soft)
```
| 修饰类 | 作用 |
|---|---|
| `.num`(td/th) | **右对齐** + `font-size: 13px` + `font-variant-numeric: tabular-nums`(等宽数字,必须加,否则数字跳动) |
| `.th-wide` | 数值列表头 min-width 90px + 右对齐 |
| `.sortable` | cursor pointer,hover 变 primary |
| `.cell-content` | max-width 220px + 省略号 |
| `.cell-loc` | 账号定位列缩窄 110px |
| `.th-act` / `.cell-act` | 功能列居中 |
| `.title-plain` | 视频标题:默认同正文色,**hover 才变蓝+下划线**(避免整列花掉) |
| `.link` / `.link.strong` | `#4a90e2`;`.strong` 加粗 |
**列宽策略(关键)**
| 场景 | 做法 |
|---|---|
| 榜单表格 `.table-rank` | **必须 `table-layout: fixed`**——auto 布局下 100% 表格的富余空间会按比例摊薄,**min-width 压不住**;fixed 下 width 才是硬约束,账号列自动吃剩余宽度 |
| 普通数据表格 | auto 布局,靠 `.cell-content` max-width + 省略号收口 |
**行高对齐(关键)**:榜单三个 tab 的行高必须统一,否则切换时整表跳动——
- 表格 `.table-rank td { height: 42px }`(功能列 `.btn-sm` 撑到 42px,其他 tab 原本只有 34.7px)
- 首页预览 `.rank-preview-list .rank-row:not(.rank-head) { min-height: 40px }`
### 4.4 Tab(两套并存,注意覆盖)
| 位置 | 样式 |
|---|---|
| 顶栏 | 胶囊式:r8,hover 浅蓝底蓝字,active **蓝底白字 + 600** |
| 数据页(**重新定义同名 `.tabs/.tab`**) | 下划线式:无背景无圆角,`border-bottom: 2px transparent` + `margin-bottom: -1px`(压住容器分隔线),active **蓝字 + 蓝色下划线** |
> ⚠️ 同名类二次定义,**数据页规则在后会覆盖顶栏规则**。新增页面若在下划线式区域用 `.tab`,拿到的是下划线式;需要胶囊式请另起类名。
榜单 tab `.rank-tab`:r8 8px 0 0(上圆角),active 浅蓝底 + 蓝字 + 底部 2px 蓝线。
首页预览里需 `align-self: center`(全局 `.rank-tabs` 用 stretch,会让 tab 组相对兄弟控件偏上约 5px)。
### 4.5 卡片
| 类 | 规格 |
|---|---|
| `.data-card`(首页入口) | r12,`16px 18px`,shadow,hover **上浮** `translateY(-2px)` + 蓝边 + `0 4px 12px rgba(47,111,237,.15)`,transition .15s |
| `.info-card`(信息区) | r8,`14px 16px`,margin-bottom 14px;标题 15px/600 + 底部 1px 分隔线 |
| `.recent-card`(脚本快捷卡) | r10,`14px 16px`,min-height **172px**(保证网格等高),hover 蓝边 + 淡影 |
| `.func-card` | r12,`16px 18px` |
### 4.6 徽章 / 标签
`.badge`:`2px 8px`,r10,13px。
- `.badge-signed` 浅蓝底蓝字(签约/内部)
- `.badge-ext` `#fff4e5` 底 + accent 橙字
- `.rw-ver-new` primary 底白字 r9 12px(最新版本标记,附在版本条最右)
- `.recent-card-score` 12px/600 r10
### 4.7 弹窗
```css
.modal-mask fixed inset 0, rgba(0,0,0,.35), flex 居中, z-index 100
.modal 白底 r14, 22px 26px, width 520px, max-width 92vw, shadow 0 8px 30px
.req-modal 760px × min(840px, 92vh) /* AI创作 */
.rw-modal min(1600px, 100% - 100px) × calc(100vh - 60px) /* 脚本对比/查看 */
```
> ⚠️ `.modal-mask[hidden] { display: none }` **必须显式写**——`.modal-mask` 是 `display:flex`,会覆盖 HTML `hidden` 属性导致弹窗关不掉。
**对比弹窗三段式(`.rw-*`)**:`.rw-stack`(flex row, gap 12)→ `.rw-sec`(flex 1, min-width 0, r8, overflow hidden)→ 内分 `.rw-sec-head`(8px 12px,白底,底部边框)/ `.rw-sec-body`(12px 14px,overflow auto)/ `.rw-sec-foot`(**min-height 46px**,右对齐,顶部边框)。
> `.rw-sec-foot` 的 min-height 是为了**空 foot(源脚本列占位)与其他列等高**,不能删。
### 4.8 Toast
固定在底部居中(`bottom: 32px`,`translateX(-50%)`),深底 `#24292f` 白字,`10px 18px` r8,z-index 200,**显示 1.8 秒后自动消失**。用于复制成功、保存结果等轻量反馈;失败重试类提示走行内 `.toolbar-note` / `.empty`。
### 4.9 空状态
`.empty-state`:纵向居中,`padding: 56px 20px`,gap 10px——图标(dim + opacity .45)→ 标题(17px/600)→ 说明(14px/dim)→ 行动按钮(margin-top 10px)。
轻量占位可用 `.empty`(`padding: 30px`,居中,dim)。
### 4.10 分页 / 批量
`.pager` 右对齐(gap 12,`padding: 12px 4px`);`.list-footer` 用 space-between 把批量操作条放左、分页放右;批量条 `.list-batchbar` 出现时与分页同行。
### 4.11 评分条
`.score-row`:名称 `.score-name` 固定 **88px** → 进度条 `.score-bar`(flex 1,height 6px,r3,底 `#eef1f5`)→ 分值 `.score-val` 固定 **36px** 右对齐。三档宽度固定,保证多行对齐。
### 4.12 图标
```js
svgIcon(name, size = 16, color) ;生成 class="ic" 的 SVG
```
- Lucide 风格:`viewBox 0 0 24 24`,`stroke-width: 2`,`fill: none`,`stroke: currentColor`
- 静态 HTML 用 `<i data-icon="home">` 占位,`initIcons()` 统一替换
- `.ic { vertical-align: -0.18em; flex-shrink: 0 }` ——**垂直微调 -0.18em,去掉会和文字基线错开**
---
## 5. 关键交互约定
| 约定 | 说明 |
|---|---|
| **数值一律右对齐 + 等宽数字** | `text-align: right` + `font-variant-numeric: tabular-nums`,列表头同步右对齐 |
| **字段对齐用 grid 列轨道共享** | 账号信息/视频详情的多行字段,用同一个 grid 让列跨行对齐;每行独立 flex 会因内容长短把后续字段顶偏(R44/R45 教训) |
| **超宽字段移出共享网格** | 如「账号定位」整行留在网格内会把 4 条轨道撑成等宽(各 232.5px),右侧大片留白 |
| **滚动联动单向** | AI 脚本列滚动驱动分镜列对齐,**不做双向耦合**(双向会互相抖动) |
| **长文本截断三件套** | `overflow: hidden` + `text-overflow: ellipsis` + `white-space: nowrap`,并挂 `title` 显示全文 |
| **hover 才提示可点** | 列表中的标题类文本用 `.title-plain`(默认同正文色,hover 变蓝+下划线),避免整列都是蓝字 |
---
## 6. 已知坑与规避
| # | 坑 | 规避 |
|---|---|---|
| 1 | `main#view` 写成 `calc(100vh - 54px)` → 底部溢出 1px,出现第二层滚动条 | 用 **55px**(padding 20 + 行高 34 + 边框 1) |
| 2 | 榜单表格用 auto 布局 → 列宽被富余空间摊薄,`min-width` 失效 | 加 `.table-rank { table-layout: fixed }` + 显式列宽 |
| 3 | 榜单三 tab 行高不一致 → 切换时整表跳动 | 表格 td 固定 42px;首页预览行 min-height 40px |
| 4 | `.modal-mask` 的 `display:flex` 覆盖 `hidden` → 弹窗关不掉 | 显式写 `.modal-mask[hidden] { display: none }` |
| 5 | 数值列没加 `tabular-nums` → 滚动/翻页时数字左右跳 | 数值列统一加 |
| 6 | 图标与文字基线错开 | `.ic` 的 `vertical-align: -0.18em` 不能删 |
| 7 | 首页 `.rank-tabs` 相对兄弟控件偏上 ~5px | 预览场景加 `align-self: center; margin-bottom: 0` |
@@ -1,7 +1,8 @@
// 短视频工作台 - 数据访问层(只读复用业务数据目录)
// 数据源:工作台目录 mcn-plugin.db(dsh 库副本,node:sqlite 只读)+ 三方数据目录 抖音榜单/*.json
// 与 dsh 原库(~/.dsh/mcn-plugin.db)隔离:两边各访问各的,互不影响
// 页面逻辑参照 dsh-plugin-mcn 重新开发,仅复用数据库 schema 与榜单文件格式
// 数据源:工作台目录 mcn-plugin.db(node:sqlite 只读)+ 三方数据目录 抖音榜单/*.json
// 09-10:dsh 侧已整套下线(~/.dsh/skills/、~/.dsh/mcn-plugin.db、D:\dshworkspace 均不存在),
// 工作台 DB 为唯一读写副本,不再有「与 dsh 原库隔离」的双库关系
// 页面逻辑参照 dsh-plugin-mcn 重新开发(历史沿革),仅复用数据库 schema 与榜单文件格式
'use strict';
const { DatabaseSync } = require('node:sqlite');
@@ -9,18 +10,19 @@ const path = require('node:path');
const fs = require('node:fs');
const os = require('node:os');
// 工作台自有数据库副本(与 dsh 原库隔离)
const DSH_DB = path.join(__dirname, 'mcn-plugin.db');
// 读取工作台 config.json(load-config.js 唯一入口 + 默认值唯一来源,配置收敛)
const { loadConfig } = require('./load-config');
const CFG = loadConfig();
// 榜单目录解析(09-03 定稿:跟随 mcn-data-insight 榜单落盘规范的数据源分层)
// 优先级:① 环境变量 MCN_RANKING_DIR 显式覆盖 → ② dsh 部署(技能在 ~/.dsh 下)→ D:\dshworkspace\抖音榜单
// → ③ 开发机/用户环境(①③):桌面 MCNSkill项目\三方数据\红狐数据\抖音榜单(运行时解析真实桌面,兼容 OneDrive 重定向)
// 工作台自有数据库副本(唯一读写副本)
const DSH_DB = path.join(__dirname, CFG.dbName);
// 榜单目录解析(09-03 定稿)
// 优先级:① 环境变量 MCN_RANKING_DIR 显式覆盖 → ② 桌面/<CFG.businessDataDir>(config.json,运行时解析真实桌面,兼容 OneDrive 重定向)
function resolveRankingDir() {
if (process.env.MCN_RANKING_DIR) return process.env.MCN_RANKING_DIR;
const isDshDeploy = __dirname.includes('.dsh');
if (isDshDeploy) return 'D:\\dshworkspace\\抖音榜单';
const home = os.homedir();
const rel = path.join('MCNSkill项目', '三方数据', '红狐数据', '抖音榜单');
const rel = path.join(...CFG.businessDataDir);
const candidates = [
path.join(home, 'Desktop', rel),
path.join(home, 'OneDrive', 'Desktop', rel),
@@ -295,8 +297,14 @@ function listRewrites(videoId) {
return { items: rows };
}
function listStoryboards(videoId) {
/* 09-10:原创选题(video_id 为空)原本查不到分镜 → 分镜 tab 无生成入口。
现支持按 rewrite_id 查询,使无视频载体的脚本也能挂分镜(storyboard_log 本就有 rewrite_id 列)。 */
function listStoryboards(videoId, rewriteId) {
const d = getDb(); if (!d) return [];
const rid = Number(rewriteId);
if (Number.isFinite(rid) && rid > 0 && !(Number(videoId) > 0)) {
return d.prepare(`SELECT id, video_id, rewrite_id, script_text, status, created_time, updated_time FROM storyboard_log WHERE rewrite_id=? ORDER BY id DESC`).all(rid);
}
return d.prepare(`SELECT id, video_id, rewrite_id, script_text, status, created_time, updated_time FROM storyboard_log WHERE video_id=? ORDER BY id DESC`).all(videoId);
}
@@ -428,12 +436,30 @@ function collectCategories(board, files) {
return RANK_KEEP.filter((c) => found.has(c));
}
// 期次真实区间:{ date: {start, end} }(周榜=周一~周日;日榜 start=end=当日)
// 09-10:UI 改显示「08-31 ~ 09-06」而非裸期号 2026-08-31——期号是周一,只看期号会误以为仅 8/31 单日。
// 同一 date 可能有多个文件(week 每赛道一个),取首个即可(区间相同)。
function collectRanges(board, files) {
const out = {};
const seen = new Set();
for (const pf of files) {
if (seen.has(pf.date)) continue;
seen.add(pf.date);
try {
const d = JSON.parse(fs.readFileSync(rankFilePath(pf.dir, pf.name), 'utf8'));
const s = String(d.dateStart || d.date || '').trim();
if (s) out[pf.date] = { start: s, end: String(d.dateEnd || d.date || s).trim() };
} catch (e) {}
}
return out;
}
function rankingMeta(board = 'week') {
const empty = { dates: [], categories: [] };
const empty = { dates: [], categories: [], ranges: {} };
const files = listRankFiles(board);
if (!files.length) return empty;
const dates = [...new Set(files.map((f) => f.date))];
return { dates, categories: collectCategories(board, files) };
return { dates, categories: collectCategories(board, files), ranges: collectRanges(board, files) };
}
// 榜单数据新鲜度(更新防重依据,09-03 新增):递归扫描 RANKING_DIR(三榜子目录 + 根目录旧平铺)全部 .json,
@@ -472,7 +498,7 @@ function parseNum(v) {
}
function getRanking({ board = 'week', date = '', category = '', sortBy = '', order = '', page = 1, pageSize = 20 } = {}) {
const empty = { dates: [], categories: [], category: '', date: '', total: 0, items: [] };
const empty = { dates: [], categories: [], ranges: {}, category: '', date: '', total: 0, items: [] };
const files = listRankFiles(board);
if (!files.length) return empty;
const dates = [...new Set(files.map((f) => f.date))];
@@ -524,7 +550,7 @@ function getRanking({ board = 'week', date = '', category = '', sortBy = '', ord
}
return { ...row, inDb: !!hit, dbId: hit ? hit.id : null };
});
return { dates, categories, category, date: selDate, total, page: Math.max(1, Number(page) || 1), pageSize: limit, items: pageItems };
return { dates, categories, ranges: collectRanges(board, files), category, date: selDate, total, page: Math.max(1, Number(page) || 1), pageSize: limit, items: pageItems };
}
/* ================= AI写脚本:原创选题(无视频载体)================= */
@@ -0,0 +1,4 @@
// 输出工作台端口(供 start.bat 读取)
// 09-10:bat 内嵌复杂 JS(-e "console.log(require('./load-config')...)" )会被 cmd 解析截断报错,
// 改为独立脚本,bat 只执行简单命令 "%NODE_EXE%" get-port.js,避免引号/括号嵌套问题。
console.log(require('./load-config').loadConfig().port);
@@ -0,0 +1,24 @@
// 工作台 config.json 读取工具(唯一入口 + 默认值唯一来源)
// 所有工作台脚本(server.js / dsh-data.js / *.cjs)统一通过它读配置,
// 调整端口 / DB 名 / 目录只需改 config.json 一处;config 缺失或损坏时回退下方默认值。
'use strict';
const fs = require('node:fs');
const path = require('node:path');
const DEFAULTS = {
port: 8900,
dbName: 'mcn-plugin.db',
outputRoot: 'MCNSkill项目',
businessDataDir: ['MCNSkill项目', '三方数据', '红狐数据', '抖音榜单'],
extraRoots: [],
};
function loadConfig() {
try {
const p = path.join(__dirname, 'config.json');
if (fs.existsSync(p)) return Object.assign({}, DEFAULTS, JSON.parse(fs.readFileSync(p, 'utf8')));
} catch (e) { /* 配置损坏回退默认 */ }
return Object.assign({}, DEFAULTS);
}
module.exports = { DEFAULTS, loadConfig };
@@ -7,7 +7,9 @@ const TASKS = [
{ videoId: 318, id: 'automation-1788258530542' },
{ videoId: 320, id: 'automation-1788258533562' },
];
const API = 'http://localhost:8900/api/run/status?id=';
const { loadConfig } = require('./load-config');
const CFG = loadConfig();
const API = `http://localhost:${CFG.port}/api/run/status?id=`;
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const stateOf = new Map(TASKS.map((t) => [t.videoId, 'queued']));
@@ -7,11 +7,14 @@ const TASKS = [
{ rw: 27, id: 'automation-1788251464600' },
{ rw: 26, id: 'automation-1788251467630' },
];
const { loadConfig } = require('./load-config');
const CFG = loadConfig();
const API = `http://localhost:${CFG.port}/api/run/status?id=`;
const state = {};
TASKS.forEach((t) => (state[t.rw] = 'pending'));
const status = async (id) => {
try {
const r = await fetch('http://localhost:8900/api/run/status?id=' + encodeURIComponent(id));
const r = await fetch(API + encodeURIComponent(id));
const d = await r.json();
return d.state || 'unknown';
} catch { return 'unknown'; }
@@ -16,6 +16,15 @@ const cleanTitle = (t) => {
return stripTitleQuotes(s.slice(0, idx)) + s.slice(idx);
};
const fmtSize = (n) => { if (n < 1024) return n + ' B'; if (n < 1024 * 1024) return (n / 1024).toFixed(1) + ' KB'; return (n / 1024 / 1024).toFixed(1) + ' MB'; };
/* 榜单期号显示为区间(09-10):周榜「08/31 ~ 09/06」、日榜「09/08」。
期号 = 该周周一,只显示 2026-08-31 会被误读成"仅 8/31 单日",实际覆盖到 09-06。
ranges 由 /api/dsh/ranking 返回(每期真实 dateStart/dateEnd,读自落盘文件)。 */
const fmtRankPeriod = (date, ranges) => {
const r = ranges && ranges[String(date || '')];
if (!r) return String(date || '');
const mm = (x) => String(x || '').slice(5).replace(/-/g, '/'); // 08/31(斜杠比连字符更易读)
return r.start === r.end ? mm(r.start) : mm(r.start) + ' ~ ' + mm(r.end);
};
const EXT_ICON = { md: 'file-text', json: 'file-code', xlsx: 'table', csv: 'table', txt: 'file-text', log: 'file-text', bak: 'file' };
/* ================= AI 任务触发(确认弹窗 + 执行中状态) =================
@@ -460,7 +469,7 @@ async function loadRecentScripts() {
}
/* ================= 首页 榜单预览(账号周榜/视频热榜/点赞榜 三榜;前10 + 赛道/日期筛选,无分页/排序) ================= */
let _rankPreview = { board: 'week', pageSize: 10, dates: [], categories: [], date: '', category: '', _metaBoard: '' };
let _rankPreview = { board: 'week', pageSize: 10, dates: [], categories: [], ranges: {}, date: '', category: '', _metaBoard: '' };
function renderRankTools() {
const has = _rankPreview.dates.length > 0;
const tools = $('#rankTools');
@@ -472,7 +481,7 @@ function renderRankTools() {
}
const dateLabel = $('#rankDateLabel');
const di = _rankPreview.dates.indexOf(_rankPreview.date);
if (dateLabel) dateLabel.textContent = _rankPreview.date ? _rankPreview.date.slice(5).replace('-', '-') : '';
if (dateLabel) dateLabel.textContent = fmtRankPeriod(_rankPreview.date, _rankPreview.ranges);
const prevBtn = $('#rankPrevDate'), nextBtn = $('#rankNextDate');
if (prevBtn) { prevBtn.disabled = di <= 0; prevBtn.onclick = () => { if (di > 0) { _rankPreview.date = _rankPreview.dates[di - 1]; renderRankTools(); loadRankingPreview(); } }; }
if (nextBtn) { nextBtn.disabled = di < 0 || di >= _rankPreview.dates.length - 1; nextBtn.onclick = () => { if (di >= 0 && di < _rankPreview.dates.length - 1) { _rankPreview.date = _rankPreview.dates[di + 1]; renderRankTools(); loadRankingPreview(); } }; }
@@ -532,6 +541,7 @@ async function loadRankingPreview() {
const m = await api('/api/dsh/ranking?board=' + board + '&page=1&pageSize=1');
_rankPreview.dates = m.dates || [];
_rankPreview.categories = m.categories || [];
_rankPreview.ranges = m.ranges || {};
_rankPreview.date = m.date || (_rankPreview.dates[0] || '');
_rankPreview.category = m.category || '';
_rankPreview._metaBoard = board;
@@ -2,6 +2,10 @@
依赖 app.js 暴露的工具:esc/api/renderMarkdown/copyText/showToast/fmtSize */
'use strict';
/* 工作台服务地址(09-10 配置收敛):前端用 location.origin 动态取当前访问地址,端口自动跟随,
不再写死 8900 —— 换端口时 prompt 里的回调地址自动一致,无需改代码 */
const BASE = location.origin;
/* AI创作技能指令(09-01 修复:任务必须按「短视频工作台」技能执行,禁止模型自由发挥导致分镜表格/TXT/MD 格式混杂。
定位方式跨环境:优先用 Skill 工具按技能名加载;技能位于用户技能目录 ~/.workbuddy/skills/短视频工作台/(部署到用户环境时同此标准位),不写死机器绝对路径 */
const SKILL_HINT_CREATE = '请调用 Skill 工具加载「短视频工作台」技能(技能根目录 = 用户技能目录 ~/.workbuddy/skills/短视频工作台/,先读取其 SKILL.md 及 references 规范;若 Skill 工具无法加载,请直接读取该目录下的 SKILL.md 文件)。本任务以技能 ⚡自动模式 执行:严格按 SKILL.md 的 S1-S11 步骤顺序逐步走完完整创作流程(选题→框架→大纲→情绪→结构→脚本)——中间步骤后台静默 = 各步产物在对话上下文中按序完整产出但不逐个展示,禁止跳过或合并中间步骤一步直出成品。最终输出 S9 标准脚本格式(按场次+对白组织,画面标注【景别|环境|光线|场景|场景元素】),禁止输出分镜头表格、禁止偏离脚本格式。创作时必须逐条读取并严格执行 SKILL.md 的「开场钩子铁律」与【账号设定】的「内容规则」(如果…则…)——二者为硬约束,不得自由发挥、不得跳过,具体标准一律以 SKILL.md 与内容规则原文为准。';
@@ -16,19 +20,19 @@ const SKILL_HINT_STORYBOARD = '请调用 Skill 工具加载「短视频分镜脚
const SKILL_HINT_REVIEW = '请调用 Skill 工具加载「脚本复盘」技能(技能根目录 = 用户技能目录 ~/.workbuddy/skills/短视频工作台/subskills/mcn-script-review/,先读取其 SKILL.md 及 references 规范;若 Skill 工具无法加载,请直接读取该目录下的 SKILL.md 文件)。本任务为工作台复盘任务:严格按技能执行方式判定与复盘流程执行(评分式复盘/对比分析模式由任务说明指定;一条脚本=一个独立上下文,禁止混审多条脚本)。技能边界:只做分析与建议,不修改脚本、不触发创作流程、不改动任何技能文件。';
/* 视频解析技能声明(09-03 R28:视频详情页「解析/刷新」按钮=单视频 MCP 解析,任务必须按 mcn-dou-analysis 功能五执行,
一次性产出「视频脚本」(content 分镜头表)与「视频拆解」(analysis 结构化拆解)两个数据源 */
const SKILL_HINT_VIDEO_PARSE = '请调用 Skill 工具加载「mcn-dou-analysis」技能(技能根目录 = 用户技能目录 ~/.workbuddy/skills/短视频工作台/subskills/mcn-dou-analysis/,先读取其 SKILL.md 及 references 规范;若 Skill 工具无法加载,请直接读取该目录下的 SKILL.md 文件)。本任务为技能功能五「解析视频」:对单条抖音视频做 MCP 内容解析。按 references/feature/05_解析视频.md 流程执行:① 调用 MCP 工具 upload_douyin_video 提交视频链接(share_text=该视频抖音链接)获取 detailId;② 等待解析完成(新视频约 3-5 分钟,用 sleep 等待后查询,content 已出而 analysis 未出时稍后重试);③ 用 short_video_detail 查询解析结果 content(视频脚本/分镜头表)与 analysis(视频拆解/结构化JSON);④ 按 references/解析结果格式规范.md 用 scripts/json_tool.py 将 content.json 与 analysis.json 保存到产出目录对应视频文件夹;⑤ 解析结果入库:工作台环境(本机 http://localhost:8900)产物落盘后调用 POST /api/import/account(body {"account":"{达人昵称}"})同步入库,接口不可用则跳过不影响保存;**入库后必须自查关联是否成功(09-04 修复后强制)**:检查接口返回体 `unmatched`/`unmatched_folders` 字段——若 `unmatched>0` 且含本次视频文件夹名,说明 aweme_id 未能与库内视频关联(入库失败),必须明确汇报「入库关联失败 + unmatched 明细」,禁止报成功;返回正常后再查询库中该视频(按 aweme_id 或标题)确能查到 source 记录方为成功。完成后汇报 detailId、content/analysis 是否齐备、保存路径与入库关联结果(成功/失败含 unmatched)。技能边界:只做该单视频解析,不抓取账号信息、不提炼账号设定、不创作/改写脚本。';
const SKILL_HINT_VIDEO_PARSE = '请调用 Skill 工具加载「mcn-dou-analysis」技能(技能根目录 = 用户技能目录 ~/.workbuddy/skills/短视频工作台/subskills/mcn-dou-analysis/,先读取其 SKILL.md 及 references 规范;若 Skill 工具无法加载,请直接读取该目录下的 SKILL.md 文件)。本任务为技能功能五「解析视频」:对单条抖音视频做 MCP 内容解析。按 references/feature/05_解析视频.md 流程执行:① 调用 MCP 工具 upload_douyin_video 提交视频链接(share_text=该视频抖音链接)获取 detailId;② 等待解析完成(新视频约 3-5 分钟,用 sleep 等待后查询,content 已出而 analysis 未出时稍后重试);③ 用 short_video_detail 查询解析结果 content(视频脚本/分镜头表)与 analysis(视频拆解/结构化JSON);④ 按 references/解析结果格式规范.md 用 scripts/json_tool.py 将 content.json 与 analysis.json 保存到产出目录对应视频文件夹;⑤ 解析结果入库:工作台环境(本机 ' + BASE + ')产物落盘后调用 POST /api/import/account(body {"account":"{达人昵称}"})同步入库,接口不可用则跳过不影响保存;**入库后必须自查关联是否成功(09-04 修复后强制)**:检查接口返回体 `unmatched`/`unmatched_folders` 字段——若 `unmatched>0` 且含本次视频文件夹名,说明 aweme_id 未能与库内视频关联(入库失败),必须明确汇报「入库关联失败 + unmatched 明细」,禁止报成功;返回正常后再查询库中该视频(按 aweme_id 或标题)确能查到 source 记录方为成功。完成后汇报 detailId、content/analysis 是否齐备、保存路径与入库关联结果(成功/失败含 unmatched)。技能边界:只做该单视频解析,不抓取账号信息、不提炼账号设定、不创作/改写脚本。';
/* 09-02 v25:AI创作任务完成后的写库回调指令——否则脚本只落会话/产出目录,工作台「AI写脚本」列表看不到。
参考选题创作(有原视频,如视频行 AI创作):传 videoId/awemeId → rewrite_log.video_id 关联原视频;
原创选题创作(无原视频,如账号列表 AI创作 弹窗):不传 videoId → video_id 留空,归「原创选题创作」tab。
保存走本地服务 /api/dsh/script-save:server 自行解析 mcn-plugin.db 路径(跨开发机/dsh 环境最可靠),勿让 agent 猜库路径 */
保存走本地服务 /api/dsh/script-save:server 自行解析 mcn-plugin.db 路径(server 端解析最可靠),勿让 agent 猜库路径 */
/* 榜单数据更新技能声明(09-03 新增 09-04 四榜扩展:热点数据页「更新榜单」按钮 → 后台任务按 mcn-data-insight 落盘规范抓取四榜最新一期。
范围与调用次数已在点击确认弹窗中向用户明示并获授权(账号日榜 全品类 1 次 + 账号周榜 5 赛道 + 视频热榜/点赞榜,约 8 次红狐 API);
prompt 内保留「查询幂等铁律」与「落盘目录按环境解析」,禁止模型逐赛道/逐日循环浪费 API 配额 */
const SKILL_HINT_RANKING = '请调用 Skill 工具加载「mcn-data-insight」技能(技能根目录 = 用户技能目录 ~/.workbuddy/skills/短视频工作台/subskills/mcn-data-insight/,先读取其 SKILL.md 及 references 规范;若 Skill 工具无法加载,请直接读取该目录下的 SKILL.md 文件)。本任务为「榜单数据更新」(工作台热点数据页更新按钮触发,用户已确认本次调用范围与次数):按 SKILL.md「榜单数据落盘规范」将热点数据四榜最新一期数据落盘到本地 抖音榜单/ 目录:① 视频热榜 + 点赞榜:运行 scripts/fetch_week_ranks.py --board both(全品类单次调用,禁止逐赛道/逐日循环);② 账号日榜:按 subskills/douyin-top-account 技能流程抓取最新一期账号日榜——用 fetch_rank.py --period day 直接获取全品类(不传赛道,单次调用),将结果写入 账号日榜/抖音账号日榜_{榜单日期}.json;③ 账号周榜:按 subskills/douyin-top-account 技能流程抓取保留 5 赛道(生活vlog/小剧场/亲子/美食/旅行)最新一期账号周榜,逐赛道写入 账号周榜/抖音周榜_{赛道}_{周一}.json;④ 落盘目录按技能规范按环境解析(开发机/用户环境 = 桌面 MCNSkill项目\\三方数据\\红狐数据\\抖音榜单\\;dsh 环境 = D:\\dshworkspace\\抖音榜单\\),新产物入 账号日榜/账号周榜/视频热榜/点赞榜 四个子目录,文件名保持 抖音账号日榜_/抖音周榜_ 前缀;⑤ 查询幂等铁律:每次调用前先 ls 目标目录,同榜名+赛道+数据日期文件已存在则直接跳过对应 API 调用(红狐 API 按次计费);⑥ 数据日期取 API 返回的榜单对应日期(最新一期),非获取时刻。完成后汇报:四榜各写入的文件路径与数据期、缓存命中跳过项、实际 API 调用次数。技能边界:只做榜单数据抓取与落盘,不做账号分析/脚本创作等其他能力。';
const SKILL_HINT_RANKING = '请调用 Skill 工具加载「mcn-data-insight」技能(技能根目录 = 用户技能目录 ~/.workbuddy/skills/短视频工作台/subskills/mcn-data-insight/,先读取其 SKILL.md 及 references 规范;若 Skill 工具无法加载,请直接读取该目录下的 SKILL.md 文件)。本任务为「榜单数据更新」(工作台热点数据页更新按钮触发,用户已确认本次调用范围与次数):按 SKILL.md「榜单数据落盘规范」将热点数据四榜最新一期数据落盘到本地 抖音榜单/ 目录:① 视频热榜 + 点赞榜:运行 scripts/fetch_week_ranks.py --board both(全品类单次调用,禁止逐赛道/逐日循环);② 账号日榜:按 subskills/douyin-top-account 技能流程抓取最新一期账号日榜——用 fetch_rank.py --period day 直接获取全品类(不传赛道,单次调用),将结果写入 账号日榜/抖音账号日榜_{榜单日期}.json;③ 账号周榜:按 subskills/douyin-top-account 技能流程抓取保留 5 赛道(生活vlog/小剧场/亲子/美食/旅行)最新一期账号周榜,逐赛道写入 账号周榜/抖音周榜_{赛道}_{周一}.json;④ 落盘目录:桌面 MCNSkill项目\\三方数据\\红狐数据\\抖音榜单\\(按运行系统解析真实桌面,兼容 OneDrive 重定向;环境变量 MCN_RANKING_DIR 可覆盖),新产物入 账号日榜/账号周榜/视频热榜/点赞榜 四个子目录,文件名保持 抖音账号日榜_/抖音周榜_ 前缀;⑤ 查询幂等铁律:每次调用前先 ls 目标目录,同榜名+赛道+数据日期文件已存在则直接跳过对应 API 调用(红狐 API 按次计费);⑥ 数据日期取 API 返回的榜单对应日期(最新一期),非获取时刻。完成后汇报:四榜各写入的文件路径与数据期、缓存命中跳过项、实际 API 调用次数。技能边界:只做榜单数据抓取与落盘,不做账号分析/脚本创作等其他能力。';
/* 账号数据分析技能声明(09-04 新增:账号详情「数据分析」tab 空态按钮 → 后台任务按 mcn-dou-analysis 功能四执行。
功能四=基于近 4 个月视频数据(每月最好/最差各 1 条)先逐条拆解再汇总分析,产出 8 章结构报告;
数据源优先读取产出目录短视频表格 xlsx,缺失则以工作台库中视频数据为准(不重新抓取账号) */
const SKILL_HINT_ANALYZE = '请调用 Skill 工具加载「mcn-dou-analysis」技能(技能根目录 = 用户技能目录 ~/.workbuddy/skills/短视频工作台/subskills/mcn-dou-analysis/,先读取其 SKILL.md 及 references 规范;若 Skill 工具无法加载,请直接读取该目录下的 SKILL.md 文件)。本任务为技能功能四「分析视频数据(账号数据分析)」:先读 SKILL.md「功能总览」定位功能四,再按 references/feature/04_分析视频数据.md 与 references/账号数据分析方法.md 执行——基于该账号近 4 个月视频数据(每月按 点赞数+分享数 降序取最好 1 条 + 最差 1 条,共最多 8 条;某月不足 2 条则全取),先逐条拆解(可复用该账号已有 MCP 解析 analysis,缺解析的按规范提交补齐)→ 拆解全部完成后再进入汇总分析(两段式强制,禁止跳过拆解直接写报告),按 3.1-3.6 维度产出 8 章结构报告。数据来源:优先读取该账号产出目录的 短视频表格*.xlsx(13 列标准格式);文件缺失时以工作台库中该账号视频数据为准(无需重新抓取账号信息/视频列表,属数据更新场景时按规范处理)。红线:所有分析结论必须具体到视频标题 + 数值(点赞/评论/分享/收藏等),私有数据(完播率/GMV 等)拿不到时标注「无数据」禁止推断;无 MCP 解析的视频只做数据层分析、禁止作为内容层结论论据(注明「无解析,植入方式待验证」)。产物落盘到该账号产出目录 {达人昵称}账号数据分析.md;落盘后调用 POST /api/import/account(body {"account":"{达人昵称}"})同步入库(工作台环境 http://localhost:8900,接口不可用则跳过不影响保存)。技能边界:只做账号数据分析报告,不生成/改写脚本(属「短视频工作台」技能 S1-S11 流程),不提炼账号设定,不触发账号信息/视频列表抓取流程。';
const SKILL_HINT_ANALYZE = '请调用 Skill 工具加载「mcn-dou-analysis」技能(技能根目录 = 用户技能目录 ~/.workbuddy/skills/短视频工作台/subskills/mcn-dou-analysis/,先读取其 SKILL.md 及 references 规范;若 Skill 工具无法加载,请直接读取该目录下的 SKILL.md 文件)。本任务为技能功能四「分析视频数据(账号数据分析)」:先读 SKILL.md「功能总览」定位功能四,再按 references/feature/04_分析视频数据.md 与 references/账号数据分析方法.md 执行——基于该账号近 4 个月视频数据(每月按 点赞数+分享数 降序取最好 1 条 + 最差 1 条,共最多 8 条;某月不足 2 条则全取),先逐条拆解(可复用该账号已有 MCP 解析 analysis,缺解析的按规范提交补齐)→ 拆解全部完成后再进入汇总分析(两段式强制,禁止跳过拆解直接写报告),按 3.1-3.6 维度产出 8 章结构报告。数据来源:优先读取该账号产出目录的 短视频表格*.xlsx(13 列标准格式);文件缺失时以工作台库中该账号视频数据为准(无需重新抓取账号信息/视频列表,属数据更新场景时按规范处理)。红线:所有分析结论必须具体到视频标题 + 数值(点赞/评论/分享/收藏等),私有数据(完播率/GMV 等)拿不到时标注「无数据」禁止推断;无 MCP 解析的视频只做数据层分析、禁止作为内容层结论论据(注明「无解析,植入方式待验证」)。产物落盘到该账号产出目录 {达人昵称}账号数据分析.md;落盘后调用 POST /api/import/account(body {"account":"{达人昵称}"})同步入库(工作台环境 ' + BASE + ',接口不可用则跳过不影响保存)。技能边界:只做账号数据分析报告,不生成/改写脚本(属「短视频工作台」技能 S1-S11 流程),不提炼账号设定,不触发账号信息/视频列表抓取流程。';
const saveHintOf = (ctx) => {
const ref = Number(ctx.videoId) > 0;
const lines = [];
@@ -38,8 +42,8 @@ const saveHintOf = (ctx) => {
lines.push(` "accountId": ${Number(ctx.accountId) || 'null'},`);
lines.push(` "title": "(一句话脚本标题,供列表展示)",`);
lines.push(` "scriptText": "(完整脚本全文)"`);
return `\n\n【任务完成后必须执行】将完整脚本保存到工作台数据库(工作台与本任务同机运行,服务地址 http://localhost:8900):\n`
+ `1. 用 fetch 发起 POST http://localhost:8900/api/dsh/script-save;body 用 JSON.stringify 构造(scriptText 为完整脚本全文,注意转义,不要手拼 JSON):\n{\n${lines.join('\n')}\n}\n`
return `\n\n【任务完成后必须执行】将完整脚本保存到工作台数据库(工作台与本任务同机运行,服务地址 ${BASE}):\n`
+ `1. 用 fetch 发起 POST ${BASE}/api/dsh/script-save;body 用 JSON.stringify 构造(scriptText 为完整脚本全文,注意转义,不要手拼 JSON):\n{\n${lines.join('\n')}\n}\n`
+ (ref ? '(参考选题创作:脚本归入「参考选题创作」列表,关联该原视频)\n' : '(原创选题创作:不传 videoId,脚本归入「原创选题创作」列表)\n')
+ `2. 响应含 ok:true 即已入库;失败重试一次,仍失败则在回复中说明原因。\n完成后回复:脚本标题 + 保存结果。`;
};
@@ -698,7 +702,7 @@ async function renderRanking(view) {
['hot', '视频热榜', '近7日数据'],
['likes', '点赞榜', '近7日数据'],
];
const mkState = () => ({ date: '', category: '', sortBy: '', order: '', page: 1, pageSize: 20, dates: [], categories: [] });
const mkState = () => ({ date: '', category: '', sortBy: '', order: '', page: 1, pageSize: 20, dates: [], categories: [], ranges: {} });
// 每榜独立状态(切 tab 保留各自筛选/翻页/日期)
const st = { week: mkState(), day: mkState(), hot: mkState(), likes: mkState() };
let tab = 'week';
@@ -713,7 +717,7 @@ async function renderRanking(view) {
${s.categories.map((c) => `<option value="${esc(c)}" ${c === s.category ? 'selected' : ''}>${esc(c)}</option>`).join('')}
</select>
<button class="btn" id="rankPrev" title="上一期">◀</button>
<span class="pager-info" title="选择日期">${esc(s.date)}</span>
<span class="pager-info rank-period" title="该期覆盖区间(期号 = 该周周一)">${esc(fmtRankPeriod(s.date, s.ranges))}</span>
<button class="btn" id="rankNext" title="下一期">▶</button>
</div>` : '';
return `<div class="page-toolbar">
@@ -800,7 +804,7 @@ async function renderRanking(view) {
if (!m.ok) { view.innerHTML = `<div class="page">${toolbar(st[tab], false)}<div class="empty">${esc(m.error || '加载失败')}</div></div>`; bindShell(); return null; }
metas[tab] = m;
const s = st[tab];
s.dates = m.dates || []; s.categories = m.categories || [];
s.dates = m.dates || []; s.categories = m.categories || []; s.ranges = m.ranges || {};
if (!s.date) { s.date = m.date || (s.dates[0] || ''); s.category = m.category || ''; }
return m;
}
@@ -2005,9 +2009,9 @@ async function renderReviews(view) {
}
const [desc, prompt] = hasSource
? ['将对该 AI 脚本做复盘评分(AI脚本 / 原视频脚本 / 对比分析三种模式)。',
`${SKILL_HINT_REVIEW}\n请对这条 AI 脚本做复盘评分(AI脚本 / 原视频脚本 / 对比分析三种模式)。`]
`${SKILL_HINT_REVIEW}\n请对这条 AI 脚本做复盘评分(AI脚本 / 原视频脚本 / 对比分析三种模式)。\n${REVIEW_ISSUE_RULE}`]
: ['将对该 AI 脚本做质量复盘评分(该脚本无原视频脚本,仅评 AI 脚本:独立分析模式·10 维评分制)。',
`${SKILL_HINT_REVIEW}\n请对这条 AI 脚本做质量复盘评分(独立分析模式·10 维评分制)。该脚本无原视频脚本,只评 AI 脚本本身,不做源脚本复盘、不做对比分析。`];
`${SKILL_HINT_REVIEW}\n请对这条 AI 脚本做质量复盘评分(独立分析模式·10 维评分制)。该脚本无原视频脚本,只评 AI 脚本本身,不做源脚本复盘、不做对比分析。\n${REVIEW_ISSUE_RULE}`];
confirmRun({
title: hasSource ? '复盘评分' : 'AI脚本复盘',
desc,
@@ -2039,7 +2043,7 @@ async function renderReviews(view) {
body = `<div class="score-box"><div class="score-box-title">总分 ${fmtScore(Number(rv.total))}</div>${scoreBar(dimsObj)}</div>`;
}
let issues = []; try { issues = JSON.parse(rv.issues_json || '[]'); } catch (e) {}
if (issues.length) body += `<div class="review-issues"><div class="rw-pane-title">问题清单</div>${issues.map((it) => `<div class="issue-row"><span style="color:${it.severity === '高' ? '#d9534f' : it.severity === '中' ? '#d99a2b' : '#3a9e6d'};font-weight:600">${esc(it.severity || '低')}</span><span>${esc(it.issue || '')}</span>${it.suggestion ? `<span class="dim">→ ${esc(it.suggestion)}</span>` : ''}</div>`).join('')}</div>`;
if (issues.length) body += `<div class="review-issues"><div class="rw-pane-title">问题清单</div>${issues.map(issueRowHtml).join('')}</div>`;
if (rv.summary) body += `<div class="review-summary">综合评语:${esc(rv.summary)}</div>`;
if (rv.content) body += `<div class="md-body" style="border-top:1px solid var(--border);padding-top:10px">${renderMarkdown(rv.content)}</div>`;
const mask = document.createElement('div'); mask.className = 'modal-mask';
@@ -2057,7 +2061,7 @@ async function renderReviews(view) {
布局同 AI写脚本查看弹窗(rw-stack 横向列):源脚本(可折叠,有原视频时) | AI脚本(版本切换) | 右侧列
右侧列头部 tab:脚本诊断(默认) | 分镜提示词
—— 诊断内容 = 当前版本 AI 脚本最新复盘(script_review type=ai 优先;compare 取 AI 视角总分并带源对比分)
—— 分镜内容 = 该视频下 storyboard_log 中属当前版本的记录 + 生成/重新生成入口(仅参考选题脚本;原创选题无视频载体不支持)
—— 分镜内容 = storyboard_log 中属当前版本的记录 + 生成/重新生成入口(09-10 起:原创选题无视频载体时改按 rewrite_id 关联,同样支持)
—— 数据源:/api/dsh/video-script + /api/dsh/video-detail(参考选题)/ /api/dsh/custom-script(原创选题)
+ /api/dsh/review-full?rewriteId=(当前版本诊断)+ /api/dsh/storyboards?videoId=(分镜) */
const DIAG_DIMS = ['创意看点', '设定承接', '框架节奏', '爆款开场', '叙事逻辑', '极致事件', '格式规范性', '台词口语化', '内容合规', '教程完整链', '其他'];
@@ -2071,6 +2075,32 @@ const diagVisibleDims = (dims) => (diagDimHas(dims, '教程完整链') ? DIAG_DI
const diagScoreColor = (v) => (v >= 75 ? '#3a9e6d' : v >= 60 ? '#d99a2b' : '#d9534f');
const diagFmtScore = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : '-');
/* 问题清单字段归一化(09-10 修复):模型输出的 issues_json schema 不稳定——
历史同时存在 {sev:'high', issue, fix} 与 {severity:'高', issue, suggestion} 两套写法,
前端原按后者严格读取 → severity 取不到 → 严重度全部兜底成绿色(60 分却全绿),
且 suggestion 取不到 → 修改建议整段不显示。此处统一归一化,兼容两种字段与中英取值。 */
const normIssue = (it) => {
const o = it && typeof it === 'object' ? it : {};
const pick = (...ks) => { for (const k of ks) { if (o[k] != null && String(o[k]).trim() !== '') return String(o[k]).trim(); } return ''; };
const raw = pick('severity', 'sev', 'level', '严重度').toLowerCase();
return {
level: /高|high|^h$|p0|严重/.test(raw) ? '高' : /中|mid|medium|^m$|p1/.test(raw) ? '中' : '低',
text: pick('issue', 'desc', 'text', '问题'),
fix: pick('suggestion', 'fix', 'suggest', '建议', '修改建议'),
dim: pick('dim', 'dimension', '维度'),
loc: pick('loc', 'position', '定位'),
};
};
const issueLevelColor = (lv) => (lv === '高' ? '#d9534f' : lv === '中' ? '#d99a2b' : '#3a9e6d');
/* 复盘落库 issues_json 的字段契约(09-10):作为 prompt 后缀发给模型,从源头避免 schema 漂移 */
const REVIEW_ISSUE_RULE = '【落库字段要求】issues_json 必须是数组,每条固定为 {"severity":"高|中|低","dim":"维度名","loc":"定位(场次/时间码)","issue":"问题描述","suggestion":"修改建议","step":"环节(S5-S11)"};severity 只能取中文「高/中/低」,禁止写成英文 high/medium/low,也禁止改用 sev/level 等其它字段名——否则工作台无法按严重度着色(会全部显示成绿色),修改建议也不会显示。';
/* 单条问题渲染:严重度(色) + [所属维度] + 问题描述 + →修改建议 */
const issueRowHtml = (raw) => {
const it = normIssue(raw);
const dimTag = it.dim ? `<span class="dim" style="flex:none">[${esc(it.dim)}]</span>` : '';
return `<div class="issue-row"><span style="color:${issueLevelColor(it.level)};font-weight:600;flex:none">${esc(it.level)}</span>${dimTag}<span style="flex:1;min-width:120px">${esc(it.text)}</span>${it.fix ? `<span class="dim" style="flex:1;min-width:120px">→ ${esc(it.fix)}</span>` : ''}</div>`;
};
/* 分镜文本渲染:按「段 N(起-止 秒)|标题」分块(继承原 AI写脚本查看弹窗逻辑;分镜时间戳分段格式) */
function renderSbBlocks(text) {
const blocks = String(text || '').split(/\n\s*---+\s*\n/).map((b) => b.trim()).filter(Boolean);
@@ -2101,7 +2131,7 @@ async function reportFullReview(reviewId) {
body = `<div class="score-box"><div class="score-box-title">总分 ${diagFmtScore(Number(rv.total))}</div>${scoreBar(dimsObj)}</div>`;
}
let issues = []; try { issues = JSON.parse(rv.issues_json || '[]'); } catch (e) {}
if (issues.length) body += `<div class="review-issues"><div class="rw-pane-title">问题清单</div>${issues.map((it) => `<div class="issue-row"><span style="color:${it.severity === '高' ? '#d9534f' : it.severity === '中' ? '#d99a2b' : '#3a9e6d'};font-weight:600">${esc(it.severity || '低')}</span><span>${esc(it.issue || '')}</span>${it.suggestion ? `<span class="dim">→ ${esc(it.suggestion)}</span>` : ''}</div>`).join('')}</div>`;
if (issues.length) body += `<div class="review-issues"><div class="rw-pane-title">问题清单</div>${issues.map(issueRowHtml).join('')}</div>`;
if (rv.summary) body += `<div class="review-summary">综合评语:${esc(rv.summary)}</div>`;
if (rv.content) body += `<div class="md-body" style="border-top:1px solid var(--border);padding-top:10px">${renderMarkdown(rv.content)}</div>`;
const mask = document.createElement('div'); mask.className = 'modal-mask';
@@ -2148,8 +2178,11 @@ async function openScriptModal({ rewriteId, videoId } = {}) {
const hasSource = vid > 0 && !!source;
let stab = 'diag'; /* 右侧 tab:diag=脚本诊断(默认)/ sb=分镜提示词 */
let showSrc = false, sbIdx = -1, showSide = false; /* R41:showSide=右侧「脚本诊断|分镜提示词」面板是否展开(收起后只展示 AI 脚本);R50:默认收起=不显示复盘和分镜;R55:sbIdx=-1=未选分镜版本,sideContent 自动指向该 AI 版本的最新分镜(末位) */
const stb = vid > 0 ? await api('/api/dsh/storyboards?videoId=' + vid).catch(() => ({ items: [] })) : { items: [] };
const storyboards = ((stb && stb.items) || []).slice().reverse(); /* 同版本条:旧→新,末位=最新分镜 */
/* 09-10:原创选题(vid=0)改按 rewriteId 查分镜——原逻辑只按 videoId,导致无视频载体的脚本永远查不到分镜、也没有生成入口 */
const sbQueryOf = (curId) => (vid > 0 ? 'videoId=' + vid : 'rewriteId=' + encodeURIComponent(curId));
const fetchSb = async (curId) => ((await api('/api/dsh/storyboards?' + sbQueryOf(curId)).catch(() => ({ items: [] }))) || {}).items || [];
const stb = { items: await fetchSb((rewrites[verIdx] || {}).id) };
let storyboards = ((stb && stb.items) || []).slice().reverse(); /* 同版本条:旧→新,末位=最新分镜 */
const visibleSbOf = (curId) => storyboards.filter((s) => s.rewrite_id == null || String(s.rewrite_id) === String(curId));
/* 复盘任务触发:有源脚本 → 三模式(AI/源/对比);无源 → 独立分析仅评 AI 脚本(同脚本诊断页行按钮口径) */
@@ -2158,9 +2191,9 @@ async function openScriptModal({ rewriteId, videoId } = {}) {
try {
const [desc, prompt] = hasSource
? ['将对该 AI 脚本做复盘评分(AI脚本 / 原视频脚本 / 对比分析三种模式)。',
`${SKILL_HINT_REVIEW}\n请对这条 AI 脚本做复盘评分(AI脚本 / 原视频脚本 / 对比分析三种模式)。`]
`${SKILL_HINT_REVIEW}\n请对这条 AI 脚本做复盘评分(AI脚本 / 原视频脚本 / 对比分析三种模式)。\n${REVIEW_ISSUE_RULE}`]
: ['将对该 AI 脚本做质量复盘评分(该脚本无原视频脚本,仅评 AI 脚本:独立分析模式·10 维评分制)。',
`${SKILL_HINT_REVIEW}\n请对这条 AI 脚本做质量复盘评分(独立分析模式·10 维评分制)。该脚本无原视频脚本,只评 AI 脚本本身,不做源脚本复盘、不做对比分析。`];
`${SKILL_HINT_REVIEW}\n请对这条 AI 脚本做质量复盘评分(独立分析模式·10 维评分制)。该脚本无原视频脚本,只评 AI 脚本本身,不做源脚本复盘、不做对比分析。\n${REVIEW_ISSUE_RULE}`];
confirmRun({ title: hasSource ? '复盘评分' : 'AI脚本复盘', desc, prompt, label: '复盘指令', skills: ['脚本复盘'], btn });
} finally { delete btn.dataset.lock; }
};
@@ -2170,7 +2203,10 @@ async function openScriptModal({ rewriteId, videoId } = {}) {
title: regenerate ? '重新生成分镜' : '生成分镜',
desc: '将基于该 AI 脚本(AI 创作的故事脚本),展开成 AI 分镜提示词(按分镜技能生成镜头级描述)。',
target: `《${title}》`,
prompt: `${SKILL_HINT_STORYBOARD}\n请将视频《${title}》的 AI 脚本${regenerate ? '重新' : ''}展开成 AI 分镜提示词(F4 生成分镜,镜头级描述)。`,
/* 09-10:补落库指令——此前 prompt 只让"产出分镜",未要求写 storyboard_log,
入库全靠模型自由发挥(故仅 3 条记录、且原创选题因无 video_id 无从关联)。
现明确 video_id / rewrite_id,原创选题(vid=0)靠 rewrite_id 关联即可挂分镜。 */
prompt: `${SKILL_HINT_STORYBOARD}\n请将视频《${title}》的 AI 脚本${regenerate ? '重新' : ''}展开成 AI 分镜提示词(F4 生成分镜,镜头级描述)。\n\n【任务完成后必须执行】将分镜提示词全文写入工作台数据库 storyboard_log 表(数据库在本会话 cwd 下,文件名 mcn-plugin.db),字段:video_id=${vid > 0 ? vid : 'null'}、rewrite_id=${(curOf() || {}).id || ''}、script_text=分镜提示词全文、status='done'。(用 node:sqlite 或 sqlite3 写入;表不存在则创建)`,
label: '分镜指令',
skills: ['短视频分镜脚本'],
btn,
@@ -2194,7 +2230,7 @@ async function openScriptModal({ rewriteId, videoId } = {}) {
const scoreBar = diagVisibleDims(dims).map((k) => { const v = diagDimVal(dims, k); const has = typeof v === 'number' && Number.isFinite(v); return `<div class="score-row"><span class="score-name">${esc(k)}</span><div class="score-bar"><div class="score-fill" style="width:${has ? v : 0}%;background:${has ? diagScoreColor(v) : '#eee'}"></div></div><span class="score-val" style="color:${has ? diagScoreColor(v) : 'var(--dim)'}">${has ? v : '—'}</span></div>`; }).join('');
let html = `<div class="score-box"><div class="score-box-title">AI 总分 ${diagFmtScore(totalFin ? total : Number(rv.total))}${rv.type === 'compare' && Number.isFinite(srcTotal) ? `(对比源脚本 ${diagFmtScore(srcTotal)})` : ''}${rv.verdict ? ` · <span style="color:${totalFin ? diagScoreColor(total) : 'var(--dim)'}">${esc(rv.verdict)}</span>` : ''}</div>${scoreBar}</div>`;
let issues = []; try { issues = JSON.parse(rv.issues_json || '[]'); } catch (e) {}
if (issues.length) html += `<div class="review-issues"><div class="rw-pane-title">问题清单</div>${issues.map((it) => `<div class="issue-row"><span style="color:${it.severity === '高' ? '#d9534f' : it.severity === '中' ? '#d99a2b' : '#3a9e6d'};font-weight:600">${esc(it.severity || '低')}</span><span>${esc(it.issue || '')}</span>${it.suggestion ? `<span class="dim">→ ${esc(it.suggestion)}</span>` : ''}</div>`).join('')}</div>`;
if (issues.length) html += `<div class="review-issues"><div class="rw-pane-title">问题清单</div>${issues.map(issueRowHtml).join('')}</div>`;
if (rv.summary) html += `<div class="review-summary">综合评语:${esc(rv.summary)}</div>`;
html += `<div><a class="link" data-review="${rv.id}">查看完整复盘报告 →</a></div>`;
return html;
@@ -2283,12 +2319,10 @@ async function openScriptModal({ rewriteId, videoId } = {}) {
} else if (visibleSb.length) {
sideBody = sb ? renderSbBlocks(sb.script_text) : '' /* R51:空态提示清空——不再显示「(暂无分镜提示词)」 */;
sideFoot = verCtrl('s', sbIdx, visibleSb.length, '分镜', '重新生成', sb ? sb.created_time : '');
} else if (vid > 0) {
} else {
/* 09-10:移除 vid>0 限制——原创选题(无视频载体)同样可生成分镜,分镜按 rewrite_id 关联 */
sideBody = '' /* R51:空态提示清空——不再显示「(暂无分镜提示词)」 */;
sideFoot = `<div class="rw-sec-foot"><button class="btn btn-sm btn-view" data-gen-sb>${svgIcon('zap', 14, 'currentColor')} 生成分镜</button></div>`;
} else {
sideBody = '<div class="diag-empty">该脚本为原创选题(无关联视频),暂不支持分镜提示词</div>';
sideFoot = '';
}
return { sideBody, sideFoot };
}
@@ -2344,6 +2378,8 @@ async function openScriptModal({ rewriteId, videoId } = {}) {
[...sideSec.children].forEach((ch) => { if (ch.classList && ch.classList.contains('rw-sec-foot')) ch.remove(); });
if (bodyEl) bodyEl.innerHTML = '<div class="dim" style="padding:12px 2px">加载中…</div>';
const token = ++sideToken;
/* 原创选题(vid=0)分镜按 rewrite_id 查,切版本必须重查,否则会显示上一版本的分镜 */
if (!(vid > 0)) storyboards = (await fetchSb(c.id)).slice().reverse();
const { sideBody, sideFoot } = await sideContent(c);
if (!mask.isConnected || token !== sideToken) return; /* 弹窗已关闭 / 期间又切了 tab → 丢弃过期结果 */
if (bodyEl) bodyEl.innerHTML = sideBody;
@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>短视频工作台</title>
<link rel="stylesheet" href="style.css?v=20260904i">
<link rel="stylesheet" href="style.css?v=20260910c">
<script src="vendor/echarts.min.js"></script>
</head>
<body>
@@ -41,8 +41,8 @@
</div>
</div>
<script src="app.js?v=20260904i"></script>
<script src="app.js?v=20260910b"></script>
<script src="help-guide.js?v=20260904e"></script>
<script src="data-pages.js?v=20260904v"></script>
<script src="data-pages.js?v=20260910d"></script>
</body>
</html>
@@ -653,7 +653,7 @@ td.td-title { max-width: 240px; }
.score-box { flex: 1; padding: 10px; border: 1px solid var(--border); border-radius: 8px; }
.score-box-title { font-weight: 600; margin-bottom: 8px; }
.review-issues { margin-bottom: 10px; }
.issue-row { display: flex; gap: 6px; font-size: 14px; margin-bottom: 4px; }
.issue-row { display: flex; gap: 6px; font-size: 14px; margin-bottom: 4px; flex-wrap: wrap; } /* 09-10:问题/建议较长时换行,避免挤压成一行读不清 */
.review-summary { margin-bottom: 10px; font-size: 15px; }
.diag-empty { color: var(--dim); font-size: 14px; padding: 14px 2px 10px; line-height: 1.6; } /* AI脚本诊断弹窗:该版本未诊断时的占位提示 */
@@ -678,7 +678,9 @@ td.td-title { max-width: 240px; }
.rank-placeholder { border: 1px dashed var(--border); border-radius: 10px; padding: 36px 12px; text-align: center; color: var(--dim); background: var(--panel); font-size: 14px; }
.rank-tools { display: flex; align-items: center; gap: 6px; margin-left: auto; }
.rank-filter { padding: 3px 6px; font-size: 14px; max-width: 110px; }
.rank-date-label { font-size: 14px; color: var(--dim); min-width: 62px; text-align: center; white-space: nowrap; }
/* 09-10:显示期区间「08/31 ~ 09/06」替代裸期号,宽度按最长文本留足避免切期时按钮抖动 */
.rank-date-label { font-size: 14px; color: var(--dim); min-width: 104px; text-align: center; white-space: nowrap; }
.rank-period { min-width: 104px; text-align: center; display: inline-block; white-space: nowrap; }
.rank-preview-list { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; background: var(--panel); box-shadow: var(--shadow); }
.rank-row { display: grid; grid-template-columns: 34px 84px minmax(80px, 1fr) 76px repeat(4, 76px) 72px; align-items: center; gap: 6px; padding: 6px 10px; font-size: 14px; border-bottom: 1px solid var(--border); }
/* R49 首页三 tab 行高对齐(09-03):账号周榜行高 40px(功能列按钮撑高),热榜/点赞榜 30.7px——统一数据行 min-height 40px */
@@ -1,6 +1,6 @@
// 短视频工作台 - 本地 Web 服务(零依赖 Node 实现)
// 启动: node server.js [端口] → http://localhost:8900(默认;可传参指定端口,如 node server.js 9000)
// 功能: 技能导航 + 产出内容浏览(默认读桌面 MCNSkill项目)+ dsh 数据功能页(账号/视频/脚本/复盘/周榜,只读)
// 功能: 技能导航 + 产出内容浏览(默认读桌面 <outputRoot>,根目录名由 config.json 决定)+ dsh 数据功能页(账号/视频/脚本/复盘/周榜,只读)
'use strict';
const http = require('http');
@@ -11,14 +11,24 @@ const { execFileSync } = require('child_process');
const { URL } = require('url');
const dsh = require('./dsh-data');
const { parseAttachment } = require('./scripts/attachment-parser');
const { loadConfig } = require('./load-config');
// 09-01 默认端口由 8899 改为 8900:本机 8899 被小米 PC 管家 MiPCAudio.exe 系统服务占用(0.0.0.0 监听且自动复活),
// 此时 127.0.0.1:8899 绑定不生效 → 默认直接起在 8900;仍可 `node server.js <端口>` 显式换端口
const PORT_BASE = parseInt(process.argv[2], 10) || 8900;
const ROOT = __dirname;
const PUBLIC_DIR = path.join(ROOT, 'public');
const CONFIG_FILE = path.join(ROOT, 'config.json');
// 09-10 配置收敛:工作台设定(端口/DB名/产出根/榜单目录)统一收进 config.json,代码层一律从 CFG 读,
// 避免调整时到处改(config 缺失/损坏时回退 load-config.cjs 默认值)。argv 端口优先级仍高于 config。
const CFG = loadConfig();
const PORT_BASE = parseInt(process.argv[2], 10) || CFG.port;
// 09-10 路径治本(配套「仓库特征判定」):工作台运行时路径一律从 ROOT 动态推导,禁止写死盘符。
// 根因:原写死盘符指向仓库外的固定目录,仓库迁移后该目录不存在 → 浏览器锁失效、会话 cwd 悬空。
const BROWSER_LOCK_FILE = path.join(ROOT, '.browser-lock'); // 浏览器互斥锁文件(与工作台同目录)
const SESSION_CWD = ROOT; // 工作台触发的 AI 会话 cwd(分组名 = mcn-work-shop)
// 09-07:prompt 模板文件化(prompts/*.md)——规则迭代只改文件不碰代码;文件缺失/损坏回退 null,调用方自行兜底。
// 模板正文取 ==PROMPT== 与 ==CHANGELOG== 之间(头部说明/文末变更记录不发给模型)
const PROMPTS_DIR = path.join(ROOT, 'prompts');
@@ -45,22 +55,14 @@ function loadPrompt(name) {
} catch (e) { return null; }
}
// ---- 默认根目录:桌面/MCNSkill项目 ----
const DEFAULT_ROOT = path.join(os.homedir(), 'Desktop', 'MCNSkill项目');
// ---- 默认根目录:桌面/<CFG.outputRoot> ----
const DEFAULT_ROOT = path.join(os.homedir(), 'Desktop', CFG.outputRoot);
// ---- 配置读写(自定义根目录列表)----
function loadConfig() {
try {
if (fs.existsSync(CONFIG_FILE)) {
const c = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
if (Array.isArray(c.extraRoots)) return { extraRoots: c.extraRoots };
}
} catch (e) { /* 忽略损坏配置 */ }
return { extraRoots: [] };
}
function saveConfig(cfg) {
fs.writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2), 'utf8');
// ---- 配置读写(自定义根目录列表;loadConfig 来自 load-config.js,每次读盘取最新值)----
function saveConfig(patch) {
// 合并写盘:保留 config.json 里的其它字段(port/dbName/outputRoot/businessDataDir),只更新传入的键
const merged = Object.assign({}, loadConfig(), patch);
fs.writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2), 'utf8');
}
// ---- 允许读取的根目录白名单 ----
@@ -338,7 +340,7 @@ function createOnceAutomation(name, prompt, skillsArr, connectorIdsArr) {
try {
const now = Date.now();
const id = 'automation-' + now;
const cwd = 'D:\\AgentSkill\\mcn-workshop'; // 工作台触发的会话归入 mcn-workshop 空间分组
const cwd = SESSION_CWD; // 工作台触发的会话归入 mcn-work-shop 空间分组(由 ROOT 动态推导)
const d = new Date(now + 5 * 1000);
const pad = (n) => String(n).padStart(2, '0');
const scheduledAt = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
@@ -490,7 +492,7 @@ const server = http.createServer((req, res) => {
// ---------- 数据功能(只读工作台自有数据库副本) ----------
const dshOk = (code, obj) => sendJSON(code, { ok: true, ...obj });
const dshGuard = (res2) => { if (!dsh.dbAvailable()) { sendJSON(200, { ok: false, error: '未检测到工作台数据库(mcn-plugin.db),本功能不可用' }); return true; } return false; };
const dshGuard = (res2) => { if (!dsh.dbAvailable()) { sendJSON(200, { ok: false, error: '未检测到工作台数据库(' + CFG.dbName + '),本功能不可用' }); return true; } return false; };
const qInt = (k, def) => { const v = Number(q.get(k)); return Number.isFinite(v) ? v : def; };
if (p === '/api/dsh/meta') {
@@ -627,7 +629,7 @@ const server = http.createServer((req, res) => {
}
if (p === '/api/dsh/storyboards') {
if (dshGuard()) return;
return dshOk(200, { items: dsh.listStoryboards(qInt('videoId', 0)) });
return dshOk(200, { items: dsh.listStoryboards(qInt('videoId', 0), qInt('rewriteId', 0)) });
}
if (p === '/api/dsh/review-detail') {
if (dshGuard()) return;
@@ -717,7 +719,7 @@ const server = http.createServer((req, res) => {
// 09-01 并发方案A:浏览器类任务(账号信息/视频列表/采集/导入)自动注入浏览器锁约束——
// 多个 AI 会话任务并行共享同一 9223 Chrome 实例,必须互斥使用,否则标签页互相导航抢占
const BROWSER_HINT = /账号信息|视频列表|保存并分析|采集|导入账号|网页采集|浏览器/;
const BROWSER_LOCK = 'D:\\\\AgentSkill\\\\mcn-workshop\\\\.browser-lock';
const BROWSER_LOCK = BROWSER_LOCK_FILE;
let finalPrompt = prompt.trim();
if (BROWSER_HINT.test(finalPrompt)) {
finalPrompt += `
@@ -733,7 +735,7 @@ const server = http.createServer((req, res) => {
const db = new DatabaseSync(process.env.WORKBUDDY_DB || path.join(os.homedir(), '.workbuddy', 'workbuddy.db'));
const now = Date.now();
const id = 'automation-' + now;
const cwd = 'D:\\AgentSkill\\mcn-workshop'; // 工作台触发的会话归入 mcn-workshop 空间分组
const cwd = SESSION_CWD; // 工作台触发的会话归入 mcn-work-shop 空间分组(由 ROOT 动态推导)
const d = new Date(Date.now() + 5 * 1000); // 未来 5 秒(08-31 根因:客户端只对未来 scheduledAt 补算 next_run_at;写 now=过去时间→不补算→调度器扫不到→卡死)
const pad = (n) => String(n).padStart(2, '0');
const scheduledAt = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
@@ -798,7 +800,7 @@ const server = http.createServer((req, res) => {
let contentText = '', personaText = '', videoRefText = '';
try {
const { DatabaseSync } = require('node:sqlite');
const db = new DatabaseSync(path.join(ROOT, 'mcn-plugin.db'), { readOnly: true });
const db = new DatabaseSync(path.join(ROOT, CFG.dbName), { readOnly: true });
const acc = db.prepare('SELECT * FROM hot_accounts WHERE account_name=? LIMIT 1').get(accountName);
if (acc) {
contentText = String(acc.content || '').slice(0, 500);
@@ -873,7 +875,7 @@ const server = http.createServer((req, res) => {
let personaText = '', contentText = '';
try {
const { DatabaseSync } = require('node:sqlite');
const db = new DatabaseSync(path.join(ROOT, 'mcn-plugin.db'), { readOnly: true });
const db = new DatabaseSync(path.join(ROOT, CFG.dbName), { readOnly: true });
const acc = db.prepare('SELECT * FROM hot_accounts WHERE account_name=? LIMIT 1').get(accountName);
if (acc) {
contentText = String(acc.content || '').slice(0, 300);
@@ -1019,7 +1021,7 @@ const server = http.createServer((req, res) => {
const accountDir = resolveAccountDir(account, root);
if (!accountDir) return sendErr(404, '账号产出目录不存在: ' + account);
const { DatabaseSync } = require('node:sqlite');
const db = new DatabaseSync(path.join(ROOT, 'mcn-plugin.db'));
const db = new DatabaseSync(path.join(ROOT, CFG.dbName));
const res = importAccountIntoDb(db, account, accountDir);
db.close();
log('分析产物入库: ' + account + ' → ' + JSON.stringify(res));
@@ -1042,12 +1044,14 @@ const server = http.createServer((req, res) => {
});
});
// ---- 会话自动清理:mcn-workshop 空间超 6 小时无活动的已结束会话自动清除 ----
// 09-01 用户需求:工作台触发(cwd=D:\AgentSkill\mcn-workshop)的 AI 会话任务,超过 6 小时自动清除
// 判定:cwd 含 mcn-workshop + status=completed(执行中一律不删)+ 未删过 + 最后活动时间超过 6 小时
// ---- 会话自动清理:mcn-work-shop 空间超 6 小时无活动的已结束会话自动清除 ----
// 09-01 用户需求:工作台触发(cwd = 工作台目录 SESSION_CWD)的 AI 会话任务,超过 6 小时自动清除
// 09-10 路径治本:cwd 由写死盘符改为 ROOT 动态推导(<技能根>/mcn-work-shop);
// 清理模式放宽为 '%mcn-work%',同时覆盖历史会话(旧 mcn-workshop)与新建会话(mcn-work-shop)
// 判定:cwd 含 mcn-work + status=completed(执行中一律不删)+ 未删过 + 最后活动时间超过 6 小时
// 方式:软删除(置 deleted_at),客户端左侧会话栏按 deleted_at 过滤即不再显示;可逆安全
const SESSION_TTL_MS = 6 * 3600 * 1000; // 6 小时
const SESSION_CWD_PATTERN = '%mcn-workshop%'; // 与 /api/run 写入的 cwd 一致
const SESSION_CWD_PATTERN = '%mcn-work%'; // 兼容 SESSION_CWD(mcn-work-shop)与历史 cwd(mcn-workshop)
const SESSION_CLEAN_INTERVAL_MS = 60 * 60 * 1000; // 每小时检查一次
function cleanupExpiredSessions() {
@@ -1064,7 +1068,7 @@ function cleanupExpiredSessions() {
AND COALESCE(last_activity_at, updated_at, created_at, 0) < ?
`).run(now, SESSION_CWD_PATTERN, cutoff);
db.close();
if (Number(r.changes) > 0) log(`会话自动清理:已清除 mcn-workshop 超 6 小时无活动的已完成会话 ${r.changes} 条`);
if (Number(r.changes) > 0) log(`会话自动清理:已清除 mcn-work-shop 超 6 小时无活动的已完成会话 ${r.changes} 条`);
} catch (e) {
console.error('会话自动清理失败:', e.message);
}
@@ -1,12 +1,16 @@
@echo off
rem MCN工作台 启动脚本(Windows 双击运行)
rem 优先使用 WorkBuddy managed node,回退系统 node
rem 优先使用 WorkBuddy managed node(自动探测版本目录,避免写死版本号),回退系统 node
setlocal
cd /d "%~dp0"
set "NODE_EXE=C:\Users\maidou\.workbuddy\binaries\node\versions\22.22.2\node.exe"
if exist "%NODE_EXE%" goto run
rem 09-10 修复:原写死 ...\versions\22.22.2\node.exe 已不存在(实际为 22.22.2-3)→ 改为遍历 versions 自动探测,版本升级免改脚本
set "NODE_EXE="
for /d %%v in ("%USERPROFILE%\.workbuddy\binaries\node\versions\*") do (
if exist "%%v\node.exe" set "NODE_EXE=%%v\node.exe"
)
if defined NODE_EXE goto run
where node >nul 2>nul
if %errorlevel%==0 (
@@ -20,7 +24,10 @@ exit /b 1
:run
echo 正在启动 MCN工作台...
start "" "%NODE_EXE%" server.js 8900
rem 09-10 配置收敛:端口从 config.json 读取(不再写死 8900),改 config.json 即可,无需改本脚本
for /f "usebackq delims=" %%p in (`"%NODE_EXE%" get-port.js`) do set "PORT=%%p"
if not defined PORT set "PORT=8900"
start "" "%NODE_EXE%" server.js
timeout /t 1 /nobreak >nul
start http://localhost:8900
start http://localhost:%PORT%
endlocal
@@ -2,9 +2,11 @@
用法: node submit-review-tasks.cjs [rewriteId1,rewriteId2,...] (不传则自动取 account_id=3 的最新 done 记录) */
const { DatabaseSync } = require('node:sqlite');
const path = require('path');
const { loadConfig } = require('./load-config');
const DB = path.join(__dirname, 'mcn-plugin.db');
const API = 'http://localhost:8900/api/run';
const CFG = loadConfig();
const DB = path.join(__dirname, CFG.dbName);
const API = `http://localhost:${CFG.port}/api/run`;
const ACCOUNT_ID = 3;
const db = new DatabaseSync(DB, { readOnly: true });
@@ -27,14 +29,19 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
脚本标题:《${r.title || r.video_title || ''}》(rewrite_log.id=${r.id},video_id=${r.video_id})
【任务完成后必须执行】
将复盘结果保存到工作台数据库 mcn-plugin.db 的 script_review 表(数据库路径:D:\\AgentSkill\\mcn-video-script\\project\\短视频脚本创作\\V1.0\\mcn-work-shop\\mcn-plugin.db),字段:
将复盘结果保存到工作台数据库 ${CFG.dbName} 的 script_review 表(数据库路径:${DB},即本会话 cwd 下的 ${CFG.dbName}),字段:
- type='ai'
- rewrite_id=${r.id}
- video_id=${r.video_id}
- dims_json=各维度评分 JSON(设定承接/框架节奏/钩子设置/叙事逻辑/极致事件/共鸣和共情/格式规范性/台词口语化/内容合规/教程完整链/其他)
- total=总分(数字)
- verdict=结论
- issues_json=问题清单 JSON
- issues_json=问题清单 JSON 数组,**每条字段固定如下(字段名与取值不可自创)**:
[{"severity":"高","dim":"叙事逻辑","loc":"场8 妈妈台词(510-520s)","issue":"问题描述","suggestion":"修改建议","step":"S7"},
{"severity":"中","dim":"台词口语化","loc":"场3","issue":"…","suggestion":"…","step":"S9"}]
· severity:**只能取「高」「中」「低」三个中文值**(禁止 high/medium/low、禁止 sev/level 等其它字段名)
· dim=所属维度名;loc=定位(场次/时间码);issue=问题描述;suggestion=修改建议;step=所属环节(S5-S11)
· ⚠️ 若 severity 缺失或写成英文,工作台无法按严重度着色,会全部显示成绿色
- content=复盘完整内容
- summary=摘要
- status='done'
@@ -86,7 +86,7 @@ references-add/
|:---|:---|
| 文件命名 | `{编号}_{类型名}_{细分}.md`(如 `01_美食库_川菜.md`) |
| 字段结构 | 与 `references/素材库/` 同类型文件保持一致(四维标签:极致维度×适用场景×镜头维度×画面维度) |
| 04_对话库 | 账号专属,不设通用增量,仍在产出根目录 `{账号名}/`(dsh 部署环境 `D:\dshworkspace\创作任务\`;开发机与用户环境桌面 `MCNSkill项目/`,或用户自定义路径)下管理 |
| 04_对话库 | 账号专属,不设通用增量,仍在产出根目录 `{账号名}/`(开发机与用户环境同构:桌面 `MCNSkill项目/`,或用户自定义路径)下管理 |
| 分块定义 | 不设增量,统一使用 `references/素材库/分块定义/` |
## 与 references/ 的关系
@@ -120,4 +120,4 @@ references/(标准) + references-add/(增量) → 合并结果
| **素材库** | 调取素材时同时检索 `references/素材库/` 和 `references-add/素材库/`。取并集,标准库优先,增量作为补充 |
| **接口调用** | 需要调用外部接口时,同时查阅 `references/接口调用/` 和 `references-add/接口调用/`。两个目录的接口定义**均可用** |
| **铁律避坑** | 创作完成前,**必须**检索 `铁律避坑规则/通用/` + `铁律避坑规则/分赛道/{当前赛道}/{当前内容形式}/`,逐条检查是否违反铁律 |
| **路径配置** | 每次执行创作流程前,**先查** `路径配置.md`。有「用户自定义路径」记录 → 以用户指定为准(**覆盖标准库默认桌面路径**);无记录 → 按三环境两步判定(本机无 `D:\AgentSkill` → 用户环境 → 桌面 `MCNSkill项目/`;本机有且技能路径含 `.dsh` → dsh 部署环境 → `D:\dshworkspace\创作任务/`;本机有且不含 `.dsh` → 开发机 → 桌面 `MCNSkill项目/`),再按系统解析桌面路径。此维度为增量优先特例 |
| **路径配置** | 每次执行创作流程前,**先查** `路径配置.md`。有「用户自定义路径」记录 → 以用户指定为准(**覆盖标准库默认桌面路径**);无记录 → 按两环境判定(技能处于源技能 git 检出内 → 开发机 → 桌面 `MCNSkill项目/`;不在任何 git 检出内 → 用户环境 → 桌面 `MCNSkill项目/`。**两环境产出同构**),再按系统解析桌面路径。此维度为增量优先特例 |
@@ -7,6 +7,58 @@
---
## 2026-09-10(工作台配置收敛:端口/DB/目录单源化,代码层统一引用 config.json)
- **触发**:用户问「这些设定能否用配置,所有文件和脚本都引用,避免有调整时到处修改」。确认范围:只收敛代码层(不动文档/技能规范),配置放 `mcn-work-shop/config.json`。
- **目标**:工作台散落在 server.js / dsh-data.js / 4 个辅助脚本 / 前端 data-pages.js / start.bat 里的硬编码设定(端口 8900、DB 名 mcn-plugin.db、产出根 MCNSkill项目、榜单目录),统一收进 config.json,代码层一律从配置读,调整只需改 config.json 一处。
- **新增**:
- `mcn-work-shop/config.json`:单一配置源(port / dbName / outputRoot / businessDataDir / extraRoots)
- `mcn-work-shop/load-config.js`:配置读取唯一入口 + 默认值唯一来源(config 缺失/损坏回退 DEFAULTS)
- **改动**:
- `server.js`:`require('./load-config')` 取 CFG;`PORT_BASE = argv[2] || CFG.port`(argv 端口仍优先);`DEFAULT_ROOT` / `path.join(ROOT, CFG.dbName)` 从 CFG 读;`saveConfig` 合并写盘保留其它字段;错误提示「mcn-plugin.db」→ CFG.dbName 动态
- `dsh-data.js`:删本地 loadCfg,改用 loadConfig;`DSH_DB = path.join(__dirname, CFG.dbName)`;榜单目录 `path.join(...CFG.businessDataDir)`
- `build-create-prompts.cjs` / `submit-review-tasks.cjs` / `poll-create-tasks.cjs` / `poll-review-tasks.cjs`:DB / API 端口 / prompt 内 dbName 字面量 → CFG 动态
- `public/data-pages.js`:新增 `const BASE = location.origin`;SKILL_HINT_VIDEO_PARSE / SKILL_HINT_ANALYZE / saveHintOf 里的 `http://localhost:8900` 全部改 BASE 动态(前端读不到 server 端 config.json,用 location.origin 让回调地址端口自动跟随当前访问地址)
- `start.bat`:端口从 config.json 读取(`for /f` + load-config),不再写死 8900
- **踩坑**:`load-config.cjs` 用 `.cjs` 扩展名时,`require('./load-config')` 无扩展名解析会 MODULE_NOT_FOUND(Node 不会把 .cjs 作为无扩展 require 的解析目标)→ 重命名为 `load-config.js`(无 package.json,默认 CommonJS)后所有引用自动生效。node --check 只查语法不执行 require,此坑在运行时才暴露。
- **验证**:8 文件 node --check 全通过;`loadConfig()` 正确读回 config.json 全字段;工作台重启 HTTP 200(端口 8900、产出根 MCNSkill项目 均从 config 正确读取)。
## 2026-09-10(dsh 部署环境废弃:三环境 → 两环境,判定简化为一步)
- **触发**:用户确认「第一步 技能路径含 `.dsh` 路径段 ② dsh 部署环境 —— 没有这个环境了」。实测依据:`~/.dsh/skills/` 不存在、`~/.dsh/mcn-plugin.db` 不存在、`~/.dsh` 下仅剩 `profiles/`、`D:\dshworkspace` 整个目录不存在。
- **判据 v3(一步 · 仓库特征判定)**:删除 `.dsh` 路径段判定步骤,仅保留 `git -C {技能目录} rev-parse --show-toplevel` 返回仓库根 = ①开发机、失败 = ②用户环境(无 git 兜底向上找 `.git`)。环境模型由三环境精简为两环境。
- **产出结果不变**:两环境(开发机/用户环境)产出本就同构(桌面 `MCNSkill项目/`),仅判定依据简化。
- **改动范围**:
- **判定层**:`V1.0/references-add/路径配置.md`、`Lite1.0/references-add/路径配置.md`、`subskills/mcn-dou-analysis/references/路径配置.md`、`subskills/mcn-video-prompt/references-add/路径配置.md`(判定表/执行规则/解析规则/变更记录,v3)
- **SKILL 层**:`V1.0/SKILL.md`(榜单读取 71/78、外部技能依赖 dsh 副本删除、产出路径表述)、`Lite1.0/SKILL.md`、`subskills/mcn-dou-analysis/SKILL.md`(42/44/51/52/58/59/82/95 行)、`subskills/mcn-video-prompt/SKILL.md`(502/505/531-539)、`subskills/mcn-data-insight/SKILL.md`(90-96/116 行)
- **标准库**:5 份 `references/规则/路径引用规范.md`(「三种机器」→「两种机器」)、`references/创作流程规范.md`、`references/创作流程/{11_脚本检查和诊断,4_账号设定解析和确认}.md`、`references/接口调用/{MCP_工具调用规范,自动化任务清理规范}.md`
- **数据源层**:`subskills/mcn-data-insight/scripts/fetch_week_ranks.py`(删 `.dsh` 分支代码)、`mcn-work-shop/dsh-data.js`(删 `isDshDeploy` 分支 + 注释)、`mcn-work-shop/public/data-pages.js`(SKILL_HINT_RANKING + 写库注释)、`subskills/mcn-data-insight/subskills/{douyin-rise-ranking,douyin-weekly-surge,douyin-content-surge,douyin-daily-hot}/SKILL.md`、`references/规则/抖音数据规则.md`
- **账号分析层**:`references/账号数据分析方法.md`、`references/feature/{01_获取账号信息,03_提炼账号设定,04_分析视频数据,06_生成账号设定卡片}.md`、`references/铁律避坑规则/账号信息获取执行避坑.md`(删「dsh 原库」)、`references/浏览器搜索抖音账号操作规范.md`(159/175 行 browser-harness dsh 部署环境)
- **README**:`references-add/README.md`(两处)、`subskills/mcn-dou-analysis/references-add/README.md`、`subskills/mcn-video-prompt/references-add/README.md`、`subskills/mcn-video-prompt/references/README.md`
- **残留核对**:全树扫描 `.dsh`/`dshworkspace`/`dsh 部署`/`dsh 环境`/`dsh_MCNProject`/`②dsh`/`③用户环境`——**当前有效规则零残留**,剩余命中全部为豁免三类:①变更记录/变更日志历史行;②「09-10 起 dsh 环境已废弃」说明文字;③`路径引用规范.md` 用户级路径示例 `{主目录}/.dsh/...`(DSH 工具配置目录,非部署环境)。工作台重启验证 HTTP 200,`dsh-data.js`/`fetch_week_ranks.py` 语法校验通过。
## 2026-09-10(路径治本:环境判定锚点重构为「仓库特征判定」+ 全树写死路径清理)
- **触发**:仓库本地路径由 `D:\AgentSkill\mcn-video-script` 迁移至 `D:\AI技能\mcn-short-video`,`D:\AgentSkill` 已不存在 → 全部以「本机是否存在 `D:\AgentSkill`」为锚点的环境判定**失效**,开发机被误判为「③ 用户环境」(产出落桌面、且误启用 references-add 增量分支)。同时发现技能目录 8 处**真实读写路径**写死盘符。
- **判据重构(新口径 v2 · 两步)**:
- 第一步:技能路径是否含 `.dsh` 路径段 → 含 = ② dsh 部署环境
- 第二步:不含 `.dsh` 时,`git -C {技能目录} rev-parse --show-toplevel` 能返回仓库根 = ① 开发机(处于源技能 git 检出内);命令失败 = ③ 用户环境。无 git 环境兜底:从技能目录向上逐级查找 `.git`
- ⚠️ 必须 `.dsh` 优先判定——`~/.dsh` 自身也是 git 仓库,先查 `.git` 会把 dsh 环境误判为开发机
- **产出根目录三环境结果完全不变**,仅判定依据改变
- **改动范围(21 个文件)**:
- **规范层**:`references/规则/路径引用规范.md`(允许项「锚点禁止用写死盘符」+ 禁令项示例 `D:\MyRepo` + 豁免表);`V1.0/references-add/路径配置.md`、`Lite1.0/references-add/路径配置.md`(判定表/执行规则/解析规则/变更记录);两份 `references-add/README.md`
- **标准库副本(4 份 `路径引用规范.md` 同步)**:`subskills/{mcn-data-insight,mcn-dou-analysis,mcn-script-review,mcn-video-prompt}/references/规则/`
- **子技能判定节**:`subskills/mcn-dou-analysis/{SKILL.md,references/路径配置.md,references-add/README.md}`、`subskills/mcn-video-prompt/{SKILL.md,references-add/README.md,references-add/路径配置.md,references/README.md}`
- **MCP 文档**:`references/接口调用/MCP_工具调用规范.md`、`subskills/mcn-dou-analysis/references/接口调用/MCP_工具调用规范.md`(开发/非开发环境判定)
- **Lite1.0**:`SKILL.md` 外部技能依赖段落(见下)
- **真实读写路径修复(B 类 8 处)**:
- `mcn-work-shop/server.js`:新增 `BROWSER_LOCK_FILE = path.join(ROOT, '.browser-lock')`、`SESSION_CWD = ROOT`;2 处 `const cwd` 与 `BROWSER_LOCK` 改用常量;`SESSION_CWD_PATTERN` 由 `'%mcn-workshop%'` 放宽为 `'%mcn-work%'`(**同时兼容历史会话 mcn-workshop 与新建会话 mcn-work-shop**,避免 6 小时清理对旧会话失效)
- `mcn-work-shop/build-create-prompts.cjs` / `submit-review-tasks.cjs`:prompt 内 DB 绝对路径 → 改用文件内既有 `DB` 常量插值
- `V1.0/SKILL.md` 第 5 条:文档里的运行时 `cwds=["D:\AgentSkill\mcn-workshop"]` → 改为 `<工作台目录>`(由 server.js 从 ROOT 动态推导)
- **Lite1.0 外部技能依赖修正(09-02 结构变更补同步)**:原段落仍写「三环境两步判定 + `D:\AgentSkill\mcn-dou-analysis` 外部路径」,既失效又未同步 09-02「mcn-dou-analysis 并入 `subskills/`」变更。修正为:**Lite1.0 目录下无 `subskills/`(实测),未内置该技能**;需要账号解析改用完整版「短视频工作台」V1.0,或先把技能同步进 `Lite1.0/subskills/` 后按相对路径 `subskills/mcn-dou-analysis/SKILL.md` 调用;禁止盘符绝对路径定位。
- **BROWSER_LOCK 契约同步**:`subskills/mcn-dou-analysis/references/浏览器搜索抖音账号操作规范.md` 5.6 节锁文件路径由 8 处盘符硬编码 → 改为相对 `.browser-lock`(工作台触发的会话 cwd 即工作台目录),并注明以工作台注入的绝对路径为准。
- **残留核对**:全树扫描 `AgentSkill` 剩 18 处(含本条目自身 5 处),**全部为豁免类别**——变更记录/变更日志行 11 处、frontmatter `last_change` 摘要 1 处、09-04/09-10 修正说明行 2 处、Lite 技能审计报告历史 2 处、路径引用规范修订脚注 1 处、本条目残留核对说明 1 处。**当前规则行零残留**。`mcn-video-script` 剩 1 处(变更记录行)、`MCNVideo AI` 剩 3 处(Lite 审计报告历史)。
## 2026-09-07(S1/S2 产物文件名改名:01_需求理解 / 02_需求补全)
- **决策**:产物文件名 01_需求拆分.md / 02_需求拆解.md(旧步骤名「需求拆分任务规划 / 任务需求拆解完善」残留,两词同音近义、与现行步骤名对不上)→ 改名 **01_需求理解.md**(S1 产物=五板块状态表)/ **02_需求补全.md**(S2 产物=归类+AI 补全)。命名口径:编号承担"属于哪步"(01=S1、02=S2),文件名承担"装了什么"(与 03_对标拆解~11_诊断报告 产物语义名体系一致;不采用对齐步骤名方案,避免与 references/创作流程/1_意图识别.md、2_需求完善.md 撞名)。
@@ -5,13 +5,12 @@
## 一、默认路径(用户未自定义时生效)
**先判断运行环境(三环境两步判定),再按运行系统解析桌面路径:**
**先判断运行环境(两环境判定 · 仓库特征判定,v3),再按运行系统解析桌面路径:**
| 运行环境 | 判断依据 | 默认产出根目录 |
|:---|:---|:---|
| dsh 部署环境 | 技能所在路径含 `.dsh`,且本机存在 `D:\AgentSkill`(如 `C:\Users\{用户名}\.dsh\skills\…`) | **优先** `D:\dshworkspace\创作任务\`(主工作区「创作任务」目录) |
| 开发机 | 技能所在路径不含 `.dsh`(源技能在 `D:\AgentSkill` 下) | 用户桌面 `MCNSkill项目/`(`{账号名}/脚本{编号}/` 直挂,无技能文件夹层) |
| 用户环境 | 本机不存在 `D:\AgentSkill`(技能装在用户机器上,路径不限、不一定含 `.dsh`) | 用户桌面 `MCNSkill项目/`(与开发机同构) |
| 开发机 | 技能**处于源技能 git 检出内**(`git -C {技能目录} rev-parse --show-toplevel` 能返回仓库根) | 用户桌面 `MCNSkill项目/`(`{账号名}/脚本{编号}/` 直挂,无技能文件夹层) |
| 用户环境 | 技能**不在任何 git 检出内**(技能装在用户机器上,路径不限,如 `~/.workbuddy/skills/…`) | 用户桌面 `MCNSkill项目/`(与开发机同构) |
桌面路径按运行系统自动解析:
@@ -21,7 +20,10 @@
| macOS | `~/Desktop/`(即 `/Users/{用户名}/Desktop/`) |
**AI 执行规则:**
1. 先做三环境两步判定:① 本机是否存在 `D:\AgentSkill`——不存在即**用户环境** → 桌面 `MCNSkill项目/`;② 本机存在时再看技能自身路径——含 `.dsh` 即 **dsh 部署环境** → **优先操作** `D:\dshworkspace\创作任务\`(「创作任务」子目录不存在则创建);不含 `.dsh` 即**开发机** → 桌面 `MCNSkill项目/`(开发机与用户环境同构:`MCNSkill项目/{账号名}/脚本{编号}/`)
1. 先做两环境判定(**仓库特征判定**):
- 判断技能是否处于**源技能 git 检出内**——执行 `git -C "{技能目录}" rev-parse --show-toplevel`,返回仓库根即 **① 开发机**;命令失败(或返回 NOT_IN_REPO)即 **② 用户环境**。无 git 环境时兜底:从技能目录向上逐级查找,任一级存在 `.git` 目录/文件即开发机,查到盘符根仍未命中即用户环境
- 开发机与用户环境产出**同构** → 桌面 `MCNSkill项目/{账号名}/脚本{编号}/`
- ⚠️ 禁止按 `.dsh` 路径段或 `D:\dshworkspace` 做任何分支判断(该部署环境已不存在)
2. 再判断当前运行系统(Windows / macOS),拼接桌面路径
3. 目录不存在则自动创建
4. 产出结构:`{产出根目录}/{账号名}/脚本{编号}/…`(内部文件结构不变,仅根目录可变)
@@ -49,7 +51,7 @@
## 三、解析规则(优先级从高到低)
1. 本文件「用户自定义路径」记录 → 用自定义路径(最新一条生效)
2. 本文件默认路径 → 按三环境两步判定确定产出根目录(本机无 `D:\AgentSkill` → 桌面 `MCNSkill项目/`;有且技能路径含 `.dsh` → `D:\dshworkspace\创作任务\`;有且不含 `.dsh` → 桌面 `MCNSkill项目/`),再按系统拼接桌面路径
2. 本文件默认路径 → 按两环境判定确定产出根目录(在源技能 git 检出内 = 开发机 → 桌面 `MCNSkill项目/`;不在任何 git 检出内 = 用户环境 → 桌面 `MCNSkill项目/`。**两者同构**),再按系统拼接桌面路径
3. 路径含 `~` 时自动展开为用户主目录
4. 路径含 `{用户名}` 时替换为当前系统用户名
5. 目录不存在时自动创建
@@ -61,3 +63,5 @@
- 2026-08-26(再修订):dsh 部署环境改为**优先操作** `D:\dshworkspace\创作任务\`(主工作区「创作任务」目录,与 .dsh 部署副本既有自定义路径记录一致;用户指定:"dsh 中优先操作 D:\dshworkspace\创作任务\");主工作区不存在时兜底桌面 `DSHSkill项目/`。产出结构不变。
- 2026-08-26(三环境重构):环境判定由两级改为**三环境两步判定**(①本机无 `D:\AgentSkill`=用户环境;②有且技能路径含 `.dsh`=dsh 部署环境;③有且不含 `.dsh`=开发机);`DSHSkill项目/` 根目录**整体废弃**(用户确认),原"dsh 异机兜底 DSHSkill项目/"场景并入用户环境 → 桌面 `MCNSkill项目/脚本创作/`;dsh 部署环境产出 `D:\dshworkspace\创作任务\` 不变。产出结构不变。
- 2026-08-26(层级精简):开发机与用户环境产出根目录由 `MCNSkill项目/脚本创作/` 改为 `MCNSkill项目/`——去掉技能文件夹层,`{账号名}/脚本{编号}/` 直挂(用户指定:"不论1还是3,都是 桌面\MCNSkill项目\{账号名}\{脚本名}\对应文件");dsh 部署环境不变。
- 2026-09-10(判定锚点重构 v2 · 仓库特征判定):环境判定锚点由「本机是否存在 `D:\AgentSkill`」改为**仓库特征判定**——第一步查技能路径是否含 `.dsh`(含 = dsh 部署环境),第二步用 `git -C {技能目录} rev-parse --show-toplevel` 判断技能是否处于源技能 git 检出内(在 = 开发机,不在 = 用户环境;无 git 时兜底为向上逐级查找 `.git`)。产出根目录三环境结果**完全不变**,仅判定依据改变。**根因**:仓库本地路径由 `D:\AgentSkill\mcn-video-script` 迁移至 `D:\AI技能\mcn-short-video`,写死盘符锚点失效 → 开发机被误判为「用户环境」;改为仓库特征判定后,仓库搬移到任何盘符/目录名均自动正确判定。(用户确认:"改为仓库特征判定")
- 2026-09-10(环境模型精简 v3 · 三环境 → 两环境):**dsh 部署环境整体废弃**(用户确认:"第一步 技能路径含 .dsh 路径段 ② dsh 部署环境 没有这个环境了")。实测依据:`~/.dsh/skills/` 不存在、`~/.dsh/mcn-plugin.db` 不存在、`~/.dsh` 下仅剩 `profiles/`、`D:\dshworkspace` 整个目录不存在。故删除 `.dsh` 判定分支与 `D:\dshworkspace\创作任务\` 产出路径,环境判定简化为**一步判定**(是否处于源技能 git 检出内)。剩余两环境(开发机 / 用户环境)产出根目录**本就同构**(桌面 `MCNSkill项目/`),故产出结果完全不变。
@@ -207,7 +207,7 @@
- 人设校验结论(7项逐项检查结果)
- 合规校验结论(5项逐项检查结果)
- 脚本格式规范检查结果(参照 `../创作流程规范.md` 三、脚本格式规范)
- 🔴 **创作回顾.html**(Gate-6 确认后必须生成)— 整合全流程 1-11 步各阶段产出的可视化回顾页,命名 `脚本{编号}_{脚本标题}_创作回顾.html`,存放于产出根目录 `{账号名}/` 账号文件夹根目录(产出根以 `references-add/路径配置.md` 为唯一权威:开发机/用户环境默认桌面 `MCNSkill项目/`、dsh 环境 `D:\dshworkspace\创作任务\`)
- 🔴 **创作回顾.html**(Gate-6 确认后必须生成)— 整合全流程 1-11 步各阶段产出的可视化回顾页,命名 `脚本{编号}_{脚本标题}_创作回顾.html`,存放于产出根目录 `{账号名}/` 账号文件夹根目录(产出根以 `references-add/路径配置.md` 为唯一权威:开发机与用户环境同构,默认桌面 `MCNSkill项目/`)
## 上下游依赖
- 上游:[步骤9:生成短视频脚本] + [步骤10:生成短视频分镜脚本] + [步骤4:账号设定解析和确认(约束引用)] + [步骤6:生成短视频框架(约束引用)] + [步骤7:生成短视频大纲(约束引用)] + [步骤8:植入广告内容(约束引用)]
@@ -251,7 +251,7 @@ Gate-6 确认通过后,必须生成创作回顾.html。**页面板块顺序严
| S10 分镜脚本 | 10_分镜脚本.md | |
| S11 诊断报告 | 11_诊断报告.md | |
命名规则:`脚本{编号}_{脚本标题}_创作回顾.html`,存放于产出根目录 `{账号名}/` 下(产出根以 `references-add/路径配置.md` 为唯一权威:开发机/用户环境默认桌面 `MCNSkill项目/`、dsh 环境 `D:\dshworkspace\创作任务\`;用户自定义路径优先)。
命名规则:`脚本{编号}_{脚本标题}_创作回顾.html`,存放于产出根目录 `{账号名}/` 下(产出根以 `references-add/路径配置.md` 为唯一权威:开发机与用户环境同构,默认桌面 `MCNSkill项目/`;用户自定义路径优先)。
---
@@ -134,7 +134,7 @@
| 账号设定 | 账号基本信息 + 内容策略(定位/内容打法/内容规则/结构偏好/视听风格/系列内容) | 新建账号时填写,后续脚本创作全程参照 |
| 达人设定 | 人物的创作约束(身份/性格/外在特质/价值观与底线/说话风格/行为决策) | 账号设定完成后填写,台词/选题/诊断时做一致性校验 |
> 两份板块合并为同一份产物文件:`{账号名}人设卡片.md`(账号层级,存放于产出根目录 `{账号名}/` 下,**三种模式均落盘**)+ `脚本{编号}/04_人设卡片.md`(脚本层级副本,**仅开发模式落盘**,自动/共创模式不写盘)。产出根目录以 `references-add/路径配置.md` 为唯一权威(默认开发机/用户环境 = 桌面 `MCNSkill项目/`、dsh 环境 = `D:\dshworkspace\创作任务\`)。
> 两份板块合并为同一份产物文件:`{账号名}人设卡片.md`(账号层级,存放于产出根目录 `{账号名}/` 下,**三种模式均落盘**)+ `脚本{编号}/04_人设卡片.md`(脚本层级副本,**仅开发模式落盘**,自动/共创模式不写盘)。产出根目录以 `references-add/路径配置.md` 为唯一权威(开发机与用户环境同构,默认 = 桌面 `MCNSkill项目/`)。
## 上下游依赖
- 上游:[步骤2:需求完善] / [步骤3:对标视频账号拆解(如有)]
@@ -411,13 +411,13 @@ F05 教程教学类脚本必须包含**完整步骤链**,缺任一段 = 格式
# 五、产出路径规范
> 🔴 **每次执行步骤后,必须将产物写入对应文件路径。** 产出路径唯一权威 = `references-add/路径配置.md`(运行时先读该文件,用户自定义路径优先)。默认产出根按三环境判定:**①开发机 / ③用户环境** = 用户桌面 `MCNSkill项目/`(`{账号名}/脚本{编号}/` 直挂,无技能文件夹层);**②dsh 环境** = `D:\dshworkspace\创作任务\`(主工作区「创作任务」,与开发机同构 `{账号名}/脚本{编号}/`)。Windows/macOS 按运行系统解析桌面路径;目录不存在时自动创建。
> 🔴 **每次执行步骤后,必须将产物写入对应文件路径。** 产出路径唯一权威 = `references-add/路径配置.md`(运行时先读该文件,用户自定义路径优先)。默认产出根按**两环境判定**:**开发机 / 用户环境同构** = 用户桌面 `MCNSkill项目/`(`{账号名}/脚本{编号}/` 直挂,无技能文件夹层)。Windows/macOS 按运行系统解析桌面路径;目录不存在时自动创建。
> ⚠️ **用户自定义路径优先**:若 `references-add/路径配置.md` 存在「用户自定义路径」记录,一律以用户指定为准,本默认路径自动失效(AI 每次执行前先查该文件)。
## 输出根目录
```
{产出根目录}/{账号名}/ ← 默认:开发机/用户环境 = 桌面 MCNSkill项目/;dsh 环境 = D:\dshworkspace\创作任务\(按系统解析)
{产出根目录}/{账号名}/ ← 默认:开发机/用户环境同构 = 桌面 MCNSkill项目/(按系统解析)
{用户自定义根目录}/{账号名}/ ← 用户指定时(以 references-add/路径配置.md 为准)
```
@@ -438,11 +438,11 @@ F05 教程教学类脚本必须包含**完整步骤链**,缺任一段 = 格式
| 🔴 | 创作回顾.html | `脚本{编号}_{标题}_创作回顾.html` | Gate-6确认后自动生成 |
**规则:**
- 步骤1执行时创建目录:在产出根目录(`references-add/路径配置.md` 判定,默认桌面 `MCNSkill项目/` 或 `D:\dshworkspace\创作任务\`)下先建 `{账号名}/`,再建 `{账号名}/脚本{编号}/`
- 步骤1执行时创建目录:在产出根目录(`references-add/路径配置.md` 判定,默认桌面 `MCNSkill项目/`)下先建 `{账号名}/`,再建 `{账号名}/脚本{编号}/`
- `{编号}` 首次为 `01`,后续自增(检查已有文件夹确定编号)
- `{账号名}人设卡片.md` 写入账号根目录(跨脚本复用),步骤4同步写入一份到 `脚本{编号}/04_人设卡片.md`
- 🔶 步骤无对应输入时跳过
- 产出根目录由 `references-add/路径配置.md` 唯一权威判定(用户未自定义时:开发机/用户环境=桌面 `MCNSkill项目/`、dsh 环境=`D:\dshworkspace\创作任务\`),不存在则自动创建,部署到任意电脑均可正常工作
- 产出根目录由 `references-add/路径配置.md` 唯一权威判定(用户未自定义时:开发机与用户环境同构 = 桌面 `MCNSkill项目/`),不存在则自动创建,部署到任意电脑均可正常工作
- **用户自定义路径**:用户明确指定其他位置时,以用户指定为准(记录在 `references-add/路径配置.md`,运行时优先);产出结构(`{账号名}/脚本{编号}/`)不变,仅根目录变化
**三种模式的落盘清单:**
@@ -481,7 +481,7 @@ F05 教程教学类脚本必须包含**完整步骤链**,缺任一段 = 格式
| 知识库 | `references/知识库/` | 账号名、角色名、账号专属机制名 | 通用描述词、方法论术语 | 方法论是通用原则,不是某账号说明书 |
| 素材库·标准库 | `references/素材库/*.md` | 账号名、角色关系绑定(如"XX账号的XX") | 具体物品(菜品/对话/穿搭) | 库本身存具体物品,但不能绑定到某账号 |
| 素材库·分块定义 | `references/素材库/分块定义/` | 账号名、角色名 | 账号A/账号B + 具体物品做格式示例 | 定义文件示格式,不是存数据 |
| 账号产出目录 | `{产出根目录}/{账号名}/`(产出根由 `references-add/路径配置.md` 判定:桌面 `MCNSkill项目/` 或 `D:\dshworkspace\创作任务\`,或用户自定义) | — | 全部允许 | 账号专属产出目录,就该写具体的 |
| 账号产出目录 | `{产出根目录}/{账号名}/`(产出根由 `references-add/路径配置.md` 判定:开发机与用户环境同构 = 桌面 `MCNSkill项目/`,或用户自定义) | — | 全部允许 | 账号专属产出目录,就该写具体的 |
## 关键区分:素材库存具体物品 ≠ 硬编码
@@ -15,12 +15,12 @@
## 2) 环境判定与安装策略
复用技能已有的部署检测机制(两步判定锚点:本机有 `D:\AgentSkill` 且技能根目录路径在 `D:\AgentSkill\` 下 = 开发机;技能路径含 `.dsh` = dsh 部署环境;本机无 `D:\AgentSkill` = 用户环境——开发机装正式+测试,其余只装正式)。
复用技能已有的部署检测机制(**仓库特征判定 · 两环境**:技能处于源技能 git 检出内(`git -C {技能目录} rev-parse --show-toplevel` 能返回仓库根)= 开发机;否则 = 用户环境——开发机装正式+测试,其余只装正式)。
| 环境 | 判定条件 | 安装内容 |
|------|---------|---------|
| **开发环境** | 本机存在 `D:\AgentSkill` 且 Skill 根目录路径在 `D:\AgentSkill\` 下(源技能仓库) | `myai-mcp-production` + `myai-mcp-test`(推荐两个都装,可选只装正式) |
| **非开发环境** | 部署到其他路径(组员机器、服务器) | 只装 `myai-mcp-production`(正式),不装测试环境 |
| **开发环境** | Skill 根目录**处于源技能 git 检出内**(源技能仓库,`git -C {技能目录} rev-parse --show-toplevel` 返回仓库根) | `myai-mcp-production` + `myai-mcp-test`(推荐两个都装,可选只装正式) |
| **非开发环境** | 部署到其他路径(组员机器、服务器;不在任何 git 检出内) | 只装 `myai-mcp-production`(正式),不装测试环境 |
---
@@ -1,7 +1,7 @@
# 自动化任务清理规范(09-03 定稿)
> **用途**:后台自动化任务(一次性任务)积攒过多时,安全清理**过期 / 执行失败**的任务。
> **适用环境**:WorkBuddy 全环境通用(开发机 / dsh 部署环境 / 用户环境)——automations 表是 WorkBuddy 宿主自带(`~/.workbuddy/workbuddy.db`),不依赖 MCN 工作台,任何装了本技能的 WorkBuddy 都能执行。
> **适用环境**:WorkBuddy 全环境通用(开发机 / 用户环境)——automations 表是 WorkBuddy 宿主自带(`~/.workbuddy/workbuddy.db`),不依赖 MCN 工作台,任何装了本技能的 WorkBuddy 都能执行。
> **红线(用户明确)**:**只能清理「过期」或「错误/失败」的任务**,禁止清理未来任务 / 周期任务 / 运行中任务。
## 一、触发场景
@@ -1,6 +1,6 @@
# 路径引用规范(相对路径铁律,P0)
> 适用:本技能树(主技能 + references/references-add + scripts + 全部 subskills 及嵌套 subskills)。技能会被部署到 **开发机 / dsh 环境 / 用户环境** 三种机器,**绝对路径随机器变化必然失效**,故技能内一切文件引用一律使用**相对路径**。
> 适用:本技能树(主技能 + references/references-add + scripts + 全部 subskills 及嵌套 subskills)。技能会被部署到 **开发机 / 用户环境** 两种机器,**绝对路径随机器变化必然失效**,故技能内一切文件引用一律使用**相对路径**。
## 一、铁律(3 条)
@@ -18,12 +18,12 @@
| `__file__` 动态推导 | `BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))` | 脚本定位技能根,跨环境可迁移 |
| 环境变量 | `os.getenv("REDFOX_API_KEY")`、`MCN_RANKING_DIR` | 值由外部注入 |
| URL | `https://github.com/...` | 外部资源 |
| 环境判别锚点 | 「本机是否存在 `D:\AgentSkill`」「技能路径是否含 `.dsh`」 | 仅作**是否存在/是否包含**的判定条件,**禁止**将其作为实际读写路径 |
| 动态产出目录占位 | 桌面 `MCNSkill项目/{账号名}/`、`D:\dshworkspace\创作任务\` | 由各技能 `references(-add)/路径配置.md` 运行时判定,允许出现于规则文档 |
| 环境判别锚点 | 「技能目录是否处于 git 检出内」 | 仅作**是否存在**的判定条件,**禁止**将其作为实际读写路径;锚点必须用**仓库特征**(git 检出),禁止用写死盘符 |
| 动态产出目录占位 | 桌面 `MCNSkill项目/{账号名}/` | 由各技能 `references(-add)/路径配置.md` 运行时判定,允许出现于规则文档 |
## 三、禁令项(这些绝对禁止)
- **Windows 盘符绝对路径**:`C:\Users\maidou\...`、`D:\AgentSkill\...`(含正反斜杠两种写法)——除「环境判别锚点/路径配置规则表」语境外的任何引用位置。
- **Windows 盘符绝对路径**:`C:\Users\maidou\...`、`D:\MyRepo\...`(含正反斜杠两种写法)——除「环境判别锚点/路径配置规则表」语境外的任何引用位置。
- **Unix 绝对路径**:`/Users/maidou/...`、`/home/...`、`/tmp/...`(shebang `#!/usr/bin/env python3` 除外)。
- **越级引用**:`../../..` 跳出技能根指向包外文件。
- **引用技能包外文档**:md 链接指向不存在于包内的本地文件(如仓库根的维护文档)——部署环境无此文件即断链。
@@ -34,7 +34,7 @@
| 场景 | 说明 |
|------|------|
| 仅开发机工具 | 不随技能分发的目录(如 `mcn-work-shop/`),内部可保留开发机路径 |
| 环境判别/路径配置文档 | `路径配置.md`、SKILL.md 环境判定节——锚点 `D:\AgentSkill`、`.dsh`、`D:\dshworkspace\...` 是**规则内容**不是引用,保留 |
| 环境判别/路径配置文档 | `路径配置.md`、SKILL.md 环境判定节——锚点 git 检出特征是**规则内容**不是引用,保留 |
| 示例/教学文本 | `C:/Videos/demo.mp4`、`C:/path/to/file` 等明确示例(`path/to`、`demo` 字样) |
| 历史日志 | `.workbuddy/memory/` 既有日志保留原样,不改写历史 |
| 第三方整包 | 整包拷贝的第三方技能(如 browser-harness/lieflat-charts/参考skills/)内部自带约定不重写;但**本技能对它们的引用**仍须相对路径 |
@@ -55,3 +55,5 @@
---
*变更记录:2026-09-03 初建(全技能树相对路径审计定稿,随 V1.0 与各 subskills 分发;各技能副本内容一致,升级时批量同步)。*
*2026-09-10 修订:环境判别锚点由写死盘符 `D:\AgentSkill` 改为**仓库特征**(git 检出判定),并在「允许项」明确「锚点禁止用写死盘符」——避免仓库搬移/改名后判定失效。*
*2026-09-10 再修订:dsh 部署环境废弃(`~/.dsh/skills/` 与 `D:\dshworkspace` 均不存在),环境模型由三环境精简为**两环境**(开发机 / 用户环境);「允许项」锚点与「豁免表」同步去掉 `.dsh` 路径段与 `D:\dshworkspace\...` 表述。*
@@ -87,12 +87,11 @@ export REDFOX_API_KEY="ak_xxx..."
### 落盘目标(按运行环境 + 数据源分层)
> 桌面 `MCNSkill项目\` 顶层先分「业务账号层 / 三方数据层」:账号产出(分析、脚本等)直挂账号文件夹,**三方数据统一下 `三方数据\{数据源}\`**——后续数据获取不止一个数据源,每个数据源一个文件夹(当前红狐数据源=`红狐数据\`),其下再按数据类型分(抖音榜单/信息源数据/作品数据…)。**文件名不加数据源前缀**(保持 `抖音周榜_` 等榜名开头,兼容 dsh 工作台 RANKING_DIR 正则 `^抖音周榜_`)。
> 桌面 `MCNSkill项目\` 顶层先分「业务账号层 / 三方数据层」:账号产出(分析、脚本等)直挂账号文件夹,**三方数据统一下 `三方数据\{数据源}\`**——后续数据获取不止一个数据源,每个数据源一个文件夹(当前红狐数据源=`红狐数据\`),其下再按数据类型分(抖音榜单/信息源数据/作品数据…)。**文件名不加数据源前缀**(保持 `抖音周榜_` 等榜名开头,兼容工作台 RANKING_DIR 正则 `^抖音周榜_`)。
| 环境 | 落盘目录 |
|---|---|
| ①③ 用户环境 / 开发机(本机无 `.dsh`) | 桌面\MCNSkill项目\三方数据\红狐数据\抖音榜单\(Windows/macOS 按系统解析桌面,禁止硬编码用户目录) |
| ② dsh 环境(本机有 `.dsh`) | `D:\dshworkspace\抖音榜单\`(兼容工作台 RANKING_DIR 现有读取) |
| 开发机 / 用户环境(两环境同构) | 桌面\MCNSkill项目\三方数据\红狐数据\抖音榜单\(Windows/macOS 按系统解析桌面,禁止硬编码用户目录) |
### 目录结构(09-03 起分目录管理,按工作台 tab 对齐)
@@ -112,7 +111,7 @@ export REDFOX_API_KEY="ak_xxx..."
| 3 点赞榜 | 作品维七日点赞飙升 | `点赞榜/抖音周榜_点赞榜_{周一}.json` | ✅ 已实现(fetch_week_ranks.py,全品类 TOP50) |
| 4 涨粉榜 | 账号维涨粉榜 | 平铺 `抖音涨粉榜_{赛道}_{日期}.json` | ✅ 已实现(douyin-rise-ranking 子技能命名,未接入工作台 tab) |
> **周粒度抓取器(工作台 视频热榜/点赞榜 数据源,09-03 重构定稿)**:`scripts/fetch_week_ranks.py` —— **不传赛道(type)= 全品类,每榜每期仅 1 次 API 调用**(禁止逐赛道/逐日循环)。hot = likesRank 传 周一~周日(服务端周聚合);likes = hotContentRank **source 必须为「抖音每日点赞飙升榜」生产名**(写「抖音七日点赞飙升榜-GitHub」返回空,09-03 实测修正),startTime 传周日(七日口径)。**★API 单次上限 TOP50**(limit=100 / page=2 / offset=50 均被忽略,09-03 实测),无 51-100 名获取途径,勿再探测。list 条目补 `rank`;顶层 `category:"全部"`(实际赛道在 list[].category,展示端按条目赛道筛选/下拉去重)。用法:`python scripts/fetch_week_ranks.py --board both [--date 2026-08-24] [--force]`。落盘目录解析同工作台(env `MCN_RANKING_DIR` → `.dsh` 部署 `D:\dshworkspace\抖音榜单` → 桌面 `三方数据\红狐数据\抖音榜单`)。**成本铁律(09-03 固化)**:涉及多赛道/多日/多窗口循环的付费调用前,必须先向用户确认调用次数与范围,禁止未经确认直接批量调用。
> **周粒度抓取器(工作台 视频热榜/点赞榜 数据源,09-03 重构定稿)**:`scripts/fetch_week_ranks.py` —— **不传赛道(type)= 全品类,每榜每期仅 1 次 API 调用**(禁止逐赛道/逐日循环)。hot = likesRank 传 周一~周日(服务端周聚合);likes = hotContentRank **source 必须为「抖音每日点赞飙升榜」生产名**(写「抖音七日点赞飙升榜-GitHub」返回空,09-03 实测修正),startTime 传周日(七日口径)。**★API 单次上限 TOP50**(limit=100 / page=2 / offset=50 均被忽略,09-03 实测),无 51-100 名获取途径,勿再探测。list 条目补 `rank`;顶层 `category:"全部"`(实际赛道在 list[].category,展示端按条目赛道筛选/下拉去重)。用法:`python scripts/fetch_week_ranks.py --board both [--date 2026-08-24] [--force]`。落盘目录解析同工作台(env `MCN_RANKING_DIR` → 桌面 `三方数据\红狐数据\抖音榜单`,**无 dsh 分支**)。**成本铁律(09-03 固化)**:涉及多赛道/多日/多窗口循环的付费调用前,必须先向用户确认调用次数与范围,禁止未经确认直接批量调用。
> 子技能 douyin-daily-hot(视频热榜)/ douyin-content-surge(点赞榜)日粒度归档沿用各自 SKILL.md 命名;**工作台周粒度文件一律 fetch_week_ranks.py 落盘对应子目录**,避免日/周口径混入。douyin-weekly-surge(官方七日口径)落盘 `抖音七日飙升榜_…` 命名保留,未接入工作台 tab。
@@ -50,15 +50,15 @@
数据按类型分三个文件夹存放;**榜单数据**因属「同日期固定快照」+ API 按调用计费,**采用业务数据目录落盘 + 查询先查本地缓存命中免 API**(09-03 定稿规范):
| 数据类型 | 归档目录(①③用户/开发机) | 归档目录(②dsh 环境) | 说明 |
|---|---|---|---|
| **榜单数据**(账号周榜/视频热榜/点赞榜/涨粉榜/日榜/月榜/七日飙升等) | 桌面\MCNSkill项目\三方数据\红狐数据\抖音榜单\(按运行系统解析桌面,禁止硬编码用户目录) | `D:\dshworkspace\抖音榜单\` | 桌面顶层先分「业务账号/三方数据」:三方数据统一 `三方数据\{数据源}\`(当前=红狐数据);文件命名 `{榜名}_{赛道}_{数据日期}.json` 不加数据源前缀;详见 [SKILL.md 榜单数据落盘规范](../../SKILL.md) |
| **信息源数据**(AI信息源、短剧信息源等) | `RedFox数据\信息源数据\YYYY-MM\` | 同(沙箱内) | 按月分文件夹 |
| **作品数据**(单作品爬取、账号诊断等) | `RedFox数据\作品数据\YYYY-MM-DD\` | 同 | 按日分文件夹 |
| 数据类型 | 归档目录(开发机 / 用户环境同构) | 说明 |
|---|---|---|
| **榜单数据**(账号周榜/视频热榜/点赞榜/涨粉榜/日榜/月榜/七日飙升等) | 桌面\MCNSkill项目\三方数据\红狐数据\抖音榜单\(按运行系统解析桌面,禁止硬编码用户目录) | 桌面顶层先分「业务账号/三方数据」:三方数据统一 `三方数据\{数据源}\`(当前=红狐数据);文件命名 `{榜名}_{赛道}_{数据日期}.json` 不加数据源前缀;详见 [SKILL.md 榜单数据落盘规范](../../SKILL.md)。**09-10 起 dsh 环境已废弃**(`D:\dshworkspace` 不存在),原「②dsh 环境」列删除 |
| **信息源数据**(AI信息源、短剧信息源等) | `RedFox数据\信息源数据\YYYY-MM\` | 按月分文件夹 |
| **作品数据**(单作品爬取、账号诊断等) | `RedFox数据\作品数据\YYYY-MM-DD\` | 按日分文件夹 |
### 2.2 日期命名规则
> 以下示例归档目录 = 表 2.1 对应环境的目录内(①③ = `三方数据\红狐数据\抖音榜单\`;② dsh = `D:\dshworkspace\抖音榜单\`)。
> 以下示例归档目录 = 表 2.1 的 `三方数据\红狐数据\抖音榜单\` 内(**无 dsh 分支**)。
- 归档文件夹日期按 **数据实际日期** 命名,**不是获取日期**。
- 例:8月8日获取8月7日的日榜 → 存入 `抖音榜单\抖音视频热榜_美食_2026-08-07.json`
@@ -1,6 +1,6 @@
# 路径引用规范(相对路径铁律,P0)
> 适用:本技能树(主技能 + references/references-add + scripts + 全部 subskills 及嵌套 subskills)。技能会被部署到 **开发机 / dsh 环境 / 用户环境** 三种机器,**绝对路径随机器变化必然失效**,故技能内一切文件引用一律使用**相对路径**。
> 适用:本技能树(主技能 + references/references-add + scripts + 全部 subskills 及嵌套 subskills)。技能会被部署到 **开发机 / 用户环境** 两种机器,**绝对路径随机器变化必然失效**,故技能内一切文件引用一律使用**相对路径**。
## 一、铁律(3 条)
@@ -18,12 +18,12 @@
| `__file__` 动态推导 | `BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))` | 脚本定位技能根,跨环境可迁移 |
| 环境变量 | `os.getenv("REDFOX_API_KEY")`、`MCN_RANKING_DIR` | 值由外部注入 |
| URL | `https://github.com/...` | 外部资源 |
| 环境判别锚点 | 「本机是否存在 `D:\AgentSkill`」「技能路径是否含 `.dsh`」 | 仅作**是否存在/是否包含**的判定条件,**禁止**将其作为实际读写路径 |
| 动态产出目录占位 | 桌面 `MCNSkill项目/{账号名}/`、`D:\dshworkspace\创作任务\` | 由各技能 `references(-add)/路径配置.md` 运行时判定,允许出现于规则文档 |
| 环境判别锚点 | 「技能目录是否处于 git 检出内」 | 仅作**是否存在**的判定条件,**禁止**将其作为实际读写路径;锚点必须用**仓库特征**(git 检出),禁止用写死盘符 |
| 动态产出目录占位 | 桌面 `MCNSkill项目/{账号名}/` | 由各技能 `references(-add)/路径配置.md` 运行时判定,允许出现于规则文档 |
## 三、禁令项(这些绝对禁止)
- **Windows 盘符绝对路径**:`C:\Users\maidou\...`、`D:\AgentSkill\...`(含正反斜杠两种写法)——除「环境判别锚点/路径配置规则表」语境外的任何引用位置。
- **Windows 盘符绝对路径**:`C:\Users\maidou\...`、`D:\MyRepo\...`(含正反斜杠两种写法)——除「环境判别锚点/路径配置规则表」语境外的任何引用位置。
- **Unix 绝对路径**:`/Users/maidou/...`、`/home/...`、`/tmp/...`(shebang `#!/usr/bin/env python3` 除外)。
- **越级引用**:`../../..` 跳出技能根指向包外文件。
- **引用技能包外文档**:md 链接指向不存在于包内的本地文件(如仓库根的维护文档)——部署环境无此文件即断链。
@@ -34,7 +34,7 @@
| 场景 | 说明 |
|------|------|
| 仅开发机工具 | 不随技能分发的目录(如 `mcn-work-shop/`),内部可保留开发机路径 |
| 环境判别/路径配置文档 | `路径配置.md`、SKILL.md 环境判定节——锚点 `D:\AgentSkill`、`.dsh`、`D:\dshworkspace\...` 是**规则内容**不是引用,保留 |
| 环境判别/路径配置文档 | `路径配置.md`、SKILL.md 环境判定节——锚点 git 检出特征是**规则内容**不是引用,保留 |
| 示例/教学文本 | `C:/Videos/demo.mp4`、`C:/path/to/file` 等明确示例(`path/to`、`demo` 字样) |
| 历史日志 | `.workbuddy/memory/` 既有日志保留原样,不改写历史 |
| 第三方整包 | 整包拷贝的第三方技能(如 browser-harness/lieflat-charts/参考skills/)内部自带约定不重写;但**本技能对它们的引用**仍须相对路径 |
@@ -50,13 +50,10 @@ CATEGORY_ALL = "全部"
def resolve_ranking_dir():
"""与 mcn-work-shop/dsh-data.js resolveRankingDir 同款:env → .dsh 部署 → 桌面 抖音榜单"""
"""与 mcn-work-shop/dsh-data.js resolveRankingDir 同款:env → 桌面 抖音榜单(09-10 起无 dsh 分支)"""
env = os.environ.get("MCN_RANKING_DIR", "").strip()
if env:
return env
here = os.path.abspath(__file__)
if ".dsh" in here.replace("\\", "/"):
return r"D:\dshworkspace\抖音榜单"
home = os.path.expanduser("~")
rel = os.path.join("MCNSkill项目", "三方数据", "红狐数据", "抖音榜单")
candidates = [
@@ -50,6 +50,6 @@ description: 抖音每日点赞飙升榜查询工具。日度收录全平台抖
## 安全约束
- **API Key**:仅通过 `os.getenv("REDFOX_API_KEY")` 获取,禁止读取系统文件
- **文件操作**:对话回复仅输出 Markdown 表格(不暴露原始 JSON);**榜单数据落盘到业务数据目录**(白名单例外,详则见主 [SKILL.md](../../SKILL.md)「榜单数据落盘规范」)——日粒度归档命名 `抖音点赞榜_{赛道}_{数据日期}.json`,目标 `桌面\MCNSkill项目\三方数据\红狐数据\抖音榜单\`(①③环境)或 `D:\dshworkspace\抖音榜单\`(②dsh 环境)。**查询前先 `ls` 本地缓存命中免 API**(同榜名+赛道+数据日期全机器只调一次 API)。工作台「点赞榜」周粒度文件(`抖音周榜_点赞榜_{赛道}_{周一}.json`)由主技能 `scripts/fetch_week_ranks.py` 统一落盘,本技能不做周粒度输出。
- **文件操作**:对话回复仅输出 Markdown 表格(不暴露原始 JSON);**榜单数据落盘到业务数据目录**(白名单例外,详则见主 [SKILL.md](../../SKILL.md)「榜单数据落盘规范」)——日粒度归档命名 `抖音点赞榜_{赛道}_{数据日期}.json`,目标 `桌面\MCNSkill项目\三方数据\红狐数据\抖音榜单\`(开发机与用户环境同构,**无 dsh 分支**)。**查询前先 `ls` 本地缓存命中免 API**(同榜名+赛道+数据日期全机器只调一次 API)。工作台「点赞榜」周粒度文件(`抖音周榜_点赞榜_{赛道}_{周一}.json`)由主技能 `scripts/fetch_week_ranks.py` 统一落盘,本技能不做周粒度输出。
- **网络请求**:脚本使用 `urllib.request`(标准库),禁止调用外部脚本或 subprocess
- **输出内容**:仅输出 Markdown 表格格式榜单,不暴露原始 JSON
@@ -10,7 +10,7 @@ description: 抖音每日最热作品榜,日度收录全平台抖音作品,
1. **路径沙箱**:文件操作限定在技能根目录内
2. **API Key**:仅 `os.getenv("REDFOX_API_KEY")`,禁止 .env/dotenv/配置文件读取
3. **无系统命令**:不调用 subprocess/eval/exec/system()
4. **输出限定**:仅 stdout(Markdown 表格对话回复);**榜单数据同时落盘到业务数据目录**(白名单例外,详则见主 [SKILL.md](../../SKILL.md) 「榜单数据落盘规范」)——日粒度归档命名:`抖音视频热榜_{赛道}_{数据日期}.json`,目标 `桌面\MCNSkill项目\三方数据\红狐数据\抖音榜单\`(①③环境)或 `D:\dshworkspace\抖音榜单\`(②dsh 环境)。**查询前先 `ls` 本地缓存命中免 API**(同榜名+赛道+数据日期全机器只调一次 API)。工作台「视频热榜」周粒度文件(`抖音周榜_视频热榜_{赛道}_{周一}.json`)由主技能 `scripts/fetch_week_ranks.py` 统一落盘,本技能不做周粒度输出。
4. **输出限定**:仅 stdout(Markdown 表格对话回复);**榜单数据同时落盘到业务数据目录**(白名单例外,详则见主 [SKILL.md](../../SKILL.md) 「榜单数据落盘规范」)——日粒度归档命名:`抖音视频热榜_{赛道}_{数据日期}.json`,目标 `桌面\MCNSkill项目\三方数据\红狐数据\抖音榜单\`(开发机与用户环境同构,**无 dsh 分支**)。**查询前先 `ls` 本地缓存命中免 API**(同榜名+赛道+数据日期全机器只调一次 API)。工作台「视频热榜」周粒度文件(`抖音周榜_视频热榜_{赛道}_{周一}.json`)由主技能 `scripts/fetch_week_ranks.py` 统一落盘,本技能不做周粒度输出。
## 执行流程
@@ -10,7 +10,7 @@ description: 抖音涨粉账号推荐(涨粉榜)查询工具。统计抖音
1. **路径沙箱**:文件操作限定在技能根目录内;产物落盘走主技能白名单例外(见下)
2. **API Key**:仅 `os.getenv("REDFOX_API_KEY")`,禁止 .env/dotenv/配置文件读取
3. **无系统命令**:不调用 subprocess/eval/exec/system()
4. **输出限定**:仅 stdout(Markdown 表格对话回复 + JSON 原始数据);**榜单数据同时落盘到业务数据目录**(白名单例外,详则见主 [SKILL.md](../../SKILL.md)「榜单数据落盘规范」)——命名 `抖音涨粉榜_{赛道}_{数据日期}.json`,目标 `桌面\MCNSkill项目\三方数据\红狐数据\抖音榜单\`(①③环境)或 `D:\dshworkspace\抖音榜单\`(②dsh 环境)。**查询前先 `ls` 本地缓存命中免 API**(同榜名+赛道+数据日期全机器只调一次 API)
4. **输出限定**:仅 stdout(Markdown 表格对话回复 + JSON 原始数据);**榜单数据同时落盘到业务数据目录**(白名单例外,详则见主 [SKILL.md](../../SKILL.md)「榜单数据落盘规范」)——命名 `抖音涨粉榜_{赛道}_{数据日期}.json`,目标 `桌面\MCNSkill项目\三方数据\红狐数据\抖音榜单\`(开发机与用户环境同构,**无 dsh 分支**)。**查询前先 `ls` 本地缓存命中免 API**(同榜名+赛道+数据日期全机器只调一次 API)
## 执行流程
@@ -68,7 +68,7 @@ cd {SKILL_ROOT} && python scripts/douyin_weekly_surge.py --full #
### 5. 交付
- 直接输出 Markdown 表格(对话回复);**榜单数据同时落盘到业务数据目录**(白名单例外,详则见主 [SKILL.md](../../SKILL.md)「榜单数据落盘规范」)—— `抖音七日飙升榜_{赛道}_{数据日期}.json`,目标 `桌面\MCNSkill项目\三方数据\红狐数据\抖音榜单\`(①③环境)或 `D:\dshworkspace\抖音榜单\`(②dsh 环境)。**查询前先 `ls` 本地缓存命中免 API**(同榜名+赛道+数据日期全机器只调一次 API)
- 直接输出 Markdown 表格(对话回复);**榜单数据同时落盘到业务数据目录**(白名单例外,详则见主 [SKILL.md](../../SKILL.md)「榜单数据落盘规范」)—— `抖音七日飙升榜_{赛道}_{数据日期}.json`,目标 `桌面\MCNSkill项目\三方数据\红狐数据\抖音榜单\`(开发机与用户环境同构)。**查询前先 `ls` 本地缓存命中免 API**(同榜名+赛道+数据日期全机器只调一次 API)
- 详情参阅 `references/api-config.md`(API 详情)和 `references/interaction-guide.md`(交互示例)
## 安全约束
@@ -39,24 +39,24 @@ description: "MCN 达人爆款视频解析与人设卡生成技能:达人账
| 规则 | 说明 |
|------|------|
| 达人账号表.xlsx(外部数据文件,不入技能包) | 账号登记台账为**外部数据文件,技能包不内置**(09-03 起:原内置副本为 08-26 早期过期数据已删除)。**权威台账 = 数据目录的桌面 `MCNSkill项目/达人账号表.xlsx`(①开发机/③用户环境,含俊希等最新登记);②dsh 环境侧数据已停更(dshworkspace 遗留表为 08-26 死数据),不作为权威、不维护**。表缺失时按 15 列结构现场新建:账号ID/达人昵称/抖音号/抖音ID/抖音地址/粉丝数/解析状态/人设卡/更新时间/获赞数/关注数/作品数/IP属地/简介/备注。**只能根据用户明确要求添加/修改账号**,不得自行添加或扩充。用户说加谁就加谁。输入信息中的账号若不在表中,**先添加到表**:账号ID自增、抖音号/抖音ID(sec_uid)/抖音地址存文本格式,其余字段留空待补。 |
| 达人账号表.xlsx(外部数据文件,不入技能包) | 账号登记台账为**外部数据文件,技能包不内置**(09-03 起:原内置副本为 08-26 早期过期数据已删除)。**权威台账 = 数据目录的桌面 `MCNSkill项目/达人账号表.xlsx`(开发机与用户环境同构);dsh 侧已整套下线(09-10:`D:\dshworkspace` 不存在),其遗留表不再作为任何来源**。表缺失时按 15 列结构现场新建:账号ID/达人昵称/抖音号/抖音ID/抖音地址/粉丝数/解析状态/人设卡/更新时间/获赞数/关注数/作品数/IP属地/简介/备注。**只能根据用户明确要求添加/修改账号**,不得自行添加或扩充。用户说加谁就加谁。输入信息中的账号若不在表中,**先添加到表**:账号ID自增、抖音号/抖音ID(sec_uid)/抖音地址存文本格式,其余字段留空待补。 |
| 数据格式 | 账号原始数据保存为 **原始 JSON 格式**,不用 Markdown。 |
| 文件夹命名 | 统一使用 `账号分析/` 作为根目录(默认产出根目录按三环境判定:**①开发机/③用户环境** 桌面 `~/Desktop/MCNSkill项目/`(`{达人昵称}/` 账号层直挂);**②dsh环境** `D:\dshworkspace\解析任务\`;**实际产出路径以 `references/路径配置.md` 为准**),子文件夹以达人昵称命名。视频文件夹命名规则:`视频标题(去特殊符号)_标签1_标签2`(去掉账号名标签和#号)。 |
| 文件夹命名 | 统一使用 `账号分析/` 作为根目录(默认产出根目录按**两环境判定**:开发机与用户环境**同构** → 桌面 `~/Desktop/MCNSkill项目/`(`{达人昵称}/` 账号层直挂);**实际产出路径以 `references/路径配置.md` 为准**),子文件夹以达人昵称命名。视频文件夹命名规则:`视频标题(去特殊符号)_标签1_标签2`(去掉账号名标签和#号)。 |
| 视频筛选标准 | 先按发布时间过滤**最近三个月**,排除**视频时长>15分钟**的视频,再按点赞+分享总量降序排列,取 TOP 6(不再设30W互动量门槛)。**实际满足条件的视频不足 6 条时,按实际条数取(TOPn 的 n=实际解析条数,如王微斯 4 条→TOP4)**,产出文件(设定 md/卡片)的「TOPn」均按实际条数写,不写 6。**本参数为唯一权威参数**:只在 SKILL.md 定义,其他文件(feature/流程/方法/避坑等)引用此处,**不得重复写数字**;调整只改本行,改后自检:`references/` 下 *.md 用 `grep "TOP ?[0-9]|凑满[0-9]|默认取[0-9]"` 应无残留,并同步 `scripts/excel_tool.py` 与 `scripts/select_top6.py` 两处 `count` 默认值(执行镜像)。 |
| 视频获取数量(**唯一权威参数**) | **≥60 条即停止滚动**;若滚动到底部后无法加载更多视频,也立即停止(两个条件先到者为准,禁止无限滚动)。本参数只在 SKILL.md 定义,其他文件(feature/操作规范等)引用此处,不得重复写数字;调整只改本行。 |
| 视频补充规则 | 解析失败或无法解析的视频,从视频数据中按排名规则(点赞+分享降序)取下一位视频补充,直至凑满「视频筛选标准」规定的数量(数量见上「视频筛选标准」唯一权威参数)。 |
| 人设卡方法论 | 必须结合 **脚本创作技能 + 女娲造人术** 两个技能库综合生成,不可仅凭通用知识。**人设卡聚焦内容创作本身(内容基因/打法/情绪/视听/规则/差异化壁垒的内容侧),不含商业化变现路径分析**(商业化/市场分析归功能四账号数据分析)。**人设卡统一 12 段结构**(账号核心定位→内容基因→类型库→情绪结构→视听风格→内容打法→内容规则→系列内容规划→差异化壁垒→红线清单→当前需解决的问题→设定分析说明),详见 `references/人设卡生成方法.md` 5.3。 |
| 账号类型判定 | 生成人设卡前**必须先判定账号类型**,不同类型使用不同模板结构。判定标准见 `references/人设卡生成方法.md`。 |
| 浏览器操作规范(红线) | **执行任何浏览器操作(功能一/二及任何网页获取)前,必须先读取 `references/浏览器搜索抖音账号操作规范.md` 全文**,严格遵守:① 环境前置(清除代理环境变量、固定 9223 调试 Chrome 实例);② **标签页复用(5.5)**——操作前检查已打开标签页,有则 goto_url 直接导航复用,无才 new_tab 新建,用完不关闭;③ 登录检测(弹登录窗口停止操作提示用户);④ 滚动收集技巧(.route-sc 容器);⑤ **访问账号主页必须走搜索路径→模拟点击进入,禁止直接构造 `/user/{sec_uid}` 地址跳转**;⑥ **视频数据提取首选列表组件 Fiber `defaultDataList`(一次拿全含 createTime),禁止逐条打开 `/video/{id}` 视频页抓字段**(详见规范「提取方法优先级」);⑦ **浏览器进程生命周期:已开启的 Chrome(9223 调试实例/用户 Chrome)一律不主动关闭,除非用户明确要求**(详见规范 5.7)。浏览器要求由插件 prompt 注入与本规范双重保证。 |
| browser-harness 依赖(红线) | 功能一/二(浏览器抓取)执行前**必须先检测 browser-harness 是否可用**:检测方法(任一即可判定)——① 可执行文件存在性:`~/.local/bin/browser-harness.exe`(Windows)/ `~/.local/bin/browser-harness`(macOS/Linux);② `command -v browser-harness`(macOS/Linux)/ `where browser-harness`(Windows)有输出。**未安装 → 提醒用户安装**(可跳过:改用三方数据 API / MCP 兜底数据源,但功能一/二浏览器抓取无法执行;同意后安装)→ 执行 `uv tool install --python 3.12 --upgrade --force browser-harness` → `browser-harness doctor` 验证三要素(工具/daemon/Chrome)全绿。**技能本体位置(09-02 调整)**:主技能 `短视频工作台/subskills/browser-harness/`(源仓库 `project/短视频脚本创作/V1.0/subskills/browser-harness/`,含 envs 环境与 src;**不再挂在 mcn-dou-analysis 下**);dsh 部署环境 `~/.dsh/skills/browser-harness/`(随 dsh_MCNProject 仓库分发,换机器自带)。检测与安装细节见 `references/浏览器搜索抖音账号操作规范.md` 第五节。 |
| 账号信息数据源(红线) | 获取达人账号信息(功能一)**必须浏览器优先**:browser-harness 打开抖音网页版访问达人主页提取(昵称/抖音号/抖音ID(sec_uid)/抖音地址/粉丝数/获赞数/关注数/作品数/IP属地/简介等)。**禁止从任何数据库读取账号字段回填账号表(含身份标识 sec_uid/抖音号/抖音地址)**:工作台库 `mcn-plugin.db` 的 `hot_accounts` 表 = MCP 内部账号库(`list_hot_accounts`/`hot_account_detail`)的**本地副本,读取其数据填表等同违反本红线**;dsh 原库(`~/.dsh/mcn-plugin.db`)同样只读参考。这些库仅用于**流程状态参考**(判断是否已有解析/产出),**不是功能一数据源**;账号表登记(Step 1.0)只占位 账号ID+达人昵称,抖音号/sec_uid/地址等一律留空,**待浏览器抓取后回填**(浏览器抓取到的值才写)。仅当浏览器无法访问(账号搜索不到/主页打不开等)时才可兜底补充,并须注明数据来源。详见 `references/铁律避坑规则/账号信息获取执行避坑.md`。 |
| browser-harness 依赖(红线) | 功能一/二(浏览器抓取)执行前**必须先检测 browser-harness 是否可用**:检测方法(任一即可判定)——① 可执行文件存在性:`~/.local/bin/browser-harness.exe`(Windows)/ `~/.local/bin/browser-harness`(macOS/Linux);② `command -v browser-harness`(macOS/Linux)/ `where browser-harness`(Windows)有输出。**未安装 → 提醒用户安装**(可跳过:改用三方数据 API / MCP 兜底数据源,但功能一/二浏览器抓取无法执行;同意后安装)→ 执行 `uv tool install --python 3.12 --upgrade --force browser-harness` → `browser-harness doctor` 验证三要素(工具/daemon/Chrome)全绿。**技能本体位置(09-02 调整)**:主技能 `短视频工作台/subskills/browser-harness/`(源仓库 `project/短视频脚本创作/V1.0/subskills/browser-harness/`,含 envs 环境与 src;**不再挂在 mcn-dou-analysis 下**;**09-10 起无 dsh 部署环境**)。检测与安装细节见 `references/浏览器搜索抖音账号操作规范.md` 第五节。 |
| 账号信息数据源(红线) | 获取达人账号信息(功能一)**必须浏览器优先**:browser-harness 打开抖音网页版访问达人主页提取(昵称/抖音号/抖音ID(sec_uid)/抖音地址/粉丝数/获赞数/关注数/作品数/IP属地/简介等)。**禁止从任何数据库读取账号字段回填账号表(含身份标识 sec_uid/抖音号/抖音地址)**:工作台库 `mcn-plugin.db` 的 `hot_accounts` 表 = MCP 内部账号库(`list_hot_accounts`/`hot_account_detail`)的**本地副本,读取其数据填表等同违反本红线**。这些库仅用于**流程状态参考**(判断是否已有解析/产出),**不是功能一数据源**;账号表登记(Step 1.0)只占位 账号ID+达人昵称,抖音号/sec_uid/地址等一律留空,**待浏览器抓取后回填**(浏览器抓取到的值才写)。仅当浏览器无法访问(账号搜索不到/主页打不开等)时才可兜底补充,并须注明数据来源。详见 `references/铁律避坑规则/账号信息获取执行避坑.md`。 |
| 已有数据处理(统一决策) | 用户要求「获取账号数据/提取账号设定/分析视频」时,**库中已有 ≠ 可直接使用**:实时数据(账号信息/视频列表)必须重新获取(红线禁回填);分析产物(人设卡/视频解析/分析报告)**复用优先 + 询问是否更新**,只对增量(新视频/未解析)补获取;「已有」须校验内容质量(存在≠有效,假数据视为无)。分场景决策表见 `references/铁律避坑规则/已有数据处理规则.md`(收敛更新时间检查/卡片覆盖询问/表格复用/入库去重等散落规则)。 |
| 赛道标准枚举(29个,**唯一权威**) | 赛道(track/「赛道」键)取值必须属于以下 **29 个赛道**(28 标准 + 1 补充):个人才艺 / 生活vlog / 财富理财 / 二次元 / 居家装修 / 学习教育 / 小剧场 / 数码科技 / 旅行 / 美食 / 美妆(原「化妆美容」统一更名) / 动物 / 亲子 / 汽车 / 情感 / 三农 / 健康医学 / 潮流风尚 / 舞蹈才艺 / 颜值造型 / 人文 / 音乐 / 影视 / 身体锻炼 / 体育 / 明星娱乐 / 游戏 / 其他 / **剧情**(剧情为用户指定保留,不归一)。**不带「赛道」后缀**(如「生活vlog」);人设卡/账号设定「赛道」键取首段(" / " 分隔)后去后缀,不在 29 个枚举内则归一到最接近标准值或置空待确认。本参数只在 SKILL.md 定义(源:`dsh-plugin-dev/references/赛道标准枚举.md`),其他文件引用此处,调整只改本行。 |
| 账号数据分析(功能四) | 独立于人设卡,聚焦「数据表现规律」(内容表现规律/月度趋势/差异化壁垒/商业化空间),产出独立文件 `{达人昵称}账号数据分析.md`,**不与功能三人设卡嵌套**。方法论见 `references/账号数据分析方法.md`。 |
| 数据分析依据(红线) | 功能四所有分析结论必须有明确数据依据:具体到视频标题 + 具体数值。后台私有数据拿不到时(完播率/留存率/GMV等)标「无数据」,**禁止用间接推断或定性描述替代**。 |
| 数据分析两段式(红线) | 功能四强制「**逐条拆解 → 汇总分析**」两段式:先对每条入选视频重度调用源技能逐条拆解(拆解内容单独保存到各视频自己的文件夹),**拆解全部完成后**才能进入汇总分析,禁止跳过逐条拆解直接写报告。 |
| 源技能路径与同步 | 生成账号设定/分析视频数据时,**调用源技能** `{源技能目录}/references/` 下的对应文件(`创作流程/4_账号设定解析和确认.md`、`创作流程/3_对标视频账号拆解.md`、`知识库/01_账号设定/`、`知识库/08_对话风格/`、`知识库/12_爆款拆解/`、`知识库/11_广告植入/` 等)。**三环境判定(锚点 = 本机是否存在 `D:\AgentSkill`,与技能装在什么路径无关)**:① 开发机 = 本机有 `D:\AgentSkill` 且技能路径在 `D:\AgentSkill\` 下(源技能);② dsh 环境 = 本机有 `D:\AgentSkill` 且技能路径含 `.dsh`(如 `C:\Users\{用户名}\.dsh\skills\`);③ 用户环境 = 本机**没有** `D:\AgentSkill`(技能装在哪、什么路径都是用户环境)。`{源技能目录}` 仅在 ①/② 存在,③ 不存在(用本技能自有知识库)。**源技能只读,禁止修改(不提供任何改前评估流程,一律视为不可写)**;本技能只能**从源技能单向同步**:① 从源技能**定位相关文件**(账号设定提炼实际依赖);② **重新整理为适合本技能的流程和知识库**(按本技能自有体系组织,**不复刻源技能目录结构**)。源技能为权威源;本技能 `references/知识库/账号设定/`(账号设定专用)与 `references/知识库/视频拆解/`(账号数据分析专用,23文件)为重组后的自有知识库,③用户环境独立使用。 |
| 部署场景修改规则 | **仅 ③ 用户环境(本机没有 `D:\AgentSkill`)适用本规则**;① 开发机 / ② dsh 环境(本机有 `D:\AgentSkill`)不适用本规则。用户环境:源技能路径不存在,标准库 `references/`(含自有知识库 `知识库/账号设定/` + `知识库/视频拆解/`)**永远不变**。用户新增/修改内容统一写入 `references-add/` 增量库,**目录结构与标准库镜像对齐**:`feature/`(功能级增量,按 `01_获取账号信息/02_获取视频/03_提炼账号设定/04_分析视频数据` 命名,对应标准库 `references/feature/`)、`知识库/`(方法论增量)、`铁律避坑规则/`(铁律与避坑)。运行时 `references/` + `references-add/` 取并集、标准库优先。**功能禁用判定 = 标准库与增量库中该功能文件都不存在**(只删标准库、增量库仍有时功能仍启用)。产出路径以 `references/路径配置.md` 为准(①开发机/②dsh环境并入标准库;③ 用户环境迁移回 `references-add/` 见其 README)。详见 `references-add/README.md`。 |
| 源技能路径与同步 | 生成账号设定/分析视频数据时,**调用源技能** `{源技能目录}/references/` 下的对应文件(`创作流程/4_账号设定解析和确认.md`、`创作流程/3_对标视频账号拆解.md`、`知识库/01_账号设定/`、`知识库/08_对话风格/`、`知识库/12_爆款拆解/`、`知识库/11_广告植入/` 等)。**两环境判定(仓库特征判定:看技能是否处于源技能 git 检出内——`git -C {技能目录} rev-parse --show-toplevel`;与技能装在什么路径无关)**:① 开发机 = 技能**处于源技能 git 检出内**(源技能);② 用户环境 = 技能**不在任何 git 检出内**(技能装在哪、什么路径都是用户环境)。`{源技能目录}` 仅在 ① 存在,② 不存在(用本技能自有知识库)。**源技能只读,禁止修改(不提供任何改前评估流程,一律视为不可写)**;本技能只能**从源技能单向同步**:① 从源技能**定位相关文件**(账号设定提炼实际依赖);② **重新整理为适合本技能的流程和知识库**(按本技能自有体系组织,**不复刻源技能目录结构**)。源技能为权威源;本技能 `references/知识库/账号设定/`(账号设定专用)与 `references/知识库/视频拆解/`(账号数据分析专用,23文件)为重组后的自有知识库,②用户环境独立使用。 |
| 部署场景修改规则 | **仅 ② 用户环境(技能不在任何 git 检出内)适用本规则**;① 开发机(技能处于源技能 git 检出内)不适用本规则。用户环境:源技能路径不存在,标准库 `references/`(含自有知识库 `知识库/账号设定/` + `知识库/视频拆解/`)**永远不变**。用户新增/修改内容统一写入 `references-add/` 增量库,**目录结构与标准库镜像对齐**:`feature/`(功能级增量,按 `01_获取账号信息/02_获取视频/03_提炼账号设定/04_分析视频数据` 命名,对应标准库 `references/feature/`)、`知识库/`(方法论增量)、`铁律避坑规则/`(铁律与避坑)。运行时 `references/` + `references-add/` 取并集、标准库优先。**功能禁用判定 = 标准库与增量库中该功能文件都不存在**(只删标准库、增量库仍有时功能仍启用)。产出路径以 `references/路径配置.md` 为准(①开发机并入标准库;② 用户环境迁移回 `references-add/` 见其 README)。详见 `references-add/README.md`。 |
| 技能边界(红线) | 本技能是**账号分析技能**,产出止步于账号分析侧交付物:达人账号表更新(功能一)、短视频数据表格(功能二)、账号设定人设卡(功能三)、账号数据分析报告(功能四)、视频解析产物(功能五)。凡需**生成或改写脚本**(含「基于账号设定替换事件重写」等轻量场景),**必须切换到「短视频工作台」技能走 S1-S11 全流程**(含 S11 脚本检查和诊断:数字账取证、开场钩子铁律、编导终审验收),**禁止在本技能会话内直接手写脚本交付**。 |
---
@@ -79,7 +79,7 @@ description: "MCN 达人爆款视频解析与人设卡生成技能:达人账
| 技能 | 路径 |
|------|------|
| 脚本创作技能(权威源) | `{源技能目录}`(**三环境判定**:① 开发机 = 本机有 `D:\AgentSkill` 且技能在 `D:\AgentSkill\` 下;② dsh 环境 = 本机有 `D:\AgentSkill` 且技能路径含 `.dsh`;③ 用户环境 = 本机没有 `D:\AgentSkill`(源技能不存在)。当前机器源技能路径 `D:/AgentSkill/mcn-video-script/project/短视频脚本创作/V1.0`;生成设定/分析数据时调用此源;**只读,禁止修改,只能从源技能同步到本技能**;本技能 `references/知识库/` 为账号设定 + 账号数据分析两个专用重组库,按两步法同步,不复刻源技能目录结构) |
| 脚本创作技能(权威源) | `{源技能目录}`(**两环境判定(仓库特征判定)**:看技能是否处于源技能 git 检出内(`git -C {技能目录} rev-parse --show-toplevel`)——在 = ① 开发机、不在 = ② 用户环境(源技能不存在)。**`{源技能目录}` 定位**:本技能内嵌部署时 = 本技能所在技能树的根(即本技能目录上两级,该目录中同时含 `SKILL.md` 与 `subskills/`);独立部署时按上述判定在 ① 环境中定位。生成设定/分析数据时调用此源;**只读,禁止修改,只能从源技能同步到本技能**;本技能 `references/知识库/` 为账号设定 + 账号数据分析两个专用重组库,按两步法同步,不复刻源技能目录结构) |
| 女娲造人术(nuwa-skill) | `subskills/nuwa-skill-main/`(已内嵌,无需跨 skill 调用) |
| 人设卡生成方法 | `references/人设卡生成方法.md`(账号类型判定+模板结构+深度分析,已吸收V2.0模板核心;综合人设卡统一12段结构,独立可执行) |
| 账号设定知识库(自有) | `references/知识库/账号设定/`(功能三人设选项+IP体系+对话风格9法) |
@@ -92,7 +92,7 @@ description: "MCN 达人爆款视频解析与人设卡生成技能:达人账
| 功能六流程 | `references/feature/06_生成账号设定卡片.md`(生成账号设定卡片五步执行:读设定→读模板样例→模块映射→SVG手绘→验证交付) |
| 账号设定卡模板样例 | `references/样例/`(最佳成品模板:`模板_旧梦留声机账号设定卡.svg` + 渲染参考 `参考_旧梦留声机_账号设定卡_长图版.png`;新账号复用版式骨架与样式常量,内容全替换) |
| 产出路径配置 | `references/路径配置.md`(🔴 产出路径唯一权威配置:用户自定义路径最高优先,默认桌面 `账号分析/` 兜底) |
| 增量库(references-add) | `references-add/`(③ 用户环境场景的增量库机制;当前①开发机/②dsh环境状态仅保留 README 机制说明,原增量已并入标准库:`references/路径配置.md` + `references/铁律避坑规则/`(含功能三`账号设定执行避坑.md` + 功能四`账号数据分析执行避坑.md`)) |
| 增量库(references-add) | `references-add/`(② 用户环境场景的增量库机制;当前 ①开发机 状态仅保留 README 机制说明,原增量已并入标准库:`references/路径配置.md` + `references/铁律避坑规则/`(含功能三`账号设定执行避坑.md` + 功能四`账号数据分析执行避坑.md`)) |
### 2.3 MCP 工具(myai-mcp-production)
@@ -1,19 +1,19 @@
# references-add(增量库)—— 机制说明
> **三环境判定(两步判定,与 SKILL.md 一致)**:第一步查本机是否存在 `D:\AgentSkill`——**无 = ③ 用户环境**(技能无论装在哪、什么路径);有 → 第二步看技能路径——**在 `D:\AgentSkill\` 下 = ① 开发机(源技能)**、**含 `.dsh` = ② dsh 部署环境**(如 `{主目录}/.dsh/skills/`)。
> **两环境判定(一步判定 · 仓库特征判定,与 SKILL.md 一致)**:判断技能是否**处于源技能 git 检出内**(`git -C {技能目录} rev-parse --show-toplevel` 返回仓库根)——**在 = ① 开发机(源技能)**、**不在 = ② 用户环境**(技能无论装在哪、什么路径)。
>
> **references-add 机制只服务 ③ 用户环境**:① 开发机 / ② dsh 环境标准库 `references/` 可正常修改(新增内容直接并入标准库对应位置),本目录可仅保留本说明(机制文档);③ 用户环境部署后标准库 `references/` **永远不变**,新增/修改内容统一写入本目录。
> **references-add 机制只服务 ② 用户环境**:① 开发机标准库 `references/` 可正常修改(新增内容直接并入标准库对应位置),本目录可仅保留本说明(机制文档);② 用户环境部署后标准库 `references/` **永远不变**,新增/修改内容统一写入本目录。
## 用途
当本技能(MCNDouAnalysis)被部署到**③ 用户环境**(本机没有 `D:\AgentSkill`)时,本技能的标准库 `references/`(含自有知识库 `知识库/账号设定/` + `知识库/视频拆解/`)在部署后**永远不变**。用户在使用过程中新增/修改的流程、方法论、分析方式、铁律等内容,统一整理到本文件夹。
当本技能(MCNDouAnalysis)被部署到**② 用户环境**(技能不在任何 git 检出内)时,本技能的标准库 `references/`(含自有知识库 `知识库/账号设定/` + `知识库/视频拆解/`)在部署后**永远不变**。用户在使用过程中新增/修改的流程、方法论、分析方式、铁律等内容,统一整理到本文件夹。
## 核心原则
| 目录 | 定位 | 内容来源 | 修改策略 |
|:---:|------|------|------|
| `references/` | 原始标准库(内置) | 开发时固化(含从源技能按两步法重组的账号设定知识库 + 视频拆解知识库) | **部署后永远不变** |
| `references-add/` | 动态增量库 | ③ 用户环境中用户对话中积累 | 随使用持续增长 |
| `references-add/` | 动态增量库 | ② 用户环境中用户对话中积累 | 随使用持续增长 |
## 目录结构
@@ -57,14 +57,13 @@ references/(标准库) + references-add/(增量库) → 取并集
## 修改规则(区分场景,判定标准与 SKILL.md 一致)
| 场景 | 判断依据(两步判定) | 生成设定调用的文件 | 修改方式 |
| 场景 | 判断依据(一步判定:看是否处于源技能 git 检出内) | 生成设定调用的文件 | 修改方式 |
|------|---------|------------------------|---------|
| **① 开发机** | 本机有 `D:\AgentSkill` 且技能路径在 `D:\AgentSkill\` 下(源技能) | 调用源技能对应文件 | **源技能只读,禁止修改**;只能从源技能**单向同步**到本技能:按两步法(① 从源技能定位相关文件 → ② 重组为本技能自有流程/知识库)同步到 `references/知识库/`;标准库 `references/` 可正常修改(开发机直接改标准库,不使用增量库机制) |
| **② dsh 环境** | 本机有 `D:\AgentSkill` 且技能路径含 `.dsh`(如 `{主目录}/.dsh/skills/`) | 调用源技能对应文件 | **源技能只读,禁止修改**;只能从源技能**单向同步**到本技能:按两步法(① 从源技能定位相关文件 → ② 重组为本技能自有流程/知识库)同步到 `references/知识库/`;标准库 `references/` 可正常修改,增量内容直接并入标准库对应位置 |
| **③ 用户环境** | 本机**没有** `D:\AgentSkill`(技能装在哪、什么路径都是用户环境) | 调用本技能 `references/`(标准库,含账号设定 + 视频拆解自有知识库) | **不修改 `references/`**,新增/修改内容写入 `references-add/` 对应目录 |
| **① 开发机** | 技能**处于源技能 git 检出内**(源技能) | 调用源技能对应文件 | **源技能只读,禁止修改**;只能从源技能**单向同步**到本技能:按两步法(① 从源技能定位相关文件 → ② 重组为本技能自有流程/知识库)同步到 `references/知识库/`;标准库 `references/` 可正常修改(开发机直接改标准库,不使用增量库机制) |
| **② 用户环境** | 技能**不在任何 git 检出内**(技能装在哪、什么路径都是用户环境) | 调用本技能 `references/`(标准库,含账号设定 + 视频拆解自有知识库) | **不修改 `references/`**,新增/修改内容写入 `references-add/` 对应目录 |
## ③ 用户环境启用流程(如未来部署)
## ② 用户环境启用流程(如未来部署)
1. 把 `references/路径配置.md`、`references/铁律避坑规则/` 移回 `references-add/` 对应位置
2. 删除标准库中③ 用户环境不需要的增量内容
2. 删除标准库中② 用户环境不需要的增量内容
3. 按本文件「修改规则」维护增量库
@@ -13,7 +13,7 @@
### Step 1.0 账号表登记(前置)
先检查数据目录的 `达人账号表.xlsx`(**外部数据文件,不入技能包**,位置见 SKILL.md 红线「达人账号表.xlsx」行;**表缺失时先按 15 列结构现场新建**),输入信息中的账号若不在表中,先添加到表:**只占位 账号ID(自增)+ 达人昵称**;抖音号/抖音ID(sec_uid)/抖音地址等身份标识字段**留空,待浏览器抓取后回填**(浏览器抓取到的值才写,存文本格式防科学计数法),其余字段(粉丝数/解析状态/更新时间等)同样留空待补。**禁止从工作台库(`mcn-plugin.db` 的 `hot_accounts` 表 = MCP 内部账号库本地副本)/ dsh 原库 / MCP 内部账号库读取任何字段填表**——身份标识也一样,一律浏览器抓取(红线见 SKILL.md「账号信息数据源」)。再继续获取数据。
先检查数据目录的 `达人账号表.xlsx`(**外部数据文件,不入技能包**,位置见 SKILL.md 红线「达人账号表.xlsx」行;**表缺失时先按 15 列结构现场新建**),输入信息中的账号若不在表中,先添加到表:**只占位 账号ID(自增)+ 达人昵称**;抖音号/抖音ID(sec_uid)/抖音地址等身份标识字段**留空,待浏览器抓取后回填**(浏览器抓取到的值才写,存文本格式防科学计数法),其余字段(粉丝数/解析状态/更新时间等)同样留空待补。**禁止从工作台库(`mcn-plugin.db` 的 `hot_accounts` 表 = MCP 内部账号库本地副本)/ MCP 内部账号库读取任何字段填表**——身份标识也一样,一律浏览器抓取(红线见 SKILL.md「账号信息数据源」)。再继续获取数据。
### 浏览器搜索抖音账号ID
@@ -77,7 +77,7 @@ id = "{detailId}"
> 完整方法见 `references/人设卡生成方法.md`(账号类型判定 + 三种模板结构 + 女娲造人术深度分析 + 综合人设卡结构)
**必须使用两个技能库综合生成,缺一不可:**
- 脚本创作技能 → 源技能 `{源技能目录}/references/`(结构骨架,调用 `创作流程/4_账号设定解析和确认.md` + `创作流程/3_对标视频账号拆解.md` + `知识库/01_账号设定/` + `知识库/08_对话风格/`;①开发机/②dsh环境优先调用源技能。V2.0 模板核心已吸收进 `references/人设卡生成方法.md`,③用户环境独立使用;选项体系在 `references/知识库/账号设定/`)
- 脚本创作技能 → 源技能 `{源技能目录}/references/`(结构骨架,调用 `创作流程/4_账号设定解析和确认.md` + `创作流程/3_对标视频账号拆解.md` + `知识库/01_账号设定/` + `知识库/08_对话风格/`;①开发机优先调用源技能。V2.0 模板核心已吸收进 `references/人设卡生成方法.md`,②用户环境独立使用;选项体系在 `references/知识库/账号设定/`)
- 女娲造人术 → `subskills/nuwa-skill-main/`(深度分析方法)
**人设卡统一 12 段结构**:账号核心定位 → 账号内容基因 → 类型库(按账号类型适配)→ 情绪结构 → 视听风格 → 内容打法 → 内容规则 → 系列内容规划 → 差异化壁垒 → 红线清单 → 当前需解决的问题 → 设定分析说明。**人设卡聚焦内容创作本身(含差异化壁垒的内容侧"为什么别人做不了"),不含商业化变现路径分析**(商业化/市场分析归功能四账号数据分析)。
@@ -35,7 +35,7 @@
2. 按发布时间过滤近4个月
3. 按月份分组,每月取最好1条 + 最差1条
4. 补齐 MCP 解析:入选视频无解析的先提交解析(`upload_douyin_video` → 等待 → `short_video_detail`)
5. **逐条拆解(重度调用源技能 + 自有拆解知识库)**:按 `references/账号数据分析方法.md` 3.0 对每条入选视频完整拆解(调用源技能 `{源技能目录}/references/` 的拆解知识库,见《账号数据分析方法.md》3.0 源技能依赖表;**本技能自有拆解知识库 `references/知识库/视频拆解/`(23文件)③用户环境独立运行**,无源技能时以自有知识库为准),拆解分析保存到各视频自己的文件夹(`视频分析/{视频文件夹}/{视频标题}_拆解分析.md`)
5. **逐条拆解(重度调用源技能 + 自有拆解知识库)**:按 `references/账号数据分析方法.md` 3.0 对每条入选视频完整拆解(调用源技能 `{源技能目录}/references/` 的拆解知识库,见《账号数据分析方法.md》3.0 源技能依赖表;**本技能自有拆解知识库 `references/知识库/视频拆解/`(23文件)②用户环境独立运行**,无源技能时以自有知识库为准),拆解分析保存到各视频自己的文件夹(`视频分析/{视频文件夹}/{视频标题}_拆解分析.md`)
6. **汇总分析**:拆解全部完成后,结合拆解文件 + 表格数据,按 3.1-3.6 维度逐项分析(月度趋势/爆款共性/低表现共性/内容要素关联/差异化壁垒/商业化空间);报告内容层结论必须回溯到拆解文件
7. 产出 `{达人昵称}账号数据分析.md`
@@ -59,7 +59,7 @@
| 文件 | 用途 |
|------|------|
| `references/账号数据分析方法.md` | 完整方法(六维分析维度/报告8章结构/13条报告质量自查红线) |
| `references/知识库/视频拆解/` | 功能四拆解知识库(23文件:开场/框架/情绪/钩子/植入/拆解,③用户环境独立运行) |
| `references/知识库/视频拆解/` | 功能四拆解知识库(23文件:开场/框架/情绪/钩子/植入/拆解,②用户环境独立运行) |
| `references/解析结果格式规范.md` | 目录结构权威源 |
| `references/接口调用/MCP_工具调用规范.md` | MCP 安装/登录/降级规范(Step 7 补解析 + 拆解降级) |
| `references/铁律避坑规则/账号数据分析执行避坑.md` | 功能四执行避坑(14个问题按工具/数据/流程/分析四类归档) |
@@ -47,7 +47,7 @@
| 6 | 参考某账号样式 | 用户指定「像 XX 那样做」 | 读被参考账号的卡片做样式参考(版式+底色跟该账号风格),内容仍用本账号设定 |
| 7 | 调整已有卡片 | 用户说改现有卡片的某处 | **直接改 SVG 文件**(单点修改,不重走功能三/全流程) |
**环境路径**:① 开发机 / ③ 用户环境 = `桌面/MCNSkill项目/{账号名}/`;② dsh 环境 = `D:\dshworkspace\解析任务\{达人昵称}/`(以 `references/路径配置.md` 为唯一权威)。
**环境路径**:开发机与用户环境**同构** = `桌面/MCNSkill项目/{账号名}/`(**无 dsh 分支**,以 `references/路径配置.md` 为唯一权威)。
**双向联动维护(修改时两边一起修改)**:
- 设定 md 与设定卡 = **同一账号信息的两种载体**,任一边修改,另一边必须同步:
@@ -17,8 +17,8 @@
| 环境 | 判定条件 | 安装内容 |
|------|---------|---------|
| **开发环境** | 本机存在 `D:\AgentSkill` 且 Skill 根目录路径在 `D:\AgentSkill\` 下(源技能仓库) | `myai-mcp-production` + `myai-mcp-test`(推荐两个都装,可选只装正式) |
| **非开发环境** | 部署到其他路径(组员机器、服务器) | 只装 `myai-mcp-production`(正式),不装测试环境 |
| **开发环境** | Skill 根目录**处于源技能 git 检出内**(源技能仓库,`git -C {技能目录} rev-parse --show-toplevel` 返回仓库根) | `myai-mcp-production` + `myai-mcp-test`(推荐两个都装,可选只装正式) |
| **非开发环境** | 部署到其他路径(组员机器、服务器;不在任何 git 检出内) | 只装 `myai-mcp-production`(正式),不装测试环境 |
---
@@ -156,7 +156,7 @@
## 五、browser-harness 调用方式(环境 SOP)
> **环境说明(2026-09-02 调整)**:browser-harness 由 uv 工具安装,可执行文件位置**按系统区分**——Windows:`~/.local/bin/browser-harness.exe`;macOS/Linux:`~/.local/bin/browser-harness`;**技能本体位置(09-02 起)**——主技能 `短视频工作台/subskills/browser-harness/`(源仓库:`project/短视频脚本创作/V1.0/subskills/browser-harness/`,含 `envs/browser-harness/` 环境与 `src/`;**不再挂在 mcn-dou-analysis 下**);dsh 部署环境 `~/.dsh/skills/browser-harness/`(随 dsh_MCNProject 仓库分发,换机器自带)。
> **环境说明(2026-09-02 调整)**:browser-harness 由 uv 工具安装,可执行文件位置**按系统区分**——Windows:`~/.local/bin/browser-harness.exe`;macOS/Linux:`~/.local/bin/browser-harness`;**技能本体位置(09-02 起)**——主技能 `短视频工作台/subskills/browser-harness/`(源仓库:`project/短视频脚本创作/V1.0/subskills/browser-harness/`,含 `envs/browser-harness/` 环境与 `src/`;**不再挂在 mcn-dou-analysis 下**;**09-10 起无 dsh 部署环境**)。
### 5.0 环境检查与安装(每次浏览器任务前【必做】)
@@ -172,7 +172,7 @@
- **可跳过**:改用三方数据 API / MCP 兜底数据源,但功能一/二浏览器抓取无法执行
- **同意安装**:`uv tool install --python 3.12 --upgrade --force browser-harness`(`--python 3.12` 防止选中过旧版本;`--upgrade --force` 覆盖旧安装)
- **技能本体**:源仓库 `project/短视频脚本创作/V1.0/subskills/browser-harness/`(主技能 `短视频工作台/subskills/browser-harness/`);dsh 部署环境 `~/.dsh/skills/browser-harness/`(随 dsh_MCNProject 仓库分发;如缺失可从 browser-harness 仓库的 `browser-harness skill` 命令重新生成)
- **技能本体**:源仓库 `project/短视频脚本创作/V1.0/subskills/browser-harness/`(主技能 `短视频工作台/subskills/browser-harness/`;**09-10 起无 dsh 部署环境**;如缺失可从 browser-harness 仓库的 `browser-harness skill` 命令重新生成)
- **安装后验证**:`command -v browser-harness`(macOS/Linux)/ `where browser-harness`(Windows)有输出 + `browser-harness doctor` 三要素全绿,再进入下方 5.1 环境准备
### 5.1 环境准备(每次浏览器任务前【必做】)
@@ -262,30 +262,33 @@ PY
**锁规则(每个需要浏览器的任务必须遵守)**:
1. **锁文件**:`D:\AgentSkill\mcn-workshop\.browser-lock`(工作台 `/api/run` 对浏览器类任务已自动注入本约束到 prompt,AI 会话执行时遵守即可)
1. **锁文件位置**:**工作台目录下的 `.browser-lock`**(工作台目录 = 本技能根下的 `mcn-work-shop/`)。工作台 `/api/run` 对浏览器类任务会把**运行时解析出的绝对路径**注入 prompt,AI 会话执行时**以注入的路径为准**;此处不写死盘符。
- 工作台触发的会话 cwd 即**工作台目录**,故可直接用相对路径 `.browser-lock`(推荐,跨机器可迁移)
- 手工执行(cwd 不是工作台目录)时,先定位工作台目录再拼 `.browser-lock`
2. **操作前**:检查锁文件是否存在(`ls`)→ 存在则等 10 秒重试(最多 18 次 ≈ 3 分钟)
3. **死锁保护**:锁文件修改时间超过 10 分钟 → 视为死锁,可删除后抢占
4. **拿锁**:`echo "<任务名> <时间戳>" > D:\AgentSkill\mcn-workshop\.browser-lock` → 才操作浏览器
5. **释放**:浏览器操作全部完成后(无论成败)`rm -f D:\AgentSkill\mcn-workshop\.browser-lock`
4. **拿锁**:`echo "<任务名> <时间戳>" > .browser-lock` → 才操作浏览器
5. **释放**:浏览器操作全部完成后(无论成败)`rm -f .browser-lock`
6. **串行协调**:多个任务抢锁 = 先到先用;拿到锁的任务完成后释放,下一个任务继续;标签页复用规则(5.5)仅在自己持有锁期间适用
```bash
# 前置:确认当前目录就是工作台目录(目录内含 .browser-lock 与 server.js)
# 拿锁(互斥,带重试)
for i in $(seq 1 18); do
if [ ! -f "D:/AgentSkill/mcn-workshop/.browser-lock" ]; then
echo "$(date '+%H:%M:%S') ${任务名}" > "D:/AgentSkill/mcn-workshop/.browser-lock"
if [ ! -f ".browser-lock" ]; then
echo "$(date '+%H:%M:%S') ${任务名}" > ".browser-lock"
break
fi
# 死锁保护:锁超过 10 分钟强制抢占
if [ -f "D:/AgentSkill/mcn-workshop/.browser-lock" ] && [ $(($(date +%s) - $(stat -c %Y "D:/AgentSkill/mcn-workshop/.browser-lock" 2>/dev/null || echo 0))) -gt 600 ]; then
rm -f "D:/AgentSkill/mcn-workshop/.browser-lock"
if [ -f ".browser-lock" ] && [ $(($(date +%s) - $(stat -c %Y ".browser-lock" 2>/dev/null || echo 0))) -gt 600 ]; then
rm -f ".browser-lock"
continue
fi
sleep 10
done
# ... 浏览器操作 ...
# 释放锁(无论成败)
rm -f "D:/AgentSkill/mcn-workshop/.browser-lock"
rm -f ".browser-lock"
```
```bash
@@ -1,6 +1,6 @@
# 路径引用规范(相对路径铁律,P0)
> 适用:本技能树(主技能 + references/references-add + scripts + 全部 subskills 及嵌套 subskills)。技能会被部署到 **开发机 / dsh 环境 / 用户环境** 三种机器,**绝对路径随机器变化必然失效**,故技能内一切文件引用一律使用**相对路径**。
> 适用:本技能树(主技能 + references/references-add + scripts + 全部 subskills 及嵌套 subskills)。技能会被部署到 **开发机 / 用户环境** 两种机器,**绝对路径随机器变化必然失效**,故技能内一切文件引用一律使用**相对路径**。
## 一、铁律(3 条)
@@ -18,12 +18,12 @@
| `__file__` 动态推导 | `BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))` | 脚本定位技能根,跨环境可迁移 |
| 环境变量 | `os.getenv("REDFOX_API_KEY")`、`MCN_RANKING_DIR` | 值由外部注入 |
| URL | `https://github.com/...` | 外部资源 |
| 环境判别锚点 | 「本机是否存在 `D:\AgentSkill`」「技能路径是否含 `.dsh`」 | 仅作**是否存在/是否包含**的判定条件,**禁止**将其作为实际读写路径 |
| 动态产出目录占位 | 桌面 `MCNSkill项目/{账号名}/`、`D:\dshworkspace\创作任务\` | 由各技能 `references(-add)/路径配置.md` 运行时判定,允许出现于规则文档 |
| 环境判别锚点 | 「技能目录是否处于 git 检出内」 | 仅作**是否存在**的判定条件,**禁止**将其作为实际读写路径;锚点必须用**仓库特征**(git 检出),禁止用写死盘符 |
| 动态产出目录占位 | 桌面 `MCNSkill项目/{账号名}/` | 由各技能 `references(-add)/路径配置.md` 运行时判定,允许出现于规则文档 |
## 三、禁令项(这些绝对禁止)
- **Windows 盘符绝对路径**:`C:\Users\maidou\...`、`D:\AgentSkill\...`(含正反斜杠两种写法)——除「环境判别锚点/路径配置规则表」语境外的任何引用位置。
- **Windows 盘符绝对路径**:`C:\Users\maidou\...`、`D:\MyRepo\...`(含正反斜杠两种写法)——除「环境判别锚点/路径配置规则表」语境外的任何引用位置。
- **Unix 绝对路径**:`/Users/maidou/...`、`/home/...`、`/tmp/...`(shebang `#!/usr/bin/env python3` 除外)。
- **越级引用**:`../../..` 跳出技能根指向包外文件。
- **引用技能包外文档**:md 链接指向不存在于包内的本地文件(如仓库根的维护文档)——部署环境无此文件即断链。
@@ -34,7 +34,7 @@
| 场景 | 说明 |
|------|------|
| 仅开发机工具 | 不随技能分发的目录(如 `mcn-work-shop/`),内部可保留开发机路径 |
| 环境判别/路径配置文档 | `路径配置.md`、SKILL.md 环境判定节——锚点 `D:\AgentSkill`、`.dsh`、`D:\dshworkspace\...` 是**规则内容**不是引用,保留 |
| 环境判别/路径配置文档 | `路径配置.md`、SKILL.md 环境判定节——锚点 git 检出特征是**规则内容**不是引用,保留 |
| 示例/教学文本 | `C:/Videos/demo.mp4`、`C:/path/to/file` 等明确示例(`path/to`、`demo` 字样) |
| 历史日志 | `.workbuddy/memory/` 既有日志保留原样,不改写历史 |
| 第三方整包 | 整包拷贝的第三方技能(如 browser-harness/lieflat-charts/参考skills/)内部自带约定不重写;但**本技能对它们的引用**仍须相对路径 |
@@ -50,16 +50,16 @@
### 3.0 逐条拆解(汇总前必做)
**重度调用源技能 + 本技能自有拆解知识库**(源技能为权威源,仅①开发机/②dsh环境存在;本技能自有知识库③用户环境独立运行):
**重度调用源技能 + 本技能自有拆解知识库**(源技能为权威源,仅①开发机存在;本技能自有知识库②用户环境独立运行):
| 源技能文件(权威源,路径相对 `{源技能目录}/references/`,仅①开发机/②dsh环境存在) | 用于 |
| 源技能文件(权威源,路径相对 `{源技能目录}/references/`,仅①开发机存在) | 用于 |
|-----------|------|
| `创作流程/3_对标视频账号拆解.md` | 拆解主流程:逐条视频按拆解步骤完整过一遍 |
| `知识库/12_爆款拆解/爆款文案反推工作流_13字段标准化拆解.md` | 逐条标准化拆解字段(选题/钩子/框架/情绪/结尾等) |
| `知识库/11_广告植入/广告植入方法论.md` | 带品牌视频的植入方式评估(五种植入方式/黄金位/口播花字分工/卖点分层/自然度) |
| `知识库/03_框架节奏` `04_爆款开场` `06_剧情钩子` `07_情绪共鸣` | 拆解各内容维度时的评判标准 |
> **本技能自有拆解知识库 `references/知识库/视频拆解/`(23文件)**:与上表源技能文件对应(两步法重组为本技能自有体系),③用户环境无源技能时以自有知识库为准拆解,深度不受限。拆解时源技能(①开发机/②dsh环境)与自有知识库取并集、源技能优先。
> **本技能自有拆解知识库 `references/知识库/视频拆解/`(23文件)**:与上表源技能文件对应(两步法重组为本技能自有体系),②用户环境无源技能时以自有知识库为准拆解,深度不受限。拆解时源技能(①开发机)与自有知识库取并集、源技能优先。
> **五维诊断模型说明**:单条视频数据表现诊断沿用五维模型(传播力/留存力/互动力/转化力/内容力),维度定义见本方法论 四(报告结构·二)(不依赖源技能文件——源技能 V2.0 已删除独立五维模型文件,数据诊断标准以本方法论为准)。
@@ -129,7 +129,7 @@
### 3.6 商业化空间分析
> 原"人设卡"中的广告植入相关内容移至此处。
> **植入方式分析必须参考《广告植入方法论》**(源技能 `知识库/11_广告植入/广告植入方法论.md`,本技能自有 `references/知识库/视频拆解/植入01_广告植入方法.md` ③用户环境独立使用):用「五种植入方式(口播/画面/情节/场景/体验)+ 植入黄金位(四段式)+ 口播花字分工 + 卖点分层 + 自然度评估」评估植入好坏,禁止只用"硬广/软广"浅标签。
> **植入方式分析必须参考《广告植入方法论》**(源技能 `知识库/11_广告植入/广告植入方法论.md`,本技能自有 `references/知识库/视频拆解/植入01_广告植入方法.md` ②用户环境独立使用):用「五种植入方式(口播/画面/情节/场景/体验)+ 植入黄金位(四段式)+ 口播花字分工 + 卖点分层 + 自然度评估」评估植入好坏,禁止只用"硬广/软广"浅标签。
> **已验证的「好的植入」范式**(示例数据仅作方法说明,不代表当前分析账号):「痛点场景化口播 + 剧情道具回收」——口播嵌在剧情痛点场景(如约会/形象焦虑类痛点)而非中段打断情绪,产品梗提前埋设、剧情结尾回收。对标案例:该类植入视频点赞 80万+(示例账号近4个月 TOP1/2)。
- 当前是否有广告植入
@@ -5,13 +5,14 @@
## 一、默认路径(用户未自定义时生效)
**先判断运行环境(三环境两步判定),再按运行系统解析桌面路径:**
**先判断运行环境(两环境判定 · 仓库特征判定,v3),再按运行系统解析桌面路径:**
| 运行环境 | 判断依据 | 默认产出根目录 |
|:---|:---|:---|
| dsh 部署环境 | 技能路径含 `.dsh`,且本机存在 `D:\AgentSkill`(如 `C:\Users\{用户名}\.dsh\skills\…`) | **优先** `D:\dshworkspace\解析任务\`(主工作区「解析任务」目录) |
| 开发机 | 技能路径不含 `.dsh`(源技能在 `D:\AgentSkill` 下) | 用户桌面 `MCNSkill项目/`(`{达人昵称}/` 直挂,无技能文件夹层) |
| 用户环境 | 本机不存在 `D:\AgentSkill`(技能装在用户机器上,路径不限、不一定含 `.dsh`) | 用户桌面 `MCNSkill项目/`(与开发机同构) |
| 开发机 | 技能**处于源技能 git 检出内**(`git -C {技能目录} rev-parse --show-toplevel` 能返回仓库根) | 用户桌面 `MCNSkill项目/`(`{达人昵称}/` 直挂,无技能文件夹层) |
| 用户环境 | 技能**不在任何 git 检出内**(技能装在用户机器上,路径不限,如 `~/.workbuddy/skills/…`) | 用户桌面 `MCNSkill项目/`(与开发机同构) |
> **09-10 起 dsh 部署环境已废弃**(`~/.dsh/skills/` 与 `D:\dshworkspace` 均不存在)——不再有 dsh 分支与 `D:\dshworkspace\解析任务\` 产出路径。历史条目见文末「变更记录」。
桌面路径按运行系统自动解析:
@@ -21,7 +22,10 @@
| macOS | `~/Desktop/`(即 `/Users/{用户名}/Desktop/`) |
**AI 执行规则:**
1. 先做三环境两步判定:① 本机是否存在 `D:\AgentSkill`——不存在即**用户环境** → 桌面 `MCNSkill项目/`;② 本机存在时再看技能自身路径——含 `.dsh` 即 **dsh 部署环境** → **优先操作** `D:\dshworkspace\解析任务\`(「解析任务」子目录不存在则创建);不含 `.dsh` 即**开发机** → 桌面 `MCNSkill项目/`(开发机与用户环境同构:`MCNSkill项目/{达人昵称}/`)
1. 先做两环境判定(**仓库特征判定**):
- 判断技能是否处于**源技能 git 检出内**——执行 `git -C "{技能目录}" rev-parse --show-toplevel`,返回仓库根即 **① 开发机**;命令失败即 **② 用户环境**。无 git 环境时兜底:从技能目录向上逐级查找,任一级存在 `.git` 目录/文件即开发机,查到盘符根仍未命中即用户环境
- 开发机与用户环境产出**同构** → 桌面 `MCNSkill项目/{达人昵称}/`
- ⚠️ **不存在 dsh 分支**:dsh 侧已整套下线(09-10),不要再按 `.dsh` 路径段或 `D:\dshworkspace` 做任何分支判断
2. 再判断当前运行系统(Windows / macOS),拼接桌面路径
3. 目录不存在则自动创建
4. 产出结构:`{产出根目录}/{达人昵称}/…`(内部文件结构不变,仅根目录可变)
@@ -49,7 +53,7 @@
## 三、解析规则(优先级从高到低)
1. 本文件「用户自定义路径」记录 → 用自定义路径(最新一条生效)
2. 本文件默认路径 → 按三环境两步判定确定产出根目录(本机无 `D:\AgentSkill` → 桌面 `MCNSkill项目/`;有且技能路径含 `.dsh` → `D:\dshworkspace\解析任务\`;有且不含 `.dsh` → 桌面 `MCNSkill项目/`),再按系统拼接桌面路径
2. 本文件默认路径 → 按两环境判定确定产出根目录(在源技能 git 检出内 = 开发机 → 桌面 `MCNSkill项目/`;不在任何 git 检出内 = 用户环境 → 桌面 `MCNSkill项目/`。**两者同构**,均无 dsh 分支),再按系统拼接桌面路径
3. 路径含 `~` 时自动展开为用户主目录
4. 路径含 `{用户名}` 时替换为当前系统用户名
5. 目录不存在时自动创建
@@ -58,3 +62,5 @@
- 2026-08-26:产出路径改为三级优先级(借鉴脚本创作技能):① dsh 环境 + 主工作区 `D:\dshworkspace` 存在 → `D:\dshworkspace\解析任务\`;② dsh 环境但无主工作区(异机部署)→ 桌面 `DSHSkill项目/账号分析/`;③ 非 dsh → 桌面 `MCNSkill项目/账号分析/`。产出结构 `{达人昵称}/…` 不变。
- 2026-08-26(三环境重构 + 账号层直挂,对齐脚本创作技能定稿):环境判定由「dsh vs 非 dsh」改为**三环境两步判定**(①本机无 `D:\AgentSkill`=用户环境;②有且技能路径含 `.dsh`=dsh 部署环境;③有且不含 `.dsh`=开发机);`DSHSkill项目/` 根目录**整体废弃**(原「dsh 异机兜底」场景并入用户环境);非 dsh 产出根目录由 `MCNSkill项目/账号分析/` 改为 `MCNSkill项目/`——去掉技能文件夹层,`{达人昵称}/` 直挂(与脚本创作 `MCNSkill项目/{账号名}/` 同根);dsh 部署环境产出 `D:\dshworkspace\解析任务\` 不变。产出结构 `{达人昵称}/…` 不变。
- 2026-09-10(判定锚点重构 v2 · 仓库特征判定):环境判定锚点由「本机是否存在 `D:\AgentSkill`」改为**仓库特征判定**——第一步查技能路径是否含 `.dsh`(含 = dsh 部署环境),第二步用 `git -C {技能目录} rev-parse --show-toplevel` 判断技能是否处于源技能 git 检出内(在 = 开发机,不在 = 用户环境;无 git 时兜底为向上逐级查找 `.git`)。产出根目录三环境结果**完全不变**,仅判定依据改变。**根因**:仓库本地路径迁移导致写死盘符锚点失效 → 开发机被误判为「用户环境」。(用户确认:"改为仓库特征判定";对齐脚本创作技能定稿)
- 2026-09-10(环境模型精简 v3 · 三环境 → 两环境):**dsh 部署环境整体废弃**(用户确认:"第一步 技能路径含 .dsh 路径段 ② dsh 部署环境 没有这个环境了")。实测依据:`~/.dsh/skills/` 不存在、`~/.dsh/mcn-plugin.db` 不存在、`~/.dsh` 下仅剩 `profiles/`、`D:\dshworkspace` 整个目录不存在。故删除 `.dsh` 判定分支与 `D:\dshworkspace\解析任务\` 产出路径,判定简化为**一步**。剩余两环境产出本就同构(桌面 `MCNSkill项目/`),产出结果不变。(对齐脚本创作技能定稿)
@@ -29,6 +29,6 @@
| 数据 | 允许来源 | 禁止来源 |
|------|---------|---------|
| 账号身份标识(抖音号/sec_uid/抖音地址) | **浏览器抓取**(browser-harness 访问抖音主页) | 工作台库 `mcn-plugin.db`(hot_accounts 表 = MCP 内部账号库本地副本)、dsh 原库、MCP `list_hot_accounts`/`hot_account_detail` |
| 账号身份标识(抖音号/sec_uid/抖音地址) | **浏览器抓取**(browser-harness 访问抖音主页) | 工作台库 `mcn-plugin.db`(hot_accounts 表 = MCP 内部账号库本地副本)、MCP `list_hot_accounts`/`hot_account_detail` |
| 账号统计数据(粉丝/获赞/关注/作品/IP/简介) | **浏览器抓取**(网页实时值) | 同上(MCP 兜底仅限浏览器无法访问时,须注明来源) |
| 流程状态参考(是否已解析/已生成) | 工作台库/dsh 库**只读参考** | ——(只用于判断,不回填账号表) |
| 流程状态参考(是否已解析/已生成) | 工作台库**只读参考** | ——(只用于判断,不回填账号表) |
@@ -1,6 +1,6 @@
# 路径引用规范(相对路径铁律,P0)
> 适用:本技能树(主技能 + references/references-add + scripts + 全部 subskills 及嵌套 subskills)。技能会被部署到 **开发机 / dsh 环境 / 用户环境** 三种机器,**绝对路径随机器变化必然失效**,故技能内一切文件引用一律使用**相对路径**。
> 适用:本技能树(主技能 + references/references-add + scripts + 全部 subskills 及嵌套 subskills)。技能会被部署到 **开发机 / 用户环境** 两种机器,**绝对路径随机器变化必然失效**,故技能内一切文件引用一律使用**相对路径**。
## 一、铁律(3 条)
@@ -18,12 +18,12 @@
| `__file__` 动态推导 | `BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))` | 脚本定位技能根,跨环境可迁移 |
| 环境变量 | `os.getenv("REDFOX_API_KEY")`、`MCN_RANKING_DIR` | 值由外部注入 |
| URL | `https://github.com/...` | 外部资源 |
| 环境判别锚点 | 「本机是否存在 `D:\AgentSkill`」「技能路径是否含 `.dsh`」 | 仅作**是否存在/是否包含**的判定条件,**禁止**将其作为实际读写路径 |
| 动态产出目录占位 | 桌面 `MCNSkill项目/{账号名}/`、`D:\dshworkspace\创作任务\` | 由各技能 `references(-add)/路径配置.md` 运行时判定,允许出现于规则文档 |
| 环境判别锚点 | 「技能目录是否处于 git 检出内」 | 仅作**是否存在**的判定条件,**禁止**将其作为实际读写路径;锚点必须用**仓库特征**(git 检出),禁止用写死盘符 |
| 动态产出目录占位 | 桌面 `MCNSkill项目/{账号名}/` | 由各技能 `references(-add)/路径配置.md` 运行时判定,允许出现于规则文档 |
## 三、禁令项(这些绝对禁止)
- **Windows 盘符绝对路径**:`C:\Users\maidou\...`、`D:\AgentSkill\...`(含正反斜杠两种写法)——除「环境判别锚点/路径配置规则表」语境外的任何引用位置。
- **Windows 盘符绝对路径**:`C:\Users\maidou\...`、`D:\MyRepo\...`(含正反斜杠两种写法)——除「环境判别锚点/路径配置规则表」语境外的任何引用位置。
- **Unix 绝对路径**:`/Users/maidou/...`、`/home/...`、`/tmp/...`(shebang `#!/usr/bin/env python3` 除外)。
- **越级引用**:`../../..` 跳出技能根指向包外文件。
- **引用技能包外文档**:md 链接指向不存在于包内的本地文件(如仓库根的维护文档)——部署环境无此文件即断链。
@@ -34,7 +34,7 @@
| 场景 | 说明 |
|------|------|
| 仅开发机工具 | 不随技能分发的目录(如 `mcn-work-shop/`),内部可保留开发机路径 |
| 环境判别/路径配置文档 | `路径配置.md`、SKILL.md 环境判定节——锚点 `D:\AgentSkill`、`.dsh`、`D:\dshworkspace\...` 是**规则内容**不是引用,保留 |
| 环境判别/路径配置文档 | `路径配置.md`、SKILL.md 环境判定节——锚点 git 检出特征是**规则内容**不是引用,保留 |
| 示例/教学文本 | `C:/Videos/demo.mp4`、`C:/path/to/file` 等明确示例(`path/to`、`demo` 字样) |
| 历史日志 | `.workbuddy/memory/` 既有日志保留原样,不改写历史 |
| 第三方整包 | 整包拷贝的第三方技能(如 browser-harness/lieflat-charts/参考skills/)内部自带约定不重写;但**本技能对它们的引用**仍须相对路径 |
@@ -499,11 +499,11 @@ F4 输出 = 主题 + 导演阐述 + 完整提示词(代码块包裹)。多
### 二、过程产物目录(产出落盘位置)
> **⛔ 产出路径唯一权威:`references-add/路径配置.md`**。执行分镜创作前必须先读该文件;用户自定义路径优先。默认路径按运行环境判定(两步:第一步查本机 `D:\AgentSkill` 是否存在——**无 = ③用户环境**(技能无论装在哪、什么路径);有 → 第二步看技能自身路径——**在 `D:\AgentSkill\` 下 = ①开发机**、**含 `.dsh` = ②dsh 环境**):dsh 环境**优先操作主工作区** `D:\dshworkspace\创作任务\`(与脚本创作技能同根同构),无主工作区时并入用户环境;开发机/用户环境为**用户桌面 `MCNSkill项目/`**(**账号层直挂**:`{账号名}/提示词{编号}/`,与脚本创作「脚本{编号}」同构,Windows/macOS 自动适配)。按账号名/提示词编号建子目录(编号默认继承上游脚本编号)。
> **⛔ 产出路径唯一权威:`references-add/路径配置.md`**。执行分镜创作前必须先读该文件;用户自定义路径优先。默认路径按运行环境判定(一步 · **仓库特征判定**:看技能是否处于源技能 git 检出内(`git -C {技能目录} rev-parse --show-toplevel` 返回仓库根)——**在 = ①开发机**、**不在 = ②用户环境**(技能无论装在哪、什么路径)):开发机与用户环境**同构**,均为**用户桌面 `MCNSkill项目/`**(**账号层直挂**:`{账号名}/提示词{编号}/`,与脚本创作「脚本{编号}」同构,Windows/macOS 自动适配)。**09-10 起无 dsh 环境**(`~/.dsh/skills/` 与 `D:\dshworkspace` 均不存在),不再有 dsh 分支。按账号名/提示词编号建子目录(编号默认继承上游脚本编号)。
```
D:\dshworkspace\创作任务\ ← dsh 环境优先根目录(主工作区「创作任务」,与脚本创作同根;无主工作区时并入用户环境)
└── {账号名}/ ← 对应账号(从上游脚本创作上下文继承,来自 references-add/路径配置.md;开发机/用户环境为 桌面 MCNSkill项目/{账号名}/,账号层直挂)
桌面 MCNSkill项目/ ← 唯一产出根目录(开发机/用户环境同构,Windows/macOS 自动适配)
└── {账号名}/ ← 对应账号(从上游脚本创作上下文继承,来自 references-add/路径配置.md;账号层直挂)
└── 提示词{编号}/ ← 与「脚本{编号}」平级(编号默认继承上游脚本编号)
├── 00_剧本分析.md ← Step 1 剧本分析(5维度)
├── 01_需求分析与路径.md ← Step 2-3 需求分析+路径分流
@@ -528,13 +528,12 @@ D:\dshworkspace\创作任务\ ← dsh 环境优先根目录(主
### 部署环境检测
根据**两步判定模型**判断当前环境(第一步查本机 `D:\AgentSkill` 是否存在;有 → 第二步看技能自身路径):
根据**一步判定模型(仓库特征判定)**判断当前环境(看技能是否处于源技能 git 检出内):
| 环境 | 判定条件 | 增量机制 | 新增内容写入位置 |
|:---:|:---|:---|:---|
| **①开发机** | 本机有 `D:\AgentSkill` 且技能路径在 `D:\AgentSkill\` 下 | 不启用 references-add/ | 直接写入 `references/` 对应子目录 |
| **②dsh 部署环境** | 本机有 `D:\AgentSkill` 且技能路径含 `.dsh` | 启用 references-add/ | 写入 `references-add/` 对应子目录,不修改 `references/` |
| **③用户环境** | 本机无 `D:\AgentSkill`(技能无论装在哪、什么路径,如 `~/.workbuddy/skills/...`) | 启用 references-add/ | 写入 `references-add/` 对应子目录,不修改 `references/` |
| **①开发机** | 技能处于源技能 git 检出内(`git -C {技能目录} rev-parse --show-toplevel` 返回仓库根) | 不启用 references-add/ | 直接写入 `references/` 对应子目录 |
| **②用户环境** | 技能不在任何 git 检出内(技能无论装在哪、什么路径,如 `~/.workbuddy/skills/...`) | 启用 references-add/ | 写入 `references-add/` 对应子目录,不修改 `references/` |
### 冲突检测与修复
@@ -7,7 +7,7 @@
```
references-add/
├── README.md ← 本文件
├── 路径配置.md ← 产出路径唯一权威(dsh 环境优先 `D:\dshworkspace\创作任务\`、无主工作区时并入用户环境;开发机/用户环境 桌面 MCNSkill项目/{账号名}/提示词{编号}/ 账号层直挂,运行时必读)
├── 路径配置.md ← 产出路径唯一权威(开发机/用户环境同构:桌面 MCNSkill项目/{账号名}/提示词{编号}/ 账号层直挂。运行时必读)
├── 制作流程/ ← 新增/变更的制作流程文件
├── 接口调用/ ← 新增的 API 接口文档与平台规范
└── 知识库/ ← 新增知识(15 维度,与 references/ 结构对应)
@@ -38,13 +38,12 @@ references-add/
### 部署环境检测
根据**两步判定模型**判断当前环境(第一步查本机 `D:\AgentSkill` 是否存在;有 → 第二步看技能自身路径):
根据**一步判定模型(仓库特征判定)**判断当前环境(看技能是否处于源技能 git 检出内):
| 环境 | 判定条件 | 增量机制 | 新增内容写入位置 |
|:---:|:---|:---|:---|
| **①开发机** | 本机有 `D:\AgentSkill` 且技能路径在 `D:\AgentSkill\` 下 | 不启用 references-add/ | `references/` 对应子目录 |
| **②dsh 部署环境** | 本机有 `D:\AgentSkill` 且技能路径含 `.dsh` | 启用 references-add/(本目录) | `references-add/` 对应子目录 |
| **③用户环境** | 本机无 `D:\AgentSkill`(技能无论装在哪、什么路径) | 启用 references-add/(本目录) | `references-add/` 对应子目录 |
| **①开发机** | 技能处于源技能 git 检出内(`git -C {技能目录} rev-parse --show-toplevel` 返回仓库根) | 不启用 references-add/ | `references/` 对应子目录 |
| **②用户环境** | 技能不在任何 git 检出内(技能无论装在哪、什么路径) | 启用 references-add/(本目录) | `references-add/` 对应子目录 |
### 冲突检测与修复
@@ -81,7 +80,7 @@ references-add/
## 运行时加载规则
**仅非开发机环境生效**(②dsh 部署环境/③用户环境,即本机无 `D:\AgentSkill` 或技能路径含 `.dsh` 时)。同时加载 `references/` 和 `references-add/` 的内容,两者冲突时**以 references-add/ 为主**:
**仅非开发机环境生效**(②用户环境,即技能不在任何 git 检出内时)。同时加载 `references/` 和 `references-add/` 的内容,两者冲突时**以 references-add/ 为主**:
| 维度 | 加载规则 |
|:---|:---|
@@ -5,13 +5,12 @@
## 一、默认路径(用户未自定义时生效)
**先判定运行环境(两步:第一步查本机 `D:\AgentSkill` 是否存在——无 = 用户环境;有 → 第二步看技能自身路径),再按运行系统解析桌面路径:**
**先判定运行环境(一步 · 仓库特征判定:看技能是否处于源技能 git 检出内),再按运行系统解析桌面路径:**
| 判定步骤 | 条件 | 环境 | 默认产出根目录 |
| 判定 | 条件 | 环境 | 默认产出根目录 |
|:---|:---|:---|:---|
| 第一步 | 本机**无** `D:\AgentSkill`(源技能仓库目录,只存在于开发机) | ③ 用户环境(技能无论装在哪、什么路径) | 用户桌面 `MCNSkill项目/`(**账号层直挂**:`{账号名}/提示词{编号}/`,与脚本创作「脚本{编号}」同构) |
| 第二步 | 本机有 `D:\AgentSkill`,且技能路径在 `D:\AgentSkill\` 下 | ① 开发机(源技能) | 用户桌面 `MCNSkill项目/`(**账号层直挂**:`{账号名}/提示词{编号}/`,与脚本创作「脚本{编号}」同构) |
| 第二步 | 本机有 `D:\AgentSkill`,且技能路径含 `.dsh`(如 `C:\Users\{用户名}\.dsh\skills\storyboard-prompt`) | ② dsh 部署环境 | **优先** `D:\dshworkspace\创作任务\`(主工作区「创作任务」目录);无主工作区时并入用户环境 → 桌面 `MCNSkill项目/` |
| 一步判定 | 技能**处于源技能 git 检出内**(`git -C {技能目录} rev-parse --show-toplevel` 返回仓库根) | ① 开发机(源技能) | 用户桌面 `MCNSkill项目/`(**账号层直挂**:`{账号名}/提示词{编号}/`,与脚本创作「脚本{编号}」同构) |
| 一步判定 | 技能**不在任何 git 检出内**(技能无论装在哪、什么路径) | ② 用户环境 | 用户桌面 `MCNSkill项目/`(**账号层直挂**:`{账号名}/提示词{编号}/`,与脚本创作「脚本{编号}」同构) |
桌面路径按运行系统自动解析:
@@ -21,7 +20,8 @@
| macOS | `~/Desktop/`(即 `/Users/{用户名}/Desktop/`) |
**AI 执行规则:**
1. 先按两步判定运行环境——第一步查本机 `D:\AgentSkill` 是否存在(**无 = ③用户环境**,技能无论装在哪、什么路径);有 → 第二步看技能自身路径(**在 `D:\AgentSkill\` 下 = ①开发机**;**含 `.dsh` = ②dsh 环境**)。再按上表确定产出根目录(dsh 环境 → **优先操作** `D:\dshworkspace\创作任务\`,主工作区 `D:\dshworkspace` 存在即用(「创作任务」子目录不存在则创建);无主工作区时并入用户环境 → 桌面 `MCNSkill项目/`;开发机/用户环境 → 桌面 `MCNSkill项目/`,**账号层直挂**(`{账号名}/提示词{编号}/`,与脚本创作同构))
1. 先按一步判定运行环境(**仓库特征判定**)——看技能是否处于源技能 git 检出内(`git -C "{技能目录}" rev-parse --show-toplevel` 返回仓库根 = **①开发机**,命令失败 = **②用户环境**;无 git 环境时兜底为从技能目录向上逐级查找 `.git`)。再按上表确定产出根目录(开发机/用户环境**同构** → 桌面 `MCNSkill项目/`,**账号层直挂**(`{账号名}/提示词{编号}/`,与脚本创作同构))
- ⚠️ 禁止按 `.dsh` 路径段或 `D:\dshworkspace` 做任何分支判断(该部署环境已不存在)
2. 再判断当前运行系统(Windows / macOS),拼接桌面路径
3. 目录不存在则自动创建
4. 产出结构:`{产出根目录}/{账号名}/提示词{编号}/…`(内部文件结构不变,仅根目录可变)
@@ -31,9 +31,9 @@
**产出结构示例(6步流程产物):**
```
D:\dshworkspace\创作任务\ ← dsh 环境优先根目录(主工作区「创作任务」,与脚本创作同根;无主工作区时并入用户环境)
桌面 MCNSkill项目/ ← 唯一产出根目录(开发机 / 用户环境同构;Windows/macOS 自动适配)
└── {账号名}/ ← 对应账号(从上游脚本创作上下文继承,与脚本创作同口径)
└── 提示词{编号}/ ← 与「脚本{编号}」平级(编号默认继承上游脚本编号;开发机/用户环境为 桌面 MCNSkill项目/{账号名}/提示词{编号}/,账号层直挂)
└── 提示词{编号}/ ← 与「脚本{编号}」平级(编号默认继承上游脚本编号)
├── 00_剧本分析.md ← Step 1 剧本分析(5维度)
├── 01_需求分析与路径.md ← Step 2-3
├── 02_段1提示词.md ← Step 4(多段时 02/03/04… 递增)
@@ -58,7 +58,7 @@ D:\dshworkspace\创作任务\ ← dsh 环境优先根目录(主
## 三、解析规则(优先级从高到低)
1. 本文件「用户自定义路径」记录 → 用自定义路径(最新一条生效)
2. 本文件默认路径 → 先按两步判定运行环境(第一步查本机 `D:\AgentSkill` 是否存在:无 = ③用户环境;有 → 第二步看技能路径:在 `D:\AgentSkill\` 下 = ①开发机、含 `.dsh` = ②dsh 环境),再按上表确定产出根目录(dsh 环境 → 优先 `D:\dshworkspace\创作任务\`、无主工作区时并入用户环境 → 桌面 `MCNSkill项目/`;开发机/用户环境 → 桌面 `MCNSkill项目/`,**账号层直挂** `{账号名}/提示词{编号}/`),再按系统拼接桌面路径
2. 本文件默认路径 → 先按一步判定运行环境(看技能是否处于源技能 git 检出内:在 = ①开发机、不在 = ②用户环境),再按上表确定产出根目录(开发机/用户环境**同构** → 桌面 `MCNSkill项目/`,**账号层直挂** `{账号名}/提示词{编号}/`),再按系统拼接桌面路径
3. 路径含 `~` 时自动展开为用户主目录
4. 路径含 `{用户名}` 时替换为当前系统用户名
5. 目录不存在时自动创建
@@ -73,3 +73,5 @@ D:\dshworkspace\创作任务\ ← dsh 环境优先根目录(主
- 2026-08-26(第四次修订,与脚本创作两级规则对齐):dsh 部署环境改为**优先操作** `D:\dshworkspace\创作任务\`(主工作区「创作任务」目录,与脚本创作技能同根;用户指定:"dsh 环境下 分镜提示词也应该在 工作区 D:\dshworkspace\创作任务\{账号名}\提示词{编号}");主工作区不存在时兜底桌面 `DSHSkill项目/`。产出结构由 `{账号名}/{视频标题}/` 改为 `{账号名}/提示词{编号}/`——与脚本创作「脚本{编号}」平级,编号默认继承上游脚本编号,独立使用时按已有最大值递增。
- 2026-08-26(第五次修订,环境判定模型修正):判定锚点由「技能路径是否含 `.dsh`」单一判断改为**两步判定**——第一步查本机 `D:\AgentSkill` 是否存在(**无 = ③用户环境**,技能无论装在哪、什么路径);有 → 第二步看技能路径(**在 `D:\AgentSkill\` 下 = ①开发机**;**含 `.dsh` = ②dsh 环境**)。修复用户环境技能装在非 `.dsh` 路径(如 `~/.workbuddy/skills/`)时被误判为开发机、可能错误直接编辑 `references/` 的漏洞(用户指定:用户环境判定锚点是「本机没有 D:\AgentSkill」,与技能装在什么路径无关)。
- 2026-08-26(第六次修订,产出结构定稿):①开发机与③用户环境产出根目录由 `桌面 MCNSkill项目/分镜提示词/` 改为 `桌面 MCNSkill项目/`(**账号层直挂**:`{账号名}/提示词{编号}/`,去掉「分镜提示词」技能文件夹层,与脚本创作「脚本{编号}」同构;用户指定:"定稿产出结构:桌面\MCNSkill项目\{账号名}\{脚本文件夹}\(①开发机与③用户环境同构,账号层直挂);② dsh 部署环境不变 D:\dshworkspace\创作任务\{账号名}\脚本{编号}\")。dsh 环境产出不变。
- 2026-09-10(第七次修订,判定锚点重构 v2 · 仓库特征判定):环境判定锚点由「本机是否存在 `D:\AgentSkill`」改为**仓库特征判定**——第一步查技能路径是否含 `.dsh` 路径段(含 = dsh 部署环境),第二步用 `git -C {技能目录} rev-parse --show-toplevel` 判断技能是否处于源技能 git 检出内(在 = 开发机,不在 = 用户环境;无 git 时兜底为向上逐级查找 `.git`)。产出根目录三环境结果**完全不变**,仅判定依据改变。**根因**:仓库本地路径迁移导致写死盘符锚点失效 → 开发机被误判为「用户环境」,会重演第五次修订修复过的「误直接编辑 `references/`」漏洞。(用户确认:"改为仓库特征判定";与脚本创作技能同源定稿)
- 2026-09-10(第八次修订,环境模型精简 v3 · 三环境 → 两环境):**dsh 部署环境整体废弃**(用户确认:"第一步 技能路径含 .dsh 路径段 ② dsh 部署环境 没有这个环境了")。实测依据:`~/.dsh/skills/` 不存在、`~/.dsh/mcn-plugin.db` 不存在、`~/.dsh` 下仅剩 `profiles/`、`D:\dshworkspace` 整个目录不存在。故删除 `.dsh` 判定步骤与 `D:\dshworkspace\创作任务\` 产出路径,判定简化为**一步**。剩余两环境产出本就同构(桌面 `MCNSkill项目/{账号名}/提示词{编号}/`),产出结果不变。(与脚本创作技能同源定稿)
@@ -62,13 +62,12 @@ references/
### 部署环境检测
根据**两步判定模型**判断当前环境(第一步查本机 `D:\AgentSkill` 是否存在;有 → 第二步看技能自身路径):
根据**一步判定模型(仓库特征判定)**判断当前环境(看技能是否处于源技能 git 检出内):
| 环境 | 判定条件 | references/ 权限 | 新增内容写入位置 |
|:---:|:---|:---|:---|
| **①开发机** | 本机有 `D:\AgentSkill` 且技能路径在 `D:\AgentSkill\` 下 | 可直接编辑 | `references/` 对应子目录 |
| **②dsh 部署环境** | 本机有 `D:\AgentSkill` 且技能路径含 `.dsh` | 只读 | `references-add/` 对应子目录 |
| **③用户环境** | 本机无 `D:\AgentSkill`(技能无论装在哪、什么路径) | 只读 | `references-add/` 对应子目录 |
| **①开发机** | 技能处于源技能 git 检出内(`git -C {技能目录} rev-parse --show-toplevel` 返回仓库根) | 可直接编辑 | `references/` 对应子目录 |
| **②用户环境** | 技能不在任何 git 检出内(技能无论装在哪、什么路径) | 只读 | `references-add/` 对应子目录 |
### 冲突检测与运行时优先级
@@ -1,6 +1,6 @@
# 路径引用规范(相对路径铁律,P0)
> 适用:本技能树(主技能 + references/references-add + scripts + 全部 subskills 及嵌套 subskills)。技能会被部署到 **开发机 / dsh 环境 / 用户环境** 三种机器,**绝对路径随机器变化必然失效**,故技能内一切文件引用一律使用**相对路径**。
> 适用:本技能树(主技能 + references/references-add + scripts + 全部 subskills 及嵌套 subskills)。技能会被部署到 **开发机 / 用户环境** 两种机器,**绝对路径随机器变化必然失效**,故技能内一切文件引用一律使用**相对路径**。
## 一、铁律(3 条)
@@ -18,12 +18,12 @@
| `__file__` 动态推导 | `BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))` | 脚本定位技能根,跨环境可迁移 |
| 环境变量 | `os.getenv("REDFOX_API_KEY")`、`MCN_RANKING_DIR` | 值由外部注入 |
| URL | `https://github.com/...` | 外部资源 |
| 环境判别锚点 | 「本机是否存在 `D:\AgentSkill`」「技能路径是否含 `.dsh`」 | 仅作**是否存在/是否包含**的判定条件,**禁止**将其作为实际读写路径 |
| 动态产出目录占位 | 桌面 `MCNSkill项目/{账号名}/`、`D:\dshworkspace\创作任务\` | 由各技能 `references(-add)/路径配置.md` 运行时判定,允许出现于规则文档 |
| 环境判别锚点 | 「技能目录是否处于 git 检出内」 | 仅作**是否存在**的判定条件,**禁止**将其作为实际读写路径;锚点必须用**仓库特征**(git 检出),禁止用写死盘符 |
| 动态产出目录占位 | 桌面 `MCNSkill项目/{账号名}/` | 由各技能 `references(-add)/路径配置.md` 运行时判定,允许出现于规则文档 |
## 三、禁令项(这些绝对禁止)
- **Windows 盘符绝对路径**:`C:\Users\maidou\...`、`D:\AgentSkill\...`(含正反斜杠两种写法)——除「环境判别锚点/路径配置规则表」语境外的任何引用位置。
- **Windows 盘符绝对路径**:`C:\Users\maidou\...`、`D:\MyRepo\...`(含正反斜杠两种写法)——除「环境判别锚点/路径配置规则表」语境外的任何引用位置。
- **Unix 绝对路径**:`/Users/maidou/...`、`/home/...`、`/tmp/...`(shebang `#!/usr/bin/env python3` 除外)。
- **越级引用**:`../../..` 跳出技能根指向包外文件。
- **引用技能包外文档**:md 链接指向不存在于包内的本地文件(如仓库根的维护文档)——部署环境无此文件即断链。
@@ -34,7 +34,7 @@
| 场景 | 说明 |
|------|------|
| 仅开发机工具 | 不随技能分发的目录(如 `mcn-work-shop/`),内部可保留开发机路径 |
| 环境判别/路径配置文档 | `路径配置.md`、SKILL.md 环境判定节——锚点 `D:\AgentSkill`、`.dsh`、`D:\dshworkspace\...` 是**规则内容**不是引用,保留 |
| 环境判别/路径配置文档 | `路径配置.md`、SKILL.md 环境判定节——锚点 git 检出特征是**规则内容**不是引用,保留 |
| 示例/教学文本 | `C:/Videos/demo.mp4`、`C:/path/to/file` 等明确示例(`path/to`、`demo` 字样) |
| 历史日志 | `.workbuddy/memory/` 既有日志保留原样,不改写历史 |
| 第三方整包 | 整包拷贝的第三方技能(如 browser-harness/lieflat-charts/参考skills/)内部自带约定不重写;但**本技能对它们的引用**仍须相对路径 |
+16
View File
@@ -145,3 +145,19 @@ S1/S2 产物文件名改名(01_需求拆分/拆解 → 01_需求理解/02_需
1. **同一文件的多次修改必须串行**(一条消息一个 Edit),不同文件之间才可以并行;同文件批量替换宁可用一次 replace_all 也不要拆多个并行 Edit。
2. **「Successfully edited」≠ 已生效**:并行场景下报成功不代表最终文件含该改动,改完必须全量 grep 验证(本次靠 `需求拆分|需求拆解` 残留扫描抓出 3 处漏网)。
3. 多词替换建议顺序:先做含下划线/更长的精确词(如 01_需求拆分),再做短词行文(如 01需求拆分),或统一用带上下文的整行替换,降低互相覆盖概率。
---
## #017 环境判定锚点写死盘符 → 仓库迁移后判定整体失效(2026-09-10)
仓库本地路径由 `D:\AgentSkill\mcn-video-script` 迁移至 `D:\AI技能\mcn-short-video` 后,技能树内以「本机是否存在 `D:\AgentSkill`」为锚点的**三环境两步判定全部失效**——开发机被误判为「③ 用户环境」,后果是产出落桌面而非既定位置、且误启用 `references-add/` 增量分支。该锚点当时被规则文档列为「允许项(环境判别锚点)」,属于**设计上就写死**的路径,因此历次相对路径审计都没能查出——它不是"漏网违规",而是"合规但脆弱"。
同批还查出 8 处**真正的违规**:`mcn-work-shop/server.js` 的 `cwd` / `BROWSER_LOCK`(真实写入 automations 表与浏览器锁文件)、两个 `.cjs` 任务提交脚本 prompt 内的 DB 绝对路径、`V1.0/SKILL.md` 文档里的运行时 `cwds`、`Lite1.0/SKILL.md` 的外部技能依赖路径(且该段自 09-02 dou-analysis 并入 `subskills/` 后从未同步)。
**规避与铁律:**
1. **环境判定的锚点必须是"仓库特征",不能是盘符地标**:统一改用 ① 技能路径是否含 `.dsh` 路径段 → ② `git -C {技能目录} rev-parse --show-toplevel` 是否返回仓库根(无 git 时兜底向上找 `.git`)。仓库搬移到任何盘符/目录名都能自动正确判定。
2. **`.dsh` 必须先判**:`~/.dsh` 自身也是 git 仓库,若先查 `.git` 会把 dsh 环境误判为开发机。
3. **「允许项」不等于「不会坏」**:规则文件里被豁免的写法(锚点、占位路径)也需在仓库/环境迁移时**主动复核**——豁免的是"可以这么写",不是"永远不用改"。
4. **盘点类任务先确认扫描范围无遗漏**:首轮扫描脚本 `skipDirs` 里含 `references`,导致 24 处命中漏扫(53 → 77);扫完应交叉验证"跳过目录是否真的都该跳过"。
5. **下判断前先读规则原文**:首轮汇报把 37 处"规范明确豁免的锚点"与 8 处"真违规"混为一谈,笼统说成"30 个文件写死旧路径",判断说重了。凡结论涉及"违规/不合规",必须先读对应规范文件、按规范分类后再下结论。