Initial code

This commit is contained in:
2025-03-11 17:20:20 +00:00
parent 94b70b0c40
commit 567427b29b
12 changed files with 652 additions and 0 deletions

33
.replit Normal file
View File

@@ -0,0 +1,33 @@
modules = ["nodejs-20"]
[nix]
channel = "stable-24_05"
[workflows]
runButton = "Project"
[[workflows.workflow]]
name = "Project"
mode = "parallel"
author = "agent"
[[workflows.workflow.tasks]]
task = "workflow.run"
args = "Build VSCode Extension"
[[workflows.workflow]]
name = "Build VSCode Extension"
author = "agent"
[workflows.workflow.metadata]
agentRequireRestartOnSave = false
[[workflows.workflow.tasks]]
task = "packager.installForAll"
[[workflows.workflow.tasks]]
task = "shell.exec"
args = "npm run compile && npx @vscode/vsce package"
[deployment]
run = ["sh", "-c", "npm run compile && npx @vscode/vsce package"]

28
.vscodeignore Normal file
View File

@@ -0,0 +1,28 @@
.vscode/**
.vscode-test/**
node_modules/**
src/**
.gitignore
.yarnrc
**/tsconfig.json
**/.eslintrc.json
**/*.map
**/*.ts
.replit
.breakpoints
.local/**
.cache/**
.upm/**
.git/**
.github/**
**/test/**
**/tests/**
**/*.test.js
**/*.spec.js
*.vsix
# Development and build artifacts
generated-icon.png
out/test/**
package-lock.json
# Assets not needed in the final package
attached_assets/**

9
CHANGELOG.md Normal file
View File

@@ -0,0 +1,9 @@
# Change Log
All notable changes to the "prompter" extension will be documented in this file.
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
## [Unreleased]
- Initial release

7
LICENSE Normal file
View File

@@ -0,0 +1,7 @@
Copyright 2025 Abhishek Bhakat
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

153
README.md Normal file
View File

@@ -0,0 +1,153 @@
# Prompter
Code smarter with AI—no more messy copy-pasting. Prompter structures your prompts and applies AI changes seamlessly, streamlining your coding workflow.
## Why Prompter?
- **Too much bloat in your repo?** Stop zipping everything—send only the files that matter.
- **LLM underperforming?** Cut the noise for sharper, more accurate responses.
- **Better AI coding?** Select just the right context to optimize results.
Prompter empowers you to work efficiently with AI, reducing token waste and improving clarity.
## Features
- **Advanced File Selection & Token Estimation**
Precisely filter files and estimate token usage instantly for optimized, cost-effective prompts.
- **Optimized XML Prompt**
Structured file trees, CodeMaps, content, and instructions in XML for maximum LLM clarity.
- **Structured XML Diffs**
Converts LLM-generated XML edits into precise, reviewable diffs—works at any file size.
- **Codemap Extraction**
Scans files locally to extract classes, functions, and references, minimizing tokens and hallucinations. Auto-detects referenced types.
- **Mac-Native Performance**
Built for macOS with native speed and responsiveness—because performance matters.
- **Clipboard Integration**
Copy structured prompts into any AI chat app—your data stays local, no external API needed.
- **Works with Any Model**
Compatible with OpenAI, Anthropic, DeepSeek, Gemini, Azure, OpenRouter, and local models—private and offline when you need it.
- **Privacy First**
Local models, offline scanning, and direct clipboard use—no intermediaries required.
## Installation
*(Note: Installation steps are assumed based on the VS Code context from other files. Adjust as needed.)*
1. Clone the repository:
```bash
git clone <repository-url>
```
2. Open the project in VS Code.
3. Install dependencies:
```bash
npm install
```
4. Build the extension:
```bash
npm run compile
```
5. Press `F5` in VS Code to launch the extension in a development window.
## Usage
1. Open your project in VS Code.
2. Use the Prompter interface to select files and estimate tokens.
3. Generate a structured XML prompt via the clipboard.
4. Paste into your preferred AI model (e.g., ChatGPT, Claude, or a local LLM).
5. Apply the returned XML diffs directly through Prompter for seamless integration.
## Contributing
We welcome contributions! To get started:
1. Fork the repository.
2. Create a feature branch: `git checkout -b my-feature`.
3. Commit your changes: `git commit -m "Add my feature"`.
4. Push to your branch: `git push origin my-feature`.
5. Open a pull request.
See `vsc-extension-quickstart.md` for development setup and testing details.
---
Built with ❤️ by the Prompter team.
Code smarter with AI—no more messy copy-pasting. Prompter structures your prompts and applies AI changes seamlessly, streamlining your coding workflow.
## Why Prompter?
- **Too much bloat in your repo?** Stop zipping everything—send only the files that matter.
- **LLM underperforming?** Cut the noise for sharper, more accurate responses.
- **Better AI coding?** Select just the right context to optimize results.
Prompter empowers you to work efficiently with AI, reducing token waste and improving clarity.
## Features
- **Advanced File Selection & Token Estimation**
Precisely filter files and estimate token usage instantly for optimized, cost-effective prompts.
- **Optimized XML Prompt**
Structured file trees, CodeMaps, content, and instructions in XML for maximum LLM clarity.
- **Structured XML Diffs**
Converts LLM-generated XML edits into precise, reviewable diffs—works at any file size.
- **Codemap Extraction**
Scans files locally to extract classes, functions, and references, minimizing tokens and hallucinations. Auto-detects referenced types.
- **Mac-Native Performance**
Built for macOS with native speed and responsiveness—because performance matters.
- **Clipboard Integration**
Copy structured prompts into any AI chat app—your data stays local, no external API needed.
- **Works with Any Model**
Compatible with OpenAI, Anthropic, DeepSeek, Gemini, Azure, OpenRouter, and local models—private and offline when you need it.
- **Privacy First**
Local models, offline scanning, and direct clipboard use—no intermediaries required.
## Installation
*(Note: Installation steps are assumed based on the VS Code context from other files. Adjust as needed.)*
1. Clone the repository:
```bash
git clone <repository-url>
```
2. Open the project in VS Code.
3. Install dependencies:
```bash
npm install
```
4. Build the extension:
```bash
npm run compile
```
5. Press `F5` in VS Code to launch the extension in a development window.
## Usage
1. Open your project in VS Code.
2. Use the Prompter interface to select files and estimate tokens.
3. Generate a structured XML prompt via the clipboard.
4. Paste into your preferred AI model (e.g., ChatGPT, Claude, or a local LLM).
5. Apply the returned XML diffs directly through Prompter for seamless integration.
## Contributing
We welcome contributions! To get started:
1. Fork the repository.
2. Create a feature branch: `git checkout -b my-feature`.
3. Commit your changes: `git commit -m "Add my feature"`.
4. Push to your branch: `git push origin my-feature`.
5. Open a pull request.
See `vsc-extension-quickstart.md` for development setup and testing details.
---
Built with ❤️ by the Prompter team.

28
eslint.config.mjs Normal file
View File

@@ -0,0 +1,28 @@
import typescriptEslint from "@typescript-eslint/eslint-plugin";
import tsParser from "@typescript-eslint/parser";
export default [{
files: ["**/*.ts"],
}, {
plugins: {
"@typescript-eslint": typescriptEslint,
},
languageOptions: {
parser: tsParser,
ecmaVersion: 2022,
sourceType: "module",
},
rules: {
"@typescript-eslint/naming-convention": ["warn", {
selector: "import",
format: ["camelCase", "PascalCase"],
}],
curly: "warn",
eqeqeq: "warn",
"no-throw-literal": "warn",
semi: "warn",
},
}];

