
最近整理简历时发现一个尴尬的事实我能列出一串技能名但真被问到你当前掌握到哪一步、最近三个月在哪个技能上花了多少时间时回答全靠感觉。这种感觉驱动导致两个直接后果——学东西东一榔头西一棒子季度总结时翻遍日历也找不出有效数据最后只能写持续学习、积极成长这种空话。为了把技能管理这件事从感觉变成数据我动手写了一个本地优先的命令行小工具名字就叫skills。它不做社交、不做排行、不上云只管三件事记录技能清单、追踪每个技能上的时间投入、按时段自动生成复盘报告。如果你也经常做年度规划、季度总结或者正在为转岗、竞聘准备技能论据这篇内容应该能帮上忙。1. 为什么我不用Notion和Excel来管理技能清单1.1 最初暴露的四类真实痛点动手写工具之前我先冷静地把自己的需求列了一遍发现核心痛点其实只有四个技能数量膨胀后清单维护变成了负担。两年前我的技能清单大概 20 项去年膨胀到 40 项。Excel 里确实能加一行就完事但每次打开文件都要先回忆我上一次更新是什么时候、当时定的目标熟练度是多少。熟练度没有客观衡量标准。熟悉和掌握在简历上看起来差不多真正面试时几句话就会被问穿。我对技能自评需要一套能复用的分级锚点而不是今天心情好就写熟练。时间投入和技能成长是割裂的。日历、番茄钟、手账都在记录时间但没有一个地方能回答我上周投入在 Vue 上的 6 小时到底让我从哪个阶段走到了哪个阶段。复盘时缺少历史数据支撑。季度末想写实质性的成长总结翻来翻去只有零散笔记没有可汇总、可对比的结构化数据。这四个痛点叠加起来等于说技能管理在我这里不是知识管理而是一个轻量级的数据统计问题。它的核心动作只有两个录入和查询。录入要足够快查询要能按周、按月、按季度聚合。1.2 为什么表格工具折在这儿Notion 和 Excel 我都认真用过各自的折点也很明确。Notion 的问题在于录入成本太高。为了记录一次 45 分钟的技能投入我要打开数据库、新建条目、填关联字段、选技能标签、写日期走完这套流程学习的热情已经凉了一半。而且 Notion 的关系型数据表确实能统计汇总但一旦记录量过千页面加载变慢移动端录入更是难受。Excel 的问题是它把所有结构都摊在用户面前。没有固定约束就意味着每次维护都在创造新的不一致今天用Vue.js明天用vue下周又变成前端框架。等我想统计时间时光清洗名称就得花一个晚上。表格工具其实不是不能用它们的问题出在万能上——没有内置技能树熟练度等级时间投入这些领域概念所有规则都要用户自己制定并长期自律执行。而真实情况是人没法长期自律对待一张空白表格。我要的工具体验应该是花两三秒完成一次记录长期保持同样格式剩下的统计交给程序。1.3 于是决定自造一个窄工具与其在通用工具里反复造轮子不如做一个只属于这个场景的窄工具。这也直接定了skills的设计基调它是一个命令行工具终端永远是打开成本最低的地方。数据默认存在本机~/.skills/目录下一行命令完成记录。所有统计逻辑内置不需要我自己写公式。从那时起这个工具就从一个业余项目变成了我日常使用频率仅次于编辑器的软件。2. 本地优先的小数据哲学技术选型背后的取舍2.1 为什么用 Node.js TypeScript而不是 Python 或 Go技术选型没有标准答案只有场景答案。我当时的选择是 Node.js 加 TypeScript理由说出来其实很朴素生态里现成的 CLI 组件最全。commander 处理参数解析、chalk 输出带颜色文本、dayjs 处理日期这些库我都用过好几年基本没有学习成本。TypeScript 的克制力刚好落在小工具的量级上。为几十个 JSON 结构定义接口写起来不费劲重构时却能少踩很多脑壳疼的坑。单文件部署友好。用 esbuild 打包后是单个可执行文件换电脑时拷过去就能用不需要为了一个记笔记工具先装一套 Python 虚拟环境。Python 的 argparse 和 Typer 也很好但我在 Python 项目上的交互式开发体验不如 Node 顺手Go 适合更追求低内存的场景但开发速度不如 TS。在做这种个人工具时选择自己手指最熟悉的语言比选择理论最优的语言更重要。2.2 存储为什么选了 SQLite而不是 JSON 文件最开始的版本确实用 JSON 文件存储因为我笃信个人工具数据量小没必要上数据库。但随着记录累积JSON 方案的三个问题逐渐浮出水面并发写入会静默覆盖。偶尔开着多个终端窗口两个进程同时写skills.json后写入的会覆盖先写入的记录直接丢失。跨记录统计需要重复读全文件。月初想算上个月 Vue 投入多少小时程序得把整个 JSON 读进内存再逐条过滤随着文件增大操作越来越笨重。数据结构演进没有中间态。当我打算给技能记录增加来源标签字段时JSON 文件要么整体迁移要么程序里写一堆兼容分支。SQLite 把这三个问题一次性解决单文件依然适合本地优先但自带原子写和事务跨记录聚合用一条 SQL 完成不需要读全量数据增加字段只要ALTER TABLE加一列旧数据完全不用动。这个决策是我认为整个项目里做得最正确的一个。2.3 目录结构和 CLI 交互思路工具初始化后会生成这样的目录结构~/.skills/ ├── skills.db # SQLite 主数据库 ├── skills.json # 导出档案供外部工具和 AI 助手调用 └── reports/ └── 2026-Q1.md # 季度复盘报告CLI 交互遵循的核心理念是常用操作一条命令、非常用操作也不超过两条命令。我不做交互式 TUI因为技能记录这类行为追求的是完成速度不是操作界面的美观度。输入skills log vue4 45 -n 组合式API重构等价于在日历软件里创建条目、关联技能、写备注、选择时间范围这五六个动作的合成。这些选型决策都没有什么惊天动地的理由重点在于把每个选择的成本边界想清楚本地优先意味着数据永远在自己手里SQLite 保证了记录不丢命令行保证了录入速度。三者叠加起来才让这个工具值得长期用下去。3. 核心数据模型一套能支撑三年回顾的技能档案3.1 技能的三个维度分类树、熟练度、方向我一开始设计的表结构只有两列技能名称和熟练度分数。结果用了两周就发现不够——同样的Spring Boot在后端开发和微服务架构两种分类下完全是两种深度理解。后来我把技能拆成三个维度来建模分类树一级分类前端、后端、工程化、软技能等、二级标签框架、语言、理念等。分类树的作用是生成报告时能按层级聚合比如前端类别下面同时包含 Vue、React 和工程化知识统计时不会把三个不相关的东西搅在一起。熟练度等级一套五档分级详情见下表。目标方向每个技能可以设定期望达成的目标等级报告里会计算目标等级与当前等级之间的差距是否缩小了。熟练度五档是我反复调了很久的部分最初的版本是了解、掌握、精通这种含混词汇自评时毫无参考价值。后来改成行为化描述每一档都绑定一个当你处于这个阶段能做出什么的具体标准等级编码行为锚点L0 未入门只看过概念或教程没有独立产出过任何东西编码时完全需要照抄别人的代码L1 上手可用照着文档能做 Demo能完成单一功能模块运行报错时解决不了超出文档范围的问题L2 独立交付能独立完成完整功能模块能讲清楚关键设计取舍遇到同类项目任务不需要外部帮助L3 熟练调优能定位和解决复杂性能问题能指导他人使用该技能能根据业务场景做方案对比和选型决策L4 领域专家能在团队内建立方法论能基于该技能产出通用工具或规范能影响跨团队的技术方向我实测下来行为锚点的价值比想象中大很多。以前写对 Vue 比较熟悉现在用 L2 的锚点一对照发现只能完成单一组件模块还到不了独立交付完整页面的程度于是会明确知道自己缺的是系统性项目练习。3.2 三张表串起时间账本skills、logs、reviewsskills表是技能主表logs表是时间流水reviews表是熟练度变更履历。我把核心 DDL 贴出来结构很朴素但后续所有查询都建立在它上面CREATE TABLE IF NOT EXISTS skills ( id TEXT PRIMARY KEY, -- 8位短码如 vue4 name TEXT NOT NULL UNIQUE, -- 技能名 category TEXT NOT NULL, -- 一级分类 tags TEXT NOT NULL DEFAULT [], level INTEGER NOT NULL DEFAULT 0, -- 当前熟练度 target_level INTEGER NOT NULL DEFAULT 2, -- 目标等级 status TEXT NOT NULL DEFAULT active,-- active / archived created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, skill_id TEXT NOT NULL REFERENCES skills(id), occurred_on TEXT NOT NULL, -- 日期避免时区问题 minutes INTEGER NOT NULL, -- 投入分钟数 note TEXT, created_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS reviews ( id INTEGER PRIMARY KEY AUTOINCREMENT, skill_id TEXT NOT NULL REFERENCES skills(id), old_level INTEGER NOT NULL, new_level INTEGER NOT NULL, reviewed_at TEXT NOT NULL, note TEXT );把熟练度变更单独存一张reviews表是我在后来的复盘需求里补上的设计。没有它的时候我只能知道现在 L2却说不清三个月前是不是才 L0。有了这张表报告的成长速度一栏直接从历史记录里取数skills review vue4 --to 3 -n 完成一次完整后台管理系统开发执行后更新当前等级同时在reviews表里留下一行从 L2 到 L3 的变更记录。3.3 一份完整的技能档案长什么样抽象的结构说完了看一份实际输出的 JSON 更有感觉。这是skills.json里新增的一个技能片段{ id: vue4, name: Vue 3, category: 前端, tags: [框架, 组合式API], level: 2, target_level: 3, status: active, timeline: [ { date: 2026-01-12, minutes: 90, note: setup 语法糖和响应式原理 }, { date: 2026-03-02, minutes: 60, note: 组件通信方案对比 } ], reviews: [ { date: 2026-01-12, from: 1, to: 2, note: 完成购物车模块开发 } ] }这个 JSON 平时只有在需要给外部工具比如 AI 写作助手做分析时才手动导出日常记录永远不会直接编辑它——所有写入都经过命令入口这保证格式永远统一。3.4 为什么给每个技能分配一个固定 ID我在设计时坚持每个技能都要有一个 8 位短码 ID如vue4、tsad、sqlite而不是直接用中文名做关联。原因有两个命令行录入时减少击键和编解码问题。中文名输入慢而且在终端会话里涉及字体和编码问题一个vue4比切换输入法快得多。记录历史时不受改名影响。比如Vue3后来改成Vue 3 组合式API只要 ID 不变所有历史流水依然准确归到同一个技能下。这是数据库设计里主键不可变原则的具体应用。这个细节在最初一周看不出什么变化半年后当你想重命名技能时会发现它省下了一整晚的清洗数据时间。4. 命令行里的日常节奏add、log、next 怎么串起来4.1 init 和 add建立技能清单的正确姿势首次使用先初始化skills init这条命令会在~/.skills/生成数据库和配置文件然后就可以开始添加技能。添加一个技能的同时会要求指定分类和目标等级这样后续统计才有维度skills add Vue 3 --category 前端 --target 3 # 输出: 已添加技能 vue4当前等级 L0目标等级 L2我个人建议新技能刚开始时目标等级不要定太高。从 L0 直接对标 L4 会让人压力很大实际操作中往往坚持不到两周就放弃记录。按 L2 起步是更现实的选择——它意味着能独立完成一个项目模块大多数学习行为可以在几个月内达成这能维持反馈的节奏感。4.2 log一次记录只需要三秒日常使用中最频繁的操作是记录时间投入没有之一skills log vue4 -m 45 -n 组合式API重构练习 skills log tsad --date 2026-05-06 -m 30 -n 泛型约束理解支持三个可选项--date默认当天--minutes必填可以传 15、30、45 这类 15 分钟倍数--note随手写一句话备注。默认按 15 分钟粒度控制是有意的设计——低于 15 分钟的学习碎片很难形成实质产出强行记录只会让数据变噪。实测下来记录动作在肌肉记忆形成后大概三秒完成打开终端输入命令回车继续干活。这完整体验了一遍记录成本决定工具生死这句话凡是需要打开界面、点击新建、下拉选择的方案长期都不可持续。4.3 next把今天学什么的决策成本降到零我整理清单后另一个常见问题是技能一多不知道今天该练哪个。与其每天做选择我把排队逻辑写进工具里skills next 3next命令会输出三个推荐技能排序规则是当前等级低于目标等级的技能优先。最近一次有日志记录的时间最早优先避免某个技能被遗忘超过两周。同等级下累计投入时间更少者优先。这本质上是一种贪心调度先补最短缺的技能。很多人以为自己的拖延是执行力问题其实大部分时候是选择成本太高。next输出三个选项而不是一个是保留一点自由度避免工具变成硬约束。我一般会直接从第一条开始学如果今天状态明显不适合才看向第二条。4.4 view查看单个技能的历史全貌输入skills view vue4输出一张包含全部记录的纵向历史视图技能: Vue 3 [前端] 当前等级: L2 目标等级: L3 最近活跃: 2026-05-02 累计投入: 24小时30分钟 时间分布: 2026-01 4小时30分钟 2026-02 6小时15分钟 2026-03 8小时0分钟 2026-04 4小时45分钟 2026-05 1小时0分钟 最近日志: 05-02 45分钟 组合式API重构练习 04-15 60分钟 组件通信方案对比单看一条记录没有感觉但累积到这种视图时我经常发现我以为自己花了大量时间实际上只投入了 6 个多小时。这种数据比任何自我感觉都诚实它会直接纠正我下个月的安排方式。5. 季度复盘报告从时间账本里算出技能成长5.1 报告里到底看什么指标我每季度跑一次skills report --from 2026-01-01 --to 2026-03-31生成的 Markdown 报告包含五个部分总投入概览按分类聚合的投入时间回答这三个月的时间都流向了哪里。熟练度变化从reviews表取数列出本季度发生等级跃迁的技能没有跃迁的不会出现在这一栏。差距分析仍在活跃状态、但投入不足 10 小时且与目标等级差距 ≥ 2 的技能列表。荒废清单季度内零投入的活跃技能。这类技能会自动被标记为待归档避免清单无限膨胀。投入与产出对比每个技能平均每投入 1 小时时间熟练度提升是否合理不完全准确但作为指标足够。这五个部分本质上是把季度总结需要的数据全部自动准备好我不需要抱着日历和手账熬夜回忆。5.2 两条最核心的 SQL 查询报告的逻辑核心是两组 SQL 查询其他部分都是从它们延伸出来的变体。第一组是按月聚合时间投入SELECT strftime(%Y-%m, occurred_on) AS month, s.category, SUM(minutes) AS total_minutes FROM logs l JOIN skills s ON l.skill_id s.id WHERE occurred_on BETWEEN 2026-01-01 AND 2026-03-31 GROUP BY month, s.category ORDER BY month, total_minutes DESC;第二组是找出等级跃迁记录SELECT s.name, r.old_level, r.new_level, r.reviewed_at, r.note FROM reviews r JOIN skills s ON r.skill_id s.id WHERE r.reviewed_at BETWEEN 2026-01-01 AND 2026-03-31 ORDER BY r.reviewed_at;这两条 SQL 加起来不到 20 行但它们替代了过去一个季度末才做的所有手工统计。我自己最大的感受是数据可视化不重要能跑出准确可复用的查询才是核心价值。5.3 一次真实的复盘结论长什么样举一个我自己的例子。2026 年第一季度报告显示我在前端框架上投入了 26 小时其中 Vue 3 占 18 小时从 L1 跃迁到 L2但同时段我在算法基础上的投入为 0 小时而这个技能的目标等级是 L3。这个数据直接回答了三个问题为什么 Vue 3 熟练度提升了因为投入时间确实到位。为什么算法一直停留在 L1因为根本没分配时间。下一季度最该补哪个不是继续堆 Vue 3而是给算法分配固定时段。如果没有这套数据我季度末大概率会写在 Vue 3 上进行了深入学习然后继续忽略算法。复盘报告的最大价值不是给我吹牛素材而是让时间流向变得可审计。5.4 给报告加一层 AI 可读的导出报告还需要被进一步分析的时候比如把季度总结改写成个人年度总结我直接导出 JSON 格式skills export --from 2026-01-01 --to 2026-03-31 2026-Q1.json这份 JSON 包含了查到的所有统计数据、技能清单和时间线可以把它发给 AI 助手让它基于数据生成一段有论据支撑的总结文字。实测效果比直接口头描述可靠得多——因为所有结论都有数字兜底AI 生成的文本能准确提到累计投入 18 小时完成从 L1 到 L2 的跃迁而不是写持续学习、不断进步这种空话。6. 踩坑记录与下一轮的改造方向6.1 坑一按天记录的粒度太粗复盘时直接失真第一个版本里我设计的记录最小单位是天skills log vue4 120表示今天在 Vue 4 上投入了 120 分钟。听起来合理实际用下来发现令人困惑今天学到一半跑去做别的事晚上回填记录时根本算不清中间发呆和查资料的几分钟要不要算进去。后来改成按任务会话记录每次登录一个明确的学习片段保持在 15 到 60 分钟之间。一个45 分钟的组合式 API 重构练习是完整单元的记录远超今天学了 2 小时这种模糊表述。如果你也想做类似的工具这个粒度设计建议一开始就做对。6.2 坑二熟练度自评的锚定效应第二版里也许我把 L1 到 L4 定义成了不熟、一般、熟练、精通结果自评时永远往高里靠——写精通会被问穿写一般又显得不甘心。后来才改成现在这版行为化描述。没有任何一套自评体系能完全消除主观性但把每一级绑定到具体能做什么之后主观空间被极大地压缩了。我每次做等级变更前还会问自己一句话最近一个月有没有独立完成过符合这个锚点的产出没有就不升。6.3 坑三技能清单膨胀导致维护负担用了三个月之后我的 active 技能涨到了 47 项每天光看next建议就很有压力。后来加上了archive命令把暂时不学、但未来可能捡起来的技能冻结skills archive rust --note 等做系统编程项目时再激活归档后的技能不再参与next推荐也不进入季度报告但历史记录仍然保留。技能管理本质是精力管理的一部分清单也应该像衣橱一样定期清理。每个季度末的归档日现在是我固定的仪式。6.4 下一轮改造方向技能依赖和日历导入目前这个版本已经陪我跑了好几轮季度复盘下一个版本我计划加两个能力技能依赖关系表。比如学 Vue 3依赖JavaScript ES6next推荐时会优先推荐依赖链更底层的技能避免出现基础没打好直接冲高级框架的无效投入。日历导入。很多人包括我自己已经有日历工具下一步是从 iCal 源自动导入开发时间降低手动登记的频率。这两个方向都会保持本地优先、命令行为主的架构不变。唯一的新设计是依赖关系的建模——它更像是一个有向图而不是简单的 tag 列表得在现有的三张表基础上增加一张skill_deps表。我不急着把功能一次做满因为按季度迭代的节奏也许更能让这个工具保持轻量。最后说一点使用体会skills 这类工具的价值不在于熟练度评估有多精准而在于让时间流向这件事变得可观测。过去三个月我最大的变化不是某个技能从 L1 涨到 L3而是终于能指着报告说——我没在这上面花时间所以没进步这份实打实的数据比任何计划表都更能校正行动。