@tonyclaw/llm-inspector 1.18.2 → 1.19.1

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 (42) hide show
  1. package/.output/cli.js +903 -139
  2. package/.output/nitro.json +1 -1
  3. package/.output/public/assets/{CompareDrawer-C-4ypEWs.js → CompareDrawer-DtERUdIt.js} +1 -1
  4. package/.output/public/assets/ProxyViewerContainer-DfxRK7Nt.js +101 -0
  5. package/.output/public/assets/{ReplayDialog-CyBKOgba.js → ReplayDialog-VMsGnJSI.js} +1 -1
  6. package/.output/public/assets/{RequestAnatomy-C0IrVQ3q.js → RequestAnatomy-Cx_vluvK.js} +1 -1
  7. package/.output/public/assets/{ResponseView-MogToC4i.js → ResponseView-5F8Ms5z4.js} +1 -1
  8. package/.output/public/assets/{StreamingChunkSequence-ClhUhT-s.js → StreamingChunkSequence-CKDCWfu9.js} +1 -1
  9. package/.output/public/assets/_sessionId-C-aKd1Ky.js +1 -0
  10. package/.output/public/assets/index-B8ttyigz.js +1 -0
  11. package/.output/public/assets/index-DeJyypsp.css +1 -0
  12. package/.output/public/assets/{json-viewer-BicGakI5.js → json-viewer-CztuZ9cT.js} +2 -2
  13. package/.output/public/assets/{main-Be2qqUUW.js → main-CR9IJlz1.js} +2 -2
  14. package/.output/server/_libs/lucide-react.mjs +93 -72
  15. package/.output/server/{_sessionId-DhKJIdQC.mjs → _sessionId-DvWQaDEm.mjs} +2 -2
  16. package/.output/server/_ssr/{CompareDrawer-BGUgukJ8.mjs → CompareDrawer-C5FsxSDS.mjs} +4 -4
  17. package/.output/server/_ssr/{ProxyViewerContainer--3K3o3Sm.mjs → ProxyViewerContainer-v0cvR8f5.mjs} +354 -343
  18. package/.output/server/_ssr/{ReplayDialog-Bo86xZI4.mjs → ReplayDialog-C3KOv9OW.mjs} +4 -4
  19. package/.output/server/_ssr/{RequestAnatomy-jRU5qgwB.mjs → RequestAnatomy-BYRe33eG.mjs} +3 -3
  20. package/.output/server/_ssr/{ResponseView-DdO_-79a.mjs → ResponseView-va7yQDeL.mjs} +4 -4
  21. package/.output/server/_ssr/{StreamingChunkSequence-BigLwhh4.mjs → StreamingChunkSequence-BJlI-gWl.mjs} +3 -3
  22. package/.output/server/_ssr/{index-BHG6vOnr.mjs → index-CS0fA2GT.mjs} +2 -2
  23. package/.output/server/_ssr/index.mjs +2 -2
  24. package/.output/server/_ssr/{json-viewer-B4c_WjXD.mjs → json-viewer-Dg8rqrxL.mjs} +9 -5
  25. package/.output/server/_ssr/{router-DVixpJO-.mjs → router-D_Boe9Bu.mjs} +3 -3
  26. package/.output/server/{_tanstack-start-manifest_v-BbvWUF4v.mjs → _tanstack-start-manifest_v-KFXyNRGC.mjs} +1 -1
  27. package/.output/server/index.mjs +65 -65
  28. package/package.json +2 -1
  29. package/src/cli/detect-tools.ts +146 -0
  30. package/src/cli/onboard.ts +229 -0
  31. package/src/cli/templates/command-onboard.ts +17 -0
  32. package/src/cli/templates/skill-onboard.ts +458 -0
  33. package/src/cli.ts +193 -163
  34. package/src/components/ProxyViewer.tsx +153 -142
  35. package/src/components/proxy-viewer/LogEntry.tsx +136 -157
  36. package/src/components/proxy-viewer/LogEntryHeader.tsx +147 -66
  37. package/src/components/proxy-viewer/useCopyFeedback.ts +36 -0
  38. package/src/components/ui/json-viewer.tsx +12 -0
  39. package/.output/public/assets/ProxyViewerContainer-WRenRpeh.js +0 -101
  40. package/.output/public/assets/_sessionId-BO47oA3Z.js +0 -1
  41. package/.output/public/assets/index-BRvz6-L6.css +0 -1
  42. package/.output/public/assets/index-Btw8ec7-.js +0 -1
