Skill: cat-xierluo/article2book (version 1.0.0)

--- name: article2book homepage: https://github.com/cat-xierluo/legal-skills author: 杨卫薪律师（微信ywxlaw） version: "1.0.0" description: 基于现有文章、专栏、课程讲稿、逐字稿、访谈、课件、会议纪要、案例材料、PDF 文本、Word 文档和笔记等内容资产，判断它们最适合转化为书、小册子、课程、系列文章、实务手册或知识库，并输出精简策划意见；用户提到“把现有内容整理成书”“判断素材适合做书还是课程”“把文章/直播稿/课程稿/笔记重组成知识产品”等场景时，应使用本技能。 license: MIT --- # 现有内容资产再组织技能 **最后更新**：2026-05-17 ## 定位 `article2book` 用于把一个目录下已经存在的内容资产，整理成可出版、可交付或可持续维护的结构化成果。 本技能仍以“成书策划”为默认主路径，但不把所有素材强行导向一本书。它会先判断这批素材最适合成为： - 书稿 - 小册子 - 课程 - 系列文章 - 实务手册 - 知识库 - 暂不建议产品化的素材池 这里的“现有内容资产”包括但不限于： - 已发布文章、专栏稿、公众号稿件 - 课程讲稿、分享提纲、直播稿、演讲稿 - 视频教程配套文稿、字幕稿、逐字稿 - 访谈稿、问答记录、会议纪要 - 课件、案例材料、实务清单 - PDF 文本、Word 文档、Obsidian 笔记 - 卡片、方法论草稿、系列随笔 它的出发点不是“从空白开始写一本书”，而是： - 先盘点已有内容资产 - 再判断最合适的交付形态 - 然后筛掉低价值内容，收束母题、读者和结构 - 最后把已有素材重组为更适合出版或交付的内容产品 本技能的默认方法论是： - **Agent 通读优先** - **脚本索引辅助** - **先判断内容形态，再判断是否成书** - **先筛掉低价值内容，再做结构设计** - **默认单文件交付** - **确认后进入对应产物初稿** ## 典型输入 - 一个目录下混合放置的 Markdown 文章、专栏稿、公众号稿、课程讲稿、字幕稿、逐字稿、Obsidian 笔记 - 一批直播稿、访谈稿、课程稿或会议纪要，用户不确定适合做书、课程还是知识库 - 已写成很多长文，但主题散、重复多、表达角度不统一 - 同一作者围绕某条方法论持续写作和输出，但还没有沉淀成稳定内容产品 - 一个混合目录里同时有文章、课件、案例、手册草稿和零散笔记 **示例**： - `请读取 <素材目录>，判断这些文章能不能整理成一本书，并给出书稿结构方案。` - `我有一批直播逐字稿和配套文章，帮我判断适合做书、课程还是小册子。` - `这个混合目录能不能整理成一个知识产品？如果不适合成书，也请给出替代方案。` - `你先只输出一份策划意见，我确认后你再直接生成对应初稿。` ## 不适用场景 - 用户只是想汇总文件目录，不关心内容形态、读者定位或重组路径 - 用户已经有成熟目录，只需要续写某一章 - 用户希望完全从零构思一本书，而不是基于已有素材整理 - 文件夹里主要是图片、扫描件、录音、纯视频等材料，且尚未转为可读文本 - 用户要做的是排版、封面、营销文案或发行流程，而不是内容资产重组 ## 默认输出目录 默认在**源目录下**创建 `书稿策划输出/` 目录。即使最终建议不是成书，也继续使用该目录，避免为不同形态制造多套路径规则。 ## 工作模式 根据用户请求和素材复杂度选择工作模式： | 模式 | 适用场景 | 默认产物 | |------|----------|----------| | 快速盘点 | 用户只想先知道方向，或素材规模很大需要先判断值不值得深入 | 对话内给出简短判断，必要时生成 `书稿策划意见.md` 的简版 | | 标准策划 | 默认模式；用户要求判断能否成书、适合什么形态、怎么重组 | `书稿策划意见.md` | | 深度重构 | 用户确认策划意见后，或明确要求直接生成完整初稿 | `全书初稿.md` 或对应形态初稿 | 默认使用**标准策划**。只有在用户确认方向、明确要求生成初稿，或素材复杂到必须拆批处理时，才进入深度重构。 ## 交付形态判断树 先判断最佳内容形态，再判断是否进入成书路径： 1. **成书**：素材有稳定母题、明确读者、足够厚度和可持续扩写空间。 2. **小册子**：主题集中但厚度不足，更适合短平快交付。 3. **课程**：素材以讲授顺序、操作演示、练习任务或学习路径为主。 4. **系列文章**：观点有价值但主题尚未形成完整体系。 5. **实务手册**：素材以流程、清单、标准、案例和操作口径为主。 6. **知识库**：素材多主题并存，适合持续维护和检索，而不适合线性阅读。 7. **暂不建议产品化**：素材过散、重复、过时或缺少足够原创判断。 各形态的判断标准见 [references/content-productization-models.md](references/content-productization-models.md)。 ## 默认交付 本技能默认采用**两阶段**交付，并尽量减少用户需要阅读的文件数量。 ### 第一阶段：先出一份策划意见 默认只输出： | 文件 | 用途 | |------|------| | `书稿策划意见.md` | 唯一默认交付件。集中给出最佳内容形态、是否适合成书、主命题、目标读者、结构草案、删改动作、转化路径和下一步产物 | 只有在以下情况，才额外输出支撑文件： - 素材规模很大，需要保留通读证据 - 用户明确要求看细表 - 主题分叉明显，需要展示筛选依据 - 输入格式复杂，需要记录预处理状态 可选支撑文件包括： | 文件 | 用途 | |------|------| | `processed/agent_reading_notes.md` | Agent 分批通读后的内部阅读笔记 | | `processed/article_inventory.csv` | 脚本建立的基础清单索引 | | `processed/article_inventory.jsonl` | 结构化索引 | | `附录-排除与降权清单.md` | 仅在需要解释筛选依据时输出 | ### 第二阶段：确认后生成对应形态初稿 如果用户确认第一阶段意见无误，则直接进入内容重构，不再先产出大批中间文件。 默认输出取决于第一阶段判断： | 推荐形态 | 默认初稿 | |----------|----------| | 成书 | `全书初稿.md` | | 小册子 | `小册子初稿.md` | | 课程 | `课程大纲与讲稿初稿.md` | | 系列文章 | `系列文章规划与首批初稿.md` | | 实务手册 | `实务手册初稿.md` | | 知识库 | `知识库结构与核心条目.md` | 仅当内容过长、需要分章并行起草或用户明确要求拆分时，才输出： | 文件 | 用途 | |------|------| | `chapters/01-*.md ...` | 分章或分单元初稿 | | `全书结构说明.md` / `内容结构说明.md` | 记录章节边界、统一术语和合并规则 | 完整模板见 [references/output-template.md](references/output-template.md)。 ## 工作流程 ### Step 1：确认源目录、文件类型、工作模式与输出位置 - 确认用户给的是单一主题目录，还是多个主题混放目录 - 默认优先处理已文本化的素材，如 `.md`、`.markdown`、`.mdx`、`.txt`、`.srt`、`.vtt` - 对 `.docx`、`.pdf`，先作为“需预处理素材”纳入清单；能直接提取文本时再进入通读 - 对课程视频、录音、播客等非文本素材，先转写为逐字稿再纳入判断 - 对图片、附件、配图目录先排除，避免把配图误识别成正文内容 - 明确采用快速盘点、标准策划还是深度重构 - 明确输出落在源目录 `书稿策划输出/` 不同来源的处理方式见 [references/source-type-handling.md](references/source-type-handling.md)。 ### Step 2：先让 Agent 通读全部候选文本素材 这是本技能的主流程，不可跳过。 - 对能直接读取的文本素材，Agent 应尽量全部通读，而不是只看标题、预览或脚本统计结果 - 如果目录较大，可按批次通读，例如每批 10-20 份素材；每读完一批，立刻记录笔记 - 每份素材至少记录： - 一句话摘要 - 它主要在回答什么问题 - 它更适合作为主章节、案例、附录、课程单元、手册条目还是应移出 - 是否存在强时效性、重复表达、深度不足、口语化过强或格式预处理问题 通读记录建议沉淀到： - `processed/agent_reading_notes.md` 但该文件默认视为内部工作底稿，不应自动作为用户第一阅读入口。 更细的通读方式见 [references/agent-reading-protocol.md](references/agent-reading-protocol.md)。 ### Step 3：通读后先做“保留 / 降权 / 排除”筛选 在开始主题聚类之前，先把低价值内容筛掉，不要把所有素材都硬塞进最终产物。 优先排除或降权以下内容： - **老而浅**：时间较早，且只停留在信息罗列、工具介绍、表层观点，没有形成今天仍有价值的判断框架 - **过时失效**：高度依赖某次产品更新、某个版本功能、某个热点事件，且难以改写为常青表达 - **重复但更弱**：与另一份素材表达的是同一判断，但论证、案例、结构和表达都明显更弱 - **只有热闹没有母题**：看起来吸睛，但与主线关联很弱 - **内容密度过低**：篇幅不短，但信息增量很少，难以支撑章节、单元或条目 筛选时要注意： - **老内容不等于低质量** - **短内容不等于低质量** - **排除要说明理由** 筛选标准见 [references/content-screening-rubric.md](references/content-screening-rubric.md)。 ### Step 4：在筛选基础上建立素材清单，脚本只做辅助 如需补路径、查漏或生成基础索引，可运行： ```bash python3 scripts/build_article_inventory.py "<源目录>" --output-dir "<输出目录>/processed" ``` 但要注意： - 该脚本只用于补路径、查漏、做基础索引和标记需预处理素材 - 不得仅凭脚本输出就直接做主题判断 - `.docx`、`.pdf` 在脚本中只标记为需预处理，不直接解析正文 - 如果 Agent 通读记录与脚本字段冲突，以通读理解为准，再人工校正 ### Step 5：抽取主题和内容形态，而不是只看标题 读取清单后，至少要覆盖： - 高密度代表素材 - 看似相近但论点不同的素材 - 可能属于边缘主题的素材 - 标题像工具测评、但正文承载方法论的素材 - 口语化很强、但内里已经有章节、课程单元或手册条目雏形的逐字稿 - 看似零散但可沉淀为知识库条目的笔记或会议纪要 不要只根据标题聚类。正文中反复出现的“问题意识、判断口径、方法论词汇、操作步骤、案例结构、叙事张力”，通常比标题更能决定最终形态。 ### Step 6：识别“母题”与“最合适的内容产品” 先把素材按主题簇归类，再判断它们能否收束成稳定内容产品。 重点识别： - 作者反复回答的是哪一个核心问题 - 作者与同类写作相比真正有区分度的视角是什么 - 素材更适合线性阅读、系统学习、快速查阅，还是持续维护 - 这些内容更像“同一本书的不同章节”，还是“多个内容产品的种子” 如果目录里同时存在两条以上都足够强的主线，不要硬拼成一本书。应明确提出： - 方案 A：聚焦为一本书 - 方案 B：拆成两本书 / 一本书 + 一套课程 / 手册 + 知识库 - 方案 C：暂不成书，先做系列文章或素材库收束 ### Step 7：评估成书可行性，并给出替代形态建议 按 [references/book-viability-rubric.md](references/book-viability-rubric.md) 逐项判断： - 主题集中度 - 核心命题清晰度 - 目标读者清晰度 - 内容厚度与互补性 - 观点区分度 - 时效风险 - 可持续扩写空间 输出时必须同时明确： 1. **最佳内容形态**：成书 / 小册子 / 课程 / 系列文章 / 实务手册 / 知识库 / 暂不建议产品化 2. **成书结论**：可以直接进入书稿策划 / 可以成书但需收束重写 / 暂不建议成书 3. **替代路径**：如果不成书，最适合转向什么产物，以及为什么 ### Step 8：收束成一份 `书稿策划意见.md` 默认不要把分析过程拆成很多用户可见文件。 应把以下内容集中写进一份 `书稿策划意见.md`： - 最佳内容形态 - 是否值得成书 - 不建议走的形态 - 推荐主命题 - 目标读者与差异化 - 推荐结构草案 - 哪些内容保留、哪些压缩、哪些删除 - 保留 / 合并 / 排除的核心原则 - 转化路径与下一步产物 - 如果用户确认推进，第二阶段将如何直接生成对应初稿 只有当用户明确要求，或素材规模特别大、争议特别多时，才把筛选清单、总表和阅读笔记额外显性输出。 ### Step 9：如用户确认，直接进入第二阶段生成初稿 如果用户对 `书稿策划意见.md` 表示认可，则直接推进，不必再停留在更多策划文件上。 默认做法： - 先按意见中的推荐结构建立章节、单元或条目骨架 - 以现有素材为底稿做合并、删改、重写与补写 - 默认输出与推荐形态对应的单文件初稿 当素材很多时，可以： - 用 subagents 按批次通读素材 - 用 subagents 分章、分单元或分条目起草初稿 - 但主 Agent 必须负责： - 统一主命题与读者对象 - 统一术语和表达口径 - 去除重复论证 - 做最后的结构收束与总稿合并 ### Step 10：只有在必要时才回退到细分文件 以下情况才建议回退到多文件包： - 用户明确要求逐份核对素材去向 - 需要向第三方展示筛选依据 - 主题分叉严重，需要比较多个内容形态方案 - 第二阶段已经启动，需要保留更细的编辑台账 无论输出是一份还是多份，内部都仍应完成以下判断： - `直接保留` - `局部吸收` - `合并重写` - `拆分改写` - `仅作案例/附录/练习/条目` - `移出本产物` ## 判断原则 1. **内容形态优先于成书冲动**：不是所有素材都该写成书，先判断最适合用户目标和素材状态的形态。 2. **母题优先于素材数量**：素材多不代表能成书，关键看是否围绕同一问题持续推进。 3. **区分度优先于覆盖面**：一本书或课程最重要的是独特的判断框架，不是把所有话题都讲一遍。 4. **读者问题优先于作者时间线**：结构应按读者理解顺序组织，而不是按素材产生顺序排列。 5. **常青内容优先于时评热度**：遇到强时效内容，要评估其能否改写成长期有效的案例或论证。 6. **证据可回溯**：主题判断、结构映射和删改建议都应尽量回指原文件路径。 7. **允许得出“不适合成一本书”**：如果内容天然分叉，不要强行整合。 8. **全文阅读优先于脚本结论**：最终判断必须建立在 Agent 已覆盖全部可读候选文本素材的前提上。 9. **先减法，再结构化**：通读后先剔除低价值内容，再做结构设计，避免成品被低质量旧内容拖垮。 10. **默认减少用户阅读负担**：能用一份主意见讲清楚的，不拆成 6-8 份用户文件。 ## 输出质量要求 - 结论必须明确，不要只做模糊描述 - 必须同时说明“最佳内容形态”和“是否适合成书” - 每个重大判断尽量附对应素材或主题簇依据 - 区分“内容已有”与“必须补写” - 区分“适合出版成书”与“更适合课程、专栏、手册、知识库” - 对缺失信息标注 `未提及 / 待补充 / 需作者确认` ## 协作工具 | 工具 | 用途 | |------|------| | Agent / 文件读取能力 | 主流程：分批通读全部可读素材，并形成阅读笔记 | | Subagent / 并行 Agent | 素材量大时并行通读、分章/分单元起草，但最终判断与总稿统一必须回到主 Agent | | `python3` | 可选辅助：运行 `scripts/build_article_inventory.py` 建立索引、补路径、查漏、标记需预处理素材 | | Markdown 输出能力 | 第一阶段生成单文件 `书稿策划意见.md`，第二阶段生成对应形态初稿 | ## 参考文件 | 文件 | 说明 | |------|------| | `references/agent-reading-protocol.md` | Agent 通读素材、分批记笔记和形成初步判断的建议流程 | | `references/content-screening-rubric.md` | 通读后判断“保留 / 降权 / 排除”的筛选标准 | | `references/content-productization-models.md` | 书、小册子、课程、专栏、手册、知识库等内容形态的判断标准 | | `references/source-type-handling.md` | 不同来源素材的处理方式和预处理规则 | | `references/output-template.md` | 默认输出模板 | | `references/book-viability-rubric.md` | 成书可行性评估维度与替代形态建议 | | `scripts/build_article_inventory.py` | 素材目录扫描与索引脚本（可选辅助） | ## 依赖 ### 系统依赖 | 依赖 | 安装方式 | |------|----------| | `python3` | macOS: 系统自带或 `brew install python`<br>Linux: `sudo apt-get install python3` | ### Python 包 | 包名 | 用途 | 安装命令 | |------|------|----------| | 无额外第三方依赖 | 脚本仅使用 Python 标准库；脚本为可选辅助 | 无需安装 | ## 注意事项 1. 如果目录中混有大量图片、配图目录或自动生成素材，先排除再分析。 2. 如果文章横跨两个强主题，例如“法律 AI 方法论”和“通用 AI 哲学随笔”，应优先考虑拆书或拆成书 + 课程 / 知识库。 3. 如果文章标题很像资讯或测评，但正文承载了稳定的方法论，应按方法论价值而不是标题风格判断去向。 4. 如果用户已经有明确出版方向，本技能应服务于收束和整合；如果用户还没有方向，本技能应先做形态判断而不是急于起目录。 5. 默认不要向用户交付太多中间文件。能用一份 `书稿策划意见.md` 讲清楚的，就不要拆成 6-8 份结果。 6. 第二阶段默认直接生成对应形态初稿，不再让用户先阅读大量过渡性文件。

