acp-devtools 0.4.0 → 0.6.0

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/dist/index.js CHANGED
@@ -3755,21 +3755,21 @@ var require_fast_uri = __commonJS({
3755
3755
  normalizeString(uri, options);
3756
3756
  } else if (typeof uri === "object") {
3757
3757
  uri = /** @type {T} */
3758
- parse(serialize(uri, options), options);
3758
+ parse2(serialize(uri, options), options);
3759
3759
  }
3760
3760
  return uri;
3761
3761
  }
3762
3762
  function resolve2(baseURI, relativeURI, options) {
3763
3763
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3764
- const resolved = resolveComponent(parse(baseURI, schemelessOptions), parse(relativeURI, schemelessOptions), schemelessOptions, true);
3764
+ const resolved = resolveComponent(parse2(baseURI, schemelessOptions), parse2(relativeURI, schemelessOptions), schemelessOptions, true);
3765
3765
  schemelessOptions.skipEscape = true;
3766
3766
  return serialize(resolved, schemelessOptions);
3767
3767
  }
3768
3768
  function resolveComponent(base, relative, options, skipNormalization) {
3769
3769
  const target = {};
3770
3770
  if (!skipNormalization) {
3771
- base = parse(serialize(base, options), options);
3772
- relative = parse(serialize(relative, options), options);
3771
+ base = parse2(serialize(base, options), options);
3772
+ relative = parse2(serialize(relative, options), options);
3773
3773
  }
3774
3774
  options = options || {};
3775
3775
  if (!options.tolerant && relative.scheme) {
@@ -3992,7 +3992,7 @@ var require_fast_uri = __commonJS({
3992
3992
  }
3993
3993
  return { parsed, malformedAuthorityOrPort };
3994
3994
  }
3995
- function parse(uri, opts) {
3995
+ function parse2(uri, opts) {
3996
3996
  return parseWithStatus(uri, opts).parsed;
3997
3997
  }
3998
3998
  function normalizeString(uri, opts) {
@@ -4021,7 +4021,7 @@ var require_fast_uri = __commonJS({
4021
4021
  resolveComponent,
4022
4022
  equal,
4023
4023
  serialize,
4024
- parse
4024
+ parse: parse2
4025
4025
  };
4026
4026
  module.exports = fastUri;
4027
4027
  module.exports.default = fastUri;
@@ -7278,8 +7278,12 @@ function createAcpValidator(schema3) {
7278
7278
  const ajv = new Ajv2020({ strict: false, allErrors: true, validateFormats: false });
7279
7279
  ajv.addSchema(schema3, SCHEMA_ID);
7280
7280
  const methodIndex = /* @__PURE__ */ new Map();
7281
+ const softRequired = /* @__PURE__ */ new Map();
7281
7282
  const defs = schema3.$defs ?? {};
7282
7283
  for (const [name, def] of Object.entries(defs)) {
7284
+ const soon = Object.entries(def.properties ?? {}).filter(([, prop]) => SOON_REQUIRED_RE.test(prop.description ?? "")).map(([field]) => field);
7285
+ if (soon.length > 0)
7286
+ softRequired.set(name, soon);
7283
7287
  const method = def["x-method"];
7284
7288
  if (typeof method !== "string")
7285
7289
  continue;
@@ -7332,22 +7336,41 @@ function createAcpValidator(schema3) {
7332
7336
  }
7333
7337
  if (!defName)
7334
7338
  return { valid: true, errors: [], skipped: "no-schema-for-kind" };
7339
+ const advisories = collectAdvisories(defName, target);
7335
7340
  const validator2 = getValidator(defName);
7336
7341
  const ok = validator2(target);
7337
- if (ok)
7338
- return { valid: true, errors: [], schemaName: defName };
7339
- return {
7342
+ const base = ok ? { valid: true, errors: [], schemaName: defName } : {
7340
7343
  valid: false,
7341
7344
  errors: (validator2.errors ?? []).map(toError),
7342
7345
  schemaName: defName
7343
7346
  };
7347
+ if (advisories.length > 0)
7348
+ base.advisories = advisories;
7349
+ return base;
7350
+ }
7351
+ function collectAdvisories(defName, target) {
7352
+ const fields = softRequired.get(defName);
7353
+ if (!fields || typeof target !== "object" || target === null)
7354
+ return [];
7355
+ const obj = target;
7356
+ const out = [];
7357
+ for (const field of fields) {
7358
+ if (obj[field] === void 0 || obj[field] === null) {
7359
+ out.push({
7360
+ path: field,
7361
+ message: `missing ${field} \u2014 will be required in a future ACP version`,
7362
+ kind: "soon-required"
7363
+ });
7364
+ }
7365
+ }
7366
+ return out;
7344
7367
  }
7345
7368
  function knownAcpMethods2() {
7346
7369
  return [...methodIndex.keys()].sort();
7347
7370
  }
7348
7371
  return { validateAcpMessage: validateAcpMessage2, knownAcpMethods: knownAcpMethods2 };
7349
7372
  }
7350
- var import__, ajvModule, Ajv2020, SCHEMA_ID;
7373
+ var import__, ajvModule, Ajv2020, SCHEMA_ID, SOON_REQUIRED_RE;
7351
7374
  var init_validate_factory = __esm({
7352
7375
  "../core/dist/acp/validate-factory.js"() {
7353
7376
  "use strict";
@@ -7355,6 +7378,7 @@ var init_validate_factory = __esm({
7355
7378
  ajvModule = import__.default;
7356
7379
  Ajv2020 = ajvModule.default ?? ajvModule;
7357
7380
  SCHEMA_ID = "acp";
7381
+ SOON_REQUIRED_RE = /will be required/i;
7358
7382
  }
7359
7383
  });
7360
7384
 
@@ -8430,6 +8454,7 @@ function detectAgentShortcut(agentCommand) {
8430
8454
  const trimmed = agentCommand.trim();
8431
8455
  if (trimmed.length === 0)
8432
8456
  return null;
8457
+ const candidates = [trimmed, basenameFirstToken(trimmed)];
8433
8458
  for (const def of Object.values(AGENT_REGISTRY)) {
8434
8459
  const variants = [
8435
8460
  [def.command, ...def.args],
@@ -8437,14 +8462,23 @@ function detectAgentShortcut(agentCommand) {
8437
8462
  ];
8438
8463
  for (const variant of variants) {
8439
8464
  const exact = variant.join(" ");
8440
- if (trimmed === exact)
8441
- return def;
8442
- if (trimmed.startsWith(exact + " "))
8443
- return def;
8465
+ for (const candidate of candidates) {
8466
+ if (candidate === exact)
8467
+ return def;
8468
+ if (candidate.startsWith(exact + " "))
8469
+ return def;
8470
+ }
8444
8471
  }
8445
8472
  }
8446
8473
  return null;
8447
8474
  }
8475
+ function basenameFirstToken(command) {
8476
+ const spaceAt = command.indexOf(" ");
8477
+ const head = spaceAt === -1 ? command : command.slice(0, spaceAt);
8478
+ const rest = spaceAt === -1 ? "" : command.slice(spaceAt);
8479
+ const base = head.split(/[/\\]/).pop() ?? head;
8480
+ return base + rest;
8481
+ }
8448
8482
  var AGENT_REGISTRY, DEFAULT_AGENT;
8449
8483
  var init_registry = __esm({
8450
8484
  "../core/dist/agents/registry.js"() {
@@ -8484,6 +8518,14 @@ var init_registry = __esm({
8484
8518
  command: "opencode",
8485
8519
  args: ["acp"],
8486
8520
  requiresExternalInstall: true
8521
+ },
8522
+ junie: {
8523
+ shortName: "junie",
8524
+ displayName: "Junie",
8525
+ description: "JetBrains Junie CLI \u2014 install separately, then this runs `junie --acp=true`",
8526
+ command: "junie",
8527
+ args: ["--acp=true"],
8528
+ requiresExternalInstall: true
8487
8529
  }
8488
8530
  };
8489
8531
  DEFAULT_AGENT = "claude-code";
@@ -10039,6 +10081,142 @@ var init_dist = __esm({
10039
10081
  }
10040
10082
  });
10041
10083
 
10084
+ // src/lib/style.ts
10085
+ function colorEnabled(stream = process.stdout, env = process.env) {
10086
+ if (env.FORCE_COLOR === "0" || env.FORCE_COLOR === "false") return false;
10087
+ if (env.FORCE_COLOR !== void 0) return true;
10088
+ if (env.NO_COLOR !== void 0) return false;
10089
+ return Boolean(stream.isTTY);
10090
+ }
10091
+ function paint(enabled, code, s) {
10092
+ if (!enabled) return s;
10093
+ return `${ESC}[${CODES[code]}m${s}${ESC}[${CODES.reset}m`;
10094
+ }
10095
+ function createStyler(enabled = colorEnabled()) {
10096
+ const wrap = (code) => (s) => paint(enabled, code, s);
10097
+ return {
10098
+ enabled,
10099
+ bold: wrap("bold"),
10100
+ dim: wrap("dim"),
10101
+ italic: wrap("italic"),
10102
+ underline: wrap("underline"),
10103
+ red: wrap("red"),
10104
+ green: wrap("green"),
10105
+ yellow: wrap("yellow"),
10106
+ blue: wrap("blue"),
10107
+ magenta: wrap("magenta"),
10108
+ cyan: wrap("cyan"),
10109
+ gray: wrap("gray")
10110
+ };
10111
+ }
10112
+ function stripAnsi(s) {
10113
+ return s.replace(ANSI_PATTERN, "");
10114
+ }
10115
+ function visibleWidth(s) {
10116
+ return stripAnsi(s).length;
10117
+ }
10118
+ var ESC, CODES, ANSI_PATTERN;
10119
+ var init_style = __esm({
10120
+ "src/lib/style.ts"() {
10121
+ "use strict";
10122
+ ESC = String.fromCharCode(27);
10123
+ CODES = {
10124
+ reset: 0,
10125
+ bold: 1,
10126
+ dim: 2,
10127
+ italic: 3,
10128
+ underline: 4,
10129
+ red: 31,
10130
+ green: 32,
10131
+ yellow: 33,
10132
+ blue: 34,
10133
+ magenta: 35,
10134
+ cyan: 36,
10135
+ gray: 90
10136
+ };
10137
+ ANSI_PATTERN = new RegExp(`${ESC}\\[[0-9;]*m`, "g");
10138
+ }
10139
+ });
10140
+
10141
+ // src/lib/table.ts
10142
+ function renderTable(s, columns, rows, opts = {}) {
10143
+ const indent = opts.indent ?? "";
10144
+ const gap = opts.gap ?? " ";
10145
+ const widths = columns.map(
10146
+ (col, c) => Math.max(visibleWidth(col.title), ...rows.map((r) => visibleWidth(r[c] ?? "")))
10147
+ );
10148
+ const pad2 = (text, width, align) => {
10149
+ const fill = " ".repeat(Math.max(0, width - visibleWidth(text)));
10150
+ return align === "right" ? fill + text : text + fill;
10151
+ };
10152
+ const lineFor = (cells, color) => {
10153
+ const rendered = columns.map((col, c) => {
10154
+ const align = col.align ?? "left";
10155
+ const text = cells[c] ?? "";
10156
+ const isLast = c === columns.length - 1;
10157
+ const cell = isLast && align === "left" ? text : pad2(text, widths[c], align);
10158
+ return color(cell);
10159
+ });
10160
+ return indent + rendered.join(gap);
10161
+ };
10162
+ const out = [];
10163
+ out.push(
10164
+ lineFor(
10165
+ columns.map((col) => col.title),
10166
+ (t) => s.dim(t)
10167
+ )
10168
+ );
10169
+ for (const row2 of rows) {
10170
+ out.push(lineFor(row2, (t) => t));
10171
+ }
10172
+ return out.map((l) => l + "\n").join("");
10173
+ }
10174
+ var init_table = __esm({
10175
+ "src/lib/table.ts"() {
10176
+ "use strict";
10177
+ init_style();
10178
+ }
10179
+ });
10180
+
10181
+ // src/commands/agents.ts
10182
+ function registerAgentsCommand(program) {
10183
+ program.command("agents").description("List the built-in agent shortcuts (use with proxy / setup / --agent)").option("--json", "emit machine-readable JSON instead of a table").action((opts) => {
10184
+ const agents = listAgents();
10185
+ if (opts.json) {
10186
+ process.stdout.write(JSON.stringify(agents, null, 2) + "\n");
10187
+ return;
10188
+ }
10189
+ const s = createStyler(colorEnabled(process.stdout));
10190
+ const rows = agents.map((a) => [
10191
+ s.green(a.shortName),
10192
+ a.displayName,
10193
+ [a.command, ...a.args].join(" "),
10194
+ a.requiresExternalInstall ? "install separately" : "auto via npx"
10195
+ ]);
10196
+ process.stdout.write(renderTable(s, COLUMNS, rows));
10197
+ process.stdout.write(
10198
+ s.dim(
10199
+ "\nUse a shortcut: acp-devtools <name> \xB7 proxy --agent <name> \xB7 setup <name> --editor <zed|jetbrains>\nAny other ACP agent: acp-devtools setup --editor <ed> -- <command\u2026>\n"
10200
+ )
10201
+ );
10202
+ });
10203
+ }
10204
+ var COLUMNS;
10205
+ var init_agents2 = __esm({
10206
+ "src/commands/agents.ts"() {
10207
+ "use strict";
10208
+ init_dist();
10209
+ init_style();
10210
+ init_table();
10211
+ COLUMNS = [
10212
+ { title: "SHORTCUT" },
10213
+ { title: "NAME" },
10214
+ { title: "RUNS" },
10215
+ { title: "INSTALL" }
10216
+ ];
10217
+ }
10218
+ });
10219
+
10042
10220
  // src/commands/backfill-metadata.ts
10043
10221
  function backfillSession(db, id) {
10044
10222
  let session;
@@ -10187,63 +10365,6 @@ var init_delete = __esm({
10187
10365
  }
10188
10366
  });
10189
10367
 
10190
- // src/lib/style.ts
10191
- function colorEnabled(stream = process.stdout, env = process.env) {
10192
- if (env.FORCE_COLOR === "0" || env.FORCE_COLOR === "false") return false;
10193
- if (env.FORCE_COLOR !== void 0) return true;
10194
- if (env.NO_COLOR !== void 0) return false;
10195
- return Boolean(stream.isTTY);
10196
- }
10197
- function paint(enabled, code, s) {
10198
- if (!enabled) return s;
10199
- return `${ESC}[${CODES[code]}m${s}${ESC}[${CODES.reset}m`;
10200
- }
10201
- function createStyler(enabled = colorEnabled()) {
10202
- const wrap = (code) => (s) => paint(enabled, code, s);
10203
- return {
10204
- enabled,
10205
- bold: wrap("bold"),
10206
- dim: wrap("dim"),
10207
- italic: wrap("italic"),
10208
- underline: wrap("underline"),
10209
- red: wrap("red"),
10210
- green: wrap("green"),
10211
- yellow: wrap("yellow"),
10212
- blue: wrap("blue"),
10213
- magenta: wrap("magenta"),
10214
- cyan: wrap("cyan"),
10215
- gray: wrap("gray")
10216
- };
10217
- }
10218
- function stripAnsi(s) {
10219
- return s.replace(ANSI_PATTERN, "");
10220
- }
10221
- function visibleWidth(s) {
10222
- return stripAnsi(s).length;
10223
- }
10224
- var ESC, CODES, ANSI_PATTERN;
10225
- var init_style = __esm({
10226
- "src/lib/style.ts"() {
10227
- "use strict";
10228
- ESC = String.fromCharCode(27);
10229
- CODES = {
10230
- reset: 0,
10231
- bold: 1,
10232
- dim: 2,
10233
- italic: 3,
10234
- underline: 4,
10235
- red: 31,
10236
- green: 32,
10237
- yellow: 33,
10238
- blue: 34,
10239
- magenta: 35,
10240
- cyan: 36,
10241
- gray: 90
10242
- };
10243
- ANSI_PATTERN = new RegExp(`${ESC}\\[[0-9;]*m`, "g");
10244
- }
10245
- });
10246
-
10247
10368
  // src/lib/palette.ts
10248
10369
  function colorKind(s, kind, text) {
10249
10370
  switch (kind) {
@@ -10277,46 +10398,6 @@ var init_palette = __esm({
10277
10398
  }
10278
10399
  });
10279
10400
 
10280
- // src/lib/table.ts
10281
- function renderTable(s, columns, rows, opts = {}) {
10282
- const indent = opts.indent ?? "";
10283
- const gap = opts.gap ?? " ";
10284
- const widths = columns.map(
10285
- (col, c) => Math.max(visibleWidth(col.title), ...rows.map((r) => visibleWidth(r[c] ?? "")))
10286
- );
10287
- const pad2 = (text, width, align) => {
10288
- const fill = " ".repeat(Math.max(0, width - visibleWidth(text)));
10289
- return align === "right" ? fill + text : text + fill;
10290
- };
10291
- const lineFor = (cells, color) => {
10292
- const rendered = columns.map((col, c) => {
10293
- const align = col.align ?? "left";
10294
- const text = cells[c] ?? "";
10295
- const isLast = c === columns.length - 1;
10296
- const cell = isLast && align === "left" ? text : pad2(text, widths[c], align);
10297
- return color(cell);
10298
- });
10299
- return indent + rendered.join(gap);
10300
- };
10301
- const out = [];
10302
- out.push(
10303
- lineFor(
10304
- columns.map((col) => col.title),
10305
- (t) => s.dim(t)
10306
- )
10307
- );
10308
- for (const row2 of rows) {
10309
- out.push(lineFor(row2, (t) => t));
10310
- }
10311
- return out.map((l) => l + "\n").join("");
10312
- }
10313
- var init_table = __esm({
10314
- "src/lib/table.ts"() {
10315
- "use strict";
10316
- init_style();
10317
- }
10318
- });
10319
-
10320
10401
  // src/commands/diff.ts
10321
10402
  function registerDiffCommand(program) {
10322
10403
  program.command("diff").description(
@@ -10600,10 +10681,130 @@ var init_diff = __esm({
10600
10681
  }
10601
10682
  });
10602
10683
 
10684
+ // src/lib/setup-config.ts
10685
+ import { homedir as homedir3, platform } from "os";
10686
+ import { join as join3 } from "path";
10687
+ import { applyEdits, modify, parse } from "jsonc-parser";
10688
+ function isEditor(value) {
10689
+ return EDITORS.includes(value);
10690
+ }
10691
+ function zedSettingsPath() {
10692
+ if (platform() === "win32") {
10693
+ return join3(process.env.APPDATA ?? "", "Zed", "settings.json");
10694
+ }
10695
+ if (platform() === "darwin") {
10696
+ return join3(homedir3(), ".config", "zed", "settings.json");
10697
+ }
10698
+ return join3(process.env.XDG_CONFIG_HOME ?? join3(homedir3(), ".config"), "zed", "settings.json");
10699
+ }
10700
+ function jetbrainsConfigPath() {
10701
+ return join3(homedir3(), ".jetbrains", "acp.json");
10702
+ }
10703
+ function editorInfo(editor) {
10704
+ if (editor === "zed") {
10705
+ return {
10706
+ id: "zed",
10707
+ label: "Zed",
10708
+ configPath: zedSettingsPath(),
10709
+ placement: "add the agent_servers block to Zed\u2019s settings.json (create the file or the key if missing)",
10710
+ restart: "reopen the agent panel so Zed re-reads the config",
10711
+ dedicatedFile: false
10712
+ };
10713
+ }
10714
+ return {
10715
+ id: "jetbrains",
10716
+ label: "JetBrains",
10717
+ configPath: jetbrainsConfigPath(),
10718
+ placement: "write this to the global JetBrains ACP config (create it if missing \u2014 every JetBrains IDE shares this one file)",
10719
+ restart: "restart the AI chat tool window (close and reopen it)",
10720
+ dedicatedFile: true
10721
+ };
10722
+ }
10723
+ function deriveCustomName(target) {
10724
+ const first = target[0] ?? "agent";
10725
+ if (first === "npx") {
10726
+ for (let i = 1; i < target.length; i++) {
10727
+ const token = target[i];
10728
+ if (!token.startsWith("-")) return token;
10729
+ }
10730
+ }
10731
+ return first.split(/[/\\]/).pop() || first;
10732
+ }
10733
+ function buildSetupPlan(input) {
10734
+ const target = input.target.length > 0 ? [...input.target] : [DEFAULT_AGENT];
10735
+ const single = target.length === 1 ? target[0] : null;
10736
+ let args;
10737
+ let displayName;
10738
+ let isCustom;
10739
+ let agentDescription;
10740
+ let requiresExternalInstall = false;
10741
+ if (single !== null && isAgentShortcut(single)) {
10742
+ const def = resolveAgent(single);
10743
+ args = [def.shortName];
10744
+ displayName = input.name ?? `${def.displayName} (via ACP Devtools)`;
10745
+ isCustom = false;
10746
+ agentDescription = def.description;
10747
+ requiresExternalInstall = def.requiresExternalInstall ?? false;
10748
+ } else {
10749
+ args = ["proxy", ...target];
10750
+ displayName = input.name ?? `${deriveCustomName(target)} (via ACP Devtools)`;
10751
+ isCustom = true;
10752
+ }
10753
+ return {
10754
+ editor: editorInfo(input.editor),
10755
+ displayName,
10756
+ entry: { type: "custom", command: input.selfPath, args },
10757
+ isCustom,
10758
+ agentDescription,
10759
+ requiresExternalInstall
10760
+ };
10761
+ }
10762
+ function renderConfigBlock(plan) {
10763
+ const entries = { [plan.displayName]: plan.entry };
10764
+ if (plan.editor.dedicatedFile) {
10765
+ return JSON.stringify({ agent_servers: entries }, null, 4);
10766
+ }
10767
+ return `"agent_servers": ${JSON.stringify(entries, null, 4)}`;
10768
+ }
10769
+ function detectFormatting(text) {
10770
+ const match = text.match(/\n([ \t]+)\S/);
10771
+ if (!match) return { insertSpaces: true, tabSize: 4 };
10772
+ const ws = match[1];
10773
+ if (ws.startsWith(" ")) return { insertSpaces: false, tabSize: 1 };
10774
+ return { insertSpaces: true, tabSize: ws.length };
10775
+ }
10776
+ function mergeIntoConfig(existing, name, entry) {
10777
+ const base = existing !== null && existing.trim().length > 0 ? existing : "{}";
10778
+ const errors = [];
10779
+ const root = parse(base, errors, { allowTrailingComma: true });
10780
+ if (errors.length > 0) {
10781
+ throw new Error("config is not valid JSON");
10782
+ }
10783
+ if (typeof root !== "object" || root === null || Array.isArray(root)) {
10784
+ throw new Error("config root is not a JSON object");
10785
+ }
10786
+ const servers = root.agent_servers;
10787
+ const replaced = typeof servers === "object" && servers !== null && !Array.isArray(servers) ? Object.prototype.hasOwnProperty.call(servers, name) : false;
10788
+ const edits = modify(base, ["agent_servers", name], entry, {
10789
+ formattingOptions: detectFormatting(base)
10790
+ });
10791
+ let text = applyEdits(base, edits);
10792
+ if (!text.endsWith("\n")) text += "\n";
10793
+ return { text, replaced };
10794
+ }
10795
+ var EDITORS;
10796
+ var init_setup_config = __esm({
10797
+ "src/lib/setup-config.ts"() {
10798
+ "use strict";
10799
+ init_dist();
10800
+ EDITORS = ["zed", "jetbrains"];
10801
+ }
10802
+ });
10803
+
10603
10804
  // src/commands/doctor.ts
10604
10805
  import { existsSync as existsSync6, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
10605
- import { homedir as homedir3, platform, release } from "os";
10606
- import { join as join3 } from "path";
10806
+ import { homedir as homedir4, platform as platform2, release } from "os";
10807
+ import { join as join4 } from "path";
10607
10808
  function colorStatus(s, status, text) {
10608
10809
  switch (status) {
10609
10810
  case "ok":
@@ -10626,7 +10827,7 @@ function checkEnvironment() {
10626
10827
  });
10627
10828
  results.push({
10628
10829
  status: "info",
10629
- label: `Platform: ${platform()} ${process.arch} (${release()})`
10830
+ label: `Platform: ${platform2()} ${process.arch} (${release()})`
10630
10831
  });
10631
10832
  const binary = process.argv[1] ?? "(unknown)";
10632
10833
  results.push({
@@ -10710,21 +10911,17 @@ function checkState() {
10710
10911
  return results;
10711
10912
  }
10712
10913
  function probeZed() {
10713
- const path = platform() === "win32" ? join3(process.env.APPDATA ?? "", "Zed", "settings.json") : platform() === "darwin" ? join3(homedir3(), ".config", "zed", "settings.json") : join3(
10714
- process.env.XDG_CONFIG_HOME ?? join3(homedir3(), ".config"),
10715
- "zed",
10716
- "settings.json"
10717
- );
10914
+ const path = zedSettingsPath();
10718
10915
  return { name: "Zed", path, exists: existsSync6(path) };
10719
10916
  }
10720
10917
  function probeJetBrains() {
10721
10918
  const baseDirs = [];
10722
- if (platform() === "darwin") {
10723
- baseDirs.push(join3(homedir3(), "Library", "Application Support", "JetBrains"));
10724
- } else if (platform() === "win32") {
10725
- baseDirs.push(join3(process.env.APPDATA ?? "", "JetBrains"));
10919
+ if (platform2() === "darwin") {
10920
+ baseDirs.push(join4(homedir4(), "Library", "Application Support", "JetBrains"));
10921
+ } else if (platform2() === "win32") {
10922
+ baseDirs.push(join4(process.env.APPDATA ?? "", "JetBrains"));
10726
10923
  } else {
10727
- baseDirs.push(join3(homedir3(), ".config", "JetBrains"));
10924
+ baseDirs.push(join4(homedir4(), ".config", "JetBrains"));
10728
10925
  }
10729
10926
  const probes = [];
10730
10927
  for (const base of baseDirs) {
@@ -10738,7 +10935,7 @@ function probeJetBrains() {
10738
10935
  for (const entry of entries) {
10739
10936
  if (entry.startsWith(".")) continue;
10740
10937
  if (!JETBRAINS_VERSIONED.test(entry) && !JETBRAINS_UNVERSIONED.has(entry)) continue;
10741
- const full = join3(base, entry);
10938
+ const full = join4(base, entry);
10742
10939
  try {
10743
10940
  if (!statSync2(full).isDirectory()) continue;
10744
10941
  } catch {
@@ -10837,6 +11034,7 @@ var init_doctor = __esm({
10837
11034
  "use strict";
10838
11035
  init_dist();
10839
11036
  init_style();
11037
+ init_setup_config();
10840
11038
  STATUS_MARK = {
10841
11039
  ok: "\u2713",
10842
11040
  warn: "!",
@@ -10853,7 +11051,7 @@ var CLI_VERSION;
10853
11051
  var init_version = __esm({
10854
11052
  "src/version.ts"() {
10855
11053
  "use strict";
10856
- CLI_VERSION = "0.4.0";
11054
+ CLI_VERSION = "0.6.0";
10857
11055
  }
10858
11056
  });
10859
11057
 
@@ -11322,9 +11520,9 @@ function formatTable(rows) {
11322
11520
  labelOf(r)
11323
11521
  ];
11324
11522
  });
11325
- return renderTable(s, COLUMNS, body);
11523
+ return renderTable(s, COLUMNS2, body);
11326
11524
  }
11327
- var COLUMNS;
11525
+ var COLUMNS2;
11328
11526
  var init_list = __esm({
11329
11527
  "src/commands/list.ts"() {
11330
11528
  "use strict";
@@ -11332,7 +11530,7 @@ var init_list = __esm({
11332
11530
  init_style();
11333
11531
  init_palette();
11334
11532
  init_table();
11335
- COLUMNS = [
11533
+ COLUMNS2 = [
11336
11534
  { title: "ID", align: "left" },
11337
11535
  { title: "AGE", align: "right" },
11338
11536
  { title: "KIND", align: "left" },
@@ -11589,7 +11787,9 @@ function buildMcpServer(opts) {
11589
11787
  description: "Fetch a single captured frame by (session_id, seq). Returns the full record: raw JSON bytes, parsed payload, parseError (if any), direction, kind, method, rpcId, timestamp. Use this after find_spec_violations / get_session_messages narrows you down to a specific seq.",
11590
11788
  inputSchema: {
11591
11789
  session_id: z.number().int().positive().describe("session id from list_sessions"),
11592
- seq: z.number().int().positive().describe("per-session sequence number from list_session_messages or find_spec_violations")
11790
+ seq: z.number().int().positive().describe(
11791
+ "per-session sequence number from list_session_messages or find_spec_violations"
11792
+ )
11593
11793
  },
11594
11794
  outputSchema: {
11595
11795
  session_id: z.number(),
@@ -11668,7 +11868,7 @@ function buildMcpServer(opts) {
11668
11868
  server.registerTool(
11669
11869
  "find_spec_violations",
11670
11870
  {
11671
- description: "Validate every captured frame in a session against the ACP JSON schema (Draft 2020-12, ajv-compiled). Returns every offender with its seq, method, and per-error path + keyword + message. Schema-skipped frames (unknown methods, parse errors, generic envelopes) are not counted. Use the returned seq with get_message to inspect the offending raw payload. Start here when an agent is misbehaving but you don't yet know how.",
11871
+ description: "Validate every captured frame in a session against the ACP JSON schema (Draft 2020-12, ajv-compiled). Returns every offender with its seq, method, and per-error path + keyword + message. Schema-skipped frames (unknown methods, parse errors, generic envelopes) are not counted. Also returns `advisories`: frames valid under today's schema that omit a field the spec says will become mandatory (e.g. `agentInfo` on an initialize response, `clientInfo` on the request) \u2014 these never count as violations. Use the returned seq with get_message to inspect the offending raw payload. Start here when an agent is misbehaving but you don't yet know how.",
11672
11872
  inputSchema: {
11673
11873
  session_id: z.number().int().positive().describe("session id from list_sessions")
11674
11874
  },
@@ -11683,13 +11883,23 @@ function buildMcpServer(opts) {
11683
11883
  z.object({
11684
11884
  seq: z.number(),
11685
11885
  method: z.string().nullable(),
11686
- errors: z.array(z.looseObject({
11687
- keyword: z.string(),
11688
- instancePath: z.string().optional(),
11689
- schemaPath: z.string().optional(),
11690
- message: z.string().optional()
11691
- }))
11886
+ errors: z.array(
11887
+ z.looseObject({
11888
+ keyword: z.string(),
11889
+ instancePath: z.string().optional(),
11890
+ schemaPath: z.string().optional(),
11891
+ message: z.string().optional()
11892
+ })
11893
+ )
11692
11894
  }).loose()
11895
+ ),
11896
+ advisories: z.array(
11897
+ z.object({
11898
+ seq: z.number(),
11899
+ method: z.string().nullable(),
11900
+ path: z.string(),
11901
+ message: z.string()
11902
+ })
11693
11903
  )
11694
11904
  },
11695
11905
  annotations: annotations({ title: "Validate session against ACP schema" })
@@ -11700,6 +11910,7 @@ function buildMcpServer(opts) {
11700
11910
  const seqToMethod = /* @__PURE__ */ new Map();
11701
11911
  for (const m of messages) if (m.method) seqToMethod.set(m.seq, m.method);
11702
11912
  const violations = [];
11913
+ const advisories = [];
11703
11914
  let checked = 0;
11704
11915
  for (const m of messages) {
11705
11916
  const pair = pairs.get(m.seq);
@@ -11709,15 +11920,15 @@ function buildMcpServer(opts) {
11709
11920
  const result = validateAcpMessage(m, validationOpts);
11710
11921
  if (result.skipped) continue;
11711
11922
  checked += 1;
11923
+ const method = m.method ?? pairedMethod ?? null;
11712
11924
  if (!result.valid) {
11713
- violations.push({
11714
- seq: m.seq,
11715
- method: m.method ?? pairedMethod ?? null,
11716
- errors: result.errors
11717
- });
11925
+ violations.push({ seq: m.seq, method, errors: result.errors });
11926
+ }
11927
+ for (const a of result.advisories ?? []) {
11928
+ advisories.push({ seq: m.seq, method, path: a.path, message: a.message });
11718
11929
  }
11719
11930
  }
11720
- return structuredResult({ session_id, checked, violations });
11931
+ return structuredResult({ session_id, checked, violations, advisories });
11721
11932
  }
11722
11933
  );
11723
11934
  server.registerTool(
@@ -11808,6 +12019,88 @@ function buildMcpServer(opts) {
11808
12019
  });
11809
12020
  }
11810
12021
  );
12022
+ server.registerTool(
12023
+ "list_agent_shortcuts",
12024
+ {
12025
+ description: "List the built-in agent shortcuts you can pass to generate_editor_config as `agent` (or that the CLI accepts as `acp-devtools <name>` / `proxy --agent <name>`). Each is a known ACP agent wired into the registry; anything else is still supported via generate_editor_config's `command` argument. Same data as the CLI `acp-devtools agents`.",
12026
+ inputSchema: {},
12027
+ outputSchema: {
12028
+ agents: z.array(
12029
+ z.object({
12030
+ shortName: z.string(),
12031
+ displayName: z.string(),
12032
+ command: z.string(),
12033
+ args: z.array(z.string()),
12034
+ requiresExternalInstall: z.boolean()
12035
+ })
12036
+ )
12037
+ },
12038
+ annotations: annotations({ title: "List agent shortcuts" })
12039
+ },
12040
+ async () => {
12041
+ const agents = listAgents().map((a) => ({
12042
+ shortName: a.shortName,
12043
+ displayName: a.displayName,
12044
+ command: a.command,
12045
+ args: [...a.args],
12046
+ requiresExternalInstall: a.requiresExternalInstall ?? false
12047
+ }));
12048
+ return structuredResult({ agents });
12049
+ }
12050
+ );
12051
+ server.registerTool(
12052
+ "generate_editor_config",
12053
+ {
12054
+ description: 'Generate the editor config (Zed or JetBrains) that runs an ACP agent through ACP Devtools, with the absolute acp-devtools binary path already filled in. Use this when the user asks to wire up / add an agent \u2014 e.g. "add junie for zed". Returns `block` (the JSON to add to the editor config), `configPath` (where it goes), and `writeCommand` (the equivalent `acp-devtools setup --write` to run instead). This tool only COMPUTES the config \u2014 it does not write any file; add `block` to `configPath` with your own file tools, or tell the user to run `writeCommand`. Pass `agent` for a registry shortcut (see list_agent_shortcuts) or `command` for any other agent (wrapped via `proxy`). Mirrors the CLI `acp-devtools setup`.',
12055
+ inputSchema: {
12056
+ editor: z.enum(["zed", "jetbrains"]).describe("target editor: zed or jetbrains"),
12057
+ agent: z.string().optional().describe(
12058
+ "registry shortcut: junie, goose, codex, opencode, or claude-code (the default when neither agent nor command is given)"
12059
+ ),
12060
+ command: z.array(z.string()).optional().describe(
12061
+ 'custom agent command tokens (e.g. ["npx","-y","@scope/acp"]), wrapped via proxy; ignored when `agent` is set'
12062
+ ),
12063
+ name: z.string().optional().describe("display name override for the agent entry")
12064
+ },
12065
+ outputSchema: {
12066
+ displayName: z.string(),
12067
+ editor: z.enum(["zed", "jetbrains"]),
12068
+ configPath: z.string(),
12069
+ dedicatedFile: z.boolean(),
12070
+ entry: z.object({
12071
+ type: z.literal("custom"),
12072
+ command: z.string(),
12073
+ args: z.array(z.string())
12074
+ }),
12075
+ block: z.string(),
12076
+ writeCommand: z.string(),
12077
+ requiresExternalInstall: z.boolean(),
12078
+ installHint: z.string().optional()
12079
+ },
12080
+ annotations: annotations({ title: "Generate editor config" })
12081
+ },
12082
+ async ({ editor, agent, command, name }) => {
12083
+ const target = command && command.length > 0 ? command : agent ? [agent] : [];
12084
+ const selfPath = process.argv[1] ?? "acp-devtools";
12085
+ const plan = buildSetupPlan({ target, editor, selfPath, name });
12086
+ const writeParts = ["acp-devtools setup", `--editor ${editor}`, "--write"];
12087
+ if (name) writeParts.push(`--name ${JSON.stringify(name)}`);
12088
+ if (target.length === 1) writeParts.push(target[0]);
12089
+ else if (target.length > 1) writeParts.push("--", ...target);
12090
+ const installHint = plan.isCustom ? void 0 : plan.requiresExternalInstall ? plan.agentDescription : "Installs automatically via npx on first run.";
12091
+ return structuredResult({
12092
+ displayName: plan.displayName,
12093
+ editor,
12094
+ configPath: plan.editor.configPath,
12095
+ dedicatedFile: plan.editor.dedicatedFile,
12096
+ entry: plan.entry,
12097
+ block: renderConfigBlock(plan),
12098
+ writeCommand: writeParts.join(" "),
12099
+ requiresExternalInstall: plan.requiresExternalInstall,
12100
+ ...installHint !== void 0 ? { installHint } : {}
12101
+ });
12102
+ }
12103
+ );
11811
12104
  registerPrompts(server);
11812
12105
  return server;
11813
12106
  }
@@ -11931,12 +12224,14 @@ var init_mcp = __esm({
11931
12224
  "use strict";
11932
12225
  init_dist();
11933
12226
  init_version();
12227
+ init_setup_config();
11934
12228
  SERVER_INSTRUCTIONS = `acp-devtools exposes a local SQLite store of captured
11935
12229
  Agent Client Protocol (ACP) sessions. ACP is the editor\u2194agent JSON-RPC
11936
12230
  protocol used by Claude Code, Goose, Codex, and other IDE agents.
11937
12231
 
11938
12232
  Every tool here is **read-only** \u2014 nothing in this server writes to the
11939
- database. Use it to investigate:
12233
+ database or to any file (\`generate_editor_config\` only returns the config
12234
+ text to write; you apply it). Use it to investigate:
11940
12235
 
11941
12236
  - a slow session (\`get_latency_stats\` returns per-method p50/p99/max
11942
12237
  plus auto-detected insights: hotspot, long-tail, outlier, busiest,
@@ -11953,6 +12248,12 @@ database. Use it to investigate:
11953
12248
  (\`diff_sessions\` aligns two captures and reports added / removed
11954
12249
  frames and field-level payload changes)
11955
12250
 
12251
+ It also helps wire agents up: \`list_agent_shortcuts\` enumerates the known
12252
+ agents, and \`generate_editor_config({editor, agent})\` returns the
12253
+ Zed/JetBrains config block to run one through the proxy (e.g. "add junie for
12254
+ zed") \u2014 apply \`block\` to \`configPath\` with your own file tools, or run the
12255
+ returned \`writeCommand\`.
12256
+
11956
12257
  A typical investigation:
11957
12258
  1. \`list_sessions\` to see what is captured
11958
12259
  2. \`get_session_summary({session_id})\` for the headline numbers and
@@ -12888,7 +13189,7 @@ function renderHits(s, hits, needle) {
12888
13189
  truncate2(h.method ?? "\u2014", METHOD_MAX2),
12889
13190
  highlight(s, h.snippet, needle)
12890
13191
  ]);
12891
- return renderTable(s, COLUMNS2, body);
13192
+ return renderTable(s, COLUMNS3, body);
12892
13193
  }
12893
13194
  function highlight(s, snippet, needle) {
12894
13195
  if (!s.enabled || needle.length === 0) return snippet;
@@ -12910,7 +13211,7 @@ function truncate2(s, max) {
12910
13211
  if (s.length <= max) return s;
12911
13212
  return s.slice(0, Math.max(1, max - 1)) + "\u2026";
12912
13213
  }
12913
- var SNIPPET_LEN, METHOD_MAX2, COLUMNS2;
13214
+ var SNIPPET_LEN, METHOD_MAX2, COLUMNS3;
12914
13215
  var init_search = __esm({
12915
13216
  "src/commands/search.ts"() {
12916
13217
  "use strict";
@@ -12919,7 +13220,7 @@ var init_search = __esm({
12919
13220
  init_table();
12920
13221
  SNIPPET_LEN = 80;
12921
13222
  METHOD_MAX2 = 32;
12922
- COLUMNS2 = [
13223
+ COLUMNS3 = [
12923
13224
  { title: "LOC", align: "left" },
12924
13225
  { title: "METHOD", align: "left" },
12925
13226
  { title: "MATCH", align: "left" }
@@ -13107,6 +13408,139 @@ var init_session_info = __esm({
13107
13408
  }
13108
13409
  });
13109
13410
 
13411
+ // src/commands/setup.ts
13412
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
13413
+ import { dirname as dirname5 } from "path";
13414
+ function fail(message, code) {
13415
+ process.stderr.write(`acp-devtools: ${message}
13416
+ `);
13417
+ process.exit(code);
13418
+ }
13419
+ function rerunCommand(editor, target, opts) {
13420
+ const parts = ["acp-devtools setup", `--editor ${editor}`, "--write"];
13421
+ if (opts.name) parts.push(`--name ${JSON.stringify(opts.name)}`);
13422
+ if (opts.configPath) parts.push(`--config-path ${opts.configPath}`);
13423
+ if (target.length === 1) {
13424
+ parts.push(target[0]);
13425
+ } else if (target.length > 1) {
13426
+ parts.push("--", ...target);
13427
+ }
13428
+ return parts.join(" ");
13429
+ }
13430
+ function printInstallHint(plan, s) {
13431
+ if (plan.isCustom) return;
13432
+ if (plan.requiresExternalInstall) {
13433
+ process.stdout.write(`
13434
+ ${s.yellow("!")} ${plan.agentDescription}
13435
+ `);
13436
+ } else {
13437
+ process.stdout.write(`
13438
+ ${s.dim("Installs automatically via npx on first run.")}
13439
+ `);
13440
+ }
13441
+ }
13442
+ function printGuidance(plan, target, opts, configPath, s) {
13443
+ const block = renderConfigBlock(plan);
13444
+ process.stdout.write(
13445
+ `
13446
+ ${s.bold(plan.displayName)} ${s.dim("\u2192")} ${s.bold(plan.editor.label)}
13447
+
13448
+ `
13449
+ );
13450
+ process.stdout.write(` ${plan.editor.placement}:
13451
+ `);
13452
+ process.stdout.write(` ${s.cyan(configPath)}
13453
+
13454
+ `);
13455
+ process.stdout.write(`${block}
13456
+ `);
13457
+ printInstallHint(plan, s);
13458
+ process.stdout.write(`
13459
+ Then ${plan.editor.restart}.
13460
+ `);
13461
+ process.stdout.write(
13462
+ `
13463
+ ${s.dim("Or let ACP Devtools edit the file for you:")}
13464
+ ${rerunCommand(opts.editor, target, opts)}
13465
+
13466
+ `
13467
+ );
13468
+ }
13469
+ function doWrite(plan, target, opts, configPath, s) {
13470
+ let existing = null;
13471
+ try {
13472
+ existing = readFileSync5(configPath, "utf8");
13473
+ } catch (err) {
13474
+ if (err.code !== "ENOENT") {
13475
+ fail(`cannot read ${configPath}: ${err.message}`, 1);
13476
+ }
13477
+ }
13478
+ let merged;
13479
+ try {
13480
+ merged = mergeIntoConfig(existing, plan.displayName, plan.entry);
13481
+ } catch {
13482
+ process.stderr.write(
13483
+ `${s.red("\u2717")} ${configPath} isn't valid JSON \u2014 leaving it untouched.
13484
+ Paste this block in by hand:
13485
+ `
13486
+ );
13487
+ process.stdout.write(`
13488
+ ${renderConfigBlock(plan)}
13489
+
13490
+ `);
13491
+ process.exit(1);
13492
+ }
13493
+ try {
13494
+ mkdirSync6(dirname5(configPath), { recursive: true });
13495
+ writeFileSync3(configPath, merged.text);
13496
+ } catch (err) {
13497
+ fail(`cannot write ${configPath}: ${err.message}`, 1);
13498
+ }
13499
+ const verb = merged.replaced ? "updated" : "wrote";
13500
+ process.stdout.write(
13501
+ `
13502
+ ${s.green("\u2713")} ${verb} ${s.bold(JSON.stringify(plan.displayName))} in ${s.cyan(configPath)}
13503
+ `
13504
+ );
13505
+ printInstallHint(plan, s);
13506
+ process.stdout.write(`
13507
+ Then ${plan.editor.restart}.
13508
+
13509
+ `);
13510
+ process.exit(0);
13511
+ }
13512
+ function registerSetupCommand(program) {
13513
+ program.command("setup [target...]").description("Generate an editor config that runs an agent through ACP Devtools").option("--editor <name>", `target editor (${EDITORS.join(" | ")})`).option("--name <label>", "display name for the agent entry").option("--config-path <path>", "override the editor config file location").option("--write", "merge the entry into the editor config instead of printing it", false).option("--no-color", "disable ANSI colour output").action((target, opts) => {
13514
+ if (!opts.editor) {
13515
+ fail(`--editor is required (${EDITORS.join(" | ")})`, 2);
13516
+ }
13517
+ if (!isEditor(opts.editor)) {
13518
+ fail(`unknown editor "${opts.editor}" (expected ${EDITORS.join(" | ")})`, 2);
13519
+ }
13520
+ const selfPath = process.argv[1] ?? "acp-devtools";
13521
+ const plan = buildSetupPlan({
13522
+ target,
13523
+ editor: opts.editor,
13524
+ selfPath,
13525
+ name: opts.name
13526
+ });
13527
+ const configPath = opts.configPath ?? editorInfo(opts.editor).configPath;
13528
+ const s = createStyler(opts.color && colorEnabled(process.stdout));
13529
+ if (opts.write) {
13530
+ doWrite(plan, target, opts, configPath, s);
13531
+ }
13532
+ printGuidance(plan, target, opts, configPath, s);
13533
+ process.exit(0);
13534
+ });
13535
+ }
13536
+ var init_setup = __esm({
13537
+ "src/commands/setup.ts"() {
13538
+ "use strict";
13539
+ init_style();
13540
+ init_setup_config();
13541
+ }
13542
+ });
13543
+
13110
13544
  // src/commands/stats.ts
13111
13545
  function registerStatsCommand(program) {
13112
13546
  program.command("stats").description(
@@ -13401,11 +13835,11 @@ var init_stats = __esm({
13401
13835
  // src/commands/ui.ts
13402
13836
  import { createReadStream, existsSync as existsSync7, statSync as statSync3 } from "fs";
13403
13837
  import { createServer } from "http";
13404
- import { dirname as dirname5, extname, join as join4, normalize, resolve, sep } from "path";
13838
+ import { dirname as dirname6, extname, join as join5, normalize, resolve, sep } from "path";
13405
13839
  import { fileURLToPath } from "url";
13406
13840
  import { spawn as spawn2 } from "child_process";
13407
13841
  function uiDistDir() {
13408
- const here = dirname5(fileURLToPath(import.meta.url));
13842
+ const here = dirname6(fileURLToPath(import.meta.url));
13409
13843
  return resolve(here, "ui");
13410
13844
  }
13411
13845
  function contentTypeFor(path) {
@@ -13419,7 +13853,7 @@ function serveStatic(req, res, rootDir) {
13419
13853
  }
13420
13854
  const urlPath = decodeURIComponent((req.url ?? "/").split("?")[0] ?? "/");
13421
13855
  const relative = urlPath === "/" || urlPath === "" ? "/index.html" : urlPath;
13422
- const candidate = normalize(join4(rootDir, relative));
13856
+ const candidate = normalize(join5(rootDir, relative));
13423
13857
  if (!candidate.startsWith(rootDir + sep) && candidate !== rootDir) {
13424
13858
  res.statusCode = 403;
13425
13859
  res.end();
@@ -13427,7 +13861,7 @@ function serveStatic(req, res, rootDir) {
13427
13861
  }
13428
13862
  let filePath = candidate;
13429
13863
  if (!existsSync7(filePath) || !statSync3(filePath).isFile()) {
13430
- filePath = join4(rootDir, "index.html");
13864
+ filePath = join5(rootDir, "index.html");
13431
13865
  if (!existsSync7(filePath)) {
13432
13866
  res.statusCode = 404;
13433
13867
  res.setHeader("content-type", "text/plain; charset=utf-8");
@@ -13470,7 +13904,7 @@ function registerUiCommand(program) {
13470
13904
  process.exit(2);
13471
13905
  }
13472
13906
  const rootDir = uiDistDir();
13473
- if (!existsSync7(join4(rootDir, "index.html"))) {
13907
+ if (!existsSync7(join5(rootDir, "index.html"))) {
13474
13908
  process.stderr.write(
13475
13909
  `acp-devtools: UI bundle missing at ${rootDir}
13476
13910
  Run \`npm run build:full\` in the monorepo, or reinstall the published package.
@@ -13597,6 +14031,7 @@ function registerValidateCommand(program) {
13597
14031
  }
13598
14032
  const methodNeedle = opts.method ? opts.method.toLowerCase() : null;
13599
14033
  const violations = [];
14034
+ const advisories = [];
13600
14035
  let checked = 0;
13601
14036
  let skipped = 0;
13602
14037
  for (const m of messages) {
@@ -13610,11 +14045,20 @@ function registerValidateCommand(program) {
13610
14045
  continue;
13611
14046
  }
13612
14047
  checked += 1;
13613
- if (result.valid) continue;
13614
14048
  const method = m.method ?? pairedMethod ?? null;
13615
- if (methodNeedle && (!method || !method.toLowerCase().includes(methodNeedle))) {
13616
- continue;
14049
+ const methodMatch = !methodNeedle || !!method && method.toLowerCase().includes(methodNeedle);
14050
+ if (methodMatch && result.advisories) {
14051
+ for (const a of result.advisories) {
14052
+ advisories.push({
14053
+ seq: m.seq,
14054
+ method,
14055
+ schemaName: result.schemaName ?? "?",
14056
+ path: a.path,
14057
+ message: a.message
14058
+ });
14059
+ }
13617
14060
  }
14061
+ if (result.valid || !methodMatch) continue;
13618
14062
  for (const e of result.errors) {
13619
14063
  if (violations.length >= limit) break;
13620
14064
  const v = {
@@ -13633,7 +14077,7 @@ function registerValidateCommand(program) {
13633
14077
  if (opts.json) {
13634
14078
  process.stdout.write(
13635
14079
  JSON.stringify(
13636
- { sessionId: id, checked, skipped, violations },
14080
+ { sessionId: id, checked, skipped, violations, advisories },
13637
14081
  null,
13638
14082
  2
13639
14083
  ) + "\n"
@@ -13644,18 +14088,25 @@ function registerValidateCommand(program) {
13644
14088
  const errStyler = createStyler(colorEnabled(process.stderr));
13645
14089
  const affected = new Set(violations.map((v) => v.method ?? "?"));
13646
14090
  const vCount = violations.length === 0 ? errStyler.green("0 violations") : errStyler.red(`${violations.length} violations`);
14091
+ const aCount = advisories.length === 0 ? "" : ` \xB7 ${errStyler.yellow(`${advisories.length} advisor${advisories.length === 1 ? "y" : "ies"}`)}`;
13647
14092
  process.stderr.write(
13648
- `${errStyler.bold(errStyler.cyan(`session #${id}`))} \xB7 ${checked} checked \xB7 ${skipped} skipped (no schema) \xB7 ${vCount} in ${affected.size} method${affected.size === 1 ? "" : "s"}
14093
+ `${errStyler.bold(errStyler.cyan(`session #${id}`))} \xB7 ${checked} checked \xB7 ${skipped} skipped (no schema) \xB7 ${vCount} in ${affected.size} method${affected.size === 1 ? "" : "s"}${aCount}
13649
14094
 
13650
14095
  `
13651
14096
  );
13652
- if (violations.length === 0) {
14097
+ const s = createStyler(colorEnabled(process.stdout));
14098
+ if (violations.length > 0) {
14099
+ process.stdout.write(renderViolations(s, violations));
14100
+ }
14101
+ if (advisories.length > 0) {
14102
+ if (violations.length > 0) process.stdout.write("\n");
14103
+ process.stdout.write(renderAdvisories(s, advisories));
14104
+ }
14105
+ if (violations.length === 0 && advisories.length === 0) {
13653
14106
  process.stdout.write(errStyler.green("no violations") + "\n");
13654
14107
  return;
13655
14108
  }
13656
- const s = createStyler(colorEnabled(process.stdout));
13657
- process.stdout.write(renderViolations(s, violations));
13658
- process.exit(1);
14109
+ if (violations.length > 0) process.exit(1);
13659
14110
  });
13660
14111
  }
13661
14112
  function renderViolations(s, violations) {
@@ -13666,13 +14117,24 @@ function renderViolations(s, violations) {
13666
14117
  s.cyan(truncate3(v.path, PATH_MAX)),
13667
14118
  s.red(v.message)
13668
14119
  ]);
13669
- return renderTable(s, COLUMNS3, body);
14120
+ return renderTable(s, COLUMNS4, body);
14121
+ }
14122
+ function renderAdvisories(s, advisories) {
14123
+ const heading5 = s.yellow(`${advisories.length} advisor${advisories.length === 1 ? "y" : "ies"}`) + s.dim(" \u2014 valid today, but a future ACP version will require these fields") + "\n";
14124
+ const body = advisories.map((a) => [
14125
+ s.dim(`#${a.seq}`),
14126
+ truncate3(a.method ?? "\u2014", METHOD_MAX4),
14127
+ s.dim(truncate3(a.schemaName, SCHEMA_MAX)),
14128
+ s.yellow(truncate3(a.path, PATH_MAX)),
14129
+ s.yellow(a.message)
14130
+ ]);
14131
+ return heading5 + renderTable(s, ADVISORY_COLUMNS, body);
13670
14132
  }
13671
14133
  function truncate3(s, max) {
13672
14134
  if (s.length <= max) return s;
13673
14135
  return s.slice(0, Math.max(1, max - 1)) + "\u2026";
13674
14136
  }
13675
- var METHOD_MAX4, SCHEMA_MAX, PATH_MAX, COLUMNS3;
14137
+ var METHOD_MAX4, SCHEMA_MAX, PATH_MAX, COLUMNS4, ADVISORY_COLUMNS;
13676
14138
  var init_validate2 = __esm({
13677
14139
  "src/commands/validate.ts"() {
13678
14140
  "use strict";
@@ -13683,19 +14145,26 @@ var init_validate2 = __esm({
13683
14145
  METHOD_MAX4 = 28;
13684
14146
  SCHEMA_MAX = 28;
13685
14147
  PATH_MAX = 38;
13686
- COLUMNS3 = [
14148
+ COLUMNS4 = [
13687
14149
  { title: "SEQ", align: "right" },
13688
14150
  { title: "METHOD", align: "left" },
13689
14151
  { title: "SCHEMA", align: "left" },
13690
14152
  { title: "PATH", align: "left" },
13691
14153
  { title: "ERROR", align: "left" }
13692
14154
  ];
14155
+ ADVISORY_COLUMNS = [
14156
+ { title: "SEQ", align: "right" },
14157
+ { title: "METHOD", align: "left" },
14158
+ { title: "SCHEMA", align: "left" },
14159
+ { title: "FIELD", align: "left" },
14160
+ { title: "ADVISORY", align: "left" }
14161
+ ];
13693
14162
  }
13694
14163
  });
13695
14164
 
13696
14165
  // src/lib/help.ts
13697
- import { homedir as homedir4 } from "os";
13698
- function tidyHome(text, home = homedir4()) {
14166
+ import { homedir as homedir5 } from "os";
14167
+ function tidyHome(text, home = homedir5()) {
13699
14168
  if (!home) return text;
13700
14169
  const escapedHome = home.replace(/\\/g, "\\\\");
13701
14170
  const collapsed = text.split(escapedHome).join("~").split(home).join("~");
@@ -13857,7 +14326,7 @@ var init_help = __esm({
13857
14326
  },
13858
14327
  { title: "View", commands: ["ui", "replay"] },
13859
14328
  { title: "Manage", commands: ["export", "import", "delete", "backfill-metadata"] },
13860
- { title: "Setup", commands: ["doctor", "mcp"] }
14329
+ { title: "Setup", commands: ["setup", "agents", "doctor", "mcp"] }
13861
14330
  ];
13862
14331
  SUMMARIES = {
13863
14332
  proxy: "Capture an agent's traffic through a proxy",
@@ -13876,6 +14345,8 @@ var init_help = __esm({
13876
14345
  import: "Import a JSON export into the store",
13877
14346
  delete: "Delete saved sessions permanently",
13878
14347
  "backfill-metadata": "Recompute session metadata columns",
14348
+ setup: "Generate an editor config for an agent",
14349
+ agents: "List the built-in agent shortcuts",
13879
14350
  doctor: "Diagnose setup and report issues",
13880
14351
  mcp: "Expose captures to Claude over MCP"
13881
14352
  };
@@ -13934,6 +14405,17 @@ var init_help = __esm({
13934
14405
  replay: ["acp-devtools replay 12", "acp-devtools replay --file session-12.json"],
13935
14406
  validate: ["acp-devtools validate 12"],
13936
14407
  "session-info": ["acp-devtools session-info 12"],
14408
+ setup: [
14409
+ "# See which agent shortcuts exist",
14410
+ "acp-devtools agents",
14411
+ "",
14412
+ "# Print a Zed config block for a registry agent",
14413
+ "acp-devtools setup junie --editor zed",
14414
+ "",
14415
+ "# Wrap a custom agent and write it straight into the JetBrains config",
14416
+ "acp-devtools setup --editor jetbrains --write -- npx -y @your-scope/your-acp"
14417
+ ],
14418
+ agents: ["acp-devtools agents", "", "# Machine-readable", "acp-devtools agents --json"],
13937
14419
  doctor: ["acp-devtools doctor"],
13938
14420
  mcp: ["# Read-only MCP server over stdio (wire into .claude/mcp_servers.json)", "acp-devtools mcp"],
13939
14421
  "backfill-metadata": [
@@ -13970,6 +14452,8 @@ function buildProgram() {
13970
14452
  registerInspectCommand(program);
13971
14453
  registerSearchCommand(program);
13972
14454
  registerSessionInfoCommand(program);
14455
+ registerSetupCommand(program);
14456
+ registerAgentsCommand(program);
13973
14457
  registerStatsCommand(program);
13974
14458
  registerBackfillMetadataCommand(program);
13975
14459
  registerMcpCommand(program);
@@ -13981,6 +14465,7 @@ function buildProgram() {
13981
14465
  var init_program = __esm({
13982
14466
  "src/program.ts"() {
13983
14467
  "use strict";
14468
+ init_agents2();
13984
14469
  init_backfill_metadata();
13985
14470
  init_delete();
13986
14471
  init_diff();
@@ -13996,6 +14481,7 @@ var init_program = __esm({
13996
14481
  init_replay2();
13997
14482
  init_search();
13998
14483
  init_session_info();
14484
+ init_setup();
13999
14485
  init_stats();
14000
14486
  init_ui();
14001
14487
  init_validate2();
@@ -14100,6 +14586,8 @@ var KNOWN_SUBCOMMANDS = /* @__PURE__ */ new Set([
14100
14586
  "inspect",
14101
14587
  "search",
14102
14588
  "session-info",
14589
+ "setup",
14590
+ "agents",
14103
14591
  "stats",
14104
14592
  "validate",
14105
14593
  "backfill-metadata",