musubi-sdd 5.1.0 → 5.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. package/README.ja.md +106 -48
  2. package/README.md +110 -32
  3. package/bin/musubi-analyze.js +74 -67
  4. package/bin/musubi-browser.js +27 -26
  5. package/bin/musubi-change.js +48 -47
  6. package/bin/musubi-checkpoint.js +10 -7
  7. package/bin/musubi-convert.js +25 -25
  8. package/bin/musubi-costs.js +27 -10
  9. package/bin/musubi-gui.js +52 -46
  10. package/bin/musubi-init.js +1952 -10
  11. package/bin/musubi-orchestrate.js +327 -239
  12. package/bin/musubi-remember.js +69 -56
  13. package/bin/musubi-resolve.js +53 -45
  14. package/bin/musubi-trace.js +51 -22
  15. package/bin/musubi-validate.js +39 -30
  16. package/bin/musubi-workflow.js +33 -34
  17. package/bin/musubi.js +39 -2
  18. package/package.json +1 -1
  19. package/src/agents/agent-loop.js +94 -95
  20. package/src/agents/agentic/code-generator.js +119 -109
  21. package/src/agents/agentic/code-reviewer.js +105 -108
  22. package/src/agents/agentic/index.js +4 -4
  23. package/src/agents/browser/action-executor.js +13 -13
  24. package/src/agents/browser/ai-comparator.js +11 -10
  25. package/src/agents/browser/context-manager.js +6 -6
  26. package/src/agents/browser/index.js +5 -5
  27. package/src/agents/browser/nl-parser.js +31 -46
  28. package/src/agents/browser/screenshot.js +2 -2
  29. package/src/agents/browser/test-generator.js +6 -4
  30. package/src/agents/function-tool.js +71 -65
  31. package/src/agents/index.js +7 -7
  32. package/src/agents/schema-generator.js +98 -94
  33. package/src/analyzers/ast-extractor.js +158 -146
  34. package/src/analyzers/codegraph-auto-update.js +858 -0
  35. package/src/analyzers/complexity-analyzer.js +536 -0
  36. package/src/analyzers/context-optimizer.js +241 -126
  37. package/src/analyzers/impact-analyzer.js +1 -1
  38. package/src/analyzers/large-project-analyzer.js +766 -0
  39. package/src/analyzers/repository-map.js +77 -81
  40. package/src/analyzers/security-analyzer.js +19 -11
  41. package/src/analyzers/stuck-detector.js +19 -17
  42. package/src/converters/index.js +78 -57
  43. package/src/converters/ir/types.js +12 -12
  44. package/src/converters/parsers/musubi-parser.js +134 -126
  45. package/src/converters/parsers/openapi-parser.js +70 -53
  46. package/src/converters/parsers/speckit-parser.js +239 -175
  47. package/src/converters/writers/musubi-writer.js +123 -118
  48. package/src/converters/writers/speckit-writer.js +124 -113
  49. package/src/generators/rust-migration-generator.js +512 -0
  50. package/src/gui/public/index.html +1365 -1211
  51. package/src/gui/server.js +41 -40
  52. package/src/gui/services/file-watcher.js +23 -8
  53. package/src/gui/services/project-scanner.js +26 -20
  54. package/src/gui/services/replanning-service.js +27 -23
  55. package/src/gui/services/traceability-service.js +8 -8
  56. package/src/gui/services/workflow-service.js +14 -7
  57. package/src/index.js +151 -0
  58. package/src/integrations/cicd.js +90 -104
  59. package/src/integrations/codegraph-mcp.js +643 -0
  60. package/src/integrations/documentation.js +142 -103
  61. package/src/integrations/examples.js +95 -80
  62. package/src/integrations/github-client.js +17 -17
  63. package/src/integrations/index.js +5 -5
  64. package/src/integrations/mcp/index.js +21 -21
  65. package/src/integrations/mcp/mcp-context-provider.js +76 -78
  66. package/src/integrations/mcp/mcp-discovery.js +74 -72
  67. package/src/integrations/mcp/mcp-tool-registry.js +99 -94
  68. package/src/integrations/mcp-connector.js +70 -66
  69. package/src/integrations/platforms.js +50 -49
  70. package/src/integrations/tool-discovery.js +37 -31
  71. package/src/llm-providers/anthropic-provider.js +11 -11
  72. package/src/llm-providers/base-provider.js +16 -18
  73. package/src/llm-providers/copilot-provider.js +22 -19
  74. package/src/llm-providers/index.js +26 -25
  75. package/src/llm-providers/ollama-provider.js +11 -11
  76. package/src/llm-providers/openai-provider.js +12 -12
  77. package/src/managers/agent-memory.js +36 -24
  78. package/src/managers/checkpoint-manager.js +4 -8
  79. package/src/managers/delta-spec.js +19 -19
  80. package/src/managers/index.js +13 -4
  81. package/src/managers/memory-condenser.js +35 -45
  82. package/src/managers/repo-skill-manager.js +57 -31
  83. package/src/managers/skill-loader.js +25 -22
  84. package/src/managers/skill-tools.js +36 -72
  85. package/src/managers/workflow.js +30 -22
  86. package/src/monitoring/cost-tracker.js +48 -46
  87. package/src/monitoring/incident-manager.js +116 -106
  88. package/src/monitoring/index.js +144 -134
  89. package/src/monitoring/observability.js +75 -62
  90. package/src/monitoring/quality-dashboard.js +45 -41
  91. package/src/monitoring/release-manager.js +63 -53
  92. package/src/orchestration/agent-skill-binding.js +39 -47
  93. package/src/orchestration/error-handler.js +65 -107
  94. package/src/orchestration/guardrails/base-guardrail.js +26 -24
  95. package/src/orchestration/guardrails/guardrail-rules.js +50 -64
  96. package/src/orchestration/guardrails/index.js +5 -5
  97. package/src/orchestration/guardrails/input-guardrail.js +58 -45
  98. package/src/orchestration/guardrails/output-guardrail.js +104 -81
  99. package/src/orchestration/guardrails/safety-check.js +79 -79
  100. package/src/orchestration/index.js +38 -55
  101. package/src/orchestration/mcp-tool-adapters.js +96 -99
  102. package/src/orchestration/orchestration-engine.js +21 -21
  103. package/src/orchestration/pattern-registry.js +60 -45
  104. package/src/orchestration/patterns/auto.js +34 -47
  105. package/src/orchestration/patterns/group-chat.js +59 -65
  106. package/src/orchestration/patterns/handoff.js +67 -65
  107. package/src/orchestration/patterns/human-in-loop.js +51 -72
  108. package/src/orchestration/patterns/nested.js +25 -40
  109. package/src/orchestration/patterns/sequential.js +35 -34
  110. package/src/orchestration/patterns/swarm.js +63 -56
  111. package/src/orchestration/patterns/triage.js +150 -109
  112. package/src/orchestration/reasoning/index.js +9 -9
  113. package/src/orchestration/reasoning/planning-engine.js +143 -140
  114. package/src/orchestration/reasoning/reasoning-engine.js +206 -144
  115. package/src/orchestration/reasoning/self-correction.js +121 -128
  116. package/src/orchestration/replanning/adaptive-goal-modifier.js +107 -112
  117. package/src/orchestration/replanning/alternative-generator.js +37 -42
  118. package/src/orchestration/replanning/config.js +63 -59
  119. package/src/orchestration/replanning/goal-progress-tracker.js +98 -100
  120. package/src/orchestration/replanning/index.js +24 -20
  121. package/src/orchestration/replanning/plan-evaluator.js +49 -50
  122. package/src/orchestration/replanning/plan-monitor.js +32 -28
  123. package/src/orchestration/replanning/proactive-path-optimizer.js +175 -178
  124. package/src/orchestration/replanning/replan-history.js +33 -26
  125. package/src/orchestration/replanning/replanning-engine.js +106 -108
  126. package/src/orchestration/skill-executor.js +107 -109
  127. package/src/orchestration/skill-registry.js +85 -89
  128. package/src/orchestration/workflow-examples.js +228 -231
  129. package/src/orchestration/workflow-executor.js +65 -68
  130. package/src/orchestration/workflow-orchestrator.js +72 -73
  131. package/src/phase4-integration.js +47 -40
  132. package/src/phase5-integration.js +89 -30
  133. package/src/reporters/coverage-report.js +82 -30
  134. package/src/reporters/hierarchical-reporter.js +498 -0
  135. package/src/reporters/traceability-matrix-report.js +29 -20
  136. package/src/resolvers/issue-resolver.js +43 -31
  137. package/src/steering/advanced-validation.js +133 -124
  138. package/src/steering/auto-updater.js +60 -73
  139. package/src/steering/index.js +6 -6
  140. package/src/steering/quality-metrics.js +41 -35
  141. package/src/steering/steering-auto-update.js +83 -86
  142. package/src/steering/steering-validator.js +98 -106
  143. package/src/steering/template-constraints.js +53 -54
  144. package/src/templates/agents/claude-code/CLAUDE.md +32 -32
  145. package/src/templates/agents/claude-code/skills/agent-assistant/SKILL.md +13 -5
  146. package/src/templates/agents/claude-code/skills/ai-ml-engineer/mlops-guide.md +23 -23
  147. package/src/templates/agents/claude-code/skills/ai-ml-engineer/model-card-template.md +60 -41
  148. package/src/templates/agents/claude-code/skills/api-designer/api-patterns.md +27 -19
  149. package/src/templates/agents/claude-code/skills/api-designer/openapi-template.md +11 -7
  150. package/src/templates/agents/claude-code/skills/bug-hunter/SKILL.md +4 -3
  151. package/src/templates/agents/claude-code/skills/bug-hunter/root-cause-analysis.md +37 -15
  152. package/src/templates/agents/claude-code/skills/change-impact-analyzer/dependency-graph-patterns.md +36 -42
  153. package/src/templates/agents/claude-code/skills/change-impact-analyzer/impact-analysis-template.md +69 -60
  154. package/src/templates/agents/claude-code/skills/cloud-architect/aws-patterns.md +31 -38
  155. package/src/templates/agents/claude-code/skills/cloud-architect/azure-patterns.md +28 -23
  156. package/src/templates/agents/claude-code/skills/code-reviewer/SKILL.md +61 -0
  157. package/src/templates/agents/claude-code/skills/code-reviewer/best-practices.md +27 -0
  158. package/src/templates/agents/claude-code/skills/code-reviewer/review-checklist.md +29 -10
  159. package/src/templates/agents/claude-code/skills/code-reviewer/review-standards.md +29 -24
  160. package/src/templates/agents/claude-code/skills/constitution-enforcer/SKILL.md +8 -6
  161. package/src/templates/agents/claude-code/skills/constitution-enforcer/constitutional-articles.md +62 -26
  162. package/src/templates/agents/claude-code/skills/constitution-enforcer/phase-minus-one-gates.md +35 -16
  163. package/src/templates/agents/claude-code/skills/database-administrator/backup-recovery.md +27 -17
  164. package/src/templates/agents/claude-code/skills/database-administrator/tuning-guide.md +25 -20
  165. package/src/templates/agents/claude-code/skills/database-schema-designer/schema-patterns.md +39 -22
  166. package/src/templates/agents/claude-code/skills/devops-engineer/ci-cd-templates.md +25 -22
  167. package/src/templates/agents/claude-code/skills/issue-resolver/SKILL.md +24 -21
  168. package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +148 -63
  169. package/src/templates/agents/claude-code/skills/orchestrator/patterns.md +35 -16
  170. package/src/templates/agents/claude-code/skills/orchestrator/selection-matrix.md +69 -64
  171. package/src/templates/agents/claude-code/skills/performance-engineer/optimization-playbook.md +47 -47
  172. package/src/templates/agents/claude-code/skills/performance-optimizer/SKILL.md +69 -0
  173. package/src/templates/agents/claude-code/skills/performance-optimizer/benchmark-template.md +63 -45
  174. package/src/templates/agents/claude-code/skills/performance-optimizer/optimization-patterns.md +33 -35
  175. package/src/templates/agents/claude-code/skills/project-manager/SKILL.md +7 -6
  176. package/src/templates/agents/claude-code/skills/project-manager/agile-ceremonies.md +47 -28
  177. package/src/templates/agents/claude-code/skills/project-manager/project-templates.md +94 -78
  178. package/src/templates/agents/claude-code/skills/quality-assurance/SKILL.md +20 -17
  179. package/src/templates/agents/claude-code/skills/quality-assurance/qa-plan-template.md +63 -49
  180. package/src/templates/agents/claude-code/skills/release-coordinator/SKILL.md +5 -5
  181. package/src/templates/agents/claude-code/skills/release-coordinator/feature-flag-guide.md +30 -26
  182. package/src/templates/agents/claude-code/skills/release-coordinator/release-plan-template.md +67 -35
  183. package/src/templates/agents/claude-code/skills/requirements-analyst/ears-format.md +54 -42
  184. package/src/templates/agents/claude-code/skills/requirements-analyst/validation-rules.md +36 -33
  185. package/src/templates/agents/claude-code/skills/security-auditor/SKILL.md +77 -19
  186. package/src/templates/agents/claude-code/skills/security-auditor/audit-checklists.md +24 -24
  187. package/src/templates/agents/claude-code/skills/security-auditor/owasp-top-10.md +61 -20
  188. package/src/templates/agents/claude-code/skills/security-auditor/vulnerability-patterns.md +43 -11
  189. package/src/templates/agents/claude-code/skills/site-reliability-engineer/SKILL.md +1 -0
  190. package/src/templates/agents/claude-code/skills/site-reliability-engineer/incident-response-template.md +55 -25
  191. package/src/templates/agents/claude-code/skills/site-reliability-engineer/observability-patterns.md +78 -68
  192. package/src/templates/agents/claude-code/skills/site-reliability-engineer/slo-sli-guide.md +73 -53
  193. package/src/templates/agents/claude-code/skills/software-developer/solid-principles.md +83 -37
  194. package/src/templates/agents/claude-code/skills/software-developer/test-first-workflow.md +38 -31
  195. package/src/templates/agents/claude-code/skills/steering/SKILL.md +1 -0
  196. package/src/templates/agents/claude-code/skills/steering/auto-update-rules.md +31 -0
  197. package/src/templates/agents/claude-code/skills/system-architect/adr-template.md +25 -7
  198. package/src/templates/agents/claude-code/skills/system-architect/c4-model-guide.md +74 -61
  199. package/src/templates/agents/claude-code/skills/technical-writer/doc-templates/documentation-templates.md +70 -52
  200. package/src/templates/agents/claude-code/skills/test-engineer/SKILL.md +2 -0
  201. package/src/templates/agents/claude-code/skills/test-engineer/ears-test-mapping.md +75 -71
  202. package/src/templates/agents/claude-code/skills/test-engineer/test-types.md +85 -63
  203. package/src/templates/agents/claude-code/skills/traceability-auditor/coverage-matrix-template.md +39 -36
  204. package/src/templates/agents/claude-code/skills/traceability-auditor/gap-detection-rules.md +22 -17
  205. package/src/templates/agents/claude-code/skills/ui-ux-designer/SKILL.md +1 -0
  206. package/src/templates/agents/claude-code/skills/ui-ux-designer/accessibility-guidelines.md +49 -75
  207. package/src/templates/agents/claude-code/skills/ui-ux-designer/design-system-components.md +71 -59
  208. package/src/templates/agents/codex/AGENTS.md +74 -42
  209. package/src/templates/agents/cursor/AGENTS.md +74 -42
  210. package/src/templates/agents/gemini-cli/GEMINI.md +74 -42
  211. package/src/templates/agents/github-copilot/AGENTS.md +83 -51
  212. package/src/templates/agents/qwen-code/QWEN.md +74 -42
  213. package/src/templates/agents/windsurf/AGENTS.md +74 -42
  214. package/src/templates/architectures/README.md +41 -0
  215. package/src/templates/architectures/clean-architecture/README.md +113 -0
  216. package/src/templates/architectures/event-driven/README.md +162 -0
  217. package/src/templates/architectures/hexagonal/README.md +130 -0
  218. package/src/templates/index.js +6 -1
  219. package/src/templates/locale-manager.js +16 -16
  220. package/src/templates/shared/delta-spec-template.md +20 -13
  221. package/src/templates/shared/github-actions/musubi-issue-resolver.yml +5 -5
  222. package/src/templates/shared/github-actions/musubi-security-check.yml +3 -3
  223. package/src/templates/shared/github-actions/musubi-validate.yml +4 -4
  224. package/src/templates/shared/steering/structure.md +95 -0
  225. package/src/templates/skills/browser-agent.md +21 -16
  226. package/src/templates/skills/web-gui.md +8 -0
  227. package/src/templates/template-constraints.js +50 -53
  228. package/src/validators/advanced-validation.js +30 -36
  229. package/src/validators/constitutional-validator.js +77 -73
  230. package/src/validators/critic-system.js +49 -59
  231. package/src/validators/delta-format.js +59 -55
  232. package/src/validators/traceability-validator.js +7 -11
