@inspecto-dev/cli 0.2.0-alpha.5 → 0.3.0-alpha.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 (49) hide show
  1. package/.turbo/turbo-build.log +19 -20
  2. package/CHANGELOG.md +22 -0
  3. package/README.md +93 -11
  4. package/bin/inspecto.js +5 -1
  5. package/dist/bin.d.ts +5 -1
  6. package/dist/bin.js +530 -49
  7. package/dist/chunk-FZS2TLXQ.js +3140 -0
  8. package/dist/index.d.ts +233 -2
  9. package/dist/index.js +17 -3
  10. package/package.json +3 -2
  11. package/src/bin.ts +286 -66
  12. package/src/commands/apply.ts +118 -0
  13. package/src/commands/detect.ts +59 -0
  14. package/src/commands/doctor.ts +225 -72
  15. package/src/commands/init.ts +143 -183
  16. package/src/commands/integration-install.ts +452 -0
  17. package/src/commands/onboard.ts +50 -0
  18. package/src/commands/plan.ts +41 -0
  19. package/src/detect/build-tool.ts +107 -3
  20. package/src/index.ts +17 -2
  21. package/src/inject/ast-injector.ts +17 -6
  22. package/src/inject/extension.ts +40 -22
  23. package/src/inject/gitignore.ts +10 -3
  24. package/src/instructions.ts +60 -46
  25. package/src/onboarding/apply.ts +364 -0
  26. package/src/onboarding/context.ts +36 -0
  27. package/src/onboarding/planner.ts +284 -0
  28. package/src/onboarding/session.ts +434 -0
  29. package/src/onboarding/target-resolution.ts +116 -0
  30. package/src/prompts.ts +54 -11
  31. package/src/types.ts +184 -0
  32. package/src/utils/fs.ts +2 -1
  33. package/src/utils/logger.ts +9 -0
  34. package/src/utils/output.ts +40 -0
  35. package/tests/apply.test.ts +583 -0
  36. package/tests/ast-injector.test.ts +50 -0
  37. package/tests/build-tool.test.ts +3 -5
  38. package/tests/detect.test.ts +94 -0
  39. package/tests/doctor.test.ts +224 -0
  40. package/tests/init.test.ts +364 -0
  41. package/tests/install-wrapper.test.ts +76 -0
  42. package/tests/instructions.test.ts +61 -0
  43. package/tests/integration-install.test.ts +294 -0
  44. package/tests/logger.test.ts +100 -0
  45. package/tests/onboard.test.ts +258 -0
  46. package/tests/plan.test.ts +713 -0
  47. package/tests/workspace-build-tool.test.ts +75 -0
  48. package/.turbo/turbo-test.log +0 -16
  49. package/dist/chunk-MIHQGC3L.js +0 -1720
package/dist/bin.js CHANGED
@@ -1,65 +1,546 @@
1
1
  import {
2
+ apply,
3
+ detect,
2
4
  doctor,
5
+ exists,
3
6
  init,
4
7
  log,
5
- teardown
6
- } from "./chunk-MIHQGC3L.js";
8
+ onboard,
9
+ plan,
10
+ reportCommandError,
11
+ teardown,
12
+ writeFile
13
+ } from "./chunk-FZS2TLXQ.js";
7
14
 
8
15
  // src/bin.ts
9
16
  import { cac } from "cac";
17
+ import { fileURLToPath as fileURLToPath2 } from "url";
10
18
  import { createRequire } from "module";
