project-logbook 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.
Files changed (102) hide show
  1. package/README.md +34 -0
  2. package/dist/commands/build.d.ts +1 -0
  3. package/dist/commands/build.js +174 -0
  4. package/dist/commands/init.d.ts +1 -0
  5. package/dist/commands/init.js +83 -0
  6. package/dist/commands/lint.d.ts +1 -0
  7. package/dist/commands/lint.js +78 -0
  8. package/dist/commands/list.d.ts +3 -0
  9. package/dist/commands/list.js +127 -0
  10. package/dist/commands/log.d.ts +3 -0
  11. package/dist/commands/log.js +31 -0
  12. package/dist/commands/new.d.ts +1 -0
  13. package/dist/commands/new.js +135 -0
  14. package/dist/commands/preview.d.ts +1 -0
  15. package/dist/commands/preview.js +19 -0
  16. package/dist/commands/release.d.ts +3 -0
  17. package/dist/commands/release.js +66 -0
  18. package/dist/commands/start.d.ts +1 -0
  19. package/dist/commands/start.js +87 -0
  20. package/dist/commands/steer.d.ts +1 -0
  21. package/dist/commands/steer.js +22 -0
  22. package/dist/commands/upgrade.d.ts +1 -0
  23. package/dist/commands/upgrade.js +23 -0
  24. package/dist/index.d.ts +2 -0
  25. package/dist/index.js +92 -0
  26. package/dist/lib/about-content.d.ts +1 -0
  27. package/dist/lib/about-content.js +7 -0
  28. package/dist/lib/build-helpers.d.ts +15 -0
  29. package/dist/lib/build-helpers.js +171 -0
  30. package/dist/lib/config.d.ts +14 -0
  31. package/dist/lib/config.js +41 -0
  32. package/dist/lib/git-helpers.d.ts +16 -0
  33. package/dist/lib/git-helpers.js +93 -0
  34. package/dist/lib/image-helpers.d.ts +19 -0
  35. package/dist/lib/image-helpers.js +121 -0
  36. package/dist/lib/jira-helpers.d.ts +5 -0
  37. package/dist/lib/jira-helpers.js +5 -0
  38. package/dist/lib/lint-runner.d.ts +2 -0
  39. package/dist/lib/lint-runner.js +31 -0
  40. package/dist/lib/lint-types.d.ts +24 -0
  41. package/dist/lib/lint-types.js +1 -0
  42. package/dist/lib/lockfile.d.ts +6 -0
  43. package/dist/lib/lockfile.js +1 -0
  44. package/dist/lib/logbook-client.d.ts +5 -0
  45. package/dist/lib/logbook-client.js +11 -0
  46. package/dist/lib/migrations.d.ts +11 -0
  47. package/dist/lib/migrations.js +34 -0
  48. package/dist/lib/session.d.ts +16 -0
  49. package/dist/lib/session.js +74 -0
  50. package/dist/lib/styles.d.ts +1 -0
  51. package/dist/lib/styles.js +21 -0
  52. package/dist/lib/template-types.d.ts +118 -0
  53. package/dist/lib/template-types.js +1 -0
  54. package/dist/lib/templates.d.ts +14 -0
  55. package/dist/lib/templates.js +158 -0
  56. package/dist/linters/frontmatter.d.ts +3 -0
  57. package/dist/linters/frontmatter.js +68 -0
  58. package/dist/linters/index.d.ts +1 -0
  59. package/dist/linters/index.js +18 -0
  60. package/dist/linters/jira-prefix.d.ts +3 -0
  61. package/dist/linters/jira-prefix.js +34 -0
  62. package/dist/linters/links.d.ts +3 -0
  63. package/dist/linters/links.js +28 -0
  64. package/dist/linters/lockfile.d.ts +3 -0
  65. package/dist/linters/lockfile.js +20 -0
  66. package/dist/linters/placeholders.d.ts +3 -0
  67. package/dist/linters/placeholders.js +62 -0
  68. package/dist/linters/project-integrity.d.ts +3 -0
  69. package/dist/linters/project-integrity.js +29 -0
  70. package/dist/linters/readability.d.ts +3 -0
  71. package/dist/linters/readability.js +41 -0
  72. package/dist/linters/workspaces.d.ts +3 -0
  73. package/dist/linters/workspaces.js +26 -0
  74. package/dist/templates/ABOUT.md +23 -0
  75. package/dist/templates/CONTRIBUTING.md +78 -0
  76. package/dist/templates/favicon.svg +21 -0
  77. package/dist/templates/index.md +30 -0
  78. package/dist/templates/log.md +4 -0
  79. package/dist/templates/logbook-client.js +162 -0
  80. package/dist/templates/steer.txt +25 -0
  81. package/dist/templates/styles.css +641 -0
  82. package/dist/templates/ticket.md +4 -0
  83. package/dist/utils/date.d.ts +8 -0
  84. package/dist/utils/date.js +47 -0
  85. package/dist/utils/fs.d.ts +13 -0
  86. package/dist/utils/fs.js +38 -0
  87. package/dist/utils/id.d.ts +7 -0
  88. package/dist/utils/id.js +36 -0
  89. package/dist/utils/slug.d.ts +2 -0
  90. package/dist/utils/slug.js +9 -0
  91. package/dist/utils/string.d.ts +2 -0
  92. package/dist/utils/string.js +4 -0
  93. package/package.json +69 -0
  94. package/src/templates/ABOUT.md +23 -0
  95. package/src/templates/CONTRIBUTING.md +78 -0
  96. package/src/templates/favicon.svg +21 -0
  97. package/src/templates/index.md +30 -0
  98. package/src/templates/log.md +4 -0
  99. package/src/templates/logbook-client.js +162 -0
  100. package/src/templates/steer.txt +25 -0
  101. package/src/templates/styles.css +641 -0
  102. package/src/templates/ticket.md +4 -0
