@inspecto-dev/cli 0.3.5 → 0.3.7

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/src/detect/ide.ts CHANGED
@@ -31,8 +31,14 @@ export async function detectIDE(root: string): Promise<IDEProbeResult> {
31
31
  detected.set('Cursor', { ide: 'cursor', supported: true })
32
32
  }
33
33
 
34
- // Trae
34
+ // Trae CN
35
35
  if (
36
+ process.env.__CFBundleIdentifier === 'com.byteocean.trae.cn' ||
37
+ process.env.COCO_IDE_PLUGIN_TYPE === 'TraeCN' ||
38
+ (process.env.npm_config_user_agent && process.env.npm_config_user_agent.includes('trae-cn'))
39
+ ) {
40
+ detected.set('Trae CN', { ide: 'trae-cn', supported: true })
41
+ } else if (
36
42
  process.env.TRAE_APP_DIR ||
37
43
  process.env.__CFBundleIdentifier === 'com.byteocean.trae' ||
38
44
  process.env.COCO_IDE_PLUGIN_TYPE === 'Trae' ||
@@ -41,6 +47,21 @@ export async function detectIDE(root: string): Promise<IDEProbeResult> {
41
47
  detected.set('Trae', { ide: 'trae', supported: true })
42
48
  }
43
49
 
50
+ // CodeBuddy CN
51
+ if (
52
+ process.env.__CFBundleIdentifier === 'ai.codebuddy.mac.cn' ||
53
+ process.env.COCO_IDE_PLUGIN_TYPE === 'CodeBuddyCN' ||
54
+ (process.env.npm_config_user_agent && process.env.npm_config_user_agent.includes('codebuddy-cn'))
55
+ ) {
56
+ detected.set('CodeBuddy CN', { ide: 'codebuddy-cn', supported: true })
57
+ } else if (
58
+ process.env.__CFBundleIdentifier === 'ai.codebuddy.mac' ||
59
+ process.env.COCO_IDE_PLUGIN_TYPE === 'CodeBuddy' ||
60
+ (process.env.npm_config_user_agent && process.env.npm_config_user_agent.includes('codebuddy'))
61
+ ) {
62
+ detected.set('CodeBuddy', { ide: 'codebuddy', supported: true })
63
+ }
64
+
44
65
  // Zed
45
66
  if (process.env.ZED_TERM) {
46
67
  detected.set('Zed', { ide: 'Zed', supported: false })
@@ -62,11 +83,14 @@ export async function detectIDE(root: string): Promise<IDEProbeResult> {
62
83
  // if (process.env.TERM_PROGRAM === 'vscode') { ... }
63
84
 
64
85
  // 2. Check Directory Artifacts (Indicates project has been opened in these IDEs)
65
- const [hasTrae, hasCursor, hasVscode, hasIdea] = await Promise.all([
86
+ const [hasTrae, hasTraeCn, hasCursor, hasVscode, hasIdea, hasCodeBuddy, hasCodeBuddyCn] = await Promise.all([
66
87
  exists(path.join(root, '.trae')),
88
+ exists(path.join(root, '.trae-cn')),
67
89
  exists(path.join(root, '.cursor')),
68
90
  exists(path.join(root, '.vscode')),
69
91
  exists(path.join(root, '.idea')),
92
+ exists(path.join(root, '.codebuddy')),
93
+ exists(path.join(root, '.codebuddy-cn')),
70
94
  ])
71
95
 
72
96
  // If a directory artifact exists, add it to the detection list.
@@ -74,6 +98,15 @@ export async function detectIDE(root: string): Promise<IDEProbeResult> {
74
98
  if (hasTrae && !detected.has('Trae')) {
75
99
  detected.set('Trae', { ide: 'trae', supported: true })
76
100
  }
101
+ if (hasTraeCn && !detected.has('Trae CN')) {
102
+ detected.set('Trae CN', { ide: 'trae-cn', supported: true })
103
+ }
104
+ if (hasCodeBuddy && !detected.has('CodeBuddy')) {
105
+ detected.set('CodeBuddy', { ide: 'codebuddy', supported: true })
106
+ }
107
+ if (hasCodeBuddyCn && !detected.has('CodeBuddy CN')) {
108
+ detected.set('CodeBuddy CN', { ide: 'codebuddy-cn', supported: true })
109
+ }
77
110
  if (hasCursor && !detected.has('Cursor')) {
78
111
  detected.set('Cursor', { ide: 'cursor', supported: true })
79
112
  }
@@ -256,6 +256,36 @@ export async function installExtension(
256
256
  }
257
257
  }
258
258
 
259
+ if (ide === 'codebuddy' && process.platform === 'darwin') {
260
+ const codebuddyPath = await findIdeBinary('codebuddy')
261
+ if (codebuddyPath) {
262
+ const result = await installAlternativeIdeExtension(
263
+ codebuddyPath,
264
+ getHostIdeLabel('codebuddy'),
265
+ extensionRef,
266
+ quiet,
267
+ )
268
+ if (result) {
269
+ return result
270
+ }
271
+ }
272
+ }
273
+
274
+ if (ide === 'codebuddy-cn' && process.platform === 'darwin') {
275
+ const codebuddyCnPath = await findIdeBinary('codebuddy-cn')
276
+ if (codebuddyCnPath) {
277
+ const result = await installAlternativeIdeExtension(
278
+ codebuddyCnPath,
279
+ getHostIdeLabel('codebuddy-cn'),
280
+ extensionRef,
281
+ quiet,
282
+ )
283
+ if (result) {
284
+ return result
285
+ }
286
+ }
287
+ }
288
+
259
289
  // Other IDEs: Prompt to install via VSIX
260
290
  if (!quiet) {
261
291
  log.warn(`Could not auto-install extension for ${ide}`)
@@ -94,6 +94,44 @@ export const HOST_IDE_CAPABILITIES: Record<SupportedHostIde, HostIdeCapability>
94
94
  ],
95
95
  },
96
96
  },
97
+ codebuddy: {
98
+ label: 'CodeBuddy',
99
+ artifactDir: '.codebuddy',
100
+ extensionDir: '.codebuddy/extensions',
101
+ binaryName: 'codebuddy',
102
+ binaryPaths: {
103
+ darwin: [
104
+ '/Applications/CodeBuddy.app/Contents/Resources/app/bin/codebuddy',
105
+ '/Applications/CodeBuddy.app/Contents/Resources/app/bin/code',
106
+ `${process.env.HOME}/Applications/CodeBuddy.app/Contents/Resources/app/bin/codebuddy`,
107
+ `${process.env.HOME}/Applications/CodeBuddy.app/Contents/Resources/app/bin/code`,
108
+ ],
109
+ linux: ['/usr/bin/codebuddy', '/opt/CodeBuddy/resources/app/bin/codebuddy'],
110
+ win32: [
111
+ `${process.env.LOCALAPPDATA}\\Programs\\CodeBuddy\\resources\\app\\bin\\codebuddy.cmd`,
112
+ `${process.env.PROGRAMFILES}\\CodeBuddy\\resources\\app\\bin\\codebuddy.cmd`,
113
+ ],
114
+ },
115
+ },
116
+ 'codebuddy-cn': {
117
+ label: 'CodeBuddy CN',
118
+ artifactDir: '.codebuddy-cn',
119
+ extensionDir: '.codebuddy-cn/extensions',
120
+ binaryName: 'codebuddy-cn',
121
+ binaryPaths: {
122
+ darwin: [
123
+ '/Applications/CodeBuddy CN.app/Contents/Resources/app/bin/codebuddy-cn',
124
+ '/Applications/CodeBuddy CN.app/Contents/Resources/app/bin/code',
125
+ `${process.env.HOME}/Applications/CodeBuddy CN.app/Contents/Resources/app/bin/codebuddy-cn`,
126
+ `${process.env.HOME}/Applications/CodeBuddy CN.app/Contents/Resources/app/bin/code`,
127
+ ],
128
+ linux: ['/usr/bin/codebuddy-cn', '/opt/CodeBuddy CN/resources/app/bin/codebuddy-cn'],
129
+ win32: [
130
+ `${process.env.LOCALAPPDATA}\\Programs\\CodeBuddy CN\\resources\\app\\bin\\codebuddy-cn.cmd`,
131
+ `${process.env.PROGRAMFILES}\\CodeBuddy CN\\resources\\app\\bin\\codebuddy-cn.cmd`,
132
+ ],
133
+ },
134
+ },
97
135
  }
98
136
 
99
137
  export { HOST_IDE_IDS, getHostIdeLabel, isSupportedHostIde }
@@ -177,6 +177,36 @@ describe('installExtension', () => {
177
177
  expect(logMock.success).toHaveBeenCalledWith('Trae CN extension already installed')
178
178
  expect(logMock.warn).not.toHaveBeenCalledWith('Could not auto-install extension for trae-cn')
179
179
  })
180
+
181
+ it('installs the CodeBuddy CN extension via the app bundle code launcher on macOS when available', async () => {
182
+ vi.spyOn(process, 'platform', 'get').mockReturnValue('darwin')
183
+ whichMock.mockResolvedValue(false)
184
+ existsMock.mockImplementation(async filePath => {
185
+ return filePath === '/Applications/CodeBuddy CN.app/Contents/Resources/app/bin/code'
186
+ })
187
+ runMock.mockImplementation(async (_command, args: string[]) => {
188
+ if (args[0] === '--list-extensions') {
189
+ return { stdout: '', stderr: '' }
190
+ }
191
+ if (args[0] === '--install-extension') {
192
+ return { stdout: '', stderr: '' }
193
+ }
194
+ throw new Error(`unexpected args: ${args.join(' ')}`)
195
+ })
196
+
197
+ const { installExtension } = await import('../src/inject/extension.js')
198
+
199
+ await expect(installExtension(false, 'codebuddy-cn')).resolves.toMatchObject({
200
+ type: 'extension_installed',
201
+ id: 'inspecto.inspecto',
202
+ })
203
+
204
+ expect(runMock).toHaveBeenCalledWith(
205
+ '/Applications/CodeBuddy CN.app/Contents/Resources/app/bin/code',
206
+ ['--install-extension', 'inspecto.inspecto', '--force'],
207
+ )
208
+ expect(logMock.success).toHaveBeenCalledWith('CodeBuddy CN extension installed via CLI')
209
+ })
180
210
  })
