@tonyclaw/agent-inspector 2.0.38 → 2.0.39

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.
Files changed (29) hide show
  1. package/.output/cli.js +528 -24
  2. package/.output/nitro.json +1 -1
  3. package/.output/public/assets/{CompareDrawer-hc5OoAls.js → CompareDrawer-Jfe6M6DQ.js} +1 -1
  4. package/.output/public/assets/{ProxyViewerContainer-BMKBs8Qz.js → ProxyViewerContainer-CQKV0Bqq.js} +8 -8
  5. package/.output/public/assets/{ReplayDialog-CN-KM2nu.js → ReplayDialog-B58TbTtY.js} +1 -1
  6. package/.output/public/assets/{RequestAnatomy-B1kTlAUk.js → RequestAnatomy-d4P6JNiP.js} +1 -1
  7. package/.output/public/assets/{ResponseView-BchUjKjt.js → ResponseView-Bum1a3tG.js} +1 -1
  8. package/.output/public/assets/{StreamingChunkSequence-BQOxI9Hi.js → StreamingChunkSequence-CjBDp5E9.js} +1 -1
  9. package/.output/public/assets/_sessionId-BupV2gtf.js +1 -0
  10. package/.output/public/assets/index-DHSww5em.js +1 -0
  11. package/.output/public/assets/{main-Blykwzsn.js → main-DK9PoBjx.js} +2 -2
  12. package/.output/server/{_sessionId-BUNZz2zZ.mjs → _sessionId-DjarbXpT.mjs} +2 -2
  13. package/.output/server/_ssr/{CompareDrawer-VaDYPM5G.mjs → CompareDrawer-C5Z6AlUk.mjs} +2 -2
  14. package/.output/server/_ssr/{ProxyViewerContainer-CpdWs7Pk.mjs → ProxyViewerContainer-4pXsBywn.mjs} +34 -6
  15. package/.output/server/_ssr/{ReplayDialog-DbfIjJhb.mjs → ReplayDialog-w1vc1p5e.mjs} +3 -3
  16. package/.output/server/_ssr/{RequestAnatomy-CrD8h6As.mjs → RequestAnatomy-2HaBhsNV.mjs} +2 -2
  17. package/.output/server/_ssr/{ResponseView-BalvDwtU.mjs → ResponseView-SO_Y_W85.mjs} +2 -2
  18. package/.output/server/_ssr/{StreamingChunkSequence-RxatDbPV.mjs → StreamingChunkSequence-CXEa2DkO.mjs} +2 -2
  19. package/.output/server/_ssr/{index-11qAtV9d.mjs → index-DuCGt4a2.mjs} +2 -2
  20. package/.output/server/_ssr/index.mjs +2 -2
  21. package/.output/server/_ssr/{router-C4OdRnqX.mjs → router-D0BaSzkZ.mjs} +3 -3
  22. package/.output/server/{_tanstack-start-manifest_v-F6bwcl33.mjs → _tanstack-start-manifest_v-6N4SODyd.mjs} +1 -1
  23. package/.output/server/index.mjs +57 -57
  24. package/README.md +7 -0
  25. package/package.json +1 -1
  26. package/src/cli/onboard.ts +629 -21
  27. package/src/components/providers/SettingsDialog.tsx +30 -0
  28. package/.output/public/assets/_sessionId-CVw5e8TK.js +0 -1
  29. package/.output/public/assets/index-BdUTMQL6.js +0 -1