path: CHANGELOG.md
content: # 变更日志

## [1.0.0] - 2026-05-17

### 新增

- 将 `article2book` 正式发布到 `skills/` 目录，作为公开可安装技能
- 已有内容资产再组织能力：成书、小册子、课程、系列文章、实务手册、知识库和暂不建议产品化七类形态判断
- 快速盘点、标准策划、深度重构三档工作模式，默认使用标准策划
- `references/content-productization-models.md`，说明不同内容产品形态的适用条件、典型结构和不适合信号
- `references/source-type-handling.md`，说明长文、短笔记、逐字稿、访谈稿、课件、会议纪要、案例材料、`.docx`、`.pdf` 和混合目录的处理规则

### 改进

- 默认交付收束为单一 `书稿策划意见.md`，确认后直出 `全书初稿.md`
- 两阶段工作流：先出策划意见，确认后生成全书初稿
- 成书可行性量表，成书分数不足时必须给出替代形态建议
- `书稿策划意见.md` 模板包含最佳内容形态、不建议形态、转化路径和下一步产物


path: DECISIONS.md
content: # 技术决策记录

本文档记录 `article2book` 技能设计过程中的关键决策与工作日志。

## [DEC-001] - 2026-04-02 - 输出重点放在“成书判断 + 结构方案”，而不是文章摘要

### 背景

用户的真实需求不是简单汇总文章，而是判断一个既有文章目录能否被提炼为可出版的书籍项目，并给出结构化整合方案。

### 选项

1. **做成文章总结器**
   - 优点：实现简单
   - 缺点：无法回答“能否成书”和“怎么重组”的关键问题

2. **做成书稿策划器**
   - 优点：直接回应出版判断、目录结构、读者定位和删改建议
   - 缺点：需要更强的评估标准与输出模板

### 决策

选择 **选项 2：做成书稿策划器**。

### 理由

1. 用户已经有相当多现成内容，真正缺的是“收束”和“结构化”
2. 成书不是摘要工作，而是主题判断、读者定位和章节重组工作
3. 这样设计更符合文章资产再利用的真实场景

### 影响

- 标准输出改为书稿策划包，而不是摘要合集
- 增加成书可行性评估和目录映射两个核心文件

## [DEC-002] - 2026-04-02 - 采用“源目录优先，archive 回退”的输出策略

### 背景

用户希望技能对任意文章目录都可复用。如果把输出路径写死到某个固定目录，后续跨设备、跨目录使用会非常别扭。

### 选项

1. **固定输出到某个个人路径**
   - 优点：单机使用方便
   - 缺点：不可移植

2. **要求用户每次都手动指定输出路径**
   - 优点：灵活
   - 缺点：交互成本高

