Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
27954e0
fix(oauth): handle windowId in callback path, add app_name, move stat…
MarcelRoozekrans Mar 23, 2026
b5cce08
fix(oauth): remove PKCE (OpenRouter unsupported), consolidate auth co…
MarcelRoozekrans Mar 23, 2026
138fc45
chore: ignore playwright-mcp artifacts and regression reports
MarcelRoozekrans Mar 23, 2026
0eae410
docs: add credits awareness design
MarcelRoozekrans Mar 23, 2026
6be04dc
docs: add credits awareness implementation plan
MarcelRoozekrans Mar 23, 2026
8897910
feat(credits): add usage types and usageUpdate message
MarcelRoozekrans Mar 23, 2026
1d09ffd
feat(credits): track session cost and fetch account balance
MarcelRoozekrans Mar 23, 2026
ca65f5c
feat(credits): consolidate status bar, show session cost, credits menu
MarcelRoozekrans Mar 23, 2026
e957ac0
fix(credits): correct balance display and type postMessage wrapper sa…
MarcelRoozekrans Mar 23, 2026
0e89e63
feat(credits): handle usageUpdate and noCredits in webview store
MarcelRoozekrans Mar 23, 2026
c9968af
fix(credits): correct usageUpdate property access in App.tsx
MarcelRoozekrans Mar 23, 2026
e3d87f9
feat(credits): per-message cost tooltip, no-credits banner, openExter…
MarcelRoozekrans Mar 23, 2026
476c3d7
fix: handle 401 by invalidating auth and prompting re-sign-in
MarcelRoozekrans Mar 23, 2026
d6c04c0
feat: structured OpenRouter error handling for all API error codes
MarcelRoozekrans Mar 23, 2026
9abcad0
test: fix stale skill-registry tests, add OpenRouterError/mid-stream/…
MarcelRoozekrans Mar 23, 2026
89835be
fix: raise tool loop limit to 15, force text response on final iteration
MarcelRoozekrans Mar 23, 2026
175b803
feat: show per-1M-token pricing in model selector dropdown
MarcelRoozekrans Mar 23, 2026
12af90b
docs: add missing tools + scoped approval design
MarcelRoozekrans Mar 23, 2026
399172f
docs: add missing tools implementation plan
MarcelRoozekrans Mar 23, 2026
43d1512
feat(tools): add ToolApprovalManager for scoped tool approvals
MarcelRoozekrans Mar 23, 2026
4a5dc4b
feat(tools): extend toolApprovalResponse with scope field
MarcelRoozekrans Mar 23, 2026
871abcd
feat(tools): wire ToolApprovalManager into MessageHandler
MarcelRoozekrans Mar 23, 2026
517c6ad
feat(tools): add write_file, delete_file, run_terminal_command, list_…
MarcelRoozekrans Mar 23, 2026
a066a24
fix(tools): address code quality issues in editor-tools
MarcelRoozekrans Mar 23, 2026
be23d03
feat(tools): add 4-button scoped approval UI to ToolCallCard
MarcelRoozekrans Mar 23, 2026
fca452d
fix(tools): fix approval button class names, centralize ApprovalScope…
MarcelRoozekrans Mar 23, 2026
23e0850
feat(tools): style scoped approval buttons in ToolCallCard
MarcelRoozekrans Mar 23, 2026
16a22d1
fix(tools): autonomous mode bypass, session approval, useTrash on delete
MarcelRoozekrans Mar 23, 2026
8ca3d06
docs: add LUCENT.md, built-in skills & model switching design
MarcelRoozekrans Mar 23, 2026
ee29d23
docs: add LUCENT.md, skills & model switching implementation plan
MarcelRoozekrans Mar 23, 2026
ab50956
feat(skills): update InstructionsLoader — LUCENT.md filenames + @skil…
MarcelRoozekrans Mar 23, 2026
2a99ed2
test(skills): remove stale openrouter-instructions test descriptions
MarcelRoozekrans Mar 23, 2026
299c7f1
fix(skills): single-pass @skill() extraction with anchored regex
MarcelRoozekrans Mar 23, 2026
af1a43e
feat(skills): add 6 built-in language-agnostic skills
MarcelRoozekrans Mar 23, 2026
2e9807c
fix(skills): embed built-in skills as module imports to survive packa…
MarcelRoozekrans Mar 23, 2026
762cfd2
feat(skills): add ClaudeCodeSource adapter — auto-loads ~/.claude/skills
MarcelRoozekrans Mar 23, 2026
ee45e3d
feat(skills): pull-only loading — remove SkillMatcher, surface activa…
MarcelRoozekrans Mar 23, 2026
e3670a0
fix(skills): language-neutral truncation marker, delete dead SkillMat…
MarcelRoozekrans Mar 23, 2026
2e69f08
feat(tools): add use_model tool with scoped approval and model switch
MarcelRoozekrans Mar 23, 2026
589a6cb
fix(tools): thread currentModel through webview for use_model approva…
MarcelRoozekrans Mar 23, 2026
a0e407b
fix(tools): validate model_id, improve use_model robustness and card …
MarcelRoozekrans Mar 23, 2026
26375fe
feat(chat): add @model mention for user-initiated model switching
MarcelRoozekrans Mar 23, 2026
0b45930
fix(chat): reset model picker state on Escape, add empty-state row
MarcelRoozekrans Mar 23, 2026
e3fd7f5
feat(chat): style use_model approval card and @model picker empty state
MarcelRoozekrans Mar 23, 2026
e772a27
docs: add LUCENT.md, built-in skills, use_model, @model to feature in…
MarcelRoozekrans Mar 23, 2026
5aa1186
fix(tools): break modelChanged→setModel echo loop — use receiveModelC…
MarcelRoozekrans Mar 23, 2026
d7bbad4
docs: design doc for slash commands, /compact, and @file mention
MarcelRoozekrans Mar 24, 2026
ebb16d2
docs: implementation plan for slash commands and @file mention
MarcelRoozekrans Mar 24, 2026
813f207
feat(skills): add doc, tests, commit, onboard built-in skills
MarcelRoozekrans Mar 24, 2026
7acdcf5
feat(chat): drop @test action mention — replaced by /tests skill
MarcelRoozekrans Mar 24, 2026
0fbc678
fix(chat): restore indent on @terminal entry in MENTION_SOURCES
MarcelRoozekrans Mar 24, 2026
0a434f3
feat(chat): add /compact system command — summarize and truncate conv…
MarcelRoozekrans Mar 24, 2026
8430a98
fix(chat): remove unnecessary as any casts in compact divider
MarcelRoozekrans Mar 24, 2026
6305997
fix(chat): resolve TypeScript errors and improve compact robustness
MarcelRoozekrans Mar 24, 2026
446e317
feat(chat): add @file typed mention for keyboard-driven file context …
MarcelRoozekrans Mar 24, 2026
42029d7
fix(chat): binary file rejection, error feedback, and case-insensitiv…
MarcelRoozekrans Mar 24, 2026
52c3a83
fix(chat): fix binary probe, test nesting, path.relative, and attachm…
MarcelRoozekrans Mar 24, 2026
83dd4cb
feat(chat): style compaction divider and update feature inventory
MarcelRoozekrans Mar 24, 2026
5604b9b
fix(chat): remove dead @test handler, add binary/oversized file tests
MarcelRoozekrans Mar 24, 2026
c1993ce
docs: add Lucent Code documentation site design
MarcelRoozekrans Mar 24, 2026
3031593
docs: add Lucent Code docs site implementation plan
MarcelRoozekrans Mar 24, 2026
5c311fc
feat(docs): scaffold Docusaurus 3.7 in docs-site/
MarcelRoozekrans Mar 24, 2026
a123b91
chore(docs): widen typescript range, document intentional dark prism …
MarcelRoozekrans Mar 24, 2026
ca2a2a8
feat(docs): apply Lucent Code brand styling and home page
MarcelRoozekrans Mar 24, 2026
86bddd5
docs: add all 7 user guide pages
MarcelRoozekrans Mar 24, 2026
872a9c7
docs: add all 6 developer reference pages
MarcelRoozekrans Mar 24, 2026
b7f64db
docs: add Cloudflare deployment notes and verify final build
MarcelRoozekrans Mar 24, 2026
4d30b39
fix(skills): use source.type as fallback label in load(), update getS…
MarcelRoozekrans Mar 24, 2026
49469af
chore(skills): delete orphaned tdd.md skill file
MarcelRoozekrans Mar 25, 2026
4ea4b70
feat(marketing): add Docs link to navbar and footer, fix GitHub URL, …
MarcelRoozekrans Mar 25, 2026
81a72da
feat(webview): collapsible skill groups with count badge and indented…
MarcelRoozekrans Mar 25, 2026
14a6100
fix(test): update AdvancedFeaturesGrid test to match current card title
MarcelRoozekrans Mar 25, 2026
2254c86
fix(core): fix notifications command name and context-builder test mocks
MarcelRoozekrans Mar 25, 2026
3aeebef
feat(skills): add doc, tests, commit, onboard, compact built-in skill…
MarcelRoozekrans Mar 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ webview/node_modules/
webview/dist/
.lucent/
package-lock.json
.playwright-mcp/
docs/regression-screenshots/
docs/regression-report-*.md
1 change: 1 addition & 0 deletions .playwright-mcp/console-2026-03-20T17-22-00-332Z.log
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[ 548ms] [ERROR] Failed to load resource: the server responded with a status of 404 (Not Found) @ http://localhost:5173/favicon.ico:0
1 change: 1 addition & 0 deletions .playwright-mcp/console-2026-03-20T18-43-51-205Z.log
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[ 332ms] [ERROR] Failed to load resource: the server responded with a status of 404 (Not Found) @ http://localhost:5173/favicon.ico:0
1 change: 1 addition & 0 deletions .playwright-mcp/console-2026-03-21T07-20-13-106Z.log
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[ 814ms] [ERROR] Failed to load resource: the server responded with a status of 404 (Not Found) @ http://localhost:5173/favicon.ico:0
12 changes: 12 additions & 0 deletions .playwright-mcp/console-2026-03-21T08-11-48-416Z.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[ 13898ms] TypeError: Cannot read properties of undefined (reading 'trim')
at Object.sendMessage (http://localhost:5177/src/stores/chat.ts:33:18)
at http://localhost:5177/src/App.tsx:62:21
at dispatch (eval at evaluate (:301:30), <anonymous>:2:37)
at eval (eval at evaluate (:301:30), <anonymous>:6:3)
at UtilityScript.evaluate (<anonymous>:303:16)
at UtilityScript.<anonymous> (<anonymous>:1:44)
[ 14604ms] TypeError: Cannot read properties of undefined (reading 'trim')
at Object.sendMessage (http://localhost:5177/src/stores/chat.ts:33:18)
at http://localhost:5177/src/App.tsx:62:21
at dispatch (eval at evaluate (:301:30), <anonymous>:2:37)
at eval (eval at evaluate (:301:30), <anonymous>:13:7)
Binary file added .playwright-mcp/page-2026-03-20T13-39-34-819Z.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .playwright-mcp/page-2026-03-20T13-39-46-309Z.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .playwright-mcp/page-2026-03-20T13-39-54-577Z.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .playwright-mcp/page-2026-03-21T08-11-20-185Z.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .playwright-mcp/page-2026-03-21T08-12-54-313Z.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .playwright-mcp/page-2026-03-21T08-13-56-896Z.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .playwright-mcp/page-2026-03-21T09-27-04-951Z.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .playwright-mcp/page-2026-03-21T09-27-36-866Z.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .playwright-mcp/page-2026-03-21T09-31-17-686Z.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions .vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ webview/src/**
webview/node_modules/**
node_modules/**
*.map
**/*.map
.gitignore
tsconfig.json
esbuild.config.mjs
Expand All @@ -12,3 +13,6 @@ scripts/**
marketing/**
docs/**
.github/**
.playwright-mcp/**
.lucent/**
lucent-code-*.vsix
3 changes: 3 additions & 0 deletions docs-site/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
build/
.docusaurus/
22 changes: 22 additions & 0 deletions docs-site/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Lucent Code Docs

Docusaurus 3.x documentation site for [Lucent Code](https://lucentcode.dev).

## Local development

```bash
npm install
npm start # dev server at http://localhost:3000
npm run build # production build → build/
```

## Cloudflare Pages deployment

| Setting | Value |
|---|---|
| Root directory | `docs-site` |
| Build command | `npm run build` |
| Build output directory | `build` |
| Node.js version | `20` |

Set the `NODE_VERSION` environment variable to `20` in the Cloudflare Pages project settings.
98 changes: 98 additions & 0 deletions docs-site/docs/developer/adding-skills.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
sidebar_position: 2
title: Adding Skills
description: How to write a skill file, the frontmatter spec, and how skills are loaded from different sources.
---

# Adding Skills

A **skill** is a Markdown file with a YAML frontmatter header. The body is the prompt that gets sent to the AI when the skill is invoked.

## Skill File Format

```markdown
---
name: my-skill
description: One sentence describing what this skill does
---

Your prompt here. Write clear, structured instructions for the AI.

Include examples, output formats, and any constraints you want enforced.
```

**Frontmatter fields:**

| Field | Required | Description |
|---|---|---|
| `name` | Yes | Slug used in the `/` picker and for `use_skill` tool calls |
| `description` | Yes | Shown in the skill picker and system prompt — keep it to one sentence |

The `name` and `description` values may optionally be quoted (single or double quotes — both are stripped).

## Built-in Skills

Built-in skills live in `src/skills/builtin/` as individual `.md` files. They're bundled at build time by esbuild's text loader.

To add a new built-in skill:

1. Create `src/skills/builtin/your-skill.md` with frontmatter
2. Import it in `src/skills/builtin/index.ts`:
```typescript
import yourSkill from './your-skill.md';
export const BUILTIN_SKILLS: readonly string[] = [
// ... existing skills ...
yourSkill,
];
```
3. Build: `npm run build`

## User Skills (`~/.claude/skills/`)

Create a directory per skill:

```
~/.claude/skills/
my-skill/
SKILL.md ← skill content (frontmatter + body)
another-skill/
SKILL.md
```

Lucent Code reads each subdirectory's `SKILL.md` on startup.

## Skill Sources

Skills are loaded from multiple sources, appearing in the picker grouped by source label:

| Label | Source |
|---|---|
| `builtin` | Bundled with the extension |
| `claude` | `~/.claude/skills/` and `~/.claude/plugins/cache/` |
| `github` | Fetched from a GitHub repository |
| `npm` | Fetched from an npm package via unpkg.com |
| `marketplace` | Fetched from the Superpowers registry |
| `local` | Loaded from a local directory path |

## SkillRegistry API

`src/skills/skill-registry.ts` — central registry:

```typescript
class SkillRegistry {
load(content: string, source?: string): void // parse and index one skill
clear(): void // reset all loaded skills
get(name: string): Skill | undefined
getSummaries(): SkillSummary[] // name + description + source
getContent(name: string): string | undefined // full body
}
```

`getSummaries()` is called when building the system prompt (lightweight — no body content). `getContent()` is called when the AI invokes `use_skill`.

## Writing Good Skills

- **Be specific about output format.** The AI will follow explicit format instructions. Vague instructions produce inconsistent output.
- **Show don't tell.** Include a concrete example of the expected output in the skill body.
- **Keep descriptions scannable.** The description appears in the AI's system prompt — one sentence is enough.
- **YAGNI.** Don't add instructions for edge cases that won't come up. Longer prompts don't mean better results.
103 changes: 103 additions & 0 deletions docs-site/docs/developer/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
---
sidebar_position: 1
title: Architecture
description: How the extension is structured — the VS Code host process, the SolidJS webview, and the postMessage protocol between them.
---

# Architecture

Lucent Code is a VS Code extension split into two processes that communicate via VS Code's webview postMessage API.

## High-Level Structure

```
┌─────────────────────────────────────────────────────────────┐
│ VS Code Extension Host (Node.js) │
│ │
│ src/extension.ts ← activation entry point │
│ src/chat/chat-handler.ts ← OpenRouter API calls │
│ src/skills/ ← skill registry & sources │
│ src/tools/ ← editor tool implementations │
│ src/mcp/ ← MCP client manager │
│ src/context/ ← LSP context gatherer │
│ src/search/ ← codebase indexer │
└──────────────────┬──────────────────────────────────────────┘
│ postMessage (WebviewMessage / ExtensionMessage)
┌──────────────────▼──────────────────────────────────────────┐
│ Webview (SolidJS, bundled by esbuild) │
│ │
│ webview/src/App.tsx ← root component │
│ webview/src/components/ ← UI components │
│ webview/src/services/ ← vscode API bridge │
└─────────────────────────────────────────────────────────────┘
```

## Message Protocol

All communication uses typed messages defined in `src/shared/types.ts`.

**Webview → Extension** (`WebviewMessage`):
```typescript
type WebviewMessage =
| { type: 'sendMessage'; content: string; images?: string[]; model: string }
| { type: 'getModels' }
| { type: 'setModel'; modelId: string }
| { type: 'toolApprovalResponse'; requestId: string; approved: boolean; scope?: ApprovalScope }
| { type: 'listFiles'; query: string }
// ... more
```

**Extension → Webview** (`ExtensionMessage`):
```typescript
type ExtensionMessage =
| { type: 'streamChunk'; content: string }
| { type: 'streamEnd'; usage?: Usage }
| { type: 'modelsLoaded'; models: OpenRouterModel[] }
| { type: 'toolApprovalRequest'; requestId: string; toolName: string; args: Record<string, unknown>; diff?: DiffLine[] }
| { type: 'usageUpdate'; lastMessageCost: number; sessionCost: number; creditsUsed: number }
// ... more
```

The webview sends messages via `vscode.postMessage()`. The extension sends messages via `panel.webview.postMessage()`.

## Key Source Files

| File | Purpose |
|---|---|
| `src/extension.ts` | Activation, panel creation, message routing |
| `src/chat/chat-handler.ts` | Builds prompts, calls OpenRouter API, streams responses, routes tool calls |
| `src/skills/skill-registry.ts` | Loads and indexes skills from all sources |
| `src/skills/sources/` | Skill source implementations (builtin, claude-code, github, npm, marketplace, local) |
| `src/tools/editor-tools.ts` | Implements editor tool calls (write_file, rename_symbol, etc.) |
| `src/mcp/mcp-client-manager.ts` | Spawns and manages MCP server subprocesses |
| `src/context/context-gatherer.ts` | Collects LSP context (file, selection, diagnostics) |
| `src/search/indexer.ts` | Vector indexing for @codebase search |
| `src/shared/types.ts` | **All shared types** — read this first |
| `webview/src/App.tsx` | Root SolidJS component, message handler |
| `webview/src/components/ChatMessage.tsx` | Renders a single message with markdown, code blocks, tool cards |
| `webview/src/components/ChatInput.tsx` | Input with @mentions, skill chips, file attachment |
| `webview/src/components/ModelSelector.tsx` | Model picker dropdown |

## Build System

esbuild bundles both the extension and the webview:

```bash
npm run build # production build
npm run watch # incremental rebuild on file change
```

Config: `esbuild.config.mjs`. Notable: `.md` files are loaded as text strings (used for built-in skills).

## Data Flow: Sending a Message

1. User types in `ChatInput.tsx` and presses Enter
2. Webview posts `{ type: 'sendMessage', content, model }` to extension
3. `extension.ts` routes to `ChatHandler`
4. `ChatHandler` builds the system prompt (active file, skills list, capabilities, LUCENT.md)
5. OpenRouter API called with streaming enabled
6. Each token streamed back as `{ type: 'streamChunk', content }`
7. If AI calls a tool: `{ type: 'toolApprovalRequest' }` sent to webview
8. User approves → webview posts `{ type: 'toolApprovalResponse', approved: true }`
9. Tool executed, result appended to messages, API called again
10. `{ type: 'streamEnd', usage }` sent when done; `{ type: 'usageUpdate' }` updates status bar
111 changes: 111 additions & 0 deletions docs-site/docs/developer/building-locally.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
---
sidebar_position: 5
title: Building Locally
description: How to set up your development environment, run a debug build, and work with the esbuild bundler.
---

# Building Locally

## Prerequisites

- Node.js 20+
- VS Code 1.85+
- Git

## Setup

```bash
git clone https://github.com/lucent-org/lucent-code
cd lucent-code
npm install
```

This installs dependencies for both the extension host (`src/`) and the webview (`webview/`). They share a single root `package.json`.

## Development Build

Press **F5** in VS Code to launch the Extension Development Host — a separate VS Code window with the extension loaded from source.

Or manually:

```bash
npm run watch # rebuild on every file change (esbuild incremental)
```

Then open the Command Palette (`Ctrl+Shift+P`) → **Developer: Reload Window** to pick up changes.

## Production Build

```bash
npm run build # one-off production build
npm run package # build + vsce package → .vsix file
```

Output:
- `dist/extension.js` — bundled extension host
- `dist/webview.js` — bundled webview

## esbuild Config

`esbuild.config.mjs` bundles both targets. Key configuration:

```javascript
// Extension host — CommonJS for VS Code
{
entryPoints: ['src/extension.ts'],
platform: 'node',
format: 'cjs',
external: ['vscode'],
loader: { '.md': 'text' }, // built-in skills imported as strings
}

// Webview — ESM/browser
{
entryPoints: ['webview/src/index.tsx'],
platform: 'browser',
format: 'iife',
}
```

The `.md` text loader allows skill files to be imported directly:
```typescript
import codeReview from './code-review.md'; // returns file content as string
```

## Shared Types

`src/shared/types.ts` is imported by both the extension host and the webview. It's the single source of truth for the message protocol, model types, and conversation types.

## TypeScript Declaration for `.md` Imports

`src/markdown.d.ts` declares the module type so TypeScript is happy:

```typescript
declare module '*.md' {
const content: string;
export default content;
}
```

## Testing

Run regression test scripts against a live OpenRouter API:

```bash
node scripts/test-skills-full.mjs # test all built-in skills
node scripts/test-code-review.mjs # test code-review skill specifically
```

These scripts call the OpenRouter API directly — you need `OPENROUTER_API_KEY` set in your environment (or a `.env` file).

## Packaging for Marketplace

```bash
npm run package
```

Generates `lucent-code-X.Y.Z.vsix`. Install locally with:

```bash
code --install-extension lucent-code-X.Y.Z.vsix
```
Loading
Loading