loader.ts 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  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. console.error(`[lite] Config validation error in ${configPath}:`, result.error.issues);
  19. return null;
  20. }
  21. console.log(`[lite] Config loaded from ${configPath}`);
  22. return result.data;
  23. }
  24. } catch (err) {
  25. console.error(`[lite] Error loading config from ${configPath}:`, err);
  26. }
  27. return null;
  28. }
  29. function deepMerge<T extends Record<string, unknown>>(base?: T, override?: T): T | undefined {
  30. if (!base) return override;
  31. if (!override) return base;
  32. const result = { ...base } as T;
  33. for (const key of Object.keys(override) as (keyof T)[]) {
  34. const baseVal = base[key];
  35. const overrideVal = override[key];
  36. if (
  37. typeof baseVal === "object" && baseVal !== null &&
  38. typeof overrideVal === "object" && overrideVal !== null &&
  39. !Array.isArray(baseVal) && !Array.isArray(overrideVal)
  40. ) {
  41. result[key] = deepMerge(
  42. baseVal as Record<string, unknown>,
  43. overrideVal as Record<string, unknown>
  44. ) as T[keyof T];
  45. } else {
  46. result[key] = overrideVal;
  47. }
  48. }
  49. return result;
  50. }
  51. export function loadPluginConfig(directory: string): PluginConfig {
  52. const userConfigPath = path.join(
  53. getUserConfigDir(),
  54. "opencode",
  55. "oh-my-opencode-lite.json"
  56. );
  57. const projectConfigPath = path.join(directory, ".opencode", "oh-my-opencode-lite.json");
  58. let config: PluginConfig = loadConfigFromPath(userConfigPath) ?? {};
  59. const projectConfig = loadConfigFromPath(projectConfigPath);
  60. if (projectConfig) {
  61. config = {
  62. ...config,
  63. ...projectConfig,
  64. agents: deepMerge(config.agents, projectConfig.agents),
  65. disabled_agents: [
  66. ...new Set([
  67. ...(config.disabled_agents ?? []),
  68. ...(projectConfig.disabled_agents ?? []),
  69. ]),
  70. ],
  71. disabled_hooks: [
  72. ...new Set([
  73. ...(config.disabled_hooks ?? []),
  74. ...(projectConfig.disabled_hooks ?? []),
  75. ]),
  76. ],
  77. };
  78. }
  79. return config;
  80. }