@inspecto-dev/cli 0.3.7 → 0.3.9
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/.turbo/turbo-build.log +7 -7
- package/.turbo/turbo-test.log +43 -42
- package/CHANGELOG.md +16 -0
- package/dist/bin.js +12 -1
- package/dist/{chunk-LLQA5L7E.js → chunk-T46P6RD7.js} +450 -74
- package/dist/index.d.ts +51 -2
- package/dist/index.js +9 -1
- package/package.json +5 -3
- package/src/bin.ts +20 -0
- package/src/commands/init.ts +7 -3
- package/src/commands/integration-automation.ts +4 -2
- package/src/commands/integration-host-ide.ts +10 -3
- package/src/commands/integration-install.ts +40 -5
- package/src/commands/mcp.ts +386 -0
- package/src/commands/onboard.ts +5 -1
- package/src/detect/framework.ts +9 -6
- package/src/detect/ide.ts +12 -10
- package/src/detect/provider.ts +1 -1
- package/src/index.ts +6 -0
- package/src/inject/strategies/esbuild.ts +2 -2
- package/src/inject/strategies/rollup.ts +2 -2
- package/src/inject/strategies/rsbuild.ts +2 -2
- package/src/inject/strategies/rspack.ts +2 -2
- package/src/inject/strategies/vite.ts +2 -2
- package/src/inject/strategies/webpack.ts +2 -2
- package/src/instructions.ts +1 -1
- package/src/onboarding/session.ts +40 -3
- package/src/onboarding/umi-guidance.ts +1 -1
- package/src/prompts.ts +19 -9
- package/src/types.ts +10 -0
- package/tests/detect.test.ts +2 -2
- package/tests/framework.test.ts +27 -3
- package/tests/integration-install.test.ts +16 -0
- package/tests/mcp.test.ts +197 -0
- package/tests/onboard.test.ts +15 -0
- package/tests/plan.test.ts +16 -16
|
@@ -14,6 +14,7 @@ import { resolveOnboardingTarget } from './target-resolution.js'
|
|
|
14
14
|
import { readJSON } from '../utils/fs.js'
|
|
15
15
|
import type {
|
|
16
16
|
OnboardCommandResult,
|
|
17
|
+
OnboardingDailyUsageHandoff,
|
|
17
18
|
OnboardingContext,
|
|
18
19
|
OnboardingDiagnostics,
|
|
19
20
|
OnboardingExecutionResult,
|
|
@@ -256,9 +257,34 @@ function buildConfirmation(
|
|
|
256
257
|
}
|
|
257
258
|
}
|
|
258
259
|
|
|
260
|
+
async function buildDailyUsageHandoff(
|
|
261
|
+
projectRoot: string,
|
|
262
|
+
): Promise<OnboardingDailyUsageHandoff | undefined> {
|
|
263
|
+
const localSettings =
|
|
264
|
+
(await readJSON<Record<string, unknown>>(path.join(projectRoot, '.inspecto', 'settings.local.json'))) ??
|
|
265
|
+
{}
|
|
266
|
+
const sharedSettings =
|
|
267
|
+
(await readJSON<Record<string, unknown>>(path.join(projectRoot, '.inspecto', 'settings.json'))) ?? {}
|
|
268
|
+
const annotateDeliveryMode =
|
|
269
|
+
(localSettings['annotate.deliveryMode'] as string | undefined) ??
|
|
270
|
+
(sharedSettings['annotate.deliveryMode'] as string | undefined)
|
|
271
|
+
|
|
272
|
+
if (annotateDeliveryMode !== 'agent') {
|
|
273
|
+
return undefined
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
mode: 'agent',
|
|
278
|
+
skill: 'inspecto-agent',
|
|
279
|
+
prompt: 'Use $inspecto-agent to claim Inspecto tasks continuously',
|
|
280
|
+
requiresMcp: true,
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
259
284
|
function buildPreApplyResult(
|
|
260
285
|
status: ResolvedOnboardingSession['status'],
|
|
261
286
|
session: ResolvedOnboardingSession,
|
|
287
|
+
dailyUsage?: OnboardingDailyUsageHandoff,
|
|
262
288
|
): OnboardCommandResult {
|
|
263
289
|
const diagnostics: OnboardingDiagnostics | undefined =
|
|
264
290
|
session.summary.risks.length > 0 ||
|
|
@@ -292,6 +318,7 @@ function buildPreApplyResult(
|
|
|
292
318
|
...(session.pendingSteps ? { pendingSteps: session.pendingSteps } : {}),
|
|
293
319
|
...(session.assistantPrompt ? { assistantPrompt: session.assistantPrompt } : {}),
|
|
294
320
|
...(session.patches ? { patches: session.patches } : {}),
|
|
321
|
+
...(dailyUsage ? { dailyUsage } : {}),
|
|
295
322
|
...(diagnostics ? { diagnostics } : {}),
|
|
296
323
|
}
|
|
297
324
|
}
|
|
@@ -353,6 +380,7 @@ export async function resolveOnboardingSession(
|
|
|
353
380
|
): Promise<ResolvedOnboardingSession> {
|
|
354
381
|
const rootContext = await buildOnboardingContext(root)
|
|
355
382
|
const rootVerification = await buildVerification(root, rootContext.packageManager)
|
|
383
|
+
const rootDailyUsage = await buildDailyUsageHandoff(root)
|
|
356
384
|
const frameworkSupportByPackage = await detectFrameworkSupportByPackage(root, rootContext)
|
|
357
385
|
const target = resolveOnboardingTarget({
|
|
358
386
|
repoRoot: root,
|
|
@@ -392,6 +420,7 @@ export async function resolveOnboardingSession(
|
|
|
392
420
|
...(plan.pendingSteps ? { pendingSteps: plan.pendingSteps } : {}),
|
|
393
421
|
...(plan.assistantPrompt ? { assistantPrompt: plan.assistantPrompt } : {}),
|
|
394
422
|
...(plan.patches ? { patches: plan.patches } : {}),
|
|
423
|
+
...(rootDailyUsage ? { dailyUsage: rootDailyUsage } : {}),
|
|
395
424
|
}
|
|
396
425
|
}
|
|
397
426
|
|
|
@@ -418,6 +447,7 @@ export async function resolveOnboardingSession(
|
|
|
418
447
|
|
|
419
448
|
const context = await buildTargetedContext(rootContext, target.selected!)
|
|
420
449
|
const verification = await buildVerification(context.root, context.packageManager)
|
|
450
|
+
const dailyUsage = await buildDailyUsageHandoff(context.root)
|
|
421
451
|
const plan = createPlanResult(context)
|
|
422
452
|
const summary = buildOnboardingSummary(plan, context.root)
|
|
423
453
|
const confirmation = buildConfirmation(
|
|
@@ -469,6 +499,7 @@ export async function resolveOnboardingSession(
|
|
|
469
499
|
...(plan.pendingSteps ? { pendingSteps: plan.pendingSteps } : {}),
|
|
470
500
|
...(plan.assistantPrompt ? { assistantPrompt: plan.assistantPrompt } : {}),
|
|
471
501
|
...(plan.patches ? { patches: plan.patches } : {}),
|
|
502
|
+
...(dailyUsage ? { dailyUsage } : {}),
|
|
472
503
|
}
|
|
473
504
|
}
|
|
474
505
|
|
|
@@ -477,6 +508,7 @@ export async function applyResolvedOnboardingSession(
|
|
|
477
508
|
options: ResolveOnboardingSessionOptions = {},
|
|
478
509
|
): Promise<OnboardCommandResult> {
|
|
479
510
|
const verification = await buildVerification(session.projectRoot, session.context.packageManager)
|
|
511
|
+
const dailyUsage = await buildDailyUsageHandoff(session.projectRoot)
|
|
480
512
|
const applyResult = await applyOnboardingPlan({
|
|
481
513
|
repoRoot: process.cwd(),
|
|
482
514
|
projectRoot: session.projectRoot,
|
|
@@ -528,12 +560,17 @@ export async function applyResolvedOnboardingSession(
|
|
|
528
560
|
...(session.pendingSteps ? { pendingSteps: session.pendingSteps } : {}),
|
|
529
561
|
...(session.assistantPrompt ? { assistantPrompt: session.assistantPrompt } : {}),
|
|
530
562
|
...(session.patches ? { patches: session.patches } : {}),
|
|
563
|
+
...(dailyUsage ? { dailyUsage } : {}),
|
|
531
564
|
...(diagnostics ? { diagnostics } : {}),
|
|
532
565
|
}
|
|
533
566
|
}
|
|
534
567
|
|
|
535
|
-
export function buildDeferredOnboardResult(
|
|
568
|
+
export async function buildDeferredOnboardResult(
|
|
536
569
|
session: ResolvedOnboardingSession,
|
|
537
|
-
): OnboardCommandResult {
|
|
538
|
-
return buildPreApplyResult(
|
|
570
|
+
): Promise<OnboardCommandResult> {
|
|
571
|
+
return buildPreApplyResult(
|
|
572
|
+
session.status,
|
|
573
|
+
session,
|
|
574
|
+
await buildDailyUsageHandoff(session.projectRoot),
|
|
575
|
+
)
|
|
539
576
|
}
|
|
@@ -93,7 +93,7 @@ function buildUmiMountSnippet(): string {
|
|
|
93
93
|
" if (process.env.NODE_ENV !== 'production') {",
|
|
94
94
|
" import('@inspecto-dev/core').then(({ mountInspector }) => {",
|
|
95
95
|
' mountInspector({',
|
|
96
|
-
" serverUrl: 'http://
|
|
96
|
+
" serverUrl: 'http://0.0.0.0:' + ((window as { __AI_INSPECTOR_PORT__?: number }).__AI_INSPECTOR_PORT__ || 5678),",
|
|
97
97
|
' })',
|
|
98
98
|
' })',
|
|
99
99
|
' }',
|
package/src/prompts.ts
CHANGED
|
@@ -10,23 +10,33 @@ export async function promptIDEChoice(
|
|
|
10
10
|
detections: { ide: string; supported: boolean }[],
|
|
11
11
|
): Promise<{ ide: string; supported: boolean } | null> {
|
|
12
12
|
if (!process.stdin.isTTY) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
if (detections.length > 0) {
|
|
14
|
+
log.warn('Multiple IDEs detected but stdin is not interactive')
|
|
15
|
+
log.hint(`Using: ${detections[0]!.ide} (first match)`)
|
|
16
|
+
return detections[0]!
|
|
17
|
+
}
|
|
18
|
+
return { ide: 'none', supported: true }
|
|
16
19
|
}
|
|
17
20
|
|
|
21
|
+
const choices = detections.map(d => ({
|
|
22
|
+
title: `${d.ide} ${d.supported ? '(supported)' : '(unsupported/limited)'}`,
|
|
23
|
+
value: d,
|
|
24
|
+
}))
|
|
25
|
+
|
|
26
|
+
choices.push({
|
|
27
|
+
title: 'none (Standalone / MCP / Browser-only)',
|
|
28
|
+
value: { ide: 'none', supported: true },
|
|
29
|
+
})
|
|
30
|
+
|
|
18
31
|
const { choice } = await prompts({
|
|
19
32
|
type: 'select',
|
|
20
33
|
name: 'choice',
|
|
21
|
-
message: 'Detected multiple IDEs, please choose one:',
|
|
22
|
-
choices
|
|
23
|
-
title: `${d.ide} ${d.supported ? '(supported)' : '(unsupported/limited)'}`,
|
|
24
|
-
value: i,
|
|
25
|
-
})),
|
|
34
|
+
message: 'Detected multiple IDEs, please choose one (or select none for standalone use):',
|
|
35
|
+
choices,
|
|
26
36
|
})
|
|
27
37
|
|
|
28
38
|
if (choice === undefined) return null
|
|
29
|
-
return
|
|
39
|
+
return choice
|
|
30
40
|
}
|
|
31
41
|
|
|
32
42
|
/**
|
package/src/types.ts
CHANGED
|
@@ -134,6 +134,13 @@ export interface OnboardingVerification {
|
|
|
134
134
|
message: string
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
export interface OnboardingDailyUsageHandoff {
|
|
138
|
+
mode: 'agent'
|
|
139
|
+
skill: string
|
|
140
|
+
prompt: string
|
|
141
|
+
requiresMcp: boolean
|
|
142
|
+
}
|
|
143
|
+
|
|
137
144
|
export interface OnboardingAssistantHandoff {
|
|
138
145
|
framework?: string
|
|
139
146
|
metaFramework?: string
|
|
@@ -142,6 +149,7 @@ export interface OnboardingAssistantHandoff {
|
|
|
142
149
|
pendingSteps?: string[]
|
|
143
150
|
assistantPrompt?: string
|
|
144
151
|
patches?: OnboardingPatchPlan[]
|
|
152
|
+
dailyUsage?: OnboardingDailyUsageHandoff
|
|
145
153
|
}
|
|
146
154
|
|
|
147
155
|
export interface ResolvedOnboardingSession {
|
|
@@ -162,6 +170,7 @@ export interface ResolvedOnboardingSession {
|
|
|
162
170
|
pendingSteps?: string[]
|
|
163
171
|
assistantPrompt?: string
|
|
164
172
|
patches?: OnboardingPatchPlan[]
|
|
173
|
+
dailyUsage?: OnboardingDailyUsageHandoff
|
|
165
174
|
handoff?: OnboardingAssistantHandoff
|
|
166
175
|
}
|
|
167
176
|
|
|
@@ -181,6 +190,7 @@ export interface OnboardCommandResult {
|
|
|
181
190
|
pendingSteps?: string[]
|
|
182
191
|
assistantPrompt?: string
|
|
183
192
|
patches?: OnboardingPatchPlan[]
|
|
193
|
+
dailyUsage?: OnboardingDailyUsageHandoff
|
|
184
194
|
handoff?: OnboardingAssistantHandoff
|
|
185
195
|
}
|
|
186
196
|
|
package/tests/detect.test.ts
CHANGED
|
@@ -45,7 +45,7 @@ describe('buildOnboardingContext', () => {
|
|
|
45
45
|
})
|
|
46
46
|
vi.mocked(frameworkDetector.detectFrameworks).mockResolvedValue({
|
|
47
47
|
supported: ['react'],
|
|
48
|
-
unsupported: [{ name: '
|
|
48
|
+
unsupported: [{ name: 'Angular', dep: 'angular' }],
|
|
49
49
|
})
|
|
50
50
|
vi.mocked(ideDetector.detectIDE).mockResolvedValue({
|
|
51
51
|
detected: [{ ide: 'vscode', supported: true }],
|
|
@@ -79,7 +79,7 @@ describe('buildOnboardingContext', () => {
|
|
|
79
79
|
},
|
|
80
80
|
frameworks: {
|
|
81
81
|
supported: ['react'],
|
|
82
|
-
unsupported: ['
|
|
82
|
+
unsupported: ['Angular'],
|
|
83
83
|
},
|
|
84
84
|
ides: [{ ide: 'vscode', supported: true }],
|
|
85
85
|
providers: [{ id: 'codex', label: 'Codex CLI', supported: true, preferredMode: 'cli' }],
|
package/tests/framework.test.ts
CHANGED
|
@@ -31,7 +31,7 @@ describe('detectFrameworks', () => {
|
|
|
31
31
|
expect(result.unsupported).toHaveLength(0)
|
|
32
32
|
})
|
|
33
33
|
|
|
34
|
-
it('detects Svelte as
|
|
34
|
+
it('detects Svelte as supported framework', async () => {
|
|
35
35
|
vi.mocked(fsUtils.readJSON).mockResolvedValue({
|
|
36
36
|
devDependencies: {
|
|
37
37
|
svelte: '^4.0.0',
|
|
@@ -39,8 +39,32 @@ describe('detectFrameworks', () => {
|
|
|
39
39
|
})
|
|
40
40
|
|
|
41
41
|
const result = await detectFrameworks('/mock/root')
|
|
42
|
-
expect(result.supported).
|
|
43
|
-
expect(result.unsupported).
|
|
42
|
+
expect(result.supported).toContain('svelte')
|
|
43
|
+
expect(result.unsupported).toHaveLength(0)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('detects Solid as supported framework', async () => {
|
|
47
|
+
vi.mocked(fsUtils.readJSON).mockResolvedValue({
|
|
48
|
+
devDependencies: {
|
|
49
|
+
'solid-js': '^1.0.0',
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const result = await detectFrameworks('/mock/root')
|
|
54
|
+
expect(result.supported).toContain('solid')
|
|
55
|
+
expect(result.unsupported).toHaveLength(0)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('detects Astro as supported framework', async () => {
|
|
59
|
+
vi.mocked(fsUtils.readJSON).mockResolvedValue({
|
|
60
|
+
devDependencies: {
|
|
61
|
+
astro: '^4.0.0',
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const result = await detectFrameworks('/mock/root')
|
|
66
|
+
expect(result.supported).toContain('astro')
|
|
67
|
+
expect(result.unsupported).toHaveLength(0)
|
|
44
68
|
})
|
|
45
69
|
|
|
46
70
|
it('returns empty if no framework is matched', async () => {
|
|
@@ -126,6 +126,14 @@ describe('integration install', () => {
|
|
|
126
126
|
'/Users/tester/.agents/skills/inspecto-onboarding-codex/scripts/run-inspecto.sh',
|
|
127
127
|
'# mock asset',
|
|
128
128
|
)
|
|
129
|
+
expect(writeFileMock).toHaveBeenCalledWith(
|
|
130
|
+
'/Users/tester/.agents/skills/inspecto-agent-codex/SKILL.md',
|
|
131
|
+
'# mock asset',
|
|
132
|
+
)
|
|
133
|
+
expect(writeFileMock).toHaveBeenCalledWith(
|
|
134
|
+
'/Users/tester/.agents/skills/inspecto-agent-codex/agents/openai.yaml',
|
|
135
|
+
'# mock asset',
|
|
136
|
+
)
|
|
129
137
|
expect(chmodMock).toHaveBeenCalledWith(
|
|
130
138
|
'/Users/tester/.agents/skills/inspecto-onboarding-codex/scripts/run-inspecto.sh',
|
|
131
139
|
0o755,
|
|
@@ -134,6 +142,9 @@ describe('integration install', () => {
|
|
|
134
142
|
expect(logMock.hint).toHaveBeenCalledWith(
|
|
135
143
|
'/Users/tester/.agents/skills/inspecto-onboarding-codex/SKILL.md',
|
|
136
144
|
)
|
|
145
|
+
expect(logMock.hint).toHaveBeenCalledWith(
|
|
146
|
+
'/Users/tester/.agents/skills/inspecto-agent-codex/SKILL.md',
|
|
147
|
+
)
|
|
137
148
|
expect(runIntegrationAutomationMock).not.toHaveBeenCalled()
|
|
138
149
|
expect(logMock.ready).toHaveBeenCalledWith(
|
|
139
150
|
'Installed Codex integration assets. User-level installs only write integration assets and do not launch onboarding automatically.',
|
|
@@ -199,6 +210,7 @@ describe('integration install', () => {
|
|
|
199
210
|
expect(writeJSONMock).toHaveBeenCalledWith('/repo/.inspecto/settings.local.json', {
|
|
200
211
|
ide: 'trae-cn',
|
|
201
212
|
'provider.default': 'gemini.extension',
|
|
213
|
+
'annotate.deliveryMode': 'both',
|
|
202
214
|
})
|
|
203
215
|
})
|
|
204
216
|
|
|
@@ -216,6 +228,7 @@ describe('integration install', () => {
|
|
|
216
228
|
expect(writeJSONMock).toHaveBeenCalledWith('/repo/.inspecto/settings.local.json', {
|
|
217
229
|
ide: 'trae-cn',
|
|
218
230
|
'provider.default': 'gemini.extension',
|
|
231
|
+
'annotate.deliveryMode': 'both',
|
|
219
232
|
'prompt.autoSend': true,
|
|
220
233
|
})
|
|
221
234
|
})
|
|
@@ -228,6 +241,7 @@ describe('integration install', () => {
|
|
|
228
241
|
expect(writeJSONMock).toHaveBeenCalledWith('/repo/.inspecto/settings.local.json', {
|
|
229
242
|
ide: 'cursor',
|
|
230
243
|
'provider.default': 'codex.extension',
|
|
244
|
+
'annotate.deliveryMode': 'both',
|
|
231
245
|
})
|
|
232
246
|
})
|
|
233
247
|
|
|
@@ -540,6 +554,8 @@ describe('integration install', () => {
|
|
|
540
554
|
'/Users/tester/.agents/skills/inspecto-onboarding-codex/SKILL.md',
|
|
541
555
|
'/Users/tester/.agents/skills/inspecto-onboarding-codex/agents/openai.yaml',
|
|
542
556
|
'/Users/tester/.agents/skills/inspecto-onboarding-codex/scripts/run-inspecto.sh',
|
|
557
|
+
'/Users/tester/.agents/skills/inspecto-agent-codex/SKILL.md',
|
|
558
|
+
'/Users/tester/.agents/skills/inspecto-agent-codex/agents/openai.yaml',
|
|
543
559
|
],
|
|
544
560
|
preferredInstall:
|
|
545
561
|
'npx @inspecto-dev/cli integrations install codex --host-ide <vscode|cursor|trae|trae-cn|codebuddy|codebuddy-cn>',
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import os from 'node:os'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import crypto from 'node:crypto'
|
|
6
|
+
import {
|
|
7
|
+
INSPECTO_MCP_TOOLS,
|
|
8
|
+
createInspectoMcpRuntime,
|
|
9
|
+
resolveInspectoServerBaseUrl,
|
|
10
|
+
} from '../src/commands/mcp.js'
|
|
11
|
+
|
|
12
|
+
describe('inspecto mcp command', () => {
|
|
13
|
+
const portFile = path.join(os.tmpdir(), 'inspecto.port.json')
|
|
14
|
+
let originalPortFile: string | null = null
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
vi.restoreAllMocks()
|
|
18
|
+
try {
|
|
19
|
+
fs.unlinkSync(portFile)
|
|
20
|
+
} catch {
|
|
21
|
+
// ignore
|
|
22
|
+
}
|
|
23
|
+
if (originalPortFile !== null) {
|
|
24
|
+
fs.writeFileSync(portFile, originalPortFile, 'utf-8')
|
|
25
|
+
originalPortFile = null
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('resolves the current project server URL from inspecto.port.json', () => {
|
|
30
|
+
originalPortFile = fs.existsSync(portFile) ? fs.readFileSync(portFile, 'utf-8') : null
|
|
31
|
+
const cwd = '/repo/current'
|
|
32
|
+
const currentHash = crypto.createHash('md5').update(cwd).digest('hex')
|
|
33
|
+
const otherHash = crypto.createHash('md5').update('/repo/other').digest('hex')
|
|
34
|
+
|
|
35
|
+
fs.writeFileSync(
|
|
36
|
+
portFile,
|
|
37
|
+
JSON.stringify(
|
|
38
|
+
{
|
|
39
|
+
[otherHash]: 5679,
|
|
40
|
+
[currentHash]: 5680,
|
|
41
|
+
},
|
|
42
|
+
null,
|
|
43
|
+
2,
|
|
44
|
+
),
|
|
45
|
+
'utf-8',
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
expect(resolveInspectoServerBaseUrl(cwd)).toBe('http://0.0.0.0:5680')
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('resolves the project server URL when Codex runs from a nested working directory', () => {
|
|
52
|
+
originalPortFile = fs.existsSync(portFile) ? fs.readFileSync(portFile, 'utf-8') : null
|
|
53
|
+
const projectRoot = '/repo/current'
|
|
54
|
+
const nestedCwd = '/repo/current/packages/app'
|
|
55
|
+
const projectRootHash = crypto.createHash('md5').update(projectRoot).digest('hex')
|
|
56
|
+
|
|
57
|
+
fs.writeFileSync(
|
|
58
|
+
portFile,
|
|
59
|
+
JSON.stringify(
|
|
60
|
+
{
|
|
61
|
+
[projectRootHash]: 5681,
|
|
62
|
+
},
|
|
63
|
+
null,
|
|
64
|
+
2,
|
|
65
|
+
),
|
|
66
|
+
'utf-8',
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
expect(resolveInspectoServerBaseUrl(nestedCwd)).toBe('http://0.0.0.0:5681')
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('exposes the expected MCP tool definitions', () => {
|
|
73
|
+
expect(INSPECTO_MCP_TOOLS.map(tool => tool.name)).toEqual([
|
|
74
|
+
'inspecto_get_session',
|
|
75
|
+
'inspecto_claim_next',
|
|
76
|
+
'inspecto_reply',
|
|
77
|
+
'inspecto_resolve',
|
|
78
|
+
'inspecto_dismiss',
|
|
79
|
+
])
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('maps getSession and claimNext to the HTTP session endpoints', async () => {
|
|
83
|
+
const fetchMock = vi
|
|
84
|
+
.spyOn(globalThis, 'fetch')
|
|
85
|
+
.mockResolvedValueOnce({
|
|
86
|
+
ok: true,
|
|
87
|
+
json: async () => ({ success: true, session: { id: 'session-1' } }),
|
|
88
|
+
} as Response)
|
|
89
|
+
.mockResolvedValueOnce({
|
|
90
|
+
ok: true,
|
|
91
|
+
json: async () => ({
|
|
92
|
+
success: true,
|
|
93
|
+
timedOut: false,
|
|
94
|
+
matchedExisting: true,
|
|
95
|
+
session: { id: 'session-claim', status: 'acknowledged' },
|
|
96
|
+
}),
|
|
97
|
+
} as Response)
|
|
98
|
+
const runtime = createInspectoMcpRuntime('http://0.0.0.0:5678')
|
|
99
|
+
|
|
100
|
+
const session = await runtime.getSession({ sessionId: 'session-1' })
|
|
101
|
+
|
|
102
|
+
expect(fetchMock).toHaveBeenCalledWith('http://0.0.0.0:5678/inspecto/api/v1/sessions/session-1')
|
|
103
|
+
expect(session).toEqual({ success: true, session: { id: 'session-1' } })
|
|
104
|
+
|
|
105
|
+
const claim = await runtime.claimNext()
|
|
106
|
+
|
|
107
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
108
|
+
'http://0.0.0.0:5678/inspecto/api/v1/sessions/claim',
|
|
109
|
+
expect.objectContaining({
|
|
110
|
+
method: 'POST',
|
|
111
|
+
body: JSON.stringify({}),
|
|
112
|
+
}),
|
|
113
|
+
)
|
|
114
|
+
expect(claim).toEqual({
|
|
115
|
+
success: true,
|
|
116
|
+
timedOut: false,
|
|
117
|
+
matchedExisting: true,
|
|
118
|
+
session: { id: 'session-claim', status: 'acknowledged' },
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('maps reply, resolve, and dismiss to session mutation endpoints', async () => {
|
|
123
|
+
const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue({
|
|
124
|
+
ok: true,
|
|
125
|
+
json: async () => ({ success: true, session: { id: 'session-1' } }),
|
|
126
|
+
} as Response)
|
|
127
|
+
const runtime = createInspectoMcpRuntime('http://0.0.0.0:5678')
|
|
128
|
+
|
|
129
|
+
const reply = await runtime.reply({
|
|
130
|
+
sessionId: 'session-1',
|
|
131
|
+
text: 'Working on it.',
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
135
|
+
'http://0.0.0.0:5678/inspecto/api/v1/sessions/session-1/reply',
|
|
136
|
+
expect.objectContaining({
|
|
137
|
+
method: 'POST',
|
|
138
|
+
}),
|
|
139
|
+
)
|
|
140
|
+
expect(reply).toEqual({ success: true, session: { id: 'session-1' } })
|
|
141
|
+
|
|
142
|
+
const resolve = await runtime.resolve({
|
|
143
|
+
sessionId: 'session-1',
|
|
144
|
+
message: 'Done.',
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
148
|
+
'http://0.0.0.0:5678/inspecto/api/v1/sessions/session-1/resolve',
|
|
149
|
+
expect.objectContaining({
|
|
150
|
+
method: 'POST',
|
|
151
|
+
}),
|
|
152
|
+
)
|
|
153
|
+
expect(resolve).toEqual({ success: true, session: { id: 'session-1' } })
|
|
154
|
+
|
|
155
|
+
const dismiss = await runtime.dismiss({
|
|
156
|
+
sessionId: 'session-1',
|
|
157
|
+
message: 'No action needed.',
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
161
|
+
'http://0.0.0.0:5678/inspecto/api/v1/sessions/session-1/dismiss',
|
|
162
|
+
expect.objectContaining({
|
|
163
|
+
method: 'POST',
|
|
164
|
+
}),
|
|
165
|
+
)
|
|
166
|
+
expect(dismiss).toEqual({ success: true, session: { id: 'session-1' } })
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('surfaces failed session mutations as rejected runtime calls', async () => {
|
|
170
|
+
vi.spyOn(globalThis, 'fetch').mockResolvedValue({
|
|
171
|
+
ok: false,
|
|
172
|
+
status: 404,
|
|
173
|
+
json: async () => ({ success: false, error: 'Session not found' }),
|
|
174
|
+
} as Response)
|
|
175
|
+
const runtime = createInspectoMcpRuntime('http://0.0.0.0:5678')
|
|
176
|
+
|
|
177
|
+
await expect(
|
|
178
|
+
runtime.reply({
|
|
179
|
+
sessionId: 'missing-session',
|
|
180
|
+
text: 'Working on it.',
|
|
181
|
+
}),
|
|
182
|
+
).rejects.toThrow('Session not found')
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('surfaces direct session lookup failures as rejected runtime calls', async () => {
|
|
186
|
+
vi.spyOn(globalThis, 'fetch').mockResolvedValue({
|
|
187
|
+
ok: false,
|
|
188
|
+
status: 404,
|
|
189
|
+
json: async () => ({ success: false, error: 'Session not found' }),
|
|
190
|
+
} as Response)
|
|
191
|
+
const runtime = createInspectoMcpRuntime('http://0.0.0.0:5678')
|
|
192
|
+
|
|
193
|
+
await expect(runtime.getSession({ sessionId: 'missing-session' })).rejects.toThrow(
|
|
194
|
+
'Session not found',
|
|
195
|
+
)
|
|
196
|
+
})
|
|
197
|
+
})
|
package/tests/onboard.test.ts
CHANGED
|
@@ -352,6 +352,12 @@ describe('onboard command', () => {
|
|
|
352
352
|
'Complete the remaining client-side mount step for your App Router entry.',
|
|
353
353
|
],
|
|
354
354
|
assistantPrompt: 'Complete the remaining Inspecto onboarding for this Next.js project.',
|
|
355
|
+
dailyUsage: {
|
|
356
|
+
mode: 'agent',
|
|
357
|
+
skill: 'inspecto-agent',
|
|
358
|
+
prompt: 'Use $inspecto-agent to claim Inspecto tasks continuously',
|
|
359
|
+
requiresMcp: true,
|
|
360
|
+
},
|
|
355
361
|
patches: [
|
|
356
362
|
{
|
|
357
363
|
path: 'next.config.mjs',
|
|
@@ -393,6 +399,12 @@ describe('onboard command', () => {
|
|
|
393
399
|
'Complete the remaining client-side mount step for your App Router entry.',
|
|
394
400
|
],
|
|
395
401
|
assistantPrompt: 'Complete the remaining Inspecto onboarding for this Next.js project.',
|
|
402
|
+
dailyUsage: {
|
|
403
|
+
mode: 'agent',
|
|
404
|
+
skill: 'inspecto-agent',
|
|
405
|
+
prompt: 'Use $inspecto-agent to claim Inspecto tasks continuously',
|
|
406
|
+
requiresMcp: true,
|
|
407
|
+
},
|
|
396
408
|
patches: [
|
|
397
409
|
{
|
|
398
410
|
path: 'next.config.mjs',
|
|
@@ -425,6 +437,9 @@ describe('onboard command', () => {
|
|
|
425
437
|
'Complete the remaining client-side mount step for your App Router entry.',
|
|
426
438
|
]),
|
|
427
439
|
)
|
|
440
|
+
expect(result.handoff?.dailyUsage?.prompt).toBe(
|
|
441
|
+
'Use $inspecto-agent to claim Inspecto tasks continuously',
|
|
442
|
+
)
|
|
428
443
|
expect(result.assistantPrompt).toContain('Complete the remaining Inspecto onboarding')
|
|
429
444
|
expect(result.patches).toEqual(
|
|
430
445
|
expect.arrayContaining([
|
package/tests/plan.test.ts
CHANGED
|
@@ -236,7 +236,7 @@ describe('planner orchestration', () => {
|
|
|
236
236
|
},
|
|
237
237
|
frameworks: {
|
|
238
238
|
supported: [],
|
|
239
|
-
unsupported: ['
|
|
239
|
+
unsupported: ['Angular'],
|
|
240
240
|
},
|
|
241
241
|
ides: [{ ide: 'vscode', supported: true }],
|
|
242
242
|
providers: [{ id: 'codex', label: 'Codex CLI', supported: true, preferredMode: 'cli' }],
|
|
@@ -249,13 +249,13 @@ describe('planner orchestration', () => {
|
|
|
249
249
|
expect(result.blockers).toEqual([
|
|
250
250
|
{
|
|
251
251
|
code: 'unsupported-framework',
|
|
252
|
-
message: 'Detected unsupported framework(s):
|
|
252
|
+
message: 'Detected unsupported framework(s): Angular',
|
|
253
253
|
},
|
|
254
254
|
])
|
|
255
255
|
expect(result.actions).toEqual([
|
|
256
256
|
{
|
|
257
257
|
type: 'manual_step',
|
|
258
|
-
target: '
|
|
258
|
+
target: 'Angular',
|
|
259
259
|
description:
|
|
260
260
|
'Inspecto cannot auto-configure this framework yet. Follow the manual setup guide for the detected framework.',
|
|
261
261
|
},
|
|
@@ -278,7 +278,7 @@ describe('planner orchestration', () => {
|
|
|
278
278
|
},
|
|
279
279
|
frameworks: {
|
|
280
280
|
supported: ['react'],
|
|
281
|
-
unsupported: ['
|
|
281
|
+
unsupported: ['Angular'],
|
|
282
282
|
},
|
|
283
283
|
ides: [{ ide: 'vscode', supported: true }],
|
|
284
284
|
providers: [{ id: 'codex', label: 'Codex CLI', supported: true, preferredMode: 'cli' }],
|
|
@@ -297,7 +297,7 @@ describe('planner orchestration', () => {
|
|
|
297
297
|
expect(result.warnings).toEqual([
|
|
298
298
|
{
|
|
299
299
|
code: 'unsupported-framework-present',
|
|
300
|
-
message: 'Unsupported framework(s) also detected:
|
|
300
|
+
message: 'Unsupported framework(s) also detected: Angular',
|
|
301
301
|
},
|
|
302
302
|
])
|
|
303
303
|
expect(result.actions).toEqual([
|
|
@@ -597,7 +597,7 @@ describe('planner orchestration', () => {
|
|
|
597
597
|
},
|
|
598
598
|
frameworks: {
|
|
599
599
|
supported: ['react'],
|
|
600
|
-
unsupported: ['
|
|
600
|
+
unsupported: ['Angular'],
|
|
601
601
|
},
|
|
602
602
|
ides: [{ ide: 'vscode', supported: true }],
|
|
603
603
|
providers: [{ id: 'codex', label: 'Codex CLI', supported: true, preferredMode: 'cli' }],
|
|
@@ -612,7 +612,7 @@ describe('planner orchestration', () => {
|
|
|
612
612
|
})
|
|
613
613
|
expect(result.environment).toEqual({
|
|
614
614
|
frameworks: ['react'],
|
|
615
|
-
unsupportedFrameworks: ['
|
|
615
|
+
unsupportedFrameworks: ['Angular'],
|
|
616
616
|
buildTools: [
|
|
617
617
|
{
|
|
618
618
|
tool: 'vite',
|
|
@@ -633,7 +633,7 @@ describe('planner orchestration', () => {
|
|
|
633
633
|
expect(result.warnings).toEqual([
|
|
634
634
|
{
|
|
635
635
|
code: 'unsupported-framework-present',
|
|
636
|
-
message: 'Unsupported framework(s) also detected:
|
|
636
|
+
message: 'Unsupported framework(s) also detected: Angular',
|
|
637
637
|
},
|
|
638
638
|
])
|
|
639
639
|
})
|
|
@@ -646,7 +646,7 @@ describe('planner orchestration', () => {
|
|
|
646
646
|
project: { root: '/repo', packageManager: 'pnpm' },
|
|
647
647
|
environment: {
|
|
648
648
|
frameworks: ['react'],
|
|
649
|
-
unsupportedFrameworks: ['
|
|
649
|
+
unsupportedFrameworks: ['Angular'],
|
|
650
650
|
buildTools: [],
|
|
651
651
|
unsupportedBuildTools: ['Next.js'],
|
|
652
652
|
ides: [{ ide: 'vscode', supported: true }],
|
|
@@ -677,7 +677,7 @@ describe('planner orchestration', () => {
|
|
|
677
677
|
warnings: [
|
|
678
678
|
{
|
|
679
679
|
code: 'unsupported-framework-present',
|
|
680
|
-
message: 'Unsupported framework(s) also detected:
|
|
680
|
+
message: 'Unsupported framework(s) also detected: Angular',
|
|
681
681
|
},
|
|
682
682
|
],
|
|
683
683
|
blockers: [
|
|
@@ -687,13 +687,13 @@ describe('planner orchestration', () => {
|
|
|
687
687
|
},
|
|
688
688
|
{
|
|
689
689
|
code: 'unsupported-framework',
|
|
690
|
-
message: 'Detected unsupported framework(s):
|
|
690
|
+
message: 'Detected unsupported framework(s): Angular',
|
|
691
691
|
},
|
|
692
692
|
],
|
|
693
693
|
project: { root: '/repo', packageManager: 'pnpm' },
|
|
694
694
|
environment: {
|
|
695
695
|
frameworks: ['react'],
|
|
696
|
-
unsupportedFrameworks: ['
|
|
696
|
+
unsupportedFrameworks: ['Angular'],
|
|
697
697
|
buildTools: [],
|
|
698
698
|
unsupportedBuildTools: ['Next.js'],
|
|
699
699
|
ides: [{ ide: 'vscode', supported: true }],
|
|
@@ -712,13 +712,13 @@ describe('planner orchestration', () => {
|
|
|
712
712
|
const output = logSpy.mock.calls.map(call => call.join(' ')).join('\n')
|
|
713
713
|
|
|
714
714
|
expect(detectSpy).toHaveBeenCalledWith('/repo')
|
|
715
|
-
expect(output).toContain('Unsupported frameworks:
|
|
715
|
+
expect(output).toContain('Unsupported frameworks: Angular')
|
|
716
716
|
expect(output).toContain('Unsupported build tools: Next.js')
|
|
717
|
-
expect(output).not.toContain('Detected unsupported framework(s):
|
|
717
|
+
expect(output).not.toContain('Detected unsupported framework(s): Angular')
|
|
718
718
|
expect(output).not.toContain('Detected unsupported build tool(s): Next.js')
|
|
719
|
-
expect(output.match(/Unsupported frameworks:
|
|
719
|
+
expect(output.match(/Unsupported frameworks: Angular/g)).toHaveLength(1)
|
|
720
720
|
expect(output.match(/Unsupported build tools: Next.js/g)).toHaveLength(1)
|
|
721
|
-
expect(output.match(/
|
|
721
|
+
expect(output.match(/Angular/g)).toHaveLength(1)
|
|
722
722
|
|
|
723
723
|
cwdSpy.mockRestore()
|
|
724
724
|
logSpy.mockRestore()
|