181
211
 
182
212
  describe('openIdeWorkspace', () => {
@@ -202,4 +232,22 @@ describe('openIdeWorkspace', () => {
202
232
  ['--new-window', '/repo/app'],
203
233
  )
204
234
  })
235
+
236
+ it('opens CodeBuddy in a new window via the app bundle code launcher when available', async () => {
237
+ vi.spyOn(process, 'platform', 'get').mockReturnValue('darwin')
238
+ whichMock.mockResolvedValue(false)
239
+ existsMock.mockImplementation(async filePath => {
240
+ return filePath === '/Applications/CodeBuddy.app/Contents/Resources/app/bin/code'
241
+ })
242
+ runMock.mockResolvedValue({ stdout: '', stderr: '' })
243
+
244
+ const { openIdeWorkspace } = await import('../src/inject/extension.js')
245
+
246
+ await expect(openIdeWorkspace('codebuddy', '/repo/app')).resolves.toBe(true)
247
+
248
+ expect(runMock).toHaveBeenCalledWith(
249
+ '/Applications/CodeBuddy.app/Contents/Resources/app/bin/code',
250
+ ['--new-window', '/repo/app'],
251
+ )
252
+ })
205
253
  })
@@ -335,7 +335,7 @@ describe('runIntegrationAutomation', () => {
335
335
  'Step 2/6: Could not confidently resolve the host IDE',
336
336
  )
337
337
  expect(logMock.hint).toHaveBeenCalledWith(
338
- 'Re-run with --host-ide <vscode|cursor|trae|trae-cn> or run the command from the target IDE terminal to continue automatic setup.',
338
+ 'Re-run with --host-ide <vscode|cursor|trae|trae-cn|codebuddy|codebuddy-cn> or run the command from the target IDE terminal to continue automatic setup.',
339
339
  )
340
340
  })
