@leonarto/spec-embryo 0.1.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 (62) hide show
  1. package/README.md +156 -0
  2. package/package.json +48 -0
  3. package/src/backends/base.ts +18 -0
  4. package/src/backends/deterministic.ts +105 -0
  5. package/src/backends/index.ts +26 -0
  6. package/src/backends/prompt.ts +169 -0
  7. package/src/backends/subprocess.ts +198 -0
  8. package/src/cli.ts +111 -0
  9. package/src/commands/agents.ts +16 -0
  10. package/src/commands/current.ts +95 -0
  11. package/src/commands/doctor.ts +12 -0
  12. package/src/commands/handoff.ts +64 -0
  13. package/src/commands/init.ts +101 -0
  14. package/src/commands/reshape.ts +20 -0
  15. package/src/commands/resume.ts +19 -0
  16. package/src/commands/spec.ts +108 -0
  17. package/src/commands/status.ts +98 -0
  18. package/src/commands/task.ts +190 -0
  19. package/src/commands/ui.ts +35 -0
  20. package/src/domain.ts +357 -0
  21. package/src/engine.ts +290 -0
  22. package/src/frontmatter.ts +83 -0
  23. package/src/index.ts +75 -0
  24. package/src/paths.ts +32 -0
  25. package/src/repository.ts +807 -0
  26. package/src/services/adoption.ts +169 -0
  27. package/src/services/agents.ts +191 -0
  28. package/src/services/dashboard.ts +776 -0
  29. package/src/services/details.ts +453 -0
  30. package/src/services/doctor.ts +452 -0
  31. package/src/services/layout.ts +420 -0
  32. package/src/services/spec-answer-evaluation.ts +103 -0
  33. package/src/services/spec-import.ts +217 -0
  34. package/src/services/spec-questions.ts +343 -0
  35. package/src/services/ui.ts +34 -0
  36. package/src/storage.ts +57 -0
  37. package/src/templates.ts +270 -0
  38. package/tsconfig.json +17 -0
  39. package/web/package.json +24 -0
  40. package/web/src/app.css +83 -0
  41. package/web/src/app.d.ts +6 -0
  42. package/web/src/app.html +11 -0
  43. package/web/src/lib/components/AnalysisFilters.svelte +293 -0
  44. package/web/src/lib/components/DocumentBody.svelte +100 -0
  45. package/web/src/lib/components/MultiSelectDropdown.svelte +280 -0
  46. package/web/src/lib/components/SelectDropdown.svelte +265 -0
  47. package/web/src/lib/server/project-root.ts +34 -0
  48. package/web/src/lib/task-board.ts +20 -0
  49. package/web/src/routes/+layout.server.ts +57 -0
  50. package/web/src/routes/+layout.svelte +421 -0
  51. package/web/src/routes/+layout.ts +1 -0
  52. package/web/src/routes/+page.svelte +530 -0
  53. package/web/src/routes/specs/+page.svelte +416 -0
  54. package/web/src/routes/specs/[specId]/+page.server.ts +81 -0
  55. package/web/src/routes/specs/[specId]/+page.svelte +675 -0
  56. package/web/src/routes/tasks/+page.svelte +341 -0
  57. package/web/src/routes/tasks/[taskId]/+page.server.ts +12 -0
  58. package/web/src/routes/tasks/[taskId]/+page.svelte +431 -0
  59. package/web/src/routes/timeline/+page.svelte +1093 -0
  60. package/web/svelte.config.js +10 -0
  61. package/web/tsconfig.json +9 -0
  62. package/web/vite.config.ts +11 -0
