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