123 Commits
0.1.0 ... 2.0.6

Author SHA1 Message Date
andrepimenta
c11224310d Add 2.0.4 and 2.0.6 release notes to CHANGELOG
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:48:49 +01:00
andrepimenta
cb5943eec5 Replace PATH injection with post-install executable.path auto-config
Drops the cross-platform PATH-injection workaround in favor of probing
claude availability after install and writing the known install location
to claudeCodeChat.executable.path when it's not on PATH. Terminals for
login/model/usage/slash commands now launch claude directly via
createTerminal's shellPath/shellArgs so they work identically across
PowerShell, cmd, bash, and zsh. WSL nodePath is now optional (recent
Claude ships as a native binary) and its field moved below claudePath.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:44:00 +01:00
andrepimenta
3d8bcf5241 Hide OpenCredits option when feature flag is disabled and bump to 2.0.5
Only show "Just try it · Pay as you go with OpenCredits" in the
install modal when the OpenCredits feature flag is enabled for
the user's region.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-19 21:21:11 +01:00
andrepimenta
474910a330 Bump version to 2.0.4
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 23:11:33 +01:00
andrepimenta
58de99030c Improve plan mode display and permission prompt
- Render ExitPlanMode as formatted markdown plan with proper headings,
  lists, and code blocks instead of raw key-value dump
- Show allowed prompts as clickable action buttons below the plan
- Use 📋 Plan header instead of 🔧 ExitPlanMode
- Permission prompt says "Approve the plan above?" with Approve button
- Hide "Always allow" for plan approvals
- Full height plan content without scroll constraint

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 21:18:52 +01:00
andrepimenta
6d112012b2 Add analytics tracking to MCP, skills, plugins, and checkout
Track modal opens, installs, removals with properties for MCP
servers, skills, and plugins. Add checkout completed event and
support attempted event. Include error details in install failures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 22:37:41 +01:00
andrepimenta
e31c5357d2 Bump version to 2.0.2
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 15:11:14 +01:00
andrepimenta
b3ff6e9c03 Update README for v2.0 with new features
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 15:09:58 +01:00
andrepimenta
05a15d19a6 Add editor name tag to Umami analytics
Pass vscode.env.appName (VS Code, Cursor, etc.) as data-tag
on the Umami script to segment analytics by editor.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 14:55:34 +01:00
andrepimenta
f5d4c7851b Gitignore .claude/settings.local.json
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 14:41:30 +01:00
andrepimenta
ef0b3c1b4e Update Umami to product.opencredits.ai and track checkout start
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 14:35:17 +01:00
andrepimenta
deca7de8d5 Bring in mcp-skills-plugins branch: marketplace, plugins, skills, OpenCredits, and image previews
Major release adding:
- MCP marketplace with curated registry and search across multiple registries
- Plugins and skills marketplace integration
- OpenCredits payment integration with model selection and checkout flow
- Image preview before sending (paste, file picker)
- Self-hosted Umami analytics with custom events
- Support feedback modal with Discord webhook
- Local OpenAI to Anthropic router for model routing
- Inline stop button replacing send during processing
- Improved install flow requiring Node.js 18+
- WSL env var passthrough fixes
- Better error handling and Windows compatibility

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 23:54:13 +01:00
andrepimenta
b527b6f4c9 Add Open VSIX build script
Build script that creates Open VSIX variant with different branding:
- Changes displayName to "Claude Code Chat"
- Uses icon.png instead of icon-bubble.png
- Automatically backs up and restores files after build

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 21:44:03 +00:00
andrepimenta
2f792e7158 Bump version to 1.1.0
- Update changelog with all new features since 1.0.7
- Add Inline Diff Viewer section to README
- Update package.json version

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-06 12:56:15 +00:00
andrepimenta
97920395d1 Add WSL support for usage terminal commands
Run ccusage commands through bash -ic in WSL to properly load shell environment.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-06 12:55:44 +00:00
andrepimenta
2e640fa20a Use --permission-mode plan flag for plan mode
Replace message prepending with native CLI flag for cleaner implementation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-06 12:32:25 +00:00
andrepimenta
5136985474 Add Umami analytics events to install flow
Track user journey through installation:
- Install modal shown
- Install started
- Install success/failed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-06 11:19:23 +00:00
andrepimenta
683148c4cf Add install modal for users without Claude Code CLI
Shows a clean modal when Claude Code is not installed, with one-click
installation that auto-detects platform (npm if node>=18, otherwise
curl/PowerShell). Runs silently in background with progress indicator.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 10:58:44 +00:00
andrepimenta
e18fa5e261 Improve terminal and UI experience
- Open all terminals in editor area (first column) instead of panel
- Update login message to mention Claude plan (Pro/Max) and API key
- Hide "Claude Code Chat" title when window is very small (<350px)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 00:41:53 +00:00
andrepimenta
63299008d0 Add subscription detection and usage badge to status bar
- Detect user subscription type (Pro/Max) via CLI initialize request
- Cache subscription type in globalState for persistence
- Show clickable usage badge with chart icon in status bar
- Plan users: show "Max Plan" badge, opens live usage view
- API users: show cost badge, opens recent usage history
- Badge opens terminal in editor with ccusage command

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 00:29:08 +00:00
andrepimenta
14ac46018f Run /compact command in chat instead of spawning terminal
The /compact command now executes through the chat interface using
the existing Claude process, providing a seamless user experience
instead of opening a separate terminal window.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 18:12:46 +00:00
andrepimenta
a156881a08 Migrate permission system from MCP file-based to stdio-based
Replace MCP permission server with stdio-based permission flow that
communicates directly with Claude CLI via stdin/stdout. This simplifies
the architecture and fixes permission expiration issues.

Key changes:
- Use --permission-prompt-tool stdio and --input-format stream-json
- Handle control_request messages for permission prompts
- Send control_response via stdin to approve/deny
- Check local permissions for auto-approval of pre-approved tools
- Only expire pending permissions when VS Code restarts, not panel close

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 18:07:45 +00:00
andrepimenta
0764bf8202 Improve process termination and update diff icon colors
- Add AbortController for clean process management
- Add _killProcessGroup() with platform-specific handling (Unix/Windows/WSL)
- Add _killClaudeProcess() with proper SIGTERM→wait→SIGKILL flow
- Update spawn options with detached and signal support
- Handle WSL specially with pkill inside the distro
- Update Open Diff button icon to pastel colors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 16:33:27 +00:00
andrepimenta
82899ebb40 Handle conversation compacting with status messages and token reset
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 00:22:47 +00:00
andrepimenta
abf81a1176 Add morphing orange dot processing indicator
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 00:07:08 +00:00
andrepimenta
da46d5e3d9 Optimize diff storage and improve Open Diff button behavior
- Stop storing full file contents in conversation history to reduce memory
- Compute and store only startLine/startLines for accurate line numbers on reload
- Open Diff button now only shows on last pending edit request
- Button uses stored diff data directly instead of re-reading file
- Hide button when edit result arrives (edit no longer pending)
- Show simple completion messages for Edit/MultiEdit/Write results
- Use virtual document scheme (claude-diff:) for read-only diff viewer
- Simplify tool result handling for Read/TodoWrite

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 23:19:04 +00:00
andrepimenta
d20d8667f3 Show full diff in Edit, MultiEdit, and Write tool use messages
Instead of showing simple previews like "Editing X lines" or "Writing X lines",
now displays the actual diff with added/removed lines during the tool request phase.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 16:29:58 +00:00
andrepimenta
6c37394015 Add Open Diff button to open VS Code's native side-by-side diff editor
- Add _openDiffEditor method using vscode.diff command
- Store temp files in extension's storageUri instead of workspace
- Clean up temp files when diff editor is closed
- Force side-by-side mode when opening diff
- Add Open Diff button with red/green icon in summary row

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 16:20:11 +00:00
andrepimenta
2b1ad70f6b Add truncation with expand button to diff display
Show first 6 lines of diff by default with a "Show X more lines"
button to expand and view the full diff when there are more lines.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 15:42:19 +00:00
andrepimenta
bf527bb922 Strip tool_use_error tags from error messages
Remove XML-like <tool_use_error> tags from error content before
displaying to users, showing only the clean error message text.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 15:39:00 +00:00
andrepimenta
df8188380d Fix auto-scroll for diff tool results
Replace incorrect scrollToBottom() calls with scrollToBottomIfNeeded()
to restore auto-scrolling functionality when new Edit, MultiEdit, and
Write tool results are displayed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 15:26:16 +00:00
andrepimenta
79a0b6b4b2 Fix diff line alignment by removing ::before pseudo-elements
Remove ::before pseudo-elements from added/removed diff lines that were
causing inconsistent margins and misaligned text. Lines now align properly
with consistent spacing across context, added, and removed lines.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 15:23:04 +00:00
andrepimenta
dd47efec04 Implement unified diff visualization for Edit, MultiEdit, and Write tools
- Add LCS-based diff algorithm with intelligent line matching
- Show proper line numbers from actual file positions
- Display color-coded additions (green), removals (red), and context lines
- Include summary statistics (+X lines added, -Y lines removed)
- Simplify tool use previews, show detailed diffs in tool results
- Parse tool result content to extract line numbers
- Update styling with monospace font and VS Code git colors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 15:09:20 +00:00
andrepimenta
d891070d9e Modify icon and name 2025-10-14 17:23:49 +01:00
andrepimenta
1be89d43a4 Added more built-in commands 2025-10-01 23:20:26 +01:00
andrepimenta
0abfab72a8 Add changelog 2025-08-26 23:48:38 +01:00
andrepimenta
1eacc6ff74 Remove priority 2025-08-26 23:44:29 +01:00
andrepimenta
031a2c5fc3 Fix typo 2025-07-31 00:03:16 +01:00
andrepimenta
73c4a38da1 Move script to another file 2025-07-30 23:27:24 +01:00
andrepimenta
53acc0a79f changelog 2025-07-30 03:36:58 +01:00
andrepimenta
62163dbc32 respect telemetry settings 2025-07-30 03:36:37 +01:00
andrepimenta
d225ff2596 Fix permission dialog when closing and opening 2025-07-30 03:26:59 +01:00
Andre Pimenta
ab5c393253 Merge pull request #87 from horatio-sans-serif/main
Remove maxlength limit for custom command prompt textarea
2025-07-29 01:03:06 +01:00
andrepimenta
d6a73a1a7f Add claude-code-chat-permissions-mcp folder 2025-07-29 00:58:20 +01:00
andrepimenta
5abb1fedd9 Save message in text box 2025-07-28 23:45:42 +01:00
andrepimenta
3b534cfce2 Always display history and new chat 2025-07-28 23:37:31 +01:00
andrepimenta
6bd906981b Fix new chat 2025-07-28 23:33:17 +01:00
andrepimenta
4f126641e4 Fix request start time isProcessing 2025-07-28 22:31:30 +01:00
andrepimenta
2d63eaac58 Fix close and open conversation 2025-07-28 22:09:22 +01:00
Old Yeller
f44dc28763 Remove maxlength limit for custom command prompt textarea
This fixes issue #86 by removing the maxlength attribute from the custom command prompt textarea in the slash commands modal. Users can now add much longer markdown content when creating custom slash commands, resolving the previous truncation at ~500 characters.
2025-07-28 20:14:13 +00:00
andrepimenta
2c47349282 fix: input overflow and flexible panel positioning 2025-07-22 21:50:56 +01:00
andrepimenta
43c1c85efb Fix permissions and add windows support 2025-07-20 00:07:02 +01:00
Andre Pimenta
b07857bf57 Update README.md 2025-07-16 00:00:06 +01:00
andrepimenta
c9677b6185 Changelog and readme 2025-07-15 22:58:59 +01:00
andrepimenta
2053e768a8 Yolo mode warning less scary 2025-07-14 23:59:22 +01:00
andrepimenta
980d19bcb2 Yolo mode a bit less subtle 2025-07-14 23:55:13 +01:00
andrepimenta
8a581908e3 Yolo mode 2025-07-14 23:46:03 +01:00
andrepimenta
2feaed600d Fix permissions on side panel 2025-07-14 23:15:40 +01:00
andrepimenta
826c25bdd6 Remove custom commands from settings 2025-07-14 22:18:29 +01:00
Andre Pimenta
3e8a9630bd Merge pull request #49 from andrepimenta/feature/mcp-support
Feature/mcp support
2025-07-14 22:12:13 +01:00
andrepimenta
00f4e272b6 Custom commands 2025-07-14 22:11:48 +01:00
andrepimenta
2aa7db86e7 custom commands init 2025-07-14 21:57:33 +01:00
andrepimenta
42a5ebf763 Remove MCP from settings 2025-07-14 21:26:55 +01:00
andrepimenta
5b671485a1 Fix panel sending multiple prompts 2025-07-14 20:47:16 +01:00
andrepimenta
6e9893e3f3 Working in windows 2025-07-12 13:24:25 +01:00
andrepimenta
5c33bc94c1 Fix expand 2025-07-12 00:10:10 +01:00
andrepimenta
1681bea37f Filter internal mcp 2025-07-12 00:02:12 +01:00
andrepimenta
b8e5c253a3 Popular MCPs 2025-07-11 23:57:55 +01:00
andrepimenta
2eceda51ed Edit MCP 2025-07-11 23:19:33 +01:00
andrepimenta
cba6138828 Add popular MCPs 2025-07-11 23:06:35 +01:00
andrepimenta
03b0eb96fe MCP Servers UI 2025-07-11 22:39:48 +01:00
andrepimenta
cf46551f00 Don't override mcp-servers.json 2025-07-09 14:48:18 +01:00
andrepimenta
857d55e8d1 change permissions mcp to claude-code-chat-permissions 2025-07-09 14:43:10 +01:00
andrepimenta
ae9ad4a794 Showing mcp servers 2025-07-09 14:39:33 +01:00
andrepimenta
83584fff60 MCP modal 2025-07-09 14:32:12 +01:00
andrepimenta
521f9a7d68 Only scroll to bottom if the user is at bottom 2025-07-09 13:56:35 +01:00
andrepimenta
4f5a4cb7ef Copy code block 2025-07-09 13:46:32 +01:00
andrepimenta
234516c1ef Parse code blocks 2025-07-09 13:36:25 +01:00
Andre Pimenta
b261f5cb8a Merge pull request #45 from andrepimenta/feature/permissions
Feature/permissions
2025-07-09 13:17:31 +01:00
andrepimenta
88a2ba71a1 Add permission text 2025-07-09 02:19:50 +01:00
andrepimenta
717284a979 Let user chose yolo mode when there is an error with permissions 2025-07-09 01:59:39 +01:00
andrepimenta
d9baf71e4a Use fs watch instead of polling 2025-07-09 01:36:24 +01:00
andrepimenta
bad8c9a0a8 yolo mode warning 2025-07-09 01:28:43 +01:00
andrepimenta
2e4c866da2 Yolo mode init 2025-07-09 00:32:38 +01:00
andrepimenta
1fa94b9c54 Ability to add permission 2025-07-09 00:19:14 +01:00
andrepimenta
3ec983188a Settings UI for permissions 2025-07-08 23:57:38 +01:00
andrepimenta
0b98538903 permission button ui 2025-07-08 23:32:55 +01:00
andrepimenta
44166defce check commands that make sense to use wildcards 2025-07-08 23:20:50 +01:00
andrepimenta
ddf83cf760 Nice always allow ui 2025-07-08 22:51:52 +01:00
andrepimenta
63acf5e7f9 Always allow 2025-07-08 22:29:57 +01:00
andrepimenta
ede4fbaf98 Permission UI 2025-07-08 22:01:33 +01:00
andrepimenta
06eb335f7b initial implementation 2025-07-08 21:04:10 +01:00
andrepimenta
f501d2ddc4 Move images to .claude folder 2025-07-08 13:16:24 +01:00
andrepimenta
586b004273 Enable sidebar view 2025-07-08 12:48:15 +01:00
andrepimenta
b2579ed0f8 Plan mode only for current message fix 2025-07-07 23:41:34 +01:00
andrepimenta
0bdb0ce30a Allow copy paste images and screenshots 2025-07-07 23:23:27 +01:00
andrepimenta
bb20bb29c5 Display unix time when message is Claude AI usage limit reached 2025-07-07 22:39:39 +01:00
Andre Pimenta
5a59d67021 Merge pull request #38 from tonydehnke/patch-2
fix/Update README.md - Fix Issues Link
2025-07-07 21:38:21 +01:00
Tony Dehnke
7649c9aaab Update README.md - Fix Issues Link
Fix Support - Issues Link so it goes the repo correctly.

