@maintainer-pro/ai-cli 0.1.3 → 0.1.4
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/README.md +0 -1
- package/dist/index.cjs +123 -9
- package/dist/index.d.cts +40 -3
- package/dist/index.d.ts +40 -3
- package/dist/index.js +122 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,6 +58,5 @@ Chat history is stored in Maintainer Pro admin. Do not put API keys in the brows
|
|
|
58
58
|
|---------|------|
|
|
59
59
|
| [`@maintainer-pro/ai-server`](https://www.npmjs.com/package/@maintainer-pro/ai-server) | Local Node chat server (sidecar) that uses this SDK |
|
|
60
60
|
| [`@maintainer-pro/ai-ui`](https://www.npmjs.com/package/@maintainer-pro/ai-ui) | Embed widget (`ai-ui.iife.js`) — sidecar in dev, static file in prod |
|
|
61
|
-
| [`@maintainer-pro/setup`](https://www.npmjs.com/package/@maintainer-pro/setup) | Download the sandbox setup guide |
|
|
62
61
|
|
|
63
62
|
Optional SQL persistence lives on `@maintainer-pro/ai-cli/db` (install `better-sqlite3`, `postgres`, or `mysql2`).
|
package/dist/index.cjs
CHANGED
|
@@ -49,6 +49,7 @@ __export(index_exports, {
|
|
|
49
49
|
createToolValidator: () => createToolValidator,
|
|
50
50
|
formatClientContext: () => formatClientContext,
|
|
51
51
|
getProviderPreference: () => getProviderPreference,
|
|
52
|
+
inspectAndRepairWorkspace: () => inspectAndRepairWorkspace,
|
|
52
53
|
parseAiResponse: () => parseAiResponse,
|
|
53
54
|
providerLabel: () => providerLabel,
|
|
54
55
|
resolveCliBinary: () => resolveCliBinary,
|
|
@@ -103,7 +104,7 @@ function splitMessages(messages) {
|
|
|
103
104
|
const history = latestUserIndex > 0 ? messages.slice(0, latestUserIndex) : [];
|
|
104
105
|
return { request, history };
|
|
105
106
|
}
|
|
106
|
-
function buildUserFacingPrompt(systemPrompt, messages, context, attachmentPaths, priorConversationsContext) {
|
|
107
|
+
function buildUserFacingPrompt(systemPrompt, messages, context, attachmentPaths, priorConversationsContext, technical) {
|
|
107
108
|
const { request, history } = splitMessages(messages);
|
|
108
109
|
const historyBlock = history.length > 0 ? `Conversation so far:
|
|
109
110
|
${buildConversationPrompt(history)}
|
|
@@ -123,27 +124,30 @@ ${buildConversationPrompt(history)}
|
|
|
123
124
|
${attachmentPaths.map((p) => `- ${p}`).join("\n")}
|
|
124
125
|
|
|
125
126
|
` : "";
|
|
127
|
+
const closer = technical ? "Do the work now. Follow the requested output format exactly." : "Do the work now (or ask one short clarifying question if needed). Reply in English, non-technical and user-facing. If it's not resolving, offer a quick call.";
|
|
126
128
|
return `${systemSection}${contextSection}${priorSection}${historyBlock}${attachmentsSection}Current user request:
|
|
127
129
|
${request || "(See the attached screenshot(s).)"}
|
|
128
130
|
|
|
129
|
-
|
|
131
|
+
${closer}`;
|
|
130
132
|
}
|
|
131
|
-
function buildCursorPrompt(systemPrompt, messages, context, attachmentPaths, priorConversationsContext) {
|
|
133
|
+
function buildCursorPrompt(systemPrompt, messages, context, attachmentPaths, priorConversationsContext, technical) {
|
|
132
134
|
return buildUserFacingPrompt(
|
|
133
135
|
systemPrompt,
|
|
134
136
|
messages,
|
|
135
137
|
context,
|
|
136
138
|
attachmentPaths,
|
|
137
|
-
priorConversationsContext
|
|
139
|
+
priorConversationsContext,
|
|
140
|
+
technical
|
|
138
141
|
);
|
|
139
142
|
}
|
|
140
|
-
function buildClaudeUserPrompt(messages, context, attachmentPaths, priorConversationsContext) {
|
|
143
|
+
function buildClaudeUserPrompt(messages, context, attachmentPaths, priorConversationsContext, technical) {
|
|
141
144
|
return buildUserFacingPrompt(
|
|
142
145
|
null,
|
|
143
146
|
messages,
|
|
144
147
|
context,
|
|
145
148
|
attachmentPaths,
|
|
146
|
-
priorConversationsContext
|
|
149
|
+
priorConversationsContext,
|
|
150
|
+
technical
|
|
147
151
|
);
|
|
148
152
|
}
|
|
149
153
|
|
|
@@ -256,7 +260,8 @@ async function callClaudeCli(command, messages, context, options) {
|
|
|
256
260
|
messages,
|
|
257
261
|
context,
|
|
258
262
|
options.attachmentPaths,
|
|
259
|
-
options.priorConversationsContext
|
|
263
|
+
options.priorConversationsContext,
|
|
264
|
+
options.technical
|
|
260
265
|
);
|
|
261
266
|
const resolved = resolveCliCommand("claude", command);
|
|
262
267
|
const workspace = options.workspaceDir ?? process.env.AI_CLI_WORKSPACE ?? process.cwd();
|
|
@@ -295,7 +300,8 @@ async function callCursorCli(command, messages, context, options) {
|
|
|
295
300
|
messages,
|
|
296
301
|
context,
|
|
297
302
|
options.attachmentPaths,
|
|
298
|
-
options.priorConversationsContext
|
|
303
|
+
options.priorConversationsContext,
|
|
304
|
+
options.technical
|
|
299
305
|
);
|
|
300
306
|
const resolved = resolveCliCommand("cursor", command);
|
|
301
307
|
const workspace = options.workspaceDir ?? process.env.AI_CLI_WORKSPACE ?? process.cwd();
|
|
@@ -336,7 +342,8 @@ async function callAntigravityCli(command, messages, context, options) {
|
|
|
336
342
|
messages,
|
|
337
343
|
context,
|
|
338
344
|
options.attachmentPaths,
|
|
339
|
-
options.priorConversationsContext
|
|
345
|
+
options.priorConversationsContext,
|
|
346
|
+
options.technical
|
|
340
347
|
);
|
|
341
348
|
const prompt = `You are operating as a coding agent with full permission to read and edit files in this workspace. Do not introduce yourself. Do not ask what you are. Execute the user's latest request now (edit files and/or emit runtime tool JSON as instructed).
|
|
342
349
|
|
|
@@ -1142,6 +1149,112 @@ function createMaintainerProStoreFromEnv() {
|
|
|
1142
1149
|
if (!baseUrl || !apiKey) return null;
|
|
1143
1150
|
return createMaintainerProStore({ baseUrl, apiKey });
|
|
1144
1151
|
}
|
|
1152
|
+
|
|
1153
|
+
// src/workspace-inspect.ts
|
|
1154
|
+
var import_zod = require("zod");
|
|
1155
|
+
var inspectJsonSchema = import_zod.z.object({
|
|
1156
|
+
kind: import_zod.z.enum(["next", "vite", "html", "empty", "other"]).optional(),
|
|
1157
|
+
name: import_zod.z.string().max(200).optional(),
|
|
1158
|
+
summary: import_zod.z.string().max(800).optional(),
|
|
1159
|
+
scripts: import_zod.z.object({
|
|
1160
|
+
ui: import_zod.z.string().min(1).max(64).optional(),
|
|
1161
|
+
backend: import_zod.z.string().min(1).max(64).optional(),
|
|
1162
|
+
app: import_zod.z.string().min(1).max(64).optional()
|
|
1163
|
+
}).optional(),
|
|
1164
|
+
ports: import_zod.z.object({
|
|
1165
|
+
ui: import_zod.z.number().int().min(1).max(65535).optional(),
|
|
1166
|
+
backend: import_zod.z.number().int().min(1).max(65535).optional(),
|
|
1167
|
+
app: import_zod.z.number().int().min(1).max(65535).optional()
|
|
1168
|
+
}).optional(),
|
|
1169
|
+
issues: import_zod.z.array(import_zod.z.string().max(500)).max(20).optional(),
|
|
1170
|
+
fixes: import_zod.z.array(import_zod.z.string().max(500)).max(20).optional(),
|
|
1171
|
+
ready: import_zod.z.boolean().optional()
|
|
1172
|
+
});
|
|
1173
|
+
function extractInspectJson(text) {
|
|
1174
|
+
const fence = text.match(/```json\s*([\s\S]*?)```/i);
|
|
1175
|
+
const raw = fence?.[1]?.trim() || text.match(/\{[\s\S]*\}/)?.[0];
|
|
1176
|
+
if (!raw) return null;
|
|
1177
|
+
try {
|
|
1178
|
+
const parsed = inspectJsonSchema.safeParse(JSON.parse(raw));
|
|
1179
|
+
return parsed.success ? parsed.data : null;
|
|
1180
|
+
} catch {
|
|
1181
|
+
return null;
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
function setupSystemPrompt(input) {
|
|
1185
|
+
return `You are Maintainer Pro's local setup assistant on the developer's machine.
|
|
1186
|
+
|
|
1187
|
+
Workspace: ${input.workspaceDir}
|
|
1188
|
+
App name: ${input.appName || "the app"}
|
|
1189
|
+
|
|
1190
|
+
Inspect this repository (package.json, README, lockfiles, existing env files, how the UI and API start).
|
|
1191
|
+
You MAY edit files to fix setup problems that would block local run or the chat widget (env keys, widget mount, missing config).
|
|
1192
|
+
Do NOT start long-lived dev servers, tunnels, or install global tools.
|
|
1193
|
+
Do NOT run \`npm run dev\` or similar.
|
|
1194
|
+
|
|
1195
|
+
Script names you report MUST already exist in package.json. Omit chat/ai-server scripts.
|
|
1196
|
+
|
|
1197
|
+
End with a short human summary AND one \`\`\`json fence with:
|
|
1198
|
+
|
|
1199
|
+
{
|
|
1200
|
+
"kind": "next" | "vite" | "html" | "empty" | "other",
|
|
1201
|
+
"name": "package or folder name",
|
|
1202
|
+
"summary": "one sentence about this project",
|
|
1203
|
+
"scripts": { "ui": "dev:client", "backend": "dev:server" },
|
|
1204
|
+
"ports": { "ui": 5173, "backend": 4100 },
|
|
1205
|
+
"issues": ["remaining problems"],
|
|
1206
|
+
"fixes": ["what you changed"],
|
|
1207
|
+
"ready": true
|
|
1208
|
+
}`;
|
|
1209
|
+
}
|
|
1210
|
+
function setupUserMessage(input) {
|
|
1211
|
+
if (input.problem?.trim()) {
|
|
1212
|
+
return `The local bridge failed to keep this project running:
|
|
1213
|
+
|
|
1214
|
+
${input.problem.trim()}
|
|
1215
|
+
|
|
1216
|
+
${input.extraContext ? `${input.extraContext.trim()}
|
|
1217
|
+
|
|
1218
|
+
` : ""}Inspect the repo, fix the cause if you can, and return the JSON report.`;
|
|
1219
|
+
}
|
|
1220
|
+
return `${input.extraContext ? `${input.extraContext.trim()}
|
|
1221
|
+
|
|
1222
|
+
` : ""}Inspect this project, fix any setup gaps you can, and return the JSON report.`;
|
|
1223
|
+
}
|
|
1224
|
+
async function inspectAndRepairWorkspace(input) {
|
|
1225
|
+
const workspaceDir = input.workspaceDir;
|
|
1226
|
+
const provider = await resolveProvider({ preference: "auto" });
|
|
1227
|
+
const response = await callAi(
|
|
1228
|
+
[{ role: "user", content: setupUserMessage(input) }],
|
|
1229
|
+
{
|
|
1230
|
+
route: "/local-setup",
|
|
1231
|
+
pageTitle: input.appName || "Local setup",
|
|
1232
|
+
relevantFiles: ["package.json", "README.md", ".env", ".env.example"],
|
|
1233
|
+
data: {
|
|
1234
|
+
workspaceDir,
|
|
1235
|
+
problem: input.problem || null
|
|
1236
|
+
}
|
|
1237
|
+
},
|
|
1238
|
+
{
|
|
1239
|
+
systemPrompt: setupSystemPrompt(input),
|
|
1240
|
+
workspaceDir,
|
|
1241
|
+
technical: true
|
|
1242
|
+
}
|
|
1243
|
+
);
|
|
1244
|
+
const parsed = extractInspectJson(response.text);
|
|
1245
|
+
return {
|
|
1246
|
+
kind: parsed?.kind ?? "other",
|
|
1247
|
+
name: parsed?.name?.trim() || input.appName || "",
|
|
1248
|
+
summary: parsed?.summary?.trim() || response.text.replace(/```json[\s\S]*```/i, "").trim().slice(0, 400),
|
|
1249
|
+
scripts: parsed?.scripts ?? {},
|
|
1250
|
+
ports: parsed?.ports ?? {},
|
|
1251
|
+
issues: parsed?.issues ?? [],
|
|
1252
|
+
fixes: parsed?.fixes ?? [],
|
|
1253
|
+
ready: parsed?.ready ?? parsed?.issues?.length === 0,
|
|
1254
|
+
provider: response.provider || provider.id,
|
|
1255
|
+
rawText: response.text
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1145
1258
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1146
1259
|
0 && (module.exports = {
|
|
1147
1260
|
WORKING_PROVIDER,
|
|
@@ -1163,6 +1276,7 @@ function createMaintainerProStoreFromEnv() {
|
|
|
1163
1276
|
createToolValidator,
|
|
1164
1277
|
formatClientContext,
|
|
1165
1278
|
getProviderPreference,
|
|
1279
|
+
inspectAndRepairWorkspace,
|
|
1166
1280
|
parseAiResponse,
|
|
1167
1281
|
providerLabel,
|
|
1168
1282
|
resolveCliBinary,
|
package/dist/index.d.cts
CHANGED
|
@@ -56,6 +56,8 @@ interface CallAiOptions {
|
|
|
56
56
|
priorConversationsContext?: string;
|
|
57
57
|
/** Extra / override providers (e.g. future HTTP model backends). */
|
|
58
58
|
providers?: AiProvider[];
|
|
59
|
+
/** When true, skip the end-user chat footer so the agent can return structured output. */
|
|
60
|
+
technical?: boolean;
|
|
59
61
|
}
|
|
60
62
|
interface AiProvider {
|
|
61
63
|
id: string;
|
|
@@ -85,9 +87,9 @@ declare function parseAiResponse(raw: string, provider: AiCliProviderId): AiResp
|
|
|
85
87
|
declare function buildConversationPrompt(messages: ChatMessage[]): string;
|
|
86
88
|
declare function formatClientContext(context?: ClientContext): string;
|
|
87
89
|
/** Full prompt for providers without a native --system-prompt flag (Cursor). */
|
|
88
|
-
declare function buildCursorPrompt(systemPrompt: string, messages: ChatMessage[], context?: ClientContext, attachmentPaths?: string[], priorConversationsContext?: string): string;
|
|
90
|
+
declare function buildCursorPrompt(systemPrompt: string, messages: ChatMessage[], context?: ClientContext, attachmentPaths?: string[], priorConversationsContext?: string, technical?: boolean): string;
|
|
89
91
|
/** Conversation + context for Claude (system prompt passed separately). */
|
|
90
|
-
declare function buildClaudeUserPrompt(messages: ChatMessage[], context?: ClientContext, attachmentPaths?: string[], priorConversationsContext?: string): string;
|
|
92
|
+
declare function buildClaudeUserPrompt(messages: ChatMessage[], context?: ClientContext, attachmentPaths?: string[], priorConversationsContext?: string, technical?: boolean): string;
|
|
91
93
|
|
|
92
94
|
declare function createDefaultSystemPrompt(input: {
|
|
93
95
|
productDescription: string;
|
|
@@ -241,4 +243,39 @@ interface PriorConversationOptions {
|
|
|
241
243
|
*/
|
|
242
244
|
declare function buildPriorConversationsContext(db: ChatStoreLike, options: PriorConversationOptions): Promise<string>;
|
|
243
245
|
|
|
244
|
-
|
|
246
|
+
type WorkspaceKind = "next" | "vite" | "html" | "empty" | "other";
|
|
247
|
+
interface WorkspaceInspectInput {
|
|
248
|
+
workspaceDir: string;
|
|
249
|
+
appName?: string;
|
|
250
|
+
/** When set, the agent diagnoses and tries to fix this failure. */
|
|
251
|
+
problem?: string;
|
|
252
|
+
extraContext?: string;
|
|
253
|
+
}
|
|
254
|
+
interface WorkspaceInspectResult {
|
|
255
|
+
kind: WorkspaceKind;
|
|
256
|
+
name: string;
|
|
257
|
+
summary: string;
|
|
258
|
+
scripts: {
|
|
259
|
+
ui?: string;
|
|
260
|
+
backend?: string;
|
|
261
|
+
app?: string;
|
|
262
|
+
};
|
|
263
|
+
ports: {
|
|
264
|
+
ui?: number;
|
|
265
|
+
backend?: number;
|
|
266
|
+
app?: number;
|
|
267
|
+
};
|
|
268
|
+
issues: string[];
|
|
269
|
+
fixes: string[];
|
|
270
|
+
ready: boolean;
|
|
271
|
+
provider: string;
|
|
272
|
+
rawText: string;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Ask the coding-agent CLI (via callAi) to inspect a workspace and optionally
|
|
276
|
+
* repair setup issues. Used by ai-bridge so host-process planning uses the
|
|
277
|
+
* same provider resolution as chat.
|
|
278
|
+
*/
|
|
279
|
+
declare function inspectAndRepairWorkspace(input: WorkspaceInspectInput): Promise<WorkspaceInspectResult>;
|
|
280
|
+
|
|
281
|
+
export { type AiCliProviderId, type AiProvider, type AiResponse, type CallAiOptions, type ChatAttachment, type ChatHandlerOptions, type ChatHandlers, type ChatMessage, type ChatSenderType, type ChatStoreLike, type ChatUser, type ClientContext, type LocalStoredMessage, type MaintainerProStoreOptions, type PriorConversationOptions, type ProviderPreference, type ToolCall, type ToolSchemaMap, WORKING_PROVIDER, type WorkspaceInspectInput, type WorkspaceInspectResult, type WorkspaceKind, buildClaudeUserPrompt, buildConversationPrompt, buildCursorPrompt, buildPriorConversationsContext, callAi, commandExists, createAntigravityProvider, createBuiltinProviders, createChatHandler, createClaudeProvider, createCursorProvider, createDefaultSystemPrompt, createLocalDirectoryStore, createMaintainerProStore, createMaintainerProStoreFromEnv, createToolValidator, formatClientContext, getProviderPreference, inspectAndRepairWorkspace, parseAiResponse, providerLabel, resolveCliBinary, resolveProvider, saveChatAttachments, toNextRoute };
|
package/dist/index.d.ts
CHANGED
|
@@ -56,6 +56,8 @@ interface CallAiOptions {
|
|
|
56
56
|
priorConversationsContext?: string;
|
|
57
57
|
/** Extra / override providers (e.g. future HTTP model backends). */
|
|
58
58
|
providers?: AiProvider[];
|
|
59
|
+
/** When true, skip the end-user chat footer so the agent can return structured output. */
|
|
60
|
+
technical?: boolean;
|
|
59
61
|
}
|
|
60
62
|
interface AiProvider {
|
|
61
63
|
id: string;
|
|
@@ -85,9 +87,9 @@ declare function parseAiResponse(raw: string, provider: AiCliProviderId): AiResp
|
|
|
85
87
|
declare function buildConversationPrompt(messages: ChatMessage[]): string;
|
|
86
88
|
declare function formatClientContext(context?: ClientContext): string;
|
|
87
89
|
/** Full prompt for providers without a native --system-prompt flag (Cursor). */
|
|
88
|
-
declare function buildCursorPrompt(systemPrompt: string, messages: ChatMessage[], context?: ClientContext, attachmentPaths?: string[], priorConversationsContext?: string): string;
|
|
90
|
+
declare function buildCursorPrompt(systemPrompt: string, messages: ChatMessage[], context?: ClientContext, attachmentPaths?: string[], priorConversationsContext?: string, technical?: boolean): string;
|
|
89
91
|
/** Conversation + context for Claude (system prompt passed separately). */
|
|
90
|
-
declare function buildClaudeUserPrompt(messages: ChatMessage[], context?: ClientContext, attachmentPaths?: string[], priorConversationsContext?: string): string;
|
|
92
|
+
declare function buildClaudeUserPrompt(messages: ChatMessage[], context?: ClientContext, attachmentPaths?: string[], priorConversationsContext?: string, technical?: boolean): string;
|
|
91
93
|
|
|
92
94
|
declare function createDefaultSystemPrompt(input: {
|
|
93
95
|
productDescription: string;
|
|
@@ -241,4 +243,39 @@ interface PriorConversationOptions {
|
|
|
241
243
|
*/
|
|
242
244
|
declare function buildPriorConversationsContext(db: ChatStoreLike, options: PriorConversationOptions): Promise<string>;
|
|
243
245
|
|
|
244
|
-
|
|
246
|
+
type WorkspaceKind = "next" | "vite" | "html" | "empty" | "other";
|
|
247
|
+
interface WorkspaceInspectInput {
|
|
248
|
+
workspaceDir: string;
|
|
249
|
+
appName?: string;
|
|
250
|
+
/** When set, the agent diagnoses and tries to fix this failure. */
|
|
251
|
+
problem?: string;
|
|
252
|
+
extraContext?: string;
|
|
253
|
+
}
|
|
254
|
+
interface WorkspaceInspectResult {
|
|
255
|
+
kind: WorkspaceKind;
|
|
256
|
+
name: string;
|
|
257
|
+
summary: string;
|
|
258
|
+
scripts: {
|
|
259
|
+
ui?: string;
|
|
260
|
+
backend?: string;
|
|
261
|
+
app?: string;
|
|
262
|
+
};
|
|
263
|
+
ports: {
|
|
264
|
+
ui?: number;
|
|
265
|
+
backend?: number;
|
|
266
|
+
app?: number;
|
|
267
|
+
};
|
|
268
|
+
issues: string[];
|
|
269
|
+
fixes: string[];
|
|
270
|
+
ready: boolean;
|
|
271
|
+
provider: string;
|
|
272
|
+
rawText: string;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Ask the coding-agent CLI (via callAi) to inspect a workspace and optionally
|
|
276
|
+
* repair setup issues. Used by ai-bridge so host-process planning uses the
|
|
277
|
+
* same provider resolution as chat.
|
|
278
|
+
*/
|
|
279
|
+
declare function inspectAndRepairWorkspace(input: WorkspaceInspectInput): Promise<WorkspaceInspectResult>;
|
|
280
|
+
|
|
281
|
+
export { type AiCliProviderId, type AiProvider, type AiResponse, type CallAiOptions, type ChatAttachment, type ChatHandlerOptions, type ChatHandlers, type ChatMessage, type ChatSenderType, type ChatStoreLike, type ChatUser, type ClientContext, type LocalStoredMessage, type MaintainerProStoreOptions, type PriorConversationOptions, type ProviderPreference, type ToolCall, type ToolSchemaMap, WORKING_PROVIDER, type WorkspaceInspectInput, type WorkspaceInspectResult, type WorkspaceKind, buildClaudeUserPrompt, buildConversationPrompt, buildCursorPrompt, buildPriorConversationsContext, callAi, commandExists, createAntigravityProvider, createBuiltinProviders, createChatHandler, createClaudeProvider, createCursorProvider, createDefaultSystemPrompt, createLocalDirectoryStore, createMaintainerProStore, createMaintainerProStoreFromEnv, createToolValidator, formatClientContext, getProviderPreference, inspectAndRepairWorkspace, parseAiResponse, providerLabel, resolveCliBinary, resolveProvider, saveChatAttachments, toNextRoute };
|
package/dist/index.js
CHANGED
|
@@ -43,7 +43,7 @@ function splitMessages(messages) {
|
|
|
43
43
|
const history = latestUserIndex > 0 ? messages.slice(0, latestUserIndex) : [];
|
|
44
44
|
return { request, history };
|
|
45
45
|
}
|
|
46
|
-
function buildUserFacingPrompt(systemPrompt, messages, context, attachmentPaths, priorConversationsContext) {
|
|
46
|
+
function buildUserFacingPrompt(systemPrompt, messages, context, attachmentPaths, priorConversationsContext, technical) {
|
|
47
47
|
const { request, history } = splitMessages(messages);
|
|
48
48
|
const historyBlock = history.length > 0 ? `Conversation so far:
|
|
49
49
|
${buildConversationPrompt(history)}
|
|
@@ -63,27 +63,30 @@ ${buildConversationPrompt(history)}
|
|
|
63
63
|
${attachmentPaths.map((p) => `- ${p}`).join("\n")}
|
|
64
64
|
|
|
65
65
|
` : "";
|
|
66
|
+
const closer = technical ? "Do the work now. Follow the requested output format exactly." : "Do the work now (or ask one short clarifying question if needed). Reply in English, non-technical and user-facing. If it's not resolving, offer a quick call.";
|
|
66
67
|
return `${systemSection}${contextSection}${priorSection}${historyBlock}${attachmentsSection}Current user request:
|
|
67
68
|
${request || "(See the attached screenshot(s).)"}
|
|
68
69
|
|
|
69
|
-
|
|
70
|
+
${closer}`;
|
|
70
71
|
}
|
|
71
|
-
function buildCursorPrompt(systemPrompt, messages, context, attachmentPaths, priorConversationsContext) {
|
|
72
|
+
function buildCursorPrompt(systemPrompt, messages, context, attachmentPaths, priorConversationsContext, technical) {
|
|
72
73
|
return buildUserFacingPrompt(
|
|
73
74
|
systemPrompt,
|
|
74
75
|
messages,
|
|
75
76
|
context,
|
|
76
77
|
attachmentPaths,
|
|
77
|
-
priorConversationsContext
|
|
78
|
+
priorConversationsContext,
|
|
79
|
+
technical
|
|
78
80
|
);
|
|
79
81
|
}
|
|
80
|
-
function buildClaudeUserPrompt(messages, context, attachmentPaths, priorConversationsContext) {
|
|
82
|
+
function buildClaudeUserPrompt(messages, context, attachmentPaths, priorConversationsContext, technical) {
|
|
81
83
|
return buildUserFacingPrompt(
|
|
82
84
|
null,
|
|
83
85
|
messages,
|
|
84
86
|
context,
|
|
85
87
|
attachmentPaths,
|
|
86
|
-
priorConversationsContext
|
|
88
|
+
priorConversationsContext,
|
|
89
|
+
technical
|
|
87
90
|
);
|
|
88
91
|
}
|
|
89
92
|
|
|
@@ -196,7 +199,8 @@ async function callClaudeCli(command, messages, context, options) {
|
|
|
196
199
|
messages,
|
|
197
200
|
context,
|
|
198
201
|
options.attachmentPaths,
|
|
199
|
-
options.priorConversationsContext
|
|
202
|
+
options.priorConversationsContext,
|
|
203
|
+
options.technical
|
|
200
204
|
);
|
|
201
205
|
const resolved = resolveCliCommand("claude", command);
|
|
202
206
|
const workspace = options.workspaceDir ?? process.env.AI_CLI_WORKSPACE ?? process.cwd();
|
|
@@ -235,7 +239,8 @@ async function callCursorCli(command, messages, context, options) {
|
|
|
235
239
|
messages,
|
|
236
240
|
context,
|
|
237
241
|
options.attachmentPaths,
|
|
238
|
-
options.priorConversationsContext
|
|
242
|
+
options.priorConversationsContext,
|
|
243
|
+
options.technical
|
|
239
244
|
);
|
|
240
245
|
const resolved = resolveCliCommand("cursor", command);
|
|
241
246
|
const workspace = options.workspaceDir ?? process.env.AI_CLI_WORKSPACE ?? process.cwd();
|
|
@@ -276,7 +281,8 @@ async function callAntigravityCli(command, messages, context, options) {
|
|
|
276
281
|
messages,
|
|
277
282
|
context,
|
|
278
283
|
options.attachmentPaths,
|
|
279
|
-
options.priorConversationsContext
|
|
284
|
+
options.priorConversationsContext,
|
|
285
|
+
options.technical
|
|
280
286
|
);
|
|
281
287
|
const prompt = `You are operating as a coding agent with full permission to read and edit files in this workspace. Do not introduce yourself. Do not ask what you are. Execute the user's latest request now (edit files and/or emit runtime tool JSON as instructed).
|
|
282
288
|
|
|
@@ -1082,6 +1088,112 @@ function createMaintainerProStoreFromEnv() {
|
|
|
1082
1088
|
if (!baseUrl || !apiKey) return null;
|
|
1083
1089
|
return createMaintainerProStore({ baseUrl, apiKey });
|
|
1084
1090
|
}
|
|
1091
|
+
|
|
1092
|
+
// src/workspace-inspect.ts
|
|
1093
|
+
import { z } from "zod";
|
|
1094
|
+
var inspectJsonSchema = z.object({
|
|
1095
|
+
kind: z.enum(["next", "vite", "html", "empty", "other"]).optional(),
|
|
1096
|
+
name: z.string().max(200).optional(),
|
|
1097
|
+
summary: z.string().max(800).optional(),
|
|
1098
|
+
scripts: z.object({
|
|
1099
|
+
ui: z.string().min(1).max(64).optional(),
|
|
1100
|
+
backend: z.string().min(1).max(64).optional(),
|
|
1101
|
+
app: z.string().min(1).max(64).optional()
|
|
1102
|
+
}).optional(),
|
|
1103
|
+
ports: z.object({
|
|
1104
|
+
ui: z.number().int().min(1).max(65535).optional(),
|
|
1105
|
+
backend: z.number().int().min(1).max(65535).optional(),
|
|
1106
|
+
app: z.number().int().min(1).max(65535).optional()
|
|
1107
|
+
}).optional(),
|
|
1108
|
+
issues: z.array(z.string().max(500)).max(20).optional(),
|
|
1109
|
+
fixes: z.array(z.string().max(500)).max(20).optional(),
|
|
1110
|
+
ready: z.boolean().optional()
|
|
1111
|
+
});
|
|
1112
|
+
function extractInspectJson(text) {
|
|
1113
|
+
const fence = text.match(/```json\s*([\s\S]*?)```/i);
|
|
1114
|
+
const raw = fence?.[1]?.trim() || text.match(/\{[\s\S]*\}/)?.[0];
|
|
1115
|
+
if (!raw) return null;
|
|
1116
|
+
try {
|
|
1117
|
+
const parsed = inspectJsonSchema.safeParse(JSON.parse(raw));
|
|
1118
|
+
return parsed.success ? parsed.data : null;
|
|
1119
|
+
} catch {
|
|
1120
|
+
return null;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
function setupSystemPrompt(input) {
|
|
1124
|
+
return `You are Maintainer Pro's local setup assistant on the developer's machine.
|
|
1125
|
+
|
|
1126
|
+
Workspace: ${input.workspaceDir}
|
|
1127
|
+
App name: ${input.appName || "the app"}
|
|
1128
|
+
|
|
1129
|
+
Inspect this repository (package.json, README, lockfiles, existing env files, how the UI and API start).
|
|
1130
|
+
You MAY edit files to fix setup problems that would block local run or the chat widget (env keys, widget mount, missing config).
|
|
1131
|
+
Do NOT start long-lived dev servers, tunnels, or install global tools.
|
|
1132
|
+
Do NOT run \`npm run dev\` or similar.
|
|
1133
|
+
|
|
1134
|
+
Script names you report MUST already exist in package.json. Omit chat/ai-server scripts.
|
|
1135
|
+
|
|
1136
|
+
End with a short human summary AND one \`\`\`json fence with:
|
|
1137
|
+
|
|
1138
|
+
{
|
|
1139
|
+
"kind": "next" | "vite" | "html" | "empty" | "other",
|
|
1140
|
+
"name": "package or folder name",
|
|
1141
|
+
"summary": "one sentence about this project",
|
|
1142
|
+
"scripts": { "ui": "dev:client", "backend": "dev:server" },
|
|
1143
|
+
"ports": { "ui": 5173, "backend": 4100 },
|
|
1144
|
+
"issues": ["remaining problems"],
|
|
1145
|
+
"fixes": ["what you changed"],
|
|
1146
|
+
"ready": true
|
|
1147
|
+
}`;
|
|
1148
|
+
}
|
|
1149
|
+
function setupUserMessage(input) {
|
|
1150
|
+
if (input.problem?.trim()) {
|
|
1151
|
+
return `The local bridge failed to keep this project running:
|
|
1152
|
+
|
|
1153
|
+
${input.problem.trim()}
|
|
1154
|
+
|
|
1155
|
+
${input.extraContext ? `${input.extraContext.trim()}
|
|
1156
|
+
|
|
1157
|
+
` : ""}Inspect the repo, fix the cause if you can, and return the JSON report.`;
|
|
1158
|
+
}
|
|
1159
|
+
return `${input.extraContext ? `${input.extraContext.trim()}
|
|
1160
|
+
|
|
1161
|
+
` : ""}Inspect this project, fix any setup gaps you can, and return the JSON report.`;
|
|
1162
|
+
}
|
|
1163
|
+
async function inspectAndRepairWorkspace(input) {
|
|
1164
|
+
const workspaceDir = input.workspaceDir;
|
|
1165
|
+
const provider = await resolveProvider({ preference: "auto" });
|
|
1166
|
+
const response = await callAi(
|
|
1167
|
+
[{ role: "user", content: setupUserMessage(input) }],
|
|
1168
|
+
{
|
|
1169
|
+
route: "/local-setup",
|
|
1170
|
+
pageTitle: input.appName || "Local setup",
|
|
1171
|
+
relevantFiles: ["package.json", "README.md", ".env", ".env.example"],
|
|
1172
|
+
data: {
|
|
1173
|
+
workspaceDir,
|
|
1174
|
+
problem: input.problem || null
|
|
1175
|
+
}
|
|
1176
|
+
},
|
|
1177
|
+
{
|
|
1178
|
+
systemPrompt: setupSystemPrompt(input),
|
|
1179
|
+
workspaceDir,
|
|
1180
|
+
technical: true
|
|
1181
|
+
}
|
|
1182
|
+
);
|
|
1183
|
+
const parsed = extractInspectJson(response.text);
|
|
1184
|
+
return {
|
|
1185
|
+
kind: parsed?.kind ?? "other",
|
|
1186
|
+
name: parsed?.name?.trim() || input.appName || "",
|
|
1187
|
+
summary: parsed?.summary?.trim() || response.text.replace(/```json[\s\S]*```/i, "").trim().slice(0, 400),
|
|
1188
|
+
scripts: parsed?.scripts ?? {},
|
|
1189
|
+
ports: parsed?.ports ?? {},
|
|
1190
|
+
issues: parsed?.issues ?? [],
|
|
1191
|
+
fixes: parsed?.fixes ?? [],
|
|
1192
|
+
ready: parsed?.ready ?? parsed?.issues?.length === 0,
|
|
1193
|
+
provider: response.provider || provider.id,
|
|
1194
|
+
rawText: response.text
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1085
1197
|
export {
|
|
1086
1198
|
WORKING_PROVIDER,
|
|
1087
1199
|
buildClaudeUserPrompt,
|
|
@@ -1102,6 +1214,7 @@ export {
|
|
|
1102
1214
|
createToolValidator,
|
|
1103
1215
|
formatClientContext,
|
|
1104
1216
|
getProviderPreference,
|
|
1217
|
+
inspectAndRepairWorkspace,
|
|
1105
1218
|
parseAiResponse,
|
|
1106
1219
|
providerLabel,
|
|
1107
1220
|
resolveCliBinary,
|