@tonyclaw/agent-inspector 2.0.16 → 2.0.18
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/.output/cli.js +394 -55
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-DPs4UHwd.js → CompareDrawer-CepU-dLW.js} +1 -1
- package/.output/public/assets/{ProxyViewerContainer-B1KylMPs.js → ProxyViewerContainer-BG7rc33d.js} +16 -16
- package/.output/public/assets/{ReplayDialog-8ISC28XF.js → ReplayDialog-DK6SPv8X.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-BhbsgfBO.js → RequestAnatomy-C_C6ZU3T.js} +1 -1
- package/.output/public/assets/{ResponseView-CyM7JXeA.js → ResponseView-Bqpra4Zw.js} +1 -1
- package/.output/public/assets/{StreamingChunkSequence-BaX0x38n.js → StreamingChunkSequence-CdPbpprq.js} +1 -1
- package/.output/public/assets/_sessionId-CsNARHj5.js +1 -0
- package/.output/public/assets/index-37NfO0zx.js +1 -0
- package/.output/public/assets/{main-B2t-3SYj.js → main-BRXsrxtv.js} +2 -2
- package/.output/server/{_sessionId-CEbZTs68.mjs → _sessionId-B8Bbfs-v.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-CEo48Wsu.mjs → CompareDrawer-TgGZLf1J.mjs} +2 -2
- package/.output/server/_ssr/{ProxyViewerContainer-5ekIK_jx.mjs → ProxyViewerContainer-B_ppaNmQ.mjs} +41 -12
- package/.output/server/_ssr/{ReplayDialog-C0nEvbYo.mjs → ReplayDialog-BXlajnMC.mjs} +3 -3
- package/.output/server/_ssr/{RequestAnatomy-CT55prmM.mjs → RequestAnatomy-CCrx09-i.mjs} +2 -2
- package/.output/server/_ssr/{ResponseView-DBZSBCOD.mjs → ResponseView-BxtMJrov.mjs} +2 -2
- package/.output/server/_ssr/{StreamingChunkSequence-BuVT7Oke.mjs → StreamingChunkSequence-BlpdSPb8.mjs} +2 -2
- package/.output/server/_ssr/{index-Cw_zJVQv.mjs → index-BeTMNSJy.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-DxjqHipT.mjs → router-Ddl1tuU7.mjs} +27 -11
- package/.output/server/{_tanstack-start-manifest_v-xeOo5gW5.mjs → _tanstack-start-manifest_v-Bvdz_U0f.mjs} +1 -1
- package/.output/server/index.mjs +56 -56
- package/README.md +37 -5
- package/package.json +3 -3
- package/scripts/setup-agent-skills.mjs +64 -0
- package/src/cli/doctor.ts +12 -5
- package/src/cli/onboard.ts +407 -13
- package/src/cli/templates/codex-skill-onboard.ts +5 -0
- package/src/cli/templates/command-onboard.ts +12 -4
- package/src/cli/templates/skill-onboard.ts +17 -21
- package/src/components/OnboardingBanner.tsx +5 -0
- package/src/components/providers/SettingsDialog.tsx +22 -4
- package/src/proxy/dataDir.ts +35 -13
- package/.output/public/assets/_sessionId-CShO0OrR.js +0 -1
- package/.output/public/assets/index-BkdYiTpV.js +0 -1
- package/scripts/setup-codex-skill.mjs +0 -38
package/src/cli/onboard.ts
CHANGED
|
@@ -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`
|
|
9
|
-
* `--force` overwrites, `--dry-run` writes
|
|
10
|
-
* setup step that can fail softly without
|
|
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 {
|
|
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
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
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(
|
|
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`
|
|
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
|
|
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
|
|
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
|
|
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:**
|
|
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?
|
|
@@ -89,8 +101,6 @@ agent_inspector_data_dir() {
|
|
|
89
101
|
case "$AGENT_INSPECTOR_DATA_DIR" in /*) printf '%s\n' "$AGENT_INSPECTOR_DATA_DIR" ;; *) printf '%s\n' "$base/$AGENT_INSPECTOR_DATA_DIR" ;; esac
|
|
90
102
|
elif [ -n "\${AGENT_INSPECTOR_CONFIG_DIR:-}" ]; then
|
|
91
103
|
case "$AGENT_INSPECTOR_CONFIG_DIR" in /*) printf '%s\n' "$AGENT_INSPECTOR_CONFIG_DIR" ;; *) printf '%s\n' "$base/$AGENT_INSPECTOR_CONFIG_DIR" ;; esac
|
|
92
|
-
elif { [ ! -e "$base/.agent-inspector/providers.json" ] && [ ! -e "$base/.agent-inspector/config.json" ] && [ ! -e "$base/.agent-inspector/logs" ] && [ ! -e "$base/.agent-inspector/chunks" ]; } && { [ -e "$base/.llm-inspector/providers.json" ] || [ -e "$base/.llm-inspector/config.json" ] || [ -e "$base/.llm-inspector/logs" ] || [ -e "$base/.llm-inspector/chunks" ]; }; then
|
|
93
|
-
printf '%s\n' "$base/.llm-inspector"
|
|
94
104
|
else
|
|
95
105
|
printf '%s\n' "$base/.agent-inspector"
|
|
96
106
|
fi
|
|
@@ -137,14 +147,7 @@ function Resolve-AgentInspectorDataDir {
|
|
|
137
147
|
if ([System.IO.Path]::IsPathRooted($env:AGENT_INSPECTOR_CONFIG_DIR)) { return $env:AGENT_INSPECTOR_CONFIG_DIR }
|
|
138
148
|
return (Join-Path $base $env:AGENT_INSPECTOR_CONFIG_DIR)
|
|
139
149
|
}
|
|
140
|
-
|
|
141
|
-
$legacy = Join-Path $base '.llm-inspector'
|
|
142
|
-
$currentHasState = (Test-Path (Join-Path $current 'providers.json')) -or (Test-Path (Join-Path $current 'config.json')) -or (Test-Path (Join-Path $current 'logs')) -or (Test-Path (Join-Path $current 'chunks'))
|
|
143
|
-
$legacyHasState = (Test-Path (Join-Path $legacy 'providers.json')) -or (Test-Path (Join-Path $legacy 'config.json')) -or (Test-Path (Join-Path $legacy 'logs')) -or (Test-Path (Join-Path $legacy 'chunks'))
|
|
144
|
-
if (-not $currentHasState -and $legacyHasState) {
|
|
145
|
-
return $legacy
|
|
146
|
-
}
|
|
147
|
-
return $current
|
|
150
|
+
return (Join-Path $base '.agent-inspector')
|
|
148
151
|
}
|
|
149
152
|
$dataDir = Resolve-AgentInspectorDataDir
|
|
150
153
|
$providers = Join-Path $dataDir 'providers.json'
|
|
@@ -260,8 +263,6 @@ agent_inspector_data_dir() {
|
|
|
260
263
|
case "$AGENT_INSPECTOR_DATA_DIR" in /*) printf '%s\n' "$AGENT_INSPECTOR_DATA_DIR" ;; *) printf '%s\n' "$base/$AGENT_INSPECTOR_DATA_DIR" ;; esac
|
|
261
264
|
elif [ -n "\${AGENT_INSPECTOR_CONFIG_DIR:-}" ]; then
|
|
262
265
|
case "$AGENT_INSPECTOR_CONFIG_DIR" in /*) printf '%s\n' "$AGENT_INSPECTOR_CONFIG_DIR" ;; *) printf '%s\n' "$base/$AGENT_INSPECTOR_CONFIG_DIR" ;; esac
|
|
263
|
-
elif { [ ! -e "$base/.agent-inspector/providers.json" ] && [ ! -e "$base/.agent-inspector/config.json" ] && [ ! -e "$base/.agent-inspector/logs" ] && [ ! -e "$base/.agent-inspector/chunks" ]; } && { [ -e "$base/.llm-inspector/providers.json" ] || [ -e "$base/.llm-inspector/config.json" ] || [ -e "$base/.llm-inspector/logs" ] || [ -e "$base/.llm-inspector/chunks" ]; }; then
|
|
264
|
-
printf '%s\n' "$base/.llm-inspector"
|
|
265
266
|
else
|
|
266
267
|
printf '%s\n' "$base/.agent-inspector"
|
|
267
268
|
fi
|
|
@@ -305,14 +306,7 @@ function Resolve-AgentInspectorDataDir {
|
|
|
305
306
|
if ([System.IO.Path]::IsPathRooted($env:AGENT_INSPECTOR_CONFIG_DIR)) { return $env:AGENT_INSPECTOR_CONFIG_DIR }
|
|
306
307
|
return (Join-Path $base $env:AGENT_INSPECTOR_CONFIG_DIR)
|
|
307
308
|
}
|
|
308
|
-
|
|
309
|
-
$legacy = Join-Path $base '.llm-inspector'
|
|
310
|
-
$currentHasState = (Test-Path (Join-Path $current 'providers.json')) -or (Test-Path (Join-Path $current 'config.json')) -or (Test-Path (Join-Path $current 'logs')) -or (Test-Path (Join-Path $current 'chunks'))
|
|
311
|
-
$legacyHasState = (Test-Path (Join-Path $legacy 'providers.json')) -or (Test-Path (Join-Path $legacy 'config.json')) -or (Test-Path (Join-Path $legacy 'logs')) -or (Test-Path (Join-Path $legacy 'chunks'))
|
|
312
|
-
if (-not $currentHasState -and $legacyHasState) {
|
|
313
|
-
return $legacy
|
|
314
|
-
}
|
|
315
|
-
return $current
|
|
309
|
+
return (Join-Path $base '.agent-inspector')
|
|
316
310
|
}
|
|
317
311
|
$dir = Resolve-AgentInspectorDataDir
|
|
318
312
|
$file = Join-Path $dir 'providers.json'
|
|
@@ -546,6 +540,8 @@ done
|
|
|
546
540
|
\`\`\`
|
|
547
541
|
|
|
548
542
|
- **Re-run onboard**: \`agent-inspector onboard --force\` refreshes this skill.
|
|
543
|
+
- **Check onboard files**: \`agent-inspector onboard --status\` shows whether generated skills are
|
|
544
|
+
current, outdated, missing, newer, or custom.
|
|
549
545
|
- **Full docs**: see the project README (linked from the Web UI footer).
|
|
550
546
|
|
|
551
547
|
> **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
|