https://github.com/andrepimenta/claude-code-chat/issues
2025-07-01 14:32:43 +02:00
andrepimenta
2d3c12ca38 Update expand UI 2025-06-24 22:58:35 +01:00
andrepimenta
c5486c6e26 Write UI 2025-06-24 22:45:58 +01:00
andrepimenta
5168aa0333 changelog 2025-06-24 18:00:02 +01:00
andrepimenta
581ad59ea2 Fix thinking 2025-06-24 17:57:42 +01:00
andrepimenta
4c57af4141 MultiEdit UI 2025-06-24 17:40:55 +01:00
andrepimenta
64767e3850 Update ui-styles.ts 2025-06-24 04:15:47 +01:00
andrepimenta
be8be4c700 Click on file container to open it 2025-06-24 03:46:44 +01:00
andrepimenta
f603b4a298 No completed messages 2025-06-24 03:43:53 +01:00
andrepimenta
e6bdc80946 Better colors 2025-06-24 03:42:22 +01:00
andrepimenta
dcd49458cb Label Todos 2025-06-24 03:36:15 +01:00
andrepimenta
0129cd1bc9 Changed to claude is working 2025-06-24 03:28:12 +01:00
andrepimenta
6dfdc24500 Hide result message for Read and Edit 2025-06-24 03:18:52 +01:00
andrepimenta
e97062ae03 File name better 2025-06-24 02:53:54 +01:00
andrepimenta
8b0bc2904b Diff preview 2025-06-24 02:39:00 +01:00
andrepimenta
bc7fa07e3a Update README 2025-06-24 02:19:36 +01:00
andrepimenta
cd2b563be4 Show WSL alert for windows users 2025-06-24 01:59:24 +01:00
andrepimenta
4bab912554 Thinking modal confirm button 2025-06-24 00:06:22 +01:00
andrepimenta
c3645e5d6b Update thinking mode switch name 2025-06-23 23:58:12 +01:00
andrepimenta
3bd35f0685 Change thinking mode modal to when it is enabled 2025-06-23 23:52:25 +01:00
andrepimenta
a1c2e6d139 Start new session when WSL config changes 2025-06-23 23:33:42 +01:00
andrepimenta
e6681c4ae1 Fix wsl commands 2025-06-23 23:28:11 +01:00
andrepimenta
300e569c96 Fix italic formatting 2025-06-21 00:58:24 +01:00
andrepimenta
cedde172bf version 2025-06-21 00:44:28 +01:00
andrepimenta
1dca03a3ec Always show new session button 2025-06-21 00:39:53 +01:00
38 changed files with 45120 additions and 2131 deletions

5
.claude/settings.json Normal file
View File

@@ -0,0 +1,5 @@
{
"enabledPlugins": {
"frontend-design@claude-plugins-official": true
}
}

View File

@@ -1,13 +0,0 @@
{
"permissions": {
"allow": [
"Bash(npm run compile:*)",
"Bash(grep:*)",
"Bash(sed:*)",
"Bash(rg:*)",
"Bash(npx tsc:*)"
],
"deny": []
},
"enableAllProjectMcpServers": false
}

4
.gitignore vendored
View File

@@ -3,4 +3,6 @@ dist
node_modules
.vscode-test/
*.vsix
backup
backup
backup-files
.claude/settings.local.json

View File

