[tab] crowd-pilot-extension/out/extension.js (javascript)
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.activate = activate;
exports.deactivate = deactivate;
const vscode = __importStar(require("vscode"));
const http = __importStar(require("http"));
const buffer_1 = require("buffer");
const HOSTNAME = 'hai005';
const PORT = 30000;
function activate(context) {
    console.log('[crowd-pilot] Extension activated');
    // Configure terminal to allow tab keybinding to work
    (async () => {
        const config = vscode.workspace.getConfiguration('terminal.integrated');
        const commandsToSkipShell = config.get('commandsToSkipShell', []);
        let updated = false;
        if (!commandsToSkipShell.includes('crowd-pilot.modelRun')) {
            commandsToSkipShell.push('crowd-pilot.modelRun');
            updated = true;
        }
        if (!commandsToSkipShell.includes('crowd-pilot.hideUi')) {
            commandsToSkipShell.push('crowd-pilot.hideUi');
            updated = true;
        }
        if (updated) {
            await config.update('commandsToSkipShell', commandsToSkipShell, vscode.ConfigurationTarget.Global);
        }
    })().catch((err) => console.error('[crowd-pilot] Startup initialization error:', err));
    const hideUi = vscode.commands.registerCommand('crowd-pilot.hideUi', () => {
        hidePreviewUI();
    });
    const modelRun = vscode.commands.registerCommand('crowd-pilot.modelRun', async () => {
        const editor = vscode.window.activeTextEditor;
        if (!editor) {
            return;
        }
        try {
            const plan = await requestModelActions(editor);
            if (!previewVisible) {
                showPreviewUI(plan);
                return;
            }
            const runPlan = currentPlan ?? plan;
            hidePreviewUI();
            await executePlan(runPlan);
            vscode.window.showInformationMessage('All actions emitted');
        }
        catch (err) {
            const errorMessage = err instanceof Error ? err.message : String(err);
            vscode.window.showErrorMessage(`Model run failed: ${errorMessage}`);
        }
    });
    const sglangTest = vscode.commands.registerCommand('crowd-pilot.sglangTest', async () => {
        try {
            await callSGLangChat();
        }
        catch (err) {
            const errorMessage = err instanceof Error ? err.message : String(err);
            vscode.window.showErrorMessage(`SGLang test failed: ${errorMessage}`);
        }
    });
    context.subscriptions.push(hideUi, sglangTest, modelRun);
}
function deactivate() { }
let currentPlan;
function buildTestRunPlan(_editor, _doc, _term) {
    const plan = [];
    plan.push({ kind: 'showTextDocument' });
    plan.push({ kind: 'setSelections', selections: [{ start: [0, 0], end: [0, 0] }] });
    plan.push({ kind: 'editInsert', position: [0, 0], text: 'hello world
' });
    plan.push({ kind: 'terminalShow' });
    plan.push({ kind: 'terminalSendText', text: 'echo VSCode test' });
    return plan;
}
async function executePlan(plan) {
    const editor = vscode.window.activeTextEditor;
    if (!editor) {
        return;
    }
    const doc = editor.document;
    const term = vscode.window.terminals[0] ?? vscode.window.createTerminal('Test');
    for (const action of plan) {
        if (action.kind === 'showTextDocument') {
            await vscode.window.showTextDocument(doc);
            continue;
        }
        if (action.kind === 'setSelections') {
            editor.selections = action.selections.map(s => new vscode.Selection(new vscode.Position(s.start[0], s.start[1]), new vscode.Position(s.end[0], s.end[1])));
            continue;
        }
        if (action.kind === 'editInsert') {
            await editor.edit((e) => e.insert(new vscode.Position(action.position[0], action.position[1]), action.text));
            continue;
        }
        if (action.kind === 'terminalShow') {
            term.show();
            continue;
        }
        if (action.kind === 'terminalSendText') {
            term.sendText(action.text);
            continue;
        }
    }
}
// -------------------- UI State & Helpers --------------------
const UI_CONTEXT_KEY = 'crowdPilot.uiVisible';
let previewVisible = false;
let previewQuickPick;
function showPreviewUI(plan) {
    const items = plan.map((action, index) => {
        switch (action.kind) {
            case 'showTextDocument':
                return { index, label: '$(file) Focus active text document' };
            case 'setSelections':
                {
                    const cursors = action.selections.map(s => `(${s.start[0]}, ${s.start[1]})`).join(', ');
                    return { index, label: `$(cursor) Move cursor to ${cursors}` };
                }
            case 'editInsert':
                return { index, label: `$(pencil) Insert "${action.text.replace(/
/g, '\
')}" at (${action.position[0]}, ${action.position[1]})` };
            case 'terminalShow':
                return { index, label: '$(terminal) Focus terminal' };
            case 'terminalSendText':
                return { index, label: `$(terminal) Run "${action.text}" in terminal` };
        }
    });
    if (!previewQuickPick) {
        previewQuickPick = vscode.window.createQuickPick();
        previewQuickPick.title = 'crowd-pilot: preview';
        previewQuickPick.matchOnDetail = true;
        previewQuickPick.ignoreFocusOut = true;
        previewQuickPick.canSelectMany = false;
        previewQuickPick.onDidAccept(async () => {
            const qp = previewQuickPick;
            const selected = qp.selectedItems?.[0];
            qp.hide();
            if (selected) {
                await executePlan([plan[selected.index]]);
                vscode.window.showInformationMessage('Action executed');
            }
        });
        previewQuickPick.onDidHide(() => {
            previewVisible = false;
            vscode.commands.executeCommand('setContext', UI_CONTEXT_KEY, false);
            try {
                previewQuickPick?.dispose();
            }
            catch { }
            previewQuickPick = undefined;
        });
    }
    previewQuickPick.items = items;
    previewQuickPick.placeholder = 'Press Tab to run all, Enter for selected, or Esc to hide';
    previewQuickPick.show();
    previewVisible = true;
    vscode.commands.executeCommand('setContext', UI_CONTEXT_KEY, true);
    currentPlan = plan;
}
function hidePreviewUI() {
    if (previewQuickPick) {
        try {
            previewQuickPick.hide();
        }
        catch { }
        return;
    }
    previewVisible = false;
    vscode.commands.executeCommand('setContext', UI_CONTEXT_KEY, false);
}
// -------------------- SGLang Client (simple test) --------------------
async function callSGLangChat() {
    const requestBody = {
        model: 'qwen/qwen2.5-0.5b-instruct',
        messages: [
            { role: 'user', content: 'What is the capital of France?' }
        ]
    };
    const postData = JSON.stringify(requestBody);
    const options = {
        hostname: HOSTNAME,
        port: PORT,
        path: '/v1/chat/completions',
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Content-Length': buffer_1.Buffer.byteLength(postData)
        }
    };
    try {
        const json = await new Promise((resolve, reject) => {
            const req = http.request(options, (res) => {
                let data = '';
                res.on('data', (chunk) => {
                    data += chunk.toString();
                });
                res.on('end', () => {
                    try {
                        resolve(JSON.parse(data));
                    }
                    catch (err) {
                        reject(new Error(`Failed to parse response: ${err instanceof Error ? err.message : String(err)}`));
                    }
                });
            });
            req.on('error', (err) => {
                reject(err);
            });
            req.write(postData);
            req.end();
        });
        vscode.window.showInformationMessage(`SGLang response: ${JSON.stringify(json, null, 2)}`);
    }
    catch (err) {
        const errorMessage = err instanceof Error ? err.message : String(err);
        vscode.window.showErrorMessage(`SGLang request failed: ${errorMessage}`);
    }
}
// -------------------- Model-planned Actions --------------------
async function requestModelActions(editor) {
    const schemaDescription = [
        'Role: You suggest the next VS Code editor/terminal action to progress the current task.',
        'Output ONLY a JSON array (no prose, no code fences). Length exactly 1.',
        'Coordinates are zero-based [line, column].',
        'Allowed actions (JSON schema-like):',
        '{ kind: "showTextDocument" }',
        '{ kind: "setSelections", selections: Array<{ start: [number, number], end: [number, number] }> }',
        '{ kind: "editInsert", position: [number, number], text: string }',
        '{ kind: "terminalShow" }',
        '{ kind: "terminalSendText", text: string }',
        'Guidelines:',
        '- Prefer small and safe steps.',
        '- If uncertain, set a helpful cursor/selection.',
        '- Use double-quoted JSON strings.'
    ].join('
');
    const doc = editor.document;
    const cursor = editor.selection.active;
    const contextRange = new vscode.Range(new vscode.Position(0, 0), cursor);
    const contextCode = doc.getText(contextRange);
    const maxContextChars = 20000;
    const allLines = contextCode.split(/
?
/);
    let startLineIndex = 0;
    let visibleLines = allLines;
    if (contextCode.length > maxContextChars) {
        let acc = 0;
        let idx = allLines.length;
        while (idx > 0 && acc <= maxContextChars) {
            idx--;
            acc += allLines[idx].length + 1;
        }
        startLineIndex = idx;
        visibleLines = allLines.slice(idx);
    }
    const numberedContext = visibleLines.map((line, i) => `${startLineIndex + i}: ${line}`).join('
');
    const tabbingPrompt = [
        'Your role: Propose the single next action according to the schema to help the developer progress.',
        '',
        'Available context:',
        `- File: ${doc.fileName}`,
        `- Language: ${doc.languageId}`,
        `- Cursor: (${cursor.line}, ${cursor.character})`,
        '',
        'Current file content up to the cursor (zero-based line numbers):',
        '```',
        numberedContext,
        '```',
        '',
        'Respond with ONLY a JSON array containing exactly one action.'
    ].join('
');
    const requestBody = {
        model: 'qwen/qwen2.5-0.5b-instruct',
        messages: [
            { role: 'system', content: schemaDescription },
            { role: 'user', content: tabbingPrompt }
        ]
    };
    const postData = JSON.stringify(requestBody);
    const options = {
        hostname: HOSTNAME,
        port: PORT,
        path: '/v1/chat/completions',
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Content-Length': buffer_1.Buffer.byteLength(postData)
        }
    };
    const json = await new Promise((resolve, reject) => {
        const req = http.request(options, (res) => {
            let data = '';
            res.on('data', (chunk) => { data += chunk.toString(); });
            res.on('end', () => {
                try {
                    resolve(JSON.parse(data));
                }
                catch (err) {
                    reject(new Error(`Failed to parse response: ${err instanceof Error ? err.message : String(err)}`));
                }
            });
        });
        req.on('error', (err) => reject(err));
        req.write(postData);
        req.end();
    });
    const content = extractChatContent(json);
    if (typeof content !== 'string' || content.trim().length === 0) {
        throw new Error('Empty model content');
    }
    const actions = parsePlannedActions(content);
    if (actions.length === 0) {
        throw new Error('No valid actions parsed from model output');
    }
    return actions;
}
function extractChatContent(json) {
    try {
        if (json && Array.isArray(json.choices) && json.choices[0]) {
            const choice = json.choices[0];
            if (choice.message && typeof choice.message.content === 'string') {
                return choice.message.content;
            }
            if (typeof choice.text === 'string') {
                return choice.text;
            }
        }
        return undefined;
    }
    catch {
        return undefined;
    }
}
function parsePlannedActions(raw) {
    let text = raw.trim();
    text = text.replace(/^```(?:json)?\s*/i, '').replace(/```\s*$/i, '').trim();
    const arrayMatch = text.match(/\[[\s\S]*\]/);
    const jsonText = arrayMatch ? arrayMatch[0] : text;
    let parsed;
    try {
        parsed = JSON.parse(jsonText);
    }
    catch (err) {
        return [];
    }
    if (!Array.isArray(parsed)) {
        return [];
    }
    const result = [];
    for (const item of parsed) {
        if (!item || typeof item !== 'object' || typeof item.kind !== 'string') {
            continue;
        }
        switch (item.kind) {
            case 'showTextDocument':
                result.push({ kind: 'showTextDocument' });
                break;
            case 'setSelections': {
                const selections = Array.isArray(item.selections) ? item.selections : [];
                const norm = selections.map((s) => ({
                    start: Array.isArray(s?.start) && s.start.length === 2 ? [Number(s.start[0]) || 0, Number(s.start[1]) || 0] : [0, 0],
                    end: Array.isArray(s?.end) && s.end.length === 2 ? [Number(s.end[0]) || 0, Number(s.end[1]) || 0] : [0, 0]
                }));
                result.push({ kind: 'setSelections', selections: norm });
                break;
            }
            case 'editInsert': {
                const pos = Array.isArray(item.position) && item.position.length === 2 ? [Number(item.position[0]) || 0, Number(item.position[1]) || 0] : [0, 0];
                const text = typeof item.text === 'string' ? item.text : '';
                result.push({ kind: 'editInsert', position: pos, text });
                break;
            }
            case 'terminalShow':
                result.push({ kind: 'terminalShow' });
                break;
            case 'terminalSendText': {
                const text = typeof item.text === 'string' ? item.text : '';
                result.push({ kind: 'terminalSendText', text });
                break;
            }
            default:
                break;
        }
    }
    return result;
}
//# sourceMappingURL=extension.js.map