Initial release v0.1.0

This commit is contained in:
2949984428 2026-04-23 16:16:00 +08:00
commit 67fb0abf29
11 changed files with 2688 additions and 0 deletions

13
.gitignore vendored Normal file
View file

@ -0,0 +1,13 @@
# 用户本地设置(含本机 home 路径,不应入库)
data.json
# macOS
.DS_Store
# Editor
.vscode/
.idea/
# Node如果以后改成 TS 编译)
node_modules/
*.log

29
CHANGELOG.md Normal file
View file

@ -0,0 +1,29 @@
# Changelog
All notable changes to this plugin will be documented here.
## [0.1.0] - 2026-04-23
### 首发版本
**核心功能**
- 扫描 Obsidian 内的 Skill 文件夹,一键 symlink 到 18+ Coding 工具Claude Code / Codex / Cursor / Gemini CLI / Windsurf / Trae 等)
- 双向同步vault → 工具(安装)+ 工具 → vault导入
- 8 格同步状态卡:已同步 / 部分同步 / 未安装 / 待导入 / 指向错误 / 失效链接 / 冲突,每格 hover 显示用户视角的 tooltip
- Skill 列表带搜索 + 折叠 description
**自动化**
- 定时扫描(默认 5 分钟,可配置)
- 自动安装新 Skill 到所有 Coding 工具(默认开,安全)
- 自动从工具导入新 Skill 到 vault默认关需主动开启
- 启动时同步状态摘要通知
**安全护栏**
- 移除只断开链接,不删除源 Skill
- 冲突状态需人工处理,绝不覆盖工具自带的实体 Skill
- 删除按钮二次确认3 秒确认窗口)
**配置**
- 添加 Coding 工具弹窗18 个预设可选 + 自定义路径
- 设置面板用用户视角语言,避免技术黑话
- 仅桌面端(移动端 Obsidian 不支持 symlink

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Chocolae
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 OF OTHER DEALINGS IN THE
SOFTWARE.

151
PUBLISH.md Normal file
View file

@ -0,0 +1,151 @@
# 发布到 GitHub + Obsidian 插件商店
> 本指南假设你已安装 `git``gh`GitHub CLI且已 `gh auth login`
## 一次性准备:替换占位符
发布前先把这些 `<YOUR-...>` 占位符替换成你的真实信息:
```bash
# 在仓库根目录执行macOS
cd ~/Documents/claude-skill-sync
sed -i '' 's/<YOUR-NAME>/你的真名/g' manifest.json LICENSE
sed -i '' 's/<YOUR-GITHUB>/你的GitHub用户名/g' manifest.json README.md docs/USAGE.md
```
或者手动编辑:
- `manifest.json``author`、`authorUrl`
- `LICENSE` → 版权人姓名
- `README.md` → repo 链接
- `docs/USAGE.md` → repo 链接
## 第一阶段:发布到 GitHub
### 1. 初始化 git 仓库
```bash
cd ~/Documents/claude-skill-sync
git init
git add .
git status # 确认 data.json 已被 .gitignore 忽略
git commit -m "Initial release v0.1.0"
```
### 2. 在 GitHub 创建公开 repo 并 push
```bash
gh repo create claude-skill-sync --public --source=. --push --description "Manage AI coding agent Skills from your Obsidian vault"
```
或网页创建后:
```bash
git branch -M main
git remote add origin https://github.com/<你的用户名>/claude-skill-sync.git
git push -u origin main
```
### 3. 创建 v0.1.0 Release**Obsidian 商店硬要求**
商店审核会从 GitHub Releases 拉取 `manifest.json` / `main.js` / `styles.css`**tag 名必须等于 manifest.json 的 version不带 `v` 前缀**。
```bash
gh release create 0.1.0 \
manifest.json main.js styles.css \
--title "0.1.0 — 首发版本" \
--notes-file CHANGELOG.md
```
注意三个 asset 必须直接上传到 release不是源码 zip 里)。
### 4. 验证 release 可访问
```bash
curl -sI https://github.com/<你的用户名>/claude-skill-sync/releases/download/0.1.0/manifest.json | head -1
# 期望 200 或 302
```
## 第二阶段:提交到 Obsidian 插件商店
### 1. Fork obsidianmd/obsidian-releases
```bash
gh repo fork obsidianmd/obsidian-releases --clone --remote
cd obsidian-releases
```
### 2. 编辑 community-plugins.json追加你的条目
打开文件,在数组**末尾**追加(注意前一项末尾要补逗号):
```json
{
"id": "claude-skill-sync",
"name": "Claude Skill Sync",
"author": "<你的真名>",
"description": "Manage AI coding agent Skills (Claude Code, Codex, Cursor, Gemini, and more) from your Obsidian vault. One source, symlinked to each tool.",
"repo": "<你的GitHub用户名>/claude-skill-sync"
}
```
`repo` 字段格式必须是 `<owner>/<repo-name>`,不带 https://。
### 3. 提交 PR
```bash
git checkout -b add-claude-skill-sync
git add community-plugins.json
git commit -m "Add Claude Skill Sync plugin"
git push origin add-claude-skill-sync
gh pr create --repo obsidianmd/obsidian-releases \
--title "Add Claude Skill Sync" \
--body "Plugin repo: https://github.com/<你的用户名>/claude-skill-sync
Manages AI coding agent Skills from Obsidian vault, symlinked to Claude Code / Codex / Cursor / 18+ tools."
```
### 4. 等待审核
- 自动检查bot通常几分钟内完成会跑 `manifest.json` 校验、文件存在性、命名规范等
- 人工审核1-4 周(受 Obsidian 团队的处理速度影响)
- 如果 bot 标红,修复后 push 同分支即可重新触发
## 常见审核问题与修复
| 问题 | 原因 | 修复 |
|---|---|---|
| Release tag 不匹配 | tag 用了 `v0.1.0` 而非 `0.1.0` | `gh release delete v0.1.0` 后用正确 tag 重发 |
| manifest.json not found in release | 上传到 release 时没附 asset | `gh release upload 0.1.0 manifest.json main.js styles.css` |
| `id` 已被占用 | 跟现有插件 id 冲突 | 改一个独特的 id记得 manifest.json 和商店 PR 都改 |
| 包含 `console.log` | 商店要求 production 代码无调试输出 | 全文搜 `console.` 删除或注释 |
| `data.json` 被提交 | `.gitignore` 没生效 | `git rm --cached data.json && git commit` |
| Description 太长 | > 250 chars 会被警告 | 精简到一句话 |
| innerHTML 用法 | Obsidian 出于 XSS 安全禁止 `innerHTML` | 改用 createEl / setText |
## 后续版本发布流程
```bash
# 1. 改 manifest.json 的 version (如 0.2.0)
# 2. versions.json 加新条目: "0.2.0": "1.4.0"
# 3. CHANGELOG.md 加新版本说明
# 4. 提交并打 tag
git add manifest.json versions.json CHANGELOG.md
git commit -m "Release v0.2.0"
git push
# 5. 创建新 release
gh release create 0.2.0 manifest.json main.js styles.css \
--title "0.2.0 — 描述" \
--notes "见 CHANGELOG.md"
```
新版本发布后**不需要再提交 PR 到 obsidian-releases**,商店会自动从你的 GitHub Releases 拉新版。
## 用户安装方式
商店审核通过后,用户可以:
1. Obsidian → 设置 → 第三方插件 → 浏览
2. 搜索 "Claude Skill Sync"
3. Install → Enable
也可以用 [BRAT](https://github.com/TfTHacker/obsidian42-brat) 提前预览(无需等审核):
- 在 BRAT 里 Add Beta Plugin → 输入 `<你的用户名>/claude-skill-sync`

114
README.md Normal file
View file

@ -0,0 +1,114 @@
# Claude Skill Sync
Obsidian 插件 — 把 Obsidian vault 作为 AI agent skill 的中央仓库,一键 symlink 到 Claude Code / Codex / Cursor 等工具的 skill 目录,跨电脑同步交给 vault 自身的 sync 方案。
> 配套理念:**单一真身vault多处替身symlink**。任何位置编辑都即时一致,不复制内容、不占额外空间。
![架构图](docs/architecture.png)
> 详细使用指南:[docs/USAGE.md](docs/USAGE.md)
## 安装
### 方式 A开发者本地推荐
```bash
# 1. clone 到本地任意位置
git clone https://github.com/<owner>/claude-skill-sync.git ~/Documents/claude-skill-sync
# 2. 在你的 Obsidian vault 里建 symlink 指向它
cd /path/to/your-vault
ln -s ~/Documents/claude-skill-sync .obsidian/plugins/claude-skill-sync
# 3. 启动 Obsidian → 设置 → 第三方插件 → 关闭安全模式 → 启用 Claude Skill Sync
```
升级:`cd ~/Documents/claude-skill-sync && git pull`,然后在 Obsidian 里重新加载插件即可。
### 方式 B直接复制最简
把整个目录复制到 vault 内:
```bash
git clone https://github.com/<owner>/claude-skill-sync.git \
/path/to/your-vault/.obsidian/plugins/claude-skill-sync
```
## 启用后
1. 设置 → Claude Skill Sync 中确认:
- **Vault 内 Skill 根目录**(默认 `Skills`,可改成你 vault 里的实际路径,如 `SkillPack/skills`
- **目标平台**(默认 Claude Code + Codex/Agents可加任意
2. 左侧 ribbon 点插头图标打开侧边栏
3. 在 vault 的 skill 根目录下放 skill每个子目录 = 一个 skill`SKILL.md`
4. 点"全部安装"——所有 skill symlink 到本机平台目录AI 工具立即可用
## 功能
- **侧边栏状态卡**8 格 grid 显示已同步 / 部分同步 / 未安装 / 待导入 / 指向错误 / 失效 link / 冲突
- **双向同步**vault → 平台install+ 平台 → vaultimport 向导)
- **失效 link 一键清理**vault 删了 skill残留的 symlink 一键扫掉
- **指向错误一键修复**vault 路径变了,所有 symlink 一键改指
- **定时自动同步**(默认 5 分钟vault 新 skill 自动安装;平台新真目录默认仅提醒、可选自动导入
- **启动状态提醒**:打开 Obsidian 弹通知摘要
## 工作机制
```
vault/<skillRoot>/<name>/ ← 真身(数据)
│ symlink
~/.claude/skills/<name> ← 替身
~/.agents/skills/<name> ← 替身
~/.cursor/skills/<name> ← 替身(自行添加平台)
```
所有 AI 工具读取的是同一份内容,跨工具立即一致。
## 跨电脑同步
vault 通过 Obsidian Sync / iCloud / Git / 坚果云等任意方案同步真身;每台电脑上的"替身"由本插件本地建立。
`data.json`(每机平台路径)应当**不参与跨机同步**(含 `~`,不同电脑展开后不同;本仓库 `.gitignore` 已忽略)。
## 平台支持
- ✅ macOS / Linux / Windows 桌面端
- ❌ 移动端iOS/Android Obsidian 无 Node fs / symlink 能力,插件 `isDesktopOnly: true`
## 配置示例
`data.json`(首次启用后自动生成;勿提交至 git
```json
{
"skillRoot": "SkillPack/skills",
"platforms": [
{ "name": "Claude Code", "target": "~/.claude/skills", "enabled": true },
{ "name": "Codex / Agents", "target": "~/.agents/skills", "enabled": true },
{ "name": "Cursor", "target": "~/.cursor/skills", "enabled": false }
],
"notifyOnStartup": true,
"autoScanIntervalMin": 5,
"autoInstall": true,
"autoImport": false
}
```
## 开发
纯 JS无 build。改 `main.js` → Obsidian 设置里关掉再开启插件即可。
```
.
├── manifest.json # 插件元信息
├── main.js # 全部逻辑(~770 行)
├── styles.css # 侧边栏样式
├── data.json # 用户本地设置gitignore
└── README.md
```
## License
MIT — 见 LICENSE

164
docs/USAGE.md Normal file
View file

@ -0,0 +1,164 @@
# Claude Skill Sync — 使用指南
> Obsidian 插件 + AI Coding 工具的协同管理:把 vault 当中央仓库,所有 AI 工具读到的是同一份 skill。
## 架构图
![架构图](./architecture.png)
> 源文件:[architecture.excalidraw](./architecture.excalidraw)
> 在 [excalidraw.com](https://excalidraw.com) 打开 → Open → 选择该文件即可编辑。
> 导出 PNG在 excalidraw.com 里 Menu → Export image → PNG覆盖 `architecture.png` 后 commit 即可在 README 里渲染。
## 30 秒理解
- vault 内 `SkillPack/skills/<name>/` 是 skill **真身**(数据本体,唯一一份)
- `~/.claude/skills/<name>` `~/.agents/skills/<name>` 等是 **替身**symlink指向真身
- 跨电脑vault 同步把真身推到另一台机;另一台机点插件「全部安装」自动建好替身
- 任何位置编辑都即时一致 — Obsidian 改、Claude Code 立刻看到
## 安装4 步)
### 1. clone 插件 repo
```bash
git clone https://github.com/<owner>/claude-skill-sync.git ~/Documents/claude-skill-sync
```
### 2. 在 vault 里建 skill 中央目录
```bash
mkdir -p /path/to/your-vault/SkillPack/skills
```
> 目录名可改,默认是 `Skills/`,本指南推荐用 `SkillPack/skills/`(便于和文档放一起)。
### 3. 把插件 symlink 到 vault 的 .obsidian/plugins/
```bash
cd /path/to/your-vault
ln -s ~/Documents/claude-skill-sync .obsidian/plugins/claude-skill-sync
```
> 用 symlink 而不是 cp之后 `git pull` 升级插件,所有 vault 一起生效。
### 4. 在 Obsidian 启用
1. 设置 → 第三方插件 → **关闭安全模式**
2. 启用 **Claude Skill Sync**
3. 设置面板里把 **Vault 内 Skill 根目录**改成你的实际路径(如 `SkillPack/skills`
## 日常使用
### 添加一个新 skill
```bash
mkdir -p /path/to/your-vault/SkillPack/skills/my-new-skill
```
在该目录里创建 `SKILL.md`
```markdown
---
name: my-new-skill
description: 一句话描述这个 skill 干嘛
---
# my-new-skill
详细内容...
```
回到 Obsidian 侧边栏点「全部安装」(或等 5 分钟自动同步),所有 AI 工具立刻可用。
### 添加新平台
设置 → Claude Skill Sync → 「+ 添加平台」按钮,弹窗里选预设:
- Claude Code, Codex / Agents, Cursor, Gemini CLI, Qwen Code, Windsurf, Trae / Trae CN
- Junie, Factory Droid, Augment, OpenCode, KiloCode, Qoder, OB1, Amp, Kiro, CodeBuddy
- 自定义(点底部「+ 自定义平台」展开)
### 跨电脑同步
**新机首次接入**
1. vault 同步软件Obsidian Sync / iCloud / Git / 坚果云 / Remotely Save拉下 vault
2. 按上面安装步骤 1+3+4不需要重新建 SkillPack/skills/sync 已经带过来了)
3. 启用插件后状态卡显示「48 未安装」,点「全部安装」即完成
**日常**
- A 电脑添加新 skill → vault sync 推到云 → B 电脑拉到 → B 电脑插件 5 分钟内自动安装(默认开启)
## 侧边栏功能
**顶部**
- 「刷新」「全部安装」「导入现有」三个按钮
- 中央目录路径显示
**同步状态卡**8 格 grid
| 格子 | 含义 | 点击行为 |
|---|---|---|
| vault skill (×N 平台) | vault 内 skill 数 | — |
| 已同步 | 在所有启用平台都装好的数 | — |
| 部分同步 | 只装了部分平台 | 全部安装补齐 |
| 未安装 | 一个平台都没装 | 全部安装 |
| 待导入 | 平台目录有真目录、vault 无 | 开导入向导 |
| 指向错误 | symlink 指向其他位置 | 修复全部 |
| 失效 link | symlink 指向已不存在 | 一键清理 |
| 冲突 | 平台目录有同名真目录 | 需手动处理 |
**搜索框**:按 skill 名 / description 即时过滤
**skill 列表**(默认收起 description
- 每行:`▸ skill-name` + 各平台徽章 + 操作按钮(安装 / 移除 / 改指向)
- 点 ▸ 三角或 name 行展开 description
## 设置项详解
### 启动时提醒同步状态(默认 ON
打开 Obsidian 后 1.5 秒扫一遍,发现不一致弹通知。
### 定时扫描间隔(默认 5 分钟0 = 关)
后台轮询。修改即时生效。
### 自动安装新 skill (vault → 平台)(默认 ON安全
扫描时发现 vault 新 skill 没装到平台 → 自动建 symlink。
### 自动导入新 skill (平台 → vault)(默认 OFF 危险)
扫描时发现平台目录有新真目录 → 自动 mv 进 vault。
**mv 不可逆**,仅当确定平台目录不会被其他工具临时使用时才开。
## 安全护栏
| 行为 | 保护 |
|---|---|
| 卸载 / 移除 | 只删 symlink检测目标不是 symlink 拒绝执行 |
| 全部安装 | 跳过冲突状态(同名真目录),不会覆盖 |
| 导入现有 | 用 `fs.rename` 原子操作,跨设备失败立刻报告 |
| 清理失效 | 只删孤儿 symlink不动真目录 |
| 自动同步 | install 默认开安全import 默认关(危险) |
## 常见问题
**Q状态卡数字不对**
A点「刷新」或在设置里修正 skillRoot 路径。
**Q换电脑后所有 symlink 都「指向错误」?**
Avault 路径变了。点「指向错误」格子一键 repair自动按当前 vault 路径重建。
**Q移动设备能用吗**
A不能。iOS/Android Obsidian 没有 Node fs / symlink 能力,插件 `isDesktopOnly: true`。但 vault 里的 skill 文档你仍然可以在手机上**阅读和编辑**,回到桌面端再 install。
**Q插件升级怎么做**
A`cd ~/Documents/claude-skill-sync && git pull`,回 Obsidian 设置里关掉再开启 Claude Skill Sync 即重载。
**Qdata.json 要不要提交 git**
A**不要**。它含本机 home 路径,每台电脑展开后不同;本仓库 `.gitignore` 已忽略。
**Qvault 同步把 .obsidian/ 排除了,插件怎么办?**
A插件本来就是每台电脑独立 `git clone` + `ln -s`。vault 同步只负责真身(`SkillPack/skills/`)。
## 反馈
- Issue: <https://github.com/&lt;owner&gt;/claude-skill-sync/issues>
- 详细原理:[../docs/流程与原理.md](https://github.com/&lt;owner&gt;/skill-pack-data) (如果你也共享了 SkillPack 数据 repo

View file

@ -0,0 +1,817 @@
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"id": "title-main",
"type": "text",
"x": 380, "y": 20, "width": 540, "height": 38,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100001, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "Claude Skill Sync — 架构与使用流程",
"fontSize": 28, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "Claude Skill Sync — 架构与使用流程",
"autoResize": true, "lineHeight": 1.25
},
{
"id": "github-container",
"type": "rectangle",
"x": 50, "y": 90, "width": 280, "height": 400,
"angle": 0,
"strokeColor": "#15803d", "backgroundColor": "#d3f9d8",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 3}, "seed": 100002, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false
},
{
"id": "github-title",
"type": "text",
"x": 110, "y": 105, "width": 160, "height": 28,
"angle": 0,
"strokeColor": "#15803d", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100003, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "GitHub Repo",
"fontSize": 20, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "GitHub Repo",
"autoResize": true, "lineHeight": 1.25
},
{
"id": "github-sub",
"type": "text",
"x": 95, "y": 138, "width": 200, "height": 22,
"angle": 0,
"strokeColor": "#374151", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100004, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "claude-skill-sync",
"fontSize": 14, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "claude-skill-sync",
"autoResize": true, "lineHeight": 1.25
},
{
"id": "github-files",
"type": "text",
"x": 80, "y": 180, "width": 220, "height": 220,
"angle": 0,
"strokeColor": "#374151", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100005, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "• main.js\n• manifest.json\n• styles.css\n• README.md\n• LICENSE\n• .gitignore",
"fontSize": 16, "fontFamily": 5,
"textAlign": "left", "verticalAlign": "top",
"containerId": null,
"originalText": "• main.js\n• manifest.json\n• styles.css\n• README.md\n• LICENSE\n• .gitignore",
"autoResize": true, "lineHeight": 1.5
},
{
"id": "github-note",
"type": "text",
"x": 75, "y": 440, "width": 230, "height": 22,
"angle": 0,
"strokeColor": "#15803d", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100006, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "纯 JS · 无 build · MIT",
"fontSize": 14, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "纯 JS · 无 build · MIT",
"autoResize": true, "lineHeight": 1.25
},
{
"id": "vault-container",
"type": "rectangle",
"x": 380, "y": 90, "width": 560, "height": 400,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "#a5d8ff",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 3}, "seed": 100010, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false
},
{
"id": "vault-title",
"type": "text",
"x": 575, "y": 105, "width": 170, "height": 28,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100011, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "Obsidian Vault",
"fontSize": 20, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "Obsidian Vault",
"autoResize": true, "lineHeight": 1.25
},
{
"id": "vault-sub",
"type": "text",
"x": 530, "y": 138, "width": 260, "height": 22,
"angle": 0,
"strokeColor": "#374151", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100012, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "跨电脑通过 vault 自身同步",
"fontSize": 14, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "跨电脑通过 vault 自身同步",
"autoResize": true, "lineHeight": 1.25
},
{
"id": "skills-box",
"type": "rectangle",
"x": 400, "y": 175, "width": 520, "height": 115,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "#ffffff",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 3}, "seed": 100020, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false
},
{
"id": "skills-title",
"type": "text",
"x": 415, "y": 187, "width": 280, "height": 22,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100021, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "SkillPack/skills/ 「真身, 唯一一份」",
"fontSize": 16, "fontFamily": 5,
"textAlign": "left", "verticalAlign": "middle",
"containerId": null,
"originalText": "SkillPack/skills/ 「真身, 唯一一份」",
"autoResize": true, "lineHeight": 1.25
},
{
"id": "skills-list",
"type": "text",
"x": 415, "y": 218, "width": 490, "height": 60,
"angle": 0,
"strokeColor": "#374151", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100022, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "├─ hello-world/ ├─ lark-im/ ├─ ai-products/\n├─ agent-browser/ ├─ pm-tracker/ ├─ research/\n└─ ... 「48 个 skill」",
"fontSize": 14, "fontFamily": 5,
"textAlign": "left", "verticalAlign": "top",
"containerId": null,
"originalText": "├─ hello-world/ ├─ lark-im/ ├─ ai-products/\n├─ agent-browser/ ├─ pm-tracker/ ├─ research/\n└─ ... 「48 个 skill」",
"autoResize": true, "lineHeight": 1.4
},
{
"id": "docs-box",
"type": "rectangle",
"x": 400, "y": 305, "width": 250, "height": 80,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "#ffffff",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 3}, "seed": 100030, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false
},
{
"id": "docs-text",
"type": "text",
"x": 415, "y": 318, "width": 220, "height": 56,
"angle": 0,
"strokeColor": "#374151", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100031, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "SkillPack/docs/\n流程·实现·共享文档",
"fontSize": 15, "fontFamily": 5,
"textAlign": "left", "verticalAlign": "top",
"containerId": null,
"originalText": "SkillPack/docs/\n流程·实现·共享文档",
"autoResize": true, "lineHeight": 1.4
},
{
"id": "plugin-box",
"type": "rectangle",
"x": 670, "y": 305, "width": 250, "height": 80,
"angle": 0,
"strokeColor": "#f59e0b", "backgroundColor": "#fff3bf",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 3}, "seed": 100040, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false
},
{
"id": "plugin-text",
"type": "text",
"x": 685, "y": 318, "width": 220, "height": 56,
"angle": 0,
"strokeColor": "#374151", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100041, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": ".obsidian/plugins/\nclaude-skill-sync 「symlink」",
"fontSize": 14, "fontFamily": 5,
"textAlign": "left", "verticalAlign": "top",
"containerId": null,
"originalText": ".obsidian/plugins/\nclaude-skill-sync 「symlink」",
"autoResize": true, "lineHeight": 1.4
},
{
"id": "vault-bottom-note",
"type": "text",
"x": 405, "y": 410, "width": 510, "height": 22,
"angle": 0,
"strokeColor": "#3b82f6", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100050, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "Obsidian Sync · iCloud · Git · 坚果云 · Remotely Save",
"fontSize": 13, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "Obsidian Sync · iCloud · Git · 坚果云 · Remotely Save",
"autoResize": true, "lineHeight": 1.25
},
{
"id": "vault-bottom-note2",
"type": "text",
"x": 480, "y": 442, "width": 360, "height": 22,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100051, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "↑ vault 跨电脑同步任选其一",
"fontSize": 13, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "↑ vault 跨电脑同步任选其一",
"autoResize": true, "lineHeight": 1.25
},
{
"id": "tools-container",
"type": "rectangle",
"x": 990, "y": 90, "width": 380, "height": 400,
"angle": 0,
"strokeColor": "#d97706", "backgroundColor": "#ffd8a8",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 3}, "seed": 100060, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false
},
{
"id": "tools-title",
"type": "text",
"x": 1095, "y": 105, "width": 170, "height": 28,
"angle": 0,
"strokeColor": "#d97706", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100061, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "AI Coding Tools",
"fontSize": 20, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "AI Coding Tools",
"autoResize": true, "lineHeight": 1.25
},
{
"id": "tools-sub",
"type": "text",
"x": 1060, "y": 138, "width": 240, "height": 22,
"angle": 0,
"strokeColor": "#374151", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100062, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "本地各工具的 skills 目录",
"fontSize": 13, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "本地各工具的 skills 目录",
"autoResize": true, "lineHeight": 1.25
},
{
"id": "tool1-box",
"type": "rectangle",
"x": 1010, "y": 175, "width": 340, "height": 80,
"angle": 0,
"strokeColor": "#d97706", "backgroundColor": "#ffffff",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 3}, "seed": 100070, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false
},
{
"id": "tool1-text",
"type": "text",
"x": 1025, "y": 188, "width": 310, "height": 56,
"angle": 0,
"strokeColor": "#374151", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100071, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "Claude Code\n~/.claude/skills/",
"fontSize": 16, "fontFamily": 5,
"textAlign": "left", "verticalAlign": "top",
"containerId": null,
"originalText": "Claude Code\n~/.claude/skills/",
"autoResize": true, "lineHeight": 1.4
},
{
"id": "tool2-box",
"type": "rectangle",
"x": 1010, "y": 270, "width": 340, "height": 80,
"angle": 0,
"strokeColor": "#d97706", "backgroundColor": "#ffffff",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 3}, "seed": 100080, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false
},
{
"id": "tool2-text",
"type": "text",
"x": 1025, "y": 283, "width": 310, "height": 56,
"angle": 0,
"strokeColor": "#374151", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100081, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "Codex / Agents\n~/.agents/skills/",
"fontSize": 16, "fontFamily": 5,
"textAlign": "left", "verticalAlign": "top",
"containerId": null,
"originalText": "Codex / Agents\n~/.agents/skills/",
"autoResize": true, "lineHeight": 1.4
},
{
"id": "tool3-box",
"type": "rectangle",
"x": 1010, "y": 365, "width": 340, "height": 100,
"angle": 0,
"strokeColor": "#d97706", "backgroundColor": "#ffffff",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 3}, "seed": 100090, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false
},
{
"id": "tool3-text",
"type": "text",
"x": 1025, "y": 378, "width": 310, "height": 76,
"angle": 0,
"strokeColor": "#374151", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100091, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "Cursor / Gemini / Trae / ...\n~/.cursor/skills/\n~/.gemini/skills/ (18 个预设)",
"fontSize": 14, "fontFamily": 5,
"textAlign": "left", "verticalAlign": "top",
"containerId": null,
"originalText": "Cursor / Gemini / Trae / ...\n~/.cursor/skills/\n~/.gemini/skills/ (18 个预设)",
"autoResize": true, "lineHeight": 1.4
},
{
"id": "arrow-github-plugin",
"type": "arrow",
"x": 330, "y": 345, "width": 340, "height": 0,
"angle": 0,
"strokeColor": "#f59e0b", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 2}, "seed": 100100, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"points": [[0, 0], [340, 0]],
"lastCommittedPoint": null, "startBinding": null, "endBinding": null,
"startArrowhead": null, "endArrowhead": "arrow"
},
{
"id": "arrow-github-plugin-label",
"type": "text",
"x": 380, "y": 318, "width": 240, "height": 22,
"angle": 0,
"strokeColor": "#d97706", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100101, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "git clone + ln -s",
"fontSize": 13, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "git clone + ln -s",
"autoResize": true, "lineHeight": 1.25
},
{
"id": "arrow-skills-tool1",
"type": "arrow",
"x": 920, "y": 215, "width": 90, "height": 0,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 2}, "seed": 100110, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"points": [[0, 0], [90, 0]],
"lastCommittedPoint": null, "startBinding": null, "endBinding": null,
"startArrowhead": null, "endArrowhead": "arrow"
},
{
"id": "arrow-skills-tool2",
"type": "arrow",
"x": 920, "y": 230, "width": 90, "height": 80,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 2}, "seed": 100111, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"points": [[0, 0], [90, 80]],
"lastCommittedPoint": null, "startBinding": null, "endBinding": null,
"startArrowhead": null, "endArrowhead": "arrow"
},
{
"id": "arrow-skills-tool3",
"type": "arrow",
"x": 920, "y": 245, "width": 90, "height": 175,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 2}, "seed": 100112, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"points": [[0, 0], [90, 175]],
"lastCommittedPoint": null, "startBinding": null, "endBinding": null,
"startArrowhead": null, "endArrowhead": "arrow"
},
{
"id": "symlink-label",
"type": "text",
"x": 935, "y": 165, "width": 70, "height": 22,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100113, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "symlink",
"fontSize": 13, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "symlink",
"autoResize": true, "lineHeight": 1.25
},
{
"id": "flow-title",
"type": "text",
"x": 580, "y": 540, "width": 240, "height": 32,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100200, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "使用流程「6 步」",
"fontSize": 24, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "使用流程「6 步」",
"autoResize": true, "lineHeight": 1.25
},
{
"id": "step1-box",
"type": "rectangle",
"x": 50, "y": 600, "width": 200, "height": 100,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "#a5d8ff",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 3}, "seed": 100210, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false
},
{
"id": "step1-text",
"type": "text",
"x": 65, "y": 615, "width": 170, "height": 70,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100211, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "1. clone\n插件 GitHub repo\n到本地",
"fontSize": 15, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "1. clone\n插件 GitHub repo\n到本地",
"autoResize": true, "lineHeight": 1.4
},
{
"id": "step2-box",
"type": "rectangle",
"x": 280, "y": 600, "width": 200, "height": 100,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "#a5d8ff",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 3}, "seed": 100220, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false
},
{
"id": "step2-text",
"type": "text",
"x": 295, "y": 615, "width": 170, "height": 70,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100221, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "2. 在 vault 建\nSkillPack/skills/\n目录",
"fontSize": 15, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "2. 在 vault 建\nSkillPack/skills/\n目录",
"autoResize": true, "lineHeight": 1.4
},
{
"id": "step3-box",
"type": "rectangle",
"x": 510, "y": 600, "width": 200, "height": 100,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "#a5d8ff",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 3}, "seed": 100230, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false
},
{
"id": "step3-text",
"type": "text",
"x": 525, "y": 615, "width": 170, "height": 70,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100231, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "3. ln -s 插件\n到 .obsidian/\nplugins/",
"fontSize": 15, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "3. ln -s 插件\n到 .obsidian/\nplugins/",
"autoResize": true, "lineHeight": 1.4
},
{
"id": "step4-box",
"type": "rectangle",
"x": 740, "y": 600, "width": 200, "height": 100,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "#a5d8ff",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 3}, "seed": 100240, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false
},
{
"id": "step4-text",
"type": "text",
"x": 755, "y": 615, "width": 170, "height": 70,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100241, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "4. Obsidian\n启用插件\n「关安全模式」",
"fontSize": 15, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "4. Obsidian\n启用插件\n「关安全模式」",
"autoResize": true, "lineHeight": 1.4
},
{
"id": "step5-box",
"type": "rectangle",
"x": 970, "y": 600, "width": 200, "height": 100,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "#b2f2bb",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 3}, "seed": 100250, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false
},
{
"id": "step5-text",
"type": "text",
"x": 985, "y": 615, "width": 170, "height": 70,
"angle": 0,
"strokeColor": "#15803d", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100251, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "5. 在 vault\n添加 / 编辑 skill\n「即时一致」",
"fontSize": 15, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "5. 在 vault\n添加 / 编辑 skill\n「即时一致」",
"autoResize": true, "lineHeight": 1.4
},
{
"id": "step6-box",
"type": "rectangle",
"x": 1200, "y": 600, "width": 200, "height": 100,
"angle": 0,
"strokeColor": "#15803d", "backgroundColor": "#b2f2bb",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 3}, "seed": 100260, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false
},
{
"id": "step6-text",
"type": "text",
"x": 1215, "y": 615, "width": 170, "height": 70,
"angle": 0,
"strokeColor": "#15803d", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100261, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "6. 点「全部安装」\nAI 工具立即可用\n✓ 完成",
"fontSize": 15, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "6. 点「全部安装」\nAI 工具立即可用\n✓ 完成",
"autoResize": true, "lineHeight": 1.4
},
{
"id": "flow-arrow1",
"type": "arrow",
"x": 250, "y": 650, "width": 30, "height": 0,
"angle": 0,
"strokeColor": "#3b82f6", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 2}, "seed": 100300, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"points": [[0, 0], [30, 0]],
"lastCommittedPoint": null, "startBinding": null, "endBinding": null,
"startArrowhead": null, "endArrowhead": "arrow"
},
{
"id": "flow-arrow2",
"type": "arrow",
"x": 480, "y": 650, "width": 30, "height": 0,
"angle": 0,
"strokeColor": "#3b82f6", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 2}, "seed": 100301, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"points": [[0, 0], [30, 0]],
"lastCommittedPoint": null, "startBinding": null, "endBinding": null,
"startArrowhead": null, "endArrowhead": "arrow"
},
{
"id": "flow-arrow3",
"type": "arrow",
"x": 710, "y": 650, "width": 30, "height": 0,
"angle": 0,
"strokeColor": "#3b82f6", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 2}, "seed": 100302, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"points": [[0, 0], [30, 0]],
"lastCommittedPoint": null, "startBinding": null, "endBinding": null,
"startArrowhead": null, "endArrowhead": "arrow"
},
{
"id": "flow-arrow4",
"type": "arrow",
"x": 940, "y": 650, "width": 30, "height": 0,
"angle": 0,
"strokeColor": "#3b82f6", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 2}, "seed": 100303, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"points": [[0, 0], [30, 0]],
"lastCommittedPoint": null, "startBinding": null, "endBinding": null,
"startArrowhead": null, "endArrowhead": "arrow"
},
{
"id": "flow-arrow5",
"type": "arrow",
"x": 1170, "y": 650, "width": 30, "height": 0,
"angle": 0,
"strokeColor": "#15803d", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": {"type": 2}, "seed": 100304, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"points": [[0, 0], [30, 0]],
"lastCommittedPoint": null, "startBinding": null, "endBinding": null,
"startArrowhead": null, "endArrowhead": "arrow"
},
{
"id": "bottom-tagline",
"type": "text",
"x": 280, "y": 750, "width": 880, "height": 28,
"angle": 0,
"strokeColor": "#1e40af", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100400, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "单一真身「vault」 · 多处替身「symlink」 · 跨电脑同步交给 vault 自身",
"fontSize": 18, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "单一真身「vault」 · 多处替身「symlink」 · 跨电脑同步交给 vault 自身",
"autoResize": true, "lineHeight": 1.25
},
{
"id": "bottom-tagline2",
"type": "text",
"x": 380, "y": 790, "width": 680, "height": 22,
"angle": 0,
"strokeColor": "#3b82f6", "backgroundColor": "transparent",
"fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid",
"roughness": 1, "opacity": 100, "groupIds": [],
"roundness": null, "seed": 100401, "version": 1,
"isDeleted": false, "boundElements": null, "updated": 1, "link": null, "locked": false,
"text": "任何位置编辑都即时一致 · 不复制内容 · 不占额外空间",
"fontSize": 14, "fontFamily": 5,
"textAlign": "center", "verticalAlign": "middle",
"containerId": null,
"originalText": "任何位置编辑都即时一致 · 不复制内容 · 不占额外空间",
"autoResize": true, "lineHeight": 1.25
}
],
"appState": {
"gridSize": null,
"viewBackgroundColor": "#ffffff"
},
"files": {}
}

