瀏覽代碼

chore: initialize VS Code extension project scaffold

- Set up TypeScript + esbuild build pipeline
- Add three copy commands: Selection, Active File, Workspace Files
- Configure VS Code launch/tasks for development
- Add ESLint configuration
Developer 1 月之前
當前提交
cd3d968674
共有 11 個文件被更改,包括 392 次插入0 次删除
  1. 17 0
      .vscode/launch.json
  2. 15 0
      .vscode/tasks.json
  3. 10 0
      .vscodeignore
  4. 10 0
      CHANGELOG.md
  5. 39 0
      README.md
  6. 33 0
      esbuild.js
  7. 21 0
      eslint.config.mjs
  8. 89 0
      package.json
  9. 110 0
      src/copyContext.ts
  10. 30 0
      src/extension.ts
  11. 18 0
      tsconfig.json

+ 17 - 0
.vscode/launch.json

@@ -0,0 +1,17 @@
+{
+  "version": "0.2.0",
+  "configurations": [
+    {
+      "name": "Run Extension",
+      "type": "extensionHost",
+      "request": "launch",
+      "args": [
+        "--extensionDevelopmentPath=${workspaceFolder}"
+      ],
+      "outFiles": [
+        "${workspaceFolder}/dist/**/*.js"
+      ],
+      "preLaunchTask": "${defaultBuildTask}"
+    }
+  ]
+}

+ 15 - 0
.vscode/tasks.json

@@ -0,0 +1,15 @@
+{
+  "version": "2.0.0",
+  "tasks": [
+    {
+      "type": "npm",
+      "script": "watch",
+      "group": {
+        "kind": "build",
+        "isDefault": true
+      },
+      "isBackground": true,
+      "problemMatcher": "$esbuild-watch"
+    }
+  ]
+}

+ 10 - 0
.vscodeignore

@@ -0,0 +1,10 @@
+.vscode/**
+.vscode-test/**
+src/**
+.gitignore
+tsconfig.json
+esbuild.js
+eslint.config.mjs
+node_modules/**
+**/*.ts
+**/*.map

+ 10 - 0
CHANGELOG.md

@@ -0,0 +1,10 @@
+# Change Log
+
+## [0.0.1] - 2026-07-11
+
+### Added
+
+- Initial project scaffold
+- Command: Copy AI Context — Selection  (copy selected code with file path and line numbers)
+- Command: Copy AI Context — Active File (copy entire active file)
+- Command: Copy AI Context — Workspace Files (pick files from workspace to copy)

+ 39 - 0
README.md

@@ -0,0 +1,39 @@
+# Copy AI Context
+
+A VS Code extension to copy code context formatted for AI assistants (ChatGPT, Claude, etc.).
+
+## Features
+
+- **Copy Selection** (`Ctrl+Alt+C` / `Cmd+Alt+C`) — Copy selected code with file path and line numbers
+- **Copy Active File** (`Ctrl+Alt+F` / `Cmd+Alt+F`) — Copy the entire active file
+- **Copy Workspace Files** — Pick files from the workspace to copy as context
+
+## Usage
+
+Select code in the editor, then:
+
+- Right-click and choose **Copy AI Context: Selection**
+- Use the keyboard shortcut `Ctrl+Alt+C` (Windows/Linux) or `Cmd+Alt+C` (macOS)
+- Open the Command Palette (`Ctrl+Shift+P`) and search for **Copy AI Context**
+
+Output format:
+
+```
+--- path/to/file.ts:10-25 ---
+```ts
+// your code here
+```
+```
+
+## Requirements
+
+- VS Code 1.85+
+
+## Development
+
+```bash
+npm install
+npm run build
+```
+
+Press `F5` in VS Code to launch a development extension host.

+ 33 - 0
esbuild.js

@@ -0,0 +1,33 @@
+const esbuild = require('esbuild');
+
+const production = process.argv.includes('--minify');
+const watch = process.argv.includes('--watch');
+
+/** @type {esbuild.BuildOptions} */
+const config = {
+  entryPoints: ['src/extension.ts'],
+  bundle: true,
+  outfile: 'dist/extension.js',
+  external: ['vscode'],
+  format: 'cjs',
+  platform: 'node',
+  sourcemap: !production,
+  minify: production,
+  treeShaking: true,
+};
+
+async function main() {
+  if (watch) {
+    const ctx = await esbuild.context(config);
+    await ctx.watch();
+    console.log('[esbuild] watching for changes...');
+  } else {
+    await esbuild.build(config);
+    console.log('[esbuild] build complete');
+  }
+}
+
+main().catch((e) => {
+  console.error(e);
+  process.exit(1);
+});

