@hanzlaa/rcode 2.2.0 → 2.3.2

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 (143) hide show
  1. package/CLAUDE.md +15 -0
  2. package/CONTRIBUTING.md +138 -0
  3. package/README.md +74 -15
  4. package/cli/install.js +312 -80
  5. package/cli/postinstall.js +4 -4
  6. package/cli/uninstall.js +8 -0
  7. package/dist/rcode.js +19777 -0
  8. package/package.json +18 -5
  9. package/rihal/DOCS-AUDIT.md +14 -0
  10. package/rihal/agents/rihal-code-reviewer.md +1 -1
  11. package/rihal/agents/rihal-codebase-mapper.md +1 -1
  12. package/rihal/agents/rihal-docs-auditor.md +1 -1
  13. package/rihal/agents/rihal-edge-case-hunter.md +1 -1
  14. package/rihal/agents/rihal-executor.md +2 -1
  15. package/rihal/agents/rihal-hussain-pm.md +1 -0
  16. package/rihal/agents/rihal-nyquist-auditor.md +1 -1
  17. package/rihal/agents/rihal-phase-researcher.md +2 -2
  18. package/rihal/agents/rihal-planner.md +3 -2
  19. package/rihal/agents/rihal-roadmapper.md +1 -0
  20. package/rihal/agents/rihal-security-adversary.md +1 -1
  21. package/rihal/agents/rihal-security-auditor.md +1 -1
  22. package/rihal/agents/rihal-sprint-checker.md +1 -1
  23. package/rihal/agents/rihal-verifier.md +1 -1
  24. package/rihal/bin/lib/roadmap.cjs +2 -3
  25. package/rihal/bin/rihal-tools.cjs +11 -31
  26. package/rihal/brain/best-practices/no-theoretical-suggestions.md +56 -0
  27. package/rihal/commands/add-phase.md +2 -2
  28. package/rihal/commands/audit.md +8 -0
  29. package/rihal/commands/checkpoint-preview.md +13 -0
  30. package/rihal/commands/cleanup.md +2 -2
  31. package/rihal/commands/config.md +4 -4
  32. package/rihal/commands/pr-branch.md +2 -2
  33. package/rihal/commands/prfaq.md +15 -0
  34. package/rihal/commands/remove-phase.md +2 -2
  35. package/rihal/commands/research-phase.md +2 -2
  36. package/rihal/commands/settings.md +2 -2
  37. package/rihal/commands/ship.md +15 -3
  38. package/rihal/commands/validate-phase.md +1 -1
  39. package/rihal/commands/verify-phase.md +2 -2
  40. package/rihal/references/agent-contracts.md +12 -0
  41. package/rihal/references/karpathy-guidelines-full.md +79 -0
  42. package/rihal/references/karpathy-guidelines.md +8 -76
  43. package/rihal/references/model-profile-resolution.md +8 -0
  44. package/rihal/references/phase-argument-parsing.md +11 -0
  45. package/rihal/references/revision-loop.md +11 -0
  46. package/rihal/references/universal-anti-patterns.md +15 -0
  47. package/rihal/skills/actions/1-analysis/research/rihal-domain-research/SKILL.md +11 -0
  48. package/rihal/skills/actions/1-analysis/research/rihal-market-research/SKILL.md +11 -0
  49. package/rihal/skills/actions/1-analysis/research/rihal-technical-research/SKILL.md +13 -0
  50. package/rihal/skills/actions/1-analysis/rihal-document-project/SKILL.md +11 -0
  51. package/rihal/skills/actions/1-analysis/rihal-prfaq/SKILL.md +12 -0
  52. package/rihal/skills/actions/1-analysis/rihal-product-brief/SKILL.md +7 -0
  53. package/rihal/skills/actions/2-plan/rihal-create-epics-and-stories/SKILL.md +15 -1
  54. package/rihal/skills/actions/2-plan/rihal-create-milestone/SKILL.md +21 -1
  55. package/rihal/skills/actions/2-plan/rihal-create-milestone/steps/step-10-complete.md +1 -1
  56. package/rihal/skills/actions/2-plan/rihal-create-prd/SKILL.md +26 -0
  57. package/rihal/skills/actions/2-plan/rihal-create-story/SKILL.md +16 -2
  58. package/rihal/skills/actions/2-plan/rihal-create-ux-design/SKILL.md +12 -0
  59. package/rihal/skills/actions/2-plan/rihal-edit-prd/SKILL.md +11 -0
  60. package/rihal/skills/actions/2-plan/rihal-frontend-design/SKILL.md +13 -0
  61. package/rihal/skills/actions/2-plan/rihal-validate-prd/SKILL.md +12 -0
  62. package/rihal/skills/actions/3-solutioning/rihal-check-implementation-readiness/SKILL.md +12 -0
  63. package/rihal/skills/actions/3-solutioning/rihal-create-architecture/SKILL.md +14 -0
  64. package/rihal/skills/actions/3-solutioning/rihal-generate-project-context/SKILL.md +12 -0
  65. package/rihal/skills/actions/4-implementation/rihal-checkpoint-preview/SKILL.md +16 -0
  66. package/rihal/skills/actions/4-implementation/rihal-code-review/SKILL.md +12 -0
  67. package/rihal/skills/actions/4-implementation/rihal-correct-course/SKILL.md +13 -0
  68. package/rihal/skills/actions/4-implementation/rihal-dev-story/SKILL.md +12 -0
  69. package/rihal/skills/actions/4-implementation/rihal-qa-generate-e2e-tests/SKILL.md +12 -0
  70. package/rihal/skills/actions/4-implementation/rihal-retrospective/SKILL.md +11 -0
  71. package/rihal/skills/actions/4-implementation/rihal-scaffold-project/SKILL.md +10 -0
  72. package/rihal/skills/actions/4-implementation/rihal-sprint-planning/SKILL.md +14 -1
  73. package/rihal/skills/actions/4-implementation/rihal-sprint-status/SKILL.md +10 -0
  74. package/rihal/skills/agents/ahmed-hassani-director/SKILL.md +13 -1
  75. package/rihal/skills/agents/fatima-qa/SKILL.md +14 -1
  76. package/rihal/skills/agents/haitham-frontend/SKILL.md +15 -1
  77. package/rihal/skills/agents/hanzla-engineer/SKILL.md +14 -1
  78. package/rihal/skills/agents/hussain-pm/SKILL.md +22 -1
  79. package/rihal/skills/agents/hussain-sm/SKILL.md +22 -1
  80. package/rihal/skills/agents/layla-designer/SKILL.md +15 -1
  81. package/rihal/skills/agents/majlis-council/SKILL.md +14 -1
  82. package/rihal/skills/agents/mariam-marketing/SKILL.md +15 -1
  83. package/rihal/skills/agents/nasser-eng-manager/SKILL.md +14 -1
  84. package/rihal/skills/agents/noor-writer/SKILL.md +14 -1
  85. package/rihal/skills/agents/raees-orchestrator/SKILL.md +13 -1
  86. package/rihal/skills/agents/sadiq-analyst/SKILL.md +15 -1
  87. package/rihal/skills/agents/waleed-architect/SKILL.md +16 -1
  88. package/rihal/skills/agents/yousef-backend/SKILL.md +16 -1
  89. package/rihal/skills/agents/zahra-branding/SKILL.md +15 -1
  90. package/rihal/skills/agents/zayd-ml/SKILL.md +17 -1
  91. package/rihal/skills/core/rihal-advanced-elicitation/SKILL.md +12 -0
  92. package/rihal/skills/core/rihal-brainstorming/SKILL.md +16 -0
  93. package/rihal/skills/core/rihal-clone-website/SKILL.md +21 -0
  94. package/rihal/skills/core/rihal-distillator/SKILL.md +8 -0
  95. package/rihal/skills/core/rihal-editorial-review-prose/SKILL.md +12 -0
  96. package/rihal/skills/core/rihal-editorial-review-structure/SKILL.md +18 -0
  97. package/rihal/skills/core/rihal-help/SKILL.md +12 -0
  98. package/rihal/skills/core/rihal-index-docs/SKILL.md +18 -0
  99. package/rihal/skills/core/rihal-init/SKILL.md +8 -0
  100. package/rihal/skills/core/rihal-party-mode/SKILL.md +14 -0
  101. package/rihal/skills/core/rihal-review-adversarial-general/SKILL.md +12 -0
  102. package/rihal/skills/core/rihal-review-edge-case-hunter/SKILL.md +18 -0
  103. package/rihal/skills/core/rihal-shard-doc/SKILL.md +18 -0
  104. package/rihal/team.yaml +205 -0
  105. package/rihal/templates/UAT.md +29 -0
  106. package/rihal/templates/milestone.md +2 -0
  107. package/rihal/templates/sprint.md +11 -28
  108. package/rihal/templates/summary.md +30 -0
  109. package/rihal/templates/verification-report.md +28 -0
  110. package/rihal/workflows/audit-milestone.md +34 -2
  111. package/rihal/workflows/audit.md +172 -0
  112. package/rihal/workflows/autonomous.md +67 -0
  113. package/rihal/workflows/checkpoint-preview.md +7 -0
  114. package/rihal/workflows/council.md +3 -1
  115. package/rihal/workflows/debug.md +8 -1
  116. package/rihal/workflows/diagnose-issues.md +34 -0
  117. package/rihal/workflows/do.md +47 -3
  118. package/rihal/workflows/document-project.md +1 -1
  119. package/rihal/workflows/execute-sprint.md +11 -4
  120. package/rihal/workflows/execute.md +9 -3
  121. package/rihal/workflows/help.md +1 -1
  122. package/rihal/workflows/karpathy-audit.md +7 -14
  123. package/rihal/workflows/pause-work.md +7 -1
  124. package/rihal/workflows/prfaq.md +7 -0
  125. package/rihal/workflows/profile-user.md +2 -2
  126. package/rihal/workflows/settings.md +116 -117
  127. package/rihal/workflows/ship.md +31 -1
  128. package/rihal/workflows/sprint-planning.md +39 -8
  129. package/rihal/workflows/status.md +5 -0
  130. package/rihal/workflows/ui-phase.md +3 -3
  131. package/rihal/workflows/update.md +80 -22
  132. package/rihal/workflows/validate-phase.md +7 -1
  133. package/server/dashboard.js +34 -575
  134. package/server/lib/api.js +123 -0
  135. package/server/lib/html/client.js +969 -0
  136. package/server/lib/html/css.js +416 -0
  137. package/server/lib/html/shell.js +230 -0
  138. package/server/lib/scanner.js +142 -0
  139. package/rihal/agents/rihal-ui-designer.md +0 -6
  140. package/rihal/skills/core/rihal-advanced-elicitation/rihal-advanced-elicitation/SKILL.md +0 -148
  141. package/rihal/skills/core/rihal-advanced-elicitation/rihal-advanced-elicitation/methods.csv +0 -51
  142. package/rihal/skills/core/rihal-shard-doc/rihal-shard-doc/SKILL.md +0 -122
  143. package/rihal/workflows/config.md +0 -105
