Skip to content
Draft
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 172 additions & 0 deletions docs/plans/2026-03-01-task4-slash-commands-design.md
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)
Comment on lines +26 to +28
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Consider documenting security measures for skill content injection.

Enhancement 1 introduces scanning and loading of SKILL.md files from ~/.claude/skills/ and ~/.claude/plugins/, with the content being injected into the system prompt via systemPrompt.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:

  1. Whether skill content undergoes any validation or sanitization before injection
  2. Size limits or format constraints on SKILL.md files
  3. How the system handles malformed or malicious skill content
  4. Whether the security checks mentioned in Step 2 (line 82) extend beyond path validation to content validation

This is especially important since the injected content becomes part of the system prompt that influences AI behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/plans/2026-03-01-task4-slash-commands-design.md` around lines 26 - 28,
Update the design doc to explicitly document content-security measures for skill
injection: state that scanUserSkills() and scanPluginSkills() must call a new
validateSkillContent()/sanitizeSkillContent() routine before passing text to
systemPrompt.append, describe reasonable constraints (e.g., MAX_SKILL_SIZE
bytes, allowed Markdown subset or schema, max heading/section counts), explain
how malformed or malicious content is handled (reject+log, strip disallowed
constructs, or load in read-only sandbox) and note that security checks beyond
path validation (content validation, size limits, logging, and failure modes)
are required and where they integrate (during scanning and before setting
isSkill on execute responses).


### 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
Copy link
Contributor

Choose a reason for hiding this comment

The 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 .claude/skills/ and .claude/plugins/ paths (line 82), but the design doesn't specify what security validations are performed during scanning. Consider adding guidance on:

  • Path traversal prevention (ensuring SKILL.md files don't reference paths outside allowed directories)
  • Symbolic link handling
  • File size limits during scanning
  • Error handling for inaccessible or malformed files

These details would help ensure the implementation is secure and robust.

📝 Suggested addition to Step 2

Add 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
Verify each finding against the current code and only fix it if needed.

In `@docs/plans/2026-03-01-task4-slash-commands-design.md` around lines 76 - 84,
Add concrete security validations when implementing scanUserSkills(skillsDir)
and scanPluginSkills(pluginsDir): resolve each SKILL.md to an absolute path and
ensure it stays inside the allowed base (prevent path traversal), reject or
carefully validate symlinks (deny links that resolve outside the allowed dir),
enforce a maximum file size (e.g., 100KB) and read files with size checks, and
catch/read errors to return sanitized errors (no system paths). When updating
the /list handler, call both scanUserSkills and scanPluginSkills and merge
results while filtering out any entries that failed validation; for /load expand
the allowlist to include .claude/skills/ and .claude/plugins/ but validate
requested paths by canonicalizing and ensuring they remain within those
directories before loading; for /execute detect isSkill by checking that the
canonicalized path is under the skills or plugins skill dirs and include that
flag in the response. Ensure all path checks use canonicalized/realpath
resolution and never trust raw input paths.

**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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Checklist differs from main contribution plan.

This checklist omits the "emoji-generated lines" constraint that appears in the main contribution plan document (docs/plans/2026-03-01-upstream-pr-contribution.md line 101). For consistency across planning documents, both checklists should use identical wording.

🔄 Align with main plan checklist
-- [ ] No `Co-Authored-By` lines
+- [ ] No `Co-Authored-By` or emoji-generated lines in commits
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/plans/2026-03-01-task4-slash-commands-design.md` around lines 151 - 160,
Update the "Checklist Per PR Rules" section so it includes the same
"emoji-generated lines" constraint text used in the main contribution plan (the
document titled 2026-03-01-upstream-pr-contribution.md) to ensure identical
wording; modify the checklist under the heading "Checklist Per PR Rules" to add
the missing checkbox line exactly matching the main plan's phrasing and
formatting so both documents remain consistent.

---

## 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) |
144 changes: 144 additions & 0 deletions docs/plans/2026-03-01-upstream-pr-contribution-design.md
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
Loading