@inspecto-dev/cli 0.2.0-alpha.6 → 0.3.0

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/dist/index.d.ts CHANGED
@@ -4,6 +4,8 @@ type PackageManager = 'bun' | 'pnpm' | 'yarn' | 'npm';
4
4
  type BuildTool = 'vite' | 'webpack' | 'rspack' | 'rsbuild' | 'esbuild' | 'rollup';
5
5
  /** Machine-readable status for onboarding commands */
6
6
  type CommandStatus = 'ok' | 'warning' | 'blocked' | 'error';
7
+ /** Assistant-facing status for single-entry onboarding */
8
+ type OnboardStatus = 'success' | 'partial_success' | 'needs_target_selection' | 'needs_confirmation' | 'error';
7
9
  /** Structured message emitted by onboarding commands */
8
10
  interface CommandMessage {
9
11
  code: string;
@@ -28,6 +30,98 @@ interface BuildToolDetection {
28
30
  /** Relative package path when using --packages */
29
31
  packagePath?: string;
30
32
  }
33
+ /** Normalized onboarding context shared by detection/planning */
34
+ interface OnboardingContext {
35
+ root: string;
36
+ packageManager: PackageManager;
37
+ buildTools: {
38
+ supported: BuildToolDetection[];
39
+ unsupported: string[];
40
+ };
41
+ frameworks: {
42
+ supported: string[];
43
+ unsupported: string[];
44
+ };
45
+ ides: Array<{
46
+ ide: string;
47
+ supported: boolean;
48
+ }>;
49
+ providers: OnboardingProvider[];
50
+ }
51
+ interface OnboardingTargetCandidate {
52
+ packagePath: string;
53
+ configPath: string;
54
+ buildTool: BuildTool;
55
+ frameworks: string[];
56
+ automaticInjection: boolean;
57
+ }
58
+ interface OnboardingTargetResolution {
59
+ status: 'resolved' | 'needs_selection';
60
+ selected?: OnboardingTargetCandidate;
61
+ candidates: OnboardingTargetCandidate[];
62
+ reason: string;
63
+ }
64
+ interface OnboardingSummary {
65
+ headline: string;
66
+ changes: string[];
67
+ risks: string[];
68
+ manualFollowUp: string[];
69
+ }
70
+ interface OnboardingConfirmation {
71
+ required: boolean;
72
+ reason?: string;
73
+ question?: string;
74
+ }
75
+ interface OnboardingExecutionResult {
76
+ changedFiles: string[];
77
+ installedDependencies: string[];
78
+ selectedProviderDefault?: string;
79
+ selectedIDE?: string;
80
+ mutations: Mutation[];
81
+ }
82
+ interface OnboardingDiagnostics {
83
+ warnings: string[];
84
+ errors: string[];
85
+ nextSteps: string[];
86
+ }
87
+ interface OnboardingIdeExtensionStatus {
88
+ required: boolean;
89
+ installed: boolean;
90
+ manualRequired: boolean;
91
+ installCommand?: string;
92
+ marketplaceUrl?: string;
93
+ openVsxUrl?: string;
94
+ }
95
+ interface OnboardingVerification {
96
+ available: boolean;
97
+ devCommand?: string;
98
+ message: string;
99
+ }
100
+ interface ResolvedOnboardingSession {
101
+ status: OnboardStatus;
102
+ target: OnboardingTargetResolution;
103
+ summary: OnboardingSummary;
104
+ confirmation: OnboardingConfirmation;
105
+ verification: OnboardingVerification;
106
+ context: OnboardingContext;
107
+ plan: PlanResult;
108
+ projectRoot: string;
109
+ selectedIDE?: {
110
+ ide: string;
111
+ supported: boolean;
112
+ } | null;
113
+ providerDefault?: string;
114
+ }
115
+ interface OnboardCommandResult {
116
+ status: OnboardStatus;
117
+ target: OnboardingTargetResolution;
118
+ summary: OnboardingSummary;
119
+ confirmation: OnboardingConfirmation;
120
+ ideExtension?: OnboardingIdeExtensionStatus;
121
+ verification?: OnboardingVerification;
122
+ result?: OnboardingExecutionResult;
123
+ diagnostics?: OnboardingDiagnostics;
124
+ }
31
125
  /** Machine-readable detection output for skill-first onboarding */
32
126
  interface DetectionResult {
33
127
  status: CommandStatus;
@@ -151,6 +245,17 @@ interface DoctorCommandOptions {
151
245
  declare function collectDoctorResult(root?: string): Promise<DoctorResult>;
152
246
  declare function doctor(options?: DoctorCommandOptions | boolean): Promise<DoctorResult>;
153
247
 
248
+ interface OnboardCommandOptions {
249
+ json?: boolean;
250
+ target?: string;
251
+ yes?: boolean;
252
+ shared?: boolean;
253
+ skipInstall?: boolean;
254
+ dryRun?: boolean;
255
+ noExtension?: boolean;
256
+ }
257
+ declare function onboard(options?: OnboardCommandOptions): Promise<OnboardCommandResult>;
258
+
154
259
  declare function plan(json?: boolean): Promise<PlanResult>;
155
260
 
156
261
  declare function teardown(): Promise<void>;
@@ -164,4 +269,4 @@ declare function reportCommandError(error: unknown, options?: ReportCommandError
164
269
 
165
270
  type Framework = 'react' | 'vue';
166
271
 
167
- export { type BuildTool, type DoctorDiagnostic, type DoctorResult, type Framework, type InitOptions, type InstallLock, type PackageManager, apply, collectDoctorResult, detect, doctor, init, plan, reportCommandError, teardown, writeCommandOutput };
272
+ export { type BuildTool, type DoctorDiagnostic, type DoctorResult, type Framework, type InitOptions, type InstallLock, type OnboardCommandResult, type OnboardStatus, type PackageManager, type ResolvedOnboardingSession, apply, collectDoctorResult, detect, doctor, init, onboard, plan, reportCommandError, teardown, writeCommandOutput };
package/dist/index.js CHANGED
@@ -4,17 +4,19 @@ import {
4
4
  detect,
5
5
  doctor,
6
6
  init,
7
+ onboard,
7
8
  plan,
8
9
  reportCommandError,
9
10
  teardown,
10
11
  writeCommandOutput
11
- } from "./chunk-PDDFPQJS.js";
12
+ } from "./chunk-IBYH7QZM.js";
12
13
  export {
13
14
  apply,
14
15
  collectDoctorResult,
15
16
  detect,
16
17
  doctor,
17
18
  init,
19
+ onboard,
18
20
  plan,
19
21
  reportCommandError,
20
22
  teardown,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inspecto-dev/cli",
3
- "version": "0.2.0-alpha.6",
3
+ "version": "0.3.0",
4
4
  "description": "CLI tools for Inspecto onboarding and lifecycle management",
5
5
  "keywords": [
6
6
  "inspecto",
@@ -20,7 +20,7 @@
20
20
  "ora": "^9.3.0",
21
21
  "picocolors": "^1.0.0",
22
22
  "prompts": "^2.4.2",
23
- "@inspecto-dev/types": "0.2.0-alpha.4"
23
+ "@inspecto-dev/types": "0.3.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/node": "^20.0.0",
package/src/bin.ts CHANGED
@@ -10,8 +10,15 @@ import { apply } from './commands/apply.js'
10
10
  import { detect } from './commands/detect.js'
11
11
  import { init } from './commands/init.js'
12
12
  import { doctor } from './commands/doctor.js'
13
+ import { onboard } from './commands/onboard.js'
13
14
  import { plan } from './commands/plan.js'
14
15
  import { teardown } from './commands/teardown.js'
16
+ import {
17
+ type InstallIntegrationOptions,
18
+ installIntegration,
19
+ printIntegrationList,
20
+ printIntegrationPath,
21
+ } from './commands/integration-install.js'
15
22
  import { reportCommandError } from './utils/output.js'
16
23
 
17
24
  const require = createRequire(import.meta.url)
@@ -42,6 +49,24 @@ interface ApplyCommandOptions extends JsonCommandOptions {
42
49
  extension?: boolean
43
50
  }
44
51
 
52
+ interface OnboardCliOptions extends JsonCommandOptions {
53
+ target?: string
54
+ yes?: boolean
55
+ shared?: boolean
56
+ skipInstall?: boolean
57
+ dryRun?: boolean
58
+ extension?: boolean
59
+ }
60
+
61
+ interface IntegrationCommandOptions extends GlobalOptions {
62
+ scope?: string
63
+ mode?: string
64
+ force?: boolean
65
+ }
66
+
67
+ const integrationScopes = ['project', 'user'] as const
68
+ const integrationModes = ['instructions', 'agents', 'rules'] as const
69
+
45
70
  function exitWithError(error: unknown, options: JsonCommandOptions = {}): never {
46
71
  reportCommandError(error, {
47
72
  debug: options.debug ?? false,
@@ -95,6 +120,32 @@ export function createCli(_argv: readonly string[] = process.argv): CAC {
95
120
  }
96
121
  })
97
122
 
123
+ cli
124
+ .command('onboard', 'Run assistant-oriented Inspecto onboarding in one structured flow')
125
+ .option('--json', 'Print machine-readable JSON output', { default: false })
126
+ .option('--target <packagePath>', 'Select a monorepo target package explicitly')
127
+ .option('--yes', 'Accept a lightweight confirmation gate automatically', { default: false })
128
+ .option('--shared', 'Write shared Inspecto settings instead of local-only settings')
129
+ .option('--skip-install', 'Skip npm dependency installation')
130
+ .option('--dry-run', 'Preview changes without modifying files')
131
+ .option('--no-extension', 'Skip IDE extension installation')
132
+ .option('--debug', 'Enable debug mode to show full error traces', { default: false })
133
+ .action(async (options: OnboardCliOptions) => {
134
+ try {
135
+ await onboard({
136
+ json: options.json ?? false,
137
+ ...(options.target && { target: options.target }),
138
+ yes: options.yes ?? false,
139
+ ...(options.shared !== undefined && { shared: options.shared }),
140
+ ...(options.skipInstall !== undefined && { skipInstall: options.skipInstall }),
141
+ ...(options.dryRun !== undefined && { dryRun: options.dryRun }),
142
+ ...(options.extension === false && { noExtension: true }),
143
+ })
144
+ } catch (error) {
145
+ exitWithError(error, options)
146
+ }
147
+ })
148
+
98
149
  cli
99
150
  .command('detect', 'Detect whether the current project can be onboarded automatically')
100
151
  .option('--json', 'Print machine-readable JSON output', { default: false })
@@ -152,12 +203,109 @@ export function createCli(_argv: readonly string[] = process.argv): CAC {
152
203
  }
153
204
  })
154
205
 
206
+ cli
207
+ .command('integrations [...args]', 'Manage assistant integration assets')
208
+ .option(
209
+ '--scope <scope>',
210
+ 'Set install scope for supported assistants (e.g. claude-code: project|user)',
211
+ )
212
+ .option(
213
+ '--mode <mode>',
214
+ 'Set install mode for supported assistants (e.g. copilot: instructions|agents)',
215
+ )
216
+ .option('--force', 'Overwrite existing integration files', { default: false })
217
+ .option('--debug', 'Enable debug mode to show full error traces', { default: false })
218
+ .action(async (args: string[], options: IntegrationCommandOptions) => {
219
+ try {
220
+ const [subcommand, assistant, ...rest] = args
221
+ const integrationOptions = buildIntegrationOptions(options)
222
+
223
+ if (subcommand === 'list') {
224
+ if (assistant || rest.length > 0 || options.scope || options.mode || options.force) {
225
+ throw new Error(
226
+ 'The `list` subcommand does not accept assistant names, --scope, --mode, or --force.',
227
+ )
228
+ }
229
+
230
+ printIntegrationList()
231
+ return
232
+ }
233
+
234
+ if (subcommand === 'path' && assistant) {
235
+ if (rest.length > 0) {
236
+ throw new Error('The `path` subcommand accepts exactly one assistant argument.')
237
+ }
238
+
239
+ if (options.force) {
240
+ throw new Error('The `path` subcommand does not support `--force`.')
241
+ }
242
+
243
+ printIntegrationPath(assistant, integrationOptions)
244
+ return
245
+ }
246
+
247
+ if (subcommand !== 'install' || !assistant) {
248
+ throw new Error(
249
+ [
250
+ 'Usage:',
251
+ ' inspecto integrations list',
252
+ ' inspecto integrations path <assistant> [--scope <scope>] [--mode <mode>]',
253
+ ' inspecto integrations install <assistant> [--scope <scope>] [--mode <mode>] [--force]',
254
+ ].join('\n'),
255
+ )
256
+ }
257
+
258
+ if (rest.length > 0) {
259
+ throw new Error('The `install` subcommand accepts exactly one assistant argument.')
260
+ }
261
+
262
+ await installIntegration(assistant, {
263
+ ...integrationOptions,
264
+ force: options.force ?? false,
265
+ })
266
+ } catch (error) {
267
+ exitWithError(error, options)
268
+ }
269
+ })
270
+
155
271
  cli.help()
156
272
  cli.version(version)
157
273
 
158
274
  return cli
159
275
  }
160
276
 
277
+ function buildIntegrationOptions(options: IntegrationCommandOptions): InstallIntegrationOptions {
278
+ const resolved: InstallIntegrationOptions = {}
279
+
280
+ if (options.scope) {
281
+ if (isIntegrationScope(options.scope)) {
282
+ resolved.scope = options.scope
283
+ } else {
284
+ throw new Error(`Unknown integration scope: ${options.scope}`)
285
+ }
286
+ }
287
+
288
+ if (options.mode) {
289
+ if (isIntegrationMode(options.mode)) {
290
+ resolved.mode = options.mode
291
+ } else {
292
+ throw new Error(`Unknown integration mode: ${options.mode}`)
293
+ }
294
+ }
295
+
296
+ return resolved
297
+ }
298
+
299
+ function isIntegrationScope(
300
+ value: string,
301
+ ): value is NonNullable<InstallIntegrationOptions['scope']> {
302
+ return (integrationScopes as readonly string[]).includes(value)
303
+ }
304
+
305
+ function isIntegrationMode(value: string): value is NonNullable<InstallIntegrationOptions['mode']> {
306
+ return (integrationModes as readonly string[]).includes(value)
307
+ }
308
+
161
309
  export async function runCli(argv: readonly string[] = process.argv): Promise<void> {
162
310
  const cli = createCli(argv)
163
311
  const parsedArgv = [...argv]
@@ -74,7 +74,11 @@ function printApplyResult(result: ApplyCommandResult): void {
74
74
  return
75
75
  }
76
76
 
77
- log.ready('Ready! Hold Alt + Click any element to inspect.')
77
+ log.ready('Ready! Inspecto is set up.')
78
+ log.info('Next:')
79
+ log.hint('1. Start or restart your dev server.')
80
+ log.hint('2. Open your app in the browser.')
81
+ log.hint('3. Hold Alt + Click any element to inspect.')
78
82
  }
79
83
 
80
84
  export async function apply(options: ApplyCommandOptions = {}): Promise<ApplyCommandResult> {
@@ -16,6 +16,7 @@ import { detectIDE } from '../detect/ide.js'
16
16
  import { detectProviders, type ProviderDetection } from '../detect/provider.js'
17
17
  import { applyOnboardingPlan } from '../onboarding/apply.js'
18
18
  import type { InitOptions, Mutation, BuildToolDetection } from '../types.js'
19
+ import { resolveOnboardingTarget } from '../onboarding/target-resolution.js'
19
20
  import {
20
21
  promptIDEChoice,
21
22
  promptProviderChoice,
@@ -70,29 +71,42 @@ export async function init(options: InitOptions): Promise<void> {
70
71
  )
71
72
 
72
73
  if (verifiedPackages.length === 0) {
73
- const monorepoTargets = Array.from(
74
- new Set(
75
- buildResult.supported.map(d => d.packagePath).filter((value): value is string => !!value),
76
- ),
77
- )
78
-
79
- if (monorepoTargets.length > 1) {
80
- log.warn('Monorepo root detected with multiple candidate apps.')
81
- const selectedPackage = await promptMonorepoPackageChoice(buildResult.supported)
82
- if (!selectedPackage) {
83
- log.hint('Run `inspecto init` inside the target app, or pass --packages <app-path>.')
84
- return
74
+ const monorepoCandidates = buildResult.supported.filter(detection => !!detection.packagePath)
75
+ if (monorepoCandidates.length > 0) {
76
+ const frameworkSupportByPackage = await detectFrameworkSupportByPackage(
77
+ repoRoot,
78
+ monorepoCandidates,
79
+ )
80
+ const targetResolution = resolveOnboardingTarget({
81
+ repoRoot,
82
+ buildTools: monorepoCandidates,
83
+ frameworkSupportByPackage,
84
+ })
85
+
86
+ if (targetResolution.status === 'needs_selection') {
87
+ log.warn('Monorepo root detected with multiple candidate apps.')
88
+ const selectedPackage = await promptMonorepoPackageChoice(
89
+ monorepoCandidates.filter(detection =>
90
+ targetResolution.candidates.some(
91
+ candidate => candidate.packagePath === (detection.packagePath ?? ''),
92
+ ),
93
+ ),
94
+ )
95
+ if (!selectedPackage) {
96
+ log.hint('Run `inspecto init` inside the target app, or pass --packages <app-path>.')
97
+ return
98
+ }
99
+
100
+ projectRoot = path.join(repoRoot, selectedPackage)
101
+ buildResult = await detectBuildTools(repoRoot, [selectedPackage])
102
+ log.info(`Continuing initialization in ${selectedPackage}`)
103
+ } else if (targetResolution.selected?.packagePath) {
104
+ const selectedPackage = targetResolution.selected.packagePath
105
+ projectRoot = path.join(repoRoot, selectedPackage)
106
+ buildResult = await detectBuildTools(repoRoot, [selectedPackage])
107
+ log.warn(`Monorepo root detected. Using the only candidate app: ${selectedPackage}`)
108
+ log.hint('Run `inspecto init` inside that app next time to skip this prompt.')
85
109
  }
86
-
87
- projectRoot = path.join(repoRoot, selectedPackage)
88
- buildResult = await detectBuildTools(repoRoot, [selectedPackage])
89
- log.info(`Continuing initialization in ${selectedPackage}`)
90
- } else if (monorepoTargets.length === 1) {
91
- const [selectedPackage] = monorepoTargets
92
- projectRoot = path.join(repoRoot, selectedPackage!)
93
- buildResult = await detectBuildTools(repoRoot, [selectedPackage!])
94
- log.warn(`Monorepo root detected. Using the only candidate app: ${selectedPackage}`)
95
- log.hint('Run `inspecto init` inside that app next time to skip this prompt.')
96
110
  }
97
111
  }
98
112
 
@@ -310,7 +324,11 @@ export async function init(options: InitOptions): Promise<void> {
310
324
  log.hint('Complete the items above.')
311
325
  log.blank()
312
326
  } else {
313
- log.ready('Ready! Hold Alt + Click any element to inspect.')
327
+ log.ready('Ready! Inspecto is set up.')
328
+ log.info('Next:')
329
+ log.hint('1. Start or restart your dev server.')
330
+ log.hint('2. Open your app in the browser.')
331
+ log.hint('3. Hold Alt + Click any element to inspect.')
314
332
  }
315
333
  }
316
334
  }
@@ -344,3 +362,22 @@ function matchesAnyPackage(detection: BuildToolDetection, packages: string[]): b
344
362
  if (packages.length === 0) return true
345
363
  return packages.some(pkg => matchesPackage(detection, pkg))
346
364
  }
365
+
366
+ async function detectFrameworkSupportByPackage(
367
+ repoRoot: string,
368
+ buildTools: BuildToolDetection[],
369
+ ): Promise<Record<string, string[]>> {
370
+ const packagePaths = Array.from(
371
+ new Set(buildTools.map(buildTool => buildTool.packagePath).filter((value): value is string => !!value)),
372
+ )
373
+ const supportByPackage: Record<string, string[]> = {}
374
+
375
+ await Promise.all(
376
+ packagePaths.map(async packagePath => {
377
+ const result = await detectFrameworks(path.join(repoRoot, packagePath))
378
+ supportByPackage[packagePath] = result.supported
379
+ }),
380
+ )
381
+
382
+ return supportByPackage
383
+ }