@@ -9,3 +9,10 @@ vsc-extension-quickstart.md
**/*.map
**/*.ts
**/.vscode-test.*
backup
.claude
claude-code-chat-permissions-mcp/**
node_modules
mcp-permissions.js
backup-files
build

View File

@@ -4,6 +4,317 @@ All notable changes to the "claude-code-chat" extension will be documented in th
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
## [2.0.6] - 2026-04-23
### 🚀 Features Added
- **Smarter post-install setup**: Fresh installs now "just work" without a VS Code restart. After install, the extension checks whether `claude` resolved on your PATH and, if not, auto-configures `claudeCodeChat.executable.path` to the known install location. An existing custom executable path is respected.
- **WSL: Node.js path is now optional**: Recent Claude Code ships as a native binary and doesn't need Node. Leave the **Node.js Path** field blank unless you installed Claude via npm. The WSL settings panel was also reordered so **Claude Path** comes first.
### 🐛 Bug Fixes
- **Rock-solid terminals across shells**: Login, Model, /usage, and slash-command terminals now launch Claude directly instead of sending text through the shell. Fixes a class of quoting issues on Windows PowerShell and keeps behavior identical across PowerShell, cmd, bash, and zsh.
### 🔧 Technical Improvements
- Terminal sites now use `createTerminal`'s `shellPath`/`shellArgs` — no shell quoting, consistent env inheritance, identical behavior across OSes.
## [2.0.4] - 2026-04-21
### 🚀 Features Added
- **Plan Mode (Improved)**:
- Plans now render as beautifully formatted markdown with headings, lists, and code blocks
- Suggested actions shown as clickable buttons below the plan (e.g. "run npm build")
- Permission prompt says "Approve the plan above?" with an Approve button instead of the generic tool approval
- **MCP, Skills & Plugins Marketplace**:
- Browse 30+ curated MCP servers (GitHub, Slack, Stripe, Notion, Supabase, etc.)
- Search across both add-mcp curated and official Anthropic registries with smart ranking
- Install MCP servers to project (`.mcp.json`) or global (`~/.claude.json`)
- Skills marketplace with one-click install via `npx skills add`
- Plugins marketplace to extend Claude Code
- OAuth authentication support — open terminal to log in to MCPs
- **150+ AI Models via OpenCredits**:
- Quick model switching: GPT, Gemini, MiniMax, Kimi, GLM, DeepSeek buttons above the text box
- Browse and select from 150+ models across providers
- Pay-as-you-go with OpenCredits — no subscription needed
- US & EU provider filtering option in settings
- Model selection persists correctly after checkout
- **Image Preview**:
- Paste or pick images with thumbnail preview before sending
- Remove attached images before sending
- Multiple image attachments per message
- Image paths in text auto-detected and sent as base64
- **Support & Feedback**:
- "Support" button in status bar to send bug reports and feature requests
- Submissions sent directly to Discord
### 🎨 UI Improvements
- Inline stop button replaces send button during processing
- Self-hosted Umami analytics with editor tracking (VS Code vs Cursor)
- BETA badge on model section with instant tooltip
- Cleaner model selector and Browse All Models alignment
### 🐛 Bug Fixes & Reliability
- Fix model not being selected after OpenCredits checkout
- Fix provider choice modal appearing unexpectedly after settings changes
- Fix duplicate login error toast
- Fix WSL environment variable passthrough for OpenCredits
- Fix Windows URL opening with `start` command
- Fix `--mcp-config` error on fresh installs
- Await `setEnvsDisabled` so settings reflect changes immediately
- Skip npx install prompt with `-y` flag for skills
- Better install error messages (Node.js 18+ requirement)
- Add node and mocha types to tsconfig for clean editor diagnostics
- Remove debug `console.log`s, add `console.error` to empty catch blocks
## [1.1.0] - 2025-12-06
### 🚀 Features Added
- **Install Modal**: Added installation flow for users without Claude Code CLI
- Auto-detects when Claude Code is not installed
- One-click installation with progress indicator
- Platform-specific installation commands
- **Diff Viewer Improvements**:
- Show full diff in Edit, MultiEdit, and Write tool use messages
- Add "Open Diff" button to open VS Code's native side-by-side diff editor
- Add truncation with expand button for long diffs
- Optimize diff storage and improve Open Diff button behavior
- **Processing Indicator**: New morphing orange dot animation while Claude is working
- **Subscription Detection**: Added usage badge to status bar showing plan type (Pro, Max) or API cost
- **Conversation Compacting**: Handle `/compact` command in chat with status messages and token reset
- **Permission System**: Migrated from MCP file-based to stdio-based permission prompts
- **Plan Mode**: Now uses native `--permission-mode plan` CLI flag for cleaner implementation
### 🐛 Bug Fixes
- Fixed diff line alignment by removing ::before pseudo-elements
- Fixed auto-scroll for diff tool results
- Strip tool_use_error tags from error messages
- Improved process termination handling
### 🔧 Technical Improvements
- Run /compact command in chat instead of spawning terminal
- Improved terminal and UI experience
- Updated diff icon colors
### 📊 Analytics
- Added Umami analytics events to track install flow (modal shown, started, success/failed)
## [1.0.7] - 2025-10-01
### 🚀 Features Added
- **Slash Commands Update**: Added 4 new slash commands to the commands modal
- `/add-dir` - Add additional working directories
- `/agents` - Manage custom AI subagents for specialized tasks
- `/rewind` - Rewind the conversation and/or code
- `/usage` - Show plan usage limits and rate limit status (subscription plans only)
### 📚 Documentation Updates
- Updated slash commands count from 19+ to 23+ built-in commands
- Enhanced command descriptions for better clarity:
- `/config` - Now specifies "Open the Settings interface (Config tab)"
- `/cost` - Added note about cost tracking guide for subscription-specific details
- `/status` - Expanded description to mention version, model, account, and connectivity
- `/terminal-setup` - Added clarification about iTerm2 and VSCode only support
## [1.0.6] - 2025-08-26
### 🐛 Bug Fixes
- Fixed typo in codebase
- Removed priority settings that were no longer needed
### 🔧 Technical Improvements
- Moved script to separate file for better code organization
## [1.0.5] - 2025-07-30
### 🚀 Features Added
- **MCP Integration**: Added claude-code-chat-permissions-mcp folder for enhanced permission management
- **Message Persistence**: Save message in text box for better user experience
- **UI Improvements**: Always display history and new chat options
- **Input Enhancement**: Removed maxlength limit for custom command prompt textarea
### 🐛 Bug Fixes
- Fixed new chat functionality
- Fixed request start time isProcessing issue
- Fixed close and open conversation behavior
### 🔄 Merged Pull Requests
- Merged PR #87 from horatio-sans-serif/main
## [1.0.4] - 2025-01-22
### 🐛 Bug Fixes
- Fixed input text area overflow issue by adding `box-sizing: border-box` to prevent padding from extending beyond container width
- Fixed command parameter handling for `claude-code-chat.openChat` to properly handle both ViewColumn and Uri parameters from different invocation contexts
### 🔧 Technical Improvements
- Enhanced `show()` method to accept optional ViewColumn parameter with ViewColumn.Two as default
- Added proper type checking for command parameters to handle context menu invocations
- Improved webview panel positioning with flexible column parameter support
### 🎨 UI/UX Improvements
- Resolved text input container sizing issues that caused visual overflow
- Better input field styling consistency across different VS Code themes
## [1.0.0] - 2025-01-15
### 🚀 Major Features Added
#### **Advanced Permissions Management System**
- Complete permissions framework with MCP integration for secure tool execution
- Interactive permission dialogs with detailed tool information and command previews
- "Always Allow" functionality with smart command pattern matching for common tools (npm, git, docker, etc.)
- YOLO mode for power users to skip all permission checks
- Comprehensive permissions settings UI with ability to add/remove specific permissions
- File system watcher for real-time permission request handling
- Workspace-specific permission storage and management
#### **MCP (Model Context Protocol) Server Management**
- Complete MCP server configuration interface
- Popular MCP servers gallery with one-click installation
- Custom MCP server creation with validation
- Server management (edit, delete, enable/disable)
- Automatic permissions server integration
- WSL path conversion for cross-platform compatibility
#### **Sidebar Integration & Multi-Panel Support**
- Native VS Code sidebar view with full chat functionality
- Smart panel management (auto-close main panel when sidebar opens)
- Persistent session state across panel switches
- Proper webview lifecycle management
- Activity bar integration with custom icon
#### **Image & Clipboard Enhancements**
- Drag-and-drop image support directly into chat
- Clipboard image paste functionality (Ctrl+V for screenshots)
- Multiple image selection with VS Code's native file picker
- Automatic image organization in `.claude/claude-code-chat-images/` folder
- Automatic `.gitignore` creation for image folders
- Support for PNG, JPG, JPEG, GIF, SVG, WebP, BMP formats
#### **Code Block & Syntax Improvements**
- Enhanced markdown parsing with proper code block detection
- Syntax highlighting for code blocks with language detection
- Copy-to-clipboard functionality for code blocks
- Improved inline code rendering
- Better handling of technical identifiers and underscores
### 🎨 UI/UX Improvements
#### **Settings & Configuration**
- Comprehensive settings modal with organized sections
- YOLO mode toggle with visual warnings and explanations
- Real-time settings synchronization between UI and VS Code config
- Better visual hierarchy and professional styling
- Smart configuration validation and error handling
#### **Message & Chat Interface**
- Improved message spacing and visual consistency
- Enhanced tool result display with better formatting
- Smart scrolling behavior (only auto-scroll if user is at bottom)
- Loading indicators and processing states
- Better error handling and user feedback
#### **YOLO Mode Visual Design**
- Less subtle YOLO mode section (increased opacity and size)
- Changed warning icon from ⚠️ to 🚀 for less intimidating appearance
- Soft tomato red styling that's noticeable but not scary
- Clear explanation of YOLO mode functionality
### 🔧 Technical Enhancements
#### **Session & State Management**
- Persistent session state across VS Code restarts
- Proper cleanup of resources and event listeners
- Better error handling for failed operations
- Improved memory management for large conversations
#### **Cross-Platform Compatibility**
- Enhanced WSL support with proper path conversion
- Windows-specific improvements and fixes
- Better handling of different operating system environments
- Improved subprocess management and cleanup
#### **Performance Optimizations**
- Reduced context usage with more efficient tool operations
- Better file search and workspace integration
- Optimized message handling and UI updates
- Improved extension activation and initialization times
#### **Developer Experience**
- Better error messages and debugging information
- Improved extension logging and troubleshooting
- Enhanced development workflow support
- Better TypeScript integration and type safety
### 🐛 Bug Fixes
- Fixed multiple permission prompts being sent simultaneously
- Resolved panel management issues with multiple webviews
- Fixed expand/collapse functionality for long content
- Corrected Unix timestamp parsing for usage limit messages
- Fixed WSL integration on Windows systems
- Resolved markdown parsing issues with underscores in code
- Fixed copy-paste functionality for images and code blocks
- Corrected file path handling across different platforms
### 🔄 Breaking Changes
- Permission system now requires explicit approval for tool execution (unless YOLO mode is enabled)
- Image files are now stored in `.claude/claude-code-chat-images/` instead of root directory
- MCP configuration moved to extension storage instead of global config
### 📚 Documentation & Community
- Updated README with comprehensive feature documentation
- Fixed GitHub issues link in repository
- Enhanced examples and usage guides
- Better onboarding experience for new users
## [0.1.3] - 2025-06-24
### 🚀 Features Added
#### **MultiEdit and Edit Tool Diff Display**
- Added comprehensive diff visualization for MultiEdit tool operations
- Shows file path with click-to-open functionality
- Displays multiple edits with numbered labels (Edit #1, Edit #2, etc.)
- Smart truncation handling - shows complete edits within line limits
- Expandable interface with "Show X more edits" button
- Visual separators between individual edits
- Consistent styling with existing Edit tool diff display
#### **Enhanced Tool Result Management**
- Added MultiEdit to hidden tool results list for cleaner interface
- Tool results for Read, Edit, TodoWrite, and MultiEdit now show loading states instead of uninteresting success messages
- Improved user experience by hiding redundant "Tool executed successfully" messages
### 🎨 UI/UX Improvements
#### **Thinking Intensity Modal Enhancement**
- Fixed bug where thinking mode toggle text changed before user confirmation
- Toggle text now only updates when user clicks "Confirm" button
- Preview selection highlighting still works during option exploration
- Improved user experience with proper confirmation workflow
#### **Consistent Message Spacing**
- Standardized spacing between tool messages and user/Claude messages
- Updated tool input padding from 12px to 8px to match message spacing
- Unified visual consistency across all message types
#### **Refined Visual Design**
- Changed MultiEdit edit number labels from purple to subtle professional styling
- Used VS Code theme colors for better integration
- Improved overall visual cohesion with more sober color palette
### 🔧 Technical Improvements
- Enhanced tool message formatting infrastructure
- Improved diff rendering performance for multiple edits
- Better error handling for malformed MultiEdit tool inputs
- Optimized truncation logic for complex multi-edit operations
## [0.1.2] - 2025-06-20
### 🐛 Bug Fixes
- Fixed markdown parsing bug where underscores in code identifiers (like "protein_id") were incorrectly converted to italic formatting
- Updated regex pattern to only apply italic formatting when underscores are surrounded by whitespace or at string boundaries
- Preserved proper formatting for code snippets and technical identifiers
- Always show New Chat button
## [0.1.0] - 2025-06-20
### 🚀 Major Features Added

View File

@@ -16,14 +16,14 @@ Ditch the command line and experience Claude Code like never before. This extens
🖥️ **No Terminal Required** - Beautiful chat interface replaces command-line interactions
**Restore Checkpoints** - Undo changes and restore code to any previous state
🔌 **MCP, Skills & Plugins** - Browse, search, and install from curated marketplaces
💾 **Conversation History** - Automatic conversation history and session management
**Instant Access** - Claude Code integrated directly into VS Code
🎨 **VS Code Native** - Seamlessly matches your editor's theme and design
📁 **Smart File Context** - Reference any file with simple @ mentions
🛑 **Full Control** - Start, stop, and manage AI processes with ease
🤖 **Model Selection** - Choose between Opus, Sonnet, or Default based on your needs
🎨 **VS Code & Cursor** - Works in VS Code, Cursor, and other compatible editors
🧠 **Plan and Ultrathink modes** - Plan First and Ultrathink modes
**Smart Context** - Reference files, paste images, and create custom commands
🐧 **Windows/WSL Support** - Full native Windows and WSL support
![Claude Code Chat Cut](https://github.com/user-attachments/assets/d4ded28f-a4ed-4862-9766-c1ff89947775)
![Claude Code Chat 1 0 0](https://github.com/user-attachments/assets/5954a74c-eff7-4205-8482-6a1c9de6e102)
---
@@ -34,8 +34,9 @@ Ditch the command line and experience Claude Code like never before. This extens
- No terminal required - everything through the UI
- Real-time streaming responses with typing indicators
- One-click message copying with visual feedback
- Rich markdown support for code blocks and formatting
- Enhanced markdown support with syntax highlighting
- Auto-resizing input that grows with your content
- Copy-to-clipboard for code blocks
### ⏪ **Checkpoint & Session Management**
- **Restore Checkpoints** - Instantly undo changes and restore to any previous state
@@ -45,35 +46,71 @@ Ditch the command line and experience Claude Code like never before. This extens
- Real-time cost and token tracking
- Session statistics and performance metrics
### 📝 **Inline Diff Viewer**
- **Full Diff Display** - See complete file changes directly in Edit, MultiEdit, and Write messages
- **Open in VS Code Diff** - One-click button to open VS Code's native side-by-side diff editor
- **Smart Truncation** - Long diffs are truncated with an expand button for better readability
- **Syntax Highlighting** - Proper code highlighting in diff views
- **Visual Change Indicators** - Clear green/red highlighting for additions and deletions
### 🔌 **MCP, Skills & Plugins Marketplace** ⭐ **NEW IN V2.0**
- **MCP Servers** - Browse 30+ featured servers (GitHub, Slack, Stripe, Notion, etc.) with dual registry search
- **Skills** - Browse and install curated skills from skills.sh with project or global scope
- **Plugins** - Browse and install plugins to extend Claude Code
- **Smart Search** - Search across add-mcp curated and official Anthropic registries with relevance ranking
- **Project or Global Install** - Install MCP servers to `.mcp.json` or `~/.claude.json`, skills to `.claude/skills/`
- **One-Click Install** - Pre-filled configuration with env vars, headers, and OAuth authentication
### 🖼️ **Image Preview & Attachments** ⭐ **NEW IN V2.0**
- **Paste Images** - Paste images with thumbnail preview before sending
- **File Picker** - Select images through VS Code's native file picker
- **Preview Strip** - See attached images above the text box with remove buttons
- **Inline Detection** - Image paths in messages are automatically detected and sent as base64
- **Multiple Images** - Attach multiple images to a single message
### 🔒 **Advanced Permissions System**
- **Interactive Permission Dialogs** - Detailed tool information with command previews
- **Always Allow Functionality** - Smart command pattern matching for common tools (npm, git, docker)
- **YOLO Mode** - Skip all permission checks for power users
- **Workspace Permissions** - Granular control over what tools can execute
- **Real-time Permission Management** - Add/remove permissions through intuitive UI
### 📱 **Sidebar Integration**
- **Native VS Code Sidebar** - Full chat functionality in the sidebar panel
- **Smart Panel Management** - Automatic switching between main and sidebar views
- **Persistent Sessions** - State maintained across panel switches
- **Activity Bar Integration** - Quick access from VS Code's activity bar
### 📁 **Smart File Integration**
- Type `@` to instantly search and reference workspace files
- Image attachments via file browser
- Image attachments via file browser and copy-paste screenshots
- Lightning-fast file search across your entire project
- Seamless context preservation for multi-file discussions
### 🛠️ **Tool Management**
- Visual dashboard showing all available Claude Code tools
- Real-time tool execution with formatted results
- Process control - start, stop, and monitor operations
- Inline stop button replaces send during processing
- Smart permission system for secure tool execution
### 🎨 **VS Code Integration**
- Native theming that matches your editor
- Status bar integration with connection status
- Status bar with support button
- Activity bar panel for quick access
- Responsive design for any screen size
### 🤖 **Model Selection**
- **Quick Buttons** - GPT, Gemini, MiniMax, Kimi, GLM, DeepSeek above the text box
- **Opus** - Most capable model for complex tasks requiring deep reasoning
- **Sonnet** - Balanced model offering great performance for most use cases
- **Default** - Uses your configured model setting
- Model preference persists across sessions and is saved automatically
- Easy switching via dropdown selector in the chat interface
- Visual confirmation when switching between models
- One-click model configuration through integrated terminal
- **150+ OpenCredits Models** - Browse and switch to any available model
- Model preference persists across sessions
- Provider choice (OpenCredits vs Anthropic) for Claude models
### ⚡ **Slash Commands Integration**
- **Slash Commands Modal** - Type "/" to access all Claude Code commands instantly
- **19+ Built-in Commands** - /cost, /status, /config, /help, /memory, /review, and more
- **23+ Built-in Commands** - /agents, /cost, /config, /memory, /review, and more
- **Custom Command Support** - Execute any Claude Code command with session context
- **Session-Aware Execution** - All commands run with current conversation context
- **Terminal Integration** - Commands open directly in VS Code terminal with WSL support
@@ -85,14 +122,20 @@ Ditch the command line and experience Claude Code like never before. This extens
- **Intelligent Prompting** - Different prompts based on selected thinking intensity
- **Token Awareness** - Higher thinking levels consume more tokens but provide deeper reasoning
### 💬 **Support & Feedback** ⭐ **NEW IN V2.0**
- **In-App Support** - Click "Support" in the status bar to send feedback
- **Bug Reports & Feature Requests** - Submit directly from the extension
- **Optional Email** - Include your email for follow-up
---
## 🚀 **Getting Started**
### Prerequisites
- **VS Code 1.80+** - Latest version recommended
- **VS Code 1.80+** or **Cursor** - Latest version recommended
- **Claude Code CLI** - [Install from Anthropic](https://claude.ai/code)
- **Active Claude API or subscription** - API or Pro/Max plan
- **Node.js 18+** - Required for installation
### Installation
@@ -214,31 +257,40 @@ Example configuration in `settings.json`:
- Type `@` followed by your search term to quickly reference files
- Use `@src/` to narrow down to specific directories
- Reference multiple files in one message for cross-file analysis
- Paste images directly with preview thumbnails before sending
- Paste screenshots with Ctrl+V for instant visual communication
### ⚡ **Productivity Boosters**
- **Creates checkpoints automatically** before changes for safe experimentation
- **Restore instantly** if changes don't work out as expected
- Use the stop button to cancel long-running operations
- Permission system prevents accidental tool execution
- YOLO mode for power users who want speed over safety
- Inline stop button to cancel long-running operations
- Copy message contents to reuse Claude's responses
- Open history panel to reference previous conversations
- Sidebar integration for multi-panel workflow
- **Plan mode** and **Ultrathink** toggles above the text box
### 🎨 **Interface Customization**
- The UI automatically adapts to your VS Code theme
- Messages are color-coded: Green for you, Blue for Claude
- Hover over messages to reveal the copy button
- Enhanced code block rendering with syntax highlighting
- Copy-to-clipboard functionality for code blocks
---
## 🔧 **Advanced Features**
### 🛠️ **Tool Integration**
Claude Code Chat provides full access to all Claude Code tools:
- **Bash** - Execute shell commands
Claude Code Chat provides secure access to all Claude Code tools:
- **Bash** - Execute shell commands with permission controls
- **File Operations** - Read, write, and edit files
- **Search** - Grep and glob pattern matching
- **Search** - Grep and glob pattern matching across workspace
- **Web** - Fetch and search web content
- **Multi-edit** - Batch file modifications
- **While in Beta, all tools are enabled by default, use at your own risk!**
- **MCP Servers** - Extend functionality with Model Context Protocol servers
- **Permissions System** - Granular control over tool execution for security
### 📊 **Analytics & Monitoring**
- **Real-time cost tracking** - Monitor your API usage
@@ -296,7 +348,8 @@ See the [LICENSE](LICENSE) file for details.
Need help? We've got you covered:
- 🐛 **Issues**: [GitHub Issues](https://github.com/your-repo/claude-code-chat/issues)
- 💬 **In-App** - Click "Support" in the status bar to send feedback directly
- 🐛 **Issues**: [GitHub Issues](https://github.com/andrepimenta/claude-code-chat/issues)
---

21
backup.sh Executable file
View File

@@ -0,0 +1,21 @@
#!/bin/bash
# Backup script for src folder
# Get the directory where the script is located
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Create backup directory if it doesn't exist
BACKUP_DIR="$SCRIPT_DIR/backup-files"
mkdir -p "$BACKUP_DIR"
# Generate timestamp
TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S")
# Create backup filename
BACKUP_NAME="src-backup-$TIMESTAMP"
# Copy src folder to backup
cp -r "$SCRIPT_DIR/src" "$BACKUP_DIR/$BACKUP_NAME"
echo "Backup created: $BACKUP_DIR/$BACKUP_NAME"

71
build/open-vsix/build.sh Executable file
View File

@@ -0,0 +1,71 @@
#!/bin/bash
# Build script for Open VSIX version
# This applies Open VSIX-specific changes, builds the package, then reverts
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VERSION="2.0.6"
OUTPUT_NAME="vsix-claude-code-chat-${VERSION}.vsix"
echo "Building Open VSIX version ${VERSION}..."
# Backup original files to build folder
cp package.json "${SCRIPT_DIR}/package.json.backup"
cp src/extension.ts "${SCRIPT_DIR}/extension.ts.backup"
# Backup original icon.png if it exists
if [ -f "icon.png" ]; then
mv icon.png "${SCRIPT_DIR}/icon.png.backup"
fi
# Copy Open VSIX icon
cp "${SCRIPT_DIR}/icon.png" icon.png
echo "Copied Open VSIX icon"
# Temporarily remove icon-bubble.png (not needed for Open VSIX)
if [ -f "icon-bubble.png" ]; then
mv icon-bubble.png "${SCRIPT_DIR}/icon-bubble.png.backup"
fi
# Apply Open VSIX changes to package.json
sed -i.bak 's/"displayName": "Chat for Claude Code"/"displayName": "Claude Code Chat"/' package.json
sed -i.bak 's/"icon": "icon-bubble.png"/"icon": "icon.png"/g' package.json
rm -f package.json.bak
# Apply Open VSIX changes to extension.ts
sed -i.bak "s/icon-bubble.png/icon.png/g" src/extension.ts
rm -f src/extension.ts.bak
echo "Applied Open VSIX changes to package.json and extension.ts"
# Compile TypeScript
echo "Compiling TypeScript..."
npm run compile
# Build the VSIX
echo "Building VSIX package..."
vsce package --out "${OUTPUT_NAME}"
# Restore original files from build folder
mv "${SCRIPT_DIR}/package.json.backup" package.json
mv "${SCRIPT_DIR}/extension.ts.backup" src/extension.ts
# Restore original icon
rm -f icon.png
if [ -f "${SCRIPT_DIR}/icon.png.backup" ]; then
mv "${SCRIPT_DIR}/icon.png.backup" icon.png
fi
# Restore icon-bubble.png
if [ -f "${SCRIPT_DIR}/icon-bubble.png.backup" ]; then
mv "${SCRIPT_DIR}/icon-bubble.png.backup" icon-bubble.png
fi
# Recompile with original extension.ts
echo "Recompiling with original files..."
npm run compile
echo "Restored original files"
echo "Built: ${OUTPUT_NAME}"

BIN
build/open-vsix/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 689 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,212 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import * as fs from "fs";
import * as path from "path";
const server = new McpServer({
name: "Claude Code Permissions MCP Server",
version: "0.0.1",
});
// Get permissions directory from environment
const PERMISSIONS_PATH = process.env.CLAUDE_PERMISSIONS_PATH;
if (!PERMISSIONS_PATH) {
console.error("CLAUDE_PERMISSIONS_PATH environment variable not set");
process.exit(1);
}
interface WorkspacePermissions {
alwaysAllow: {
[toolName: string]: boolean | string[]; // true for all, or array of allowed commands/patterns
};
}
function getWorkspacePermissionsPath(): string | null {
if (!PERMISSIONS_PATH) return null;
return path.join(PERMISSIONS_PATH, 'permissions.json');
}
function loadWorkspacePermissions(): WorkspacePermissions {
const permissionsPath = getWorkspacePermissionsPath();
if (!permissionsPath || !fs.existsSync(permissionsPath)) {
return { alwaysAllow: {} };
}
try {
const content = fs.readFileSync(permissionsPath, 'utf8');
return JSON.parse(content);
} catch (error) {
console.error(`Error loading workspace permissions: ${error}`);
return { alwaysAllow: {} };
}
}
function isAlwaysAllowed(toolName: string, input: any): boolean {
const permissions = loadWorkspacePermissions();
const toolPermission = permissions.alwaysAllow[toolName];
if (!toolPermission) return false;
// If it's true, always allow
if (toolPermission === true) return true;
// If it's an array, check for specific commands (mainly for Bash)
if (Array.isArray(toolPermission)) {
if (toolName === 'Bash' && input.command) {
const command = input.command.trim();
return toolPermission.some(allowedCmd => {
// Support exact match or pattern matching
if (allowedCmd.includes('*')) {
// Handle patterns like "npm i *" to match both "npm i" and "npm i something"
const baseCommand = allowedCmd.replace(' *', '');
if (command === baseCommand) {
return true; // Exact match for base command
}
// Pattern match for command with arguments
const pattern = allowedCmd.replace(/\*/g, '.*');
return new RegExp(`^${pattern}$`).test(command);
}
return command.startsWith(allowedCmd);
});
}
}
return false;
}
function generateRequestId(): string {
return `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
}
async function requestPermission(tool_name: string, input: any): Promise<{approved: boolean, reason?: string}> {
if (!PERMISSIONS_PATH) {
console.error("Permissions path not available");
return { approved: false, reason: "Permissions path not configured" };
}
// Check if this tool/command is always allowed for this workspace
if (isAlwaysAllowed(tool_name, input)) {
console.error(`Tool ${tool_name} is always allowed for this workspace`);
return { approved: true };
}
const requestId = generateRequestId();
const requestFile = path.join(PERMISSIONS_PATH, `${requestId}.request`);
const responseFile = path.join(PERMISSIONS_PATH, `${requestId}.response`);
// Write request file
const request = {
id: requestId,
tool: tool_name,
input: input,
timestamp: new Date().toISOString()
};
try {
fs.writeFileSync(requestFile, JSON.stringify(request, null, 2));
// Use fs.watch to wait for response file
return new Promise<{approved: boolean, reason?: string}>((resolve) => {
const timeout = setTimeout(() => {
watcher.close();
// Clean up request file on timeout
if (fs.existsSync(requestFile)) {
fs.unlinkSync(requestFile);
}
console.error(`Permission request ${requestId} timed out`);
resolve({ approved: false, reason: "Permission request timed out" });
}, 3600000); // 1 hour timeout
const watcher = fs.watch(PERMISSIONS_PATH, (eventType, filename) => {
if (eventType === 'rename' && filename === path.basename(responseFile)) {
// Check if file exists (rename event can be for creation or deletion)
if (fs.existsSync(responseFile)) {
try {
const responseContent = fs.readFileSync(responseFile, 'utf8');
const response = JSON.parse(responseContent);
// Clean up response file
fs.unlinkSync(responseFile);
// Clear timeout and close watcher
clearTimeout(timeout);
watcher.close();
resolve({
approved: response.approved,
reason: response.approved ? undefined : "User rejected the request"
});
} catch (error) {
console.error(`Error reading response file: ${error}`);
// Continue watching in case of read error
}
}
}
});
// Handle watcher errors
watcher.on('error', (error) => {
console.error(`File watcher error: ${error}`);
clearTimeout(timeout);
watcher.close();
resolve({ approved: false, reason: "File watcher error" });
});
});
} catch (error) {
console.error(`Error requesting permission: ${error}`);
return { approved: false, reason: `Error processing permission request: ${error}` };
}
}
server.tool(
"approval_prompt",
'Request user permission to execute a tool via VS Code dialog',
{
tool_name: z.string().describe("The name of the tool requesting permission"),
input: z.object({}).passthrough().describe("The input for the tool"),
tool_use_id: z.string().optional().describe("The unique tool use request ID"),
},
async ({ tool_name, input }) => {
console.error(`Requesting permission for tool: ${tool_name}`);
const permissionResult = await requestPermission(tool_name, input);
const behavior = permissionResult.approved ? "allow" : "deny";
console.error(`Permission ${behavior}ed for tool: ${tool_name}`);
return {
content: [
{
type: "text",
text: behavior === "allow" ?
JSON.stringify({
behavior: behavior,
updatedInput: input,
})
:
JSON.stringify({
behavior: behavior,
message: permissionResult.reason || "Permission denied",
})
,
},
],
};
}
);
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error(`Permissions MCP Server running on stdio`);
console.error(`Using permissions directory: ${PERMISSIONS_PATH}`);
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
{
"name": "claude-code-chat-permissions-mcp",
"version": "1.0.0",
"main": "dist/mcp-permissions.js",
"scripts": {
"start": "tsc && node dist/mcp-permissions.js",
"lint": "eslint . --ext .ts",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"@types/node": "^24.0.13",
"typescript": "^5.8.3"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.15.1",
"zod": "^3.25.76"
}
}

View File

@@ -0,0 +1,11 @@
{
"compilerOptions": {
"module": "commonjs",
"esModuleInterop": true,
"target": "es6",
"moduleResolution": "node",
"sourceMap": true,
"outDir": "dist"
},
"lib": ["es2015"]
}

BIN
icon-bubble.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 KiB

BIN
icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 689 KiB

After

Width:  |  Height:  |  Size: 689 KiB

13954
mcp-permissions.js Normal file

File diff suppressed because one or more lines are too long

14
package-lock.json generated
View File

@@ -1,17 +1,17 @@
{
"name": "claude-code-chat",
"version": "0.0.8",
"version": "2.0.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "claude-code-chat",
"version": "0.0.8",
"version": "2.0.6",
"license": "SEE LICENSE IN LICENSE",
"devDependencies": {
"@types/mocha": "^10.0.10",
"@types/node": "20.x",
"@types/vscode": "^1.95.0",
"@types/vscode": "^1.94.0",
"@typescript-eslint/eslint-plugin": "^8.31.1",
"@typescript-eslint/parser": "^8.31.1",
"@vscode/test-cli": "^0.0.10",
@@ -21,7 +21,7 @@
"typescript": "^5.8.3"
},
"engines": {
"vscode": "^1.95.0"
"vscode": "^1.94.0"
}
},
"node_modules/@azu/format-text": {
@@ -978,9 +978,9 @@
"dev": true
},
"node_modules/@types/vscode": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.100.0.tgz",
"integrity": "sha512-4uNyvzHoraXEeCamR3+fzcBlh7Afs4Ifjs4epINyUX/jvdk0uzLnwiDY35UKDKnkCHP5Nu3dljl2H8lR6s+rQw==",
"version": "1.101.0",
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.101.0.tgz",
"integrity": "sha512-ZWf0IWa+NGegdW3iU42AcDTFHWW7fApLdkdnBqwYEtHVIBGbTu0ZNQKP/kX3Ds/uMJXIMQNAojHR4vexCEEz5Q==",
"dev": true,
"license": "MIT"
},

View File

@@ -1,8 +1,8 @@
{
"name": "claude-code-chat",
"displayName": "Claude Code Chat",
"displayName": "Chat for Claude Code",
"description": "Beautiful Claude Code Chat Interface for VS Code",
"version": "0.1.0",
"version": "2.0.6",
"publisher": "AndrePimenta",
"author": "Andre Pimenta",
"repository": {
@@ -11,7 +11,7 @@
},
"license": "SEE LICENSE IN LICENSE",
"engines": {
"vscode": "^1.95.0"
"vscode": "^1.94.0"
},
"categories": [
"Other",
@@ -56,7 +56,7 @@
"command": "claude-code-chat.openChat",
"title": "Open Claude Code Chat",
"category": "Claude Code Chat",
"icon": "icon.png"
"icon": "icon-bubble.png"
}
],
"keybindings": [
@@ -130,9 +130,10 @@
"claude-code-chat": [
{
"id": "claude-code-chat.chat",
"type": "webview",
"name": "Claude Code Chat",
"when": "true",
"icon": "icon.png",
"icon": "icon-bubble.png",
"contextualTitle": "Claude Code Chat"
}
]
@@ -142,7 +143,7 @@
{
"id": "claude-code-chat",
"title": "Claude Code Chat",
"icon": "icon.png"
"icon": "icon-bubble.png"
}
]
},
@@ -161,8 +162,8 @@
},
"claudeCodeChat.wsl.nodePath": {
"type": "string",
"default": "/usr/bin/node",
"description": "Path to Node.js in the WSL distribution"
"default": "",
"description": "Optional path to Node.js in the WSL distribution. Only needed if Claude was installed via npm. Recent Claude installs ship as a native executable and don't require Node."
},
"claudeCodeChat.wsl.claudePath": {
"type": "string",
@@ -171,9 +172,39 @@
},
"claudeCodeChat.thinking.intensity": {
"type": "string",
"enum": ["think", "think-hard", "think-harder", "ultrathink"],
"enum": [
"think",
"think-hard",
"think-harder",
"ultrathink"
],
"default": "think",
"description": "Thinking mode intensity level. Higher levels provide more detailed reasoning but consume more tokens."
},
"claudeCodeChat.permissions.yoloMode": {
"type": "boolean",
"default": false,
"description": "Enable Yolo Mode to skip all permission checks. Use with caution as Claude can execute any command without asking."
},
"claudeCodeChat.executable.path": {
"type": "string",
"default": "",
"description": "Custom path to the Claude Code executable. Leave empty to use the default 'claude' command."
},
"claudeCodeChat.environment.variables": {
"type": "object",
"default": {},
"description": "Custom environment variables to pass to Claude Code. Example: {\"ANTHROPIC_API_KEY\": \"sk-...\"}"
},
"claudeCodeChat.environment.disabled": {
"type": "boolean",
"default": false,
"description": "When enabled, custom environment variables are not passed to Claude Code."
},
"claudeCodeChat.router.enabled": {
"type": "boolean",
"default": false,
"description": "Enable the local router to convert OpenAI format to Anthropic format. Required for providers that use OpenAI-compatible APIs."
}
}
}
@@ -189,7 +220,7 @@
"devDependencies": {
"@types/mocha": "^10.0.10",
"@types/node": "20.x",
"@types/vscode": "^1.95.0",
"@types/vscode": "^1.94.0",
"@typescript-eslint/eslint-plugin": "^8.31.1",
"@typescript-eslint/parser": "^8.31.1",
"@vscode/test-cli": "^0.0.10",

File diff suppressed because it is too large Load Diff

148
src/model-updater.ts Normal file
View File

@@ -0,0 +1,148 @@
interface ApiModel {
id: string;
name?: string;
description?: string;
pricing?: { prompt: number; completion: number; currency?: string; unit?: string };
context_length?: number;
max_output_tokens?: number;
[key: string]: any;
}
interface BundledModel {
id: string;
name: string;
description?: string;
provider: string;
quickLabel?: string;
context_length?: number;
max_output_tokens?: number;
tierModels?: { sonnet: string; opus: string; haiku: string };
[key: string]: any;
}
interface ProviderResolver {
main: RegExp;
opus?: RegExp;
haiku?: RegExp;
}
function parseVersion(ver: string): number[] {
return ver.split('.').map(Number);
}
function compareVersions(a: string, b: string): number {
const va = parseVersion(a);
const vb = parseVersion(b);
for (let i = 0; i < Math.max(va.length, vb.length); i++) {
const na = va[i] || 0;
const nb = vb[i] || 0;
if (na !== nb) { return na - nb; }
}
return 0;
}
function findHighestMatch(apiModels: ApiModel[], regex: RegExp): ApiModel | null {
let best: ApiModel | null = null;
let bestVer: string | null = null;
for (const m of apiModels) {
const match = regex.exec(m.id);
if (match) {
const ver = match[1] || '0';
if (!bestVer || compareVersions(ver, bestVer) > 0) {
bestVer = ver;
best = m;
}
}
}
return best;
}
const providerResolvers: Record<string, ProviderResolver> = {
'zai/glm-': {
main: /^zai\/glm-(\d+(?:\.\d+)?)$/,
haiku: /^zai\/GLM-([\d.]+)-(?:Air|Flash)$/i
},
'openai/gpt-': {
main: /^openai\/gpt-([\d.]+)-codex$/,
haiku: /^openai\/gpt-([\d.]+)-codex-mini$/
},
'gemini-': {
main: /^(?:google\/)?gemini-([\d.]+)-pro-preview$/,
opus: /^(?:google\/)?gemini-([\d.]+)-pro-preview-thinking$/,
haiku: /^(?:google\/)?gemini-([\d.]+)-flash(?:-preview)?$/
},
'deepseek/deepseek-': {
main: /^deepseek\/deepseek-v([\d.]+)[-:]thinking$/
},
'minimax/minimax-': {
main: /^minimax\/minimax-m([\d.]+)$/
},
'moonshotai/kimi-': {
main: /^moonshotai\/kimi-k([\d.]+)$/,
haiku: /^moonshotai\/kimi-k([\d.]+)-turbo$/
}
};
export function resolveLatestModels(apiModels: ApiModel[], bundledModels: BundledModel[]): BundledModel[] {
return bundledModels.map(bundled => {
const b: BundledModel = JSON.parse(JSON.stringify(bundled));
let resolver: ProviderResolver | null = null;
for (const prefix of Object.keys(providerResolvers)) {
if (b.id.toLowerCase().startsWith(prefix)) {
resolver = providerResolvers[prefix];
break;
}
}
if (!resolver) { return b; }
// Resolve main (sonnet-tier) model
const mainMatch = findHighestMatch(apiModels, resolver.main);
if (mainMatch) {
b.id = mainMatch.id;
b.name = mainMatch.name || b.name;
b.description = mainMatch.description || b.description;
b.context_length = mainMatch.context_length || b.context_length;
b.max_output_tokens = mainMatch.max_output_tokens || b.max_output_tokens;
if (b.tierModels) {
b.tierModels.sonnet = mainMatch.id;
if (!resolver.opus) {
b.tierModels.opus = mainMatch.id;
}
}
}
// Resolve opus-tier model (e.g. Gemini thinking variant)
if (resolver.opus && b.tierModels) {
const opusMatch = findHighestMatch(apiModels, resolver.opus);
if (opusMatch) {
b.tierModels.opus = opusMatch.id;
}
}
// Resolve haiku-tier model
if (resolver.haiku && b.tierModels) {
const haikuMatch = findHighestMatch(apiModels, resolver.haiku);
if (haikuMatch) {
b.tierModels.haiku = haikuMatch.id;
}
}
return b;
});
}
export async function fetchAndResolveModels(bundledModels: BundledModel[], apiBaseUrl: string = 'https://ccc.api.opencredits.ai'): Promise<BundledModel[] | null> {
try {
const response = await fetch(apiBaseUrl + '/v1/models');
const data: any = await response.json();
const apiModels: ApiModel[] = data.data || data;
if (!Array.isArray(apiModels) || apiModels.length === 0) {
return null;
}
return resolveLatestModels(apiModels, bundledModels);
} catch (e) {
console.log('Auto-update models failed:', e);
return null;
}
}

153
src/plugins-script.ts Normal file
View File

@@ -0,0 +1,153 @@
const getPluginsScript = () => `
// ─── Plugins ───
var topPlugins = (window.__topPlugins || []);
var pluginsDisplayedList = null;
function formatPluginName(name) {
return name.replace(/-/g, ' ').replace(/\\b\\w/g, function(c) { return c.toUpperCase(); });
}
function showPluginsModal() {
sendStats('Plugins modal opened');
document.getElementById('pluginsModal').style.display = 'flex';
loadInstalledPlugins();
renderAvailablePlugins(topPlugins);
}
function hidePluginsModal() {
document.getElementById('pluginsModal').style.display = 'none';
}
function loadInstalledPlugins() {
vscode.postMessage({ type: 'loadPlugins' });
}
function displayPlugins(data) {
var pluginsList = document.getElementById('pluginsList');
pluginsList.innerHTML = '';
var enabled = data.enabled || {};
var keys = Object.keys(enabled);
if (keys.length === 0) {
pluginsList.innerHTML = '<div class="no-servers">' +
'<div class="no-servers-icon"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg></div>' +
'<div class="no-servers-text">No plugins enabled</div>' +
'</div>';
return;
}
keys.forEach(function(installId) {
var isEnabled = enabled[installId];
var name = installId.replace(/@.*$/, '');
var displayName = formatPluginName(name);
var plugin = topPlugins.find(function(p) { return p.installId === installId; });
var desc = plugin ? plugin.description : '';
var verified = plugin ? plugin.verified : false;
var item = document.createElement('div');
item.className = 'mcp-server-item';
var verifiedHtml = verified ? '<span class="marketplace-item-verified" title="Anthropic verified">&#10003;</span>' : '';
var statusHtml = isEnabled ? '<span class="server-type" style="background:rgba(0,122,204,0.2);color:var(--vscode-charts-blue);">enabled</span>' : '<span class="server-type">disabled</span>';
item.innerHTML = '<div class="server-info" style="min-width:0;overflow:hidden;">' +
'<div class="server-name">' + escapeHtml(displayName) + verifiedHtml + ' ' + statusHtml + '</div>' +
(desc ? '<div class="server-config" style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">' + escapeHtml(desc) + '</div>' : '') +
'</div>' +
'<div class="server-actions" style="flex-shrink:0;">' +
'<button class="btn outlined server-delete-btn" data-plugin="' + escapeHtml(installId) + '" onclick="removePlugin(this.dataset.plugin)">Remove</button>' +
'</div>';
pluginsList.appendChild(item);
});
}
function renderAvailablePlugins(plugins) {
var grid = document.getElementById('pluginsGrid');
if (!grid) return;
if (!plugins || plugins.length === 0) {
grid.innerHTML = '<div class="marketplace-loading">No plugins found.</div>';
return;
}
var html = '';
plugins.forEach(function(plugin) {
var name = plugin.name || 'Unknown';
var displayName = formatPluginName(name);
var desc = escapeHtml(plugin.description || 'No description');
var verified = plugin.verified;
var safeId = escapeHtml(plugin.installId || name).replace(/'/g, '&#39;');
html += '<div class="marketplace-item" data-plugin-id="' + safeId + '" onclick="showPluginDetail(this.dataset.pluginId)">' +
'<div class="marketplace-item-header">' +
'<div class="marketplace-item-icon-placeholder">' + escapeHtml(displayName.charAt(0).toUpperCase()) + '</div>' +
'<div class="marketplace-item-info">' +
'<div class="marketplace-item-name">' + escapeHtml(displayName) + '</div>' +
'</div>' +
'</div>' +
'<div class="marketplace-item-desc">' + desc + '</div>' +
'</div>';
});
grid.innerHTML = html;
}
function searchPlugins(query) {
if (!query) {
renderAvailablePlugins(topPlugins);
return;
}
var q = query.toLowerCase();
var filtered = topPlugins.filter(function(p) {
return (p.name && p.name.toLowerCase().indexOf(q) >= 0) ||
(p.description && p.description.toLowerCase().indexOf(q) >= 0);
});
renderAvailablePlugins(filtered);
}
function showPluginDetail(installId) {
var plugin = topPlugins.find(function(p) { return p.installId === installId; });
if (!plugin) return;
var name = plugin.name || 'Unknown';
var displayName = formatPluginName(name);
var desc = plugin.description || 'No description available.';
var verified = plugin.verified;
var verifiedHtml = verified ? '<span class="marketplace-item-verified" title="Anthropic verified">&#10003; Anthropic verified</span>' : '';
var grid = document.getElementById('pluginsGrid');
pluginsDisplayedList = grid.innerHTML;
grid.innerHTML = '<div class="marketplace-detail">' +
'<button class="marketplace-back-btn" onclick="backToPluginsList()">&#8592; Back</button>' +
'<div class="marketplace-detail-header">' +
'<div class="marketplace-item-icon-placeholder" style="width:40px;height:40px;font-size:18px;">' + escapeHtml(displayName.charAt(0).toUpperCase()) + '</div>' +
'<div class="marketplace-detail-header-info">' +
'<div class="marketplace-detail-name">' + escapeHtml(displayName) + '</div>' +
'<div class="marketplace-detail-header-meta">' + verifiedHtml + '</div>' +
'</div>' +
'<button class="btn marketplace-install-btn" data-plugin="' + escapeHtml(installId) + '" onclick="installPlugin(this.dataset.plugin)">Enable</button>' +
'</div>' +
'<div class="marketplace-detail-desc">' + escapeHtml(desc) + '</div>' +
'<div class="marketplace-detail-row"><a href="https://github.com/anthropics/claude-plugins-official/tree/main/' + (plugin.type === 'official' ? 'plugins' : 'external_plugins') + '/' + escapeHtml(name) + '" target="_blank" class="marketplace-detail-link">View on GitHub</a></div>' +
'<div style="font-size:11px;color:var(--vscode-descriptionForeground);margin-top:4px;">Adds <code style="font-size:10px;">' + escapeHtml(installId) + '</code> to .claude/settings.json</div>' +
'</div>';
}
function backToPluginsList() {
var grid = document.getElementById('pluginsGrid');
if (pluginsDisplayedList) {
grid.innerHTML = pluginsDisplayedList;
} else {
renderAvailablePlugins(topPlugins);
}
}
function installPlugin(installId) {
sendStats('Plugin installed', { plugin: installId });
vscode.postMessage({ type: 'installPlugin', installId: installId });
hidePluginsModal();
}
function removePlugin(installId) {
sendStats('Plugin removed', { plugin: installId });
vscode.postMessage({ type: 'removePlugin', installId: installId });
}
`;
export default getPluginsScript;

26
src/plugins-ui.ts Normal file
View File

@@ -0,0 +1,26 @@
const getPluginsHtml = () => `
<!-- Plugins modal -->
<div id="pluginsModal" class="tools-modal" style="display: none;">
<div class="tools-modal-content">
<div class="tools-modal-header">
<span>Plugins</span>
<button class="tools-close-btn" onclick="hidePluginsModal()">✕</button>
</div>
<div class="tools-list">
<div class="mcp-servers-list" id="pluginsList">
<!-- Installed plugins will be loaded here -->
</div>
<div class="mcp-popular-servers" id="pluginsMarketplace">
<h4>Available Plugins</h4>
<div class="marketplace-search">
<input type="text" id="pluginsSearch" placeholder="Search plugins..." oninput="searchPlugins(this.value)" />
</div>
<div class="marketplace-grid" id="pluginsGrid">
</div>
</div>
</div>
</div>
</div>
`;
export default getPluginsHtml;

View File

@@ -0,0 +1,66 @@
[
{
"id": "openai/gpt-5.3-codex",
"name": "GPT 5.3 Codex",
"description": "Coding-focused GPT-5.3 variant with optimized routing.",
"context_length": 400000,
"max_output_tokens": 128000,
"credits_per_request": 4.921875,
"provider": "OpenAI",
"quickLabel": "GPT",
"tierModels": { "sonnet": "openai/gpt-5.3-codex", "opus": "openai/gpt-5.3-codex", "haiku": "openai/gpt-5.1-codex-mini" }
},
{
"id": "google/gemini-3.1-pro-preview",
"name": "Gemini 3.1 Pro Preview",
"description": "Google's Gemini 3.1 Pro with enhanced reasoning and multimodal support.",
"context_length": 1000000,
"max_output_tokens": 64000,
"credits_per_request": 4.375,
"provider": "Google",
"quickLabel": "Gemini",
"tierModels": { "sonnet": "google/gemini-3.1-pro-preview", "opus": "google/gemini-3.1-pro-preview", "haiku": "google/gemini-3-flash" }
},
{
"id": "minimax/minimax-m2.7",
"name": "Minimax M2.7",
"description": "MiniMax M2.7 with enhanced context understanding and improved complex tool use. Optimized for agentic workflows and long-horizon tasks.",
"context_length": 204800,
"max_output_tokens": 131000,
"credits_per_request": 0.46875,
"provider": "MiniMax",
"quickLabel": "MiniMax"
},
{
"id": "moonshotai/kimi-k2.5",
"name": "Kimi K2.5",
"description": "Kimi K2.5 is Moonshot AI's native multimodal model with strong general reasoning, visual coding, and agentic tool-calling.",
"context_length": 262114,
"max_output_tokens": 262114,
"credits_per_request": 1.125,
"provider": "Moonshot AI",
"quickLabel": "Kimi",
"tierModels": { "sonnet": "moonshotai/kimi-k2.5", "opus": "moonshotai/kimi-k2.5", "haiku": "moonshotai/kimi-k2-turbo" }
},
{
"id": "zai/glm-5",
"name": "GLM 5",
"description": "GLM-5 is the latest GLM series text model with stronger reasoning, long-context chat, and reliable tool use.",
"context_length": 202800,
"max_output_tokens": 131100,
"credits_per_request": 1.3125,
"provider": "Zhipu AI",
"quickLabel": "GLM",
"tierModels": { "sonnet": "zai/glm-5", "opus": "zai/glm-5", "haiku": "zai/glm-4.7-flash" }
},
{
"id": "deepseek/deepseek-v3.2-thinking",
"name": "DeepSeek V3.2 Thinking",
"description": "DeepSeek V3.2 thinking/reasoner mode. Reasoning-first model built for agents. First DeepSeek model with thinking-in-tool-use capability.",
"context_length": 128000,
"max_output_tokens": 64000,
"credits_per_request": 0.21875,
"provider": "DeepSeek",
"quickLabel": "DeepSeek"
}
]

265
src/router/formatRequest.ts Normal file
View File

@@ -0,0 +1,265 @@
interface MessageCreateParamsBase {
model: string;
messages: any[];
system?: any;
temperature?: number;
tools?: any[];
stream?: boolean;
}
/**
* Validates OpenAI format messages to ensure complete tool_calls/tool message pairing.
* Requires tool messages to immediately follow assistant messages with tool_calls.
* Enforces strict immediate following sequence between tool_calls and tool messages.
*/
function validateOpenAIToolCalls(messages: any[]): any[] {
const validatedMessages: any[] = [];
for (let i = 0; i < messages.length; i++) {
const currentMessage = { ...messages[i] };
// Process assistant messages with tool_calls
if (currentMessage.role === "assistant" && currentMessage.tool_calls) {
const validToolCalls: any[] = [];
const removedToolCallIds: string[] = [];
// Collect all immediately following tool messages
const immediateToolMessages: any[] = [];
let j = i + 1;
while (j < messages.length && messages[j].role === "tool") {
immediateToolMessages.push(messages[j]);
j++;
}
// For each tool_call, check if there's an immediately following tool message
currentMessage.tool_calls.forEach((toolCall: any) => {
const hasImmediateToolMessage = immediateToolMessages.some(toolMsg =>
toolMsg.tool_call_id === toolCall.id
);
if (hasImmediateToolMessage) {
validToolCalls.push(toolCall);
} else {
removedToolCallIds.push(toolCall.id);
}
});
// Update the assistant message
if (validToolCalls.length > 0) {
currentMessage.tool_calls = validToolCalls;
} else {
delete currentMessage.tool_calls;
}
// Only include message if it has content or valid tool_calls
if (currentMessage.content || currentMessage.tool_calls) {
validatedMessages.push(currentMessage);
}
}
// Process tool messages
else if (currentMessage.role === "tool") {
let hasImmediateToolCall = false;
// Check if the immediately preceding assistant message has matching tool_call
if (i > 0) {
const prevMessage = messages[i - 1];
if (prevMessage.role === "assistant" && prevMessage.tool_calls) {
hasImmediateToolCall = prevMessage.tool_calls.some((toolCall: any) =>
toolCall.id === currentMessage.tool_call_id
);
} else if (prevMessage.role === "tool") {
// Check for assistant message before the sequence of tool messages
for (let k = i - 1; k >= 0; k--) {
if (messages[k].role === "tool") continue;
if (messages[k].role === "assistant" && messages[k].tool_calls) {
hasImmediateToolCall = messages[k].tool_calls.some((toolCall: any) =>
toolCall.id === currentMessage.tool_call_id
);
}
break;
}
}
}
if (hasImmediateToolCall) {
validatedMessages.push(currentMessage);
}
}
// For all other message types, include as-is
else {
validatedMessages.push(currentMessage);
}
}
return validatedMessages;
}
// Model configuration - set from extension
interface ModelConfig {
haikuModel: string;
sonnetModel: string;
opusModel: string;
}
let modelConfig: ModelConfig | null = null;
export function setModelConfig(config: ModelConfig): void {
modelConfig = config;
console.log('[Router] Model config updated:', config);
}
export function mapModel(anthropicModel: string): string {
console.log('[Router] Mapping model:', anthropicModel);
// If model already contains '/', it's already a provider model ID - return as-is
if (anthropicModel.includes('/')) {
console.log(`[Router] Model already has provider prefix, passing through: ${anthropicModel}`);
return anthropicModel;
}
if (!modelConfig) {
console.log('[Router] No model config set, returning as-is');
return anthropicModel;
}
if (anthropicModel.includes('haiku') && modelConfig.haikuModel) {
console.log(`[Router] Mapping haiku -> ${modelConfig.haikuModel}`);
return modelConfig.haikuModel;
} else if (anthropicModel.includes('sonnet') && modelConfig.sonnetModel) {
console.log(`[Router] Mapping sonnet -> ${modelConfig.sonnetModel}`);
return modelConfig.sonnetModel;
} else if (anthropicModel.includes('opus') && modelConfig.opusModel) {
console.log(`[Router] Mapping opus -> ${modelConfig.opusModel}`);
return modelConfig.opusModel;
}
console.log(`[Router] No mapping found for model: ${anthropicModel}, passing through`);
return anthropicModel;
}
export function formatAnthropicToOpenAI(body: MessageCreateParamsBase): any {
const { model, messages, system = [], temperature, tools, stream } = body;
const openAIMessages = Array.isArray(messages)
? messages.flatMap((anthropicMessage) => {
const openAiMessagesFromThisAnthropicMessage: any[] = [];
if (!Array.isArray(anthropicMessage.content)) {
if (typeof anthropicMessage.content === "string") {
openAiMessagesFromThisAnthropicMessage.push({
role: anthropicMessage.role,
content: anthropicMessage.content,
});
}
return openAiMessagesFromThisAnthropicMessage;
}
if (anthropicMessage.role === "assistant") {
const assistantMessage: any = {
role: "assistant",
content: null,
};
let textContent = "";
const toolCalls: any[] = [];
anthropicMessage.content.forEach((contentPart: any) => {
if (contentPart.type === "text") {
textContent += (typeof contentPart.text === "string"
? contentPart.text
: JSON.stringify(contentPart.text)) + "\n";
} else if (contentPart.type === "tool_use") {
toolCalls.push({
id: contentPart.id,
type: "function",
function: {
name: contentPart.name,
arguments: JSON.stringify(contentPart.input),
},
});
}
});
const trimmedTextContent = textContent.trim();
if (trimmedTextContent.length > 0) {
assistantMessage.content = trimmedTextContent;
}
if (toolCalls.length > 0) {
assistantMessage.tool_calls = toolCalls;
}
if (assistantMessage.content || (assistantMessage.tool_calls && assistantMessage.tool_calls.length > 0)) {
openAiMessagesFromThisAnthropicMessage.push(assistantMessage);
}
} else if (anthropicMessage.role === "user") {
let userTextMessageContent = "";
const subsequentToolMessages: any[] = [];
anthropicMessage.content.forEach((contentPart: any) => {
if (contentPart.type === "text") {
userTextMessageContent += (typeof contentPart.text === "string"
? contentPart.text
: JSON.stringify(contentPart.text)) + "\n";
} else if (contentPart.type === "tool_result") {
subsequentToolMessages.push({
role: "tool",
tool_call_id: contentPart.tool_use_id,
content: typeof contentPart.content === "string"
? contentPart.content
: JSON.stringify(contentPart.content),
});
}
});
const trimmedUserText = userTextMessageContent.trim();
if (trimmedUserText.length > 0) {
openAiMessagesFromThisAnthropicMessage.push({
role: "user",
content: trimmedUserText,
});
}
openAiMessagesFromThisAnthropicMessage.push(...subsequentToolMessages);
}
return openAiMessagesFromThisAnthropicMessage;
})
: [];
const systemMessages = Array.isArray(system)
? system.map((item) => ({
role: "system",
content: typeof item === "string" ? item : item.text
}))
: typeof system === "string" && system.length > 0
? [{ role: "system", content: system }]
: [];
const data: any = {
model: mapModel(model),
messages: [...systemMessages, ...openAIMessages],
temperature,
stream,
};
// Request usage stats in streaming responses
if (stream) {
data.stream_options = { include_usage: true };
}
if (tools) {
data.tools = tools.map((item: any) => ({
type: "function",
function: {
name: item.name,
description: item.description,
parameters: item.input_schema,
},
}));
}
// Validate OpenAI messages to ensure complete tool_calls/tool message pairing
data.messages = [...systemMessages, ...validateOpenAIToolCalls(openAIMessages)];
return data;
}

View File

@@ -0,0 +1,37 @@
export function formatOpenAIToAnthropic(completion: any, model: string): any {
const messageId = "msg_" + Date.now();
const message = completion.choices[0].message;
const content: any[] = [];
if (message.content) {
content.push({ text: message.content, type: "text" });
}
if (message.tool_calls) {
for (const item of message.tool_calls) {
content.push({
type: 'tool_use',
id: item.id,
name: item.function?.name,
input: item.function?.arguments ? JSON.parse(item.function.arguments) : {},
});
}
}
const hasToolUse = message.tool_calls && message.tool_calls.length > 0;
const usage = completion.usage || {};
const result = {
id: messageId,
type: "message",
role: "assistant",
content: content,
stop_reason: hasToolUse ? "tool_use" : "end_turn",
stop_sequence: null,
model,
usage: {
input_tokens: usage.prompt_tokens || 0,
output_tokens: usage.completion_tokens || 0,
},
};
return result;
}

2
src/router/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export { startRouter, stopRouter, isRouterRunning, getRouterPort, setBaseUrl } from './server';
export { setModelConfig } from './formatRequest';

220
src/router/server.ts Normal file
View File

@@ -0,0 +1,220 @@
import * as http from 'http';
import { formatAnthropicToOpenAI } from './formatRequest';
import { streamOpenAIToAnthropic } from './streamResponse';
import { formatOpenAIToAnthropic } from './formatResponse';
const DEFAULT_PORT = 31548;
const DEFAULT_BASE_URL = "http://localhost:8787/v1";
let server: http.Server | null = null;
let currentPort: number = DEFAULT_PORT;
let baseUrl: string = DEFAULT_BASE_URL;
export function setBaseUrl(url: string): void {
baseUrl = url || DEFAULT_BASE_URL;
console.log('[Router] Base URL set to:', baseUrl);
}
// Helper to parse JSON body
async function parseBody(req: http.IncomingMessage): Promise<any> {
return new Promise((resolve, reject) => {
let body = '';
req.on('data', chunk => {
body += chunk.toString();
// Prevent payload too large (50MB limit)
if (body.length > 50 * 1024 * 1024) {
req.destroy();
reject(new Error('Payload too large'));
}
});
req.on('end', () => {
try {
resolve(body ? JSON.parse(body) : {});
} catch (e) {
reject(new Error('Invalid JSON'));
}
});
req.on('error', reject);
});
}
function createServer(): http.Server {
return http.createServer(async (req, res) => {
const url = new URL(req.url || '/', `http://${req.headers.host}`);
const method = req.method || 'GET';
try {
// POST /v1/messages
if (url.pathname === '/v1/messages' && method === 'POST') {
console.log('[Router] 📥 Received request to /v1/messages');
const anthropicRequest = await parseBody(req);
const openaiRequest = formatAnthropicToOpenAI(anthropicRequest);
console.log('[Router] 🔄 Converted to OpenAI format:', {
model: openaiRequest.model,
stream: openaiRequest.stream,
messageCount: openaiRequest.messages?.length
});
const bearerToken = (req.headers['x-api-key'] as string) ||
(req.headers.authorization as string)?.replace("Bearer ", "").replace("bearer ", "");
if (!bearerToken || bearerToken.trim() === '') {
console.log('[Router] ❌ No bearer token found');
res.writeHead(401, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
type: 'error',
error: {
type: 'authentication_error',
message: 'No API key provided. Please configure your OpenCredits user key in environment variables.'
}
}));
return;
}
const fetchHeaders = {
"Content-Type": "application/json",
"Authorization": `Bearer ${bearerToken}`,
"HTTP-Referer": "https://claude-code-chat.local",
"X-Title": "Claude-Code-Chat-Router"
};
const openaiResponse = await fetch(`${baseUrl}/chat/completions`, {
method: "POST",
headers: fetchHeaders,
body: JSON.stringify(openaiRequest),
});
console.log('[Router] 📥 Response status:', openaiResponse.status);
if (!openaiResponse.ok) {
const errorText = await openaiResponse.text();
console.log('[Router] ❌ Error:', errorText);
// Try to parse as JSON, otherwise use raw text
let errorMessage = errorText;
try {
const parsed = JSON.parse(errorText);
errorMessage = parsed.error?.message || parsed.message || errorText;
} catch {}
res.writeHead(openaiResponse.status, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
type: 'error',
error: {
type: openaiResponse.status === 401 ? 'authentication_error' : 'api_error',
message: `[Router] ${errorMessage}`
}
}));
return;
}
if (openaiRequest.stream) {
console.log('[Router] 🌊 Starting stream response');
const anthropicStream = streamOpenAIToAnthropic(
openaiResponse.body as ReadableStream,
openaiRequest.model
);
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
});
const reader = anthropicStream.getReader();
const pump = async () => {
try {
while (true) {
const { done, value } = await reader.read();
if (done) {
res.end();
break;
}
res.write(value);
}
} catch (error) {
console.error('[Router] Stream error:', error);
res.end();
}
};
pump();
} else {
const openaiData = await openaiResponse.json();
const anthropicResponse = formatOpenAIToAnthropic(openaiData, openaiRequest.model);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(anthropicResponse));
}
return;
}
// 404 Not Found
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
} catch (error) {
console.error('[Router] Error processing request:', error);
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
type: 'error',
error: {
type: 'api_error',
message: `[Router] Internal error: ${(error as Error).message}`
}
}));
}
});
}
export function startRouter(port: number = DEFAULT_PORT): Promise<number> {
return new Promise((resolve, reject) => {
if (server) {
console.log('[Router] Already running on port', currentPort);
resolve(currentPort);
return;
}
server = createServer();
server.on('error', (err: NodeJS.ErrnoException) => {
if (err.code === 'EADDRINUSE') {
console.log(`[Router] Port ${port} in use, trying ${port + 1}`);
server = null;
startRouter(port + 1).then(resolve).catch(reject);
} else {
reject(err);
}
});
server.listen(port, () => {
currentPort = port;
console.log(`[Router] 🚀 Running on http://localhost:${port}`);
resolve(port);
});
});
}
export function stopRouter(): Promise<void> {
return new Promise((resolve) => {
if (!server) {
resolve();
return;
}
server.close(() => {
console.log('[Router] Stopped');
server = null;
resolve();
});
});
}
export function isRouterRunning(): boolean {
return server !== null;
}
export function getRouterPort(): number {
return currentPort;
}