+ 21 - 0
eslint.config.mjs

@@ -0,0 +1,21 @@
+import eslint from '@typescript-eslint/eslint-plugin';
+import tsParser from '@typescript-eslint/parser';
+
+export default [
+  {
+    files: ['src/**/*.ts'],
+    languageOptions: {
+      parser: tsParser,
+      ecmaVersion: 2021,
+      sourceType: 'module',
+    },
+    plugins: {
+      '@typescript-eslint': eslint,
+    },
+    rules: {
+      'no-unused-vars': 'off',
+      '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
+      '@typescript-eslint/no-explicit-any': 'warn',
+    },
+  },
+];

+ 89 - 0
package.json

@@ -0,0 +1,89 @@
+{
+  "name": "vsix-cp-ai-ctx",
+  "displayName": "Copy AI Context",
+  "description": "Copy code context for AI assistants — selected code, file contents, with paths and formatting",
+  "version": "0.0.1",
+  "publisher": "your-publisher",
+  "engines": {
+    "vscode": "^1.85.0"
+  },
+  "categories": [
+    "Other"
+  ],
+  "keywords": [
+    "ai",
+    "context",
+    "copy",
+    "clipboard",
+    "chatgpt",
+    "claude"
+  ],
+  "activationEvents": [],
+  "main": "./dist/extension.js",
+  "contributes": {
+    "commands": [
+      {
+        "command": "vsix-cp-ai-ctx.copySelection",
+        "title": "Copy AI Context: Selection"
+      },
+      {
+        "command": "vsix-cp-ai-ctx.copyFile",
+        "title": "Copy AI Context: Active File"
+      },
+      {
+        "command": "vsix-cp-ai-ctx.copyWorkspaceFiles",
+        "title": "Copy AI Context: Workspace Files"
+      }
+    ],
+    "keybindings": [
+      {
+        "command": "vsix-cp-ai-ctx.copySelection",
+        "key": "ctrl+alt+c",
+        "mac": "cmd+alt+c"
+      },
+      {
+        "command": "vsix-cp-ai-ctx.copyFile",
+        "key": "ctrl+alt+f",
+        "mac": "cmd+alt+f"
+      }
+    ],
+    "menus": {
+      "editor/context": [
+        {
+          "command": "vsix-cp-ai-ctx.copySelection",
+          "when": "editorHasSelection",
+          "group": "9_cutcopypaste@10"
+        }
+      ],
+      "explorer/context": [
+        {
+          "command": "vsix-cp-ai-ctx.copyFile",
+          "when": "!editorHasSelection",
+          "group": "9_cutcopypaste@10"
+        }
+      ]
+    }
+  },
+  "scripts": {
+    "vscode:prepublish": "node esbuild.js --minify",
+    "build": "node esbuild.js",
+    "watch": "node esbuild.js --watch",
+    "lint": "eslint src --ext .ts",
+    "package": "vsce package",
+    "publish": "vsce publish"
+  },
+  "devDependencies": {
+    "@types/vscode": "^1.85.0",
+    "@types/node": "^20.0.0",
+    "esbuild": "^0.20.0",
+    "typescript": "^5.3.0",
+    "eslint": "^8.56.0",
+    "@typescript-eslint/eslint-plugin": "^6.0.0",
+    "@typescript-eslint/parser": "^6.0.0",
+    "@vscode/vsce": "^2.22.0"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://git.ooo.ink/vsix/vsix-cp-ai-ctx.git"
+  }
+}

+ 110 - 0
src/copyContext.ts

