feat: add commands/ to skill paths and preloadSkills to sendMessage#9
Conversation
Extends skill discovery to include .claude/commands/ and .agents/commands/ directories (both project-local and global), so the skill tool can find slash command files that Superset users define as skills. Also adds preloadSkills?: string[] to harness.sendMessage(). When provided, the harness prepends an instruction for the agent to call the skill tool for each listed skill before responding to the user's message. This allows callers to deterministically trigger skill loading as the agent's first action — surfacing visible skill tool call blocks in the UI immediately on submission, before the LLM reply appears.
WalkthroughThe changes expand the filesystem locations scanned for skills to include four new base directories ( Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/core/src/harness/harness.ts`:
- Around line 1327-1332: Validate and sanitize preloadSkills before
interpolating into effectiveContent: ensure each entry in the preloadSkills
array matches the canonical skill-name pattern (lowercase letters/numbers and
single hyphens, 1–64 chars, no leading/trailing/consecutive hyphens) or,
alternatively, confirm each name exists in the workspace skill registry, then
use only the validated list when building effectiveContent; perform this check
right before constructing effectiveContent (referencing preloadSkills and
effectiveContent) and either filter out invalid names or throw/return an error
so untrusted strings cannot be injected into the system prompt.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: d4c2f1bf-a3f7-4142-930c-733b4004eced
📒 Files selected for processing (2)
mastracode/src/agents/workspace.tspackages/core/src/harness/harness.ts
| // Prepend skill loading instructions when the caller has requested pre-loading. | ||
| // The agent calls the skill tool for each name before responding, surfacing | ||
| // visible tool call blocks in the UI immediately on submission. | ||
| const effectiveContent = preloadSkills?.length | ||
| ? `<system>The user has invoked the following skills. Before responding to their request, you MUST call the skill tool once for each skill listed here — do not skip or defer this step:\n${preloadSkills.map(s => ` - ${s}`).join('\n')}\n</system>\n\n${content}` | ||
| : content; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check how preloadSkills values are populated upstream
# Look for callers of sendMessage that pass preloadSkills to understand the data source
rg -n --type=ts -C5 'preloadSkills' -g '!packages/core/src/harness/harness.ts'Repository: superset-sh/mastra
Length of output: 44
🏁 Script executed:
# Find the function containing lines 1327-1332 in harness.ts
head -n 1332 packages/core/src/harness/harness.ts | tail -n 50Repository: superset-sh/mastra
Length of output: 2243
🏁 Script executed:
# Search for the function signature that includes preloadSkills parameter
rg -n 'preloadSkills' packages/core/src/harness/harness.ts -B 20 | head -n 100Repository: superset-sh/mastra
Length of output: 2611
🏁 Script executed:
# Check if there are any type definitions or interfaces that define preloadSkills
rg -n 'interface|type.*\{' packages/core/src/harness/harness.ts | head -n 20Repository: superset-sh/mastra
Length of output: 1256
🏁 Script executed:
# Look for skill-related validation or normalization
rg -n 'skill.*validat|skill.*normaliz|validat.*skill' packages/core/src/harness/harness.tsRepository: superset-sh/mastra
Length of output: 44
🏁 Script executed:
# Find all callers of sendMessage to see how preloadSkills is passed
rg -n 'sendMessage' packages/core --type=ts | head -n 50Repository: superset-sh/mastra
Length of output: 1623
🏁 Script executed:
# Look for skill-related type definitions or constants that might constrain names
rg -n 'skill' packages/core/src --type=ts -i | grep -E 'interface|type|const.*=' | head -n 30Repository: superset-sh/mastra
Length of output: 3668
🏁 Script executed:
# Check if there's any validation or constraints on tool/skill names
rg -n 'tool.*name|skill.*name|buildToolsets' packages/core/src/harness/harness.ts | head -n 20Repository: superset-sh/mastra
Length of output: 1362
🏁 Script executed:
# Check the Skill type definition and how skill names are validated/constrained
rg -n 'interface Skill|type Skill' packages/core/src --type=ts -A 10 | head -n 50Repository: superset-sh/mastra
Length of output: 3886
🏁 Script executed:
# Look for how skill names are used or validated in skill resolution
rg -n 'resolveByName|resolveByPath|Skill.*name' packages/core/src/workspace/skills --type=ts -A 3 | head -n 60Repository: superset-sh/mastra
Length of output: 4460
🏁 Script executed:
# Check if preloadSkills parameter is documented with any constraints
rg -n 'preloadSkills' packages/core/src --type=ts -B 5 -A 5Repository: superset-sh/mastra
Length of output: 3066
🏁 Script executed:
# Check if preloadSkills is validated anywhere in the sendMessage function body
sed -n '1278,1400p' packages/core/src/harness/harness.ts | grep -n -C 3 'preloadSkills'Repository: superset-sh/mastra
Length of output: 1264
🏁 Script executed:
# Check if there's any validation or error handling for invalid skill names
rg -n 'preloadSkills' packages/core/src/harness/harness.ts -A 30 | grep -E 'validat|check|throw|error|sanitiz'Repository: superset-sh/mastra
Length of output: 44
Validate preloadSkills names to prevent prompt injection.
The preloadSkills parameter accepts arbitrary strings that are directly interpolated into the system prompt without validation. While legitimate skill names follow a strict pattern (lowercase letters/numbers/hyphens, 1-64 chars, no leading/trailing/consecutive hyphens), the parameter bypasses this validation. An attacker able to influence these values could inject additional instructions into the LLM system prompt.
Apply the same validation used for skill metadata: either validate that each name matches the skill name pattern, or verify that each name exists in the workspace skill registry before interpolation.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/core/src/harness/harness.ts` around lines 1327 - 1332, Validate and
sanitize preloadSkills before interpolating into effectiveContent: ensure each
entry in the preloadSkills array matches the canonical skill-name pattern
(lowercase letters/numbers and single hyphens, 1–64 chars, no
leading/trailing/consecutive hyphens) or, alternatively, confirm each name
exists in the workspace skill registry, then use only the validated list when
building effectiveContent; perform this check right before constructing
effectiveContent (referencing preloadSkills and effectiveContent) and either
filter out invalid names or throw/return an error so untrusted strings cannot be
injected into the system prompt.
|
Not ready for review yet. This change is meant to allow skills/commands to be mentioned anywhere in the prompt (using the / key menu) and be added to the context prior to the LLM responding. I still have some more testing to do. |
There was a problem hiding this comment.
3 issues found across 2 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/core/src/harness/harness.ts">
<violation number="1" location="packages/core/src/harness/harness.ts:1331">
P1: Internal preload control instructions are injected into persisted user message content, causing prompt leakage and long-lived context pollution.</violation>
</file>
<file name="mastracode/src/agents/workspace.ts">
<violation number="1" location="mastracode/src/agents/workspace.ts:32">
P2: Project-local command paths are computed from process CWD at module load, causing workspace skill/command discovery to ignore request-specific `projectPath` for non-CWD projects.</violation>
<violation number="2" location="mastracode/src/agents/workspace.ts:92">
P2: Added commands paths are treated as skill roots, but core discovery only loads SKILL.md skills, so command markdown files are likely not discovered.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| // The agent calls the skill tool for each name before responding, surfacing | ||
| // visible tool call blocks in the UI immediately on submission. | ||
| const effectiveContent = preloadSkills?.length | ||
| ? `<system>The user has invoked the following skills. Before responding to their request, you MUST call the skill tool once for each skill listed here — do not skip or defer this step:\n${preloadSkills.map(s => ` - ${s}`).join('\n')}\n</system>\n\n${content}` |
There was a problem hiding this comment.
P1: Internal preload control instructions are injected into persisted user message content, causing prompt leakage and long-lived context pollution.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/core/src/harness/harness.ts, line 1331:
<comment>Internal preload control instructions are injected into persisted user message content, causing prompt leakage and long-lived context pollution.</comment>
<file context>
@@ -1316,7 +1324,14 @@ export class Harness<TState = {}> {
+ // The agent calls the skill tool for each name before responding, surfacing
+ // visible tool call blocks in the UI immediately on submission.
+ const effectiveContent = preloadSkills?.length
+ ? `<system>The user has invoked the following skills. Before responding to their request, you MUST call the skill tool once for each skill listed here — do not skip or defer this step:\n${preloadSkills.map(s => ` - ${s}`).join('\n')}\n</system>\n\n${content}`
+ : content;
+
</file context>
|
|
||
| const claudeLocalSkillsPath = path.join(process.cwd(), '.claude', 'skills'); | ||
|
|
||
| const claudeLocalCommandsPath = path.join(process.cwd(), '.claude', 'commands'); |
There was a problem hiding this comment.
P2: Project-local command paths are computed from process CWD at module load, causing workspace skill/command discovery to ignore request-specific projectPath for non-CWD projects.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At mastracode/src/agents/workspace.ts, line 32:
<comment>Project-local command paths are computed from process CWD at module load, causing workspace skill/command discovery to ignore request-specific `projectPath` for non-CWD projects.</comment>
<file context>
@@ -18,17 +18,29 @@ import { TOOL_NAME_OVERRIDES } from '../tool-names.js';
const claudeLocalSkillsPath = path.join(process.cwd(), '.claude', 'skills');
+const claudeLocalCommandsPath = path.join(process.cwd(), '.claude', 'commands');
+
+const agentsLocalCommandsPath = path.join(process.cwd(), '.agents', 'commands');
</file context>
| export const skillPaths = collectSkillPaths([ | ||
| mastraCodeLocalSkillsPath, | ||
| claudeLocalSkillsPath, | ||
| claudeLocalCommandsPath, |
There was a problem hiding this comment.
P2: Added commands paths are treated as skill roots, but core discovery only loads SKILL.md skills, so command markdown files are likely not discovered.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At mastracode/src/agents/workspace.ts, line 92:
<comment>Added commands paths are treated as skill roots, but core discovery only loads SKILL.md skills, so command markdown files are likely not discovered.</comment>
<file context>
@@ -77,8 +89,12 @@ function collectSkillPaths(skillsDirs: string[]): string[] {
export const skillPaths = collectSkillPaths([
mastraCodeLocalSkillsPath,
claudeLocalSkillsPath,
+ claudeLocalCommandsPath,
+ agentsLocalCommandsPath,
mastraCodeGlobalSkillsPath,
</file context>
…ls before LLM - Add SkillToolCall component (ZapIcon, Skill(name) title, success/error state) - Register SkillToolCall in ToolCallBlock for tool names 'skill' and 'load_skill' - In ChatPaneInterface.handleSend: extract custom command chip names from content, strip leading / from message text, pass names as metadata.skills to sendMessage - Add skills?: string[] to sendMessageInput metadata schema (zod.ts) - Pass preloadSkills to harness.sendMessage in service.ts - Add ChatSendMessageInput.metadata.skills type field - Add docs/skill-preload-feature.md with implementation state and setup instructions Requires superset-sh/mastra#9 for harness.sendMessage preloadSkills support and .claude/commands/ being included in skillPaths.
…ls before LLM - Add SkillToolCall component (ZapIcon, Skill(name) title, success/error state) - Register SkillToolCall in ToolCallBlock for tool names 'skill' and 'load_skill' - In ChatPaneInterface.handleSend: extract custom command chip names from content, strip leading / from message text, pass names as metadata.skills to sendMessage - Add skills?: string[] to sendMessageInput metadata schema (zod.ts) - Pass preloadSkills to harness.sendMessage in service.ts - Add ChatSendMessageInput.metadata.skills type field - Add docs/skill-preload-feature.md with implementation state and setup instructions Requires superset-sh/mastra#9 for harness.sendMessage preloadSkills support and .claude/commands/ being included in skillPaths.
There was a problem hiding this comment.
1 issue found across 2 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="mastracode/src/agents/workspace.ts">
<violation number="1" location="mastracode/src/agents/workspace.ts:32">
P2: Project-local command skill directories are resolved from process CWD instead of the active project path, so command skills may be missed when projectPath != process.cwd().</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
|
|
||
| const claudeLocalSkillsPath = path.join(process.cwd(), '.claude', 'skills'); | ||
|
|
||
| const claudeLocalCommandsPath = path.join(process.cwd(), '.claude', 'commands'); |
There was a problem hiding this comment.
P2: Project-local command skill directories are resolved from process CWD instead of the active project path, so command skills may be missed when projectPath != process.cwd().
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At mastracode/src/agents/workspace.ts, line 32:
<comment>Project-local command skill directories are resolved from process CWD instead of the active project path, so command skills may be missed when projectPath != process.cwd().</comment>
<file context>
@@ -18,17 +18,29 @@ import { TOOL_NAME_OVERRIDES } from '../tool-names.js';
const claudeLocalSkillsPath = path.join(process.cwd(), '.claude', 'skills');
+const claudeLocalCommandsPath = path.join(process.cwd(), '.claude', 'commands');
+
+const agentsLocalCommandsPath = path.join(process.cwd(), '.agents', 'commands');
</file context>
…ls before LLM - Add SkillToolCall component (ZapIcon, Skill(name) title, success/error state) - Register SkillToolCall in ToolCallBlock for tool names 'skill' and 'load_skill' - In ChatPaneInterface.handleSend: extract custom command chip names from content, strip leading / from message text, pass names as metadata.skills to sendMessage - Add skills?: string[] to sendMessageInput metadata schema (zod.ts) - Pass preloadSkills to harness.sendMessage in service.ts - Add ChatSendMessageInput.metadata.skills type field - Add docs/skill-preload-feature.md with implementation state and setup instructions Requires superset-sh/mastra#9 for harness.sendMessage preloadSkills support and .claude/commands/ being included in skillPaths.
Summary
mastracode/src/agents/workspace.ts: Adds.claude/commands/and.agents/commands/(both project-local and global) toskillPathsalongside the existingskills/directories. This lets theskilltool discover files that Superset users have defined as slash commands (.claude/commands/*.md,.agents/commands/*.md).packages/core/src/harness/harness.ts: AddspreloadSkills?: string[]tosendMessage(). When provided, the harness prepends an instruction telling the agent to call theskilltool for each listed name before responding. This surfaces visible skill tool call blocks in the chat UI immediately on submission, before the LLM reply.Context
Superset's chat UI supports
/commandchip nodes embedded in messages (e.g. "please help me /redesign this component"). On submit, the client extracts custom command names and passes them asmetadata.skills. The server forwards these toharness.sendMessage({ ..., preloadSkills: skills }).Without these changes:
skilltool couldn't find.claude/commands/files (only scannedskills/subdirs).Building locally for Superset testing
To restore production packages, revert the
resolutionsback to themastracode-v0.4.0-superset.16GitHub URLs and re-runbun install.Test plan
.claude/commands/redesign.mdin a project/redesignchip embedded (e.g. "help me /redesign this component")Skill(redesign)tool call block appears before the LLM replyskills/directories still work (no regression)/new,/stop,/model) still behave normally