3. **源目录优先，archive 回退**
   - 优点：成书策划结果与原文章目录相邻，同时兼容不可写目录
   - 缺点：需要清楚说明双路径规则

### 决策

选择 **选项 3：源目录优先，archive 回退**。

### 理由

1. 书稿整理结果最好贴着原文章资产存放，方便反复修订
2. 回退策略能兼容只读目录和统一归档需求
3. 该方式比硬编码个人路径更利于复用

### 影响

- `SKILL.md` 中明确输出目录规则
- 新增 `archive/.gitkeep`

## [DEC-003] - 2026-04-02 - 增加标准库扫描脚本，先建立素材清单再做判断

### 背景

如果直接阅读大量素材全文再人工整理，成本高且容易漏项。先建立素材清单，有助于识别主题分布、规模和边缘内容。

### 选项

1. **只靠人工读取**
   - 优点：灵活
   - 缺点：面对几十篇文章时容易漏读、漏记

2. **增加轻量扫描脚本**
   - 优点：先把候选文章、标题、预览、规模统一索引出来
   - 缺点：需要维护一个小脚本

### 决策

选择 **选项 2：增加轻量扫描脚本**。

### 理由

1. 先盘点素材资产，再做主题判断，更符合内容策划流程
2. 标准库脚本没有额外依赖，适合本地快速运行
3. 结构化索引可作为后续章节映射和删改判断的基础

### 影响

- 新增 `scripts/build_article_inventory.py`
- 标准输出中加入 `processed/article_inventory.csv/jsonl`

## [DEC-004] - 2026-04-02 - 技能以“现有素材重组”为主，不以“从零写书”为主

### 背景

用户进一步明确，这个技能的目标并不是从零生成一本书，而是基于已经存在的文章、视频教程、课程讲稿等素材，把内容重组为书籍形式。

### 选项

1. **保留“文章整理成书”的窄定义**
   - 优点：边界更简单
   - 缺点：无法覆盖视频教程、讲稿、逐字稿等真实来源

2. **扩展为“现有内容素材结构化成书”**
   - 优点：更贴近作者已有内容资产的真实形态
   - 缺点：需要明确非文本素材需先转写

### 决策

选择 **选项 2：扩展为“现有内容素材结构化成书”**。

### 理由

1. 很多可出版内容的起点并不是文章，而是课程、视频、分享或逐字稿
2. 真正的价值是“把已有表达重新编排为书籍结构”，不是“凭空产书”
3. 这样定义后，技能更适合作为内容资产再开发工具

### 影响

- `SKILL.md` 明确加入课程讲稿、视频教程逐字稿、演讲提纲等典型输入
- 标准输出改称“候选素材总表”
- 扫描脚本增加 `.srt`、`.vtt` 支持，并新增 `source_type` 字段

## [DEC-005] - 2026-04-02 - 技能命名改为 `article2book`

### 背景

用户希望把技能名从 `article-to-book` 改成 `article2book`，以便与自己的命名习惯保持一致。

### 选项

1. **保留 `article-to-book`**
   - 优点：语义直观
   - 缺点：不符合用户当前希望使用的命名

2. **改为 `article2book`**
   - 优点：更短，更贴近用户指定名称
   - 缺点：需要同步修改目录名和文档引用

### 决策

选择 **选项 2：改为 `article2book`**。

### 理由

1. 技能目录名应与技能名保持一致
2. 用户已经明确指定新的命名方式
3. 当前技能尚处于早期阶段，改名成本很低

### 影响

- `SKILL.md` frontmatter 的 `name` 改为 `article2book`
- 技能目录由 `article-to-book/` 改为 `article2book/`
- `CHANGELOG.md`、`DECISIONS.md`、`TASKS.md` 中的名称引用同步更新

## [DEC-006] - 2026-04-02 - Agent 通读优先，脚本仅作辅助索引

### 背景

用户明确指出，成书判断最重要的是 Agent 对全文内容的阅读理解，而不是依赖脚本先扫目录。脚本可以做索引，但不应该主导主题判断。

### 选项

1. **保留“脚本先扫，再人工抽读”的流程**
   - 优点：更快
   - 缺点：容易把主题判断建立在不充分阅读上

2. **改为“Agent 通读优先，脚本仅作辅助”**
   - 优点：更贴近真正的阅读理解过程
   - 缺点：耗时更高，需要明确批次阅读方法

### 决策

选择 **选项 2：Agent 通读优先，脚本仅作辅助**。

### 理由

1. 成书判断依赖的是母题、论证、重复度和章节潜力，这些都需要全文理解
2. 文件名、摘要和统计字段只能帮助查漏，不能替代阅读
3. 对课程讲稿和逐字稿这类口语素材，尤其需要 Agent 自己判断其可书稿化程度

### 影响

- `SKILL.md` 的主流程改为先通读全部候选文本素材
- 新增 `references/agent-reading-protocol.md`
- 输出模板增加 `processed/agent_reading_notes.md`
- `scripts/build_article_inventory.py` 降级为可选辅助工具

## [DEC-007] - 2026-04-02 - 通读后先做低质量内容排除

### 背景

用户指出，在 Agent 通读完素材后，还应先筛掉一些老旧、浅层、低质量或明显较弱的内容，否则后续目录设计会被噪音拖累。

### 选项

1. **把全部素材都纳入后续聚类**
   - 优点：不容易漏材料
   - 缺点：低质量旧内容会稀释主线，降低成书质量

2. **通读后先做“保留 / 降权 / 排除”筛选**
   - 优点：先做减法，再做结构化，更接近真实编辑流程
   - 缺点：需要明确筛选标准，避免误删有价值旧文

### 决策

选择 **选项 2：通读后先做“保留 / 降权 / 排除”筛选**。

### 理由

1. 一本书不需要收进所有旧内容，而是要留下最能支撑母题的内容
2. 老内容不是问题，老而浅、过时失效、重复且更弱才是问题
3. 先筛掉噪音，后续主题聚类和目录设计会更清晰

### 影响

- `SKILL.md` 新增“通读后先筛选”的明确步骤
- 新增 `references/content-screening-rubric.md`
- 输出模板增加 `00A-排除与降权清单.md`

## [DEC-008] - 2026-04-02 - 默认输出改为单一“书稿策划意见”

### 背景

在真实目录上跑完第一版后，发现虽然多文件分析包足够完整，但用户在首次判断阶段并不需要同时阅读这么多文件。过多拆分文件会放大认知负担，反而削弱“先确认方向”的效率。

### 选项

1. **继续默认输出多文件分析包**
   - 优点：结构完整，可追溯性强
   - 缺点：文件过多，首次阅读成本高

2. **默认只输出一份主意见，支撑文件按需生成**
   - 优点：更贴近真实决策路径，先看结论，再决定是否展开细节
   - 缺点：需要在技能中额外说明哪些文件属于可选支撑

### 决策

选择 **选项 2：默认只输出一份 `书稿策划意见.md`，支撑文件按需生成**。

### 理由

1. 用户在第一阶段真正需要的是一份可判断、可修改、可确认的主意见
2. 素材总表、筛选清单和阅读笔记更适合作为后备依据，而不是默认阅读入口
3. 这样更利于把 skill 接到第二阶段的直接写书动作上

### 影响

- `SKILL.md` 的标准输出调整为“单文件轻交付”
- `references/output-template.md` 改为以 `书稿策划意见.md` 为默认模板
- `processed/agent_reading_notes.md`、素材总表和筛选清单降级为可选支撑文件

## [DEC-009] - 2026-04-02 - 引入“先策划，后直出全书”的两阶段流程

### 背景

用户明确希望先拿到一份整体意见，确认方向后不要再经历冗长中间步骤，而是直接进入全书重构，尽量一步到位地得到完整书稿。

### 选项

1. **技能只负责策划，不负责后续书稿重构**
   - 优点：边界单纯
   - 缺点：用户还要再发起第二套流程，衔接成本高

2. **技能内置两阶段流程：先策划，确认后直接出全书初稿**
   - 优点：更贴近真实使用路径，也更利于复用通读结果
   - 缺点：需要说明阶段切换条件和统稿责任

### 决策

选择 **选项 2：技能内置两阶段流程**。

### 理由

1. 用户需求不是“分析完就停”，而是“分析后立刻可进入重构”
2. 通读与筛选的结果若不继续用于写书，很多中间理解会被浪费
3. 两阶段放在同一 skill 中，更容易统一母题、目录、术语和文风

### 影响

- `SKILL.md` 新增第二阶段：确认后输出 `全书初稿.md`
- `references/output-template.md` 新增 `全书初稿.md` 模板说明
- 允许用 subagent 分章并行起草，但由主 Agent 负责最终统稿

## [DEC-010] - 2026-04-02 - 默认不外显内部分析痕迹

### 背景

在第一版真实交付中，虽然已经把默认输出压缩为单份意见，但筛选细表、阅读笔记和过程性文件仍然容易被继续显性输出，用户首次判断时仍会承受不必要的阅读负担。

### 选项

1. **继续保留过程性文件作为常规可见输出**
   - 优点：可追溯性强
   - 缺点：容易把内部工作痕迹误变成用户默认阅读入口

2. **把过程性文件进一步内化，只在必要时显性输出**
   - 优点：更符合“先看意见、再决定是否展开”的真实使用路径
   - 缺点：需要在主意见中压缩保留足够的筛选依据

### 决策

选择 **选项 2：把过程性文件进一步内化**。

### 理由

1. 用户真正要先判断的是方向，不是阅读完整分析过程
2. 筛选原则和删改动作可以压缩进主意见，不必默认拆成单独文件
3. 这样更利于从第一阶段无缝衔接到第二阶段的整书重构

### 影响

- `SKILL.md` 新增“内部痕迹不默认外显”的判断原则
- `references/output-template.md` 要求把“保留 / 合并 / 排除原则”默认写入 `书稿策划意见.md`
- `附录-排除与降权清单.md` 明确为非默认产物

## [DEC-011] - 2026-04-03 - frontmatter description 收缩为“触发条件 + 核心能力”

### 背景

用户指出 `article2book` 的 `description` 过长，不符合成熟已发布 skill 的写法。对比其他稳定 skill 后，可以看到 `description` 更适合承担“何时触发、能做什么”的职责，而不是重复完整工作流。

