@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 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
- 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.`;
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
- 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, buildClaudeUserPrompt, buildConversationPrompt, buildCursorPrompt, buildPriorConversationsContext, callAi, commandExists, createAntigravityProvider, createBuiltinProviders, createChatHandler, createClaudeProvider, createCursorProvider, createDefaultSystemPrompt, createLocalDirectoryStore, createMaintainerProStore, createMaintainerProStoreFromEnv, createToolValidator, formatClientContext, getProviderPreference, parseAiResponse, providerLabel, resolveCliBinary, resolveProvider, saveChatAttachments, toNextRoute };
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
- 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, buildClaudeUserPrompt, buildConversationPrompt, buildCursorPrompt, buildPriorConversationsContext, callAi, commandExists, createAntigravityProvider, createBuiltinProviders, createChatHandler, createClaudeProvider, createCursorProvider, createDefaultSystemPrompt, createLocalDirectoryStore, createMaintainerProStore, createMaintainerProStoreFromEnv, createToolValidator, formatClientContext, getProviderPreference, parseAiResponse, providerLabel, resolveCliBinary, resolveProvider, saveChatAttachments, toNextRoute };
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
- 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.`;
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maintainer-pro/ai-cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Maintainer Pro server SDK: Claude/Cursor CLI, chat HTTP handlers, and optional SQL persistence",
5
5
  "keywords": [
6
6
  "maintainer-pro",