@@ -0,0 +1,110 @@
+import * as vscode from 'vscode';
+
+/**
+ * Format a single file's content as AI context.
+ */
+function formatFileContent(filePath: string, content: string): string {
+  const ext = filePath.split('.').pop() ?? '';
+  return [
+    `--- ${filePath} ---`,
+    '```' + ext,
+    content,
+    '```',
+    '',
+  ].join('\n');
+}
+
+/**
+ * Copy the current editor selection to clipboard with AI-friendly formatting.
+ */
+export async function copySelection(): Promise<void> {
+  const editor = vscode.window.activeTextEditor;
+  if (!editor) {
+    vscode.window.showWarningMessage('No active editor');
+    return;
+  }
+
+  const selection = editor.selection;
+  if (selection.isEmpty) {
+    vscode.window.showWarningMessage('No text selected');
+    return;
+  }
+
+  const document = editor.document;
+  const filePath = vscode.workspace.asRelativePath(document.uri);
+  const text = document.getText(selection);
+  const lineStart = selection.start.line + 1;
+  const lineEnd = selection.end.line + 1;
+
+  const ext = filePath.split('.').pop() ?? '';
+  const header = lineStart === lineEnd
+    ? `${filePath}:${lineStart}`
+    : `${filePath}:${lineStart}-${lineEnd}`;
+
+  const result = [
+    `--- ${header} ---`,
+    '```' + ext,
+    text,
+    '```',
+    '',
+  ].join('\n');
+
+  await vscode.env.clipboard.writeText(result);
+  vscode.window.showInformationMessage(`Copied ${filePath}:${lineStart}-${lineEnd}`);
+}
+
+/**
+ * Copy the entire active file to clipboard with AI-friendly formatting.
+ */
+export async function copyActiveFile(): Promise<void> {
+  const editor = vscode.window.activeTextEditor;
+  if (!editor) {
+    vscode.window.showWarningMessage('No active editor');
+    return;
+  }
+
+  const document = editor.document;
+  const filePath = vscode.workspace.asRelativePath(document.uri);
+  const content = document.getText();
+  const result = formatFileContent(filePath, content);
+
+  await vscode.env.clipboard.writeText(result);
+  vscode.window.showInformationMessage(`Copied file: ${filePath}`);
+}
+
+/**
+ * Select files from the workspace and copy their contents.
+ */
+export async function copyWorkspaceFiles(): Promise<void> {
+  const workspaceFolders = vscode.workspace.workspaceFolders;
+  if (!workspaceFolders || workspaceFolders.length === 0) {
+    vscode.window.showWarningMessage('No workspace folder open');
+    return;
+  }
+
+  const uris = await vscode.window.showOpenDialog({
+    canSelectFiles: true,
+    canSelectFolders: false,
+    canSelectMany: true,
+    openLabel: 'Select files to copy as context',
+  });
+
+  if (!uris || uris.length === 0) return;
+
+  const parts: string[] = [];
+
+  for (const uri of uris) {
+    try {
+      const doc = await vscode.workspace.openTextDocument(uri);
+      const filePath = vscode.workspace.asRelativePath(uri);
+      parts.push(formatFileContent(filePath, doc.getText()));
+    } catch {
+      // skip binary files
+      vscode.window.showWarningMessage(`Skipped (binary/unreadable): ${uri.fsPath}`);
+    }
+  }
+
+  const result = parts.join('\n');
+  await vscode.env.clipboard.writeText(result);
+  vscode.window.showInformationMessage(`Copied ${uris.length} file(s) to clipboard`);
+}

+ 30 - 0
src/extension.ts

@@ -0,0 +1,30 @@
+import * as vscode from 'vscode';
+import { copySelection, copyActiveFile, copyWorkspaceFiles } from './copyContext';
+
+export function activate(context: vscode.ExtensionContext) {
+  console.log('[vsix-cp-ai-ctx] activating...');
+
+  context.subscriptions.push(
+    vscode.commands.registerCommand('vsix-cp-ai-ctx.copySelection', () => {
+      copySelection();
+    }),
+  );
+
+  context.subscriptions.push(
+    vscode.commands.registerCommand('vsix-cp-ai-ctx.copyFile', () => {
+      copyActiveFile();
+    }),
+  );
+
+  context.subscriptions.push(
+    vscode.commands.registerCommand('vsix-cp-ai-ctx.copyWorkspaceFiles', () => {
+      copyWorkspaceFiles();
+    }),
+  );
+
+  console.log('[vsix-cp-ai-ctx] activated');
+}
+
+export function deactivate() {
+  // cleanup if needed
+}

+ 18 - 0
tsconfig.json

@@ -0,0 +1,18 @@
+{
+  "compilerOptions": {
+    "target": "ES2021",
+    "module": "commonjs",
+    "lib": ["ES2021"],
+    "outDir": "dist",
+    "rootDir": "src",
+    "strict": true,
+    "esModuleInterop": true,
+    "skipLibCheck": true,
+    "forceConsistentCasingInFileNames": true,
+    "resolveJsonModule": true,
+    "declaration": true,
+    "sourceMap": true
+  },
+  "include": ["src"],
+  "exclude": ["node_modules", "dist", ".vscode-test"]
+}