@@ -1,895 +1,952 @@
1
- <!DOCTYPE html>
1
+ <!doctype html>
2
2
  <html lang="ja">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>MUSUBI - SDD Dashboard</title>
7
- <style>
8
- * {
9
- margin: 0;
10
- padding: 0;
11
- box-sizing: border-box;
12
- }
13
-
14
- :root {
15
- --primary: #6366f1;
16
- --primary-dark: #4f46e5;
17
- --secondary: #0ea5e9;
18
- --success: #22c55e;
19
- --warning: #f59e0b;
20
- --error: #ef4444;
21
- --bg: #0f172a;
22
- --bg-card: #1e293b;
23
- --text: #f8fafc;
24
- --text-muted: #94a3b8;
25
- --border: #334155;
26
- }
27
-
28
- body {
29
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
30
- background: var(--bg);
31
- color: var(--text);
32
- min-height: 100vh;
33
- }
34
-
35
- .app {
36
- display: flex;
37
- flex-direction: column;
38
- min-height: 100vh;
39
- }
40
-
41
- header {
42
- background: var(--bg-card);
43
- border-bottom: 1px solid var(--border);
44
- padding: 1rem 2rem;
45
- display: flex;
46
- align-items: center;
47
- justify-content: space-between;
48
- }
49
-
50
- .logo {
51
- display: flex;
52
- align-items: center;
53
- gap: 0.75rem;
54
- font-size: 1.5rem;
55
- font-weight: 700;
56
- color: var(--primary);
57
- }
58
-
59
- .logo-icon {
60
- font-size: 2rem;
61
- }
62
-
63
- .connection-status {
64
- display: flex;
65
- align-items: center;
66
- gap: 0.5rem;
67
- font-size: 0.875rem;
68
- color: var(--text-muted);
69
- }
70
-
71
- .status-dot {
72
- width: 8px;
73
- height: 8px;
74
- border-radius: 50%;
75
- background: var(--error);
76
- }
77
-
78
- .status-dot.connected {
79
- background: var(--success);
80
- }
81
-
82
- main {
83
- flex: 1;
84
- display: grid;
85
- grid-template-columns: 280px 1fr 320px;
86
- gap: 1px;
87
- background: var(--border);
88
- }
89
-
90
- .sidebar {
91
- background: var(--bg-card);
92
- padding: 1.5rem;
93
- overflow-y: auto;
94
- }
95
-
96
- .sidebar h2 {
97
- font-size: 0.75rem;
98
- font-weight: 600;
99
- text-transform: uppercase;
100
- letter-spacing: 0.05em;
101
- color: var(--text-muted);
102
- margin-bottom: 1rem;
103
- }
104
-
105
- .nav-item {
106
- display: flex;
107
- align-items: center;
108
- gap: 0.75rem;
109
- padding: 0.75rem 1rem;
110
- border-radius: 0.5rem;
111
- color: var(--text-muted);
112
- text-decoration: none;
113
- margin-bottom: 0.25rem;
114
- cursor: pointer;
115
- transition: all 0.2s;
116
- }
117
-
118
- .nav-item:hover {
119
- background: rgba(99, 102, 241, 0.1);
120
- color: var(--text);
121
- }
122
-
123
- .nav-item.active {
124
- background: var(--primary);
125
- color: white;
126
- }
127
-
128
- .content {
129
- background: var(--bg);
130
- padding: 2rem;
131
- overflow-y: auto;
132
- }
133
-
134
- .panel {
135
- background: var(--bg-card);
136
- padding: 1.5rem;
137
- overflow-y: auto;
138
- }
139
-
140
- .panel h3 {
141
- font-size: 1rem;
142
- font-weight: 600;
143
- margin-bottom: 1rem;
144
- display: flex;
145
- align-items: center;
146
- gap: 0.5rem;
147
- }
148
-
149
- .card {
150
- background: var(--bg-card);
151
- border: 1px solid var(--border);
152
- border-radius: 0.75rem;
153
- padding: 1.5rem;
154
- margin-bottom: 1rem;
155
- }
156
-
157
- .card h3 {
158
- font-size: 1.125rem;
159
- font-weight: 600;
160
- margin-bottom: 1rem;
161
- }
162
-
163
- .stats-grid {
164
- display: grid;
165
- grid-template-columns: repeat(4, 1fr);
166
- gap: 1rem;
167
- margin-bottom: 2rem;
168
- }
169
-
170
- .stat-card {
171
- background: var(--bg-card);
172
- border: 1px solid var(--border);
173
- border-radius: 0.75rem;
174
- padding: 1.25rem;
175
- }
176
-
177
- .stat-card .label {
178
- font-size: 0.875rem;
179
- color: var(--text-muted);
180
- margin-bottom: 0.5rem;
181
- }
182
-
183
- .stat-card .value {
184
- font-size: 2rem;
185
- font-weight: 700;
186
- }
187
-
188
- .stat-card .change {
189
- font-size: 0.875rem;
190
- color: var(--success);
191
- margin-top: 0.25rem;
192
- }
193
-
194
- .workflow-stage {
195
- display: flex;
196
- align-items: center;
197
- gap: 0.75rem;
198
- padding: 0.75rem;
199
- background: rgba(99, 102, 241, 0.1);
200
- border-radius: 0.5rem;
201
- margin-bottom: 0.5rem;
202
- }
203
-
204
- .workflow-stage.completed {
205
- background: rgba(34, 197, 94, 0.1);
206
- }
207
-
208
- .workflow-stage.active {
209
- background: rgba(14, 165, 233, 0.2);
210
- border: 1px solid var(--secondary);
211
- }
212
-
213
- .stage-number {
214
- width: 24px;
215
- height: 24px;
216
- border-radius: 50%;
217
- background: var(--border);
218
- display: flex;
219
- align-items: center;
220
- justify-content: center;
221
- font-size: 0.75rem;
222
- font-weight: 600;
223
- }
224
-
225
- .workflow-stage.completed .stage-number {
226
- background: var(--success);
227
- color: white;
228
- }
229
-
230
- .workflow-stage.active .stage-number {
231
- background: var(--secondary);
232
- color: white;
233
- }
234
-
235
- .progress-bar {
236
- height: 8px;
237
- background: var(--border);
238
- border-radius: 4px;
239
- overflow: hidden;
240
- margin-top: 1rem;
241
- }
242
-
243
- .progress-bar-fill {
244
- height: 100%;
245
- background: linear-gradient(90deg, var(--primary), var(--secondary));
246
- border-radius: 4px;
247
- transition: width 0.3s ease;
248
- }
249
-
250
- .requirements-list {
251
- list-style: none;
252
- }
253
-
254
- .requirement-item {
255
- display: flex;
256
- align-items: flex-start;
257
- gap: 0.75rem;
258
- padding: 0.75rem;
259
- border-bottom: 1px solid var(--border);
260
- }
261
-
262
- .requirement-item:last-child {
263
- border-bottom: none;
264
- }
265
-
266
- .req-id {
267
- font-family: monospace;
268
- font-size: 0.75rem;
269
- background: var(--primary);
270
- color: white;
271
- padding: 0.25rem 0.5rem;
272
- border-radius: 0.25rem;
273
- }
274
-
275
- .req-title {
276
- flex: 1;
277
- font-size: 0.875rem;
278
- }
279
-
280
- .req-status {
281
- font-size: 0.75rem;
282
- padding: 0.25rem 0.5rem;
283
- border-radius: 0.25rem;
284
- }
285
-
286
- .req-status.implemented {
287
- background: rgba(34, 197, 94, 0.2);
288
- color: var(--success);
289
- }
290
-
291
- .req-status.designed {
292
- background: rgba(14, 165, 233, 0.2);
293
- color: var(--secondary);
294
- }
295
-
296
- .req-status.unlinked {
297
- background: rgba(148, 163, 184, 0.2);
298
- color: var(--text-muted);
299
- }
300
-
301
- .constitution-article {
302
- padding: 1rem;
303
- background: rgba(99, 102, 241, 0.05);
304
- border-left: 3px solid var(--primary);
305
- margin-bottom: 0.75rem;
306
- border-radius: 0 0.5rem 0.5rem 0;
307
- }
308
-
309
- .article-number {
310
- font-size: 0.75rem;
311
- color: var(--primary);
312
- font-weight: 600;
313
- margin-bottom: 0.25rem;
314
- }
315
-
316
- .loading {
317
- display: flex;
318
- align-items: center;
319
- justify-content: center;
320
- padding: 2rem;
321
- color: var(--text-muted);
322
- }
323
-
324
- .loading-spinner {
325
- width: 24px;
326
- height: 24px;
327
- border: 2px solid var(--border);
328
- border-top-color: var(--primary);
329
- border-radius: 50%;
330
- animation: spin 1s linear infinite;
331
- margin-right: 0.75rem;
332
- }
333
-
334
- @keyframes spin {
335
- to { transform: rotate(360deg); }
336
- }
337
-
338
- .empty-state {
339
- text-align: center;
340
- padding: 3rem;
341
- color: var(--text-muted);
342
- }
343
-
344
- .empty-state h4 {
345
- font-size: 1.25rem;
346
- margin-bottom: 0.5rem;
347
- color: var(--text);
348
- }
349
-
350
- .btn {
351
- display: inline-flex;
352
- align-items: center;
353
- gap: 0.5rem;
354
- padding: 0.625rem 1rem;
355
- border-radius: 0.5rem;
356
- font-size: 0.875rem;
357
- font-weight: 500;
358
- cursor: pointer;
359
- border: none;
360
- transition: all 0.2s;
361
- }
362
-
363
- .btn-primary {
364
- background: var(--primary);
365
- color: white;
366
- }
367
-
368
- .btn-primary:hover {
369
- background: var(--primary-dark);
370
- }
371
-
372
- .btn-outline {
373
- background: transparent;
374
- border: 1px solid var(--border);
375
- color: var(--text);
376
- }
377
-
378
- .btn-outline:hover {
379
- background: rgba(255, 255, 255, 0.05);
380
- }
381
-
382
- footer {
383
- background: var(--bg-card);
384
- border-top: 1px solid var(--border);
385
- padding: 1rem 2rem;
386
- text-align: center;
387
- font-size: 0.875rem;
388
- color: var(--text-muted);
389
- }
390
-
391
- #traceability-graph {
392
- width: 100%;
393
- height: 400px;
394
- background: var(--bg);
395
- border-radius: 0.5rem;
396
- border: 1px solid var(--border);
397
- }
398
-
399
- /* Modal styles */
400
- .modal-overlay {
401
- display: none;
402
- position: fixed;
403
- top: 0;
404
- left: 0;
405
- right: 0;
406
- bottom: 0;
407
- background: rgba(0, 0, 0, 0.7);
408
- z-index: 1000;
409
- align-items: center;
410
- justify-content: center;
411
- }
412
-
413
- .modal-overlay.active {
414
- display: flex;
415
- }
416
-
417
- .modal {
418
- background: var(--bg-card);
419
- border: 1px solid var(--border);
420
- border-radius: 0.75rem;
421
- padding: 1.5rem;
422
- width: 100%;
423
- max-width: 500px;
424
- max-height: 90vh;
425
- overflow-y: auto;
426
- }
427
-
428
- .modal h2 {
429
- font-size: 1.25rem;
430
- margin-bottom: 1rem;
431
- display: flex;
432
- align-items: center;
433
- gap: 0.5rem;
434
- }
435
-
436
- .form-group {
437
- margin-bottom: 1rem;
438
- }
439
-
440
- .form-group label {
441
- display: block;
442
- font-size: 0.875rem;
443
- font-weight: 500;
444
- margin-bottom: 0.5rem;
445
- color: var(--text-muted);
446
- }
447
-
448
- .form-group input,
449
- .form-group select,
450
- .form-group textarea {
451
- width: 100%;
452
- padding: 0.625rem;
453
- border: 1px solid var(--border);
454
- border-radius: 0.5rem;
455
- background: var(--bg);
456
- color: var(--text);
457
- font-size: 0.875rem;
458
- }
459
-
460
- .form-group input:focus,
461
- .form-group select:focus,
462
- .form-group textarea:focus {
463
- outline: none;
464
- border-color: var(--primary);
465
- }
466
-
467
- .form-group textarea {
468
- min-height: 100px;
469
- resize: vertical;
470
- }
471
-
472
- .modal-actions {
473
- display: flex;
474
- gap: 0.75rem;
475
- justify-content: flex-end;
476
- margin-top: 1.5rem;
477
- }
478
-
479
- @media (max-width: 1200px) {
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>MUSUBI - SDD Dashboard</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ :root {
15
+ --primary: #6366f1;
16
+ --primary-dark: #4f46e5;
17
+ --secondary: #0ea5e9;
18
+ --success: #22c55e;
19
+ --warning: #f59e0b;
20
+ --error: #ef4444;
21
+ --bg: #0f172a;
22
+ --bg-card: #1e293b;
23
+ --text: #f8fafc;
24
+ --text-muted: #94a3b8;
25
+ --border: #334155;
26
+ }
27
+
28
+ body {
29
+ font-family:
30
+ -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
31
+ background: var(--bg);
32
+ color: var(--text);
33
+ min-height: 100vh;
34
+ }
35
+
36
+ .app {
37
+ display: flex;
38
+ flex-direction: column;
39
+ min-height: 100vh;
40
+ }
41
+
42
+ header {
43
+ background: var(--bg-card);
44
+ border-bottom: 1px solid var(--border);
45
+ padding: 1rem 2rem;
46
+ display: flex;
47
+ align-items: center;
48
+ justify-content: space-between;
49
+ }
50
+
51
+ .logo {
52
+ display: flex;
53
+ align-items: center;
54
+ gap: 0.75rem;
55
+ font-size: 1.5rem;
56
+ font-weight: 700;
57
+ color: var(--primary);
58
+ }
59
+
60
+ .logo-icon {
61
+ font-size: 2rem;
62
+ }
63
+
64
+ .connection-status {
65
+ display: flex;
66
+ align-items: center;
67
+ gap: 0.5rem;
68
+ font-size: 0.875rem;
69
+ color: var(--text-muted);
70
+ }
71
+
72
+ .status-dot {
73
+ width: 8px;
74
+ height: 8px;
75
+ border-radius: 50%;
76
+ background: var(--error);
77
+ }
78
+
79
+ .status-dot.connected {
80
+ background: var(--success);
81
+ }
82
+
480
83
  main {
481
- grid-template-columns: 1fr;
84
+ flex: 1;
85
+ display: grid;
86
+ grid-template-columns: 280px 1fr 320px;
87
+ gap: 1px;
88
+ background: var(--border);
89
+ }
90
+
91
+ .sidebar {
92
+ background: var(--bg-card);
93
+ padding: 1.5rem;
94
+ overflow-y: auto;
95
+ }
96
+
97
+ .sidebar h2 {
98
+ font-size: 0.75rem;
99
+ font-weight: 600;
100
+ text-transform: uppercase;
101
+ letter-spacing: 0.05em;
102
+ color: var(--text-muted);
103
+ margin-bottom: 1rem;
104
+ }
105
+
106
+ .nav-item {
107
+ display: flex;
108
+ align-items: center;
109
+ gap: 0.75rem;
110
+ padding: 0.75rem 1rem;
111
+ border-radius: 0.5rem;
112
+ color: var(--text-muted);
113
+ text-decoration: none;
114
+ margin-bottom: 0.25rem;
115
+ cursor: pointer;
116
+ transition: all 0.2s;
117
+ }
118
+
119
+ .nav-item:hover {
120
+ background: rgba(99, 102, 241, 0.1);
121
+ color: var(--text);
122
+ }
123
+
124
+ .nav-item.active {
125
+ background: var(--primary);
126
+ color: white;
127
+ }
128
+
129
+ .content {
130
+ background: var(--bg);
131
+ padding: 2rem;
132
+ overflow-y: auto;
133
+ }
134
+
135
+ .panel {
136
+ background: var(--bg-card);
137
+ padding: 1.5rem;
138
+ overflow-y: auto;
139
+ }
140
+
141
+ .panel h3 {
142
+ font-size: 1rem;
143
+ font-weight: 600;
144
+ margin-bottom: 1rem;
145
+ display: flex;
146
+ align-items: center;
147
+ gap: 0.5rem;
148
+ }
149
+
150
+ .card {
151
+ background: var(--bg-card);
152
+ border: 1px solid var(--border);
153
+ border-radius: 0.75rem;
154
+ padding: 1.5rem;
155
+ margin-bottom: 1rem;
156
+ }
157
+
158
+ .card h3 {
159
+ font-size: 1.125rem;
160
+ font-weight: 600;
161
+ margin-bottom: 1rem;
162
+ }
163
+
164
+ .stats-grid {
165
+ display: grid;
166
+ grid-template-columns: repeat(4, 1fr);
167
+ gap: 1rem;
168
+ margin-bottom: 2rem;
169
+ }
170
+
171
+ .stat-card {
172
+ background: var(--bg-card);
173
+ border: 1px solid var(--border);
174
+ border-radius: 0.75rem;
175
+ padding: 1.25rem;
176
+ }
177
+
178
+ .stat-card .label {
179
+ font-size: 0.875rem;
180
+ color: var(--text-muted);
181
+ margin-bottom: 0.5rem;
182
+ }
183
+
184
+ .stat-card .value {
185
+ font-size: 2rem;
186
+ font-weight: 700;
187
+ }
188
+
189
+ .stat-card .change {
190
+ font-size: 0.875rem;
191
+ color: var(--success);
192
+ margin-top: 0.25rem;
193
+ }
194
+
195
+ .workflow-stage {
196
+ display: flex;
197
+ align-items: center;
198
+ gap: 0.75rem;
199
+ padding: 0.75rem;
200
+ background: rgba(99, 102, 241, 0.1);
201
+ border-radius: 0.5rem;
202
+ margin-bottom: 0.5rem;
203
+ }
204
+
205
+ .workflow-stage.completed {
206
+ background: rgba(34, 197, 94, 0.1);
207
+ }
208
+
209
+ .workflow-stage.active {
210
+ background: rgba(14, 165, 233, 0.2);
211
+ border: 1px solid var(--secondary);
212
+ }
213
+
214
+ .stage-number {
215
+ width: 24px;
216
+ height: 24px;
217
+ border-radius: 50%;
218
+ background: var(--border);
219
+ display: flex;
220
+ align-items: center;
221
+ justify-content: center;
222
+ font-size: 0.75rem;
223
+ font-weight: 600;
224
+ }
225
+
226
+ .workflow-stage.completed .stage-number {
227
+ background: var(--success);
228
+ color: white;
229
+ }
230
+
231
+ .workflow-stage.active .stage-number {
232
+ background: var(--secondary);
233
+ color: white;
234
+ }
235
+
236
+ .progress-bar {
237
+ height: 8px;
238
+ background: var(--border);
239
+ border-radius: 4px;
240
+ overflow: hidden;
241
+ margin-top: 1rem;
242
+ }
243
+
244
+ .progress-bar-fill {
245
+ height: 100%;
246
+ background: linear-gradient(90deg, var(--primary), var(--secondary));
247
+ border-radius: 4px;
248
+ transition: width 0.3s ease;
249
+ }
250
+
251
+ .requirements-list {
252
+ list-style: none;
253
+ }
254
+
255
+ .requirement-item {
256
+ display: flex;
257
+ align-items: flex-start;
258
+ gap: 0.75rem;
259
+ padding: 0.75rem;
260
+ border-bottom: 1px solid var(--border);
261
+ }
262
+
263
+ .requirement-item:last-child {
264
+ border-bottom: none;
265
+ }
266
+
267
+ .req-id {
268
+ font-family: monospace;
269
+ font-size: 0.75rem;
270
+ background: var(--primary);
271
+ color: white;
272
+ padding: 0.25rem 0.5rem;
273
+ border-radius: 0.25rem;
274
+ }
275
+
276
+ .req-title {
277
+ flex: 1;
278
+ font-size: 0.875rem;
279
+ }
280
+
281
+ .req-status {
282
+ font-size: 0.75rem;
283
+ padding: 0.25rem 0.5rem;
284
+ border-radius: 0.25rem;
285
+ }
286
+
287
+ .req-status.implemented {
288
+ background: rgba(34, 197, 94, 0.2);
289
+ color: var(--success);
290
+ }
291
+
292
+ .req-status.designed {
293
+ background: rgba(14, 165, 233, 0.2);
294
+ color: var(--secondary);
295
+ }
296
+
297
+ .req-status.unlinked {
298
+ background: rgba(148, 163, 184, 0.2);
299
+ color: var(--text-muted);
300
+ }
301
+
302
+ .constitution-article {
303
+ padding: 1rem;
304
+ background: rgba(99, 102, 241, 0.05);
305
+ border-left: 3px solid var(--primary);
306
+ margin-bottom: 0.75rem;
307
+ border-radius: 0 0.5rem 0.5rem 0;
308
+ }
309
+
310
+ .article-number {
311
+ font-size: 0.75rem;
312
+ color: var(--primary);
313
+ font-weight: 600;
314
+ margin-bottom: 0.25rem;
315
+ }
316
+
317
+ .loading {
318
+ display: flex;
319
+ align-items: center;
320
+ justify-content: center;
321
+ padding: 2rem;
322
+ color: var(--text-muted);
323
+ }
324
+
325
+ .loading-spinner {
326
+ width: 24px;
327
+ height: 24px;
328
+ border: 2px solid var(--border);
329
+ border-top-color: var(--primary);
330
+ border-radius: 50%;
331
+ animation: spin 1s linear infinite;
332
+ margin-right: 0.75rem;
333
+ }
334
+
335
+ @keyframes spin {
336
+ to {
337
+ transform: rotate(360deg);
338
+ }
339
+ }
340
+
341
+ .empty-state {
342
+ text-align: center;
343
+ padding: 3rem;
344
+ color: var(--text-muted);
345
+ }
346
+
347
+ .empty-state h4 {
348
+ font-size: 1.25rem;
349
+ margin-bottom: 0.5rem;
350
+ color: var(--text);
351
+ }
352
+
353
+ .btn {
354
+ display: inline-flex;
355
+ align-items: center;
356
+ gap: 0.5rem;
357
+ padding: 0.625rem 1rem;
358
+ border-radius: 0.5rem;
359
+ font-size: 0.875rem;
360
+ font-weight: 500;
361
+ cursor: pointer;
362
+ border: none;
363
+ transition: all 0.2s;
364
+ }
365
+
366
+ .btn-primary {
367
+ background: var(--primary);
368
+ color: white;
369
+ }
370
+
371
+ .btn-primary:hover {
372
+ background: var(--primary-dark);
373
+ }
374
+
375
+ .btn-outline {
376
+ background: transparent;
377
+ border: 1px solid var(--border);
378
+ color: var(--text);
482
379
  }
483
-
484
- .sidebar, .panel {
380
+
381
+ .btn-outline:hover {
382
+ background: rgba(255, 255, 255, 0.05);
383
+ }
384
+
385
+ footer {
386
+ background: var(--bg-card);
387
+ border-top: 1px solid var(--border);
388
+ padding: 1rem 2rem;
389
+ text-align: center;
390
+ font-size: 0.875rem;
391
+ color: var(--text-muted);
392
+ }
393
+
394
+ #traceability-graph {
395
+ width: 100%;
396
+ height: 400px;
397
+ background: var(--bg);
398
+ border-radius: 0.5rem;
399
+ border: 1px solid var(--border);
400
+ }
401
+
402
+ /* Modal styles */
403
+ .modal-overlay {
485
404
  display: none;
405
+ position: fixed;
406
+ top: 0;
407
+ left: 0;
408
+ right: 0;
409
+ bottom: 0;
410
+ background: rgba(0, 0, 0, 0.7);
411
+ z-index: 1000;
412
+ align-items: center;
413
+ justify-content: center;
486
414
  }
487
- }
488
-
489
- /* Replanning Styles */
490
- .replan-status {
491
- display: flex;
492
- align-items: center;
493
- gap: 0.5rem;
494
- padding: 0.75rem 1rem;
495
- border-radius: 0.5rem;
496
- margin-bottom: 1rem;
497
- }
498
-
499
- .replan-status.idle {
500
- background: rgba(148, 163, 184, 0.1);
501
- border: 1px solid var(--text-muted);
502
- }
503
-
504
- .replan-status.monitoring {
505
- background: rgba(14, 165, 233, 0.1);
506
- border: 1px solid var(--secondary);
507
- }
508
-
509
- .replan-status.replanning {
510
- background: rgba(245, 158, 11, 0.1);
511
- border: 1px solid var(--warning);
512
- }
513
-
514
- .replan-status.optimizing {
515
- background: rgba(99, 102, 241, 0.1);
516
- border: 1px solid var(--primary);
517
- }
518
-
519
- .status-indicator {
520
- width: 10px;
521
- height: 10px;
522
- border-radius: 50%;
523
- animation: pulse 2s infinite;
524
- }
525
-
526
- .status-indicator.idle { background: var(--text-muted); animation: none; }
527
- .status-indicator.monitoring { background: var(--secondary); }
528
- .status-indicator.replanning { background: var(--warning); }
529
- .status-indicator.optimizing { background: var(--primary); }
530
-
531
- @keyframes pulse {
532
- 0%, 100% { opacity: 1; }
533
- 50% { opacity: 0.5; }
534
- }
535
-
536
- @keyframes slideIn {
537
- from { transform: translateX(100%); opacity: 0; }
538
- to { transform: translateX(0); opacity: 1; }
539
- }
540
-
541
- @keyframes slideOut {
542
- from { transform: translateX(0); opacity: 1; }
543
- to { transform: translateX(100%); opacity: 0; }
544
- }
545
-
546
- .goal-progress {
547
- display: flex;
548
- flex-direction: column;
549
- gap: 0.5rem;
550
- }
551
-
552
- .goal-item {
553
- display: flex;
554
- align-items: center;
555
- gap: 0.75rem;
556
- padding: 0.75rem;
557
- background: rgba(99, 102, 241, 0.05);
558
- border-radius: 0.5rem;
559
- }
560
-
561
- .goal-progress-bar {
562
- flex: 1;
563
- height: 8px;
564
- background: var(--border);
565
- border-radius: 4px;
566
- overflow: hidden;
567
- }
568
-
569
- .goal-progress-fill {
570
- height: 100%;
571
- border-radius: 4px;
572
- transition: width 0.3s ease;
573
- }
574
-
575
- .goal-progress-fill.low { background: var(--error); }
576
- .goal-progress-fill.medium { background: var(--warning); }
577
- .goal-progress-fill.high { background: var(--success); }
578
-
579
- .optimization-card {
580
- display: flex;
581
- flex-direction: column;
582
- gap: 0.5rem;
583
- padding: 1rem;
584
- background: rgba(99, 102, 241, 0.1);
585
- border-radius: 0.5rem;
586
- margin-bottom: 0.75rem;
587
- }
588
-
589
- .optimization-suggestion {
590
- display: flex;
591
- align-items: flex-start;
592
- gap: 0.5rem;
593
- padding: 0.5rem;
594
- background: rgba(255, 255, 255, 0.05);
595
- border-radius: 0.25rem;
596
- }
597
-
598
- .history-timeline {
599
- position: relative;
600
- padding-left: 1.5rem;
601
- }
602
-
603
- .history-timeline::before {
604
- content: '';
605
- position: absolute;
606
- left: 0.5rem;
607
- top: 0;
608
- bottom: 0;
609
- width: 2px;
610
- background: var(--border);
611
- }
612
-
613
- .history-item {
614
- position: relative;
615
- padding: 0.75rem;
616
- background: rgba(255, 255, 255, 0.02);
617
- border-radius: 0.5rem;
618
- margin-bottom: 0.75rem;
619
- }
620
-
621
- .history-item::before {
622
- content: '';
623
- position: absolute;
624
- left: -1.25rem;
625
- top: 1rem;
626
- width: 10px;
627
- height: 10px;
628
- border-radius: 50%;
629
- background: var(--primary);
630
- }
631
-
632
- .history-item.success::before { background: var(--success); }
633
- .history-item.warning::before { background: var(--warning); }
634
- .history-item.error::before { background: var(--error); }
635
- </style>
636
- </head>
637
- <body>
638
- <div class="app">
639
- <header>
640
- <div class="logo">
641
- <span class="logo-icon">🔮</span>
642
- MUSUBI
643
- </div>
644
- <div class="connection-status">
645
- <span class="status-dot" id="statusDot"></span>
646
- <span id="statusText">Connecting...</span>
647
- </div>
648
- </header>
649
415
 
