Skip to content
This repository was archived by the owner on Apr 17, 2026. It is now read-only.

feat: add commands/ to skill paths and preloadSkills to sendMessage#9

Open
michalkopanski wants to merge 1 commit intosuperset-sh:mainfrom
michalkopanski:mk/skill-preload-and-command-paths
Open

feat: add commands/ to skill paths and preloadSkills to sendMessage#9
michalkopanski wants to merge 1 commit intosuperset-sh:mainfrom
michalkopanski:mk/skill-preload-and-command-paths

Conversation

@michalkopanski
Copy link
Copy Markdown

@michalkopanski michalkopanski commented Apr 1, 2026

Summary

  • mastracode/src/agents/workspace.ts: Adds .claude/commands/ and .agents/commands/ (both project-local and global) to skillPaths alongside the existing skills/ directories. This lets the skill tool discover files that Superset users have defined as slash commands (.claude/commands/*.md, .agents/commands/*.md).

  • packages/core/src/harness/harness.ts: Adds preloadSkills?: string[] to sendMessage(). When provided, the harness prepends an instruction telling the agent to call the skill tool 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 /command chip nodes embedded in messages (e.g. "please help me /redesign this component"). On submit, the client extracts custom command names and passes them as metadata.skills. The server forwards these to harness.sendMessage({ ..., preloadSkills: skills }).

Without these changes:

  1. The skill tool couldn't find .claude/commands/ files (only scanned skills/ subdirs).
  2. There was no way to deterministically trigger skill loading before the LLM saw the user's message.

Building locally for Superset testing

# 1. Clone the fork and check out this branch
git clone https://github.com/superset-sh/mastra.git ~/Sites/mastra
cd ~/Sites/mastra
git checkout mk/skill-preload-and-command-paths

# 2. Install + build
corepack enable
pnpm install
pnpm turbo build --filter="@mastra/core" --filter="mastracode"

# 3. Pack tarballs
mkdir -p /tmp/mastra-local
cd mastracode && pnpm pack --pack-destination /tmp/mastra-local && cd ..
cd packages/core && pnpm pack --pack-destination /tmp/mastra-local && cd ../..

# 4. In super-canopy, update package.json resolutions:
#   "mastracode": "file:/tmp/mastra-local/mastracode-0.10.0-alpha.6.tgz"
#   "@mastra/core": "file:/tmp/mastra-local/mastra-core-1.18.0-alpha.3.tgz"
bun install

To restore production packages, revert the resolutions back to the mastracode-v0.4.0-superset.16 GitHub URLs and re-run bun install.

Test plan

  • Place a markdown file in .claude/commands/redesign.md in a project
  • Send a chat message with a /redesign chip embedded (e.g. "help me /redesign this component")
  • Verify Skill(redesign) tool call block appears before the LLM reply
  • Verify the LLM's response uses the skill content as context
  • Verify existing skills/ directories still work (no regression)
  • Verify built-in commands (/new, /stop, /model) still behave normally

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.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 1, 2026

Walkthrough

The changes expand the filesystem locations scanned for skills to include four new base directories (.claude/commands, .agents/commands, ~/.claude/commands, ~/.agents/commands) and add an optional preloadSkills parameter to the harness message-sending API to inject skill names into the system context before sending messages to the agent.

Changes

Cohort / File(s) Summary
Skill Management Configuration
mastracode/src/agents/workspace.ts, packages/core/src/harness/harness.ts
Extended skill discovery paths to include project-local and global .claude/commands and .agents/commands directories. Added optional preloadSkills parameter to Harness.sendMessage() that prepends a system instruction listing skill names to the user message when provided.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes both main changes: adding commands/ directories to skill paths and introducing preloadSkills parameter to sendMessage.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@michalkopanski michalkopanski marked this pull request as draft April 1, 2026 02:44
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between 06fc880 and efcb180.

📒 Files selected for processing (2)
  • mastracode/src/agents/workspace.ts
  • packages/core/src/harness/harness.ts

Comment on lines +1327 to +1332
// 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;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 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 50

Repository: 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 100

Repository: 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 20

Repository: 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.ts

Repository: 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 50

Repository: 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 30

Repository: 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 20

Repository: 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 50

Repository: 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 60

Repository: 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 5

Repository: 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.

@michalkopanski
Copy link
Copy Markdown
Author

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.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

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}`
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Apr 1, 2026

Choose a reason for hiding this comment

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

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>
Fix with Cubic


const claudeLocalSkillsPath = path.join(process.cwd(), '.claude', 'skills');

const claudeLocalCommandsPath = path.join(process.cwd(), '.claude', 'commands');
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Apr 1, 2026

Choose a reason for hiding this comment

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

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>
Fix with Cubic

export const skillPaths = collectSkillPaths([
mastraCodeLocalSkillsPath,
claudeLocalSkillsPath,
claudeLocalCommandsPath,
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Apr 1, 2026

Choose a reason for hiding this comment

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

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>
Fix with Cubic

michalkopanski added a commit to michalkopanski/superset that referenced this pull request Apr 11, 2026
…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.
@michalkopanski michalkopanski marked this pull request as ready for review April 14, 2026 13:56
michalkopanski added a commit to michalkopanski/superset that referenced this pull request Apr 14, 2026
…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.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

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');
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Apr 14, 2026

Choose a reason for hiding this comment

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

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>
Fix with Cubic

michalkopanski added a commit to michalkopanski/superset that referenced this pull request Apr 17, 2026
…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.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant