@agentskit/doc-bridge 1.5.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/action.yml +1 -1
- package/dist/cli/program.js +83 -33
- package/dist/cli/program.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +83 -33
- package/dist/index.js.map +1 -1
- package/mcpb/manifest.json +1 -1
- package/package.json +43 -46
- package/skills/doc-bridge-handoff/scripts/resolve-handoff.mjs +1 -1
- package/src/report/html.ts +82 -30
- package/src/safety/repository.ts +1 -1
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Replace the offline HTML report with a progressive, read-only architecture viewer. The report now includes grouped SVG topology by level, documentation drift, risk/hotspot, and evidence lenses, deterministic heuristic signals, selected-entity evidence details, Jest-like diagnostics, and explicit analyzer coverage boundaries.
|
|
8
|
+
|
|
9
|
+
## 1.5.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Exclude Turbo cache files from repository discovery by default.
|
|
14
|
+
|
|
3
15
|
## 1.5.1
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/action.yml
CHANGED
package/dist/cli/program.js
CHANGED
|
@@ -1439,7 +1439,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
|
|
|
1439
1439
|
};
|
|
1440
1440
|
|
|
1441
1441
|
// src/version.ts
|
|
1442
|
-
var PACKAGE_VERSION = "1.
|
|
1442
|
+
var PACKAGE_VERSION = "1.6.0";
|
|
1443
1443
|
|
|
1444
1444
|
// src/index-builder/capabilities.ts
|
|
1445
1445
|
var renderCapabilitiesJson = (config, index, paths) => {
|
|
@@ -1951,7 +1951,7 @@ var discoverNxProjects = (root, config) => {
|
|
|
1951
1951
|
for (const target of Object.keys(targetRecord)) targets.add(target);
|
|
1952
1952
|
}
|
|
1953
1953
|
if (!isProjectJson && isRecord(json.scripts)) {
|
|
1954
|
-
for (const
|
|
1954
|
+
for (const script2 of Object.keys(json.scripts)) targets.add(script2);
|
|
1955
1955
|
}
|
|
1956
1956
|
const id = packageId(projectName2);
|
|
1957
1957
|
const rank = isProjectJson ? 2 : 1;
|
|
@@ -2969,7 +2969,7 @@ import * as ts from "typescript";
|
|
|
2969
2969
|
import { lstatSync as lstatSync3, readdirSync as readdirSync3, realpathSync as realpathSync7, statSync as statSync4 } from "fs";
|
|
2970
2970
|
import { isAbsolute as isAbsolute5, relative as relative5, resolve as resolve7, sep as sep6 } from "path";
|
|
2971
2971
|
import { minimatch as minimatch4 } from "minimatch";
|
|
2972
|
-
var DEFAULT_SAFETY_EXCLUDES = ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/build/**", "**/coverage/**", "**/.doc-bridge/**", "**/.env", "**/.env.*", "**/*secret*", "**/*credential*", "**/*.pem", "**/*.key"];
|
|
2972
|
+
var DEFAULT_SAFETY_EXCLUDES = ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/build/**", "**/coverage/**", "**/.doc-bridge/**", "**/.turbo/**", "**/.env", "**/.env.*", "**/*secret*", "**/*credential*", "**/*.pem", "**/*.key"];
|
|
2973
2973
|
var containedPath = (root, candidate) => {
|
|
2974
2974
|
const projectRoot = realpathSync7.native(resolve7(root));
|
|
2975
2975
|
const unresolved = resolve7(projectRoot, candidate);
|
|
@@ -6428,40 +6428,90 @@ var reconcileKnowledge = (observed, declared) => {
|
|
|
6428
6428
|
// src/report/html.ts
|
|
6429
6429
|
var escapeHtml = (value) => String(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
6430
6430
|
var anchor = (prefix, value) => `${prefix}-${value.replace(/[^A-Za-z0-9_-]+/g, "-")}`;
|
|
6431
|
-
var evidenceText = (evidence2, includeSnippets) => {
|
|
6432
|
-
const location = `${evidence2.path}${evidence2.lineStart ? `:${evidence2.lineStart}${evidence2.lineEnd && evidence2.lineEnd !== evidence2.lineStart ? `-${evidence2.lineEnd}` : ""}` : ""}`;
|
|
6433
|
-
return `${location}${includeSnippets && evidence2.context ? ` \u2014 ${redactSecrets(evidence2.context)}` : ""}`;
|
|
6434
|
-
};
|
|
6435
|
-
var errorPage = (message) => `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Doc Bridge report error</title><style>body{font:16px system-ui;margin:3rem;color:#311}main{max-width:60rem;margin:auto;border:1px solid #d99;padding:2rem;border-radius:8px;background:#fff8f8}code{white-space:pre-wrap}</style></head><body><main><h1>Doc Bridge report unavailable</h1><p>The saved snapshot/report could not be rendered.</p><code>${escapeHtml(message)}</code><p>Run <code>ak-docs check</code> to regenerate valid artifacts.</p></main></body></html>`;
|
|
6436
6431
|
var embeddedJson = (value) => JSON.stringify(value).replaceAll("<", "\\u003c");
|
|
6437
|
-
var
|
|
6438
|
-
var
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
}
|
|
6432
|
+
var errorPage = (message) => `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Doc Bridge report error</title><style>body{font:16px system-ui;margin:3rem;color:#311}main{max-width:60rem;margin:auto;border:1px solid #d99;padding:2rem;border-radius:8px;background:#fff8f8}code{white-space:pre-wrap}</style></head><body><main><h1>Doc Bridge report unavailable</h1><p>The saved snapshot/report could not be rendered.</p><code>${escapeHtml(message)}</code><p>Run <code>ak-docs check</code> to regenerate valid artifacts.</p></main></body></html>`;
|
|
6433
|
+
var reportData = (snapshot, report, includeSnippets) => ({
|
|
6434
|
+
project: snapshot.project,
|
|
6435
|
+
revision: snapshot.sourceRevision,
|
|
6436
|
+
revisionKind: snapshot.sourceRevisionKind,
|
|
6437
|
+
snapshotHash: snapshot.contentHash,
|
|
6438
|
+
reportHash: report.contentHash,
|
|
6439
|
+
configurationHash: snapshot.configurationHash,
|
|
6440
|
+
pipelineVersion: snapshot.pipelineVersion,
|
|
6441
|
+
analyzerVersions: snapshot.analyzerVersions,
|
|
6442
|
+
entities: snapshot.entities.map((entity, index) => ({
|
|
6443
|
+
id: entity.id,
|
|
6444
|
+
anchor: entity.id.length > 64 ? `entity-n${index}` : anchor("entity", entity.id),
|
|
6445
|
+
kind: entity.kind,
|
|
6446
|
+
name: entity.name,
|
|
6447
|
+
path: entity.path,
|
|
6448
|
+
provenance: entity.provenance,
|
|
6449
|
+
evidence: entity.evidence.map(({ context, ...item }) => includeSnippets && context ? { ...item, context: redactSecrets(context) } : item)
|
|
6450
|
+
})),
|
|
6451
|
+
relations: snapshot.relations.map((relation) => ({
|
|
6452
|
+
id: relation.id,
|
|
6453
|
+
kind: relation.kind,
|
|
6454
|
+
from: relation.from,
|
|
6455
|
+
to: relation.to,
|
|
6456
|
+
provenance: relation.provenance,
|
|
6457
|
+
evidence: relation.evidence.map(({ context, ...item }) => includeSnippets && context ? { ...item, context: redactSecrets(context) } : item)
|
|
6458
|
+
})),
|
|
6459
|
+
diagnostics: report.diagnostics.map((diagnostic2) => ({
|
|
6460
|
+
id: diagnostic2.id,
|
|
6461
|
+
code: diagnostic2.code,
|
|
6462
|
+
status: diagnostic2.status,
|
|
6463
|
+
severity: diagnostic2.severity,
|
|
6464
|
+
message: diagnostic2.message,
|
|
6465
|
+
entityIds: diagnostic2.entityIds ?? [],
|
|
6466
|
+
relationIds: diagnostic2.relationIds ?? [],
|
|
6467
|
+
remediation: diagnostic2.remediation,
|
|
6468
|
+
evidence: diagnostic2.evidence.map(({ context, ...item }) => includeSnippets && context ? { ...item, context: redactSecrets(context) } : item)
|
|
6469
|
+
})),
|
|
6470
|
+
coverage: snapshot.coverage
|
|
6471
|
+
});
|
|
6472
|
+
var styles = String.raw`
|
|
6473
|
+
:root{color-scheme:light;--ink:#17251f;--muted:#60706a;--paper:#f5f7f3;--panel:#fff;--line:#d9e0da;--green:#18794e;--blue:#236b83;--amber:#a9640b;--red:#b33c35;--shadow:0 12px 32px rgba(23,37,31,.08)}
|
|
6474
|
+
*{box-sizing:border-box}body{margin:0;background:var(--paper);color:var(--ink);font:14px/1.5 Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}button,input,select{font:inherit}button{cursor:pointer}a{color:var(--blue)}.shell{max-width:1500px;margin:auto;padding:24px}.masthead{display:flex;justify-content:space-between;gap:24px;align-items:flex-end;border-bottom:1px solid var(--line);padding:8px 0 24px}.eyebrow{color:var(--green);font-size:11px;font-weight:800;letter-spacing:.12em;text-transform:uppercase}.masthead h1{font:700 clamp(28px,4vw,52px)/1.03 Georgia,serif;letter-spacing:-.04em;margin:8px 0}.lede{color:var(--muted);font-size:16px;max-width:720px;margin:0}.run-meta{text-align:right;color:var(--muted);font-size:12px}.run-meta strong{display:block;color:var(--green);font-size:14px}.read-only{border:1px solid #b9d7c6;background:#edf8f0;color:#185b3b;border-radius:999px;display:inline-flex;padding:4px 10px;font-weight:700;font-size:12px}.summary{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px;margin:20px 0}.metric,.insight,.panel{background:var(--panel);border:1px solid var(--line);box-shadow:var(--shadow);border-radius:14px}.metric{padding:16px}.metric b{font-size:27px;display:block;line-height:1}.metric span{color:var(--muted);display:block;margin-top:7px}.metric.warn b{color:var(--amber)}.metric.bad b{color:var(--red)}.lens-bar{display:flex;align-items:center;justify-content:space-between;gap:14px;margin:22px 0 12px}.tabs{display:flex;gap:4px;flex-wrap:wrap}.tab,.level{border:1px solid transparent;background:transparent;border-radius:8px;padding:8px 11px;color:var(--muted)}.tab:hover,.level:hover{background:#e8eee9;color:var(--ink)}.tab[aria-selected=true],.level[aria-pressed=true]{background:var(--ink);color:#fff}.filters{display:flex;gap:8px;flex-wrap:wrap;margin:12px 0}.filters label{color:var(--muted);font-size:12px;display:flex;align-items:center;gap:6px}.filters input,.filters select,.filters button{border:1px solid var(--line);background:#fff;border-radius:8px;padding:8px 10px;color:var(--ink)}.filters input{min-width:220px}.filters button{color:var(--blue)}.insights{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin:14px 0 20px}.insight{padding:14px}.insight h3{font-size:13px;margin:0 0 5px}.insight p{color:var(--muted);margin:0;font-size:12px}.tag{display:inline-block;border-radius:999px;padding:2px 7px;font-size:10px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;background:#edf0ee;color:var(--muted)}.tag.heuristic{background:#fff3dc;color:#8a570a}.tag.error{background:#fde8e6;color:var(--red)}.tag.warn{background:#fff2d9;color:var(--amber)}.tag.info{background:#e6f1f5;color:var(--blue)}.workspace{display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:14px}.panel{padding:18px}.panel h2{font:700 23px Georgia,serif;margin:0}.panel h3{font-size:14px;margin:0}.panel-head{display:flex;justify-content:space-between;gap:12px;align-items:flex-start;margin-bottom:14px}.subtle{color:var(--muted);font-size:12px}.map-wrap{border:1px solid var(--line);border-radius:10px;overflow:auto;background:linear-gradient(135deg,#fbfcfa,#f0f5f0)}#graph{display:block;width:100%;min-width:760px;height:560px}.edge{stroke:#b8c6bd;stroke-width:1.3;opacity:.65}.edge.alert{stroke:#d08a35;stroke-width:2}.edge-label{fill:var(--muted);font-size:10px}.graph-node{cursor:pointer}.graph-node rect{fill:#fff;stroke:#9eb0a4;stroke-width:1.2}.graph-node:hover rect,.graph-node:focus rect{stroke:var(--blue);stroke-width:2}.graph-node.selected rect{stroke:var(--green);stroke-width:3}.graph-node.issue rect{fill:#fffaf0;stroke:#c98a2c}.graph-node text{pointer-events:none}.node-label{font-size:12px;font-weight:750}.node-kind{font-size:10px;fill:var(--muted)}.node-count{font-size:10px;fill:var(--green);font-weight:800}.empty{padding:32px;text-align:center;color:var(--muted)}.side{position:sticky;top:14px;align-self:start}.detail-title{font:700 22px Georgia,serif;margin:4px 0}.detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;border-top:1px solid var(--line);border-bottom:1px solid var(--line);padding:12px 0;margin:14px 0}.detail-grid b{display:block;font-size:19px}.detail-grid span{color:var(--muted);font-size:11px}.path{overflow-wrap:anywhere;color:var(--muted);font-size:12px}.list{list-style:none;margin:10px 0 0;padding:0}.list li{border-top:1px solid var(--line);padding:8px 0;font-size:12px}.run{margin-top:14px}.run-summary{display:flex;gap:8px;flex-wrap:wrap;margin:12px 0}.chip{border:1px solid var(--line);border-radius:999px;padding:5px 9px;color:var(--muted);font-size:12px}.chip b{color:var(--ink)}.finding{border-top:1px solid var(--line);padding:14px 0}.finding-head{display:flex;justify-content:space-between;gap:10px}.finding h3{margin:0;font-size:14px}.finding p{margin:5px 0;color:var(--muted)}.finding ul{margin:8px 0 0;padding-left:18px;color:var(--muted);font-size:12px}.coverage-row{display:grid;grid-template-columns:170px 1fr 90px;gap:10px;align-items:center;border-top:1px solid var(--line);padding:10px 0;font-size:12px}.bar{height:7px;background:#e7ece8;border-radius:99px;overflow:hidden}.bar i{display:block;height:100%;background:var(--green)}.bar i.partial{background:var(--amber)}.bar i.none{background:var(--red)}.metadata{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin-top:14px}.metadata div{border-top:1px solid var(--line);padding-top:8px;color:var(--muted);font-size:12px;overflow-wrap:anywhere}.metadata b{display:block;color:var(--ink);font-size:11px;text-transform:uppercase;letter-spacing:.08em;margin-bottom:3px}.hidden{display:none!important}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media(max-width:900px){.summary{grid-template-columns:repeat(3,minmax(0,1fr))}.insights{grid-template-columns:repeat(2,minmax(0,1fr))}.workspace{grid-template-columns:1fr}.side{position:static}.run-meta{display:none}}@media(max-width:560px){.shell{padding:14px}.summary{grid-template-columns:repeat(2,minmax(0,1fr))}.insights{grid-template-columns:1fr}.masthead{display:block}.filters input{min-width:0;width:100%}.filters label:first-child{width:100%}.metadata{grid-template-columns:1fr}}@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important}}
|
|
6475
|
+
`;
|
|
6476
|
+
var script = String.raw`
|
|
6477
|
+
const data=${"${DATA}"};
|
|
6478
|
+
const esc=(value)=>String(value??'').replaceAll('&','&').replaceAll('<','<').replaceAll('>','>').replaceAll('"','"').replaceAll("'",''');
|
|
6479
|
+
const byId=new Map(data.entities.map((entity)=>[entity.id,entity]));
|
|
6480
|
+
const parent=new Map();
|
|
6481
|
+
const relationById=new Map(data.relations.map((relation)=>[relation.id,relation]));
|
|
6482
|
+
data.relations.forEach((relation)=>{if(relation.kind==='contains'&&!parent.has(relation.to))parent.set(relation.to,relation.from)});
|
|
6483
|
+
const state={lens:'architecture',level:'overview',selected:null,query:'',status:'',severity:''};
|
|
6484
|
+
const rootOf=(id)=>{let current=id;const seen=new Set();while(parent.has(current)&&!seen.has(current)){seen.add(current);current=parent.get(current)}return current};
|
|
6485
|
+
const packageNodes=()=>data.entities.filter((entity)=>entity.kind==='package'||entity.kind==='external');
|
|
6486
|
+
const groupFor=(id)=>{const entity=byId.get(id);if(!entity)return id;if(entity.kind==='package'||entity.kind==='external')return entity.id;const root=rootOf(id);return byId.has(root)?root:id};
|
|
6487
|
+
const label=(entity)=>entity?.name||entity?.id||'Unknown';
|
|
6488
|
+
const short=(value,max)=>{const text=String(value??'');return text.length>max?text.slice(0,max-1)+'…':text};
|
|
6489
|
+
const diagnosticsFor=(id)=>data.diagnostics.filter((finding)=>(finding.entityIds||[]).includes(id)||(finding.relationIds||[]).some((relationId)=>{const relation=relationById.get(relationId);return relation?.from===id||relation?.to===id}));
|
|
6490
|
+
const relationHealth=(relation)=>{const findings=data.diagnostics.filter((finding)=>(finding.relationIds||[]).includes(relation.id));return findings.some((finding)=>finding.severity==='error')?'error':findings.length?'warn':''};
|
|
6491
|
+
const degreeMap=(relations)=>{const degrees=new Map();relations.forEach((relation)=>{degrees.set(relation.from,(degrees.get(relation.from)||0)+1);degrees.set(relation.to,(degrees.get(relation.to)||0)+1)});return degrees};
|
|
6492
|
+
const selectedScope=()=>{if(!state.selected)return null;const selected=byId.get(state.selected);return selected?.kind==='package'?selected.id:groupFor(state.selected)};
|
|
6493
|
+
const nodesFor=()=>{const packages=packageNodes();if(state.level==='overview'||(state.level==='package'&&!state.selected))return packages.length?packages:data.entities.filter((entity)=>entity.kind!=='document').slice(0,80);const scope=selectedScope();let nodes=data.entities.filter((entity)=>{if(entity.kind==='package'||entity.kind==='external')return false;if(entity.kind==='document'&&state.level==='module')return false;return !scope||groupFor(entity.id)===scope});if(state.level==='module')nodes=nodes.filter((entity)=>entity.kind==='module');if(state.level==='file')nodes=nodes.filter((entity)=>Boolean(entity.path));const degrees=degreeMap(data.relations);return nodes.sort((a,b)=>(degrees.get(b.id)||0)-(degrees.get(a.id)||0)||a.id.localeCompare(b.id)).slice(0,160)};
|
|
6494
|
+
const graphModel=()=>{const nodes=nodesFor();const ids=new Set(nodes.map((node)=>node.id));const aggregate=state.level==='overview'||(state.level==='package'&&!state.selected);const edges=new Map();data.relations.forEach((relation)=>{const from=aggregate?groupFor(relation.from):relation.from;const to=aggregate?groupFor(relation.to):relation.to;if(from===to||!ids.has(from)||!ids.has(to))return;const key=from+'→'+to;const current=edges.get(key)||{from,to,count:0,kinds:new Set(),health:''};current.count++;current.kinds.add(relation.kind);current.health=current.health==='error'||relationHealth(relation)==='error'?'error':current.health||relationHealth(relation);edges.set(key,current)});return{nodes,edges:[...edges.values()].sort((a,b)=>b.count-a.count||a.from.localeCompare(b.from)).slice(0,600)}};
|
|
6495
|
+
const nodeIssues=(nodeId)=>{const ids=new Set([nodeId]);data.entities.forEach((entity)=>{if(groupFor(entity.id)===nodeId)ids.add(entity.id)});return data.diagnostics.filter((finding)=>(finding.entityIds||[]).some((id)=>ids.has(id))||(finding.relationIds||[]).some((id)=>{const relation=relationById.get(id);return relation&&ids.has(relation.from)&&ids.has(relation.to)}))};
|
|
6496
|
+
const renderInsights=()=>{const undocumented=data.diagnostics.filter((finding)=>finding.status==='undocumented').length;const drift=data.diagnostics.filter((finding)=>finding.status==='stale-or-unverified'||finding.status==='conflict').length;const unsupported=data.coverage.filter((entry)=>entry.status==='not-analyzed'||entry.status==='partial').length;const model=graphModel();const degrees=degreeMap(model.edges);const values=[...degrees.values()].sort((a,b)=>a-b);const median=values.length?values[Math.floor(values.length/2)]:0;const hot=[...degrees.values()].filter((value)=>value>=Math.max(4,median*2)).length;const isolated=model.nodes.filter((node)=>!model.edges.some((edge)=>edge.from===node.id||edge.to===node.id)).length;document.querySelector('#insights').innerHTML=[['Documentation drift',undocumented+drift,'Relations or docs needing comparison.',''],['Unanalyzed scope',unsupported,'Coverage gaps are explicit.',''],['Connectivity hotspots',hot,'Heuristic: unusually connected nodes.','heuristic'],['Disconnected nodes',isolated,'Heuristic: no visible edge at this level.','heuristic']].map(([title,count,copy,tag])=>'<article class="insight"><h3>'+esc(title)+' <span class="tag '+tag+'">'+(tag?'heuristic':'signal')+'</span></h3><p><strong>'+count+'</strong> · '+esc(copy)+'</p></article>').join('')};
|
|
6497
|
+
const renderGraph=()=>{const model=graphModel();const svg=document.querySelector('#graph');if(!model.nodes.length){svg.innerHTML='<text x="500" y="270" text-anchor="middle" class="subtle">No entities match this level.</text>';return}const width=1000,height=560,pad=56,columns=Math.max(1,Math.ceil(Math.sqrt(model.nodes.length))),rows=Math.ceil(model.nodes.length/columns),positions=new Map();model.nodes.forEach((node,index)=>{const col=index%columns,row=Math.floor(index/columns);positions.set(node.id,{x:pad+(col+0.5)*(width-2*pad)/columns,y:pad+(row+0.5)*(height-2*pad)/rows})});const defs='<defs><marker id="arrow" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto"><path d="M0,0 L0,6 L7,3 z" fill="#9eb0a4"/></marker></defs>';const edges=model.edges.map((edge)=>{const from=positions.get(edge.from),to=positions.get(edge.to);if(!from||!to)return '';const midX=(from.x+to.x)/2,midY=(from.y+to.y)/2;return '<g><line class="edge '+(edge.health?'alert':'')+'" x1="'+from.x+'" y1="'+from.y+'" x2="'+to.x+'" y2="'+to.y+'" marker-end="url(#arrow)"/><text class="edge-label" x="'+midX+'" y="'+midY+'">'+esc(edge.count>1?edge.count+'×':'')+'</text></g>'}).join('');const nodes=model.nodes.map((node)=>{const pos=positions.get(node.id),issues=nodeIssues(node.id),degree=(degreeMap(model.edges).get(node.id)||0),selected=state.selected===node.id?' selected':'',issue=issues.length?' issue':'';return '<g class="graph-node'+selected+issue+'" role="button" tabindex="0" data-node="'+esc(node.id)+'" transform="translate('+(pos.x-62)+','+(pos.y-27)+')"><title>'+esc(label(node))+' · '+esc(node.kind)+'</title><rect width="124" height="54" rx="8"></rect><text class="node-label" x="10" y="19">'+esc(short(label(node),18))+'</text><text class="node-kind" x="10" y="34">'+esc(short(node.kind,18))+'</text><text class="node-count" x="112" y="19" text-anchor="end">'+(degree||'')+'</text></g>'}).join('');svg.innerHTML=defs+edges+nodes;svg.querySelectorAll('[data-node]').forEach((node)=>{node.addEventListener('click',()=>{state.selected=node.dataset.node;render()});node.addEventListener('keydown',(event)=>{if(event.key==='Enter'||event.key===' '){event.preventDefault();state.selected=node.dataset.node;render()}})});document.querySelector('#map-note').textContent=(state.level==='overview'?'Grouped package/external view. ':state.level+' view. ')+'Edges are aggregated from canonical relations; '+model.edges.length+' visible connection groups.'};
|
|
6498
|
+
const renderDetails=()=>{const panel=document.querySelector('#details'),entity=state.selected?byId.get(state.selected):null;if(!entity){panel.innerHTML='<p class="subtle">Select a node in the map to inspect its evidence, connectivity, and findings.</p>';return}const incoming=data.relations.filter((relation)=>relation.to===entity.id),outgoing=data.relations.filter((relation)=>relation.from===entity.id),findings=diagnosticsFor(entity.id),evidence=[...(entity.evidence||[]),...incoming.flatMap((relation)=>relation.evidence||[]),...outgoing.flatMap((relation)=>relation.evidence||[])].slice(0,8);panel.innerHTML='<div class="eyebrow">Selected entity</div><h2 class="detail-title">'+esc(label(entity))+'</h2><span class="tag">'+esc(entity.kind)+'</span><p class="path">'+esc(entity.path||entity.id)+'</p><div class="detail-grid"><div><b>'+incoming.length+'</b><span>incoming</span></div><div><b>'+outgoing.length+'</b><span>outgoing</span></div><div><b>'+findings.length+'</b><span>findings</span></div><div><b>'+evidence.length+'</b><span>evidence items</span></div></div><h3>Evidence</h3><ul class="list">'+(evidence.length?evidence.map((item)=>'<li>'+esc(item.path+(item.lineStart?':'+item.lineStart:'')+(item.context?' — '+item.context:''))+'</li>').join(''):'<li>No evidence recorded.</li>')+'</ul>'+(findings.length?'<h3 style="margin-top:16px">Attention</h3><ul class="list">'+findings.slice(0,5).map((finding)=>'<li><span class="tag '+esc(finding.severity)+'">'+esc(finding.severity)+'</span> '+esc(finding.code)+'</li>').join('')+'</ul>':'')};
|
|
6499
|
+
const findingMatches=(finding)=>{const query=state.query.toLowerCase();return(!query||[finding.id,finding.code,finding.message,...finding.entityIds,...finding.relationIds].join(' ').toLowerCase().includes(query))&&(!state.status||finding.status===state.status)&&(!state.severity||finding.severity===state.severity)};
|
|
6500
|
+
const renderFindings=()=>{let findings=data.diagnostics.filter(findingMatches);if(state.lens==='risks')findings=findings.filter((finding)=>finding.severity==='error'||finding.severity==='warn');if(state.lens==='evidence')findings=findings.filter((finding)=>finding.evidence.length);if(state.lens==='drift')findings=findings.filter((finding)=>finding.status!=='confirmed');document.querySelector('#finding-count').textContent=findings.length+' shown';document.querySelector('#findings').innerHTML=findings.slice(0,300).map((finding)=>'<article class="finding" id="'+esc('diagnostic-'+finding.id.replace(/[^A-Za-z0-9_-]+/g,'-'))+'"><div class="finding-head"><h3>'+esc(finding.code)+'</h3><span><span class="tag '+esc(finding.severity)+'">'+esc(finding.severity)+'</span> <span class="tag">'+esc(finding.status)+'</span></span></div><p>'+esc(finding.message)+'</p>'+(finding.evidence.length?'<ul>'+finding.evidence.slice(0,4).map((item)=>'<li>'+esc(item.path+(item.lineStart?':'+item.lineStart:'')+(item.context?' — '+item.context:''))+'</li>').join('')+'</ul>':'')+(finding.remediation?'<p><strong>Next check:</strong> '+esc(finding.remediation)+'</p>':'')+'</article>').join('')||'<p class="empty">No findings match the current lens and filters.</p>';};
|
|
6501
|
+
const renderCoverage=()=>{document.querySelector('#coverage-list').innerHTML=data.coverage.map((entry)=>{const width=entry.status==='complete'?100:entry.status==='partial'?55:12;return '<div class="coverage-row"><div><b>'+esc(entry.analyzer)+'</b><br><span class="subtle">'+esc(entry.scope)+'</span></div><div class="bar"><i class="'+(entry.status==='complete'?'':entry.status==='partial'?'partial':'none')+'" style="width:'+width+'%"></i></div><div>'+esc(entry.status)+'</div></div>'}).join('')||'<p class="empty">No coverage metadata.</p>'};
|
|
6502
|
+
const render=()=>{document.querySelectorAll('.tab[data-lens]').forEach((tab)=>tab.setAttribute('aria-selected',String(tab.dataset.lens===state.lens)));document.querySelectorAll('.level').forEach((button)=>button.setAttribute('aria-pressed',String(button.dataset.level===state.level)));document.querySelector('#lens-caption').textContent=state.lens==='architecture'?'The repository topology at the selected level.':state.lens==='drift'?'Where canonical code relations and documentation declarations need attention.':state.lens==='risks'?'Signals that deserve human review; connectivity warnings are heuristics, not architectural proof.':'What the analyzers observed, declared, or could not analyze.';renderInsights();renderGraph();renderDetails();renderFindings();renderCoverage()};
|
|
6503
|
+
document.querySelectorAll('.tab[data-lens]').forEach((tab)=>tab.addEventListener('click',()=>{state.lens=tab.dataset.lens;render()}));document.querySelectorAll('.level').forEach((button)=>button.addEventListener('click',()=>{state.level=button.dataset.level;render()}));document.querySelector('#search').addEventListener('input',(event)=>{state.query=event.target.value;renderFindings()});document.querySelector('#status').addEventListener('change',(event)=>{state.status=event.target.value;renderFindings()});document.querySelector('#severity').addEventListener('change',(event)=>{state.severity=event.target.value;renderFindings()});document.querySelector('#reset').addEventListener('click',()=>{state.query='';state.status='';state.severity='';document.querySelector('#search').value='';document.querySelector('#status').value='';document.querySelector('#severity').value='';renderFindings()});document.querySelector('#clear-selection').addEventListener('click',()=>{state.selected=null;render()});document.addEventListener('keydown',(event)=>{if(event.key==='/'&&document.activeElement.tagName!=='INPUT'){event.preventDefault();document.querySelector('#search').focus()}});render();
|
|
6504
|
+
`;
|
|
6450
6505
|
var render = (input, options) => {
|
|
6451
6506
|
const { snapshot, report } = input;
|
|
6452
6507
|
const includeSnippets = options.includeSnippets === true;
|
|
6453
|
-
const
|
|
6454
|
-
|
|
6455
|
-
const
|
|
6456
|
-
const
|
|
6457
|
-
const
|
|
6458
|
-
const
|
|
6459
|
-
|
|
6460
|
-
const diagnostics = report.diagnostics.map((diagnostic2) => `<article id="${anchor("diagnostic", diagnostic2.id)}" class="diagnostic" data-status="${escapeHtml(diagnostic2.status)}" data-severity="${escapeHtml(diagnostic2.severity)}" data-analyzer="report" data-entity="${escapeHtml(diagnostic2.entityIds?.join(" ") ?? "")}" data-relation="${escapeHtml(diagnostic2.relationIds?.join(" ") ?? "")}" data-search="${escapeHtml(`${diagnostic2.id} ${diagnostic2.code} ${diagnostic2.message} ${diagnostic2.entityIds?.join(" ") ?? ""} ${diagnostic2.relationIds?.join(" ") ?? ""}`)}"><h3><a href="#${anchor("diagnostic", diagnostic2.id)}">${escapeHtml(diagnostic2.code)}</a> <span class="badge ${escapeHtml(diagnostic2.severity)}">${escapeHtml(diagnostic2.severity)}</span></h3><p>${escapeHtml(diagnostic2.message)}</p><p>Status: <b>${escapeHtml(diagnostic2.status)}</b></p><ul>${diagnostic2.evidence.map((item) => `<li>${escapeHtml(evidenceText(item, includeSnippets))}</li>`).join("")}</ul>${diagnostic2.entityIds?.length ? `<p>Entities: ${diagnostic2.entityIds.map((id) => entityLink(id)).join(", ")}</p>` : ""}</article>`).join("");
|
|
6461
|
-
const coverage = snapshot.coverage.map((entry) => `<li data-status="${escapeHtml(entry.status)}" data-analyzer="${escapeHtml(entry.analyzer)}" data-search="${escapeHtml(`${entry.analyzer} ${entry.scope} ${entry.status} ${entry.reason ?? ""}`)}"><b>${escapeHtml(entry.analyzer)}</b> / ${escapeHtml(entry.scope)}: ${escapeHtml(entry.status)}${entry.reason ? ` \u2014 ${escapeHtml(entry.reason)}` : ""}</li>`).join("");
|
|
6462
|
-
const nodes = snapshot.entities.map((entity) => `<div class="node" id="${entityAnchor(entity.id)}" data-node="${entityAnchor(entity.id)}"><a href="#${entityAnchor(entity.id)}">${escapeHtml(entity.name)}</a><br><span class="muted">${escapeHtml(entity.kind)}</span></div>`).join("");
|
|
6463
|
-
const script = `const q=document.querySelector('#search'),severity=document.querySelector('#severity'),provenance=document.querySelector('#provenance'),status=document.querySelector('#status'),analyzer=document.querySelector('#analyzer'),entity=document.querySelector('#entity'),relation=document.querySelector('#relation');function apply(){const term=q.value.toLowerCase(),entityTerm=entity.value.toLowerCase(),relationTerm=relation.value.toLowerCase();document.querySelectorAll('[data-search]').forEach((el)=>{const match=(!term||el.dataset.search.toLowerCase().includes(term))&&(!severity.value||el.dataset.severity===severity.value)&&(!provenance.value||el.dataset.provenance===provenance.value)&&(!status.value||el.dataset.status===status.value)&&(!analyzer.value||el.dataset.analyzer?.toLowerCase().includes(analyzer.value.toLowerCase()))&&(!entityTerm||el.dataset.entity?.toLowerCase().includes(entityTerm))&&(!relationTerm||el.dataset.relation?.toLowerCase().includes(relationTerm));el.classList.toggle('hidden',!match)});}q.oninput=apply;severity.onchange=apply;provenance.onchange=apply;status.onchange=apply;analyzer.oninput=apply;entity.oninput=apply;relation.oninput=apply;document.querySelector('#reset').onclick=()=>{q.value='';severity.value='';provenance.value='';status.value='';analyzer.value='';entity.value='';relation.value='';apply()};document.querySelectorAll('[data-node]').forEach((node)=>node.onclick=()=>{const id=node.dataset.node;document.querySelectorAll('[data-from],[data-to]').forEach((edge)=>edge.classList.toggle('hidden',edge.dataset.from!==id&&edge.dataset.to!==id));});`;
|
|
6464
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Doc Bridge \u2014 ${escapeHtml(snapshot.project.name)}</title><style>body{font:14px system-ui;margin:0;color:#18202a;background:#f5f7fa}header,main{max-width:1200px;margin:auto;padding:1.25rem}header{background:#18202a;color:white;max-width:none;padding-left:calc((100% - 1200px)/2);padding-right:calc((100% - 1200px)/2)}main{background:white}section{margin:1.5rem 0;border-top:1px solid #d7dde5;padding-top:1rem}table{border-collapse:collapse;width:100%;margin-top:.75rem}td,th{border-bottom:1px solid #e5e9ef;text-align:left;padding:.45rem;vertical-align:top}input,select,button{padding:.45rem;margin:.15rem;border:1px solid #b9c3d0;border-radius:4px;background:white}.badge{border-radius:1rem;padding:.15rem .5rem;background:#dce4ee}.error{background:#ffd9d9}.warn{background:#fff0c2}.info{background:#dcecff}.diagnostic{border:1px solid #d7dde5;border-left:4px solid #9aa7b5;padding:.75rem;margin:.75rem 0}.diagnostic:target,tr:target{background:#fff8cf}.muted{color:#5d6a78}a{color:#0b5cad}#map{display:flex;gap:1rem;flex-wrap:wrap}.node{border:1px solid #9aa7b5;padding:.5rem;border-radius:4px}.hidden{display:none}</style></head><body><header><h1>Doc Bridge: ${escapeHtml(snapshot.project.name)}</h1><p>Offline architecture and documentation reconciliation report</p><p class="muted">Snapshot ${escapeHtml(snapshot.contentHash)} \xB7 Report ${escapeHtml(report.contentHash)} \xB7 Revision ${escapeHtml(snapshot.sourceRevision)}</p></header><main><section id="controls"><label>Search <input id="search" type="search" placeholder="entity, relation, diagnostic"></label><label>Severity <select id="severity"><option value="">Any</option><option>error</option><option>warn</option><option>info</option></select></label><label>Provenance <select id="provenance"><option value="">Any</option><option>observed</option><option>declared</option><option>proposed</option></select></label><label>Status <select id="status"><option value="">Any</option>${["confirmed", "undocumented", "stale-or-unverified", "conflict", "unresolved", "not-analyzed"].map((value) => `<option>${value}</option>`).join("")}</select></label><label>Analyzer <input id="analyzer" type="search" placeholder="js-ts, report"></label><label>Entity <input id="entity" type="search" placeholder="entity id"></label><label>Relation <input id="relation" type="search" placeholder="relation id"></label><button id="reset">Reset filters</button></section><section id="architecture"><h2>Architecture map</h2><p>Observed and declared relations are rendered from the canonical snapshot; browser code does not infer edges.</p><div id="map">${nodes}</div><h3>Relations</h3><table><thead><tr><th>Kind</th><th>From</th><th>To</th><th>Provenance</th></tr></thead><tbody id="relations">${relations}</tbody></table></section><section id="diagnostic-lens"><h2>Diagnostic lens</h2><p>Findings: ${report.diagnostics.length}</p><div id="diagnostics">${diagnostics || '<p class="muted">No diagnostics.</p>'}</div></section><section id="coverage"><h2>Coverage and unsupported areas</h2><ul>${coverage || "<li>No coverage metadata.</li>"}</ul></section><section id="metadata"><h2>Run metadata</h2><dl><dt>Source revision</dt><dd>${escapeHtml(snapshot.sourceRevision)} (${escapeHtml(snapshot.sourceRevisionKind)})</dd><dt>Configuration hash</dt><dd>${escapeHtml(snapshot.configurationHash)}</dd><dt>Pipeline</dt><dd>${escapeHtml(snapshot.pipelineVersion)}</dd></dl></section></main><script>${script}</script></body></html>`;
|
|
6508
|
+
const data = reportData(snapshot, report, includeSnippets);
|
|
6509
|
+
const scriptBody = script.replace("${DATA}", embeddedJson(data));
|
|
6510
|
+
const largeNote = snapshot.entities.length > 500 || report.diagnostics.length > 1e3 ? "Large snapshots are rendered from compact canonical data with progressive graph levels." : "The viewer starts with a grouped topology and expands into canonical entities on demand.";
|
|
6511
|
+
const issueCount = report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "error" || diagnostic2.severity === "warn").length;
|
|
6512
|
+
const unsupportedCount = snapshot.coverage.filter((entry) => entry.status !== "complete").length;
|
|
6513
|
+
const statusOptions = ["confirmed", "undocumented", "stale-or-unverified", "conflict", "unresolved", "not-analyzed"];
|
|
6514
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Doc Bridge \u2014 ${escapeHtml(snapshot.project.name)}</title><style>${styles}</style></head><body><main class="shell"><header class="masthead"><div><div class="eyebrow">Doc Bridge / Knowledge report</div><h1>${escapeHtml(snapshot.project.name)}</h1><p class="lede">A read-only architecture and documentation map. Start broad, then follow evidence to the exact entity, relation, or finding.</p></div><div class="run-meta"><span class="read-only">Read-only snapshot</span><strong>${escapeHtml(snapshot.sourceRevision)}</strong><span>${escapeHtml(snapshot.sourceRevisionKind)} \xB7 pipeline ${escapeHtml(snapshot.pipelineVersion)}</span></div></header><section class="summary" aria-label="Snapshot summary"><div class="metric"><b>${snapshot.entities.length}</b><span>entities</span></div><div class="metric"><b>${snapshot.relations.length}</b><span>canonical relations</span></div><div class="metric ${issueCount ? "warn" : ""}"><b>${issueCount}</b><span>warn/error findings</span></div><div class="metric ${report.diagnostics.length ? "warn" : ""}"><b>${report.diagnostics.length}</b><span>total findings</span></div><div class="metric ${unsupportedCount ? "bad" : ""}"><b>${unsupportedCount}</b><span>partial / unanalyzed scopes</span></div></section><div class="lens-bar"><nav class="tabs" aria-label="Report lenses"><button class="tab" data-lens="architecture" aria-selected="true">Architecture</button><button class="tab" data-lens="drift" aria-selected="false">Documentation drift</button><button class="tab" data-lens="risks" aria-selected="false">Risks & hotspots</button><button class="tab" data-lens="evidence" aria-selected="false">Evidence</button></nav><span class="subtle">${escapeHtml(largeNote)}</span></div><p id="lens-caption" class="subtle">The repository topology at the selected level.</p><section id="insights" class="insights" aria-label="Attention signals"></section><section class="filters" aria-label="Finding filters"><label>Search <input id="search" type="search" placeholder="Press / to search findings"></label><label>Status <select id="status"><option value="">Any status</option>${statusOptions.map((value) => `<option>${escapeHtml(value)}</option>`).join("")}</select></label><label>Severity <select id="severity"><option value="">Any severity</option><option>error</option><option>warn</option><option>info</option></select></label><button id="reset" type="button">Reset filters</button></section><div class="workspace"><section class="panel" aria-labelledby="architecture-title"><div class="panel-head"><div><h2 id="architecture-title">Architecture map</h2><p id="map-note" class="subtle">Grouped package/external view.</p></div><div class="tabs" aria-label="Graph level"><button class="level" data-level="overview" aria-pressed="true">Overview</button><button class="level" data-level="package" aria-pressed="false">Package</button><button class="level" data-level="module" aria-pressed="false">Module</button><button class="level" data-level="file" aria-pressed="false">File</button></div></div><div class="map-wrap"><svg id="graph" viewBox="0 0 1000 560" role="img" aria-label="Interactive architecture graph"></svg></div><p class="subtle">Node number = visible connection degree. Amber nodes/edges have findings. Click or focus a node to inspect its evidence. Grouped edges preserve canonical relation direction and count.</p></section><aside class="panel side" aria-labelledby="details-title"><div class="panel-head"><div><div class="eyebrow">Evidence trail</div><h2 id="details-title">Details</h2></div><button id="clear-selection" class="tab" type="button">Clear</button></div><div id="details"><p class="subtle">Select a node in the map to inspect its evidence, connectivity, and findings.</p></div></aside></div><section class="panel run" aria-labelledby="run-title"><div class="panel-head"><div><div class="eyebrow">Jest-like diagnostics</div><h2 id="run-title">Run report</h2></div><span id="finding-count" class="subtle">${report.diagnostics.length} shown</span></div><div class="run-summary"><span class="chip"><b>${report.diagnostics.filter((item) => item.severity === "error").length}</b> errors</span><span class="chip"><b>${report.diagnostics.filter((item) => item.severity === "warn").length}</b> warnings</span><span class="chip"><b>${report.diagnostics.filter((item) => item.status === "undocumented").length}</b> undocumented</span><span class="chip"><b>${report.diagnostics.filter((item) => item.status === "stale-or-unverified" || item.status === "conflict").length}</b> drift/conflict</span></div><div id="findings"></div></section><section class="panel run" aria-labelledby="coverage-title"><div class="panel-head"><div><div class="eyebrow">Analyzer boundaries</div><h2 id="coverage-title">Coverage & unsupported areas</h2></div><span class="subtle">Explicit limits are part of the evidence</span></div><div id="coverage-list"></div></section><section class="metadata" aria-label="Run metadata"><div><b>Snapshot</b>${escapeHtml(snapshot.contentHash)}</div><div><b>Report</b>${escapeHtml(report.contentHash)}</div><div><b>Configuration</b>${escapeHtml(snapshot.configurationHash)}</div><div><b>Analyzers</b>${escapeHtml(Object.entries(snapshot.analyzerVersions).map(([name, version]) => `${name} ${version}`).join(", "))}</div><div><b>Source revision</b>${escapeHtml(snapshot.sourceRevision)} (${escapeHtml(snapshot.sourceRevisionKind)})</div><div><b>Mode</b>Read-only browser viewer; approvals and fixes remain outside this artifact.</div></section></main><script>${scriptBody}</script></body></html>`;
|
|
6465
6515
|
};
|
|
6466
6516
|
var renderOfflineReport = (input, options = {}) => {
|
|
6467
6517
|
try {
|