@@ -0,0 +1,530 @@
1
+ <script lang="ts">
2
+ import {
3
+ ArrowRight,
4
+ Blocks,
5
+ ChartNoAxesGantt,
6
+ Clock3,
7
+ FolderKanban,
8
+ GitBranch,
9
+ NotebookText,
10
+ ShieldAlert,
11
+ ShieldCheck,
12
+ Sparkles,
13
+ Target,
14
+ } from "@lucide/svelte";
15
+ import type { DashboardData } from "../../../src/services/dashboard.ts";
16
+
17
+ let { data } = $props();
18
+ const dashboard = $derived(data.dashboard as DashboardData);
19
+ </script>
20
+
21
+ <svelte:head>
22
+ <title>{dashboard.project.name} Dash</title>
23
+ </svelte:head>
24
+
25
+ <section class="hero-grid">
26
+ <article class="hero-card">
27
+ <div class="hero-head">
28
+ <div>
29
+ <p class="eyebrow">Workspace overview</p>
30
+ <h3>Dash</h3>
31
+ </div>
32
+ <span class="icon-seal">
33
+ <Sparkles size={18} strokeWidth={1.9} />
34
+ </span>
35
+ </div>
36
+
37
+ <p class="hero-copy">{dashboard.currentState.summary}</p>
38
+
39
+ <div class="chip-row">
40
+ <span class="chip chip-accent">
41
+ <NotebookText size={13} strokeWidth={1.9} />
42
+ {dashboard.project.memoryDir}
43
+ </span>
44
+ {#if dashboard.git.available}
45
+ <span class="chip">
46
+ <GitBranch size={13} strokeWidth={1.9} />
47
+ {dashboard.git.branch ?? "unknown"} · {dashboard.git.dirty ? "dirty" : "clean"}
48
+ </span>
49
+ {/if}
50
+ <span class="chip">
51
+ <NotebookText size={13} strokeWidth={1.9} />
52
+ {dashboard.counts.specs} specs
53
+ </span>
54
+ <span class="chip">
55
+ <FolderKanban size={13} strokeWidth={1.9} />
56
+ {dashboard.counts.tasks} tasks
57
+ </span>
58
+ </div>
59
+ </article>
60
+
61
+ <article class="stats-card">
62
+ <div class="stats-grid">
63
+ <div>
64
+ <span>Active specs</span>
65
+ <strong>{dashboard.counts.activeSpecs}</strong>
66
+ </div>
67
+ <div>
68
+ <span>In progress</span>
69
+ <strong>{dashboard.counts.inProgressTasks}</strong>
70
+ </div>
71
+ <div>
72
+ <span>Blocked</span>
73
+ <strong>{dashboard.counts.blockedTasks}</strong>
74
+ </div>
75
+ <div>
76
+ <span>Review</span>
77
+ <strong>{dashboard.counts.reviewTasks}</strong>
78
+ </div>
79
+ <div>
80
+ <span>Ready</span>
81
+ <strong>{dashboard.counts.readyTasks}</strong>
82
+ </div>
83
+ </div>
84
+ </article>
85
+ </section>
86
+
87
+ <section class="dashboard-grid">
88
+ <article class="panel panel-wide">
89
+ <div class="panel-head">
90
+ <div class="panel-title">
91
+ <span class="icon-seal icon-soft">
92
+ <Target size={16} strokeWidth={1.9} />
93
+ </span>
94
+ <div>
95
+ <p class="eyebrow">Current state</p>
96
+ <h3>Focus and next actions</h3>
97
+ </div>
98
+ </div>
99
+ </div>
100
+
101
+ <div class="focus-grid">
102
+ <div class="focus-card">
103
+ <div class="mini-head">
104
+ <Target size={14} strokeWidth={1.9} />
105
+ <h4>Focus</h4>
106
+ </div>
107
+ <p>{dashboard.currentState.focus}</p>
108
+ </div>
109
+ <div class="focus-card">
110
+ <div class="mini-head">
111
+ <NotebookText size={14} strokeWidth={1.9} />
112
+ <h4>Active specs</h4>
113
+ </div>
114
+ <div class="chip-row">
115
+ {#each dashboard.currentState.activeSpecIds as specId}
116
+ <a class="chip" href={`/specs/${specId}`}>{specId}</a>
117
+ {/each}
118
+ </div>
119
+ </div>
120
+ <div class="focus-card">
121
+ <div class="mini-head">
122
+ <FolderKanban size={14} strokeWidth={1.9} />
123
+ <h4>Active tasks</h4>
124
+ </div>
125
+ <div class="chip-row">
126
+ {#each dashboard.currentState.activeTaskIds as taskId}
127
+ <a class="chip" href={`/tasks/${taskId}`}>{taskId}</a>
128
+ {/each}
129
+ </div>
130
+ </div>
131
+ </div>
132
+
133
+ <ol class="action-list">
134
+ {#each dashboard.nextActions as action}
135
+ <li>
136
+ <ArrowRight size={14} strokeWidth={2} />
137
+ <span>{action}</span>
138
+ </li>
139
+ {/each}
140
+ </ol>
141
+ </article>
142
+
143
+ <article class="panel">
144
+ <div class="panel-head">
145
+ <div class="panel-title">
146
+ <span class="icon-seal icon-soft">
147
+ {#if dashboard.agents.ok}
148
+ <ShieldCheck size={16} strokeWidth={1.9} />
149
+ {:else}
150
+ <ShieldAlert size={16} strokeWidth={1.9} />
151
+ {/if}
152
+ </span>
153
+ <div>
154
+ <p class="eyebrow">Agents</p>
155
+ <h3>AGENTS health</h3>
156
+ </div>
157
+ </div>
158
+ </div>
159
+ <div class="health-card {dashboard.agents.ok ? 'ok' : 'warn'}">
160
+ <strong>{dashboard.agents.ok ? "Healthy" : "Needs attention"}</strong>
161
+ <p>max file {dashboard.agents.maxFileTokens} · max chain {dashboard.agents.maxChainTokens}</p>
162
+ </div>
163
+ {#if dashboard.agents.failures.length > 0}
164
+ <ul class="notes-list">
165
+ {#each dashboard.agents.failures as failure}
166
+ <li>{failure}</li>
167
+ {/each}
168
+ </ul>
169
+ {/if}
170
+ </article>
171
+
172
+ <article class="panel">
173
+ <div class="panel-head">
174
+ <div class="panel-title">
175
+ <span class="icon-seal icon-soft">
176
+ <Clock3 size={16} strokeWidth={1.9} />
177
+ </span>
178
+ <div>
179
+ <p class="eyebrow">Resume</p>
180
+ <h3>Recent handoff</h3>
181
+ </div>
182
+ </div>
183
+ </div>
184
+ {#if dashboard.recentHandoff}
185
+ <div class="focus-card">
186
+ <div class="mini-head">
187
+ <Clock3 size={14} strokeWidth={1.9} />
188
+ <h4>{dashboard.recentHandoff.id}</h4>
189
+ </div>
190
+ <p>{dashboard.recentHandoff.title}</p>
191
+ <small>{dashboard.recentHandoff.createdAt}</small>
192
+ </div>
193
+ {:else}
194
+ <p class="empty">No handoff exists yet.</p>
195
+ {/if}
196
+ </article>
197
+
198
+ <article class="panel">
199
+ <div class="panel-head">
200
+ <div class="panel-title">
201
+ <span class="icon-seal icon-soft">
202
+ <Blocks size={16} strokeWidth={1.9} />
203
+ </span>
204
+ <div>
205
+ <p class="eyebrow">Execution</p>
206
+ <h3>Blocked work</h3>
207
+ </div>
208
+ </div>
209
+ </div>
210
+ {#if dashboard.blockedTasks.length === 0}
211
+ <p class="empty">No blocked tasks right now.</p>
212
+ {:else}
213
+ <ul class="notes-list">
214
+ {#each dashboard.blockedTasks as task}
215
+ <li><a href={`/tasks/${task.id}`}>{task.id} · {task.title}</a></li>
216
+ {/each}
217
+ </ul>
218
+ {/if}
219
+ </article>
220
+
221
+ <article class="panel">
222
+ <div class="panel-head">
223
+ <div class="panel-title">
224
+ <span class="icon-seal icon-soft">
225
+ <ChartNoAxesGantt size={16} strokeWidth={1.9} />
226
+ </span>
227
+ <div>
228
+ <p class="eyebrow">Execution</p>
229
+ <h3>Critical path</h3>
230
+ </div>
231
+ </div>
232
+ </div>
233
+ <ul class="notes-list">
234
+ {#each dashboard.criticalPath as task}
235
+ <li><a href={`/tasks/${task.id}`}>{task.id} · {task.title}</a></li>
236
+ {/each}
237
+ </ul>
238
+ </article>
239
+
240
+ <article class="panel panel-wide">
241
+ <div class="panel-head">
242
+ <div class="panel-title">
243
+ <span class="icon-seal icon-soft">
244
+ <Sparkles size={16} strokeWidth={1.9} />
245
+ </span>
246
+ <div>
247
+ <p class="eyebrow">What does not fit elsewhere</p>
248
+ <h3>Signals and notes</h3>
249
+ </div>
250
+ </div>
251
+ </div>
252
+
253
+ <div class="signal-grid">
254
+ <div class="focus-card">
255
+ <div class="mini-head">
256
+ <ArrowRight size={14} strokeWidth={2} />
257
+ <h4>Current-state hints</h4>
258
+ </div>
259
+ <ul class="notes-list">
260
+ {#each dashboard.currentState.nextActionHints as hint}
261
+ <li>{hint}</li>
262
+ {/each}
263
+ </ul>
264
+ </div>
265
+
266
+ <div class="focus-card">
267
+ <div class="mini-head">
268
+ <Blocks size={14} strokeWidth={1.9} />
269
+ <h4>Dependency issues</h4>
270
+ </div>
271
+ {#if dashboard.dependencyIssues.length === 0}
272
+ <p>No missing dependencies detected.</p>
273
+ {:else}
274
+ <ul class="notes-list">
275
+ {#each dashboard.dependencyIssues as issue}
276
+ <li>{issue.taskId} references missing {issue.missingTaskId}</li>
277
+ {/each}
278
+ </ul>
279
+ {/if}
280
+ </div>
281
+
282
+ <div class="focus-card">
283
+ <div class="mini-head">
284
+ <NotebookText size={14} strokeWidth={1.9} />
285
+ <h4>Spec coverage snapshot</h4>
286
+ </div>
287
+ <ul class="notes-list">
288
+ {#each dashboard.specs as spec}
289
+ <li><a href={`/specs/${spec.id}`}>{spec.id} · {spec.linkedTaskCount} linked tasks</a></li>
290
+ {/each}
291
+ </ul>
292
+ </div>
293
+ </div>
294
+ </article>
295
+ </section>
296
+
297
+ <style>
298
+ .eyebrow {
299
+ margin: 0 0 0.35rem;
300
+ text-transform: uppercase;
301
+ letter-spacing: 0.14em;
302
+ font-size: 0.68rem;
303
+ color: var(--muted);
304
+ }
305
+
306
+ h3,
307
+ h4 {
308
+ margin: 0;
309
+ font-family: var(--display-font);
310
+ }
311
+
312
+ h3 {
313
+ font-size: clamp(1.55rem, 1.8vw, 2rem);
314
+ }
315
+
316
+ h4 {
317
+ font-size: 1.02rem;
318
+ }
319
+
320
+ .hero-grid,
321
+ .dashboard-grid,
322
+ .focus-grid,
323
+ .signal-grid,
324
+ .stats-grid,
325
+ .chip-row {
326
+ display: grid;
327
+ gap: 0.9rem;
328
+ }
329
+
330
+ .hero-grid {
331
+ grid-template-columns: 1.45fr 1fr;
332
+ margin-bottom: 1rem;
333
+ }
334
+
335
+ .hero-card,
336
+ .stats-card,
337
+ .panel,
338
+ .focus-card {
339
+ border-radius: var(--radius-xl);
340
+ border: 1px solid var(--line);
341
+ background: var(--panel);
342
+ box-shadow: var(--shadow);
343
+ }
344
+
345
+ .hero-card,
346
+ .stats-card,
347
+ .panel {
348
+ padding: 1rem;
349
+ }
350
+
351
+ .hero-head,
352
+ .panel-head,
353
+ .panel-title,
354
+ .mini-head {
355
+ display: flex;
356
+ align-items: center;
357
+ }
358
+
359
+ .hero-head,
360
+ .panel-head {
361
+ justify-content: space-between;
362
+ gap: 0.8rem;
363
+ }
364
+
365
+ .panel-title,
366
+ .mini-head {
367
+ gap: 0.55rem;
368
+ }
369
+
370
+ .icon-seal {
371
+ display: inline-flex;
372
+ align-items: center;
373
+ justify-content: center;
374
+ width: 2.2rem;
375
+ height: 2.2rem;
376
+ border-radius: 0.95rem;
377
+ border: 1px solid var(--line);
378
+ background: rgba(255, 255, 255, 0.84);
379
+ color: var(--muted-soft);
380
+ flex-shrink: 0;
381
+ }
382
+
383
+ .icon-soft {
384
+ width: 2rem;
385
+ height: 2rem;
386
+ background: rgba(255, 255, 255, 0.72);
387
+ }
388
+
389
+ .hero-copy,
390
+ .focus-card p,
391
+ .empty,
392
+ .notes-list,
393
+ .action-list,
394
+ .health-card p,
395
+ .focus-card small,
396
+ .stats-card span {
397
+ color: var(--muted);
398
+ line-height: 1.45;
399
+ }
400
+
401
+ .chip-row {
402
+ grid-template-columns: repeat(auto-fit, minmax(8rem, max-content));
403
+ align-items: start;
404
+ }
405
+
406
+ .chip {
407
+ display: inline-flex;
408
+ align-items: center;
409
+ gap: 0.35rem;
410
+ border-radius: 999px;
411
+ padding: 0.34rem 0.65rem;
412
+ border: 1px solid var(--line);
413
+ background: rgba(255, 255, 255, 0.9);
414
+ font-size: 0.75rem;
415
+ box-shadow: var(--shadow-soft);
416
+ }
417
+
418
+ .chip-accent {
419
+ background: var(--accent-soft);
420
+ }
421
+
422
+ .stats-card {
423
+ background: linear-gradient(180deg, var(--panel-strong), rgba(248, 242, 233, 0.92));
424
+ }
425
+
426
+ .stats-grid {
427
+ grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
428
+ }
429
+
430
+ .stats-grid div {
431
+ padding: 0.8rem;
432
+ border-radius: var(--radius-lg);
433
+ border: 1px solid var(--line);
434
+ background: rgba(255, 255, 255, 0.78);
435
+ }
436
+
437
+ .stats-grid strong {
438
+ display: block;
439
+ margin-top: 0.2rem;
440
+ font-family: var(--display-font);
441
+ font-size: 1.55rem;
442
+ }
443
+
444
+ .dashboard-grid {
445
+ grid-template-columns: repeat(3, minmax(0, 1fr));
446
+ }
447
+
448
+ .panel-wide {
449
+ grid-column: span 2;
450
+ }
451
+
452
+ .focus-grid,
453
+ .signal-grid {
454
+ grid-template-columns: repeat(3, minmax(0, 1fr));
455
+ }
456
+
457
+ .focus-card {
458
+ display: grid;
459
+ gap: 0.75rem;
460
+ padding: 0.9rem;
461
+ background: rgba(255, 255, 255, 0.58);
462
+ box-shadow: var(--shadow-soft);
463
+ }
464
+
465
+ .action-list,
466
+ .notes-list {
467
+ margin: 0;
468
+ padding-left: 0;
469
+ list-style: none;
470
+ }
471
+
472
+ .action-list {
473
+ display: grid;
474
+ gap: 0.7rem;
475
+ margin-top: 0.95rem;
476
+ }
477
+
478
+ .action-list li {
479
+ display: grid;
480
+ grid-template-columns: auto 1fr;
481
+ gap: 0.55rem;
482
+ align-items: start;
483
+ padding: 0.75rem 0.8rem;
484
+ border-radius: var(--radius-lg);
485
+ border: 1px solid var(--line);
486
+ background: rgba(255, 255, 255, 0.62);
487
+ box-shadow: var(--shadow-soft);
488
+ }
489
+
490
+ .notes-list {
491
+ display: grid;
492
+ gap: 0.55rem;
493
+ }
494
+
495
+ .notes-list li {
496
+ padding: 0.7rem 0.78rem;
497
+ border-radius: var(--radius-lg);
498
+ border: 1px solid rgba(24, 22, 18, 0.08);
499
+ background: rgba(255, 255, 255, 0.52);
500
+ }
501
+
502
+ .health-card {
503
+ padding: 0.9rem;
504
+ border-radius: var(--radius-lg);
505
+ border: 1px solid var(--line);
506
+ background: rgba(255, 255, 255, 0.76);
507
+ box-shadow: var(--shadow-soft);
508
+ }
509
+
510
+ .health-card.ok {
511
+ background: var(--accent-soft);
512
+ }
513
+
514
+ .health-card.warn {
515
+ background: var(--warning-soft);
516
+ }
517
+
518
+ @media (max-width: 1040px) {
519
+ .hero-grid,
520
+ .dashboard-grid,
521
+ .focus-grid,
522
+ .signal-grid {
523
+ grid-template-columns: 1fr;
524
+ }
525
+
526
+ .panel-wide {
527
+ grid-column: span 1;
528
+ }
529
+ }
530
+ </style>