@agimon-ai/doompi-config 0.0.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +36 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.cjs +1 -0
  4. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.mjs +1 -0
  5. package/dist/_virtual/_rolldown/runtime.cjs +1 -0
  6. package/dist/atomicJson.cjs +2 -0
  7. package/dist/atomicJson.cjs.map +1 -0
  8. package/dist/atomicJson.d.cts +6 -0
  9. package/dist/atomicJson.d.cts.map +1 -0
  10. package/dist/atomicJson.d.mts +6 -0
  11. package/dist/atomicJson.d.mts.map +1 -0
  12. package/dist/atomicJson.mjs +2 -0
  13. package/dist/atomicJson.mjs.map +1 -0
  14. package/dist/config/piConfig.cjs +2 -0
  15. package/dist/config/piConfig.cjs.map +1 -0
  16. package/dist/config/piConfig.d.cts +7 -0
  17. package/dist/config/piConfig.d.cts.map +1 -0
  18. package/dist/config/piConfig.d.mts +7 -0
  19. package/dist/config/piConfig.d.mts.map +1 -0
  20. package/dist/config/piConfig.mjs +2 -0
  21. package/dist/config/piConfig.mjs.map +1 -0
  22. package/dist/config/schema.cjs +0 -0
  23. package/dist/config/schema.d.cts +33 -0
  24. package/dist/config/schema.d.cts.map +1 -0
  25. package/dist/config/schema.d.mts +33 -0
  26. package/dist/config/schema.d.mts.map +1 -0
  27. package/dist/config/schema.mjs +1 -0
  28. package/dist/config.cjs +2 -0
  29. package/dist/config.cjs.map +1 -0
  30. package/dist/config.d.cts +24 -0
  31. package/dist/config.d.cts.map +1 -0
  32. package/dist/config.d.mts +24 -0
  33. package/dist/config.d.mts.map +1 -0
  34. package/dist/config.mjs +2 -0
  35. package/dist/config.mjs.map +1 -0
  36. package/dist/configWriter.cjs +2 -0
  37. package/dist/configWriter.cjs.map +1 -0
  38. package/dist/configWriter.d.cts +52 -0
  39. package/dist/configWriter.d.cts.map +1 -0
  40. package/dist/configWriter.d.mts +52 -0
  41. package/dist/configWriter.d.mts.map +1 -0
  42. package/dist/configWriter.mjs +2 -0
  43. package/dist/configWriter.mjs.map +1 -0
  44. package/dist/container.cjs +2 -0
  45. package/dist/container.cjs.map +1 -0
  46. package/dist/container.d.cts +12 -0
  47. package/dist/container.d.cts.map +1 -0
  48. package/dist/container.d.mts +12 -0
  49. package/dist/container.d.mts.map +1 -0
  50. package/dist/container.mjs +2 -0
  51. package/dist/container.mjs.map +1 -0
  52. package/dist/domains.cjs +2 -0
  53. package/dist/domains.cjs.map +1 -0
  54. package/dist/domains.d.cts +75 -0
  55. package/dist/domains.d.cts.map +1 -0
  56. package/dist/domains.d.mts +75 -0
  57. package/dist/domains.d.mts.map +1 -0
  58. package/dist/domains.mjs +2 -0
  59. package/dist/domains.mjs.map +1 -0
  60. package/dist/extensions/doom.cjs +2 -0
  61. package/dist/extensions/doom.cjs.map +1 -0
  62. package/dist/extensions/doom.d.cts +8 -0
  63. package/dist/extensions/doom.d.cts.map +1 -0
  64. package/dist/extensions/doom.d.mts +8 -0
  65. package/dist/extensions/doom.d.mts.map +1 -0
  66. package/dist/extensions/doom.mjs +2 -0
  67. package/dist/extensions/doom.mjs.map +1 -0
  68. package/dist/extensions/pi.cjs +2 -0
  69. package/dist/extensions/pi.cjs.map +1 -0
  70. package/dist/extensions/pi.d.cts +8 -0
  71. package/dist/extensions/pi.d.cts.map +1 -0
  72. package/dist/extensions/pi.d.mts +8 -0
  73. package/dist/extensions/pi.d.mts.map +1 -0
  74. package/dist/extensions/pi.mjs +2 -0
  75. package/dist/extensions/pi.mjs.map +1 -0
  76. package/dist/harnessState.cjs +2 -0
  77. package/dist/harnessState.cjs.map +1 -0
  78. package/dist/harnessState.d.cts +50 -0
  79. package/dist/harnessState.d.cts.map +1 -0
  80. package/dist/harnessState.d.mts +50 -0
  81. package/dist/harnessState.d.mts.map +1 -0
  82. package/dist/harnessState.mjs +2 -0
  83. package/dist/harnessState.mjs.map +1 -0
  84. package/dist/harnessStore.cjs +2 -0
  85. package/dist/harnessStore.cjs.map +1 -0
  86. package/dist/harnessStore.d.cts +102 -0
  87. package/dist/harnessStore.d.cts.map +1 -0
  88. package/dist/harnessStore.d.mts +102 -0
  89. package/dist/harnessStore.d.mts.map +1 -0
  90. package/dist/harnessStore.mjs +2 -0
  91. package/dist/harnessStore.mjs.map +1 -0
  92. package/dist/index.cjs +1 -0
  93. package/dist/index.d.cts +16 -0
  94. package/dist/index.d.mts +16 -0
  95. package/dist/index.mjs +1 -0
  96. package/dist/init.cjs +73 -0
  97. package/dist/init.cjs.map +1 -0
  98. package/dist/init.d.cts +33 -0
  99. package/dist/init.d.cts.map +1 -0
  100. package/dist/init.d.mts +33 -0
  101. package/dist/init.d.mts.map +1 -0
  102. package/dist/init.mjs +73 -0
  103. package/dist/init.mjs.map +1 -0
  104. package/dist/layeredConfig.cjs +2 -0
  105. package/dist/layeredConfig.cjs.map +1 -0
  106. package/dist/layeredConfig.d.cts +49 -0
  107. package/dist/layeredConfig.d.cts.map +1 -0
  108. package/dist/layeredConfig.d.mts +49 -0
  109. package/dist/layeredConfig.d.mts.map +1 -0
  110. package/dist/layeredConfig.mjs +2 -0
  111. package/dist/layeredConfig.mjs.map +1 -0
  112. package/dist/majorModes.cjs +2 -0
  113. package/dist/majorModes.cjs.map +1 -0
  114. package/dist/majorModes.d.cts +73 -0
  115. package/dist/majorModes.d.cts.map +1 -0
  116. package/dist/majorModes.d.mts +73 -0
  117. package/dist/majorModes.d.mts.map +1 -0
  118. package/dist/majorModes.mjs +2 -0
  119. package/dist/majorModes.mjs.map +1 -0
  120. package/dist/pi.cjs +1 -0
  121. package/dist/pi.d.cts +2 -0
  122. package/dist/pi.d.mts +2 -0
  123. package/dist/pi.mjs +1 -0
  124. package/dist/piContext.cjs +2 -0
  125. package/dist/piContext.cjs.map +1 -0
  126. package/dist/piContext.d.cts +19 -0
  127. package/dist/piContext.d.cts.map +1 -0
  128. package/dist/piContext.d.mts +19 -0
  129. package/dist/piContext.d.mts.map +1 -0
  130. package/dist/piContext.mjs +2 -0
  131. package/dist/piContext.mjs.map +1 -0
  132. package/dist/profiles.cjs +4 -0
  133. package/dist/profiles.cjs.map +1 -0
  134. package/dist/profiles.d.cts +35 -0
  135. package/dist/profiles.d.cts.map +1 -0
  136. package/dist/profiles.d.mts +35 -0
  137. package/dist/profiles.d.mts.map +1 -0
  138. package/dist/profiles.mjs +4 -0
  139. package/dist/profiles.mjs.map +1 -0
  140. package/dist/types.cjs +2 -0
  141. package/dist/types.cjs.map +1 -0
  142. package/dist/types.d.cts +132 -0
  143. package/dist/types.d.cts.map +1 -0
  144. package/dist/types.d.mts +132 -0
  145. package/dist/types.d.mts.map +1 -0
  146. package/dist/types.mjs +2 -0
  147. package/dist/types.mjs.map +1 -0
  148. package/package.json +150 -0
  149. package/skills/doom-pi-config/SKILL.md +26 -0
  150. package/skills/doom-pi-config/agents/openai.yaml +4 -0
  151. package/skills/doom-pi-config/references/config-contract.md +43 -0
