@tonyclaw/agent-inspector 2.0.15 → 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.
- package/.output/cli.js +399 -28
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-B6WCFRJq.js → CompareDrawer-DDg7Cs4N.js} +1 -1
- package/.output/public/assets/{ProxyViewerContainer-CofGRJoO.js → ProxyViewerContainer-DbbK1y7N.js} +24 -24
- package/.output/public/assets/{ReplayDialog-O_53zkX8.js → ReplayDialog-C1zWNkoz.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-CqBSMZv5.js → RequestAnatomy-DR8OICOJ.js} +1 -1
- package/.output/public/assets/{ResponseView-BvbA6zot.js → ResponseView-Dh9iSj6h.js} +1 -1
- package/.output/public/assets/{StreamingChunkSequence-Bnf6djjc.js → StreamingChunkSequence-B_xElyH3.js} +1 -1
- package/.output/public/assets/_sessionId-gv1a_NNu.js +1 -0
- package/.output/public/assets/index-u-LwDaeH.js +1 -0
- package/.output/public/assets/{main-Biir9ZGe.js → main-bwZlEXw2.js} +2 -2
- package/.output/server/{_sessionId-B77Gh4IL.mjs → _sessionId-BEuJJhqT.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-D4-RHFqW.mjs → CompareDrawer-yTO93GMz.mjs} +2 -2
- package/.output/server/_ssr/{ProxyViewerContainer-YP28-_2H.mjs → ProxyViewerContainer-C8qCkHGB.mjs} +72 -22
- package/.output/server/_ssr/{ReplayDialog-D9rQlrcY.mjs → ReplayDialog-DRF9PG7Z.mjs} +3 -3
- package/.output/server/_ssr/{RequestAnatomy-BXCRWD9H.mjs → RequestAnatomy-DBcuV_jV.mjs} +2 -2
- package/.output/server/_ssr/{ResponseView-DEwWMqSS.mjs → ResponseView-JJm78HZT.mjs} +2 -2
- package/.output/server/_ssr/{StreamingChunkSequence-CdrTPf5v.mjs → StreamingChunkSequence-DyGKjBYx.mjs} +2 -2
- package/.output/server/_ssr/{index-Chf1DYqT.mjs → index-T7JG28t6.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-BHXU5jWm.mjs → router-BJob8RN3.mjs} +2 -2
- package/.output/server/{_tanstack-start-manifest_v-D1dtacWy.mjs → _tanstack-start-manifest_v-CuiFi3XC.mjs} +1 -1
- package/.output/server/index.mjs +57 -57
- package/README.md +22 -5
- package/package.json +3 -3
- package/scripts/setup-agent-skills.mjs +64 -0
- package/src/cli/onboard.ts +407 -13
- package/src/cli/templates/codex-skill-onboard.ts +13 -4
- package/src/cli/templates/command-onboard.ts +12 -4
- package/src/cli/templates/skill-onboard.ts +30 -8
- package/src/components/OnboardingBanner.tsx +35 -7
- package/src/components/providers/SettingsDialog.tsx +22 -4
- package/src/lib/useOnboarding.ts +9 -1
- package/.output/public/assets/_sessionId-9ICCw6YA.js +0 -1
- package/.output/public/assets/index-DVkMYH8H.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) {
|
|
@@ -47,6 +47,8 @@ Codex user-level configuration lives in \`~/.codex/config.toml\`. Codex supports
|
|
|
47
47
|
|
|
48
48
|
Keep this setup local and explicit. Agent Inspector MCP tools can read captured requests and provider configuration, so only wire it into trusted local Codex environments.
|
|
49
49
|
|
|
50
|
+
Agent Inspector starts in **Simple mode** by default. Keep Simple mode for everyday capture: requests, responses, tools, timing, and token usage stay available without retaining heavier raw headers, raw response, or detailed SSE chunk artifacts. Use **Full mode** only when the user is debugging provider headers, raw response payloads, or SSE stream details.
|
|
51
|
+
|
|
50
52
|
---
|
|
51
53
|
|
|
52
54
|
## Phase 0: Preflight
|
|
@@ -55,12 +57,14 @@ Check that the package and Codex home exist without printing secrets.
|
|
|
55
57
|
|
|
56
58
|
\`\`\`powershell
|
|
57
59
|
Get-Command agent-inspector -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source
|
|
60
|
+
agent-inspector onboard --status
|
|
58
61
|
Test-Path (Join-Path $env:USERPROFILE ".codex")
|
|
59
62
|
Test-Path (Join-Path $env:USERPROFILE ".codex\\config.toml")
|
|
60
63
|
\`\`\`
|
|
61
64
|
|
|
62
65
|
\`\`\`bash
|
|
63
66
|
command -v agent-inspector || true
|
|
67
|
+
agent-inspector onboard --status
|
|
64
68
|
test -d "$HOME/.codex" && echo "codex home: present" || echo "codex home: missing"
|
|
65
69
|
test -f "$HOME/.codex/config.toml" && echo "config: present" || echo "config: missing"
|
|
66
70
|
\`\`\`
|
|
@@ -71,25 +75,30 @@ If \`agent-inspector\` is missing, ask the user to install it first:
|
|
|
71
75
|
npm install -g @tonyclaw/agent-inspector
|
|
72
76
|
\`\`\`
|
|
73
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
|
+
|
|
74
81
|
Do not read \`~/.codex/auth.json\`, token files, or unrelated Codex state files.
|
|
75
82
|
|
|
76
83
|
---
|
|
77
84
|
|
|
78
85
|
## Phase 1: Start Agent Inspector
|
|
79
86
|
|
|
80
|
-
Start or reuse Agent Inspector before wiring MCP. Prefer the installed binary so Windows users get the branded runtime in Task Manager.
|
|
87
|
+
Start or reuse Agent Inspector before wiring MCP. Prefer the installed binary so Windows users get the branded runtime in Task Manager. Start onboarding in Simple mode; if the user needs raw headers, raw response, or SSE chunks later, restart with \`agent-inspector --mode full\`.
|
|
81
88
|
|
|
82
89
|
\`\`\`powershell
|
|
83
|
-
agent-inspector --background --no-open
|
|
90
|
+
agent-inspector --mode simple --background --no-open
|
|
84
91
|
Invoke-RestMethod -Uri "http://localhost:${port}/api/health" -TimeoutSec 3
|
|
92
|
+
Invoke-RestMethod -Uri "http://localhost:${port}/api/config" -TimeoutSec 3 | Select-Object captureMode
|
|
85
93
|
\`\`\`
|
|
86
94
|
|
|
87
95
|
\`\`\`bash
|
|
88
|
-
agent-inspector --background --no-open
|
|
96
|
+
agent-inspector --mode simple --background --no-open
|
|
89
97
|
curl -fsS "http://localhost:${port}/api/health"
|
|
98
|
+
curl -fsS "http://localhost:${port}/api/config" | grep -o '"captureMode":"[^"]*"'
|
|
90
99
|
\`\`\`
|
|
91
100
|
|
|
92
|
-
If the health check fails, show only the relevant error and ask whether to diagnose startup before editing Codex config.
|
|
101
|
+
If the health check fails, show only the relevant error and ask whether to diagnose startup before editing Codex config. If the active capture mode is not \`simple\`, explain that an existing process is already running and offer to restart with \`agent-inspector --mode simple --force-restart\`.
|
|
93
102
|
|
|
94
103
|
---
|
|
95
104
|
|
|
@@ -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
|
}
|