@agimon-ai/doompi 0.0.1-alpha.71 → 0.0.1-alpha.72

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.
@@ -1,4 +1,4 @@
1
- let _agimon_ai_doompi_core_web = require("@agimon-ai/doompi-core/web");
1
+ let _agimon_ai_doompi_core_bundle_asset_policy = require("@agimon-ai/doompi-core/bundle-asset-policy");
2
2
  //#region src/builders/web/bundleAssetPolicy.ts
3
3
  const OPTIONAL_PACKAGES = ["/node_modules/mermaid/", "/node_modules/pdfjs-dist/"];
4
4
  function packageOwner(output, owned) {
@@ -75,7 +75,7 @@ function bundleAssetPolicyPlugin() {
75
75
  name: "doompi-bundle-asset-policy",
76
76
  generateBundle(_options, bundle) {
77
77
  const policy = {
78
- version: _agimon_ai_doompi_core_web.BUNDLE_ASSET_POLICY_VERSION,
78
+ version: _agimon_ai_doompi_core_bundle_asset_policy.BUNDLE_ASSET_POLICY_VERSION,
79
79
  optional: classifyOptionalBundleAssets(bundle, (id) => {
80
80
  const info = this.getModuleInfo(id);
81
81
  return info === null ? void 0 : [...info.importedIds, ...info.dynamicallyImportedIds];
@@ -83,7 +83,7 @@ function bundleAssetPolicyPlugin() {
83
83
  };
84
84
  this.emitFile({
85
85
  type: "asset",
86
- fileName: _agimon_ai_doompi_core_web.BUNDLE_ASSET_POLICY_PATH.slice(1),
86
+ fileName: _agimon_ai_doompi_core_bundle_asset_policy.BUNDLE_ASSET_POLICY_PATH.slice(1),
87
87
  source: `${JSON.stringify(policy)}\n`
88
88
  });
89
89
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bundleAssetPolicy.cjs","names":["BUNDLE_ASSET_POLICY_VERSION","BUNDLE_ASSET_POLICY_PATH"],"sources":["../../../src/builders/web/bundleAssetPolicy.ts"],"sourcesContent":["import {\n BUNDLE_ASSET_POLICY_PATH,\n BUNDLE_ASSET_POLICY_VERSION,\n type BundleAssetPolicy,\n} from '@agimon-ai/doompi-core/web';\nimport type { Plugin } from 'vite';\n\nconst OPTIONAL_PACKAGES = ['/node_modules/mermaid/', '/node_modules/pdfjs-dist/'] as const;\n\ninterface PolicyOutput {\n type: 'asset' | 'chunk';\n isEntry?: boolean;\n facadeModuleId?: string | null;\n modules?: Record<string, unknown>;\n imports?: string[];\n dynamicImports?: string[];\n referencedFiles?: string[];\n code?: string;\n source?: string | Uint8Array;\n viteMetadata?: { importedAssets?: Set<string> };\n}\n\nfunction packageOwner(output: PolicyOutput, owned: ReadonlyMap<string, ReadonlySet<string>>): string | undefined {\n const ids = Object.keys(output.modules ?? {});\n return OPTIONAL_PACKAGES.find(\n (dependency) =>\n ids.some((id) => id.replaceAll('\\\\', '/').includes(dependency)) &&\n ids.every((id) => owned.get(dependency)?.has(id)),\n );\n}\n\nfunction references(output: PolicyOutput): string[] {\n if (output.type === 'asset') return [];\n return [\n ...(output.imports ?? []),\n ...(output.dynamicImports ?? []),\n ...(output.referencedFiles ?? []),\n ...(output.viteMetadata?.importedAssets ?? []),\n ];\n}\n\n/** Classifies only output owned exclusively by a known optional dependency graph. */\nexport function classifyOptionalBundleAssets(\n bundle: Readonly<Record<string, PolicyOutput>>,\n moduleDependencies?: (id: string) => readonly string[] | undefined,\n): string[] {\n const outputs = new Map(Object.entries(bundle));\n const owned = new Map<string, Set<string>>();\n for (const dependency of OPTIONAL_PACKAGES) {\n const modules = new Set<string>();\n const visit = (id: string): void => {\n // Application and unresolved virtual modules never inherit optional ownership.\n if (modules.has(id) || !id.replaceAll('\\\\', '/').includes('/node_modules/')) return;\n modules.add(id);\n for (const imported of moduleDependencies?.(id) ?? []) visit(imported);\n };\n for (const output of outputs.values()) {\n for (const id of Object.keys(output.modules ?? {})) {\n if (id.replaceAll('\\\\', '/').includes(dependency)) visit(id);\n }\n }\n owned.set(dependency, modules);\n }\n const graphReferences = (output: PolicyOutput): string[] => {\n const source =\n output.code ??\n (typeof output.source === 'string'\n ? output.source\n : output.source === undefined\n ? ''\n : new TextDecoder().decode(output.source));\n return [\n ...references(output),\n ...[...outputs]\n .filter(([fileName, candidate]) => candidate.type === 'asset' && source.includes(fileName))\n .map(([fileName]) => fileName),\n ];\n };\n const optionalRoots = new Map<string, string>();\n for (const [fileName, output] of outputs) {\n if (output.type !== 'chunk') continue;\n const owner = packageOwner(output, owned);\n const facade = output.facadeModuleId?.replaceAll('\\\\', '/');\n if (\n owner !== undefined &&\n (!output.isEntry || OPTIONAL_PACKAGES.some((dependency) => facade?.includes(dependency)))\n ) {\n optionalRoots.set(fileName, owner);\n }\n }\n\n const eager = new Set<string>();\n const visitEager = (fileName: string): void => {\n if (eager.has(fileName)) return;\n eager.add(fileName);\n const output = outputs.get(fileName);\n if (output === undefined) return;\n for (const reference of graphReferences(output)) {\n const deferredRoot = optionalRoots.has(reference) && output.dynamicImports?.includes(reference);\n if (!deferredRoot) visitEager(reference);\n }\n };\n for (const [fileName, output] of outputs) {\n if (fileName === 'index.html' || (output.type === 'chunk' && output.isEntry && !optionalRoots.has(fileName)))\n visitEager(fileName);\n }\n\n const optional = new Set<string>();\n const visitOptional = (fileName: string, owner: string): void => {\n if (eager.has(fileName) || optional.has(fileName)) return;\n const output = outputs.get(fileName);\n if (output === undefined) return;\n if (output.type === 'chunk') {\n const ids = Object.keys(output.modules ?? {});\n if (ids.length === 0 || !ids.every((id) => owned.get(owner)?.has(id))) {\n visitEager(fileName);\n return;\n }\n }\n optional.add(fileName);\n for (const reference of graphReferences(output)) {\n const referencedRootOwner = optionalRoots.get(reference);\n if (referencedRootOwner === undefined || referencedRootOwner === owner) visitOptional(reference, owner);\n }\n };\n for (const [fileName, owner] of optionalRoots) visitOptional(fileName, owner);\n return [...optional]\n .filter((fileName) => !eager.has(fileName) && !fileName.endsWith('.map'))\n .map((fileName) => `/${fileName}`)\n .sort();\n}\n\n/** Emits an authenticated v1 policy derived from Rollup's module and resource graph. */\nexport function bundleAssetPolicyPlugin(): Plugin {\n return {\n name: 'doompi-bundle-asset-policy',\n generateBundle(_options, bundle) {\n const policy: BundleAssetPolicy = {\n version: BUNDLE_ASSET_POLICY_VERSION,\n optional: classifyOptionalBundleAssets(bundle, (id) => {\n const info = this.getModuleInfo(id);\n return info === null ? undefined : [...info.importedIds, ...info.dynamicallyImportedIds];\n }),\n };\n this.emitFile({\n type: 'asset',\n fileName: BUNDLE_ASSET_POLICY_PATH.slice(1),\n source: `${JSON.stringify(policy)}\\n`,\n });\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"bundleAssetPolicy.cjs","names":["BUNDLE_ASSET_POLICY_VERSION","BUNDLE_ASSET_POLICY_PATH"],"sources":["../../../src/builders/web/bundleAssetPolicy.ts"],"sourcesContent":["import {\n BUNDLE_ASSET_POLICY_PATH,\n BUNDLE_ASSET_POLICY_VERSION,\n type BundleAssetPolicy,\n} from '@agimon-ai/doompi-core/bundle-asset-policy';\nimport type { Plugin } from 'vite';\n\nconst OPTIONAL_PACKAGES = ['/node_modules/mermaid/', '/node_modules/pdfjs-dist/'] as const;\n\ninterface PolicyOutput {\n type: 'asset' | 'chunk';\n isEntry?: boolean;\n facadeModuleId?: string | null;\n modules?: Record<string, unknown>;\n imports?: string[];\n dynamicImports?: string[];\n referencedFiles?: string[];\n code?: string;\n source?: string | Uint8Array;\n viteMetadata?: { importedAssets?: Set<string> };\n}\n\nfunction packageOwner(output: PolicyOutput, owned: ReadonlyMap<string, ReadonlySet<string>>): string | undefined {\n const ids = Object.keys(output.modules ?? {});\n return OPTIONAL_PACKAGES.find(\n (dependency) =>\n ids.some((id) => id.replaceAll('\\\\', '/').includes(dependency)) &&\n ids.every((id) => owned.get(dependency)?.has(id)),\n );\n}\n\nfunction references(output: PolicyOutput): string[] {\n if (output.type === 'asset') return [];\n return [\n ...(output.imports ?? []),\n ...(output.dynamicImports ?? []),\n ...(output.referencedFiles ?? []),\n ...(output.viteMetadata?.importedAssets ?? []),\n ];\n}\n\n/** Classifies only output owned exclusively by a known optional dependency graph. */\nexport function classifyOptionalBundleAssets(\n bundle: Readonly<Record<string, PolicyOutput>>,\n moduleDependencies?: (id: string) => readonly string[] | undefined,\n): string[] {\n const outputs = new Map(Object.entries(bundle));\n const owned = new Map<string, Set<string>>();\n for (const dependency of OPTIONAL_PACKAGES) {\n const modules = new Set<string>();\n const visit = (id: string): void => {\n // Application and unresolved virtual modules never inherit optional ownership.\n if (modules.has(id) || !id.replaceAll('\\\\', '/').includes('/node_modules/')) return;\n modules.add(id);\n for (const imported of moduleDependencies?.(id) ?? []) visit(imported);\n };\n for (const output of outputs.values()) {\n for (const id of Object.keys(output.modules ?? {})) {\n if (id.replaceAll('\\\\', '/').includes(dependency)) visit(id);\n }\n }\n owned.set(dependency, modules);\n }\n const graphReferences = (output: PolicyOutput): string[] => {\n const source =\n output.code ??\n (typeof output.source === 'string'\n ? output.source\n : output.source === undefined\n ? ''\n : new TextDecoder().decode(output.source));\n return [\n ...references(output),\n ...[...outputs]\n .filter(([fileName, candidate]) => candidate.type === 'asset' && source.includes(fileName))\n .map(([fileName]) => fileName),\n ];\n };\n const optionalRoots = new Map<string, string>();\n for (const [fileName, output] of outputs) {\n if (output.type !== 'chunk') continue;\n const owner = packageOwner(output, owned);\n const facade = output.facadeModuleId?.replaceAll('\\\\', '/');\n if (\n owner !== undefined &&\n (!output.isEntry || OPTIONAL_PACKAGES.some((dependency) => facade?.includes(dependency)))\n ) {\n optionalRoots.set(fileName, owner);\n }\n }\n\n const eager = new Set<string>();\n const visitEager = (fileName: string): void => {\n if (eager.has(fileName)) return;\n eager.add(fileName);\n const output = outputs.get(fileName);\n if (output === undefined) return;\n for (const reference of graphReferences(output)) {\n const deferredRoot = optionalRoots.has(reference) && output.dynamicImports?.includes(reference);\n if (!deferredRoot) visitEager(reference);\n }\n };\n for (const [fileName, output] of outputs) {\n if (fileName === 'index.html' || (output.type === 'chunk' && output.isEntry && !optionalRoots.has(fileName)))\n visitEager(fileName);\n }\n\n const optional = new Set<string>();\n const visitOptional = (fileName: string, owner: string): void => {\n if (eager.has(fileName) || optional.has(fileName)) return;\n const output = outputs.get(fileName);\n if (output === undefined) return;\n if (output.type === 'chunk') {\n const ids = Object.keys(output.modules ?? {});\n if (ids.length === 0 || !ids.every((id) => owned.get(owner)?.has(id))) {\n visitEager(fileName);\n return;\n }\n }\n optional.add(fileName);\n for (const reference of graphReferences(output)) {\n const referencedRootOwner = optionalRoots.get(reference);\n if (referencedRootOwner === undefined || referencedRootOwner === owner) visitOptional(reference, owner);\n }\n };\n for (const [fileName, owner] of optionalRoots) visitOptional(fileName, owner);\n return [...optional]\n .filter((fileName) => !eager.has(fileName) && !fileName.endsWith('.map'))\n .map((fileName) => `/${fileName}`)\n .sort();\n}\n\n/** Emits an authenticated v1 policy derived from Rollup's module and resource graph. */\nexport function bundleAssetPolicyPlugin(): Plugin {\n return {\n name: 'doompi-bundle-asset-policy',\n generateBundle(_options, bundle) {\n const policy: BundleAssetPolicy = {\n version: BUNDLE_ASSET_POLICY_VERSION,\n optional: classifyOptionalBundleAssets(bundle, (id) => {\n const info = this.getModuleInfo(id);\n return info === null ? undefined : [...info.importedIds, ...info.dynamicallyImportedIds];\n }),\n };\n this.emitFile({\n type: 'asset',\n fileName: BUNDLE_ASSET_POLICY_PATH.slice(1),\n source: `${JSON.stringify(policy)}\\n`,\n });\n },\n };\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { BUNDLE_ASSET_POLICY_PATH, BUNDLE_ASSET_POLICY_VERSION } from "@agimon-ai/doompi-core/web";
1
+ import { BUNDLE_ASSET_POLICY_PATH, BUNDLE_ASSET_POLICY_VERSION } from "@agimon-ai/doompi-core/bundle-asset-policy";
2
2
  //#region src/builders/web/bundleAssetPolicy.ts
3
3
  const OPTIONAL_PACKAGES = ["/node_modules/mermaid/", "/node_modules/pdfjs-dist/"];
4
4
  function packageOwner(output, owned) {
@@ -1 +1 @@
1
- {"version":3,"file":"bundleAssetPolicy.mjs","names":[],"sources":["../../../src/builders/web/bundleAssetPolicy.ts"],"sourcesContent":["import {\n BUNDLE_ASSET_POLICY_PATH,\n BUNDLE_ASSET_POLICY_VERSION,\n type BundleAssetPolicy,\n} from '@agimon-ai/doompi-core/web';\nimport type { Plugin } from 'vite';\n\nconst OPTIONAL_PACKAGES = ['/node_modules/mermaid/', '/node_modules/pdfjs-dist/'] as const;\n\ninterface PolicyOutput {\n type: 'asset' | 'chunk';\n isEntry?: boolean;\n facadeModuleId?: string | null;\n modules?: Record<string, unknown>;\n imports?: string[];\n dynamicImports?: string[];\n referencedFiles?: string[];\n code?: string;\n source?: string | Uint8Array;\n viteMetadata?: { importedAssets?: Set<string> };\n}\n\nfunction packageOwner(output: PolicyOutput, owned: ReadonlyMap<string, ReadonlySet<string>>): string | undefined {\n const ids = Object.keys(output.modules ?? {});\n return OPTIONAL_PACKAGES.find(\n (dependency) =>\n ids.some((id) => id.replaceAll('\\\\', '/').includes(dependency)) &&\n ids.every((id) => owned.get(dependency)?.has(id)),\n );\n}\n\nfunction references(output: PolicyOutput): string[] {\n if (output.type === 'asset') return [];\n return [\n ...(output.imports ?? []),\n ...(output.dynamicImports ?? []),\n ...(output.referencedFiles ?? []),\n ...(output.viteMetadata?.importedAssets ?? []),\n ];\n}\n\n/** Classifies only output owned exclusively by a known optional dependency graph. */\nexport function classifyOptionalBundleAssets(\n bundle: Readonly<Record<string, PolicyOutput>>,\n moduleDependencies?: (id: string) => readonly string[] | undefined,\n): string[] {\n const outputs = new Map(Object.entries(bundle));\n const owned = new Map<string, Set<string>>();\n for (const dependency of OPTIONAL_PACKAGES) {\n const modules = new Set<string>();\n const visit = (id: string): void => {\n // Application and unresolved virtual modules never inherit optional ownership.\n if (modules.has(id) || !id.replaceAll('\\\\', '/').includes('/node_modules/')) return;\n modules.add(id);\n for (const imported of moduleDependencies?.(id) ?? []) visit(imported);\n };\n for (const output of outputs.values()) {\n for (const id of Object.keys(output.modules ?? {})) {\n if (id.replaceAll('\\\\', '/').includes(dependency)) visit(id);\n }\n }\n owned.set(dependency, modules);\n }\n const graphReferences = (output: PolicyOutput): string[] => {\n const source =\n output.code ??\n (typeof output.source === 'string'\n ? output.source\n : output.source === undefined\n ? ''\n : new TextDecoder().decode(output.source));\n return [\n ...references(output),\n ...[...outputs]\n .filter(([fileName, candidate]) => candidate.type === 'asset' && source.includes(fileName))\n .map(([fileName]) => fileName),\n ];\n };\n const optionalRoots = new Map<string, string>();\n for (const [fileName, output] of outputs) {\n if (output.type !== 'chunk') continue;\n const owner = packageOwner(output, owned);\n const facade = output.facadeModuleId?.replaceAll('\\\\', '/');\n if (\n owner !== undefined &&\n (!output.isEntry || OPTIONAL_PACKAGES.some((dependency) => facade?.includes(dependency)))\n ) {\n optionalRoots.set(fileName, owner);\n }\n }\n\n const eager = new Set<string>();\n const visitEager = (fileName: string): void => {\n if (eager.has(fileName)) return;\n eager.add(fileName);\n const output = outputs.get(fileName);\n if (output === undefined) return;\n for (const reference of graphReferences(output)) {\n const deferredRoot = optionalRoots.has(reference) && output.dynamicImports?.includes(reference);\n if (!deferredRoot) visitEager(reference);\n }\n };\n for (const [fileName, output] of outputs) {\n if (fileName === 'index.html' || (output.type === 'chunk' && output.isEntry && !optionalRoots.has(fileName)))\n visitEager(fileName);\n }\n\n const optional = new Set<string>();\n const visitOptional = (fileName: string, owner: string): void => {\n if (eager.has(fileName) || optional.has(fileName)) return;\n const output = outputs.get(fileName);\n if (output === undefined) return;\n if (output.type === 'chunk') {\n const ids = Object.keys(output.modules ?? {});\n if (ids.length === 0 || !ids.every((id) => owned.get(owner)?.has(id))) {\n visitEager(fileName);\n return;\n }\n }\n optional.add(fileName);\n for (const reference of graphReferences(output)) {\n const referencedRootOwner = optionalRoots.get(reference);\n if (referencedRootOwner === undefined || referencedRootOwner === owner) visitOptional(reference, owner);\n }\n };\n for (const [fileName, owner] of optionalRoots) visitOptional(fileName, owner);\n return [...optional]\n .filter((fileName) => !eager.has(fileName) && !fileName.endsWith('.map'))\n .map((fileName) => `/${fileName}`)\n .sort();\n}\n\n/** Emits an authenticated v1 policy derived from Rollup's module and resource graph. */\nexport function bundleAssetPolicyPlugin(): Plugin {\n return {\n name: 'doompi-bundle-asset-policy',\n generateBundle(_options, bundle) {\n const policy: BundleAssetPolicy = {\n version: BUNDLE_ASSET_POLICY_VERSION,\n optional: classifyOptionalBundleAssets(bundle, (id) => {\n const info = this.getModuleInfo(id);\n return info === null ? undefined : [...info.importedIds, ...info.dynamicallyImportedIds];\n }),\n };\n this.emitFile({\n type: 'asset',\n fileName: BUNDLE_ASSET_POLICY_PATH.slice(1),\n source: `${JSON.stringify(policy)}\\n`,\n });\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"bundleAssetPolicy.mjs","names":[],"sources":["../../../src/builders/web/bundleAssetPolicy.ts"],"sourcesContent":["import {\n BUNDLE_ASSET_POLICY_PATH,\n BUNDLE_ASSET_POLICY_VERSION,\n type BundleAssetPolicy,\n} from '@agimon-ai/doompi-core/bundle-asset-policy';\nimport type { Plugin } from 'vite';\n\nconst OPTIONAL_PACKAGES = ['/node_modules/mermaid/', '/node_modules/pdfjs-dist/'] as const;\n\ninterface PolicyOutput {\n type: 'asset' | 'chunk';\n isEntry?: boolean;\n facadeModuleId?: string | null;\n modules?: Record<string, unknown>;\n imports?: string[];\n dynamicImports?: string[];\n referencedFiles?: string[];\n code?: string;\n source?: string | Uint8Array;\n viteMetadata?: { importedAssets?: Set<string> };\n}\n\nfunction packageOwner(output: PolicyOutput, owned: ReadonlyMap<string, ReadonlySet<string>>): string | undefined {\n const ids = Object.keys(output.modules ?? {});\n return OPTIONAL_PACKAGES.find(\n (dependency) =>\n ids.some((id) => id.replaceAll('\\\\', '/').includes(dependency)) &&\n ids.every((id) => owned.get(dependency)?.has(id)),\n );\n}\n\nfunction references(output: PolicyOutput): string[] {\n if (output.type === 'asset') return [];\n return [\n ...(output.imports ?? []),\n ...(output.dynamicImports ?? []),\n ...(output.referencedFiles ?? []),\n ...(output.viteMetadata?.importedAssets ?? []),\n ];\n}\n\n/** Classifies only output owned exclusively by a known optional dependency graph. */\nexport function classifyOptionalBundleAssets(\n bundle: Readonly<Record<string, PolicyOutput>>,\n moduleDependencies?: (id: string) => readonly string[] | undefined,\n): string[] {\n const outputs = new Map(Object.entries(bundle));\n const owned = new Map<string, Set<string>>();\n for (const dependency of OPTIONAL_PACKAGES) {\n const modules = new Set<string>();\n const visit = (id: string): void => {\n // Application and unresolved virtual modules never inherit optional ownership.\n if (modules.has(id) || !id.replaceAll('\\\\', '/').includes('/node_modules/')) return;\n modules.add(id);\n for (const imported of moduleDependencies?.(id) ?? []) visit(imported);\n };\n for (const output of outputs.values()) {\n for (const id of Object.keys(output.modules ?? {})) {\n if (id.replaceAll('\\\\', '/').includes(dependency)) visit(id);\n }\n }\n owned.set(dependency, modules);\n }\n const graphReferences = (output: PolicyOutput): string[] => {\n const source =\n output.code ??\n (typeof output.source === 'string'\n ? output.source\n : output.source === undefined\n ? ''\n : new TextDecoder().decode(output.source));\n return [\n ...references(output),\n ...[...outputs]\n .filter(([fileName, candidate]) => candidate.type === 'asset' && source.includes(fileName))\n .map(([fileName]) => fileName),\n ];\n };\n const optionalRoots = new Map<string, string>();\n for (const [fileName, output] of outputs) {\n if (output.type !== 'chunk') continue;\n const owner = packageOwner(output, owned);\n const facade = output.facadeModuleId?.replaceAll('\\\\', '/');\n if (\n owner !== undefined &&\n (!output.isEntry || OPTIONAL_PACKAGES.some((dependency) => facade?.includes(dependency)))\n ) {\n optionalRoots.set(fileName, owner);\n }\n }\n\n const eager = new Set<string>();\n const visitEager = (fileName: string): void => {\n if (eager.has(fileName)) return;\n eager.add(fileName);\n const output = outputs.get(fileName);\n if (output === undefined) return;\n for (const reference of graphReferences(output)) {\n const deferredRoot = optionalRoots.has(reference) && output.dynamicImports?.includes(reference);\n if (!deferredRoot) visitEager(reference);\n }\n };\n for (const [fileName, output] of outputs) {\n if (fileName === 'index.html' || (output.type === 'chunk' && output.isEntry && !optionalRoots.has(fileName)))\n visitEager(fileName);\n }\n\n const optional = new Set<string>();\n const visitOptional = (fileName: string, owner: string): void => {\n if (eager.has(fileName) || optional.has(fileName)) return;\n const output = outputs.get(fileName);\n if (output === undefined) return;\n if (output.type === 'chunk') {\n const ids = Object.keys(output.modules ?? {});\n if (ids.length === 0 || !ids.every((id) => owned.get(owner)?.has(id))) {\n visitEager(fileName);\n return;\n }\n }\n optional.add(fileName);\n for (const reference of graphReferences(output)) {\n const referencedRootOwner = optionalRoots.get(reference);\n if (referencedRootOwner === undefined || referencedRootOwner === owner) visitOptional(reference, owner);\n }\n };\n for (const [fileName, owner] of optionalRoots) visitOptional(fileName, owner);\n return [...optional]\n .filter((fileName) => !eager.has(fileName) && !fileName.endsWith('.map'))\n .map((fileName) => `/${fileName}`)\n .sort();\n}\n\n/** Emits an authenticated v1 policy derived from Rollup's module and resource graph. */\nexport function bundleAssetPolicyPlugin(): Plugin {\n return {\n name: 'doompi-bundle-asset-policy',\n generateBundle(_options, bundle) {\n const policy: BundleAssetPolicy = {\n version: BUNDLE_ASSET_POLICY_VERSION,\n optional: classifyOptionalBundleAssets(bundle, (id) => {\n const info = this.getModuleInfo(id);\n return info === null ? undefined : [...info.importedIds, ...info.dynamicallyImportedIds];\n }),\n };\n this.emitFile({\n type: 'asset',\n fileName: BUNDLE_ASSET_POLICY_PATH.slice(1),\n source: `${JSON.stringify(policy)}\\n`,\n });\n },\n };\n}\n"],"mappings":"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"}
@@ -19,7 +19,6 @@ const require_index$10 = require("../../../composition/syncDrift/index.cjs");
19
19
  const require_presenter = require("./presenter.cjs");
20
20
  let node_fs = require("node:fs");
21
21
  node_fs = require_runtime.__toESM(node_fs, 1);
22
- let node_module = require("node:module");
23
22
  let node_path = require("node:path");
24
23
  node_path = require_runtime.__toESM(node_path, 1);
25
24
  let _agimon_ai_doompi_core_sync_registration = require("@agimon-ai/doompi-core/sync-registration");
@@ -33,7 +32,6 @@ let _agimon_ai_doompi_core_runtime_pi_settings = require("@agimon-ai/doompi-core
33
32
  let node_crypto = require("node:crypto");
34
33
  node_crypto = require_runtime.__toESM(node_crypto, 1);
35
34
  let _agimon_ai_doompi_core_sync_location = require("@agimon-ai/doompi-core/sync-location");
36
- let _agimon_ai_doompi_core_doom_package = require("@agimon-ai/doompi-core/doom-package");
37
35
  let _agimon_ai_doompi_core_server_facet = require("@agimon-ai/doompi-core/server-facet");
38
36
  let _agimon_ai_doompi_ui_theme = require("@agimon-ai/doompi-ui/theme");
39
37
  let _agimon_ai_doompi_config_harnessStore = require("@agimon-ai/doompi-config/harnessStore");
@@ -226,23 +224,21 @@ function formatSyncResult(result, runner = "pi") {
226
224
  ].join("\n");
227
225
  }
228
226
  /**
229
- * The DoomPi package a repository pins for itself, if it pins one.
227
+ * The DoomPi that produced this generation, which is the one that can load it.
230
228
  *
231
- * Extensions are version-coupled to the harness that loads them, so the
232
- * registration must name the copy the repository resolves rather than whichever
233
- * copy happened to run sync. A globally installed DoomPi syncing a repository
234
- * that pins its own would otherwise record itself, and the dispatcher would
235
- * then load the wrong harness for every session in that repository.
229
+ * Always the executing package, never another copy the repository happens to
230
+ * install. A generation is not portable between two installations: the bundles
231
+ * are compiled from the building package's own extension entries, the recorded
232
+ * compiler inputs are its files, and the state names its bootstrap entry. Naming
233
+ * a second copy here publishes a registration whose package disagrees with the
234
+ * state it points at, and Pi's dispatcher then loads a harness that rejects the
235
+ * bootstrap as stale on every session, with no sync able to fix it.
236
+ *
237
+ * A repository that wants its own copy to own its sessions runs sync with that
238
+ * copy's CLI, which makes it the executing package.
236
239
  */
237
- function repositoryPackageRoot(repoRoot) {
238
- try {
239
- return node_path.default.dirname((0, node_module.createRequire)(node_path.default.join(repoRoot, "package.json")).resolve(`${_agimon_ai_doompi_core_doom_package.DOOM_PACKAGE_NAME}/package.json`));
240
- } catch {
241
- return;
242
- }
243
- }
244
- function packageRegistrationFor(repoRoot) {
245
- const root = node_fs.default.realpathSync(repositoryPackageRoot(repoRoot) ?? require_index$8.doomPiPackageRoot());
240
+ function packageRegistrationFor() {
241
+ const root = node_fs.default.realpathSync(require_index$8.doomPiPackageRoot());
246
242
  const manifestPath = node_path.default.join(root, "package.json");
247
243
  const manifest = JSON.parse(node_fs.default.readFileSync(manifestPath, "utf8"));
248
244
  const version = manifest.version;
@@ -512,7 +508,7 @@ async function stageSync(repoRoot, options, environment, homeDirectory, progress
512
508
  fingerprint,
513
509
  sha256: (0, _agimon_ai_doompi_core_sync_registration.syncStateSha256)(descriptorPath)
514
510
  },
515
- package: packageRegistrationFor(location.root)
511
+ package: packageRegistrationFor()
516
512
  }, homeDirectory);
517
513
  return result;
518
514
  } catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["path","HARNESS_STATE_POINTER","loadDoomConfigLenient","DOOMPI_MAJOR_MODE_ENV","DOOMPI_PROFILE_ENV","DOOMPI_DOMAINS_ENV","os","loadMajorModesConfig","createLayerResolvers","resolveExtensionComposition","PERSONA_ENTRY","filterHookDisabledLayers","resolveLayers","piThemeDirectory","DEFAULT_THEME_NAME","readPiSettings","mergePiSettings","serializePiSettings","piExtensionAliasIsCurrent","DEFAULT_THEME","fs","syncStateRootMatches","computeInputsHash","recordResolvedEntries","readBootstrapStatus","piAgentDirectory","projectRegistersDoom","DUPLICATE_REGISTRATION_DRIFT","readSyncDrift","spawnSync","createRequire","DOOM_PACKAGE_NAME","doomPiPackageRoot","globalDoomConfigDirectory","resolveDoomConfigurationRoot","loadMajorModesConfigLenient","loadDomains","parseHarnessArgs","piExtensionDispatcherIsUpgradeable","piExtensionDispatcherVersion","missingLayerPackageSpecifiers","readLocatedSyncState","SyncProgress","acquireSyncLocationLock","resolveSyncLocation","crypto","syncGenerationDirectory","buildHarnessContext","ensureLayerPackages","SYNC_STATE_VERSION","computeWebSourcesHash","loadHarnessState","buildSyncedRuntime","syncWebBundle","syncServerBundle","DOOM_SERVER_BUNDLE_FILE","computeServerSourcesHash","writeSyncState","writeProjectPiSettings","readMcpServerNames","SYNC_REGISTRATION_VERSION","syncStateSha256"],"sources":["../../../../src/cli/commands/sync/index.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { globalDoomConfigDirectory } from '@agimon-ai/doompi-config/config';\nimport { loadDomains } from '@agimon-ai/doompi-config/domains';\nimport { filterHookDisabledLayers, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport { loadMajorModesConfig, loadMajorModesConfigLenient } from '@agimon-ai/doompi-config/majorModes';\nimport type { ConfigDiagnostic } from '@agimon-ai/doompi-config/types';\nimport { DOOM_PACKAGE_NAME } from '@agimon-ai/doompi-core/doom-package';\nimport {\n mergePiSettings,\n piAgentDirectory,\n piThemeDirectory,\n readPiSettings,\n serializePiSettings,\n} from '@agimon-ai/doompi-core/runtime-pi-settings';\nimport { DOOM_SERVER_BUNDLE_FILE } from '@agimon-ai/doompi-core/server-facet';\nimport {\n acquireSyncLocationLock,\n resolveSyncLocation,\n syncGenerationDirectory,\n} from '@agimon-ai/doompi-core/sync-location';\nimport {\n publishSyncRegistration,\n SYNC_REGISTRATION_VERSION,\n syncStateSha256,\n type SyncPackageRegistration,\n} from '@agimon-ai/doompi-core/sync-registration';\nimport { DEFAULT_THEME, DEFAULT_THEME_NAME } from '@agimon-ai/doompi-ui/theme';\n\nimport { buildSyncedRuntime } from '../../../builders/cli';\nimport { readBootstrapStatus } from '../../../builders/cli/bootstrapLocator';\nimport {\n createLayerResolvers,\n type ExtensionComposition,\n PERSONA_ENTRY,\n resolveExtensionComposition,\n} from '../../../builders/cli/extensionAssembler';\nimport { buildHarnessContext } from '../../../builders/cli/harnessContext';\nimport {\n doomPiPackageRoot,\n piExtensionAliasIsCurrent,\n writePiExtensionAlias,\n} from '../../../builders/cli/piExtensionAlias';\nimport {\n PI_DISPATCHER_VERSION,\n piExtensionDispatcherIsUpgradeable,\n piExtensionDispatcherVersion,\n} from '../../../builders/cli/piExtensionDispatcher';\nimport {\n DUPLICATE_REGISTRATION_DRIFT,\n projectRegistersDoom,\n writeProjectPiSettings,\n} from '../../../builders/cli/projectSettings';\nimport { syncServerBundle } from '../../../builders/server';\nimport { syncWebBundle } from '../../../builders/web';\nimport { HARNESS_STATE_POINTER, loadHarnessState } from '../../../composition/harnessState';\nimport { ensureLayerPackages, missingLayerPackageSpecifiers } from '../../../composition/layerPackageInstaller';\nimport { loadDoomConfigLenient } from '../../../composition/projectTrust';\nimport { resolveDoomConfigurationRoot } from '../../../composition/repository';\nimport { readSyncDrift } from '../../../composition/syncDrift';\nimport {\n computeInputsHash,\n computeWebSourcesHash,\n computeServerSourcesHash,\n readLocatedSyncState,\n readMcpServerNames,\n recordResolvedEntries,\n SYNC_STATE_VERSION,\n type SyncSelection,\n type SyncState,\n syncStateRootMatches,\n writeSyncState,\n} from '../../../composition/syncState';\nimport type { HarnessOptions } from '../../../composition/types/harness';\nimport { DOOMPI_DOMAINS_ENV, DOOMPI_MAJOR_MODE_ENV, DOOMPI_PROFILE_ENV } from '../../matrixOptions';\nimport { parseHarnessArgs } from '../../options';\nimport { SyncProgress, type SyncProgressOutput } from './presenter';\n\n/**\n * `doom-pi sync`: resolve the matrix once and write it where plain Pi finds it.\n *\n * The doom-emacs split. Everything that needs a real Node process (module\n * resolution, staging skills and agents, generating the MCP config) happens\n * here, and the doom-pi extension then only reads what this produced. The\n * launcher is untouched and keeps resolving the same matrix per run.\n */\n\nconst SYNC_COMMAND = 'sync';\nconst CHECK_OPTION = '--check';\n/** Republishes even when nothing drifted, for a generation suspected of being damaged. */\nconst FORCE_OPTION = '--force';\nconst GLOBAL_OPTION = '--global';\nconst HARNESS_ROOT_ENV = 'DOOMPI_ROOT';\nconst PERSONA_FILE_ENV = 'DOOMPI_PERSONA_FILE';\nconst HOOK_EMITTER = path.join('tools', 'harness', 'emit-hooks.mjs');\nconst NONE = '(none)';\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst SYNC_LABEL = 'sync';\nconst RUNTIME_LABEL = 'runtime';\nconst WEB_LABEL = 'web';\nconst API_LABEL = 'api';\n\n/**\n * Harness variables worth recording, by prefix or exact name.\n *\n * An allowlist rather than the whole environment: the state file is a snapshot\n * of resolved configuration, and dumping `process.env` into it would write\n * every credential the sync happened to run with onto disk.\n */\nconst RECORDED_PREFIXES = ['DOOMPI_'];\nconst RECORDED_KEYS = ['CLAUDE_PROJECT_DIR', 'CODEX_REPO_ROOT', 'ORIGINAL_REPO_PATH', 'MCP_UI_VIEWER'];\n/**\n * Launcher-only values a synced session must not inherit.\n *\n * The child extension list is recomposed on every load, and the subagent binary\n * points at `pi.sh`, which a session started as plain `pi` should not shell out\n * to: Doom Team resolves Pi's own CLI when the variable is absent.\n */\nconst EXCLUDED_KEYS = new Set([\n 'DOOMPI_CHILD_EXTENSIONS',\n 'DOOMPI_COMPOSED',\n 'DOOMPI_MUTE',\n 'DOOMPI_TEMP_DIR',\n // A pointer to the syncing process's own state file. Recording it would hand\n // every later session a path to a state that died with this one.\n HARNESS_STATE_POINTER,\n 'PI_SUBAGENT_PI_BINARY',\n]);\n\ntype SyncOutput = SyncProgressOutput;\n\nexport type SyncSettingsMode = 'persisted' | 'embedded';\n\nexport interface SyncCommandOptions {\n settingsMode?: SyncSettingsMode;\n /** Test/embedding override; normal CLI execution uses the process home. */\n homeDirectory?: string;\n /** Internal pipeline seam when the caller owns the worktree lock. */\n lockHeld?: boolean;\n}\n\nexport interface SyncResult {\n statePath: string;\n /** Omitted when DPI supplies the integration as a process-local overlay. */\n settingsPath?: string;\n /** Set only when the repository still carried its own DoomPi registration. */\n projectSettingsPath?: string;\n selection: SyncSelection;\n mcpServers: string[];\n skillCount: number;\n agentCount: number;\n}\n\nexport function recordedEnvironment(environment: NodeJS.ProcessEnv): Record<string, string> {\n const recorded: Record<string, string> = {};\n for (const [key, value] of Object.entries(environment)) {\n if (value === undefined || EXCLUDED_KEYS.has(key)) continue;\n if (RECORDED_KEYS.includes(key) || RECORDED_PREFIXES.some((prefix) => key.startsWith(prefix))) {\n recorded[key] = value;\n }\n }\n return recorded;\n}\n\n/**\n * Reports the config keys sync chose to ignore.\n *\n * Never fatal. A key nobody recognises is usually a config written for another\n * version of a layer, and refusing to build over it is worse than proceeding\n * without it. The strict check lives in `doompi doctor`.\n */\nfunction writeConfigDiagnostics(diagnostics: readonly ConfigDiagnostic[], output: SyncOutput): void {\n if (diagnostics.length === 0) return;\n const lines = diagnostics.map((entry) => ` ${entry.filePath}: ${entry.path}`).join('\\n');\n output.write(\n `config: ignored ${String(diagnostics.length)} unsupported key(s); run doompi doctor for the strict check\\n${lines}\\n`,\n );\n}\n/**\n * Layers the repository's declared selection under the usual resolution.\n *\n * `.doom/config.yaml` holds what the repository selects by default, the way\n * init.el does for doom-emacs. Seeding the environment the parser reads keeps\n * the precedence the launcher already documents: an explicit flag wins, then an\n * exported variable, then the declared default.\n */\nexport function selectionEnvironment(\n repoRoot: string,\n environment: NodeJS.ProcessEnv,\n homeDirectory?: string,\n): NodeJS.ProcessEnv {\n const { selection } = loadDoomConfigLenient(repoRoot, homeDirectory).config;\n if (!selection) return environment;\n return {\n ...environment,\n ...(selection.majorMode && !environment[DOOMPI_MAJOR_MODE_ENV]\n ? { [DOOMPI_MAJOR_MODE_ENV]: selection.majorMode }\n : {}),\n ...(selection.profile && !environment[DOOMPI_PROFILE_ENV] ? { [DOOMPI_PROFILE_ENV]: selection.profile } : {}),\n ...(selection.domains && environment[DOOMPI_DOMAINS_ENV] === undefined\n ? { [DOOMPI_DOMAINS_ENV]: selection.domains.join(',') }\n : {}),\n };\n}\n\nexport function toSelection(\n options: Pick<HarnessOptions, 'majorMode' | 'domains' | 'profile' | 'preset'>,\n): SyncSelection {\n return {\n majorMode: options.majorMode,\n domains: options.domains,\n profile: options.profile,\n preset: options.preset,\n };\n}\n\nexport function selectionCompositionFingerprint(\n repoRoot: string,\n options: Pick<HarnessOptions, 'agents' | 'hooks' | 'majorMode' | 'mcp' | 'preset'>,\n homeDirectory: string = os.homedir(),\n): string {\n const majorModesConfig = loadMajorModesConfig(repoRoot, homeDirectory);\n const resolvers = createLayerResolvers(repoRoot);\n return resolveExtensionComposition({\n agents: options.agents,\n autoStop: false,\n mute: false,\n preset: options.preset,\n personaEntry: resolvers.packageEntry(PERSONA_ENTRY),\n majorMode: options.majorMode,\n layers: filterHookDisabledLayers(\n majorModesConfig,\n resolveLayers(majorModesConfig, options.majorMode),\n options.hooks,\n ),\n majorModesConfig,\n resolvers,\n }).fingerprint;\n}\n\n/** Settings, dispatcher and theme differences an init would fix, independent of sync state. */\nfunction piIntegrationDrift(agentDirectory: string): string[] {\n const drift: string[] = [];\n const themePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n const settings = readPiSettings(agentDirectory);\n const merged = mergePiSettings(settings, agentDirectory, { themePath, themeName: DEFAULT_THEME_NAME });\n if (serializePiSettings(merged) !== serializePiSettings(settings)) {\n drift.push('Pi user settings are out of date; run doompi init');\n }\n if (!piExtensionAliasIsCurrent(agentDirectory)) drift.push('Pi user dispatcher is out of date; run doompi init');\n const expectedTheme = `${JSON.stringify(DEFAULT_THEME, null, 2)}\\n`;\n if (!fs.existsSync(themePath) || fs.readFileSync(themePath, 'utf8') !== expectedTheme) {\n drift.push('Pi user theme is out of date; run doompi init');\n }\n return drift;\n}\n\n/** Differences between what a sync would produce and what is on disk. */\nexport function collectDrift(\n repoRoot: string,\n selection: SyncSelection,\n state: SyncState | undefined,\n environment: NodeJS.ProcessEnv = process.env,\n settingsMode: SyncSettingsMode = 'persisted',\n expectedCompositionFingerprint?: string,\n): string[] {\n if (!state) return ['no sync state: run doompi sync'];\n const drift: string[] = [];\n if (!syncStateRootMatches(repoRoot, state.root)) drift.push('sync state belongs to a different repository');\n const recorded = state.selection;\n if (\n recorded.majorMode !== selection.majorMode ||\n recorded.profile !== selection.profile ||\n recorded.preset !== selection.preset ||\n recorded.domains.join(',') !== selection.domains.join(',')\n ) {\n drift.push('selection changed since the last sync');\n }\n // Hashed against the recorded selection, not the requested one, so a\n // selection change is reported once rather than as two findings.\n if (computeInputsHash(repoRoot, recorded, environment.HOME ?? os.homedir()) !== state.inputsHash) {\n drift.push('.doom configuration changed');\n }\n // Re-resolving is what catches a dependency upgrade moving a package, which\n // the inputs hash deliberately does not read.\n if (\n JSON.stringify(\n recordResolvedEntries(\n loadMajorModesConfig(repoRoot, environment.HOME ?? os.homedir()),\n createLayerResolvers(repoRoot),\n ),\n ) !== JSON.stringify(state.resolved)\n ) {\n drift.push('resolved extension paths changed');\n }\n if (expectedCompositionFingerprint && state.compositionFingerprint !== expectedCompositionFingerprint) {\n drift.push('extension composition changed');\n }\n try {\n if (!readBootstrapStatus(repoRoot, undefined, environment.HOME ?? os.homedir()).fresh) {\n drift.push('precompiled runtime is missing or stale');\n }\n } catch {\n drift.push('precompiled runtime is missing or stale');\n }\n\n if (settingsMode === 'persisted') {\n const agentDirectory = piAgentDirectory(environment);\n const themePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n drift.push(...piIntegrationDrift(agentDirectory));\n if (projectRegistersDoom(repoRoot)) drift.push(DUPLICATE_REGISTRATION_DRIFT);\n if (state.baseline.themePath !== themePath || state.baseline.themeName !== DEFAULT_THEME_NAME) {\n drift.push('synced theme location is out of date');\n }\n }\n if (\n readSyncDrift({ repoRoot, homeDirectory: environment.HOME ?? os.homedir() }).reasons.includes('server-bundle-stale')\n ) {\n drift.push('server bundle is missing or stale');\n }\n return drift;\n}\n\n/** Regenerates the hook files the other frontends read before any harness code runs. */\nfunction emitFrontendHooks(repoRoot: string, output: SyncOutput): void {\n const emitter = path.join(repoRoot, HOOK_EMITTER);\n if (!fs.existsSync(emitter)) return;\n const result = spawnSync(process.execPath, [emitter, '--write'], { cwd: repoRoot, encoding: 'utf8' });\n if (result.status === 0) {\n output.write('hooks: regenerated for Claude Code and Codex\\n');\n return;\n }\n output.write(`hooks: emit-hooks failed (${result.stderr?.trim() || `exit ${String(result.status)}`})\\n`);\n}\n\nexport function formatSyncResult(result: SyncResult, runner = 'pi'): string {\n const { selection } = result;\n return [\n `mode: ${selection.majorMode}`,\n `domains: ${selection.domains.join(', ') || NONE}`,\n `profile: ${selection.profile ?? NONE}`,\n `skills: ${result.skillCount}`,\n `agents: ${result.agentCount}`,\n `mcp: ${result.mcpServers.join(', ') || NONE}`,\n `state: ${result.statePath}`,\n ...(result.settingsPath ? [`settings: ${result.settingsPath}`] : []),\n ...(result.projectSettingsPath\n ? [`project: removed duplicate registration from ${result.projectSettingsPath}`]\n : []),\n '',\n `Run ${runner} from the repository root to use it.`,\n '',\n ].join('\\n');\n}\n\n/**\n * The DoomPi package a repository pins for itself, if it pins one.\n *\n * Extensions are version-coupled to the harness that loads them, so the\n * registration must name the copy the repository resolves rather than whichever\n * copy happened to run sync. A globally installed DoomPi syncing a repository\n * that pins its own would otherwise record itself, and the dispatcher would\n * then load the wrong harness for every session in that repository.\n */\nfunction repositoryPackageRoot(repoRoot: string): string | undefined {\n try {\n return path.dirname(\n createRequire(path.join(repoRoot, 'package.json')).resolve(`${DOOM_PACKAGE_NAME}/package.json`),\n );\n } catch {\n // Not pinned here, which is normal; the executing package stands in.\n return undefined;\n }\n}\n\nfunction packageRegistrationFor(repoRoot: string): SyncPackageRegistration {\n const root = fs.realpathSync(repositoryPackageRoot(repoRoot) ?? doomPiPackageRoot());\n const manifestPath = path.join(root, 'package.json');\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as {\n version?: unknown;\n pi?: { extensions?: unknown };\n };\n const version = manifest.version;\n const extensions = manifest.pi?.extensions;\n const extension = Array.isArray(extensions) ? extensions.find((value) => typeof value === 'string') : undefined;\n if (typeof version !== 'string' || typeof extension !== 'string') {\n throw new Error(`Installed DoomPi package at ${root} has no versioned Pi extension entry`);\n }\n return {\n root,\n version,\n manifestPath,\n entry: fs.realpathSync(path.resolve(root, extension)),\n };\n}\n\n/** Resolves the matrix, stages it into home-scoped worktree storage, and publishes one generation. */\nexport async function synchronize(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n commandOptions: SyncCommandOptions = {},\n): Promise<number> {\n const check = args.includes(CHECK_OPTION);\n const force = args.includes(FORCE_OPTION);\n const globalOnly = args.includes(GLOBAL_OPTION);\n const rest = args.slice(1).filter((argument) => ![CHECK_OPTION, FORCE_OPTION, GLOBAL_OPTION].includes(argument));\n const homeDirectory = commandOptions.homeDirectory ?? environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const globalRoot = globalDoomConfigDirectory(homeDirectory);\n const repoRoot = globalOnly\n ? globalRoot\n : inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n if (globalOnly && !check) fs.mkdirSync(globalRoot, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n // A fresh home has no global plugin selection until `doompi init` creates\n // modes.yaml. Repository sync can still publish its own workspace bundle.\n // A check validates its requested scope. Another installed package may own\n // the shared global generation without making this workspace stale.\n if (\n !check &&\n !globalOnly &&\n path.resolve(repoRoot) !== path.resolve(globalRoot) &&\n fs.existsSync(path.join(globalRoot, 'modes.yaml'))\n ) {\n const globalEnvironment = { ...environment };\n for (const key of [\n HARNESS_ROOT_ENV,\n HARNESS_STATE_POINTER,\n DOOMPI_MAJOR_MODE_ENV,\n DOOMPI_DOMAINS_ENV,\n DOOMPI_PROFILE_ENV,\n ])\n delete globalEnvironment[key];\n const globalStatus = await synchronize(\n [SYNC_COMMAND, GLOBAL_OPTION, ...(force ? [FORCE_OPTION] : [])],\n globalEnvironment,\n globalRoot,\n output,\n { settingsMode: 'embedded', homeDirectory },\n );\n if (globalStatus !== 0) return globalStatus;\n }\n // Sync tolerates keys it does not recognise so a config written against a\n // different version cannot break a build. `doompi doctor` reports them.\n const modes = loadMajorModesConfigLenient(repoRoot, homeDirectory);\n const configDiagnostics = [...loadDoomConfigLenient(repoRoot, homeDirectory).diagnostics, ...modes.diagnostics];\n const defaultMajorMode = modes.config.defaultMajorMode;\n const defaultDomains = loadDomains(repoRoot, homeDirectory).defaultDomains;\n const parsed = parseHarnessArgs(\n rest,\n selectionEnvironment(repoRoot, environment, homeDirectory),\n globalOnly ? globalRoot : currentDirectory,\n defaultMajorMode,\n defaultDomains,\n );\n const selection = toSelection(parsed.options);\n const agentDirectory = piAgentDirectory(environment, homeDirectory);\n if ((commandOptions.settingsMode ?? 'persisted') === 'persisted' && !check) {\n if (piExtensionDispatcherIsUpgradeable(agentDirectory)) {\n const previousVersion = piExtensionDispatcherVersion(agentDirectory);\n writePiExtensionAlias(agentDirectory);\n output.write(\n `repair: upgraded Pi user dispatcher from protocol ${String(previousVersion)} to ${String(PI_DISPATCHER_VERSION)}\\n`,\n );\n }\n const drift = piIntegrationDrift(agentDirectory);\n if (drift.length > 0) {\n throw new Error(`DoomPi Pi integration is not ready:\\n${drift.map((entry) => ` ${entry}`).join('\\n')}`);\n }\n }\n writeConfigDiagnostics(configDiagnostics, output);\n if (check) {\n const majorModesConfig = modes.config;\n const missingPackages = missingLayerPackageSpecifiers(\n majorModesConfig,\n Object.keys(majorModesConfig.layers),\n createLayerResolvers(repoRoot),\n );\n if (missingPackages.length > 0) {\n output.write(\n `doompi sync is out of date:\\n${missingPackages\n .map((specifier) => ` configured package is not installed: ${specifier}`)\n .join('\\n')}\\n`,\n );\n return 1;\n }\n let located: ReturnType<typeof readLocatedSyncState>;\n try {\n located = readLocatedSyncState(repoRoot, homeDirectory);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n output.write(`doompi sync is out of date:\\n ${detail}\\n`);\n return 1;\n }\n const expectedCompositionFingerprint = selectionCompositionFingerprint(repoRoot, parsed.options, homeDirectory);\n const drift = collectDrift(\n repoRoot,\n selection,\n located?.state,\n environment,\n commandOptions.settingsMode ?? 'persisted',\n expectedCompositionFingerprint,\n );\n if (drift.length === 0) {\n output.write('doompi sync is up to date\\n');\n return 0;\n }\n output.write(`doompi sync is out of date:\\n${drift.map((entry) => ` ${entry}`).join('\\n')}\\n`);\n return 1;\n }\n\n // Publishing an identical generation is not a no-op: it moves the\n // registration, so every attached cockpit reloads and the previous\n // generation becomes garbage. Same inputs, same published result.\n const driftOptions = {\n repoRoot,\n homeDirectory,\n requireWebBundle: Boolean(environment.DOOMPI_WEB_PACKAGE_ROOT),\n };\n if (!force && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n\n const progress = new SyncProgress(output);\n const releaseLock = commandOptions.lockHeld\n ? undefined\n : await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n let result: SyncResult;\n try {\n // A concurrent publisher may have resolved the drift while this command\n // waited for the lock. Avoid moving the registration for no change.\n if (!force && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n result = await stageSync(repoRoot, parsed.options, environment, homeDirectory, progress, commandOptions);\n } finally {\n await releaseLock?.();\n }\n emitFrontendHooks(repoRoot, output);\n output.write(formatSyncResult(result, (commandOptions.settingsMode ?? 'persisted') === 'embedded' ? 'dpi' : 'pi'));\n return 0;\n}\n\nasync function stageSync(\n repoRoot: string,\n options: Omit<HarnessOptions, 'repoRoot'>,\n environment: NodeJS.ProcessEnv,\n homeDirectory: string,\n progress: SyncProgress,\n commandOptions: SyncCommandOptions = {},\n): Promise<SyncResult> {\n const location = resolveSyncLocation(repoRoot, homeDirectory);\n const generation = `${Date.now().toString(36)}-${crypto.randomUUID()}`;\n const directory = syncGenerationDirectory(location, generation);\n await fs.promises.mkdir(location.generationsDirectory, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n // The leaf is created without `recursive`, so an existing path is an error\n // rather than something to adopt: the cockpit signs and serves whatever the\n // published generation holds, and sync must only ever publish bytes it\n // wrote itself into a directory it just created.\n await fs.promises.mkdir(directory, { mode: PRIVATE_DIRECTORY_MODE });\n\n try {\n const staged = progress.start(SYNC_LABEL, 'resolving the matrix and staging resources');\n const context = await buildHarnessContext({\n ...options,\n repoRoot: location.root,\n homeDirectory,\n cwd: location.root,\n resourceDirectory: directory,\n });\n await ensureLayerPackages({\n repoRoot: location.root,\n config: context.majorModesConfig,\n layers: Object.keys(context.majorModesConfig.layers),\n environment,\n });\n staged(`${String(context.resources.skillCount)} skills, ${String(context.resources.agentCount)} agents`);\n const selection = toSelection(options);\n const resolvers = createLayerResolvers(location.root);\n const resolved = recordResolvedEntries(context.majorModesConfig, resolvers);\n const compositionFingerprint = selectionCompositionFingerprint(location.root, options, homeDirectory);\n const agentDirectory = piAgentDirectory(environment, homeDirectory);\n const persistedThemePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n const themePath =\n (commandOptions.settingsMode ?? 'persisted') === 'persisted' ? persistedThemePath : context.defaultThemePath;\n const state: SyncState = {\n version: SYNC_STATE_VERSION,\n root: location.root,\n identity: location.identity,\n inputsHash: computeInputsHash(location.root, selection, homeDirectory),\n webSourcesHash: computeWebSourcesHash(resolved),\n compositionFingerprint,\n selection,\n env: recordedEnvironment(context.environment),\n fileState: {\n profileEnvironment: loadHarnessState(context.environment).state.profileEnvironment,\n pluginHooks: context.resources.pluginHooks,\n mcpProjection: context.resources.mcpProjection,\n },\n resolved,\n baseline: {\n mcpConfigPath: context.resources.mcpConfigPath,\n personaFile: context.environment[PERSONA_FILE_ENV],\n themePath,\n themeName: DEFAULT_THEME_NAME,\n },\n };\n\n // Runtime compilation writes the package dist files consumed by both the web\n // and server bundlers. Finish it first so a package clean cannot race either\n // consumer, then run the independent web and server builds together.\n let resolveCompositions!: (compositions: readonly ExtensionComposition[]) => void;\n let rejectCompositions!: (reason?: unknown) => void;\n const compositionsReady = new Promise<readonly ExtensionComposition[]>((resolve, reject) => {\n resolveCompositions = resolve;\n rejectCompositions = reject;\n });\n const runtimeProgress = progress.start(RUNTIME_LABEL, 'precompiling the mode bundles');\n const runtimeBuild = buildSyncedRuntime(location.root, environment, homeDirectory, {\n state,\n directory,\n onCompositionsResolved: resolveCompositions,\n }).then((synced) => {\n runtimeProgress(`${String(Object.keys(synced.bundles).length)} mode bundles`);\n return synced;\n });\n void runtimeBuild.catch(rejectCompositions);\n const webBuild = (async () => {\n await runtimeBuild;\n const webProgress = progress.start(WEB_LABEL, 'bundling the web cockpit plugins');\n const web = await syncWebBundle({\n repoRoot: location.root,\n resolvedEntries: state.resolved,\n environment,\n outputDirectory: path.join(directory, 'web-bundle'),\n onNotice: (message) => progress.line(WEB_LABEL, message),\n });\n if (web.status === 'failed') throw new Error(`Cockpit bundle failed: ${web.reason}`);\n webProgress(web.status === 'bundled' ? `cockpit bundled with plugins: ${web.pluginIds.join(', ')}` : web.reason);\n return web;\n })();\n const serverBuild = (async () => {\n const compositions = await compositionsReady;\n await runtimeBuild;\n const apiProgress = progress.start(API_LABEL, 'compiling the server bundle');\n const apiDirectory = path.join(directory, 'api');\n const fingerprint = crypto\n .createHash('sha256')\n .update(JSON.stringify([...new Set(compositions.map((composition) => composition.fingerprint))]))\n .digest('hex');\n const server = await syncServerBundle({\n repositoryRoot: location.root,\n generation,\n fingerprint,\n compositions,\n outputDirectory: apiDirectory,\n cacheDirectory: path.join(directory, 'cache'),\n sharedCacheDirectory: location.sharedCacheDirectory,\n });\n apiProgress(`${server.descriptor.entries.length} server facet(s) compiled`);\n for (const gap of server.contractGaps) progress.line(API_LABEL, `API contract incomplete: ${gap}`);\n return { server, fingerprint, apiDirectory };\n })();\n const [runtimeResult, webResult, serverResult] = await Promise.allSettled([runtimeBuild, webBuild, serverBuild]);\n if (runtimeResult.status === 'rejected') throw runtimeResult.reason;\n if (webResult.status === 'rejected') throw webResult.reason;\n if (serverResult.status === 'rejected') throw serverResult.reason;\n const synced = runtimeResult.value;\n const web = webResult.value;\n const { server, fingerprint, apiDirectory } = serverResult.value;\n const descriptorPath = path.join(apiDirectory, DOOM_SERVER_BUNDLE_FILE);\n const finalState: SyncState = {\n ...synced.state,\n serverBundle: {\n descriptorPath,\n fingerprint,\n compilerManifests: server.compilerManifests,\n sourcesHash: computeServerSourcesHash(synced.state.resolved),\n },\n };\n const statePath = await writeSyncState(\n location.root,\n finalState,\n homeDirectory,\n path.join(directory, 'state.json'),\n );\n const projectSettingsPath =\n (commandOptions.settingsMode ?? 'persisted') === 'persisted'\n ? writeProjectPiSettings(location.root, homeDirectory)\n : undefined;\n const result: SyncResult = {\n statePath,\n ...(projectSettingsPath ? { projectSettingsPath } : {}),\n selection,\n mcpServers: synced.state.baseline.mcpConfigPath ? readMcpServerNames(synced.state.baseline.mcpConfigPath) : [],\n skillCount: context.resources.skillCount,\n agentCount: context.resources.agentCount,\n };\n publishSyncRegistration(\n location.root,\n {\n version: SYNC_REGISTRATION_VERSION,\n root: location.root,\n identity: location.identity,\n generation,\n generationRoot: directory,\n statePath,\n stateSha256: syncStateSha256(statePath),\n webDirectory: web.status === 'bundled' ? web.assetsDir : null,\n apiDirectory,\n serverBundle: { path: descriptorPath, fingerprint, sha256: syncStateSha256(descriptorPath) },\n package: packageRegistrationFor(location.root),\n },\n homeDirectory,\n );\n // ponytail: retain generations until host-owned drain evidence can prove no session uses them.\n // Directory age and an open-file check cannot establish that a lazy import is finished.\n return result;\n } catch (error) {\n await fs.promises.rm(directory, { recursive: true, force: true });\n throw error;\n }\n}\n\n/** Compatibility API. Executables call the command function directly. */\nexport class SyncCommand {\n readonly name = SYNC_COMMAND;\n private readonly settingsMode: SyncSettingsMode;\n private readonly homeDirectory: string | undefined;\n private readonly lockHeld: boolean;\n\n constructor(options: SyncCommandOptions = {}) {\n this.settingsMode = options.settingsMode ?? 'persisted';\n this.homeDirectory = options.homeDirectory;\n this.lockHeld = options.lockHeld ?? false;\n }\n\n matches(args: string[]): boolean {\n return args[0] === this.name;\n }\n\n async execute(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n ): Promise<number> {\n return synchronize(args, environment, currentDirectory, output, {\n settingsMode: this.settingsMode,\n homeDirectory: this.homeDirectory,\n lockHeld: this.lockHeld,\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["path","HARNESS_STATE_POINTER","loadDoomConfigLenient","DOOMPI_MAJOR_MODE_ENV","DOOMPI_PROFILE_ENV","DOOMPI_DOMAINS_ENV","os","loadMajorModesConfig","createLayerResolvers","resolveExtensionComposition","PERSONA_ENTRY","filterHookDisabledLayers","resolveLayers","piThemeDirectory","DEFAULT_THEME_NAME","readPiSettings","mergePiSettings","serializePiSettings","piExtensionAliasIsCurrent","DEFAULT_THEME","fs","syncStateRootMatches","computeInputsHash","recordResolvedEntries","readBootstrapStatus","piAgentDirectory","projectRegistersDoom","DUPLICATE_REGISTRATION_DRIFT","readSyncDrift","spawnSync","doomPiPackageRoot","globalDoomConfigDirectory","resolveDoomConfigurationRoot","loadMajorModesConfigLenient","loadDomains","parseHarnessArgs","piExtensionDispatcherIsUpgradeable","piExtensionDispatcherVersion","missingLayerPackageSpecifiers","readLocatedSyncState","SyncProgress","acquireSyncLocationLock","resolveSyncLocation","crypto","syncGenerationDirectory","buildHarnessContext","ensureLayerPackages","SYNC_STATE_VERSION","computeWebSourcesHash","loadHarnessState","buildSyncedRuntime","syncWebBundle","syncServerBundle","DOOM_SERVER_BUNDLE_FILE","computeServerSourcesHash","writeSyncState","writeProjectPiSettings","readMcpServerNames","SYNC_REGISTRATION_VERSION","syncStateSha256"],"sources":["../../../../src/cli/commands/sync/index.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { globalDoomConfigDirectory } from '@agimon-ai/doompi-config/config';\nimport { loadDomains } from '@agimon-ai/doompi-config/domains';\nimport { filterHookDisabledLayers, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport { loadMajorModesConfig, loadMajorModesConfigLenient } from '@agimon-ai/doompi-config/majorModes';\nimport type { ConfigDiagnostic } from '@agimon-ai/doompi-config/types';\nimport {\n mergePiSettings,\n piAgentDirectory,\n piThemeDirectory,\n readPiSettings,\n serializePiSettings,\n} from '@agimon-ai/doompi-core/runtime-pi-settings';\nimport { DOOM_SERVER_BUNDLE_FILE } from '@agimon-ai/doompi-core/server-facet';\nimport {\n acquireSyncLocationLock,\n resolveSyncLocation,\n syncGenerationDirectory,\n} from '@agimon-ai/doompi-core/sync-location';\nimport {\n publishSyncRegistration,\n SYNC_REGISTRATION_VERSION,\n syncStateSha256,\n type SyncPackageRegistration,\n} from '@agimon-ai/doompi-core/sync-registration';\nimport { DEFAULT_THEME, DEFAULT_THEME_NAME } from '@agimon-ai/doompi-ui/theme';\n\nimport { buildSyncedRuntime } from '../../../builders/cli';\nimport { readBootstrapStatus } from '../../../builders/cli/bootstrapLocator';\nimport {\n createLayerResolvers,\n type ExtensionComposition,\n PERSONA_ENTRY,\n resolveExtensionComposition,\n} from '../../../builders/cli/extensionAssembler';\nimport { buildHarnessContext } from '../../../builders/cli/harnessContext';\nimport {\n doomPiPackageRoot,\n piExtensionAliasIsCurrent,\n writePiExtensionAlias,\n} from '../../../builders/cli/piExtensionAlias';\nimport {\n PI_DISPATCHER_VERSION,\n piExtensionDispatcherIsUpgradeable,\n piExtensionDispatcherVersion,\n} from '../../../builders/cli/piExtensionDispatcher';\nimport {\n DUPLICATE_REGISTRATION_DRIFT,\n projectRegistersDoom,\n writeProjectPiSettings,\n} from '../../../builders/cli/projectSettings';\nimport { syncServerBundle } from '../../../builders/server';\nimport { syncWebBundle } from '../../../builders/web';\nimport { HARNESS_STATE_POINTER, loadHarnessState } from '../../../composition/harnessState';\nimport { ensureLayerPackages, missingLayerPackageSpecifiers } from '../../../composition/layerPackageInstaller';\nimport { loadDoomConfigLenient } from '../../../composition/projectTrust';\nimport { resolveDoomConfigurationRoot } from '../../../composition/repository';\nimport { readSyncDrift } from '../../../composition/syncDrift';\nimport {\n computeInputsHash,\n computeWebSourcesHash,\n computeServerSourcesHash,\n readLocatedSyncState,\n readMcpServerNames,\n recordResolvedEntries,\n SYNC_STATE_VERSION,\n type SyncSelection,\n type SyncState,\n syncStateRootMatches,\n writeSyncState,\n} from '../../../composition/syncState';\nimport type { HarnessOptions } from '../../../composition/types/harness';\nimport { DOOMPI_DOMAINS_ENV, DOOMPI_MAJOR_MODE_ENV, DOOMPI_PROFILE_ENV } from '../../matrixOptions';\nimport { parseHarnessArgs } from '../../options';\nimport { SyncProgress, type SyncProgressOutput } from './presenter';\n\n/**\n * `doom-pi sync`: resolve the matrix once and write it where plain Pi finds it.\n *\n * The doom-emacs split. Everything that needs a real Node process (module\n * resolution, staging skills and agents, generating the MCP config) happens\n * here, and the doom-pi extension then only reads what this produced. The\n * launcher is untouched and keeps resolving the same matrix per run.\n */\n\nconst SYNC_COMMAND = 'sync';\nconst CHECK_OPTION = '--check';\n/** Republishes even when nothing drifted, for a generation suspected of being damaged. */\nconst FORCE_OPTION = '--force';\nconst GLOBAL_OPTION = '--global';\nconst HARNESS_ROOT_ENV = 'DOOMPI_ROOT';\nconst PERSONA_FILE_ENV = 'DOOMPI_PERSONA_FILE';\nconst HOOK_EMITTER = path.join('tools', 'harness', 'emit-hooks.mjs');\nconst NONE = '(none)';\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst SYNC_LABEL = 'sync';\nconst RUNTIME_LABEL = 'runtime';\nconst WEB_LABEL = 'web';\nconst API_LABEL = 'api';\n\n/**\n * Harness variables worth recording, by prefix or exact name.\n *\n * An allowlist rather than the whole environment: the state file is a snapshot\n * of resolved configuration, and dumping `process.env` into it would write\n * every credential the sync happened to run with onto disk.\n */\nconst RECORDED_PREFIXES = ['DOOMPI_'];\nconst RECORDED_KEYS = ['CLAUDE_PROJECT_DIR', 'CODEX_REPO_ROOT', 'ORIGINAL_REPO_PATH', 'MCP_UI_VIEWER'];\n/**\n * Launcher-only values a synced session must not inherit.\n *\n * The child extension list is recomposed on every load, and the subagent binary\n * points at `pi.sh`, which a session started as plain `pi` should not shell out\n * to: Doom Team resolves Pi's own CLI when the variable is absent.\n */\nconst EXCLUDED_KEYS = new Set([\n 'DOOMPI_CHILD_EXTENSIONS',\n 'DOOMPI_COMPOSED',\n 'DOOMPI_MUTE',\n 'DOOMPI_TEMP_DIR',\n // A pointer to the syncing process's own state file. Recording it would hand\n // every later session a path to a state that died with this one.\n HARNESS_STATE_POINTER,\n 'PI_SUBAGENT_PI_BINARY',\n]);\n\ntype SyncOutput = SyncProgressOutput;\n\nexport type SyncSettingsMode = 'persisted' | 'embedded';\n\nexport interface SyncCommandOptions {\n settingsMode?: SyncSettingsMode;\n /** Test/embedding override; normal CLI execution uses the process home. */\n homeDirectory?: string;\n /** Internal pipeline seam when the caller owns the worktree lock. */\n lockHeld?: boolean;\n}\n\nexport interface SyncResult {\n statePath: string;\n /** Omitted when DPI supplies the integration as a process-local overlay. */\n settingsPath?: string;\n /** Set only when the repository still carried its own DoomPi registration. */\n projectSettingsPath?: string;\n selection: SyncSelection;\n mcpServers: string[];\n skillCount: number;\n agentCount: number;\n}\n\nexport function recordedEnvironment(environment: NodeJS.ProcessEnv): Record<string, string> {\n const recorded: Record<string, string> = {};\n for (const [key, value] of Object.entries(environment)) {\n if (value === undefined || EXCLUDED_KEYS.has(key)) continue;\n if (RECORDED_KEYS.includes(key) || RECORDED_PREFIXES.some((prefix) => key.startsWith(prefix))) {\n recorded[key] = value;\n }\n }\n return recorded;\n}\n\n/**\n * Reports the config keys sync chose to ignore.\n *\n * Never fatal. A key nobody recognises is usually a config written for another\n * version of a layer, and refusing to build over it is worse than proceeding\n * without it. The strict check lives in `doompi doctor`.\n */\nfunction writeConfigDiagnostics(diagnostics: readonly ConfigDiagnostic[], output: SyncOutput): void {\n if (diagnostics.length === 0) return;\n const lines = diagnostics.map((entry) => ` ${entry.filePath}: ${entry.path}`).join('\\n');\n output.write(\n `config: ignored ${String(diagnostics.length)} unsupported key(s); run doompi doctor for the strict check\\n${lines}\\n`,\n );\n}\n/**\n * Layers the repository's declared selection under the usual resolution.\n *\n * `.doom/config.yaml` holds what the repository selects by default, the way\n * init.el does for doom-emacs. Seeding the environment the parser reads keeps\n * the precedence the launcher already documents: an explicit flag wins, then an\n * exported variable, then the declared default.\n */\nexport function selectionEnvironment(\n repoRoot: string,\n environment: NodeJS.ProcessEnv,\n homeDirectory?: string,\n): NodeJS.ProcessEnv {\n const { selection } = loadDoomConfigLenient(repoRoot, homeDirectory).config;\n if (!selection) return environment;\n return {\n ...environment,\n ...(selection.majorMode && !environment[DOOMPI_MAJOR_MODE_ENV]\n ? { [DOOMPI_MAJOR_MODE_ENV]: selection.majorMode }\n : {}),\n ...(selection.profile && !environment[DOOMPI_PROFILE_ENV] ? { [DOOMPI_PROFILE_ENV]: selection.profile } : {}),\n ...(selection.domains && environment[DOOMPI_DOMAINS_ENV] === undefined\n ? { [DOOMPI_DOMAINS_ENV]: selection.domains.join(',') }\n : {}),\n };\n}\n\nexport function toSelection(\n options: Pick<HarnessOptions, 'majorMode' | 'domains' | 'profile' | 'preset'>,\n): SyncSelection {\n return {\n majorMode: options.majorMode,\n domains: options.domains,\n profile: options.profile,\n preset: options.preset,\n };\n}\n\nexport function selectionCompositionFingerprint(\n repoRoot: string,\n options: Pick<HarnessOptions, 'agents' | 'hooks' | 'majorMode' | 'mcp' | 'preset'>,\n homeDirectory: string = os.homedir(),\n): string {\n const majorModesConfig = loadMajorModesConfig(repoRoot, homeDirectory);\n const resolvers = createLayerResolvers(repoRoot);\n return resolveExtensionComposition({\n agents: options.agents,\n autoStop: false,\n mute: false,\n preset: options.preset,\n personaEntry: resolvers.packageEntry(PERSONA_ENTRY),\n majorMode: options.majorMode,\n layers: filterHookDisabledLayers(\n majorModesConfig,\n resolveLayers(majorModesConfig, options.majorMode),\n options.hooks,\n ),\n majorModesConfig,\n resolvers,\n }).fingerprint;\n}\n\n/** Settings, dispatcher and theme differences an init would fix, independent of sync state. */\nfunction piIntegrationDrift(agentDirectory: string): string[] {\n const drift: string[] = [];\n const themePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n const settings = readPiSettings(agentDirectory);\n const merged = mergePiSettings(settings, agentDirectory, { themePath, themeName: DEFAULT_THEME_NAME });\n if (serializePiSettings(merged) !== serializePiSettings(settings)) {\n drift.push('Pi user settings are out of date; run doompi init');\n }\n if (!piExtensionAliasIsCurrent(agentDirectory)) drift.push('Pi user dispatcher is out of date; run doompi init');\n const expectedTheme = `${JSON.stringify(DEFAULT_THEME, null, 2)}\\n`;\n if (!fs.existsSync(themePath) || fs.readFileSync(themePath, 'utf8') !== expectedTheme) {\n drift.push('Pi user theme is out of date; run doompi init');\n }\n return drift;\n}\n\n/** Differences between what a sync would produce and what is on disk. */\nexport function collectDrift(\n repoRoot: string,\n selection: SyncSelection,\n state: SyncState | undefined,\n environment: NodeJS.ProcessEnv = process.env,\n settingsMode: SyncSettingsMode = 'persisted',\n expectedCompositionFingerprint?: string,\n): string[] {\n if (!state) return ['no sync state: run doompi sync'];\n const drift: string[] = [];\n if (!syncStateRootMatches(repoRoot, state.root)) drift.push('sync state belongs to a different repository');\n const recorded = state.selection;\n if (\n recorded.majorMode !== selection.majorMode ||\n recorded.profile !== selection.profile ||\n recorded.preset !== selection.preset ||\n recorded.domains.join(',') !== selection.domains.join(',')\n ) {\n drift.push('selection changed since the last sync');\n }\n // Hashed against the recorded selection, not the requested one, so a\n // selection change is reported once rather than as two findings.\n if (computeInputsHash(repoRoot, recorded, environment.HOME ?? os.homedir()) !== state.inputsHash) {\n drift.push('.doom configuration changed');\n }\n // Re-resolving is what catches a dependency upgrade moving a package, which\n // the inputs hash deliberately does not read.\n if (\n JSON.stringify(\n recordResolvedEntries(\n loadMajorModesConfig(repoRoot, environment.HOME ?? os.homedir()),\n createLayerResolvers(repoRoot),\n ),\n ) !== JSON.stringify(state.resolved)\n ) {\n drift.push('resolved extension paths changed');\n }\n if (expectedCompositionFingerprint && state.compositionFingerprint !== expectedCompositionFingerprint) {\n drift.push('extension composition changed');\n }\n try {\n if (!readBootstrapStatus(repoRoot, undefined, environment.HOME ?? os.homedir()).fresh) {\n drift.push('precompiled runtime is missing or stale');\n }\n } catch {\n drift.push('precompiled runtime is missing or stale');\n }\n\n if (settingsMode === 'persisted') {\n const agentDirectory = piAgentDirectory(environment);\n const themePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n drift.push(...piIntegrationDrift(agentDirectory));\n if (projectRegistersDoom(repoRoot)) drift.push(DUPLICATE_REGISTRATION_DRIFT);\n if (state.baseline.themePath !== themePath || state.baseline.themeName !== DEFAULT_THEME_NAME) {\n drift.push('synced theme location is out of date');\n }\n }\n if (\n readSyncDrift({ repoRoot, homeDirectory: environment.HOME ?? os.homedir() }).reasons.includes('server-bundle-stale')\n ) {\n drift.push('server bundle is missing or stale');\n }\n return drift;\n}\n\n/** Regenerates the hook files the other frontends read before any harness code runs. */\nfunction emitFrontendHooks(repoRoot: string, output: SyncOutput): void {\n const emitter = path.join(repoRoot, HOOK_EMITTER);\n if (!fs.existsSync(emitter)) return;\n const result = spawnSync(process.execPath, [emitter, '--write'], { cwd: repoRoot, encoding: 'utf8' });\n if (result.status === 0) {\n output.write('hooks: regenerated for Claude Code and Codex\\n');\n return;\n }\n output.write(`hooks: emit-hooks failed (${result.stderr?.trim() || `exit ${String(result.status)}`})\\n`);\n}\n\nexport function formatSyncResult(result: SyncResult, runner = 'pi'): string {\n const { selection } = result;\n return [\n `mode: ${selection.majorMode}`,\n `domains: ${selection.domains.join(', ') || NONE}`,\n `profile: ${selection.profile ?? NONE}`,\n `skills: ${result.skillCount}`,\n `agents: ${result.agentCount}`,\n `mcp: ${result.mcpServers.join(', ') || NONE}`,\n `state: ${result.statePath}`,\n ...(result.settingsPath ? [`settings: ${result.settingsPath}`] : []),\n ...(result.projectSettingsPath\n ? [`project: removed duplicate registration from ${result.projectSettingsPath}`]\n : []),\n '',\n `Run ${runner} from the repository root to use it.`,\n '',\n ].join('\\n');\n}\n\n/**\n * The DoomPi that produced this generation, which is the one that can load it.\n *\n * Always the executing package, never another copy the repository happens to\n * install. A generation is not portable between two installations: the bundles\n * are compiled from the building package's own extension entries, the recorded\n * compiler inputs are its files, and the state names its bootstrap entry. Naming\n * a second copy here publishes a registration whose package disagrees with the\n * state it points at, and Pi's dispatcher then loads a harness that rejects the\n * bootstrap as stale on every session, with no sync able to fix it.\n *\n * A repository that wants its own copy to own its sessions runs sync with that\n * copy's CLI, which makes it the executing package.\n */\nfunction packageRegistrationFor(): SyncPackageRegistration {\n const root = fs.realpathSync(doomPiPackageRoot());\n const manifestPath = path.join(root, 'package.json');\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as {\n version?: unknown;\n pi?: { extensions?: unknown };\n };\n const version = manifest.version;\n const extensions = manifest.pi?.extensions;\n const extension = Array.isArray(extensions) ? extensions.find((value) => typeof value === 'string') : undefined;\n if (typeof version !== 'string' || typeof extension !== 'string') {\n throw new Error(`Installed DoomPi package at ${root} has no versioned Pi extension entry`);\n }\n return {\n root,\n version,\n manifestPath,\n entry: fs.realpathSync(path.resolve(root, extension)),\n };\n}\n\n/** Resolves the matrix, stages it into home-scoped worktree storage, and publishes one generation. */\nexport async function synchronize(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n commandOptions: SyncCommandOptions = {},\n): Promise<number> {\n const check = args.includes(CHECK_OPTION);\n const force = args.includes(FORCE_OPTION);\n const globalOnly = args.includes(GLOBAL_OPTION);\n const rest = args.slice(1).filter((argument) => ![CHECK_OPTION, FORCE_OPTION, GLOBAL_OPTION].includes(argument));\n const homeDirectory = commandOptions.homeDirectory ?? environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const globalRoot = globalDoomConfigDirectory(homeDirectory);\n const repoRoot = globalOnly\n ? globalRoot\n : inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n if (globalOnly && !check) fs.mkdirSync(globalRoot, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n // A fresh home has no global plugin selection until `doompi init` creates\n // modes.yaml. Repository sync can still publish its own workspace bundle.\n // A check validates its requested scope. Another installed package may own\n // the shared global generation without making this workspace stale.\n if (\n !check &&\n !globalOnly &&\n path.resolve(repoRoot) !== path.resolve(globalRoot) &&\n fs.existsSync(path.join(globalRoot, 'modes.yaml'))\n ) {\n const globalEnvironment = { ...environment };\n for (const key of [\n HARNESS_ROOT_ENV,\n HARNESS_STATE_POINTER,\n DOOMPI_MAJOR_MODE_ENV,\n DOOMPI_DOMAINS_ENV,\n DOOMPI_PROFILE_ENV,\n ])\n delete globalEnvironment[key];\n const globalStatus = await synchronize(\n [SYNC_COMMAND, GLOBAL_OPTION, ...(force ? [FORCE_OPTION] : [])],\n globalEnvironment,\n globalRoot,\n output,\n { settingsMode: 'embedded', homeDirectory },\n );\n if (globalStatus !== 0) return globalStatus;\n }\n // Sync tolerates keys it does not recognise so a config written against a\n // different version cannot break a build. `doompi doctor` reports them.\n const modes = loadMajorModesConfigLenient(repoRoot, homeDirectory);\n const configDiagnostics = [...loadDoomConfigLenient(repoRoot, homeDirectory).diagnostics, ...modes.diagnostics];\n const defaultMajorMode = modes.config.defaultMajorMode;\n const defaultDomains = loadDomains(repoRoot, homeDirectory).defaultDomains;\n const parsed = parseHarnessArgs(\n rest,\n selectionEnvironment(repoRoot, environment, homeDirectory),\n globalOnly ? globalRoot : currentDirectory,\n defaultMajorMode,\n defaultDomains,\n );\n const selection = toSelection(parsed.options);\n const agentDirectory = piAgentDirectory(environment, homeDirectory);\n if ((commandOptions.settingsMode ?? 'persisted') === 'persisted' && !check) {\n if (piExtensionDispatcherIsUpgradeable(agentDirectory)) {\n const previousVersion = piExtensionDispatcherVersion(agentDirectory);\n writePiExtensionAlias(agentDirectory);\n output.write(\n `repair: upgraded Pi user dispatcher from protocol ${String(previousVersion)} to ${String(PI_DISPATCHER_VERSION)}\\n`,\n );\n }\n const drift = piIntegrationDrift(agentDirectory);\n if (drift.length > 0) {\n throw new Error(`DoomPi Pi integration is not ready:\\n${drift.map((entry) => ` ${entry}`).join('\\n')}`);\n }\n }\n writeConfigDiagnostics(configDiagnostics, output);\n if (check) {\n const majorModesConfig = modes.config;\n const missingPackages = missingLayerPackageSpecifiers(\n majorModesConfig,\n Object.keys(majorModesConfig.layers),\n createLayerResolvers(repoRoot),\n );\n if (missingPackages.length > 0) {\n output.write(\n `doompi sync is out of date:\\n${missingPackages\n .map((specifier) => ` configured package is not installed: ${specifier}`)\n .join('\\n')}\\n`,\n );\n return 1;\n }\n let located: ReturnType<typeof readLocatedSyncState>;\n try {\n located = readLocatedSyncState(repoRoot, homeDirectory);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n output.write(`doompi sync is out of date:\\n ${detail}\\n`);\n return 1;\n }\n const expectedCompositionFingerprint = selectionCompositionFingerprint(repoRoot, parsed.options, homeDirectory);\n const drift = collectDrift(\n repoRoot,\n selection,\n located?.state,\n environment,\n commandOptions.settingsMode ?? 'persisted',\n expectedCompositionFingerprint,\n );\n if (drift.length === 0) {\n output.write('doompi sync is up to date\\n');\n return 0;\n }\n output.write(`doompi sync is out of date:\\n${drift.map((entry) => ` ${entry}`).join('\\n')}\\n`);\n return 1;\n }\n\n // Publishing an identical generation is not a no-op: it moves the\n // registration, so every attached cockpit reloads and the previous\n // generation becomes garbage. Same inputs, same published result.\n const driftOptions = {\n repoRoot,\n homeDirectory,\n requireWebBundle: Boolean(environment.DOOMPI_WEB_PACKAGE_ROOT),\n };\n if (!force && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n\n const progress = new SyncProgress(output);\n const releaseLock = commandOptions.lockHeld\n ? undefined\n : await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n let result: SyncResult;\n try {\n // A concurrent publisher may have resolved the drift while this command\n // waited for the lock. Avoid moving the registration for no change.\n if (!force && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n result = await stageSync(repoRoot, parsed.options, environment, homeDirectory, progress, commandOptions);\n } finally {\n await releaseLock?.();\n }\n emitFrontendHooks(repoRoot, output);\n output.write(formatSyncResult(result, (commandOptions.settingsMode ?? 'persisted') === 'embedded' ? 'dpi' : 'pi'));\n return 0;\n}\n\nasync function stageSync(\n repoRoot: string,\n options: Omit<HarnessOptions, 'repoRoot'>,\n environment: NodeJS.ProcessEnv,\n homeDirectory: string,\n progress: SyncProgress,\n commandOptions: SyncCommandOptions = {},\n): Promise<SyncResult> {\n const location = resolveSyncLocation(repoRoot, homeDirectory);\n const generation = `${Date.now().toString(36)}-${crypto.randomUUID()}`;\n const directory = syncGenerationDirectory(location, generation);\n await fs.promises.mkdir(location.generationsDirectory, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n // The leaf is created without `recursive`, so an existing path is an error\n // rather than something to adopt: the cockpit signs and serves whatever the\n // published generation holds, and sync must only ever publish bytes it\n // wrote itself into a directory it just created.\n await fs.promises.mkdir(directory, { mode: PRIVATE_DIRECTORY_MODE });\n\n try {\n const staged = progress.start(SYNC_LABEL, 'resolving the matrix and staging resources');\n const context = await buildHarnessContext({\n ...options,\n repoRoot: location.root,\n homeDirectory,\n cwd: location.root,\n resourceDirectory: directory,\n });\n await ensureLayerPackages({\n repoRoot: location.root,\n config: context.majorModesConfig,\n layers: Object.keys(context.majorModesConfig.layers),\n environment,\n });\n staged(`${String(context.resources.skillCount)} skills, ${String(context.resources.agentCount)} agents`);\n const selection = toSelection(options);\n const resolvers = createLayerResolvers(location.root);\n const resolved = recordResolvedEntries(context.majorModesConfig, resolvers);\n const compositionFingerprint = selectionCompositionFingerprint(location.root, options, homeDirectory);\n const agentDirectory = piAgentDirectory(environment, homeDirectory);\n const persistedThemePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n const themePath =\n (commandOptions.settingsMode ?? 'persisted') === 'persisted' ? persistedThemePath : context.defaultThemePath;\n const state: SyncState = {\n version: SYNC_STATE_VERSION,\n root: location.root,\n identity: location.identity,\n inputsHash: computeInputsHash(location.root, selection, homeDirectory),\n webSourcesHash: computeWebSourcesHash(resolved),\n compositionFingerprint,\n selection,\n env: recordedEnvironment(context.environment),\n fileState: {\n profileEnvironment: loadHarnessState(context.environment).state.profileEnvironment,\n pluginHooks: context.resources.pluginHooks,\n mcpProjection: context.resources.mcpProjection,\n },\n resolved,\n baseline: {\n mcpConfigPath: context.resources.mcpConfigPath,\n personaFile: context.environment[PERSONA_FILE_ENV],\n themePath,\n themeName: DEFAULT_THEME_NAME,\n },\n };\n\n // Runtime compilation writes the package dist files consumed by both the web\n // and server bundlers. Finish it first so a package clean cannot race either\n // consumer, then run the independent web and server builds together.\n let resolveCompositions!: (compositions: readonly ExtensionComposition[]) => void;\n let rejectCompositions!: (reason?: unknown) => void;\n const compositionsReady = new Promise<readonly ExtensionComposition[]>((resolve, reject) => {\n resolveCompositions = resolve;\n rejectCompositions = reject;\n });\n const runtimeProgress = progress.start(RUNTIME_LABEL, 'precompiling the mode bundles');\n const runtimeBuild = buildSyncedRuntime(location.root, environment, homeDirectory, {\n state,\n directory,\n onCompositionsResolved: resolveCompositions,\n }).then((synced) => {\n runtimeProgress(`${String(Object.keys(synced.bundles).length)} mode bundles`);\n return synced;\n });\n void runtimeBuild.catch(rejectCompositions);\n const webBuild = (async () => {\n await runtimeBuild;\n const webProgress = progress.start(WEB_LABEL, 'bundling the web cockpit plugins');\n const web = await syncWebBundle({\n repoRoot: location.root,\n resolvedEntries: state.resolved,\n environment,\n outputDirectory: path.join(directory, 'web-bundle'),\n onNotice: (message) => progress.line(WEB_LABEL, message),\n });\n if (web.status === 'failed') throw new Error(`Cockpit bundle failed: ${web.reason}`);\n webProgress(web.status === 'bundled' ? `cockpit bundled with plugins: ${web.pluginIds.join(', ')}` : web.reason);\n return web;\n })();\n const serverBuild = (async () => {\n const compositions = await compositionsReady;\n await runtimeBuild;\n const apiProgress = progress.start(API_LABEL, 'compiling the server bundle');\n const apiDirectory = path.join(directory, 'api');\n const fingerprint = crypto\n .createHash('sha256')\n .update(JSON.stringify([...new Set(compositions.map((composition) => composition.fingerprint))]))\n .digest('hex');\n const server = await syncServerBundle({\n repositoryRoot: location.root,\n generation,\n fingerprint,\n compositions,\n outputDirectory: apiDirectory,\n cacheDirectory: path.join(directory, 'cache'),\n sharedCacheDirectory: location.sharedCacheDirectory,\n });\n apiProgress(`${server.descriptor.entries.length} server facet(s) compiled`);\n for (const gap of server.contractGaps) progress.line(API_LABEL, `API contract incomplete: ${gap}`);\n return { server, fingerprint, apiDirectory };\n })();\n const [runtimeResult, webResult, serverResult] = await Promise.allSettled([runtimeBuild, webBuild, serverBuild]);\n if (runtimeResult.status === 'rejected') throw runtimeResult.reason;\n if (webResult.status === 'rejected') throw webResult.reason;\n if (serverResult.status === 'rejected') throw serverResult.reason;\n const synced = runtimeResult.value;\n const web = webResult.value;\n const { server, fingerprint, apiDirectory } = serverResult.value;\n const descriptorPath = path.join(apiDirectory, DOOM_SERVER_BUNDLE_FILE);\n const finalState: SyncState = {\n ...synced.state,\n serverBundle: {\n descriptorPath,\n fingerprint,\n compilerManifests: server.compilerManifests,\n sourcesHash: computeServerSourcesHash(synced.state.resolved),\n },\n };\n const statePath = await writeSyncState(\n location.root,\n finalState,\n homeDirectory,\n path.join(directory, 'state.json'),\n );\n const projectSettingsPath =\n (commandOptions.settingsMode ?? 'persisted') === 'persisted'\n ? writeProjectPiSettings(location.root, homeDirectory)\n : undefined;\n const result: SyncResult = {\n statePath,\n ...(projectSettingsPath ? { projectSettingsPath } : {}),\n selection,\n mcpServers: synced.state.baseline.mcpConfigPath ? readMcpServerNames(synced.state.baseline.mcpConfigPath) : [],\n skillCount: context.resources.skillCount,\n agentCount: context.resources.agentCount,\n };\n publishSyncRegistration(\n location.root,\n {\n version: SYNC_REGISTRATION_VERSION,\n root: location.root,\n identity: location.identity,\n generation,\n generationRoot: directory,\n statePath,\n stateSha256: syncStateSha256(statePath),\n webDirectory: web.status === 'bundled' ? web.assetsDir : null,\n apiDirectory,\n serverBundle: { path: descriptorPath, fingerprint, sha256: syncStateSha256(descriptorPath) },\n package: packageRegistrationFor(),\n },\n homeDirectory,\n );\n // ponytail: retain generations until host-owned drain evidence can prove no session uses them.\n // Directory age and an open-file check cannot establish that a lazy import is finished.\n return result;\n } catch (error) {\n await fs.promises.rm(directory, { recursive: true, force: true });\n throw error;\n }\n}\n\n/** Compatibility API. Executables call the command function directly. */\nexport class SyncCommand {\n readonly name = SYNC_COMMAND;\n private readonly settingsMode: SyncSettingsMode;\n private readonly homeDirectory: string | undefined;\n private readonly lockHeld: boolean;\n\n constructor(options: SyncCommandOptions = {}) {\n this.settingsMode = options.settingsMode ?? 'persisted';\n this.homeDirectory = options.homeDirectory;\n this.lockHeld = options.lockHeld ?? false;\n }\n\n matches(args: string[]): boolean {\n return args[0] === this.name;\n }\n\n async execute(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n ): Promise<number> {\n return synchronize(args, environment, currentDirectory, output, {\n settingsMode: this.settingsMode,\n homeDirectory: this.homeDirectory,\n lockHeld: this.lockHeld,\n });\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../../../src/cli/commands/sync/index.ts"],"mappings":";;;;KAsIK,aAAa;YAEN;iBAEK;EACf,eAAe;;EAEf;;EAEA;;iBAGe;EACf;;EAEA;;EAEA;EACA,WAAW;EACX;EACA;EACA;;wBAGc,oBAAoB,aAAa,OAAO,aAAa;;;;;;;;;wBAiCrD,qBACd,kBACA,aAAa,OAAO,YACpB,yBACC,OAAO;wBAeM,YACd,SAAS,KAAK,kEACb;wBASa,gCACd,kBACA,SAAS,KAAK,sEACd;;wBAuCc,aACd,kBACA,WAAW,eACX,OAAO,uBACP,cAAa,OAAO,YACpB,eAAc,kBACd;wBAuEc,iBAAiB,QAAQ,YAAY;;wBA8D/B,YACpB,gBACA,cAAa,OAAO,YACpB,2BACA,SAAQ,YACR,iBAAgB,qBACf;;qBAuUU;WACF;mBACQ;mBACA;mBACA;EAEjB,YAAY,UAAS;EAMrB,QAAQ;EAIF,QACJ,gBACA,cAAa,OAAO,YACpB,2BACA,SAAQ,aACP"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../../../src/cli/commands/sync/index.ts"],"mappings":";;;;KAoIK,aAAa;YAEN;iBAEK;EACf,eAAe;;EAEf;;EAEA;;iBAGe;EACf;;EAEA;;EAEA;EACA,WAAW;EACX;EACA;EACA;;wBAGc,oBAAoB,aAAa,OAAO,aAAa;;;;;;;;;wBAiCrD,qBACd,kBACA,aAAa,OAAO,YACpB,yBACC,OAAO;wBAeM,YACd,SAAS,KAAK,kEACb;wBASa,gCACd,kBACA,SAAS,KAAK,sEACd;;wBAuCc,aACd,kBACA,WAAW,eACX,OAAO,uBACP,cAAa,OAAO,YACpB,eAAc,kBACd;wBAuEc,iBAAiB,QAAQ,YAAY;;wBAwD/B,YACpB,gBACA,cAAa,OAAO,YACpB,2BACA,SAAQ,YACR,iBAAgB,qBACf;;qBAuUU;WACF;mBACQ;mBACA;mBACA;EAEjB,YAAY,UAAS;EAMrB,QAAQ;EAIF,QACJ,gBACA,cAAa,OAAO,YACpB,2BACA,SAAQ,aACP"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../../../src/cli/commands/sync/index.ts"],"mappings":";;;;KAsIK,aAAa;YAEN;iBAEK;EACf,eAAe;;EAEf;;EAEA;;iBAGe;EACf;;EAEA;;EAEA;EACA,WAAW;EACX;EACA;EACA;;wBAGc,oBAAoB,aAAa,OAAO,aAAa;;;;;;;;;wBAiCrD,qBACd,kBACA,aAAa,OAAO,YACpB,yBACC,OAAO;wBAeM,YACd,SAAS,KAAK,kEACb;wBASa,gCACd,kBACA,SAAS,KAAK,sEACd;;wBAuCc,aACd,kBACA,WAAW,eACX,OAAO,uBACP,cAAa,OAAO,YACpB,eAAc,kBACd;wBAuEc,iBAAiB,QAAQ,YAAY;;wBA8D/B,YACpB,gBACA,cAAa,OAAO,YACpB,2BACA,SAAQ,YACR,iBAAgB,qBACf;;qBAuUU;WACF;mBACQ;mBACA;mBACA;EAEjB,YAAY,UAAS;EAMrB,QAAQ;EAIF,QACJ,gBACA,cAAa,OAAO,YACpB,2BACA,SAAQ,aACP"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../../src/cli/commands/sync/index.ts"],"mappings":";;;;KAoIK,aAAa;YAEN;iBAEK;EACf,eAAe;;EAEf;;EAEA;;iBAGe;EACf;;EAEA;;EAEA;EACA,WAAW;EACX;EACA;EACA;;wBAGc,oBAAoB,aAAa,OAAO,aAAa;;;;;;;;;wBAiCrD,qBACd,kBACA,aAAa,OAAO,YACpB,yBACC,OAAO;wBAeM,YACd,SAAS,KAAK,kEACb;wBASa,gCACd,kBACA,SAAS,KAAK,sEACd;;wBAuCc,aACd,kBACA,WAAW,eACX,OAAO,uBACP,cAAa,OAAO,YACpB,eAAc,kBACd;wBAuEc,iBAAiB,QAAQ,YAAY;;wBAwD/B,YACpB,gBACA,cAAa,OAAO,YACpB,2BACA,SAAQ,YACR,iBAAgB,qBACf;;qBAuUU;WACF;mBACQ;mBACA;mBACA;EAEjB,YAAY,UAAS;EAMrB,QAAQ;EAIF,QACJ,gBACA,cAAa,OAAO,YACpB,2BACA,SAAQ,aACP"}
@@ -16,7 +16,6 @@ import { DUPLICATE_REGISTRATION_DRIFT, projectRegistersDoom, writeProjectPiSetti
16
16
  import { syncServerBundle } from "../../../builders/server/index.mjs";
17
17
  import { readSyncDrift } from "../../../composition/syncDrift/index.mjs";
18
18
  import { SyncProgress } from "./presenter.mjs";
19
- import { createRequire } from "node:module";
20
19
  import fs from "node:fs";
21
20
  import path from "node:path";
22
21
  import { SYNC_REGISTRATION_VERSION, publishSyncRegistration, syncStateSha256 } from "@agimon-ai/doompi-core/sync-registration";
@@ -28,7 +27,6 @@ import { spawnSync } from "node:child_process";
28
27
  import { mergePiSettings, piAgentDirectory, piThemeDirectory, readPiSettings, serializePiSettings } from "@agimon-ai/doompi-core/runtime-pi-settings";
29
28
  import crypto from "node:crypto";
30
29
  import { acquireSyncLocationLock, resolveSyncLocation, syncGenerationDirectory } from "@agimon-ai/doompi-core/sync-location";
31
- import { DOOM_PACKAGE_NAME } from "@agimon-ai/doompi-core/doom-package";
32
30
  import { DOOM_SERVER_BUNDLE_FILE } from "@agimon-ai/doompi-core/server-facet";
33
31
  import { DEFAULT_THEME, DEFAULT_THEME_NAME } from "@agimon-ai/doompi-ui/theme";
34
32
  //#region src/cli/commands/sync/index.ts
@@ -219,23 +217,21 @@ function formatSyncResult(result, runner = "pi") {
219
217
  ].join("\n");
220
218
  }
221
219
  /**
222
- * The DoomPi package a repository pins for itself, if it pins one.
220
+ * The DoomPi that produced this generation, which is the one that can load it.
223
221
  *
224
- * Extensions are version-coupled to the harness that loads them, so the
225
- * registration must name the copy the repository resolves rather than whichever
226
- * copy happened to run sync. A globally installed DoomPi syncing a repository
227
- * that pins its own would otherwise record itself, and the dispatcher would
228
- * then load the wrong harness for every session in that repository.
222
+ * Always the executing package, never another copy the repository happens to
223
+ * install. A generation is not portable between two installations: the bundles
224
+ * are compiled from the building package's own extension entries, the recorded
225
+ * compiler inputs are its files, and the state names its bootstrap entry. Naming
226
+ * a second copy here publishes a registration whose package disagrees with the
227
+ * state it points at, and Pi's dispatcher then loads a harness that rejects the
228
+ * bootstrap as stale on every session, with no sync able to fix it.
229
+ *
230
+ * A repository that wants its own copy to own its sessions runs sync with that
231
+ * copy's CLI, which makes it the executing package.
229
232
  */
230
- function repositoryPackageRoot(repoRoot) {
231
- try {
232
- return path.dirname(createRequire(path.join(repoRoot, "package.json")).resolve(`${DOOM_PACKAGE_NAME}/package.json`));
233
- } catch {
234
- return;
235
- }
236
- }
237
- function packageRegistrationFor(repoRoot) {
238
- const root = fs.realpathSync(repositoryPackageRoot(repoRoot) ?? doomPiPackageRoot());
233
+ function packageRegistrationFor() {
234
+ const root = fs.realpathSync(doomPiPackageRoot());
239
235
  const manifestPath = path.join(root, "package.json");
240
236
  const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
241
237
  const version = manifest.version;
@@ -505,7 +501,7 @@ async function stageSync(repoRoot, options, environment, homeDirectory, progress
505
501
  fingerprint,
506
502
  sha256: syncStateSha256(descriptorPath)
507
503
  },
508
- package: packageRegistrationFor(location.root)
504
+ package: packageRegistrationFor()
509
505
  }, homeDirectory);
510
506
  return result;
511
507
  } catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["loadDoomConfigLenient"],"sources":["../../../../src/cli/commands/sync/index.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { globalDoomConfigDirectory } from '@agimon-ai/doompi-config/config';\nimport { loadDomains } from '@agimon-ai/doompi-config/domains';\nimport { filterHookDisabledLayers, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport { loadMajorModesConfig, loadMajorModesConfigLenient } from '@agimon-ai/doompi-config/majorModes';\nimport type { ConfigDiagnostic } from '@agimon-ai/doompi-config/types';\nimport { DOOM_PACKAGE_NAME } from '@agimon-ai/doompi-core/doom-package';\nimport {\n mergePiSettings,\n piAgentDirectory,\n piThemeDirectory,\n readPiSettings,\n serializePiSettings,\n} from '@agimon-ai/doompi-core/runtime-pi-settings';\nimport { DOOM_SERVER_BUNDLE_FILE } from '@agimon-ai/doompi-core/server-facet';\nimport {\n acquireSyncLocationLock,\n resolveSyncLocation,\n syncGenerationDirectory,\n} from '@agimon-ai/doompi-core/sync-location';\nimport {\n publishSyncRegistration,\n SYNC_REGISTRATION_VERSION,\n syncStateSha256,\n type SyncPackageRegistration,\n} from '@agimon-ai/doompi-core/sync-registration';\nimport { DEFAULT_THEME, DEFAULT_THEME_NAME } from '@agimon-ai/doompi-ui/theme';\n\nimport { buildSyncedRuntime } from '../../../builders/cli';\nimport { readBootstrapStatus } from '../../../builders/cli/bootstrapLocator';\nimport {\n createLayerResolvers,\n type ExtensionComposition,\n PERSONA_ENTRY,\n resolveExtensionComposition,\n} from '../../../builders/cli/extensionAssembler';\nimport { buildHarnessContext } from '../../../builders/cli/harnessContext';\nimport {\n doomPiPackageRoot,\n piExtensionAliasIsCurrent,\n writePiExtensionAlias,\n} from '../../../builders/cli/piExtensionAlias';\nimport {\n PI_DISPATCHER_VERSION,\n piExtensionDispatcherIsUpgradeable,\n piExtensionDispatcherVersion,\n} from '../../../builders/cli/piExtensionDispatcher';\nimport {\n DUPLICATE_REGISTRATION_DRIFT,\n projectRegistersDoom,\n writeProjectPiSettings,\n} from '../../../builders/cli/projectSettings';\nimport { syncServerBundle } from '../../../builders/server';\nimport { syncWebBundle } from '../../../builders/web';\nimport { HARNESS_STATE_POINTER, loadHarnessState } from '../../../composition/harnessState';\nimport { ensureLayerPackages, missingLayerPackageSpecifiers } from '../../../composition/layerPackageInstaller';\nimport { loadDoomConfigLenient } from '../../../composition/projectTrust';\nimport { resolveDoomConfigurationRoot } from '../../../composition/repository';\nimport { readSyncDrift } from '../../../composition/syncDrift';\nimport {\n computeInputsHash,\n computeWebSourcesHash,\n computeServerSourcesHash,\n readLocatedSyncState,\n readMcpServerNames,\n recordResolvedEntries,\n SYNC_STATE_VERSION,\n type SyncSelection,\n type SyncState,\n syncStateRootMatches,\n writeSyncState,\n} from '../../../composition/syncState';\nimport type { HarnessOptions } from '../../../composition/types/harness';\nimport { DOOMPI_DOMAINS_ENV, DOOMPI_MAJOR_MODE_ENV, DOOMPI_PROFILE_ENV } from '../../matrixOptions';\nimport { parseHarnessArgs } from '../../options';\nimport { SyncProgress, type SyncProgressOutput } from './presenter';\n\n/**\n * `doom-pi sync`: resolve the matrix once and write it where plain Pi finds it.\n *\n * The doom-emacs split. Everything that needs a real Node process (module\n * resolution, staging skills and agents, generating the MCP config) happens\n * here, and the doom-pi extension then only reads what this produced. The\n * launcher is untouched and keeps resolving the same matrix per run.\n */\n\nconst SYNC_COMMAND = 'sync';\nconst CHECK_OPTION = '--check';\n/** Republishes even when nothing drifted, for a generation suspected of being damaged. */\nconst FORCE_OPTION = '--force';\nconst GLOBAL_OPTION = '--global';\nconst HARNESS_ROOT_ENV = 'DOOMPI_ROOT';\nconst PERSONA_FILE_ENV = 'DOOMPI_PERSONA_FILE';\nconst HOOK_EMITTER = path.join('tools', 'harness', 'emit-hooks.mjs');\nconst NONE = '(none)';\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst SYNC_LABEL = 'sync';\nconst RUNTIME_LABEL = 'runtime';\nconst WEB_LABEL = 'web';\nconst API_LABEL = 'api';\n\n/**\n * Harness variables worth recording, by prefix or exact name.\n *\n * An allowlist rather than the whole environment: the state file is a snapshot\n * of resolved configuration, and dumping `process.env` into it would write\n * every credential the sync happened to run with onto disk.\n */\nconst RECORDED_PREFIXES = ['DOOMPI_'];\nconst RECORDED_KEYS = ['CLAUDE_PROJECT_DIR', 'CODEX_REPO_ROOT', 'ORIGINAL_REPO_PATH', 'MCP_UI_VIEWER'];\n/**\n * Launcher-only values a synced session must not inherit.\n *\n * The child extension list is recomposed on every load, and the subagent binary\n * points at `pi.sh`, which a session started as plain `pi` should not shell out\n * to: Doom Team resolves Pi's own CLI when the variable is absent.\n */\nconst EXCLUDED_KEYS = new Set([\n 'DOOMPI_CHILD_EXTENSIONS',\n 'DOOMPI_COMPOSED',\n 'DOOMPI_MUTE',\n 'DOOMPI_TEMP_DIR',\n // A pointer to the syncing process's own state file. Recording it would hand\n // every later session a path to a state that died with this one.\n HARNESS_STATE_POINTER,\n 'PI_SUBAGENT_PI_BINARY',\n]);\n\ntype SyncOutput = SyncProgressOutput;\n\nexport type SyncSettingsMode = 'persisted' | 'embedded';\n\nexport interface SyncCommandOptions {\n settingsMode?: SyncSettingsMode;\n /** Test/embedding override; normal CLI execution uses the process home. */\n homeDirectory?: string;\n /** Internal pipeline seam when the caller owns the worktree lock. */\n lockHeld?: boolean;\n}\n\nexport interface SyncResult {\n statePath: string;\n /** Omitted when DPI supplies the integration as a process-local overlay. */\n settingsPath?: string;\n /** Set only when the repository still carried its own DoomPi registration. */\n projectSettingsPath?: string;\n selection: SyncSelection;\n mcpServers: string[];\n skillCount: number;\n agentCount: number;\n}\n\nexport function recordedEnvironment(environment: NodeJS.ProcessEnv): Record<string, string> {\n const recorded: Record<string, string> = {};\n for (const [key, value] of Object.entries(environment)) {\n if (value === undefined || EXCLUDED_KEYS.has(key)) continue;\n if (RECORDED_KEYS.includes(key) || RECORDED_PREFIXES.some((prefix) => key.startsWith(prefix))) {\n recorded[key] = value;\n }\n }\n return recorded;\n}\n\n/**\n * Reports the config keys sync chose to ignore.\n *\n * Never fatal. A key nobody recognises is usually a config written for another\n * version of a layer, and refusing to build over it is worse than proceeding\n * without it. The strict check lives in `doompi doctor`.\n */\nfunction writeConfigDiagnostics(diagnostics: readonly ConfigDiagnostic[], output: SyncOutput): void {\n if (diagnostics.length === 0) return;\n const lines = diagnostics.map((entry) => ` ${entry.filePath}: ${entry.path}`).join('\\n');\n output.write(\n `config: ignored ${String(diagnostics.length)} unsupported key(s); run doompi doctor for the strict check\\n${lines}\\n`,\n );\n}\n/**\n * Layers the repository's declared selection under the usual resolution.\n *\n * `.doom/config.yaml` holds what the repository selects by default, the way\n * init.el does for doom-emacs. Seeding the environment the parser reads keeps\n * the precedence the launcher already documents: an explicit flag wins, then an\n * exported variable, then the declared default.\n */\nexport function selectionEnvironment(\n repoRoot: string,\n environment: NodeJS.ProcessEnv,\n homeDirectory?: string,\n): NodeJS.ProcessEnv {\n const { selection } = loadDoomConfigLenient(repoRoot, homeDirectory).config;\n if (!selection) return environment;\n return {\n ...environment,\n ...(selection.majorMode && !environment[DOOMPI_MAJOR_MODE_ENV]\n ? { [DOOMPI_MAJOR_MODE_ENV]: selection.majorMode }\n : {}),\n ...(selection.profile && !environment[DOOMPI_PROFILE_ENV] ? { [DOOMPI_PROFILE_ENV]: selection.profile } : {}),\n ...(selection.domains && environment[DOOMPI_DOMAINS_ENV] === undefined\n ? { [DOOMPI_DOMAINS_ENV]: selection.domains.join(',') }\n : {}),\n };\n}\n\nexport function toSelection(\n options: Pick<HarnessOptions, 'majorMode' | 'domains' | 'profile' | 'preset'>,\n): SyncSelection {\n return {\n majorMode: options.majorMode,\n domains: options.domains,\n profile: options.profile,\n preset: options.preset,\n };\n}\n\nexport function selectionCompositionFingerprint(\n repoRoot: string,\n options: Pick<HarnessOptions, 'agents' | 'hooks' | 'majorMode' | 'mcp' | 'preset'>,\n homeDirectory: string = os.homedir(),\n): string {\n const majorModesConfig = loadMajorModesConfig(repoRoot, homeDirectory);\n const resolvers = createLayerResolvers(repoRoot);\n return resolveExtensionComposition({\n agents: options.agents,\n autoStop: false,\n mute: false,\n preset: options.preset,\n personaEntry: resolvers.packageEntry(PERSONA_ENTRY),\n majorMode: options.majorMode,\n layers: filterHookDisabledLayers(\n majorModesConfig,\n resolveLayers(majorModesConfig, options.majorMode),\n options.hooks,\n ),\n majorModesConfig,\n resolvers,\n }).fingerprint;\n}\n\n/** Settings, dispatcher and theme differences an init would fix, independent of sync state. */\nfunction piIntegrationDrift(agentDirectory: string): string[] {\n const drift: string[] = [];\n const themePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n const settings = readPiSettings(agentDirectory);\n const merged = mergePiSettings(settings, agentDirectory, { themePath, themeName: DEFAULT_THEME_NAME });\n if (serializePiSettings(merged) !== serializePiSettings(settings)) {\n drift.push('Pi user settings are out of date; run doompi init');\n }\n if (!piExtensionAliasIsCurrent(agentDirectory)) drift.push('Pi user dispatcher is out of date; run doompi init');\n const expectedTheme = `${JSON.stringify(DEFAULT_THEME, null, 2)}\\n`;\n if (!fs.existsSync(themePath) || fs.readFileSync(themePath, 'utf8') !== expectedTheme) {\n drift.push('Pi user theme is out of date; run doompi init');\n }\n return drift;\n}\n\n/** Differences between what a sync would produce and what is on disk. */\nexport function collectDrift(\n repoRoot: string,\n selection: SyncSelection,\n state: SyncState | undefined,\n environment: NodeJS.ProcessEnv = process.env,\n settingsMode: SyncSettingsMode = 'persisted',\n expectedCompositionFingerprint?: string,\n): string[] {\n if (!state) return ['no sync state: run doompi sync'];\n const drift: string[] = [];\n if (!syncStateRootMatches(repoRoot, state.root)) drift.push('sync state belongs to a different repository');\n const recorded = state.selection;\n if (\n recorded.majorMode !== selection.majorMode ||\n recorded.profile !== selection.profile ||\n recorded.preset !== selection.preset ||\n recorded.domains.join(',') !== selection.domains.join(',')\n ) {\n drift.push('selection changed since the last sync');\n }\n // Hashed against the recorded selection, not the requested one, so a\n // selection change is reported once rather than as two findings.\n if (computeInputsHash(repoRoot, recorded, environment.HOME ?? os.homedir()) !== state.inputsHash) {\n drift.push('.doom configuration changed');\n }\n // Re-resolving is what catches a dependency upgrade moving a package, which\n // the inputs hash deliberately does not read.\n if (\n JSON.stringify(\n recordResolvedEntries(\n loadMajorModesConfig(repoRoot, environment.HOME ?? os.homedir()),\n createLayerResolvers(repoRoot),\n ),\n ) !== JSON.stringify(state.resolved)\n ) {\n drift.push('resolved extension paths changed');\n }\n if (expectedCompositionFingerprint && state.compositionFingerprint !== expectedCompositionFingerprint) {\n drift.push('extension composition changed');\n }\n try {\n if (!readBootstrapStatus(repoRoot, undefined, environment.HOME ?? os.homedir()).fresh) {\n drift.push('precompiled runtime is missing or stale');\n }\n } catch {\n drift.push('precompiled runtime is missing or stale');\n }\n\n if (settingsMode === 'persisted') {\n const agentDirectory = piAgentDirectory(environment);\n const themePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n drift.push(...piIntegrationDrift(agentDirectory));\n if (projectRegistersDoom(repoRoot)) drift.push(DUPLICATE_REGISTRATION_DRIFT);\n if (state.baseline.themePath !== themePath || state.baseline.themeName !== DEFAULT_THEME_NAME) {\n drift.push('synced theme location is out of date');\n }\n }\n if (\n readSyncDrift({ repoRoot, homeDirectory: environment.HOME ?? os.homedir() }).reasons.includes('server-bundle-stale')\n ) {\n drift.push('server bundle is missing or stale');\n }\n return drift;\n}\n\n/** Regenerates the hook files the other frontends read before any harness code runs. */\nfunction emitFrontendHooks(repoRoot: string, output: SyncOutput): void {\n const emitter = path.join(repoRoot, HOOK_EMITTER);\n if (!fs.existsSync(emitter)) return;\n const result = spawnSync(process.execPath, [emitter, '--write'], { cwd: repoRoot, encoding: 'utf8' });\n if (result.status === 0) {\n output.write('hooks: regenerated for Claude Code and Codex\\n');\n return;\n }\n output.write(`hooks: emit-hooks failed (${result.stderr?.trim() || `exit ${String(result.status)}`})\\n`);\n}\n\nexport function formatSyncResult(result: SyncResult, runner = 'pi'): string {\n const { selection } = result;\n return [\n `mode: ${selection.majorMode}`,\n `domains: ${selection.domains.join(', ') || NONE}`,\n `profile: ${selection.profile ?? NONE}`,\n `skills: ${result.skillCount}`,\n `agents: ${result.agentCount}`,\n `mcp: ${result.mcpServers.join(', ') || NONE}`,\n `state: ${result.statePath}`,\n ...(result.settingsPath ? [`settings: ${result.settingsPath}`] : []),\n ...(result.projectSettingsPath\n ? [`project: removed duplicate registration from ${result.projectSettingsPath}`]\n : []),\n '',\n `Run ${runner} from the repository root to use it.`,\n '',\n ].join('\\n');\n}\n\n/**\n * The DoomPi package a repository pins for itself, if it pins one.\n *\n * Extensions are version-coupled to the harness that loads them, so the\n * registration must name the copy the repository resolves rather than whichever\n * copy happened to run sync. A globally installed DoomPi syncing a repository\n * that pins its own would otherwise record itself, and the dispatcher would\n * then load the wrong harness for every session in that repository.\n */\nfunction repositoryPackageRoot(repoRoot: string): string | undefined {\n try {\n return path.dirname(\n createRequire(path.join(repoRoot, 'package.json')).resolve(`${DOOM_PACKAGE_NAME}/package.json`),\n );\n } catch {\n // Not pinned here, which is normal; the executing package stands in.\n return undefined;\n }\n}\n\nfunction packageRegistrationFor(repoRoot: string): SyncPackageRegistration {\n const root = fs.realpathSync(repositoryPackageRoot(repoRoot) ?? doomPiPackageRoot());\n const manifestPath = path.join(root, 'package.json');\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as {\n version?: unknown;\n pi?: { extensions?: unknown };\n };\n const version = manifest.version;\n const extensions = manifest.pi?.extensions;\n const extension = Array.isArray(extensions) ? extensions.find((value) => typeof value === 'string') : undefined;\n if (typeof version !== 'string' || typeof extension !== 'string') {\n throw new Error(`Installed DoomPi package at ${root} has no versioned Pi extension entry`);\n }\n return {\n root,\n version,\n manifestPath,\n entry: fs.realpathSync(path.resolve(root, extension)),\n };\n}\n\n/** Resolves the matrix, stages it into home-scoped worktree storage, and publishes one generation. */\nexport async function synchronize(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n commandOptions: SyncCommandOptions = {},\n): Promise<number> {\n const check = args.includes(CHECK_OPTION);\n const force = args.includes(FORCE_OPTION);\n const globalOnly = args.includes(GLOBAL_OPTION);\n const rest = args.slice(1).filter((argument) => ![CHECK_OPTION, FORCE_OPTION, GLOBAL_OPTION].includes(argument));\n const homeDirectory = commandOptions.homeDirectory ?? environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const globalRoot = globalDoomConfigDirectory(homeDirectory);\n const repoRoot = globalOnly\n ? globalRoot\n : inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n if (globalOnly && !check) fs.mkdirSync(globalRoot, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n // A fresh home has no global plugin selection until `doompi init` creates\n // modes.yaml. Repository sync can still publish its own workspace bundle.\n // A check validates its requested scope. Another installed package may own\n // the shared global generation without making this workspace stale.\n if (\n !check &&\n !globalOnly &&\n path.resolve(repoRoot) !== path.resolve(globalRoot) &&\n fs.existsSync(path.join(globalRoot, 'modes.yaml'))\n ) {\n const globalEnvironment = { ...environment };\n for (const key of [\n HARNESS_ROOT_ENV,\n HARNESS_STATE_POINTER,\n DOOMPI_MAJOR_MODE_ENV,\n DOOMPI_DOMAINS_ENV,\n DOOMPI_PROFILE_ENV,\n ])\n delete globalEnvironment[key];\n const globalStatus = await synchronize(\n [SYNC_COMMAND, GLOBAL_OPTION, ...(force ? [FORCE_OPTION] : [])],\n globalEnvironment,\n globalRoot,\n output,\n { settingsMode: 'embedded', homeDirectory },\n );\n if (globalStatus !== 0) return globalStatus;\n }\n // Sync tolerates keys it does not recognise so a config written against a\n // different version cannot break a build. `doompi doctor` reports them.\n const modes = loadMajorModesConfigLenient(repoRoot, homeDirectory);\n const configDiagnostics = [...loadDoomConfigLenient(repoRoot, homeDirectory).diagnostics, ...modes.diagnostics];\n const defaultMajorMode = modes.config.defaultMajorMode;\n const defaultDomains = loadDomains(repoRoot, homeDirectory).defaultDomains;\n const parsed = parseHarnessArgs(\n rest,\n selectionEnvironment(repoRoot, environment, homeDirectory),\n globalOnly ? globalRoot : currentDirectory,\n defaultMajorMode,\n defaultDomains,\n );\n const selection = toSelection(parsed.options);\n const agentDirectory = piAgentDirectory(environment, homeDirectory);\n if ((commandOptions.settingsMode ?? 'persisted') === 'persisted' && !check) {\n if (piExtensionDispatcherIsUpgradeable(agentDirectory)) {\n const previousVersion = piExtensionDispatcherVersion(agentDirectory);\n writePiExtensionAlias(agentDirectory);\n output.write(\n `repair: upgraded Pi user dispatcher from protocol ${String(previousVersion)} to ${String(PI_DISPATCHER_VERSION)}\\n`,\n );\n }\n const drift = piIntegrationDrift(agentDirectory);\n if (drift.length > 0) {\n throw new Error(`DoomPi Pi integration is not ready:\\n${drift.map((entry) => ` ${entry}`).join('\\n')}`);\n }\n }\n writeConfigDiagnostics(configDiagnostics, output);\n if (check) {\n const majorModesConfig = modes.config;\n const missingPackages = missingLayerPackageSpecifiers(\n majorModesConfig,\n Object.keys(majorModesConfig.layers),\n createLayerResolvers(repoRoot),\n );\n if (missingPackages.length > 0) {\n output.write(\n `doompi sync is out of date:\\n${missingPackages\n .map((specifier) => ` configured package is not installed: ${specifier}`)\n .join('\\n')}\\n`,\n );\n return 1;\n }\n let located: ReturnType<typeof readLocatedSyncState>;\n try {\n located = readLocatedSyncState(repoRoot, homeDirectory);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n output.write(`doompi sync is out of date:\\n ${detail}\\n`);\n return 1;\n }\n const expectedCompositionFingerprint = selectionCompositionFingerprint(repoRoot, parsed.options, homeDirectory);\n const drift = collectDrift(\n repoRoot,\n selection,\n located?.state,\n environment,\n commandOptions.settingsMode ?? 'persisted',\n expectedCompositionFingerprint,\n );\n if (drift.length === 0) {\n output.write('doompi sync is up to date\\n');\n return 0;\n }\n output.write(`doompi sync is out of date:\\n${drift.map((entry) => ` ${entry}`).join('\\n')}\\n`);\n return 1;\n }\n\n // Publishing an identical generation is not a no-op: it moves the\n // registration, so every attached cockpit reloads and the previous\n // generation becomes garbage. Same inputs, same published result.\n const driftOptions = {\n repoRoot,\n homeDirectory,\n requireWebBundle: Boolean(environment.DOOMPI_WEB_PACKAGE_ROOT),\n };\n if (!force && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n\n const progress = new SyncProgress(output);\n const releaseLock = commandOptions.lockHeld\n ? undefined\n : await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n let result: SyncResult;\n try {\n // A concurrent publisher may have resolved the drift while this command\n // waited for the lock. Avoid moving the registration for no change.\n if (!force && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n result = await stageSync(repoRoot, parsed.options, environment, homeDirectory, progress, commandOptions);\n } finally {\n await releaseLock?.();\n }\n emitFrontendHooks(repoRoot, output);\n output.write(formatSyncResult(result, (commandOptions.settingsMode ?? 'persisted') === 'embedded' ? 'dpi' : 'pi'));\n return 0;\n}\n\nasync function stageSync(\n repoRoot: string,\n options: Omit<HarnessOptions, 'repoRoot'>,\n environment: NodeJS.ProcessEnv,\n homeDirectory: string,\n progress: SyncProgress,\n commandOptions: SyncCommandOptions = {},\n): Promise<SyncResult> {\n const location = resolveSyncLocation(repoRoot, homeDirectory);\n const generation = `${Date.now().toString(36)}-${crypto.randomUUID()}`;\n const directory = syncGenerationDirectory(location, generation);\n await fs.promises.mkdir(location.generationsDirectory, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n // The leaf is created without `recursive`, so an existing path is an error\n // rather than something to adopt: the cockpit signs and serves whatever the\n // published generation holds, and sync must only ever publish bytes it\n // wrote itself into a directory it just created.\n await fs.promises.mkdir(directory, { mode: PRIVATE_DIRECTORY_MODE });\n\n try {\n const staged = progress.start(SYNC_LABEL, 'resolving the matrix and staging resources');\n const context = await buildHarnessContext({\n ...options,\n repoRoot: location.root,\n homeDirectory,\n cwd: location.root,\n resourceDirectory: directory,\n });\n await ensureLayerPackages({\n repoRoot: location.root,\n config: context.majorModesConfig,\n layers: Object.keys(context.majorModesConfig.layers),\n environment,\n });\n staged(`${String(context.resources.skillCount)} skills, ${String(context.resources.agentCount)} agents`);\n const selection = toSelection(options);\n const resolvers = createLayerResolvers(location.root);\n const resolved = recordResolvedEntries(context.majorModesConfig, resolvers);\n const compositionFingerprint = selectionCompositionFingerprint(location.root, options, homeDirectory);\n const agentDirectory = piAgentDirectory(environment, homeDirectory);\n const persistedThemePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n const themePath =\n (commandOptions.settingsMode ?? 'persisted') === 'persisted' ? persistedThemePath : context.defaultThemePath;\n const state: SyncState = {\n version: SYNC_STATE_VERSION,\n root: location.root,\n identity: location.identity,\n inputsHash: computeInputsHash(location.root, selection, homeDirectory),\n webSourcesHash: computeWebSourcesHash(resolved),\n compositionFingerprint,\n selection,\n env: recordedEnvironment(context.environment),\n fileState: {\n profileEnvironment: loadHarnessState(context.environment).state.profileEnvironment,\n pluginHooks: context.resources.pluginHooks,\n mcpProjection: context.resources.mcpProjection,\n },\n resolved,\n baseline: {\n mcpConfigPath: context.resources.mcpConfigPath,\n personaFile: context.environment[PERSONA_FILE_ENV],\n themePath,\n themeName: DEFAULT_THEME_NAME,\n },\n };\n\n // Runtime compilation writes the package dist files consumed by both the web\n // and server bundlers. Finish it first so a package clean cannot race either\n // consumer, then run the independent web and server builds together.\n let resolveCompositions!: (compositions: readonly ExtensionComposition[]) => void;\n let rejectCompositions!: (reason?: unknown) => void;\n const compositionsReady = new Promise<readonly ExtensionComposition[]>((resolve, reject) => {\n resolveCompositions = resolve;\n rejectCompositions = reject;\n });\n const runtimeProgress = progress.start(RUNTIME_LABEL, 'precompiling the mode bundles');\n const runtimeBuild = buildSyncedRuntime(location.root, environment, homeDirectory, {\n state,\n directory,\n onCompositionsResolved: resolveCompositions,\n }).then((synced) => {\n runtimeProgress(`${String(Object.keys(synced.bundles).length)} mode bundles`);\n return synced;\n });\n void runtimeBuild.catch(rejectCompositions);\n const webBuild = (async () => {\n await runtimeBuild;\n const webProgress = progress.start(WEB_LABEL, 'bundling the web cockpit plugins');\n const web = await syncWebBundle({\n repoRoot: location.root,\n resolvedEntries: state.resolved,\n environment,\n outputDirectory: path.join(directory, 'web-bundle'),\n onNotice: (message) => progress.line(WEB_LABEL, message),\n });\n if (web.status === 'failed') throw new Error(`Cockpit bundle failed: ${web.reason}`);\n webProgress(web.status === 'bundled' ? `cockpit bundled with plugins: ${web.pluginIds.join(', ')}` : web.reason);\n return web;\n })();\n const serverBuild = (async () => {\n const compositions = await compositionsReady;\n await runtimeBuild;\n const apiProgress = progress.start(API_LABEL, 'compiling the server bundle');\n const apiDirectory = path.join(directory, 'api');\n const fingerprint = crypto\n .createHash('sha256')\n .update(JSON.stringify([...new Set(compositions.map((composition) => composition.fingerprint))]))\n .digest('hex');\n const server = await syncServerBundle({\n repositoryRoot: location.root,\n generation,\n fingerprint,\n compositions,\n outputDirectory: apiDirectory,\n cacheDirectory: path.join(directory, 'cache'),\n sharedCacheDirectory: location.sharedCacheDirectory,\n });\n apiProgress(`${server.descriptor.entries.length} server facet(s) compiled`);\n for (const gap of server.contractGaps) progress.line(API_LABEL, `API contract incomplete: ${gap}`);\n return { server, fingerprint, apiDirectory };\n })();\n const [runtimeResult, webResult, serverResult] = await Promise.allSettled([runtimeBuild, webBuild, serverBuild]);\n if (runtimeResult.status === 'rejected') throw runtimeResult.reason;\n if (webResult.status === 'rejected') throw webResult.reason;\n if (serverResult.status === 'rejected') throw serverResult.reason;\n const synced = runtimeResult.value;\n const web = webResult.value;\n const { server, fingerprint, apiDirectory } = serverResult.value;\n const descriptorPath = path.join(apiDirectory, DOOM_SERVER_BUNDLE_FILE);\n const finalState: SyncState = {\n ...synced.state,\n serverBundle: {\n descriptorPath,\n fingerprint,\n compilerManifests: server.compilerManifests,\n sourcesHash: computeServerSourcesHash(synced.state.resolved),\n },\n };\n const statePath = await writeSyncState(\n location.root,\n finalState,\n homeDirectory,\n path.join(directory, 'state.json'),\n );\n const projectSettingsPath =\n (commandOptions.settingsMode ?? 'persisted') === 'persisted'\n ? writeProjectPiSettings(location.root, homeDirectory)\n : undefined;\n const result: SyncResult = {\n statePath,\n ...(projectSettingsPath ? { projectSettingsPath } : {}),\n selection,\n mcpServers: synced.state.baseline.mcpConfigPath ? readMcpServerNames(synced.state.baseline.mcpConfigPath) : [],\n skillCount: context.resources.skillCount,\n agentCount: context.resources.agentCount,\n };\n publishSyncRegistration(\n location.root,\n {\n version: SYNC_REGISTRATION_VERSION,\n root: location.root,\n identity: location.identity,\n generation,\n generationRoot: directory,\n statePath,\n stateSha256: syncStateSha256(statePath),\n webDirectory: web.status === 'bundled' ? web.assetsDir : null,\n apiDirectory,\n serverBundle: { path: descriptorPath, fingerprint, sha256: syncStateSha256(descriptorPath) },\n package: packageRegistrationFor(location.root),\n },\n homeDirectory,\n );\n // ponytail: retain generations until host-owned drain evidence can prove no session uses them.\n // Directory age and an open-file check cannot establish that a lazy import is finished.\n return result;\n } catch (error) {\n await fs.promises.rm(directory, { recursive: true, force: true });\n throw error;\n }\n}\n\n/** Compatibility API. Executables call the command function directly. */\nexport class SyncCommand {\n readonly name = SYNC_COMMAND;\n private readonly settingsMode: SyncSettingsMode;\n private readonly homeDirectory: string | undefined;\n private readonly lockHeld: boolean;\n\n constructor(options: SyncCommandOptions = {}) {\n this.settingsMode = options.settingsMode ?? 'persisted';\n this.homeDirectory = options.homeDirectory;\n this.lockHeld = options.lockHeld ?? false;\n }\n\n matches(args: string[]): boolean {\n return args[0] === this.name;\n }\n\n async execute(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n ): Promise<number> {\n return synchronize(args, environment, currentDirectory, output, {\n settingsMode: this.settingsMode,\n homeDirectory: this.homeDirectory,\n lockHeld: this.lockHeld,\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["loadDoomConfigLenient"],"sources":["../../../../src/cli/commands/sync/index.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { globalDoomConfigDirectory } from '@agimon-ai/doompi-config/config';\nimport { loadDomains } from '@agimon-ai/doompi-config/domains';\nimport { filterHookDisabledLayers, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport { loadMajorModesConfig, loadMajorModesConfigLenient } from '@agimon-ai/doompi-config/majorModes';\nimport type { ConfigDiagnostic } from '@agimon-ai/doompi-config/types';\nimport {\n mergePiSettings,\n piAgentDirectory,\n piThemeDirectory,\n readPiSettings,\n serializePiSettings,\n} from '@agimon-ai/doompi-core/runtime-pi-settings';\nimport { DOOM_SERVER_BUNDLE_FILE } from '@agimon-ai/doompi-core/server-facet';\nimport {\n acquireSyncLocationLock,\n resolveSyncLocation,\n syncGenerationDirectory,\n} from '@agimon-ai/doompi-core/sync-location';\nimport {\n publishSyncRegistration,\n SYNC_REGISTRATION_VERSION,\n syncStateSha256,\n type SyncPackageRegistration,\n} from '@agimon-ai/doompi-core/sync-registration';\nimport { DEFAULT_THEME, DEFAULT_THEME_NAME } from '@agimon-ai/doompi-ui/theme';\n\nimport { buildSyncedRuntime } from '../../../builders/cli';\nimport { readBootstrapStatus } from '../../../builders/cli/bootstrapLocator';\nimport {\n createLayerResolvers,\n type ExtensionComposition,\n PERSONA_ENTRY,\n resolveExtensionComposition,\n} from '../../../builders/cli/extensionAssembler';\nimport { buildHarnessContext } from '../../../builders/cli/harnessContext';\nimport {\n doomPiPackageRoot,\n piExtensionAliasIsCurrent,\n writePiExtensionAlias,\n} from '../../../builders/cli/piExtensionAlias';\nimport {\n PI_DISPATCHER_VERSION,\n piExtensionDispatcherIsUpgradeable,\n piExtensionDispatcherVersion,\n} from '../../../builders/cli/piExtensionDispatcher';\nimport {\n DUPLICATE_REGISTRATION_DRIFT,\n projectRegistersDoom,\n writeProjectPiSettings,\n} from '../../../builders/cli/projectSettings';\nimport { syncServerBundle } from '../../../builders/server';\nimport { syncWebBundle } from '../../../builders/web';\nimport { HARNESS_STATE_POINTER, loadHarnessState } from '../../../composition/harnessState';\nimport { ensureLayerPackages, missingLayerPackageSpecifiers } from '../../../composition/layerPackageInstaller';\nimport { loadDoomConfigLenient } from '../../../composition/projectTrust';\nimport { resolveDoomConfigurationRoot } from '../../../composition/repository';\nimport { readSyncDrift } from '../../../composition/syncDrift';\nimport {\n computeInputsHash,\n computeWebSourcesHash,\n computeServerSourcesHash,\n readLocatedSyncState,\n readMcpServerNames,\n recordResolvedEntries,\n SYNC_STATE_VERSION,\n type SyncSelection,\n type SyncState,\n syncStateRootMatches,\n writeSyncState,\n} from '../../../composition/syncState';\nimport type { HarnessOptions } from '../../../composition/types/harness';\nimport { DOOMPI_DOMAINS_ENV, DOOMPI_MAJOR_MODE_ENV, DOOMPI_PROFILE_ENV } from '../../matrixOptions';\nimport { parseHarnessArgs } from '../../options';\nimport { SyncProgress, type SyncProgressOutput } from './presenter';\n\n/**\n * `doom-pi sync`: resolve the matrix once and write it where plain Pi finds it.\n *\n * The doom-emacs split. Everything that needs a real Node process (module\n * resolution, staging skills and agents, generating the MCP config) happens\n * here, and the doom-pi extension then only reads what this produced. The\n * launcher is untouched and keeps resolving the same matrix per run.\n */\n\nconst SYNC_COMMAND = 'sync';\nconst CHECK_OPTION = '--check';\n/** Republishes even when nothing drifted, for a generation suspected of being damaged. */\nconst FORCE_OPTION = '--force';\nconst GLOBAL_OPTION = '--global';\nconst HARNESS_ROOT_ENV = 'DOOMPI_ROOT';\nconst PERSONA_FILE_ENV = 'DOOMPI_PERSONA_FILE';\nconst HOOK_EMITTER = path.join('tools', 'harness', 'emit-hooks.mjs');\nconst NONE = '(none)';\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst SYNC_LABEL = 'sync';\nconst RUNTIME_LABEL = 'runtime';\nconst WEB_LABEL = 'web';\nconst API_LABEL = 'api';\n\n/**\n * Harness variables worth recording, by prefix or exact name.\n *\n * An allowlist rather than the whole environment: the state file is a snapshot\n * of resolved configuration, and dumping `process.env` into it would write\n * every credential the sync happened to run with onto disk.\n */\nconst RECORDED_PREFIXES = ['DOOMPI_'];\nconst RECORDED_KEYS = ['CLAUDE_PROJECT_DIR', 'CODEX_REPO_ROOT', 'ORIGINAL_REPO_PATH', 'MCP_UI_VIEWER'];\n/**\n * Launcher-only values a synced session must not inherit.\n *\n * The child extension list is recomposed on every load, and the subagent binary\n * points at `pi.sh`, which a session started as plain `pi` should not shell out\n * to: Doom Team resolves Pi's own CLI when the variable is absent.\n */\nconst EXCLUDED_KEYS = new Set([\n 'DOOMPI_CHILD_EXTENSIONS',\n 'DOOMPI_COMPOSED',\n 'DOOMPI_MUTE',\n 'DOOMPI_TEMP_DIR',\n // A pointer to the syncing process's own state file. Recording it would hand\n // every later session a path to a state that died with this one.\n HARNESS_STATE_POINTER,\n 'PI_SUBAGENT_PI_BINARY',\n]);\n\ntype SyncOutput = SyncProgressOutput;\n\nexport type SyncSettingsMode = 'persisted' | 'embedded';\n\nexport interface SyncCommandOptions {\n settingsMode?: SyncSettingsMode;\n /** Test/embedding override; normal CLI execution uses the process home. */\n homeDirectory?: string;\n /** Internal pipeline seam when the caller owns the worktree lock. */\n lockHeld?: boolean;\n}\n\nexport interface SyncResult {\n statePath: string;\n /** Omitted when DPI supplies the integration as a process-local overlay. */\n settingsPath?: string;\n /** Set only when the repository still carried its own DoomPi registration. */\n projectSettingsPath?: string;\n selection: SyncSelection;\n mcpServers: string[];\n skillCount: number;\n agentCount: number;\n}\n\nexport function recordedEnvironment(environment: NodeJS.ProcessEnv): Record<string, string> {\n const recorded: Record<string, string> = {};\n for (const [key, value] of Object.entries(environment)) {\n if (value === undefined || EXCLUDED_KEYS.has(key)) continue;\n if (RECORDED_KEYS.includes(key) || RECORDED_PREFIXES.some((prefix) => key.startsWith(prefix))) {\n recorded[key] = value;\n }\n }\n return recorded;\n}\n\n/**\n * Reports the config keys sync chose to ignore.\n *\n * Never fatal. A key nobody recognises is usually a config written for another\n * version of a layer, and refusing to build over it is worse than proceeding\n * without it. The strict check lives in `doompi doctor`.\n */\nfunction writeConfigDiagnostics(diagnostics: readonly ConfigDiagnostic[], output: SyncOutput): void {\n if (diagnostics.length === 0) return;\n const lines = diagnostics.map((entry) => ` ${entry.filePath}: ${entry.path}`).join('\\n');\n output.write(\n `config: ignored ${String(diagnostics.length)} unsupported key(s); run doompi doctor for the strict check\\n${lines}\\n`,\n );\n}\n/**\n * Layers the repository's declared selection under the usual resolution.\n *\n * `.doom/config.yaml` holds what the repository selects by default, the way\n * init.el does for doom-emacs. Seeding the environment the parser reads keeps\n * the precedence the launcher already documents: an explicit flag wins, then an\n * exported variable, then the declared default.\n */\nexport function selectionEnvironment(\n repoRoot: string,\n environment: NodeJS.ProcessEnv,\n homeDirectory?: string,\n): NodeJS.ProcessEnv {\n const { selection } = loadDoomConfigLenient(repoRoot, homeDirectory).config;\n if (!selection) return environment;\n return {\n ...environment,\n ...(selection.majorMode && !environment[DOOMPI_MAJOR_MODE_ENV]\n ? { [DOOMPI_MAJOR_MODE_ENV]: selection.majorMode }\n : {}),\n ...(selection.profile && !environment[DOOMPI_PROFILE_ENV] ? { [DOOMPI_PROFILE_ENV]: selection.profile } : {}),\n ...(selection.domains && environment[DOOMPI_DOMAINS_ENV] === undefined\n ? { [DOOMPI_DOMAINS_ENV]: selection.domains.join(',') }\n : {}),\n };\n}\n\nexport function toSelection(\n options: Pick<HarnessOptions, 'majorMode' | 'domains' | 'profile' | 'preset'>,\n): SyncSelection {\n return {\n majorMode: options.majorMode,\n domains: options.domains,\n profile: options.profile,\n preset: options.preset,\n };\n}\n\nexport function selectionCompositionFingerprint(\n repoRoot: string,\n options: Pick<HarnessOptions, 'agents' | 'hooks' | 'majorMode' | 'mcp' | 'preset'>,\n homeDirectory: string = os.homedir(),\n): string {\n const majorModesConfig = loadMajorModesConfig(repoRoot, homeDirectory);\n const resolvers = createLayerResolvers(repoRoot);\n return resolveExtensionComposition({\n agents: options.agents,\n autoStop: false,\n mute: false,\n preset: options.preset,\n personaEntry: resolvers.packageEntry(PERSONA_ENTRY),\n majorMode: options.majorMode,\n layers: filterHookDisabledLayers(\n majorModesConfig,\n resolveLayers(majorModesConfig, options.majorMode),\n options.hooks,\n ),\n majorModesConfig,\n resolvers,\n }).fingerprint;\n}\n\n/** Settings, dispatcher and theme differences an init would fix, independent of sync state. */\nfunction piIntegrationDrift(agentDirectory: string): string[] {\n const drift: string[] = [];\n const themePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n const settings = readPiSettings(agentDirectory);\n const merged = mergePiSettings(settings, agentDirectory, { themePath, themeName: DEFAULT_THEME_NAME });\n if (serializePiSettings(merged) !== serializePiSettings(settings)) {\n drift.push('Pi user settings are out of date; run doompi init');\n }\n if (!piExtensionAliasIsCurrent(agentDirectory)) drift.push('Pi user dispatcher is out of date; run doompi init');\n const expectedTheme = `${JSON.stringify(DEFAULT_THEME, null, 2)}\\n`;\n if (!fs.existsSync(themePath) || fs.readFileSync(themePath, 'utf8') !== expectedTheme) {\n drift.push('Pi user theme is out of date; run doompi init');\n }\n return drift;\n}\n\n/** Differences between what a sync would produce and what is on disk. */\nexport function collectDrift(\n repoRoot: string,\n selection: SyncSelection,\n state: SyncState | undefined,\n environment: NodeJS.ProcessEnv = process.env,\n settingsMode: SyncSettingsMode = 'persisted',\n expectedCompositionFingerprint?: string,\n): string[] {\n if (!state) return ['no sync state: run doompi sync'];\n const drift: string[] = [];\n if (!syncStateRootMatches(repoRoot, state.root)) drift.push('sync state belongs to a different repository');\n const recorded = state.selection;\n if (\n recorded.majorMode !== selection.majorMode ||\n recorded.profile !== selection.profile ||\n recorded.preset !== selection.preset ||\n recorded.domains.join(',') !== selection.domains.join(',')\n ) {\n drift.push('selection changed since the last sync');\n }\n // Hashed against the recorded selection, not the requested one, so a\n // selection change is reported once rather than as two findings.\n if (computeInputsHash(repoRoot, recorded, environment.HOME ?? os.homedir()) !== state.inputsHash) {\n drift.push('.doom configuration changed');\n }\n // Re-resolving is what catches a dependency upgrade moving a package, which\n // the inputs hash deliberately does not read.\n if (\n JSON.stringify(\n recordResolvedEntries(\n loadMajorModesConfig(repoRoot, environment.HOME ?? os.homedir()),\n createLayerResolvers(repoRoot),\n ),\n ) !== JSON.stringify(state.resolved)\n ) {\n drift.push('resolved extension paths changed');\n }\n if (expectedCompositionFingerprint && state.compositionFingerprint !== expectedCompositionFingerprint) {\n drift.push('extension composition changed');\n }\n try {\n if (!readBootstrapStatus(repoRoot, undefined, environment.HOME ?? os.homedir()).fresh) {\n drift.push('precompiled runtime is missing or stale');\n }\n } catch {\n drift.push('precompiled runtime is missing or stale');\n }\n\n if (settingsMode === 'persisted') {\n const agentDirectory = piAgentDirectory(environment);\n const themePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n drift.push(...piIntegrationDrift(agentDirectory));\n if (projectRegistersDoom(repoRoot)) drift.push(DUPLICATE_REGISTRATION_DRIFT);\n if (state.baseline.themePath !== themePath || state.baseline.themeName !== DEFAULT_THEME_NAME) {\n drift.push('synced theme location is out of date');\n }\n }\n if (\n readSyncDrift({ repoRoot, homeDirectory: environment.HOME ?? os.homedir() }).reasons.includes('server-bundle-stale')\n ) {\n drift.push('server bundle is missing or stale');\n }\n return drift;\n}\n\n/** Regenerates the hook files the other frontends read before any harness code runs. */\nfunction emitFrontendHooks(repoRoot: string, output: SyncOutput): void {\n const emitter = path.join(repoRoot, HOOK_EMITTER);\n if (!fs.existsSync(emitter)) return;\n const result = spawnSync(process.execPath, [emitter, '--write'], { cwd: repoRoot, encoding: 'utf8' });\n if (result.status === 0) {\n output.write('hooks: regenerated for Claude Code and Codex\\n');\n return;\n }\n output.write(`hooks: emit-hooks failed (${result.stderr?.trim() || `exit ${String(result.status)}`})\\n`);\n}\n\nexport function formatSyncResult(result: SyncResult, runner = 'pi'): string {\n const { selection } = result;\n return [\n `mode: ${selection.majorMode}`,\n `domains: ${selection.domains.join(', ') || NONE}`,\n `profile: ${selection.profile ?? NONE}`,\n `skills: ${result.skillCount}`,\n `agents: ${result.agentCount}`,\n `mcp: ${result.mcpServers.join(', ') || NONE}`,\n `state: ${result.statePath}`,\n ...(result.settingsPath ? [`settings: ${result.settingsPath}`] : []),\n ...(result.projectSettingsPath\n ? [`project: removed duplicate registration from ${result.projectSettingsPath}`]\n : []),\n '',\n `Run ${runner} from the repository root to use it.`,\n '',\n ].join('\\n');\n}\n\n/**\n * The DoomPi that produced this generation, which is the one that can load it.\n *\n * Always the executing package, never another copy the repository happens to\n * install. A generation is not portable between two installations: the bundles\n * are compiled from the building package's own extension entries, the recorded\n * compiler inputs are its files, and the state names its bootstrap entry. Naming\n * a second copy here publishes a registration whose package disagrees with the\n * state it points at, and Pi's dispatcher then loads a harness that rejects the\n * bootstrap as stale on every session, with no sync able to fix it.\n *\n * A repository that wants its own copy to own its sessions runs sync with that\n * copy's CLI, which makes it the executing package.\n */\nfunction packageRegistrationFor(): SyncPackageRegistration {\n const root = fs.realpathSync(doomPiPackageRoot());\n const manifestPath = path.join(root, 'package.json');\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as {\n version?: unknown;\n pi?: { extensions?: unknown };\n };\n const version = manifest.version;\n const extensions = manifest.pi?.extensions;\n const extension = Array.isArray(extensions) ? extensions.find((value) => typeof value === 'string') : undefined;\n if (typeof version !== 'string' || typeof extension !== 'string') {\n throw new Error(`Installed DoomPi package at ${root} has no versioned Pi extension entry`);\n }\n return {\n root,\n version,\n manifestPath,\n entry: fs.realpathSync(path.resolve(root, extension)),\n };\n}\n\n/** Resolves the matrix, stages it into home-scoped worktree storage, and publishes one generation. */\nexport async function synchronize(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n commandOptions: SyncCommandOptions = {},\n): Promise<number> {\n const check = args.includes(CHECK_OPTION);\n const force = args.includes(FORCE_OPTION);\n const globalOnly = args.includes(GLOBAL_OPTION);\n const rest = args.slice(1).filter((argument) => ![CHECK_OPTION, FORCE_OPTION, GLOBAL_OPTION].includes(argument));\n const homeDirectory = commandOptions.homeDirectory ?? environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const globalRoot = globalDoomConfigDirectory(homeDirectory);\n const repoRoot = globalOnly\n ? globalRoot\n : inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n if (globalOnly && !check) fs.mkdirSync(globalRoot, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n // A fresh home has no global plugin selection until `doompi init` creates\n // modes.yaml. Repository sync can still publish its own workspace bundle.\n // A check validates its requested scope. Another installed package may own\n // the shared global generation without making this workspace stale.\n if (\n !check &&\n !globalOnly &&\n path.resolve(repoRoot) !== path.resolve(globalRoot) &&\n fs.existsSync(path.join(globalRoot, 'modes.yaml'))\n ) {\n const globalEnvironment = { ...environment };\n for (const key of [\n HARNESS_ROOT_ENV,\n HARNESS_STATE_POINTER,\n DOOMPI_MAJOR_MODE_ENV,\n DOOMPI_DOMAINS_ENV,\n DOOMPI_PROFILE_ENV,\n ])\n delete globalEnvironment[key];\n const globalStatus = await synchronize(\n [SYNC_COMMAND, GLOBAL_OPTION, ...(force ? [FORCE_OPTION] : [])],\n globalEnvironment,\n globalRoot,\n output,\n { settingsMode: 'embedded', homeDirectory },\n );\n if (globalStatus !== 0) return globalStatus;\n }\n // Sync tolerates keys it does not recognise so a config written against a\n // different version cannot break a build. `doompi doctor` reports them.\n const modes = loadMajorModesConfigLenient(repoRoot, homeDirectory);\n const configDiagnostics = [...loadDoomConfigLenient(repoRoot, homeDirectory).diagnostics, ...modes.diagnostics];\n const defaultMajorMode = modes.config.defaultMajorMode;\n const defaultDomains = loadDomains(repoRoot, homeDirectory).defaultDomains;\n const parsed = parseHarnessArgs(\n rest,\n selectionEnvironment(repoRoot, environment, homeDirectory),\n globalOnly ? globalRoot : currentDirectory,\n defaultMajorMode,\n defaultDomains,\n );\n const selection = toSelection(parsed.options);\n const agentDirectory = piAgentDirectory(environment, homeDirectory);\n if ((commandOptions.settingsMode ?? 'persisted') === 'persisted' && !check) {\n if (piExtensionDispatcherIsUpgradeable(agentDirectory)) {\n const previousVersion = piExtensionDispatcherVersion(agentDirectory);\n writePiExtensionAlias(agentDirectory);\n output.write(\n `repair: upgraded Pi user dispatcher from protocol ${String(previousVersion)} to ${String(PI_DISPATCHER_VERSION)}\\n`,\n );\n }\n const drift = piIntegrationDrift(agentDirectory);\n if (drift.length > 0) {\n throw new Error(`DoomPi Pi integration is not ready:\\n${drift.map((entry) => ` ${entry}`).join('\\n')}`);\n }\n }\n writeConfigDiagnostics(configDiagnostics, output);\n if (check) {\n const majorModesConfig = modes.config;\n const missingPackages = missingLayerPackageSpecifiers(\n majorModesConfig,\n Object.keys(majorModesConfig.layers),\n createLayerResolvers(repoRoot),\n );\n if (missingPackages.length > 0) {\n output.write(\n `doompi sync is out of date:\\n${missingPackages\n .map((specifier) => ` configured package is not installed: ${specifier}`)\n .join('\\n')}\\n`,\n );\n return 1;\n }\n let located: ReturnType<typeof readLocatedSyncState>;\n try {\n located = readLocatedSyncState(repoRoot, homeDirectory);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n output.write(`doompi sync is out of date:\\n ${detail}\\n`);\n return 1;\n }\n const expectedCompositionFingerprint = selectionCompositionFingerprint(repoRoot, parsed.options, homeDirectory);\n const drift = collectDrift(\n repoRoot,\n selection,\n located?.state,\n environment,\n commandOptions.settingsMode ?? 'persisted',\n expectedCompositionFingerprint,\n );\n if (drift.length === 0) {\n output.write('doompi sync is up to date\\n');\n return 0;\n }\n output.write(`doompi sync is out of date:\\n${drift.map((entry) => ` ${entry}`).join('\\n')}\\n`);\n return 1;\n }\n\n // Publishing an identical generation is not a no-op: it moves the\n // registration, so every attached cockpit reloads and the previous\n // generation becomes garbage. Same inputs, same published result.\n const driftOptions = {\n repoRoot,\n homeDirectory,\n requireWebBundle: Boolean(environment.DOOMPI_WEB_PACKAGE_ROOT),\n };\n if (!force && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n\n const progress = new SyncProgress(output);\n const releaseLock = commandOptions.lockHeld\n ? undefined\n : await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n let result: SyncResult;\n try {\n // A concurrent publisher may have resolved the drift while this command\n // waited for the lock. Avoid moving the registration for no change.\n if (!force && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n result = await stageSync(repoRoot, parsed.options, environment, homeDirectory, progress, commandOptions);\n } finally {\n await releaseLock?.();\n }\n emitFrontendHooks(repoRoot, output);\n output.write(formatSyncResult(result, (commandOptions.settingsMode ?? 'persisted') === 'embedded' ? 'dpi' : 'pi'));\n return 0;\n}\n\nasync function stageSync(\n repoRoot: string,\n options: Omit<HarnessOptions, 'repoRoot'>,\n environment: NodeJS.ProcessEnv,\n homeDirectory: string,\n progress: SyncProgress,\n commandOptions: SyncCommandOptions = {},\n): Promise<SyncResult> {\n const location = resolveSyncLocation(repoRoot, homeDirectory);\n const generation = `${Date.now().toString(36)}-${crypto.randomUUID()}`;\n const directory = syncGenerationDirectory(location, generation);\n await fs.promises.mkdir(location.generationsDirectory, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n // The leaf is created without `recursive`, so an existing path is an error\n // rather than something to adopt: the cockpit signs and serves whatever the\n // published generation holds, and sync must only ever publish bytes it\n // wrote itself into a directory it just created.\n await fs.promises.mkdir(directory, { mode: PRIVATE_DIRECTORY_MODE });\n\n try {\n const staged = progress.start(SYNC_LABEL, 'resolving the matrix and staging resources');\n const context = await buildHarnessContext({\n ...options,\n repoRoot: location.root,\n homeDirectory,\n cwd: location.root,\n resourceDirectory: directory,\n });\n await ensureLayerPackages({\n repoRoot: location.root,\n config: context.majorModesConfig,\n layers: Object.keys(context.majorModesConfig.layers),\n environment,\n });\n staged(`${String(context.resources.skillCount)} skills, ${String(context.resources.agentCount)} agents`);\n const selection = toSelection(options);\n const resolvers = createLayerResolvers(location.root);\n const resolved = recordResolvedEntries(context.majorModesConfig, resolvers);\n const compositionFingerprint = selectionCompositionFingerprint(location.root, options, homeDirectory);\n const agentDirectory = piAgentDirectory(environment, homeDirectory);\n const persistedThemePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n const themePath =\n (commandOptions.settingsMode ?? 'persisted') === 'persisted' ? persistedThemePath : context.defaultThemePath;\n const state: SyncState = {\n version: SYNC_STATE_VERSION,\n root: location.root,\n identity: location.identity,\n inputsHash: computeInputsHash(location.root, selection, homeDirectory),\n webSourcesHash: computeWebSourcesHash(resolved),\n compositionFingerprint,\n selection,\n env: recordedEnvironment(context.environment),\n fileState: {\n profileEnvironment: loadHarnessState(context.environment).state.profileEnvironment,\n pluginHooks: context.resources.pluginHooks,\n mcpProjection: context.resources.mcpProjection,\n },\n resolved,\n baseline: {\n mcpConfigPath: context.resources.mcpConfigPath,\n personaFile: context.environment[PERSONA_FILE_ENV],\n themePath,\n themeName: DEFAULT_THEME_NAME,\n },\n };\n\n // Runtime compilation writes the package dist files consumed by both the web\n // and server bundlers. Finish it first so a package clean cannot race either\n // consumer, then run the independent web and server builds together.\n let resolveCompositions!: (compositions: readonly ExtensionComposition[]) => void;\n let rejectCompositions!: (reason?: unknown) => void;\n const compositionsReady = new Promise<readonly ExtensionComposition[]>((resolve, reject) => {\n resolveCompositions = resolve;\n rejectCompositions = reject;\n });\n const runtimeProgress = progress.start(RUNTIME_LABEL, 'precompiling the mode bundles');\n const runtimeBuild = buildSyncedRuntime(location.root, environment, homeDirectory, {\n state,\n directory,\n onCompositionsResolved: resolveCompositions,\n }).then((synced) => {\n runtimeProgress(`${String(Object.keys(synced.bundles).length)} mode bundles`);\n return synced;\n });\n void runtimeBuild.catch(rejectCompositions);\n const webBuild = (async () => {\n await runtimeBuild;\n const webProgress = progress.start(WEB_LABEL, 'bundling the web cockpit plugins');\n const web = await syncWebBundle({\n repoRoot: location.root,\n resolvedEntries: state.resolved,\n environment,\n outputDirectory: path.join(directory, 'web-bundle'),\n onNotice: (message) => progress.line(WEB_LABEL, message),\n });\n if (web.status === 'failed') throw new Error(`Cockpit bundle failed: ${web.reason}`);\n webProgress(web.status === 'bundled' ? `cockpit bundled with plugins: ${web.pluginIds.join(', ')}` : web.reason);\n return web;\n })();\n const serverBuild = (async () => {\n const compositions = await compositionsReady;\n await runtimeBuild;\n const apiProgress = progress.start(API_LABEL, 'compiling the server bundle');\n const apiDirectory = path.join(directory, 'api');\n const fingerprint = crypto\n .createHash('sha256')\n .update(JSON.stringify([...new Set(compositions.map((composition) => composition.fingerprint))]))\n .digest('hex');\n const server = await syncServerBundle({\n repositoryRoot: location.root,\n generation,\n fingerprint,\n compositions,\n outputDirectory: apiDirectory,\n cacheDirectory: path.join(directory, 'cache'),\n sharedCacheDirectory: location.sharedCacheDirectory,\n });\n apiProgress(`${server.descriptor.entries.length} server facet(s) compiled`);\n for (const gap of server.contractGaps) progress.line(API_LABEL, `API contract incomplete: ${gap}`);\n return { server, fingerprint, apiDirectory };\n })();\n const [runtimeResult, webResult, serverResult] = await Promise.allSettled([runtimeBuild, webBuild, serverBuild]);\n if (runtimeResult.status === 'rejected') throw runtimeResult.reason;\n if (webResult.status === 'rejected') throw webResult.reason;\n if (serverResult.status === 'rejected') throw serverResult.reason;\n const synced = runtimeResult.value;\n const web = webResult.value;\n const { server, fingerprint, apiDirectory } = serverResult.value;\n const descriptorPath = path.join(apiDirectory, DOOM_SERVER_BUNDLE_FILE);\n const finalState: SyncState = {\n ...synced.state,\n serverBundle: {\n descriptorPath,\n fingerprint,\n compilerManifests: server.compilerManifests,\n sourcesHash: computeServerSourcesHash(synced.state.resolved),\n },\n };\n const statePath = await writeSyncState(\n location.root,\n finalState,\n homeDirectory,\n path.join(directory, 'state.json'),\n );\n const projectSettingsPath =\n (commandOptions.settingsMode ?? 'persisted') === 'persisted'\n ? writeProjectPiSettings(location.root, homeDirectory)\n : undefined;\n const result: SyncResult = {\n statePath,\n ...(projectSettingsPath ? { projectSettingsPath } : {}),\n selection,\n mcpServers: synced.state.baseline.mcpConfigPath ? readMcpServerNames(synced.state.baseline.mcpConfigPath) : [],\n skillCount: context.resources.skillCount,\n agentCount: context.resources.agentCount,\n };\n publishSyncRegistration(\n location.root,\n {\n version: SYNC_REGISTRATION_VERSION,\n root: location.root,\n identity: location.identity,\n generation,\n generationRoot: directory,\n statePath,\n stateSha256: syncStateSha256(statePath),\n webDirectory: web.status === 'bundled' ? web.assetsDir : null,\n apiDirectory,\n serverBundle: { path: descriptorPath, fingerprint, sha256: syncStateSha256(descriptorPath) },\n package: packageRegistrationFor(),\n },\n homeDirectory,\n );\n // ponytail: retain generations until host-owned drain evidence can prove no session uses them.\n // Directory age and an open-file check cannot establish that a lazy import is finished.\n return result;\n } catch (error) {\n await fs.promises.rm(directory, { recursive: true, force: true });\n throw error;\n }\n}\n\n/** Compatibility API. Executables call the command function directly. */\nexport class SyncCommand {\n readonly name = SYNC_COMMAND;\n private readonly settingsMode: SyncSettingsMode;\n private readonly homeDirectory: string | undefined;\n private readonly lockHeld: boolean;\n\n constructor(options: SyncCommandOptions = {}) {\n this.settingsMode = options.settingsMode ?? 'persisted';\n this.homeDirectory = options.homeDirectory;\n this.lockHeld = options.lockHeld ?? false;\n }\n\n matches(args: string[]): boolean {\n return args[0] === this.name;\n }\n\n async execute(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n ): Promise<number> {\n return synchronize(args, environment, currentDirectory, output, {\n settingsMode: this.settingsMode,\n homeDirectory: this.homeDirectory,\n lockHeld: this.lockHeld,\n });\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi",
3
- "version": "0.0.1-alpha.71",
3
+ "version": "0.0.1-alpha.72",
4
4
  "description": "Opinionated, composable Pi distribution for scoped agent tools, skills, MCP servers, and developer workflows.",
5
5
  "keywords": [
6
6
  "ai",
@@ -299,18 +299,18 @@
299
299
  "vite": "8.2.2",
300
300
  "ws": "8.21.3",
301
301
  "yaml": "2.9.0",
302
- "@agimon-ai/doompi-autostop": "0.0.1-alpha.47",
303
- "@agimon-ai/doompi-cache": "0.0.1-alpha.36",
304
- "@agimon-ai/doompi-config": "0.0.1-alpha.69",
305
- "@agimon-ai/doompi-core": "0.0.1-alpha.70",
306
- "@agimon-ai/doompi-domain": "0.0.1-alpha.48",
307
- "@agimon-ai/doompi-major-mode": "0.0.1-alpha.48",
308
- "@agimon-ai/doompi-notification": "0.0.1-alpha.47",
309
- "@agimon-ai/doompi-skill": "0.0.1-alpha.48",
310
- "@agimon-ai/doompi-profile": "0.0.1-alpha.48",
311
- "@agimon-ai/doompi-minor-mode": "0.0.1-alpha.70",
302
+ "@agimon-ai/doompi-autostop": "0.0.1-alpha.48",
303
+ "@agimon-ai/doompi-cache": "0.0.1-alpha.37",
304
+ "@agimon-ai/doompi-config": "0.0.1-alpha.70",
305
+ "@agimon-ai/doompi-core": "0.0.1-alpha.71",
306
+ "@agimon-ai/doompi-domain": "0.0.1-alpha.49",
307
+ "@agimon-ai/doompi-major-mode": "0.0.1-alpha.49",
308
+ "@agimon-ai/doompi-minor-mode": "0.0.1-alpha.71",
309
+ "@agimon-ai/doompi-notification": "0.0.1-alpha.48",
310
+ "@agimon-ai/doompi-profile": "0.0.1-alpha.49",
311
+ "@agimon-ai/doompi-skill": "0.0.1-alpha.49",
312
312
  "@agimon-ai/doompi-telemetry": "0.0.1-alpha.68",
313
- "@agimon-ai/doompi-ui": "0.0.1-alpha.70",
313
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.71",
314
314
  "@agimon-ai/doompi-web-security": "0.0.1-alpha.31"
315
315
  },
316
316
  "devDependencies": {
@@ -324,7 +324,7 @@
324
324
  "tsdown": "0.23.0",
325
325
  "typescript": "7.0.2",
326
326
  "vitest": "4.1.11",
327
- "@agimon-ai/doompi-runner": "0.0.1-alpha.70",
327
+ "@agimon-ai/doompi-runner": "0.0.1-alpha.71",
328
328
  "@agimon-ai/vibe-lint-plugin-doom-cli": "0.0.1-alpha.2"
329
329
  },
330
330
  "peerDependencies": {