@theproductguy/create-mission-control 1.0.17 → 1.0.26

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 (51) hide show
  1. package/package.json +2 -2
  2. package/src/template/agent-os/WORKFLOW.md +139 -0
  3. package/src/template/agent-os/commands/adapt/adapt.md +189 -0
  4. package/src/template/agent-os/commands/animate/animate.md +184 -0
  5. package/src/template/agent-os/commands/audit/audit.md +123 -0
  6. package/src/template/agent-os/commands/bolder/bolder.md +126 -0
  7. package/src/template/agent-os/commands/clarify/clarify.md +173 -0
  8. package/src/template/agent-os/commands/colorize/colorize.md +152 -0
  9. package/src/template/agent-os/commands/critique/critique.md +112 -0
  10. package/src/template/agent-os/commands/delight/delight.md +311 -0
  11. package/src/template/agent-os/commands/design-screen/design-screen.md +5 -0
  12. package/src/template/agent-os/commands/design-shell/design-shell.md +5 -0
  13. package/src/template/agent-os/commands/design-tokens/design-tokens.md +5 -0
  14. package/src/template/agent-os/commands/extract/extract.md +88 -0
  15. package/src/template/agent-os/commands/harden/harden.md +351 -0
  16. package/src/template/agent-os/commands/impeccable/impeccable.md +163 -0
  17. package/src/template/agent-os/commands/normalize/normalize.md +61 -0
  18. package/src/template/agent-os/commands/onboard/onboard.md +236 -0
  19. package/src/template/agent-os/commands/optimize/optimize.md +262 -0
  20. package/src/template/agent-os/commands/plan-product/3-create-roadmap.md +7 -3
  21. package/src/template/agent-os/commands/polish/polish.md +196 -0
  22. package/src/template/agent-os/commands/quieter/quieter.md +112 -0
  23. package/src/template/agent-os/commands/simplify/simplify.md +131 -0
  24. package/src/template/agent-os/commands/teach-impeccable/teach-impeccable.md +67 -0
  25. package/src/template/control-center/backend/index.js +1 -1
  26. package/src/template/control-center/frontend/src/App.tsx +91 -840
  27. package/src/template/control-center/frontend/src/components/DesignOSOverlay.tsx +38 -0
  28. package/src/template/control-center/frontend/src/components/Guidance.tsx +62 -0
  29. package/src/template/control-center/frontend/src/components/NextStepCard.tsx +115 -0
  30. package/src/template/control-center/frontend/src/components/PromptButton.tsx +43 -0
  31. package/src/template/control-center/frontend/src/components/StatusItem.tsx +38 -0
  32. package/src/template/control-center/frontend/src/components/modals/CreateSpecModal.tsx +78 -0
  33. package/src/template/control-center/frontend/src/components/modals/DeleteSpecModal.tsx +42 -0
  34. package/src/template/control-center/frontend/src/components/modals/FileEditorModal.tsx +87 -0
  35. package/src/template/control-center/frontend/src/components/modals/SettingsModal.tsx +46 -0
  36. package/src/template/control-center/frontend/src/components/ui/ToastContext.tsx +1 -1
  37. package/src/template/control-center/frontend/src/contexts/IdeContext.tsx +6 -0
  38. package/src/template/control-center/frontend/src/hooks/useFileEditor.ts +42 -0
  39. package/src/template/control-center/frontend/src/hooks/useProjectState.ts +45 -0
  40. package/src/template/control-center/frontend/src/index.css +26 -0
  41. package/src/template/control-center/frontend/src/types.ts +65 -0
  42. package/src/template/control-center/frontend/tailwind.config.js +15 -3
  43. package/src/template/control-center/product/design-system/QA/audit-report.md +34 -0
  44. package/src/template/control-center/product/mission.md +38 -0
  45. package/src/template/control-center/product/roadmap.md +10 -0
  46. package/src/template/design-system/src/lib/product-loader.ts +6 -0
  47. package/src/template/package-lock.json +2756 -134
  48. package/src/template/package.json +2 -2
  49. package/src/template/agent-os-ui/_package.json +0 -54
  50. package/src/template/agent-os-ui/package.json +0 -54
  51. package/src/template/control-center/frontend/src/components/ThemeToggle.tsx +0 -64
@@ -1,303 +1,41 @@
1
- import { useEffect, useState, createContext, useContext } from 'react';
2
- import axios from 'axios';
3
- import { Layout, Layers, FileText, Code, CheckSquare, RefreshCw, ArrowRight, X, Plus, Trash2, WalletCards, LayoutDashboard, Copy, Package, Play, Settings } from 'lucide-react';
1
+ import { useState, useEffect } from 'react';
2
+ import { LayoutDashboard, Layers, Code, Package, Settings, RefreshCw, FileText, CheckSquare, Plus, ArrowRight, Layout, Trash2, WalletCards } from 'lucide-react';
4
3
  import { useToast } from './components/ui/ToastContext';
5
4
  // @ts-ignore
6
5
  import { ThemeToggle } from '@theproductguy/agent-os-ui';
7
6
  import '@theproductguy/agent-os-ui/style.css';
8
- import ReactMarkdown from 'react-markdown';
9
- import remarkGfm from 'remark-gfm';
10
- import remarkBreaks from 'remark-breaks';
11
7
 
