@tonyclaw/agent-inspector 2.0.16 → 2.0.17

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 (35) hide show
  1. package/.output/cli.js +376 -17
  2. package/.output/nitro.json +1 -1
  3. package/.output/public/assets/{CompareDrawer-DPs4UHwd.js → CompareDrawer-DDg7Cs4N.js} +1 -1
  4. package/.output/public/assets/{ProxyViewerContainer-B1KylMPs.js → ProxyViewerContainer-DbbK1y7N.js} +16 -16
  5. package/.output/public/assets/{ReplayDialog-8ISC28XF.js → ReplayDialog-C1zWNkoz.js} +1 -1
  6. package/.output/public/assets/{RequestAnatomy-BhbsgfBO.js → RequestAnatomy-DR8OICOJ.js} +1 -1
  7. package/.output/public/assets/{ResponseView-CyM7JXeA.js → ResponseView-Dh9iSj6h.js} +1 -1
  8. package/.output/public/assets/{StreamingChunkSequence-BaX0x38n.js → StreamingChunkSequence-B_xElyH3.js} +1 -1
  9. package/.output/public/assets/_sessionId-gv1a_NNu.js +1 -0
  10. package/.output/public/assets/index-u-LwDaeH.js +1 -0
  11. package/.output/public/assets/{main-B2t-3SYj.js → main-bwZlEXw2.js} +2 -2
  12. package/.output/server/{_sessionId-CEbZTs68.mjs → _sessionId-BEuJJhqT.mjs} +2 -2
  13. package/.output/server/_ssr/{CompareDrawer-CEo48Wsu.mjs → CompareDrawer-yTO93GMz.mjs} +2 -2
  14. package/.output/server/_ssr/{ProxyViewerContainer-5ekIK_jx.mjs → ProxyViewerContainer-C8qCkHGB.mjs} +41 -12
  15. package/.output/server/_ssr/{ReplayDialog-C0nEvbYo.mjs → ReplayDialog-DRF9PG7Z.mjs} +3 -3
  16. package/.output/server/_ssr/{RequestAnatomy-CT55prmM.mjs → RequestAnatomy-DBcuV_jV.mjs} +2 -2
  17. package/.output/server/_ssr/{ResponseView-DBZSBCOD.mjs → ResponseView-JJm78HZT.mjs} +2 -2
  18. package/.output/server/_ssr/{StreamingChunkSequence-BuVT7Oke.mjs → StreamingChunkSequence-DyGKjBYx.mjs} +2 -2
  19. package/.output/server/_ssr/{index-Cw_zJVQv.mjs → index-T7JG28t6.mjs} +2 -2
  20. package/.output/server/_ssr/index.mjs +2 -2
  21. package/.output/server/_ssr/{router-DxjqHipT.mjs → router-BJob8RN3.mjs} +2 -2
  22. package/.output/server/{_tanstack-start-manifest_v-xeOo5gW5.mjs → _tanstack-start-manifest_v-CuiFi3XC.mjs} +1 -1
  23. package/.output/server/index.mjs +59 -59
  24. package/README.md +22 -5
  25. package/package.json +3 -3
  26. package/scripts/setup-agent-skills.mjs +64 -0
  27. package/src/cli/onboard.ts +407 -13
  28. package/src/cli/templates/codex-skill-onboard.ts +5 -0
  29. package/src/cli/templates/command-onboard.ts +12 -4
  30. package/src/cli/templates/skill-onboard.ts +15 -1
  31. package/src/components/OnboardingBanner.tsx +5 -0
  32. package/src/components/providers/SettingsDialog.tsx +22 -4
  33. package/.output/public/assets/_sessionId-CShO0OrR.js +0 -1
  34. package/.output/public/assets/index-BkdYiTpV.js +0 -1
  35. package/scripts/setup-codex-skill.mjs +0 -38
@@ -5,12 +5,21 @@
5
5
  * Claude Code gets a skill + slash command. Codex gets a skill under
6
6
  * ~/.codex/skills that guides the user through connecting /api/mcp.
7
7
  *