### 选项

1. **继续保留长 description**
   - 优点：信息完整
   - 缺点：frontmatter 负担过重，触发信息与执行细节混在一起

2. **改为短 description，只保留触发条件和核心能力**
   - 优点：更符合成熟 skill 的常见写法，也更利于快速理解
   - 缺点：需要把细节继续留在 `SKILL.md` 正文中承载

### 决策

选择 **选项 2：改为短 description**。

### 理由

1. `description` 的主要任务是帮助识别使用时机，而不是承载完整规范
2. 已有的执行细节、边界和工作流在 `SKILL.md` 正文里已经足够完整
3. 收短后更接近成熟 skill 的 frontmatter 风格

### 影响

- `SKILL.md` 的 `description` 调整为“现有素材成书评估 + 书稿策划意见 + 确认后直出全书初稿”的简洁表达
- 版本号更新为 `0.2.2`

## [DEC-012] - 2026-04-03 - 示例与验证记录不保留用户本地绝对路径

### 背景

用户指出，skill 中的示例不应暴露其本地目录路径。该问题不仅出现在 `SKILL.md` 的示例请求里，也会连带影响变更记录中的真实验证表述。

### 选项

1. **保留真实绝对路径**
   - 优点：记录精确
   - 缺点：会把用户本地目录信息固化在技能文档中

2. **改为通用占位或泛化表述**
   - 优点：保留文档可读性和验证事实，同时避免泄露本地路径
   - 缺点：失去少量环境细节

### 决策

选择 **选项 2：改为通用占位或泛化表述**。

### 理由

1. 示例的任务是传达用法，不需要绑定某个真实机器路径
2. 技能级文档应尽量可复用，而不是带着单个用户环境痕迹
3. 验证事实可以保留，但不需要暴露验证时使用的具体本地目录

### 影响

- `SKILL.md` 示例中的素材目录改为 `<素材目录>`
- `CHANGELOG.md` 与 `DECISIONS.md` 中涉及真实验证路径的表述改为泛化描述

## [DEC-013] - 2026-04-03 - 取消 `archive` 回退，永远默认输出到源目录

### 背景

用户明确要求 `article2book` 不再使用技能目录内的 `archive/` 作为回退输出位置，而是始终把结果写回素材所在目录。

### 选项

1. **保留“源目录优先，archive 回退”**
   - 优点：兼容不可写目录和集中存放
   - 缺点：会在 skill 目录内再引入一层输出位置，增加理解负担

2. **永远默认输出到源目录**
   - 优点：结果始终贴着原素材，路径规则简单直接
   - 缺点：若源目录不可写，需要在实际运行时再显式处理异常或由用户指定目录

### 决策

选择 **选项 2：永远默认输出到源目录**。

### 理由

1. 用户希望输出位置稳定且可预期，不再引入额外回退目录
2. `书稿策划输出/` 贴着原素材目录，更符合实际整理和反复修改的工作流
3. `archive/` 会让 skill 自己多出一套中间存放逻辑，当前收益不高

### 影响

- `SKILL.md` 的默认输出目录改为只写“源目录下的 `书稿策划输出/`”
- `scripts/build_article_inventory.py` 不再把 `archive` 视为技能默认生成目录
- 删除技能目录中的 `archive/.gitkeep`

## [DEC-014] - 2026-05-17 - 从成书策划升级为内容资产再组织

### 背景

用户希望 `article2book` 写得更有泛化性。现有版本已经能处理文章、讲稿和逐字稿的成书策划，但默认判断仍以“能不能成一本书”为中心。真实内容资产整理场景中，很多素材更适合转化为课程、小册子、系列文章、实务手册或知识库。

### 选项

1. **继续强化成书路径**
   - 优点：边界清晰，延续既有定位
   - 缺点：容易把不适合成书的素材也强行导向书稿

2. **升级为内容资产再组织，保留成书作为默认主路径**
   - 优点：既保留已有能力，又能覆盖课程、手册、知识库等更自然的转化结果
   - 缺点：需要新增形态判断、素材类型处理和输出模板字段

### 决策

选择 **选项 2：升级为内容资产再组织，保留成书作为默认主路径**。

### 理由

1. 用户的核心需求是把已有内容资产变成更有价值的结构化成果，不一定总是一本书
2. 先判断最佳内容形态，可以减少错误成书、硬拼目录和过度写作
3. 保留 `article2book` 名称与两阶段流程，能兼容既有触发习惯和历史文档

### 影响

- `SKILL.md` 的定位扩展为“内容资产再组织”
- 新增内容形态判断树和快速盘点 / 标准策划 / 深度重构三档模式
- 新增 `references/content-productization-models.md` 和 `references/source-type-handling.md`
- `references/output-template.md` 增加最佳内容形态、不建议形态、转化路径和下一步产物
- `references/book-viability-rubric.md` 增加替代形态建议
- `scripts/build_article_inventory.py` 标记 `.docx`、`.pdf` 为需预处理素材

## [DEC-015] - 2026-05-17 - 正式发布到项目 skills 目录

### 背景

`article2book` 已在私有目录完成内容资产再组织能力升级，用户要求将该技能正式发布到本项目的 `skills/` 目录，并同步 README 文档。

### 选项

1. **继续保留在 `private-skills/`**
   - 优点：可继续内部迭代
   - 缺点：不能作为项目正式技能被安装和发现

2. **复制到 `skills/article2book/` 并升级为正式版本**
   - 优点：符合项目正式发布目录规范，可被 README 和 Marketplace 发现
   - 缺点：需要同步维护公开版本号和项目级入口

### 决策

选择 **选项 2：复制到 `skills/article2book/` 并升级为正式版本**。

### 理由

1. 用户已明确要求正式发布
2. 技能已具备完整的 `SKILL.md`、参考文件、脚本、许可证和技能级文档
3. 正式目录技能应使用 `1.x.x` 版本号，因此公开版本从 `1.0.0` 开始

### 影响

- `article2book` 进入项目正式 `skills/` 目录
- `SKILL.md` 版本号升级为 `1.0.0`
- `CHANGELOG.md` 保留 0.x 私有开发历史，并新增 1.0.0 正式发布记录
- 项目 README 与 Marketplace 清单需要同步添加该技能

## 工作日志

### 2026-05-17 16:51 (Codex)

- **目标**：将 `article2book` 从私有技能发布为项目正式技能
- **操作**：
  - 将私有目录当前版本复制到 `skills/article2book/`
  - 将公开版本号升级为 `1.0.0`
  - 新增正式发布变更记录和决策记录
  - 同步项目 README 与 Marketplace 清单
- **结果**：`article2book` 已具备正式发布目录、公开版本号和项目入口
- **下一步**：在更多真实内容资产目录中回归验证，并补充匿名化样例

### 2026-05-17 16:38 (Codex)

- **目标**：按用户确认的计划，将 `article2book` 泛化为内容资产再组织技能
- **操作**：
  - 更新 `SKILL.md` 的定位、触发描述、工作模式、交付形态判断树和工作流程
  - 新增内容产品形态模型和素材类型处理规则
  - 扩展输出模板与成书可行性量表，要求低分时给出替代形态建议
  - 增强扫描脚本，对 `.docx`、`.pdf` 标记预处理状态
  - 清理发布无关文件，并同步 `CHANGELOG.md`、`TASKS.md`
- **结果**：`article2book` 当前不再只判断“能不能成书”，而是先判断素材最适合转化为书、小册子、课程、系列文章、实务手册、知识库或暂不产品化
- **下一步**：补充匿名化样例，并在多个真实混合目录中做回归验证

### 2026-04-02 21:55 (Codex)

- **目标**：创建一个私有 skill，把零散文章目录整理为可出版书籍的策划底稿
- **操作**：
  - 抽样阅读用户提供的一组真实文章目录，识别主题主线
  - 设计技能定位、标准输出、判断原则与工作流程
  - 新增输出模板、成书评估量表和轻量扫描脚本
  - 创建 `CHANGELOG.md`、`DECISIONS.md`、`TASKS.md`、`LICENSE.txt`、`archive/.gitkeep`
- **结果**：完成 `article2book` 初版，并对一组真实素材目录做了脚本级冒烟验证，成功识别 70 份候选素材；当前技能已可执行主题提炼、成书评估、目录映射与重写补写建议
- **下一步**：用真实文章目录跑一次完整验证，并按结果微调聚类与输出模板

### 2026-04-02 22:00 (Codex)

- **目标**：根据用户反馈，把 skill 的边界从“文章成书”扩展为“现有素材结构化成书”
- **操作**：
  - 重写 `SKILL.md` 的定位、输入范围与不适用场景
  - 将“文章总表”调整为“素材总表”
  - 更新输出模板，使其支持讲稿和逐字稿映射
  - 更新扫描脚本，加入 `.srt`、`.vtt` 和 `source_type`
- **结果**：技能现在明确服务于“已有内容资产再开发”，而不是从零造书
- **下一步**：找一组包含文章和逐字稿的混合目录，验证素材类型区分是否足够好

### 2026-04-02 22:14 (Codex)

- **目标**：按用户要求，将技能名从 `article-to-book` 改为 `article2book`
- **操作**：
  - 更新 `SKILL.md` 的 frontmatter 名称与版本号
  - 在技能级文档中统一替换技能名
  - 将技能目录改名为 `article2book/`
- **结果**：技能名称、目录名和文档引用保持一致
- **下一步**：如需继续使用该 skill，后续均以 `article2book` 为准

### 2026-04-02 22:18 (Codex)

- **目标**：按用户反馈，把 `article2book` 的主流程从“脚本先扫”改为“Agent 通读优先”
- **操作**：
  - 重写 `SKILL.md` 的工作流程和判断原则
  - 新增 `references/agent-reading-protocol.md`
  - 在输出模板中加入 `processed/agent_reading_notes.md`
  - 明确脚本只承担索引和查漏角色
- **结果**：技能的主导能力调整为 Agent 的全文阅读理解，而不是脚本摘要
- **下一步**：拿真实目录跑一版完整通读式输出，检查阅读笔记模板是否顺手

### 2026-04-02 22:51 (Codex)

