@agimon-ai/doompi-config 0.0.1-alpha.0 → 0.0.1-alpha.10
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.
- package/dist/adapters/config.cjs +2 -0
- package/dist/adapters/config.cjs.map +1 -0
- package/dist/adapters/config.d.cts +25 -0
- package/dist/adapters/config.d.cts.map +1 -0
- package/dist/adapters/config.d.mts +25 -0
- package/dist/adapters/config.d.mts.map +1 -0
- package/dist/adapters/config.mjs +2 -0
- package/dist/adapters/config.mjs.map +1 -0
- package/dist/adapters/init.cjs +74 -0
- package/dist/adapters/init.cjs.map +1 -0
- package/dist/adapters/init.d.cts +33 -0
- package/dist/adapters/init.d.cts.map +1 -0
- package/dist/adapters/init.d.mts +33 -0
- package/dist/adapters/init.d.mts.map +1 -0
- package/dist/adapters/init.mjs +74 -0
- package/dist/adapters/init.mjs.map +1 -0
- package/dist/adapters/pi/piContext.cjs +2 -0
- package/dist/adapters/pi/piContext.cjs.map +1 -0
- package/dist/adapters/pi/piContext.d.cts +20 -0
- package/dist/adapters/pi/piContext.d.cts.map +1 -0
- package/dist/adapters/pi/piContext.d.mts +20 -0
- package/dist/adapters/pi/piContext.d.mts.map +1 -0
- package/dist/adapters/pi/piContext.mjs +2 -0
- package/dist/adapters/pi/piContext.mjs.map +1 -0
- package/dist/adapters/piConfig.cjs +2 -0
- package/dist/adapters/piConfig.cjs.map +1 -0
- package/dist/adapters/piConfig.d.cts +8 -0
- package/dist/adapters/piConfig.d.cts.map +1 -0
- package/dist/adapters/piConfig.d.mts +8 -0
- package/dist/adapters/piConfig.d.mts.map +1 -0
- package/dist/adapters/piConfig.mjs +2 -0
- package/dist/adapters/piConfig.mjs.map +1 -0
- package/dist/atomicJson.cjs +1 -2
- package/dist/atomicJson.d.cts +2 -6
- package/dist/atomicJson.d.mts +2 -6
- package/dist/atomicJson.mjs +1 -2
- package/dist/config/piConfig.cjs +1 -2
- package/dist/config/piConfig.d.cts +2 -7
- package/dist/config/piConfig.d.mts +2 -7
- package/dist/config/piConfig.mjs +1 -2
- package/dist/config/schema.d.cts +2 -33
- package/dist/config/schema.d.mts +2 -33
- package/dist/config.cjs +1 -2
- package/dist/config.d.cts +2 -24
- package/dist/config.d.mts +2 -24
- package/dist/config.mjs +1 -2
- package/dist/configWriter.cjs +1 -2
- package/dist/configWriter.d.cts +2 -52
- package/dist/configWriter.d.mts +2 -52
- package/dist/configWriter.mjs +1 -2
- package/dist/container/index.cjs +2 -0
- package/dist/container/index.cjs.map +1 -0
- package/dist/container/index.d.cts +12 -0
- package/dist/container/index.d.cts.map +1 -0
- package/dist/container/index.d.mts +12 -0
- package/dist/container/index.d.mts.map +1 -0
- package/dist/container/index.mjs +2 -0
- package/dist/container/index.mjs.map +1 -0
- package/dist/container.cjs +1 -2
- package/dist/container.d.cts +2 -12
- package/dist/container.d.mts +2 -12
- package/dist/container.mjs +1 -2
- package/dist/domains.cjs +1 -2
- package/dist/domains.d.cts +3 -75
- package/dist/domains.d.mts +3 -75
- package/dist/domains.mjs +1 -2
- package/dist/extensions/doom.cjs +1 -1
- package/dist/extensions/doom.cjs.map +1 -1
- package/dist/extensions/doom.d.cts +1 -1
- package/dist/extensions/doom.d.mts +1 -1
- package/dist/extensions/doom.mjs +1 -1
- package/dist/extensions/doom.mjs.map +1 -1
- package/dist/extensions/pi.cjs +1 -1
- package/dist/extensions/pi.cjs.map +1 -1
- package/dist/extensions/pi.d.cts +1 -1
- package/dist/extensions/pi.d.mts +1 -1
- package/dist/extensions/pi.mjs +1 -1
- package/dist/extensions/pi.mjs.map +1 -1
- package/dist/harnessState.cjs +1 -2
- package/dist/harnessState.d.cts +2 -50
- package/dist/harnessState.d.mts +2 -50
- package/dist/harnessState.mjs +1 -2
- package/dist/harnessStore.cjs +1 -2
- package/dist/harnessStore.d.cts +2 -102
- package/dist/harnessStore.d.mts +2 -102
- package/dist/harnessStore.mjs +1 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +16 -16
- package/dist/index.d.mts +16 -16
- package/dist/index.mjs +1 -1
- package/dist/init.cjs +1 -73
- package/dist/init.d.cts +2 -33
- package/dist/init.d.mts +2 -33
- package/dist/init.mjs +1 -73
- package/dist/layeredConfig.cjs +1 -2
- package/dist/layeredConfig.d.cts +2 -49
- package/dist/layeredConfig.d.mts +2 -49
- package/dist/layeredConfig.mjs +1 -2
- package/dist/majorModes.cjs +1 -2
- package/dist/majorModes.d.cts +2 -73
- package/dist/majorModes.d.mts +2 -73
- package/dist/majorModes.mjs +1 -2
- package/dist/pi.cjs +1 -1
- package/dist/pi.d.cts +1 -1
- package/dist/pi.d.mts +1 -1
- package/dist/pi.mjs +1 -1
- package/dist/piContext.cjs +1 -2
- package/dist/piContext.d.cts +2 -19
- package/dist/piContext.d.mts +2 -19
- package/dist/piContext.mjs +1 -2
- package/dist/profiles.cjs +1 -4
- package/dist/profiles.d.cts +2 -35
- package/dist/profiles.d.mts +2 -35
- package/dist/profiles.mjs +1 -4
- package/dist/schemas/config/piConfig.cjs +1 -0
- package/dist/schemas/config/piConfig.mjs +1 -0
- package/dist/schemas/config/schema.d.cts +33 -0
- package/dist/schemas/config/schema.d.cts.map +1 -0
- package/dist/schemas/config/schema.d.mts +33 -0
- package/dist/schemas/config/schema.d.mts.map +1 -0
- package/dist/services/atomicJson.cjs +2 -0
- package/dist/services/atomicJson.cjs.map +1 -0
- package/dist/services/atomicJson.d.cts +6 -0
- package/dist/services/atomicJson.d.cts.map +1 -0
- package/dist/services/atomicJson.d.mts +6 -0
- package/dist/services/atomicJson.d.mts.map +1 -0
- package/dist/services/atomicJson.mjs +2 -0
- package/dist/services/atomicJson.mjs.map +1 -0
- package/dist/services/config.cjs +1 -0
- package/dist/services/config.mjs +1 -0
- package/dist/services/configWriter.cjs +2 -0
- package/dist/services/configWriter.cjs.map +1 -0
- package/dist/services/configWriter.d.cts +52 -0
- package/dist/services/configWriter.d.cts.map +1 -0
- package/dist/services/configWriter.d.mts +52 -0
- package/dist/services/configWriter.d.mts.map +1 -0
- package/dist/services/configWriter.mjs +2 -0
- package/dist/services/configWriter.mjs.map +1 -0
- package/dist/services/domains.cjs +2 -0
- package/dist/services/domains.cjs.map +1 -0
- package/dist/services/domains.d.cts +77 -0
- package/dist/services/domains.d.cts.map +1 -0
- package/dist/services/domains.d.mts +77 -0
- package/dist/services/domains.d.mts.map +1 -0
- package/dist/services/domains.mjs +2 -0
- package/dist/services/domains.mjs.map +1 -0
- package/dist/services/harnessState.cjs +2 -0
- package/dist/services/harnessState.cjs.map +1 -0
- package/dist/services/harnessState.d.cts +50 -0
- package/dist/services/harnessState.d.cts.map +1 -0
- package/dist/services/harnessState.d.mts +50 -0
- package/dist/services/harnessState.d.mts.map +1 -0
- package/dist/services/harnessState.mjs +2 -0
- package/dist/services/harnessState.mjs.map +1 -0
- package/dist/services/harnessStore.cjs +2 -0
- package/dist/services/harnessStore.cjs.map +1 -0
- package/dist/services/harnessStore.d.cts +102 -0
- package/dist/services/harnessStore.d.cts.map +1 -0
- package/dist/services/harnessStore.d.mts +102 -0
- package/dist/services/harnessStore.d.mts.map +1 -0
- package/dist/services/harnessStore.mjs +2 -0
- package/dist/services/harnessStore.mjs.map +1 -0
- package/dist/services/init.cjs +1 -0
- package/dist/services/init.mjs +1 -0
- package/dist/services/layeredConfig.cjs +2 -0
- package/dist/services/layeredConfig.cjs.map +1 -0
- package/dist/services/layeredConfig.d.cts +49 -0
- package/dist/services/layeredConfig.d.cts.map +1 -0
- package/dist/services/layeredConfig.d.mts +49 -0
- package/dist/services/layeredConfig.d.mts.map +1 -0
- package/dist/services/layeredConfig.mjs +2 -0
- package/dist/services/layeredConfig.mjs.map +1 -0
- package/dist/services/majorModes.cjs +2 -0
- package/dist/services/majorModes.cjs.map +1 -0
- package/dist/services/majorModes.d.cts +75 -0
- package/dist/services/majorModes.d.cts.map +1 -0
- package/dist/services/majorModes.d.mts +75 -0
- package/dist/services/majorModes.d.mts.map +1 -0
- package/dist/services/majorModes.mjs +2 -0
- package/dist/services/majorModes.mjs.map +1 -0
- package/dist/services/profiles.cjs +4 -0
- package/dist/services/profiles.cjs.map +1 -0
- package/dist/services/profiles.d.cts +35 -0
- package/dist/services/profiles.d.cts.map +1 -0
- package/dist/services/profiles.d.mts +35 -0
- package/dist/services/profiles.d.mts.map +1 -0
- package/dist/services/profiles.mjs +4 -0
- package/dist/services/profiles.mjs.map +1 -0
- package/dist/types/index.cjs +2 -0
- package/dist/types/index.cjs.map +1 -0
- package/dist/types/index.d.cts +136 -0
- package/dist/types/index.d.cts.map +1 -0
- package/dist/types/index.d.mts +136 -0
- package/dist/types/index.d.mts.map +1 -0
- package/dist/types/index.mjs +2 -0
- package/dist/types/index.mjs.map +1 -0
- package/dist/types.cjs +1 -2
- package/dist/types.d.cts +2 -132
- package/dist/types.d.mts +2 -132
- package/dist/types.mjs +1 -2
- package/package.json +2 -2
- package/skills/doom-pi-config/references/config-contract.md +1 -1
- package/dist/atomicJson.cjs.map +0 -1
- package/dist/atomicJson.d.cts.map +0 -1
- package/dist/atomicJson.d.mts.map +0 -1
- package/dist/atomicJson.mjs.map +0 -1
- package/dist/config/piConfig.cjs.map +0 -1
- package/dist/config/piConfig.d.cts.map +0 -1
- package/dist/config/piConfig.d.mts.map +0 -1
- package/dist/config/piConfig.mjs.map +0 -1
- package/dist/config/schema.d.cts.map +0 -1
- package/dist/config/schema.d.mts.map +0 -1
- package/dist/config.cjs.map +0 -1
- package/dist/config.d.cts.map +0 -1
- package/dist/config.d.mts.map +0 -1
- package/dist/config.mjs.map +0 -1
- package/dist/configWriter.cjs.map +0 -1
- package/dist/configWriter.d.cts.map +0 -1
- package/dist/configWriter.d.mts.map +0 -1
- package/dist/configWriter.mjs.map +0 -1
- package/dist/container.cjs.map +0 -1
- package/dist/container.d.cts.map +0 -1
- package/dist/container.d.mts.map +0 -1
- package/dist/container.mjs.map +0 -1
- package/dist/domains.cjs.map +0 -1
- package/dist/domains.d.cts.map +0 -1
- package/dist/domains.d.mts.map +0 -1
- package/dist/domains.mjs.map +0 -1
- package/dist/harnessState.cjs.map +0 -1
- package/dist/harnessState.d.cts.map +0 -1
- package/dist/harnessState.d.mts.map +0 -1
- package/dist/harnessState.mjs.map +0 -1
- package/dist/harnessStore.cjs.map +0 -1
- package/dist/harnessStore.d.cts.map +0 -1
- package/dist/harnessStore.d.mts.map +0 -1
- package/dist/harnessStore.mjs.map +0 -1
- package/dist/init.cjs.map +0 -1
- package/dist/init.d.cts.map +0 -1
- package/dist/init.d.mts.map +0 -1
- package/dist/init.mjs.map +0 -1
- package/dist/layeredConfig.cjs.map +0 -1
- package/dist/layeredConfig.d.cts.map +0 -1
- package/dist/layeredConfig.d.mts.map +0 -1
- package/dist/layeredConfig.mjs.map +0 -1
- package/dist/majorModes.cjs.map +0 -1
- package/dist/majorModes.d.cts.map +0 -1
- package/dist/majorModes.d.mts.map +0 -1
- package/dist/majorModes.mjs.map +0 -1
- package/dist/piContext.cjs.map +0 -1
- package/dist/piContext.d.cts.map +0 -1
- package/dist/piContext.d.mts.map +0 -1
- package/dist/piContext.mjs.map +0 -1
- package/dist/profiles.cjs.map +0 -1
- package/dist/profiles.d.cts.map +0 -1
- package/dist/profiles.d.mts.map +0 -1
- package/dist/profiles.mjs.map +0 -1
- package/dist/types.cjs.map +0 -1
- package/dist/types.d.cts.map +0 -1
- package/dist/types.d.mts.map +0 -1
- package/dist/types.mjs.map +0 -1
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:fs");t=e.__toESM(t,1);let n=require("node:os");n=e.__toESM(n,1);let r=require("node:path");r=e.__toESM(r,1);let i=require("yaml");const a=`.doom`,o=`config.yaml`,s=`auto`,ee=[`modes`,`projectTrust`,`editor`,`voice`,`selection`],te=[`majorMode`,`domains`,`profile`],ne=[`planning`],c=[`main`,`subagents`,`plansDirectory`],re=[`model`,`thinking`],ie=[`command`],l=[`engine`,`language`,`recorder`,`adapters`,`autoCapture`],u=[`binary`,`device`],d=`whisper-cpp`,f=`openai-whisper`,p=`mlx-whisper`,ae=[d,f,p],oe=[`binary`,`model`],se=[`path`,`id`],m=[`model`,`startPhrases`,`stopPhrases`,`utteranceIdleMs`,`transcriptionTimeoutMs`,`tts`],h=[`engine`,`voice`,`rate`],g=1500,_=1e4,v=1e3,y=12e4,b=/^[^/\s]+\/[^/\s]+(?:\/[^/\s]+)*$/u,x=/[\p{P}\p{S}]+/gu,S=[`off`,`minimal`,`low`,`medium`,`high`,`xhigh`,`max`],C=[s,d,f,p],w=[`macos-say`],T=new Set(S),ce=new Set([`ask`,`always`,`never`]),E=new Set(C),D=new Set(w);function O(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function k(e,t,n,r){let i=Object.keys(e).filter(e=>!t.includes(e));if(i.length>0)throw Error(`Doom config at ${r} has unsupported ${n} field(s): ${i.join(`, `)}`)}function A(e,t,n){if(e!==void 0){if(typeof e!=`string`||e.trim().length===0)throw Error(`Doom config at ${n} requires ${t} to be a non-empty string`);return e.trim()}}function j(e){for(let t of e){let e=t.codePointAt(0);if(e!==void 0&&(e<=31||e===127))return!0}return!1}function M(e,t,n,r){let i=A(e,t,n);if(i){if(i.length>r)throw Error(`Doom config at ${n} requires ${t} to be at most ${r} characters`);if(j(i))throw Error(`Doom config at ${n} does not allow control characters in ${t}`);return i}}function N(e,t){let n=`voice.autoCapture.model`,r=A(e,n,t);if(!r||!b.test(r))throw Error(`Doom config at ${t} requires ${n} in provider/model-id form`);return r}function P(e){return e.normalize(`NFKC`).toLocaleLowerCase(`en-US`).replace(x,` `).replace(/\s+/gu,` `).trim()}function F(e,t,n){if(e===void 0)return;if(!Array.isArray(e)||e.some(e=>typeof e!=`string`))throw Error(`Doom config at ${n} requires ${t} to be an array of strings`);if(e.length>16)throw Error(`Doom config at ${n} requires ${t} to contain at most 16 phrases`);let r=e.map(e=>e.trim()),i=new Set;for(let e of r){if(e.length===0)throw Error(`Doom config at ${n} requires ${t} to contain non-empty strings`);if(e.length>64)throw Error(`Doom config at ${n} requires ${t} phrases to be at most 64 characters`);if(j(e))throw Error(`Doom config at ${n} does not allow control characters in ${t}`);let r=P(e);if(!r)throw Error(`Doom config at ${n} requires ${t} to contain words`);if(i.has(r))throw Error(`Doom config at ${n} does not allow normalized duplicates in ${t}`);i.add(r)}return r}function I(e,t){let n=`voice.autoCapture.tts`;if(!O(e))throw Error(`Doom config at ${t} requires ${n} to be an object`);if(k(e,h,n,t),typeof e.engine!=`string`||!D.has(e.engine))throw Error(`Doom config at ${t} requires ${n}.engine to be one of: ${[...D].join(`, `)}`);let r=M(e.voice,`${n}.voice`,t,64),i=e.rate;if(i!==void 0&&(typeof i!=`number`||!Number.isInteger(i)||i<80||i>500))throw Error(`Doom config at ${t} requires ${n}.rate to be an integer from 80 to 500`);return{engine:e.engine,...r?{voice:r}:{},...i===void 0?{}:{rate:i}}}function L(e,t){if(e===void 0)return;let n=`voice.autoCapture`;if(!O(e))throw Error(`Doom config at ${t} requires ${n} to be an object`);k(e,m,n,t);let r=F(e.startPhrases,`${n}.startPhrases`,t),i=F(e.stopPhrases,`${n}.stopPhrases`,t),a=e.utteranceIdleMs;if(a!==void 0&&(typeof a!=`number`||!Number.isInteger(a)||a<g||a>_))throw Error(`Doom config at ${t} requires ${n}.utteranceIdleMs to be an integer from ${g} to ${_}`);let o=e.transcriptionTimeoutMs;if(o!==void 0&&(typeof o!=`number`||!Number.isInteger(o)||o<v||o>y))throw Error(`Doom config at ${t} requires ${n}.transcriptionTimeoutMs to be an integer from ${v} to ${y}`);let s=new Set((r??[]).map(P));if((i??[]).some(e=>s.has(P(e))))throw Error(`Doom config at ${t} does not allow a phrase in both startPhrases and stopPhrases`);return{model:N(e.model,t),...r?{startPhrases:r}:{},...i?{stopPhrases:i}:{},...a===void 0?{}:{utteranceIdleMs:a},...o===void 0?{}:{transcriptionTimeoutMs:o},tts:I(e.tts,t)}}function R(e,t,n){if(e===void 0)return;if(!O(e))throw Error(`Doom config at ${n} requires ${t} to be an object`);k(e,re,t,n);let r=e.thinking;if(r!==void 0&&(typeof r!=`string`||!T.has(r)))throw Error(`Doom config at ${n} requires ${t}.thinking to be one of: ${[...T].join(`, `)}`);let i=A(e.model,`${t}.model`,n);return{...i?{model:i}:{},...r?{thinking:r}:{}}}function z(e,t,n=`modes.planning`){if(!O(e))throw Error(`Doom config at ${t} requires ${n} to be an object`);return k(e,c,n,t),{main:R(e.main,`${n}.main`,t),subagents:R(e.subagents,`${n}.subagents`,t),plansDirectory:A(e.plansDirectory,`${n}.plansDirectory`,t)}}function B(e,t,n,r){if(!O(e))throw Error(`Doom config at ${n} requires ${t} to be an object`);k(e,se,t,n);let i=A(e.path,`${t}.path`,n),a=A(e.id,`${t}.id`,n);if(!!i==!!a)throw Error(`Doom config at ${n} requires exactly one of ${t}.path or ${t}.id`);if(a&&!r)throw Error(`Doom config at ${n} does not support ${t}.id for whisper-cpp`);return{...i?{path:i}:{},...a?{id:a}:{}}}function V(e,t,n,r){if(e===void 0)return;if(!O(e))throw Error(`Doom config at ${n} requires ${t} to be an object`);k(e,oe,t,n);let i=A(e.binary,`${t}.binary`,n);return{...i?{binary:i}:{},model:B(e.model,`${t}.model`,n,r)}}function H(e,t){if(e===void 0)return;if(!O(e))throw Error(`Doom config at ${t} requires voice to be an object`);k(e,l,`voice`,t);let n={};if(e.engine!==void 0){if(typeof e.engine!=`string`||!E.has(e.engine))throw Error(`Doom config at ${t} requires voice.engine to be one of: ${[...E].join(`, `)}`);n.engine=e.engine}let r=A(e.language,`voice.language`,t);if(r&&(n.language=r),e.recorder!==void 0){if(!O(e.recorder))throw Error(`Doom config at ${t} requires voice.recorder to be an object`);k(e.recorder,u,`voice.recorder`,t),n.recorder={};let r=A(e.recorder.binary,`voice.recorder.binary`,t),i=A(e.recorder.device,`voice.recorder.device`,t);r&&(n.recorder.binary=r),i&&(n.recorder.device=i)}if(e.adapters!==void 0){if(!O(e.adapters))throw Error(`Doom config at ${t} requires voice.adapters to be an object`);k(e.adapters,ae,`voice.adapters`,t),n.adapters={[d]:V(e.adapters[d],`voice.adapters.${d}`,t,!1),[f]:V(e.adapters[f],`voice.adapters.${f}`,t,!0),[p]:V(e.adapters[p],`voice.adapters.${p}`,t,!0)}}let i=L(e.autoCapture,t);return i&&(n.autoCapture=i),n}function U(e,t){if(e===void 0)return;if(!O(e))throw Error(`Doom config at ${t} requires selection to be an object`);k(e,te,`selection`,t);let n=e.domains;if(n!==void 0&&(!Array.isArray(n)||n.some(e=>typeof e!=`string`)))throw Error(`Doom config at ${t} requires selection.domains to be an array of strings`);return{majorMode:A(e.majorMode,`selection.majorMode`,t),domains:n,profile:A(e.profile,`selection.profile`,t)}}function W(e,t){if(e!==void 0){if(!O(e))throw Error(`Doom config at ${t} requires editor to be an object`);return k(e,ie,`editor`,t),{command:A(e.command,`editor.command`,t)}}}function G(e,t){let n;try{n=(0,i.parse)(e)??{}}catch(e){throw Error(`Could not parse Doom config at ${t}`,{cause:e})}if(!O(n))throw Error(`Doom config at ${t} must be a YAML object`);k(n,ee,`root`,t);let r;if(n.modes!==void 0){if(!O(n.modes))throw Error(`Doom config at ${t} requires modes to be an object`);k(n.modes,ne,`modes`,t),r=n.modes.planning===void 0?{}:{planning:z(n.modes.planning,t)}}let a=n.projectTrust===void 0?`ask`:n.projectTrust;if(typeof a!=`string`||!ce.has(a))throw Error(`Doom config at ${t} projectTrust must be ask, always, or never`);return{modes:r,projectTrust:a,editor:W(n.editor,t),voice:H(n.voice,t),selection:U(n.selection,t)}}function K(e){return t.default.existsSync(e)?G(t.default.readFileSync(e,`utf8`),e):{projectTrust:`ask`}}async function q(e){try{return G(await t.default.promises.readFile(e,`utf8`),e)}catch(e){if(e.code===`ENOENT`)return{projectTrust:`ask`};throw e}}function J(e,t){return e||t?{...e,...t}:void 0}function Y(e,t){if(t.voice?.autoCapture)throw Error(`Repository Doom config voice.autoCapture is global-only; configure it in ~/.pi/.doom/config.yaml`);let n=e.modes?.planning,r=t.modes?.planning,i=n||r?{main:J(n?.main,r?.main),subagents:J(n?.subagents,r?.subagents),plansDirectory:r?.plansDirectory??n?.plansDirectory}:void 0,a=le(e.voice,t.voice);return{modes:i?{planning:i}:void 0,projectTrust:t.projectTrust,editor:e.editor,voice:a,selection:e.selection||t.selection?{...e.selection,...t.selection}:void 0}}function X(e,t){if(!(!e&&!t))return{...e,...t,model:t?.model??e.model}}function le(e,t){if(!(!e&&!t))return{...e,...t,recorder:e?.recorder||t?.recorder?{...e?.recorder,...t?.recorder}:void 0,adapters:e?.adapters||t?.adapters?{[d]:X(e?.adapters?.[d],t?.adapters?.[d]),[f]:X(e?.adapters?.[f],t?.adapters?.[f]),[p]:X(e?.adapters?.[p],t?.adapters?.[p])}:void 0,...e?.autoCapture?{autoCapture:e.autoCapture}:{}}}function ue(e){return{engine:e.engine??s,language:e.language??`auto`,recorder:{...e.recorder,device:e.recorder?.device??`none:default`},adapters:e.adapters??{},...e.autoCapture?{autoCapture:{...e.autoCapture,startPhrases:[...e.autoCapture.startPhrases??[]],stopPhrases:[...e.autoCapture.stopPhrases??[]],utteranceIdleMs:e.autoCapture.utteranceIdleMs??3e3,transcriptionTimeoutMs:e.autoCapture.transcriptionTimeoutMs??15e3,tts:{...e.autoCapture.tts}}}:{}}}function Z(e=n.default.homedir()){return r.default.join(e,`.pi`,a)}function Q(e=n.default.homedir()){return r.default.join(Z(e),o)}function de(e,t,i=n.default.homedir()){if(!e)return r.default.join(i,`.pi`,`plans`);if(e===`~`)return i;if(e.startsWith(`~/`))return r.default.resolve(i,e.slice(2));if(e.startsWith(`~`))throw Error(`Doom planning plansDirectory supports only '~' or '~/' home aliases`);return r.default.isAbsolute(e)?r.default.normalize(e):r.default.resolve(t,e)}function $(e){return r.default.join(e,a,o)}function fe(e,t=n.default.homedir()){return Y(K(Q(t)),K($(e)))}async function pe(e,t=n.default.homedir()){return Y(await q(Q(t)),await q($(e)))}exports.DOOM_PLANNING_THINKING_LEVELS=S,exports.DOOM_VOICE_ENGINES=C,exports.DOOM_VOICE_TTS_ENGINES=w,exports.globalDoomConfigDirectory=Z,exports.globalDoomConfigPath=Q,exports.loadDoomConfig=fe,exports.loadDoomConfigAsync=pe,exports.mergeDoomConfigs=Y,exports.parseDoomConfig=G,exports.parsePlanningModeConfig=z,exports.repositoryDoomConfigPath=$,exports.resolvePlanningPlansDirectory=de,exports.resolveVoiceConfig=ue;
|
|
2
|
+
//# sourceMappingURL=config.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.cjs","names":["parseYaml","fs","os","path"],"sources":["../../src/adapters/config.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport type {\n DoomConfig,\n DoomSelectionConfig,\n EditorConfig,\n PlanningAgentConfig,\n PlanningModeConfig,\n PlanningThinkingLevel,\n ProjectTrust,\n ResolvedVoiceConfig,\n VoiceAdapterConfig,\n VoiceAutoCaptureConfig,\n VoiceConfig,\n VoiceEngine,\n VoiceModelConfig,\n VoiceTtsConfig,\n VoiceTtsEngine,\n} from '../types/index.ts';\n\nconst DOOM_DIR = '.doom';\nconst CONFIG_FILE = 'config.yaml';\nconst GLOBAL_CONFIG_DIR = '.pi';\nconst PLANS_DIR = 'plans';\nconst DEFAULT_PROJECT_TRUST: ProjectTrust = 'ask';\nconst DEFAULT_VOICE_ENGINE: VoiceEngine = 'auto';\nconst DEFAULT_VOICE_LANGUAGE = 'auto';\nconst DEFAULT_RECORDER_DEVICE = 'none:default';\nconst ROOT_KEYS = ['modes', 'projectTrust', 'editor', 'voice', 'selection'] as const;\nconst SELECTION_KEYS = ['majorMode', 'domains', 'profile'] as const;\nconst MODE_KEYS = ['planning'] as const;\nconst PLANNING_KEYS = ['main', 'subagents', 'plansDirectory'] as const;\nconst AGENT_KEYS = ['model', 'thinking'] as const;\nconst EDITOR_KEYS = ['command'] as const;\nconst VOICE_KEYS = ['engine', 'language', 'recorder', 'adapters', 'autoCapture'] as const;\nconst RECORDER_KEYS = ['binary', 'device'] as const;\nconst WHISPER_CPP_ENGINE = 'whisper-cpp' as const;\nconst OPENAI_WHISPER_ENGINE = 'openai-whisper' as const;\nconst MLX_WHISPER_ENGINE = 'mlx-whisper' as const;\nconst MACOS_SAY_TTS_ENGINE = 'macos-say' as const;\nconst ADAPTERS_KEYS = [WHISPER_CPP_ENGINE, OPENAI_WHISPER_ENGINE, MLX_WHISPER_ENGINE] as const;\nconst ADAPTER_KEYS = ['binary', 'model'] as const;\nconst MODEL_KEYS = ['path', 'id'] as const;\nconst AUTO_CAPTURE_KEYS = [\n 'model',\n 'startPhrases',\n 'stopPhrases',\n 'utteranceIdleMs',\n 'transcriptionTimeoutMs',\n 'tts',\n] as const;\nconst TTS_KEYS = ['engine', 'voice', 'rate'] as const;\nconst DEFAULT_UTTERANCE_IDLE_MS = 3_000;\nconst MIN_UTTERANCE_IDLE_MS = 1_500;\nconst MAX_UTTERANCE_IDLE_MS = 10_000;\nconst DEFAULT_TRANSCRIPTION_TIMEOUT_MS = 15_000;\nconst MIN_TRANSCRIPTION_TIMEOUT_MS = 1_000;\nconst MAX_TRANSCRIPTION_TIMEOUT_MS = 120_000;\nconst MAX_CONTROL_PHRASES = 16;\nconst MAX_CONTROL_PHRASE_LENGTH = 64;\nconst MAX_TTS_VOICE_LENGTH = 64;\nconst MIN_TTS_RATE = 80;\nconst MAX_TTS_RATE = 500;\nconst MODEL_REFERENCE_PATTERN = /^[^/\\s]+\\/[^/\\s]+(?:\\/[^/\\s]+)*$/u;\nconst PHRASE_PUNCTUATION_PATTERN = /[\\p{P}\\p{S}]+/gu;\n/**\n * The accepted values, in the order a picker should offer them.\n *\n * Exported because the config UI has to offer exactly what the parser accepts,\n * and a second hand-written list in the contributing package would drift. The\n * validating Sets below are built from these for the same reason.\n */\nexport const DOOM_PLANNING_THINKING_LEVELS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'] as const;\nexport const DOOM_VOICE_ENGINES = [\n DEFAULT_VOICE_ENGINE,\n WHISPER_CPP_ENGINE,\n OPENAI_WHISPER_ENGINE,\n MLX_WHISPER_ENGINE,\n] as const;\nexport const DOOM_VOICE_TTS_ENGINES = [MACOS_SAY_TTS_ENGINE] as const;\n\nconst THINKING_VALUES = new Set<PlanningThinkingLevel>(DOOM_PLANNING_THINKING_LEVELS);\nconst TRUST_VALUES = new Set<ProjectTrust>(['ask', 'always', 'never']);\nconst ENGINE_VALUES = new Set<VoiceEngine>(DOOM_VOICE_ENGINES);\nconst TTS_ENGINE_VALUES = new Set<VoiceTtsEngine>(DOOM_VOICE_TTS_ENGINES);\ntype ConfigObject = Record<string, unknown>;\n\nfunction isObject(value: unknown): value is ConfigObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\nfunction assertKeys(value: ConfigObject, allowed: readonly string[], location: string, filePath: string): void {\n const unknown = Object.keys(value).filter((key) => !allowed.includes(key));\n if (unknown.length > 0)\n throw new Error(`Doom config at ${filePath} has unsupported ${location} field(s): ${unknown.join(', ')}`);\n}\nfunction optionalString(value: unknown, location: string, filePath: string): string | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== 'string' || value.trim().length === 0)\n throw new Error(`Doom config at ${filePath} requires ${location} to be a non-empty string`);\n return value.trim();\n}\nfunction hasControlCharacters(value: string): boolean {\n for (const character of value) {\n const codePoint = character.codePointAt(0);\n if (codePoint !== undefined && (codePoint <= 0x1f || codePoint === 0x7f)) return true;\n }\n return false;\n}\nfunction boundedOptionalString(\n value: unknown,\n location: string,\n filePath: string,\n maximumLength: number,\n): string | undefined {\n const text = optionalString(value, location, filePath);\n if (!text) return undefined;\n if (text.length > maximumLength)\n throw new Error(`Doom config at ${filePath} requires ${location} to be at most ${maximumLength} characters`);\n if (hasControlCharacters(text))\n throw new Error(`Doom config at ${filePath} does not allow control characters in ${location}`);\n return text;\n}\nfunction parseModelReference(value: unknown, filePath: string): string {\n const location = 'voice.autoCapture.model';\n const model = optionalString(value, location, filePath);\n if (!model || !MODEL_REFERENCE_PATTERN.test(model))\n throw new Error(`Doom config at ${filePath} requires ${location} in provider/model-id form`);\n return model;\n}\nfunction normalizeControlPhrase(value: string): string {\n return value\n .normalize('NFKC')\n .toLocaleLowerCase('en-US')\n .replace(PHRASE_PUNCTUATION_PATTERN, ' ')\n .replace(/\\s+/gu, ' ')\n .trim();\n}\nfunction parseControlPhrases(value: unknown, location: string, filePath: string): string[] | undefined {\n if (value === undefined) return undefined;\n if (!Array.isArray(value) || value.some((phrase) => typeof phrase !== 'string'))\n throw new Error(`Doom config at ${filePath} requires ${location} to be an array of strings`);\n if (value.length > MAX_CONTROL_PHRASES)\n throw new Error(\n `Doom config at ${filePath} requires ${location} to contain at most ${MAX_CONTROL_PHRASES} phrases`,\n );\n const phrases = value.map((phrase) => phrase.trim());\n const normalized = new Set<string>();\n for (const phrase of phrases) {\n if (phrase.length === 0)\n throw new Error(`Doom config at ${filePath} requires ${location} to contain non-empty strings`);\n if (phrase.length > MAX_CONTROL_PHRASE_LENGTH)\n throw new Error(\n `Doom config at ${filePath} requires ${location} phrases to be at most ${MAX_CONTROL_PHRASE_LENGTH} characters`,\n );\n if (hasControlCharacters(phrase))\n throw new Error(`Doom config at ${filePath} does not allow control characters in ${location}`);\n const key = normalizeControlPhrase(phrase);\n if (!key) throw new Error(`Doom config at ${filePath} requires ${location} to contain words`);\n if (normalized.has(key))\n throw new Error(`Doom config at ${filePath} does not allow normalized duplicates in ${location}`);\n normalized.add(key);\n }\n return phrases;\n}\nfunction parseTtsConfig(value: unknown, filePath: string): VoiceTtsConfig {\n const location = 'voice.autoCapture.tts';\n if (!isObject(value)) throw new Error(`Doom config at ${filePath} requires ${location} to be an object`);\n assertKeys(value, TTS_KEYS, location, filePath);\n if (typeof value.engine !== 'string' || !TTS_ENGINE_VALUES.has(value.engine as VoiceTtsEngine))\n throw new Error(\n `Doom config at ${filePath} requires ${location}.engine to be one of: ${[...TTS_ENGINE_VALUES].join(', ')}`,\n );\n const voice = boundedOptionalString(value.voice, `${location}.voice`, filePath, MAX_TTS_VOICE_LENGTH);\n const rate = value.rate;\n if (\n rate !== undefined &&\n (typeof rate !== 'number' || !Number.isInteger(rate) || rate < MIN_TTS_RATE || rate > MAX_TTS_RATE)\n ) {\n throw new Error(\n `Doom config at ${filePath} requires ${location}.rate to be an integer from ${MIN_TTS_RATE} to ${MAX_TTS_RATE}`,\n );\n }\n return {\n engine: value.engine as VoiceTtsEngine,\n ...(voice ? { voice } : {}),\n ...(rate === undefined ? {} : { rate }),\n };\n}\nfunction parseAutoCapture(value: unknown, filePath: string): VoiceAutoCaptureConfig | undefined {\n if (value === undefined) return undefined;\n const location = 'voice.autoCapture';\n if (!isObject(value)) throw new Error(`Doom config at ${filePath} requires ${location} to be an object`);\n assertKeys(value, AUTO_CAPTURE_KEYS, location, filePath);\n const startPhrases = parseControlPhrases(value.startPhrases, `${location}.startPhrases`, filePath);\n const stopPhrases = parseControlPhrases(value.stopPhrases, `${location}.stopPhrases`, filePath);\n const utteranceIdleMs = value.utteranceIdleMs;\n if (\n utteranceIdleMs !== undefined &&\n (typeof utteranceIdleMs !== 'number' ||\n !Number.isInteger(utteranceIdleMs) ||\n utteranceIdleMs < MIN_UTTERANCE_IDLE_MS ||\n utteranceIdleMs > MAX_UTTERANCE_IDLE_MS)\n ) {\n throw new Error(\n `Doom config at ${filePath} requires ${location}.utteranceIdleMs to be an integer from ${MIN_UTTERANCE_IDLE_MS} to ${MAX_UTTERANCE_IDLE_MS}`,\n );\n }\n const transcriptionTimeoutMs = value.transcriptionTimeoutMs;\n if (\n transcriptionTimeoutMs !== undefined &&\n (typeof transcriptionTimeoutMs !== 'number' ||\n !Number.isInteger(transcriptionTimeoutMs) ||\n transcriptionTimeoutMs < MIN_TRANSCRIPTION_TIMEOUT_MS ||\n transcriptionTimeoutMs > MAX_TRANSCRIPTION_TIMEOUT_MS)\n ) {\n throw new Error(\n `Doom config at ${filePath} requires ${location}.transcriptionTimeoutMs to be an integer from ${MIN_TRANSCRIPTION_TIMEOUT_MS} to ${MAX_TRANSCRIPTION_TIMEOUT_MS}`,\n );\n }\n const startKeys = new Set((startPhrases ?? []).map(normalizeControlPhrase));\n if ((stopPhrases ?? []).some((phrase) => startKeys.has(normalizeControlPhrase(phrase))))\n throw new Error(`Doom config at ${filePath} does not allow a phrase in both startPhrases and stopPhrases`);\n return {\n model: parseModelReference(value.model, filePath),\n ...(startPhrases ? { startPhrases } : {}),\n ...(stopPhrases ? { stopPhrases } : {}),\n ...(utteranceIdleMs === undefined ? {} : { utteranceIdleMs }),\n ...(transcriptionTimeoutMs === undefined ? {} : { transcriptionTimeoutMs }),\n tts: parseTtsConfig(value.tts, filePath),\n };\n}\nfunction parseAgent(value: unknown, location: string, filePath: string): PlanningAgentConfig | undefined {\n if (value === undefined) return undefined;\n if (!isObject(value)) throw new Error(`Doom config at ${filePath} requires ${location} to be an object`);\n assertKeys(value, AGENT_KEYS, location, filePath);\n const thinking = value.thinking;\n if (\n thinking !== undefined &&\n (typeof thinking !== 'string' || !THINKING_VALUES.has(thinking as PlanningThinkingLevel))\n ) {\n throw new Error(\n `Doom config at ${filePath} requires ${location}.thinking to be one of: ${[...THINKING_VALUES].join(', ')}`,\n );\n }\n const model = optionalString(value.model, `${location}.model`, filePath);\n return { ...(model ? { model } : {}), ...(thinking ? { thinking: thinking as PlanningThinkingLevel } : {}) };\n}\nexport function parsePlanningModeConfig(\n value: unknown,\n filePath: string,\n location = 'modes.planning',\n): PlanningModeConfig {\n if (!isObject(value)) throw new Error(`Doom config at ${filePath} requires ${location} to be an object`);\n assertKeys(value, PLANNING_KEYS, location, filePath);\n return {\n main: parseAgent(value.main, `${location}.main`, filePath),\n subagents: parseAgent(value.subagents, `${location}.subagents`, filePath),\n plansDirectory: optionalString(value.plansDirectory, `${location}.plansDirectory`, filePath),\n };\n}\nfunction parseModel(value: unknown, location: string, filePath: string, allowId: boolean): VoiceModelConfig {\n if (!isObject(value)) throw new Error(`Doom config at ${filePath} requires ${location} to be an object`);\n assertKeys(value, MODEL_KEYS, location, filePath);\n const modelPath = optionalString(value.path, `${location}.path`, filePath);\n const id = optionalString(value.id, `${location}.id`, filePath);\n if (Boolean(modelPath) === Boolean(id))\n throw new Error(`Doom config at ${filePath} requires exactly one of ${location}.path or ${location}.id`);\n if (id && !allowId) throw new Error(`Doom config at ${filePath} does not support ${location}.id for whisper-cpp`);\n return { ...(modelPath ? { path: modelPath } : {}), ...(id ? { id } : {}) };\n}\nfunction parseAdapter(\n value: unknown,\n location: string,\n filePath: string,\n allowId: boolean,\n): VoiceAdapterConfig | undefined {\n if (value === undefined) return undefined;\n if (!isObject(value)) throw new Error(`Doom config at ${filePath} requires ${location} to be an object`);\n assertKeys(value, ADAPTER_KEYS, location, filePath);\n const binary = optionalString(value.binary, `${location}.binary`, filePath);\n return { ...(binary ? { binary } : {}), model: parseModel(value.model, `${location}.model`, filePath, allowId) };\n}\nfunction parseVoice(value: unknown, filePath: string): VoiceConfig | undefined {\n if (value === undefined) return undefined;\n if (!isObject(value)) throw new Error(`Doom config at ${filePath} requires voice to be an object`);\n assertKeys(value, VOICE_KEYS, 'voice', filePath);\n const result: VoiceConfig = {};\n if (value.engine !== undefined) {\n if (typeof value.engine !== 'string' || !ENGINE_VALUES.has(value.engine as VoiceEngine))\n throw new Error(\n `Doom config at ${filePath} requires voice.engine to be one of: ${[...ENGINE_VALUES].join(', ')}`,\n );\n result.engine = value.engine as VoiceEngine;\n }\n const language = optionalString(value.language, 'voice.language', filePath);\n if (language) result.language = language;\n if (value.recorder !== undefined) {\n if (!isObject(value.recorder))\n throw new Error(`Doom config at ${filePath} requires voice.recorder to be an object`);\n assertKeys(value.recorder, RECORDER_KEYS, 'voice.recorder', filePath);\n result.recorder = {};\n const binary = optionalString(value.recorder.binary, 'voice.recorder.binary', filePath);\n const device = optionalString(value.recorder.device, 'voice.recorder.device', filePath);\n if (binary) result.recorder.binary = binary;\n if (device) result.recorder.device = device;\n }\n if (value.adapters !== undefined) {\n if (!isObject(value.adapters))\n throw new Error(`Doom config at ${filePath} requires voice.adapters to be an object`);\n assertKeys(value.adapters, ADAPTERS_KEYS, 'voice.adapters', filePath);\n result.adapters = {\n [WHISPER_CPP_ENGINE]: parseAdapter(\n value.adapters[WHISPER_CPP_ENGINE],\n `voice.adapters.${WHISPER_CPP_ENGINE}`,\n filePath,\n false,\n ),\n [OPENAI_WHISPER_ENGINE]: parseAdapter(\n value.adapters[OPENAI_WHISPER_ENGINE],\n `voice.adapters.${OPENAI_WHISPER_ENGINE}`,\n filePath,\n true,\n ),\n [MLX_WHISPER_ENGINE]: parseAdapter(\n value.adapters[MLX_WHISPER_ENGINE],\n `voice.adapters.${MLX_WHISPER_ENGINE}`,\n filePath,\n true,\n ),\n };\n }\n const autoCapture = parseAutoCapture(value.autoCapture, filePath);\n if (autoCapture) result.autoCapture = autoCapture;\n return result;\n}\nfunction parseSelection(value: unknown, filePath: string): DoomSelectionConfig | undefined {\n if (value === undefined) return undefined;\n if (!isObject(value)) throw new Error(`Doom config at ${filePath} requires selection to be an object`);\n assertKeys(value, SELECTION_KEYS, 'selection', filePath);\n const domains = value.domains;\n if (domains !== undefined && (!Array.isArray(domains) || domains.some((name) => typeof name !== 'string'))) {\n throw new Error(`Doom config at ${filePath} requires selection.domains to be an array of strings`);\n }\n return {\n majorMode: optionalString(value.majorMode, 'selection.majorMode', filePath),\n // An empty list is meaningful: it selects no content domains at all.\n domains: domains as string[] | undefined,\n profile: optionalString(value.profile, 'selection.profile', filePath),\n };\n}\nfunction parseEditor(value: unknown, filePath: string): EditorConfig | undefined {\n if (value === undefined) return undefined;\n if (!isObject(value)) throw new Error(`Doom config at ${filePath} requires editor to be an object`);\n assertKeys(value, EDITOR_KEYS, 'editor', filePath);\n return { command: optionalString(value.command, 'editor.command', filePath) };\n}\nexport function parseDoomConfig(content: string, filePath: string): DoomConfig {\n let parsed: unknown;\n try {\n parsed = parseYaml(content) ?? {};\n } catch (error) {\n throw new Error(`Could not parse Doom config at ${filePath}`, { cause: error });\n }\n if (!isObject(parsed)) throw new Error(`Doom config at ${filePath} must be a YAML object`);\n assertKeys(parsed, ROOT_KEYS, 'root', filePath);\n let modes: DoomConfig['modes'];\n if (parsed.modes !== undefined) {\n if (!isObject(parsed.modes)) throw new Error(`Doom config at ${filePath} requires modes to be an object`);\n assertKeys(parsed.modes, MODE_KEYS, 'modes', filePath);\n modes =\n parsed.modes.planning === undefined ? {} : { planning: parsePlanningModeConfig(parsed.modes.planning, filePath) };\n }\n const trust = parsed.projectTrust === undefined ? DEFAULT_PROJECT_TRUST : parsed.projectTrust;\n if (typeof trust !== 'string' || !TRUST_VALUES.has(trust as ProjectTrust))\n throw new Error(`Doom config at ${filePath} projectTrust must be ask, always, or never`);\n return {\n modes,\n projectTrust: trust as ProjectTrust,\n editor: parseEditor(parsed.editor, filePath),\n voice: parseVoice(parsed.voice, filePath),\n selection: parseSelection(parsed.selection, filePath),\n };\n}\nfunction readConfig(filePath: string): DoomConfig {\n return fs.existsSync(filePath)\n ? parseDoomConfig(fs.readFileSync(filePath, 'utf8'), filePath)\n : { projectTrust: DEFAULT_PROJECT_TRUST };\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: DEFAULT_PROJECT_TRUST };\n throw error;\n }\n}\nfunction mergeAgent(\n globalValue?: PlanningAgentConfig,\n repositoryValue?: PlanningAgentConfig,\n): PlanningAgentConfig | undefined {\n return globalValue || repositoryValue ? { ...globalValue, ...repositoryValue } : undefined;\n}\nexport function mergeDoomConfigs(globalConfig: DoomConfig, repositoryConfig: DoomConfig): DoomConfig {\n if (repositoryConfig.voice?.autoCapture)\n throw new Error('Repository Doom config voice.autoCapture is global-only; configure it in ~/.pi/.doom/config.yaml');\n const globalPlanning = globalConfig.modes?.planning;\n const repositoryPlanning = repositoryConfig.modes?.planning;\n const planning =\n globalPlanning || repositoryPlanning\n ? {\n main: mergeAgent(globalPlanning?.main, repositoryPlanning?.main),\n subagents: mergeAgent(globalPlanning?.subagents, repositoryPlanning?.subagents),\n plansDirectory: repositoryPlanning?.plansDirectory ?? globalPlanning?.plansDirectory,\n }\n : undefined;\n const voice = mergeVoice(globalConfig.voice, repositoryConfig.voice);\n return {\n modes: planning ? { planning } : undefined,\n projectTrust: repositoryConfig.projectTrust,\n editor: globalConfig.editor,\n voice,\n // Per key, so a repository can pin one axis and inherit the rest.\n selection:\n globalConfig.selection || repositoryConfig.selection\n ? { ...globalConfig.selection, ...repositoryConfig.selection }\n : undefined,\n };\n}\nfunction mergeAdapter(\n globalValue?: VoiceAdapterConfig,\n repositoryValue?: VoiceAdapterConfig,\n): VoiceAdapterConfig | undefined {\n if (!globalValue && !repositoryValue) return undefined;\n return { ...globalValue, ...repositoryValue, model: repositoryValue?.model ?? globalValue!.model };\n}\nfunction mergeVoice(globalValue?: VoiceConfig, repositoryValue?: VoiceConfig): VoiceConfig | undefined {\n if (!globalValue && !repositoryValue) return undefined;\n return {\n ...globalValue,\n ...repositoryValue,\n recorder:\n globalValue?.recorder || repositoryValue?.recorder\n ? { ...globalValue?.recorder, ...repositoryValue?.recorder }\n : undefined,\n adapters:\n globalValue?.adapters || repositoryValue?.adapters\n ? {\n [WHISPER_CPP_ENGINE]: mergeAdapter(\n globalValue?.adapters?.[WHISPER_CPP_ENGINE],\n repositoryValue?.adapters?.[WHISPER_CPP_ENGINE],\n ),\n [OPENAI_WHISPER_ENGINE]: mergeAdapter(\n globalValue?.adapters?.[OPENAI_WHISPER_ENGINE],\n repositoryValue?.adapters?.[OPENAI_WHISPER_ENGINE],\n ),\n [MLX_WHISPER_ENGINE]: mergeAdapter(\n globalValue?.adapters?.[MLX_WHISPER_ENGINE],\n repositoryValue?.adapters?.[MLX_WHISPER_ENGINE],\n ),\n }\n : undefined,\n ...(globalValue?.autoCapture ? { autoCapture: globalValue.autoCapture } : {}),\n };\n}\nexport function resolveVoiceConfig(config: VoiceConfig): ResolvedVoiceConfig {\n return {\n engine: config.engine ?? DEFAULT_VOICE_ENGINE,\n language: config.language ?? DEFAULT_VOICE_LANGUAGE,\n recorder: { ...config.recorder, device: config.recorder?.device ?? DEFAULT_RECORDER_DEVICE },\n adapters: config.adapters ?? {},\n ...(config.autoCapture\n ? {\n autoCapture: {\n ...config.autoCapture,\n startPhrases: [...(config.autoCapture.startPhrases ?? [])],\n stopPhrases: [...(config.autoCapture.stopPhrases ?? [])],\n utteranceIdleMs: config.autoCapture.utteranceIdleMs ?? DEFAULT_UTTERANCE_IDLE_MS,\n transcriptionTimeoutMs: config.autoCapture.transcriptionTimeoutMs ?? DEFAULT_TRANSCRIPTION_TIMEOUT_MS,\n tts: { ...config.autoCapture.tts },\n },\n }\n : {}),\n };\n}\nexport function globalDoomConfigDirectory(homeDirectory = os.homedir()): string {\n return path.join(homeDirectory, GLOBAL_CONFIG_DIR, DOOM_DIR);\n}\nexport function globalDoomConfigPath(homeDirectory = os.homedir()): string {\n return path.join(globalDoomConfigDirectory(homeDirectory), CONFIG_FILE);\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}\nexport function repositoryDoomConfigPath(repoRoot: string): string {\n return path.join(repoRoot, DOOM_DIR, CONFIG_FILE);\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"],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { DoomConfig, PlanningModeConfig, ResolvedVoiceConfig, VoiceConfig } from "../types/index.cjs";
|
|
2
|
+
//#region src/adapters/config.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* The accepted values, in the order a picker should offer them.
|
|
5
|
+
*
|
|
6
|
+
* Exported because the config UI has to offer exactly what the parser accepts,
|
|
7
|
+
* and a second hand-written list in the contributing package would drift. The
|
|
8
|
+
* validating Sets below are built from these for the same reason.
|
|
9
|
+
*/
|
|
10
|
+
declare const DOOM_PLANNING_THINKING_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
11
|
+
declare const DOOM_VOICE_ENGINES: readonly ["auto", "whisper-cpp", "openai-whisper", "mlx-whisper"];
|
|
12
|
+
declare const DOOM_VOICE_TTS_ENGINES: readonly ["macos-say"];
|
|
13
|
+
declare function parsePlanningModeConfig(value: unknown, filePath: string, location?: string): PlanningModeConfig;
|
|
14
|
+
declare function parseDoomConfig(content: string, filePath: string): DoomConfig;
|
|
15
|
+
declare function mergeDoomConfigs(globalConfig: DoomConfig, repositoryConfig: DoomConfig): DoomConfig;
|
|
16
|
+
declare function resolveVoiceConfig(config: VoiceConfig): ResolvedVoiceConfig;
|
|
17
|
+
declare function globalDoomConfigDirectory(homeDirectory?: string): string;
|
|
18
|
+
declare function globalDoomConfigPath(homeDirectory?: string): string;
|
|
19
|
+
declare function resolvePlanningPlansDirectory(configured: string | undefined, repoRoot: string, homeDirectory?: string): string;
|
|
20
|
+
declare function repositoryDoomConfigPath(repoRoot: string): string;
|
|
21
|
+
declare function loadDoomConfig(repoRoot: string, homeDirectory?: string): DoomConfig;
|
|
22
|
+
declare function loadDoomConfigAsync(repoRoot: string, homeDirectory?: string): Promise<DoomConfig>;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { DOOM_PLANNING_THINKING_LEVELS, DOOM_VOICE_ENGINES, DOOM_VOICE_TTS_ENGINES, globalDoomConfigDirectory, globalDoomConfigPath, loadDoomConfig, loadDoomConfigAsync, mergeDoomConfigs, parseDoomConfig, parsePlanningModeConfig, repositoryDoomConfigPath, resolvePlanningPlansDirectory, resolveVoiceConfig };
|
|
25
|
+
//# sourceMappingURL=config.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.cts","names":[],"sources":["../../src/adapters/config.ts"],"mappings":";;;;;;;;;cA0Ea;cACA;cAMA;iBAwKG,wBACd,gBACA,kBACA,oBACC;iBAyGa,gBAAgB,iBAAiB,mBAAmB;iBA+CpD,iBAAiB,cAAc,YAAY,kBAAkB,aAAa;iBA8D1E,mBAAmB,QAAQ,cAAc;iBAoBzC,0BAA0B;iBAG1B,qBAAqB;iBAGrB,8BACd,gCACA,kBACA;iBAUc,yBAAyB;iBAGzB,eAAe,kBAAkB,yBAA+B;iBAO1D,oBAAoB,kBAAkB,yBAA+B,QAAQ"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { DoomConfig, PlanningModeConfig, ResolvedVoiceConfig, VoiceConfig } from "../types/index.mjs";
|
|
2
|
+
//#region src/adapters/config.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* The accepted values, in the order a picker should offer them.
|
|
5
|
+
*
|
|
6
|
+
* Exported because the config UI has to offer exactly what the parser accepts,
|
|
7
|
+
* and a second hand-written list in the contributing package would drift. The
|
|
8
|
+
* validating Sets below are built from these for the same reason.
|
|
9
|
+
*/
|
|
10
|
+
declare const DOOM_PLANNING_THINKING_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
11
|
+
declare const DOOM_VOICE_ENGINES: readonly ["auto", "whisper-cpp", "openai-whisper", "mlx-whisper"];
|
|
12
|
+
declare const DOOM_VOICE_TTS_ENGINES: readonly ["macos-say"];
|
|
13
|
+
declare function parsePlanningModeConfig(value: unknown, filePath: string, location?: string): PlanningModeConfig;
|
|
14
|
+
declare function parseDoomConfig(content: string, filePath: string): DoomConfig;
|
|
15
|
+
declare function mergeDoomConfigs(globalConfig: DoomConfig, repositoryConfig: DoomConfig): DoomConfig;
|
|
16
|
+
declare function resolveVoiceConfig(config: VoiceConfig): ResolvedVoiceConfig;
|
|
17
|
+
declare function globalDoomConfigDirectory(homeDirectory?: string): string;
|
|
18
|
+
declare function globalDoomConfigPath(homeDirectory?: string): string;
|
|
19
|
+
declare function resolvePlanningPlansDirectory(configured: string | undefined, repoRoot: string, homeDirectory?: string): string;
|
|
20
|
+
declare function repositoryDoomConfigPath(repoRoot: string): string;
|
|
21
|
+
declare function loadDoomConfig(repoRoot: string, homeDirectory?: string): DoomConfig;
|
|
22
|
+
declare function loadDoomConfigAsync(repoRoot: string, homeDirectory?: string): Promise<DoomConfig>;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { DOOM_PLANNING_THINKING_LEVELS, DOOM_VOICE_ENGINES, DOOM_VOICE_TTS_ENGINES, globalDoomConfigDirectory, globalDoomConfigPath, loadDoomConfig, loadDoomConfigAsync, mergeDoomConfigs, parseDoomConfig, parsePlanningModeConfig, repositoryDoomConfigPath, resolvePlanningPlansDirectory, resolveVoiceConfig };
|
|
25
|
+
//# sourceMappingURL=config.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.mts","names":[],"sources":["../../src/adapters/config.ts"],"mappings":";;;;;;;;;cA0Ea;cACA;cAMA;iBAwKG,wBACd,gBACA,kBACA,oBACC;iBAyGa,gBAAgB,iBAAiB,mBAAmB;iBA+CpD,iBAAiB,cAAc,YAAY,kBAAkB,aAAa;iBA8D1E,mBAAmB,QAAQ,cAAc;iBAoBzC,0BAA0B;iBAG1B,qBAAqB;iBAGrB,8BACd,gCACA,kBACA;iBAUc,yBAAyB;iBAGzB,eAAe,kBAAkB,yBAA+B;iBAO1D,oBAAoB,kBAAkB,yBAA+B,QAAQ"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import e from"node:fs";import t from"node:os";import n from"node:path";import{parse as r}from"yaml";const i=`.doom`,a=`config.yaml`,o=`auto`,s=[`modes`,`projectTrust`,`editor`,`voice`,`selection`],c=[`majorMode`,`domains`,`profile`],ee=[`planning`],l=[`main`,`subagents`,`plansDirectory`],te=[`model`,`thinking`],ne=[`command`],re=[`engine`,`language`,`recorder`,`adapters`,`autoCapture`],u=[`binary`,`device`],d=`whisper-cpp`,f=`openai-whisper`,p=`mlx-whisper`,ie=[d,f,p],ae=[`binary`,`model`],oe=[`path`,`id`],m=[`model`,`startPhrases`,`stopPhrases`,`utteranceIdleMs`,`transcriptionTimeoutMs`,`tts`],h=[`engine`,`voice`,`rate`],g=1500,_=1e4,v=1e3,y=12e4,b=/^[^/\s]+\/[^/\s]+(?:\/[^/\s]+)*$/u,x=/[\p{P}\p{S}]+/gu,S=[`off`,`minimal`,`low`,`medium`,`high`,`xhigh`,`max`],C=[o,d,f,p],w=[`macos-say`],T=new Set(S),E=new Set([`ask`,`always`,`never`]),D=new Set(C),O=new Set(w);function k(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function A(e,t,n,r){let i=Object.keys(e).filter(e=>!t.includes(e));if(i.length>0)throw Error(`Doom config at ${r} has unsupported ${n} field(s): ${i.join(`, `)}`)}function j(e,t,n){if(e!==void 0){if(typeof e!=`string`||e.trim().length===0)throw Error(`Doom config at ${n} requires ${t} to be a non-empty string`);return e.trim()}}function M(e){for(let t of e){let e=t.codePointAt(0);if(e!==void 0&&(e<=31||e===127))return!0}return!1}function se(e,t,n,r){let i=j(e,t,n);if(i){if(i.length>r)throw Error(`Doom config at ${n} requires ${t} to be at most ${r} characters`);if(M(i))throw Error(`Doom config at ${n} does not allow control characters in ${t}`);return i}}function N(e,t){let n=`voice.autoCapture.model`,r=j(e,n,t);if(!r||!b.test(r))throw Error(`Doom config at ${t} requires ${n} in provider/model-id form`);return r}function P(e){return e.normalize(`NFKC`).toLocaleLowerCase(`en-US`).replace(x,` `).replace(/\s+/gu,` `).trim()}function F(e,t,n){if(e===void 0)return;if(!Array.isArray(e)||e.some(e=>typeof e!=`string`))throw Error(`Doom config at ${n} requires ${t} to be an array of strings`);if(e.length>16)throw Error(`Doom config at ${n} requires ${t} to contain at most 16 phrases`);let r=e.map(e=>e.trim()),i=new Set;for(let e of r){if(e.length===0)throw Error(`Doom config at ${n} requires ${t} to contain non-empty strings`);if(e.length>64)throw Error(`Doom config at ${n} requires ${t} phrases to be at most 64 characters`);if(M(e))throw Error(`Doom config at ${n} does not allow control characters in ${t}`);let r=P(e);if(!r)throw Error(`Doom config at ${n} requires ${t} to contain words`);if(i.has(r))throw Error(`Doom config at ${n} does not allow normalized duplicates in ${t}`);i.add(r)}return r}function I(e,t){let n=`voice.autoCapture.tts`;if(!k(e))throw Error(`Doom config at ${t} requires ${n} to be an object`);if(A(e,h,n,t),typeof e.engine!=`string`||!O.has(e.engine))throw Error(`Doom config at ${t} requires ${n}.engine to be one of: ${[...O].join(`, `)}`);let r=se(e.voice,`${n}.voice`,t,64),i=e.rate;if(i!==void 0&&(typeof i!=`number`||!Number.isInteger(i)||i<80||i>500))throw Error(`Doom config at ${t} requires ${n}.rate to be an integer from 80 to 500`);return{engine:e.engine,...r?{voice:r}:{},...i===void 0?{}:{rate:i}}}function L(e,t){if(e===void 0)return;let n=`voice.autoCapture`;if(!k(e))throw Error(`Doom config at ${t} requires ${n} to be an object`);A(e,m,n,t);let r=F(e.startPhrases,`${n}.startPhrases`,t),i=F(e.stopPhrases,`${n}.stopPhrases`,t),a=e.utteranceIdleMs;if(a!==void 0&&(typeof a!=`number`||!Number.isInteger(a)||a<g||a>_))throw Error(`Doom config at ${t} requires ${n}.utteranceIdleMs to be an integer from ${g} to ${_}`);let o=e.transcriptionTimeoutMs;if(o!==void 0&&(typeof o!=`number`||!Number.isInteger(o)||o<v||o>y))throw Error(`Doom config at ${t} requires ${n}.transcriptionTimeoutMs to be an integer from ${v} to ${y}`);let s=new Set((r??[]).map(P));if((i??[]).some(e=>s.has(P(e))))throw Error(`Doom config at ${t} does not allow a phrase in both startPhrases and stopPhrases`);return{model:N(e.model,t),...r?{startPhrases:r}:{},...i?{stopPhrases:i}:{},...a===void 0?{}:{utteranceIdleMs:a},...o===void 0?{}:{transcriptionTimeoutMs:o},tts:I(e.tts,t)}}function R(e,t,n){if(e===void 0)return;if(!k(e))throw Error(`Doom config at ${n} requires ${t} to be an object`);A(e,te,t,n);let r=e.thinking;if(r!==void 0&&(typeof r!=`string`||!T.has(r)))throw Error(`Doom config at ${n} requires ${t}.thinking to be one of: ${[...T].join(`, `)}`);let i=j(e.model,`${t}.model`,n);return{...i?{model:i}:{},...r?{thinking:r}:{}}}function z(e,t,n=`modes.planning`){if(!k(e))throw Error(`Doom config at ${t} requires ${n} to be an object`);return A(e,l,n,t),{main:R(e.main,`${n}.main`,t),subagents:R(e.subagents,`${n}.subagents`,t),plansDirectory:j(e.plansDirectory,`${n}.plansDirectory`,t)}}function B(e,t,n,r){if(!k(e))throw Error(`Doom config at ${n} requires ${t} to be an object`);A(e,oe,t,n);let i=j(e.path,`${t}.path`,n),a=j(e.id,`${t}.id`,n);if(!!i==!!a)throw Error(`Doom config at ${n} requires exactly one of ${t}.path or ${t}.id`);if(a&&!r)throw Error(`Doom config at ${n} does not support ${t}.id for whisper-cpp`);return{...i?{path:i}:{},...a?{id:a}:{}}}function V(e,t,n,r){if(e===void 0)return;if(!k(e))throw Error(`Doom config at ${n} requires ${t} to be an object`);A(e,ae,t,n);let i=j(e.binary,`${t}.binary`,n);return{...i?{binary:i}:{},model:B(e.model,`${t}.model`,n,r)}}function H(e,t){if(e===void 0)return;if(!k(e))throw Error(`Doom config at ${t} requires voice to be an object`);A(e,re,`voice`,t);let n={};if(e.engine!==void 0){if(typeof e.engine!=`string`||!D.has(e.engine))throw Error(`Doom config at ${t} requires voice.engine to be one of: ${[...D].join(`, `)}`);n.engine=e.engine}let r=j(e.language,`voice.language`,t);if(r&&(n.language=r),e.recorder!==void 0){if(!k(e.recorder))throw Error(`Doom config at ${t} requires voice.recorder to be an object`);A(e.recorder,u,`voice.recorder`,t),n.recorder={};let r=j(e.recorder.binary,`voice.recorder.binary`,t),i=j(e.recorder.device,`voice.recorder.device`,t);r&&(n.recorder.binary=r),i&&(n.recorder.device=i)}if(e.adapters!==void 0){if(!k(e.adapters))throw Error(`Doom config at ${t} requires voice.adapters to be an object`);A(e.adapters,ie,`voice.adapters`,t),n.adapters={[d]:V(e.adapters[d],`voice.adapters.${d}`,t,!1),[f]:V(e.adapters[f],`voice.adapters.${f}`,t,!0),[p]:V(e.adapters[p],`voice.adapters.${p}`,t,!0)}}let i=L(e.autoCapture,t);return i&&(n.autoCapture=i),n}function U(e,t){if(e===void 0)return;if(!k(e))throw Error(`Doom config at ${t} requires selection to be an object`);A(e,c,`selection`,t);let n=e.domains;if(n!==void 0&&(!Array.isArray(n)||n.some(e=>typeof e!=`string`)))throw Error(`Doom config at ${t} requires selection.domains to be an array of strings`);return{majorMode:j(e.majorMode,`selection.majorMode`,t),domains:n,profile:j(e.profile,`selection.profile`,t)}}function W(e,t){if(e!==void 0){if(!k(e))throw Error(`Doom config at ${t} requires editor to be an object`);return A(e,ne,`editor`,t),{command:j(e.command,`editor.command`,t)}}}function G(e,t){let n;try{n=r(e)??{}}catch(e){throw Error(`Could not parse Doom config at ${t}`,{cause:e})}if(!k(n))throw Error(`Doom config at ${t} must be a YAML object`);A(n,s,`root`,t);let i;if(n.modes!==void 0){if(!k(n.modes))throw Error(`Doom config at ${t} requires modes to be an object`);A(n.modes,ee,`modes`,t),i=n.modes.planning===void 0?{}:{planning:z(n.modes.planning,t)}}let a=n.projectTrust===void 0?`ask`:n.projectTrust;if(typeof a!=`string`||!E.has(a))throw Error(`Doom config at ${t} projectTrust must be ask, always, or never`);return{modes:i,projectTrust:a,editor:W(n.editor,t),voice:H(n.voice,t),selection:U(n.selection,t)}}function K(t){return e.existsSync(t)?G(e.readFileSync(t,`utf8`),t):{projectTrust:`ask`}}async function q(t){try{return G(await e.promises.readFile(t,`utf8`),t)}catch(e){if(e.code===`ENOENT`)return{projectTrust:`ask`};throw e}}function J(e,t){return e||t?{...e,...t}:void 0}function Y(e,t){if(t.voice?.autoCapture)throw Error(`Repository Doom config voice.autoCapture is global-only; configure it in ~/.pi/.doom/config.yaml`);let n=e.modes?.planning,r=t.modes?.planning,i=n||r?{main:J(n?.main,r?.main),subagents:J(n?.subagents,r?.subagents),plansDirectory:r?.plansDirectory??n?.plansDirectory}:void 0,a=ce(e.voice,t.voice);return{modes:i?{planning:i}:void 0,projectTrust:t.projectTrust,editor:e.editor,voice:a,selection:e.selection||t.selection?{...e.selection,...t.selection}:void 0}}function X(e,t){if(!(!e&&!t))return{...e,...t,model:t?.model??e.model}}function ce(e,t){if(!(!e&&!t))return{...e,...t,recorder:e?.recorder||t?.recorder?{...e?.recorder,...t?.recorder}:void 0,adapters:e?.adapters||t?.adapters?{[d]:X(e?.adapters?.[d],t?.adapters?.[d]),[f]:X(e?.adapters?.[f],t?.adapters?.[f]),[p]:X(e?.adapters?.[p],t?.adapters?.[p])}:void 0,...e?.autoCapture?{autoCapture:e.autoCapture}:{}}}function le(e){return{engine:e.engine??o,language:e.language??`auto`,recorder:{...e.recorder,device:e.recorder?.device??`none:default`},adapters:e.adapters??{},...e.autoCapture?{autoCapture:{...e.autoCapture,startPhrases:[...e.autoCapture.startPhrases??[]],stopPhrases:[...e.autoCapture.stopPhrases??[]],utteranceIdleMs:e.autoCapture.utteranceIdleMs??3e3,transcriptionTimeoutMs:e.autoCapture.transcriptionTimeoutMs??15e3,tts:{...e.autoCapture.tts}}}:{}}}function Z(e=t.homedir()){return n.join(e,`.pi`,i)}function Q(e=t.homedir()){return n.join(Z(e),a)}function ue(e,r,i=t.homedir()){if(!e)return n.join(i,`.pi`,`plans`);if(e===`~`)return i;if(e.startsWith(`~/`))return n.resolve(i,e.slice(2));if(e.startsWith(`~`))throw Error(`Doom planning plansDirectory supports only '~' or '~/' home aliases`);return n.isAbsolute(e)?n.normalize(e):n.resolve(r,e)}function $(e){return n.join(e,i,a)}function de(e,n=t.homedir()){return Y(K(Q(n)),K($(e)))}async function fe(e,n=t.homedir()){return Y(await q(Q(n)),await q($(e)))}export{S as DOOM_PLANNING_THINKING_LEVELS,C as DOOM_VOICE_ENGINES,w as DOOM_VOICE_TTS_ENGINES,Z as globalDoomConfigDirectory,Q as globalDoomConfigPath,de as loadDoomConfig,fe as loadDoomConfigAsync,Y as mergeDoomConfigs,G as parseDoomConfig,z as parsePlanningModeConfig,$ as repositoryDoomConfigPath,ue as resolvePlanningPlansDirectory,le as resolveVoiceConfig};
|
|
2
|
+
//# sourceMappingURL=config.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.mjs","names":["parseYaml"],"sources":["../../src/adapters/config.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport type {\n DoomConfig,\n DoomSelectionConfig,\n EditorConfig,\n PlanningAgentConfig,\n PlanningModeConfig,\n PlanningThinkingLevel,\n ProjectTrust,\n ResolvedVoiceConfig,\n VoiceAdapterConfig,\n VoiceAutoCaptureConfig,\n VoiceConfig,\n VoiceEngine,\n VoiceModelConfig,\n VoiceTtsConfig,\n VoiceTtsEngine,\n} from '../types/index.ts';\n\nconst DOOM_DIR = '.doom';\nconst CONFIG_FILE = 'config.yaml';\nconst GLOBAL_CONFIG_DIR = '.pi';\nconst PLANS_DIR = 'plans';\nconst DEFAULT_PROJECT_TRUST: ProjectTrust = 'ask';\nconst DEFAULT_VOICE_ENGINE: VoiceEngine = 'auto';\nconst DEFAULT_VOICE_LANGUAGE = 'auto';\nconst DEFAULT_RECORDER_DEVICE = 'none:default';\nconst ROOT_KEYS = ['modes', 'projectTrust', 'editor', 'voice', 'selection'] as const;\nconst SELECTION_KEYS = ['majorMode', 'domains', 'profile'] as const;\nconst MODE_KEYS = ['planning'] as const;\nconst PLANNING_KEYS = ['main', 'subagents', 'plansDirectory'] as const;\nconst AGENT_KEYS = ['model', 'thinking'] as const;\nconst EDITOR_KEYS = ['command'] as const;\nconst VOICE_KEYS = ['engine', 'language', 'recorder', 'adapters', 'autoCapture'] as const;\nconst RECORDER_KEYS = ['binary', 'device'] as const;\nconst WHISPER_CPP_ENGINE = 'whisper-cpp' as const;\nconst OPENAI_WHISPER_ENGINE = 'openai-whisper' as const;\nconst MLX_WHISPER_ENGINE = 'mlx-whisper' as const;\nconst MACOS_SAY_TTS_ENGINE = 'macos-say' as const;\nconst ADAPTERS_KEYS = [WHISPER_CPP_ENGINE, OPENAI_WHISPER_ENGINE, MLX_WHISPER_ENGINE] as const;\nconst ADAPTER_KEYS = ['binary', 'model'] as const;\nconst MODEL_KEYS = ['path', 'id'] as const;\nconst AUTO_CAPTURE_KEYS = [\n 'model',\n 'startPhrases',\n 'stopPhrases',\n 'utteranceIdleMs',\n 'transcriptionTimeoutMs',\n 'tts',\n] as const;\nconst TTS_KEYS = ['engine', 'voice', 'rate'] as const;\nconst DEFAULT_UTTERANCE_IDLE_MS = 3_000;\nconst MIN_UTTERANCE_IDLE_MS = 1_500;\nconst MAX_UTTERANCE_IDLE_MS = 10_000;\nconst DEFAULT_TRANSCRIPTION_TIMEOUT_MS = 15_000;\nconst MIN_TRANSCRIPTION_TIMEOUT_MS = 1_000;\nconst MAX_TRANSCRIPTION_TIMEOUT_MS = 120_000;\nconst MAX_CONTROL_PHRASES = 16;\nconst MAX_CONTROL_PHRASE_LENGTH = 64;\nconst MAX_TTS_VOICE_LENGTH = 64;\nconst MIN_TTS_RATE = 80;\nconst MAX_TTS_RATE = 500;\nconst MODEL_REFERENCE_PATTERN = /^[^/\\s]+\\/[^/\\s]+(?:\\/[^/\\s]+)*$/u;\nconst PHRASE_PUNCTUATION_PATTERN = /[\\p{P}\\p{S}]+/gu;\n/**\n * The accepted values, in the order a picker should offer them.\n *\n * Exported because the config UI has to offer exactly what the parser accepts,\n * and a second hand-written list in the contributing package would drift. The\n * validating Sets below are built from these for the same reason.\n */\nexport const DOOM_PLANNING_THINKING_LEVELS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'] as const;\nexport const DOOM_VOICE_ENGINES = [\n DEFAULT_VOICE_ENGINE,\n WHISPER_CPP_ENGINE,\n OPENAI_WHISPER_ENGINE,\n MLX_WHISPER_ENGINE,\n] as const;\nexport const DOOM_VOICE_TTS_ENGINES = [MACOS_SAY_TTS_ENGINE] as const;\n\nconst THINKING_VALUES = new Set<PlanningThinkingLevel>(DOOM_PLANNING_THINKING_LEVELS);\nconst TRUST_VALUES = new Set<ProjectTrust>(['ask', 'always', 'never']);\nconst ENGINE_VALUES = new Set<VoiceEngine>(DOOM_VOICE_ENGINES);\nconst TTS_ENGINE_VALUES = new Set<VoiceTtsEngine>(DOOM_VOICE_TTS_ENGINES);\ntype ConfigObject = Record<string, unknown>;\n\nfunction isObject(value: unknown): value is ConfigObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\nfunction assertKeys(value: ConfigObject, allowed: readonly string[], location: string, filePath: string): void {\n const unknown = Object.keys(value).filter((key) => !allowed.includes(key));\n if (unknown.length > 0)\n throw new Error(`Doom config at ${filePath} has unsupported ${location} field(s): ${unknown.join(', ')}`);\n}\nfunction optionalString(value: unknown, location: string, filePath: string): string | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== 'string' || value.trim().length === 0)\n throw new Error(`Doom config at ${filePath} requires ${location} to be a non-empty string`);\n return value.trim();\n}\nfunction hasControlCharacters(value: string): boolean {\n for (const character of value) {\n const codePoint = character.codePointAt(0);\n if (codePoint !== undefined && (codePoint <= 0x1f || codePoint === 0x7f)) return true;\n }\n return false;\n}\nfunction boundedOptionalString(\n value: unknown,\n location: string,\n filePath: string,\n maximumLength: number,\n): string | undefined {\n const text = optionalString(value, location, filePath);\n if (!text) return undefined;\n if (text.length > maximumLength)\n throw new Error(`Doom config at ${filePath} requires ${location} to be at most ${maximumLength} characters`);\n if (hasControlCharacters(text))\n throw new Error(`Doom config at ${filePath} does not allow control characters in ${location}`);\n return text;\n}\nfunction parseModelReference(value: unknown, filePath: string): string {\n const location = 'voice.autoCapture.model';\n const model = optionalString(value, location, filePath);\n if (!model || !MODEL_REFERENCE_PATTERN.test(model))\n throw new Error(`Doom config at ${filePath} requires ${location} in provider/model-id form`);\n return model;\n}\nfunction normalizeControlPhrase(value: string): string {\n return value\n .normalize('NFKC')\n .toLocaleLowerCase('en-US')\n .replace(PHRASE_PUNCTUATION_PATTERN, ' ')\n .replace(/\\s+/gu, ' ')\n .trim();\n}\nfunction parseControlPhrases(value: unknown, location: string, filePath: string): string[] | undefined {\n if (value === undefined) return undefined;\n if (!Array.isArray(value) || value.some((phrase) => typeof phrase !== 'string'))\n throw new Error(`Doom config at ${filePath} requires ${location} to be an array of strings`);\n if (value.length > MAX_CONTROL_PHRASES)\n throw new Error(\n `Doom config at ${filePath} requires ${location} to contain at most ${MAX_CONTROL_PHRASES} phrases`,\n );\n const phrases = value.map((phrase) => phrase.trim());\n const normalized = new Set<string>();\n for (const phrase of phrases) {\n if (phrase.length === 0)\n throw new Error(`Doom config at ${filePath} requires ${location} to contain non-empty strings`);\n if (phrase.length > MAX_CONTROL_PHRASE_LENGTH)\n throw new Error(\n `Doom config at ${filePath} requires ${location} phrases to be at most ${MAX_CONTROL_PHRASE_LENGTH} characters`,\n );\n if (hasControlCharacters(phrase))\n throw new Error(`Doom config at ${filePath} does not allow control characters in ${location}`);\n const key = normalizeControlPhrase(phrase);\n if (!key) throw new Error(`Doom config at ${filePath} requires ${location} to contain words`);\n if (normalized.has(key))\n throw new Error(`Doom config at ${filePath} does not allow normalized duplicates in ${location}`);\n normalized.add(key);\n }\n return phrases;\n}\nfunction parseTtsConfig(value: unknown, filePath: string): VoiceTtsConfig {\n const location = 'voice.autoCapture.tts';\n if (!isObject(value)) throw new Error(`Doom config at ${filePath} requires ${location} to be an object`);\n assertKeys(value, TTS_KEYS, location, filePath);\n if (typeof value.engine !== 'string' || !TTS_ENGINE_VALUES.has(value.engine as VoiceTtsEngine))\n throw new Error(\n `Doom config at ${filePath} requires ${location}.engine to be one of: ${[...TTS_ENGINE_VALUES].join(', ')}`,\n );\n const voice = boundedOptionalString(value.voice, `${location}.voice`, filePath, MAX_TTS_VOICE_LENGTH);\n const rate = value.rate;\n if (\n rate !== undefined &&\n (typeof rate !== 'number' || !Number.isInteger(rate) || rate < MIN_TTS_RATE || rate > MAX_TTS_RATE)\n ) {\n throw new Error(\n `Doom config at ${filePath} requires ${location}.rate to be an integer from ${MIN_TTS_RATE} to ${MAX_TTS_RATE}`,\n );\n }\n return {\n engine: value.engine as VoiceTtsEngine,\n ...(voice ? { voice } : {}),\n ...(rate === undefined ? {} : { rate }),\n };\n}\nfunction parseAutoCapture(value: unknown, filePath: string): VoiceAutoCaptureConfig | undefined {\n if (value === undefined) return undefined;\n const location = 'voice.autoCapture';\n if (!isObject(value)) throw new Error(`Doom config at ${filePath} requires ${location} to be an object`);\n assertKeys(value, AUTO_CAPTURE_KEYS, location, filePath);\n const startPhrases = parseControlPhrases(value.startPhrases, `${location}.startPhrases`, filePath);\n const stopPhrases = parseControlPhrases(value.stopPhrases, `${location}.stopPhrases`, filePath);\n const utteranceIdleMs = value.utteranceIdleMs;\n if (\n utteranceIdleMs !== undefined &&\n (typeof utteranceIdleMs !== 'number' ||\n !Number.isInteger(utteranceIdleMs) ||\n utteranceIdleMs < MIN_UTTERANCE_IDLE_MS ||\n utteranceIdleMs > MAX_UTTERANCE_IDLE_MS)\n ) {\n throw new Error(\n `Doom config at ${filePath} requires ${location}.utteranceIdleMs to be an integer from ${MIN_UTTERANCE_IDLE_MS} to ${MAX_UTTERANCE_IDLE_MS}`,\n );\n }\n const transcriptionTimeoutMs = value.transcriptionTimeoutMs;\n if (\n transcriptionTimeoutMs !== undefined &&\n (typeof transcriptionTimeoutMs !== 'number' ||\n !Number.isInteger(transcriptionTimeoutMs) ||\n transcriptionTimeoutMs < MIN_TRANSCRIPTION_TIMEOUT_MS ||\n transcriptionTimeoutMs > MAX_TRANSCRIPTION_TIMEOUT_MS)\n ) {\n throw new Error(\n `Doom config at ${filePath} requires ${location}.transcriptionTimeoutMs to be an integer from ${MIN_TRANSCRIPTION_TIMEOUT_MS} to ${MAX_TRANSCRIPTION_TIMEOUT_MS}`,\n );\n }\n const startKeys = new Set((startPhrases ?? []).map(normalizeControlPhrase));\n if ((stopPhrases ?? []).some((phrase) => startKeys.has(normalizeControlPhrase(phrase))))\n throw new Error(`Doom config at ${filePath} does not allow a phrase in both startPhrases and stopPhrases`);\n return {\n model: parseModelReference(value.model, filePath),\n ...(startPhrases ? { startPhrases } : {}),\n ...(stopPhrases ? { stopPhrases } : {}),\n ...(utteranceIdleMs === undefined ? {} : { utteranceIdleMs }),\n ...(transcriptionTimeoutMs === undefined ? {} : { transcriptionTimeoutMs }),\n tts: parseTtsConfig(value.tts, filePath),\n };\n}\nfunction parseAgent(value: unknown, location: string, filePath: string): PlanningAgentConfig | undefined {\n if (value === undefined) return undefined;\n if (!isObject(value)) throw new Error(`Doom config at ${filePath} requires ${location} to be an object`);\n assertKeys(value, AGENT_KEYS, location, filePath);\n const thinking = value.thinking;\n if (\n thinking !== undefined &&\n (typeof thinking !== 'string' || !THINKING_VALUES.has(thinking as PlanningThinkingLevel))\n ) {\n throw new Error(\n `Doom config at ${filePath} requires ${location}.thinking to be one of: ${[...THINKING_VALUES].join(', ')}`,\n );\n }\n const model = optionalString(value.model, `${location}.model`, filePath);\n return { ...(model ? { model } : {}), ...(thinking ? { thinking: thinking as PlanningThinkingLevel } : {}) };\n}\nexport function parsePlanningModeConfig(\n value: unknown,\n filePath: string,\n location = 'modes.planning',\n): PlanningModeConfig {\n if (!isObject(value)) throw new Error(`Doom config at ${filePath} requires ${location} to be an object`);\n assertKeys(value, PLANNING_KEYS, location, filePath);\n return {\n main: parseAgent(value.main, `${location}.main`, filePath),\n subagents: parseAgent(value.subagents, `${location}.subagents`, filePath),\n plansDirectory: optionalString(value.plansDirectory, `${location}.plansDirectory`, filePath),\n };\n}\nfunction parseModel(value: unknown, location: string, filePath: string, allowId: boolean): VoiceModelConfig {\n if (!isObject(value)) throw new Error(`Doom config at ${filePath} requires ${location} to be an object`);\n assertKeys(value, MODEL_KEYS, location, filePath);\n const modelPath = optionalString(value.path, `${location}.path`, filePath);\n const id = optionalString(value.id, `${location}.id`, filePath);\n if (Boolean(modelPath) === Boolean(id))\n throw new Error(`Doom config at ${filePath} requires exactly one of ${location}.path or ${location}.id`);\n if (id && !allowId) throw new Error(`Doom config at ${filePath} does not support ${location}.id for whisper-cpp`);\n return { ...(modelPath ? { path: modelPath } : {}), ...(id ? { id } : {}) };\n}\nfunction parseAdapter(\n value: unknown,\n location: string,\n filePath: string,\n allowId: boolean,\n): VoiceAdapterConfig | undefined {\n if (value === undefined) return undefined;\n if (!isObject(value)) throw new Error(`Doom config at ${filePath} requires ${location} to be an object`);\n assertKeys(value, ADAPTER_KEYS, location, filePath);\n const binary = optionalString(value.binary, `${location}.binary`, filePath);\n return { ...(binary ? { binary } : {}), model: parseModel(value.model, `${location}.model`, filePath, allowId) };\n}\nfunction parseVoice(value: unknown, filePath: string): VoiceConfig | undefined {\n if (value === undefined) return undefined;\n if (!isObject(value)) throw new Error(`Doom config at ${filePath} requires voice to be an object`);\n assertKeys(value, VOICE_KEYS, 'voice', filePath);\n const result: VoiceConfig = {};\n if (value.engine !== undefined) {\n if (typeof value.engine !== 'string' || !ENGINE_VALUES.has(value.engine as VoiceEngine))\n throw new Error(\n `Doom config at ${filePath} requires voice.engine to be one of: ${[...ENGINE_VALUES].join(', ')}`,\n );\n result.engine = value.engine as VoiceEngine;\n }\n const language = optionalString(value.language, 'voice.language', filePath);\n if (language) result.language = language;\n if (value.recorder !== undefined) {\n if (!isObject(value.recorder))\n throw new Error(`Doom config at ${filePath} requires voice.recorder to be an object`);\n assertKeys(value.recorder, RECORDER_KEYS, 'voice.recorder', filePath);\n result.recorder = {};\n const binary = optionalString(value.recorder.binary, 'voice.recorder.binary', filePath);\n const device = optionalString(value.recorder.device, 'voice.recorder.device', filePath);\n if (binary) result.recorder.binary = binary;\n if (device) result.recorder.device = device;\n }\n if (value.adapters !== undefined) {\n if (!isObject(value.adapters))\n throw new Error(`Doom config at ${filePath} requires voice.adapters to be an object`);\n assertKeys(value.adapters, ADAPTERS_KEYS, 'voice.adapters', filePath);\n result.adapters = {\n [WHISPER_CPP_ENGINE]: parseAdapter(\n value.adapters[WHISPER_CPP_ENGINE],\n `voice.adapters.${WHISPER_CPP_ENGINE}`,\n filePath,\n false,\n ),\n [OPENAI_WHISPER_ENGINE]: parseAdapter(\n value.adapters[OPENAI_WHISPER_ENGINE],\n `voice.adapters.${OPENAI_WHISPER_ENGINE}`,\n filePath,\n true,\n ),\n [MLX_WHISPER_ENGINE]: parseAdapter(\n value.adapters[MLX_WHISPER_ENGINE],\n `voice.adapters.${MLX_WHISPER_ENGINE}`,\n filePath,\n true,\n ),\n };\n }\n const autoCapture = parseAutoCapture(value.autoCapture, filePath);\n if (autoCapture) result.autoCapture = autoCapture;\n return result;\n}\nfunction parseSelection(value: unknown, filePath: string): DoomSelectionConfig | undefined {\n if (value === undefined) return undefined;\n if (!isObject(value)) throw new Error(`Doom config at ${filePath} requires selection to be an object`);\n assertKeys(value, SELECTION_KEYS, 'selection', filePath);\n const domains = value.domains;\n if (domains !== undefined && (!Array.isArray(domains) || domains.some((name) => typeof name !== 'string'))) {\n throw new Error(`Doom config at ${filePath} requires selection.domains to be an array of strings`);\n }\n return {\n majorMode: optionalString(value.majorMode, 'selection.majorMode', filePath),\n // An empty list is meaningful: it selects no content domains at all.\n domains: domains as string[] | undefined,\n profile: optionalString(value.profile, 'selection.profile', filePath),\n };\n}\nfunction parseEditor(value: unknown, filePath: string): EditorConfig | undefined {\n if (value === undefined) return undefined;\n if (!isObject(value)) throw new Error(`Doom config at ${filePath} requires editor to be an object`);\n assertKeys(value, EDITOR_KEYS, 'editor', filePath);\n return { command: optionalString(value.command, 'editor.command', filePath) };\n}\nexport function parseDoomConfig(content: string, filePath: string): DoomConfig {\n let parsed: unknown;\n try {\n parsed = parseYaml(content) ?? {};\n } catch (error) {\n throw new Error(`Could not parse Doom config at ${filePath}`, { cause: error });\n }\n if (!isObject(parsed)) throw new Error(`Doom config at ${filePath} must be a YAML object`);\n assertKeys(parsed, ROOT_KEYS, 'root', filePath);\n let modes: DoomConfig['modes'];\n if (parsed.modes !== undefined) {\n if (!isObject(parsed.modes)) throw new Error(`Doom config at ${filePath} requires modes to be an object`);\n assertKeys(parsed.modes, MODE_KEYS, 'modes', filePath);\n modes =\n parsed.modes.planning === undefined ? {} : { planning: parsePlanningModeConfig(parsed.modes.planning, filePath) };\n }\n const trust = parsed.projectTrust === undefined ? DEFAULT_PROJECT_TRUST : parsed.projectTrust;\n if (typeof trust !== 'string' || !TRUST_VALUES.has(trust as ProjectTrust))\n throw new Error(`Doom config at ${filePath} projectTrust must be ask, always, or never`);\n return {\n modes,\n projectTrust: trust as ProjectTrust,\n editor: parseEditor(parsed.editor, filePath),\n voice: parseVoice(parsed.voice, filePath),\n selection: parseSelection(parsed.selection, filePath),\n };\n}\nfunction readConfig(filePath: string): DoomConfig {\n return fs.existsSync(filePath)\n ? parseDoomConfig(fs.readFileSync(filePath, 'utf8'), filePath)\n : { projectTrust: DEFAULT_PROJECT_TRUST };\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: DEFAULT_PROJECT_TRUST };\n throw error;\n }\n}\nfunction mergeAgent(\n globalValue?: PlanningAgentConfig,\n repositoryValue?: PlanningAgentConfig,\n): PlanningAgentConfig | undefined {\n return globalValue || repositoryValue ? { ...globalValue, ...repositoryValue } : undefined;\n}\nexport function mergeDoomConfigs(globalConfig: DoomConfig, repositoryConfig: DoomConfig): DoomConfig {\n if (repositoryConfig.voice?.autoCapture)\n throw new Error('Repository Doom config voice.autoCapture is global-only; configure it in ~/.pi/.doom/config.yaml');\n const globalPlanning = globalConfig.modes?.planning;\n const repositoryPlanning = repositoryConfig.modes?.planning;\n const planning =\n globalPlanning || repositoryPlanning\n ? {\n main: mergeAgent(globalPlanning?.main, repositoryPlanning?.main),\n subagents: mergeAgent(globalPlanning?.subagents, repositoryPlanning?.subagents),\n plansDirectory: repositoryPlanning?.plansDirectory ?? globalPlanning?.plansDirectory,\n }\n : undefined;\n const voice = mergeVoice(globalConfig.voice, repositoryConfig.voice);\n return {\n modes: planning ? { planning } : undefined,\n projectTrust: repositoryConfig.projectTrust,\n editor: globalConfig.editor,\n voice,\n // Per key, so a repository can pin one axis and inherit the rest.\n selection:\n globalConfig.selection || repositoryConfig.selection\n ? { ...globalConfig.selection, ...repositoryConfig.selection }\n : undefined,\n };\n}\nfunction mergeAdapter(\n globalValue?: VoiceAdapterConfig,\n repositoryValue?: VoiceAdapterConfig,\n): VoiceAdapterConfig | undefined {\n if (!globalValue && !repositoryValue) return undefined;\n return { ...globalValue, ...repositoryValue, model: repositoryValue?.model ?? globalValue!.model };\n}\nfunction mergeVoice(globalValue?: VoiceConfig, repositoryValue?: VoiceConfig): VoiceConfig | undefined {\n if (!globalValue && !repositoryValue) return undefined;\n return {\n ...globalValue,\n ...repositoryValue,\n recorder:\n globalValue?.recorder || repositoryValue?.recorder\n ? { ...globalValue?.recorder, ...repositoryValue?.recorder }\n : undefined,\n adapters:\n globalValue?.adapters || repositoryValue?.adapters\n ? {\n [WHISPER_CPP_ENGINE]: mergeAdapter(\n globalValue?.adapters?.[WHISPER_CPP_ENGINE],\n repositoryValue?.adapters?.[WHISPER_CPP_ENGINE],\n ),\n [OPENAI_WHISPER_ENGINE]: mergeAdapter(\n globalValue?.adapters?.[OPENAI_WHISPER_ENGINE],\n repositoryValue?.adapters?.[OPENAI_WHISPER_ENGINE],\n ),\n [MLX_WHISPER_ENGINE]: mergeAdapter(\n globalValue?.adapters?.[MLX_WHISPER_ENGINE],\n repositoryValue?.adapters?.[MLX_WHISPER_ENGINE],\n ),\n }\n : undefined,\n ...(globalValue?.autoCapture ? { autoCapture: globalValue.autoCapture } : {}),\n };\n}\nexport function resolveVoiceConfig(config: VoiceConfig): ResolvedVoiceConfig {\n return {\n engine: config.engine ?? DEFAULT_VOICE_ENGINE,\n language: config.language ?? DEFAULT_VOICE_LANGUAGE,\n recorder: { ...config.recorder, device: config.recorder?.device ?? DEFAULT_RECORDER_DEVICE },\n adapters: config.adapters ?? {},\n ...(config.autoCapture\n ? {\n autoCapture: {\n ...config.autoCapture,\n startPhrases: [...(config.autoCapture.startPhrases ?? [])],\n stopPhrases: [...(config.autoCapture.stopPhrases ?? [])],\n utteranceIdleMs: config.autoCapture.utteranceIdleMs ?? DEFAULT_UTTERANCE_IDLE_MS,\n transcriptionTimeoutMs: config.autoCapture.transcriptionTimeoutMs ?? DEFAULT_TRANSCRIPTION_TIMEOUT_MS,\n tts: { ...config.autoCapture.tts },\n },\n }\n : {}),\n };\n}\nexport function globalDoomConfigDirectory(homeDirectory = os.homedir()): string {\n return path.join(homeDirectory, GLOBAL_CONFIG_DIR, DOOM_DIR);\n}\nexport function globalDoomConfigPath(homeDirectory = os.homedir()): string {\n return path.join(globalDoomConfigDirectory(homeDirectory), CONFIG_FILE);\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}\nexport function repositoryDoomConfigPath(repoRoot: string): string {\n return path.join(repoRoot, DOOM_DIR, CONFIG_FILE);\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"],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./config.cjs");let n=require("node:fs");n=e.__toESM(n,1);let r=require("node:os");r=e.__toESM(r,1);let i=require("node:path");i=e.__toESM(i,1);let a=require("node:crypto");const o=`utf8`,s=`EEXIST`,c=`ENOENT`,l=[`config.yaml`,`modes.yaml`,`domains.yaml`,`profiles.yaml`,`hooks.yaml`],u={"config.yaml":`projectTrust: ask
|
|
2
|
+
|
|
3
|
+
# Autonomous voice is opt-in and uses an explicitly configured Pi model.
|
|
4
|
+
# voice:
|
|
5
|
+
# autoCapture:
|
|
6
|
+
# model: provider/model-id
|
|
7
|
+
# startPhrases:
|
|
8
|
+
# - hey doom
|
|
9
|
+
# stopPhrases:
|
|
10
|
+
# - stop speaking
|
|
11
|
+
# utteranceIdleMs: 3000
|
|
12
|
+
# tts:
|
|
13
|
+
# engine: macos-say
|
|
14
|
+
# voice: Samantha
|
|
15
|
+
# rate: 190
|
|
16
|
+
`,"modes.yaml":`# Seeded defaults. Every layer here resolves from what Doom Pi installs or
|
|
17
|
+
# from a public package, so a fresh install works with no extra setup.
|
|
18
|
+
#
|
|
19
|
+
# Layers and modes defined here apply to every repository. A repository's own
|
|
20
|
+
# .doom/modes.yaml is read after this file and replaces any layer or mode of the
|
|
21
|
+
# same name, so a repository can override one entry without restating the rest.
|
|
22
|
+
#
|
|
23
|
+
# A layer package is an npm specifier, or a path if it was never published:
|
|
24
|
+
# packages: ['./extensions/my-ext']
|
|
25
|
+
# A path is relative to the config that declares it, so it resolves beside this
|
|
26
|
+
# file here (~/.pi/.doom) and against the repository root in a repository copy.
|
|
27
|
+
layers:
|
|
28
|
+
guardrails:
|
|
29
|
+
# repositoryHooks ships with Doom Pi, so the layer steers the agent toward
|
|
30
|
+
# the governing skill and scaffolding before it writes.
|
|
31
|
+
extensions: [repositoryHooks]
|
|
32
|
+
hookGroups: [guardrails, scaffolding]
|
|
33
|
+
vibe-lint:
|
|
34
|
+
packages:
|
|
35
|
+
- '@agimon-ai/vibe-lint/extensions/pi'
|
|
36
|
+
team:
|
|
37
|
+
packages:
|
|
38
|
+
- '@agimon-ai/doompi-team/extensions/doom'
|
|
39
|
+
plan-mode:
|
|
40
|
+
packages:
|
|
41
|
+
- '@agimon-ai/doompi-plan/extensions/doom'
|
|
42
|
+
runner:
|
|
43
|
+
packages:
|
|
44
|
+
- '@agimon-ai/doompi-runner/extensions/pi'
|
|
45
|
+
style-system:
|
|
46
|
+
extensions: [styleSystem]
|
|
47
|
+
ask-user:
|
|
48
|
+
packages:
|
|
49
|
+
- '@juicesharp/rpiv-ask-user-question'
|
|
50
|
+
# A mode is a named set of the layers above. The default keeps its real mode
|
|
51
|
+
# name; it is selected only when neither --major-mode nor DOOMPI_MAJOR_MODE is set.
|
|
52
|
+
defaultMajorMode: copilot
|
|
53
|
+
majorMode:
|
|
54
|
+
minimal: [guardrails, team]
|
|
55
|
+
copilot: [guardrails, vibe-lint, team, plan-mode, runner, style-system, ask-user]
|
|
56
|
+
`,"domains.yaml":`# A domain is a named set of plugin directories loaded for a session.
|
|
57
|
+
# Add your own alongside default; aliases map short names onto them.
|
|
58
|
+
# Defaults are a list because one session may combine several domains.
|
|
59
|
+
defaultDomains: [default]
|
|
60
|
+
#
|
|
61
|
+
# Each plugin path is written in full and is relative to the config that
|
|
62
|
+
# declares it, so it resolves beside this file here (~/.pi/.doom) and against
|
|
63
|
+
# the repository root in a repository copy:
|
|
64
|
+
# plugins: ['plugins/my-plugin']
|
|
65
|
+
# A repository's own .doom/domains.yaml is read after this file, replaces any
|
|
66
|
+
# domain of the same name, and can replace defaultDomains.
|
|
67
|
+
domains:
|
|
68
|
+
default:
|
|
69
|
+
plugins: []
|
|
70
|
+
aliases: {}
|
|
71
|
+
`,"profiles.yaml":`profiles: {}
|
|
72
|
+
`,"hooks.yaml":`groups: {}
|
|
73
|
+
`};function d(e){if(!(e instanceof Error))return;let t=e.code;return typeof t==`string`?t:void 0}function f(e){let t;try{t=n.default.lstatSync(e)}catch(r){if(d(r)!==c)throw r;try{n.default.mkdirSync(e,{mode:448}),t=n.default.lstatSync(e)}catch(r){if(d(r)!==s)throw r;t=n.default.lstatSync(e)}}if(t.isSymbolicLink())throw Error(`Refusing to initialize through symlinked directory: ${e}`);if(!t.isDirectory())throw Error(`Doom config path is not a directory: ${e}`)}function p(e){f(i.default.dirname(e)),f(e)}function m(e){let t;try{t=n.default.lstatSync(e)}catch(e){if(d(e)===c)return`missing`;throw e}if(t.isFile())return`file`;if(t.isSymbolicLink())try{if(n.default.statSync(e).isFile())return`file`}catch(t){throw Error(`Doom config seed symlink is not file-like: ${e}`,{cause:t})}throw Error(`Doom config seed path is not file-like: ${e}`)}function h(e){try{n.default.unlinkSync(e)}catch(e){if(d(e)!==c)throw e}}function g(e,t){let r=`${e}.${process.pid}.${(0,a.randomUUID)()}.tmp`,i;try{i=n.default.openSync(r,`wx`,384),n.default.writeFileSync(i,t,o),n.default.fchmodSync(i,384),n.default.fsyncSync(i);let a=i;i=void 0,n.default.closeSync(a);try{return n.default.linkSync(r,e),!0}catch(t){if(d(t)===s&&m(e)===`file`)return!1;throw t}}finally{if(i!==void 0){let e=i;i=void 0,n.default.closeSync(e)}h(r)}}function _(e,t){let r=`${e}.${process.pid}.${(0,a.randomUUID)()}.tmp`,i;try{i=n.default.openSync(r,`wx`,384),n.default.writeFileSync(i,t,o),n.default.fchmodSync(i,384),n.default.fsyncSync(i);let a=i;i=void 0,n.default.closeSync(a),n.default.renameSync(r,e)}finally{if(i!==void 0){let e=i;i=void 0,n.default.closeSync(e)}h(r)}}function v(e=r.default.homedir(),n={}){let a=t.globalDoomConfigDirectory(e);p(a);let o=[],s=[],c=[];for(let e of l){let t=i.default.join(a,e),r=m(t)===`file`;if(n.force){_(t,u[e]),(r?c:o).push(e);continue}if(r){s.push(e);continue}g(t,u[e])?o.push(e):s.push(e)}return{directory:a,created:o,preserved:s,replaced:c}}exports.DOOM_CONFIG_TEMPLATES=u,exports.GLOBAL_DOOM_SEED_FILES=l,exports.initializeGlobalDoomConfig=v;
|
|
74
|
+
//# sourceMappingURL=init.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.cjs","names":["fs","path","randomUUID","os","globalDoomConfigDirectory"],"sources":["../../src/adapters/init.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { globalDoomConfigDirectory } from './config.ts';\n\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\nconst UTF8_ENCODING = 'utf8';\nconst FILE_ALREADY_EXISTS = 'EEXIST';\nconst FILE_NOT_FOUND = 'ENOENT';\n\n/** The global seed files are processed in this fixed order. */\nexport const GLOBAL_DOOM_SEED_FILES = [\n 'config.yaml',\n 'modes.yaml',\n 'domains.yaml',\n 'profiles.yaml',\n 'hooks.yaml',\n] as const;\n\ntype GlobalDoomSeedFile = (typeof GLOBAL_DOOM_SEED_FILES)[number];\n\n/** Portable defaults for a new global Doom configuration directory. */\nexport const DOOM_CONFIG_TEMPLATES: Readonly<Record<GlobalDoomSeedFile, string>> = {\n 'config.yaml': `projectTrust: ask\n\n# Autonomous voice is opt-in and uses an explicitly configured Pi model.\n# voice:\n# autoCapture:\n# model: provider/model-id\n# startPhrases:\n# - hey doom\n# stopPhrases:\n# - stop speaking\n# utteranceIdleMs: 3000\n# tts:\n# engine: macos-say\n# voice: Samantha\n# rate: 190\n`,\n 'modes.yaml': `# Seeded defaults. Every layer here resolves from what Doom Pi installs or\n# from a public package, so a fresh install works with no extra setup.\n#\n# Layers and modes defined here apply to every repository. A repository's own\n# .doom/modes.yaml is read after this file and replaces any layer or mode of the\n# same name, so a repository can override one entry without restating the rest.\n#\n# A layer package is an npm specifier, or a path if it was never published:\n# packages: ['./extensions/my-ext']\n# A path is relative to the config that declares it, so it resolves beside this\n# file here (~/.pi/.doom) and against the repository root in a repository copy.\nlayers:\n guardrails:\n # repositoryHooks ships with Doom Pi, so the layer steers the agent toward\n # the governing skill and scaffolding before it writes.\n extensions: [repositoryHooks]\n hookGroups: [guardrails, scaffolding]\n vibe-lint:\n packages:\n - '@agimon-ai/vibe-lint/extensions/pi'\n team:\n packages:\n - '@agimon-ai/doompi-team/extensions/doom'\n plan-mode:\n packages:\n - '@agimon-ai/doompi-plan/extensions/doom'\n runner:\n packages:\n - '@agimon-ai/doompi-runner/extensions/pi'\n style-system:\n extensions: [styleSystem]\n ask-user:\n packages:\n - '@juicesharp/rpiv-ask-user-question'\n# A mode is a named set of the layers above. The default keeps its real mode\n# name; it is selected only when neither --major-mode nor DOOMPI_MAJOR_MODE is set.\ndefaultMajorMode: copilot\nmajorMode:\n minimal: [guardrails, team]\n copilot: [guardrails, vibe-lint, team, plan-mode, runner, style-system, ask-user]\n`,\n 'domains.yaml': `# A domain is a named set of plugin directories loaded for a session.\n# Add your own alongside default; aliases map short names onto them.\n# Defaults are a list because one session may combine several domains.\ndefaultDomains: [default]\n#\n# Each plugin path is written in full and is relative to the config that\n# declares it, so it resolves beside this file here (~/.pi/.doom) and against\n# the repository root in a repository copy:\n# plugins: ['plugins/my-plugin']\n# A repository's own .doom/domains.yaml is read after this file, replaces any\n# domain of the same name, and can replace defaultDomains.\ndomains:\n default:\n plugins: []\naliases: {}\n`,\n 'profiles.yaml': 'profiles: {}\\n',\n 'hooks.yaml': 'groups: {}\\n',\n};\n\n/** Result of filling the global seed directory. File arrays contain names in seed order. */\nexport interface GlobalDoomInitResult {\n directory: string;\n created: string[];\n preserved: string[];\n /** Existing files overwritten from the template. Only ever non-empty under `force`. */\n replaced: string[];\n}\n\nexport interface GlobalDoomInitOptions {\n /**\n * Overwrite existing seed files instead of preserving them.\n *\n * Off by default: init is safe to re-run, and hand-edited config is the\n * normal case rather than the exception.\n */\n force?: boolean;\n}\n\nfunction errorCode(error: unknown): string | undefined {\n if (!(error instanceof Error)) return undefined;\n const code = (error as NodeJS.ErrnoException).code;\n return typeof code === 'string' ? code : undefined;\n}\n\nfunction ensureDirectory(directory: string): void {\n let stats: fs.Stats;\n try {\n stats = fs.lstatSync(directory);\n } catch (error) {\n if (errorCode(error) !== FILE_NOT_FOUND) throw error;\n try {\n fs.mkdirSync(directory, { mode: PRIVATE_DIRECTORY_MODE });\n stats = fs.lstatSync(directory);\n } catch (mkdirError) {\n if (errorCode(mkdirError) !== FILE_ALREADY_EXISTS) throw mkdirError;\n stats = fs.lstatSync(directory);\n }\n }\n\n if (stats.isSymbolicLink()) {\n throw new Error(`Refusing to initialize through symlinked directory: ${directory}`);\n }\n if (!stats.isDirectory()) {\n throw new Error(`Doom config path is not a directory: ${directory}`);\n }\n}\n\nfunction ensureGlobalDoomDirectory(directory: string): void {\n ensureDirectory(path.dirname(directory));\n ensureDirectory(directory);\n}\n\nfunction classifyTarget(targetPath: string): 'missing' | 'file' {\n let stats: fs.Stats;\n try {\n stats = fs.lstatSync(targetPath);\n } catch (error) {\n if (errorCode(error) === FILE_NOT_FOUND) return 'missing';\n throw error;\n }\n\n if (stats.isFile()) return 'file';\n if (stats.isSymbolicLink()) {\n try {\n if (fs.statSync(targetPath).isFile()) return 'file';\n } catch (error) {\n throw new Error(`Doom config seed symlink is not file-like: ${targetPath}`, { cause: error });\n }\n }\n throw new Error(`Doom config seed path is not file-like: ${targetPath}`);\n}\n\nfunction cleanupTemporaryFile(temporaryPath: string): void {\n try {\n fs.unlinkSync(temporaryPath);\n } catch (error) {\n if (errorCode(error) !== FILE_NOT_FOUND) throw error;\n }\n}\n\nfunction publishExclusively(targetPath: string, content: string): boolean {\n const temporaryPath = `${targetPath}.${process.pid}.${randomUUID()}.tmp`;\n let fileDescriptor: number | undefined;\n try {\n fileDescriptor = fs.openSync(temporaryPath, 'wx', PRIVATE_FILE_MODE);\n fs.writeFileSync(fileDescriptor, content, UTF8_ENCODING);\n fs.fchmodSync(fileDescriptor, PRIVATE_FILE_MODE);\n fs.fsyncSync(fileDescriptor);\n const descriptorToClose = fileDescriptor;\n fileDescriptor = undefined;\n fs.closeSync(descriptorToClose);\n\n try {\n fs.linkSync(temporaryPath, targetPath);\n return true;\n } catch (error) {\n if (errorCode(error) === FILE_ALREADY_EXISTS && classifyTarget(targetPath) === 'file') return false;\n throw error;\n }\n } finally {\n if (fileDescriptor !== undefined) {\n const descriptorToClose = fileDescriptor;\n fileDescriptor = undefined;\n fs.closeSync(descriptorToClose);\n }\n cleanupTemporaryFile(temporaryPath);\n }\n}\n\n/**\n * Replaces a seed file with the template, atomically.\n *\n * The rename swaps the directory entry in one step, so a reader either sees the\n * whole old file or the whole new one. Renaming onto a symlinked seed replaces\n * the link rather than writing through it.\n */\nfunction publishForcefully(targetPath: string, content: string): void {\n const temporaryPath = `${targetPath}.${process.pid}.${randomUUID()}.tmp`;\n let fileDescriptor: number | undefined;\n try {\n fileDescriptor = fs.openSync(temporaryPath, 'wx', PRIVATE_FILE_MODE);\n fs.writeFileSync(fileDescriptor, content, UTF8_ENCODING);\n fs.fchmodSync(fileDescriptor, PRIVATE_FILE_MODE);\n fs.fsyncSync(fileDescriptor);\n const descriptorToClose = fileDescriptor;\n fileDescriptor = undefined;\n fs.closeSync(descriptorToClose);\n\n fs.renameSync(temporaryPath, targetPath);\n } finally {\n if (fileDescriptor !== undefined) {\n const descriptorToClose = fileDescriptor;\n fileDescriptor = undefined;\n fs.closeSync(descriptorToClose);\n }\n cleanupTemporaryFile(temporaryPath);\n }\n}\n\n/**\n * Fills missing global seed files.\n *\n * Existing files are left alone so that re-running init never discards\n * hand-edited config; pass `force` to overwrite them from the template.\n */\nexport function initializeGlobalDoomConfig(\n homeDirectory = os.homedir(),\n options: GlobalDoomInitOptions = {},\n): GlobalDoomInitResult {\n const directory = globalDoomConfigDirectory(homeDirectory);\n ensureGlobalDoomDirectory(directory);\n\n const created: string[] = [];\n const preserved: string[] = [];\n const replaced: string[] = [];\n for (const fileName of GLOBAL_DOOM_SEED_FILES) {\n const targetPath = path.join(directory, fileName);\n const existed = classifyTarget(targetPath) === 'file';\n\n if (options.force) {\n publishForcefully(targetPath, DOOM_CONFIG_TEMPLATES[fileName]);\n (existed ? replaced : created).push(fileName);\n continue;\n }\n if (existed) {\n preserved.push(fileName);\n continue;\n }\n if (publishExclusively(targetPath, DOOM_CONFIG_TEMPLATES[fileName])) created.push(fileName);\n else preserved.push(fileName);\n }\n\n return { directory, created, preserved, replaced };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/adapters/init.d.ts
|
|
2
|
+
/** The global seed files are processed in this fixed order. */
|
|
3
|
+
declare const GLOBAL_DOOM_SEED_FILES: readonly ["config.yaml", "modes.yaml", "domains.yaml", "profiles.yaml", "hooks.yaml"];
|
|
4
|
+
type GlobalDoomSeedFile = (typeof GLOBAL_DOOM_SEED_FILES)[number];
|
|
5
|
+
/** Portable defaults for a new global Doom configuration directory. */
|
|
6
|
+
declare const DOOM_CONFIG_TEMPLATES: Readonly<Record<GlobalDoomSeedFile, string>>;
|
|
7
|
+
/** Result of filling the global seed directory. File arrays contain names in seed order. */
|
|
8
|
+
interface GlobalDoomInitResult {
|
|
9
|
+
directory: string;
|
|
10
|
+
created: string[];
|
|
11
|
+
preserved: string[];
|
|
12
|
+
/** Existing files overwritten from the template. Only ever non-empty under `force`. */
|
|
13
|
+
replaced: string[];
|
|
14
|
+
}
|
|
15
|
+
interface GlobalDoomInitOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Overwrite existing seed files instead of preserving them.
|
|
18
|
+
*
|
|
19
|
+
* Off by default: init is safe to re-run, and hand-edited config is the
|
|
20
|
+
* normal case rather than the exception.
|
|
21
|
+
*/
|
|
22
|
+
force?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Fills missing global seed files.
|
|
26
|
+
*
|
|
27
|
+
* Existing files are left alone so that re-running init never discards
|
|
28
|
+
* hand-edited config; pass `force` to overwrite them from the template.
|
|
29
|
+
*/
|
|
30
|
+
declare function initializeGlobalDoomConfig(homeDirectory?: string, options?: GlobalDoomInitOptions): GlobalDoomInitResult;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { DOOM_CONFIG_TEMPLATES, GLOBAL_DOOM_SEED_FILES, GlobalDoomInitOptions, GlobalDoomInitResult, initializeGlobalDoomConfig };
|
|
33
|
+
//# sourceMappingURL=init.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.cts","names":[],"sources":["../../src/adapters/init.ts"],"mappings":";;cAca;KAQR,6BAA6B;;cAGrB,uBAAuB,SAAS,OAAO;;UA+EnC;EACf;EACA;EACA;;EAEA;;UAGe;;;;;;;EAOf;;;;;;;;iBAkIc,2BACd,wBACA,UAAS,wBACR"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/adapters/init.d.ts
|
|
2
|
+
/** The global seed files are processed in this fixed order. */
|
|
3
|
+
declare const GLOBAL_DOOM_SEED_FILES: readonly ["config.yaml", "modes.yaml", "domains.yaml", "profiles.yaml", "hooks.yaml"];
|
|
4
|
+
type GlobalDoomSeedFile = (typeof GLOBAL_DOOM_SEED_FILES)[number];
|
|
5
|
+
/** Portable defaults for a new global Doom configuration directory. */
|
|
6
|
+
declare const DOOM_CONFIG_TEMPLATES: Readonly<Record<GlobalDoomSeedFile, string>>;
|
|
7
|
+
/** Result of filling the global seed directory. File arrays contain names in seed order. */
|
|
8
|
+
interface GlobalDoomInitResult {
|
|
9
|
+
directory: string;
|
|
10
|
+
created: string[];
|
|
11
|
+
preserved: string[];
|
|
12
|
+
/** Existing files overwritten from the template. Only ever non-empty under `force`. */
|
|
13
|
+
replaced: string[];
|
|
14
|
+
}
|
|
15
|
+
interface GlobalDoomInitOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Overwrite existing seed files instead of preserving them.
|
|
18
|
+
*
|
|
19
|
+
* Off by default: init is safe to re-run, and hand-edited config is the
|
|
20
|
+
* normal case rather than the exception.
|
|
21
|
+
*/
|
|
22
|
+
force?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Fills missing global seed files.
|
|
26
|
+
*
|
|
27
|
+
* Existing files are left alone so that re-running init never discards
|
|
28
|
+
* hand-edited config; pass `force` to overwrite them from the template.
|
|
29
|
+
*/
|
|
30
|
+
declare function initializeGlobalDoomConfig(homeDirectory?: string, options?: GlobalDoomInitOptions): GlobalDoomInitResult;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { DOOM_CONFIG_TEMPLATES, GLOBAL_DOOM_SEED_FILES, GlobalDoomInitOptions, GlobalDoomInitResult, initializeGlobalDoomConfig };
|
|
33
|
+
//# sourceMappingURL=init.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.mts","names":[],"sources":["../../src/adapters/init.ts"],"mappings":";;cAca;KAQR,6BAA6B;;cAGrB,uBAAuB,SAAS,OAAO;;UA+EnC;EACf;EACA;EACA;;EAEA;;UAGe;;;;;;;EAOf;;;;;;;;iBAkIc,2BACd,wBACA,UAAS,wBACR"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import{globalDoomConfigDirectory as e}from"./config.mjs";import t from"node:fs";import n from"node:os";import r from"node:path";import{randomUUID as i}from"node:crypto";const a=`utf8`,o=`EEXIST`,s=`ENOENT`,c=[`config.yaml`,`modes.yaml`,`domains.yaml`,`profiles.yaml`,`hooks.yaml`],l={"config.yaml":`projectTrust: ask
|
|
2
|
+
|
|
3
|
+
# Autonomous voice is opt-in and uses an explicitly configured Pi model.
|
|
4
|
+
# voice:
|
|
5
|
+
# autoCapture:
|
|
6
|
+
# model: provider/model-id
|
|
7
|
+
# startPhrases:
|
|
8
|
+
# - hey doom
|
|
9
|
+
# stopPhrases:
|
|
10
|
+
# - stop speaking
|
|
11
|
+
# utteranceIdleMs: 3000
|
|
12
|
+
# tts:
|
|
13
|
+
# engine: macos-say
|
|
14
|
+
# voice: Samantha
|
|
15
|
+
# rate: 190
|
|
16
|
+
`,"modes.yaml":`# Seeded defaults. Every layer here resolves from what Doom Pi installs or
|
|
17
|
+
# from a public package, so a fresh install works with no extra setup.
|
|
18
|
+
#
|
|
19
|
+
# Layers and modes defined here apply to every repository. A repository's own
|
|
20
|
+
# .doom/modes.yaml is read after this file and replaces any layer or mode of the
|
|
21
|
+
# same name, so a repository can override one entry without restating the rest.
|
|
22
|
+
#
|
|
23
|
+
# A layer package is an npm specifier, or a path if it was never published:
|
|
24
|
+
# packages: ['./extensions/my-ext']
|
|
25
|
+
# A path is relative to the config that declares it, so it resolves beside this
|
|
26
|
+
# file here (~/.pi/.doom) and against the repository root in a repository copy.
|
|
27
|
+
layers:
|
|
28
|
+
guardrails:
|
|
29
|
+
# repositoryHooks ships with Doom Pi, so the layer steers the agent toward
|
|
30
|
+
# the governing skill and scaffolding before it writes.
|
|
31
|
+
extensions: [repositoryHooks]
|
|
32
|
+
hookGroups: [guardrails, scaffolding]
|
|
33
|
+
vibe-lint:
|
|
34
|
+
packages:
|
|
35
|
+
- '@agimon-ai/vibe-lint/extensions/pi'
|
|
36
|
+
team:
|
|
37
|
+
packages:
|
|
38
|
+
- '@agimon-ai/doompi-team/extensions/doom'
|
|
39
|
+
plan-mode:
|
|
40
|
+
packages:
|
|
41
|
+
- '@agimon-ai/doompi-plan/extensions/doom'
|
|
42
|
+
runner:
|
|
43
|
+
packages:
|
|
44
|
+
- '@agimon-ai/doompi-runner/extensions/pi'
|
|
45
|
+
style-system:
|
|
46
|
+
extensions: [styleSystem]
|
|
47
|
+
ask-user:
|
|
48
|
+
packages:
|
|
49
|
+
- '@juicesharp/rpiv-ask-user-question'
|
|
50
|
+
# A mode is a named set of the layers above. The default keeps its real mode
|
|
51
|
+
# name; it is selected only when neither --major-mode nor DOOMPI_MAJOR_MODE is set.
|
|
52
|
+
defaultMajorMode: copilot
|
|
53
|
+
majorMode:
|
|
54
|
+
minimal: [guardrails, team]
|
|
55
|
+
copilot: [guardrails, vibe-lint, team, plan-mode, runner, style-system, ask-user]
|
|
56
|
+
`,"domains.yaml":`# A domain is a named set of plugin directories loaded for a session.
|
|
57
|
+
# Add your own alongside default; aliases map short names onto them.
|
|
58
|
+
# Defaults are a list because one session may combine several domains.
|
|
59
|
+
defaultDomains: [default]
|
|
60
|
+
#
|
|
61
|
+
# Each plugin path is written in full and is relative to the config that
|
|
62
|
+
# declares it, so it resolves beside this file here (~/.pi/.doom) and against
|
|
63
|
+
# the repository root in a repository copy:
|
|
64
|
+
# plugins: ['plugins/my-plugin']
|
|
65
|
+
# A repository's own .doom/domains.yaml is read after this file, replaces any
|
|
66
|
+
# domain of the same name, and can replace defaultDomains.
|
|
67
|
+
domains:
|
|
68
|
+
default:
|
|
69
|
+
plugins: []
|
|
70
|
+
aliases: {}
|
|
71
|
+
`,"profiles.yaml":`profiles: {}
|
|
72
|
+
`,"hooks.yaml":`groups: {}
|
|
73
|
+
`};function u(e){if(!(e instanceof Error))return;let t=e.code;return typeof t==`string`?t:void 0}function d(e){let n;try{n=t.lstatSync(e)}catch(r){if(u(r)!==s)throw r;try{t.mkdirSync(e,{mode:448}),n=t.lstatSync(e)}catch(r){if(u(r)!==o)throw r;n=t.lstatSync(e)}}if(n.isSymbolicLink())throw Error(`Refusing to initialize through symlinked directory: ${e}`);if(!n.isDirectory())throw Error(`Doom config path is not a directory: ${e}`)}function f(e){d(r.dirname(e)),d(e)}function p(e){let n;try{n=t.lstatSync(e)}catch(e){if(u(e)===s)return`missing`;throw e}if(n.isFile())return`file`;if(n.isSymbolicLink())try{if(t.statSync(e).isFile())return`file`}catch(t){throw Error(`Doom config seed symlink is not file-like: ${e}`,{cause:t})}throw Error(`Doom config seed path is not file-like: ${e}`)}function m(e){try{t.unlinkSync(e)}catch(e){if(u(e)!==s)throw e}}function h(e,n){let r=`${e}.${process.pid}.${i()}.tmp`,s;try{s=t.openSync(r,`wx`,384),t.writeFileSync(s,n,a),t.fchmodSync(s,384),t.fsyncSync(s);let i=s;s=void 0,t.closeSync(i);try{return t.linkSync(r,e),!0}catch(t){if(u(t)===o&&p(e)===`file`)return!1;throw t}}finally{if(s!==void 0){let e=s;s=void 0,t.closeSync(e)}m(r)}}function g(e,n){let r=`${e}.${process.pid}.${i()}.tmp`,o;try{o=t.openSync(r,`wx`,384),t.writeFileSync(o,n,a),t.fchmodSync(o,384),t.fsyncSync(o);let i=o;o=void 0,t.closeSync(i),t.renameSync(r,e)}finally{if(o!==void 0){let e=o;o=void 0,t.closeSync(e)}m(r)}}function _(t=n.homedir(),i={}){let a=e(t);f(a);let o=[],s=[],u=[];for(let e of c){let t=r.join(a,e),n=p(t)===`file`;if(i.force){g(t,l[e]),(n?u:o).push(e);continue}if(n){s.push(e);continue}h(t,l[e])?o.push(e):s.push(e)}return{directory:a,created:o,preserved:s,replaced:u}}export{l as DOOM_CONFIG_TEMPLATES,c as GLOBAL_DOOM_SEED_FILES,_ as initializeGlobalDoomConfig};
|
|
74
|
+
//# sourceMappingURL=init.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.mjs","names":[],"sources":["../../src/adapters/init.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { globalDoomConfigDirectory } from './config.ts';\n\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\nconst UTF8_ENCODING = 'utf8';\nconst FILE_ALREADY_EXISTS = 'EEXIST';\nconst FILE_NOT_FOUND = 'ENOENT';\n\n/** The global seed files are processed in this fixed order. */\nexport const GLOBAL_DOOM_SEED_FILES = [\n 'config.yaml',\n 'modes.yaml',\n 'domains.yaml',\n 'profiles.yaml',\n 'hooks.yaml',\n] as const;\n\ntype GlobalDoomSeedFile = (typeof GLOBAL_DOOM_SEED_FILES)[number];\n\n/** Portable defaults for a new global Doom configuration directory. */\nexport const DOOM_CONFIG_TEMPLATES: Readonly<Record<GlobalDoomSeedFile, string>> = {\n 'config.yaml': `projectTrust: ask\n\n# Autonomous voice is opt-in and uses an explicitly configured Pi model.\n# voice:\n# autoCapture:\n# model: provider/model-id\n# startPhrases:\n# - hey doom\n# stopPhrases:\n# - stop speaking\n# utteranceIdleMs: 3000\n# tts:\n# engine: macos-say\n# voice: Samantha\n# rate: 190\n`,\n 'modes.yaml': `# Seeded defaults. Every layer here resolves from what Doom Pi installs or\n# from a public package, so a fresh install works with no extra setup.\n#\n# Layers and modes defined here apply to every repository. A repository's own\n# .doom/modes.yaml is read after this file and replaces any layer or mode of the\n# same name, so a repository can override one entry without restating the rest.\n#\n# A layer package is an npm specifier, or a path if it was never published:\n# packages: ['./extensions/my-ext']\n# A path is relative to the config that declares it, so it resolves beside this\n# file here (~/.pi/.doom) and against the repository root in a repository copy.\nlayers:\n guardrails:\n # repositoryHooks ships with Doom Pi, so the layer steers the agent toward\n # the governing skill and scaffolding before it writes.\n extensions: [repositoryHooks]\n hookGroups: [guardrails, scaffolding]\n vibe-lint:\n packages:\n - '@agimon-ai/vibe-lint/extensions/pi'\n team:\n packages:\n - '@agimon-ai/doompi-team/extensions/doom'\n plan-mode:\n packages:\n - '@agimon-ai/doompi-plan/extensions/doom'\n runner:\n packages:\n - '@agimon-ai/doompi-runner/extensions/pi'\n style-system:\n extensions: [styleSystem]\n ask-user:\n packages:\n - '@juicesharp/rpiv-ask-user-question'\n# A mode is a named set of the layers above. The default keeps its real mode\n# name; it is selected only when neither --major-mode nor DOOMPI_MAJOR_MODE is set.\ndefaultMajorMode: copilot\nmajorMode:\n minimal: [guardrails, team]\n copilot: [guardrails, vibe-lint, team, plan-mode, runner, style-system, ask-user]\n`,\n 'domains.yaml': `# A domain is a named set of plugin directories loaded for a session.\n# Add your own alongside default; aliases map short names onto them.\n# Defaults are a list because one session may combine several domains.\ndefaultDomains: [default]\n#\n# Each plugin path is written in full and is relative to the config that\n# declares it, so it resolves beside this file here (~/.pi/.doom) and against\n# the repository root in a repository copy:\n# plugins: ['plugins/my-plugin']\n# A repository's own .doom/domains.yaml is read after this file, replaces any\n# domain of the same name, and can replace defaultDomains.\ndomains:\n default:\n plugins: []\naliases: {}\n`,\n 'profiles.yaml': 'profiles: {}\\n',\n 'hooks.yaml': 'groups: {}\\n',\n};\n\n/** Result of filling the global seed directory. File arrays contain names in seed order. */\nexport interface GlobalDoomInitResult {\n directory: string;\n created: string[];\n preserved: string[];\n /** Existing files overwritten from the template. Only ever non-empty under `force`. */\n replaced: string[];\n}\n\nexport interface GlobalDoomInitOptions {\n /**\n * Overwrite existing seed files instead of preserving them.\n *\n * Off by default: init is safe to re-run, and hand-edited config is the\n * normal case rather than the exception.\n */\n force?: boolean;\n}\n\nfunction errorCode(error: unknown): string | undefined {\n if (!(error instanceof Error)) return undefined;\n const code = (error as NodeJS.ErrnoException).code;\n return typeof code === 'string' ? code : undefined;\n}\n\nfunction ensureDirectory(directory: string): void {\n let stats: fs.Stats;\n try {\n stats = fs.lstatSync(directory);\n } catch (error) {\n if (errorCode(error) !== FILE_NOT_FOUND) throw error;\n try {\n fs.mkdirSync(directory, { mode: PRIVATE_DIRECTORY_MODE });\n stats = fs.lstatSync(directory);\n } catch (mkdirError) {\n if (errorCode(mkdirError) !== FILE_ALREADY_EXISTS) throw mkdirError;\n stats = fs.lstatSync(directory);\n }\n }\n\n if (stats.isSymbolicLink()) {\n throw new Error(`Refusing to initialize through symlinked directory: ${directory}`);\n }\n if (!stats.isDirectory()) {\n throw new Error(`Doom config path is not a directory: ${directory}`);\n }\n}\n\nfunction ensureGlobalDoomDirectory(directory: string): void {\n ensureDirectory(path.dirname(directory));\n ensureDirectory(directory);\n}\n\nfunction classifyTarget(targetPath: string): 'missing' | 'file' {\n let stats: fs.Stats;\n try {\n stats = fs.lstatSync(targetPath);\n } catch (error) {\n if (errorCode(error) === FILE_NOT_FOUND) return 'missing';\n throw error;\n }\n\n if (stats.isFile()) return 'file';\n if (stats.isSymbolicLink()) {\n try {\n if (fs.statSync(targetPath).isFile()) return 'file';\n } catch (error) {\n throw new Error(`Doom config seed symlink is not file-like: ${targetPath}`, { cause: error });\n }\n }\n throw new Error(`Doom config seed path is not file-like: ${targetPath}`);\n}\n\nfunction cleanupTemporaryFile(temporaryPath: string): void {\n try {\n fs.unlinkSync(temporaryPath);\n } catch (error) {\n if (errorCode(error) !== FILE_NOT_FOUND) throw error;\n }\n}\n\nfunction publishExclusively(targetPath: string, content: string): boolean {\n const temporaryPath = `${targetPath}.${process.pid}.${randomUUID()}.tmp`;\n let fileDescriptor: number | undefined;\n try {\n fileDescriptor = fs.openSync(temporaryPath, 'wx', PRIVATE_FILE_MODE);\n fs.writeFileSync(fileDescriptor, content, UTF8_ENCODING);\n fs.fchmodSync(fileDescriptor, PRIVATE_FILE_MODE);\n fs.fsyncSync(fileDescriptor);\n const descriptorToClose = fileDescriptor;\n fileDescriptor = undefined;\n fs.closeSync(descriptorToClose);\n\n try {\n fs.linkSync(temporaryPath, targetPath);\n return true;\n } catch (error) {\n if (errorCode(error) === FILE_ALREADY_EXISTS && classifyTarget(targetPath) === 'file') return false;\n throw error;\n }\n } finally {\n if (fileDescriptor !== undefined) {\n const descriptorToClose = fileDescriptor;\n fileDescriptor = undefined;\n fs.closeSync(descriptorToClose);\n }\n cleanupTemporaryFile(temporaryPath);\n }\n}\n\n/**\n * Replaces a seed file with the template, atomically.\n *\n * The rename swaps the directory entry in one step, so a reader either sees the\n * whole old file or the whole new one. Renaming onto a symlinked seed replaces\n * the link rather than writing through it.\n */\nfunction publishForcefully(targetPath: string, content: string): void {\n const temporaryPath = `${targetPath}.${process.pid}.${randomUUID()}.tmp`;\n let fileDescriptor: number | undefined;\n try {\n fileDescriptor = fs.openSync(temporaryPath, 'wx', PRIVATE_FILE_MODE);\n fs.writeFileSync(fileDescriptor, content, UTF8_ENCODING);\n fs.fchmodSync(fileDescriptor, PRIVATE_FILE_MODE);\n fs.fsyncSync(fileDescriptor);\n const descriptorToClose = fileDescriptor;\n fileDescriptor = undefined;\n fs.closeSync(descriptorToClose);\n\n fs.renameSync(temporaryPath, targetPath);\n } finally {\n if (fileDescriptor !== undefined) {\n const descriptorToClose = fileDescriptor;\n fileDescriptor = undefined;\n fs.closeSync(descriptorToClose);\n }\n cleanupTemporaryFile(temporaryPath);\n }\n}\n\n/**\n * Fills missing global seed files.\n *\n * Existing files are left alone so that re-running init never discards\n * hand-edited config; pass `force` to overwrite them from the template.\n */\nexport function initializeGlobalDoomConfig(\n homeDirectory = os.homedir(),\n options: GlobalDoomInitOptions = {},\n): GlobalDoomInitResult {\n const directory = globalDoomConfigDirectory(homeDirectory);\n ensureGlobalDoomDirectory(directory);\n\n const created: string[] = [];\n const preserved: string[] = [];\n const replaced: string[] = [];\n for (const fileName of GLOBAL_DOOM_SEED_FILES) {\n const targetPath = path.join(directory, fileName);\n const existed = classifyTarget(targetPath) === 'file';\n\n if (options.force) {\n publishForcefully(targetPath, DOOM_CONFIG_TEMPLATES[fileName]);\n (existed ? replaced : created).push(fileName);\n continue;\n }\n if (existed) {\n preserved.push(fileName);\n continue;\n }\n if (publishExclusively(targetPath, DOOM_CONFIG_TEMPLATES[fileName])) created.push(fileName);\n else preserved.push(fileName);\n }\n\n return { directory, created, preserved, replaced };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../config.cjs");require("../../services/config.cjs");const t=require("../../services/harnessStore.cjs");let n=require("@agimon-ai/doompi-telemetry");const r=`doom-pi:config:v1`,i=new WeakMap;function a(e){if(typeof e!=`object`||!e)return!1;let t=e;return t.version===1&&typeof t.majorMode==`string`&&Array.isArray(t.domains)&&t.domains.every(e=>typeof e==`string`)&&(t.profile===void 0||typeof t.profile==`string`)}function o(e){for(let t=e.length-1;t>=0;--t){let n=e[t];if(n?.type===`custom`&&n.customType===`doom-pi:config:v1`&&a(n.data))return n.data}}function s(e){if(typeof e!=`object`||!e||Object.isFrozen(e))return e;for(let t of Object.values(e))s(t);return Object.freeze(e)}function c(e){return o(e.sessionManager.getBranch())}function l(e){let n=t.getHarnessState(),r=c(e);return{harness:r?{...n,majorMode:r.majorMode,domains:[...r.domains],profile:r.profile}:n,requiresRelaunch:!!(r&&(r.majorMode!==n.majorMode||r.profile!==n.profile||r.domains.join(`,`)!==n.domains.join(`,`))),root:n.root}}function u(t){let{harness:n,requiresRelaunch:r,root:i}=l(t);return s({settings:e.loadDoomConfig(i??t.cwd),harness:n,requiresRelaunch:r})}async function d(t){let{harness:n,requiresRelaunch:r,root:i}=l(t);return s({settings:await e.loadDoomConfigAsync(i??t.cwd),harness:n,requiresRelaunch:r})}function f(e,t){e.appendEntry(r,t)}function p(e,t){let n={context:t,generation:Symbol(`doom-config-context`)};return i.set(e.sessionManager,n),()=>{i.get(e.sessionManager)?.generation===n.generation&&i.delete(e.sessionManager)}}function m(e){let t=i.get(e.sessionManager);if(!t)throw Error(`Doom config context is unavailable. Load @agimon-ai/doompi-config/pi.`);return t.context}const h=e=>{let t,r;e.on(`session_start`,async(e,i)=>{t?.();let a=await d(i);t=p(i,a),r??=(0,n.createDoomTelemetry)({serviceName:`doom-config`,packageName:`@agimon-ai/doompi-config`,cwd:i.cwd,env:process.env,enableLogs:!0,enableTraces:!0}),r.recordEvent(`doom_config.context_bound`,{"config.major_mode":a.harness.majorMode,"config.domain_count":a.harness.domains.length,"config.has_profile":a.harness.profile!==void 0,"config.requires_relaunch":a.requiresRelaunch})}),e.on(`session_shutdown`,async()=>{t=void 0,await r?.shutdown(),r=void 0})};exports.DOOM_CONFIG_ENTRY_TYPE=r,exports.appendDoomConfigSelection=f,exports.createDoomConfigContext=u,exports.createDoomConfigContextAsync=d,exports.doomConfigExtension=h,exports.provideDoomConfigContext=p,exports.readDoomConfigSelection=c,exports.requireDoomConfigContext=m;
|
|
2
|
+
//# sourceMappingURL=piContext.cjs.map
|