package/.output/cli.js CHANGED
@@ -938,6 +938,11 @@ function parseFlags(argv) {
938
938
  skipToolWire: false,
939
939
  codexOnly: false,
940
940
  skipCodexSkill: false,
941
+ opencode: false,
942
+ opencodeOnly: false,
943
+ opencodeConfig: null,
944
+ opencodeTransport: "local",
945
+ opencodeMcpUrl: DEFAULT_MCP_URL,
941
946
  uninstall: false,
942
947
  status: false,
943
948
  json: false,
@@ -967,6 +972,58 @@ function parseFlags(argv) {
967
972
  case "--skip-codex-skill":
968
973
  flags.skipCodexSkill = true;
969
974
  break;
975
+ case "--opencode":
976
+ flags.opencode = true;
977
+ break;
978
+ case "--opencode-only":
979
+ flags.opencode = true;
980
+ flags.opencodeOnly = true;
981
+ break;
982
+ case "--opencode-config": {
983
+ const next = argv[i + 1];
984
+ if (next === void 0) {
985
+ process.stderr.write(
986
+ "agent-inspector onboard: --opencode-config requires a path argument\n"
987
+ );
988
+ process.exit(2);
989
+ }
990
+ flags.opencodeConfig = next;
991
+ i++;
992
+ break;
993
+ }
994
+ case "--opencode-transport": {
995
+ const next = argv[i + 1];
996
+ if (next === void 0) {
997
+ process.stderr.write(
998
+ "agent-inspector onboard: --opencode-transport requires local or remote\n"
999
+ );
1000
+ process.exit(2);
1001
+ }
1002
+ switch (next) {
1003
+ case "local":
1004
+ case "remote":
1005
+ flags.opencodeTransport = next;
1006
+ break;
1007
+ default:
1008
+ process.stderr.write(
1009
+ `agent-inspector onboard: invalid --opencode-transport: ${next}
1010
+ `
1011
+ );
1012
+ process.exit(2);
1013
+ }
1014
+ i++;
1015
+ break;
1016
+ }
1017
+ case "--opencode-mcp-url": {
1018
+ const next = argv[i + 1];
1019
+ if (next === void 0) {
1020
+ process.stderr.write("agent-inspector onboard: --opencode-mcp-url requires a URL\n");
1021
+ process.exit(2);
1022
+ }
1023
+ flags.opencodeMcpUrl = next;
1024
+ i++;
1025
+ break;
1026
+ }
970
1027
  case "--uninstall":
971
1028
  flags.uninstall = true;
972
1029
  break;
@@ -1024,6 +1081,12 @@ Options:
1024
1081
  --skip-tool-wire Skip the wire-tool phase in the Claude skill body
1025
1082
  --skip-codex-skill Install only the Claude Code skill and slash command
1026
1083
  --codex-only Install only the Codex skill
1084
+ --opencode Also configure OpenCode MCP in opencode.json/jsonc
1085
+ --opencode-only Configure only OpenCode MCP
1086
+ --opencode-config <path> Override OpenCode config path (default: ~/.config/opencode/opencode.json)
1087
+ --opencode-transport <local|remote>
1088
+ OpenCode MCP transport to write (default: local)
1089
+ --opencode-mcp-url <url> Agent Inspector MCP endpoint for OpenCode
1027
1090
  --uninstall Remove generated files whose version matches this package
1028
1091
  --status Show installed onboarding file versions and suggested action
1029
1092
  --json Emit machine-readable JSON with --status
@@ -1052,6 +1115,356 @@ function resolveTargets(flags) {
1052
1115
  function isObject(value) {
1053
1116
  return typeof value === "object" && value !== null;
1054
1117
  }
1118
+ function isOpenCodeEnabled(flags) {
1119
+ return flags.opencode || flags.opencodeOnly;
1120
+ }
1121
+ function resolveOpenCodeConfigPath(flags) {
1122
+ if (flags.opencodeConfig !== null) {
1123
+ return flags.opencodeConfig;
1124
+ }
1125
+ const configDir = join2(homedir2(), ".config", "opencode");
1126
+ const jsoncPath = join2(configDir, "opencode.jsonc");
1127
+ if (existsSync2(jsoncPath)) {
1128
+ return jsoncPath;
1129
+ }
1130
+ return join2(configDir, "opencode.json");
1131
+ }
1132
+ function stripJsoncComments(raw) {
1133
+ let result = "";
1134
+ let index = 0;
1135
+ let inString = false;
1136
+ let stringChar = "";
1137
+ while (index < raw.length) {
1138
+ const ch = raw[index];
1139
+ const next = raw[index + 1];
1140
+ if (inString) {
1141
+ if (ch === "\\" && next !== void 0) {
1142
+ result += ch + next;
1143
+ index += 2;
1144
+ continue;
1145
+ }
1146
+ if (ch === stringChar) {
1147
+ inString = false;
1148
+ stringChar = "";
1149
+ }
1150
+ result += ch;
1151
+ index += 1;
1152
+ continue;
1153
+ }
1154
+ if (ch === '"' || ch === "'") {
1155
+ inString = true;
1156
+ stringChar = ch;
1157
+ result += ch;
1158
+ index += 1;
1159
+ continue;
1160
+ }
1161
+ if (ch === "/" && next === "/") {
1162
+ while (index < raw.length && raw[index] !== "\n") {
1163
+ index += 1;
1164
+ }
1165
+ continue;
1166
+ }
1167
+ if (ch === "/" && next === "*") {
1168
+ index += 2;
1169
+ while (index < raw.length - 1 && !(raw[index] === "*" && raw[index + 1] === "/")) {
1170
+ index += 1;
1171
+ }
1172
+ index += 2;
1173
+ continue;
1174
+ }
1175
+ result += ch;
1176
+ index += 1;
1177
+ }
1178
+ return result;
1179
+ }
1180
+ function readOpenCodeConfig(path) {
1181
+ if (!existsSync2(path)) {
1182
+ return { ok: true, exists: false, config: {} };
1183
+ }
1184
+ try {
1185
+ const raw = readFileSync(path, "utf8");
1186
+ if (raw.trim().length === 0) {
1187
+ return { ok: true, exists: true, config: {} };
1188
+ }
1189
+ const parsed = JSON.parse(stripJsoncComments(raw));
1190
+ if (isObject(parsed)) {
1191
+ return { ok: true, exists: true, config: parsed };
1192
+ }
1193
+ return {
1194
+ ok: false,
1195
+ exists: true,
1196
+ message: "OpenCode config root must be a JSON object"
1197
+ };
1198
+ } catch (err) {
1199
+ const message = err instanceof Error ? err.message : String(err);
1200
+ return {
1201
+ ok: false,
1202
+ exists: true,
1203
+ message
1204
+ };
1205
+ }
1206
+ }
1207
+ function buildOpenCodeMcpEntry(transport, endpoint) {
1208
+ switch (transport) {
1209
+ case "local":
1210
+ return {
1211
+ type: "local",
1212
+ command: ["agent-inspector-mcp", "stdio", "--url", endpoint],
1213
+ enabled: true
1214
+ };
1215
+ case "remote":
1216
+ return {
1217
+ type: "remote",
1218
+ url: endpoint,
1219
+ enabled: true
1220
+ };
1221
+ default:
1222
+ return {
1223
+ type: "local",
1224
+ command: ["agent-inspector-mcp", "stdio", "--url", endpoint],
1225
+ enabled: true
1226
+ };
1227
+ }
1228
+ }
1229
+ function stringArrayEquals(value, expected) {
1230
+ if (!Array.isArray(value) || value.length !== expected.length) {
1231
+ return false;
1232
+ }
1233
+ return value.every((item, index) => item === expected[index]);
1234
+ }
1235
+ function isOpenCodeLocalEntry(value, endpoint, ignoreEnabled) {
1236
+ if (!isObject(value)) {
1237
+ return false;
1238
+ }
1239
+ const enabled = value["enabled"];
1240
+ if (!ignoreEnabled && enabled === false) {
1241
+ return false;
1242
+ }
1243
+ const env = value["environment"];
1244
+ const usesUrlArg = stringArrayEquals(value["command"], [
1245
+ "agent-inspector-mcp",
1246
+ "stdio",
1247
+ "--url",
1248
+ endpoint
1249
+ ]);
1250
+ const usesEnv = stringArrayEquals(value["command"], ["agent-inspector-mcp", "stdio"]) && isObject(env) && env["AGENT_INSPECTOR_MCP_URL"] === endpoint;
1251
+ return value["type"] === "local" && (usesUrlArg || usesEnv);
1252
+ }
1253
+ function isOpenCodeRemoteEntry(value, endpoint, ignoreEnabled) {
1254
+ if (!isObject(value)) {
1255
+ return false;
1256
+ }
1257
+ const enabled = value["enabled"];
1258
+ if (!ignoreEnabled && enabled === false) {
1259
+ return false;
1260
+ }
1261
+ return value["type"] === "remote" && value["url"] === endpoint;
1262
+ }
1263
+ function isKnownOpenCodeEntry(value, endpoint, ignoreEnabled) {
1264
+ return isOpenCodeLocalEntry(value, endpoint, ignoreEnabled) || isOpenCodeRemoteEntry(value, endpoint, ignoreEnabled);
1265
+ }
1266
+ function detectOpenCodeTransport(value) {
1267
+ if (!isObject(value)) {
1268
+ return "unknown";
1269
+ }
1270
+ switch (value["type"]) {
1271
+ case "local":
1272
+ return "local";
1273
+ case "remote":
1274
+ return "remote";
1275
+ default:
1276
+ return "unknown";
1277
+ }
1278
+ }
1279
+ function openCodeActionForStatus(status) {
1280
+ switch (status.state) {
1281
+ case "missing":
1282
+ return "agent-inspector onboard --opencode-only";
1283
+ case "configured":
1284
+ return "opencode mcp list";
1285
+ case "disabled":
1286
+ case "custom":
1287
+ return "agent-inspector onboard --opencode-only --force";
1288
+ case "invalid":
1289
+ return "fix OpenCode JSON/JSONC, then rerun agent-inspector onboard --opencode-only";
1290
+ default:
1291
+ return "agent-inspector onboard --opencode-only";
1292
+ }
1293
+ }
1294
+ function makeOpenCodeStatus(path, state, transport, endpoint, detail) {
1295
+ const status = {
1296
+ label: "OpenCode MCP config",
1297
+ path,
1298
+ state,
1299
+ transport,
1300
+ endpoint,
1301
+ action: "agent-inspector onboard --opencode-only",
1302
+ detail
1303
+ };
1304
+ return {
1305
+ ...status,
1306
+ action: openCodeActionForStatus(status)
1307
+ };
1308
+ }
1309
+ function getOpenCodeEntry(config) {
1310
+ const mcp = config["mcp"];
1311
+ if (!isObject(mcp)) {
1312
+ return void 0;
1313
+ }
1314
+ return mcp["agent-inspector"];
1315
+ }
1316
+ function buildOpenCodeConfig(config, transport, endpoint) {
1317
+ const schema = typeof config["$schema"] === "string" ? config["$schema"] : "https://opencode.ai/config.json";
1318
+ const nextConfig = {
1319
+ $schema: schema
1320
+ };
1321
+ for (const [key, value] of Object.entries(config)) {
1322
+ if (key !== "$schema" && key !== "mcp") {
1323
+ nextConfig[key] = value;
1324
+ }
1325
+ }
1326
+ const currentMcp = config["mcp"];
1327
+ const nextMcp = isObject(currentMcp) ? { ...currentMcp } : {};
1328
+ nextMcp["agent-inspector"] = buildOpenCodeMcpEntry(transport, endpoint);
1329
+ nextConfig["mcp"] = nextMcp;
1330
+ return nextConfig;
1331
+ }
1332
+ function formatOpenCodeConfig(config) {
1333
+ return `${JSON.stringify(config, null, 2)}
1334
+ `;
1335
+ }
1336
+ function removeOpenCodeEntry(config) {
1337
+ const nextConfig = {};
1338
+ for (const [key, value] of Object.entries(config)) {
1339
+ if (key !== "mcp") {
1340
+ nextConfig[key] = value;
1341
+ }
1342
+ }
1343
+ const currentMcp = config["mcp"];
1344
+ if (!isObject(currentMcp)) {
1345
+ return nextConfig;
1346
+ }
1347
+ const nextMcp = {};
1348
+ for (const [key, value] of Object.entries(currentMcp)) {
1349
+ if (key !== "agent-inspector") {
1350
+ nextMcp[key] = value;
1351
+ }
1352
+ }
1353
+ if (Object.keys(nextMcp).length > 0) {
1354
+ nextConfig["mcp"] = nextMcp;
1355
+ }
1356
+ return nextConfig;
1357
+ }
1358
+ function readOpenCodeStatus(flags) {
1359
+ const path = resolveOpenCodeConfigPath(flags);
1360
+ const read = readOpenCodeConfig(path);
1361
+ if (!read.ok) {
1362
+ return makeOpenCodeStatus(path, "invalid", "unknown", flags.opencodeMcpUrl, read.message);
1363
+ }
1364
+ const entry = getOpenCodeEntry(read.config);
1365
+ if (entry === void 0) {
1366
+ const detail = read.exists ? "OpenCode config has no mcp.agent-inspector entry" : "Missing file";
1367
+ return makeOpenCodeStatus(path, "missing", "unknown", flags.opencodeMcpUrl, detail);
1368
+ }
1369
+ const transport = detectOpenCodeTransport(entry);
1370
+ if (isObject(entry) && entry["enabled"] === false) {
1371
+ return makeOpenCodeStatus(
1372
+ path,
1373
+ "disabled",
1374
+ transport,
1375
+ flags.opencodeMcpUrl,
1376
+ "mcp.agent-inspector is present but disabled"
1377
+ );
1378
+ }
1379
+ if (isKnownOpenCodeEntry(entry, flags.opencodeMcpUrl, false)) {
1380
+ return makeOpenCodeStatus(
1381
+ path,
1382
+ "configured",
1383
+ transport,
1384
+ flags.opencodeMcpUrl,
1385
+ "mcp.agent-inspector points at Agent Inspector MCP"
1386
+ );
1387
+ }
1388
+ return makeOpenCodeStatus(
1389
+ path,
1390
+ "custom",
1391
+ transport,
1392
+ flags.opencodeMcpUrl,
1393
+ "mcp.agent-inspector exists but does not match Agent Inspector defaults"
1394
+ );
1395
+ }
1396
+ function buildOpenCodePlan(flags) {
1397
+ const path = resolveOpenCodeConfigPath(flags);
1398
+ const read = readOpenCodeConfig(path);
1399
+ if (!read.ok) {
1400
+ return {
1401
+ status: makeOpenCodeStatus(path, "invalid", "unknown", flags.opencodeMcpUrl, read.message),
1402
+ body: null,
1403
+ shouldWrite: false,
1404
+ shouldRemove: false,
1405
+ blockedMessage: `OpenCode config is invalid: ${read.message}`
1406
+ };
1407
+ }
1408
+ const status = readOpenCodeStatus(flags);
1409
+ const entry = getOpenCodeEntry(read.config);
1410
+ const nextBody = formatOpenCodeConfig(
1411
+ buildOpenCodeConfig(read.config, flags.opencodeTransport, flags.opencodeMcpUrl)
1412
+ );
1413
+ if (flags.uninstall) {
1414
+ const shouldRemove = entry !== void 0 && isKnownOpenCodeEntry(entry, flags.opencodeMcpUrl, true);
1415
+ const removeBody = shouldRemove ? formatOpenCodeConfig(removeOpenCodeEntry(read.config)) : null;
1416
+ return {
1417
+ status,
1418
+ body: removeBody,
1419
+ shouldWrite: false,
1420
+ shouldRemove,
1421
+ blockedMessage: shouldRemove ? null : "OpenCode mcp.agent-inspector is missing or custom; preserved"
1422
+ };
1423
+ }
1424
+ switch (status.state) {
1425
+ case "configured":
1426
+ return {
1427
+ status,
1428
+ body: nextBody,
1429
+ shouldWrite: flags.force,
1430
+ shouldRemove: false,
1431
+ blockedMessage: null
1432
+ };
1433
+ case "missing":
1434
+ return {
1435
+ status,
1436
+ body: nextBody,
1437
+ shouldWrite: true,
1438
+ shouldRemove: false,
1439
+ blockedMessage: null
1440
+ };
1441
+ case "custom":
1442
+ case "disabled":
1443
+ return {
1444
+ status,
1445
+ body: nextBody,
1446
+ shouldWrite: flags.force,
1447
+ shouldRemove: false,
1448
+ blockedMessage: flags.force ? null : "OpenCode mcp.agent-inspector is custom or disabled; use --force to replace"
1449
+ };
1450
+ case "invalid":
1451
+ return {
1452
+ status,
1453
+ body: null,
1454
+ shouldWrite: false,
1455
+ shouldRemove: false,
1456
+ blockedMessage: "OpenCode config is invalid"
1457
+ };
1458
+ default:
1459
+ return {
1460
+ status,
1461
+ body: null,
1462
+ shouldWrite: false,
1463
+ shouldRemove: false,
1464
+ blockedMessage: "OpenCode config state is unknown"
1465
+ };
1466
+ }
1467
+ }
1055
1468
  function buildDetectedSummary() {
1056
1469
  const entries = detectAll();
1057
1470
  const present = entries.filter((e) => e.result.found);
@@ -1230,8 +1643,8 @@ function readGeneratedFileStatus(file, currentVersion) {
1230
1643
  }
1231
1644
  }
1232
1645
  function buildPlannedFiles(flags, targets, version) {
1233
- const installClaude = !flags.codexOnly;
1234
- const installCodex = !flags.skipCodexSkill;
1646
+ const installClaude = !flags.codexOnly && !flags.opencodeOnly;
1647
+ const installCodex = !flags.skipCodexSkill && !flags.opencodeOnly;
1235
1648
  const detectedSummary = installClaude ? buildDetectedSummary() : "";
1236
1649
  const claudeSkillBody = installClaude ? renderSkillOnboard({
1237
1650
  version,
@@ -1289,7 +1702,7 @@ function removeEmptyDirectory(path) {
1289
1702
  } catch {
1290
1703
  }
1291
1704
  }
1292
- function runUninstall(plannedFiles, currentVersion, dryRun) {
1705
+ function runUninstall(plannedFiles, currentVersion, dryRun, openCodePlan) {
1293
1706
  const enabledFiles = plannedFiles.filter((file) => file.enabled);
1294
1707
  const filesToRemove = enabledFiles.filter((file) => shouldUninstall(file, currentVersion));
1295
1708
  if (dryRun) {
@@ -1301,15 +1714,31 @@ function runUninstall(plannedFiles, currentVersion, dryRun) {
1301
1714
  process.stdout.write(`${file.label}: ${file.path} (${status})
1302
1715
  `);
1303
1716
  }
1717
+ if (openCodePlan !== null) {
1718
+ const status = openCodePlan.shouldRemove ? "matching" : "skipped";
1719
+ process.stdout.write(
1720
+ `${openCodePlan.status.label}: ${openCodePlan.status.path} (${status})
1721
+ `
1722
+ );
1723
+ if (openCodePlan.blockedMessage !== null) {
1724
+ process.stdout.write(` ${openCodePlan.blockedMessage}
1725
+ `);
1726
+ }
1727
+ }
1304
1728
  process.stdout.write(`
1305
1729
  No files were removed.
1306
1730
  `);
1307
1731
  return 0;
1308
1732
  }
1309
- if (filesToRemove.length === 0) {
1733
+ const removeOpenCode = openCodePlan !== null && openCodePlan.shouldRemove;
1734
+ if (filesToRemove.length === 0 && !removeOpenCode) {
1310
1735
  process.stdout.write(
1311
1736
  "agent-inspector onboard: no generated onboarding files match this package version\n"
1312
1737
  );
1738
+ if (openCodePlan !== null && openCodePlan.blockedMessage !== null) {
1739
+ process.stdout.write(`${openCodePlan.blockedMessage}
1740
+ `);
1741
+ }
1313
1742
  return 0;
1314
1743
  }
1315
1744
  try {
@@ -1317,6 +1746,12 @@ No files were removed.
1317
1746
  unlinkSync(file.path);
1318
1747
  removeEmptyDirectory(dirname(file.path));
1319
1748
  process.stdout.write(`Removed ${file.label}: ${file.path}
1749
+ `);
1750
+ }
1751
+ if (openCodePlan !== null && openCodePlan.shouldRemove && openCodePlan.body !== null) {
1752
+ mkdirSync(dirname(openCodePlan.status.path), { recursive: true });
1753
+ writeFileSync(openCodePlan.status.path, openCodePlan.body, "utf8");
1754
+ process.stdout.write(`Removed OpenCode MCP entry from: ${openCodePlan.status.path}
1320
1755
  `);
1321
1756
  }
1322
1757
  } catch (err) {
@@ -1330,13 +1765,20 @@ No files were removed.
1330
1765
  function formatStatusVersion(status) {
1331
1766
  return status.version ?? "-";
1332
1767
  }
1333
- function summarizeStatusAction(statuses) {
1768
+ function summarizeStatusAction(statuses, openCodeStatus) {
1334
1769
  const actionable = statuses.filter(
1335
1770
  (status) => status.state === "missing" || status.state === "outdated"
1336
1771
  );
1337
- if (actionable.length > 0) {
1338
- const actions = Array.from(new Set(actionable.map((status) => status.action)));
1339
- return `Run: ${actions.join(" && ")}`;
1772
+ const actions = actionable.map((status) => status.action);
1773
+ if (openCodeStatus !== null && openCodeStatus.state === "missing") {
1774
+ actions.push(openCodeStatus.action);
1775
+ }
1776
+ if (actions.length > 0) {
1777
+ const uniqueActions = Array.from(new Set(actions));
1778
+ return `Run: ${uniqueActions.join(" && ")}`;
1779
+ }
1780
+ if (openCodeStatus !== null && (openCodeStatus.state === "custom" || openCodeStatus.state === "disabled" || openCodeStatus.state === "invalid")) {
1781
+ return `OpenCode config needs review: ${openCodeStatus.action}`;
1340
1782
  }
1341
1783
  if (statuses.some((status) => status.state === "custom")) {
1342
1784
  return "Custom files are preserved. Use --force only if you want to replace them.";
@@ -1346,16 +1788,17 @@ function summarizeStatusAction(statuses) {
1346
1788
  }
1347
1789
  return "All selected onboarding files are current.";
1348
1790
  }
1349
- function runStatus(plannedFiles, currentVersion, json) {
1791
+ function runStatus(plannedFiles, currentVersion, json, openCodeStatus) {
1350
1792
  const enabledFiles = plannedFiles.filter((file) => file.enabled);
1351
1793
  const statuses = enabledFiles.map((file) => readGeneratedFileStatus(file, currentVersion));
1352
- const summary = summarizeStatusAction(statuses);
1794
+ const summary = summarizeStatusAction(statuses, openCodeStatus);
1353
1795
  if (json) {
1354
1796
  process.stdout.write(
1355
1797
  `${JSON.stringify(
1356
1798
  {
1357
1799
  packageVersion: currentVersion,
1358
1800
  files: statuses,
1801
+ opencodeConfig: openCodeStatus,
1359
1802
  suggestedAction: summary
1360
1803
  },
1361
1804
  null,
@@ -1377,6 +1820,19 @@ function runStatus(plannedFiles, currentVersion, json) {
1377
1820
  process.stdout.write(` ${status.path}
1378
1821
  `);
1379
1822
  process.stdout.write(` Action: ${status.action}
1823
+ `);
1824
+ }
1825
+ if (openCodeStatus !== null) {
1826
+ process.stdout.write(`${openCodeStatus.label}: ${openCodeStatus.state}`);
1827
+ process.stdout.write(`, transport ${openCodeStatus.transport}
1828
+ `);
1829
+ process.stdout.write(` ${openCodeStatus.path}
1830
+ `);
1831
+ process.stdout.write(` Endpoint: ${openCodeStatus.endpoint}
1832
+ `);
1833
+ process.stdout.write(` Detail: ${openCodeStatus.detail}
1834
+ `);
1835
+ process.stdout.write(` Action: ${openCodeStatus.action}
1380
1836
  `);
1381
1837
  }
1382
1838
  process.stdout.write(`
@@ -1401,19 +1857,21 @@ function runOnboardSync(argv) {
1401
1857
  const version = readPackageVersion();
1402
1858
  const plannedFiles = buildPlannedFiles(flags, targets, version);
1403
1859
  const enabledFiles = plannedFiles.filter((file) => file.enabled);
1860
+ const openCodePlan = isOpenCodeEnabled(flags) ? buildOpenCodePlan(flags) : null;
1861
+ const openCodeStatus = openCodePlan === null ? null : openCodePlan.status;
1404
1862
  if (flags.json && !flags.status) {
1405
1863
  process.stderr.write("agent-inspector onboard: --json is only supported with --status\n");
1406
1864
  return 2;
1407
1865
  }
1408
- if (enabledFiles.length === 0) {
1866
+ if (enabledFiles.length === 0 && openCodePlan === null) {
1409
1867
  process.stderr.write("agent-inspector onboard: no onboarding target selected\n");
1410
1868
  return 2;
1411
1869
  }
1412
1870
  if (flags.status) {
1413
- return runStatus(plannedFiles, version, flags.json);
1871
+ return runStatus(plannedFiles, version, flags.json, openCodeStatus);
1414
1872
  }
1415
1873
  if (flags.uninstall) {
1416
- return runUninstall(plannedFiles, version, flags.dryRun);
1874
+ return runUninstall(plannedFiles, version, flags.dryRun, openCodePlan);
1417
1875
  }
1418
1876
  if (flags.dryRun) {
1419
1877
  process.stdout.write(`agent-inspector onboard --dry-run
@@ -1424,31 +1882,47 @@ function runOnboardSync(argv) {
1424
1882
  process.stdout.write(`${file.label}: ${file.path} (${status})
1425
1883
  `);
1426
1884
  }
1885
+ if (openCodePlan !== null) {
1886
+ process.stdout.write(
1887
+ `${openCodePlan.status.label}: ${openCodePlan.status.path} (${openCodePlan.status.state})
1888
+ `
1889
+ );
1890
+ process.stdout.write(` Transport: ${flags.opencodeTransport}
1891
+ `);
1892
+ process.stdout.write(` Endpoint: ${openCodePlan.status.endpoint}
1893
+ `);
1894
+ process.stdout.write(` Action: ${openCodePlan.status.action}
1895
+ `);
1896
+ if (openCodePlan.blockedMessage !== null) {
1897
+ process.stdout.write(` ${openCodePlan.blockedMessage}
1898
+ `);
1899
+ }
1900
+ }
1427
1901
  process.stdout.write(`
1428
1902
  Claude skill headings:
1429
1903
  `);
1430
- if (!flags.codexOnly) {
1904
+ if (!flags.codexOnly && !flags.opencodeOnly) {
1431
1905
  for (const heading of REQUIRED_PHASE_HEADINGS) {
1432
1906
  process.stdout.write(` - ${heading}
1433
1907
  `);
1434
1908
  }
1435
1909
  } else {
1436
- process.stdout.write(` (disabled by --codex-only)
1910
+ process.stdout.write(` (disabled by selected targets)
1437
1911
  `);
1438
1912
  }
1439
1913
  process.stdout.write(`
1440
1914
  Codex skill headings:
1441
1915
  `);
1442
- if (!flags.skipCodexSkill) {
1916
+ if (!flags.skipCodexSkill && !flags.opencodeOnly) {
1443
1917
  for (const heading of REQUIRED_CODEX_PHASE_HEADINGS) {
1444
1918
  process.stdout.write(` - ${heading}
1445
1919
  `);
1446
1920
  }
1447
1921
  } else {
1448
- process.stdout.write(` (disabled by --skip-codex-skill)
1922
+ process.stdout.write(` (disabled by selected targets)
1449
1923
  `);
1450
1924
  }
1451
- if (!flags.codexOnly) {
1925
+ if (!flags.codexOnly && !flags.opencodeOnly) {
1452
1926
  process.stdout.write(`
1453
1927
  Detected tools:
1454
1928
  ${buildDetectedSummary()}
@@ -1460,10 +1934,20 @@ No files were written.
1460
1934
  return 0;
1461
1935
  }
1462
1936
  const filesToWrite = plannedFiles.filter((file) => shouldWrite(file, flags.force, version));
1463
- if (filesToWrite.length === 0) {
1937
+ const shouldWriteOpenCode = openCodePlan !== null && openCodePlan.shouldWrite && openCodePlan.body !== null;
1938
+ if (openCodePlan !== null && openCodePlan.status.state === "invalid") {
1939
+ process.stderr.write(`agent-inspector onboard: ${openCodePlan.blockedMessage ?? "invalid"}
1940
+ `);
1941
+ return 1;
1942
+ }
1943
+ if (filesToWrite.length === 0 && !shouldWriteOpenCode) {
1464
1944
  process.stdout.write(
1465
1945
  "agent-inspector onboard: all selected onboarding files are already up to date\n"
1466
1946
  );
1947
+ if (openCodePlan !== null && openCodePlan.blockedMessage !== null) {
1948
+ process.stdout.write(`${openCodePlan.blockedMessage}
1949
+ `);
1950
+ }
1467
1951
  process.stdout.write("Re-run with --force to refresh.\n");
1468
1952
  return 0;
1469
1953
  }
@@ -1472,6 +1956,10 @@ No files were written.
1472
1956
  mkdirSync(dirname(file.path), { recursive: true });
1473
1957
  writeFileSync(file.path, file.body, "utf8");
1474
1958
  }
1959
+ if (shouldWriteOpenCode && openCodePlan !== null && openCodePlan.body !== null) {
1960
+ mkdirSync(dirname(openCodePlan.status.path), { recursive: true });
1961
+ writeFileSync(openCodePlan.status.path, openCodePlan.body, "utf8");
1962
+ }
1475
1963
  } catch (err) {
1476
1964
  const msg = err instanceof Error ? err.message : String(err);
1477
1965
  process.stderr.write(`agent-inspector onboard: failed to write files: ${msg}
@@ -1482,26 +1970,41 @@ No files were written.
1482
1970
  const toolHint = firstTool !== null ? firstTool.displayName : "no known tool detected";
1483
1971
  for (const file of filesToWrite) {
1484
1972
  process.stdout.write(`Installed/updated ${file.label} to: ${file.path}
1973
+ `);
1974
+ }
1975
+ if (shouldWriteOpenCode && openCodePlan !== null) {
1976
+ process.stdout.write(`Installed/updated OpenCode MCP config: ${openCodePlan.status.path}
1485
1977
  `);
1486
1978
  }
1487
1979
  process.stdout.write(`
1488
1980
  Next steps:
1489
1981
  `);
1490
- if (!flags.codexOnly) {
1982
+ if (!flags.codexOnly && !flags.opencodeOnly) {
1491
1983
  process.stdout.write(` - Open Claude Code and run: /agent-inspector:onboard
1492
1984
  `);
1493
1985
  }
1494
- if (!flags.skipCodexSkill) {
1986
+ if (!flags.skipCodexSkill && !flags.opencodeOnly) {
1495
1987
  process.stdout.write(` - Open Codex and ask to use the agent-inspector-onboard skill
1496
1988
  `);
1497
1989
  }
1498
- process.stdout.write(` - Refresh later: agent-inspector onboard --force
1990
+ if (openCodePlan !== null) {
1991
+ process.stdout.write(` - Verify OpenCode MCP: opencode mcp list
1992
+ `);
1993
+ }
1994
+ if (flags.opencodeOnly) {
1995
+ process.stdout.write(
1996
+ ` - Refresh OpenCode later: agent-inspector onboard --opencode-only --force
1997
+ `
1998
+ );
1999
+ } else {
2000
+ process.stdout.write(` - Refresh later: agent-inspector onboard --force
1499
2001
  `);
1500
- process.stdout.write(` - Detected primary tool: ${toolHint}
2002
+ process.stdout.write(` - Detected primary tool: ${toolHint}
1501
2003
  `);
2004
+ }
1502
2005
  return 0;
1503
2006
  }
1504
- var __filename, __dirname, DEFAULT_PORT, SKILL_DIR_NAME, SKILL_FILE_NAME, COMMAND_FILE_NAME, GENERATED_VERSION_PATTERN, FRONTMATTER_PATTERN, SEMVER_PATTERN;
2007
+ var __filename, __dirname, DEFAULT_PORT, DEFAULT_MCP_URL, SKILL_DIR_NAME, SKILL_FILE_NAME, COMMAND_FILE_NAME, GENERATED_VERSION_PATTERN, FRONTMATTER_PATTERN, SEMVER_PATTERN;
1505
2008
  var init_onboard = __esm({
1506
2009
  "src/cli/onboard.ts"() {
1507
2010
  "use strict";
@@ -1512,6 +2015,7 @@ var init_onboard = __esm({
1512
2015
  __filename = fileURLToPath(import.meta.url);
1513
2016
  __dirname = dirname(__filename);
1514
2017
  DEFAULT_PORT = 25947;
2018
+ DEFAULT_MCP_URL = `http://localhost:${DEFAULT_PORT}/api/mcp`;
1515
2019
  SKILL_DIR_NAME = "agent-inspector-onboard";
1516
2020
  SKILL_FILE_NAME = "SKILL.md";
1517
2021
  COMMAND_FILE_NAME = process.platform === "win32" ? "agent-inspector-onboard.md" : "agent-inspector:onboard.md";