View File

@@ -0,0 +1,219 @@
export function streamOpenAIToAnthropic(openaiStream: ReadableStream, model: string): ReadableStream {
const messageId = "msg_" + Date.now();
const enqueueSSE = (controller: ReadableStreamDefaultController, eventType: string, data: any) => {
const sseMessage = `event: ${eventType}\ndata: ${JSON.stringify(data)}\n\n`;
controller.enqueue(new TextEncoder().encode(sseMessage));
};
return new ReadableStream({
async start(controller) {
// Send message_start event
const messageStart = {
type: "message_start",
message: {
id: messageId,
type: "message",
role: "assistant",
content: [],
model,
stop_reason: null,
stop_sequence: null,
usage: { input_tokens: 0, output_tokens: 0 },
},
};
enqueueSSE(controller, "message_start", messageStart);
let contentBlockIndex = 0;
let hasAnyBlock = false;
let hasStartedTextBlock = false;
let isToolUse = false;
let currentToolCallId: string | null = null;
let toolCallJsonMap = new Map<string, string>();
let streamUsage: { input_tokens: number; output_tokens: number } | null = null;
const reader = openaiStream.getReader();
const decoder = new TextDecoder();
let buffer = '';
try {
while (true) {
const { done, value } = await reader.read();
if (done) {
// Process any remaining data in buffer
if (buffer.trim()) {
const lines = buffer.split('\n');
for (const line of lines) {
if (line.trim() && line.startsWith('data: ')) {
const data = line.slice(6).trim();
if (data === '[DONE]') continue;
try {
const parsed = JSON.parse(data);
processStreamChunk(parsed);
} catch (e) {
// Parse error
}
}
}
}
break;
}
// Decode chunk and add to buffer
const chunk = decoder.decode(value, { stream: true });
buffer += chunk;
// Process complete lines from buffer
const lines = buffer.split('\n');
// Keep the last potentially incomplete line in buffer
buffer = lines.pop() || '';
// Process complete lines in order
for (const line of lines) {
if (line.trim() && line.startsWith('data: ')) {
const data = line.slice(6).trim();
if (data === '[DONE]') continue;
try {
const parsed = JSON.parse(data);
processStreamChunk(parsed);
} catch (e) {
// Parse error
continue;
}
}
}
}
} finally {
reader.releaseLock();
}
function processStreamChunk(parsed: any) {
// Capture usage from the chunk if available
if (parsed.usage) {
streamUsage = {
input_tokens: parsed.usage.prompt_tokens || 0,
output_tokens: parsed.usage.completion_tokens || 0,
};
}
const delta = parsed.choices?.[0]?.delta;
if (delta) {
processStreamDelta(delta);
}
}
function closeCurrentBlock() {
if (hasAnyBlock) {
enqueueSSE(controller, "content_block_stop", {
type: "content_block_stop",
index: contentBlockIndex,
});
contentBlockIndex++;
}
hasAnyBlock = true;
}
function processStreamDelta(delta: any) {
// Handle tool calls
if (delta.tool_calls?.length > 0) {
for (const toolCall of delta.tool_calls) {
const toolCallId = toolCall.id;
if (toolCallId && toolCallId !== currentToolCallId) {
closeCurrentBlock();
isToolUse = true;
hasStartedTextBlock = false;
currentToolCallId = toolCallId;
toolCallJsonMap.set(toolCallId, "");
const toolBlock = {
type: "tool_use",
id: toolCallId,
name: toolCall.function?.name,
input: {},
};
enqueueSSE(controller, "content_block_start", {
type: "content_block_start",
index: contentBlockIndex,
content_block: toolBlock,
});
}
if (toolCall.function?.arguments && currentToolCallId) {
const currentJson = toolCallJsonMap.get(currentToolCallId) || "";
toolCallJsonMap.set(currentToolCallId, currentJson + toolCall.function.arguments);
enqueueSSE(controller, "content_block_delta", {
type: "content_block_delta",
index: contentBlockIndex,
delta: {
type: "input_json_delta",
partial_json: toolCall.function.arguments,
},
});
}
}
} else if (delta.content) {
if (isToolUse) {
closeCurrentBlock();
isToolUse = false;
currentToolCallId = null;
}
if (!hasStartedTextBlock) {
if (!hasAnyBlock) {
hasAnyBlock = true;
}
enqueueSSE(controller, "content_block_start", {
type: "content_block_start",
index: contentBlockIndex,
content_block: {
type: "text",
text: "",
},
});
hasStartedTextBlock = true;
}
enqueueSSE(controller, "content_block_delta", {
type: "content_block_delta",
index: contentBlockIndex,
delta: {
type: "text_delta",
text: delta.content,
},
});
}
}
// Close last content block
if (hasAnyBlock) {
enqueueSSE(controller, "content_block_stop", {
type: "content_block_stop",
index: contentBlockIndex,
});
}
// Send message_delta and message_stop
enqueueSSE(controller, "message_delta", {
type: "message_delta",
delta: {
stop_reason: isToolUse ? "tool_use" : "end_turn",
stop_sequence: null,
},
usage: streamUsage || { input_tokens: 0, output_tokens: 0 },
});
enqueueSSE(controller, "message_stop", {
type: "message_stop",
});
controller.close();
},
});
}

