@grifhinz/logics-manager 2.2.0 → 2.3.1

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 (43) hide show
  1. package/README.md +95 -1
  2. package/VERSION +1 -1
  3. package/clients/README.md +9 -0
  4. package/clients/shared-web/media/css/board.css +658 -0
  5. package/clients/shared-web/media/css/details.css +457 -0
  6. package/clients/shared-web/media/css/layout.css +123 -0
  7. package/clients/shared-web/media/css/toolbar.css +576 -0
  8. package/clients/shared-web/media/harnessApi.js +324 -0
  9. package/clients/shared-web/media/hostApi.js +213 -0
  10. package/clients/shared-web/media/hostApiContract.js +55 -0
  11. package/clients/shared-web/media/icon.png +0 -0
  12. package/clients/shared-web/media/layoutController.js +246 -0
  13. package/clients/shared-web/media/logics.svg +7 -0
  14. package/clients/shared-web/media/logicsModel.js +910 -0
  15. package/clients/shared-web/media/main.css +112 -0
  16. package/clients/shared-web/media/main.js +3 -0
  17. package/clients/shared-web/media/mainApp.js +1005 -0
  18. package/clients/shared-web/media/mainCore.js +604 -0
  19. package/clients/shared-web/media/mainInteractionHandlers.js +324 -0
  20. package/clients/shared-web/media/mainInteractions.js +378 -0
  21. package/clients/shared-web/media/renderBoard.js +3 -0
  22. package/clients/shared-web/media/renderBoardApp.js +1339 -0
  23. package/clients/shared-web/media/renderDetails.js +685 -0
  24. package/clients/shared-web/media/renderMarkdown.js +449 -0
  25. package/clients/shared-web/media/toolsPanelLayout.js +172 -0
  26. package/clients/shared-web/media/uiStatus.js +54 -0
  27. package/clients/shared-web/media/webviewChrome.js +405 -0
  28. package/clients/shared-web/media/webviewPersistence.js +116 -0
  29. package/clients/shared-web/media/webviewSelectors.js +491 -0
  30. package/clients/viewer/README.md +5 -0
  31. package/clients/viewer/browser-host.js +847 -0
  32. package/clients/viewer/index.html +237 -0
  33. package/clients/viewer/viewer.css +433 -0
  34. package/logics_manager/assist.py +9 -142
  35. package/logics_manager/assist_handoff.py +132 -0
  36. package/logics_manager/assist_surface.py +38 -0
  37. package/logics_manager/cli.py +78 -5
  38. package/logics_manager/flow.py +126 -24
  39. package/logics_manager/flow_evidence.py +63 -0
  40. package/logics_manager/update_check.py +138 -0
  41. package/logics_manager/viewer.py +533 -0
  42. package/package.json +12 -6
  43. package/pyproject.toml +1 -1
