Status: COMPLETED — PR #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).
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 endpointssrc/components/chat/hooks/useSlashCommands.ts(375 lines) — detection, fuzzy search, keyboard nav,onExecuteCommandcallbacksrc/components/chat/view/subcomponents/CommandMenu.tsx(224 lines) — dropdown UIuseChatComposerState.ts— wired withexecuteCommand, single-command interception on submit
Files:
server/routes/commands.js— AddscanUserSkills()for~/.claude/skills/,scanPluginSkills()for~/.claude/plugins/,isSkillflag on execute response, security checks for new pathsserver/claude-sdk.js— AddsystemPrompt.append = options.skillContent(do NOT includetaskOutputFallbackHint— that belongs to Task 5)
Files:
useChatComposerState.ts— Replace single-command interception with regex-based multi-command extraction, sequential skill loading, combined skill content, auto-submit with remaining text
Files:
useSlashCommands.ts— RemoveonExecuteCommandparameter, changeselectCommandFromKeyboardandhandleCommandSelectto insert command name into input instead of executing immediatelyChatInterface.tsx— RemoveonExecuteCommandprop threading (no longer needed)
Files:
useChatComposerState.ts—setChatMessagespush{ type: 'skill-loaded', ... }when skill loadsMessageComponent.tsx— Add skill-loaded card rendering (purple collapsible card)
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 |
git checkout main
git checkout -b feat/slash-commands-skills
npm install
npm run build # verify clean baselineAdd skill scanning functions and integrate into endpoints:
- Add
scanUserSkills(skillsDir)function (~40 lines) — scans~/.claude/skills/forSKILL.mdfiles - Add
scanPluginSkills(pluginsDir)function (~60 lines) — readsinstalled_plugins.json, scans each plugin'sskills/dir - Modify
/listendpoint — call both scan functions, append results to command list - Modify
/loadendpoint — expand security check to allow.claude/skills/and.claude/plugins/ - Modify
/executeendpoint — detectisSkillbased on path, include in response
Source of truth: git show feature/personal-enhancements:server/routes/commands.js
Add skill content injection into system prompt:
- Find the
sdkOptionsconstruction block (search forsystemPrompt) - Add:
if (options.skillContent) { sdkOptions.systemPrompt.append = options.skillContent; } - Do NOT include
taskOutputFallbackHint(Task 5 specific)
Caution: Upstream did SDK upgrade (#446). Read current main version first to find correct injection point.
Change command selection from immediate-execute to autocomplete:
- Remove
onExecuteCommandfromUseSlashCommandsOptionsinterface - Remove
onExecuteCommandfrom function parameters - Remove
isPromiseLikehelper (no longer needed) - In
selectCommandFromKeyboard: replaceonExecuteCommand(command)with input insertion + cursor positioning - In
handleCommandSelect: same replacement — insert command name, don't execute - Remove
selectedProjectnull guard fromfetchCommands(works without project) - Change
selectedProject.pathtoselectedProject?.path
Add skill handling and multi-command parsing:
- Add
pendingSkillContentRef = useRef<string | null>(null) - Add to
CommandExecutionResultinterface:command?,metadata?,isSkill?,userArgs? - In
handleCustomCommand: add skill path — ifisSkill, show skill-loaded card, store/auto-submit - In
executeCommand: passuserArgs = argsTexttohandleCustomCommandfor custom results - Replace single-command interception in
handleSubmitwith multi-command regex extraction - Add
skillContenttoclaude-commandmessage:skillContent: pendingSkillContentRef.current - Remove
onExecuteCommandfromuseSlashCommandscall - Do NOT remove
geminiModel,onSessionProcessing,latestMessage— those are unrelated
Remove onExecuteCommand prop threading:
- Remove
onExecuteCommandfrom the props passed touseChatComposerState(it no longer exists) - Keep all Gemini props,
latestMessage,onSessionProcessing— don't touch unrelated code
Add rendering for message.type === 'skill-loaded':
- Add purple collapsible card between system-injected and user message blocks
- Show skill name, description, and content on expand
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 ...- Branch based on latest
main - No
debug:commits - No cross-feature changes (no Gemini removal, no scroll fixes, no background task code)
-
npm run buildpasses -
npm run typecheckpasses - Commit messages follow Conventional Commits
- No
Co-Authored-Bylines
| 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) |