commands.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import type { Part } from "@kirincode-ai/sdk"
  2. import type { MemoryStore } from "./memory"
  3. import type { SkillRegistry } from "./skills"
  4. let partSeq = 0
  5. function textPart(text: string, sessionID: string): Part {
  6. const id = `kc_part_${Date.now()}_${partSeq++}`
  7. return { id, sessionID, messageID: id, type: "text", text }
  8. }
  9. export async function handleMemoryCommand(args: string, store: MemoryStore, sessionID: string): Promise<Part[]> {
  10. const sub = args.trim()
  11. if (!sub || sub === "list") {
  12. return [textPart(store.format(), sessionID)]
  13. }
  14. if (sub.startsWith("read ")) {
  15. const topic = sub.slice(5).trim()
  16. const content = store.readTopic(topic)
  17. return [textPart(content || `No topic '${topic}' found. Topics: ${store.listTopics().join(", ") || "none"}`, sessionID)]
  18. }
  19. if (sub.startsWith("write ")) {
  20. const rest = sub.slice(6).trim()
  21. const spaceIdx = rest.indexOf(" ")
  22. if (spaceIdx === -1) return [textPart("Usage: /memory write <topic> <content>", sessionID)]
  23. const topic = rest.slice(0, spaceIdx)
  24. const content = rest.slice(spaceIdx + 1)
  25. store.writeTopic(topic, content)
  26. return [textPart(`Topic '${topic}' saved.`, sessionID)]
  27. }
  28. return [textPart("Usage: /memory [list|read <topic>|write <topic> <content>]", sessionID)]
  29. }
  30. export async function handleSkillCommand(args: string, registry: SkillRegistry, worktree: string, sessionID: string): Promise<Part[]> {
  31. const sub = args.trim()
  32. if (!sub || sub === "list") {
  33. const skills = registry.list()
  34. if (skills.length === 0) return [textPart("No skills registered. Use `/skill create <name> <description>` to create one.", sessionID)]
  35. const lines = skills.map((s) => `- **/${s.manifest.name}** [${s.scope}] — ${s.manifest.description}`)
  36. return [textPart(lines.join("\n"), sessionID)]
  37. }
  38. if (sub.startsWith("create ")) {
  39. const rest = sub.slice(7).trim()
  40. const firstQuote = rest.match(/["'](.+?)["']/)
  41. if (firstQuote) {
  42. const name = firstQuote[1] ?? ""
  43. if (!name) return [textPart("Could not parse skill name from quoted string.", sessionID)]
  44. const desc = rest.slice((firstQuote[0] ?? "").length).trim().replace(/^["'](.+?)["']/, "$1")
  45. const path = registry.createSkill(name, desc, `# ${name}\n\nTODO: Write skill instructions here.`, "project", worktree)
  46. registry.load(worktree)
  47. return [textPart(`Skill '/${name}' created at ${path}\nEdit the file to add instructions, then use /skill ${name} to invoke.`, sessionID)]
  48. }
  49. const parts = rest.split(/\s+/)
  50. if (parts.length < 2) return [textPart("Usage: /skill create <name> <description>", sessionID)]
  51. const name = parts[0] ?? ""
  52. if (!name) return [textPart("Usage: /skill create <name> <description>", sessionID)]
  53. const desc = parts.slice(1).join(" ")
  54. const path = registry.createSkill(name, desc, `# ${name}\n\nTODO: Write skill instructions here.`, "project", worktree)
  55. registry.load(worktree)
  56. return [textPart(`Skill '/${name}' created at ${path}`, sessionID)]
  57. }
  58. if (sub === "reload") {
  59. registry.load(worktree)
  60. return [textPart(`Skills reloaded. ${registry.list().length} skill(s) available.`, sessionID)]
  61. }
  62. // Treat as skill name invocation
  63. const content = registry.injectSkill(sub)
  64. return [textPart(content, sessionID)]
  65. }