@theproductguy/create-mission-control 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/README.md +37 -0
  2. package/bin/cli.js +170 -0
  3. package/package.json +44 -0
  4. package/src/template/App.tsx +28 -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 +181 -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 +67 -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 +35 -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/scripts/generate_docs.sh +150 -0
  35. package/src/template/agent-os/specs/README.md +1 -0
  36. package/src/template/agent-os/standards/backend/api.md +10 -0
  37. package/src/template/agent-os/standards/backend/migrations.md +9 -0
  38. package/src/template/agent-os/standards/backend/models.md +10 -0
  39. package/src/template/agent-os/standards/backend/queries.md +9 -0
  40. package/src/template/agent-os/standards/frontend/accessibility.md +10 -0
  41. package/src/template/agent-os/standards/frontend/components.md +11 -0
  42. package/src/template/agent-os/standards/frontend/css.md +7 -0
  43. package/src/template/agent-os/standards/frontend/responsive.md +11 -0
  44. package/src/template/agent-os/standards/global/coding-style.md +10 -0
  45. package/src/template/agent-os/standards/global/commenting.md +5 -0
  46. package/src/template/agent-os/standards/global/conventions.md +11 -0
  47. package/src/template/agent-os/standards/global/error-handling.md +9 -0
  48. package/src/template/agent-os/standards/global/tech-stack.md +31 -0
  49. package/src/template/agent-os/standards/global/validation.md +11 -0
  50. package/src/template/agent-os/standards/testing/test-writing.md +9 -0
  51. package/src/template/agent-os-ui/README.md +73 -0
  52. package/src/template/agent-os-ui/package.json +54 -0
  53. package/src/template/agent-os-ui/src/components/AgentShell.tsx +31 -0
  54. package/src/template/agent-os-ui/src/components/AgentSidebar.tsx +65 -0
  55. package/src/template/agent-os-ui/src/components/GuidanceCard.tsx +75 -0
  56. package/src/template/agent-os-ui/src/components/MarkdownViewer.tsx +25 -0
  57. package/src/template/agent-os-ui/src/components/PromptButton.tsx +28 -0
  58. package/src/template/agent-os-ui/src/components/StatusItem.tsx +45 -0
  59. package/src/template/agent-os-ui/src/components/ThemeToggle.tsx +72 -0
  60. package/src/template/agent-os-ui/src/index.ts +11 -0
  61. package/src/template/agent-os-ui/src/style.css +3 -0
  62. package/src/template/agent-os-ui/tsconfig.json +33 -0
  63. package/src/template/agent-os-ui/vite.config.ts +32 -0
  64. package/src/template/control-center/backend/index.js +253 -0
  65. package/src/template/control-center/backend/package.json +19 -0
  66. package/src/template/control-center/frontend/README.md +73 -0
  67. package/src/template/control-center/frontend/eslint.config.js +23 -0
  68. package/src/template/control-center/frontend/index.html +21 -0
  69. package/src/template/control-center/frontend/package.json +43 -0
  70. package/src/template/control-center/frontend/postcss.config.js +6 -0
  71. package/src/template/control-center/frontend/public/favicon.svg +4 -0
  72. package/src/template/control-center/frontend/public/runtime-config.json +11 -0
  73. package/src/template/control-center/frontend/public/vite.svg +1 -0
  74. package/src/template/control-center/frontend/src/App.css +42 -0
  75. package/src/template/control-center/frontend/src/App.tsx +835 -0
  76. package/src/template/control-center/frontend/src/assets/react.svg +1 -0
  77. package/src/template/control-center/frontend/src/components/ThemeToggle.tsx +64 -0
  78. package/src/template/control-center/frontend/src/components/ui/ToastContext.tsx +81 -0
  79. package/src/template/control-center/frontend/src/index.css +149 -0
  80. package/src/template/control-center/frontend/src/main.tsx +14 -0
  81. package/src/template/control-center/frontend/src/vite-env.d.ts +1 -0
  82. package/src/template/control-center/frontend/tailwind.config.js +81 -0
  83. package/src/template/control-center/frontend/tsconfig.app.json +28 -0
  84. package/src/template/control-center/frontend/tsconfig.json +7 -0
  85. package/src/template/control-center/frontend/tsconfig.node.json +26 -0
  86. package/src/template/control-center/frontend/vite.config.ts +21 -0
  87. package/src/template/design/.claude/commands/design-os/data-model.md +122 -0
  88. package/src/template/design/.claude/commands/design-os/design-screen.md +309 -0
  89. package/src/template/design/.claude/commands/design-os/design-shell.md +238 -0
  90. package/src/template/design/.claude/commands/design-os/design-tokens.md +166 -0
  91. package/src/template/design/.claude/commands/design-os/export-product.md +1105 -0
  92. package/src/template/design/.claude/commands/design-os/product-roadmap.md +121 -0
  93. package/src/template/design/.claude/commands/design-os/product-vision.md +99 -0
  94. package/src/template/design/.claude/commands/design-os/sample-data.md +263 -0
  95. package/src/template/design/.claude/commands/design-os/screenshot-design.md +112 -0
  96. package/src/template/design/.claude/commands/design-os/shape-section.md +138 -0
  97. package/src/template/design/.claude/skills/frontend-design/SKILL.md +42 -0
  98. package/src/template/design/.github/CODE_OF_CONDUCT.md +5 -0
  99. package/src/template/design/.github/CONTRIBUTING.md +51 -0
  100. package/src/template/design/.github/ISSUE_TEMPLATE/config.yml +22 -0
  101. package/src/template/design/.github/PULL_REQUEST_TEMPLATE.md +20 -0
  102. package/src/template/design/.github/SECURITY.yml +5 -0
  103. package/src/template/design/.github/SUPPORT.md +19 -0
  104. package/src/template/design/.github/workflows/pr-decline.yml +135 -0
  105. package/src/template/design/.github/workflows/stale.yml +25 -0
  106. package/src/template/design/CHANGELOG.md +13 -0
  107. package/src/template/design/LICENSE +21 -0
  108. package/src/template/design/README.md +54 -0
  109. package/src/template/design/agents.md +218 -0
  110. package/src/template/design/claude.md +1 -0
  111. package/src/template/design/components.json +22 -0
  112. package/src/template/design/docs/codebase-implementation.md +153 -0
  113. package/src/template/design/docs/design-section.md +135 -0
  114. package/src/template/design/docs/export.md +149 -0
  115. package/src/template/design/docs/getting-started.md +59 -0
  116. package/src/template/design/docs/index.md +56 -0
  117. package/src/template/design/docs/product-planning.md +113 -0
  118. package/src/template/design/docs/requirements.md +22 -0
  119. package/src/template/design/docs/usage.md +62 -0
  120. package/src/template/design/eslint.config.js +23 -0
  121. package/src/template/design/index.html +21 -0
  122. package/src/template/design/package.json +46 -0
  123. package/src/template/design/postcss.config.js +6 -0
  124. package/src/template/design/public/favicon.svg +4 -0
  125. package/src/template/design/public/vite.svg +1 -0
  126. package/src/template/design/src/assets/react.svg +1 -0
  127. package/src/template/design/src/components/AppLayout.tsx +95 -0
  128. package/src/template/design/src/components/DataCard.tsx +139 -0
  129. package/src/template/design/src/components/DataModelPage.tsx +120 -0
  130. package/src/template/design/src/components/DesignPage.tsx +284 -0
  131. package/src/template/design/src/components/EmptyState.tsx +158 -0
  132. package/src/template/design/src/components/ExportPage.tsx +354 -0
  133. package/src/template/design/src/components/NextPhaseButton.tsx +33 -0
  134. package/src/template/design/src/components/PhaseNav.tsx +152 -0
  135. package/src/template/design/src/components/PhaseWarningBanner.tsx +81 -0
  136. package/src/template/design/src/components/ProductOverviewCard.tsx +102 -0
  137. package/src/template/design/src/components/ProductPage.tsx +97 -0
  138. package/src/template/design/src/components/ScreenDesignPage.tsx +370 -0
  139. package/src/template/design/src/components/ScreenDesignsCard.tsx +49 -0
  140. package/src/template/design/src/components/SectionPage.tsx +256 -0
  141. package/src/template/design/src/components/SectionsCard.tsx +47 -0
  142. package/src/template/design/src/components/SectionsPage.tsx +181 -0
  143. package/src/template/design/src/components/ShellCard.tsx +85 -0
  144. package/src/template/design/src/components/ShellDesignPage.tsx +242 -0
  145. package/src/template/design/src/components/SpecCard.tsx +121 -0
  146. package/src/template/design/src/components/StepIndicator.tsx +75 -0
  147. package/src/template/design/src/components/ThemeToggle.tsx +86 -0
  148. package/src/template/design/src/components/ui/ToastContext.tsx +81 -0
  149. package/src/template/design/src/components/ui/avatar.tsx +53 -0
  150. package/src/template/design/src/components/ui/badge.tsx +46 -0
  151. package/src/template/design/src/components/ui/button.tsx +60 -0
  152. package/src/template/design/src/components/ui/card.tsx +92 -0
  153. package/src/template/design/src/components/ui/collapsible.tsx +48 -0
  154. package/src/template/design/src/components/ui/dialog.tsx +143 -0
  155. package/src/template/design/src/components/ui/dropdown-menu.tsx +255 -0
  156. package/src/template/design/src/components/ui/input.tsx +21 -0
  157. package/src/template/design/src/components/ui/label.tsx +22 -0
  158. package/src/template/design/src/components/ui/progress.tsx +24 -0
  159. package/src/template/design/src/components/ui/scroll-area.tsx +18 -0
  160. package/src/template/design/src/components/ui/select.tsx +67 -0
  161. package/src/template/design/src/components/ui/separator.tsx +28 -0
  162. package/src/template/design/src/components/ui/sheet.tsx +137 -0
  163. package/src/template/design/src/components/ui/skeleton.tsx +13 -0
  164. package/src/template/design/src/components/ui/switch.tsx +46 -0
  165. package/src/template/design/src/components/ui/table.tsx +116 -0
  166. package/src/template/design/src/components/ui/tabs.tsx +64 -0
  167. package/src/template/design/src/index.css +239 -0
  168. package/src/template/design/src/lib/data-model-loader.ts +91 -0
  169. package/src/template/design/src/lib/design-system-loader.ts +101 -0
  170. package/src/template/design/src/lib/product-loader.ts +221 -0
  171. package/src/template/design/src/lib/router.tsx +61 -0
  172. package/src/template/design/src/lib/section-loader.ts +272 -0
  173. package/src/template/design/src/lib/shell-loader.ts +175 -0
  174. package/src/template/design/src/lib/utils.ts +6 -0
  175. package/src/template/design/src/main.tsx +15 -0
  176. package/src/template/design/src/sections/.gitkeep +0 -0
  177. package/src/template/design/src/sections/ai-orchestration-engine-oai/OrchestrationEngine.tsx +348 -0
  178. package/src/template/design/src/sections/core-platform-shell/AppShell.tsx +403 -0
  179. package/src/template/design/src/sections/gemini-live-integration/GeminiIntegration.tsx +332 -0
  180. package/src/template/design/src/sections/interactive-2d-canvas/WhiteboardCanvas.tsx +334 -0
  181. package/src/template/design/src/sections/participation-equity-tracker/EquityTracker.tsx +383 -0
  182. package/src/template/design/src/sections/persistent-memory-system/PersistentMemory.tsx +308 -0
  183. package/src/template/design/src/sections/real-time-communication-layer/VideoSession.tsx +342 -0
  184. package/src/template/design/src/sections/visual-intelligence-agents/VisualAgents.tsx +311 -0
  185. package/src/template/design/src/types/product.ts +97 -0
  186. package/src/template/design/src/types/section.ts +33 -0
  187. package/src/template/design/tailwind.config.js +77 -0
  188. package/src/template/design/tsconfig.app.json +34 -0
  189. package/src/template/design/tsconfig.json +13 -0
  190. package/src/template/design/tsconfig.node.json +26 -0
  191. package/src/template/design/vite.config.ts +17 -0
  192. package/src/template/index.css +102 -0
  193. package/src/template/package.json +27 -0
  194. package/src/template/tailwind.config.js +80 -0
  195. package/src/template/vite.config.ts +9 -0
