@agimon-ai/doompi-config 0.0.1-alpha.66 → 0.0.1-alpha.68

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/adapters/config.cjs +1 -1
  2. package/dist/adapters/config.cjs.map +1 -1
  3. package/dist/adapters/config.d.cts +10 -1
  4. package/dist/adapters/config.d.cts.map +1 -1
  5. package/dist/adapters/config.d.mts +10 -1
  6. package/dist/adapters/config.d.mts.map +1 -1
  7. package/dist/adapters/config.mjs +1 -1
  8. package/dist/adapters/config.mjs.map +1 -1
  9. package/dist/adapters/majorModes.cjs +1 -1
  10. package/dist/adapters/majorModes.cjs.map +1 -1
  11. package/dist/adapters/majorModes.d.cts +12 -0
  12. package/dist/adapters/majorModes.d.cts.map +1 -1
  13. package/dist/adapters/majorModes.d.mts +12 -0
  14. package/dist/adapters/majorModes.d.mts.map +1 -1
  15. package/dist/adapters/majorModes.mjs +1 -1
  16. package/dist/adapters/majorModes.mjs.map +1 -1
  17. package/dist/config.cjs +1 -1
  18. package/dist/config.d.cts +4 -4
  19. package/dist/config.d.mts +4 -4
  20. package/dist/config.mjs +1 -1
  21. package/dist/index.cjs +1 -1
  22. package/dist/index.d.cts +5 -5
  23. package/dist/index.d.mts +5 -5
  24. package/dist/index.mjs +1 -1
  25. package/dist/majorModes.cjs +1 -1
  26. package/dist/majorModes.d.cts +2 -2
  27. package/dist/majorModes.d.mts +2 -2
  28. package/dist/majorModes.mjs +1 -1
  29. package/dist/services/configPolicy.cjs +1 -1
  30. package/dist/services/configPolicy.cjs.map +1 -1
  31. package/dist/services/configPolicy.d.cts +14 -3
  32. package/dist/services/configPolicy.d.cts.map +1 -1
  33. package/dist/services/configPolicy.d.mts +14 -3
  34. package/dist/services/configPolicy.d.mts.map +1 -1
  35. package/dist/services/configPolicy.mjs +1 -1
  36. package/dist/services/configPolicy.mjs.map +1 -1
  37. package/dist/types/config.d.cts +25 -0
  38. package/dist/types/config.d.cts.map +1 -1
  39. package/dist/types/config.d.mts +25 -0
  40. package/dist/types/config.d.mts.map +1 -1
  41. package/dist/types.d.cts +2 -2
  42. package/dist/types.d.mts +2 -2
  43. package/package.json +3 -3
  44. package/src/prompts/doompi-author-config/SKILL.md +4 -1
@@ -1,2 +1,2 @@
1
- const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../services/configPolicy.cjs");let n=require("node:crypto"),r=require("node:fs");r=e.__toESM(r,1);let i=require("node:path");i=e.__toESM(i,1);let a=require("node:os");a=e.__toESM(a,1);let o=require("yaml");const s=`.doom`,c=`config.yaml`;function l(e){return r.default.existsSync(e)?t.parseDoomConfig(r.default.readFileSync(e,`utf8`),e):{projectTrust:`ask`}}async function u(e){try{return t.parseDoomConfig(await r.default.promises.readFile(e,`utf8`),e)}catch(e){if(e.code===`ENOENT`)return{projectTrust:`ask`};throw e}}function d(e=a.default.homedir()){return i.default.join(e,`.pi`,s)}function f(e=a.default.homedir()){return i.default.join(d(e),c)}function p(e,t,n=a.default.homedir()){if(!e)return i.default.join(n,`.pi`,`plans`);if(e===`~`)return n;if(e.startsWith(`~/`))return i.default.resolve(n,e.slice(2));if(e.startsWith(`~`))throw Error(`Doom planning plansDirectory supports only '~' or '~/' home aliases`);return i.default.isAbsolute(e)?i.default.normalize(e):i.default.resolve(t,e)}function m(e){return i.default.join(e,s,c)}function h(e,n=a.default.homedir()){return t.mergeDoomConfigs(l(f(n)),l(m(e)))}async function g(e,n=a.default.homedir()){let r=await u(f(n)),i=await u(m(e));return t.mergeDoomConfigs(r,i)}function _(e){let i;try{i=r.default.readFileSync(e,`utf8`)}catch(t){if(t.code===`ENOENT`)return{filePath:e,exists:!1,hash:``,keys:[]};throw t}let a=(0,o.parse)(i)??{};return{filePath:e,exists:!0,hash:(0,n.createHash)(`sha256`).update(i,`utf8`).digest(`hex`),keys:t.configLeafKeys(a)}}function v(e,n=a.default.homedir()){let r=_(f(n)),i=e===void 0?{filePath:``,exists:!1,hash:``,keys:[]}:_(m(e)),o=e===void 0?t.mergeDoomConfigs(l(f(n)),{projectTrust:`ask`}):h(e,n),s=(e,t)=>{let n=t.join(`.`);return e.keys.some(e=>e===n||e.startsWith(`${n}.`))};return{globalFile:r,repositoryFile:i,effective:o,originOf(e){let n=t.configScopeOf(e);return n!==`global`&&s(i,e)?`repository`:n!==`repository`&&s(r,e)?`global`:`default`},valueAt:e=>t.valueAtKeyPath(o,e)}}exports.globalDoomConfigDirectory=d,exports.globalDoomConfigPath=f,exports.loadDoomConfig=h,exports.loadDoomConfigAsync=g,exports.loadDoomConfigLayers=v,exports.repositoryDoomConfigPath=m,exports.resolvePlanningPlansDirectory=p;
1
+ const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../services/configPolicy.cjs");let n=require("node:crypto"),r=require("node:fs");r=e.__toESM(r,1);let i=require("node:path");i=e.__toESM(i,1);let a=require("node:os");a=e.__toESM(a,1);let o=require("yaml");const s=`.doom`,c=`config.yaml`;function l(e){return r.default.existsSync(e)?t.parseDoomConfig(r.default.readFileSync(e,`utf8`),e):{projectTrust:`ask`}}async function u(e){try{return t.parseDoomConfig(await r.default.promises.readFile(e,`utf8`),e)}catch(e){if(e.code===`ENOENT`)return{projectTrust:`ask`};throw e}}function d(e=a.default.homedir()){return i.default.join(e,`.pi`,s)}function f(e=a.default.homedir()){return i.default.join(d(e),c)}function p(e,t,n=a.default.homedir()){if(!e)return i.default.join(n,`.pi`,`plans`);if(e===`~`)return n;if(e.startsWith(`~/`))return i.default.resolve(n,e.slice(2));if(e.startsWith(`~`))throw Error(`Doom planning plansDirectory supports only '~' or '~/' home aliases`);return i.default.isAbsolute(e)?i.default.normalize(e):i.default.resolve(t,e)}function m(e){return i.default.join(e,s,c)}function h(e,n=a.default.homedir()){return t.mergeDoomConfigs(l(f(n)),l(m(e)))}function g(e,n=a.default.homedir()){let i=e=>r.default.existsSync(e)?t.parseDoomConfig(r.default.readFileSync(e,`utf8`),e,{lenient:!0}):{config:{projectTrust:`ask`},diagnostics:[]},o=i(f(n)),s=i(m(e)),c=[...o.diagnostics,...s.diagnostics];return{config:t.mergeDoomConfigs(o.config,s.config),diagnostics:c}}async function _(e,n=a.default.homedir()){let r=await u(f(n)),i=await u(m(e));return t.mergeDoomConfigs(r,i)}function v(e){let i;try{i=r.default.readFileSync(e,`utf8`)}catch(t){if(t.code===`ENOENT`)return{filePath:e,exists:!1,hash:``,keys:[]};throw t}let a=(0,o.parse)(i)??{};return{filePath:e,exists:!0,hash:(0,n.createHash)(`sha256`).update(i,`utf8`).digest(`hex`),keys:t.configLeafKeys(a)}}function y(e,n=a.default.homedir()){let r=v(f(n)),i=e===void 0?{filePath:``,exists:!1,hash:``,keys:[]}:v(m(e)),o=e===void 0?t.mergeDoomConfigs(l(f(n)),{projectTrust:`ask`}):h(e,n),s=(e,t)=>{let n=t.join(`.`);return e.keys.some(e=>e===n||e.startsWith(`${n}.`))};return{globalFile:r,repositoryFile:i,effective:o,originOf(e){let n=t.configScopeOf(e);return n!==`global`&&s(i,e)?`repository`:n!==`repository`&&s(r,e)?`global`:`default`},valueAt:e=>t.valueAtKeyPath(o,e)}}exports.globalDoomConfigDirectory=d,exports.globalDoomConfigPath=f,exports.loadDoomConfig=h,exports.loadDoomConfigAsync=_,exports.loadDoomConfigLayers=y,exports.loadDoomConfigLenient=g,exports.repositoryDoomConfigPath=m,exports.resolvePlanningPlansDirectory=p;
2
2
  //# sourceMappingURL=config.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.cjs","names":["fs","parseDoomConfig","os","path","mergeDoomConfigs","parseYaml","createHash","configLeafKeys","configScopeOf","valueAtKeyPath"],"sources":["../../src/adapters/config.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport {\n configLeafKeys,\n configScopeOf,\n mergeDoomConfigs,\n parseDoomConfig,\n valueAtKeyPath,\n} from '../services/configPolicy.ts';\nimport type { DoomConfig } from '../types/config.ts';\nimport type { ConfigValueOrigin, DoomConfigLayer, DoomConfigLayers } from '../types/config.ts';\n\nconst DOOM_DIR = '.doom';\nconst CONFIG_FILE = 'config.yaml';\nconst GLOBAL_CONFIG_DIR = '.pi';\nconst PLANS_DIR = 'plans';\n\nfunction readConfig(filePath: string): DoomConfig {\n return fs.existsSync(filePath)\n ? parseDoomConfig(fs.readFileSync(filePath, 'utf8'), filePath)\n : { projectTrust: 'ask' };\n}\n\nasync function readConfigAsync(filePath: string): Promise<DoomConfig> {\n try {\n return parseDoomConfig(await fs.promises.readFile(filePath, 'utf8'), filePath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { projectTrust: 'ask' };\n throw error;\n }\n}\n\nexport function globalDoomConfigDirectory(homeDirectory = os.homedir()): string {\n return path.join(homeDirectory, GLOBAL_CONFIG_DIR, DOOM_DIR);\n}\n\nexport function globalDoomConfigPath(homeDirectory = os.homedir()): string {\n return path.join(globalDoomConfigDirectory(homeDirectory), CONFIG_FILE);\n}\n\nexport function resolvePlanningPlansDirectory(\n configured: string | undefined,\n repoRoot: string,\n homeDirectory = os.homedir(),\n): string {\n if (!configured) return path.join(homeDirectory, GLOBAL_CONFIG_DIR, PLANS_DIR);\n if (configured === '~') return homeDirectory;\n if (configured.startsWith('~/')) return path.resolve(homeDirectory, configured.slice(2));\n if (configured.startsWith('~')) {\n throw new Error(\"Doom planning plansDirectory supports only '~' or '~/' home aliases\");\n }\n return path.isAbsolute(configured) ? path.normalize(configured) : path.resolve(repoRoot, configured);\n}\n\nexport function repositoryDoomConfigPath(repoRoot: string): string {\n return path.join(repoRoot, DOOM_DIR, CONFIG_FILE);\n}\n\nexport function loadDoomConfig(repoRoot: string, homeDirectory = os.homedir()): DoomConfig {\n return mergeDoomConfigs(\n readConfig(globalDoomConfigPath(homeDirectory)),\n readConfig(repositoryDoomConfigPath(repoRoot)),\n );\n}\n\nexport async function loadDoomConfigAsync(repoRoot: string, homeDirectory = os.homedir()): Promise<DoomConfig> {\n const globalConfig = await readConfigAsync(globalDoomConfigPath(homeDirectory));\n const repositoryConfig = await readConfigAsync(repositoryDoomConfigPath(repoRoot));\n return mergeDoomConfigs(globalConfig, repositoryConfig);\n}\n\n/**\n * Reads one config file as a layer: what it is, what it sets, and the bytes it\n * held. A file that does not exist is a layer that sets nothing, which is a\n * normal state rather than an error.\n */\nfunction readLayer(filePath: string): DoomConfigLayer {\n let text: string;\n try {\n text = fs.readFileSync(filePath, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return { filePath, exists: false, hash: '', keys: [] };\n }\n throw error;\n }\n // The raw document, not the parsed config: this is the one read that has to\n // distinguish a key the file set from a key the parser defaulted.\n const document = (parseYaml(text) ?? {}) as unknown;\n return {\n filePath,\n exists: true,\n hash: createHash('sha256').update(text, 'utf8').digest('hex'),\n keys: configLeafKeys(document),\n };\n}\n\n/**\n * The two config files kept apart, with the merge alongside them.\n *\n * `repoRoot` is optional because the global file is complete on its own: a\n * settings surface with no repository in view still has settings to show, and\n * demanding one would leave it with nothing to render rather than with the\n * global half.\n *\n * The origin answer respects the merge's own per-field rules rather than simply\n * preferring the repository: `editor` is read from the global side whatever the\n * repository says, and `projectTrust` from the repository side whatever the\n * global one says. Reporting a file as the source of a value the merge discards\n * is the exact confusion this exists to remove.\n */\nexport function loadDoomConfigLayers(repoRoot: string | undefined, homeDirectory = os.homedir()): DoomConfigLayers {\n const globalLayer = readLayer(globalDoomConfigPath(homeDirectory));\n const repositoryLayer =\n repoRoot === undefined\n ? { filePath: '', exists: false, hash: '', keys: [] }\n : readLayer(repositoryDoomConfigPath(repoRoot));\n // With no repository there is nothing to merge, so the global file stands as\n // the effective config on its own.\n const effective =\n repoRoot === undefined\n ? mergeDoomConfigs(readConfig(globalDoomConfigPath(homeDirectory)), { projectTrust: 'ask' })\n : loadDoomConfig(repoRoot, homeDirectory);\n const sets = (layer: DoomConfigLayer, keyPath: readonly string[]): boolean => {\n const dotted = keyPath.join('.');\n return layer.keys.some((key) => key === dotted || key.startsWith(`${dotted}.`));\n };\n return {\n globalFile: globalLayer,\n repositoryFile: repositoryLayer,\n effective,\n originOf(keyPath: readonly string[]): ConfigValueOrigin {\n const scope = configScopeOf(keyPath);\n if (scope !== 'global' && sets(repositoryLayer, keyPath)) return 'repository';\n if (scope !== 'repository' && sets(globalLayer, keyPath)) return 'global';\n return 'default';\n },\n valueAt: (keyPath: readonly string[]) => valueAtKeyPath(effective, keyPath),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"config.cjs","names":["fs","parseDoomConfig","os","path","mergeDoomConfigs","parseYaml","createHash","configLeafKeys","configScopeOf","valueAtKeyPath"],"sources":["../../src/adapters/config.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport {\n configLeafKeys,\n configScopeOf,\n mergeDoomConfigs,\n parseDoomConfig,\n valueAtKeyPath,\n} from '../services/configPolicy.ts';\nimport type { ConfigDiagnostic, DoomConfig, LenientParseResult } from '../types/config.ts';\nimport type { ConfigValueOrigin, DoomConfigLayer, DoomConfigLayers } from '../types/config.ts';\n\nconst DOOM_DIR = '.doom';\nconst CONFIG_FILE = 'config.yaml';\nconst GLOBAL_CONFIG_DIR = '.pi';\nconst PLANS_DIR = 'plans';\n\nfunction readConfig(filePath: string): DoomConfig {\n return fs.existsSync(filePath)\n ? parseDoomConfig(fs.readFileSync(filePath, 'utf8'), filePath)\n : { projectTrust: 'ask' };\n}\n\nasync function readConfigAsync(filePath: string): Promise<DoomConfig> {\n try {\n return parseDoomConfig(await fs.promises.readFile(filePath, 'utf8'), filePath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { projectTrust: 'ask' };\n throw error;\n }\n}\n\nexport function globalDoomConfigDirectory(homeDirectory = os.homedir()): string {\n return path.join(homeDirectory, GLOBAL_CONFIG_DIR, DOOM_DIR);\n}\n\nexport function globalDoomConfigPath(homeDirectory = os.homedir()): string {\n return path.join(globalDoomConfigDirectory(homeDirectory), CONFIG_FILE);\n}\n\nexport function resolvePlanningPlansDirectory(\n configured: string | undefined,\n repoRoot: string,\n homeDirectory = os.homedir(),\n): string {\n if (!configured) return path.join(homeDirectory, GLOBAL_CONFIG_DIR, PLANS_DIR);\n if (configured === '~') return homeDirectory;\n if (configured.startsWith('~/')) return path.resolve(homeDirectory, configured.slice(2));\n if (configured.startsWith('~')) {\n throw new Error(\"Doom planning plansDirectory supports only '~' or '~/' home aliases\");\n }\n return path.isAbsolute(configured) ? path.normalize(configured) : path.resolve(repoRoot, configured);\n}\n\nexport function repositoryDoomConfigPath(repoRoot: string): string {\n return path.join(repoRoot, DOOM_DIR, CONFIG_FILE);\n}\n\nexport function loadDoomConfig(repoRoot: string, homeDirectory = os.homedir()): DoomConfig {\n return mergeDoomConfigs(\n readConfig(globalDoomConfigPath(homeDirectory)),\n readConfig(repositoryDoomConfigPath(repoRoot)),\n );\n}\n\n/**\n * Loads the merged config, tolerating unknown keys in either file.\n *\n * Only `doompi sync` uses this. Everything that runs during a session keeps\n * `loadDoomConfig`, because a key a running extension cannot understand is\n * better reported than silently ignored; a build that refuses to start over\n * the same key is not.\n */\nexport function loadDoomConfigLenient(repoRoot: string, homeDirectory = os.homedir()): LenientParseResult {\n const read = (filePath: string): LenientParseResult =>\n fs.existsSync(filePath)\n ? parseDoomConfig(fs.readFileSync(filePath, 'utf8'), filePath, { lenient: true })\n : { config: { projectTrust: 'ask' }, diagnostics: [] };\n const globalConfig = read(globalDoomConfigPath(homeDirectory));\n const repositoryConfig = read(repositoryDoomConfigPath(repoRoot));\n const diagnostics: ConfigDiagnostic[] = [...globalConfig.diagnostics, ...repositoryConfig.diagnostics];\n // mergeDoomConfigs stays strict on purpose: its errors are cross-file scope\n // violations on known keys, which is a different fault from an unknown key.\n return { config: mergeDoomConfigs(globalConfig.config, repositoryConfig.config), diagnostics };\n}\n\nexport async function loadDoomConfigAsync(repoRoot: string, homeDirectory = os.homedir()): Promise<DoomConfig> {\n const globalConfig = await readConfigAsync(globalDoomConfigPath(homeDirectory));\n const repositoryConfig = await readConfigAsync(repositoryDoomConfigPath(repoRoot));\n return mergeDoomConfigs(globalConfig, repositoryConfig);\n}\n\n/**\n * Reads one config file as a layer: what it is, what it sets, and the bytes it\n * held. A file that does not exist is a layer that sets nothing, which is a\n * normal state rather than an error.\n */\nfunction readLayer(filePath: string): DoomConfigLayer {\n let text: string;\n try {\n text = fs.readFileSync(filePath, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return { filePath, exists: false, hash: '', keys: [] };\n }\n throw error;\n }\n // The raw document, not the parsed config: this is the one read that has to\n // distinguish a key the file set from a key the parser defaulted.\n const document = (parseYaml(text) ?? {}) as unknown;\n return {\n filePath,\n exists: true,\n hash: createHash('sha256').update(text, 'utf8').digest('hex'),\n keys: configLeafKeys(document),\n };\n}\n\n/**\n * The two config files kept apart, with the merge alongside them.\n *\n * `repoRoot` is optional because the global file is complete on its own: a\n * settings surface with no repository in view still has settings to show, and\n * demanding one would leave it with nothing to render rather than with the\n * global half.\n *\n * The origin answer respects the merge's own per-field rules rather than simply\n * preferring the repository: `editor` is read from the global side whatever the\n * repository says, and `projectTrust` from the repository side whatever the\n * global one says. Reporting a file as the source of a value the merge discards\n * is the exact confusion this exists to remove.\n */\nexport function loadDoomConfigLayers(repoRoot: string | undefined, homeDirectory = os.homedir()): DoomConfigLayers {\n const globalLayer = readLayer(globalDoomConfigPath(homeDirectory));\n const repositoryLayer =\n repoRoot === undefined\n ? { filePath: '', exists: false, hash: '', keys: [] }\n : readLayer(repositoryDoomConfigPath(repoRoot));\n // With no repository there is nothing to merge, so the global file stands as\n // the effective config on its own.\n const effective =\n repoRoot === undefined\n ? mergeDoomConfigs(readConfig(globalDoomConfigPath(homeDirectory)), { projectTrust: 'ask' })\n : loadDoomConfig(repoRoot, homeDirectory);\n const sets = (layer: DoomConfigLayer, keyPath: readonly string[]): boolean => {\n const dotted = keyPath.join('.');\n return layer.keys.some((key) => key === dotted || key.startsWith(`${dotted}.`));\n };\n return {\n globalFile: globalLayer,\n repositoryFile: repositoryLayer,\n effective,\n originOf(keyPath: readonly string[]): ConfigValueOrigin {\n const scope = configScopeOf(keyPath);\n if (scope !== 'global' && sets(repositoryLayer, keyPath)) return 'repository';\n if (scope !== 'repository' && sets(globalLayer, keyPath)) return 'global';\n return 'default';\n },\n valueAt: (keyPath: readonly string[]) => valueAtKeyPath(effective, keyPath),\n };\n}\n"],"mappings":"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"}
@@ -1,10 +1,19 @@
1
- import { DoomConfig, DoomConfigLayers } from "../types/config.cjs";
1
+ import { DoomConfig, DoomConfigLayers, LenientParseResult } from "../types/config.cjs";
2
2
  //#region src/adapters/config.d.ts
3
3
  export declare function globalDoomConfigDirectory(homeDirectory?: string): string;
4
4
  export declare function globalDoomConfigPath(homeDirectory?: string): string;
5
5
  export declare function resolvePlanningPlansDirectory(configured: string | undefined, repoRoot: string, homeDirectory?: string): string;
6
6
  export declare function repositoryDoomConfigPath(repoRoot: string): string;
