@inspecto-dev/plugin 0.3.9 → 0.3.11

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.
@@ -5,7 +5,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
5
5
  throw Error('Dynamic require of "' + x + '" is not supported');
6
6
  });
7
7
 
8
- // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.10_typescript@5.9.3_yaml@2.8.3/node_modules/tsup/assets/esm_shims.js
8
+ // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.7.0_postcss@8.5.14_typescript@5.9.3_yaml@2.8.4/node_modules/tsup/assets/esm_shims.js
9
9
  import path from "path";
10
10
  import { fileURLToPath } from "url";
11
11
  var getFilename = () => fileURLToPath(import.meta.url);
@@ -13,13 +13,14 @@ var getDirname = () => path.dirname(getFilename());
13
13
  var __dirname = /* @__PURE__ */ getDirname();
14
14
 
15
15
  // src/injectors/webpack.ts
16
- function getWebpackHtmlScript(serverPort) {
16
+ function getWebpackHtmlScript(serverPort, publicServerUrl) {
17
17
  return `
18
18
  window.__AI_INSPECTOR_PORT__ = ${serverPort};
19
+ window.__AI_INSPECTOR_SERVER_URL__ = '${publicServerUrl ?? `http://127.0.0.1:${serverPort}`}';
19
20
  window.addEventListener('load', () => {
20
21
  if (window.InspectoClient) {
21
22
  window.InspectoClient.mountInspector({
22
- serverUrl: 'http://0.0.0.0:' + window.__AI_INSPECTOR_PORT__,
23
+ serverUrl: window.__AI_INSPECTOR_SERVER_URL__,
23
24
  });
24
25
  }
25
26
  });
@@ -168,7 +169,8 @@ import { createDefu } from "defu";
168
169
  import {
169
170
  DEFAULT_PROVIDER_MODE,
170
171
  VALID_MODES,
171
- DEFAULT_INTENTS
172
+ DEFAULT_INTENTS,
173
+ isWorkflowConfig
172
174
  } from "@inspecto-dev/types";
173
175
 
174
176
  // src/utils/logger.ts
@@ -402,13 +404,28 @@ function resolveIntents(serverPrompts) {
402
404
  );
403
405
  continue;
404
406
  }
405
- if (!item.aiIntent) {
406
- configLogger.warn(`Intent "${item.id}" is missing required "aiIntent".`);
407
- continue;
407
+ if (item.kind === "workflow") {
408
+ if (!item.prompt) {
409
+ configLogger.warn(`Workflow "${item.id}" missing required "prompt", skipping`);
410
+ continue;
411
+ }
412
+ result.push({
413
+ kind: "workflow",
414
+ id: item.id,
415
+ label: item.label ?? item.id,
416
+ prompt: item.prompt,
417
+ confirm: item.confirm ?? false,
418
+ enabled: item.enabled ?? true
419
+ });
420
+ } else {
421
+ if (!item.aiIntent) {
422
+ configLogger.warn(`Intent "${item.id}" is missing required "aiIntent".`);
423
+ continue;
424
+ }
425
+ result.push(
426
+ baseMap.has(item.id) ? { ...baseMap.get(item.id), ...item } : item
427
+ );
408
428
  }
409
- result.push(
410
- baseMap.has(item.id) ? { ...baseMap.get(item.id), ...item } : item
411
- );
412
429
  }
413
430
  }
414
431
  return result;
@@ -428,26 +445,61 @@ function resolveIntents(serverPrompts) {
428
445
  configLogger.warn('Intent object missing required "id" field, skipping.');
429
446
  continue;
430
447
  }
