@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);
@@ -137,7 +137,8 @@ import { createDefu } from "defu";
137
137
  import {
138
138
  DEFAULT_PROVIDER_MODE,
139
139
  VALID_MODES,
140
- DEFAULT_INTENTS
140
+ DEFAULT_INTENTS,
141
+ isWorkflowConfig
141
142
  } from "@inspecto-dev/types";
142
143
 
143
144
  // src/utils/logger.ts
@@ -371,13 +372,28 @@ function resolveIntents(serverPrompts) {
371
372
  );
372
373
  continue;
373
374
  }
374
- if (!item.aiIntent) {
375
- configLogger.warn(`Intent "${item.id}" is missing required "aiIntent".`);
376
- continue;
375
+ if (item.kind === "workflow") {
376
+ if (!item.prompt) {
377
+ configLogger.warn(`Workflow "${item.id}" missing required "prompt", skipping`);
378
+ continue;
379
+ }
380
+ result.push({
381
+ kind: "workflow",
382
+ id: item.id,
383
+ label: item.label ?? item.id,
384
+ prompt: item.prompt,
385
+ confirm: item.confirm ?? false,
386
+ enabled: item.enabled ?? true
387
+ });
388
+ } else {
389
+ if (!item.aiIntent) {
390
+ configLogger.warn(`Intent "${item.id}" is missing required "aiIntent".`);
391
+ continue;
392
+ }
393
+ result.push(
394
+ baseMap.has(item.id) ? { ...baseMap.get(item.id), ...item } : item
395
+ );
377
396
  }
378
- result.push(
379
- baseMap.has(item.id) ? { ...baseMap.get(item.id), ...item } : item
380
- );
381
397
  }
382
398
  }
383
399
  return result;
@@ -397,26 +413,61 @@ function resolveIntents(serverPrompts) {
397
413
  configLogger.warn('Intent object missing required "id" field, skipping.');
398
414
  continue;
399
415
  }
400
- if (!item.aiIntent) {
401
- configLogger.warn(`Intent "${item.id}" is missing required "aiIntent".`);
402
- continue;
403
- }
404
- const existingIdx = merged.findIndex((i) => i.id === item.id);
405
- if (existingIdx !== -1) {
406
- if (item.enabled === false) {
407
- merged.splice(existingIdx, 1);
416
+ if (item.kind === "workflow") {
417
+ if (!item.prompt) {
418
+ configLogger.warn(`Workflow "${item.id}" missing required "prompt", skipping`);
419
+ continue;
420
+ }
421
+ const wfConfig = {
422
+ kind: "workflow",
423
+ id: item.id,
424
+ label: item.label ?? item.id,
425
+ prompt: item.prompt,
426
+ confirm: item.confirm ?? false,
427
+ enabled: item.enabled ?? true
428
+ };
429
+ const existingIdx = merged.findIndex((i) => i.id === item.id);
430
+ if (existingIdx !== -1) {
431
+ if (item.enabled === false) {
432
+ merged.splice(existingIdx, 1);
433
+ } else {
434
+ merged[existingIdx] = wfConfig;
435
+ }
408
436
  } else {
409
- merged[existingIdx] = { ...merged[existingIdx], ...item };
437
+ if (item.enabled !== false) {
438
+ merged.push(wfConfig);
439
+ }
410
440
  }
411
441
  } else {
412
- if (item.enabled !== false) {
413
- merged.push(item);
442
+ if (!item.aiIntent) {
443
+ configLogger.warn(`Intent "${item.id}" is missing required "aiIntent".`);
444
+ continue;
445
+ }
446
+ const existingIdx = merged.findIndex((i) => i.id === item.id);
447
+ if (existingIdx !== -1) {
448
+ if (item.enabled === false) {
449
+ merged.splice(existingIdx, 1);
450
+ } else {
451
+ merged[existingIdx] = { ...merged[existingIdx], ...item };
452
+ }
453
+ } else {
454
+ if (item.enabled !== false) {
455
+ merged.push(item);
456
+ }
414
457
  }
415
458
  }
416
459
  }
417
460
  }
418
461
  return merged;
419
462
  }