8
- * Idempotent by default: re-runs without `--force` only write missing files.
9
- * `--force` overwrites, `--dry-run` writes nothing. Designed as an explicit
10
- * setup step that can fail softly without blocking normal CLI use.
8
+ * Idempotent by default: re-runs without `--force` write missing files and
9
+ * refresh older generated files. `--force` overwrites, `--dry-run` writes
10
+ * nothing. Designed as an explicit setup step that can fail softly without
11
+ * blocking normal CLI use.
11
12
  */
12
13
 
13
- import { mkdirSync, writeFileSync, existsSync, readFileSync } from "node:fs";
14
+ import {
15
+ mkdirSync,
16
+ writeFileSync,
17
+ existsSync,
18
+ readFileSync,
19
+ unlinkSync,
20
+ readdirSync,
21
+ rmdirSync,
22
+ } from "node:fs";
14
23
  import { homedir } from "node:os";
15
24
  import { dirname, join } from "node:path";
16
25
  import { fileURLToPath } from "node:url";
@@ -37,6 +46,10 @@ const SKILL_FILE_NAME = "SKILL.md";
37
46
  const COMMAND_FILE_NAME =
38
47
  process.platform === "win32" ? "agent-inspector-onboard.md" : "agent-inspector:onboard.md";
39
48
 
