{"version":3,"file":"dpiSettings.mjs","names":[],"sources":["../../../src/builders/cli/dpiSettings.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport { piCliPath } from '@agimon-ai/doompi-core/module-resolution';\nimport { piAgentDirectory } from '@agimon-ai/doompi-core/runtime-pi-settings';\nimport { readSyncRegistration } from '@agimon-ai/doompi-core/sync-registration';\nimport type { SettingsManager, SettingsManagerCreateOptions } from '@earendil-works/pi-coding-agent';\n\nimport { findRepositoryRoot } from '../../composition/repository';\n\nconst GLOBAL_SCOPE = 'global';\nconst PROJECT_SCOPE = 'project';\nconst MANAGED_KEYS = ['extensions', 'themes', 'theme', 'quietStartup'] as const;\n\nexport interface DpiManagedSettings {\n  extensions: string[];\n  themes: string[];\n  theme: string;\n  quietStartup: boolean;\n}\n\nexport const DPI_MANAGED_SETTINGS: DpiManagedSettings = {\n  extensions: ['@agimon-ai/doompi', '!extensions/**'],\n  themes: ['themes/doom-pi-dark.json'],\n  theme: 'doom-pi-dark',\n  quietStartup: true,\n};\n\nexport type DpiSettingsScope = typeof GLOBAL_SCOPE | typeof PROJECT_SCOPE;\n\nexport interface DpiSettingsStorageBackend {\n  withLock(scope: DpiSettingsScope, callback: (current: string | undefined) => string | undefined): void;\n}\n\ninterface SettingsManagerClass {\n  create(this: void, cwd: string, agentDir?: string, options?: SettingsManagerCreateOptions): SettingsManager;\n  fromStorage(this: void, storage: DpiSettingsStorageBackend, options?: SettingsManagerCreateOptions): SettingsManager;\n}\n\ninterface FileSettingsStorageConstructor {\n  new (cwd: string, agentDir: string): DpiSettingsStorageBackend;\n}\n\nexport interface PiSettingsRuntime {\n  FileSettingsStorage: FileSettingsStorageConstructor;\n  SettingsManager: SettingsManagerClass;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n  return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction parseSettings(content: string | undefined): Record<string, unknown> {\n  if (content === undefined) return {};\n  const parsed: unknown = JSON.parse(content);\n  if (!isRecord(parsed)) throw new SyntaxError('Pi settings must contain a JSON object');\n  return parsed;\n}\n\nfunction managedView(\n  scope: DpiSettingsScope,\n  current: string | undefined,\n  managedSettings: DpiManagedSettings,\n): string | undefined {\n  if (scope === PROJECT_SCOPE && current === undefined) return undefined;\n  const settings = parseSettings(current);\n  if (scope === GLOBAL_SCOPE) Object.assign(settings, managedSettings);\n  else for (const key of MANAGED_KEYS) delete settings[key];\n  return JSON.stringify(settings, null, 2);\n}\n\nfunction restorePersistedSettings(current: string | undefined, next: string): string {\n  const persisted = parseSettings(current);\n  const updated = parseSettings(next);\n  for (const key of MANAGED_KEYS) {\n    if (Object.hasOwn(persisted, key)) updated[key] = persisted[key];\n    else delete updated[key];\n  }\n  return JSON.stringify(updated, null, 2);\n}\n\n/**\n * Presents DoomPi-owned settings to Pi without allowing that overlay to leak\n * into either the global or project settings file when Pi saves another key.\n */\nexport class DpiSettingsStorage implements DpiSettingsStorageBackend {\n  private readonly backend: DpiSettingsStorageBackend;\n  private readonly managedSettings: DpiManagedSettings;\n\n  constructor(backend: DpiSettingsStorageBackend, managedSettings: DpiManagedSettings = DPI_MANAGED_SETTINGS) {\n    this.backend = backend;\n    this.managedSettings = managedSettings;\n  }\n\n  withLock(scope: DpiSettingsScope, callback: (current: string | undefined) => string | undefined): void {\n    this.backend.withLock(scope, (current) => {\n      const next = callback(managedView(scope, current, this.managedSettings));\n      return next === undefined ? undefined : restorePersistedSettings(current, next);\n    });\n  }\n}\n\nfunction isPiSettingsRuntime(value: unknown): value is PiSettingsRuntime {\n  if (!isRecord(value) || typeof value.FileSettingsStorage !== 'function') return false;\n  const settingsManager = value.SettingsManager;\n  if (typeof settingsManager !== 'function') return false;\n  return (\n    typeof Reflect.get(settingsManager, 'create') === 'function' &&\n    typeof Reflect.get(settingsManager, 'fromStorage') === 'function'\n  );\n}\n\n/** Loads Pi's concrete file storage from the same pinned installation as its CLI. */\nexport async function loadPiSettingsRuntime(): Promise<PiSettingsRuntime> {\n  const modulePath = path.join(path.dirname(piCliPath()), 'core', 'settings-manager.js');\n  const loaded: unknown = await import(pathToFileURL(modulePath).href);\n  if (!isPiSettingsRuntime(loaded)) throw new Error(`Unsupported Pi settings runtime: ${modulePath}`);\n  return loaded;\n}\n\n/** Selects the package recorded by sync so embedded DPI does not depend on init's global dispatcher. */\nexport function resolveDpiManagedSettings(\n  cwd: string,\n  environment: NodeJS.ProcessEnv = process.env,\n): DpiManagedSettings {\n  try {\n    const root = findRepositoryRoot(cwd);\n    const homeDirectory = environment.HOME?.trim() || os.homedir();\n    const registration = readSyncRegistration(root, homeDirectory);\n    if (!registration) return DPI_MANAGED_SETTINGS;\n    return { ...DPI_MANAGED_SETTINGS, extensions: [registration.package.root, '!extensions/**'] };\n  } catch {\n    return { ...DPI_MANAGED_SETTINGS, extensions: ['!extensions/**'] };\n  }\n}\n\n/** Installs the process-local create() hook used by upstream Pi startup and reloads. */\nexport function installDpiSettingsOverlay(\n  runtime: PiSettingsRuntime,\n  environment: NodeJS.ProcessEnv = process.env,\n): () => void {\n  const originalCreate = runtime.SettingsManager.create;\n  runtime.SettingsManager.create = (cwd, agentDir, options) => {\n    const resolvedAgentDirectory = agentDir ?? piAgentDirectory(environment);\n    const storage = new runtime.FileSettingsStorage(cwd, resolvedAgentDirectory);\n    const managedSettings = resolveDpiManagedSettings(cwd, environment);\n    return runtime.SettingsManager.fromStorage(new DpiSettingsStorage(storage, managedSettings), options);\n  };\n\n  return () => {\n    runtime.SettingsManager.create = originalCreate;\n  };\n}\n"],"mappings":"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"}