@builderos/create-agent-os 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/README.md +39 -0
  2. package/bin/cli.js +133 -0
  3. package/package.json +40 -0
  4. package/src/template/App.tsx +68 -0
  5. package/src/template/agent-os/commands/create-tasks/1-get-spec-requirements.md +19 -0
  6. package/src/template/agent-os/commands/create-tasks/2-create-tasks-list.md +234 -0
  7. package/src/template/agent-os/commands/create-tasks/create-tasks.md +254 -0
  8. package/src/template/agent-os/commands/design-screen/design-screen.md +32 -0
  9. package/src/template/agent-os/commands/design-shell/design-shell.md +34 -0
  10. package/src/template/agent-os/commands/design-tokens/design-tokens.md +36 -0
  11. package/src/template/agent-os/commands/export-product/export-product.md +44 -0
  12. package/src/template/agent-os/commands/implement-tasks/1-determine-tasks.md +13 -0
  13. package/src/template/agent-os/commands/implement-tasks/2-implement-tasks.md +63 -0
  14. package/src/template/agent-os/commands/implement-tasks/3-verify-implementation.md +113 -0
  15. package/src/template/agent-os/commands/implement-tasks/implement-tasks.md +207 -0
  16. package/src/template/agent-os/commands/initialize-design/initialize-design.md +54 -0
  17. package/src/template/agent-os/commands/orchestrate-tasks/orchestrate-tasks.md +180 -0
  18. package/src/template/agent-os/commands/plan-product/1-product-concept.md +53 -0
  19. package/src/template/agent-os/commands/plan-product/2-create-mission.md +78 -0
  20. package/src/template/agent-os/commands/plan-product/3-create-roadmap.md +73 -0
  21. package/src/template/agent-os/commands/plan-product/4-create-tech-stack.md +46 -0
  22. package/src/template/agent-os/commands/plan-product/plan-product.md +241 -0
  23. package/src/template/agent-os/commands/sample-data/sample-data.md +51 -0
  24. package/src/template/agent-os/commands/scaffold-implementation/scaffold-implementation.md +36 -0
  25. package/src/template/agent-os/commands/screenshot-design/screenshot-design.md +21 -0
  26. package/src/template/agent-os/commands/shape-spec/1-initialize-spec.md +95 -0
  27. package/src/template/agent-os/commands/shape-spec/2-shape-spec.md +300 -0
  28. package/src/template/agent-os/commands/shape-spec/shape-spec.md +40 -0
  29. package/src/template/agent-os/commands/write-spec/write-spec.md +134 -0
  30. package/src/template/agent-os/config.yml +13 -0
  31. package/src/template/agent-os/product/mission.md +29 -0
  32. package/src/template/agent-os/product/roadmap.md +9 -0
  33. package/src/template/agent-os/product/tech-stack.md +14 -0
  34. package/src/template/agent-os/specs/README.md +1 -0
  35. package/src/template/agent-os/standards/backend/api.md +10 -0
  36. package/src/template/agent-os/standards/backend/migrations.md +9 -0
  37. package/src/template/agent-os/standards/backend/models.md +10 -0
  38. package/src/template/agent-os/standards/backend/queries.md +9 -0
  39. package/src/template/agent-os/standards/frontend/accessibility.md +10 -0
  40. package/src/template/agent-os/standards/frontend/components.md +11 -0
  41. package/src/template/agent-os/standards/frontend/css.md +7 -0
  42. package/src/template/agent-os/standards/frontend/responsive.md +11 -0
  43. package/src/template/agent-os/standards/global/coding-style.md +10 -0
  44. package/src/template/agent-os/standards/global/commenting.md +5 -0
  45. package/src/template/agent-os/standards/global/conventions.md +11 -0
  46. package/src/template/agent-os/standards/global/error-handling.md +9 -0
  47. package/src/template/agent-os/standards/global/tech-stack.md +31 -0
  48. package/src/template/agent-os/standards/global/validation.md +11 -0
  49. package/src/template/agent-os/standards/testing/test-writing.md +9 -0
  50. package/src/template/agent-os-ui/README.md +73 -0
  51. package/src/template/agent-os-ui/package-lock.json +5028 -0
  52. package/src/template/agent-os-ui/package.json +52 -0
  53. package/src/template/agent-os-ui/postcss.config.js +6 -0
  54. package/src/template/agent-os-ui/src/components/AgentShell.tsx +31 -0
  55. package/src/template/agent-os-ui/src/components/AgentSidebar.tsx +65 -0
  56. package/src/template/agent-os-ui/src/components/GuidanceCard.tsx +75 -0
  57. package/src/template/agent-os-ui/src/components/MarkdownViewer.tsx +25 -0
  58. package/src/template/agent-os-ui/src/components/PromptButton.tsx +28 -0
  59. package/src/template/agent-os-ui/src/components/StatusItem.tsx +45 -0
  60. package/src/template/agent-os-ui/src/components/ThemeToggle.tsx +72 -0
  61. package/src/template/agent-os-ui/src/index.ts +11 -0
  62. package/src/template/agent-os-ui/src/style.css +3 -0
  63. package/src/template/agent-os-ui/tailwind.config.js +50 -0
  64. package/src/template/agent-os-ui/tsconfig.json +33 -0
  65. package/src/template/agent-os-ui/vite.config.ts +32 -0
  66. package/src/template/control-center/backend/backend.log +2 -0
  67. package/src/template/control-center/backend/index.js +228 -0
  68. package/src/template/control-center/backend/package-lock.json +951 -0
  69. package/src/template/control-center/backend/package.json +19 -0
  70. package/src/template/control-center/frontend/README.md +73 -0
  71. package/src/template/control-center/frontend/eslint.config.js +23 -0
  72. package/src/template/control-center/frontend/index.html +21 -0
  73. package/src/template/control-center/frontend/package-lock.json +5752 -0
  74. package/src/template/control-center/frontend/package.json +42 -0
  75. package/src/template/control-center/frontend/public/runtime-config.json +11 -0
  76. package/src/template/control-center/frontend/public/vite.svg +1 -0
  77. package/src/template/control-center/frontend/src/App.css +42 -0
  78. package/src/template/control-center/frontend/src/App.tsx +738 -0
  79. package/src/template/control-center/frontend/src/assets/react.svg +1 -0
  80. package/src/template/control-center/frontend/src/components/ThemeToggle.tsx +64 -0
  81. package/src/template/control-center/frontend/src/components/ui/ToastContext.tsx +81 -0
  82. package/src/template/control-center/frontend/src/index.css +194 -0
  83. package/src/template/control-center/frontend/src/main.tsx +14 -0
  84. package/src/template/control-center/frontend/src/vite-env.d.ts +1 -0
  85. package/src/template/control-center/frontend/tsconfig.app.json +28 -0
  86. package/src/template/control-center/frontend/tsconfig.json +7 -0
  87. package/src/template/control-center/frontend/tsconfig.node.json +26 -0
  88. package/src/template/control-center/frontend/vite.config.ts +22 -0
  89. package/src/template/design/.claude/commands/design-os/data-model.md +122 -0
  90. package/src/template/design/.claude/commands/design-os/design-screen.md +309 -0
  91. package/src/template/design/.claude/commands/design-os/design-shell.md +238 -0
  92. package/src/template/design/.claude/commands/design-os/design-tokens.md +166 -0
  93. package/src/template/design/.claude/commands/design-os/export-product.md +1105 -0
  94. package/src/template/design/.claude/commands/design-os/product-roadmap.md +121 -0
  95. package/src/template/design/.claude/commands/design-os/product-vision.md +99 -0
  96. package/src/template/design/.claude/commands/design-os/sample-data.md +263 -0
  97. package/src/template/design/.claude/commands/design-os/screenshot-design.md +112 -0
  98. package/src/template/design/.claude/commands/design-os/shape-section.md +138 -0
  99. package/src/template/design/.claude/skills/frontend-design/SKILL.md +42 -0
  100. package/src/template/design/.github/CODE_OF_CONDUCT.md +5 -0
  101. package/src/template/design/.github/CONTRIBUTING.md +51 -0
  102. package/src/template/design/.github/ISSUE_TEMPLATE/config.yml +22 -0
  103. package/src/template/design/.github/PULL_REQUEST_TEMPLATE.md +20 -0
  104. package/src/template/design/.github/SECURITY.yml +5 -0
  105. package/src/template/design/.github/SUPPORT.md +19 -0
  106. package/src/template/design/.github/workflows/pr-decline.yml +135 -0
  107. package/src/template/design/.github/workflows/stale.yml +25 -0
  108. package/src/template/design/CHANGELOG.md +13 -0
  109. package/src/template/design/LICENSE +21 -0
  110. package/src/template/design/README.md +54 -0
  111. package/src/template/design/agents.md +218 -0
  112. package/src/template/design/claude.md +1 -0
  113. package/src/template/design/components.json +22 -0
  114. package/src/template/design/docs/codebase-implementation.md +153 -0
  115. package/src/template/design/docs/design-section.md +135 -0
  116. package/src/template/design/docs/export.md +149 -0
  117. package/src/template/design/docs/getting-started.md +59 -0
  118. package/src/template/design/docs/index.md +56 -0
  119. package/src/template/design/docs/product-planning.md +113 -0
  120. package/src/template/design/docs/requirements.md +22 -0
  121. package/src/template/design/docs/usage.md +62 -0
  122. package/src/template/design/eslint.config.js +23 -0
  123. package/src/template/design/index.html +21 -0
  124. package/src/template/design/package-lock.json +5473 -0
  125. package/src/template/design/package.json +47 -0
  126. package/src/template/design/product-plan.zip +0 -0
  127. package/src/template/design/public/vite.svg +1 -0
  128. package/src/template/design/src/assets/react.svg +1 -0
  129. package/src/template/design/src/components/AppLayout.tsx +95 -0
  130. package/src/template/design/src/components/DataCard.tsx +139 -0
  131. package/src/template/design/src/components/DataModelPage.tsx +120 -0
  132. package/src/template/design/src/components/DesignPage.tsx +284 -0
  133. package/src/template/design/src/components/EmptyState.tsx +155 -0
  134. package/src/template/design/src/components/ExportPage.tsx +344 -0
  135. package/src/template/design/src/components/NextPhaseButton.tsx +33 -0
  136. package/src/template/design/src/components/PhaseNav.tsx +152 -0
  137. package/src/template/design/src/components/PhaseWarningBanner.tsx +81 -0
  138. package/src/template/design/src/components/ProductOverviewCard.tsx +102 -0
  139. package/src/template/design/src/components/ProductPage.tsx +97 -0
  140. package/src/template/design/src/components/ScreenDesignPage.tsx +370 -0
  141. package/src/template/design/src/components/ScreenDesignsCard.tsx +49 -0
  142. package/src/template/design/src/components/SectionPage.tsx +256 -0
  143. package/src/template/design/src/components/SectionsCard.tsx +47 -0
  144. package/src/template/design/src/components/SectionsPage.tsx +181 -0
  145. package/src/template/design/src/components/ShellCard.tsx +85 -0
  146. package/src/template/design/src/components/ShellDesignPage.tsx +242 -0
  147. package/src/template/design/src/components/SpecCard.tsx +121 -0
  148. package/src/template/design/src/components/StepIndicator.tsx +75 -0
  149. package/src/template/design/src/components/ThemeToggle.tsx +86 -0
  150. package/src/template/design/src/components/ui/ToastContext.tsx +81 -0
  151. package/src/template/design/src/components/ui/avatar.tsx +53 -0
  152. package/src/template/design/src/components/ui/badge.tsx +46 -0
  153. package/src/template/design/src/components/ui/button.tsx +60 -0
  154. package/src/template/design/src/components/ui/card.tsx +92 -0
  155. package/src/template/design/src/components/ui/collapsible.tsx +48 -0
  156. package/src/template/design/src/components/ui/dialog.tsx +143 -0
  157. package/src/template/design/src/components/ui/dropdown-menu.tsx +255 -0
  158. package/src/template/design/src/components/ui/input.tsx +21 -0
  159. package/src/template/design/src/components/ui/label.tsx +22 -0
  160. package/src/template/design/src/components/ui/progress.tsx +24 -0
  161. package/src/template/design/src/components/ui/scroll-area.tsx +18 -0
  162. package/src/template/design/src/components/ui/select.tsx +67 -0
  163. package/src/template/design/src/components/ui/separator.tsx +28 -0
  164. package/src/template/design/src/components/ui/sheet.tsx +137 -0
  165. package/src/template/design/src/components/ui/skeleton.tsx +13 -0
  166. package/src/template/design/src/components/ui/switch.tsx +46 -0
  167. package/src/template/design/src/components/ui/table.tsx +116 -0
  168. package/src/template/design/src/components/ui/tabs.tsx +64 -0
  169. package/src/template/design/src/index.css +284 -0
  170. package/src/template/design/src/lib/data-model-loader.ts +91 -0
  171. package/src/template/design/src/lib/design-system-loader.ts +101 -0
  172. package/src/template/design/src/lib/product-loader.ts +221 -0
  173. package/src/template/design/src/lib/router.tsx +52 -0
  174. package/src/template/design/src/lib/section-loader.ts +272 -0
  175. package/src/template/design/src/lib/shell-loader.ts +175 -0
  176. package/src/template/design/src/lib/utils.ts +6 -0
  177. package/src/template/design/src/main.tsx +15 -0
  178. package/src/template/design/src/sections/.gitkeep +0 -0
  179. package/src/template/design/src/sections/ai-orchestration-engine-oai/OrchestrationEngine.tsx +348 -0
  180. package/src/template/design/src/sections/core-platform-shell/AppShell.tsx +403 -0
  181. package/src/template/design/src/sections/gemini-live-integration/GeminiIntegration.tsx +332 -0
  182. package/src/template/design/src/sections/interactive-2d-canvas/WhiteboardCanvas.tsx +334 -0
  183. package/src/template/design/src/sections/participation-equity-tracker/EquityTracker.tsx +383 -0
  184. package/src/template/design/src/sections/persistent-memory-system/PersistentMemory.tsx +308 -0
  185. package/src/template/design/src/sections/real-time-communication-layer/VideoSession.tsx +342 -0
  186. package/src/template/design/src/sections/visual-intelligence-agents/VisualAgents.tsx +311 -0
  187. package/src/template/design/src/types/product.ts +97 -0
  188. package/src/template/design/src/types/section.ts +33 -0
  189. package/src/template/design/tsconfig.app.json +34 -0
  190. package/src/template/design/tsconfig.json +13 -0
  191. package/src/template/design/tsconfig.node.json +26 -0
  192. package/src/template/design/vite.config.ts +18 -0
  193. package/src/template/package.json +27 -0
  194. package/src/template/vite.config.ts +16 -0
