command.ts 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. export * as ConfigCommand from "./command"
  2. import * as Log from "@opencode-ai/core/util/log"
  3. import { Cause, Exit, Schema } from "effect"
  4. import { Glob } from "@opencode-ai/core/util/glob"
  5. import { configEntryNameFromPath } from "./entry-name"
  6. import { InvalidError } from "./error"
  7. import * as ConfigMarkdown from "./markdown"
  8. import { ConfigModelID } from "./model-id"
  9. const log = Log.create({ service: "config" })
  10. export const Info = Schema.Struct({
  11. template: Schema.String,
  12. description: Schema.optional(Schema.String),
  13. agent: Schema.optional(Schema.String),
  14. model: Schema.optional(ConfigModelID),
  15. subtask: Schema.optional(Schema.Boolean),
  16. })
  17. export type Info = Schema.Schema.Type<typeof Info>
  18. const decodeInfo = Schema.decodeUnknownExit(Info)
  19. export async function load(dir: string) {
  20. const result: Record<string, Info> = {}
  21. for (const item of await Glob.scan("{command,commands}/**/*.md", {
  22. cwd: dir,
  23. absolute: true,
  24. dot: true,
  25. symlink: true,
  26. })) {
  27. const md = await ConfigMarkdown.parse(item).catch((err) => {
  28. log.error("failed to load command", { command: item, err })
  29. return undefined
  30. })
  31. if (!md) continue
  32. const patterns = ["/.opencode/command/", "/.opencode/commands/", "/command/", "/commands/"]
  33. const name = configEntryNameFromPath(item, patterns)
  34. const config = {
  35. name,
  36. ...md.data,
  37. template: md.content.trim(),
  38. }
  39. const parsed = decodeInfo(config, { errors: "all", propertyOrder: "original" })
  40. if (Exit.isSuccess(parsed)) {
  41. result[config.name] = parsed.value
  42. continue
  43. }
  44. throw new InvalidError({ path: item, message: Cause.pretty(parsed.cause) }, { cause: Cause.squash(parsed.cause) })
  45. }
  46. return result
  47. }