@tonyclaw/agent-inspector 2.1.7 → 2.1.8

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 (34) hide show
  1. package/.output/cli.js +274 -140
  2. package/.output/nitro.json +1 -1
  3. package/.output/public/assets/{CompareDrawer-BLS7CVvA.js → CompareDrawer-CHkT7kun.js} +1 -1
  4. package/.output/public/assets/{ProxyViewerContainer-BFZ1WG07.js → ProxyViewerContainer-BiZ2_tMC.js} +21 -21
  5. package/.output/public/assets/{ReplayDialog-DIGF807X.js → ReplayDialog-C85cakjx.js} +1 -1
  6. package/.output/public/assets/{RequestAnatomy-C3Nlvp9P.js → RequestAnatomy-Dn_pMMoX.js} +1 -1
  7. package/.output/public/assets/{ResponseView-BbZywqHe.js → ResponseView-DzcxCUBf.js} +1 -1
  8. package/.output/public/assets/{StreamingChunkSequence-Dk27PfPn.js → StreamingChunkSequence-BamtvkgZ.js} +1 -1
  9. package/.output/public/assets/_sessionId-xBBbFH1M.js +1 -0
  10. package/.output/public/assets/index-6udjwQO1.js +1 -0
  11. package/.output/public/assets/{main-BxHXMs6B.js → main-Dc2qVWpb.js} +2 -2
  12. package/.output/server/{_sessionId-D5HNwb9A.mjs → _sessionId-BnimGger.mjs} +2 -2
  13. package/.output/server/_ssr/{CompareDrawer-QNk1twf8.mjs → CompareDrawer-CwVFykVF.mjs} +2 -2
  14. package/.output/server/_ssr/{ProxyViewerContainer-DGhc2le_.mjs → ProxyViewerContainer-X3S2TWBJ.mjs} +205 -132
  15. package/.output/server/_ssr/{ReplayDialog-D8wg4VNL.mjs → ReplayDialog-BxC12XUj.mjs} +3 -3
  16. package/.output/server/_ssr/{RequestAnatomy-BtJHKVl2.mjs → RequestAnatomy-CWWS8Qh7.mjs} +2 -2
  17. package/.output/server/_ssr/{ResponseView-CndDItiU.mjs → ResponseView-BewErHF-.mjs} +2 -2
  18. package/.output/server/_ssr/{StreamingChunkSequence-D5Ywpbr7.mjs → StreamingChunkSequence-BlmCVryW.mjs} +2 -2
  19. package/.output/server/_ssr/{index-DtcUTHAX.mjs → index-5qbGuoFX.mjs} +2 -2
  20. package/.output/server/_ssr/index.mjs +2 -2
  21. package/.output/server/_ssr/{router-C0apgqfC.mjs → router-s78S54MU.mjs} +16 -7
  22. package/.output/server/{_tanstack-start-manifest_v-BoTPaB-V.mjs → _tanstack-start-manifest_v-DTxpr3KE.mjs} +1 -1
  23. package/.output/server/index.mjs +63 -63
  24. package/README.md +10 -6
  25. package/package.json +1 -1
  26. package/src/cli/detect-tools.ts +2 -1
  27. package/src/cli/onboard.ts +334 -167
  28. package/src/components/providers/SettingsDialog.tsx +47 -0
  29. package/src/components/proxy-viewer/ConversationGroup.tsx +28 -0
  30. package/src/components/proxy-viewer/ConversationHeader.tsx +34 -0
  31. package/src/components/proxy-viewer/LogEntryHeader.tsx +3 -12
  32. package/src/mcp/server.ts +18 -4
  33. package/.output/public/assets/_sessionId-BHpX8AVf.js +0 -1
  34. package/.output/public/assets/index-BXHM9OMb.js +0 -1
