@agentskit/doc-bridge 1.5.2 → 1.6.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.
- package/CHANGELOG.md +12 -0
- package/action.yml +1 -1
- package/dist/cli/program.js +85 -32
- package/dist/cli/program.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +85 -32
- 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 +85 -30
- package/src/version.ts +1 -1
package/mcpb/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": "0.3",
|
|
3
3
|
"name": "doc-bridge",
|
|
4
4
|
"display_name": "Doc Bridge",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.6.1",
|
|
6
6
|
"description": "Deterministic repository handoffs for coding agents, running locally without an LLM or API key.",
|
|
7
7
|
"long_description": "Doc Bridge turns a repository's own documentation and ownership metadata into deterministic handoffs: where an agent should start, which paths it may edit, which checks it must run, and when a human must take over. The local connector exposes the same read-only contract available through Doc Bridge CLI and CI.",
|
|
8
8
|
"author": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentskit/doc-bridge",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"mcpName": "io.github.AgentsKit-io/doc-bridge",
|
|
5
5
|
"description": "Human↔agent documentation bridge — deterministic handoffs, doc-site links, memory→docs, optional AgentsKit RAG/chat.",
|
|
6
6
|
"type": "module",
|
|
@@ -43,49 +43,6 @@
|
|
|
43
43
|
"src",
|
|
44
44
|
"skills"
|
|
45
45
|
],
|
|
46
|
-
"scripts": {
|
|
47
|
-
"prebuild": "node scripts/sync-version.mjs",
|
|
48
|
-
"build": "tsup",
|
|
49
|
-
"test": "vitest run && pnpm test:cursor-plugin && pnpm test:claude-plugin && pnpm test:copilot-plugin && pnpm test:portable-skill",
|
|
50
|
-
"test:watch": "vitest",
|
|
51
|
-
"coverage": "vitest run --coverage",
|
|
52
|
-
"check:ecosystem-upstream": "node scripts/check-ecosystem-upstream.mjs",
|
|
53
|
-
"check:no-legacy-chat-imports": "node scripts/check-no-legacy-chat-imports.mjs",
|
|
54
|
-
"typecheck": "tsc --noEmit",
|
|
55
|
-
"smoke:packaged": "node scripts/smoke-packaged.mjs",
|
|
56
|
-
"smoke:docsites": "node scripts/smoke-docsites.mjs",
|
|
57
|
-
"smoke:real-docsites": "node scripts/smoke-real-docsites.mjs",
|
|
58
|
-
"smoke:ollama": "node scripts/smoke-ollama.mjs",
|
|
59
|
-
"mcpb:stage": "npm run build && node scripts/build-mcpb.mjs stage",
|
|
60
|
-
"mcpb:validate": "node scripts/build-mcpb.mjs validate",
|
|
61
|
-
"mcpb:smoke": "node scripts/smoke-mcpb.mjs",
|
|
62
|
-
"mcpb:pack": "npm run mcpb:stage && npm run mcpb:smoke && node scripts/build-mcpb.mjs pack",
|
|
63
|
-
"test:mcpb": "node --test scripts/mcpb-contract.test.mjs",
|
|
64
|
-
"test:cursor-plugin": "node --test scripts/cursor-plugin-contract.test.mjs",
|
|
65
|
-
"test:claude-plugin": "node --test scripts/claude-plugin-contract.test.mjs",
|
|
66
|
-
"test:copilot-plugin": "node --test scripts/copilot-plugin-contract.test.mjs",
|
|
67
|
-
"test:portable-skill": "node --test scripts/portable-skill-contract.test.mjs",
|
|
68
|
-
"coverage:badge": "node scripts/update-coverage-badge.mjs",
|
|
69
|
-
"changeset": "changeset",
|
|
70
|
-
"version-packages": "changeset version && node scripts/sync-version.mjs",
|
|
71
|
-
"release": "npm run build && npm test && changeset publish",
|
|
72
|
-
"version": "node scripts/sync-version.mjs && git add package.json src/version.ts action.yml",
|
|
73
|
-
"prepublishOnly": "npm run build && npm test",
|
|
74
|
-
"prepare": "node scripts/prepare.mjs",
|
|
75
|
-
"prepack": "npm run build",
|
|
76
|
-
"docs:dev": "cd apps/docs && next dev",
|
|
77
|
-
"docs:artifacts": "node scripts/build-docs-artifacts.mjs",
|
|
78
|
-
"docs:build": "pnpm build && node bin/ak-docs.js index && pnpm docs:artifacts && cd apps/docs && next build",
|
|
79
|
-
"docs:typecheck": "cd apps/docs && fumadocs-mdx && tsc --noEmit",
|
|
80
|
-
"docs:e2e": "playwright test",
|
|
81
|
-
"docs:lighthouse": "lhci autorun",
|
|
82
|
-
"test:readme-standard": "node --test scripts/readme-standard.test.mjs",
|
|
83
|
-
"test:marketplace": "node --test scripts/marketplace-contract.test.mjs",
|
|
84
|
-
"check:marketplace": "node scripts/check-marketplace-contract.mjs",
|
|
85
|
-
"test:docs-artifacts": "node --test scripts/docs-artifacts-contract.test.mjs",
|
|
86
|
-
"check:readme-standard": "node scripts/check-readme-standard.mjs",
|
|
87
|
-
"check:docs": "pnpm check:readme-standard && node bin/ak-docs.js gate run && node bin/ak-docs.js conformance run documentation-standard-v1 --text && pnpm docs:typecheck && pnpm docs:build && pnpm test:docs-artifacts"
|
|
88
|
-
},
|
|
89
46
|
"keywords": [
|
|
90
47
|
"agentskit",
|
|
91
48
|
"documentation",
|
|
@@ -183,5 +140,45 @@
|
|
|
183
140
|
"bugs": {
|
|
184
141
|
"url": "https://github.com/AgentsKit-io/doc-bridge/issues"
|
|
185
142
|
},
|
|
186
|
-
"homepage": "https://doc-bridge.agentskit.io/"
|
|
187
|
-
|
|
143
|
+
"homepage": "https://doc-bridge.agentskit.io/",
|
|
144
|
+
"scripts": {
|
|
145
|
+
"prebuild": "node scripts/sync-version.mjs",
|
|
146
|
+
"build": "tsup",
|
|
147
|
+
"test": "vitest run && pnpm test:cursor-plugin && pnpm test:claude-plugin && pnpm test:copilot-plugin && pnpm test:portable-skill",
|
|
148
|
+
"test:watch": "vitest",
|
|
149
|
+
"coverage": "vitest run --coverage",
|
|
150
|
+
"check:ecosystem-upstream": "node scripts/check-ecosystem-upstream.mjs",
|
|
151
|
+
"check:no-legacy-chat-imports": "node scripts/check-no-legacy-chat-imports.mjs",
|
|
152
|
+
"typecheck": "tsc --noEmit",
|
|
153
|
+
"smoke:packaged": "node scripts/smoke-packaged.mjs",
|
|
154
|
+
"smoke:docsites": "node scripts/smoke-docsites.mjs",
|
|
155
|
+
"smoke:real-docsites": "node scripts/smoke-real-docsites.mjs",
|
|
156
|
+
"smoke:ollama": "node scripts/smoke-ollama.mjs",
|
|
157
|
+
"mcpb:stage": "npm run build && node scripts/build-mcpb.mjs stage",
|
|
158
|
+
"mcpb:validate": "node scripts/build-mcpb.mjs validate",
|
|
159
|
+
"mcpb:smoke": "node scripts/smoke-mcpb.mjs",
|
|
160
|
+
"mcpb:pack": "npm run mcpb:stage && npm run mcpb:smoke && node scripts/build-mcpb.mjs pack",
|
|
161
|
+
"test:mcpb": "node --test scripts/mcpb-contract.test.mjs",
|
|
162
|
+
"test:cursor-plugin": "node --test scripts/cursor-plugin-contract.test.mjs",
|
|
163
|
+
"test:claude-plugin": "node --test scripts/claude-plugin-contract.test.mjs",
|
|
164
|
+
"test:copilot-plugin": "node --test scripts/copilot-plugin-contract.test.mjs",
|
|
165
|
+
"test:portable-skill": "node --test scripts/portable-skill-contract.test.mjs",
|
|
166
|
+
"coverage:badge": "node scripts/update-coverage-badge.mjs",
|
|
167
|
+
"changeset": "changeset",
|
|
168
|
+
"version-packages": "changeset version && node scripts/sync-version.mjs",
|
|
169
|
+
"release": "npm run build && npm test && changeset publish",
|
|
170
|
+
"version": "node scripts/sync-version.mjs && git add package.json src/version.ts action.yml",
|
|
171
|
+
"docs:dev": "cd apps/docs && next dev",
|
|
172
|
+
"docs:artifacts": "node scripts/build-docs-artifacts.mjs",
|
|
173
|
+
"docs:build": "pnpm build && node bin/ak-docs.js index && pnpm docs:artifacts && cd apps/docs && next build",
|
|
174
|
+
"docs:typecheck": "cd apps/docs && fumadocs-mdx && tsc --noEmit",
|
|
175
|
+
"docs:e2e": "playwright test",
|
|
176
|
+
"docs:lighthouse": "lhci autorun",
|
|
177
|
+
"test:readme-standard": "node --test scripts/readme-standard.test.mjs",
|
|
178
|
+
"test:marketplace": "node --test scripts/marketplace-contract.test.mjs",
|
|
179
|
+
"check:marketplace": "node scripts/check-marketplace-contract.mjs",
|
|
180
|
+
"test:docs-artifacts": "node --test scripts/docs-artifacts-contract.test.mjs",
|
|
181
|
+
"check:readme-standard": "node scripts/check-readme-standard.mjs",
|
|
182
|
+
"check:docs": "pnpm check:readme-standard && node bin/ak-docs.js gate run && node bin/ak-docs.js conformance run documentation-standard-v1 --text && pnpm docs:typecheck && pnpm docs:build && pnpm test:docs-artifacts"
|
|
183
|
+
}
|
|
184
|
+
}
|
package/src/report/html.ts
CHANGED
|
@@ -19,45 +19,100 @@ const escapeHtml = (value: unknown): string => String(value)
|
|
|
19
19
|
|
|
20
20
|
const anchor = (prefix: string, value: string): string => `${prefix}-${value.replace(/[^A-Za-z0-9_-]+/g, '-')}`
|
|
21
21
|
|
|
22
|
-
const
|
|
23
|
-
const location = `${evidence.path}${evidence.lineStart ? `:${evidence.lineStart}${evidence.lineEnd && evidence.lineEnd !== evidence.lineStart ? `-${evidence.lineEnd}` : ''}` : ''}`
|
|
24
|
-
return `${location}${includeSnippets && evidence.context ? ` — ${redactSecrets(evidence.context)}` : ''}`
|
|
25
|
-
}
|
|
22
|
+
const embeddedJson = (value: unknown): string => JSON.stringify(value).replaceAll('<', '\\u003c')
|
|
26
23
|
|
|
27
24
|
const errorPage = (message: string): string => `<!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>`
|
|
28
25
|
|
|
29
|
-
const
|
|
26
|
+
const reportData = (snapshot: DiscoverySnapshotV1, report: ReconciliationReportV1, includeSnippets: boolean) => ({
|
|
27
|
+
project: snapshot.project,
|
|
28
|
+
revision: snapshot.sourceRevision,
|
|
29
|
+
revisionKind: snapshot.sourceRevisionKind,
|
|
30
|
+
snapshotHash: snapshot.contentHash,
|
|
31
|
+
reportHash: report.contentHash,
|
|
32
|
+
configurationHash: snapshot.configurationHash,
|
|
33
|
+
pipelineVersion: snapshot.pipelineVersion,
|
|
34
|
+
analyzerVersions: snapshot.analyzerVersions,
|
|
35
|
+
entities: snapshot.entities.map((entity, index) => ({
|
|
36
|
+
id: entity.id,
|
|
37
|
+
anchor: entity.id.length > 64 ? `entity-n${index}` : anchor('entity', entity.id),
|
|
38
|
+
kind: entity.kind,
|
|
39
|
+
name: entity.name,
|
|
40
|
+
path: entity.path,
|
|
41
|
+
provenance: entity.provenance,
|
|
42
|
+
evidence: entity.evidence.map(({ context, ...item }) => includeSnippets && context ? { ...item, context: redactSecrets(context) } : item),
|
|
43
|
+
})),
|
|
44
|
+
relations: snapshot.relations.map((relation) => ({
|
|
45
|
+
id: relation.id,
|
|
46
|
+
kind: relation.kind,
|
|
47
|
+
from: relation.from,
|
|
48
|
+
to: relation.to,
|
|
49
|
+
provenance: relation.provenance,
|
|
50
|
+
evidence: relation.evidence.map(({ context, ...item }) => includeSnippets && context ? { ...item, context: redactSecrets(context) } : item),
|
|
51
|
+
})),
|
|
52
|
+
diagnostics: report.diagnostics.map((diagnostic) => ({
|
|
53
|
+
id: diagnostic.id,
|
|
54
|
+
code: diagnostic.code,
|
|
55
|
+
status: diagnostic.status,
|
|
56
|
+
severity: diagnostic.severity,
|
|
57
|
+
message: diagnostic.message,
|
|
58
|
+
entityIds: diagnostic.entityIds ?? [],
|
|
59
|
+
relationIds: diagnostic.relationIds ?? [],
|
|
60
|
+
remediation: diagnostic.remediation,
|
|
61
|
+
evidence: diagnostic.evidence.map(({ context, ...item }) => includeSnippets && context ? { ...item, context: redactSecrets(context) } : item),
|
|
62
|
+
})),
|
|
63
|
+
coverage: snapshot.coverage,
|
|
64
|
+
})
|
|
30
65
|
|
|
31
|
-
const
|
|
66
|
+
const styles = String.raw`
|
|
67
|
+
: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)}
|
|
68
|
+
*{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}}
|
|
69
|
+
`
|
|
32
70
|
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
71
|
+
const script = String.raw`
|
|
72
|
+
const data=${'${DATA}'};
|
|
73
|
+
const esc=(value)=>String(value??'').replaceAll('&','&').replaceAll('<','<').replaceAll('>','>').replaceAll('"','"').replaceAll("'",''');
|
|
74
|
+
const byId=new Map(data.entities.map((entity)=>[entity.id,entity]));
|
|
75
|
+
const parent=new Map();
|
|
76
|
+
const relationById=new Map(data.relations.map((relation)=>[relation.id,relation]));
|
|
77
|
+
data.relations.forEach((relation)=>{if(relation.kind==='contains'&&!parent.has(relation.to))parent.set(relation.to,relation.from)});
|
|
78
|
+
const findingIndex=new Map(),relationFindingIndex=new Map();
|
|
79
|
+
const addFinding=(index,id,finding)=>{const findings=index.get(id)||[];findings.push(finding);index.set(id,findings)};
|
|
80
|
+
data.diagnostics.forEach((finding)=>{(finding.entityIds||[]).forEach((id)=>addFinding(findingIndex,id,finding));(finding.relationIds||[]).forEach((id)=>addFinding(relationFindingIndex,id,finding));});
|
|
81
|
+
const state={lens:'architecture',level:'overview',selected:null,query:'',status:'',severity:''};
|
|
82
|
+
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};
|
|
83
|
+
const packageNodes=()=>data.entities.filter((entity)=>entity.kind==='package'||entity.kind==='external');
|
|
84
|
+
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};
|
|
85
|
+
const label=(entity)=>entity?.name||entity?.id||'Unknown';
|
|
86
|
+
const short=(value,max)=>{const text=String(value??'');return text.length>max?text.slice(0,max-1)+'…':text};
|
|
87
|
+
const diagnosticsFor=(id)=>findingIndex.get(id)||[];
|
|
88
|
+
const relationHealth=(relation)=>{const findings=relationFindingIndex.get(relation.id)||[];return findings.some((finding)=>finding.severity==='error')?'error':findings.length?'warn':''};
|
|
89
|
+
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};
|
|
90
|
+
const selectedScope=()=>{if(!state.selected)return null;const selected=byId.get(state.selected);return selected?.kind==='package'?selected.id:groupFor(state.selected)};
|
|
91
|
+
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)};
|
|
92
|
+
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)}};
|
|
93
|
+
const nodeIssues=(nodeId)=>{const ids=new Set([nodeId]);data.entities.forEach((entity)=>{if(groupFor(entity.id)===nodeId)ids.add(entity.id)});const findings=new Set();ids.forEach((id)=>(findingIndex.get(id)||[]).forEach((finding)=>findings.add(finding)));data.relations.forEach((relation)=>{if(ids.has(relation.from)||ids.has(relation.to))(relationFindingIndex.get(relation.id)||[]).forEach((finding)=>findings.add(finding))});return[...findings]};
|
|
94
|
+
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('')};
|
|
95
|
+
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.'};
|
|
96
|
+
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>':'')};
|
|
97
|
+
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)};
|
|
98
|
+
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>';};
|
|
99
|
+
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>'};
|
|
100
|
+
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()};
|
|
101
|
+
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();
|
|
102
|
+
`
|
|
45
103
|
|
|
46
104
|
const render = (input: OfflineReportInput, options: OfflineReportOptions): string => {
|
|
47
105
|
const { snapshot, report } = input
|
|
48
106
|
const includeSnippets = options.includeSnippets === true
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
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('')
|
|
59
|
-
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));});`
|
|
60
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Doc Bridge — ${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)} · Report ${escapeHtml(report.contentHash)} · 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>`
|
|
107
|
+
const data = reportData(snapshot, report, includeSnippets)
|
|
108
|
+
const scriptBody = script.replace('${' + 'DATA}', embeddedJson(data))
|
|
109
|
+
const largeNote = snapshot.entities.length > 500 || report.diagnostics.length > 1_000
|
|
110
|
+
? 'Large snapshots are rendered from compact canonical data with progressive graph levels.'
|
|
111
|
+
: 'The viewer starts with a grouped topology and expands into canonical entities on demand.'
|
|
112
|
+
const issueCount = report.diagnostics.filter((diagnostic) => diagnostic.severity === 'error' || diagnostic.severity === 'warn').length
|
|
113
|
+
const unsupportedCount = snapshot.coverage.filter((entry) => entry.status !== 'complete').length
|
|
114
|
+
const statusOptions = ['confirmed', 'undocumented', 'stale-or-unverified', 'conflict', 'unresolved', 'not-analyzed']
|
|
115
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Doc Bridge — ${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)} · 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>`
|
|
61
116
|
}
|
|
62
117
|
|
|
63
118
|
export const renderOfflineReport = (input: unknown, options: OfflineReportOptions = {}): string => {
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const PACKAGE_VERSION = '1.
|
|
1
|
+
export const PACKAGE_VERSION = '1.6.1'
|