1010
main.js Normal file

File diff suppressed because it is too large Load diff

10
manifest.json Normal file
View file

@ -0,0 +1,10 @@
{
"id": "claude-skill-sync",
"name": "Claude Skill Sync",
"version": "0.1.0",
"minAppVersion": "1.4.0",
"description": "Manage AI coding agent Skills (Claude Code, Codex, Cursor, Gemini, and 14+ more) from your Obsidian vault. One source, symlinked to each tool. Cross-machine sync via your existing vault sync.",
"author": "Chocolae",
"authorUrl": "https://github.com/2949984428",
"isDesktopOnly": true
}

356
styles.css Normal file
View file

@ -0,0 +1,356 @@
.css-skill-sync {
padding: 8px 10px;
}
.css-skill-sync .css-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 4px;
}
.css-skill-sync .css-header h3 {
margin: 0;
font-size: 14px;
}
.css-skill-sync .css-btn-row {
display: flex;
gap: 6px;
margin-bottom: 8px;
}
.css-skill-sync .css-btn-row button {
flex: 1;
font-size: 12px;
}
.css-skill-sync .css-meta {
font-size: 11px;
color: var(--text-muted);
margin-bottom: 10px;
word-break: break-all;
}
.css-skill-sync .css-status-card {
border: 1px solid var(--background-modifier-border);
border-radius: 6px;
padding: 8px;
margin-bottom: 10px;
background: var(--background-secondary);
}
.css-skill-sync .css-status-loading {
font-size: 11px;
color: var(--text-muted);
text-align: center;
padding: 4px;
}
.css-skill-sync .css-status-head {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 6px;
font-size: 12px;
}
.css-skill-sync .css-status-title { font-weight: 600; }
.css-skill-sync .css-status-overall {
font-size: 10px;
padding: 2px 6px;
border-radius: 3px;
}
.css-skill-sync .css-status-overall.all-good {
background: rgba(16, 185, 129, 0.15);
color: #10b981;
}
.css-skill-sync .css-status-overall.has-issue {
background: rgba(234, 179, 8, 0.15);
color: #eab308;
}
.css-skill-sync .css-status-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4px;
}
.css-skill-sync .css-status-cell {
border: 1px solid var(--background-modifier-border);
border-radius: 4px;
padding: 6px 8px;
text-align: center;
}
.css-skill-sync .css-status-cell.css-clickable {
cursor: pointer;
transition: background 0.1s;
}
.css-skill-sync .css-status-cell.css-clickable:hover {
background: var(--background-modifier-hover);
}
.css-skill-sync .css-status-num {
font-size: 16px;
font-weight: 600;
line-height: 1.2;
}
.css-skill-sync .css-status-label {
font-size: 10px;
color: var(--text-muted);
margin-top: 2px;
}
.css-skill-sync .css-status-cell.css-good .css-status-num { color: #10b981; }
.css-skill-sync .css-status-cell.css-warn .css-status-num { color: #eab308; }
.css-skill-sync .css-status-cell.css-bad .css-status-num { color: #ef4444; }
.css-skill-sync .css-status-cell.css-info .css-status-num { color: var(--text-normal); }
.css-skill-sync .css-status-cell.css-mute .css-status-num { color: var(--text-muted); }
.css-skill-sync .css-empty {
font-size: 12px;
color: var(--text-muted);
padding: 12px;
border: 1px dashed var(--background-modifier-border);
border-radius: 6px;
}
.css-skill-sync .css-search-wrap {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 8px;
}
.css-skill-sync .css-search-input {
flex: 1;
font-size: 12px;
padding: 4px 8px;
border: 1px solid var(--background-modifier-border);
border-radius: 4px;
background: var(--background-primary);
}
.css-skill-sync .css-search-count {
font-size: 10px;
color: var(--text-muted);
white-space: nowrap;
min-width: 30px;
text-align: right;
}
.css-skill-sync .css-skill-list {
display: flex;
flex-direction: column;
}
.css-skill-sync .css-skill {
border: 1px solid var(--background-modifier-border);
border-radius: 6px;
padding: 8px 10px;
margin-bottom: 6px;
background: var(--background-secondary);
}
.css-skill-sync .css-name-row {
display: flex;
align-items: center;
gap: 4px;
user-select: none;
}
.css-skill-sync .css-toggle {
font-size: 10px;
color: var(--text-muted);
width: 12px;
display: inline-block;
text-align: center;
}
.css-skill-sync .css-name {
font-weight: 600;
font-size: 13px;
}
.css-skill-sync .css-desc {
font-size: 11px;
color: var(--text-muted);
margin: 6px 0 4px 16px;
line-height: 1.4;
padding-left: 6px;
border-left: 2px solid var(--background-modifier-border);
}
.css-skill-sync .css-row {
display: flex;
align-items: center;
gap: 6px;
margin-top: 4px;
font-size: 11px;
}
.css-skill-sync .css-plat {
flex: 1;
color: var(--text-muted);
}
.css-skill-sync .css-badge {
padding: 1px 6px;
border-radius: 3px;
font-size: 10px;
font-weight: 500;
}
.css-skill-sync .css-linked { background: rgba(16, 185, 129, 0.15); color: #10b981; }
.css-skill-sync .css-missing { background: rgba(255, 255, 255, 0.06); color: var(--text-muted); }
.css-skill-sync .css-conflict { background: rgba(239, 68, 68, 0.15); color: #ef4444; }
.css-skill-sync .css-mislinked { background: rgba(234, 179, 8, 0.15); color: #eab308; }
.css-skill-sync .css-warn {
font-size: 10px;
color: var(--text-warning, #eab308);
margin-left: 4px;
}
.css-skill-sync .css-row button {
font-size: 10px;
padding: 2px 8px;
height: auto;
}
.css-platform-edit {
border-left: 2px solid var(--background-modifier-border);
padding-left: 8px;
margin-bottom: 4px;
}
.css-skill-sync-modal .css-import-hint {
font-size: 12px;
color: var(--text-muted);
margin: 4px 0 12px;
line-height: 1.5;
}
.css-skill-sync-modal .css-import-ctrl {
display: flex;
gap: 6px;
align-items: center;
margin-bottom: 8px;
}
.css-skill-sync-modal .css-import-count {
margin-left: auto;
font-size: 11px;
color: var(--text-muted);
}
.css-skill-sync-modal .css-import-list {
max-height: 50vh;
overflow-y: auto;
border: 1px solid var(--background-modifier-border);
border-radius: 6px;
padding: 4px;
}
.css-skill-sync-modal .css-import-row {
display: flex;
align-items: flex-start;
gap: 8px;
padding: 6px 8px;
border-radius: 4px;
}
.css-skill-sync-modal .css-import-row:hover {
background: var(--background-modifier-hover);
}
.css-skill-sync-modal .css-import-row label {
flex: 1;
cursor: pointer;
font-size: 12px;
}
.css-skill-sync-modal .css-del-btn {
font-size: 11px;
padding: 2px 8px;
background: transparent;
border: 1px solid var(--background-modifier-border);
color: var(--text-muted);
border-radius: 3px;
cursor: pointer;
white-space: nowrap;
}
.css-skill-sync-modal .css-del-btn:hover {
border-color: #ef4444;
color: #ef4444;
}
.css-skill-sync-modal .css-del-btn.mod-warning {
background: #ef4444;
border-color: #ef4444;
color: #fff;
}
.css-skill-sync-modal .css-source {
color: var(--text-muted);
font-size: 11px;
}
.css-skill-sync-modal .css-source-path {
font-size: 10px;
color: var(--text-faint);
word-break: break-all;
margin-top: 2px;
}
.css-skill-sync-modal .css-empty {
color: var(--text-muted);
padding: 16px;
text-align: center;
}
.css-skill-sync-modal .css-custom-row {
display: flex;
gap: 6px;
margin-bottom: 10px;
}
.css-skill-sync-modal .css-custom-row input {
flex: 1;
font-size: 12px;
padding: 4px 8px;
border: 1px solid var(--background-modifier-border);
border-radius: 4px;
background: var(--background-primary);
}
.css-skill-sync-modal .css-import-row input[type="checkbox"]:disabled + label {
opacity: 0.5;
}
/* —— 添加平台 modal —— */
.css-add-platform-modal .css-search-wrap {
margin-bottom: 8px;
}
.css-add-platform-modal .css-preset-list {
max-height: 320px;
overflow-y: auto;
border: 1px solid var(--background-modifier-border);
border-radius: 4px;
margin-bottom: 10px;
}
.css-add-platform-modal .css-preset-row {
display: flex;
align-items: center;
gap: 8px;
padding: 4px 8px;
font-size: 12px;
border-bottom: 1px solid var(--background-modifier-border);
}
.css-add-platform-modal .css-preset-row:last-child {
border-bottom: none;
}
.css-add-platform-modal .css-preset-row:hover {
background: var(--background-modifier-hover);
}
.css-add-platform-modal .css-preset-row label {
flex: 1;
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
margin: 0;
}
.css-add-platform-modal .css-preset-name {
font-weight: 500;
min-width: 110px;
}
.css-add-platform-modal .css-preset-path {
color: var(--text-muted);
font-size: 11px;
font-family: var(--font-monospace);
flex: 1;
}
.css-add-platform-modal .css-preset-tag {
font-size: 10px;
color: var(--text-muted);
padding: 1px 6px;
background: var(--background-modifier-border);
border-radius: 3px;
}
.css-add-platform-modal .css-preset-exists {
opacity: 0.5;
}
.css-add-platform-modal .css-custom-toggle {
font-size: 12px;
background: transparent;
border: 1px dashed var(--background-modifier-border);
color: var(--text-muted);
padding: 4px 10px;
border-radius: 4px;
cursor: pointer;
width: 100%;
margin-bottom: 8px;
}
.css-add-platform-modal .css-custom-toggle:hover {
background: var(--background-modifier-hover);
color: var(--text-normal);
}
.css-add-platform-modal .css-custom-wrap {
margin-bottom: 10px;
}

3
versions.json Normal file
View file

@ -0,0 +1,3 @@
{
"0.1.0": "1.4.0"
}