463
+ function resolveWorkflowSlots(intents) {
464
+ return intents.filter(isWorkflowConfig).filter((w) => w.enabled !== false).map((w) => ({
465
+ id: w.id,
466
+ label: w.label ?? w.id,
467
+ prompt: w.prompt,
468
+ confirm: w.confirm ?? false
469
+ }));
470
+ }
420
471
  var watchers = [];
421
472
  function watchConfig(onReload, cwd = process.cwd(), gitRoot) {
422
473
  if (isWatching) return;
@@ -646,6 +697,10 @@ function assertPathWithinIdeOpenScope(file, projectRoot) {
646
697
  }
647
698
  }
648
699
 
700
+ // src/server/annotation-dispatch.ts
701
+ import { exec } from "child_process";
702
+ import { promisify } from "util";
703
+
649
704
  // src/server/session-store.ts
650
705
  var DEFAULT_STATUS = "pending";
651
706
  function createAnnotationSessionStore(options = {}) {
@@ -653,8 +708,12 @@ function createAnnotationSessionStore(options = {}) {
653
708
  const listeners = /* @__PURE__ */ new Set();
654
709
  const now = options.now ?? (() => Date.now());
655
710
  const createId = options.createId ?? createRandomId;
656
- function findNewestMatchingSession(statuses) {
657
- return [...sessions.values()].filter((session) => statuses ? statuses.has(session.status) : true).sort((left, right) => right.updatedAt - left.updatedAt)[0] ?? null;
711
+ function findNewestMatchingSession(statuses, source) {
712
+ return [...sessions.values()].filter((session) => {
713
+ if (statuses && !statuses.has(session.status)) return false;
714
+ if (source && session.source !== source) return false;
715
+ return true;
716
+ }).sort((left, right) => right.updatedAt - left.updatedAt)[0] ?? null;
658
717
  }
659
718
  function updateSessionStatus(id, status) {
660
719
  const session = sessions.get(id);
@@ -714,7 +773,7 @@ function createAnnotationSessionStore(options = {}) {
714
773
  },
715
774
  async claimNextSession(options2 = {}) {
716
775
  const statuses = normalizeStatuses(DEFAULT_STATUS);
717
- const existingSession = findNewestMatchingSession(statuses);
776
+ const existingSession = findNewestMatchingSession(statuses, options2.source);
718
777
  if (existingSession) {
719
778
  return {
720
779
  session: claimSession(existingSession.id, statuses),
@@ -829,6 +888,7 @@ function cloneValue(value) {
829
888
  }
830
889
 
831
890
  // src/server/annotation-dispatch.ts
891
+ var execAsync = promisify(exec);
832
892
  var AnnotationDispatchError = class extends Error {
833
893
  constructor(message, errorCode) {
834
894
  super(message);
@@ -840,9 +900,14 @@ async function dispatchAnnotationsToAi(req, state, store = annotationSessionStor
840
900
  try {
841
901
  validateAnnotationDispatchRequest(req, state);
842
902
  const batch = normalizeAnnotationBatch(req);
843
- const prompt = buildAnnotationBatchPrompt(batch);
903
+ let prompt = buildAnnotationBatchPrompt(batch);
904
+ if (req.source === "workflow") {
905
+ prompt = await appendProjectMetadata(prompt, state);
906
+ }
844
907
  const deliveryMode = normalizeDeliveryMode(req.deliveryMode);
845
908
  const session = store.createSession({
909
+ source: req.source || "annotation",
910
+ ...req.workflowId ? { workflowId: req.workflowId } : {},
846
911
  instruction: batch.instruction,
847
912
  annotations: toSessionAnnotations(batch.annotations),
848
913
  deliveryMode,
@@ -868,6 +933,33 @@ async function dispatchAnnotationsToAi(req, state, store = annotationSessionStor
868
933
  };
869
934
  }
870
935
  }
936
+ async function appendProjectMetadata(prompt, state) {
937
+ const lines = ["\n## Project"];
938
+ lines.push(`- Root: ${state.projectRoot}`);
939
+ try {
940
+ const options = {
941
+ cwd: state.projectRoot,
942
+ encoding: "utf-8",
943
+ timeout: 2e3
944
+ };
945
+ const { stdout: branchStdout } = await execAsync("git branch --show-current", options);
946
+ const branch = branchStdout.trim();
947
+ lines.push(`- Branch: ${branch}`);
948
+ const { stdout: statusStdout } = await execAsync("git status --porcelain", options);
949
+ const statusRaw = statusStdout.trim();
950
+ const entries = statusRaw ? statusRaw.split("\n") : [];
951
+ const staged = entries.filter((l) => l[0] !== " " && l[0] !== "?").length;
952
+ const unstaged = entries.filter((l) => l[1] !== " " && l[1] !== "?").length;
953
+ const untracked = entries.filter((l) => l[0] === "?").length;
954
+ lines.push(`- Status: ${staged} staged, ${unstaged} unstaged, ${untracked} untracked`);
955
+ } catch (err) {
956
+ console.warn("[inspecto] Failed to get git status for workflow:", err);
957
+ lines.push("- Git: unavailable or check timeout");
958
+ }
959
+ return `${prompt}
960
+
961
+ ${lines.join("\n")}`;
962
+ }
871
963
  function normalizeDeliveryMode(input) {
872
964
  return input === "agent" ? "agent" : "ide";
873
965
  }
@@ -904,7 +996,7 @@ function toSessionSummary(session) {
904
996
  };
905
997
  }
906
998
  function validateAnnotationDispatchRequest(req, state) {
907
- if (!req.annotations.length) {
999
+ if (!req.annotations.length && req.source !== "workflow") {
908
1000
  throw new AnnotationDispatchError("At least one annotation is required.", "INVALID_REQUEST");
909
1001
  }
910
1002
  for (const annotation of req.annotations) {
@@ -1006,6 +1098,7 @@ function getAnnotationDispatchErrorCode(error) {
1006
1098
  }
1007
1099
 
1008
1100
  // src/server/client-config.ts
1101
+ import { isAiIntentConfig } from "@inspecto-dev/types";
1009
1102
  async function buildClientConfig(serverState2) {
1010
1103
  const userConfig = loadUserConfigSync(false, serverState2.cwd, serverState2.configRoot);
1011
1104
  const promptsConfig = await loadPromptsConfig(false, serverState2.cwd, serverState2.configRoot);
@@ -1017,11 +1110,13 @@ async function buildClientConfig(serverState2) {
1017
1110
  const { scheme: _scheme, ...rest } = serverState2.ideInfo;
1018
1111
  info = rest;
1019
1112
  }
1113
+ const allIntents = resolveIntents(promptsConfig);
1020
1114
  return {
1021
1115
  ...info,
1022
- prompts: resolveIntents(promptsConfig),
1116
+ prompts: allIntents.filter(isAiIntentConfig),
1117
+ workflows: resolveWorkflowSlots(allIntents),
1023
1118
  hotKeys: userConfig["inspector.hotKey"] ?? "alt",
1024
- annotateDeliveryMode: userConfig["annotate.deliveryMode"] ?? "both",
1119
+ annotateDeliveryMode: userConfig["annotate.deliveryMode"] ?? "agent",
1025
1120
  includeSnippet: userConfig["prompt.includeSnippet"] ?? false,
1026
1121
  runtimeContext: {
1027
1122
  enabled: true,
@@ -1118,33 +1213,61 @@ import fs4 from "fs";
1118
1213
  import path5 from "path";
1119
1214
  import { execSync } from "child_process";
1120
1215
  var serverLogger3 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
1121
- function resolveProjectRoot() {
1122
- const cwd = process.cwd();
1123
- let gitRoot;
1216
+ function resolveGitRoot(_cwd) {
1124
1217
  try {
1125
- gitRoot = execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
1218
+ const output = execSync("git rev-parse --show-toplevel", { encoding: "utf-8" });
1219
+ return typeof output === "string" ? output.trim() : null;
1126
1220
  } catch (e) {
1127
1221
  serverLogger3.warn("Failed to resolve git root via git rev-parse:", e);
1128
- gitRoot = cwd;
1129
- }
1130
- const visited = /* @__PURE__ */ new Set();
1131
- const search = (start, stop) => {
1132
- let current = start;
1133
- while (!visited.has(current)) {
1134
- visited.add(current);
1135
- if (fs4.existsSync(path5.join(current, ".inspecto"))) return current;
1136
- if (current === stop) break;
1137
- const parent = path5.dirname(current);
1138
- if (parent === current) break;
1139
- current = parent;
1140
- }
1141
1222
  return null;
1142
- };
1143
- const cwdMatch = search(cwd, path5.parse(cwd).root);
1144
- if (cwdMatch) return cwdMatch;
1145
- const repoMatch = search(gitRoot, path5.parse(gitRoot).root);
1146
- if (repoMatch) return repoMatch;
1147
- return gitRoot;
1223
+ }
1224
+ }
1225
+ function findNearestAncestorWith(start, predicate) {
1226
+ let current = start;
1227
+ while (true) {
1228
+ if (predicate(current)) return current;
1229
+ const parent = path5.dirname(current);
1230
+ if (parent === current) break;
1231
+ current = parent;
1232
+ }
1233
+ return null;
1234
+ }
1235
+ function resolveWorkspaceRoot() {
1236
+ const cwd = process.cwd();
1237
+ const inspectoRoot = findNearestAncestorWith(
1238
+ cwd,
1239
+ (dir) => fs4.existsSync(path5.join(dir, ".inspecto"))
1240
+ );
1241
+ if (inspectoRoot) return inspectoRoot;
1242
+ const packageRoot = findNearestAncestorWith(
1243
+ cwd,
1244
+ (dir) => fs4.existsSync(path5.join(dir, "package.json"))
1245
+ );
1246
+ if (packageRoot) return packageRoot;
1247
+ return resolveGitRoot(cwd) ?? cwd;
1248
+ }
1249
+ function resolveProjectRoot() {
1250
+ const cwd = process.cwd();
1251
+ const packageRoot = findNearestAncestorWith(
1252
+ cwd,
1253
+ (dir) => fs4.existsSync(path5.join(dir, "package.json"))
1254
+ );
1255
+ if (packageRoot) return packageRoot;
1256
+ const inspectoRoot = findNearestAncestorWith(
1257
+ cwd,
1258
+ (dir) => fs4.existsSync(path5.join(dir, ".inspecto"))
1259
+ );
1260
+ if (inspectoRoot) return inspectoRoot;
1261
+ return resolveGitRoot(cwd) ?? cwd;
1262
+ }
1263
+
1264
+ // src/server/server-url.ts
1265
+ function resolveServerHost(cwd, configRoot) {
1266
+ if (process.env["VITEST"]) return "127.0.0.1";
1267
+ const userConfig = loadUserConfigSync(false, cwd, configRoot);
1268
+ const configuredHost = userConfig["server.host"]?.trim();
1269
+ if (configuredHost) return configuredHost;
1270
+ return "127.0.0.1";
1148
1271
  }
1149
1272
 
1150
1273
  // src/server/index.ts
@@ -1199,8 +1322,9 @@ async function startServer() {
1199
1322
  return serverState.port;
1200
1323
  }
1201
1324
  serverState.projectRoot = resolveProjectRoot();
1202
- serverState.configRoot = serverState.projectRoot;
1325
+ serverState.configRoot = resolveWorkspaceRoot();
1203
1326
  serverState.cwd = process.cwd();
1327
+ const serverHost = resolveServerHost(serverState.cwd, serverState.configRoot);
1204
1328
  portfinder.basePort = 5678;
1205
1329
  const port = await portfinder.getPortPromise();
1206
1330
  watchConfig(
@@ -1227,7 +1351,7 @@ async function startServer() {
1227
1351
  });
1228
1352
  });
1229
1353
  await new Promise((resolve2, reject) => {
1230
- serverInstance.listen(port, "0.0.0.0", () => {
1354
+ serverInstance.listen(port, serverHost, () => {
1231
1355
  serverInstance.unref();
1232
1356
  resolve2();
1233
1357
  });
@@ -1249,7 +1373,7 @@ async function startServer() {
1249
1373
  } catch {
1250
1374
  }
1251
1375
  });
1252
- serverLogger4.info(`server running at http://0.0.0.0:${port}`);
1376
+ serverLogger4.info(`server running at http://${serverHost}:${port}`);
1253
1377
  return port;
1254
1378
  }
1255
1379
  async function readBody(req) {
@@ -1611,10 +1735,23 @@ data: ${JSON.stringify({ ok: true })}
1611
1735
  }
1612
1736
  async function dispatchToAi(req) {
1613
1737
  const { location, snippet, prompt } = req;
1614
- const formattedPrompt = prompt ?? `Please help me with this code from \`${location.file}\` (line ${location.line}):
1738
+ if (prompt?.trim()) {
1739
+ const runtime2 = resolvePromptDispatchRuntime(serverState);
1740
+ return dispatchPromptThroughIde(runtime2, {
1741
+ prompt: prompt.trim()
1742
+ });
1743
+ }
1744
+ if (!location) {
1745
+ return {
1746
+ success: false,
1747
+ error: "Source location is required when prompt is omitted.",
1748
+ errorCode: "INVALID_REQUEST"
1749
+ };
1750
+ }
1751
+ const formattedPrompt = `Please help me with this code from \`${location.file}\` (line ${location.line}):
1615
1752
 
1616
1753
  \`\`\`
1617
- ${snippet}
1754
+ ${snippet ?? ""}
1618
1755
  \`\`\`
1619
1756
  `;
1620
1757
  const runtime = resolvePromptDispatchRuntime(serverState);
@@ -1623,7 +1760,7 @@ ${snippet}
1623
1760
  filePath: location.file,
1624
1761
  line: location.line,
1625
1762
  column: location.column,
1626
- snippet
1763
+ ...snippet !== void 0 ? { snippet } : {}
1627
1764
  });
1628
1765
  }
1629
1766
  function getBatchDispatchStatusCode(errorCode, success) {
@@ -1666,13 +1803,14 @@ var resolveClientModule = () => {
1666
1803
  };
1667
1804
 
1668
1805
  // src/injectors/webpack.ts
1669
- function getWebpackHtmlScript(serverPort2) {
1806
+ function getWebpackHtmlScript(serverPort2, publicServerUrl) {
1670
1807
  return `
1671
1808
  window.__AI_INSPECTOR_PORT__ = ${serverPort2};
1809
+ window.__AI_INSPECTOR_SERVER_URL__ = '${publicServerUrl ?? `http://127.0.0.1:${serverPort2}`}';
1672
1810
  window.addEventListener('load', () => {
1673
1811
  if (window.InspectoClient) {
1674
1812
  window.InspectoClient.mountInspector({
1675
- serverUrl: 'http://0.0.0.0:' + window.__AI_INSPECTOR_PORT__,
1813
+ serverUrl: window.__AI_INSPECTOR_SERVER_URL__,
1676
1814
  });
1677
1815
  }
1678
1816
  });