fix: rely on optimistic removal instead of stale backend response

The DELETE endpoint was returning data that somehow rendered as empty
rows in the webview. Simplify the approach:

- Webview optimistically removes the skill from its signal immediately
- Extension fires DELETE to the backend (deletes from disk + cache)
- Extension invalidates cachedSkillsMessage so next tab visit fetches
  fresh data
- No second skillsLoaded message is sent after removal, avoiding the
  stale data overwrite that caused the UI corruption
This commit is contained in:
Mark IJbema
2026-03-16 13:04:07 +01:00
parent f998ee32dc
commit 9507b01c4c
5 changed files with 15 additions and 44 deletions
+5 -9
View File
@@ -1133,23 +1133,19 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
}
/**
* Remove a skill via the CLI backend (deletes from disk + clears cache).
* The DELETE endpoint returns the updated skills list, which we send directly to the webview.
* Remove a skill via the CLI backend (deletes from disk + clears cache), then refresh.
* The webview optimistically removes the skill from its list before this runs.
*/
private async handleRemoveSkill(location: string): Promise<void> {
if (!this.client) return
try {
const dir = this.getWorkspaceDirectory()
const { data: skills } = await this.client.kilocode.removeSkill(
{ location, directory: dir },
{ throwOnError: true },
)
const message = { type: "skillsLoaded", skills }
this.cachedSkillsMessage = message
this.postMessage(message)
await this.client.kilocode.removeSkill({ location, directory: dir })
} catch (error) {
console.error("[Kilo New] KiloProvider: Failed to remove skill:", error)
}
// Invalidate cache so next requestSkills fetches fresh data
this.cachedSkillsMessage = null
}
/**
@@ -13,15 +13,14 @@ export const KilocodeRoutes = lazy(() =>
"/skill",
describeRoute({
summary: "Remove a skill",
description:
"Remove a skill by deleting its directory from disk and clearing it from cache. Returns the updated skills list.",
description: "Remove a skill by deleting its directory from disk and clearing it from cache.",
operationId: "kilocode.removeSkill",
responses: {
200: {
description: "Updated list of skills after removal",
description: "Skill removed",
content: {
"application/json": {
schema: resolver(Skill.Info.array()),
schema: resolver(z.boolean()),
},
},
},
@@ -37,8 +36,7 @@ export const KilocodeRoutes = lazy(() =>
async (c) => {
const { location } = c.req.valid("query")
await Skill.remove(location)
const skills = await Skill.all()
return c.json(skills)
return c.json(true)
},
),
)
+1 -1
View File
@@ -2934,7 +2934,7 @@ export class Kilocode extends HeyApiClient {
/**
* Remove a skill
*
* Remove a skill by deleting its directory from disk and clearing it from cache. Returns the updated skills list.
* Remove a skill by deleting its directory from disk and clearing it from cache.
*/
public removeSkill<ThrowOnError extends boolean = false>(
parameters: {
+2 -7
View File
@@ -4424,14 +4424,9 @@ export type KilocodeRemoveSkillError = KilocodeRemoveSkillErrors[keyof KilocodeR
export type KilocodeRemoveSkillResponses = {
/**
* Updated list of skills after removal
* Skill removed
*/
200: Array<{
name: string
description: string
location: string
content: string
}>
200: boolean
}
export type KilocodeRemoveSkillResponse = KilocodeRemoveSkillResponses[keyof KilocodeRemoveSkillResponses]
+3 -21
View File
@@ -5400,32 +5400,14 @@
}
],
"summary": "Remove a skill",
"description": "Remove a skill by deleting its directory from disk and clearing it from cache. Returns the updated skills list.",
"description": "Remove a skill by deleting its directory from disk and clearing it from cache.",
"responses": {
"200": {
"description": "Updated list of skills after removal",
"description": "Skill removed",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"location": {
"type": "string"
},
"content": {
"type": "string"
}
},
"required": ["name", "description", "location", "content"]
}
"type": "boolean"
}
}
}