specweave 1.0.259 → 1.0.260

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 (63) hide show
  1. package/CLAUDE.md +39 -25
  2. package/bin/specweave.js +11 -0
  3. package/dist/dashboard/assets/index-DdtF4K1G.css +1 -0
  4. package/dist/dashboard/assets/index-cZA6rz8s.js +11 -0
  5. package/dist/dashboard/index.html +14 -0
  6. package/dist/src/cli/commands/dashboard.d.ts +18 -0
  7. package/dist/src/cli/commands/dashboard.d.ts.map +1 -0
  8. package/dist/src/cli/commands/dashboard.js +142 -0
  9. package/dist/src/cli/commands/dashboard.js.map +1 -0
  10. package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts +9 -4
  11. package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts.map +1 -1
  12. package/dist/src/core/lazy-loading/llm-plugin-detector.js +9 -4
  13. package/dist/src/core/lazy-loading/llm-plugin-detector.js.map +1 -1
  14. package/dist/src/dashboard/server/command-runner.d.ts +21 -0
  15. package/dist/src/dashboard/server/command-runner.d.ts.map +1 -0
  16. package/dist/src/dashboard/server/command-runner.js +92 -0
  17. package/dist/src/dashboard/server/command-runner.js.map +1 -0
  18. package/dist/src/dashboard/server/dashboard-server.d.ts +33 -0
  19. package/dist/src/dashboard/server/dashboard-server.d.ts.map +1 -0
  20. package/dist/src/dashboard/server/dashboard-server.js +812 -0
  21. package/dist/src/dashboard/server/dashboard-server.js.map +1 -0
  22. package/dist/src/dashboard/server/data/activity-stream.d.ts +27 -0
  23. package/dist/src/dashboard/server/data/activity-stream.d.ts.map +1 -0
  24. package/dist/src/dashboard/server/data/activity-stream.js +142 -0
  25. package/dist/src/dashboard/server/data/activity-stream.js.map +1 -0
  26. package/dist/src/dashboard/server/data/claude-log-parser.d.ts +34 -0
  27. package/dist/src/dashboard/server/data/claude-log-parser.d.ts.map +1 -0
  28. package/dist/src/dashboard/server/data/claude-log-parser.js +218 -0
  29. package/dist/src/dashboard/server/data/claude-log-parser.js.map +1 -0
  30. package/dist/src/dashboard/server/data/dashboard-data-aggregator.d.ts +35 -0
  31. package/dist/src/dashboard/server/data/dashboard-data-aggregator.d.ts.map +1 -0
  32. package/dist/src/dashboard/server/data/dashboard-data-aggregator.js +219 -0
  33. package/dist/src/dashboard/server/data/dashboard-data-aggregator.js.map +1 -0
  34. package/dist/src/dashboard/server/data/plugin-scanner.d.ts +35 -0
  35. package/dist/src/dashboard/server/data/plugin-scanner.d.ts.map +1 -0
  36. package/dist/src/dashboard/server/data/plugin-scanner.js +96 -0
  37. package/dist/src/dashboard/server/data/plugin-scanner.js.map +1 -0
  38. package/dist/src/dashboard/server/data/sync-audit-reader.d.ts +38 -0
  39. package/dist/src/dashboard/server/data/sync-audit-reader.d.ts.map +1 -0
  40. package/dist/src/dashboard/server/data/sync-audit-reader.js +94 -0
  41. package/dist/src/dashboard/server/data/sync-audit-reader.js.map +1 -0
  42. package/dist/src/dashboard/server/file-watcher.d.ts +19 -0
  43. package/dist/src/dashboard/server/file-watcher.d.ts.map +1 -0
  44. package/dist/src/dashboard/server/file-watcher.js +104 -0
  45. package/dist/src/dashboard/server/file-watcher.js.map +1 -0
  46. package/dist/src/dashboard/server/router.d.ts +16 -0
  47. package/dist/src/dashboard/server/router.d.ts.map +1 -0
  48. package/dist/src/dashboard/server/router.js +110 -0
  49. package/dist/src/dashboard/server/router.js.map +1 -0
  50. package/dist/src/dashboard/server/sse-manager.d.ts +25 -0
  51. package/dist/src/dashboard/server/sse-manager.d.ts.map +1 -0
  52. package/dist/src/dashboard/server/sse-manager.js +75 -0
  53. package/dist/src/dashboard/server/sse-manager.js.map +1 -0
  54. package/dist/src/dashboard/types.d.ts +183 -0
  55. package/dist/src/dashboard/types.d.ts.map +1 -0
  56. package/dist/src/dashboard/types.js +2 -0
  57. package/dist/src/dashboard/types.js.map +1 -0
  58. package/package.json +12 -2
  59. package/plugins/specweave/hooks/user-prompt-submit.sh +79 -154
  60. package/plugins/specweave/skills/do/SKILL.md +31 -1
  61. package/plugins/specweave/skills/increment/SKILL.md +1 -1
  62. package/plugins/specweave/skills/increment-planner/SKILL.md +26 -0
  63. package/plugins/specweave/skills/increment-work-router/SKILL.md +37 -9
