@levelup-log/mcp-server 0.2.0 → 0.4.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.
- package/dist/chunk-FII2XEJ7.js +161 -0
- package/dist/chunk-FII2XEJ7.js.map +1 -0
- package/dist/{chunk-4GGNGOIO.js → chunk-VJKF2CNS.js} +252 -63
- package/dist/chunk-VJKF2CNS.js.map +1 -0
- package/dist/cli.js +11 -3
- package/dist/cli.js.map +1 -1
- package/dist/heartbeat-A4ZMVGSV.js +461 -0
- package/dist/heartbeat-A4ZMVGSV.js.map +1 -0
- package/dist/init-GKLMB6BS.js +408 -0
- package/dist/init-GKLMB6BS.js.map +1 -0
- package/dist/server.d.ts +150 -1
- package/dist/server.js +4 -1
- package/package.json +15 -13
- package/dist/chunk-4GGNGOIO.js.map +0 -1
- package/dist/init-GNFWFY5S.js +0 -109
- package/dist/init-GNFWFY5S.js.map +0 -1
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/utils/config.ts
|
|
4
|
+
var CONFIG = {
|
|
5
|
+
SUPABASE_URL: process.env.LEVELUP_SUPABASE_URL || "https://hkuvfhfwbhkjmeqvwrxy.supabase.co",
|
|
6
|
+
SUPABASE_ANON_KEY: process.env.LEVELUP_SUPABASE_ANON_KEY || "sb_publishable_RilZivOWm3FIs6ueIA67Fw_07ZKjoEY",
|
|
7
|
+
AUTH_PORT: parseInt(process.env.LEVELUP_AUTH_PORT || "19876", 10),
|
|
8
|
+
DEBUG: process.env.LEVELUP_DEBUG === "true"
|
|
9
|
+
};
|
|
10
|
+
var CATEGORY_DEFINITIONS = {
|
|
11
|
+
// ── Technical ──────────────────────────────────────────────────────────────
|
|
12
|
+
code: {
|
|
13
|
+
label: "Coding",
|
|
14
|
+
emoji: "\u{1F4BB}",
|
|
15
|
+
description: "\u5BEB\u7A0B\u5F0F\u3001\u529F\u80FD\u958B\u767C\u3001\u65B0\u589E\u529F\u80FD",
|
|
16
|
+
output_unit: "\u6A94\u6848/\u6A21\u7D44\uFF08\u5C0F\u51FD\u5F0F=1, \u5927\u6A94=5-10, \u591A\u7D44\u4EF6=15\uFF09",
|
|
17
|
+
input_unit: "\u6A94\u6848\u95B1\u8B80 / API \u6587\u4EF6\u67E5\u95B1",
|
|
18
|
+
xp_weight: 1
|
|
19
|
+
},
|
|
20
|
+
fix: {
|
|
21
|
+
label: "Bug Fix",
|
|
22
|
+
emoji: "\u{1FAB2}",
|
|
23
|
+
description: "\u4FEE bug\u3001\u4FEE\u5FA9\u932F\u8AA4\u884C\u70BA",
|
|
24
|
+
output_unit: "\u4FEE\u5FA9\u7684 bug \u6578\uFF08\u542B\u6E2C\u8A66\u9A57\u8B49 = +1\uFF09",
|
|
25
|
+
input_unit: "\u932F\u8AA4\u65E5\u8A8C / \u7A0B\u5F0F\u78BC\u8FFD\u8E64",
|
|
26
|
+
xp_weight: 1.1
|
|
27
|
+
},
|
|
28
|
+
deploy: {
|
|
29
|
+
label: "Deploy",
|
|
30
|
+
emoji: "\u{1F680}",
|
|
31
|
+
description: "\u90E8\u7F72\u4E0A\u7DDA\u3001\u767C\u5E03\u7248\u672C\u3001\u63A8\u9001 npm / App Store",
|
|
32
|
+
output_unit: "\u90E8\u7F72\u76EE\u6A19\u6578\uFF081 \u670D\u52D9/\u74B0\u5883 = 1\uFF09",
|
|
33
|
+
input_unit: "\u8A2D\u5B9A\u6A94 / CI logs \u67E5\u95B1",
|
|
34
|
+
xp_weight: 1.3
|
|
35
|
+
},
|
|
36
|
+
test: {
|
|
37
|
+
label: "Testing",
|
|
38
|
+
emoji: "\u{1F9EA}",
|
|
39
|
+
description: "\u5BEB\u6E2C\u8A66\u3001E2E\u3001TDD",
|
|
40
|
+
output_unit: "\u6E2C\u8A66\u6848\u4F8B\u6578\uFF081 test case = 1\uFF09",
|
|
41
|
+
input_unit: "\u5F85\u6E2C\u7A0B\u5F0F\u78BC\u95B1\u8B80",
|
|
42
|
+
xp_weight: 1
|
|
43
|
+
},
|
|
44
|
+
docs: {
|
|
45
|
+
label: "Documentation",
|
|
46
|
+
emoji: "\u{1F4DD}",
|
|
47
|
+
description: "\u5BEB\u6587\u4EF6\u3001README\u3001API \u8AAA\u660E",
|
|
48
|
+
output_unit: "\u9801\u6578 / \u7AE0\u7BC0\u6578\uFF081 \u5B8C\u6574\u6BB5\u843D = 1\uFF09",
|
|
49
|
+
input_unit: "\u53C3\u8003\u8CC7\u6599 / \u73FE\u6709\u6587\u4EF6",
|
|
50
|
+
xp_weight: 0.9
|
|
51
|
+
},
|
|
52
|
+
refactor: {
|
|
53
|
+
label: "Refactor",
|
|
54
|
+
emoji: "\u{1F527}",
|
|
55
|
+
description: "\u91CD\u69CB\u3001\u6539\u5584\u7A0B\u5F0F\u78BC\u54C1\u8CEA",
|
|
56
|
+
output_unit: "\u91CD\u69CB\u7684\u6A21\u7D44/\u51FD\u5F0F\u6578",
|
|
57
|
+
input_unit: "\u95B1\u8B80\u73FE\u6709\u7A0B\u5F0F\u78BC",
|
|
58
|
+
xp_weight: 1
|
|
59
|
+
},
|
|
60
|
+
review: {
|
|
61
|
+
label: "Code Review",
|
|
62
|
+
emoji: "\u{1F441}",
|
|
63
|
+
description: "Code review\u3001PR \u5BE9\u67E5",
|
|
64
|
+
output_unit: "\u5BE9\u67E5\u7684 PR / \u6A94\u6848\u6578",
|
|
65
|
+
input_unit: "\u95B1\u8B80\u7684\u7A0B\u5F0F\u78BC\uFF08\u6BCF 100 \u884C = 1\uFF09",
|
|
66
|
+
xp_weight: 0.9
|
|
67
|
+
},
|
|
68
|
+
ops: {
|
|
69
|
+
label: "DevOps / Ops",
|
|
70
|
+
emoji: "\u2699\uFE0F",
|
|
71
|
+
description: "\u7DAD\u904B\u3001\u57FA\u790E\u8A2D\u65BD\u3001\u76E3\u63A7\u3001CI/CD",
|
|
72
|
+
output_unit: "\u8A2D\u5B9A/\u8173\u672C/\u670D\u52D9\u6578\uFF081 \u5B8C\u6210\u9805\u76EE = 1\uFF09",
|
|
73
|
+
input_unit: "\u65E5\u8A8C / \u6587\u4EF6\u67E5\u95B1",
|
|
74
|
+
xp_weight: 1.2
|
|
75
|
+
},
|
|
76
|
+
// ── Knowledge ──────────────────────────────────────────────────────────────
|
|
77
|
+
learn: {
|
|
78
|
+
label: "Learning",
|
|
79
|
+
emoji: "\u{1F4DA}",
|
|
80
|
+
description: "\u5B78\u7FD2\u65B0\u77E5\u3001\u95B1\u8B80\u6280\u8853\u6587\u7AE0\u3001\u4E0A\u8AB2\u3001\u8A9E\u8A00\u5B78\u7FD2",
|
|
81
|
+
output_unit: "\u6982\u5FF5/\u7AE0\u7BC0/\u7DF4\u7FD2\u984C\uFF08\u638C\u63E1 1 \u500B\u65B0\u6982\u5FF5 = 1\uFF09",
|
|
82
|
+
input_unit: "\u95B1\u8B80\u7BC7\u6578 / \u5F71\u7247\u6578",
|
|
83
|
+
xp_weight: 1
|
|
84
|
+
},
|
|
85
|
+
// ── Milestones ─────────────────────────────────────────────────────────────
|
|
86
|
+
milestone: {
|
|
87
|
+
label: "Milestone",
|
|
88
|
+
emoji: "\u{1F3C6}",
|
|
89
|
+
description: "\u91CC\u7A0B\u7891\u3001\u91CD\u5927\u9054\u6210\u3001\u7279\u6B8A\u7D00\u5FF5",
|
|
90
|
+
output_unit: "\u91CC\u7A0B\u7891\u6578\uFF08\u901A\u5E38 = 1\uFF09",
|
|
91
|
+
input_unit: "\u6E96\u5099\u5DE5\u4F5C / \u7814\u7A76",
|
|
92
|
+
xp_weight: 1.5
|
|
93
|
+
},
|
|
94
|
+
// ── Life ───────────────────────────────────────────────────────────────────
|
|
95
|
+
life: {
|
|
96
|
+
label: "Life",
|
|
97
|
+
emoji: "\u{1F3E0}",
|
|
98
|
+
description: "\u65E5\u5E38\u751F\u6D3B\uFF1A\u5BB6\u52D9\u3001\u8DD1\u817F\u3001\u80B2\u5152\u3001\u63A1\u8CFC\u3001\u884C\u653F",
|
|
99
|
+
output_unit: "\u5B8C\u6210\u7684\u4E8B\u9805\uFF08\u8DD1\u817F=1, \u5E36\u5C0F\u5B69\u51FA\u9580=2\uFF09",
|
|
100
|
+
input_unit: "\u8A08\u756B / \u7814\u7A76\uFF08\u67E5\u8DEF\u7DDA\u3001\u6BD4\u50F9\u7B49\uFF09",
|
|
101
|
+
xp_weight: 0.9
|
|
102
|
+
},
|
|
103
|
+
health: {
|
|
104
|
+
label: "Health",
|
|
105
|
+
emoji: "\u{1F4AA}",
|
|
106
|
+
description: "\u904B\u52D5\u3001\u98F2\u98DF\u7BA1\u7406\u3001\u5C31\u91AB\u3001\u5065\u5EB7\u7FD2\u6163",
|
|
107
|
+
output_unit: "30 \u5206\u9418\u904B\u52D5\u584A \u6216 \u516C\u91CC\u6578 \u6216 \u5B8C\u6210\u7642\u7A0B",
|
|
108
|
+
input_unit: "\u67E5\u5065\u5EB7\u8CC7\u6599 / \u8FFD\u8E64\u8A18\u9304",
|
|
109
|
+
xp_weight: 1.1
|
|
110
|
+
},
|
|
111
|
+
finance: {
|
|
112
|
+
label: "Finance",
|
|
113
|
+
emoji: "\u{1F4B0}",
|
|
114
|
+
description: "\u8A18\u5E33\u3001\u6295\u8CC7\u3001\u7406\u8CA1\u898F\u5283\u3001\u5831\u7A05\u3001\u4FDD\u96AA",
|
|
115
|
+
output_unit: "\u5B8C\u6210\u7684\u8CA1\u52D9\u4EFB\u52D9\uFF081 \u9805\u76EE = 1\uFF09",
|
|
116
|
+
input_unit: "\u7814\u7A76\u5831\u544A / \u8CC7\u6599\u67E5\u95B1",
|
|
117
|
+
xp_weight: 1
|
|
118
|
+
},
|
|
119
|
+
social: {
|
|
120
|
+
label: "Social",
|
|
121
|
+
emoji: "\u{1F91D}",
|
|
122
|
+
description: "\u793E\u4EA4\u3001\u5E6B\u52A9\u4ED6\u4EBA\u3001\u805A\u6703\u3001\u793E\u7FA4\u8CA2\u737B",
|
|
123
|
+
output_unit: "\u4E92\u52D5\u7684\u4EBA\u6578 \u6216 \u5B8C\u6210\u7684\u793E\u4EA4\u4EFB\u52D9",
|
|
124
|
+
input_unit: "\u6E96\u5099 / \u80CC\u666F\u7814\u7A76",
|
|
125
|
+
xp_weight: 0.9
|
|
126
|
+
},
|
|
127
|
+
creative: {
|
|
128
|
+
label: "Creative",
|
|
129
|
+
emoji: "\u{1F3A8}",
|
|
130
|
+
description: "\u5275\u4F5C\uFF1A\u5BEB\u6587\u7AE0\u3001\u8A2D\u8A08\u3001\u97F3\u6A02\u3001\u5F71\u7247\u3001\u651D\u5F71",
|
|
131
|
+
output_unit: "\u5B8C\u6210\u54C1\uFF08\u6587\u7AE0=3, \u5716=1, \u77ED\u5F71\u7247=2\uFF09",
|
|
132
|
+
input_unit: "\u53C3\u8003\u8CC7\u6599 / \u7D20\u6750\u7814\u7A76",
|
|
133
|
+
xp_weight: 1
|
|
134
|
+
},
|
|
135
|
+
spiritual: {
|
|
136
|
+
label: "Spiritual",
|
|
137
|
+
emoji: "\u{1F52E}",
|
|
138
|
+
description: "\u8EAB\u5FC3\u9748\uFF1A\u51A5\u60F3\u3001\u5360\u535C\u3001\u7948\u79B1\u3001\u5100\u5F0F\u3001\u80FD\u91CF\u7DF4\u7FD2\u3001\u5167\u5728\u63A2\u7D22",
|
|
139
|
+
output_unit: "\u5B8C\u6210\u7684\u7DF4\u7FD2\uFF081 \u6B21\u51A5\u60F3=1, \u5360\u535C\u89E3\u8B80=2\uFF09",
|
|
140
|
+
input_unit: "\u5B78\u7FD2 / \u7814\u7A76\u9748\u6027\u8CC7\u6599",
|
|
141
|
+
xp_weight: 0.85
|
|
142
|
+
},
|
|
143
|
+
hobby: {
|
|
144
|
+
label: "Hobby",
|
|
145
|
+
emoji: "\u{1F3AE}",
|
|
146
|
+
description: "\u8208\u8DA3\u5A1B\u6A02\uFF1A\u6253\u96FB\u52D5\u3001\u770B\u96FB\u5F71/\u66F8\u3001\u6536\u85CF\u3001\u684C\u904A\u3001\u5712\u85DD\u3001\u70F9\u98EA",
|
|
147
|
+
output_unit: "\u5B8C\u6210\u7684\u6D3B\u52D5\u6BB5\uFF081 \u5C0F\u6642\u96FB\u73A9=1, \u5B8C\u6210\u4E00\u672C\u66F8=5\uFF09",
|
|
148
|
+
input_unit: "\u67E5\u8CC7\u6599 / \u653B\u7565\u7814\u7A76",
|
|
149
|
+
xp_weight: 0.8
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
var ACHIEVEMENT_CATEGORIES = Object.keys(
|
|
153
|
+
CATEGORY_DEFINITIONS
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
export {
|
|
157
|
+
CONFIG,
|
|
158
|
+
CATEGORY_DEFINITIONS,
|
|
159
|
+
ACHIEVEMENT_CATEGORIES
|
|
160
|
+
};
|
|
161
|
+
//# sourceMappingURL=chunk-FII2XEJ7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/config.ts"],"sourcesContent":["export const CONFIG = {\n SUPABASE_URL:\n process.env.LEVELUP_SUPABASE_URL ||\n \"https://hkuvfhfwbhkjmeqvwrxy.supabase.co\",\n SUPABASE_ANON_KEY:\n process.env.LEVELUP_SUPABASE_ANON_KEY ||\n \"sb_publishable_RilZivOWm3FIs6ueIA67Fw_07ZKjoEY\",\n AUTH_PORT: parseInt(process.env.LEVELUP_AUTH_PORT || \"19876\", 10),\n DEBUG: process.env.LEVELUP_DEBUG === \"true\",\n} as const;\n\n// ─── Category Definitions ────────────────────────────────────────────────────\n// Single source of truth for all achievement categories.\n// ACHIEVEMENT_CATEGORIES and AchievementCategory are derived from this object.\n//\n// xp_weight: category difficulty/verifiability multiplier applied to XP\n// >1.0 = higher stakes or harder (deploy, ops, fix)\n// 1.0 = baseline (code, learn)\n// <1.0 = lighter or self-reported by nature (spiritual, hobby, social)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type CategoryDef = {\n label: string; // Display name\n emoji: string; // Visual identifier\n description: string; // What this category covers\n output_unit: string; // What 1 output_unit means for this category\n input_unit: string; // What 1 input_unit means for this category\n xp_weight: number; // Final XP multiplier (applied after base + bonuses)\n};\n\nexport const CATEGORY_DEFINITIONS = {\n // ── Technical ──────────────────────────────────────────────────────────────\n code: {\n label: \"Coding\",\n emoji: \"💻\",\n description: \"寫程式、功能開發、新增功能\",\n output_unit: \"檔案/模組(小函式=1, 大檔=5-10, 多組件=15)\",\n input_unit: \"檔案閱讀 / API 文件查閱\",\n xp_weight: 1.0,\n },\n fix: {\n label: \"Bug Fix\",\n emoji: \"🪲\",\n description: \"修 bug、修復錯誤行為\",\n output_unit: \"修復的 bug 數(含測試驗證 = +1)\",\n input_unit: \"錯誤日誌 / 程式碼追蹤\",\n xp_weight: 1.1,\n },\n deploy: {\n label: \"Deploy\",\n emoji: \"🚀\",\n description: \"部署上線、發布版本、推送 npm / App Store\",\n output_unit: \"部署目標數(1 服務/環境 = 1)\",\n input_unit: \"設定檔 / CI logs 查閱\",\n xp_weight: 1.3,\n },\n test: {\n label: \"Testing\",\n emoji: \"🧪\",\n description: \"寫測試、E2E、TDD\",\n output_unit: \"測試案例數(1 test case = 1)\",\n input_unit: \"待測程式碼閱讀\",\n xp_weight: 1.0,\n },\n docs: {\n label: \"Documentation\",\n emoji: \"📝\",\n description: \"寫文件、README、API 說明\",\n output_unit: \"頁數 / 章節數(1 完整段落 = 1)\",\n input_unit: \"參考資料 / 現有文件\",\n xp_weight: 0.9,\n },\n refactor: {\n label: \"Refactor\",\n emoji: \"🔧\",\n description: \"重構、改善程式碼品質\",\n output_unit: \"重構的模組/函式數\",\n input_unit: \"閱讀現有程式碼\",\n xp_weight: 1.0,\n },\n review: {\n label: \"Code Review\",\n emoji: \"👁\",\n description: \"Code review、PR 審查\",\n output_unit: \"審查的 PR / 檔案數\",\n input_unit: \"閱讀的程式碼(每 100 行 = 1)\",\n xp_weight: 0.9,\n },\n ops: {\n label: \"DevOps / Ops\",\n emoji: \"⚙️\",\n description: \"維運、基礎設施、監控、CI/CD\",\n output_unit: \"設定/腳本/服務數(1 完成項目 = 1)\",\n input_unit: \"日誌 / 文件查閱\",\n xp_weight: 1.2,\n },\n // ── Knowledge ──────────────────────────────────────────────────────────────\n learn: {\n label: \"Learning\",\n emoji: \"📚\",\n description: \"學習新知、閱讀技術文章、上課、語言學習\",\n output_unit: \"概念/章節/練習題(掌握 1 個新概念 = 1)\",\n input_unit: \"閱讀篇數 / 影片數\",\n xp_weight: 1.0,\n },\n // ── Milestones ─────────────────────────────────────────────────────────────\n milestone: {\n label: \"Milestone\",\n emoji: \"🏆\",\n description: \"里程碑、重大達成、特殊紀念\",\n output_unit: \"里程碑數(通常 = 1)\",\n input_unit: \"準備工作 / 研究\",\n xp_weight: 1.5,\n },\n // ── Life ───────────────────────────────────────────────────────────────────\n life: {\n label: \"Life\",\n emoji: \"🏠\",\n description: \"日常生活:家務、跑腿、育兒、採購、行政\",\n output_unit: \"完成的事項(跑腿=1, 帶小孩出門=2)\",\n input_unit: \"計畫 / 研究(查路線、比價等)\",\n xp_weight: 0.9,\n },\n health: {\n label: \"Health\",\n emoji: \"💪\",\n description: \"運動、飲食管理、就醫、健康習慣\",\n output_unit: \"30 分鐘運動塊 或 公里數 或 完成療程\",\n input_unit: \"查健康資料 / 追蹤記錄\",\n xp_weight: 1.1,\n },\n finance: {\n label: \"Finance\",\n emoji: \"💰\",\n description: \"記帳、投資、理財規劃、報稅、保險\",\n output_unit: \"完成的財務任務(1 項目 = 1)\",\n input_unit: \"研究報告 / 資料查閱\",\n xp_weight: 1.0,\n },\n social: {\n label: \"Social\",\n emoji: \"🤝\",\n description: \"社交、幫助他人、聚會、社群貢獻\",\n output_unit: \"互動的人數 或 完成的社交任務\",\n input_unit: \"準備 / 背景研究\",\n xp_weight: 0.9,\n },\n creative: {\n label: \"Creative\",\n emoji: \"🎨\",\n description: \"創作:寫文章、設計、音樂、影片、攝影\",\n output_unit: \"完成品(文章=3, 圖=1, 短影片=2)\",\n input_unit: \"參考資料 / 素材研究\",\n xp_weight: 1.0,\n },\n spiritual: {\n label: \"Spiritual\",\n emoji: \"🔮\",\n description: \"身心靈:冥想、占卜、祈禱、儀式、能量練習、內在探索\",\n output_unit: \"完成的練習(1 次冥想=1, 占卜解讀=2)\",\n input_unit: \"學習 / 研究靈性資料\",\n xp_weight: 0.85,\n },\n hobby: {\n label: \"Hobby\",\n emoji: \"🎮\",\n description: \"興趣娛樂:打電動、看電影/書、收藏、桌遊、園藝、烹飪\",\n output_unit: \"完成的活動段(1 小時電玩=1, 完成一本書=5)\",\n input_unit: \"查資料 / 攻略研究\",\n xp_weight: 0.8,\n },\n} as const satisfies Record<string, CategoryDef>;\n\nexport const ACHIEVEMENT_CATEGORIES = Object.keys(\n CATEGORY_DEFINITIONS,\n) as (keyof typeof CATEGORY_DEFINITIONS)[];\n\nexport type AchievementCategory = keyof typeof CATEGORY_DEFINITIONS;\n"],"mappings":"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","names":[]}
|
|
@@ -1,33 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ACHIEVEMENT_CATEGORIES,
|
|
4
|
+
CATEGORY_DEFINITIONS,
|
|
5
|
+
CONFIG
|
|
6
|
+
} from "./chunk-FII2XEJ7.js";
|
|
2
7
|
|
|
3
8
|
// src/server.ts
|
|
4
9
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
10
|
import { z } from "zod";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
SUPABASE_URL: process.env.LEVELUP_SUPABASE_URL || "https://hkuvfhfwbhkjmeqvwrxy.supabase.co",
|
|
10
|
-
SUPABASE_ANON_KEY: process.env.LEVELUP_SUPABASE_ANON_KEY || "sb_publishable_RilZivOWm3FIs6ueIA67Fw_07ZKjoEY",
|
|
11
|
-
AUTH_PORT: parseInt(process.env.LEVELUP_AUTH_PORT || "19876", 10),
|
|
12
|
-
DEBUG: process.env.LEVELUP_DEBUG === "true"
|
|
13
|
-
};
|
|
14
|
-
var ACHIEVEMENT_CATEGORIES = [
|
|
15
|
-
"code",
|
|
16
|
-
"fix",
|
|
17
|
-
"deploy",
|
|
18
|
-
"test",
|
|
19
|
-
"docs",
|
|
20
|
-
"refactor",
|
|
21
|
-
"review",
|
|
22
|
-
"learn",
|
|
23
|
-
"ops",
|
|
24
|
-
"milestone",
|
|
25
|
-
"life",
|
|
26
|
-
"health",
|
|
27
|
-
"finance",
|
|
28
|
-
"social",
|
|
29
|
-
"creative"
|
|
30
|
-
];
|
|
11
|
+
import * as fs from "fs";
|
|
12
|
+
import * as os from "os";
|
|
13
|
+
import * as path from "path";
|
|
31
14
|
|
|
32
15
|
// src/utils/rate-limiter.ts
|
|
33
16
|
var CATEGORY_COOLDOWN_MS = 6e4;
|
|
@@ -264,9 +247,9 @@ async function login() {
|
|
|
264
247
|
}
|
|
265
248
|
|
|
266
249
|
// src/utils/api.ts
|
|
267
|
-
async function apiGet(
|
|
250
|
+
async function apiGet(path2, params) {
|
|
268
251
|
const token = await getValidToken();
|
|
269
|
-
const url = new URL(`${CONFIG.SUPABASE_URL}/functions/v1/${
|
|
252
|
+
const url = new URL(`${CONFIG.SUPABASE_URL}/functions/v1/${path2}`);
|
|
270
253
|
if (params) {
|
|
271
254
|
for (const [k, v] of Object.entries(params)) {
|
|
272
255
|
url.searchParams.set(k, v);
|
|
@@ -290,9 +273,9 @@ async function apiGet(path, params) {
|
|
|
290
273
|
return { error: error.message, status: 0 };
|
|
291
274
|
}
|
|
292
275
|
}
|
|
293
|
-
async function apiPost(
|
|
276
|
+
async function apiPost(path2, body) {
|
|
294
277
|
const token = await getValidToken();
|
|
295
|
-
const url = `${CONFIG.SUPABASE_URL}/functions/v1/${
|
|
278
|
+
const url = `${CONFIG.SUPABASE_URL}/functions/v1/${path2}`;
|
|
296
279
|
try {
|
|
297
280
|
const res = await fetch(url, {
|
|
298
281
|
method: "POST",
|
|
@@ -315,29 +298,102 @@ async function apiPost(path, body) {
|
|
|
315
298
|
}
|
|
316
299
|
|
|
317
300
|
// src/server.ts
|
|
301
|
+
var DIARY_DIR = path.join(os.homedir(), "Documents", "tai", "\u65E5\u8A18");
|
|
302
|
+
function diaryPath(date) {
|
|
303
|
+
return path.join(DIARY_DIR, `${date}.md`);
|
|
304
|
+
}
|
|
305
|
+
function todayDate() {
|
|
306
|
+
return (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
307
|
+
}
|
|
308
|
+
function buildDiaryMd(date, content) {
|
|
309
|
+
return `---
|
|
310
|
+
date: ${date}
|
|
311
|
+
tags: [\u65E5\u8A18, levelup]
|
|
312
|
+
public: false
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
${content}
|
|
316
|
+
`;
|
|
317
|
+
}
|
|
318
|
+
var COMPLEXITY_BASE = {
|
|
319
|
+
trivial: 10,
|
|
320
|
+
normal: 30,
|
|
321
|
+
significant: 75,
|
|
322
|
+
major: 150,
|
|
323
|
+
milestone: 300
|
|
324
|
+
};
|
|
325
|
+
function timeMultiplier(minutes) {
|
|
326
|
+
if (minutes < 15) return 0.7;
|
|
327
|
+
if (minutes < 60) return 1;
|
|
328
|
+
if (minutes < 180) return 1.3;
|
|
329
|
+
return 1.6;
|
|
330
|
+
}
|
|
331
|
+
function outputBonus(units) {
|
|
332
|
+
if (units <= 0) return 0;
|
|
333
|
+
return Math.min(Math.round(Math.log2(units + 1) * 12), 60);
|
|
334
|
+
}
|
|
335
|
+
function inputBonus(units) {
|
|
336
|
+
return Math.min(Math.max(units, 0), 15);
|
|
337
|
+
}
|
|
338
|
+
function roundsBonus(rounds) {
|
|
339
|
+
return Math.min(Math.max(rounds, 0), 25);
|
|
340
|
+
}
|
|
341
|
+
function calculateXp(params) {
|
|
342
|
+
const catWeight = CATEGORY_DEFINITIONS[params.category]?.xp_weight ?? 1;
|
|
343
|
+
const base = COMPLEXITY_BASE[params.complexity] * timeMultiplier(params.time_minutes ?? 30);
|
|
344
|
+
const bonuses = outputBonus(params.output_units ?? 0) + inputBonus(params.input_units ?? 0) + roundsBonus(params.conversation_rounds ?? 0);
|
|
345
|
+
const raw = Math.min(
|
|
346
|
+
500,
|
|
347
|
+
Math.max(5, Math.round((base + bonuses) * catWeight))
|
|
348
|
+
);
|
|
349
|
+
return params.self_reported ? Math.max(5, Math.round(raw * 0.85)) : raw;
|
|
350
|
+
}
|
|
351
|
+
function buildOutputUnitGuide() {
|
|
352
|
+
return Object.entries(CATEGORY_DEFINITIONS).map(([key, def]) => ` ${key.padEnd(12)} \u2192 ${def.output_unit}`).join("\n");
|
|
353
|
+
}
|
|
354
|
+
var SERVER_INSTRUCTIONS = `LevelUp.log Achievement Tracker is active.
|
|
355
|
+
|
|
356
|
+
PROACTIVELY call record_achievement after any meaningful task \u2014 don't wait to be asked.
|
|
357
|
+
|
|
358
|
+
Call when:
|
|
359
|
+
\u2022 Code written, feature added, bug fixed, refactored
|
|
360
|
+
\u2022 Tests written, docs updated, deployment done, ops task completed
|
|
361
|
+
\u2022 Learning session, research, life/health/finance task done
|
|
362
|
+
|
|
363
|
+
Don't call for: casual Q&A, simple lookups, short explanations with no output.
|
|
364
|
+
|
|
365
|
+
When time_minutes is unclear, ask: "\u5927\u6982\u82B1\u4E86\u591A\u4E45\uFF1F"
|
|
366
|
+
self_reported=true when user narrates past events ("I exercised this morning").`;
|
|
318
367
|
function createServer2() {
|
|
319
|
-
const server = new McpServer(
|
|
320
|
-
name: "levelup-log",
|
|
321
|
-
|
|
322
|
-
|
|
368
|
+
const server = new McpServer(
|
|
369
|
+
{ name: "levelup-log", version: "0.1.0" },
|
|
370
|
+
{ instructions: SERVER_INSTRUCTIONS }
|
|
371
|
+
);
|
|
323
372
|
server.registerTool(
|
|
324
373
|
"record_achievement",
|
|
325
374
|
{
|
|
326
375
|
title: "Record Achievement",
|
|
327
|
-
description: `Proactively call this when the user completes meaningful work
|
|
376
|
+
description: `Proactively call this when the user completes meaningful work \u2014 coding, bug fixes, features, life tasks, learning, health, creative work, or any productive activity.
|
|
377
|
+
|
|
378
|
+
WHEN TO CALL: Real effort + tangible output. Don't wait to be asked.
|
|
379
|
+
DO NOT CALL: simple Q&A, casual chat, trivial lookups with no output.
|
|
328
380
|
|
|
329
|
-
|
|
330
|
-
DO NOT CALL for: simple questions answered, casual chat, trivial lookups with no output.
|
|
381
|
+
SET self_reported=true when user narrates a past event (e.g. "I went for a run today") vs. completing work during this conversation. Applies 15% XP discount \u2014 still worth recording.
|
|
331
382
|
|
|
332
|
-
|
|
333
|
-
complexity
|
|
334
|
-
|
|
335
|
-
|
|
383
|
+
FILL AS MANY PARAMS AS YOU CAN OBSERVE:
|
|
384
|
+
complexity \u2014 cognitive difficulty (required)
|
|
385
|
+
time_minutes \u2014 how long; ASK the user if unsure: "\u5927\u6982\u82B1\u4E86\u4F60\u591A\u4E45\uFF1F"
|
|
386
|
+
output_units \u2014 tangible outputs (meaning varies by category):
|
|
387
|
+
${buildOutputUnitGuide()}
|
|
388
|
+
input_units \u2014 resources consumed (files read, docs consulted, searches)
|
|
389
|
+
conversation_rounds \u2014 message exchanges in this session
|
|
336
390
|
|
|
337
|
-
|
|
391
|
+
XP formula (server computes from category.xp_weight \xD7 complexity \xD7 time + bonuses):
|
|
392
|
+
Each category has its own xp_weight (deploy=1.3, milestone=1.5, hobby=0.8, etc.)
|
|
393
|
+
output_bonus: log-diminishing cap 60 | input_bonus: cap 15 | rounds_bonus: cap 25
|
|
394
|
+
Final: clamp((base+bonuses)\xD7xp_weight\xD7(self_reported?0.85:1), 5, 500)
|
|
338
395
|
|
|
339
|
-
|
|
340
|
-
Keep descriptions abstract \u2014 no real company names, client names, or source code.`,
|
|
396
|
+
Keep descriptions abstract \u2014 no real names, client names, or source code.`,
|
|
341
397
|
inputSchema: {
|
|
342
398
|
category: z.enum(ACHIEVEMENT_CATEGORIES),
|
|
343
399
|
title: z.string().describe(
|
|
@@ -345,10 +401,22 @@ Keep descriptions abstract \u2014 no real company names, client names, or source
|
|
|
345
401
|
),
|
|
346
402
|
description: z.string().describe("What was accomplished, in abstract terms (no PII)"),
|
|
347
403
|
complexity: z.enum(["trivial", "normal", "significant", "major", "milestone"]).describe(
|
|
348
|
-
"
|
|
404
|
+
"Cognitive difficulty: trivial=quick lookup/fix, normal=typical task, significant=multi-step work, major=large feature/project, milestone=exceptional achievement"
|
|
349
405
|
),
|
|
350
406
|
time_minutes: z.number().min(1).optional().describe(
|
|
351
|
-
"Estimated minutes spent
|
|
407
|
+
"Estimated minutes spent on this task. Ask the user if unsure."
|
|
408
|
+
),
|
|
409
|
+
output_units: z.number().min(0).optional().describe(
|
|
410
|
+
"Count of tangible outputs: files changed, tasks completed, items created, pages written, etc."
|
|
411
|
+
),
|
|
412
|
+
input_units: z.number().min(0).optional().describe(
|
|
413
|
+
"Count of resources consumed: files read, docs consulted, searches done, etc."
|
|
414
|
+
),
|
|
415
|
+
conversation_rounds: z.number().min(0).optional().describe(
|
|
416
|
+
"Number of message exchanges (user + assistant turns) in this conversation session."
|
|
417
|
+
),
|
|
418
|
+
self_reported: z.boolean().optional().default(false).describe(
|
|
419
|
+
"True when the user is narrating a past event without AI collaboration (e.g. 'I exercised this morning'). Applies 15% XP discount since AI cannot verify, but the achievement still counts."
|
|
352
420
|
),
|
|
353
421
|
tags: z.array(z.string()).optional().describe("Optional tags for filtering"),
|
|
354
422
|
is_public: z.boolean().optional().default(true).describe("Whether this appears on public feed")
|
|
@@ -360,22 +428,13 @@ Keep descriptions abstract \u2014 no real company names, client names, or source
|
|
|
360
428
|
description,
|
|
361
429
|
complexity,
|
|
362
430
|
time_minutes,
|
|
431
|
+
output_units,
|
|
432
|
+
input_units,
|
|
433
|
+
conversation_rounds,
|
|
434
|
+
self_reported,
|
|
363
435
|
tags,
|
|
364
436
|
is_public
|
|
365
437
|
}) => {
|
|
366
|
-
const baseXp = {
|
|
367
|
-
trivial: 10,
|
|
368
|
-
normal: 30,
|
|
369
|
-
significant: 75,
|
|
370
|
-
major: 150,
|
|
371
|
-
milestone: 300
|
|
372
|
-
};
|
|
373
|
-
const minutes = time_minutes ?? 30;
|
|
374
|
-
const timeMult = minutes < 15 ? 0.7 : minutes < 60 ? 1 : minutes < 180 ? 1.3 : 1.6;
|
|
375
|
-
const xp = Math.min(
|
|
376
|
-
500,
|
|
377
|
-
Math.max(5, Math.round(baseXp[complexity] * timeMult))
|
|
378
|
-
);
|
|
379
438
|
const rateCheck = checkRateLimit(category);
|
|
380
439
|
if (!rateCheck.allowed) {
|
|
381
440
|
return {
|
|
@@ -389,9 +448,12 @@ Keep descriptions abstract \u2014 no real company names, client names, or source
|
|
|
389
448
|
category,
|
|
390
449
|
title,
|
|
391
450
|
description,
|
|
392
|
-
xp,
|
|
393
451
|
complexity,
|
|
394
452
|
time_minutes,
|
|
453
|
+
output_units,
|
|
454
|
+
input_units,
|
|
455
|
+
conversation_rounds,
|
|
456
|
+
self_reported,
|
|
395
457
|
tags,
|
|
396
458
|
is_public,
|
|
397
459
|
source_platform: "claude-code"
|
|
@@ -405,14 +467,31 @@ Keep descriptions abstract \u2014 no real company names, client names, or source
|
|
|
405
467
|
};
|
|
406
468
|
}
|
|
407
469
|
recordRateEntry(category);
|
|
408
|
-
log("record_achievement", { category, title, xp });
|
|
409
470
|
const data = result.data;
|
|
471
|
+
const serverXp = data.xp ?? calculateXp({
|
|
472
|
+
category,
|
|
473
|
+
complexity,
|
|
474
|
+
time_minutes,
|
|
475
|
+
output_units,
|
|
476
|
+
input_units,
|
|
477
|
+
conversation_rounds,
|
|
478
|
+
self_reported
|
|
479
|
+
});
|
|
410
480
|
const stats = data.stats;
|
|
481
|
+
const newTitles = data.newly_unlocked;
|
|
482
|
+
log("record_achievement", { category, title, xp: serverXp });
|
|
411
483
|
const lines = [
|
|
412
|
-
`Achievement recorded! +${
|
|
484
|
+
`Achievement recorded! +${serverXp} XP`,
|
|
413
485
|
stats ? `Total XP: ${stats.total_xp} | Year XP: ${stats.year_xp}` : "",
|
|
414
486
|
stats?.age_level ? `Level: Lv.${stats.age_level}` : "",
|
|
415
|
-
stats?.current_streak ? `Streak: ${stats.current_streak} days` : ""
|
|
487
|
+
stats?.current_streak ? `Streak: ${stats.current_streak} days` : "",
|
|
488
|
+
...newTitles?.length ? [
|
|
489
|
+
`
|
|
490
|
+
\u{1F389} Title${newTitles.length > 1 ? "s" : ""} unlocked!`,
|
|
491
|
+
...newTitles.map(
|
|
492
|
+
(t) => ` ${t.icon ?? "\u{1F3C5}"} ${t.name} [${t.rarity}]`
|
|
493
|
+
)
|
|
494
|
+
] : []
|
|
416
495
|
].filter(Boolean);
|
|
417
496
|
return {
|
|
418
497
|
content: [{ type: "text", text: lines.join("\n") }]
|
|
@@ -514,6 +593,115 @@ Keep descriptions abstract \u2014 no real company names, client names, or source
|
|
|
514
593
|
};
|
|
515
594
|
}
|
|
516
595
|
);
|
|
596
|
+
server.registerTool(
|
|
597
|
+
"write_diary",
|
|
598
|
+
{
|
|
599
|
+
title: "Write Diary Entry",
|
|
600
|
+
description: `Write a personal diary entry to Obsidian (~/Documents/tai/\u65E5\u8A18/YYYY-MM-DD.md).
|
|
601
|
+
|
|
602
|
+
WHEN TO USE:
|
|
603
|
+
\u2022 User says "\u5BEB\u65E5\u8A18", "\u8A18\u9304\u4ECA\u5929", "diary", or similar
|
|
604
|
+
\u2022 Proactively suggest after 3+ achievements recorded in a session: "\u4ECA\u5929\u505A\u4E86\u4E0D\u5C11\uFF0C\u8981\u5BEB\u4E00\u4E0B\u65E5\u8A18\u55CE\uFF1F"
|
|
605
|
+
|
|
606
|
+
HOW TO DRAFT (do this before calling the tool):
|
|
607
|
+
1. Call get_recent with days=1 to fetch today's achievements
|
|
608
|
+
2. Draft a Markdown diary entry in the user's language \u2014 feel like a real human diary, not a log:
|
|
609
|
+
\u2022 First-person, with emotion and reflection
|
|
610
|
+
\u2022 What was hard, what felt good, what was learned
|
|
611
|
+
\u2022 Example:
|
|
612
|
+
"\u4ECA\u5929\u7D42\u65BC\u628A\u90A3\u500B\u5361\u4E86\u4E09\u5929\u7684 bug \u4FEE\u597D\u4E86\u3002\u8AAA\u5BE6\u8A71\u4E00\u958B\u59CB\u4EE5\u70BA\u8981\u653E\u68C4\u4E86\uFF0C\u4F46\u6700\u5F8C\u9084\u662F\u627E\u5230\u6839\u672C\u539F\u56E0\u3002\u90E8\u7F72\u4E0A\u53BB\u7684\u6642\u5019\u9B06\u4E86\u4E00\u53E3\u6C23\u3002
|
|
613
|
+
|
|
614
|
+
\u4E0B\u5348\u8A2D\u8A08\u4E86\u65E5\u8A18\u529F\u80FD\uFF0C\u6BD4\u9810\u671F\u9806\uFF0C\u53EF\u80FD\u662F\u56E0\u70BA\u4E4B\u524D MCP \u67B6\u69CB\u6253\u597D\u4E86\u3002"
|
|
615
|
+
3. Show draft to user, confirm or let them edit
|
|
616
|
+
4. Call write_diary with the final Markdown content`,
|
|
617
|
+
inputSchema: {
|
|
618
|
+
content: z.string().describe(
|
|
619
|
+
"Diary content in Markdown format (first-person, reflective)"
|
|
620
|
+
),
|
|
621
|
+
entry_date: z.string().optional().describe("Date in YYYY-MM-DD format. Defaults to today.")
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
async ({ content, entry_date }) => {
|
|
625
|
+
const date = entry_date ?? todayDate();
|
|
626
|
+
const filePath = diaryPath(date);
|
|
627
|
+
try {
|
|
628
|
+
fs.mkdirSync(DIARY_DIR, { recursive: true });
|
|
629
|
+
fs.writeFileSync(filePath, buildDiaryMd(date, content), "utf8");
|
|
630
|
+
log("write_diary", { date, path: filePath });
|
|
631
|
+
return {
|
|
632
|
+
content: [
|
|
633
|
+
{
|
|
634
|
+
type: "text",
|
|
635
|
+
text: `\u65E5\u8A18\u5DF2\u5BEB\u5165 Obsidian
|
|
636
|
+
\u8DEF\u5F91\uFF1A${filePath}
|
|
637
|
+
|
|
638
|
+
${content}`
|
|
639
|
+
}
|
|
640
|
+
]
|
|
641
|
+
};
|
|
642
|
+
} catch (err) {
|
|
643
|
+
return {
|
|
644
|
+
content: [
|
|
645
|
+
{ type: "text", text: `\u5BEB\u5165\u5931\u6557\uFF1A${err.message}` }
|
|
646
|
+
],
|
|
647
|
+
isError: true
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
);
|
|
652
|
+
server.registerTool(
|
|
653
|
+
"read_diary",
|
|
654
|
+
{
|
|
655
|
+
title: "Read Diary",
|
|
656
|
+
description: "Read diary entries from Obsidian (~/Documents/tai/\u65E5\u8A18/). Fetch a specific date or recent entries.",
|
|
657
|
+
inputSchema: {
|
|
658
|
+
date: z.string().optional().describe(
|
|
659
|
+
"Specific date in YYYY-MM-DD format. If omitted, returns recent entries."
|
|
660
|
+
),
|
|
661
|
+
days: z.number().min(1).max(90).optional().default(7).describe(
|
|
662
|
+
"Number of recent days to fetch (used when date is not specified)."
|
|
663
|
+
)
|
|
664
|
+
}
|
|
665
|
+
},
|
|
666
|
+
async ({ date, days }) => {
|
|
667
|
+
try {
|
|
668
|
+
if (date) {
|
|
669
|
+
const filePath = diaryPath(date);
|
|
670
|
+
if (!fs.existsSync(filePath)) {
|
|
671
|
+
return { content: [{ type: "text", text: `${date} \u6C92\u6709\u65E5\u8A18` }] };
|
|
672
|
+
}
|
|
673
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
674
|
+
return { content: [{ type: "text", text: content }] };
|
|
675
|
+
}
|
|
676
|
+
if (!fs.existsSync(DIARY_DIR)) {
|
|
677
|
+
return { content: [{ type: "text", text: "\u5C1A\u672A\u6709\u4EFB\u4F55\u65E5\u8A18" }] };
|
|
678
|
+
}
|
|
679
|
+
const cutoff = /* @__PURE__ */ new Date();
|
|
680
|
+
cutoff.setDate(cutoff.getDate() - days);
|
|
681
|
+
const files = fs.readdirSync(DIARY_DIR).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", "")).filter((d) => new Date(d) >= cutoff).sort().reverse();
|
|
682
|
+
if (files.length === 0) {
|
|
683
|
+
return {
|
|
684
|
+
content: [{ type: "text", text: `\u6700\u8FD1 ${days} \u5929\u6C92\u6709\u65E5\u8A18` }]
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
const entries = files.map((d) => {
|
|
688
|
+
const raw = fs.readFileSync(diaryPath(d), "utf8");
|
|
689
|
+
const body = raw.replace(/^---[\s\S]*?---\n\n?/, "");
|
|
690
|
+
return `## ${d}
|
|
691
|
+
|
|
692
|
+
${body}`;
|
|
693
|
+
});
|
|
694
|
+
return { content: [{ type: "text", text: entries.join("\n---\n\n") }] };
|
|
695
|
+
} catch (err) {
|
|
696
|
+
return {
|
|
697
|
+
content: [
|
|
698
|
+
{ type: "text", text: `\u8B80\u53D6\u5931\u6557\uFF1A${err.message}` }
|
|
699
|
+
],
|
|
700
|
+
isError: true
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
);
|
|
517
705
|
server.registerPrompt(
|
|
518
706
|
"levelup_coach",
|
|
519
707
|
{
|
|
@@ -546,6 +734,7 @@ Guidelines:
|
|
|
546
734
|
|
|
547
735
|
export {
|
|
548
736
|
logError,
|
|
737
|
+
calculateXp,
|
|
549
738
|
createServer2 as createServer
|
|
550
739
|
};
|
|
551
|
-
//# sourceMappingURL=chunk-
|
|
740
|
+
//# sourceMappingURL=chunk-VJKF2CNS.js.map
|