@tekyzinc/gsd-t 2.51.10 → 2.53.11

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 (100) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +379 -373
  3. package/bin/component-registry.js +250 -0
  4. package/bin/graph-cgc.js +510 -510
  5. package/bin/graph-indexer.js +147 -147
  6. package/bin/graph-overlay.js +195 -195
  7. package/bin/graph-parsers.js +327 -327
  8. package/bin/graph-query.js +453 -452
  9. package/bin/graph-store.js +154 -154
  10. package/bin/qa-calibrator.js +194 -0
  11. package/bin/scan-data-collector.js +153 -153
  12. package/bin/scan-diagrams-generators.js +187 -187
  13. package/bin/scan-diagrams.js +79 -79
  14. package/bin/scan-renderer.js +92 -92
  15. package/bin/scan-report-sections.js +121 -121
  16. package/bin/scan-report.js +184 -184
  17. package/bin/scan-schema-parsers.js +199 -199
  18. package/bin/scan-schema.js +103 -103
  19. package/bin/token-budget.js +246 -0
  20. package/commands/Claude-md.md +10 -10
  21. package/commands/branch.md +15 -15
  22. package/commands/checkin.md +45 -45
  23. package/commands/global-change.md +209 -209
  24. package/commands/gsd-t-audit.md +199 -0
  25. package/commands/gsd-t-backlog-add.md +94 -94
  26. package/commands/gsd-t-backlog-edit.md +111 -111
  27. package/commands/gsd-t-backlog-list.md +63 -63
  28. package/commands/gsd-t-backlog-move.md +94 -94
  29. package/commands/gsd-t-backlog-promote.md +123 -123
  30. package/commands/gsd-t-backlog-remove.md +86 -86
  31. package/commands/gsd-t-backlog-settings.md +158 -158
  32. package/commands/gsd-t-complete-milestone.md +528 -515
  33. package/commands/gsd-t-debug.md +506 -482
  34. package/commands/gsd-t-discuss.md +174 -174
  35. package/commands/gsd-t-execute.md +758 -715
  36. package/commands/gsd-t-feature.md +276 -276
  37. package/commands/gsd-t-health.md +142 -142
  38. package/commands/gsd-t-help.md +465 -457
  39. package/commands/gsd-t-impact.md +302 -302
  40. package/commands/gsd-t-init-scan-setup.md +1 -5
  41. package/commands/gsd-t-init.md +314 -280
  42. package/commands/gsd-t-integrate.md +365 -333
  43. package/commands/gsd-t-milestone.md +87 -87
  44. package/commands/gsd-t-partition.md +442 -361
  45. package/commands/gsd-t-pause.md +82 -82
  46. package/commands/gsd-t-plan.md +345 -344
  47. package/commands/gsd-t-populate.md +111 -111
  48. package/commands/gsd-t-prd.md +326 -326
  49. package/commands/gsd-t-project.md +211 -211
  50. package/commands/gsd-t-promote-debt.md +123 -123
  51. package/commands/gsd-t-prompt.md +137 -137
  52. package/commands/gsd-t-qa.md +266 -266
  53. package/commands/gsd-t-quick.md +357 -315
  54. package/commands/gsd-t-reflect.md +134 -134
  55. package/commands/gsd-t-resume.md +72 -72
  56. package/commands/gsd-t-scan.md +615 -615
  57. package/commands/gsd-t-setup.md +76 -0
  58. package/commands/gsd-t-status.md +192 -166
  59. package/commands/gsd-t-test-sync.md +381 -381
  60. package/commands/gsd-t-triage-and-merge.md +171 -171
  61. package/commands/gsd-t-verify.md +382 -382
  62. package/commands/gsd-t-visualize.md +118 -118
  63. package/commands/gsd-t-wave.md +401 -378
  64. package/docs/GSD-T-README.md +425 -424
  65. package/docs/architecture.md +385 -369
  66. package/docs/harness-design-analysis.md +371 -0
  67. package/docs/infrastructure.md +205 -205
  68. package/docs/prd-graph-engine.md +398 -398
  69. package/docs/prd-gsd2-hybrid.md +559 -559
  70. package/docs/prd-harness-evolution.md +583 -0
  71. package/docs/requirements.md +14 -0
  72. package/docs/workflows.md +226 -226
  73. package/examples/.gsd-t/domains/example-domain/scope.md +13 -13
  74. package/package.json +40 -40
  75. package/scripts/gsd-t-auto-route.js +39 -39
  76. package/scripts/gsd-t-dashboard-mockup.html +1143 -1143
  77. package/scripts/gsd-t-dashboard-server.js +171 -171
  78. package/scripts/gsd-t-dashboard.html +262 -262
  79. package/scripts/gsd-t-event-writer.js +128 -128
  80. package/scripts/gsd-t-statusline.js +94 -94
  81. package/scripts/gsd-t-tools.js +175 -175
  82. package/templates/CLAUDE-global.md +638 -634
  83. package/templates/CLAUDE-project.md +24 -0
  84. package/templates/backlog-settings.md +18 -18
  85. package/templates/backlog.md +1 -1
  86. package/templates/progress.md +40 -40
  87. package/templates/shared-services-contract.md +60 -60
  88. package/templates/stacks/desktop.ini +2 -2
  89. package/bin/desktop.ini +0 -2
  90. package/commands/desktop.ini +0 -2
  91. package/docs/ci-examples/desktop.ini +0 -2
  92. package/docs/desktop.ini +0 -2
  93. package/examples/.gsd-t/contracts/desktop.ini +0 -2
  94. package/examples/.gsd-t/desktop.ini +0 -2
  95. package/examples/.gsd-t/domains/desktop.ini +0 -2
  96. package/examples/.gsd-t/domains/example-domain/desktop.ini +0 -2
  97. package/examples/desktop.ini +0 -2
  98. package/examples/rules/desktop.ini +0 -2
  99. package/scripts/desktop.ini +0 -2
  100. package/templates/desktop.ini +0 -2
