codebase-onboarder 0.7.0 → 1.0.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 (139) hide show
  1. package/README.md +140 -12
  2. package/bin/onboarder.js +0 -0
  3. package/cli/agent/command.d.ts +71 -0
  4. package/cli/agent/command.js +177 -0
  5. package/cli/agent/command.js.map +1 -0
  6. package/cli/agent/config.d.ts +31 -0
  7. package/cli/agent/config.js +98 -0
  8. package/cli/agent/config.js.map +1 -0
  9. package/cli/agent/contracts.d.ts +77 -0
  10. package/cli/agent/contracts.js +2 -0
  11. package/cli/agent/contracts.js.map +1 -0
  12. package/cli/agent/files.d.ts +19 -0
  13. package/cli/agent/files.js +109 -0
  14. package/cli/agent/files.js.map +1 -0
  15. package/cli/agent/gitSource.d.ts +18 -0
  16. package/cli/agent/gitSource.js +69 -0
  17. package/cli/agent/gitSource.js.map +1 -0
  18. package/cli/agent/github.d.ts +21 -0
  19. package/cli/agent/github.js +94 -0
  20. package/cli/agent/github.js.map +1 -0
  21. package/cli/agent/mcp.d.ts +15 -0
  22. package/cli/agent/mcp.js +78 -0
  23. package/cli/agent/mcp.js.map +1 -0
  24. package/cli/agent/process.d.ts +18 -0
  25. package/cli/agent/process.js +118 -0
  26. package/cli/agent/process.js.map +1 -0
  27. package/cli/agent/protocol.d.ts +3 -0
  28. package/cli/agent/protocol.js +26 -0
  29. package/cli/agent/protocol.js.map +1 -0
  30. package/cli/agent/runner.d.ts +34 -0
  31. package/cli/agent/runner.js +152 -0
  32. package/cli/agent/runner.js.map +1 -0
  33. package/cli/agent/stdio.d.ts +1 -0
  34. package/cli/agent/stdio.js +62 -0
  35. package/cli/agent/stdio.js.map +1 -0
  36. package/cli/agent/tools.d.ts +7 -0
  37. package/cli/agent/tools.js +225 -0
  38. package/cli/agent/tools.js.map +1 -0
  39. package/cli/chat/analysis.d.ts +26 -0
  40. package/cli/chat/analysis.js +188 -0
  41. package/cli/chat/analysis.js.map +1 -0
  42. package/cli/chat/app.d.ts +16 -0
  43. package/cli/chat/app.js +363 -0
  44. package/cli/chat/app.js.map +1 -0
  45. package/cli/chat/commands.d.ts +11 -0
  46. package/cli/chat/commands.js +122 -0
  47. package/cli/chat/commands.js.map +1 -0
  48. package/cli/chat/composer.d.ts +75 -0
  49. package/cli/chat/composer.js +467 -0
  50. package/cli/chat/composer.js.map +1 -0
  51. package/cli/chat/controller.d.ts +64 -0
  52. package/cli/chat/controller.js +402 -0
  53. package/cli/chat/controller.js.map +1 -0
  54. package/cli/chat/logo.d.ts +3 -0
  55. package/cli/chat/logo.js +14 -0
  56. package/cli/chat/logo.js.map +1 -0
  57. package/cli/chat/models.d.ts +18 -0
  58. package/cli/chat/models.js +88 -0
  59. package/cli/chat/models.js.map +1 -0
  60. package/cli/chat/profile.d.ts +5 -0
  61. package/cli/chat/profile.js +98 -0
  62. package/cli/chat/profile.js.map +1 -0
  63. package/cli/chat/render.d.ts +40 -0
  64. package/cli/chat/render.js +259 -0
  65. package/cli/chat/render.js.map +1 -0
  66. package/cli/chat/screen.d.ts +26 -0
  67. package/cli/chat/screen.js +117 -0
  68. package/cli/chat/screen.js.map +1 -0
  69. package/cli/chat/source.d.ts +29 -0
  70. package/cli/chat/source.js +116 -0
  71. package/cli/chat/source.js.map +1 -0
  72. package/cli/chat/store.d.ts +43 -0
  73. package/cli/chat/store.js +94 -0
  74. package/cli/chat/store.js.map +1 -0
  75. package/cli/chat/text.d.ts +4 -0
  76. package/cli/chat/text.js +27 -0
  77. package/cli/chat/text.js.map +1 -0
  78. package/cli/explorer/app.d.ts +1 -0
  79. package/cli/explorer/app.js +20 -1
  80. package/cli/explorer/app.js.map +1 -1
  81. package/cli/explorer/commands.js +5 -0
  82. package/cli/explorer/commands.js.map +1 -1
  83. package/cli/explorer/featureViews.d.ts +1 -0
  84. package/cli/explorer/featureViews.js +4 -0
  85. package/cli/explorer/featureViews.js.map +1 -1
  86. package/cli/main.js +47 -13
  87. package/cli/main.js.map +1 -1
  88. package/docs/HERMES_RESEARCH.md +51 -0
  89. package/package.json +2 -1
  90. package/public/app.js +98 -24
  91. package/public/app.js.map +1 -1
  92. package/public/assets/onboarder-mark.svg +11 -0
  93. package/public/browser.d.ts +3 -0
  94. package/public/browser.js.map +1 -1
  95. package/public/index.html +24 -7
  96. package/public/js/about.js +2 -2
  97. package/public/js/about.js.map +1 -1
  98. package/public/js/aiDraft.d.ts +1 -0
  99. package/public/js/aiDraft.js +66 -17
  100. package/public/js/aiDraft.js.map +1 -1
  101. package/public/js/api.d.ts +8 -5
  102. package/public/js/api.js +45 -22
  103. package/public/js/api.js.map +1 -1
  104. package/public/js/deepAnalysisView.js +2 -2
  105. package/public/js/deepAnalysisView.js.map +1 -1
  106. package/public/js/diagramPane.d.ts +1 -0
  107. package/public/js/diagramPane.js +5 -0
  108. package/public/js/diagramPane.js.map +1 -1
  109. package/public/js/docsView.js +6 -6
  110. package/public/js/docsView.js.map +1 -1
  111. package/public/js/dom.d.ts +8 -0
  112. package/public/js/dom.js.map +1 -1
  113. package/public/js/llm.d.ts +3 -1
  114. package/public/js/llm.js +15 -8
  115. package/public/js/llm.js.map +1 -1
  116. package/public/js/repoFiles.js +1 -1
  117. package/public/js/repoFiles.js.map +1 -1
  118. package/public/js/reviewView.js +1 -1
  119. package/public/js/reviewView.js.map +1 -1
  120. package/public/styles.css +25 -0
  121. package/server/gitDiff.d.ts +1 -1
  122. package/server/hermesChat.d.ts +13 -0
  123. package/server/hermesChat.js +145 -0
  124. package/server/hermesChat.js.map +1 -0
  125. package/server/http.d.ts +5 -4
  126. package/server/http.js +0 -4
  127. package/server/http.js.map +1 -1
  128. package/server/mcp/server.d.ts +1 -1
  129. package/server/mcp/server.js +2 -2
  130. package/server/mcp/server.js.map +1 -1
  131. package/server/router.js +7 -3
  132. package/server/router.js.map +1 -1
  133. package/server/tools/scan.js +16 -2
  134. package/server/tools/scan.js.map +1 -1
  135. package/server/tools.d.ts +1 -0
  136. package/server/tools.js +46 -6
  137. package/server/tools.js.map +1 -1
  138. package/shared/diagram/aiMermaid.js +16 -13
  139. package/shared/diagram/aiMermaid.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"docsView.js","sourceRoot":"","sources":["../../src/public/js/docsView.ts"],"names":[],"mappings":"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","sourcesContent":["import { eventElement } from '/js/dom.js';\n// The Docs tab: a written account of the repo.\n//\n// Three layers, in order of how much they cost. The overview page and the\n// folder-by-folder sections are written from the import graph and need no\n// network at all. Pressing \"Generate documentation\" replaces those sentences\n// with a model's, streamed in place. Opening a topic from the contents adds a\n// tab that documents one file or folder in depth, and the bar at the bottom\n// answers questions scoped to whichever of those is on screen.\n//\n// The static sentences live in `/shared/analyzer/docs.js` — pure, and tested.\n// What is here is the DOM: tabs, streaming, and the wiring between them.\n\nimport { parseFolderDoc, fileFactsLine, fileStaticDoc, folderStaticDoc, docFileRow } from '/shared/analyzer/docs.js';\nimport { explainOverview } from '/shared/analyzer/explainLocal.js';\nimport { scanIndex } from '/shared/analyzer/graph.js';\nimport { escapeHtml } from './html.js';\nimport { mdLite, mdRender } from './markdown.js';\nimport { state } from './state.js';\nimport { streamExplain } from './api.js';\nimport { findNode, countFiles } from './tree.js';\nimport { readRepoFile, gatherFileContext } from './repoFiles.js';\nimport * as llm from './llm.js';\n\nlet host = null;\nlet hooks = {\n onToast: (..._args: unknown[]) => {},\n onOpenSettings: (..._args: unknown[]) => {},\n onOpenFile: (..._args: unknown[]) => {},\n repoOverview: () => '',\n};\n\nexport function initDocs(options) {\n host = options.host;\n hooks = { ...hooks, ...options };\n}\n\n// Facts about one file or folder, bound to the loaded repo. Every prompt and\n// every static sentence on this page goes through these two, so the doc tab and\n// the folder pass can never describe the same file differently.\nconst factsLine = (path) => fileFactsLine(path, state.scan, state.facts);\nconst staticDoc = (path) => fileStaticDoc(path, state.scan, state.facts);\nconst fileRow = (path) => docFileRow(path, state.scan, state.facts);\n\n// ---- the page ---------------------------------------------------------------\n\n// One-time document build per scan; AI briefs update rows in place afterwards.\nexport function renderDocs() {\n if (state.docs.rendered) return;\n state.docs.rendered = true;\n\n const tabsEl = document.createElement('div');\n tabsEl.className = 'doc-tabs';\n const wrapEl = document.createElement('div');\n const scrollEl = document.createElement('div');\n scrollEl.className = 'doc-scroll';\n scrollEl.append(tabsEl, wrapEl);\n\n // Answers stream into this rail, above the bar, only once there's something\n // to show.\n const railEl = document.createElement('div');\n railEl.className = 'doc-qa-rail';\n railEl.hidden = true;\n\n // The sticky bottom bar: questions about whatever doc is on screen, with\n // clear/close once a conversation is underway.\n const barEl = document.createElement('form');\n barEl.className = 'doc-askbar';\n barEl.innerHTML = `\n <input type=\"text\" id=\"docAskInput\" class=\"text-input\" placeholder=\"Ask me anything about this doc…\" spellcheck=\"false\" autocomplete=\"off\">\n <button type=\"submit\" class=\"btn btn-ink\">Ask</button>`;\n const clearBtn = document.createElement('button');\n clearBtn.type = 'button';\n clearBtn.className = 'btn btn-sm btn-ghost doc-clear';\n clearBtn.textContent = 'clear';\n clearBtn.title = 'Clear the conversation';\n clearBtn.hidden = true;\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'btn btn-sm btn-ghost doc-close';\n closeBtn.textContent = 'close';\n closeBtn.title = 'Close the conversation';\n closeBtn.hidden = true;\n clearBtn.addEventListener('click', () => { railEl.innerHTML = ''; });\n closeBtn.addEventListener('click', () => {\n railEl.hidden = true;\n clearBtn.hidden = true;\n closeBtn.hidden = true;\n });\n barEl.append(clearBtn, closeBtn);\n barEl.addEventListener('submit', (event) => {\n event.preventDefault();\n askAboutRepo();\n });\n\n host.innerHTML = '';\n host.append(scrollEl, railEl, barEl);\n state.docs.tabsEl = tabsEl;\n state.docs.wrapEl = wrapEl;\n state.docs.railEl = railEl;\n state.docs.bar = barEl;\n state.docs.pageEl = buildOverviewPage();\n\n renderDocStrip();\n showActiveDoc();\n loadReadme();\n}\n\n// The landing tab: repo header, topic index, README, the documentation zone\n// (filled on demand by the AI), and the question box at the bottom.\nfunction buildOverviewPage() {\n const { scan, facts, manifest } = state;\n const root = state.treeData;\n\n const page = document.createElement('div');\n page.className = 'doc-page';\n page.innerHTML = `\n <div id=\"doc-top\">\n <h1 class=\"doc-title\">${escapeHtml(scan.name)}</h1>\n <p class=\"doc-sub\">${escapeHtml(scan.root)}</p>\n <p class=\"doc-sub\">${scan.stats.filesParsed} code files · ${scan.stats.edgeCount} connections · scanned in ${scan.stats.tookMs} ms</p>\n <p class=\"doc-ai-note\" id=\"docAiNote\">Pick a topic in the contents for a detailed AI document. “Generate documentation” writes the full folder-by-folder write-up below.</p>\n <p class=\"doc-overview\" id=\"docOverview\">${escapeHtml(explainOverview(scan, facts, manifest).replace(/[*`]/g, ''))}</p>\n </div>\n <nav class=\"doc-toc\" id=\"docToc\"></nav>\n <h2 class=\"doc-section-h doc-anchor\" id=\"doc-readme-h\">In its own words</h2>\n <div class=\"doc-readme\" id=\"docReadme\"><span class=\"doc-readme-none\">Looking for a README…</span></div>\n <h2 class=\"doc-section-h doc-anchor\" id=\"doc-layout-h\">The documentation</h2>\n <div id=\"docGenZone\">\n <p class=\"doc-gen-empty\">Nothing written yet. Press the button and the AI will document this repo folder by folder, right here.</p>\n <button class=\"btn btn-ink\" id=\"docGenBtn\">Generate documentation</button>\n </div>\n `;\n\n buildToc(page, root);\n\n page.addEventListener('click', (event) => {\n const b = eventElement(event)?.closest<HTMLElement>('[data-doc-open]');\n if (!b) return;\n const raw = b.dataset.docOpen;\n const i = raw.indexOf(':');\n openDocTab(raw.slice(0, i), raw.slice(i + 1));\n });\n page.querySelector<HTMLButtonElement>('#docGenBtn').addEventListener('click', writeDocsWithAI);\n\n return page;\n}\n\n// The contents index: page anchors up top, then every folder and file as a\n// topic — clicking any of them opens its detailed AI tab.\nfunction buildToc(page, root) {\n const toc = page.querySelector('#docToc');\n const bits = [\n `<span class=\"doc-toc-h\">Get documentation about a topic</span>`,\n `<a href=\"#doc-top\">Overview</a>`,\n `<a href=\"#doc-readme-h\">In its own words</a>`,\n `<span class=\"doc-toc-group\">Folders and files — click for the AI document</span>`,\n ];\n let folderCount = 0;\n const parsedSet = new Set(state.scan.files.map((f) => f.path));\n const walk = (node, depth) => {\n if (folderCount >= 30) return;\n folderCount++;\n const indent = Math.min(depth, 3);\n bits.push(`<button class=\"doc-toc-link doc-toc-folder doc-indent-${indent}\" data-doc-open=\"folder:${escapeHtml(node.path)}\">${escapeHtml(node.path ? node.name + '/' : '(repo root)')}</button>`);\n const files = node.files.filter((f) => parsedSet.has(f)).slice(0, 10);\n if (files.length) {\n const links = files\n .map((f) => `<button class=\"doc-toc-link\" data-doc-open=\"file:${escapeHtml(f)}\">${escapeHtml(f.split('/').pop())}</button>`)\n .join('<span class=\"doc-toc-sep\"> · </span>');\n bits.push(`<span class=\"doc-toc-files doc-indent-${indent}\">${links}${node.files.length > files.length ? ' …' : ''}</span>`);\n }\n for (const child of [...node.dirs.values()].sort((a, b) => a.name.localeCompare(b.name))) {\n walk(child, depth + 1);\n }\n };\n if (root.files.length) walk({ ...root, dirs: new Map() }, 0);\n for (const child of [...root.dirs.values()].sort((a, b) => a.name.localeCompare(b.name))) {\n walk(child, 0);\n }\n toc.innerHTML = bits.join('');\n}\n\nasync function loadReadme() {\n const box = host.querySelector('#docReadme');\n if (!box) return;\n for (const name of ['README.md', 'readme.md', 'Readme.md', 'README', 'README.txt']) {\n try {\n const text = await readRepoFile(name);\n if (text) {\n box.innerHTML = mdRender(text);\n return;\n }\n } catch {\n /* try the next spelling */\n }\n }\n box.innerHTML = '<span class=\"doc-readme-none\">This repo ships no README — the map and the patterns are all you get.</span>';\n}\n\n// ---- the sticky ask bar: questions scoped to the doc on screen -------------\n\nasync function askAboutRepo() {\n const input = host.querySelector('#docAskInput');\n const question = input?.value.trim();\n if (!question) return hooks.onToast('Type a question first.');\n if (!llm.isConfigured()) {\n hooks.onOpenSettings();\n hooks.onToast('Add an endpoint and model first.');\n return;\n }\n input.value = '';\n const rail = state.docs.railEl;\n rail.hidden = false;\n // reveal the clear/close controls now that a conversation exists\n const bar = state.docs.bar;\n if (bar) {\n const c = bar.querySelector<HTMLElement>('.doc-clear');\n const x = bar.querySelector<HTMLElement>('.doc-close');\n if (c) c.hidden = false;\n if (x) x.hidden = false;\n }\n const settings = llm.getSettings();\n\n const card = document.createElement('div');\n card.className = 'doc-qa-card';\n card.innerHTML = `<p class=\"doc-qa-q\">“${escapeHtml(question)}”</p><div class=\"doc-qa-a\"><span class=\"caret\"></span></div>`;\n rail.appendChild(card);\n card.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n\n // Scope the answer to whatever is on screen: an open file/folder doc tab\n // gets its facts (and source, for files) sent along; the overview answers\n // from the repo-wide picture.\n const active = state.docs.active || 'overview';\n let file = null;\n let scope = 'the repo overview';\n let overview = hooks.repoOverview() + ' Top-level folders: ' + [...state.treeData.dirs.keys()].join(', ') + '.';\n if (active.startsWith('file:')) {\n const path = active.slice(5);\n file = await gatherFileContext(path);\n scope = path;\n } else if (active.startsWith('folder:')) {\n scope = active.slice(7) + '/';\n overview += ` The user is currently reading the documentation for the folder \"${active.slice(7)}\".`;\n }\n\n const messages = llm.questionMessages({ repoName: state.scan.name, overview, question, file });\n let text = '';\n try {\n for await (const delta of streamExplain({ ...settings, messages, maxTokens: 1200 })) {\n text += delta;\n card.querySelector('.doc-qa-a').innerHTML = mdLite(text) + '<span class=\"caret\"></span>';\n }\n card.querySelector('.doc-qa-a').innerHTML =\n mdLite(text) + `<p class=\"explain-src\">Answered by ${settings.model}, looking at ${escapeHtml(scope)}.</p>`;\n } catch (err) {\n card.querySelector('.doc-qa-a').innerHTML =\n `<p style=\"color:var(--warn)\">${escapeHtml(err.message)} — check the API key drawer, top right.</p>`;\n }\n}\n\n// ---- doc tabs: detailed AI documents per file/folder -----------------------\n\nfunction renderDocStrip() {\n const strip = state.docs.tabsEl;\n if (!strip) return;\n strip.innerHTML = '';\n\n const mk = (key, label, closable) => {\n const b = document.createElement('button');\n b.className = 'doc-tab' + (state.docs.active === key ? ' is-active' : '');\n const entry = state.docs.tabs.get(key);\n b.innerHTML = `<span>${escapeHtml(label)}</span>${entry?.status === 'writing' ? '<span class=\"doc-tab-spin\">…</span>' : ''}`;\n b.addEventListener('click', () => {\n state.docs.active = key;\n renderDocStrip();\n showActiveDoc();\n });\n if (closable) {\n const x = document.createElement('span');\n x.className = 'doc-tab-x';\n x.textContent = '×';\n x.title = 'Close this tab';\n x.addEventListener('click', (event) => {\n event.stopPropagation();\n closeDocTab(key);\n });\n b.appendChild(x);\n }\n return b;\n };\n\n strip.appendChild(mk('overview', 'Overview', false));\n for (const [key, entry] of state.docs.tabs) {\n strip.appendChild(mk(key, entry.title, true));\n }\n}\n\nfunction openDocTab(kind, path) {\n const key = kind + ':' + path;\n if (!state.docs.tabs.has(key)) {\n const title = (path ? path.split('/').pop() : '(root)') + (kind === 'folder' ? '/' : '');\n state.docs.tabs.set(key, { key, kind, path, title, status: 'idle', text: '', error: '' });\n }\n state.docs.active = key;\n renderDocStrip();\n showActiveDoc();\n const entry = state.docs.tabs.get(key);\n if (entry.status === 'idle') generateDocTab(entry);\n}\n\nfunction closeDocTab(key) {\n state.docs.tabs.delete(key);\n if (state.docs.active === key) state.docs.active = 'overview';\n renderDocStrip();\n showActiveDoc();\n}\n\nfunction showActiveDoc() {\n const wrap = state.docs.wrapEl;\n if (!wrap) return;\n if (state.docs.active === 'overview') {\n wrap.replaceChildren(state.docs.pageEl);\n return;\n }\n const entry = state.docs.tabs.get(state.docs.active);\n if (entry) wrap.replaceChildren(buildPaneEl(entry));\n}\n\nfunction buildPaneEl(entry) {\n const pane = document.createElement('div');\n pane.className = 'doc-page doc-detail';\n\n const chips = [];\n if (entry.kind === 'file') {\n const f = scanIndex(state.scan).fileAt(entry.path);\n const fin = state.facts.fanIn[entry.path] || 0;\n const fout = state.facts.fanOut[entry.path] || 0;\n chips.push(`<b>${fin}</b> dependents`, `<b>${fout}</b> imports`);\n if (f) chips.push(`<b>${f.functions.length}</b> functions`, `<b>${(f.size / 1024).toFixed(1)}</b> kb`);\n } else {\n const node = findNode(state.treeData, entry.path);\n if (node) chips.push(`<b>${node.files.length}</b> files`, `<b>${node.dirs.size}</b> subfolders`);\n }\n\n pane.innerHTML = `\n <h1 class=\"doc-title\">${escapeHtml(entry.title)}</h1>\n <p class=\"doc-sub\">${escapeHtml(entry.path || '(repo root)')}</p>\n <p class=\"doc-sub doc-detail-meta\">${chips.join(' &nbsp;·&nbsp; ')}</p>\n <div class=\"doc-detail-actions\">\n ${entry.kind === 'file' ? '<button class=\"linklike\" data-doc-dd>Open the diagram deep-dive →</button>' : ''}\n <button class=\"linklike\" data-doc-regen>Regenerate</button>\n </div>\n <div class=\"doc-detail-body\"></div>\n `;\n\n const body = pane.querySelector('.doc-detail-body');\n if (entry.status === 'error') {\n body.innerHTML = `<p style=\"color:var(--warn)\">${escapeHtml(entry.error)} — hit Regenerate to try again.</p>`;\n } else if (entry.status === 'idle' || (entry.status === 'writing' && !entry.text)) {\n // The static write-up is the floor: something true is on screen before the\n // model answers, and stays there if it never does.\n const fallback = entry.kind === 'file'\n ? staticDoc(entry.path)\n : folderStaticDoc(entry.path, state.scan, state.facts, findNode(state.treeData, entry.path)?.dirs.size || 0);\n body.innerHTML = `<p>${escapeHtml(fallback)}</p>`\n + '<p class=\"doc-readme-none\">Gathering facts and writing<span class=\"caret\"></span></p>';\n } else {\n body.innerHTML = mdRender(entry.text) + (entry.status === 'writing' ? '<span class=\"caret\"></span>' : '');\n }\n\n pane.addEventListener('click', (event) => {\n if (eventElement(event)?.closest<HTMLElement>('[data-doc-dd]')) hooks.onOpenFile(entry.path);\n if (eventElement(event)?.closest<HTMLElement>('[data-doc-regen]')) {\n entry.status = 'idle';\n entry.text = '';\n generateDocTab(entry);\n showActiveDoc();\n }\n });\n return pane;\n}\n\nlet docRefreshTimer = null;\nfunction scheduleDocPaneRefresh(entry) {\n if (state.docs.active !== entry.key || docRefreshTimer) return;\n docRefreshTimer = setTimeout(() => {\n docRefreshTimer = null;\n if (state.docs.active === entry.key) showActiveDoc();\n }, 160);\n}\n\nasync function generateDocTab(entry) {\n if (!llm.isConfigured()) {\n entry.status = 'error';\n entry.error = 'No endpoint configured — open the API key drawer, top right.';\n showActiveDoc();\n renderDocStrip();\n hooks.onOpenSettings();\n return;\n }\n const settings = llm.getSettings();\n entry.status = 'writing';\n entry.text = '';\n entry.error = '';\n renderDocStrip();\n try {\n let messages;\n if (entry.kind === 'file') {\n const ctx = await gatherFileContext(entry.path);\n messages = llm.fileDocMessages({ repoName: state.scan.name, overview: hooks.repoOverview(), file: ctx });\n } else {\n const node = findNode(state.treeData, entry.path);\n messages = llm.folderDocDetailedMessages({\n repoName: state.scan.name,\n folder: entry.path,\n subfolders: node ? [...node.dirs.keys()].map((n) => n + '/') : [],\n files: (node?.files || []).slice(0, 18).map(fileRow),\n });\n }\n for await (const delta of streamExplain({ ...settings, messages, maxTokens: 1800 })) {\n entry.text += delta;\n scheduleDocPaneRefresh(entry);\n }\n entry.status = 'done';\n } catch (err) {\n entry.status = 'error';\n entry.error = err.message;\n }\n renderDocStrip();\n if (state.docs.active === entry.key) showActiveDoc();\n}\n\n// ---- the folder-by-folder pass --------------------------------------------\n\n// Which folders get an AI brief, and with what file facts. Top-level folders\n// by size, capped — one call briefs a folder and everything directly in it.\nfunction docTargets() {\n const root = state.treeData;\n const targets = [];\n if (root.files.length) {\n targets.push({\n path: '',\n label: '(repo root)',\n subfolders: [...root.dirs.keys()].map((n) => n + '/'),\n files: root.files.slice(0, 18).map(fileRow),\n });\n }\n const topDirs = [...root.dirs.values()]\n .map((node) => ({ node, count: countFiles(node) }))\n .sort((a, b) => b.count - a.count)\n .slice(0, 8);\n for (const { node } of topDirs) {\n targets.push({\n path: node.path,\n label: node.path + '/',\n subfolders: [...node.dirs.keys()].map((n) => n + '/'),\n files: node.files.slice(0, 18).map(fileRow),\n });\n }\n return targets;\n}\n\nasync function writeDocsWithAI() {\n if (state.docs.writing) return;\n if (!llm.isConfigured()) {\n hooks.onOpenSettings();\n hooks.onToast('Add an endpoint and model first.');\n return;\n }\n const settings = llm.getSettings();\n const page = state.docs.pageEl;\n const zone = page?.querySelector('#docGenZone');\n const btn = page?.querySelector<HTMLButtonElement>('#docGenBtn');\n const note = page?.querySelector('#docAiNote');\n const overviewEl = page?.querySelector('#docOverview');\n if (!zone) return;\n\n state.docs.writing = true;\n if (btn) {\n btn.disabled = true;\n btn.textContent = 'writing…';\n }\n zone.querySelectorAll('.doc-generated').forEach((el) => el.remove()); // regenerate = fresh page\n const empty = zone.querySelector<HTMLElement>('.doc-gen-empty');\n if (empty) empty.hidden = true;\n\n try {\n // 1. The repo-level overview, streamed in place.\n if (note) note.textContent = 'Writing the overview…';\n const readmeText = host.querySelector('#docReadme')?.innerText?.slice(0, 1500) || '';\n let out = '';\n if (overviewEl) overviewEl.innerHTML = '<span class=\"caret\"></span>';\n const overviewMsgs = llm.repoDocMessages({\n repoName: state.scan.name,\n overview: hooks.repoOverview(),\n readmeExcerpt: readmeText,\n entries: state.facts.entries,\n hubs: state.facts.hubs,\n });\n for await (const delta of streamExplain({ ...settings, messages: overviewMsgs, maxTokens: 900 })) {\n out += delta;\n if (overviewEl) overviewEl.textContent = out;\n }\n\n // 2. Folder by folder: each section appears with static sentences first,\n // then the AI's documentation overwrites it as the answer lands.\n const targets = docTargets();\n let done = 0;\n for (const t of targets) {\n done++;\n if (note) note.textContent = `Writing documentation… ${done} of ${targets.length} — ${t.label}`;\n\n const node = findNode(state.treeData, t.path);\n const section = document.createElement('section');\n section.className = 'doc-folder doc-generated';\n section.innerHTML = `\n <h3 class=\"doc-folder-h\"><button data-doc-open=\"folder:${escapeHtml(t.path)}\">${escapeHtml(t.label)}</button></h3>\n <p class=\"doc-folder-brief\"><span class=\"caret\"></span></p>\n ${t.files.map((f) => `\n <div class=\"doc-entry${f.parsed ? '' : ' is-unparsed'}\">\n <div class=\"doc-entry-head\">\n ${f.parsed ? `<button class=\"doc-entry-name\" data-doc-open=\"file:${escapeHtml(f.path)}\">${escapeHtml(f.name)}</button>` : `<span class=\"doc-entry-name\">${escapeHtml(f.name)}</span>`}\n <span class=\"doc-entry-facts\">${escapeHtml(factsLine(f.path))}</span>\n </div>\n <p class=\"doc-entry-text\" data-doc-file=\"${escapeHtml(f.path)}\">${escapeHtml(staticDoc(f.path))}</p>\n </div>`).join('')}\n `;\n zone.appendChild(section);\n section.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n\n let text = '';\n const msgs = llm.folderDocMessages({\n repoName: state.scan.name,\n folder: t.path,\n subfolders: t.subfolders,\n files: t.files,\n });\n for await (const delta of streamExplain({ ...settings, messages: msgs, maxTokens: 1600 })) {\n text += delta;\n }\n const parsed = parseFolderDoc(text);\n const folderP = section.querySelector('.doc-folder-brief');\n if (folderP) {\n folderP.textContent = parsed.folderBrief\n || folderStaticDoc(t.path, state.scan, state.facts, node?.dirs.size || 0);\n }\n for (const [name, brief] of parsed.files) {\n const row = t.files.find((f) => f.name === name);\n if (!row) continue;\n const el = section.querySelector(`[data-doc-file=\"${CSS.escape(row.path)}\"]`);\n if (el) el.textContent = brief;\n }\n }\n if (note) note.textContent = `Written by ${settings.model} from the real import graph. Press “Regenerate documentation” for a fresh take.`;\n if (btn) btn.textContent = 'Regenerate documentation';\n } catch (err) {\n if (note) note.textContent = 'AI writing stopped: ' + err.message;\n hooks.onToast(err.message);\n } finally {\n state.docs.writing = false;\n if (btn) {\n btn.disabled = false;\n if (btn.textContent === 'writing…') btn.textContent = 'Generate documentation';\n }\n }\n}\n"]}
