@theproductguy/create-mission-control 1.0.17 → 1.0.25
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.
- package/package.json +1 -1
- package/src/template/agent-os/WORKFLOW.md +139 -0
- package/src/template/agent-os/commands/adapt/adapt.md +189 -0
- package/src/template/agent-os/commands/animate/animate.md +184 -0
- package/src/template/agent-os/commands/audit/audit.md +123 -0
- package/src/template/agent-os/commands/bolder/bolder.md +126 -0
- package/src/template/agent-os/commands/clarify/clarify.md +173 -0
- package/src/template/agent-os/commands/colorize/colorize.md +152 -0
- package/src/template/agent-os/commands/critique/critique.md +112 -0
- package/src/template/agent-os/commands/delight/delight.md +311 -0
- package/src/template/agent-os/commands/design-screen/design-screen.md +5 -0
- package/src/template/agent-os/commands/design-shell/design-shell.md +5 -0
- package/src/template/agent-os/commands/design-tokens/design-tokens.md +5 -0
- package/src/template/agent-os/commands/extract/extract.md +88 -0
- package/src/template/agent-os/commands/harden/harden.md +351 -0
- package/src/template/agent-os/commands/impeccable/impeccable.md +163 -0
- package/src/template/agent-os/commands/normalize/normalize.md +61 -0
- package/src/template/agent-os/commands/onboard/onboard.md +236 -0
- package/src/template/agent-os/commands/optimize/optimize.md +262 -0
- package/src/template/agent-os/commands/plan-product/3-create-roadmap.md +7 -3
- package/src/template/agent-os/commands/polish/polish.md +196 -0
- package/src/template/agent-os/commands/quieter/quieter.md +112 -0
- package/src/template/agent-os/commands/simplify/simplify.md +131 -0
- package/src/template/agent-os/commands/teach-impeccable/teach-impeccable.md +67 -0
- package/src/template/control-center/backend/index.js +1 -1
- package/src/template/control-center/frontend/src/App.tsx +85 -839
- package/src/template/control-center/frontend/src/components/DesignOSOverlay.tsx +38 -0
- package/src/template/control-center/frontend/src/components/Guidance.tsx +56 -0
- package/src/template/control-center/frontend/src/components/NextStepCard.tsx +115 -0
- package/src/template/control-center/frontend/src/components/PromptButton.tsx +43 -0
- package/src/template/control-center/frontend/src/components/StatusItem.tsx +38 -0
- package/src/template/control-center/frontend/src/components/modals/CreateSpecModal.tsx +73 -0
- package/src/template/control-center/frontend/src/components/modals/DeleteSpecModal.tsx +42 -0
- package/src/template/control-center/frontend/src/components/modals/FileEditorModal.tsx +87 -0
- package/src/template/control-center/frontend/src/components/modals/SettingsModal.tsx +46 -0
- package/src/template/control-center/frontend/src/components/ui/ToastContext.tsx +1 -1
- package/src/template/control-center/frontend/src/contexts/IdeContext.tsx +6 -0
- package/src/template/control-center/frontend/src/hooks/useFileEditor.ts +42 -0
- package/src/template/control-center/frontend/src/hooks/useProjectState.ts +45 -0
- package/src/template/control-center/frontend/src/index.css +26 -0
- package/src/template/control-center/frontend/src/types.ts +65 -0
- package/src/template/control-center/frontend/tailwind.config.js +15 -3
- package/src/template/control-center/product/design-system/QA/audit-report.md +34 -0
- package/src/template/control-center/product/mission.md +38 -0
- package/src/template/control-center/product/roadmap.md +10 -0
- package/src/template/design-system/src/lib/product-loader.ts +6 -0
- package/src/template/package-lock.json +2756 -134
- package/src/template/package.json +2 -2
- package/src/template/agent-os-ui/_package.json +0 -54
- package/src/template/agent-os-ui/package.json +0 -54
- package/src/template/control-center/frontend/src/components/ThemeToggle.tsx +0 -64
|
@@ -1,303 +1,41 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
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
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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: () => { } };
|
|
8
|
+
// Contexts & Hooks
|
|
9
|
+
import { IdeContext } from './contexts/IdeContext';
|
|
10
|
+
import { useProjectState } from './hooks/useProjectState';
|
|
11
|
+
import { useFileEditor } from './hooks/useFileEditor';
|
|
184
12
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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';
|
|
189
18
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
}
|
|
276
|
-
|
|
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
|
|
292
|
-
const
|
|
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
|
-
//
|
|
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,26 @@ 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;
|
|
331
|
-
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
49
|
const copyToClipboard = (text: string) => {
|
|
358
50
|
navigator.clipboard.writeText(text);
|
|
359
51
|
toast({ title: "Prompt Copied!", type: "success" });
|
|
360
52
|
};
|
|
361
53
|
|
|
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" });
|
|
395
|
-
}
|
|
396
|
-
};
|
|
397
|
-
|
|
398
54
|
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
|
-
}
|
|
55
|
+
if (!state) return <div className="min-h-screen bg-background text-foreground flex items-center justify-center text-red-500">Connection Failed</div>;
|
|
417
56
|
|
|
418
57
|
return (
|
|
419
58
|
<IdeContext.Provider value={{ scheme: ideScheme, setScheme: updateIdeScheme }}>
|
|
420
59
|
<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
60
|
|
|
617
|
-
{
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
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
|
-
}
|
|
61
|
+
{/* Overlays & Modals */}
|
|
62
|
+
{showSettings && <SettingsModal onClose={() => setShowSettings(false)} />}
|
|
63
|
+
{viewingFile && <FileEditorModal viewingFile={viewingFile} content={fileContent} onClose={closeFile} onSave={saveFile} />}
|
|
64
|
+
{creatingSpec && <CreateSpecModal runtimeConfig={runtimeConfig} onClose={() => setCreatingSpec(false)} onSuccess={fetchStatus} openFile={openFile} />}
|
|
65
|
+
{deletingSpec && <DeleteSpecModal specName={deletingSpec} runtimeConfig={runtimeConfig} onClose={() => setDeletingSpec(null)} onSuccess={fetchStatus} />}
|
|
66
|
+
{showDesignOS && <DesignOSOverlay onClose={() => setShowDesignOS(false)} />}
|
|
648
67
|
|
|
68
|
+
{/* Header */}
|
|
649
69
|
<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
70
|
<div className="flex flex-col gap-4">
|
|
651
71
|
<h1 className="text-2xl font-semibold text-foreground tracking-tight flex items-center gap-3">
|
|
@@ -661,44 +81,36 @@ function App() {
|
|
|
661
81
|
<div className="flex gap-3 items-center">
|
|
662
82
|
<ThemeToggle onThemeChange={(theme: string) => {
|
|
663
83
|
const iframe = document.querySelector('iframe[title="Design OS"]') as HTMLIFrameElement;
|
|
664
|
-
if (iframe?.contentWindow) {
|
|
665
|
-
iframe.contentWindow.postMessage({ type: 'THEME_CHANGE', theme }, '*');
|
|
666
|
-
}
|
|
84
|
+
if (iframe?.contentWindow) iframe.contentWindow.postMessage({ type: 'THEME_CHANGE', theme }, '*');
|
|
667
85
|
}} />
|
|
668
86
|
<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
87
|
onClick={() => setShowDesignOS(!showDesignOS)}>
|
|
670
88
|
<Layers size={16} /> {showDesignOS ? 'Close Design OS' : 'Open Design OS'}
|
|
671
89
|
</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
|
-
>
|
|
90
|
+
<button onClick={() => setShowSettings(true)} className="p-2 bg-background hover:bg-secondary border border-border rounded-lg text-foreground transition-colors" title="Settings">
|
|
677
91
|
<Settings size={20} />
|
|
678
92
|
</button>
|
|
679
93
|
</div>
|
|
680
94
|
</header>
|
|
681
95
|
|
|
96
|
+
{/* Main Content */}
|
|
682
97
|
<div className="p-8 overflow-y-auto flex-1 bg-background/50">
|
|
683
98
|
<div className="max-w-7xl mx-auto">
|
|
684
|
-
|
|
99
|
+
<NextStepCard state={state} onOpenDesign={() => setShowDesignOS(true)} />
|
|
685
100
|
|
|
686
101
|
<main className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
102
|
+
|
|
687
103
|
{/* 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">
|
|
104
|
+
<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
105
|
<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>
|
|
106
|
+
<div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg"><LayoutDashboard size={20} /></div>
|
|
693
107
|
<h2 className="text-lg font-semibold">1. Product Strategy</h2>
|
|
694
108
|
</div>
|
|
695
|
-
|
|
696
109
|
<div className="space-y-3 flex-1">
|
|
697
110
|
<StatusItem label="Mission" status={state?.product?.mission} icon={<FileText size={16} />} onClick={() => openFile('product/mission.md', 'Product Mission')} />
|
|
698
111
|
<StatusItem label="Roadmap" status={state?.product?.roadmap} icon={<CheckSquare size={16} />} onClick={() => openFile('product/roadmap.md', 'Product Roadmap')} />
|
|
699
112
|
<StatusItem label="Tech Stack" status={state?.product?.techStack} icon={<Code size={16} />} onClick={() => openFile('product/tech-stack.md', 'Tech Stack')} />
|
|
700
113
|
</div>
|
|
701
|
-
|
|
702
114
|
<div className="mt-6">
|
|
703
115
|
<PromptButton
|
|
704
116
|
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 +122,118 @@ function App() {
|
|
|
710
122
|
</section>
|
|
711
123
|
|
|
712
124
|
{/* 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">
|
|
125
|
+
<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
126
|
<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>
|
|
127
|
+
<div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg"><Layers size={20} /></div>
|
|
718
128
|
<h2 className="text-lg font-semibold">2. Design System</h2>
|
|
719
129
|
</div>
|
|
720
|
-
|
|
721
130
|
<div className="space-y-4 flex-1">
|
|
722
131
|
<div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
|
|
723
132
|
<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
|
-
)}
|
|
133
|
+
{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
134
|
</div>
|
|
730
|
-
|
|
731
135
|
<div className="grid grid-cols-2 gap-2">
|
|
732
|
-
<div className={`p-2 rounded-lg border text-center ${state?.design?.tokens ? 'bg-
|
|
136
|
+
<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
137
|
<span className="text-xs font-medium block mb-1">Tokens</span>
|
|
734
138
|
{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
139
|
</div>
|
|
736
|
-
<div className={`p-2 rounded-lg border text-center ${state?.design?.shell ? 'bg-
|
|
140
|
+
<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
141
|
<span className="text-xs font-medium block mb-1">App Shell</span>
|
|
738
142
|
{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
143
|
</div>
|
|
740
144
|
</div>
|
|
741
|
-
|
|
742
145
|
<div className="grid grid-cols-2 gap-2">
|
|
743
|
-
<div className={`p-2 rounded-lg border text-center ${state?.design?.qa?.audit ? 'bg-
|
|
146
|
+
<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
147
|
<span className="text-xs font-medium block mb-1">Audit</span>
|
|
745
148
|
{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
149
|
</div>
|
|
747
|
-
<div className={`p-2 rounded-lg border text-center ${state?.design?.qa?.polish ? 'bg-
|
|
150
|
+
<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
151
|
<span className="text-xs font-medium block mb-1">Polish</span>
|
|
749
152
|
{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
153
|
</div>
|
|
751
154
|
</div>
|
|
752
|
-
|
|
753
155
|
<div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
|
|
754
156
|
<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
|
-
)}
|
|
157
|
+
{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
158
|
</div>
|
|
763
159
|
</div>
|
|
764
|
-
|
|
765
160
|
<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
|
-
)}
|
|
161
|
+
{!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
162
|
{state?.design?.initialized && !state?.design?.exported && (
|
|
775
163
|
<div className="flex gap-2">
|
|
776
|
-
<PromptButton
|
|
777
|
-
|
|
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
|
-
/>
|
|
164
|
+
<PromptButton label="Run Audit" prompt="Antigravity, run an audit on the design system. Read 'design-system/.gemini/commands/impeccable/audit.md'." onClick={copyToClipboard} small />
|
|
165
|
+
<PromptButton label="Run Polish" prompt="Antigravity, run a polish pass. Read 'design-system/.gemini/commands/impeccable/polish.md'." onClick={copyToClipboard} small />
|
|
788
166
|
</div>
|
|
789
167
|
)}
|
|
790
168
|
</div>
|
|
791
169
|
</section>
|
|
792
170
|
|
|
793
171
|
{/* 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">
|
|
172
|
+
<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
173
|
<div className="flex items-center justify-between mb-5 pb-4 border-b border-border/50">
|
|
796
174
|
<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>
|
|
175
|
+
<div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg"><Code size={20} /></div>
|
|
800
176
|
<h2 className="text-lg font-semibold">3. Feature Specs</h2>
|
|
801
177
|
</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)}>
|
|
178
|
+
<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
179
|
<Plus size={14} /> New
|
|
805
180
|
</button>
|
|
806
181
|
</div>
|
|
807
|
-
|
|
808
182
|
<div className="space-y-3 flex-1 overflow-y-auto max-h-[300px] pr-1">
|
|
809
|
-
{
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
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
|
-
)}
|
|
183
|
+
{(state?.product?.roadmap?.items?.filter(item => !item.completed && !state?.product?.roadmap?.isBoilerplate) ?? []).length > 0 && !state?.specs?.length && (
|
|
184
|
+
<div className="bg-info/10 border border-info/20 p-4 rounded-lg mb-3">
|
|
185
|
+
<p className="text-sm text-info mb-2">Ready to generate specs from roadmap items?</p>
|
|
186
|
+
<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 />
|
|
187
|
+
</div>
|
|
188
|
+
)}
|
|
827
189
|
{state?.specs?.map(spec => (
|
|
828
190
|
<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
|
-
|
|
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>
|
|
191
|
+
<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>
|
|
192
|
+
<h3 className="font-medium text-base mb-3 flex items-center gap-2"><WalletCards size={16} className="text-muted-foreground" />{spec.name}</h3>
|
|
843
193
|
<div className="space-y-2 mb-4">
|
|
844
194
|
<StatusItem label="Spec" status={spec.spec} icon={<FileText size={14} />} small onClick={() => openFile(`specs/${spec.name}/spec.md`, `${spec.name} Spec`)} />
|
|
845
195
|
<StatusItem label="Tasks" status={spec.tasks} icon={<CheckSquare size={14} />} small onClick={() => openFile(`specs/${spec.name}/tasks.md`, `${spec.name} Tasks`)} />
|
|
846
196
|
</div>
|
|
847
197
|
<div className="flex gap-2">
|
|
848
|
-
<PromptButton
|
|
849
|
-
|
|
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
|
-
/>
|
|
198
|
+
<PromptButton label="Shape" prompt={`Antigravity, let's shape the spec for '${spec.name}'. Read commands/shape-spec/shape-spec.md.`} onClick={copyToClipboard} small />
|
|
199
|
+
<PromptButton label="Implement" prompt={`Antigravity, implement the tasks for '${spec.name}'. Read commands/implement-tasks/implement-tasks.md.`} onClick={copyToClipboard} small primary />
|
|
861
200
|
</div>
|
|
862
201
|
</div>
|
|
863
202
|
))}
|
|
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 => (
|
|
203
|
+
{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
204
|
<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
205
|
<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>
|
|
206
|
+
<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
207
|
<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
208
|
</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>
|
|
209
|
+
<p className="text-xs text-muted-foreground mb-4 pl-4 opacity-70">Defined in Product Roadmap. Ready to spec.</p>
|
|
883
210
|
<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
|
-
>
|
|
211
|
+
<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
212
|
<Plus size={12} className="group-hover:text-primary transition-colors" /> Shape Spec
|
|
892
213
|
</button>
|
|
893
214
|
</div>
|
|
894
215
|
</div>
|
|
895
216
|
))}
|
|
896
|
-
|
|
897
|
-
{/* Empty State */}
|
|
898
217
|
{(!state?.specs?.length && (!state?.product?.roadmap?.items?.length || state?.product?.roadmap?.isBoilerplate)) && (
|
|
899
218
|
<div className="border border-dashed border-border/60 p-8 rounded-lg text-center h-40 flex flex-col items-center justify-center">
|
|
900
219
|
<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>
|
|
220
|
+
<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
221
|
</div>
|
|
908
222
|
)}
|
|
909
|
-
|
|
910
|
-
|
|
911
223
|
</div>
|
|
912
224
|
</section>
|
|
913
225
|
|
|
914
226
|
{/* 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">
|
|
227
|
+
<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
228
|
<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>
|
|
229
|
+
<div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg"><Package size={20} /></div>
|
|
920
230
|
<h2 className="text-lg font-semibold">4. Implementation</h2>
|
|
921
231
|
</div>
|
|
922
|
-
|
|
923
232
|
<div className="space-y-3 flex-1">
|
|
924
|
-
{/* Scaffold Status */}
|
|
925
233
|
<div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
|
|
926
234
|
<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
|
-
)}
|
|
235
|
+
{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
236
|
</div>
|
|
933
|
-
|
|
934
|
-
{/* Git Status */}
|
|
935
237
|
{state?.implementation?.git?.initialized && (
|
|
936
238
|
<div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
|
|
937
239
|
<span className="text-foreground font-medium text-sm flex items-center gap-2">
|
|
@@ -939,97 +241,41 @@ function App() {
|
|
|
939
241
|
{state.implementation.git.branch || 'main'}
|
|
940
242
|
</span>
|
|
941
243
|
<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
|
-
)}
|
|
244
|
+
{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
245
|
</div>
|
|
951
246
|
</div>
|
|
952
247
|
)}
|
|
953
|
-
|
|
954
|
-
{/* Tests & Coverage */}
|
|
955
248
|
{state?.implementation?.scaffolded && (
|
|
956
249
|
<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>
|
|
250
|
+
<span className="text-foreground font-medium text-sm flex items-center gap-2"><CheckSquare size={16} className="text-muted-foreground" /> Tests</span>
|
|
960
251
|
<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
|
-
)}
|
|
252
|
+
{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
253
|
{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>
|
|
254
|
+
<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
255
|
)}
|
|
978
256
|
</div>
|
|
979
257
|
</div>
|
|
980
258
|
)}
|
|
981
|
-
|
|
982
|
-
{/* Spec Progress */}
|
|
983
259
|
{state?.implementation?.specs && state.implementation.specs.total > 0 && (
|
|
984
260
|
<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
|
-
|
|
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>
|
|
261
|
+
<span className="text-foreground font-medium text-sm flex items-center gap-2"><FileText size={16} className="text-muted-foreground" /> Specs</span>
|
|
262
|
+
<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
263
|
</div>
|
|
995
264
|
)}
|
|
996
265
|
</div>
|
|
997
|
-
|
|
998
266
|
<div className="mt-6">
|
|
999
267
|
{!state?.implementation?.scaffolded ? (
|
|
1000
|
-
<PromptButton
|
|
1001
|
-
|
|
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
|
-
|
|
268
|
+
<PromptButton label="Scaffold App" prompt="Antigravity, scaffold the implementation. Read 'agent-os/commands/scaffold-implementation/scaffold-implementation.md'." onClick={copyToClipboard} />
|
|
269
|
+
) : <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
270
|
{state?.implementation?.scaffolded && state?.design?.exportPrompts?.oneShot && (
|
|
1012
271
|
<div className="pt-3 border-t border-border/50">
|
|
1013
272
|
<p className="text-xs text-muted-foreground mb-2 font-medium">Implementation Options:</p>
|
|
1014
273
|
<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
|
-
/>
|
|
274
|
+
<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
275
|
{state?.design?.exportPrompts?.section && (
|
|
1023
276
|
<div className="relative">
|
|
1024
|
-
<PromptButton
|
|
1025
|
-
|
|
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> ←
|
|
1032
|
-
</p>
|
|
277
|
+
<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 />
|
|
278
|
+
<p className="text-[10px] text-muted-foreground mt-1.5 text-center leading-tight">Build by spec from <strong>Feature Specs</strong> ←</p>
|
|
1033
279
|
</div>
|
|
1034
280
|
)}
|
|
1035
281
|
</div>
|
|
@@ -1043,17 +289,17 @@ function App() {
|
|
|
1043
289
|
</div>
|
|
1044
290
|
<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
291
|
<div className="flex items-center gap-2">
|
|
1046
|
-
<div className={`w-1.5 h-1.5 rounded-full ${state?.services?.api ? 'bg-
|
|
292
|
+
<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
293
|
<span>Control Center API ({runtimeConfig?.ports?.api || 5403})</span>
|
|
1048
294
|
</div>
|
|
1049
295
|
<div className="h-3 w-[1px] bg-border" />
|
|
1050
296
|
<div className="flex items-center gap-2">
|
|
1051
|
-
<div className={`w-1.5 h-1.5 rounded-full ${state?.services?.design ? 'bg-
|
|
297
|
+
<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
298
|
<span>Design OS ({runtimeConfig?.ports?.design || 5400})</span>
|
|
1053
299
|
</div>
|
|
1054
300
|
<div className="h-3 w-[1px] bg-border" />
|
|
1055
301
|
<div className="flex items-center gap-2">
|
|
1056
|
-
<div className={`w-1.5 h-1.5 rounded-full ${state?.services?.app ? 'bg-
|
|
302
|
+
<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
303
|
<span>App ({runtimeConfig?.ports?.app || 5402})</span>
|
|
1058
304
|
</div>
|
|
1059
305
|
</footer>
|