feat(ai): patch Architect prompt with full Spec Kit PRD template rules
This commit is contained in:
@@ -109,91 +109,92 @@ export default function PlanPageV2() {
|
|||||||
// The business case / 1-page summary
|
// The business case / 1-page summary
|
||||||
// ──────────────────────────────────────────────────
|
// ──────────────────────────────────────────────────
|
||||||
function ObjectiveView({ plan, projectId, onChange }: { plan: Plan, projectId: string, onChange: (p: Plan) => void }) {
|
function ObjectiveView({ plan, projectId, onChange }: { plan: Plan, projectId: string, onChange: (p: Plan) => void }) {
|
||||||
const [editing, setEditing] = useState(!plan.vision);
|
|
||||||
const [draft, setDraft] = useState(plan.vision ?? "");
|
const [draft, setDraft] = useState(plan.vision ?? "");
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
|
const [generating, setGenerating] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (plan.vision !== draft) {
|
||||||
|
setDraft(plan.vision ?? "");
|
||||||
|
}
|
||||||
|
}, [plan.vision]);
|
||||||
|
|
||||||
const save = async () => {
|
const save = async (text: string) => {
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
try {
|
try {
|
||||||
const r = await fetch(`/api/projects/${projectId}/plan`, {
|
const r = await fetch(`/api/projects/${projectId}/plan`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ kind: "vision", text: draft }),
|
body: JSON.stringify({ kind: "vision", text }),
|
||||||
});
|
});
|
||||||
const d = await r.json();
|
const d = await r.json();
|
||||||
if (d.plan) onChange(d.plan);
|
if (d.plan) onChange(d.plan);
|
||||||
setEditing(false);
|
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleGenerate = async () => {
|
||||||
|
if (!draft.trim()) {
|
||||||
|
alert("Please write an objective first before generating the PRD.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!confirm("This will overwrite the PRD and Execution Plan based on this objective. Continue?")) return;
|
||||||
|
|
||||||
|
setGenerating(true);
|
||||||
|
try {
|
||||||
|
await fetch(`/api/projects/${projectId}/plan`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ kind: "vision", text: draft }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const r = await fetch(`/api/projects/${projectId}/plan/generate`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ objective: draft }),
|
||||||
|
});
|
||||||
|
const d = await r.json();
|
||||||
|
if (d.plan) onChange(d.plan);
|
||||||
|
alert("Blueprint generated successfully! Check the PRD and Delegate tabs.");
|
||||||
|
} finally {
|
||||||
|
setGenerating(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="panel-container">
|
<div className="panel-container">
|
||||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 16 }}>
|
<div style={{ display: "flex", justifyContent: "flex-end", marginBottom: 16 }}>
|
||||||
<div>
|
<div style={{ display: "flex", alignItems: "center", gap: 12 }}>
|
||||||
|
{saving && <span style={{ fontSize: "0.75rem", color: INK.faint }}>Saving...</span>}
|
||||||
</div>
|
|
||||||
<div style={{ display: "flex", gap: 12 }}>
|
|
||||||
<button
|
<button
|
||||||
onClick={async () => {
|
onClick={handleGenerate}
|
||||||
if (!confirm("This will overwrite the PRD and Execution Plan based on the current objective. Continue?")) return;
|
disabled={generating || !draft.trim()}
|
||||||
setSaving(true);
|
|
||||||
try {
|
|
||||||
const r = await fetch(`/api/projects/${projectId}/plan/generate`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ objective: draft }),
|
|
||||||
});
|
|
||||||
const d = await r.json();
|
|
||||||
if (d.plan) onChange(d.plan);
|
|
||||||
} finally {
|
|
||||||
setSaving(false);
|
|
||||||
setEditing(false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
disabled={saving || !draft.trim()}
|
|
||||||
className="btn-primary"
|
className="btn-primary"
|
||||||
style={{ fontWeight: 600, fontSize: "0.85rem", padding: "6px 16px", borderRadius: 6 }}
|
style={{ padding: "8px 16px", borderRadius: 8, fontWeight: 600 }}
|
||||||
>
|
>
|
||||||
{saving ? <><Loader2 size={12} className="animate-spin" /> Generating...</> : "Generate Complete PRD"}
|
{generating ? (
|
||||||
|
<><Loader2 size={14} className="animate-spin" /> Generating Blueprint...</>
|
||||||
|
) : (
|
||||||
|
"Generate Complete PRD"
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
{!editing && (
|
|
||||||
<button onClick={() => setEditing(true)} className="btn-ghost">
|
|
||||||
<Pencil size={12} /> Edit
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{editing ? (
|
<div style={{ border: `1px solid ${INK.border}`, borderRadius: 8, overflow: "hidden", background: "#fff", boxShadow: "0 1px 3px rgba(0,0,0,0.02)" }}>
|
||||||
<div style={{ border: `1px solid ${INK.border}`, borderRadius: 8, overflow: "hidden" }}>
|
<textarea
|
||||||
<textarea
|
value={draft}
|
||||||
value={draft}
|
onChange={(e) => setDraft(e.target.value)}
|
||||||
onChange={(e) => setDraft(e.target.value)}
|
onBlur={() => save(draft)}
|
||||||
style={{
|
style={{
|
||||||
width: "100%", minHeight: 400, padding: 20, fontSize: "0.95rem", lineHeight: 1.6,
|
width: "100%", minHeight: 400, padding: 24, fontSize: "0.95rem", lineHeight: 1.6,
|
||||||
border: "none", outline: "none", resize: "vertical", fontFamily: "var(--font-sans)",
|
border: "none", outline: "none", resize: "vertical", fontFamily: "var(--font-sans)",
|
||||||
}}
|
color: INK.main
|
||||||
placeholder="Describe the business objective..."
|
}}
|
||||||
/>
|
placeholder="Describe the business objective..."
|
||||||
<div style={{ display: "flex", justifyContent: "flex-end", gap: 8, padding: "12px 20px", background: INK.bgHover, borderTop: `1px solid ${INK.border}` }}>
|
/>
|
||||||
<button onClick={() => setEditing(false)} className="btn-ghost">Cancel</button>
|
</div>
|
||||||
<button onClick={save} disabled={saving} className="btn-primary">
|
|
||||||
{saving ? "Saving..." : "Save Objective"}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="markdown-prose" style={{ background: "#fff", border: `1px solid ${INK.border}`, padding: 32, borderRadius: 8, minHeight: 200 }}>
|
|
||||||
{plan.vision ? (
|
|
||||||
<ReactMarkdown>{plan.vision}</ReactMarkdown>
|
|
||||||
) : (
|
|
||||||
<div style={{ color: INK.faint, fontStyle: "italic" }}>No objective defined yet. Switch to Architect mode and brainstorm with the AI.</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user