1
+ {"version":3,"file":"docsView.js","sourceRoot":"","sources":["../../src/public/js/docsView.ts"],"names":[],"mappings":"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","sourcesContent":["import { eventElement } from '/js/dom.js';\n// The Docs tab: a written account of the repo.\n//\n// Three layers, in order of how much they cost. The overview page and the\n// folder-by-folder sections are written from the import graph and need no\n// network at all. Pressing \"Generate documentation\" replaces those sentences\n// with a model's, streamed in place. Opening a topic from the contents adds a\n// tab that documents one file or folder in depth, and the bar at the bottom\n// answers questions scoped to whichever of those is on screen.\n//\n// The static sentences live in `/shared/analyzer/docs.js` — pure, and tested.\n// What is here is the DOM: tabs, streaming, and the wiring between them.\n\nimport { parseFolderDoc, fileFactsLine, fileStaticDoc, folderStaticDoc, docFileRow } from '/shared/analyzer/docs.js';\nimport { explainOverview } from '/shared/analyzer/explainLocal.js';\nimport { scanIndex } from '/shared/analyzer/graph.js';\nimport { escapeHtml } from './html.js';\nimport { mdLite, mdRender } from './markdown.js';\nimport { state } from './state.js';\nimport { streamExplain } from './api.js';\nimport { findNode, countFiles } from './tree.js';\nimport { readRepoFile, gatherFileContext } from './repoFiles.js';\nimport * as llm from './llm.js';\n\nlet host = null;\nlet hooks = {\n onToast: (..._args: unknown[]) => {},\n onOpenSettings: (..._args: unknown[]) => {},\n onOpenFile: (..._args: unknown[]) => {},\n repoOverview: () => '',\n};\n\nexport function initDocs(options) {\n host = options.host;\n hooks = { ...hooks, ...options };\n}\n\n// Facts about one file or folder, bound to the loaded repo. Every prompt and\n// every static sentence on this page goes through these two, so the doc tab and\n// the folder pass can never describe the same file differently.\nconst factsLine = (path) => fileFactsLine(path, state.scan, state.facts);\nconst staticDoc = (path) => fileStaticDoc(path, state.scan, state.facts);\nconst fileRow = (path) => docFileRow(path, state.scan, state.facts);\n\n// ---- the page ---------------------------------------------------------------\n\n// One-time document build per scan; AI briefs update rows in place afterwards.\nexport function renderDocs() {\n if (state.docs.rendered) return;\n state.docs.rendered = true;\n\n const tabsEl = document.createElement('div');\n tabsEl.className = 'doc-tabs';\n const wrapEl = document.createElement('div');\n const scrollEl = document.createElement('div');\n scrollEl.className = 'doc-scroll';\n scrollEl.append(tabsEl, wrapEl);\n\n // Answers stream into this rail, above the bar, only once there's something\n // to show.\n const railEl = document.createElement('div');\n railEl.className = 'doc-qa-rail';\n railEl.hidden = true;\n\n // The sticky bottom bar: questions about whatever doc is on screen, with\n // clear/close once a conversation is underway.\n const barEl = document.createElement('form');\n barEl.className = 'doc-askbar';\n barEl.innerHTML = `\n <input type=\"text\" id=\"docAskInput\" class=\"text-input\" placeholder=\"Ask me anything about this doc…\" spellcheck=\"false\" autocomplete=\"off\">\n <button type=\"submit\" class=\"btn btn-ink\">Ask</button>`;\n const clearBtn = document.createElement('button');\n clearBtn.type = 'button';\n clearBtn.className = 'btn btn-sm btn-ghost doc-clear';\n clearBtn.textContent = 'clear';\n clearBtn.title = 'Clear the conversation';\n clearBtn.hidden = true;\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'btn btn-sm btn-ghost doc-close';\n closeBtn.textContent = 'close';\n closeBtn.title = 'Close the conversation';\n closeBtn.hidden = true;\n clearBtn.addEventListener('click', () => { railEl.innerHTML = ''; });\n closeBtn.addEventListener('click', () => {\n railEl.hidden = true;\n clearBtn.hidden = true;\n closeBtn.hidden = true;\n });\n barEl.append(clearBtn, closeBtn);\n barEl.addEventListener('submit', (event) => {\n event.preventDefault();\n askAboutRepo();\n });\n\n host.innerHTML = '';\n host.append(scrollEl, railEl, barEl);\n state.docs.tabsEl = tabsEl;\n state.docs.wrapEl = wrapEl;\n state.docs.railEl = railEl;\n state.docs.bar = barEl;\n state.docs.pageEl = buildOverviewPage();\n\n renderDocStrip();\n showActiveDoc();\n loadReadme();\n}\n\n// The landing tab: repo header, topic index, README, the documentation zone\n// (filled on demand by the AI), and the question box at the bottom.\nfunction buildOverviewPage() {\n const { scan, facts, manifest } = state;\n const root = state.treeData;\n\n const page = document.createElement('div');\n page.className = 'doc-page';\n page.innerHTML = `\n <div id=\"doc-top\">\n <h1 class=\"doc-title\">${escapeHtml(scan.name)}</h1>\n <p class=\"doc-sub\">${escapeHtml(scan.root)}</p>\n <p class=\"doc-sub\">${scan.stats.filesParsed} code files · ${scan.stats.edgeCount} connections · scanned in ${scan.stats.tookMs} ms</p>\n <p class=\"doc-ai-note\" id=\"docAiNote\">Pick a topic in the contents for a detailed AI document. “Generate documentation” writes the full folder-by-folder write-up below.</p>\n <p class=\"doc-overview\" id=\"docOverview\">${escapeHtml(explainOverview(scan, facts, manifest).replace(/[*`]/g, ''))}</p>\n </div>\n <nav class=\"doc-toc\" id=\"docToc\"></nav>\n <h2 class=\"doc-section-h doc-anchor\" id=\"doc-readme-h\">In its own words</h2>\n <div class=\"doc-readme\" id=\"docReadme\"><span class=\"doc-readme-none\">Looking for a README…</span></div>\n <h2 class=\"doc-section-h doc-anchor\" id=\"doc-layout-h\">The documentation</h2>\n <div id=\"docGenZone\">\n <p class=\"doc-gen-empty\">Nothing written yet. Press the button and the AI will document this repo folder by folder, right here.</p>\n <button class=\"btn btn-ink\" id=\"docGenBtn\">Generate documentation</button>\n </div>\n `;\n\n buildToc(page, root);\n\n page.addEventListener('click', (event) => {\n const b = eventElement(event)?.closest<HTMLElement>('[data-doc-open]');\n if (!b) return;\n const raw = b.dataset.docOpen;\n const i = raw.indexOf(':');\n openDocTab(raw.slice(0, i), raw.slice(i + 1));\n });\n page.querySelector<HTMLButtonElement>('#docGenBtn').addEventListener('click', writeDocsWithAI);\n\n return page;\n}\n\n// The contents index: page anchors up top, then every folder and file as a\n// topic — clicking any of them opens its detailed AI tab.\nfunction buildToc(page, root) {\n const toc = page.querySelector('#docToc');\n const bits = [\n `<span class=\"doc-toc-h\">Get documentation about a topic</span>`,\n `<a href=\"#doc-top\">Overview</a>`,\n `<a href=\"#doc-readme-h\">In its own words</a>`,\n `<span class=\"doc-toc-group\">Folders and files — click for the AI document</span>`,\n ];\n let folderCount = 0;\n const parsedSet = new Set(state.scan.files.map((f) => f.path));\n const walk = (node, depth) => {\n if (folderCount >= 30) return;\n folderCount++;\n const indent = Math.min(depth, 3);\n bits.push(`<button class=\"doc-toc-link doc-toc-folder doc-indent-${indent}\" data-doc-open=\"folder:${escapeHtml(node.path)}\">${escapeHtml(node.path ? node.name + '/' : '(repo root)')}</button>`);\n const files = node.files.filter((f) => parsedSet.has(f)).slice(0, 10);\n if (files.length) {\n const links = files\n .map((f) => `<button class=\"doc-toc-link\" data-doc-open=\"file:${escapeHtml(f)}\">${escapeHtml(f.split('/').pop())}</button>`)\n .join('<span class=\"doc-toc-sep\"> · </span>');\n bits.push(`<span class=\"doc-toc-files doc-indent-${indent}\">${links}${node.files.length > files.length ? ' …' : ''}</span>`);\n }\n for (const child of [...node.dirs.values()].sort((a, b) => a.name.localeCompare(b.name))) {\n walk(child, depth + 1);\n }\n };\n if (root.files.length) walk({ ...root, dirs: new Map() }, 0);\n for (const child of [...root.dirs.values()].sort((a, b) => a.name.localeCompare(b.name))) {\n walk(child, 0);\n }\n toc.innerHTML = bits.join('');\n}\n\nasync function loadReadme() {\n const box = host.querySelector('#docReadme');\n if (!box) return;\n for (const name of ['README.md', 'readme.md', 'Readme.md', 'README', 'README.txt']) {\n try {\n const text = await readRepoFile(name);\n if (text) {\n box.innerHTML = mdRender(text);\n return;\n }\n } catch {\n /* try the next spelling */\n }\n }\n box.innerHTML = '<span class=\"doc-readme-none\">This repo ships no README — the map and the patterns are all you get.</span>';\n}\n\n// ---- the sticky ask bar: questions scoped to the doc on screen -------------\n\nasync function askAboutRepo() {\n const input = host.querySelector('#docAskInput');\n const question = input?.value.trim();\n if (!question) return hooks.onToast('Type a question first.');\n if (!llm.isConfigured()) {\n hooks.onOpenSettings();\n hooks.onToast('Choose Hermes or an endpoint in AI connection first.');\n return;\n }\n input.value = '';\n const rail = state.docs.railEl;\n rail.hidden = false;\n // reveal the clear/close controls now that a conversation exists\n const bar = state.docs.bar;\n if (bar) {\n const c = bar.querySelector<HTMLElement>('.doc-clear');\n const x = bar.querySelector<HTMLElement>('.doc-close');\n if (c) c.hidden = false;\n if (x) x.hidden = false;\n }\n const settings = llm.getSettings();\n\n const card = document.createElement('div');\n card.className = 'doc-qa-card';\n card.innerHTML = `<p class=\"doc-qa-q\">“${escapeHtml(question)}”</p><div class=\"doc-qa-a\"><span class=\"caret\"></span></div>`;\n rail.appendChild(card);\n card.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n\n // Scope the answer to whatever is on screen: an open file/folder doc tab\n // gets its facts (and source, for files) sent along; the overview answers\n // from the repo-wide picture.\n const active = state.docs.active || 'overview';\n let file = null;\n let scope = 'the repo overview';\n let overview = hooks.repoOverview() + ' Top-level folders: ' + [...state.treeData.dirs.keys()].join(', ') + '.';\n if (active.startsWith('file:')) {\n const path = active.slice(5);\n file = await gatherFileContext(path);\n scope = path;\n } else if (active.startsWith('folder:')) {\n scope = active.slice(7) + '/';\n overview += ` The user is currently reading the documentation for the folder \"${active.slice(7)}\".`;\n }\n\n const messages = llm.questionMessages({ repoName: state.scan.name, overview, question, file });\n let text = '';\n try {\n for await (const delta of streamExplain({ ...settings, messages, maxTokens: 1200 })) {\n text += delta;\n card.querySelector('.doc-qa-a').innerHTML = mdLite(text) + '<span class=\"caret\"></span>';\n }\n card.querySelector('.doc-qa-a').innerHTML =\n mdLite(text) + `<p class=\"explain-src\">Answered by ${escapeHtml(settings.model)}, looking at ${escapeHtml(scope)}.</p>`;\n } catch (err) {\n card.querySelector('.doc-qa-a').innerHTML =\n `<p style=\"color:var(--warn)\">${escapeHtml(err.message)} — check the AI connection drawer, top right.</p>`;\n }\n}\n\n// ---- doc tabs: detailed AI documents per file/folder -----------------------\n\nfunction renderDocStrip() {\n const strip = state.docs.tabsEl;\n if (!strip) return;\n strip.innerHTML = '';\n\n const mk = (key, label, closable) => {\n const b = document.createElement('button');\n b.className = 'doc-tab' + (state.docs.active === key ? ' is-active' : '');\n const entry = state.docs.tabs.get(key);\n b.innerHTML = `<span>${escapeHtml(label)}</span>${entry?.status === 'writing' ? '<span class=\"doc-tab-spin\">…</span>' : ''}`;\n b.addEventListener('click', () => {\n state.docs.active = key;\n renderDocStrip();\n showActiveDoc();\n });\n if (closable) {\n const x = document.createElement('span');\n x.className = 'doc-tab-x';\n x.textContent = '×';\n x.title = 'Close this tab';\n x.addEventListener('click', (event) => {\n event.stopPropagation();\n closeDocTab(key);\n });\n b.appendChild(x);\n }\n return b;\n };\n\n strip.appendChild(mk('overview', 'Overview', false));\n for (const [key, entry] of state.docs.tabs) {\n strip.appendChild(mk(key, entry.title, true));\n }\n}\n\nfunction openDocTab(kind, path) {\n const key = kind + ':' + path;\n if (!state.docs.tabs.has(key)) {\n const title = (path ? path.split('/').pop() : '(root)') + (kind === 'folder' ? '/' : '');\n state.docs.tabs.set(key, { key, kind, path, title, status: 'idle', text: '', error: '' });\n }\n state.docs.active = key;\n renderDocStrip();\n showActiveDoc();\n const entry = state.docs.tabs.get(key);\n if (entry.status === 'idle') generateDocTab(entry);\n}\n\nfunction closeDocTab(key) {\n state.docs.tabs.delete(key);\n if (state.docs.active === key) state.docs.active = 'overview';\n renderDocStrip();\n showActiveDoc();\n}\n\nfunction showActiveDoc() {\n const wrap = state.docs.wrapEl;\n if (!wrap) return;\n if (state.docs.active === 'overview') {\n wrap.replaceChildren(state.docs.pageEl);\n return;\n }\n const entry = state.docs.tabs.get(state.docs.active);\n if (entry) wrap.replaceChildren(buildPaneEl(entry));\n}\n\nfunction buildPaneEl(entry) {\n const pane = document.createElement('div');\n pane.className = 'doc-page doc-detail';\n\n const chips = [];\n if (entry.kind === 'file') {\n const f = scanIndex(state.scan).fileAt(entry.path);\n const fin = state.facts.fanIn[entry.path] || 0;\n const fout = state.facts.fanOut[entry.path] || 0;\n chips.push(`<b>${fin}</b> dependents`, `<b>${fout}</b> imports`);\n if (f) chips.push(`<b>${f.functions.length}</b> functions`, `<b>${(f.size / 1024).toFixed(1)}</b> kb`);\n } else {\n const node = findNode(state.treeData, entry.path);\n if (node) chips.push(`<b>${node.files.length}</b> files`, `<b>${node.dirs.size}</b> subfolders`);\n }\n\n pane.innerHTML = `\n <h1 class=\"doc-title\">${escapeHtml(entry.title)}</h1>\n <p class=\"doc-sub\">${escapeHtml(entry.path || '(repo root)')}</p>\n <p class=\"doc-sub doc-detail-meta\">${chips.join(' &nbsp;·&nbsp; ')}</p>\n <div class=\"doc-detail-actions\">\n ${entry.kind === 'file' ? '<button class=\"linklike\" data-doc-dd>Open the diagram deep-dive →</button>' : ''}\n <button class=\"linklike\" data-doc-regen>Regenerate</button>\n </div>\n <div class=\"doc-detail-body\"></div>\n `;\n\n const body = pane.querySelector('.doc-detail-body');\n if (entry.status === 'error') {\n body.innerHTML = `<p style=\"color:var(--warn)\">${escapeHtml(entry.error)} — hit Regenerate to try again.</p>`;\n } else if (entry.status === 'idle' || (entry.status === 'writing' && !entry.text)) {\n // The static write-up is the floor: something true is on screen before the\n // model answers, and stays there if it never does.\n const fallback = entry.kind === 'file'\n ? staticDoc(entry.path)\n : folderStaticDoc(entry.path, state.scan, state.facts, findNode(state.treeData, entry.path)?.dirs.size || 0);\n body.innerHTML = `<p>${escapeHtml(fallback)}</p>`\n + '<p class=\"doc-readme-none\">Gathering facts and writing<span class=\"caret\"></span></p>';\n } else {\n body.innerHTML = mdRender(entry.text) + (entry.status === 'writing' ? '<span class=\"caret\"></span>' : '');\n }\n\n pane.addEventListener('click', (event) => {\n if (eventElement(event)?.closest<HTMLElement>('[data-doc-dd]')) hooks.onOpenFile(entry.path);\n if (eventElement(event)?.closest<HTMLElement>('[data-doc-regen]')) {\n entry.status = 'idle';\n entry.text = '';\n generateDocTab(entry);\n showActiveDoc();\n }\n });\n return pane;\n}\n\nlet docRefreshTimer = null;\nfunction scheduleDocPaneRefresh(entry) {\n if (state.docs.active !== entry.key || docRefreshTimer) return;\n docRefreshTimer = setTimeout(() => {\n docRefreshTimer = null;\n if (state.docs.active === entry.key) showActiveDoc();\n }, 160);\n}\n\nasync function generateDocTab(entry) {\n if (!llm.isConfigured()) {\n entry.status = 'error';\n entry.error = 'No AI connection configured — open the AI connection drawer, top right.';\n showActiveDoc();\n renderDocStrip();\n hooks.onOpenSettings();\n return;\n }\n const settings = llm.getSettings();\n entry.status = 'writing';\n entry.text = '';\n entry.error = '';\n renderDocStrip();\n try {\n let messages;\n if (entry.kind === 'file') {\n const ctx = await gatherFileContext(entry.path);\n messages = llm.fileDocMessages({ repoName: state.scan.name, overview: hooks.repoOverview(), file: ctx });\n } else {\n const node = findNode(state.treeData, entry.path);\n messages = llm.folderDocDetailedMessages({\n repoName: state.scan.name,\n folder: entry.path,\n subfolders: node ? [...node.dirs.keys()].map((n) => n + '/') : [],\n files: (node?.files || []).slice(0, 18).map(fileRow),\n });\n }\n for await (const delta of streamExplain({ ...settings, messages, maxTokens: 1800 })) {\n entry.text += delta;\n scheduleDocPaneRefresh(entry);\n }\n entry.status = 'done';\n } catch (err) {\n entry.status = 'error';\n entry.error = err.message;\n }\n renderDocStrip();\n if (state.docs.active === entry.key) showActiveDoc();\n}\n\n// ---- the folder-by-folder pass --------------------------------------------\n\n// Which folders get an AI brief, and with what file facts. Top-level folders\n// by size, capped — one call briefs a folder and everything directly in it.\nfunction docTargets() {\n const root = state.treeData;\n const targets = [];\n if (root.files.length) {\n targets.push({\n path: '',\n label: '(repo root)',\n subfolders: [...root.dirs.keys()].map((n) => n + '/'),\n files: root.files.slice(0, 18).map(fileRow),\n });\n }\n const topDirs = [...root.dirs.values()]\n .map((node) => ({ node, count: countFiles(node) }))\n .sort((a, b) => b.count - a.count)\n .slice(0, 8);\n for (const { node } of topDirs) {\n targets.push({\n path: node.path,\n label: node.path + '/',\n subfolders: [...node.dirs.keys()].map((n) => n + '/'),\n files: node.files.slice(0, 18).map(fileRow),\n });\n }\n return targets;\n}\n\nasync function writeDocsWithAI() {\n if (state.docs.writing) return;\n if (!llm.isConfigured()) {\n hooks.onOpenSettings();\n hooks.onToast('Choose Hermes or an endpoint in AI connection first.');\n return;\n }\n const settings = llm.getSettings();\n const page = state.docs.pageEl;\n const zone = page?.querySelector('#docGenZone');\n const btn = page?.querySelector<HTMLButtonElement>('#docGenBtn');\n const note = page?.querySelector('#docAiNote');\n const overviewEl = page?.querySelector('#docOverview');\n if (!zone) return;\n\n state.docs.writing = true;\n if (btn) {\n btn.disabled = true;\n btn.textContent = 'writing…';\n }\n zone.querySelectorAll('.doc-generated').forEach((el) => el.remove()); // regenerate = fresh page\n const empty = zone.querySelector<HTMLElement>('.doc-gen-empty');\n if (empty) empty.hidden = true;\n\n try {\n // 1. The repo-level overview, streamed in place.\n if (note) note.textContent = 'Writing the overview…';\n const readmeText = host.querySelector('#docReadme')?.innerText?.slice(0, 1500) || '';\n let out = '';\n if (overviewEl) overviewEl.innerHTML = '<span class=\"caret\"></span>';\n const overviewMsgs = llm.repoDocMessages({\n repoName: state.scan.name,\n overview: hooks.repoOverview(),\n readmeExcerpt: readmeText,\n entries: state.facts.entries,\n hubs: state.facts.hubs,\n });\n for await (const delta of streamExplain({ ...settings, messages: overviewMsgs, maxTokens: 900 })) {\n out += delta;\n if (overviewEl) overviewEl.textContent = out;\n }\n\n // 2. Folder by folder: each section appears with static sentences first,\n // then the AI's documentation overwrites it as the answer lands.\n const targets = docTargets();\n let done = 0;\n for (const t of targets) {\n done++;\n if (note) note.textContent = `Writing documentation… ${done} of ${targets.length} — ${t.label}`;\n\n const node = findNode(state.treeData, t.path);\n const section = document.createElement('section');\n section.className = 'doc-folder doc-generated';\n section.innerHTML = `\n <h3 class=\"doc-folder-h\"><button data-doc-open=\"folder:${escapeHtml(t.path)}\">${escapeHtml(t.label)}</button></h3>\n <p class=\"doc-folder-brief\"><span class=\"caret\"></span></p>\n ${t.files.map((f) => `\n <div class=\"doc-entry${f.parsed ? '' : ' is-unparsed'}\">\n <div class=\"doc-entry-head\">\n ${f.parsed ? `<button class=\"doc-entry-name\" data-doc-open=\"file:${escapeHtml(f.path)}\">${escapeHtml(f.name)}</button>` : `<span class=\"doc-entry-name\">${escapeHtml(f.name)}</span>`}\n <span class=\"doc-entry-facts\">${escapeHtml(factsLine(f.path))}</span>\n </div>\n <p class=\"doc-entry-text\" data-doc-file=\"${escapeHtml(f.path)}\">${escapeHtml(staticDoc(f.path))}</p>\n </div>`).join('')}\n `;\n zone.appendChild(section);\n section.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n\n let text = '';\n const msgs = llm.folderDocMessages({\n repoName: state.scan.name,\n folder: t.path,\n subfolders: t.subfolders,\n files: t.files,\n });\n for await (const delta of streamExplain({ ...settings, messages: msgs, maxTokens: 1600 })) {\n text += delta;\n }\n const parsed = parseFolderDoc(text);\n const folderP = section.querySelector('.doc-folder-brief');\n if (folderP) {\n folderP.textContent = parsed.folderBrief\n || folderStaticDoc(t.path, state.scan, state.facts, node?.dirs.size || 0);\n }\n for (const [name, brief] of parsed.files) {\n const row = t.files.find((f) => f.name === name);\n if (!row) continue;\n const el = section.querySelector(`[data-doc-file=\"${CSS.escape(row.path)}\"]`);\n if (el) el.textContent = brief;\n }\n }\n if (note) note.textContent = `Written by ${escapeHtml(settings.model)} from the real import graph. Press “Regenerate documentation” for a fresh take.`;\n if (btn) btn.textContent = 'Regenerate documentation';\n } catch (err) {\n if (note) note.textContent = 'AI writing stopped: ' + err.message;\n hooks.onToast(err.message);\n } finally {\n state.docs.writing = false;\n if (btn) {\n btn.disabled = false;\n if (btn.textContent === 'writing…') btn.textContent = 'Generate documentation';\n }\n }\n}\n"]}
@@ -123,6 +123,14 @@ export interface AppElements {
123
123
  'pickCard': HTMLElement;
124
124
  'repoChip': HTMLElement;
125
125
  'saveSettings': HTMLButtonElement;
126
+ 'setConnection': HTMLSelectElement;
127
+ 'apiConnection': HTMLElement;
128
+ 'hermesConnection': HTMLElement;
129
+ 'hermesConnectionStatus': HTMLElement;
130
+ 'hermesModel': HTMLElement;
131
+ 'hermesProvider': HTMLElement;
132
+ 'refreshHermes': HTMLButtonElement;
133
+ 'aiConnectionNote': HTMLElement;
126
134
  'sbomSearchInput': HTMLInputElement;
127
135
  'sbomTable': HTMLElement;
128
136
  'sbomTableBody': HTMLElement;
@@ -1 +1 @@
1
- {"version":3,"file":"dom.js","sourceRoot":"","sources":["../../src/public/js/dom.ts"],"names":[],"mappings":"AA4LA,8DAA8D;AAC9D,MAAM,UAAU,YAAY,CAAC,KAAY;IACvC,OAAO,KAAK,CAAC,MAAM,YAAY,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AACnE,CAAC","sourcesContent":["/** Element types for the app shell and view templates. */\nexport interface AppElements {\n reviewView: HTMLElement;\n cancelScanBtn: HTMLButtonElement;\n scanProgress: HTMLProgressElement;\n inspTitle: HTMLElement;\n 'aboutRemote': HTMLElement;\n 'aboutView': HTMLElement;\n 'aiDrawBtn': HTMLButtonElement;\n 'aiExplainBtn': HTMLButtonElement;\n 'analysisView': HTMLElement;\n 'askBtn': HTMLButtonElement;\n 'askInput': HTMLInputElement;\n 'blameGutter': HTMLElement;\n 'blastDesc': HTMLElement;\n 'blastMetric': HTMLElement;\n 'blastRadiusBox': HTMLElement;\n 'brandNote': HTMLElement;\n 'canvas': HTMLElement;\n 'canvasContent': HTMLElement;\n 'canvasEmpty': HTMLElement;\n 'codeAlt': HTMLElement;\n 'codeHead': HTMLElement;\n 'codeLinks': HTMLElement;\n 'codeMonaco': HTMLElement;\n 'codeTabContainer': HTMLElement;\n 'codeView': HTMLElement;\n 'copyMermaidBtn': HTMLButtonElement;\n 'creditsBar': HTMLElement;\n 'creditsBarRemove': HTMLButtonElement;\n 'creditsHide': HTMLButtonElement;\n 'creditsRemove': HTMLButtonElement;\n 'creditsShow': HTMLButtonElement;\n 'crumbs': HTMLElement;\n 'demoBtn': HTMLButtonElement;\n 'depthSelect': HTMLSelectElement;\n 'diffBaseSelect': HTMLSelectElement;\n 'diffFilesList': HTMLElement;\n 'diffHeadSelect': HTMLSelectElement;\n 'diffModeSplit': HTMLButtonElement;\n 'diffModeUnified': HTMLButtonElement;\n 'diffRunBtn': HTMLButtonElement;\n 'diffSummaryChip': HTMLElement;\n 'diffView': HTMLElement;\n 'diffViewerMain': HTMLElement;\n 'doc-top': HTMLElement;\n 'docAiNote': HTMLElement;\n 'docAskInput': HTMLInputElement;\n 'docGenBtn': HTMLButtonElement;\n 'docGenZone': HTMLElement;\n 'docOverview': HTMLElement;\n 'docReadme': HTMLElement;\n 'docToc': HTMLElement;\n 'docView': HTMLElement;\n 'drawerClose': HTMLButtonElement;\n 'drawerScrim': HTMLElement;\n 'explorer': HTMLElement;\n 'fileSidebar': HTMLElement;\n 'fileTree': HTMLElement;\n 'fitBtn': HTMLButtonElement;\n 'footCredit': HTMLElement;\n 'gitForm': HTMLFormElement;\n 'gitInput': HTMLInputElement;\n 'graphCanvas': HTMLCanvasElement;\n 'graphControls': HTMLElement;\n 'graphFit': HTMLButtonElement;\n 'graphReset': HTMLButtonElement;\n 'graphSearchInput': HTMLInputElement;\n 'graphTooltip': HTMLElement;\n 'graphView': HTMLElement;\n 'graphZoomIn': HTMLButtonElement;\n 'graphZoomOut': HTMLButtonElement;\n 'heatmapCanvas': HTMLCanvasElement;\n 'heatmapTooltip': HTMLElement;\n 'heatmapView': HTMLElement;\n 'insightsView': HTMLElement;\n 'inspBack': HTMLButtonElement;\n 'inspExplain': HTMLElement;\n 'inspLists': HTMLElement;\n 'inspPath': HTMLElement;\n 'inspRole': HTMLElement;\n 'inspSecurityTools': HTMLElement;\n 'inspStats': HTMLElement;\n 'inspector': HTMLElement;\n 'inspectorBody': HTMLElement;\n 'inspectorEmpty': HTMLElement;\n 'landing': HTMLElement;\n 'landingAbout': HTMLElement;\n 'landingError': HTMLElement;\n 'landingFoot': HTMLElement;\n 'landingStatus': HTMLElement;\n 'mcpBtn': HTMLButtonElement;\n 'mcpBtnLabel': HTMLElement;\n 'mcpClose': HTMLButtonElement;\n 'mcpCommandInput': HTMLInputElement;\n 'mcpConfig': HTMLElement;\n 'mcpConfigBlock': HTMLElement;\n 'mcpCopy': HTMLButtonElement;\n 'mcpDot': HTMLElement;\n 'mcpDrawer': HTMLElement;\n 'mcpPanelDot': HTMLElement;\n 'mcpPanelState': HTMLElement;\n 'mcpScrim': HTMLElement;\n 'mcpStart': HTMLButtonElement;\n 'mcpStateRow': HTMLElement;\n 'mcpStatus': HTMLElement;\n 'mcpStop': HTMLButtonElement;\n 'mcpTabJson': HTMLButtonElement;\n 'mcpTabToml': HTMLButtonElement;\n 'mcpToolsHint': HTMLElement;\n 'mmToggleBtn': HTMLButtonElement;\n 'mobileDetailsBtn': HTMLButtonElement;\n 'mobileDetailsClose': HTMLButtonElement;\n 'mobileFilesBtn': HTMLButtonElement;\n 'mobileFilesClose': HTMLButtonElement;\n 'mobilePanelScrim': HTMLButtonElement;\n 'modeSwitch': HTMLElement;\n 'newRepoBtn': HTMLButtonElement;\n 'nodeFilter': HTMLInputElement;\n 'pathForm': HTMLFormElement;\n 'pathInput': HTMLInputElement;\n 'pickBtn': HTMLButtonElement;\n 'pickCard': HTMLElement;\n 'repoChip': HTMLElement;\n 'saveSettings': HTMLButtonElement;\n 'sbomSearchInput': HTMLInputElement;\n 'sbomTable': HTMLElement;\n 'sbomTableBody': HTMLElement;\n 'sbomView': HTMLElement;\n 'searchOverlay': HTMLElement;\n 'searchResultList': HTMLElement;\n 'searchTriggerBtn': HTMLButtonElement;\n 'serverBtn': HTMLButtonElement;\n 'serverClose': HTMLButtonElement;\n 'serverDrawer': HTMLElement;\n 'serverModeNote': HTMLElement;\n 'serverScrim': HTMLElement;\n 'setApiKey': HTMLInputElement;\n 'setBaseUrl': HTMLInputElement;\n 'setModel': HTMLInputElement;\n 'settingsBtn': HTMLButtonElement;\n 'settingsDrawer': HTMLElement;\n 'settingsStatus': HTMLElement;\n 'srvAutoOpen': HTMLInputElement;\n 'srvConfigPath': HTMLElement;\n 'srvCopyKey': HTMLButtonElement;\n 'srvDomain': HTMLInputElement;\n 'srvDomainField': HTMLElement;\n 'srvEmail': HTMLInputElement;\n 'srvFreshKey': HTMLInputElement;\n 'srvFreshKeyRow': HTMLElement;\n 'srvHost': HTMLInputElement;\n 'srvHttps': HTMLInputElement;\n 'srvHttpsField': HTMLElement;\n 'srvHttpsStatus': HTMLElement;\n 'srvKeyHint': HTMLElement;\n 'srvKeyMasked': HTMLInputElement;\n 'srvKeySection': HTMLElement;\n 'srvLogout': HTMLButtonElement;\n 'srvMode': HTMLSelectElement;\n 'srvName': HTMLInputElement;\n 'srvPort': HTMLInputElement;\n 'srvRotate': HTMLButtonElement;\n 'srvSave': HTMLButtonElement;\n 'srvStatus': HTMLElement;\n 'srvTunnelCloudflare': HTMLInputElement;\n 'srvTunnelCloudflareStatus': HTMLElement;\n 'srvTunnelTailscale': HTMLInputElement;\n 'srvTunnelTailscaleStatus': HTMLElement;\n 'stackZone': HTMLElement;\n 'stageFilters': HTMLElement;\n 'stageFoot': HTMLElement;\n 'svgBtn': HTMLButtonElement;\n 'testSettings': HTMLButtonElement;\n 'testsToggle': HTMLButtonElement;\n 'themeBtn': HTMLButtonElement;\n 'toast': HTMLElement;\n 'tourCount': HTMLElement;\n 'tourExit': HTMLButtonElement;\n 'tourNext': HTMLButtonElement;\n 'tourPrev': HTMLButtonElement;\n 'tourTitle': HTMLElement;\n 'tourbar': HTMLElement;\n 'treeFilter': HTMLInputElement;\n 'viewTabs': HTMLElement;\n 'workflowsView': HTMLElement;\n}\n\n/** Delegated events may originate from a non-element node. */\nexport function eventElement(event: Event): HTMLElement | null {\n return event.target instanceof HTMLElement ? event.target : null;\n}\n"]}
1
+ {"version":3,"file":"dom.js","sourceRoot":"","sources":["../../src/public/js/dom.ts"],"names":[],"mappings":"AAoMA,8DAA8D;AAC9D,MAAM,UAAU,YAAY,CAAC,KAAY;IACvC,OAAO,KAAK,CAAC,MAAM,YAAY,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AACnE,CAAC","sourcesContent":["/** Element types for the app shell and view templates. */\nexport interface AppElements {\n reviewView: HTMLElement;\n cancelScanBtn: HTMLButtonElement;\n scanProgress: HTMLProgressElement;\n inspTitle: HTMLElement;\n 'aboutRemote': HTMLElement;\n 'aboutView': HTMLElement;\n 'aiDrawBtn': HTMLButtonElement;\n 'aiExplainBtn': HTMLButtonElement;\n 'analysisView': HTMLElement;\n 'askBtn': HTMLButtonElement;\n 'askInput': HTMLInputElement;\n 'blameGutter': HTMLElement;\n 'blastDesc': HTMLElement;\n 'blastMetric': HTMLElement;\n 'blastRadiusBox': HTMLElement;\n 'brandNote': HTMLElement;\n 'canvas': HTMLElement;\n 'canvasContent': HTMLElement;\n 'canvasEmpty': HTMLElement;\n 'codeAlt': HTMLElement;\n 'codeHead': HTMLElement;\n 'codeLinks': HTMLElement;\n 'codeMonaco': HTMLElement;\n 'codeTabContainer': HTMLElement;\n 'codeView': HTMLElement;\n 'copyMermaidBtn': HTMLButtonElement;\n 'creditsBar': HTMLElement;\n 'creditsBarRemove': HTMLButtonElement;\n 'creditsHide': HTMLButtonElement;\n 'creditsRemove': HTMLButtonElement;\n 'creditsShow': HTMLButtonElement;\n 'crumbs': HTMLElement;\n 'demoBtn': HTMLButtonElement;\n 'depthSelect': HTMLSelectElement;\n 'diffBaseSelect': HTMLSelectElement;\n 'diffFilesList': HTMLElement;\n 'diffHeadSelect': HTMLSelectElement;\n 'diffModeSplit': HTMLButtonElement;\n 'diffModeUnified': HTMLButtonElement;\n 'diffRunBtn': HTMLButtonElement;\n 'diffSummaryChip': HTMLElement;\n 'diffView': HTMLElement;\n 'diffViewerMain': HTMLElement;\n 'doc-top': HTMLElement;\n 'docAiNote': HTMLElement;\n 'docAskInput': HTMLInputElement;\n 'docGenBtn': HTMLButtonElement;\n 'docGenZone': HTMLElement;\n 'docOverview': HTMLElement;\n 'docReadme': HTMLElement;\n 'docToc': HTMLElement;\n 'docView': HTMLElement;\n 'drawerClose': HTMLButtonElement;\n 'drawerScrim': HTMLElement;\n 'explorer': HTMLElement;\n 'fileSidebar': HTMLElement;\n 'fileTree': HTMLElement;\n 'fitBtn': HTMLButtonElement;\n 'footCredit': HTMLElement;\n 'gitForm': HTMLFormElement;\n 'gitInput': HTMLInputElement;\n 'graphCanvas': HTMLCanvasElement;\n 'graphControls': HTMLElement;\n 'graphFit': HTMLButtonElement;\n 'graphReset': HTMLButtonElement;\n 'graphSearchInput': HTMLInputElement;\n 'graphTooltip': HTMLElement;\n 'graphView': HTMLElement;\n 'graphZoomIn': HTMLButtonElement;\n 'graphZoomOut': HTMLButtonElement;\n 'heatmapCanvas': HTMLCanvasElement;\n 'heatmapTooltip': HTMLElement;\n 'heatmapView': HTMLElement;\n 'insightsView': HTMLElement;\n 'inspBack': HTMLButtonElement;\n 'inspExplain': HTMLElement;\n 'inspLists': HTMLElement;\n 'inspPath': HTMLElement;\n 'inspRole': HTMLElement;\n 'inspSecurityTools': HTMLElement;\n 'inspStats': HTMLElement;\n 'inspector': HTMLElement;\n 'inspectorBody': HTMLElement;\n 'inspectorEmpty': HTMLElement;\n 'landing': HTMLElement;\n 'landingAbout': HTMLElement;\n 'landingError': HTMLElement;\n 'landingFoot': HTMLElement;\n 'landingStatus': HTMLElement;\n 'mcpBtn': HTMLButtonElement;\n 'mcpBtnLabel': HTMLElement;\n 'mcpClose': HTMLButtonElement;\n 'mcpCommandInput': HTMLInputElement;\n 'mcpConfig': HTMLElement;\n 'mcpConfigBlock': HTMLElement;\n 'mcpCopy': HTMLButtonElement;\n 'mcpDot': HTMLElement;\n 'mcpDrawer': HTMLElement;\n 'mcpPanelDot': HTMLElement;\n 'mcpPanelState': HTMLElement;\n 'mcpScrim': HTMLElement;\n 'mcpStart': HTMLButtonElement;\n 'mcpStateRow': HTMLElement;\n 'mcpStatus': HTMLElement;\n 'mcpStop': HTMLButtonElement;\n 'mcpTabJson': HTMLButtonElement;\n 'mcpTabToml': HTMLButtonElement;\n 'mcpToolsHint': HTMLElement;\n 'mmToggleBtn': HTMLButtonElement;\n 'mobileDetailsBtn': HTMLButtonElement;\n 'mobileDetailsClose': HTMLButtonElement;\n 'mobileFilesBtn': HTMLButtonElement;\n 'mobileFilesClose': HTMLButtonElement;\n 'mobilePanelScrim': HTMLButtonElement;\n 'modeSwitch': HTMLElement;\n 'newRepoBtn': HTMLButtonElement;\n 'nodeFilter': HTMLInputElement;\n 'pathForm': HTMLFormElement;\n 'pathInput': HTMLInputElement;\n 'pickBtn': HTMLButtonElement;\n 'pickCard': HTMLElement;\n 'repoChip': HTMLElement;\n 'saveSettings': HTMLButtonElement;\n 'setConnection': HTMLSelectElement;\n 'apiConnection': HTMLElement;\n 'hermesConnection': HTMLElement;\n 'hermesConnectionStatus': HTMLElement;\n 'hermesModel': HTMLElement;\n 'hermesProvider': HTMLElement;\n 'refreshHermes': HTMLButtonElement;\n 'aiConnectionNote': HTMLElement;\n 'sbomSearchInput': HTMLInputElement;\n 'sbomTable': HTMLElement;\n 'sbomTableBody': HTMLElement;\n 'sbomView': HTMLElement;\n 'searchOverlay': HTMLElement;\n 'searchResultList': HTMLElement;\n 'searchTriggerBtn': HTMLButtonElement;\n 'serverBtn': HTMLButtonElement;\n 'serverClose': HTMLButtonElement;\n 'serverDrawer': HTMLElement;\n 'serverModeNote': HTMLElement;\n 'serverScrim': HTMLElement;\n 'setApiKey': HTMLInputElement;\n 'setBaseUrl': HTMLInputElement;\n 'setModel': HTMLInputElement;\n 'settingsBtn': HTMLButtonElement;\n 'settingsDrawer': HTMLElement;\n 'settingsStatus': HTMLElement;\n 'srvAutoOpen': HTMLInputElement;\n 'srvConfigPath': HTMLElement;\n 'srvCopyKey': HTMLButtonElement;\n 'srvDomain': HTMLInputElement;\n 'srvDomainField': HTMLElement;\n 'srvEmail': HTMLInputElement;\n 'srvFreshKey': HTMLInputElement;\n 'srvFreshKeyRow': HTMLElement;\n 'srvHost': HTMLInputElement;\n 'srvHttps': HTMLInputElement;\n 'srvHttpsField': HTMLElement;\n 'srvHttpsStatus': HTMLElement;\n 'srvKeyHint': HTMLElement;\n 'srvKeyMasked': HTMLInputElement;\n 'srvKeySection': HTMLElement;\n 'srvLogout': HTMLButtonElement;\n 'srvMode': HTMLSelectElement;\n 'srvName': HTMLInputElement;\n 'srvPort': HTMLInputElement;\n 'srvRotate': HTMLButtonElement;\n 'srvSave': HTMLButtonElement;\n 'srvStatus': HTMLElement;\n 'srvTunnelCloudflare': HTMLInputElement;\n 'srvTunnelCloudflareStatus': HTMLElement;\n 'srvTunnelTailscale': HTMLInputElement;\n 'srvTunnelTailscaleStatus': HTMLElement;\n 'stackZone': HTMLElement;\n 'stageFilters': HTMLElement;\n 'stageFoot': HTMLElement;\n 'svgBtn': HTMLButtonElement;\n 'testSettings': HTMLButtonElement;\n 'testsToggle': HTMLButtonElement;\n 'themeBtn': HTMLButtonElement;\n 'toast': HTMLElement;\n 'tourCount': HTMLElement;\n 'tourExit': HTMLButtonElement;\n 'tourNext': HTMLButtonElement;\n 'tourPrev': HTMLButtonElement;\n 'tourTitle': HTMLElement;\n 'tourbar': HTMLElement;\n 'treeFilter': HTMLInputElement;\n 'viewTabs': HTMLElement;\n 'workflowsView': HTMLElement;\n}\n\n/** Delegated events may originate from a non-element node. */\nexport function eventElement(event: Event): HTMLElement | null {\n return event.target instanceof HTMLElement ? event.target : null;\n}\n"]}
@@ -1,9 +1,11 @@
1
1
  export declare const DEFAULT_SETTINGS: {
2
+ connection: string;
2
3
  baseUrl: string;
3
4
  apiKey: string;
4
5
  model: string;
6
+ hermesModel: string;
5
7
  };
6
- export declare function getSettings(): any;
8
+ export declare function getSettings(displayModel?: boolean): any;
7
9
  export declare function saveSettings(settings: any): void;
8
10
  export declare function isConfigured(): boolean;
9
11
  export declare function isAzureHost(baseUrl: any): boolean;
package/public/js/llm.js CHANGED
@@ -1,18 +1,23 @@
1
- // LLM settings and prompt assembly. The key lives in localStorage and only
2
- // ever travels to the local server, which forwards it to whatever
3
- // OpenAI-compatible endpoint the user pointed at.
1
+ // AI connection settings and prompt assembly. Hermes resolves its login on
2
+ // the server; API mode forwards the browser's key to the chosen endpoint.
4
3
  const STORE_KEY = 'onboarder.llm.v1';
5
4
  // No endpoint, model, or key ships with the repo. Anything saved in the
6
- // API-key drawer overrides these empty defaults (localStorage wins); without
7
- // a saved endpoint the app stays on the offline graph explanations.
5
+ // connection drawer overrides these defaults; without a connection the app
6
+ // stays on the offline graph explanations. Old settings remain in API mode.
8
7
  export const DEFAULT_SETTINGS = {
8
+ connection: 'api',
9
9
  baseUrl: '',
10
10
  apiKey: '',
11
11
  model: '',
12
+ hermesModel: '',
12
13
  };
13
- export function getSettings() {
14
+ export function getSettings(displayModel = true) {
14
15
  try {
15
- return { ...DEFAULT_SETTINGS, ...JSON.parse(localStorage.getItem(STORE_KEY) || '{}') };
16
+ const s = { ...DEFAULT_SETTINGS, ...JSON.parse(localStorage.getItem(STORE_KEY) || '{}') };
17
+ s.connection = s.connection === 'hermes' ? 'hermes' : 'api';
18
+ if (displayModel && s.connection === 'hermes')
19
+ s.model = s.hermesModel || 'Hermes';
20
+ return s;
16
21
  }
17
22
  catch {
18
23
  return { ...DEFAULT_SETTINGS };
@@ -20,6 +25,8 @@ export function getSettings() {
20
25
  }
21
26
  export function saveSettings(settings) {
22
27
  localStorage.setItem(STORE_KEY, JSON.stringify({
28
+ connection: settings.connection === 'hermes' ? 'hermes' : 'api',
29
+ hermesModel: settings.hermesModel || '',
23
30
  baseUrl: (settings.baseUrl || '').trim().replace(/\/+$/, ''),
24
31
  apiKey: settings.apiKey || '',
25
32
  model: (settings.model || '').trim(),
@@ -27,7 +34,7 @@ export function saveSettings(settings) {
27
34
  }
28
35
  export function isConfigured() {
29
36
  const s = getSettings();
30
- return Boolean(s.baseUrl && s.model);
37
+ return s.connection === 'hermes' || Boolean(s.baseUrl && s.model);
31
38
  }
32
39
  // Azure OpenAI speaks a slightly different dialect than the rest: api-key
33
40
  // header (handled server-side) and no `reasoning` request parameter.
@@ -1 +1 @@
1
- {"version":3,"file":"llm.js","sourceRoot":"","sources":["../../src/public/js/llm.ts"],"names":[],"mappings":"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","sourcesContent":["// LLM settings and prompt assembly. The key lives in localStorage and only\n// ever travels to the local server, which forwards it to whatever\n// OpenAI-compatible endpoint the user pointed at.\n\nconst STORE_KEY = 'onboarder.llm.v1';\n\n// No endpoint, model, or key ships with the repo. Anything saved in the\n// API-key drawer overrides these empty defaults (localStorage wins); without\n// a saved endpoint the app stays on the offline graph explanations.\nexport const DEFAULT_SETTINGS = {\n baseUrl: '',\n apiKey: '',\n model: '',\n};\n\nexport function getSettings() {\n try {\n return { ...DEFAULT_SETTINGS, ...JSON.parse(localStorage.getItem(STORE_KEY) || '{}') };\n } catch {\n return { ...DEFAULT_SETTINGS };\n }\n}\n\nexport function saveSettings(settings) {\n localStorage.setItem(STORE_KEY, JSON.stringify({\n baseUrl: (settings.baseUrl || '').trim().replace(/\\/+$/, ''),\n apiKey: settings.apiKey || '',\n model: (settings.model || '').trim(),\n }));\n}\n\nexport function isConfigured() {\n const s = getSettings();\n return Boolean(s.baseUrl && s.model);\n}\n\n// Azure OpenAI speaks a slightly different dialect than the rest: api-key\n// header (handled server-side) and no `reasoning` request parameter.\nexport function isAzureHost(baseUrl) {\n return /\\.openai\\.azure\\.com|\\.cognitiveservices\\.azure\\.com/i.test(baseUrl || '');\n}\n\nconst SYSTEM_PROMPT = [\n 'You are a senior engineer walking a new teammate through a codebase they have never seen.',\n 'Write like a person, not a product page: plain sentences, concrete names, no hype, no bullet-point cosplay unless a list is genuinely clearer.',\n 'Keep it under 180 words. Refer to files and functions by their real names in backticks.',\n 'If something looks risky to change, say so plainly.',\n].join(' ');\n\nexport function fileMessages({ repoName, overview, path, role, fanIn, fanOut, importers, imports, functions, exports_, source }) {\n const context = [\n `Repository: ${repoName}`,\n overview ? `Big picture: ${overview}` : '',\n '',\n `File: ${path} (role: ${role})`,\n `Imported by ${fanIn} files: ${importers.slice(0, 8).join(', ') || 'none'}`,\n `Imports ${fanOut} files: ${imports.slice(0, 8).join(', ') || 'none'}`,\n functions?.length ? `Functions: ${functions.slice(0, 14).join(', ')}` : '',\n exports_?.length ? `Exports: ${exports_.slice(0, 10).join(', ')}` : '',\n ].filter(Boolean).join('\\n');\n\n const user = source\n ? `${context}\\n\\nHere is the source, possibly truncated:\\n\\`\\`\\`\\n${source.slice(0, 12000)}\\n\\`\\`\\`\\n\\nExplain what this file does, how it fits into the repo, and what breaks if it is deleted.`\n : `${context}\\n\\n(Source was not available.) Explain what this file most likely does, how it fits into the repo, and what breaks if it is deleted. Say what you are inferring versus what the graph proves.`;\n\n return [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: user },\n ];\n}\n\nexport function overviewMessages({ repoName, overview, entries, hubs, services, cycles, orphans, languages }) {\n const user = [\n `Repository: ${repoName}`,\n `Languages: ${languages}`,\n overview ? `Static notes: ${overview}` : '',\n entries?.length ? `Entry points: ${entries.slice(0, 5).join(', ')}` : '',\n hubs?.length ? `Most-depended-on files: ${hubs.slice(0, 5).map((h) => h.path + ' (' + h.fanIn + ')').join(', ')}` : '',\n services?.length ? `Services: ${services.map((s) => s.name).join(', ')}` : '',\n cycles?.length ? `${cycles.length} circular dependencies` : '',\n orphans?.length ? `${orphans.length} files imported by nothing` : '',\n '',\n 'Give a new teammate the orientation you would give on their first day: what this thing is, where to start reading, and which files to treat with respect.',\n ].filter(Boolean).join('\\n');\n\n return [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: user },\n ];\n}\n\n// Diagram generation prompts. The model gets compact graph facts and returns\n// a Mermaid flowchart with a caption; the theme is injected by us afterwards\n// so AI drafts look exactly like the hand-drawn ones.\n\nconst DIAGRAM_PROMPT = [\n 'You turn codebase facts into Mermaid flowcharts that teach.',\n 'Output format, exactly: first line `%% caption: <one honest sentence about what the diagram shows>`, then the flowchart source. Nothing else — no fences, no commentary, no sign-off.',\n 'Rules: start with `flowchart LR` or `flowchart TD` on its own line; use short alphanumeric ids (a1, b2); put every label in \"double quotes\"; at most 36 nodes; group with subgraphs where it helps; A --> B means A depends on B; no click, style or linkStyle statements.',\n 'Where you can infer what a group of files is FOR, name the cluster by its purpose instead of repeating folder names.',\n].join(' ');\n\nexport function diagramMessages({ repoName, kind, facts }) {\n const user = [\n `Repository: ${repoName}`,\n `Diagram to draw: ${kind}`,\n 'Facts:',\n facts.join('\\n'),\n '',\n `Draw the most useful ${kind} for someone who has never seen this codebase.`,\n ].join('\\n');\n\n return [\n { role: 'system', content: DIAGRAM_PROMPT },\n { role: 'user', content: user },\n ];\n}\n\n// ---- documentation prompts -------------------------------------------------\n\nconst DOC_SYSTEM = [\n 'You write documentation for a codebase as a senior engineer would: concrete, calm, specific.',\n 'Plain sentences. Real names. No hype, no filler, no \"this file plays a crucial role\".',\n].join(' ');\n\n// Detailed reference notes for one file or folder, opened as a tab in Docs.\nconst FILE_DOC_SYSTEM = [\n 'You write detailed reference notes for one file in a codebase, as a senior engineer would for a new teammate.',\n 'Concrete and specific: real names, real numbers, no filler. Answer in Markdown with exactly these sections, in this order:',\n '## Purpose',\n '## How it fits',\n '## Key parts',\n '## Handle with care',\n 'Keep \"Key parts\" a short bullet list of the functions or exports that matter. Under 350 words total. If a section honestly has nothing to say, one line saying so.',\n].join('\\n');\n\nconst FOLDER_DOC_SYSTEM = [\n 'You write detailed reference notes for one folder in a codebase, as a senior engineer would for a new teammate.',\n 'Concrete and specific: real names, no filler. Answer in Markdown with exactly these sections, in this order:',\n '## Purpose',\n '## How the parts cooperate',\n '## The files that matter',\n '## Handle with care',\n 'Under 300 words total. Say what you infer from names and import facts versus what you know.',\n].join('\\n');\n\nexport function fileDocMessages({ repoName, overview, file }) {\n const blocks = [\n `Repository: ${repoName}`,\n overview ? `Big picture: ${overview}` : '',\n `File: ${file.path} (role: ${file.role})`,\n `Imported by ${file.fanIn} files: ${file.importers.slice(0, 8).join(', ') || 'none'}`,\n `Imports ${file.fanOut} files: ${file.imports.slice(0, 8).join(', ') || 'none'}`,\n file.functions?.length ? `Functions: ${file.functions.slice(0, 14).join(', ')}` : '',\n file.exports_?.length ? `Exports: ${file.exports_.slice(0, 10).join(', ')}` : '',\n file.source\n ? `Source, possibly truncated:\\n\\`\\`\\`\\n${file.source.slice(0, 12000)}\\n\\`\\`\\``\n : '(Source unavailable — write from the graph facts and say what you are inferring.)',\n ].filter(Boolean);\n return [\n { role: 'system', content: FILE_DOC_SYSTEM },\n { role: 'user', content: blocks.join('\\n') },\n ];\n}\n\nexport function folderDocDetailedMessages({ repoName, folder, subfolders, files }) {\n const lines = files.map((f) =>\n `- ${f.name} — ${f.fanIn} dependents, pulls in ${f.fanOut}` +\n (f.role !== 'module' ? `, role: ${f.role}` : '') +\n (f.functions.length ? `, functions: ${f.functions.slice(0, 5).join(', ')}` : '')\n );\n const user = [\n `Repository: ${repoName}`,\n `Folder: ${folder || '(repo root)'}`,\n subfolders?.length ? `Subfolders: ${subfolders.join(', ')}` : '',\n 'Files with import-graph facts:',\n lines.join('\\n') || '(no files directly here)',\n ].filter(Boolean).join('\\n');\n return [\n { role: 'system', content: FOLDER_DOC_SYSTEM },\n { role: 'user', content: user },\n ];\n}\n\nexport function repoDocMessages({ repoName, overview, readmeExcerpt, entries, hubs }) {\n const user = [\n `Repository: ${repoName}`,\n overview ? `Static analysis notes: ${overview}` : '',\n entries?.length ? `Entry points: ${entries.slice(0, 5).join(', ')}` : '',\n hubs?.length ? `Most depended-on files: ${hubs.slice(0, 5).map((h) => h.path + ' (' + h.fanIn + ')').join(', ')}` : '',\n readmeExcerpt ? `The project's own README begins:\\n\"\"\"\\n${readmeExcerpt.slice(0, 1500)}\\n\"\"\"` : '(No README found.)',\n '',\n 'Write 3–4 sentences of high-level overview for a brand-new teammate: what this project is, how it is organized at the top level, and where to start reading. Plain prose only — no headings, no bullets.',\n ].filter(Boolean).join('\\n');\n\n return [\n { role: 'system', content: DOC_SYSTEM },\n { role: 'user', content: user },\n ];\n}\n\nexport function folderDocMessages({ repoName, folder, subfolders, files }) {\n const fileLines = files.map((f) => {\n if (!f.parsed) return `- ${f.name} — not parsed (asset or data)`;\n const bits = [`${f.fanIn} dependents`, `pulls in ${f.fanOut}`];\n if (f.role !== 'module') bits.push('role: ' + f.role);\n if (f.functions.length) bits.push('functions: ' + f.functions.slice(0, 5).join(', '));\n return `- ${f.name} — ${bits.join(', ')}`;\n });\n\n const user = [\n `Repository: ${repoName}`,\n `Folder under review: ${folder || '(repo root)'}`,\n subfolders?.length ? `Subfolders inside it: ${subfolders.join(', ')}` : '',\n 'Files (with import-graph facts):',\n fileLines.join('\\n') || '(no files directly here)',\n '',\n 'Write complete documentation for this folder, in exactly this format, no preamble, no fences:',\n 'FOLDER: <3-5 sentences — what lives here, why it exists, how the parts cooperate, and what a newcomer should read first>',\n ...files.filter((f) => f.parsed).map((f) => `FILE: ${f.name}: <2-3 sentences — what this file is for, who it talks to, and what to handle with care>`),\n ].filter(Boolean).join('\\n');\n\n return [\n { role: 'system', content: DOC_SYSTEM },\n { role: 'user', content: user },\n ];\n}\n\nconst QUESTION_PROMPT = [\n 'You are a senior engineer answering a teammate\\'s question about a codebase.',\n 'Answer directly and concretely, citing real file and function names in backticks.',\n 'If the answer is not in the graph facts or source you were given, say so plainly and name the file you would open next to find out.',\n 'Keep it under 200 words unless the question genuinely needs more.',\n].join(' ');\n\n// A free-form question. When the user has a file selected, its facts and\n// source ride along; otherwise the model reasons from the repo overview.\nexport function questionMessages({ repoName, overview, question, file }) {\n const blocks = [\n `Repository: ${repoName}`,\n overview ? `Big picture: ${overview}` : '',\n ];\n\n if (file) {\n blocks.push(\n `The user is currently looking at: ${file.path} (role: ${file.role})`,\n `Imported by ${file.fanIn} files: ${file.importers.slice(0, 8).join(', ') || 'none'}`,\n `Imports ${file.fanOut} files: ${file.imports.slice(0, 8).join(', ') || 'none'}`\n );\n if (file.functions?.length) blocks.push(`Functions: ${file.functions.slice(0, 14).join(', ')}`);\n if (file.exports_?.length) blocks.push(`Exports: ${file.exports_.slice(0, 10).join(', ')}`);\n if (file.source) {\n blocks.push(`Source of ${file.path}, possibly truncated:\\n\\`\\`\\`\\n${file.source.slice(0, 12000)}\\n\\`\\`\\``);\n } else {\n blocks.push('(Source for that file was not available — answer from the graph facts and say so.)');\n }\n }\n\n blocks.push(`Question: ${question}`);\n\n return [\n { role: 'system', content: QUESTION_PROMPT },\n { role: 'user', content: blocks.filter(Boolean).join('\\n') },\n ];\n}\n\n\n// Used by the About tab to summarize a detected framework/library: the model\n// reasons from the official docs excerpt (when fetched) plus the repo's own\n// context. docsText may be empty — the prompt says so and falls back to\n// general knowledge.\nexport function stackSummaryMessages({ repoName, item, docsText, repoNote }) {\n const system =\n 'You are a senior developer writing compact field notes for a codebase onboarding tool. Be specific and honest; distinguish between what the docs state and what you infer. No headings, no \"Sure\" / \"Absolutely\" openers, no filler.';\n const declared = item.version\n ? `The project declares ${item.name} ${item.version} (${item.category}, ${item.lang}${item.dev ? ', dev dependency' : ''}).`\n : `The project uses ${item.name} (${item.category}, ${item.lang}).`;\n const user = [\n `Repository: ${repoName}.`,\n declared,\n `Repo context: ${repoNote}`,\n docsText\n ? `Official docs excerpt:\\n${docsText}`\n : 'No official docs could be fetched for this dependency — rely on your own knowledge and say so in one short clause.',\n 'In 2-4 sentences: what this is, why the project likely chose it, and anything a new teammate should know about using it here.',\n ].join('\\n\\n');\n return [{ role: 'system', content: system }, { role: 'user', content: user }];\n}\n\n// The Deep Analysis report, read back by the model. Two jobs in one builder: a\n// report is either *explained* (no question — prioritize what to fix) or asked\n// about (\"is the eval finding actually reachable?\"). The engine list is part of\n// the context on purpose: what the engines could not see is often the real\n// answer, and a model that does not know a tool was missing will invent\n// confidence it has not earned.\nconst ANALYSIS_SYSTEM = [\n 'You are a senior engineer walking the owner of a codebase through a static-analysis report.',\n 'Be concrete and calm. Name real files, rules and line numbers in backticks.',\n 'Prioritize: what must be fixed now, what can wait, and what is noise. Say plainly when a finding looks like a false positive and why.',\n 'Never invent a finding that is not in the list you were given. If the list is empty, say so — and say what that does and does not prove.',\n 'If some engines did not run or are not installed, say what that leaves unchecked.',\n 'Keep it under 220 words unless the question genuinely needs more.',\n].join(' ');\n\nexport function analysisMessages({ repoName, grade, score, counts, engines, findings, question }) {\n const engineLines = (engines || []).map((e) => {\n if (e.ran) return `- ${e.label}: ran, ${e.findings} finding(s) in ${e.ms}ms`;\n if (e.failed) return `- ${e.label}: ran but failed — ${e.failure || 'no reason given'}`;\n if (e.available) return `- ${e.label}: installed but not run`;\n return `- ${e.label}: NOT INSTALLED (${e.reason || 'unknown'})`;\n });\n\n const findingLines = (findings || []).slice(0, 40).map(\n (f) => `- [${f.severity}] ${f.path}:${f.line} — ${f.message} (${f.tool} · ${f.rule})`,\n );\n\n const user = [\n `Repository: ${repoName}`,\n grade ? `Built-in security grade: ${grade} (${score}/100)` : '',\n counts\n ? `Findings: ${counts.total} total — ${counts.critical} critical, ${counts.high} high, ${counts.medium} medium, ${counts.low} low. ${counts.external} came from external engines, ${counts.builtin} from the built-in scanner.`\n : '',\n engineLines.length ? `Engines:\\n${engineLines.join('\\n')}` : '',\n findingLines.length\n ? `Findings, worst first:\\n${findingLines.join('\\n')}`\n : 'No findings were reported by anything.',\n question\n ? `Question: ${question}`\n : 'Explain this report: what matters most, what to fix first, and what the engines could not see.',\n ].filter(Boolean).join('\\n\\n');\n\n return [\n { role: 'system', content: ANALYSIS_SYSTEM },\n { role: 'user', content: user },\n ];\n}\n\nexport async function streamTokens(container, text) {\n container.innerHTML = '';\n for (let i = 0; i < text.length; i++) {\n container.innerHTML += text[i];\n await new Promise(r => setTimeout(r, 10));\n }\n}\n\n"]}
1
+ {"version":3,"file":"llm.js","sourceRoot":"","sources":["../../src/public/js/llm.ts"],"names":[],"mappings":"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","sourcesContent":["// AI connection settings and prompt assembly. Hermes resolves its login on\n// the server; API mode forwards the browser's key to the chosen endpoint.\n\nconst STORE_KEY = 'onboarder.llm.v1';\n\n// No endpoint, model, or key ships with the repo. Anything saved in the\n// connection drawer overrides these defaults; without a connection the app\n// stays on the offline graph explanations. Old settings remain in API mode.\nexport const DEFAULT_SETTINGS = {\n connection: 'api',\n baseUrl: '',\n apiKey: '',\n model: '',\n hermesModel: '',\n};\n\nexport function getSettings(displayModel = true) {\n try {\n const s = { ...DEFAULT_SETTINGS, ...JSON.parse(localStorage.getItem(STORE_KEY) || '{}') };\n s.connection = s.connection === 'hermes' ? 'hermes' : 'api';\n if (displayModel && s.connection === 'hermes') s.model = s.hermesModel || 'Hermes';\n return s;\n } catch {\n return { ...DEFAULT_SETTINGS };\n }\n}\n\nexport function saveSettings(settings) {\n localStorage.setItem(STORE_KEY, JSON.stringify({\n connection: settings.connection === 'hermes' ? 'hermes' : 'api',\n hermesModel: settings.hermesModel || '',\n baseUrl: (settings.baseUrl || '').trim().replace(/\\/+$/, ''),\n apiKey: settings.apiKey || '',\n model: (settings.model || '').trim(),\n }));\n}\n\nexport function isConfigured() {\n const s = getSettings();\n return s.connection === 'hermes' || Boolean(s.baseUrl && s.model);\n}\n\n// Azure OpenAI speaks a slightly different dialect than the rest: api-key\n// header (handled server-side) and no `reasoning` request parameter.\nexport function isAzureHost(baseUrl) {\n return /\\.openai\\.azure\\.com|\\.cognitiveservices\\.azure\\.com/i.test(baseUrl || '');\n}\n\nconst SYSTEM_PROMPT = [\n 'You are a senior engineer walking a new teammate through a codebase they have never seen.',\n 'Write like a person, not a product page: plain sentences, concrete names, no hype, no bullet-point cosplay unless a list is genuinely clearer.',\n 'Keep it under 180 words. Refer to files and functions by their real names in backticks.',\n 'If something looks risky to change, say so plainly.',\n].join(' ');\n\nexport function fileMessages({ repoName, overview, path, role, fanIn, fanOut, importers, imports, functions, exports_, source }) {\n const context = [\n `Repository: ${repoName}`,\n overview ? `Big picture: ${overview}` : '',\n '',\n `File: ${path} (role: ${role})`,\n `Imported by ${fanIn} files: ${importers.slice(0, 8).join(', ') || 'none'}`,\n `Imports ${fanOut} files: ${imports.slice(0, 8).join(', ') || 'none'}`,\n functions?.length ? `Functions: ${functions.slice(0, 14).join(', ')}` : '',\n exports_?.length ? `Exports: ${exports_.slice(0, 10).join(', ')}` : '',\n ].filter(Boolean).join('\\n');\n\n const user = source\n ? `${context}\\n\\nHere is the source, possibly truncated:\\n\\`\\`\\`\\n${source.slice(0, 12000)}\\n\\`\\`\\`\\n\\nExplain what this file does, how it fits into the repo, and what breaks if it is deleted.`\n : `${context}\\n\\n(Source was not available.) Explain what this file most likely does, how it fits into the repo, and what breaks if it is deleted. Say what you are inferring versus what the graph proves.`;\n\n return [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: user },\n ];\n}\n\nexport function overviewMessages({ repoName, overview, entries, hubs, services, cycles, orphans, languages }) {\n const user = [\n `Repository: ${repoName}`,\n `Languages: ${languages}`,\n overview ? `Static notes: ${overview}` : '',\n entries?.length ? `Entry points: ${entries.slice(0, 5).join(', ')}` : '',\n hubs?.length ? `Most-depended-on files: ${hubs.slice(0, 5).map((h) => h.path + ' (' + h.fanIn + ')').join(', ')}` : '',\n services?.length ? `Services: ${services.map((s) => s.name).join(', ')}` : '',\n cycles?.length ? `${cycles.length} circular dependencies` : '',\n orphans?.length ? `${orphans.length} files imported by nothing` : '',\n '',\n 'Give a new teammate the orientation you would give on their first day: what this thing is, where to start reading, and which files to treat with respect.',\n ].filter(Boolean).join('\\n');\n\n return [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: user },\n ];\n}\n\n// Diagram generation prompts. The model gets compact graph facts and returns\n// a Mermaid flowchart with a caption; the theme is injected by us afterwards\n// so AI drafts look exactly like the hand-drawn ones.\n\nconst DIAGRAM_PROMPT = [\n 'You turn codebase facts into Mermaid flowcharts that teach.',\n 'Output format, exactly: first line `%% caption: <one honest sentence about what the diagram shows>`, then the flowchart source. Nothing else — no fences, no commentary, no sign-off.',\n 'Rules: start with `flowchart LR` or `flowchart TD` on its own line; use short alphanumeric ids (a1, b2); put every label in \"double quotes\"; at most 36 nodes; group with subgraphs where it helps; A --> B means A depends on B; no click, style or linkStyle statements.',\n 'Where you can infer what a group of files is FOR, name the cluster by its purpose instead of repeating folder names.',\n].join(' ');\n\nexport function diagramMessages({ repoName, kind, facts }) {\n const user = [\n `Repository: ${repoName}`,\n `Diagram to draw: ${kind}`,\n 'Facts:',\n facts.join('\\n'),\n '',\n `Draw the most useful ${kind} for someone who has never seen this codebase.`,\n ].join('\\n');\n\n return [\n { role: 'system', content: DIAGRAM_PROMPT },\n { role: 'user', content: user },\n ];\n}\n\n// ---- documentation prompts -------------------------------------------------\n\nconst DOC_SYSTEM = [\n 'You write documentation for a codebase as a senior engineer would: concrete, calm, specific.',\n 'Plain sentences. Real names. No hype, no filler, no \"this file plays a crucial role\".',\n].join(' ');\n\n// Detailed reference notes for one file or folder, opened as a tab in Docs.\nconst FILE_DOC_SYSTEM = [\n 'You write detailed reference notes for one file in a codebase, as a senior engineer would for a new teammate.',\n 'Concrete and specific: real names, real numbers, no filler. Answer in Markdown with exactly these sections, in this order:',\n '## Purpose',\n '## How it fits',\n '## Key parts',\n '## Handle with care',\n 'Keep \"Key parts\" a short bullet list of the functions or exports that matter. Under 350 words total. If a section honestly has nothing to say, one line saying so.',\n].join('\\n');\n\nconst FOLDER_DOC_SYSTEM = [\n 'You write detailed reference notes for one folder in a codebase, as a senior engineer would for a new teammate.',\n 'Concrete and specific: real names, no filler. Answer in Markdown with exactly these sections, in this order:',\n '## Purpose',\n '## How the parts cooperate',\n '## The files that matter',\n '## Handle with care',\n 'Under 300 words total. Say what you infer from names and import facts versus what you know.',\n].join('\\n');\n\nexport function fileDocMessages({ repoName, overview, file }) {\n const blocks = [\n `Repository: ${repoName}`,\n overview ? `Big picture: ${overview}` : '',\n `File: ${file.path} (role: ${file.role})`,\n `Imported by ${file.fanIn} files: ${file.importers.slice(0, 8).join(', ') || 'none'}`,\n `Imports ${file.fanOut} files: ${file.imports.slice(0, 8).join(', ') || 'none'}`,\n file.functions?.length ? `Functions: ${file.functions.slice(0, 14).join(', ')}` : '',\n file.exports_?.length ? `Exports: ${file.exports_.slice(0, 10).join(', ')}` : '',\n file.source\n ? `Source, possibly truncated:\\n\\`\\`\\`\\n${file.source.slice(0, 12000)}\\n\\`\\`\\``\n : '(Source unavailable — write from the graph facts and say what you are inferring.)',\n ].filter(Boolean);\n return [\n { role: 'system', content: FILE_DOC_SYSTEM },\n { role: 'user', content: blocks.join('\\n') },\n ];\n}\n\nexport function folderDocDetailedMessages({ repoName, folder, subfolders, files }) {\n const lines = files.map((f) =>\n `- ${f.name} — ${f.fanIn} dependents, pulls in ${f.fanOut}` +\n (f.role !== 'module' ? `, role: ${f.role}` : '') +\n (f.functions.length ? `, functions: ${f.functions.slice(0, 5).join(', ')}` : '')\n );\n const user = [\n `Repository: ${repoName}`,\n `Folder: ${folder || '(repo root)'}`,\n subfolders?.length ? `Subfolders: ${subfolders.join(', ')}` : '',\n 'Files with import-graph facts:',\n lines.join('\\n') || '(no files directly here)',\n ].filter(Boolean).join('\\n');\n return [\n { role: 'system', content: FOLDER_DOC_SYSTEM },\n { role: 'user', content: user },\n ];\n}\n\nexport function repoDocMessages({ repoName, overview, readmeExcerpt, entries, hubs }) {\n const user = [\n `Repository: ${repoName}`,\n overview ? `Static analysis notes: ${overview}` : '',\n entries?.length ? `Entry points: ${entries.slice(0, 5).join(', ')}` : '',\n hubs?.length ? `Most depended-on files: ${hubs.slice(0, 5).map((h) => h.path + ' (' + h.fanIn + ')').join(', ')}` : '',\n readmeExcerpt ? `The project's own README begins:\\n\"\"\"\\n${readmeExcerpt.slice(0, 1500)}\\n\"\"\"` : '(No README found.)',\n '',\n 'Write 3–4 sentences of high-level overview for a brand-new teammate: what this project is, how it is organized at the top level, and where to start reading. Plain prose only — no headings, no bullets.',\n ].filter(Boolean).join('\\n');\n\n return [\n { role: 'system', content: DOC_SYSTEM },\n { role: 'user', content: user },\n ];\n}\n\nexport function folderDocMessages({ repoName, folder, subfolders, files }) {\n const fileLines = files.map((f) => {\n if (!f.parsed) return `- ${f.name} — not parsed (asset or data)`;\n const bits = [`${f.fanIn} dependents`, `pulls in ${f.fanOut}`];\n if (f.role !== 'module') bits.push('role: ' + f.role);\n if (f.functions.length) bits.push('functions: ' + f.functions.slice(0, 5).join(', '));\n return `- ${f.name} — ${bits.join(', ')}`;\n });\n\n const user = [\n `Repository: ${repoName}`,\n `Folder under review: ${folder || '(repo root)'}`,\n subfolders?.length ? `Subfolders inside it: ${subfolders.join(', ')}` : '',\n 'Files (with import-graph facts):',\n fileLines.join('\\n') || '(no files directly here)',\n '',\n 'Write complete documentation for this folder, in exactly this format, no preamble, no fences:',\n 'FOLDER: <3-5 sentences — what lives here, why it exists, how the parts cooperate, and what a newcomer should read first>',\n ...files.filter((f) => f.parsed).map((f) => `FILE: ${f.name}: <2-3 sentences — what this file is for, who it talks to, and what to handle with care>`),\n ].filter(Boolean).join('\\n');\n\n return [\n { role: 'system', content: DOC_SYSTEM },\n { role: 'user', content: user },\n ];\n}\n\nconst QUESTION_PROMPT = [\n 'You are a senior engineer answering a teammate\\'s question about a codebase.',\n 'Answer directly and concretely, citing real file and function names in backticks.',\n 'If the answer is not in the graph facts or source you were given, say so plainly and name the file you would open next to find out.',\n 'Keep it under 200 words unless the question genuinely needs more.',\n].join(' ');\n\n// A free-form question. When the user has a file selected, its facts and\n// source ride along; otherwise the model reasons from the repo overview.\nexport function questionMessages({ repoName, overview, question, file }) {\n const blocks = [\n `Repository: ${repoName}`,\n overview ? `Big picture: ${overview}` : '',\n ];\n\n if (file) {\n blocks.push(\n `The user is currently looking at: ${file.path} (role: ${file.role})`,\n `Imported by ${file.fanIn} files: ${file.importers.slice(0, 8).join(', ') || 'none'}`,\n `Imports ${file.fanOut} files: ${file.imports.slice(0, 8).join(', ') || 'none'}`\n );\n if (file.functions?.length) blocks.push(`Functions: ${file.functions.slice(0, 14).join(', ')}`);\n if (file.exports_?.length) blocks.push(`Exports: ${file.exports_.slice(0, 10).join(', ')}`);\n if (file.source) {\n blocks.push(`Source of ${file.path}, possibly truncated:\\n\\`\\`\\`\\n${file.source.slice(0, 12000)}\\n\\`\\`\\``);\n } else {\n blocks.push('(Source for that file was not available — answer from the graph facts and say so.)');\n }\n }\n\n blocks.push(`Question: ${question}`);\n\n return [\n { role: 'system', content: QUESTION_PROMPT },\n { role: 'user', content: blocks.filter(Boolean).join('\\n') },\n ];\n}\n\n\n// Used by the About tab to summarize a detected framework/library: the model\n// reasons from the official docs excerpt (when fetched) plus the repo's own\n// context. docsText may be empty — the prompt says so and falls back to\n// general knowledge.\nexport function stackSummaryMessages({ repoName, item, docsText, repoNote }) {\n const system =\n 'You are a senior developer writing compact field notes for a codebase onboarding tool. Be specific and honest; distinguish between what the docs state and what you infer. No headings, no \"Sure\" / \"Absolutely\" openers, no filler.';\n const declared = item.version\n ? `The project declares ${item.name} ${item.version} (${item.category}, ${item.lang}${item.dev ? ', dev dependency' : ''}).`\n : `The project uses ${item.name} (${item.category}, ${item.lang}).`;\n const user = [\n `Repository: ${repoName}.`,\n declared,\n `Repo context: ${repoNote}`,\n docsText\n ? `Official docs excerpt:\\n${docsText}`\n : 'No official docs could be fetched for this dependency — rely on your own knowledge and say so in one short clause.',\n 'In 2-4 sentences: what this is, why the project likely chose it, and anything a new teammate should know about using it here.',\n ].join('\\n\\n');\n return [{ role: 'system', content: system }, { role: 'user', content: user }];\n}\n\n// The Deep Analysis report, read back by the model. Two jobs in one builder: a\n// report is either *explained* (no question — prioritize what to fix) or asked\n// about (\"is the eval finding actually reachable?\"). The engine list is part of\n// the context on purpose: what the engines could not see is often the real\n// answer, and a model that does not know a tool was missing will invent\n// confidence it has not earned.\nconst ANALYSIS_SYSTEM = [\n 'You are a senior engineer walking the owner of a codebase through a static-analysis report.',\n 'Be concrete and calm. Name real files, rules and line numbers in backticks.',\n 'Prioritize: what must be fixed now, what can wait, and what is noise. Say plainly when a finding looks like a false positive and why.',\n 'Never invent a finding that is not in the list you were given. If the list is empty, say so — and say what that does and does not prove.',\n 'If some engines did not run or are not installed, say what that leaves unchecked.',\n 'Keep it under 220 words unless the question genuinely needs more.',\n].join(' ');\n\nexport function analysisMessages({ repoName, grade, score, counts, engines, findings, question }) {\n const engineLines = (engines || []).map((e) => {\n if (e.ran) return `- ${e.label}: ran, ${e.findings} finding(s) in ${e.ms}ms`;\n if (e.failed) return `- ${e.label}: ran but failed — ${e.failure || 'no reason given'}`;\n if (e.available) return `- ${e.label}: installed but not run`;\n return `- ${e.label}: NOT INSTALLED (${e.reason || 'unknown'})`;\n });\n\n const findingLines = (findings || []).slice(0, 40).map(\n (f) => `- [${f.severity}] ${f.path}:${f.line} — ${f.message} (${f.tool} · ${f.rule})`,\n );\n\n const user = [\n `Repository: ${repoName}`,\n grade ? `Built-in security grade: ${grade} (${score}/100)` : '',\n counts\n ? `Findings: ${counts.total} total — ${counts.critical} critical, ${counts.high} high, ${counts.medium} medium, ${counts.low} low. ${counts.external} came from external engines, ${counts.builtin} from the built-in scanner.`\n : '',\n engineLines.length ? `Engines:\\n${engineLines.join('\\n')}` : '',\n findingLines.length\n ? `Findings, worst first:\\n${findingLines.join('\\n')}`\n : 'No findings were reported by anything.',\n question\n ? `Question: ${question}`\n : 'Explain this report: what matters most, what to fix first, and what the engines could not see.',\n ].filter(Boolean).join('\\n\\n');\n\n return [\n { role: 'system', content: ANALYSIS_SYSTEM },\n { role: 'user', content: user },\n ];\n}\n\nexport async function streamTokens(container, text) {\n container.innerHTML = '';\n for (let i = 0; i < text.length; i++) {\n container.innerHTML += text[i];\n await new Promise(r => setTimeout(r, 10));\n }\n}\n"]}
@@ -5,7 +5,7 @@
5
5
  // browser-picked folder reads from the File System Access handle it kept. Every
6
6
  // caller that wants file text goes through here, so neither the docs view nor
7
7
  // the code tab has to know which kind of repo it is looking at.
8
- import { roleOf, scanIndex } from '/shared/analyzer/graph.js';
8
+ import { roleOf, scanIndex } from '../../shared/analyzer/graph.js';
9
9
  import { state } from './state.js';
10
10
  import { fetchFileText } from './api.js';
11
11
  export async function readRepoFile(path) {
@@ -1 +1 @@
1
- {"version":3,"file":"repoFiles.js","sourceRoot":"","sources":["../../src/public/js/repoFiles.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,iCAAiC;AACjC,EAAE;AACF,sEAAsE;AACtE,gFAAgF;AAChF,8EAA8E;AAC9E,gEAAgE;AAEhE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAI;IACrC,IAAI,KAAK,CAAC,aAAa;QAAE,OAAO,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/D,IAAI,KAAK,CAAC,MAAM;QAAE,OAAO,aAAa,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC3D,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;AAClD,CAAC;AAED,8EAA8E;AAC9E,+EAA+E;AAC/E,oDAAoD;AACpD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAI;IAC1C,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAChD,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,GAAG,EAAE,CAAC;IACd,CAAC;IACD,OAAO;QACL,IAAI;QACJ,qEAAqE;QACrE,8EAA8E;QAC9E,sEAAsE;QACtE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC;QAC/B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QACnC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;QACrC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE;QAC5C,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE;QAC1C,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7C,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC1C,MAAM;KACP,CAAC;AACJ,CAAC","sourcesContent":["// Reading a file out of whichever repo is loaded, and packing up what the\n// prompts need to know about it.\n//\n// Two sources, one signature: a server-side scan fetches over HTTP, a\n// browser-picked folder reads from the File System Access handle it kept. Every\n// caller that wants file text goes through here, so neither the docs view nor\n// the code tab has to know which kind of repo it is looking at.\n\nimport { roleOf, scanIndex } from '/shared/analyzer/graph.js';\nimport { state } from './state.js';\nimport { fetchFileText } from './api.js';\n\nexport async function readRepoFile(path) {\n if (state.browserSource) return state.browserSource.read(path);\n if (state.scanId) return fetchFileText(state.scanId, path);\n throw new Error('no file source for this repo');\n}\n\n// Everything the AI prompts want about one file: its place in the graph, plus\n// its source if we can get it. A failed read is not an error — the prompts are\n// written to work from the graph alone, and say so.\nexport async function gatherFileContext(path) {\n const file = scanIndex(state.scan).fileAt(path);\n let source = '';\n try {\n source = await readRepoFile(path);\n } catch {\n source = '';\n }\n return {\n path,\n // Read from the graph, not from whatever the inspector happens to be\n // showing. This used to be `document.querySelector('#inspRole').textContent`,\n // which is why one caller overwrote the field immediately afterwards.\n role: roleOf(path, state.facts),\n fanIn: state.facts.fanIn[path] || 0,\n fanOut: state.facts.fanOut[path] || 0,\n importers: state.facts.importers[path] || [],\n imports: state.facts.importsOf[path] || [],\n functions: file?.functions.map((f) => f.name),\n exports_: file?.exports.map((e) => e.name),\n source,\n };\n}\n"]}
1
+ {"version":3,"file":"repoFiles.js","sourceRoot":"","sources":["../../src/public/js/repoFiles.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,iCAAiC;AACjC,EAAE;AACF,sEAAsE;AACtE,gFAAgF;AAChF,8EAA8E;AAC9E,gEAAgE;AAEhE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAI;IACrC,IAAI,KAAK,CAAC,aAAa;QAAE,OAAO,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/D,IAAI,KAAK,CAAC,MAAM;QAAE,OAAO,aAAa,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC3D,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;AAClD,CAAC;AAED,8EAA8E;AAC9E,+EAA+E;AAC/E,oDAAoD;AACpD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAI;IAC1C,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAChD,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,GAAG,EAAE,CAAC;IACd,CAAC;IACD,OAAO;QACL,IAAI;QACJ,qEAAqE;QACrE,8EAA8E;QAC9E,sEAAsE;QACtE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC;QAC/B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QACnC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;QACrC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE;QAC5C,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE;QAC1C,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7C,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC1C,MAAM;KACP,CAAC;AACJ,CAAC","sourcesContent":["// Reading a file out of whichever repo is loaded, and packing up what the\n// prompts need to know about it.\n//\n// Two sources, one signature: a server-side scan fetches over HTTP, a\n// browser-picked folder reads from the File System Access handle it kept. Every\n// caller that wants file text goes through here, so neither the docs view nor\n// the code tab has to know which kind of repo it is looking at.\n\nimport { roleOf, scanIndex } from '../../shared/analyzer/graph.js';\nimport { state } from './state.js';\nimport { fetchFileText } from './api.js';\n\nexport async function readRepoFile(path) {\n if (state.browserSource) return state.browserSource.read(path);\n if (state.scanId) return fetchFileText(state.scanId, path);\n throw new Error('no file source for this repo');\n}\n\n// Everything the AI prompts want about one file: its place in the graph, plus\n// its source if we can get it. A failed read is not an error — the prompts are\n// written to work from the graph alone, and say so.\nexport async function gatherFileContext(path) {\n const file = scanIndex(state.scan).fileAt(path);\n let source = '';\n try {\n source = await readRepoFile(path);\n } catch {\n source = '';\n }\n return {\n path,\n // Read from the graph, not from whatever the inspector happens to be\n // showing. This used to be `document.querySelector('#inspRole').textContent`,\n // which is why one caller overwrote the field immediately afterwards.\n role: roleOf(path, state.facts),\n fanIn: state.facts.fanIn[path] || 0,\n fanOut: state.facts.fanOut[path] || 0,\n importers: state.facts.importers[path] || [],\n imports: state.facts.importsOf[path] || [],\n functions: file?.functions.map((f) => f.name),\n exports_: file?.exports.map((e) => e.name),\n source,\n };\n}\n"]}
@@ -125,7 +125,7 @@ export function renderReviewView(container, context) {
125
125
  <div class="review-finding-list">${findingMarkup}</div></section>
126
126
  <aside class="review-aside"><section class="review-card"><span class="workbench-eyebrow">BEFORE YOU MERGE</span><h2>Review checklist</h2>${report.checks.map(c => `<div class="review-check"><span class="review-check-icon check-${c.status}" aria-label="${c.status}">${c.status === 'pass' ? '✓' : c.status === 'skip' ? '−' : '!'}</span><div><strong>${esc(c.label)}</strong><p>${esc(c.detail)}</p></div></div>`).join('')}</section><section class="review-card"><span class="workbench-eyebrow">DEPENDENCY CONTEXT</span><h2>Potential impact</h2><p class="review-small">${affected.size ? [...affected].slice(0, 8).map(p => `<button class="review-impact-file linklike" data-path="${esc(p)}">${esc(p)}</button>`).join('') : 'No additional dependents found in the last scan.'}</p>${affected.size > 8 ? `<p class="review-small">And ${affected.size - 8} more dependent files.</p>` : ''}<p class="review-small">Rescan after structural edits. For branch ranges, this graph may differ from the compared revision.</p></section></aside></div>
127
127
  <section class="review-card review-walkthrough"><div class="review-section-heading"><div><span class="workbench-eyebrow">CHANGE WALKTHROUGH</span><h2>Start with the riskiest files</h2></div><span class="review-small">Risk is an estimate</span></div><div class="review-table-wrap"><table><thead><tr><th>File</th><th>Change</th><th>Lines</th><th>Risk</th><th>Review</th></tr></thead><tbody>${report.files.map(f => `<tr><td><button class="linklike" data-path="${esc(f.path)}">${esc(f.path)}</button><small>${esc(f.kind)}${f.instructions.length ? `<br>Guidelines: ${f.instructions.map(esc).join('; ')}` : ''}</small></td><td>${esc(f.status)}</td><td><span class="review-add">+${f.additions}</span> <span class="review-del">−${f.deletions}</span></td><td><span class="review-risk risk-${f.risk}">${f.risk}</span></td><td>${f.skipped ? esc(f.skipped) : `${f.findings} findings`}</td></tr>`).join('')}</tbody></table></div></section>
128
- <section class="review-card review-ai"><div class="review-section-heading"><div><span class="workbench-eyebrow">OPTIONAL AI FOLLOW-UP</span><h2>A reviewer’s brief</h2><p class="review-small">Send this redacted report and repository guidelines to your configured AI provider for a summary and testing plan.</p></div><button class="btn" data-action="ai"${!isConfigured() || !report.files.length ? ' disabled' : ''}>${s.aiText ? 'Generate again' : 'Generate brief'}</button></div>${!isConfigured() ? '<p class="review-small">Configure an endpoint and model in API key to enable this.</p>' : ''}<div class="review-ai-text" aria-live="polite">${esc(s.aiText)}</div></section>
128
+ <section class="review-card review-ai"><div class="review-section-heading"><div><span class="workbench-eyebrow">OPTIONAL AI FOLLOW-UP</span><h2>A reviewer’s brief</h2><p class="review-small">Send this redacted report and repository guidelines to your configured AI provider for a summary and testing plan.</p></div><button class="btn" data-action="ai"${!isConfigured() || !report.files.length ? ' disabled' : ''}>${s.aiText ? 'Generate again' : 'Generate brief'}</button></div>${!isConfigured() ? '<p class="review-small">Choose Hermes or an endpoint in AI connection to enable this.</p>' : ''}<div class="review-ai-text" aria-live="polite">${esc(s.aiText)}</div></section>
129
129
  <details class="review-limitations"><summary>What this review covers</summary>${report.coverage.limitations.map(l => `<p>${esc(l)}</p>`).join('')}<p>Exports contain fresh findings regardless of local acknowledgements. Nothing is applied to source files or posted to a pull request.</p></details>`;
130
130
  results.querySelector('#reviewFilter').oninput = event => { s.query = event.target.value; draw(true); };
131
131
  results.querySelector('[data-filter="severity"]').onchange = event => { s.severity = event.target.value; draw(true); };