@@ -0,0 +1,685 @@
1
+ (() => {
2
+ function pencilIcon() {
3
+ return `
4
+ <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
5
+ <path d="M12 20h9" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
6
+ <path
7
+ d="M16.5 3.5a2.1 2.1 0 013 3L8 18l-4 1 1-4 11.5-11.5z"
8
+ fill="none"
9
+ stroke="currentColor"
10
+ stroke-width="2"
11
+ stroke-linecap="round"
12
+ stroke-linejoin="round"
13
+ />
14
+ </svg>
15
+ `;
16
+ }
17
+
18
+ function createChevronIcon() {
19
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
20
+ svg.setAttribute("viewBox", "0 0 24 24");
21
+ svg.setAttribute("aria-hidden", "true");
22
+ svg.setAttribute("focusable", "false");
23
+ svg.classList.add("details__section-chevron");
24
+
25
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
26
+ path.setAttribute("d", "M6 9l6 6 6-6");
27
+ path.setAttribute("fill", "none");
28
+ path.setAttribute("stroke", "currentColor");
29
+ path.setAttribute("stroke-width", "2");
30
+ path.setAttribute("stroke-linecap", "round");
31
+ path.setAttribute("stroke-linejoin", "round");
32
+
33
+ svg.appendChild(path);
34
+ return svg;
35
+ }
36
+
37
+ window.createCdxLogicsDetailsRenderer = function createCdxLogicsDetailsRenderer(options) {
38
+ const {
39
+ detailsBody,
40
+ detailsTitle,
41
+ detailsEyebrow,
42
+ hostApi,
43
+ getItems,
44
+ getSelectedId,
45
+ getActiveWorkspaceRoot,
46
+ getChangedPaths,
47
+ getActiveAgent,
48
+ getLastInjectedContext,
49
+ getCollapsedDetailSections,
50
+ persistState,
51
+ getStageLabel,
52
+ isPrimaryFlowStage,
53
+ collectCompanionDocs,
54
+ collectSpecs,
55
+ collectPrimaryFlowItems,
56
+ getAttentionReasons,
57
+ buildContextPack,
58
+ buildDependencyMap,
59
+ findManagedItemByReference,
60
+ formatDate,
61
+ setLastInjectedContext,
62
+ selectItem
63
+ } = options;
64
+ let contextPackPreview = { id: null, mode: "standard" };
65
+
66
+ function createSectionTitle(label, key) {
67
+ const title = document.createElement("button");
68
+ title.className = "details__section-title";
69
+ title.type = "button";
70
+ title.setAttribute("aria-expanded", "true");
71
+ title.title = `Toggle ${label}`;
72
+ if (key) {
73
+ title.dataset.section = key;
74
+ }
75
+ const text = document.createElement("span");
76
+ text.textContent = label;
77
+ title.appendChild(text);
78
+ title.appendChild(createChevronIcon());
79
+ return title;
80
+ }
81
+
82
+ function createSectionHeader(label, key, addLabel, onAdd) {
83
+ const header = document.createElement("div");
84
+ header.className = "details__section-header";
85
+ const title = createSectionTitle(label, key);
86
+ header.appendChild(title);
87
+ if (addLabel && typeof onAdd === "function") {
88
+ const addButton = document.createElement("button");
89
+ addButton.type = "button";
90
+ addButton.className = "details__section-add";
91
+ addButton.textContent = "+";
92
+ addButton.setAttribute("aria-label", addLabel);
93
+ addButton.title = addLabel;
94
+ addButton.addEventListener("click", (event) => {
95
+ event.stopPropagation();
96
+ onAdd();
97
+ });
98
+ header.appendChild(addButton);
99
+ }
100
+ return { header, title };
101
+ }
102
+
103
+ function createInlineCta(label, onClick, className = "") {
104
+ const button = document.createElement("button");
105
+ button.type = "button";
106
+ button.className = ["details__inline-cta", className].filter(Boolean).join(" ");
107
+ button.textContent = label;
108
+ button.title = label;
109
+ button.addEventListener("click", onClick);
110
+ return button;
111
+ }
112
+
113
+ function createCompanionDocCtas(item, companionDocs) {
114
+ if (!isPrimaryFlowStage(item.stage)) {
115
+ return [];
116
+ }
117
+ const existingStages = new Set(companionDocs.map((companion) => companion.stage));
118
+ const actions = [];
119
+ if (!existingStages.has("product")) {
120
+ actions.push(createInlineCta("+ Product brief", () => hostApi.createCompanionDoc(item.id, "product"), "details__inline-cta--primary"));
121
+ }
122
+ if (!existingStages.has("architecture")) {
123
+ actions.push(
124
+ createInlineCta(
125
+ "+ Architecture decision",
126
+ () => hostApi.createCompanionDoc(item.id, "architecture"),
127
+ "details__inline-cta--primary"
128
+ )
129
+ );
130
+ }
131
+ return actions;
132
+ }
133
+
134
+ function createValueContainer(value) {
135
+ const container = document.createElement("div");
136
+ container.className = "details__indicator-value";
137
+ if (value !== undefined && value !== null && value !== "") {
138
+ const text = document.createElement("div");
139
+ text.className = "details__indicator-text";
140
+ text.textContent = value;
141
+ container.appendChild(text);
142
+ }
143
+ return container;
144
+ }
145
+
146
+ function createIndicatorRow(label, value) {
147
+ const row = document.createElement("div");
148
+ row.className = "details__indicator";
149
+ const left = document.createElement("div");
150
+ left.className = "details__indicator-label";
151
+ left.textContent = label;
152
+ const right = createValueContainer(value ?? "");
153
+ row.appendChild(left);
154
+ row.appendChild(right);
155
+ return row;
156
+ }
157
+
158
+ function appendManagedDocActions(container, targetItem) {
159
+ if (!targetItem) {
160
+ return;
161
+ }
162
+ const actions = document.createElement("div");
163
+ actions.className = "details__indicator-actions";
164
+ actions.appendChild(createInlineCta("Open", () => hostApi.openItem(targetItem, "open")));
165
+ actions.appendChild(createInlineCta("Read", () => hostApi.openItem(targetItem, "read")));
166
+ container.appendChild(actions);
167
+ }
168
+
169
+ function createLinkedIndicatorRow(label, value, targetItem) {
170
+ if (!targetItem) {
171
+ return createIndicatorRow(label, value);
172
+ }
173
+ const row = document.createElement("div");
174
+ row.className = "details__indicator";
175
+ const left = document.createElement("div");
176
+ left.className = "details__indicator-label";
177
+ left.textContent = label;
178
+ const right = createValueContainer(value ?? "");
179
+ appendManagedDocActions(right, targetItem);
180
+ row.appendChild(left);
181
+ row.appendChild(right);
182
+ return row;
183
+ }
184
+
185
+ function createCompanionDocRow(companion) {
186
+ const row = document.createElement("div");
187
+ row.className = "details__indicator";
188
+ const info = document.createElement("div");
189
+ info.className = "details__indicator-label";
190
+ info.textContent = `${getStageLabel(companion.stage)} • ${companion.id}`;
191
+
192
+ const actions = createValueContainer("");
193
+ if (companion.title && companion.title !== companion.relPath && companion.title !== companion.id) {
194
+ const text = document.createElement("div");
195
+ text.className = "details__indicator-text";
196
+ text.textContent = companion.title;
197
+ actions.appendChild(text);
198
+ } else if (companion.relPath) {
199
+ const text = document.createElement("div");
200
+ text.className = "details__indicator-text";
201
+ text.textContent = companion.relPath;
202
+ actions.appendChild(text);
203
+ }
204
+ if (companion.item) {
205
+ actions.replaceChildren();
206
+ const text = document.createElement("div");
207
+ text.className = "details__indicator-text";
208
+ text.textContent = companion.title || companion.relPath || companion.id;
209
+ actions.appendChild(text);
210
+ appendManagedDocActions(actions, companion.item);
211
+ }
212
+
213
+ row.appendChild(info);
214
+ row.appendChild(actions);
215
+ return row;
216
+ }
217
+
218
+ function handleReasonAction(item, reason) {
219
+ const action = reason && reason.remediation ? reason.remediation.action : "";
220
+ if (action === "promote") {
221
+ hostApi.promote(item.id);
222
+ } else if (action === "add-reference") {
223
+ hostApi.addReference(item.id);
224
+ } else if (action === "create-companion-doc") {
225
+ hostApi.createCompanionDoc(item.id);
226
+ }
227
+ }
228
+
229
+ function createReasonCard(item, reason, primary = false) {
230
+ const card = document.createElement("div");
231
+ card.className = `details__reason-card${primary ? " details__reason-card--primary" : ""}`;
232
+
233
+ const badge = document.createElement("span");
234
+ badge.className = "details__reason-badge";
235
+ badge.textContent = primary ? `Primary • ${reason.label}` : reason.label;
236
+ card.appendChild(badge);
237
+
238
+ const description = document.createElement("div");
239
+ description.className = "details__reason-description";
240
+ description.textContent = reason.description;
241
+ card.appendChild(description);
242
+
243
+ if (reason.remediation && reason.remediation.description) {
244
+ const remediation = document.createElement("div");
245
+ remediation.className = "details__reason-remediation";
246
+ remediation.textContent = reason.remediation.description;
247
+ card.appendChild(remediation);
248
+ }
249
+
250
+ if (reason.remediation && reason.remediation.label) {
251
+ if (reason.remediation.action) {
252
+ card.appendChild(
253
+ createInlineCta(reason.remediation.label, () => handleReasonAction(item, reason), "details__inline-cta--primary")
254
+ );
255
+ } else {
256
+ const note = document.createElement("div");
257
+ note.className = "details__reason-remediation";
258
+ note.textContent = reason.remediation.label;
259
+ card.appendChild(note);
260
+ }
261
+ }
262
+
263
+ return card;
264
+ }
265
+
266
+ function createMapNode(targetItem, currentId) {
267
+ const button = document.createElement("button");
268
+ button.type = "button";
269
+ button.className = "details__map-node";
270
+ if (targetItem.id === currentId) {
271
+ button.classList.add("details__map-node--current");
272
+ button.disabled = true;
273
+ }
274
+ button.textContent = `${getStageLabel(targetItem.stage)} • ${targetItem.id}`;
275
+ button.title = targetItem.title;
276
+ if (targetItem.id !== currentId) {
277
+ button.addEventListener("click", () => selectItem(targetItem.id));
278
+ }
279
+ return button;
280
+ }
281
+
282
+ function applySectionCollapse(section, title, content, isCollapsed) {
283
+ section.classList.toggle("details__section--collapsed", isCollapsed);
284
+ title.setAttribute("aria-expanded", String(!isCollapsed));
285
+ if (content) {
286
+ content.setAttribute("aria-hidden", String(isCollapsed));
287
+ }
288
+ }
289
+
290
+ function attachSectionToggle(section, title, content, key) {
291
+ title.addEventListener("click", () => {
292
+ const collapsedSections = getCollapsedDetailSections();
293
+ const isCollapsed = !section.classList.contains("details__section--collapsed");
294
+ applySectionCollapse(section, title, content, isCollapsed);
295
+ if (key) {
296
+ if (isCollapsed) {
297
+ collapsedSections.add(key);
298
+ } else {
299
+ collapsedSections.delete(key);
300
+ }
301
+ persistState();
302
+ }
303
+ });
304
+ }
305
+
306
+ function renderDetails() {
307
+ detailsBody.innerHTML = "";
308
+ const item = getItems().find((entry) => entry.id === getSelectedId());
309
+ if (!item) {
310
+ if (detailsEyebrow) detailsEyebrow.textContent = "Logics item";
311
+ if (detailsTitle) detailsTitle.textContent = "Details";
312
+ const empty = document.createElement("div");
313
+ empty.className = "details__empty";
314
+ empty.textContent = "Select a card to inspect indicators, references, and actions. Use Search or Attention to narrow the workspace first.";
315
+ detailsBody.appendChild(empty);
316
+ return;
317
+ }
318
+
319
+ if (detailsEyebrow) {
320
+ const headerBits = [getStageLabel(item.stage)];
321
+ const status = item.indicators && item.indicators.Status ? String(item.indicators.Status).trim() : "";
322
+ if (status) headerBits.push(status);
323
+ detailsEyebrow.textContent = headerBits.join(" • ");
324
+ }
325
+ if (detailsTitle) {
326
+ detailsTitle.innerHTML = "";
327
+ const titleLine = document.createElement("div");
328
+ titleLine.className = "details__header-title-main";
329
+ titleLine.textContent = item.title;
330
+ detailsTitle.appendChild(titleLine);
331
+
332
+ const filePath = String(item?.relPath || item?.path || item?.id || "").trim();
333
+ if (filePath) {
334
+ const fileLine = document.createElement("div");
335
+ fileLine.className = "details__header-title-file";
336
+ fileLine.textContent = `File: ${filePath}`;
337
+ detailsTitle.appendChild(fileLine);
338
+ }
339
+ }
340
+
341
+ const list = document.createElement("div");
342
+ list.className = "details__list";
343
+
344
+ const nameRow = document.createElement("div");
345
+ nameRow.className = "details__list-row details__list-row--name";
346
+ const nameLabel = document.createElement("span");
347
+ nameLabel.textContent = "Name";
348
+ nameRow.appendChild(nameLabel);
349
+
350
+ const nameValueWrap = document.createElement("span");
351
+ nameValueWrap.className = "details__name-value-wrap";
352
+ const nameValue = document.createElement("span");
353
+ nameValue.className = "details__name-value";
354
+ nameValue.textContent = item.id;
355
+ nameValueWrap.appendChild(nameValue);
356
+
357
+ const renameButton = document.createElement("button");
358
+ renameButton.type = "button";
359
+ renameButton.className = "details__rename";
360
+ renameButton.setAttribute("aria-label", "Rename entry");
361
+ renameButton.title = "Rename entry";
362
+ renameButton.innerHTML = pencilIcon();
363
+ renameButton.addEventListener("click", () => hostApi.renameEntry(item.id));
364
+ nameValueWrap.appendChild(renameButton);
365
+ nameRow.appendChild(nameValueWrap);
366
+ list.appendChild(nameRow);
367
+
368
+ const updatedRow = document.createElement("div");
369
+ updatedRow.className = "details__list-row";
370
+ const updatedLabel = document.createElement("span");
371
+ updatedLabel.textContent = "Updated:";
372
+ updatedRow.appendChild(updatedLabel);
373
+ const updatedValue = document.createElement("span");
374
+ updatedValue.className = "details__list-value";
375
+ updatedValue.textContent = formatDate(item.updatedAt);
376
+ updatedRow.appendChild(updatedValue);
377
+ list.appendChild(updatedRow);
378
+ detailsBody.appendChild(list);
379
+
380
+ const indicators = item.indicators || {};
381
+ const indicatorKeys = Object.keys(indicators).filter((key) => key.toLowerCase() !== "reminder");
382
+ if (indicatorKeys.length) {
383
+ const section = document.createElement("div");
384
+ section.className = "details__section";
385
+ const indicatorKey = "indicators";
386
+ const sectionHeader = createSectionHeader("Indicators", indicatorKey);
387
+ const indicatorList = document.createElement("div");
388
+ indicatorList.className = "details__indicators";
389
+ indicatorKeys.forEach((key) => indicatorList.appendChild(createIndicatorRow(key, indicators[key])));
390
+ section.appendChild(sectionHeader.header);
391
+ section.appendChild(indicatorList);
392
+ applySectionCollapse(section, sectionHeader.title, indicatorList, getCollapsedDetailSections().has(indicatorKey));
393
+ attachSectionToggle(section, sectionHeader.title, indicatorList, indicatorKey);
394
+ detailsBody.appendChild(section);
395
+ }
396
+
397
+ const attentionReasons = typeof getAttentionReasons === "function" ? getAttentionReasons(item) : [];
398
+ if (attentionReasons.length) {
399
+ const attentionSection = document.createElement("div");
400
+ attentionSection.className = "details__section";
401
+ const attentionKey = "attentionExplain";
402
+ const attentionHeader = createSectionHeader("Attention explain", attentionKey);
403
+ const attentionContent = document.createElement("div");
404
+ attentionContent.className = "details__indicators";
405
+ attentionContent.appendChild(createReasonCard(item, attentionReasons[0], true));
406
+ attentionReasons.slice(1).forEach((reason) => attentionContent.appendChild(createReasonCard(item, reason)));
407
+ attentionSection.appendChild(attentionHeader.header);
408
+ attentionSection.appendChild(attentionContent);
409
+ applySectionCollapse(attentionSection, attentionHeader.title, attentionContent, getCollapsedDetailSections().has(attentionKey));
410
+ attachSectionToggle(attentionSection, attentionHeader.title, attentionContent, attentionKey);
411
+ detailsBody.appendChild(attentionSection);
412
+ }
413
+
414
+ const activeWorkspaceRoot = typeof getActiveWorkspaceRoot === "function" ? getActiveWorkspaceRoot() : null;
415
+ const previewMode = contextPackPreview.id === item.id ? contextPackPreview.mode : "standard";
416
+ const contextPack = typeof buildContextPack === "function" ? buildContextPack(item, { mode: previewMode }) : null;
417
+ if (contextPack) {
418
+ const contextSection = document.createElement("div");
419
+ contextSection.className = "details__section";
420
+ const contextKey = "contextPack";
421
+ const contextHeader = createSectionHeader("Context pack for AI assistants", contextKey);
422
+ const contextContent = document.createElement("div");
423
+ contextContent.className = "details__indicators";
424
+
425
+ const summary = document.createElement("div");
426
+ summary.className = "details__reason-description";
427
+ summary.textContent = `Mode ${String(contextPack.summary.mode || "standard")} • Profile ${String(contextPack.summary.profile || "normal")} • ${contextPack.summary.docCount} docs • ~${contextPack.summary.tokenEstimate} tokens (${contextPack.summary.budgetLabel}).`;
428
+ contextContent.appendChild(summary);
429
+
430
+ const estimateList = document.createElement("div");
431
+ estimateList.className = "details__indicators";
432
+ estimateList.appendChild(createIndicatorRow("Task type", contextPack.summary.taskKind || "default"));
433
+ estimateList.appendChild(createIndicatorRow("Response", contextPack.summary.responseContract || "Keep the answer concise."));
434
+ estimateList.appendChild(createIndicatorRow("Docs", String(contextPack.summary.docCount || 0)));
435
+ estimateList.appendChild(createIndicatorRow("Lines", String(contextPack.summary.lineCount || 0)));
436
+ estimateList.appendChild(createIndicatorRow("Characters", String(contextPack.summary.charCount || 0)));
437
+ estimateList.appendChild(createIndicatorRow("Changed paths", String(contextPack.summary.changedPathCount || 0)));
438
+ if (contextPack.summary.excludedStaleCount) {
439
+ estimateList.appendChild(createIndicatorRow("Stale excluded", String(contextPack.summary.excludedStaleCount)));
440
+ }
441
+ if (contextPack.summary.blockedDocCount) {
442
+ estimateList.appendChild(createIndicatorRow("Agent filtered", String(contextPack.summary.blockedDocCount)));
443
+ }
444
+ contextContent.appendChild(estimateList);
445
+
446
+ if (contextPack.summary.trimmed) {
447
+ const trimmed = document.createElement("div");
448
+ trimmed.className = "details__reason-remediation";
449
+ trimmed.textContent = "The preview is trimmed to keep the pack compact and predictable before injection.";
450
+ contextContent.appendChild(trimmed);
451
+ }
452
+
453
+ if (contextPack.sessionHygiene) {
454
+ const hygiene = document.createElement("div");
455
+ hygiene.className = "details__reason-remediation";
456
+ hygiene.textContent = contextPack.sessionHygiene;
457
+ contextContent.appendChild(hygiene);
458
+ }
459
+
460
+ const previewToolbar = document.createElement("div");
461
+ previewToolbar.className = "details__pack-toolbar";
462
+ const previewActions = [
463
+ { label: "Preview standard", mode: "standard" },
464
+ { label: "Preview summary-only", mode: "summary-only" }
465
+ ];
466
+ const changedPaths = typeof getChangedPaths === "function" ? getChangedPaths() : [];
467
+ if (Array.isArray(changedPaths) && changedPaths.length > 0) {
468
+ previewActions.push({ label: "Preview diff-first", mode: "diff-first" });
469
+ }
470
+ previewActions.forEach((action) => {
471
+ const isActive = contextPackPreview.id === item.id && contextPackPreview.mode === action.mode;
472
+ previewToolbar.appendChild(
473
+ createInlineCta(
474
+ isActive ? `Hide ${action.mode}` : action.label,
475
+ () => {
476
+ contextPackPreview = isActive ? { id: null, mode: "standard" } : { id: item.id, mode: action.mode };
477
+ renderDetails();
478
+ },
479
+ isActive ? "details__inline-cta--primary" : ""
480
+ )
481
+ );
482
+ });
483
+ contextContent.appendChild(previewToolbar);
484
+
485
+ if (contextPackPreview.id === item.id) {
486
+ const preview = document.createElement("pre");
487
+ preview.className = "details__pack-preview";
488
+ preview.textContent = contextPack.text;
489
+ contextContent.appendChild(preview);
490
+
491
+ const injectToolbar = document.createElement("div");
492
+ injectToolbar.className = "details__pack-toolbar";
493
+ injectToolbar.appendChild(
494
+ createInlineCta(
495
+ "Copy for assistant",
496
+ () => {
497
+ if (typeof setLastInjectedContext === "function") {
498
+ setLastInjectedContext({
499
+ itemId: item.id,
500
+ mode: contextPack.summary.mode,
501
+ taskKind: contextPack.summary.taskKind,
502
+ root: activeWorkspaceRoot
503
+ });
504
+ }
505
+ hostApi.injectPrompt(contextPack.text);
506
+ },
507
+ "details__inline-cta--primary"
508
+ )
509
+ );
510
+ injectToolbar.appendChild(
511
+ createInlineCta("Copy for new assistant session", () => {
512
+ if (typeof setLastInjectedContext === "function") {
513
+ setLastInjectedContext({
514
+ itemId: item.id,
515
+ mode: contextPack.summary.mode,
516
+ taskKind: contextPack.summary.taskKind,
517
+ root: activeWorkspaceRoot
518
+ });
519
+ }
520
+ hostApi.injectPrompt(contextPack.text, { preferNewThread: true });
521
+ })
522
+ );
523
+ contextContent.appendChild(injectToolbar);
524
+ }
525
+
526
+ contextSection.appendChild(contextHeader.header);
527
+ contextSection.appendChild(contextContent);
528
+ applySectionCollapse(contextSection, contextHeader.title, contextContent, getCollapsedDetailSections().has(contextKey));
529
+ attachSectionToggle(contextSection, contextHeader.title, contextContent, contextKey);
530
+ detailsBody.appendChild(contextSection);
531
+ }
532
+
533
+ const dependencyMap = typeof buildDependencyMap === "function" ? buildDependencyMap(item) : null;
534
+ if (dependencyMap && dependencyMap.groups && dependencyMap.groups.length) {
535
+ const mapSection = document.createElement("div");
536
+ mapSection.className = "details__section";
537
+ const mapKey = "dependencyMap";
538
+ const mapHeader = createSectionHeader("Dependency map", mapKey);
539
+ const mapContent = document.createElement("div");
540
+ mapContent.className = "details__dependency-map";
541
+ dependencyMap.groups.forEach((group) => {
542
+ const groupEl = document.createElement("div");
543
+ groupEl.className = "details__map-group";
544
+ const label = document.createElement("div");
545
+ label.className = "details__map-group-label";
546
+ label.textContent = group.label;
547
+ groupEl.appendChild(label);
548
+ const nodes = document.createElement("div");
549
+ nodes.className = "details__map-nodes";
550
+ group.items.forEach((targetItem) => nodes.appendChild(createMapNode(targetItem, item.id)));
551
+ groupEl.appendChild(nodes);
552
+ mapContent.appendChild(groupEl);
553
+ });
554
+ mapSection.appendChild(mapHeader.header);
555
+ mapSection.appendChild(mapContent);
556
+ applySectionCollapse(mapSection, mapHeader.title, mapContent, getCollapsedDetailSections().has(mapKey));
557
+ attachSectionToggle(mapSection, mapHeader.title, mapContent, mapKey);
558
+ detailsBody.appendChild(mapSection);
559
+ }
560
+
561
+ const companionDocs = collectCompanionDocs(item);
562
+ if (isPrimaryFlowStage(item.stage) || companionDocs.length) {
563
+ const companionSection = document.createElement("div");
564
+ companionSection.className = "details__section";
565
+ const companionKey = "companionDocs";
566
+ const companionHeader = createSectionHeader("Companion docs", companionKey, "Create companion doc", () => hostApi.createCompanionDoc(item.id));
567
+ const companionList = document.createElement("div");
568
+ companionList.className = "details__indicators";
569
+ if (companionDocs.length) {
570
+ companionDocs.forEach((companion) => companionList.appendChild(createCompanionDocRow(companion)));
571
+ } else {
572
+ const empty = document.createElement("div");
573
+ empty.className = "details__empty";
574
+ empty.textContent = "No companion docs linked yet.";
575
+ companionList.appendChild(empty);
576
+ companionList.appendChild(createInlineCta("+ Create companion doc", () => hostApi.createCompanionDoc(item.id)));
577
+ }
578
+ createCompanionDocCtas(item, companionDocs).forEach((cta) => companionList.appendChild(cta));
579
+ companionSection.appendChild(companionHeader.header);
580
+ companionSection.appendChild(companionList);
581
+ applySectionCollapse(companionSection, companionHeader.title, companionList, getCollapsedDetailSections().has(companionKey));
582
+ attachSectionToggle(companionSection, companionHeader.title, companionList, companionKey);
583
+ detailsBody.appendChild(companionSection);
584
+ }
585
+
586
+ const specs = collectSpecs(item);
587
+ if (isPrimaryFlowStage(item.stage) || specs.length) {
588
+ const specsSection = document.createElement("div");
589
+ specsSection.className = "details__section";
590
+ const specsKey = "specs";
591
+ const specsHeader = createSectionHeader("Specs", specsKey);
592
+ const specsList = document.createElement("div");
593
+ specsList.className = "details__indicators";
594
+ if (specs.length) {
595
+ specs.forEach((spec) => specsList.appendChild(createLinkedIndicatorRow(`${getStageLabel(spec.stage)} • ${spec.id}`, spec.title, spec)));
596
+ } else {
597
+ const empty = document.createElement("div");
598
+ empty.className = "details__empty";
599
+ empty.textContent = "No spec linked yet.";
600
+ specsList.appendChild(empty);
601
+ }
602
+ specsSection.appendChild(specsHeader.header);
603
+ specsSection.appendChild(specsList);
604
+ applySectionCollapse(specsSection, specsHeader.title, specsList, getCollapsedDetailSections().has(specsKey));
605
+ attachSectionToggle(specsSection, specsHeader.title, specsList, specsKey);
606
+ detailsBody.appendChild(specsSection);
607
+ }
608
+
609
+ const primaryFlowItems = collectPrimaryFlowItems(item);
610
+ if (!isPrimaryFlowStage(item.stage)) {
611
+ const primaryFlowSection = document.createElement("div");
612
+ primaryFlowSection.className = "details__section";
613
+ const primaryFlowKey = "primaryFlow";
614
+ const primaryFlowHeader = createSectionHeader("Primary flow", primaryFlowKey);
615
+ const primaryFlowList = document.createElement("div");
616
+ primaryFlowList.className = "details__indicators";
617
+ if (primaryFlowItems.length) {
618
+ primaryFlowItems.forEach((linkedItem) => {
619
+ primaryFlowList.appendChild(
620
+ createLinkedIndicatorRow(`${getStageLabel(linkedItem.stage)} • ${linkedItem.id}`, linkedItem.title, linkedItem)
621
+ );
622
+ });
623
+ } else {
624
+ const empty = document.createElement("div");
625
+ empty.className = "details__empty";
626
+ empty.textContent = "No primary workflow item linked yet.";
627
+ primaryFlowList.appendChild(empty);
628
+ primaryFlowList.appendChild(
629
+ createInlineCta("+ Link to primary flow", () => hostApi.addReference(item.id), "details__inline-cta--primary")
630
+ );
631
+ }
632
+ primaryFlowSection.appendChild(primaryFlowHeader.header);
633
+ primaryFlowSection.appendChild(primaryFlowList);
634
+ applySectionCollapse(primaryFlowSection, primaryFlowHeader.title, primaryFlowList, getCollapsedDetailSections().has(primaryFlowKey));
635
+ attachSectionToggle(primaryFlowSection, primaryFlowHeader.title, primaryFlowList, primaryFlowKey);
636
+ detailsBody.appendChild(primaryFlowSection);
637
+ }
638
+
639
+ const refSection = document.createElement("div");
640
+ refSection.className = "details__section";
641
+ const refKey = "references";
642
+ const refHeader = createSectionHeader("References", refKey, "Add reference", () => hostApi.addReference(item.id));
643
+ const refList = document.createElement("div");
644
+ refList.className = "details__indicators";
645
+ if (item.references && item.references.length) {
646
+ item.references.forEach((ref) => {
647
+ if (typeof ref === "string") {
648
+ refList.appendChild(createLinkedIndicatorRow(ref, "", findManagedItemByReference(ref)));
649
+ } else {
650
+ refList.appendChild(createLinkedIndicatorRow(ref.label, ref.path, findManagedItemByReference(ref.path)));
651
+ }
652
+ });
653
+ } else {
654
+ refList.appendChild(createInlineCta("+ Add reference", () => hostApi.addReference(item.id)));
655
+ }
656
+ refSection.appendChild(refHeader.header);
657
+ refSection.appendChild(refList);
658
+ applySectionCollapse(refSection, refHeader.title, refList, getCollapsedDetailSections().has(refKey));
659
+ attachSectionToggle(refSection, refHeader.title, refList, refKey);
660
+ detailsBody.appendChild(refSection);
661
+
662
+ const usedSection = document.createElement("div");
663
+ usedSection.className = "details__section";
664
+ const usedKey = "usedBy";
665
+ const usedHeader = createSectionHeader("Used by", usedKey, "Add used-by link", () => hostApi.addUsedBy(item.id));
666
+ const usedList = document.createElement("div");
667
+ usedList.className = "details__indicators";
668
+ if (item.usedBy && item.usedBy.length) {
669
+ item.usedBy.forEach((usage) => {
670
+ const targetItem = findManagedItemByReference(usage.relPath || usage.id, usage);
671
+ usedList.appendChild(createLinkedIndicatorRow(`${getStageLabel(usage.stage)} • ${usage.id}`, usage.title, targetItem));
672
+ });
673
+ } else {
674
+ usedList.appendChild(createInlineCta("+ Add used by", () => hostApi.addUsedBy(item.id)));
675
+ }
676
+ usedSection.appendChild(usedHeader.header);
677
+ usedSection.appendChild(usedList);
678
+ applySectionCollapse(usedSection, usedHeader.title, usedList, getCollapsedDetailSections().has(usedKey));
679
+ attachSectionToggle(usedSection, usedHeader.title, usedList, usedKey);
680
+ detailsBody.appendChild(usedSection);
681
+ }
682
+
683
+ return { renderDetails };
684
+ };
685
+ })();