@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,431 @@
1
+ <script lang="ts">
2
+ import { AlertTriangle, Archive, ArrowLeft, Blocks, Clock3, FolderKanban, GitBranchPlus, Link2, NotebookText, Target } from "@lucide/svelte";
3
+ import DocumentBody from "../../../lib/components/DocumentBody.svelte";
4
+ import type { TaskDetailResult } from "../../../../../src/services/details.ts";
5
+
6
+ let { data } = $props();
7
+ const detail = $derived(data.detail as TaskDetailResult);
8
+ </script>
9
+
10
+ <svelte:head>
11
+ <title>{detail.found ? `${detail.task.id} · ${detail.task.title}` : `Missing task · ${detail.id}`}</title>
12
+ </svelte:head>
13
+
14
+ {#if !detail.found}
15
+ <section class="missing-shell">
16
+ <a class="back-link" href="/tasks">
17
+ <ArrowLeft size={14} strokeWidth={1.9} />
18
+ Back to Tasks
19
+ </a>
20
+ <p class="eyebrow">Deterministic fallback</p>
21
+ <h2>Task {detail.id} was not found</h2>
22
+ <p class="intro">The requested task does not exist in the current managed memory. Pick one of the known IDs below.</p>
23
+ <div class="chip-row">
24
+ {#each detail.availableIds as taskId}
25
+ <a class="chip" href={`/tasks/${taskId}`}>{taskId}</a>
26
+ {/each}
27
+ </div>
28
+ </section>
29
+ {:else}
30
+ <section class="detail-shell">
31
+ <div class="detail-header">
32
+ <div class="header-copy">
33
+ <a class="back-link" href="/tasks">
34
+ <ArrowLeft size={14} strokeWidth={1.9} />
35
+ Back to Tasks
36
+ </a>
37
+ <p class="eyebrow">Task detail</p>
38
+ <div class="title-row">
39
+ <div>
40
+ <p class="item-id">{detail.task.id}</p>
41
+ <h2>{detail.task.title}</h2>
42
+ </div>
43
+ <span class="status-pill">{detail.task.status.replaceAll("_", " ")}</span>
44
+ </div>
45
+ <p class="intro">{detail.task.summary}</p>
46
+ </div>
47
+
48
+ <div class="meta-panel">
49
+ <div class="meta-grid">
50
+ <div>
51
+ <span>Priority</span>
52
+ <strong>p{detail.task.priority}</strong>
53
+ </div>
54
+ <div>
55
+ <span>Dependencies</span>
56
+ <strong>{detail.dependencies.length}</strong>
57
+ </div>
58
+ <div>
59
+ <span>Current state</span>
60
+ <strong>{detail.currentState.isActiveTask ? "Active" : detail.currentState.isBlockedTask ? "Blocked" : "Inactive"}</strong>
61
+ </div>
62
+ </div>
63
+ <div class="chip-row">
64
+ {#each detail.task.tags as tag}
65
+ <span class="chip">{tag}</span>
66
+ {/each}
67
+ {#if detail.task.ownerRole}
68
+ <span class="chip">owner {detail.task.ownerRole}</span>
69
+ {/if}
70
+ {#if detail.task.createdAt}
71
+ <span class="chip">created {detail.task.createdAt.slice(0, 10)}</span>
72
+ {/if}
73
+ {#if detail.task.archivedAt}
74
+ <span class="chip chip-warning">
75
+ <Archive size={12} strokeWidth={1.9} />
76
+ archived
77
+ </span>
78
+ {/if}
79
+ </div>
80
+ </div>
81
+ </div>
82
+
83
+ <section class="detail-grid">
84
+ <article class="panel">
85
+ <div class="panel-head">
86
+ <div class="panel-title">
87
+ <Link2 size={15} strokeWidth={1.9} />
88
+ <h3>Dependencies and blockers</h3>
89
+ </div>
90
+ </div>
91
+
92
+ <div class="state-stack">
93
+ <div class="state-card">
94
+ <div class="panel-title">
95
+ <GitBranchPlus size={14} strokeWidth={1.9} />
96
+ <strong>Declared dependencies</strong>
97
+ </div>
98
+ {#if detail.dependencies.length === 0}
99
+ <p class="empty">This task has no declared dependencies.</p>
100
+ {:else}
101
+ <div class="linked-list">
102
+ {#each detail.dependencies as dependency}
103
+ <a class="linked-card" href={`/tasks/${dependency.id}`}>
104
+ <p class="linked-id">{dependency.id}</p>
105
+ <h4>{dependency.title}</h4>
106
+ <p>{dependency.summary}</p>
107
+ </a>
108
+ {/each}
109
+ </div>
110
+ {/if}
111
+ </div>
112
+
113
+ <div class="state-card">
114
+ <div class="panel-title">
115
+ <AlertTriangle size={14} strokeWidth={1.9} />
116
+ <strong>Unmet dependencies</strong>
117
+ </div>
118
+ {#if detail.unmetDependencyIds.length === 0}
119
+ <p class="empty">No unmet dependencies right now.</p>
120
+ {:else}
121
+ <div class="chip-row">
122
+ {#each detail.unmetDependencyIds as dependencyId}
123
+ <span class="chip chip-warning">{dependencyId}</span>
124
+ {/each}
125
+ </div>
126
+ {/if}
127
+ </div>
128
+
129
+ <div class="state-card">
130
+ <div class="panel-title">
131
+ <Blocks size={14} strokeWidth={1.9} />
132
+ <strong>Downstream tasks waiting here</strong>
133
+ </div>
134
+ {#if detail.blockingTasks.length === 0}
135
+ <p class="empty">No downstream tasks currently depend on this task.</p>
136
+ {:else}
137
+ <div class="linked-list">
138
+ {#each detail.blockingTasks as blockingTask}
139
+ <a class="linked-card" href={`/tasks/${blockingTask.id}`}>
140
+ <p class="linked-id">{blockingTask.id}</p>
141
+ <h4>{blockingTask.title}</h4>
142
+ <p>{blockingTask.summary}</p>
143
+ </a>
144
+ {/each}
145
+ </div>
146
+ {/if}
147
+ </div>
148
+ </div>
149
+ </article>
150
+
151
+ <article class="panel">
152
+ <div class="panel-head">
153
+ <div class="panel-title">
154
+ <FolderKanban size={15} strokeWidth={1.9} />
155
+ <h3>Context and links</h3>
156
+ </div>
157
+ </div>
158
+
159
+ <div class="state-stack">
160
+ <div class="state-card">
161
+ <div class="panel-title">
162
+ <Target size={14} strokeWidth={1.9} />
163
+ <strong>Current state</strong>
164
+ </div>
165
+ <p>{detail.currentState.focus}</p>
166
+ <div class="chip-row">
167
+ <span class={`chip ${detail.currentState.isActiveTask ? "chip-accent" : ""}`}>
168
+ {detail.currentState.isActiveTask ? "Active task" : "Not active"}
169
+ </span>
170
+ {#if detail.currentState.isBlockedTask}
171
+ <span class="chip chip-warning">Blocked in current state</span>
172
+ {/if}
173
+ </div>
174
+ </div>
175
+
176
+ <div class="state-card">
177
+ <div class="panel-title">
178
+ <NotebookText size={14} strokeWidth={1.9} />
179
+ <strong>Linked specs</strong>
180
+ </div>
181
+ <div class="chip-row">
182
+ {#each detail.linkedSpecs as spec}
183
+ <a class="chip" href={`/specs/${spec.id}`}>{spec.id}</a>
184
+ {/each}
185
+ </div>
186
+ </div>
187
+
188
+ <div class="state-card">
189
+ <div class="panel-title">
190
+ <Clock3 size={14} strokeWidth={1.9} />
191
+ <strong>Lifecycle</strong>
192
+ </div>
193
+ <div class="meta-list">
194
+ {#if detail.task.completedAt}
195
+ <span>completed {detail.task.completedAt}</span>
196
+ {/if}
197
+ {#if detail.task.cancelledAt}
198
+ <span>cancelled {detail.task.cancelledAt}</span>
199
+ {/if}
200
+ {#if detail.task.archivedAt}
201
+ <span>archived {detail.task.archivedAt}</span>
202
+ {/if}
203
+ {#if detail.task.archiveReason}
204
+ <span>archive reason {detail.task.archiveReason}</span>
205
+ {/if}
206
+ {#if !detail.task.completedAt && !detail.task.cancelledAt && !detail.task.archivedAt}
207
+ <span>No closed-state lifecycle timestamps yet.</span>
208
+ {/if}
209
+ </div>
210
+ </div>
211
+
212
+ <div class="state-card">
213
+ <div class="panel-title">
214
+ <NotebookText size={14} strokeWidth={1.9} />
215
+ <strong>Related handoffs</strong>
216
+ </div>
217
+ {#if detail.relatedHandoffs.length === 0}
218
+ <p class="empty">No related handoffs were found for this task yet.</p>
219
+ {:else}
220
+ <div class="handoff-list">
221
+ {#each detail.relatedHandoffs as handoff}
222
+ <div class="handoff-card">
223
+ <p class="linked-id">{handoff.id}</p>
224
+ <strong>{handoff.title}</strong>
225
+ <p>{handoff.summary}</p>
226
+ <small>{handoff.createdAt}</small>
227
+ </div>
228
+ {/each}
229
+ </div>
230
+ {/if}
231
+ </div>
232
+ </div>
233
+ </article>
234
+ </section>
235
+
236
+ <article class="panel panel-body">
237
+ <div class="panel-head">
238
+ <div class="panel-title">
239
+ <NotebookText size={15} strokeWidth={1.9} />
240
+ <h3>Source narrative</h3>
241
+ </div>
242
+ </div>
243
+ <DocumentBody sections={detail.bodySections} emptyLabel="This task does not have narrative sections yet." />
244
+ </article>
245
+ </section>
246
+ {/if}
247
+
248
+ <style>
249
+ .eyebrow,
250
+ .item-id,
251
+ .linked-id {
252
+ margin: 0 0 0.32rem;
253
+ text-transform: uppercase;
254
+ letter-spacing: 0.14em;
255
+ font-size: 0.7rem;
256
+ color: var(--muted);
257
+ }
258
+
259
+ h2,
260
+ h3,
261
+ h4,
262
+ p {
263
+ margin: 0;
264
+ }
265
+
266
+ h2,
267
+ h3,
268
+ h4 {
269
+ font-family: var(--display-font);
270
+ }
271
+
272
+ h2 {
273
+ font-size: clamp(1.8rem, 2.4vw, 2.6rem);
274
+ }
275
+
276
+ h3 {
277
+ font-size: 1.1rem;
278
+ }
279
+
280
+ h4 {
281
+ font-size: 1rem;
282
+ }
283
+
284
+ .intro,
285
+ .panel p,
286
+ .panel small,
287
+ .empty,
288
+ .meta-list {
289
+ color: var(--muted);
290
+ line-height: 1.5;
291
+ }
292
+
293
+ .detail-shell,
294
+ .missing-shell,
295
+ .detail-grid,
296
+ .meta-panel,
297
+ .state-stack,
298
+ .linked-list,
299
+ .handoff-list,
300
+ .meta-list {
301
+ display: grid;
302
+ gap: 1rem;
303
+ }
304
+
305
+ .detail-header {
306
+ display: grid;
307
+ grid-template-columns: minmax(0, 1.7fr) minmax(18rem, 0.9fr);
308
+ gap: 1rem;
309
+ margin-bottom: 1rem;
310
+ }
311
+
312
+ .header-copy {
313
+ display: grid;
314
+ gap: 0.7rem;
315
+ }
316
+
317
+ .back-link,
318
+ .linked-card,
319
+ .chip {
320
+ text-decoration: none;
321
+ }
322
+
323
+ .back-link {
324
+ display: inline-flex;
325
+ align-items: center;
326
+ gap: 0.42rem;
327
+ width: fit-content;
328
+ color: var(--muted-soft);
329
+ }
330
+
331
+ .title-row,
332
+ .panel-head,
333
+ .panel-title,
334
+ .chip-row,
335
+ .meta-grid {
336
+ display: flex;
337
+ gap: 0.6rem;
338
+ flex-wrap: wrap;
339
+ align-items: center;
340
+ }
341
+
342
+ .title-row,
343
+ .meta-grid {
344
+ justify-content: space-between;
345
+ }
346
+
347
+ .meta-panel,
348
+ .panel {
349
+ padding: 1rem;
350
+ border-radius: var(--radius-xl);
351
+ border: 1px solid var(--line);
352
+ background: var(--panel);
353
+ box-shadow: var(--shadow);
354
+ }
355
+
356
+ .meta-grid div {
357
+ display: grid;
358
+ gap: 0.18rem;
359
+ min-width: 8rem;
360
+ }
361
+
362
+ .meta-grid span {
363
+ font-size: 0.76rem;
364
+ color: var(--muted);
365
+ }
366
+
367
+ .meta-grid strong {
368
+ font-family: var(--display-font);
369
+ font-size: 1.3rem;
370
+ }
371
+
372
+ .status-pill,
373
+ .chip {
374
+ display: inline-flex;
375
+ align-items: center;
376
+ gap: 0.32rem;
377
+ border-radius: 999px;
378
+ padding: 0.34rem 0.7rem;
379
+ border: 1px solid var(--line);
380
+ background: rgba(255, 255, 255, 0.88);
381
+ box-shadow: var(--shadow-soft);
382
+ font-size: 0.77rem;
383
+ }
384
+
385
+ .chip-accent {
386
+ background: rgba(15, 141, 96, 0.12);
387
+ border-color: rgba(15, 141, 96, 0.22);
388
+ }
389
+
390
+ .chip-warning {
391
+ background: rgba(143, 84, 33, 0.12);
392
+ border-color: rgba(143, 84, 33, 0.22);
393
+ }
394
+
395
+ .detail-grid {
396
+ grid-template-columns: repeat(2, minmax(0, 1fr));
397
+ margin-bottom: 1rem;
398
+ }
399
+
400
+ .panel {
401
+ gap: 0.9rem;
402
+ }
403
+
404
+ .linked-card,
405
+ .state-card,
406
+ .handoff-card {
407
+ display: grid;
408
+ gap: 0.65rem;
409
+ padding: 0.9rem;
410
+ border-radius: var(--radius-lg);
411
+ border: 1px solid var(--line);
412
+ background: rgba(255, 255, 255, 0.82);
413
+ box-shadow: var(--shadow-soft);
414
+ }
415
+
416
+ .linked-card:hover {
417
+ border-color: rgba(15, 141, 96, 0.18);
418
+ transform: translateY(-1px);
419
+ }
420
+
421
+ .meta-list {
422
+ gap: 0.45rem;
423
+ }
424
+
425
+ @media (max-width: 980px) {
426
+ .detail-header,
427
+ .detail-grid {
428
+ grid-template-columns: 1fr;
429
+ }
430
+ }
431
+ </style>