@@ -1,184 +1,184 @@
1
- 'use strict';
2
- const fs = require('fs');
3
- const path = require('path');
4
-
5
- function buildCss() {
6
- return `:root{--bg:#070b12;--surface:#0c1120;--card:#0f1624;--card2:#131c2e;--border:#1a2840;--border2:#243654;--blue:#3b82f6;--violet:#7c3aed;--cyan:#06b6d4;--green:#10b981;--amber:#f59e0b;--red:#ef4444;--orange:#f97316;--text:#e2e8f0;--muted:#4b6080;--muted2:#7a96b8;--radius:12px}
7
- *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}html{scroll-behavior:smooth}
8
- body{font-family:'Segoe UI',system-ui,sans-serif;background:var(--bg);color:var(--text);display:flex;min-height:100vh}
9
- nav{width:192px;min-width:192px;background:var(--surface);border-right:1px solid var(--border);position:sticky;top:0;height:100vh;overflow-y:auto;padding:14px 0 24px;display:flex;flex-direction:column;gap:1px}
10
- .nb{padding:0 12px 14px;border-bottom:1px solid var(--border);margin-bottom:6px}
11
- .nb .pill{font-size:8.5px;font-weight:800;letter-spacing:1.5px;text-transform:uppercase;background:linear-gradient(135deg,var(--blue),var(--violet));color:#fff;padding:2px 7px;border-radius:4px;display:inline-block;margin-bottom:5px}
12
- .nb h2{font-size:12.5px;font-weight:700}.nb p{font-size:10px;color:var(--muted2);margin-top:1px}
13
- .ns{font-size:8.5px;font-weight:800;letter-spacing:1.5px;text-transform:uppercase;color:var(--muted);padding:10px 12px 3px;opacity:.55}
14
- nav a{display:flex;align-items:center;gap:7px;padding:5px 12px;color:var(--muted2);text-decoration:none;font-size:11.5px;font-weight:500;border-left:2px solid transparent;transition:all .1s}
15
- nav a:hover,nav a.active{color:var(--text);background:rgba(59,130,246,.07);border-left-color:var(--blue)}
16
- .nd{width:5px;height:5px;border-radius:50%;background:var(--muted);flex-shrink:0;transition:background .1s}
17
- nav a:hover .nd,nav a.active .nd{background:var(--blue)}.nd.g{background:var(--green)}.nd.y{background:var(--amber)}.nd.r{background:var(--red)}
18
- main{flex:1;overflow-y:auto;padding:24px 36px 60px;min-width:0}
19
- .ph{display:flex;align-items:baseline;justify-content:space-between;padding-bottom:14px;border-bottom:1px solid var(--border);margin-bottom:24px;flex-wrap:wrap;gap:6px}
20
- .ph h1{font-size:15px;font-weight:700;display:flex;align-items:center;gap:8px}.ph h1 .bc{font-size:10px;font-weight:400;color:var(--muted2)}
21
- .ph .meta{font-size:10.5px;color:var(--muted2);display:flex;gap:12px;flex-wrap:wrap}.ph .meta .sep{color:var(--border2)}
22
- section{margin-bottom:44px;scroll-margin-top:20px}
23
- .sl{font-size:9px;font-weight:800;letter-spacing:1.5px;text-transform:uppercase;color:var(--muted2);margin-bottom:12px;display:flex;align-items:center;gap:8px}
24
- .sl::after{content:'';flex:1;height:1px;background:var(--border)}
25
- .mxg{display:grid;grid-template-columns:repeat(auto-fit,minmax(138px,1fr));gap:10px}
26
- .mc{background:var(--card);border:1px solid var(--border);border-radius:10px;padding:14px 16px;overflow:hidden;position:relative}
27
- .mc-bar{height:2px;border-radius:1px;margin-bottom:10px;background:linear-gradient(90deg,var(--blue),var(--violet))}
28
- .mc-bar.g{background:linear-gradient(90deg,var(--green),#34d399)}.mc-bar.y{background:linear-gradient(90deg,var(--amber),#fbbf24)}.mc-bar.r{background:linear-gradient(90deg,var(--red),#f87171)}.mc-bar.o{background:linear-gradient(90deg,var(--orange),#fb923c)}
29
- .mc-lbl{font-size:9px;font-weight:800;letter-spacing:.7px;text-transform:uppercase;color:var(--muted2);margin-bottom:4px}.mc-val{font-size:24px;font-weight:800;color:var(--text);line-height:1}.mc-sub{font-size:9.5px;color:var(--muted2);margin-top:3px}
30
- .dc{background:var(--card);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;margin-bottom:16px;transition:border-color .15s}
31
- .dc:hover{border-color:var(--border2)}.dc-h{display:flex;align-items:center;justify-content:space-between;padding:9px 14px;background:var(--card2);border-bottom:1px solid var(--border)}
32
- .dc-hl{display:flex;align-items:center;gap:8px}.dc-t{font-size:11.5px;font-weight:700;color:var(--text)}
33
- .dc-tag{font-size:8.5px;font-weight:800;letter-spacing:.8px;text-transform:uppercase;padding:2px 8px;border-radius:20px;background:rgba(59,130,246,.1);color:var(--blue);border:1px solid rgba(59,130,246,.2)}
34
- .btn-exp{display:flex;align-items:center;gap:5px;background:rgba(255,255,255,.04);border:1px solid var(--border2);border-radius:6px;color:var(--muted2);cursor:pointer;padding:4px 10px;font-size:12px;transition:all .15s}
35
- .btn-exp span.lbl{font-size:9px;font-weight:700;letter-spacing:.5px;text-transform:uppercase}.btn-exp:hover{background:rgba(59,130,246,.12);border-color:var(--blue);color:var(--blue)}
36
- .dc-b{min-height:520px;display:flex;align-items:flex-start;justify-content:center;background:radial-gradient(ellipse 80% 60% at 50% 20%,rgba(59,130,246,.04) 0%,transparent 70%),var(--bg);padding:24px 20px;overflow:auto}
37
- .dc-b .mermaid{width:100%;display:flex;align-items:flex-start;justify-content:center}.dc-b .mermaid svg{width:100%!important;height:auto!important;display:block}
38
- .dc-b svg{width:100%;height:auto;display:block}
39
- .diagram-placeholder{background:rgba(255,255,255,.03);border:1px dashed var(--border);border-radius:8px;padding:48px 32px;text-align:center;color:var(--muted2);font-size:13px;width:100%}
40
- .diagram-placeholder code{font-family:monospace;background:rgba(255,255,255,.07);padding:2px 6px;border-radius:4px}
41
- .dc-n{padding:9px 14px;font-size:11px;color:var(--muted2);border-top:1px solid var(--border);background:var(--card2);line-height:1.5}
42
- .dc-n strong{color:var(--text)}.dc-n code{font-family:'Consolas',monospace;font-size:10px;background:rgba(255,255,255,.05);padding:1px 5px;border-radius:3px;color:#7dd3fc}
43
- .tw{background:var(--card);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden}
44
- table{width:100%;border-collapse:collapse;font-size:12px}
45
- thead th{background:var(--card2);color:var(--muted2);font-size:8.5px;font-weight:800;letter-spacing:1px;text-transform:uppercase;padding:9px 13px;text-align:left;border-bottom:1px solid var(--border)}
46
- tbody td{padding:9px 13px;border-bottom:1px solid var(--border);vertical-align:top}
47
- tbody tr:last-child td{border-bottom:none}tbody tr:hover{background:rgba(255,255,255,.012)}
48
- td code{font-family:'Consolas',monospace;font-size:10px;background:rgba(255,255,255,.05);padding:1px 5px;border-radius:3px;color:#7dd3fc}
49
- .loc-cell{font-family:'Consolas',monospace;font-size:11px;color:var(--blue);white-space:nowrap}
50
- .bx{display:inline-flex;align-items:center;padding:2px 7px;border-radius:20px;font-size:8.5px;font-weight:800;letter-spacing:.4px;text-transform:uppercase}
51
- .bx.c{background:rgba(239,68,68,.1);color:var(--red);border:1px solid rgba(239,68,68,.22)}.bx.h{background:rgba(249,115,22,.1);color:var(--orange);border:1px solid rgba(249,115,22,.22)}
52
- .bx.m{background:rgba(245,158,11,.1);color:var(--amber);border:1px solid rgba(245,158,11,.22)}.bx.l{background:rgba(16,185,129,.1);color:var(--green);border:1px solid rgba(16,185,129,.22)}
53
- .bx.i{background:rgba(59,130,246,.1);color:var(--blue);border:1px solid rgba(59,130,246,.22)}
54
- .fl{display:flex;flex-direction:column;gap:8px}
55
- .fi{background:var(--card);border:1px solid var(--border);border-radius:10px;padding:13px 14px;display:flex;gap:11px;align-items:flex-start}
56
- .fi .ico{font-size:15px;flex-shrink:0;margin-top:1px}.fi h4{font-size:11.5px;font-weight:700;margin-bottom:2px}.fi p{font-size:11px;color:var(--muted2)}
57
- .fi code{font-family:'Consolas',monospace;font-size:10px;background:rgba(255,255,255,.05);padding:1px 5px;border-radius:3px;color:#7dd3fc}
58
- #modal{display:none;position:fixed;inset:0;background:rgba(4,7,14,.97);z-index:1000;flex-direction:column}#modal.open{display:flex}
59
- .mbar{display:flex;align-items:center;justify-content:space-between;padding:10px 18px;background:rgba(12,17,32,.98);border-bottom:1px solid var(--border);backdrop-filter:blur(8px);flex-shrink:0}
60
- .mbar h3{font-size:12.5px;font-weight:700}.mbar .hint{font-size:9.5px;color:var(--muted2);margin-left:10px}.mbar-r{display:flex;align-items:center;gap:8px}
61
- .btn-z{background:rgba(255,255,255,.05);border:1px solid var(--border2);border-radius:6px;color:var(--muted2);cursor:pointer;padding:4px 10px;font-size:13px;font-weight:700;transition:all .12s}
62
- .btn-z:hover{background:rgba(59,130,246,.12);border-color:var(--blue);color:var(--blue)}.zlbl{font-size:10.5px;color:var(--muted2);min-width:36px;text-align:center}
63
- .btn-cls{background:rgba(239,68,68,.08);border:1px solid rgba(239,68,68,.2);border-radius:7px;color:#f87171;cursor:pointer;padding:5px 14px;font-size:12px;font-weight:600;transition:all .12s}
64
- .btn-cls:hover{background:rgba(239,68,68,.2);border-color:var(--red)}
65
- #mcontent{flex:1;overflow:auto;display:flex;align-items:center;justify-content:center;padding:48px}
66
- #mdiagram{transform-origin:center center;transition:transform .12s}#mdiagram svg{display:block;width:auto;max-width:100%;height:auto}
67
- footer{margin-top:48px;padding-top:14px;border-top:1px solid var(--border);font-size:9.5px;color:var(--muted);display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:5px;opacity:.5}
68
- footer a{color:var(--muted2);text-decoration:none}footer a:hover{color:var(--blue)}
69
- ::-webkit-scrollbar{width:4px;height:4px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border2);border-radius:4px}`;
70
- }
71
-
72
- function buildScript() {
73
- return `<script>
74
- let zoom=1;
75
- function expandDiagram(btn){const card=btn.closest('.dc');const title=card.dataset.title||'Diagram';const svg=card.querySelector('.dc-b svg');if(!svg)return;const clone=svg.cloneNode(true);clone.style.cssText='display:block;width:auto;height:auto;max-width:90vw;max-height:85vh;';clone.removeAttribute('width');clone.removeAttribute('height');const wrap=document.getElementById('mdiagram');wrap.innerHTML='';wrap.appendChild(clone);document.getElementById('mtitle').textContent=title;zoom=1;applyZoom();document.getElementById('modal').classList.add('open');document.body.style.overflow='hidden';}
76
- function closeModal(){document.getElementById('modal').classList.remove('open');document.body.style.overflow='';}
77
- function adjustZoom(d){zoom=Math.max(0.2,Math.min(5,zoom+d));applyZoom();}
78
- function resetZoom(){zoom=1;applyZoom();}
79
- function applyZoom(){document.getElementById('mdiagram').style.transform='scale('+zoom+')';document.getElementById('zlbl').textContent=Math.round(zoom*100)+'%';}
80
- document.getElementById('mcontent').addEventListener('wheel',e=>{e.preventDefault();adjustZoom(e.deltaY<0?0.06:-0.06);},{passive:false});
81
- document.addEventListener('keydown',e=>{if(e.key==='Escape')closeModal();});
82
- document.getElementById('modal').addEventListener('click',e=>{if(e.target===document.getElementById('modal')||e.target===document.getElementById('mcontent'))closeModal();});
83
- const navLinks=document.querySelectorAll('nav a[href^="#"]');document.querySelectorAll('section[id]').forEach(s=>new IntersectionObserver(entries=>{if(entries[0].isIntersecting){navLinks.forEach(l=>l.classList.remove('active'));const a=document.querySelector('nav a[href="#'+entries[0].target.id+'"]');if(a)a.classList.add('active');}},{threshold:0.15}).observe(s));
84
- <\/script>`;
85
- }
86
-
87
- function buildSidebar(projectName, version, scanDate, diagTypes) {
88
- const vp = (version ? esc(version) + ' &middot; ' : '') + esc(scanDate || '');
89
- const ALL = [['system-architecture','System Architecture'],['app-architecture','App Architecture'],['workflow','Workflow'],['data-flow','Data Flow'],['sequence','Sequence'],['database-schema','Database Schema']];
90
- const dlinks = ALL.filter(([t]) => !diagTypes || diagTypes.includes(t)).map(([t, l]) => '<a href="#diagram-' + t + '"><span class="nd"></span>' + esc(l) + '</a>').join('\n ');
91
- return '<nav>\n <div class="nb"><div class="pill">GSD&#x2011;T Scan</div><h2>' + esc(projectName) + '</h2><p>' + vp + '</p></div>\n <div class="ns">Overview</div>\n <a href="#summary"><span class="nd g"></span>Summary</a>\n <a href="#domains"><span class="nd"></span>Domains</a>\n <div class="ns">Diagrams</div>\n ' + dlinks + '\n <div class="ns">Analysis</div>\n <a href="#tech-debt"><span class="nd y"></span>Tech Debt</a>\n <a href="#findings"><span class="nd r"></span>Key Findings</a>\n</nav>';
92
- }
93
-
94
- function buildPageHeader(data, opts) {
95
- const projectName = data.projectName || path.basename(opts.projectRoot || process.cwd());
96
- const stack = data.techStack || '';
97
- const files = data.filesScanned ? data.filesScanned + ' files' : '';
98
- const loc = data.totalLoc ? (data.totalLoc > 999 ? (data.totalLoc / 1000).toFixed(1) + 'k' : data.totalLoc) + ' LoC' : '';
99
- const fileLoc = [files, loc].filter(Boolean).join(' \u00b7 ');
100
- const metaParts = [stack, fileLoc, data.scanDate || ''].filter(Boolean);
101
- const metaHtml = metaParts.map((m, i) =>
102
- (i > 0 ? '<span class="sep">|</span>' : '') + '<span>' + esc(m) + '</span>'
103
- ).join('');
104
- return '<div class="ph"><h1><span class="bc">GSD&#x2011;T &rsaquo; Scan &rsaquo;</span> ' +
105
- esc(projectName) + ' Codebase Report</h1><div class="meta">' + metaHtml + '</div></div>';
106
- }
107
-
108
- function buildHtmlSkeleton(title, css, sidebar, body) {
109
- return `<!DOCTYPE html>
110
- <html lang="en">
111
- <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
112
- <title>${title}</title>
113
- <style>
114
- ${css}
115
- </style></head>
116
- <body>
117
- ${sidebar}
118
- <div id="modal">
119
- <div class="mbar">
120
- <div style="display:flex;align-items:center;gap:8px"><h3 id="mtitle">Diagram</h3><span class="hint">Scroll to zoom &middot; Esc to close</span></div>
121
- <div class="mbar-r">
122
- <button class="btn-z" onclick="adjustZoom(-0.15)">&#8722;</button>
123
- <span class="zlbl" id="zlbl">100%</span>
124
- <button class="btn-z" onclick="adjustZoom(0.15)">+</button>
125
- <button class="btn-z" onclick="resetZoom()" style="font-size:10px;padding:4px 9px">Reset</button>
126
- <button class="btn-cls" onclick="closeModal()">&#10005; Close</button>
127
- </div>
128
- </div>
129
- <div id="mcontent"><div id="mdiagram"></div></div>
130
- </div>
131
- <main>
132
- ${body}
133
- <footer><span>GSD-T Scan Report</span><span><a href="https://mermaid.js.org">Mermaid.js (MIT)</a></span></footer>
134
- </main>
135
- ${buildScript()}
136
- </body>
137
- </html>`;
138
- }
139
-
140
- function esc(s) { return String(s || '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'); }
141
- const sections = require('./scan-report-sections.js');
142
- function generateReport(analysisData, schemaData, diagrams, options) {
143
- try {
144
- const safeData = analysisData || {};
145
- const safeDiagrams = (Array.isArray(diagrams) ? diagrams : []).filter(d => d && d.type);
146
- const opts = options || {};
147
- const projectName = safeData.projectName || path.basename(opts.projectRoot || process.cwd());
148
- const css = buildCss();
149
- const sidebar = buildSidebar(projectName, safeData.version || '', safeData.scanDate || '', safeDiagrams.map(d => d.type));
150
- const pageHeader = buildPageHeader(safeData, opts);
151
- const body = pageHeader
152
- + sections.buildMetricCards(safeData)
153
- + sections.buildDomainHealth(safeData)
154
- + safeDiagrams.map(sections.buildDiagramSection).join('')
155
- + sections.buildTechDebt(safeData)
156
- + sections.buildFindings(safeData);
157
- const html = buildHtmlSkeleton(esc(projectName) + ' \u2014 GSD-T Scan Report', css, sidebar, body);
158
- const defaultDir = path.join(opts.projectRoot || process.cwd(), '.gsd-t', 'scan');
159
- const outputDir = opts.outputDir || defaultDir;
160
- if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
161
- const outputPath = path.join(outputDir, 'scan-report.html');
162
- fs.writeFileSync(outputPath, html, 'utf8');
163
- return {
164
- outputPath,
165
- diagramsRendered: safeDiagrams.filter(d => d.rendered).length,
166
- diagramsPlaceholder: safeDiagrams.filter(d => !d.rendered).length
167
- };
168
- } catch (err) {
169
- process.stderr.write('scan-report error: ' + err.message + '\n');
170
- return { outputPath: null, error: err.message };
171
- }
172
- }
173
-
174
- module.exports = {
175
- generateReport,
176
- buildCss,
177
- buildSidebar,
178
- buildHtmlSkeleton,
179
- buildMetricCards: sections.buildMetricCards,
180
- buildDomainHealth: sections.buildDomainHealth,
181
- buildDiagramSection: sections.buildDiagramSection,
182
- buildTechDebt: sections.buildTechDebt,
183
- buildFindings: sections.buildFindings
184
- };
1
+ 'use strict';
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ function buildCss() {
6
+ return `:root{--bg:#070b12;--surface:#0c1120;--card:#0f1624;--card2:#131c2e;--border:#1a2840;--border2:#243654;--blue:#3b82f6;--violet:#7c3aed;--cyan:#06b6d4;--green:#10b981;--amber:#f59e0b;--red:#ef4444;--orange:#f97316;--text:#e2e8f0;--muted:#4b6080;--muted2:#7a96b8;--radius:12px}
7
+ *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}html{scroll-behavior:smooth}
8
+ body{font-family:'Segoe UI',system-ui,sans-serif;background:var(--bg);color:var(--text);display:flex;min-height:100vh}
9
+ nav{width:192px;min-width:192px;background:var(--surface);border-right:1px solid var(--border);position:sticky;top:0;height:100vh;overflow-y:auto;padding:14px 0 24px;display:flex;flex-direction:column;gap:1px}
10
+ .nb{padding:0 12px 14px;border-bottom:1px solid var(--border);margin-bottom:6px}
11
+ .nb .pill{font-size:8.5px;font-weight:800;letter-spacing:1.5px;text-transform:uppercase;background:linear-gradient(135deg,var(--blue),var(--violet));color:#fff;padding:2px 7px;border-radius:4px;display:inline-block;margin-bottom:5px}
12
+ .nb h2{font-size:12.5px;font-weight:700}.nb p{font-size:10px;color:var(--muted2);margin-top:1px}
13
+ .ns{font-size:8.5px;font-weight:800;letter-spacing:1.5px;text-transform:uppercase;color:var(--muted);padding:10px 12px 3px;opacity:.55}
14
+ nav a{display:flex;align-items:center;gap:7px;padding:5px 12px;color:var(--muted2);text-decoration:none;font-size:11.5px;font-weight:500;border-left:2px solid transparent;transition:all .1s}
15
+ nav a:hover,nav a.active{color:var(--text);background:rgba(59,130,246,.07);border-left-color:var(--blue)}
16
+ .nd{width:5px;height:5px;border-radius:50%;background:var(--muted);flex-shrink:0;transition:background .1s}
17
+ nav a:hover .nd,nav a.active .nd{background:var(--blue)}.nd.g{background:var(--green)}.nd.y{background:var(--amber)}.nd.r{background:var(--red)}
18
+ main{flex:1;overflow-y:auto;padding:24px 36px 60px;min-width:0}
19
+ .ph{display:flex;align-items:baseline;justify-content:space-between;padding-bottom:14px;border-bottom:1px solid var(--border);margin-bottom:24px;flex-wrap:wrap;gap:6px}
20
+ .ph h1{font-size:15px;font-weight:700;display:flex;align-items:center;gap:8px}.ph h1 .bc{font-size:10px;font-weight:400;color:var(--muted2)}
21
+ .ph .meta{font-size:10.5px;color:var(--muted2);display:flex;gap:12px;flex-wrap:wrap}.ph .meta .sep{color:var(--border2)}
22
+ section{margin-bottom:44px;scroll-margin-top:20px}
23
+ .sl{font-size:9px;font-weight:800;letter-spacing:1.5px;text-transform:uppercase;color:var(--muted2);margin-bottom:12px;display:flex;align-items:center;gap:8px}
24
+ .sl::after{content:'';flex:1;height:1px;background:var(--border)}
25
+ .mxg{display:grid;grid-template-columns:repeat(auto-fit,minmax(138px,1fr));gap:10px}
26
+ .mc{background:var(--card);border:1px solid var(--border);border-radius:10px;padding:14px 16px;overflow:hidden;position:relative}
27
+ .mc-bar{height:2px;border-radius:1px;margin-bottom:10px;background:linear-gradient(90deg,var(--blue),var(--violet))}
28
+ .mc-bar.g{background:linear-gradient(90deg,var(--green),#34d399)}.mc-bar.y{background:linear-gradient(90deg,var(--amber),#fbbf24)}.mc-bar.r{background:linear-gradient(90deg,var(--red),#f87171)}.mc-bar.o{background:linear-gradient(90deg,var(--orange),#fb923c)}
29
+ .mc-lbl{font-size:9px;font-weight:800;letter-spacing:.7px;text-transform:uppercase;color:var(--muted2);margin-bottom:4px}.mc-val{font-size:24px;font-weight:800;color:var(--text);line-height:1}.mc-sub{font-size:9.5px;color:var(--muted2);margin-top:3px}
30
+ .dc{background:var(--card);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;margin-bottom:16px;transition:border-color .15s}
31
+ .dc:hover{border-color:var(--border2)}.dc-h{display:flex;align-items:center;justify-content:space-between;padding:9px 14px;background:var(--card2);border-bottom:1px solid var(--border)}
32
+ .dc-hl{display:flex;align-items:center;gap:8px}.dc-t{font-size:11.5px;font-weight:700;color:var(--text)}
33
+ .dc-tag{font-size:8.5px;font-weight:800;letter-spacing:.8px;text-transform:uppercase;padding:2px 8px;border-radius:20px;background:rgba(59,130,246,.1);color:var(--blue);border:1px solid rgba(59,130,246,.2)}
34
+ .btn-exp{display:flex;align-items:center;gap:5px;background:rgba(255,255,255,.04);border:1px solid var(--border2);border-radius:6px;color:var(--muted2);cursor:pointer;padding:4px 10px;font-size:12px;transition:all .15s}
35
+ .btn-exp span.lbl{font-size:9px;font-weight:700;letter-spacing:.5px;text-transform:uppercase}.btn-exp:hover{background:rgba(59,130,246,.12);border-color:var(--blue);color:var(--blue)}
36
+ .dc-b{min-height:520px;display:flex;align-items:flex-start;justify-content:center;background:radial-gradient(ellipse 80% 60% at 50% 20%,rgba(59,130,246,.04) 0%,transparent 70%),var(--bg);padding:24px 20px;overflow:auto}
37
+ .dc-b .mermaid{width:100%;display:flex;align-items:flex-start;justify-content:center}.dc-b .mermaid svg{width:100%!important;height:auto!important;display:block}
38
+ .dc-b svg{width:100%;height:auto;display:block}
39
+ .diagram-placeholder{background:rgba(255,255,255,.03);border:1px dashed var(--border);border-radius:8px;padding:48px 32px;text-align:center;color:var(--muted2);font-size:13px;width:100%}
40
+ .diagram-placeholder code{font-family:monospace;background:rgba(255,255,255,.07);padding:2px 6px;border-radius:4px}
41
+ .dc-n{padding:9px 14px;font-size:11px;color:var(--muted2);border-top:1px solid var(--border);background:var(--card2);line-height:1.5}
42
+ .dc-n strong{color:var(--text)}.dc-n code{font-family:'Consolas',monospace;font-size:10px;background:rgba(255,255,255,.05);padding:1px 5px;border-radius:3px;color:#7dd3fc}
43
+ .tw{background:var(--card);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden}
44
+ table{width:100%;border-collapse:collapse;font-size:12px}
45
+ thead th{background:var(--card2);color:var(--muted2);font-size:8.5px;font-weight:800;letter-spacing:1px;text-transform:uppercase;padding:9px 13px;text-align:left;border-bottom:1px solid var(--border)}
46
+ tbody td{padding:9px 13px;border-bottom:1px solid var(--border);vertical-align:top}
47
+ tbody tr:last-child td{border-bottom:none}tbody tr:hover{background:rgba(255,255,255,.012)}
48
+ td code{font-family:'Consolas',monospace;font-size:10px;background:rgba(255,255,255,.05);padding:1px 5px;border-radius:3px;color:#7dd3fc}
49
+ .loc-cell{font-family:'Consolas',monospace;font-size:11px;color:var(--blue);white-space:nowrap}
50
+ .bx{display:inline-flex;align-items:center;padding:2px 7px;border-radius:20px;font-size:8.5px;font-weight:800;letter-spacing:.4px;text-transform:uppercase}
51
+ .bx.c{background:rgba(239,68,68,.1);color:var(--red);border:1px solid rgba(239,68,68,.22)}.bx.h{background:rgba(249,115,22,.1);color:var(--orange);border:1px solid rgba(249,115,22,.22)}
52
+ .bx.m{background:rgba(245,158,11,.1);color:var(--amber);border:1px solid rgba(245,158,11,.22)}.bx.l{background:rgba(16,185,129,.1);color:var(--green);border:1px solid rgba(16,185,129,.22)}
53
+ .bx.i{background:rgba(59,130,246,.1);color:var(--blue);border:1px solid rgba(59,130,246,.22)}
54
+ .fl{display:flex;flex-direction:column;gap:8px}
55
+ .fi{background:var(--card);border:1px solid var(--border);border-radius:10px;padding:13px 14px;display:flex;gap:11px;align-items:flex-start}
56
+ .fi .ico{font-size:15px;flex-shrink:0;margin-top:1px}.fi h4{font-size:11.5px;font-weight:700;margin-bottom:2px}.fi p{font-size:11px;color:var(--muted2)}
57
+ .fi code{font-family:'Consolas',monospace;font-size:10px;background:rgba(255,255,255,.05);padding:1px 5px;border-radius:3px;color:#7dd3fc}
58
+ #modal{display:none;position:fixed;inset:0;background:rgba(4,7,14,.97);z-index:1000;flex-direction:column}#modal.open{display:flex}
59
+ .mbar{display:flex;align-items:center;justify-content:space-between;padding:10px 18px;background:rgba(12,17,32,.98);border-bottom:1px solid var(--border);backdrop-filter:blur(8px);flex-shrink:0}
60
+ .mbar h3{font-size:12.5px;font-weight:700}.mbar .hint{font-size:9.5px;color:var(--muted2);margin-left:10px}.mbar-r{display:flex;align-items:center;gap:8px}
61
+ .btn-z{background:rgba(255,255,255,.05);border:1px solid var(--border2);border-radius:6px;color:var(--muted2);cursor:pointer;padding:4px 10px;font-size:13px;font-weight:700;transition:all .12s}
62
+ .btn-z:hover{background:rgba(59,130,246,.12);border-color:var(--blue);color:var(--blue)}.zlbl{font-size:10.5px;color:var(--muted2);min-width:36px;text-align:center}
63
+ .btn-cls{background:rgba(239,68,68,.08);border:1px solid rgba(239,68,68,.2);border-radius:7px;color:#f87171;cursor:pointer;padding:5px 14px;font-size:12px;font-weight:600;transition:all .12s}
64
+ .btn-cls:hover{background:rgba(239,68,68,.2);border-color:var(--red)}
65
+ #mcontent{flex:1;overflow:auto;display:flex;align-items:center;justify-content:center;padding:48px}
66
+ #mdiagram{transform-origin:center center;transition:transform .12s}#mdiagram svg{display:block;width:auto;max-width:100%;height:auto}
67
+ footer{margin-top:48px;padding-top:14px;border-top:1px solid var(--border);font-size:9.5px;color:var(--muted);display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:5px;opacity:.5}
68
+ footer a{color:var(--muted2);text-decoration:none}footer a:hover{color:var(--blue)}
69
+ ::-webkit-scrollbar{width:4px;height:4px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border2);border-radius:4px}`;
70
+ }
71
+
72
+ function buildScript() {
73
+ return `<script>
74
+ let zoom=1;
75
+ function expandDiagram(btn){const card=btn.closest('.dc');const title=card.dataset.title||'Diagram';const svg=card.querySelector('.dc-b svg');if(!svg)return;const clone=svg.cloneNode(true);clone.style.cssText='display:block;width:auto;height:auto;max-width:90vw;max-height:85vh;';clone.removeAttribute('width');clone.removeAttribute('height');const wrap=document.getElementById('mdiagram');wrap.innerHTML='';wrap.appendChild(clone);document.getElementById('mtitle').textContent=title;zoom=1;applyZoom();document.getElementById('modal').classList.add('open');document.body.style.overflow='hidden';}
76
+ function closeModal(){document.getElementById('modal').classList.remove('open');document.body.style.overflow='';}
77
+ function adjustZoom(d){zoom=Math.max(0.2,Math.min(5,zoom+d));applyZoom();}
78
+ function resetZoom(){zoom=1;applyZoom();}
79
+ function applyZoom(){document.getElementById('mdiagram').style.transform='scale('+zoom+')';document.getElementById('zlbl').textContent=Math.round(zoom*100)+'%';}
80
+ document.getElementById('mcontent').addEventListener('wheel',e=>{e.preventDefault();adjustZoom(e.deltaY<0?0.06:-0.06);},{passive:false});
81
+ document.addEventListener('keydown',e=>{if(e.key==='Escape')closeModal();});
82
+ document.getElementById('modal').addEventListener('click',e=>{if(e.target===document.getElementById('modal')||e.target===document.getElementById('mcontent'))closeModal();});
83
+ const navLinks=document.querySelectorAll('nav a[href^="#"]');document.querySelectorAll('section[id]').forEach(s=>new IntersectionObserver(entries=>{if(entries[0].isIntersecting){navLinks.forEach(l=>l.classList.remove('active'));const a=document.querySelector('nav a[href="#'+entries[0].target.id+'"]');if(a)a.classList.add('active');}},{threshold:0.15}).observe(s));
84
+ <\/script>`;
85
+ }
86
+
87
+ function buildSidebar(projectName, version, scanDate, diagTypes) {
88
+ const vp = (version ? esc(version) + ' &middot; ' : '') + esc(scanDate || '');
89
+ const ALL = [['system-architecture','System Architecture'],['app-architecture','App Architecture'],['workflow','Workflow'],['data-flow','Data Flow'],['sequence','Sequence'],['database-schema','Database Schema']];
90
+ const dlinks = ALL.filter(([t]) => !diagTypes || diagTypes.includes(t)).map(([t, l]) => '<a href="#diagram-' + t + '"><span class="nd"></span>' + esc(l) + '</a>').join('\n ');
91
+ return '<nav>\n <div class="nb"><div class="pill">GSD&#x2011;T Scan</div><h2>' + esc(projectName) + '</h2><p>' + vp + '</p></div>\n <div class="ns">Overview</div>\n <a href="#summary"><span class="nd g"></span>Summary</a>\n <a href="#domains"><span class="nd"></span>Domains</a>\n <div class="ns">Diagrams</div>\n ' + dlinks + '\n <div class="ns">Analysis</div>\n <a href="#tech-debt"><span class="nd y"></span>Tech Debt</a>\n <a href="#findings"><span class="nd r"></span>Key Findings</a>\n</nav>';
92
+ }
93
+
94
+ function buildPageHeader(data, opts) {
95
+ const projectName = data.projectName || path.basename(opts.projectRoot || process.cwd());
96
+ const stack = data.techStack || '';
97
+ const files = data.filesScanned ? data.filesScanned + ' files' : '';
98
+ const loc = data.totalLoc ? (data.totalLoc > 999 ? (data.totalLoc / 1000).toFixed(1) + 'k' : data.totalLoc) + ' LoC' : '';
99
+ const fileLoc = [files, loc].filter(Boolean).join(' \u00b7 ');
100
+ const metaParts = [stack, fileLoc, data.scanDate || ''].filter(Boolean);
101
+ const metaHtml = metaParts.map((m, i) =>
102
+ (i > 0 ? '<span class="sep">|</span>' : '') + '<span>' + esc(m) + '</span>'
103
+ ).join('');
104
+ return '<div class="ph"><h1><span class="bc">GSD&#x2011;T &rsaquo; Scan &rsaquo;</span> ' +
105
+ esc(projectName) + ' Codebase Report</h1><div class="meta">' + metaHtml + '</div></div>';
106
+ }
107
+
108
+ function buildHtmlSkeleton(title, css, sidebar, body) {
109
+ return `<!DOCTYPE html>
110
+ <html lang="en">
111
+ <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
112
+ <title>${title}</title>
113
+ <style>
114
+ ${css}
115
+ </style></head>
116
+ <body>
117
+ ${sidebar}
118
+ <div id="modal">
119
+ <div class="mbar">
120
+ <div style="display:flex;align-items:center;gap:8px"><h3 id="mtitle">Diagram</h3><span class="hint">Scroll to zoom &middot; Esc to close</span></div>
121
+ <div class="mbar-r">
122
+ <button class="btn-z" onclick="adjustZoom(-0.15)">&#8722;</button>
123
+ <span class="zlbl" id="zlbl">100%</span>
124
+ <button class="btn-z" onclick="adjustZoom(0.15)">+</button>
125
+ <button class="btn-z" onclick="resetZoom()" style="font-size:10px;padding:4px 9px">Reset</button>
126
+ <button class="btn-cls" onclick="closeModal()">&#10005; Close</button>
127
+ </div>
128
+ </div>
129
+ <div id="mcontent"><div id="mdiagram"></div></div>
130
+ </div>
131
+ <main>
132
+ ${body}
133
+ <footer><span>GSD-T Scan Report</span><span><a href="https://mermaid.js.org">Mermaid.js (MIT)</a></span></footer>
134
+ </main>
135
+ ${buildScript()}
136
+ </body>
137
+ </html>`;
138
+ }
139
+
140
+ function esc(s) { return String(s || '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'); }
141
+ const sections = require('./scan-report-sections.js');
142
+ function generateReport(analysisData, schemaData, diagrams, options) {
143
+ try {
144
+ const safeData = analysisData || {};
145
+ const safeDiagrams = (Array.isArray(diagrams) ? diagrams : []).filter(d => d && d.type);
146
+ const opts = options || {};
147
+ const projectName = safeData.projectName || path.basename(opts.projectRoot || process.cwd());
148
+ const css = buildCss();
149
+ const sidebar = buildSidebar(projectName, safeData.version || '', safeData.scanDate || '', safeDiagrams.map(d => d.type));
150
+ const pageHeader = buildPageHeader(safeData, opts);
151
+ const body = pageHeader
152
+ + sections.buildMetricCards(safeData)
153
+ + sections.buildDomainHealth(safeData)
154
+ + safeDiagrams.map(sections.buildDiagramSection).join('')
155
+ + sections.buildTechDebt(safeData)
156
+ + sections.buildFindings(safeData);
157
+ const html = buildHtmlSkeleton(esc(projectName) + ' \u2014 GSD-T Scan Report', css, sidebar, body);
158
+ const defaultDir = path.join(opts.projectRoot || process.cwd(), '.gsd-t', 'scan');
159
+ const outputDir = opts.outputDir || defaultDir;
160
+ if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
161
+ const outputPath = path.join(outputDir, 'scan-report.html');
162
+ fs.writeFileSync(outputPath, html, 'utf8');
163
+ return {
164
+ outputPath,
165
+ diagramsRendered: safeDiagrams.filter(d => d.rendered).length,
166
+ diagramsPlaceholder: safeDiagrams.filter(d => !d.rendered).length
167
+ };
168
+ } catch (err) {
169
+ process.stderr.write('scan-report error: ' + err.message + '\n');
170
+ return { outputPath: null, error: err.message };
171
+ }
172
+ }
173
+
174
+ module.exports = {
175
+ generateReport,
176
+ buildCss,
177
+ buildSidebar,
178
+ buildHtmlSkeleton,
179
+ buildMetricCards: sections.buildMetricCards,
180
+ buildDomainHealth: sections.buildDomainHealth,
181
+ buildDiagramSection: sections.buildDiagramSection,
182
+ buildTechDebt: sections.buildTechDebt,
183
+ buildFindings: sections.buildFindings
184
+ };