11
- var require2 = createRequire(import.meta.url);
12
- var { version } = require2("../package.json");
13
- var cli = cac("inspecto");
14
- cli.command("init", "Set up Inspecto in your project").option("--shared", "Share .inspecto/settings.json with your team via Git", { default: false }).option("--skip-install", "Skip npm dependency installation", { default: false }).option("--dry-run", "Preview changes without modifying files", { default: false }).option("--provider <provider>", "Set default provider (e.g. copilot, claude-code)").option("--no-extension", "Skip VS Code extension installation", { default: false }).option("--packages <names>", "(Monorepo) Comma-separated list of packages to inject").option("--force", "Force initialization even if environment is unsupported", { default: false }).option("--debug", "Enable debug mode to show full error traces", { default: false }).action(async (options) => {
15
- try {
16
- await init({
17
- shared: options.shared ?? false,
18
- skipInstall: options.skipInstall ?? false,
19
- dryRun: options.dryRun ?? false,
20
- ...options.provider && { provider: options.provider },
21
- // Changed from prefer to provider
22
- noExtension: options.noExtension ?? false,
23
- ...options.packages && {
24
- packages: options.packages.split(",").map((s) => s.trim())
19
+
20
+ // src/commands/integration-install.ts
21
+ import fs from "fs/promises";
22
+ import { homedir } from "os";
23
+ import path from "path";
24
+ import { fileURLToPath } from "url";
25
+ var REPO_RAW_BASE = "https://raw.githubusercontent.com/inspecto-dev/inspecto/main";
26
+ var INTEGRATION_MANIFESTS = [
27
+ {
28
+ assistant: "codex",
29
+ type: "native-skill",
30
+ installTarget: "~/.codex/skills/",
31
+ preferredInstall: "npx @inspecto-dev/cli integrations install codex",
32
+ cliSupported: true
33
+ },
34
+ {
35
+ assistant: "claude-code",
36
+ type: "native-skill",
37
+ installTarget: ".claude/skills/ or ~/.claude/skills/",
38
+ preferredInstall: "npx @inspecto-dev/cli integrations install claude-code --scope project",
39
+ cliSupported: true
40
+ },
41
+ {
42
+ assistant: "copilot",
43
+ type: "instruction-template",
44
+ installTarget: ".github/copilot-instructions.md or AGENTS.md",
45
+ preferredInstall: "npx @inspecto-dev/cli integrations install copilot",
46
+ cliSupported: true
47
+ },
48
+ {
49
+ assistant: "cursor",
50
+ type: "rule-template",
51
+ installTarget: ".cursor/rules/inspecto-onboarding.mdc or AGENTS.md",
52
+ preferredInstall: "npx @inspecto-dev/cli integrations install cursor --mode rules",
53
+ cliSupported: true
54
+ },
55
+ {
56
+ assistant: "gemini",
57
+ type: "context-template",
58
+ installTarget: "GEMINI.md",
59
+ preferredInstall: "npx @inspecto-dev/cli integrations install gemini",
60
+ cliSupported: true
61
+ },
62
+ {
63
+ assistant: "trae",
64
+ type: "compatibility-template",
65
+ installTarget: "AGENTS.md",
66
+ preferredInstall: "npx @inspecto-dev/cli integrations install trae",
67
+ cliSupported: true
68
+ },
69
+ {
70
+ assistant: "coco",
71
+ type: "compatibility-template",
72
+ installTarget: "AGENTS.md",
73
+ preferredInstall: "npx @inspecto-dev/cli integrations install coco",
74
+ cliSupported: true
75
+ }
76
+ ];
77
+ async function installIntegration(assistant, options = {}) {
78
+ const plan2 = resolveInstallPlan(assistant, options);
79
+ log.header("Inspecto Integration Install");
80
+ for (const asset of plan2.assets) {
81
+ if (await exists(asset.target) && !options.force) {
82
+ throw new Error(
83
+ `Refusing to overwrite existing file: ${asset.target}. Re-run with --force if you want to replace it.`
84
+ );
85
+ }
86
+ }
87
+ const downloadedAssets = [];
88
+ for (const asset of plan2.assets) {
89
+ const content = await loadAsset(asset);
90
+ downloadedAssets.push({ asset, content });
91
+ }
92
+ for (const { asset, content } of downloadedAssets) {
93
+ await writeFile(asset.target, content);
94
+ if (asset.executable) {
95
+ await fs.chmod(asset.target, 493);
96
+ }
97
+ }
98
+ log.success(plan2.successMessage);
99
+ log.hint(plan2.nextStep);
100
+ }
101
+ function describeIntegration(assistant, options = {}) {
102
+ const manifest = getIntegrationManifest(assistant);
103
+ const targets = manifest.cliSupported ? resolveInstallPlan(assistant, options).assets.map((asset) => asset.target) : [manifest.installTarget];
104
+ return {
105
+ assistant: manifest.assistant,
106
+ type: manifest.type,
107
+ targets,
108
+ preferredInstall: manifest.preferredInstall,
109
+ cliSupported: manifest.cliSupported
110
+ };
111
+ }
112
+ function printIntegrationList() {
113
+ log.header("Inspecto Integrations");
114
+ for (const manifest of INTEGRATION_MANIFESTS) {
115
+ const support = manifest.cliSupported ? "CLI" : "native installer";
116
+ log.info(`${manifest.assistant} \u2014 ${manifest.type} \u2014 ${manifest.installTarget} \u2014 ${support}`);
117
+ }
118
+ }
119
+ function printIntegrationPath(assistant, options = {}) {
120
+ const description = describeIntegration(assistant, options);
121
+ log.header(`Inspecto Integration Paths: ${description.assistant}`);
122
+ for (const target of description.targets) {
123
+ log.info(target);
124
+ }
125
+ if (description.cliSupported) {
126
+ log.hint(`Preferred install: ${description.preferredInstall}`);
127
+ } else {
128
+ log.hint(`Native install required: ${description.preferredInstall}`);
129
+ }
130
+ }
131
+ function resolveInstallPlan(assistant, options) {
132
+ switch (assistant) {
133
+ case "codex":
134
+ return resolveCodexPlan(options);
135
+ case "claude-code":
136
+ return resolveClaudeCodePlan(options);
137
+ case "copilot":
138
+ return resolveCopilotPlan(options);
139
+ case "cursor":
140
+ return resolveCursorPlan(options);
141
+ case "gemini":
142
+ return {
143
+ assets: [
144
+ {
145
+ source: `${REPO_RAW_BASE}/assistant-integrations/gemini/GEMINI.md`,
146
+ target: "GEMINI.md",
147
+ localSource: "assistant-integrations/gemini/GEMINI.md"
148
+ }
149
+ ],
150
+ successMessage: "Installed Gemini context to GEMINI.md",
151
+ nextStep: "Start a new Gemini CLI session."
152
+ };
153
+ case "trae":
154
+ return {
155
+ assets: [
156
+ {
157
+ source: `${REPO_RAW_BASE}/assistant-integrations/trae/AGENTS.md`,
158
+ target: "AGENTS.md",
159
+ localSource: "assistant-integrations/trae/AGENTS.md"
160
+ }
161
+ ],
162
+ successMessage: "Installed Trae compatibility instructions to AGENTS.md",
163
+ nextStep: "Open a new Trae chat."
164
+ };
165
+ case "coco":
166
+ return {
167
+ assets: [
168
+ {
169
+ source: `${REPO_RAW_BASE}/assistant-integrations/coco/AGENTS.md`,
170
+ target: "AGENTS.md",
171
+ localSource: "assistant-integrations/coco/AGENTS.md"
172
+ }
173
+ ],
174
+ successMessage: "Installed Coco compatibility instructions to AGENTS.md",
175
+ nextStep: "Start a new Coco session."
176
+ };
177
+ default:
178
+ throw new Error(`Unknown assistant: ${assistant}`);
179
+ }
180
+ }
181
+ function getIntegrationManifest(assistant) {
182
+ const manifest = INTEGRATION_MANIFESTS.find((item) => item.assistant === assistant);
183
+ if (!manifest) {
184
+ throw new Error(
185
+ `Unknown assistant: ${assistant}. Run 'inspecto integrations list' to see available targets.`
186
+ );
187
+ }
188
+ return manifest;
189
+ }
190
+ function resolveCodexPlan(options) {
191
+ if (options.scope !== void 0) {
192
+ throw new Error("`--scope` is not supported for codex.");
193
+ }
194
+ if (options.mode !== void 0) {
195
+ throw new Error("`--mode` is not supported for codex.");
196
+ }
197
+ const baseDir = path.join(homedir(), ".codex/skills/inspecto-onboarding-codex");
198
+ return {
199
+ assets: [
200
+ {
201
+ source: `${REPO_RAW_BASE}/skills/inspecto-onboarding-codex/SKILL.md`,
202
+ target: path.join(baseDir, "SKILL.md"),
203
+ localSource: "skills/inspecto-onboarding-codex/SKILL.md"
204
+ },
205
+ {
206
+ source: `${REPO_RAW_BASE}/skills/inspecto-onboarding-codex/agents/openai.yaml`,
207
+ target: path.join(baseDir, "agents/openai.yaml"),
208
+ localSource: "skills/inspecto-onboarding-codex/agents/openai.yaml"
25
209
  },
26
- force: options.force ?? false
27
- });
28
- } catch (err) {
29
- log.error(err instanceof Error ? err.message : String(err));
30
- if (options.debug && err instanceof Error && err.stack) {
31
- console.error(err.stack);
210
+ {
211
+ source: `${REPO_RAW_BASE}/skills/inspecto-onboarding-codex/scripts/run-inspecto.sh`,
212
+ target: path.join(baseDir, "scripts/run-inspecto.sh"),
213
+ executable: true,
214
+ localSource: "skills/inspecto-onboarding-codex/scripts/run-inspecto.sh"
215
+ }
216
+ ],
217
+ successMessage: `Installed Codex skill to ${baseDir}`,
218
+ nextStep: "Restart Codex or start a new Codex session to load the skill."
219
+ };
220
+ }
221
+ function resolveClaudeCodePlan(options) {
222
+ const scope = options.scope ?? "project";
223
+ const unsupportedMode = options.mode !== void 0;
224
+ if (unsupportedMode) {
225
+ throw new Error(
226
+ "`--mode` is not supported for claude-code. Use `--scope project|user` instead."
227
+ );
228
+ }
229
+ if (scope !== "project" && scope !== "user") {
230
+ throw new Error(`Unknown Claude Code scope: ${scope}`);
231
+ }
232
+ const baseDir = scope === "user" ? path.join(homedir(), ".claude/skills/inspecto-onboarding-claude-code") : ".claude/skills/inspecto-onboarding-claude-code";
233
+ return {
234
+ assets: [
235
+ {
236
+ source: `${REPO_RAW_BASE}/skills/inspecto-onboarding-claude-code/SKILL.md`,
237
+ target: path.join(baseDir, "SKILL.md"),
238
+ localSource: "skills/inspecto-onboarding-claude-code/SKILL.md"
239
+ },
240
+ {
241
+ source: `${REPO_RAW_BASE}/skills/inspecto-onboarding-claude-code/agents/openai.yaml`,
242
+ target: path.join(baseDir, "agents/openai.yaml"),
243
+ localSource: "skills/inspecto-onboarding-claude-code/agents/openai.yaml"
244
+ },
245
+ {
246
+ source: `${REPO_RAW_BASE}/skills/inspecto-onboarding-claude-code/scripts/run-inspecto.sh`,
247
+ target: path.join(baseDir, "scripts/run-inspecto.sh"),
248
+ executable: true,
249
+ localSource: "skills/inspecto-onboarding-claude-code/scripts/run-inspecto.sh"
250
+ }
251
+ ],
252
+ successMessage: `Installed Claude Code skill to ${baseDir}`,
253
+ nextStep: "Restart Claude Code to load the new skill."
254
+ };
255
+ }
256
+ function resolveCopilotPlan(options) {
257
+ const mode = options.mode ?? "instructions";
258
+ if (options.scope !== void 0) {
259
+ throw new Error(
260
+ "`--scope` is not supported for copilot. Use `--mode instructions|agents` instead."
261
+ );
262
+ }
263
+ switch (mode) {
264
+ case "instructions":
265
+ return {
266
+ assets: [
267
+ {
268
+ source: `${REPO_RAW_BASE}/assistant-integrations/copilot/.github/copilot-instructions.md`,
269
+ target: ".github/copilot-instructions.md",
270
+ localSource: "assistant-integrations/copilot/.github/copilot-instructions.md"
271
+ }
272
+ ],
273
+ successMessage: "Installed Copilot instructions to .github/copilot-instructions.md",
274
+ nextStep: "Open a new Copilot chat or agent session."
275
+ };
276
+ case "agents":
277
+ return {
278
+ assets: [
279
+ {
280
+ source: `${REPO_RAW_BASE}/assistant-integrations/copilot/AGENTS.md`,
281
+ target: "AGENTS.md",
282
+ localSource: "assistant-integrations/copilot/AGENTS.md"
283
+ }
284
+ ],
285
+ successMessage: "Installed Copilot compatibility instructions to AGENTS.md",
286
+ nextStep: "Open a new Copilot chat or agent session."
287
+ };
288
+ default:
289
+ throw new Error(`Unknown Copilot mode: ${mode}`);
290
+ }
291
+ }
292
+ function resolveCursorPlan(options) {
293
+ const mode = options.mode ?? "rules";
294
+ if (options.scope !== void 0) {
295
+ throw new Error("`--scope` is not supported for cursor. Use `--mode rules|agents` instead.");
296
+ }
297
+ switch (mode) {
298
+ case "rules":
299
+ return {
300
+ assets: [
301
+ {
302
+ source: `${REPO_RAW_BASE}/assistant-integrations/cursor/.cursor/rules/inspecto-onboarding.mdc`,
303
+ target: ".cursor/rules/inspecto-onboarding.mdc",
304
+ localSource: "assistant-integrations/cursor/.cursor/rules/inspecto-onboarding.mdc"
305
+ }
306
+ ],
307
+ successMessage: "Installed Cursor rule to .cursor/rules/inspecto-onboarding.mdc",
308
+ nextStep: "Open a new Cursor chat."
309
+ };
310
+ case "agents":
311
+ return {
312
+ assets: [
313
+ {
314
+ source: `${REPO_RAW_BASE}/assistant-integrations/cursor/AGENTS.md`,
315
+ target: "AGENTS.md",
316
+ localSource: "assistant-integrations/cursor/AGENTS.md"
317
+ }
318
+ ],
319
+ successMessage: "Installed Cursor compatibility instructions to AGENTS.md",
320
+ nextStep: "Open a new Cursor chat."
321
+ };
322
+ default:
323
+ throw new Error(`Unknown Cursor mode: ${mode}`);
324
+ }
325
+ }
326
+ async function loadAsset(asset) {
327
+ if (asset.localSource) {
328
+ const localPath = await resolveBundledAssetPath(asset.localSource);
329
+ if (localPath) {
330
+ return await fs.readFile(localPath, "utf-8");
32
331
  }
33
- process.exit(1);
34
332
  }
35
- });
36
- cli.command("doctor", "Diagnose your Inspecto installation").option("--debug", "Enable debug mode to show full error traces", { default: false }).action(async (options) => {
37
- try {
38
- await doctor();
39
- } catch (err) {
40
- log.error(err instanceof Error ? err.message : String(err));
41
- if (options.debug && err instanceof Error && err.stack) {
42
- console.error(err.stack);
333
+ return await downloadAsset(asset.source);
334
+ }
335
+ async function resolveBundledAssetPath(relativePath) {
336
+ const startDir = path.dirname(fileURLToPath(import.meta.url));
337
+ let currentDir = startDir;
338
+ for (let depth = 0; depth < 8; depth += 1) {
339
+ const candidate = path.join(currentDir, relativePath);
340
+ if (await exists(candidate)) {
341
+ return candidate;
43
342
  }
44
- process.exit(1);
343
+ const parent = path.dirname(currentDir);
344
+ if (parent === currentDir) break;
345
+ currentDir = parent;
45
346
  }
46
- });
47
- cli.command("teardown", "Remove Inspecto from your project").option("--debug", "Enable debug mode to show full error traces", { default: false }).action(async (options) => {
347
+ return null;
348
+ }
349
+ async function downloadAsset(source) {
350
+ let response;
48
351
  try {
49
- await teardown();
50
- } catch (err) {
51
- log.error(err instanceof Error ? err.message : String(err));
52
- if (options.debug && err instanceof Error && err.stack) {
53
- console.error(err.stack);
54
- }
55
- process.exit(1);
56
- }
57
- });
58
- cli.help();
59
- cli.version(version);
60
- try {
61
- cli.parse();
62
- } catch (err) {
63
- log.error(err instanceof Error ? err.message : String(err));
352
+ response = await fetch(source);
353
+ } catch (error) {
354
+ const reason = error instanceof Error ? error.message : String(error);
355
+ throw new Error(`Failed to download ${source}: ${reason}`);
356
+ }
357
+ if (!response.ok) {
358
+ throw new Error(`Failed to download ${source}: ${response.status} ${response.statusText}`);
359
+ }
360
+ return await response.text();
361
+ }
362
+
363
+ // src/bin.ts
364
+ var require2 = createRequire(import.meta.url);
365
+ var { version } = require2("../package.json");
366
+ var integrationScopes = ["project", "user"];
367
+ var integrationModes = ["instructions", "agents", "rules"];
368
+ function exitWithError(error, options = {}) {
369
+ reportCommandError(error, {
370
+ debug: options.debug ?? false,
371
+ json: options.json ?? false
372
+ });
64
373
  process.exit(1);
65
374
  }
375
+ function createCli(_argv = process.argv) {
376
+ const cli = cac("inspecto");
377
+ cli.command("init", "Set up Inspecto in your project").option("--shared", "Share .inspecto/settings.json with your team via Git", { default: false }).option("--skip-install", "Skip npm dependency installation", { default: false }).option("--dry-run", "Preview changes without modifying files", { default: false }).option("--provider <provider>", "Set default provider (e.g. copilot, claude-code)").option("--no-extension", "Skip VS Code extension installation", { default: false }).option("--packages <names>", "(Monorepo) Comma-separated list of packages to inject").option("--force", "Force initialization even if environment is unsupported", {
378
+ default: false
379
+ }).option("--debug", "Enable debug mode to show full error traces", { default: false }).action(async (options) => {
380
+ try {
381
+ await init({
382
+ shared: options.shared ?? false,
383
+ skipInstall: options.skipInstall ?? false,
384
+ dryRun: options.dryRun ?? false,
385
+ ...options.provider && { provider: options.provider },
386
+ noExtension: options.noExtension ?? false,
387
+ ...options.packages && {
388
+ packages: options.packages.split(",").map((s) => s.trim())
389
+ },
390
+ force: options.force ?? false
391
+ });
392
+ } catch (error) {
393
+ exitWithError(error, options);
394
+ }
395
+ });
396
+ cli.command("doctor", "Diagnose your Inspecto installation").option("--json", "Print machine-readable JSON output", { default: false }).option("--debug", "Enable debug mode to show full error traces", { default: false }).action(async (options) => {
397
+ try {
398
+ await doctor({ json: options.json ?? false });
399
+ } catch (error) {
400
+ exitWithError(error, options);
401
+ }
402
+ });
403
+ cli.command("onboard", "Run assistant-oriented Inspecto onboarding in one structured flow").option("--json", "Print machine-readable JSON output", { default: false }).option("--target <packagePath>", "Select a monorepo target package explicitly").option("--yes", "Accept a lightweight confirmation gate automatically", { default: false }).option("--shared", "Write shared Inspecto settings instead of local-only settings").option("--skip-install", "Skip npm dependency installation").option("--dry-run", "Preview changes without modifying files").option("--no-extension", "Skip IDE extension installation").option("--debug", "Enable debug mode to show full error traces", { default: false }).action(async (options) => {
404
+ try {
405
+ await onboard({
406
+ json: options.json ?? false,
407
+ ...options.target && { target: options.target },
408
+ yes: options.yes ?? false,
409
+ ...options.shared !== void 0 && { shared: options.shared },
410
+ ...options.skipInstall !== void 0 && { skipInstall: options.skipInstall },
411
+ ...options.dryRun !== void 0 && { dryRun: options.dryRun },
412
+ ...options.extension === false && { noExtension: true }
413
+ });
414
+ } catch (error) {
415
+ exitWithError(error, options);
416
+ }
417
+ });
418
+ cli.command("detect", "Detect whether the current project can be onboarded automatically").option("--json", "Print machine-readable JSON output", { default: false }).option("--debug", "Enable debug mode to show full error traces", { default: false }).action(async (options) => {
419
+ try {
420
+ await detect(options.json ?? false);
421
+ } catch (error) {
422
+ exitWithError(error, options);
423
+ }
424
+ });
425
+ cli.command("plan", "Preview the onboarding plan for the current project").option("--json", "Print machine-readable JSON output", { default: false }).option("--debug", "Enable debug mode to show full error traces", { default: false }).action(async (options) => {
426
+ try {
427
+ await plan(options.json ?? false);
428
+ } catch (error) {
429
+ exitWithError(error, options);
430
+ }
431
+ });
432
+ cli.command("apply", "Apply the onboarding plan to the current project").option("--json", "Print machine-readable JSON output", { default: false }).option("--shared", "Write shared Inspecto settings instead of local-only settings").option("--skip-install", "Skip npm dependency installation").option("--dry-run", "Preview changes without modifying files").option("--no-extension", "Skip IDE extension installation").option("--debug", "Enable debug mode to show full error traces", { default: false }).action(async (options) => {
433
+ try {
434
+ await apply({
435
+ json: options.json ?? false,
436
+ ...options.shared !== void 0 && { shared: options.shared },
437
+ ...options.skipInstall !== void 0 && { skipInstall: options.skipInstall },
438
+ ...options.dryRun !== void 0 && { dryRun: options.dryRun },
439
+ ...options.extension === false && { noExtension: true }
440
+ });
441
+ } catch (error) {
442
+ exitWithError(error, options);
443
+ }
444
+ });
445
+ cli.command("teardown", "Remove Inspecto from your project").option("--debug", "Enable debug mode to show full error traces", { default: false }).action(async (options) => {
446
+ try {
447
+ await teardown();
448
+ } catch (error) {
449
+ exitWithError(error, options);
450
+ }
451
+ });
452
+ cli.command("integrations [...args]", "Manage assistant integration assets").option(
453
+ "--scope <scope>",
454
+ "Set install scope for supported assistants (e.g. claude-code: project|user)"
455
+ ).option(
456
+ "--mode <mode>",
457
+ "Set install mode for supported assistants (e.g. copilot: instructions|agents)"
458
+ ).option("--force", "Overwrite existing integration files", { default: false }).option("--debug", "Enable debug mode to show full error traces", { default: false }).action(async (args, options) => {
459
+ try {
460
+ const [subcommand, assistant, ...rest] = args;
461
+ const integrationOptions = buildIntegrationOptions(options);
462
+ if (subcommand === "list") {
463
+ if (assistant || rest.length > 0 || options.scope || options.mode || options.force) {
464
+ throw new Error(
465
+ "The `list` subcommand does not accept assistant names, --scope, --mode, or --force."
466
+ );
467
+ }
468
+ printIntegrationList();
469
+ return;
470
+ }
471
+ if (subcommand === "path" && assistant) {
472
+ if (rest.length > 0) {
473
+ throw new Error("The `path` subcommand accepts exactly one assistant argument.");
474
+ }
475
+ if (options.force) {
476
+ throw new Error("The `path` subcommand does not support `--force`.");
477
+ }
478
+ printIntegrationPath(assistant, integrationOptions);
479
+ return;
480
+ }
481
+ if (subcommand !== "install" || !assistant) {
482
+ throw new Error(
483
+ [
484
+ "Usage:",
485
+ " inspecto integrations list",
486
+ " inspecto integrations path <assistant> [--scope <scope>] [--mode <mode>]",
487
+ " inspecto integrations install <assistant> [--scope <scope>] [--mode <mode>] [--force]"
488
+ ].join("\n")
489
+ );
490
+ }
491
+ if (rest.length > 0) {
492
+ throw new Error("The `install` subcommand accepts exactly one assistant argument.");
493
+ }
494
+ await installIntegration(assistant, {
495
+ ...integrationOptions,
496
+ force: options.force ?? false
497
+ });
498
+ } catch (error) {
499
+ exitWithError(error, options);
500
+ }
501
+ });
502
+ cli.help();
503
+ cli.version(version);
504
+ return cli;
505
+ }
506
+ function buildIntegrationOptions(options) {
507
+ const resolved = {};
508
+ if (options.scope) {
509
+ if (isIntegrationScope(options.scope)) {
510
+ resolved.scope = options.scope;
511
+ } else {
512
+ throw new Error(`Unknown integration scope: ${options.scope}`);
513
+ }
514
+ }
515
+ if (options.mode) {
516
+ if (isIntegrationMode(options.mode)) {
517
+ resolved.mode = options.mode;
518
+ } else {
519
+ throw new Error(`Unknown integration mode: ${options.mode}`);
520
+ }
521
+ }
522
+ return resolved;
523
+ }
524
+ function isIntegrationScope(value) {
525
+ return integrationScopes.includes(value);
526
+ }
527
+ function isIntegrationMode(value) {
528
+ return integrationModes.includes(value);
529
+ }
530
+ async function runCli(argv = process.argv) {
531
+ const cli = createCli(argv);
532
+ const parsedArgv = [...argv];
533
+ try {
534
+ await cli.parse(parsedArgv);
535
+ } catch (error) {
536
+ exitWithError(error, { json: argv.includes("--json") });
537
+ }
538
+ }
539
+ var entryPath = process.argv[1];
540
+ if (entryPath && fileURLToPath2(import.meta.url) === entryPath) {
541
+ void runCli();
542
+ }
543
+ export {
544
+ createCli,
545
+ runCli
546
+ };