650
- <main>
651
- <aside class="sidebar">
652
- <h2>Navigation</h2>
653
- <div class="nav-item active" data-view="dashboard">
654
- 📊 Dashboard
655
- </div>
656
- <div class="nav-item" data-view="workflow">
657
- 🔄 Workflow
658
- </div>
659
- <div class="nav-item" data-view="requirements">
660
- 📋 Requirements
661
- </div>
662
- <div class="nav-item" data-view="traceability">
663
- 🔗 Traceability
664
- </div>
665
- <div class="nav-item" data-view="replanning">
666
- 🔄 Replanning
667
- </div>
668
- <div class="nav-item" data-view="constitution">
669
- 📜 Constitution
670
- </div>
671
- </aside>
416
+ .modal-overlay.active {
417
+ display: flex;
418
+ }
672
419
 
673
- <section class="content" id="mainContent">
674
- <div class="loading">
675
- <div class="loading-spinner"></div>
676
- Loading project data...
677
- </div>
678
- </section>
420
+ .modal {
421
+ background: var(--bg-card);
422
+ border: 1px solid var(--border);
423
+ border-radius: 0.75rem;
424
+ padding: 1.5rem;
425
+ width: 100%;
426
+ max-width: 500px;
427
+ max-height: 90vh;
428
+ overflow-y: auto;
429
+ }
679
430
 