12
- // Types
13
- interface Status {
14
- exists: boolean;
15
- completed: number;
16
- total: number;
17
- nextItem?: string | null;
18
- items?: { name: string; completed: boolean }[];
19
- isBoilerplate?: boolean;
20
- }
21
- interface ProductStatus {
22
- mission: Status;
23
- roadmap: Status;
24
- techStack: Status;
25
- }
26
- interface Spec {
27
- name: string;
28
- spec: Status;
29
- tasks: Status;
30
- }
31
- interface ProjectState {
32
- product: ProductStatus;
33
- design: {
34
- exists: boolean;
35
- initialized: boolean;
36
- tokens: boolean;
37
- shell: boolean;
38
- exported: boolean;
39
- exportPrompts?: {
40
- oneShot: boolean;
41
- section: boolean;
42
- };
43
- qa?: {
44
- audit: boolean;
45
- polish: boolean;
46
- };
47
- };
48
- implementation: {
49
- scaffolded: boolean;
50
- tests?: {
51
- count: number;
52
- hasTests: boolean;
53
- };
54
- coverage?: number | null;
55
- specs?: {
56
- total: number;
57
- completed: number;
58
- };
59
- git?: {
60
- initialized: boolean;
61
- branch: string | null;
62
- uncommitted: number;
63
- lastCommit: string | null;
64
- };
65
- };
66
- specs: Spec[];
67
- services?: {
68
- api: boolean;
69
- design: boolean;
70
- app: boolean;
71
- };
72
- projectRoot: string;
73
- }
74
-
75
- const Guidance = ({ phase, title, description, prompt, actionLabel, onAction, className }: any) => (
76
- <div className={`bg-card border border-border rounded-xl p-6 shadow-sm ${className}`}>
77
- <div className="text-xs font-semibold tracking-wider text-muted-foreground uppercase mb-2">
78
- {phase}
79
- </div>
80
- <div className="flex flex-col md:flex-row gap-6 items-start justify-between">
81
- <div className="max-w-xl">
82
- <h2 className="text-2xl font-bold text-foreground mb-2">{title}</h2>
83
- <p className="text-muted-foreground leading-relaxed">
84
- {description}
85
- </p>
86
- </div>
87
- {(prompt || actionLabel) && (
88
- <div className="flex items-center gap-3 shrink-0">
89
- {prompt && (
90
- <button
91
- onClick={() => {
92
- navigator.clipboard.writeText(prompt);
93
- const toast = document.createElement('div');
94
- toast.textContent = "Prompt Copied!";
95
- 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";
96
- document.body.appendChild(toast);
97
- setTimeout(() => toast.remove(), 2000);
98
- }}
99
- 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"
100
- >
101
- <Copy size={16} />
102
- Copy Prompt
103
- </button>
104
- )}
105
- {actionLabel && (
106
- <button
107
- onClick={onAction}
108
- 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"
109
- >
110
- {actionLabel} <ArrowRight size={16} />
111
- </button>
112
- )}
113
- </div>
114
- )}
115
- </div>
116
- </div>
117
- );
118
-
119
- const IdeContext = createContext({ scheme: 'vscode', setScheme: (_s: string) => { } });
120
-
121
- function StatusItem({ label, status, icon, small, onClick }: any) {
122
- if (!status) return null;
123
- const isComplete = status.exists && (status.total > 0 ? status.completed === status.total : true);
124
-
125
- return (
126
- <div
127
- onClick={onClick}
128
- 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' : ''}`}
129
- >
130
- <div className="flex items-center gap-2 text-muted-foreground group-hover:text-foreground transition-colors">
131
- {icon}
132
- <span>{label}</span>
133
- </div>
134
- <div>
135
- {status.exists && !status.isBoilerplate ? (
136
- <span className={`px-2 py-0.5 rounded-full text-xs font-medium border ${isComplete
137
- ? 'bg-emerald-50 dark:bg-emerald-900/20 text-emerald-600 border-emerald-200 dark:border-emerald-800'
138
- : 'bg-secondary text-muted-foreground border-border'
139
- }`}>
140
- {isComplete ? 'Done' : 'Pending'}
141
- </span>
142
- ) : (
143
- <span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">Pending</span>
144
- )}
145
- </div>
146
- </div>
147
- );
148
- }
149
-
150
- function PromptButton({ label, prompt, onClick, small, primary }: any) {
151
- const { scheme } = useContext(IdeContext);
152
- const deepLink = `${scheme}://vscode.executeCommand/workbench.action.chat.open?query=${encodeURIComponent(prompt)}`;
153
-
154
- return (
155
- <div className={`flex gap-1 items-center ${!small ? 'w-full' : 'flex-1'}`}>
156
- <button
157
- onClick={() => onClick(prompt)}
158
- className={`flex items-center justify-center gap-2 rounded-lg font-medium transition cursor-pointer flex-1
159
- ${small ? 'px-3 py-1.5 text-xs' : 'px-4 py-2 text-sm'}
160
- ${primary
161
- ? 'bg-primary hover:bg-primary/90 text-primary-foreground shadow-sm'
162
- : 'bg-background hover:bg-secondary border border-border text-foreground hover:text-foreground'}
163
- `}
164
- >
165
- <Copy size={small ? 12 : 14} />
166
- {label}
167
- </button>
168
-
169
- <a
170
- href={deepLink}
171
- className={`flex items-center justify-center rounded-lg border border-border bg-secondary hover:bg-secondary/80 text-foreground transition
172
- ${small ? 'w-8 h-[30px]' : 'w-10 h-[38px]'}
173
- `}
174
- title={`Run in IDE (${scheme}://)`}
175
- >
176
- <Play size={small ? 12 : 14} fill="currentColor" className="opacity-70" />
177
- </a>
178
- </div>
179
- )
180
- }
181
-
182
- const NextStepCard = ({ state, onOpenDesign }: { state: ProjectState, onOpenDesign: () => void }) => {
183
- let step = { phase: "", title: "", description: "", prompt: "", link: "", actionLabel: "", action: () => { } };
184
-
185
- const isProductComplete =
186
- state?.product?.mission?.exists && !state?.product?.mission?.isBoilerplate &&
187
- state?.product?.techStack?.exists && !state?.product?.techStack?.isBoilerplate &&
188
- state?.product?.roadmap?.exists && !state?.product?.roadmap?.isBoilerplate;
8
+ // Contexts & Hooks
9
+ import { IdeContext } from './contexts/IdeContext';
10
+ import { useProjectState } from './hooks/useProjectState';
11
+ import { useFileEditor } from './hooks/useFileEditor';
189
12
 
190
- if (!isProductComplete) {
191
- step = {
192
- phase: "Phase 1: Product Strategy",
193
- title: "Plan Your Product",
194
- description: "Define your Mission, Roadmap, and Tech Stack to build a solid foundation.",
195
- prompt: "Antigravity, let's start Phase 1: Product Planning. Please read 'agent-os/commands/plan-product/plan-product.md' and guide me.",
196
- link: "",
197
- actionLabel: "",
198
- action: () => { }
199
- }
200
- } else if (!state?.design?.initialized) {
201
- step = {
202
- phase: "Phase 2: Design System",
203
- title: "Sync Product to Design OS",
204
- description: "Transfer your Product Mission and Roadmap to Design OS to automate the setup.",
205
- prompt: "Antigravity, please sync my product plan to Design OS. Read 'agent-os/commands/initialize-design/initialize-design.md'.",
206
- link: "",
207
- actionLabel: "",
208
- action: () => { }
209
- }
210
- } else if (!state?.design?.exported) {
211
- step = {
212
- phase: "Phase 2: Design System",
213
- title: "Define Your Visuals",
214
- description: "Defining the visuals now prevents generic UI later. Export your system when ready.",
215
- prompt: "",
216
- link: "",
217
- actionLabel: "Open Design OS",
218
- action: onOpenDesign
219
- };
220
- } else if (!state?.implementation?.scaffolded) {
221
- step = {
222
- phase: "Phase 3: Implementation",
223
- title: "Scaffold Application",
224
- description: "Your design is exported. Now, scaffold the production app with the design system.",
225
- prompt: "Antigravity, scaffold the implementation. Read 'agent-os/commands/scaffold-implementation/scaffold-implementation.md'.",
226
- link: "",
227
- actionLabel: "",
228
- action: () => { }
229
- };
230
- } else if (state?.specs?.length === 0) {
231
- step = {
232
- phase: "Phase 4: Feature Specs",
233
- title: "Shape Your First Spec",
234
- description: "Your app is ready! Create a spec for the first feature in your roadmap.",
235
- prompt: "Antigravity, let's shape the spec for a new feature. Please read 'agent-os/commands/shape-spec/shape-spec.md'.",
236
- link: "",
237
- actionLabel: "",
238
- action: () => { }
239
- };
240
- } else {
241
- const incompleteSpec = state?.specs?.find(s => s.tasks.completed < s.tasks.total || !s.tasks.exists);
242
- if (incompleteSpec) {
243
- step = {
244
- phase: "Phase 4: Feature Specs",
245
- title: `Implement '${incompleteSpec.name}'`,
246
- description: `Active spec detected. Write the code!`,
247
- prompt: `Antigravity, implement the tasks for '${incompleteSpec.name}'. Read commands/implement-tasks/implement-tasks.md.`,
248
- link: "",
249
- actionLabel: "",
250
- action: () => { }
251
- };
252
- } else if (state?.product?.roadmap?.nextItem && !state?.product?.roadmap?.isBoilerplate) {
253
- const rawName = state?.product?.roadmap?.nextItem;
254
- const cleanName = rawName.replace(/\*\*/g, '').replace(/^\d+\.\s*/, '').split('\n')[0].trim();
255
- step = {
256
- phase: "Phase 4: Feature Specs",
257
- title: `Shape '${cleanName}'`,
258
- description: `Ready to start the next feature? Shape the spec now.`,
259
- prompt: `Antigravity, let's shape the spec for '${cleanName}'. Please read 'agent-os/commands/shape-spec/shape-spec.md'.`,
260
- link: "",
261
- actionLabel: "",
262
- action: () => { }
263
- };
264
- } else {
265
- step = {
266
- phase: "Phase 4: Feature Specs",
267
- title: "Verify or Plan Next",
268
- description: "Verify implementation or start a new feature.",
269
- prompt: "Antigravity, let's verify the implementation. Read 'agent-os/commands/implement-tasks/3-verify-implementation.md'.",
270
- link: "",
271
- actionLabel: "",
272
- action: () => { }
273
- }
274
- }
275
- }
13
+ // Components
14
+ import { StatusItem } from './components/StatusItem';
15
+ import { PromptButton } from './components/PromptButton';
16
+ import { NextStepCard } from './components/NextStepCard';
17
+ import { DesignOSOverlay } from './components/DesignOSOverlay';
276
18
 
277
- return (
278
- <Guidance
279
- phase={step.phase}
280
- title={step.title}
281
- description={step.description}
282
- prompt={step.prompt}
283
- actionLabel={step.actionLabel}
284
- onAction={step.action}
285
- className="mb-8"
286
- />
287
- );
288
- };
19
+ // Modals
20
+ import { SettingsModal } from './components/modals/SettingsModal';
21
+ import { FileEditorModal } from './components/modals/FileEditorModal';
22
+ import { CreateSpecModal } from './components/modals/CreateSpecModal';
23
+ import { DeleteSpecModal } from './components/modals/DeleteSpecModal';
289
24
 
