@mengzf/skillflow-mcp 0.0.0-stage → 1.0.1

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 (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +279 -2
  3. package/dist/adapters/claude-code.adapter.d.ts +10 -0
  4. package/dist/adapters/claude-code.adapter.d.ts.map +1 -0
  5. package/dist/adapters/claude-code.adapter.js +166 -0
  6. package/dist/adapters/claude-code.adapter.js.map +1 -0
  7. package/dist/adapters/codex.adapter.d.ts +10 -0
  8. package/dist/adapters/codex.adapter.d.ts.map +1 -0
  9. package/dist/adapters/codex.adapter.js +109 -0
  10. package/dist/adapters/codex.adapter.js.map +1 -0
  11. package/dist/adapters/dsh.adapter.d.ts +7 -0
  12. package/dist/adapters/dsh.adapter.d.ts.map +1 -0
  13. package/dist/adapters/dsh.adapter.js +60 -0
  14. package/dist/adapters/dsh.adapter.js.map +1 -0
  15. package/dist/adapters/qoder.adapter.d.ts +10 -0
  16. package/dist/adapters/qoder.adapter.d.ts.map +1 -0
  17. package/dist/adapters/qoder.adapter.js +119 -0
  18. package/dist/adapters/qoder.adapter.js.map +1 -0
  19. package/dist/adapters/trae.adapter.d.ts +8 -0
  20. package/dist/adapters/trae.adapter.d.ts.map +1 -0
  21. package/dist/adapters/trae.adapter.js +104 -0
  22. package/dist/adapters/trae.adapter.js.map +1 -0
  23. package/dist/adapters/types.d.ts +25 -0
  24. package/dist/adapters/types.d.ts.map +1 -0
  25. package/dist/adapters/types.js +2 -0
  26. package/dist/adapters/types.js.map +1 -0
  27. package/dist/adapters/universal.sync.d.ts +22 -0
  28. package/dist/adapters/universal.sync.d.ts.map +1 -0
  29. package/dist/adapters/universal.sync.js +140 -0
  30. package/dist/adapters/universal.sync.js.map +1 -0
  31. package/dist/adapters/workbuddy.sniffer.d.ts +22 -0
  32. package/dist/adapters/workbuddy.sniffer.d.ts.map +1 -0
  33. package/dist/adapters/workbuddy.sniffer.js +148 -0
  34. package/dist/adapters/workbuddy.sniffer.js.map +1 -0
  35. package/dist/config.d.ts +9 -0
  36. package/dist/config.d.ts.map +1 -0
  37. package/dist/config.js +33 -0
  38. package/dist/config.js.map +1 -0
  39. package/dist/db/index.d.ts +4 -0
  40. package/dist/db/index.d.ts.map +1 -0
  41. package/dist/db/index.js +39 -0
  42. package/dist/db/index.js.map +1 -0
  43. package/dist/db/invocation-filter.d.ts +29 -0
  44. package/dist/db/invocation-filter.d.ts.map +1 -0
  45. package/dist/db/invocation-filter.js +78 -0
  46. package/dist/db/invocation-filter.js.map +1 -0
  47. package/dist/db/schema.d.ts +3 -0
  48. package/dist/db/schema.d.ts.map +1 -0
  49. package/dist/db/schema.js +129 -0
  50. package/dist/db/schema.js.map +1 -0
  51. package/dist/db/seed.d.ts +15 -0
  52. package/dist/db/seed.d.ts.map +1 -0
  53. package/dist/db/seed.js +128 -0
  54. package/dist/db/seed.js.map +1 -0
  55. package/dist/index.d.ts +3 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +51 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/server.d.ts +7 -0
  60. package/dist/server.d.ts.map +1 -0
  61. package/dist/server.js +372 -0
  62. package/dist/server.js.map +1 -0
  63. package/dist/services/benchmark.service.d.ts +27 -0
  64. package/dist/services/benchmark.service.d.ts.map +1 -0
  65. package/dist/services/benchmark.service.js +94 -0
  66. package/dist/services/benchmark.service.js.map +1 -0
  67. package/dist/services/dashboard.service.d.ts +14 -0
  68. package/dist/services/dashboard.service.d.ts.map +1 -0
  69. package/dist/services/dashboard.service.js +296 -0
  70. package/dist/services/dashboard.service.js.map +1 -0
  71. package/dist/services/filter.service.d.ts +26 -0
  72. package/dist/services/filter.service.d.ts.map +1 -0
  73. package/dist/services/filter.service.js +103 -0
  74. package/dist/services/filter.service.js.map +1 -0
  75. package/dist/services/impact.service.d.ts +20 -0
  76. package/dist/services/impact.service.d.ts.map +1 -0
  77. package/dist/services/impact.service.js +114 -0
  78. package/dist/services/impact.service.js.map +1 -0
  79. package/dist/services/telemetry.service.d.ts +41 -0
  80. package/dist/services/telemetry.service.d.ts.map +1 -0
  81. package/dist/services/telemetry.service.js +244 -0
  82. package/dist/services/telemetry.service.js.map +1 -0
  83. package/dist/types/index.d.ts +3 -0
  84. package/dist/types/index.d.ts.map +1 -0
  85. package/dist/types/index.js +3 -0
  86. package/dist/types/index.js.map +1 -0
  87. package/dist/types/models.d.ts +73 -0
  88. package/dist/types/models.d.ts.map +1 -0
  89. package/dist/types/models.js +2 -0
  90. package/dist/types/models.js.map +1 -0
  91. package/dist/types/tools.d.ts +173 -0
  92. package/dist/types/tools.d.ts.map +1 -0
  93. package/dist/types/tools.js +195 -0
  94. package/dist/types/tools.js.map +1 -0
  95. package/dist/utils/ascii-chart.d.ts +9 -0
  96. package/dist/utils/ascii-chart.d.ts.map +1 -0
  97. package/dist/utils/ascii-chart.js +29 -0
  98. package/dist/utils/ascii-chart.js.map +1 -0
  99. package/dist/utils/formatter.d.ts +6 -0
  100. package/dist/utils/formatter.d.ts.map +1 -0
  101. package/dist/utils/formatter.js +29 -0
  102. package/dist/utils/formatter.js.map +1 -0
  103. package/dist/utils/governance.d.ts +25 -0
  104. package/dist/utils/governance.d.ts.map +1 -0
  105. package/dist/utils/governance.js +70 -0
  106. package/dist/utils/governance.js.map +1 -0
  107. package/dist/utils/hours.d.ts +3 -0
  108. package/dist/utils/hours.d.ts.map +1 -0
  109. package/dist/utils/hours.js +15 -0
  110. package/dist/utils/hours.js.map +1 -0
  111. package/dist/utils/pricing.d.ts +19 -0
  112. package/dist/utils/pricing.d.ts.map +1 -0
  113. package/dist/utils/pricing.js +96 -0
  114. package/dist/utils/pricing.js.map +1 -0
  115. package/dist/utils/quality.d.ts +15 -0
  116. package/dist/utils/quality.d.ts.map +1 -0
  117. package/dist/utils/quality.js +47 -0
  118. package/dist/utils/quality.js.map +1 -0
  119. package/dist/web/render.d.ts +2 -0
  120. package/dist/web/render.d.ts.map +1 -0
  121. package/dist/web/render.js +1919 -0
  122. package/dist/web/render.js.map +1 -0
  123. package/dist/web/server.d.ts +10 -0
  124. package/dist/web/server.d.ts.map +1 -0
  125. package/dist/web/server.js +160 -0
  126. package/dist/web/server.js.map +1 -0
  127. package/package.json +51 -4
@@ -0,0 +1,148 @@
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 (idempotent per session, skill, round)
94
+ const alreadySynced = this.skillflowDb
95
+ .prepare('SELECT id FROM invocations WHERE session_id = ? AND skill_id = ? AND round_index = ?')
96
+ .get(sess.id, skillId, 1);
97
+ if (!alreadySynced) {
98
+ const actualTokens = sess.tokens_used || 0;
99
+ const isExplicit = Boolean(sess.title && (sess.title.includes(skillName) || sess.title.includes(skillId)));
100
+ const triggerMode = isExplicit ? 'explicit' : 'implicit';
101
+ this.telemetryService.recordHit({
102
+ session_id: sess.id,
103
+ skill_id: skillId,
104
+ agent_name: 'WorkBuddy',
105
+ model_name: sess.model || 'deepseek-v4.1-flash',
106
+ round_index: 1,
107
+ trigger_mode: triggerMode,
108
+ status: 'success',
109
+ actual_tokens: actualTokens,
110
+ context_tokens: Math.max(0, actualTokens - 500),
111
+ output_tokens: Math.min(actualTokens, 500),
112
+ actual_steps: 1,
113
+ metadata: {
114
+ title: sess.title,
115
+ model: sess.model,
116
+ created_at: sess.created_at,
117
+ source: 'workbuddy_auto_sniffer',
118
+ },
119
+ });
120
+ // Ensure skill name in manifest is human-friendly
121
+ this.skillflowDb
122
+ .prepare('UPDATE skills_manifest SET skill_name = ? WHERE skill_id = ?')
123
+ .run(skillName, skillId);
124
+ syncedCount++;
125
+ syncedSkills.push(`${skillName} (${sess.title})`);
126
+ }
127
+ }
128
+ }
129
+ }
130
+ catch { }
131
+ }
132
+ }
133
+ catch (err) {
134
+ process.stderr.write(`[SkillFlow Sniffer] Error syncing WorkBuddy: ${err.message}\n`);
135
+ }
136
+ finally {
137
+ if (wbDb) {
138
+ try {
139
+ wbDb.close();
140
+ }
141
+ catch { }
142
+ }
143
+ }
144
+ }
145
+ return { syncedCount, syncedSkills };
146
+ }
147
+ }
148
+ //# 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,29 @@
1
+ import { InvocationStatus } from '../types/models.js';
2
+ export type TimeRange = 'current_session' | 'today' | '7days' | '30days' | 'all';
3
+ export interface InvocationFilter {
4
+ timeRange?: string | null;
5
+ sessionId?: string | null;
6
+ agentName?: string | null;
7
+ skillId?: string | null;
8
+ modelName?: string | null;
9
+ status?: string | null;
10
+ }
11
+ export declare const INVOCATION_STATUSES: InvocationStatus[];
12
+ /**
13
+ * Normalizes an arbitrary query-string value into a supported time range.
14
+ */
15
+ export declare function normalizeTimeRange(value: string | null | undefined): TimeRange;
16
+ export declare function isInvocationStatus(value: string | null | undefined): value is InvocationStatus;
17
+ /**
18
+ * Builds a shared WHERE clause for `invocations` so the dashboard aggregation and the
19
+ * invocation feed always interpret the same filter set identically.
20
+ */
21
+ export declare function buildInvocationWhere(filter?: InvocationFilter, alias?: string): {
22
+ whereSql: string;
23
+ params: (string | number)[];
24
+ };
25
+ /**
26
+ * Reads the shared filter set from a request query string.
27
+ */
28
+ export declare function readInvocationFilter(searchParams: URLSearchParams): InvocationFilter;
29
+ //# sourceMappingURL=invocation-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invocation-filter.d.ts","sourceRoot":"","sources":["../../src/db/invocation-filter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,MAAM,MAAM,SAAS,GAAG,iBAAiB,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEjF,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,eAAO,MAAM,mBAAmB,EAAE,gBAAgB,EAAgD,CAAC;AAEnG;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,SAAS,CAK9E;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,IAAI,gBAAgB,CAK9F;AASD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,GAAE,gBAAqB,EAC7B,KAAK,SAAM,GACV;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAA;CAAE,CAyCnD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,eAAe,GAAG,gBAAgB,CAQpF"}
@@ -0,0 +1,78 @@
1
+ export const INVOCATION_STATUSES = ['success', 'partial', 'failed', 'skipped'];
2
+ /**
3
+ * Normalizes an arbitrary query-string value into a supported time range.
4
+ */
5
+ export function normalizeTimeRange(value) {
6
+ if (value === 'current_session' || value === 'today' || value === '7days' || value === '30days') {
7
+ return value;
8
+ }
9
+ return 'all';
10
+ }
11
+ export function isInvocationStatus(value) {
12
+ if (!value) {
13
+ return false;
14
+ }
15
+ return INVOCATION_STATUSES.includes(value);
16
+ }
17
+ function readText(value) {
18
+ if (!value) {
19
+ return '';
20
+ }
21
+ return value.trim();
22
+ }
23
+ /**
24
+ * Builds a shared WHERE clause for `invocations` so the dashboard aggregation and the
25
+ * invocation feed always interpret the same filter set identically.
26
+ */
27
+ export function buildInvocationWhere(filter = {}, alias = 'i') {
28
+ if (!/^[a-zA-Z_]\w*$/.test(alias)) {
29
+ throw new Error(`Invalid SQL alias: ${alias}`);
30
+ }
31
+ const clauses = ['1=1'];
32
+ const params = [];
33
+ const timeRange = normalizeTimeRange(filter.timeRange);
34
+ if (timeRange === 'current_session') {
35
+ clauses.push(`${alias}.session_id = ?`);
36
+ params.push(readText(filter.sessionId) || 'unknown');
37
+ }
38
+ else if (timeRange === 'today') {
39
+ clauses.push(`date(${alias}.timestamp) = date('now')`);
40
+ }
41
+ else if (timeRange === '7days') {
42
+ clauses.push(`${alias}.timestamp >= datetime('now', '-7 days')`);
43
+ }
44
+ else if (timeRange === '30days') {
45
+ clauses.push(`${alias}.timestamp >= datetime('now', '-30 days')`);
46
+ }
47
+ const exactMatches = [
48
+ [`${alias}.agent_name`, readText(filter.agentName)],
49
+ [`${alias}.skill_id`, readText(filter.skillId)],
50
+ [`${alias}.model_name`, readText(filter.modelName)],
51
+ ];
52
+ for (const [column, value] of exactMatches) {
53
+ if (!value) {
54
+ continue;
55
+ }
56
+ clauses.push(`${column} = ?`);
57
+ params.push(value);
58
+ }
59
+ const status = readText(filter.status);
60
+ if (isInvocationStatus(status)) {
61
+ clauses.push(`${alias}.status = ?`);
62
+ params.push(status);
63
+ }
64
+ return { whereSql: clauses.join(' AND '), params };
65
+ }
66
+ /**
67
+ * Reads the shared filter set from a request query string.
68
+ */
69
+ export function readInvocationFilter(searchParams) {
70
+ return {
71
+ timeRange: searchParams.get('time_range'),
72
+ agentName: searchParams.get('agent_name'),
73
+ skillId: searchParams.get('skill_id'),
74
+ modelName: searchParams.get('model_name'),
75
+ status: searchParams.get('status'),
76
+ };
77
+ }
78
+ //# sourceMappingURL=invocation-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invocation-filter.js","sourceRoot":"","sources":["../../src/db/invocation-filter.ts"],"names":[],"mappings":"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"}
@@ -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;AAgB3C,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,YAAY,EAAE,UAAU,UAAQ,GAAG,IAAI,CAsH3E"}
@@ -0,0 +1,129 @@
1
+ function tryAddColumn(db, table, colDef) {
2
+ if (!/^[a-zA-Z_]\w*$/.test(table)) {
3
+ throw new Error(`Invalid table name: ${table}`);
4
+ }
5
+ if (!/^[a-zA-Z_]\w*\s+[a-zA-Z_]/.test(colDef.trim())) {
6
+ throw new Error(`Invalid column definition: ${colDef}`);
7
+ }
8
+ try {
9
+ db.exec(`ALTER TABLE ${table} ADD COLUMN ${colDef};`);
10
+ }
11
+ catch {
12
+ // Column already exists
13
+ }
14
+ }
15
+ export function initializeSchema(db, isInMemory = false) {
16
+ db.exec('PRAGMA foreign_keys = ON;');
17
+ db.exec('PRAGMA busy_timeout = 5000;');
18
+ if (!isInMemory) {
19
+ db.exec('PRAGMA journal_mode = WAL;');
20
+ db.exec('PRAGMA synchronous = NORMAL;');
21
+ }
22
+ // 1. Skills Manifest table
23
+ db.exec(`
24
+ CREATE TABLE IF NOT EXISTS skills_manifest (
25
+ skill_id TEXT PRIMARY KEY,
26
+ skill_name TEXT NOT NULL,
27
+ category TEXT NOT NULL DEFAULT 'general',
28
+ version TEXT NOT NULL DEFAULT '1.0.0',
29
+ description TEXT,
30
+ baseline_steps INTEGER NOT NULL DEFAULT 5,
31
+ baseline_tokens INTEGER NOT NULL DEFAULT 3000,
32
+ baseline_manual_minutes INTEGER NOT NULL DEFAULT 15,
33
+ default_saved_steps INTEGER NOT NULL DEFAULT 4,
34
+ default_saved_tokens INTEGER NOT NULL DEFAULT 2000,
35
+ health_score INTEGER NOT NULL DEFAULT 100,
36
+ tags TEXT DEFAULT '[]',
37
+ last_invoked_at TEXT DEFAULT NULL,
38
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
39
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
40
+ );
41
+ `);
42
+ tryAddColumn(db, 'skills_manifest', 'baseline_manual_minutes INTEGER NOT NULL DEFAULT 15');
43
+ tryAddColumn(db, 'skills_manifest', "version TEXT NOT NULL DEFAULT '1.0.0'");
44
+ tryAddColumn(db, 'skills_manifest', 'health_score INTEGER NOT NULL DEFAULT 100');
45
+ tryAddColumn(db, 'skills_manifest', 'last_invoked_at TEXT DEFAULT NULL');
46
+ // 2. Sessions table
47
+ db.exec(`
48
+ CREATE TABLE IF NOT EXISTS sessions (
49
+ session_id TEXT PRIMARY KEY,
50
+ agent_name TEXT NOT NULL DEFAULT 'unknown',
51
+ title TEXT,
52
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
53
+ last_active_at TEXT NOT NULL DEFAULT (datetime('now')),
54
+ round_count INTEGER NOT NULL DEFAULT 1,
55
+ total_invocations INTEGER NOT NULL DEFAULT 0,
56
+ total_saved_steps INTEGER NOT NULL DEFAULT 0,
57
+ total_saved_tokens INTEGER NOT NULL DEFAULT 0,
58
+ total_saved_hours REAL NOT NULL DEFAULT 0.0,
59
+ total_cost_saved_usd REAL NOT NULL DEFAULT 0.0,
60
+ total_cost_saved_cny REAL NOT NULL DEFAULT 0.0,
61
+ total_corrections INTEGER NOT NULL DEFAULT 0,
62
+ metadata TEXT DEFAULT '{}'
63
+ );
64
+ `);
65
+ tryAddColumn(db, 'sessions', 'total_saved_hours REAL NOT NULL DEFAULT 0.0');
66
+ tryAddColumn(db, 'sessions', 'total_cost_saved_usd REAL NOT NULL DEFAULT 0.0');
67
+ tryAddColumn(db, 'sessions', 'total_cost_saved_cny REAL NOT NULL DEFAULT 0.0');
68
+ tryAddColumn(db, 'sessions', 'total_corrections INTEGER NOT NULL DEFAULT 0');
69
+ // 3. Invocations table
70
+ db.exec(`
71
+ CREATE TABLE IF NOT EXISTS invocations (
72
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
73
+ session_id TEXT NOT NULL,
74
+ agent_name TEXT NOT NULL DEFAULT 'unknown',
75
+ model_name TEXT NOT NULL DEFAULT 'unknown',
76
+ skill_id TEXT NOT NULL,
77
+ skill_version TEXT NOT NULL DEFAULT '1.0.0',
78
+ round_index INTEGER NOT NULL DEFAULT 1,
79
+ trigger_mode TEXT NOT NULL DEFAULT 'explicit',
80
+ status TEXT NOT NULL CHECK(status IN ('success', 'failed', 'partial', 'skipped')),
81
+ actual_tokens INTEGER NOT NULL DEFAULT 0,
82
+ context_tokens INTEGER NOT NULL DEFAULT 0,
83
+ output_tokens INTEGER NOT NULL DEFAULT 0,
84
+ actual_steps INTEGER NOT NULL DEFAULT 1,
85
+ saved_steps INTEGER NOT NULL DEFAULT 0,
86
+ saved_tokens INTEGER NOT NULL DEFAULT 0,
87
+ net_tokens INTEGER NOT NULL DEFAULT 0,
88
+ saved_hours REAL NOT NULL DEFAULT 0.0,
89
+ cost_saved_usd REAL NOT NULL DEFAULT 0.0,
90
+ cost_saved_cny REAL NOT NULL DEFAULT 0.0,
91
+ tool_chain_depth INTEGER NOT NULL DEFAULT 0,
92
+ tool_failures INTEGER NOT NULL DEFAULT 0,
93
+ has_correction INTEGER NOT NULL DEFAULT 0,
94
+ follow_up_modifier TEXT DEFAULT NULL,
95
+ quality_profile TEXT NOT NULL DEFAULT 'efficient',
96
+ execution_time_ms INTEGER DEFAULT 0,
97
+ metadata TEXT DEFAULT '{}',
98
+ timestamp TEXT NOT NULL DEFAULT (datetime('now')),
99
+ FOREIGN KEY (skill_id) REFERENCES skills_manifest(skill_id) ON UPDATE CASCADE,
100
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
101
+ );
102
+ `);
103
+ tryAddColumn(db, 'invocations', "model_name TEXT NOT NULL DEFAULT 'unknown'");
104
+ tryAddColumn(db, 'invocations', "skill_version TEXT NOT NULL DEFAULT '1.0.0'");
105
+ tryAddColumn(db, 'invocations', "trigger_mode TEXT NOT NULL DEFAULT 'explicit'");
106
+ tryAddColumn(db, 'invocations', 'context_tokens INTEGER NOT NULL DEFAULT 0');
107
+ tryAddColumn(db, 'invocations', 'output_tokens INTEGER NOT NULL DEFAULT 0');
108
+ tryAddColumn(db, 'invocations', 'saved_hours REAL NOT NULL DEFAULT 0.0');
109
+ tryAddColumn(db, 'invocations', 'cost_saved_usd REAL NOT NULL DEFAULT 0.0');
110
+ tryAddColumn(db, 'invocations', 'cost_saved_cny REAL NOT NULL DEFAULT 0.0');
111
+ tryAddColumn(db, 'invocations', 'tool_chain_depth INTEGER NOT NULL DEFAULT 0');
112
+ tryAddColumn(db, 'invocations', 'tool_failures INTEGER NOT NULL DEFAULT 0');
113
+ tryAddColumn(db, 'invocations', 'net_tokens INTEGER NOT NULL DEFAULT 0');
114
+ tryAddColumn(db, 'invocations', 'has_correction INTEGER NOT NULL DEFAULT 0');
115
+ tryAddColumn(db, 'invocations', 'follow_up_modifier TEXT DEFAULT NULL');
116
+ tryAddColumn(db, 'invocations', "quality_profile TEXT NOT NULL DEFAULT 'efficient'");
117
+ // Indices for query performance
118
+ db.exec(`
119
+ CREATE INDEX IF NOT EXISTS idx_invocations_session_round ON invocations(session_id, round_index);
120
+ CREATE INDEX IF NOT EXISTS idx_invocations_session_skill ON invocations(session_id, skill_id);
121
+ CREATE INDEX IF NOT EXISTS idx_invocations_session_skill_round ON invocations(session_id, skill_id, round_index);
122
+ CREATE INDEX IF NOT EXISTS idx_invocations_agent ON invocations(agent_name);
123
+ CREATE INDEX IF NOT EXISTS idx_invocations_model ON invocations(model_name);
124
+ CREATE INDEX IF NOT EXISTS idx_invocations_skill ON invocations(skill_id);
125
+ CREATE INDEX IF NOT EXISTS idx_invocations_timestamp ON invocations(timestamp);
126
+ CREATE INDEX IF NOT EXISTS idx_sessions_last_active ON sessions(last_active_at);
127
+ `);
128
+ }
129
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ export interface DefaultSkillSeed {
3
+ skill_id: string;
4
+ skill_name: string;
5
+ category: string;
6
+ description: string;
7
+ baseline_steps: number;
8
+ baseline_tokens: number;
9
+ default_saved_steps: number;
10
+ default_saved_tokens: number;
11
+ tags: string[];
12
+ }
13
+ export declare const DEFAULT_SKILL_BENCHMARKS: DefaultSkillSeed[];
14
+ export declare function seedDefaultSkills(db: DatabaseSync): void;
15
+ //# sourceMappingURL=seed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../src/db/seed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,eAAO,MAAM,wBAAwB,EAAE,gBAAgB,EA+GtD,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA0BxD"}