680
- <aside class="panel">
681
- <h3>📌 Quick Actions</h3>
682
- <div style="margin-bottom: 1rem;">
683
- <button class="btn btn-primary" style="width: 100%; margin-bottom: 0.5rem;" onclick="openModal('requirementModal')">
684
- New Requirement
685
- </button>
686
- <button class="btn btn-outline" style="width: 100%; margin-bottom: 0.5rem;" onclick="runQuickAction('validate')">
687
- 🔍 Validate Project
688
- </button>
689
- <button class="btn btn-outline" style="width: 100%;" onclick="runQuickAction('export-report')">
690
- 📤 Export Report
691
- </button>
692
- </div>
431
+ .modal h2 {
432
+ font-size: 1.25rem;
433
+ margin-bottom: 1rem;
434
+ display: flex;
435
+ align-items: center;
436
+ gap: 0.5rem;
437
+ }
438
+
439
+ .form-group {
440
+ margin-bottom: 1rem;
441
+ }
442
+
443
+ .form-group label {
444
+ display: block;
445
+ font-size: 0.875rem;
446
+ font-weight: 500;
447
+ margin-bottom: 0.5rem;
448
+ color: var(--text-muted);
449
+ }
450
+
451
+ .form-group input,
452
+ .form-group select,
453
+ .form-group textarea {
454
+ width: 100%;
455
+ padding: 0.625rem;
456
+ border: 1px solid var(--border);
457
+ border-radius: 0.5rem;
458
+ background: var(--bg);
459
+ color: var(--text);
460
+ font-size: 0.875rem;
461
+ }
462
+
463
+ .form-group input:focus,
464
+ .form-group select:focus,
465
+ .form-group textarea:focus {
466
+ outline: none;
467
+ border-color: var(--primary);
468
+ }
469
+
470
+ .form-group textarea {
471
+ min-height: 100px;
472
+ resize: vertical;
473
+ }
474
+
475
+ .modal-actions {
476
+ display: flex;
477
+ gap: 0.75rem;
478
+ justify-content: flex-end;
479
+ margin-top: 1.5rem;
480
+ }
481
+
482
+ @media (max-width: 1200px) {
483
+ main {
484
+ grid-template-columns: 1fr;
485
+ }
486
+
487
+ .sidebar,
488
+ .panel {
489
+ display: none;
490
+ }
491
+ }
492
+
493
+ /* Replanning Styles */
494
+ .replan-status {
495
+ display: flex;
496
+ align-items: center;
497
+ gap: 0.5rem;
498
+ padding: 0.75rem 1rem;
499
+ border-radius: 0.5rem;
500
+ margin-bottom: 1rem;
501
+ }
502
+
503
+ .replan-status.idle {
504
+ background: rgba(148, 163, 184, 0.1);
505
+ border: 1px solid var(--text-muted);
506
+ }
507
+
508
+ .replan-status.monitoring {
509
+ background: rgba(14, 165, 233, 0.1);
510
+ border: 1px solid var(--secondary);
511
+ }
512
+
513
+ .replan-status.replanning {
514
+ background: rgba(245, 158, 11, 0.1);
515
+ border: 1px solid var(--warning);
516
+ }
517
+
518
+ .replan-status.optimizing {
519
+ background: rgba(99, 102, 241, 0.1);
520
+ border: 1px solid var(--primary);
521
+ }
522
+
523
+ .status-indicator {
524
+ width: 10px;
525
+ height: 10px;
526
+ border-radius: 50%;
527
+ animation: pulse 2s infinite;
528
+ }
529
+
530
+ .status-indicator.idle {
531
+ background: var(--text-muted);
532
+ animation: none;
533
+ }
534
+ .status-indicator.monitoring {
535
+ background: var(--secondary);
536
+ }
537
+ .status-indicator.replanning {
538
+ background: var(--warning);
539
+ }
540
+ .status-indicator.optimizing {
541
+ background: var(--primary);
542
+ }
543
+
544
+ @keyframes pulse {
545
+ 0%,
546
+ 100% {
547
+ opacity: 1;
548
+ }
549
+ 50% {
550
+ opacity: 0.5;
551
+ }
552
+ }
553
+
554
+ @keyframes slideIn {
555
+ from {
556
+ transform: translateX(100%);
557
+ opacity: 0;
558
+ }
559
+ to {
560
+ transform: translateX(0);
561
+ opacity: 1;
562
+ }
563
+ }
564
+
565
+ @keyframes slideOut {
566
+ from {
567
+ transform: translateX(0);
568
+ opacity: 1;
569
+ }
570
+ to {
571
+ transform: translateX(100%);
572
+ opacity: 0;
573
+ }
574
+ }
575
+
576
+ .goal-progress {
577
+ display: flex;
578
+ flex-direction: column;
579
+ gap: 0.5rem;
580
+ }
581
+
582
+ .goal-item {
583
+ display: flex;
584
+ align-items: center;
585
+ gap: 0.75rem;
586
+ padding: 0.75rem;
587
+ background: rgba(99, 102, 241, 0.05);
588
+ border-radius: 0.5rem;
589
+ }
590
+
591
+ .goal-progress-bar {
592
+ flex: 1;
593
+ height: 8px;
594
+ background: var(--border);
595
+ border-radius: 4px;
596
+ overflow: hidden;
597
+ }
598
+
599
+ .goal-progress-fill {
600
+ height: 100%;
601
+ border-radius: 4px;
602
+ transition: width 0.3s ease;
603
+ }
604
+
605
+ .goal-progress-fill.low {
606
+ background: var(--error);
607
+ }
608
+ .goal-progress-fill.medium {
609
+ background: var(--warning);
610
+ }
611
+ .goal-progress-fill.high {
612
+ background: var(--success);
613
+ }
614
+
615
+ .optimization-card {
616
+ display: flex;
617
+ flex-direction: column;
618
+ gap: 0.5rem;
619
+ padding: 1rem;
620
+ background: rgba(99, 102, 241, 0.1);
621
+ border-radius: 0.5rem;
622
+ margin-bottom: 0.75rem;
623
+ }
624
+
625
+ .optimization-suggestion {
626
+ display: flex;
627
+ align-items: flex-start;
628
+ gap: 0.5rem;
629
+ padding: 0.5rem;
630
+ background: rgba(255, 255, 255, 0.05);
631
+ border-radius: 0.25rem;
632
+ }
633
+
634
+ .history-timeline {
635
+ position: relative;
636
+ padding-left: 1.5rem;
637
+ }
638
+
639
+ .history-timeline::before {
640
+ content: '';
641
+ position: absolute;
642
+ left: 0.5rem;
643
+ top: 0;
644
+ bottom: 0;
645
+ width: 2px;
646
+ background: var(--border);
647
+ }
693
648
 