49
+ const GENERATED_VERSION_PATTERN = /^\s*version:\s*([0-9]+\.[0-9]+\.[0-9]+(?:[-+][^\s]+)?)\s*$/m;
50
+ const FRONTMATTER_PATTERN = /^---\r?\n([\s\S]*?)\r?\n---/;
51
+ const SEMVER_PATTERN = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/;
52
+
40
53
  export type OnboardFlags = {
41
54
  force: boolean;
42
55
  dryRun: boolean;
@@ -44,6 +57,9 @@ export type OnboardFlags = {
44
57
  skipToolWire: boolean;
45
58
  codexOnly: boolean;
46
59
  skipCodexSkill: boolean;
60
+ uninstall: boolean;
61
+ status: boolean;
62
+ json: boolean;
47
63
  skillDir: string | null;
48
64
  codexSkillDir: string | null;
49
65
  };
@@ -61,6 +77,47 @@ type PlannedFile = {
61
77
  enabled: boolean;
62
78
  };
63
79
 
80
+ type ParsedVersion = {
81
+ major: number;
82
+ minor: number;
83
+ patch: number;
84
+ };
85
+
86
+ type GeneratedFileState = "missing" | "current" | "outdated" | "newer" | "custom";
87
+
88
+ type GeneratedFileStatus = {
89
+ label: string;
90
+ path: string;
91
+ state: GeneratedFileState;
92
+ version: string | null;
93
+ action: string;
94
+ };
95
+
96
+ function actionForStatus(label: string, state: GeneratedFileState): string {
97
+ switch (state) {
98
+ case "missing":
99
+ switch (label) {
100
+ case "Codex skill":
101
+ return "agent-inspector onboard --codex-only";
102
+ case "Claude skill":
103
+ case "Claude command":
104
+ return "agent-inspector onboard --skip-codex-skill";
105
+ default:
106
+ return "agent-inspector onboard";
107
+ }
108
+ case "outdated":
109
+ return "agent-inspector onboard --force";
110
+ case "custom":
111
+ return "preserved; use agent-inspector onboard --force to replace";
112
+ case "newer":
113
+ return "no action; installed file is newer than this package";
114
+ case "current":
115
+ return "no action";
116
+ default:
117
+ return "agent-inspector onboard";
118
+ }
119
+ }
120
+
64
121
  function parseFlags(argv: readonly string[]): OnboardFlags {
65
122
  const flags: OnboardFlags = {
66
123
  force: false,
@@ -69,6 +126,9 @@ function parseFlags(argv: readonly string[]): OnboardFlags {
69
126
  skipToolWire: false,
70
127
  codexOnly: false,
71
128
  skipCodexSkill: false,
129
+ uninstall: false,
130
+ status: false,
131
+ json: false,
72
132
  skillDir: null,
73
133
  codexSkillDir: null,
74
134
  };
@@ -95,6 +155,15 @@ function parseFlags(argv: readonly string[]): OnboardFlags {
95
155
  case "--skip-codex-skill":
96
156
  flags.skipCodexSkill = true;
97
157
  break;
158
+ case "--uninstall":
159
+ flags.uninstall = true;
160
+ break;
161
+ case "--status":
162
+ flags.status = true;
163
+ break;
164
+ case "--json":
165
+ flags.json = true;
166
+ break;
98
167
  case "--skill-dir": {
99
168
  const next = argv[i + 1];
100
169
  if (next === undefined) {
@@ -143,6 +212,9 @@ Options:
143
212
  --skip-tool-wire Skip the wire-tool phase in the Claude skill body
144
213
  --skip-codex-skill Install only the Claude Code skill and slash command
145
214
  --codex-only Install only the Codex skill
215
+ --uninstall Remove generated files whose version matches this package
216
+ --status Show installed onboarding file versions and suggested action
217
+ --json Emit machine-readable JSON with --status
146
218
  --skill-dir <path> Override the target Claude root directory (default: ~/.claude)
147
219
  --codex-skill-dir <path> Override the target Codex skills directory (default: ~/.codex/skills)
148
220
  -h, --help Show this help
@@ -181,10 +253,19 @@ function buildDetectedSummary(): string {
181
253
  }
182
254
 
183
255
  function readPackageVersion(): string {
256
+ const packageJsonPaths = [
257
+ join(__dirname, "..", "package.json"),
258
+ join(__dirname, "..", "..", "package.json"),
259
+ ];
260
+
184
261
  try {
185
- const raw: unknown = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf8"));
186
- if (isObject(raw) && typeof raw["version"] === "string") {
187
- return raw["version"];
262
+ for (const packageJsonPath of packageJsonPaths) {
263
+ if (existsSync(packageJsonPath)) {
264
+ const raw: unknown = JSON.parse(readFileSync(packageJsonPath, "utf8"));
265
+ if (isObject(raw) && typeof raw["version"] === "string") {
266
+ return raw["version"];
267
+ }
268
+ }
188
269
  }
189
270
  } catch {
190
271
  // best-effort: leave version as the fallback
@@ -192,6 +273,176 @@ function readPackageVersion(): string {
192
273
  return "0.0.0";
193
274
  }
194
275
 
276
+ function parseVersion(version: string): ParsedVersion | null {
277
+ const match = SEMVER_PATTERN.exec(version);
278
+ if (match === null) {
279
+ return null;
280
+ }
281
+
282
+ const majorText = match[1];
283
+ const minorText = match[2];
284
+ const patchText = match[3];
285
+ if (majorText === undefined || minorText === undefined || patchText === undefined) {
286
+ return null;
287
+ }
288
+
289
+ const major = Number(majorText);
290
+ const minor = Number(minorText);
291
+ const patch = Number(patchText);
292
+ if (!Number.isInteger(major) || !Number.isInteger(minor) || !Number.isInteger(patch)) {
293
+ return null;
294
+ }
295
+
296
+ return { major, minor, patch };
297
+ }
298
+
299
+ function compareVersions(left: string, right: string): number | null {
300
+ const leftVersion = parseVersion(left);
301
+ const rightVersion = parseVersion(right);
302
+ if (leftVersion === null || rightVersion === null) {
303
+ return null;
304
+ }
305
+
306
+ const leftParts = [leftVersion.major, leftVersion.minor, leftVersion.patch];
307
+ const rightParts = [rightVersion.major, rightVersion.minor, rightVersion.patch];
308
+ for (let index = 0; index < leftParts.length; index++) {
309
+ const leftPart = leftParts[index];
310
+ const rightPart = rightParts[index];
311
+ if (leftPart === undefined || rightPart === undefined) {
312
+ return null;
313
+ }
314
+ if (leftPart > rightPart) {
315
+ return 1;
316
+ }
317
+ if (leftPart < rightPart) {
318
+ return -1;
319
+ }
320
+ }
321
+
322
+ return 0;
323
+ }
324
+
325
+ function extractGeneratedVersion(body: string): string | null {
326
+ const frontmatterMatch = FRONTMATTER_PATTERN.exec(body);
327
+ if (frontmatterMatch === null) {
328
+ return null;
329
+ }
330
+
331
+ const frontmatter = frontmatterMatch[1];
332
+ if (frontmatter === undefined) {
333
+ return null;
334
+ }
335
+
336
+ const versionMatch = GENERATED_VERSION_PATTERN.exec(frontmatter);
337
+ if (versionMatch === null) {
338
+ return null;
339
+ }
340
+
341
+ return versionMatch[1] ?? null;
342
+ }
343
+
344
+ function hasAgentInspectorAuthor(body: string): boolean {
345
+ const frontmatterMatch = FRONTMATTER_PATTERN.exec(body);
346
+ if (frontmatterMatch === null) {
347
+ return false;
348
+ }
349
+
350
+ const frontmatter = frontmatterMatch[1];
351
+ if (frontmatter === undefined) {
352
+ return false;
353
+ }
354
+
355
+ return /^\s*author:\s*agent-inspector\s*$/m.test(frontmatter);
356
+ }
357
+
358
+ function readGeneratedVersion(path: string): string | null {
359
+ try {
360
+ return extractGeneratedVersion(readFileSync(path, "utf8"));
361
+ } catch {
362
+ return null;
363
+ }
364
+ }
365
+
366
+ function isMatchingGeneratedFile(path: string, currentVersion: string): boolean {
367
+ try {
368
+ const body = readFileSync(path, "utf8");
369
+ return hasAgentInspectorAuthor(body) && extractGeneratedVersion(body) === currentVersion;
370
+ } catch {
371
+ return false;
372
+ }
373
+ }
374
+
375
+ function readGeneratedFileStatus(file: PlannedFile, currentVersion: string): GeneratedFileStatus {
376
+ if (!existsSync(file.path)) {
377
+ return {
378
+ label: file.label,
379
+ path: file.path,
380
+ state: "missing",
381
+ version: null,
382
+ action: actionForStatus(file.label, "missing"),
383
+ };
384
+ }
385
+
386
+ try {
387
+ const body = readFileSync(file.path, "utf8");
388
+ const version = extractGeneratedVersion(body);
389
+ if (!hasAgentInspectorAuthor(body) || version === null) {
390
+ return {
391
+ label: file.label,
392
+ path: file.path,
393
+ state: "custom",
394
+ version,
395
+ action: actionForStatus(file.label, "custom"),
396
+ };
397
+ }
398
+
399
+ const comparison = compareVersions(version, currentVersion);
400
+ if (comparison === null) {
401
+ return {
402
+ label: file.label,
403
+ path: file.path,
404
+ state: "custom",
405
+ version,
406
+ action: actionForStatus(file.label, "custom"),
407
+ };
408
+ }
409
+ if (comparison < 0) {
410
+ return {
411
+ label: file.label,
412
+ path: file.path,
413
+ state: "outdated",
414
+ version,
415
+ action: actionForStatus(file.label, "outdated"),
416
+ };
417
+ }
418
+ if (comparison > 0) {
419
+ return {
420
+ label: file.label,
421
+ path: file.path,
422
+ state: "newer",
423
+ version,
424
+ action: actionForStatus(file.label, "newer"),
425
+ };
426
+ }
427
+
428
+ return {
429
+ label: file.label,
430
+ path: file.path,
431
+ state: "current",
432
+ version,
433
+ action: actionForStatus(file.label, "current"),
434
+ };
435
+ } catch {
436
+ return {
437
+ label: file.label,
438
+ path: file.path,
439
+ state: "custom",
440
+ version: null,
441
+ action: actionForStatus(file.label, "custom"),
442
+ };
443
+ }
444
+ }
445
+
195
446
  function buildPlannedFiles(
196
447
  flags: OnboardFlags,
197
448
  targets: OnboardTargets,
@@ -207,7 +458,7 @@ function buildPlannedFiles(
207
458
  detectedSummary,
208
459
  })
209
460
  : "";
210
- const commandBody = installClaude ? renderCommandOnboard() : "";
461
+ const commandBody = installClaude ? renderCommandOnboard({ version }) : "";
211
462
  const codexSkillBody = installCodex
212
463
  ? renderCodexSkillOnboard({
213
464
  version,
@@ -237,8 +488,136 @@ function buildPlannedFiles(
237
488
  ];
238
489
  }
239
490
 
240
- function shouldWrite(file: PlannedFile, force: boolean): boolean {
241
- return file.enabled && (force || !existsSync(file.path));
491
+ function shouldWrite(file: PlannedFile, force: boolean, currentVersion: string): boolean {
492
+ if (!file.enabled) {
493
+ return false;
494
+ }
495
+ if (force || !existsSync(file.path)) {
496
+ return true;
497
+ }
498
+
499
+ const existingVersion = readGeneratedVersion(file.path);
500
+ if (existingVersion === null) {
501
+ return false;
502
+ }
503
+
504
+ const comparison = compareVersions(existingVersion, currentVersion);
505
+ return comparison !== null && comparison < 0;
506
+ }
507
+
508
+ function shouldUninstall(file: PlannedFile, currentVersion: string): boolean {
509
+ return (
510
+ file.enabled && existsSync(file.path) && isMatchingGeneratedFile(file.path, currentVersion)
511
+ );
512
+ }
513
+
514
+ function removeEmptyDirectory(path: string): void {
515
+ try {
516
+ if (readdirSync(path).length === 0) {
517
+ rmdirSync(path);
518
+ }
519
+ } catch {
520
+ // best-effort cleanup only
521
+ }
522
+ }
523
+
524
+ function runUninstall(
525
+ plannedFiles: readonly PlannedFile[],
526
+ currentVersion: string,
527
+ dryRun: boolean,
528
+ ): number {
529
+ const enabledFiles = plannedFiles.filter((file) => file.enabled);
530
+ const filesToRemove = enabledFiles.filter((file) => shouldUninstall(file, currentVersion));
531
+
532
+ if (dryRun) {
533
+ process.stdout.write(`agent-inspector onboard --uninstall --dry-run\n\n`);
534
+ for (const file of enabledFiles) {
535
+ const status = shouldUninstall(file, currentVersion) ? "matching" : "skipped";
536
+ process.stdout.write(`${file.label}: ${file.path} (${status})\n`);
537
+ }
538
+ process.stdout.write(`\nNo files were removed.\n`);
539
+ return 0;
540
+ }
541
+
542
+ if (filesToRemove.length === 0) {
543
+ process.stdout.write(
544
+ "agent-inspector onboard: no generated onboarding files match this package version\n",
545
+ );
546
+ return 0;
547
+ }
548
+
549
+ try {
550
+ for (const file of filesToRemove) {
551
+ unlinkSync(file.path);
552
+ removeEmptyDirectory(dirname(file.path));
553
+ process.stdout.write(`Removed ${file.label}: ${file.path}\n`);
554
+ }
555
+ } catch (err) {
556
+ const msg = err instanceof Error ? err.message : String(err);
557
+ process.stderr.write(`agent-inspector onboard: failed to remove files: ${msg}\n`);
558
+ return 1;
559
+ }
560
+
561
+ return 0;
562
+ }
563
+
564
+ function formatStatusVersion(status: GeneratedFileStatus): string {
565
+ return status.version ?? "-";
566
+ }
567
+
568
+ function summarizeStatusAction(statuses: readonly GeneratedFileStatus[]): string {
569
+ const actionable = statuses.filter(
570
+ (status) => status.state === "missing" || status.state === "outdated",
571
+ );
572
+ if (actionable.length > 0) {
573
+ const actions = Array.from(new Set(actionable.map((status) => status.action)));
574
+ return `Run: ${actions.join(" && ")}`;
575
+ }
576
+ if (statuses.some((status) => status.state === "custom")) {
577
+ return "Custom files are preserved. Use --force only if you want to replace them.";
578
+ }
579
+ if (statuses.some((status) => status.state === "newer")) {
580
+ return "Installed files are newer than this package. No action needed.";
581
+ }
582
+ return "All selected onboarding files are current.";
583
+ }
584
+
585
+ function runStatus(
586
+ plannedFiles: readonly PlannedFile[],
587
+ currentVersion: string,
588
+ json: boolean,
589
+ ): number {
590
+ const enabledFiles = plannedFiles.filter((file) => file.enabled);
591
+ const statuses = enabledFiles.map((file) => readGeneratedFileStatus(file, currentVersion));
592
+ const summary = summarizeStatusAction(statuses);
593
+
594
+ if (json) {
595
+ process.stdout.write(
596
+ `${JSON.stringify(
597
+ {
598
+ packageVersion: currentVersion,
599
+ files: statuses,
600
+ suggestedAction: summary,
601
+ },
602
+ null,
603
+ 2,
604
+ )}\n`,
605
+ );
606
+ return 0;
607
+ }
608
+
609
+ process.stdout.write(`agent-inspector onboard status\n`);
610
+ process.stdout.write(`Package version: ${currentVersion}\n\n`);
611
+
612
+ for (const status of statuses) {
613
+ process.stdout.write(`${status.label}: ${status.state}`);
614
+ process.stdout.write(`, version ${formatStatusVersion(status)}\n`);
615
+ process.stdout.write(` ${status.path}\n`);
616
+ process.stdout.write(` Action: ${status.action}\n`);
617
+ }
618
+
619
+ process.stdout.write(`\nSuggested action: ${summary}\n`);
620
+ return 0;
242
621
  }
243
622
 
244
623
  export function runOnboard(argv: readonly string[]): Promise<number> {
@@ -260,11 +639,24 @@ function runOnboardSync(argv: readonly string[]): number {
260
639
  const plannedFiles = buildPlannedFiles(flags, targets, version);
261
640
  const enabledFiles = plannedFiles.filter((file) => file.enabled);
262
641
 
642
+ if (flags.json && !flags.status) {
643
+ process.stderr.write("agent-inspector onboard: --json is only supported with --status\n");
644
+ return 2;
645
+ }
646
+
263
647
  if (enabledFiles.length === 0) {
264
648
  process.stderr.write("agent-inspector onboard: no onboarding target selected\n");
265
649
  return 2;
266
650
  }
267
651
 
652
+ if (flags.status) {
653
+ return runStatus(plannedFiles, version, flags.json);
654
+ }
655
+
656
+ if (flags.uninstall) {
657
+ return runUninstall(plannedFiles, version, flags.dryRun);
658
+ }
659
+
268
660
  if (flags.dryRun) {
269
661
  process.stdout.write(`agent-inspector onboard --dry-run\n\n`);
270
662
  for (const file of enabledFiles) {
@@ -294,9 +686,11 @@ function runOnboardSync(argv: readonly string[]): number {
294
686
  return 0;
295
687
  }
296
688
 
297
- const filesToWrite = plannedFiles.filter((file) => shouldWrite(file, flags.force));
689
+ const filesToWrite = plannedFiles.filter((file) => shouldWrite(file, flags.force, version));
298
690
  if (filesToWrite.length === 0) {
299
- process.stdout.write("agent-inspector onboard: all selected onboarding files already exist\n");
691
+ process.stdout.write(
692
+ "agent-inspector onboard: all selected onboarding files are already up to date\n",
693
+ );
300
694
  process.stdout.write("Re-run with --force to refresh.\n");
301
695
  return 0;
302
696
  }
@@ -316,7 +710,7 @@ function runOnboardSync(argv: readonly string[]): number {
316
710
  const toolHint = firstTool !== null ? firstTool.displayName : "no known tool detected";
317
711
 
318
712
  for (const file of filesToWrite) {
319
- process.stdout.write(`Installed ${file.label} to: ${file.path}\n`);
713
+ process.stdout.write(`Installed/updated ${file.label} to: ${file.path}\n`);
320
714
  }
321
715
  process.stdout.write(`\nNext steps:\n`);
322
716
  if (!flags.codexOnly) {
@@ -57,12 +57,14 @@ Check that the package and Codex home exist without printing secrets.
57
57
 
58
58
  \`\`\`powershell
59
59
  Get-Command agent-inspector -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source
60
+ agent-inspector onboard --status
60
61
  Test-Path (Join-Path $env:USERPROFILE ".codex")
61
62
  Test-Path (Join-Path $env:USERPROFILE ".codex\\config.toml")
62
63
  \`\`\`
63
64
 
64
65
  \`\`\`bash
65
66
  command -v agent-inspector || true
67
+ agent-inspector onboard --status
66
68
  test -d "$HOME/.codex" && echo "codex home: present" || echo "codex home: missing"
67
69
  test -f "$HOME/.codex/config.toml" && echo "config: present" || echo "config: missing"
68
70
  \`\`\`
@@ -73,6 +75,9 @@ If \`agent-inspector\` is missing, ask the user to install it first:
73
75
  npm install -g @tonyclaw/agent-inspector
74
76
  \`\`\`
75
77
 
78
+ If \`agent-inspector onboard --status\` reports \`outdated\` or \`missing\`, ask the user to run
79
+ \`agent-inspector onboard --force\` and restart this skill before continuing.
80
+
76
81
  Do not read \`~/.codex/auth.json\`, token files, or unrelated Codex state files.
77
82
 
78
83
  ---
@@ -1,17 +1,25 @@
1
1
  /**
2
- * Renders `~/.claude/commands/agent-inspector:onboard.md` a thin slash
2
+ * Renders `~/.claude/commands/agent-inspector:onboard.md` - a thin slash
3
3
  * command that loads the `agent-inspector-onboard` skill. Claude Code
4
4
  * auto-discovers both the skill and the command once they're written,
5
5
  * so the command body just references the skill by name.
6
6
  */
7
7
 
8
- export function renderCommandOnboard(): string {
8
+ export type CommandOnboardContext = {
9
+ /** Package version (from `package.json`), stamped into the frontmatter. */
10
+ readonly version: string;
11
+ };
12
+
13
+ export function renderCommandOnboard(ctx: CommandOnboardContext): string {
9
14
  return `---
10
- description: Walk through agent-inspector setup start the proxy, wire your AI tool, capture your first request.
15
+ description: Walk through agent-inspector setup - start the proxy, wire your AI tool, capture your first request.
16
+ metadata:
17
+ author: agent-inspector
18
+ version: ${ctx.version}
11
19
  ---
12
20
 
13
21
  Invoke the \`agent-inspector-onboard\` skill and follow its phases.
14
22
 
15
- The user wants to set up agent-inspector. If they have specific context (e.g. "I'm on Windows", "I already added my API key"), honor it but otherwise just run the skill end-to-end and let the \`EXPLAIN / DO / PAUSE\` markers drive the conversation.
23
+ The user wants to set up agent-inspector. If they have specific context (e.g. "I'm on Windows", "I already added my API key"), honor it - but otherwise just run the skill end-to-end and let the \`EXPLAIN / DO / PAUSE\` markers drive the conversation.
16
24
  `;
17
25
  }
@@ -76,7 +76,19 @@ Default proxy port: \`${port}\` (override with \`PORT=<n> agent-inspector\` or \
76
76
 
77
77
  **EXPLAIN:** "Before we do anything, let me see what's already set up. If some of the steps are already done, we can skip them."
78
78
 
79
- **DO:** Probe the three pieces of state this skill touches. Use targeted checks do **not** read large JSON files into the conversation.
79
+ **DO:** First check whether the generated onboarding files match the installed npm package. If any
80
+ entry is \`outdated\` or \`missing\`, tell the user to run \`agent-inspector onboard --force\` before
81
+ continuing so they are not guided by stale instructions.
82
+
83
+ \`\`\`bash
84
+ agent-inspector onboard --status
85
+ \`\`\`
86
+
87
+ \`\`\`powershell
88
+ agent-inspector onboard --status
89
+ \`\`\`
90
+
91
+ **DO:** Then probe the three pieces of state this skill touches. Use targeted checks — do **not** read large JSON files into the conversation.
80
92
 
81
93
  \`\`\`bash
82
94
  # 1. Is the proxy already up?
@@ -546,6 +558,8 @@ done
546
558
  \`\`\`
547
559
 
548
560
  - **Re-run onboard**: \`agent-inspector onboard --force\` refreshes this skill.
561
+ - **Check onboard files**: \`agent-inspector onboard --status\` shows whether generated skills are
562
+ current, outdated, missing, newer, or custom.
549
563
  - **Full docs**: see the project README (linked from the Web UI footer).
550
564
 
551
565
  > **PAUSE** — use \`AskUserQuestion\` with header \`All set?\` and options \`["All set, I'm done", "Wait, I want to revisit a phase"]\`. Wait for the answer.
@@ -54,6 +54,11 @@ export function OnboardingBanner(): JSX.Element | null {
54
54
  <strong>Replay and Memory</strong>: available in both modes for provider checks and
55
55
  reviewable knowledge candidates.
56
56
  </li>
57
+ <li>
58
+ <strong>Agent setup</strong>: run <code>agent-inspector onboard --status</code>, then
59
+ use <code>/agent-inspector:onboard</code> in Claude Code or ask Codex to use{" "}
60
+ <code>agent-inspector-onboard</code>.
61
+ </li>
57
62
  </ul>
58
63
  </div>
59
64
  <button
@@ -75,7 +75,7 @@ export function SettingsDialog(): JSX.Element {
75
75
  <TabsTrigger value="onboarding">Onboarding</TabsTrigger>
76
76
  </TabsList>
77
77
 
78
- <div className="mt-4 overflow-y-auto flex-1 pr-3">
78
+ <div className="mt-4 flex-1 overflow-x-hidden overflow-y-auto pr-3">
79
79
  <TabsContent value="providers">
80
80
  <ProvidersPanel
81
81
  externalProviders={providers}
@@ -119,7 +119,7 @@ function CopyableSetupValue({
119
119
  }): JSX.Element {
120
120
  const copied = copiedId === id;
121
121
  return (
122
- <div className="rounded-md border border-border bg-muted/20 px-3 py-2">
122
+ <div className="min-w-0 rounded-md border border-border bg-muted/20 px-3 py-2">
123
123
  <div className="mb-1 text-xs font-medium text-muted-foreground">{label}</div>
124
124
  <div className="flex min-w-0 items-center gap-2">
125
125
  <code className="min-w-0 flex-1 truncate font-mono text-xs text-foreground">{value}</code>
@@ -146,10 +146,28 @@ function OnboardingSettingsTab(): JSX.Element {
146
146
  }, []);
147
147
  const values = useMemo(
148
148
  () => [
149
- { id: "skill", label: "Codex skill", value: "agent-inspector onboard --force" },
149
+ { id: "status", label: "Check onboarding", value: "agent-inspector onboard --status" },
150
+ {
151
+ id: "status-json",
152
+ label: "Check onboarding JSON",
153
+ value: "agent-inspector onboard --status --json",
154
+ },
155
+ { id: "refresh", label: "Refresh generated files", value: "agent-inspector onboard --force" },
156
+ {
157
+ id: "uninstall",
158
+ label: "Remove matching generated files",
159
+ value: "agent-inspector onboard --uninstall",
160
+ },
161
+ { id: "claude", label: "Claude Code command", value: "/agent-inspector:onboard" },
162
+ {
163
+ id: "codex",
164
+ label: "Codex prompt",
165
+ value: "Use the agent-inspector-onboard skill",
166
+ },
150
167
  { id: "mcp", label: "MCP URL", value: `${origin}/api/mcp` },
151
168
  { id: "proxy", label: "Proxy URL", value: `${origin}/proxy` },
152
169
  { id: "anthropic", label: "Anthropic base", value: `ANTHROPIC_BASE_URL=${origin}/proxy` },
170
+ { id: "openai", label: "OpenAI base", value: `OPENAI_BASE_URL=${origin}/proxy` },
153
171
  ],
154
172
  [origin],
155
173
  );
@@ -162,7 +180,7 @@ function OnboardingSettingsTab(): JSX.Element {
162
180
  }, []);
163
181
 
164
182
  return (
165
- <div className="space-y-4">
183
+ <div className="min-w-0 space-y-4">
166
184
  <div className="flex items-center gap-2">
167
185
  <Terminal className="size-4 text-muted-foreground" />
168
186
  <h3 className="text-sm font-semibold">Agent onboarding</h3>
@@ -1 +0,0 @@
1
- import{R as s,j as e}from"./main-B2t-3SYj.js";import{P as i}from"./ProxyViewerContainer-B1KylMPs.js";function t(){const{sessionId:o}=s.useParams();return e.jsx(i,{initialSessionId:o},o)}export{t as component};
@@ -1 +0,0 @@
1
- import{P as o}from"./ProxyViewerContainer-B1KylMPs.js";import"./main-B2t-3SYj.js";const r=o;export{r as component};