@@ -0,0 +1,219 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ export class DashboardDataAggregator {
4
+ constructor(projectRoot) {
5
+ this.projectRoot = projectRoot;
6
+ }
7
+ /** Get overview combining all sources */
8
+ async getOverview() {
9
+ const dashboard = this.readDashboardJson();
10
+ const syncMeta = this.readSyncMetadata();
11
+ const notifications = this.readNotifications();
12
+ const costs = this.readCosts();
13
+ const analytics = this.readAnalyticsCache();
14
+ const config = this.readConfig();
15
+ return {
16
+ project: {
17
+ name: config?.project?.name,
18
+ totalIncrements: dashboard?.summary?.total ?? 0,
19
+ activeIncrements: dashboard?.summary?.active ?? 0,
20
+ completedIncrements: dashboard?.summary?.completed ?? 0,
21
+ statusBreakdown: this.extractStatusBreakdown(dashboard?.summary),
22
+ typeBreakdown: dashboard?.summary?.byType ?? {},
23
+ priorityBreakdown: dashboard?.summary?.byPriority ?? {},
24
+ },
25
+ analytics: {
26
+ totalEvents: analytics?.totalEvents ?? 0,
27
+ successRate: analytics?.successRate ?? 0,
28
+ last24hEvents: this.count24hEvents(analytics),
29
+ },
30
+ costs: {
31
+ totalCost: costs?.totalCost ?? dashboard?.costs?.totalCost ?? 0,
32
+ totalSavings: costs?.totalSavings ?? dashboard?.costs?.totalSavings ?? 0,
33
+ totalTokens: costs?.totalTokens ?? dashboard?.costs?.totalTokens ?? 0,
34
+ sessionCount: Array.isArray(costs?.sessions) ? costs.sessions.length : 0,
35
+ },
36
+ notifications: {
37
+ pendingCount: Array.isArray(notifications)
38
+ ? notifications.filter((n) => !n.dismissedAt).length
39
+ : 0,
40
+ criticalCount: Array.isArray(notifications)
41
+ ? notifications.filter((n) => !n.dismissedAt && n.severity === 'critical').length
42
+ : 0,
43
+ },
44
+ sync: {
45
+ platforms: Object.fromEntries(Object.entries(syncMeta || {})
46
+ .filter(([key]) => ['github', 'jira', 'ado'].includes(key))
47
+ .map(([key, val]) => [key, {
48
+ lastImport: val?.lastImport ?? '',
49
+ lastSyncResult: val?.lastSyncResult ?? 'unknown',
50
+ }])),
51
+ },
52
+ generatedAt: new Date().toISOString(),
53
+ };
54
+ }
55
+ /** Get increments list */
56
+ async getIncrements() {
57
+ const dashboard = this.readDashboardJson();
58
+ const increments = [];
59
+ if (dashboard?.increments) {
60
+ for (const [id, data] of Object.entries(dashboard.increments)) {
61
+ increments.push({
62
+ id,
63
+ title: data.title || id,
64
+ status: data.status || 'unknown',
65
+ type: data.type || 'feature',
66
+ priority: data.priority || 'P2',
67
+ project: data.project,
68
+ tasks: data.tasks || { total: 0, completed: 0 },
69
+ acs: data.acs || { total: 0, completed: 0 },
70
+ createdAt: data.createdAt || '',
71
+ lastActivity: data.lastActivity || '',
72
+ });
73
+ }
74
+ }
75
+ // Sort: active first, then by last activity descending
76
+ increments.sort((a, b) => {
77
+ const statusOrder = { active: 0, planning: 1, paused: 2, ready_for_review: 3, completed: 4, abandoned: 5 };
78
+ const aOrder = statusOrder[a.status] ?? 3;
79
+ const bOrder = statusOrder[b.status] ?? 3;
80
+ if (aOrder !== bOrder)
81
+ return aOrder - bOrder;
82
+ return (b.lastActivity || '').localeCompare(a.lastActivity || '');
83
+ });
84
+ return {
85
+ increments,
86
+ summary: dashboard?.summary ?? {},
87
+ };
88
+ }
89
+ /** Get sync status */
90
+ async getSyncStatus() {
91
+ const meta = this.readSyncMetadata();
92
+ return {
93
+ platforms: meta || {},
94
+ lastUpdated: meta?.lastUpdated,
95
+ };
96
+ }
97
+ /** Get analytics summary (from cache if available) */
98
+ async getAnalyticsSummary() {
99
+ return this.readAnalyticsCache() || { totalEvents: 0, topCommands: [], topSkills: [], topAgents: [] };
100
+ }
101
+ /** Get per-skill usage stats */
102
+ async getSkillUsage() {
103
+ const cache = this.readAnalyticsCache();
104
+ if (!cache?.topSkills)
105
+ return [];
106
+ return cache.topSkills.map((s) => ({
107
+ name: s.name || '',
108
+ plugin: s.plugin || '',
109
+ count: s.count || 0,
110
+ successCount: s.successCount || 0,
111
+ failureCount: s.failureCount || 0,
112
+ avgDuration: s.avgDuration,
113
+ lastUsed: s.lastUsed || '',
114
+ }));
115
+ }
116
+ /** Get costs summary */
117
+ async getCostsSummary() {
118
+ const costs = this.readCosts();
119
+ return costs || { totalCost: 0, totalTokens: 0, totalSavings: 0, sessions: [] };
120
+ }
121
+ /** Get notifications */
122
+ async getNotifications() {
123
+ return this.readNotifications() || [];
124
+ }
125
+ /** Get config */
126
+ async getConfig() {
127
+ return this.readConfig() || {};
128
+ }
129
+ /** Get LSP status */
130
+ async getLspStatus() {
131
+ return this.readJsonFile('.specweave/state/lsp-check.json');
132
+ }
133
+ /** Get installed plugins */
134
+ async getPlugins() {
135
+ const cacheDir = path.join(process.env.HOME || '', '.claude/plugins/cache/specweave');
136
+ if (!fs.existsSync(cacheDir))
137
+ return [];
138
+ const plugins = [];
139
+ try {
140
+ const entries = fs.readdirSync(cacheDir, { withFileTypes: true });
141
+ for (const entry of entries) {
142
+ if (!entry.isDirectory())
143
+ continue;
144
+ const pluginDir = path.join(cacheDir, entry.name);
145
+ // Find version directory
146
+ const versions = fs.readdirSync(pluginDir, { withFileTypes: true })
147
+ .filter(v => v.isDirectory())
148
+ .map(v => v.name);
149
+ const version = versions[0] || '0.0.0';
150
+ const versionDir = path.join(pluginDir, version);
151
+ let skillCount = 0;
152
+ let commandCount = 0;
153
+ const skillsDir = path.join(versionDir, 'skills');
154
+ const commandsDir = path.join(versionDir, 'commands');
155
+ if (fs.existsSync(skillsDir)) {
156
+ skillCount = fs.readdirSync(skillsDir, { withFileTypes: true })
157
+ .filter(d => d.isDirectory()).length;
158
+ }
159
+ if (fs.existsSync(commandsDir)) {
160
+ commandCount = fs.readdirSync(commandsDir)
161
+ .filter(f => f.endsWith('.md')).length;
162
+ }
163
+ plugins.push({ name: entry.name, version, skillCount, commandCount });
164
+ }
165
+ }
166
+ catch { /* ignore read errors */ }
167
+ return plugins;
168
+ }
169
+ // --- Private helpers ---
170
+ readDashboardJson() {
171
+ return this.readJsonFile('.specweave/state/dashboard.json');
172
+ }
173
+ readSyncMetadata() {
174
+ return this.readJsonFile('.specweave/sync-metadata.json');
175
+ }
176
+ readNotifications() {
177
+ const data = this.readJsonFile('.specweave/state/notifications.json');
178
+ return data?.notifications || data || [];
179
+ }
180
+ readCosts() {
181
+ return this.readJsonFile('.specweave/logs/costs.json');
182
+ }
183
+ readAnalyticsCache() {
184
+ return this.readJsonFile('.specweave/state/analytics/cache.json');
185
+ }
186
+ readConfig() {
187
+ return this.readJsonFile('.specweave/config.json');
188
+ }
189
+ readJsonFile(relativePath) {
190
+ const fullPath = path.join(this.projectRoot, relativePath);
191
+ try {
192
+ if (!fs.existsSync(fullPath))
193
+ return null;
194
+ const content = fs.readFileSync(fullPath, 'utf-8');
195
+ return JSON.parse(content);
196
+ }
197
+ catch {
198
+ return null;
199
+ }
200
+ }
201
+ extractStatusBreakdown(summary) {
202
+ if (!summary)
203
+ return {};
204
+ const result = {};
205
+ for (const key of ['active', 'completed', 'paused', 'planning', 'backlog', 'ready_for_review', 'abandoned']) {
206
+ if (summary[key] != null)
207
+ result[key] = summary[key];
208
+ }
209
+ return result;
210
+ }
211
+ count24hEvents(analytics) {
212
+ if (!analytics?.dailySummaries?.length)
213
+ return 0;
214
+ const today = new Date().toISOString().slice(0, 10);
215
+ const todaySummary = analytics.dailySummaries.find((d) => d.date === today);
216
+ return todaySummary?.totalEvents ?? 0;
217
+ }
218
+ }
219
+ //# sourceMappingURL=dashboard-data-aggregator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-data-aggregator.js","sourceRoot":"","sources":["../../../../../src/dashboard/server/data/dashboard-data-aggregator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAY7B,MAAM,OAAO,uBAAuB;IAClC,YAAoB,WAAmB;QAAnB,gBAAW,GAAX,WAAW,CAAQ;IAAG,CAAC;IAE3C,yCAAyC;IACzC,KAAK,CAAC,WAAW;QACf,MAAM,SAAS,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACzC,MAAM,aAAa,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QAEjC,OAAO;YACL,OAAO,EAAE;gBACP,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;gBAC3B,eAAe,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;gBAC/C,gBAAgB,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;gBACjD,mBAAmB,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,IAAI,CAAC;gBACvD,eAAe,EAAE,IAAI,CAAC,sBAAsB,CAAC,SAAS,EAAE,OAAO,CAAC;gBAChE,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,IAAI,EAAE;gBAC/C,iBAAiB,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,IAAI,EAAE;aACxD;YACD,SAAS,EAAE;gBACT,WAAW,EAAE,SAAS,EAAE,WAAW,IAAI,CAAC;gBACxC,WAAW,EAAE,SAAS,EAAE,WAAW,IAAI,CAAC;gBACxC,aAAa,EAAE,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC;aAC9C;YACD,KAAK,EAAE;gBACL,SAAS,EAAE,KAAK,EAAE,SAAS,IAAI,SAAS,EAAE,KAAK,EAAE,SAAS,IAAI,CAAC;gBAC/D,YAAY,EAAE,KAAK,EAAE,YAAY,IAAI,SAAS,EAAE,KAAK,EAAE,YAAY,IAAI,CAAC;gBACxE,WAAW,EAAE,KAAK,EAAE,WAAW,IAAI,SAAS,EAAE,KAAK,EAAE,WAAW,IAAI,CAAC;gBACrE,YAAY,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;aACzE;YACD,aAAa,EAAE;gBACb,YAAY,EAAE,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC;oBACxC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,MAAM;oBACzD,CAAC,CAAC,CAAC;gBACL,aAAa,EAAE,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC;oBACzC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,MAAM;oBACtF,CAAC,CAAC,CAAC;aACN;YACD,IAAI,EAAE;gBACJ,SAAS,EAAE,MAAM,CAAC,WAAW,CAC3B,MAAM,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;qBAC3B,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;qBAC1D,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAgB,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE;wBACxC,UAAU,EAAE,GAAG,EAAE,UAAU,IAAI,EAAE;wBACjC,cAAc,EAAE,GAAG,EAAE,cAAc,IAAI,SAAS;qBACjD,CAAC,CAAC,CACN;aACF;YACD,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACtC,CAAC;IACJ,CAAC;IAED,0BAA0B;IAC1B,KAAK,CAAC,aAAa;QACjB,MAAM,SAAS,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3C,MAAM,UAAU,GAAuB,EAAE,CAAC;QAE1C,IAAI,SAAS,EAAE,UAAU,EAAE,CAAC;YAC1B,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,UAAU,CAAoB,EAAE,CAAC;gBACjF,UAAU,CAAC,IAAI,CAAC;oBACd,EAAE;oBACF,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE;oBACvB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,SAAS;oBAChC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,SAAS;oBAC5B,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI;oBAC/B,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE;oBAC/C,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE;oBAC3C,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,EAAE;oBAC/B,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,EAAE;iBACtC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,uDAAuD;QACvD,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACvB,MAAM,WAAW,GAA2B,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;YACnI,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC1C,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC1C,IAAI,MAAM,KAAK,MAAM;gBAAE,OAAO,MAAM,GAAG,MAAM,CAAC;YAC9C,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;QAEH,OAAO;YACL,UAAU;YACV,OAAO,EAAE,SAAS,EAAE,OAAO,IAAI,EAAE;SAClC,CAAC;IACJ,CAAC;IAED,sBAAsB;IACtB,KAAK,CAAC,aAAa;QACjB,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACrC,OAAO;YACL,SAAS,EAAE,IAAI,IAAI,EAAE;YACrB,WAAW,EAAE,IAAI,EAAE,WAAW;SAC/B,CAAC;IACJ,CAAC;IAED,sDAAsD;IACtD,KAAK,CAAC,mBAAmB;QACvB,OAAO,IAAI,CAAC,kBAAkB,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IACxG,CAAC;IAED,gCAAgC;IAChC,KAAK,CAAC,aAAa;QACjB,MAAM,KAAK,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACxC,IAAI,CAAC,KAAK,EAAE,SAAS;YAAE,OAAO,EAAE,CAAC;QACjC,OAAQ,KAAK,CAAC,SAAmB,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;YACjD,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE;YAClB,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE;YACtB,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;YACnB,YAAY,EAAE,CAAC,CAAC,YAAY,IAAI,CAAC;YACjC,YAAY,EAAE,CAAC,CAAC,YAAY,IAAI,CAAC;YACjC,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,EAAE;SAC3B,CAAC,CAAC,CAAC;IACN,CAAC;IAED,wBAAwB;IACxB,KAAK,CAAC,eAAe;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC/B,OAAO,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAClF,CAAC;IAED,wBAAwB;IACxB,KAAK,CAAC,gBAAgB;QACpB,OAAO,IAAI,CAAC,iBAAiB,EAAE,IAAI,EAAE,CAAC;IACxC,CAAC;IAED,iBAAiB;IACjB,KAAK,CAAC,SAAS;QACb,OAAO,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC;IACjC,CAAC;IAED,qBAAqB;IACrB,KAAK,CAAC,YAAY;QAChB,OAAO,IAAI,CAAC,YAAY,CAAC,iCAAiC,CAAC,CAAC;IAC9D,CAAC;IAED,4BAA4B;IAC5B,KAAK,CAAC,UAAU;QACd,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CACxB,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,EACtB,iCAAiC,CAClC,CAAC;QACF,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,EAAE,CAAC;QAExC,MAAM,OAAO,GAAiB,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YAClE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;oBAAE,SAAS;gBACnC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBAClD,yBAAyB;gBACzB,MAAM,QAAQ,GAAG,EAAE,CAAC,WAAW,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;qBAChE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;qBAC5B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACpB,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC;gBACvC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;gBAEjD,IAAI,UAAU,GAAG,CAAC,CAAC;gBACnB,IAAI,YAAY,GAAG,CAAC,CAAC;gBACrB,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;gBAClD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;gBACtD,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC7B,UAAU,GAAG,EAAE,CAAC,WAAW,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;yBAC5D,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;gBACzC,CAAC;gBACD,IAAI,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;oBAC/B,YAAY,GAAG,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC;yBACvC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;gBAC3C,CAAC;gBAED,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC;YACxE,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAC,wBAAwB,CAAC,CAAC;QACpC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,0BAA0B;IAElB,iBAAiB;QACvB,OAAO,IAAI,CAAC,YAAY,CAAC,iCAAiC,CAAC,CAAC;IAC9D,CAAC;IAEO,gBAAgB;QACtB,OAAO,IAAI,CAAC,YAAY,CAAC,+BAA+B,CAAC,CAAC;IAC5D,CAAC;IAEO,iBAAiB;QACvB,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,qCAAqC,CAAC,CAAC;QACtE,OAAO,IAAI,EAAE,aAAa,IAAI,IAAI,IAAI,EAAE,CAAC;IAC3C,CAAC;IAEO,SAAS;QACf,OAAO,IAAI,CAAC,YAAY,CAAC,4BAA4B,CAAC,CAAC;IACzD,CAAC;IAEO,kBAAkB;QACxB,OAAO,IAAI,CAAC,YAAY,CAAC,uCAAuC,CAAC,CAAC;IACpE,CAAC;IAEO,UAAU;QAChB,OAAO,IAAI,CAAC,YAAY,CAAC,wBAAwB,CAAC,CAAC;IACrD,CAAC;IAEO,YAAY,CAAC,YAAoB;QACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;QAC3D,IAAI,CAAC;YACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC1C,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAEO,sBAAsB,CAAC,OAAY;QACzC,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QACxB,MAAM,MAAM,GAA2B,EAAE,CAAC;QAC1C,KAAK,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,kBAAkB,EAAE,WAAW,CAAC,EAAE,CAAC;YAC5G,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI;gBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QACvD,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,cAAc,CAAC,SAAc;QACnC,IAAI,CAAC,SAAS,EAAE,cAAc,EAAE,MAAM;YAAE,OAAO,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACpD,MAAM,YAAY,GAAG,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC;QACjF,OAAO,YAAY,EAAE,WAAW,IAAI,CAAC,CAAC;IACxC,CAAC;CACF"}
@@ -0,0 +1,35 @@
1
+ import type { PluginInfo, SkillUsage, LspStatus } from '../../types.js';
2
+ /**
3
+ * Enhanced plugin scanner that reads plugin cache, LSP status,
4
+ * LSP warmup, and skill trigger data.
5
+ */
6
+ export declare class PluginScanner {
7
+ private projectRoot;
8
+ constructor(projectRoot: string);
9
+ /** Scan installed plugins from cache */
10
+ getInstalledPlugins(): PluginInfo[];
11
+ /** Get LSP check status */
12
+ getLspStatus(): LspStatus | null;
13
+ /** Get LSP warmup status */
14
+ getLspWarmup(): LspWarmupStatus | null;
15
+ /** Get skill trigger index */
16
+ getSkillTriggers(): Record<string, string[]>;
17
+ /** Get skill usage from analytics cache */
18
+ getSkillUsage(): SkillUsage[];
19
+ /** Combined plugins + LSP response */
20
+ getFullPluginData(): {
21
+ plugins: PluginInfo[];
22
+ skills: SkillUsage[];
23
+ lsp: LspStatus | null;
24
+ warmup: LspWarmupStatus | null;
25
+ triggerCount: number;
26
+ };
27
+ private readJson;
28
+ }
29
+ export interface LspWarmupStatus {
30
+ warmedUp: string[];
31
+ failed: string[];
32
+ elapsed: number;
33
+ timestamp: string;
34
+ }
35
+ //# sourceMappingURL=plugin-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-scanner.d.ts","sourceRoot":"","sources":["../../../../../src/dashboard/server/data/plugin-scanner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAExE;;;GAGG;AACH,qBAAa,aAAa;IACZ,OAAO,CAAC,WAAW;gBAAX,WAAW,EAAE,MAAM;IAEvC,wCAAwC;IACxC,mBAAmB,IAAI,UAAU,EAAE;IAoCnC,2BAA2B;IAC3B,YAAY,IAAI,SAAS,GAAG,IAAI;IAIhC,4BAA4B;IAC5B,YAAY,IAAI,eAAe,GAAG,IAAI;IAItC,8BAA8B;IAC9B,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAK5C,2CAA2C;IAC3C,aAAa,IAAI,UAAU,EAAE;IAc7B,sCAAsC;IACtC,iBAAiB,IAAI;QACnB,OAAO,EAAE,UAAU,EAAE,CAAC;QACtB,MAAM,EAAE,UAAU,EAAE,CAAC;QACrB,GAAG,EAAE,SAAS,GAAG,IAAI,CAAC;QACtB,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;QAC/B,YAAY,EAAE,MAAM,CAAC;KACtB;IAUD,OAAO,CAAC,QAAQ;CAOjB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB"}
@@ -0,0 +1,96 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ /**
4
+ * Enhanced plugin scanner that reads plugin cache, LSP status,
5
+ * LSP warmup, and skill trigger data.
6
+ */
7
+ export class PluginScanner {
8
+ constructor(projectRoot) {
9
+ this.projectRoot = projectRoot;
10
+ }
11
+ /** Scan installed plugins from cache */
12
+ getInstalledPlugins() {
13
+ const cacheDir = path.join(process.env.HOME || '', '.claude/plugins/cache/specweave');
14
+ if (!fs.existsSync(cacheDir))
15
+ return [];
16
+ const plugins = [];
17
+ try {
18
+ const entries = fs.readdirSync(cacheDir, { withFileTypes: true });
19
+ for (const entry of entries) {
20
+ if (!entry.isDirectory())
21
+ continue;
22
+ const pluginDir = path.join(cacheDir, entry.name);
23
+ const versions = fs.readdirSync(pluginDir, { withFileTypes: true })
24
+ .filter(v => v.isDirectory())
25
+ .map(v => v.name);
26
+ const version = versions[0] || '0.0.0';
27
+ const versionDir = path.join(pluginDir, version);
28
+ let skillCount = 0;
29
+ let commandCount = 0;
30
+ const skillsDir = path.join(versionDir, 'skills');
31
+ const commandsDir = path.join(versionDir, 'commands');
32
+ if (fs.existsSync(skillsDir)) {
33
+ skillCount = fs.readdirSync(skillsDir, { withFileTypes: true })
34
+ .filter(d => d.isDirectory()).length;
35
+ }
36
+ if (fs.existsSync(commandsDir)) {
37
+ commandCount = fs.readdirSync(commandsDir)
38
+ .filter(f => f.endsWith('.md')).length;
39
+ }
40
+ plugins.push({ name: entry.name, version, skillCount, commandCount });
41
+ }
42
+ }
43
+ catch { /* ignore */ }
44
+ return plugins.sort((a, b) => a.name.localeCompare(b.name));
45
+ }
46
+ /** Get LSP check status */
47
+ getLspStatus() {
48
+ return this.readJson('.specweave/state/lsp-check.json');
49
+ }
50
+ /** Get LSP warmup status */
51
+ getLspWarmup() {
52
+ return this.readJson('.specweave/state/lsp-warmup.json');
53
+ }
54
+ /** Get skill trigger index */
55
+ getSkillTriggers() {
56
+ const data = this.readJson('.specweave/state/skill-triggers-index.json');
57
+ return data || {};
58
+ }
59
+ /** Get skill usage from analytics cache */
60
+ getSkillUsage() {
61
+ const cache = this.readJson('.specweave/state/analytics/cache.json');
62
+ if (!cache?.topSkills)
63
+ return [];
64
+ return cache.topSkills.map((s) => ({
65
+ name: s.name || '',
66
+ plugin: s.plugin || '',
67
+ count: s.count || 0,
68
+ successCount: s.successCount || s.count || 0,
69
+ failureCount: s.failureCount || 0,
70
+ avgDuration: s.avgDuration,
71
+ lastUsed: s.lastUsed || '',
72
+ }));
73
+ }
74
+ /** Combined plugins + LSP response */
75
+ getFullPluginData() {
76
+ return {
77
+ plugins: this.getInstalledPlugins(),
78
+ skills: this.getSkillUsage(),
79
+ lsp: this.getLspStatus(),
80
+ warmup: this.getLspWarmup(),
81
+ triggerCount: Object.keys(this.getSkillTriggers()).length,
82
+ };
83
+ }
84
+ readJson(relativePath) {
85
+ const fullPath = path.join(this.projectRoot, relativePath);
86
+ try {
87
+ if (!fs.existsSync(fullPath))
88
+ return null;
89
+ return JSON.parse(fs.readFileSync(fullPath, 'utf-8'));
90
+ }
91
+ catch {
92
+ return null;
93
+ }
94
+ }
95
+ }
96
+ //# sourceMappingURL=plugin-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-scanner.js","sourceRoot":"","sources":["../../../../../src/dashboard/server/data/plugin-scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ export interface SyncAuditEntry {
2
+ timestamp: string;
3
+ platform: string;
4
+ operation: string;
5
+ itemId: string;
6
+ result: 'success' | 'denied' | 'error' | 'skipped';
7
+ direction: 'push' | 'pull';
8
+ duration?: number;
9
+ message?: string;
10
+ conflict?: boolean;
11
+ oldValues?: Record<string, unknown>;
12
+ newValues?: Record<string, unknown>;
13
+ }
14
+ /**
15
+ * Reads sync audit JSONL log files from .specweave/logs/sync/
16
+ */
17
+ export declare class SyncAuditReader {
18
+ private auditDir;
19
+ constructor(projectRoot: string);
20
+ /** Get recent audit entries, optionally filtered */
21
+ getRecentEntries(options?: {
22
+ limit?: number;
23
+ platform?: string;
24
+ result?: string;
25
+ since?: string;
26
+ }): Promise<SyncAuditEntry[]>;
27
+ /** Get audit summary per platform */
28
+ getSummary(): Promise<Record<string, {
29
+ total: number;
30
+ success: number;
31
+ errors: number;
32
+ denied: number;
33
+ lastEntry?: string;
34
+ }>>;
35
+ private parseAuditFile;
36
+ private getAuditFiles;
37
+ }
38
+ //# sourceMappingURL=sync-audit-reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-audit-reader.d.ts","sourceRoot":"","sources":["../../../../../src/dashboard/server/data/sync-audit-reader.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;IACnD,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAS;gBAEb,WAAW,EAAE,MAAM;IAI/B,oDAAoD;IAC9C,gBAAgB,CAAC,OAAO,GAAE;QAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;KACX,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAoBlC,qCAAqC;IAC/B,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE;QACzC,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;YAmBW,cAAc;IA4B5B,OAAO,CAAC,aAAa;CAStB"}
@@ -0,0 +1,94 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as readline from 'readline';
4
+ /**
5
+ * Reads sync audit JSONL log files from .specweave/logs/sync/
6
+ */
7
+ export class SyncAuditReader {
8
+ constructor(projectRoot) {
9
+ this.auditDir = path.join(projectRoot, '.specweave/logs/sync');
10
+ }
11
+ /** Get recent audit entries, optionally filtered */
12
+ async getRecentEntries(options = {}) {
13
+ const { limit = 100, platform, result, since } = options;
14
+ const files = this.getAuditFiles();
15
+ const entries = [];
16
+ // Read files in reverse order (newest first)
17
+ for (let i = files.length - 1; i >= 0 && entries.length < limit; i--) {
18
+ const fileEntries = await this.parseAuditFile(files[i]);
19
+ for (let j = fileEntries.length - 1; j >= 0 && entries.length < limit; j--) {
20
+ const entry = fileEntries[j];
21
+ if (platform && entry.platform !== platform)
22
+ continue;
23
+ if (result && entry.result !== result)
24
+ continue;
25
+ if (since && entry.timestamp < since)
26
+ continue;
27
+ entries.push(entry);
28
+ }
29
+ }
30
+ return entries;
31
+ }
32
+ /** Get audit summary per platform */
33
+ async getSummary() {
34
+ const entries = await this.getRecentEntries({ limit: 500 });
35
+ const summary = {};
36
+ for (const entry of entries) {
37
+ if (!summary[entry.platform]) {
38
+ summary[entry.platform] = { total: 0, success: 0, errors: 0, denied: 0 };
39
+ }
40
+ const s = summary[entry.platform];
41
+ s.total++;
42
+ if (entry.result === 'success')
43
+ s.success++;
44
+ if (entry.result === 'error')
45
+ s.errors++;
46
+ if (entry.result === 'denied')
47
+ s.denied++;
48
+ if (!s.lastEntry || entry.timestamp > s.lastEntry)
49
+ s.lastEntry = entry.timestamp;
50
+ }
51
+ return summary;
52
+ }
53
+ async parseAuditFile(filePath) {
54
+ const entries = [];
55
+ const stream = fs.createReadStream(filePath, { encoding: 'utf-8' });
56
+ const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
57
+ for await (const line of rl) {
58
+ if (!line.trim())
59
+ continue;
60
+ try {
61
+ const data = JSON.parse(line);
62
+ entries.push({
63
+ timestamp: data.timestamp || '',
64
+ platform: data.platform || 'unknown',
65
+ operation: data.operation || data.action || '',
66
+ itemId: data.itemId || data.id || '',
67
+ result: data.result || 'success',
68
+ direction: data.direction || 'push',
69
+ duration: data.duration,
70
+ message: data.message || data.error,
71
+ conflict: data.conflict || false,
72
+ oldValues: data.oldValues,
73
+ newValues: data.newValues,
74
+ });
75
+ }
76
+ catch { /* skip malformed */ }
77
+ }
78
+ return entries;
79
+ }
80
+ getAuditFiles() {
81
+ if (!fs.existsSync(this.auditDir))
82
+ return [];
83
+ try {
84
+ return fs.readdirSync(this.auditDir)
85
+ .filter(f => f.endsWith('.jsonl'))
86
+ .sort()
87
+ .map(f => path.join(this.auditDir, f));
88
+ }
89
+ catch {
90
+ return [];
91
+ }
92
+ }
93
+ }
94
+ //# sourceMappingURL=sync-audit-reader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-audit-reader.js","sourceRoot":"","sources":["../../../../../src/dashboard/server/data/sync-audit-reader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { SSEEventType } from '../types.js';
2
+ export interface WatchEvent {
3
+ type: SSEEventType;
4
+ data: Record<string, unknown>;
5
+ }
6
+ export declare class FileWatcher {
7
+ private projectRoot;
8
+ private onEvent;
9
+ private debounceMs;
10
+ private watchers;
11
+ private debounceTimers;
12
+ constructor(projectRoot: string, onEvent: (event: WatchEvent) => void, debounceMs?: number);
13
+ private startWatching;
14
+ private watchFile;
15
+ private debouncedEmit;
16
+ /** Stop all watchers and clear timers */
17
+ close(): void;
18
+ }
19
+ //# sourceMappingURL=file-watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-watcher.d.ts","sourceRoot":"","sources":["../../../../src/dashboard/server/file-watcher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,qBAAa,WAAW;IAKpB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,UAAU;IANpB,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,cAAc,CAAoD;gBAGhE,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,EACpC,UAAU,SAAM;IAK1B,OAAO,CAAC,aAAa;IAqDrB,OAAO,CAAC,SAAS;IAajB,OAAO,CAAC,aAAa;IAqBrB,yCAAyC;IACzC,KAAK,IAAI,IAAI;CAQd"}
@@ -0,0 +1,104 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ export class FileWatcher {
4
+ constructor(projectRoot, onEvent, debounceMs = 300) {
5
+ this.projectRoot = projectRoot;
6
+ this.onEvent = onEvent;
7
+ this.debounceMs = debounceMs;
8
+ this.watchers = [];
9
+ this.debounceTimers = new Map();
10
+ this.startWatching();
11
+ }
12
+ startWatching() {
13
+ // Watch specific state files
14
+ const targets = [
15
+ { relativePath: '.specweave/state/dashboard.json', eventType: 'increment-update' },
16
+ { relativePath: '.specweave/state/notifications.json', eventType: 'notification' },
17
+ { relativePath: '.specweave/state/analytics/events.jsonl', eventType: 'analytics-event' },
18
+ { relativePath: '.specweave/logs/costs.json', eventType: 'cost-update' },
19
+ { relativePath: '.specweave/sync-metadata.json', eventType: 'sync-update' },
20
+ { relativePath: '.specweave/config.json', eventType: 'config-changed' },
21
+ ];
22
+ for (const target of targets) {
23
+ const fullPath = path.join(this.projectRoot, target.relativePath);
24
+ this.watchFile(fullPath, target.eventType);
25
+ }
26
+ // Watch increments directory recursively for metadata/task changes
27
+ const incDir = path.join(this.projectRoot, '.specweave/increments');
28
+ if (fs.existsSync(incDir)) {
29
+ try {
30
+ const watcher = fs.watch(incDir, { recursive: true }, (_event, filename) => {
31
+ if (filename && (filename.endsWith('metadata.json') || filename.endsWith('tasks.md'))) {
32
+ const incrementId = filename.split(path.sep)[0] || filename.split('/')[0];
33
+ this.debouncedEmit('increment-update', incDir, {
34
+ incrementId,
35
+ file: filename,
36
+ });
37
+ }
38
+ });
39
+ this.watchers.push(watcher);
40
+ watcher.on('error', () => { }); // Silently handle watcher errors
41
+ }
42
+ catch {
43
+ // Directory may not exist or be watchable
44
+ }
45
+ }
46
+ // Watch sync audit logs directory
47
+ const syncLogDir = path.join(this.projectRoot, '.specweave/logs/sync');
48
+ if (fs.existsSync(syncLogDir)) {
49
+ try {
50
+ const watcher = fs.watch(syncLogDir, (_event, filename) => {
51
+ if (filename && filename.endsWith('.jsonl')) {
52
+ this.debouncedEmit('sync-audit', syncLogDir, { file: filename });
53
+ }
54
+ });
55
+ this.watchers.push(watcher);
56
+ watcher.on('error', () => { });
57
+ }
58
+ catch {
59
+ // Silent
60
+ }
61
+ }
62
+ }
63
+ watchFile(fullPath, eventType) {
64
+ if (!fs.existsSync(fullPath))
65
+ return;
66
+ try {
67
+ const watcher = fs.watch(fullPath, () => {
68
+ this.debouncedEmit(eventType, fullPath, { file: path.basename(fullPath) });
69
+ });
70
+ this.watchers.push(watcher);
71
+ watcher.on('error', () => { }); // Silently handle
72
+ }
73
+ catch {
74
+ // File may not be watchable
75
+ }
76
+ }
77
+ debouncedEmit(eventType, source, data) {
78
+ const key = `${eventType}:${source}`;
79
+ const existing = this.debounceTimers.get(key);
80
+ if (existing)
81
+ clearTimeout(existing);
82
+ this.debounceTimers.set(key, setTimeout(() => {
83
+ this.debounceTimers.delete(key);
84
+ this.onEvent({
85
+ type: eventType,
86
+ data: { ...data, source, timestamp: new Date().toISOString() },
87
+ });
88
+ }, this.debounceMs));
89
+ }
90
+ /** Stop all watchers and clear timers */
91
+ close() {
92
+ for (const w of this.watchers) {
93
+ try {
94
+ w.close();
95
+ }
96
+ catch { /* already closed */ }
97
+ }
98
+ this.watchers = [];
99
+ for (const t of this.debounceTimers.values())
100
+ clearTimeout(t);
101
+ this.debounceTimers.clear();
102
+ }
103
+ }
104
+ //# sourceMappingURL=file-watcher.js.map