@agimon-ai/doompi 0.0.1-alpha.55 → 0.0.1-alpha.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -50,7 +50,7 @@ releases.
50
50
 
51
51
  - Node.js 22.19.0 or newer
52
52
  - macOS or Linux on arm64 or x64 for the bundled Runner backend
53
- - Pi 0.84.4 and Pi TUI 0.84.4 for packages that declare them as peer requirements
53
+ - Pi 0.85.0 and Pi TUI 0.85.0 for packages that declare them as peer requirements
54
54
 
55
55
  ## Try DoomPi without replacing your Pi setup
56
56
 
@@ -1,2 +1,2 @@
1
- const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("./syncRegistration.cjs"),n=require("./syncState.cjs"),r=require("./bootstrapLocator.cjs");let i=require("node:path");i=e.__toESM(i,1);let a=require("node:fs");a=e.__toESM(a,1);let o=require("node:os");o=e.__toESM(o,1);function s(e){let s=e.homeDirectory??o.default.homedir(),c=[],l,u;try{u=t.readSyncRegistration(e.repoRoot,s),l=u?n.readSyncState(e.repoRoot,s):void 0}catch{l=void 0,u=void 0}if(!l||!u)return{fresh:!1,reasons:[`never-synced`]};let d;try{d=n.computeInputsHash(e.repoRoot,l.selection,s)}catch{c.push(`configuration-changed`)}d!==void 0&&d!==l.inputsHash&&c.push(`configuration-changed`),l.webSourcesHash!==void 0&&n.computeWebSourcesHash(l.resolved)!==l.webSourcesHash&&c.push(`code-changed`);try{r.readBootstrapStatus(e.repoRoot,e.expectedBootstrapEntry,s).fresh||c.push(`runtime-stale`)}catch{c.push(`runtime-stale`)}return u.webDirectory!==null&&(!a.default.existsSync(i.default.join(u.webDirectory,`index.html`))||!a.default.existsSync(i.default.join(i.default.dirname(u.webDirectory),`plugins`,`composition.js`))||!a.default.existsSync(i.default.join(i.default.dirname(u.webDirectory),`plugins`,`manifest.json`)))&&c.push(`cockpit-bundle-missing`),a.default.existsSync(u.apiDirectory)||c.push(`package-apis-missing`),{fresh:c.length===0,reasons:c,recordedInputsHash:l.inputsHash,...d===void 0?{}:{currentInputsHash:d}}}function c(e){if(e.fresh)return`the repository is synced`;let t={"never-synced":`it has never been synced`,"configuration-changed":`its configuration changed since the last sync`,"code-changed":`its cockpit sources changed since the last sync`,"runtime-stale":`its precompiled runtime is out of date`,"cockpit-bundle-missing":`the cockpit bundle is missing`,"package-apis-missing":`the package API routes are missing`};return e.reasons.map(e=>t[e]).join(`, `)}exports.describeSyncDrift=c,exports.readSyncDrift=s;
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("./syncRegistration.cjs"),n=require("./syncState.cjs"),r=require("./bootstrapLocator.cjs");let i=require("node:path");i=e.__toESM(i,1);let a=require("node:fs");a=e.__toESM(a,1);let o=require("node:os");o=e.__toESM(o,1);function s(e){let s=e.homeDirectory??o.default.homedir(),c=[],l,u;try{u=t.readSyncRegistration(e.repoRoot,s),l=u?n.readSyncState(e.repoRoot,s):void 0}catch{l=void 0,u=void 0}if(!l||!u)return{fresh:!1,reasons:[`never-synced`]};let d;try{d=n.computeInputsHash(e.repoRoot,l.selection,s)}catch{c.push(`configuration-changed`)}d!==void 0&&d!==l.inputsHash&&c.push(`configuration-changed`),l.webSourcesHash!==void 0&&n.computeWebSourcesHash(l.resolved)!==l.webSourcesHash&&c.push(`code-changed`);try{r.readBootstrapStatus(e.repoRoot,e.expectedBootstrapEntry,s).fresh||c.push(`runtime-stale`)}catch{c.push(`runtime-stale`)}return(u.webDirectory===null&&e.requireWebBundle||u.webDirectory!==null&&(!a.default.existsSync(i.default.join(u.webDirectory,`index.html`))||!a.default.existsSync(i.default.join(i.default.dirname(u.webDirectory),`plugins`,`composition.js`))||!a.default.existsSync(i.default.join(i.default.dirname(u.webDirectory),`plugins`,`manifest.json`))))&&c.push(`cockpit-bundle-missing`),a.default.existsSync(u.apiDirectory)||c.push(`package-apis-missing`),{fresh:c.length===0,reasons:c,recordedInputsHash:l.inputsHash,...d===void 0?{}:{currentInputsHash:d}}}function c(e){if(e.fresh)return`the repository is synced`;let t={"never-synced":`it has never been synced`,"configuration-changed":`its configuration changed since the last sync`,"code-changed":`its cockpit sources changed since the last sync`,"runtime-stale":`its precompiled runtime is out of date`,"cockpit-bundle-missing":`the cockpit bundle is missing`,"package-apis-missing":`the package API routes are missing`};return e.reasons.map(e=>t[e]).join(`, `)}exports.describeSyncDrift=c,exports.readSyncDrift=s;
2
2
  //# sourceMappingURL=syncDrift.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"syncDrift.cjs","names":["os","readSyncRegistration","readSyncState","computeInputsHash","computeWebSourcesHash","readBootstrapStatus","fs","path"],"sources":["../../../src/adapters/syncDrift.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { readBootstrapStatus } from './bootstrapLocator.ts';\nimport { computeInputsHash, computeWebSourcesHash, readSyncState, type SyncState } from './syncState.ts';\nimport { readSyncRegistration } from './syncRegistration.ts';\n\nexport type SyncDriftReason =\n | 'never-synced'\n | 'configuration-changed'\n | 'code-changed'\n | 'runtime-stale'\n | 'cockpit-bundle-missing'\n | 'package-apis-missing';\n\nexport interface SyncDrift {\n /** True when nothing needs syncing before a session starts. */\n fresh: boolean;\n reasons: SyncDriftReason[];\n /** The recorded inputs hash, for a caller that wants to log what moved. */\n recordedInputsHash?: string;\n currentInputsHash?: string;\n}\n\nexport interface ReadSyncDriftOptions {\n repoRoot: string;\n /** Launcher-owned Doom entry used to build this repository's generated bootstrap. */\n expectedBootstrapEntry?: string;\n homeDirectory?: string;\n}\n\n/**\n * Whether this repository is synced for the composition it would launch.\n *\n * Sync produces three things a session or the cockpit reads later: the\n * resolved composition, the plugin bundle the browser loads, and the package\n * API routes each host mounts. A session started against any of them stale\n * runs, but quietly wrong: the cockpit shows no plugin surfaces and package\n * APIs answer nothing, with nothing in the log to say why.\n *\n * The hash is the one sync already records. It covers every `.doom` document\n * in both the repository and the home directory, the MCP configuration, the\n * plugin catalogue, and each profile's persona files, so an edit to any of\n * them reads as drift without this having to know what changed.\n */\nexport function readSyncDrift(options: ReadSyncDriftOptions): SyncDrift {\n const homeDirectory = options.homeDirectory ?? os.homedir();\n const reasons: SyncDriftReason[] = [];\n\n let state: SyncState | undefined;\n let registration: ReturnType<typeof readSyncRegistration>;\n try {\n registration = readSyncRegistration(options.repoRoot, homeDirectory);\n state = registration ? readSyncState(options.repoRoot, homeDirectory) : undefined;\n } catch {\n // Unreadable or invalid registration is indistinguishable from absent for\n // this purpose: the next session needs a sync before it can be trusted.\n state = undefined;\n registration = undefined;\n }\n if (!state || !registration) return { fresh: false, reasons: ['never-synced'] };\n\n let currentInputsHash: string | undefined;\n try {\n currentInputsHash = computeInputsHash(options.repoRoot, state.selection, homeDirectory);\n } catch {\n // A malformed document cannot be hashed, and syncing is what reports it.\n reasons.push('configuration-changed');\n }\n if (currentInputsHash !== undefined && currentInputsHash !== state.inputsHash) {\n reasons.push('configuration-changed');\n }\n // Cockpit sources are compiled straight from each package's web/ folder, so a\n // rebuilt plugin surface is a real change that no configuration hash sees. A\n // state recorded before this was tracked has nothing to compare and is left\n // alone rather than being called stale on sight.\n if (state.webSourcesHash !== undefined && computeWebSourcesHash(state.resolved) !== state.webSourcesHash) {\n reasons.push('code-changed');\n }\n // The same question the `--check` path asks, so the cockpit and the CLI\n // cannot disagree about whether one repository is synced.\n try {\n if (!readBootstrapStatus(options.repoRoot, options.expectedBootstrapEntry, homeDirectory).fresh) {\n reasons.push('runtime-stale');\n }\n } catch {\n // An unreadable bootstrap record is exactly as unusable as a stale one, and\n // syncing is what reports the underlying cause.\n reasons.push('runtime-stale');\n }\n if (\n registration.webDirectory !== null &&\n (!fs.existsSync(path.join(registration.webDirectory, 'index.html')) ||\n !fs.existsSync(path.join(path.dirname(registration.webDirectory), 'plugins', 'composition.js')) ||\n !fs.existsSync(path.join(path.dirname(registration.webDirectory), 'plugins', 'manifest.json')))\n ) {\n reasons.push('cockpit-bundle-missing');\n }\n if (!fs.existsSync(registration.apiDirectory)) reasons.push('package-apis-missing');\n\n return {\n fresh: reasons.length === 0,\n reasons,\n recordedInputsHash: state.inputsHash,\n ...(currentInputsHash === undefined ? {} : { currentInputsHash }),\n };\n}\n\n/** A one-line account of what drifted, for a log the person running the hub reads. */\nexport function describeSyncDrift(drift: SyncDrift): string {\n if (drift.fresh) return 'the repository is synced';\n const detail: Record<SyncDriftReason, string> = {\n 'never-synced': 'it has never been synced',\n 'configuration-changed': 'its configuration changed since the last sync',\n 'code-changed': 'its cockpit sources changed since the last sync',\n 'runtime-stale': 'its precompiled runtime is out of date',\n 'cockpit-bundle-missing': 'the cockpit bundle is missing',\n 'package-apis-missing': 'the package API routes are missing',\n };\n return drift.reasons.map((reason) => detail[reason]).join(', ');\n}\n"],"mappings":"6RA6CA,SAAgB,EAAc,EAA0C,CACtE,IAAM,EAAgB,EAAQ,eAAiBA,EAAAA,QAAG,QAAQ,EACpD,EAA6B,CAAC,EAEhC,EACA,EACJ,GAAI,CACF,EAAeC,EAAAA,qBAAqB,EAAQ,SAAU,CAAa,EACnE,EAAQ,EAAeC,EAAAA,cAAc,EAAQ,SAAU,CAAa,EAAI,IAAA,EAC1E,MAAQ,CAGN,EAAQ,IAAA,GACR,EAAe,IAAA,EACjB,CACA,GAAI,CAAC,GAAS,CAAC,EAAc,MAAO,CAAE,MAAO,GAAO,QAAS,CAAC,cAAc,CAAE,EAE9E,IAAI,EACJ,GAAI,CACF,EAAoBC,EAAAA,kBAAkB,EAAQ,SAAU,EAAM,UAAW,CAAa,CACxF,MAAQ,CAEN,EAAQ,KAAK,uBAAuB,CACtC,CACI,IAAsB,IAAA,IAAa,IAAsB,EAAM,YACjE,EAAQ,KAAK,uBAAuB,EAMlC,EAAM,iBAAmB,IAAA,IAAaC,EAAAA,sBAAsB,EAAM,QAAQ,IAAM,EAAM,gBACxF,EAAQ,KAAK,cAAc,EAI7B,GAAI,CACGC,EAAAA,oBAAoB,EAAQ,SAAU,EAAQ,uBAAwB,CAAa,CAAC,CAAC,OACxF,EAAQ,KAAK,eAAe,CAEhC,MAAQ,CAGN,EAAQ,KAAK,eAAe,CAC9B,CAWA,OATE,EAAa,eAAiB,OAC7B,CAACC,EAAAA,QAAG,WAAWC,EAAAA,QAAK,KAAK,EAAa,aAAc,YAAY,CAAC,GAChE,CAACD,EAAAA,QAAG,WAAWC,EAAAA,QAAK,KAAKA,EAAAA,QAAK,QAAQ,EAAa,YAAY,EAAG,UAAW,gBAAgB,CAAC,GAC9F,CAACD,EAAAA,QAAG,WAAWC,EAAAA,QAAK,KAAKA,EAAAA,QAAK,QAAQ,EAAa,YAAY,EAAG,UAAW,eAAe,CAAC,IAE/F,EAAQ,KAAK,wBAAwB,EAElCD,EAAAA,QAAG,WAAW,EAAa,YAAY,GAAG,EAAQ,KAAK,sBAAsB,EAE3E,CACL,MAAO,EAAQ,SAAW,EAC1B,UACA,mBAAoB,EAAM,WAC1B,GAAI,IAAsB,IAAA,GAAY,CAAC,EAAI,CAAE,mBAAkB,CACjE,CACF,CAGA,SAAgB,EAAkB,EAA0B,CAC1D,GAAI,EAAM,MAAO,MAAO,2BACxB,IAAM,EAA0C,CAC9C,eAAgB,2BAChB,wBAAyB,gDACzB,eAAgB,kDAChB,gBAAiB,yCACjB,yBAA0B,gCAC1B,uBAAwB,oCAC1B,EACA,OAAO,EAAM,QAAQ,IAAK,GAAW,EAAO,EAAO,CAAC,CAAC,KAAK,IAAI,CAChE"}
