-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat(chat): add skill support and multi-command input for slash commands #466
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 8 commits
a1fe7e5
79a9cd7
d7e89c6
68f5272
074993d
79dc9ba
8c29792
d5fa0e7
d473923
61fcf68
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| # Task 4: Slash Commands & Skills Support — Design Doc | ||
|
|
||
| > **Status: COMPLETED** — PR [#466](https://github.com/siteboon/claudecodeui/pull/466) created at commit `d7e89c6`. | ||
|
|
||
| **Goal:** Port all slash command enhancements from `feature/personal-enhancements` to a single focused PR for upstream. | ||
|
|
||
| **Branch:** `feat/slash-commands-skills` (from `main`) | ||
|
|
||
| **Approach:** Single comprehensive PR. Manual porting (no cherry-pick). | ||
|
|
||
| --- | ||
|
|
||
| ## What Upstream Already Has | ||
|
|
||
| Upstream main has a complete slash command system (built across PRs #211, #392, #374, #402): | ||
|
|
||
| - `server/routes/commands.js` (601 lines) — command discovery from `.claude/commands/`, 8 built-in commands, 3 API endpoints | ||
| - `src/components/chat/hooks/useSlashCommands.ts` (375 lines) — detection, fuzzy search, keyboard nav, `onExecuteCommand` callback | ||
| - `src/components/chat/view/subcomponents/CommandMenu.tsx` (224 lines) — dropdown UI | ||
| - `useChatComposerState.ts` — wired with `executeCommand`, single-command interception on submit | ||
|
|
||
| ## What This PR Adds (3 Enhancements) | ||
|
|
||
| ### Enhancement 1: Skill Discovery & Loading (~200 lines) | ||
|
|
||
| **Files:** | ||
| - `server/routes/commands.js` — Add `scanUserSkills()` for `~/.claude/skills/`, `scanPluginSkills()` for `~/.claude/plugins/`, `isSkill` flag on execute response, security checks for new paths | ||
| - `server/claude-sdk.js` — Add `systemPrompt.append = options.skillContent` (do NOT include `taskOutputFallbackHint` — that belongs to Task 5) | ||
|
|
||
| ### Enhancement 2: Multi-Command Input (~130 lines) | ||
|
|
||
| **Files:** | ||
| - `useChatComposerState.ts` — Replace single-command interception with regex-based multi-command extraction, sequential skill loading, combined skill content, auto-submit with remaining text | ||
|
|
||
| ### Enhancement 3: Command Selection as Autocomplete (~60 lines) | ||
|
|
||
| **Files:** | ||
| - `useSlashCommands.ts` — Remove `onExecuteCommand` parameter, change `selectCommandFromKeyboard` and `handleCommandSelect` to insert command name into input instead of executing immediately | ||
| - `ChatInterface.tsx` — Remove `onExecuteCommand` prop threading (no longer needed) | ||
|
|
||
| ### Supporting: Skill-Loaded Card Rendering | ||
|
|
||
| **Files:** | ||
| - `useChatComposerState.ts` — `setChatMessages` push `{ type: 'skill-loaded', ... }` when skill loads | ||
| - `MessageComponent.tsx` — Add skill-loaded card rendering (purple collapsible card) | ||
|
|
||
| --- | ||
|
|
||
| ## Changes to EXCLUDE | ||
|
|
||
| These appear in the feature branch diff but are NOT slash-command-specific: | ||
|
|
||
| | Change | Reason to exclude | | ||
| |--------|-------------------| | ||
| | Gemini removal (all files) | Upstream feature, must keep | | ||
| | `latestMessage` removal from ChatInterface | Unrelated refactor | | ||
| | `onSessionProcessing` callback → effect | Unrelated behavior change | | ||
| | Scroll-to-bottom interval during loading | UI improvement, unrelated | | ||
| | `taskOutputFallbackHint` in systemPrompt.append | Belongs to Task 5 (Background Tasks) | | ||
| | `CommandMenu.tsx` deletion | Keep upstream's TSX version | | ||
|
|
||
| --- | ||
|
|
||
| ## Implementation Steps | ||
|
|
||
| ### Step 1: Create branch and verify baseline | ||
|
|
||
| ```bash | ||
| git checkout main | ||
| git checkout -b feat/slash-commands-skills | ||
| npm install | ||
| npm run build # verify clean baseline | ||
| ``` | ||
|
|
||
| ### Step 2: Modify `server/routes/commands.js` | ||
|
|
||
| Add skill scanning functions and integrate into endpoints: | ||
|
|
||
| 1. Add `scanUserSkills(skillsDir)` function (~40 lines) — scans `~/.claude/skills/` for `SKILL.md` files | ||
| 2. Add `scanPluginSkills(pluginsDir)` function (~60 lines) — reads `installed_plugins.json`, scans each plugin's `skills/` dir | ||
| 3. Modify `/list` endpoint — call both scan functions, append results to command list | ||
| 4. Modify `/load` endpoint — expand security check to allow `.claude/skills/` and `.claude/plugins/` | ||
| 5. Modify `/execute` endpoint — detect `isSkill` based on path, include in response | ||
|
|
||
|
Comment on lines
+76
to
+84
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Implementation steps should reference security best practices. Step 2 mentions expanding security checks to allow
These details would help ensure the implementation is secure and robust. 📝 Suggested addition to Step 2Add a security considerations subsection: **Security considerations for Step 2:**
- Validate that resolved paths remain within `.claude/skills/` or `.claude/plugins/`
- Reject symbolic links pointing outside allowed directories
- Enforce maximum file size limit (e.g., 100KB per SKILL.md)
- Handle read errors gracefully without exposing system paths🤖 Prompt for AI Agents |
||
| **Source of truth:** `git show feature/personal-enhancements:server/routes/commands.js` | ||
|
|
||
| ### Step 3: Modify `server/claude-sdk.js` | ||
|
|
||
| Add skill content injection into system prompt: | ||
|
|
||
| 1. Find the `sdkOptions` construction block (search for `systemPrompt`) | ||
| 2. Add: `if (options.skillContent) { sdkOptions.systemPrompt.append = options.skillContent; }` | ||
| 3. Do NOT include `taskOutputFallbackHint` (Task 5 specific) | ||
|
|
||
| **Caution:** Upstream did SDK upgrade (#446). Read current main version first to find correct injection point. | ||
|
|
||
| ### Step 4: Modify `useSlashCommands.ts` | ||
|
|
||
| Change command selection from immediate-execute to autocomplete: | ||
|
|
||
| 1. Remove `onExecuteCommand` from `UseSlashCommandsOptions` interface | ||
| 2. Remove `onExecuteCommand` from function parameters | ||
| 3. Remove `isPromiseLike` helper (no longer needed) | ||
| 4. In `selectCommandFromKeyboard`: replace `onExecuteCommand(command)` with input insertion + cursor positioning | ||
| 5. In `handleCommandSelect`: same replacement — insert command name, don't execute | ||
| 6. Remove `selectedProject` null guard from `fetchCommands` (works without project) | ||
| 7. Change `selectedProject.path` to `selectedProject?.path` | ||
|
|
||
| ### Step 5: Modify `useChatComposerState.ts` | ||
|
|
||
| Add skill handling and multi-command parsing: | ||
|
|
||
| 1. Add `pendingSkillContentRef = useRef<string | null>(null)` | ||
| 2. Add to `CommandExecutionResult` interface: `command?`, `metadata?`, `isSkill?`, `userArgs?` | ||
| 3. In `handleCustomCommand`: add skill path — if `isSkill`, show skill-loaded card, store/auto-submit | ||
| 4. In `executeCommand`: pass `userArgs = argsText` to `handleCustomCommand` for custom results | ||
| 5. Replace single-command interception in `handleSubmit` with multi-command regex extraction | ||
| 6. Add `skillContent` to `claude-command` message: `skillContent: pendingSkillContentRef.current` | ||
| 7. Remove `onExecuteCommand` from `useSlashCommands` call | ||
| 8. **Do NOT remove** `geminiModel`, `onSessionProcessing`, `latestMessage` — those are unrelated | ||
|
|
||
| ### Step 6: Modify `ChatInterface.tsx` | ||
|
|
||
| Remove `onExecuteCommand` prop threading: | ||
|
|
||
| 1. Remove `onExecuteCommand` from the props passed to `useChatComposerState` (it no longer exists) | ||
| 2. **Keep** all Gemini props, `latestMessage`, `onSessionProcessing` — don't touch unrelated code | ||
|
|
||
| ### Step 7: Add skill-loaded card in `MessageComponent.tsx` | ||
|
|
||
| Add rendering for `message.type === 'skill-loaded'`: | ||
|
|
||
| 1. Add purple collapsible card between system-injected and user message blocks | ||
| 2. Show skill name, description, and content on expand | ||
|
|
||
| ### Step 8: Build, verify, commit | ||
|
|
||
| ```bash | ||
| npm run build | ||
| npm run typecheck | ||
| # Verify no Gemini code removed, no unrelated changes | ||
| git diff --stat HEAD | ||
| git add <specific files> | ||
| git commit -m "feat(chat): add skill support and multi-command input for slash commands" | ||
| git push -u origin feat/slash-commands-skills | ||
| gh pr create --repo siteboon/claudecodeui ... | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Checklist Per PR Rules | ||
|
|
||
| - [ ] Branch based on latest `main` | ||
| - [ ] No `debug:` commits | ||
| - [ ] No cross-feature changes (no Gemini removal, no scroll fixes, no background task code) | ||
| - [ ] `npm run build` passes | ||
| - [ ] `npm run typecheck` passes | ||
| - [ ] Commit messages follow Conventional Commits | ||
| - [ ] No `Co-Authored-By` lines | ||
|
|
||
|
Comment on lines
+151
to
+160
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Checklist differs from main contribution plan. This checklist omits the "emoji-generated lines" constraint that appears in the main contribution plan document ( 🔄 Align with main plan checklist-- [ ] No `Co-Authored-By` lines
+- [ ] No `Co-Authored-By` or emoji-generated lines in commits🤖 Prompt for AI Agents |
||
| --- | ||
|
|
||
| ## Conflict Risk Assessment | ||
|
|
||
| | File | Risk | Strategy | | ||
| |------|------|----------| | ||
| | `commands.js` | Low | Pure additions, no upstream changes | | ||
| | `useSlashCommands.ts` | Low | Interface change + behavior change | | ||
| | `useChatComposerState.ts` | Medium | Large logic changes, must preserve Gemini code | | ||
| | `claude-sdk.js` | High | SDK upgrade changed structure — read current version carefully | | ||
| | `ChatInterface.tsx` | Medium | Component refactored in #402 — adapt to new structure | | ||
| | `MessageComponent.tsx` | Low | Adding new block (skill-loaded card) | | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| # Upstream PR Contribution Design | ||
|
|
||
| Date: 2026-03-01 | ||
|
|
||
| ## Goal | ||
|
|
||
| Split the `feature/personal-enhancements` branch (32 commits ahead of `main`) into independent, focused PRs for the upstream repository `siteboon/claudecodeui`. | ||
|
|
||
| ## Context | ||
|
|
||
| ### Repository Structure | ||
|
|
||
| | Remote | URL | Purpose | | ||
| |--------|-----|---------| | ||
| | `origin` | `github.com/shikihane/claudecodeui` | Personal fork | | ||
| | `upstream` | `github.com/siteboon/claudecodeui` | Main repository | | ||
|
|
||
| ### Current State (2026-03-01) | ||
|
|
||
| - Local `main` at `597e9c5`, behind upstream/main by 19 commits (upstream at v1.21.0) | ||
| - `feature/personal-enhancements` has 32 commits on top of local main | ||
| - 4 unstaged working tree changes | ||
| - Upstream merged a major refactor (#402: Settings, FileTree, GitPanel, Shell, CodeEditor) plus Gemini integration | ||
|
|
||
| ### Conflict Analysis | ||
|
|
||
| - **18 files** modified by both feature branch and upstream | ||
| - **27 files** modified only by feature branch (no upstream conflict) | ||
| - High-conflict files: `server/claude-sdk.js`, `server/index.js`, `ChatInterface.tsx`, `useChatRealtimeHandlers.ts` | ||
|
|
||
| ## Design: Per-Feature Branch Strategy | ||
|
|
||
| ### Step 1: Sync local main | ||
|
|
||
| Merge `upstream/main` into local `main` and push to `origin/main`. This gives all feature branches a clean, up-to-date base. | ||
|
|
||
| ### Step 2: Feature Modules to Extract | ||
|
|
||
| Each module becomes an independent branch off `main` and a separate PR. | ||
|
|
||
| #### PR 1: Background Tasks Management (largest, highest value) | ||
|
|
||
| **Scope**: Full background task lifecycle — spawn, monitor, display, complete. | ||
|
|
||
| New files (no conflict): | ||
| - `server/ws-clients.js` — WebSocket broadcast helpers | ||
| - `src/components/app/BackgroundTasksPage.tsx` — full-page task view | ||
| - `src/components/app/BackgroundTasksPopover.tsx` — popover task list | ||
| - `src/i18n/locales/*/backgroundTasks.json` — i18n (4 languages) | ||
|
|
||
| Files requiring upstream adaptation: | ||
| - `server/claude-sdk.js` — background task spawning, subagent monitoring, fd-based polling | ||
| - `server/index.js` — route registration for background task endpoints | ||
| - `server/routes/commands.js` — task status/output API endpoints | ||
| - `src/components/chat/hooks/useChatRealtimeHandlers.ts` — WS event handling for task updates | ||
| - `src/components/app/AppContent.tsx` — routing for background tasks page | ||
| - `src/components/sidebar/view/subcomponents/SidebarContent.tsx` — sidebar nav entry | ||
|
|
||
| Commits to include (squashed, excluding debug): `c73489f`, `c762977`, `4ff0d53`, `48787e3`, `f5b3aab`, `be64cfc`, `52b74d0`, `532a285`, `5f1d3b9`, `4d7fe5d` | ||
|
|
||
| #### PR 2: Slash Commands & Skills Support | ||
|
|
||
| **Scope**: Enable slash command/skill detection, loading, and execution in chat input. | ||
|
|
||
| Files: | ||
| - `src/components/chat/hooks/useSlashCommands.ts` | ||
| - `src/components/chat/hooks/useChatComposerState.ts` | ||
| - `src/components/chat/view/ChatInterface.tsx` | ||
| - `server/claude-sdk.js` (appendSystemPrompt integration) | ||
|
|
||
| Commits: `547ee5e`, `3966f81`, `9479876`, `6466464`, `c64617d`, `1e685ec`, `abee2a0` | ||
|
|
||
| #### PR 3: WebSocket Permission Request Fixes | ||
|
|
||
| **Scope**: Fix permission requests lost on WebSocket reconnection. | ||
|
|
||
| Files: | ||
| - `src/contexts/WebSocketContext.tsx` | ||
| - `server/claude-sdk.js` (server-side permission state) | ||
| - `src/components/chat/hooks/useChatRealtimeHandlers.ts` | ||
|
|
||
| Commits: `42c8437`, `589af13`, `4a6b04e` | ||
|
|
||
| #### PR 4: React 18 Message Sync Fixes | ||
|
|
||
| **Scope**: Fix message disappearing and Thinking indicator stuck due to React 18 batching. | ||
|
|
||
| Files: | ||
| - `src/components/chat/hooks/useChatSessionState.ts` | ||
| - `src/components/chat/view/subcomponents/MessageComponent.tsx` | ||
| - `src/components/chat/utils/messageTransforms.ts` | ||
|
|
||
| Commits: `9a84153`, `f71a2f8`, `ceaa704`, `c9fa0fc`, `2ff419e` | ||
|
|
||
| #### PR 5: System-Injected Message Display (optional, low priority) | ||
|
|
||
| **Scope**: Collapsible UI for system-injected messages. | ||
|
|
||
| Commits: `eaaf3ac` | ||
|
|
||
| #### PR 6: Tool Display Refactor (optional, low priority) | ||
|
|
||
| **Scope**: Extract `getToolInputSummary` helper. | ||
|
|
||
| Commits: `631f3f7` | ||
|
|
||
| ### Step 3: Per-Branch Workflow | ||
|
|
||
| For each feature branch: | ||
|
|
||
| 1. `git checkout -b feat/<name> main` | ||
| 2. Cherry-pick or manually port relevant commits | ||
| 3. Remove all `debug:` commits | ||
| 4. Squash related fix commits into clean logical commits | ||
| 5. Resolve conflicts against upstream's latest code | ||
| 6. Verify `npm run build` passes | ||
| 7. Push to `origin` and open PR against `upstream/main` | ||
| 8. Add screenshots for UI changes per CONTRIBUTING.md | ||
|
|
||
| ### Commit Exclusions | ||
|
|
||
| The following commits are debug-only and should NOT appear in any PR: | ||
| - `85c9577` debug: add logging to trace background bash monitoring | ||
| - `087e465` debug: add logging to trace subagent progress input data | ||
| - `b375752` debug: add console logs for permission request handling | ||
| - `b64dec5` debug: add logs to PermissionRequestsBanner component | ||
| - `e019dc2` fix: add detailed logging for permission request flow | ||
|
|
||
| ### Priority Order | ||
|
|
||
| 1. **PR 4** (React 18 Message Sync) — smallest scope, highest chance of clean merge | ||
| 2. **PR 3** (Permission Requests) — focused fix, moderate conflict | ||
| 3. **PR 6** (Tool Display Refactor) — single commit, trivial | ||
| 4. **PR 2** (Slash Commands) — medium scope | ||
| 5. **PR 1** (Background Tasks) — largest scope, most conflict, most value | ||
| 6. **PR 5** (System Messages) — optional | ||
|
|
||
| ## Success Criteria | ||
|
|
||
| - Each PR passes `npm run build` independently | ||
| - Each PR contains only related changes (no cross-feature leakage) | ||
| - No debug logging commits in any PR | ||
| - All commits follow Conventional Commits format | ||
| - UI change PRs include screenshots |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider documenting security measures for skill content injection.
Enhancement 1 introduces scanning and loading of
SKILL.mdfiles from~/.claude/skills/and~/.claude/plugins/, with the content being injected into the system prompt viasystemPrompt.append. However, the design doc does not mention validation, sanitization, or security checks on the skill content before injection.While the files are read from user-controlled directories (which may imply trust), it would be valuable to document:
SKILL.mdfilesThis is especially important since the injected content becomes part of the system prompt that influences AI behavior.
🤖 Prompt for AI Agents