Repository navigation
feat: add session-aware slash skill commands - #311
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThis change adds unified session command catalogs, slash-command parsing and skill expansion, durable invocation metadata, command-list APIs and tools, stream invalidation events, CLI support, and composer rendering for admitted skill invocations. ChangesSession command catalog and transport
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant Composer
participant SessionAPI
participant SessionManager
participant CommandCatalog
participant TranscriptStream
Composer->>SessionAPI: Request session command catalog
SessionAPI->>SessionManager: Resolve session commands
SessionManager->>CommandCatalog: Build catalog and revision
CommandCatalog-->>SessionManager: Return commands and revision
SessionManager-->>SessionAPI: Return path-free command projection
SessionAPI-->>Composer: Render command menu
TranscriptStream-->>Composer: Emit session_commands_changed
Composer->>SessionAPI: Refresh command catalog
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 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 |
|
React Doctor found 3 new issues in 2 files · 2 errors & 1 warning · score 84 / 100 (Needs work) · 0 fixed · vs Errors
1 warning
Reviewed by React Doctor for commit |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 15
🧹 Nitpick comments (13)
web/src/components/assistant-ui/session-composer.tsx (1)
158-159: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueReplace the arbitrary type and spacing values with design tokens.
gap-[7px],pt-[11px],text-[10.5px], andtext-[9px]are invented design values. The coding guidelines require type and spacing values to come frompackages/ui/src/tokens.cssandDESIGN.md.If these values are unchanged code that this diff only re-indented under the new popover root, treat this as a follow-up rather than a blocker for this PR.
Also applies to: 191-193
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/components/assistant-ui/session-composer.tsx` around lines 158 - 159, Replace the arbitrary Tailwind spacing and typography values in the session composer, including gap-[7px], pt-[11px], text-[10.5px], and text-[9px], with the closest approved design tokens defined in tokens.css and DESIGN.md; preserve the existing layout and styling behavior.Source: Coding guidelines
web/src/components/assistant-ui/session-composer-command-menu.tsx (1)
224-226: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueReuse
COMMAND_TRIGGER_CHARACTERfor thecharprop.Line 40 defines
COMMAND_TRIGGER_CHARACTER = "/"and Line 146 uses it for offset math. Line 225 hardcodes"/"again. If the trigger character changes, the two values can diverge.♻️ Proposed change
- char="/" + char={COMMAND_TRIGGER_CHARACTER}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/components/assistant-ui/session-composer-command-menu.tsx` around lines 224 - 226, Update the char prop on ComposerPrimitive.Unstable_TriggerPopover to use the existing COMMAND_TRIGGER_CHARACTER constant instead of a hardcoded slash, keeping the trigger configuration and offset calculations consistent.internal/transcript/ui_input_messages.go (1)
36-46: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueLog the marshal failure instead of dropping metadata silently.
If
json.Marshalfails, the function returnsniland the message loses its turn ID, message ID, goal, and skill invocations. No signal reaches an operator. The struct holds only strings, ints, and*acp.GoalPromptMeta, so failure is unlikely, but the guidelines require handling every error or documenting a justification.Add a
slogwarning or a short comment that states why the error is safe to ignore.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/transcript/ui_input_messages.go` around lines 36 - 46, Update the JSON marshaling error branch in the surrounding metadata helper to log a concise slog warning before returning nil, preserving the existing return behavior while exposing marshal failures to operators.Source: Coding guidelines
web/src/components/assistant-ui/__tests__/session-thread.test.tsx (1)
1516-1522: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAssert menu closure by test ID instead of by label text.
screen.queryByText("Frontend QA")matches any node with that exact text. The assertion passes for the wrong reason if the label moves outside the menu, and it fails if the inserted token ever renders the label elsewhere. Query the menu container instead.♻️ Proposed change
- expect(screen.queryByText("Frontend QA")).not.toBeInTheDocument(); + await waitFor(() => { + expect(screen.queryByTestId("composer-command-menu")).not.toBeInTheDocument(); + });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/components/assistant-ui/__tests__/session-thread.test.tsx` around lines 1516 - 1522, Update the menu-closure assertion in the session thread test near the existing textarea and draft expectations to query the menu container by its test ID, asserting that this container is absent from the document instead of relying on the “Frontend QA” label text.internal/command/slug.go (1)
16-27: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSimplify the always-true separator condition.
The allowed branch ends with
continue. Execution reaches line 21 only whenisAllowedis false. The conditionunicode.IsSpace(character) || character > unicode.MaxASCII || !isAllowedis therefore always true, and theunicodeimport exists only for it. The current form suggests three distinct cases that do not exist.♻️ Proposed simplification
- if unicode.IsSpace(character) || character > unicode.MaxASCII || !isAllowed { - if builder.Len() > 0 && !wroteSeparator { - builder.WriteByte('-') - wroteSeparator = true - } - } + if builder.Len() > 0 && !wroteSeparator { + builder.WriteByte('-') + wroteSeparator = true + }Remove the now-unused
unicodeimport.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/command/slug.go` around lines 16 - 27, In the slug-building loop, simplify the condition guarding separator insertion after the isAllowed branch continues, since this path only handles disallowed characters. Remove the redundant condition and delete the now-unused unicode import, while preserving the existing separator behavior.internal/command/command_test.go (1)
16-90: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a case for an unqualified skill that collides with a claimed bare name.
BuildCatalogdrops an unqualified skill when its bare name is already claimed by a builtin or an agent (internal/command/catalog.golines 66-72). That precedence branch has no coverage. The current cases only prove builtin-over-agent precedence and qualified-skill duplicates.A regression that removed the
continuewould produce a second/goalor/reviewdescriptor and no existing case would fail.💚 Proposed added case
+ t.Run("Should drop an unqualified skill that collides with a claimed bare name", func(t *testing.T) { + t.Parallel() + catalog, err := commandpkg.BuildCatalog( + commandpkg.DefaultBuiltins(), + []commandpkg.AgentSpec{{Name: "review", SourceID: "codex"}}, + []commandpkg.SkillSpec{{ + Name: "review", Available: true, + Source: commandpkg.Source{Kind: "workspace", Scope: "workspace"}, + }}, + ) + if err != nil { + t.Fatalf("BuildCatalog() error = %v", err) + } + got := make([]string, 0, len(catalog.Commands)) + for _, descriptor := range catalog.Commands { + got = append(got, descriptor.CanonicalToken) + } + want := []string{"/goal", "/review"} + if strings.Join(got, ",") != strings.Join(want, ",") { + t.Fatalf("BuildCatalog() tokens = %#v, want %#v", got, want) + } + if catalog.Commands[1].Lane != commandpkg.LaneAgent { + t.Fatalf("/review lane = %q, want the agent to win the bare name", catalog.Commands[1].Lane) + } + })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/command/command_test.go` around lines 16 - 90, Add a BuildCatalog test case covering an unqualified available skill whose bare name matches an existing builtin or agent, such as “goal” or “review”; assert the catalog succeeds with only the claimed command descriptor and does not include a duplicate skill token. Place the coverage alongside the existing ordering and collision cases, preserving builtin/agent precedence.Source: Path instructions
internal/session/manager_prompt_contract_test.go (1)
1112-1191: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winNew test functions assert inline instead of using
Should …subtests. Both new tests place all assertions directly in the test function body. The repository test conventions requiret.Run("Should …")for every test case. The practical cost is that the firstt.Fatalfaborts the whole function, so later independent assertions never run and never report.
internal/session/manager_prompt_contract_test.go#L1112-L1191: split the four scenarios (operator expansion, authored-text preservation, untrusted-caller rejection, internal-prompt literal handling) into separatet.Run("Should …")blocks, so a failure in the first scenario does not hide the untrusted-ingress checks at lines 1148-1158. Also replace theerr == nilcheck at line 1155 witherrors.Isagainst the sentinel thatvalidateCommandIngressreturns.internal/api/core/session_stream_internal_test.go#L617-L667: wrap the body int.Run("Should emit a revision-only wake frame when the catalog changes", …), and add a second subtest asserting that an unchanged catalog stays silent and returns the same revision.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/session/manager_prompt_contract_test.go` around lines 1112 - 1191, The test in internal/session/manager_prompt_contract_test.go lines 1112-1191 needs four separate t.Run("Should …") subtests for operator expansion/authored-text preservation, untrusted-caller rejection, and internal-prompt literal handling; use errors.Is with the validateCommandIngress sentinel for rejection checks. In internal/api/core/session_stream_internal_test.go lines 617-667, wrap the existing revision-only wake-frame assertions in the named subtest and add a second subtest verifying unchanged catalogs emit no frame and retain the same revision.Sources: Coding guidelines, Path instructions
internal/cli/client_test.go (1)
2408-2412: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse a named subtest for the command-catalog assertion.
Place this check in
t.Run("Should return the session command catalog", ...). This gives the command-catalog invariant a stable failure name.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/client_test.go` around lines 2408 - 2412, Wrap the ListSessionCommands assertion in a named subtest using t.Run("Should return the session command catalog", ...), keeping the existing command-catalog validation and failure behavior unchanged.Source: Path instructions
internal/cli/client_session_commands.go (1)
15-21: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winWrap CLI errors with operation context.
Both paths return raw errors. Add operation-specific context with
%wso callers can identify the failed CLI step while preservingerrors.Isanderrors.As.
internal/cli/client_session_commands.go#L15-L21: wrap thesessionScopedPathanddoJSONerrors.internal/cli/session_commands.go#L22-L32: wrap theclientFromDepsandListSessionCommandserrors.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/client_session_commands.go` around lines 15 - 21, Wrap errors from sessionScopedPath and doJSON in internal/cli/client_session_commands.go#L15-L21 with operation-specific context using %w, preserving errors.Is and errors.As. Also wrap errors from clientFromDeps and ListSessionCommands in internal/cli/session_commands.go#L22-L32 with context identifying each failed CLI operation.Source: Coding guidelines
internal/daemon/native_tool_registry_skills.go (1)
142-149: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueBuild the skill payload once.
Line 143 calls
core.SkillPayloadFromSkill(skill), and Line 148 replaces that value when a command ID is present. Select the payload before building the map.♻️ Proposed refactor
+ commandID := strings.TrimSpace(input.CommandID) + skillPayload := any(core.SkillPayloadFromSkill(skill)) + if commandID != "" { + skillPayload = sourceQualifiedSkillViewPayload(skill) + } payload := map[string]any{ - "skill": core.SkillPayloadFromSkill(skill), + "skill": skillPayload, "content": content, } - if commandID := strings.TrimSpace(input.CommandID); commandID != "" { + if commandID != "" { payload["command_id"] = commandID - payload["skill"] = sourceQualifiedSkillViewPayload(skill) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/daemon/native_tool_registry_skills.go` around lines 142 - 149, In the payload construction flow, select the skill payload once before creating the map: use core.SkillPayloadFromSkill(skill) by default and sourceQualifiedSkillViewPayload(skill) when CommandID is non-empty. Then build the map with the selected payload and only add command_id conditionally, avoiding the current assignment and replacement in native tool registration.internal/api/core/session_stream_transcript.go (1)
136-143: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsolidate the command-refresh cadence into one path and one constant.
Three call sites now hardcode
time.Second. Both streaming loops own acommandRefreshticker, andrefreshTranscriptStreamrepeats the same elapsed-time gate. Because the ticker updatesstate.commandCheckedAt, the gate insiderefreshTranscriptStreamalmost never fires, so the logic is duplicated without effect.Define a named interval constant next to
sessionStreamKeepAliveInterval, and keep the refresh in a single place.Also applies to: 231-232, 283-284
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/api/core/session_stream_transcript.go` around lines 136 - 143, Consolidate command-refresh timing around a single named interval constant defined beside sessionStreamKeepAliveInterval. Replace the hardcoded time.Second values at the streaming-loop ticker sites and in refreshTranscriptStream, then remove the redundant elapsed-time gate from refreshTranscriptStream so command refresh is performed only by the existing commandRefresh ticker path.internal/session/command_parser.go (1)
21-28: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winConsider skipping catalog resolution when the message contains no command token.
preparePromptSkillInvocationsresolves the full command catalog for every non-goal prompt. If catalog resolution fails, the prompt fails, even when the message contains no slash token at all. Catalog resolution depends on workspace resolution and the skills registry, so an unrelated registry error blocks ordinary prompts.A cheap pre-check for a slash token at a whitespace boundary would keep plain prompts working when the catalog is unavailable.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/session/command_parser.go` around lines 21 - 28, Update preparePromptSkillInvocations to detect a slash command token at a whitespace boundary before calling CommandCatalog; return no invocations immediately when the authored message contains no such token. Preserve the existing catalog resolution and ParseSkillInvocations flow for messages that may contain command tokens.internal/session/manager_prompt_submit.go (1)
218-233: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a test for the composed expansion and augmentation order.
promptDispatchMessagenow expands skill invocations before augmentation, so the augmenter receives the expanded prompt instead of the authored prompt. The final dispatched text becomes the augmenter block, then the invoked-skills block, then the authored text.The existing tests cover each stage alone.
internal/daemon/prompt_skills_test.goasserts the augmenter preserves the trailing prompt, and the new command service test assertsExpandpreserves the trailing authored text. Neither test covers the two stages together.Add a case to the canonical prompt dispatch suite that runs both stages and asserts the final ordering and the preserved authored suffix.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/session/manager_prompt_submit.go` around lines 218 - 233, Add an integration case to the canonical prompt dispatch test suite that configures both skill expansion and input augmentation, then assert the augmenter receives expanded content and the final dispatched text orders the augmenter block before the invoked-skills block and authored prompt suffix. Preserve the existing standalone stage tests and verify the authored trailing text remains intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/api/core/handlers_test.go`:
- Around line 62-133: Wrap the test body in internal/api/core/handlers_test.go
lines 62-133 in a t.Run subtest named “Should return a workspace-fenced command
catalog,” preserving all existing setup and assertions inside it. Also wrap the
CLI output test body in internal/cli/session_test.go lines 1253-1287 in a t.Run
subtest named “Should render the unified command catalog,” preserving its
current behavior.
In `@internal/api/core/session_stream_transcript.go`:
- Around line 245-254: Update the command-refresh error handling in the shown
transcript stream flow, plus pushAndStreamSessionTranscript and
refreshTranscriptStream, so writeSessionCommandsChanged failures are logged
without terminating a healthy transcript stream and state.commandRevision
remains unchanged. Apply the same nonfatal or bounded-retry behavior
consistently across all three paths.
In `@internal/daemon/native_tool_session_calls.go`:
- Around line 72-79: Replace untyped failures in
internal/daemon/native_tool_session_calls.go lines 72-79 and
internal/daemon/native_tool_registry_skills.go lines 175-227 with the package’s
typed tool errors: in the session manager path, use ErrToolUnavailable with
ReasonDependencyMissing and wrap CommandCatalog failures with tool context; in
the registry skills path, use ErrToolInvalidInput with ReasonSchemaInvalid for
exclusive-argument and missing-session-scope validation, ErrToolUnavailable with
ReasonDependencyMissing for missing agent or registry dependencies, and
ErrToolNotFound for unknown command_id values.
In `@internal/daemon/prompt_skills_test.go`:
- Around line 282-283: Wrap the assertion body of
TestSessionCommandServiceProjectsAndRevalidatesExactSkillSources in a t.Run
subtest named with the required “Should …” convention. Move the existing test
logic inside that subtest, retain the test’s parallel configuration, and close
the subtest before the outer test function ends.
In `@internal/session/manager_hooks_events_test.go`:
- Around line 71-72: Wrap the scenario in
TestPromptInputHookReconcilesSkillInvocations with a named t.Run subtest
beginning with “Should”, and move t.Parallel() into that subtest. Keep the test
behavior unchanged and follow the required pattern for this test case.
In `@internal/session/manager_managed_input_submit.go`:
- Around line 97-133: Keep stateOwned true through dispatchTurnStart and until
submitPromptInReservedSlot completes successfully, so the deferred cleanup
remains responsible for failed reservation failover. Move stateOwned = false to
immediately after the successful submitPromptInReservedSlot call, preserving
cleanup of currentTurnID, currentPromptMessage, currentPromptMeta, and
currentSkillInvocations on any earlier failure.
In `@internal/session/manager_prompt_contract_test.go`:
- Around line 1210-1219: Update promptCommandServiceStub.Expand to append
invocations to s.expanded without replacing previously recorded entries, so
repeated expansions accumulate. Add and use an expandCalls counter in the stub,
increment it on every Expand call, and assert service.expandCalls equals one
alongside the existing invocation assertion.
In `@internal/skills/registry_command.go`:
- Around line 73-80: Update CommandCandidatesForAgentDefSession around
externalSnapshot and applyDisabledSkillList to use the same effective disabled
list as ForAgentDefSession, including registry- and workspace-level entries,
after merging the resource snapshot and before projectAgentSkillActivation. Do
not limit this step to agent.Skills.Disabled, so external candidates retain
their effective disabled state.
In `@internal/skills/registry_test.go`:
- Around line 2027-2133: Wrap the body of
TestRegistryCommandCandidatesRespectScopeSourceAndActivation in a named t.Run
subtest using the required “Should ...” naming convention. Move t.Parallel()
inside that subtest, preserving the existing test setup and assertions
unchanged.
In `@internal/tools/builtin/skills.go`:
- Around line 85-89: Update the root schema containing the name and command_id
properties to add a oneOf constraint requiring exactly one selector: name or
command_id. Preserve both property definitions and ensure requests with neither
or both fields are rejected.
In `@web/src/components/assistant-ui/session-composer-command-menu.tsx`:
- Around line 170-171: Remove the render-time catalogRef assignment and ref
usage in the adapter definition. Update the categories, categoryItems, and
search closures to read catalog directly; preserve their existing scope-state
behavior. Only memoize the adapter with catalog and scope if
Unstable_TriggerPopover requires stable identity.
- Around line 143-149: Update syncScope in session-composer-command.tsx to guard
triggerOffset before calling input.value.slice; when the offset is negative,
handle the scope consistently as having no valid trigger prefix and return
without deriving scope from unrelated text. Preserve the existing prefix-based
standalone/inline behavior for non-negative offsets.
In `@web/src/components/assistant-ui/session-directive-text.tsx`:
- Line 80: Update the DirectiveToken key in the directive-rendering loop to use
the directive’s offset, ensuring separate occurrences of the same command
receive distinct keys; preserve the existing directive and filtering behavior.
- Around line 23-35: Fix MessageTextFragment so whitespace-only text is emitted
exactly once instead of being rendered as both leading and trailing whitespace;
preserve the existing separation of surrounding whitespace and content for
non-whitespace fragments. Add a canonical session-thread.test.tsx case rendering
adjacent directives and assert that the rendered text exactly matches the
authored spacing.
In `@web/src/systems/session/hooks/session-stream-source.ts`:
- Around line 38-45: Update the EventSource setup in the session stream
initialization to register the recovery handler only once: keep either the
source.onerror assignment or the STREAM_ERROR_EVENT listener registration, and
remove the other so a single error event cannot trigger handleError twice.
---
Nitpick comments:
In `@internal/api/core/session_stream_transcript.go`:
- Around line 136-143: Consolidate command-refresh timing around a single named
interval constant defined beside sessionStreamKeepAliveInterval. Replace the
hardcoded time.Second values at the streaming-loop ticker sites and in
refreshTranscriptStream, then remove the redundant elapsed-time gate from
refreshTranscriptStream so command refresh is performed only by the existing
commandRefresh ticker path.
In `@internal/cli/client_session_commands.go`:
- Around line 15-21: Wrap errors from sessionScopedPath and doJSON in
internal/cli/client_session_commands.go#L15-L21 with operation-specific context
using %w, preserving errors.Is and errors.As. Also wrap errors from
clientFromDeps and ListSessionCommands in
internal/cli/session_commands.go#L22-L32 with context identifying each failed
CLI operation.
In `@internal/cli/client_test.go`:
- Around line 2408-2412: Wrap the ListSessionCommands assertion in a named
subtest using t.Run("Should return the session command catalog", ...), keeping
the existing command-catalog validation and failure behavior unchanged.
In `@internal/command/command_test.go`:
- Around line 16-90: Add a BuildCatalog test case covering an unqualified
available skill whose bare name matches an existing builtin or agent, such as
“goal” or “review”; assert the catalog succeeds with only the claimed command
descriptor and does not include a duplicate skill token. Place the coverage
alongside the existing ordering and collision cases, preserving builtin/agent
precedence.
In `@internal/command/slug.go`:
- Around line 16-27: In the slug-building loop, simplify the condition guarding
separator insertion after the isAllowed branch continues, since this path only
handles disallowed characters. Remove the redundant condition and delete the
now-unused unicode import, while preserving the existing separator behavior.
In `@internal/daemon/native_tool_registry_skills.go`:
- Around line 142-149: In the payload construction flow, select the skill
payload once before creating the map: use core.SkillPayloadFromSkill(skill) by
default and sourceQualifiedSkillViewPayload(skill) when CommandID is non-empty.
Then build the map with the selected payload and only add command_id
conditionally, avoiding the current assignment and replacement in native tool
registration.
In `@internal/session/command_parser.go`:
- Around line 21-28: Update preparePromptSkillInvocations to detect a slash
command token at a whitespace boundary before calling CommandCatalog; return no
invocations immediately when the authored message contains no such token.
Preserve the existing catalog resolution and ParseSkillInvocations flow for
messages that may contain command tokens.
In `@internal/session/manager_prompt_contract_test.go`:
- Around line 1112-1191: The test in
internal/session/manager_prompt_contract_test.go lines 1112-1191 needs four
separate t.Run("Should …") subtests for operator expansion/authored-text
preservation, untrusted-caller rejection, and internal-prompt literal handling;
use errors.Is with the validateCommandIngress sentinel for rejection checks. In
internal/api/core/session_stream_internal_test.go lines 617-667, wrap the
existing revision-only wake-frame assertions in the named subtest and add a
second subtest verifying unchanged catalogs emit no frame and retain the same
revision.
In `@internal/session/manager_prompt_submit.go`:
- Around line 218-233: Add an integration case to the canonical prompt dispatch
test suite that configures both skill expansion and input augmentation, then
assert the augmenter receives expanded content and the final dispatched text
orders the augmenter block before the invoked-skills block and authored prompt
suffix. Preserve the existing standalone stage tests and verify the authored
trailing text remains intact.
In `@internal/transcript/ui_input_messages.go`:
- Around line 36-46: Update the JSON marshaling error branch in the surrounding
metadata helper to log a concise slog warning before returning nil, preserving
the existing return behavior while exposing marshal failures to operators.
In `@web/src/components/assistant-ui/__tests__/session-thread.test.tsx`:
- Around line 1516-1522: Update the menu-closure assertion in the session thread
test near the existing textarea and draft expectations to query the menu
container by its test ID, asserting that this container is absent from the
document instead of relying on the “Frontend QA” label text.
In `@web/src/components/assistant-ui/session-composer-command-menu.tsx`:
- Around line 224-226: Update the char prop on
ComposerPrimitive.Unstable_TriggerPopover to use the existing
COMMAND_TRIGGER_CHARACTER constant instead of a hardcoded slash, keeping the
trigger configuration and offset calculations consistent.
In `@web/src/components/assistant-ui/session-composer.tsx`:
- Around line 158-159: Replace the arbitrary Tailwind spacing and typography
values in the session composer, including gap-[7px], pt-[11px], text-[10.5px],
and text-[9px], with the closest approved design tokens defined in tokens.css
and DESIGN.md; preserve the existing layout and styling behavior.
🪄 Autofix
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 Plus
Run ID: af916c73-97e6-4e6f-a11a-f33f9c6fe529
⛔ Files ignored due to path filters (10)
internal/store/globaldb/schema/migrations/atlas.sumis excluded by!**/*.sum,!**/*.suminternal/tools/builtin/testdata/native-tool-catalog.jsonis excluded by!**/*.jsonopenapi/compozy.jsonis excluded by!**/*.jsonpackages/site/content/docs/cli/session/commands.mdxis excluded by!**/*.mdxpackages/site/content/docs/cli/session/index.mdxis excluded by!**/*.mdxpackages/site/content/docs/cli/session/meta.jsonis excluded by!**/*.jsonskills/compozy/references/native-tools.mdis excluded by!**/*.mdskills/compozy/references/tools-and-skills.mdis excluded by!**/*.mdweb/src/generated/compozy-openapi.d.tsis excluded by!**/generated/**,!**/generated/**,!**/*.d.tsweb/src/systems/session/components/assistant-ui/stories/session-thread.stories.tsxis excluded by!**/*.stories.tsx
📒 Files selected for processing (132)
internal/acp/agent_event_tool.gointernal/api/contract/prompt.gointernal/api/contract/session_commands.gointernal/api/contract/session_transcript.gointernal/api/core/handler_edge_cases_test.gointernal/api/core/handlers_test.gointernal/api/core/session_commands.gointernal/api/core/session_input.gointernal/api/core/session_prompt_dispatch.gointernal/api/core/session_stream_frames.gointernal/api/core/session_stream_internal_test.gointernal/api/core/session_stream_transcript.gointernal/api/core/test_helpers_test.gointernal/api/httpapi/handlers_test.gointernal/api/httpapi/session_routes.gointernal/api/spec/registry_session_runtime.gointernal/api/spec/registry_sessions.gointernal/api/udsapi/handlers_test.gointernal/api/udsapi/session_routes.gointernal/api/udsapi/transport_parity_integration_test.gointernal/cli/automation.gointernal/cli/client_session_commands.gointernal/cli/client_session_types.gointernal/cli/client_test.gointernal/cli/helpers_test.gointernal/cli/resource.gointernal/cli/roles.gointernal/cli/session_command.gointernal/cli/session_commands.gointernal/cli/session_test.gointernal/cli/tool_operator_output.gointernal/command/catalog.gointernal/command/command_test.gointernal/command/instructions.gointernal/command/parser.gointernal/command/slug.gointernal/command/types.gointernal/daemon/boot.gointernal/daemon/hooks_declaration_providers.gointernal/daemon/native_hook_helpers.gointernal/daemon/native_tool_binding_groups.gointernal/daemon/native_tool_network_session_input.gointernal/daemon/native_tool_registry_skills.gointernal/daemon/native_tool_session_calls.gointernal/daemon/native_tools_test.gointernal/daemon/prompt_skills_test.gointernal/daemon/session_commands.gointernal/daemon/session_manager_deps.gointernal/daemon/session_manager_factory.gointernal/session/command_catalog.gointernal/session/command_parser.gointernal/session/inputqueue/mutation.gointernal/session/inputqueue/queue.gointernal/session/interfaces.gointernal/session/manager_busy_input.gointernal/session/manager_busy_input_test.gointernal/session/manager_busy_input_types.gointernal/session/manager_goal_commands.gointernal/session/manager_hooks_events_test.gointernal/session/manager_input_dispatch.gointernal/session/manager_managed_input_submit.gointernal/session/manager_options.gointernal/session/manager_pending_input.gointernal/session/manager_prompt_admission_identity.gointernal/session/manager_prompt_contract_test.gointernal/session/manager_prompt_input_event.gointernal/session/manager_prompt_pump.gointernal/session/manager_prompt_submit.gointernal/session/manager_types.gointernal/session/session.gointernal/session/session_skill_invocations.gointernal/skills/registry.gointernal/skills/registry_command.gointernal/skills/registry_discovery.gointernal/skills/registry_source.gointernal/skills/registry_test.gointernal/skills/types.gointernal/store/globaldb/global_db_session_input_queue.gointernal/store/globaldb/global_db_session_input_queue_mutation.gointernal/store/globaldb/global_db_session_input_queue_scan.gointernal/store/globaldb/global_db_session_input_queue_test.gointernal/store/globaldb/global_db_session_prompt_admission.gointernal/store/globaldb/global_db_session_prompt_admission_queue.gointernal/store/globaldb/global_db_session_prompt_admission_scan.gointernal/store/globaldb/queries/session_input.sqlinternal/store/globaldb/queries/session_prompt_admission.sqlinternal/store/globaldb/schema/definitions/20_sessions.sqlinternal/store/globaldb/schema/migrations/00053_schema.sqlinternal/store/globaldb/sqlcgen/models.gointernal/store/globaldb/sqlcgen/session_input.sql.gointernal/store/globaldb/sqlcgen/session_prompt_admission.sql.gointernal/store/session_input_queue.gointernal/store/session_prompt_admission.gointernal/toolmeta/native_entries.gointernal/toolmeta/registry_test.gointernal/tools/builtin/builtin_test.gointernal/tools/builtin/session_commands.gointernal/tools/builtin/sessions.gointernal/tools/builtin/skills.gointernal/tools/builtin_ids.gointernal/tools/dispatch_test.gointernal/tools/result_limit.gointernal/transcript/agent_event_codec.gointernal/transcript/canonical_payload.gointernal/transcript/transcript_codec_contract_test.gointernal/transcript/transcript_ui_projection_test.gointernal/transcript/ui_input_messages.goweb/src/components/assistant-ui/__tests__/session-thread.test.tsxweb/src/components/assistant-ui/session-composer-command-menu.tsxweb/src/components/assistant-ui/session-composer.tsxweb/src/components/assistant-ui/session-directive-registry.tsweb/src/components/assistant-ui/session-directive-text.tsxweb/src/components/assistant-ui/session-thread.tsxweb/src/components/assistant-ui/session-user-message.tsxweb/src/integrations/tanstack-query/reconcile-installed-extension.tsweb/src/systems/marketplace/hooks/use-marketplace-actions.tsweb/src/systems/os/apps/session/session-window-content.tsxweb/src/systems/os/apps/session/use-session-window-controller.tsxweb/src/systems/session/adapters/__tests__/session-api.test.tsweb/src/systems/session/adapters/session-api.tsweb/src/systems/session/adapters/session-command-api.tsweb/src/systems/session/hooks/__tests__/use-session-commands.test.tsweb/src/systems/session/hooks/__tests__/use-session-live-tail.test.tsxweb/src/systems/session/hooks/session-stream-source.tsweb/src/systems/session/hooks/use-session-commands.tsweb/src/systems/session/hooks/use-session-live-tail.tsweb/src/systems/session/index.tsweb/src/systems/session/lib/__tests__/query-options.test.tsweb/src/systems/session/lib/query-keys.tsweb/src/systems/session/lib/query-options.tsweb/src/systems/session/types.tsweb/src/systems/skill/hooks/use-skill-actions.ts
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
internal/daemon/native_tools_test.go (1)
1635-1635: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winUse the required
Shouldsubtest name.The new
command_listcase runs ascompozy__command_listbecause the loop usest.Run(tc.id.String(), ...). Name each generated case with aShould ...prefix.As per coding guidelines and path instructions,
**/*_test.gorequirest.Run("Should...")for all test cases.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/daemon/native_tools_test.go` at line 1635, Update the test case naming in the loop that calls t.Run(tc.id.String(), ...) so each generated subtest uses a “Should ...” prefix, including the command_list case currently named from tc.id.String(). Keep the existing table-driven structure and adjust only the subtest name generation in the native_tools_test.go test helper.Sources: Coding guidelines, Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/api/core/session_stream_internal_test.go`:
- Around line 704-724: Extend the session stream test around
writeSessionCommandsChanged to cover an unchanged nonempty revision by invoking
it again with second.Revision while manager.catalog stays unchanged. Use the
existing writer, revision, and second.Revision symbols in the test to verify the
call does not emit a new session_commands_changed frame and preserves the
current revision when the catalog has not changed.
---
Outside diff comments:
In `@internal/daemon/native_tools_test.go`:
- Line 1635: Update the test case naming in the loop that calls
t.Run(tc.id.String(), ...) so each generated subtest uses a “Should ...” prefix,
including the command_list case currently named from tc.id.String(). Keep the
existing table-driven structure and adjust only the subtest name generation in
the native_tools_test.go test helper.
🪄 Autofix
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 Plus
Run ID: 286f36d0-80a9-4fed-bc4d-52e22dcdf025
⛔ Files ignored due to path filters (1)
internal/tools/builtin/testdata/native-tool-catalog.jsonis excluded by!**/*.json
📒 Files selected for processing (23)
internal/api/core/handlers_test.gointernal/api/core/session_stream_frames.gointernal/api/core/session_stream_internal_test.gointernal/cli/session_test.gointernal/daemon/native_tool_command_errors.gointernal/daemon/native_tool_registry_skills.gointernal/daemon/native_tool_session_calls.gointernal/daemon/native_tools_test.gointernal/daemon/prompt_skills_test.gointernal/session/manager_hooks_events_test.gointernal/session/manager_managed_input_submit.gointernal/session/manager_prompt_contract_test.gointernal/skills/registry_command.gointernal/skills/registry_test.gointernal/tools/builtin/skills.goweb/src/components/assistant-ui/__tests__/session-thread.test.tsxweb/src/components/assistant-ui/hooks/use-session-composer-state.tsweb/src/components/assistant-ui/session-composer-command-menu.tsxweb/src/components/assistant-ui/session-composer.tsxweb/src/components/assistant-ui/session-directive-text.tsxweb/src/systems/session/hooks/__tests__/use-session-live-tail.test.tsxweb/src/systems/session/hooks/session-stream-source.tsweb/src/systems/session/hooks/use-session-live-tail.ts
💤 Files with no reviewable changes (2)
- web/src/systems/session/hooks/use-session-live-tail.ts
- web/src/systems/session/hooks/session-stream-source.ts
🚧 Files skipped from review as they are similar to previous changes (14)
- internal/api/core/session_stream_frames.go
- internal/daemon/native_tool_registry_skills.go
- internal/cli/session_test.go
- internal/session/manager_hooks_events_test.go
- internal/api/core/handlers_test.go
- internal/daemon/native_tool_session_calls.go
- internal/tools/builtin/skills.go
- internal/session/manager_prompt_contract_test.go
- web/src/components/assistant-ui/tests/session-thread.test.tsx
- web/src/components/assistant-ui/session-directive-text.tsx
- web/src/systems/session/hooks/tests/use-session-live-tail.test.tsx
- internal/session/manager_managed_input_submit.go
- internal/daemon/prompt_skills_test.go
- internal/skills/registry_command.go
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
Too many files changed for review (172 files, 100 file limit). Bypass the limit by tagging |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/daemon/daemon_automation_task_integration_test.go`:
- Around line 321-323: In TestDaemonE2EAutomationTaskBackedJobDelegatesTaskRun,
move the startedRun.SessionID assertion and the related session-type assertion
into separate focused t.Run subtests named with the “Should ...” pattern, while
preserving each assertion’s existing validation and failure message.
🪄 Autofix
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 Plus
Run ID: 5c30e5bb-2274-4f3d-b487-aed2a15cc19e
📒 Files selected for processing (1)
internal/daemon/daemon_automation_task_integration_test.go
79bde5c to
efbc3bc
Compare
Summary
Implements the deferred session command specification and replaces the hand-built slash UI with assistant-ui's native Composer Trigger Popover and Directive Text primitives.
Slash commands now work at the start of a prompt or after whitespace anywhere in the draft. Built-in and ACP control commands remain standalone, while session-effective skill commands can be inserted inline and repeated without losing authored text.
User behavior
/at the start or in the middle of a prompt./goaland ACP commands standalone;/runremains reserved and hidden.Runtime and public surfaces
SkillInvocationRefvalues and UTF-16 authored offsets through admission, busy-input queueing, replay, transcript storage, and UI projection.GET /api/workspaces/{workspace_id}/sessions/{session_id}/commands,compozy session commands,compozy__command_list, and exact-sourcecompozy__skill_viewbehavior.00053and regenerated SQLC, OpenAPI, CLI reference, and TypeScript contracts.Skill lifecycle semantics
The effective catalog respects:
Slash activation is limited to human operator ingress. Hooks may remove admitted invocations but cannot add new ones, and internal/agent-generated prompts do not activate slash text.
Validation
make gate-full: pass on fingerprint3f645d11291888f60fe730323e0a262373a3118f.QA evidence
Two fresh isolated daemon/Web labs cover catalog parity, wrong-workspace fencing, native assistant-ui insertion, adjacent repeated skills, exact Unicode/spacing preservation, live provider injection, cold reload, and the 320 px menu.
docs/qa/reports/2026-08-05-session-slash-commands.mdclean: true, zero survivorsCompozy Impact Audit
compozy__command_list, its sessions toolset descriptor, schemas, dispatch binding, availability coverage, catalog snapshot, and presentation metadata. Exact command IDs can drivecompozy__skill_view; no existing tool ID changed.skills/compozy/references/native-tools.mdandtools-and-skills.mdfor command discovery and exact-source skill behavior.Spec and references
Implements the accepted follow-up plan for
.compozy/tasks/_archived/20260712-004812-d49e8375-goal, using the explicit Synara and Hermes reference paths captured in the archived analysis. The archived spec remains historical and was not modified.Summary by CodeRabbit