devguard 0.2.1 → 0.3.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/index.js CHANGED
@@ -9,6 +9,7 @@ const read_entries_js_1 = require("./tools/read-entries.js");
9
9
  const catch_me_up_js_1 = require("./tools/catch-me-up.js");
10
10
  const setup_js_1 = require("./tools/setup.js");
11
11
  const branch_map_js_1 = require("./tools/branch-map.js");
12
+ const daily_view_js_1 = require("./tools/daily-view.js");
12
13
  const auto_setup_js_1 = require("./utils/auto-setup.js");
13
14
  // Auto-setup on first run: adds .devdiary/ to .gitignore
14
15
  // and auto-logging instruction to CLAUDE.md or .cursorrules
@@ -23,6 +24,7 @@ const server = new mcp_js_1.McpServer({
23
24
  (0, catch_me_up_js_1.registerCatchMeUp)(server);
24
25
  (0, setup_js_1.registerSetup)(server);
25
26
  (0, branch_map_js_1.registerBranchMap)(server);
27
+ (0, daily_view_js_1.registerDailyView)(server);
26
28
  async function main() {
27
29
  const transport = new stdio_js_1.StdioServerTransport();
28
30
  await server.connect(transport);
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerDailyView(server: McpServer): void;
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.registerDailyView = registerDailyView;
37
+ const zod_1 = require("zod");
38
+ const git = __importStar(require("../utils/git.js"));
39
+ const storage = __importStar(require("../utils/storage.js"));
40
+ const daily_view_html_js_1 = require("../utils/daily-view-html.js");
41
+ function registerDailyView(server) {
42
+ server.tool("daily_view", "Opens a calendar dashboard in the browser. Each day shows bullet points of what you worked on — pulled from diary entries across all branches.", {
43
+ project_path: zod_1.z.string().describe("Absolute path to the project directory"),
44
+ }, async ({ project_path }) => {
45
+ if (!git.isGitRepo(project_path)) {
46
+ return {
47
+ content: [{ type: "text", text: "This project isn't using git yet — nothing to show." }],
48
+ };
49
+ }
50
+ const currentBranch = git.getBranch(project_path);
51
+ // Collect all diary entries from main stem
52
+ const mainEntries = storage.readEntries(project_path, 90);
53
+ const allParsed = [];
54
+ for (const content of mainEntries) {
55
+ const entries = storage.parseDiaryEntries(content);
56
+ for (const entry of entries) {
57
+ const date = entry.date ? entry.date.slice(0, 10) : "";
58
+ if (date)
59
+ allParsed.push({ date, entry });
60
+ }
61
+ }
62
+ // Collect diary entries from all branches
63
+ const branchFiles = storage.listBranchFiles(project_path);
64
+ for (const bf of branchFiles) {
65
+ const entries = storage.parseDiaryEntries(bf.content);
66
+ for (const entry of entries) {
67
+ const date = entry.date ? entry.date.slice(0, 10) : "";
68
+ if (date)
69
+ allParsed.push({ date, entry });
70
+ }
71
+ }
72
+ // Group by date
73
+ const dateMap = new Map();
74
+ for (const { date, entry } of allParsed) {
75
+ if (!dateMap.has(date))
76
+ dateMap.set(date, []);
77
+ dateMap.get(date).push(entry);
78
+ }
79
+ const days = Array.from(dateMap.entries())
80
+ .map(([date, entries]) => ({ date, entries }))
81
+ .sort((a, b) => a.date.localeCompare(b.date));
82
+ const filePath = (0, daily_view_html_js_1.generateAndOpenDailyView)(project_path, days, currentBranch);
83
+ return {
84
+ content: [{ type: "text", text: `Daily view opened in your browser.\n\nSaved to: ${filePath}` }],
85
+ };
86
+ });
87
+ }
@@ -0,0 +1,6 @@
1
+ import { DiaryEntry } from "./storage.js";
2
+ export interface DayData {
3
+ date: string;
4
+ entries: DiaryEntry[];
5
+ }
6
+ export declare function generateAndOpenDailyView(projectPath: string, days: DayData[], currentBranch: string): string;
@@ -0,0 +1,596 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateAndOpenDailyView = generateAndOpenDailyView;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const child_process_1 = require("child_process");
7
+ function generateAndOpenDailyView(projectPath, days, currentBranch) {
8
+ const projectName = projectPath.split("/").pop() || "project";
9
+ const html = buildHtml(days, projectName, currentBranch);
10
+ const dir = (0, path_1.join)(projectPath, ".devguard");
11
+ (0, fs_1.mkdirSync)(dir, { recursive: true });
12
+ const filePath = (0, path_1.join)(dir, "daily-view.html");
13
+ (0, fs_1.writeFileSync)(filePath, html, "utf-8");
14
+ try {
15
+ const platform = process.platform;
16
+ if (platform === "darwin") {
17
+ (0, child_process_1.execSync)(`open "${filePath}"`, { timeout: 5000 });
18
+ }
19
+ else if (platform === "linux") {
20
+ (0, child_process_1.execSync)(`xdg-open "${filePath}"`, { timeout: 5000 });
21
+ }
22
+ else if (platform === "win32") {
23
+ (0, child_process_1.execSync)(`start "" "${filePath}"`, { timeout: 5000 });
24
+ }
25
+ }
26
+ catch {
27
+ // Silently fail — file is still written
28
+ }
29
+ return filePath;
30
+ }
31
+ function esc(str) {
32
+ return str
33
+ .replace(/&/g, "&amp;")
34
+ .replace(/</g, "&lt;")
35
+ .replace(/>/g, "&gt;")
36
+ .replace(/"/g, "&quot;")
37
+ .replace(/'/g, "&#39;");
38
+ }
39
+ function buildHtml(days, projectName, currentBranch) {
40
+ // Build a JSON-safe data structure for the JS side
41
+ const daysJson = JSON.stringify(days.map((d) => ({
42
+ date: d.date,
43
+ entries: d.entries.map((e) => ({
44
+ title: e.title,
45
+ summary: e.summary,
46
+ date: e.date,
47
+ commit: e.commit,
48
+ whatChanged: e.whatChanged,
49
+ decisions: e.decisions,
50
+ issues: e.issues,
51
+ nextSteps: e.nextSteps,
52
+ })),
53
+ })));
54
+ return `<!DOCTYPE html>
55
+ <html lang="en">
56
+ <head>
57
+ <meta charset="UTF-8">
58
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
59
+ <title>${esc(projectName)} — Daily View</title>
60
+ <style>
61
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
62
+
63
+ * { margin: 0; padding: 0; box-sizing: border-box; }
64
+
65
+ body {
66
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
67
+ background: #1a1a2e;
68
+ color: #e0e0e0;
69
+ min-height: 100vh;
70
+ }
71
+
72
+ /* Header */
73
+ .header {
74
+ display: flex;
75
+ align-items: center;
76
+ justify-content: space-between;
77
+ padding: 20px 32px;
78
+ border-bottom: 1px solid #2a2a4a;
79
+ background: #16162a;
80
+ }
81
+
82
+ .header h1 {
83
+ font-size: 20px;
84
+ font-weight: 600;
85
+ color: #fff;
86
+ }
87
+
88
+ .header h1 span {
89
+ color: #4fc3f7;
90
+ font-weight: 400;
91
+ }
92
+
93
+ .header .branch-badge {
94
+ font-size: 13px;
95
+ background: #2a2a4a;
96
+ color: #a0a0c0;
97
+ padding: 4px 12px;
98
+ border-radius: 12px;
99
+ font-family: 'JetBrains Mono', monospace;
100
+ }
101
+
102
+ /* Nav */
103
+ .nav {
104
+ display: flex;
105
+ align-items: center;
106
+ justify-content: center;
107
+ gap: 16px;
108
+ padding: 16px 32px;
109
+ border-bottom: 1px solid #2a2a4a;
110
+ }
111
+
112
+ .nav button {
113
+ background: #2a2a4a;
114
+ color: #e0e0e0;
115
+ border: none;
116
+ padding: 8px 16px;
117
+ border-radius: 8px;
118
+ cursor: pointer;
119
+ font-family: inherit;
120
+ font-size: 14px;
121
+ transition: background 0.15s;
122
+ }
123
+
124
+ .nav button:hover { background: #3a3a5a; }
125
+
126
+ .nav .month-label {
127
+ font-size: 18px;
128
+ font-weight: 600;
129
+ min-width: 200px;
130
+ text-align: center;
131
+ }
132
+
133
+ /* Calendar grid */
134
+ .calendar-container {
135
+ max-width: 1200px;
136
+ margin: 24px auto;
137
+ padding: 0 24px;
138
+ }
139
+
140
+ .weekday-header {
141
+ display: grid;
142
+ grid-template-columns: repeat(7, 1fr);
143
+ gap: 4px;
144
+ margin-bottom: 4px;
145
+ }
146
+
147
+ .weekday-header div {
148
+ text-align: center;
149
+ font-size: 12px;
150
+ font-weight: 600;
151
+ color: #808098;
152
+ padding: 8px 0;
153
+ text-transform: uppercase;
154
+ letter-spacing: 0.5px;
155
+ }
156
+
157
+ .calendar-grid {
158
+ display: grid;
159
+ grid-template-columns: repeat(7, 1fr);
160
+ gap: 4px;
161
+ }
162
+
163
+ .day-cell {
164
+ min-height: 120px;
165
+ background: #20203a;
166
+ border-radius: 8px;
167
+ padding: 8px;
168
+ cursor: default;
169
+ transition: background 0.15s, box-shadow 0.15s;
170
+ position: relative;
171
+ overflow: hidden;
172
+ }
173
+
174
+ .day-cell.empty {
175
+ background: transparent;
176
+ }
177
+
178
+ .day-cell.today {
179
+ box-shadow: inset 0 0 0 2px #4fc3f7;
180
+ }
181
+
182
+ .day-cell.has-entries {
183
+ cursor: pointer;
184
+ }
185
+
186
+ .day-cell.has-entries:hover {
187
+ background: #2a2a4a;
188
+ box-shadow: 0 2px 12px rgba(79, 195, 247, 0.1);
189
+ }
190
+
191
+ .day-cell.selected {
192
+ background: #2a2a4a;
193
+ box-shadow: inset 0 0 0 2px #4fc3f7, 0 2px 12px rgba(79, 195, 247, 0.15);
194
+ }
195
+
196
+ .day-number {
197
+ font-size: 13px;
198
+ font-weight: 600;
199
+ color: #808098;
200
+ margin-bottom: 6px;
201
+ }
202
+
203
+ .day-cell.has-entries .day-number {
204
+ color: #4fc3f7;
205
+ }
206
+
207
+ .day-cell.today .day-number {
208
+ color: #fff;
209
+ }
210
+
211
+ .day-bullets {
212
+ list-style: none;
213
+ padding: 0;
214
+ }
215
+
216
+ .day-bullets li {
217
+ font-size: 11px;
218
+ color: #b0b0c8;
219
+ padding: 2px 0;
220
+ line-height: 1.4;
221
+ white-space: nowrap;
222
+ overflow: hidden;
223
+ text-overflow: ellipsis;
224
+ }
225
+
226
+ .day-bullets li::before {
227
+ content: "";
228
+ display: inline-block;
229
+ width: 5px;
230
+ height: 5px;
231
+ background: #4fc3f7;
232
+ border-radius: 50%;
233
+ margin-right: 6px;
234
+ vertical-align: middle;
235
+ }
236
+
237
+ .day-entry-count {
238
+ position: absolute;
239
+ top: 8px;
240
+ right: 8px;
241
+ background: #4fc3f7;
242
+ color: #1a1a2e;
243
+ font-size: 10px;
244
+ font-weight: 700;
245
+ width: 18px;
246
+ height: 18px;
247
+ border-radius: 50%;
248
+ display: flex;
249
+ align-items: center;
250
+ justify-content: center;
251
+ }
252
+
253
+ /* Detail panel */
254
+ .detail-overlay {
255
+ display: none;
256
+ position: fixed;
257
+ top: 0; left: 0; right: 0; bottom: 0;
258
+ background: rgba(10, 10, 20, 0.7);
259
+ z-index: 100;
260
+ }
261
+
262
+ .detail-overlay.open { display: flex; align-items: center; justify-content: center; }
263
+
264
+ .detail-panel {
265
+ background: #1e1e38;
266
+ border-radius: 16px;
267
+ width: 90%;
268
+ max-width: 700px;
269
+ max-height: 80vh;
270
+ overflow-y: auto;
271
+ padding: 28px 32px;
272
+ box-shadow: 0 12px 40px rgba(0,0,0,0.5);
273
+ }
274
+
275
+ .detail-panel::-webkit-scrollbar { width: 6px; }
276
+ .detail-panel::-webkit-scrollbar-track { background: transparent; }
277
+ .detail-panel::-webkit-scrollbar-thumb { background: #3a3a5a; border-radius: 3px; }
278
+
279
+ .detail-header {
280
+ display: flex;
281
+ align-items: center;
282
+ justify-content: space-between;
283
+ margin-bottom: 20px;
284
+ }
285
+
286
+ .detail-header h2 {
287
+ font-size: 18px;
288
+ font-weight: 600;
289
+ color: #fff;
290
+ }
291
+
292
+ .detail-close {
293
+ background: none;
294
+ border: none;
295
+ color: #808098;
296
+ font-size: 22px;
297
+ cursor: pointer;
298
+ padding: 4px 8px;
299
+ border-radius: 6px;
300
+ transition: background 0.15s;
301
+ }
302
+
303
+ .detail-close:hover { background: #2a2a4a; color: #e0e0e0; }
304
+
305
+ .detail-entry {
306
+ margin-bottom: 20px;
307
+ padding: 16px;
308
+ background: #252545;
309
+ border-radius: 10px;
310
+ border-left: 3px solid #4fc3f7;
311
+ }
312
+
313
+ .detail-entry:last-child { margin-bottom: 0; }
314
+
315
+ .detail-entry h3 {
316
+ font-size: 15px;
317
+ font-weight: 600;
318
+ color: #e0e0e0;
319
+ margin-bottom: 4px;
320
+ }
321
+
322
+ .detail-entry .entry-meta {
323
+ font-size: 12px;
324
+ color: #808098;
325
+ font-family: 'JetBrains Mono', monospace;
326
+ margin-bottom: 12px;
327
+ }
328
+
329
+ .detail-section {
330
+ margin-top: 10px;
331
+ }
332
+
333
+ .detail-section .section-label {
334
+ font-size: 11px;
335
+ font-weight: 600;
336
+ text-transform: uppercase;
337
+ letter-spacing: 0.5px;
338
+ margin-bottom: 4px;
339
+ display: block;
340
+ }
341
+
342
+ .detail-section.changes .section-label { color: #4fc3f7; }
343
+ .detail-section.decisions .section-label { color: #ce93d8; }
344
+ .detail-section.issues .section-label { color: #ef9a9a; }
345
+ .detail-section.next-steps .section-label { color: #a5d6a7; }
346
+
347
+ .detail-section ul {
348
+ list-style: none;
349
+ padding: 0;
350
+ }
351
+
352
+ .detail-section ul li {
353
+ font-size: 13px;
354
+ color: #c0c0d8;
355
+ padding: 3px 0;
356
+ padding-left: 14px;
357
+ position: relative;
358
+ line-height: 1.5;
359
+ }
360
+
361
+ .detail-section ul li::before {
362
+ content: "";
363
+ position: absolute;
364
+ left: 0;
365
+ top: 10px;
366
+ width: 5px;
367
+ height: 5px;
368
+ border-radius: 50%;
369
+ }
370
+
371
+ .detail-section.changes ul li::before { background: #4fc3f7; }
372
+ .detail-section.decisions ul li::before { background: #ce93d8; }
373
+ .detail-section.issues ul li::before { background: #ef9a9a; }
374
+ .detail-section.next-steps ul li::before { background: #a5d6a7; }
375
+
376
+ /* Empty state */
377
+ .empty-state {
378
+ text-align: center;
379
+ padding: 80px 32px;
380
+ color: #808098;
381
+ }
382
+
383
+ .empty-state h2 { font-size: 18px; margin-bottom: 8px; color: #a0a0c0; }
384
+ .empty-state p { font-size: 14px; }
385
+ </style>
386
+ </head>
387
+ <body>
388
+
389
+ <div class="header">
390
+ <h1>${esc(projectName)} <span>/ daily view</span></h1>
391
+ <div class="branch-badge">${esc(currentBranch)}</div>
392
+ </div>
393
+
394
+ <div class="nav">
395
+ <button onclick="prevMonth()">&larr; Prev</button>
396
+ <div class="month-label" id="month-label"></div>
397
+ <button onclick="nextMonth()">Next &rarr;</button>
398
+ <button onclick="goToday()" style="margin-left: 12px; background: #4fc3f7; color: #1a1a2e; font-weight: 600;">Today</button>
399
+ </div>
400
+
401
+ <div class="calendar-container">
402
+ <div class="weekday-header">
403
+ <div>Sun</div><div>Mon</div><div>Tue</div><div>Wed</div><div>Thu</div><div>Fri</div><div>Sat</div>
404
+ </div>
405
+ <div class="calendar-grid" id="calendar-grid"></div>
406
+ </div>
407
+
408
+ <div class="detail-overlay" id="detail-overlay" onclick="closeDetail(event)">
409
+ <div class="detail-panel" id="detail-panel" onclick="event.stopPropagation()">
410
+ <div class="detail-header">
411
+ <h2 id="detail-title"></h2>
412
+ <button class="detail-close" onclick="closeDetail()">&times;</button>
413
+ </div>
414
+ <div id="detail-body"></div>
415
+ </div>
416
+ </div>
417
+
418
+ <script>
419
+ const DAYS_DATA = ${daysJson};
420
+
421
+ // Index entries by date
422
+ const dateMap = {};
423
+ DAYS_DATA.forEach(d => { dateMap[d.date] = d.entries; });
424
+
425
+ // State
426
+ const today = new Date();
427
+ let viewYear = today.getFullYear();
428
+ let viewMonth = today.getMonth();
429
+
430
+ const MONTH_NAMES = ["January","February","March","April","May","June","July","August","September","October","November","December"];
431
+
432
+ function todayStr() {
433
+ const y = today.getFullYear();
434
+ const m = String(today.getMonth() + 1).padStart(2, "0");
435
+ const d = String(today.getDate()).padStart(2, "0");
436
+ return y + "-" + m + "-" + d;
437
+ }
438
+
439
+ function dateStr(y, m, d) {
440
+ return y + "-" + String(m + 1).padStart(2, "0") + "-" + String(d).padStart(2, "0");
441
+ }
442
+
443
+ function esc(s) {
444
+ const el = document.createElement("span");
445
+ el.textContent = s;
446
+ return el.innerHTML;
447
+ }
448
+
449
+ function render() {
450
+ document.getElementById("month-label").textContent = MONTH_NAMES[viewMonth] + " " + viewYear;
451
+
452
+ const grid = document.getElementById("calendar-grid");
453
+ grid.innerHTML = "";
454
+
455
+ const firstDay = new Date(viewYear, viewMonth, 1).getDay();
456
+ const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate();
457
+ const todayString = todayStr();
458
+
459
+ // Empty cells before first day
460
+ for (let i = 0; i < firstDay; i++) {
461
+ const cell = document.createElement("div");
462
+ cell.className = "day-cell empty";
463
+ grid.appendChild(cell);
464
+ }
465
+
466
+ for (let d = 1; d <= daysInMonth; d++) {
467
+ const ds = dateStr(viewYear, viewMonth, d);
468
+ const entries = dateMap[ds] || [];
469
+ const isToday = ds === todayString;
470
+ const hasEntries = entries.length > 0;
471
+
472
+ const cell = document.createElement("div");
473
+ cell.className = "day-cell" + (isToday ? " today" : "") + (hasEntries ? " has-entries" : "");
474
+
475
+ if (hasEntries) {
476
+ cell.onclick = function() { openDetail(ds, entries); };
477
+ }
478
+
479
+ let inner = '<div class="day-number">' + d + '</div>';
480
+
481
+ if (hasEntries) {
482
+ inner += '<div class="day-entry-count">' + entries.length + '</div>';
483
+ inner += '<ul class="day-bullets">';
484
+ // Show up to 4 bullet items from whatChanged across all entries
485
+ const bullets = [];
486
+ for (const e of entries) {
487
+ for (const item of e.whatChanged) {
488
+ bullets.push(item);
489
+ if (bullets.length >= 4) break;
490
+ }
491
+ if (bullets.length >= 4) break;
492
+ }
493
+ if (bullets.length === 0) {
494
+ // Fallback to entry titles
495
+ for (const e of entries) {
496
+ if (e.title) bullets.push(e.title);
497
+ if (bullets.length >= 4) break;
498
+ }
499
+ }
500
+ for (const b of bullets) {
501
+ inner += '<li>' + esc(b) + '</li>';
502
+ }
503
+ inner += '</ul>';
504
+ }
505
+
506
+ cell.innerHTML = inner;
507
+ grid.appendChild(cell);
508
+ }
509
+ }
510
+
511
+ function openDetail(ds, entries) {
512
+ const overlay = document.getElementById("detail-overlay");
513
+ const d = new Date(ds + "T12:00:00");
514
+ document.getElementById("detail-title").textContent =
515
+ MONTH_NAMES[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear();
516
+
517
+ let html = "";
518
+ for (const e of entries) {
519
+ html += '<div class="detail-entry">';
520
+ html += '<h3>' + esc(e.title) + '</h3>';
521
+
522
+ const meta = [];
523
+ if (e.commit) meta.push(e.commit);
524
+ if (e.date) {
525
+ try { meta.push(new Date(e.date).toLocaleTimeString()); } catch(x) {}
526
+ }
527
+ if (meta.length) html += '<div class="entry-meta">' + esc(meta.join(" / ")) + '</div>';
528
+
529
+ if (e.whatChanged.length) {
530
+ html += '<div class="detail-section changes"><span class="section-label">What Changed</span><ul>';
531
+ e.whatChanged.forEach(function(item) { html += '<li>' + esc(item) + '</li>'; });
532
+ html += '</ul></div>';
533
+ }
534
+ if (e.decisions.length) {
535
+ html += '<div class="detail-section decisions"><span class="section-label">Decisions</span><ul>';
536
+ e.decisions.forEach(function(item) { html += '<li>' + esc(item) + '</li>'; });
537
+ html += '</ul></div>';
538
+ }
539
+ if (e.issues.length) {
540
+ html += '<div class="detail-section issues"><span class="section-label">Issues</span><ul>';
541
+ e.issues.forEach(function(item) { html += '<li>' + esc(item) + '</li>'; });
542
+ html += '</ul></div>';
543
+ }
544
+ if (e.nextSteps.length) {
545
+ html += '<div class="detail-section next-steps"><span class="section-label">Next Steps</span><ul>';
546
+ e.nextSteps.forEach(function(item) { html += '<li>' + esc(item) + '</li>'; });
547
+ html += '</ul></div>';
548
+ }
549
+
550
+ html += '</div>';
551
+ }
552
+
553
+ if (!html) html = '<div style="color:#808098;text-align:center;padding:20px;">No details available</div>';
554
+
555
+ document.getElementById("detail-body").innerHTML = html;
556
+ overlay.classList.add("open");
557
+ }
558
+
559
+ function closeDetail(event) {
560
+ if (event && event.target !== document.getElementById("detail-overlay")) return;
561
+ document.getElementById("detail-overlay").classList.remove("open");
562
+ }
563
+
564
+ function prevMonth() {
565
+ viewMonth--;
566
+ if (viewMonth < 0) { viewMonth = 11; viewYear--; }
567
+ render();
568
+ }
569
+
570
+ function nextMonth() {
571
+ viewMonth++;
572
+ if (viewMonth > 11) { viewMonth = 0; viewYear++; }
573
+ render();
574
+ }
575
+
576
+ function goToday() {
577
+ viewYear = today.getFullYear();
578
+ viewMonth = today.getMonth();
579
+ render();
580
+ }
581
+
582
+ document.addEventListener("keydown", function(e) {
583
+ if (e.key === "Escape") {
584
+ document.getElementById("detail-overlay").classList.remove("open");
585
+ } else if (e.key === "ArrowLeft") {
586
+ prevMonth();
587
+ } else if (e.key === "ArrowRight") {
588
+ nextMonth();
589
+ }
590
+ });
591
+
592
+ render();
593
+ </script>
594
+ </body>
595
+ </html>`;
596
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devguard",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "MCP server that auto-generates dev diary entries from git activity",
5
5
  "license": "MIT",
6
6
  "bin": {