341
341
 
@@ -57,6 +57,21 @@ describe('resolveIntegrationHostIde', () => {
57
57
  })
58
58
  })
59
59
 
60
+ it('accepts codebuddy as an explicit ide argument', async () => {
61
+ const { resolveIntegrationHostIde } = await import('../src/commands/integration-host-ide.js')
62
+
63
+ await expect(
64
+ resolveIntegrationHostIde({
65
+ explicitIde: 'codebuddy',
66
+ cwd: '/repo',
67
+ }),
68
+ ).resolves.toMatchObject({
69
+ ide: 'codebuddy',
70
+ confidence: 'high',
71
+ source: 'explicit',
72
+ })
73
+ })
74
+
60
75
  it('uses .inspecto settings ide when present', async () => {
61
76
  vi.mocked(fsUtils.readJSON).mockImplementation(async filePath => {
62
77
  if (filePath === '/repo/.inspecto/settings.local.json') {
@@ -99,6 +114,27 @@ describe('resolveIntegrationHostIde', () => {
99
114
  })
100
115
  })
101
116
 
117
+ it('uses codebuddy-cn from .inspecto settings when present', async () => {
118
+ vi.mocked(fsUtils.readJSON).mockImplementation(async filePath => {
119
+ if (filePath === '/repo/.inspecto/settings.local.json') {
120
+ return { ide: 'codebuddy-cn' }
121
+ }
122
+ return null
123
+ })
124
+
125
+ const { resolveIntegrationHostIde } = await import('../src/commands/integration-host-ide.js')
126
+
127
+ await expect(
128
+ resolveIntegrationHostIde({
129
+ cwd: '/repo',
130
+ }),
131
+ ).resolves.toMatchObject({
132
+ ide: 'codebuddy-cn',
133
+ confidence: 'high',
134
+ source: 'config',
135
+ })
136
+ })
137
+
102
138
  it('treats a single env-detected ide as high confidence', async () => {
103
139
  process.env.CURSOR_CHANNEL = 'stable'
104
140
 
@@ -151,6 +187,24 @@ describe('resolveIntegrationHostIde', () => {
151
187
  })
152
188
  })
153
189
 
190
+ it('treats a .codebuddy-cn project artifact as medium confidence', async () => {
191
+ vi.mocked(fsUtils.exists).mockImplementation(async filePath => {
192
+ return filePath === '/repo/.codebuddy-cn'
193
+ })
194
+
195
+ const { resolveIntegrationHostIde } = await import('../src/commands/integration-host-ide.js')
196
+
197
+ await expect(
198
+ resolveIntegrationHostIde({
199
+ cwd: '/repo',
200
+ }),
201
+ ).resolves.toMatchObject({
202
+ ide: 'codebuddy-cn',
203
+ confidence: 'medium',
204
+ source: 'artifact',
205
+ })
206
+ })
207
+
154
208
  it('refuses to resolve an ide when project artifacts are ambiguous', async () => {
155
209
  vi.mocked(fsUtils.exists).mockImplementation(async filePath => {
156
210
  return filePath === '/repo/.cursor' || filePath === '/repo/.vscode'
@@ -139,7 +139,7 @@ describe('integration install', () => {
139
139
  'Installed Codex integration assets. User-level installs only write integration assets and do not launch onboarding automatically.',
140
140
  )
141
141
  expect(logMock.hint).toHaveBeenCalledWith(
142
- 'Run the install command again from your target project root with --host-ide <vscode|cursor|trae|trae-cn> when you want to launch onboarding automatically.',
142
+ 'Run the install command again from your target project root with --host-ide <vscode|cursor|trae|trae-cn|codebuddy|codebuddy-cn> when you want to launch onboarding automatically.',
143
143
  )
144
144
  })
145
145
 
@@ -285,7 +285,7 @@ describe('integration install', () => {
285
285
  message:
286
286
  'Automatic setup stopped: Inspecto could not determine which IDE should receive onboarding.',
287
287
  nextStep:
288
- 'Re-run with --host-ide <vscode|cursor|trae|trae-cn> or run the command from the target IDE terminal to continue automatic setup.',
288
+ 'Re-run with --host-ide <vscode|cursor|trae|trae-cn|codebuddy|codebuddy-cn> or run the command from the target IDE terminal to continue automatic setup.',
289
289
  })
290
290
  resolveIntegrationHostIdeMock.mockResolvedValue({
291
291
  ide: null,
@@ -309,7 +309,7 @@ describe('integration install', () => {
309
309
  'Automatic setup stopped: Inspecto could not determine which IDE should receive onboarding.',
310
310
  )
311
311
  expect(logMock.hint).toHaveBeenCalledWith(
312
- 'Re-run with --host-ide <vscode|cursor|trae|trae-cn> or run the command from the target IDE terminal to continue automatic setup.',
312
+ 'Re-run with --host-ide <vscode|cursor|trae|trae-cn|codebuddy|codebuddy-cn> or run the command from the target IDE terminal to continue automatic setup.',
313
313
  )
314
314
  })
315
315
 
@@ -527,7 +527,7 @@ describe('integration install', () => {
527
527
  '/Users/tester/.claude/skills/inspecto-onboarding-claude-code/scripts/run-inspecto.sh',
528
528
  ],
529
529
  preferredInstall:
530
- 'npx @inspecto-dev/cli integrations install claude-code --scope project --host-ide <vscode|cursor|trae|trae-cn>',
530
+ 'npx @inspecto-dev/cli integrations install claude-code --scope project --host-ide <vscode|cursor|trae|trae-cn|codebuddy|codebuddy-cn>',
531
531
  })
532
532
  })
533
533
 
@@ -542,7 +542,7 @@ describe('integration install', () => {
542
542
  '/Users/tester/.agents/skills/inspecto-onboarding-codex/scripts/run-inspecto.sh',
543
543
  ],
544
544
  preferredInstall:
545
- 'npx @inspecto-dev/cli integrations install codex --host-ide <vscode|cursor|trae|trae-cn>',
545
+ 'npx @inspecto-dev/cli integrations install codex --host-ide <vscode|cursor|trae|trae-cn|codebuddy|codebuddy-cn>',
546
546
  })
547
547
  })
548
548
 
@@ -556,7 +556,7 @@ describe('integration install', () => {
556
556
  '.trae/skills/inspecto-onboarding/scripts/run-inspecto.sh',
557
557
  ],
558
558
  preferredInstall:
559
- 'npx @inspecto-dev/cli integrations install coco --host-ide <vscode|cursor|trae|trae-cn>',
559
+ 'npx @inspecto-dev/cli integrations install coco --host-ide <vscode|cursor|trae|trae-cn|codebuddy|codebuddy-cn>',
560
560
  })