- **目标**：按用户反馈，把“低质量旧内容排除”加进 `article2book` 的主流程
- **操作**：
  - 在 `SKILL.md` 中加入“通读后先做保留 / 降权 / 排除筛选”的步骤
  - 新增 `references/content-screening-rubric.md`
  - 更新 `agent-reading-protocol.md` 和 `output-template.md`，加入深度判断、筛选结论与排除清单
- **结果**：技能现在会先剔除老而浅、过时失效、重复且更弱的内容，再做主题聚类和目录设计
- **下一步**：用真实目录验证排除标准是否过严或过宽

### 2026-04-02 23:35 (Codex)

- **目标**：根据真实使用反馈，压缩默认输出文件数量，并把技能扩展为“两阶段直达全书”工作流
- **操作**：
  - 在真实目录上完成一次完整策划输出，观察用户阅读成本
  - 将默认交付改为单一 `书稿策划意见.md`
  - 将阅读笔记、素材总表、筛选清单改为可选支撑文件
  - 在 `SKILL.md`、`output-template.md`、`agent-reading-protocol.md` 中加入“用户确认后直接生成 `全书初稿.md`”和 subagent 并行说明
- **结果**：技能的默认体验从“多文件分析包”切换为“先一份主意见，再一步到位重构全书”
- **下一步**：补充匿名化样例，验证简化后的默认模式在更多目录中的适用性

### 2026-04-02 23:55 (Codex)

- **目标**：把“单文件优先”的边界再收紧一层，避免内部分析痕迹继续外显
- **操作**：
  - 在 `SKILL.md` 中新增“默认轻交付 / 结论统一 / 内部痕迹不默认外显”三条判断原则
  - 补充更贴近用户心智的示例请求，明确“先出一份意见，再直接生成全书初稿”
  - 在 `output-template.md` 中把“保留 / 合并 / 排除原则”并入主意见模板，并将筛选清单明确为非默认产物
- **结果**：技能默认交付进一步稳定为“一份主意见 + 按需支撑”，减少首次阅读的认知负担
- **下一步**：继续用真实目录验证单文件意见是否足以支撑用户直接进入第二阶段

### 2026-04-03 00:10 (Codex)

- **目标**：把 `article2book` 的 frontmatter `description` 调整为更成熟的简洁写法
- **操作**：
  - 对比多个成熟 skill 的 `description` 组织方式
  - 将 `description` 收缩为“触发场景 + 核心能力”的两句式表达
  - 同步更新版本记录和最后更新日期
- **结果**：`description` 不再承载完整工作流，frontmatter 风格更接近成熟已发布 skill
- **下一步**：后续如继续公开化或上架，再统一检查其他 frontmatter 字段是否也需要进一步收敛

### 2026-04-03 00:18 (Codex)

- **目标**：清理 skill 示例与文档记录中的用户本地路径痕迹
- **操作**：
  - 将 `SKILL.md` 示例中的绝对路径替换为 `<素材目录>`
  - 将 `CHANGELOG.md` 和 `DECISIONS.md` 中涉及真实验证目录的表述改为泛化描述
- **结果**：skill 示例和文档记录不再暴露用户本地目录信息，同时保留验证上下文
- **下一步**：后续新增示例时继续保持占位写法，避免重新引入本地路径

### 2026-04-03 00:26 (Codex)

- **目标**：按用户要求移除 `archive` 回退输出，统一改为输出到源目录
- **操作**：
  - 修改 `SKILL.md` 的默认输出目录与 Step 1 表述
  - 从扫描脚本忽略目录中移除 `archive`
  - 删除技能目录中的 `archive/.gitkeep`
- **结果**：`article2book` 当前规则已收紧为永远默认输出到源目录，不再使用技能内 `archive/`
- **下一步**：后续如需支持特殊输出位置，应改为显式用户指定，而不是再引入隐式回退目录


path: LICENSE.txt
content: MIT License

Copyright (c) 2025 杨卫薪律师（微信ywxlaw）

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


path: TASKS.md
content: # 任务清单

## 已完成

- [x] 抽样阅读真实文章目录，明确技能服务的是“文章资产成书策划”而不是普通摘要
- [x] 创建 `SKILL.md`，明确定位、边界、标准输出和工作流程
- [x] 根据用户反馈，将技能边界扩展为“基于现有素材结构化成书”，覆盖文章、讲稿、逐字稿等输入
- [x] 按用户要求将技能名统一改为 `article2book`
- [x] 根据用户反馈，将主流程改为“Agent 通读全部素材优先”，脚本仅作辅助索引
- [x] 根据用户反馈，增加“通读后先做低质量内容排除”的筛选关口
- [x] 用真实文章目录完整跑一次 `article2book` 工作流，并验证默认输出包的实际认知负担
- [x] 根据真实使用反馈，将默认输出收束为单一 `书稿策划意见.md`
- [x] 增加“用户确认后直接生成 `全书初稿.md`”的第二阶段流程
- [x] 明确素材较多时可用 subagent 并行通读与分章起草，但最终判断与统稿由主 Agent 统一完成
- [x] 根据进一步使用反馈，将筛选依据默认压缩进主意见，不再把过程性文件作为常规用户输出
- [x] 将 frontmatter `description` 收缩为成熟 skill 常见的“触发条件 + 核心能力”写法
- [x] 清理示例与文档记录中的用户本地绝对路径，统一改为通用占位或泛化表述
- [x] 取消 `archive` 回退输出，统一为默认写回源目录
- [x] 创建 `references/output-template.md`
- [x] 创建 `references/agent-reading-protocol.md`
- [x] 创建 `references/content-screening-rubric.md`
- [x] 创建 `references/book-viability-rubric.md`
- [x] 创建 `scripts/build_article_inventory.py`
- [x] 为扫描脚本增加 `.srt`、`.vtt` 和 `source_type` 支持
- [x] 对真实文章目录完成一次扫描脚本冒烟验证（识别 70 份候选素材）
- [x] 创建技能级 `CHANGELOG.md`、`DECISIONS.md`、`TASKS.md`、`LICENSE.txt`
- [x] 建立 `archive/.gitkeep`
- [x] 将技能定位从“成书策划”泛化为“内容资产再组织”，默认先判断最佳内容形态
- [x] 增加成书 / 小册子 / 课程 / 系列文章 / 实务手册 / 知识库 / 暂不建议产品化的判断树
- [x] 增加快速盘点 / 标准策划 / 深度重构三档工作模式
- [x] 新增 `references/content-productization-models.md` 与 `references/source-type-handling.md`
- [x] 扩展 `references/output-template.md` 和 `references/book-viability-rubric.md`，加入替代形态与转化路径
- [x] 为扫描脚本增加 `.docx`、`.pdf` 需预处理素材标记
- [x] 清理 `.DS_Store` 和 `scripts/__pycache__/` 等发布无关文件
- [x] 正式发布到项目 `skills/article2book/` 目录
- [x] 将公开版本号升级为 `1.0.0`
- [x] 同步更新项目 README 技能列表和 Marketplace 清单

## 待办事项

- [ ] 为多主题混合目录增加“拆书/拆系列”判断示例
- [ ] 实现 `.docx` / `.pdf` 正文提取或外部预处理接入
- [ ] 补充 1-2 份匿名化的 `书稿策划意见.md` 样例
- [ ] 补充 1 份“策划意见确认后直接生成全书初稿”的匿名样例

## 后续版本方向

### 0.2.0

- [x] 增加更细的时效内容识别规则
- [x] 增加目录去重和重复观点预警逻辑
- [x] 补充出版定位与书名生成示例
- [x] 将默认输出模式改为“单意见书优先”
- [x] 明确两阶段流程：先策划，后直出全书初稿
- [x] 增加 subagent 并行通读与分章起草说明

### 0.2.1

- [x] 明确阅读笔记、索引表、筛选细表为内部或按需支撑材料
- [x] 将“保留 / 合并 / 排除原则”并入 `书稿策划意见.md` 默认模板
- [x] 补充“先出一份意见，再直接生成全书初稿”的示例请求

### 0.2.2

- [x] 精简 `SKILL.md` frontmatter 中的 `description`
- [x] 统一为“何时触发 + 能做什么”的描述方式

### 0.2.3

- [x] 将 `SKILL.md` 示例中的本地路径改为 `<素材目录>`
- [x] 将验证记录中的真实本地路径改为泛化表述

### 0.2.4

- [x] 将默认输出目录收紧为源目录下的 `书稿策划输出/`
- [x] 移除技能目录中的 `archive/.gitkeep`
- [x] 从扫描脚本中移除 `archive` 目录的特殊忽略规则

### 0.3.0

- [x] 将定位升级为“内容资产再组织”，保留成书策划作为默认主路径
- [x] 增加内容产品形态判断树和三档工作模式
- [x] 补充素材类型处理规则，覆盖访谈、课件、会议纪要、案例、`.docx`、`.pdf` 和混合目录
- [x] 扩展成书评估量表，低分时必须给出替代形态建议
- [x] 增强扫描脚本，标记 `.docx`、`.pdf` 为需预处理素材
- [x] 完成脚本帮助信息和最小目录扫描验证

### 1.0.0

- [x] 迁移为公开发布技能
- [x] 同步 README 与 Marketplace 清单
- [ ] 在多个真实目录中完成回归验证
- [ ] 补齐样例资产和使用说明


path: references/agent-reading-protocol.md
content: # Agent 通读素材协议

本文件用于约束 `article2book` 的主流程：在做内容形态判断和成书判断之前，先让 Agent 尽量通读全部候选文本素材。

## 核心原则

1. 不要只看文件名、标题或脚本摘要就下结论。
2. 最终的主题判断、内容形态判断、成书可行性判断和结构映射，应建立在 Agent 已覆盖全部候选文本素材的前提上。
3. 脚本只能用来补路径、查漏和做基础索引，不能替代阅读理解。
4. 当素材很多时，可以使用 subagent 分批并行通读；但最终的母题判断、筛选结论和统稿建议，必须由主 Agent 统一收束。

## 推荐流程

### 1. 先识别可直接阅读的素材范围

优先纳入：

- `.md`
- `.markdown`
- `.mdx`
- `.txt`
- `.srt`
- `.vtt`

对纯视频、音频、图片、扫描件：