1
+ {"version":3,"file":"syncDrift.cjs","names":["os","readSyncRegistration","readSyncState","computeInputsHash","computeWebSourcesHash","readBootstrapStatus","fs","path"],"sources":["../../../src/adapters/syncDrift.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { readBootstrapStatus } from './bootstrapLocator.ts';\nimport { computeInputsHash, computeWebSourcesHash, readSyncState, type SyncState } from './syncState.ts';\nimport { readSyncRegistration } from './syncRegistration.ts';\n\nexport type SyncDriftReason =\n | 'never-synced'\n | 'configuration-changed'\n | 'code-changed'\n | 'runtime-stale'\n | 'cockpit-bundle-missing'\n | 'package-apis-missing';\n\nexport interface SyncDrift {\n /** True when nothing needs syncing before a session starts. */\n fresh: boolean;\n reasons: SyncDriftReason[];\n /** The recorded inputs hash, for a caller that wants to log what moved. */\n recordedInputsHash?: string;\n currentInputsHash?: string;\n}\n\nexport interface ReadSyncDriftOptions {\n repoRoot: string;\n /** Launcher-owned Doom entry used to build this repository's generated bootstrap. */\n expectedBootstrapEntry?: string;\n homeDirectory?: string;\n /** Web hosts cannot reuse a CLI-only generation without plugin artifacts. */\n requireWebBundle?: boolean;\n}\n\n/**\n * Whether this repository is synced for the composition it would launch.\n *\n * Sync produces three things a session or the cockpit reads later: the\n * resolved composition, the plugin bundle the browser loads, and the package\n * API routes each host mounts. A session started against any of them stale\n * runs, but quietly wrong: the cockpit shows no plugin surfaces and package\n * APIs answer nothing, with nothing in the log to say why.\n *\n * The hash is the one sync already records. It covers every `.doom` document\n * in both the repository and the home directory, the MCP configuration, the\n * plugin catalogue, and each profile's persona files, so an edit to any of\n * them reads as drift without this having to know what changed.\n */\nexport function readSyncDrift(options: ReadSyncDriftOptions): SyncDrift {\n const homeDirectory = options.homeDirectory ?? os.homedir();\n const reasons: SyncDriftReason[] = [];\n\n let state: SyncState | undefined;\n let registration: ReturnType<typeof readSyncRegistration>;\n try {\n registration = readSyncRegistration(options.repoRoot, homeDirectory);\n state = registration ? readSyncState(options.repoRoot, homeDirectory) : undefined;\n } catch {\n // Unreadable or invalid registration is indistinguishable from absent for\n // this purpose: the next session needs a sync before it can be trusted.\n state = undefined;\n registration = undefined;\n }\n if (!state || !registration) return { fresh: false, reasons: ['never-synced'] };\n\n let currentInputsHash: string | undefined;\n try {\n currentInputsHash = computeInputsHash(options.repoRoot, state.selection, homeDirectory);\n } catch {\n // A malformed document cannot be hashed, and syncing is what reports it.\n reasons.push('configuration-changed');\n }\n if (currentInputsHash !== undefined && currentInputsHash !== state.inputsHash) {\n reasons.push('configuration-changed');\n }\n // Cockpit sources are compiled straight from each package's web/ folder, so a\n // rebuilt plugin surface is a real change that no configuration hash sees. A\n // state recorded before this was tracked has nothing to compare and is left\n // alone rather than being called stale on sight.\n if (state.webSourcesHash !== undefined && computeWebSourcesHash(state.resolved) !== state.webSourcesHash) {\n reasons.push('code-changed');\n }\n // The same question the `--check` path asks, so the cockpit and the CLI\n // cannot disagree about whether one repository is synced.\n try {\n if (!readBootstrapStatus(options.repoRoot, options.expectedBootstrapEntry, homeDirectory).fresh) {\n reasons.push('runtime-stale');\n }\n } catch {\n // An unreadable bootstrap record is exactly as unusable as a stale one, and\n // syncing is what reports the underlying cause.\n reasons.push('runtime-stale');\n }\n if (\n (registration.webDirectory === null && options.requireWebBundle) ||\n (registration.webDirectory !== null &&\n (!fs.existsSync(path.join(registration.webDirectory, 'index.html')) ||\n !fs.existsSync(path.join(path.dirname(registration.webDirectory), 'plugins', 'composition.js')) ||\n !fs.existsSync(path.join(path.dirname(registration.webDirectory), 'plugins', 'manifest.json'))))\n ) {\n reasons.push('cockpit-bundle-missing');\n }\n if (!fs.existsSync(registration.apiDirectory)) reasons.push('package-apis-missing');\n\n return {\n fresh: reasons.length === 0,\n reasons,\n recordedInputsHash: state.inputsHash,\n ...(currentInputsHash === undefined ? {} : { currentInputsHash }),\n };\n}\n\n/** A one-line account of what drifted, for a log the person running the hub reads. */\nexport function describeSyncDrift(drift: SyncDrift): string {\n if (drift.fresh) return 'the repository is synced';\n const detail: Record<SyncDriftReason, string> = {\n 'never-synced': 'it has never been synced',\n 'configuration-changed': 'its configuration changed since the last sync',\n 'code-changed': 'its cockpit sources changed since the last sync',\n 'runtime-stale': 'its precompiled runtime is out of date',\n 'cockpit-bundle-missing': 'the cockpit bundle is missing',\n 'package-apis-missing': 'the package API routes are missing',\n };\n return drift.reasons.map((reason) => detail[reason]).join(', ');\n}\n"],"mappings":"6RA+CA,SAAgB,EAAc,EAA0C,CACtE,IAAM,EAAgB,EAAQ,eAAiBA,EAAAA,QAAG,QAAQ,EACpD,EAA6B,CAAC,EAEhC,EACA,EACJ,GAAI,CACF,EAAeC,EAAAA,qBAAqB,EAAQ,SAAU,CAAa,EACnE,EAAQ,EAAeC,EAAAA,cAAc,EAAQ,SAAU,CAAa,EAAI,IAAA,EAC1E,MAAQ,CAGN,EAAQ,IAAA,GACR,EAAe,IAAA,EACjB,CACA,GAAI,CAAC,GAAS,CAAC,EAAc,MAAO,CAAE,MAAO,GAAO,QAAS,CAAC,cAAc,CAAE,EAE9E,IAAI,EACJ,GAAI,CACF,EAAoBC,EAAAA,kBAAkB,EAAQ,SAAU,EAAM,UAAW,CAAa,CACxF,MAAQ,CAEN,EAAQ,KAAK,uBAAuB,CACtC,CACI,IAAsB,IAAA,IAAa,IAAsB,EAAM,YACjE,EAAQ,KAAK,uBAAuB,EAMlC,EAAM,iBAAmB,IAAA,IAAaC,EAAAA,sBAAsB,EAAM,QAAQ,IAAM,EAAM,gBACxF,EAAQ,KAAK,cAAc,EAI7B,GAAI,CACGC,EAAAA,oBAAoB,EAAQ,SAAU,EAAQ,uBAAwB,CAAa,CAAC,CAAC,OACxF,EAAQ,KAAK,eAAe,CAEhC,MAAQ,CAGN,EAAQ,KAAK,eAAe,CAC9B,CAYA,OAVG,EAAa,eAAiB,MAAQ,EAAQ,kBAC9C,EAAa,eAAiB,OAC5B,CAACC,EAAAA,QAAG,WAAWC,EAAAA,QAAK,KAAK,EAAa,aAAc,YAAY,CAAC,GAChE,CAACD,EAAAA,QAAG,WAAWC,EAAAA,QAAK,KAAKA,EAAAA,QAAK,QAAQ,EAAa,YAAY,EAAG,UAAW,gBAAgB,CAAC,GAC9F,CAACD,EAAAA,QAAG,WAAWC,EAAAA,QAAK,KAAKA,EAAAA,QAAK,QAAQ,EAAa,YAAY,EAAG,UAAW,eAAe,CAAC,KAEjG,EAAQ,KAAK,wBAAwB,EAElCD,EAAAA,QAAG,WAAW,EAAa,YAAY,GAAG,EAAQ,KAAK,sBAAsB,EAE3E,CACL,MAAO,EAAQ,SAAW,EAC1B,UACA,mBAAoB,EAAM,WAC1B,GAAI,IAAsB,IAAA,GAAY,CAAC,EAAI,CAAE,mBAAkB,CACjE,CACF,CAGA,SAAgB,EAAkB,EAA0B,CAC1D,GAAI,EAAM,MAAO,MAAO,2BACxB,IAAM,EAA0C,CAC9C,eAAgB,2BAChB,wBAAyB,gDACzB,eAAgB,kDAChB,gBAAiB,yCACjB,yBAA0B,gCAC1B,uBAAwB,oCAC1B,EACA,OAAO,EAAM,QAAQ,IAAK,GAAW,EAAO,EAAO,CAAC,CAAC,KAAK,IAAI,CAChE"}
@@ -13,6 +13,8 @@ interface ReadSyncDriftOptions {
13
13
  /** Launcher-owned Doom entry used to build this repository's generated bootstrap. */
14
14
  expectedBootstrapEntry?: string;
15
15
  homeDirectory?: string;
16
+ /** Web hosts cannot reuse a CLI-only generation without plugin artifacts. */
17
+ requireWebBundle?: boolean;
16
18
  }
17
19
  /**
18
20
  * Whether this repository is synced for the composition it would launch.
@@ -1 +1 @@
1
- {"version":3,"file":"syncDrift.d.cts","names":[],"sources":["../../../src/adapters/syncDrift.ts"],"mappings":";KAOY;UAQK;;EAEf;EACA,SAAS;;EAET;EACA;;UAGe;EACf;;EAEA;EACA;;;;;;;;;;;;;;;;iBAiBc,cAAc,SAAS,uBAAuB;;iBAgE9C,kBAAkB,OAAO"}
1
+ {"version":3,"file":"syncDrift.d.cts","names":[],"sources":["../../../src/adapters/syncDrift.ts"],"mappings":";KAOY;UAQK;;EAEf;EACA,SAAS;;EAET;EACA;;UAGe;EACf;;EAEA;EACA;;EAEA;;;;;;;;;;;;;;;;iBAiBc,cAAc,SAAS,uBAAuB;;iBAiE9C,kBAAkB,OAAO"}
@@ -13,6 +13,8 @@ interface ReadSyncDriftOptions {
13
13
  /** Launcher-owned Doom entry used to build this repository's generated bootstrap. */
14
14
  expectedBootstrapEntry?: string;
15
15
  homeDirectory?: string;
16
+ /** Web hosts cannot reuse a CLI-only generation without plugin artifacts. */
17
+ requireWebBundle?: boolean;
16
18
  }
