mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
fix(native-chat): keep Claude skills tagged as skills after the agent changes folder (#29030)
This commit is contained in:
@@ -57,6 +57,25 @@ describe('claude slash command catalog', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('keeps a project skill a skill after the agent cds out of the launch folder', () => {
|
||||
// Captured on 2.1.280: after `cd` into an --add-dir, `skills` drops the launch folder's
|
||||
// project skills while `slash_commands` still lists them.
|
||||
const catalog = new ClaudeSlashCommandCatalog(
|
||||
init({ cwd: '/repo', slash_commands: ['qa-project'], skills: ['qa-project'] })
|
||||
)
|
||||
catalog.observe(
|
||||
init({
|
||||
cwd: '/scratch',
|
||||
slash_commands: ['qa-project', 'found-here'],
|
||||
skills: ['found-here']
|
||||
})
|
||||
)
|
||||
expect(catalog.commands).toEqual([
|
||||
{ name: 'qa-project', kind: 'skill' },
|
||||
{ name: 'found-here', kind: 'skill' }
|
||||
])
|
||||
})
|
||||
|
||||
it('notices a name that only changed kind', () => {
|
||||
const catalog = new ClaudeSlashCommandCatalog(
|
||||
init({ slash_commands: ['review'], skills: [], terminal_slash_commands: [] })
|
||||
|
||||
@@ -105,6 +105,8 @@ export function readClaudeSlashCommands(
|
||||
export class ClaudeSlashCommandCatalog {
|
||||
private entries: AgentSessionSlashCommand[] | undefined
|
||||
private hasSkillClassification = false
|
||||
/** The folder the first catalog frame reported: the one the session's own `/` list was loaded for. */
|
||||
private launchCwd: string | undefined
|
||||
private hidden = new Set<string>()
|
||||
private commandNames = new Set<string>()
|
||||
private details = new Map<string, CommandDetail>()
|
||||
@@ -136,6 +138,23 @@ export class ClaudeSlashCommandCatalog {
|
||||
return this.entries
|
||||
}
|
||||
|
||||
/** Why: the CLI lists `skills` for the frame's `cwd`, which follows the agent's `cd`, but
|
||||
* `slash_commands` for the session, so only a launch-folder frame may change a known kind. */
|
||||
private classify(message: Record<string, unknown>): AgentSessionSlashCommand[] {
|
||||
const reported = readClaudeSlashCommands(message)
|
||||
const cwd = typeof message.cwd === 'string' ? message.cwd : undefined
|
||||
this.launchCwd ??= cwd
|
||||
if (cwd === undefined || cwd === this.launchCwd) {
|
||||
return reported
|
||||
}
|
||||
const known = new Map(
|
||||
(this.entries ?? [])
|
||||
.filter((entry) => !entry.kindUnspecified)
|
||||
.map((entry) => [entry.name, entry.kind])
|
||||
)
|
||||
return reported.map((entry) => ({ ...entry, kind: known.get(entry.name) ?? entry.kind }))
|
||||
}
|
||||
|
||||
/** Provider row text, carried across the name-only frames that never restate it. */
|
||||
private describe(entries: AgentSessionSlashCommand[]): AgentSessionSlashCommand[] {
|
||||
return entries.map((entry) => ({ ...entry, ...this.details.get(entry.name) }))
|
||||
@@ -147,7 +166,7 @@ export class ClaudeSlashCommandCatalog {
|
||||
if (carriesCommandCatalog(message)) {
|
||||
this.hasSkillClassification = true
|
||||
this.hidden = new Set(names(message.terminal_slash_commands))
|
||||
next = this.describe(readClaudeSlashCommands(message))
|
||||
next = this.describe(this.classify(message))
|
||||
this.commandNames = new Set(
|
||||
next.filter((entry) => entry.kind === 'command').map((entry) => entry.name)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user