5295
src/script.ts Normal file

File diff suppressed because it is too large Load Diff

288
src/skills-script.ts Normal file
View File

@@ -0,0 +1,288 @@
const getSkillsScript = () => `
// ─── Skills ───
var skillsSearchTimeout = null;
var skillsCache = null;
var topSkills = (window.__topSkills || []);
function showSkillsModal() {
sendStats('Skills modal opened');
document.getElementById('skillsModal').style.display = 'flex';
loadInstalledSkills();
if (topSkills.length > 0) {
renderFeaturedSkills(topSkills);
}
}
function renderFeaturedSkills(skills) {
var grid = document.getElementById('skillsGrid');
if (!grid) return;
var html = '';
skills.forEach(function(skill) {
var name = skill.name || 'Unknown';
var installs = skill.installs || 0;
var source = skill.source || '';
var installsHtml = installs > 0 ? '<span class="marketplace-item-stars">' + (installs >= 1000 ? (Math.round(installs / 100) / 10) + 'k' : installs) + ' installs</span>' : '';
var safeId = escapeHtml(skill.id || name).replace(/'/g, '&#39;');
var rawUrl = skill.rawUrl || '';
var installsText = installs >= 1000 ? (Math.round(installs / 100) / 10) + 'k installs' : (installs > 0 ? installs + ' installs' : '');
html += '<div class="marketplace-item" data-skill-id="' + safeId + '" data-skill-source="' + escapeHtml(source) + '" data-skill-name="' + escapeHtml(name) + '" data-skill-rawurl="' + escapeHtml(rawUrl) + '" data-skill-installs="' + escapeHtml(installsText) + '" onclick="installSkillFromMarketplace(this)">' +
'<div class="marketplace-item-header">' +
'<div class="marketplace-item-icon-placeholder">' + escapeHtml(name.charAt(0).toUpperCase()) + '</div>' +
'<div class="marketplace-item-info">' +
'<div class="marketplace-item-name">' + escapeHtml(name) + '</div>' +
'<div class="marketplace-item-meta">' + installsHtml + '</div>' +
'</div>' +
'</div>' +
'<div class="marketplace-item-desc">' + escapeHtml(source) + '</div>' +
'</div>';
});
grid.innerHTML = html;
}
function hideSkillsModal() {
document.getElementById('skillsModal').style.display = 'none';
}
function loadInstalledSkills() {
vscode.postMessage({ type: 'loadSkills' });
}
function displaySkills(skills) {
var skillsList = document.getElementById('skillsList');
skillsList.innerHTML = '';
if (!skills || skills.length === 0) {
skillsList.innerHTML = '<div class="no-servers">' +
'<div class="no-servers-icon"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg></div>' +
'<div class="no-servers-text">No skills installed</div>' +
'<button class="btn outlined no-servers-btn" onclick="showSkillAddForm()">+ Create skill</button>' +
'</div>';
return;
}
skills.forEach(function(skill, idx) {
var item = document.createElement('div');
item.className = 'mcp-server-item';
item.style.flexDirection = 'column';
item.style.alignItems = 'stretch';
var desc = skill.description || 'No description';
var content = skill.content || '';
var detailId = 'skill-detail-' + idx;
item.innerHTML = '<div class="skill-item-row">' +
'<div class="skill-item-info">' +
'<div class="server-name">' + escapeHtml(skill.name) + ' <span class="server-type">' + escapeHtml(skill.scope) + '</span></div>' +
'<div class="skill-item-desc">' + escapeHtml(desc) + '</div>' +
'</div>' +
'<div class="server-actions" style="flex-shrink:0;">' +
'<button class="btn outlined" style="font-size:11px;padding:3px 8px;" onclick="toggleSkillDetail(\\'' + detailId + '\\')">Details</button>' +
'<button class="btn outlined server-delete-btn" data-skill="' + escapeHtml(skill.name) + '" data-scope="' + escapeHtml(skill.scope) + '" onclick="deleteSkill(this.dataset.skill, this.dataset.scope)">Delete</button>' +
'</div>' +
'</div>' +
'<div id="' + detailId + '" class="skill-detail-content" style="display:none;">' +
'<pre style="white-space:pre-wrap;font-size:11px;color:var(--vscode-descriptionForeground);margin:8px 0 0;max-height:200px;overflow-y:auto;">' + escapeHtml(content) + '</pre>' +
'</div>';
skillsList.appendChild(item);
});
// Add create button at bottom
var addDiv = document.createElement('div');
addDiv.className = 'mcp-add-server';
addDiv.innerHTML = '<button class="btn outlined" onclick="showSkillAddForm()">+ Create skill</button>';
skillsList.appendChild(addDiv);
}
function showSkillAddForm() {
document.getElementById('skillsList').style.display = 'none';
document.getElementById('skillsMarketplace').style.display = 'none';
document.getElementById('skillAddForm').style.display = 'block';
// Clear form
document.getElementById('skillName').value = '';
document.getElementById('skillDescription').value = '';
document.getElementById('skillContent').value = '';
document.getElementById('skillName').disabled = false;
}
function hideSkillAddForm() {
document.getElementById('skillsList').style.display = '';
document.getElementById('skillsMarketplace').style.display = 'block';
document.getElementById('skillAddForm').style.display = 'none';
loadInstalledSkills();
}
function saveSkill() {
var name = document.getElementById('skillName').value.trim();
var description = document.getElementById('skillDescription').value.trim();
var scope = document.getElementById('skillScope').value;
var content = document.getElementById('skillContent').value;
if (!name) return;
// Build SKILL.md content
var skillMd = '---\\n';
skillMd += 'name: ' + name + '\\n';
if (description) {
skillMd += 'description: ' + description + '\\n';
}
skillMd += '---\\n\\n';
skillMd += content || '';
vscode.postMessage({
type: 'saveSkill',
name: name,
scope: scope,
content: skillMd
});
hideSkillAddForm();
}
function deleteSkill(name, scope) {
vscode.postMessage({
type: 'deleteSkill',
name: name,
scope: scope
});
}
function searchSkills(query) {
clearTimeout(skillsSearchTimeout);
skillsSearchTimeout = setTimeout(function() {
if (!query || query.length < 2) {
renderFeaturedSkills(topSkills);
return;
}
// Filter featured locally first
var q = query.toLowerCase();
var local = topSkills.filter(function(s) {
return (s.name && s.name.toLowerCase().indexOf(q) >= 0) ||
(s.source && s.source.toLowerCase().indexOf(q) >= 0);
});
if (local.length > 0) {
renderFeaturedSkills(local);
} else {
var grid = document.getElementById('skillsGrid');
grid.innerHTML = '<div class="marketplace-loading">Searching...</div>';
}
// Also search API
vscode.postMessage({ type: 'searchSkills', query: query });
}, 300);
}
function handleSkillsSearchResponse(data) {
var grid = document.getElementById('skillsGrid');
if (!grid) return;
var skills = data.skills || [];
if (skills.length === 0) {
grid.innerHTML = '<div class="marketplace-loading">No skills found.</div>';
return;
}
var html = '';
skills.forEach(function(skill) {
var name = skill.name || skill.skillId || 'Unknown';
var installs = skill.installs || 0;
var source = skill.source || '';
var safeId = escapeHtml(skill.id || name).replace(/'/g, '&#39;');
var installsHtml = installs > 0 ? '<span class="marketplace-item-stars">' + (installs >= 1000 ? (Math.round(installs / 100) / 10) + 'k' : installs) + ' installs</span>' : '';
var rawUrl = skill.rawUrl || '';
var installsText = installs >= 1000 ? (Math.round(installs / 100) / 10) + 'k installs' : (installs > 0 ? installs + ' installs' : '');
html += '<div class="marketplace-item" data-skill-id="' + safeId + '" data-skill-source="' + escapeHtml(source) + '" data-skill-name="' + escapeHtml(name) + '" data-skill-rawurl="' + escapeHtml(rawUrl) + '" data-skill-installs="' + escapeHtml(installsText) + '" onclick="installSkillFromMarketplace(this)">' +
'<div class="marketplace-item-header">' +
'<div class="marketplace-item-icon-placeholder">' + escapeHtml(name.charAt(0).toUpperCase()) + '</div>' +
'<div class="marketplace-item-info">' +
'<div class="marketplace-item-name">' + escapeHtml(name) + '</div>' +
'<div class="marketplace-item-meta">' + installsHtml + '</div>' +
'</div>' +
'</div>' +
'<div class="marketplace-item-desc">' + escapeHtml(source) + '</div>' +
'</div>';
});
grid.innerHTML = html;
}
var skillsDisplayedList = null;
function installSkillFromMarketplace(el) {
var source = el.dataset.skillSource;
var name = el.dataset.skillName;
var installs = el.dataset.skillInstalls || '';
if (!source || !name) return;
var repoUrl = 'https://github.com/' + source.replace(/^github\\//, '');
var installsHtml = installs ? '<span class="marketplace-item-stars">' + installs + '</span>' : '';
var grid = document.getElementById('skillsGrid');
// Save current grid content to restore on back
skillsDisplayedList = grid.innerHTML;
grid.innerHTML = '<div class="marketplace-detail">' +
'<button class="marketplace-back-btn" onclick="backToSkillsList()">&#8592; Back</button>' +
'<div class="marketplace-detail-header">' +
'<div class="marketplace-item-icon-placeholder" style="width:40px;height:40px;font-size:18px;">' + escapeHtml(name.charAt(0).toUpperCase()) + '</div>' +
'<div class="marketplace-detail-header-info">' +
'<div class="marketplace-detail-name">' + escapeHtml(name) + '</div>' +
'<div class="marketplace-detail-header-meta">' +
installsHtml +
'<a href="' + escapeHtml(repoUrl) + '" target="_blank" class="marketplace-detail-link">GitHub</a>' +
'</div>' +
'</div>' +
'</div>' +
'<div class="marketplace-detail-desc">' + escapeHtml('Source: ' + source) + '</div>' +
'<div class="marketplace-detail-config">' +
'<div class="marketplace-detail-section-title">Install to</div>' +
'<div class="form-group" style="margin:0;">' +
'<select id="skillInstallScope">' +
'<option value="project">Project (.claude/skills/)</option>' +
'<option value="global">Global (~/.claude/skills/)</option>' +
'</select>' +
'</div>' +
'</div>' +
'<div class="marketplace-detail-actions" style="margin-top:12px;">' +
'<button class="btn" data-source="' + escapeHtml(source) + '" data-name="' + escapeHtml(name) + '" onclick="confirmSkillInstall(this)">Install</button>' +
'<div style="font-size:11px;color:var(--vscode-descriptionForeground);margin-top:6px;">Opens a terminal running <code style="font-size:10px;">npx skills add</code> via <a href="https://skills.sh" target="_blank" class="marketplace-detail-link">skills.sh</a></div>' +
'</div>' +
'</div>';
}
function backToSkillsList() {
var grid = document.getElementById('skillsGrid');
if (skillsDisplayedList) {
grid.innerHTML = skillsDisplayedList;
} else {
renderFeaturedSkills(topSkills);
}
}
function toggleSkillDetail(id) {
var el = document.getElementById(id);
if (!el) return;
el.style.display = el.style.display === 'none' ? 'block' : 'none';
}
function confirmSkillInstall(btn) {
var source = btn.dataset.source;
var name = btn.dataset.name;
sendStats('Skill installed', { name: name, source: source });
var scope = document.getElementById('skillInstallScope').value;
var repoUrl = 'https://github.com/' + source.replace(/^github\\//, '');
var command = 'npx -y skills add ' + repoUrl + ' --skill ' + name + ' --agent claude-code -y';
if (scope === 'global') {
command += ' --global';
}
vscode.postMessage({
type: 'runTerminalCommand',
command: command
});
hideSkillsModal();
}
`;
export default getSkillsScript;