694
- <h3>📈 Coverage</h3>
695
- <div id="coverageStats">
649
+ .history-item {
650
+ position: relative;
651
+ padding: 0.75rem;
652
+ background: rgba(255, 255, 255, 0.02);
653
+ border-radius: 0.5rem;
654
+ margin-bottom: 0.75rem;
655
+ }
656
+
657
+ .history-item::before {
658
+ content: '';
659
+ position: absolute;
660
+ left: -1.25rem;
661
+ top: 1rem;
662
+ width: 10px;
663
+ height: 10px;
664
+ border-radius: 50%;
665
+ background: var(--primary);
666
+ }
667
+
668
+ .history-item.success::before {
669
+ background: var(--success);
670
+ }
671
+ .history-item.warning::before {
672
+ background: var(--warning);
673
+ }
674
+ .history-item.error::before {
675
+ background: var(--error);
676
+ }
677
+ </style>
678
+ </head>
679
+ <body>
680
+ <div class="app">
681
+ <header>
682
+ <div class="logo">
683
+ <span class="logo-icon">🔮</span>
684
+ MUSUBI
685
+ </div>
686
+ <div class="connection-status">
687
+ <span class="status-dot" id="statusDot"></span>
688
+ <span id="statusText">Connecting...</span>
689
+ </div>
690
+ </header>
691
+
692
+ <main>
693
+ <aside class="sidebar">
694
+ <h2>Navigation</h2>
695
+ <div class="nav-item active" data-view="dashboard">📊 Dashboard</div>
696
+ <div class="nav-item" data-view="workflow">🔄 Workflow</div>
697
+ <div class="nav-item" data-view="requirements">📋 Requirements</div>
698
+ <div class="nav-item" data-view="traceability">🔗 Traceability</div>
699
+ <div class="nav-item" data-view="replanning">🔄 Replanning</div>
700
+ <div class="nav-item" data-view="constitution">📜 Constitution</div>
701
+ </aside>
702
+
703
+ <section class="content" id="mainContent">
696
704
  <div class="loading">
697
705
  <div class="loading-spinner"></div>
706
+ Loading project data...
698
707
  </div>
699
- </div>
700
- </aside>
701
- </main>
702
-
703
- <!-- New Requirement Modal -->
704
- <div class="modal-overlay" id="requirementModal">
705
- <div class="modal">
706
- <h2>📝 New Requirement</h2>
707
- <form id="requirementForm" onsubmit="submitRequirement(event)">
708
- <div class="form-group">
709
- <label for="reqId">Requirement ID</label>
710
- <input type="text" id="reqId" placeholder="REQ-001" required pattern="REQ-\d{3}" title="Format: REQ-001">
711
- </div>
712
- <div class="form-group">
713
- <label for="reqTitle">Title</label>
714
- <input type="text" id="reqTitle" placeholder="User can login with email" required>
708
+ </section>
709
+
710
+ <aside class="panel">
711
+ <h3>📌 Quick Actions</h3>
712
+ <div style="margin-bottom: 1rem">
713
+ <button
714
+ class="btn btn-primary"
715
+ style="width: 100%; margin-bottom: 0.5rem"
716
+ onclick="openModal('requirementModal')"
717
+ >
718
+ New Requirement
719
+ </button>
720
+ <button
721
+ class="btn btn-outline"
722
+ style="width: 100%; margin-bottom: 0.5rem"
723
+ onclick="runQuickAction('validate')"
724
+ >
725
+ 🔍 Validate Project
726
+ </button>
727
+ <button
728
+ class="btn btn-outline"
729
+ style="width: 100%"
730
+ onclick="runQuickAction('export-report')"
731
+ >
732
+ 📤 Export Report
733
+ </button>
715
734
  </div>
716
- <div class="form-group">
717
- <label for="reqType">Type</label>
718
- <select id="reqType">
719
- <option value="functional">Functional</option>
720
- <option value="non-functional">Non-Functional</option>
721
- <option value="constraint">Constraint</option>
722
- <option value="interface">Interface</option>
723
- </select>
724
- </div>
725
- <div class="form-group">
726
- <label for="reqPriority">Priority</label>
727
- <select id="reqPriority">
728
- <option value="must">Must</option>
729
- <option value="should">Should</option>
730
- <option value="could">Could</option>
731
- <option value="wont">Won't</option>
732
- </select>
733
- </div>
734
- <div class="form-group">
735
- <label for="reqDescription">Description (EARS Format)</label>
736
- <textarea id="reqDescription" placeholder="When [trigger], the system shall [action]..." required></textarea>
737
- </div>
738
- <div class="form-group">
739
- <label for="reqFeature">Feature</label>
740
- <input type="text" id="reqFeature" placeholder="authentication" required>
741
- </div>
742
- <div class="modal-actions">
743
- <button type="button" class="btn btn-outline" onclick="closeModal('requirementModal')">Cancel</button>
744
- <button type="submit" class="btn btn-primary">Create Requirement</button>
735
+
736
+ <h3>📈 Coverage</h3>
737
+ <div id="coverageStats">
738
+ <div class="loading">
739
+ <div class="loading-spinner"></div>
740
+ </div>
745
741
  </div>
746
- </form>
742
+ </aside>
743
+ </main>
744
+
745
+ <!-- New Requirement Modal -->
746
+ <div class="modal-overlay" id="requirementModal">
747
+ <div class="modal">
748
+ <h2>📝 New Requirement</h2>
749
+ <form id="requirementForm" onsubmit="submitRequirement(event)">
750
+ <div class="form-group">
751
+ <label for="reqId">Requirement ID</label>
752
+ <input
753
+ type="text"
754
+ id="reqId"
755
+ placeholder="REQ-001"
756
+ required
757
+ pattern="REQ-\d{3}"
758
+ title="Format: REQ-001"
759
+ />
760
+ </div>
761
+ <div class="form-group">
762
+ <label for="reqTitle">Title</label>
763
+ <input type="text" id="reqTitle" placeholder="User can login with email" required />
764
+ </div>
765
+ <div class="form-group">
766
+ <label for="reqType">Type</label>
767
+ <select id="reqType">
768
+ <option value="functional">Functional</option>
769
+ <option value="non-functional">Non-Functional</option>
770
+ <option value="constraint">Constraint</option>
771
+ <option value="interface">Interface</option>
772
+ </select>
773
+ </div>
774
+ <div class="form-group">
775
+ <label for="reqPriority">Priority</label>
776
+ <select id="reqPriority">
777
+ <option value="must">Must</option>
778
+ <option value="should">Should</option>
779
+ <option value="could">Could</option>
780
+ <option value="wont">Won't</option>
781
+ </select>
782
+ </div>
783
+ <div class="form-group">
784
+ <label for="reqDescription">Description (EARS Format)</label>
785
+ <textarea
786
+ id="reqDescription"
787
+ placeholder="When [trigger], the system shall [action]..."
788
+ required
789
+ ></textarea>
790
+ </div>
791
+ <div class="form-group">
792
+ <label for="reqFeature">Feature</label>
793
+ <input type="text" id="reqFeature" placeholder="authentication" required />
794
+ </div>
795
+ <div class="modal-actions">
796
+ <button
797
+ type="button"
798
+ class="btn btn-outline"
799
+ onclick="closeModal('requirementModal')"
800
+ >
801
+ Cancel
802
+ </button>
803
+ <button type="submit" class="btn btn-primary">Create Requirement</button>
804
+ </div>
805
+ </form>
806
+ </div>
747
807
  </div>
808
+
809
+ <footer>MUSUBI SDD - Specification Driven Development Dashboard</footer>
748
810
  </div>
749
811
 
