Show Claude skills in inline autocomplete

This commit is contained in:
Mohamed Boudra
2026-06-12 12:28:06 +07:00
parent d84d5f66cd
commit c5da7505da
2 changed files with 102 additions and 1 deletions

View File

@@ -999,6 +999,88 @@ describe("ClaudeAgentSession context window usage", () => {
expect(persistedQueryFactory.mock.calls[0]?.[0].options.persistSession).toBe(true);
});
test("classifies Claude root-only commands separately from inline skills", async () => {
const queryFactory = vi.fn(({ prompt }: { prompt: AsyncIterable<unknown> }) => {
void prompt;
return {
next: async () => ({ done: true, value: undefined }),
interrupt: async () => undefined,
return: async () => undefined,
close: () => undefined,
setPermissionMode: async () => undefined,
setModel: async () => undefined,
supportedModels: async () => [],
supportedCommands: async () => [
{
name: "taste",
description: "Use when another skill needs the shared standard. (user)",
argumentHint: "",
},
{
name: "claude-api",
description: "Build, debug, and optimize Claude API apps with this skill.",
argumentHint: "",
},
{
name: "usage",
description: "Show the total cost and duration of the current session",
argumentHint: "",
},
{
name: "clear",
description: "Start a new session with empty context",
argumentHint: "",
},
],
rewindFiles: async () => ({ canRewind: true }),
[Symbol.asyncIterator]() {
return this;
},
};
});
const client = new ClaudeAgentClient({
logger,
queryFactory,
resolveBinary: async () => "/test/claude/bin",
});
const session = await client.createSession({ provider: "claude", cwd: process.cwd() });
const commands = await session.listCommands();
await session.close();
expect(commands).toEqual([
{
name: "claude-api",
description: "Build, debug, and optimize Claude API apps with this skill.",
argumentHint: "",
kind: "skill",
},
{
name: "clear",
description: "Start a new session with empty context",
argumentHint: "",
kind: "command",
},
{
name: "rewind",
description: "Rewind tracked files to a previous user message",
argumentHint: "[user_message_uuid]",
},
{
name: "taste",
description: "Use when another skill needs the shared standard. (user)",
argumentHint: "",
kind: "skill",
},
{
name: "usage",
description: "Show the total cost and duration of the current session",
argumentHint: "",
kind: "command",
},
]);
});
test("deletes the persisted session jsonl on close when persistSession=false", async () => {
const tmpConfigDir = await fs.mkdtemp(path.join(os.tmpdir(), "paseo-claude-persist-"));
const previousConfigDir = process.env.CLAUDE_CONFIG_DIR;

View File

@@ -302,6 +302,18 @@ const REWIND_COMMAND: AgentSlashCommand = {
description: "Rewind tracked files to a previous user message",
argumentHint: "[user_message_uuid]",
};
const CLAUDE_ROOT_ONLY_COMMANDS = new Set([
"clear",
"compact",
"context",
"debug",
"extra-usage",
"heapdump",
"init",
"loop",
"schedule",
"usage",
]);
const INTERRUPT_TOOL_USE_PLACEHOLDER = "[Request interrupted by user for tool use]";
const INTERRUPT_PLACEHOLDER_PATTERN = /^\[Request interrupted by user(?:[^\]]*)\]$/;
const NO_RESPONSE_REQUESTED_PLACEHOLDER = "No response requested.";
@@ -313,6 +325,13 @@ interface SlashCommandInvocation {
rawInput: string;
}
function classifyClaudeSlashCommand(commandName: string): AgentSlashCommand["kind"] {
// Claude exposes commands and skills as one flat SDK list, without structured source
// metadata. Keep obvious root-only/session controls out of inline autocomplete and
// treat the rest as skills; the worst failure mode is an inert inline suggestion.
return CLAUDE_ROOT_ONLY_COMMANDS.has(commandName) ? "command" : "skill";
}
type ClaudeAgentConfig = AgentSessionConfig & { provider: "claude" };
export interface ClaudeContentChunk {
@@ -2137,7 +2156,7 @@ class ClaudeAgentSession implements AgentSession {
name: cmd.name,
description: cmd.description,
argumentHint: cmd.argumentHint,
kind: "command",
kind: classifyClaudeSlashCommand(cmd.name),
});
}
}