mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-24 16:02:55 +08:00
feat(cli): expose command metadata in command-file endpoints
This commit is contained in:
@@ -2,4 +2,4 @@
|
||||
"@kilocode/cli": minor
|
||||
---
|
||||
|
||||
Add kilocode command-file endpoints so clients can list editable command/workflow files and remove them.
|
||||
Add kilocode command-file endpoints so clients can list editable command/workflow files, inspect model and reasoning variant metadata, and remove them.
|
||||
|
||||
+12
@@ -631,8 +631,12 @@ object KiloCliDataParser {
|
||||
CommandInfo(
|
||||
name = obj.str("name") ?: "",
|
||||
description = obj.str("description"),
|
||||
agent = obj.str("agent"),
|
||||
model = obj.str("model"),
|
||||
variant = obj.str("variant"),
|
||||
source = obj.str("source"),
|
||||
hints = obj["hints"]?.jsonArray?.mapNotNull { it.jsonPrimitive.contentOrNull } ?: emptyList(),
|
||||
subtask = obj.flagOrNull("subtask"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -663,9 +667,13 @@ object KiloCliDataParser {
|
||||
CommandDto(
|
||||
name = name,
|
||||
description = obj.str("description"),
|
||||
agent = obj.str("agent"),
|
||||
model = obj.str("model"),
|
||||
variant = obj.str("variant"),
|
||||
source = obj.str("source"),
|
||||
hints = obj["hints"].arr()?.mapNotNull { it.jsonPrimitive.contentOrNull } ?: emptyList(),
|
||||
template = obj.str("template"),
|
||||
subtask = obj.flagOrNull("subtask"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -677,11 +685,15 @@ object KiloCliDataParser {
|
||||
CommandFileDto(
|
||||
name = name,
|
||||
description = obj.str("description"),
|
||||
agent = obj.str("agent"),
|
||||
model = obj.str("model"),
|
||||
variant = obj.str("variant"),
|
||||
source = obj.str("source"),
|
||||
builtin = obj.bool("builtin"),
|
||||
location = location,
|
||||
editable = obj.bool("editable"),
|
||||
content = obj.str("content"),
|
||||
subtask = obj.flagOrNull("subtask"),
|
||||
hints = obj["hints"].arr()?.mapNotNull { it.jsonPrimitive.contentOrNull } ?: emptyList(),
|
||||
)
|
||||
}
|
||||
|
||||
+4
@@ -56,8 +56,12 @@ internal object KiloWorkspaceDtoMapper {
|
||||
fun command(c: CommandInfo) = CommandDto(
|
||||
name = c.name,
|
||||
description = c.description,
|
||||
agent = c.agent,
|
||||
model = c.model,
|
||||
variant = c.variant,
|
||||
source = c.source,
|
||||
hints = c.hints,
|
||||
subtask = c.subtask,
|
||||
)
|
||||
|
||||
fun skill(s: SkillInfo) = SkillDto(
|
||||
|
||||
+4
@@ -135,8 +135,12 @@ data class AgentInfo(
|
||||
data class CommandInfo(
|
||||
val name: String,
|
||||
val description: String?,
|
||||
val agent: String?,
|
||||
val model: String?,
|
||||
val variant: String?,
|
||||
val source: String?,
|
||||
val hints: List<String>,
|
||||
val subtask: Boolean?,
|
||||
)
|
||||
|
||||
data class SkillInfo(
|
||||
|
||||
+5
-1
@@ -1817,7 +1817,7 @@ class KiloCliDataParserTest {
|
||||
@Test
|
||||
fun `parseCommands - maps name, description, source, and hints`() {
|
||||
val raw = """[
|
||||
{"name":"init","description":"guided AGENTS.md setup","template":"static body","hints":["${'$'}ARGUMENTS"],"source":"command"},
|
||||
{"name":"init","description":"guided AGENTS.md setup","agent":"reviewer","model":"anthropic/claude-sonnet-4-6","variant":"high","template":"static body","hints":["${'$'}ARGUMENTS"],"source":"command","subtask":true},
|
||||
{"name":"mcp-tool","template":"","hints":["${'$'}1","${'$'}2"],"source":"mcp"}
|
||||
]"""
|
||||
|
||||
@@ -1826,8 +1826,12 @@ class KiloCliDataParserTest {
|
||||
assertEquals(2, result.size)
|
||||
assertEquals("init", result[0].name)
|
||||
assertEquals("guided AGENTS.md setup", result[0].description)
|
||||
assertEquals("reviewer", result[0].agent)
|
||||
assertEquals("anthropic/claude-sonnet-4-6", result[0].model)
|
||||
assertEquals("high", result[0].variant)
|
||||
assertEquals("command", result[0].source)
|
||||
assertEquals(listOf("\$ARGUMENTS"), result[0].hints)
|
||||
assertEquals(true, result[0].subtask)
|
||||
assertEquals("mcp", result[1].source)
|
||||
assertEquals(listOf("\$1", "\$2"), result[1].hints)
|
||||
}
|
||||
|
||||
+5
-1
@@ -125,7 +125,7 @@ class KiloAgentBehaviorRpcApiImplTest {
|
||||
val file = Files.createDirectories(dir.resolve("command")).resolve("review.md")
|
||||
Files.writeString(file, "---\ndescription: Review code\n---\n\nReview $" + "ARGUMENTS")
|
||||
mock.commandFiles = """[
|
||||
{"name":"review","description":"Review code","source":"command","builtin":false,"location":"$file","editable":true,"content":"Review"},
|
||||
{"name":"review","description":"Review code","agent":"reviewer","model":"anthropic/claude-sonnet-4-6","variant":"high","source":"command","builtin":false,"location":"$file","editable":true,"content":"Review","subtask":true},
|
||||
{"name":"init","source":"command","builtin":true,"location":"builtin","editable":false,"content":"Init"}
|
||||
]""".trimIndent()
|
||||
val rpc = rpc()
|
||||
@@ -133,6 +133,10 @@ class KiloAgentBehaviorRpcApiImplTest {
|
||||
val commands = rpc.commandFiles("/test project")
|
||||
assertEquals(listOf("review", "init"), commands.map { it.name })
|
||||
assertEquals(true, commands.single { it.name == "review" }.editable)
|
||||
assertEquals("reviewer", commands.single { it.name == "review" }.agent)
|
||||
assertEquals("anthropic/claude-sonnet-4-6", commands.single { it.name == "review" }.model)
|
||||
assertEquals("high", commands.single { it.name == "review" }.variant)
|
||||
assertEquals(true, commands.single { it.name == "review" }.subtask)
|
||||
assertEquals(false, commands.single { it.name == "init" }.editable)
|
||||
|
||||
assertTrue(rpc.removeCommand("/test project", file.toString()))
|
||||
|
||||
@@ -6,7 +6,11 @@ import kotlinx.serialization.Serializable
|
||||
data class CommandDto(
|
||||
val name: String,
|
||||
val description: String? = null,
|
||||
val agent: String? = null,
|
||||
val model: String? = null,
|
||||
val variant: String? = null,
|
||||
val source: String? = null,
|
||||
val hints: List<String> = emptyList(),
|
||||
val template: String? = null,
|
||||
val subtask: Boolean? = null,
|
||||
)
|
||||
|
||||
@@ -6,10 +6,14 @@ import kotlinx.serialization.Serializable
|
||||
data class CommandFileDto(
|
||||
val name: String,
|
||||
val description: String? = null,
|
||||
val agent: String? = null,
|
||||
val model: String? = null,
|
||||
val variant: String? = null,
|
||||
val source: String? = null,
|
||||
val builtin: Boolean = false,
|
||||
val location: String,
|
||||
val editable: Boolean = false,
|
||||
val content: String? = null,
|
||||
val subtask: Boolean? = null,
|
||||
val hints: List<String> = emptyList(),
|
||||
)
|
||||
|
||||
@@ -9,11 +9,15 @@ import { WorkflowsMigrator } from "@/kilocode/workflows-migrator"
|
||||
export const Info = Schema.Struct({
|
||||
name: Schema.String,
|
||||
description: Schema.optional(Schema.String),
|
||||
agent: Schema.optional(Schema.String),
|
||||
model: Schema.optional(Schema.String),
|
||||
variant: Schema.optional(Schema.String),
|
||||
source: Schema.optional(Schema.String),
|
||||
builtin: Schema.Boolean,
|
||||
location: Schema.String,
|
||||
editable: Schema.Boolean,
|
||||
content: Schema.optional(Schema.String),
|
||||
subtask: Schema.optional(Schema.Boolean),
|
||||
hints: Schema.Array(Schema.String),
|
||||
}).annotate({ identifier: "CommandFile" })
|
||||
|
||||
@@ -85,22 +89,30 @@ export async function discover(input: { commands: readonly Command.Info[]; direc
|
||||
return {
|
||||
name: cmd.name,
|
||||
description: description(cmd, file),
|
||||
agent: cmd.agent,
|
||||
model: cmd.model,
|
||||
variant: cmd.variant,
|
||||
source: cmd.source,
|
||||
builtin: false,
|
||||
location: file.location,
|
||||
editable: true,
|
||||
content: file.content,
|
||||
subtask: cmd.subtask,
|
||||
hints: cmd.hints,
|
||||
}
|
||||
}
|
||||
return {
|
||||
name: cmd.name,
|
||||
description: description(cmd),
|
||||
agent: cmd.agent,
|
||||
model: cmd.model,
|
||||
variant: cmd.variant,
|
||||
source: cmd.source,
|
||||
builtin: true,
|
||||
location: "builtin",
|
||||
editable: false,
|
||||
content: literal(cmd),
|
||||
subtask: cmd.subtask,
|
||||
hints: cmd.hints,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -22,8 +22,12 @@ function cmd(input: Partial<Command.Info> & Pick<Command.Info, "name">): Command
|
||||
return {
|
||||
name: input.name,
|
||||
description: input.description,
|
||||
agent: input.agent,
|
||||
model: input.model,
|
||||
variant: input.variant,
|
||||
source: input.source,
|
||||
template: input.template ?? "body",
|
||||
subtask: input.subtask,
|
||||
hints: input.hints ?? [],
|
||||
}
|
||||
}
|
||||
@@ -38,11 +42,31 @@ describe("CommandFiles", () => {
|
||||
const items = await CommandFiles.discover({
|
||||
directory: dir,
|
||||
directories: [path.join(dir, ".kilo")],
|
||||
commands: [cmd({ name: "review", source: "command", hints: ["$ARGUMENTS"] }), cmd({ name: "init", source: "command" })],
|
||||
commands: [
|
||||
cmd({
|
||||
name: "review",
|
||||
source: "command",
|
||||
agent: "reviewer",
|
||||
model: "anthropic/claude-sonnet-4-6",
|
||||
variant: "high",
|
||||
subtask: true,
|
||||
hints: ["$ARGUMENTS"],
|
||||
}),
|
||||
cmd({ name: "init", source: "command" }),
|
||||
],
|
||||
})
|
||||
|
||||
expect(items.map((item) => item.name)).toEqual(["review", "init"])
|
||||
expect(items[0]).toMatchObject({ name: "review", editable: true, builtin: false, location: file })
|
||||
expect(items[0]).toMatchObject({
|
||||
name: "review",
|
||||
editable: true,
|
||||
builtin: false,
|
||||
location: file,
|
||||
agent: "reviewer",
|
||||
model: "anthropic/claude-sonnet-4-6",
|
||||
variant: "high",
|
||||
subtask: true,
|
||||
})
|
||||
expect(items[0].content).toContain("Review $ARGUMENTS")
|
||||
expect(items[1]).toMatchObject({ name: "init", editable: false, builtin: true, location: "builtin" })
|
||||
})
|
||||
|
||||
@@ -3237,11 +3237,15 @@ export type AgentRequirementResult = {
|
||||
export type CommandFile = {
|
||||
name: string
|
||||
description?: string
|
||||
agent?: string
|
||||
model?: string
|
||||
variant?: string
|
||||
source?: string
|
||||
builtin: boolean
|
||||
location: string
|
||||
editable: boolean
|
||||
content?: string
|
||||
subtask?: boolean
|
||||
hints: Array<string>
|
||||
}
|
||||
|
||||
|
||||
@@ -34497,6 +34497,15 @@
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"agent": {
|
||||
"type": "string"
|
||||
},
|
||||
"model": {
|
||||
"type": "string"
|
||||
},
|
||||
"variant": {
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -34512,6 +34521,9 @@
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"subtask": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hints": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
||||
Reference in New Issue
Block a user