loader.ts 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import * as fs from "fs";
  2. import * as path from "path";
  3. import * as os from "os";
  4. import { PluginConfigSchema, type PluginConfig } from "./schema";
  5. function getUserConfigDir(): string {
  6. if (process.platform === "win32") {
  7. return process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
  8. }
  9. return process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
  10. }
  11. function loadConfigFromPath(configPath: string): PluginConfig | null {
  12. try {
  13. if (fs.existsSync(configPath)) {
  14. const content = fs.readFileSync(configPath, "utf-8");
  15. const rawConfig = JSON.parse(content);
  16. const result = PluginConfigSchema.safeParse(rawConfig);
  17. if (!result.success) {
  18. return null;
  19. }
  20. return result.data;
  21. }
  22. } catch {
  23. // Silently ignore config loading errors
  24. }
  25. return null;
  26. }
  27. function deepMerge<T extends Record<string, unknown>>(base?: T, override?: T): T | undefined {
  28. if (!base) return override;
  29. if (!override) return base;
  30. const result = { ...base } as T;
  31. for (const key of Object.keys(override) as (keyof T)[]) {
  32. const baseVal = base[key];
  33. const overrideVal = override[key];
  34. if (
  35. typeof baseVal === "object" && baseVal !== null &&
  36. typeof overrideVal === "object" && overrideVal !== null &&
  37. !Array.isArray(baseVal) && !Array.isArray(overrideVal)
  38. ) {
  39. result[key] = deepMerge(
  40. baseVal as Record<string, unknown>,
  41. overrideVal as Record<string, unknown>
  42. ) as T[keyof T];
  43. } else {
  44. result[key] = overrideVal;
  45. }
  46. }
  47. return result;
  48. }
  49. export function loadPluginConfig(directory: string): PluginConfig {
  50. const userConfigPath = path.join(
  51. getUserConfigDir(),
  52. "opencode",
  53. "oh-my-opencode-slim.json"
  54. );
  55. const projectConfigPath = path.join(directory, ".opencode", "oh-my-opencode-slim.json");
  56. let config: PluginConfig = loadConfigFromPath(userConfigPath) ?? {};
  57. const projectConfig = loadConfigFromPath(projectConfigPath);
  58. if (projectConfig) {
  59. config = {
  60. ...config,
  61. ...projectConfig,
  62. agents: deepMerge(config.agents, projectConfig.agents),
  63. disabled_agents: [
  64. ...new Set([
  65. ...(config.disabled_agents ?? []),
  66. ...(projectConfig.disabled_agents ?? []),
  67. ]),
  68. ],
  69. };
  70. }
  71. return config;
  72. }