You are not optimizing Copilot — you are controlling what you allow yourself to send into it.
Gatekeeper is a VS Code extension that acts as a prompt discipline layer in front of GitHub Copilot. It intercepts every @gatekeeper chat request, trims and structures the context before it leaves your machine, and routes low-complexity questions to a local model entirely — so Copilot only ever sees the minimum signal it needs.
User Action
│ (Ctrl+Shift+G · right-click "Gatekeeper: Run" · @gatekeeper chat · //gk: ghost text)
▼
┌──────────────────────────────────────────────┐
│ Smart Run (gatekeeper.run) │
│ extension.ts │
│ · Cursor on //gk: line → ghost-text trigger │
│ · Selection active → inline edit flow │
│ · Otherwise → contextual QuickPick │
└──────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Gatekeeper Command / Chat Participant │
│ extension.ts · chatParticipant.ts │
└──────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Context Extractor │
│ contextExtractor.ts │
│ · Active file text & language │
│ · Cursor selection / highlighted range │
│ · Scored related open files │
│ · Workspace diagnostics (errors/warnings) │
│ · Token usage estimates │
└──────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Context Trimmer │
│ contextTrimmer.ts │
│ · Extract enclosing function (AST-aware) │
│ · Strip block & line comments │
│ · Remove unreferenced import statements │
└──────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Prompt Optimizer │
│ promptOptimizer.ts │
│ · Detect intent (fix/refactor/test/…) │
│ · Scope breadth check — block if too broad │
│ · Select intent-specific system prompt │
│ · Compile structured Context/Task/Constraints│
│ · Inject diff-first instruction if enabled │
└──────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Ollama Pre-filter (optional, Feature E) │
│ modelRouter.ts │
│ · Ping local Ollama at 127.0.0.1:11434 │
│ · Classify complexity (low/medium/high) │
│ · Answer directly if low-complexity │
│ · Fall through to Copilot otherwise │
└──────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Copilot LM API │
│ vscode.lm.selectChatModels + sendRequest() │
│ · Receives the trimmed, structured prompt │
│ · Streams response back token-by-token │
└──────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Rollback Snapshot · Audit Log │
│ snapshotStore.ts · auditLog.ts │
│ · Save pre-edit file state to disk │
│ · Append JSONL audit entry (hash/intent/…) │
└──────────────────────────────────────────────┘
│
▼
Response streamed to chat / Unified diff patch applied to editor
Instead of sending the entire active file, Gatekeeper extracts only the function containing your cursor. It also strips block/line comments and removes import statements that are never referenced in the file body.
gatekeeper.focusCurrentFunction— extract only the enclosing function (default:true)gatekeeper.stripComments— remove comments before sending (default:true)gatekeeper.stripUnusedImports— drop unreferenced imports (default:true)
The optimization report shows exactly how many lines were trimmed and why.
Requests that are too broad (e.g. "refactor the entire codebase", "redesign the architecture") are detected before they reach Copilot. Broad requests show a warning but proceed. Architectural-scope requests are blocked with an explanation to narrow the scope.
Scope levels: targeted → moderate → broad → architectural
Raw user input is transformed into a structured format that eliminates ambiguity:
Context:
- file: auth.ts
- function: validateToken()
Task:
Fix the null-check so it handles undefined without throwing.
Constraints:
- State the root cause in one sentence
- Patch only the affected lines
- Do not introduce unrelated refactoring
Constraints are intent-specific — fix, explain, refactor, test, document, implement, and review each have a tailored constraint set.
When enabled, Gatekeeper appends a system-level instruction telling the model to respond with unified diffs instead of full files for fix, refactor, and review tasks. This eliminates wall-of-code responses.
gatekeeper.diffFirstMode— enable diff output (default:false)
Example response format:
--- a/src/auth.ts
+++ b/src/auth.ts
@@ -14,7 +14,7 @@
- if (token == null) throw new Error('missing');
+ if (token == null || token === undefined) throw new Error('missing token');Low-complexity questions that require no code context (general knowledge, syntax questions, concept explanations) are answered by a local Ollama model without ever reaching Copilot.
gatekeeper.useLocalModelPreFilter— enable local routing (default:false)gatekeeper.ollamaModel— Ollama model to use (default:"llama3")
The pre-filter performs a two-step check: first classifies complexity, then either answers locally or escalates. The report shows Pre-filter: ollama/llama3 when Ollama answered.
Open the GitHub Copilot Chat panel (Ctrl+Alt+I or View → Chat) and invoke @gatekeeper:
@gatekeeper explain what this function does
@gatekeeper fix the null-check on line 42
@gatekeeper refactor this to reduce nesting
@gatekeeper write unit tests for validateToken()
@gatekeeper review this for security issues
For best results: place your cursor inside the function you want to work with before sending the prompt. The context trimmer will extract that function as the primary context.
Every response is preceded by a report showing what was done:
> Gatekeeper Optimization Report
>
> | Metric | Value |
> |-------------------|--------------------|
> | Intent | `fix` |
> | Raw context | ~3,498 tokens |
> | Optimized prompt | ~890 tokens |
> | Reduction | ↓ 75% |
> | Output format | unified diff |
>
> Applied:
> - Applied "fix" system-prompt template
> - Focused on function (lines 42–67, was 312 lines)
> - Stripped comments (−31 lines)
> - Removed 4 unused imports
> - Diff-first mode enabled
> - Structured prompt compiled (Context / Task / Constraints)
Disable the report with gatekeeper.showOptimizationReport: false.
You no longer need to remember which command does what. One action auto-routes based on context:
| Editor state when triggered | What happens automatically |
|---|---|
Cursor is on a //gk: … line |
Fires the inline ghost-text suggestion |
| Text is selected | Opens the inline edit flow (prompt → patch → confirm → apply) |
| Nothing selected | Opens a contextual QuickPick with all available actions |
Trigger it via:
- Keyboard:
Ctrl+Shift+G/Cmd+Shift+G - Right-click context menu → Gatekeeper: Run
- Command Palette →
Gatekeeper: Run
| Action | Description |
|---|---|
$(edit) Optimize & Preview |
Extract context, score files, compile structured prompt — inspect before sending |
$(history) Rollback Last Edit |
Pick a pre-edit snapshot for the active file |
$(graph) Show Stats |
Token savings, intent breakdown, model usage |
$(output) View Audit Log |
JSONL audit of every inline edit attempt |
$(trash) Reset Stats |
Wipe accumulated stats |
| Command | Description |
|---|---|
@gatekeeper <prompt> |
Chat participant — optimized prompt via chat panel |
| Gatekeeper: Inline Edit Selection | Direct inline edit (requires selection) |
| Gatekeeper: Optimize & Preview | Direct preview (any open file) |
| Gatekeeper: Show Optimization Stats | WebView stats panel |
| Gatekeeper: Reset Stats | Clear saved statistics |
| Gatekeeper: Show Audit Log | Markdown table of all edits |
| Gatekeeper: Rollback Last Edit | Restore snapshot for active file |
//gk: <instruction> (ghost text) |
Type in any file, pause 600 ms → suggestion appears |
| Setting | Type | Default | Description |
|---|---|---|---|
gatekeeper.focusCurrentFunction |
boolean | true |
(A) Send only the function at the cursor |
gatekeeper.stripComments |
boolean | true |
(A) Strip comments from source context |
gatekeeper.stripUnusedImports |
boolean | true |
(A) Remove unreferenced imports |
gatekeeper.diffFirstMode |
boolean | false |
(D) Request unified diff output for fix/refactor/review |
gatekeeper.useLocalModelPreFilter |
boolean | false |
(E) Route simple requests to Ollama first |
gatekeeper.ollamaModel |
string | "llama3" |
(E) Ollama model name |
gatekeeper.enableInlineEdits |
boolean | true |
Enable the Inline Edit Selection command |
gatekeeper.inlinePreviewBeforeApply |
boolean | true |
Show diff preview and confirm before applying patches |
gatekeeper.enableAuditLog |
boolean | true |
Write audit entries to .gatekeeper/audit.jsonl |
gatekeeper.maxRollbackSnapshots |
number | 5 |
Max pre-edit snapshots kept per file |
gatekeeper.enableInlineCompletions |
boolean | false |
Enable //gk: ghost-text completions |
gatekeeper.compressStackTraces |
boolean | true |
Compress stack traces in diagnostics and chat messages to top 3 frames |
gatekeeper.maxContextLines |
number | 150 |
Max lines from the active file |
gatekeeper.maxRelatedFiles |
number | 3 |
Max number of related open files to include |
gatekeeper.relevanceThreshold |
number | 0.3 |
Minimum relevance score for related files (0–1) |
gatekeeper.preferredModelFamily |
string | "" |
Copilot model family (leave empty for any available) |
gatekeeper.usePromptTemplates |
boolean | true |
Use intent-specific system prompts |
gatekeeper.showOptimizationReport |
boolean | true |
Show the token-reduction report above responses |
- Open the project folder in VS Code.
- Press F5 — a new Extension Development Host window opens with Gatekeeper live-loaded.
- In the host window, open any source file and run through the scenarios below.
After any code change: press
Ctrl+Shift+F5(restart) orCtrl+Rin the host window — no packaging needed.
- Open any
.ts/.js/.pyfile. - Enable inline completions:
gatekeeper.enableInlineCompletions: truein Settings. - Type
// gk: sort this array by date descendingand pause ~600 ms. - Expected: ghost-text suggestion appears inline. Press
Tabto accept. - Alternative trigger: place cursor on that line and press
Ctrl+Shift+G. The ghost-text engine fires without waiting for the debounce.
- Open any source file and select 3–10 lines of code.
- Press
Ctrl+Shift+G. - Expected: an input box appears immediately asking what edit to apply — no QuickPick shown.
- Type
add null-check before first use→ confirm → a diff preview opens → click Apply. - Check the file is patched and
.gatekeeper/audit.jsonlhas a new entry.
- Open a file, ensure no text is selected, cursor is not on a
//gk:line. - Press
Ctrl+Shift+G. - Expected: the QuickPick appears with 5 actions (Optimize & Preview, Rollback, Stats, Audit Log, Reset Stats).
- Choose Optimize & Preview → enter a prompt → a Markdown preview opens in a side panel.
- Open the Copilot Chat panel and type:
@gatekeeper fix this error: TypeError: Cannot read property 'id' of null at UserService.find (user.service.ts:42:18) at AuthController.login (auth.controller.ts:88:12) at Layer.handle [as handle_request] (express/lib/router/layer.js:95:5) at next (express/lib/router/route.js:137:13) at Route.dispatch (express/lib/router/route.js:112:3) at Layer.handle [as handle_request] (express/lib/router/layer.js:95:5) - Expected: Gatekeeper reports
compressed an embedded stack trace (−3 lines)and sends only the error message + top 3 frames to the model. - Disable with
gatekeeper.compressStackTraces: falseand repeat — full trace passes through unchanged.
- Select code, apply an inline edit via
Ctrl+Shift+G(Route 2 above). - Press
Ctrl+Shift+Gagain with nothing selected → choose Rollback Last Edit. - Expected: a snapshot QuickPick appears; select the pre-edit entry → file reverts.
@gatekeeper explain what this function does
@gatekeeper fix the null-check on line 42
@gatekeeper write unit tests for validateToken()
Every response opens with a Gatekeeper Optimization Report table showing token reduction, intent, and applied rules.
- Start Ollama:
ollama serve(requires Ollama installed). - Set
gatekeeper.useLocalModelPreFilter: trueandgatekeeper.ollamaModel: "llama3"in Settings. - In chat:
@gatekeeper what is a closure in JavaScript? - Expected: the report shows
Pre-filter: ollama/llama3and no Copilot request is made.
- VS Code
^1.90.0 - GitHub Copilot and GitHub Copilot Chat extensions — signed in
- Node.js
^20(bundled with VS Code 1.90+) - Ollama (optional, for Feature E) — running at
http://127.0.0.1:11434
git clone <repo>
cd gatekeeper
npm install
npm run compile
npx @vscode/vsce package --no-dependencies --allow-missing-repository
code --install-extension gatekeeper-0.1.0.vsixOpen the project folder in VS Code and press F5. This launches an Extension Development Host with the extension loaded live — no packaging needed. Reload the host window after any source change.
src/
├── extension.ts # Activation, smart-run command, all wiring
├── chatParticipant.ts # @gatekeeper chat handler, request lifecycle
├── contextExtractor.ts # Active file, selection, related files, diagnostics
├── contextTrimmer.ts # (A) Comment stripping, import pruning, function extraction
├── promptOptimizer.ts # (B+C) Intent detection, scope check, structured prompt builder
├── modelRouter.ts # (E) Ollama availability check, local routing, pre-analysis
├── logCompressor.ts # Regex-based stack trace summariser (Node/Java/Python/.NET)
├── unifiedDiff.ts # Unified diff parser, hunk validator, patch applicator
├── inlineCompletionProvider.ts # Ghost-text provider triggered by //gk: comments
├── snapshotStore.ts # Pre-edit rollback snapshots (ring-buffer, disk-persisted)
├── auditLog.ts # JSONL audit log per inline edit (hash · intent · outcome)
├── statsTracker.ts # Persistent token-savings statistics, WebView report
└── types.ts # Shared TypeScript interfaces
<workspace>/
└── .gatekeeper/
├── audit.jsonl # One JSON line per inline edit event
├── snapshots/ # Pre-edit file snapshots (auto-pruned)
└── .gitignore # Auto-created — excludes audit + snapshots from git
Most AI cost-control tools try to optimize what happens inside the model. Gatekeeper controls what you allow into the model in the first place.
Three concrete enforcement points:
- ContextTrimmer — the active file shrinks from N lines to the enclosing function. You never send what the model doesn't need.
- PromptCompiler — structured format eliminates ambiguity; the model doesn't waste tokens hedging or re-interpreting scope.
- ModelRouter — questions requiring no code context never reach Copilot at all.
The scope enforcer is the most direct expression of this: it blocks a request before it leaves your machine, not after tokens have been consumed.