@@ -61,8 +61,13 @@ export type OnboardFlags = {
61
61
  opencode: boolean;
62
62
  opencodeOnly: boolean;
63
63
  opencodeConfig: string | null;
64
- opencodeTransport: OpenCodeTransport;
64
+ opencodeTransport: AgentMcpTransport;
65
65
  opencodeMcpUrl: string;
66
+ mimo: boolean;
67
+ mimoOnly: boolean;
68
+ mimoConfig: string | null;
69
+ mimoTransport: AgentMcpTransport;
70
+ mimoMcpUrl: string;
66
71
  uninstall: boolean;
67
72
  status: boolean;
68
73
  json: boolean;
@@ -70,29 +75,45 @@ export type OnboardFlags = {
70
75
  codexSkillDir: string | null;
71
76
  };
72
77
 
73
- type OpenCodeTransport = "local" | "remote";
74
- type OpenCodeStatusTransport = OpenCodeTransport | "unknown";
75
- type OpenCodeConfigState = "missing" | "configured" | "custom" | "disabled" | "invalid";
78
+ type AgentMcpTransport = "local" | "remote";
79
+ type AgentMcpStatusTransport = AgentMcpTransport | "unknown";
80
+ type AgentMcpConfigState = "missing" | "configured" | "custom" | "disabled" | "invalid";
76
81
 
77
- type OpenCodeConfigStatus = {
78
- label: "OpenCode MCP config";
82
+ type AgentMcpConfigTarget = {
83
+ label: "OpenCode" | "MiMo Code";
84
+ configLabel: "OpenCode MCP config" | "MiMo Code MCP config";
85
+ configPath: string;
86
+ transport: AgentMcpTransport;
87
+ endpoint: string;
88
+ setupAction: string;
89
+ forceAction: string;
90
+ verifyCommand: string;
91
+ invalidAction: string;
92
+ configObjectName: string;
93
+ defaultSchema: string;
94
+ };
95
+
96
+ type AgentMcpConfigStatus = {
97
+ label: "OpenCode MCP config" | "MiMo Code MCP config";
79
98
  path: string;
80
- state: OpenCodeConfigState;
81
- transport: OpenCodeStatusTransport;
99
+ state: AgentMcpConfigState;
100
+ transport: AgentMcpStatusTransport;
82
101
  endpoint: string;
83
102
  action: string;
103
+ forceAction: string;
104
+ verifyCommand: string;
84
105
  detail: string;
85
106
  };
86
107
 
87
- type OpenCodeConfigPlan = {
88
- status: OpenCodeConfigStatus;
108
+ type AgentMcpConfigPlan = {
109
+ status: AgentMcpConfigStatus;
89
110
  body: string | null;
90
111
  shouldWrite: boolean;
91
112
  shouldRemove: boolean;
92
113
  blockedMessage: string | null;
93
114
  };
94
115
 
95
- type OpenCodeReadResult =
116
+ type AgentConfigReadResult =
96
117
  | {
97
118
  ok: true;
98
119
  exists: boolean;
@@ -171,6 +192,11 @@ function parseFlags(argv: readonly string[]): OnboardFlags {
171
192
  opencodeConfig: null,
172
193
  opencodeTransport: "local",
173
194
  opencodeMcpUrl: DEFAULT_MCP_URL,
195
+ mimo: false,
196
+ mimoOnly: false,
197
+ mimoConfig: null,
198
+ mimoTransport: "local",
199
+ mimoMcpUrl: DEFAULT_MCP_URL,
174
200
  uninstall: false,
175
201
  status: false,
176
202
  json: false,
@@ -251,6 +277,53 @@ function parseFlags(argv: readonly string[]): OnboardFlags {
251
277
  i++;
252
278
  break;
253
279
  }
280
+ case "--mimo":
281
+ flags.mimo = true;
282
+ break;
283
+ case "--mimo-only":
284
+ flags.mimo = true;
285
+ flags.mimoOnly = true;
286
+ break;
287
+ case "--mimo-config": {
288
+ const next = argv[i + 1];
289
+ if (next === undefined) {
290
+ process.stderr.write("agent-inspector onboard: --mimo-config requires a path argument\n");
291
+ process.exit(2);
292
+ }
293
+ flags.mimoConfig = next;
294
+ i++;
295
+ break;
296
+ }
297
+ case "--mimo-transport": {
298
+ const next = argv[i + 1];
299
+ if (next === undefined) {
300
+ process.stderr.write(
301
+ "agent-inspector onboard: --mimo-transport requires local or remote\n",
302
+ );
303
+ process.exit(2);
304
+ }
305
+ switch (next) {
306
+ case "local":
307
+ case "remote":
308
+ flags.mimoTransport = next;
309
+ break;
310
+ default:
311
+ process.stderr.write(`agent-inspector onboard: invalid --mimo-transport: ${next}\n`);
312
+ process.exit(2);
313
+ }
314
+ i++;
315
+ break;
316
+ }
317
+ case "--mimo-mcp-url": {
318
+ const next = argv[i + 1];
319
+ if (next === undefined) {
320
+ process.stderr.write("agent-inspector onboard: --mimo-mcp-url requires a URL\n");
321
+ process.exit(2);
322
+ }
323
+ flags.mimoMcpUrl = next;
324
+ i++;
325
+ break;
326
+ }
254
327
  case "--uninstall":
255
328
  flags.uninstall = true;
256
329
  break;
@@ -314,6 +387,12 @@ Options:
314
387
  --opencode-transport <local|remote>
315
388
  OpenCode MCP transport to write (default: local)
316
389
  --opencode-mcp-url <url> Agent Inspector MCP endpoint for OpenCode
390
+ --mimo Also configure MiMo Code MCP in mimocode.json/jsonc
391
+ --mimo-only Configure only MiMo Code MCP
392
+ --mimo-config <path> Override MiMo config path (default: ~/.config/mimocode/mimocode.jsonc)
393
+ --mimo-transport <local|remote>
394
+ MiMo Code MCP transport to write (default: local)
395
+ --mimo-mcp-url <url> Agent Inspector MCP endpoint for MiMo Code
317
396
  --uninstall Remove generated files whose version matches this package
318
397
  --status Show installed onboarding file versions and suggested action
319
398
  --json Emit machine-readable JSON with --status
@@ -349,6 +428,14 @@ function isOpenCodeEnabled(flags: OnboardFlags): boolean {
349
428
  return flags.opencode || flags.opencodeOnly;
350
429
  }
351
430
 
431
+ function isMiMoEnabled(flags: OnboardFlags): boolean {
432
+ return flags.mimo || flags.mimoOnly;
433
+ }
434
+
435
+ function hasOnlyAgentMcpTarget(flags: OnboardFlags): boolean {
436
+ return flags.opencodeOnly || flags.mimoOnly;
437
+ }
438
+
352
439
  function resolveOpenCodeConfigPath(flags: OnboardFlags): string {
353
440
  if (flags.opencodeConfig !== null) {
354
441
  return flags.opencodeConfig;
@@ -362,6 +449,68 @@ function resolveOpenCodeConfigPath(flags: OnboardFlags): string {
362
449
  return join(configDir, "opencode.json");
363
450
  }
364
451
 
452
+ function resolveMiMoConfigPath(flags: OnboardFlags): string {
453
+ if (flags.mimoConfig !== null) {
454
+ return flags.mimoConfig;
455
+ }
456
+
457
+ const configDir = join(homedir(), ".config", "mimocode");
458
+ const jsoncPath = join(configDir, "mimocode.jsonc");
459
+ if (existsSync(jsoncPath)) {
460
+ return jsoncPath;
461
+ }
462
+
463
+ const jsonPath = join(configDir, "mimocode.json");
464
+ if (existsSync(jsonPath)) {
465
+ return jsonPath;
466
+ }
467
+
468
+ const legacyDir = join(homedir(), ".mimocode");
469
+ const legacyJsoncPath = join(legacyDir, "config.jsonc");
470
+ if (existsSync(legacyJsoncPath)) {
471
+ return legacyJsoncPath;
472
+ }
473
+
474
+ const legacyJsonPath = join(legacyDir, "config.json");
475
+ if (existsSync(legacyJsonPath)) {
476
+ return legacyJsonPath;
477
+ }
478
+
479
+ return jsoncPath;
480
+ }
481
+
482
+ function openCodeTarget(flags: OnboardFlags): AgentMcpConfigTarget {
483
+ return {
484
+ label: "OpenCode",
485
+ configLabel: "OpenCode MCP config",
486
+ configPath: resolveOpenCodeConfigPath(flags),
487
+ transport: flags.opencodeTransport,
488
+ endpoint: flags.opencodeMcpUrl,
489
+ setupAction: "agent-inspector onboard --opencode-only",
490
+ forceAction: "agent-inspector onboard --opencode-only --force",
491
+ verifyCommand: "opencode mcp list",
492
+ invalidAction: "fix OpenCode JSON/JSONC, then rerun agent-inspector onboard --opencode-only",
493
+ configObjectName: "mcp.agent-inspector",
494
+ defaultSchema: "https://opencode.ai/config.json",
495
+ };
496
+ }
497
+
498
+ function miMoTarget(flags: OnboardFlags): AgentMcpConfigTarget {
499
+ return {
500
+ label: "MiMo Code",
501
+ configLabel: "MiMo Code MCP config",
502
+ configPath: resolveMiMoConfigPath(flags),
503
+ transport: flags.mimoTransport,
504
+ endpoint: flags.mimoMcpUrl,
505
+ setupAction: "agent-inspector onboard --mimo-only",
506
+ forceAction: "agent-inspector onboard --mimo-only --force",
507
+ verifyCommand: "mimo mcp list",
508
+ invalidAction: "fix MiMo Code JSON/JSONC, then rerun agent-inspector onboard --mimo-only",
509
+ configObjectName: "mcp.agent-inspector",
510
+ defaultSchema: "https://mimo.xiaomi.com/mimocode/config.json",
511
+ };
512
+ }
513
+
365
514
  function stripJsoncComments(raw: string): string {
366
515
  let result = "";
367
516
  let index = 0;
@@ -418,7 +567,7 @@ function stripJsoncComments(raw: string): string {
418
567
  return result;
419
568
  }
420
569
 
421
- function readOpenCodeConfig(path: string): OpenCodeReadResult {
570
+ function readAgentConfig(path: string, target: AgentMcpConfigTarget): AgentConfigReadResult {
422
571
  if (!existsSync(path)) {
423
572
  return { ok: true, exists: false, config: {} };
424
573
  }
@@ -435,7 +584,7 @@ function readOpenCodeConfig(path: string): OpenCodeReadResult {
435
584
  return {
436
585
  ok: false,
437
586
  exists: true,
438
- message: "OpenCode config root must be a JSON object",
587
+ message: `${target.label} config root must be a JSON object`,
439
588
  };
440
589
  } catch (err) {
441
590
  const message = err instanceof Error ? err.message : String(err);
@@ -447,8 +596,8 @@ function readOpenCodeConfig(path: string): OpenCodeReadResult {
447
596
  }
448
597
  }
449
598
 
450
- function buildOpenCodeMcpEntry(
451
- transport: OpenCodeTransport,
599
+ function buildAgentMcpEntry(
600
+ transport: AgentMcpTransport,
452
601
  endpoint: string,
453
602
  ): Record<string, unknown> {
454
603
  switch (transport) {
@@ -481,7 +630,7 @@ function stringArrayEquals(value: unknown, expected: readonly string[]): boolean
481
630
  return value.every((item, index) => item === expected[index]);
482
631
  }
483
632
 
484
- function isOpenCodeLocalEntry(value: unknown, endpoint: string, ignoreEnabled: boolean): boolean {
633
+ function isAgentLocalEntry(value: unknown, endpoint: string, ignoreEnabled: boolean): boolean {
485
634
  if (!isObject(value)) {
486
635
  return false;
487
636
  }
@@ -506,7 +655,7 @@ function isOpenCodeLocalEntry(value: unknown, endpoint: string, ignoreEnabled: b
506
655
  return value["type"] === "local" && (usesUrlArg || usesEnv);
507
656
  }
508
657
 
509
- function isOpenCodeRemoteEntry(value: unknown, endpoint: string, ignoreEnabled: boolean): boolean {
658
+ function isAgentRemoteEntry(value: unknown, endpoint: string, ignoreEnabled: boolean): boolean {
510
659
  if (!isObject(value)) {
511
660
  return false;
512
661
  }
@@ -519,14 +668,14 @@ function isOpenCodeRemoteEntry(value: unknown, endpoint: string, ignoreEnabled:
519
668
  return value["type"] === "remote" && value["url"] === endpoint;
520
669
  }
521
670
 
522
- function isKnownOpenCodeEntry(value: unknown, endpoint: string, ignoreEnabled: boolean): boolean {
671
+ function isKnownAgentEntry(value: unknown, endpoint: string, ignoreEnabled: boolean): boolean {
523
672
  return (
524
- isOpenCodeLocalEntry(value, endpoint, ignoreEnabled) ||
525
- isOpenCodeRemoteEntry(value, endpoint, ignoreEnabled)
673
+ isAgentLocalEntry(value, endpoint, ignoreEnabled) ||
674
+ isAgentRemoteEntry(value, endpoint, ignoreEnabled)
526
675
  );
527
676
  }
528
677
 
529
- function detectOpenCodeTransport(value: unknown): OpenCodeStatusTransport {
678
+ function detectAgentTransport(value: unknown): AgentMcpStatusTransport {
530
679
  if (!isObject(value)) {
531
680
  return "unknown";
532
681
  }
@@ -541,46 +690,47 @@ function detectOpenCodeTransport(value: unknown): OpenCodeStatusTransport {
541
690
  }
542
691
  }
543
692
 
544
- function openCodeActionForStatus(status: OpenCodeConfigStatus): string {
693
+ function agentActionForStatus(target: AgentMcpConfigTarget, status: AgentMcpConfigStatus): string {
545
694
  switch (status.state) {
546
695
  case "missing":
547
- return "agent-inspector onboard --opencode-only";
696
+ return target.setupAction;
548
697
  case "configured":
549
- return "opencode mcp list";
698
+ return target.verifyCommand;
550
699
  case "disabled":
551
700
  case "custom":
552
- return "agent-inspector onboard --opencode-only --force";
701
+ return target.forceAction;
553
702
  case "invalid":
554
- return "fix OpenCode JSON/JSONC, then rerun agent-inspector onboard --opencode-only";
703
+ return target.invalidAction;
555
704
  default:
556
- return "agent-inspector onboard --opencode-only";
705
+ return target.setupAction;
557
706
  }
558
707
  }
559
708
 
560
- function makeOpenCodeStatus(
561
- path: string,
562
- state: OpenCodeConfigState,
563
- transport: OpenCodeStatusTransport,
564
- endpoint: string,
709
+ function makeAgentStatus(
710
+ target: AgentMcpConfigTarget,
711
+ state: AgentMcpConfigState,
712
+ transport: AgentMcpStatusTransport,
565
713
  detail: string,
566
- ): OpenCodeConfigStatus {
567
- const status: OpenCodeConfigStatus = {
568
- label: "OpenCode MCP config",
569
- path,
714
+ ): AgentMcpConfigStatus {
715
+ const status: AgentMcpConfigStatus = {
716
+ label: target.configLabel,
717
+ path: target.configPath,
570
718
  state,
571
719
  transport,
572
- endpoint,
573
- action: "agent-inspector onboard --opencode-only",
720
+ endpoint: target.endpoint,
721
+ action: target.setupAction,
722
+ forceAction: target.forceAction,
723
+ verifyCommand: target.verifyCommand,
574
724
  detail,
575
725
  };
576
726
 
577
727
  return {
578
728
  ...status,
579
- action: openCodeActionForStatus(status),
729
+ action: agentActionForStatus(target, status),
580
730
  };
581
731
  }
582
732
 
583
- function getOpenCodeEntry(config: Record<string, unknown>): unknown {
733
+ function getAgentEntry(config: Record<string, unknown>): unknown {
584
734
  const mcp = config["mcp"];
585
735
  if (!isObject(mcp)) {
586
736
  return undefined;
@@ -588,13 +738,11 @@ function getOpenCodeEntry(config: Record<string, unknown>): unknown {
588
738
  return mcp["agent-inspector"];
589
739
  }
590
740
 
591
- function buildOpenCodeConfig(
741
+ function buildAgentConfig(
592
742
  config: Record<string, unknown>,
593
- transport: OpenCodeTransport,
594
- endpoint: string,
743
+ target: AgentMcpConfigTarget,
595
744
  ): Record<string, unknown> {
596
- const schema =
597
- typeof config["$schema"] === "string" ? config["$schema"] : "https://opencode.ai/config.json";
745
+ const schema = typeof config["$schema"] === "string" ? config["$schema"] : target.defaultSchema;
598
746
  const nextConfig: Record<string, unknown> = {
599
747
  $schema: schema,
600
748
  };
@@ -607,16 +755,16 @@ function buildOpenCodeConfig(
607
755
 
608
756
  const currentMcp = config["mcp"];
609
757
  const nextMcp: Record<string, unknown> = isObject(currentMcp) ? { ...currentMcp } : {};
610
- nextMcp["agent-inspector"] = buildOpenCodeMcpEntry(transport, endpoint);
758
+ nextMcp["agent-inspector"] = buildAgentMcpEntry(target.transport, target.endpoint);
611
759
  nextConfig["mcp"] = nextMcp;
612
760
  return nextConfig;
613
761
  }
614
762
 
615
- function formatOpenCodeConfig(config: Record<string, unknown>): string {
763
+ function formatAgentConfig(config: Record<string, unknown>): string {
616
764
  return `${JSON.stringify(config, null, 2)}\n`;
617
765
  }
618
766
 
619
- function removeOpenCodeEntry(config: Record<string, unknown>): Record<string, unknown> {
767
+ function removeAgentEntry(config: Record<string, unknown>): Record<string, unknown> {
620
768
  const nextConfig: Record<string, unknown> = {};
621
769
  for (const [key, value] of Object.entries(config)) {
622
770
  if (key !== "mcp") {
@@ -642,74 +790,70 @@ function removeOpenCodeEntry(config: Record<string, unknown>): Record<string, un
642
790
  return nextConfig;
643
791
  }
644
792
 
645
- function readOpenCodeStatus(flags: OnboardFlags): OpenCodeConfigStatus {
646
- const path = resolveOpenCodeConfigPath(flags);
647
- const read = readOpenCodeConfig(path);
793
+ function readAgentStatus(target: AgentMcpConfigTarget): AgentMcpConfigStatus {
794
+ const read = readAgentConfig(target.configPath, target);
648
795
  if (!read.ok) {
649
- return makeOpenCodeStatus(path, "invalid", "unknown", flags.opencodeMcpUrl, read.message);
796
+ return makeAgentStatus(target, "invalid", "unknown", read.message);
650
797
  }
651
798
 
652
- const entry = getOpenCodeEntry(read.config);
799
+ const entry = getAgentEntry(read.config);
653
800
  if (entry === undefined) {
654
801
  const detail = read.exists
655
- ? "OpenCode config has no mcp.agent-inspector entry"
802
+ ? `${target.label} config has no ${target.configObjectName}`
656
803
  : "Missing file";
657
- return makeOpenCodeStatus(path, "missing", "unknown", flags.opencodeMcpUrl, detail);
804
+ return makeAgentStatus(target, "missing", "unknown", detail);
658
805
  }
659
806
 
660
- const transport = detectOpenCodeTransport(entry);
807
+ const transport = detectAgentTransport(entry);
661
808
  if (isObject(entry) && entry["enabled"] === false) {
662
- return makeOpenCodeStatus(
663
- path,
809
+ return makeAgentStatus(
810
+ target,
664
811
  "disabled",
665
812
  transport,
666
- flags.opencodeMcpUrl,
667
- "mcp.agent-inspector is present but disabled",
813
+ `${target.configObjectName} is present but disabled`,
668
814
  );
669
815
  }
670
816
 
671
- if (isKnownOpenCodeEntry(entry, flags.opencodeMcpUrl, false)) {
672
- return makeOpenCodeStatus(
673
- path,
817
+ if (isKnownAgentEntry(entry, target.endpoint, false)) {
818
+ return makeAgentStatus(
819
+ target,
674
820
  "configured",
675
821
  transport,
676
- flags.opencodeMcpUrl,
677
- "mcp.agent-inspector points at Agent Inspector MCP",
822
+ `${target.configObjectName} points at Agent Inspector MCP`,
678
823
  );
679
824
  }
680
825
 
681
- return makeOpenCodeStatus(
682
- path,
826
+ return makeAgentStatus(
827
+ target,
683
828
  "custom",
684
829
  transport,
685
- flags.opencodeMcpUrl,
686
- "mcp.agent-inspector exists but does not match Agent Inspector defaults",
830
+ `${target.configObjectName} exists but does not match Agent Inspector defaults`,
687
831
  );
688
832
  }
689
833
 
690
- function buildOpenCodePlan(flags: OnboardFlags): OpenCodeConfigPlan {
691
- const path = resolveOpenCodeConfigPath(flags);
692
- const read = readOpenCodeConfig(path);
834
+ function buildAgentPlan(
835
+ target: AgentMcpConfigTarget,
836
+ uninstall: boolean,
837
+ force: boolean,
838
+ ): AgentMcpConfigPlan {
839
+ const read = readAgentConfig(target.configPath, target);
693
840
  if (!read.ok) {
694
841
  return {
695
- status: makeOpenCodeStatus(path, "invalid", "unknown", flags.opencodeMcpUrl, read.message),
842
+ status: makeAgentStatus(target, "invalid", "unknown", read.message),
696
843
  body: null,
697
844
  shouldWrite: false,
698
845
  shouldRemove: false,
699
- blockedMessage: `OpenCode config is invalid: ${read.message}`,
846
+ blockedMessage: `${target.label} config is invalid: ${read.message}`,
700
847
  };
701
848
  }
702
849
 
703
- const status = readOpenCodeStatus(flags);
704
- const entry = getOpenCodeEntry(read.config);
705
- const nextBody = formatOpenCodeConfig(
706
- buildOpenCodeConfig(read.config, flags.opencodeTransport, flags.opencodeMcpUrl),
707
- );
850
+ const status = readAgentStatus(target);
851
+ const entry = getAgentEntry(read.config);
852
+ const nextBody = formatAgentConfig(buildAgentConfig(read.config, target));
708
853
 
709
- if (flags.uninstall) {
710
- const shouldRemove =
711
- entry !== undefined && isKnownOpenCodeEntry(entry, flags.opencodeMcpUrl, true);
712
- const removeBody = shouldRemove ? formatOpenCodeConfig(removeOpenCodeEntry(read.config)) : null;
854
+ if (uninstall) {
855
+ const shouldRemove = entry !== undefined && isKnownAgentEntry(entry, target.endpoint, true);
856
+ const removeBody = shouldRemove ? formatAgentConfig(removeAgentEntry(read.config)) : null;
713
857
  return {
714
858
  status,
715
859
  body: removeBody,
@@ -717,7 +861,7 @@ function buildOpenCodePlan(flags: OnboardFlags): OpenCodeConfigPlan {
717
861
  shouldRemove,
718
862
  blockedMessage: shouldRemove
719
863
  ? null
720
- : "OpenCode mcp.agent-inspector is missing or custom; preserved",
864
+ : `${target.label} ${target.configObjectName} is missing or custom; preserved`,
721
865
  };
722
866
  }
723
867
 
@@ -726,7 +870,7 @@ function buildOpenCodePlan(flags: OnboardFlags): OpenCodeConfigPlan {
726
870
  return {
727
871
  status,
728
872
  body: nextBody,
729
- shouldWrite: flags.force,
873
+ shouldWrite: force,
730
874
  shouldRemove: false,
731
875
  blockedMessage: null,
732
876
  };
@@ -743,11 +887,11 @@ function buildOpenCodePlan(flags: OnboardFlags): OpenCodeConfigPlan {
743
887
  return {
744
888
  status,
745
889
  body: nextBody,
746
- shouldWrite: flags.force,
890
+ shouldWrite: force,
747
891
  shouldRemove: false,
748
- blockedMessage: flags.force
892
+ blockedMessage: force
749
893
  ? null
750
- : "OpenCode mcp.agent-inspector is custom or disabled; use --force to replace",
894
+ : `${target.label} ${target.configObjectName} is custom or disabled; use --force to replace`,
751
895
  };
752
896
  case "invalid":
753
897
  return {
@@ -755,7 +899,7 @@ function buildOpenCodePlan(flags: OnboardFlags): OpenCodeConfigPlan {
755
899
  body: null,
756
900
  shouldWrite: false,
757
901
  shouldRemove: false,
758
- blockedMessage: "OpenCode config is invalid",
902
+ blockedMessage: `${target.label} config is invalid`,
759
903
  };
760
904
  default:
761
905
  return {
@@ -763,7 +907,7 @@ function buildOpenCodePlan(flags: OnboardFlags): OpenCodeConfigPlan {
763
907
  body: null,
764
908
  shouldWrite: false,
765
909
  shouldRemove: false,
766
- blockedMessage: "OpenCode config state is unknown",
910
+ blockedMessage: `${target.label} config state is unknown`,
767
911
  };
768
912
  }
769
913
  }
@@ -973,8 +1117,8 @@ function buildPlannedFiles(
973
1117
  targets: OnboardTargets,
974
1118
  version: string,
975
1119
  ): PlannedFile[] {
976
- const installClaude = !flags.codexOnly && !flags.opencodeOnly;
977
- const installCodex = !flags.skipCodexSkill && !flags.opencodeOnly;
1120
+ const installClaude = !flags.codexOnly && !hasOnlyAgentMcpTarget(flags);
1121
+ const installCodex = !flags.skipCodexSkill && !hasOnlyAgentMcpTarget(flags);
978
1122
  const detectedSummary = installClaude ? buildDetectedSummary() : "";
979
1123
  const claudeSkillBody = installClaude
980
1124
  ? renderSkillOnboard({
@@ -1050,7 +1194,7 @@ function runUninstall(
1050
1194
  plannedFiles: readonly PlannedFile[],
1051
1195
  currentVersion: string,
1052
1196
  dryRun: boolean,
1053
- openCodePlan: OpenCodeConfigPlan | null,
1197
+ agentPlans: readonly AgentMcpConfigPlan[],
1054
1198
  ): number {
1055
1199
  const enabledFiles = plannedFiles.filter((file) => file.enabled);
1056
1200
  const filesToRemove = enabledFiles.filter((file) => shouldUninstall(file, currentVersion));
@@ -1061,26 +1205,26 @@ function runUninstall(
1061
1205
  const status = shouldUninstall(file, currentVersion) ? "matching" : "skipped";
1062
1206
  process.stdout.write(`${file.label}: ${file.path} (${status})\n`);
1063
1207
  }
1064
- if (openCodePlan !== null) {
1065
- const status = openCodePlan.shouldRemove ? "matching" : "skipped";
1066
- process.stdout.write(
1067
- `${openCodePlan.status.label}: ${openCodePlan.status.path} (${status})\n`,
1068
- );
1069
- if (openCodePlan.blockedMessage !== null) {
1070
- process.stdout.write(` ${openCodePlan.blockedMessage}\n`);
1208
+ for (const plan of agentPlans) {
1209
+ const status = plan.shouldRemove ? "matching" : "skipped";
1210
+ process.stdout.write(`${plan.status.label}: ${plan.status.path} (${status})\n`);
1211
+ if (plan.blockedMessage !== null) {
1212
+ process.stdout.write(` ${plan.blockedMessage}\n`);
1071
1213
  }
1072
1214
  }
1073
1215
  process.stdout.write(`\nNo files were removed.\n`);
1074
1216
  return 0;
1075
1217
  }
1076
1218
 
1077
- const removeOpenCode = openCodePlan !== null && openCodePlan.shouldRemove;
1078
- if (filesToRemove.length === 0 && !removeOpenCode) {
1219
+ const hasAgentConfigToRemove = agentPlans.some((plan) => plan.shouldRemove);
1220
+ if (filesToRemove.length === 0 && !hasAgentConfigToRemove) {
1079
1221
  process.stdout.write(
1080
1222
  "agent-inspector onboard: no generated onboarding files match this package version\n",
1081
1223
  );
1082
- if (openCodePlan !== null && openCodePlan.blockedMessage !== null) {
1083
- process.stdout.write(`${openCodePlan.blockedMessage}\n`);
1224
+ for (const plan of agentPlans) {
1225
+ if (plan.blockedMessage !== null) {
1226
+ process.stdout.write(`${plan.blockedMessage}\n`);
1227
+ }
1084
1228
  }
1085
1229
  return 0;
1086
1230
  }
@@ -1091,10 +1235,12 @@ function runUninstall(
1091
1235
  removeEmptyDirectory(dirname(file.path));
1092
1236
  process.stdout.write(`Removed ${file.label}: ${file.path}\n`);
1093
1237
  }
1094
- if (openCodePlan !== null && openCodePlan.shouldRemove && openCodePlan.body !== null) {
1095
- mkdirSync(dirname(openCodePlan.status.path), { recursive: true });
1096
- writeFileSync(openCodePlan.status.path, openCodePlan.body, "utf8");
1097
- process.stdout.write(`Removed OpenCode MCP entry from: ${openCodePlan.status.path}\n`);
1238
+ for (const plan of agentPlans) {
1239
+ if (plan.shouldRemove && plan.body !== null) {
1240
+ mkdirSync(dirname(plan.status.path), { recursive: true });
1241
+ writeFileSync(plan.status.path, plan.body, "utf8");
1242
+ process.stdout.write(`Removed ${plan.status.label} entry from: ${plan.status.path}\n`);
1243
+ }
1098
1244
  }
1099
1245
  } catch (err) {
1100
1246
  const msg = err instanceof Error ? err.message : String(err);
@@ -1111,26 +1257,29 @@ function formatStatusVersion(status: GeneratedFileStatus): string {
1111
1257
 
1112
1258
  function summarizeStatusAction(
1113
1259
  statuses: readonly GeneratedFileStatus[],
1114
- openCodeStatus: OpenCodeConfigStatus | null,
1260
+ agentStatuses: readonly AgentMcpConfigStatus[],
1115
1261
  ): string {
1116
1262
  const actionable = statuses.filter(
1117
1263
  (status) => status.state === "missing" || status.state === "outdated",
1118
1264
  );
1119
1265
  const actions = actionable.map((status) => status.action);
1120
- if (openCodeStatus !== null && openCodeStatus.state === "missing") {
1121
- actions.push(openCodeStatus.action);
1266
+ for (const agentStatus of agentStatuses) {
1267
+ if (agentStatus.state === "missing") {
1268
+ actions.push(agentStatus.action);
1269
+ }
1122
1270
  }
1123
1271
  if (actions.length > 0) {
1124
1272
  const uniqueActions = Array.from(new Set(actions));
1125
1273
  return `Run: ${uniqueActions.join(" && ")}`;
1126
1274
  }
1127
- if (
1128
- openCodeStatus !== null &&
1129
- (openCodeStatus.state === "custom" ||
1130
- openCodeStatus.state === "disabled" ||
1131
- openCodeStatus.state === "invalid")
1132
- ) {
1133
- return `OpenCode config needs review: ${openCodeStatus.action}`;
1275
+ for (const agentStatus of agentStatuses) {
1276
+ if (
1277
+ agentStatus.state === "custom" ||
1278
+ agentStatus.state === "disabled" ||
1279
+ agentStatus.state === "invalid"
1280
+ ) {
1281
+ return `${agentStatus.label} needs review: ${agentStatus.action}`;
1282
+ }
1134
1283
  }
1135
1284
  if (statuses.some((status) => status.state === "custom")) {
1136
1285
  return "Custom files are preserved. Use --force only if you want to replace them.";
@@ -1145,11 +1294,15 @@ function runStatus(
1145
1294
  plannedFiles: readonly PlannedFile[],
1146
1295
  currentVersion: string,
1147
1296
  json: boolean,
1148
- openCodeStatus: OpenCodeConfigStatus | null,
1297
+ agentStatuses: readonly AgentMcpConfigStatus[],
1149
1298
  ): number {
1150
1299
  const enabledFiles = plannedFiles.filter((file) => file.enabled);
1151
1300
  const statuses = enabledFiles.map((file) => readGeneratedFileStatus(file, currentVersion));
1152
- const summary = summarizeStatusAction(statuses, openCodeStatus);
1301
+ const summary = summarizeStatusAction(statuses, agentStatuses);
1302
+ const opencodeConfig =
1303
+ agentStatuses.find((status) => status.label === "OpenCode MCP config") ?? null;
1304
+ const mimoConfig =
1305
+ agentStatuses.find((status) => status.label === "MiMo Code MCP config") ?? null;
1153
1306
 
1154
1307
  if (json) {
1155
1308
  process.stdout.write(
@@ -1157,7 +1310,8 @@ function runStatus(
1157
1310
  {
1158
1311
  packageVersion: currentVersion,
1159
1312
  files: statuses,
1160
- opencodeConfig: openCodeStatus,
1313
+ opencodeConfig,
1314
+ mimoConfig,
1161
1315
  suggestedAction: summary,
1162
1316
  },
1163
1317
  null,
@@ -1177,13 +1331,13 @@ function runStatus(
1177
1331
  process.stdout.write(` Action: ${status.action}\n`);
1178
1332
  }
1179
1333
 
1180
- if (openCodeStatus !== null) {
1181
- process.stdout.write(`${openCodeStatus.label}: ${openCodeStatus.state}`);
1182
- process.stdout.write(`, transport ${openCodeStatus.transport}\n`);
1183
- process.stdout.write(` ${openCodeStatus.path}\n`);
1184
- process.stdout.write(` Endpoint: ${openCodeStatus.endpoint}\n`);
1185
- process.stdout.write(` Detail: ${openCodeStatus.detail}\n`);
1186
- process.stdout.write(` Action: ${openCodeStatus.action}\n`);
1334
+ for (const agentStatus of agentStatuses) {
1335
+ process.stdout.write(`${agentStatus.label}: ${agentStatus.state}`);
1336
+ process.stdout.write(`, transport ${agentStatus.transport}\n`);
1337
+ process.stdout.write(` ${agentStatus.path}\n`);
1338
+ process.stdout.write(` Endpoint: ${agentStatus.endpoint}\n`);
1339
+ process.stdout.write(` Detail: ${agentStatus.detail}\n`);
1340
+ process.stdout.write(` Action: ${agentStatus.action}\n`);
1187
1341
  }
1188
1342
 
1189
1343
  process.stdout.write(`\nSuggested action: ${summary}\n`);
@@ -1208,25 +1362,31 @@ function runOnboardSync(argv: readonly string[]): number {
1208
1362
  const version = readPackageVersion();
1209
1363
  const plannedFiles = buildPlannedFiles(flags, targets, version);
1210
1364
  const enabledFiles = plannedFiles.filter((file) => file.enabled);
1211
- const openCodePlan = isOpenCodeEnabled(flags) ? buildOpenCodePlan(flags) : null;
1212
- const openCodeStatus = openCodePlan === null ? null : openCodePlan.status;
1365
+ const agentPlans: AgentMcpConfigPlan[] = [];
1366
+ if (isOpenCodeEnabled(flags)) {
1367
+ agentPlans.push(buildAgentPlan(openCodeTarget(flags), flags.uninstall, flags.force));
1368
+ }
1369
+ if (isMiMoEnabled(flags)) {
1370
+ agentPlans.push(buildAgentPlan(miMoTarget(flags), flags.uninstall, flags.force));
1371
+ }
1372
+ const agentStatuses = agentPlans.map((plan) => plan.status);
1213
1373
 
1214
1374
  if (flags.json && !flags.status) {
1215
1375
  process.stderr.write("agent-inspector onboard: --json is only supported with --status\n");
1216
1376
  return 2;
1217
1377
  }
1218
1378
 
1219
- if (enabledFiles.length === 0 && openCodePlan === null) {
1379
+ if (enabledFiles.length === 0 && agentPlans.length === 0) {
1220
1380
  process.stderr.write("agent-inspector onboard: no onboarding target selected\n");
1221
1381
  return 2;
1222
1382
  }
1223
1383
 
1224
1384
  if (flags.status) {
1225
- return runStatus(plannedFiles, version, flags.json, openCodeStatus);
1385
+ return runStatus(plannedFiles, version, flags.json, agentStatuses);
1226
1386
  }
1227
1387
 
1228
1388
  if (flags.uninstall) {
1229
- return runUninstall(plannedFiles, version, flags.dryRun, openCodePlan);
1389
+ return runUninstall(plannedFiles, version, flags.dryRun, agentPlans);
1230
1390
  }
1231
1391
 
1232
1392
  if (flags.dryRun) {
@@ -1235,19 +1395,17 @@ function runOnboardSync(argv: readonly string[]): number {
1235
1395
  const status = existsSync(file.path) ? "exists" : "missing";
1236
1396
  process.stdout.write(`${file.label}: ${file.path} (${status})\n`);
1237
1397
  }
1238
- if (openCodePlan !== null) {
1239
- process.stdout.write(
1240
- `${openCodePlan.status.label}: ${openCodePlan.status.path} (${openCodePlan.status.state})\n`,
1241
- );
1242
- process.stdout.write(` Transport: ${flags.opencodeTransport}\n`);
1243
- process.stdout.write(` Endpoint: ${openCodePlan.status.endpoint}\n`);
1244
- process.stdout.write(` Action: ${openCodePlan.status.action}\n`);
1245
- if (openCodePlan.blockedMessage !== null) {
1246
- process.stdout.write(` ${openCodePlan.blockedMessage}\n`);
1398
+ for (const plan of agentPlans) {
1399
+ process.stdout.write(`${plan.status.label}: ${plan.status.path} (${plan.status.state})\n`);
1400
+ process.stdout.write(` Transport: ${plan.status.transport}\n`);
1401
+ process.stdout.write(` Endpoint: ${plan.status.endpoint}\n`);
1402
+ process.stdout.write(` Action: ${plan.status.action}\n`);
1403
+ if (plan.blockedMessage !== null) {
1404
+ process.stdout.write(` ${plan.blockedMessage}\n`);
1247
1405
  }
1248
1406
  }
1249
1407
  process.stdout.write(`\nClaude skill headings:\n`);
1250
- if (!flags.codexOnly && !flags.opencodeOnly) {
1408
+ if (!flags.codexOnly && !hasOnlyAgentMcpTarget(flags)) {
1251
1409
  for (const heading of REQUIRED_PHASE_HEADINGS) {
1252
1410
  process.stdout.write(` - ${heading}\n`);
1253
1411
  }
@@ -1255,14 +1413,14 @@ function runOnboardSync(argv: readonly string[]): number {
1255
1413
  process.stdout.write(` (disabled by selected targets)\n`);
1256
1414
  }
1257
1415
  process.stdout.write(`\nCodex skill headings:\n`);
1258
- if (!flags.skipCodexSkill && !flags.opencodeOnly) {
1416
+ if (!flags.skipCodexSkill && !hasOnlyAgentMcpTarget(flags)) {
1259
1417
  for (const heading of REQUIRED_CODEX_PHASE_HEADINGS) {
1260
1418
  process.stdout.write(` - ${heading}\n`);
1261
1419
  }
1262
1420
  } else {
1263
1421
  process.stdout.write(` (disabled by selected targets)\n`);
1264
1422
  }
1265
- if (!flags.codexOnly && !flags.opencodeOnly) {
1423
+ if (!flags.codexOnly && !hasOnlyAgentMcpTarget(flags)) {
1266
1424
  process.stdout.write(`\nDetected tools:\n${buildDetectedSummary()}\n`);
1267
1425
  }
1268
1426
  process.stdout.write(`\nNo files were written.\n`);
@@ -1270,18 +1428,21 @@ function runOnboardSync(argv: readonly string[]): number {
1270
1428
  }
1271
1429
 
1272
1430
  const filesToWrite = plannedFiles.filter((file) => shouldWrite(file, flags.force, version));
1273
- const shouldWriteOpenCode =
1274
- openCodePlan !== null && openCodePlan.shouldWrite && openCodePlan.body !== null;
1275
- if (openCodePlan !== null && openCodePlan.status.state === "invalid") {
1276
- process.stderr.write(`agent-inspector onboard: ${openCodePlan.blockedMessage ?? "invalid"}\n`);
1277
- return 1;
1431
+ const agentPlansToWrite = agentPlans.filter((plan) => plan.shouldWrite && plan.body !== null);
1432
+ for (const plan of agentPlans) {
1433
+ if (plan.status.state === "invalid") {
1434
+ process.stderr.write(`agent-inspector onboard: ${plan.blockedMessage ?? "invalid"}\n`);
1435
+ return 1;
1436
+ }
1278
1437
  }
1279
- if (filesToWrite.length === 0 && !shouldWriteOpenCode) {
1438
+ if (filesToWrite.length === 0 && agentPlansToWrite.length === 0) {
1280
1439
  process.stdout.write(
1281
1440
  "agent-inspector onboard: all selected onboarding files are already up to date\n",
1282
1441
  );
1283
- if (openCodePlan !== null && openCodePlan.blockedMessage !== null) {
1284
- process.stdout.write(`${openCodePlan.blockedMessage}\n`);
1442
+ for (const plan of agentPlans) {
1443
+ if (plan.blockedMessage !== null) {
1444
+ process.stdout.write(`${plan.blockedMessage}\n`);
1445
+ }
1285
1446
  }
1286
1447
  process.stdout.write("Re-run with --force to refresh.\n");
1287
1448
  return 0;
@@ -1292,9 +1453,11 @@ function runOnboardSync(argv: readonly string[]): number {
1292
1453
  mkdirSync(dirname(file.path), { recursive: true });
1293
1454
  writeFileSync(file.path, file.body, "utf8");
1294
1455
  }
1295
- if (shouldWriteOpenCode && openCodePlan !== null && openCodePlan.body !== null) {
1296
- mkdirSync(dirname(openCodePlan.status.path), { recursive: true });
1297
- writeFileSync(openCodePlan.status.path, openCodePlan.body, "utf8");
1456
+ for (const plan of agentPlansToWrite) {
1457
+ if (plan.body !== null) {
1458
+ mkdirSync(dirname(plan.status.path), { recursive: true });
1459
+ writeFileSync(plan.status.path, plan.body, "utf8");
1460
+ }
1298
1461
  }
1299
1462
  } catch (err) {
1300
1463
  const msg = err instanceof Error ? err.message : String(err);
@@ -1308,23 +1471,27 @@ function runOnboardSync(argv: readonly string[]): number {
1308
1471
  for (const file of filesToWrite) {
1309
1472
  process.stdout.write(`Installed/updated ${file.label} to: ${file.path}\n`);
1310
1473
  }
1311
- if (shouldWriteOpenCode && openCodePlan !== null) {
1312
- process.stdout.write(`Installed/updated OpenCode MCP config: ${openCodePlan.status.path}\n`);
1474
+ for (const plan of agentPlansToWrite) {
1475
+ process.stdout.write(`Installed/updated ${plan.status.label}: ${plan.status.path}\n`);
1313
1476
  }
1314
1477
  process.stdout.write(`\nNext steps:\n`);
1315
- if (!flags.codexOnly && !flags.opencodeOnly) {
1478
+ if (!flags.codexOnly && !hasOnlyAgentMcpTarget(flags)) {
1316
1479
  process.stdout.write(` - Open Claude Code and run: /agent-inspector:onboard\n`);
1317
1480
  }
1318
- if (!flags.skipCodexSkill && !flags.opencodeOnly) {
1481
+ if (!flags.skipCodexSkill && !hasOnlyAgentMcpTarget(flags)) {
1319
1482
  process.stdout.write(` - Open Codex and ask to use the agent-inspector-onboard skill\n`);
1320
1483
  }
1321
- if (openCodePlan !== null) {
1322
- process.stdout.write(` - Verify OpenCode MCP: opencode mcp list\n`);
1323
- }
1324
- if (flags.opencodeOnly) {
1325
- process.stdout.write(
1326
- ` - Refresh OpenCode later: agent-inspector onboard --opencode-only --force\n`,
1327
- );
1484
+ for (const plan of agentPlans) {
1485
+ process.stdout.write(` - Verify ${plan.status.label}: ${plan.status.verifyCommand}\n`);
1486
+ }
1487
+ if (hasOnlyAgentMcpTarget(flags)) {
1488
+ const refreshActions = agentPlans.map((plan) => plan.status.forceAction);
1489
+ const uniqueRefreshActions = Array.from(new Set(refreshActions));
1490
+ const refresh =
1491
+ uniqueRefreshActions.length > 0
1492
+ ? uniqueRefreshActions.join(" && ")
1493
+ : "agent-inspector onboard --force";
1494
+ process.stdout.write(` - Refresh selected MCP config later: ${refresh}\n`);
1328
1495
  } else {
1329
1496
  process.stdout.write(` - Refresh later: agent-inspector onboard --force\n`);
1330
1497
  process.stdout.write(` - Detected primary tool: ${toolHint}\n`);