以任务为中心浏览工作记录、沉淀经验与成果的本地 Markdown 工作台|作者个人站:https://softwarescience.top/projects/tasklens
当前访问频次受限,请登录后继续访问
TaskLens · 任务透镜
作者个人站:https://softwarescience.top/ · 本项目主页:https://softwarescience.top/projects/tasklens(版本、截图与下载都在那儿)
从任务视角看待工作、经验与成果的本地 Markdown 工作台。
作者 创意 / 创建者:CodeLife Leon 开发者:OMP
Tolaria 以「笔记」为中心浏览知识库;TaskLens 以「任务」为中心:选中一个任务,就能同时看到它的工作记录(每日日志里提到它的内容)、沉淀出来的经验笔记、产出的成果笔记,以及所有引用它的笔记。
工作日志 ──┐
经验笔记 ──┼──► 任务 ──► 成果笔记
关联笔记 ──┘
- 数据即文件:笔记仍然是 vault 里的
.md,与 Obsidian / Tolaria 完全兼容,不锁定格式。 - 结构化存储:SQLite 索引库保存派生数据(笔记索引、链接图)与真实历史(任务变更事件),随时可删可重建。
- 技术架构与 Tolaria 一致:Tauri v2 + React 19 + TypeScript + Vite 7 + Tailwind v4 + CodeMirror 6。
- 一套核心,两种入口:同一个 Rust 核心既通过 Tauri IPC 驱动桌面窗口,也通过
tasklens-cli提供 stdio JSON 接口(浏览器调试与后续 agent 集成共用)。
目录
TaskLens/
├── core/ # 纯 Rust vault 核心(无 Tauri 依赖,可独立测试)
│ ├── src/frontmatter.rs # 逐行手术式 frontmatter 读写(保留注释、引号、行尾、键序)
│ ├── src/vault.rs # 扫描、索引、读写、链接提取、搜索
│ ├── src/brief.rs # task_brief:一次调用拿全任务上下文(AI 的信息梳理原语)
│ ├── src/db.rs # SQLite 索引 + 事件日志
│ ├── src/git.rs # Git 同步(--ff-only 拉取、永不强推)
│ ├── src/agent.rs # Pi agent 会话:内置宿主(SDK)或外部 `pi --mode rpc`,双向 JSONL
│ ├── src/mcp.rs # MCP server(stdio JSON-RPC,17 个工具)
│ ├── src/extension.rs # 内嵌 Pi 扩展源码,一键安装到 vault(外部命令用)
│ ├── src/settings.rs # 安装级设置(vault、主题、Git、AI;API Key 只写不读)
│ ├── src/dispatch.rs # 唯一命令表(Tauri / CLI / MCP 共用)
│ └── src/bin/ # tasklens-cli、tasklens-mcp
├── src-tauri/ # 桌面外壳:命令转发 + vault 文件监听 + agent 进程
├── agent-host/ # 内置 agent 宿主:用 pi SDK 起会话,工具走 customTools
├── pi-extension/ # pi.dev 扩展:同一批 vault 工具的外部挂载形态(运行时零依赖)
├── src/ # React 前端
│ ├── lib/ipc.ts # 唯一 IPC 边界(桌面 invoke / 浏览器 HTTP 同一套命令)
│ ├── lib/agent.ts # agent 传输层(桌面事件 / 浏览器 SSE,同一份事件流)
│ ├── lib/vault.ts # 派生模型:类型分桶、链接解析、任务上下文
│ ├── lib/ai-prompts.ts # 知识闭环快捷动作(填报 / 梳理 / 沉淀 / 周报 / 体检)
│ ├── state/ # useVault、useNoteDocument、useGit、useSettings、useAgent
│ └── components/ # 侧栏 / 列表 / 看板 / 任务工作台 / 日志页 / AI 面板 / 设置
└── tools/ # 图标生成、vault 调试、agent 与 MCP 冒烟脚本
运行
依赖:Node 22+ 或 bun、Rust 工具链;内置 AI 宿主需要一个 JS 运行时(bun 优先,退回 node)。
npm install || bun install # 仓库带 bun.lock,两种都行
npm run tauri:dev # 桌面窗口
npm run dev # 只起浏览器调试版(需 TASK_LENS_VAULT=/path/to/vault)
脚本一律写成直接命令(脚本之间不做包管理器嵌套),所以 npm run … 与 bun run … 行为完全一致:
| 脚本 | 作用 |
|---|---|
dev |
打包内置宿主 agent-host/host.mjs → 编译 tasklens-cli → 起 vite(浏览器调试) |
build |
同上 + tsc -b + vite build,产出前端 dist/ |
tauri:dev / package |
桌面开发 / 桌面打包(会额外准备 sidecar 与宿主资源,详见「打包与发布」) |
build:host / build:cli / build:sidecar |
单独重建内置宿主 / CLI / 打包用 sidecar |
test / typecheck / check |
前端测试 / 类型检查 / Rust 测试 + 类型检查 + 前端测试 |
core:build / core:test |
编译 tasklens-cli+tasklens-mcp / 只跑 Rust core 测试 |
AI 面板默认走内置 Pi 组件:应用用 pi SDK 在本地起一个 agent 宿主进程(agent-host/index.ts),由本机 JS 运行时执行——需要 bun 或 node 之一(bun 优先)。在「设置 → AI 助手」里选模型、填 API Key 即可对话,不需要安装 pi 命令。
agent-host/host.mjs(宿主单文件 bundle)与 src-tauri/binaries/tasklens-cli-*(sidecar)是构建产物,由 npm run build:host / npm run build:sidecar 生成;tauri dev|build 会自动执行,只跑前端时可以手动执行。
想改用别的 agent CLI,就把「外部命令」填上(例如 pi);此时才会用到「安装到 vault」写入的 .pi/extensions/tasklens.ts。
浏览器调试模式需要指定 vault:
TASK_LENS_VAULT=/path/to/vault npm run dev
桌面模式则在应用内选择目录,选择结果记入安装级设置(%APPDATA%/top.softwarescience/settings.json)。
标识符为
top.softwarescience(src-tauri/tauri.conf.json的identifier,同时决定配置目录与安装包身份)。改名前的com.sundata.tasklens目录仍会被读取一次作为兜底,所以升级后 vault 列表、主题、API Key 不会丢;下一次改动设置时自动写到新目录。设了TASK_LENS_CONFIG_DIR时不会读旧目录(隔离开发/测试用)。
打开一个空目录时会问一次「初始化这个目录?」:
- 可勾选要生成什么——每种类型的标准目录、
templates/<类型>模板.md模板、AGENTS.md(写给 AI/agent 的 vault 约定);默认全选。 - 已存在的文件一律跳过、从不覆盖(幂等,重复点也不会改坏东西)。
- 目录与文件名来自「设置 → 类型」,所以自定义类型同样会被建好。
- 选「暂不」会记住这个 vault(设置里的
initDismissed),不再打扰;空目录里自己加了笔记后也不会再问。
打包与发布
npm run package # release 编译 + 生成安装包(Windows 上默认 NSIS + MSI)
打包入口都在 package.json(含 "//" 注释,说明每一步做什么、产物在哪、有哪些坑):
| 脚本 | 等价命令 | 用途 |
|---|---|---|
package |
tauri build |
打包安装包(默认 NSIS + MSI) |
package:nsis / package:msi |
tauri build --bundles nsis / --bundles msi |
只打某一种安装包 |
package:exe |
tauri build --no-bundle |
只出 tasklens.exe,不打安装包(最快) |
package:debug |
tauri build --no-bundle --debug |
debug 产物,编译快,产物在 target/debug |
一条命令包含四步,全部在 package.json / src-tauri/tauri.conf.json 里声明,不需要手工准备:
| 步骤 | 做什么 |
|---|---|
1. beforeBuildCommand |
bun run build(= bun build agent-host/index.ts --target=node --outfile=agent-host/host.mjs 把内置 AI 宿主打成单文件 ≈12 MB,再 tsc -b + vite build)+ bun run build:sidecar(node tools/prepare-sidecar.mjs,按 rustc -vV 的 host triple 把 tasklens-cli 拷成 src-tauri/binaries/tasklens-cli-<triple>.exe) |
| 2. 前端 | dist/(由上一步的 vite build 产出) |
| 3. Rust | release 编译 → target/release/tasklens.exe(首次约 2 分钟) |
| 4. 打包 | bundle.resources 带上 agent-host/host.mjs、bundle.externalBin 带上 tasklens-cli.exe,都放进安装目录 |
产物(Windows,文件名含版本与架构):
| 文件 | 大小(参考) | 说明 |
|---|---|---|
target/release/bundle/nsis/TaskLens_<版本>_x64-setup.exe |
~7 MB | NSIS 安装包(推荐) |
target/release/bundle/msi/TaskLens_<版本>_x64_en-US.msi |
~9 MB | MSI 安装包(便于域内部署) |
target/release/tasklens.exe |
~14 MB | 免安装运行用;旁边必须同时有 tasklens-cli.exe 与 agent-host/host.mjs(Tauri 构建时已 staged 在 target/release/ 下) |
需要交叉编译或其它参数时,仍可直接透传:
npm run package -- --target aarch64-pc-windows-msvc
注意事项:
- 首次打包会联网下载 NSIS 3.11 与 WiX 3.14 工具链(各几十 MB,之后走缓存)。
- 目标机器:Windows 10/11;Win11 自带 WebView2,Win10 可能需要装 WebView2 Runtime;AI 功能需要一个 JS 运行时(
bun优先,退回node)。 - 未做代码签名:安装时 SmartScreen 会提示「未知发布者」,正式分发需自行配置证书(见下)。
- 打包前建议关掉正在运行的
tasklens.exe/tasklens-cli.exe,否则 Windows 会锁住target/里的二进制导致编译失败。
代码签名(可选)
证书不在仓库里,需要向 CA 申请、把签名接入构建。先认清 SmartScreen 的现状(官方说明):
- EV 不再免 SmartScreen:微软已取消「EV 证书自动获得正声誉」,为绕过 SmartScreen 而多花钱买 EV 已不划算(企业采购另说)。
- 签名后首次下载仍可能提示,但显示的是已验证的发布者名而不是「未知发布者」;声誉随下载量按发布者身份累积,之后签名的新版本可以继承。
- 不签名则每个版本都从零开始攒声誉,且 Win11 的 Smart App Control 会直接拦下无声誉的未签名可执行文件。
三条路线,按代价从低到高:
| 路线 | 成本 | 适用 |
|---|---|---|
| 不签名 + 内部分发 | 0 | 只在自家/客户内网装:把证书或安装包放到内网共享(Trusted Intranet),域内用 GPO 部署 MSI,管理员也可在 Microsoft Security Intelligence 提交文件加速信任 |
| Azure Artifact Signing(原 Trusted Signing) | Basic $9.99/月(5,000 次签名)起 | 个人/小团队首选:证书由微软托管在 FIPS 140-2 L3 HSM,不需要买硬件令牌,可直接接 CI;但需先通过身份验证(个人或组织),审核由第三方执行,材料/周期要有预期 |
| CA 的 OV/EV 代码签名证书 | 约 $200–300/年起(各 CA 报价与主体审核要求不同) | 需要正规「发布者名称」对外分发;自 2023-06 起私钥必须存放在 FIPS 140-2 L2+ 硬件令牌或云 HSM,拿不到裸 .pfx |
接好之后在 src-tauri/tauri.conf.json 的 bundle.windows 里二选一(键名以本地 @tauri-apps/cli/config.schema.json 为准):
// A. 令牌 / 证书store 里的证书(signtool 路线)
"windows": {
"certificateThumbprint": "<证书指纹 SHA-1,去掉空格>",
"digestAlgorithm": "sha256",
"timestampUrl": "http://timestamp.digicert.com" // RFC3161 时间戳,证书过期后签名仍有效
}
// B. 云签名 / 托管签名:让 Tauri 调你自己的签名命令,%1 是要签的文件
"windows": {
"signCommand": "trusted-signing-cli -e https://<region>.codesigning.azure.net -a <账号> -c <证书配置名> -d TaskLens %1"
}
- 不要用
npm run tauri signer generate想着签 exe——那是 Tauri 自动更新(minisign)用的密钥,和 Authenticode 是两回事;本项目没启用 updater,暂时用不到。 - 签名后自查:
"C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe" verify /pa /v target\release\bundle\nsis\TaskLens_0.1.0_x64-setup.exe(本机 Windows SDK 已装,signtool只是不在 PATH 上)。 - 证书换新(续费/换 CA)会重置发布者声誉,续签时尽量沿用同一主体与同一张证书。
验证
npm run check # Rust 测试 + 类型检查 + 前端测试(一条命令全跑)
cargo test -p tasklens-core # 82 个 Rust 测试:frontmatter 手术、索引增量、事件、Git 全流程、设置校验、task_brief、改名/移动、agent 启动参数
npm run test # 111 个前端测试:markdown 行号、链接解析、任务上下文、日志日期、笔记创建、对话状态机、设置合并
npm run typecheck # 类型检查(含 pi-extension、agent-host)
冒烟脚本(真实进程,不是 mock):
node tools/mcp-smoke.mjs .dev-vault target/debug/tasklens-mcp.exe # MCP 握手 + 17 工具 + 错误码
node tools/agent-smoke.mjs .dev-vault "用 vault_task_brief 看看某个任务" # 真机 agent:起内置宿主、调工具、给答案
手动验证建议先复制一份 vault,避免测试写入真实笔记:
mkdir -p .dev-vault && cp -r <vault>/{任务,工作日志,经验,成果,templates,views} .dev-vault/ # 按你 vault 的实际目录取
TASK_LENS_VAULT=$PWD/.dev-vault TASK_LENS_CONFIG_DIR=$PWD/.dev-config npm run dev
TASK_LENS_CONFIG_DIR 把安装级设置(vault 列表、主题、Git 自动同步)写到指定目录,这样开发/测试不会动到真实安装的配置。
辅助脚本:
bun tools/inspect-cli.mjs <vault> '{"cmd":"list_vault","args":{}}'— 直接调用核心 CLI 查看扫描结果bun tools/make-icon.mjs app-icon.png && bunx tauri icon app-icon.png— 重新生成应用图标
开发手册
面向想自己改这个项目的人。先读后面的「存储设计」「Vault 约定」两节了解数据模型,这一节只讲怎么加东西。
0. 架构一页图
┌─ Tauri IPC(vault_command 统一转发)
前端 src/ ──────────┼─ POST /__vault/cmd(浏览器调试,tools/vault-dev-server.ts)
└─ /__agent/*(SSE,agent 事件)
│
core::dispatch::run ← 唯一命令表,三种入口共用
│
┌─────────────────────┼──────────────────────┬───────────────┐
vault.rs frontmatter.rs git.rs db.rs
扫描/读写/搜索 逐行 YAML 手术 同步(不强推) SQLite 索引 + 事件
│
agent 侧:pi 扩展(pi-extension/tasklens.ts) ─spawn→ tasklens-cli ─┘
MCP server(core/src/mcp.rs) ────────→ dispatch::run ─┘
一条铁律:任何写入都先落盘、再 reindex(vault.rs::edit_body 是唯一收敛点),索引 .tasklens/tasklens.db 随时可删可重建,只有 events 表是不可重建的历史。
1. 加一个 vault 命令(Rust → 界面)
| 步骤 | 落点 | 说明 |
|---|---|---|
| 1 | core/src/dispatch.rs run() 的 match command |
加分支,参数用 root_for/string_arg 等助手,返回值 as_value(...) |
| 2 | 同文件 COMMANDS 常量 |
只影响 commands 自省输出,run() 不校验它(task_brief 就没登记)——但请登记,免得漏 |
| 3 | — | Tauri 零改动:src-tauri/src/lib.rs:19 的 vault_command 统一转发,别新增 #[tauri::command] |
| 4 | — | tasklens-cli 零改动:core/src/bin/tasklens-cli.rs 逐行调 dispatch::run |
| 5 | core/src/mcp.rs 的 TOOLS |
给 MCP 客户端加一条 Tool{name, description, command, params} |
| 6 | pi-extension/tasklens.ts 的 VAULT_TOOLS |
给 Pi agent 加一条 ToolDefinition,execute 里 invoke("<命令名>", args, …) |
| 7 | src/lib/ipc.ts 的 vaultApi |
加一个包装:readNote: (path, root?) => command<NoteContent>("read_note", { path, root }) |
最小例子(dispatch.rs):
"list_vault" => {
let root = root_for(args, default_root)?;
let entries = vault::scan(&root)?;
Ok(json!({ "root": root.to_string_lossy(), "entries": as_value(entries) }))
}
命令名与工具名不是一回事:vault_read → read_note、vault_write → save_note、vault_toggle_checkbox → toggle_checkbox……映射表见「命令表」与 core/src/mcp.rs。
2. 加 MCP / Pi 工具
- MCP:
Tool{ name, description, command, params },param(name, kind, required, forward, description)。类型只认string/integer/array/object(没有 boolean/number);forward=false表示这个参数不传给命令、由工具侧自己消化(vault_list的type就是这样过滤的)。 - Pi:
parameters用 typebox 的Type.Object({...}),execute里先做本地校验(checkPath/checkLine),再invoke("<命令名>", args, signal, formatter)。 - 测试是硬契约:
core/src/mcp.rs与pi-extension/tasklens.test.ts都断言「恰好 17 个工具、名字与顺序」——加/删工具必须同步这两处,否则npm run check直接红。
3. 加一个左侧视图(lens)
lens 是纯前端概念,固定四处:
src/lib/lenses.ts:LensKind联合加名字;lensTitle()加case(穷尽 switch,TS 会报缺分支)。src/components/ListPane.tsx:取数(filterNotes(index.byType.get(lens.type) ?? [], query))、count三元链、渲染分支(可复用NoteCardList)。src/components/Sidebar.tsx:加一个Chip(active/onClick/count)。src/App.tsx:状态与透传已就绪,通常不用改;只有新视图的右侧详情要加分支(按selected.noteType分派)。
多数新类型不需要动 lens:设置里新增一条类型(行为 note)后,侧栏会自动多一个入口,列表读 index.byType,详情页由 NoteView + NoteSections 承接——只有需要全新交互(像看板那样)时才走上面四步。
4. 加一个设置项
Rust(core/src/settings.rs)五件套,缺一个就会出现「界面存了、重启丢了」:
- 字段进
Settings/AiSettings(#[serde(rename_all = "camelCase", default)]); Default给默认值;apply()加分支并校验(复用enum_value/number_value/boolean_value/note_path,错误信息中文,未知键忽略);Settings::load()里加磁盘归一化(非法值回落默认);settings_for_client():普通字段自动带出,敏感字段必须像api_key一样手动剔除(apiKeySet只回传真假)。
前端三层同步:src/types.ts(类型 + SettingsPatch)→ src/state/useSettings.ts(FALLBACK 默认值必须与 Rust Default 对齐)→ 设置界面(顶层放 SettingsDialog.tsx,类型放 TypeSettingsSection.tsx,AI 放 AiSettingsSection.tsx)。mergeSettings 对嵌套对象做浅合并;数组字段(如 types)是整表替换。
5. 笔记类型 / 目录 / 模板
- 类型是用户维护的数据(
Settings.types: NoteTypeDef[]),不是代码常量:{ type, label, color, folder, template, namePattern, behavior, published }。默认五条在src/lib/constants.ts的DEFAULT_TYPES(Rust 侧settings.rs::default_types()必须同步),运行时统一走src/lib/noteTypes.ts(typeDefs/typeDef/typeColor/folderForType/templateForType/nameForType/moveTargetForType/initialContent)。 - Rust 侧没有类型枚举,一律按 frontmatter
type:字符串处理(core/src/brief.rs里对日志/经验/成果做字面量匹配,其余类型自然落到backlinks);behavior只有task/log/note,且task的 id 必须是任务、log的必须是日志——这条约束在 Rust 校验里,改前端也要守住。 - 颜色通过
syncTypeColors()(useSettings在设置变化时调用)同步给不看 settings 的哑组件(卡片、搜索命中、徽标),组件里统一用typeColor(type)。 - 新建笔记的落点与内容见「类型与模板」一节:
folderForType()决定目录(配置 →vault.ts::defaultFolderFor的"该类型的家" →TYPE_FOLDERS),createTypedNote()(src/lib/createNote.ts)是唯一的建笔记路径(新建对话框、每日日志、待办→任务都走它),initialContent()决定用模板文件还是内置模板。 - 类型目录下面的子目录只是分类:
defaultFolderFor取的是"该类型笔记最多、且父目录不再是该类型的家"的目录,moveTargetForType()保证标记类型时不会把已有的子分类拍平。
6. 测试与验证
| 层 | 做法 |
|---|---|
| Rust | 单测写在各文件内的 #[cfg(test)] mod tests(无独立 tests/)。需要 vault 时 tempfile::tempdir() 手工建目录写文件,范本见 core/src/mcp.rs 的 vault() 助手 |
| 前端 | vitest,测试与源码同目录 *.test.ts(x),纯函数优先;src/test/setup.ts 只装 jest-dom |
| 一条命令 | npm run check(= cargo test -p tasklens-core + tsc -b + vitest run) |
| MCP 冒烟 | node tools/mcp-smoke.mjs .dev-vault target/debug/tasklens-mcp.exe |
| Agent 冒烟 | node tools/agent-smoke.mjs .dev-vault "用 vault_task_brief 看看某个任务" deepseek deepseek-v4-flash |
| dev fixture | TASK_LENS_VAULT=$PWD/.dev-vault TASK_LENS_CONFIG_DIR=$PWD/.dev-config npm run dev(.dev-vault 是示例 vault,.dev-config 隔离安装级设置) |
7. 调试技巧与坑
调试
- 桌面窗口开远程调试:
WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS=--remote-debugging-port=9333再启动应用,然后用 CDP 连接即可看 DOM / 截图 / 执行 JS。 tauri dev下前端改动走 HMR(改完立刻生效);Rust 改动会自动重编译重启。- 直接摸核心:
bun tools/inspect-cli.mjs .dev-vault '{"cmd":"list_vault","args":{}}'。 - 起不来时先清残留:
powershell -NoProfile -Command "Get-Process tasklens,tasklens-cli -ErrorAction SilentlyContinue | Stop-Process -Force"(否则target/debug里的二进制被锁,cargo 报「拒绝访问」);vite 端口 1420 被占也会让 dev 起不来。
必须知道的坑
| 坑 | 说明 |
|---|---|
| frontmatter 是逐行手术 | core/src/frontmatter.rs 从不整体重排 YAML:每个字段保留原行、注释、引号、键序、CRLF/BOM、末尾换行。永远不要用 serde 重新序列化整个 frontmatter |
| 行号是绝对文件行 | set_body_line / delete_body_line / insert_body_line / toggle_checkbox 收的都是 1-based 含 frontmatter 的行号;删/插会让后面的行号前移,连续操作要先重读或从大到小 |
| 路径规则 | vault.rs::resolve 拒绝绝对路径与任何非普通组件(.././根),对外统一正斜杠;无 canonicalize,符号链接不在防护范围内 |
| 索引可重建、事件不可 | notes/links 是派生缓存,schema_version 不符就重建;events 是唯一真实历史,别乱删 |
.tasklens/ 不进 git |
索引目录由 core/src/git.rs 写进仓库 info/exclude(不改 vault 的 .gitignore),也不会被扫描 |
| MCP 参数类型受限 | 只有 string / integer / array / object;用 boolean 会永远校验失败 |
| Windows 进程 | 优先真实 .exe 而不是 .cmd 垫片(垫片要过 cmd.exe,会丢 stdio);结束后代要 taskkill /T 杀整棵树——agent 会话已按此实现(core/src/agent.rs) |
命令表
tasklens-cli 与 Tauri IPC 共用同一套命令(core/src/dispatch.rs):
| 分组 | 命令 |
|---|---|
| vault | list_vault reload_entry read_note save_note search task_brief |
| frontmatter | update_frontmatter create_note trash_note |
| 文件名 / 位置 | rename_note(folder 可选:省略=原地改名,""=移到知识库根,其余为相对目录;指向旧路径的 wikilink 一并改写) |
| 文件 | create_folder(含多级,已存在视为成功) scaffold(批量建目录 + 只补缺失文件,用于初始化空 vault) |
| 正文 | append_body_item toggle_checkbox set_body_line insert_body_line delete_body_line replace_section |
| 索引 / 事件 | list_events |
| 设置 / AI | get_settings update_settings set_theme add_vault remove_vault ai_extension_install |
| Git | git_status git_init git_set_remote git_fetch git_pull git_commit git_push git_sync |
AI 助手(内置 Pi)
对话是核心:面板里和 agent 说话,它调用 vault 工具去读、搜索、按行改写,完成任务填报 → 信息梳理 → 经验/成果沉淀的闭环。
怎么接的
| 层 | 做法 |
|---|---|
| Agent 进程 | core/src/agent.rs 启动 agent:默认是内置宿主(bun/node 跑 agent-host/index.ts,配置经 stdin 的 start 下发);填了外部命令才改走 pi --mode rpc。工作目录 = 当前 vault,stdin 发命令、stdout 收事件(JSONL)。Windows 上裸命令若是 .cmd 会经 cmd.exe 启动。 |
| 桌面 | Tauri 命令 agent_start/send/stop + agent-event 事件流。 |
| 浏览器预览 | dev server 起 tasklens-cli --agent-server,命令走 POST、事件走 SSE。 |
| 前端 | lib/agent.ts 屏蔽两种传输;state/agentEvents.ts 把 pi 的 delta/工具事件映射成对话(纯函数,可单测)。 |
给 agent 的工具
pi-extension/tasklens.ts(应用内嵌,设置里一键安装到 <vault>/.pi/extensions/)注册 17 个工具,全部经 tasklens-cli 落到同一套 vault 语义:
vault_list vault_read vault_search vault_task_brief vault_events vault_create_note vault_write vault_update_frontmatter vault_replace_section vault_append_item vault_set_line vault_insert_line vault_delete_line vault_toggle_checkbox vault_git_status vault_git_sync
外加四个命令:/tasklens-file(填报任务)、/tasklens-today(梳理今天)、/tasklens-distill(沉淀经验与成果)、/tasklens-sync。
面板里的快捷动作
「填报任务」「梳理今天」「沉淀经验与成果」「写周报」「整理知识库」——每个都是一段写好的提示词,明确告诉 agent 用哪些工具、遵循什么 vault 约定;点一下填进输入框,确认后再发送(不会未经确认就改文件)。任务页和日志页也有直达按钮(AI 沉淀 / AI 梳理)。
模型与凭证(设置 → AI 助手)
- 运行方式:默认内置 Pi 组件(只挑 JS 运行时 bun/node,设置里显示探测到的路径);填「外部命令」则改为启动该 CLI。
- Provider / 模型 / 思考强度:留空则沿用 pi 自己的配置(
pi --list-models可查可用模型)。 - API Key:只写不读——写进本机设置文件,界面只显示「已保存」,任何客户端都拿不回明文。
- 自定义端点:填写 OpenAI 兼容地址(自建或代理)时通过
OPENAI_BASE_URL传给模型客户端。 - 其他:会话目录、附加参数、是否读取 vault 的
AGENTS.md、是否信任 vault 内的.pi资源。内置模式不自动发现.pi/extensions,只加载列表里显式挂载的扩展(避免工具重名)。
MCP server
cargo build -p tasklens-core --bin tasklens-mcp
stdio JSON-RPC(MCP 2024-11-05),17 个工具与上表同名同参。注册示例:
{
"mcpServers": {
"tasklens": {
"command": "D:/Develop/workspace/Files/Tolaria/TaskLens/target/debug/tasklens-mcp.exe",
"args": ["--vault", "D:/Develop/workspace/Files/Tolaria/SundataWork"]
}
}
}
这样 Claude Code / Cursor 等 MCP 客户端也能读任务上下文、检索、按行改写这个 vault。
存储设计
笔记是事实来源,索引是派生物。
| 层 | 位置 | 说明 |
|---|---|---|
| 笔记 | <vault>/**/*.md |
人可读、可 Git、可被 Obsidian / Tolaria 同时编辑 |
| 索引 | <vault>/.tasklens/tasklens.db |
SQLite;删除后下次扫描自动重建 |
| 事件 | 同上 events 表 |
唯一不可重建的历史:谁在什么时候把哪个字段从什么改成了什么 |
SQLite 架构(core/src/db.rs):
notes(path, …frontmatter json, links json, body, snippet, modified_ms, size, …)— 扫描缓存;只有mtime/size变化的文件会被重新解析。links(source, target)— 链接图,用于反向链接与任务上下文。events(path, kind, detail json, at_ms, source)— 生命周期事件;source区分写入者(app或未来的 agent)。meta(schema_version)— 版本不匹配时重建派生表,保留events。
写入路径始终是 先落盘、再更新状态:frontmatter 逐行改写,正文与其它字段字节不变;写完后单文件重新入索引。
Vault 约定
术语:界面上一律叫**「知识库」;代码里保留 Obsidian 的术语
vault(vault.rs、VaultIndex、vaultApi、list_vault、vault_*工具、--vault/TASK_LENS_VAULT)。两者指同一个东西:装着全部笔记的那个根目录。它不是**密钥保险库,也不加密任何内容。
目录只用于组织,笔记类型一律看 frontmatter 的 type:。下表是默认目录(类型同名目录,落在知识库根下),都可以在「设置 → 类型」里改(留空则跟随知识库现状:已有该类笔记就放它们所在的目录,否则用默认值)。
| type | 默认目录 | 关键字段 |
|---|---|---|
任务 |
任务/ |
status priority category created owner related_to |
日志 |
工作日志/ |
date |
经验 |
经验/ |
kind status created related_to(指向任务) |
成果 |
成果/ |
kind status created related_to(指向任务) |
记忆碎片 |
记忆碎片/ |
created related_to |
- 目录只做分类,类型只看
type::类型目录下面可以再分子目录(经验/踩坑/、任务/客户项目/),子目录里的笔记仍然是该类型,TaskLens 不会因为目录不同就换类型,标记类型时也不会把已有的子分类拍平。 - 新建笔记落在哪里:配置的目录 → 该类笔记最多的那个目录(若它的父目录里也直接放着同类型笔记,就继续往上,直到那个目录不再是该类型的"家")→ 默认表。所以
产研工作/工作任务/有 12 篇、产研工作/产品研发/有 1 篇时,新任务仍然落在产研工作/工作任务/。 - 链接使用 wikilink:
related_to: ["[[软著工作-任务]]"]。解析顺序为「路径 → 文件名 → 唯一 H1 标题」,因此手写的[[软著工作]]也能命中软著工作-任务.md(标题歧义时不做猜测)。 templates/下的文件和含<% %>的文件被识别为模板,不会进入任务/经验/成果视图。- 类型定义见 vault 根目录的
任务.md/经验.md/成果.md;Obsidian 模板见templates/。 - 重命名 / 移动任务/日志/经验/成果的文件名或目录时,指向旧路径的 wikilink 会被自动改写(
[[旧]]、[[旧|别名]]、[[旧#小节]]、[[文件夹/旧.md]]等写法都覆盖;指向 H1 标题的链接不动,因为标题没变)。 - 标记为:任意笔记的详情页右上角有「标记为…」,可把它改成另一种笔记型类型(经验 / 成果 / 记忆碎片,以及你自建的
note行为类型)。改的是 frontmatter 的type;若它不在目标类型的目录树里,会先确认再连文件一起移过去。任务/日志不出现在这个列表里——它们带有看板、状态字段、按天导航等行为约定,只能在新建时选择。
类型与模板(设置 → 类型)
类型由你维护:每条类型 = type(frontmatter 里的值)+ 名称、颜色、默认目录、模板文件、文件名格式、是否显示在侧栏。默认五条(任务 / 日志 / 经验 / 成果 / 记忆碎片),可以新增(如「会议纪要」)、改、删。
type与「行为」创建后不可改:行为task(看板 + 状态字段)、log(每日日志)、note(分节编辑器)由程序决定,且task只能是任务、log只能是日志。改名会孤立已有笔记,所以 id 只读。- 默认目录:留空=跟随 vault(该类笔记最多所在目录,父目录也是该类型的"家"时继续往上;没有笔记时用上表默认值);新类型留空且还没有笔记时,创建对话框会让你填目录。
- 模板:填 vault 内的 Markdown 路径(如
templates/任务模板.md)。占位符{{title}}{{name}}{{date}}{{type}}{{task}}(关联任务文件名);Templater 的<% tp.file.title %>/<% tp.date.now(…) %>会自动转成占位符,其余写法原样保留。一键「生成模板」把内置内容写进templates/<名称>模板.md(已存在则不覆盖)。 - 文件名格式:
{title}/{date}组合,例如日志用{date}-工作日志,会议纪要用{date} 会议纪要。 - 侧栏只显示「已显示」的类型;「全部文件」视图列出整个 vault 的文件夹树,可直接新建文件夹(含多级)与在任意文件夹新建笔记(类型、目录都在对话框里选)。
- 类型列表是整表存的,所以升级不会往你已有的列表里塞新类型;缺哪个内置类型时,列表下方会出现「补回内置类型:…」按钮(只补缺失的那几条,不动你改过的、也不会把删掉的类型偷偷加回来)。
功能范围
已实现:
- 任务工作台:字段内联编辑(状态 / 优先级 / 类别 / 负责人 / 截止日期)、结构化正文编辑、Markdown 原文编辑器
- 任务结构化编辑(不需要写 Markdown)
- 「说明」:直接编辑
## 背景一节,失焦或 Ctrl/⌘+Enter 保存 - 「子任务」:勾选 / 改写 / 删除 / 回车新增,写回
- [ ]列表 - 「变更日志」:回车追加、逐条改写或删除
- 其余章节(
## 自定义)原样渲染,不会丢内容
- 「说明」:直接编辑
- 工作记录:自动聚合每日日志中提到该任务的行
- 工作日志(每日)
- 生成:日志视图右上角「写今天的日志」——当天已有日志就直接打开,没有就新建
工作日志/YYYY-MM-DD-工作日志.md(目录与文件名格式可在设置里改) - 结构继承:新建时复制「最近一篇日志」的
##分节(例如产研/项目:/其他工作),不用每次从空模板开始 - 按天导航:日志页顶部「前一天 / 后一天」,缺失的日期点开即创建
- 填写:每一节都是可编辑清单 —— 勾选、回车新增待办、改写文字、删除;Tab / Shift+Tab 调整缩进;行尾「+」在其下方插入子项
- 待办 → 任务:待办行尾的靶心按钮打开「新建任务 / 关联已有任务」,两条路都把
[[任务]]写回那一行——任务的「工作记录」随即收录这篇日志,不需要把同一条信息复制进任务的## 子任务 - 进度:页头显示当天待办 / 已完成数量
- 生成:日志视图右上角「写今天的日志」——当天已有日志就直接打开,没有就新建
- 变更时间线:来自索引库的结构化事件(应用内与外部写入都可追溯)
- 经验 / 成果 / 记忆碎片:从任务页面一键新建经验与成果(自动带上
related_to),并在任务页面自动回收;随手记的碎片先落成记忆碎片,想归位时用「标记为…」升级成经验或成果 - 看板:按状态分列,拖拽卡片即改写
status并写入变更日志;详情栏可一键收起(进入看板时自动收起,点卡片再展开),看板占满剩余宽度 - 笔记视图:frontmatter 属性、关联链接、反向链接、wikilink 跳转、Markdown 勾选框可点、「标记为…」改类型(经验 / 成果 / 记忆碎片等
note型类型;不在目标目录树里时会确认并一起移动文件) - 类型设置(设置 → 类型):类型由用户维护——新增/删除、改名称与颜色、指定默认目录与模板、自定义文件名格式、决定是否显示在侧栏;内置「任务」「日志」的行为不可改(看板/每日日志);缺内置类型时一键补回
- 全部文件视图:整个 vault 的文件夹树,可在任意层级新建文件夹、在任意文件夹新建笔记(类型与目录在对话框里选),不再只能从任务/日志派生
- 重命名 / 移动笔记:任务 / 日志 / 经验 / 成果页头一键改名,标记类型时可连目录一起移动;指向旧路径的 wikilink(含
|别名、#小节、文件夹/名字.md)自动改写,不留死链 - 可配置的建卡规则:每种类型的目录、模板文件、文件名格式(设置 → 类型);一键把内置模板写进
templates/供 Obsidian 里直接编辑 - 全库搜索(Ctrl/⌘+K):走索引,无需读取文件
- AI 助手(内置 Pi):对话式完成任务填报、信息梳理与沉淀;17 个 vault 工具 + 知识闭环快捷动作;模型/API Key/端点可配(Key 只写不读);工具扩展一键安装到 vault
- MCP server:
tasklens-mcp让任何 MCP 客户端(Claude Code / Cursor …)获得同一套 vault 工具 - 外部改动:Rust 侧文件监听 + 400ms 防抖重扫
- 设置(侧栏齿轮)
- 外观:跟随系统 / 浅色 / 深色;主色(预设 + 取色器),高亮与选中色由主色派生
- 字体:界面字体(系统 / Inter / 微软雅黑 / 苹方·思源)、代码字体、字号 10–22px(全部文本与 Markdown 正文随之缩放)
- Git 同步:分支 / 上游 / 变更数 / 领先落后;获取远端状态、拉取、提交、推送、一键同步;远程地址设置
- 自动同步:可设模式(仅拉取 / 提交并推送)、间隔(1–240 分钟)、打开 vault 时先同步一次
- Git 安全边界:拉取只用
--ff-only(分叉即停止并提示),推送永不强制;索引库通过仓库级info/exclude排除,不会污染提交
尚未实现(有意留白):
- 附件、图片、公式、Mermaid 渲染
- 富文本编辑器(说明与子任务是结构化的,其余正文仍是 Markdown 原文编辑)
- AI 面板的会话恢复(新开会话即新起 pi 会话;历史可在
~/.pi/agent/sessions查看) - 扩展 UI 对话框(
select/confirm/input)——当前只渲染notify,其余按「忽略」处理并在面板里提示
版本与发版
每次代码改动落地后先判断:这次要不要升版本? 要升就一路做完——绝不停在「代码改了、版本号没动、 标签没打」;判断不用升的,在交付说明里写明理由。
- 版本号唯一来源:三处必须同号:根
Cargo.toml的[workspace.package].version、package.json的version、src-tauri/tauri.conf.json的version。 - 标签与版本同号,前缀跟随本仓库既有习惯:
v前缀(如v0.1.0);一个仓库里不得混用两种写法。 - 要升的完整序列(缺一步都算没做完):
- 在版本号唯一来源处改号;仓库里还有别处重复写了版本(前端
package.json、tauri.conf.json、 README 里的版本引用)的,同一提交里一起改; - 重新构建并验证:
bun install(走 npmmirror)→ 按 README「打包与发布」构建桌面端;本机 vite 端口 1420 落在 Windows 保留段时改用生产形态(bun run build+cargo build --manifest-path src-tauri/Cargo.toml --features tauri/custom-protocol); - 提交、
git push当前分支; - 打附注标签(
git tag -a <标签> -m "TaskLens 任务透镜 <版本号>")并git push origin <标签>; - 对外发布的版本要在本仓库留下可查的记录:在 README 的版本历史 / CHANGELOG(没有就新建一节) 写清这一版改了什么、为什么改;并确认分发物(安装包 / 便携包 / 镜像等)的文件名与版本号一致。
- 在版本号唯一来源处改号;仓库里还有别处重复写了版本(前端
- 判断口径:用户可见的行为 / 界面 / 协议 / 只读数据格式变了,或修了用户能踩到的缺陷 → 升 (补丁位修缺陷,小版本位加能力);纯内部重构、注释、测试、脚本、文档 → 不升。
- 交付说明里写清两件事:这次要不要升版本(要升就写升到哪个号),以及版本号 / 标签 / 推送 三件事是否都做完;不升就写「不升 + 理由」,不许留白。
关于
这个项目由 CodeLife Leon 独立开发与维护。更多项目、各项目发版信息、赞赏与联系方式都在个人站:
- 个人站:https://softwarescience.top/
- 本项目主页:https://softwarescience.top/projects/tasklens
- 下载与发版:https://softwarescience.top/releases/tasklens
- 赞赏支持:https://softwarescience.top/sponsor(自愿,不影响任何版本的发布)
问题反馈与需求建议:优先在仓库提 Issue,也可以邮件 virgil_lgp@163.com。
项目介绍
以任务为中心浏览工作记录、沉淀经验与成果的本地 Markdown 工作台|作者个人站:https://softwarescience.top/projects/tasklens
https://softwarescience.top/projects/tasklens定制我的领域