@@ -0,0 +1,229 @@
1
+ /**
2
+ * `llm-inspector onboard` subcommand.
3
+ *
4
+ * Generates a Claude Code skill + slash command into the user's home dir so
5
+ * the user can run `/llm-inspector:onboard` to walk through a guided setup.
6
+ *
7
+ * Idempotent by default — re-runs without `--force` are no-ops. `--force`
8
+ * overwrites, `--dry-run` writes nothing. Designed to be safe to invoke
9
+ * from npm's `postinstall` script (any error is logged to stderr, not
10
+ * thrown — the postinstall must not fail the install).
11
+ */
12
+
13
+ import { mkdirSync, writeFileSync, existsSync, readFileSync } from "node:fs";
14
+ import { homedir } from "node:os";
15
+ import { dirname, join } from "node:path";
16
+ import { fileURLToPath } from "node:url";
17
+
18
+ import { detectAll, detectFirst } from "./detect-tools.js";
19
+ import { renderCommandOnboard } from "./templates/command-onboard.js";
20
+ import { renderSkillOnboard, REQUIRED_PHASE_HEADINGS } from "./templates/skill-onboard.js";
21
+
22
+ const __filename = fileURLToPath(import.meta.url);
23
+ const __dirname = dirname(__filename);
24
+
25
+ const DEFAULT_PORT = 25947;
26
+ const SKILL_DIR_NAME = "llm-inspector-onboard";
27
+ const SKILL_FILE_NAME = "SKILL.md";
28
+ // Windows reserves `:` for NTFS alternate data streams, so a file named
29
+ // `llm-inspector:onboard.md` becomes an empty `llm-inspector` on disk.
30
+ // Use `-` on Windows and `:` on Unix to match the slash-command convention
31
+ // (`/llm-inspector:onboard` vs `/llm-inspector-onboard`) within the
32
+ // constraints of each platform.
33
+ const COMMAND_FILE_NAME =
34
+ process.platform === "win32" ? "llm-inspector-onboard.md" : "llm-inspector:onboard.md";
35
+
36
+ export type OnboardFlags = {
37
+ force: boolean;
38
+ dryRun: boolean;
39
+ skipProvider: boolean;
40
+ skipToolWire: boolean;
41
+ skillDir: string | null;
42
+ };
43
+
44
+ function parseFlags(argv: readonly string[]): OnboardFlags {
45
+ const flags: OnboardFlags = {
46
+ force: false,
47
+ dryRun: false,
48
+ skipProvider: false,
49
+ skipToolWire: false,
50
+ skillDir: null,
51
+ };
52
+ for (let i = 0; i < argv.length; i++) {
53
+ const arg = argv[i];
54
+ switch (arg) {
55
+ case undefined:
56
+ continue;
57
+ case "--force":
58
+ flags.force = true;
59
+ break;
60
+ case "--dry-run":
61
+ flags.dryRun = true;
62
+ break;
63
+ case "--skip-provider":
64
+ flags.skipProvider = true;
65
+ break;
66
+ case "--skip-tool-wire":
67
+ flags.skipToolWire = true;
68
+ break;
69
+ case "--skill-dir": {
70
+ const next = argv[i + 1];
71
+ if (next === undefined) {
72
+ process.stderr.write("llm-inspector onboard: --skill-dir requires a path argument\n");
73
+ process.exit(2);
74
+ }
75
+ flags.skillDir = next;
76
+ i++;
77
+ break;
78
+ }
79
+ case "--help":
80
+ case "-h":
81
+ printHelp();
82
+ process.exit(0);
83
+ break;
84
+ default:
85
+ process.stderr.write(`llm-inspector onboard: unknown flag: ${arg}\n`);
86
+ process.exit(2);
87
+ }
88
+ }
89
+ return flags;
90
+ }
91
+
92
+ function printHelp(): void {
93
+ process.stdout.write(`llm-inspector onboard — install the llm-inspector Claude Code skill
94
+
95
+ Usage:
96
+ llm-inspector onboard [options]
97
+
98
+ Options:
99
+ --force Overwrite the existing skill and slash command if they exist
100
+ --dry-run Print target paths and a template preview, write nothing
101
+ --skip-provider Skip the provider-setup phase in the skill body
102
+ --skip-tool-wire Skip the wire-tool phase in the skill body
103
+ --skill-dir <path> Override the target skill directory (default: ~/.claude/skills)
104
+ -h, --help Show this help
105
+
106
+ Exit codes:
107
+ 0 success (or already installed)
108
+ 2 invalid arguments
109
+ 1 write failed
110
+ `);
111
+ }
112
+
113
+ function resolveTargets(flags: OnboardFlags): {
114
+ skillFile: string;
115
+ commandFile: string;
116
+ } {
117
+ const claudeRoot = flags.skillDir ?? join(homedir(), ".claude");
118
+ const skillDir = join(claudeRoot, "skills", SKILL_DIR_NAME);
119
+ const commandsDir = join(claudeRoot, "commands");
120
+ return {
121
+ skillFile: join(skillDir, SKILL_FILE_NAME),
122
+ commandFile: join(commandsDir, COMMAND_FILE_NAME),
123
+ };
124
+ }
125
+
126
+ function isObject(value: unknown): value is Record<string, unknown> {
127
+ return typeof value === "object" && value !== null;
128
+ }
129
+
130
+ function buildDetectedSummary(): string {
131
+ const entries = detectAll();
132
+ const present = entries.filter((e) => e.result.found);
133
+ const absent = entries.filter((e) => !e.result.found);
134
+ const presentList = present.map((e) => e.displayName).join(", ") || "(none)";
135
+ const absentList = absent.map((e) => e.displayName).join(", ") || "(none)";
136
+ return ` - Detected: ${presentList}\n - Not detected: ${absentList}`;
137
+ }
138
+
139
+ export function runOnboard(argv: readonly string[]): Promise<number> {
140
+ try {
141
+ return Promise.resolve(runOnboardSync(argv));
142
+ } catch (err) {
143
+ // Last-resort safety net. The postinstall hook relies on this never
144
+ // surfacing — log to stderr and exit 0 so npm install keeps working.
145
+ const msg = err instanceof Error ? err.message : String(err);
146
+ process.stderr.write(`llm-inspector onboard: ${msg}\n`);
147
+ process.stderr.write(
148
+ "(postinstall skill install skipped — run `llm-inspector onboard` later)\n",
149
+ );
150
+ return Promise.resolve(0);
151
+ }
152
+ }
153
+
154
+ function runOnboardSync(argv: readonly string[]): number {
155
+ const flags = parseFlags(argv);
156
+ const { skillFile, commandFile } = resolveTargets(flags);
157
+
158
+ // Idempotency check — only relevant for the skill file. If the slash
159
+ // command file is missing but the skill is present, that's weird; treat
160
+ // it the same as "already installed" so a partial state doesn't get
161
+ // half-rewritten on the next run.
162
+ if (!flags.force && existsSync(skillFile)) {
163
+ process.stdout.write(`llm-inspector onboard: already installed at ${skillFile}\n`);
164
+ process.stdout.write("Re-run with --force to refresh.\n");
165
+ return 0;
166
+ }
167
+
168
+ // Version stamp — read from package.json so the skill and the published
169
+ // package can never disagree about which version wrote them.
170
+ let version = "0.0.0";
171
+ try {
172
+ const raw: unknown = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf8"));
173
+ if (isObject(raw) && typeof raw["version"] === "string") {
174
+ version = raw["version"];
175
+ }
176
+ } catch {
177
+ // best-effort: leave version as the fallback
178
+ }
179
+
180
+ const detectedSummary = buildDetectedSummary();
181
+ const skillBody = renderSkillOnboard({
182
+ version,
183
+ port: DEFAULT_PORT,
184
+ detectedSummary,
185
+ });
186
+ const commandBody = renderCommandOnboard();
187
+
188
+ if (flags.dryRun) {
189
+ process.stdout.write(`llm-inspector onboard --dry-run\n\n`);
190
+ process.stdout.write(`Skill target: ${skillFile}\n`);
191
+ process.stdout.write(`Command target: ${commandFile}\n\n`);
192
+ process.stdout.write(`Skill preview (first 5 lines + headings):\n`);
193
+ const previewLines = skillBody.split("\n").slice(0, 5);
194
+ process.stdout.write(`${previewLines.join("\n")}\n`);
195
+ process.stdout.write(`...\n`);
196
+ for (const heading of REQUIRED_PHASE_HEADINGS) {
197
+ process.stdout.write(` - ${heading}\n`);
198
+ }
199
+ process.stdout.write(`\nDetected tools:\n${detectedSummary}\n`);
200
+ process.stdout.write(`\nNo files were written.\n`);
201
+ return 0;
202
+ }
203
+
204
+ // Ensure target dirs exist. mkdirSync with recursive: true is a no-op if
205
+ // the dir already exists, and on failure it throws — caught by the
206
+ // outer try/catch and reported as a soft postinstall skip.
207
+ mkdirSync(join(skillFile, ".."), { recursive: true });
208
+ mkdirSync(join(commandFile, ".."), { recursive: true });
209
+
210
+ try {
211
+ writeFileSync(skillFile, skillBody, "utf8");
212
+ writeFileSync(commandFile, commandBody, "utf8");
213
+ } catch (err) {
214
+ const msg = err instanceof Error ? err.message : String(err);
215
+ process.stderr.write(`llm-inspector onboard: failed to write files: ${msg}\n`);
216
+ return 1;
217
+ }
218
+
219
+ const firstTool = detectFirst();
220
+ const toolHint = firstTool !== null ? firstTool.displayName : "no known tool detected";
221
+
222
+ process.stdout.write(`Installed skill to: ${skillFile}\n`);
223
+ process.stdout.write(`Installed command to: ${commandFile}\n`);
224
+ process.stdout.write(`\nNext steps:\n`);
225
+ process.stdout.write(` - Open Claude Code and run: /llm-inspector:onboard\n`);
226
+ process.stdout.write(` - Or refresh later: llm-inspector onboard --force\n`);
227
+ process.stdout.write(` - Detected primary tool: ${toolHint}\n`);
228
+ return 0;
229
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Renders `~/.claude/commands/llm-inspector:onboard.md` — a thin slash
3
+ * command that loads the `llm-inspector-onboard` skill. Claude Code
4
+ * auto-discovers both the skill and the command once they're written,
5
+ * so the command body just references the skill by name.
6
+ */
7
+
8
+ export function renderCommandOnboard(): string {
9
+ return `---
10
+ description: Walk through llm-inspector setup — start the proxy, wire your AI tool, capture your first request.
11
+ ---
12
+
13
+ Invoke the \`llm-inspector-onboard\` skill and follow its phases.
14
+
15
+ The user wants to set up llm-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
+ `;
17
+ }