17
19
  /**
18
20
  * Whether this repository is synced for the composition it would launch.
@@ -1 +1 @@
1
- {"version":3,"file":"syncDrift.d.mts","names":[],"sources":["../../../src/adapters/syncDrift.ts"],"mappings":";KAOY;UAQK;;EAEf;EACA,SAAS;;EAET;EACA;;UAGe;EACf;;EAEA;EACA;;;;;;;;;;;;;;;;iBAiBc,cAAc,SAAS,uBAAuB;;iBAgE9C,kBAAkB,OAAO"}
1
+ {"version":3,"file":"syncDrift.d.mts","names":[],"sources":["../../../src/adapters/syncDrift.ts"],"mappings":";KAOY;UAQK;;EAEf;EACA,SAAS;;EAET;EACA;;UAGe;EACf;;EAEA;EACA;;EAEA;;;;;;;;;;;;;;;;iBAiBc,cAAc,SAAS,uBAAuB;;iBAiE9C,kBAAkB,OAAO"}
@@ -1,2 +1,2 @@
1
- import{readSyncRegistration as e}from"./syncRegistration.mjs";import{computeInputsHash as t,computeWebSourcesHash as n,readSyncState as r}from"./syncState.mjs";import{readBootstrapStatus as i}from"./bootstrapLocator.mjs";import a from"node:path";import o from"node:fs";import s from"node:os";function c(c){let l=c.homeDirectory??s.homedir(),u=[],d,f;try{f=e(c.repoRoot,l),d=f?r(c.repoRoot,l):void 0}catch{d=void 0,f=void 0}if(!d||!f)return{fresh:!1,reasons:[`never-synced`]};let p;try{p=t(c.repoRoot,d.selection,l)}catch{u.push(`configuration-changed`)}p!==void 0&&p!==d.inputsHash&&u.push(`configuration-changed`),d.webSourcesHash!==void 0&&n(d.resolved)!==d.webSourcesHash&&u.push(`code-changed`);try{i(c.repoRoot,c.expectedBootstrapEntry,l).fresh||u.push(`runtime-stale`)}catch{u.push(`runtime-stale`)}return f.webDirectory!==null&&(!o.existsSync(a.join(f.webDirectory,`index.html`))||!o.existsSync(a.join(a.dirname(f.webDirectory),`plugins`,`composition.js`))||!o.existsSync(a.join(a.dirname(f.webDirectory),`plugins`,`manifest.json`)))&&u.push(`cockpit-bundle-missing`),o.existsSync(f.apiDirectory)||u.push(`package-apis-missing`),{fresh:u.length===0,reasons:u,recordedInputsHash:d.inputsHash,...p===void 0?{}:{currentInputsHash:p}}}function l(e){if(e.fresh)return`the repository is synced`;let t={"never-synced":`it has never been synced`,"configuration-changed":`its configuration changed since the last sync`,"code-changed":`its cockpit sources changed since the last sync`,"runtime-stale":`its precompiled runtime is out of date`,"cockpit-bundle-missing":`the cockpit bundle is missing`,"package-apis-missing":`the package API routes are missing`};return e.reasons.map(e=>t[e]).join(`, `)}export{l as describeSyncDrift,c as readSyncDrift};
1
+ import{readSyncRegistration as e}from"./syncRegistration.mjs";import{computeInputsHash as t,computeWebSourcesHash as n,readSyncState as r}from"./syncState.mjs";import{readBootstrapStatus as i}from"./bootstrapLocator.mjs";import a from"node:path";import o from"node:fs";import s from"node:os";function c(c){let l=c.homeDirectory??s.homedir(),u=[],d,f;try{f=e(c.repoRoot,l),d=f?r(c.repoRoot,l):void 0}catch{d=void 0,f=void 0}if(!d||!f)return{fresh:!1,reasons:[`never-synced`]};let p;try{p=t(c.repoRoot,d.selection,l)}catch{u.push(`configuration-changed`)}p!==void 0&&p!==d.inputsHash&&u.push(`configuration-changed`),d.webSourcesHash!==void 0&&n(d.resolved)!==d.webSourcesHash&&u.push(`code-changed`);try{i(c.repoRoot,c.expectedBootstrapEntry,l).fresh||u.push(`runtime-stale`)}catch{u.push(`runtime-stale`)}return(f.webDirectory===null&&c.requireWebBundle||f.webDirectory!==null&&(!o.existsSync(a.join(f.webDirectory,`index.html`))||!o.existsSync(a.join(a.dirname(f.webDirectory),`plugins`,`composition.js`))||!o.existsSync(a.join(a.dirname(f.webDirectory),`plugins`,`manifest.json`))))&&u.push(`cockpit-bundle-missing`),o.existsSync(f.apiDirectory)||u.push(`package-apis-missing`),{fresh:u.length===0,reasons:u,recordedInputsHash:d.inputsHash,...p===void 0?{}:{currentInputsHash:p}}}function l(e){if(e.fresh)return`the repository is synced`;let t={"never-synced":`it has never been synced`,"configuration-changed":`its configuration changed since the last sync`,"code-changed":`its cockpit sources changed since the last sync`,"runtime-stale":`its precompiled runtime is out of date`,"cockpit-bundle-missing":`the cockpit bundle is missing`,"package-apis-missing":`the package API routes are missing`};return e.reasons.map(e=>t[e]).join(`, `)}export{l as describeSyncDrift,c as readSyncDrift};
2
2
  //# sourceMappingURL=syncDrift.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"syncDrift.mjs","names":[],"sources":["../../../src/adapters/syncDrift.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { readBootstrapStatus } from './bootstrapLocator.ts';\nimport { computeInputsHash, computeWebSourcesHash, readSyncState, type SyncState } from './syncState.ts';\nimport { readSyncRegistration } from './syncRegistration.ts';\n\nexport type SyncDriftReason =\n | 'never-synced'\n | 'configuration-changed'\n | 'code-changed'\n | 'runtime-stale'\n | 'cockpit-bundle-missing'\n | 'package-apis-missing';\n\nexport interface SyncDrift {\n /** True when nothing needs syncing before a session starts. */\n fresh: boolean;\n reasons: SyncDriftReason[];\n /** The recorded inputs hash, for a caller that wants to log what moved. */\n recordedInputsHash?: string;\n currentInputsHash?: string;\n}\n\nexport interface ReadSyncDriftOptions {\n repoRoot: string;\n /** Launcher-owned Doom entry used to build this repository's generated bootstrap. */\n expectedBootstrapEntry?: string;\n homeDirectory?: string;\n}\n\n/**\n * Whether this repository is synced for the composition it would launch.\n *\n * Sync produces three things a session or the cockpit reads later: the\n * resolved composition, the plugin bundle the browser loads, and the package\n * API routes each host mounts. A session started against any of them stale\n * runs, but quietly wrong: the cockpit shows no plugin surfaces and package\n * APIs answer nothing, with nothing in the log to say why.\n *\n * The hash is the one sync already records. It covers every `.doom` document\n * in both the repository and the home directory, the MCP configuration, the\n * plugin catalogue, and each profile's persona files, so an edit to any of\n * them reads as drift without this having to know what changed.\n */\nexport function readSyncDrift(options: ReadSyncDriftOptions): SyncDrift {\n const homeDirectory = options.homeDirectory ?? os.homedir();\n const reasons: SyncDriftReason[] = [];\n\n let state: SyncState | undefined;\n let registration: ReturnType<typeof readSyncRegistration>;\n try {\n registration = readSyncRegistration(options.repoRoot, homeDirectory);\n state = registration ? readSyncState(options.repoRoot, homeDirectory) : undefined;\n } catch {\n // Unreadable or invalid registration is indistinguishable from absent for\n // this purpose: the next session needs a sync before it can be trusted.\n state = undefined;\n registration = undefined;\n }\n if (!state || !registration) return { fresh: false, reasons: ['never-synced'] };\n\n let currentInputsHash: string | undefined;\n try {\n currentInputsHash = computeInputsHash(options.repoRoot, state.selection, homeDirectory);\n } catch {\n // A malformed document cannot be hashed, and syncing is what reports it.\n reasons.push('configuration-changed');\n }\n if (currentInputsHash !== undefined && currentInputsHash !== state.inputsHash) {\n reasons.push('configuration-changed');\n }\n // Cockpit sources are compiled straight from each package's web/ folder, so a\n // rebuilt plugin surface is a real change that no configuration hash sees. A\n // state recorded before this was tracked has nothing to compare and is left\n // alone rather than being called stale on sight.\n if (state.webSourcesHash !== undefined && computeWebSourcesHash(state.resolved) !== state.webSourcesHash) {\n reasons.push('code-changed');\n }\n // The same question the `--check` path asks, so the cockpit and the CLI\n // cannot disagree about whether one repository is synced.\n try {\n if (!readBootstrapStatus(options.repoRoot, options.expectedBootstrapEntry, homeDirectory).fresh) {\n reasons.push('runtime-stale');\n }\n } catch {\n // An unreadable bootstrap record is exactly as unusable as a stale one, and\n // syncing is what reports the underlying cause.\n reasons.push('runtime-stale');\n }\n if (\n registration.webDirectory !== null &&\n (!fs.existsSync(path.join(registration.webDirectory, 'index.html')) ||\n !fs.existsSync(path.join(path.dirname(registration.webDirectory), 'plugins', 'composition.js')) ||\n !fs.existsSync(path.join(path.dirname(registration.webDirectory), 'plugins', 'manifest.json')))\n ) {\n reasons.push('cockpit-bundle-missing');\n }\n if (!fs.existsSync(registration.apiDirectory)) reasons.push('package-apis-missing');\n\n return {\n fresh: reasons.length === 0,\n reasons,\n recordedInputsHash: state.inputsHash,\n ...(currentInputsHash === undefined ? {} : { currentInputsHash }),\n };\n}\n\n/** A one-line account of what drifted, for a log the person running the hub reads. */\nexport function describeSyncDrift(drift: SyncDrift): string {\n if (drift.fresh) return 'the repository is synced';\n const detail: Record<SyncDriftReason, string> = {\n 'never-synced': 'it has never been synced',\n 'configuration-changed': 'its configuration changed since the last sync',\n 'code-changed': 'its cockpit sources changed since the last sync',\n 'runtime-stale': 'its precompiled runtime is out of date',\n 'cockpit-bundle-missing': 'the cockpit bundle is missing',\n 'package-apis-missing': 'the package API routes are missing',\n };\n return drift.reasons.map((reason) => detail[reason]).join(', ');\n}\n"],"mappings":"oSA6CA,SAAgB,EAAc,EAA0C,CACtE,IAAM,EAAgB,EAAQ,eAAiB,EAAG,QAAQ,EACpD,EAA6B,CAAC,EAEhC,EACA,EACJ,GAAI,CACF,EAAe,EAAqB,EAAQ,SAAU,CAAa,EACnE,EAAQ,EAAe,EAAc,EAAQ,SAAU,CAAa,EAAI,IAAA,EAC1E,MAAQ,CAGN,EAAQ,IAAA,GACR,EAAe,IAAA,EACjB,CACA,GAAI,CAAC,GAAS,CAAC,EAAc,MAAO,CAAE,MAAO,GAAO,QAAS,CAAC,cAAc,CAAE,EAE9E,IAAI,EACJ,GAAI,CACF,EAAoB,EAAkB,EAAQ,SAAU,EAAM,UAAW,CAAa,CACxF,MAAQ,CAEN,EAAQ,KAAK,uBAAuB,CACtC,CACI,IAAsB,IAAA,IAAa,IAAsB,EAAM,YACjE,EAAQ,KAAK,uBAAuB,EAMlC,EAAM,iBAAmB,IAAA,IAAa,EAAsB,EAAM,QAAQ,IAAM,EAAM,gBACxF,EAAQ,KAAK,cAAc,EAI7B,GAAI,CACG,EAAoB,EAAQ,SAAU,EAAQ,uBAAwB,CAAa,CAAC,CAAC,OACxF,EAAQ,KAAK,eAAe,CAEhC,MAAQ,CAGN,EAAQ,KAAK,eAAe,CAC9B,CAWA,OATE,EAAa,eAAiB,OAC7B,CAAC,EAAG,WAAW,EAAK,KAAK,EAAa,aAAc,YAAY,CAAC,GAChE,CAAC,EAAG,WAAW,EAAK,KAAK,EAAK,QAAQ,EAAa,YAAY,EAAG,UAAW,gBAAgB,CAAC,GAC9F,CAAC,EAAG,WAAW,EAAK,KAAK,EAAK,QAAQ,EAAa,YAAY,EAAG,UAAW,eAAe,CAAC,IAE/F,EAAQ,KAAK,wBAAwB,EAElC,EAAG,WAAW,EAAa,YAAY,GAAG,EAAQ,KAAK,sBAAsB,EAE3E,CACL,MAAO,EAAQ,SAAW,EAC1B,UACA,mBAAoB,EAAM,WAC1B,GAAI,IAAsB,IAAA,GAAY,CAAC,EAAI,CAAE,mBAAkB,CACjE,CACF,CAGA,SAAgB,EAAkB,EAA0B,CAC1D,GAAI,EAAM,MAAO,MAAO,2BACxB,IAAM,EAA0C,CAC9C,eAAgB,2BAChB,wBAAyB,gDACzB,eAAgB,kDAChB,gBAAiB,yCACjB,yBAA0B,gCAC1B,uBAAwB,oCAC1B,EACA,OAAO,EAAM,QAAQ,IAAK,GAAW,EAAO,EAAO,CAAC,CAAC,KAAK,IAAI,CAChE"}
1
+ {"version":3,"file":"syncDrift.mjs","names":[],"sources":["../../../src/adapters/syncDrift.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { readBootstrapStatus } from './bootstrapLocator.ts';\nimport { computeInputsHash, computeWebSourcesHash, readSyncState, type SyncState } from './syncState.ts';\nimport { readSyncRegistration } from './syncRegistration.ts';\n\nexport type SyncDriftReason =\n | 'never-synced'\n | 'configuration-changed'\n | 'code-changed'\n | 'runtime-stale'\n | 'cockpit-bundle-missing'\n | 'package-apis-missing';\n\nexport interface SyncDrift {\n /** True when nothing needs syncing before a session starts. */\n fresh: boolean;\n reasons: SyncDriftReason[];\n /** The recorded inputs hash, for a caller that wants to log what moved. */\n recordedInputsHash?: string;\n currentInputsHash?: string;\n}\n\nexport interface ReadSyncDriftOptions {\n repoRoot: string;\n /** Launcher-owned Doom entry used to build this repository's generated bootstrap. */\n expectedBootstrapEntry?: string;\n homeDirectory?: string;\n /** Web hosts cannot reuse a CLI-only generation without plugin artifacts. */\n requireWebBundle?: boolean;\n}\n\n/**\n * Whether this repository is synced for the composition it would launch.\n *\n * Sync produces three things a session or the cockpit reads later: the\n * resolved composition, the plugin bundle the browser loads, and the package\n * API routes each host mounts. A session started against any of them stale\n * runs, but quietly wrong: the cockpit shows no plugin surfaces and package\n * APIs answer nothing, with nothing in the log to say why.\n *\n * The hash is the one sync already records. It covers every `.doom` document\n * in both the repository and the home directory, the MCP configuration, the\n * plugin catalogue, and each profile's persona files, so an edit to any of\n * them reads as drift without this having to know what changed.\n */\nexport function readSyncDrift(options: ReadSyncDriftOptions): SyncDrift {\n const homeDirectory = options.homeDirectory ?? os.homedir();\n const reasons: SyncDriftReason[] = [];\n\n let state: SyncState | undefined;\n let registration: ReturnType<typeof readSyncRegistration>;\n try {\n registration = readSyncRegistration(options.repoRoot, homeDirectory);\n state = registration ? readSyncState(options.repoRoot, homeDirectory) : undefined;\n } catch {\n // Unreadable or invalid registration is indistinguishable from absent for\n // this purpose: the next session needs a sync before it can be trusted.\n state = undefined;\n registration = undefined;\n }\n if (!state || !registration) return { fresh: false, reasons: ['never-synced'] };\n\n let currentInputsHash: string | undefined;\n try {\n currentInputsHash = computeInputsHash(options.repoRoot, state.selection, homeDirectory);\n } catch {\n // A malformed document cannot be hashed, and syncing is what reports it.\n reasons.push('configuration-changed');\n }\n if (currentInputsHash !== undefined && currentInputsHash !== state.inputsHash) {\n reasons.push('configuration-changed');\n }\n // Cockpit sources are compiled straight from each package's web/ folder, so a\n // rebuilt plugin surface is a real change that no configuration hash sees. A\n // state recorded before this was tracked has nothing to compare and is left\n // alone rather than being called stale on sight.\n if (state.webSourcesHash !== undefined && computeWebSourcesHash(state.resolved) !== state.webSourcesHash) {\n reasons.push('code-changed');\n }\n // The same question the `--check` path asks, so the cockpit and the CLI\n // cannot disagree about whether one repository is synced.\n try {\n if (!readBootstrapStatus(options.repoRoot, options.expectedBootstrapEntry, homeDirectory).fresh) {\n reasons.push('runtime-stale');\n }\n } catch {\n // An unreadable bootstrap record is exactly as unusable as a stale one, and\n // syncing is what reports the underlying cause.\n reasons.push('runtime-stale');\n }\n if (\n (registration.webDirectory === null && options.requireWebBundle) ||\n (registration.webDirectory !== null &&\n (!fs.existsSync(path.join(registration.webDirectory, 'index.html')) ||\n !fs.existsSync(path.join(path.dirname(registration.webDirectory), 'plugins', 'composition.js')) ||\n !fs.existsSync(path.join(path.dirname(registration.webDirectory), 'plugins', 'manifest.json'))))\n ) {\n reasons.push('cockpit-bundle-missing');\n }\n if (!fs.existsSync(registration.apiDirectory)) reasons.push('package-apis-missing');\n\n return {\n fresh: reasons.length === 0,\n reasons,\n recordedInputsHash: state.inputsHash,\n ...(currentInputsHash === undefined ? {} : { currentInputsHash }),\n };\n}\n\n/** A one-line account of what drifted, for a log the person running the hub reads. */\nexport function describeSyncDrift(drift: SyncDrift): string {\n if (drift.fresh) return 'the repository is synced';\n const detail: Record<SyncDriftReason, string> = {\n 'never-synced': 'it has never been synced',\n 'configuration-changed': 'its configuration changed since the last sync',\n 'code-changed': 'its cockpit sources changed since the last sync',\n 'runtime-stale': 'its precompiled runtime is out of date',\n 'cockpit-bundle-missing': 'the cockpit bundle is missing',\n 'package-apis-missing': 'the package API routes are missing',\n };\n return drift.reasons.map((reason) => detail[reason]).join(', ');\n}\n"],"mappings":"oSA+CA,SAAgB,EAAc,EAA0C,CACtE,IAAM,EAAgB,EAAQ,eAAiB,EAAG,QAAQ,EACpD,EAA6B,CAAC,EAEhC,EACA,EACJ,GAAI,CACF,EAAe,EAAqB,EAAQ,SAAU,CAAa,EACnE,EAAQ,EAAe,EAAc,EAAQ,SAAU,CAAa,EAAI,IAAA,EAC1E,MAAQ,CAGN,EAAQ,IAAA,GACR,EAAe,IAAA,EACjB,CACA,GAAI,CAAC,GAAS,CAAC,EAAc,MAAO,CAAE,MAAO,GAAO,QAAS,CAAC,cAAc,CAAE,EAE9E,IAAI,EACJ,GAAI,CACF,EAAoB,EAAkB,EAAQ,SAAU,EAAM,UAAW,CAAa,CACxF,MAAQ,CAEN,EAAQ,KAAK,uBAAuB,CACtC,CACI,IAAsB,IAAA,IAAa,IAAsB,EAAM,YACjE,EAAQ,KAAK,uBAAuB,EAMlC,EAAM,iBAAmB,IAAA,IAAa,EAAsB,EAAM,QAAQ,IAAM,EAAM,gBACxF,EAAQ,KAAK,cAAc,EAI7B,GAAI,CACG,EAAoB,EAAQ,SAAU,EAAQ,uBAAwB,CAAa,CAAC,CAAC,OACxF,EAAQ,KAAK,eAAe,CAEhC,MAAQ,CAGN,EAAQ,KAAK,eAAe,CAC9B,CAYA,OAVG,EAAa,eAAiB,MAAQ,EAAQ,kBAC9C,EAAa,eAAiB,OAC5B,CAAC,EAAG,WAAW,EAAK,KAAK,EAAa,aAAc,YAAY,CAAC,GAChE,CAAC,EAAG,WAAW,EAAK,KAAK,EAAK,QAAQ,EAAa,YAAY,EAAG,UAAW,gBAAgB,CAAC,GAC9F,CAAC,EAAG,WAAW,EAAK,KAAK,EAAK,QAAQ,EAAa,YAAY,EAAG,UAAW,eAAe,CAAC,KAEjG,EAAQ,KAAK,wBAAwB,EAElC,EAAG,WAAW,EAAa,YAAY,GAAG,EAAQ,KAAK,sBAAsB,EAE3E,CACL,MAAO,EAAQ,SAAW,EAC1B,UACA,mBAAoB,EAAM,WAC1B,GAAI,IAAsB,IAAA,GAAY,CAAC,EAAI,CAAE,mBAAkB,CACjE,CACF,CAGA,SAAgB,EAAkB,EAA0B,CAC1D,GAAI,EAAM,MAAO,MAAO,2BACxB,IAAM,EAA0C,CAC9C,eAAgB,2BAChB,wBAAyB,gDACzB,eAAgB,kDAChB,gBAAiB,yCACjB,yBAA0B,gCAC1B,uBAAwB,oCAC1B,EACA,OAAO,EAAM,QAAQ,IAAK,GAAW,EAAO,EAAO,CAAC,CAAC,KAAK,IAAI,CAChE"}
@@ -4,7 +4,7 @@ const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../adapters/r
4
4
  `)}`)}if(c){let e=(0,D.loadMajorModesConfig)(y,g),t=h.missingLayerPackageSpecifiers(e,Object.keys(e.layers),a.createLayerResolvers(y));if(t.length>0)return s.write(`doompi sync is out of date:\n${t.map(e=>` configured package is not installed: ${e}`).join(`
5
5
  `)}\n`),1;let r;try{r=o.readLocatedSyncState(y,g)}catch(e){let t=e instanceof Error?e.message:String(e);return s.write(`doompi sync is out of date:\n ${t}\n`),1}let i=U(y,w.options,g),c=G(y,O,r?.state,n,this.settingsMode,i);return c.length===0?(s.write(`doompi sync is up to date
6
6
  `),0):(s.write(`doompi sync is out of date:\n${c.map(e=>` ${e}`).join(`
7
- `)}\n`),1)}if(!p&&v.readSyncDrift({repoRoot:y,homeDirectory:g}).fresh)return s.write(`doompi sync is already up to date
8
- `),0;let A=new x.SyncProgress(s),j=this.lockHeld?void 0:await r.acquireSyncLocationLock(r.resolveSyncLocation(y,g)),P;try{if(!p&&v.readSyncDrift({repoRoot:y,homeDirectory:g}).fresh)return s.write(`doompi sync is already up to date
9
- `),0;P=await this.stage(y,w.options,n,g,A)}finally{await j?.()}return K(y,s),s.write(q(P,this.settingsMode===`embedded`?`dpi`:`pi`)),0}async stage(e,t,n,s,c){let l=r.resolveSyncLocation(e,s),u=`${Date.now().toString(36)}-${C.default.randomUUID()}`,d=r.syncGenerationDirectory(l,u);await w.default.promises.mkdir(l.generationsDirectory,{recursive:!0,mode:448}),await w.default.promises.mkdir(d,{mode:448});try{let e=c.start(I,`resolving the matrix and staging resources`),r=await m.buildHarnessContext({...t,repoRoot:l.root,homeDirectory:s,cwd:l.root,resourceDirectory:d});await h.ensureLayerPackages({repoRoot:l.root,config:r.majorModesConfig,layers:Object.keys(r.majorModesConfig.layers),environment:n}),e(`${String(r.resources.skillCount)} skills, ${String(r.resources.agentCount)} agents`);let p=H(t),v=a.createLayerResolvers(l.root),x=o.recordResolvedEntries(r.majorModesConfig,v),C=U(l.root,t,s),w=f.piAgentDirectory(n,s),T=S.default.join(f.piThemeDirectory(w),`${A.DEFAULT_THEME_NAME}.json`),E=this.settingsMode===`persisted`?T:r.defaultThemePath,D={version:13,root:l.root,identity:l.identity,inputsHash:o.computeInputsHash(l.root,p,s),webSourcesHash:o.computeWebSourcesHash(x),compositionFingerprint:C,selection:p,env:B(r.environment),fileState:{profileEnvironment:(0,j.loadHarnessState)(r.environment).state.profileEnvironment,pluginHooks:r.resources.pluginHooks,mcpProjection:r.resources.mcpProjection},resolved:x,baseline:{mcpConfigPath:r.resources.mcpConfigPath,personaFile:r.environment.DOOMPI_PERSONA_FILE,themePath:E,themeName:A.DEFAULT_THEME_NAME}},O=c.start(`runtime`,`precompiling the mode bundles`),k=await _.buildSyncedRuntime(l.root,n,s,{state:D,directory:d});O(`${String(Object.keys(k.bundles).length)} mode bundles`);let M=await o.writeSyncState(l.root,k.state,s,S.default.join(d,`state.json`)),N=c.start(`web`,`bundling the web cockpit plugins`),P=await b.syncWebBundle({repoRoot:l.root,resolvedEntries:k.state.resolved,environment:n,outputDirectory:S.default.join(d,`web-bundle`),onNotice:e=>c.line(`web`,e)});if(P.status===`failed`)throw Error(`Cockpit bundle failed: ${P.reason}`);N(P.status===`bundled`?`cockpit bundled with plugins: ${P.pluginIds.join(`, `)}`:P.reason);let F=c.start(`api`,`generating the package API routes`),L=y.syncApiRoutes({resolvedEntries:k.state.resolved,outputDirectory:S.default.join(d,`api`),onNotice:e=>c.line(`api`,e)});F(`routes written to ${L.directory}`),i.publishSyncRegistration(l.root,{version:1,root:l.root,identity:l.identity,generation:u,generationRoot:d,statePath:M,stateSha256:i.syncStateSha256(M),webDirectory:P.status===`bundled`?P.assetsDir:null,apiDirectory:L.directory,package:Y(l.root)},s),X(l.generationsDirectory,u,e=>c.line(I,e));let R=this.settingsMode===`persisted`?g.writeProjectPiSettings(l.root):void 0;return{statePath:M,...R?{projectSettingsPath:R}:{},selection:p,mcpServers:k.state.baseline.mcpConfigPath?o.readMcpServerNames(k.state.baseline.mcpConfigPath):[],skillCount:r.resources.skillCount,agentCount:r.resources.agentCount}}catch(e){throw await w.default.promises.rm(d,{recursive:!0,force:!0}),e}}};exports.SyncCommand=Q,exports.collectDrift=G,exports.formatSyncResult=q,exports.recordedEnvironment=B,exports.selectionCompositionFingerprint=U,exports.selectionEnvironment=V,exports.toSelection=H;
7
+ `)}\n`),1)}let A={repoRoot:y,homeDirectory:g,requireWebBundle:!!n.DOOMPI_WEB_PACKAGE_ROOT};if(!p&&v.readSyncDrift(A).fresh)return s.write(`doompi sync is already up to date
8
+ `),0;let j=new x.SyncProgress(s),P=this.lockHeld?void 0:await r.acquireSyncLocationLock(r.resolveSyncLocation(y,g)),F;try{if(!p&&v.readSyncDrift(A).fresh)return s.write(`doompi sync is already up to date
9
+ `),0;F=await this.stage(y,w.options,n,g,j)}finally{await P?.()}return K(y,s),s.write(q(F,this.settingsMode===`embedded`?`dpi`:`pi`)),0}async stage(e,t,n,s,c){let l=r.resolveSyncLocation(e,s),u=`${Date.now().toString(36)}-${C.default.randomUUID()}`,d=r.syncGenerationDirectory(l,u);await w.default.promises.mkdir(l.generationsDirectory,{recursive:!0,mode:448}),await w.default.promises.mkdir(d,{mode:448});try{let e=c.start(I,`resolving the matrix and staging resources`),r=await m.buildHarnessContext({...t,repoRoot:l.root,homeDirectory:s,cwd:l.root,resourceDirectory:d});await h.ensureLayerPackages({repoRoot:l.root,config:r.majorModesConfig,layers:Object.keys(r.majorModesConfig.layers),environment:n}),e(`${String(r.resources.skillCount)} skills, ${String(r.resources.agentCount)} agents`);let p=H(t),v=a.createLayerResolvers(l.root),x=o.recordResolvedEntries(r.majorModesConfig,v),C=U(l.root,t,s),w=f.piAgentDirectory(n,s),T=S.default.join(f.piThemeDirectory(w),`${A.DEFAULT_THEME_NAME}.json`),E=this.settingsMode===`persisted`?T:r.defaultThemePath,D={version:13,root:l.root,identity:l.identity,inputsHash:o.computeInputsHash(l.root,p,s),webSourcesHash:o.computeWebSourcesHash(x),compositionFingerprint:C,selection:p,env:B(r.environment),fileState:{profileEnvironment:(0,j.loadHarnessState)(r.environment).state.profileEnvironment,pluginHooks:r.resources.pluginHooks,mcpProjection:r.resources.mcpProjection},resolved:x,baseline:{mcpConfigPath:r.resources.mcpConfigPath,personaFile:r.environment.DOOMPI_PERSONA_FILE,themePath:E,themeName:A.DEFAULT_THEME_NAME}},O=c.start(`runtime`,`precompiling the mode bundles`),k=await _.buildSyncedRuntime(l.root,n,s,{state:D,directory:d});O(`${String(Object.keys(k.bundles).length)} mode bundles`);let M=await o.writeSyncState(l.root,k.state,s,S.default.join(d,`state.json`)),N=c.start(`web`,`bundling the web cockpit plugins`),P=await b.syncWebBundle({repoRoot:l.root,resolvedEntries:k.state.resolved,environment:n,outputDirectory:S.default.join(d,`web-bundle`),onNotice:e=>c.line(`web`,e)});if(P.status===`failed`)throw Error(`Cockpit bundle failed: ${P.reason}`);N(P.status===`bundled`?`cockpit bundled with plugins: ${P.pluginIds.join(`, `)}`:P.reason);let F=c.start(`api`,`generating the package API routes`),L=y.syncApiRoutes({resolvedEntries:k.state.resolved,outputDirectory:S.default.join(d,`api`),onNotice:e=>c.line(`api`,e)});F(`routes written to ${L.directory}`),i.publishSyncRegistration(l.root,{version:1,root:l.root,identity:l.identity,generation:u,generationRoot:d,statePath:M,stateSha256:i.syncStateSha256(M),webDirectory:P.status===`bundled`?P.assetsDir:null,apiDirectory:L.directory,package:Y(l.root)},s),X(l.generationsDirectory,u,e=>c.line(I,e));let R=this.settingsMode===`persisted`?g.writeProjectPiSettings(l.root):void 0;return{statePath:M,...R?{projectSettingsPath:R}:{},selection:p,mcpServers:k.state.baseline.mcpConfigPath?o.readMcpServerNames(k.state.baseline.mcpConfigPath):[],skillCount:r.resources.skillCount,agentCount:r.resources.agentCount}}catch(e){throw await w.default.promises.rm(d,{recursive:!0,force:!0}),e}}};exports.SyncCommand=Q,exports.collectDrift=G,exports.formatSyncResult=q,exports.recordedEnvironment=B,exports.selectionCompositionFingerprint=U,exports.selectionEnvironment=V,exports.toSelection=H;
10
10
  //# sourceMappingURL=syncCommand.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"syncCommand.cjs","names":["path","HARNESS_STATE_POINTER","loadDoomConfig","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","spawnSync","createRequire","DOOM_PACKAGE_NAME","doomPiPackageRoot","resolveDoomConfigurationRoot","loadDomains","parseHarnessArgs","piExtensionDispatcherIsUpgradeable","piExtensionDispatcherVersion","missingLayerPackageSpecifiers","readLocatedSyncState","readSyncDrift","SyncProgress","acquireSyncLocationLock","resolveSyncLocation","crypto","syncGenerationDirectory","buildHarnessContext","ensureLayerPackages","computeWebSourcesHash","loadHarnessState","buildSyncedRuntime","writeSyncState","syncWebBundle","syncApiRoutes","syncStateSha256","writeProjectPiSettings","readMcpServerNames"],"sources":["../../../src/commands/syncCommand.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';\nimport { filterHookDisabledLayers, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport { buildHarnessContext } from '../adapters/harnessContext.ts';\nimport { ensureLayerPackages, missingLayerPackageSpecifiers } from '../adapters/layerPackageInstaller.ts';\nimport { readBootstrapStatus } from '../adapters/bootstrapLocator.ts';\nimport { DOOM_PACKAGE_NAME } from '../adapters/doomPackage.ts';\nimport { doomPiPackageRoot, piExtensionAliasIsCurrent, writePiExtensionAlias } from '../adapters/piExtensionAlias.ts';\nimport {\n PI_DISPATCHER_VERSION,\n piExtensionDispatcherIsUpgradeable,\n piExtensionDispatcherVersion,\n} from '../adapters/piExtensionDispatcher.ts';\nimport {\n mergePiSettings,\n piAgentDirectory,\n piThemeDirectory,\n readPiSettings,\n serializePiSettings,\n} from '../adapters/piSettings.ts';\nimport {\n DUPLICATE_REGISTRATION_DRIFT,\n projectRegistersDoom,\n writeProjectPiSettings,\n} from '../adapters/projectPiSettings.ts';\nimport { buildSyncedRuntime } from '../adapters/syncedRuntimeBuilder.ts';\nimport { acquireSyncLocationLock, resolveSyncLocation, syncGenerationDirectory } from '../adapters/syncLocation.ts';\nimport {\n publishSyncRegistration,\n SYNC_REGISTRATION_VERSION,\n syncStateSha256,\n type SyncPackageRegistration,\n} from '../adapters/syncRegistration.ts';\nimport { readSyncDrift } from '../adapters/syncDrift.ts';\nimport { syncApiRoutes } from '../adapters/apiRoutesSync.ts';\nimport { syncWebBundle } from '../adapters/webBundleSync.ts';\nimport {\n computeInputsHash,\n computeWebSourcesHash,\n readLocatedSyncState,\n readMcpServerNames,\n recordResolvedEntries,\n SYNC_STATE_VERSION,\n type SyncSelection,\n type SyncState,\n syncStateRootMatches,\n writeSyncState,\n} from '../adapters/syncState.ts';\nimport { HARNESS_STATE_POINTER, loadHarnessState } from '../adapters/config/harnessState';\nimport { loadDomains } from '@agimon-ai/doompi-config/domains';\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { DEFAULT_THEME, DEFAULT_THEME_NAME } from '@agimon-ai/doompi-ui/theme';\nimport { loadDoomConfig } from '../services/config/projectTrust';\nimport { createLayerResolvers, PERSONA_ENTRY, resolveExtensionComposition } from '../services/extensionAssembler.ts';\nimport type { HarnessOptions } from '../types/interfaces/harness';\nimport { resolveDoomConfigurationRoot } from '../adapters/repository/repository';\nimport { DOOMPI_DOMAINS_ENV, DOOMPI_MAJOR_MODE_ENV, DOOMPI_PROFILE_ENV } from './cli/matrixOptions.ts';\nimport { parseHarnessArgs } from './cli/options.ts';\nimport { SyncProgress, type SyncProgressOutput } from './syncPresenter.ts';\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 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;\n/** Published generations kept behind the current one, so a running hub mid-read survives a prune. */\nconst RETAINED_GENERATIONS = 1;\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 * 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(repoRoot: string, environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const { selection } = loadDoomConfig(repoRoot);\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 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/**\n * Removes generations the published one replaced.\n *\n * Each generation holds a full cockpit bundle and runtime, so keeping every one\n * ever built grows without bound. One superseded generation is retained because\n * a hub that resolved its assets a moment ago may still be reading them; older\n * ones have no reader left. Ordered by directory name, whose leading timestamp\n * makes publication order recoverable without reading each state file.\n */\nfunction pruneSupersededGenerations(\n generationsDirectory: string,\n published: string,\n onNotice: (message: string) => void,\n): void {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(generationsDirectory, { withFileTypes: true });\n } catch (error) {\n // Nothing to prune is indistinguishable from an unreadable directory here,\n // and neither is worth failing a sync that already published.\n onNotice(`could not list generations to prune: ${describeError(error)}`);\n return;\n }\n const superseded = entries\n .filter((entry) => entry.isDirectory() && entry.name !== published)\n .map((entry) => entry.name)\n .sort();\n const removable = superseded.slice(0, Math.max(0, superseded.length - RETAINED_GENERATIONS));\n for (const name of removable) {\n try {\n fs.rmSync(path.join(generationsDirectory, name), { recursive: true, force: true });\n } catch (error) {\n // A generation still held open elsewhere stays; the next sync retries it.\n onNotice(`could not remove superseded generation ${name}: ${describeError(error)}`);\n }\n }\n if (removable.length > 0) onNotice(`pruned ${String(removable.length)} superseded generation(s)`);\n}\n\nfunction describeError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** Resolves the matrix, stages it into home-scoped worktree storage, and publishes one generation. */\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 const check = args.includes(CHECK_OPTION);\n const force = args.includes(FORCE_OPTION);\n const rest = args.slice(1).filter((argument) => argument !== CHECK_OPTION && argument !== FORCE_OPTION);\n const homeDirectory = this.homeDirectory ?? environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n const defaultMajorMode = loadMajorModesConfig(repoRoot, homeDirectory).defaultMajorMode;\n const defaultDomains = loadDomains(repoRoot, homeDirectory).defaultDomains;\n const parsed = parseHarnessArgs(\n rest,\n selectionEnvironment(repoRoot, environment),\n currentDirectory,\n defaultMajorMode,\n defaultDomains,\n );\n const selection = toSelection(parsed.options);\n const agentDirectory = piAgentDirectory(environment, homeDirectory);\n if (this.settingsMode === '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 if (check) {\n const majorModesConfig = loadMajorModesConfig(repoRoot, homeDirectory);\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 this.settingsMode,\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 if (!force && readSyncDrift({ repoRoot, homeDirectory }).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 = this.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({ repoRoot, homeDirectory }).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n result = await this.stage(repoRoot, parsed.options, environment, homeDirectory, progress);\n } finally {\n await releaseLock?.();\n }\n emitFrontendHooks(repoRoot, output);\n output.write(formatSyncResult(result, this.settingsMode === 'embedded' ? 'dpi' : 'pi'));\n return 0;\n }\n\n private async stage(\n repoRoot: string,\n options: Omit<HarnessOptions, 'repoRoot'>,\n environment: NodeJS.ProcessEnv,\n homeDirectory: string,\n progress: SyncProgress,\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 = this.settingsMode === '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 const precompiled = progress.start(RUNTIME_LABEL, 'precompiling the mode bundles');\n const synced = await buildSyncedRuntime(location.root, environment, homeDirectory, { state, directory });\n precompiled(`${String(Object.keys(synced.bundles).length)} mode bundles`);\n const statePath = await writeSyncState(\n location.root,\n synced.state,\n homeDirectory,\n path.join(directory, 'state.json'),\n );\n\n const webProgress = progress.start(WEB_LABEL, 'bundling the web cockpit plugins');\n const web = await syncWebBundle({\n repoRoot: location.root,\n resolvedEntries: synced.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\n const apiProgress = progress.start(API_LABEL, 'generating the package API routes');\n const api = syncApiRoutes({\n resolvedEntries: synced.state.resolved,\n outputDirectory: path.join(directory, 'api'),\n onNotice: (message) => progress.line(API_LABEL, message),\n });\n apiProgress(`routes written to ${api.directory}`);\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: api.directory,\n package: packageRegistrationFor(location.root),\n },\n homeDirectory,\n );\n pruneSupersededGenerations(location.generationsDirectory, generation, (message) =>\n progress.line(SYNC_LABEL, message),\n );\n const projectSettingsPath = this.settingsMode === 'persisted' ? writeProjectPiSettings(location.root) : undefined;\n\n return {\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 } catch (error) {\n await fs.promises.rm(directory, { recursive: true, force: true });\n throw error;\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"syncCommand.cjs","names":["path","HARNESS_STATE_POINTER","loadDoomConfig","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","spawnSync","createRequire","DOOM_PACKAGE_NAME","doomPiPackageRoot","resolveDoomConfigurationRoot","loadDomains","parseHarnessArgs","piExtensionDispatcherIsUpgradeable","piExtensionDispatcherVersion","missingLayerPackageSpecifiers","readLocatedSyncState","readSyncDrift","SyncProgress","acquireSyncLocationLock","resolveSyncLocation","crypto","syncGenerationDirectory","buildHarnessContext","ensureLayerPackages","computeWebSourcesHash","loadHarnessState","buildSyncedRuntime","writeSyncState","syncWebBundle","syncApiRoutes","syncStateSha256","writeProjectPiSettings","readMcpServerNames"],"sources":["../../../src/commands/syncCommand.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';\nimport { filterHookDisabledLayers, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport { buildHarnessContext } from '../adapters/harnessContext.ts';\nimport { ensureLayerPackages, missingLayerPackageSpecifiers } from '../adapters/layerPackageInstaller.ts';\nimport { readBootstrapStatus } from '../adapters/bootstrapLocator.ts';\nimport { DOOM_PACKAGE_NAME } from '../adapters/doomPackage.ts';\nimport { doomPiPackageRoot, piExtensionAliasIsCurrent, writePiExtensionAlias } from '../adapters/piExtensionAlias.ts';\nimport {\n PI_DISPATCHER_VERSION,\n piExtensionDispatcherIsUpgradeable,\n piExtensionDispatcherVersion,\n} from '../adapters/piExtensionDispatcher.ts';\nimport {\n mergePiSettings,\n piAgentDirectory,\n piThemeDirectory,\n readPiSettings,\n serializePiSettings,\n} from '../adapters/piSettings.ts';\nimport {\n DUPLICATE_REGISTRATION_DRIFT,\n projectRegistersDoom,\n writeProjectPiSettings,\n} from '../adapters/projectPiSettings.ts';\nimport { buildSyncedRuntime } from '../adapters/syncedRuntimeBuilder.ts';\nimport { acquireSyncLocationLock, resolveSyncLocation, syncGenerationDirectory } from '../adapters/syncLocation.ts';\nimport {\n publishSyncRegistration,\n SYNC_REGISTRATION_VERSION,\n syncStateSha256,\n type SyncPackageRegistration,\n} from '../adapters/syncRegistration.ts';\nimport { readSyncDrift } from '../adapters/syncDrift.ts';\nimport { syncApiRoutes } from '../adapters/apiRoutesSync.ts';\nimport { syncWebBundle } from '../adapters/webBundleSync.ts';\nimport {\n computeInputsHash,\n computeWebSourcesHash,\n readLocatedSyncState,\n readMcpServerNames,\n recordResolvedEntries,\n SYNC_STATE_VERSION,\n type SyncSelection,\n type SyncState,\n syncStateRootMatches,\n writeSyncState,\n} from '../adapters/syncState.ts';\nimport { HARNESS_STATE_POINTER, loadHarnessState } from '../adapters/config/harnessState';\nimport { loadDomains } from '@agimon-ai/doompi-config/domains';\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { DEFAULT_THEME, DEFAULT_THEME_NAME } from '@agimon-ai/doompi-ui/theme';\nimport { loadDoomConfig } from '../services/config/projectTrust';\nimport { createLayerResolvers, PERSONA_ENTRY, resolveExtensionComposition } from '../services/extensionAssembler.ts';\nimport type { HarnessOptions } from '../types/interfaces/harness';\nimport { resolveDoomConfigurationRoot } from '../adapters/repository/repository';\nimport { DOOMPI_DOMAINS_ENV, DOOMPI_MAJOR_MODE_ENV, DOOMPI_PROFILE_ENV } from './cli/matrixOptions.ts';\nimport { parseHarnessArgs } from './cli/options.ts';\nimport { SyncProgress, type SyncProgressOutput } from './syncPresenter.ts';\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 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;\n/** Published generations kept behind the current one, so a running hub mid-read survives a prune. */\nconst RETAINED_GENERATIONS = 1;\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 * 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(repoRoot: string, environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const { selection } = loadDoomConfig(repoRoot);\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 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/**\n * Removes generations the published one replaced.\n *\n * Each generation holds a full cockpit bundle and runtime, so keeping every one\n * ever built grows without bound. One superseded generation is retained because\n * a hub that resolved its assets a moment ago may still be reading them; older\n * ones have no reader left. Ordered by directory name, whose leading timestamp\n * makes publication order recoverable without reading each state file.\n */\nfunction pruneSupersededGenerations(\n generationsDirectory: string,\n published: string,\n onNotice: (message: string) => void,\n): void {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(generationsDirectory, { withFileTypes: true });\n } catch (error) {\n // Nothing to prune is indistinguishable from an unreadable directory here,\n // and neither is worth failing a sync that already published.\n onNotice(`could not list generations to prune: ${describeError(error)}`);\n return;\n }\n const superseded = entries\n .filter((entry) => entry.isDirectory() && entry.name !== published)\n .map((entry) => entry.name)\n .sort();\n const removable = superseded.slice(0, Math.max(0, superseded.length - RETAINED_GENERATIONS));\n for (const name of removable) {\n try {\n fs.rmSync(path.join(generationsDirectory, name), { recursive: true, force: true });\n } catch (error) {\n // A generation still held open elsewhere stays; the next sync retries it.\n onNotice(`could not remove superseded generation ${name}: ${describeError(error)}`);\n }\n }\n if (removable.length > 0) onNotice(`pruned ${String(removable.length)} superseded generation(s)`);\n}\n\nfunction describeError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** Resolves the matrix, stages it into home-scoped worktree storage, and publishes one generation. */\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 const check = args.includes(CHECK_OPTION);\n const force = args.includes(FORCE_OPTION);\n const rest = args.slice(1).filter((argument) => argument !== CHECK_OPTION && argument !== FORCE_OPTION);\n const homeDirectory = this.homeDirectory ?? environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n const defaultMajorMode = loadMajorModesConfig(repoRoot, homeDirectory).defaultMajorMode;\n const defaultDomains = loadDomains(repoRoot, homeDirectory).defaultDomains;\n const parsed = parseHarnessArgs(\n rest,\n selectionEnvironment(repoRoot, environment),\n currentDirectory,\n defaultMajorMode,\n defaultDomains,\n );\n const selection = toSelection(parsed.options);\n const agentDirectory = piAgentDirectory(environment, homeDirectory);\n if (this.settingsMode === '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 if (check) {\n const majorModesConfig = loadMajorModesConfig(repoRoot, homeDirectory);\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 this.settingsMode,\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 = this.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 this.stage(repoRoot, parsed.options, environment, homeDirectory, progress);\n } finally {\n await releaseLock?.();\n }\n emitFrontendHooks(repoRoot, output);\n output.write(formatSyncResult(result, this.settingsMode === 'embedded' ? 'dpi' : 'pi'));\n return 0;\n }\n\n private async stage(\n repoRoot: string,\n options: Omit<HarnessOptions, 'repoRoot'>,\n environment: NodeJS.ProcessEnv,\n homeDirectory: string,\n progress: SyncProgress,\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 = this.settingsMode === '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 const precompiled = progress.start(RUNTIME_LABEL, 'precompiling the mode bundles');\n const synced = await buildSyncedRuntime(location.root, environment, homeDirectory, { state, directory });\n precompiled(`${String(Object.keys(synced.bundles).length)} mode bundles`);\n const statePath = await writeSyncState(\n location.root,\n synced.state,\n homeDirectory,\n path.join(directory, 'state.json'),\n );\n\n const webProgress = progress.start(WEB_LABEL, 'bundling the web cockpit plugins');\n const web = await syncWebBundle({\n repoRoot: location.root,\n resolvedEntries: synced.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\n const apiProgress = progress.start(API_LABEL, 'generating the package API routes');\n const api = syncApiRoutes({\n resolvedEntries: synced.state.resolved,\n outputDirectory: path.join(directory, 'api'),\n onNotice: (message) => progress.line(API_LABEL, message),\n });\n apiProgress(`routes written to ${api.directory}`);\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: api.directory,\n package: packageRegistrationFor(location.root),\n },\n homeDirectory,\n );\n pruneSupersededGenerations(location.generationsDirectory, generation, (message) =>\n progress.line(SYNC_LABEL, message),\n );\n const projectSettingsPath = this.settingsMode === 'persisted' ? writeProjectPiSettings(location.root) : undefined;\n\n return {\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 } catch (error) {\n await fs.promises.rm(directory, { recursive: true, force: true });\n throw error;\n }\n }\n}\n"],"mappings":"09CAyEA,MACM,EAAe,UAEf,EAAe,UAGf,EAAeA,EAAAA,QAAK,KAAK,QAAS,UAAW,gBAAgB,EAC7D,EAAO,SAIP,EAAa,OAYb,EAAoB,CAAC,SAAS,EAC9B,EAAgB,CAAC,qBAAsB,kBAAmB,qBAAsB,eAAe,EAQ/F,EAAgB,IAAI,IAAI,CAC5B,0BACA,kBACA,cACA,kBAGAC,EAAAA,sBACA,uBACF,CAAC,EA0BD,SAAgB,EAAoB,EAAwD,CAC1F,IAAM,EAAmC,CAAC,EAC1C,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,CAAW,EAC/C,IAAU,IAAA,IAAa,EAAc,IAAI,CAAG,IAC5C,EAAc,SAAS,CAAG,GAAK,EAAkB,KAAM,GAAW,EAAI,WAAW,CAAM,CAAC,KAC1F,EAAS,GAAO,GAGpB,OAAO,CACT,CAUA,SAAgB,EAAqB,EAAkB,EAAmD,CACxG,GAAM,CAAE,aAAcC,EAAAA,eAAe,CAAQ,EAE7C,OADK,EACE,CACL,GAAG,EACH,GAAI,EAAU,WAAa,CAAC,EAAA,kBACxB,EAAGC,EAAAA,uBAAwB,EAAU,SAAU,EAC/C,CAAC,EACL,GAAI,EAAU,SAAW,CAAC,EAAA,eAAkC,EAAGC,EAAAA,oBAAqB,EAAU,OAAQ,EAAI,CAAC,EAC3G,GAAI,EAAU,SAAW,EAAA,iBAAoC,IAAA,GACzD,EAAGC,EAAAA,oBAAqB,EAAU,QAAQ,KAAK,GAAG,CAAE,EACpD,CAAC,CACP,EAVuB,CAWzB,CAEA,SAAgB,EACd,EACe,CACf,MAAO,CACL,UAAW,EAAQ,UACnB,QAAS,EAAQ,QACjB,QAAS,EAAQ,QACjB,OAAQ,EAAQ,MAClB,CACF,CAEA,SAAgB,EACd,EACA,EACA,EAAwBC,EAAAA,QAAG,QAAQ,EAC3B,CACR,IAAM,GAAA,EAAmBC,EAAAA,qBAAAA,CAAqB,EAAU,CAAa,EAC/D,EAAYC,EAAAA,qBAAqB,CAAQ,EAC/C,OAAOC,EAAAA,4BAA4B,CACjC,OAAQ,EAAQ,OAChB,SAAU,GACV,KAAM,GACN,OAAQ,EAAQ,OAChB,aAAc,EAAU,aAAaC,EAAAA,aAAa,EAClD,UAAW,EAAQ,UACnB,QAAA,EAAQC,EAAAA,yBAAAA,CACN,GAAA,EACAC,EAAAA,cAAAA,CAAc,EAAkB,EAAQ,SAAS,EACjD,EAAQ,KACV,EACA,mBACA,WACF,CAAC,CAAC,CAAC,WACL,CAGA,SAAS,EAAmB,EAAkC,CAC5D,IAAM,EAAkB,CAAC,EACnB,EAAYZ,EAAAA,QAAK,KAAKa,EAAAA,iBAAiB,CAAc,EAAG,GAAGC,EAAAA,mBAAmB,MAAM,EACpF,EAAWC,EAAAA,eAAe,CAAc,EACxC,EAASC,EAAAA,gBAAgB,EAAU,EAAgB,CAAE,YAAW,UAAWF,EAAAA,kBAAmB,CAAC,EACjGG,EAAAA,oBAAoB,CAAM,IAAMA,EAAAA,oBAAoB,CAAQ,GAC9D,EAAM,KAAK,mDAAmD,EAE3DC,EAAAA,0BAA0B,CAAc,GAAG,EAAM,KAAK,oDAAoD,EAC/G,IAAM,EAAgB,GAAG,KAAK,UAAUC,EAAAA,cAAe,KAAM,CAAC,EAAE,IAIhE,OAHI,CAACC,EAAAA,QAAG,WAAW,CAAS,GAAKA,EAAAA,QAAG,aAAa,EAAW,MAAM,IAAM,IACtE,EAAM,KAAK,+CAA+C,EAErD,CACT,CAGA,SAAgB,EACd,EACA,EACA,EACA,EAAiC,QAAQ,IACzC,EAAiC,YACjC,EACU,CACV,GAAI,CAAC,EAAO,MAAO,CAAC,gCAAgC,EACpD,IAAM,EAAkB,CAAC,EACpBC,EAAAA,qBAAqB,EAAU,EAAM,IAAI,GAAG,EAAM,KAAK,8CAA8C,EAC1G,IAAM,EAAW,EAAM,WAErB,EAAS,YAAc,EAAU,WACjC,EAAS,UAAY,EAAU,SAC/B,EAAS,SAAW,EAAU,QAC9B,EAAS,QAAQ,KAAK,GAAG,IAAM,EAAU,QAAQ,KAAK,GAAG,IAEzD,EAAM,KAAK,uCAAuC,EAIhDC,EAAAA,kBAAkB,EAAU,EAAU,EAAY,MAAQhB,EAAAA,QAAG,QAAQ,CAAC,IAAM,EAAM,YACpF,EAAM,KAAK,6BAA6B,EAKxC,KAAK,UACHiB,EAAAA,uBAAAA,EACEhB,EAAAA,qBAAAA,CAAqB,EAAU,EAAY,MAAQD,EAAAA,QAAG,QAAQ,CAAC,EAC/DE,EAAAA,qBAAqB,CAAQ,CAC/B,CACF,IAAM,KAAK,UAAU,EAAM,QAAQ,GAEnC,EAAM,KAAK,kCAAkC,EAE3C,GAAkC,EAAM,yBAA2B,GACrE,EAAM,KAAK,+BAA+B,EAE5C,GAAI,CACGgB,EAAAA,oBAAoB,EAAU,IAAA,GAAW,EAAY,MAAQlB,EAAAA,QAAG,QAAQ,CAAC,CAAC,CAAC,OAC9E,EAAM,KAAK,yCAAyC,CAExD,MAAQ,CACN,EAAM,KAAK,yCAAyC,CACtD,CAEA,GAAI,IAAiB,YAAa,CAChC,IAAM,EAAiBmB,EAAAA,iBAAiB,CAAW,EAC7C,EAAYzB,EAAAA,QAAK,KAAKa,EAAAA,iBAAiB,CAAc,EAAG,GAAGC,EAAAA,mBAAmB,MAAM,EAC1F,EAAM,KAAK,GAAG,EAAmB,CAAc,CAAC,EAC5CY,EAAAA,qBAAqB,CAAQ,GAAG,EAAM,KAAKC,EAAAA,4BAA4B,GACvE,EAAM,SAAS,YAAc,GAAa,EAAM,SAAS,YAAcb,EAAAA,qBACzE,EAAM,KAAK,sCAAsC,CAErD,CACA,OAAO,CACT,CAGA,SAAS,EAAkB,EAAkB,EAA0B,CACrE,IAAM,EAAUd,EAAAA,QAAK,KAAK,EAAU,CAAY,EAChD,GAAI,CAACoB,EAAAA,QAAG,WAAW,CAAO,EAAG,OAC7B,IAAM,GAAA,EAASQ,EAAAA,UAAAA,CAAU,QAAQ,SAAU,CAAC,EAAS,SAAS,EAAG,CAAE,IAAK,EAAU,SAAU,MAAO,CAAC,EACpG,GAAI,EAAO,SAAW,EAAG,CACvB,EAAO,MAAM;CAAmD,EAChE,MACF,CACA,EAAO,MAAM,gCAAgC,EAAO,QAAQ,KAAK,GAAK,QAAQ,OAAO,EAAO,MAAM,IAAI,IAAI,CAC5G,CAEA,SAAgB,EAAiB,EAAoB,EAAS,KAAc,CAC1E,GAAM,CAAE,aAAc,EACtB,MAAO,CACL,aAAa,EAAU,YACvB,aAAa,EAAU,QAAQ,KAAK,IAAI,GAAK,IAC7C,aAAa,EAAU,SAAW,IAClC,aAAa,EAAO,aACpB,aAAa,EAAO,aACpB,aAAa,EAAO,WAAW,KAAK,IAAI,GAAK,IAC7C,aAAa,EAAO,YACpB,GAAI,EAAO,aAAe,CAAC,aAAa,EAAO,cAAc,EAAI,CAAC,EAClE,GAAI,EAAO,oBACP,CAAC,iDAAiD,EAAO,qBAAqB,EAC9E,CAAC,EACL,GACA,OAAO,EAAO,sCACd,EACF,CAAC,CAAC,KAAK;CAAI,CACb,CAWA,SAAS,EAAsB,EAAsC,CACnE,GAAI,CACF,OAAO5B,EAAAA,QAAK,SAAA,EACV6B,EAAAA,cAAAA,CAAc7B,EAAAA,QAAK,KAAK,EAAU,cAAc,CAAC,CAAC,CAAC,QAAQ,GAAG8B,EAAAA,kBAAkB,cAAc,CAChG,CACF,MAAQ,CAEN,MACF,CACF,CAEA,SAAS,EAAuB,EAA2C,CACzE,IAAM,EAAOV,EAAAA,QAAG,aAAa,EAAsB,CAAQ,GAAKW,EAAAA,kBAAkB,CAAC,EAC7E,EAAe/B,EAAAA,QAAK,KAAK,EAAM,cAAc,EAC7C,EAAW,KAAK,MAAMoB,EAAAA,QAAG,aAAa,EAAc,MAAM,CAAC,EAI3D,EAAU,EAAS,QACnB,EAAa,EAAS,IAAI,WAC1B,EAAY,MAAM,QAAQ,CAAU,EAAI,EAAW,KAAM,GAAU,OAAO,GAAU,QAAQ,EAAI,IAAA,GACtG,GAAI,OAAO,GAAY,UAAY,OAAO,GAAc,SACtD,MAAU,MAAM,+BAA+B,EAAK,qCAAqC,EAE3F,MAAO,CACL,OACA,UACA,eACA,MAAOA,EAAAA,QAAG,aAAapB,EAAAA,QAAK,QAAQ,EAAM,CAAS,CAAC,CACtD,CACF,CAWA,SAAS,EACP,EACA,EACA,EACM,CACN,IAAI,EACJ,GAAI,CACF,EAAUoB,EAAAA,QAAG,YAAY,EAAsB,CAAE,cAAe,EAAK,CAAC,CACxE,OAAS,EAAO,CAGd,EAAS,wCAAwC,EAAc,CAAK,GAAG,EACvE,MACF,CACA,IAAM,EAAa,EAChB,OAAQ,GAAU,EAAM,YAAY,GAAK,EAAM,OAAS,CAAS,CAAC,CAClE,IAAK,GAAU,EAAM,IAAI,CAAC,CAC1B,KAAK,EACF,EAAY,EAAW,MAAM,EAAG,KAAK,IAAI,EAAG,EAAW,OAAS,CAAoB,CAAC,EAC3F,IAAK,IAAM,KAAQ,EACjB,GAAI,CACF,EAAA,QAAG,OAAOpB,EAAAA,QAAK,KAAK,EAAsB,CAAI,EAAG,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,CACnF,OAAS,EAAO,CAEd,EAAS,0CAA0C,EAAK,IAAI,EAAc,CAAK,GAAG,CACpF,CAEE,EAAU,OAAS,GAAG,EAAS,UAAU,OAAO,EAAU,MAAM,EAAE,0BAA0B,CAClG,CAEA,SAAS,EAAc,EAAwB,CAC7C,OAAO,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,CAC9D,CAGA,IAAa,EAAb,KAAyB,CACvB,KAAgB,OAChB,aACA,cACA,SAEA,YAAY,EAA8B,CAAC,EAAG,CAC5C,KAAK,aAAe,EAAQ,cAAgB,YAC5C,KAAK,cAAgB,EAAQ,cAC7B,KAAK,SAAW,EAAQ,UAAY,EACtC,CAEA,QAAQ,EAAyB,CAC/B,OAAO,EAAK,KAAO,KAAK,IAC1B,CAEA,MAAM,QACJ,EACA,EAAiC,QAAQ,IACzC,EAAmB,QAAQ,IAAI,EAC/B,EAAqB,QAAQ,OACZ,CACjB,IAAM,EAAQ,EAAK,SAAS,CAAY,EAClC,EAAQ,EAAK,SAAS,CAAY,EAClC,EAAO,EAAK,MAAM,CAAC,CAAC,CAAC,OAAQ,GAAa,IAAa,GAAgB,IAAa,CAAY,EAChG,EAAgB,KAAK,eAAiB,EAAY,MAAQM,EAAAA,QAAG,QAAQ,EACrE,EAAgB,EAAY,YAC5B,EAAW,EACbN,EAAAA,QAAK,QAAQ,CAAa,EAC1BgC,EAAAA,6BAA6B,EAAkB,CAAa,EAC1D,GAAA,EAAmBzB,EAAAA,qBAAAA,CAAqB,EAAU,CAAa,CAAC,CAAC,iBACjE,GAAA,EAAiB0B,EAAAA,YAAAA,CAAY,EAAU,CAAa,CAAC,CAAC,eACtD,EAASC,EAAAA,iBACb,EACA,EAAqB,EAAU,CAAW,EAC1C,EACA,EACA,CACF,EACM,EAAY,EAAY,EAAO,OAAO,EACtC,EAAiBT,EAAAA,iBAAiB,EAAa,CAAa,EAClE,GAAI,KAAK,eAAiB,aAAe,CAAC,EAAO,CAC/C,GAAIU,EAAAA,mCAAmC,CAAc,EAAG,CACtD,IAAM,EAAkBC,EAAAA,6BAA6B,CAAc,EACnE,EAAA,sBAAsB,CAAc,EACpC,EAAO,MACL,uDAAuD,OAAO,CAAe,EAAE,QACjF,CACF,CACA,IAAM,EAAQ,EAAmB,CAAc,EAC/C,GAAI,EAAM,OAAS,EACjB,MAAU,MAAM,wCAAwC,EAAM,IAAK,GAAU,KAAK,GAAO,CAAC,CAAC,KAAK;CAAI,GAAG,CAE3G,CACA,GAAI,EAAO,CACT,IAAM,GAAA,EAAmB7B,EAAAA,qBAAAA,CAAqB,EAAU,CAAa,EAC/D,EAAkB8B,EAAAA,8BACtB,EACA,OAAO,KAAK,EAAiB,MAAM,EACnC7B,EAAAA,qBAAqB,CAAQ,CAC/B,EACA,GAAI,EAAgB,OAAS,EAM3B,OALA,EAAO,MACL,gCAAgC,EAC7B,IAAK,GAAc,0CAA0C,GAAW,CAAC,CACzE,KAAK;CAAI,EAAE,GAChB,EACO,EAET,IAAI,EACJ,GAAI,CACF,EAAU8B,EAAAA,qBAAqB,EAAU,CAAa,CACxD,OAAS,EAAO,CACd,IAAM,EAAS,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EAEpE,OADA,EAAO,MAAM,kCAAkC,EAAO,GAAG,EAClD,CACT,CACA,IAAM,EAAiC,EAAgC,EAAU,EAAO,QAAS,CAAa,EACxG,EAAQ,EACZ,EACA,EACA,GAAS,MACT,EACA,KAAK,aACL,CACF,EAMA,OALI,EAAM,SAAW,GACnB,EAAO,MAAM;CAA6B,EACnC,IAET,EAAO,MAAM,gCAAgC,EAAM,IAAK,GAAU,KAAK,GAAO,CAAC,CAAC,KAAK;CAAI,EAAE,GAAG,EACvF,EACT,CAKA,IAAM,EAAe,CACnB,WACA,gBACA,iBAAkB,EAAQ,EAAY,uBACxC,EACA,GAAI,CAAC,GAASC,EAAAA,cAAc,CAAY,CAAC,CAAC,MAExC,OADA,EAAO,MAAM;CAAqC,EAC3C,EAGT,IAAM,EAAW,IAAIC,EAAAA,aAAa,CAAM,EAClC,EAAc,KAAK,SACrB,IAAA,GACA,MAAMC,EAAAA,wBAAwBC,EAAAA,oBAAoB,EAAU,CAAa,CAAC,EAC1E,EACJ,GAAI,CAGF,GAAI,CAAC,GAASH,EAAAA,cAAc,CAAY,CAAC,CAAC,MAExC,OADA,EAAO,MAAM;CAAqC,EAC3C,EAET,EAAS,MAAM,KAAK,MAAM,EAAU,EAAO,QAAS,EAAa,EAAe,CAAQ,CAC1F,QAAU,CACR,MAAM,IAAc,CACtB,CAGA,OAFA,EAAkB,EAAU,CAAM,EAClC,EAAO,MAAM,EAAiB,EAAQ,KAAK,eAAiB,WAAa,MAAQ,IAAI,CAAC,EAC/E,CACT,CAEA,MAAc,MACZ,EACA,EACA,EACA,EACA,EACqB,CACrB,IAAM,EAAWG,EAAAA,oBAAoB,EAAU,CAAa,EACtD,EAAa,GAAG,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,GAAGC,EAAAA,QAAO,WAAW,IAC7D,EAAYC,EAAAA,wBAAwB,EAAU,CAAU,EAC9D,MAAMxB,EAAAA,QAAG,SAAS,MAAM,EAAS,qBAAsB,CAAE,UAAW,GAAM,KAAM,GAAuB,CAAC,EAKxG,MAAMA,EAAAA,QAAG,SAAS,MAAM,EAAW,CAAE,KAAM,GAAuB,CAAC,EAEnE,GAAI,CACF,IAAM,EAAS,EAAS,MAAM,EAAY,4CAA4C,EAChF,EAAU,MAAMyB,EAAAA,oBAAoB,CACxC,GAAG,EACH,SAAU,EAAS,KACnB,gBACA,IAAK,EAAS,KACd,kBAAmB,CACrB,CAAC,EACD,MAAMC,EAAAA,oBAAoB,CACxB,SAAU,EAAS,KACnB,OAAQ,EAAQ,iBAChB,OAAQ,OAAO,KAAK,EAAQ,iBAAiB,MAAM,EACnD,aACF,CAAC,EACD,EAAO,GAAG,OAAO,EAAQ,UAAU,UAAU,EAAE,WAAW,OAAO,EAAQ,UAAU,UAAU,EAAE,QAAQ,EACvG,IAAM,EAAY,EAAY,CAAO,EAC/B,EAAYtC,EAAAA,qBAAqB,EAAS,IAAI,EAC9C,EAAWe,EAAAA,sBAAsB,EAAQ,iBAAkB,CAAS,EACpE,EAAyB,EAAgC,EAAS,KAAM,EAAS,CAAa,EAC9F,EAAiBE,EAAAA,iBAAiB,EAAa,CAAa,EAC5D,EAAqBzB,EAAAA,QAAK,KAAKa,EAAAA,iBAAiB,CAAc,EAAG,GAAGC,EAAAA,mBAAmB,MAAM,EAC7F,EAAY,KAAK,eAAiB,YAAc,EAAqB,EAAQ,iBAC7E,EAAmB,CACvB,QAAA,GACA,KAAM,EAAS,KACf,SAAU,EAAS,SACnB,WAAYQ,EAAAA,kBAAkB,EAAS,KAAM,EAAW,CAAa,EACrE,eAAgByB,EAAAA,sBAAsB,CAAQ,EAC9C,yBACA,YACA,IAAK,EAAoB,EAAQ,WAAW,EAC5C,UAAW,CACT,oBAAA,EAAoBC,EAAAA,iBAAAA,CAAiB,EAAQ,WAAW,CAAC,CAAC,MAAM,mBAChE,YAAa,EAAQ,UAAU,YAC/B,cAAe,EAAQ,UAAU,aACnC,EACA,WACA,SAAU,CACR,cAAe,EAAQ,UAAU,cACjC,YAAa,EAAQ,YAAY,oBACjC,YACA,UAAWlC,EAAAA,kBACb,CACF,EAEM,EAAc,EAAS,MAAM,UAAe,+BAA+B,EAC3E,EAAS,MAAMmC,EAAAA,mBAAmB,EAAS,KAAM,EAAa,EAAe,CAAE,QAAO,WAAU,CAAC,EACvG,EAAY,GAAG,OAAO,OAAO,KAAK,EAAO,OAAO,CAAC,CAAC,MAAM,EAAE,cAAc,EACxE,IAAM,EAAY,MAAMC,EAAAA,eACtB,EAAS,KACT,EAAO,MACP,EACAlD,EAAAA,QAAK,KAAK,EAAW,YAAY,CACnC,EAEM,EAAc,EAAS,MAAM,MAAW,kCAAkC,EAC1E,EAAM,MAAMmD,EAAAA,cAAc,CAC9B,SAAU,EAAS,KACnB,gBAAiB,EAAO,MAAM,SAC9B,cACA,gBAAiBnD,EAAAA,QAAK,KAAK,EAAW,YAAY,EAClD,SAAW,GAAY,EAAS,KAAK,MAAW,CAAO,CACzD,CAAC,EACD,GAAI,EAAI,SAAW,SAAU,MAAU,MAAM,0BAA0B,EAAI,QAAQ,EACnF,EAAY,EAAI,SAAW,UAAY,iCAAiC,EAAI,UAAU,KAAK,IAAI,IAAM,EAAI,MAAM,EAE/G,IAAM,EAAc,EAAS,MAAM,MAAW,mCAAmC,EAC3E,EAAMoD,EAAAA,cAAc,CACxB,gBAAiB,EAAO,MAAM,SAC9B,gBAAiBpD,EAAAA,QAAK,KAAK,EAAW,KAAK,EAC3C,SAAW,GAAY,EAAS,KAAK,MAAW,CAAO,CACzD,CAAC,EACD,EAAY,qBAAqB,EAAI,WAAW,EAEhD,EAAA,wBACE,EAAS,KACT,CACE,QAAA,EACA,KAAM,EAAS,KACf,SAAU,EAAS,SACnB,aACA,eAAgB,EAChB,YACA,YAAaqD,EAAAA,gBAAgB,CAAS,EACtC,aAAc,EAAI,SAAW,UAAY,EAAI,UAAY,KACzD,aAAc,EAAI,UAClB,QAAS,EAAuB,EAAS,IAAI,CAC/C,EACA,CACF,EACA,EAA2B,EAAS,qBAAsB,EAAa,GACrE,EAAS,KAAK,EAAY,CAAO,CACnC,EACA,IAAM,EAAsB,KAAK,eAAiB,YAAcC,EAAAA,uBAAuB,EAAS,IAAI,EAAI,IAAA,GAExG,MAAO,CACL,YACA,GAAI,EAAsB,CAAE,qBAAoB,EAAI,CAAC,EACrD,YACA,WAAY,EAAO,MAAM,SAAS,cAAgBC,EAAAA,mBAAmB,EAAO,MAAM,SAAS,aAAa,EAAI,CAAC,EAC7G,WAAY,EAAQ,UAAU,WAC9B,WAAY,EAAQ,UAAU,UAChC,CACF,OAAS,EAAO,CAEd,MADA,MAAMnC,EAAAA,QAAG,SAAS,GAAG,EAAW,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EAC1D,CACR,CACF,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"syncCommand.d.cts","names":[],"sources":["../../../src/commands/syncCommand.ts"],"mappings":";;;;KAoHK,aAAa;KAEN;UAEK;EACf,eAAe;;EAEf;;EAEA;;UAGe;EACf;;EAEA;;EAEA;EACA,WAAW;EACX;EACA;EACA;;iBAGc,oBAAoB,aAAa,OAAO,aAAa;;;;;;;;;iBAmBrD,qBAAqB,kBAAkB,aAAa,OAAO,aAAa,OAAO;iBAe/E,YACd,SAAS,KAAK,kEACb;iBASa,gCACd,kBACA,SAAS,KAAK,sEACd;;iBAuCc,aACd,kBACA,WAAW,eACX,OAAO,uBACP,cAAa,OAAO,YACpB,eAAc,kBACd;iBAkEc,iBAAiB,QAAQ,YAAY;;cAyGxC;WACF;mBACQ;mBACA;mBACA;EAEjB,YAAY,UAAS;EAMrB,QAAQ;EAIF,QACJ,gBACA,cAAa,OAAO,YACpB,2BACA,SAAQ,aACP;UAsGW"}