- 先转写或 OCR
- 转为可读文本后再纳入主流程

对 `.docx`、`.pdf`：

- 先标记为需预处理素材
- 能提取正文时再纳入通读
- 无法提取时，不要仅凭文件名判断内容价值

### 2. 分批通读，不要一上来就凭印象聚类

如果素材较多，建议按批次处理：

- `1-20` 份为第一批
- `21-40` 份为第二批
- 依此类推

每批完成后，都要形成阶段性笔记，避免只凭短时记忆做判断。

如果采用 subagent 并行阅读：

- 每个 subagent 只负责一批素材
- 返回的内容应尽量简短，只保留判断所需字段
- 主 Agent 统一汇总，避免不同批次口径不一致

## 每份素材至少记录的内容

建议用如下字段：

| 字段 | 说明 |
|------|------|
| 文件路径 | 便于回溯 |
| 标题 | 可用原标题，也可补临时标题 |
| 一句话摘要 | 这份素材主要在讲什么 |
| 核心问题 | 它在回答什么问题 |
| 关键判断 | 这份素材真正有价值的观点是什么 |
| 深度判断 | 高 / 中 / 低 |
| 建议去向 | 主章节 / 课程单元 / 手册条目 / 知识库条目 / 案例 / 附录 / 移出 |
| 可能形态 | 成书 / 小册子 / 课程 / 系列文章 / 实务手册 / 知识库 |
| 风险提示 | 时效性 / 重复 / 口语化 / 观点过散 |
| 筛选结论 | 保留 / 降权 / 排除 |
| 理由 | 为什么这样判断 |

## 推荐输出

通读阶段默认只需要形成内部阅读依据，不必自动公开输出太多文件。

建议优先级如下：

1. **默认**：把通读结论直接压缩进 `书稿策划意见.md`
2. **可选**：当素材很多或需要留痕时，再生成 `processed/agent_reading_notes.md`

`processed/agent_reading_notes.md` 不需要过度工整，但应足够支撑后续主题聚类、内容形态判断、目录设计和删改判断。

## 注意事项

1. 如果某份素材明显与主线无关，也要先读完再移出，而不是只凭标题排除。
2. 如果逐字稿口语化很强，重点看其中是否已有可重组的论证顺序、案例素材和章节雏形。
3. 如果多个素材重复表达同一个判断，先各自读完，再决定保留哪个最强版本。
4. 如果素材较老，不要直接排除；先判断它是否仍承载母题、框架或关键转折。
5. 第二阶段如果要直接生成全书、课程、手册或知识库初稿，主 Agent 应优先保留足够的阅读依据供统稿时回看，但这些依据不一定都要公开给用户。


path: references/book-viability-rubric.md
content: # 成书可行性评估量表

使用本量表评估一个内容资产目录是否适合进一步收束为书籍项目。每项 1-5 分，尽量附具体素材依据。

本量表只回答“是否适合成书”。如果分数不高，不要只给否定结论，还要结合 `content-productization-models.md` 给出替代形态建议。

## 评分维度

### 1. 主题集中度

- `5 分`：大多数素材围绕同一个稳定问题推进
- `3 分`：有主线，但混入较多旁支主题
- `1 分`：主题明显分叉，难以整合为一本书

### 2. 核心命题清晰度

- `5 分`：作者有鲜明的一句话主张，可作为全书命题
- `3 分`：能看到方向，但命题需要二次提炼
- `1 分`：只是话题堆叠，没有稳定主张

### 3. 目标读者清晰度

- `5 分`：目标读者明确，素材持续回应同一类读者问题
- `3 分`：存在多个相邻读者群，需要做取舍
- `1 分`：读者过于分散，难形成统一出版定位

### 4. 内容厚度与互补性

- `5 分`：素材之间互相补强，可以形成完整章节体系
- `3 分`：有厚度，但存在明显重复或断层
- `1 分`：更多是零散观点，难以撑起一本书

### 5. 区分度与新颖性

- `5 分`：有清晰的独特框架、视角或方法论
- `3 分`：表达有个性，但仍偏经验分享
- `1 分`：主要是资讯综述、同质化评论或常规操作说明

### 6. 时效风险

- `5 分`：多数内容可转化为长期有效的常青表达
- `3 分`：部分内容强依赖特定工具、模型或热点
- `1 分`：大部分素材都是强时评或过时说明

### 7. 可持续扩写空间

- `5 分`：现有素材已接近半本以上，且仍有自然可扩章节
- `3 分`：已有基础，但需要较多补写
- `1 分`：现有内容只能拼成专题小册子或素材库

## 成书结论参考

- **30-35 分**：适合直接进入书稿策划
- **24-29 分**：具备成书潜力，但需做主题收束与重写
- **18-23 分**：暂缓按图书推进，更适合先做小册子、课程、系列文章或手册
- **17 分及以下**：不建议按一本书处理，应先重新组织内容资产

## 替代形态建议

当成书分数不足时，按以下线索给出替代建议：

| 主要问题 | 更适合的形态 | 判断依据 |
|----------|--------------|----------|
| 主题集中但厚度不足 | 小册子 | 可以讲清一个问题，但不足以支撑完整章节体系 |
| 讲授顺序明显、练习和案例较多 | 课程 | 用户更需要学习路径和操作练习 |
| 流程、清单、标准、模板占比高 | 实务手册 | 用户更需要查用和执行，不需要线性叙事 |
| 多篇观点独立，体系尚未成熟 | 系列文章 | 适合先发布测试母题和读者反馈 |
| 多主题并存、短笔记和问答较多 | 知识库 | 适合分类、标签和持续维护 |
| 文本不足、格式未处理或内容过散 | 暂不建议产品化 | 需要先补文本、脱敏、转写或按主题整理 |

## 必问问题

在打分前，至少回答以下问题：

1. 作者最想反复回答的那个问题是什么？
2. 这批素材最适合服务哪一类读者？
3. 读者消费这个内容，是为了学方法、拿框架、查流程、看案例，还是建立世界观？
4. 哪些素材只是热点评论，哪些素材承载了作者真正的框架？
5. 如果必须砍掉三分之一内容，最先砍什么？
6. 如果不做书，最自然的替代形态是什么？

## 输出时的最低要求

在 `书稿策划意见.md` 中至少写明：

- 总分和结论区间
- 是否适合成书
- 最佳内容形态
- 如果暂不成书，推荐转向哪种形态
- 进入下一阶段前最需要补写、删改或预处理的内容


path: references/content-productization-models.md
content: # 内容产品形态判断模型

本文件用于帮助 `article2book` 在“是否成书”之前，先判断一批已有素材最适合转化为何种内容产品。

## 判断顺序

1. 先判断素材主要服务的是阅读、学习、操作、检索还是传播。
2. 再判断素材是否有稳定母题、明确读者、足够厚度和可持续扩写空间。
3. 最后决定是否进入成书路径，或转向小册子、课程、系列文章、实务手册、知识库。

## 形态一：成书

适合条件：

- 有一个稳定、可被一句话表达的核心命题
- 多数素材围绕同一类读者问题展开
- 素材之间能互相补强，而不是简单重复
- 有足够厚度支撑 6-10 个章节
- 作者有区别于同类内容的判断框架

典型结构：

- 序章：提出问题与读者处境
- 第一部分：建立核心框架
- 第二部分：展开方法、案例或场景
- 第三部分：回应难题、边界与未来方向

不适合信号：

- 只能形成 2-3 个章节
- 内容主要是操作清单或工具说明
- 多主题并列，无法收束为一本书的母题

## 形态二：小册子

适合条件：

- 主题集中，但内容厚度不足以支撑完整图书
- 读者需要快速理解一个方法、流程或观点
- 素材适合短篇幅交付，重在清晰和可执行

典型结构：

- 问题背景
- 核心观点
- 方法步骤
- 示例或案例
- 行动清单

不适合信号：

- 素材已经足够厚，应升级为书或课程
- 主题分叉明显，小册子会变成零散拼贴

## 形态三：课程

适合条件：

- 素材天然带有讲授顺序、练习任务或学习路径
- 内容需要通过案例、演示、作业逐步内化
- 逐字稿、课件、直播稿占比较高
- 读者更需要“学会做”，而不是只读懂观点

典型结构：

- 课程目标
- 模块划分
- 每节课的学习目标、讲稿、案例、练习
- 课后任务和检查清单

不适合信号：

- 内容主要是个人观点随笔
- 缺少明确学习路径和可练习动作

## 形态四：系列文章

适合条件：

- 有一组有价值观点，但尚未形成完整体系
- 每篇可以独立阅读，读者不必按严格顺序学习
- 适合先通过发布测试读者反馈

典型结构：

- 系列总主题
- 8-12 篇文章标题
- 每篇的核心问题、观点和素材来源
- 发布顺序建议

不适合信号：

- 已经有稳定框架，应直接进入书或小册子
- 内容需要大量上下文铺垫，拆成文章会损失理解

## 形态五：实务手册

适合条件：

- 素材以流程、标准、清单、模板、案例和注意事项为主
- 读者主要想解决具体工作问题
- 内容适合被查阅、复用和按场景执行

典型结构：

- 使用场景
- 操作流程
- 判断标准
- 模板和清单
- 常见错误
- 案例说明

不适合信号：

- 观点表达和叙事讨论占比太高
- 缺少明确流程或可执行标准

## 形态六：知识库

适合条件：

- 素材多主题并存，短条目、笔记、问答、会议纪要较多
- 内容需要持续维护和检索
- 线性阅读不是主要需求
- 用户更在意“找得到、可复用、可更新”

典型结构：

- 一级分类
- 标签体系
- 条目模板
- 核心条目
- 待补条目

不适合信号：

- 已经有强主线和清晰读者旅程，应转为书、课程或手册
- 用户需要对外发布的完整叙事产品

## 形态七：暂不建议产品化

适用条件：

- 素材过散，缺少可收束的读者问题
- 大量内容过时、重复或缺少原创判断
- 只有素材量，没有稳定框架
- 缺少足够可读文本，主要材料仍需转写或 OCR

此时输出不应只是否定结论，还应给出下一步：

- 先补充可读文本
- 先按主题整理素材池
- 先发布系列文章测试方向
- 先补写核心方法论或关键案例