561
561
  })
562
562
 
@@ -569,7 +569,7 @@ describe('integration install', () => {
569
569
  '/Users/tester/.claude/skills/inspecto-onboarding-claude-code/SKILL.md',
570
570
  )
571
571
  expect(logMock.hint).toHaveBeenCalledWith(
572
- 'Preferred install: npx @inspecto-dev/cli integrations install claude-code --scope project --host-ide <vscode|cursor|trae|trae-cn>',
572
+ 'Preferred install: npx @inspecto-dev/cli integrations install claude-code --scope project --host-ide <vscode|cursor|trae|trae-cn|codebuddy|codebuddy-cn>',
573
573
  )
574
574
  expect(logMock.hint).not.toHaveBeenCalledWith('Restart Claude Code to load the new skill.')
575
575
  })
@@ -11,6 +11,8 @@ describe('shared capabilities', () => {
11
11
  expect(getHostIdeLabel('cursor')).toBe('Cursor')
12
12
  expect(getHostIdeLabel('trae')).toBe('Trae')
13
13
  expect(getHostIdeLabel('trae-cn')).toBe('Trae CN')
14
+ expect(getHostIdeLabel('codebuddy')).toBe('CodeBuddy')
15
+ expect(getHostIdeLabel('codebuddy-cn')).toBe('CodeBuddy CN')
14
16
  })
15
17
 
16
18
  it('exposes supported host IDE guards', () => {
@@ -18,6 +20,8 @@ describe('shared capabilities', () => {
18
20
  expect(isSupportedHostIde('cursor')).toBe(true)
19
21
  expect(isSupportedHostIde('trae')).toBe(true)
20
22
  expect(isSupportedHostIde('trae-cn')).toBe(true)
23
+ expect(isSupportedHostIde('codebuddy')).toBe(true)
24
+ expect(isSupportedHostIde('codebuddy-cn')).toBe(true)
21
25
  expect(isSupportedHostIde('unknown')).toBe(false)
22
26
  })
23
27