7
7
  export declare function loadDoomConfig(repoRoot: string, homeDirectory?: string): DoomConfig;
8
+ /**
9
+ * Loads the merged config, tolerating unknown keys in either file.
10
+ *
11
+ * Only `doompi sync` uses this. Everything that runs during a session keeps
12
+ * `loadDoomConfig`, because a key a running extension cannot understand is
13
+ * better reported than silently ignored; a build that refuses to start over
14
+ * the same key is not.
15
+ */
16
+ export declare function loadDoomConfigLenient(repoRoot: string, homeDirectory?: string): LenientParseResult;
8
17
  export declare function loadDoomConfigAsync(repoRoot: string, homeDirectory?: string): Promise<DoomConfig>;
9
18
  /**
10
19
  * The two config files kept apart, with the merge alongside them.
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.cts","names":[],"sources":["../../src/adapters/config.ts"],"mappings":";;wBAmCgB,0BAA0B;wBAI1B,qBAAqB;wBAIrB,8BACd,gCACA,kBACA;wBAWc,yBAAyB;wBAIzB,eAAe,kBAAkB,yBAA+B;wBAO1D,oBAAoB,kBAAkB,yBAA+B,QAAQ;;;;;;;;;;;;;;;wBA8CnF,qBAAqB,8BAA8B,yBAA+B"}
1
+ {"version":3,"file":"config.d.cts","names":[],"sources":["../../src/adapters/config.ts"],"mappings":";;wBAmCgB,0BAA0B;wBAI1B,qBAAqB;wBAIrB,8BACd,gCACA,kBACA;wBAWc,yBAAyB;wBAIzB,eAAe,kBAAkB,yBAA+B;;;;;;;;;wBAehE,sBAAsB,kBAAkB,yBAA+B;wBAajE,oBAAoB,kBAAkB,yBAA+B,QAAQ;;;;;;;;;;;;;;;wBA8CnF,qBAAqB,8BAA8B,yBAA+B"}
@@ -1,10 +1,19 @@
1
- import { DoomConfig, DoomConfigLayers } from "../types/config.mjs";
1
+ import { DoomConfig, DoomConfigLayers, LenientParseResult } from "../types/config.mjs";
2
2
  //#region src/adapters/config.d.ts
3
3
  export declare function globalDoomConfigDirectory(homeDirectory?: string): string;
4
4
  export declare function globalDoomConfigPath(homeDirectory?: string): string;
5
5
  export declare function resolvePlanningPlansDirectory(configured: string | undefined, repoRoot: string, homeDirectory?: string): string;
6
6
  export declare function repositoryDoomConfigPath(repoRoot: string): string;
7
7
  export declare function loadDoomConfig(repoRoot: string, homeDirectory?: string): DoomConfig;
8
+ /**
9
+ * Loads the merged config, tolerating unknown keys in either file.
10
+ *
11
+ * Only `doompi sync` uses this. Everything that runs during a session keeps
12
+ * `loadDoomConfig`, because a key a running extension cannot understand is
13
+ * better reported than silently ignored; a build that refuses to start over
14
+ * the same key is not.
15
+ */
16
+ export declare function loadDoomConfigLenient(repoRoot: string, homeDirectory?: string): LenientParseResult;
8
17
  export declare function loadDoomConfigAsync(repoRoot: string, homeDirectory?: string): Promise<DoomConfig>;