1
+ {"version":3,"file":"syncCommand.d.cts","names":[],"sources":["../../../src/commands/syncCommand.ts"],"mappings":";;;;KAoHK,aAAa;KAEN;UAEK;EACf,eAAe;;EAEf;;EAEA;;UAGe;EACf;;EAEA;;EAEA;EACA,WAAW;EACX;EACA;EACA;;iBAGc,oBAAoB,aAAa,OAAO,aAAa;;;;;;;;;iBAmBrD,qBAAqB,kBAAkB,aAAa,OAAO,aAAa,OAAO;iBAe/E,YACd,SAAS,KAAK,kEACb;iBASa,gCACd,kBACA,SAAS,KAAK,sEACd;;iBAuCc,aACd,kBACA,WAAW,eACX,OAAO,uBACP,cAAa,OAAO,YACpB,eAAc,kBACd;iBAkEc,iBAAiB,QAAQ,YAAY;;cAyGxC;WACF;mBACQ;mBACA;mBACA;EAEjB,YAAY,UAAS;EAMrB,QAAQ;EAIF,QACJ,gBACA,cAAa,OAAO,YACpB,2BACA,SAAQ,aACP;UA2GW"}
@@ -1 +1 @@
1
- {"version":3,"file":"syncCommand.d.mts","names":[],"sources":["../../../src/commands/syncCommand.ts"],"mappings":";;;;KAoHK,aAAa;KAEN;UAEK;EACf,eAAe;;EAEf;;EAEA;;UAGe;EACf;;EAEA;;EAEA;EACA,WAAW;EACX;EACA;EACA;;iBAGc,oBAAoB,aAAa,OAAO,aAAa;;;;;;;;;iBAmBrD,qBAAqB,kBAAkB,aAAa,OAAO,aAAa,OAAO;iBAe/E,YACd,SAAS,KAAK,kEACb;iBASa,gCACd,kBACA,SAAS,KAAK,sEACd;;iBAuCc,aACd,kBACA,WAAW,eACX,OAAO,uBACP,cAAa,OAAO,YACpB,eAAc,kBACd;iBAkEc,iBAAiB,QAAQ,YAAY;;cAyGxC;WACF;mBACQ;mBACA;mBACA;EAEjB,YAAY,UAAS;EAMrB,QAAQ;EAIF,QACJ,gBACA,cAAa,OAAO,YACpB,2BACA,SAAQ,aACP;UAsGW"}
1
+ {"version":3,"file":"syncCommand.d.mts","names":[],"sources":["../../../src/commands/syncCommand.ts"],"mappings":";;;;KAoHK,aAAa;KAEN;UAEK;EACf,eAAe;;EAEf;;EAEA;;UAGe;EACf;;EAEA;;EAEA;EACA,WAAW;EACX;EACA;EACA;;iBAGc,oBAAoB,aAAa,OAAO,aAAa;;;;;;;;;iBAmBrD,qBAAqB,kBAAkB,aAAa,OAAO,aAAa,OAAO;iBAe/E,YACd,SAAS,KAAK,kEACb;iBASa,gCACd,kBACA,SAAS,KAAK,sEACd;;iBAuCc,aACd,kBACA,WAAW,eACX,OAAO,uBACP,cAAa,OAAO,YACpB,eAAc,kBACd;iBAkEc,iBAAiB,QAAQ,YAAY;;cAyGxC;WACF;mBACQ;mBACA;mBACA;EAEjB,YAAY,UAAS;EAMrB,QAAQ;EAIF,QACJ,gBACA,cAAa,OAAO,YACpB,2BACA,SAAQ,aACP;UA2GW"}
@@ -4,7 +4,7 @@ import{resolveDoomConfigurationRoot as e}from"../adapters/repository/repository.
4
4
  `)}`)}if(s){let e=B(m,d),t=F(e,Object.keys(e.layers),c(m));if(t.length>0)return o.write(`doompi sync is out of date:\n${t.map(e=>` configured package is not installed: ${e}`).join(`
5
5
  `)}\n`),1;let n;try{n=f(m,d)}catch(e){let t=e instanceof Error?e.message:String(e);return o.write(`doompi sync is out of date:\n ${t}\n`),1}let r=Y(m,_.options,d),a=Z(m,v,n?.state,i,this.settingsMode,r);return a.length===0?(o.write(`doompi sync is up to date
6
6
  `),0):(o.write(`doompi sync is out of date:\n${a.map(e=>` ${e}`).join(`
7
- `)}\n`),1)}if(!l&&I({repoRoot:m,homeDirectory:d}).fresh)return o.write(`doompi sync is already up to date
8
- `),0;let b=new ce(o),S=this.lockHeld?void 0:await n(r(m,d)),C;try{if(!l&&I({repoRoot:m,homeDirectory:d}).fresh)return o.write(`doompi sync is already up to date
9
- `),0;C=await this.stage(m,_.options,i,d,b)}finally{await S?.()}return be(m,o),o.write(Q(C,this.settingsMode===`embedded`?`dpi`:`pi`)),0}async stage(e,t,n,s,l){let f=r(e,s),h=`${Date.now().toString(36)}-${ue.randomUUID()}`,_=i(f,h);await R.promises.mkdir(f.generationsDirectory,{recursive:!0,mode:448}),await R.promises.mkdir(_,{mode:448});try{let e=l.start(G,`resolving the matrix and staging resources`),r=await ee({...t,repoRoot:f.root,homeDirectory:s,cwd:f.root,resourceDirectory:_});await te({repoRoot:f.root,config:r.majorModesConfig,layers:Object.keys(r.majorModesConfig.layers),environment:n}),e(`${String(r.resources.skillCount)} skills, ${String(r.resources.agentCount)} agents`);let i=J(t),v=c(f.root),y=m(r.majorModesConfig,v),b=Y(f.root,t,s),x=A(n,s),S=L.join(j(x),`${V}.json`),w=this.settingsMode===`persisted`?S:r.defaultThemePath,T={version:13,root:f.root,identity:f.identity,inputsHash:u(f.root,i,s),webSourcesHash:d(y),compositionFingerprint:b,selection:i,env:K(r.environment),fileState:{profileEnvironment:C(r.environment).state.profileEnvironment,pluginHooks:r.resources.pluginHooks,mcpProjection:r.resources.mcpProjection},resolved:y,baseline:{mcpConfigPath:r.resources.mcpConfigPath,personaFile:r.environment.DOOMPI_PERSONA_FILE,themePath:w,themeName:V}},E=l.start(`runtime`,`precompiling the mode bundles`),D=await ae(f.root,n,s,{state:T,directory:_});E(`${String(Object.keys(D.bundles).length)} mode bundles`);let O=await g(f.root,D.state,s,L.join(_,`state.json`)),k=l.start(`web`,`bundling the web cockpit plugins`),M=await se({repoRoot:f.root,resolvedEntries:D.state.resolved,environment:n,outputDirectory:L.join(_,`web-bundle`),onNotice:e=>l.line(`web`,e)});if(M.status===`failed`)throw Error(`Cockpit bundle failed: ${M.reason}`);k(M.status===`bundled`?`cockpit bundled with plugins: ${M.pluginIds.join(`, `)}`:M.reason);let N=l.start(`api`,`generating the package API routes`),P=oe({resolvedEntries:D.state.resolved,outputDirectory:L.join(_,`api`),onNotice:e=>l.line(`api`,e)});N(`routes written to ${P.directory}`),a(f.root,{version:1,root:f.root,identity:f.identity,generation:h,generationRoot:_,statePath:O,stateSha256:o(O),webDirectory:M.status===`bundled`?M.assetsDir:null,apiDirectory:P.directory,package:Se(f.root)},s),Ce(f.generationsDirectory,h,e=>l.line(G,e));let F=this.settingsMode===`persisted`?ie(f.root):void 0;return{statePath:O,...F?{projectSettingsPath:F}:{},selection:i,mcpServers:D.state.baseline.mcpConfigPath?p(D.state.baseline.mcpConfigPath):[],skillCount:r.resources.skillCount,agentCount:r.resources.agentCount}}catch(e){throw await R.promises.rm(_,{recursive:!0,force:!0}),e}}};export{we as SyncCommand,Z as collectDrift,Q as formatSyncResult,K as recordedEnvironment,Y as selectionCompositionFingerprint,q as selectionEnvironment,J as toSelection};
7
+ `)}\n`),1)}let b={repoRoot:m,homeDirectory:d,requireWebBundle:!!i.DOOMPI_WEB_PACKAGE_ROOT};if(!l&&I(b).fresh)return o.write(`doompi sync is already up to date
8
+ `),0;let S=new ce(o),C=this.lockHeld?void 0:await n(r(m,d)),E;try{if(!l&&I(b).fresh)return o.write(`doompi sync is already up to date
9
+ `),0;E=await this.stage(m,_.options,i,d,S)}finally{await C?.()}return be(m,o),o.write(Q(E,this.settingsMode===`embedded`?`dpi`:`pi`)),0}async stage(e,t,n,s,l){let f=r(e,s),h=`${Date.now().toString(36)}-${ue.randomUUID()}`,_=i(f,h);await R.promises.mkdir(f.generationsDirectory,{recursive:!0,mode:448}),await R.promises.mkdir(_,{mode:448});try{let e=l.start(G,`resolving the matrix and staging resources`),r=await ee({...t,repoRoot:f.root,homeDirectory:s,cwd:f.root,resourceDirectory:_});await te({repoRoot:f.root,config:r.majorModesConfig,layers:Object.keys(r.majorModesConfig.layers),environment:n}),e(`${String(r.resources.skillCount)} skills, ${String(r.resources.agentCount)} agents`);let i=J(t),v=c(f.root),y=m(r.majorModesConfig,v),b=Y(f.root,t,s),x=A(n,s),S=L.join(j(x),`${V}.json`),w=this.settingsMode===`persisted`?S:r.defaultThemePath,T={version:13,root:f.root,identity:f.identity,inputsHash:u(f.root,i,s),webSourcesHash:d(y),compositionFingerprint:b,selection:i,env:K(r.environment),fileState:{profileEnvironment:C(r.environment).state.profileEnvironment,pluginHooks:r.resources.pluginHooks,mcpProjection:r.resources.mcpProjection},resolved:y,baseline:{mcpConfigPath:r.resources.mcpConfigPath,personaFile:r.environment.DOOMPI_PERSONA_FILE,themePath:w,themeName:V}},E=l.start(`runtime`,`precompiling the mode bundles`),D=await ae(f.root,n,s,{state:T,directory:_});E(`${String(Object.keys(D.bundles).length)} mode bundles`);let O=await g(f.root,D.state,s,L.join(_,`state.json`)),k=l.start(`web`,`bundling the web cockpit plugins`),M=await se({repoRoot:f.root,resolvedEntries:D.state.resolved,environment:n,outputDirectory:L.join(_,`web-bundle`),onNotice:e=>l.line(`web`,e)});if(M.status===`failed`)throw Error(`Cockpit bundle failed: ${M.reason}`);k(M.status===`bundled`?`cockpit bundled with plugins: ${M.pluginIds.join(`, `)}`:M.reason);let N=l.start(`api`,`generating the package API routes`),P=oe({resolvedEntries:D.state.resolved,outputDirectory:L.join(_,`api`),onNotice:e=>l.line(`api`,e)});N(`routes written to ${P.directory}`),a(f.root,{version:1,root:f.root,identity:f.identity,generation:h,generationRoot:_,statePath:O,stateSha256:o(O),webDirectory:M.status===`bundled`?M.assetsDir:null,apiDirectory:P.directory,package:Se(f.root)},s),Ce(f.generationsDirectory,h,e=>l.line(G,e));let F=this.settingsMode===`persisted`?ie(f.root):void 0;return{statePath:O,...F?{projectSettingsPath:F}:{},selection:i,mcpServers:D.state.baseline.mcpConfigPath?p(D.state.baseline.mcpConfigPath):[],skillCount:r.resources.skillCount,agentCount:r.resources.agentCount}}catch(e){throw await R.promises.rm(_,{recursive:!0,force:!0}),e}}};export{we as SyncCommand,Z as collectDrift,Q as formatSyncResult,K as recordedEnvironment,Y as selectionCompositionFingerprint,q as selectionEnvironment,J as toSelection};
10
10
  //# sourceMappingURL=syncCommand.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"syncCommand.mjs","names":[],"sources":["../../../src/commands/syncCommand.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';\nimport { filterHookDisabledLayers, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport { buildHarnessContext } from '../adapters/harnessContext.ts';\nimport { ensureLayerPackages, missingLayerPackageSpecifiers } from '../adapters/layerPackageInstaller.ts';\nimport { readBootstrapStatus } from '../adapters/bootstrapLocator.ts';\nimport { DOOM_PACKAGE_NAME } from '../adapters/doomPackage.ts';\nimport { doomPiPackageRoot, piExtensionAliasIsCurrent, writePiExtensionAlias } from '../adapters/piExtensionAlias.ts';\nimport {\n PI_DISPATCHER_VERSION,\n piExtensionDispatcherIsUpgradeable,\n piExtensionDispatcherVersion,\n} from '../adapters/piExtensionDispatcher.ts';\nimport {\n mergePiSettings,\n piAgentDirectory,\n piThemeDirectory,\n readPiSettings,\n serializePiSettings,\n} from '../adapters/piSettings.ts';\nimport {\n DUPLICATE_REGISTRATION_DRIFT,\n projectRegistersDoom,\n writeProjectPiSettings,\n} from '../adapters/projectPiSettings.ts';\nimport { buildSyncedRuntime } from '../adapters/syncedRuntimeBuilder.ts';\nimport { acquireSyncLocationLock, resolveSyncLocation, syncGenerationDirectory } from '../adapters/syncLocation.ts';\nimport {\n publishSyncRegistration,\n SYNC_REGISTRATION_VERSION,\n syncStateSha256,\n type SyncPackageRegistration,\n} from '../adapters/syncRegistration.ts';\nimport { readSyncDrift } from '../adapters/syncDrift.ts';\nimport { syncApiRoutes } from '../adapters/apiRoutesSync.ts';\nimport { syncWebBundle } from '../adapters/webBundleSync.ts';\nimport {\n computeInputsHash,\n computeWebSourcesHash,\n readLocatedSyncState,\n readMcpServerNames,\n recordResolvedEntries,\n SYNC_STATE_VERSION,\n type SyncSelection,\n type SyncState,\n syncStateRootMatches,\n writeSyncState,\n} from '../adapters/syncState.ts';\nimport { HARNESS_STATE_POINTER, loadHarnessState } from '../adapters/config/harnessState';\nimport { loadDomains } from '@agimon-ai/doompi-config/domains';\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { DEFAULT_THEME, DEFAULT_THEME_NAME } from '@agimon-ai/doompi-ui/theme';\nimport { loadDoomConfig } from '../services/config/projectTrust';\nimport { createLayerResolvers, PERSONA_ENTRY, resolveExtensionComposition } from '../services/extensionAssembler.ts';\nimport type { HarnessOptions } from '../types/interfaces/harness';\nimport { resolveDoomConfigurationRoot } from '../adapters/repository/repository';\nimport { DOOMPI_DOMAINS_ENV, DOOMPI_MAJOR_MODE_ENV, DOOMPI_PROFILE_ENV } from './cli/matrixOptions.ts';\nimport { parseHarnessArgs } from './cli/options.ts';\nimport { SyncProgress, type SyncProgressOutput } from './syncPresenter.ts';\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 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;\n/** Published generations kept behind the current one, so a running hub mid-read survives a prune. */\nconst RETAINED_GENERATIONS = 1;\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 * 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(repoRoot: string, environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const { selection } = loadDoomConfig(repoRoot);\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 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/**\n * Removes generations the published one replaced.\n *\n * Each generation holds a full cockpit bundle and runtime, so keeping every one\n * ever built grows without bound. One superseded generation is retained because\n * a hub that resolved its assets a moment ago may still be reading them; older\n * ones have no reader left. Ordered by directory name, whose leading timestamp\n * makes publication order recoverable without reading each state file.\n */\nfunction pruneSupersededGenerations(\n generationsDirectory: string,\n published: string,\n onNotice: (message: string) => void,\n): void {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(generationsDirectory, { withFileTypes: true });\n } catch (error) {\n // Nothing to prune is indistinguishable from an unreadable directory here,\n // and neither is worth failing a sync that already published.\n onNotice(`could not list generations to prune: ${describeError(error)}`);\n return;\n }\n const superseded = entries\n .filter((entry) => entry.isDirectory() && entry.name !== published)\n .map((entry) => entry.name)\n .sort();\n const removable = superseded.slice(0, Math.max(0, superseded.length - RETAINED_GENERATIONS));\n for (const name of removable) {\n try {\n fs.rmSync(path.join(generationsDirectory, name), { recursive: true, force: true });\n } catch (error) {\n // A generation still held open elsewhere stays; the next sync retries it.\n onNotice(`could not remove superseded generation ${name}: ${describeError(error)}`);\n }\n }\n if (removable.length > 0) onNotice(`pruned ${String(removable.length)} superseded generation(s)`);\n}\n\nfunction describeError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** Resolves the matrix, stages it into home-scoped worktree storage, and publishes one generation. */\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 const check = args.includes(CHECK_OPTION);\n const force = args.includes(FORCE_OPTION);\n const rest = args.slice(1).filter((argument) => argument !== CHECK_OPTION && argument !== FORCE_OPTION);\n const homeDirectory = this.homeDirectory ?? environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n const defaultMajorMode = loadMajorModesConfig(repoRoot, homeDirectory).defaultMajorMode;\n const defaultDomains = loadDomains(repoRoot, homeDirectory).defaultDomains;\n const parsed = parseHarnessArgs(\n rest,\n selectionEnvironment(repoRoot, environment),\n currentDirectory,\n defaultMajorMode,\n defaultDomains,\n );\n const selection = toSelection(parsed.options);\n const agentDirectory = piAgentDirectory(environment, homeDirectory);\n if (this.settingsMode === '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 if (check) {\n const majorModesConfig = loadMajorModesConfig(repoRoot, homeDirectory);\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 this.settingsMode,\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 if (!force && readSyncDrift({ repoRoot, homeDirectory }).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 = this.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({ repoRoot, homeDirectory }).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n result = await this.stage(repoRoot, parsed.options, environment, homeDirectory, progress);\n } finally {\n await releaseLock?.();\n }\n emitFrontendHooks(repoRoot, output);\n output.write(formatSyncResult(result, this.settingsMode === 'embedded' ? 'dpi' : 'pi'));\n return 0;\n }\n\n private async stage(\n repoRoot: string,\n options: Omit<HarnessOptions, 'repoRoot'>,\n environment: NodeJS.ProcessEnv,\n homeDirectory: string,\n progress: SyncProgress,\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 = this.settingsMode === '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 const precompiled = progress.start(RUNTIME_LABEL, 'precompiling the mode bundles');\n const synced = await buildSyncedRuntime(location.root, environment, homeDirectory, { state, directory });\n precompiled(`${String(Object.keys(synced.bundles).length)} mode bundles`);\n const statePath = await writeSyncState(\n location.root,\n synced.state,\n homeDirectory,\n path.join(directory, 'state.json'),\n );\n\n const webProgress = progress.start(WEB_LABEL, 'bundling the web cockpit plugins');\n const web = await syncWebBundle({\n repoRoot: location.root,\n resolvedEntries: synced.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\n const apiProgress = progress.start(API_LABEL, 'generating the package API routes');\n const api = syncApiRoutes({\n resolvedEntries: synced.state.resolved,\n outputDirectory: path.join(directory, 'api'),\n onNotice: (message) => progress.line(API_LABEL, message),\n });\n apiProgress(`routes written to ${api.directory}`);\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: api.directory,\n package: packageRegistrationFor(location.root),\n },\n homeDirectory,\n );\n pruneSupersededGenerations(location.generationsDirectory, generation, (message) =>\n progress.line(SYNC_LABEL, message),\n );\n const projectSettingsPath = this.settingsMode === 'persisted' ? writeProjectPiSettings(location.root) : undefined;\n\n return {\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 } catch (error) {\n await fs.promises.rm(directory, { recursive: true, force: true });\n throw error;\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"syncCommand.mjs","names":[],"sources":["../../../src/commands/syncCommand.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';\nimport { filterHookDisabledLayers, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport { buildHarnessContext } from '../adapters/harnessContext.ts';\nimport { ensureLayerPackages, missingLayerPackageSpecifiers } from '../adapters/layerPackageInstaller.ts';\nimport { readBootstrapStatus } from '../adapters/bootstrapLocator.ts';\nimport { DOOM_PACKAGE_NAME } from '../adapters/doomPackage.ts';\nimport { doomPiPackageRoot, piExtensionAliasIsCurrent, writePiExtensionAlias } from '../adapters/piExtensionAlias.ts';\nimport {\n PI_DISPATCHER_VERSION,\n piExtensionDispatcherIsUpgradeable,\n piExtensionDispatcherVersion,\n} from '../adapters/piExtensionDispatcher.ts';\nimport {\n mergePiSettings,\n piAgentDirectory,\n piThemeDirectory,\n readPiSettings,\n serializePiSettings,\n} from '../adapters/piSettings.ts';\nimport {\n DUPLICATE_REGISTRATION_DRIFT,\n projectRegistersDoom,\n writeProjectPiSettings,\n} from '../adapters/projectPiSettings.ts';\nimport { buildSyncedRuntime } from '../adapters/syncedRuntimeBuilder.ts';\nimport { acquireSyncLocationLock, resolveSyncLocation, syncGenerationDirectory } from '../adapters/syncLocation.ts';\nimport {\n publishSyncRegistration,\n SYNC_REGISTRATION_VERSION,\n syncStateSha256,\n type SyncPackageRegistration,\n} from '../adapters/syncRegistration.ts';\nimport { readSyncDrift } from '../adapters/syncDrift.ts';\nimport { syncApiRoutes } from '../adapters/apiRoutesSync.ts';\nimport { syncWebBundle } from '../adapters/webBundleSync.ts';\nimport {\n computeInputsHash,\n computeWebSourcesHash,\n readLocatedSyncState,\n readMcpServerNames,\n recordResolvedEntries,\n SYNC_STATE_VERSION,\n type SyncSelection,\n type SyncState,\n syncStateRootMatches,\n writeSyncState,\n} from '../adapters/syncState.ts';\nimport { HARNESS_STATE_POINTER, loadHarnessState } from '../adapters/config/harnessState';\nimport { loadDomains } from '@agimon-ai/doompi-config/domains';\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { DEFAULT_THEME, DEFAULT_THEME_NAME } from '@agimon-ai/doompi-ui/theme';\nimport { loadDoomConfig } from '../services/config/projectTrust';\nimport { createLayerResolvers, PERSONA_ENTRY, resolveExtensionComposition } from '../services/extensionAssembler.ts';\nimport type { HarnessOptions } from '../types/interfaces/harness';\nimport { resolveDoomConfigurationRoot } from '../adapters/repository/repository';\nimport { DOOMPI_DOMAINS_ENV, DOOMPI_MAJOR_MODE_ENV, DOOMPI_PROFILE_ENV } from './cli/matrixOptions.ts';\nimport { parseHarnessArgs } from './cli/options.ts';\nimport { SyncProgress, type SyncProgressOutput } from './syncPresenter.ts';\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 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;\n/** Published generations kept behind the current one, so a running hub mid-read survives a prune. */\nconst RETAINED_GENERATIONS = 1;\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 * 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(repoRoot: string, environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const { selection } = loadDoomConfig(repoRoot);\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 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/**\n * Removes generations the published one replaced.\n *\n * Each generation holds a full cockpit bundle and runtime, so keeping every one\n * ever built grows without bound. One superseded generation is retained because\n * a hub that resolved its assets a moment ago may still be reading them; older\n * ones have no reader left. Ordered by directory name, whose leading timestamp\n * makes publication order recoverable without reading each state file.\n */\nfunction pruneSupersededGenerations(\n generationsDirectory: string,\n published: string,\n onNotice: (message: string) => void,\n): void {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(generationsDirectory, { withFileTypes: true });\n } catch (error) {\n // Nothing to prune is indistinguishable from an unreadable directory here,\n // and neither is worth failing a sync that already published.\n onNotice(`could not list generations to prune: ${describeError(error)}`);\n return;\n }\n const superseded = entries\n .filter((entry) => entry.isDirectory() && entry.name !== published)\n .map((entry) => entry.name)\n .sort();\n const removable = superseded.slice(0, Math.max(0, superseded.length - RETAINED_GENERATIONS));\n for (const name of removable) {\n try {\n fs.rmSync(path.join(generationsDirectory, name), { recursive: true, force: true });\n } catch (error) {\n // A generation still held open elsewhere stays; the next sync retries it.\n onNotice(`could not remove superseded generation ${name}: ${describeError(error)}`);\n }\n }\n if (removable.length > 0) onNotice(`pruned ${String(removable.length)} superseded generation(s)`);\n}\n\nfunction describeError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** Resolves the matrix, stages it into home-scoped worktree storage, and publishes one generation. */\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 const check = args.includes(CHECK_OPTION);\n const force = args.includes(FORCE_OPTION);\n const rest = args.slice(1).filter((argument) => argument !== CHECK_OPTION && argument !== FORCE_OPTION);\n const homeDirectory = this.homeDirectory ?? environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n const defaultMajorMode = loadMajorModesConfig(repoRoot, homeDirectory).defaultMajorMode;\n const defaultDomains = loadDomains(repoRoot, homeDirectory).defaultDomains;\n const parsed = parseHarnessArgs(\n rest,\n selectionEnvironment(repoRoot, environment),\n currentDirectory,\n defaultMajorMode,\n defaultDomains,\n );\n const selection = toSelection(parsed.options);\n const agentDirectory = piAgentDirectory(environment, homeDirectory);\n if (this.settingsMode === '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 if (check) {\n const majorModesConfig = loadMajorModesConfig(repoRoot, homeDirectory);\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 this.settingsMode,\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 = this.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 this.stage(repoRoot, parsed.options, environment, homeDirectory, progress);\n } finally {\n await releaseLock?.();\n }\n emitFrontendHooks(repoRoot, output);\n output.write(formatSyncResult(result, this.settingsMode === 'embedded' ? 'dpi' : 'pi'));\n return 0;\n }\n\n private async stage(\n repoRoot: string,\n options: Omit<HarnessOptions, 'repoRoot'>,\n environment: NodeJS.ProcessEnv,\n homeDirectory: string,\n progress: SyncProgress,\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 = this.settingsMode === '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 const precompiled = progress.start(RUNTIME_LABEL, 'precompiling the mode bundles');\n const synced = await buildSyncedRuntime(location.root, environment, homeDirectory, { state, directory });\n precompiled(`${String(Object.keys(synced.bundles).length)} mode bundles`);\n const statePath = await writeSyncState(\n location.root,\n synced.state,\n homeDirectory,\n path.join(directory, 'state.json'),\n );\n\n const webProgress = progress.start(WEB_LABEL, 'bundling the web cockpit plugins');\n const web = await syncWebBundle({\n repoRoot: location.root,\n resolvedEntries: synced.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\n const apiProgress = progress.start(API_LABEL, 'generating the package API routes');\n const api = syncApiRoutes({\n resolvedEntries: synced.state.resolved,\n outputDirectory: path.join(directory, 'api'),\n onNotice: (message) => progress.line(API_LABEL, message),\n });\n apiProgress(`routes written to ${api.directory}`);\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: api.directory,\n package: packageRegistrationFor(location.root),\n },\n homeDirectory,\n );\n pruneSupersededGenerations(location.generationsDirectory, generation, (message) =>\n progress.line(SYNC_LABEL, message),\n );\n const projectSettingsPath = this.settingsMode === 'persisted' ? writeProjectPiSettings(location.root) : undefined;\n\n return {\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 } catch (error) {\n await fs.promises.rm(directory, { recursive: true, force: true });\n throw error;\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.55",