431
- if (!item.aiIntent) {
432
- configLogger.warn(`Intent "${item.id}" is missing required "aiIntent".`);
433
- continue;
434
- }
435
- const existingIdx = merged.findIndex((i) => i.id === item.id);
436
- if (existingIdx !== -1) {
437
- if (item.enabled === false) {
438
- merged.splice(existingIdx, 1);
448
+ if (item.kind === "workflow") {
449
+ if (!item.prompt) {
450
+ configLogger.warn(`Workflow "${item.id}" missing required "prompt", skipping`);
451
+ continue;
452
+ }
453
+ const wfConfig = {
454
+ kind: "workflow",
455
+ id: item.id,
456
+ label: item.label ?? item.id,
457
+ prompt: item.prompt,
458
+ confirm: item.confirm ?? false,
459
+ enabled: item.enabled ?? true
460
+ };
461
+ const existingIdx = merged.findIndex((i) => i.id === item.id);
462
+ if (existingIdx !== -1) {
463
+ if (item.enabled === false) {
464
+ merged.splice(existingIdx, 1);
465
+ } else {
466
+ merged[existingIdx] = wfConfig;
467
+ }
439
468
  } else {
440
- merged[existingIdx] = { ...merged[existingIdx], ...item };
469
+ if (item.enabled !== false) {
470
+ merged.push(wfConfig);
471
+ }
441
472
  }
442
473
  } else {
443
- if (item.enabled !== false) {
444
- merged.push(item);
474
+ if (!item.aiIntent) {
475
+ configLogger.warn(`Intent "${item.id}" is missing required "aiIntent".`);
476
+ continue;
477
+ }
478
+ const existingIdx = merged.findIndex((i) => i.id === item.id);
479
+ if (existingIdx !== -1) {
480
+ if (item.enabled === false) {
481
+ merged.splice(existingIdx, 1);
482
+ } else {
483
+ merged[existingIdx] = { ...merged[existingIdx], ...item };
484
+ }
485
+ } else {
486
+ if (item.enabled !== false) {
487
+ merged.push(item);
488
+ }
445
489
  }
446
490
  }
447
491
  }
448
492
  }
449
493
  return merged;
450
494
  }
495
+ function resolveWorkflowSlots(intents) {
496
+ return intents.filter(isWorkflowConfig).filter((w) => w.enabled !== false).map((w) => ({
497
+ id: w.id,
498
+ label: w.label ?? w.id,
499
+ prompt: w.prompt,
500
+ confirm: w.confirm ?? false
501
+ }));
502
+ }
451
503
  var watchers = [];
452
504
  function watchConfig(onReload, cwd = process.cwd(), gitRoot) {
453
505
  if (isWatching) return;
@@ -677,6 +729,10 @@ function assertPathWithinIdeOpenScope(file, projectRoot) {
677
729
  }
678
730
  }
679
731
 
732
+ // src/server/annotation-dispatch.ts
733
+ import { exec } from "child_process";
734
+ import { promisify } from "util";
735
+
680
736
  // src/server/session-store.ts
681
737
  var DEFAULT_STATUS = "pending";
