@planu/cli 0.54.2 → 0.56.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.
Files changed (178) hide show
  1. package/dist/config/license-plans.json +1 -0
  2. package/dist/engine/doc-generator/executive-summary/executive-summary-generator.d.ts +7 -1
  3. package/dist/engine/doc-generator/executive-summary/executive-summary-generator.d.ts.map +1 -1
  4. package/dist/engine/doc-generator/executive-summary/executive-summary-generator.js +16 -3
  5. package/dist/engine/doc-generator/executive-summary/executive-summary-generator.js.map +1 -1
  6. package/dist/engine/doc-generator/executive-summary/html-template.d.ts +5 -2
  7. package/dist/engine/doc-generator/executive-summary/html-template.d.ts.map +1 -1
  8. package/dist/engine/doc-generator/executive-summary/html-template.js +7 -3
  9. package/dist/engine/doc-generator/executive-summary/html-template.js.map +1 -1
  10. package/dist/engine/doc-generator/per-spec-report/executive-report.d.ts +3 -1
  11. package/dist/engine/doc-generator/per-spec-report/executive-report.d.ts.map +1 -1
  12. package/dist/engine/doc-generator/per-spec-report/executive-report.js +22 -2
  13. package/dist/engine/doc-generator/per-spec-report/executive-report.js.map +1 -1
  14. package/dist/engine/doc-generator/per-spec-report/html-wrapper.d.ts +5 -3
  15. package/dist/engine/doc-generator/per-spec-report/html-wrapper.d.ts.map +1 -1
  16. package/dist/engine/doc-generator/per-spec-report/html-wrapper.js +27 -6
  17. package/dist/engine/doc-generator/per-spec-report/html-wrapper.js.map +1 -1
  18. package/dist/engine/doc-generator/per-spec-report/spec-data-extractor.d.ts +1 -0
  19. package/dist/engine/doc-generator/per-spec-report/spec-data-extractor.d.ts.map +1 -1
  20. package/dist/engine/doc-generator/per-spec-report/spec-data-extractor.js +29 -0
  21. package/dist/engine/doc-generator/per-spec-report/spec-data-extractor.js.map +1 -1
  22. package/dist/engine/doc-generator/per-spec-report/spec-section-builders.d.ts +8 -1
  23. package/dist/engine/doc-generator/per-spec-report/spec-section-builders.d.ts.map +1 -1
  24. package/dist/engine/doc-generator/per-spec-report/spec-section-builders.js +188 -0
  25. package/dist/engine/doc-generator/per-spec-report/spec-section-builders.js.map +1 -1
  26. package/dist/engine/doc-generator/per-spec-report/technical-report.d.ts +3 -1
  27. package/dist/engine/doc-generator/per-spec-report/technical-report.d.ts.map +1 -1
  28. package/dist/engine/doc-generator/per-spec-report/technical-report.js +24 -7
  29. package/dist/engine/doc-generator/per-spec-report/technical-report.js.map +1 -1
  30. package/dist/engine/doc-generator/portal/analytics-page.d.ts +8 -0
  31. package/dist/engine/doc-generator/portal/analytics-page.d.ts.map +1 -0
  32. package/dist/engine/doc-generator/portal/analytics-page.js +265 -0
  33. package/dist/engine/doc-generator/portal/analytics-page.js.map +1 -0
  34. package/dist/engine/doc-generator/portal/architecture-page.d.ts +6 -0
  35. package/dist/engine/doc-generator/portal/architecture-page.d.ts.map +1 -0
  36. package/dist/engine/doc-generator/portal/architecture-page.js +250 -0
  37. package/dist/engine/doc-generator/portal/architecture-page.js.map +1 -0
  38. package/dist/engine/doc-generator/portal/changelog-page.d.ts +6 -0
  39. package/dist/engine/doc-generator/portal/changelog-page.d.ts.map +1 -0
  40. package/dist/engine/doc-generator/portal/changelog-page.js +204 -0
  41. package/dist/engine/doc-generator/portal/changelog-page.js.map +1 -0
  42. package/dist/engine/doc-generator/portal/data-aggregators/accuracy-aggregator.d.ts +11 -0
  43. package/dist/engine/doc-generator/portal/data-aggregators/accuracy-aggregator.d.ts.map +1 -0
  44. package/dist/engine/doc-generator/portal/data-aggregators/accuracy-aggregator.js +24 -0
  45. package/dist/engine/doc-generator/portal/data-aggregators/accuracy-aggregator.js.map +1 -0
  46. package/dist/engine/doc-generator/portal/data-aggregators/bottleneck-detector.d.ts +9 -0
  47. package/dist/engine/doc-generator/portal/data-aggregators/bottleneck-detector.d.ts.map +1 -0
  48. package/dist/engine/doc-generator/portal/data-aggregators/bottleneck-detector.js +31 -0
  49. package/dist/engine/doc-generator/portal/data-aggregators/bottleneck-detector.js.map +1 -0
  50. package/dist/engine/doc-generator/portal/data-aggregators/burndown-aggregator.d.ts +10 -0
  51. package/dist/engine/doc-generator/portal/data-aggregators/burndown-aggregator.d.ts.map +1 -0
  52. package/dist/engine/doc-generator/portal/data-aggregators/burndown-aggregator.js +77 -0
  53. package/dist/engine/doc-generator/portal/data-aggregators/burndown-aggregator.js.map +1 -0
  54. package/dist/engine/doc-generator/portal/data-aggregators/velocity-aggregator.d.ts +9 -0
  55. package/dist/engine/doc-generator/portal/data-aggregators/velocity-aggregator.d.ts.map +1 -0
  56. package/dist/engine/doc-generator/portal/data-aggregators/velocity-aggregator.js +31 -0
  57. package/dist/engine/doc-generator/portal/data-aggregators/velocity-aggregator.js.map +1 -0
  58. package/dist/engine/doc-generator/portal/decision-page.d.ts +6 -0
  59. package/dist/engine/doc-generator/portal/decision-page.d.ts.map +1 -0
  60. package/dist/engine/doc-generator/portal/decision-page.js +169 -0
  61. package/dist/engine/doc-generator/portal/decision-page.js.map +1 -0
  62. package/dist/engine/doc-generator/portal/decision-timeline-builder.d.ts +8 -0
  63. package/dist/engine/doc-generator/portal/decision-timeline-builder.d.ts.map +1 -0
  64. package/dist/engine/doc-generator/portal/decision-timeline-builder.js +131 -0
  65. package/dist/engine/doc-generator/portal/decision-timeline-builder.js.map +1 -0
  66. package/dist/engine/doc-generator/portal/index.d.ts +19 -0
  67. package/dist/engine/doc-generator/portal/index.d.ts.map +1 -0
  68. package/dist/engine/doc-generator/portal/index.js +21 -0
  69. package/dist/engine/doc-generator/portal/index.js.map +1 -0
  70. package/dist/engine/doc-generator/portal/kanban-view-builder.d.ts +8 -0
  71. package/dist/engine/doc-generator/portal/kanban-view-builder.d.ts.map +1 -0
  72. package/dist/engine/doc-generator/portal/kanban-view-builder.js +140 -0
  73. package/dist/engine/doc-generator/portal/kanban-view-builder.js.map +1 -0
  74. package/dist/engine/doc-generator/portal/portal-breadcrumbs.d.ts +27 -0
  75. package/dist/engine/doc-generator/portal/portal-breadcrumbs.d.ts.map +1 -0
  76. package/dist/engine/doc-generator/portal/portal-breadcrumbs.js +139 -0
  77. package/dist/engine/doc-generator/portal/portal-breadcrumbs.js.map +1 -0
  78. package/dist/engine/doc-generator/portal/portal-landing-cards.d.ts +8 -0
  79. package/dist/engine/doc-generator/portal/portal-landing-cards.d.ts.map +1 -0
  80. package/dist/engine/doc-generator/portal/portal-landing-cards.js +177 -0
  81. package/dist/engine/doc-generator/portal/portal-landing-cards.js.map +1 -0
  82. package/dist/engine/doc-generator/portal/portal-navbar.d.ts +15 -0
  83. package/dist/engine/doc-generator/portal/portal-navbar.d.ts.map +1 -0
  84. package/dist/engine/doc-generator/portal/portal-navbar.js +143 -0
  85. package/dist/engine/doc-generator/portal/portal-navbar.js.map +1 -0
  86. package/dist/engine/doc-generator/portal/portal-page-detector.d.ts +22 -0
  87. package/dist/engine/doc-generator/portal/portal-page-detector.d.ts.map +1 -0
  88. package/dist/engine/doc-generator/portal/portal-page-detector.js +58 -0
  89. package/dist/engine/doc-generator/portal/portal-page-detector.js.map +1 -0
  90. package/dist/engine/doc-generator/portal/portal-theme.d.ts +7 -0
  91. package/dist/engine/doc-generator/portal/portal-theme.d.ts.map +1 -0
  92. package/dist/engine/doc-generator/portal/portal-theme.js +53 -0
  93. package/dist/engine/doc-generator/portal/portal-theme.js.map +1 -0
  94. package/dist/engine/doc-generator/portal/risk-gauge-svg.d.ts +5 -0
  95. package/dist/engine/doc-generator/portal/risk-gauge-svg.d.ts.map +1 -0
  96. package/dist/engine/doc-generator/portal/risk-gauge-svg.js +80 -0
  97. package/dist/engine/doc-generator/portal/risk-gauge-svg.js.map +1 -0
  98. package/dist/engine/doc-generator/portal/risk-matrix-svg.d.ts +11 -0
  99. package/dist/engine/doc-generator/portal/risk-matrix-svg.d.ts.map +1 -0
  100. package/dist/engine/doc-generator/portal/risk-matrix-svg.js +181 -0
  101. package/dist/engine/doc-generator/portal/risk-matrix-svg.js.map +1 -0
  102. package/dist/engine/doc-generator/portal/risk-page.d.ts +7 -0
  103. package/dist/engine/doc-generator/portal/risk-page.d.ts.map +1 -0
  104. package/dist/engine/doc-generator/portal/risk-page.js +219 -0
  105. package/dist/engine/doc-generator/portal/risk-page.js.map +1 -0
  106. package/dist/engine/doc-generator/portal/roadmap-page.d.ts +7 -0
  107. package/dist/engine/doc-generator/portal/roadmap-page.d.ts.map +1 -0
  108. package/dist/engine/doc-generator/portal/roadmap-page.js +204 -0
  109. package/dist/engine/doc-generator/portal/roadmap-page.js.map +1 -0
  110. package/dist/engine/doc-generator/portal/svg-charts/bar-chart.d.ts +4 -0
  111. package/dist/engine/doc-generator/portal/svg-charts/bar-chart.d.ts.map +1 -0
  112. package/dist/engine/doc-generator/portal/svg-charts/bar-chart.js +50 -0
  113. package/dist/engine/doc-generator/portal/svg-charts/bar-chart.js.map +1 -0
  114. package/dist/engine/doc-generator/portal/svg-charts/chart-utils.d.ts +16 -0
  115. package/dist/engine/doc-generator/portal/svg-charts/chart-utils.d.ts.map +1 -0
  116. package/dist/engine/doc-generator/portal/svg-charts/chart-utils.js +45 -0
  117. package/dist/engine/doc-generator/portal/svg-charts/chart-utils.js.map +1 -0
  118. package/dist/engine/doc-generator/portal/svg-charts/line-chart.d.ts +16 -0
  119. package/dist/engine/doc-generator/portal/svg-charts/line-chart.d.ts.map +1 -0
  120. package/dist/engine/doc-generator/portal/svg-charts/line-chart.js +159 -0
  121. package/dist/engine/doc-generator/portal/svg-charts/line-chart.js.map +1 -0
  122. package/dist/engine/doc-generator/portal/svg-charts/pie-chart.d.ts +7 -0
  123. package/dist/engine/doc-generator/portal/svg-charts/pie-chart.d.ts.map +1 -0
  124. package/dist/engine/doc-generator/portal/svg-charts/pie-chart.js +52 -0
  125. package/dist/engine/doc-generator/portal/svg-charts/pie-chart.js.map +1 -0
  126. package/dist/engine/doc-generator/proposal/proposal-gantt-builder.d.ts +8 -0
  127. package/dist/engine/doc-generator/proposal/proposal-gantt-builder.d.ts.map +1 -0
  128. package/dist/engine/doc-generator/proposal/proposal-gantt-builder.js +126 -0
  129. package/dist/engine/doc-generator/proposal/proposal-gantt-builder.js.map +1 -0
  130. package/dist/engine/doc-generator/proposal/proposal-generator.d.ts +10 -0
  131. package/dist/engine/doc-generator/proposal/proposal-generator.d.ts.map +1 -0
  132. package/dist/engine/doc-generator/proposal/proposal-generator.js +93 -0
  133. package/dist/engine/doc-generator/proposal/proposal-generator.js.map +1 -0
  134. package/dist/engine/doc-generator/proposal/proposal-html-wrapper.d.ts +7 -0
  135. package/dist/engine/doc-generator/proposal/proposal-html-wrapper.d.ts.map +1 -0
  136. package/dist/engine/doc-generator/proposal/proposal-html-wrapper.js +31 -0
  137. package/dist/engine/doc-generator/proposal/proposal-html-wrapper.js.map +1 -0
  138. package/dist/engine/doc-generator/proposal/proposal-i18n.d.ts +12 -0
  139. package/dist/engine/doc-generator/proposal/proposal-i18n.d.ts.map +1 -0
  140. package/dist/engine/doc-generator/proposal/proposal-i18n.js +230 -0
  141. package/dist/engine/doc-generator/proposal/proposal-i18n.js.map +1 -0
  142. package/dist/engine/doc-generator/proposal/proposal-kpi-builder.d.ts +8 -0
  143. package/dist/engine/doc-generator/proposal/proposal-kpi-builder.d.ts.map +1 -0
  144. package/dist/engine/doc-generator/proposal/proposal-kpi-builder.js +32 -0
  145. package/dist/engine/doc-generator/proposal/proposal-kpi-builder.js.map +1 -0
  146. package/dist/engine/doc-generator/proposal/proposal-section-builders-advanced.d.ts +14 -0
  147. package/dist/engine/doc-generator/proposal/proposal-section-builders-advanced.d.ts.map +1 -0
  148. package/dist/engine/doc-generator/proposal/proposal-section-builders-advanced.js +248 -0
  149. package/dist/engine/doc-generator/proposal/proposal-section-builders-advanced.js.map +1 -0
  150. package/dist/engine/doc-generator/proposal/proposal-section-builders.d.ts +13 -0
  151. package/dist/engine/doc-generator/proposal/proposal-section-builders.d.ts.map +1 -0
  152. package/dist/engine/doc-generator/proposal/proposal-section-builders.js +423 -0
  153. package/dist/engine/doc-generator/proposal/proposal-section-builders.js.map +1 -0
  154. package/dist/engine/doc-generator/proposal/proposal-themes.d.ts +4 -0
  155. package/dist/engine/doc-generator/proposal/proposal-themes.d.ts.map +1 -0
  156. package/dist/engine/doc-generator/proposal/proposal-themes.js +194 -0
  157. package/dist/engine/doc-generator/proposal/proposal-themes.js.map +1 -0
  158. package/dist/engine/spec-summary-html.d.ts.map +1 -1
  159. package/dist/engine/spec-summary-html.js +17 -5
  160. package/dist/engine/spec-summary-html.js.map +1 -1
  161. package/dist/index.js +2 -0
  162. package/dist/index.js.map +1 -1
  163. package/dist/tools/generate-proposal.d.ts +3 -0
  164. package/dist/tools/generate-proposal.d.ts.map +1 -0
  165. package/dist/tools/generate-proposal.js +166 -0
  166. package/dist/tools/generate-proposal.js.map +1 -0
  167. package/dist/types/docs.d.ts +75 -0
  168. package/dist/types/docs.d.ts.map +1 -1
  169. package/dist/types/portal.d.ts +128 -0
  170. package/dist/types/portal.d.ts.map +1 -0
  171. package/dist/types/portal.js +3 -0
  172. package/dist/types/portal.js.map +1 -0
  173. package/dist/types/proposal.d.ts +57 -0
  174. package/dist/types/proposal.d.ts.map +1 -0
  175. package/dist/types/proposal.js +2 -0
  176. package/dist/types/proposal.js.map +1 -0
  177. package/package.json +1 -1
  178. package/src/config/license-plans.json +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal-theme.js","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/portal-theme.ts"],"names":[],"mappings":"AAAA,yEAAyE;AAEzE;;;;GAIG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA2CL,CAAC;AACL,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Build a semicircle gauge SVG for a risk score (0-100).
