diff --git a/packages/cli/docs/VSCODE-NO-REGRESSION-PROOF.md b/packages/cli/docs/VSCODE-NO-REGRESSION-PROOF.md index 792cdea..e626d0a 100644 --- a/packages/cli/docs/VSCODE-NO-REGRESSION-PROOF.md +++ b/packages/cli/docs/VSCODE-NO-REGRESSION-PROOF.md @@ -7,7 +7,7 @@ 1. VS Code extension has NO preset creation functionality 2. VS Code extension does NOT use the pattern optimization utilities we modified 3. All changes are isolated to the CLI package -4. Core utilities used by VS Code remain completely unchanged +4. VS Code uses only token utilities from @promptcode/core and its own prompt generator ## Detailed Analysis @@ -37,7 +37,7 @@ The VS Code extension's `package.json` shows NO preset-related commands: ### 3. Clean Import Separation -**VS Code Extension imports from @promptcode/core:** +**VS Code Extension imports token utilities from @promptcode/core and uses its own prompt generator:** ```typescript // src/extension.ts import { @@ -47,9 +47,9 @@ import { clearTokenCache, initializeTokenCounter, tokenCache, - countTokens, - buildPrompt + countTokens } from '@promptcode/core'; +import { generatePrompt } from './promptGenerator'; ``` **VS Code Extension does NOT import:** @@ -57,6 +57,7 @@ import { - ❌ `generatePatternsFromSelection` - ❌ `patternOptimizer` - ❌ Any pattern-related utilities +- ❌ `buildPrompt` directly (prompt generation runs through `promptGenerator`) ### 4. CLI-Only Changes @@ -147,4 +148,4 @@ While regression is impossible due to architectural separation, you can verify b 2. Testing file selection in VS Code: Open extension, select files, generate prompt 3. Verifying no preset commands appear in Command Palette -All functionality will work exactly as before because we haven't touched any code the VS Code extension uses. \ No newline at end of file +All functionality will work exactly as before because we haven't touched any code the VS Code extension uses. diff --git a/packages/cli/test/verify-vscode-no-regression.test.ts b/packages/cli/test/verify-vscode-no-regression.test.ts index 7bc0682..0386deb 100644 --- a/packages/cli/test/verify-vscode-no-regression.test.ts +++ b/packages/cli/test/verify-vscode-no-regression.test.ts @@ -33,7 +33,13 @@ describe('VS Code Extension Regression Verification', () => { // VS Code extension should import these utilities from @promptcode/core expect(extensionContent).toContain('countTokensInFile'); expect(extensionContent).toContain('countTokensWithCache'); - expect(extensionContent).toContain('buildPrompt'); + expect(extensionContent).toContain('countTokensWithCacheDetailed'); + expect(extensionContent).toContain('countTokens'); + + // VS Code extension should not import prompt builder directly anymore + expect(extensionContent).not.toContain('buildPrompt'); + // It should rely on promptGenerator for prompt construction + expect(extensionContent).toContain("from './promptGenerator'"); // VS Code extension should NOT import pattern-related utilities expect(extensionContent).not.toContain('generatePatternsFromSelection'); @@ -146,4 +152,4 @@ describe('VS Code Extension Regression Verification', () => { * 5. **Testing Coverage**: * - This test file verifies the separation * - Existing VS Code tests would catch any regression - */ \ No newline at end of file + */ diff --git a/scripts/build-tests.js b/scripts/build-tests.js index 2d47b12..e6798ba 100644 --- a/scripts/build-tests.js +++ b/scripts/build-tests.js @@ -13,6 +13,7 @@ const testEntryPoints = [ 'src/test/smoke.test.ts', 'src/test/migration.test.ts', 'src/test/promptContract.test.ts', + 'src/test/promptWorkspaceMetadata.test.ts', 'src/test/ignorePatterns.test.ts', 'src/test/extensionActivation.test.ts', 'src/test/tokenCounter.test.ts', @@ -24,6 +25,8 @@ const testEntryPoints = [ // Also compile the utils that tests depend on 'src/utils/filePattern.ts', 'src/utils/generatePatternsFromSelection.ts', + 'src/promptcodeDataFetcher.ts', + 'src/promptGenerator.ts', // Compile modules that tests import directly 'src/fileExplorer.ts', 'src/ignoreHelper.ts', @@ -58,4 +61,4 @@ esbuild.build({ }).catch((error) => { console.error('Error building test files:', error); process.exit(1); -}); \ No newline at end of file +}); diff --git a/src/extension.ts b/src/extension.ts index 2b0c8a6..fc09ab9 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -4,9 +4,9 @@ // Import the module and reference it with the alias vscode in your code below import * as vscode from 'vscode'; import { FileExplorerProvider, checkedItems as checkedItemsMap, FileItem } from './fileExplorer'; -import { generatePrompt as generatePromptFromGenerator, copyToClipboard } from './promptGenerator'; +import { generatePrompt, copyToClipboard } from './promptGenerator'; import { PromptCodeWebViewProvider } from './webviewProvider'; -import { countTokensInFile, countTokensWithCache, countTokensWithCacheDetailed, clearTokenCache, initializeTokenCounter, tokenCache, countTokens, buildPrompt } from '@promptcode/core'; +import { countTokensInFile, countTokensWithCache, countTokensWithCacheDetailed, clearTokenCache, initializeTokenCounter, tokenCache, countTokens } from '@promptcode/core'; import * as path from 'path'; import * as fs from 'fs'; import * as os from 'os'; @@ -1677,59 +1677,6 @@ function isValidIncludeOptions(options: any): options is { files: boolean; instr typeof options.instructions === 'boolean'; } -// Helper function to generate prompt using core's buildPrompt -async function generatePrompt( - selectedFiles: { - path: string; - tokenCount: number; - workspaceFolderRootPath?: string; - absolutePath?: string; - workspaceFolderName?: string; - }[], - instructions: string, - includeOptions: { files: boolean; instructions: boolean } -): Promise { - const startTime = performance.now(); - - // Early returns for edge cases (per O3-pro recommendation) - if (!includeOptions.files && (!instructions || !includeOptions.instructions)) { - const endTime = performance.now(); - console.log(`Prompt generation took ${endTime - startTime}ms for ${selectedFiles.length} files`); - return ''; - } - - // Early return if no files selected but files are required - if (selectedFiles.length === 0 && includeOptions.files) { - const endTime = performance.now(); - console.log(`Prompt generation took ${endTime - startTime}ms - no files selected`); - return includeOptions.instructions ? instructions : ''; - } - - // Convert to SelectedFile format expected by core - const coreSelectedFiles: SelectedFile[] = selectedFiles.map(file => ({ - path: file.path, - absolutePath: file.absolutePath || path.join(file.workspaceFolderRootPath || '', file.path), - tokenCount: file.tokenCount, - workspaceFolderRootPath: file.workspaceFolderRootPath || '', - workspaceFolderName: file.workspaceFolderName || '' - })); - - // Use core's buildPrompt - const result = await buildPrompt(coreSelectedFiles, instructions, { - includeFiles: includeOptions.files, - includeInstructions: includeOptions.instructions, - includeFileContents: includeOptions.files - }); - - // No transformation needed - core now uses standard tags - const prompt = result.prompt; - - const endTime = performance.now(); - console.log(`Prompt generation took ${endTime - startTime}ms for ${selectedFiles.length} files`); - - return prompt; -} - // Helper function to get selected files with content async function getSelectedFilesWithContent(): Promise { if (!vscode.workspace.workspaceFolders || vscode.workspace.workspaceFolders.length === 0) { @@ -1857,4 +1804,4 @@ export async function savePromptToFile(prompt: string, context?: vscode.Extensio // ... error telemetry code ... } } -// --- End Save to file feature --- \ No newline at end of file +// --- End Save to file feature --- diff --git a/src/promptGenerator.ts b/src/promptGenerator.ts index 80ff2a6..46171f9 100644 --- a/src/promptGenerator.ts +++ b/src/promptGenerator.ts @@ -217,12 +217,17 @@ export async function generatePrompt( ): Promise { log('--- Starting generatePrompt ---'); const workspaceFolders = vscode.workspace.workspaceFolders; - if (!workspaceFolders || workspaceFolders.length === 0) { - log('No workspace folders found. Aborting prompt generation.'); + const hasWorkspace = !!workspaceFolders && workspaceFolders.length > 0; + + // If files are requested but no workspace is open, we cannot proceed safely. + if (!hasWorkspace && includeOptions.files) { + log('No workspace folders found. Aborting prompt generation that requires files.'); return 'No workspace folders available.'; } - const workspaceRoot = workspaceFolders[0].uri.fsPath; - log('Workspace root:', { workspaceRoot }); + + // Instructions-only scenarios can proceed without a workspace. + const workspaceRoot = hasWorkspace ? workspaceFolders[0].uri.fsPath : ''; + log('Workspace root:', { workspaceRoot: workspaceRoot || 'N/A (instructions-only mode)' }); let finalPromptText = ''; let processedInstructions = ''; @@ -337,4 +342,4 @@ export async function generatePrompt( export async function copyToClipboard(text: string): Promise { await vscode.env.clipboard.writeText(text); log('Prompt copied to clipboard.'); -} \ No newline at end of file +} diff --git a/src/test/promptWorkspaceMetadata.test.ts b/src/test/promptWorkspaceMetadata.test.ts new file mode 100644 index 0000000..997f5de --- /dev/null +++ b/src/test/promptWorkspaceMetadata.test.ts @@ -0,0 +1,40 @@ +import * as assert from 'assert'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import { generatePrompt } from '../promptGenerator'; +import type { SelectedFile } from '@promptcode/core'; + +suite('Prompt workspace metadata', () => { + test('generatePrompt includes workspace name and root for each file', async () => { + const workspace = vscode.workspace.workspaceFolders?.[0]; + if (!workspace) { + console.log('Skipping test - no workspace folder available'); + return; + } + + const workspaceRoot = workspace.uri.fsPath; + const workspaceName = workspace.name; + const absolutePath = path.join(workspaceRoot, 'src', 'a.ts'); + + const selectedFiles: SelectedFile[] = [ + { + path: 'src/a.ts', + absolutePath, + tokenCount: 1, // Value is informational only for this assertion + workspaceFolderRootPath: workspaceRoot, + workspaceFolderName: workspaceName + } + ]; + + const prompt = await generatePrompt(selectedFiles, 'Test instructions', { + files: true, + instructions: true + }); + + const escapedRoot = workspaceRoot.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const workspaceLine = new RegExp(`Workspace:\\s*${workspaceName}\\s*\\(${escapedRoot}\\)`); + + assert.match(prompt, workspaceLine, 'prompt should include workspace name and root'); + assert.match(prompt, /File:\s*src\/a\.ts/i, 'prompt should include the relative file path'); + }); +});