@@ -0,0 +1,641 @@
1
+ * {
2
+ box-sizing: border-box;
3
+ }
4
+
5
+ body {
6
+ font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', Roboto, sans-serif;
7
+ line-height: 1.6;
8
+ color: var(--text);
9
+ background: var(--bg);
10
+ margin: 0;
11
+ padding: 0;
12
+ }
13
+
14
+ header {
15
+ background: var(--card-bg);
16
+ border-bottom: 1px solid var(--border);
17
+ padding: 2rem 1rem;
18
+ text-align: center;
19
+ }
20
+
21
+ header h1 {
22
+ margin: 0;
23
+ font-size: 2.25rem;
24
+ font-weight: 800;
25
+ letter-spacing: -0.025em;
26
+ }
27
+
28
+ .tagline {
29
+ color: var(--text-muted);
30
+ font-size: 1.125rem;
31
+ margin-top: 0.5rem;
32
+ }
33
+
34
+ main {
35
+ max-width: var(--max-width);
36
+ margin: 2rem auto;
37
+ padding: 0 1.5rem;
38
+ }
39
+
40
+ /* Timeline */
41
+ .timeline {
42
+ position: relative;
43
+ }
44
+
45
+ .month-group {
46
+ margin-bottom: 3rem;
47
+ }
48
+
49
+ .month-group h2 {
50
+ font-size: 0.75rem;
51
+ text-transform: uppercase;
52
+ letter-spacing: 0.1em;
53
+ color: var(--text-muted);
54
+ margin-bottom: 1.5rem;
55
+ display: flex;
56
+ align-items: center;
57
+ }
58
+
59
+ .month-group h2::after {
60
+ content: '';
61
+ flex: 1;
62
+ height: 1px;
63
+ background: var(--border);
64
+ margin-left: 1rem;
65
+ }
66
+
67
+ .timeline-item {
68
+ position: relative;
69
+ padding-left: 2rem;
70
+ padding-bottom: 3.5rem;
71
+ border-left: 1px solid #cccccc;
72
+ }
73
+
74
+ .timeline-item:last-child {
75
+ border-left: 1px solid transparent;
76
+ }
77
+
78
+ .timeline-item::before {
79
+ content: '';
80
+ position: absolute;
81
+ left: calc(-7px);
82
+ top: 0.75em;
83
+ width: 9px;
84
+ height: 9px;
85
+ border-radius: 50%;
86
+ background: var(--bg);
87
+ border: 2px solid var(--primary);
88
+ }
89
+
90
+ .item-meta {
91
+ font-size: 0.8125rem;
92
+ color: var(--text-muted);
93
+ margin-bottom: 0.25rem;
94
+ }
95
+
96
+ .timeline-card {
97
+ display: flex;
98
+ align-items: center;
99
+ gap: 1rem;
100
+ text-decoration: none;
101
+ color: inherit;
102
+ border-radius: 8px;
103
+ transition: background 0.2s;
104
+ margin: -0.5rem -0.75rem;
105
+ padding: 0.5rem 0.75rem;
106
+ }
107
+
108
+ .timeline-card:hover {
109
+ background: var(--primary-soft);
110
+ }
111
+
112
+ .item-content {
113
+ flex: 1;
114
+ }
115
+
116
+ .item-title {
117
+ margin: 0 0 0.5rem 0;
118
+ font-size: 1.25rem;
119
+ font-weight: 600;
120
+ color: var(--text);
121
+ transition: color 0.2s;
122
+ }
123
+
124
+ .timeline-card:hover .item-title {
125
+ color: var(--primary);
126
+ }
127
+
128
+ .item-summary {
129
+ font-size: 0.9375rem;
130
+ color: var(--text-muted);
131
+ display: -webkit-box;
132
+ -webkit-line-clamp: 3;
133
+ -webkit-box-orient: vertical;
134
+ overflow: hidden;
135
+ }
136
+
137
+ .item-arrow {
138
+ font-size: 2.8rem;
139
+ color: var(--primary);
140
+ opacity: 1;
141
+ flex-shrink: 0;
142
+ transition: transform 0.2s;
143
+ line-height: 1;
144
+ user-select: none;
145
+ }
146
+
147
+ .timeline-card:hover .item-arrow {
148
+ transform: translateX(4px);
149
+ }
150
+
151
+ /* Post Page */
152
+ .back-link {
153
+ display: inline-flex;
154
+ align-items: center;
155
+ font-size: 0.8125rem;
156
+ font-weight: 500;
157
+ color: var(--primary);
158
+ text-decoration: none;
159
+ transition:
160
+ color 0.2s,
161
+ background 0.2s;
162
+ background: var(--primary-soft);
163
+ padding: 0.5rem 1rem;
164
+ border-radius: 2rem;
165
+ }
166
+
167
+ .back-link:hover {
168
+ background: var(--primary);
169
+ color: white;
170
+ }
171
+
172
+ /* Action bar - Back button + tabs on same line */
173
+ .action-bar {
174
+ display: flex;
175
+ align-items: center;
176
+ justify-content: space-between;
177
+ gap: 1rem;
178
+ margin-bottom: 3rem;
179
+ flex-wrap: wrap;
180
+ }
181
+
182
+ .action-tabs-wrapper {
183
+ display: flex;
184
+ align-items: center;
185
+ gap: 1rem;
186
+ flex-wrap: wrap;
187
+ }
188
+
189
+ .jira-link-btn {
190
+ display: inline-flex;
191
+ align-items: center;
192
+ font-size: 0.8125rem;
193
+ font-weight: 500;
194
+ color: var(--primary);
195
+ text-decoration: none;
196
+ transition:
197
+ color 0.2s,
198
+ background 0.2s;
199
+ background: var(--primary-soft);
200
+ padding: 0.5rem 1rem;
201
+ border-radius: 2rem;
202
+ }
203
+
204
+ .jira-link-btn:hover {
205
+ background: var(--primary);
206
+ color: white;
207
+ }
208
+
209
+ /* Jira link in build-meta - inline style */
210
+ .build-meta .jira-link-btn {
211
+ margin-left: 0.5rem;
212
+ background: var(--primary-soft);
213
+ border-radius: 2rem;
214
+ padding: 0.25rem 0.75rem;
215
+ font-size: 0.8125rem;
216
+ }
217
+
218
+ .build-meta .jira-link-btn:hover {
219
+ background: var(--primary);
220
+ color: white;
221
+ }
222
+
223
+ article h1 {
224
+ font-size: 3rem;
225
+ font-weight: 800;
226
+ margin-bottom: 0.75rem;
227
+ letter-spacing: -0.03em;
228
+ line-height: 1.1;
229
+ }
230
+
231
+ .article-meta {
232
+ font-size: 0.875rem;
233
+ color: var(--text-muted);
234
+ margin-bottom: 3rem;
235
+ display: flex;
236
+ gap: 0.75rem;
237
+ align-items: center;
238
+ }
239
+
240
+ .article-meta::before {
241
+ content: '';
242
+ width: 2rem;
243
+ height: 1px;
244
+ background: var(--border);
245
+ }
246
+
247
+ .tabs {
248
+ display: flex;
249
+ gap: 0.5rem;
250
+ background: #f4f4f4;
251
+ padding: 0.25rem;
252
+ border-radius: 0.75rem;
253
+ width: fit-content;
254
+ }
255
+
256
+ .tab {
257
+ padding: 0.625rem 1.25rem;
258
+ font-size: 0.8125rem;
259
+ font-weight: 600;
260
+ color: var(--text-muted);
261
+ cursor: pointer;
262
+ border-radius: 0.5rem;
263
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
264
+ }
265
+
266
+ .tab:hover {
267
+ color: var(--text);
268
+ }
269
+
270
+ .tab.active {
271
+ color: var(--primary);
272
+ background: white;
273
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
274
+ }
275
+
276
+ .content-section {
277
+ animation: fadeIn 0.4s cubic-bezier(0.4, 0, 0.2, 1);
278
+ font-size: 1.125rem;
279
+ line-height: 1.7;
280
+ }
281
+
282
+ .content-section h2 {
283
+ font-size: 1.5rem;
284
+ font-weight: 700;
285
+ margin-top: 2.5rem;
286
+ margin-bottom: 1rem;
287
+ }
288
+
289
+ @keyframes fadeIn {
290
+ from {
291
+ opacity: 0;
292
+ transform: translateY(15px);
293
+ }
294
+ to {
295
+ opacity: 1;
296
+ transform: translateY(0);
297
+ }
298
+ }
299
+
300
+ /* Typography */
301
+ pre {
302
+ background: #1a1a1a;
303
+ color: #e0e0e0;
304
+ padding: 1.5rem;
305
+ border-radius: 0.75rem;
306
+ overflow-x: auto;
307
+ font-size: 0.875rem;
308
+ margin: 2rem 0;
309
+ }
310
+
311
+ code {
312
+ font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
313
+ background: #f4f4f4;
314
+ padding: 0.2rem 0.4rem;
315
+ border-radius: 0.25rem;
316
+ }
317
+
318
+ pre code {
319
+ background: transparent;
320
+ padding: 0;
321
+ }
322
+
323
+ /* NEW badge on timeline */
324
+ .new-badge {
325
+ display: none;
326
+ background: var(--primary);
327
+ color: white;
328
+ font-size: 0.625rem;
329
+ font-weight: 700;
330
+ letter-spacing: 0.08em;
331
+ text-transform: uppercase;
332
+ padding: 0.15rem 0.45rem;
333
+ border-radius: 0.25rem;
334
+ vertical-align: middle;
335
+ margin-right: 0.5rem;
336
+ line-height: 1.6;
337
+ }
338
+
339
+ /* Prev / Next navigation */
340
+ .post-nav {
341
+ display: flex;
342
+ justify-content: space-between;
343
+ gap: 1rem;
344
+ margin-top: 4rem;
345
+ padding-top: 2rem;
346
+ border-top: 1px solid var(--border);
347
+ }
348
+
349
+ .post-nav-prev,
350
+ .post-nav-next {
351
+ flex: 1;
352
+ }
353
+
354
+ .post-nav-next {
355
+ text-align: right;
356
+ }
357
+
358
+ .post-nav-link {
359
+ display: inline-flex;
360
+ flex-direction: column;
361
+ gap: 0.25rem;
362
+ text-decoration: none;
363
+ color: inherit;
364
+ transition: color 0.2s;
365
+ }
366
+
367
+ .post-nav-link:hover {
368
+ color: var(--primary);
369
+ }
370
+
371
+ .post-nav-label {
372
+ font-size: 0.75rem;
373
+ font-weight: 600;
374
+ color: var(--text-muted);
375
+ text-transform: uppercase;
376
+ letter-spacing: 0.05em;
377
+ }
378
+
379
+ .post-nav-title {
380
+ font-size: 0.9375rem;
381
+ font-weight: 500;
382
+ }
383
+
384
+ footer {
385
+ margin-top: 8rem;
386
+ padding: 4rem 1rem;
387
+ text-align: center;
388
+ border-top: 1px solid var(--border);
389
+ font-size: 0.8125rem;
390
+ color: var(--text-muted);
391
+ letter-spacing: 0.05em;
392
+ text-transform: uppercase;
393
+ }
394
+
395
+ footer .build-time {
396
+ margin: 0 0 0.5rem 0;
397
+ font-size: 0.875rem;
398
+ }
399
+
400
+ footer .build-time a {
401
+ color: var(--text-muted);
402
+ }
403
+
404
+ footer .build-time a:hover {
405
+ color: var(--primary);
406
+ }
407
+
408
+ /* Tag badges */
409
+ .tags-wrapper {
410
+ display: flex;
411
+ flex-wrap: wrap;
412
+ gap: 0.5rem;
413
+ margin-top: 0.75rem;
414
+ }
415
+
416
+ .tag-badge {
417
+ display: inline-flex;
418
+ align-items: center;
419
+ font-size: 0.75rem;
420
+ font-weight: 600;
421
+ padding: 0.25rem 0.625rem;
422
+ border-radius: 0.375rem;
423
+ transition: opacity 0.2s;
424
+ }
425
+
426
+ .tag-badge:hover {
427
+ opacity: 0.85;
428
+ }
429
+
430
+ .tag-feature {
431
+ background: #dcfce7;
432
+ color: #166534;
433
+ }
434
+
435
+ .tag-bugfix {
436
+ background: #fee2e2;
437
+ color: #991b1b;
438
+ }
439
+
440
+ .tag-refactor {
441
+ background: #fef3c7;
442
+ color: #92400e;
443
+ }
444
+
445
+ .tag-workspace {
446
+ background: #e5e7eb;
447
+ color: #374151;
448
+ }
449
+
450
+ /* Timeline card tag styling */
451
+ .timeline-card .tags-wrapper {
452
+ margin-top: 0.5rem;
453
+ }
454
+
455
+ .timeline-card:hover .tag-badge {
456
+ opacity: 0.75;
457
+ }
458
+
459
+ /* Detail view tags above story content */
460
+ .content-section > .tags-wrapper:first-child {
461
+ margin-top: 0;
462
+ margin-bottom: 1.5rem;
463
+ }
464
+
465
+ /* Git commit list in Technical Log tab */
466
+ .commit-list {
467
+ margin-top: 2rem;
468
+ border-top: 1px solid var(--border);
469
+ padding-top: 1.5rem;
470
+ }
471
+
472
+ .commit-list-heading {
473
+ font-size: 0.8125rem;
474
+ font-weight: 600;
475
+ text-transform: uppercase;
476
+ letter-spacing: 0.07em;
477
+ color: var(--text-muted);
478
+ margin: 0 0 0.75rem 0;
479
+ }
480
+
481
+ .commit-item {
482
+ display: flex;
483
+ align-items: baseline;
484
+ gap: 0.625rem;
485
+ padding: 0.4rem 0;
486
+ border-bottom: 1px solid var(--border);
487
+ font-size: 0.875rem;
488
+ line-height: 1.5;
489
+ }
490
+
491
+ .commit-item:last-child {
492
+ border-bottom: none;
493
+ }
494
+
495
+ .commit-sha {
496
+ font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, monospace;
497
+ font-size: 0.8125rem;
498
+ color: var(--primary);
499
+ background: color-mix(in srgb, var(--primary) 5%, transparent);
500
+ padding: 0.125rem 0.375rem;
501
+ border-radius: 0.25rem;
502
+ white-space: nowrap;
503
+ flex-shrink: 0;
504
+ }
505
+
506
+ .commit-message {
507
+ flex: 1;
508
+ color: var(--text);
509
+ min-width: 0;
510
+ overflow: hidden;
511
+ text-overflow: ellipsis;
512
+ white-space: nowrap;
513
+ }
514
+
515
+ .commit-time {
516
+ font-size: 0.8125rem;
517
+ color: var(--text-muted);
518
+ white-space: nowrap;
519
+ flex-shrink: 0;
520
+ }
521
+
522
+ /* Standalone git tag items in the timeline (non-clickable) */
523
+ .timeline-item--tag {
524
+ padding-bottom: 3.5rem;
525
+ }
526
+
527
+ .timeline-item--tag:has(+ .timeline-item--tag),
528
+ .timeline-item--tag:has(+ .timeline-item--commit) {
529
+ padding-bottom: 1rem;
530
+ }
531
+
532
+ .timeline-item--tag::before {
533
+ border-color: var(--text-muted);
534
+ width: 7px;
535
+ height: 7px;
536
+ border-width: 2px;
537
+ left: calc(-6px);
538
+ top: 0.5rem;
539
+ }
540
+
541
+ .tag-chip {
542
+ display: inline-flex;
543
+ align-items: baseline;
544
+ gap: 0.375rem;
545
+ font-size: 0.8125rem;
546
+ line-height: 1.4;
547
+ padding: 0.25rem 0.625rem;
548
+ border-radius: 0.375rem;
549
+ background: color-mix(in srgb, var(--primary) 8%, transparent);
550
+ border: 1px solid color-mix(in srgb, var(--primary) 20%, transparent);
551
+ width: fit-content;
552
+ max-width: 100%;
553
+ }
554
+
555
+ .tag-chip-icon {
556
+ flex-shrink: 0;
557
+ font-style: normal;
558
+ }
559
+
560
+ .tag-chip-name {
561
+ font-weight: 600;
562
+ color: var(--primary);
563
+ white-space: nowrap;
564
+ }
565
+
566
+ .tag-chip-date {
567
+ font-size: 0.75rem;
568
+ color: var(--text-muted);
569
+ white-space: nowrap;
570
+ flex-shrink: 0;
571
+ }
572
+
573
+ /* Standalone git commit items in the timeline (non-clickable) */
574
+ .timeline-item--commit {
575
+ padding-bottom: 3.5rem;
576
+ }
577
+
578
+ .timeline-item--commit:has(+ .timeline-item--commit),
579
+ .timeline-item--commit:has(+ .timeline-item--tag) {
580
+ padding-bottom: 0.5rem;
581
+ }
582
+
583
+ .timeline-item--commit::before {
584
+ border-color: var(--text-muted);
585
+ width: 7px;
586
+ height: 7px;
587
+ border-width: 2px;
588
+ left: calc(-6px);
589
+ top: 0.4rem;
590
+ }
591
+
592
+ .commit-chip {
593
+ display: flex;
594
+ align-items: baseline;
595
+ gap: 0.5rem;
596
+ font-size: 0.8125rem;
597
+ line-height: 1.4;
598
+ color: var(--text-muted);
599
+ padding-top: 0.125rem;
600
+ border-radius: 0.375rem;
601
+ width: fit-content;
602
+ max-width: 100%;
603
+ }
604
+
605
+ .commit-chip .commit-sha {
606
+ font-size: 0.75rem;
607
+ flex-shrink: 0;
608
+ }
609
+
610
+ .commit-chip .commit-message {
611
+ color: var(--text-muted);
612
+ font-size: 0.8125rem;
613
+ overflow: hidden;
614
+ text-overflow: ellipsis;
615
+ white-space: nowrap;
616
+ min-width: 0;
617
+ }
618
+
619
+ .commit-chip .commit-time {
620
+ font-size: 0.75rem;
621
+ flex-shrink: 0;
622
+ }
623
+
624
+ /* Images */
625
+ article img {
626
+ max-width: 100%;
627
+ height: auto;
628
+ border: 1px solid var(--border);
629
+ border-radius: 0.5rem;
630
+ margin: 1.5rem 0;
631
+ display: block;
632
+ }
633
+
634
+ article img[src$='.png'],
635
+ article img[src$='.jpg'],
636
+ article img[src$='.jpeg'],
637
+ article img[src$='.gif'],
638
+ article img[src$='.webp'],
639
+ article img[src$='.svg'] {
640
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
641
+ }
@@ -0,0 +1,4 @@
1
+ # Ticket {{id}}: {{title}}
2
+
3
+ ## Requirements
4
+ (Paste Jira/Linear issue description here)
@@ -0,0 +1,8 @@
1
+ /** Format a date value as a relative time string (e.g., "today, May 16, 2026"). */
2
+ export declare function formatRelativeDate(date: string | Date): string;
3
+ /** Format a date value as YYYY-MM-DD. */
4
+ export declare function formatIsoDate(value: string | Date): string;
5
+ /** Get "Month Year" string for a date (e.g., "May 2026"). */
6
+ export declare function getMonthYear(date: string | Date): string;
7
+ /** Get a short "HH:mmh" string for sorting/display based on dateEnd or dateStart. */
8
+ export declare function getSortTime(dateStart: string | Date, dateEnd?: string | Date): string;
@@ -0,0 +1,47 @@
1
+ /** Format a date value as a relative time string (e.g., "today, May 16, 2026"). */
2
+ export function formatRelativeDate(date) {
3
+ const d = new Date(date);
4
+ if (isNaN(d.getTime())) {
5
+ return 'unknown date';
6
+ }
7
+ const now = new Date();
8
+ const diffTime = Math.abs(now.getTime() - d.getTime());
9
+ const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
10
+ const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
11
+ const relative = diffDays === 0 ? 'today' : rtf.format(-diffDays, 'day');
12
+ const absolute = d.toLocaleDateString('en-US', {
13
+ year: 'numeric',
14
+ month: 'long',
15
+ day: 'numeric',
16
+ });
17
+ return `${relative}, ${absolute}`;
18
+ }
19
+ /** Format a date value as YYYY-MM-DD. */
20
+ export function formatIsoDate(value) {
21
+ const d = new Date(value);
22
+ const yyyy = d.getFullYear();
23
+ const mm = String(d.getMonth() + 1).padStart(2, '0');
24
+ const dd = String(d.getDate()).padStart(2, '0');
25
+ return `${yyyy}-${mm}-${dd}`;
26
+ }
27
+ /** Get "Month Year" string for a date (e.g., "May 2026"). */
28
+ export function getMonthYear(date) {
29
+ const d = new Date(date);
30
+ if (isNaN(d.getTime()))
31
+ return 'Unknown Date';
32
+ return d.toLocaleDateString('en-US', {
33
+ year: 'numeric',
34
+ month: 'long',
35
+ });
36
+ }
37
+ /** Get a short "HH:mmh" string for sorting/display based on dateEnd or dateStart. */
38
+ export function getSortTime(dateStart, dateEnd) {
39
+ const dStart = new Date(dateStart);
40
+ const dEnd = dateEnd ? new Date(dateEnd) : null;
41
+ const date = dEnd && !isNaN(dEnd.getTime()) ? dEnd : dStart;
42
+ if (isNaN(date.getTime()))
43
+ return '--:--h';
44
+ const hours = date.getHours().toString().padStart(2, '0');
45
+ const minutes = date.getMinutes().toString().padStart(2, '0');
46
+ return `${hours}:${minutes}h`;
47
+ }
@@ -0,0 +1,13 @@
1
+ export interface EntryMeta {
2
+ slug: string;
3
+ path: string;
4
+ indexPath: string;
5
+ content: string;
6
+ data: Record<string, unknown>;
7
+ hasIndex: boolean;
8
+ }
9
+ /**
10
+ * Reads the logbook directory and returns metadata for all entries.
11
+ * Filters for directories and parses index.md if it exists.
12
+ */
13
+ export declare function getLogbookEntries(logbookDir: string): Promise<EntryMeta[]>;