80
package.json Normal file
View File

@@ -0,0 +1,80 @@
{
"name": "prompter",
"displayName": "Prompter",
"description": "Easy prompt generation and apply edits using prompter.",
"version": "0.0.1",
"publisher": "abhishekbhakat",
"engines": {
"vscode": "^1.98.0"
},
"categories": [
"Other"
],
"activationEvents": [],
"main": "./out/extension.js",
"contributes": {
"commands": [
{
"command": "prompter.toggleSelection",
"title": "Toggle File Selection"
},
{
"command": "prompter.generatePrompt",
"title": "Generate XML Prompt"
}
],
"viewsContainers": {
"activitybar": [
{
"id": "prompter-sidebar",
"title": "Prompter",
"icon": "resources/icon.svg"
}
]
},
"views": {
"prompter-sidebar": [
{
"id": "prompterView",
"name": "Prompter"
}
]
},
"menus": {
"view/title": [
{
"command": "prompter.generatePrompt",
"group": "navigation"
}
],
"view/item/context": [
{
"command": "prompter.toggleSelection",
"when": "view == prompterView"
}
]
}
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"pretest": "npm run compile && npm run lint",
"lint": "eslint src",
"test": "vscode-test"
},
"devDependencies": {
"@types/mocha": "^10.0.10",
"@types/node": "20.x",
"@types/vscode": "^1.98.0",
"@typescript-eslint/eslint-plugin": "^8.25.0",
"@typescript-eslint/parser": "^8.25.0",
"@vscode/test-cli": "^0.0.10",
"@vscode/test-electron": "^2.4.1",
"eslint": "^9.21.0",
"typescript": "^5.7.3"
},
"dependencies": {
"@vscode/vsce": "^3.2.2"
}
}

