Skip to content

Commit a2c4a90

Browse files
committed
Fix wizard agent prompt handing and route writing
- Normalize wizard prompt handing across UI, agent, and CLI - Route AI wizard requests through /agent/wizard/ai - Add guard to prevent empty or malformed prompts - Clean up stale wizardAgent exports and imports
1 parent 7c8ed21 commit a2c4a90

File tree

7 files changed

+305
-316
lines changed

7 files changed

+305
-316
lines changed

client/src/lib/api.ts

Lines changed: 51 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -82,30 +82,60 @@ export const api = {
8282

8383

8484
// AI wizard – talks to /agent/wizard on the backend
85+
// askYamlWizard: async (input: {
86+
// repoUrl: string;
87+
// provider: string;
88+
// branch: string;
89+
// message?: string;
90+
// yaml?: string;
91+
// }) => {
92+
// const res = await fetch(`${SERVER_BASE}/agent/wizard/ai`, {
93+
// method: "POST",
94+
// headers: { "Content-Type": "application/json" },
95+
// credentials: "include",
96+
// body: JSON.stringify(input),
97+
// });
98+
99+
// const payload = await res.json().catch(() => ({}));
100+
// if (!res.ok || (payload as any)?.success === false) {
101+
// throw new Error(
102+
// (payload as any)?.error || res.statusText || "Agent error"
103+
// );
104+
// }
105+
106+
// // whatever runWizardAgent returns is in payload.data
107+
// return (payload as any).data;
108+
// },
109+
85110
askYamlWizard: async (input: {
86-
repoUrl: string;
87-
provider: string;
88-
branch: string;
89-
message?: string;
90-
yaml?: string;
91-
}) => {
92-
const res = await fetch(`${SERVER_BASE}/agent/wizard`, {
93-
method: "POST",
94-
headers: { "Content-Type": "application/json" },
95-
credentials: "include",
96-
body: JSON.stringify(input),
97-
});
111+
repoUrl: string;
112+
provider: string;
113+
branch: string;
114+
message?: string; // frontend name
115+
yaml?: string;
116+
}) => {
117+
const payload = {
118+
...input,
119+
prompt: input.message ?? "", // 👈 REQUIRED BY BACKEND
120+
};
121+
122+
delete (payload as any).message; // 👈 prevent backend confusion
123+
124+
const res = await fetch(`${SERVER_BASE}/agent/wizard/ai`, {
125+
method: "POST",
126+
headers: { "Content-Type": "application/json" },
127+
credentials: "include",
128+
body: JSON.stringify(payload),
129+
});
98130

99-
const payload = await res.json().catch(() => ({}));
100-
if (!res.ok || (payload as any)?.success === false) {
101-
throw new Error(
102-
(payload as any)?.error || res.statusText || "Agent error"
103-
);
104-
}
131+
const data = await res.json().catch(() => ({}));
105132

106-
// whatever runWizardAgent returns is in payload.data
107-
return (payload as any).data;
108-
},
133+
if (!res.ok || data?.success === false) {
134+
throw new Error(data?.error || res.statusText || "Agent error");
135+
}
136+
137+
return data.data;
138+
},
109139

110140
// ===== MCP helpers for repos / branches / pipeline generation =====
111141
async listRepos(): Promise<{ repos: string[] }> {
@@ -151,75 +181,6 @@ export const api = {
151181
return { branches: cachedBranches.get(repo) ?? [] };
152182
}
153183
},
154-
// async listRepos(): Promise<{ repos: string[] }> {
155-
// // If we already have repos cached, reuse them.
156-
// if (cachedRepos && cachedRepos.length > 0) {
157-
// return { repos: cachedRepos };
158-
// }
159-
160-
// // If we've already tried once and failed, don't hammer the server.
161-
// if (reposAttempted && !cachedRepos) {
162-
// return { repos: [] };
163-
// }
164-
165-
// reposAttempted = true;
166-
167-
// try {
168-
// const outer = await mcp<{
169-
// provider: string;
170-
// user: string;
171-
// repositories: { full_name: string }[];
172-
// }>("repo_reader", {});
173-
174-
// const repos = outer?.repositories?.map((r) => r.full_name) ?? [];
175-
// cachedRepos = repos;
176-
// return { repos };
177-
// } catch (err) {
178-
// console.error("[api.listRepos] failed:", err);
179-
// // Don't throw to avoid retry loops from effects; just return whatever we have (or empty).
180-
// return { repos: cachedRepos ?? [] };
181-
// }
182-
// },
183-
184-
async listBranches(repo: string): Promise<{ branches: string[] }> {
185-
// ✅ If we already have branches cached for this repo, reuse them.
186-
const cached = cachedBranches.get(repo);
187-
if (cached) {
188-
return { branches: cached };
189-
}
190-
191-
try {
192-
// For now we still use repo_reader, but we only call it
193-
// when the cache is cold. We can later swap this to a
194-
// more specific MCP tool like "repo_branches".
195-
const outer = await mcp<{
196-
success?: boolean;
197-
data?: { repositories: { full_name: string; branches?: string[] }[] };
198-
repositories?: { full_name: string; branches?: string[] }[];
199-
}>("repo_reader", {
200-
// This extra input is safe: current server ignores it,
201-
// future server can use it to optimize.
202-
repoFullName: repo,
203-
});
204-
205-
// Unwrap the payload (tool responses come back as { success, data })
206-
const body = (outer as any)?.data ?? outer;
207-
208-
const match = body?.repositories?.find((r: any) => r.full_name === repo);
209-
const branches = match?.branches ?? [];
210-
211-
// Cache even empty arrays so we don't re-query a repo with no branches
212-
cachedBranches.set(repo, branches);
213-
214-
return { branches };
215-
} catch (err) {
216-
console.error("[api.listBranches] failed:", err);
217-
218-
// If we have anything cached (even empty), use it.
219-
const fallback = cachedBranches.get(repo) ?? [];
220-
return { branches: fallback };
221-
}
222-
},
223184

224185
async createPipeline(payload: any) {
225186
const { repo, branch, template = "node_app", options } = payload || {};

0 commit comments

Comments
 (0)