import path from "path"
import fs from "fs/promises"
import { Effect } from "effect"
import type { AppFileSystem } from "@opencode-ai/core/filesystem"
import { Global } from "@opencode-ai/core/global"
import * as Log from "@opencode-ai/core/util/log"
type EmbeddedSkillFile = string | { encoding: "base64"; content: string }
declare const DEVECO_DEFAULT_SKILLS: Record<string, Record<string, EmbeddedSkillFile>> | undefined
export namespace Defaults {
const log = Log.create({ service: "skill-defaults" })
export const ensure = Effect.fn("Skill.Defaults.ensure")(function* (version: string, fsys: AppFileSystem.Interface) {
const dir = path.join(Global.Path.data, "skills")
const versionFile = path.join(dir, ".version")
const current = yield* Effect.gen(function* () {
const exists = yield* fsys.existsSafe(versionFile)
if (!exists) return ""
const buf = yield* fsys.readFile(versionFile)
return new TextDecoder().decode(buf)
}).pipe(Effect.catch(() => Effect.succeed("")))
if (current === version && current !== "local") {
return dir
}
log.info("extracting default skills", { version })
const userSkillBackupDir = path.join(Global.Path.config, "skills")
yield* Effect.tryPromise(() =>
fs.readdir(dir, { withFileTypes: true }),
).pipe(
Effect.flatMap((entries) =>
Effect.forEach(
entries.filter((e) => e.isDirectory()),
(entry) =>
Effect.gen(function* () {
const subVersionFile = path.join(dir, entry.name, ".version")
const isBuiltin = yield* fsys.existsSafe(subVersionFile)
if (isBuiltin) return
const src = path.join(dir, entry.name)
const dest = path.join(userSkillBackupDir, entry.name)
yield* Effect.tryPromise(() =>
fs.cp(src, dest, { recursive: true }),
).pipe(Effect.catch(() => Effect.void))
}),
{ concurrency: "unbounded" },
),
),
Effect.catch(() => Effect.void),
)
const data = typeof DEVECO_DEFAULT_SKILLS !== "undefined" ? DEVECO_DEFAULT_SKILLS : {}
yield* Effect.tryPromise(() =>
fs.readdir(dir, { withFileTypes: true }),
).pipe(
Effect.flatMap((entries) =>
Effect.forEach(
entries.filter((e) => e.isDirectory()),
(entry) =>
Effect.gen(function* () {
const subVersionFile = path.join(dir, entry.name, ".version")
const isBuiltin = yield* fsys.existsSafe(subVersionFile)
if (!isBuiltin) return
yield* Effect.tryPromise(() =>
fs.rm(path.join(dir, entry.name), { recursive: true, force: true }),
).pipe(Effect.catch(() => Effect.void))
}),
{ concurrency: "unbounded" },
),
),
Effect.catch(() => Effect.void),
)
for (const [skillName, files] of Object.entries(data)) {
yield* fsys.writeWithDirs(path.join(dir, skillName, ".version"), skillName)
for (const [fileName, content] of Object.entries(files)) {
yield* fsys.writeWithDirs(
path.join(dir, skillName, fileName),
typeof content === "string" ? content : Uint8Array.from(Buffer.from(content.content, content.encoding)),
)
}
}
yield* fsys.writeWithDirs(versionFile, version)
return dir
})
}