4
resources/icon.svg Normal file
View File

@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 5C4 4.44772 4.44772 4 5 4H19C19.5523 4 20 4.44772 20 5V19C20 19.5523 19.5523 20 19 20H5C4.44772 20 4 19.5523 4 19V5Z" stroke="currentColor" stroke-width="2"/>
<path d="M8 9H16M8 12H16M8 15H13" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 380 B

234
src/extension.ts Normal file
View File

@@ -0,0 +1,234 @@
// The module 'vscode' contains the VS Code extensibility API
import * as vscode from 'vscode';
import * as path from 'path';
// Custom TreeItem for files and folders
class FileTreeItem extends vscode.TreeItem {
constructor(
public readonly resourceUri: vscode.Uri,
public readonly collapsibleState: vscode.TreeItemCollapsibleState,
public readonly isDirectory: boolean
) {
super(resourceUri, collapsibleState);
this.contextValue = isDirectory ? 'directory' : 'file';
this.tooltip = resourceUri.fsPath;
this.description = isDirectory ? 'folder' : path.extname(resourceUri.fsPath);
this.checkboxState = vscode.TreeItemCheckboxState.Unchecked;
}
}
// TreeView provider for the sidebar
class PrompterTreeProvider implements vscode.TreeDataProvider<FileTreeItem> {
private _onDidChangeTreeData: vscode.EventEmitter<FileTreeItem | undefined | null | void> = new vscode.EventEmitter<FileTreeItem | undefined | null | void>();
readonly onDidChangeTreeData: vscode.Event<FileTreeItem | undefined | null | void> = this._onDidChangeTreeData.event;
private selectedFiles: Set<string> = new Set();
private xmlEditsEnabled: boolean = false;
constructor(private workspaceRoot: string | undefined) {}
getTreeItem(element: FileTreeItem): vscode.TreeItem {
const item = element;
if (this.selectedFiles.has(element.resourceUri.fsPath)) {
item.checkboxState = vscode.TreeItemCheckboxState.Checked;
item.label = path.basename(element.resourceUri.fsPath);
} else {
item.checkboxState = vscode.TreeItemCheckboxState.Unchecked;
item.label = path.basename(element.resourceUri.fsPath);
}
return item;
}
async getChildren(element?: FileTreeItem): Promise<FileTreeItem[]> {
if (!this.workspaceRoot) {
vscode.window.showInformationMessage('No workspace folder is opened');
return Promise.resolve([]);
}
if (element) {
const dirPath = element.resourceUri.fsPath;
return this.getFilesInDirectory(dirPath);
} else {
return this.getFilesInDirectory(this.workspaceRoot);
}
}
private async getFilesInDirectory(dirPath: string): Promise<FileTreeItem[]> {
try {
const files = await vscode.workspace.fs.readDirectory(vscode.Uri.file(dirPath));
return Promise.all(files.map(async ([name, type]) => {
const filePath = path.join(dirPath, name);
const uri = vscode.Uri.file(filePath);
const isDirectory = (type & vscode.FileType.Directory) !== 0;
return new FileTreeItem(
uri,
isDirectory ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None,
isDirectory
);
}));
} catch (error) {
console.error(`Error reading directory ${dirPath}:`, error);
return [];
}
}
toggleSelection(item: FileTreeItem): void {
const filePath = item.resourceUri.fsPath;
if (this.selectedFiles.has(filePath)) {
this.selectedFiles.delete(filePath);
} else {
this.selectedFiles.add(filePath);
}
this._onDidChangeTreeData.fire();
}
getSelectedFiles(): Set<string> {
return this.selectedFiles;
}
toggleXmlEdits(): void {
this.xmlEditsEnabled = !this.xmlEditsEnabled;
}
isXmlEditsEnabled(): boolean {
return this.xmlEditsEnabled;
}
refresh(): void {
this._onDidChangeTreeData.fire();
}
}
// Utility function to estimate tokens in text
function estimateTokens(text: string): number {
// Rough estimation: Split by whitespace and punctuation
// This is a simple approximation, actual token count may vary by model
const words = text.split(/[\s\p{P}]+/u).filter(Boolean);
return Math.ceil(words.length * 1.3); // Add 30% overhead for special tokens
}
// Function to read file content
async function readFileContent(filePath: string): Promise<string> {
try {
const readData = await vscode.workspace.fs.readFile(vscode.Uri.file(filePath));
return Buffer.from(readData).toString('utf8');
} catch (error) {
console.error(`Error reading file ${filePath}:`, error);
return '';
}
}
// Function to generate XML prompt
function generateXMLPrompt(files: Map<string, { content: string; tokens: number }>, xmlEdits: boolean): string {
const xmlParts = ['<?xml version="1.0" encoding="UTF-8"?>', '<prompt>'];
// Add files section
xmlParts.push(' <files>');
for (const [path, { content, tokens }] of files) {
xmlParts.push(' <file>');
xmlParts.push(` <path>${path}</path>`);
xmlParts.push(` <tokens>${tokens}</tokens>`);
xmlParts.push(` <content><![CDATA[${content}]]></content>`);
xmlParts.push(' </file>');
}
xmlParts.push(' </files>');
// Add XML edits flag if enabled
if (xmlEdits) {
xmlParts.push(' <options>');
xmlParts.push(' <xml_edits>true</xml_edits>');
xmlParts.push(' </options>');
}
xmlParts.push('</prompt>');
return xmlParts.join('\n');
}
// This method is called when your extension is activated
export function activate(context: vscode.ExtensionContext) {
console.log('Congratulations, your extension "prompter" is now active!');
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri?.fsPath;
const prompterTreeProvider = new PrompterTreeProvider(workspaceRoot);
// Register the TreeView
const treeView = vscode.window.createTreeView('prompterView', {
treeDataProvider: prompterTreeProvider,
showCollapseAll: true
});
// Create XML edits toggle button
const xmlEditsButton = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
xmlEditsButton.text = "$(diff-added) XML Edits";
xmlEditsButton.tooltip = "Toggle XML Edits mode";
xmlEditsButton.command = 'prompter.toggleXmlEdits';
xmlEditsButton.show();
// Create copy button
const copyButton = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right);
copyButton.text = "$(clippy) Copy";
copyButton.tooltip = "Generate and copy XML prompt";
copyButton.command = 'prompter.generatePrompt';
copyButton.show();
// Register command to toggle file selection
let toggleSelectionCommand = vscode.commands.registerCommand('prompter.toggleSelection', (item: FileTreeItem) => {
prompterTreeProvider.toggleSelection(item);
});
// Register command to toggle XML edits
let toggleXmlEditsCommand = vscode.commands.registerCommand('prompter.toggleXmlEdits', () => {
prompterTreeProvider.toggleXmlEdits();
xmlEditsButton.text = prompterTreeProvider.isXmlEditsEnabled() ?
"$(check) XML Edits" : "$(diff-added) XML Edits";
});
// Register command to generate prompt from selected files
let generatePromptCommand = vscode.commands.registerCommand('prompter.generatePrompt', async () => {
const selectedFiles = prompterTreeProvider.getSelectedFiles();
if (selectedFiles.size === 0) {
vscode.window.showInformationMessage('Please select files first');
return;
}
let totalTokens = 0;
const fileContents = new Map<string, { content: string; tokens: number }>();
// Process each selected file
for (const filePath of selectedFiles) {
const content = await readFileContent(filePath);
const tokens = estimateTokens(content);
totalTokens += tokens;
fileContents.set(filePath, { content, tokens });
// Show individual file token count
vscode.window.showInformationMessage(
`File: ${filePath}\nEstimated tokens: ${tokens}`
);
}
// Show total token count
vscode.window.showInformationMessage(
`Total estimated tokens for ${selectedFiles.size} files: ${totalTokens}`
);
// Generate XML prompt
const xmlPrompt = generateXMLPrompt(fileContents, prompterTreeProvider.isXmlEditsEnabled());
// Copy to clipboard
await vscode.env.clipboard.writeText(xmlPrompt);
vscode.window.showInformationMessage('XML prompt copied to clipboard!');
});
context.subscriptions.push(
treeView,
xmlEditsButton,
copyButton,
toggleSelectionCommand,
toggleXmlEditsCommand,
generatePromptCommand
);
}
// This method is called when your extension is deactivated
export function deactivate() {}

