vscode-pi-companion 0.0.10
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/.github/workflows/publish.yml +62 -0
- package/.github/workflows/release.yml +19 -0
- package/.vscode/launch.json +17 -0
- package/.vscode/tasks.json +12 -0
- package/.vscodeignore +12 -0
- package/LICENSE +1 -0
- package/README.md +83 -0
- package/extensions/index.ts +370 -0
- package/extensions/package.json +9 -0
- package/package.json +88 -0
- package/screenshot.png +0 -0
- package/src/extension.ts +84 -0
- package/src/ide-server.ts +381 -0
- package/src/open-files-manager.ts +249 -0
- package/src/types.ts +66 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
name: Publish to VS Code Marketplace & npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish-vscode:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
with:
|
|
16
|
+
persist-credentials: true
|
|
17
|
+
|
|
18
|
+
- uses: actions/setup-node@v4
|
|
19
|
+
with:
|
|
20
|
+
node-version: '20'
|
|
21
|
+
cache: 'npm'
|
|
22
|
+
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: npm ci
|
|
25
|
+
|
|
26
|
+
- name: Install vsce
|
|
27
|
+
run: npm install -g @vscode/vsce
|
|
28
|
+
|
|
29
|
+
- name: Publish to VS Code Marketplace
|
|
30
|
+
env:
|
|
31
|
+
VSCE_TOKEN: ${{ secrets.VSCE_TOKEN }}
|
|
32
|
+
run: |
|
|
33
|
+
git config --local user.name "github-actions"
|
|
34
|
+
git config --local user.email "github-actions@github.com"
|
|
35
|
+
|
|
36
|
+
# Package and publish with version bump
|
|
37
|
+
npx vsce package
|
|
38
|
+
vsce publish patch --pat $VSCE_TOKEN --skip-duplicate
|
|
39
|
+
|
|
40
|
+
# Push the tag created by vsce
|
|
41
|
+
git push --follow-tags origin main
|
|
42
|
+
|
|
43
|
+
publish-npm:
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
needs: publish-vscode
|
|
46
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
47
|
+
permissions:
|
|
48
|
+
id-token: write # Required for OIDC Trusted Publishing
|
|
49
|
+
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v4
|
|
52
|
+
|
|
53
|
+
- uses: actions/setup-node@v4
|
|
54
|
+
with:
|
|
55
|
+
node-version: '20'
|
|
56
|
+
cache: 'npm'
|
|
57
|
+
|
|
58
|
+
- name: Install dependencies
|
|
59
|
+
run: npm ci
|
|
60
|
+
|
|
61
|
+
- name: Publish to npm
|
|
62
|
+
run: npm publish --access public --skip-duplicate
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: write
|
|
9
|
+
pull-requests: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
release:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: googleapis/release-please-action@v4
|
|
16
|
+
with:
|
|
17
|
+
release-type: node
|
|
18
|
+
package-name: vscode-pi-companion
|
|
19
|
+
target-branch: main
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.2.0",
|
|
3
|
+
"configurations": [
|
|
4
|
+
{
|
|
5
|
+
"name": "Run Extension",
|
|
6
|
+
"type": "extensionHost",
|
|
7
|
+
"request": "launch",
|
|
8
|
+
"args": [
|
|
9
|
+
"--extensionDevelopmentPath=${workspaceFolder}"
|
|
10
|
+
],
|
|
11
|
+
"outFiles": [
|
|
12
|
+
"${workspaceFolder}/out/**/*.js"
|
|
13
|
+
],
|
|
14
|
+
"preLaunchTask": "${defaultBuildTask}"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
package/.vscodeignore
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name":"MIT","url":"https://opensource.org/licenses/MIT"}
|
package/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# VS Code PI Companion
|
|
2
|
+
|
|
3
|
+
A VS Code extension that provides real-time IDE context (files, selection) to pi and other agents.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Real-time context**: Monitors open files, cursor position, and selection
|
|
8
|
+
- **Instant updates**: Pushes context changes via SSE
|
|
9
|
+
- **Seamless integration**: Selected lines injected into LLM context automatically
|
|
10
|
+
- **Widget display**: Shows current file/selection in pi UI
|
|
11
|
+
|
|
12
|
+
## Architecture
|
|
13
|
+
|
|
14
|
+
**VS Code Extension** → SSE ← **pi Extension**
|
|
15
|
+
|
|
16
|
+
- **VS Code**: Context tracking, SSE server
|
|
17
|
+
- **pi**: SSE client, Widget, Context injection
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
### 1. Install pi Extension
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pi install git:github.com/ravshansbox/vscode-pi-companion
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Or manually:
|
|
28
|
+
```bash
|
|
29
|
+
mkdir -p ~/.pi/agent/extensions/vscode-pi-companion
|
|
30
|
+
git clone git@github.com:ravshansbox/vscode-pi-companion.git ~/.pi/agent/extensions/vscode-pi-companion
|
|
31
|
+
cd ~/.pi/agent/extensions/vscode-pi-companion
|
|
32
|
+
npm install
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. Install VS Code Extension
|
|
36
|
+
|
|
37
|
+
The pi extension will auto-install the VS Code extension when you run `pi` in a VS Code terminal.
|
|
38
|
+
|
|
39
|
+
Alternatively, install manually:
|
|
40
|
+
```bash
|
|
41
|
+
# Download the .vsix from releases
|
|
42
|
+
code --install-extension vscode-pi-companion.vsix
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Then reload VS Code window.
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
1. Open a folder in VS Code
|
|
50
|
+
2. Open VS Code terminal (`Ctrl+\``)
|
|
51
|
+
3. Run `pi`
|
|
52
|
+
4. The extension auto-connects and shows current file in widget
|
|
53
|
+
5. Select code → widget updates instantly
|
|
54
|
+
6. Selected lines are injected into LLM context automatically
|
|
55
|
+
|
|
56
|
+
## Widget Display
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
📄 filename # no selection
|
|
60
|
+
📄 filename L9 # single line
|
|
61
|
+
📄 filename L9:14 # multi-line selection
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## MCP Tools (for LLM)
|
|
65
|
+
|
|
66
|
+
| Tool | Description |
|
|
67
|
+
|------|-------------|
|
|
68
|
+
| `get_ide_context` | Full IDE context |
|
|
69
|
+
| `get_current_file` | Current file with selection |
|
|
70
|
+
| `list_open_files` | List all open files |
|
|
71
|
+
|
|
72
|
+
## Development
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Build VS Code extension
|
|
76
|
+
npm install
|
|
77
|
+
npm run compile
|
|
78
|
+
|
|
79
|
+
# Package as .vsix
|
|
80
|
+
npx vsce package
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Press F5 to run in Extension Development Host.
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PI Companion Extension
|
|
3
|
+
*
|
|
4
|
+
* Connects to the VS Code extension via SSE for real-time context updates.
|
|
5
|
+
* No polling, instant updates, no tool calls visible.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
9
|
+
import * as fs from "fs";
|
|
10
|
+
import * as path from "path";
|
|
11
|
+
import * as os from "os";
|
|
12
|
+
import * as http from "http";
|
|
13
|
+
import { execSync } from "child_process";
|
|
14
|
+
import { Type } from "@sinclair/typebox";
|
|
15
|
+
|
|
16
|
+
// Check if running in VS Code integrated terminal
|
|
17
|
+
function isRunningInVSCode(): boolean {
|
|
18
|
+
return process.env.TERM_PROGRAM === 'vscode';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Install VS Code extension from marketplace
|
|
22
|
+
function installFromMarketplace(ctx: ExtensionContext): void {
|
|
23
|
+
try {
|
|
24
|
+
execSync('code --install-extension ravshansbox.vscode-pi-companion --force', { stdio: 'pipe' });
|
|
25
|
+
ctx.ui.notify("PI Companion: VS Code extension installed. Reload window to activate.", "info");
|
|
26
|
+
} catch {
|
|
27
|
+
// Silently ignore
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Check if extension is installed
|
|
32
|
+
function isExtensionInstalled(): boolean {
|
|
33
|
+
try {
|
|
34
|
+
const result = execSync('code --list-extensions', { encoding: 'utf8' });
|
|
35
|
+
return result.includes('ravshansbox.vscode-pi-companion');
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Install/update VS Code extension if running in VS Code terminal
|
|
42
|
+
async function installVSCodeExtension(ctx: ExtensionContext): Promise<void> {
|
|
43
|
+
if (!isRunningInVSCode()) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const isInstalled = isExtensionInstalled();
|
|
48
|
+
|
|
49
|
+
if (isInstalled) {
|
|
50
|
+
// Already installed - check for updates
|
|
51
|
+
const latestVersion = await getLatestVersion();
|
|
52
|
+
if (latestVersion) {
|
|
53
|
+
console.log(`[PI Companion] Update available: v${latestVersion}`);
|
|
54
|
+
console.log(`[PI Companion] Run: pi install git:github.com/ravshansbox/vscode-pi-companion`);
|
|
55
|
+
}
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Not installed - install from marketplace
|
|
60
|
+
installFromMarketplace(ctx);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Get latest version from GitHub
|
|
64
|
+
async function getLatestVersion(): Promise<string | null> {
|
|
65
|
+
try {
|
|
66
|
+
const response = await fetch('https://api.github.com/repos/ravshansbox/vscode-pi-companion/releases/latest', {
|
|
67
|
+
headers: { 'Accept': 'application/vnd.github.v3+json' }
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (!response.ok) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const release = await response.json() as { tag_name?: string };
|
|
75
|
+
return release.tag_name?.replace('v', '') || null;
|
|
76
|
+
} catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface IdeContext {
|
|
82
|
+
workspaceState: {
|
|
83
|
+
openFiles: Array<{
|
|
84
|
+
path: string;
|
|
85
|
+
timestamp: number;
|
|
86
|
+
isActive?: boolean;
|
|
87
|
+
selectedText?: string;
|
|
88
|
+
cursor?: { line: number; character: number };
|
|
89
|
+
selectionStart?: { line: number; character: number };
|
|
90
|
+
selectionEnd?: { line: number; character: number };
|
|
91
|
+
content?: string;
|
|
92
|
+
}>;
|
|
93
|
+
isTrusted: boolean;
|
|
94
|
+
workspacePath: string;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let currentContext: IdeContext | undefined;
|
|
99
|
+
let currentCtx: ExtensionContext | undefined;
|
|
100
|
+
let lastWidgetKey = "";
|
|
101
|
+
let widgetDebounceTimer: NodeJS.Timeout | undefined;
|
|
102
|
+
let reconnectTimer: NodeJS.Timeout | undefined;
|
|
103
|
+
let sseRequest: http.ClientRequest | undefined;
|
|
104
|
+
|
|
105
|
+
function getPort(): number | undefined {
|
|
106
|
+
const configDir = path.join(os.tmpdir(), "pi-companion");
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
const files = fs.readdirSync(configDir);
|
|
110
|
+
const connectionFiles = files
|
|
111
|
+
.filter((f) => f.startsWith("connection-") && f.endsWith(".json"))
|
|
112
|
+
.map((f) => ({
|
|
113
|
+
name: f,
|
|
114
|
+
path: path.join(configDir, f),
|
|
115
|
+
mtime: fs.statSync(path.join(configDir, f)).mtime.getTime(),
|
|
116
|
+
}))
|
|
117
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
118
|
+
|
|
119
|
+
if (connectionFiles.length > 0) {
|
|
120
|
+
const content = fs.readFileSync(connectionFiles[0].path, "utf-8");
|
|
121
|
+
const info = JSON.parse(content);
|
|
122
|
+
return info.port;
|
|
123
|
+
}
|
|
124
|
+
} catch {
|
|
125
|
+
// No connection files found
|
|
126
|
+
}
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function updateWidget(): void {
|
|
131
|
+
if (!currentCtx) return;
|
|
132
|
+
|
|
133
|
+
if (!currentContext || !currentContext.workspaceState.openFiles.length) {
|
|
134
|
+
currentCtx.ui.setWidget("pi-companion", ["PI Companion: No files open"]);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const { workspaceState } = currentContext;
|
|
139
|
+
const lines: string[] = [];
|
|
140
|
+
|
|
141
|
+
const activeFile = workspaceState.openFiles.find((f) => f.isActive);
|
|
142
|
+
|
|
143
|
+
if (activeFile) {
|
|
144
|
+
const filename = activeFile.path.split("/").pop() || activeFile.path;
|
|
145
|
+
|
|
146
|
+
// Build location info - ONLY show when text is actually selected
|
|
147
|
+
let location = "";
|
|
148
|
+
if (activeFile.selectionStart && activeFile.selectionEnd) {
|
|
149
|
+
const start = activeFile.selectionStart.line;
|
|
150
|
+
const end = activeFile.selectionEnd.line;
|
|
151
|
+
if (start === end) {
|
|
152
|
+
location = ` L${start}`;
|
|
153
|
+
} else {
|
|
154
|
+
location = ` L${start}:${end}`;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Don't show cursor position when nothing is selected
|
|
158
|
+
|
|
159
|
+
lines.push(`📄 ${filename}${location}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const otherCount = workspaceState.openFiles.length - 1;
|
|
163
|
+
if (otherCount > 0) {
|
|
164
|
+
lines.push(` +${otherCount} more file${otherCount > 1 ? "s" : ""} open`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const widgetKey = lines.join("|");
|
|
168
|
+
|
|
169
|
+
// Debounce widget updates to avoid flickering during rapid selection changes
|
|
170
|
+
if (widgetDebounceTimer) {
|
|
171
|
+
clearTimeout(widgetDebounceTimer);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
widgetDebounceTimer = setTimeout(() => {
|
|
175
|
+
if (widgetKey !== lastWidgetKey) {
|
|
176
|
+
lastWidgetKey = widgetKey;
|
|
177
|
+
currentCtx!.ui.setWidget("pi-companion", lines);
|
|
178
|
+
}
|
|
179
|
+
widgetDebounceTimer = undefined;
|
|
180
|
+
}, 50); // Wait 50ms after last update
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function connectSSE(): void {
|
|
184
|
+
const port = getPort();
|
|
185
|
+
if (!port) return;
|
|
186
|
+
|
|
187
|
+
// Close existing connection
|
|
188
|
+
if (sseRequest) {
|
|
189
|
+
sseRequest.destroy();
|
|
190
|
+
sseRequest = undefined;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const url = new URL(`http://127.0.0.1:${port}/context/stream`);
|
|
194
|
+
|
|
195
|
+
sseRequest = http.get({
|
|
196
|
+
hostname: url.hostname,
|
|
197
|
+
port: url.port,
|
|
198
|
+
path: url.pathname,
|
|
199
|
+
}, (res) => {
|
|
200
|
+
let buffer = "";
|
|
201
|
+
|
|
202
|
+
res.on("data", (chunk: string) => {
|
|
203
|
+
buffer += chunk;
|
|
204
|
+
|
|
205
|
+
// Process complete SSE messages
|
|
206
|
+
const lines = buffer.split("\n");
|
|
207
|
+
buffer = lines.pop() || "";
|
|
208
|
+
|
|
209
|
+
for (const line of lines) {
|
|
210
|
+
if (line.startsWith("data: ")) {
|
|
211
|
+
const data = line.slice(6);
|
|
212
|
+
if (data && data !== "[object Object]") {
|
|
213
|
+
try {
|
|
214
|
+
const parsed = JSON.parse(data);
|
|
215
|
+
currentContext = parsed;
|
|
216
|
+
updateWidget();
|
|
217
|
+
} catch {
|
|
218
|
+
// Ignore parse errors
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
res.on("end", () => {
|
|
226
|
+
scheduleReconnect();
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
res.on("error", () => {
|
|
230
|
+
scheduleReconnect();
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
sseRequest.on("error", () => {
|
|
235
|
+
scheduleReconnect();
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function scheduleReconnect(): void {
|
|
240
|
+
if (reconnectTimer || !currentCtx) return;
|
|
241
|
+
reconnectTimer = setTimeout(() => {
|
|
242
|
+
reconnectTimer = undefined;
|
|
243
|
+
connectSSE();
|
|
244
|
+
}, 2000);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export default function (pi: ExtensionAPI) {
|
|
248
|
+
// Inject IDE context before each prompt
|
|
249
|
+
pi.on("before_agent_start", async (event, ctx) => {
|
|
250
|
+
if (!currentContext) return;
|
|
251
|
+
|
|
252
|
+
const activeFile = currentContext.workspaceState.openFiles.find((f) => f.isActive);
|
|
253
|
+
if (!activeFile) return;
|
|
254
|
+
|
|
255
|
+
let contextText = "";
|
|
256
|
+
|
|
257
|
+
if (activeFile.selectedText) {
|
|
258
|
+
// Inject selected text
|
|
259
|
+
contextText = `Current selection in ${activeFile.path}:\n\`\`\`\n${activeFile.selectedText}\n\`\`\``;
|
|
260
|
+
} else if (activeFile.content) {
|
|
261
|
+
// Inject full file content for small files, or first 100 lines
|
|
262
|
+
const lines = activeFile.content.split("\n");
|
|
263
|
+
if (lines.length <= 100) {
|
|
264
|
+
contextText = `Current file ${activeFile.path}:\n\`\`\`\n${activeFile.content}\n\`\`\``;
|
|
265
|
+
} else {
|
|
266
|
+
contextText = `Current file ${activeFile.path} (first 100 lines):\n\`\`\`\n${lines.slice(0, 100).join("\n")}\n\`\`\``;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (contextText) {
|
|
271
|
+
return {
|
|
272
|
+
message: {
|
|
273
|
+
customType: "pi-companion",
|
|
274
|
+
content: contextText,
|
|
275
|
+
display: false, // Don't show in chat history
|
|
276
|
+
},
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
282
|
+
currentCtx = ctx;
|
|
283
|
+
|
|
284
|
+
// Auto-install/update VS Code extension if needed
|
|
285
|
+
installVSCodeExtension(ctx);
|
|
286
|
+
|
|
287
|
+
const port = getPort();
|
|
288
|
+
if (port) {
|
|
289
|
+
ctx.ui.notify("PI Companion: Connected to VS Code", "info");
|
|
290
|
+
connectSSE();
|
|
291
|
+
} else {
|
|
292
|
+
ctx.ui.notify("PI Companion: VS Code extension not detected", "warning");
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
pi.on("session_shutdown", async () => {
|
|
297
|
+
if (reconnectTimer) {
|
|
298
|
+
clearTimeout(reconnectTimer);
|
|
299
|
+
reconnectTimer = undefined;
|
|
300
|
+
}
|
|
301
|
+
if (sseRequest) {
|
|
302
|
+
sseRequest.destroy();
|
|
303
|
+
sseRequest = undefined;
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// Register tools
|
|
308
|
+
pi.registerTool({
|
|
309
|
+
name: "get_ide_context",
|
|
310
|
+
label: "Get IDE Context",
|
|
311
|
+
description: "Get the current IDE context: open files, cursor position, and selection",
|
|
312
|
+
parameters: Type.Object({}),
|
|
313
|
+
async execute() {
|
|
314
|
+
if (!currentContext) {
|
|
315
|
+
return {
|
|
316
|
+
content: [{ type: "text", text: "No IDE context available. Make sure the PI Companion VS Code extension is installed and running." }],
|
|
317
|
+
details: {},
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
content: [{ type: "text", text: JSON.stringify(currentContext, null, 2) }],
|
|
322
|
+
details: {},
|
|
323
|
+
};
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
pi.registerTool({
|
|
328
|
+
name: "get_current_file",
|
|
329
|
+
label: "Get Current File",
|
|
330
|
+
description: "Get the content of the currently active file in the IDE",
|
|
331
|
+
parameters: Type.Object({}),
|
|
332
|
+
async execute() {
|
|
333
|
+
if (!currentContext) {
|
|
334
|
+
return { content: [{ type: "text", text: "No IDE context available" }], details: {} };
|
|
335
|
+
}
|
|
336
|
+
const activeFile = currentContext.workspaceState.openFiles.find((f) => f.isActive);
|
|
337
|
+
if (!activeFile) {
|
|
338
|
+
return { content: [{ type: "text", text: "No active file" }], details: {} };
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
342
|
+
path: activeFile.path,
|
|
343
|
+
cursor: activeFile.cursor,
|
|
344
|
+
selection: activeFile.selectedText,
|
|
345
|
+
content: activeFile.content,
|
|
346
|
+
}, null, 2) }],
|
|
347
|
+
details: {},
|
|
348
|
+
};
|
|
349
|
+
},
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
pi.registerTool({
|
|
353
|
+
name: "list_open_files",
|
|
354
|
+
label: "List Open Files",
|
|
355
|
+
description: "List all files currently open in the IDE",
|
|
356
|
+
parameters: Type.Object({}),
|
|
357
|
+
async execute() {
|
|
358
|
+
if (!currentContext) {
|
|
359
|
+
return { content: [{ type: "text", text: "No IDE context available" }], details: {} };
|
|
360
|
+
}
|
|
361
|
+
const files = currentContext.workspaceState.openFiles.map((f) => ({
|
|
362
|
+
path: f.path,
|
|
363
|
+
isActive: f.isActive,
|
|
364
|
+
cursor: f.cursor,
|
|
365
|
+
hasSelection: !!f.selectedText,
|
|
366
|
+
}));
|
|
367
|
+
return { content: [{ type: "text", text: JSON.stringify(files, null, 2) }], details: {} };
|
|
368
|
+
},
|
|
369
|
+
});
|
|
370
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vscode-pi-companion",
|
|
3
|
+
"displayName": "PI Companion",
|
|
4
|
+
"description": "Share VS Code context with pi coding agent",
|
|
5
|
+
"repository": "https://github.com/ravshansbox/vscode-pi-companion",
|
|
6
|
+
"version": "0.0.10",
|
|
7
|
+
"publisher": "ravshansbox",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"pi-package"
|
|
10
|
+
],
|
|
11
|
+
"engines": {
|
|
12
|
+
"vscode": "^1.85.0"
|
|
13
|
+
},
|
|
14
|
+
"categories": [
|
|
15
|
+
"AI",
|
|
16
|
+
"Other"
|
|
17
|
+
],
|
|
18
|
+
"main": "./out/extension.js",
|
|
19
|
+
"scripts": {
|
|
20
|
+
"compile": "tsc -p ./",
|
|
21
|
+
"watch": "tsc -watch -p ./",
|
|
22
|
+
"package": "vsce package",
|
|
23
|
+
"vscode:prepublish": "npm run compile"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/cors": "^2.8.19",
|
|
27
|
+
"@types/express": "^5.0.6",
|
|
28
|
+
"@types/node": "^18.0.0",
|
|
29
|
+
"@types/vscode": "^1.85.0",
|
|
30
|
+
"@types/ws": "^8.18.1",
|
|
31
|
+
"@vscode/vsce": "^3.7.1",
|
|
32
|
+
"esbuild": "^0.27.4",
|
|
33
|
+
"typescript": "^5.0.0"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
37
|
+
"cors": "^2.8.5",
|
|
38
|
+
"express": "^4.18.2",
|
|
39
|
+
"ws": "^8.16.0",
|
|
40
|
+
"zod": "^3.22.4"
|
|
41
|
+
},
|
|
42
|
+
"pi": {
|
|
43
|
+
"extensions": [
|
|
44
|
+
"./extensions"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"activationEvents": [
|
|
48
|
+
"onStartupFinished"
|
|
49
|
+
],
|
|
50
|
+
"contributes": {
|
|
51
|
+
"configuration": {
|
|
52
|
+
"title": "PI Companion",
|
|
53
|
+
"properties": {
|
|
54
|
+
"piCompanion.port": {
|
|
55
|
+
"type": "number",
|
|
56
|
+
"default": 8765,
|
|
57
|
+
"description": "Port for MCP server"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"commands": [
|
|
62
|
+
{
|
|
63
|
+
"command": "pi-companion.run",
|
|
64
|
+
"title": "PI Companion: Run PI"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"command": "pi-companion.start",
|
|
68
|
+
"title": "PI Companion: Start Server"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"command": "pi-companion.stop",
|
|
72
|
+
"title": "PI Companion: Stop Server"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"command": "pi-companion.status",
|
|
76
|
+
"title": "PI Companion: Show Status"
|
|
77
|
+
}
|
|
78
|
+
],
|
|
79
|
+
"keybindings": [
|
|
80
|
+
{
|
|
81
|
+
"command": "pi-companion.run",
|
|
82
|
+
"key": "cmd+escape",
|
|
83
|
+
"mac": "cmd+escape",
|
|
84
|
+
"when": "editorTextFocus"
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
}
|
package/screenshot.png
ADDED
|
Binary file
|