51
src/skills-ui.ts Normal file
View File

@@ -0,0 +1,51 @@
const getSkillsHtml = () => `
<!-- Skills modal -->
<div id="skillsModal" class="tools-modal" style="display: none;">
<div class="tools-modal-content">
<div class="tools-modal-header">
<span>Skills</span>
<button class="tools-close-btn" onclick="hideSkillsModal()">✕</button>
</div>
<div class="tools-list">
<div class="mcp-servers-list" id="skillsList">
<!-- Installed skills will be loaded here -->
</div>
<div class="mcp-popular-servers" id="skillsMarketplace">
<h4>Search Skills</h4>
<div class="marketplace-search">
<input type="text" id="skillsSearch" placeholder="Search skills..." oninput="searchSkills(this.value)" />
</div>
<div class="marketplace-grid" id="skillsGrid">
</div>
</div>
<div class="mcp-add-form" id="skillAddForm" style="display: none;">
<div class="form-group">
<label for="skillName">Skill Name:</label>
<input type="text" id="skillName" placeholder="my-skill" required>
</div>
<div class="form-group">
<label for="skillDescription">Description:</label>
<input type="text" id="skillDescription" placeholder="What this skill does">
</div>
<div class="form-group">
<label for="skillScope">Scope:</label>
<select id="skillScope">
<option value="personal">Personal (~/.claude/skills/)</option>
<option value="project">Project (.claude/skills/)</option>
</select>
</div>
<div class="form-group">
<label for="skillContent">Instructions (Markdown):</label>
<textarea id="skillContent" placeholder="Instructions for Claude to follow when this skill is invoked..." rows="8"></textarea>
</div>
<div class="form-buttons">
<button class="btn" onclick="saveSkill()">Create Skill</button>
<button class="btn outlined" onclick="hideSkillAddForm()">Cancel</button>
</div>
</div>
</div>
</div>
</div>
`;
export default getSkillsHtml;