@@ -0,0 +1,242 @@
1
+ import { Suspense, useMemo, useState, useRef, useCallback, useEffect } from 'react'
2
+ import { useNavigate } from 'react-router-dom'
3
+ import { ArrowLeft, PanelLeft, Maximize2, GripVertical, Smartphone, Tablet, Monitor } from 'lucide-react'
4
+ import { Button } from '@/components/ui/button'
5
+ import { ThemeToggle } from '@/components/ThemeToggle'
6
+ import { loadShellPreview } from '@/lib/shell-loader'
7
+ import React from 'react'
8
+
9
+ const MIN_WIDTH = 320
10
+ const DEFAULT_WIDTH_PERCENT = 100
11
+
12
+ export function ShellDesignPage() {
13
+ const navigate = useNavigate()
14
+ const [widthPercent, setWidthPercent] = useState(DEFAULT_WIDTH_PERCENT)
15
+ const containerRef = useRef<HTMLDivElement>(null)
16
+ const isDragging = useRef(false)
17
+
18
+ // Handle resize drag
19
+ const handleMouseDown = useCallback(() => {
20
+ isDragging.current = true
21
+
22
+ const handleMouseMove = (e: MouseEvent) => {
23
+ if (!isDragging.current || !containerRef.current) return
24
+
25
+ const containerRect = containerRef.current.getBoundingClientRect()
26
+ const containerWidth = containerRect.width
27
+ const containerCenter = containerRect.left + containerWidth / 2
28
+
29
+ // Calculate distance from center
30
+ const distanceFromCenter = Math.abs(e.clientX - containerCenter)
31
+ const maxDistance = containerWidth / 2
32
+
33
+ // Convert to percentage (distance from center * 2 = total width)
34
+ let newWidthPercent = (distanceFromCenter / maxDistance) * 100
35
+
36
+ // Clamp between min width and 100%
37
+ const minPercent = (MIN_WIDTH / containerWidth) * 100
38
+ newWidthPercent = Math.max(minPercent, Math.min(100, newWidthPercent))
39
+
40
+ setWidthPercent(newWidthPercent)
41
+ }
42
+
43
+ const handleMouseUp = () => {
44
+ isDragging.current = false
45
+ document.removeEventListener('mousemove', handleMouseMove)
46
+ document.removeEventListener('mouseup', handleMouseUp)
47
+ document.body.style.cursor = ''
48
+ document.body.style.userSelect = ''
49
+ }
50
+
51
+ document.addEventListener('mousemove', handleMouseMove)
52
+ document.addEventListener('mouseup', handleMouseUp)
53
+ document.body.style.cursor = 'ew-resize'
54
+ document.body.style.userSelect = 'none'
55
+ }, [])
56
+
57
+ const previewWidth = `${widthPercent}%`
58
+
59
+ return (
60
+ <div className="h-screen bg-stone-100 dark:bg-stone-900 animate-fade-in flex flex-col overflow-hidden">
61
+ {/* Header */}
62
+ <header className="border-b border-stone-200 dark:border-stone-800 bg-white dark:bg-stone-950 shrink-0 z-50">
63
+ <div className="px-4 py-2 flex items-center gap-4">
64
+ <Button
65
+ variant="ghost"
66
+ size="sm"
67
+ onClick={() => navigate('/design')}
68
+ className="text-stone-600 dark:text-stone-400 hover:text-stone-900 dark:hover:text-stone-100 -ml-2"
69
+ >
70
+ <ArrowLeft className="w-4 h-4 mr-2" strokeWidth={1.5} />
71
+ Back
72
+ </Button>
73
+ <div className="h-4 w-px bg-stone-200 dark:bg-stone-700" />
74
+ <div className="flex items-center gap-2">
75
+ <PanelLeft className="w-4 h-4 text-stone-400" strokeWidth={1.5} />
76
+ <span className="text-sm font-medium text-stone-700 dark:text-stone-300">
77
+ Shell Design
78
+ </span>
79
+ </div>
80
+
81
+ {/* Width indicator and device presets */}
82
+ <div className="ml-auto flex items-center gap-4">
83
+ {/* Device size presets */}
84
+ <div className="flex items-center gap-1 border-r border-stone-200 dark:border-stone-700 pr-4">
85
+ <button
86
+ onClick={() => setWidthPercent(30)}
87
+ className={`p-1.5 rounded transition-colors ${
88
+ widthPercent <= 40
89
+ ? 'bg-stone-200 dark:bg-stone-700 text-stone-900 dark:text-stone-100'
90
+ : 'text-stone-400 dark:text-stone-500 hover:text-stone-600 dark:hover:text-stone-300 hover:bg-stone-100 dark:hover:bg-stone-800'
91
+ }`}
92
+ title="Mobile (30%)"
93
+ >
94
+ <Smartphone className="w-4 h-4" strokeWidth={1.5} />
95
+ </button>
96
+ <button
97
+ onClick={() => setWidthPercent(60)}
98
+ className={`p-1.5 rounded transition-colors ${
99
+ widthPercent > 40 && widthPercent <= 60
100
+ ? 'bg-stone-200 dark:bg-stone-700 text-stone-900 dark:text-stone-100'
101
+ : 'text-stone-400 dark:text-stone-500 hover:text-stone-600 dark:hover:text-stone-300 hover:bg-stone-100 dark:hover:bg-stone-800'
102
+ }`}
103
+ title="Tablet (60%)"
104
+ >
105
+ <Tablet className="w-4 h-4" strokeWidth={1.5} />
106
+ </button>
107
+ <button
108
+ onClick={() => setWidthPercent(100)}
109
+ className={`p-1.5 rounded transition-colors ${
110
+ widthPercent > 60
111
+ ? 'bg-stone-200 dark:bg-stone-700 text-stone-900 dark:text-stone-100'
112
+ : 'text-stone-400 dark:text-stone-500 hover:text-stone-600 dark:hover:text-stone-300 hover:bg-stone-100 dark:hover:bg-stone-800'
113
+ }`}
114
+ title="Desktop (100%)"
115
+ >
116
+ <Monitor className="w-4 h-4" strokeWidth={1.5} />
117
+ </button>
118
+ </div>
119
+ <span className="text-xs text-stone-500 dark:text-stone-400 font-mono w-10 text-right">
120
+ {Math.round(widthPercent)}%
121
+ </span>
122
+ <ThemeToggle />
123
+ <a
124
+ href="/shell/design/fullscreen"
125
+ target="_blank"
126
+ rel="noopener noreferrer"
127
+ className="flex items-center gap-1.5 text-xs text-stone-500 dark:text-stone-400 hover:text-stone-700 dark:hover:text-stone-200 transition-colors"
128
+ >
129
+ <Maximize2 className="w-3.5 h-3.5" strokeWidth={1.5} />
130
+ Fullscreen
131
+ </a>
132
+ </div>
133
+ </div>
134
+ </header>
135
+
136
+ {/* Preview area with resizable container */}
137
+ <div
138
+ ref={containerRef}
139
+ className="flex-1 overflow-hidden flex items-stretch justify-center p-6"
140
+ >
141
+ {/* Left resize handle */}
142
+ <div
143
+ className="w-4 flex items-center justify-center cursor-ew-resize group shrink-0"
144
+ onMouseDown={handleMouseDown}
145
+ >
146
+ <div className="w-1 h-16 rounded-full bg-stone-300 dark:bg-stone-600 group-hover:bg-stone-400 dark:group-hover:bg-stone-500 transition-colors flex items-center justify-center">
147
+ <GripVertical className="w-3 h-3 text-stone-500 dark:text-stone-400 opacity-0 group-hover:opacity-100 transition-opacity" strokeWidth={2} />
148
+ </div>
149
+ </div>
150
+
151
+ {/* Preview container using iframe for true isolation */}
152
+ <div
153
+ className="bg-white dark:bg-stone-950 rounded-lg shadow-xl border border-stone-200 dark:border-stone-700 overflow-hidden"
154
+ style={{ width: previewWidth, minWidth: MIN_WIDTH, maxWidth: '100%' }}
155
+ >
156
+ <iframe
157
+ src="/shell/design/fullscreen"
158
+ className="w-full h-full border-0"
159
+ title="Shell Preview"
160
+ />
161
+ </div>
162
+
163
+ {/* Right resize handle */}
164
+ <div
165
+ className="w-4 flex items-center justify-center cursor-ew-resize group shrink-0"
166
+ onMouseDown={handleMouseDown}
167
+ >
168
+ <div className="w-1 h-16 rounded-full bg-stone-300 dark:bg-stone-600 group-hover:bg-stone-400 dark:group-hover:bg-stone-500 transition-colors flex items-center justify-center">
169
+ <GripVertical className="w-3 h-3 text-stone-500 dark:text-stone-400 opacity-0 group-hover:opacity-100 transition-opacity" strokeWidth={2} />
170
+ </div>
171
+ </div>
172
+ </div>
173
+ </div>
174
+ )
175
+ }
176
+
177
+ /**
178
+ * Fullscreen version of the shell preview (for screenshots)
179
+ * Syncs theme with parent window via localStorage
180
+ */
181
+ export function ShellDesignFullscreen() {
182
+ const shellPreviewLoader = loadShellPreview()
183
+
184
+ const ShellPreviewComponent = useMemo(() => {
185
+ if (!shellPreviewLoader) return null
186
+ return React.lazy(shellPreviewLoader)
187
+ }, [shellPreviewLoader])
188
+
189
+ // Sync theme with parent window
190
+ useEffect(() => {
191
+ const applyTheme = () => {
192
+ const theme = localStorage.getItem('theme') || 'system'
193
+ const root = document.documentElement
194
+
195
+ if (theme === 'system') {
196
+ const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches
197
+ root.classList.toggle('dark', systemDark)
198
+ } else {
199
+ root.classList.toggle('dark', theme === 'dark')
200
+ }
201
+ }
202
+
203
+ // Apply on mount
204
+ applyTheme()
205
+
206
+ // Listen for storage changes (from parent window)
207
+ const handleStorageChange = (e: StorageEvent) => {
208
+ if (e.key === 'theme') {
209
+ applyTheme()
210
+ }
211
+ }
212
+ window.addEventListener('storage', handleStorageChange)
213
+
214
+ // Also poll for changes since storage event doesn't fire in same window
215
+ const interval = setInterval(applyTheme, 100)
216
+
217
+ return () => {
218
+ window.removeEventListener('storage', handleStorageChange)
219
+ clearInterval(interval)
220
+ }
221
+ }, [])
222
+
223
+ if (!ShellPreviewComponent) {
224
+ return (
225
+ <div className="h-screen flex items-center justify-center bg-background">
226
+ <p className="text-stone-600 dark:text-stone-400">Shell preview not found.</p>
227
+ </div>
228
+ )
229
+ }
230
+
231
+ return (
232
+ <Suspense
233
+ fallback={
234
+ <div className="h-screen flex items-center justify-center bg-background">
235
+ <div className="text-stone-500 dark:text-stone-400">Loading...</div>
236
+ </div>
237
+ }
238
+ >
239
+ <ShellPreviewComponent />
240
+ </Suspense>
241
+ )
242
+ }
@@ -0,0 +1,121 @@
1
+ import { useState } from 'react'
2
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
3
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
4
+ import { ChevronDown, PanelTop, Square } from 'lucide-react'
5
+ import { EmptyState } from '@/components/EmptyState'
6
+ import type { ParsedSpec } from '@/types/section'
7
+
8
+ interface SpecCardProps {
9
+ spec: ParsedSpec | null
10
+ sectionTitle?: string
11
+ sectionId?: string
12
+ }
13
+
14
+ export function SpecCard({ spec, sectionTitle, sectionId }: SpecCardProps) {
15
+ const [userFlowsOpen, setUserFlowsOpen] = useState(false)
16
+ const [uiReqOpen, setUiReqOpen] = useState(false)
17
+
18
+ // Empty state
19
+ if (!spec) {
20
+ return <EmptyState type="spec" context={sectionTitle} contextId={sectionId} />
21
+ }
22
+
23
+ return (
24
+ <Card className="border-stone-200 dark:border-stone-700 shadow-sm">
25
+ <CardHeader className="pb-4">
26
+ <CardTitle className="text-lg font-semibold text-stone-900 dark:text-stone-100">
27
+ {sectionTitle || 'Specification'}
28
+ </CardTitle>
29
+ </CardHeader>
30
+ <CardContent className="space-y-4">
31
+ {/* Overview */}
32
+ {spec.overview && (
33
+ <p className="text-stone-600 dark:text-stone-400 leading-relaxed">
34
+ {spec.overview}
35
+ </p>
36
+ )}
37
+
38
+ {/* User Flows - Expandable */}
39
+ {spec.userFlows.length > 0 && (
40
+ <Collapsible open={userFlowsOpen} onOpenChange={setUserFlowsOpen}>
41
+ <CollapsibleTrigger className="flex items-center justify-between w-full py-2 text-left group">
42
+ <span className="text-sm font-medium text-stone-500 dark:text-stone-400 uppercase tracking-wide">
43
+ User Flows
44
+ <span className="ml-2 text-stone-400 dark:text-stone-500 normal-case tracking-normal">
45
+ ({spec.userFlows.length})
46
+ </span>
47
+ </span>
48
+ <ChevronDown
49
+ className={`w-4 h-4 text-stone-400 dark:text-stone-500 transition-transform ${userFlowsOpen ? 'rotate-180' : ''
50
+ }`}
51
+ strokeWidth={1.5}
52
+ />
53
+ </CollapsibleTrigger>
54
+ <CollapsibleContent>
55
+ <ul className="space-y-2 pt-2">
56
+ {spec.userFlows.map((flow, index) => (
57
+ <li key={index} className="flex items-start gap-3">
58
+ <span className="w-1.5 h-1.5 rounded-full bg-stone-900 dark:bg-stone-100 mt-2 shrink-0" />
59
+ <span className="text-stone-700 dark:text-stone-300 text-sm">
60
+ {flow}
61
+ </span>
62
+ </li>
63
+ ))}
64
+ </ul>
65
+ </CollapsibleContent>
66
+ </Collapsible>
67
+ )}
68
+
69
+ {/* UI Requirements - Expandable */}
70
+ {spec.uiRequirements.length > 0 && (
71
+ <Collapsible open={uiReqOpen} onOpenChange={setUiReqOpen}>
72
+ <CollapsibleTrigger className="flex items-center justify-between w-full py-2 text-left group">
73
+ <span className="text-sm font-medium text-stone-500 dark:text-stone-400 uppercase tracking-wide">
74
+ UI Requirements
75
+ <span className="ml-2 text-stone-400 dark:text-stone-500 normal-case tracking-normal">
76
+ ({spec.uiRequirements.length})
77
+ </span>
78
+ </span>
79
+ <ChevronDown
80
+ className={`w-4 h-4 text-stone-400 dark:text-stone-500 transition-transform ${uiReqOpen ? 'rotate-180' : ''
81
+ }`}
82
+ strokeWidth={1.5}
83
+ />
84
+ </CollapsibleTrigger>
85
+ <CollapsibleContent>
86
+ <ul className="space-y-2 pt-2">
87
+ {spec.uiRequirements.map((req, index) => (
88
+ <li key={index} className="flex items-start gap-3">
89
+ <span className="w-1.5 h-1.5 rounded-full bg-stone-900 dark:bg-stone-100 mt-2 shrink-0" />
90
+ <span className="text-stone-700 dark:text-stone-300 text-sm">
91
+ {req}
92
+ </span>
93
+ </li>
94
+ ))}
95
+ </ul>
96
+ </CollapsibleContent>
97
+ </Collapsible>
98
+ )}
99
+
100
+ {/* Display Configuration */}
101
+ <div className="flex items-center gap-2 pt-2 border-t border-stone-100 dark:border-stone-800">
102
+ {spec.useShell ? (
103
+ <>
104
+ <PanelTop className="w-4 h-4 text-stone-400 dark:text-stone-500" strokeWidth={1.5} />
105
+ <span className="text-sm text-stone-500 dark:text-stone-400">
106
+ Displays inside app shell
107
+ </span>
108
+ </>
109
+ ) : (
110
+ <>
111
+ <Square className="w-4 h-4 text-stone-400 dark:text-stone-500" strokeWidth={1.5} />
112
+ <span className="text-sm text-stone-500 dark:text-stone-400">
113
+ Standalone page (no shell)
114
+ </span>
115
+ </>
116
+ )}
117
+ </div>
118
+ </CardContent>
119
+ </Card>
120
+ )
121
+ }
@@ -0,0 +1,75 @@
1
+ import { Check, ArrowRight, AlertTriangle } from 'lucide-react'
2
+ import type { ReactNode } from 'react'
3
+
4
+ export type StepStatus = 'completed' | 'current' | 'upcoming' | 'skipped'
5
+
6
+ interface StepIndicatorProps {
7
+ step: number
8
+ status: StepStatus
9
+ children: ReactNode
10
+ isLast?: boolean
11
+ }
12
+
13
+ export function StepIndicator({ step, status, children, isLast = false }: StepIndicatorProps) {
14
+ return (
15
+ <div className="relative">
16
+ {/* Vertical connecting line - extends from this step to the next */}
17
+ {!isLast && (
18
+ <div
19
+ className="absolute left-[10px] top-[28px] w-[2px] h-[calc(100%+16px)] bg-stone-200 dark:bg-stone-700"
20
+ aria-hidden="true"
21
+ />
22
+ )}
23
+
24
+ {/* Step badge positioned at top-left */}
25
+ <div className="absolute -left-[2px] top-0 z-10">
26
+ <StepBadge step={step} status={status} />
27
+ </div>
28
+
29
+ {/* Card content with left padding to accommodate the step indicator */}
30
+ <div className="pl-10">
31
+ {children}
32
+ </div>
33
+ </div>
34
+ )
35
+ }
36
+
37
+ interface StepBadgeProps {
38
+ step: number
39
+ status: StepStatus
40
+ }
41
+
42
+ function StepBadge({ step, status }: StepBadgeProps) {
43
+ const baseClasses = "w-6 h-6 rounded-full flex items-center justify-center text-xs font-semibold transition-all duration-200"
44
+
45
+ if (status === 'completed') {
46
+ return (
47
+ <div className={`${baseClasses} bg-stone-200 dark:bg-stone-700 text-stone-500 dark:text-stone-400`}>
48
+ <Check className="w-3 h-3" strokeWidth={2.5} />
49
+ </div>
50
+ )
51
+ }
52
+
53
+ if (status === 'current') {
54
+ return (
55
+ <div className={`${baseClasses} bg-stone-900 dark:bg-stone-100 text-stone-100 dark:text-stone-900 shadow-sm`}>
56
+ <ArrowRight className="w-3 h-3" strokeWidth={2.5} />
57
+ </div>
58
+ )
59
+ }
60
+
61
+ if (status === 'skipped') {
62
+ return (
63
+ <div className={`${baseClasses} bg-amber-100 dark:bg-amber-900/30 text-amber-600 dark:text-amber-400`}>
64
+ <AlertTriangle className="w-3 h-3" strokeWidth={2.5} />
65
+ </div>
66
+ )
67
+ }
68
+
69
+ // upcoming
70
+ return (
71
+ <div className={`${baseClasses} bg-stone-200 dark:bg-stone-700 text-stone-500 dark:text-stone-400`}>
72
+ {step}
73
+ </div>
74
+ )
75
+ }
@@ -0,0 +1,86 @@
1
+ import { useState, useEffect } from 'react'
2
+ import { Moon, Sun } from 'lucide-react'
3
+ import { Button } from '@/components/ui/button'
4
+
5
+ type Theme = 'light' | 'dark' | 'system'
6
+
7
+ export function ThemeToggle({ headless }: { headless?: boolean }) {
8
+ const [theme, setTheme] = useState<Theme>(() => {
9
+ if (typeof window !== 'undefined') {
10
+ // Check URL param first (from Agent OS)
11
+ const params = new URLSearchParams(window.location.search)
12
+ const urlTheme = params.get('theme') as Theme
13
+ if (urlTheme && ['light', 'dark', 'system'].includes(urlTheme)) {
14
+ return urlTheme
15
+ }
16
+ return (localStorage.getItem('theme') as Theme) || 'system'
17
+ }
18
+ return 'system'
19
+ })
20
+
21
+ useEffect(() => {
22
+ const root = document.documentElement
23
+
24
+ const applyTheme = (theme: Theme) => {
25
+ if (theme === 'system') {
26
+ const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches
27
+ root.classList.toggle('dark', systemDark)
28
+ } else {
29
+ root.classList.toggle('dark', theme === 'dark')
30
+ }
31
+ }
32
+
33
+ applyTheme(theme)
34
+ localStorage.setItem('theme', theme)
35
+
36
+ // Listen for system theme changes when in system mode
37
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
38
+ const handleChange = () => {
39
+ if (theme === 'system') {
40
+ applyTheme('system')
41
+ }
42
+ }
43
+ mediaQuery.addEventListener('change', handleChange)
44
+
45
+ // Listen for messages from Agent OS (Parent Window)
46
+ const handleMessage = (event: MessageEvent) => {
47
+ if (event.data?.type === 'THEME_CHANGE' && event.data.theme) {
48
+ setTheme(event.data.theme as Theme)
49
+ }
50
+ }
51
+ window.addEventListener('message', handleMessage)
52
+
53
+ return () => {
54
+ mediaQuery.removeEventListener('change', handleChange)
55
+ window.removeEventListener('message', handleMessage)
56
+ }
57
+ }, [theme])
58
+
59
+ const toggleTheme = () => {
60
+ setTheme((prev) => {
61
+ if (prev === 'light') return 'dark'
62
+ if (prev === 'dark') return 'system'
63
+ return 'light'
64
+ })
65
+ }
66
+
67
+ const isDark = theme === 'dark' || (theme === 'system' && typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches)
68
+
69
+ if (headless) return null
70
+
71
+ return (
72
+ <Button
73
+ variant="ghost"
74
+ size="icon"
75
+ onClick={toggleTheme}
76
+ className="w-8 h-8 text-stone-600 dark:text-stone-400 hover:text-stone-900 dark:hover:text-stone-100"
77
+ title={`Theme: ${theme}`}
78
+ >
79
+ {isDark ? (
80
+ <Moon className="w-4 h-4" strokeWidth={1.5} />
81
+ ) : (
82
+ <Sun className="w-4 h-4" strokeWidth={1.5} />
83
+ )}
84
+ </Button>
85
+ )
86
+ }
@@ -0,0 +1,81 @@
1
+ import React, { createContext, useContext, useState, useCallback, ReactNode } from 'react';
2
+ import { X, CheckCircle, Info, AlertTriangle } from 'lucide-react';
3
+
4
+ type ToastType = 'success' | 'error' | 'info' | 'warning';
5
+
6
+ interface Toast {
7
+ id: string;
8
+ title: string;
9
+ description?: string;
10
+ type?: ToastType;
11
+ }
12
+
13
+ interface ToastContextType {
14
+ toast: (props: Omit<Toast, 'id'>) => void;
15
+ }
16
+
17
+ const ToastContext = createContext<ToastContextType | undefined>(undefined);
18
+
19
+ export function useToast() {
20
+ const context = useContext(ToastContext);
21
+ if (!context) {
22
+ throw new Error('useToast must be used within a ToastProvider');
23
+ }
24
+ return context;
25
+ }
26
+
27
+ export function ToastProvider({ children }: { children: ReactNode }) {
28
+ const [toasts, setToasts] = useState<Toast[]>([]);
29
+
30
+ const toast = useCallback(({ title, description, type = 'info' }: Omit<Toast, 'id'>) => {
31
+ const id = Math.random().toString(36).substring(2, 9);
32
+ setToasts((prev) => [...prev, { id, title, description, type }]);
33
+
34
+ // Auto dismiss
35
+ setTimeout(() => {
36
+ removeToast(id);
37
+ }, 3000);
38
+ }, []);
39
+
40
+ const removeToast = useCallback((id: string) => {
41
+ setToasts((prev) => prev.filter((t) => t.id !== id));
42
+ }, []);
43
+
44
+ return (
45
+ <ToastContext.Provider value={{ toast }}>
46
+ {children}
47
+ <div className="fixed bottom-4 right-4 z-[9999] flex flex-col gap-2">
48
+ {toasts.map((t) => (
49
+ <div
50
+ key={t.id}
51
+ className={`
52
+ min-w-[300px] border rounded-lg shadow-lg p-4 transition-all duration-300 animate-in slide-in-from-right
53
+ bg-white dark:bg-stone-900 border-stone-200 dark:border-stone-800
54
+ `}
55
+ >
56
+ <div className="flex items-start gap-3">
57
+ <div className="shrink-0 pt-0.5">
58
+ {t.type === 'success' && <CheckCircle size={18} className="text-emerald-500" />}
59
+ {t.type === 'error' && <AlertTriangle size={18} className="text-red-500" />}
60
+ {t.type === 'warning' && <AlertTriangle size={18} className="text-amber-500" />}
61
+ {t.type === 'info' && <Info size={18} className="text-blue-500" />}
62
+ </div>
63
+ <div className="flex-1">
64
+ <h4 className="text-sm font-semibold text-stone-900 dark:text-stone-100">{t.title}</h4>
65
+ {t.description && (
66
+ <p className="text-sm text-stone-500 dark:text-stone-400 mt-1">{t.description}</p>
67
+ )}
68
+ </div>
69
+ <button
70
+ onClick={() => removeToast(t.id)}
71
+ className="shrink-0 text-stone-400 hover:text-stone-600 dark:hover:text-stone-300"
72
+ >
73
+ <X size={16} />
74
+ </button>
75
+ </div>
76
+ </div>
77
+ ))}
78
+ </div>
79
+ </ToastContext.Provider>
80
+ );
81
+ }
@@ -0,0 +1,53 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as AvatarPrimitive from "@radix-ui/react-avatar"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ function Avatar({
9
+ className,
10
+ ...props
11
+ }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
12
+ return (
13
+ <AvatarPrimitive.Root
14
+ data-slot="avatar"
15
+ className={cn(
16
+ "relative flex size-8 shrink-0 overflow-hidden rounded-full",
17
+ className
18
+ )}
19
+ {...props}
20
+ />
21
+ )
22
+ }
23
+
24
+ function AvatarImage({
25
+ className,
26
+ ...props
27
+ }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
28
+ return (
29
+ <AvatarPrimitive.Image
30
+ data-slot="avatar-image"
31
+ className={cn("aspect-square size-full", className)}
32
+ {...props}
33
+ />
34
+ )
35
+ }
36
+
37
+ function AvatarFallback({
38
+ className,
39
+ ...props
40
+ }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
41
+ return (
42
+ <AvatarPrimitive.Fallback
43
+ data-slot="avatar-fallback"
44
+ className={cn(
45
+ "bg-muted flex size-full items-center justify-center rounded-full",
46
+ className
47
+ )}
48
+ {...props}
49
+ />
50
+ )
51
+ }
52
+
53
+ export { Avatar, AvatarImage, AvatarFallback }