tadado-activity-import · v2.2.0 · 2026-09-21 · sha256 fae0c58305e571da
tadado-activity-import v2.2.0A
Immutable. This exact content is served forever at /api/v1/blob/fae0c58305e571da.
--- name: tadado-activity-import version: "2.2.0" description: | 把 py 版(v0.x)「活动分析」导出的 md 清单(标签 → 任务 → 活动三层),转换成 Tadado2 能**直接导入**的 md —— md 进、md 出,不碰数据库、不调 CLI。逐项对账、认不出就停、 不替用户拍板。触发词:活动分析导出、活动清单转换、旧数据迁移、数据迁入、md 转换。 --- > 📦 **本 skill 是自包含的**:转换器就在 [`scripts/migrate-activity.mjs`](scripts/migrate-activity.mjs), > 样例在 [`sample-input.md`](sample-input.md)。把整个 `tadado-activity-import/` 目录拷走就能用 —— > **不依赖任何仓库**(脚本零依赖,只要机器上有 Node)。 > > 仓库里这份是**权威源**;要让它在本机生效,把整个目录拷到工作区的 `.claude/skills/` 下即可。 > **不要**在那边改内容:两份必然分叉,改这里。 # py 活动清单 → Tadado2 可直接导入的 md ## 这个 skill 做什么 | | | |---|---| | **输入** | 一份从 py 版「活动分析」导出的 `.md` 清单(三层结构:标签 → 任务 → 活动) | | **输出** | 一份能被 Tadado2 的「**数据迁入**」直接导入的 `.md`,任务与活动一条不少 | 它**只做文本转换**:不读数据库、不碰应用数据、不调任何 CLI、不改任何既有文件(除非显式要写)。 **导入与导出都由用户手动**在两边应用里做 —— 你只负责把中间那份 md 转出来。 ## 转换器在哪 **就在本 skill 里**:`scripts/migrate-activity.mjs`(相对本文件的位置)。它是**零依赖纯 ESM**, `node` / `deno` / `bun` 任何一个都能跑,**不需要 `npm install`**。 先定位本 skill 自己的目录(即本 SKILL.md 所在目录),再按相对路径调它 —— **不要去找别的副本**, 也不要在用户的项目里另找一份。**拿不准目录就问用户**,别乱猜路径。 **为什么自带而不是外链**:skill 要能被**单独分享** —— 别人拿到这个目录就得能跑。 (仓库内那条端到端验收跑的就是**这一份**:`desktop/e2e/smoke.mjs` 调 `resources/skill/tadado-activity-import/scripts/migrate-activity.mjs` 产出真文件、喂进应用的 「数据迁入」、断言「共 3 条 · 0 行认不出」。一份实现,两处验。) ## 铁律 1. **先预演,再写出。** 默认只解析、只对账、不写文件(加 `--apply` 才写)。 2. **认不出就停下问。** 任何不符合已知行型的行都要报出来 —— 不许「看着差不多就跳过」, 也不许自己猜一种解释。 3. **数字必须平。** `源活动行数 = 输出 + 系统记录过滤 + 重复剔除`(续行并入上一条)。 平不了就不许写出,先把对不上的查清楚。 4. **不替用户拍板。** 见「必须停下来问用户」一节,每一条都要问过再继续。 5. **跑不了脚本时,不许自己硬转。** 见下一节 —— 这是本 skill 最容易犯的错。 ## 用法 在**本 skill 目录**下执行(下面的 `scripts/` 是相对路径,按实际位置给): ```bash # 1) 预演(默认不写任何文件)—— 先读这份对账报告 node scripts/migrate-activity.mjs <清单.md> # 2) 想留档一份报告 node scripts/migrate-activity.mjs <清单.md> --report 报告.json # 3) 对账全过、待确认项都问过之后,才生成 node scripts/migrate-activity.mjs <清单.md> --apply # 默认写到清单旁边:<清单>-可导入.md # 这个名字**会显示在导入对话框里**,也是用户切分区时的对照物,别随手改 # 4) 用户手改过那份输出、确实要按当前源重写 node scripts/migrate-activity.mjs <清单.md> --apply --force ``` 想先看看它长什么样,拿自带样例跑一遍(**不写任何文件**): ```bash node scripts/migrate-activity.mjs sample-input.md ``` 退出码:`0` 干净 · `1` 有待确认项 · `2` 对账失败 / 有认不出的行 / 输出已存在 (后两种都不许往下走)。 **默认不覆盖已有输出**:用户很可能**手改过**那份文件 —— 比如工具报了「标签超过 3 个」, 而它按原则不替用户删,用户就得自己去删到 3 个。重跑 `--apply` 会把那些手改**静默抹掉**, 所以只有确实要按当前源重写时才加 `--force`。 生成成功后脚本会打印**下一步**(切目标分区 → 任务管理 → 数据迁入 → 选那个文件), 把它转述给用户。 ## 环境里没有 JS 运行时时怎么办(重要) **不许自己逐行硬转。** 这种机械转换由 LLM 手工做会**静默丢行** —— 一份真实清单里八成 以上的行是活动行,丢掉是看不出来的,用户只会看到「导进去了」。 两条路,**都必须带对账**: 1. **让用户装一个**(Windows:`winget install OpenJS.NodeJS`),再照上面的用法跑; 2. **走慢路径**:逐行转、逐行报,把「**源 N 行 → 输出 M 行**」的账**摊开给用户看**, 任何一行没去处就停下问。宁可慢,不许静默。 ## 认识的四种行 脚本对两种导出写法都兼容(同一套层次,符号略有差别): | 行 | 写法 | 说明 | |---|---|---| | 标签 | `#标签名` | 分组标题。清单就是按它分块的 | | 任务 | `1. 标题` 或 `1. 标题 [待办→逾期, 0%→0%]:` | 序号与摘要可能没有,标题是必须的 | | 活动 | ` 01-05 09:10 内容` 或 ` - 01-05 09:10 内容` | **缩进** + `MM-DD HH:MM` 开头。缩进量与 `- ` 前缀都可选 | | 续行 | 紧跟在活动后面的非空行 | 活动文本里带换行时会被导成独立行,并回上一条活动,并在报告里逐行列出 | 其余一律算「认不出的行」→ 停下。 ## 输出的 md 长什么样 ``` - [~] 标题 #标签1 #标签2 ⏰03-20 :: 60% - 01-07 10:30 把接口签名对齐了 - 02-10 16:50 读完第 3 章 ``` 每一段读成什么 —— 这是**应用自己的写法**(读入口是 `desktop/src/data/markdown.ts` 的 `parseTasks`;批量框用 `parseTasksDetailed`,就是它加一份「认不出的行」清单): | 写法 | 含义 | |---|---| | `-` 或 `*` 开头 + `[~]` / `[x]`(`[ ]` 也认) | 一条任务;方括号里是状态 | | `#标签` | 标签,一条任务最多 3 个 | | `⏰09-21` 或 `⏰09-21 14:30` | 截止(年份按离今天最近的一年推算);不写就是「无截止」 | | `:: 40%` | 进度 | | 缩进 ≥2 格、以 `MM-DD HH:MM` 开头的一行 | **活动**,挂到上一条任务的时间线上 | - 状态用方括号:`[~]` 进行中 · `[x]` 已完成 · `[ ]` —— **工具产出的是它**,应用读作 「进行中」(「待办」那一档 2026-09-21 删了)。清单里的「逾期」也落成 `[ ]` —— 应用会按截止日期自己把它标回逾期 - 活动行**缩进 3 个空格**,紧跟在它的任务下面;时刻保持 `MM-DD HH:MM` 原样 - **只留人写的进展**:旧版自动写下的系统记录被滤掉,见下节 - 活动文本**一个字符都不改**(`[xx]` 这类前缀是原作者的语义,不翻译) ## 滤掉系统记录(默认行为) 旧版会**自动**记下这些(不是人写的),**默认全部滤掉**: ``` [批量创建] 创建任务 1/3 创建 创建任务 创建 延后处理: 截止时间 A → B 延后 状态变更为 已完成 / 状态 X → Y 状态 进度 0% → 30% 进度 截止时间 2026-01-31 设截止 ``` 理由:它们的**结果已经写在任务行上**了(方括号 = 状态、`::` = 进度、`⏰` = 截止), 本身不携带新信息;而且这套写法里的活动行导入后一律是「**人手写的一条进展**」, 把系统记录带进去等于给它挂个人名,还会落进「可改可删」那一类。 ⚠️ 截止与状态正是**从这些行里挖出来**的,所以工具是**先挖字段、再滤行** —— 字段不会丢。滤掉多少会报在两处:对账那一行,与报告末尾的清单。 用户想全留着 → 加 `--keep-system`(时间线会很长,但「什么时候完成的」也留着了)。 ## 字段从哪来 | 目标字段 | 来源 | 清单里没有时 | |---|---|---| | 标题 | 任务行(剥掉序号与状态摘要) | ——(没有标题的行会停下问) | | 标签 | 分组标题;一条任务出现在多个组时**合并** | 空 | | 状态 | 任务行摘要的右侧值;没有摘要就翻活动文本里的「状态变更」 | 落 `[ ]`,**应用读作「进行中」**,并在报告里列出 | | 进度 | 摘要里 `0%→M%` 的 M | 已完成 = 100,其余 = 0 | | 截止 | 活动文本里 `截止时间 A -> B` 取**最后一次**的 B(带箭头的写法优先于「直接设」的) | 无截止,并在报告里列出 | | 活动 | 缩进活动行里**人写的那些**,时刻原样;系统记录滤掉 | 应用会给它补一条「导入任务」 | | **优先级** | **清单里没有这个信息** → 全部落 P2(关注) | —— | | 起止区间 | `start` = **最早一条活动的日期**(没有活动行 = 导入日)、`end` = 截止 | —— | | 创建日 | **最早一条活动的日期**(没有活动行 = 导入日) | —— | | 归档 / 关联 | 清单里没有 → 未归档 / 无关联 | —— | **创建日 / 起止是应用在导入时算的,不是工具写进 md 的** —— 这套写法里没有能装创建日的字段, 所以**不要去发明一个**:应用会取这条任务**最早一条活动**的日期。为什么不用导入日:迁进来的 任务历史全在活动行里 —— 一律落导入日会让整批任务的「创建于」挤在同一天,而**创建日是这次 迁移里唯一会被整个抹平的时间维度**(截止 / 进度 / 状态都能从活动文本里挖到)。 **精度要对用户说清**:它给的是「**第一次动手**」那天,不是真正的创建时刻(旧版的「创建任务」 记录按上一条的规矩不转移),对「建了长期搁着」的任务会偏晚。`start` 与创建日取**同一个日期**, 否则会出现「创建于 01-05、甘特色条却从 09-20 才开始」这种矛盾。 ## 必须停下来问用户 1. **有认不出的行** —— 把行号与原文贴给用户,问这几行是什么。不要自己归类。 2. **有任务挖不到截止** —— 告诉用户这些会以「无截止」导入(不计入「今日到期」), 并问:这个来源里确实没有它们的截止,还是需要另找来源? 3. **状态无法确定** —— 列出标题,说明默认落 `[ ]`、**应用读作「进行中」**。 4. **标签超过 3 个** —— 应用上限是 3,让用户决定保留哪几个 (工具按原则**不替他删**,所以要他去改那份输出文件;改完重跑记得加 `--force`)。 5. **同名任务被合并** —— 清单里区分不出「两条同名任务」和「一条任务出现在两个标签组」, 把合并清单给用户确认。 6. **优先级** —— 说明这一列在清单里不存在,导入后统一是 P2。 7. **标题里带保留字符**(`#`、`⏰`+日期、`::`+数字)—— 这套写法**没有转义机制**, 那一段在导入时会被应用的解析器挖走:标题「发布 v1.0 #发布日」会变成标题「发布 v1.0」 + 标签 `#发布日`,**而且不报错**。让用户先改标题,别带着它往下走。 ## 导入(**用户手动做**) 1. **先在 rail 底部切到目标分区** —— 导入落在**当前分区**。旧版是按分区导出的, 一个清单文件通常对应一个分区,报错也让用户拿文件名对一下。 2. 任务管理页 → **「数据迁入」** → 选那份 `<清单>-可导入.md`。 3. 看它显示「**共 N 条**」:**N 必须等于报告里的任务数**,不等就先查。 4. 看它下面**有没有列出「没能识别的行」** —— 必须是空的;有就说明有行没被读懂,停下。 5. 若列出「N 条在当前分区里已有同名任务」—— 那说明这个分区里已经有同一批了, 让用户自己决定「跳过重复」还是「全部导入」。 ## 导入之后复核 1. **任务数**:任务页 / 管理页的「共 N 条」= 报告里的任务数。 2. **活动数**:活动分析页选「全年」,看「共 N 条活动」是否等于脚本最后打印的 「**活动 M 条**」(注意是**输出**的条数,不是源清单的 —— 系统记录已被滤掉)。 3. **抽查两三条**:打开抽屉,看时间线的条数与内容是否与输出文件里那几条一致。 任何一项对不上,回到预演报告重新查 —— 不要在应用里手工补。 ## 谁负责验证(两层,别混) | 层 | 查什么 | 在哪 | |---|---|---| | **工具** | 源侧:每一行活动都有去向、数字要平、要人拍板的事集中列出来 | 那份终端报告 | | **应用** | 解析侧:哪几行认不出、哪几条与库里同名 | 「数据迁入」那一屏,**导入之前**列出来 | 别把「工具没报」当成「没问题」—— 工具看不到你的库。