Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions packages/cli/docs/VSCODE-NO-REGRESSION-PROOF.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 {
Expand All @@ -47,16 +47,17 @@ import {
clearTokenCache,
initializeTokenCounter,
tokenCache,
countTokens,
buildPrompt
countTokens
} from '@promptcode/core';
import { generatePrompt } from './promptGenerator';
```

**VS Code Extension does NOT import:**
- ❌ `optimizeSelection`
- ❌ `generatePatternsFromSelection`
- ❌ `patternOptimizer`
- ❌ Any pattern-related utilities
- ❌ `buildPrompt` directly (prompt generation runs through `promptGenerator`)

### 4. CLI-Only Changes

Expand Down Expand Up @@ -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.
All functionality will work exactly as before because we haven't touched any code the VS Code extension uses.
10 changes: 8 additions & 2 deletions packages/cli/test/verify-vscode-no-regression.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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
*/
*/
5 changes: 4 additions & 1 deletion scripts/build-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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',
Expand Down Expand Up @@ -58,4 +61,4 @@ esbuild.build({
}).catch((error) => {
console.error('Error building test files:', error);
process.exit(1);
});
});
59 changes: 3 additions & 56 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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<string> {
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<SelectedFile[]> {
if (!vscode.workspace.workspaceFolders || vscode.workspace.workspaceFolders.length === 0) {
Expand Down Expand Up @@ -1857,4 +1804,4 @@ export async function savePromptToFile(prompt: string, context?: vscode.Extensio
// ... error telemetry code ...
}
}
// --- End Save to file feature ---
// --- End Save to file feature ---
15 changes: 10 additions & 5 deletions src/promptGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,17 @@ export async function generatePrompt(
): Promise<string> {
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 = '';
Expand Down Expand Up @@ -337,4 +342,4 @@ export async function generatePrompt(
export async function copyToClipboard(text: string): Promise<void> {
await vscode.env.clipboard.writeText(text);
log('Prompt copied to clipboard.');
}
}
40 changes: 40 additions & 0 deletions src/test/promptWorkspaceMetadata.test.ts
Original file line number Diff line number Diff line change
@@ -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');
});
});