479
src/top-mcp-servers.json Normal file
View File

@@ -0,0 +1,479 @@
[
{
"id": "sequential-thinking",
"name": "Sequential Thinking",
"description": "Step-by-step reasoning capabilities",
"icon": "",
"stars": 0,
"url": "",
"installType": "npm",
"installConfig": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-sequential-thinking"
]
},
"featured": true
},
{
"id": "memory",
"name": "Memory",
"description": "Knowledge graph storage",
"icon": "",
"stars": 0,
"url": "",
"installType": "npm",
"installConfig": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-memory"
]
},
"featured": true
},
{
"id": "puppeteer",
"name": "Puppeteer",
"description": "Browser automation",
"icon": "",
"stars": 0,
"url": "",
"installType": "npm",
"installConfig": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-puppeteer"
]
},
"featured": true
},
{
"id": "fetch",
"name": "Fetch",
"description": "HTTP requests & web scraping",
"icon": "",
"stars": 0,
"url": "",
"installType": "npm",
"installConfig": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-fetch"
]
},
"featured": true
},
{
"id": "filesystem",
"name": "Filesystem",
"description": "File operations & management",
"icon": "",
"stars": 0,
"url": "",
"installType": "npm",
"installConfig": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem"
]
},
"featured": true
},
{
"id": "io.github.upstash/context7",
"name": "Context7",
"description": "Up-to-date code docs for any prompt",
"icon": "",
"stars": 0,
"url": "https://github.com/upstash/context7",
"installType": "npm",
"installConfig": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@upstash/context7-mcp"
],
"env": {
"CONTEXT7_API_KEY": ""
}
},
"featured": true
},
{
"id": "com.airtable/mcp",
"name": "Airtable",
"description": "Official Airtable MCP server for managing bases, tables, and records.",
"icon": "",
"stars": 0,
"url": "",
"installType": "http",
"installConfig": {
"type": "http",
"url": "https://mcp.airtable.com/mcp",
"headers": {
"Authorization": ""
}
}
},
{
"id": "com.apify/mcp",
"name": "Apify",
"description": "Extract data from social media, search engines, maps, e-commerce sites, and any website using thousands of ready-made tools from Apify Store.",
"icon": "",
"stars": 0,
"url": "https://github.com/apify/apify-mcp-server",
"installType": "http",
"installConfig": {
"type": "http",
"url": "https://mcp.apify.com"
}
},
{
"id": "io.github.browserbase/mcp-server-browserbase",
"name": "Browserbase",
"description": "MCP server for AI web browser automation using Browserbase and Stagehand",
"icon": "",
"stars": 0,
"url": "https://github.com/browserbase/mcp-server-browserbase",
"installType": "npm",
"installConfig": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@browserbasehq/mcp-server-browserbase"
],
"env": {
"BROWSERBASE_API_KEY": "",
"BROWSERBASE_PROJECT_ID": "",
"GEMINI_API_KEY": ""
}
}
},
{
"id": "io.github.clerk/mcp-server",
"name": "Clerk",
"description": "Access Clerk authentication docs, SDK snippets, and quickstart guides",
"icon": "",
"stars": 0,
"url": "https://clerk.com/docs/guides/ai/mcp/clerk-mcp-server",
"installType": "http",
"installConfig": {
"type": "http",
"url": "https://mcp.clerk.com/mcp"
}
},
{
"id": "com.cloudflare.mcp/mcp",
"name": "Cloudflare",
"description": "Cloudflare MCP servers",
"icon": "",
"stars": 0,
"url": "https://github.com/cloudflare/mcp-server-cloudflare",
"installType": "http",
"installConfig": {
"type": "http",
"url": "https://docs.mcp.cloudflare.com/mcp"
}
},
{
"id": "ai.exa/mcp",
"name": "Exa",
"description": "Web search and code search MCP server powered by Exa",
"icon": "",
"stars": 0,
"url": "https://github.com/exa-labs/exa-mcp-server",
"installType": "http",
"installConfig": {
"type": "http",
"url": "https://mcp.exa.ai/mcp"
}
},
{
"id": "com.figma/mcp",
"name": "Figma",
"description": "Official Figma MCP server for accessing design files, components, and design context",
"icon": "",
"stars": 0,
"url": "https://help.figma.com/hc/en-us/articles/35281350665623-Figma-MCP-collection-How-to-set-up-the-Figma-remote-MCP-server-preferred",
"installType": "http",
"installConfig": {
"type": "http",
"url": "https://mcp.figma.com/mcp"
}
},
{
"id": "dev.firecrawl/mcp",
"name": "Firecrawl",
"description": "Web scraping, crawling, search, and structured data extraction powered by Firecrawl.",
"icon": "",
"stars": 0,
"url": "https://github.com/firecrawl/firecrawl-mcp-server",
"installType": "http",
"installConfig": {
"type": "http",
"url": "https://mcp.firecrawl.dev/v2/mcp",
"headers": {
"Authorization": ""
}
}
},
{
"id": "io.github.github/github-mcp-server",
"name": "GitHub",
"description": "Official GitHub MCP server for repos, issues, PRs, and workflows",
"icon": "",
"stars": 0,
"url": "https://github.com/github/github-mcp-server",
"installType": "http",
"installConfig": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/"
}
},
{
"id": "app.linear/linear",
"name": "Linear",
"description": "MCP server for Linear project management and issue tracking",
"icon": "",
"stars": 0,
"url": "",
"installType": "sse",
"installConfig": {
"type": "sse",
"url": "https://mcp.linear.app/sse"
}
},
{
"id": "com.mux/mcp",
"name": "Mux",
"description": "The official MCP Server for the Mux API",
"icon": "",
"stars": 0,
"url": "https://github.com/muxinc/mux-node-sdk",
"installType": "http",
"installConfig": {
"type": "http",
"url": "https://mcp.mux.com",
"headers": {
"Authorization": ""
}
}
},
{
"id": "com.neon/mcp",
"name": "Neon",
"description": "Official Neon MCP server for managing Neon projects and Postgres databases.",
"icon": "",
"stars": 0,
"url": "https://github.com/neondatabase/mcp-server-neon",
"installType": "http",
"installConfig": {
"type": "http",
"url": "https://mcp.neon.tech/mcp",
"headers": {
"Authorization": "",
"x-read-only": ""
}
}
},
{
"id": "com.netlify/mcp",
"name": "Netlify",
"description": "Netlify's official MCP server for builds, deploys, and project management.",
"icon": "",
"stars": 0,
"url": "https://github.com/netlify/netlify-mcp",
"installType": "npm",
"installConfig": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@netlify/mcp"
],
"env": {
"NETLIFY_PERSONAL_ACCESS_TOKEN": ""
}
}
},
{
"id": "io.github.vercel/next-devtools-mcp",
"name": "Next.js Devtools",
"description": "Next.js development tools MCP server with stdio transport",
"icon": "",
"stars": 0,
"url": "https://github.com/vercel/next-devtools-mcp",
"installType": "npm",
"installConfig": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"next-devtools-mcp"
]
}
},
{
"id": "com.notion/mcp",
"name": "Notion",
"description": "Official Notion MCP server",
"icon": "",
"stars": 0,
"url": "",
"installType": "http",
"installConfig": {
"type": "http",
"url": "https://mcp.notion.com/mcp"
}
},
{
"id": "io.github.railwayapp/mcp-server",
"name": "Railway",
"description": "Official Railway MCP server",
"icon": "",
"stars": 0,
"url": "https://github.com/railwayapp/railway-mcp-server",
"installType": "npm",
"installConfig": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@railway/mcp-server"
]
}
},
{
"id": "com.render/mcp",
"name": "Render",
"description": "Official Render MCP server for managing Render resources.",
"icon": "",
"stars": 0,
"url": "https://github.com/render-oss/render-mcp-server",
"installType": "http",
"installConfig": {
"type": "http",
"url": "https://mcp.render.com/mcp",
"headers": {
"Authorization": ""
}
}
},
{
"id": "com.resend/mcp",
"name": "Resend",
"description": "Official Resend MCP server for email operations and audience management.",
"icon": "",
"stars": 0,
"url": "https://github.com/resend/mcp-send-email",
"installType": "npm",
"installConfig": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"resend-mcp"
],
"env": {
"RESEND_API_KEY": ""
}
}
},
{
"id": "io.sanity.www/mcp",
"name": "Sanity",
"description": "Direct access to your Sanity projects (content, datasets, releases, schemas) and agent rules",
"icon": "",
"stars": 0,
"url": "https://github.com/sanity-io/agent-toolkit",
"installType": "http",
"installConfig": {
"type": "http",
"url": "https://mcp.sanity.io"
}
},
{
"id": "io.github.getsentry/sentry-mcp",
"name": "Sentry",
"description": "MCP server for Sentry issue tracking and debugging",
"icon": "",
"stars": 0,
"url": "https://github.com/getsentry/sentry-mcp",
"installType": "npm",
"installConfig": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@sentry/mcp-server"
],
"env": {
"SENTRY_ACCESS_TOKEN": ""
}
}
},
{
"id": "com.slack/mcp",
"name": "Slack",
"description": "Official Slack MCP server for search, messaging, canvases, and users.",
"icon": "",
"stars": 0,
"url": "https://github.com/slackapi/slack-mcp-plugin",
"installType": "http",
"installConfig": {
"type": "http",
"url": "https://mcp.slack.com/mcp"
}
},
{
"id": "com.stripe/mcp",
"name": "Stripe",
"description": "Official Stripe MCP server for Stripe API tools.",
"icon": "",
"stars": 0,
"url": "https://github.com/stripe/agent-toolkit",
"installType": "http",
"installConfig": {
"type": "http",
"url": "https://mcp.stripe.com"
}
},
{
"id": "com.supabase/mcp",
"name": "Supabase",
"description": "MCP server for interacting with the Supabase platform",
"icon": "",
"stars": 0,
"url": "https://github.com/supabase-community/supabase-mcp",
"installType": "http",
"installConfig": {
"type": "http",
"url": "https://mcp.supabase.com/mcp"
}
},
{
"id": "com.vercel/vercel-mcp",
"name": "Vercel",
"description": "An MCP server for Vercel",
"icon": "",
"stars": 0,
"url": "https://github.com/vercel/vercel-mcp-overview",
"installType": "http",
"installConfig": {
"type": "http",
"url": "https://mcp.vercel.com"
}
}
]

240
src/top-plugins.json Normal file
View File

