@mengzf/skillflow-mcp 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +238 -2
  3. package/dist/adapters/workbuddy.sniffer.d.ts +22 -0
  4. package/dist/adapters/workbuddy.sniffer.d.ts.map +1 -0
  5. package/dist/adapters/workbuddy.sniffer.js +143 -0
  6. package/dist/adapters/workbuddy.sniffer.js.map +1 -0
  7. package/dist/config.d.ts +9 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +33 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/db/index.d.ts +4 -0
  12. package/dist/db/index.d.ts.map +1 -0
  13. package/dist/db/index.js +39 -0
  14. package/dist/db/index.js.map +1 -0
  15. package/dist/db/schema.d.ts +3 -0
  16. package/dist/db/schema.d.ts.map +1 -0
  17. package/dist/db/schema.js +103 -0
  18. package/dist/db/schema.js.map +1 -0
  19. package/dist/db/seed.d.ts +15 -0
  20. package/dist/db/seed.d.ts.map +1 -0
  21. package/dist/db/seed.js +128 -0
  22. package/dist/db/seed.js.map +1 -0
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +51 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/server.d.ts +7 -0
  28. package/dist/server.d.ts.map +1 -0
  29. package/dist/server.js +368 -0
  30. package/dist/server.js.map +1 -0
  31. package/dist/services/benchmark.service.d.ts +27 -0
  32. package/dist/services/benchmark.service.d.ts.map +1 -0
  33. package/dist/services/benchmark.service.js +94 -0
  34. package/dist/services/benchmark.service.js.map +1 -0
  35. package/dist/services/dashboard.service.d.ts +12 -0
  36. package/dist/services/dashboard.service.d.ts.map +1 -0
  37. package/dist/services/dashboard.service.js +191 -0
  38. package/dist/services/dashboard.service.js.map +1 -0
  39. package/dist/services/impact.service.d.ts +20 -0
  40. package/dist/services/impact.service.d.ts.map +1 -0
  41. package/dist/services/impact.service.js +114 -0
  42. package/dist/services/impact.service.js.map +1 -0
  43. package/dist/services/telemetry.service.d.ts +33 -0
  44. package/dist/services/telemetry.service.d.ts.map +1 -0
  45. package/dist/services/telemetry.service.js +175 -0
  46. package/dist/services/telemetry.service.js.map +1 -0
  47. package/dist/types/index.d.ts +3 -0
  48. package/dist/types/index.d.ts.map +1 -0
  49. package/dist/types/index.js +3 -0
  50. package/dist/types/index.js.map +1 -0
  51. package/dist/types/models.d.ts +61 -0
  52. package/dist/types/models.d.ts.map +1 -0
  53. package/dist/types/models.js +2 -0
  54. package/dist/types/models.js.map +1 -0
  55. package/dist/types/tools.d.ts +134 -0
  56. package/dist/types/tools.d.ts.map +1 -0
  57. package/dist/types/tools.js +149 -0
  58. package/dist/types/tools.js.map +1 -0
  59. package/dist/utils/ascii-chart.d.ts +9 -0
  60. package/dist/utils/ascii-chart.d.ts.map +1 -0
  61. package/dist/utils/ascii-chart.js +29 -0
  62. package/dist/utils/ascii-chart.js.map +1 -0
  63. package/dist/utils/formatter.d.ts +6 -0
  64. package/dist/utils/formatter.d.ts.map +1 -0
  65. package/dist/utils/formatter.js +29 -0
  66. package/dist/utils/formatter.js.map +1 -0
  67. package/dist/utils/hours.d.ts +3 -0
  68. package/dist/utils/hours.d.ts.map +1 -0
  69. package/dist/utils/hours.js +15 -0
  70. package/dist/utils/hours.js.map +1 -0
  71. package/dist/utils/pricing.d.ts +19 -0
  72. package/dist/utils/pricing.d.ts.map +1 -0
  73. package/dist/utils/pricing.js +96 -0
  74. package/dist/utils/pricing.js.map +1 -0
  75. package/dist/utils/quality.d.ts +15 -0
  76. package/dist/utils/quality.d.ts.map +1 -0
  77. package/dist/utils/quality.js +47 -0
  78. package/dist/utils/quality.js.map +1 -0
  79. package/dist/web/render.d.ts +2 -0
  80. package/dist/web/render.d.ts.map +1 -0
  81. package/dist/web/render.js +593 -0
  82. package/dist/web/render.js.map +1 -0
  83. package/dist/web/server.d.ts +10 -0
  84. package/dist/web/server.d.ts.map +1 -0
  85. package/dist/web/server.js +136 -0
  86. package/dist/web/server.js.map +1 -0
  87. package/package.json +51 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SkillFlow Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,239 @@