@@ -0,0 +1,416 @@
1
+ /**
2
+ * Dashboard CSS — all styles in one module.
3
+ * Supports dark mode (default) and light mode via data-theme="light".
4
+ */
5
+ function renderCss() {
6
+ return `<style>
7
+ :root {
8
+ --rihal-blue: #1e3a8a;
9
+ --rihal-gold: #f59e0b;
10
+ --bg: #0a0a0b;
11
+ --bg-card: #111113;
12
+ --bg-hover: #1a1a1e;
13
+ --border: #1e1e24;
14
+ --text-primary: #f0f0f2;
15
+ --text-secondary: #a0a0aa;
16
+ --text-muted: #606068;
17
+ --accent-blue: #3b82f6;
18
+ --accent-green: #10b981;
19
+ --accent-amber: #f59e0b;
20
+ --accent-red: #ef4444;
21
+ --text-xs: 11px;
22
+ --text-sm: 13px;
23
+ --text-base: 15px;
24
+ --text-lg: 18px;
25
+ --text-xl: 24px;
26
+ --space-1: 4px;
27
+ --space-2: 8px;
28
+ --space-3: 12px;
29
+ --space-4: 16px;
30
+ --space-5: 20px;
31
+ --space-6: 24px;
32
+ --space-7: 28px;
33
+ --space-8: 32px;
34
+ --radius-sm: 4px;
35
+ --radius-md: 8px;
36
+ --radius-lg: 12px;
37
+ --shadow-card: 0 1px 3px rgba(0,0,0,0.4), 0 0 0 1px var(--border);
38
+ }
39
+ /* #313 Light mode */
40
+ [data-theme="light"] {
41
+ --bg: #f8f9fa;
42
+ --bg-card: #ffffff;
43
+ --bg-hover: #f0f1f3;
44
+ --border: #e2e4e8;
45
+ --text-primary: #1a1a1a;
46
+ --text-secondary: #555;
47
+ --text-muted: #888;
48
+ --shadow-card: 0 1px 3px rgba(0,0,0,0.08), 0 0 0 1px var(--border);
49
+ }
50
+ * { box-sizing: border-box; margin: 0; padding: 0; }
51
+ body {
52
+ font-family: 'Inter', -apple-system, 'Segoe UI', sans-serif;
53
+ background: var(--bg);
54
+ color: var(--text-primary);
55
+ line-height: 1.6;
56
+ }
57
+ .app-shell { display: flex; height: 100vh; overflow: hidden; }
58
+ /* Sidebar */
59
+ .sidebar {
60
+ width: 240px; min-width: 240px;
61
+ background: var(--bg-card);
62
+ border-right: 1px solid var(--border);
63
+ display: flex; flex-direction: column;
64
+ overflow-y: auto; padding: var(--space-4) 0;
65
+ }
66
+ .sidebar-project {
67
+ padding: var(--space-3) var(--space-4);
68
+ font-size: var(--text-sm); font-weight: 600;
69
+ color: var(--text-primary);
70
+ border-bottom: 1px solid var(--border);
71
+ margin-bottom: var(--space-3);
72
+ }
73
+ .sidebar-project .project-label {
74
+ font-size: var(--text-xs); color: var(--text-muted);
75
+ text-transform: uppercase; letter-spacing: 0.07em;
76
+ margin-bottom: var(--space-1);
77
+ }
78
+ .nav-link {
79
+ display: flex; align-items: center; gap: var(--space-2);
80
+ padding: var(--space-2) var(--space-4);
81
+ font-size: var(--text-sm); color: var(--text-secondary);
82
+ cursor: pointer; border-radius: 0; border: none; background: none;
83
+ width: 100%; text-align: left;
84
+ transition: background 0.15s, color 0.15s; user-select: none;
85
+ }
86
+ .nav-link:hover { background: var(--bg-hover); color: var(--text-primary); }
87
+ .nav-link.active { background: var(--bg-hover); color: var(--text-primary); font-weight: 600; }
88
+ .nav-section {
89
+ padding: var(--space-3) var(--space-4) var(--space-1);
90
+ font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em;
91
+ color: var(--text-muted); font-weight: 600;
92
+ }
93
+ /* Content */
94
+ .content-area { flex: 1; overflow-y: auto; background: var(--bg); display: flex; flex-direction: column; }
95
+ .view { display: none; padding: var(--space-8); }
96
+ .view.active { display: block; }
97
+ /* Header */
98
+ header {
99
+ background: var(--bg-card); border-bottom: 1px solid var(--border);
100
+ padding: var(--space-4) var(--space-8);
101
+ display: flex; justify-content: space-between; align-items: center; flex-shrink: 0;
102
+ }
103
+ .brand { display: flex; align-items: center; gap: var(--space-4); }
104
+ .brand .icon { font-size: 40px; }
105
+ .brand h1 { font-size: var(--text-xl); font-weight: 700; }
106
+ .brand .arabic { color: var(--rihal-gold); font-size: var(--text-lg); margin-top: 2px; }
107
+ .header-meta { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); color: var(--text-secondary); }
108
+ .header-actions { display: flex; align-items: center; gap: var(--space-2); }
109
+ .live { display: inline-block; width: 8px; height: 8px; background: var(--accent-green); border-radius: 50%; animation: pulse 2s infinite; }
110
+ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
111
+ .header-btn {
112
+ background: var(--bg-hover); border: 1px solid var(--border); color: var(--text-primary);
113
+ padding: var(--space-1) var(--space-3); border-radius: var(--radius-sm);
114
+ cursor: pointer; font-size: var(--text-sm); transition: background 0.15s; font-family: inherit;
115
+ }
116
+ .header-btn:hover { background: var(--border); }
117
+ /* Blocker banner */
118
+ #blocker-banner {
119
+ background: rgba(239,68,68,0.12); border-bottom: 1px solid rgba(239,68,68,0.4);
120
+ padding: var(--space-3) var(--space-8); display: flex;
121
+ align-items: center; justify-content: space-between; gap: var(--space-4);
122
+ color: var(--accent-red); font-size: var(--text-sm);
123
+ }
124
+ #blocker-banner .banner-title { font-weight: 600; }
125
+ #blocker-banner .banner-list { flex: 1; color: var(--text-secondary); font-size: var(--text-xs); margin-left: var(--space-3); }
126
+ #blocker-banner .banner-dismiss {
127
+ background: none; border: 1px solid rgba(239,68,68,0.4); color: var(--accent-red);
128
+ padding: 2px 10px; border-radius: var(--radius-sm); cursor: pointer; font-size: var(--text-xs); font-family: inherit;
129
+ }
130
+ #blocker-banner .banner-dismiss:hover { background: rgba(239,68,68,0.2); }
131
+ /* #322 Warning banner for parse errors */
132
+ #parse-warning {
133
+ background: rgba(245,158,11,0.12); border-bottom: 1px solid rgba(245,158,11,0.4);
134
+ padding: var(--space-3) var(--space-8); display: flex;
135
+ align-items: center; gap: var(--space-4);
136
+ color: var(--accent-amber); font-size: var(--text-sm);
137
+ }
138
+ /* Stats grid */
139
+ .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--space-4); margin-bottom: var(--space-8); }
140
+ .stat {
141
+ background: var(--bg-card); border: 1px solid var(--border); border-left: 4px solid var(--rihal-gold);
142
+ padding: var(--space-5) var(--space-6); border-radius: var(--radius-md);
143
+ }
144
+ .stat .label { color: var(--text-muted); font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: var(--space-2); }
145
+ .stat .value { font-size: 28px; font-weight: 700; }
146
+ .stat .sub { color: var(--text-muted); font-size: var(--text-sm); margin-top: var(--space-1); }
147
+ /* Sections */
148
+ section { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-lg); margin-bottom: var(--space-6); overflow: hidden; }
149
+ section > h2 {
150
+ background: rgba(245,158,11,0.08); padding: var(--space-4) var(--space-6);
151
+ font-size: var(--text-sm); text-transform: uppercase; letter-spacing: 0.1em;
152
+ color: var(--rihal-gold); border-bottom: 1px solid var(--border);
153
+ display: flex; align-items: center; gap: 10px;
154
+ }
155
+ section .body { padding: var(--space-6); }
156
+ /* Agent cards */
157
+ .agents { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: var(--space-3); }
158
+ .agent-card {
159
+ background: rgba(59,130,246,0.05); border: 1px solid var(--border);
160
+ border-radius: var(--radius-md); padding: var(--space-4); transition: transform 0.2s;
161
+ }
162
+ .agent-card:hover { transform: translateY(-2px); border-color: var(--rihal-gold); }
163
+ .agent-card .name { font-weight: 600; font-size: var(--text-base); margin-bottom: var(--space-1); }
164
+ .agent-card .arabic { color: var(--rihal-gold); font-size: 14px; }
165
+ .agent-card .role { color: var(--text-muted); font-size: var(--text-xs); margin-top: var(--space-2); }
166
+ .agent-card.active { background: rgba(16,185,129,0.1); border-color: var(--accent-green); }
167
+ .real-badge {
168
+ display: inline-block; background: rgba(16,185,129,0.2); color: var(--accent-green);
169
+ padding: 1px 6px; border-radius: 8px; font-size: 9px;
170
+ text-transform: uppercase; letter-spacing: 0.05em; vertical-align: middle; margin-left: 4px;
171
+ }
172
+ /* #304 Agent type badge */
173
+ .type-badge {
174
+ display: inline-block; background: rgba(59,130,246,0.15); color: var(--accent-blue);
175
+ padding: 1px 6px; border-radius: 8px; font-size: 9px;
176
+ text-transform: uppercase; letter-spacing: 0.05em; vertical-align: middle; margin-left: 4px;
177
+ }
178
+ /* Items */
179
+ .phase-list, .decision-list, .progress-list { display: flex; flex-direction: column; gap: var(--space-3); }
180
+ .item {
181
+ background: rgba(255,255,255,0.02); border: 1px solid var(--border);
182
+ border-left: 3px solid var(--accent-blue); padding: var(--space-4) var(--space-5);
183
+ border-radius: var(--radius-sm);
184
+ }
185
+ .item .item-title { font-weight: 600; margin-bottom: var(--space-2); }
186
+ .item .item-meta { color: var(--text-muted); font-size: var(--text-xs); margin-bottom: var(--space-2); }
187
+ .item-clickable { cursor: pointer; }
188
+ .item-clickable:hover { background: var(--bg-hover); border-color: var(--accent-blue); }
189
+ .empty { color: var(--text-muted); text-align: center; padding: var(--space-8); font-style: italic; }
190
+ /* #316 Actionable empty states */
191
+ .empty-action {
192
+ display: inline-block; margin-top: var(--space-3);
193
+ background: var(--bg-hover); border: 1px solid var(--border);
194
+ padding: var(--space-2) var(--space-4); border-radius: var(--radius-md);
195
+ color: var(--accent-blue); font-size: var(--text-sm); font-style: normal;
196
+ }
197
+ .tag {
198
+ display: inline-block; background: rgba(245,158,11,0.15); color: var(--rihal-gold);
199
+ padding: 2px 10px; border-radius: 12px; font-size: 11px;
200
+ text-transform: uppercase; letter-spacing: 0.05em; margin-right: 6px;
201
+ }
202
+ .status-chip {
203
+ display: inline-flex; align-items: center; gap: 4px;
204
+ padding: 2px 8px; border-radius: 99px; font-size: var(--text-xs);
205
+ font-weight: 500; letter-spacing: 0.04em; text-transform: lowercase;
206
+ }
207
+ .status-chip.complete { background: rgba(16,185,129,0.15); color: var(--accent-green); }
208
+ .status-chip.active,
209
+ .status-chip.in_progress { background: rgba(59,130,246,0.15); color: var(--accent-blue); }
210
+ .status-chip.blocked { background: rgba(239,68,68,0.15); color: var(--accent-red); }
211
+ /* Fix #314: 'planned' gets its own class */
212
+ .status-chip.planned { background: rgba(96,96,104,0.2); color: var(--text-muted); }
213
+ .status-chip.todo { background: rgba(96,96,104,0.2); color: var(--text-muted); }
214
+ .status-chip.other { background: rgba(96,96,104,0.2); color: var(--text-muted); }
215
+ /* Progress bar */
216
+ .progress-bar {
217
+ height: 6px; background: var(--border); border-radius: 3px; overflow: hidden; width: 100%;
218
+ }
219
+ .progress-bar-fill {
220
+ height: 100%; border-radius: 3px; transition: width 0.3s ease;
221
+ background: var(--accent-green);
222
+ }
223
+ /* File tree */
224
+ .file-tree { font-size: var(--text-xs); }
225
+ .file-tree-group { margin-bottom: var(--space-3); }
226
+ .file-tree-group summary {
227
+ color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.07em;
228
+ font-size: 10px; padding: var(--space-1) var(--space-2); cursor: pointer; list-style: none;
229
+ }
230
+ .file-tree-item {
231
+ display: block; padding: 3px var(--space-3); color: var(--text-secondary);
232
+ cursor: pointer; border-radius: var(--radius-sm); overflow: hidden;
233
+ text-overflow: ellipsis; white-space: nowrap; font-family: 'SF Mono', Monaco, Consolas, monospace;
234
+ }
235
+ .file-tree-item:hover { color: var(--text-primary); background: var(--bg-hover); }
236
+ .file-tree-item.selected { color: var(--accent-blue); background: rgba(59,130,246,0.1); }
237
+ /* #300 File modification date */
238
+ .file-tree-date { color: var(--text-muted); font-size: 9px; margin-left: 4px; }
239
+ /* Markdown render */
240
+ .md-render {
241
+ background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-lg);
242
+ padding: var(--space-8); max-width: 860px; line-height: 1.7;
243
+ }
244
+ .md-render h1, .md-render h2, .md-render h3 { margin: var(--space-6) 0 var(--space-3); }
245
+ .md-render code { background: var(--bg-hover); padding: 2px 6px; border-radius: var(--radius-sm); font-size: var(--text-sm); }
246
+ .md-render pre { background: var(--bg-hover); padding: var(--space-4); border-radius: var(--radius-md); overflow-x: auto; }
247
+ .md-render a { color: var(--accent-blue); }
248
+ .md-render ul, .md-render ol { margin-left: var(--space-6); margin-bottom: var(--space-3); }
249
+ /* #302 Syntax highlighting for fenced code blocks */
250
+ .md-render pre code {
251
+ background: none; padding: 0; display: block;
252
+ color: var(--text-secondary); font-size: var(--text-sm);
253
+ font-family: "SF Mono", Monaco, Consolas, monospace;
254
+ }
255
+ .md-render pre code .kw { color: #c678dd; }
256
+ .md-render pre code .str { color: #98c379; }
257
+ .md-render pre code .cm { color: #5c6370; font-style: italic; }
258
+ /* Filter bar */
259
+ .filter-bar { margin-bottom: var(--space-6); display: flex; gap: var(--space-3); align-items: center; flex-wrap: wrap; }
260
+ .filter-input {
261
+ width: 100%; max-width: 360px; background: var(--bg-card);
262
+ border: 1px solid var(--border); border-radius: var(--radius-md);
263
+ color: var(--text-primary); font-size: var(--text-sm);
264
+ padding: var(--space-2) var(--space-3); outline: none; font-family: inherit;
265
+ }
266
+ .filter-input:focus { border-color: var(--accent-blue); }
267
+ .filter-input::placeholder { color: var(--text-muted); }
268
+ /* #296 Filter select */
269
+ .filter-select {
270
+ background: var(--bg-card); border: 1px solid var(--border);
271
+ border-radius: var(--radius-md); color: var(--text-primary);
272
+ font-size: var(--text-sm); padding: var(--space-2) var(--space-3);
273
+ font-family: inherit; outline: none;
274
+ }
275
+ .view-title { font-size: var(--text-lg); font-weight: 600; margin-bottom: var(--space-6); }
276
+ /* Breadcrumb */
277
+ .breadcrumb { margin-bottom: var(--space-5); }
278
+ .back-btn {
279
+ background: var(--bg-card); border: 1px solid var(--border); color: var(--text-secondary);
280
+ padding: var(--space-2) var(--space-4); border-radius: var(--radius-md);
281
+ cursor: pointer; font-size: var(--text-sm); font-family: inherit; transition: all 0.15s;
282
+ }
283
+ .back-btn:hover { color: var(--text-primary); border-color: var(--accent-blue); }
284
+ /* Entity detail */
285
+ .entity-header { margin-bottom: var(--space-6); }
286
+ .entity-title { font-size: var(--text-xl); font-weight: 700; margin-bottom: var(--space-4); }
287
+ .attr-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: var(--space-3); }
288
+ .attr-item {
289
+ background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-md);
290
+ padding: var(--space-3) var(--space-4); display: flex; flex-direction: column; gap: 4px;
291
+ }
292
+ .attr-label { font-size: var(--text-xs); color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em; }
293
+ .attr-value { font-size: var(--text-sm); font-weight: 500; }
294
+ /* Tree */
295
+ .tree-container { padding: 0; }
296
+ .tree-ms { border-left: none !important; margin-left: 0 !important; }
297
+ .tree-node { border-left: 1px solid var(--border); margin-left: var(--space-4); }
298
+ .tree-row {
299
+ display: flex; align-items: center; gap: var(--space-2);
300
+ padding: var(--space-2) var(--space-3); cursor: pointer;
301
+ border-radius: var(--radius-sm); transition: background 0.1s; user-select: none;
302
+ }
303
+ .tree-row:hover { background: var(--bg-hover); }
304
+ .task-leaf > .tree-row { cursor: default; }
305
+ .tree-chevron { color: var(--text-muted); font-size: 10px; width: 14px; flex-shrink: 0; }
306
+ .tree-icon { flex-shrink: 0; }
307
+ .tree-label { flex: 1; font-size: var(--text-sm); }
308
+ .tree-badge { color: var(--text-muted); font-size: var(--text-xs); flex-shrink: 0; }
309
+ .tree-ms > .tree-row .tree-label { font-weight: 700; font-size: var(--text-base); color: var(--rihal-gold); }
310
+ .tree-children { padding-left: var(--space-3); }
311
+ /* #311 Tree animation */
312
+ .tree-children { overflow: hidden; transition: max-height 0.25s ease; }
313
+ /* #315 Loading skeleton */
314
+ .skeleton {
315
+ background: linear-gradient(90deg, var(--bg-hover) 25%, var(--border) 50%, var(--bg-hover) 75%);
316
+ background-size: 200% 100%;
317
+ animation: shimmer 1.5s infinite;
318
+ border-radius: var(--radius-md); height: 80px; margin-bottom: var(--space-3);
319
+ }
320
+ @keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
321
+ /* #298 File path header */
322
+ .file-path-header {
323
+ font-family: 'SF Mono', Monaco, Consolas, monospace;
324
+ font-size: var(--text-sm); color: var(--text-muted);
325
+ padding: var(--space-3) 0; margin-bottom: var(--space-4);
326
+ border-bottom: 1px solid var(--border);
327
+ display: flex; align-items: center; gap: var(--space-3);
328
+ }
329
+ .file-path-header .copy-btn {
330
+ background: var(--bg-hover); border: 1px solid var(--border);
331
+ color: var(--text-secondary); padding: 2px 8px; border-radius: var(--radius-sm);
332
+ cursor: pointer; font-size: var(--text-xs); font-family: inherit;
333
+ }
334
+ .file-path-header .copy-btn:hover { color: var(--text-primary); }
335
+ /* Footer */
336
+ footer {
337
+ text-align: center; padding: var(--space-8); color: var(--text-muted); font-size: var(--text-sm);
338
+ border-top: 1px solid var(--border); margin-top: 48px;
339
+ }
340
+ footer .arabic { color: var(--rihal-gold); font-size: 16px; margin-bottom: var(--space-2); }
341
+ code {
342
+ background: rgba(255,255,255,0.05); padding: 2px 6px;
343
+ border-radius: var(--radius-sm); font-size: var(--text-sm);
344
+ font-family: "SF Mono", Monaco, Consolas, monospace;
345
+ }
346
+ h1, h2, h3 { line-height: 1.3; }
347
+ p { margin-bottom: 10px; }
348
+ ul { margin-left: 20px; margin-bottom: 10px; }
349
+ /* Velocity bar */
350
+ .velocity-bar { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-2); }
351
+ .velocity-bar-label { font-size: var(--text-xs); color: var(--text-muted); width: 60px; text-align: right; }
352
+ .velocity-bar-track { flex: 1; height: 14px; background: var(--border); border-radius: 7px; overflow: hidden; position: relative; }
353
+ .velocity-bar-fill { height: 100%; border-radius: 7px; background: var(--accent-blue); }
354
+ .velocity-bar-val { font-size: var(--text-xs); color: var(--text-secondary); width: 50px; }
355
+ /* #280 Completion ring */
356
+ .completion-ring { position: relative; width: 64px; height: 64px; }
357
+ .completion-ring svg { transform: rotate(-90deg); }
358
+ .completion-ring .ring-text {
359
+ position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);
360
+ font-size: var(--text-sm); font-weight: 700;
361
+ }
362
+ /* #323 Responsive */
363
+ @media (max-width: 768px) {
364
+ .sidebar { display: none; }
365
+ .content-area { width: 100%; }
366
+ .view { padding: var(--space-4); }
367
+ header { padding: var(--space-3) var(--space-4); flex-wrap: wrap; gap: var(--space-2); }
368
+ .brand .icon { font-size: 28px; }
369
+ .brand h1 { font-size: var(--text-lg); }
370
+ .stats { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
371
+ .agents { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }
372
+ .attr-grid { grid-template-columns: 1fr 1fr; }
373
+ }
374
+ /* Command hints accordion */
375
+ .cmd-hints {
376
+ margin-top: var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-md);
377
+ background: var(--bg-card); overflow: hidden;
378
+ }
379
+ .cmd-hints summary {
380
+ padding: var(--space-2) var(--space-3); cursor: pointer; font-size: var(--text-sm);
381
+ color: var(--text-muted); font-weight: 500; list-style: none;
382
+ display: flex; align-items: center; gap: 6px; user-select: none;
383
+ }
384
+ .cmd-hints summary::-webkit-details-marker { display: none; }
385
+ .cmd-hints summary::before { content: '▶'; font-size: 10px; transition: transform 0.2s; }
386
+ .cmd-hints[open] summary::before { transform: rotate(90deg); }
387
+ .cmd-hints summary:hover { color: var(--text-primary); background: var(--bg-hover); }
388
+ .cmd-hints-list { padding: var(--space-2) 0; }
389
+ .cmd-hint-item {
390
+ display: flex; align-items: baseline; gap: var(--space-3); padding: var(--space-2) var(--space-3);
391
+ cursor: pointer; transition: background 0.15s;
392
+ }
393
+ .cmd-hint-item:hover { background: var(--bg-hover); }
394
+ .cmd-hint-item .cmd-text {
395
+ font-family: 'SF Mono', Monaco, Consolas, monospace; font-size: var(--text-xs);
396
+ color: var(--accent-blue); white-space: nowrap; font-weight: 500;
397
+ }
398
+ .cmd-hint-item .cmd-desc {
399
+ font-size: var(--text-xs); color: var(--text-muted); flex: 1;
400
+ }
401
+ .cmd-hint-item .cmd-copy {
402
+ font-size: 10px; color: var(--text-muted); opacity: 0; transition: opacity 0.15s; margin-left: auto;
403
+ }
404
+ .cmd-hint-item:hover .cmd-copy { opacity: 1; }
405
+ /* Toast notification (for copy feedback) */
406
+ .toast {
407
+ position: fixed; bottom: 20px; right: 20px; background: var(--accent-green);
408
+ color: #fff; padding: var(--space-2) var(--space-4); border-radius: var(--radius-md);
409
+ font-size: var(--text-sm); z-index: 1000; opacity: 0; transition: opacity 0.3s;
410
+ pointer-events: none;
411
+ }
412
+ .toast.show { opacity: 1; }
413
+ </style>`;
414
+ }
415
+
416
+ module.exports = { renderCss };
@@ -0,0 +1,230 @@
1
+ /**
2
+ * HTML shell — composes the full page from CSS, views, and client JS.
3
+ */
4
+ const { renderCss } = require('./css');
5
+ const { renderClientJs } = require('./client');
6
+
7
+ function esc(s) { return String(s || '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'); }
8
+
9
+ function renderHtml(state) {
10
+ const projectName = state.projectName || 'No project initialized';
11
+ const currentPhase = state.currentPhase || '—';
12
+ const currentSprint = state.currentSprint || null;
13
+ const phaseCount = (state.raw?.phases || []).length;
14
+ const decisionCount = (state.raw?.decisions || []).length;
15
+ const artifactCount = state.planningFiles.length;
16
+
17
+ const agents = [
18
+ { name: 'Sadiq Damani', arabic: 'صادق', role: 'Director of Strategy', real: true, type: 'leadership' },
19
+ { name: 'Waleed Al Harthi', arabic: 'وليد', role: 'CTO', real: true, type: 'leadership' },
20
+ { name: 'Ahmed Al Hassani', arabic: 'أحمد الحسني', role: 'Technology & Development Director', real: true, type: 'leadership' },
21
+ { name: 'Nasser', arabic: 'ناصر', role: 'Engineering Manager', real: true, type: 'leadership' },
22
+ { name: 'Hussain', arabic: 'حسين', role: 'PM + Scrum Master', type: 'product' },
23
+ { name: 'Layla', arabic: 'ليلى', role: 'Lead UX Designer', type: 'design' },
24
+ { name: 'Zahra', arabic: 'زهرة', role: 'Branding & Creative Director', type: 'design' },
25
+ { name: 'Omar', arabic: 'عمر', role: 'Full-Stack Engineer', type: 'engineering' },
26
+ { name: 'Haitham Al Khamiyasi', arabic: 'هيثم', role: 'Senior Frontend', real: true, type: 'engineering' },
27
+ { name: 'Yousef', arabic: 'يوسف', role: 'Senior Backend', type: 'engineering' },
28
+ { name: 'Zayd', arabic: 'زيد', role: 'ML Engineer', type: 'engineering' },
29
+ { name: 'Fatima', arabic: 'فاطمة', role: 'QA Lead', type: 'quality' },
30
+ { name: 'Khalid', arabic: 'خالد', role: 'DevOps', type: 'engineering' },
31
+ { name: 'Noor', arabic: 'نور', role: 'Scribe', type: 'support' },
32
+ { name: 'Mariam', arabic: 'مريم', role: 'Marketing Lead', type: 'product' },
33
+ { name: 'Raees', arabic: 'رئيس', role: 'Orchestration Director', type: 'system' },
34
+ { name: 'Majlis', arabic: 'مجلس', role: 'Consulting Council', type: 'system' },
35
+ { name: 'Diwan', arabic: 'ديوان', role: 'Dashboard Registry', type: 'system' },
36
+ ];
37
+
38
+ // #305: separate real vs AI agents
39
+ const realAgents = agents.filter(a => a.real);
40
+ const aiAgents = agents.filter(a => !a.real);
41
+
42
+ function agentCard(a) {
43
+ const filterText = (a.name + ' ' + a.role + ' ' + a.arabic + ' ' + a.type).toLowerCase();
44
+ // #303: link to SKILL.md
45
+ const skillName = a.name.split(' ')[0].toLowerCase();
46
+ return `<div class="agent-card" data-filter-text="${filterText}" onclick="viewAgentSkill('${skillName}')" style="cursor:pointer;">
47
+ <div class="name">${esc(a.name)}${a.real ? ' <span class="real-badge">real</span>' : ''} <span class="type-badge">${esc(a.type)}</span></div>
48
+ <div class="arabic">${a.arabic}</div>
49
+ <div class="role">${esc(a.role)}</div>
50
+ </div>`;
51
+ }
52
+
53
+ return `<!DOCTYPE html>
54
+ <html lang="en" dir="ltr">
55
+ <head>
56
+ <meta charset="UTF-8">
57
+ <meta name="viewport" content="width=device-width, initial-scale=1">
58
+ <title>Majlis — ${esc(projectName)}</title>
59
+ <link rel="preconnect" href="https://fonts.googleapis.com">
60
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
61
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"><\/script>
62
+ ${renderCss()}
63
+ </head>
64
+ <body>
65
+ <div class="app-shell">
66
+ <aside class="sidebar">
67
+ <div class="sidebar-project">
68
+ <div class="project-label">Project</div>
69
+ ${esc(projectName)}
70
+ </div>
71
+ <nav>
72
+ <div class="nav-section">Overview</div>
73
+ <button class="nav-link" data-view="overview">🏠 Overview</button>
74
+ <button class="nav-link" data-view="roadmap">🗺 Roadmap</button>
75
+ <div class="nav-section">Planning</div>
76
+ <button class="nav-link" data-view="milestones">🎯 Milestones</button>
77
+ <button class="nav-link" data-view="phases">📋 Phases</button>
78
+ <button class="nav-link" data-view="sprints">⚡ Sprints</button>
79
+ <button class="nav-link" data-view="tasks">✓ Tasks</button>
80
+ <div class="nav-section">Workspace</div>
81
+ <button class="nav-link" data-view="files">📄 Files</button>
82
+ <button class="nav-link" data-view="agents">🤝 Agents</button>
83
+ <button class="nav-link" data-view="decisions">⚖ Decisions</button>
84
+ </nav>
85
+ <div id="sidebar-file-tree" style="margin-top:var(--space-4);padding:0 var(--space-2);"></div>
86
+ </aside>
87
+ <div class="content-area" id="main-content">
88
+ <header>
89
+ <div class="brand">
90
+ <div class="icon">🕌</div>
91
+ <div>
92
+ <h1>Majlis — The Council</h1>
93
+ <div class="arabic">مجلس · ${esc(projectName)}</div>
94
+ </div>
95
+ </div>
96
+ <div class="header-actions">
97
+ <span class="live" id="live-dot"></span>
98
+ <span id="updated-ago" style="font-size:var(--text-sm);color:var(--text-secondary);">just now</span>
99
+ &nbsp;·&nbsp;
100
+ <button class="header-btn" id="refresh-btn" onclick="manualRefresh()">↺ Refresh</button>
101
+ <button class="header-btn" id="theme-btn" onclick="toggleTheme()" title="Toggle dark/light">☀️</button>
102
+ <button class="header-btn" onclick="copyUrl()" title="Copy URL">🔗</button>
103
+ <button class="header-btn" onclick="exportSnapshot()" title="Export snapshot">📥</button>
104
+ </div>
105
+ </header>
106
+
107
+ ${state.rawParseError ? `<div id="parse-warning">⚠️ <strong>state.json parse error:</strong> ${esc(state.rawParseError)} — Dashboard showing partial data.</div>` : ''}
108
+
109
+ ${state.blockers.length > 0 ? `
110
+ <div id="blocker-banner">
111
+ <span class="banner-title">🚧 ${state.blockers.length} Blocker${state.blockers.length > 1 ? 's' : ''}</span>
112
+ <span class="banner-list">${state.blockers.map(b => esc(typeof b === 'string' ? b : (b.title || ''))).join(' · ')}</span>
113
+ <button class="banner-dismiss" onclick="dismissBlockers()">Dismiss</button>
114
+ </div>` : ''}
115
+
116
+ <div id="view-overview" class="view active">
117
+ ${!state.exists ? `
118
+ <div class="empty" style="padding:80px;background:var(--bg-card);border-radius:var(--radius-lg);">
119
+ <h2 style="color:var(--rihal-gold);margin-bottom:16px;">No .rihal/ directory found</h2>
120
+ <p>Run the <code>*kickoff</code> workflow to initialize a project.</p>
121
+ <div class="empty-action">npx rcode install</div>
122
+ </div>
123
+ ` : `
124
+ <div class="stats">
125
+ <div class="stat">
126
+ <div class="label">Current Phase</div>
127
+ <div class="value">${esc(currentPhase)}</div>
128
+ <div class="sub">${phaseCount} total phases${currentSprint ? ` · Sprint ${esc(currentSprint)}` : ''}</div>
129
+ </div>
130
+ <div class="stat">
131
+ <div class="label">Milestone</div>
132
+ <div class="value" style="font-size:16px;padding-top:6px;" id="stat-milestone">${esc(state.milestone || '—')}</div>
133
+ <div class="sub">&nbsp;</div>
134
+ </div>
135
+ <div class="stat">
136
+ <div class="label">Decisions (ADRs)</div>
137
+ <div class="value">${decisionCount}</div>
138
+ <div class="sub">Architecture records</div>
139
+ </div>
140
+ <div class="stat">
141
+ <div class="label">Planning Files</div>
142
+ <div class="value">${artifactCount}</div>
143
+ <div class="sub">SPRINT, CONTEXT, VERIFY, RESEARCH</div>
144
+ </div>
145
+ ${state.blockers.length > 0 ? `
146
+ <div class="stat" style="border-left-color:var(--accent-red);">
147
+ <div class="label" style="color:var(--accent-red);">Blockers</div>
148
+ <div class="value" style="color:var(--accent-red);">${state.blockers.length}</div>
149
+ <div class="sub">Active blockers</div>
150
+ </div>` : ''}
151
+ ${state.councilSessions > 0 ? `
152
+ <div class="stat">
153
+ <div class="label">Council Sessions</div>
154
+ <div class="value">${state.councilSessions}</div>
155
+ <div class="sub">Recorded sessions</div>
156
+ </div>` : ''}
157
+ </div>
158
+
159
+ <div id="view-overview-dynamic"></div>
160
+
161
+ <section>
162
+ <h2>🎯 Active Context</h2>
163
+ <div class="body">
164
+ ${state.context
165
+ ? `<div class="item-preview" style="max-height:none;">${esc(state.context)}</div>`
166
+ : `<div class="empty">No active context.<div class="empty-action">Run context-build workflow</div></div>`}
167
+ </div>
168
+ </section>
169
+ `}
170
+ </div>
171
+
172
+ <div id="view-roadmap" class="view"></div>
173
+ <div id="view-milestones" class="view"></div>
174
+ <div id="view-phases" class="view"></div>
175
+ <div id="view-sprints" class="view"></div>
176
+ <div id="view-tasks" class="view"></div>
177
+
178
+ <div id="view-files" class="view">
179
+ <div class="view-title">Files</div>
180
+ <div id="file-list-inline"></div>
181
+ <div id="file-view"></div>
182
+ </div>
183
+
184
+ <div id="view-agents" class="view">
185
+ <div class="view-title">Agents</div>
186
+ <div class="filter-bar">
187
+ <input class="filter-input" type="text" placeholder="Filter…" oninput="filterItems(this,'agents-list')">
188
+ </div>
189
+ <div id="agents-list">
190
+ <div style="font-size:var(--text-sm);font-weight:600;color:var(--rihal-gold);margin-bottom:var(--space-3);">Team Members</div>
191
+ <div class="agents" style="margin-bottom:var(--space-6);">
192
+ ${realAgents.map(agentCard).join('')}
193
+ </div>
194
+ <div style="font-size:var(--text-sm);font-weight:600;color:var(--accent-blue);margin-bottom:var(--space-3);">AI Agents</div>
195
+ <div class="agents">
196
+ ${aiAgents.map(agentCard).join('')}
197
+ </div>
198
+ </div>
199
+ </div>
200
+
201
+ <div id="view-decisions" class="view"></div>
202
+
203
+ <footer>
204
+ <div class="arabic">رحلة البناء · The Journey of Building</div>
205
+ <div>Rihal Code · View-Only Dashboard · <kbd>R</kbd> refresh · <kbd>1-9</kbd> switch views · <kbd>F</kbd> filter</div>
206
+ </footer>
207
+ </div>
208
+ </div>
209
+ <div class="toast" id="toast"></div>
210
+ <script>
211
+ // #303: view agent skill file
212
+ function viewAgentSkill(name) {
213
+ // Try to find matching file in file tree
214
+ var items = document.querySelectorAll('.file-tree-item');
215
+ for (var i = 0; i < items.length; i++) {
216
+ if ((items[i].dataset.path || '').toLowerCase().includes(name)) {
217
+ items[i].click();
218
+ return;
219
+ }
220
+ }
221
+ // Fallback
222
+ navTo('files');
223
+ }
224
+ <\/script>
225
+ ${renderClientJs(state)}
226
+ </body>
227
+ </html>`;
228
+ }
229
+
230
+ module.exports = { renderHtml };