@@ -0,0 +1,50 @@
1
+ import { HarnessState } from "./types.cjs";
2
+ //#region src/harnessState.d.ts
3
+ /**
4
+ * State that lives in the file only.
5
+ *
6
+ * Both are JSON documents that every spawned process and every hook would
7
+ * otherwise copy in its environment, and neither has a reader outside this
8
+ * package: the plugin hook list is consumed by the Pi hooks extension and the
9
+ * profile environment by the profile switcher, both of which read the store.
10
+ */
11
+ declare const FILE_ONLY_STATE_FIELDS: ReadonlySet<keyof HarnessState>;
12
+ type HarnessStateParseReporter = (key: string, error: unknown) => void;
13
+ declare const HARNESS_STATE_KEYS: {
14
+ readonly root: "DOOMPI_ROOT";
15
+ readonly majorMode: "DOOMPI_MAJOR_MODE";
16
+ readonly temporaryDirectory: "DOOMPI_TEMP_DIR";
17
+ readonly domains: "DOOMPI_DOMAINS";
18
+ readonly layers: "DOOMPI_LAYERS";
19
+ readonly profile: "DOOMPI_PROFILE";
20
+ readonly profileEnvironment: "DOOMPI_PROFILE_ENV";
21
+ readonly hookGroups: "DOOMPI_HOOK_GROUPS";
22
+ readonly skillDirectories: "DOOMPI_SKILL_DIRS";
23
+ readonly agentDirectories: "DOOMPI_AGENT_DIRS";
24
+ readonly additionalDirectories: "DOOMPI_ADDITIONAL_DIRS";
25
+ readonly childExtensions: "DOOMPI_CHILD_EXTENSIONS";
26
+ readonly pluginDirectories: "DOOMPI_PLUGIN_DIRS";
27
+ readonly pluginHooks: "DOOMPI_PLUGIN_HOOKS";
28
+ readonly mcpConfigPath: "DOOMPI_MCP_CONFIG";
29
+ readonly personaFile: "DOOMPI_PERSONA_FILE";
30
+ readonly workflowExtension: "DOOMPI_WORKFLOW_EXTENSION";
31
+ readonly hooks: "DOOMPI_HOOKS_ENABLED";
32
+ readonly agents: "DOOMPI_AGENTS_ENABLED";
33
+ readonly mcp: "DOOMPI_MCP_ENABLED";
34
+ readonly allowProtectedWrites: "DOOMPI_ALLOW_PROTECTED_WRITES";
35
+ };
36
+ declare function readHarnessState(environment?: NodeJS.ProcessEnv, report?: HarnessStateParseReporter): HarnessState;
37
+ /**
38
+ * Publishes a state patch into an environment.
39
+ *
40
+ * Takes the environment rather than assuming `process.env`, because the
41
+ * launcher builds a child environment and Doom Team builds a spawn snapshot;
42
+ * neither should mutate its own process to describe another one.
43
+ *
44
+ * Fields in FILE_ONLY_FIELDS are skipped: the file carries them, and a reader
45
+ * that needs them reads the store.
46
+ */
47
+ declare function projectHarnessEnvironment(patch: Partial<HarnessState>, environment?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
48
+ //#endregion
49
+ export { FILE_ONLY_STATE_FIELDS, HARNESS_STATE_KEYS, HarnessStateParseReporter, projectHarnessEnvironment, readHarnessState };
50
+ //# sourceMappingURL=harnessState.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessState.d.cts","names":[],"sources":["../src/harnessState.ts"],"mappings":";;;;;;;;;;cAwBa,wBAAwB,kBAAkB;KAE3C,6BAA6B,aAAa;cAEzC;;;;;;;;;;;;;;;;;;;;;;;iBAiGG,iBACd,cAAa,OAAO,YACpB,SAAS,4BACR;;;;;;;;;;;iBAsCa,0BACd,OAAO,QAAQ,eACf,cAAa,OAAO,aACnB,OAAO"}
@@ -0,0 +1,50 @@
1
+ import { HarnessState } from "./types.mjs";
2
+ //#region src/harnessState.d.ts
3
+ /**
4
+ * State that lives in the file only.
5
+ *
6
+ * Both are JSON documents that every spawned process and every hook would
7
+ * otherwise copy in its environment, and neither has a reader outside this
8
+ * package: the plugin hook list is consumed by the Pi hooks extension and the
9
+ * profile environment by the profile switcher, both of which read the store.
10
+ */
11
+ declare const FILE_ONLY_STATE_FIELDS: ReadonlySet<keyof HarnessState>;
12
+ type HarnessStateParseReporter = (key: string, error: unknown) => void;
13
+ declare const HARNESS_STATE_KEYS: {
14
+ readonly root: "DOOMPI_ROOT";
15
+ readonly majorMode: "DOOMPI_MAJOR_MODE";
16
+ readonly temporaryDirectory: "DOOMPI_TEMP_DIR";
17
+ readonly domains: "DOOMPI_DOMAINS";
18
+ readonly layers: "DOOMPI_LAYERS";
19
+ readonly profile: "DOOMPI_PROFILE";
20
+ readonly profileEnvironment: "DOOMPI_PROFILE_ENV";
21
+ readonly hookGroups: "DOOMPI_HOOK_GROUPS";
22
+ readonly skillDirectories: "DOOMPI_SKILL_DIRS";
23
+ readonly agentDirectories: "DOOMPI_AGENT_DIRS";
24
+ readonly additionalDirectories: "DOOMPI_ADDITIONAL_DIRS";
25
+ readonly childExtensions: "DOOMPI_CHILD_EXTENSIONS";
26
+ readonly pluginDirectories: "DOOMPI_PLUGIN_DIRS";
27
+ readonly pluginHooks: "DOOMPI_PLUGIN_HOOKS";
28
+ readonly mcpConfigPath: "DOOMPI_MCP_CONFIG";
29
+ readonly personaFile: "DOOMPI_PERSONA_FILE";
30
+ readonly workflowExtension: "DOOMPI_WORKFLOW_EXTENSION";
31
+ readonly hooks: "DOOMPI_HOOKS_ENABLED";
32
+ readonly agents: "DOOMPI_AGENTS_ENABLED";
33
+ readonly mcp: "DOOMPI_MCP_ENABLED";
34
+ readonly allowProtectedWrites: "DOOMPI_ALLOW_PROTECTED_WRITES";
35
+ };
36
+ declare function readHarnessState(environment?: NodeJS.ProcessEnv, report?: HarnessStateParseReporter): HarnessState;
37
+ /**
38
+ * Publishes a state patch into an environment.
39
+ *
40
+ * Takes the environment rather than assuming `process.env`, because the
41
+ * launcher builds a child environment and Doom Team builds a spawn snapshot;
42
+ * neither should mutate its own process to describe another one.
43
+ *
44
+ * Fields in FILE_ONLY_FIELDS are skipped: the file carries them, and a reader
45
+ * that needs them reads the store.
46
+ */
47
+ declare function projectHarnessEnvironment(patch: Partial<HarnessState>, environment?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
48
+ //#endregion
49
+ export { FILE_ONLY_STATE_FIELDS, HARNESS_STATE_KEYS, HarnessStateParseReporter, projectHarnessEnvironment, readHarnessState };
50
+ //# sourceMappingURL=harnessState.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessState.d.mts","names":[],"sources":["../src/harnessState.ts"],"mappings":";;;;;;;;;;cAwBa,wBAAwB,kBAAkB;KAE3C,6BAA6B,aAAa;cAEzC;;;;;;;;;;;;;;;;;;;;;;;iBAiGG,iBACd,cAAa,OAAO,YACpB,SAAS,4BACR;;;;;;;;;;;iBAsCa,0BACd,OAAO,QAAQ,eACf,cAAa,OAAO,aACnB,OAAO"}
@@ -0,0 +1,2 @@
1
+ import e from"node:path";const t=new Set([`profileEnvironment`,`pluginHooks`]),n={root:`DOOMPI_ROOT`,majorMode:`DOOMPI_MAJOR_MODE`,temporaryDirectory:`DOOMPI_TEMP_DIR`,domains:`DOOMPI_DOMAINS`,layers:`DOOMPI_LAYERS`,profile:`DOOMPI_PROFILE`,profileEnvironment:`DOOMPI_PROFILE_ENV`,hookGroups:`DOOMPI_HOOK_GROUPS`,skillDirectories:`DOOMPI_SKILL_DIRS`,agentDirectories:`DOOMPI_AGENT_DIRS`,additionalDirectories:`DOOMPI_ADDITIONAL_DIRS`,childExtensions:`DOOMPI_CHILD_EXTENSIONS`,pluginDirectories:`DOOMPI_PLUGIN_DIRS`,pluginHooks:`DOOMPI_PLUGIN_HOOKS`,mcpConfigPath:`DOOMPI_MCP_CONFIG`,personaFile:`DOOMPI_PERSONA_FILE`,workflowExtension:`DOOMPI_WORKFLOW_EXTENSION`,hooks:`DOOMPI_HOOKS_ENABLED`,agents:`DOOMPI_AGENTS_ENABLED`,mcp:`DOOMPI_MCP_ENABLED`,allowProtectedWrites:`DOOMPI_ALLOW_PROTECTED_WRITES`};function r(e){return e?.split(`,`).map(e=>e.trim()).filter(Boolean)??[]}function i(t){return t?.split(e.delimiter).map(e=>e.trim()).filter(Boolean)??[]}function a(e){return e!==`0`}function o(e,t,n){if(!e)return{};try{let t=JSON.parse(e);return typeof t!=`object`||!t||Array.isArray(t)?{}:Object.fromEntries(Object.entries(t).filter(e=>typeof e[1]==`string`))}catch(e){return n?.(t,e),{}}}function s(e,t,n){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`string`):[]}catch(e){return n?.(t,e),[]}}function c(e,t,n){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`object`&&!!e&&typeof e.pluginRoot==`string`&&typeof e.configPath==`string`):[]}catch(e){return n?.(t,e),[]}}function l(e=process.env,t){let l=n,u=e[l.hookGroups];return{root:e[l.root]||void 0,majorMode:e[l.majorMode]||`copilot`,temporaryDirectory:e[l.temporaryDirectory]||void 0,domains:r(e[l.domains]),layers:r(e[l.layers]),profile:e[l.profile]||void 0,profileEnvironment:o(e[l.profileEnvironment],l.profileEnvironment,t),hookGroups:u===void 0?void 0:r(u),skillDirectories:i(e[l.skillDirectories]),agentDirectories:i(e[l.agentDirectories]),additionalDirectories:i(e[l.additionalDirectories]),childExtensions:s(e[l.childExtensions],l.childExtensions,t),pluginDirectories:i(e[l.pluginDirectories]),pluginHooks:c(e[l.pluginHooks],l.pluginHooks,t),mcpConfigPath:e[l.mcpConfigPath]||void 0,personaFile:e[l.personaFile]||void 0,workflowExtension:e[l.workflowExtension]||void 0,hooks:a(e[l.hooks]),agents:a(e[l.agents]),mcp:a(e[l.mcp]),allowProtectedWrites:a(e[l.allowProtectedWrites])}}function u(t,r=process.env){let i=(e,t)=>{t===void 0?delete r[e]:r[e]=t},a=(e,t)=>i(e,t?`1`:`0`),o=n;return`root`in t&&i(o.root,t.root),`majorMode`in t&&i(o.majorMode,t.majorMode),`temporaryDirectory`in t&&i(o.temporaryDirectory,t.temporaryDirectory),`domains`in t&&i(o.domains,t.domains?.join(`,`)),`layers`in t&&i(o.layers,t.layers?.join(`,`)),`profile`in t&&i(o.profile,t.profile),`hookGroups`in t&&i(o.hookGroups,t.hookGroups?.join(`,`)),`skillDirectories`in t&&i(o.skillDirectories,t.skillDirectories?.join(e.delimiter)),`agentDirectories`in t&&i(o.agentDirectories,t.agentDirectories?.join(e.delimiter)),`additionalDirectories`in t&&i(o.additionalDirectories,t.additionalDirectories?.join(e.delimiter)),`childExtensions`in t&&i(o.childExtensions,JSON.stringify(t.childExtensions??[])),`pluginDirectories`in t&&i(o.pluginDirectories,t.pluginDirectories?.join(e.delimiter)),`mcpConfigPath`in t&&i(o.mcpConfigPath,t.mcpConfigPath),`personaFile`in t&&i(o.personaFile,t.personaFile),`workflowExtension`in t&&i(o.workflowExtension,t.workflowExtension),t.hooks!==void 0&&a(o.hooks,t.hooks),t.agents!==void 0&&a(o.agents,t.agents),t.mcp!==void 0&&a(o.mcp,t.mcp),t.allowProtectedWrites!==void 0&&a(o.allowProtectedWrites,t.allowProtectedWrites),r}export{t as FILE_ONLY_STATE_FIELDS,n as HARNESS_STATE_KEYS,u as projectHarnessEnvironment,l as readHarnessState};
2
+ //# sourceMappingURL=harnessState.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessState.mjs","names":[],"sources":["../src/harnessState.ts"],"sourcesContent":["import path from 'node:path';\nimport type { HarnessState, PluginHookSource } from './types.ts';\n\n/**\n * The codec between harness state and the environment.\n *\n * Decoding exists for processes that were started without a state file: a\n * nested run, a third-party spawn, or anything older than the store. Encoding\n * exists because bash hooks, the shell launchers, and packages that do not\n * depend on this one can only read an environment. Ownership, persistence and\n * mutation belong to harnessStore.ts; nothing here touches disk or holds state.\n */\n\nconst DEFAULT_MAJOR_MODE = 'copilot';\nconst DISABLED_FLAG = '0';\n\n/**\n * State that lives in the file only.\n *\n * Both are JSON documents that every spawned process and every hook would\n * otherwise copy in its environment, and neither has a reader outside this\n * package: the plugin hook list is consumed by the Pi hooks extension and the\n * profile environment by the profile switcher, both of which read the store.\n */\nexport const FILE_ONLY_STATE_FIELDS: ReadonlySet<keyof HarnessState> = new Set(['profileEnvironment', 'pluginHooks']);\n\nexport type HarnessStateParseReporter = (key: string, error: unknown) => void;\n\nexport const HARNESS_STATE_KEYS = {\n root: 'DOOMPI_ROOT',\n majorMode: 'DOOMPI_MAJOR_MODE',\n temporaryDirectory: 'DOOMPI_TEMP_DIR',\n domains: 'DOOMPI_DOMAINS',\n layers: 'DOOMPI_LAYERS',\n profile: 'DOOMPI_PROFILE',\n profileEnvironment: 'DOOMPI_PROFILE_ENV',\n hookGroups: 'DOOMPI_HOOK_GROUPS',\n skillDirectories: 'DOOMPI_SKILL_DIRS',\n agentDirectories: 'DOOMPI_AGENT_DIRS',\n additionalDirectories: 'DOOMPI_ADDITIONAL_DIRS',\n childExtensions: 'DOOMPI_CHILD_EXTENSIONS',\n pluginDirectories: 'DOOMPI_PLUGIN_DIRS',\n pluginHooks: 'DOOMPI_PLUGIN_HOOKS',\n mcpConfigPath: 'DOOMPI_MCP_CONFIG',\n personaFile: 'DOOMPI_PERSONA_FILE',\n workflowExtension: 'DOOMPI_WORKFLOW_EXTENSION',\n hooks: 'DOOMPI_HOOKS_ENABLED',\n agents: 'DOOMPI_AGENTS_ENABLED',\n mcp: 'DOOMPI_MCP_ENABLED',\n allowProtectedWrites: 'DOOMPI_ALLOW_PROTECTED_WRITES',\n} as const;\n\nfunction splitCsv(value: string | undefined): string[] {\n return (\n value\n ?.split(',')\n .map((item) => item.trim())\n .filter(Boolean) ?? []\n );\n}\n\nfunction splitPaths(value: string | undefined): string[] {\n return (\n value\n ?.split(path.delimiter)\n .map((item) => item.trim())\n .filter(Boolean) ?? []\n );\n}\n\nfunction parseFlag(value: string | undefined): boolean {\n return value !== DISABLED_FLAG;\n}\n\nfunction parseStringRecord(\n value: string | undefined,\n key: string,\n report?: HarnessStateParseReporter,\n): Record<string, string> {\n if (!value) return {};\n try {\n const parsed = JSON.parse(value) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return {};\n return Object.fromEntries(\n Object.entries(parsed).filter((entry): entry is [string, string] => typeof entry[1] === 'string'),\n );\n } catch (error) {\n report?.(key, error);\n return {};\n }\n}\n\nfunction parseStringList(value: string | undefined, key: string, report?: HarnessStateParseReporter): string[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value) as unknown;\n return Array.isArray(parsed) ? parsed.filter((item): item is string => typeof item === 'string') : [];\n } catch (error) {\n report?.(key, error);\n return [];\n }\n}\n\nfunction parsePluginHooks(\n value: string | undefined,\n key: string,\n report?: HarnessStateParseReporter,\n): PluginHookSource[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value) as unknown;\n if (!Array.isArray(parsed)) return [];\n return parsed.filter(\n (item): item is PluginHookSource =>\n typeof item === 'object' &&\n item !== null &&\n typeof (item as PluginHookSource).pluginRoot === 'string' &&\n typeof (item as PluginHookSource).configPath === 'string',\n );\n } catch (error) {\n report?.(key, error);\n return [];\n }\n}\n\nexport function readHarnessState(\n environment: NodeJS.ProcessEnv = process.env,\n report?: HarnessStateParseReporter,\n): HarnessState {\n const keys = HARNESS_STATE_KEYS;\n const hookGroups = environment[keys.hookGroups];\n return {\n root: environment[keys.root] || undefined,\n majorMode: environment[keys.majorMode] || DEFAULT_MAJOR_MODE,\n temporaryDirectory: environment[keys.temporaryDirectory] || undefined,\n domains: splitCsv(environment[keys.domains]),\n layers: splitCsv(environment[keys.layers]),\n profile: environment[keys.profile] || undefined,\n profileEnvironment: parseStringRecord(environment[keys.profileEnvironment], keys.profileEnvironment, report),\n hookGroups: hookGroups === undefined ? undefined : splitCsv(hookGroups),\n skillDirectories: splitPaths(environment[keys.skillDirectories]),\n agentDirectories: splitPaths(environment[keys.agentDirectories]),\n additionalDirectories: splitPaths(environment[keys.additionalDirectories]),\n childExtensions: parseStringList(environment[keys.childExtensions], keys.childExtensions, report),\n pluginDirectories: splitPaths(environment[keys.pluginDirectories]),\n pluginHooks: parsePluginHooks(environment[keys.pluginHooks], keys.pluginHooks, report),\n mcpConfigPath: environment[keys.mcpConfigPath] || undefined,\n personaFile: environment[keys.personaFile] || undefined,\n workflowExtension: environment[keys.workflowExtension] || undefined,\n hooks: parseFlag(environment[keys.hooks]),\n agents: parseFlag(environment[keys.agents]),\n mcp: parseFlag(environment[keys.mcp]),\n allowProtectedWrites: parseFlag(environment[keys.allowProtectedWrites]),\n };\n}\n\n/**\n * Publishes a state patch into an environment.\n *\n * Takes the environment rather than assuming `process.env`, because the\n * launcher builds a child environment and Doom Team builds a spawn snapshot;\n * neither should mutate its own process to describe another one.\n *\n * Fields in FILE_ONLY_FIELDS are skipped: the file carries them, and a reader\n * that needs them reads the store.\n */\nexport function projectHarnessEnvironment(\n patch: Partial<HarnessState>,\n environment: NodeJS.ProcessEnv = process.env,\n): NodeJS.ProcessEnv {\n const assign = (key: string, value: string | undefined): void => {\n if (value === undefined) delete environment[key];\n else environment[key] = value;\n };\n const assignFlag = (key: string, value: boolean): void => assign(key, value ? '1' : DISABLED_FLAG);\n const keys = HARNESS_STATE_KEYS;\n\n if ('root' in patch) assign(keys.root, patch.root);\n if ('majorMode' in patch) assign(keys.majorMode, patch.majorMode);\n if ('temporaryDirectory' in patch) assign(keys.temporaryDirectory, patch.temporaryDirectory);\n if ('domains' in patch) assign(keys.domains, patch.domains?.join(','));\n if ('layers' in patch) assign(keys.layers, patch.layers?.join(','));\n if ('profile' in patch) assign(keys.profile, patch.profile);\n if ('hookGroups' in patch) assign(keys.hookGroups, patch.hookGroups?.join(','));\n if ('skillDirectories' in patch) assign(keys.skillDirectories, patch.skillDirectories?.join(path.delimiter));\n if ('agentDirectories' in patch) assign(keys.agentDirectories, patch.agentDirectories?.join(path.delimiter));\n if ('additionalDirectories' in patch) {\n assign(keys.additionalDirectories, patch.additionalDirectories?.join(path.delimiter));\n }\n if ('childExtensions' in patch) assign(keys.childExtensions, JSON.stringify(patch.childExtensions ?? []));\n if ('pluginDirectories' in patch) assign(keys.pluginDirectories, patch.pluginDirectories?.join(path.delimiter));\n if ('mcpConfigPath' in patch) assign(keys.mcpConfigPath, patch.mcpConfigPath);\n if ('personaFile' in patch) assign(keys.personaFile, patch.personaFile);\n if ('workflowExtension' in patch) assign(keys.workflowExtension, patch.workflowExtension);\n if (patch.hooks !== undefined) assignFlag(keys.hooks, patch.hooks);\n if (patch.agents !== undefined) assignFlag(keys.agents, patch.agents);\n if (patch.mcp !== undefined) assignFlag(keys.mcp, patch.mcp);\n if (patch.allowProtectedWrites !== undefined) assignFlag(keys.allowProtectedWrites, patch.allowProtectedWrites);\n return environment;\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./_virtual/_rolldown/runtime.cjs"),t=require("./atomicJson.cjs"),n=require("./harnessState.cjs");let r=require("node:fs");r=e.__toESM(r,1);let i=require("node:os");i=e.__toESM(i,1);let a=require("node:path");a=e.__toESM(a,1);const o=`DOOMPI_STATE`;let s;function c(){s=void 0}function l(e,t){let n=JSON.parse(e);if(n.version!==1)throw Error(`Harness state at ${t} has version ${String(n.version)}, expected 1`);if(typeof n.state!=`object`||n.state===null)throw Error(`Harness state at ${t} has no state object`);return{version:1,owner:{pid:n.owner?.pid??-1,startedAt:n.owner?.startedAt??``},state:n.state}}function u(e=process.env,t){let i=e===process.env;if(i&&s)return s;let a=e[o],c;if(a&&r.default.existsSync(a))try{let e=l(r.default.readFileSync(a,`utf8`),a);c={filePath:a,owned:e.owner.pid===process.pid||e.owner.pid===0,state:e.state}}catch(e){t?.(o,e)}return c??={owned:!1,state:n.readHarnessState(e,t)},i&&(s=c),c}function d(e){return u(process.env,e).state}function f(e){return a.default.join(e,`harness-state.json`)}function p(){return a.default.join(i.default.tmpdir(),`doom-harness`,`${String(process.pid)}.json`)}function m(e,n,r=process.pid){t.writePrivateAtomicJson(e,{version:1,owner:{pid:r,startedAt:new Date().toISOString()},state:n})}function h(e,t){let r=f(t.directory);return m(r,e,t.unclaimed?0:process.pid),t.environment[o]=r,n.projectHarnessEnvironment(e,t.environment),!t.unclaimed&&t.environment===process.env&&(s={filePath:r,owned:!0,state:e}),r}function g(e,t=process.env){let r=u(t),i={...r.state,...e},a=r.owned&&r.filePath?r.filePath:p();return m(a,i),t[o]=a,t===process.env&&(s={filePath:a,owned:!0,state:i}),n.projectHarnessEnvironment(e,t),i}function _(e=process.env){let t=s;c(),!(!t?.owned||!t.filePath)&&(r.default.rmSync(t.filePath,{force:!0}),delete e[o])}function v(e=d()){return e.root??process.cwd()}function y(e=d()){if(!e.root)throw Error(`${n.HARNESS_STATE_KEYS.root} is not set`);return e.root}function b(e=d()){let t=y(e);if(!e.temporaryDirectory)throw Error(`${n.HARNESS_STATE_KEYS.temporaryDirectory} is not set`);return{root:t,temporaryDirectory:e.temporaryDirectory}}exports.HARNESS_STATE_POINTER=o,exports.createHarnessSession=h,exports.disposeHarnessState=_,exports.getHarnessState=d,exports.harnessRoot=v,exports.loadHarnessState=u,exports.requireHarnessPaths=b,exports.requireHarnessRoot=y,exports.resetHarnessStore=c,exports.updateHarnessState=g;
2
+ //# sourceMappingURL=harnessStore.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessStore.cjs","names":["fs","readHarnessState","path","os","HARNESS_STATE_KEYS"],"sources":["../src/harnessStore.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { writePrivateAtomicJson } from './atomicJson.ts';\nimport {\n HARNESS_STATE_KEYS,\n projectHarnessEnvironment,\n readHarnessState,\n type HarnessStateParseReporter,\n} from './harnessState.ts';\nimport type { HarnessState } from './types.ts';\n\n/**\n * Who owns the harness state, and where it lives.\n *\n * The state used to be the environment: twenty variables that every reader\n * re-parsed and every writer mutated globally. The file is the authority now,\n * and the environment is a projection published for the readers that can only\n * see an environment, which are bash hooks, the shell launchers, packages that\n * do not depend on this one, and any process spawned by any of them.\n *\n * OWNERSHIP IS PER PROCESS, AND THAT IS THE WHOLE DESIGN:\n * Doom Team subagents run detached, in their own process group, from an\n * environment snapshot taken at spawn. A child can outlive its parent. If it\n * shared the parent's file it would corrupt a session it does not own, and if\n * that file sat in the parent's run directory the parent's shutdown sweep would\n * delete it from under a live run. So every process writes a file it owns:\n * `owner.pid` records who, and a process that finds someone else's file copies\n * it before its first write.\n *\n * AVOID:\n * - Treating the file as shared mutable state between processes. Atomic writes\n * prevent torn reads, not lost updates\n * - Reading the environment directly for anything this exposes\n */\n\n/** Points at the state file. The one variable that is not derived. */\nexport const HARNESS_STATE_POINTER = 'DOOMPI_STATE';\n\nconst STATE_FILE_VERSION = 1;\nconst STATE_FILE_NAME = 'harness-state.json';\nconst ADOPTED_DIRECTORY = 'doom-harness';\n/**\n * Written for a process that does not exist yet, so the first one to read it\n * owns it. A spawner cannot know its child's pid, and a child that inherited\n * its parent's file would lose it the moment the parent cleaned up.\n */\nconst UNCLAIMED_OWNER = 0;\n\nexport interface HarnessStateFile {\n version: number;\n owner: { pid: number; startedAt: string };\n state: HarnessState;\n}\n\nexport interface LoadedHarnessState {\n /** Undefined when this process is running off the environment fallback. */\n filePath?: string;\n owned: boolean;\n state: HarnessState;\n}\n\nlet loaded: LoadedHarnessState | undefined;\n\n/** Drops the cached state. For tests, and for a process changing identity. */\nexport function resetHarnessStore(): void {\n loaded = undefined;\n}\n\nfunction parseStateFile(source: string, filePath: string): HarnessStateFile {\n const parsed = JSON.parse(source) as Partial<HarnessStateFile>;\n if (parsed.version !== STATE_FILE_VERSION) {\n throw new Error(\n `Harness state at ${filePath} has version ${String(parsed.version)}, expected ${STATE_FILE_VERSION}`,\n );\n }\n if (typeof parsed.state !== 'object' || parsed.state === null) {\n throw new Error(`Harness state at ${filePath} has no state object`);\n }\n return {\n version: STATE_FILE_VERSION,\n owner: { pid: parsed.owner?.pid ?? -1, startedAt: parsed.owner?.startedAt ?? '' },\n state: parsed.state as HarnessState,\n };\n}\n\n/**\n * Reads the state for this process, once.\n *\n * A missing pointer means nobody wrote a file for this process, so the\n * environment is all there is: a nested run, a third-party spawn, or a caller\n * older than the store. A pointer that cannot be read is reported and then\n * treated the same way, because a session that starts degraded beats a session\n * that does not start at all.\n */\nexport function loadHarnessState(\n environment: NodeJS.ProcessEnv = process.env,\n report?: HarnessStateParseReporter,\n): LoadedHarnessState {\n // The cache describes this process. Asking about another environment is a\n // question about another process, so it reads through rather than answering\n // from what this one happens to hold.\n const cacheable = environment === process.env;\n if (cacheable && loaded) return loaded;\n\n const filePath = environment[HARNESS_STATE_POINTER];\n let result: LoadedHarnessState | undefined;\n if (filePath && fs.existsSync(filePath)) {\n try {\n const file = parseStateFile(fs.readFileSync(filePath, 'utf8'), filePath);\n const owned = file.owner.pid === process.pid || file.owner.pid === UNCLAIMED_OWNER;\n result = { filePath, owned, state: file.state };\n } catch (error) {\n report?.(HARNESS_STATE_POINTER, error);\n }\n }\n result ??= { owned: false, state: readHarnessState(environment, report) };\n if (cacheable) loaded = result;\n return result;\n}\n\nexport function getHarnessState(report?: HarnessStateParseReporter): HarnessState {\n return loadHarnessState(process.env, report).state;\n}\n\nfunction stateFilePath(directory: string): string {\n return path.join(directory, STATE_FILE_NAME);\n}\n\n/** Where a process writes state when it was not given a file of its own. */\nfunction adoptedStateFilePath(): string {\n return path.join(os.tmpdir(), ADOPTED_DIRECTORY, `${String(process.pid)}.json`);\n}\n\nfunction persist(filePath: string, state: HarnessState, ownerPid = process.pid): void {\n writePrivateAtomicJson(filePath, {\n version: STATE_FILE_VERSION,\n owner: { pid: ownerPid, startedAt: new Date().toISOString() },\n state,\n } satisfies HarnessStateFile);\n}\n\n/**\n * Starts a session: writes the file and publishes the derived environment.\n *\n * The environment is a parameter because the caller is usually describing a\n * process other than itself. The launcher builds the environment for the Pi it\n * is about to spawn, and Doom Team builds the snapshot for a detached child, so\n * neither should touch its own `process.env` to do it.\n */\nexport function createHarnessSession(\n state: HarnessState,\n options: {\n directory: string;\n environment: NodeJS.ProcessEnv;\n /**\n * Leave the file for a process that does not exist yet to claim.\n *\n * What a spawner wants: the child owns its state from its first read, in a\n * directory the spawner manages, so the parent cleaning up its own session\n * cannot pull the file out from under a detached run.\n */\n unclaimed?: boolean;\n },\n): string {\n const filePath = stateFilePath(options.directory);\n persist(filePath, state, options.unclaimed ? UNCLAIMED_OWNER : process.pid);\n options.environment[HARNESS_STATE_POINTER] = filePath;\n projectHarnessEnvironment(state, options.environment);\n // Only prime the cache when this process is the one being described.\n if (!options.unclaimed && options.environment === process.env) loaded = { filePath, owned: true, state };\n return filePath;\n}\n\n/**\n * Applies a patch to this process's state.\n *\n * Copy on write: a process holding a file it does not own writes its own copy\n * and repoints itself at it, so a child can never rewrite its parent's session.\n */\nexport function updateHarnessState(\n patch: Partial<HarnessState>,\n environment: NodeJS.ProcessEnv = process.env,\n): HarnessState {\n const current = loadHarnessState(environment);\n const state = { ...current.state, ...patch };\n const filePath = current.owned && current.filePath ? current.filePath : adoptedStateFilePath();\n persist(filePath, state);\n environment[HARNESS_STATE_POINTER] = filePath;\n if (environment === process.env) loaded = { filePath, owned: true, state };\n projectHarnessEnvironment(patch, environment);\n return state;\n}\n\n/** Removes this process's own state file. Safe when there is none. */\nexport function disposeHarnessState(environment: NodeJS.ProcessEnv = process.env): void {\n const current = loaded;\n resetHarnessStore();\n if (!current?.owned || !current.filePath) return;\n fs.rmSync(current.filePath, { force: true });\n delete environment[HARNESS_STATE_POINTER];\n}\n\nexport function harnessRoot(state: Pick<HarnessState, 'root'> = getHarnessState()): string {\n return state.root ?? process.cwd();\n}\n\n/**\n * The repository root, or a hard error.\n *\n * Separate from `requireHarnessPaths` because most callers only need the root,\n * and separate from `harnessRoot` because falling back to `process.cwd()` would\n * quietly operate on whatever directory the session happens to sit in. A caller\n * that asks for the root wants the session's repository or nothing.\n */\nexport function requireHarnessRoot(state: Pick<HarnessState, 'root'> = getHarnessState()): string {\n if (!state.root) throw new Error(`${HARNESS_STATE_KEYS.root} is not set`);\n return state.root;\n}\n\n/** The root and the run's scratch directory, for callers that write to disk. */\nexport function requireHarnessPaths(state: Pick<HarnessState, 'root' | 'temporaryDirectory'> = getHarnessState()): {\n root: string;\n temporaryDirectory: string;\n} {\n const root = requireHarnessRoot(state);\n if (!state.temporaryDirectory) throw new Error(`${HARNESS_STATE_KEYS.temporaryDirectory} is not set`);\n return { root, temporaryDirectory: state.temporaryDirectory };\n}\n"],"mappings":"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"}
@@ -0,0 +1,102 @@
1
+ import { HarnessState } from "./types.cjs";
2
+ import { HarnessStateParseReporter } from "./harnessState.cjs";
3
+ //#region src/harnessStore.d.ts
4
+ /**
5
+ * Who owns the harness state, and where it lives.
6
+ *
7
+ * The state used to be the environment: twenty variables that every reader
8
+ * re-parsed and every writer mutated globally. The file is the authority now,
9
+ * and the environment is a projection published for the readers that can only
10
+ * see an environment, which are bash hooks, the shell launchers, packages that
11
+ * do not depend on this one, and any process spawned by any of them.
12
+ *
13
+ * OWNERSHIP IS PER PROCESS, AND THAT IS THE WHOLE DESIGN:
14
+ * Doom Team subagents run detached, in their own process group, from an
15
+ * environment snapshot taken at spawn. A child can outlive its parent. If it
16
+ * shared the parent's file it would corrupt a session it does not own, and if
17
+ * that file sat in the parent's run directory the parent's shutdown sweep would
18
+ * delete it from under a live run. So every process writes a file it owns:
19
+ * `owner.pid` records who, and a process that finds someone else's file copies
20
+ * it before its first write.
21
+ *
22
+ * AVOID:
23
+ * - Treating the file as shared mutable state between processes. Atomic writes
24
+ * prevent torn reads, not lost updates
25
+ * - Reading the environment directly for anything this exposes
26
+ */
27
+ /** Points at the state file. The one variable that is not derived. */
28
+ declare const HARNESS_STATE_POINTER = "DOOMPI_STATE";
29
+ interface HarnessStateFile {
30
+ version: number;
31
+ owner: {
32
+ pid: number;
33
+ startedAt: string;
34
+ };
35
+ state: HarnessState;
36
+ }
37
+ interface LoadedHarnessState {
38
+ /** Undefined when this process is running off the environment fallback. */
39
+ filePath?: string;
40
+ owned: boolean;
41
+ state: HarnessState;
42
+ }
43
+ /** Drops the cached state. For tests, and for a process changing identity. */
44
+ declare function resetHarnessStore(): void;
45
+ /**
46
+ * Reads the state for this process, once.
47
+ *
48
+ * A missing pointer means nobody wrote a file for this process, so the
49
+ * environment is all there is: a nested run, a third-party spawn, or a caller
50
+ * older than the store. A pointer that cannot be read is reported and then
51
+ * treated the same way, because a session that starts degraded beats a session
52
+ * that does not start at all.
53
+ */
54
+ declare function loadHarnessState(environment?: NodeJS.ProcessEnv, report?: HarnessStateParseReporter): LoadedHarnessState;
55
+ declare function getHarnessState(report?: HarnessStateParseReporter): HarnessState;
56
+ /**
57
+ * Starts a session: writes the file and publishes the derived environment.
58
+ *
59
+ * The environment is a parameter because the caller is usually describing a
60
+ * process other than itself. The launcher builds the environment for the Pi it
61
+ * is about to spawn, and Doom Team builds the snapshot for a detached child, so
62
+ * neither should touch its own `process.env` to do it.
63
+ */
64
+ declare function createHarnessSession(state: HarnessState, options: {
65
+ directory: string;
66
+ environment: NodeJS.ProcessEnv;
67
+ /**
68
+ * Leave the file for a process that does not exist yet to claim.
69
+ *
70
+ * What a spawner wants: the child owns its state from its first read, in a
71
+ * directory the spawner manages, so the parent cleaning up its own session
72
+ * cannot pull the file out from under a detached run.
73
+ */
74
+ unclaimed?: boolean;
75
+ }): string;
76
+ /**
77
+ * Applies a patch to this process's state.
78
+ *
79
+ * Copy on write: a process holding a file it does not own writes its own copy
80
+ * and repoints itself at it, so a child can never rewrite its parent's session.
81
+ */
82
+ declare function updateHarnessState(patch: Partial<HarnessState>, environment?: NodeJS.ProcessEnv): HarnessState;
83
+ /** Removes this process's own state file. Safe when there is none. */
84
+ declare function disposeHarnessState(environment?: NodeJS.ProcessEnv): void;
85
+ declare function harnessRoot(state?: Pick<HarnessState, 'root'>): string;
86
+ /**
87
+ * The repository root, or a hard error.
88
+ *
89
+ * Separate from `requireHarnessPaths` because most callers only need the root,
90
+ * and separate from `harnessRoot` because falling back to `process.cwd()` would
91
+ * quietly operate on whatever directory the session happens to sit in. A caller
92
+ * that asks for the root wants the session's repository or nothing.
93
+ */
94
+ declare function requireHarnessRoot(state?: Pick<HarnessState, 'root'>): string;
95
+ /** The root and the run's scratch directory, for callers that write to disk. */
96
+ declare function requireHarnessPaths(state?: Pick<HarnessState, 'root' | 'temporaryDirectory'>): {
97
+ root: string;
98
+ temporaryDirectory: string;
99
+ };
100
+ //#endregion
101
+ export { HARNESS_STATE_POINTER, HarnessStateFile, LoadedHarnessState, createHarnessSession, disposeHarnessState, getHarnessState, harnessRoot, loadHarnessState, requireHarnessPaths, requireHarnessRoot, resetHarnessStore, updateHarnessState };
102
+ //# sourceMappingURL=harnessStore.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessStore.d.cts","names":[],"sources":["../src/harnessStore.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;cAqCa;UAYI;EACf;EACA;IAAS;IAAa;;EACtB,OAAO;;UAGQ;;EAEf;EACA;EACA,OAAO;;;iBAMO;;;;;;;;;;iBA8BA,iBACd,cAAa,OAAO,YACpB,SAAS,4BACR;iBAuBa,gBAAgB,SAAS,4BAA4B;;;;;;;;;iBA6BrD,qBACd,OAAO,cACP;EACE;EACA,aAAa,OAAO;;;;;;;;EAQpB;;;;;;;;iBAkBY,mBACd,OAAO,QAAQ,eACf,cAAa,OAAO,aACnB;;iBAYa,oBAAoB,cAAa,OAAO;iBAQxC,YAAY,QAAO,KAAK;;;;;;;;;iBAYxB,mBAAmB,QAAO,KAAK;;iBAM/B,oBAAoB,QAAO,KAAK;EAC9C;EACA"}
@@ -0,0 +1,102 @@
1
+ import { HarnessState } from "./types.mjs";
2
+ import { HarnessStateParseReporter } from "./harnessState.mjs";
3
+ //#region src/harnessStore.d.ts
4
+ /**
5
+ * Who owns the harness state, and where it lives.
6
+ *
7
+ * The state used to be the environment: twenty variables that every reader
8
+ * re-parsed and every writer mutated globally. The file is the authority now,
9
+ * and the environment is a projection published for the readers that can only
10
+ * see an environment, which are bash hooks, the shell launchers, packages that
11
+ * do not depend on this one, and any process spawned by any of them.
12
+ *
13
+ * OWNERSHIP IS PER PROCESS, AND THAT IS THE WHOLE DESIGN:
14
+ * Doom Team subagents run detached, in their own process group, from an
15
+ * environment snapshot taken at spawn. A child can outlive its parent. If it
16
+ * shared the parent's file it would corrupt a session it does not own, and if
17
+ * that file sat in the parent's run directory the parent's shutdown sweep would
18
+ * delete it from under a live run. So every process writes a file it owns:
19
+ * `owner.pid` records who, and a process that finds someone else's file copies
20
+ * it before its first write.
21
+ *
22
+ * AVOID:
23
+ * - Treating the file as shared mutable state between processes. Atomic writes
24
+ * prevent torn reads, not lost updates
25
+ * - Reading the environment directly for anything this exposes
26
+ */
27
+ /** Points at the state file. The one variable that is not derived. */
28
+ declare const HARNESS_STATE_POINTER = "DOOMPI_STATE";
29
+ interface HarnessStateFile {
30
+ version: number;
31
+ owner: {
32
+ pid: number;
33
+ startedAt: string;
34
+ };
35
+ state: HarnessState;
36
+ }
37
+ interface LoadedHarnessState {
38
+ /** Undefined when this process is running off the environment fallback. */
39
+ filePath?: string;
40
+ owned: boolean;
41
+ state: HarnessState;
42
+ }
43
+ /** Drops the cached state. For tests, and for a process changing identity. */
44
+ declare function resetHarnessStore(): void;
45
+ /**
46
+ * Reads the state for this process, once.
47
+ *
48
+ * A missing pointer means nobody wrote a file for this process, so the
49
+ * environment is all there is: a nested run, a third-party spawn, or a caller
50
+ * older than the store. A pointer that cannot be read is reported and then
51
+ * treated the same way, because a session that starts degraded beats a session
52
+ * that does not start at all.
53
+ */
54
+ declare function loadHarnessState(environment?: NodeJS.ProcessEnv, report?: HarnessStateParseReporter): LoadedHarnessState;
55
+ declare function getHarnessState(report?: HarnessStateParseReporter): HarnessState;
56
+ /**
57
+ * Starts a session: writes the file and publishes the derived environment.
58
+ *
59
+ * The environment is a parameter because the caller is usually describing a
60
+ * process other than itself. The launcher builds the environment for the Pi it
61
+ * is about to spawn, and Doom Team builds the snapshot for a detached child, so
62
+ * neither should touch its own `process.env` to do it.
63
+ */
64
+ declare function createHarnessSession(state: HarnessState, options: {
65
+ directory: string;
66
+ environment: NodeJS.ProcessEnv;
67
+ /**
68
+ * Leave the file for a process that does not exist yet to claim.
69
+ *
70
+ * What a spawner wants: the child owns its state from its first read, in a
71
+ * directory the spawner manages, so the parent cleaning up its own session
72
+ * cannot pull the file out from under a detached run.
73
+ */
74
+ unclaimed?: boolean;
75
+ }): string;
76
+ /**
77
+ * Applies a patch to this process's state.
78
+ *
79
+ * Copy on write: a process holding a file it does not own writes its own copy
80
+ * and repoints itself at it, so a child can never rewrite its parent's session.
81
+ */
82
+ declare function updateHarnessState(patch: Partial<HarnessState>, environment?: NodeJS.ProcessEnv): HarnessState;
83
+ /** Removes this process's own state file. Safe when there is none. */
84
+ declare function disposeHarnessState(environment?: NodeJS.ProcessEnv): void;
85
+ declare function harnessRoot(state?: Pick<HarnessState, 'root'>): string;
86
+ /**
87
+ * The repository root, or a hard error.
88
+ *
89
+ * Separate from `requireHarnessPaths` because most callers only need the root,
90
+ * and separate from `harnessRoot` because falling back to `process.cwd()` would
91
+ * quietly operate on whatever directory the session happens to sit in. A caller
92
+ * that asks for the root wants the session's repository or nothing.
93
+ */
94
+ declare function requireHarnessRoot(state?: Pick<HarnessState, 'root'>): string;
95
+ /** The root and the run's scratch directory, for callers that write to disk. */
96
+ declare function requireHarnessPaths(state?: Pick<HarnessState, 'root' | 'temporaryDirectory'>): {
97
+ root: string;
98
+ temporaryDirectory: string;
99
+ };
100
+ //#endregion
101
+ export { HARNESS_STATE_POINTER, HarnessStateFile, LoadedHarnessState, createHarnessSession, disposeHarnessState, getHarnessState, harnessRoot, loadHarnessState, requireHarnessPaths, requireHarnessRoot, resetHarnessStore, updateHarnessState };
102
+ //# sourceMappingURL=harnessStore.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessStore.d.mts","names":[],"sources":["../src/harnessStore.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;cAqCa;UAYI;EACf;EACA;IAAS;IAAa;;EACtB,OAAO;;UAGQ;;EAEf;EACA;EACA,OAAO;;;iBAMO;;;;;;;;;;iBA8BA,iBACd,cAAa,OAAO,YACpB,SAAS,4BACR;iBAuBa,gBAAgB,SAAS,4BAA4B;;;;;;;;;iBA6BrD,qBACd,OAAO,cACP;EACE;EACA,aAAa,OAAO;;;;;;;;EAQpB;;;;;;;;iBAkBY,mBACd,OAAO,QAAQ,eACf,cAAa,OAAO,aACnB;;iBAYa,oBAAoB,cAAa,OAAO;iBAQxC,YAAY,QAAO,KAAK;;;;;;;;;iBAYxB,mBAAmB,QAAO,KAAK;;iBAM/B,oBAAoB,QAAO,KAAK;EAC9C;EACA"}
@@ -0,0 +1,2 @@
1
+ import{writePrivateAtomicJson as e}from"./atomicJson.mjs";import{HARNESS_STATE_KEYS as t,projectHarnessEnvironment as n,readHarnessState as r}from"./harnessState.mjs";import i from"node:fs";import a from"node:os";import o from"node:path";const s=`DOOMPI_STATE`;let c;function l(){c=void 0}function u(e,t){let n=JSON.parse(e);if(n.version!==1)throw Error(`Harness state at ${t} has version ${String(n.version)}, expected 1`);if(typeof n.state!=`object`||n.state===null)throw Error(`Harness state at ${t} has no state object`);return{version:1,owner:{pid:n.owner?.pid??-1,startedAt:n.owner?.startedAt??``},state:n.state}}function d(e=process.env,t){let n=e===process.env;if(n&&c)return c;let a=e[s],o;if(a&&i.existsSync(a))try{let e=u(i.readFileSync(a,`utf8`),a);o={filePath:a,owned:e.owner.pid===process.pid||e.owner.pid===0,state:e.state}}catch(e){t?.(s,e)}return o??={owned:!1,state:r(e,t)},n&&(c=o),o}function f(e){return d(process.env,e).state}function p(e){return o.join(e,`harness-state.json`)}function m(){return o.join(a.tmpdir(),`doom-harness`,`${String(process.pid)}.json`)}function h(t,n,r=process.pid){e(t,{version:1,owner:{pid:r,startedAt:new Date().toISOString()},state:n})}function g(e,t){let r=p(t.directory);return h(r,e,t.unclaimed?0:process.pid),t.environment[s]=r,n(e,t.environment),!t.unclaimed&&t.environment===process.env&&(c={filePath:r,owned:!0,state:e}),r}function _(e,t=process.env){let r=d(t),i={...r.state,...e},a=r.owned&&r.filePath?r.filePath:m();return h(a,i),t[s]=a,t===process.env&&(c={filePath:a,owned:!0,state:i}),n(e,t),i}function v(e=process.env){let t=c;l(),!(!t?.owned||!t.filePath)&&(i.rmSync(t.filePath,{force:!0}),delete e[s])}function y(e=f()){return e.root??process.cwd()}function b(e=f()){if(!e.root)throw Error(`${t.root} is not set`);return e.root}function x(e=f()){let n=b(e);if(!e.temporaryDirectory)throw Error(`${t.temporaryDirectory} is not set`);return{root:n,temporaryDirectory:e.temporaryDirectory}}export{s as HARNESS_STATE_POINTER,g as createHarnessSession,v as disposeHarnessState,f as getHarnessState,y as harnessRoot,d as loadHarnessState,x as requireHarnessPaths,b as requireHarnessRoot,l as resetHarnessStore,_ as updateHarnessState};
2
+ //# sourceMappingURL=harnessStore.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessStore.mjs","names":[],"sources":["../src/harnessStore.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { writePrivateAtomicJson } from './atomicJson.ts';\nimport {\n HARNESS_STATE_KEYS,\n projectHarnessEnvironment,\n readHarnessState,\n type HarnessStateParseReporter,\n} from './harnessState.ts';\nimport type { HarnessState } from './types.ts';\n\n/**\n * Who owns the harness state, and where it lives.\n *\n * The state used to be the environment: twenty variables that every reader\n * re-parsed and every writer mutated globally. The file is the authority now,\n * and the environment is a projection published for the readers that can only\n * see an environment, which are bash hooks, the shell launchers, packages that\n * do not depend on this one, and any process spawned by any of them.\n *\n * OWNERSHIP IS PER PROCESS, AND THAT IS THE WHOLE DESIGN:\n * Doom Team subagents run detached, in their own process group, from an\n * environment snapshot taken at spawn. A child can outlive its parent. If it\n * shared the parent's file it would corrupt a session it does not own, and if\n * that file sat in the parent's run directory the parent's shutdown sweep would\n * delete it from under a live run. So every process writes a file it owns:\n * `owner.pid` records who, and a process that finds someone else's file copies\n * it before its first write.\n *\n * AVOID:\n * - Treating the file as shared mutable state between processes. Atomic writes\n * prevent torn reads, not lost updates\n * - Reading the environment directly for anything this exposes\n */\n\n/** Points at the state file. The one variable that is not derived. */\nexport const HARNESS_STATE_POINTER = 'DOOMPI_STATE';\n\nconst STATE_FILE_VERSION = 1;\nconst STATE_FILE_NAME = 'harness-state.json';\nconst ADOPTED_DIRECTORY = 'doom-harness';\n/**\n * Written for a process that does not exist yet, so the first one to read it\n * owns it. A spawner cannot know its child's pid, and a child that inherited\n * its parent's file would lose it the moment the parent cleaned up.\n */\nconst UNCLAIMED_OWNER = 0;\n\nexport interface HarnessStateFile {\n version: number;\n owner: { pid: number; startedAt: string };\n state: HarnessState;\n}\n\nexport interface LoadedHarnessState {\n /** Undefined when this process is running off the environment fallback. */\n filePath?: string;\n owned: boolean;\n state: HarnessState;\n}\n\nlet loaded: LoadedHarnessState | undefined;\n\n/** Drops the cached state. For tests, and for a process changing identity. */\nexport function resetHarnessStore(): void {\n loaded = undefined;\n}\n\nfunction parseStateFile(source: string, filePath: string): HarnessStateFile {\n const parsed = JSON.parse(source) as Partial<HarnessStateFile>;\n if (parsed.version !== STATE_FILE_VERSION) {\n throw new Error(\n `Harness state at ${filePath} has version ${String(parsed.version)}, expected ${STATE_FILE_VERSION}`,\n );\n }\n if (typeof parsed.state !== 'object' || parsed.state === null) {\n throw new Error(`Harness state at ${filePath} has no state object`);\n }\n return {\n version: STATE_FILE_VERSION,\n owner: { pid: parsed.owner?.pid ?? -1, startedAt: parsed.owner?.startedAt ?? '' },\n state: parsed.state as HarnessState,\n };\n}\n\n/**\n * Reads the state for this process, once.\n *\n * A missing pointer means nobody wrote a file for this process, so the\n * environment is all there is: a nested run, a third-party spawn, or a caller\n * older than the store. A pointer that cannot be read is reported and then\n * treated the same way, because a session that starts degraded beats a session\n * that does not start at all.\n */\nexport function loadHarnessState(\n environment: NodeJS.ProcessEnv = process.env,\n report?: HarnessStateParseReporter,\n): LoadedHarnessState {\n // The cache describes this process. Asking about another environment is a\n // question about another process, so it reads through rather than answering\n // from what this one happens to hold.\n const cacheable = environment === process.env;\n if (cacheable && loaded) return loaded;\n\n const filePath = environment[HARNESS_STATE_POINTER];\n let result: LoadedHarnessState | undefined;\n if (filePath && fs.existsSync(filePath)) {\n try {\n const file = parseStateFile(fs.readFileSync(filePath, 'utf8'), filePath);\n const owned = file.owner.pid === process.pid || file.owner.pid === UNCLAIMED_OWNER;\n result = { filePath, owned, state: file.state };\n } catch (error) {\n report?.(HARNESS_STATE_POINTER, error);\n }\n }\n result ??= { owned: false, state: readHarnessState(environment, report) };\n if (cacheable) loaded = result;\n return result;\n}\n\nexport function getHarnessState(report?: HarnessStateParseReporter): HarnessState {\n return loadHarnessState(process.env, report).state;\n}\n\nfunction stateFilePath(directory: string): string {\n return path.join(directory, STATE_FILE_NAME);\n}\n\n/** Where a process writes state when it was not given a file of its own. */\nfunction adoptedStateFilePath(): string {\n return path.join(os.tmpdir(), ADOPTED_DIRECTORY, `${String(process.pid)}.json`);\n}\n\nfunction persist(filePath: string, state: HarnessState, ownerPid = process.pid): void {\n writePrivateAtomicJson(filePath, {\n version: STATE_FILE_VERSION,\n owner: { pid: ownerPid, startedAt: new Date().toISOString() },\n state,\n } satisfies HarnessStateFile);\n}\n\n/**\n * Starts a session: writes the file and publishes the derived environment.\n *\n * The environment is a parameter because the caller is usually describing a\n * process other than itself. The launcher builds the environment for the Pi it\n * is about to spawn, and Doom Team builds the snapshot for a detached child, so\n * neither should touch its own `process.env` to do it.\n */\nexport function createHarnessSession(\n state: HarnessState,\n options: {\n directory: string;\n environment: NodeJS.ProcessEnv;\n /**\n * Leave the file for a process that does not exist yet to claim.\n *\n * What a spawner wants: the child owns its state from its first read, in a\n * directory the spawner manages, so the parent cleaning up its own session\n * cannot pull the file out from under a detached run.\n */\n unclaimed?: boolean;\n },\n): string {\n const filePath = stateFilePath(options.directory);\n persist(filePath, state, options.unclaimed ? UNCLAIMED_OWNER : process.pid);\n options.environment[HARNESS_STATE_POINTER] = filePath;\n projectHarnessEnvironment(state, options.environment);\n // Only prime the cache when this process is the one being described.\n if (!options.unclaimed && options.environment === process.env) loaded = { filePath, owned: true, state };\n return filePath;\n}\n\n/**\n * Applies a patch to this process's state.\n *\n * Copy on write: a process holding a file it does not own writes its own copy\n * and repoints itself at it, so a child can never rewrite its parent's session.\n */\nexport function updateHarnessState(\n patch: Partial<HarnessState>,\n environment: NodeJS.ProcessEnv = process.env,\n): HarnessState {\n const current = loadHarnessState(environment);\n const state = { ...current.state, ...patch };\n const filePath = current.owned && current.filePath ? current.filePath : adoptedStateFilePath();\n persist(filePath, state);\n environment[HARNESS_STATE_POINTER] = filePath;\n if (environment === process.env) loaded = { filePath, owned: true, state };\n projectHarnessEnvironment(patch, environment);\n return state;\n}\n\n/** Removes this process's own state file. Safe when there is none. */\nexport function disposeHarnessState(environment: NodeJS.ProcessEnv = process.env): void {\n const current = loaded;\n resetHarnessStore();\n if (!current?.owned || !current.filePath) return;\n fs.rmSync(current.filePath, { force: true });\n delete environment[HARNESS_STATE_POINTER];\n}\n\nexport function harnessRoot(state: Pick<HarnessState, 'root'> = getHarnessState()): string {\n return state.root ?? process.cwd();\n}\n\n/**\n * The repository root, or a hard error.\n *\n * Separate from `requireHarnessPaths` because most callers only need the root,\n * and separate from `harnessRoot` because falling back to `process.cwd()` would\n * quietly operate on whatever directory the session happens to sit in. A caller\n * that asks for the root wants the session's repository or nothing.\n */\nexport function requireHarnessRoot(state: Pick<HarnessState, 'root'> = getHarnessState()): string {\n if (!state.root) throw new Error(`${HARNESS_STATE_KEYS.root} is not set`);\n return state.root;\n}\n\n/** The root and the run's scratch directory, for callers that write to disk. */\nexport function requireHarnessPaths(state: Pick<HarnessState, 'root' | 'temporaryDirectory'> = getHarnessState()): {\n root: string;\n temporaryDirectory: string;\n} {\n const root = requireHarnessRoot(state);\n if (!state.temporaryDirectory) throw new Error(`${HARNESS_STATE_KEYS.temporaryDirectory} is not set`);\n return { root, temporaryDirectory: state.temporaryDirectory };\n}\n"],"mappings":"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"}
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./config.cjs"),t=require("./configWriter.cjs"),n=require("./init.cjs"),r=require("./types.cjs"),i=require("./container.cjs"),a=require("./layeredConfig.cjs"),o=require("./domains.cjs"),s=require("./config/piConfig.cjs"),c=require("./atomicJson.cjs"),l=require("./harnessState.cjs"),u=require("./harnessStore.cjs"),d=require("./majorModes.cjs"),f=require("./piContext.cjs"),p=require("./profiles.cjs");exports.DOOM_CONFIG_ENTRY_TYPE=f.DOOM_CONFIG_ENTRY_TYPE,exports.DOOM_CONFIG_TEMPLATES=n.DOOM_CONFIG_TEMPLATES,exports.DOOM_CONFIG_TYPES=r.DOOM_CONFIG_TYPES,exports.DOOM_DIR=a.DOOM_DIR,exports.DOOM_PLANNING_THINKING_LEVELS=e.DOOM_PLANNING_THINKING_LEVELS,exports.DOOM_VOICE_ENGINES=e.DOOM_VOICE_ENGINES,exports.DOOM_VOICE_TTS_ENGINES=e.DOOM_VOICE_TTS_ENGINES,Object.defineProperty(exports,"DoomConfigService",{enumerable:!0,get:function(){return i.DoomConfigService}}),exports.FILE_ONLY_STATE_FIELDS=l.FILE_ONLY_STATE_FIELDS,exports.GLOBAL_DOOM_SEED_FILES=n.GLOBAL_DOOM_SEED_FILES,exports.HARNESS_STATE_KEYS=l.HARNESS_STATE_KEYS,exports.HARNESS_STATE_POINTER=u.HARNESS_STATE_POINTER,exports.PERSONA_FILES=p.PERSONA_FILES,exports.appendDoomConfigSelection=f.appendDoomConfigSelection,exports.applyProfileEnvironment=p.applyProfileEnvironment,exports.buildPersonaPrompt=p.buildPersonaPrompt,exports.createDoomConfigContainer=i.createDoomConfigContainer,exports.createDoomConfigContext=f.createDoomConfigContext,exports.createDoomConfigModule=i.createDoomConfigModule,exports.createHarnessSession=u.createHarnessSession,exports.disposeHarnessState=u.disposeHarnessState,exports.domainCompletionItems=o.domainCompletionItems,exports.domainCompletionPrefix=o.domainCompletionPrefix,exports.expandDomainNames=o.expandDomainNames,exports.getHarnessState=u.getHarnessState,exports.globalDoomConfigDirectory=e.globalDoomConfigDirectory,exports.globalDoomConfigPath=e.globalDoomConfigPath,exports.harnessRoot=u.harnessRoot,exports.initializeGlobalDoomConfig=n.initializeGlobalDoomConfig,exports.isLocalPackageSpecifier=d.isLocalPackageSpecifier,exports.layerEntries=d.layerEntries,exports.layerHookGroups=d.layerHookGroups,exports.listDomainNames=o.listDomainNames,exports.listProfileNames=p.listProfileNames,exports.loadDomains=o.loadDomains,exports.loadDoomConfig=e.loadDoomConfig,exports.loadHarnessState=u.loadHarnessState,exports.loadMajorModesConfig=d.loadMajorModesConfig,exports.loadPiConfig=s.loadPiConfig,exports.loadProfiles=p.loadProfiles,exports.mergeDoomConfigs=e.mergeDoomConfigs,exports.parseDoomConfig=e.parseDoomConfig,exports.parsePlanningModeConfig=e.parsePlanningModeConfig,exports.piConfigPaths=s.piConfigPaths,exports.projectHarnessEnvironment=l.projectHarnessEnvironment,exports.provideDoomConfigContext=f.provideDoomConfigContext,exports.readDoomConfigSelection=f.readDoomConfigSelection,exports.readHarnessState=l.readHarnessState,exports.replaceProfileEnvironment=p.replaceProfileEnvironment,exports.repositoryDoomConfigPath=e.repositoryDoomConfigPath,exports.requireDoomConfigContext=f.requireDoomConfigContext,exports.requireHarnessPaths=u.requireHarnessPaths,exports.requireHarnessRoot=u.requireHarnessRoot,exports.resetHarnessStore=u.resetHarnessStore,exports.resolveLayerCustomization=d.resolveLayerCustomization,exports.resolveLayers=d.resolveLayers,exports.resolvePlanningPlansDirectory=e.resolvePlanningPlansDirectory,exports.resolvePluginDirectories=o.resolvePluginDirectories,exports.resolvePluginEntries=o.resolvePluginEntries,exports.resolveProfile=p.resolveProfile,exports.resolveSharedSkills=o.resolveSharedSkills,exports.resolveVoiceConfig=e.resolveVoiceConfig,exports.setDoomConfigValue=t.setDoomConfigValue,exports.unsetDoomConfigValue=t.unsetDoomConfigValue,exports.updateHarnessState=u.updateHarnessState,exports.writeDoomConfigValues=t.writeDoomConfigValues,exports.writePrivateAtomicJson=c.writePrivateAtomicJson;
@@ -0,0 +1,16 @@
1
+ import { writePrivateAtomicJson } from "./atomicJson.cjs";
2
+ import { DOOM_CONFIG_TYPES, DeepReadonly, DoomConfig, DoomConfigContext, DoomHarnessContext, DoomSelectionConfig, EditorConfig, HarnessState, IDoomConfigService, PlanningAgentConfig, PlanningModeConfig, PlanningThinkingLevel, PluginHookSource, ProjectTrust, ResolvedVoiceAutoCaptureConfig, ResolvedVoiceConfig, VoiceAdapterConfig, VoiceAutoCaptureConfig, VoiceConfig, VoiceEngine, VoiceModelConfig, VoiceTtsConfig, VoiceTtsEngine } from "./types.cjs";
3
+ import { DOOM_PLANNING_THINKING_LEVELS, DOOM_VOICE_ENGINES, DOOM_VOICE_TTS_ENGINES, globalDoomConfigDirectory, globalDoomConfigPath, loadDoomConfig, mergeDoomConfigs, parseDoomConfig, parsePlanningModeConfig, repositoryDoomConfigPath, resolvePlanningPlansDirectory, resolveVoiceConfig } from "./config.cjs";
4
+ import { ConfigAdapterRegistrationOptions, PiConfig, PiConfigLoadOptions, PiConfigPaths } from "./config/schema.cjs";
5
+ import { loadPiConfig, piConfigPaths } from "./config/piConfig.cjs";
6
+ import { DoomConfigEdit, WriteDoomConfigOptions, setDoomConfigValue, unsetDoomConfigValue, writeDoomConfigValues } from "./configWriter.cjs";
7
+ import { DoomConfigService, createDoomConfigContainer, createDoomConfigModule } from "./container.cjs";
8
+ import { DOOM_DIR } from "./layeredConfig.cjs";
9
+ import { DomainDefinition, DomainManifest, DomainMcpAllowlist, DomainPlugin, PluginEntry, ResolvedDomain, domainCompletionItems, domainCompletionPrefix, expandDomainNames, listDomainNames, loadDomains, resolvePluginDirectories, resolvePluginEntries, resolveSharedSkills } from "./domains.cjs";
10
+ import { FILE_ONLY_STATE_FIELDS, HARNESS_STATE_KEYS, HarnessStateParseReporter, projectHarnessEnvironment, readHarnessState } from "./harnessState.cjs";
11
+ import { HARNESS_STATE_POINTER, HarnessStateFile, LoadedHarnessState, createHarnessSession, disposeHarnessState, getHarnessState, harnessRoot, loadHarnessState, requireHarnessPaths, requireHarnessRoot, resetHarnessStore, updateHarnessState } from "./harnessStore.cjs";
12
+ import { DOOM_CONFIG_TEMPLATES, GLOBAL_DOOM_SEED_FILES, GlobalDoomInitResult, initializeGlobalDoomConfig } from "./init.cjs";
13
+ import { LayerCustomizationConfig, LayerDefinition, LayerPackage, LayerResolvers, MajorModesConfig, ResolvedLayerDefinition, SubagentLayerConfig, SubagentLayerModelConfig, isLocalPackageSpecifier, layerEntries, layerHookGroups, loadMajorModesConfig, resolveLayerCustomization, resolveLayers } from "./majorModes.cjs";
14
+ import { DOOM_CONFIG_ENTRY_TYPE, DoomConfigSelection, appendDoomConfigSelection, createDoomConfigContext, provideDoomConfigContext, readDoomConfigSelection, requireDoomConfigContext } from "./piContext.cjs";
15
+ import { AgentProfile, PERSONA_FILES, applyProfileEnvironment, buildPersonaPrompt, listProfileNames, loadProfiles, replaceProfileEnvironment, resolveProfile } from "./profiles.cjs";
16
+ export { AgentProfile, type ConfigAdapterRegistrationOptions, DOOM_CONFIG_ENTRY_TYPE, DOOM_CONFIG_TEMPLATES, DOOM_CONFIG_TYPES, DOOM_DIR, DOOM_PLANNING_THINKING_LEVELS, DOOM_VOICE_ENGINES, DOOM_VOICE_TTS_ENGINES, type DeepReadonly, DomainDefinition, DomainManifest, DomainMcpAllowlist, DomainPlugin, type DoomConfig, type DoomConfigContext, type DoomConfigEdit, type DoomConfigSelection, DoomConfigService, type DoomHarnessContext, type DoomSelectionConfig, type EditorConfig, FILE_ONLY_STATE_FIELDS, GLOBAL_DOOM_SEED_FILES, type GlobalDoomInitResult, HARNESS_STATE_KEYS, HARNESS_STATE_POINTER, type HarnessState, type HarnessStateFile, type HarnessStateParseReporter, type IDoomConfigService, LayerCustomizationConfig, LayerDefinition, LayerPackage, LayerResolvers, type LoadedHarnessState, MajorModesConfig, PERSONA_FILES, type PiConfig, type PiConfigLoadOptions, type PiConfigPaths, type PlanningAgentConfig, type PlanningModeConfig, type PlanningThinkingLevel, PluginEntry, type PluginHookSource, type ProjectTrust, ResolvedDomain, ResolvedLayerDefinition, type ResolvedVoiceAutoCaptureConfig, type ResolvedVoiceConfig, SubagentLayerConfig, SubagentLayerModelConfig, type VoiceAdapterConfig, type VoiceAutoCaptureConfig, type VoiceConfig, type VoiceEngine, type VoiceModelConfig, type VoiceTtsConfig, type VoiceTtsEngine, type WriteDoomConfigOptions, appendDoomConfigSelection, applyProfileEnvironment, buildPersonaPrompt, createDoomConfigContainer, createDoomConfigContext, createDoomConfigModule, createHarnessSession, disposeHarnessState, domainCompletionItems, domainCompletionPrefix, expandDomainNames, getHarnessState, globalDoomConfigDirectory, globalDoomConfigPath, harnessRoot, initializeGlobalDoomConfig, isLocalPackageSpecifier, layerEntries, layerHookGroups, listDomainNames, listProfileNames, loadDomains, loadDoomConfig, loadHarnessState, loadMajorModesConfig, loadPiConfig, loadProfiles, mergeDoomConfigs, parseDoomConfig, parsePlanningModeConfig, piConfigPaths, projectHarnessEnvironment, provideDoomConfigContext, readDoomConfigSelection, readHarnessState, replaceProfileEnvironment, repositoryDoomConfigPath, requireDoomConfigContext, requireHarnessPaths, requireHarnessRoot, resetHarnessStore, resolveLayerCustomization, resolveLayers, resolvePlanningPlansDirectory, resolvePluginDirectories, resolvePluginEntries, resolveProfile, resolveSharedSkills, resolveVoiceConfig, setDoomConfigValue, unsetDoomConfigValue, updateHarnessState, writeDoomConfigValues, writePrivateAtomicJson };
@@ -0,0 +1,16 @@
1
+ import { writePrivateAtomicJson } from "./atomicJson.mjs";
2
+ import { DOOM_CONFIG_TYPES, DeepReadonly, DoomConfig, DoomConfigContext, DoomHarnessContext, DoomSelectionConfig, EditorConfig, HarnessState, IDoomConfigService, PlanningAgentConfig, PlanningModeConfig, PlanningThinkingLevel, PluginHookSource, ProjectTrust, ResolvedVoiceAutoCaptureConfig, ResolvedVoiceConfig, VoiceAdapterConfig, VoiceAutoCaptureConfig, VoiceConfig, VoiceEngine, VoiceModelConfig, VoiceTtsConfig, VoiceTtsEngine } from "./types.mjs";
3
+ import { DOOM_PLANNING_THINKING_LEVELS, DOOM_VOICE_ENGINES, DOOM_VOICE_TTS_ENGINES, globalDoomConfigDirectory, globalDoomConfigPath, loadDoomConfig, mergeDoomConfigs, parseDoomConfig, parsePlanningModeConfig, repositoryDoomConfigPath, resolvePlanningPlansDirectory, resolveVoiceConfig } from "./config.mjs";
4
+ import { ConfigAdapterRegistrationOptions, PiConfig, PiConfigLoadOptions, PiConfigPaths } from "./config/schema.mjs";
5
+ import { loadPiConfig, piConfigPaths } from "./config/piConfig.mjs";
6
+ import { DoomConfigEdit, WriteDoomConfigOptions, setDoomConfigValue, unsetDoomConfigValue, writeDoomConfigValues } from "./configWriter.mjs";
7
+ import { DoomConfigService, createDoomConfigContainer, createDoomConfigModule } from "./container.mjs";
8
+ import { DOOM_DIR } from "./layeredConfig.mjs";
9
+ import { DomainDefinition, DomainManifest, DomainMcpAllowlist, DomainPlugin, PluginEntry, ResolvedDomain, domainCompletionItems, domainCompletionPrefix, expandDomainNames, listDomainNames, loadDomains, resolvePluginDirectories, resolvePluginEntries, resolveSharedSkills } from "./domains.mjs";
10
+ import { FILE_ONLY_STATE_FIELDS, HARNESS_STATE_KEYS, HarnessStateParseReporter, projectHarnessEnvironment, readHarnessState } from "./harnessState.mjs";
11
+ import { HARNESS_STATE_POINTER, HarnessStateFile, LoadedHarnessState, createHarnessSession, disposeHarnessState, getHarnessState, harnessRoot, loadHarnessState, requireHarnessPaths, requireHarnessRoot, resetHarnessStore, updateHarnessState } from "./harnessStore.mjs";
12
+ import { DOOM_CONFIG_TEMPLATES, GLOBAL_DOOM_SEED_FILES, GlobalDoomInitResult, initializeGlobalDoomConfig } from "./init.mjs";
13
+ import { LayerCustomizationConfig, LayerDefinition, LayerPackage, LayerResolvers, MajorModesConfig, ResolvedLayerDefinition, SubagentLayerConfig, SubagentLayerModelConfig, isLocalPackageSpecifier, layerEntries, layerHookGroups, loadMajorModesConfig, resolveLayerCustomization, resolveLayers } from "./majorModes.mjs";
14
+ import { DOOM_CONFIG_ENTRY_TYPE, DoomConfigSelection, appendDoomConfigSelection, createDoomConfigContext, provideDoomConfigContext, readDoomConfigSelection, requireDoomConfigContext } from "./piContext.mjs";
15
+ import { AgentProfile, PERSONA_FILES, applyProfileEnvironment, buildPersonaPrompt, listProfileNames, loadProfiles, replaceProfileEnvironment, resolveProfile } from "./profiles.mjs";
16
+ export { AgentProfile, type ConfigAdapterRegistrationOptions, DOOM_CONFIG_ENTRY_TYPE, DOOM_CONFIG_TEMPLATES, DOOM_CONFIG_TYPES, DOOM_DIR, DOOM_PLANNING_THINKING_LEVELS, DOOM_VOICE_ENGINES, DOOM_VOICE_TTS_ENGINES, type DeepReadonly, DomainDefinition, DomainManifest, DomainMcpAllowlist, DomainPlugin, type DoomConfig, type DoomConfigContext, type DoomConfigEdit, type DoomConfigSelection, DoomConfigService, type DoomHarnessContext, type DoomSelectionConfig, type EditorConfig, FILE_ONLY_STATE_FIELDS, GLOBAL_DOOM_SEED_FILES, type GlobalDoomInitResult, HARNESS_STATE_KEYS, HARNESS_STATE_POINTER, type HarnessState, type HarnessStateFile, type HarnessStateParseReporter, type IDoomConfigService, LayerCustomizationConfig, LayerDefinition, LayerPackage, LayerResolvers, type LoadedHarnessState, MajorModesConfig, PERSONA_FILES, type PiConfig, type PiConfigLoadOptions, type PiConfigPaths, type PlanningAgentConfig, type PlanningModeConfig, type PlanningThinkingLevel, PluginEntry, type PluginHookSource, type ProjectTrust, ResolvedDomain, ResolvedLayerDefinition, type ResolvedVoiceAutoCaptureConfig, type ResolvedVoiceConfig, SubagentLayerConfig, SubagentLayerModelConfig, type VoiceAdapterConfig, type VoiceAutoCaptureConfig, type VoiceConfig, type VoiceEngine, type VoiceModelConfig, type VoiceTtsConfig, type VoiceTtsEngine, type WriteDoomConfigOptions, appendDoomConfigSelection, applyProfileEnvironment, buildPersonaPrompt, createDoomConfigContainer, createDoomConfigContext, createDoomConfigModule, createHarnessSession, disposeHarnessState, domainCompletionItems, domainCompletionPrefix, expandDomainNames, getHarnessState, globalDoomConfigDirectory, globalDoomConfigPath, harnessRoot, initializeGlobalDoomConfig, isLocalPackageSpecifier, layerEntries, layerHookGroups, listDomainNames, listProfileNames, loadDomains, loadDoomConfig, loadHarnessState, loadMajorModesConfig, loadPiConfig, loadProfiles, mergeDoomConfigs, parseDoomConfig, parsePlanningModeConfig, piConfigPaths, projectHarnessEnvironment, provideDoomConfigContext, readDoomConfigSelection, readHarnessState, replaceProfileEnvironment, repositoryDoomConfigPath, requireDoomConfigContext, requireHarnessPaths, requireHarnessRoot, resetHarnessStore, resolveLayerCustomization, resolveLayers, resolvePlanningPlansDirectory, resolvePluginDirectories, resolvePluginEntries, resolveProfile, resolveSharedSkills, resolveVoiceConfig, setDoomConfigValue, unsetDoomConfigValue, updateHarnessState, writeDoomConfigValues, writePrivateAtomicJson };
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ import{DOOM_PLANNING_THINKING_LEVELS as e,DOOM_VOICE_ENGINES as t,DOOM_VOICE_TTS_ENGINES as n,globalDoomConfigDirectory as r,globalDoomConfigPath as i,loadDoomConfig as a,mergeDoomConfigs as o,parseDoomConfig as s,parsePlanningModeConfig as c,repositoryDoomConfigPath as l,resolvePlanningPlansDirectory as u,resolveVoiceConfig as d}from"./config.mjs";import{setDoomConfigValue as f,unsetDoomConfigValue as p,writeDoomConfigValues as m}from"./configWriter.mjs";import{DOOM_CONFIG_TEMPLATES as h,GLOBAL_DOOM_SEED_FILES as g,initializeGlobalDoomConfig as _}from"./init.mjs";import{DOOM_CONFIG_TYPES as v}from"./types.mjs";import{DoomConfigService as y,createDoomConfigContainer as b,createDoomConfigModule as x}from"./container.mjs";import{DOOM_DIR as S}from"./layeredConfig.mjs";import{domainCompletionItems as C,domainCompletionPrefix as w,expandDomainNames as T,listDomainNames as E,loadDomains as D,resolvePluginDirectories as O,resolvePluginEntries as k,resolveSharedSkills as A}from"./domains.mjs";import{loadPiConfig as j,piConfigPaths as M}from"./config/piConfig.mjs";import{writePrivateAtomicJson as N}from"./atomicJson.mjs";import{FILE_ONLY_STATE_FIELDS as P,HARNESS_STATE_KEYS as F,projectHarnessEnvironment as I,readHarnessState as L}from"./harnessState.mjs";import{HARNESS_STATE_POINTER as R,createHarnessSession as z,disposeHarnessState as B,getHarnessState as V,harnessRoot as H,loadHarnessState as U,requireHarnessPaths as W,requireHarnessRoot as G,resetHarnessStore as K,updateHarnessState as q}from"./harnessStore.mjs";import{isLocalPackageSpecifier as J,layerEntries as Y,layerHookGroups as X,loadMajorModesConfig as Z,resolveLayerCustomization as Q,resolveLayers as $}from"./majorModes.mjs";import{DOOM_CONFIG_ENTRY_TYPE as ee,appendDoomConfigSelection as te,createDoomConfigContext as ne,provideDoomConfigContext as re,readDoomConfigSelection as ie,requireDoomConfigContext as ae}from"./piContext.mjs";import{PERSONA_FILES as oe,applyProfileEnvironment as se,buildPersonaPrompt as ce,listProfileNames as le,loadProfiles as ue,replaceProfileEnvironment as de,resolveProfile as fe}from"./profiles.mjs";export{ee as DOOM_CONFIG_ENTRY_TYPE,h as DOOM_CONFIG_TEMPLATES,v as DOOM_CONFIG_TYPES,S as DOOM_DIR,e as DOOM_PLANNING_THINKING_LEVELS,t as DOOM_VOICE_ENGINES,n as DOOM_VOICE_TTS_ENGINES,y as DoomConfigService,P as FILE_ONLY_STATE_FIELDS,g as GLOBAL_DOOM_SEED_FILES,F as HARNESS_STATE_KEYS,R as HARNESS_STATE_POINTER,oe as PERSONA_FILES,te as appendDoomConfigSelection,se as applyProfileEnvironment,ce as buildPersonaPrompt,b as createDoomConfigContainer,ne as createDoomConfigContext,x as createDoomConfigModule,z as createHarnessSession,B as disposeHarnessState,C as domainCompletionItems,w as domainCompletionPrefix,T as expandDomainNames,V as getHarnessState,r as globalDoomConfigDirectory,i as globalDoomConfigPath,H as harnessRoot,_ as initializeGlobalDoomConfig,J as isLocalPackageSpecifier,Y as layerEntries,X as layerHookGroups,E as listDomainNames,le as listProfileNames,D as loadDomains,a as loadDoomConfig,U as loadHarnessState,Z as loadMajorModesConfig,j as loadPiConfig,ue as loadProfiles,o as mergeDoomConfigs,s as parseDoomConfig,c as parsePlanningModeConfig,M as piConfigPaths,I as projectHarnessEnvironment,re as provideDoomConfigContext,ie as readDoomConfigSelection,L as readHarnessState,de as replaceProfileEnvironment,l as repositoryDoomConfigPath,ae as requireDoomConfigContext,W as requireHarnessPaths,G as requireHarnessRoot,K as resetHarnessStore,Q as resolveLayerCustomization,$ as resolveLayers,u as resolvePlanningPlansDirectory,O as resolvePluginDirectories,k as resolvePluginEntries,fe as resolveProfile,A as resolveSharedSkills,d as resolveVoiceConfig,f as setDoomConfigValue,p as unsetDoomConfigValue,q as updateHarnessState,m as writeDoomConfigValues,N as writePrivateAtomicJson};
package/dist/init.cjs ADDED
@@ -0,0 +1,73 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});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
+ # tts:
12
+ # engine: macos-say
13
+ # voice: Samantha
14
+ # rate: 190
15
+ `,"modes.yaml":`# Seeded defaults. Every layer here resolves from what Doom Pi installs or
16
+ # from a public package, so a fresh install works with no extra setup.
17
+ #
18
+ # Layers and modes defined here apply to every repository. A repository's own
19
+ # .doom/modes.yaml is read after this file and replaces any layer or mode of the
20
+ # same name, so a repository can override one entry without restating the rest.
21
+ #
22
+ # A layer package is an npm specifier, or a path if it was never published:
23
+ # packages: ['./extensions/my-ext']
24
+ # A path is relative to the config that declares it, so it resolves beside this
25
+ # file here (~/.pi/.doom) and against the repository root in a repository copy.
26
+ layers:
27
+ guardrails:
28
+ # repositoryHooks ships with Doom Pi, so the layer steers the agent toward
29
+ # the governing skill and scaffolding before it writes.
30
+ extensions: [repositoryHooks]
31
+ hookGroups: [guardrails, scaffolding]
32
+ vibe-lint:
33
+ packages:
34
+ - '@agimon-ai/vibe-lint/extensions/pi'
35
+ team:
36
+ packages:
37
+ - '@agimon-ai/doompi-team/extensions/doom'
38
+ plan-mode:
39
+ packages:
40
+ - '@agimon-ai/doompi-plan/extensions/doom'
41
+ runner:
42
+ packages:
43
+ - '@agimon-ai/doompi-runner/extensions/pi'
44
+ style-system:
45
+ extensions: [styleSystem]
46
+ goal:
47
+ packages:
48
+ - '@narumitw/pi-goal/src/index.ts'
49
+ ask-user:
50
+ packages:
51
+ - '@juicesharp/rpiv-ask-user-question'
52
+ # copilot is the default when --major-mode is not passed. Add your own modes
53
+ # here; a mode is just a named set of the layers above.
54
+ majorMode:
55
+ minimal: [guardrails, team]
56
+ copilot: [guardrails, vibe-lint, team, plan-mode, runner, style-system, goal, ask-user]
57
+ `,"domains.yaml":`# A domain is a named set of plugin directories loaded for a session.
58
+ # Add your own alongside default; aliases map short names onto them.
59
+ #
60
+ # Each plugin path is written in full and is relative to the config that
61
+ # declares it, so it resolves beside this file here (~/.pi/.doom) and against
62
+ # the repository root in a repository copy:
63
+ # plugins: ['plugins/my-plugin']
64
+ # A repository's own .doom/domains.yaml is read after this file and replaces any
65
+ # domain of the same name.
66
+ domains:
67
+ default:
68
+ plugins: []
69
+ aliases: {}
70
+ `,"profiles.yaml":`profiles: {}
71
+ `,"hooks.yaml":`groups: {}
72
+ `};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;
73
+ //# sourceMappingURL=init.cjs.map