682
738
  function createAnnotationSessionStore(options = {}) {
@@ -684,8 +740,12 @@ function createAnnotationSessionStore(options = {}) {
684
740
  const listeners = /* @__PURE__ */ new Set();
685
741
  const now = options.now ?? (() => Date.now());
686
742
  const createId = options.createId ?? createRandomId;
687
- function findNewestMatchingSession(statuses) {
688
- return [...sessions.values()].filter((session) => statuses ? statuses.has(session.status) : true).sort((left, right) => right.updatedAt - left.updatedAt)[0] ?? null;
743
+ function findNewestMatchingSession(statuses, source) {
744
+ return [...sessions.values()].filter((session) => {
745
+ if (statuses && !statuses.has(session.status)) return false;
746
+ if (source && session.source !== source) return false;
747
+ return true;
748
+ }).sort((left, right) => right.updatedAt - left.updatedAt)[0] ?? null;
689
749
  }
690
750
  function updateSessionStatus(id, status) {
691
751
  const session = sessions.get(id);
@@ -745,7 +805,7 @@ function createAnnotationSessionStore(options = {}) {
745
805
  },
746
806
  async claimNextSession(options2 = {}) {
747
807
  const statuses = normalizeStatuses(DEFAULT_STATUS);
748
- const existingSession = findNewestMatchingSession(statuses);
808
+ const existingSession = findNewestMatchingSession(statuses, options2.source);
749
809
  if (existingSession) {
750
810
  return {
751
811
  session: claimSession(existingSession.id, statuses),
@@ -860,6 +920,7 @@ function cloneValue(value) {
860
920
  }
861
921
 
862
922
  // src/server/annotation-dispatch.ts
923
+ var execAsync = promisify(exec);
863
924
  var AnnotationDispatchError = class extends Error {
864
925
  constructor(message, errorCode) {
865
926
  super(message);
@@ -871,9 +932,14 @@ async function dispatchAnnotationsToAi(req, state, store = annotationSessionStor
871
932
  try {
872
933
  validateAnnotationDispatchRequest(req, state);
873
934
  const batch = normalizeAnnotationBatch(req);
874
- const prompt = buildAnnotationBatchPrompt(batch);
935
+ let prompt = buildAnnotationBatchPrompt(batch);
936
+ if (req.source === "workflow") {
937
+ prompt = await appendProjectMetadata(prompt, state);
938
+ }
875
939
  const deliveryMode = normalizeDeliveryMode(req.deliveryMode);
876
940
  const session = store.createSession({
941
+ source: req.source || "annotation",
942
+ ...req.workflowId ? { workflowId: req.workflowId } : {},
877
943
  instruction: batch.instruction,
878
944
  annotations: toSessionAnnotations(batch.annotations),
879
945
  deliveryMode,
@@ -899,6 +965,33 @@ async function dispatchAnnotationsToAi(req, state, store = annotationSessionStor
899
965
  };
900
966
  }
901
967
  }
968
+ async function appendProjectMetadata(prompt, state) {
969
+ const lines = ["\n## Project"];
970
+ lines.push(`- Root: ${state.projectRoot}`);
971
+ try {
972
+ const options = {
973
+ cwd: state.projectRoot,
974
+ encoding: "utf-8",
975
+ timeout: 2e3
976
+ };
977
+ const { stdout: branchStdout } = await execAsync("git branch --show-current", options);
978
+ const branch = branchStdout.trim();
979
+ lines.push(`- Branch: ${branch}`);
980
+ const { stdout: statusStdout } = await execAsync("git status --porcelain", options);
981
+ const statusRaw = statusStdout.trim();
982
+ const entries = statusRaw ? statusRaw.split("\n") : [];
983
+ const staged = entries.filter((l) => l[0] !== " " && l[0] !== "?").length;
984
+ const unstaged = entries.filter((l) => l[1] !== " " && l[1] !== "?").length;
985
+ const untracked = entries.filter((l) => l[0] === "?").length;
986
+ lines.push(`- Status: ${staged} staged, ${unstaged} unstaged, ${untracked} untracked`);
987
+ } catch (err) {
988
+ console.warn("[inspecto] Failed to get git status for workflow:", err);
989
+ lines.push("- Git: unavailable or check timeout");
990
+ }
991
+ return `${prompt}
992
+
993
+ ${lines.join("\n")}`;
994
+ }
902
995
  function normalizeDeliveryMode(input) {
903
996
  return input === "agent" ? "agent" : "ide";
904
997
  }
@@ -935,7 +1028,7 @@ function toSessionSummary(session) {
935
1028
  };
936
1029
  }
937
1030
  function validateAnnotationDispatchRequest(req, state) {
938
- if (!req.annotations.length) {
1031
+ if (!req.annotations.length && req.source !== "workflow") {
939
1032
  throw new AnnotationDispatchError("At least one annotation is required.", "INVALID_REQUEST");
940
1033
  }
941
1034
  for (const annotation of req.annotations) {
@@ -1037,6 +1130,7 @@ function getAnnotationDispatchErrorCode(error) {
1037
1130
  }
1038
1131
 
1039
1132
  // src/server/client-config.ts
1133
+ import { isAiIntentConfig } from "@inspecto-dev/types";
1040
1134
  async function buildClientConfig(serverState2) {
1041
1135
  const userConfig = loadUserConfigSync(false, serverState2.cwd, serverState2.configRoot);
1042
1136
  const promptsConfig = await loadPromptsConfig(false, serverState2.cwd, serverState2.configRoot);
@@ -1048,11 +1142,13 @@ async function buildClientConfig(serverState2) {
1048
1142
  const { scheme: _scheme, ...rest } = serverState2.ideInfo;
1049
1143
  info = rest;
1050
1144
  }
1145
+ const allIntents = resolveIntents(promptsConfig);
1051
1146
  return {
1052
1147
  ...info,
1053
- prompts: resolveIntents(promptsConfig),
1148
+ prompts: allIntents.filter(isAiIntentConfig),
1149
+ workflows: resolveWorkflowSlots(allIntents),
1054
1150
  hotKeys: userConfig["inspector.hotKey"] ?? "alt",
1055
- annotateDeliveryMode: userConfig["annotate.deliveryMode"] ?? "both",
1151
+ annotateDeliveryMode: userConfig["annotate.deliveryMode"] ?? "agent",
1056
1152
  includeSnippet: userConfig["prompt.includeSnippet"] ?? false,
1057
1153
  runtimeContext: {
1058
1154
  enabled: true,
@@ -1149,33 +1245,61 @@ import fs4 from "fs";
1149
1245
  import path5 from "path";
1150
1246
  import { execSync } from "child_process";
1151
1247
  var serverLogger3 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
1152
- function resolveProjectRoot() {
1153
- const cwd = process.cwd();
1154
- let gitRoot;
1248
+ function resolveGitRoot(_cwd) {
1155
1249
  try {
1156
- gitRoot = execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
1250
+ const output = execSync("git rev-parse --show-toplevel", { encoding: "utf-8" });
1251
+ return typeof output === "string" ? output.trim() : null;
1157
1252
  } catch (e) {
1158
1253
  serverLogger3.warn("Failed to resolve git root via git rev-parse:", e);
1159
- gitRoot = cwd;
1160
- }
1161
- const visited = /* @__PURE__ */ new Set();
1162
- const search = (start, stop) => {
1163
- let current = start;
1164
- while (!visited.has(current)) {
1165
- visited.add(current);
1166
- if (fs4.existsSync(path5.join(current, ".inspecto"))) return current;
1167
- if (current === stop) break;
1168
- const parent = path5.dirname(current);
1169
- if (parent === current) break;
1170
- current = parent;
1171
- }
1172
1254
  return null;
1173
- };
1174
- const cwdMatch = search(cwd, path5.parse(cwd).root);
1175
- if (cwdMatch) return cwdMatch;
1176
- const repoMatch = search(gitRoot, path5.parse(gitRoot).root);
1177
- if (repoMatch) return repoMatch;
1178
- return gitRoot;
1255
+ }
1256
+ }
1257
+ function findNearestAncestorWith(start, predicate) {
1258
+ let current = start;
1259
+ while (true) {
1260
+ if (predicate(current)) return current;
1261
+ const parent = path5.dirname(current);
1262
+ if (parent === current) break;
1263
+ current = parent;
1264
+ }
1265
+ return null;
1266
+ }
1267
+ function resolveWorkspaceRoot() {
1268
+ const cwd = process.cwd();
1269
+ const inspectoRoot = findNearestAncestorWith(
1270
+ cwd,
1271
+ (dir) => fs4.existsSync(path5.join(dir, ".inspecto"))
1272
+ );
1273
+ if (inspectoRoot) return inspectoRoot;
1274
+ const packageRoot = findNearestAncestorWith(
1275
+ cwd,
1276
+ (dir) => fs4.existsSync(path5.join(dir, "package.json"))
1277
+ );
1278
+ if (packageRoot) return packageRoot;
1279
+ return resolveGitRoot(cwd) ?? cwd;
1280
+ }
1281
+ function resolveProjectRoot() {
1282
+ const cwd = process.cwd();
1283
+ const packageRoot = findNearestAncestorWith(
1284
+ cwd,
1285
+ (dir) => fs4.existsSync(path5.join(dir, "package.json"))
1286
+ );
1287
+ if (packageRoot) return packageRoot;
1288
+ const inspectoRoot = findNearestAncestorWith(
1289
+ cwd,
1290
+ (dir) => fs4.existsSync(path5.join(dir, ".inspecto"))
1291
+ );
1292
+ if (inspectoRoot) return inspectoRoot;
1293
+ return resolveGitRoot(cwd) ?? cwd;
1294
+ }
1295
+
1296
+ // src/server/server-url.ts
1297
+ function resolveServerHost(cwd, configRoot) {
1298
+ if (process.env["VITEST"]) return "127.0.0.1";
1299
+ const userConfig = loadUserConfigSync(false, cwd, configRoot);
1300
+ const configuredHost = userConfig["server.host"]?.trim();
1301
+ if (configuredHost) return configuredHost;
1302
+ return "127.0.0.1";
1179
1303
  }
1180
1304
 
1181
1305
  // src/server/index.ts
@@ -1230,8 +1354,9 @@ async function startServer() {
1230
1354
  return serverState.port;
1231
1355
  }
1232
1356
  serverState.projectRoot = resolveProjectRoot();
1233
- serverState.configRoot = serverState.projectRoot;
1357
+ serverState.configRoot = resolveWorkspaceRoot();
1234
1358
  serverState.cwd = process.cwd();
1359
+ const serverHost = resolveServerHost(serverState.cwd, serverState.configRoot);
1235
1360
  portfinder.basePort = 5678;
1236
1361
  const port = await portfinder.getPortPromise();
1237
1362
  watchConfig(
@@ -1258,7 +1383,7 @@ async function startServer() {
1258
1383
  });
1259
1384
  });
1260
1385
  await new Promise((resolve2, reject) => {
1261
- serverInstance.listen(port, "0.0.0.0", () => {
1386
+ serverInstance.listen(port, serverHost, () => {
1262
1387
  serverInstance.unref();
1263
1388
  resolve2();
1264
1389
  });
@@ -1280,7 +1405,7 @@ async function startServer() {
1280
1405
  } catch {
1281
1406
  }
1282
1407
  });
1283
- serverLogger4.info(`server running at http://0.0.0.0:${port}`);
1408
+ serverLogger4.info(`server running at http://${serverHost}:${port}`);
1284
1409
  return port;
1285
1410
  }
1286
1411
  async function readBody(req) {
@@ -1642,10 +1767,23 @@ data: ${JSON.stringify({ ok: true })}
1642
1767
  }
1643
1768
  async function dispatchToAi(req) {
1644
1769
  const { location, snippet, prompt } = req;
1645
- const formattedPrompt = prompt ?? `Please help me with this code from \`${location.file}\` (line ${location.line}):
1770
+ if (prompt?.trim()) {
1771
+ const runtime2 = resolvePromptDispatchRuntime(serverState);
1772
+ return dispatchPromptThroughIde(runtime2, {
1773
+ prompt: prompt.trim()
1774
+ });
1775
+ }
1776
+ if (!location) {
1777
+ return {
1778
+ success: false,
1779
+ error: "Source location is required when prompt is omitted.",
1780
+ errorCode: "INVALID_REQUEST"
1781
+ };
1782
+ }
1783
+ const formattedPrompt = `Please help me with this code from \`${location.file}\` (line ${location.line}):
1646
1784
 
1647
1785
  \`\`\`
1648
- ${snippet}
1786
+ ${snippet ?? ""}
1649
1787
  \`\`\`
1650
1788
  `;
1651
1789
  const runtime = resolvePromptDispatchRuntime(serverState);
@@ -1654,7 +1792,7 @@ ${snippet}
1654
1792
  filePath: location.file,
1655
1793
  line: location.line,
1656
1794
  column: location.column,
1657
- snippet
1795
+ ...snippet !== void 0 ? { snippet } : {}
1658
1796
  });
1659
1797
  }
1660
1798
  function getBatchDispatchStatusCode(errorCode, success) {