9
18
  /**
10
19
  * The two config files kept apart, with the merge alongside them.
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.mts","names":[],"sources":["../../src/adapters/config.ts"],"mappings":";;wBAmCgB,0BAA0B;wBAI1B,qBAAqB;wBAIrB,8BACd,gCACA,kBACA;wBAWc,yBAAyB;wBAIzB,eAAe,kBAAkB,yBAA+B;wBAO1D,oBAAoB,kBAAkB,yBAA+B,QAAQ;;;;;;;;;;;;;;;wBA8CnF,qBAAqB,8BAA8B,yBAA+B"}
1
+ {"version":3,"file":"config.d.mts","names":[],"sources":["../../src/adapters/config.ts"],"mappings":";;wBAmCgB,0BAA0B;wBAI1B,qBAAqB;wBAIrB,8BACd,gCACA,kBACA;wBAWc,yBAAyB;wBAIzB,eAAe,kBAAkB,yBAA+B;;;;;;;;;wBAehE,sBAAsB,kBAAkB,yBAA+B;wBAajE,oBAAoB,kBAAkB,yBAA+B,QAAQ;;;;;;;;;;;;;;;wBA8CnF,qBAAqB,8BAA8B,yBAA+B"}
@@ -1,2 +1,2 @@
1
- import{configLeafKeys as e,configScopeOf as t,mergeDoomConfigs as n,parseDoomConfig as r,valueAtKeyPath as i}from"../services/configPolicy.mjs";import{createHash as a}from"node:crypto";import o from"node:fs";import s from"node:path";import c from"node:os";import{parse as l}from"yaml";const u=`.doom`,d=`config.yaml`;function f(e){return o.existsSync(e)?r(o.readFileSync(e,`utf8`),e):{projectTrust:`ask`}}async function p(e){try{return r(await o.promises.readFile(e,`utf8`),e)}catch(e){if(e.code===`ENOENT`)return{projectTrust:`ask`};throw e}}function m(e=c.homedir()){return s.join(e,`.pi`,u)}function h(e=c.homedir()){return s.join(m(e),d)}function g(e,t,n=c.homedir()){if(!e)return s.join(n,`.pi`,`plans`);if(e===`~`)return n;if(e.startsWith(`~/`))return s.resolve(n,e.slice(2));if(e.startsWith(`~`))throw Error(`Doom planning plansDirectory supports only '~' or '~/' home aliases`);return s.isAbsolute(e)?s.normalize(e):s.resolve(t,e)}function _(e){return s.join(e,u,d)}function v(e,t=c.homedir()){return n(f(h(t)),f(_(e)))}async function y(e,t=c.homedir()){let r=await p(h(t)),i=await p(_(e));return n(r,i)}function b(t){let n;try{n=o.readFileSync(t,`utf8`)}catch(e){if(e.code===`ENOENT`)return{filePath:t,exists:!1,hash:``,keys:[]};throw e}let r=l(n)??{};return{filePath:t,exists:!0,hash:a(`sha256`).update(n,`utf8`).digest(`hex`),keys:e(r)}}function x(e,r=c.homedir()){let a=b(h(r)),o=e===void 0?{filePath:``,exists:!1,hash:``,keys:[]}:b(_(e)),s=e===void 0?n(f(h(r)),{projectTrust:`ask`}):v(e,r),l=(e,t)=>{let n=t.join(`.`);return e.keys.some(e=>e===n||e.startsWith(`${n}.`))};return{globalFile:a,repositoryFile:o,effective:s,originOf(e){let n=t(e);return n!==`global`&&l(o,e)?`repository`:n!==`repository`&&l(a,e)?`global`:`default`},valueAt:e=>i(s,e)}}export{m as globalDoomConfigDirectory,h as globalDoomConfigPath,v as loadDoomConfig,y as loadDoomConfigAsync,x as loadDoomConfigLayers,_ as repositoryDoomConfigPath,g as resolvePlanningPlansDirectory};
1
+ import{configLeafKeys as e,configScopeOf as t,mergeDoomConfigs as n,parseDoomConfig as r,valueAtKeyPath as i}from"../services/configPolicy.mjs";import{createHash as a}from"node:crypto";import o from"node:fs";import s from"node:path";import c from"node:os";import{parse as l}from"yaml";const u=`.doom`,d=`config.yaml`;function f(e){return o.existsSync(e)?r(o.readFileSync(e,`utf8`),e):{projectTrust:`ask`}}async function p(e){try{return r(await o.promises.readFile(e,`utf8`),e)}catch(e){if(e.code===`ENOENT`)return{projectTrust:`ask`};throw e}}function m(e=c.homedir()){return s.join(e,`.pi`,u)}function h(e=c.homedir()){return s.join(m(e),d)}function g(e,t,n=c.homedir()){if(!e)return s.join(n,`.pi`,`plans`);if(e===`~`)return n;if(e.startsWith(`~/`))return s.resolve(n,e.slice(2));if(e.startsWith(`~`))throw Error(`Doom planning plansDirectory supports only '~' or '~/' home aliases`);return s.isAbsolute(e)?s.normalize(e):s.resolve(t,e)}function _(e){return s.join(e,u,d)}function v(e,t=c.homedir()){return n(f(h(t)),f(_(e)))}function y(e,t=c.homedir()){let i=e=>o.existsSync(e)?r(o.readFileSync(e,`utf8`),e,{lenient:!0}):{config:{projectTrust:`ask`},diagnostics:[]},a=i(h(t)),s=i(_(e)),l=[...a.diagnostics,...s.diagnostics];return{config:n(a.config,s.config),diagnostics:l}}async function b(e,t=c.homedir()){let r=await p(h(t)),i=await p(_(e));return n(r,i)}function x(t){let n;try{n=o.readFileSync(t,`utf8`)}catch(e){if(e.code===`ENOENT`)return{filePath:t,exists:!1,hash:``,keys:[]};throw e}let r=l(n)??{};return{filePath:t,exists:!0,hash:a(`sha256`).update(n,`utf8`).digest(`hex`),keys:e(r)}}function S(e,r=c.homedir()){let a=x(h(r)),o=e===void 0?{filePath:``,exists:!1,hash:``,keys:[]}:x(_(e)),s=e===void 0?n(f(h(r)),{projectTrust:`ask`}):v(e,r),l=(e,t)=>{let n=t.join(`.`);return e.keys.some(e=>e===n||e.startsWith(`${n}.`))};return{globalFile:a,repositoryFile:o,effective:s,originOf(e){let n=t(e);return n!==`global`&&l(o,e)?`repository`:n!==`repository`&&l(a,e)?`global`:`default`},valueAt:e=>i(s,e)}}export{m as globalDoomConfigDirectory,h as globalDoomConfigPath,v as loadDoomConfig,b as loadDoomConfigAsync,S as loadDoomConfigLayers,y as loadDoomConfigLenient,_ as repositoryDoomConfigPath,g as resolvePlanningPlansDirectory};
2
2
  //# sourceMappingURL=config.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.mjs","names":["parseYaml"],"sources":["../../src/adapters/config.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport {\n configLeafKeys,\n configScopeOf,\n mergeDoomConfigs,\n parseDoomConfig,\n valueAtKeyPath,\n} from '../services/configPolicy.ts';\nimport type { DoomConfig } from '../types/config.ts';\nimport type { ConfigValueOrigin, DoomConfigLayer, DoomConfigLayers } from '../types/config.ts';\n\nconst DOOM_DIR = '.doom';\nconst CONFIG_FILE = 'config.yaml';\nconst GLOBAL_CONFIG_DIR = '.pi';\nconst PLANS_DIR = 'plans';\n\nfunction readConfig(filePath: string): DoomConfig {\n return fs.existsSync(filePath)\n ? parseDoomConfig(fs.readFileSync(filePath, 'utf8'), filePath)\n : { projectTrust: 'ask' };\n}\n\nasync function readConfigAsync(filePath: string): Promise<DoomConfig> {\n try {\n return parseDoomConfig(await fs.promises.readFile(filePath, 'utf8'), filePath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { projectTrust: 'ask' };\n throw error;\n }\n}\n\nexport function globalDoomConfigDirectory(homeDirectory = os.homedir()): string {\n return path.join(homeDirectory, GLOBAL_CONFIG_DIR, DOOM_DIR);\n}\n\nexport function globalDoomConfigPath(homeDirectory = os.homedir()): string {\n return path.join(globalDoomConfigDirectory(homeDirectory), CONFIG_FILE);\n}\n\nexport function resolvePlanningPlansDirectory(\n configured: string | undefined,\n repoRoot: string,\n homeDirectory = os.homedir(),\n): string {\n if (!configured) return path.join(homeDirectory, GLOBAL_CONFIG_DIR, PLANS_DIR);\n if (configured === '~') return homeDirectory;\n if (configured.startsWith('~/')) return path.resolve(homeDirectory, configured.slice(2));\n if (configured.startsWith('~')) {\n throw new Error(\"Doom planning plansDirectory supports only '~' or '~/' home aliases\");\n }\n return path.isAbsolute(configured) ? path.normalize(configured) : path.resolve(repoRoot, configured);\n}\n\nexport function repositoryDoomConfigPath(repoRoot: string): string {\n return path.join(repoRoot, DOOM_DIR, CONFIG_FILE);\n}\n\nexport function loadDoomConfig(repoRoot: string, homeDirectory = os.homedir()): DoomConfig {\n return mergeDoomConfigs(\n readConfig(globalDoomConfigPath(homeDirectory)),\n readConfig(repositoryDoomConfigPath(repoRoot)),\n );\n}\n\nexport async function loadDoomConfigAsync(repoRoot: string, homeDirectory = os.homedir()): Promise<DoomConfig> {\n const globalConfig = await readConfigAsync(globalDoomConfigPath(homeDirectory));\n const repositoryConfig = await readConfigAsync(repositoryDoomConfigPath(repoRoot));\n return mergeDoomConfigs(globalConfig, repositoryConfig);\n}\n\n/**\n * Reads one config file as a layer: what it is, what it sets, and the bytes it\n * held. A file that does not exist is a layer that sets nothing, which is a\n * normal state rather than an error.\n */\nfunction readLayer(filePath: string): DoomConfigLayer {\n let text: string;\n try {\n text = fs.readFileSync(filePath, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return { filePath, exists: false, hash: '', keys: [] };\n }\n throw error;\n }\n // The raw document, not the parsed config: this is the one read that has to\n // distinguish a key the file set from a key the parser defaulted.\n const document = (parseYaml(text) ?? {}) as unknown;\n return {\n filePath,\n exists: true,\n hash: createHash('sha256').update(text, 'utf8').digest('hex'),\n keys: configLeafKeys(document),\n };\n}\n\n/**\n * The two config files kept apart, with the merge alongside them.\n *\n * `repoRoot` is optional because the global file is complete on its own: a\n * settings surface with no repository in view still has settings to show, and\n * demanding one would leave it with nothing to render rather than with the\n * global half.\n *\n * The origin answer respects the merge's own per-field rules rather than simply\n * preferring the repository: `editor` is read from the global side whatever the\n * repository says, and `projectTrust` from the repository side whatever the\n * global one says. Reporting a file as the source of a value the merge discards\n * is the exact confusion this exists to remove.\n */\nexport function loadDoomConfigLayers(repoRoot: string | undefined, homeDirectory = os.homedir()): DoomConfigLayers {\n const globalLayer = readLayer(globalDoomConfigPath(homeDirectory));\n const repositoryLayer =\n repoRoot === undefined\n ? { filePath: '', exists: false, hash: '', keys: [] }\n : readLayer(repositoryDoomConfigPath(repoRoot));\n // With no repository there is nothing to merge, so the global file stands as\n // the effective config on its own.\n const effective =\n repoRoot === undefined\n ? mergeDoomConfigs(readConfig(globalDoomConfigPath(homeDirectory)), { projectTrust: 'ask' })\n : loadDoomConfig(repoRoot, homeDirectory);\n const sets = (layer: DoomConfigLayer, keyPath: readonly string[]): boolean => {\n const dotted = keyPath.join('.');\n return layer.keys.some((key) => key === dotted || key.startsWith(`${dotted}.`));\n };\n return {\n globalFile: globalLayer,\n repositoryFile: repositoryLayer,\n effective,\n originOf(keyPath: readonly string[]): ConfigValueOrigin {\n const scope = configScopeOf(keyPath);\n if (scope !== 'global' && sets(repositoryLayer, keyPath)) return 'repository';\n if (scope !== 'repository' && sets(globalLayer, keyPath)) return 'global';\n return 'default';\n },\n valueAt: (keyPath: readonly string[]) => valueAtKeyPath(effective, keyPath),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"config.mjs","names":["parseYaml"],"sources":["../../src/adapters/config.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport {\n configLeafKeys,\n configScopeOf,\n mergeDoomConfigs,\n parseDoomConfig,\n valueAtKeyPath,\n} from '../services/configPolicy.ts';\nimport type { ConfigDiagnostic, DoomConfig, LenientParseResult } from '../types/config.ts';\nimport type { ConfigValueOrigin, DoomConfigLayer, DoomConfigLayers } from '../types/config.ts';\n\nconst DOOM_DIR = '.doom';\nconst CONFIG_FILE = 'config.yaml';\nconst GLOBAL_CONFIG_DIR = '.pi';\nconst PLANS_DIR = 'plans';\n\nfunction readConfig(filePath: string): DoomConfig {\n return fs.existsSync(filePath)\n ? parseDoomConfig(fs.readFileSync(filePath, 'utf8'), filePath)\n : { projectTrust: 'ask' };\n}\n\nasync function readConfigAsync(filePath: string): Promise<DoomConfig> {\n try {\n return parseDoomConfig(await fs.promises.readFile(filePath, 'utf8'), filePath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { projectTrust: 'ask' };\n throw error;\n }\n}\n\nexport function globalDoomConfigDirectory(homeDirectory = os.homedir()): string {\n return path.join(homeDirectory, GLOBAL_CONFIG_DIR, DOOM_DIR);\n}\n\nexport function globalDoomConfigPath(homeDirectory = os.homedir()): string {\n return path.join(globalDoomConfigDirectory(homeDirectory), CONFIG_FILE);\n}\n\nexport function resolvePlanningPlansDirectory(\n configured: string | undefined,\n repoRoot: string,\n homeDirectory = os.homedir(),\n): string {\n if (!configured) return path.join(homeDirectory, GLOBAL_CONFIG_DIR, PLANS_DIR);\n if (configured === '~') return homeDirectory;\n if (configured.startsWith('~/')) return path.resolve(homeDirectory, configured.slice(2));\n if (configured.startsWith('~')) {\n throw new Error(\"Doom planning plansDirectory supports only '~' or '~/' home aliases\");\n }\n return path.isAbsolute(configured) ? path.normalize(configured) : path.resolve(repoRoot, configured);\n}\n\nexport function repositoryDoomConfigPath(repoRoot: string): string {\n return path.join(repoRoot, DOOM_DIR, CONFIG_FILE);\n}\n\nexport function loadDoomConfig(repoRoot: string, homeDirectory = os.homedir()): DoomConfig {\n return mergeDoomConfigs(\n readConfig(globalDoomConfigPath(homeDirectory)),\n readConfig(repositoryDoomConfigPath(repoRoot)),\n );\n}\n\n/**\n * Loads the merged config, tolerating unknown keys in either file.\n *\n * Only `doompi sync` uses this. Everything that runs during a session keeps\n * `loadDoomConfig`, because a key a running extension cannot understand is\n * better reported than silently ignored; a build that refuses to start over\n * the same key is not.\n */\nexport function loadDoomConfigLenient(repoRoot: string, homeDirectory = os.homedir()): LenientParseResult {\n const read = (filePath: string): LenientParseResult =>\n fs.existsSync(filePath)\n ? parseDoomConfig(fs.readFileSync(filePath, 'utf8'), filePath, { lenient: true })\n : { config: { projectTrust: 'ask' }, diagnostics: [] };\n const globalConfig = read(globalDoomConfigPath(homeDirectory));\n const repositoryConfig = read(repositoryDoomConfigPath(repoRoot));\n const diagnostics: ConfigDiagnostic[] = [...globalConfig.diagnostics, ...repositoryConfig.diagnostics];\n // mergeDoomConfigs stays strict on purpose: its errors are cross-file scope\n // violations on known keys, which is a different fault from an unknown key.\n return { config: mergeDoomConfigs(globalConfig.config, repositoryConfig.config), diagnostics };\n}\n\nexport async function loadDoomConfigAsync(repoRoot: string, homeDirectory = os.homedir()): Promise<DoomConfig> {\n const globalConfig = await readConfigAsync(globalDoomConfigPath(homeDirectory));\n const repositoryConfig = await readConfigAsync(repositoryDoomConfigPath(repoRoot));\n return mergeDoomConfigs(globalConfig, repositoryConfig);\n}\n\n/**\n * Reads one config file as a layer: what it is, what it sets, and the bytes it\n * held. A file that does not exist is a layer that sets nothing, which is a\n * normal state rather than an error.\n */\nfunction readLayer(filePath: string): DoomConfigLayer {\n let text: string;\n try {\n text = fs.readFileSync(filePath, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return { filePath, exists: false, hash: '', keys: [] };\n }\n throw error;\n }\n // The raw document, not the parsed config: this is the one read that has to\n // distinguish a key the file set from a key the parser defaulted.\n const document = (parseYaml(text) ?? {}) as unknown;\n return {\n filePath,\n exists: true,\n hash: createHash('sha256').update(text, 'utf8').digest('hex'),\n keys: configLeafKeys(document),\n };\n}\n\n/**\n * The two config files kept apart, with the merge alongside them.\n *\n * `repoRoot` is optional because the global file is complete on its own: a\n * settings surface with no repository in view still has settings to show, and\n * demanding one would leave it with nothing to render rather than with the\n * global half.\n *\n * The origin answer respects the merge's own per-field rules rather than simply\n * preferring the repository: `editor` is read from the global side whatever the\n * repository says, and `projectTrust` from the repository side whatever the\n * global one says. Reporting a file as the source of a value the merge discards\n * is the exact confusion this exists to remove.\n */\nexport function loadDoomConfigLayers(repoRoot: string | undefined, homeDirectory = os.homedir()): DoomConfigLayers {\n const globalLayer = readLayer(globalDoomConfigPath(homeDirectory));\n const repositoryLayer =\n repoRoot === undefined\n ? { filePath: '', exists: false, hash: '', keys: [] }\n : readLayer(repositoryDoomConfigPath(repoRoot));\n // With no repository there is nothing to merge, so the global file stands as\n // the effective config on its own.\n const effective =\n repoRoot === undefined\n ? mergeDoomConfigs(readConfig(globalDoomConfigPath(homeDirectory)), { projectTrust: 'ask' })\n : loadDoomConfig(repoRoot, homeDirectory);\n const sets = (layer: DoomConfigLayer, keyPath: readonly string[]): boolean => {\n const dotted = keyPath.join('.');\n return layer.keys.some((key) => key === dotted || key.startsWith(`${dotted}.`));\n };\n return {\n globalFile: globalLayer,\n repositoryFile: repositoryLayer,\n effective,\n originOf(keyPath: readonly string[]): ConfigValueOrigin {\n const scope = configScopeOf(keyPath);\n if (scope !== 'global' && sets(repositoryLayer, keyPath)) return 'repository';\n if (scope !== 'repository' && sets(globalLayer, keyPath)) return 'global';\n return 'default';\n },\n valueAt: (keyPath: readonly string[]) => valueAtKeyPath(effective, keyPath),\n };\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./layeredConfig.cjs");let n=require("node:path");n=e.__toESM(n,1);let r=require("node:os");r=e.__toESM(r,1);const i=`modes.yaml`,a=n.default.join(t.DOOM_DIR,i),o=[`./`,`../`],s=[`packages`];function c(e){return n.default.isAbsolute(e)||o.some(t=>e.startsWith(t))}const l=[`name`,`optional`,`config`],u=[`description`,`layers`];function d(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function f(e,t,n){let r=Object.keys(e).filter(e=>!t.includes(e));if(r.length>0)throw Error(`${n} has unsupported field(s): ${r.join(`, `)}`)}function p(e,t,n){let r=`Layer "${e}" package[${n}] in ${a}`;if(typeof t==`string`){if(!t.trim())throw Error(`${r} must be a non-empty string or mapping`);return t.trim()}if(!d(t))throw Error(`${r} must be a non-empty string or mapping`);if(f(t,l,r),typeof t.name!=`string`||!t.name.trim())throw Error(`${r}.name must be a non-empty string`);if(t.optional!==void 0&&typeof t.optional!=`boolean`)throw Error(`${r}.optional must be a boolean`);if(t.config!==void 0&&!d(t.config))throw Error(`${r}.config must be a mapping`);return{name:t.name.trim(),...t.optional===!0?{optional:!0}:{},...t.config?{config:{...t.config}}:{}}}function m(e,t){if(t===void 0)return;let n=`Layer "${e}" extensions in ${a}`;if(!Array.isArray(t))throw Error(`${n} must be an array`);return t.map((e,t)=>{if(typeof e!=`string`||!e.trim())throw Error(`${n}[${t}] must be a non-empty string`);return e.trim()})}function h(e,t){if(t!==void 0){if(!Array.isArray(t))throw Error(`Layer "${e}" packages in ${a} must be an array`);return t.map((t,n)=>p(e,t,n))}}function g(e){if(typeof e!=`string`||!e.trim())throw Error(`defaultMajorMode in ${a} must be a non-empty string`);return e.trim()}function _(e,t){let n=`Major mode "${e}" layers in ${a}`;if(!Array.isArray(t))throw Error(`${n} must be an array`);return t.map((e,t)=>{if(typeof e!=`string`||!e.trim())throw Error(`${n}[${t}] must be a non-empty string`);return e.trim()})}function v(e){return e.length>0?`Uses layers: ${e.join(`, `)}.`:`Uses Doom Pi core extensions only.`}function y(e,t){if(Array.isArray(t)){let n=_(e,t);return{description:v(n),layers:n}}let n=`Major mode "${e}" in ${a}`;if(!d(t))throw Error(`${n} must be a mapping with description and layers`);if(f(t,u,n),typeof t.description!=`string`||!t.description.trim())throw Error(`${n}.description must be a non-empty string`);return{description:t.description.trim(),layers:_(e,t.layers)}}function b(e){if(e===void 0)return{};if(!d(e))throw Error(`majorMode in ${a} must be a mapping`);return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,t===null?null:y(e,t)]))}function x(e,t){if(!d(t))throw Error(`Layer "${e}" in ${a} must be a mapping`);if(`targets`in t)throw Error(`Layer "${e}" in ${a} uses removed field "targets"; select availability through major modes`);if(`config`in t)throw Error(`Layer "${e}" in ${a} uses unsupported field "config"; move it under the package entry that owns it`);let n=m(e,t.extensions),r=h(e,t.packages);return{...t,...n===void 0?{}:{extensions:n},...r===void 0?{}:{packages:r}}}function S(e){if(e===void 0)return{};if(!d(e))throw Error(`layers in ${a} must be a mapping`);return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,t===null?null:x(e,t)]))}function C(e){let t=`default in ${a}`;if(!d(e))throw Error(`${t} must be a mapping with packages`);if(f(e,s,t),!Object.hasOwn(e,`packages`))throw Error(`Layer "default" packages in ${a} must be an array`);return{packages:h(`default`,e.packages)}}function w(e,n=r.default.homedir()){let o=t.readDoomConfigSources(i,e,n),s,c={},l={},u={},d=`copilot`,f,p=!1;for(let e of o){Object.hasOwn(e.document,`default`)&&(s={...C(e.document.default),baseDirectory:e.baseDirectory,filePath:e.filePath});for(let[t,n]of Object.entries(S(e.document.layers))){if(n===null){delete c[t];continue}c[t]={...n,baseDirectory:e.baseDirectory,filePath:e.filePath}}Object.hasOwn(e.document,`defaultMajorMode`)&&(d=g(e.document.defaultMajorMode),f={filePath:e.filePath,baseDirectory:e.baseDirectory},p=!0);for(let[t,n]of Object.entries(b(e.document.majorMode))){if(n===null){delete l[t],delete u[t];continue}l[t]=n,u[t]={filePath:e.filePath,baseDirectory:e.baseDirectory}}}for(let[e,t]of Object.entries(l))for(let n of t.layers)if(!c[n])throw Error(`Unknown layer "${n}" in majorMode.${e} of ${a}`);if(p&&!Object.hasOwn(l,d))throw Error(`Unknown default major mode "${d}" in ${a}`);return{...s?{default:s}:{},layers:c,defaultMajorMode:d,...f?{defaultMajorModeSource:f}:{},majorMode:l,majorModeSources:u}}function T(e,t,n){let r=n.trim();if(!r)throw Error(`Package name must be a non-empty string`);let i=[],a=[];e.default&&a.push({name:`default`,definition:e.default});for(let n of t){let t=e.layers[n];if(!t)throw Error(`Unknown layer: ${n}`);a.push({name:n,definition:t})}for(let{name:e,definition:t}of a)for(let n of t.packages??[])typeof n!=`string`&&n.config!==void 0&&(n.name===r||n.name.startsWith(`${r}/`))&&i.push({layer:e,specifier:n.name,config:{...n.config},baseDirectory:t.baseDirectory,...t.filePath?{filePath:t.filePath}:{}});return i}function E(e,t){if(!Object.hasOwn(e.majorMode,t))throw Error(`Unknown major mode: ${t}. Known major modes: ${Object.keys(e.majorMode).sort().join(`, `)}`);return[...e.majorMode[t].layers]}function D(e,t,n){let r=e.localEntries?.(t,n);if(r!==void 0)return r;let i=e.localEntry(t,n);return i?[i]:void 0}function O(e,t,n){let r=e.layers[t];if(!r)throw Error(`Unknown layer: ${t}`);let i=[];for(let e of r.extensions??[]){if(!c(e)){i.push(n.ownEntry(e));continue}let a=D(n,e,r.baseDirectory);if(!a?.length)throw Error(`Layer "${t}" extension "${e}" does not resolve under ${r.baseDirectory}. Point it at a Pi extension file or directory.`);i.push(...a)}for(let e of r.packages??[]){let a=typeof e==`string`?e:e.name,o=typeof e!=`string`&&e.optional===!0;if(c(a)){let e=D(n,a,r.baseDirectory);if(e?.length)i.push(...e);else if(!o)throw Error(`Layer "${t}" package "${a}" does not resolve under ${r.baseDirectory}. Point it at an extension file, or a directory whose package.json declares pi.extensions.`);continue}if(o){let e=n.optionalPackageEntries?.(a);if(e===void 0){let t=n.optionalPackageEntry(a);e=t?[t]:void 0}e&&i.push(...e);continue}let s=n.packageEntries?.(a)??[n.packageEntry(a)];if(s.length===0)throw Error(`Layer "${t}" package "${a}" declares no Pi extensions.`);i.push(...s)}return i}function k(e,t){let n=new Set;for(let r of t)for(let t of e.layers[r]?.hookGroups??[])n.add(t);return[...n]}function A(e,t,n){return n?[...t]:t.filter(t=>(e.layers[t]?.hookGroups?.length??0)===0)}exports.filterHookDisabledLayers=A,exports.isLocalPackageSpecifier=c,exports.layerEntries=O,exports.layerHookGroups=k,exports.loadMajorModesConfig=w,exports.resolveLayers=E,exports.resolvePackageConfigurations=T;
1
+ const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./layeredConfig.cjs");let n=require("node:path");n=e.__toESM(n,1);let r=require("node:os");r=e.__toESM(r,1);const i=`modes.yaml`,a=n.default.join(t.DOOM_DIR,i),o=[`./`,`../`],s=[`packages`];function c(e){return n.default.isAbsolute(e)||o.some(t=>e.startsWith(t))}const l=[`name`,`optional`,`config`],u=[`description`,`layers`];function d(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}let f;function p(e,t,n){let r=Object.keys(e).filter(e=>!t.includes(e));if(r.length!==0){if(f!==void 0){for(let e of r)f.push({filePath:a,path:e,message:`${n} has unsupported field`});return}throw Error(`${n} has unsupported field(s): ${r.join(`, `)}`)}}function m(e,t,n){let r=`Layer "${e}" package[${n}] in ${a}`;if(typeof t==`string`){if(!t.trim())throw Error(`${r} must be a non-empty string or mapping`);return t.trim()}if(!d(t))throw Error(`${r} must be a non-empty string or mapping`);if(p(t,l,r),typeof t.name!=`string`||!t.name.trim())throw Error(`${r}.name must be a non-empty string`);if(t.optional!==void 0&&typeof t.optional!=`boolean`)throw Error(`${r}.optional must be a boolean`);if(t.config!==void 0&&!d(t.config))throw Error(`${r}.config must be a mapping`);return{name:t.name.trim(),...t.optional===!0?{optional:!0}:{},...t.config?{config:{...t.config}}:{}}}function h(e,t){if(t===void 0)return;let n=`Layer "${e}" extensions in ${a}`;if(!Array.isArray(t))throw Error(`${n} must be an array`);return t.map((e,t)=>{if(typeof e!=`string`||!e.trim())throw Error(`${n}[${t}] must be a non-empty string`);return e.trim()})}function g(e,t){if(t!==void 0){if(!Array.isArray(t))throw Error(`Layer "${e}" packages in ${a} must be an array`);return t.map((t,n)=>m(e,t,n))}}function _(e){if(typeof e!=`string`||!e.trim())throw Error(`defaultMajorMode in ${a} must be a non-empty string`);return e.trim()}function v(e,t){let n=`Major mode "${e}" layers in ${a}`;if(!Array.isArray(t))throw Error(`${n} must be an array`);return t.map((e,t)=>{if(typeof e!=`string`||!e.trim())throw Error(`${n}[${t}] must be a non-empty string`);return e.trim()})}function y(e){return e.length>0?`Uses layers: ${e.join(`, `)}.`:`Uses Doom Pi core extensions only.`}function b(e,t){if(Array.isArray(t)){let n=v(e,t);return{description:y(n),layers:n}}let n=`Major mode "${e}" in ${a}`;if(!d(t))throw Error(`${n} must be a mapping with description and layers`);if(p(t,u,n),typeof t.description!=`string`||!t.description.trim())throw Error(`${n}.description must be a non-empty string`);return{description:t.description.trim(),layers:v(e,t.layers)}}function x(e){if(e===void 0)return{};if(!d(e))throw Error(`majorMode in ${a} must be a mapping`);return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,t===null?null:b(e,t)]))}function S(e,t){if(!d(t))throw Error(`Layer "${e}" in ${a} must be a mapping`);if(`targets`in t)throw Error(`Layer "${e}" in ${a} uses removed field "targets"; select availability through major modes`);if(`config`in t)throw Error(`Layer "${e}" in ${a} uses unsupported field "config"; move it under the package entry that owns it`);let n=h(e,t.extensions),r=g(e,t.packages);return{...t,...n===void 0?{}:{extensions:n},...r===void 0?{}:{packages:r}}}function C(e){if(e===void 0)return{};if(!d(e))throw Error(`layers in ${a} must be a mapping`);return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,t===null?null:S(e,t)]))}function w(e){let t=`default in ${a}`;if(!d(e))throw Error(`${t} must be a mapping with packages`);if(p(e,s,t),!Object.hasOwn(e,`packages`))throw Error(`Layer "default" packages in ${a} must be an array`);return{packages:g(`default`,e.packages)}}function T(e,n=r.default.homedir()){let o=t.readDoomConfigSources(i,e,n),s,c={},l={},u={},d=`copilot`,f,p=!1;for(let e of o){Object.hasOwn(e.document,`default`)&&(s={...w(e.document.default),baseDirectory:e.baseDirectory,filePath:e.filePath});for(let[t,n]of Object.entries(C(e.document.layers))){if(n===null){delete c[t];continue}c[t]={...n,baseDirectory:e.baseDirectory,filePath:e.filePath}}Object.hasOwn(e.document,`defaultMajorMode`)&&(d=_(e.document.defaultMajorMode),f={filePath:e.filePath,baseDirectory:e.baseDirectory},p=!0);for(let[t,n]of Object.entries(x(e.document.majorMode))){if(n===null){delete l[t],delete u[t];continue}l[t]=n,u[t]={filePath:e.filePath,baseDirectory:e.baseDirectory}}}for(let[e,t]of Object.entries(l))for(let n of t.layers)if(!c[n])throw Error(`Unknown layer "${n}" in majorMode.${e} of ${a}`);if(p&&!Object.hasOwn(l,d))throw Error(`Unknown default major mode "${d}" in ${a}`);return{...s?{default:s}:{},layers:c,defaultMajorMode:d,...f?{defaultMajorModeSource:f}:{},majorMode:l,majorModeSources:u}}function E(e,t,n){let r=n.trim();if(!r)throw Error(`Package name must be a non-empty string`);let i=[],a=[];e.default&&a.push({name:`default`,definition:e.default});for(let n of t){let t=e.layers[n];if(!t)throw Error(`Unknown layer: ${n}`);a.push({name:n,definition:t})}for(let{name:e,definition:t}of a)for(let n of t.packages??[])typeof n!=`string`&&n.config!==void 0&&(n.name===r||n.name.startsWith(`${r}/`))&&i.push({layer:e,specifier:n.name,config:{...n.config},baseDirectory:t.baseDirectory,...t.filePath?{filePath:t.filePath}:{}});return i}function D(e,t){if(!Object.hasOwn(e.majorMode,t))throw Error(`Unknown major mode: ${t}. Known major modes: ${Object.keys(e.majorMode).sort().join(`, `)}`);return[...e.majorMode[t].layers]}function O(e,t,n){let r=e.localEntries?.(t,n);if(r!==void 0)return r;let i=e.localEntry(t,n);return i?[i]:void 0}function k(e,t,n){let r=e.layers[t];if(!r)throw Error(`Unknown layer: ${t}`);let i=[];for(let e of r.extensions??[]){if(!c(e)){i.push(n.ownEntry(e));continue}let a=O(n,e,r.baseDirectory);if(!a?.length)throw Error(`Layer "${t}" extension "${e}" does not resolve under ${r.baseDirectory}. Point it at a Pi extension file or directory.`);i.push(...a)}for(let e of r.packages??[]){let a=typeof e==`string`?e:e.name,o=typeof e!=`string`&&e.optional===!0;if(c(a)){let e=O(n,a,r.baseDirectory);if(e?.length)i.push(...e);else if(!o)throw Error(`Layer "${t}" package "${a}" does not resolve under ${r.baseDirectory}. Point it at an extension file, or a directory whose package.json declares pi.extensions.`);continue}if(o){let e=n.optionalPackageEntries?.(a);if(e===void 0){let t=n.optionalPackageEntry(a);e=t?[t]:void 0}e&&i.push(...e);continue}let s=n.packageEntries?.(a)??[n.packageEntry(a)];if(s.length===0)throw Error(`Layer "${t}" package "${a}" declares no Pi extensions.`);i.push(...s)}return i}function A(e,t){let n=new Set;for(let r of t)for(let t of e.layers[r]?.hookGroups??[])n.add(t);return[...n]}function j(e,t,n){return n?[...t]:t.filter(t=>(e.layers[t]?.hookGroups?.length??0)===0)}function M(e,t=r.default.homedir()){let n=[],i=f;f=n;try{return{config:T(e,t),diagnostics:n}}finally{f=i}}exports.filterHookDisabledLayers=j,exports.isLocalPackageSpecifier=c,exports.layerEntries=k,exports.layerHookGroups=A,exports.loadMajorModesConfig=T,exports.loadMajorModesConfigLenient=M,exports.resolveLayers=D,exports.resolvePackageConfigurations=E;
2
2
  //# sourceMappingURL=majorModes.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"majorModes.cjs","names":["path","DOOM_DIR","os","readDoomConfigSources"],"sources":["../../src/adapters/majorModes.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\nimport { DOOM_DIR, type DoomConfigProvenance, readDoomConfigSources } from './layeredConfig.ts';\n\nexport type LayerPackageConfig = Record<string, unknown>;\n\n/** One package selected by a layer, plus configuration owned by that package. */\nexport interface LayerPackage {\n name: string;\n optional?: boolean;\n config?: LayerPackageConfig;\n}\n\n/** One configured package found while walking selected layers in order. */\nexport interface ResolvedPackageConfiguration {\n layer: string;\n specifier: string;\n config: LayerPackageConfig;\n baseDirectory: string;\n filePath?: string;\n}\n\nexport interface LayerDefinition {\n /**\n * DoomPi built-in names, or Pi-compatible local extension paths.\n * Bare names resolve from DoomPi's own extension entries. Paths beginning\n * with `./` or `../`, plus absolute paths, resolve against the config that\n * declares the layer and may name a script file or extension directory.\n */\n extensions?: string[];\n /**\n * npm specifiers, or local package paths retained for compatibility.\n * Relative paths resolve against the repository root for `.doom/modes.yaml`\n * and against the global `.doom` directory for\n * `~/.pi/.doom/modes.yaml`.\n */\n packages?: Array<string | LayerPackage>;\n hookGroups?: string[];\n}\n\n/** A layer plus the root its relative extension and package paths resolve against. */\nexport interface ResolvedLayerDefinition extends LayerDefinition {\n baseDirectory: string;\n /** Absolute path of the modes file that declared this layer. */\n filePath?: string;\n}\n\nexport interface MajorModeDefinition {\n /** Human-readable purpose used by pickers and agent-facing catalogs. */\n description: string;\n /** Ordered layer names activated by this mode. */\n layers: string[];\n}\n\nexport interface MajorModesConfig {\n /** Unconditional package bundle loaded before packages from selected named layers. */\n default?: ResolvedLayerDefinition;\n layers: Record<string, ResolvedLayerDefinition>;\n /** The mode selected when neither a flag nor the environment names one. */\n defaultMajorMode: string;\n /** Source that last declared the default, absent for the built-in fallback. */\n defaultMajorModeSource?: DoomConfigProvenance;\n /** Named major modes, including their purpose and ordered layers. */\n majorMode: Record<string, MajorModeDefinition>;\n /** Declaring source for each surviving named mode. */\n majorModeSources?: Record<string, DoomConfigProvenance>;\n}\n\nexport interface LayerResolvers {\n ownEntry: (name: string) => string;\n packageEntry: (name: string) => string;\n optionalPackageEntry: (name: string) => string | undefined;\n /** Resolves every standard package.json pi.extensions entry for a package root. */\n packageEntries?: (name: string) => string[];\n /** Optional counterpart to packageEntries. */\n optionalPackageEntries?: (name: string) => string[] | undefined;\n /** Resolves one path-style layer entry against the config that declared it. */\n localEntry: (specifier: string, baseDirectory: string) => string | undefined;\n /** Resolves every Pi extension entry contributed by a local file or directory. */\n localEntries?: (specifier: string, baseDirectory: string) => string[] | undefined;\n}\n\ninterface ModesDocument {\n default?: unknown;\n layers?: unknown;\n defaultMajorMode?: unknown;\n majorMode?: unknown;\n}\n\nconst MODES_FILE = 'modes.yaml';\nconst FALLBACK_MAJOR_MODE = 'copilot';\nconst MAJOR_MODES_RELATIVE_PATH = path.join(DOOM_DIR, MODES_FILE);\nconst RELATIVE_PREFIXES = ['./', '../'] as const;\nconst DEFAULT_KEYS = ['packages'] as const;\n\n/**\n * Whether a layer value names a path rather than a built-in or installed package.\n *\n * The same rule Node applies to import specifiers, so a bare name is always a\n * logical name and a path always looks like one.\n */\nexport function isLocalPackageSpecifier(specifier: string): boolean {\n return path.isAbsolute(specifier) || RELATIVE_PREFIXES.some((prefix) => specifier.startsWith(prefix));\n}\nconst LAYER_PACKAGE_KEYS = ['name', 'optional', 'config'] as const;\nconst MAJOR_MODE_KEYS = ['description', 'layers'] as const;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction assertKnownKeys(value: Record<string, unknown>, keys: readonly string[], location: string): void {\n const unsupported = Object.keys(value).filter((key) => !keys.includes(key));\n if (unsupported.length > 0) {\n throw new Error(`${location} has unsupported field(s): ${unsupported.join(', ')}`);\n }\n}\n\nfunction parseLayerPackage(layerName: string, value: unknown, index: number): string | LayerPackage {\n const location = `Layer \"${layerName}\" package[${index}] in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (typeof value === 'string') {\n if (!value.trim()) throw new Error(`${location} must be a non-empty string or mapping`);\n return value.trim();\n }\n if (!isRecord(value)) throw new Error(`${location} must be a non-empty string or mapping`);\n assertKnownKeys(value, LAYER_PACKAGE_KEYS, location);\n if (typeof value.name !== 'string' || !value.name.trim()) {\n throw new Error(`${location}.name must be a non-empty string`);\n }\n if (value.optional !== undefined && typeof value.optional !== 'boolean') {\n throw new Error(`${location}.optional must be a boolean`);\n }\n if (value.config !== undefined && !isRecord(value.config)) {\n throw new Error(`${location}.config must be a mapping`);\n }\n return {\n name: value.name.trim(),\n ...(value.optional === true ? { optional: true } : {}),\n ...(value.config ? { config: { ...value.config } } : {}),\n };\n}\n\nfunction parseLayerExtensions(layerName: string, value: unknown): string[] | undefined {\n if (value === undefined) return undefined;\n const location = `Layer \"${layerName}\" extensions in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (!Array.isArray(value)) throw new Error(`${location} must be an array`);\n return value.map((entry, index) => {\n if (typeof entry !== 'string' || !entry.trim()) {\n throw new Error(`${location}[${index}] must be a non-empty string`);\n }\n return entry.trim();\n });\n}\n\nfunction parseLayerPackages(layerName: string, value: unknown): Array<string | LayerPackage> | undefined {\n if (value === undefined) return undefined;\n if (!Array.isArray(value)) {\n throw new Error(`Layer \"${layerName}\" packages in ${MAJOR_MODES_RELATIVE_PATH} must be an array`);\n }\n return value.map((entry, index) => parseLayerPackage(layerName, entry, index));\n}\n\nfunction parseDefaultMajorMode(value: unknown): string {\n if (typeof value !== 'string' || !value.trim()) {\n throw new Error(`defaultMajorMode in ${MAJOR_MODES_RELATIVE_PATH} must be a non-empty string`);\n }\n return value.trim();\n}\n\nfunction parseMajorModeLayers(modeName: string, value: unknown): string[] {\n const location = `Major mode \"${modeName}\" layers in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (!Array.isArray(value)) throw new Error(`${location} must be an array`);\n return value.map((layerName, index) => {\n if (typeof layerName !== 'string' || !layerName.trim()) {\n throw new Error(`${location}[${index}] must be a non-empty string`);\n }\n return layerName.trim();\n });\n}\n\nfunction legacyMajorModeDescription(layers: readonly string[]): string {\n return layers.length > 0 ? `Uses layers: ${layers.join(', ')}.` : 'Uses Doom Pi core extensions only.';\n}\n\nfunction parseMajorMode(name: string, value: unknown): MajorModeDefinition {\n if (Array.isArray(value)) {\n const layers = parseMajorModeLayers(name, value);\n return { description: legacyMajorModeDescription(layers), layers };\n }\n\n const location = `Major mode \"${name}\" in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (!isRecord(value)) throw new Error(`${location} must be a mapping with description and layers`);\n assertKnownKeys(value, MAJOR_MODE_KEYS, location);\n if (typeof value.description !== 'string' || !value.description.trim()) {\n throw new Error(`${location}.description must be a non-empty string`);\n }\n return {\n description: value.description.trim(),\n layers: parseMajorModeLayers(name, value.layers),\n };\n}\n\nfunction parseMajorModes(value: unknown): Record<string, MajorModeDefinition | null> {\n if (value === undefined) return {};\n if (!isRecord(value)) throw new Error(`majorMode in ${MAJOR_MODES_RELATIVE_PATH} must be a mapping`);\n return Object.fromEntries(\n Object.entries(value).map(([name, definition]) => [\n name,\n definition === null ? null : parseMajorMode(name, definition),\n ]),\n );\n}\n\nfunction parseLayer(name: string, definition: unknown): LayerDefinition {\n if (!isRecord(definition)) throw new Error(`Layer \"${name}\" in ${MAJOR_MODES_RELATIVE_PATH} must be a mapping`);\n if ('targets' in definition) {\n throw new Error(\n `Layer \"${name}\" in ${MAJOR_MODES_RELATIVE_PATH} uses removed field \"targets\"; select availability through major modes`,\n );\n }\n if ('config' in definition) {\n throw new Error(\n `Layer \"${name}\" in ${MAJOR_MODES_RELATIVE_PATH} uses unsupported field \"config\"; move it under the package entry that owns it`,\n );\n }\n const extensions = parseLayerExtensions(name, definition.extensions);\n const packages = parseLayerPackages(name, definition.packages);\n return {\n ...definition,\n ...(extensions === undefined ? {} : { extensions }),\n ...(packages === undefined ? {} : { packages }),\n } as LayerDefinition;\n}\n\nfunction parseLayers(value: unknown): Record<string, LayerDefinition | null> {\n if (value === undefined) return {};\n if (!isRecord(value)) throw new Error(`layers in ${MAJOR_MODES_RELATIVE_PATH} must be a mapping`);\n return Object.fromEntries(\n Object.entries(value).map(([name, definition]) => [\n name,\n definition === null ? null : parseLayer(name, definition),\n ]),\n );\n}\n\nfunction parseDefault(value: unknown): LayerDefinition {\n const location = `default in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (!isRecord(value)) throw new Error(`${location} must be a mapping with packages`);\n assertKnownKeys(value, DEFAULT_KEYS, location);\n if (!Object.hasOwn(value, 'packages')) {\n throw new Error(`Layer \"default\" packages in ${MAJOR_MODES_RELATIVE_PATH} must be an array`);\n }\n const packages = parseLayerPackages('default', value.packages);\n return { packages };\n}\n\n/**\n * Reads layers and major modes from the global and repository configs.\n *\n * The repository is read last, so it replaces a global layer or major mode of\n * the same name. Layers keep the root they were declared under so that a\n * global layer naming `./extensions/x` still points beside the global config\n * once it is loaded from inside some other repository.\n */\nexport function loadMajorModesConfig(repoRoot: string, homeDirectory: string = os.homedir()): MajorModesConfig {\n const sources = readDoomConfigSources<ModesDocument>(MODES_FILE, repoRoot, homeDirectory);\n let defaultDefinition: ResolvedLayerDefinition | undefined;\n const layers: Record<string, ResolvedLayerDefinition> = {};\n const majorMode: Record<string, MajorModeDefinition> = {};\n const majorModeSources: Record<string, DoomConfigProvenance> = {};\n let defaultMajorMode = FALLBACK_MAJOR_MODE;\n let defaultMajorModeSource: DoomConfigProvenance | undefined;\n let hasConfiguredDefault = false;\n for (const source of sources) {\n if (Object.hasOwn(source.document, 'default')) {\n defaultDefinition = {\n ...parseDefault(source.document.default),\n baseDirectory: source.baseDirectory,\n filePath: source.filePath,\n };\n }\n for (const [name, definition] of Object.entries(parseLayers(source.document.layers))) {\n if (definition === null) {\n delete layers[name];\n continue;\n }\n layers[name] = {\n ...definition,\n baseDirectory: source.baseDirectory,\n filePath: source.filePath,\n };\n }\n if (Object.hasOwn(source.document, 'defaultMajorMode')) {\n defaultMajorMode = parseDefaultMajorMode(source.document.defaultMajorMode);\n defaultMajorModeSource = { filePath: source.filePath, baseDirectory: source.baseDirectory };\n hasConfiguredDefault = true;\n }\n for (const [name, definition] of Object.entries(parseMajorModes(source.document.majorMode))) {\n if (definition === null) {\n delete majorMode[name];\n delete majorModeSources[name];\n continue;\n }\n majorMode[name] = definition;\n majorModeSources[name] = { filePath: source.filePath, baseDirectory: source.baseDirectory };\n }\n }\n\n for (const [modeName, definition] of Object.entries(majorMode)) {\n for (const name of definition.layers) {\n if (!layers[name]) {\n throw new Error(`Unknown layer \"${name}\" in majorMode.${modeName} of ${MAJOR_MODES_RELATIVE_PATH}`);\n }\n }\n }\n if (hasConfiguredDefault && !Object.hasOwn(majorMode, defaultMajorMode)) {\n throw new Error(`Unknown default major mode \"${defaultMajorMode}\" in ${MAJOR_MODES_RELATIVE_PATH}`);\n }\n return {\n ...(defaultDefinition ? { default: defaultDefinition } : {}),\n layers,\n defaultMajorMode,\n ...(defaultMajorModeSource ? { defaultMajorModeSource } : {}),\n majorMode,\n majorModeSources,\n };\n}\n\n/** Finds configuration owned by one package while preserving layer and package order. */\nexport function resolvePackageConfigurations(\n config: MajorModesConfig,\n names: string[],\n packageName: string,\n): ResolvedPackageConfiguration[] {\n const normalizedPackageName = packageName.trim();\n if (!normalizedPackageName) throw new Error('Package name must be a non-empty string');\n const configurations: ResolvedPackageConfiguration[] = [];\n const definitions: Array<{ name: string; definition: ResolvedLayerDefinition }> = [];\n if (config.default) definitions.push({ name: 'default', definition: config.default });\n for (const name of names) {\n const definition = config.layers[name];\n if (!definition) throw new Error(`Unknown layer: ${name}`);\n definitions.push({ name, definition });\n }\n for (const { name, definition } of definitions) {\n for (const entry of definition.packages ?? []) {\n if (typeof entry === 'string' || entry.config === undefined) continue;\n if (entry.name !== normalizedPackageName && !entry.name.startsWith(`${normalizedPackageName}/`)) continue;\n configurations.push({\n layer: name,\n specifier: entry.name,\n config: { ...entry.config },\n baseDirectory: definition.baseDirectory,\n ...(definition.filePath ? { filePath: definition.filePath } : {}),\n });\n }\n }\n return configurations;\n}\n\n/** Resolves one named major mode into its configured layer components. */\nexport function resolveLayers(config: MajorModesConfig, majorMode: string): string[] {\n if (!Object.hasOwn(config.majorMode, majorMode)) {\n throw new Error(\n `Unknown major mode: ${majorMode}. Known major modes: ${Object.keys(config.majorMode).sort().join(', ')}`,\n );\n }\n return [...config.majorMode[majorMode]!.layers];\n}\n\nfunction resolveLocalEntries(\n resolvers: LayerResolvers,\n specifier: string,\n baseDirectory: string,\n): string[] | undefined {\n const resolved = resolvers.localEntries?.(specifier, baseDirectory);\n if (resolved !== undefined) return resolved;\n const single = resolvers.localEntry(specifier, baseDirectory);\n return single ? [single] : undefined;\n}\n\n/** Resolves one layer's extension and package entries to absolute paths. */\nexport function layerEntries(config: MajorModesConfig, name: string, resolvers: LayerResolvers): string[] {\n const layer = config.layers[name];\n if (!layer) throw new Error(`Unknown layer: ${name}`);\n const entries: string[] = [];\n for (const entry of layer.extensions ?? []) {\n if (!isLocalPackageSpecifier(entry)) {\n entries.push(resolvers.ownEntry(entry));\n continue;\n }\n const resolved = resolveLocalEntries(resolvers, entry, layer.baseDirectory);\n if (!resolved?.length) {\n throw new Error(\n `Layer \"${name}\" extension \"${entry}\" does not resolve under ${layer.baseDirectory}. ` +\n 'Point it at a Pi extension file or directory.',\n );\n }\n entries.push(...resolved);\n }\n for (const entry of layer.packages ?? []) {\n const specifier = typeof entry === 'string' ? entry : entry.name;\n const optional = typeof entry === 'string' ? false : entry.optional === true;\n if (isLocalPackageSpecifier(specifier)) {\n const resolved = resolveLocalEntries(resolvers, specifier, layer.baseDirectory);\n if (resolved?.length) entries.push(...resolved);\n else if (!optional) {\n throw new Error(\n `Layer \"${name}\" package \"${specifier}\" does not resolve under ${layer.baseDirectory}. ` +\n 'Point it at an extension file, or a directory whose package.json declares pi.extensions.',\n );\n }\n continue;\n }\n if (optional) {\n let resolved = resolvers.optionalPackageEntries?.(specifier);\n if (resolved === undefined) {\n const single = resolvers.optionalPackageEntry(specifier);\n resolved = single ? [single] : undefined;\n }\n if (resolved) entries.push(...resolved);\n continue;\n }\n const resolved = resolvers.packageEntries?.(specifier) ?? [resolvers.packageEntry(specifier)];\n if (resolved.length === 0) {\n throw new Error(`Layer \"${name}\" package \"${specifier}\" declares no Pi extensions.`);\n }\n entries.push(...resolved);\n }\n return entries;\n}\n\n/** Hook group ids contributed by the selected layers, deduped. */\nexport function layerHookGroups(config: MajorModesConfig, names: string[]): string[] {\n const groups = new Set<string>();\n for (const name of names) {\n for (const group of config.layers[name]?.hookGroups ?? []) groups.add(group);\n }\n return [...groups];\n}\n\n/**\n * Suppress layers that exist to contribute hook groups when hooks are disabled.\n *\n * Lives beside layerHookGroups because it is the inverse question about the same\n * field, and because every caller that resolves layers has to ask it.\n */\nexport function filterHookDisabledLayers(\n config: MajorModesConfig,\n layers: readonly string[],\n hooks: boolean,\n): string[] {\n if (hooks) return [...layers];\n return layers.filter((layer) => (config.layers[layer]?.hookGroups?.length ?? 0) === 0);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"majorModes.cjs","names":["path","DOOM_DIR","os","readDoomConfigSources"],"sources":["../../src/adapters/majorModes.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\nimport type { ConfigDiagnostic } from '../types/config.ts';\nimport { DOOM_DIR, type DoomConfigProvenance, readDoomConfigSources } from './layeredConfig.ts';\n\nexport type LayerPackageConfig = Record<string, unknown>;\n\n/** One package selected by a layer, plus configuration owned by that package. */\nexport interface LayerPackage {\n name: string;\n optional?: boolean;\n config?: LayerPackageConfig;\n}\n\n/** One configured package found while walking selected layers in order. */\nexport interface ResolvedPackageConfiguration {\n layer: string;\n specifier: string;\n config: LayerPackageConfig;\n baseDirectory: string;\n filePath?: string;\n}\n\nexport interface LayerDefinition {\n /**\n * DoomPi built-in names, or Pi-compatible local extension paths.\n * Bare names resolve from DoomPi's own extension entries. Paths beginning\n * with `./` or `../`, plus absolute paths, resolve against the config that\n * declares the layer and may name a script file or extension directory.\n */\n extensions?: string[];\n /**\n * npm specifiers, or local package paths retained for compatibility.\n * Relative paths resolve against the repository root for `.doom/modes.yaml`\n * and against the global `.doom` directory for\n * `~/.pi/.doom/modes.yaml`.\n */\n packages?: Array<string | LayerPackage>;\n hookGroups?: string[];\n}\n\n/** A layer plus the root its relative extension and package paths resolve against. */\nexport interface ResolvedLayerDefinition extends LayerDefinition {\n baseDirectory: string;\n /** Absolute path of the modes file that declared this layer. */\n filePath?: string;\n}\n\nexport interface MajorModeDefinition {\n /** Human-readable purpose used by pickers and agent-facing catalogs. */\n description: string;\n /** Ordered layer names activated by this mode. */\n layers: string[];\n}\n\nexport interface MajorModesConfig {\n /** Unconditional package bundle loaded before packages from selected named layers. */\n default?: ResolvedLayerDefinition;\n layers: Record<string, ResolvedLayerDefinition>;\n /** The mode selected when neither a flag nor the environment names one. */\n defaultMajorMode: string;\n /** Source that last declared the default, absent for the built-in fallback. */\n defaultMajorModeSource?: DoomConfigProvenance;\n /** Named major modes, including their purpose and ordered layers. */\n majorMode: Record<string, MajorModeDefinition>;\n /** Declaring source for each surviving named mode. */\n majorModeSources?: Record<string, DoomConfigProvenance>;\n}\n\nexport interface LayerResolvers {\n ownEntry: (name: string) => string;\n packageEntry: (name: string) => string;\n optionalPackageEntry: (name: string) => string | undefined;\n /** Resolves every standard package.json pi.extensions entry for a package root. */\n packageEntries?: (name: string) => string[];\n /** Optional counterpart to packageEntries. */\n optionalPackageEntries?: (name: string) => string[] | undefined;\n /** Resolves one path-style layer entry against the config that declared it. */\n localEntry: (specifier: string, baseDirectory: string) => string | undefined;\n /** Resolves every Pi extension entry contributed by a local file or directory. */\n localEntries?: (specifier: string, baseDirectory: string) => string[] | undefined;\n}\n\ninterface ModesDocument {\n default?: unknown;\n layers?: unknown;\n defaultMajorMode?: unknown;\n majorMode?: unknown;\n}\n\nconst MODES_FILE = 'modes.yaml';\nconst FALLBACK_MAJOR_MODE = 'copilot';\nconst MAJOR_MODES_RELATIVE_PATH = path.join(DOOM_DIR, MODES_FILE);\nconst RELATIVE_PREFIXES = ['./', '../'] as const;\nconst DEFAULT_KEYS = ['packages'] as const;\n\n/**\n * Whether a layer value names a path rather than a built-in or installed package.\n *\n * The same rule Node applies to import specifiers, so a bare name is always a\n * logical name and a path always looks like one.\n */\nexport function isLocalPackageSpecifier(specifier: string): boolean {\n return path.isAbsolute(specifier) || RELATIVE_PREFIXES.some((prefix) => specifier.startsWith(prefix));\n}\nconst LAYER_PACKAGE_KEYS = ['name', 'optional', 'config'] as const;\nconst MAJOR_MODE_KEYS = ['description', 'layers'] as const;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Where a lenient load collects unknown keys instead of throwing.\n *\n * Same shape as the sink in configPolicy, for the same reason: `assertKnownKeys`\n * is the only reader and the whole load is synchronous.\n */\nlet unknownKeySink: ConfigDiagnostic[] | undefined;\n\nfunction assertKnownKeys(value: Record<string, unknown>, keys: readonly string[], location: string): void {\n const unsupported = Object.keys(value).filter((key) => !keys.includes(key));\n if (unsupported.length === 0) return;\n if (unknownKeySink !== undefined) {\n for (const key of unsupported) {\n unknownKeySink.push({\n filePath: MAJOR_MODES_RELATIVE_PATH,\n path: key,\n message: `${location} has unsupported field`,\n });\n }\n return;\n }\n throw new Error(`${location} has unsupported field(s): ${unsupported.join(', ')}`);\n}\n\nfunction parseLayerPackage(layerName: string, value: unknown, index: number): string | LayerPackage {\n const location = `Layer \"${layerName}\" package[${index}] in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (typeof value === 'string') {\n if (!value.trim()) throw new Error(`${location} must be a non-empty string or mapping`);\n return value.trim();\n }\n if (!isRecord(value)) throw new Error(`${location} must be a non-empty string or mapping`);\n assertKnownKeys(value, LAYER_PACKAGE_KEYS, location);\n if (typeof value.name !== 'string' || !value.name.trim()) {\n throw new Error(`${location}.name must be a non-empty string`);\n }\n if (value.optional !== undefined && typeof value.optional !== 'boolean') {\n throw new Error(`${location}.optional must be a boolean`);\n }\n if (value.config !== undefined && !isRecord(value.config)) {\n throw new Error(`${location}.config must be a mapping`);\n }\n return {\n name: value.name.trim(),\n ...(value.optional === true ? { optional: true } : {}),\n ...(value.config ? { config: { ...value.config } } : {}),\n };\n}\n\nfunction parseLayerExtensions(layerName: string, value: unknown): string[] | undefined {\n if (value === undefined) return undefined;\n const location = `Layer \"${layerName}\" extensions in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (!Array.isArray(value)) throw new Error(`${location} must be an array`);\n return value.map((entry, index) => {\n if (typeof entry !== 'string' || !entry.trim()) {\n throw new Error(`${location}[${index}] must be a non-empty string`);\n }\n return entry.trim();\n });\n}\n\nfunction parseLayerPackages(layerName: string, value: unknown): Array<string | LayerPackage> | undefined {\n if (value === undefined) return undefined;\n if (!Array.isArray(value)) {\n throw new Error(`Layer \"${layerName}\" packages in ${MAJOR_MODES_RELATIVE_PATH} must be an array`);\n }\n return value.map((entry, index) => parseLayerPackage(layerName, entry, index));\n}\n\nfunction parseDefaultMajorMode(value: unknown): string {\n if (typeof value !== 'string' || !value.trim()) {\n throw new Error(`defaultMajorMode in ${MAJOR_MODES_RELATIVE_PATH} must be a non-empty string`);\n }\n return value.trim();\n}\n\nfunction parseMajorModeLayers(modeName: string, value: unknown): string[] {\n const location = `Major mode \"${modeName}\" layers in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (!Array.isArray(value)) throw new Error(`${location} must be an array`);\n return value.map((layerName, index) => {\n if (typeof layerName !== 'string' || !layerName.trim()) {\n throw new Error(`${location}[${index}] must be a non-empty string`);\n }\n return layerName.trim();\n });\n}\n\nfunction legacyMajorModeDescription(layers: readonly string[]): string {\n return layers.length > 0 ? `Uses layers: ${layers.join(', ')}.` : 'Uses Doom Pi core extensions only.';\n}\n\nfunction parseMajorMode(name: string, value: unknown): MajorModeDefinition {\n if (Array.isArray(value)) {\n const layers = parseMajorModeLayers(name, value);\n return { description: legacyMajorModeDescription(layers), layers };\n }\n\n const location = `Major mode \"${name}\" in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (!isRecord(value)) throw new Error(`${location} must be a mapping with description and layers`);\n assertKnownKeys(value, MAJOR_MODE_KEYS, location);\n if (typeof value.description !== 'string' || !value.description.trim()) {\n throw new Error(`${location}.description must be a non-empty string`);\n }\n return {\n description: value.description.trim(),\n layers: parseMajorModeLayers(name, value.layers),\n };\n}\n\nfunction parseMajorModes(value: unknown): Record<string, MajorModeDefinition | null> {\n if (value === undefined) return {};\n if (!isRecord(value)) throw new Error(`majorMode in ${MAJOR_MODES_RELATIVE_PATH} must be a mapping`);\n return Object.fromEntries(\n Object.entries(value).map(([name, definition]) => [\n name,\n definition === null ? null : parseMajorMode(name, definition),\n ]),\n );\n}\n\nfunction parseLayer(name: string, definition: unknown): LayerDefinition {\n if (!isRecord(definition)) throw new Error(`Layer \"${name}\" in ${MAJOR_MODES_RELATIVE_PATH} must be a mapping`);\n if ('targets' in definition) {\n throw new Error(\n `Layer \"${name}\" in ${MAJOR_MODES_RELATIVE_PATH} uses removed field \"targets\"; select availability through major modes`,\n );\n }\n if ('config' in definition) {\n throw new Error(\n `Layer \"${name}\" in ${MAJOR_MODES_RELATIVE_PATH} uses unsupported field \"config\"; move it under the package entry that owns it`,\n );\n }\n const extensions = parseLayerExtensions(name, definition.extensions);\n const packages = parseLayerPackages(name, definition.packages);\n return {\n ...definition,\n ...(extensions === undefined ? {} : { extensions }),\n ...(packages === undefined ? {} : { packages }),\n } as LayerDefinition;\n}\n\nfunction parseLayers(value: unknown): Record<string, LayerDefinition | null> {\n if (value === undefined) return {};\n if (!isRecord(value)) throw new Error(`layers in ${MAJOR_MODES_RELATIVE_PATH} must be a mapping`);\n return Object.fromEntries(\n Object.entries(value).map(([name, definition]) => [\n name,\n definition === null ? null : parseLayer(name, definition),\n ]),\n );\n}\n\nfunction parseDefault(value: unknown): LayerDefinition {\n const location = `default in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (!isRecord(value)) throw new Error(`${location} must be a mapping with packages`);\n assertKnownKeys(value, DEFAULT_KEYS, location);\n if (!Object.hasOwn(value, 'packages')) {\n throw new Error(`Layer \"default\" packages in ${MAJOR_MODES_RELATIVE_PATH} must be an array`);\n }\n const packages = parseLayerPackages('default', value.packages);\n return { packages };\n}\n\n/**\n * Reads layers and major modes from the global and repository configs.\n *\n * The repository is read last, so it replaces a global layer or major mode of\n * the same name. Layers keep the root they were declared under so that a\n * global layer naming `./extensions/x` still points beside the global config\n * once it is loaded from inside some other repository.\n */\nexport function loadMajorModesConfig(repoRoot: string, homeDirectory: string = os.homedir()): MajorModesConfig {\n const sources = readDoomConfigSources<ModesDocument>(MODES_FILE, repoRoot, homeDirectory);\n let defaultDefinition: ResolvedLayerDefinition | undefined;\n const layers: Record<string, ResolvedLayerDefinition> = {};\n const majorMode: Record<string, MajorModeDefinition> = {};\n const majorModeSources: Record<string, DoomConfigProvenance> = {};\n let defaultMajorMode = FALLBACK_MAJOR_MODE;\n let defaultMajorModeSource: DoomConfigProvenance | undefined;\n let hasConfiguredDefault = false;\n for (const source of sources) {\n if (Object.hasOwn(source.document, 'default')) {\n defaultDefinition = {\n ...parseDefault(source.document.default),\n baseDirectory: source.baseDirectory,\n filePath: source.filePath,\n };\n }\n for (const [name, definition] of Object.entries(parseLayers(source.document.layers))) {\n if (definition === null) {\n delete layers[name];\n continue;\n }\n layers[name] = {\n ...definition,\n baseDirectory: source.baseDirectory,\n filePath: source.filePath,\n };\n }\n if (Object.hasOwn(source.document, 'defaultMajorMode')) {\n defaultMajorMode = parseDefaultMajorMode(source.document.defaultMajorMode);\n defaultMajorModeSource = { filePath: source.filePath, baseDirectory: source.baseDirectory };\n hasConfiguredDefault = true;\n }\n for (const [name, definition] of Object.entries(parseMajorModes(source.document.majorMode))) {\n if (definition === null) {\n delete majorMode[name];\n delete majorModeSources[name];\n continue;\n }\n majorMode[name] = definition;\n majorModeSources[name] = { filePath: source.filePath, baseDirectory: source.baseDirectory };\n }\n }\n\n for (const [modeName, definition] of Object.entries(majorMode)) {\n for (const name of definition.layers) {\n if (!layers[name]) {\n throw new Error(`Unknown layer \"${name}\" in majorMode.${modeName} of ${MAJOR_MODES_RELATIVE_PATH}`);\n }\n }\n }\n if (hasConfiguredDefault && !Object.hasOwn(majorMode, defaultMajorMode)) {\n throw new Error(`Unknown default major mode \"${defaultMajorMode}\" in ${MAJOR_MODES_RELATIVE_PATH}`);\n }\n return {\n ...(defaultDefinition ? { default: defaultDefinition } : {}),\n layers,\n defaultMajorMode,\n ...(defaultMajorModeSource ? { defaultMajorModeSource } : {}),\n majorMode,\n majorModeSources,\n };\n}\n\n/** Finds configuration owned by one package while preserving layer and package order. */\nexport function resolvePackageConfigurations(\n config: MajorModesConfig,\n names: string[],\n packageName: string,\n): ResolvedPackageConfiguration[] {\n const normalizedPackageName = packageName.trim();\n if (!normalizedPackageName) throw new Error('Package name must be a non-empty string');\n const configurations: ResolvedPackageConfiguration[] = [];\n const definitions: Array<{ name: string; definition: ResolvedLayerDefinition }> = [];\n if (config.default) definitions.push({ name: 'default', definition: config.default });\n for (const name of names) {\n const definition = config.layers[name];\n if (!definition) throw new Error(`Unknown layer: ${name}`);\n definitions.push({ name, definition });\n }\n for (const { name, definition } of definitions) {\n for (const entry of definition.packages ?? []) {\n if (typeof entry === 'string' || entry.config === undefined) continue;\n if (entry.name !== normalizedPackageName && !entry.name.startsWith(`${normalizedPackageName}/`)) continue;\n configurations.push({\n layer: name,\n specifier: entry.name,\n config: { ...entry.config },\n baseDirectory: definition.baseDirectory,\n ...(definition.filePath ? { filePath: definition.filePath } : {}),\n });\n }\n }\n return configurations;\n}\n\n/** Resolves one named major mode into its configured layer components. */\nexport function resolveLayers(config: MajorModesConfig, majorMode: string): string[] {\n if (!Object.hasOwn(config.majorMode, majorMode)) {\n throw new Error(\n `Unknown major mode: ${majorMode}. Known major modes: ${Object.keys(config.majorMode).sort().join(', ')}`,\n );\n }\n return [...config.majorMode[majorMode]!.layers];\n}\n\nfunction resolveLocalEntries(\n resolvers: LayerResolvers,\n specifier: string,\n baseDirectory: string,\n): string[] | undefined {\n const resolved = resolvers.localEntries?.(specifier, baseDirectory);\n if (resolved !== undefined) return resolved;\n const single = resolvers.localEntry(specifier, baseDirectory);\n return single ? [single] : undefined;\n}\n\n/** Resolves one layer's extension and package entries to absolute paths. */\nexport function layerEntries(config: MajorModesConfig, name: string, resolvers: LayerResolvers): string[] {\n const layer = config.layers[name];\n if (!layer) throw new Error(`Unknown layer: ${name}`);\n const entries: string[] = [];\n for (const entry of layer.extensions ?? []) {\n if (!isLocalPackageSpecifier(entry)) {\n entries.push(resolvers.ownEntry(entry));\n continue;\n }\n const resolved = resolveLocalEntries(resolvers, entry, layer.baseDirectory);\n if (!resolved?.length) {\n throw new Error(\n `Layer \"${name}\" extension \"${entry}\" does not resolve under ${layer.baseDirectory}. ` +\n 'Point it at a Pi extension file or directory.',\n );\n }\n entries.push(...resolved);\n }\n for (const entry of layer.packages ?? []) {\n const specifier = typeof entry === 'string' ? entry : entry.name;\n const optional = typeof entry === 'string' ? false : entry.optional === true;\n if (isLocalPackageSpecifier(specifier)) {\n const resolved = resolveLocalEntries(resolvers, specifier, layer.baseDirectory);\n if (resolved?.length) entries.push(...resolved);\n else if (!optional) {\n throw new Error(\n `Layer \"${name}\" package \"${specifier}\" does not resolve under ${layer.baseDirectory}. ` +\n 'Point it at an extension file, or a directory whose package.json declares pi.extensions.',\n );\n }\n continue;\n }\n if (optional) {\n let resolved = resolvers.optionalPackageEntries?.(specifier);\n if (resolved === undefined) {\n const single = resolvers.optionalPackageEntry(specifier);\n resolved = single ? [single] : undefined;\n }\n if (resolved) entries.push(...resolved);\n continue;\n }\n const resolved = resolvers.packageEntries?.(specifier) ?? [resolvers.packageEntry(specifier)];\n if (resolved.length === 0) {\n throw new Error(`Layer \"${name}\" package \"${specifier}\" declares no Pi extensions.`);\n }\n entries.push(...resolved);\n }\n return entries;\n}\n\n/** Hook group ids contributed by the selected layers, deduped. */\nexport function layerHookGroups(config: MajorModesConfig, names: string[]): string[] {\n const groups = new Set<string>();\n for (const name of names) {\n for (const group of config.layers[name]?.hookGroups ?? []) groups.add(group);\n }\n return [...groups];\n}\n\n/**\n * Suppress layers that exist to contribute hook groups when hooks are disabled.\n *\n * Lives beside layerHookGroups because it is the inverse question about the same\n * field, and because every caller that resolves layers has to ask it.\n */\nexport function filterHookDisabledLayers(\n config: MajorModesConfig,\n layers: readonly string[],\n hooks: boolean,\n): string[] {\n if (hooks) return [...layers];\n return layers.filter((layer) => (config.layers[layer]?.hookGroups?.length ?? 0) === 0);\n}\n\n/**\n * Reads layers and major modes, tolerating unknown keys.\n *\n * `doompi sync` uses this so a modes.yaml written against a different set of\n * layer packages cannot break a build. Every other caller keeps the strict\n * `loadMajorModesConfig`, and `doompi doctor` still reports what was tolerated.\n */\nexport function loadMajorModesConfigLenient(\n repoRoot: string,\n homeDirectory: string = os.homedir(),\n): { config: MajorModesConfig; diagnostics: ConfigDiagnostic[] } {\n const diagnostics: ConfigDiagnostic[] = [];\n const previous = unknownKeySink;\n unknownKeySink = diagnostics;\n try {\n return { config: loadMajorModesConfig(repoRoot, homeDirectory), diagnostics };\n } finally {\n unknownKeySink = previous;\n }\n}\n"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { ConfigDiagnostic } from "../types/config.cjs";
1
2
  import { DoomConfigProvenance } from "./layeredConfig.cjs";
2
3
  //#region src/adapters/majorModes.d.ts
3
4
  export type LayerPackageConfig = Record<string, unknown>;
@@ -101,5 +102,16 @@ export declare function layerHookGroups(config: MajorModesConfig, names: string[
101
102
  * field, and because every caller that resolves layers has to ask it.
102
103
  */
103
104
  export declare function filterHookDisabledLayers(config: MajorModesConfig, layers: readonly string[], hooks: boolean): string[];
105
+ /**
106
+ * Reads layers and major modes, tolerating unknown keys.
107
+ *
108
+ * `doompi sync` uses this so a modes.yaml written against a different set of
109
+ * layer packages cannot break a build. Every other caller keeps the strict
110
+ * `loadMajorModesConfig`, and `doompi doctor` still reports what was tolerated.
111
+ */
112
+ export declare function loadMajorModesConfigLenient(repoRoot: string, homeDirectory?: string): {
113
+ config: MajorModesConfig;
114
+ diagnostics: ConfigDiagnostic[];
115
+ };
104
116
  //#endregion
105
117
  //# sourceMappingURL=majorModes.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"majorModes.d.cts","names":[],"sources":["../../src/adapters/majorModes.ts"],"mappings":";;YAIY,qBAAqB;;iBAGhB;EACf;EACA;EACA,SAAS;;;iBAIM;EACf;EACA;EACA,QAAQ;EACR;EACA;;iBAGe;;;;;;;EAOf;;;;;;;EAOA,WAAW,eAAe;EAC1B;;;iBAIe,gCAAgC;EAC/C;;EAEA;;iBAGe;;EAEf;;EAEA;;iBAGe;;EAEf,UAAU;EACV,QAAQ,eAAe;;EAEvB;;EAEA,yBAAyB;;EAEzB,WAAW,eAAe;;EAE1B,mBAAmB,eAAe;;iBAGnB;EACf,WAAW;EACX,eAAe;EACf,uBAAuB;;EAEvB,kBAAkB;;EAElB,0BAA0B;;EAE1B,aAAa,mBAAmB;;EAEhC,gBAAgB,mBAAmB;;;;;;;;wBAsBrB,wBAAwB;;;;;;;;;wBAmKxB,qBAAqB,kBAAkB,yBAAuC;;wBAiE9E,6BACd,QAAQ,kBACR,iBACA,sBACC;;wBA4Ba,cAAc,QAAQ,kBAAkB;;wBAqBxC,aAAa,QAAQ,kBAAkB,cAAc,WAAW;;wBAmDhE,gBAAgB,QAAQ,kBAAkB;;;;;;;wBAc1C,yBACd,QAAQ,kBACR,2BACA"}
1
+ {"version":3,"file":"majorModes.d.cts","names":[],"sources":["../../src/adapters/majorModes.ts"],"mappings":";;;YAKY,qBAAqB;;iBAGhB;EACf;EACA;EACA,SAAS;;;iBAIM;EACf;EACA;EACA,QAAQ;EACR;EACA;;iBAGe;;;;;;;EAOf;;;;;;;EAOA,WAAW,eAAe;EAC1B;;;iBAIe,gCAAgC;EAC/C;;EAEA;;iBAGe;;EAEf;;EAEA;;iBAGe;;EAEf,UAAU;EACV,QAAQ,eAAe;;EAEvB;;EAEA,yBAAyB;;EAEzB,WAAW,eAAe;;EAE1B,mBAAmB,eAAe;;iBAGnB;EACf,WAAW;EACX,eAAe;EACf,uBAAuB;;EAEvB,kBAAkB;;EAElB,0BAA0B;;EAE1B,aAAa,mBAAmB;;EAEhC,gBAAgB,mBAAmB;;;;;;;;wBAsBrB,wBAAwB;;;;;;;;;wBAoLxB,qBAAqB,kBAAkB,yBAAuC;;wBAiE9E,6BACd,QAAQ,kBACR,iBACA,sBACC;;wBA4Ba,cAAc,QAAQ,kBAAkB;;wBAqBxC,aAAa,QAAQ,kBAAkB,cAAc,WAAW;;wBAmDhE,gBAAgB,QAAQ,kBAAkB;;;;;;;wBAc1C,yBACd,QAAQ,kBACR,2BACA;;;;;;;;wBAac,4BACd,kBACA;EACG,QAAQ;EAAkB,aAAa"}
@@ -1,3 +1,4 @@
1
+ import { ConfigDiagnostic } from "../types/config.mjs";
1
2
  import { DoomConfigProvenance } from "./layeredConfig.mjs";
2
3
  //#region src/adapters/majorModes.d.ts
3
4
  export type LayerPackageConfig = Record<string, unknown>;
@@ -101,5 +102,16 @@ export declare function layerHookGroups(config: MajorModesConfig, names: string[
101
102
  * field, and because every caller that resolves layers has to ask it.
102
103
  */
103
104
  export declare function filterHookDisabledLayers(config: MajorModesConfig, layers: readonly string[], hooks: boolean): string[];
105
+ /**
106
+ * Reads layers and major modes, tolerating unknown keys.
107
+ *
108
+ * `doompi sync` uses this so a modes.yaml written against a different set of
109
+ * layer packages cannot break a build. Every other caller keeps the strict
110
+ * `loadMajorModesConfig`, and `doompi doctor` still reports what was tolerated.
111
+ */
112
+ export declare function loadMajorModesConfigLenient(repoRoot: string, homeDirectory?: string): {
113
+ config: MajorModesConfig;
114
+ diagnostics: ConfigDiagnostic[];
115
+ };
104
116
  //#endregion
105
117
  //# sourceMappingURL=majorModes.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"majorModes.d.mts","names":[],"sources":["../../src/adapters/majorModes.ts"],"mappings":";;YAIY,qBAAqB;;iBAGhB;EACf;EACA;EACA,SAAS;;;iBAIM;EACf;EACA;EACA,QAAQ;EACR;EACA;;iBAGe;;;;;;;EAOf;;;;;;;EAOA,WAAW,eAAe;EAC1B;;;iBAIe,gCAAgC;EAC/C;;EAEA;;iBAGe;;EAEf;;EAEA;;iBAGe;;EAEf,UAAU;EACV,QAAQ,eAAe;;EAEvB;;EAEA,yBAAyB;;EAEzB,WAAW,eAAe;;EAE1B,mBAAmB,eAAe;;iBAGnB;EACf,WAAW;EACX,eAAe;EACf,uBAAuB;;EAEvB,kBAAkB;;EAElB,0BAA0B;;EAE1B,aAAa,mBAAmB;;EAEhC,gBAAgB,mBAAmB;;;;;;;;wBAsBrB,wBAAwB;;;;;;;;;wBAmKxB,qBAAqB,kBAAkB,yBAAuC;;wBAiE9E,6BACd,QAAQ,kBACR,iBACA,sBACC;;wBA4Ba,cAAc,QAAQ,kBAAkB;;wBAqBxC,aAAa,QAAQ,kBAAkB,cAAc,WAAW;;wBAmDhE,gBAAgB,QAAQ,kBAAkB;;;;;;;wBAc1C,yBACd,QAAQ,kBACR,2BACA"}
1
+ {"version":3,"file":"majorModes.d.mts","names":[],"sources":["../../src/adapters/majorModes.ts"],"mappings":";;;YAKY,qBAAqB;;iBAGhB;EACf;EACA;EACA,SAAS;;;iBAIM;EACf;EACA;EACA,QAAQ;EACR;EACA;;iBAGe;;;;;;;EAOf;;;;;;;EAOA,WAAW,eAAe;EAC1B;;;iBAIe,gCAAgC;EAC/C;;EAEA;;iBAGe;;EAEf;;EAEA;;iBAGe;;EAEf,UAAU;EACV,QAAQ,eAAe;;EAEvB;;EAEA,yBAAyB;;EAEzB,WAAW,eAAe;;EAE1B,mBAAmB,eAAe;;iBAGnB;EACf,WAAW;EACX,eAAe;EACf,uBAAuB;;EAEvB,kBAAkB;;EAElB,0BAA0B;;EAE1B,aAAa,mBAAmB;;EAEhC,gBAAgB,mBAAmB;;;;;;;;wBAsBrB,wBAAwB;;;;;;;;;wBAoLxB,qBAAqB,kBAAkB,yBAAuC;;wBAiE9E,6BACd,QAAQ,kBACR,iBACA,sBACC;;wBA4Ba,cAAc,QAAQ,kBAAkB;;wBAqBxC,aAAa,QAAQ,kBAAkB,cAAc,WAAW;;wBAmDhE,gBAAgB,QAAQ,kBAAkB;;;;;;;wBAc1C,yBACd,QAAQ,kBACR,2BACA;;;;;;;;wBAac,4BACd,kBACA;EACG,QAAQ;EAAkB,aAAa"}
@@ -1,2 +1,2 @@
1
- import{DOOM_DIR as e,readDoomConfigSources as t}from"./layeredConfig.mjs";import n from"node:path";import r from"node:os";const i=`modes.yaml`,a=n.join(e,i),o=[`./`,`../`],s=[`packages`];function c(e){return n.isAbsolute(e)||o.some(t=>e.startsWith(t))}const l=[`name`,`optional`,`config`],u=[`description`,`layers`];function d(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function f(e,t,n){let r=Object.keys(e).filter(e=>!t.includes(e));if(r.length>0)throw Error(`${n} has unsupported field(s): ${r.join(`, `)}`)}function p(e,t,n){let r=`Layer "${e}" package[${n}] in ${a}`;if(typeof t==`string`){if(!t.trim())throw Error(`${r} must be a non-empty string or mapping`);return t.trim()}if(!d(t))throw Error(`${r} must be a non-empty string or mapping`);if(f(t,l,r),typeof t.name!=`string`||!t.name.trim())throw Error(`${r}.name must be a non-empty string`);if(t.optional!==void 0&&typeof t.optional!=`boolean`)throw Error(`${r}.optional must be a boolean`);if(t.config!==void 0&&!d(t.config))throw Error(`${r}.config must be a mapping`);return{name:t.name.trim(),...t.optional===!0?{optional:!0}:{},...t.config?{config:{...t.config}}:{}}}function m(e,t){if(t===void 0)return;let n=`Layer "${e}" extensions in ${a}`;if(!Array.isArray(t))throw Error(`${n} must be an array`);return t.map((e,t)=>{if(typeof e!=`string`||!e.trim())throw Error(`${n}[${t}] must be a non-empty string`);return e.trim()})}function h(e,t){if(t!==void 0){if(!Array.isArray(t))throw Error(`Layer "${e}" packages in ${a} must be an array`);return t.map((t,n)=>p(e,t,n))}}function g(e){if(typeof e!=`string`||!e.trim())throw Error(`defaultMajorMode in ${a} must be a non-empty string`);return e.trim()}function _(e,t){let n=`Major mode "${e}" layers in ${a}`;if(!Array.isArray(t))throw Error(`${n} must be an array`);return t.map((e,t)=>{if(typeof e!=`string`||!e.trim())throw Error(`${n}[${t}] must be a non-empty string`);return e.trim()})}function v(e){return e.length>0?`Uses layers: ${e.join(`, `)}.`:`Uses Doom Pi core extensions only.`}function y(e,t){if(Array.isArray(t)){let n=_(e,t);return{description:v(n),layers:n}}let n=`Major mode "${e}" in ${a}`;if(!d(t))throw Error(`${n} must be a mapping with description and layers`);if(f(t,u,n),typeof t.description!=`string`||!t.description.trim())throw Error(`${n}.description must be a non-empty string`);return{description:t.description.trim(),layers:_(e,t.layers)}}function b(e){if(e===void 0)return{};if(!d(e))throw Error(`majorMode in ${a} must be a mapping`);return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,t===null?null:y(e,t)]))}function x(e,t){if(!d(t))throw Error(`Layer "${e}" in ${a} must be a mapping`);if(`targets`in t)throw Error(`Layer "${e}" in ${a} uses removed field "targets"; select availability through major modes`);if(`config`in t)throw Error(`Layer "${e}" in ${a} uses unsupported field "config"; move it under the package entry that owns it`);let n=m(e,t.extensions),r=h(e,t.packages);return{...t,...n===void 0?{}:{extensions:n},...r===void 0?{}:{packages:r}}}function S(e){if(e===void 0)return{};if(!d(e))throw Error(`layers in ${a} must be a mapping`);return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,t===null?null:x(e,t)]))}function C(e){let t=`default in ${a}`;if(!d(e))throw Error(`${t} must be a mapping with packages`);if(f(e,s,t),!Object.hasOwn(e,`packages`))throw Error(`Layer "default" packages in ${a} must be an array`);return{packages:h(`default`,e.packages)}}function w(e,n=r.homedir()){let o=t(i,e,n),s,c={},l={},u={},d=`copilot`,f,p=!1;for(let e of o){Object.hasOwn(e.document,`default`)&&(s={...C(e.document.default),baseDirectory:e.baseDirectory,filePath:e.filePath});for(let[t,n]of Object.entries(S(e.document.layers))){if(n===null){delete c[t];continue}c[t]={...n,baseDirectory:e.baseDirectory,filePath:e.filePath}}Object.hasOwn(e.document,`defaultMajorMode`)&&(d=g(e.document.defaultMajorMode),f={filePath:e.filePath,baseDirectory:e.baseDirectory},p=!0);for(let[t,n]of Object.entries(b(e.document.majorMode))){if(n===null){delete l[t],delete u[t];continue}l[t]=n,u[t]={filePath:e.filePath,baseDirectory:e.baseDirectory}}}for(let[e,t]of Object.entries(l))for(let n of t.layers)if(!c[n])throw Error(`Unknown layer "${n}" in majorMode.${e} of ${a}`);if(p&&!Object.hasOwn(l,d))throw Error(`Unknown default major mode "${d}" in ${a}`);return{...s?{default:s}:{},layers:c,defaultMajorMode:d,...f?{defaultMajorModeSource:f}:{},majorMode:l,majorModeSources:u}}function T(e,t,n){let r=n.trim();if(!r)throw Error(`Package name must be a non-empty string`);let i=[],a=[];e.default&&a.push({name:`default`,definition:e.default});for(let n of t){let t=e.layers[n];if(!t)throw Error(`Unknown layer: ${n}`);a.push({name:n,definition:t})}for(let{name:e,definition:t}of a)for(let n of t.packages??[])typeof n!=`string`&&n.config!==void 0&&(n.name===r||n.name.startsWith(`${r}/`))&&i.push({layer:e,specifier:n.name,config:{...n.config},baseDirectory:t.baseDirectory,...t.filePath?{filePath:t.filePath}:{}});return i}function E(e,t){if(!Object.hasOwn(e.majorMode,t))throw Error(`Unknown major mode: ${t}. Known major modes: ${Object.keys(e.majorMode).sort().join(`, `)}`);return[...e.majorMode[t].layers]}function D(e,t,n){let r=e.localEntries?.(t,n);if(r!==void 0)return r;let i=e.localEntry(t,n);return i?[i]:void 0}function O(e,t,n){let r=e.layers[t];if(!r)throw Error(`Unknown layer: ${t}`);let i=[];for(let e of r.extensions??[]){if(!c(e)){i.push(n.ownEntry(e));continue}let a=D(n,e,r.baseDirectory);if(!a?.length)throw Error(`Layer "${t}" extension "${e}" does not resolve under ${r.baseDirectory}. Point it at a Pi extension file or directory.`);i.push(...a)}for(let e of r.packages??[]){let a=typeof e==`string`?e:e.name,o=typeof e!=`string`&&e.optional===!0;if(c(a)){let e=D(n,a,r.baseDirectory);if(e?.length)i.push(...e);else if(!o)throw Error(`Layer "${t}" package "${a}" does not resolve under ${r.baseDirectory}. Point it at an extension file, or a directory whose package.json declares pi.extensions.`);continue}if(o){let e=n.optionalPackageEntries?.(a);if(e===void 0){let t=n.optionalPackageEntry(a);e=t?[t]:void 0}e&&i.push(...e);continue}let s=n.packageEntries?.(a)??[n.packageEntry(a)];if(s.length===0)throw Error(`Layer "${t}" package "${a}" declares no Pi extensions.`);i.push(...s)}return i}function k(e,t){let n=new Set;for(let r of t)for(let t of e.layers[r]?.hookGroups??[])n.add(t);return[...n]}function A(e,t,n){return n?[...t]:t.filter(t=>(e.layers[t]?.hookGroups?.length??0)===0)}export{A as filterHookDisabledLayers,c as isLocalPackageSpecifier,O as layerEntries,k as layerHookGroups,w as loadMajorModesConfig,E as resolveLayers,T as resolvePackageConfigurations};
1
+ import{DOOM_DIR as e,readDoomConfigSources as t}from"./layeredConfig.mjs";import n from"node:path";import r from"node:os";const i=`modes.yaml`,a=n.join(e,i),o=[`./`,`../`],s=[`packages`];function c(e){return n.isAbsolute(e)||o.some(t=>e.startsWith(t))}const l=[`name`,`optional`,`config`],u=[`description`,`layers`];function d(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}let f;function p(e,t,n){let r=Object.keys(e).filter(e=>!t.includes(e));if(r.length!==0){if(f!==void 0){for(let e of r)f.push({filePath:a,path:e,message:`${n} has unsupported field`});return}throw Error(`${n} has unsupported field(s): ${r.join(`, `)}`)}}function m(e,t,n){let r=`Layer "${e}" package[${n}] in ${a}`;if(typeof t==`string`){if(!t.trim())throw Error(`${r} must be a non-empty string or mapping`);return t.trim()}if(!d(t))throw Error(`${r} must be a non-empty string or mapping`);if(p(t,l,r),typeof t.name!=`string`||!t.name.trim())throw Error(`${r}.name must be a non-empty string`);if(t.optional!==void 0&&typeof t.optional!=`boolean`)throw Error(`${r}.optional must be a boolean`);if(t.config!==void 0&&!d(t.config))throw Error(`${r}.config must be a mapping`);return{name:t.name.trim(),...t.optional===!0?{optional:!0}:{},...t.config?{config:{...t.config}}:{}}}function h(e,t){if(t===void 0)return;let n=`Layer "${e}" extensions in ${a}`;if(!Array.isArray(t))throw Error(`${n} must be an array`);return t.map((e,t)=>{if(typeof e!=`string`||!e.trim())throw Error(`${n}[${t}] must be a non-empty string`);return e.trim()})}function g(e,t){if(t!==void 0){if(!Array.isArray(t))throw Error(`Layer "${e}" packages in ${a} must be an array`);return t.map((t,n)=>m(e,t,n))}}function _(e){if(typeof e!=`string`||!e.trim())throw Error(`defaultMajorMode in ${a} must be a non-empty string`);return e.trim()}function v(e,t){let n=`Major mode "${e}" layers in ${a}`;if(!Array.isArray(t))throw Error(`${n} must be an array`);return t.map((e,t)=>{if(typeof e!=`string`||!e.trim())throw Error(`${n}[${t}] must be a non-empty string`);return e.trim()})}function y(e){return e.length>0?`Uses layers: ${e.join(`, `)}.`:`Uses Doom Pi core extensions only.`}function b(e,t){if(Array.isArray(t)){let n=v(e,t);return{description:y(n),layers:n}}let n=`Major mode "${e}" in ${a}`;if(!d(t))throw Error(`${n} must be a mapping with description and layers`);if(p(t,u,n),typeof t.description!=`string`||!t.description.trim())throw Error(`${n}.description must be a non-empty string`);return{description:t.description.trim(),layers:v(e,t.layers)}}function x(e){if(e===void 0)return{};if(!d(e))throw Error(`majorMode in ${a} must be a mapping`);return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,t===null?null:b(e,t)]))}function S(e,t){if(!d(t))throw Error(`Layer "${e}" in ${a} must be a mapping`);if(`targets`in t)throw Error(`Layer "${e}" in ${a} uses removed field "targets"; select availability through major modes`);if(`config`in t)throw Error(`Layer "${e}" in ${a} uses unsupported field "config"; move it under the package entry that owns it`);let n=h(e,t.extensions),r=g(e,t.packages);return{...t,...n===void 0?{}:{extensions:n},...r===void 0?{}:{packages:r}}}function C(e){if(e===void 0)return{};if(!d(e))throw Error(`layers in ${a} must be a mapping`);return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,t===null?null:S(e,t)]))}function w(e){let t=`default in ${a}`;if(!d(e))throw Error(`${t} must be a mapping with packages`);if(p(e,s,t),!Object.hasOwn(e,`packages`))throw Error(`Layer "default" packages in ${a} must be an array`);return{packages:g(`default`,e.packages)}}function T(e,n=r.homedir()){let o=t(i,e,n),s,c={},l={},u={},d=`copilot`,f,p=!1;for(let e of o){Object.hasOwn(e.document,`default`)&&(s={...w(e.document.default),baseDirectory:e.baseDirectory,filePath:e.filePath});for(let[t,n]of Object.entries(C(e.document.layers))){if(n===null){delete c[t];continue}c[t]={...n,baseDirectory:e.baseDirectory,filePath:e.filePath}}Object.hasOwn(e.document,`defaultMajorMode`)&&(d=_(e.document.defaultMajorMode),f={filePath:e.filePath,baseDirectory:e.baseDirectory},p=!0);for(let[t,n]of Object.entries(x(e.document.majorMode))){if(n===null){delete l[t],delete u[t];continue}l[t]=n,u[t]={filePath:e.filePath,baseDirectory:e.baseDirectory}}}for(let[e,t]of Object.entries(l))for(let n of t.layers)if(!c[n])throw Error(`Unknown layer "${n}" in majorMode.${e} of ${a}`);if(p&&!Object.hasOwn(l,d))throw Error(`Unknown default major mode "${d}" in ${a}`);return{...s?{default:s}:{},layers:c,defaultMajorMode:d,...f?{defaultMajorModeSource:f}:{},majorMode:l,majorModeSources:u}}function E(e,t,n){let r=n.trim();if(!r)throw Error(`Package name must be a non-empty string`);let i=[],a=[];e.default&&a.push({name:`default`,definition:e.default});for(let n of t){let t=e.layers[n];if(!t)throw Error(`Unknown layer: ${n}`);a.push({name:n,definition:t})}for(let{name:e,definition:t}of a)for(let n of t.packages??[])typeof n!=`string`&&n.config!==void 0&&(n.name===r||n.name.startsWith(`${r}/`))&&i.push({layer:e,specifier:n.name,config:{...n.config},baseDirectory:t.baseDirectory,...t.filePath?{filePath:t.filePath}:{}});return i}function D(e,t){if(!Object.hasOwn(e.majorMode,t))throw Error(`Unknown major mode: ${t}. Known major modes: ${Object.keys(e.majorMode).sort().join(`, `)}`);return[...e.majorMode[t].layers]}function O(e,t,n){let r=e.localEntries?.(t,n);if(r!==void 0)return r;let i=e.localEntry(t,n);return i?[i]:void 0}function k(e,t,n){let r=e.layers[t];if(!r)throw Error(`Unknown layer: ${t}`);let i=[];for(let e of r.extensions??[]){if(!c(e)){i.push(n.ownEntry(e));continue}let a=O(n,e,r.baseDirectory);if(!a?.length)throw Error(`Layer "${t}" extension "${e}" does not resolve under ${r.baseDirectory}. Point it at a Pi extension file or directory.`);i.push(...a)}for(let e of r.packages??[]){let a=typeof e==`string`?e:e.name,o=typeof e!=`string`&&e.optional===!0;if(c(a)){let e=O(n,a,r.baseDirectory);if(e?.length)i.push(...e);else if(!o)throw Error(`Layer "${t}" package "${a}" does not resolve under ${r.baseDirectory}. Point it at an extension file, or a directory whose package.json declares pi.extensions.`);continue}if(o){let e=n.optionalPackageEntries?.(a);if(e===void 0){let t=n.optionalPackageEntry(a);e=t?[t]:void 0}e&&i.push(...e);continue}let s=n.packageEntries?.(a)??[n.packageEntry(a)];if(s.length===0)throw Error(`Layer "${t}" package "${a}" declares no Pi extensions.`);i.push(...s)}return i}function A(e,t){let n=new Set;for(let r of t)for(let t of e.layers[r]?.hookGroups??[])n.add(t);return[...n]}function j(e,t,n){return n?[...t]:t.filter(t=>(e.layers[t]?.hookGroups?.length??0)===0)}function M(e,t=r.homedir()){let n=[],i=f;f=n;try{return{config:T(e,t),diagnostics:n}}finally{f=i}}export{j as filterHookDisabledLayers,c as isLocalPackageSpecifier,k as layerEntries,A as layerHookGroups,T as loadMajorModesConfig,M as loadMajorModesConfigLenient,D as resolveLayers,E as resolvePackageConfigurations};
2
2
  //# sourceMappingURL=majorModes.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"majorModes.mjs","names":[],"sources":["../../src/adapters/majorModes.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\nimport { DOOM_DIR, type DoomConfigProvenance, readDoomConfigSources } from './layeredConfig.ts';\n\nexport type LayerPackageConfig = Record<string, unknown>;\n\n/** One package selected by a layer, plus configuration owned by that package. */\nexport interface LayerPackage {\n name: string;\n optional?: boolean;\n config?: LayerPackageConfig;\n}\n\n/** One configured package found while walking selected layers in order. */\nexport interface ResolvedPackageConfiguration {\n layer: string;\n specifier: string;\n config: LayerPackageConfig;\n baseDirectory: string;\n filePath?: string;\n}\n\nexport interface LayerDefinition {\n /**\n * DoomPi built-in names, or Pi-compatible local extension paths.\n * Bare names resolve from DoomPi's own extension entries. Paths beginning\n * with `./` or `../`, plus absolute paths, resolve against the config that\n * declares the layer and may name a script file or extension directory.\n */\n extensions?: string[];\n /**\n * npm specifiers, or local package paths retained for compatibility.\n * Relative paths resolve against the repository root for `.doom/modes.yaml`\n * and against the global `.doom` directory for\n * `~/.pi/.doom/modes.yaml`.\n */\n packages?: Array<string | LayerPackage>;\n hookGroups?: string[];\n}\n\n/** A layer plus the root its relative extension and package paths resolve against. */\nexport interface ResolvedLayerDefinition extends LayerDefinition {\n baseDirectory: string;\n /** Absolute path of the modes file that declared this layer. */\n filePath?: string;\n}\n\nexport interface MajorModeDefinition {\n /** Human-readable purpose used by pickers and agent-facing catalogs. */\n description: string;\n /** Ordered layer names activated by this mode. */\n layers: string[];\n}\n\nexport interface MajorModesConfig {\n /** Unconditional package bundle loaded before packages from selected named layers. */\n default?: ResolvedLayerDefinition;\n layers: Record<string, ResolvedLayerDefinition>;\n /** The mode selected when neither a flag nor the environment names one. */\n defaultMajorMode: string;\n /** Source that last declared the default, absent for the built-in fallback. */\n defaultMajorModeSource?: DoomConfigProvenance;\n /** Named major modes, including their purpose and ordered layers. */\n majorMode: Record<string, MajorModeDefinition>;\n /** Declaring source for each surviving named mode. */\n majorModeSources?: Record<string, DoomConfigProvenance>;\n}\n\nexport interface LayerResolvers {\n ownEntry: (name: string) => string;\n packageEntry: (name: string) => string;\n optionalPackageEntry: (name: string) => string | undefined;\n /** Resolves every standard package.json pi.extensions entry for a package root. */\n packageEntries?: (name: string) => string[];\n /** Optional counterpart to packageEntries. */\n optionalPackageEntries?: (name: string) => string[] | undefined;\n /** Resolves one path-style layer entry against the config that declared it. */\n localEntry: (specifier: string, baseDirectory: string) => string | undefined;\n /** Resolves every Pi extension entry contributed by a local file or directory. */\n localEntries?: (specifier: string, baseDirectory: string) => string[] | undefined;\n}\n\ninterface ModesDocument {\n default?: unknown;\n layers?: unknown;\n defaultMajorMode?: unknown;\n majorMode?: unknown;\n}\n\nconst MODES_FILE = 'modes.yaml';\nconst FALLBACK_MAJOR_MODE = 'copilot';\nconst MAJOR_MODES_RELATIVE_PATH = path.join(DOOM_DIR, MODES_FILE);\nconst RELATIVE_PREFIXES = ['./', '../'] as const;\nconst DEFAULT_KEYS = ['packages'] as const;\n\n/**\n * Whether a layer value names a path rather than a built-in or installed package.\n *\n * The same rule Node applies to import specifiers, so a bare name is always a\n * logical name and a path always looks like one.\n */\nexport function isLocalPackageSpecifier(specifier: string): boolean {\n return path.isAbsolute(specifier) || RELATIVE_PREFIXES.some((prefix) => specifier.startsWith(prefix));\n}\nconst LAYER_PACKAGE_KEYS = ['name', 'optional', 'config'] as const;\nconst MAJOR_MODE_KEYS = ['description', 'layers'] as const;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction assertKnownKeys(value: Record<string, unknown>, keys: readonly string[], location: string): void {\n const unsupported = Object.keys(value).filter((key) => !keys.includes(key));\n if (unsupported.length > 0) {\n throw new Error(`${location} has unsupported field(s): ${unsupported.join(', ')}`);\n }\n}\n\nfunction parseLayerPackage(layerName: string, value: unknown, index: number): string | LayerPackage {\n const location = `Layer \"${layerName}\" package[${index}] in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (typeof value === 'string') {\n if (!value.trim()) throw new Error(`${location} must be a non-empty string or mapping`);\n return value.trim();\n }\n if (!isRecord(value)) throw new Error(`${location} must be a non-empty string or mapping`);\n assertKnownKeys(value, LAYER_PACKAGE_KEYS, location);\n if (typeof value.name !== 'string' || !value.name.trim()) {\n throw new Error(`${location}.name must be a non-empty string`);\n }\n if (value.optional !== undefined && typeof value.optional !== 'boolean') {\n throw new Error(`${location}.optional must be a boolean`);\n }\n if (value.config !== undefined && !isRecord(value.config)) {\n throw new Error(`${location}.config must be a mapping`);\n }\n return {\n name: value.name.trim(),\n ...(value.optional === true ? { optional: true } : {}),\n ...(value.config ? { config: { ...value.config } } : {}),\n };\n}\n\nfunction parseLayerExtensions(layerName: string, value: unknown): string[] | undefined {\n if (value === undefined) return undefined;\n const location = `Layer \"${layerName}\" extensions in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (!Array.isArray(value)) throw new Error(`${location} must be an array`);\n return value.map((entry, index) => {\n if (typeof entry !== 'string' || !entry.trim()) {\n throw new Error(`${location}[${index}] must be a non-empty string`);\n }\n return entry.trim();\n });\n}\n\nfunction parseLayerPackages(layerName: string, value: unknown): Array<string | LayerPackage> | undefined {\n if (value === undefined) return undefined;\n if (!Array.isArray(value)) {\n throw new Error(`Layer \"${layerName}\" packages in ${MAJOR_MODES_RELATIVE_PATH} must be an array`);\n }\n return value.map((entry, index) => parseLayerPackage(layerName, entry, index));\n}\n\nfunction parseDefaultMajorMode(value: unknown): string {\n if (typeof value !== 'string' || !value.trim()) {\n throw new Error(`defaultMajorMode in ${MAJOR_MODES_RELATIVE_PATH} must be a non-empty string`);\n }\n return value.trim();\n}\n\nfunction parseMajorModeLayers(modeName: string, value: unknown): string[] {\n const location = `Major mode \"${modeName}\" layers in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (!Array.isArray(value)) throw new Error(`${location} must be an array`);\n return value.map((layerName, index) => {\n if (typeof layerName !== 'string' || !layerName.trim()) {\n throw new Error(`${location}[${index}] must be a non-empty string`);\n }\n return layerName.trim();\n });\n}\n\nfunction legacyMajorModeDescription(layers: readonly string[]): string {\n return layers.length > 0 ? `Uses layers: ${layers.join(', ')}.` : 'Uses Doom Pi core extensions only.';\n}\n\nfunction parseMajorMode(name: string, value: unknown): MajorModeDefinition {\n if (Array.isArray(value)) {\n const layers = parseMajorModeLayers(name, value);\n return { description: legacyMajorModeDescription(layers), layers };\n }\n\n const location = `Major mode \"${name}\" in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (!isRecord(value)) throw new Error(`${location} must be a mapping with description and layers`);\n assertKnownKeys(value, MAJOR_MODE_KEYS, location);\n if (typeof value.description !== 'string' || !value.description.trim()) {\n throw new Error(`${location}.description must be a non-empty string`);\n }\n return {\n description: value.description.trim(),\n layers: parseMajorModeLayers(name, value.layers),\n };\n}\n\nfunction parseMajorModes(value: unknown): Record<string, MajorModeDefinition | null> {\n if (value === undefined) return {};\n if (!isRecord(value)) throw new Error(`majorMode in ${MAJOR_MODES_RELATIVE_PATH} must be a mapping`);\n return Object.fromEntries(\n Object.entries(value).map(([name, definition]) => [\n name,\n definition === null ? null : parseMajorMode(name, definition),\n ]),\n );\n}\n\nfunction parseLayer(name: string, definition: unknown): LayerDefinition {\n if (!isRecord(definition)) throw new Error(`Layer \"${name}\" in ${MAJOR_MODES_RELATIVE_PATH} must be a mapping`);\n if ('targets' in definition) {\n throw new Error(\n `Layer \"${name}\" in ${MAJOR_MODES_RELATIVE_PATH} uses removed field \"targets\"; select availability through major modes`,\n );\n }\n if ('config' in definition) {\n throw new Error(\n `Layer \"${name}\" in ${MAJOR_MODES_RELATIVE_PATH} uses unsupported field \"config\"; move it under the package entry that owns it`,\n );\n }\n const extensions = parseLayerExtensions(name, definition.extensions);\n const packages = parseLayerPackages(name, definition.packages);\n return {\n ...definition,\n ...(extensions === undefined ? {} : { extensions }),\n ...(packages === undefined ? {} : { packages }),\n } as LayerDefinition;\n}\n\nfunction parseLayers(value: unknown): Record<string, LayerDefinition | null> {\n if (value === undefined) return {};\n if (!isRecord(value)) throw new Error(`layers in ${MAJOR_MODES_RELATIVE_PATH} must be a mapping`);\n return Object.fromEntries(\n Object.entries(value).map(([name, definition]) => [\n name,\n definition === null ? null : parseLayer(name, definition),\n ]),\n );\n}\n\nfunction parseDefault(value: unknown): LayerDefinition {\n const location = `default in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (!isRecord(value)) throw new Error(`${location} must be a mapping with packages`);\n assertKnownKeys(value, DEFAULT_KEYS, location);\n if (!Object.hasOwn(value, 'packages')) {\n throw new Error(`Layer \"default\" packages in ${MAJOR_MODES_RELATIVE_PATH} must be an array`);\n }\n const packages = parseLayerPackages('default', value.packages);\n return { packages };\n}\n\n/**\n * Reads layers and major modes from the global and repository configs.\n *\n * The repository is read last, so it replaces a global layer or major mode of\n * the same name. Layers keep the root they were declared under so that a\n * global layer naming `./extensions/x` still points beside the global config\n * once it is loaded from inside some other repository.\n */\nexport function loadMajorModesConfig(repoRoot: string, homeDirectory: string = os.homedir()): MajorModesConfig {\n const sources = readDoomConfigSources<ModesDocument>(MODES_FILE, repoRoot, homeDirectory);\n let defaultDefinition: ResolvedLayerDefinition | undefined;\n const layers: Record<string, ResolvedLayerDefinition> = {};\n const majorMode: Record<string, MajorModeDefinition> = {};\n const majorModeSources: Record<string, DoomConfigProvenance> = {};\n let defaultMajorMode = FALLBACK_MAJOR_MODE;\n let defaultMajorModeSource: DoomConfigProvenance | undefined;\n let hasConfiguredDefault = false;\n for (const source of sources) {\n if (Object.hasOwn(source.document, 'default')) {\n defaultDefinition = {\n ...parseDefault(source.document.default),\n baseDirectory: source.baseDirectory,\n filePath: source.filePath,\n };\n }\n for (const [name, definition] of Object.entries(parseLayers(source.document.layers))) {\n if (definition === null) {\n delete layers[name];\n continue;\n }\n layers[name] = {\n ...definition,\n baseDirectory: source.baseDirectory,\n filePath: source.filePath,\n };\n }\n if (Object.hasOwn(source.document, 'defaultMajorMode')) {\n defaultMajorMode = parseDefaultMajorMode(source.document.defaultMajorMode);\n defaultMajorModeSource = { filePath: source.filePath, baseDirectory: source.baseDirectory };\n hasConfiguredDefault = true;\n }\n for (const [name, definition] of Object.entries(parseMajorModes(source.document.majorMode))) {\n if (definition === null) {\n delete majorMode[name];\n delete majorModeSources[name];\n continue;\n }\n majorMode[name] = definition;\n majorModeSources[name] = { filePath: source.filePath, baseDirectory: source.baseDirectory };\n }\n }\n\n for (const [modeName, definition] of Object.entries(majorMode)) {\n for (const name of definition.layers) {\n if (!layers[name]) {\n throw new Error(`Unknown layer \"${name}\" in majorMode.${modeName} of ${MAJOR_MODES_RELATIVE_PATH}`);\n }\n }\n }\n if (hasConfiguredDefault && !Object.hasOwn(majorMode, defaultMajorMode)) {\n throw new Error(`Unknown default major mode \"${defaultMajorMode}\" in ${MAJOR_MODES_RELATIVE_PATH}`);\n }\n return {\n ...(defaultDefinition ? { default: defaultDefinition } : {}),\n layers,\n defaultMajorMode,\n ...(defaultMajorModeSource ? { defaultMajorModeSource } : {}),\n majorMode,\n majorModeSources,\n };\n}\n\n/** Finds configuration owned by one package while preserving layer and package order. */\nexport function resolvePackageConfigurations(\n config: MajorModesConfig,\n names: string[],\n packageName: string,\n): ResolvedPackageConfiguration[] {\n const normalizedPackageName = packageName.trim();\n if (!normalizedPackageName) throw new Error('Package name must be a non-empty string');\n const configurations: ResolvedPackageConfiguration[] = [];\n const definitions: Array<{ name: string; definition: ResolvedLayerDefinition }> = [];\n if (config.default) definitions.push({ name: 'default', definition: config.default });\n for (const name of names) {\n const definition = config.layers[name];\n if (!definition) throw new Error(`Unknown layer: ${name}`);\n definitions.push({ name, definition });\n }\n for (const { name, definition } of definitions) {\n for (const entry of definition.packages ?? []) {\n if (typeof entry === 'string' || entry.config === undefined) continue;\n if (entry.name !== normalizedPackageName && !entry.name.startsWith(`${normalizedPackageName}/`)) continue;\n configurations.push({\n layer: name,\n specifier: entry.name,\n config: { ...entry.config },\n baseDirectory: definition.baseDirectory,\n ...(definition.filePath ? { filePath: definition.filePath } : {}),\n });\n }\n }\n return configurations;\n}\n\n/** Resolves one named major mode into its configured layer components. */\nexport function resolveLayers(config: MajorModesConfig, majorMode: string): string[] {\n if (!Object.hasOwn(config.majorMode, majorMode)) {\n throw new Error(\n `Unknown major mode: ${majorMode}. Known major modes: ${Object.keys(config.majorMode).sort().join(', ')}`,\n );\n }\n return [...config.majorMode[majorMode]!.layers];\n}\n\nfunction resolveLocalEntries(\n resolvers: LayerResolvers,\n specifier: string,\n baseDirectory: string,\n): string[] | undefined {\n const resolved = resolvers.localEntries?.(specifier, baseDirectory);\n if (resolved !== undefined) return resolved;\n const single = resolvers.localEntry(specifier, baseDirectory);\n return single ? [single] : undefined;\n}\n\n/** Resolves one layer's extension and package entries to absolute paths. */\nexport function layerEntries(config: MajorModesConfig, name: string, resolvers: LayerResolvers): string[] {\n const layer = config.layers[name];\n if (!layer) throw new Error(`Unknown layer: ${name}`);\n const entries: string[] = [];\n for (const entry of layer.extensions ?? []) {\n if (!isLocalPackageSpecifier(entry)) {\n entries.push(resolvers.ownEntry(entry));\n continue;\n }\n const resolved = resolveLocalEntries(resolvers, entry, layer.baseDirectory);\n if (!resolved?.length) {\n throw new Error(\n `Layer \"${name}\" extension \"${entry}\" does not resolve under ${layer.baseDirectory}. ` +\n 'Point it at a Pi extension file or directory.',\n );\n }\n entries.push(...resolved);\n }\n for (const entry of layer.packages ?? []) {\n const specifier = typeof entry === 'string' ? entry : entry.name;\n const optional = typeof entry === 'string' ? false : entry.optional === true;\n if (isLocalPackageSpecifier(specifier)) {\n const resolved = resolveLocalEntries(resolvers, specifier, layer.baseDirectory);\n if (resolved?.length) entries.push(...resolved);\n else if (!optional) {\n throw new Error(\n `Layer \"${name}\" package \"${specifier}\" does not resolve under ${layer.baseDirectory}. ` +\n 'Point it at an extension file, or a directory whose package.json declares pi.extensions.',\n );\n }\n continue;\n }\n if (optional) {\n let resolved = resolvers.optionalPackageEntries?.(specifier);\n if (resolved === undefined) {\n const single = resolvers.optionalPackageEntry(specifier);\n resolved = single ? [single] : undefined;\n }\n if (resolved) entries.push(...resolved);\n continue;\n }\n const resolved = resolvers.packageEntries?.(specifier) ?? [resolvers.packageEntry(specifier)];\n if (resolved.length === 0) {\n throw new Error(`Layer \"${name}\" package \"${specifier}\" declares no Pi extensions.`);\n }\n entries.push(...resolved);\n }\n return entries;\n}\n\n/** Hook group ids contributed by the selected layers, deduped. */\nexport function layerHookGroups(config: MajorModesConfig, names: string[]): string[] {\n const groups = new Set<string>();\n for (const name of names) {\n for (const group of config.layers[name]?.hookGroups ?? []) groups.add(group);\n }\n return [...groups];\n}\n\n/**\n * Suppress layers that exist to contribute hook groups when hooks are disabled.\n *\n * Lives beside layerHookGroups because it is the inverse question about the same\n * field, and because every caller that resolves layers has to ask it.\n */\nexport function filterHookDisabledLayers(\n config: MajorModesConfig,\n layers: readonly string[],\n hooks: boolean,\n): string[] {\n if (hooks) return [...layers];\n return layers.filter((layer) => (config.layers[layer]?.hookGroups?.length ?? 0) === 0);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"majorModes.mjs","names":[],"sources":["../../src/adapters/majorModes.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\nimport type { ConfigDiagnostic } from '../types/config.ts';\nimport { DOOM_DIR, type DoomConfigProvenance, readDoomConfigSources } from './layeredConfig.ts';\n\nexport type LayerPackageConfig = Record<string, unknown>;\n\n/** One package selected by a layer, plus configuration owned by that package. */\nexport interface LayerPackage {\n name: string;\n optional?: boolean;\n config?: LayerPackageConfig;\n}\n\n/** One configured package found while walking selected layers in order. */\nexport interface ResolvedPackageConfiguration {\n layer: string;\n specifier: string;\n config: LayerPackageConfig;\n baseDirectory: string;\n filePath?: string;\n}\n\nexport interface LayerDefinition {\n /**\n * DoomPi built-in names, or Pi-compatible local extension paths.\n * Bare names resolve from DoomPi's own extension entries. Paths beginning\n * with `./` or `../`, plus absolute paths, resolve against the config that\n * declares the layer and may name a script file or extension directory.\n */\n extensions?: string[];\n /**\n * npm specifiers, or local package paths retained for compatibility.\n * Relative paths resolve against the repository root for `.doom/modes.yaml`\n * and against the global `.doom` directory for\n * `~/.pi/.doom/modes.yaml`.\n */\n packages?: Array<string | LayerPackage>;\n hookGroups?: string[];\n}\n\n/** A layer plus the root its relative extension and package paths resolve against. */\nexport interface ResolvedLayerDefinition extends LayerDefinition {\n baseDirectory: string;\n /** Absolute path of the modes file that declared this layer. */\n filePath?: string;\n}\n\nexport interface MajorModeDefinition {\n /** Human-readable purpose used by pickers and agent-facing catalogs. */\n description: string;\n /** Ordered layer names activated by this mode. */\n layers: string[];\n}\n\nexport interface MajorModesConfig {\n /** Unconditional package bundle loaded before packages from selected named layers. */\n default?: ResolvedLayerDefinition;\n layers: Record<string, ResolvedLayerDefinition>;\n /** The mode selected when neither a flag nor the environment names one. */\n defaultMajorMode: string;\n /** Source that last declared the default, absent for the built-in fallback. */\n defaultMajorModeSource?: DoomConfigProvenance;\n /** Named major modes, including their purpose and ordered layers. */\n majorMode: Record<string, MajorModeDefinition>;\n /** Declaring source for each surviving named mode. */\n majorModeSources?: Record<string, DoomConfigProvenance>;\n}\n\nexport interface LayerResolvers {\n ownEntry: (name: string) => string;\n packageEntry: (name: string) => string;\n optionalPackageEntry: (name: string) => string | undefined;\n /** Resolves every standard package.json pi.extensions entry for a package root. */\n packageEntries?: (name: string) => string[];\n /** Optional counterpart to packageEntries. */\n optionalPackageEntries?: (name: string) => string[] | undefined;\n /** Resolves one path-style layer entry against the config that declared it. */\n localEntry: (specifier: string, baseDirectory: string) => string | undefined;\n /** Resolves every Pi extension entry contributed by a local file or directory. */\n localEntries?: (specifier: string, baseDirectory: string) => string[] | undefined;\n}\n\ninterface ModesDocument {\n default?: unknown;\n layers?: unknown;\n defaultMajorMode?: unknown;\n majorMode?: unknown;\n}\n\nconst MODES_FILE = 'modes.yaml';\nconst FALLBACK_MAJOR_MODE = 'copilot';\nconst MAJOR_MODES_RELATIVE_PATH = path.join(DOOM_DIR, MODES_FILE);\nconst RELATIVE_PREFIXES = ['./', '../'] as const;\nconst DEFAULT_KEYS = ['packages'] as const;\n\n/**\n * Whether a layer value names a path rather than a built-in or installed package.\n *\n * The same rule Node applies to import specifiers, so a bare name is always a\n * logical name and a path always looks like one.\n */\nexport function isLocalPackageSpecifier(specifier: string): boolean {\n return path.isAbsolute(specifier) || RELATIVE_PREFIXES.some((prefix) => specifier.startsWith(prefix));\n}\nconst LAYER_PACKAGE_KEYS = ['name', 'optional', 'config'] as const;\nconst MAJOR_MODE_KEYS = ['description', 'layers'] as const;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Where a lenient load collects unknown keys instead of throwing.\n *\n * Same shape as the sink in configPolicy, for the same reason: `assertKnownKeys`\n * is the only reader and the whole load is synchronous.\n */\nlet unknownKeySink: ConfigDiagnostic[] | undefined;\n\nfunction assertKnownKeys(value: Record<string, unknown>, keys: readonly string[], location: string): void {\n const unsupported = Object.keys(value).filter((key) => !keys.includes(key));\n if (unsupported.length === 0) return;\n if (unknownKeySink !== undefined) {\n for (const key of unsupported) {\n unknownKeySink.push({\n filePath: MAJOR_MODES_RELATIVE_PATH,\n path: key,\n message: `${location} has unsupported field`,\n });\n }\n return;\n }\n throw new Error(`${location} has unsupported field(s): ${unsupported.join(', ')}`);\n}\n\nfunction parseLayerPackage(layerName: string, value: unknown, index: number): string | LayerPackage {\n const location = `Layer \"${layerName}\" package[${index}] in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (typeof value === 'string') {\n if (!value.trim()) throw new Error(`${location} must be a non-empty string or mapping`);\n return value.trim();\n }\n if (!isRecord(value)) throw new Error(`${location} must be a non-empty string or mapping`);\n assertKnownKeys(value, LAYER_PACKAGE_KEYS, location);\n if (typeof value.name !== 'string' || !value.name.trim()) {\n throw new Error(`${location}.name must be a non-empty string`);\n }\n if (value.optional !== undefined && typeof value.optional !== 'boolean') {\n throw new Error(`${location}.optional must be a boolean`);\n }\n if (value.config !== undefined && !isRecord(value.config)) {\n throw new Error(`${location}.config must be a mapping`);\n }\n return {\n name: value.name.trim(),\n ...(value.optional === true ? { optional: true } : {}),\n ...(value.config ? { config: { ...value.config } } : {}),\n };\n}\n\nfunction parseLayerExtensions(layerName: string, value: unknown): string[] | undefined {\n if (value === undefined) return undefined;\n const location = `Layer \"${layerName}\" extensions in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (!Array.isArray(value)) throw new Error(`${location} must be an array`);\n return value.map((entry, index) => {\n if (typeof entry !== 'string' || !entry.trim()) {\n throw new Error(`${location}[${index}] must be a non-empty string`);\n }\n return entry.trim();\n });\n}\n\nfunction parseLayerPackages(layerName: string, value: unknown): Array<string | LayerPackage> | undefined {\n if (value === undefined) return undefined;\n if (!Array.isArray(value)) {\n throw new Error(`Layer \"${layerName}\" packages in ${MAJOR_MODES_RELATIVE_PATH} must be an array`);\n }\n return value.map((entry, index) => parseLayerPackage(layerName, entry, index));\n}\n\nfunction parseDefaultMajorMode(value: unknown): string {\n if (typeof value !== 'string' || !value.trim()) {\n throw new Error(`defaultMajorMode in ${MAJOR_MODES_RELATIVE_PATH} must be a non-empty string`);\n }\n return value.trim();\n}\n\nfunction parseMajorModeLayers(modeName: string, value: unknown): string[] {\n const location = `Major mode \"${modeName}\" layers in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (!Array.isArray(value)) throw new Error(`${location} must be an array`);\n return value.map((layerName, index) => {\n if (typeof layerName !== 'string' || !layerName.trim()) {\n throw new Error(`${location}[${index}] must be a non-empty string`);\n }\n return layerName.trim();\n });\n}\n\nfunction legacyMajorModeDescription(layers: readonly string[]): string {\n return layers.length > 0 ? `Uses layers: ${layers.join(', ')}.` : 'Uses Doom Pi core extensions only.';\n}\n\nfunction parseMajorMode(name: string, value: unknown): MajorModeDefinition {\n if (Array.isArray(value)) {\n const layers = parseMajorModeLayers(name, value);\n return { description: legacyMajorModeDescription(layers), layers };\n }\n\n const location = `Major mode \"${name}\" in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (!isRecord(value)) throw new Error(`${location} must be a mapping with description and layers`);\n assertKnownKeys(value, MAJOR_MODE_KEYS, location);\n if (typeof value.description !== 'string' || !value.description.trim()) {\n throw new Error(`${location}.description must be a non-empty string`);\n }\n return {\n description: value.description.trim(),\n layers: parseMajorModeLayers(name, value.layers),\n };\n}\n\nfunction parseMajorModes(value: unknown): Record<string, MajorModeDefinition | null> {\n if (value === undefined) return {};\n if (!isRecord(value)) throw new Error(`majorMode in ${MAJOR_MODES_RELATIVE_PATH} must be a mapping`);\n return Object.fromEntries(\n Object.entries(value).map(([name, definition]) => [\n name,\n definition === null ? null : parseMajorMode(name, definition),\n ]),\n );\n}\n\nfunction parseLayer(name: string, definition: unknown): LayerDefinition {\n if (!isRecord(definition)) throw new Error(`Layer \"${name}\" in ${MAJOR_MODES_RELATIVE_PATH} must be a mapping`);\n if ('targets' in definition) {\n throw new Error(\n `Layer \"${name}\" in ${MAJOR_MODES_RELATIVE_PATH} uses removed field \"targets\"; select availability through major modes`,\n );\n }\n if ('config' in definition) {\n throw new Error(\n `Layer \"${name}\" in ${MAJOR_MODES_RELATIVE_PATH} uses unsupported field \"config\"; move it under the package entry that owns it`,\n );\n }\n const extensions = parseLayerExtensions(name, definition.extensions);\n const packages = parseLayerPackages(name, definition.packages);\n return {\n ...definition,\n ...(extensions === undefined ? {} : { extensions }),\n ...(packages === undefined ? {} : { packages }),\n } as LayerDefinition;\n}\n\nfunction parseLayers(value: unknown): Record<string, LayerDefinition | null> {\n if (value === undefined) return {};\n if (!isRecord(value)) throw new Error(`layers in ${MAJOR_MODES_RELATIVE_PATH} must be a mapping`);\n return Object.fromEntries(\n Object.entries(value).map(([name, definition]) => [\n name,\n definition === null ? null : parseLayer(name, definition),\n ]),\n );\n}\n\nfunction parseDefault(value: unknown): LayerDefinition {\n const location = `default in ${MAJOR_MODES_RELATIVE_PATH}`;\n if (!isRecord(value)) throw new Error(`${location} must be a mapping with packages`);\n assertKnownKeys(value, DEFAULT_KEYS, location);\n if (!Object.hasOwn(value, 'packages')) {\n throw new Error(`Layer \"default\" packages in ${MAJOR_MODES_RELATIVE_PATH} must be an array`);\n }\n const packages = parseLayerPackages('default', value.packages);\n return { packages };\n}\n\n/**\n * Reads layers and major modes from the global and repository configs.\n *\n * The repository is read last, so it replaces a global layer or major mode of\n * the same name. Layers keep the root they were declared under so that a\n * global layer naming `./extensions/x` still points beside the global config\n * once it is loaded from inside some other repository.\n */\nexport function loadMajorModesConfig(repoRoot: string, homeDirectory: string = os.homedir()): MajorModesConfig {\n const sources = readDoomConfigSources<ModesDocument>(MODES_FILE, repoRoot, homeDirectory);\n let defaultDefinition: ResolvedLayerDefinition | undefined;\n const layers: Record<string, ResolvedLayerDefinition> = {};\n const majorMode: Record<string, MajorModeDefinition> = {};\n const majorModeSources: Record<string, DoomConfigProvenance> = {};\n let defaultMajorMode = FALLBACK_MAJOR_MODE;\n let defaultMajorModeSource: DoomConfigProvenance | undefined;\n let hasConfiguredDefault = false;\n for (const source of sources) {\n if (Object.hasOwn(source.document, 'default')) {\n defaultDefinition = {\n ...parseDefault(source.document.default),\n baseDirectory: source.baseDirectory,\n filePath: source.filePath,\n };\n }\n for (const [name, definition] of Object.entries(parseLayers(source.document.layers))) {\n if (definition === null) {\n delete layers[name];\n continue;\n }\n layers[name] = {\n ...definition,\n baseDirectory: source.baseDirectory,\n filePath: source.filePath,\n };\n }\n if (Object.hasOwn(source.document, 'defaultMajorMode')) {\n defaultMajorMode = parseDefaultMajorMode(source.document.defaultMajorMode);\n defaultMajorModeSource = { filePath: source.filePath, baseDirectory: source.baseDirectory };\n hasConfiguredDefault = true;\n }\n for (const [name, definition] of Object.entries(parseMajorModes(source.document.majorMode))) {\n if (definition === null) {\n delete majorMode[name];\n delete majorModeSources[name];\n continue;\n }\n majorMode[name] = definition;\n majorModeSources[name] = { filePath: source.filePath, baseDirectory: source.baseDirectory };\n }\n }\n\n for (const [modeName, definition] of Object.entries(majorMode)) {\n for (const name of definition.layers) {\n if (!layers[name]) {\n throw new Error(`Unknown layer \"${name}\" in majorMode.${modeName} of ${MAJOR_MODES_RELATIVE_PATH}`);\n }\n }\n }\n if (hasConfiguredDefault && !Object.hasOwn(majorMode, defaultMajorMode)) {\n throw new Error(`Unknown default major mode \"${defaultMajorMode}\" in ${MAJOR_MODES_RELATIVE_PATH}`);\n }\n return {\n ...(defaultDefinition ? { default: defaultDefinition } : {}),\n layers,\n defaultMajorMode,\n ...(defaultMajorModeSource ? { defaultMajorModeSource } : {}),\n majorMode,\n majorModeSources,\n };\n}\n\n/** Finds configuration owned by one package while preserving layer and package order. */\nexport function resolvePackageConfigurations(\n config: MajorModesConfig,\n names: string[],\n packageName: string,\n): ResolvedPackageConfiguration[] {\n const normalizedPackageName = packageName.trim();\n if (!normalizedPackageName) throw new Error('Package name must be a non-empty string');\n const configurations: ResolvedPackageConfiguration[] = [];\n const definitions: Array<{ name: string; definition: ResolvedLayerDefinition }> = [];\n if (config.default) definitions.push({ name: 'default', definition: config.default });\n for (const name of names) {\n const definition = config.layers[name];\n if (!definition) throw new Error(`Unknown layer: ${name}`);\n definitions.push({ name, definition });\n }\n for (const { name, definition } of definitions) {\n for (const entry of definition.packages ?? []) {\n if (typeof entry === 'string' || entry.config === undefined) continue;\n if (entry.name !== normalizedPackageName && !entry.name.startsWith(`${normalizedPackageName}/`)) continue;\n configurations.push({\n layer: name,\n specifier: entry.name,\n config: { ...entry.config },\n baseDirectory: definition.baseDirectory,\n ...(definition.filePath ? { filePath: definition.filePath } : {}),\n });\n }\n }\n return configurations;\n}\n\n/** Resolves one named major mode into its configured layer components. */\nexport function resolveLayers(config: MajorModesConfig, majorMode: string): string[] {\n if (!Object.hasOwn(config.majorMode, majorMode)) {\n throw new Error(\n `Unknown major mode: ${majorMode}. Known major modes: ${Object.keys(config.majorMode).sort().join(', ')}`,\n );\n }\n return [...config.majorMode[majorMode]!.layers];\n}\n\nfunction resolveLocalEntries(\n resolvers: LayerResolvers,\n specifier: string,\n baseDirectory: string,\n): string[] | undefined {\n const resolved = resolvers.localEntries?.(specifier, baseDirectory);\n if (resolved !== undefined) return resolved;\n const single = resolvers.localEntry(specifier, baseDirectory);\n return single ? [single] : undefined;\n}\n\n/** Resolves one layer's extension and package entries to absolute paths. */\nexport function layerEntries(config: MajorModesConfig, name: string, resolvers: LayerResolvers): string[] {\n const layer = config.layers[name];\n if (!layer) throw new Error(`Unknown layer: ${name}`);\n const entries: string[] = [];\n for (const entry of layer.extensions ?? []) {\n if (!isLocalPackageSpecifier(entry)) {\n entries.push(resolvers.ownEntry(entry));\n continue;\n }\n const resolved = resolveLocalEntries(resolvers, entry, layer.baseDirectory);\n if (!resolved?.length) {\n throw new Error(\n `Layer \"${name}\" extension \"${entry}\" does not resolve under ${layer.baseDirectory}. ` +\n 'Point it at a Pi extension file or directory.',\n );\n }\n entries.push(...resolved);\n }\n for (const entry of layer.packages ?? []) {\n const specifier = typeof entry === 'string' ? entry : entry.name;\n const optional = typeof entry === 'string' ? false : entry.optional === true;\n if (isLocalPackageSpecifier(specifier)) {\n const resolved = resolveLocalEntries(resolvers, specifier, layer.baseDirectory);\n if (resolved?.length) entries.push(...resolved);\n else if (!optional) {\n throw new Error(\n `Layer \"${name}\" package \"${specifier}\" does not resolve under ${layer.baseDirectory}. ` +\n 'Point it at an extension file, or a directory whose package.json declares pi.extensions.',\n );\n }\n continue;\n }\n if (optional) {\n let resolved = resolvers.optionalPackageEntries?.(specifier);\n if (resolved === undefined) {\n const single = resolvers.optionalPackageEntry(specifier);\n resolved = single ? [single] : undefined;\n }\n if (resolved) entries.push(...resolved);\n continue;\n }\n const resolved = resolvers.packageEntries?.(specifier) ?? [resolvers.packageEntry(specifier)];\n if (resolved.length === 0) {\n throw new Error(`Layer \"${name}\" package \"${specifier}\" declares no Pi extensions.`);\n }\n entries.push(...resolved);\n }\n return entries;\n}\n\n/** Hook group ids contributed by the selected layers, deduped. */\nexport function layerHookGroups(config: MajorModesConfig, names: string[]): string[] {\n const groups = new Set<string>();\n for (const name of names) {\n for (const group of config.layers[name]?.hookGroups ?? []) groups.add(group);\n }\n return [...groups];\n}\n\n/**\n * Suppress layers that exist to contribute hook groups when hooks are disabled.\n *\n * Lives beside layerHookGroups because it is the inverse question about the same\n * field, and because every caller that resolves layers has to ask it.\n */\nexport function filterHookDisabledLayers(\n config: MajorModesConfig,\n layers: readonly string[],\n hooks: boolean,\n): string[] {\n if (hooks) return [...layers];\n return layers.filter((layer) => (config.layers[layer]?.hookGroups?.length ?? 0) === 0);\n}\n\n/**\n * Reads layers and major modes, tolerating unknown keys.\n *\n * `doompi sync` uses this so a modes.yaml written against a different set of\n * layer packages cannot break a build. Every other caller keeps the strict\n * `loadMajorModesConfig`, and `doompi doctor` still reports what was tolerated.\n */\nexport function loadMajorModesConfigLenient(\n repoRoot: string,\n homeDirectory: string = os.homedir(),\n): { config: MajorModesConfig; diagnostics: ConfigDiagnostic[] } {\n const diagnostics: ConfigDiagnostic[] = [];\n const previous = unknownKeySink;\n unknownKeySink = diagnostics;\n try {\n return { config: loadMajorModesConfig(repoRoot, homeDirectory), diagnostics };\n } finally {\n unknownKeySink = previous;\n }\n}\n"],"mappings":"0HA0FA,MAAM,EAAa,aAEb,EAA4B,EAAK,KAAK,EAAU,CAAU,EAC1D,EAAoB,CAAC,KAAM,KAAK,EAChC,EAAe,CAAC,UAAU,EAQhC,SAAgB,EAAwB,EAA4B,CAClE,OAAO,EAAK,WAAW,CAAS,GAAK,EAAkB,KAAM,GAAW,EAAU,WAAW,CAAM,CAAC,CACtG,CACA,MAAM,EAAqB,CAAC,OAAQ,WAAY,QAAQ,EAClD,EAAkB,CAAC,cAAe,QAAQ,EAEhD,SAAS,EAAS,EAAkD,CAClE,OAAO,OAAO,GAAU,YAAY,GAAkB,CAAC,MAAM,QAAQ,CAAK,CAC5E,CAQA,IAAI,EAEJ,SAAS,EAAgB,EAAgC,EAAyB,EAAwB,CACxG,IAAM,EAAc,OAAO,KAAK,CAAK,CAAC,CAAC,OAAQ,GAAQ,CAAC,EAAK,SAAS,CAAG,CAAC,EACtE,KAAY,SAAW,EAC3B,IAAI,IAAmB,IAAA,GAAW,CAChC,IAAK,IAAM,KAAO,EAChB,EAAe,KAAK,CAClB,SAAU,EACV,KAAM,EACN,QAAS,GAAG,EAAS,uBACvB,CAAC,EAEH,MACF,CACA,MAAU,MAAM,GAAG,EAAS,6BAA6B,EAAY,KAAK,IAAI,GAAG,CADjF,CAEF,CAEA,SAAS,EAAkB,EAAmB,EAAgB,EAAsC,CAClG,IAAM,EAAW,UAAU,EAAU,YAAY,EAAM,OAAO,IAC9D,GAAI,OAAO,GAAU,SAAU,CAC7B,GAAI,CAAC,EAAM,KAAK,EAAG,MAAU,MAAM,GAAG,EAAS,uCAAuC,EACtF,OAAO,EAAM,KAAK,CACpB,CACA,GAAI,CAAC,EAAS,CAAK,EAAG,MAAU,MAAM,GAAG,EAAS,uCAAuC,EAEzF,GADA,EAAgB,EAAO,EAAoB,CAAQ,EAC/C,OAAO,EAAM,MAAS,UAAY,CAAC,EAAM,KAAK,KAAK,EACrD,MAAU,MAAM,GAAG,EAAS,iCAAiC,EAE/D,GAAI,EAAM,WAAa,IAAA,IAAa,OAAO,EAAM,UAAa,UAC5D,MAAU,MAAM,GAAG,EAAS,4BAA4B,EAE1D,GAAI,EAAM,SAAW,IAAA,IAAa,CAAC,EAAS,EAAM,MAAM,EACtD,MAAU,MAAM,GAAG,EAAS,0BAA0B,EAExD,MAAO,CACL,KAAM,EAAM,KAAK,KAAK,EACtB,GAAI,EAAM,WAAa,GAAO,CAAE,SAAU,EAAK,EAAI,CAAC,EACpD,GAAI,EAAM,OAAS,CAAE,OAAQ,CAAE,GAAG,EAAM,MAAO,CAAE,EAAI,CAAC,CACxD,CACF,CAEA,SAAS,EAAqB,EAAmB,EAAsC,CACrF,GAAI,IAAU,IAAA,GAAW,OACzB,IAAM,EAAW,UAAU,EAAU,kBAAkB,IACvD,GAAI,CAAC,MAAM,QAAQ,CAAK,EAAG,MAAU,MAAM,GAAG,EAAS,kBAAkB,EACzE,OAAO,EAAM,KAAK,EAAO,IAAU,CACjC,GAAI,OAAO,GAAU,UAAY,CAAC,EAAM,KAAK,EAC3C,MAAU,MAAM,GAAG,EAAS,GAAG,EAAM,6BAA6B,EAEpE,OAAO,EAAM,KAAK,CACpB,CAAC,CACH,CAEA,SAAS,EAAmB,EAAmB,EAA0D,CACnG,OAAU,IAAA,GACd,IAAI,CAAC,MAAM,QAAQ,CAAK,EACtB,MAAU,MAAM,UAAU,EAAU,gBAAgB,EAA0B,kBAAkB,EAElG,OAAO,EAAM,KAAK,EAAO,IAAU,EAAkB,EAAW,EAAO,CAAK,CAAC,CAFqB,CAGpG,CAEA,SAAS,EAAsB,EAAwB,CACrD,GAAI,OAAO,GAAU,UAAY,CAAC,EAAM,KAAK,EAC3C,MAAU,MAAM,uBAAuB,EAA0B,4BAA4B,EAE/F,OAAO,EAAM,KAAK,CACpB,CAEA,SAAS,EAAqB,EAAkB,EAA0B,CACxE,IAAM,EAAW,eAAe,EAAS,cAAc,IACvD,GAAI,CAAC,MAAM,QAAQ,CAAK,EAAG,MAAU,MAAM,GAAG,EAAS,kBAAkB,EACzE,OAAO,EAAM,KAAK,EAAW,IAAU,CACrC,GAAI,OAAO,GAAc,UAAY,CAAC,EAAU,KAAK,EACnD,MAAU,MAAM,GAAG,EAAS,GAAG,EAAM,6BAA6B,EAEpE,OAAO,EAAU,KAAK,CACxB,CAAC,CACH,CAEA,SAAS,EAA2B,EAAmC,CACrE,OAAO,EAAO,OAAS,EAAI,gBAAgB,EAAO,KAAK,IAAI,EAAE,GAAK,oCACpE,CAEA,SAAS,EAAe,EAAc,EAAqC,CACzE,GAAI,MAAM,QAAQ,CAAK,EAAG,CACxB,IAAM,EAAS,EAAqB,EAAM,CAAK,EAC/C,MAAO,CAAE,YAAa,EAA2B,CAAM,EAAG,QAAO,CACnE,CAEA,IAAM,EAAW,eAAe,EAAK,OAAO,IAC5C,GAAI,CAAC,EAAS,CAAK,EAAG,MAAU,MAAM,GAAG,EAAS,+CAA+C,EAEjG,GADA,EAAgB,EAAO,EAAiB,CAAQ,EAC5C,OAAO,EAAM,aAAgB,UAAY,CAAC,EAAM,YAAY,KAAK,EACnE,MAAU,MAAM,GAAG,EAAS,wCAAwC,EAEtE,MAAO,CACL,YAAa,EAAM,YAAY,KAAK,EACpC,OAAQ,EAAqB,EAAM,EAAM,MAAM,CACjD,CACF,CAEA,SAAS,EAAgB,EAA4D,CACnF,GAAI,IAAU,IAAA,GAAW,MAAO,CAAC,EACjC,GAAI,CAAC,EAAS,CAAK,EAAG,MAAU,MAAM,gBAAgB,EAA0B,mBAAmB,EACnG,OAAO,OAAO,YACZ,OAAO,QAAQ,CAAK,CAAC,CAAC,KAAK,CAAC,EAAM,KAAgB,CAChD,EACA,IAAe,KAAO,KAAO,EAAe,EAAM,CAAU,CAC9D,CAAC,CACH,CACF,CAEA,SAAS,EAAW,EAAc,EAAsC,CACtE,GAAI,CAAC,EAAS,CAAU,EAAG,MAAU,MAAM,UAAU,EAAK,OAAO,EAA0B,mBAAmB,EAC9G,GAAI,YAAa,EACf,MAAU,MACR,UAAU,EAAK,OAAO,EAA0B,uEAClD,EAEF,GAAI,WAAY,EACd,MAAU,MACR,UAAU,EAAK,OAAO,EAA0B,+EAClD,EAEF,IAAM,EAAa,EAAqB,EAAM,EAAW,UAAU,EAC7D,EAAW,EAAmB,EAAM,EAAW,QAAQ,EAC7D,MAAO,CACL,GAAG,EACH,GAAI,IAAe,IAAA,GAAY,CAAC,EAAI,CAAE,YAAW,EACjD,GAAI,IAAa,IAAA,GAAY,CAAC,EAAI,CAAE,UAAS,CAC/C,CACF,CAEA,SAAS,EAAY,EAAwD,CAC3E,GAAI,IAAU,IAAA,GAAW,MAAO,CAAC,EACjC,GAAI,CAAC,EAAS,CAAK,EAAG,MAAU,MAAM,aAAa,EAA0B,mBAAmB,EAChG,OAAO,OAAO,YACZ,OAAO,QAAQ,CAAK,CAAC,CAAC,KAAK,CAAC,EAAM,KAAgB,CAChD,EACA,IAAe,KAAO,KAAO,EAAW,EAAM,CAAU,CAC1D,CAAC,CACH,CACF,CAEA,SAAS,EAAa,EAAiC,CACrD,IAAM,EAAW,cAAc,IAC/B,GAAI,CAAC,EAAS,CAAK,EAAG,MAAU,MAAM,GAAG,EAAS,iCAAiC,EAEnF,GADA,EAAgB,EAAO,EAAc,CAAQ,EACzC,CAAC,OAAO,OAAO,EAAO,UAAU,EAClC,MAAU,MAAM,+BAA+B,EAA0B,kBAAkB,EAG7F,MAAO,CAAE,SADQ,EAAmB,UAAW,EAAM,QACrC,CAAE,CACpB,CAUA,SAAgB,EAAqB,EAAkB,EAAwB,EAAG,QAAQ,EAAqB,CAC7G,IAAM,EAAU,EAAqC,EAAY,EAAU,CAAa,EACpF,EACE,EAAkD,CAAC,EACnD,EAAiD,CAAC,EAClD,EAAyD,CAAC,EAC5D,EAAmB,UACnB,EACA,EAAuB,GAC3B,IAAK,IAAM,KAAU,EAAS,CACxB,OAAO,OAAO,EAAO,SAAU,SAAS,IAC1C,EAAoB,CAClB,GAAG,EAAa,EAAO,SAAS,OAAO,EACvC,cAAe,EAAO,cACtB,SAAU,EAAO,QACnB,GAEF,IAAK,GAAM,CAAC,EAAM,KAAe,OAAO,QAAQ,EAAY,EAAO,SAAS,MAAM,CAAC,EAAG,CACpF,GAAI,IAAe,KAAM,CACvB,OAAO,EAAO,GACd,QACF,CACA,EAAO,GAAQ,CACb,GAAG,EACH,cAAe,EAAO,cACtB,SAAU,EAAO,QACnB,CACF,CACI,OAAO,OAAO,EAAO,SAAU,kBAAkB,IACnD,EAAmB,EAAsB,EAAO,SAAS,gBAAgB,EACzE,EAAyB,CAAE,SAAU,EAAO,SAAU,cAAe,EAAO,aAAc,EAC1F,EAAuB,IAEzB,IAAK,GAAM,CAAC,EAAM,KAAe,OAAO,QAAQ,EAAgB,EAAO,SAAS,SAAS,CAAC,EAAG,CAC3F,GAAI,IAAe,KAAM,CACvB,OAAO,EAAU,GACjB,OAAO,EAAiB,GACxB,QACF,CACA,EAAU,GAAQ,EAClB,EAAiB,GAAQ,CAAE,SAAU,EAAO,SAAU,cAAe,EAAO,aAAc,CAC5F,CACF,CAEA,IAAK,GAAM,CAAC,EAAU,KAAe,OAAO,QAAQ,CAAS,EAC3D,IAAK,IAAM,KAAQ,EAAW,OAC5B,GAAI,CAAC,EAAO,GACV,MAAU,MAAM,kBAAkB,EAAK,iBAAiB,EAAS,MAAM,GAA2B,EAIxG,GAAI,GAAwB,CAAC,OAAO,OAAO,EAAW,CAAgB,EACpE,MAAU,MAAM,+BAA+B,EAAiB,OAAO,GAA2B,EAEpG,MAAO,CACL,GAAI,EAAoB,CAAE,QAAS,CAAkB,EAAI,CAAC,EAC1D,SACA,mBACA,GAAI,EAAyB,CAAE,wBAAuB,EAAI,CAAC,EAC3D,YACA,kBACF,CACF,CAGA,SAAgB,EACd,EACA,EACA,EACgC,CAChC,IAAM,EAAwB,EAAY,KAAK,EAC/C,GAAI,CAAC,EAAuB,MAAU,MAAM,yCAAyC,EACrF,IAAM,EAAiD,CAAC,EAClD,EAA4E,CAAC,EAC/E,EAAO,SAAS,EAAY,KAAK,CAAE,KAAM,UAAW,WAAY,EAAO,OAAQ,CAAC,EACpF,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAa,EAAO,OAAO,GACjC,GAAI,CAAC,EAAY,MAAU,MAAM,kBAAkB,GAAM,EACzD,EAAY,KAAK,CAAE,OAAM,YAAW,CAAC,CACvC,CACA,IAAK,GAAM,CAAE,OAAM,gBAAgB,EACjC,IAAK,IAAM,KAAS,EAAW,UAAY,CAAC,EACtC,OAAO,GAAU,UAAY,EAAM,SAAW,IAAA,KAC9C,EAAM,OAAS,GAA0B,EAAM,KAAK,WAAW,GAAG,EAAsB,EAAE,IAC9F,EAAe,KAAK,CAClB,MAAO,EACP,UAAW,EAAM,KACjB,OAAQ,CAAE,GAAG,EAAM,MAAO,EAC1B,cAAe,EAAW,cAC1B,GAAI,EAAW,SAAW,CAAE,SAAU,EAAW,QAAS,EAAI,CAAC,CACjE,CAAC,EAGL,OAAO,CACT,CAGA,SAAgB,EAAc,EAA0B,EAA6B,CACnF,GAAI,CAAC,OAAO,OAAO,EAAO,UAAW,CAAS,EAC5C,MAAU,MACR,uBAAuB,EAAU,uBAAuB,OAAO,KAAK,EAAO,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,GACxG,EAEF,MAAO,CAAC,GAAG,EAAO,UAAU,EAAU,CAAE,MAAM,CAChD,CAEA,SAAS,EACP,EACA,EACA,EACsB,CACtB,IAAM,EAAW,EAAU,eAAe,EAAW,CAAa,EAClE,GAAI,IAAa,IAAA,GAAW,OAAO,EACnC,IAAM,EAAS,EAAU,WAAW,EAAW,CAAa,EAC5D,OAAO,EAAS,CAAC,CAAM,EAAI,IAAA,EAC7B,CAGA,SAAgB,EAAa,EAA0B,EAAc,EAAqC,CACxG,IAAM,EAAQ,EAAO,OAAO,GAC5B,GAAI,CAAC,EAAO,MAAU,MAAM,kBAAkB,GAAM,EACpD,IAAM,EAAoB,CAAC,EAC3B,IAAK,IAAM,KAAS,EAAM,YAAc,CAAC,EAAG,CAC1C,GAAI,CAAC,EAAwB,CAAK,EAAG,CACnC,EAAQ,KAAK,EAAU,SAAS,CAAK,CAAC,EACtC,QACF,CACA,IAAM,EAAW,EAAoB,EAAW,EAAO,EAAM,aAAa,EAC1E,GAAI,CAAC,GAAU,OACb,MAAU,MACR,UAAU,EAAK,eAAe,EAAM,2BAA2B,EAAM,cAAc,gDAErF,EAEF,EAAQ,KAAK,GAAG,CAAQ,CAC1B,CACA,IAAK,IAAM,KAAS,EAAM,UAAY,CAAC,EAAG,CACxC,IAAM,EAAY,OAAO,GAAU,SAAW,EAAQ,EAAM,KACtD,EAAW,OAAO,GAAU,UAAmB,EAAM,WAAa,GACxE,GAAI,EAAwB,CAAS,EAAG,CACtC,IAAM,EAAW,EAAoB,EAAW,EAAW,EAAM,aAAa,EAC9E,GAAI,GAAU,OAAQ,EAAQ,KAAK,GAAG,CAAQ,OACzC,GAAI,CAAC,EACR,MAAU,MACR,UAAU,EAAK,aAAa,EAAU,2BAA2B,EAAM,cAAc,2FAEvF,EAEF,QACF,CACA,GAAI,EAAU,CACZ,IAAI,EAAW,EAAU,yBAAyB,CAAS,EAC3D,GAAI,IAAa,IAAA,GAAW,CAC1B,IAAM,EAAS,EAAU,qBAAqB,CAAS,EACvD,EAAW,EAAS,CAAC,CAAM,EAAI,IAAA,EACjC,CACI,GAAU,EAAQ,KAAK,GAAG,CAAQ,EACtC,QACF,CACA,IAAM,EAAW,EAAU,iBAAiB,CAAS,GAAK,CAAC,EAAU,aAAa,CAAS,CAAC,EAC5F,GAAI,EAAS,SAAW,EACtB,MAAU,MAAM,UAAU,EAAK,aAAa,EAAU,6BAA6B,EAErF,EAAQ,KAAK,GAAG,CAAQ,CAC1B,CACA,OAAO,CACT,CAGA,SAAgB,EAAgB,EAA0B,EAA2B,CACnF,IAAM,EAAS,IAAI,IACnB,IAAK,IAAM,KAAQ,EACjB,IAAK,IAAM,KAAS,EAAO,OAAO,EAAK,EAAE,YAAc,CAAC,EAAG,EAAO,IAAI,CAAK,EAE7E,MAAO,CAAC,GAAG,CAAM,CACnB,CAQA,SAAgB,EACd,EACA,EACA,EACU,CAEV,OADI,EAAc,CAAC,GAAG,CAAM,EACrB,EAAO,OAAQ,IAAW,EAAO,OAAO,EAAM,EAAE,YAAY,QAAU,KAAO,CAAC,CACvF,CASA,SAAgB,EACd,EACA,EAAwB,EAAG,QAAQ,EAC4B,CAC/D,IAAM,EAAkC,CAAC,EACnC,EAAW,EACjB,EAAiB,EACjB,GAAI,CACF,MAAO,CAAE,OAAQ,EAAqB,EAAU,CAAa,EAAG,aAAY,CAC9E,QAAU,CACR,EAAiB,CACnB,CACF"}