## 输出时的推荐写法

避免写：

- “不适合成书”
- “可以考虑做课程”

建议写：

- “当前最佳形态是实务手册，不建议直接成书。原因是素材主要由流程、清单和案例构成，读者更需要按场景查用，而不是线性阅读。”
- “这批素材具备成书潜力，但当前更适合先做 8 篇系列文章测试母题，再决定是否扩展为书。”


path: references/content-screening-rubric.md
content: # 内容筛选标准

本文件用于指导 `article2book` 在 Agent 通读完全部候选文本素材后，先做一次“保留 / 降权 / 排除”的减法，避免低价值内容拖累书、课程、手册或知识库结构。

## 核心原则

1. 不是“越多越好”，而是“越适合目标内容形态越好”。
2. 老内容不当然排除，关键看它是否仍有框架价值。
3. 排除必须写明理由，不能只凭直觉。

## 三种结论

### 1. 保留

适用于：

- 能承载核心命题、关键章节、课程单元、手册条目或知识库条目
- 观点清晰，论证完整，有作者独特判断
- 即使较早写成，仍可转化为常青表达

### 2. 降权

适用于：

- 有可用材料，但不够强，只适合作为案例、附录、练习或补充说明
- 与更强文本重复，保留它只是为了补案例或补细节
- 口语化较重，需要重写后才能进入正文

### 3. 排除

适用于：

- 信息陈旧且难以改写为常青表达
- 只有工具资讯、热点评论或清单堆砌，没有稳定判断
- 与主线关联很弱，纳入后只会稀释全书
- 与其他文本重复度高，且明显是更弱版本
- 读完后几乎提炼不出能进入章节、单元、条目或案例的有效信息

## 重点观察维度

### 一、深度

- 这份素材是在“解释一个框架”，还是只是“介绍一个东西”？
- 它是否有独立判断、方法论或视角？
- 读完之后，能否抽出至少一个稳定的章节观点、课程单元、手册标准或知识库条目？

### 二、时效性

- 内容是否依赖某次具体更新、某个模型版本、某个热点事件？
- 如果剥离这些时点信息，它还剩下什么？
- 能否改写成今天仍成立的判断？

### 三、重复度

- 它是不是在说另一个素材已经说过的话？
- 如果重复，它有没有提供更强的案例、更清晰的结构或更成熟的表达？
- 如果没有，就应降权或排除

### 四、内容形态适配度

- 它更像一个章节、课程单元、手册条目、知识库条目、案例、附录材料，还是根本不属于当前产物？
- 纳入后会增强主线，还是只会让产物更散？

## 排除理由建议写法

避免写：

- “一般”
- “不够好”
- “感觉没用”

建议写：

- “依赖 2025 年某次工具更新，去掉时点后几乎没有可保留框架”
- “与 A-012 表达同一判断，但案例和结构都更弱”
- “更像工具清单，缺乏作者独特判断，无法支撑章节或课程单元”
- “与当前主线关联弱，纳入会冲淡母题”


path: references/output-template.md
content: # 书稿策划输出模板

以下模板用于在 `书稿策划输出/` 目录中生成结果。**默认只交付一份主意见书**，其余文件仅在必要时输出。

## 默认交付：书稿策划意见.md

建议结构：

```markdown
# 书稿策划意见

## 一、结论
- 最佳内容形态：成书 / 小册子 / 课程 / 系列文章 / 实务手册 / 知识库 / 暂不建议产品化
- 是否值得成书：值得 / 有潜力但需收束 / 暂不建议
- 结论类型：可以直接推进 / 可以成书但需重写 / 暂不建议成书
- 一句话总判断：

## 二、这批素材真正适合做成什么
- 推荐主形态：
- 推荐理由：
- 不建议走的形态：
- 不建议理由：
- 如果一定要成书，需要先补足什么：

## 三、主命题、目标读者与定位
- 推荐主命题：
- 目标读者：
- 读者最想解决的问题：
- 这份内容产品与常见同类内容的差异：

## 四、推荐标题或产品名方向
- 推荐名称：
- 副标题：
- 备选 1：
- 备选 2：

## 五、推荐结构草案
### 形态说明
- 推荐产物：
- 结构逻辑：

### 目录 / 单元 / 栏目草案
1. ...
2. ...
3. ...

## 六、最重要的删改动作
- 建议保留：
- 建议删除：
- 建议合并重写：
- 建议补写：
- 保留 / 合并 / 排除原则：

## 七、转化路径
- 第一步：
- 第二步：
- 第三步：
- 风险点：

## 八、如果确认推进，第二阶段将怎么写
- 下一步产物：
- 默认输出文件：
- 写作方式：
- 是否拆分：
- 预计先从哪几章 / 单元 / 条目起草：
```

## 可选支撑文件

只有在以下场景才建议生成：

- 素材特别多，需保留通读痕迹
- 用户明确要求看细表
- 主题分叉明显，需要解释筛选依据
- 输入格式复杂，需要记录预处理状态

### processed/agent_reading_notes.md

建议结构：

```markdown
# Agent 通读笔记

## 第一批（A-001 ~ A-010）

### A-001 `xxx.md`
- 一句话摘要：
- 核心问题：
- 关键判断：
- 深度判断：高 / 中 / 低
- 建议去向：主章节 / 课程单元 / 手册条目 / 案例 / 附录 / 移出
- 风险：时效性 / 重复 / 口语化 / 格式预处理 / 其他
- 筛选结论：保留 / 降权 / 排除
- 理由：
```

### processed/article_inventory.csv / jsonl

- 用于补路径、查漏、保留基础索引和预处理状态
- 不作为用户默认阅读文件

### 附录-排除与降权清单.md

建议结构：

```markdown
# 排除与降权清单

## 一、排除素材
| 素材 | 理由 | 备注 |
|------|------|------|

## 二、降权素材
| 素材 | 处理方式 | 理由 |
|------|----------|------|
```

说明：

- 默认不单独生成。
- 只有当用户明确要求看筛选明细，或主题分叉明显需要解释时，才把它从主意见中拆出来。

## 第二阶段默认输出

当用户确认 `书稿策划意见.md` 无误后，默认直接进入对应形态起草。

| 推荐形态 | 默认输出 | 建议结构 |
|----------|----------|----------|
| 成书 | `全书初稿.md` | 书名、副标题、序章、章节正文 |
| 小册子 | `小册子初稿.md` | 标题、适用对象、核心观点、短章节、行动清单 |
| 课程 | `课程大纲与讲稿初稿.md` | 课程目标、模块、课时、讲稿、练习 |
| 系列文章 | `系列文章规划与首批初稿.md` | 系列主题、发布顺序、单篇标题、首批文章 |
| 实务手册 | `实务手册初稿.md` | 使用场景、流程、标准、清单、案例 |
| 知识库 | `知识库结构与核心条目.md` | 分类、标签、条目模板、首批核心条目 |

## 第二阶段可选拆分

只有在以下情况才拆分：

- 全部内容过长，单文件不利于修改
- 需要多 subagents 分章、分单元、分条目并行起草
- 用户明确要求按章节、课程模块或知识库条目输出

可选结构：

```text
书稿策划输出/
├── 书稿策划意见.md
├── 内容结构说明.md
└── chapters/
    ├── 01-序章.md
    ├── 02-第一章.md
    └── ...
```

也可按形态调整目录名：

```text
书稿策划输出/
├── 书稿策划意见.md
├── 内容结构说明.md
└── units/
    ├── 01-课程单元.md
    ├── 02-课程单元.md
    └── ...
```

## 输出原则

- 能用一份主意见讲清楚的，不拆成 6-8 份用户文件
- 主意见必须同时说明“最佳内容形态”和“是否适合成书”
- 支撑材料默认放在 `processed/`，不增加用户阅读负担
- 第二阶段默认一步到位生成对应形态初稿，而不是继续制造更多过渡文件


path: references/source-type-handling.md
content: # 素材类型处理规则

本文件用于指导 `article2book` 处理不同来源、格式和成熟度的内容资产。

## 总原则

1. 先区分“可直接通读文本”和“需预处理素材”。
2. 可直接通读的素材进入主流程；需预处理素材先记录路径、类型和处理建议。
3. 不要因为格式复杂就忽略素材，也不要在未提取正文前对其内容价值下结论。
4. 所有判断尽量保留可回溯路径。

## Markdown / MDX / 纯文本

处理方式：

- 直接纳入通读范围
- 优先读取正文，而不是只看标题或 frontmatter
- 注意识别标题、发布时间、标签、发布状态和内部链接

适合判断：

- 文章、专栏、笔记、小册子、书稿章节雏形

## 短笔记 / 卡片 / Obsidian 笔记

处理方式：

- 不要按单篇完整文章期待它们
- 重点识别概念、判断、案例、金句和可补写线索
- 多篇短笔记可合并为一个章节、课程单元或知识库条目

适合判断：

- 知识库、书稿素材池、课程补充材料

## 字幕稿 / 逐字稿 / 直播稿

处理方式：

- 先去除明显时间戳、口头禅和重复转场
- 重点识别讲授顺序、案例、判断框架和可转写段落
- 不要因为口语化强就直接降权

适合判断：

- 课程、书稿章节、问答式小册子、案例材料

风险：

- 口语重复
- 结构松散
- 观点靠语境成立，转成书面表达时需要补桥段

## 访谈稿 / 问答记录

处理方式：

- 保留问答关系，但不要被问答形式限制
- 抽取反复出现的问题、核心判断和故事性案例
- 判断是否适合保留为访谈体，或改写为章节正文

适合判断：

- 访谈集、案例集、书稿案例、系列文章

## 课件 / 大纲 / 讲义

处理方式：

- 识别课程目标、模块顺序和练习任务
- 对只有标题的页，标注“需补写”
- 对已经有讲义正文的页，可作为课程或手册素材

适合判断：

- 课程、实务手册、教材、小册子

## 会议纪要 / 项目记录

处理方式：

- 先判断是否含有可公开复用的经验，而不是只含内部过程
- 抽取决策依据、问题清单、流程节点和复盘结论
- 涉及敏感信息时，提醒用户先脱敏

适合判断：

- 知识库、实务手册、案例复盘、内部培训材料

## 案例材料