750
- <footer>
751
- MUSUBI SDD - Specification Driven Development Dashboard
752
- </footer>
753
- </div>
754
-
755
- <script>
756
- // State
757
- let project = null;
758
- let ws = null;
759
- let currentView = 'dashboard';
760
-
761
- // Modal functions
762
- function openModal(modalId) {
763
- document.getElementById(modalId).classList.add('active');
764
- }
765
-
766
- function closeModal(modalId) {
767
- document.getElementById(modalId).classList.remove('active');
768
- }
769
-
770
- async function submitRequirement(event) {
771
- event.preventDefault();
772
-
773
- const requirement = {
774
- id: document.getElementById('reqId').value,
775
- title: document.getElementById('reqTitle').value,
776
- type: document.getElementById('reqType').value,
777
- priority: document.getElementById('reqPriority').value,
778
- description: document.getElementById('reqDescription').value,
779
- feature: document.getElementById('reqFeature').value,
780
- };
781
-
782
- try {
783
- const response = await fetch('/api/requirements', {
784
- method: 'POST',
785
- headers: { 'Content-Type': 'application/json' },
786
- body: JSON.stringify(requirement)
787
- });
812
+ <script>
813
+ // State
814
+ let project = null;
815
+ let ws = null;
816
+ let currentView = 'dashboard';
788
817
 
789
- const result = await response.json();
790
-
791
- if (result.success) {
792
- showToast('✅ Requirement created successfully', 'success');
793
- closeModal('requirementModal');
794
- document.getElementById('requirementForm').reset();
795
- loadProject(); // Refresh project data
796
- } else {
797
- showToast(`❌ ${result.error || 'Failed to create requirement'}`, 'error');
818
+ // Modal functions
819
+ function openModal(modalId) {
820
+ document.getElementById(modalId).classList.add('active');
821
+ }
822
+
823
+ function closeModal(modalId) {
824
+ document.getElementById(modalId).classList.remove('active');
825
+ }
826
+
827
+ async function submitRequirement(event) {
828
+ event.preventDefault();
829
+
830
+ const requirement = {
831
+ id: document.getElementById('reqId').value,
832
+ title: document.getElementById('reqTitle').value,
833
+ type: document.getElementById('reqType').value,
834
+ priority: document.getElementById('reqPriority').value,
835
+ description: document.getElementById('reqDescription').value,
836
+ feature: document.getElementById('reqFeature').value,
837
+ };
838
+
839
+ try {
840
+ const response = await fetch('/api/requirements', {
841
+ method: 'POST',
842
+ headers: { 'Content-Type': 'application/json' },
843
+ body: JSON.stringify(requirement),
844
+ });
845
+
846
+ const result = await response.json();
847
+
848
+ if (result.success) {
849
+ showToast('✅ Requirement created successfully', 'success');
850
+ closeModal('requirementModal');
851
+ document.getElementById('requirementForm').reset();
852
+ loadProject(); // Refresh project data
853
+ } else {
854
+ showToast(`❌ ${result.error || 'Failed to create requirement'}`, 'error');
855
+ }
856
+ } catch (error) {
857
+ showToast(`❌ Error: ${error.message}`, 'error');
798
858
  }
799
- } catch (error) {
800
- showToast(`❌ Error: ${error.message}`, 'error');
801
- }
802
- }
803
-
804
- // Initialize
805
- document.addEventListener('DOMContentLoaded', () => {
806
- connectWebSocket();
807
- loadProject();
808
- setupNavigation();
809
- });
810
-
811
- // WebSocket connection
812
- function connectWebSocket() {
813
- const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
814
- ws = new WebSocket(`${protocol}//${location.host}`);
815
-
816
- ws.onopen = () => {
817
- document.getElementById('statusDot').classList.add('connected');
818
- document.getElementById('statusText').textContent = 'Connected';
819
- };
820
-
821
- ws.onclose = () => {
822
- document.getElementById('statusDot').classList.remove('connected');
823
- document.getElementById('statusText').textContent = 'Disconnected';
824
- setTimeout(connectWebSocket, 3000);
825
- };
826
-
827
- ws.onmessage = (event) => {
828
- const message = JSON.parse(event.data);
829
- handleWebSocketMessage(message);
830
- };
831
- }
832
-
833
- function handleWebSocketMessage(message) {
834
- switch (message.type) {
835
- case 'project:init':
836
- case 'file:changed':
837
- case 'file:added':
838
- case 'file:removed':
839
- project = message.data.project || message.data;
840
- renderCurrentView();
841
- loadCoverage();
842
- break;
843
- case 'replanning:state':
844
- handleReplanningStateUpdate(message.data);
845
- break;
846
- case 'replanning:event':
847
- handleReplanningEvent(message.data);
848
- break;
849
- }
850
- }
851
-
852
- // Replanning real-time handlers
853
- function handleReplanningStateUpdate(state) {
854
- // Update replanning panel if visible
855
- const replanPanel = document.querySelector('.replan-status');
856
- if (replanPanel) {
857
- const statusText = replanPanel.querySelector('.status-text');
858
- if (statusText) {
859
- statusText.textContent = state.status.charAt(0).toUpperCase() + state.status.slice(1);
859
+ }
860
+
861
+ // Initialize
862
+ document.addEventListener('DOMContentLoaded', () => {
863
+ connectWebSocket();
864
+ loadProject();
865
+ setupNavigation();
866
+ });
867
+
868
+ // WebSocket connection
869
+ function connectWebSocket() {
870
+ const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
871
+ ws = new WebSocket(`${protocol}//${location.host}`);
872
+
873
+ ws.onopen = () => {
874
+ document.getElementById('statusDot').classList.add('connected');
875
+ document.getElementById('statusText').textContent = 'Connected';
876
+ };
877
+
878
+ ws.onclose = () => {
879
+ document.getElementById('statusDot').classList.remove('connected');
880
+ document.getElementById('statusText').textContent = 'Disconnected';
881
+ setTimeout(connectWebSocket, 3000);
882
+ };
883
+
884
+ ws.onmessage = event => {
885
+ const message = JSON.parse(event.data);
886
+ handleWebSocketMessage(message);
887
+ };
888
+ }
889
+
890
+ function handleWebSocketMessage(message) {
891
+ switch (message.type) {
892
+ case 'project:init':
893
+ case 'file:changed':
894
+ case 'file:added':
895
+ case 'file:removed':
896
+ project = message.data.project || message.data;
897
+ renderCurrentView();
898
+ loadCoverage();
899
+ break;
900
+ case 'replanning:state':
901
+ handleReplanningStateUpdate(message.data);
902
+ break;
903
+ case 'replanning:event':
904
+ handleReplanningEvent(message.data);
905
+ break;
906
+ }
907
+ }
908
+
909
+ // Replanning real-time handlers
910
+ function handleReplanningStateUpdate(state) {
911
+ // Update replanning panel if visible
912
+ const replanPanel = document.querySelector('.replan-status');
913
+ if (replanPanel) {
914
+ const statusText = replanPanel.querySelector('.status-text');
915
+ if (statusText) {
916
+ statusText.textContent = state.status.charAt(0).toUpperCase() + state.status.slice(1);
917
+ }
918
+ // Update status indicator class
919
+ replanPanel.className = 'replan-status ' + state.status;
920
+ }
921
+ // Show toast notification for status changes
922
+ if (state.status === 'replanning') {
923
+ showToast('Replanning in progress...', 'warning');
924
+ } else if (state.status === 'optimizing') {
925
+ showToast('Optimizing execution path...', 'info');
926
+ }
927
+ // Refresh replanning view if active
928
+ if (currentView === 'replanning') {
929
+ renderReplanningView();
930
+ }
931
+ }
932
+
933
+ function handleReplanningEvent(event) {
934
+ // Show notification for replan events
935
+ const message = event.success
936
+ ? `Replan completed: ${event.reason || 'Optimization applied'}`
937
+ : `Replan failed: ${event.error || 'Unknown error'}`;
938
+ showToast(message, event.success ? 'success' : 'error');
939
+ // Refresh replanning view if active
940
+ if (currentView === 'replanning') {
941
+ renderReplanningView();
860
942
  }
861
- // Update status indicator class
862
- replanPanel.className = 'replan-status ' + state.status;
863
- }
864
- // Show toast notification for status changes
865
- if (state.status === 'replanning') {
866
- showToast('Replanning in progress...', 'warning');
867
- } else if (state.status === 'optimizing') {
868
- showToast('Optimizing execution path...', 'info');
869
- }
870
- // Refresh replanning view if active
871
- if (currentView === 'replanning') {
872
- renderReplanningView();
873
- }
874
- }
875
-
876
- function handleReplanningEvent(event) {
877
- // Show notification for replan events
878
- const message = event.success
879
- ? `Replan completed: ${event.reason || 'Optimization applied'}`
880
- : `Replan failed: ${event.error || 'Unknown error'}`;
881
- showToast(message, event.success ? 'success' : 'error');
882
- // Refresh replanning view if active
883
- if (currentView === 'replanning') {
884
- renderReplanningView();
885
- }
886
- }
887
-
888
- function showToast(message, type = 'info') {
889
- const toast = document.createElement('div');
890
- toast.className = `toast toast-${type}`;
891
- toast.textContent = message;
892
- toast.style.cssText = `
943
+ }
944
+
945
+ function showToast(message, type = 'info') {
946
+ const toast = document.createElement('div');
947
+ toast.className = `toast toast-${type}`;
948
+ toast.textContent = message;
949
+ toast.style.cssText = `
893
950
  position: fixed;
894
951
  bottom: 20px;
895
952
  right: 20px;
@@ -901,161 +958,164 @@
901
958
  animation: slideIn 0.3s ease;
902
959
  background: ${type === 'success' ? 'var(--success)' : type === 'error' ? 'var(--error)' : type === 'warning' ? 'var(--warning)' : 'var(--secondary)'};
903
960
  `;
904
- document.body.appendChild(toast);
905
- setTimeout(() => {
906
- toast.style.animation = 'slideOut 0.3s ease';
907
- setTimeout(() => toast.remove(), 300);
908
- }, 3000);
909
- }
910
-
911
- // API calls
912
- async function loadProject() {
913
- try {
914
- const response = await fetch('/api/project');
915
- project = await response.json();
916
- renderCurrentView();
917
- } catch (error) {
918
- console.error('Failed to load project:', error);
919
- }
920
- }
921
-
922
- async function loadCoverage() {
923
- try {
924
- const response = await fetch('/api/coverage');
925
- const coverage = await response.json();
926
- renderCoverage(coverage);
927
- } catch (error) {
928
- console.error('Failed to load coverage:', error);
929
- }
930
- }
931
-
932
- async function loadTraceability() {
933
- try {
934
- const response = await fetch('/api/traceability');
935
- return await response.json();
936
- } catch (error) {
937
- console.error('Failed to load traceability:', error);
938
- return { requirements: [], traces: [] };
939
- }
940
- }
941
-
942
- async function loadConstitution() {
943
- try {
944
- const response = await fetch('/api/constitution');
945
- return await response.json();
946
- } catch (error) {
947
- console.error('Failed to load constitution:', error);
948
- return null;
949
- }
950
- }
951
-
952
- async function loadWorkflow() {
953
- try {
954
- const response = await fetch('/api/workflow');
955
- return await response.json();
956
- } catch (error) {
957
- console.error('Failed to load workflow:', error);
958
- return null;
959
- }
960
- }
961
-
962
- async function loadReplanning() {
963
- try {
964
- const response = await fetch('/api/replanning/summary');
965
- return await response.json();
966
- } catch (error) {
967
- console.error('Failed to load replanning:', error);
968
- return null;
969
- }
970
- }
971
-
972
- async function loadReplanningGoals() {
973
- try {
974
- const response = await fetch('/api/replanning/goals');
975
- return await response.json();
976
- } catch (error) {
977
- console.error('Failed to load replanning goals:', error);
978
- return { goals: [], overallProgress: 0 };
979
- }
980
- }
981
-
982
- async function loadReplanningHistory() {
983
- try {
984
- const response = await fetch('/api/replanning/history?limit=10');
985
- return await response.json();
986
- } catch (error) {
987
- console.error('Failed to load replanning history:', error);
988
- return [];
989
- }
990
- }
991
-
992
- async function loadReplanningOptimization() {
993
- try {
994
- const response = await fetch('/api/replanning/optimization');
995
- return await response.json();
996
- } catch (error) {
997
- console.error('Failed to load optimization:', error);
998
- return { status: 'idle', suggestions: [] };
999
- }
1000
- }
1001
-
1002
- // Navigation
1003
- function setupNavigation() {
1004
- document.querySelectorAll('.nav-item').forEach(item => {
1005
- item.addEventListener('click', () => {
1006
- const view = item.dataset.view;
1007
- setActiveView(view);
961
+ document.body.appendChild(toast);
962
+ setTimeout(() => {
963
+ toast.style.animation = 'slideOut 0.3s ease';
964
+ setTimeout(() => toast.remove(), 300);
965
+ }, 3000);
966
+ }
967
+
968
+ // API calls
969
+ async function loadProject() {
970
+ try {
971
+ const response = await fetch('/api/project');
972
+ project = await response.json();
973
+ renderCurrentView();
974
+ } catch (error) {
975
+ console.error('Failed to load project:', error);
976
+ }
977
+ }
978
+
979
+ async function loadCoverage() {
980
+ try {
981
+ const response = await fetch('/api/coverage');
982
+ const coverage = await response.json();
983
+ renderCoverage(coverage);
984
+ } catch (error) {
985
+ console.error('Failed to load coverage:', error);
986
+ }
987
+ }
988
+
989
+ async function loadTraceability() {
990
+ try {
991
+ const response = await fetch('/api/traceability');
992
+ return await response.json();
993
+ } catch (error) {
994
+ console.error('Failed to load traceability:', error);
995
+ return { requirements: [], traces: [] };
996
+ }
997
+ }
998
+
999
+ async function loadConstitution() {
1000
+ try {
1001
+ const response = await fetch('/api/constitution');
1002
+ return await response.json();
1003
+ } catch (error) {
1004
+ console.error('Failed to load constitution:', error);
1005
+ return null;
1006
+ }
1007
+ }
1008
+
1009
+ async function loadWorkflow() {
1010
+ try {
1011
+ const response = await fetch('/api/workflow');
1012
+ return await response.json();
1013
+ } catch (error) {
1014
+ console.error('Failed to load workflow:', error);
1015
+ return null;
1016
+ }
1017
+ }
1018
+
1019
+ async function loadReplanning() {
1020
+ try {
1021
+ const response = await fetch('/api/replanning/summary');
1022
+ return await response.json();
1023
+ } catch (error) {
1024
+ console.error('Failed to load replanning:', error);
1025
+ return null;
1026
+ }
1027
+ }
1028
+
1029
+ async function loadReplanningGoals() {
1030
+ try {
1031
+ const response = await fetch('/api/replanning/goals');
1032
+ return await response.json();
1033
+ } catch (error) {
1034
+ console.error('Failed to load replanning goals:', error);
1035
+ return { goals: [], overallProgress: 0 };
1036
+ }
1037
+ }
1038
+
1039
+ async function loadReplanningHistory() {
1040
+ try {
1041
+ const response = await fetch('/api/replanning/history?limit=10');
1042
+ return await response.json();
1043
+ } catch (error) {
1044
+ console.error('Failed to load replanning history:', error);
1045
+ return [];
1046
+ }
1047
+ }
1048
+
1049
+ async function loadReplanningOptimization() {
1050
+ try {
1051
+ const response = await fetch('/api/replanning/optimization');
1052
+ return await response.json();
1053
+ } catch (error) {
1054
+ console.error('Failed to load optimization:', error);
1055
+ return { status: 'idle', suggestions: [] };
1056
+ }
1057
+ }
1058
+
1059
+ // Navigation
1060
+ function setupNavigation() {
1061
+ document.querySelectorAll('.nav-item').forEach(item => {
1062
+ item.addEventListener('click', () => {
1063
+ const view = item.dataset.view;
1064
+ setActiveView(view);
1065
+ });
1008
1066
  });
1009
- });
1010
- }
1067
+ }
1011
1068
 
1012
- function setActiveView(view) {
1013
- currentView = view;
1014
- document.querySelectorAll('.nav-item').forEach(item => {
1015
- item.classList.toggle('active', item.dataset.view === view);
1016
- });
1017
- renderCurrentView();
1018
- }
1019
-
1020
- // Render functions
1021
- function renderCurrentView() {
1022
- const content = document.getElementById('mainContent');
1023
-
1024
- switch (currentView) {
1025
- case 'dashboard':
1026
- renderDashboard(content);
1027
- break;
1028
- case 'workflow':
1029
- renderWorkflow(content);
1030
- break;
1031
- case 'requirements':
1032
- renderRequirements(content);
1033
- break;
1034
- case 'traceability':
1035
- renderTraceability(content);
1036
- break;
1037
- case 'replanning':
1038
- renderReplanning(content);
1039
- break;
1040
- case 'constitution':
1041
- renderConstitution(content);
1042
- break;
1043
- }
1044
- }
1045
-
1046
- function renderDashboard(container) {
1047
- if (!project) {
1048
- container.innerHTML = '<div class="loading"><div class="loading-spinner"></div>Loading...</div>';
1049
- return;
1050
- }
1051
-
1052
- const specs = project.specs || [];
1053
- const totalReqs = specs.reduce((sum, s) => sum + (s.requirements?.length || 0), 0);
1054
- const totalTasks = specs.reduce((sum, s) => sum + (s.tasks?.length || 0), 0);
1055
- const completedTasks = specs.reduce((sum, s) =>
1056
- sum + (s.tasks?.filter(t => t.completed)?.length || 0), 0);
1057
-
1058
- container.innerHTML = `
1069
+ function setActiveView(view) {
1070
+ currentView = view;
1071
+ document.querySelectorAll('.nav-item').forEach(item => {
1072
+ item.classList.toggle('active', item.dataset.view === view);
1073
+ });
1074
+ renderCurrentView();
1075
+ }
1076
+
1077
+ // Render functions
1078
+ function renderCurrentView() {
1079
+ const content = document.getElementById('mainContent');
1080
+
1081
+ switch (currentView) {
1082
+ case 'dashboard':
1083
+ renderDashboard(content);
1084
+ break;
1085
+ case 'workflow':
1086
+ renderWorkflow(content);
1087
+ break;
1088
+ case 'requirements':
1089
+ renderRequirements(content);
1090
+ break;
1091
+ case 'traceability':
1092
+ renderTraceability(content);
1093
+ break;
1094
+ case 'replanning':
1095
+ renderReplanning(content);
1096
+ break;
1097
+ case 'constitution':
1098
+ renderConstitution(content);
1099
+ break;
1100
+ }
1101
+ }
1102
+
1103
+ function renderDashboard(container) {
1104
+ if (!project) {
1105
+ container.innerHTML =
1106
+ '<div class="loading"><div class="loading-spinner"></div>Loading...</div>';
1107
+ return;
1108
+ }
1109
+
1110
+ const specs = project.specs || [];
1111
+ const totalReqs = specs.reduce((sum, s) => sum + (s.requirements?.length || 0), 0);
1112
+ const totalTasks = specs.reduce((sum, s) => sum + (s.tasks?.length || 0), 0);
1113
+ const completedTasks = specs.reduce(
1114
+ (sum, s) => sum + (s.tasks?.filter(t => t.completed)?.length || 0),
1115
+ 0
1116
+ );
1117
+
1118
+ container.innerHTML = `
1059
1119
  <h2 style="margin-bottom: 1.5rem;">📊 Project Dashboard</h2>
1060
1120
 
1061
1121
  <div class="stats-grid">
@@ -1074,7 +1134,7 @@
1074
1134
  <div class="stat-card">
1075
1135
  <div class="label">Tasks</div>
1076
1136
  <div class="value">${completedTasks}/${totalTasks}</div>
1077
- <div class="change">${totalTasks > 0 ? Math.round(completedTasks/totalTasks*100) : 0}% complete</div>
1137
+ <div class="change">${totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0}% complete</div>
1078
1138
  </div>
1079
1139
  </div>
1080
1140
 
@@ -1084,204 +1144,255 @@
1084
1144
  ${project.hasSteering ? '✅ Steering configured' : '⚠️ No steering directory'}
1085
1145
  ${project.hasSpecs ? ' • ✅ Specs available' : ' • ⚠️ No specs yet'}
1086
1146
  </p>
1087
- ${project.constitution ? `
1147
+ ${
1148
+ project.constitution
1149
+ ? `
1088
1150
  <p style="color: var(--success);">
1089
1151
  📜 Constitution: ${project.constitution.articles?.length || 0} articles
1090
1152
  </p>
1091
- ` : ''}
1153
+ `
1154
+ : ''
1155
+ }
1092
1156
  </div>
1093
1157
 
1094
- ${specs.length > 0 ? `
1158
+ ${
1159
+ specs.length > 0
1160
+ ? `
1095
1161
  <div class="card">
1096
1162
  <h3>📁 Recent Specifications</h3>
1097
1163
  <ul class="requirements-list">
1098
- ${specs.slice(0, 5).map(spec => `
1164
+ ${specs
1165
+ .slice(0, 5)
1166
+ .map(
1167
+ spec => `
1099
1168
  <li class="requirement-item">
1100
1169
  <span class="req-id">${spec.id}</span>
1101
1170
  <span class="req-title">${spec.title || spec.id}</span>
1102
1171
  <span class="req-status designed">${spec.requirements?.length || 0} reqs</span>
1103
1172
  </li>
1104
- `).join('')}
1173
+ `
1174
+ )
1175
+ .join('')}
1105
1176
  </ul>
1106
1177
  </div>
1107
- ` : ''}
1178
+ `
1179
+ : ''
1180
+ }
1108
1181
  `;
1109
- }
1110
-
1111
- async function renderWorkflow(container) {
1112
- container.innerHTML = '<div class="loading"><div class="loading-spinner"></div>Loading workflow...</div>';
1113
-
1114
- const workflow = await loadWorkflow();
1115
-
1116
- if (!workflow || !workflow.stages) {
1117
- container.innerHTML = `
1182
+ }
1183
+
1184
+ async function renderWorkflow(container) {
1185
+ container.innerHTML =
1186
+ '<div class="loading"><div class="loading-spinner"></div>Loading workflow...</div>';
1187
+
1188
+ const workflow = await loadWorkflow();
1189
+
1190
+ if (!workflow || !workflow.stages) {
1191
+ container.innerHTML = `
1118
1192
  <h2 style="margin-bottom: 1.5rem;">🔄 Workflow</h2>
1119
1193
  <div class="empty-state">
1120
1194
  <h4>No workflow defined</h4>
1121
1195
  <p>Create a workflow configuration in steering/rules/workflow.md</p>
1122
1196
  </div>
1123
1197
  `;
1124
- return;
1125
- }
1198
+ return;
1199
+ }
1126
1200
 
1127
- container.innerHTML = `
1201
+ container.innerHTML = `
1128
1202
  <h2 style="margin-bottom: 1.5rem;">🔄 Workflow</h2>
1129
1203
 
1130
1204
  <div class="card">
1131
1205
  <h3>SDD Stages</h3>
1132
- ${workflow.stages.map((stage, i) => `
1206
+ ${workflow.stages
1207
+ .map(
1208
+ (stage, i) => `
1133
1209
  <div class="workflow-stage ${stage.status}">
1134
1210
  <span class="stage-number">${i + 1}</span>
1135
1211
  <span>${stage.name}</span>
1136
1212
  </div>
1137
- `).join('')}
1213
+ `
1214
+ )
1215
+ .join('')}
1138
1216
  </div>
1139
1217
  `;
1140
- }
1141
-
1142
- async function renderRequirements(container) {
1143
- if (!project || !project.specs) {
1144
- container.innerHTML = '<div class="loading"><div class="loading-spinner"></div>Loading...</div>';
1145
- return;
1146
1218
  }
1147
1219
 
1148
- const allReqs = project.specs.flatMap(spec =>
1149
- (spec.requirements || []).map(req => ({
1150
- ...req,
1151
- specId: spec.id
1152
- }))
1153
- );
1220
+ async function renderRequirements(container) {
1221
+ if (!project || !project.specs) {
1222
+ container.innerHTML =
1223
+ '<div class="loading"><div class="loading-spinner"></div>Loading...</div>';
1224
+ return;
1225
+ }
1226
+
1227
+ const allReqs = project.specs.flatMap(spec =>
1228
+ (spec.requirements || []).map(req => ({
1229
+ ...req,
1230
+ specId: spec.id,
1231
+ }))
1232
+ );
1154
1233
 
1155
- container.innerHTML = `
1234
+ container.innerHTML = `
1156
1235
  <h2 style="margin-bottom: 1.5rem;">📋 Requirements</h2>
1157
1236
 
1158
- ${allReqs.length > 0 ? `
1237
+ ${
1238
+ allReqs.length > 0
1239
+ ? `
1159
1240
  <div class="card">
1160
1241
  <h3>All Requirements (${allReqs.length})</h3>
1161
1242
  <ul class="requirements-list">
1162
- ${allReqs.map(req => `
1243
+ ${allReqs
1244
+ .map(
1245
+ req => `
1163
1246
  <li class="requirement-item">
1164
1247
  <span class="req-id">${req.id}</span>
1165
1248
  <span class="req-title" title="${req.body || ''}">${req.body?.substring(0, 80) || req.id}...</span>
1166
1249
  <span class="req-status designed">${req.pattern}</span>
1167
1250
  </li>
1168
- `).join('')}
1251
+ `
1252
+ )
1253
+ .join('')}
1169
1254
  </ul>
1170
1255
  </div>
1171
- ` : `
1256
+ `
1257
+ : `
1172
1258
  <div class="empty-state">
1173
1259
  <h4>No Requirements</h4>
1174
1260
  <p>Create requirements using musubi-requirements command</p>
1175
1261
  </div>
1176
- `}
1262
+ `
1263
+ }
1177
1264
  `;
1178
- }
1179
-
1180
- async function renderTraceability(container) {
1181
- container.innerHTML = '<div class="loading"><div class="loading-spinner"></div>Loading traceability...</div>';
1182
-
1183
- const matrix = await loadTraceability();
1184
-
1185
- container.innerHTML = `
1265
+ }
1266
+
1267
+ async function renderTraceability(container) {
1268
+ container.innerHTML =
1269
+ '<div class="loading"><div class="loading-spinner"></div>Loading traceability...</div>';
1270
+
1271
+ const matrix = await loadTraceability();
1272
+
1273
+ container.innerHTML = `
1186
1274
  <h2 style="margin-bottom: 1.5rem;">🔗 Traceability Matrix</h2>
1187
1275
 
1188
1276
  <div class="card">
1189
1277
  <h3>Requirements → Implementation</h3>
1190
- ${matrix.requirements?.length > 0 ? `
1278
+ ${
1279
+ matrix.requirements?.length > 0
1280
+ ? `
1191
1281
  <ul class="requirements-list">
1192
- ${matrix.requirements.map(req => `
1282
+ ${matrix.requirements
1283
+ .map(
1284
+ req => `
1193
1285
  <li class="requirement-item">
1194
1286
  <span class="req-id">${req.id}</span>
1195
1287
  <span class="req-title">${req.specTitle || ''}</span>
1196
1288
  <span class="req-status ${req.status || 'unlinked'}">${req.status || 'unlinked'}</span>
1197
1289
  </li>
1198
- `).join('')}
1290
+ `
1291
+ )
1292
+ .join('')}
1199
1293
  </ul>
1200
- ` : `
1294
+ `
1295
+ : `
1201
1296
  <div class="empty-state">
1202
1297
  <p>No requirements to trace</p>
1203
1298
  </div>
1204
- `}
1299
+ `
1300
+ }
1205
1301
  </div>
1206
1302
 
1207
1303
  <div class="card">
1208
1304
  <h3>📊 Trace Links (${matrix.traces?.length || 0})</h3>
1209
- ${matrix.traces?.length > 0 ? `
1305
+ ${
1306
+ matrix.traces?.length > 0
1307
+ ? `
1210
1308
  <ul class="requirements-list">
1211
- ${matrix.traces.slice(0, 10).map(trace => `
1309
+ ${matrix.traces
1310
+ .slice(0, 10)
1311
+ .map(
1312
+ trace => `
1212
1313
  <li class="requirement-item">
1213
1314
  <span class="req-id">${trace.from}</span>
1214
1315
  <span style="color: var(--text-muted);">→</span>
1215
1316
  <span class="req-id" style="background: var(--secondary);">${trace.to}</span>
1216
1317
  <span class="req-title">${trace.type}</span>
1217
1318
  </li>
1218
- `).join('')}
1319
+ `
1320
+ )
1321
+ .join('')}
1219
1322
  </ul>
1220
- ` : '<p style="color: var(--text-muted); padding: 1rem;">No trace links found</p>'}
1323
+ `
1324
+ : '<p style="color: var(--text-muted); padding: 1rem;">No trace links found</p>'
1325
+ }
1221
1326
  </div>
1222
1327
  `;
1223
- }
1224
-
1225
- async function renderConstitution(container) {
1226
- container.innerHTML = '<div class="loading"><div class="loading-spinner"></div>Loading constitution...</div>';
1227
-
1228
- const constitution = await loadConstitution();
1229
-
1230
- if (!constitution || !constitution.articles) {
1231
- container.innerHTML = `
1328
+ }
1329
+
1330
+ async function renderConstitution(container) {
1331
+ container.innerHTML =
1332
+ '<div class="loading"><div class="loading-spinner"></div>Loading constitution...</div>';
1333
+
1334
+ const constitution = await loadConstitution();
1335
+
1336
+ if (!constitution || !constitution.articles) {
1337
+ container.innerHTML = `
1232
1338
  <h2 style="margin-bottom: 1.5rem;">📜 Constitution</h2>
1233
1339
  <div class="empty-state">
1234
1340
  <h4>No Constitution</h4>
1235
1341
  <p>Create a constitution in steering/rules/constitution.md</p>
1236
1342
  </div>
1237
1343
  `;
1238
- return;
1239
- }
1344
+ return;
1345
+ }
1240
1346
 
1241
- container.innerHTML = `
1347
+ container.innerHTML = `
1242
1348
  <h2 style="margin-bottom: 1.5rem;">📜 Constitution</h2>
1243
1349
 
1244
1350
  <div class="card">
1245
1351
  <h3>Articles (${constitution.articles.length})</h3>
1246
- ${constitution.articles.map(article => `
1352
+ ${constitution.articles
1353
+ .map(
1354
+ article => `
1247
1355
  <div class="constitution-article">
1248
1356
  <div class="article-number">Article ${article.number}</div>
1249
1357
  <div>${article.title}</div>
1250
1358
  </div>
1251
- `).join('')}
1359
+ `
1360
+ )
1361
+ .join('')}
1252
1362
  </div>
1253
1363
  `;
1254
- }
1255
-
1256
- async function renderReplanning(container) {
1257
- container.innerHTML = '<div class="loading"><div class="loading-spinner"></div>Loading replanning state...</div>';
1258
-
1259
- const [summary, goals, history, optimization] = await Promise.all([
1260
- loadReplanning(),
1261
- loadReplanningGoals(),
1262
- loadReplanningHistory(),
1263
- loadReplanningOptimization()
1264
- ]);
1265
-
1266
- const statusLabels = {
1267
- idle: 'Idle',
1268
- monitoring: 'Monitoring',
1269
- evaluating: 'Evaluating',
1270
- replanning: 'Replanning',
1271
- optimizing: 'Optimizing'
1272
- };
1273
-
1274
- const statusIcons = {
1275
- idle: '⏸️',
1276
- monitoring: '👁️',
1277
- evaluating: '🔍',
1278
- replanning: '🔄',
1279
- optimizing: ''
1280
- };
1281
-
1282
- const status = summary?.status || 'idle';
1283
-
1284
- container.innerHTML = `
1364
+ }
1365
+
1366
+ async function renderReplanning(container) {
1367
+ container.innerHTML =
1368
+ '<div class="loading"><div class="loading-spinner"></div>Loading replanning state...</div>';
1369
+
1370
+ const [summary, goals, history, optimization] = await Promise.all([
1371
+ loadReplanning(),
1372
+ loadReplanningGoals(),
1373
+ loadReplanningHistory(),
1374
+ loadReplanningOptimization(),
1375
+ ]);
1376
+
1377
+ const statusLabels = {
1378
+ idle: 'Idle',
1379
+ monitoring: 'Monitoring',
1380
+ evaluating: 'Evaluating',
1381
+ replanning: 'Replanning',
1382
+ optimizing: 'Optimizing',
1383
+ };
1384
+
1385
+ const statusIcons = {
1386
+ idle: '⏸️',
1387
+ monitoring: '👁️',
1388
+ evaluating: '🔍',
1389
+ replanning: '🔄',
1390
+ optimizing: '⚡',
1391
+ };
1392
+
1393
+ const status = summary?.status || 'idle';
1394
+
1395
+ container.innerHTML = `
1285
1396
  <h2 style="margin-bottom: 1.5rem;">🔄 Replanning Engine</h2>
1286
1397
 
1287
1398
  <!-- Status Card -->
@@ -1316,10 +1427,14 @@
1316
1427
  <div class="card">
1317
1428
  <h3>🎯 Goal Progress</h3>
1318
1429
  <div class="goal-progress">
1319
- ${goals.goals?.length > 0 ? goals.goals.slice(0, 5).map(goal => {
1320
- const progress = goal.progress || 0;
1321
- const level = progress < 33 ? 'low' : progress < 66 ? 'medium' : 'high';
1322
- return `
1430
+ ${
1431
+ goals.goals?.length > 0
1432
+ ? goals.goals
1433
+ .slice(0, 5)
1434
+ .map(goal => {
1435
+ const progress = goal.progress || 0;
1436
+ const level = progress < 33 ? 'low' : progress < 66 ? 'medium' : 'high';
1437
+ return `
1323
1438
  <div class="goal-item">
1324
1439
  <span style="flex-shrink: 0; width: 80px; font-size: 0.875rem;">${goal.id || 'Goal'}</span>
1325
1440
  <div class="goal-progress-bar">
@@ -1328,14 +1443,19 @@
1328
1443
  <span style="flex-shrink: 0; width: 40px; text-align: right; font-size: 0.875rem;">${progress}%</span>
1329
1444
  </div>
1330
1445
  `;
1331
- }).join('') : `
1446
+ })
1447
+ .join('')
1448
+ : `
1332
1449
  <div style="color: var(--text-muted); padding: 1rem; text-align: center;">
1333
1450
  <p>No goals registered</p>
1334
1451
  <p style="font-size: 0.875rem; margin-top: 0.5rem;">Use <code>npx musubi-orchestrate goal register</code></p>
1335
1452
  </div>
1336
- `}
1453
+ `
1454
+ }
1337
1455
  </div>
1338
- ${goals.goals?.length > 0 ? `
1456
+ ${
1457
+ goals.goals?.length > 0
1458
+ ? `
1339
1459
  <div class="progress-bar" style="margin-top: 1rem;">
1340
1460
  <div class="progress-bar-fill" style="width: ${goals.overallProgress || 0}%"></div>
1341
1461
  </div>
@@ -1343,7 +1463,9 @@
1343
1463
  <span>Active: ${summary?.goalProgress?.active || 0}</span>
1344
1464
  <span>Completed: ${summary?.goalProgress?.completed || 0}</span>
1345
1465
  </div>
1346
- ` : ''}
1466
+ `
1467
+ : ''
1468
+ }
1347
1469
  </div>
1348
1470
 
1349
1471
  <!-- Path Optimization -->
@@ -1354,24 +1476,37 @@
1354
1476
  <span class="status-indicator ${optimization.status || 'idle'}"></span>
1355
1477
  <span style="font-weight: 500;">${optimization.status === 'active' ? 'Optimization Active' : 'Awaiting Optimization'}</span>
1356
1478
  </div>
1357
- ${optimization.potentialSavings ? `
1479
+ ${
1480
+ optimization.potentialSavings
1481
+ ? `
1358
1482
  <div style="color: var(--success); font-size: 0.875rem;">
1359
1483
  💡 Potential savings: ${optimization.potentialSavings}%
1360
1484
  </div>
1361
- ` : ''}
1485
+ `
1486
+ : ''
1487
+ }
1362
1488
  </div>
1363
1489
 
1364
1490
  <h4 style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 0.5rem;">Suggestions</h4>
1365
- ${optimization.suggestions?.length > 0 ? optimization.suggestions.slice(0, 3).map(sug => `
1491
+ ${
1492
+ optimization.suggestions?.length > 0
1493
+ ? optimization.suggestions
1494
+ .slice(0, 3)
1495
+ .map(
1496
+ sug => `
1366
1497
  <div class="optimization-suggestion">
1367
1498
  <span>💡</span>
1368
1499
  <span style="font-size: 0.875rem;">${sug.description || sug}</span>
1369
1500
  </div>
1370
- `).join('') : `
1501
+ `
1502
+ )
1503
+ .join('')
1504
+ : `
1371
1505
  <div style="color: var(--text-muted); padding: 0.5rem; text-align: center; font-size: 0.875rem;">
1372
1506
  No optimization suggestions
1373
1507
  </div>
1374
- `}
1508
+ `
1509
+ }
1375
1510
  <div style="margin-top: 1rem;">
1376
1511
  <code style="font-size: 0.75rem; color: var(--text-muted);">npx musubi-orchestrate optimize run</code>
1377
1512
  </div>
@@ -1381,13 +1516,23 @@
1381
1516
  <!-- Replan History -->
1382
1517
  <div class="card" style="margin-top: 1rem;">
1383
1518
  <h3>📜 Recent Replan History</h3>
1384
- ${history?.length > 0 ? `
1519
+ ${
1520
+ history?.length > 0
1521
+ ? `
1385
1522
  <div class="history-timeline">
1386
- ${history.slice(0, 8).map(event => {
1387
- const eventClass = event.success === false ? 'error' :
1388
- event.trigger === 'warning' ? 'warning' : 'success';
1389
- const time = event.timestamp ? new Date(event.timestamp).toLocaleString('ja-JP') : 'Unknown';
1390
- return `
1523
+ ${history
1524
+ .slice(0, 8)
1525
+ .map(event => {
1526
+ const eventClass =
1527
+ event.success === false
1528
+ ? 'error'
1529
+ : event.trigger === 'warning'
1530
+ ? 'warning'
1531
+ : 'success';
1532
+ const time = event.timestamp
1533
+ ? new Date(event.timestamp).toLocaleString('ja-JP')
1534
+ : 'Unknown';
1535
+ return `
1391
1536
  <div class="history-item ${eventClass}">
1392
1537
  <div style="display: flex; justify-content: space-between; margin-bottom: 0.25rem;">
1393
1538
  <span style="font-weight: 500;">${event.trigger || event.type || 'Replan'}</span>
@@ -1396,21 +1541,28 @@
1396
1541
  <div style="font-size: 0.875rem; color: var(--text-muted);">
1397
1542
  ${event.reason || event.description || 'Plan adjusted'}
1398
1543
  </div>
1399
- ${event.confidence ? `
1544
+ ${
1545
+ event.confidence
1546
+ ? `
1400
1547
  <div style="font-size: 0.75rem; color: var(--primary); margin-top: 0.25rem;">
1401
1548
  Confidence: ${Math.round(event.confidence * 100)}%
1402
1549
  </div>
1403
- ` : ''}
1550
+ `
1551
+ : ''
1552
+ }
1404
1553
  </div>
1405
1554
  `;
1406
- }).join('')}
1555
+ })
1556
+ .join('')}
1407
1557
  </div>
1408
- ` : `
1558
+ `
1559
+ : `
1409
1560
  <div style="color: var(--text-muted); padding: 1rem; text-align: center;">
1410
1561
  <p>No replan history yet</p>
1411
1562
  <p style="font-size: 0.875rem; margin-top: 0.5rem;">Replanning events will appear here</p>
1412
1563
  </div>
1413
- `}
1564
+ `
1565
+ }
1414
1566
  </div>
1415
1567
 
1416
1568
  <!-- CLI Commands Reference -->
@@ -1436,12 +1588,12 @@
1436
1588
  </div>
1437
1589
  </div>
1438
1590
  `;
1439
- }
1591
+ }
1592
+
1593
+ function renderCoverage(coverage) {
1594
+ const container = document.getElementById('coverageStats');
1440
1595
 
1441
- function renderCoverage(coverage) {
1442
- const container = document.getElementById('coverageStats');
1443
-
1444
- container.innerHTML = `
1596
+ container.innerHTML = `
1445
1597
  <div style="margin-bottom: 1rem;">
1446
1598
  <div style="display: flex; justify-content: space-between; margin-bottom: 0.5rem;">
1447
1599
  <span>Total</span>
@@ -1460,64 +1612,66 @@
1460
1612
  </div>
1461
1613
  </div>
1462
1614
  `;
1463
- }
1464
-
1465
- // Quick Actions
1466
- async function runQuickAction(action) {
1467
- const button = event.target;
1468
- const originalText = button.innerHTML;
1469
- button.disabled = true;
1470
- button.innerHTML = '⏳ Processing...';
1471
-
1472
- try {
1473
- const response = await fetch(`/api/actions/${action}`, {
1474
- method: 'POST',
1475
- headers: { 'Content-Type': 'application/json' }
1476
- });
1477
- const result = await response.json();
1615
+ }
1478
1616
 
1479
- if (action === 'validate') {
1480
- if (result.success) {
1481
- showToast('✅ Validation passed', 'success');
1482
- } else {
1483
- showToast('⚠️ Validation completed with issues', 'warning');
1484
- }
1485
- // Show output in console and optionally in a modal
1486
- if (result.output) {
1487
- console.log('Validation output:', result.output);
1488
- }
1489
- if (result.errors) {
1490
- console.log('Validation errors:', result.errors);
1491
- }
1492
- } else if (result.success) {
1493
- if (action === 'export-report') {
1494
- showToast('📤 Report exported', 'success');
1495
- if (result.report) {
1496
- // Download report as JSON
1497
- const blob = new Blob([JSON.stringify(result.report, null, 2)], { type: 'application/json' });
1498
- const url = URL.createObjectURL(blob);
1499
- const a = document.createElement('a');
1500
- a.href = url;
1501
- a.download = 'musubi-report.json';
1502
- a.click();
1503
- URL.revokeObjectURL(url);
1617
+ // Quick Actions
1618
+ async function runQuickAction(action) {
1619
+ const button = event.target;
1620
+ const originalText = button.innerHTML;
1621
+ button.disabled = true;
1622
+ button.innerHTML = '⏳ Processing...';
1623
+
1624
+ try {
1625
+ const response = await fetch(`/api/actions/${action}`, {
1626
+ method: 'POST',
1627
+ headers: { 'Content-Type': 'application/json' },
1628
+ });
1629
+ const result = await response.json();
1630
+
1631
+ if (action === 'validate') {
1632
+ if (result.success) {
1633
+ showToast('✅ Validation passed', 'success');
1634
+ } else {
1635
+ showToast('⚠️ Validation completed with issues', 'warning');
1636
+ }
1637
+ // Show output in console and optionally in a modal
1638
+ if (result.output) {
1639
+ console.log('Validation output:', result.output);
1504
1640
  }
1505
- } else if (action === 'new-requirement') {
1506
- showToast('📝 Requirements wizard started in terminal', 'info');
1641
+ if (result.errors) {
1642
+ console.log('Validation errors:', result.errors);
1643
+ }
1644
+ } else if (result.success) {
1645
+ if (action === 'export-report') {
1646
+ showToast('📤 Report exported', 'success');
1647
+ if (result.report) {
1648
+ // Download report as JSON
1649
+ const blob = new Blob([JSON.stringify(result.report, null, 2)], {
1650
+ type: 'application/json',
1651
+ });
1652
+ const url = URL.createObjectURL(blob);
1653
+ const a = document.createElement('a');
1654
+ a.href = url;
1655
+ a.download = 'musubi-report.json';
1656
+ a.click();
1657
+ URL.revokeObjectURL(url);
1658
+ }
1659
+ } else if (action === 'new-requirement') {
1660
+ showToast('📝 Requirements wizard started in terminal', 'info');
1661
+ }
1662
+ } else {
1663
+ showToast(`❌ ${result.error || 'Action failed'}`, 'error');
1507
1664
  }
1508
- } else {
1509
- showToast(`❌ ${result.error || 'Action failed'}`, 'error');
1665
+ } catch (error) {
1666
+ showToast(`❌ Error: ${error.message}`, 'error');
1667
+ } finally {
1668
+ button.disabled = false;
1669
+ button.innerHTML = originalText;
1510
1670
  }
1511
- } catch (error) {
1512
- showToast(`❌ Error: ${error.message}`, 'error');
1513
- } finally {
1514
- button.disabled = false;
1515
- button.innerHTML = originalText;
1516
- }
1517
- }
1518
-
1519
- // Initial load
1520
- loadCoverage();
1521
- </script>
1522
- </body>
1671
+ }
1672
+
1673
+ // Initial load
1674
+ loadCoverage();
1675
+ </script>
1676
+ </body>
1523
1677
  </html>