3
+ */
4
+ export declare function buildRiskGaugeSvg(score: number): string;
5
+ //# sourceMappingURL=risk-gauge-svg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"risk-gauge-svg.d.ts","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/risk-gauge-svg.ts"],"names":[],"mappings":"AAkDA;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAwCvD"}
@@ -0,0 +1,80 @@
1
+ // Planu — Risk Gauge semicircle SVG (SPEC-143)
2
+ const W = 200;
3
+ const H = 120;
4
+ const CX = W / 2;
5
+ const CY = 105;
6
+ const R = 80;
7
+ /**
8
+ * Convert polar angle (in degrees, 0 = right) to SVG x,y on the gauge arc.
9
+ * Gauge goes from 180° (left) to 0° (right), i.e. the top semicircle.
10
+ */
11
+ function polarToXY(angleDeg, radius) {
12
+ const rad = (angleDeg * Math.PI) / 180;
13
+ return {
14
+ x: CX + radius * Math.cos(rad),
15
+ y: CY - radius * Math.sin(rad),
16
+ };
17
+ }
18
+ function scoreToAngle(score) {
19
+ // Map 0→180°, 100→0° (left to right)
20
+ const clamped = Math.max(0, Math.min(100, score));
21
+ return 180 - clamped * 1.8;
22
+ }
23
+ function gaugeColor(score) {
24
+ if (score <= 30) {
25
+ return '#22c55e';
26
+ } // green
27
+ if (score <= 60) {
28
+ return '#f59e0b';
29
+ } // yellow
30
+ return '#ef4444'; // red
31
+ }
32
+ function buildArcPath(startAngle, endAngle, r, _strokeWidth) {
33
+ const start = polarToXY(startAngle, r);
34
+ const end = polarToXY(endAngle, r);
35
+ const largeArc = Math.abs(endAngle - startAngle) > 180 ? 1 : 0;
36
+ // Sweep: going from high angle to low angle means clockwise in SVG
37
+ const sweep = endAngle < startAngle ? 1 : 0;
38
+ return `M ${start.x.toFixed(1)} ${start.y.toFixed(1)} A ${r} ${r} 0 ${largeArc} ${sweep} ${end.x.toFixed(1)} ${end.y.toFixed(1)}`;
39
+ }
40
+ /**
41
+ * Build a semicircle gauge SVG for a risk score (0-100).
42
+ */
43
+ export function buildRiskGaugeSvg(score) {
44
+ const clamped = Math.max(0, Math.min(100, score));
45
+ const color = gaugeColor(clamped);
46
+ const needleAngle = scoreToAngle(clamped);
47
+ const needle = polarToXY(needleAngle, R - 10);
48
+ // Background arc (grey, full 180°)
49
+ const bgPath = buildArcPath(180, 0, R, 14);
50
+ // Foreground arc (colored, proportional)
51
+ const fgPath = buildArcPath(180, needleAngle, R, 14);
52
+ // Zone arcs (thin overlay for green/yellow/red zones)
53
+ const greenEnd = scoreToAngle(30);
54
+ const yellowEnd = scoreToAngle(60);
55
+ const greenArc = buildArcPath(180, greenEnd, R, 14);
56
+ const yellowArc = buildArcPath(greenEnd, yellowEnd, R, 14);
57
+ const redArc = buildArcPath(yellowEnd, 0, R, 14);
58
+ return `<svg viewBox="0 0 ${W} ${H}" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Risk Score Gauge: ${clamped}">
59
+ <!-- Zone backgrounds -->
60
+ <path d="${greenArc}" fill="none" stroke="#d1fae5" stroke-width="14" stroke-linecap="butt"/>
61
+ <path d="${yellowArc}" fill="none" stroke="#fef3c7" stroke-width="14" stroke-linecap="butt"/>
62
+ <path d="${redArc}" fill="none" stroke="#fee2e2" stroke-width="14" stroke-linecap="butt"/>
63
+ <!-- Progress arc -->
64
+ <path d="${bgPath}" fill="none" stroke="#e5e7eb" stroke-width="10" stroke-linecap="round"/>
65
+ <path d="${fgPath}" fill="none" stroke="${color}" stroke-width="10" stroke-linecap="round"/>
66
+ <!-- Needle dot -->
67
+ <circle cx="${needle.x.toFixed(1)}" cy="${needle.y.toFixed(1)}" r="5" fill="${color}" stroke="white" stroke-width="2"/>
68
+ <!-- Center dot -->
69
+ <circle cx="${CX}" cy="${CY}" r="4" fill="#374151"/>
70
+ <!-- Score number -->
71
+ <text x="${CX}" y="${CY - 20}" text-anchor="middle" font-size="28" font-weight="700" fill="${color}">${clamped}</text>
72
+ <!-- Label -->
73
+ <text x="${CX}" y="${CY - 4}" text-anchor="middle" font-size="11" fill="#6b7280">Risk Score</text>
74
+ <!-- Scale labels -->
75
+ <text x="14" y="${CY + 16}" font-size="9" fill="#6b7280">0</text>
76
+ <text x="${CX - 4}" y="22" font-size="9" fill="#6b7280">50</text>
77
+ <text x="${W - 22}" y="${CY + 16}" font-size="9" fill="#6b7280">100</text>
78
+ </svg>`;
79
+ }
80
+ //# sourceMappingURL=risk-gauge-svg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"risk-gauge-svg.js","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/risk-gauge-svg.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { SpecRisk, RiskCategory } from '../../../types/risk.js';
2
+ declare const IMPACT_LEVELS: readonly ["low", "medium", "high", "very_high", "critical"];
3
+ declare const PROB_LEVELS: readonly [20, 40, 60, 80, 100];
4
+ declare const CATEGORY_COLORS: Record<RiskCategory, string>;
5
+ /**
6
+ * Build a 5x5 risk matrix SVG.
7
+ * Returns empty string when risks array is empty.
8
+ */
9
+ export declare function buildRiskMatrixSvg(risks: SpecRisk[]): string;
10
+ export { IMPACT_LEVELS, PROB_LEVELS, CATEGORY_COLORS };
11
+ //# sourceMappingURL=risk-matrix-svg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"risk-matrix-svg.d.ts","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/risk-matrix-svg.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAErE,QAAA,MAAM,aAAa,6DAA8D,CAAC;AAClF,QAAA,MAAM,WAAW,gCAAiC,CAAC;AAEnD,QAAA,MAAM,eAAe,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAOjD,CAAC;AA2LF;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,CAc5D;AAED,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC"}
@@ -0,0 +1,181 @@
1
+ const IMPACT_LEVELS = ['low', 'medium', 'high', 'very_high', 'critical'];
2
+ const PROB_LEVELS = [20, 40, 60, 80, 100];
3
+ const CATEGORY_COLORS = {
4
+ technical: '#3b82f6',
5
+ security: '#ef4444',
6
+ integration: '#f97316',
7
+ dependency: '#8b5cf6',
8
+ organizational: '#6b7280',
9
+ data: '#10b981',
10
+ };
11
+ const QUADRANT_COLORS = [
12
+ // Row 0 (prob 80-100) high prob
13
+ ['#fef3c7', '#fde68a', '#fcd34d', '#f87171', '#ef4444'],
14
+ // Row 1 (prob 60-80)
15
+ ['#d1fae5', '#fef3c7', '#fde68a', '#fcd34d', '#f87171'],
16
+ // Row 2 (prob 40-60)
17
+ ['#d1fae5', '#d1fae5', '#fef3c7', '#fde68a', '#fcd34d'],
18
+ // Row 3 (prob 20-40)
19
+ ['#d1fae5', '#d1fae5', '#d1fae5', '#fef3c7', '#fde68a'],
20
+ // Row 4 (prob 0-20) low prob
21
+ ['#d1fae5', '#d1fae5', '#d1fae5', '#d1fae5', '#fef3c7'],
22
+ ];
23
+ const CELL = 60;
24
+ const PADDING = 40;
25
+ const LABEL_PAD = 14;
26
+ const GRID_W = CELL * 5;
27
+ const GRID_H = CELL * 5;
28
+ const SVG_W = GRID_W + PADDING + LABEL_PAD;
29
+ const SVG_H = GRID_H + PADDING + LABEL_PAD;
30
+ function impactIndex(risk) {
31
+ // Map impactUsd ranges to 0-4
32
+ if (risk.impactUsd < 1000) {
33
+ return 0;
34
+ }
35
+ if (risk.impactUsd < 5000) {
36
+ return 1;
37
+ }
38
+ if (risk.impactUsd < 20000) {
39
+ return 2;
40
+ }
41
+ if (risk.impactUsd < 50000) {
42
+ return 3;
43
+ }
44
+ return 4;
45
+ }
46
+ function probIndex(risk) {
47
+ const p = risk.probability;
48
+ if (p <= 20) {
49
+ return 4;
50
+ } // row 4 = lowest
51
+ if (p <= 40) {
52
+ return 3;
53
+ }
54
+ if (p <= 60) {
55
+ return 2;
56
+ }
57
+ if (p <= 80) {
58
+ return 1;
59
+ }
60
+ return 0;
61
+ }
62
+ function circleSizeFromUsd(usd) {
63
+ if (usd < 1000) {
64
+ return 8;
65
+ }
66
+ if (usd < 5000) {
67
+ return 12;
68
+ }
69
+ if (usd < 20000) {
70
+ return 16;
71
+ }
72
+ if (usd < 50000) {
73
+ return 20;
74
+ }
75
+ return 24;
76
+ }
77
+ function escapeHtml(s) {
78
+ return s
79
+ .replace(/&/g, '&amp;')
80
+ .replace(/</g, '&lt;')
81
+ .replace(/>/g, '&gt;')
82
+ .replace(/"/g, '&quot;');
83
+ }
84
+ function buildGridCells() {
85
+ const cells = [];
86
+ for (let row = 0; row < 5; row++) {
87
+ for (let col = 0; col < 5; col++) {
88
+ const x = PADDING + col * CELL;
89
+ const y = LABEL_PAD + row * CELL;
90
+ cells.push(`<rect x="${x}" y="${y}" width="${CELL}" height="${CELL}" fill="${QUADRANT_COLORS[row]?.[col] ?? '#d1fae5'}" stroke="#e5e7eb" stroke-width="1"/>`);
91
+ }
92
+ }
93
+ return cells.join('\n ');
94
+ }
95
+ function buildAxisLabels() {
96
+ const parts = [];
97
+ // X-axis (Impact) labels
98
+ const impactLabels = ['Low', 'Med', 'High', 'V.High', 'Crit'];
99
+ for (let i = 0; i < 5; i++) {
100
+ const x = PADDING + i * CELL + CELL / 2;
101
+ const y = LABEL_PAD + GRID_H + 14;
102
+ parts.push(`<text x="${x}" y="${y}" text-anchor="middle" font-size="10" fill="#6b7280">${impactLabels[i]}</text>`);
103
+ }
104
+ // X-axis title
105
+ parts.push(`<text x="${PADDING + GRID_W / 2}" y="${SVG_H - 2}" text-anchor="middle" font-size="11" font-weight="600" fill="#374151">Impact</text>`);
106
+ // Y-axis (Probability) labels
107
+ const probLabels = ['80-100%', '60-80%', '40-60%', '20-40%', '0-20%'];
108
+ for (let i = 0; i < 5; i++) {
109
+ const y = LABEL_PAD + i * CELL + CELL / 2 + 4;
110
+ parts.push(`<text x="${PADDING - 4}" y="${y}" text-anchor="end" font-size="10" fill="#6b7280">${probLabels[i]}</text>`);
111
+ }
112
+ // Y-axis title (rotated)
113
+ const cx = 10;
114
+ const cy = LABEL_PAD + GRID_H / 2;
115
+ parts.push(`<text x="${cx}" y="${cy}" text-anchor="middle" font-size="11" font-weight="600" fill="#374151" transform="rotate(-90,${cx},${cy})">Probability</text>`);
116
+ return parts.join('\n ');
117
+ }
118
+ function buildRiskDots(risks) {
119
+ // Group risks in same cell to offset them
120
+ const cellMap = new Map();
121
+ for (const r of risks) {
122
+ const key = `${probIndex(r)},${impactIndex(r)}`;
123
+ const arr = cellMap.get(key) ?? [];
124
+ arr.push(r);
125
+ cellMap.set(key, arr);
126
+ }
127
+ const dots = [];
128
+ for (const [key, cellRisks] of cellMap) {
129
+ const parts = key.split(',').map(Number);
130
+ const row = parts[0] ?? 0;
131
+ const col = parts[1] ?? 0;
132
+ const cx = PADDING + col * CELL + CELL / 2;
133
+ const cy = LABEL_PAD + row * CELL + CELL / 2;
134
+ cellRisks.forEach((r, idx) => {
135
+ const offset = (idx - (cellRisks.length - 1) / 2) * 10;
136
+ const rx = cx + (cellRisks.length > 1 ? offset : 0);
137
+ const size = circleSizeFromUsd(r.impactUsd);
138
+ const color = CATEGORY_COLORS[r.category];
139
+ const label = escapeHtml(r.id);
140
+ dots.push(`<circle cx="${rx}" cy="${cy}" r="${size / 2}" fill="${color}" fill-opacity="0.85" stroke="white" stroke-width="1.5">` +
141
+ `<title>${label}: ${escapeHtml(r.description)} ($${r.impactUsd.toLocaleString()})</title>` +
142
+ `</circle>`);
143
+ });
144
+ }
145
+ return dots.join('\n ');
146
+ }
147
+ function buildLegend(risks) {
148
+ const categories = [...new Set(risks.map((r) => r.category))];
149
+ const parts = [];
150
+ const startY = LABEL_PAD + 4;
151
+ const startX = PADDING + GRID_W + 10;
152
+ if (startX + 90 > SVG_W + 110) {
153
+ return '';
154
+ } // no room
155
+ categories.forEach((cat, i) => {
156
+ const y = startY + i * 18;
157
+ const color = CATEGORY_COLORS[cat];
158
+ parts.push(`<circle cx="${startX + 6}" cy="${y}" r="5" fill="${color}"/>`);
159
+ parts.push(`<text x="${startX + 14}" y="${y + 4}" font-size="10" fill="#374151">${escapeHtml(cat)}</text>`);
160
+ });
161
+ return parts.join('\n ');
162
+ }
163
+ /**
164
+ * Build a 5x5 risk matrix SVG.
165
+ * Returns empty string when risks array is empty.
166
+ */
167
+ export function buildRiskMatrixSvg(risks) {
168
+ if (risks.length === 0) {
169
+ return '';
170
+ }
171
+ const legendWidth = 90;
172
+ const totalW = SVG_W + legendWidth;
173
+ return `<svg viewBox="0 0 ${totalW} ${SVG_H}" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Risk Matrix">
174
+ ${buildGridCells()}
175
+ ${buildAxisLabels()}
176
+ ${buildRiskDots(risks)}
177
+ ${buildLegend(risks)}
178
+ </svg>`;
179
+ }
180
+ export { IMPACT_LEVELS, PROB_LEVELS, CATEGORY_COLORS };
181
+ //# sourceMappingURL=risk-matrix-svg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"risk-matrix-svg.js","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/risk-matrix-svg.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { RiskRegister } from '../../../types/risk.js';
2
+ import type { Spec } from '../../../types/spec/core.js';
3
+ /**
4
+ * Generate the risks.html portal page.
5
+ */
6
+ export declare function generateRiskPage(risks: RiskRegister[], specs: Spec[]): string;
7
+ //# sourceMappingURL=risk-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"risk-page.d.ts","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/risk-page.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAsC,MAAM,wBAAwB,CAAC;AAC/F,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAC;AAsMxD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,CA6C7E"}
@@ -0,0 +1,219 @@
1
+ import { buildNavbar } from './portal-navbar.js';
2
+ import { getPortalThemeCSS } from './portal-theme.js';
3
+ import { buildRiskMatrixSvg } from './risk-matrix-svg.js';
4
+ import { buildRiskGaugeSvg } from './risk-gauge-svg.js';
5
+ function escapeHtml(s) {
6
+ return s
7
+ .replace(/&/g, '&amp;')
8
+ .replace(/</g, '&lt;')
9
+ .replace(/>/g, '&gt;')
10
+ .replace(/"/g, '&quot;');
11
+ }
12
+ const STATUS_BADGE = {
13
+ identified: 'planu-badge-muted',
14
+ mitigating: 'planu-badge-info',
15
+ mitigated: 'planu-badge-success',
16
+ accepted: 'planu-badge-warning',
17
+ materialized: 'planu-badge-danger',
18
+ };
19
+ function statusBadge(status) {
20
+ const cls = STATUS_BADGE[status];
21
+ return `<span class="planu-badge ${cls}">${escapeHtml(status)}</span>`;
22
+ }
23
+ function categoryBadge(cat) {
24
+ const colors = {
25
+ technical: 'planu-badge-info',
26
+ security: 'planu-badge-danger',
27
+ integration: 'planu-badge-warning',
28
+ dependency: 'planu-badge-muted',
29
+ organizational: 'planu-badge-muted',
30
+ data: 'planu-badge-success',
31
+ };
32
+ return `<span class="planu-badge ${colors[cat]}">${escapeHtml(cat)}</span>`;
33
+ }
34
+ function buildRiskTable(allRisks, specMap) {
35
+ if (allRisks.length === 0) {
36
+ return `<p class="empty-message">No risks registered yet.</p>`;
37
+ }
38
+ const rows = allRisks
39
+ .map((r) => {
40
+ const specTitle = specMap.get(r.specId) ?? r.specId;
41
+ return `<tr>
42
+ <td>${escapeHtml(r.id)}</td>
43
+ <td title="${escapeHtml(r.specId)}">${escapeHtml(specTitle)}</td>
44
+ <td>${escapeHtml(r.description)}</td>
45
+ <td>${categoryBadge(r.category)}</td>
46
+ <td class="num">${r.probability}%</td>
47
+ <td class="num">$${r.impactUsd.toLocaleString()}</td>
48
+ <td class="num">${r.impactDays}d</td>
49
+ <td>${escapeHtml(r.mitigationPlan || '—')}</td>
50
+ <td>${escapeHtml(r.owner || '—')}</td>
51
+ <td>${statusBadge(r.status)}</td>
52
+ </tr>`;
53
+ })
54
+ .join('\n');
55
+ return `<div class="table-wrap">
56
+ <table class="risk-table sortable">
57
+ <thead>
58
+ <tr>
59
+ <th>ID</th>
60
+ <th>Spec</th>
61
+ <th>Description</th>
62
+ <th>Category</th>
63
+ <th class="sortable-col">Probability</th>
64
+ <th class="sortable-col">Impact $</th>
65
+ <th class="sortable-col">Impact Days</th>
66
+ <th>Mitigation</th>
67
+ <th>Owner</th>
68
+ <th>Status</th>
69
+ </tr>
70
+ </thead>
71
+ <tbody>
72
+ ${rows}
73
+ </tbody>
74
+ </table>
75
+ </div>`;
76
+ }
77
+ function buildCoverageSection(allRisks) {
78
+ if (allRisks.length === 0) {
79
+ return '';
80
+ }
81
+ const total = allRisks.length;
82
+ const withPlan = allRisks.filter((r) => r.mitigationPlan && r.mitigationPlan.trim().length > 0).length;
83
+ const mitigated = allRisks.filter((r) => r.status === 'mitigated').length;
84
+ const noOwner = allRisks.filter((r) => !r.owner || r.owner.trim() === '').length;
85
+ const planPct = Math.round((withPlan / total) * 100);
86
+ const mitigatedPct = Math.round((mitigated / total) * 100);
87
+ const warning = noOwner > 0
88
+ ? `<div class="coverage-warning">⚠️ <strong>${noOwner}</strong> risk${noOwner > 1 ? 's' : ''} without an assigned owner.</div>`
89
+ : '';
90
+ return `<section class="planu-card section-card">
91
+ <h2>Mitigation Coverage</h2>
92
+ <div class="coverage-grid">
93
+ <div class="coverage-item">
94
+ <span class="coverage-pct">${planPct}%</span>
95
+ <span class="coverage-label">With Mitigation Plan</span>
96
+ </div>
97
+ <div class="coverage-item">
98
+ <span class="coverage-pct coverage-green">${mitigatedPct}%</span>
99
+ <span class="coverage-label">Mitigated</span>
100
+ </div>
101
+ <div class="coverage-item">
102
+ <span class="coverage-pct coverage-red">${noOwner}</span>
103
+ <span class="coverage-label">Without Owner</span>
104
+ </div>
105
+ </div>
106
+ ${warning}
107
+ </section>`;
108
+ }
109
+ function buildScoreCard(registers, allRisks) {
110
+ const avgScore = registers.length > 0
111
+ ? Math.round(registers.reduce((s, r) => s + r.aggregateRiskScore, 0) / registers.length)
112
+ : 0;
113
+ const highCount = allRisks.filter((r) => r.probability >= 60 && r.impactUsd >= 5000).length;
114
+ const criticalCount = allRisks.filter((r) => r.probability >= 80 && r.impactUsd >= 20000).length;
115
+ const gaugeSvg = buildRiskGaugeSvg(avgScore);
116
+ return `<section class="planu-card section-card score-card">
117
+ <h2>Risk Score</h2>
118
+ <div class="score-layout">
119
+ <div class="gauge-wrap">${gaugeSvg}</div>
120
+ <div class="score-stats">
121
+ <div class="stat-item">
122
+ <span class="stat-num">${allRisks.length}</span>
123
+ <span class="stat-label">Total Risks</span>
124
+ </div>
125
+ <div class="stat-item stat-warn">
126
+ <span class="stat-num">${highCount}</span>
127
+ <span class="stat-label">High Priority</span>
128
+ </div>
129
+ <div class="stat-item stat-danger">
130
+ <span class="stat-num">${criticalCount}</span>
131
+ <span class="stat-label">Critical</span>
132
+ </div>
133
+ <div class="stat-item">
134
+ <span class="stat-num">${registers.length}</span>
135
+ <span class="stat-label">Specs with Risks</span>
136
+ </div>
137
+ </div>
138
+ </div>
139
+ </section>`;
140
+ }
141
+ function buildPageCSS() {
142
+ return `
143
+ body { font-family: system-ui, sans-serif; margin: 0; padding: 0 16px 48px; background: var(--planu-bg); color: var(--planu-text); }
144
+ .page-title { font-size: 1.6rem; font-weight: 700; margin: 0 0 6px; }
145
+ .page-subtitle { color: #6b7280; margin: 0 0 24px; font-size: 0.95rem; }
146
+ .section-card { margin-bottom: 24px; }
147
+ h2 { font-size: 1.1rem; font-weight: 600; margin: 0 0 16px; color: var(--planu-primary); }
148
+ .matrix-wrap { overflow-x: auto; }
149
+ .matrix-wrap svg { max-width: 100%; height: auto; }
150
+ .table-wrap { overflow-x: auto; }
151
+ .risk-table { width: 100%; border-collapse: collapse; font-size: 0.82rem; }
152
+ .risk-table th { background: #f3f4f6; text-align: left; padding: 8px 10px; font-weight: 600; white-space: nowrap; border-bottom: 2px solid var(--planu-border); }
153
+ .risk-table td { padding: 8px 10px; border-bottom: 1px solid var(--planu-border); vertical-align: top; }
154
+ .risk-table tr:hover td { background: #f9fafb; }
155
+ .risk-table .num { text-align: right; white-space: nowrap; }
156
+ .score-layout { display: flex; align-items: flex-start; gap: 24px; flex-wrap: wrap; }
157
+ .gauge-wrap { width: 200px; flex-shrink: 0; }
158
+ .gauge-wrap svg { width: 100%; height: auto; }
159
+ .score-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; flex: 1; }
160
+ .stat-item { text-align: center; padding: 12px; background: #f9fafb; border-radius: 8px; }
161
+ .stat-num { display: block; font-size: 1.8rem; font-weight: 700; color: var(--planu-primary); }
162
+ .stat-warn .stat-num { color: var(--planu-warning); }
163
+ .stat-danger .stat-num { color: var(--planu-danger); }
164
+ .stat-label { font-size: 0.78rem; color: #6b7280; }
165
+ .coverage-grid { display: flex; gap: 24px; flex-wrap: wrap; }
166
+ .coverage-item { text-align: center; min-width: 100px; }
167
+ .coverage-pct { display: block; font-size: 2rem; font-weight: 700; color: var(--planu-primary); }
168
+ .coverage-green { color: var(--planu-success); }
169
+ .coverage-red { color: var(--planu-danger); }
170
+ .coverage-label { font-size: 0.82rem; color: #6b7280; }
171
+ .coverage-warning { margin-top: 12px; padding: 10px 14px; background: #fef3c7; border-radius: 8px; font-size: 0.88rem; color: #92400e; }
172
+ .empty-state { text-align: center; padding: 48px 24px; color: #6b7280; }
173
+ .empty-state h3 { font-size: 1.2rem; margin-bottom: 8px; }
174
+ .empty-message { color: #9ca3af; font-style: italic; }`;
175
+ }
176
+ /**
177
+ * Generate the risks.html portal page.
178
+ */
179
+ export function generateRiskPage(risks, specs) {
180
+ const specMap = new Map(specs.map((s) => [s.id, s.title]));
181
+ const allRisks = risks.flatMap((r) => r.risks);
182
+ const navbar = buildNavbar('risks', '');
183
+ const themeCSS = getPortalThemeCSS();
184
+ const pageCSS = buildPageCSS();
185
+ const matrixSvg = buildRiskMatrixSvg(allRisks);
186
+ const matrixSection = allRisks.length > 0
187
+ ? `<section class="planu-card section-card">
188
+ <h2>Risk Matrix</h2>
189
+ <div class="matrix-wrap">${matrixSvg}</div>
190
+ </section>`
191
+ : '';
192
+ const bodyContent = allRisks.length === 0
193
+ ? `<div class="empty-state"><h3>No risk data available</h3><p>Use <code>estimate</code> with risk analysis to populate this page.</p></div>`
194
+ : `${buildScoreCard(risks, allRisks)}
195
+ ${matrixSection}
196
+ <section class="planu-card section-card">
197
+ <h2>Risk Registry</h2>
198
+ ${buildRiskTable(allRisks, specMap)}
199
+ </section>
200
+ ${buildCoverageSection(allRisks)}`;
201
+ return `<!DOCTYPE html>
202
+ <html lang="en">
203
+ <head>
204
+ <meta charset="UTF-8">
205
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
206
+ <title>Risks — Planu Portal</title>
207
+ <style>${themeCSS}${pageCSS}</style>
208
+ </head>
209
+ <body>
210
+ ${navbar}
211
+ <main>
212
+ <h1 class="page-title">⚠️ Risk Register</h1>
213
+ <p class="page-subtitle">Risk matrix, registry, and mitigation coverage across all specs.</p>
214
+ ${bodyContent}
215
+ </main>
216
+ </body>
217
+ </html>`;
218
+ }
219
+ //# sourceMappingURL=risk-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"risk-page.js","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/risk-page.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { Spec } from '../../../types/index.js';
2
+ /**
3
+ * Generate the full roadmap.html page for the Planu portal.
4
+ * Returns an HTML string ready to write to disk.
5
+ */
6
+ export declare function generateRoadmapPage(specs: Spec[], ganttStartDate?: string): string;
7
+ //# sourceMappingURL=roadmap-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roadmap-page.d.ts","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/roadmap-page.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAKpD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAqClF"}