@@ -0,0 +1,240 @@
[
{
"name": "agent-sdk-dev",
"description": "Claude Agent SDK Development Plugin",
"verified": true,
"type": "official",
"installId": "agent-sdk-dev@claude-plugins-official"
},
{
"name": "claude-code-setup",
"description": "Analyze codebases and recommend tailored Claude Code automations such as hooks, skills, MCP servers, and subagents.",
"verified": true,
"type": "official",
"installId": "claude-code-setup@claude-plugins-official"
},
{
"name": "claude-md-management",
"description": "Tools to maintain and improve CLAUDE.md files - audit quality, capture session learnings, and keep project memory current.",
"verified": true,
"type": "official",
"installId": "claude-md-management@claude-plugins-official"
},
{
"name": "code-review",
"description": "Automated code review for pull requests using multiple specialized agents with confidence-based scoring",
"verified": true,
"type": "official",
"installId": "code-review@claude-plugins-official"
},
{
"name": "code-simplifier",
"description": "Agent that simplifies and refines code for clarity, consistency, and maintainability while preserving functionality",
"verified": true,
"type": "official",
"installId": "code-simplifier@claude-plugins-official"
},
{
"name": "commit-commands",
"description": "Streamline your git workflow with simple commands for committing, pushing, and creating pull requests",
"verified": true,
"type": "official",
"installId": "commit-commands@claude-plugins-official"
},
{
"name": "explanatory-output-style",
"description": "Adds educational insights about implementation choices and codebase patterns (mimics the deprecated Explanatory output style)",
"verified": true,
"type": "official",
"installId": "explanatory-output-style@claude-plugins-official"
},
{
"name": "feature-dev",
"description": "Comprehensive feature development workflow with specialized agents for codebase exploration, architecture design, and quality review",
"verified": true,
"type": "official",
"installId": "feature-dev@claude-plugins-official"
},
{
"name": "frontend-design",
"description": "Frontend design skill for UI/UX implementation",
"verified": true,
"type": "official",
"installId": "frontend-design@claude-plugins-official"
},
{
"name": "hookify",
"description": "Easily create hooks to prevent unwanted behaviors by analyzing conversation patterns",
"verified": true,
"type": "official",
"installId": "hookify@claude-plugins-official"
},
{
"name": "learning-output-style",
"description": "Interactive learning mode that requests meaningful code contributions at decision points (mimics the unshipped Learning output style)",
"verified": true,
"type": "official",
"installId": "learning-output-style@claude-plugins-official"
},
{
"name": "math-olympiad",
"description": "Solve competition math (IMO, Putnam, USAMO) with adversarial verification that catches what self-verification misses. Fresh-context verifiers attack proofs with specific failure patterns. Calibrated abstention over bluffing.",
"verified": true,
"type": "official",
"installId": "math-olympiad@claude-plugins-official"
},
{
"name": "mcp-server-dev",
"description": "Skills for designing and building MCP servers that work seamlessly with Claude \u2014 guides you through deployment models (remote HTTP, MCPB, local), tool design patterns, auth, and interactive MCP apps.",
"verified": true,
"type": "official",
"installId": "mcp-server-dev@claude-plugins-official"
},
{
"name": "playground",
"description": "Creates interactive HTML playgrounds \u2014 self-contained single-file explorers with visual controls, live preview, and prompt output with copy button",
"verified": true,
"type": "official",
"installId": "playground@claude-plugins-official"
},
{
"name": "plugin-dev",
"description": "Plugin development toolkit with skills for creating agents, commands, hooks, MCP integrations, and comprehensive plugin structure guidance",
"verified": true,
"type": "official",
"installId": "plugin-dev@claude-plugins-official"
},
{
"name": "pr-review-toolkit",
"description": "Comprehensive PR review agents specializing in comments, tests, error handling, type design, code quality, and code simplification",
"verified": true,
"type": "official",
"installId": "pr-review-toolkit@claude-plugins-official"
},
{
"name": "ralph-loop",
"description": "Continuous self-referential AI loops for interactive iterative development, implementing the Ralph Wiggum technique. Run Claude in a while-true loop with the same prompt until task completion.",
"verified": true,
"type": "official",
"installId": "ralph-loop@claude-plugins-official"
},
{
"name": "security-guidance",
"description": "Security reminder hook that warns about potential security issues when editing files, including command injection, XSS, and unsafe code patterns",
"verified": true,
"type": "official",
"installId": "security-guidance@claude-plugins-official"
},
{
"name": "skill-creator",
"description": "Create new skills, improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, update or optimize an existing skill, run evals to test a skill, or benchmark skill performance with variance analysis.",
"verified": true,
"type": "official",
"installId": "skill-creator@claude-plugins-official"
},
{
"name": "asana",
"description": "Asana project management integration. Create and manage tasks, search projects, update assignments, track progress, and integrate your development workflow with Asana's work management platform.",
"verified": false,
"type": "external",
"installId": "asana@claude-plugins-official"
},
{
"name": "context7",
"description": "Upstash Context7 MCP server for up-to-date documentation lookup. Pull version-specific documentation and code examples directly from source repositories into your LLM context.",
"verified": false,
"type": "external",
"installId": "context7@claude-plugins-official"
},
{
"name": "discord",
"description": "Discord channel for Claude Code \u2014 messaging bridge with built-in access control. Manage pairing, allowlists, and policy via /discord:access.",
"verified": false,
"type": "external",
"installId": "discord@claude-plugins-official"
},
{
"name": "fakechat",
"description": "Localhost iMessage-style web chat for Claude Code \u2014 test surface with file upload and edits. No tokens, no access control.",
"verified": false,
"type": "external",
"installId": "fakechat@claude-plugins-official"
},
{
"name": "firebase",
"description": "Google Firebase MCP integration. Manage Firestore databases, authentication, cloud functions, hosting, and storage. Build and manage your Firebase backend directly from your development workflow.",
"verified": false,
"type": "external",
"installId": "firebase@claude-plugins-official"
},
{
"name": "github",
"description": "Official GitHub MCP server for repository management. Create issues, manage pull requests, review code, search repositories, and interact with GitHub's full API directly from Claude Code.",
"verified": false,
"type": "external",
"installId": "github@claude-plugins-official"
},
{
"name": "gitlab",
"description": "GitLab DevOps platform integration. Manage repositories, merge requests, CI/CD pipelines, issues, and wikis. Full access to GitLab's comprehensive DevOps lifecycle tools.",
"verified": false,
"type": "external",
"installId": "gitlab@claude-plugins-official"
},
{
"name": "greptile",
"description": "AI code review agent for GitHub and GitLab. View and resolve Greptile's PR review comments directly from Claude Code.",
"verified": false,
"type": "external",
"installId": "greptile@claude-plugins-official"
},
{
"name": "laravel-boost",
"description": "Laravel development toolkit MCP server. Provides intelligent assistance for Laravel applications including Artisan commands, Eloquent queries, routing, migrations, and framework-specific code generation.",
"verified": false,
"type": "external",
"installId": "laravel-boost@claude-plugins-official"
},
{
"name": "linear",
"description": "Linear issue tracking integration. Create issues, manage projects, update statuses, search across workspaces, and streamline your software development workflow with Linear's modern issue tracker.",
"verified": false,
"type": "external",
"installId": "linear@claude-plugins-official"
},
{
"name": "playwright",
"description": "Browser automation and end-to-end testing MCP server by Microsoft. Enables Claude to interact with web pages, take screenshots, fill forms, click elements, and perform automated browser testing workflows.",
"verified": false,
"type": "external",
"installId": "playwright@claude-plugins-official"
},
{
"name": "serena",
"description": "Semantic code analysis MCP server providing intelligent code understanding, refactoring suggestions, and codebase navigation through language server protocol integration.",
"verified": false,
"type": "external",
"installId": "serena@claude-plugins-official"
},
{
"name": "slack",
"description": "Slack workspace integration. Search messages, access channels, read threads, and stay connected with your team's communications while coding. Find relevant discussions and context quickly.",
"verified": false,
"type": "external",
"installId": "slack@claude-plugins-official"
},
{
"name": "supabase",
"description": "Supabase MCP integration for database operations, authentication, storage, and real-time subscriptions. Manage your Supabase projects, run SQL queries, and interact with your backend directly.",
"verified": false,
"type": "external",
"installId": "supabase@claude-plugins-official"
},
{
"name": "telegram",
"description": "Telegram channel for Claude Code \u2014 messaging bridge with built-in access control. Manage pairing, allowlists, and policy via /telegram:access.",
"verified": false,
"type": "external",
"installId": "telegram@claude-plugins-official"
}
]

289
src/top-skills.json Normal file
View File

@@ -0,0 +1,289 @@
[
{
"id": "vercel-labs/skills/find-skills",
"name": "find-skills",
"installs": 654260,
"source": "vercel-labs/skills",
"rawUrl": "https://raw.githubusercontent.com/vercel-labs/skills/main/skills/find-skills/SKILL.md"
},
{
"id": "vercel-labs/agent-skills/vercel-react-best-practices",
"name": "vercel-react-best-practices",
"installs": 234225,
"source": "vercel-labs/agent-skills",
"rawUrl": "https://raw.githubusercontent.com/vercel-labs/agent-skills/main/skills/react-best-practices/SKILL.md"
},
{
"id": "vercel-labs/agent-skills/web-design-guidelines",
"name": "web-design-guidelines",
"installs": 187122,
"source": "vercel-labs/agent-skills",
"rawUrl": "https://raw.githubusercontent.com/vercel-labs/agent-skills/main/skills/web-design-guidelines/SKILL.md"
},
{
"id": "anthropics/skills/frontend-design",
"name": "frontend-design",
"installs": 184608,
"source": "anthropics/skills",
"rawUrl": "https://raw.githubusercontent.com/anthropics/skills/main/skills/frontend-design/SKILL.md"
},
{
"id": "vercel-labs/agent-browser/agent-browser",
"name": "agent-browser",
"installs": 119125,
"source": "vercel-labs/agent-browser",
"rawUrl": "https://raw.githubusercontent.com/vercel-labs/agent-browser/main/skills/agent-browser/SKILL.md"
},
{
"id": "anthropics/skills/skill-creator",
"name": "skill-creator",
"installs": 97605,
"source": "anthropics/skills",
"rawUrl": "https://raw.githubusercontent.com/anthropics/skills/main/skills/skill-creator/SKILL.md"
},
{
"id": "nextlevelbuilder/ui-ux-pro-max-skill/ui-ux-pro-max",
"name": "ui-ux-pro-max",
"installs": 74564,
"source": "nextlevelbuilder/ui-ux-pro-max-skill",
"rawUrl": "https://raw.githubusercontent.com/nextlevelbuilder/ui-ux-pro-max-skill/main/.claude/skills/ui-ux-pro-max/SKILL.md"
},
{
"id": "microsoft/azure-skills/microsoft-foundry",
"name": "microsoft-foundry",
"installs": 74376,
"source": "microsoft/azure-skills",
"rawUrl": "https://raw.githubusercontent.com/microsoft/azure-skills/main/skills/microsoft-foundry/SKILL.md"
},
{
"id": "obra/superpowers/brainstorming",
"name": "brainstorming",
"installs": 66697,
"source": "obra/superpowers",
"rawUrl": "https://raw.githubusercontent.com/obra/superpowers/main/skills/brainstorming/SKILL.md"
},
{
"id": "browser-use/browser-use/browser-use",
"name": "browser-use",
"installs": 52773,
"source": "browser-use/browser-use",
"rawUrl": "https://raw.githubusercontent.com/browser-use/browser-use/main/skills/browser-use/SKILL.md"
},
{
"id": "coreyhaines31/marketingskills/seo-audit",
"name": "seo-audit",
"installs": 50157,
"source": "coreyhaines31/marketingskills",
"rawUrl": "https://raw.githubusercontent.com/coreyhaines31/marketingskills/main/skills/seo-audit/SKILL.md"
},
{
"id": "anthropics/skills/pdf",
"name": "pdf",
"installs": 45709,
"source": "anthropics/skills",
"rawUrl": "https://raw.githubusercontent.com/anthropics/skills/main/skills/pdf/SKILL.md"
},
{
"id": "supabase/agent-skills/supabase-postgres-best-practices",
"name": "supabase-postgres-best-practices",
"installs": 43862,
"source": "supabase/agent-skills",
"rawUrl": "https://raw.githubusercontent.com/supabase/agent-skills/main/skills/supabase-postgres-best-practices/SKILL.md"
},
{
"id": "coreyhaines31/marketingskills/copywriting",
"name": "copywriting",
"installs": 42743,
"source": "coreyhaines31/marketingskills",
"rawUrl": "https://raw.githubusercontent.com/coreyhaines31/marketingskills/main/skills/copywriting/SKILL.md"
},
{
"id": "anthropics/skills/pptx",
"name": "pptx",
"installs": 41526,
"source": "anthropics/skills",
"rawUrl": "https://raw.githubusercontent.com/anthropics/skills/main/skills/pptx/SKILL.md"
},
{
"id": "vercel-labs/next-skills/next-best-practices",
"name": "next-best-practices",
"installs": 40732,
"source": "vercel-labs/next-skills",
"rawUrl": "https://raw.githubusercontent.com/vercel-labs/next-skills/main/skills/next-best-practices/SKILL.md"
},
{
"id": "squirrelscan/skills/audit-website",
"name": "audit-website",
"installs": 37654,
"source": "squirrelscan/skills",
"rawUrl": "https://raw.githubusercontent.com/squirrelscan/skills/main/audit-website/SKILL.md"
},
{
"id": "obra/superpowers/systematic-debugging",
"name": "systematic-debugging",
"installs": 36470,
"source": "obra/superpowers",
"rawUrl": "https://raw.githubusercontent.com/obra/superpowers/main/skills/systematic-debugging/SKILL.md"
},
{
"id": "anthropics/skills/docx",
"name": "docx",
"installs": 35928,
"source": "anthropics/skills",
"rawUrl": "https://raw.githubusercontent.com/anthropics/skills/main/skills/docx/SKILL.md"
},
{
"id": "obra/superpowers/writing-plans",
"name": "writing-plans",
"installs": 35010,
"source": "obra/superpowers",
"rawUrl": "https://raw.githubusercontent.com/obra/superpowers/main/skills/writing-plans/SKILL.md"
},
{
"id": "shadcn/ui/shadcn",
"name": "shadcn",
"installs": 33897,
"source": "shadcn/ui",
"rawUrl": "https://raw.githubusercontent.com/shadcn/ui/main/skills/shadcn/SKILL.md"
},
{
"id": "anthropics/skills/xlsx",
"name": "xlsx",
"installs": 32936,
"source": "anthropics/skills",
"rawUrl": "https://raw.githubusercontent.com/anthropics/skills/main/skills/xlsx/SKILL.md"
},
{
"id": "obra/superpowers/using-superpowers",
"name": "using-superpowers",
"installs": 30937,
"source": "obra/superpowers",
"rawUrl": "https://raw.githubusercontent.com/obra/superpowers/main/skills/using-superpowers/SKILL.md"
},
{
"id": "coreyhaines31/marketingskills/marketing-psychology",
"name": "marketing-psychology",
"installs": 30917,
"source": "coreyhaines31/marketingskills",
"rawUrl": "https://raw.githubusercontent.com/coreyhaines31/marketingskills/main/skills/marketing-psychology/SKILL.md"
},
{
"id": "obra/superpowers/test-driven-development",
"name": "test-driven-development",
"installs": 30410,
"source": "obra/superpowers",
"rawUrl": "https://raw.githubusercontent.com/obra/superpowers/main/skills/test-driven-development/SKILL.md"
},
{
"id": "anthropics/skills/webapp-testing",
"name": "webapp-testing",
"installs": 29748,
"source": "anthropics/skills",
"rawUrl": "https://raw.githubusercontent.com/anthropics/skills/main/skills/webapp-testing/SKILL.md"
},
{
"id": "obra/superpowers/executing-plans",
"name": "executing-plans",
"installs": 28743,
"source": "obra/superpowers",
"rawUrl": "https://raw.githubusercontent.com/obra/superpowers/main/skills/executing-plans/SKILL.md"
},
{
"id": "obra/superpowers/requesting-code-review",
"name": "requesting-code-review",
"installs": 28421,
"source": "obra/superpowers",
"rawUrl": "https://raw.githubusercontent.com/obra/superpowers/main/skills/requesting-code-review/SKILL.md"
},
{
"id": "coreyhaines31/marketingskills/content-strategy",
"name": "content-strategy",
"installs": 27875,
"source": "coreyhaines31/marketingskills",
"rawUrl": "https://raw.githubusercontent.com/coreyhaines31/marketingskills/main/skills/content-strategy/SKILL.md"
},
{
"id": "coreyhaines31/marketingskills/programmatic-seo",
"name": "programmatic-seo",
"installs": 27820,
"source": "coreyhaines31/marketingskills",
"rawUrl": "https://raw.githubusercontent.com/coreyhaines31/marketingskills/main/skills/programmatic-seo/SKILL.md"
},
{
"id": "coreyhaines31/marketingskills/social-content",
"name": "social-content",
"installs": 26700,
"source": "coreyhaines31/marketingskills",
"rawUrl": "https://raw.githubusercontent.com/coreyhaines31/marketingskills/main/skills/social-content/SKILL.md"
},
{
"id": "coreyhaines31/marketingskills/product-marketing-context",
"name": "product-marketing-context",
"installs": 25930,
"source": "coreyhaines31/marketingskills",
"rawUrl": "https://raw.githubusercontent.com/coreyhaines31/marketingskills/main/skills/product-marketing-context/SKILL.md"
},
{
"id": "coreyhaines31/marketingskills/marketing-ideas",
"name": "marketing-ideas",
"installs": 25516,
"source": "coreyhaines31/marketingskills",
"rawUrl": "https://raw.githubusercontent.com/coreyhaines31/marketingskills/main/skills/marketing-ideas/SKILL.md"
},
{
"id": "roin-orca/skills/simple",
"name": "simple",
"installs": 25467,
"source": "roin-orca/skills",
"rawUrl": "https://raw.githubusercontent.com/roin-orca/skills/main/skills/simple/SKILL.md"
},
{
"id": "coreyhaines31/marketingskills/pricing-strategy",
"name": "pricing-strategy",
"installs": 25142,
"source": "coreyhaines31/marketingskills",
"rawUrl": "https://raw.githubusercontent.com/coreyhaines31/marketingskills/main/skills/pricing-strategy/SKILL.md"
},
{
"id": "anthropics/skills/mcp-builder",
"name": "mcp-builder",
"installs": 24764,
"source": "anthropics/skills",
"rawUrl": "https://raw.githubusercontent.com/anthropics/skills/main/skills/mcp-builder/SKILL.md"
},
{
"id": "obra/superpowers/subagent-driven-development",
"name": "subagent-driven-development",
"installs": 24432,
"source": "obra/superpowers",
"rawUrl": "https://raw.githubusercontent.com/obra/superpowers/main/skills/subagent-driven-development/SKILL.md"
},
{
"id": "coreyhaines31/marketingskills/copy-editing",
"name": "copy-editing",
"installs": 24073,
"source": "coreyhaines31/marketingskills",
"rawUrl": "https://raw.githubusercontent.com/coreyhaines31/marketingskills/main/skills/copy-editing/SKILL.md"
},
{
"id": "pbakaus/impeccable/frontend-design",
"name": "frontend-design",
"installs": 23984,
"source": "pbakaus/impeccable",
"rawUrl": "https://raw.githubusercontent.com/pbakaus/impeccable/main/.claude/skills/frontend-design/SKILL.md"
},
{
"id": "pbakaus/impeccable/polish",
"name": "polish",
"installs": 23360,
"source": "pbakaus/impeccable",
"rawUrl": "https://raw.githubusercontent.com/pbakaus/impeccable/main/.claude/skills/polish/SKILL.md"
},
{
"id": "google-labs-code/stitch-skills/design-md",
"name": "design-md",
"installs": 19272,
"source": "google-labs-code/stitch-skills",
"rawUrl": "https://raw.githubusercontent.com/google-labs-code/stitch-skills/main/skills/design-md/SKILL.md"
}
]

File diff suppressed because it is too large Load Diff

2355
src/ui.ts

File diff suppressed because it is too large Load Diff

View File

@@ -6,12 +6,19 @@
"lib": [
"ES2022"
],
"types": ["node", "mocha"],
"sourceMap": true,
"rootDir": "src",
"resolveJsonModule": true,
"strict": true, /* enable all strict type-checking options */
/* Additional Checks */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
}
},
"exclude": [
"mcp-permissions.js",
"claude-code-chat-permissions-mcp",
"backup-files"
]
}