3
+ "version": "0.0.1-alpha.57",
4
4
  "description": "Opinionated, composable Pi distribution for scoped agent tools, skills, MCP servers, and developer workflows.",
5
5
  "keywords": [
6
6
  "ai",
@@ -311,7 +311,8 @@
311
311
  },
312
312
  "dependencies": {
313
313
  "@deepseek-ai/cordis": "4.0.1",
314
- "@earendil-works/pi-coding-agent": "0.84.4",
314
+ "@earendil-works/pi-coding-agent": "0.85.0",
315
+ "@earendil-works/pi-server": "0.85.0",
315
316
  "cross-spawn": "7.0.6",
316
317
  "gpt-tokenizer": "4.0.0",
317
318
  "minimatch": "10.2.6",
@@ -319,30 +320,30 @@
319
320
  "rolldown": "1.2.6",
320
321
  "typebox": "1.3.19",
321
322
  "yaml": "2.9.0",
322
- "@agimon-ai/doompi-autostop": "0.0.1-alpha.32",
323
- "@agimon-ai/doompi-config": "0.0.1-alpha.54",
324
- "@agimon-ai/doompi-domain": "0.0.1-alpha.33",
325
- "@agimon-ai/doompi-cache": "0.0.1-alpha.21",
326
- "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.54",
327
- "@agimon-ai/doompi-major-mode": "0.0.1-alpha.33",
328
- "@agimon-ai/doompi-notification": "0.0.1-alpha.32",
329
- "@agimon-ai/doompi-profile": "0.0.1-alpha.33",
330
- "@agimon-ai/doompi-skill": "0.0.1-alpha.33",
331
- "@agimon-ai/doompi-telemetry": "0.0.1-alpha.54",
332
- "@agimon-ai/doompi-ui": "0.0.1-alpha.55"
323
+ "@agimon-ai/doompi-autostop": "0.0.1-alpha.33",
324
+ "@agimon-ai/doompi-config": "0.0.1-alpha.55",
325
+ "@agimon-ai/doompi-cache": "0.0.1-alpha.22",
326
+ "@agimon-ai/doompi-domain": "0.0.1-alpha.34",
327
+ "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.55",
328
+ "@agimon-ai/doompi-major-mode": "0.0.1-alpha.34",
329
+ "@agimon-ai/doompi-profile": "0.0.1-alpha.34",
330
+ "@agimon-ai/doompi-notification": "0.0.1-alpha.33",
331
+ "@agimon-ai/doompi-skill": "0.0.1-alpha.34",
332
+ "@agimon-ai/doompi-telemetry": "0.0.1-alpha.55",
333
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.56"
333
334
  },
334
335
  "devDependencies": {
335
- "@earendil-works/pi-ai": "0.84.4",
336
- "@earendil-works/pi-tui": "0.84.4",
336
+ "@earendil-works/pi-ai": "0.85.0",
337
+ "@earendil-works/pi-tui": "0.85.0",
337
338
  "@types/cross-spawn": "6.0.6",
338
339
  "@types/node": "26.4.0",
339
340
  "@vitest/coverage-v8": "4.1.11",
340
341
  "typescript": "7.0.2",
341
342
  "vitest": "4.1.11",
342
- "@agimon-ai/doompi-runner": "0.0.1-alpha.55"
343
+ "@agimon-ai/doompi-runner": "0.0.1-alpha.56"
343
344
  },
344
345
  "peerDependencies": {
345
- "@earendil-works/pi-tui": "0.84.4"
346
+ "@earendil-works/pi-tui": "0.85.0"
346
347
  },
347
348
  "peerDependenciesMeta": {
348
349
  "@earendil-works/pi-tui": {