1
- # Temporary Holding Version
1
+ # SkillFlow MCP (跨 Agent 技能命中监测与多维效能遥测中枢)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ `SkillFlow MCP` 是一个统一的 **Model Context Protocol (MCP)** 服务与技能效能分析引擎。
4
+
5
+ 它的核心作用是:在开发者使用各类 AI 编程智能体(**Claude Code、Trae、Qoder、WorkBuddy、Codex、DSH 等**)时,**无侵入地记录每轮对话中 Skill 的命中情况,量化技能对开发流程的帮助(估算节省的交互步骤与消耗的 Token),并提供单次对话与跨 Agent 全局多维度的效能看板**。
6
+
7
+ ---
8
+
9
+ ## 🌟 核心特性
10
+
11
+ 1. **统一跨 Agent 共享底座**:
12
+ - 基于 Node.js 24 原生内置的 `node:sqlite`(`DatabaseSync`)开发,**零原生 C++ 编译依赖**(Windows/macOS/Linux 开箱即用)。
13
+ - 默认存储于 `~/.skillflow/skillflow.db`,开启 WAL(Write-Ahead Logging)高并发读写模式。无论你在 Trae、Claude Code 还是 Qoder 中工作,所有会话与统计数据**实时汇总互通**。
14
+ 2. **精准的技能命中与收益核算**:
15
+ - 记录每次 Skill 触发的执行状态(`success`、`partial`、`failed`、`skipped`)。
16
+ - 内置**反事实基准对比模型(Benchmark Diffing)**,自动计算节省的流程步骤(`saved_steps`)与节省的 Token 数量(`saved_tokens`)。
17
+ 3. **多维统计分析看板**:
18
+ - **时间维度**:当前会话(`current_session`)、今日(`today`)、近 7 天(`7days`)、近 30 天(`30days`)、全量历史(`all`)。
19
+ - **Agent 生态矩阵**:横向对比 Claude Code、Trae、Qoder、WorkBuddy、Codex、DSH 各自的调用频次与贡献节约量。
20
+ - **技能价值榜**:Top 技能排名与终端/聊天窗口友好的 ASCII 进度与柱状图。
21
+ 4. **灵活的技能基准管理**:
22
+ - 预设 10 大标准技能基准(`code-review`、`frontend-design`、`security-review`、`simplify`、`docx`、`xlsx`、`pptx`、`pdf-reading`、`dataviz`、`workflow-authoring`)。
23
+ - 支持自定义新增或动态调整团队内部技能的基准参数。
24
+
25
+ ---
26
+
27
+ ## 🛠️ 提供的 MCP Tools & Resources
28
+
29
+ ### 1. MCP Tools
30
+ | MCP Tool 名称 | 核心用途 | 参数说明 |
31
+ |:---|:---|:---|
32
+ | `record_skill_hit` | 记录/上报一次 Skill 激活命中事件并核算收益 | `session_id` (必填), `skill_id` (必填), `agent_name`, `round_index`, `status`, `actual_tokens`, `actual_steps`, `saved_steps`, `saved_tokens`, `metadata` |
33
+ | `get_round_skill_impact` | 查看指定轮次或当前会话的技能命中情况与明细报告 | `session_id` (可选,默认取最近会话), `round_index` (可选,默认最新轮次), `include_session_summary` (默认 true) |
34
+ | `get_skill_dashboard` | 获取全景效能看板(按时间范围、Agent 矩阵、Skill 贡献榜聚合) | `time_range` (`current_session`/`today`/`7days`/`30days`/`all`), `session_id`, `agent_name`, `skill_id`, `group_by` |
35
+ | `manage_skill_benchmark` | 管理技能基准库(列表、详情、新增/修改基准、删除、重置预设) | `action` (`list`/`get`/`set`/`delete`/`reset_defaults`), `skill_id`, `skill_name`, `category`, `baseline_steps`, `baseline_tokens` |
36
+ | `get_web_dashboard_url` | 获取本地 Web 可视化看板的访问地址及运行状态 | 无必填入参,返回 `http://localhost:3000` 链接与说明 |
37
+
38
+ ### 2. MCP Resources (资源直读)
39
+ | 资源 URI | 类型 | 说明 |
40
+ |:---|:---|:---|
41
+ | `skillflow://dashboard` | `text/html` | 自包含的 Web 可视化看板 HTML 视图,支持支持 Resource 渲染的 Client 直接嵌入 |
42
+ | `skillflow://analytics.json` | `application/json` | 全局聚合分析指标的原始 JSON 数据 |
43
+ | `skillflow://skills` | `application/json` | 完整技能基准清单数据 |
44
+
45
+ ---
46
+
47
+ ## 🌐 进阶增强:本地 Web 可视化看板
48
+
49
+ 服务启动时会在后台自动拉起一个内置的极轻量级 Web 服务器(基于 Node.js 原生 `node:http`,零第三方 HTTP 依赖):
50
+ - **默认地址**:`http://localhost:3000`(若端口被占用会自动顺延至 3001..)
51
+ - **环境变量**:
52
+ - `SKILLFLOW_PORT=3000`(自定义端口)
53
+ - `SKILLFLOW_ENABLE_WEB=false`(若只想要单纯 stdio 模式可关闭 Web)
54
+ - **看板功能**:
55
+ - 🎨 现代暗色主题 UI、自适应响应式布局。
56
+ - ⚡ 实时 5 秒自动轮询刷新。
57
+ - 📊 流程步骤节约与 Token 节省排行榜。
58
+ - 🤖 Claude Code / Trae / Qoder / WorkBuddy / Codex / DSH 各平台占比分布。
59
+ - ⏱️ 实时调用流水列表(展示时间、会话 ID、技能名、状态、节省指标)。
60
+
61
+ ---
62
+
63
+ ## 🚀 快速启动与编译
64
+
65
+ ### 环境要求
66
+ - Node.js >= 24.0.0
67
+ - npm >= 10.0.0
68
+
69
+ ### 安装与构建
70
+ ```bash
71
+ # 1. 克隆或进入项目目录
72
+ cd D:\code\skill
73
+
74
+ # 2. 安装依赖
75
+ npm install
76
+
77
+ # 3. 运行完整测试套件 (包含单测与 MCP 协议集成测试)
78
+ npm test
79
+
80
+ # 4. 编译输出到 ./dist
81
+ npm run build
82
+ ```
83
+
84
+ ---
85
+
86
+ ## 🔌 各 Agent 接入与配置指南
87
+
88
+ 由于你的 Agent 全面支持 MCP,只需将编译产物 `D:\code\skill\dist\index.js` 添加到对应 Agent 的 MCP 配置文件中即可。
89
+
90
+ ### 1. Claude Code
91
+ 直接使用命令行添加:
92
+ ```bash
93
+ claude mcp add skillflow -- node D:/code/skill/dist/index.js
94
+ ```
95
+ 或直接写入用户全局配置 `~/.claude/settings.json`(或项目级 `.claude/settings.json`):
96
+ ```json
97
+ {
98
+ "mcpServers": {
99
+ "skillflow": {
100
+ "command": "node",
101
+ "args": ["D:/code/skill/dist/index.js"]
102
+ }
103
+ }
104
+ }
105
+ ```
106
+
107
+ ### 2. Trae (ByteDance)
108
+ 在 Trae 设置面板中的 **MCP Servers** 或 `mcp.json` 中配置:
109
+ ```json
110
+ {
111
+ "mcpServers": {
112
+ "skillflow": {
113
+ "command": "node",
114
+ "args": ["D:/code/skill/dist/index.js"],
115
+ "disabled": false
116
+ }
117
+ }
118
+ }
119
+ ```
120
+
121
+ ### 3. Qoder (阿里通义)
122
+ 在 Qoder 的 MCP 插件设置界面添加:
123
+ - **Name**: `skillflow`
124
+ - **Transport**: `stdio`
125
+ - **Command**: `node`
126
+ - **Arguments**: `["D:/code/skill/dist/index.js"]`
127
+
128
+ ### 4. WorkBuddy (腾讯)
129
+ 在 WorkBuddy 的 Agent 扩展 / 工具市场中选择添加本地 MCP:
130
+ ```json
131
+ {
132
+ "skillflow": {
133
+ "command": "node",
134
+ "args": ["D:/code/skill/dist/index.js"]
135
+ }
136
+ }
137
+ ```
138
+
139
+ ### 5. Codex & DSH
140
+ 在客户端对应的 MCP Server 列表填入:
141
+ ```json
142
+ {
143
+ "command": "node",
144
+ "args": ["D:/code/skill/dist/index.js"]
145
+ ]
146
+ ```
147
+
148
+ ---
149
+
150
+ ## 💬 实际交互使用示例
151
+
152
+ 配置完成后,在**任何一个 Agent** 中都可以用自然的中文直接询问:
153
+
154
+ ### 场景 1:探查本轮对话命中了什么 Skill
155
+ > **用户**:“查看刚才这轮对话有没有触发 skill?帮我省了多少东西?”
156
+ > **Agent**:自动调用 `get_round_skill_impact`,返回如下 Markdown 报告:
157
+
158
+ ```markdown
159
+ ### 🎯 Skill 命中与效能分析报告 (Round #2)
160
+
161
+ - **会话 ID**: `sess-20261111-trae-01`
162
+ - **宿主 Agent**: `Trae`
163
+ - **本轮命中技能数**: **2** 个
164
+ - **流程步骤节省**: **+18 步**
165
+ - **Token 节省估计**: **+8,850 Tokens** (82.3% 资源缩减, 价值 ~$0.03)
166
+
167
+ #### 📋 本轮命中清单明细
168
+
169
+ | 命中的 Skill | 分类 | 状态 | 实际步骤 | 基准步骤 | 步骤节省 | 实际 Token | Token 节省 | 节约比 |
170
+ | :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
171
+ | **Frontend Design** (`frontend-design`) | design | ✅ 成功 | 1 | 10 | **+9** | 620 | **+4,380** | 87.6% |
172
+ | **Code Review** (`code-review`) | engineering | ✅ 成功 | 1 | 10 | **+9** | 530 | **+4,470** | 89.4% |
173
+
174
+ ---
175
+ #### 📈 会话累计数据看板 (当前会话全轮次)
176
+
177
+ - **当前总交互轮次**: 2 轮
178
+ - **全会话技能总触发**: 3 次
179
+ - **全会话累计节约步骤**: **+25 步**
180
+ - **全会话累计节约 Token**: **+13,250 Tokens** (~$0.04)
181
+ - **流程自动化压缩率**: [████████████████░░] 93%
182
+ ```
183
+
184
+ ---
185
+
186
+ ### 场景 2:跨 Agent 全局效能看板
187
+ > **用户**:“统计一下我在所有 Agent 上的 skill 使用情况”
188
+ > **Agent**:自动调用 `get_skill_dashboard(time_range="all")`,返回全景大盘:
189
+
190
+ ```markdown
191
+ ## 📊 SkillFlow 跨平台全景效能看板
192
+
193
+ > **筛选范围**: `ALL` | **Agent 维度**: `全部生态` | **主视角**: `skill`
194
+
195
+ ### 💡 核心效能大盘指标
196
+
197
+ | 统计维度 | 数值指标 | 效能评估说明 |
198
+ | :--- | :--- | :--- |
199
+ | **覆盖会话** | **68** 场 | 活跃对话会话总数 |
200
+ | **Skill 触发总量** | **184** 次 | 成功率 **97.8%** |
201
+ | **累计精简流程** | **+1,240 步** | 消除的重复/排查交互步骤 |
202
+ | **累计节省 Token** | **+5,420,000 Tokens** | 折合模型成本节约 **~$16.26** |
203
+
204
+ ### 🏆 价值贡献最大 Skill Top 5 (节省步骤榜)
205
+
206
+ 1. `Code Review` [██████████████████] +540 步 (54次)
207
+ 2. `Frontend Design` [████████████░░░░░░] +360 步 (40次)
208
+ 3. `Security Review` [████████░░░░░░░░░░] +240 步 (20次)
209
+ 4. `Code Simplificat` [██████░░░░░░░░░░░░] +180 步 (30次)
210
+ 5. `Word Document Ge` [████░░░░░░░░░░░░░░] +120 步 (20次)
211
+
212
+ ### 🤖 各 Agent 生态调用与收益贡献矩阵
213
+
214
+ | Agent 宿主 | 覆盖会话 | 触发次数 | 占比 | 贡献节约步骤 | 贡献节约 Token |
215
+ | :--- | :---: | :---: | :---: | :---: | :---: |
216
+ | **Claude Code** | 28 | 82 次 | 44.6% | **+560 步** | **+2,450,000** |
217
+ | **Trae** | 18 | 48 次 | 26.1% | **+320 步** | **+1,420,000** |
218
+ | **WorkBuddy** | 12 | 31 次 | 16.8% | **+210 步** | **+910,000** |
219
+ | **Qoder** | 6 | 15 次 | 8.2% | **+105 步** | **+440,000** |
220
+ | **DSH / Codex** | 4 | 8 次 | 4.3% | **+45 步** | **+200,000** |
221
+ ```
222
+
223
+ ---
224
+
225
+ ## 📐 算法原理:如何量化“节省”?
226
+
227
+ 1. **基准对比(Benchmark Profile)**:
228
+ 对于某个技能(如 `code-review`),若不使用该技能,开发者手动审查代码、多轮询问与修复通常需要 **12 个步骤** 并消耗约 **6,500 Tokens**。
229
+ 2. **结算逻辑**:
230
+ - 当技能执行成功(`status = 'success'`)且实际花费 1 步与 500 Token 时:
231
+ - **节省步骤** = $12 - 1 = 11$ 步;
232
+ - **节省 Token** = $6500 - 500 = 6000$ Tokens。
233
+ - 当技能部分成功(`status = 'partial'`)时按 50% 折算。
234
+ - 当执行失败(`status = 'failed'`)时节省量计为 0。
235
+
236
+ ---
237
+
238
+ ## 📄 License
239
+ MIT License.
@@ -0,0 +1,22 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ export interface WorkBuddySniffResult {
3
+ syncedCount: number;
4
+ syncedSkills: string[];
5
+ }
6
+ export declare class WorkBuddySniffer {
7
+ private skillflowDb;
8
+ private telemetryService;
9
+ constructor(skillflowDb: DatabaseSync);
10
+ /**
11
+ * Find all WorkBuddy profile paths on the system
12
+ */
13
+ findWorkBuddyPaths(): {
14
+ dbPath: string;
15
+ cachePath: string;
16
+ }[];
17
+ /**
18
+ * Sync WorkBuddy sessions into SkillFlow database
19
+ */
20
+ sync(): WorkBuddySniffResult;
21
+ }
22
+ //# sourceMappingURL=workbuddy.sniffer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workbuddy.sniffer.d.ts","sourceRoot":"","sources":["../../src/adapters/workbuddy.sniffer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAK3C,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,qBAAa,gBAAgB;IAGf,OAAO,CAAC,WAAW;IAF/B,OAAO,CAAC,gBAAgB,CAAmB;gBAEvB,WAAW,EAAE,YAAY;IAI7C;;OAEG;IACH,kBAAkB,IAAI;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,EAAE;IAsC7D;;OAEG;IACH,IAAI,IAAI,oBAAoB;CA6F7B"}
@@ -0,0 +1,143 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { TelemetryService } from '../services/telemetry.service.js';
5
+ export class WorkBuddySniffer {
6
+ skillflowDb;
7
+ telemetryService;
8
+ constructor(skillflowDb) {
9
+ this.skillflowDb = skillflowDb;
10
+ this.telemetryService = new TelemetryService(skillflowDb);
11
+ }
12
+ /**
13
+ * Find all WorkBuddy profile paths on the system
14
+ */
15
+ findWorkBuddyPaths() {
16
+ const results = [];
17
+ // Common search locations
18
+ const candidates = [
19
+ 'D:/WorkBuddyProfiles/Account3/.workbuddy',
20
+ 'D:/WorkBuddyProfiles/Account1/.workbuddy',
21
+ 'D:/WorkBuddyProfiles/Account2/.workbuddy',
22
+ path.join(process.env.USERPROFILE || '', '.workbuddy'),
23
+ path.join(process.env.APPDATA || '', 'WorkBuddy'),
24
+ ];
25
+ // Check directory if D:/WorkBuddyProfiles exists
26
+ if (fs.existsSync('D:/WorkBuddyProfiles')) {
27
+ try {
28
+ const accounts = fs.readdirSync('D:/WorkBuddyProfiles');
29
+ for (const acc of accounts) {
30
+ const p = path.join('D:/WorkBuddyProfiles', acc, '.workbuddy');
31
+ if (fs.existsSync(p)) {
32
+ candidates.push(p);
33
+ }
34
+ }
35
+ }
36
+ catch { }
37
+ }
38
+ const uniquePaths = Array.from(new Set(candidates));
39
+ for (const dir of uniquePaths) {
40
+ const dbPath = path.join(dir, 'workbuddy.db');
41
+ const cachePath = path.join(dir, '.skill-list-cache.json');
42
+ if (fs.existsSync(dbPath) && fs.existsSync(cachePath)) {
43
+ results.push({ dbPath, cachePath });
44
+ }
45
+ }
46
+ return results;
47
+ }
48
+ /**
49
+ * Sync WorkBuddy sessions into SkillFlow database
50
+ */
51
+ sync() {
52
+ const locations = this.findWorkBuddyPaths();
53
+ if (locations.length === 0) {
54
+ return { syncedCount: 0, syncedSkills: [] };
55
+ }
56
+ let syncedCount = 0;
57
+ const syncedSkills = [];
58
+ for (const loc of locations) {
59
+ let wbDb = null;
60
+ try {
61
+ wbDb = new DatabaseSync(loc.dbPath, { readOnly: true });
62
+ const cacheData = JSON.parse(fs.readFileSync(loc.cachePath, 'utf8'));
63
+ const skillMap = new Map();
64
+ for (const s of cacheData.results || []) {
65
+ if (s.skillId)
66
+ skillMap.set(s.skillId, s);
67
+ if (s.resourceId)
68
+ skillMap.set(s.resourceId, s);
69
+ if (s.slug)
70
+ skillMap.set(s.slug, s);
71
+ if (s.overrideKey)
72
+ skillMap.set(s.overrideKey, s);
73
+ }
74
+ const sessions = wbDb
75
+ .prepare(`
76
+ SELECT s.*, u.used as tokens_used
77
+ FROM sessions s
78
+ LEFT JOIN session_usage u ON s.id = u.session_id
79
+ WHERE s.addon_selection IS NOT NULL
80
+ `)
81
+ .all();
82
+ for (const sess of sessions) {
83
+ try {
84
+ const addon = JSON.parse(sess.addon_selection);
85
+ if (!addon.microSceneIds || !Array.isArray(addon.microSceneIds))
86
+ continue;
87
+ for (const scene of addon.microSceneIds) {
88
+ if (scene.startsWith('skill://') || scene.startsWith('command://')) {
89
+ const rawId = scene.replace(/^(skill|command):\/\//, '');
90
+ const meta = skillMap.get(rawId);
91
+ const skillId = meta?.slug || meta?.overrideKey || rawId;
92
+ const skillName = meta?.name || meta?.slug || rawId;
93
+ // Check if already synced into SkillFlow
94
+ const alreadySynced = this.skillflowDb
95
+ .prepare('SELECT id FROM invocations WHERE session_id = ? AND skill_id = ?')
96
+ .get(sess.id, skillId);
97
+ if (!alreadySynced) {
98
+ const actualTokens = sess.tokens_used || 0;
99
+ this.telemetryService.recordHit({
100
+ session_id: sess.id,
101
+ skill_id: skillId,
102
+ agent_name: 'WorkBuddy',
103
+ model_name: sess.model || 'deepseek-v4.1-flash',
104
+ round_index: 1,
105
+ status: 'success',
106
+ actual_tokens: actualTokens,
107
+ actual_steps: 1,
108
+ metadata: {
109
+ title: sess.title,
110
+ model: sess.model,
111
+ created_at: sess.created_at,
112
+ source: 'workbuddy_auto_sniffer',
113
+ },
114
+ });
115
+ // Ensure skill name in manifest is human-friendly
116
+ this.skillflowDb
117
+ .prepare('UPDATE skills_manifest SET skill_name = ? WHERE skill_id = ?')
118
+ .run(skillName, skillId);
119
+ syncedCount++;
120
+ syncedSkills.push(`${skillName} (${sess.title})`);
121
+ }
122
+ }
123
+ }
124
+ }
125
+ catch { }
126
+ }
127
+ }
128
+ catch (err) {
129
+ process.stderr.write(`[SkillFlow Sniffer] Error syncing WorkBuddy: ${err.message}\n`);
130
+ }
131
+ finally {
132
+ if (wbDb) {
133
+ try {
134
+ wbDb.close();
135
+ }
136
+ catch { }
137
+ }
138
+ }
139
+ }
140
+ return { syncedCount, syncedSkills };
141
+ }
142
+ }
143
+ //# sourceMappingURL=workbuddy.sniffer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workbuddy.sniffer.js","sourceRoot":"","sources":["../../src/adapters/workbuddy.sniffer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface AppConfig {
2
+ dbPath: string;
3
+ isInMemory: boolean;
4
+ webPort: number;
5
+ enableWeb: boolean;
6
+ }
7
+ export declare function resolveDbPath(): string;
8
+ export declare function getConfig(): AppConfig;
9
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,aAAa,IAAI,MAAM,CAkBtC;AAED,wBAAgB,SAAS,IAAI,SAAS,CAWrC"}
package/dist/config.js ADDED
@@ -0,0 +1,33 @@
1
+ import path from 'node:path';
2
+ import os from 'node:os';
3
+ import fs from 'node:fs';
4
+ export function resolveDbPath() {
5
+ if (process.env.SKILLFLOW_DB_PATH) {
6
+ if (process.env.SKILLFLOW_DB_PATH === ':memory:') {
7
+ return ':memory:';
8
+ }
9
+ const customPath = path.resolve(process.env.SKILLFLOW_DB_PATH);
10
+ const dir = path.dirname(customPath);
11
+ if (!fs.existsSync(dir)) {
12
+ fs.mkdirSync(dir, { recursive: true });
13
+ }
14
+ return customPath;
15
+ }
16
+ const defaultDir = path.join(os.homedir(), '.skillflow');
17
+ if (!fs.existsSync(defaultDir)) {
18
+ fs.mkdirSync(defaultDir, { recursive: true });
19
+ }
20
+ return path.join(defaultDir, 'skillflow.db');
21
+ }
22
+ export function getConfig() {
23
+ const dbPath = resolveDbPath();
24
+ const webPort = parseInt(process.env.SKILLFLOW_PORT || '3000', 10);
25
+ const enableWeb = process.env.SKILLFLOW_ENABLE_WEB !== 'false';
26
+ return {
27
+ dbPath,
28
+ isInMemory: dbPath === ':memory:',
29
+ webPort: isNaN(webPort) ? 3000 : webPort,
30
+ enableWeb,
31
+ };
32
+ }
33
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AASzB,MAAM,UAAU,aAAa;IAC3B,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC;QAClC,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,KAAK,UAAU,EAAE,CAAC;YACjD,OAAO,UAAU,CAAC;QACpB,CAAC;QACD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;QAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACrC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;IACzD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;IACnE,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,KAAK,OAAO,CAAC;IAE/D,OAAO;QACL,MAAM;QACN,UAAU,EAAE,MAAM,KAAK,UAAU;QACjC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO;QACxC,SAAS;KACV,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ export declare function getDatabase(customPath?: string): DatabaseSync;
3
+ export declare function closeDatabase(): void;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAQ3C,wBAAgB,WAAW,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,YAAY,CAuB7D;AAED,wBAAgB,aAAa,IAAI,IAAI,CAUpC"}
@@ -0,0 +1,39 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ import { getConfig } from '../config.js';
3
+ import { initializeSchema } from './schema.js';
4
+ import { seedDefaultSkills } from './seed.js';
5
+ let instance = null;
6
+ let currentPath = null;
7
+ export function getDatabase(customPath) {
8
+ const targetPath = customPath || getConfig().dbPath;
9
+ if (instance && currentPath === targetPath) {
10
+ return instance;
11
+ }
12
+ if (instance) {
13
+ try {
14
+ instance.close();
15
+ }
16
+ catch {
17
+ // Ignore close error on re-initialization
18
+ }
19
+ }
20
+ const isInMemory = targetPath === ':memory:';
21
+ instance = new DatabaseSync(targetPath);
22
+ currentPath = targetPath;
23
+ initializeSchema(instance, isInMemory);
24
+ seedDefaultSkills(instance);
25
+ return instance;
26
+ }
27
+ export function closeDatabase() {
28
+ if (instance) {
29
+ try {
30
+ instance.close();
31
+ }
32
+ catch {
33
+ // Ignore error during shutdown
34
+ }
35
+ instance = null;
36
+ currentPath = null;
37
+ }
38
+ }
39
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C,IAAI,QAAQ,GAAwB,IAAI,CAAC;AACzC,IAAI,WAAW,GAAkB,IAAI,CAAC;AAEtC,MAAM,UAAU,WAAW,CAAC,UAAmB;IAC7C,MAAM,UAAU,GAAG,UAAU,IAAI,SAAS,EAAE,CAAC,MAAM,CAAC;IAEpD,IAAI,QAAQ,IAAI,WAAW,KAAK,UAAU,EAAE,CAAC;QAC3C,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,CAAC;YACH,QAAQ,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;QAAC,MAAM,CAAC;YACP,0CAA0C;QAC5C,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,UAAU,KAAK,UAAU,CAAC;IAC7C,QAAQ,GAAG,IAAI,YAAY,CAAC,UAAU,CAAC,CAAC;IACxC,WAAW,GAAG,UAAU,CAAC;IAEzB,gBAAgB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACvC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAE5B,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,CAAC;YACH,QAAQ,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;QAAC,MAAM,CAAC;YACP,+BAA+B;QACjC,CAAC;QACD,QAAQ,GAAG,IAAI,CAAC;QAChB,WAAW,GAAG,IAAI,CAAC;IACrB,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ export declare function initializeSchema(db: DatabaseSync, isInMemory?: boolean): void;
3
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAU3C,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,YAAY,EAAE,UAAU,UAAQ,GAAG,IAAI,CAkG3E"}
@@ -0,0 +1,103 @@
1
+ function tryAddColumn(db, table, colDef) {
2
+ try {
3
+ db.exec(`ALTER TABLE ${table} ADD COLUMN ${colDef};`);
4
+ }
5
+ catch {
6
+ // Column already exists
7
+ }
8
+ }
9
+ export function initializeSchema(db, isInMemory = false) {
10
+ // Concurrency and integrity pragmas
11
+ db.exec('PRAGMA foreign_keys = ON;');
12
+ db.exec('PRAGMA busy_timeout = 5000;');
13
+ if (!isInMemory) {
14
+ db.exec('PRAGMA journal_mode = WAL;');
15
+ db.exec('PRAGMA synchronous = NORMAL;');
16
+ }
17
+ // 1. Skills Manifest table
18
+ db.exec(`
19
+ CREATE TABLE IF NOT EXISTS skills_manifest (
20
+ skill_id TEXT PRIMARY KEY,
21
+ skill_name TEXT NOT NULL,
22
+ category TEXT NOT NULL DEFAULT 'general',
23
+ description TEXT,
24
+ baseline_steps INTEGER NOT NULL DEFAULT 5,
25
+ baseline_tokens INTEGER NOT NULL DEFAULT 3000,
26
+ baseline_manual_minutes INTEGER NOT NULL DEFAULT 15,
27
+ default_saved_steps INTEGER NOT NULL DEFAULT 4,
28
+ default_saved_tokens INTEGER NOT NULL DEFAULT 2000,
29
+ tags TEXT DEFAULT '[]',
30
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
31
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
32
+ );
33
+ `);
34
+ tryAddColumn(db, 'skills_manifest', 'baseline_manual_minutes INTEGER NOT NULL DEFAULT 15');
35
+ // 2. Sessions table
36
+ db.exec(`
37
+ CREATE TABLE IF NOT EXISTS sessions (
38
+ session_id TEXT PRIMARY KEY,
39
+ agent_name TEXT NOT NULL DEFAULT 'unknown',
40
+ title TEXT,
41
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
42
+ last_active_at TEXT NOT NULL DEFAULT (datetime('now')),
43
+ round_count INTEGER NOT NULL DEFAULT 1,
44
+ total_invocations INTEGER NOT NULL DEFAULT 0,
45
+ total_saved_steps INTEGER NOT NULL DEFAULT 0,
46
+ total_saved_tokens INTEGER NOT NULL DEFAULT 0,
47
+ total_saved_hours REAL NOT NULL DEFAULT 0.0,
48
+ total_cost_saved_usd REAL NOT NULL DEFAULT 0.0,
49
+ total_cost_saved_cny REAL NOT NULL DEFAULT 0.0,
50
+ total_corrections INTEGER NOT NULL DEFAULT 0,
51
+ metadata TEXT DEFAULT '{}'
52
+ );
53
+ `);
54
+ tryAddColumn(db, 'sessions', 'total_saved_hours REAL NOT NULL DEFAULT 0.0');
55
+ tryAddColumn(db, 'sessions', 'total_cost_saved_usd REAL NOT NULL DEFAULT 0.0');
56
+ tryAddColumn(db, 'sessions', 'total_cost_saved_cny REAL NOT NULL DEFAULT 0.0');
57
+ tryAddColumn(db, 'sessions', 'total_corrections INTEGER NOT NULL DEFAULT 0');
58
+ // 3. Invocations table
59
+ db.exec(`
60
+ CREATE TABLE IF NOT EXISTS invocations (
61
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
62
+ session_id TEXT NOT NULL,
63
+ agent_name TEXT NOT NULL DEFAULT 'unknown',
64
+ model_name TEXT NOT NULL DEFAULT 'unknown',
65
+ skill_id TEXT NOT NULL,
66
+ round_index INTEGER NOT NULL DEFAULT 1,
67
+ status TEXT NOT NULL CHECK(status IN ('success', 'failed', 'partial', 'skipped')),
68
+ actual_tokens INTEGER NOT NULL DEFAULT 0,
69
+ actual_steps INTEGER NOT NULL DEFAULT 1,
70
+ saved_steps INTEGER NOT NULL DEFAULT 0,
71
+ saved_tokens INTEGER NOT NULL DEFAULT 0,
72
+ net_tokens INTEGER NOT NULL DEFAULT 0,
73
+ saved_hours REAL NOT NULL DEFAULT 0.0,
74
+ cost_saved_usd REAL NOT NULL DEFAULT 0.0,
75
+ cost_saved_cny REAL NOT NULL DEFAULT 0.0,
76
+ has_correction INTEGER NOT NULL DEFAULT 0,
77
+ quality_profile TEXT NOT NULL DEFAULT 'efficient',
78
+ execution_time_ms INTEGER DEFAULT 0,
79
+ metadata TEXT DEFAULT '{}',
80
+ timestamp TEXT NOT NULL DEFAULT (datetime('now')),
81
+ FOREIGN KEY (skill_id) REFERENCES skills_manifest(skill_id) ON UPDATE CASCADE,
82
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
83
+ );
84
+ `);
85
+ tryAddColumn(db, 'invocations', "model_name TEXT NOT NULL DEFAULT 'unknown'");
86
+ tryAddColumn(db, 'invocations', 'saved_hours REAL NOT NULL DEFAULT 0.0');
87
+ tryAddColumn(db, 'invocations', 'cost_saved_usd REAL NOT NULL DEFAULT 0.0');
88
+ tryAddColumn(db, 'invocations', 'cost_saved_cny REAL NOT NULL DEFAULT 0.0');
89
+ tryAddColumn(db, 'invocations', 'net_tokens INTEGER NOT NULL DEFAULT 0');
90
+ tryAddColumn(db, 'invocations', 'has_correction INTEGER NOT NULL DEFAULT 0');
91
+ tryAddColumn(db, 'invocations', "quality_profile TEXT NOT NULL DEFAULT 'efficient'");
92
+ tryAddColumn(db, 'sessions', 'total_corrections INTEGER NOT NULL DEFAULT 0');
93
+ // Indices for query performance
94
+ db.exec(`
95
+ CREATE INDEX IF NOT EXISTS idx_invocations_session_round ON invocations(session_id, round_index);
96
+ CREATE INDEX IF NOT EXISTS idx_invocations_agent ON invocations(agent_name);
97
+ CREATE INDEX IF NOT EXISTS idx_invocations_model ON invocations(model_name);
98
+ CREATE INDEX IF NOT EXISTS idx_invocations_skill ON invocations(skill_id);
99
+ CREATE INDEX IF NOT EXISTS idx_invocations_timestamp ON invocations(timestamp);
100
+ CREATE INDEX IF NOT EXISTS idx_sessions_last_active ON sessions(last_active_at);
101
+ `);
102
+ }
103
+ //# sourceMappingURL=schema.js.map