处理方式：

- 识别事实背景、问题、处理过程、结果和可复用经验
- 分离“个案事实”和“通用方法”
- 对未脱敏材料，标注隐私与合规风险

适合判断：

- 案例集、实务手册、书稿案例、课程练习

## Word 文档（.docx）

处理方式：

- 默认标记为“需预处理素材”
- 如当前环境具备读取能力，可提取文本后纳入通读
- 若无法读取，先记录路径、文件名和建议：转换为 Markdown、TXT 或复制正文后再处理

适合判断：

- 文章、合同/报告草稿、课程讲义、手册章节

注意：

- 本技能的索引脚本只标记 `.docx`，不直接解析正文，避免引入不稳定依赖。

## PDF 文本

处理方式：

- 默认标记为“需预处理素材”
- 可复制文本型 PDF 应先提取正文
- 扫描件 PDF 应先 OCR
- 提取失败时，不要凭文件名判断内容质量

适合判断：

- 论文、报告、讲义、手册材料、案例附件

注意：

- 本技能的索引脚本只标记 `.pdf`，不直接解析正文，避免误读扫描件或复杂版式。

## 混合目录

处理方式：

- 先按文件类型和主题建立初步分组
- 再判断是一个内容产品，还是多个产品种子
- 对明显不同的主线，不要强行合并

建议输出：

- 主方案：最适合推进的一条主线
- 备选方案：可拆出的课程、手册、知识库或系列文章
- 暂缓内容：需要补文本、脱敏或重写的素材

## 预处理状态建议字段

在内部清单中建议保留以下状态：

| 状态 | 含义 |
|------|------|
| `可直接通读` | 文本可直接读取并进入主流程 |
| `需转写` | 音频、视频或直播素材需先转成文本 |
| `需 OCR` | 扫描件或图片型 PDF 需先识别文字 |
| `需转换格式` | `.docx`、复杂 PDF、课件等需先转为可读文本 |
| `暂不纳入` | 配图、附件、自动生成目录等不进入内容判断 |


path: scripts/build_article_inventory.py
content: #!/usr/bin/env python3
from __future__ import annotations

import argparse
import csv
import json
import re
from pathlib import Path

TEXT_EXTENSIONS = {".md", ".markdown", ".mdx", ".txt", ".srt", ".vtt"}
PREPROCESS_EXTENSIONS = {".docx", ".pdf"}
ALLOWED_EXTENSIONS = TEXT_EXTENSIONS | PREPROCESS_EXTENSIONS
IGNORED_DIR_NAMES = {
    ".git",
    ".obsidian",
    ".trash",
    "node_modules",
    "assets",
    "attachments",
    "images",
    "img",
    "书稿策划输出",
    "processed",
}


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="扫描现有内容资产目录并输出可用于内容形态判断和成书分析的清单。"
    )
    parser.add_argument("source_dir", help="待扫描的素材目录")
    parser.add_argument(
        "--output-dir",
        required=True,
        help="输出目录，脚本会在该目录下写入 article_inventory.csv/jsonl/md",
    )
    return parser.parse_args()


def should_skip(path: Path) -> bool:
    return any(part in IGNORED_DIR_NAMES for part in path.parts)


def read_text(path: Path) -> str:
    try:
        return path.read_text(encoding="utf-8")
    except UnicodeDecodeError:
        return path.read_text(encoding="utf-8", errors="ignore")


def is_preprocess_only(path: Path) -> bool:
    return path.suffix.lower() in PREPROCESS_EXTENSIONS


def split_frontmatter(text: str) -> tuple[dict[str, str], str]:
    if not text.startswith("---\n"):
        return {}, text

    lines = text.splitlines()
    end_index = None
    for index in range(1, min(len(lines), 200)):
        if lines[index].strip() == "---":
            end_index = index
            break

    if end_index is None:
        return {}, text

    metadata: dict[str, str] = {}
    for raw_line in lines[1:end_index]:
        if ":" not in raw_line:
            continue
        key, value = raw_line.split(":", 1)
        metadata[key.strip()] = value.strip()

    body = "\n".join(lines[end_index + 1 :])
    return metadata, body


def extract_title(path: Path, metadata: dict[str, str], body: str) -> str:
    for key in ("title", "标题"):
        if metadata.get(key):
            return metadata[key]

    match = re.search(r"^#\s+(.+)$", body, flags=re.MULTILINE)
    if match:
        return match.group(1).strip()

    return path.stem


def clean_markdown(text: str) -> str:
    text = re.sub(r"```.*?```", " ", text, flags=re.DOTALL)
    text = re.sub(r"!\[[^\]]*\]\([^)]+\)", " ", text)
    text = re.sub(r"\[([^\]]+)\]\([^)]+\)", r"\1", text)
    text = re.sub(r"<[^>]+>", " ", text)
    text = re.sub(r"^#+\s*", "", text, flags=re.MULTILINE)
    text = re.sub(r"^>\s*", "", text, flags=re.MULTILINE)
    text = re.sub(r"\s+", " ", text)
    return text.strip()


def make_preview(text: str, limit: int = 120) -> str:
    cleaned = clean_markdown(text)
    if len(cleaned) <= limit:
        return cleaned
    return cleaned[:limit].rstrip() + "..."


def detect_source_type(path: Path) -> str:
    suffix = path.suffix.lower()
    if suffix in {".srt", ".vtt"}:
        return "逐字稿"
    if suffix in {".md", ".markdown", ".mdx"}:
        return "文章/笔记"
    if suffix == ".txt":
        return "讲稿/纯文本"
    if suffix == ".docx":
        return "需预处理：Word 文档"
    if suffix == ".pdf":
        return "需预处理：PDF 文档"
    return "其他文本"


def detect_processing_status(path: Path) -> str:
    suffix = path.suffix.lower()
    if suffix in TEXT_EXTENSIONS:
        return "可直接通读"
    if suffix == ".docx":
        return "需转换格式"
    if suffix == ".pdf":
        return "需提取文本或 OCR"
    return "需人工确认"


def make_preprocess_preview(path: Path) -> str:
    suffix = path.suffix.lower()
    if suffix == ".docx":
        return "需先转换为 Markdown/TXT 或提取正文后再纳入通读判断。"
    if suffix == ".pdf":
        return "需先确认是否为可复制文本；扫描件需 OCR 后再纳入通读判断。"
    return "需先转换为可读文本后再纳入通读判断。"


def collect_records(source_dir: Path) -> list[dict[str, object]]:
    records: list[dict[str, object]] = []
    candidate_files = sorted(
        path
        for path in source_dir.rglob("*")
        if path.is_file()
        and path.suffix.lower() in ALLOWED_EXTENSIONS
        and not should_skip(path.relative_to(source_dir))
    )

    for index, path in enumerate(candidate_files, start=1):
        relative_path = path.relative_to(source_dir).as_posix()
        preprocess_only = is_preprocess_only(path)

        if preprocess_only:
            metadata: dict[str, str] = {}
            body = ""
            cleaned = ""
            title = path.stem
            preview = make_preprocess_preview(path)
        else:
            raw_text = read_text(path)
            metadata, body = split_frontmatter(raw_text)
            cleaned = clean_markdown(body)
            title = extract_title(path, metadata, body)
            preview = make_preview(body)

        record = {
            "id": f"A-{index:03d}",
            "relative_path": relative_path,
            "title": title,
            "source_type": detect_source_type(path),
            "processing_status": detect_processing_status(path),
            "extension": path.suffix.lower(),
            "has_frontmatter": bool(metadata),
            "frontmatter_keys": ", ".join(sorted(metadata.keys())),
            "publish_status": metadata.get("公众号发布", metadata.get("published", "")),
            "chars": len(cleaned.replace(" ", "")),
            "lines": len(body.splitlines()),
            "h1_count": len(re.findall(r"^#\s+.+$", body, flags=re.MULTILINE)),
            "preview": preview,
        }
        records.append(record)

    return records


def write_csv(path: Path, records: list[dict[str, object]]) -> None:
    fieldnames = [
        "id",
        "relative_path",
        "title",
        "source_type",
        "processing_status",
        "extension",
        "has_frontmatter",
        "frontmatter_keys",
        "publish_status",
        "chars",
        "lines",
        "h1_count",
        "preview",
    ]
    with path.open("w", encoding="utf-8", newline="") as handle:
        writer = csv.DictWriter(handle, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(records)


def write_jsonl(path: Path, records: list[dict[str, object]]) -> None:
    with path.open("w", encoding="utf-8") as handle:
        for record in records:
            handle.write(json.dumps(record, ensure_ascii=False) + "\n")


def write_markdown(path: Path, records: list[dict[str, object]]) -> None:
    lines = [
        "# 素材清单",
        "",
        f"- 共扫描到 **{len(records)}** 份候选素材",
        "",
        "| 编号 | 标题 | 素材类型 | 处理状态 | 路径 | 字数估计 | 发布状态 | 预览 |",
        "|------|------|----------|----------|------|----------|----------|------|",
    ]

    for record in records:
        preview = str(record["preview"]).replace("|", "/")
        title = str(record["title"]).replace("|", "/")
        source_type = str(record["source_type"]).replace("|", "/")
        processing_status = str(record["processing_status"]).replace("|", "/")
        relative_path = str(record["relative_path"]).replace("|", "/")
        publish_status = str(record["publish_status"] or "未知").replace("|", "/")
        lines.append(
            f"| {record['id']} | {title} | {source_type} | {processing_status} | `{relative_path}` | {record['chars']} | {publish_status} | {preview} |"
        )

    path.write_text("\n".join(lines) + "\n", encoding="utf-8")


def main() -> None:
    args = parse_args()
    source_dir = Path(args.source_dir).expanduser().resolve()
    output_dir = Path(args.output_dir).expanduser().resolve()
    output_dir.mkdir(parents=True, exist_ok=True)

    records = collect_records(source_dir)

    write_csv(output_dir / "article_inventory.csv", records)
    write_jsonl(output_dir / "article_inventory.jsonl", records)
    write_markdown(output_dir / "article_inventory.md", records)

    summary = {
        "source_dir": str(source_dir),
        "output_dir": str(output_dir),
        "source_count": len(records),
    }
    print(json.dumps(summary, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)