@@ -0,0 +1,835 @@
1
+ import { useEffect, useState } from 'react';
2
+ import axios from 'axios';
3
+ import { Layout, Monitor, Layers, FileText, Code, CheckSquare, RefreshCw, ArrowRight, X, Plus, Trash2, WalletCards, LayoutDashboard, Copy, Package } from 'lucide-react';
4
+ import { useToast } from './components/ui/ToastContext';
5
+ import { ThemeToggle } from '@builderos/agent-os-ui';
6
+ import '@builderos/agent-os-ui/style.css';
7
+ import ReactMarkdown from 'react-markdown';
8
+ import remarkGfm from 'remark-gfm';
9
+ import remarkBreaks from 'remark-breaks';
10
+
11
+ // Types
12
+ interface Status {
13
+ exists: boolean;
14
+ completed: number;
15
+ total: number;
16
+ nextItem?: string | null;
17
+ items?: { name: string; completed: boolean }[];
18
+ isBoilerplate?: boolean;
19
+ }
20
+ interface ProductStatus {
21
+ mission: Status;
22
+ roadmap: Status;
23
+ techStack: Status;
24
+ }
25
+ interface Spec {
26
+ name: string;
27
+ spec: Status;
28
+ tasks: Status;
29
+ }
30
+ interface ProjectState {
31
+ product: ProductStatus;
32
+ design: {
33
+ exists: boolean;
34
+ initialized: boolean;
35
+ tokens: boolean;
36
+ shell: boolean;
37
+ exported: boolean;
38
+ exportPrompts?: {
39
+ oneShot: boolean;
40
+ section: boolean;
41
+ };
42
+ };
43
+ implementation: {
44
+ scaffolded: boolean;
45
+ };
46
+ specs: Spec[];
47
+ services?: {
48
+ api: boolean;
49
+ design: boolean;
50
+ app: boolean;
51
+ };
52
+ projectRoot: string;
53
+ }
54
+
55
+ const Guidance = ({ phase, title, description, prompt, actionLabel, onAction, className }: any) => (
56
+ <div className={`bg-card border border-border rounded-xl p-6 shadow-sm ${className}`}>
57
+ <div className="text-xs font-semibold tracking-wider text-muted-foreground uppercase mb-2">
58
+ {phase}
59
+ </div>
60
+ <div className="flex flex-col md:flex-row gap-6 items-start justify-between">
61
+ <div className="max-w-xl">
62
+ <h2 className="text-2xl font-bold text-foreground mb-2">{title}</h2>
63
+ <p className="text-muted-foreground leading-relaxed">
64
+ {description}
65
+ </p>
66
+ </div>
67
+ {(prompt || actionLabel) && (
68
+ <div className="flex items-center gap-3 shrink-0">
69
+ {prompt && (
70
+ <button
71
+ onClick={() => {
72
+ navigator.clipboard.writeText(prompt);
73
+ const toast = document.createElement('div');
74
+ toast.textContent = "Prompt Copied!";
75
+ toast.className = "fixed bottom-8 right-8 bg-foreground text-background px-4 py-2 rounded-md shadow-lg z-50 animate-in fade-in slide-in-from-bottom-4";
76
+ document.body.appendChild(toast);
77
+ setTimeout(() => toast.remove(), 2000);
78
+ }}
79
+ className="flex items-center gap-2 px-4 py-2.5 bg-secondary hover:bg-secondary/80 text-secondary-foreground font-medium rounded-lg border border-border transition-all shadow-sm hover:shadow"
80
+ >
81
+ <Copy size={16} />
82
+ Copy Prompt
83
+ </button>
84
+ )}
85
+ {actionLabel && (
86
+ <button
87
+ onClick={onAction}
88
+ className="flex items-center gap-2 px-4 py-2.5 bg-primary hover:bg-primary/90 text-primary-foreground font-medium rounded-lg shadow-sm hover:shadow-md transition-all"
89
+ >
90
+ {actionLabel} <ArrowRight size={16} />
91
+ </button>
92
+ )}
93
+ </div>
94
+ )}
95
+ </div>
96
+ </div>
97
+ );
98
+
99
+ function StatusItem({ label, status, icon, small, onClick }: any) {
100
+ if (!status) return null;
101
+ const isComplete = status.exists && (status.total > 0 ? status.completed === status.total : true);
102
+
103
+ return (
104
+ <div
105
+ onClick={onClick}
106
+ className={`flex items-center justify-between ${small ? 'text-xs' : 'text-sm'} ${onClick ? 'cursor-pointer hover:bg-secondary/50 p-1.5 -mx-1.5 rounded-md transition-colors group' : ''}`}
107
+ >
108
+ <div className="flex items-center gap-2 text-muted-foreground group-hover:text-foreground transition-colors">
109
+ {icon}
110
+ <span>{label}</span>
111
+ </div>
112
+ <div>
113
+ {status.exists && !status.isBoilerplate ? (
114
+ <span className={`px-2 py-0.5 rounded-full text-xs font-medium border ${isComplete
115
+ ? 'bg-emerald-50 dark:bg-emerald-900/20 text-emerald-600 border-emerald-200 dark:border-emerald-800'
116
+ : 'bg-secondary text-muted-foreground border-border'
117
+ }`}>
118
+ {isComplete ? 'Done' : 'Pending'}
119
+ </span>
120
+ ) : (
121
+ <span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">Pending</span>
122
+ )}
123
+ </div>
124
+ </div>
125
+ );
126
+ }
127
+
128
+ function PromptButton({ label, prompt, onClick, small, primary }: any) {
129
+ return (
130
+ <button
131
+ onClick={() => onClick(prompt)}
132
+ className={`flex items-center justify-center gap-2 rounded-lg font-medium transition cursor-pointer
133
+ ${small ? 'px-3 py-1.5 text-xs flex-1' : 'px-4 py-2 text-sm w-full'}
134
+ ${primary
135
+ ? 'bg-primary hover:bg-primary/90 text-primary-foreground shadow-sm'
136
+ : 'bg-background hover:bg-secondary border border-border text-foreground hover:text-foreground'}
137
+ `}
138
+ >
139
+ <Copy size={small ? 12 : 14} />
140
+ {label}
141
+ </button>
142
+ )
143
+ }
144
+
145
+ const NextStepCard = ({ state, onOpenDesign }: { state: ProjectState, onOpenDesign: () => void }) => {
146
+ let step = { phase: "", title: "", description: "", prompt: "", link: "", actionLabel: "", action: () => { } };
147
+
148
+ const isProductComplete =
149
+ state?.product?.mission?.exists && !state?.product?.mission?.isBoilerplate &&
150
+ state?.product?.techStack?.exists && !state?.product?.techStack?.isBoilerplate &&
151
+ state?.product?.roadmap?.exists && !state?.product?.roadmap?.isBoilerplate;
152
+
153
+ if (!isProductComplete) {
154
+ step = {
155
+ phase: "Phase 1: Product Strategy",
156
+ title: "Plan Your Product",
157
+ description: "Define your Mission, Roadmap, and Tech Stack to build a solid foundation.",
158
+ prompt: "Antigravity, let's start Phase 1: Product Planning. Please read 'agent-os/commands/plan-product/plan-product.md' and guide me.",
159
+ link: "",
160
+ actionLabel: "",
161
+ action: () => { }
162
+ }
163
+ } else if (!state?.design?.initialized) {
164
+ step = {
165
+ phase: "Phase 2: Design System",
166
+ title: "Sync Product to Design OS",
167
+ description: "Transfer your Product Mission and Roadmap to Design OS to automate the setup.",
168
+ prompt: "Antigravity, please sync my product plan to Design OS. Read 'agent-os/commands/initialize-design/initialize-design.md'.",
169
+ link: "",
170
+ actionLabel: "",
171
+ action: () => { }
172
+ }
173
+ } else if (!state?.design?.exported) {
174
+ step = {
175
+ phase: "Phase 2: Design System",
176
+ title: "Define Your Visuals",
177
+ description: "Defining the visuals now prevents generic UI later. Export your system when ready.",
178
+ prompt: "",
179
+ link: "",
180
+ actionLabel: "Open Design OS",
181
+ action: onOpenDesign
182
+ };
183
+ } else if (!state?.implementation?.scaffolded) {
184
+ step = {
185
+ phase: "Phase 3: Implementation",
186
+ title: "Scaffold Application",
187
+ description: "Your design is exported. Now, scaffold the production app with the design system.",
188
+ prompt: "Antigravity, scaffold the implementation. Read 'agent-os/commands/scaffold-implementation/scaffold-implementation.md'.",
189
+ link: "",
190
+ actionLabel: "",
191
+ action: () => { }
192
+ };
193
+ } else if (state?.specs?.length === 0) {
194
+ step = {
195
+ phase: "Phase 4: Feature Specs",
196
+ title: "Shape Your First Spec",
197
+ description: "Your app is ready! Create a spec for the first feature in your roadmap.",
198
+ prompt: "Antigravity, let's shape the spec for a new feature. Please read 'agent-os/commands/shape-spec/shape-spec.md'.",
199
+ link: "",
200
+ actionLabel: "",
201
+ action: () => { }
202
+ };
203
+ } else {
204
+ const incompleteSpec = state?.specs?.find(s => s.tasks.completed < s.tasks.total || !s.tasks.exists);
205
+ if (incompleteSpec) {
206
+ step = {
207
+ phase: "Phase 4: Feature Specs",
208
+ title: `Implement '${incompleteSpec.name}'`,
209
+ description: `Active spec detected. Write the code!`,
210
+ prompt: `Antigravity, implement the tasks for '${incompleteSpec.name}'. Read commands/implement-tasks/implement-tasks.md.`,
211
+ link: "",
212
+ actionLabel: "",
213
+ action: () => { }
214
+ };
215
+ } else if (state?.product?.roadmap?.nextItem && !state?.product?.roadmap?.isBoilerplate) {
216
+ const rawName = state?.product?.roadmap?.nextItem;
217
+ const cleanName = rawName.replace(/\*\*/g, '').replace(/^\d+\.\s*/, '').split('\n')[0].trim();
218
+ step = {
219
+ phase: "Phase 4: Feature Specs",
220
+ title: `Shape '${cleanName}'`,
221
+ description: `Ready to start the next feature? Shape the spec now.`,
222
+ prompt: `Antigravity, let's shape the spec for '${cleanName}'. Please read 'agent-os/commands/shape-spec/shape-spec.md'.`,
223
+ link: "",
224
+ actionLabel: "",
225
+ action: () => { }
226
+ };
227
+ } else {
228
+ step = {
229
+ phase: "Phase 4: Feature Specs",
230
+ title: "Verify or Plan Next",
231
+ description: "Verify implementation or start a new feature.",
232
+ prompt: "Antigravity, let's verify the implementation. Read 'agent-os/commands/implement-tasks/3-verify-implementation.md'.",
233
+ link: "",
234
+ actionLabel: "",
235
+ action: () => { }
236
+ }
237
+ }
238
+ }
239
+
240
+ return (
241
+ <Guidance
242
+ phase={step.phase}
243
+ title={step.title}
244
+ description={step.description}
245
+ prompt={step.prompt}
246
+ actionLabel={step.actionLabel}
247
+ onAction={step.action}
248
+ className="mb-8"
249
+ />
250
+ );
251
+ };
252
+
253
+ function App() {
254
+ const [state, setState] = useState<ProjectState | null>(null);
255
+ const [loading, setLoading] = useState(true);
256
+ const [showDesignOS, setShowDesignOS] = useState(false);
257
+ const { toast } = useToast();
258
+ const [runtimeConfig, setRuntimeConfig] = useState<any>(null);
259
+
260
+ // Edit State
261
+ const [isEditing, setIsEditing] = useState(false);
262
+ const [editContent, setEditContent] = useState("");
263
+
264
+ useEffect(() => {
265
+ fetch('/runtime-config.json')
266
+ .then(res => res.json())
267
+ .then(config => { setRuntimeConfig(config); })
268
+ .catch(err => {
269
+ setRuntimeConfig({
270
+ api: 'http://localhost:5403',
271
+ app: 'http://localhost:5402',
272
+ design: 'http://localhost:5400',
273
+ ports: { api: 5403, app: 5402, design: 5400 }
274
+ });
275
+ });
276
+ }, []);
277
+
278
+ const fetchStatus = async () => {
279
+ if (!runtimeConfig) return;
280
+ try {
281
+ const res = await axios.get(`${runtimeConfig.api}/api/status`);
282
+ setState(res.data);
283
+ } catch (error) { console.error(error); } finally { setLoading(false); }
284
+ };
285
+
286
+ useEffect(() => {
287
+ if (runtimeConfig) {
288
+ fetchStatus();
289
+ const interval = setInterval(fetchStatus, 2000);
290
+ return () => clearInterval(interval);
291
+ }
292
+ }, [runtimeConfig]);
293
+
294
+ // Auto-close Design OS when export completes
295
+ useEffect(() => {
296
+ if (state?.design?.exported && showDesignOS) {
297
+ // Only close if we were waiting for export
298
+ // We can infer this if we are on the export step?
299
+ // Determining "waiting for export" is tricky, but generally if it becomes exported while open, it's a good time to close or notify.
300
+ // Let's notify first.
301
+ toast({ title: "Export Complete!", description: "Proceed to Implementation phase.", type: "success" });
302
+ setShowDesignOS(false);
303
+ }
304
+ }, [state?.design?.exported]);
305
+
306
+ const copyToClipboard = (text: string) => {
307
+ navigator.clipboard.writeText(text);
308
+ toast({ title: "Prompt Copied!", type: "success" });
309
+ };
310
+
311
+ const [viewingFile, setViewingFile] = useState<{ path: string, title: string } | null>(null);
312
+ const [fileContent, setFileContent] = useState<string | null>(null);
313
+ const [creatingSpec, setCreatingSpec] = useState(false);
314
+ const [deletingSpec, setDeletingSpec] = useState<string | null>(null);
315
+ const [newSpecName, setNewSpecName] = useState("");
316
+
317
+
318
+ const openFile = async (path: string, title: string) => {
319
+ setViewingFile({ path, title });
320
+ setFileContent("Loading...");
321
+ setIsEditing(false);
322
+ try {
323
+ const res = await axios.get(`${runtimeConfig.api}/api/files?path=${encodeURIComponent(path)}`);
324
+ setFileContent(res.data.content);
325
+ setEditContent(res.data.content);
326
+ } catch (error) {
327
+ setFileContent("Error loading file.");
328
+ }
329
+ };
330
+
331
+ const saveFile = async () => {
332
+ if (!viewingFile) return;
333
+ try {
334
+ await axios.post(`${runtimeConfig.api}/api/files`, {
335
+ path: viewingFile.path,
336
+ content: editContent
337
+ });
338
+ setFileContent(editContent);
339
+ setIsEditing(false);
340
+ toast({ title: "File Saved!", type: "success" });
341
+ fetchStatus();
342
+ } catch (error) {
343
+ toast({ title: "Failed to save file", type: "error" });
344
+ }
345
+ };
346
+
347
+ if (loading) return <div className="min-h-screen bg-background text-foreground flex items-center justify-center">Loading Control Center...</div>;
348
+
349
+ if (!state) {
350
+ return (
351
+ <div className="min-h-screen bg-background text-foreground flex flex-col items-center justify-center gap-4 p-8">
352
+ <h2 className="text-xl font-semibold text-red-500">Connection Failed</h2>
353
+ <p className="text-muted-foreground text-center max-w-md">
354
+ Could not connect to the Control Center API at <code>{runtimeConfig?.api || '...'}</code>.
355
+ <br />Please ensure the backend is running.
356
+ </p>
357
+ <button
358
+ onClick={() => { setLoading(true); fetchStatus(); }}
359
+ className="px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors"
360
+ >
361
+ Retry Connection
362
+ </button>
363
+ </div>
364
+ );
365
+ }
366
+
367
+ return (
368
+ <div className="min-h-screen bg-background text-foreground font-sans flex flex-col h-screen overflow-hidden">
369
+ {[
370
+ viewingFile && (
371
+ <div className="fixed inset-0 z-[200] bg-black/50 backdrop-blur-sm flex items-center justify-center p-6 animate-in fade-in">
372
+ <div className="bg-card border border-border w-full max-w-4xl max-h-[90vh] rounded-xl shadow-2xl flex flex-col overflow-hidden animate-in zoom-in-95 duration-200">
373
+ <div className="flex items-center justify-between px-6 py-4 border-b border-border bg-muted/30">
374
+ <div className="flex items-center gap-3">
375
+ <div className="p-2 bg-primary/10 text-primary rounded-md">
376
+ <FileText size={18} />
377
+ </div>
378
+ <div>
379
+ <h3 className="font-semibold text-lg">{viewingFile.title}</h3>
380
+ <code className="text-xs text-muted-foreground font-mono">{viewingFile.path}</code>
381
+ </div>
382
+ </div>
383
+ <div className="flex items-center gap-2">
384
+ {!isEditing ? (
385
+ <button
386
+ onClick={() => setIsEditing(true)}
387
+ className="px-3 py-1.5 text-sm font-medium bg-secondary hover:bg-secondary/80 rounded-md transition-colors"
388
+ >
389
+ Edit
390
+ </button>
391
+ ) : (
392
+ <>
393
+ <button
394
+ onClick={() => setIsEditing(false)}
395
+ className="px-3 py-1.5 text-sm font-medium hover:bg-secondary rounded-md transition-colors"
396
+ >
397
+ Cancel
398
+ </button>
399
+ <button
400
+ onClick={saveFile}
401
+ className="px-3 py-1.5 text-sm font-medium bg-primary text-primary-foreground hover:bg-primary/90 rounded-md transition-colors"
402
+ >
403
+ Save Changes
404
+ </button>
405
+ </>
406
+ )}
407
+ <button onClick={() => setViewingFile(null)} className="p-2 hover:bg-secondary rounded-full transition-colors ml-2">
408
+ <X size={20} />
409
+ </button>
410
+ </div>
411
+ </div>
412
+ <div className="flex-1 overflow-y-auto p-6 bg-background">
413
+ {fileContent === "Loading..." || fileContent === "Error loading file." ? (
414
+ <div className="font-mono text-sm">{fileContent}</div>
415
+ ) : isEditing ? (
416
+ <textarea
417
+ value={editContent}
418
+ onChange={(e) => setEditContent(e.target.value)}
419
+ className="w-full h-full min-h-[500px] p-4 font-mono text-sm bg-secondary/10 border border-border rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-primary/20"
420
+ />
421
+ ) : (
422
+ <article className="prose dark:prose-invert max-w-none prose-headings:font-serif prose-headings:font-semibold prose-a:text-primary prose-code:text-primary prose-pre:bg-secondary/50 prose-pre:border prose-pre:border-border prose-p:leading-relaxed prose-p:mb-4">
423
+ <ReactMarkdown remarkPlugins={[remarkGfm, remarkBreaks]}>
424
+ {fileContent || ''}
425
+ </ReactMarkdown>
426
+ </article>
427
+ )}
428
+ </div>
429
+ </div>
430
+ </div>
431
+ ),
432
+ creatingSpec && (
433
+ <div className="fixed inset-0 z-[200] bg-black/50 backdrop-blur-sm flex items-center justify-center p-6 animate-in fade-in">
434
+ <div className="bg-card border border-border w-full max-w-md rounded-xl shadow-2xl flex flex-col overflow-hidden animate-in zoom-in-95 duration-200">
435
+ <div className="px-6 py-4 border-b border-border bg-muted/30">
436
+ <h3 className="font-semibold text-lg">New Feature Spec</h3>
437
+ <p className="text-sm text-muted-foreground">Shape a new feature for your project.</p>
438
+ </div>
439
+ <div className="p-6">
440
+ <div className="space-y-4">
441
+ <div>
442
+ <label className="text-sm font-medium mb-1.5 block">Feature Name</label>
443
+ <input
444
+ autoFocus
445
+ type="text"
446
+ className="w-full px-3 py-2 rounded-md border border-input bg-background text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
447
+ placeholder="e.g. user-profile"
448
+ value={newSpecName}
449
+ onChange={(e) => setNewSpecName(e.target.value)}
450
+ onKeyDown={async (e) => {
451
+ if (e.key === 'Enter' && newSpecName.trim()) {
452
+ const name = newSpecName.trim();
453
+ if (runtimeConfig?.api) {
454
+ await axios.post(`${runtimeConfig.api}/api/scaffold/spec`, { name });
455
+ const prompt = `Antigravity, let's shape the spec for '${name}'. Read commands/shape-spec/shape-spec.md.`;
456
+ await copyToClipboard(prompt);
457
+ setNewSpecName("");
458
+ setCreatingSpec(false);
459
+ await fetchStatus();
460
+ openFile(`specs/${name}/spec.md`, `${name} Spec`);
461
+ toast({ title: "Spec created & Prompt copied!", type: 'success' });
462
+ }
463
+ }
464
+ }}
465
+ />
466
+ <p className="text-xs text-muted-foreground mt-1.5">
467
+ This will create a new directory in <code>specs/</code> and copy the prompt to your clipboard.
468
+ </p>
469
+ </div>
470
+ <div className="flex justify-end gap-2 pt-2">
471
+ <button onClick={() => { setCreatingSpec(false); setNewSpecName(""); }} className="px-4 py-2 text-sm font-medium rounded-md hover:bg-secondary transition-colors">Cancel</button>
472
+ <button
473
+ disabled={!newSpecName.trim()}
474
+ onClick={async () => {
475
+ if (newSpecName.trim()) {
476
+ const name = newSpecName.trim();
477
+ if (runtimeConfig?.api) {
478
+ await axios.post(`${runtimeConfig.api}/api/scaffold/spec`, { name });
479
+ const prompt = `Antigravity, let's shape the spec for '${name}'. Read commands/shape-spec/shape-spec.md.`;
480
+ await copyToClipboard(prompt);
481
+ setNewSpecName("");
482
+ setCreatingSpec(false);
483
+ await fetchStatus();
484
+ openFile(`specs/${name}/spec.md`, `${name} Spec`);
485
+ toast({ title: "Spec created & Prompt copied!", type: 'success' });
486
+ }
487
+ }
488
+ }}
489
+ className="px-4 py-2 text-sm font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
490
+ >
491
+ Create Spec
492
+ </button>
493
+ </div>
494
+ </div>
495
+ </div>
496
+ </div>
497
+ </div>
498
+ ),
499
+ deletingSpec && (
500
+ <div className="fixed inset-0 z-[200] bg-black/50 backdrop-blur-sm flex items-center justify-center p-6 animate-in fade-in">
501
+ <div className="bg-card border border-border w-full max-w-sm rounded-xl shadow-2xl flex flex-col overflow-hidden animate-in zoom-in-95 duration-200">
502
+ <div className="p-6">
503
+ <div className="flex items-center gap-3 mb-4 text-red-500">
504
+ <div className="p-2 bg-red-100 dark:bg-red-900/20 rounded-full">
505
+ <Trash2 size={24} />
506
+ </div>
507
+ <h3 className="font-semibold text-lg text-foreground">Delete Spec?</h3>
508
+ </div>
509
+ <p className="text-muted-foreground mb-6">
510
+ Are you sure you want to delete <strong>{deletingSpec}</strong>? This action cannot be undone and will permanently delete the spec files.
511
+ </p>
512
+ <div className="flex justify-end gap-2">
513
+ <button onClick={() => setDeletingSpec(null)} className="px-4 py-2 text-sm font-medium rounded-md hover:bg-secondary transition-colors">Cancel</button>
514
+ <button
515
+ onClick={async () => {
516
+ await axios.delete(`${runtimeConfig.api}/api/specs/${deletingSpec}`);
517
+ setDeletingSpec(null);
518
+ fetchStatus();
519
+ }}
520
+ className="px-4 py-2 text-sm font-medium rounded-md bg-red-500 hover:bg-red-600 text-white transition-colors"
521
+ >
522
+ Delete Spec
523
+ </button>
524
+ </div>
525
+ </div>
526
+ </div>
527
+ </div>
528
+ )
529
+ ]}
530
+
531
+ {showDesignOS && (
532
+ <div className="fixed inset-0 z-[100] bg-background flex flex-col animate-fade-in w-screen h-screen">
533
+ <div className="h-14 border-b border-border flex items-center justify-between px-6 bg-card shrink-0 shadow-sm z-50">
534
+ <div className="flex items-center gap-3">
535
+ <div className="p-1.5 bg-sidebar-primary/10 text-sidebar-primary rounded-md">
536
+ <Layers size={20} />
537
+ </div>
538
+ <h2 className="font-semibold text-lg text-foreground">Design OS</h2>
539
+ <span className="text-muted-foreground text-sm border-l border-border pl-3">Design System & Handoff</span>
540
+ </div>
541
+ <div className="flex items-center gap-3">
542
+ <button
543
+ onClick={() => setShowDesignOS(false)}
544
+ className="p-2 text-muted-foreground hover:text-foreground hover:bg-secondary rounded-md transition-colors"
545
+ title="Close Design OS"
546
+ >
547
+ <X size={20} />
548
+ </button>
549
+ </div>
550
+ </div>
551
+ <div className="flex-1 bg-background relative">
552
+ <iframe
553
+ src={`http://localhost:5400?theme=${localStorage.getItem('theme') || 'system'}`}
554
+ className="absolute inset-0 w-full h-full border-none"
555
+ title="Design OS"
556
+ allow="clipboard-read; clipboard-write"
557
+ />
558
+ </div>
559
+ </div >
560
+ )
561
+ }
562
+
563
+ <header className="px-8 py-6 flex justify-between items-end border-b border-border bg-card/50 backdrop-blur-sm sticky top-0 z-10 shrink-0">
564
+ <div className="flex flex-col gap-4">
565
+ <h1 className="text-2xl font-semibold text-foreground tracking-tight flex items-center gap-3">
566
+ <div className="w-8 h-8 rounded-lg bg-primary flex items-center justify-center text-primary-foreground">
567
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" className="w-5 h-5">
568
+ <path d="M50 20 L75 80 L50 65 L25 80 Z" fill="currentColor" stroke="none" />
569
+ </svg>
570
+ </div>
571
+ Mission Control
572
+ </h1>
573
+ <p className="text-muted-foreground font-mono text-xs opacity-70">Project: {state?.projectRoot}</p>
574
+ </div>
575
+ <div className="flex gap-3 items-center">
576
+ <ThemeToggle onThemeChange={(theme: string) => {
577
+ const iframe = document.querySelector('iframe[title="Design OS"]') as HTMLIFrameElement;
578
+ if (iframe?.contentWindow) {
579
+ iframe.contentWindow.postMessage({ type: 'THEME_CHANGE', theme }, '*');
580
+ }
581
+ }} />
582
+ <button className={`px-4 py-2 border rounded-lg flex items-center gap-2 transition text-sm font-medium ${showDesignOS ? 'bg-primary text-primary-foreground border-primary' : 'bg-background hover:bg-secondary border-border text-foreground'}`}
583
+ onClick={() => setShowDesignOS(!showDesignOS)}>
584
+ <Layers size={16} /> {showDesignOS ? 'Close Design OS' : 'Open Design OS'}
585
+ </button>
586
+ </div>
587
+ </header>
588
+
589
+ <div className="p-8 overflow-y-auto flex-1 bg-background/50">
590
+ <div className="max-w-7xl mx-auto">
591
+ {state && <NextStepCard state={state} onOpenDesign={() => setShowDesignOS(true)} />}
592
+
593
+ <main className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
594
+ {/* Phase 1: Product Strategy */}
595
+ <section className="bg-card border border-border rounded-xl p-5 shadow-sm h-full flex flex-col transition-all hover:shadow-md hover:border-border/80">
596
+ <div className="flex items-center gap-3 mb-5 pb-4 border-b border-border/50">
597
+ <div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg">
598
+ <LayoutDashboard size={20} />
599
+ </div>
600
+ <h2 className="text-lg font-semibold">1. Product Strategy</h2>
601
+ </div>
602
+
603
+ <div className="space-y-3 flex-1">
604
+ <StatusItem label="Mission" status={state?.product?.mission} icon={<FileText size={16} />} onClick={() => openFile('product/mission.md', 'Product Mission')} />
605
+ <StatusItem label="Roadmap" status={state?.product?.roadmap} icon={<CheckSquare size={16} />} onClick={() => openFile('product/roadmap.md', 'Product Roadmap')} />
606
+ <StatusItem label="Tech Stack" status={state?.product?.techStack} icon={<Code size={16} />} onClick={() => openFile('product/tech-stack.md', 'Tech Stack')} />
607
+ </div>
608
+
609
+ <div className="mt-6">
610
+ <PromptButton
611
+ label={state?.product?.mission?.exists && !state?.product?.mission?.isBoilerplate && state?.product?.roadmap?.exists && !state?.product?.roadmap?.isBoilerplate && state?.product?.techStack?.exists && !state?.product?.techStack?.isBoilerplate ? "Next: Design System" : "Plan Product"}
612
+ prompt={state?.product?.mission?.exists && !state?.product?.mission?.isBoilerplate && state?.product?.roadmap?.exists && !state?.product?.roadmap?.isBoilerplate && state?.product?.techStack?.exists && !state?.product?.techStack?.isBoilerplate ? "Antigravity, please sync my product plan to Design OS. Read 'agent-os/commands/initialize-design/initialize-design.md'." : "Antigravity, let's start Phase 1: Product Planning. Please read 'agent-os/commands/plan-product/plan-product.md' and guide me."}
613
+ onClick={copyToClipboard}
614
+ primary={state?.product?.mission?.exists && !state?.product?.mission?.isBoilerplate && state?.product?.roadmap?.exists && !state?.product?.roadmap?.isBoilerplate && state?.product?.techStack?.exists && !state?.product?.techStack?.isBoilerplate}
615
+ />
616
+ </div>
617
+ </section>
618
+
619
+ {/* Phase 2: Design */}
620
+ <section className="bg-card border border-border rounded-xl p-5 shadow-sm h-full flex flex-col transition-all hover:shadow-md hover:border-border/80">
621
+ <div className="flex items-center gap-3 mb-5 pb-4 border-b border-border/50">
622
+ <div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg">
623
+ <Layers size={20} />
624
+ </div>
625
+ <h2 className="text-lg font-semibold">2. Design System</h2>
626
+ </div>
627
+
628
+ <div className="space-y-4 flex-1">
629
+ <div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
630
+ <span className="text-foreground font-medium text-sm flex items-center gap-2"><RefreshCw size={16} className="text-muted-foreground" /> Data Sync</span>
631
+ {state?.design?.initialized ? (
632
+ <span className="text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 px-2 py-0.5 rounded text-xs font-medium border border-emerald-200 dark:border-emerald-800">Synced</span>
633
+ ) : (
634
+ <span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">Pending</span>
635
+ )}
636
+ </div>
637
+
638
+ <div className="grid grid-cols-2 gap-2">
639
+ <div className={`p-2 rounded-lg border text-center ${state?.design?.tokens ? 'bg-emerald-50/50 border-emerald-200 text-emerald-700 dark:bg-emerald-900/20 dark:border-emerald-800 dark:text-emerald-300' : 'bg-secondary/20 border-border/50 text-muted-foreground'}`}>
640
+ <span className="text-xs font-medium block mb-1">Tokens</span>
641
+ {state?.design?.tokens ? <CheckSquare size={14} className="mx-auto" /> : <span className="block w-2 h-2 rounded-full bg-stone-300 mx-auto mt-1" />}
642
+ </div>
643
+ <div className={`p-2 rounded-lg border text-center ${state?.design?.shell ? 'bg-emerald-50/50 border-emerald-200 text-emerald-700 dark:bg-emerald-900/20 dark:border-emerald-800 dark:text-emerald-300' : 'bg-secondary/20 border-border/50 text-muted-foreground'}`}>
644
+ <span className="text-xs font-medium block mb-1">App Shell</span>
645
+ {state?.design?.shell ? <CheckSquare size={14} className="mx-auto" /> : <span className="block w-2 h-2 rounded-full bg-stone-300 mx-auto mt-1" />}
646
+ </div>
647
+ </div>
648
+
649
+ <div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
650
+ <span className="text-foreground font-medium text-sm flex items-center gap-2"><ArrowRight size={16} className="text-muted-foreground" /> Export</span>
651
+ {state?.design?.exported ? (
652
+ <span className="text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 px-2 py-0.5 rounded text-xs font-medium border border-emerald-200 dark:border-emerald-800">Done</span>
653
+ ) : state?.design?.exists ? (
654
+ <span className="text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 px-2 py-0.5 rounded text-xs font-medium border border-emerald-200 dark:border-emerald-800">Ready</span>
655
+ ) : (
656
+ <span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">Pending</span>
657
+ )}
658
+ </div>
659
+ </div>
660
+
661
+ <div className="mt-6 flex gap-2">
662
+ {!state?.design?.initialized && (
663
+ <PromptButton
664
+ label="Sync Data"
665
+ prompt="Antigravity, please sync my product plan to Design OS. Read 'agent-os/commands/initialize-design/initialize-design.md'."
666
+ onClick={copyToClipboard}
667
+ primary
668
+ />
669
+ )}
670
+ </div>
671
+ </section>
672
+
673
+ {/* Phase 3: Implementation */}
674
+ <section className="bg-card border border-border rounded-xl p-5 shadow-sm h-full flex flex-col transition-all hover:shadow-md hover:border-border/80">
675
+ <div className="flex items-center gap-3 mb-5 pb-4 border-b border-border/50">
676
+ <div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg">
677
+ <Package size={20} />
678
+ </div>
679
+ <h2 className="text-lg font-semibold">3. Implementation</h2>
680
+ </div>
681
+
682
+ <div className="space-y-4 flex-1">
683
+ <div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
684
+ <span className="text-foreground font-medium text-sm flex items-center gap-2"><Layout size={16} className="text-muted-foreground" /> Scaffold</span>
685
+ {state?.implementation?.scaffolded ? (
686
+ <span className="text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 px-2 py-0.5 rounded text-xs font-medium border border-emerald-200 dark:border-emerald-800">Done</span>
687
+ ) : (
688
+ <span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">Pending</span>
689
+ )}
690
+ </div>
691
+ </div>
692
+
693
+ <div className="mt-6">
694
+ <PromptButton
695
+ label="Scaffold App"
696
+ prompt="Antigravity, scaffold the implementation. Read 'agent-os/commands/scaffold-implementation/scaffold-implementation.md'."
697
+ onClick={copyToClipboard}
698
+ />
699
+
700
+ {state?.implementation?.scaffolded && state?.design?.exportPrompts?.oneShot && (
701
+ <div className="mt-3 pt-3 border-t border-border/50">
702
+ <p className="text-xs text-muted-foreground mb-2 font-medium">Implementation Options:</p>
703
+ <div className="space-y-2">
704
+ <PromptButton
705
+ label="Option A: One-Shot (All)"
706
+ prompt={`Antigravity, implement the app. Read 'product-plan/prompts/one-shot-prompt.md'.`}
707
+ onClick={copyToClipboard}
708
+ small
709
+ primary
710
+ />
711
+ {state?.design?.exportPrompts?.section && (
712
+ <PromptButton
713
+ label="Option B: Incremental (Section)"
714
+ prompt={`Antigravity, implement a section. Read 'product-plan/prompts/section-prompt.md'.`}
715
+ onClick={copyToClipboard}
716
+ small
717
+ />
718
+ )}
719
+ </div>
720
+ </div>
721
+ )}
722
+ </div>
723
+ </section>
724
+
725
+ {/* Phase 4: Specs & Implementation */}
726
+ <section className="bg-card border border-border rounded-xl p-5 shadow-sm col-span-1 md:col-span-2 lg:col-span-1 h-full flex flex-col transition-all hover:shadow-md hover:border-border/80">
727
+ <div className="flex items-center justify-between mb-5 pb-4 border-b border-border/50">
728
+ <div className="flex items-center gap-3">
729
+ <div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg">
730
+ <Code size={20} />
731
+ </div>
732
+ <h2 className="text-lg font-semibold">4. Feature Specs</h2>
733
+ </div>
734
+ <button className="text-xs bg-secondary hover:bg-secondary/80 border border-border px-3 py-2 rounded-md flex items-center gap-1.5 font-medium transition cursor-pointer"
735
+ onClick={() => setCreatingSpec(true)}>
736
+ <Plus size={14} /> New
737
+ </button>
738
+ </div>
739
+
740
+ <div className="space-y-3 flex-1 overflow-y-auto max-h-[300px] pr-1">
741
+ {state?.specs?.map(spec => (
742
+ <div key={spec.name} className="bg-secondary/20 p-4 rounded-lg border border-border/50 hover:border-border transition-colors group relative">
743
+ <button
744
+ onClick={(e) => {
745
+ e.stopPropagation();
746
+ setDeletingSpec(spec.name);
747
+ }}
748
+ className="absolute top-4 right-4 text-muted-foreground hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all p-1"
749
+ title="Delete Spec"
750
+ >
751
+ <Trash2 size={16} />
752
+ </button>
753
+ <h3 className="font-medium text-base mb-3 flex items-center gap-2">
754
+ <WalletCards size={16} className="text-muted-foreground" />
755
+ {spec.name}
756
+ </h3>
757
+ <div className="space-y-2 mb-4">
758
+ <StatusItem label="Spec" status={spec.spec} icon={<FileText size={14} />} small onClick={() => openFile(`specs/${spec.name}/spec.md`, `${spec.name} Spec`)} />
759
+ <StatusItem label="Tasks" status={spec.tasks} icon={<CheckSquare size={14} />} small onClick={() => openFile(`specs/${spec.name}/tasks.md`, `${spec.name} Tasks`)} />
760
+ </div>
761
+ <div className="flex gap-2">
762
+ <PromptButton
763
+ label="Shape"
764
+ prompt={`Antigravity, let's shape the spec for '${spec.name}'. Read commands/shape-spec/shape-spec.md.`}
765
+ onClick={copyToClipboard}
766
+ small
767
+ />
768
+ <PromptButton
769
+ label="Implement"
770
+ prompt={`Antigravity, implement the tasks for '${spec.name}'. Read commands/implement-tasks/implement-tasks.md.`}
771
+ onClick={copyToClipboard}
772
+ small
773
+ primary
774
+ />
775
+ </div>
776
+ </div>
777
+ ))}
778
+
779
+ {/* Pending Roadmap Items */}
780
+ {state?.product?.roadmap?.items?.filter(item =>
781
+ !item.completed &&
782
+ !state?.product?.roadmap?.isBoilerplate &&
783
+ !state?.specs?.some(s => s.name.toLowerCase() === item.name.toLowerCase().trim().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, ''))
784
+ ).map(item => (
785
+ <div key={item.name} className="border border-dashed border-border/60 p-4 rounded-lg bg-secondary/5 hover:bg-secondary/10 transition-colors">
786
+ <h3 className="font-medium text-base mb-2 flex items-center gap-2 text-muted-foreground">
787
+ <div className="w-2 h-2 rounded-full bg-stone-300" />
788
+ {item.name}
789
+ </h3>
790
+ <p className="text-xs text-muted-foreground mb-4 pl-4">Pending in Roadmap</p>
791
+ <div className="pl-4">
792
+ <button
793
+ onClick={() => {
794
+ setNewSpecName(item.name.toLowerCase().replace(/\s+/g, '-'));
795
+ setCreatingSpec(true);
796
+ }}
797
+ className="text-xs bg-background hover:bg-secondary border border-border px-3 py-1.5 rounded-md flex items-center gap-1.5 font-medium transition cursor-pointer"
798
+ >
799
+ <Plus size={12} /> Shape Spec
800
+ </button>
801
+ </div>
802
+ </div>
803
+ ))}
804
+
805
+ {state?.specs?.length === 0 && (!state?.product?.roadmap?.items || state?.product?.roadmap?.items?.length === 0) && (
806
+ <div className="text-muted-foreground italic p-8 text-center text-sm border border-dashed border-border rounded-lg bg-secondary/10">
807
+ No specs found yet.<br />Create one to start coding.
808
+ </div>
809
+ )}
810
+ </div>
811
+ </section>
812
+ </main>
813
+ </div>
814
+ </div>
815
+ <footer className="h-8 border-t border-border/40 bg-card px-4 flex items-center gap-6 text-[10px] font-medium text-muted-foreground fixed bottom-0 w-full z-50">
816
+ <div className="flex items-center gap-2">
817
+ <div className={`w-1.5 h-1.5 rounded-full ${state?.services?.api ? 'bg-emerald-500 shadow-[0_0_6px_rgba(16,185,129,0.4)]' : 'bg-red-500 animate-pulse'}`} />
818
+ <span>Control Center API ({runtimeConfig?.ports?.api || 5403})</span>
819
+ </div>
820
+ <div className="h-3 w-[1px] bg-border" />
821
+ <div className="flex items-center gap-2">
822
+ <div className={`w-1.5 h-1.5 rounded-full ${state?.services?.design ? 'bg-emerald-500 shadow-[0_0_6px_rgba(16,185,129,0.4)]' : 'bg-red-500'}`} />
823
+ <span>Design OS ({runtimeConfig?.ports?.design || 5400})</span>
824
+ </div>
825
+ <div className="h-3 w-[1px] bg-border" />
826
+ <div className="flex items-center gap-2">
827
+ <div className={`w-1.5 h-1.5 rounded-full ${state?.services?.app ? 'bg-emerald-500 shadow-[0_0_6px_rgba(16,185,129,0.4)]' : 'bg-red-500'}`} />
828
+ <span>App ({runtimeConfig?.ports?.app || 5402})</span>
829
+ </div>
830
+ </footer>
831
+ </div >
832
+ );
833
+ }
834
+
835
+ export default App;