Skip to content

Implement advanced Claude Code skill runtime - #21

Merged
trotsky1997 merged 2 commits into
mainfrom
agent/codex-wsl-gpt5-5/65707555
Jun 1, 2026
Merged

Implement advanced Claude Code skill runtime#21
trotsky1997 merged 2 commits into
mainfrom
agent/codex-wsl-gpt5-5/65707555

Conversation

@trotsky1997

Copy link
Copy Markdown
Owner

Summary

  • add PRD, GitHub issue draft, and OpenSpec change for advanced Claude Code skill runtime semantics
  • preserve advanced skill frontmatter and enforce direct-invocation runtime scope for model, tools, hooks, and forked execution
  • render inline and fenced dynamic shell context before skill prompts reach the model

Tests

  • cargo fmt --check
  • cargo test --test session_runner_slash
  • cargo test --test skills_discover
  • cargo test
  • openspec validate implement-advanced-claude-skills --strict

Closes #20

Co-authored-by: multica-agent <github@multica.ai>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry @trotsky1997, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9d1ca08be7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/session.rs Outdated
.map(|(tool, _)| tool)
.unwrap_or(decl)
.trim();
head == name

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Match Claude-style tool names case-insensitively

When a skill uses Claude Code frontmatter such as allowed-tools: Bash(git status:*) or disallowed-tools: Read, this exact-case comparison does not match Ra's lowercase built-in tool names (bash, read, etc.; see src/tools/core.rs). The parser preserves those Claude-style declarations, so directly invoked skills with an allow list can accidentally hide/deny the intended tool, and deny lists with capitalized tool names will fail to block it. Normalize the declaration head and tool name before comparing.

Useful? React with 👍 / 👎.

Comment thread src/session_runner.rs Outdated
Comment on lines +228 to +231
effective_text = render_slash_template(&template, &cmd.args);
skill_runtime = template.runtime.clone();
if let Some(runtime) = &skill_runtime {
match render_dynamic_shell_context(effective_text, runtime, self.session.cwd())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Render shell context before injecting user arguments

Because argument substitution runs before render_dynamic_shell_context, any direct skill whose body contains $ARGUMENTS (or no placeholders, triggering the ARGUMENTS: fallback) will execute shell syntax supplied by the user, e.g. /review !\touch /tmp/pwned`. That runs locally before the model/tool policy path and bypasses scoped allowed-tools, disallowed-tools, and hook checks, even if the skill author did not include dynamic context in the skill body. Render only the skill-authored template commands before interpolating arguments, or escape arguments so they cannot introduce !`/```! blocks.

Useful? React with 👍 / 👎.

Comment thread src/session.rs Outdated
Comment on lines +572 to +577
let head = decl
.split_once('(')
.map(|(tool, _)| tool)
.unwrap_or(decl)
.trim();
head == name

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Enforce parenthesized tool constraints

For declarations like allowed-tools: "bash(git status:*)", this strips everything after ( and treats it as permission for the entire bash tool. A skill intended to allow only read-only git status commands will advertise and execute unrestricted bash commands for the invocation, so the scoped policy is much broader than the skill declares. Preserve and validate the parenthesized constraint against the tool input instead of widening it to the tool name.

Useful? React with 👍 / 👎.

Comment thread src/session.rs Outdated
let child_messages = self.snapshot_messages().await;
self.restore_messages(parent_snapshot).await;
let outcome = result?;
if let Some(final_text) = final_assistant_text(&child_messages) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Limit fork result selection to child messages

When a forked skill is cancelled or otherwise completes without new assistant text, child_messages still contains the parent snapshot, so final_assistant_text can pick the parent's previous assistant message and append it as the skill result. This duplicates stale parent output even though the fork produced no final answer; select only assistant messages added after parent_snapshot.len() and avoid appending on cancellation/no result.

Useful? React with 👍 / 👎.

Comment thread src/session_runner.rs
Comment on lines +287 to +288
} else if let Some(scope) = scope {
session.prompt_scoped(effective_text, scope).await

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Run skill Stop hooks on successful invocations

For a directly invoked skill that declares a hooks.Stop entry, this scoped prompt path finishes and clears the skill scope before the outer AgentEnd hook runs, and that outer hook only calls self.session.hooks(). As a result, skill-scoped Stop hooks run only on the early UserPromptSubmit block/stop paths above, not on normal completion, so cleanup/audit hooks declared by the skill are silently skipped.

Useful? React with 👍 / 👎.

Co-authored-by: multica-agent <github@multica.ai>
@trotsky1997
trotsky1997 merged commit 418ef14 into main Jun 1, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement advanced Claude Code skills runtime semantics

1 participant