View File

@@ -0,0 +1,15 @@
import * as assert from 'assert';
// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from 'vscode';
// import * as myExtension from '../../extension';
suite('Extension Test Suite', () => {
vscode.window.showInformationMessage('Start all tests.');
test('Sample test', () => {
assert.strictEqual(-1, [1, 2, 3].indexOf(5));
assert.strictEqual(-1, [1, 2, 3].indexOf(0));
});
});

17
tsconfig.json Normal file
View File

@@ -0,0 +1,17 @@
{
"compilerOptions": {
"module": "Node16",
"target": "ES2022",
"outDir": "out",
"lib": [
"ES2022"
],
"sourceMap": true,
"rootDir": "src",
"strict": true, /* enable all strict type-checking options */
/* Additional Checks */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
}
}

View File

@@ -0,0 +1,44 @@
# Welcome to your VS Code Extension
## What's in the folder
* This folder contains all of the files necessary for your extension.
* `package.json` - this is the manifest file in which you declare your extension and command.
* The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesnt yet need to load the plugin.
* `src/extension.ts` - this is the main file where you will provide the implementation of your command.
* The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
* We pass the function containing the implementation of the command as the second parameter to `registerCommand`.
## Get up and running straight away
* Press `F5` to open a new window with your extension loaded.
* Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`.
* Set breakpoints in your code inside `src/extension.ts` to debug your extension.
* Find output from your extension in the debug console.
## Make changes
* You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`.
* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
## Explore the API
* You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`.
## Run tests
* Install the [Extension Test Runner](https://marketplace.visualstudio.com/items?itemName=ms-vscode.extension-test-runner)
* Run the "watch" task via the **Tasks: Run Task** command. Make sure this is running, or tests might not be discovered.
* Open the Testing view from the activity bar and click the Run Test" button, or use the hotkey `Ctrl/Cmd + ; A`
* See the output of the test result in the Test Results view.
* Make changes to `src/test/extension.test.ts` or create new test files inside the `test` folder.
* The provided test runner will only consider files matching the name pattern `**.test.ts`.
* You can create folders inside the `test` folder to structure your tests any way you want.
## Go further
* [Follow UX guidelines](https://code.visualstudio.com/api/ux-guidelines/overview) to create extensions that seamlessly integrate with VS Code's native interface and patterns.
* Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension).
* [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VS Code extension marketplace.
* Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration).
* Integrate to the [report issue](https://code.visualstudio.com/api/get-started/wrapping-up#issue-reporting) flow to get issue and feature requests reported by users.