290
25
  function App() {
291
- const [state, setState] = useState<ProjectState | null>(null);
292
- const [loading, setLoading] = useState(true);
293
- const [showDesignOS, setShowDesignOS] = useState(false);
26
+ const { state, loading, runtimeConfig, fetchStatus } = useProjectState();
27
+ const { viewingFile, fileContent, openFile, saveFile, closeFile } = useFileEditor(runtimeConfig, fetchStatus);
294
28
  const { toast } = useToast();
295
- const [runtimeConfig, setRuntimeConfig] = useState<any>(null);
296
29
 
297
- // IDE Settings
30
+ // UI State
31
+ const [showDesignOS, setShowDesignOS] = useState(false);
298
32
  const [showSettings, setShowSettings] = useState(false);
299
33
  const [ideScheme, setIdeScheme] = useState('vscode');
300
34
 
35
+ // Modal State
36
+ const [creatingSpec, setCreatingSpec] = useState(false);
37
+ const [deletingSpec, setDeletingSpec] = useState<string | null>(null);
38
+
301
39
  useEffect(() => {
302
40
  const saved = localStorage.getItem('ide_scheme');
303
41
  if (saved) setIdeScheme(saved);
@@ -308,344 +46,31 @@ function App() {
308
46
  localStorage.setItem('ide_scheme', scheme);
309
47
  };
310
48
 
311
- // Edit State
312
- const [isEditing, setIsEditing] = useState(false);
313
- const [editContent, setEditContent] = useState("");
314
-
315
- useEffect(() => {
316
- fetch('/runtime-config.json')
317
- .then(res => res.json())
318
- .then(config => { setRuntimeConfig(config); })
319
- .catch(() => {
320
- setRuntimeConfig({
321
- api: 'http://localhost:5403',
322
- app: 'http://localhost:5402',
323
- design: 'http://localhost:5400',
324
- ports: { api: 5403, app: 5402, design: 5400 }
325
- });
326
- });
327
- }, []);
328
-
329
- const fetchStatus = async () => {
330
- if (!runtimeConfig) return;
49
+ const copyToClipboard = async (text: string) => {
331
50
  try {
332
- const res = await axios.get(`${runtimeConfig.api}/api/status`);
333
- setState(res.data);
334
- } catch (error) { console.error(error); } finally { setLoading(false); }
335
- };
336
-
337
- useEffect(() => {
338
- if (runtimeConfig) {
339
- fetchStatus();
340
- const interval = setInterval(fetchStatus, 2000);
341
- return () => clearInterval(interval);
342
- }
343
- }, [runtimeConfig]);
344
-
345
- // Auto-close Design OS when export completes
346
- useEffect(() => {
347
- if (state?.design?.exported && showDesignOS) {
348
- // Only close if we were waiting for export
349
- // We can infer this if we are on the export step?
350
- // Determining "waiting for export" is tricky, but generally if it becomes exported while open, it's a good time to close or notify.
351
- // Let's notify first.
352
- toast({ title: "Export Complete!", description: "Proceed to Implementation phase.", type: "success" });
353
- setShowDesignOS(false);
354
- }
355
- }, [state?.design?.exported]);
356
-
357
- const copyToClipboard = (text: string) => {
358
- navigator.clipboard.writeText(text);
359
- toast({ title: "Prompt Copied!", type: "success" });
360
- };
361
-
362
- const [viewingFile, setViewingFile] = useState<{ path: string, title: string } | null>(null);
363
- const [fileContent, setFileContent] = useState<string | null>(null);
364
- const [creatingSpec, setCreatingSpec] = useState(false);
365
- const [deletingSpec, setDeletingSpec] = useState<string | null>(null);
366
- const [newSpecName, setNewSpecName] = useState("");
367
-
368
-
369
- const openFile = async (path: string, title: string) => {
370
- setViewingFile({ path, title });
371
- setFileContent("Loading...");
372
- setIsEditing(false);
373
- try {
374
- const res = await axios.get(`${runtimeConfig.api}/api/files?path=${encodeURIComponent(path)}`);
375
- setFileContent(res.data.content);
376
- setEditContent(res.data.content);
377
- } catch (error) {
378
- setFileContent("Error loading file.");
379
- }
380
- };
381
-
382
- const saveFile = async () => {
383
- if (!viewingFile) return;
384
- try {
385
- await axios.post(`${runtimeConfig.api}/api/files`, {
386
- path: viewingFile.path,
387
- content: editContent
388
- });
389
- setFileContent(editContent);
390
- setIsEditing(false);
391
- toast({ title: "File Saved!", type: "success" });
392
- fetchStatus();
393
- } catch (error) {
394
- toast({ title: "Failed to save file", type: "error" });
51
+ await navigator.clipboard.writeText(text);
52
+ toast({ title: "Prompt Copied!", type: "success" });
53
+ } catch (err) {
54
+ console.error('Failed to copy:', err);
55
+ toast({ title: "Failed to copy", description: "Please copy manually", type: "error" });
395
56
  }
396
57
  };
397
58
 
398
59
  if (loading) return <div className="min-h-screen bg-background text-foreground flex items-center justify-center">Loading Control Center...</div>;
399
-
400
- if (!state) {
401
- return (
402
- <div className="min-h-screen bg-background text-foreground flex flex-col items-center justify-center gap-4 p-8">
403
- <h2 className="text-xl font-semibold text-red-500">Connection Failed</h2>
404
- <p className="text-muted-foreground text-center max-w-md">
405
- Could not connect to the Control Center API at <code>{runtimeConfig?.api || '...'}</code>.
406
- <br />Please ensure the backend is running.
407
- </p>
408
- <button
409
- onClick={() => { setLoading(true); fetchStatus(); }}
410
- className="px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors"
411
- >
412
- Retry Connection
413
- </button>
414
- </div>
415
- );
416
- }
60
+ if (!state) return <div className="min-h-screen bg-background text-foreground flex items-center justify-center text-red-500">Connection Failed</div>;
417
61
 
418
62
  return (
419
63
  <IdeContext.Provider value={{ scheme: ideScheme, setScheme: updateIdeScheme }}>
420
64
  <div className="min-h-screen bg-background text-foreground font-sans flex flex-col h-screen overflow-hidden">
421
- {showSettings && (
422
- <div className="fixed inset-0 z-[200] bg-black/50 backdrop-blur-sm flex items-center justify-center p-6 animate-in fade-in">
423
- <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">
424
- <div className="p-6 border-b border-border bg-muted/30 flex items-center justify-between">
425
- <h3 className="font-semibold text-lg flex items-center gap-2"><Settings size={18} /> Settings</h3>
426
- <button onClick={() => setShowSettings(false)} className="hover:bg-secondary p-1 rounded"><X size={18} /></button>
427
- </div>
428
- <div className="p-6 space-y-4">
429
- <div>
430
- <label className="text-sm font-medium mb-2 block">IDE / Editor</label>
431
- <div className="space-y-2">
432
- {['vscode', 'cursor', 'windsurf', 'antigravity'].map((s) => (
433
- <button
434
- key={s}
435
- onClick={() => updateIdeScheme(s)}
436
- className={`w-full flex items-center justify-between px-4 py-3 rounded-lg border transition-all ${ideScheme === s
437
- ? 'bg-primary/10 border-primary text-primary'
438
- : 'bg-secondary/20 border-border hover:border-foreground/20'
439
- }`}
440
- >
441
- <span className="capitalize font-medium">{s}</span>
442
- {ideScheme === s && <div className="w-2 h-2 rounded-full bg-primary" />}
443
- </button>
444
- ))}
445
- </div>
446
- <p className="text-xs text-muted-foreground mt-3">
447
- Select the editor to open when clicking "Play".
448
- <br />Currently using: <code>{ideScheme}://</code>
449
- </p>
450
- </div>
451
- </div>
452
- </div>
453
- </div>
454
- )}
455
- {[
456
- viewingFile && (
457
- <div className="fixed inset-0 z-[200] bg-black/50 backdrop-blur-sm flex items-center justify-center p-6 animate-in fade-in">
458
- <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">
459
- <div className="flex items-center justify-between px-6 py-4 border-b border-border bg-muted/30">
460
- <div className="flex items-center gap-3">
461
- <div className="p-2 bg-primary/10 text-primary rounded-md">
462
- <FileText size={18} />
463
- </div>
464
- <div>
465
- <h3 className="font-semibold text-lg">{viewingFile.title}</h3>
466
- <code className="text-xs text-muted-foreground font-mono">{viewingFile.path}</code>
467
- </div>
468
- </div>
469
- <div className="flex items-center gap-2">
470
- {!isEditing ? (
471
- <button
472
- onClick={() => setIsEditing(true)}
473
- className="px-3 py-1.5 text-sm font-medium bg-secondary hover:bg-secondary/80 rounded-md transition-colors"
474
- >
475
- Edit
476
- </button>
477
- ) : (
478
- <>
479
- <button
480
- onClick={() => setIsEditing(false)}
481
- className="px-3 py-1.5 text-sm font-medium hover:bg-secondary rounded-md transition-colors"
482
- >
483
- Cancel
484
- </button>
485
- <button
486
- onClick={saveFile}
487
- className="px-3 py-1.5 text-sm font-medium bg-primary text-primary-foreground hover:bg-primary/90 rounded-md transition-colors"
488
- >
489
- Save Changes
490
- </button>
491
- </>
492
- )}
493
- <button onClick={() => setViewingFile(null)} className="p-2 hover:bg-secondary rounded-full transition-colors ml-2">
494
- <X size={20} />
495
- </button>
496
- </div>
497
- </div>
498
- <div className="flex-1 overflow-y-auto p-6 bg-background">
499
- {fileContent === "Loading..." || fileContent === "Error loading file." ? (
500
- <div className="font-mono text-sm">{fileContent}</div>
501
- ) : isEditing ? (
502
- <textarea
503
- value={editContent}
504
- onChange={(e) => setEditContent(e.target.value)}
505
- className="w-full h-full min-h-[500px] p-4 font-mono text-sm bg-secondary text-secondary-foreground border border-border rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-primary/20"
506
- />
507
- ) : (
508
- <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">
509
- <ReactMarkdown remarkPlugins={[remarkGfm, remarkBreaks]}>
510
- {fileContent || ''}
511
- </ReactMarkdown>
512
- </article>
513
- )}
514
- </div>
515
- </div>
516
- </div>
517
- ),
518
- creatingSpec && (
519
- <div className="fixed inset-0 z-[200] bg-black/50 backdrop-blur-sm flex items-center justify-center p-6 animate-in fade-in">
520
- <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">
521
- <div className="px-6 py-4 border-b border-border bg-muted/30">
522
- <h3 className="font-semibold text-lg">New Feature Spec</h3>
523
- <p className="text-sm text-muted-foreground">Shape a new feature for your project.</p>
524
- </div>
525
- <div className="p-6">
526
- <div className="space-y-4">
527
- <div>
528
- <label className="text-sm font-medium mb-1.5 block">Feature Name</label>
529
- <input
530
- autoFocus
531
- type="text"
532
- className="w-full px-3 py-2 rounded-md border border-input bg-secondary text-secondary-foreground text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
533
- placeholder="e.g. user-profile"
534
- value={newSpecName}
535
- onChange={(e) => setNewSpecName(e.target.value)}
536
- onKeyDown={async (e) => {
537
- if (e.key === 'Enter' && newSpecName.trim()) {
538
- const name = newSpecName.trim();
539
- if (runtimeConfig?.api) {
540
- await axios.post(`${runtimeConfig.api}/api/scaffold/spec`, { name });
541
- const prompt = `Antigravity, let's shape the spec for '${name}'. Read commands/shape-spec/shape-spec.md.`;
542
- await copyToClipboard(prompt);
543
- setNewSpecName("");
544
- setCreatingSpec(false);
545
- await fetchStatus();
546
- openFile(`specs/${name}/spec.md`, `${name} Spec`);
547
- toast({ title: "Spec created & Prompt copied!", type: 'success' });
548
- }
549
- }
550
- }}
551
- />
552
- <p className="text-xs text-muted-foreground mt-1.5">
553
- This will create a new directory in <code>specs/</code> and copy the prompt to your clipboard.
554
- </p>
555
- </div>
556
- <div className="flex justify-end gap-2 pt-2">
557
- <button onClick={() => { setCreatingSpec(false); setNewSpecName(""); }} className="px-4 py-2 text-sm font-medium rounded-md hover:bg-secondary transition-colors">Cancel</button>
558
- <button
559
- disabled={!newSpecName.trim()}
560
- onClick={async () => {
561
- if (newSpecName.trim()) {
562
- const name = newSpecName.trim();
563
- if (runtimeConfig?.api) {
564
- await axios.post(`${runtimeConfig.api}/api/scaffold/spec`, { name });
565
- const prompt = `Antigravity, let's shape the spec for '${name}'. Read commands/shape-spec/shape-spec.md.`;
566
- await copyToClipboard(prompt);
567
- setNewSpecName("");
568
- setCreatingSpec(false);
569
- await fetchStatus();
570
- openFile(`specs/${name}/spec.md`, `${name} Spec`);
571
- toast({ title: "Spec created & Prompt copied!", type: 'success' });
572
- }
573
- }
574
- }}
575
- 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"
576
- >
577
- Create Spec
578
- </button>
579
- </div>
580
- </div>
581
- </div>
582
- </div>
583
- </div>
584
- ),
585
- deletingSpec && (
586
- <div className="fixed inset-0 z-[200] bg-black/50 backdrop-blur-sm flex items-center justify-center p-6 animate-in fade-in">
587
- <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">
588
- <div className="p-6">
589
- <div className="flex items-center gap-3 mb-4 text-red-500">
590
- <div className="p-2 bg-red-100 dark:bg-red-900/20 rounded-full">
591
- <Trash2 size={24} />
592
- </div>
593
- <h3 className="font-semibold text-lg text-foreground">Delete Spec?</h3>
594
- </div>
595
- <p className="text-muted-foreground mb-6">
596
- Are you sure you want to delete <strong>{deletingSpec}</strong>? This action cannot be undone and will permanently delete the spec files.
597
- </p>
598
- <div className="flex justify-end gap-2">
599
- <button onClick={() => setDeletingSpec(null)} className="px-4 py-2 text-sm font-medium rounded-md hover:bg-secondary transition-colors">Cancel</button>
600
- <button
601
- onClick={async () => {
602
- await axios.delete(`${runtimeConfig.api}/api/specs/${deletingSpec}`);
603
- setDeletingSpec(null);
604
- fetchStatus();
605
- }}
606
- className="px-4 py-2 text-sm font-medium rounded-md bg-red-500 hover:bg-red-600 text-white transition-colors"
607
- >
608
- Delete Spec
609
- </button>
610
- </div>
611
- </div>
612
- </div>
613
- </div>
614
- )
615
- ]}
616
65
 
617
- {showDesignOS && (
618
- <div className="fixed inset-0 z-[100] bg-background flex flex-col animate-fade-in w-screen h-screen">
619
- <div className="h-14 border-b border-border flex items-center justify-between px-6 bg-card shrink-0 shadow-sm z-50">
620
- <div className="flex items-center gap-3">
621
- <div className="p-1.5 bg-sidebar-primary/10 text-sidebar-primary rounded-md">
622
- <Layers size={20} />
623
- </div>
624
- <h2 className="font-semibold text-lg text-foreground">Design OS</h2>
625
- <span className="text-muted-foreground text-sm border-l border-border pl-3">Design System & Handoff</span>
626
- </div>
627
- <div className="flex items-center gap-3">
628
- <button
629
- onClick={() => setShowDesignOS(false)}
630
- className="p-2 text-muted-foreground hover:text-foreground hover:bg-secondary rounded-md transition-colors"
631
- title="Close Design OS"
632
- >
633
- <X size={20} />
634
- </button>
635
- </div>
636
- </div>
637
- <div className="flex-1 bg-background relative">
638
- <iframe
639
- src={`http://localhost:5400?theme=${localStorage.getItem('theme') || 'system'}`}
640
- className="absolute inset-0 w-full h-full border-none"
641
- title="Design OS"
642
- allow="clipboard-read; clipboard-write"
643
- />
644
- </div>
645
- </div >
646
- )
647
- }
66
+ {/* Overlays & Modals */}
67
+ {showSettings && <SettingsModal onClose={() => setShowSettings(false)} />}
68
+ {viewingFile && <FileEditorModal viewingFile={viewingFile} content={fileContent} onClose={closeFile} onSave={saveFile} />}
69
+ {creatingSpec && <CreateSpecModal runtimeConfig={runtimeConfig} onClose={() => setCreatingSpec(false)} onSuccess={fetchStatus} openFile={openFile} />}
70
+ {deletingSpec && <DeleteSpecModal specName={deletingSpec} runtimeConfig={runtimeConfig} onClose={() => setDeletingSpec(null)} onSuccess={fetchStatus} />}
71
+ {showDesignOS && <DesignOSOverlay onClose={() => setShowDesignOS(false)} />}
648
72
 
73
+ {/* Header */}
649
74
  <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">
650
75
  <div className="flex flex-col gap-4">
651
76
  <h1 className="text-2xl font-semibold text-foreground tracking-tight flex items-center gap-3">
@@ -661,44 +86,36 @@ function App() {
661
86
  <div className="flex gap-3 items-center">
662
87
  <ThemeToggle onThemeChange={(theme: string) => {
663
88
  const iframe = document.querySelector('iframe[title="Design OS"]') as HTMLIFrameElement;
664
- if (iframe?.contentWindow) {
665
- iframe.contentWindow.postMessage({ type: 'THEME_CHANGE', theme }, '*');
666
- }
89
+ if (iframe?.contentWindow) iframe.contentWindow.postMessage({ type: 'THEME_CHANGE', theme }, '*');
667
90
  }} />
668
91
  <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'}`}
669
92
  onClick={() => setShowDesignOS(!showDesignOS)}>
670
93
  <Layers size={16} /> {showDesignOS ? 'Close Design OS' : 'Open Design OS'}
671
94
  </button>
672
- <button
673
- onClick={() => setShowSettings(true)}
674
- className="p-2 bg-background hover:bg-secondary border border-border rounded-lg text-foreground transition-colors"
675
- title="Settings"
676
- >
95
+ <button onClick={() => setShowSettings(true)} className="p-2 bg-background hover:bg-secondary border border-border rounded-lg text-foreground transition-colors" title="Settings">
677
96
  <Settings size={20} />
678
97
  </button>
679
98
  </div>
680
99
  </header>
681
100
 
101
+ {/* Main Content */}
682
102
  <div className="p-8 overflow-y-auto flex-1 bg-background/50">
683
103
  <div className="max-w-7xl mx-auto">
684
- {state && <NextStepCard state={state} onOpenDesign={() => setShowDesignOS(true)} />}
104
+ <NextStepCard state={state} onOpenDesign={() => setShowDesignOS(true)} />
685
105
 
686
106
  <main className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
107
+
687
108
  {/* Phase 1: Product Strategy */}
688
- <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">
109
+ <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 animate-in fade-in slide-in-from-bottom-4 duration-500 fill-mode-backwards" style={{ animationDelay: '100ms' }}>
689
110
  <div className="flex items-center gap-3 mb-5 pb-4 border-b border-border/50">
690
- <div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg">
691
- <LayoutDashboard size={20} />
692
- </div>
111
+ <div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg"><LayoutDashboard size={20} /></div>
693
112
  <h2 className="text-lg font-semibold">1. Product Strategy</h2>
694
113
  </div>
695
-
696
114
  <div className="space-y-3 flex-1">
697
115
  <StatusItem label="Mission" status={state?.product?.mission} icon={<FileText size={16} />} onClick={() => openFile('product/mission.md', 'Product Mission')} />
698
116
  <StatusItem label="Roadmap" status={state?.product?.roadmap} icon={<CheckSquare size={16} />} onClick={() => openFile('product/roadmap.md', 'Product Roadmap')} />
699
117
  <StatusItem label="Tech Stack" status={state?.product?.techStack} icon={<Code size={16} />} onClick={() => openFile('product/tech-stack.md', 'Tech Stack')} />
700
118
  </div>
701
-
702
119
  <div className="mt-6">
703
120
  <PromptButton
704
121
  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"}
@@ -710,228 +127,118 @@ function App() {
710
127
  </section>
711
128
 
712
129
  {/* Phase 2: Design */}
713
- <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">
130
+ <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 animate-in fade-in slide-in-from-bottom-4 duration-500 fill-mode-backwards" style={{ animationDelay: '200ms' }}>
714
131
  <div className="flex items-center gap-3 mb-5 pb-4 border-b border-border/50">
715
- <div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg">
716
- <Layers size={20} />
717
- </div>
132
+ <div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg"><Layers size={20} /></div>
718
133
  <h2 className="text-lg font-semibold">2. Design System</h2>
719
134
  </div>
720
-
721
135
  <div className="space-y-4 flex-1">
722
136
  <div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
723
137
  <span className="text-foreground font-medium text-sm flex items-center gap-2"><RefreshCw size={16} className="text-muted-foreground" /> Data Sync</span>
724
- {state?.design?.initialized ? (
725
- <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>
726
- ) : (
727
- <span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">Pending</span>
728
- )}
138
+ {state?.design?.initialized ? <span className="text-success bg-success/10 px-2 py-0.5 rounded text-xs font-medium border border-success/20">Synced</span> : <span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">Pending</span>}
729
139
  </div>
730
-
731
140
  <div className="grid grid-cols-2 gap-2">
732
- <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'}`}>
141
+ <div className={`p-2 rounded-lg border text-center ${state?.design?.tokens ? 'bg-success/10 border-success/20 text-success' : 'bg-secondary/20 border-border/50 text-muted-foreground'}`}>
733
142
  <span className="text-xs font-medium block mb-1">Tokens</span>
734
143
  {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" />}
735
144
  </div>
736
- <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'}`}>
145
+ <div className={`p-2 rounded-lg border text-center ${state?.design?.shell ? 'bg-success/10 border-success/20 text-success' : 'bg-secondary/20 border-border/50 text-muted-foreground'}`}>
737
146
  <span className="text-xs font-medium block mb-1">App Shell</span>
738
147
  {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" />}
739
148
  </div>
740
149
  </div>
741
-
742
150
  <div className="grid grid-cols-2 gap-2">
743
- <div className={`p-2 rounded-lg border text-center ${state?.design?.qa?.audit ? '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'}`}>
151
+ <div className={`p-2 rounded-lg border text-center ${state?.design?.qa?.audit ? 'bg-success/10 border-success/20 text-success' : 'bg-secondary/20 border-border/50 text-muted-foreground'}`}>
744
152
  <span className="text-xs font-medium block mb-1">Audit</span>
745
153
  {state?.design?.qa?.audit ? <CheckSquare size={14} className="mx-auto" /> : <span className="block w-2 h-2 rounded-full bg-stone-300 mx-auto mt-1" />}
746
154
  </div>
747
- <div className={`p-2 rounded-lg border text-center ${state?.design?.qa?.polish ? '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'}`}>
155
+ <div className={`p-2 rounded-lg border text-center ${state?.design?.qa?.polish ? 'bg-success/10 border-success/20 text-success' : 'bg-secondary/20 border-border/50 text-muted-foreground'}`}>
748
156
  <span className="text-xs font-medium block mb-1">Polish</span>
749
157
  {state?.design?.qa?.polish ? <CheckSquare size={14} className="mx-auto" /> : <span className="block w-2 h-2 rounded-full bg-stone-300 mx-auto mt-1" />}
750
158
  </div>
751
159
  </div>
752
-
753
160
  <div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
754
161
  <span className="text-foreground font-medium text-sm flex items-center gap-2"><ArrowRight size={16} className="text-muted-foreground" /> Export</span>
755
- {state?.design?.exported ? (
756
- <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>
757
- ) : state?.design?.exists ? (
758
- <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>
759
- ) : (
760
- <span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">Pending</span>
761
- )}
162
+ {state?.design?.exported ? <span className="text-success bg-success/10 px-2 py-0.5 rounded text-xs font-medium border border-success/20">Done</span> : state?.design?.exists ? <span className="text-success bg-success/10 px-2 py-0.5 rounded text-xs font-medium border border-success/20">Ready</span> : <span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">Pending</span>}
762
163
  </div>
763
164
  </div>
764
-
765
165
  <div className="mt-6 flex flex-col gap-2">
766
- {!state?.design?.initialized && (
767
- <PromptButton
768
- label="Sync Data"
769
- prompt="Antigravity, please sync my product plan to Design OS. Read 'agent-os/commands/initialize-design/initialize-design.md'."
770
- onClick={copyToClipboard}
771
- primary
772
- />
773
- )}
166
+ {!state?.design?.initialized && <PromptButton label="Sync Data" prompt="Antigravity, please sync my product plan to Design OS. Read 'agent-os/commands/initialize-design/initialize-design.md'." onClick={copyToClipboard} primary />}
774
167
  {state?.design?.initialized && !state?.design?.exported && (
775
168
  <div className="flex gap-2">
776
- <PromptButton
777
- label="Run Audit"
778
- prompt="Antigravity, run an audit on the design system. Read 'design-system/.gemini/commands/impeccable/audit.md'."
779
- onClick={copyToClipboard}
780
- small
781
- />
782
- <PromptButton
783
- label="Run Polish"
784
- prompt="Antigravity, run a polish pass. Read 'design-system/.gemini/commands/impeccable/polish.md'."
785
- onClick={copyToClipboard}
786
- small
787
- />
169
+ <PromptButton label="Run Audit" prompt="Antigravity, run an audit on the design system. Read 'design-system/.gemini/commands/impeccable/audit.md'." onClick={copyToClipboard} small />
170
+ <PromptButton label="Run Polish" prompt="Antigravity, run a polish pass. Read 'design-system/.gemini/commands/impeccable/polish.md'." onClick={copyToClipboard} small />
788
171
  </div>
789
172
  )}
790
173
  </div>
791
174
  </section>
792
175
 
793
176
  {/* Phase 3: Feature Specs */}
794
- <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">
177
+ <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 animate-in fade-in slide-in-from-bottom-4 duration-500 fill-mode-backwards" style={{ animationDelay: '300ms' }}>
795
178
  <div className="flex items-center justify-between mb-5 pb-4 border-b border-border/50">
796
179
  <div className="flex items-center gap-3">
797
- <div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg">
798
- <Code size={20} />
799
- </div>
180
+ <div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg"><Code size={20} /></div>
800
181
  <h2 className="text-lg font-semibold">3. Feature Specs</h2>
801
182
  </div>
802
- <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"
803
- onClick={() => setCreatingSpec(true)}>
183
+ <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" onClick={() => setCreatingSpec(true)}>
804
184
  <Plus size={14} /> New
805
185
  </button>
806
186
  </div>
807
-
808
187
  <div className="space-y-3 flex-1 overflow-y-auto max-h-[300px] pr-1">
809
- {/* Auto-generate from Roadmap after Design Export */}
810
- {(state?.product?.roadmap?.items?.filter(item =>
811
- !item.completed &&
812
- !state?.product?.roadmap?.isBoilerplate
813
- ) ?? []).length > 0 && !state?.specs?.length && (
814
- <div className="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 p-4 rounded-lg mb-3">
815
- <p className="text-sm text-blue-700 dark:text-blue-300 mb-2">
816
- Ready to generate specs from roadmap items?
817
- </p>
818
- <PromptButton
819
- label="Generate All Specs"
820
- prompt={`Antigravity, generate feature specs from the roadmap items. For each item in 'agent-os/product/roadmap.md' that is not yet completed, create a spec folder in 'agent-os/specs/[feature-name]/' with spec.md and tasks.md files. Context: 1. Read 'product-plan/product-overview.md' for data model/flows. 2. Read 'design-system/design-tokens.md' and 'design-system/app-shell.md' for UI/UX constraints. 3. Use the shape-spec command: Read 'agent-os/commands/shape-spec/shape-spec.md'.`}
821
- onClick={copyToClipboard}
822
- small
823
- primary
824
- />
825
- </div>
826
- )}
188
+ {(state?.product?.roadmap?.items?.filter(item => !item.completed && !state?.product?.roadmap?.isBoilerplate) ?? []).length > 0 && !state?.specs?.length && (
189
+ <div className="bg-info/10 border border-info/20 p-4 rounded-lg mb-3">
190
+ <p className="text-sm text-info mb-2">Ready to generate specs from roadmap items?</p>
191
+ <PromptButton label="Generate All Specs" prompt={`Antigravity, generate feature specs from the roadmap items. For each item in 'agent-os/product/roadmap.md' that is not yet completed, create a spec folder in 'agent-os/specs/[feature-name]/' with spec.md and tasks.md files. Context: 1. Read 'product-plan/product-overview.md' for data model/flows. 2. Read 'design-system/design-tokens.md' and 'design-system/app-shell.md' for UI/UX constraints. 3. Use the shape-spec command: Read 'agent-os/commands/shape-spec/shape-spec.md'.`} onClick={copyToClipboard} small primary />
192
+ </div>
193
+ )}
827
194
  {state?.specs?.map(spec => (
828
195
  <div key={spec.name} className="bg-secondary/20 p-4 rounded-lg border border-border/50 hover:border-border transition-colors group relative">
829
- <button
830
- onClick={(e) => {
831
- e.stopPropagation();
832
- setDeletingSpec(spec.name);
833
- }}
834
- className="absolute top-4 right-4 text-muted-foreground hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all p-1"
835
- title="Delete Spec"
836
- >
837
- <Trash2 size={16} />
838
- </button>
839
- <h3 className="font-medium text-base mb-3 flex items-center gap-2">
840
- <WalletCards size={16} className="text-muted-foreground" />
841
- {spec.name}
842
- </h3>
196
+ <button onClick={(e) => { e.stopPropagation(); setDeletingSpec(spec.name); }} className="absolute top-4 right-4 text-muted-foreground hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all p-1" title="Delete Spec"><Trash2 size={16} /></button>
197
+ <h3 className="font-medium text-base mb-3 flex items-center gap-2"><WalletCards size={16} className="text-muted-foreground" />{spec.name}</h3>
843
198
  <div className="space-y-2 mb-4">
844
199
  <StatusItem label="Spec" status={spec.spec} icon={<FileText size={14} />} small onClick={() => openFile(`specs/${spec.name}/spec.md`, `${spec.name} Spec`)} />
845
200
  <StatusItem label="Tasks" status={spec.tasks} icon={<CheckSquare size={14} />} small onClick={() => openFile(`specs/${spec.name}/tasks.md`, `${spec.name} Tasks`)} />
846
201
  </div>
847
202
  <div className="flex gap-2">
848
- <PromptButton
849
- label="Shape"
850
- prompt={`Antigravity, let's shape the spec for '${spec.name}'. Read commands/shape-spec/shape-spec.md.`}
851
- onClick={copyToClipboard}
852
- small
853
- />
854
- <PromptButton
855
- label="Implement"
856
- prompt={`Antigravity, implement the tasks for '${spec.name}'. Read commands/implement-tasks/implement-tasks.md.`}
857
- onClick={copyToClipboard}
858
- small
859
- primary
860
- />
203
+ <PromptButton label="Shape" prompt={`Antigravity, let's shape the spec for '${spec.name}'. Read commands/shape-spec/shape-spec.md.`} onClick={copyToClipboard} small />
204
+ <PromptButton label="Implement" prompt={`Antigravity, implement the tasks for '${spec.name}'. Read commands/implement-tasks/implement-tasks.md.`} onClick={copyToClipboard} small primary />
861
205
  </div>
862
206
  </div>
863
207
  ))}
864
-
865
- {/* Pending Roadmap Items */}
866
- {/* Pending Roadmap Items */}
867
- {state?.product?.roadmap?.items?.filter(item =>
868
- !item.completed &&
869
- !state?.product?.roadmap?.isBoilerplate &&
870
- !state?.specs?.some(s => s.name.toLowerCase().includes(item.name.toLowerCase().trim().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '')))
871
- ).map(item => (
208
+ {state?.product?.roadmap?.items?.filter(item => !item.completed && !state?.product?.roadmap?.isBoilerplate && !state?.specs?.some(s => s.name.toLowerCase().includes(item.name.toLowerCase().trim().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '')))).map(item => (
872
209
  <div key={item.name} className="border border-dashed border-border/60 p-4 rounded-lg bg-secondary/5 hover:bg-secondary/10 transition-colors">
873
210
  <div className="flex justify-between items-start mb-2">
874
- <h3 className="font-medium text-base flex items-center gap-2 text-muted-foreground w-full">
875
- <div className="w-2 h-2 rounded-full bg-stone-300 shrink-0" />
876
- <span className="truncate" title={item.name}>{item.name}</span>
877
- </h3>
211
+ <h3 className="font-medium text-base flex items-center gap-2 text-muted-foreground w-full"><div className="w-2 h-2 rounded-full bg-stone-300 shrink-0" /><span className="truncate" title={item.name}>{item.name}</span></h3>
878
212
  <span className="text-[10px] uppercase tracking-wider font-semibold text-muted-foreground bg-secondary px-1.5 py-0.5 rounded shrink-0 ml-2">Planned</span>
879
213
  </div>
880
- <p className="text-xs text-muted-foreground mb-4 pl-4 opacity-70">
881
- Defined in Product Roadmap. Ready to spec.
882
- </p>
214
+ <p className="text-xs text-muted-foreground mb-4 pl-4 opacity-70">Defined in Product Roadmap. Ready to spec.</p>
883
215
  <div className="pl-4">
884
- <button
885
- onClick={() => {
886
- setNewSpecName(item.name.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, ''));
887
- setCreatingSpec(true);
888
- }}
889
- 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 w-full justify-center group"
890
- >
216
+ <button onClick={() => { setCreatingSpec(true); }} 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 w-full justify-center group">
891
217
  <Plus size={12} className="group-hover:text-primary transition-colors" /> Shape Spec
892
218
  </button>
893
219
  </div>
894
220
  </div>
895
221
  ))}
896
-
897
- {/* Empty State */}
898
222
  {(!state?.specs?.length && (!state?.product?.roadmap?.items?.length || state?.product?.roadmap?.isBoilerplate)) && (
899
223
  <div className="border border-dashed border-border/60 p-8 rounded-lg text-center h-40 flex flex-col items-center justify-center">
900
224
  <p className="text-muted-foreground text-sm mb-3">No specs found yet.</p>
901
- <button
902
- onClick={() => setCreatingSpec(true)}
903
- className="text-sm px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors"
904
- >
905
- Create your first spec
906
- </button>
225
+ <button onClick={() => setCreatingSpec(true)} className="text-sm px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors">Create your first spec</button>
907
226
  </div>
908
227
  )}
909
-
910
-
911
228
  </div>
912
229
  </section>
913
230
 
914
231
  {/* Phase 4: Implementation */}
915
- <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">
232
+ <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 animate-in fade-in slide-in-from-bottom-4 duration-500 fill-mode-backwards" style={{ animationDelay: '400ms' }}>
916
233
  <div className="flex items-center gap-3 mb-5 pb-4 border-b border-border/50">
917
- <div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg">
918
- <Package size={20} />
919
- </div>
234
+ <div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg"><Package size={20} /></div>
920
235
  <h2 className="text-lg font-semibold">4. Implementation</h2>
921
236
  </div>
922
-
923
237
  <div className="space-y-3 flex-1">
924
- {/* Scaffold Status */}
925
238
  <div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
926
239
  <span className="text-foreground font-medium text-sm flex items-center gap-2"><Layout size={16} className="text-muted-foreground" /> Scaffold</span>
927
- {state?.implementation?.scaffolded ? (
928
- <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>
929
- ) : (
930
- <span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">Pending</span>
931
- )}
240
+ {state?.implementation?.scaffolded ? <span className="text-success bg-success/10 px-2 py-0.5 rounded text-xs font-medium border border-success/20">Done</span> : <span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">Pending</span>}
932
241
  </div>
933
-
934
- {/* Git Status */}
935
242
  {state?.implementation?.git?.initialized && (
936
243
  <div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
937
244
  <span className="text-foreground font-medium text-sm flex items-center gap-2">
@@ -939,97 +246,41 @@ function App() {
939
246
  {state.implementation.git.branch || 'main'}
940
247
  </span>
941
248
  <div className="flex items-center gap-2">
942
- {state.implementation.git.uncommitted > 0 && (
943
- <span className="text-amber-600 bg-amber-50 dark:bg-amber-900/20 px-2 py-0.5 rounded text-xs font-medium border border-amber-200 dark:border-amber-800">
944
- {state.implementation.git.uncommitted} uncommitted
945
- </span>
946
- )}
947
- {state.implementation.git.uncommitted === 0 && (
948
- <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">Clean</span>
949
- )}
249
+ {state.implementation.git.uncommitted > 0 ? <span className="text-warning bg-warning/10 px-2 py-0.5 rounded text-xs font-medium border border-warning/20">{state.implementation.git.uncommitted} uncommitted</span> : <span className="text-success bg-success/10 px-2 py-0.5 rounded text-xs font-medium border border-success/20">Clean</span>}
950
250
  </div>
951
251
  </div>
952
252
  )}
953
-
954
- {/* Tests & Coverage */}
955
253
  {state?.implementation?.scaffolded && (
956
254
  <div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
957
- <span className="text-foreground font-medium text-sm flex items-center gap-2">
958
- <CheckSquare size={16} className="text-muted-foreground" /> Tests
959
- </span>
255
+ <span className="text-foreground font-medium text-sm flex items-center gap-2"><CheckSquare size={16} className="text-muted-foreground" /> Tests</span>
960
256
  <div className="flex items-center gap-2">
961
- {state?.implementation?.tests?.hasTests ? (
962
- <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">
963
- {state.implementation.tests.count} files
964
- </span>
965
- ) : (
966
- <span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">No tests</span>
967
- )}
257
+ {state?.implementation?.tests?.hasTests ? <span className="text-success bg-success/10 px-2 py-0.5 rounded text-xs font-medium border border-success/20">{state.implementation.tests.count} files</span> : <span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">No tests</span>}
968
258
  {state?.implementation?.coverage !== null && state?.implementation?.coverage !== undefined && (
969
- <span className={`px-2 py-0.5 rounded text-xs font-medium border ${state.implementation.coverage >= 80
970
- ? 'text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 border-emerald-200 dark:border-emerald-800'
971
- : state.implementation.coverage >= 50
972
- ? 'text-amber-600 bg-amber-50 dark:bg-amber-900/20 border-amber-200 dark:border-amber-800'
973
- : 'text-red-600 bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800'
974
- }`}>
975
- {state.implementation.coverage}% coverage
976
- </span>
259
+ <span className={`px-2 py-0.5 rounded text-xs font-medium border ${state.implementation.coverage >= 80 ? 'text-success bg-success/10 border-success/20' : state.implementation.coverage >= 50 ? 'text-warning bg-warning/10 border-warning/20' : 'text-destructive bg-destructive/10 border-destructive/20'}`}>{state.implementation.coverage}% coverage</span>
977
260
  )}
978
261
  </div>
979
262
  </div>
980
263
  )}
981
-
982
- {/* Spec Progress */}
983
264
  {state?.implementation?.specs && state.implementation.specs.total > 0 && (
984
265
  <div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
985
- <span className="text-foreground font-medium text-sm flex items-center gap-2">
986
- <FileText size={16} className="text-muted-foreground" /> Specs
987
- </span>
988
- <span className={`px-2 py-0.5 rounded text-xs font-medium border ${state.implementation.specs.completed === state.implementation.specs.total
989
- ? 'text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 border-emerald-200 dark:border-emerald-800'
990
- : 'text-blue-600 bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800'
991
- }`}>
992
- {state.implementation.specs.completed}/{state.implementation.specs.total} complete
993
- </span>
266
+ <span className="text-foreground font-medium text-sm flex items-center gap-2"><FileText size={16} className="text-muted-foreground" /> Specs</span>
267
+ <span className={`px-2 py-0.5 rounded text-xs font-medium border ${state.implementation.specs.completed === state.implementation.specs.total ? 'text-success bg-success/10 border-success/20' : 'text-info bg-info/10 border-info/20'}`}>{state.implementation.specs.completed}/{state.implementation.specs.total} complete</span>
994
268
  </div>
995
269
  )}
996
270
  </div>
997
-
998
271
  <div className="mt-6">
999
272
  {!state?.implementation?.scaffolded ? (
1000
- <PromptButton
1001
- label="Scaffold App"
1002
- prompt="Antigravity, scaffold the implementation. Read 'agent-os/commands/scaffold-implementation/scaffold-implementation.md'."
1003
- onClick={copyToClipboard}
1004
- />
1005
- ) : (
1006
- <div className="p-3 bg-secondary/30 rounded-lg border border-border/50 text-sm text-muted-foreground text-center italic mb-4">
1007
- App scaffolded. Ready for implementation.
1008
- </div>
1009
- )}
1010
-
273
+ <PromptButton label="Scaffold App" prompt="Antigravity, scaffold the implementation. Read 'agent-os/commands/scaffold-implementation/scaffold-implementation.md'." onClick={copyToClipboard} />
274
+ ) : <div className="p-3 bg-secondary/30 rounded-lg border border-border/50 text-sm text-muted-foreground text-center italic mb-4">App scaffolded. Ready for implementation.</div>}
1011
275
  {state?.implementation?.scaffolded && state?.design?.exportPrompts?.oneShot && (
1012
276
  <div className="pt-3 border-t border-border/50">
1013
277
  <p className="text-xs text-muted-foreground mb-2 font-medium">Implementation Options:</p>
1014
278
  <div className="space-y-2">
1015
- <PromptButton
1016
- label="Option A: One-Shot (All)"
1017
- prompt={`Antigravity, implement the app. Read 'product-plan/prompts/one-shot-prompt.md'. If Context7 is configured, use it to verify library documentation.`}
1018
- onClick={copyToClipboard}
1019
- small
1020
- primary
1021
- />
279
+ <PromptButton label="Option A: One-Shot (All)" prompt={`Antigravity, implement the app. Read 'product-plan/prompts/one-shot-prompt.md'. If Context7 is configured, use it to verify library documentation.`} onClick={copyToClipboard} small primary />
1022
280
  {state?.design?.exportPrompts?.section && (
1023
281
  <div className="relative">
1024
- <PromptButton
1025
- label="Option B: Incremental (Section)"
1026
- prompt={`Antigravity, implement a section. Read 'product-plan/prompts/section-prompt.md'. Use Context7 for latest docs if available.`}
1027
- onClick={copyToClipboard}
1028
- small
1029
- />
1030
- <p className="text-[10px] text-muted-foreground mt-1.5 text-center leading-tight">
1031
- Build by spec from <strong>Feature Specs</strong> &larr;
1032
- </p>
282
+ <PromptButton label="Option B: Incremental (Section)" prompt={`Antigravity, implement a section. Read 'product-plan/prompts/section-prompt.md'. Use Context7 for latest docs if available.`} onClick={copyToClipboard} small />
283
+ <p className="text-[10px] text-muted-foreground mt-1.5 text-center leading-tight">Build by spec from <strong>Feature Specs</strong> &larr;</p>
1033
284
  </div>
1034
285
  )}
1035
286
  </div>
@@ -1043,17 +294,17 @@ function App() {
1043
294
  </div>
1044
295
  <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">
1045
296
  <div className="flex items-center gap-2">
1046
- <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'}`} />
297
+ <div className={`w-1.5 h-1.5 rounded-full ${state?.services?.api ? 'bg-success shadow-[0_0_6px_rgba(var(--success))]' : 'bg-destructive animate-pulse'}`} />
1047
298
  <span>Control Center API ({runtimeConfig?.ports?.api || 5403})</span>
1048
299
  </div>
1049
300
  <div className="h-3 w-[1px] bg-border" />
1050
301
  <div className="flex items-center gap-2">
1051
- <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'}`} />
302
+ <div className={`w-1.5 h-1.5 rounded-full ${state?.services?.design ? 'bg-success shadow-[0_0_6px_rgba(var(--success))]' : 'bg-destructive'}`} />
1052
303
  <span>Design OS ({runtimeConfig?.ports?.design || 5400})</span>
1053
304
  </div>
1054
305
  <div className="h-3 w-[1px] bg-border" />
1055
306
  <div className="flex items-center gap-2">
1056
- <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'}`} />
307
+ <div className={`w-1.5 h-1.5 rounded-full ${state?.services?.app ? 'bg-success shadow-[0_0_6px_rgba(var(--success))]' : 'bg-destructive'}`} />
1057
308
  <span>App ({runtimeConfig?.ports?.app || 5402})</span>
1058
309
  </div>
1059
310
  </footer>