@inspecto-dev/cli 0.2.0-alpha.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.
@@ -0,0 +1,140 @@
1
+ // ============================================================
2
+ // src/inject/extension.ts — VS Code extension auto-installer
3
+ // ============================================================
4
+ //
5
+ // Waterfall degradation strategy:
6
+ // Level 1: `code --install-extension` (code in PATH)
7
+ // Level 2: Find VS Code binary at known paths
8
+ // Level 3: Open `vscode:extension/` URI scheme
9
+ // Level 4: Print manual installation instructions
10
+ // ============================================================
11
+
12
+ import path from 'node:path'
13
+ import { which, run, shell } from '../utils/exec.js'
14
+ import { exists } from '../utils/fs.js'
15
+ import { log } from '../utils/logger.js'
16
+ import type { Mutation } from '../types.js'
17
+
18
+ const EXTENSION_ID = 'inspecto.inspecto'
19
+
20
+ /** Known VS Code binary locations by platform */
21
+ const VSCODE_PATHS: Record<string, string[]> = {
22
+ darwin: [
23
+ '/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code',
24
+ '/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code-insiders',
25
+ `${process.env.HOME}/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code`,
26
+ ],
27
+ linux: ['/usr/bin/code', '/usr/share/code/bin/code', '/snap/bin/code', '/usr/bin/code-insiders'],
28
+ win32: [
29
+ `${process.env.LOCALAPPDATA}\\Programs\\Microsoft VS Code\\bin\\code.cmd`,
30
+ `${process.env.LOCALAPPDATA}\\Programs\\Microsoft VS Code Insiders\\bin\\code-insiders.cmd`,
31
+ `${process.env.PROGRAMFILES}\\Microsoft VS Code\\bin\\code.cmd`,
32
+ ],
33
+ }
34
+
35
+ /** Try to find the VS Code binary at known filesystem paths */
36
+ async function findVSCodeBinary(): Promise<string | null> {
37
+ const platform = process.platform
38
+ const candidates = VSCODE_PATHS[platform] || []
39
+
40
+ for (const candidate of candidates) {
41
+ if (await exists(candidate)) {
42
+ return candidate
43
+ }
44
+ }
45
+
46
+ // Also check for code-insiders in PATH
47
+ if (await which('code-insiders')) {
48
+ return 'code-insiders'
49
+ }
50
+
51
+ return null
52
+ }
53
+
54
+ /** Try to open a URI using the system default handler */
55
+ async function tryOpenURI(uri: string): Promise<boolean> {
56
+ try {
57
+ const platform = process.platform
58
+ const openCmd = platform === 'darwin' ? 'open' : platform === 'win32' ? 'start' : 'xdg-open'
59
+
60
+ await shell(`${openCmd} "${uri}"`)
61
+ return true
62
+ } catch {
63
+ return false
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Attempt to install the VS Code extension using waterfall degradation.
69
+ */
70
+ export async function installExtension(dryRun: boolean): Promise<Mutation | null> {
71
+ if (dryRun) {
72
+ log.dryRun('Would attempt to install VS Code extension')
73
+ return null
74
+ }
75
+
76
+ // Level 1: Direct `code` command
77
+ if (await which('code')) {
78
+ try {
79
+ await run('code', ['--install-extension', EXTENSION_ID])
80
+ log.success('VS Code extension installed via CLI')
81
+ return { type: 'extension_installed', id: EXTENSION_ID }
82
+ } catch {
83
+ // Fall through to next level
84
+ }
85
+ }
86
+
87
+ // Level 2: Find VS Code binary at known paths
88
+ const codePath = await findVSCodeBinary()
89
+ if (codePath) {
90
+ try {
91
+ await run(codePath, ['--install-extension', EXTENSION_ID])
92
+ log.success('VS Code extension installed via binary path')
93
+ log.info('Tip: Add "code" to your PATH to help Inspecto detect other AI tools in the future')
94
+ return { type: 'extension_installed', id: EXTENSION_ID }
95
+ } catch {
96
+ // Fall through to next level
97
+ }
98
+ }
99
+
100
+ // Level 3: URI scheme
101
+ const uri = `vscode:extension/${EXTENSION_ID}`
102
+ if (await tryOpenURI(uri)) {
103
+ log.warn('Opened extension page in VS Code')
104
+ log.hint('Please click "Install" in the opened VS Code window to complete setup.')
105
+ return { type: 'extension_installed', id: EXTENSION_ID, manual_action_required: true }
106
+ }
107
+
108
+ // Level 4: Manual fallback
109
+ log.warn('Could not auto-install VS Code extension')
110
+ log.hint('Please install it manually to enable Inspector features:')
111
+ log.hint(' 1. Open VS Code')
112
+ log.hint(' 2. Press Ctrl+Shift+X (or Cmd+Shift+X)')
113
+ log.hint(' 3. Search for "Inspecto"')
114
+ log.hint(` Or visit: https://marketplace.visualstudio.com/items?itemName=${EXTENSION_ID}`)
115
+ return null
116
+ }
117
+
118
+ /**
119
+ * Check if the extension is already installed.
120
+ */
121
+ export async function isExtensionInstalled(): Promise<boolean> {
122
+ try {
123
+ // Try `code --list-extensions` first
124
+ if (await which('code')) {
125
+ const { stdout } = await run('code', ['--list-extensions'])
126
+ return stdout.toLowerCase().includes(EXTENSION_ID)
127
+ }
128
+
129
+ // Try known binary path
130
+ const codePath = await findVSCodeBinary()
131
+ if (codePath) {
132
+ const { stdout } = await run(codePath, ['--list-extensions'])
133
+ return stdout.toLowerCase().includes(EXTENSION_ID)
134
+ }
135
+
136
+ return false
137
+ } catch {
138
+ return false
139
+ }
140
+ }
@@ -0,0 +1,76 @@
1
+ // ============================================================
2
+ // src/inject/gitignore.ts — .gitignore management
3
+ // ============================================================
4
+ import path from 'node:path'
5
+ import { readFile, writeFile } from '../utils/fs.js'
6
+ import { log } from '../utils/logger.js'
7
+
8
+ /** Rules for default mode: fine-grained rules only */
9
+ const DEFAULT_RULES = ['.inspecto/install.lock', '.inspecto/cache.json', '.inspecto/*.local.json']
10
+
11
+ /** Rules for shared mode: same as default in current design to allow settings.json */
12
+ const SHARED_RULES = ['.inspecto/install.lock', '.inspecto/cache.json', '.inspecto/*.local.json']
13
+
14
+ /**
15
+ * Update .gitignore based on the init mode.
16
+ *
17
+ * Handles mode switching: if switching from default → shared,
18
+ * replaces the broad `.inspecto/` rule with fine-grained rules.
19
+ */
20
+ export async function updateGitignore(
21
+ root: string,
22
+ shared: boolean,
23
+ dryRun: boolean,
24
+ ): Promise<void> {
25
+ const gitignorePath = path.join(root, '.gitignore')
26
+ let content = (await readFile(gitignorePath)) ?? ''
27
+
28
+ const desiredRules = shared ? SHARED_RULES : DEFAULT_RULES
29
+ const hasGlobalRule = content.match(/^\.inspecto\/\s*$/m) !== null
30
+
31
+ // Mode switch: If the user previously had the broad `.inspecto/` rule, replace it
32
+ if (hasGlobalRule) {
33
+ content = content.replace(/^\.inspecto\/\s*$/gm, SHARED_RULES.join('\n'))
34
+ if (!dryRun) {
35
+ await writeFile(gitignorePath, content)
36
+ }
37
+ log.success('Updated .gitignore: .inspecto/settings.json is now trackable')
38
+ return
39
+ }
40
+
41
+ // Check if rules already exist
42
+ const missingRules = desiredRules.filter(rule => !content.includes(rule))
43
+ if (missingRules.length === 0) {
44
+ return // Already configured
45
+ }
46
+
47
+ // Append rules
48
+ const section = '\n# Inspecto\n' + missingRules.join('\n') + '\n'
49
+ content = content.trimEnd() + '\n' + section
50
+
51
+ if (dryRun) {
52
+ log.dryRun(`Would update .gitignore with: ${missingRules.join(', ')}`)
53
+ } else {
54
+ await writeFile(gitignorePath, content)
55
+ log.success('Updated .gitignore')
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Remove all Inspecto-related entries from .gitignore.
61
+ */
62
+ export async function cleanGitignore(root: string): Promise<void> {
63
+ const gitignorePath = path.join(root, '.gitignore')
64
+ const content = await readFile(gitignorePath)
65
+ if (!content) return
66
+
67
+ const cleaned = content
68
+ .replace(/^# Inspecto\s*$/m, '')
69
+ .replace(/^\.inspecto\/?\s*$/gm, '')
70
+ .replace(/^\.inspecto\/install\.lock\s*$/gm, '')
71
+ .replace(/^\.inspecto\/cache\.json\s*$/gm, '')
72
+ .replace(/^\.inspecto\/\*\.local\.json\s*$/gm, '')
73
+ .replace(/\n{3,}/g, '\n\n') // Collapse excess blank lines
74
+
75
+ await writeFile(gitignorePath, cleaned)
76
+ }
package/src/types.ts ADDED
@@ -0,0 +1,48 @@
1
+ // ============================================================
2
+ // src/types.ts — Shared type definitions
3
+ // ============================================================
4
+
5
+ /** Package manager detection result */
6
+ export type PackageManager = 'bun' | 'pnpm' | 'yarn' | 'npm'
7
+
8
+ /** Supported build tools (v1) */
9
+ export type BuildTool = 'vite' | 'webpack' | 'rspack' | 'rsbuild' | 'esbuild' | 'rollup'
10
+
11
+ /** Detected build tool with its config path */
12
+ export interface BuildToolDetection {
13
+ tool: BuildTool
14
+ configPath: string
15
+ /** Human-readable label like "Vite (vite.config.ts)" */
16
+ label: string
17
+ /** Whether this is a legacy rspack version (< 0.4.0) */
18
+ isLegacyRspack?: boolean
19
+ }
20
+
21
+ /** Options passed to `inspecto init` */
22
+ export interface InitOptions {
23
+ shared: boolean
24
+ skipInstall: boolean
25
+ dryRun: boolean
26
+ prefer?: string
27
+ noExtension: boolean
28
+ packages?: string[]
29
+ }
30
+
31
+ /** A single mutation recorded in install.lock */
32
+ export interface Mutation {
33
+ type: 'file_modified' | 'file_created' | 'dependency_added' | 'extension_installed'
34
+ path?: string
35
+ backup?: string
36
+ name?: string
37
+ id?: string
38
+ dev?: boolean
39
+ description?: string
40
+ manual_action_required?: boolean
41
+ }
42
+
43
+ /** Structure of .inspecto/install.lock */
44
+ export interface InstallLock {
45
+ version: string
46
+ created_at: string
47
+ mutations: Mutation[]
48
+ }
@@ -0,0 +1,44 @@
1
+ // ============================================================
2
+ // src/utils/exec.ts — Shell execution helpers
3
+ // ============================================================
4
+ import { execFile, exec as execCb } from 'node:child_process'
5
+ import { promisify } from 'node:util'
6
+
7
+ const execFileAsync = promisify(execFile)
8
+ const execAsync = promisify(execCb)
9
+
10
+ export interface ExecResult {
11
+ stdout: string
12
+ stderr: string
13
+ }
14
+
15
+ /** Run a command and return stdout/stderr. Throws on non-zero exit. */
16
+ export async function run(command: string, args: string[], cwd?: string): Promise<ExecResult> {
17
+ const result = await execFileAsync(command, args, {
18
+ cwd,
19
+ timeout: 60_000,
20
+ env: { ...process.env },
21
+ })
22
+ return { stdout: result.stdout ?? '', stderr: result.stderr ?? '' }
23
+ }
24
+
25
+ /** Run a shell command string. Throws on non-zero exit. */
26
+ export async function shell(command: string, cwd?: string): Promise<ExecResult> {
27
+ const result = await execAsync(command, {
28
+ cwd,
29
+ timeout: 60_000,
30
+ env: { ...process.env },
31
+ })
32
+ return { stdout: result.stdout ?? '', stderr: result.stderr ?? '' }
33
+ }
34
+
35
+ /** Check if a binary exists in PATH */
36
+ export async function which(bin: string): Promise<boolean> {
37
+ try {
38
+ const cmd = process.platform === 'win32' ? 'where' : 'which'
39
+ await run(cmd, [bin])
40
+ return true
41
+ } catch {
42
+ return false
43
+ }
44
+ }
@@ -0,0 +1,69 @@
1
+ // ============================================================
2
+ // src/utils/fs.ts — File system helpers
3
+ // ============================================================
4
+ import fs from 'node:fs/promises'
5
+ import path from 'node:path'
6
+
7
+ /** Check if a file or directory exists */
8
+ export async function exists(filePath: string): Promise<boolean> {
9
+ try {
10
+ await fs.access(filePath)
11
+ return true
12
+ } catch {
13
+ return false
14
+ }
15
+ }
16
+
17
+ /** Read file as UTF-8 text, return null if not found */
18
+ export async function readFile(filePath: string): Promise<string | null> {
19
+ try {
20
+ return await fs.readFile(filePath, 'utf-8')
21
+ } catch {
22
+ return null
23
+ }
24
+ }
25
+
26
+ /** Write file with auto-created parent directories */
27
+ export async function writeFile(filePath: string, content: string): Promise<void> {
28
+ await fs.mkdir(path.dirname(filePath), { recursive: true })
29
+ await fs.writeFile(filePath, content, 'utf-8')
30
+ }
31
+
32
+ /** Copy file (for backup) */
33
+ export async function copyFile(src: string, dest: string): Promise<void> {
34
+ await fs.copyFile(src, dest)
35
+ }
36
+
37
+ /** Delete file, no error if missing */
38
+ export async function removeFile(filePath: string): Promise<void> {
39
+ try {
40
+ await fs.unlink(filePath)
41
+ } catch {
42
+ // ignore
43
+ }
44
+ }
45
+
46
+ /** Delete directory recursively, no error if missing */
47
+ export async function removeDir(dirPath: string): Promise<void> {
48
+ try {
49
+ await fs.rm(dirPath, { recursive: true, force: true })
50
+ } catch {
51
+ // ignore
52
+ }
53
+ }
54
+
55
+ /** Read and parse JSON file */
56
+ export async function readJSON<T = unknown>(filePath: string): Promise<T | null> {
57
+ const text = await readFile(filePath)
58
+ if (!text) return null
59
+ try {
60
+ return JSON.parse(text) as T
61
+ } catch {
62
+ return null
63
+ }
64
+ }
65
+
66
+ /** Write JSON file with 2-space indent */
67
+ export async function writeJSON(filePath: string, data: unknown): Promise<void> {
68
+ await writeFile(filePath, JSON.stringify(data, null, 2) + '\n')
69
+ }
@@ -0,0 +1,64 @@
1
+ // ============================================================
2
+ // src/utils/logger.ts — Colored terminal output
3
+ // ============================================================
4
+ import pc from 'picocolors'
5
+
6
+ export const log = {
7
+ /** Section header */
8
+ header(text: string) {
9
+ console.log()
10
+ console.log(` ${pc.bold(pc.cyan('✦'))} ${pc.bold(text)}`)
11
+ console.log()
12
+ },
13
+
14
+ /** Info item (used for actionable but not fully successful states) */
15
+ info(text: string) {
16
+ console.log(` ${pc.blue('ℹ')} ${text}`)
17
+ },
18
+ success(text: string) {
19
+ console.log(` ${pc.green('✔')} ${text}`)
20
+ },
21
+
22
+ /** Warning item */
23
+ warn(text: string) {
24
+ console.log(` ${pc.yellow('⚠')} ${text}`)
25
+ },
26
+
27
+ /** Error item */
28
+ error(text: string) {
29
+ console.log(` ${pc.red('✘')} ${text}`)
30
+ },
31
+
32
+ /** Indented hint line */
33
+ hint(text: string) {
34
+ console.log(` ${pc.dim('→')} ${text}`)
35
+ },
36
+
37
+ /** Blank line */
38
+ blank() {
39
+ console.log()
40
+ },
41
+
42
+ /** Final ready message */
43
+ ready(text: string) {
44
+ console.log()
45
+ console.log(` ${pc.bold(pc.green('⚡'))} ${pc.bold(text)}`)
46
+ console.log()
47
+ },
48
+
49
+ /** Code block for manual instructions */
50
+ codeBlock(lines: string[]) {
51
+ console.log()
52
+ console.log(` ${pc.dim('┌─────────────────────────────────────────────────┐')}`)
53
+ for (const line of lines) {
54
+ console.log(` ${pc.dim('│')} ${line.padEnd(48)}${pc.dim('│')}`)
55
+ }
56
+ console.log(` ${pc.dim('└─────────────────────────────────────────────────┘')}`)
57
+ console.log()
58
+ },
59
+
60
+ /** Dry-run prefix */
61
+ dryRun(text: string) {
62
+ console.log(` ${pc.blue('[dry-run]')} ${text}`)
63
+ },
64
+ }
@@ -0,0 +1,65 @@
1
+ import { describe, it, expect, vi } from 'vitest'
2
+ import { detectFrameworks } from '../src/detect/framework.js'
3
+ import * as fsUtils from '../src/utils/fs.js'
4
+
5
+ vi.mock('../src/utils/fs.js', () => ({
6
+ readJSON: vi.fn(),
7
+ }))
8
+
9
+ describe('detectFrameworks', () => {
10
+ it('detects React based on dependencies', async () => {
11
+ vi.mocked(fsUtils.readJSON).mockResolvedValue({
12
+ dependencies: {
13
+ react: '^18.0.0',
14
+ },
15
+ })
16
+
17
+ const result = await detectFrameworks('/mock/root')
18
+ expect(result.supported).toContain('react')
19
+ expect(result.unsupported).toHaveLength(0)
20
+ })
21
+
22
+ it('detects Vue based on dependencies', async () => {
23
+ vi.mocked(fsUtils.readJSON).mockResolvedValue({
24
+ devDependencies: {
25
+ vue: '^3.0.0',
26
+ },
27
+ })
28
+
29
+ const result = await detectFrameworks('/mock/root')
30
+ expect(result.supported).toContain('vue')
31
+ expect(result.unsupported).toHaveLength(0)
32
+ })
33
+
34
+ it('detects Svelte as unsupported framework', async () => {
35
+ vi.mocked(fsUtils.readJSON).mockResolvedValue({
36
+ devDependencies: {
37
+ svelte: '^4.0.0',
38
+ },
39
+ })
40
+
41
+ const result = await detectFrameworks('/mock/root')
42
+ expect(result.supported).toHaveLength(0)
43
+ expect(result.unsupported).toContainEqual({ name: 'Svelte', dep: 'svelte' })
44
+ })
45
+
46
+ it('returns empty if no framework is matched', async () => {
47
+ vi.mocked(fsUtils.readJSON).mockResolvedValue({
48
+ dependencies: {
49
+ lodash: '^4.0.0',
50
+ },
51
+ })
52
+
53
+ const result = await detectFrameworks('/mock/root')
54
+ expect(result.supported).toHaveLength(0)
55
+ expect(result.unsupported).toHaveLength(0)
56
+ })
57
+
58
+ it('returns empty if package.json does not exist', async () => {
59
+ vi.mocked(fsUtils.readJSON).mockResolvedValue(null)
60
+
61
+ const result = await detectFrameworks('/mock/root')
62
+ expect(result.supported).toHaveLength(0)
63
+ expect(result.unsupported).toHaveLength(0)
64
+ })
65
+ })
@@ -0,0 +1,94 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
2
+ import { detectIDE } from '../src/detect/ide.js'
3
+ import * as fsUtils from '../src/utils/fs.js'
4
+
5
+ vi.mock('../src/utils/fs.js', () => ({
6
+ exists: vi.fn(),
7
+ }))
8
+
9
+ describe('detectIDE', () => {
10
+ const originalEnv = process.env
11
+
12
+ beforeEach(() => {
13
+ vi.resetAllMocks()
14
+ process.env = { ...originalEnv } // Make a copy
15
+ })
16
+
17
+ afterEach(() => {
18
+ process.env = originalEnv
19
+ })
20
+
21
+ it('detects Trae from environment variables', async () => {
22
+ process.env.TRAE_APP_DIR = '/Applications/Trae.app'
23
+ vi.mocked(fsUtils.exists).mockResolvedValue(false)
24
+
25
+ const result = await detectIDE('/mock/root')
26
+ expect(result.detected).toEqual([{ ide: 'Trae', supported: false }])
27
+ })
28
+
29
+ it('detects Cursor from environment variables', async () => {
30
+ process.env.CURSOR_CHANNEL = 'stable'
31
+ vi.mocked(fsUtils.exists).mockResolvedValue(false)
32
+
33
+ const result = await detectIDE('/mock/root')
34
+ expect(result.detected).toEqual([{ ide: 'Cursor', supported: false }])
35
+ })
36
+
37
+ it('detects VS Code from environment variables without false positives', async () => {
38
+ process.env.TERM_PROGRAM = 'vscode'
39
+ // Ensure it's not Trae
40
+ delete process.env.TRAE_APP_DIR
41
+ delete process.env.CURSOR_CHANNEL
42
+ delete process.env.__CFBundleIdentifier
43
+ delete process.env.COCO_IDE_PLUGIN_TYPE
44
+ process.env.npm_config_user_agent = 'npm'
45
+
46
+ vi.mocked(fsUtils.exists).mockResolvedValue(false)
47
+
48
+ const result = await detectIDE('/mock/root')
49
+ expect(result.detected).toEqual([{ ide: 'vscode', supported: true }])
50
+ })
51
+
52
+ it('detects VS Code from directory when environment is clean', async () => {
53
+ // Clear all related env vars
54
+ delete process.env.TERM_PROGRAM
55
+ delete process.env.TRAE_APP_DIR
56
+ delete process.env.CURSOR_CHANNEL
57
+ delete process.env.__CFBundleIdentifier
58
+ delete process.env.COCO_IDE_PLUGIN_TYPE
59
+ delete process.env.npm_config_user_agent
60
+
61
+ vi.mocked(fsUtils.exists).mockImplementation(async path => {
62
+ return path.endsWith('.vscode')
63
+ })
64
+
65
+ const result = await detectIDE('/mock/root')
66
+ expect(result.detected).toEqual([{ ide: 'vscode', supported: true }])
67
+ })
68
+
69
+ it('detects JetBrains from directory', async () => {
70
+ delete process.env.TERM_PROGRAM
71
+ delete process.env.TRAE_APP_DIR
72
+ delete process.env.CURSOR_CHANNEL
73
+ delete process.env.__CFBundleIdentifier
74
+ delete process.env.COCO_IDE_PLUGIN_TYPE
75
+
76
+ vi.mocked(fsUtils.exists).mockImplementation(async path => {
77
+ return path.endsWith('.idea')
78
+ })
79
+
80
+ const result = await detectIDE('/mock/root')
81
+ expect(result.detected).toEqual([{ ide: 'JetBrains IDE', supported: false }])
82
+ })
83
+
84
+ it('favors Trae if both VS Code TERM_PROGRAM and Trae env are present', async () => {
85
+ process.env.TERM_PROGRAM = 'vscode'
86
+ process.env.TRAE_APP_DIR = '/Applications/Trae.app'
87
+
88
+ vi.mocked(fsUtils.exists).mockResolvedValue(false)
89
+
90
+ const result = await detectIDE('/mock/root')
91
+ // VS Code shouldn't be added since it shares TERM_PROGRAM with Trae
92
+ expect(result.detected).toEqual([{ ide: 'Trae', supported: false }])
93
+ })
94
+ })
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src",
6
+ "target": "ESNext",
7
+ "module": "NodeNext",
8
+ "moduleResolution": "NodeNext",
9
+ "esModuleInterop": true,
10
+ "strict": true
11
+ },
12
+ "include": ["src/**/*", "bin/**/*"]
13
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'tsup'
2
+
3
+ export default defineConfig({
4
+ entry: ['src/index.ts', 'src/bin.ts'],
5
+ format: ['esm'],
6
+ target: 'node18',
7
+ dts: true,
8
+ clean: true,
9
+ minify: false,
10
+ })