@agimon-ai/doompi 0.0.1-alpha.80 → 0.0.1-alpha.81
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 +1 -1
- package/dist/cli/commands/sync/index.cjs +14 -2
- package/dist/cli/commands/sync/index.cjs.map +1 -1
- package/dist/cli/commands/sync/index.d.cts.map +1 -1
- package/dist/cli/commands/sync/index.d.mts.map +1 -1
- package/dist/cli/commands/sync/index.mjs +14 -2
- package/dist/cli/commands/sync/index.mjs.map +1 -1
- package/dist/cli/server/run.cjs +29 -17
- package/dist/cli/server/run.cjs.map +1 -1
- package/dist/cli/server/run.mjs +25 -15
- package/dist/cli/server/run.mjs.map +1 -1
- package/dist/compiler/inputs.cjs +10 -7
- package/dist/compiler/inputs.cjs.map +1 -1
- package/dist/compiler/inputs.mjs +10 -7
- package/dist/compiler/inputs.mjs.map +1 -1
- package/package.json +27 -27
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ releases.
|
|
|
51
51
|
|
|
52
52
|
- Node.js 22.19.0 or newer
|
|
53
53
|
- Release targets: macOS arm64, Linux x64, and Linux arm64
|
|
54
|
-
- Pi 0.
|
|
54
|
+
- Pi 0.86.0 and Pi TUI 0.86.0 for packages that declare them as peer requirements
|
|
55
55
|
|
|
56
56
|
## Try DoomPi without replacing your Pi setup
|
|
57
57
|
|
|
@@ -216,10 +216,22 @@ function collectDrift(repoRoot, selection, state, environment = process.env, set
|
|
|
216
216
|
if (require_projectSettings.projectRegistersDoom(repoRoot)) drift.push(require_projectSettings.DUPLICATE_REGISTRATION_DRIFT);
|
|
217
217
|
if (state.baseline.themePath !== themePath || state.baseline.themeName !== _agimon_ai_doompi_ui_theme.DEFAULT_THEME_NAME) drift.push("synced theme location is out of date");
|
|
218
218
|
}
|
|
219
|
-
|
|
219
|
+
const sharedDrift = require_index$10.readSyncDrift({
|
|
220
220
|
repoRoot,
|
|
221
221
|
homeDirectory: environment.HOME ?? node_os.default.homedir()
|
|
222
|
-
})
|
|
222
|
+
});
|
|
223
|
+
const sharedMessages = {
|
|
224
|
+
"never-synced": "sync registration is missing or invalid",
|
|
225
|
+
"code-changed": "cockpit sources changed since the last sync",
|
|
226
|
+
"cockpit-bundle-missing": "cockpit bundle is missing",
|
|
227
|
+
"package-apis-missing": "package API routes are missing",
|
|
228
|
+
"server-bundle-stale": "server bundle is missing or stale",
|
|
229
|
+
"mcp-bundle-stale": "MCP bundle is missing or stale"
|
|
230
|
+
};
|
|
231
|
+
for (const reason of sharedDrift.reasons) {
|
|
232
|
+
const message = sharedMessages[reason];
|
|
233
|
+
if (message) drift.push(message);
|
|
234
|
+
}
|
|
223
235
|
return drift;
|
|
224
236
|
}
|
|
225
237
|
/** Regenerates the hook files the other frontends read before any harness code runs. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["path","os","globalDoomConfigDirectory","resolveDoomConfigurationRoot","HARNESS_STATE_POINTER","DOOMPI_MAJOR_MODE_ENV","DOOMPI_DOMAINS_ENV","DOOMPI_PROFILE_ENV","loadDoomConfigLenient","loadMajorModesConfig","createLayerResolvers","resolveExtensionComposition","PERSONA_ENTRY","filterHookDisabledLayers","resolveLayers","piThemeDirectory","DEFAULT_THEME_NAME","readPiSettings","mergePiSettings","serializePiSettings","piExtensionAliasIsCurrent","DEFAULT_THEME","fs","syncStateRootMatches","computeInputsHash","recordResolvedEntries","readBootstrapStatus","piAgentDirectory","projectRegistersDoom","DUPLICATE_REGISTRATION_DRIFT","readSyncDrift","spawnSync","doomPiPackageRoot","DOOMPI_API_VERSION","readSyncRegistration","SYNC_REGISTRATION_VERSION","loadMajorModesConfigLenient","loadDomains","parseHarnessArgs","piExtensionDispatcherIsUpgradeable","missingLayerPackageSpecifiers","readLocatedSyncState","SyncProgress","acquireSyncLocationLock","resolveSyncLocation","crypto","syncGenerationDirectory","buildHarnessContext","ensureLayerPackages","SYNC_STATE_VERSION","computeWebSourcesHash","loadHarnessState","buildSyncedRuntime","syncWebBundle","syncServerBundle","syncMcpBundle","DOOM_SERVER_BUNDLE_FILE","DOOM_MCP_BUNDLE_FILE","computeServerSourcesHash","computeMcpSourcesHash","writeSyncState","writeProjectPiSettings","readMcpServerNames","syncStateSha256"],"sources":["../../../../src/cli/commands/sync/index.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { globalDoomConfigDirectory } from '@agimon-ai/doompi-config/config';\nimport { loadDomains } from '@agimon-ai/doompi-config/domains';\nimport { filterHookDisabledLayers, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport { loadMajorModesConfig, loadMajorModesConfigLenient } from '@agimon-ai/doompi-config/majorModes';\nimport type { ConfigDiagnostic } from '@agimon-ai/doompi-config/types';\nimport { DOOM_MCP_BUNDLE_FILE } from '@agimon-ai/doompi-core/mcp-facet';\nimport {\n mergePiSettings,\n piAgentDirectory,\n piThemeDirectory,\n readPiSettings,\n serializePiSettings,\n} from '@agimon-ai/doompi-core/runtime-pi-settings';\nimport { DOOM_SERVER_BUNDLE_FILE } from '@agimon-ai/doompi-core/server-facet';\nimport {\n acquireSyncLocationLock,\n resolveSyncLocation,\n syncGenerationDirectory,\n} from '@agimon-ai/doompi-core/sync-location';\nimport {\n DOOMPI_API_VERSION,\n publishSyncRegistration,\n readSyncRegistration,\n SYNC_REGISTRATION_VERSION,\n syncStateSha256,\n type SyncPackageRegistration,\n} from '@agimon-ai/doompi-core/sync-registration';\nimport { DEFAULT_THEME, DEFAULT_THEME_NAME } from '@agimon-ai/doompi-ui/theme';\n\nimport { buildSyncedRuntime } from '../../../builders/cli';\nimport { readBootstrapStatus } from '../../../builders/cli/bootstrapLocator';\nimport {\n createLayerResolvers,\n type ExtensionComposition,\n PERSONA_ENTRY,\n resolveExtensionComposition,\n} from '../../../builders/cli/extensionAssembler';\nimport { buildHarnessContext } from '../../../builders/cli/harnessContext';\nimport {\n doomPiPackageRoot,\n piExtensionAliasIsCurrent,\n writePiExtensionAlias,\n} from '../../../builders/cli/piExtensionAlias';\nimport { piExtensionDispatcherIsUpgradeable } from '../../../builders/cli/piExtensionDispatcher';\nimport {\n DUPLICATE_REGISTRATION_DRIFT,\n projectRegistersDoom,\n writeProjectPiSettings,\n} from '../../../builders/cli/projectSettings';\nimport { syncServerBundle } from '../../../builders/server';\nimport { syncMcpBundle } from '../../../builders/server/mcpBundle';\nimport { syncWebBundle } from '../../../builders/web';\nimport { HARNESS_STATE_POINTER, loadHarnessState } from '../../../composition/harnessState';\nimport { ensureLayerPackages, missingLayerPackageSpecifiers } from '../../../composition/layerPackageInstaller';\nimport { loadDoomConfigLenient } from '../../../composition/projectTrust';\nimport { resolveDoomConfigurationRoot } from '../../../composition/repository';\nimport { readSyncDrift } from '../../../composition/syncDrift';\nimport {\n computeInputsHash,\n computeMcpSourcesHash,\n computeServerSourcesHash,\n computeWebSourcesHash,\n readLocatedSyncState,\n readMcpServerNames,\n recordResolvedEntries,\n SYNC_STATE_VERSION,\n type SyncSelection,\n type SyncState,\n syncStateRootMatches,\n writeSyncState,\n} from '../../../composition/syncState';\nimport type { HarnessOptions } from '../../../composition/types/harness';\nimport { DOOMPI_DOMAINS_ENV, DOOMPI_MAJOR_MODE_ENV, DOOMPI_PROFILE_ENV } from '../../matrixOptions';\nimport { parseHarnessArgs } from '../../options';\nimport { SyncProgress, type SyncProgressOutput } from './presenter';\n\n/**\n * `doom-pi sync`: resolve the matrix once and write it where plain Pi finds it.\n *\n * The doom-emacs split. Everything that needs a real Node process (module\n * resolution, staging skills and agents, generating the MCP config) happens\n * here, and the doom-pi extension then only reads what this produced. The\n * launcher is untouched and keeps resolving the same matrix per run.\n */\n\nconst SYNC_COMMAND = 'sync';\nconst CHECK_OPTION = '--check';\n/** Republishes even when nothing drifted, for a generation suspected of being damaged. */\nconst FORCE_OPTION = '--force';\nconst GLOBAL_OPTION = '--global';\nconst HARNESS_ROOT_ENV = 'DOOMPI_ROOT';\nconst PERSONA_FILE_ENV = 'DOOMPI_PERSONA_FILE';\nconst HOOK_EMITTER = path.join('tools', 'harness', 'emit-hooks.mjs');\nconst NONE = '(none)';\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst SYNC_LABEL = 'sync';\nconst RUNTIME_LABEL = 'runtime';\nconst WEB_LABEL = 'web';\nconst API_LABEL = 'api';\n\nexport interface SyncRoots {\n globalOnly: boolean;\n globalRoot: string;\n sourceRoot: string;\n targetRoot: string;\n}\n\n/** Resolves the configuration source and publication destination for one sync. */\nexport function resolveSyncRoots(\n args: readonly string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n homeDirectory = environment.HOME ?? os.homedir(),\n): SyncRoots {\n const globalRoot = globalDoomConfigDirectory(homeDirectory);\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const sourceRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n const globalOnly = args.includes(GLOBAL_OPTION);\n return { globalOnly, globalRoot, sourceRoot, targetRoot: globalOnly ? globalRoot : sourceRoot };\n}\n\nconst GLOBAL_SCOPE_INHERITED_KEYS = [\n HARNESS_ROOT_ENV,\n HARNESS_STATE_POINTER,\n DOOMPI_MAJOR_MODE_ENV,\n DOOMPI_DOMAINS_ENV,\n DOOMPI_PROFILE_ENV,\n] as const;\n\n/** Removes workspace-only state before a global runtime is resolved. */\nexport function environmentForSyncScope(environment: NodeJS.ProcessEnv, globalOnly: boolean): NodeJS.ProcessEnv {\n if (!globalOnly) return environment;\n const scoped = { ...environment };\n for (const key of GLOBAL_SCOPE_INHERITED_KEYS) delete scoped[key];\n return scoped;\n}\n\n/**\n * Harness variables worth recording, by prefix or exact name.\n *\n * An allowlist rather than the whole environment: the state file is a snapshot\n * of resolved configuration, and dumping `process.env` into it would write\n * every credential the sync happened to run with onto disk.\n */\nconst RECORDED_PREFIXES = ['DOOMPI_'];\nconst RECORDED_KEYS = ['CLAUDE_PROJECT_DIR', 'CODEX_REPO_ROOT', 'ORIGINAL_REPO_PATH', 'MCP_UI_VIEWER'];\n/**\n * Launcher-only values a synced session must not inherit.\n *\n * The child extension list is recomposed on every load, and the subagent binary\n * points at `pi.sh`, which a session started as plain `pi` should not shell out\n * to: Doom Team resolves Pi's own CLI when the variable is absent.\n */\nconst EXCLUDED_KEYS = new Set([\n 'DOOMPI_CHILD_EXTENSIONS',\n 'DOOMPI_COMPOSED',\n 'DOOMPI_MUTE',\n 'DOOMPI_TEMP_DIR',\n // A pointer to the syncing process's own state file. Recording it would hand\n // every later session a path to a state that died with this one.\n HARNESS_STATE_POINTER,\n 'PI_SUBAGENT_PI_BINARY',\n]);\n\ntype SyncOutput = SyncProgressOutput;\n\nexport type SyncSettingsMode = 'persisted' | 'embedded';\n\nexport interface SyncCommandOptions {\n settingsMode?: SyncSettingsMode;\n /** Test/embedding override; normal CLI execution uses the process home. */\n homeDirectory?: string;\n /** Internal pipeline seam when the caller owns the worktree lock. */\n lockHeld?: boolean;\n}\n\nexport interface SyncResult {\n statePath: string;\n /** Omitted when DPI supplies the integration as a process-local overlay. */\n settingsPath?: string;\n /** Set only when the repository still carried its own DoomPi registration. */\n projectSettingsPath?: string;\n selection: SyncSelection;\n mcpServers: string[];\n skillCount: number;\n agentCount: number;\n}\n\nexport function recordedEnvironment(environment: NodeJS.ProcessEnv): Record<string, string> {\n const recorded: Record<string, string> = {};\n for (const [key, value] of Object.entries(environment)) {\n if (value === undefined || EXCLUDED_KEYS.has(key)) continue;\n if (RECORDED_KEYS.includes(key) || RECORDED_PREFIXES.some((prefix) => key.startsWith(prefix))) {\n recorded[key] = value;\n }\n }\n return recorded;\n}\n\n/**\n * Reports the config keys sync chose to ignore.\n *\n * Never fatal. A key nobody recognises is usually a config written for another\n * version of a layer, and refusing to build over it is worse than proceeding\n * without it. The strict check lives in `doompi doctor`.\n */\nfunction writeConfigDiagnostics(diagnostics: readonly ConfigDiagnostic[], output: SyncOutput): void {\n if (diagnostics.length === 0) return;\n const lines = diagnostics.map((entry) => ` ${entry.filePath}: ${entry.path}`).join('\\n');\n output.write(\n `config: ignored ${String(diagnostics.length)} unsupported key(s); run doompi doctor for the strict check\\n${lines}\\n`,\n );\n}\n/**\n * Layers the repository's declared selection under the usual resolution.\n *\n * `.doom/config.yaml` holds what the repository selects by default, the way\n * init.el does for doom-emacs. Seeding the environment the parser reads keeps\n * the precedence the launcher already documents: an explicit flag wins, then an\n * exported variable, then the declared default.\n */\nexport function selectionEnvironment(\n repoRoot: string,\n environment: NodeJS.ProcessEnv,\n homeDirectory?: string,\n): NodeJS.ProcessEnv {\n const { selection } = loadDoomConfigLenient(repoRoot, homeDirectory).config;\n if (!selection) return environment;\n return {\n ...environment,\n ...(selection.majorMode && !environment[DOOMPI_MAJOR_MODE_ENV]\n ? { [DOOMPI_MAJOR_MODE_ENV]: selection.majorMode }\n : {}),\n ...(selection.profile && !environment[DOOMPI_PROFILE_ENV] ? { [DOOMPI_PROFILE_ENV]: selection.profile } : {}),\n ...(selection.domains && environment[DOOMPI_DOMAINS_ENV] === undefined\n ? { [DOOMPI_DOMAINS_ENV]: selection.domains.join(',') }\n : {}),\n };\n}\n\nexport function toSelection(\n options: Pick<HarnessOptions, 'majorMode' | 'domains' | 'profile' | 'preset'>,\n): SyncSelection {\n return {\n majorMode: options.majorMode,\n domains: options.domains,\n profile: options.profile,\n preset: options.preset,\n };\n}\n\nexport function selectionCompositionFingerprint(\n repoRoot: string,\n options: Pick<HarnessOptions, 'agents' | 'hooks' | 'majorMode' | 'mcp' | 'preset'>,\n homeDirectory: string = os.homedir(),\n): string {\n const majorModesConfig = loadMajorModesConfig(repoRoot, homeDirectory);\n const resolvers = createLayerResolvers(repoRoot);\n return resolveExtensionComposition({\n agents: options.agents,\n autoStop: false,\n mute: false,\n preset: options.preset,\n personaEntry: resolvers.packageEntry(PERSONA_ENTRY),\n majorMode: options.majorMode,\n layers: filterHookDisabledLayers(\n majorModesConfig,\n resolveLayers(majorModesConfig, options.majorMode),\n options.hooks,\n ),\n majorModesConfig,\n resolvers,\n }).fingerprint;\n}\n\n/** Settings, dispatcher and theme differences an init would fix, independent of sync state. */\nfunction piIntegrationDrift(agentDirectory: string): string[] {\n const drift: string[] = [];\n const themePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n const settings = readPiSettings(agentDirectory);\n const merged = mergePiSettings(settings, agentDirectory, { themePath, themeName: DEFAULT_THEME_NAME });\n if (serializePiSettings(merged) !== serializePiSettings(settings)) {\n drift.push('Pi user settings are out of date; run doompi init');\n }\n if (!piExtensionAliasIsCurrent(agentDirectory)) drift.push('Pi user dispatcher is out of date; run doompi init');\n const expectedTheme = `${JSON.stringify(DEFAULT_THEME, null, 2)}\\n`;\n if (!fs.existsSync(themePath) || fs.readFileSync(themePath, 'utf8') !== expectedTheme) {\n drift.push('Pi user theme is out of date; run doompi init');\n }\n return drift;\n}\n\n/** Differences between what a sync would produce and what is on disk. */\nexport function collectDrift(\n repoRoot: string,\n selection: SyncSelection,\n state: SyncState | undefined,\n environment: NodeJS.ProcessEnv = process.env,\n settingsMode: SyncSettingsMode = 'persisted',\n expectedCompositionFingerprint?: string,\n): string[] {\n if (!state) return ['no sync state: run doompi sync'];\n const drift: string[] = [];\n if (syncRegistrationNeedsApiMigration(repoRoot, environment.HOME ?? os.homedir())) {\n drift.push('DoomPi registration needs API migration');\n }\n if (!syncStateRootMatches(repoRoot, state.root)) drift.push('sync state belongs to a different repository');\n const recorded = state.selection;\n if (\n recorded.majorMode !== selection.majorMode ||\n recorded.profile !== selection.profile ||\n recorded.preset !== selection.preset ||\n recorded.domains.join(',') !== selection.domains.join(',')\n ) {\n drift.push('selection changed since the last sync');\n }\n // Hashed against the recorded selection, not the requested one, so a\n // selection change is reported once rather than as two findings.\n if (computeInputsHash(repoRoot, recorded, environment.HOME ?? os.homedir()) !== state.inputsHash) {\n drift.push('.doom configuration changed');\n }\n // Re-resolving is what catches a dependency upgrade moving a package, which\n // the inputs hash deliberately does not read.\n if (\n JSON.stringify(\n recordResolvedEntries(\n loadMajorModesConfig(repoRoot, environment.HOME ?? os.homedir()),\n createLayerResolvers(repoRoot),\n ),\n ) !== JSON.stringify(state.resolved)\n ) {\n drift.push('resolved extension paths changed');\n }\n if (expectedCompositionFingerprint && state.compositionFingerprint !== expectedCompositionFingerprint) {\n drift.push('extension composition changed');\n }\n try {\n if (!readBootstrapStatus(repoRoot, undefined, environment.HOME ?? os.homedir()).fresh) {\n drift.push('precompiled runtime is missing or stale');\n }\n } catch {\n drift.push('precompiled runtime is missing or stale');\n }\n\n if (settingsMode === 'persisted') {\n const agentDirectory = piAgentDirectory(environment);\n const themePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n drift.push(...piIntegrationDrift(agentDirectory));\n if (projectRegistersDoom(repoRoot)) drift.push(DUPLICATE_REGISTRATION_DRIFT);\n if (state.baseline.themePath !== themePath || state.baseline.themeName !== DEFAULT_THEME_NAME) {\n drift.push('synced theme location is out of date');\n }\n }\n if (\n readSyncDrift({ repoRoot, homeDirectory: environment.HOME ?? os.homedir() }).reasons.includes('server-bundle-stale')\n ) {\n drift.push('server bundle is missing or stale');\n }\n return drift;\n}\n\n/** Regenerates the hook files the other frontends read before any harness code runs. */\nfunction emitFrontendHooks(repoRoot: string, output: SyncOutput): void {\n const emitter = path.join(repoRoot, HOOK_EMITTER);\n if (!fs.existsSync(emitter)) return;\n const result = spawnSync(process.execPath, [emitter, '--write'], { cwd: repoRoot, encoding: 'utf8' });\n if (result.status === 0) {\n output.write('hooks: regenerated for Claude Code and Codex\\n');\n return;\n }\n output.write(`hooks: emit-hooks failed (${result.stderr?.trim() || `exit ${String(result.status)}`})\\n`);\n}\n\nexport function formatSyncResult(result: SyncResult, runner = 'pi'): string {\n const { selection } = result;\n return [\n `mode: ${selection.majorMode}`,\n `domains: ${selection.domains.join(', ') || NONE}`,\n `profile: ${selection.profile ?? NONE}`,\n `skills: ${result.skillCount}`,\n `agents: ${result.agentCount}`,\n `mcp: ${result.mcpServers.join(', ') || NONE}`,\n `state: ${result.statePath}`,\n ...(result.settingsPath ? [`settings: ${result.settingsPath}`] : []),\n ...(result.projectSettingsPath\n ? [`project: removed duplicate registration from ${result.projectSettingsPath}`]\n : []),\n '',\n `Run ${runner} from the repository root to use it.`,\n '',\n ].join('\\n');\n}\n\n/**\n * The DoomPi that produced this generation, which is the one that can load it.\n *\n * Always the executing package, never another copy the repository happens to\n * install. A generation is not portable between two installations: the bundles\n * are compiled from the building package's own extension entries, the recorded\n * compiler inputs are its files, and the state names its bootstrap entry. Naming\n * a second copy here publishes a registration whose package disagrees with the\n * state it points at, and Pi's dispatcher then loads a harness that rejects the\n * bootstrap as stale on every session, with no sync able to fix it.\n *\n * A repository that wants its own copy to own its sessions runs sync with that\n * copy's CLI, which makes it the executing package.\n */\nfunction packageRegistrationFor(): SyncPackageRegistration {\n const root = fs.realpathSync(doomPiPackageRoot());\n const manifestPath = path.join(root, 'package.json');\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as {\n version?: unknown;\n doompiApiVersion?: unknown;\n pi?: { extensions?: unknown };\n };\n const version = manifest.version;\n const apiVersion = manifest.doompiApiVersion;\n const extensions = manifest.pi?.extensions;\n const extension = Array.isArray(extensions) ? extensions.find((value) => typeof value === 'string') : undefined;\n if (\n typeof version !== 'string' ||\n typeof apiVersion !== 'number' ||\n !Number.isSafeInteger(apiVersion) ||\n apiVersion < 1 ||\n apiVersion !== DOOMPI_API_VERSION ||\n typeof extension !== 'string'\n ) {\n throw new Error(`Installed DoomPi package at ${root} has no supported API-versioned Pi extension entry`);\n }\n return {\n root,\n version,\n apiVersion: apiVersion as number,\n manifestPath,\n entry: fs.realpathSync(path.resolve(root, extension)),\n };\n}\n\n/** Returns true when a valid legacy registration must be republished with API metadata. */\nexport function syncRegistrationNeedsApiMigration(repoRoot: string, homeDirectory: string): boolean {\n try {\n const registration = readSyncRegistration(repoRoot, homeDirectory);\n return (\n registration !== undefined &&\n (registration.version !== SYNC_REGISTRATION_VERSION || registration.package.apiVersion === undefined)\n );\n } catch {\n return false;\n }\n}\n\n/** Resolves the matrix, stages it into home-scoped worktree storage, and publishes one generation. */\nexport async function synchronize(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n commandOptions: SyncCommandOptions = {},\n): Promise<number> {\n const check = args.includes(CHECK_OPTION);\n const force = args.includes(FORCE_OPTION);\n const rest = args.slice(1).filter((argument) => ![CHECK_OPTION, FORCE_OPTION, GLOBAL_OPTION].includes(argument));\n const homeDirectory = commandOptions.homeDirectory ?? environment.HOME ?? os.homedir();\n const roots = resolveSyncRoots(args, environment, currentDirectory, homeDirectory);\n const { globalOnly, globalRoot, targetRoot: repoRoot } = roots;\n const scopedEnvironment = environmentForSyncScope(environment, globalOnly);\n if (globalOnly && !check) fs.mkdirSync(globalRoot, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n // Sync tolerates keys it does not recognise so a config written against a\n // different version cannot break a build. `doompi doctor` reports them.\n const modes = loadMajorModesConfigLenient(repoRoot, homeDirectory);\n const configDiagnostics = [...loadDoomConfigLenient(repoRoot, homeDirectory).diagnostics, ...modes.diagnostics];\n const defaultMajorMode = modes.config.defaultMajorMode;\n const defaultDomains = loadDomains(repoRoot, homeDirectory).defaultDomains;\n const parsed = parseHarnessArgs(\n rest,\n selectionEnvironment(repoRoot, scopedEnvironment, homeDirectory),\n globalOnly ? globalRoot : currentDirectory,\n defaultMajorMode,\n defaultDomains,\n );\n const selection = toSelection(parsed.options);\n const agentDirectory = piAgentDirectory(scopedEnvironment, homeDirectory);\n if ((commandOptions.settingsMode ?? 'persisted') === 'persisted' && !check) {\n if (piExtensionDispatcherIsUpgradeable(agentDirectory)) {\n writePiExtensionAlias(agentDirectory);\n output.write('repair: refreshed Pi user dispatcher\\n');\n }\n const drift = piIntegrationDrift(agentDirectory);\n if (drift.length > 0) {\n throw new Error(`DoomPi Pi integration is not ready:\\n${drift.map((entry) => ` ${entry}`).join('\\n')}`);\n }\n }\n writeConfigDiagnostics(configDiagnostics, output);\n if (check) {\n const majorModesConfig = modes.config;\n const missingPackages = missingLayerPackageSpecifiers(\n majorModesConfig,\n Object.keys(majorModesConfig.layers),\n createLayerResolvers(repoRoot),\n );\n if (missingPackages.length > 0) {\n output.write(\n `doompi sync is out of date:\\n${missingPackages\n .map((specifier) => ` configured package is not installed: ${specifier}`)\n .join('\\n')}\\n`,\n );\n return 1;\n }\n let located: ReturnType<typeof readLocatedSyncState>;\n try {\n located = readLocatedSyncState(repoRoot, homeDirectory);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n output.write(`doompi sync is out of date:\\n ${detail}\\n`);\n return 1;\n }\n const expectedCompositionFingerprint = selectionCompositionFingerprint(repoRoot, parsed.options, homeDirectory);\n const drift = collectDrift(\n repoRoot,\n selection,\n located?.state,\n scopedEnvironment,\n commandOptions.settingsMode ?? 'persisted',\n expectedCompositionFingerprint,\n );\n if (drift.length === 0) {\n output.write('doompi sync is up to date\\n');\n return 0;\n }\n output.write(`doompi sync is out of date:\\n${drift.map((entry) => ` ${entry}`).join('\\n')}\\n`);\n return 1;\n }\n\n // Publishing an identical generation is not a no-op: it moves the\n // registration, so every attached cockpit reloads and the previous\n // generation becomes garbage. Same inputs, same published result.\n const driftOptions = {\n repoRoot,\n homeDirectory,\n requireWebBundle: Boolean(environment.DOOMPI_WEB_PACKAGE_ROOT),\n };\n const registrationNeedsMigration = syncRegistrationNeedsApiMigration(repoRoot, homeDirectory);\n if (!force && !registrationNeedsMigration && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n\n const progress = new SyncProgress(output);\n const releaseLock = commandOptions.lockHeld\n ? undefined\n : await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n let result: SyncResult;\n try {\n // A concurrent publisher may have resolved the drift while this command\n // waited for the lock. Avoid moving the registration for no change.\n const registrationNeedsMigration = syncRegistrationNeedsApiMigration(repoRoot, homeDirectory);\n if (!force && !registrationNeedsMigration && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n result = await stageSync(repoRoot, parsed.options, scopedEnvironment, homeDirectory, progress, commandOptions);\n } finally {\n await releaseLock?.();\n }\n emitFrontendHooks(repoRoot, output);\n output.write(formatSyncResult(result, (commandOptions.settingsMode ?? 'persisted') === 'embedded' ? 'dpi' : 'pi'));\n return 0;\n}\n\nasync function stageSync(\n repoRoot: string,\n options: Omit<HarnessOptions, 'repoRoot'>,\n environment: NodeJS.ProcessEnv,\n homeDirectory: string,\n progress: SyncProgress,\n commandOptions: SyncCommandOptions = {},\n): Promise<SyncResult> {\n const location = resolveSyncLocation(repoRoot, homeDirectory);\n const generation = `${Date.now().toString(36)}-${crypto.randomUUID()}`;\n const directory = syncGenerationDirectory(location, generation);\n await fs.promises.mkdir(location.generationsDirectory, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n // The leaf is created without `recursive`, so an existing path is an error\n // rather than something to adopt: the cockpit signs and serves whatever the\n // published generation holds, and sync must only ever publish bytes it\n // wrote itself into a directory it just created.\n await fs.promises.mkdir(directory, { mode: PRIVATE_DIRECTORY_MODE });\n\n try {\n const staged = progress.start(SYNC_LABEL, 'resolving the matrix and staging resources');\n const context = await buildHarnessContext({\n ...options,\n repoRoot: location.root,\n homeDirectory,\n cwd: location.root,\n resourceDirectory: directory,\n });\n await ensureLayerPackages({\n repoRoot: location.root,\n config: context.majorModesConfig,\n layers: Object.keys(context.majorModesConfig.layers),\n environment,\n });\n staged(`${String(context.resources.skillCount)} skills, ${String(context.resources.agentCount)} agents`);\n const selection = toSelection(options);\n const resolvers = createLayerResolvers(location.root);\n const resolved = recordResolvedEntries(context.majorModesConfig, resolvers);\n const compositionFingerprint = selectionCompositionFingerprint(location.root, options, homeDirectory);\n const agentDirectory = piAgentDirectory(environment, homeDirectory);\n const persistedThemePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n const themePath =\n (commandOptions.settingsMode ?? 'persisted') === 'persisted' ? persistedThemePath : context.defaultThemePath;\n const state: SyncState = {\n version: SYNC_STATE_VERSION,\n root: location.root,\n identity: location.identity,\n inputsHash: computeInputsHash(location.root, selection, homeDirectory),\n webSourcesHash: computeWebSourcesHash(resolved),\n compositionFingerprint,\n selection,\n env: recordedEnvironment(context.environment),\n fileState: {\n profileEnvironment: loadHarnessState(context.environment).state.profileEnvironment,\n pluginHooks: context.resources.pluginHooks,\n mcpProjection: context.resources.mcpProjection,\n },\n resolved,\n baseline: {\n mcpConfigPath: context.resources.mcpConfigPath,\n personaFile: context.environment[PERSONA_FILE_ENV],\n themePath,\n themeName: DEFAULT_THEME_NAME,\n },\n };\n\n // Runtime compilation writes the package dist files consumed by both the web\n // and server bundlers. Finish it first so a package clean cannot race either\n // consumer, then run the independent web and server builds together.\n let resolveCompositions!: (compositions: readonly ExtensionComposition[]) => void;\n let rejectCompositions!: (reason?: unknown) => void;\n const compositionsReady = new Promise<readonly ExtensionComposition[]>((resolve, reject) => {\n resolveCompositions = resolve;\n rejectCompositions = reject;\n });\n const runtimeProgress = progress.start(RUNTIME_LABEL, 'precompiling the mode bundles');\n const runtimeBuild = buildSyncedRuntime(location.root, environment, homeDirectory, {\n state,\n directory,\n onCompositionsResolved: resolveCompositions,\n }).then((synced) => {\n runtimeProgress(`${String(Object.keys(synced.bundles).length)} mode bundles`);\n return synced;\n });\n void runtimeBuild.catch(rejectCompositions);\n const webBuild = (async () => {\n await runtimeBuild;\n const webProgress = progress.start(WEB_LABEL, 'bundling the web cockpit plugins');\n const web = await syncWebBundle({\n repoRoot: location.root,\n resolvedEntries: state.resolved,\n environment,\n outputDirectory: path.join(directory, 'web-bundle'),\n onNotice: (message) => progress.line(WEB_LABEL, message),\n });\n if (web.status === 'failed') throw new Error(`Cockpit bundle failed: ${web.reason}`);\n webProgress(web.status === 'bundled' ? `cockpit bundled with plugins: ${web.pluginIds.join(', ')}` : web.reason);\n return web;\n })();\n const serverBuild = (async () => {\n const compositions = await compositionsReady;\n await runtimeBuild;\n const apiProgress = progress.start(API_LABEL, 'compiling the server bundle');\n const apiDirectory = path.join(directory, 'api');\n const fingerprint = crypto\n .createHash('sha256')\n .update(JSON.stringify([...new Set(compositions.map((composition) => composition.fingerprint))]))\n .digest('hex');\n const [server, mcp] = await Promise.all([\n syncServerBundle({\n repositoryRoot: location.root,\n generation,\n fingerprint,\n compositions,\n outputDirectory: apiDirectory,\n cacheDirectory: path.join(directory, 'cache'),\n sharedCacheDirectory: location.sharedCacheDirectory,\n }),\n syncMcpBundle({\n repositoryRoot: location.root,\n generation,\n fingerprint,\n compositions,\n outputDirectory: path.join(directory, 'mcp'),\n cacheDirectory: path.join(directory, 'cache'),\n sharedCacheDirectory: location.sharedCacheDirectory,\n }),\n ]);\n apiProgress(\n `${server.descriptor.entries.length} server facet(s), ${mcp.descriptor.entries.length} MCP plugin(s) compiled`,\n );\n for (const gap of server.contractGaps) progress.line(API_LABEL, `API contract incomplete: ${gap}`);\n return { server, mcp, fingerprint, apiDirectory };\n })();\n const [runtimeResult, webResult, serverResult] = await Promise.allSettled([runtimeBuild, webBuild, serverBuild]);\n if (runtimeResult.status === 'rejected') throw runtimeResult.reason;\n if (webResult.status === 'rejected') throw webResult.reason;\n if (serverResult.status === 'rejected') throw serverResult.reason;\n const synced = runtimeResult.value;\n const web = webResult.value;\n const { server, mcp, fingerprint, apiDirectory } = serverResult.value;\n const descriptorPath = path.join(apiDirectory, DOOM_SERVER_BUNDLE_FILE);\n const mcpDescriptorPath = path.join(directory, 'mcp', DOOM_MCP_BUNDLE_FILE);\n const finalState: SyncState = {\n ...synced.state,\n serverBundle: {\n descriptorPath,\n fingerprint,\n compilerManifests: server.compilerManifests,\n sourcesHash: computeServerSourcesHash(synced.state.resolved),\n },\n mcpBundle: {\n descriptorPath: mcpDescriptorPath,\n fingerprint,\n compilerManifests: mcp.compilerManifests,\n sourcesHash: computeMcpSourcesHash(synced.state.resolved),\n },\n };\n const statePath = await writeSyncState(\n location.root,\n finalState,\n homeDirectory,\n path.join(directory, 'state.json'),\n );\n const projectSettingsPath =\n (commandOptions.settingsMode ?? 'persisted') === 'persisted'\n ? writeProjectPiSettings(location.root, homeDirectory)\n : undefined;\n const result: SyncResult = {\n statePath,\n ...(projectSettingsPath ? { projectSettingsPath } : {}),\n selection,\n mcpServers: synced.state.baseline.mcpConfigPath ? readMcpServerNames(synced.state.baseline.mcpConfigPath) : [],\n skillCount: context.resources.skillCount,\n agentCount: context.resources.agentCount,\n };\n publishSyncRegistration(\n location.root,\n {\n version: SYNC_REGISTRATION_VERSION,\n root: location.root,\n identity: location.identity,\n generation,\n generationRoot: directory,\n statePath,\n stateSha256: syncStateSha256(statePath),\n webDirectory: web.status === 'bundled' ? web.assetsDir : null,\n apiDirectory,\n serverBundle: { path: descriptorPath, fingerprint, sha256: syncStateSha256(descriptorPath) },\n mcpBundle: { path: mcpDescriptorPath, fingerprint, sha256: syncStateSha256(mcpDescriptorPath) },\n package: packageRegistrationFor(),\n },\n homeDirectory,\n );\n // ponytail: retain generations until host-owned drain evidence can prove no session uses them.\n // Directory age and an open-file check cannot establish that a lazy import is finished.\n return result;\n } catch (error) {\n await fs.promises.rm(directory, { recursive: true, force: true });\n throw error;\n }\n}\n\n/** Compatibility API. Executables call the command function directly. */\nexport class SyncCommand {\n readonly name = SYNC_COMMAND;\n private readonly settingsMode: SyncSettingsMode;\n private readonly homeDirectory: string | undefined;\n private readonly lockHeld: boolean;\n\n constructor(options: SyncCommandOptions = {}) {\n this.settingsMode = options.settingsMode ?? 'persisted';\n this.homeDirectory = options.homeDirectory;\n this.lockHeld = options.lockHeld ?? false;\n }\n\n matches(args: string[]): boolean {\n return args[0] === this.name;\n }\n\n async execute(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n ): Promise<number> {\n return synchronize(args, environment, currentDirectory, output, {\n settingsMode: this.settingsMode,\n homeDirectory: this.homeDirectory,\n lockHeld: this.lockHeld,\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2FA,MAAM,eAAe;AACrB,MAAM,eAAe;;AAErB,MAAM,eAAe;AACrB,MAAM,gBAAgB;AACtB,MAAM,mBAAmB;AACzB,MAAM,mBAAmB;AACzB,MAAM,eAAeA,UAAAA,QAAK,KAAK,SAAS,WAAW,gBAAgB;AACnE,MAAM,OAAO;AACb,MAAM,yBAAyB;AAC/B,MAAM,aAAa;AACnB,MAAM,gBAAgB;AACtB,MAAM,YAAY;AAClB,MAAM,YAAY;;AAUlB,SAAgB,iBACd,MACA,cAAiC,QAAQ,KACzC,mBAAmB,QAAQ,IAAI,GAC/B,gBAAgB,YAAY,QAAQC,QAAAA,QAAG,QAAQ,GACpC;CACX,MAAM,cAAA,GAAaC,gCAAAA,0BAAAA,CAA0B,aAAa;CAC1D,MAAM,gBAAgB,YAAY;CAClC,MAAM,aAAa,gBACfF,UAAAA,QAAK,QAAQ,aAAa,IAC1BG,gBAAAA,6BAA6B,kBAAkB,aAAa;CAChE,MAAM,aAAa,KAAK,SAAS,aAAa;CAC9C,OAAO;EAAE;EAAY;EAAY;EAAY,YAAY,aAAa,aAAa;CAAW;AAChG;AAEA,MAAM,8BAA8B;CAClC;CACAC,sCAAAA;CACAC,sBAAAA;CACAC,sBAAAA;CACAC,sBAAAA;AACF;;AAGA,SAAgB,wBAAwB,aAAgC,YAAwC;CAC9G,IAAI,CAAC,YAAY,OAAO;CACxB,MAAM,SAAS,EAAE,GAAG,YAAY;CAChC,KAAK,MAAM,OAAO,6BAA6B,OAAO,OAAO;CAC7D,OAAO;AACT;;;;;;;;AASA,MAAM,oBAAoB,CAAC,SAAS;AACpC,MAAM,gBAAgB;CAAC;CAAsB;CAAmB;CAAsB;AAAe;;;;;;;;AAQrG,MAAM,gCAAgB,IAAI,IAAI;CAC5B;CACA;CACA;CACA;CAGAH,sCAAAA;CACA;AACF,CAAC;AA0BD,SAAgB,oBAAoB,aAAwD;CAC1F,MAAM,WAAmC,CAAC;CAC1C,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,WAAW,GAAG;EACtD,IAAI,UAAU,KAAA,KAAa,cAAc,IAAI,GAAG,GAAG;EACnD,IAAI,cAAc,SAAS,GAAG,KAAK,kBAAkB,MAAM,WAAW,IAAI,WAAW,MAAM,CAAC,GAC1F,SAAS,OAAO;CAEpB;CACA,OAAO;AACT;;;;;;;;AASA,SAAS,uBAAuB,aAA0C,QAA0B;CAClG,IAAI,YAAY,WAAW,GAAG;CAC9B,MAAM,QAAQ,YAAY,KAAK,UAAU,KAAK,MAAM,SAAS,IAAI,MAAM,MAAM,CAAC,CAAC,KAAK,IAAI;CACxF,OAAO,MACL,qBAAqB,OAAO,YAAY,MAAM,EAAE,+DAA+D,MAAM,GACvH;AACF;;;;;;;;;AASA,SAAgB,qBACd,UACA,aACA,eACmB;CACnB,MAAM,EAAE,cAAcI,gBAAAA,sBAAsB,UAAU,aAAa,CAAC,CAAC;CACrE,IAAI,CAAC,WAAW,OAAO;CACvB,OAAO;EACL,GAAG;EACH,GAAI,UAAU,aAAa,CAAC,YAAA,uBACxB,GAAGH,sBAAAA,wBAAwB,UAAU,UAAU,IAC/C,CAAC;EACL,GAAI,UAAU,WAAW,CAAC,YAAA,oBAAkC,GAAGE,sBAAAA,qBAAqB,UAAU,QAAQ,IAAI,CAAC;EAC3G,GAAI,UAAU,WAAW,YAAA,sBAAoC,KAAA,IACzD,GAAGD,sBAAAA,qBAAqB,UAAU,QAAQ,KAAK,GAAG,EAAE,IACpD,CAAC;CACP;AACF;AAEA,SAAgB,YACd,SACe;CACf,OAAO;EACL,WAAW,QAAQ;EACnB,SAAS,QAAQ;EACjB,SAAS,QAAQ;EACjB,QAAQ,QAAQ;CAClB;AACF;AAEA,SAAgB,gCACd,UACA,SACA,gBAAwBL,QAAAA,QAAG,QAAQ,GAC3B;CACR,MAAM,oBAAA,GAAmBQ,oCAAAA,qBAAAA,CAAqB,UAAU,aAAa;CACrE,MAAM,YAAYC,8CAAAA,qBAAqB,QAAQ;CAC/C,OAAOC,8CAAAA,4BAA4B;EACjC,QAAQ,QAAQ;EAChB,UAAU;EACV,MAAM;EACN,QAAQ,QAAQ;EAChB,cAAc,UAAU,aAAaC,8CAAAA,aAAa;EAClD,WAAW,QAAQ;EACnB,SAAA,GAAQC,oCAAAA,yBAAAA,CACN,mBAAA,GACAC,oCAAAA,cAAAA,CAAc,kBAAkB,QAAQ,SAAS,GACjD,QAAQ,KACV;EACA;EACA;CACF,CAAC,CAAC,CAAC;AACL;;AAGA,SAAS,mBAAmB,gBAAkC;CAC5D,MAAM,QAAkB,CAAC;CACzB,MAAM,YAAYd,UAAAA,QAAK,MAAA,GAAKe,2CAAAA,iBAAAA,CAAiB,cAAc,GAAG,GAAGC,2BAAAA,mBAAmB,MAAM;CAC1F,MAAM,YAAA,GAAWC,2CAAAA,eAAAA,CAAe,cAAc;CAC9C,MAAM,UAAA,GAASC,2CAAAA,gBAAAA,CAAgB,UAAU,gBAAgB;EAAE;EAAW,WAAWF,2BAAAA;CAAmB,CAAC;CACrG,KAAA,GAAIG,2CAAAA,oBAAAA,CAAoB,MAAM,OAAA,GAAMA,2CAAAA,oBAAAA,CAAoB,QAAQ,GAC9D,MAAM,KAAK,mDAAmD;CAEhE,IAAI,CAACC,gBAAAA,0BAA0B,cAAc,GAAG,MAAM,KAAK,oDAAoD;CAC/G,MAAM,gBAAgB,GAAG,KAAK,UAAUC,2BAAAA,eAAe,MAAM,CAAC,EAAE;CAChE,IAAI,CAACC,QAAAA,QAAG,WAAW,SAAS,KAAKA,QAAAA,QAAG,aAAa,WAAW,MAAM,MAAM,eACtE,MAAM,KAAK,+CAA+C;CAE5D,OAAO;AACT;;AAGA,SAAgB,aACd,UACA,WACA,OACA,cAAiC,QAAQ,KACzC,eAAiC,aACjC,gCACU;CACV,IAAI,CAAC,OAAO,OAAO,CAAC,gCAAgC;CACpD,MAAM,QAAkB,CAAC;CACzB,IAAI,kCAAkC,UAAU,YAAY,QAAQrB,QAAAA,QAAG,QAAQ,CAAC,GAC9E,MAAM,KAAK,yCAAyC;CAEtD,IAAI,CAACsB,gBAAAA,qBAAqB,UAAU,MAAM,IAAI,GAAG,MAAM,KAAK,8CAA8C;CAC1G,MAAM,WAAW,MAAM;CACvB,IACE,SAAS,cAAc,UAAU,aACjC,SAAS,YAAY,UAAU,WAC/B,SAAS,WAAW,UAAU,UAC9B,SAAS,QAAQ,KAAK,GAAG,MAAM,UAAU,QAAQ,KAAK,GAAG,GAEzD,MAAM,KAAK,uCAAuC;CAIpD,IAAIC,gBAAAA,kBAAkB,UAAU,UAAU,YAAY,QAAQvB,QAAAA,QAAG,QAAQ,CAAC,MAAM,MAAM,YACpF,MAAM,KAAK,6BAA6B;CAI1C,IACE,KAAK,UACHwB,gBAAAA,uBAAAA,GACEhB,oCAAAA,qBAAAA,CAAqB,UAAU,YAAY,QAAQR,QAAAA,QAAG,QAAQ,CAAC,GAC/DS,8CAAAA,qBAAqB,QAAQ,CAC/B,CACF,MAAM,KAAK,UAAU,MAAM,QAAQ,GAEnC,MAAM,KAAK,kCAAkC;CAE/C,IAAI,kCAAkC,MAAM,2BAA2B,gCACrE,MAAM,KAAK,+BAA+B;CAE5C,IAAI;EACF,IAAI,CAACgB,gBAAAA,oBAAoB,UAAU,KAAA,GAAW,YAAY,QAAQzB,QAAAA,QAAG,QAAQ,CAAC,CAAC,CAAC,OAC9E,MAAM,KAAK,yCAAyC;CAExD,QAAQ;EACN,MAAM,KAAK,yCAAyC;CACtD;CAEA,IAAI,iBAAiB,aAAa;EAChC,MAAM,kBAAA,GAAiB0B,2CAAAA,iBAAAA,CAAiB,WAAW;EACnD,MAAM,YAAY3B,UAAAA,QAAK,MAAA,GAAKe,2CAAAA,iBAAAA,CAAiB,cAAc,GAAG,GAAGC,2BAAAA,mBAAmB,MAAM;EAC1F,MAAM,KAAK,GAAG,mBAAmB,cAAc,CAAC;EAChD,IAAIY,wBAAAA,qBAAqB,QAAQ,GAAG,MAAM,KAAKC,wBAAAA,4BAA4B;EAC3E,IAAI,MAAM,SAAS,cAAc,aAAa,MAAM,SAAS,cAAcb,2BAAAA,oBACzE,MAAM,KAAK,sCAAsC;CAErD;CACA,IACEc,iBAAAA,cAAc;EAAE;EAAU,eAAe,YAAY,QAAQ7B,QAAAA,QAAG,QAAQ;CAAE,CAAC,CAAC,CAAC,QAAQ,SAAS,qBAAqB,GAEnH,MAAM,KAAK,mCAAmC;CAEhD,OAAO;AACT;;AAGA,SAAS,kBAAkB,UAAkB,QAA0B;CACrE,MAAM,UAAUD,UAAAA,QAAK,KAAK,UAAU,YAAY;CAChD,IAAI,CAACsB,QAAAA,QAAG,WAAW,OAAO,GAAG;CAC7B,MAAM,UAAA,GAASS,mBAAAA,UAAAA,CAAU,QAAQ,UAAU,CAAC,SAAS,SAAS,GAAG;EAAE,KAAK;EAAU,UAAU;CAAO,CAAC;CACpG,IAAI,OAAO,WAAW,GAAG;EACvB,OAAO,MAAM,mDAAmD;EAChE;CACF;CACA,OAAO,MAAM,gCAAgC,OAAO,QAAQ,KAAK,KAAK,QAAQ,OAAO,OAAO,MAAM,IAAI,IAAI;AAC5G;AAEA,SAAgB,iBAAiB,QAAoB,SAAS,MAAc;CAC1E,MAAM,EAAE,cAAc;CACtB,OAAO;EACL,aAAa,UAAU;EACvB,aAAa,UAAU,QAAQ,KAAK,IAAI,KAAK;EAC7C,aAAa,UAAU,WAAW;EAClC,aAAa,OAAO;EACpB,aAAa,OAAO;EACpB,aAAa,OAAO,WAAW,KAAK,IAAI,KAAK;EAC7C,aAAa,OAAO;EACpB,GAAI,OAAO,eAAe,CAAC,aAAa,OAAO,cAAc,IAAI,CAAC;EAClE,GAAI,OAAO,sBACP,CAAC,iDAAiD,OAAO,qBAAqB,IAC9E,CAAC;EACL;EACA,OAAO,OAAO;EACd;CACF,CAAC,CAAC,KAAK,IAAI;AACb;;;;;;;;;;;;;;;AAgBA,SAAS,yBAAkD;CACzD,MAAM,OAAOT,QAAAA,QAAG,aAAaU,gBAAAA,kBAAkB,CAAC;CAChD,MAAM,eAAehC,UAAAA,QAAK,KAAK,MAAM,cAAc;CACnD,MAAM,WAAW,KAAK,MAAMsB,QAAAA,QAAG,aAAa,cAAc,MAAM,CAAC;CAKjE,MAAM,UAAU,SAAS;CACzB,MAAM,aAAa,SAAS;CAC5B,MAAM,aAAa,SAAS,IAAI;CAChC,MAAM,YAAY,MAAM,QAAQ,UAAU,IAAI,WAAW,MAAM,UAAU,OAAO,UAAU,QAAQ,IAAI,KAAA;CACtG,IACE,OAAO,YAAY,YACnB,OAAO,eAAe,YACtB,CAAC,OAAO,cAAc,UAAU,KAChC,aAAa,KACb,eAAeW,yCAAAA,sBACf,OAAO,cAAc,UAErB,MAAM,IAAI,MAAM,+BAA+B,KAAK,mDAAmD;CAEzG,OAAO;EACL;EACA;EACY;EACZ;EACA,OAAOX,QAAAA,QAAG,aAAatB,UAAAA,QAAK,QAAQ,MAAM,SAAS,CAAC;CACtD;AACF;;AAGA,SAAgB,kCAAkC,UAAkB,eAAgC;CAClG,IAAI;EACF,MAAM,gBAAA,GAAekC,yCAAAA,qBAAAA,CAAqB,UAAU,aAAa;EACjE,OACE,iBAAiB,KAAA,MAChB,aAAa,YAAYC,yCAAAA,6BAA6B,aAAa,QAAQ,eAAe,KAAA;CAE/F,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,eAAsB,YACpB,MACA,cAAiC,QAAQ,KACzC,mBAAmB,QAAQ,IAAI,GAC/B,SAAqB,QAAQ,QAC7B,iBAAqC,CAAC,GACrB;CACjB,MAAM,QAAQ,KAAK,SAAS,YAAY;CACxC,MAAM,QAAQ,KAAK,SAAS,YAAY;CACxC,MAAM,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,aAAa,CAAC;EAAC;EAAc;EAAc;CAAa,CAAC,CAAC,SAAS,QAAQ,CAAC;CAC/G,MAAM,gBAAgB,eAAe,iBAAiB,YAAY,QAAQlC,QAAAA,QAAG,QAAQ;CAErF,MAAM,EAAE,YAAY,YAAY,YAAY,aAD9B,iBAAiB,MAAM,aAAa,kBAAkB,aACP;CAC7D,MAAM,oBAAoB,wBAAwB,aAAa,UAAU;CACzE,IAAI,cAAc,CAAC,OAAO,QAAA,QAAG,UAAU,YAAY;EAAE,WAAW;EAAM,MAAM;CAAuB,CAAC;CAGpG,MAAM,SAAA,GAAQmC,oCAAAA,4BAAAA,CAA4B,UAAU,aAAa;CACjE,MAAM,oBAAoB,CAAC,GAAG5B,gBAAAA,sBAAsB,UAAU,aAAa,CAAC,CAAC,aAAa,GAAG,MAAM,WAAW;CAC9G,MAAM,mBAAmB,MAAM,OAAO;CACtC,MAAM,kBAAA,GAAiB6B,iCAAAA,YAAAA,CAAY,UAAU,aAAa,CAAC,CAAC;CAC5D,MAAM,SAASC,gBAAAA,iBACb,MACA,qBAAqB,UAAU,mBAAmB,aAAa,GAC/D,aAAa,aAAa,kBAC1B,kBACA,cACF;CACA,MAAM,YAAY,YAAY,OAAO,OAAO;CAC5C,MAAM,kBAAA,GAAiBX,2CAAAA,iBAAAA,CAAiB,mBAAmB,aAAa;CACxE,KAAK,eAAe,gBAAgB,iBAAiB,eAAe,CAAC,OAAO;EAC1E,IAAIY,gBAAAA,mCAAmC,cAAc,GAAG;GACtD,gBAAA,sBAAsB,cAAc;GACpC,OAAO,MAAM,0CAA0C;EACzD;EACA,MAAM,QAAQ,mBAAmB,cAAc;EAC/C,IAAI,MAAM,SAAS,GACjB,MAAM,IAAI,MAAM,wCAAwC,MAAM,KAAK,UAAU,KAAK,OAAO,CAAC,CAAC,KAAK,IAAI,GAAG;CAE3G;CACA,uBAAuB,mBAAmB,MAAM;CAChD,IAAI,OAAO;EACT,MAAM,mBAAmB,MAAM;EAC/B,MAAM,kBAAkBC,gBAAAA,8BACtB,kBACA,OAAO,KAAK,iBAAiB,MAAM,GACnC9B,8CAAAA,qBAAqB,QAAQ,CAC/B;EACA,IAAI,gBAAgB,SAAS,GAAG;GAC9B,OAAO,MACL,gCAAgC,gBAC7B,KAAK,cAAc,0CAA0C,WAAW,CAAC,CACzE,KAAK,IAAI,EAAE,GAChB;GACA,OAAO;EACT;EACA,IAAI;EACJ,IAAI;GACF,UAAU+B,gBAAAA,qBAAqB,UAAU,aAAa;EACxD,SAAS,OAAO;GACd,MAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GACpE,OAAO,MAAM,kCAAkC,OAAO,GAAG;GACzD,OAAO;EACT;EACA,MAAM,iCAAiC,gCAAgC,UAAU,OAAO,SAAS,aAAa;EAC9G,MAAM,QAAQ,aACZ,UACA,WACA,SAAS,OACT,mBACA,eAAe,gBAAgB,aAC/B,8BACF;EACA,IAAI,MAAM,WAAW,GAAG;GACtB,OAAO,MAAM,6BAA6B;GAC1C,OAAO;EACT;EACA,OAAO,MAAM,gCAAgC,MAAM,KAAK,UAAU,KAAK,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,GAAG;EAC9F,OAAO;CACT;CAKA,MAAM,eAAe;EACnB;EACA;EACA,kBAAkB,QAAQ,YAAY,uBAAuB;CAC/D;CACA,MAAM,6BAA6B,kCAAkC,UAAU,aAAa;CAC5F,IAAI,CAAC,SAAS,CAAC,8BAA8BX,iBAAAA,cAAc,YAAY,CAAC,CAAC,OAAO;EAC9E,OAAO,MAAM,qCAAqC;EAClD,OAAO;CACT;CAEA,MAAM,WAAW,IAAIY,kBAAAA,aAAa,MAAM;CACxC,MAAM,cAAc,eAAe,WAC/B,KAAA,IACA,OAAA,GAAMC,qCAAAA,wBAAAA,EAAAA,GAAwBC,qCAAAA,oBAAAA,CAAoB,UAAU,aAAa,CAAC;CAC9E,IAAI;CACJ,IAAI;EAGF,MAAM,6BAA6B,kCAAkC,UAAU,aAAa;EAC5F,IAAI,CAAC,SAAS,CAAC,8BAA8Bd,iBAAAA,cAAc,YAAY,CAAC,CAAC,OAAO;GAC9E,OAAO,MAAM,qCAAqC;GAClD,OAAO;EACT;EACA,SAAS,MAAM,UAAU,UAAU,OAAO,SAAS,mBAAmB,eAAe,UAAU,cAAc;CAC/G,UAAU;EACR,MAAM,cAAc;CACtB;CACA,kBAAkB,UAAU,MAAM;CAClC,OAAO,MAAM,iBAAiB,SAAS,eAAe,gBAAgB,iBAAiB,aAAa,QAAQ,IAAI,CAAC;CACjH,OAAO;AACT;AAEA,eAAe,UACb,UACA,SACA,aACA,eACA,UACA,iBAAqC,CAAC,GACjB;CACrB,MAAM,YAAA,GAAWc,qCAAAA,oBAAAA,CAAoB,UAAU,aAAa;CAC5D,MAAM,aAAa,GAAG,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,GAAGC,YAAAA,QAAO,WAAW;CACnE,MAAM,aAAA,GAAYC,qCAAAA,wBAAAA,CAAwB,UAAU,UAAU;CAC9D,MAAMxB,QAAAA,QAAG,SAAS,MAAM,SAAS,sBAAsB;EAAE,WAAW;EAAM,MAAM;CAAuB,CAAC;CAKxG,MAAMA,QAAAA,QAAG,SAAS,MAAM,WAAW,EAAE,MAAM,uBAAuB,CAAC;CAEnE,IAAI;EACF,MAAM,SAAS,SAAS,MAAM,YAAY,4CAA4C;EACtF,MAAM,UAAU,MAAMyB,uBAAAA,oBAAoB;GACxC,GAAG;GACH,UAAU,SAAS;GACnB;GACA,KAAK,SAAS;GACd,mBAAmB;EACrB,CAAC;EACD,MAAMC,gBAAAA,oBAAoB;GACxB,UAAU,SAAS;GACnB,QAAQ,QAAQ;GAChB,QAAQ,OAAO,KAAK,QAAQ,iBAAiB,MAAM;GACnD;EACF,CAAC;EACD,OAAO,GAAG,OAAO,QAAQ,UAAU,UAAU,EAAE,WAAW,OAAO,QAAQ,UAAU,UAAU,EAAE,QAAQ;EACvG,MAAM,YAAY,YAAY,OAAO;EACrC,MAAM,YAAYtC,8CAAAA,qBAAqB,SAAS,IAAI;EACpD,MAAM,WAAWe,gBAAAA,sBAAsB,QAAQ,kBAAkB,SAAS;EAC1E,MAAM,yBAAyB,gCAAgC,SAAS,MAAM,SAAS,aAAa;EACpG,MAAM,kBAAA,GAAiBE,2CAAAA,iBAAAA,CAAiB,aAAa,aAAa;EAClE,MAAM,qBAAqB3B,UAAAA,QAAK,MAAA,GAAKe,2CAAAA,iBAAAA,CAAiB,cAAc,GAAG,GAAGC,2BAAAA,mBAAmB,MAAM;EACnG,MAAM,aACH,eAAe,gBAAgB,iBAAiB,cAAc,qBAAqB,QAAQ;EAC9F,MAAM,QAAmB;GACvB,SAASiC,2CAAAA;GACT,MAAM,SAAS;GACf,UAAU,SAAS;GACnB,YAAYzB,gBAAAA,kBAAkB,SAAS,MAAM,WAAW,aAAa;GACrE,gBAAgB0B,gBAAAA,sBAAsB,QAAQ;GAC9C;GACA;GACA,KAAK,oBAAoB,QAAQ,WAAW;GAC5C,WAAW;IACT,qBAAA,GAAoBC,sCAAAA,iBAAAA,CAAiB,QAAQ,WAAW,CAAC,CAAC,MAAM;IAChE,aAAa,QAAQ,UAAU;IAC/B,eAAe,QAAQ,UAAU;GACnC;GACA;GACA,UAAU;IACR,eAAe,QAAQ,UAAU;IACjC,aAAa,QAAQ,YAAY;IACjC;IACA,WAAWnC,2BAAAA;GACb;EACF;EAKA,IAAI;EACJ,IAAI;EACJ,MAAM,oBAAoB,IAAI,SAA0C,SAAS,WAAW;GAC1F,sBAAsB;GACtB,qBAAqB;EACvB,CAAC;EACD,MAAM,kBAAkB,SAAS,MAAM,eAAe,+BAA+B;EACrF,MAAM,eAAeoC,2BAAAA,mBAAmB,SAAS,MAAM,aAAa,eAAe;GACjF;GACA;GACA,wBAAwB;EAC1B,CAAC,CAAC,CAAC,MAAM,WAAW;GAClB,gBAAgB,GAAG,OAAO,OAAO,KAAK,OAAO,OAAO,CAAC,CAAC,MAAM,EAAE,cAAc;GAC5E,OAAO;EACT,CAAC;EACD,aAAkB,MAAM,kBAAkB;EAC1C,MAAM,YAAY,YAAY;GAC5B,MAAM;GACN,MAAM,cAAc,SAAS,MAAM,WAAW,kCAAkC;GAChF,MAAM,MAAM,MAAMC,cAAAA,cAAc;IAC9B,UAAU,SAAS;IACnB,iBAAiB,MAAM;IACvB;IACA,iBAAiBrD,UAAAA,QAAK,KAAK,WAAW,YAAY;IAClD,WAAW,YAAY,SAAS,KAAK,WAAW,OAAO;GACzD,CAAC;GACD,IAAI,IAAI,WAAW,UAAU,MAAM,IAAI,MAAM,0BAA0B,IAAI,QAAQ;GACnF,YAAY,IAAI,WAAW,YAAY,iCAAiC,IAAI,UAAU,KAAK,IAAI,MAAM,IAAI,MAAM;GAC/G,OAAO;EACT,EAAA,CAAG;EACH,MAAM,eAAe,YAAY;GAC/B,MAAM,eAAe,MAAM;GAC3B,MAAM;GACN,MAAM,cAAc,SAAS,MAAM,WAAW,6BAA6B;GAC3E,MAAM,eAAeA,UAAAA,QAAK,KAAK,WAAW,KAAK;GAC/C,MAAM,cAAc6C,YAAAA,QACjB,WAAW,QAAQ,CAAC,CACpB,OAAO,KAAK,UAAU,CAAC,GAAG,IAAI,IAAI,aAAa,KAAK,gBAAgB,YAAY,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAChG,OAAO,KAAK;GACf,MAAM,CAAC,QAAQ,OAAO,MAAM,QAAQ,IAAI,CACtCS,gBAAAA,iBAAiB;IACf,gBAAgB,SAAS;IACzB;IACA;IACA;IACA,iBAAiB;IACjB,gBAAgBtD,UAAAA,QAAK,KAAK,WAAW,OAAO;IAC5C,sBAAsB,SAAS;GACjC,CAAC,GACDuD,kBAAAA,cAAc;IACZ,gBAAgB,SAAS;IACzB;IACA;IACA;IACA,iBAAiBvD,UAAAA,QAAK,KAAK,WAAW,KAAK;IAC3C,gBAAgBA,UAAAA,QAAK,KAAK,WAAW,OAAO;IAC5C,sBAAsB,SAAS;GACjC,CAAC,CACH,CAAC;GACD,YACE,GAAG,OAAO,WAAW,QAAQ,OAAO,oBAAoB,IAAI,WAAW,QAAQ,OAAO,wBACxF;GACA,KAAK,MAAM,OAAO,OAAO,cAAc,SAAS,KAAK,WAAW,4BAA4B,KAAK;GACjG,OAAO;IAAE;IAAQ;IAAK;IAAa;GAAa;EAClD,EAAA,CAAG;EACH,MAAM,CAAC,eAAe,WAAW,gBAAgB,MAAM,QAAQ,WAAW;GAAC;GAAc;GAAU;EAAW,CAAC;EAC/G,IAAI,cAAc,WAAW,YAAY,MAAM,cAAc;EAC7D,IAAI,UAAU,WAAW,YAAY,MAAM,UAAU;EACrD,IAAI,aAAa,WAAW,YAAY,MAAM,aAAa;EAC3D,MAAM,SAAS,cAAc;EAC7B,MAAM,MAAM,UAAU;EACtB,MAAM,EAAE,QAAQ,KAAK,aAAa,iBAAiB,aAAa;EAChE,MAAM,iBAAiBA,UAAAA,QAAK,KAAK,cAAcwD,oCAAAA,uBAAuB;EACtE,MAAM,oBAAoBxD,UAAAA,QAAK,KAAK,WAAW,OAAOyD,iCAAAA,oBAAoB;EAC1E,MAAM,aAAwB;GAC5B,GAAG,OAAO;GACV,cAAc;IACZ;IACA;IACA,mBAAmB,OAAO;IAC1B,aAAaC,gBAAAA,yBAAyB,OAAO,MAAM,QAAQ;GAC7D;GACA,WAAW;IACT,gBAAgB;IAChB;IACA,mBAAmB,IAAI;IACvB,aAAaC,gBAAAA,sBAAsB,OAAO,MAAM,QAAQ;GAC1D;EACF;EACA,MAAM,YAAY,MAAMC,gBAAAA,eACtB,SAAS,MACT,YACA,eACA5D,UAAAA,QAAK,KAAK,WAAW,YAAY,CACnC;EACA,MAAM,uBACH,eAAe,gBAAgB,iBAAiB,cAC7C6D,wBAAAA,uBAAuB,SAAS,MAAM,aAAa,IACnD,KAAA;EACN,MAAM,SAAqB;GACzB;GACA,GAAI,sBAAsB,EAAE,oBAAoB,IAAI,CAAC;GACrD;GACA,YAAY,OAAO,MAAM,SAAS,gBAAgBC,gBAAAA,mBAAmB,OAAO,MAAM,SAAS,aAAa,IAAI,CAAC;GAC7G,YAAY,QAAQ,UAAU;GAC9B,YAAY,QAAQ,UAAU;EAChC;EACA,CAAA,GAAA,yCAAA,wBAAA,CACE,SAAS,MACT;GACE,SAAS3B,yCAAAA;GACT,MAAM,SAAS;GACf,UAAU,SAAS;GACnB;GACA,gBAAgB;GAChB;GACA,cAAA,GAAa4B,yCAAAA,gBAAAA,CAAgB,SAAS;GACtC,cAAc,IAAI,WAAW,YAAY,IAAI,YAAY;GACzD;GACA,cAAc;IAAE,MAAM;IAAgB;IAAa,SAAA,GAAQA,yCAAAA,gBAAAA,CAAgB,cAAc;GAAE;GAC3F,WAAW;IAAE,MAAM;IAAmB;IAAa,SAAA,GAAQA,yCAAAA,gBAAAA,CAAgB,iBAAiB;GAAE;GAC9F,SAAS,uBAAuB;EAClC,GACA,aACF;EAGA,OAAO;CACT,SAAS,OAAO;EACd,MAAMzC,QAAAA,QAAG,SAAS,GAAG,WAAW;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;EAChE,MAAM;CACR;AACF;;AAGA,IAAa,cAAb,MAAyB;CACvB,OAAgB;CAChB;CACA;CACA;CAEA,YAAY,UAA8B,CAAC,GAAG;EAC5C,KAAK,eAAe,QAAQ,gBAAgB;EAC5C,KAAK,gBAAgB,QAAQ;EAC7B,KAAK,WAAW,QAAQ,YAAY;CACtC;CAEA,QAAQ,MAAyB;EAC/B,OAAO,KAAK,OAAO,KAAK;CAC1B;CAEA,MAAM,QACJ,MACA,cAAiC,QAAQ,KACzC,mBAAmB,QAAQ,IAAI,GAC/B,SAAqB,QAAQ,QACZ;EACjB,OAAO,YAAY,MAAM,aAAa,kBAAkB,QAAQ;GAC9D,cAAc,KAAK;GACnB,eAAe,KAAK;GACpB,UAAU,KAAK;EACjB,CAAC;CACH;AACF"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["path","os","globalDoomConfigDirectory","resolveDoomConfigurationRoot","HARNESS_STATE_POINTER","DOOMPI_MAJOR_MODE_ENV","DOOMPI_DOMAINS_ENV","DOOMPI_PROFILE_ENV","loadDoomConfigLenient","loadMajorModesConfig","createLayerResolvers","resolveExtensionComposition","PERSONA_ENTRY","filterHookDisabledLayers","resolveLayers","piThemeDirectory","DEFAULT_THEME_NAME","readPiSettings","mergePiSettings","serializePiSettings","piExtensionAliasIsCurrent","DEFAULT_THEME","fs","syncStateRootMatches","computeInputsHash","recordResolvedEntries","readBootstrapStatus","piAgentDirectory","projectRegistersDoom","DUPLICATE_REGISTRATION_DRIFT","readSyncDrift","spawnSync","doomPiPackageRoot","DOOMPI_API_VERSION","readSyncRegistration","SYNC_REGISTRATION_VERSION","loadMajorModesConfigLenient","loadDomains","parseHarnessArgs","piExtensionDispatcherIsUpgradeable","missingLayerPackageSpecifiers","readLocatedSyncState","SyncProgress","acquireSyncLocationLock","resolveSyncLocation","crypto","syncGenerationDirectory","buildHarnessContext","ensureLayerPackages","SYNC_STATE_VERSION","computeWebSourcesHash","loadHarnessState","buildSyncedRuntime","syncWebBundle","syncServerBundle","syncMcpBundle","DOOM_SERVER_BUNDLE_FILE","DOOM_MCP_BUNDLE_FILE","computeServerSourcesHash","computeMcpSourcesHash","writeSyncState","writeProjectPiSettings","readMcpServerNames","syncStateSha256"],"sources":["../../../../src/cli/commands/sync/index.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { globalDoomConfigDirectory } from '@agimon-ai/doompi-config/config';\nimport { loadDomains } from '@agimon-ai/doompi-config/domains';\nimport { filterHookDisabledLayers, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport { loadMajorModesConfig, loadMajorModesConfigLenient } from '@agimon-ai/doompi-config/majorModes';\nimport type { ConfigDiagnostic } from '@agimon-ai/doompi-config/types';\nimport { DOOM_MCP_BUNDLE_FILE } from '@agimon-ai/doompi-core/mcp-facet';\nimport {\n mergePiSettings,\n piAgentDirectory,\n piThemeDirectory,\n readPiSettings,\n serializePiSettings,\n} from '@agimon-ai/doompi-core/runtime-pi-settings';\nimport { DOOM_SERVER_BUNDLE_FILE } from '@agimon-ai/doompi-core/server-facet';\nimport {\n acquireSyncLocationLock,\n resolveSyncLocation,\n syncGenerationDirectory,\n} from '@agimon-ai/doompi-core/sync-location';\nimport {\n DOOMPI_API_VERSION,\n publishSyncRegistration,\n readSyncRegistration,\n SYNC_REGISTRATION_VERSION,\n syncStateSha256,\n type SyncPackageRegistration,\n} from '@agimon-ai/doompi-core/sync-registration';\nimport { DEFAULT_THEME, DEFAULT_THEME_NAME } from '@agimon-ai/doompi-ui/theme';\n\nimport { buildSyncedRuntime } from '../../../builders/cli';\nimport { readBootstrapStatus } from '../../../builders/cli/bootstrapLocator';\nimport {\n createLayerResolvers,\n type ExtensionComposition,\n PERSONA_ENTRY,\n resolveExtensionComposition,\n} from '../../../builders/cli/extensionAssembler';\nimport { buildHarnessContext } from '../../../builders/cli/harnessContext';\nimport {\n doomPiPackageRoot,\n piExtensionAliasIsCurrent,\n writePiExtensionAlias,\n} from '../../../builders/cli/piExtensionAlias';\nimport { piExtensionDispatcherIsUpgradeable } from '../../../builders/cli/piExtensionDispatcher';\nimport {\n DUPLICATE_REGISTRATION_DRIFT,\n projectRegistersDoom,\n writeProjectPiSettings,\n} from '../../../builders/cli/projectSettings';\nimport { syncServerBundle } from '../../../builders/server';\nimport { syncMcpBundle } from '../../../builders/server/mcpBundle';\nimport { syncWebBundle } from '../../../builders/web';\nimport { HARNESS_STATE_POINTER, loadHarnessState } from '../../../composition/harnessState';\nimport { ensureLayerPackages, missingLayerPackageSpecifiers } from '../../../composition/layerPackageInstaller';\nimport { loadDoomConfigLenient } from '../../../composition/projectTrust';\nimport { resolveDoomConfigurationRoot } from '../../../composition/repository';\nimport { readSyncDrift, type SyncDriftReason } from '../../../composition/syncDrift';\nimport {\n computeInputsHash,\n computeMcpSourcesHash,\n computeServerSourcesHash,\n computeWebSourcesHash,\n readLocatedSyncState,\n readMcpServerNames,\n recordResolvedEntries,\n SYNC_STATE_VERSION,\n type SyncSelection,\n type SyncState,\n syncStateRootMatches,\n writeSyncState,\n} from '../../../composition/syncState';\nimport type { HarnessOptions } from '../../../composition/types/harness';\nimport { DOOMPI_DOMAINS_ENV, DOOMPI_MAJOR_MODE_ENV, DOOMPI_PROFILE_ENV } from '../../matrixOptions';\nimport { parseHarnessArgs } from '../../options';\nimport { SyncProgress, type SyncProgressOutput } from './presenter';\n\n/**\n * `doom-pi sync`: resolve the matrix once and write it where plain Pi finds it.\n *\n * The doom-emacs split. Everything that needs a real Node process (module\n * resolution, staging skills and agents, generating the MCP config) happens\n * here, and the doom-pi extension then only reads what this produced. The\n * launcher is untouched and keeps resolving the same matrix per run.\n */\n\nconst SYNC_COMMAND = 'sync';\nconst CHECK_OPTION = '--check';\n/** Republishes even when nothing drifted, for a generation suspected of being damaged. */\nconst FORCE_OPTION = '--force';\nconst GLOBAL_OPTION = '--global';\nconst HARNESS_ROOT_ENV = 'DOOMPI_ROOT';\nconst PERSONA_FILE_ENV = 'DOOMPI_PERSONA_FILE';\nconst HOOK_EMITTER = path.join('tools', 'harness', 'emit-hooks.mjs');\nconst NONE = '(none)';\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst SYNC_LABEL = 'sync';\nconst RUNTIME_LABEL = 'runtime';\nconst WEB_LABEL = 'web';\nconst API_LABEL = 'api';\n\nexport interface SyncRoots {\n globalOnly: boolean;\n globalRoot: string;\n sourceRoot: string;\n targetRoot: string;\n}\n\n/** Resolves the configuration source and publication destination for one sync. */\nexport function resolveSyncRoots(\n args: readonly string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n homeDirectory = environment.HOME ?? os.homedir(),\n): SyncRoots {\n const globalRoot = globalDoomConfigDirectory(homeDirectory);\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const sourceRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n const globalOnly = args.includes(GLOBAL_OPTION);\n return { globalOnly, globalRoot, sourceRoot, targetRoot: globalOnly ? globalRoot : sourceRoot };\n}\n\nconst GLOBAL_SCOPE_INHERITED_KEYS = [\n HARNESS_ROOT_ENV,\n HARNESS_STATE_POINTER,\n DOOMPI_MAJOR_MODE_ENV,\n DOOMPI_DOMAINS_ENV,\n DOOMPI_PROFILE_ENV,\n] as const;\n\n/** Removes workspace-only state before a global runtime is resolved. */\nexport function environmentForSyncScope(environment: NodeJS.ProcessEnv, globalOnly: boolean): NodeJS.ProcessEnv {\n if (!globalOnly) return environment;\n const scoped = { ...environment };\n for (const key of GLOBAL_SCOPE_INHERITED_KEYS) delete scoped[key];\n return scoped;\n}\n\n/**\n * Harness variables worth recording, by prefix or exact name.\n *\n * An allowlist rather than the whole environment: the state file is a snapshot\n * of resolved configuration, and dumping `process.env` into it would write\n * every credential the sync happened to run with onto disk.\n */\nconst RECORDED_PREFIXES = ['DOOMPI_'];\nconst RECORDED_KEYS = ['CLAUDE_PROJECT_DIR', 'CODEX_REPO_ROOT', 'ORIGINAL_REPO_PATH', 'MCP_UI_VIEWER'];\n/**\n * Launcher-only values a synced session must not inherit.\n *\n * The child extension list is recomposed on every load, and the subagent binary\n * points at `pi.sh`, which a session started as plain `pi` should not shell out\n * to: Doom Team resolves Pi's own CLI when the variable is absent.\n */\nconst EXCLUDED_KEYS = new Set([\n 'DOOMPI_CHILD_EXTENSIONS',\n 'DOOMPI_COMPOSED',\n 'DOOMPI_MUTE',\n 'DOOMPI_TEMP_DIR',\n // A pointer to the syncing process's own state file. Recording it would hand\n // every later session a path to a state that died with this one.\n HARNESS_STATE_POINTER,\n 'PI_SUBAGENT_PI_BINARY',\n]);\n\ntype SyncOutput = SyncProgressOutput;\n\nexport type SyncSettingsMode = 'persisted' | 'embedded';\n\nexport interface SyncCommandOptions {\n settingsMode?: SyncSettingsMode;\n /** Test/embedding override; normal CLI execution uses the process home. */\n homeDirectory?: string;\n /** Internal pipeline seam when the caller owns the worktree lock. */\n lockHeld?: boolean;\n}\n\nexport interface SyncResult {\n statePath: string;\n /** Omitted when DPI supplies the integration as a process-local overlay. */\n settingsPath?: string;\n /** Set only when the repository still carried its own DoomPi registration. */\n projectSettingsPath?: string;\n selection: SyncSelection;\n mcpServers: string[];\n skillCount: number;\n agentCount: number;\n}\n\nexport function recordedEnvironment(environment: NodeJS.ProcessEnv): Record<string, string> {\n const recorded: Record<string, string> = {};\n for (const [key, value] of Object.entries(environment)) {\n if (value === undefined || EXCLUDED_KEYS.has(key)) continue;\n if (RECORDED_KEYS.includes(key) || RECORDED_PREFIXES.some((prefix) => key.startsWith(prefix))) {\n recorded[key] = value;\n }\n }\n return recorded;\n}\n\n/**\n * Reports the config keys sync chose to ignore.\n *\n * Never fatal. A key nobody recognises is usually a config written for another\n * version of a layer, and refusing to build over it is worse than proceeding\n * without it. The strict check lives in `doompi doctor`.\n */\nfunction writeConfigDiagnostics(diagnostics: readonly ConfigDiagnostic[], output: SyncOutput): void {\n if (diagnostics.length === 0) return;\n const lines = diagnostics.map((entry) => ` ${entry.filePath}: ${entry.path}`).join('\\n');\n output.write(\n `config: ignored ${String(diagnostics.length)} unsupported key(s); run doompi doctor for the strict check\\n${lines}\\n`,\n );\n}\n/**\n * Layers the repository's declared selection under the usual resolution.\n *\n * `.doom/config.yaml` holds what the repository selects by default, the way\n * init.el does for doom-emacs. Seeding the environment the parser reads keeps\n * the precedence the launcher already documents: an explicit flag wins, then an\n * exported variable, then the declared default.\n */\nexport function selectionEnvironment(\n repoRoot: string,\n environment: NodeJS.ProcessEnv,\n homeDirectory?: string,\n): NodeJS.ProcessEnv {\n const { selection } = loadDoomConfigLenient(repoRoot, homeDirectory).config;\n if (!selection) return environment;\n return {\n ...environment,\n ...(selection.majorMode && !environment[DOOMPI_MAJOR_MODE_ENV]\n ? { [DOOMPI_MAJOR_MODE_ENV]: selection.majorMode }\n : {}),\n ...(selection.profile && !environment[DOOMPI_PROFILE_ENV] ? { [DOOMPI_PROFILE_ENV]: selection.profile } : {}),\n ...(selection.domains && environment[DOOMPI_DOMAINS_ENV] === undefined\n ? { [DOOMPI_DOMAINS_ENV]: selection.domains.join(',') }\n : {}),\n };\n}\n\nexport function toSelection(\n options: Pick<HarnessOptions, 'majorMode' | 'domains' | 'profile' | 'preset'>,\n): SyncSelection {\n return {\n majorMode: options.majorMode,\n domains: options.domains,\n profile: options.profile,\n preset: options.preset,\n };\n}\n\nexport function selectionCompositionFingerprint(\n repoRoot: string,\n options: Pick<HarnessOptions, 'agents' | 'hooks' | 'majorMode' | 'mcp' | 'preset'>,\n homeDirectory: string = os.homedir(),\n): string {\n const majorModesConfig = loadMajorModesConfig(repoRoot, homeDirectory);\n const resolvers = createLayerResolvers(repoRoot);\n return resolveExtensionComposition({\n agents: options.agents,\n autoStop: false,\n mute: false,\n preset: options.preset,\n personaEntry: resolvers.packageEntry(PERSONA_ENTRY),\n majorMode: options.majorMode,\n layers: filterHookDisabledLayers(\n majorModesConfig,\n resolveLayers(majorModesConfig, options.majorMode),\n options.hooks,\n ),\n majorModesConfig,\n resolvers,\n }).fingerprint;\n}\n\n/** Settings, dispatcher and theme differences an init would fix, independent of sync state. */\nfunction piIntegrationDrift(agentDirectory: string): string[] {\n const drift: string[] = [];\n const themePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n const settings = readPiSettings(agentDirectory);\n const merged = mergePiSettings(settings, agentDirectory, { themePath, themeName: DEFAULT_THEME_NAME });\n if (serializePiSettings(merged) !== serializePiSettings(settings)) {\n drift.push('Pi user settings are out of date; run doompi init');\n }\n if (!piExtensionAliasIsCurrent(agentDirectory)) drift.push('Pi user dispatcher is out of date; run doompi init');\n const expectedTheme = `${JSON.stringify(DEFAULT_THEME, null, 2)}\\n`;\n if (!fs.existsSync(themePath) || fs.readFileSync(themePath, 'utf8') !== expectedTheme) {\n drift.push('Pi user theme is out of date; run doompi init');\n }\n return drift;\n}\n\n/** Differences between what a sync would produce and what is on disk. */\nexport function collectDrift(\n repoRoot: string,\n selection: SyncSelection,\n state: SyncState | undefined,\n environment: NodeJS.ProcessEnv = process.env,\n settingsMode: SyncSettingsMode = 'persisted',\n expectedCompositionFingerprint?: string,\n): string[] {\n if (!state) return ['no sync state: run doompi sync'];\n const drift: string[] = [];\n if (syncRegistrationNeedsApiMigration(repoRoot, environment.HOME ?? os.homedir())) {\n drift.push('DoomPi registration needs API migration');\n }\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 const sharedDrift = readSyncDrift({ repoRoot, homeDirectory: environment.HOME ?? os.homedir() });\n const sharedMessages: Partial<Record<SyncDriftReason, string>> = {\n 'never-synced': 'sync registration is missing or invalid',\n 'code-changed': 'cockpit sources changed since the last sync',\n 'cockpit-bundle-missing': 'cockpit bundle is missing',\n 'package-apis-missing': 'package API routes are missing',\n 'server-bundle-stale': 'server bundle is missing or stale',\n 'mcp-bundle-stale': 'MCP bundle is missing or stale',\n };\n for (const reason of sharedDrift.reasons) {\n const message = sharedMessages[reason];\n if (message) drift.push(message);\n }\n return drift;\n}\n\n/** Regenerates the hook files the other frontends read before any harness code runs. */\nfunction emitFrontendHooks(repoRoot: string, output: SyncOutput): void {\n const emitter = path.join(repoRoot, HOOK_EMITTER);\n if (!fs.existsSync(emitter)) return;\n const result = spawnSync(process.execPath, [emitter, '--write'], { cwd: repoRoot, encoding: 'utf8' });\n if (result.status === 0) {\n output.write('hooks: regenerated for Claude Code and Codex\\n');\n return;\n }\n output.write(`hooks: emit-hooks failed (${result.stderr?.trim() || `exit ${String(result.status)}`})\\n`);\n}\n\nexport function formatSyncResult(result: SyncResult, runner = 'pi'): string {\n const { selection } = result;\n return [\n `mode: ${selection.majorMode}`,\n `domains: ${selection.domains.join(', ') || NONE}`,\n `profile: ${selection.profile ?? NONE}`,\n `skills: ${result.skillCount}`,\n `agents: ${result.agentCount}`,\n `mcp: ${result.mcpServers.join(', ') || NONE}`,\n `state: ${result.statePath}`,\n ...(result.settingsPath ? [`settings: ${result.settingsPath}`] : []),\n ...(result.projectSettingsPath\n ? [`project: removed duplicate registration from ${result.projectSettingsPath}`]\n : []),\n '',\n `Run ${runner} from the repository root to use it.`,\n '',\n ].join('\\n');\n}\n\n/**\n * The DoomPi that produced this generation, which is the one that can load it.\n *\n * Always the executing package, never another copy the repository happens to\n * install. A generation is not portable between two installations: the bundles\n * are compiled from the building package's own extension entries, the recorded\n * compiler inputs are its files, and the state names its bootstrap entry. Naming\n * a second copy here publishes a registration whose package disagrees with the\n * state it points at, and Pi's dispatcher then loads a harness that rejects the\n * bootstrap as stale on every session, with no sync able to fix it.\n *\n * A repository that wants its own copy to own its sessions runs sync with that\n * copy's CLI, which makes it the executing package.\n */\nfunction packageRegistrationFor(): SyncPackageRegistration {\n const root = fs.realpathSync(doomPiPackageRoot());\n const manifestPath = path.join(root, 'package.json');\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as {\n version?: unknown;\n doompiApiVersion?: unknown;\n pi?: { extensions?: unknown };\n };\n const version = manifest.version;\n const apiVersion = manifest.doompiApiVersion;\n const extensions = manifest.pi?.extensions;\n const extension = Array.isArray(extensions) ? extensions.find((value) => typeof value === 'string') : undefined;\n if (\n typeof version !== 'string' ||\n typeof apiVersion !== 'number' ||\n !Number.isSafeInteger(apiVersion) ||\n apiVersion < 1 ||\n apiVersion !== DOOMPI_API_VERSION ||\n typeof extension !== 'string'\n ) {\n throw new Error(`Installed DoomPi package at ${root} has no supported API-versioned Pi extension entry`);\n }\n return {\n root,\n version,\n apiVersion: apiVersion as number,\n manifestPath,\n entry: fs.realpathSync(path.resolve(root, extension)),\n };\n}\n\n/** Returns true when a valid legacy registration must be republished with API metadata. */\nexport function syncRegistrationNeedsApiMigration(repoRoot: string, homeDirectory: string): boolean {\n try {\n const registration = readSyncRegistration(repoRoot, homeDirectory);\n return (\n registration !== undefined &&\n (registration.version !== SYNC_REGISTRATION_VERSION || registration.package.apiVersion === undefined)\n );\n } catch {\n return false;\n }\n}\n\n/** Resolves the matrix, stages it into home-scoped worktree storage, and publishes one generation. */\nexport async function synchronize(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n commandOptions: SyncCommandOptions = {},\n): Promise<number> {\n const check = args.includes(CHECK_OPTION);\n const force = args.includes(FORCE_OPTION);\n const rest = args.slice(1).filter((argument) => ![CHECK_OPTION, FORCE_OPTION, GLOBAL_OPTION].includes(argument));\n const homeDirectory = commandOptions.homeDirectory ?? environment.HOME ?? os.homedir();\n const roots = resolveSyncRoots(args, environment, currentDirectory, homeDirectory);\n const { globalOnly, globalRoot, targetRoot: repoRoot } = roots;\n const scopedEnvironment = environmentForSyncScope(environment, globalOnly);\n if (globalOnly && !check) fs.mkdirSync(globalRoot, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n // Sync tolerates keys it does not recognise so a config written against a\n // different version cannot break a build. `doompi doctor` reports them.\n const modes = loadMajorModesConfigLenient(repoRoot, homeDirectory);\n const configDiagnostics = [...loadDoomConfigLenient(repoRoot, homeDirectory).diagnostics, ...modes.diagnostics];\n const defaultMajorMode = modes.config.defaultMajorMode;\n const defaultDomains = loadDomains(repoRoot, homeDirectory).defaultDomains;\n const parsed = parseHarnessArgs(\n rest,\n selectionEnvironment(repoRoot, scopedEnvironment, homeDirectory),\n globalOnly ? globalRoot : currentDirectory,\n defaultMajorMode,\n defaultDomains,\n );\n const selection = toSelection(parsed.options);\n const agentDirectory = piAgentDirectory(scopedEnvironment, homeDirectory);\n if ((commandOptions.settingsMode ?? 'persisted') === 'persisted' && !check) {\n if (piExtensionDispatcherIsUpgradeable(agentDirectory)) {\n writePiExtensionAlias(agentDirectory);\n output.write('repair: refreshed Pi user dispatcher\\n');\n }\n const drift = piIntegrationDrift(agentDirectory);\n if (drift.length > 0) {\n throw new Error(`DoomPi Pi integration is not ready:\\n${drift.map((entry) => ` ${entry}`).join('\\n')}`);\n }\n }\n writeConfigDiagnostics(configDiagnostics, output);\n if (check) {\n const majorModesConfig = modes.config;\n const missingPackages = missingLayerPackageSpecifiers(\n majorModesConfig,\n Object.keys(majorModesConfig.layers),\n createLayerResolvers(repoRoot),\n );\n if (missingPackages.length > 0) {\n output.write(\n `doompi sync is out of date:\\n${missingPackages\n .map((specifier) => ` configured package is not installed: ${specifier}`)\n .join('\\n')}\\n`,\n );\n return 1;\n }\n let located: ReturnType<typeof readLocatedSyncState>;\n try {\n located = readLocatedSyncState(repoRoot, homeDirectory);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n output.write(`doompi sync is out of date:\\n ${detail}\\n`);\n return 1;\n }\n const expectedCompositionFingerprint = selectionCompositionFingerprint(repoRoot, parsed.options, homeDirectory);\n const drift = collectDrift(\n repoRoot,\n selection,\n located?.state,\n scopedEnvironment,\n commandOptions.settingsMode ?? 'persisted',\n expectedCompositionFingerprint,\n );\n if (drift.length === 0) {\n output.write('doompi sync is up to date\\n');\n return 0;\n }\n output.write(`doompi sync is out of date:\\n${drift.map((entry) => ` ${entry}`).join('\\n')}\\n`);\n return 1;\n }\n\n // Publishing an identical generation is not a no-op: it moves the\n // registration, so every attached cockpit reloads and the previous\n // generation becomes garbage. Same inputs, same published result.\n const driftOptions = {\n repoRoot,\n homeDirectory,\n requireWebBundle: Boolean(environment.DOOMPI_WEB_PACKAGE_ROOT),\n };\n const registrationNeedsMigration = syncRegistrationNeedsApiMigration(repoRoot, homeDirectory);\n if (!force && !registrationNeedsMigration && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n\n const progress = new SyncProgress(output);\n const releaseLock = commandOptions.lockHeld\n ? undefined\n : await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n let result: SyncResult;\n try {\n // A concurrent publisher may have resolved the drift while this command\n // waited for the lock. Avoid moving the registration for no change.\n const registrationNeedsMigration = syncRegistrationNeedsApiMigration(repoRoot, homeDirectory);\n if (!force && !registrationNeedsMigration && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n result = await stageSync(repoRoot, parsed.options, scopedEnvironment, homeDirectory, progress, commandOptions);\n } finally {\n await releaseLock?.();\n }\n emitFrontendHooks(repoRoot, output);\n output.write(formatSyncResult(result, (commandOptions.settingsMode ?? 'persisted') === 'embedded' ? 'dpi' : 'pi'));\n return 0;\n}\n\nasync function stageSync(\n repoRoot: string,\n options: Omit<HarnessOptions, 'repoRoot'>,\n environment: NodeJS.ProcessEnv,\n homeDirectory: string,\n progress: SyncProgress,\n commandOptions: SyncCommandOptions = {},\n): Promise<SyncResult> {\n const location = resolveSyncLocation(repoRoot, homeDirectory);\n const generation = `${Date.now().toString(36)}-${crypto.randomUUID()}`;\n const directory = syncGenerationDirectory(location, generation);\n await fs.promises.mkdir(location.generationsDirectory, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n // The leaf is created without `recursive`, so an existing path is an error\n // rather than something to adopt: the cockpit signs and serves whatever the\n // published generation holds, and sync must only ever publish bytes it\n // wrote itself into a directory it just created.\n await fs.promises.mkdir(directory, { mode: PRIVATE_DIRECTORY_MODE });\n\n try {\n const staged = progress.start(SYNC_LABEL, 'resolving the matrix and staging resources');\n const context = await buildHarnessContext({\n ...options,\n repoRoot: location.root,\n homeDirectory,\n cwd: location.root,\n resourceDirectory: directory,\n });\n await ensureLayerPackages({\n repoRoot: location.root,\n config: context.majorModesConfig,\n layers: Object.keys(context.majorModesConfig.layers),\n environment,\n });\n staged(`${String(context.resources.skillCount)} skills, ${String(context.resources.agentCount)} agents`);\n const selection = toSelection(options);\n const resolvers = createLayerResolvers(location.root);\n const resolved = recordResolvedEntries(context.majorModesConfig, resolvers);\n const compositionFingerprint = selectionCompositionFingerprint(location.root, options, homeDirectory);\n const agentDirectory = piAgentDirectory(environment, homeDirectory);\n const persistedThemePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n const themePath =\n (commandOptions.settingsMode ?? 'persisted') === 'persisted' ? persistedThemePath : context.defaultThemePath;\n const state: SyncState = {\n version: SYNC_STATE_VERSION,\n root: location.root,\n identity: location.identity,\n inputsHash: computeInputsHash(location.root, selection, homeDirectory),\n webSourcesHash: computeWebSourcesHash(resolved),\n compositionFingerprint,\n selection,\n env: recordedEnvironment(context.environment),\n fileState: {\n profileEnvironment: loadHarnessState(context.environment).state.profileEnvironment,\n pluginHooks: context.resources.pluginHooks,\n mcpProjection: context.resources.mcpProjection,\n },\n resolved,\n baseline: {\n mcpConfigPath: context.resources.mcpConfigPath,\n personaFile: context.environment[PERSONA_FILE_ENV],\n themePath,\n themeName: DEFAULT_THEME_NAME,\n },\n };\n\n // Runtime compilation writes the package dist files consumed by both the web\n // and server bundlers. Finish it first so a package clean cannot race either\n // consumer, then run the independent web and server builds together.\n let resolveCompositions!: (compositions: readonly ExtensionComposition[]) => void;\n let rejectCompositions!: (reason?: unknown) => void;\n const compositionsReady = new Promise<readonly ExtensionComposition[]>((resolve, reject) => {\n resolveCompositions = resolve;\n rejectCompositions = reject;\n });\n const runtimeProgress = progress.start(RUNTIME_LABEL, 'precompiling the mode bundles');\n const runtimeBuild = buildSyncedRuntime(location.root, environment, homeDirectory, {\n state,\n directory,\n onCompositionsResolved: resolveCompositions,\n }).then((synced) => {\n runtimeProgress(`${String(Object.keys(synced.bundles).length)} mode bundles`);\n return synced;\n });\n void runtimeBuild.catch(rejectCompositions);\n const webBuild = (async () => {\n await runtimeBuild;\n const webProgress = progress.start(WEB_LABEL, 'bundling the web cockpit plugins');\n const web = await syncWebBundle({\n repoRoot: location.root,\n resolvedEntries: state.resolved,\n environment,\n outputDirectory: path.join(directory, 'web-bundle'),\n onNotice: (message) => progress.line(WEB_LABEL, message),\n });\n if (web.status === 'failed') throw new Error(`Cockpit bundle failed: ${web.reason}`);\n webProgress(web.status === 'bundled' ? `cockpit bundled with plugins: ${web.pluginIds.join(', ')}` : web.reason);\n return web;\n })();\n const serverBuild = (async () => {\n const compositions = await compositionsReady;\n await runtimeBuild;\n const apiProgress = progress.start(API_LABEL, 'compiling the server bundle');\n const apiDirectory = path.join(directory, 'api');\n const fingerprint = crypto\n .createHash('sha256')\n .update(JSON.stringify([...new Set(compositions.map((composition) => composition.fingerprint))]))\n .digest('hex');\n const [server, mcp] = await Promise.all([\n syncServerBundle({\n repositoryRoot: location.root,\n generation,\n fingerprint,\n compositions,\n outputDirectory: apiDirectory,\n cacheDirectory: path.join(directory, 'cache'),\n sharedCacheDirectory: location.sharedCacheDirectory,\n }),\n syncMcpBundle({\n repositoryRoot: location.root,\n generation,\n fingerprint,\n compositions,\n outputDirectory: path.join(directory, 'mcp'),\n cacheDirectory: path.join(directory, 'cache'),\n sharedCacheDirectory: location.sharedCacheDirectory,\n }),\n ]);\n apiProgress(\n `${server.descriptor.entries.length} server facet(s), ${mcp.descriptor.entries.length} MCP plugin(s) compiled`,\n );\n for (const gap of server.contractGaps) progress.line(API_LABEL, `API contract incomplete: ${gap}`);\n return { server, mcp, fingerprint, apiDirectory };\n })();\n const [runtimeResult, webResult, serverResult] = await Promise.allSettled([runtimeBuild, webBuild, serverBuild]);\n if (runtimeResult.status === 'rejected') throw runtimeResult.reason;\n if (webResult.status === 'rejected') throw webResult.reason;\n if (serverResult.status === 'rejected') throw serverResult.reason;\n const synced = runtimeResult.value;\n const web = webResult.value;\n const { server, mcp, fingerprint, apiDirectory } = serverResult.value;\n const descriptorPath = path.join(apiDirectory, DOOM_SERVER_BUNDLE_FILE);\n const mcpDescriptorPath = path.join(directory, 'mcp', DOOM_MCP_BUNDLE_FILE);\n const finalState: SyncState = {\n ...synced.state,\n serverBundle: {\n descriptorPath,\n fingerprint,\n compilerManifests: server.compilerManifests,\n sourcesHash: computeServerSourcesHash(synced.state.resolved),\n },\n mcpBundle: {\n descriptorPath: mcpDescriptorPath,\n fingerprint,\n compilerManifests: mcp.compilerManifests,\n sourcesHash: computeMcpSourcesHash(synced.state.resolved),\n },\n };\n const statePath = await writeSyncState(\n location.root,\n finalState,\n homeDirectory,\n path.join(directory, 'state.json'),\n );\n const projectSettingsPath =\n (commandOptions.settingsMode ?? 'persisted') === 'persisted'\n ? writeProjectPiSettings(location.root, homeDirectory)\n : undefined;\n const result: SyncResult = {\n statePath,\n ...(projectSettingsPath ? { projectSettingsPath } : {}),\n selection,\n mcpServers: synced.state.baseline.mcpConfigPath ? readMcpServerNames(synced.state.baseline.mcpConfigPath) : [],\n skillCount: context.resources.skillCount,\n agentCount: context.resources.agentCount,\n };\n publishSyncRegistration(\n location.root,\n {\n version: SYNC_REGISTRATION_VERSION,\n root: location.root,\n identity: location.identity,\n generation,\n generationRoot: directory,\n statePath,\n stateSha256: syncStateSha256(statePath),\n webDirectory: web.status === 'bundled' ? web.assetsDir : null,\n apiDirectory,\n serverBundle: { path: descriptorPath, fingerprint, sha256: syncStateSha256(descriptorPath) },\n mcpBundle: { path: mcpDescriptorPath, fingerprint, sha256: syncStateSha256(mcpDescriptorPath) },\n package: packageRegistrationFor(),\n },\n homeDirectory,\n );\n // ponytail: retain generations until host-owned drain evidence can prove no session uses them.\n // Directory age and an open-file check cannot establish that a lazy import is finished.\n return result;\n } catch (error) {\n await fs.promises.rm(directory, { recursive: true, force: true });\n throw error;\n }\n}\n\n/** Compatibility API. Executables call the command function directly. */\nexport class SyncCommand {\n readonly name = SYNC_COMMAND;\n private readonly settingsMode: SyncSettingsMode;\n private readonly homeDirectory: string | undefined;\n private readonly lockHeld: boolean;\n\n constructor(options: SyncCommandOptions = {}) {\n this.settingsMode = options.settingsMode ?? 'persisted';\n this.homeDirectory = options.homeDirectory;\n this.lockHeld = options.lockHeld ?? false;\n }\n\n matches(args: string[]): boolean {\n return args[0] === this.name;\n }\n\n async execute(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n ): Promise<number> {\n return synchronize(args, environment, currentDirectory, output, {\n settingsMode: this.settingsMode,\n homeDirectory: this.homeDirectory,\n lockHeld: this.lockHeld,\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../../../../src/cli/commands/sync/index.ts"],"mappings":";;;;iBA0GiB;EACf;EACA;EACA;EACA;;;wBAIc,iBACd,yBACA,cAAa,OAAO,YACpB,2BACA,yBACC;;wBAmBa,wBAAwB,aAAa,OAAO,YAAY,sBAAsB,OAAO;KAkChG,aAAa;YAEN;iBAEK;EACf,eAAe;;EAEf;;EAEA;;iBAGe;EACf;;EAEA;;EAEA;EACA,WAAW;EACX;EACA;EACA;;wBAGc,oBAAoB,aAAa,OAAO,aAAa;;;;;;;;;wBAiCrD,qBACd,kBACA,aAAa,OAAO,YACpB,yBACC,OAAO;wBAeM,YACd,SAAS,KAAK,kEACb;wBASa,gCACd,kBACA,SAAS,KAAK,sEACd;;wBAuCc,aACd,kBACA,WAAW,eACX,OAAO,uBACP,cAAa,OAAO,YACpB,eAAc,kBACd;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../../../src/cli/commands/sync/index.ts"],"mappings":";;;;iBA0GiB;EACf;EACA;EACA;EACA;;;wBAIc,iBACd,yBACA,cAAa,OAAO,YACpB,2BACA,yBACC;;wBAmBa,wBAAwB,aAAa,OAAO,YAAY,sBAAsB,OAAO;KAkChG,aAAa;YAEN;iBAEK;EACf,eAAe;;EAEf;;EAEA;;iBAGe;EACf;;EAEA;;EAEA;EACA,WAAW;EACX;EACA;EACA;;wBAGc,oBAAoB,aAAa,OAAO,aAAa;;;;;;;;;wBAiCrD,qBACd,kBACA,aAAa,OAAO,YACpB,yBACC,OAAO;wBAeM,YACd,SAAS,KAAK,kEACb;wBASa,gCACd,kBACA,SAAS,KAAK,sEACd;;wBAuCc,aACd,kBACA,WAAW,eACX,OAAO,uBACP,cAAa,OAAO,YACpB,eAAc,kBACd;wBAkFc,iBAAiB,QAAQ,YAAY;;wBAkErC,kCAAkC,kBAAkB;;wBAa9C,YACpB,gBACA,cAAa,OAAO,YACpB,2BACA,SAAQ,YACR,iBAAgB,qBACf;;qBA0TU;WACF;mBACQ;mBACA;mBACA;EAEjB,YAAY,UAAS;EAMrB,QAAQ;EAIF,QACJ,gBACA,cAAa,OAAO,YACpB,2BACA,SAAQ,aACP"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../../src/cli/commands/sync/index.ts"],"mappings":";;;;iBA0GiB;EACf;EACA;EACA;EACA;;;wBAIc,iBACd,yBACA,cAAa,OAAO,YACpB,2BACA,yBACC;;wBAmBa,wBAAwB,aAAa,OAAO,YAAY,sBAAsB,OAAO;KAkChG,aAAa;YAEN;iBAEK;EACf,eAAe;;EAEf;;EAEA;;iBAGe;EACf;;EAEA;;EAEA;EACA,WAAW;EACX;EACA;EACA;;wBAGc,oBAAoB,aAAa,OAAO,aAAa;;;;;;;;;wBAiCrD,qBACd,kBACA,aAAa,OAAO,YACpB,yBACC,OAAO;wBAeM,YACd,SAAS,KAAK,kEACb;wBASa,gCACd,kBACA,SAAS,KAAK,sEACd;;wBAuCc,aACd,kBACA,WAAW,eACX,OAAO,uBACP,cAAa,OAAO,YACpB,eAAc,kBACd;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../../src/cli/commands/sync/index.ts"],"mappings":";;;;iBA0GiB;EACf;EACA;EACA;EACA;;;wBAIc,iBACd,yBACA,cAAa,OAAO,YACpB,2BACA,yBACC;;wBAmBa,wBAAwB,aAAa,OAAO,YAAY,sBAAsB,OAAO;KAkChG,aAAa;YAEN;iBAEK;EACf,eAAe;;EAEf;;EAEA;;iBAGe;EACf;;EAEA;;EAEA;EACA,WAAW;EACX;EACA;EACA;;wBAGc,oBAAoB,aAAa,OAAO,aAAa;;;;;;;;;wBAiCrD,qBACd,kBACA,aAAa,OAAO,YACpB,yBACC,OAAO;wBAeM,YACd,SAAS,KAAK,kEACb;wBASa,gCACd,kBACA,SAAS,KAAK,sEACd;;wBAuCc,aACd,kBACA,WAAW,eACX,OAAO,uBACP,cAAa,OAAO,YACpB,eAAc,kBACd;wBAkFc,iBAAiB,QAAQ,YAAY;;wBAkErC,kCAAkC,kBAAkB;;wBAa9C,YACpB,gBACA,cAAa,OAAO,YACpB,2BACA,SAAQ,YACR,iBAAgB,qBACf;;qBA0TU;WACF;mBACQ;mBACA;mBACA;EAEjB,YAAY,UAAS;EAMrB,QAAQ;EAIF,QACJ,gBACA,cAAa,OAAO,YACpB,2BACA,SAAQ,aACP"}
|
|
@@ -209,10 +209,22 @@ function collectDrift(repoRoot, selection, state, environment = process.env, set
|
|
|
209
209
|
if (projectRegistersDoom(repoRoot)) drift.push(DUPLICATE_REGISTRATION_DRIFT);
|
|
210
210
|
if (state.baseline.themePath !== themePath || state.baseline.themeName !== DEFAULT_THEME_NAME) drift.push("synced theme location is out of date");
|
|
211
211
|
}
|
|
212
|
-
|
|
212
|
+
const sharedDrift = readSyncDrift({
|
|
213
213
|
repoRoot,
|
|
214
214
|
homeDirectory: environment.HOME ?? os.homedir()
|
|
215
|
-
})
|
|
215
|
+
});
|
|
216
|
+
const sharedMessages = {
|
|
217
|
+
"never-synced": "sync registration is missing or invalid",
|
|
218
|
+
"code-changed": "cockpit sources changed since the last sync",
|
|
219
|
+
"cockpit-bundle-missing": "cockpit bundle is missing",
|
|
220
|
+
"package-apis-missing": "package API routes are missing",
|
|
221
|
+
"server-bundle-stale": "server bundle is missing or stale",
|
|
222
|
+
"mcp-bundle-stale": "MCP bundle is missing or stale"
|
|
223
|
+
};
|
|
224
|
+
for (const reason of sharedDrift.reasons) {
|
|
225
|
+
const message = sharedMessages[reason];
|
|
226
|
+
if (message) drift.push(message);
|
|
227
|
+
}
|
|
216
228
|
return drift;
|
|
217
229
|
}
|
|
218
230
|
/** Regenerates the hook files the other frontends read before any harness code runs. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["loadDoomConfigLenient"],"sources":["../../../../src/cli/commands/sync/index.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { globalDoomConfigDirectory } from '@agimon-ai/doompi-config/config';\nimport { loadDomains } from '@agimon-ai/doompi-config/domains';\nimport { filterHookDisabledLayers, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport { loadMajorModesConfig, loadMajorModesConfigLenient } from '@agimon-ai/doompi-config/majorModes';\nimport type { ConfigDiagnostic } from '@agimon-ai/doompi-config/types';\nimport { DOOM_MCP_BUNDLE_FILE } from '@agimon-ai/doompi-core/mcp-facet';\nimport {\n mergePiSettings,\n piAgentDirectory,\n piThemeDirectory,\n readPiSettings,\n serializePiSettings,\n} from '@agimon-ai/doompi-core/runtime-pi-settings';\nimport { DOOM_SERVER_BUNDLE_FILE } from '@agimon-ai/doompi-core/server-facet';\nimport {\n acquireSyncLocationLock,\n resolveSyncLocation,\n syncGenerationDirectory,\n} from '@agimon-ai/doompi-core/sync-location';\nimport {\n DOOMPI_API_VERSION,\n publishSyncRegistration,\n readSyncRegistration,\n SYNC_REGISTRATION_VERSION,\n syncStateSha256,\n type SyncPackageRegistration,\n} from '@agimon-ai/doompi-core/sync-registration';\nimport { DEFAULT_THEME, DEFAULT_THEME_NAME } from '@agimon-ai/doompi-ui/theme';\n\nimport { buildSyncedRuntime } from '../../../builders/cli';\nimport { readBootstrapStatus } from '../../../builders/cli/bootstrapLocator';\nimport {\n createLayerResolvers,\n type ExtensionComposition,\n PERSONA_ENTRY,\n resolveExtensionComposition,\n} from '../../../builders/cli/extensionAssembler';\nimport { buildHarnessContext } from '../../../builders/cli/harnessContext';\nimport {\n doomPiPackageRoot,\n piExtensionAliasIsCurrent,\n writePiExtensionAlias,\n} from '../../../builders/cli/piExtensionAlias';\nimport { piExtensionDispatcherIsUpgradeable } from '../../../builders/cli/piExtensionDispatcher';\nimport {\n DUPLICATE_REGISTRATION_DRIFT,\n projectRegistersDoom,\n writeProjectPiSettings,\n} from '../../../builders/cli/projectSettings';\nimport { syncServerBundle } from '../../../builders/server';\nimport { syncMcpBundle } from '../../../builders/server/mcpBundle';\nimport { syncWebBundle } from '../../../builders/web';\nimport { HARNESS_STATE_POINTER, loadHarnessState } from '../../../composition/harnessState';\nimport { ensureLayerPackages, missingLayerPackageSpecifiers } from '../../../composition/layerPackageInstaller';\nimport { loadDoomConfigLenient } from '../../../composition/projectTrust';\nimport { resolveDoomConfigurationRoot } from '../../../composition/repository';\nimport { readSyncDrift } from '../../../composition/syncDrift';\nimport {\n computeInputsHash,\n computeMcpSourcesHash,\n computeServerSourcesHash,\n computeWebSourcesHash,\n readLocatedSyncState,\n readMcpServerNames,\n recordResolvedEntries,\n SYNC_STATE_VERSION,\n type SyncSelection,\n type SyncState,\n syncStateRootMatches,\n writeSyncState,\n} from '../../../composition/syncState';\nimport type { HarnessOptions } from '../../../composition/types/harness';\nimport { DOOMPI_DOMAINS_ENV, DOOMPI_MAJOR_MODE_ENV, DOOMPI_PROFILE_ENV } from '../../matrixOptions';\nimport { parseHarnessArgs } from '../../options';\nimport { SyncProgress, type SyncProgressOutput } from './presenter';\n\n/**\n * `doom-pi sync`: resolve the matrix once and write it where plain Pi finds it.\n *\n * The doom-emacs split. Everything that needs a real Node process (module\n * resolution, staging skills and agents, generating the MCP config) happens\n * here, and the doom-pi extension then only reads what this produced. The\n * launcher is untouched and keeps resolving the same matrix per run.\n */\n\nconst SYNC_COMMAND = 'sync';\nconst CHECK_OPTION = '--check';\n/** Republishes even when nothing drifted, for a generation suspected of being damaged. */\nconst FORCE_OPTION = '--force';\nconst GLOBAL_OPTION = '--global';\nconst HARNESS_ROOT_ENV = 'DOOMPI_ROOT';\nconst PERSONA_FILE_ENV = 'DOOMPI_PERSONA_FILE';\nconst HOOK_EMITTER = path.join('tools', 'harness', 'emit-hooks.mjs');\nconst NONE = '(none)';\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst SYNC_LABEL = 'sync';\nconst RUNTIME_LABEL = 'runtime';\nconst WEB_LABEL = 'web';\nconst API_LABEL = 'api';\n\nexport interface SyncRoots {\n globalOnly: boolean;\n globalRoot: string;\n sourceRoot: string;\n targetRoot: string;\n}\n\n/** Resolves the configuration source and publication destination for one sync. */\nexport function resolveSyncRoots(\n args: readonly string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n homeDirectory = environment.HOME ?? os.homedir(),\n): SyncRoots {\n const globalRoot = globalDoomConfigDirectory(homeDirectory);\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const sourceRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n const globalOnly = args.includes(GLOBAL_OPTION);\n return { globalOnly, globalRoot, sourceRoot, targetRoot: globalOnly ? globalRoot : sourceRoot };\n}\n\nconst GLOBAL_SCOPE_INHERITED_KEYS = [\n HARNESS_ROOT_ENV,\n HARNESS_STATE_POINTER,\n DOOMPI_MAJOR_MODE_ENV,\n DOOMPI_DOMAINS_ENV,\n DOOMPI_PROFILE_ENV,\n] as const;\n\n/** Removes workspace-only state before a global runtime is resolved. */\nexport function environmentForSyncScope(environment: NodeJS.ProcessEnv, globalOnly: boolean): NodeJS.ProcessEnv {\n if (!globalOnly) return environment;\n const scoped = { ...environment };\n for (const key of GLOBAL_SCOPE_INHERITED_KEYS) delete scoped[key];\n return scoped;\n}\n\n/**\n * Harness variables worth recording, by prefix or exact name.\n *\n * An allowlist rather than the whole environment: the state file is a snapshot\n * of resolved configuration, and dumping `process.env` into it would write\n * every credential the sync happened to run with onto disk.\n */\nconst RECORDED_PREFIXES = ['DOOMPI_'];\nconst RECORDED_KEYS = ['CLAUDE_PROJECT_DIR', 'CODEX_REPO_ROOT', 'ORIGINAL_REPO_PATH', 'MCP_UI_VIEWER'];\n/**\n * Launcher-only values a synced session must not inherit.\n *\n * The child extension list is recomposed on every load, and the subagent binary\n * points at `pi.sh`, which a session started as plain `pi` should not shell out\n * to: Doom Team resolves Pi's own CLI when the variable is absent.\n */\nconst EXCLUDED_KEYS = new Set([\n 'DOOMPI_CHILD_EXTENSIONS',\n 'DOOMPI_COMPOSED',\n 'DOOMPI_MUTE',\n 'DOOMPI_TEMP_DIR',\n // A pointer to the syncing process's own state file. Recording it would hand\n // every later session a path to a state that died with this one.\n HARNESS_STATE_POINTER,\n 'PI_SUBAGENT_PI_BINARY',\n]);\n\ntype SyncOutput = SyncProgressOutput;\n\nexport type SyncSettingsMode = 'persisted' | 'embedded';\n\nexport interface SyncCommandOptions {\n settingsMode?: SyncSettingsMode;\n /** Test/embedding override; normal CLI execution uses the process home. */\n homeDirectory?: string;\n /** Internal pipeline seam when the caller owns the worktree lock. */\n lockHeld?: boolean;\n}\n\nexport interface SyncResult {\n statePath: string;\n /** Omitted when DPI supplies the integration as a process-local overlay. */\n settingsPath?: string;\n /** Set only when the repository still carried its own DoomPi registration. */\n projectSettingsPath?: string;\n selection: SyncSelection;\n mcpServers: string[];\n skillCount: number;\n agentCount: number;\n}\n\nexport function recordedEnvironment(environment: NodeJS.ProcessEnv): Record<string, string> {\n const recorded: Record<string, string> = {};\n for (const [key, value] of Object.entries(environment)) {\n if (value === undefined || EXCLUDED_KEYS.has(key)) continue;\n if (RECORDED_KEYS.includes(key) || RECORDED_PREFIXES.some((prefix) => key.startsWith(prefix))) {\n recorded[key] = value;\n }\n }\n return recorded;\n}\n\n/**\n * Reports the config keys sync chose to ignore.\n *\n * Never fatal. A key nobody recognises is usually a config written for another\n * version of a layer, and refusing to build over it is worse than proceeding\n * without it. The strict check lives in `doompi doctor`.\n */\nfunction writeConfigDiagnostics(diagnostics: readonly ConfigDiagnostic[], output: SyncOutput): void {\n if (diagnostics.length === 0) return;\n const lines = diagnostics.map((entry) => ` ${entry.filePath}: ${entry.path}`).join('\\n');\n output.write(\n `config: ignored ${String(diagnostics.length)} unsupported key(s); run doompi doctor for the strict check\\n${lines}\\n`,\n );\n}\n/**\n * Layers the repository's declared selection under the usual resolution.\n *\n * `.doom/config.yaml` holds what the repository selects by default, the way\n * init.el does for doom-emacs. Seeding the environment the parser reads keeps\n * the precedence the launcher already documents: an explicit flag wins, then an\n * exported variable, then the declared default.\n */\nexport function selectionEnvironment(\n repoRoot: string,\n environment: NodeJS.ProcessEnv,\n homeDirectory?: string,\n): NodeJS.ProcessEnv {\n const { selection } = loadDoomConfigLenient(repoRoot, homeDirectory).config;\n if (!selection) return environment;\n return {\n ...environment,\n ...(selection.majorMode && !environment[DOOMPI_MAJOR_MODE_ENV]\n ? { [DOOMPI_MAJOR_MODE_ENV]: selection.majorMode }\n : {}),\n ...(selection.profile && !environment[DOOMPI_PROFILE_ENV] ? { [DOOMPI_PROFILE_ENV]: selection.profile } : {}),\n ...(selection.domains && environment[DOOMPI_DOMAINS_ENV] === undefined\n ? { [DOOMPI_DOMAINS_ENV]: selection.domains.join(',') }\n : {}),\n };\n}\n\nexport function toSelection(\n options: Pick<HarnessOptions, 'majorMode' | 'domains' | 'profile' | 'preset'>,\n): SyncSelection {\n return {\n majorMode: options.majorMode,\n domains: options.domains,\n profile: options.profile,\n preset: options.preset,\n };\n}\n\nexport function selectionCompositionFingerprint(\n repoRoot: string,\n options: Pick<HarnessOptions, 'agents' | 'hooks' | 'majorMode' | 'mcp' | 'preset'>,\n homeDirectory: string = os.homedir(),\n): string {\n const majorModesConfig = loadMajorModesConfig(repoRoot, homeDirectory);\n const resolvers = createLayerResolvers(repoRoot);\n return resolveExtensionComposition({\n agents: options.agents,\n autoStop: false,\n mute: false,\n preset: options.preset,\n personaEntry: resolvers.packageEntry(PERSONA_ENTRY),\n majorMode: options.majorMode,\n layers: filterHookDisabledLayers(\n majorModesConfig,\n resolveLayers(majorModesConfig, options.majorMode),\n options.hooks,\n ),\n majorModesConfig,\n resolvers,\n }).fingerprint;\n}\n\n/** Settings, dispatcher and theme differences an init would fix, independent of sync state. */\nfunction piIntegrationDrift(agentDirectory: string): string[] {\n const drift: string[] = [];\n const themePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n const settings = readPiSettings(agentDirectory);\n const merged = mergePiSettings(settings, agentDirectory, { themePath, themeName: DEFAULT_THEME_NAME });\n if (serializePiSettings(merged) !== serializePiSettings(settings)) {\n drift.push('Pi user settings are out of date; run doompi init');\n }\n if (!piExtensionAliasIsCurrent(agentDirectory)) drift.push('Pi user dispatcher is out of date; run doompi init');\n const expectedTheme = `${JSON.stringify(DEFAULT_THEME, null, 2)}\\n`;\n if (!fs.existsSync(themePath) || fs.readFileSync(themePath, 'utf8') !== expectedTheme) {\n drift.push('Pi user theme is out of date; run doompi init');\n }\n return drift;\n}\n\n/** Differences between what a sync would produce and what is on disk. */\nexport function collectDrift(\n repoRoot: string,\n selection: SyncSelection,\n state: SyncState | undefined,\n environment: NodeJS.ProcessEnv = process.env,\n settingsMode: SyncSettingsMode = 'persisted',\n expectedCompositionFingerprint?: string,\n): string[] {\n if (!state) return ['no sync state: run doompi sync'];\n const drift: string[] = [];\n if (syncRegistrationNeedsApiMigration(repoRoot, environment.HOME ?? os.homedir())) {\n drift.push('DoomPi registration needs API migration');\n }\n if (!syncStateRootMatches(repoRoot, state.root)) drift.push('sync state belongs to a different repository');\n const recorded = state.selection;\n if (\n recorded.majorMode !== selection.majorMode ||\n recorded.profile !== selection.profile ||\n recorded.preset !== selection.preset ||\n recorded.domains.join(',') !== selection.domains.join(',')\n ) {\n drift.push('selection changed since the last sync');\n }\n // Hashed against the recorded selection, not the requested one, so a\n // selection change is reported once rather than as two findings.\n if (computeInputsHash(repoRoot, recorded, environment.HOME ?? os.homedir()) !== state.inputsHash) {\n drift.push('.doom configuration changed');\n }\n // Re-resolving is what catches a dependency upgrade moving a package, which\n // the inputs hash deliberately does not read.\n if (\n JSON.stringify(\n recordResolvedEntries(\n loadMajorModesConfig(repoRoot, environment.HOME ?? os.homedir()),\n createLayerResolvers(repoRoot),\n ),\n ) !== JSON.stringify(state.resolved)\n ) {\n drift.push('resolved extension paths changed');\n }\n if (expectedCompositionFingerprint && state.compositionFingerprint !== expectedCompositionFingerprint) {\n drift.push('extension composition changed');\n }\n try {\n if (!readBootstrapStatus(repoRoot, undefined, environment.HOME ?? os.homedir()).fresh) {\n drift.push('precompiled runtime is missing or stale');\n }\n } catch {\n drift.push('precompiled runtime is missing or stale');\n }\n\n if (settingsMode === 'persisted') {\n const agentDirectory = piAgentDirectory(environment);\n const themePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n drift.push(...piIntegrationDrift(agentDirectory));\n if (projectRegistersDoom(repoRoot)) drift.push(DUPLICATE_REGISTRATION_DRIFT);\n if (state.baseline.themePath !== themePath || state.baseline.themeName !== DEFAULT_THEME_NAME) {\n drift.push('synced theme location is out of date');\n }\n }\n if (\n readSyncDrift({ repoRoot, homeDirectory: environment.HOME ?? os.homedir() }).reasons.includes('server-bundle-stale')\n ) {\n drift.push('server bundle is missing or stale');\n }\n return drift;\n}\n\n/** Regenerates the hook files the other frontends read before any harness code runs. */\nfunction emitFrontendHooks(repoRoot: string, output: SyncOutput): void {\n const emitter = path.join(repoRoot, HOOK_EMITTER);\n if (!fs.existsSync(emitter)) return;\n const result = spawnSync(process.execPath, [emitter, '--write'], { cwd: repoRoot, encoding: 'utf8' });\n if (result.status === 0) {\n output.write('hooks: regenerated for Claude Code and Codex\\n');\n return;\n }\n output.write(`hooks: emit-hooks failed (${result.stderr?.trim() || `exit ${String(result.status)}`})\\n`);\n}\n\nexport function formatSyncResult(result: SyncResult, runner = 'pi'): string {\n const { selection } = result;\n return [\n `mode: ${selection.majorMode}`,\n `domains: ${selection.domains.join(', ') || NONE}`,\n `profile: ${selection.profile ?? NONE}`,\n `skills: ${result.skillCount}`,\n `agents: ${result.agentCount}`,\n `mcp: ${result.mcpServers.join(', ') || NONE}`,\n `state: ${result.statePath}`,\n ...(result.settingsPath ? [`settings: ${result.settingsPath}`] : []),\n ...(result.projectSettingsPath\n ? [`project: removed duplicate registration from ${result.projectSettingsPath}`]\n : []),\n '',\n `Run ${runner} from the repository root to use it.`,\n '',\n ].join('\\n');\n}\n\n/**\n * The DoomPi that produced this generation, which is the one that can load it.\n *\n * Always the executing package, never another copy the repository happens to\n * install. A generation is not portable between two installations: the bundles\n * are compiled from the building package's own extension entries, the recorded\n * compiler inputs are its files, and the state names its bootstrap entry. Naming\n * a second copy here publishes a registration whose package disagrees with the\n * state it points at, and Pi's dispatcher then loads a harness that rejects the\n * bootstrap as stale on every session, with no sync able to fix it.\n *\n * A repository that wants its own copy to own its sessions runs sync with that\n * copy's CLI, which makes it the executing package.\n */\nfunction packageRegistrationFor(): SyncPackageRegistration {\n const root = fs.realpathSync(doomPiPackageRoot());\n const manifestPath = path.join(root, 'package.json');\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as {\n version?: unknown;\n doompiApiVersion?: unknown;\n pi?: { extensions?: unknown };\n };\n const version = manifest.version;\n const apiVersion = manifest.doompiApiVersion;\n const extensions = manifest.pi?.extensions;\n const extension = Array.isArray(extensions) ? extensions.find((value) => typeof value === 'string') : undefined;\n if (\n typeof version !== 'string' ||\n typeof apiVersion !== 'number' ||\n !Number.isSafeInteger(apiVersion) ||\n apiVersion < 1 ||\n apiVersion !== DOOMPI_API_VERSION ||\n typeof extension !== 'string'\n ) {\n throw new Error(`Installed DoomPi package at ${root} has no supported API-versioned Pi extension entry`);\n }\n return {\n root,\n version,\n apiVersion: apiVersion as number,\n manifestPath,\n entry: fs.realpathSync(path.resolve(root, extension)),\n };\n}\n\n/** Returns true when a valid legacy registration must be republished with API metadata. */\nexport function syncRegistrationNeedsApiMigration(repoRoot: string, homeDirectory: string): boolean {\n try {\n const registration = readSyncRegistration(repoRoot, homeDirectory);\n return (\n registration !== undefined &&\n (registration.version !== SYNC_REGISTRATION_VERSION || registration.package.apiVersion === undefined)\n );\n } catch {\n return false;\n }\n}\n\n/** Resolves the matrix, stages it into home-scoped worktree storage, and publishes one generation. */\nexport async function synchronize(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n commandOptions: SyncCommandOptions = {},\n): Promise<number> {\n const check = args.includes(CHECK_OPTION);\n const force = args.includes(FORCE_OPTION);\n const rest = args.slice(1).filter((argument) => ![CHECK_OPTION, FORCE_OPTION, GLOBAL_OPTION].includes(argument));\n const homeDirectory = commandOptions.homeDirectory ?? environment.HOME ?? os.homedir();\n const roots = resolveSyncRoots(args, environment, currentDirectory, homeDirectory);\n const { globalOnly, globalRoot, targetRoot: repoRoot } = roots;\n const scopedEnvironment = environmentForSyncScope(environment, globalOnly);\n if (globalOnly && !check) fs.mkdirSync(globalRoot, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n // Sync tolerates keys it does not recognise so a config written against a\n // different version cannot break a build. `doompi doctor` reports them.\n const modes = loadMajorModesConfigLenient(repoRoot, homeDirectory);\n const configDiagnostics = [...loadDoomConfigLenient(repoRoot, homeDirectory).diagnostics, ...modes.diagnostics];\n const defaultMajorMode = modes.config.defaultMajorMode;\n const defaultDomains = loadDomains(repoRoot, homeDirectory).defaultDomains;\n const parsed = parseHarnessArgs(\n rest,\n selectionEnvironment(repoRoot, scopedEnvironment, homeDirectory),\n globalOnly ? globalRoot : currentDirectory,\n defaultMajorMode,\n defaultDomains,\n );\n const selection = toSelection(parsed.options);\n const agentDirectory = piAgentDirectory(scopedEnvironment, homeDirectory);\n if ((commandOptions.settingsMode ?? 'persisted') === 'persisted' && !check) {\n if (piExtensionDispatcherIsUpgradeable(agentDirectory)) {\n writePiExtensionAlias(agentDirectory);\n output.write('repair: refreshed Pi user dispatcher\\n');\n }\n const drift = piIntegrationDrift(agentDirectory);\n if (drift.length > 0) {\n throw new Error(`DoomPi Pi integration is not ready:\\n${drift.map((entry) => ` ${entry}`).join('\\n')}`);\n }\n }\n writeConfigDiagnostics(configDiagnostics, output);\n if (check) {\n const majorModesConfig = modes.config;\n const missingPackages = missingLayerPackageSpecifiers(\n majorModesConfig,\n Object.keys(majorModesConfig.layers),\n createLayerResolvers(repoRoot),\n );\n if (missingPackages.length > 0) {\n output.write(\n `doompi sync is out of date:\\n${missingPackages\n .map((specifier) => ` configured package is not installed: ${specifier}`)\n .join('\\n')}\\n`,\n );\n return 1;\n }\n let located: ReturnType<typeof readLocatedSyncState>;\n try {\n located = readLocatedSyncState(repoRoot, homeDirectory);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n output.write(`doompi sync is out of date:\\n ${detail}\\n`);\n return 1;\n }\n const expectedCompositionFingerprint = selectionCompositionFingerprint(repoRoot, parsed.options, homeDirectory);\n const drift = collectDrift(\n repoRoot,\n selection,\n located?.state,\n scopedEnvironment,\n commandOptions.settingsMode ?? 'persisted',\n expectedCompositionFingerprint,\n );\n if (drift.length === 0) {\n output.write('doompi sync is up to date\\n');\n return 0;\n }\n output.write(`doompi sync is out of date:\\n${drift.map((entry) => ` ${entry}`).join('\\n')}\\n`);\n return 1;\n }\n\n // Publishing an identical generation is not a no-op: it moves the\n // registration, so every attached cockpit reloads and the previous\n // generation becomes garbage. Same inputs, same published result.\n const driftOptions = {\n repoRoot,\n homeDirectory,\n requireWebBundle: Boolean(environment.DOOMPI_WEB_PACKAGE_ROOT),\n };\n const registrationNeedsMigration = syncRegistrationNeedsApiMigration(repoRoot, homeDirectory);\n if (!force && !registrationNeedsMigration && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n\n const progress = new SyncProgress(output);\n const releaseLock = commandOptions.lockHeld\n ? undefined\n : await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n let result: SyncResult;\n try {\n // A concurrent publisher may have resolved the drift while this command\n // waited for the lock. Avoid moving the registration for no change.\n const registrationNeedsMigration = syncRegistrationNeedsApiMigration(repoRoot, homeDirectory);\n if (!force && !registrationNeedsMigration && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n result = await stageSync(repoRoot, parsed.options, scopedEnvironment, homeDirectory, progress, commandOptions);\n } finally {\n await releaseLock?.();\n }\n emitFrontendHooks(repoRoot, output);\n output.write(formatSyncResult(result, (commandOptions.settingsMode ?? 'persisted') === 'embedded' ? 'dpi' : 'pi'));\n return 0;\n}\n\nasync function stageSync(\n repoRoot: string,\n options: Omit<HarnessOptions, 'repoRoot'>,\n environment: NodeJS.ProcessEnv,\n homeDirectory: string,\n progress: SyncProgress,\n commandOptions: SyncCommandOptions = {},\n): Promise<SyncResult> {\n const location = resolveSyncLocation(repoRoot, homeDirectory);\n const generation = `${Date.now().toString(36)}-${crypto.randomUUID()}`;\n const directory = syncGenerationDirectory(location, generation);\n await fs.promises.mkdir(location.generationsDirectory, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n // The leaf is created without `recursive`, so an existing path is an error\n // rather than something to adopt: the cockpit signs and serves whatever the\n // published generation holds, and sync must only ever publish bytes it\n // wrote itself into a directory it just created.\n await fs.promises.mkdir(directory, { mode: PRIVATE_DIRECTORY_MODE });\n\n try {\n const staged = progress.start(SYNC_LABEL, 'resolving the matrix and staging resources');\n const context = await buildHarnessContext({\n ...options,\n repoRoot: location.root,\n homeDirectory,\n cwd: location.root,\n resourceDirectory: directory,\n });\n await ensureLayerPackages({\n repoRoot: location.root,\n config: context.majorModesConfig,\n layers: Object.keys(context.majorModesConfig.layers),\n environment,\n });\n staged(`${String(context.resources.skillCount)} skills, ${String(context.resources.agentCount)} agents`);\n const selection = toSelection(options);\n const resolvers = createLayerResolvers(location.root);\n const resolved = recordResolvedEntries(context.majorModesConfig, resolvers);\n const compositionFingerprint = selectionCompositionFingerprint(location.root, options, homeDirectory);\n const agentDirectory = piAgentDirectory(environment, homeDirectory);\n const persistedThemePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n const themePath =\n (commandOptions.settingsMode ?? 'persisted') === 'persisted' ? persistedThemePath : context.defaultThemePath;\n const state: SyncState = {\n version: SYNC_STATE_VERSION,\n root: location.root,\n identity: location.identity,\n inputsHash: computeInputsHash(location.root, selection, homeDirectory),\n webSourcesHash: computeWebSourcesHash(resolved),\n compositionFingerprint,\n selection,\n env: recordedEnvironment(context.environment),\n fileState: {\n profileEnvironment: loadHarnessState(context.environment).state.profileEnvironment,\n pluginHooks: context.resources.pluginHooks,\n mcpProjection: context.resources.mcpProjection,\n },\n resolved,\n baseline: {\n mcpConfigPath: context.resources.mcpConfigPath,\n personaFile: context.environment[PERSONA_FILE_ENV],\n themePath,\n themeName: DEFAULT_THEME_NAME,\n },\n };\n\n // Runtime compilation writes the package dist files consumed by both the web\n // and server bundlers. Finish it first so a package clean cannot race either\n // consumer, then run the independent web and server builds together.\n let resolveCompositions!: (compositions: readonly ExtensionComposition[]) => void;\n let rejectCompositions!: (reason?: unknown) => void;\n const compositionsReady = new Promise<readonly ExtensionComposition[]>((resolve, reject) => {\n resolveCompositions = resolve;\n rejectCompositions = reject;\n });\n const runtimeProgress = progress.start(RUNTIME_LABEL, 'precompiling the mode bundles');\n const runtimeBuild = buildSyncedRuntime(location.root, environment, homeDirectory, {\n state,\n directory,\n onCompositionsResolved: resolveCompositions,\n }).then((synced) => {\n runtimeProgress(`${String(Object.keys(synced.bundles).length)} mode bundles`);\n return synced;\n });\n void runtimeBuild.catch(rejectCompositions);\n const webBuild = (async () => {\n await runtimeBuild;\n const webProgress = progress.start(WEB_LABEL, 'bundling the web cockpit plugins');\n const web = await syncWebBundle({\n repoRoot: location.root,\n resolvedEntries: state.resolved,\n environment,\n outputDirectory: path.join(directory, 'web-bundle'),\n onNotice: (message) => progress.line(WEB_LABEL, message),\n });\n if (web.status === 'failed') throw new Error(`Cockpit bundle failed: ${web.reason}`);\n webProgress(web.status === 'bundled' ? `cockpit bundled with plugins: ${web.pluginIds.join(', ')}` : web.reason);\n return web;\n })();\n const serverBuild = (async () => {\n const compositions = await compositionsReady;\n await runtimeBuild;\n const apiProgress = progress.start(API_LABEL, 'compiling the server bundle');\n const apiDirectory = path.join(directory, 'api');\n const fingerprint = crypto\n .createHash('sha256')\n .update(JSON.stringify([...new Set(compositions.map((composition) => composition.fingerprint))]))\n .digest('hex');\n const [server, mcp] = await Promise.all([\n syncServerBundle({\n repositoryRoot: location.root,\n generation,\n fingerprint,\n compositions,\n outputDirectory: apiDirectory,\n cacheDirectory: path.join(directory, 'cache'),\n sharedCacheDirectory: location.sharedCacheDirectory,\n }),\n syncMcpBundle({\n repositoryRoot: location.root,\n generation,\n fingerprint,\n compositions,\n outputDirectory: path.join(directory, 'mcp'),\n cacheDirectory: path.join(directory, 'cache'),\n sharedCacheDirectory: location.sharedCacheDirectory,\n }),\n ]);\n apiProgress(\n `${server.descriptor.entries.length} server facet(s), ${mcp.descriptor.entries.length} MCP plugin(s) compiled`,\n );\n for (const gap of server.contractGaps) progress.line(API_LABEL, `API contract incomplete: ${gap}`);\n return { server, mcp, fingerprint, apiDirectory };\n })();\n const [runtimeResult, webResult, serverResult] = await Promise.allSettled([runtimeBuild, webBuild, serverBuild]);\n if (runtimeResult.status === 'rejected') throw runtimeResult.reason;\n if (webResult.status === 'rejected') throw webResult.reason;\n if (serverResult.status === 'rejected') throw serverResult.reason;\n const synced = runtimeResult.value;\n const web = webResult.value;\n const { server, mcp, fingerprint, apiDirectory } = serverResult.value;\n const descriptorPath = path.join(apiDirectory, DOOM_SERVER_BUNDLE_FILE);\n const mcpDescriptorPath = path.join(directory, 'mcp', DOOM_MCP_BUNDLE_FILE);\n const finalState: SyncState = {\n ...synced.state,\n serverBundle: {\n descriptorPath,\n fingerprint,\n compilerManifests: server.compilerManifests,\n sourcesHash: computeServerSourcesHash(synced.state.resolved),\n },\n mcpBundle: {\n descriptorPath: mcpDescriptorPath,\n fingerprint,\n compilerManifests: mcp.compilerManifests,\n sourcesHash: computeMcpSourcesHash(synced.state.resolved),\n },\n };\n const statePath = await writeSyncState(\n location.root,\n finalState,\n homeDirectory,\n path.join(directory, 'state.json'),\n );\n const projectSettingsPath =\n (commandOptions.settingsMode ?? 'persisted') === 'persisted'\n ? writeProjectPiSettings(location.root, homeDirectory)\n : undefined;\n const result: SyncResult = {\n statePath,\n ...(projectSettingsPath ? { projectSettingsPath } : {}),\n selection,\n mcpServers: synced.state.baseline.mcpConfigPath ? readMcpServerNames(synced.state.baseline.mcpConfigPath) : [],\n skillCount: context.resources.skillCount,\n agentCount: context.resources.agentCount,\n };\n publishSyncRegistration(\n location.root,\n {\n version: SYNC_REGISTRATION_VERSION,\n root: location.root,\n identity: location.identity,\n generation,\n generationRoot: directory,\n statePath,\n stateSha256: syncStateSha256(statePath),\n webDirectory: web.status === 'bundled' ? web.assetsDir : null,\n apiDirectory,\n serverBundle: { path: descriptorPath, fingerprint, sha256: syncStateSha256(descriptorPath) },\n mcpBundle: { path: mcpDescriptorPath, fingerprint, sha256: syncStateSha256(mcpDescriptorPath) },\n package: packageRegistrationFor(),\n },\n homeDirectory,\n );\n // ponytail: retain generations until host-owned drain evidence can prove no session uses them.\n // Directory age and an open-file check cannot establish that a lazy import is finished.\n return result;\n } catch (error) {\n await fs.promises.rm(directory, { recursive: true, force: true });\n throw error;\n }\n}\n\n/** Compatibility API. Executables call the command function directly. */\nexport class SyncCommand {\n readonly name = SYNC_COMMAND;\n private readonly settingsMode: SyncSettingsMode;\n private readonly homeDirectory: string | undefined;\n private readonly lockHeld: boolean;\n\n constructor(options: SyncCommandOptions = {}) {\n this.settingsMode = options.settingsMode ?? 'persisted';\n this.homeDirectory = options.homeDirectory;\n this.lockHeld = options.lockHeld ?? false;\n }\n\n matches(args: string[]): boolean {\n return args[0] === this.name;\n }\n\n async execute(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n ): Promise<number> {\n return synchronize(args, environment, currentDirectory, output, {\n settingsMode: this.settingsMode,\n homeDirectory: this.homeDirectory,\n lockHeld: this.lockHeld,\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["loadDoomConfigLenient"],"sources":["../../../../src/cli/commands/sync/index.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { globalDoomConfigDirectory } from '@agimon-ai/doompi-config/config';\nimport { loadDomains } from '@agimon-ai/doompi-config/domains';\nimport { filterHookDisabledLayers, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport { loadMajorModesConfig, loadMajorModesConfigLenient } from '@agimon-ai/doompi-config/majorModes';\nimport type { ConfigDiagnostic } from '@agimon-ai/doompi-config/types';\nimport { DOOM_MCP_BUNDLE_FILE } from '@agimon-ai/doompi-core/mcp-facet';\nimport {\n mergePiSettings,\n piAgentDirectory,\n piThemeDirectory,\n readPiSettings,\n serializePiSettings,\n} from '@agimon-ai/doompi-core/runtime-pi-settings';\nimport { DOOM_SERVER_BUNDLE_FILE } from '@agimon-ai/doompi-core/server-facet';\nimport {\n acquireSyncLocationLock,\n resolveSyncLocation,\n syncGenerationDirectory,\n} from '@agimon-ai/doompi-core/sync-location';\nimport {\n DOOMPI_API_VERSION,\n publishSyncRegistration,\n readSyncRegistration,\n SYNC_REGISTRATION_VERSION,\n syncStateSha256,\n type SyncPackageRegistration,\n} from '@agimon-ai/doompi-core/sync-registration';\nimport { DEFAULT_THEME, DEFAULT_THEME_NAME } from '@agimon-ai/doompi-ui/theme';\n\nimport { buildSyncedRuntime } from '../../../builders/cli';\nimport { readBootstrapStatus } from '../../../builders/cli/bootstrapLocator';\nimport {\n createLayerResolvers,\n type ExtensionComposition,\n PERSONA_ENTRY,\n resolveExtensionComposition,\n} from '../../../builders/cli/extensionAssembler';\nimport { buildHarnessContext } from '../../../builders/cli/harnessContext';\nimport {\n doomPiPackageRoot,\n piExtensionAliasIsCurrent,\n writePiExtensionAlias,\n} from '../../../builders/cli/piExtensionAlias';\nimport { piExtensionDispatcherIsUpgradeable } from '../../../builders/cli/piExtensionDispatcher';\nimport {\n DUPLICATE_REGISTRATION_DRIFT,\n projectRegistersDoom,\n writeProjectPiSettings,\n} from '../../../builders/cli/projectSettings';\nimport { syncServerBundle } from '../../../builders/server';\nimport { syncMcpBundle } from '../../../builders/server/mcpBundle';\nimport { syncWebBundle } from '../../../builders/web';\nimport { HARNESS_STATE_POINTER, loadHarnessState } from '../../../composition/harnessState';\nimport { ensureLayerPackages, missingLayerPackageSpecifiers } from '../../../composition/layerPackageInstaller';\nimport { loadDoomConfigLenient } from '../../../composition/projectTrust';\nimport { resolveDoomConfigurationRoot } from '../../../composition/repository';\nimport { readSyncDrift, type SyncDriftReason } from '../../../composition/syncDrift';\nimport {\n computeInputsHash,\n computeMcpSourcesHash,\n computeServerSourcesHash,\n computeWebSourcesHash,\n readLocatedSyncState,\n readMcpServerNames,\n recordResolvedEntries,\n SYNC_STATE_VERSION,\n type SyncSelection,\n type SyncState,\n syncStateRootMatches,\n writeSyncState,\n} from '../../../composition/syncState';\nimport type { HarnessOptions } from '../../../composition/types/harness';\nimport { DOOMPI_DOMAINS_ENV, DOOMPI_MAJOR_MODE_ENV, DOOMPI_PROFILE_ENV } from '../../matrixOptions';\nimport { parseHarnessArgs } from '../../options';\nimport { SyncProgress, type SyncProgressOutput } from './presenter';\n\n/**\n * `doom-pi sync`: resolve the matrix once and write it where plain Pi finds it.\n *\n * The doom-emacs split. Everything that needs a real Node process (module\n * resolution, staging skills and agents, generating the MCP config) happens\n * here, and the doom-pi extension then only reads what this produced. The\n * launcher is untouched and keeps resolving the same matrix per run.\n */\n\nconst SYNC_COMMAND = 'sync';\nconst CHECK_OPTION = '--check';\n/** Republishes even when nothing drifted, for a generation suspected of being damaged. */\nconst FORCE_OPTION = '--force';\nconst GLOBAL_OPTION = '--global';\nconst HARNESS_ROOT_ENV = 'DOOMPI_ROOT';\nconst PERSONA_FILE_ENV = 'DOOMPI_PERSONA_FILE';\nconst HOOK_EMITTER = path.join('tools', 'harness', 'emit-hooks.mjs');\nconst NONE = '(none)';\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst SYNC_LABEL = 'sync';\nconst RUNTIME_LABEL = 'runtime';\nconst WEB_LABEL = 'web';\nconst API_LABEL = 'api';\n\nexport interface SyncRoots {\n globalOnly: boolean;\n globalRoot: string;\n sourceRoot: string;\n targetRoot: string;\n}\n\n/** Resolves the configuration source and publication destination for one sync. */\nexport function resolveSyncRoots(\n args: readonly string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n homeDirectory = environment.HOME ?? os.homedir(),\n): SyncRoots {\n const globalRoot = globalDoomConfigDirectory(homeDirectory);\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const sourceRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n const globalOnly = args.includes(GLOBAL_OPTION);\n return { globalOnly, globalRoot, sourceRoot, targetRoot: globalOnly ? globalRoot : sourceRoot };\n}\n\nconst GLOBAL_SCOPE_INHERITED_KEYS = [\n HARNESS_ROOT_ENV,\n HARNESS_STATE_POINTER,\n DOOMPI_MAJOR_MODE_ENV,\n DOOMPI_DOMAINS_ENV,\n DOOMPI_PROFILE_ENV,\n] as const;\n\n/** Removes workspace-only state before a global runtime is resolved. */\nexport function environmentForSyncScope(environment: NodeJS.ProcessEnv, globalOnly: boolean): NodeJS.ProcessEnv {\n if (!globalOnly) return environment;\n const scoped = { ...environment };\n for (const key of GLOBAL_SCOPE_INHERITED_KEYS) delete scoped[key];\n return scoped;\n}\n\n/**\n * Harness variables worth recording, by prefix or exact name.\n *\n * An allowlist rather than the whole environment: the state file is a snapshot\n * of resolved configuration, and dumping `process.env` into it would write\n * every credential the sync happened to run with onto disk.\n */\nconst RECORDED_PREFIXES = ['DOOMPI_'];\nconst RECORDED_KEYS = ['CLAUDE_PROJECT_DIR', 'CODEX_REPO_ROOT', 'ORIGINAL_REPO_PATH', 'MCP_UI_VIEWER'];\n/**\n * Launcher-only values a synced session must not inherit.\n *\n * The child extension list is recomposed on every load, and the subagent binary\n * points at `pi.sh`, which a session started as plain `pi` should not shell out\n * to: Doom Team resolves Pi's own CLI when the variable is absent.\n */\nconst EXCLUDED_KEYS = new Set([\n 'DOOMPI_CHILD_EXTENSIONS',\n 'DOOMPI_COMPOSED',\n 'DOOMPI_MUTE',\n 'DOOMPI_TEMP_DIR',\n // A pointer to the syncing process's own state file. Recording it would hand\n // every later session a path to a state that died with this one.\n HARNESS_STATE_POINTER,\n 'PI_SUBAGENT_PI_BINARY',\n]);\n\ntype SyncOutput = SyncProgressOutput;\n\nexport type SyncSettingsMode = 'persisted' | 'embedded';\n\nexport interface SyncCommandOptions {\n settingsMode?: SyncSettingsMode;\n /** Test/embedding override; normal CLI execution uses the process home. */\n homeDirectory?: string;\n /** Internal pipeline seam when the caller owns the worktree lock. */\n lockHeld?: boolean;\n}\n\nexport interface SyncResult {\n statePath: string;\n /** Omitted when DPI supplies the integration as a process-local overlay. */\n settingsPath?: string;\n /** Set only when the repository still carried its own DoomPi registration. */\n projectSettingsPath?: string;\n selection: SyncSelection;\n mcpServers: string[];\n skillCount: number;\n agentCount: number;\n}\n\nexport function recordedEnvironment(environment: NodeJS.ProcessEnv): Record<string, string> {\n const recorded: Record<string, string> = {};\n for (const [key, value] of Object.entries(environment)) {\n if (value === undefined || EXCLUDED_KEYS.has(key)) continue;\n if (RECORDED_KEYS.includes(key) || RECORDED_PREFIXES.some((prefix) => key.startsWith(prefix))) {\n recorded[key] = value;\n }\n }\n return recorded;\n}\n\n/**\n * Reports the config keys sync chose to ignore.\n *\n * Never fatal. A key nobody recognises is usually a config written for another\n * version of a layer, and refusing to build over it is worse than proceeding\n * without it. The strict check lives in `doompi doctor`.\n */\nfunction writeConfigDiagnostics(diagnostics: readonly ConfigDiagnostic[], output: SyncOutput): void {\n if (diagnostics.length === 0) return;\n const lines = diagnostics.map((entry) => ` ${entry.filePath}: ${entry.path}`).join('\\n');\n output.write(\n `config: ignored ${String(diagnostics.length)} unsupported key(s); run doompi doctor for the strict check\\n${lines}\\n`,\n );\n}\n/**\n * Layers the repository's declared selection under the usual resolution.\n *\n * `.doom/config.yaml` holds what the repository selects by default, the way\n * init.el does for doom-emacs. Seeding the environment the parser reads keeps\n * the precedence the launcher already documents: an explicit flag wins, then an\n * exported variable, then the declared default.\n */\nexport function selectionEnvironment(\n repoRoot: string,\n environment: NodeJS.ProcessEnv,\n homeDirectory?: string,\n): NodeJS.ProcessEnv {\n const { selection } = loadDoomConfigLenient(repoRoot, homeDirectory).config;\n if (!selection) return environment;\n return {\n ...environment,\n ...(selection.majorMode && !environment[DOOMPI_MAJOR_MODE_ENV]\n ? { [DOOMPI_MAJOR_MODE_ENV]: selection.majorMode }\n : {}),\n ...(selection.profile && !environment[DOOMPI_PROFILE_ENV] ? { [DOOMPI_PROFILE_ENV]: selection.profile } : {}),\n ...(selection.domains && environment[DOOMPI_DOMAINS_ENV] === undefined\n ? { [DOOMPI_DOMAINS_ENV]: selection.domains.join(',') }\n : {}),\n };\n}\n\nexport function toSelection(\n options: Pick<HarnessOptions, 'majorMode' | 'domains' | 'profile' | 'preset'>,\n): SyncSelection {\n return {\n majorMode: options.majorMode,\n domains: options.domains,\n profile: options.profile,\n preset: options.preset,\n };\n}\n\nexport function selectionCompositionFingerprint(\n repoRoot: string,\n options: Pick<HarnessOptions, 'agents' | 'hooks' | 'majorMode' | 'mcp' | 'preset'>,\n homeDirectory: string = os.homedir(),\n): string {\n const majorModesConfig = loadMajorModesConfig(repoRoot, homeDirectory);\n const resolvers = createLayerResolvers(repoRoot);\n return resolveExtensionComposition({\n agents: options.agents,\n autoStop: false,\n mute: false,\n preset: options.preset,\n personaEntry: resolvers.packageEntry(PERSONA_ENTRY),\n majorMode: options.majorMode,\n layers: filterHookDisabledLayers(\n majorModesConfig,\n resolveLayers(majorModesConfig, options.majorMode),\n options.hooks,\n ),\n majorModesConfig,\n resolvers,\n }).fingerprint;\n}\n\n/** Settings, dispatcher and theme differences an init would fix, independent of sync state. */\nfunction piIntegrationDrift(agentDirectory: string): string[] {\n const drift: string[] = [];\n const themePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n const settings = readPiSettings(agentDirectory);\n const merged = mergePiSettings(settings, agentDirectory, { themePath, themeName: DEFAULT_THEME_NAME });\n if (serializePiSettings(merged) !== serializePiSettings(settings)) {\n drift.push('Pi user settings are out of date; run doompi init');\n }\n if (!piExtensionAliasIsCurrent(agentDirectory)) drift.push('Pi user dispatcher is out of date; run doompi init');\n const expectedTheme = `${JSON.stringify(DEFAULT_THEME, null, 2)}\\n`;\n if (!fs.existsSync(themePath) || fs.readFileSync(themePath, 'utf8') !== expectedTheme) {\n drift.push('Pi user theme is out of date; run doompi init');\n }\n return drift;\n}\n\n/** Differences between what a sync would produce and what is on disk. */\nexport function collectDrift(\n repoRoot: string,\n selection: SyncSelection,\n state: SyncState | undefined,\n environment: NodeJS.ProcessEnv = process.env,\n settingsMode: SyncSettingsMode = 'persisted',\n expectedCompositionFingerprint?: string,\n): string[] {\n if (!state) return ['no sync state: run doompi sync'];\n const drift: string[] = [];\n if (syncRegistrationNeedsApiMigration(repoRoot, environment.HOME ?? os.homedir())) {\n drift.push('DoomPi registration needs API migration');\n }\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 const sharedDrift = readSyncDrift({ repoRoot, homeDirectory: environment.HOME ?? os.homedir() });\n const sharedMessages: Partial<Record<SyncDriftReason, string>> = {\n 'never-synced': 'sync registration is missing or invalid',\n 'code-changed': 'cockpit sources changed since the last sync',\n 'cockpit-bundle-missing': 'cockpit bundle is missing',\n 'package-apis-missing': 'package API routes are missing',\n 'server-bundle-stale': 'server bundle is missing or stale',\n 'mcp-bundle-stale': 'MCP bundle is missing or stale',\n };\n for (const reason of sharedDrift.reasons) {\n const message = sharedMessages[reason];\n if (message) drift.push(message);\n }\n return drift;\n}\n\n/** Regenerates the hook files the other frontends read before any harness code runs. */\nfunction emitFrontendHooks(repoRoot: string, output: SyncOutput): void {\n const emitter = path.join(repoRoot, HOOK_EMITTER);\n if (!fs.existsSync(emitter)) return;\n const result = spawnSync(process.execPath, [emitter, '--write'], { cwd: repoRoot, encoding: 'utf8' });\n if (result.status === 0) {\n output.write('hooks: regenerated for Claude Code and Codex\\n');\n return;\n }\n output.write(`hooks: emit-hooks failed (${result.stderr?.trim() || `exit ${String(result.status)}`})\\n`);\n}\n\nexport function formatSyncResult(result: SyncResult, runner = 'pi'): string {\n const { selection } = result;\n return [\n `mode: ${selection.majorMode}`,\n `domains: ${selection.domains.join(', ') || NONE}`,\n `profile: ${selection.profile ?? NONE}`,\n `skills: ${result.skillCount}`,\n `agents: ${result.agentCount}`,\n `mcp: ${result.mcpServers.join(', ') || NONE}`,\n `state: ${result.statePath}`,\n ...(result.settingsPath ? [`settings: ${result.settingsPath}`] : []),\n ...(result.projectSettingsPath\n ? [`project: removed duplicate registration from ${result.projectSettingsPath}`]\n : []),\n '',\n `Run ${runner} from the repository root to use it.`,\n '',\n ].join('\\n');\n}\n\n/**\n * The DoomPi that produced this generation, which is the one that can load it.\n *\n * Always the executing package, never another copy the repository happens to\n * install. A generation is not portable between two installations: the bundles\n * are compiled from the building package's own extension entries, the recorded\n * compiler inputs are its files, and the state names its bootstrap entry. Naming\n * a second copy here publishes a registration whose package disagrees with the\n * state it points at, and Pi's dispatcher then loads a harness that rejects the\n * bootstrap as stale on every session, with no sync able to fix it.\n *\n * A repository that wants its own copy to own its sessions runs sync with that\n * copy's CLI, which makes it the executing package.\n */\nfunction packageRegistrationFor(): SyncPackageRegistration {\n const root = fs.realpathSync(doomPiPackageRoot());\n const manifestPath = path.join(root, 'package.json');\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as {\n version?: unknown;\n doompiApiVersion?: unknown;\n pi?: { extensions?: unknown };\n };\n const version = manifest.version;\n const apiVersion = manifest.doompiApiVersion;\n const extensions = manifest.pi?.extensions;\n const extension = Array.isArray(extensions) ? extensions.find((value) => typeof value === 'string') : undefined;\n if (\n typeof version !== 'string' ||\n typeof apiVersion !== 'number' ||\n !Number.isSafeInteger(apiVersion) ||\n apiVersion < 1 ||\n apiVersion !== DOOMPI_API_VERSION ||\n typeof extension !== 'string'\n ) {\n throw new Error(`Installed DoomPi package at ${root} has no supported API-versioned Pi extension entry`);\n }\n return {\n root,\n version,\n apiVersion: apiVersion as number,\n manifestPath,\n entry: fs.realpathSync(path.resolve(root, extension)),\n };\n}\n\n/** Returns true when a valid legacy registration must be republished with API metadata. */\nexport function syncRegistrationNeedsApiMigration(repoRoot: string, homeDirectory: string): boolean {\n try {\n const registration = readSyncRegistration(repoRoot, homeDirectory);\n return (\n registration !== undefined &&\n (registration.version !== SYNC_REGISTRATION_VERSION || registration.package.apiVersion === undefined)\n );\n } catch {\n return false;\n }\n}\n\n/** Resolves the matrix, stages it into home-scoped worktree storage, and publishes one generation. */\nexport async function synchronize(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n commandOptions: SyncCommandOptions = {},\n): Promise<number> {\n const check = args.includes(CHECK_OPTION);\n const force = args.includes(FORCE_OPTION);\n const rest = args.slice(1).filter((argument) => ![CHECK_OPTION, FORCE_OPTION, GLOBAL_OPTION].includes(argument));\n const homeDirectory = commandOptions.homeDirectory ?? environment.HOME ?? os.homedir();\n const roots = resolveSyncRoots(args, environment, currentDirectory, homeDirectory);\n const { globalOnly, globalRoot, targetRoot: repoRoot } = roots;\n const scopedEnvironment = environmentForSyncScope(environment, globalOnly);\n if (globalOnly && !check) fs.mkdirSync(globalRoot, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n // Sync tolerates keys it does not recognise so a config written against a\n // different version cannot break a build. `doompi doctor` reports them.\n const modes = loadMajorModesConfigLenient(repoRoot, homeDirectory);\n const configDiagnostics = [...loadDoomConfigLenient(repoRoot, homeDirectory).diagnostics, ...modes.diagnostics];\n const defaultMajorMode = modes.config.defaultMajorMode;\n const defaultDomains = loadDomains(repoRoot, homeDirectory).defaultDomains;\n const parsed = parseHarnessArgs(\n rest,\n selectionEnvironment(repoRoot, scopedEnvironment, homeDirectory),\n globalOnly ? globalRoot : currentDirectory,\n defaultMajorMode,\n defaultDomains,\n );\n const selection = toSelection(parsed.options);\n const agentDirectory = piAgentDirectory(scopedEnvironment, homeDirectory);\n if ((commandOptions.settingsMode ?? 'persisted') === 'persisted' && !check) {\n if (piExtensionDispatcherIsUpgradeable(agentDirectory)) {\n writePiExtensionAlias(agentDirectory);\n output.write('repair: refreshed Pi user dispatcher\\n');\n }\n const drift = piIntegrationDrift(agentDirectory);\n if (drift.length > 0) {\n throw new Error(`DoomPi Pi integration is not ready:\\n${drift.map((entry) => ` ${entry}`).join('\\n')}`);\n }\n }\n writeConfigDiagnostics(configDiagnostics, output);\n if (check) {\n const majorModesConfig = modes.config;\n const missingPackages = missingLayerPackageSpecifiers(\n majorModesConfig,\n Object.keys(majorModesConfig.layers),\n createLayerResolvers(repoRoot),\n );\n if (missingPackages.length > 0) {\n output.write(\n `doompi sync is out of date:\\n${missingPackages\n .map((specifier) => ` configured package is not installed: ${specifier}`)\n .join('\\n')}\\n`,\n );\n return 1;\n }\n let located: ReturnType<typeof readLocatedSyncState>;\n try {\n located = readLocatedSyncState(repoRoot, homeDirectory);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n output.write(`doompi sync is out of date:\\n ${detail}\\n`);\n return 1;\n }\n const expectedCompositionFingerprint = selectionCompositionFingerprint(repoRoot, parsed.options, homeDirectory);\n const drift = collectDrift(\n repoRoot,\n selection,\n located?.state,\n scopedEnvironment,\n commandOptions.settingsMode ?? 'persisted',\n expectedCompositionFingerprint,\n );\n if (drift.length === 0) {\n output.write('doompi sync is up to date\\n');\n return 0;\n }\n output.write(`doompi sync is out of date:\\n${drift.map((entry) => ` ${entry}`).join('\\n')}\\n`);\n return 1;\n }\n\n // Publishing an identical generation is not a no-op: it moves the\n // registration, so every attached cockpit reloads and the previous\n // generation becomes garbage. Same inputs, same published result.\n const driftOptions = {\n repoRoot,\n homeDirectory,\n requireWebBundle: Boolean(environment.DOOMPI_WEB_PACKAGE_ROOT),\n };\n const registrationNeedsMigration = syncRegistrationNeedsApiMigration(repoRoot, homeDirectory);\n if (!force && !registrationNeedsMigration && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n\n const progress = new SyncProgress(output);\n const releaseLock = commandOptions.lockHeld\n ? undefined\n : await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n let result: SyncResult;\n try {\n // A concurrent publisher may have resolved the drift while this command\n // waited for the lock. Avoid moving the registration for no change.\n const registrationNeedsMigration = syncRegistrationNeedsApiMigration(repoRoot, homeDirectory);\n if (!force && !registrationNeedsMigration && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n result = await stageSync(repoRoot, parsed.options, scopedEnvironment, homeDirectory, progress, commandOptions);\n } finally {\n await releaseLock?.();\n }\n emitFrontendHooks(repoRoot, output);\n output.write(formatSyncResult(result, (commandOptions.settingsMode ?? 'persisted') === 'embedded' ? 'dpi' : 'pi'));\n return 0;\n}\n\nasync function stageSync(\n repoRoot: string,\n options: Omit<HarnessOptions, 'repoRoot'>,\n environment: NodeJS.ProcessEnv,\n homeDirectory: string,\n progress: SyncProgress,\n commandOptions: SyncCommandOptions = {},\n): Promise<SyncResult> {\n const location = resolveSyncLocation(repoRoot, homeDirectory);\n const generation = `${Date.now().toString(36)}-${crypto.randomUUID()}`;\n const directory = syncGenerationDirectory(location, generation);\n await fs.promises.mkdir(location.generationsDirectory, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n // The leaf is created without `recursive`, so an existing path is an error\n // rather than something to adopt: the cockpit signs and serves whatever the\n // published generation holds, and sync must only ever publish bytes it\n // wrote itself into a directory it just created.\n await fs.promises.mkdir(directory, { mode: PRIVATE_DIRECTORY_MODE });\n\n try {\n const staged = progress.start(SYNC_LABEL, 'resolving the matrix and staging resources');\n const context = await buildHarnessContext({\n ...options,\n repoRoot: location.root,\n homeDirectory,\n cwd: location.root,\n resourceDirectory: directory,\n });\n await ensureLayerPackages({\n repoRoot: location.root,\n config: context.majorModesConfig,\n layers: Object.keys(context.majorModesConfig.layers),\n environment,\n });\n staged(`${String(context.resources.skillCount)} skills, ${String(context.resources.agentCount)} agents`);\n const selection = toSelection(options);\n const resolvers = createLayerResolvers(location.root);\n const resolved = recordResolvedEntries(context.majorModesConfig, resolvers);\n const compositionFingerprint = selectionCompositionFingerprint(location.root, options, homeDirectory);\n const agentDirectory = piAgentDirectory(environment, homeDirectory);\n const persistedThemePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n const themePath =\n (commandOptions.settingsMode ?? 'persisted') === 'persisted' ? persistedThemePath : context.defaultThemePath;\n const state: SyncState = {\n version: SYNC_STATE_VERSION,\n root: location.root,\n identity: location.identity,\n inputsHash: computeInputsHash(location.root, selection, homeDirectory),\n webSourcesHash: computeWebSourcesHash(resolved),\n compositionFingerprint,\n selection,\n env: recordedEnvironment(context.environment),\n fileState: {\n profileEnvironment: loadHarnessState(context.environment).state.profileEnvironment,\n pluginHooks: context.resources.pluginHooks,\n mcpProjection: context.resources.mcpProjection,\n },\n resolved,\n baseline: {\n mcpConfigPath: context.resources.mcpConfigPath,\n personaFile: context.environment[PERSONA_FILE_ENV],\n themePath,\n themeName: DEFAULT_THEME_NAME,\n },\n };\n\n // Runtime compilation writes the package dist files consumed by both the web\n // and server bundlers. Finish it first so a package clean cannot race either\n // consumer, then run the independent web and server builds together.\n let resolveCompositions!: (compositions: readonly ExtensionComposition[]) => void;\n let rejectCompositions!: (reason?: unknown) => void;\n const compositionsReady = new Promise<readonly ExtensionComposition[]>((resolve, reject) => {\n resolveCompositions = resolve;\n rejectCompositions = reject;\n });\n const runtimeProgress = progress.start(RUNTIME_LABEL, 'precompiling the mode bundles');\n const runtimeBuild = buildSyncedRuntime(location.root, environment, homeDirectory, {\n state,\n directory,\n onCompositionsResolved: resolveCompositions,\n }).then((synced) => {\n runtimeProgress(`${String(Object.keys(synced.bundles).length)} mode bundles`);\n return synced;\n });\n void runtimeBuild.catch(rejectCompositions);\n const webBuild = (async () => {\n await runtimeBuild;\n const webProgress = progress.start(WEB_LABEL, 'bundling the web cockpit plugins');\n const web = await syncWebBundle({\n repoRoot: location.root,\n resolvedEntries: state.resolved,\n environment,\n outputDirectory: path.join(directory, 'web-bundle'),\n onNotice: (message) => progress.line(WEB_LABEL, message),\n });\n if (web.status === 'failed') throw new Error(`Cockpit bundle failed: ${web.reason}`);\n webProgress(web.status === 'bundled' ? `cockpit bundled with plugins: ${web.pluginIds.join(', ')}` : web.reason);\n return web;\n })();\n const serverBuild = (async () => {\n const compositions = await compositionsReady;\n await runtimeBuild;\n const apiProgress = progress.start(API_LABEL, 'compiling the server bundle');\n const apiDirectory = path.join(directory, 'api');\n const fingerprint = crypto\n .createHash('sha256')\n .update(JSON.stringify([...new Set(compositions.map((composition) => composition.fingerprint))]))\n .digest('hex');\n const [server, mcp] = await Promise.all([\n syncServerBundle({\n repositoryRoot: location.root,\n generation,\n fingerprint,\n compositions,\n outputDirectory: apiDirectory,\n cacheDirectory: path.join(directory, 'cache'),\n sharedCacheDirectory: location.sharedCacheDirectory,\n }),\n syncMcpBundle({\n repositoryRoot: location.root,\n generation,\n fingerprint,\n compositions,\n outputDirectory: path.join(directory, 'mcp'),\n cacheDirectory: path.join(directory, 'cache'),\n sharedCacheDirectory: location.sharedCacheDirectory,\n }),\n ]);\n apiProgress(\n `${server.descriptor.entries.length} server facet(s), ${mcp.descriptor.entries.length} MCP plugin(s) compiled`,\n );\n for (const gap of server.contractGaps) progress.line(API_LABEL, `API contract incomplete: ${gap}`);\n return { server, mcp, fingerprint, apiDirectory };\n })();\n const [runtimeResult, webResult, serverResult] = await Promise.allSettled([runtimeBuild, webBuild, serverBuild]);\n if (runtimeResult.status === 'rejected') throw runtimeResult.reason;\n if (webResult.status === 'rejected') throw webResult.reason;\n if (serverResult.status === 'rejected') throw serverResult.reason;\n const synced = runtimeResult.value;\n const web = webResult.value;\n const { server, mcp, fingerprint, apiDirectory } = serverResult.value;\n const descriptorPath = path.join(apiDirectory, DOOM_SERVER_BUNDLE_FILE);\n const mcpDescriptorPath = path.join(directory, 'mcp', DOOM_MCP_BUNDLE_FILE);\n const finalState: SyncState = {\n ...synced.state,\n serverBundle: {\n descriptorPath,\n fingerprint,\n compilerManifests: server.compilerManifests,\n sourcesHash: computeServerSourcesHash(synced.state.resolved),\n },\n mcpBundle: {\n descriptorPath: mcpDescriptorPath,\n fingerprint,\n compilerManifests: mcp.compilerManifests,\n sourcesHash: computeMcpSourcesHash(synced.state.resolved),\n },\n };\n const statePath = await writeSyncState(\n location.root,\n finalState,\n homeDirectory,\n path.join(directory, 'state.json'),\n );\n const projectSettingsPath =\n (commandOptions.settingsMode ?? 'persisted') === 'persisted'\n ? writeProjectPiSettings(location.root, homeDirectory)\n : undefined;\n const result: SyncResult = {\n statePath,\n ...(projectSettingsPath ? { projectSettingsPath } : {}),\n selection,\n mcpServers: synced.state.baseline.mcpConfigPath ? readMcpServerNames(synced.state.baseline.mcpConfigPath) : [],\n skillCount: context.resources.skillCount,\n agentCount: context.resources.agentCount,\n };\n publishSyncRegistration(\n location.root,\n {\n version: SYNC_REGISTRATION_VERSION,\n root: location.root,\n identity: location.identity,\n generation,\n generationRoot: directory,\n statePath,\n stateSha256: syncStateSha256(statePath),\n webDirectory: web.status === 'bundled' ? web.assetsDir : null,\n apiDirectory,\n serverBundle: { path: descriptorPath, fingerprint, sha256: syncStateSha256(descriptorPath) },\n mcpBundle: { path: mcpDescriptorPath, fingerprint, sha256: syncStateSha256(mcpDescriptorPath) },\n package: packageRegistrationFor(),\n },\n homeDirectory,\n );\n // ponytail: retain generations until host-owned drain evidence can prove no session uses them.\n // Directory age and an open-file check cannot establish that a lazy import is finished.\n return result;\n } catch (error) {\n await fs.promises.rm(directory, { recursive: true, force: true });\n throw error;\n }\n}\n\n/** Compatibility API. Executables call the command function directly. */\nexport class SyncCommand {\n readonly name = SYNC_COMMAND;\n private readonly settingsMode: SyncSettingsMode;\n private readonly homeDirectory: string | undefined;\n private readonly lockHeld: boolean;\n\n constructor(options: SyncCommandOptions = {}) {\n this.settingsMode = options.settingsMode ?? 'persisted';\n this.homeDirectory = options.homeDirectory;\n this.lockHeld = options.lockHeld ?? false;\n }\n\n matches(args: string[]): boolean {\n return args[0] === this.name;\n }\n\n async execute(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n ): Promise<number> {\n return synchronize(args, environment, currentDirectory, output, {\n settingsMode: this.settingsMode,\n homeDirectory: this.homeDirectory,\n lockHeld: this.lockHeld,\n });\n }\n}\n"],"mappings":"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"}
|
package/dist/cli/server/run.cjs
CHANGED
|
@@ -1,37 +1,49 @@
|
|
|
1
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
1
2
|
const require_harnessOptions = require("../harnessOptions.cjs");
|
|
2
3
|
const require_index = require("../../composition/syncDrift/index.cjs");
|
|
3
|
-
const require_runtime = require("../../builders/server/runtime.cjs");
|
|
4
|
+
const require_runtime$1 = require("../../builders/server/runtime.cjs");
|
|
4
5
|
const require_workflow = require("../commands/sync/workflow.cjs");
|
|
5
6
|
const require_options = require("./options.cjs");
|
|
7
|
+
let node_os = require("node:os");
|
|
8
|
+
node_os = require_runtime.__toESM(node_os, 1);
|
|
9
|
+
let _agimon_ai_doompi_config_config = require("@agimon-ai/doompi-config/config");
|
|
6
10
|
//#region src/cli/server/run.ts
|
|
11
|
+
const DOOMPI_ROOT_ENV = "DOOMPI_ROOT";
|
|
7
12
|
async function runServer(args) {
|
|
8
13
|
const options = require_options.parseServeOptions(args);
|
|
14
|
+
const environment = Object.freeze({ ...process.env });
|
|
15
|
+
const syncRuntime = async (root, syncEnvironment, global = false) => {
|
|
16
|
+
if (require_index.readSyncDrift({
|
|
17
|
+
repoRoot: root,
|
|
18
|
+
homeDirectory: syncEnvironment.HOME,
|
|
19
|
+
requireFreshSources: false,
|
|
20
|
+
requireWebBundle: true
|
|
21
|
+
}).fresh) return;
|
|
22
|
+
let output = "";
|
|
23
|
+
const environment = global ? {
|
|
24
|
+
...syncEnvironment,
|
|
25
|
+
[DOOMPI_ROOT_ENV]: root
|
|
26
|
+
} : syncEnvironment;
|
|
27
|
+
const code = await require_workflow.runSync(global ? ["sync", "--global"] : ["sync"], environment, root, { write(chunk) {
|
|
28
|
+
output += String(chunk);
|
|
29
|
+
return true;
|
|
30
|
+
} });
|
|
31
|
+
if (code !== 0) throw new Error(output.trim() || `Workspace sync exited with code ${String(code)}`);
|
|
32
|
+
};
|
|
9
33
|
const controller = new AbortController();
|
|
10
34
|
const stop = () => controller.abort();
|
|
11
35
|
process.on("SIGINT", stop);
|
|
12
36
|
process.on("SIGTERM", stop);
|
|
13
37
|
process.on("disconnect", stop);
|
|
14
38
|
try {
|
|
15
|
-
|
|
39
|
+
await syncRuntime((0, _agimon_ai_doompi_config_config.globalDoomConfigDirectory)(environment.HOME ?? node_os.default.homedir()), environment, true);
|
|
40
|
+
return await require_runtime$1.runServerRuntime(options, {
|
|
16
41
|
cwd: process.cwd(),
|
|
17
|
-
environment
|
|
42
|
+
environment,
|
|
18
43
|
signal: controller.signal,
|
|
19
44
|
resolveHarnessOptions: require_harnessOptions.resolveHarnessOptions,
|
|
20
45
|
notice: (message) => void process.stderr.write(`[doompi-server] ${message}\n`),
|
|
21
|
-
syncWorkspace:
|
|
22
|
-
if (require_index.readSyncDrift({
|
|
23
|
-
repoRoot: root,
|
|
24
|
-
homeDirectory: environment.HOME,
|
|
25
|
-
requireFreshSources: false,
|
|
26
|
-
requireWebBundle: true
|
|
27
|
-
}).fresh) return;
|
|
28
|
-
let output = "";
|
|
29
|
-
const code = await require_workflow.runSync(["sync"], environment, root, { write(chunk) {
|
|
30
|
-
output += String(chunk);
|
|
31
|
-
return true;
|
|
32
|
-
} });
|
|
33
|
-
if (code !== 0) throw new Error(output.trim() || `Workspace sync exited with code ${String(code)}`);
|
|
34
|
-
}
|
|
46
|
+
syncWorkspace: syncRuntime
|
|
35
47
|
});
|
|
36
48
|
} finally {
|
|
37
49
|
process.off("SIGINT", stop);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.cjs","names":["parseServeOptions","
|
|
1
|
+
{"version":3,"file":"run.cjs","names":["parseServeOptions","readSyncDrift","runSync","globalDoomConfigDirectory","os","runServerRuntime"],"sources":["../../../src/cli/server/run.ts"],"sourcesContent":["import os from 'node:os';\n\nimport { globalDoomConfigDirectory } from '@agimon-ai/doompi-config/config';\n\nimport { runServerRuntime } from '../../builders/server/runtime';\nimport { readSyncDrift } from '../../composition/syncDrift';\nimport { runSync } from '../commands/sync/workflow';\nimport { resolveHarnessOptions } from '../harnessOptions';\nimport { parseServeOptions } from './options';\n\nconst DOOMPI_ROOT_ENV = 'DOOMPI_ROOT';\n\nexport async function runServer(args: readonly string[]): Promise<number> {\n const options = parseServeOptions(args);\n const environment = Object.freeze({ ...process.env });\n const syncRuntime = async (root: string, syncEnvironment: NodeJS.ProcessEnv, global = false): Promise<void> => {\n const runtimeDrift = readSyncDrift({\n repoRoot: root,\n homeDirectory: syncEnvironment.HOME,\n requireFreshSources: false,\n requireWebBundle: true,\n });\n if (runtimeDrift.fresh) return;\n let output = '';\n const environment = global ? { ...syncEnvironment, [DOOMPI_ROOT_ENV]: root } : syncEnvironment;\n const code = await runSync(global ? ['sync', '--global'] : ['sync'], environment, root, {\n write(chunk) {\n output += String(chunk);\n return true;\n },\n });\n if (code !== 0) throw new Error(output.trim() || `Workspace sync exited with code ${String(code)}`);\n };\n const controller = new AbortController();\n const stop = () => controller.abort();\n process.on('SIGINT', stop);\n process.on('SIGTERM', stop);\n // An IPC parent owns this server's lifetime. Its channel closes even when the\n // parent is killed before it can forward a termination signal.\n process.on('disconnect', stop);\n try {\n await syncRuntime(globalDoomConfigDirectory(environment.HOME ?? os.homedir()), environment, true);\n return await runServerRuntime(options, {\n cwd: process.cwd(),\n environment,\n signal: controller.signal,\n resolveHarnessOptions,\n notice: (message) => void process.stderr.write(`[doompi-server] ${message}\\n`),\n syncWorkspace: syncRuntime,\n });\n } finally {\n process.off('SIGINT', stop);\n process.off('SIGTERM', stop);\n process.off('disconnect', stop);\n }\n}\n"],"mappings":";;;;;;;;;;AAUA,MAAM,kBAAkB;AAExB,eAAsB,UAAU,MAA0C;CACxE,MAAM,UAAUA,gBAAAA,kBAAkB,IAAI;CACtC,MAAM,cAAc,OAAO,OAAO,EAAE,GAAG,QAAQ,IAAI,CAAC;CACpD,MAAM,cAAc,OAAO,MAAc,iBAAoC,SAAS,UAAyB;EAO7G,IANqBC,cAAAA,cAAc;GACjC,UAAU;GACV,eAAe,gBAAgB;GAC/B,qBAAqB;GACrB,kBAAkB;EACpB,CACe,CAAC,CAAC,OAAO;EACxB,IAAI,SAAS;EACb,MAAM,cAAc,SAAS;GAAE,GAAG;IAAkB,kBAAkB;EAAK,IAAI;EAC/E,MAAM,OAAO,MAAMC,iBAAAA,QAAQ,SAAS,CAAC,QAAQ,UAAU,IAAI,CAAC,MAAM,GAAG,aAAa,MAAM,EACtF,MAAM,OAAO;GACX,UAAU,OAAO,KAAK;GACtB,OAAO;EACT,EACF,CAAC;EACD,IAAI,SAAS,GAAG,MAAM,IAAI,MAAM,OAAO,KAAK,KAAK,mCAAmC,OAAO,IAAI,GAAG;CACpG;CACA,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,aAAa,WAAW,MAAM;CACpC,QAAQ,GAAG,UAAU,IAAI;CACzB,QAAQ,GAAG,WAAW,IAAI;CAG1B,QAAQ,GAAG,cAAc,IAAI;CAC7B,IAAI;EACF,MAAM,aAAA,GAAYC,gCAAAA,0BAAAA,CAA0B,YAAY,QAAQC,QAAAA,QAAG,QAAQ,CAAC,GAAG,aAAa,IAAI;EAChG,OAAO,MAAMC,kBAAAA,iBAAiB,SAAS;GACrC,KAAK,QAAQ,IAAI;GACjB;GACA,QAAQ,WAAW;GACnB,uBAAA,uBAAA;GACA,SAAS,YAAY,KAAK,QAAQ,OAAO,MAAM,mBAAmB,QAAQ,GAAG;GAC7E,eAAe;EACjB,CAAC;CACH,UAAU;EACR,QAAQ,IAAI,UAAU,IAAI;EAC1B,QAAQ,IAAI,WAAW,IAAI;EAC3B,QAAQ,IAAI,cAAc,IAAI;CAChC;AACF"}
|
package/dist/cli/server/run.mjs
CHANGED
|
@@ -3,35 +3,45 @@ import { readSyncDrift } from "../../composition/syncDrift/index.mjs";
|
|
|
3
3
|
import { runServerRuntime } from "../../builders/server/runtime.mjs";
|
|
4
4
|
import { runSync } from "../commands/sync/workflow.mjs";
|
|
5
5
|
import { parseServeOptions } from "./options.mjs";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import { globalDoomConfigDirectory } from "@agimon-ai/doompi-config/config";
|
|
6
8
|
//#region src/cli/server/run.ts
|
|
9
|
+
const DOOMPI_ROOT_ENV = "DOOMPI_ROOT";
|
|
7
10
|
async function runServer(args) {
|
|
8
11
|
const options = parseServeOptions(args);
|
|
12
|
+
const environment = Object.freeze({ ...process.env });
|
|
13
|
+
const syncRuntime = async (root, syncEnvironment, global = false) => {
|
|
14
|
+
if (readSyncDrift({
|
|
15
|
+
repoRoot: root,
|
|
16
|
+
homeDirectory: syncEnvironment.HOME,
|
|
17
|
+
requireFreshSources: false,
|
|
18
|
+
requireWebBundle: true
|
|
19
|
+
}).fresh) return;
|
|
20
|
+
let output = "";
|
|
21
|
+
const environment = global ? {
|
|
22
|
+
...syncEnvironment,
|
|
23
|
+
[DOOMPI_ROOT_ENV]: root
|
|
24
|
+
} : syncEnvironment;
|
|
25
|
+
const code = await runSync(global ? ["sync", "--global"] : ["sync"], environment, root, { write(chunk) {
|
|
26
|
+
output += String(chunk);
|
|
27
|
+
return true;
|
|
28
|
+
} });
|
|
29
|
+
if (code !== 0) throw new Error(output.trim() || `Workspace sync exited with code ${String(code)}`);
|
|
30
|
+
};
|
|
9
31
|
const controller = new AbortController();
|
|
10
32
|
const stop = () => controller.abort();
|
|
11
33
|
process.on("SIGINT", stop);
|
|
12
34
|
process.on("SIGTERM", stop);
|
|
13
35
|
process.on("disconnect", stop);
|
|
14
36
|
try {
|
|
37
|
+
await syncRuntime(globalDoomConfigDirectory(environment.HOME ?? os.homedir()), environment, true);
|
|
15
38
|
return await runServerRuntime(options, {
|
|
16
39
|
cwd: process.cwd(),
|
|
17
|
-
environment
|
|
40
|
+
environment,
|
|
18
41
|
signal: controller.signal,
|
|
19
42
|
resolveHarnessOptions,
|
|
20
43
|
notice: (message) => void process.stderr.write(`[doompi-server] ${message}\n`),
|
|
21
|
-
syncWorkspace:
|
|
22
|
-
if (readSyncDrift({
|
|
23
|
-
repoRoot: root,
|
|
24
|
-
homeDirectory: environment.HOME,
|
|
25
|
-
requireFreshSources: false,
|
|
26
|
-
requireWebBundle: true
|
|
27
|
-
}).fresh) return;
|
|
28
|
-
let output = "";
|
|
29
|
-
const code = await runSync(["sync"], environment, root, { write(chunk) {
|
|
30
|
-
output += String(chunk);
|
|
31
|
-
return true;
|
|
32
|
-
} });
|
|
33
|
-
if (code !== 0) throw new Error(output.trim() || `Workspace sync exited with code ${String(code)}`);
|
|
34
|
-
}
|
|
44
|
+
syncWorkspace: syncRuntime
|
|
35
45
|
});
|
|
36
46
|
} finally {
|
|
37
47
|
process.off("SIGINT", stop);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.mjs","names":[],"sources":["../../../src/cli/server/run.ts"],"sourcesContent":["import { runServerRuntime } from '../../builders/server/runtime';\nimport { readSyncDrift } from '../../composition/syncDrift';\nimport { runSync } from '../commands/sync/workflow';\nimport { resolveHarnessOptions } from '../harnessOptions';\nimport { parseServeOptions } from './options';\n\nexport async function runServer(args: readonly string[]): Promise<number> {\n const options = parseServeOptions(args);\n const controller = new AbortController();\n const stop = () => controller.abort();\n process.on('SIGINT', stop);\n process.on('SIGTERM', stop);\n // An IPC parent owns this server's lifetime. Its channel closes even when the\n // parent is killed before it can forward a termination signal.\n process.on('disconnect', stop);\n try {\n return await runServerRuntime(options, {\n cwd: process.cwd(),\n environment
|
|
1
|
+
{"version":3,"file":"run.mjs","names":[],"sources":["../../../src/cli/server/run.ts"],"sourcesContent":["import os from 'node:os';\n\nimport { globalDoomConfigDirectory } from '@agimon-ai/doompi-config/config';\n\nimport { runServerRuntime } from '../../builders/server/runtime';\nimport { readSyncDrift } from '../../composition/syncDrift';\nimport { runSync } from '../commands/sync/workflow';\nimport { resolveHarnessOptions } from '../harnessOptions';\nimport { parseServeOptions } from './options';\n\nconst DOOMPI_ROOT_ENV = 'DOOMPI_ROOT';\n\nexport async function runServer(args: readonly string[]): Promise<number> {\n const options = parseServeOptions(args);\n const environment = Object.freeze({ ...process.env });\n const syncRuntime = async (root: string, syncEnvironment: NodeJS.ProcessEnv, global = false): Promise<void> => {\n const runtimeDrift = readSyncDrift({\n repoRoot: root,\n homeDirectory: syncEnvironment.HOME,\n requireFreshSources: false,\n requireWebBundle: true,\n });\n if (runtimeDrift.fresh) return;\n let output = '';\n const environment = global ? { ...syncEnvironment, [DOOMPI_ROOT_ENV]: root } : syncEnvironment;\n const code = await runSync(global ? ['sync', '--global'] : ['sync'], environment, root, {\n write(chunk) {\n output += String(chunk);\n return true;\n },\n });\n if (code !== 0) throw new Error(output.trim() || `Workspace sync exited with code ${String(code)}`);\n };\n const controller = new AbortController();\n const stop = () => controller.abort();\n process.on('SIGINT', stop);\n process.on('SIGTERM', stop);\n // An IPC parent owns this server's lifetime. Its channel closes even when the\n // parent is killed before it can forward a termination signal.\n process.on('disconnect', stop);\n try {\n await syncRuntime(globalDoomConfigDirectory(environment.HOME ?? os.homedir()), environment, true);\n return await runServerRuntime(options, {\n cwd: process.cwd(),\n environment,\n signal: controller.signal,\n resolveHarnessOptions,\n notice: (message) => void process.stderr.write(`[doompi-server] ${message}\\n`),\n syncWorkspace: syncRuntime,\n });\n } finally {\n process.off('SIGINT', stop);\n process.off('SIGTERM', stop);\n process.off('disconnect', stop);\n }\n}\n"],"mappings":";;;;;;;;AAUA,MAAM,kBAAkB;AAExB,eAAsB,UAAU,MAA0C;CACxE,MAAM,UAAU,kBAAkB,IAAI;CACtC,MAAM,cAAc,OAAO,OAAO,EAAE,GAAG,QAAQ,IAAI,CAAC;CACpD,MAAM,cAAc,OAAO,MAAc,iBAAoC,SAAS,UAAyB;EAO7G,IANqB,cAAc;GACjC,UAAU;GACV,eAAe,gBAAgB;GAC/B,qBAAqB;GACrB,kBAAkB;EACpB,CACe,CAAC,CAAC,OAAO;EACxB,IAAI,SAAS;EACb,MAAM,cAAc,SAAS;GAAE,GAAG;IAAkB,kBAAkB;EAAK,IAAI;EAC/E,MAAM,OAAO,MAAM,QAAQ,SAAS,CAAC,QAAQ,UAAU,IAAI,CAAC,MAAM,GAAG,aAAa,MAAM,EACtF,MAAM,OAAO;GACX,UAAU,OAAO,KAAK;GACtB,OAAO;EACT,EACF,CAAC;EACD,IAAI,SAAS,GAAG,MAAM,IAAI,MAAM,OAAO,KAAK,KAAK,mCAAmC,OAAO,IAAI,GAAG;CACpG;CACA,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,aAAa,WAAW,MAAM;CACpC,QAAQ,GAAG,UAAU,IAAI;CACzB,QAAQ,GAAG,WAAW,IAAI;CAG1B,QAAQ,GAAG,cAAc,IAAI;CAC7B,IAAI;EACF,MAAM,YAAY,0BAA0B,YAAY,QAAQ,GAAG,QAAQ,CAAC,GAAG,aAAa,IAAI;EAChG,OAAO,MAAM,iBAAiB,SAAS;GACrC,KAAK,QAAQ,IAAI;GACjB;GACA,QAAQ,WAAW;GACnB;GACA,SAAS,YAAY,KAAK,QAAQ,OAAO,MAAM,mBAAmB,QAAQ,GAAG;GAC7E,eAAe;EACjB,CAAC;CACH,UAAU;EACR,QAAQ,IAAI,UAAU,IAAI;EAC1B,QAAQ,IAAI,WAAW,IAAI;EAC3B,QAAQ,IAAI,cAAc,IAAI;CAChC;AACF"}
|
package/dist/compiler/inputs.cjs
CHANGED
|
@@ -8,12 +8,8 @@ function isRecord(value) {
|
|
|
8
8
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
* Verdicts already reached,
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* Keyed by the stats actually found on disk, so it can never answer for a set
|
|
15
|
-
* it did not see. Bounded by the compiler manifests in one composition, which
|
|
16
|
-
* is single digits.
|
|
11
|
+
* Verdicts already reached, keyed by both the receipt and the stats now on disk,
|
|
12
|
+
* so one receipt cannot answer the freshness question for another.
|
|
17
13
|
*/
|
|
18
14
|
const freshnessVerdicts = /* @__PURE__ */ new Map();
|
|
19
15
|
function digestOf(filePath) {
|
|
@@ -64,7 +60,14 @@ function inputsAreFresh(inputs) {
|
|
|
64
60
|
return false;
|
|
65
61
|
}
|
|
66
62
|
if (!stat.isFile()) return false;
|
|
67
|
-
key.update(
|
|
63
|
+
key.update(JSON.stringify([
|
|
64
|
+
input.path,
|
|
65
|
+
input.size,
|
|
66
|
+
input.mtimeMs,
|
|
67
|
+
input.sha256,
|
|
68
|
+
stat.size,
|
|
69
|
+
stat.mtimeMs
|
|
70
|
+
]));
|
|
68
71
|
if (stat.size !== input.size || stat.mtimeMs !== input.mtimeMs) moved.push(input);
|
|
69
72
|
}
|
|
70
73
|
if (moved.length === 0) return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inputs.cjs","names":["crypto","fs"],"sources":["../../src/compiler/inputs.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * One recorded build input: the stat pair for speed, the digest for truth.\n *\n * Comparing only `size` and `mtimeMs` makes every rebuild look like a change,\n * including one that restored byte-identical output from a build cache, and a\n * sync triggered that way republishes a generation nobody asked for. Comparing\n * content alone is correct but reads every input on every check, and the\n * cockpit polls this. Recording both lets the stat pair answer the common\n * \"nothing was touched\" case without opening a file, while the digest stays the\n * authority for anything whose stat moved.\n */\nexport interface InputFingerprint {\n path: string;\n size: number;\n mtimeMs: number;\n sha256: string;\n}\n\n/**\n * Verdicts already reached,
|
|
1
|
+
{"version":3,"file":"inputs.cjs","names":["crypto","fs"],"sources":["../../src/compiler/inputs.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * One recorded build input: the stat pair for speed, the digest for truth.\n *\n * Comparing only `size` and `mtimeMs` makes every rebuild look like a change,\n * including one that restored byte-identical output from a build cache, and a\n * sync triggered that way republishes a generation nobody asked for. Comparing\n * content alone is correct but reads every input on every check, and the\n * cockpit polls this. Recording both lets the stat pair answer the common\n * \"nothing was touched\" case without opening a file, while the digest stays the\n * authority for anything whose stat moved.\n */\nexport interface InputFingerprint {\n path: string;\n size: number;\n mtimeMs: number;\n sha256: string;\n}\n\n/**\n * Verdicts already reached, keyed by both the receipt and the stats now on disk,\n * so one receipt cannot answer the freshness question for another.\n */\nconst freshnessVerdicts = new Map<string, boolean>();\n\nfunction digestOf(filePath: string): string | undefined {\n try {\n return crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');\n } catch {\n // Unreadable is indistinguishable from absent here: either way the recorded\n // digest cannot be confirmed, so the caller must treat the build as stale.\n return undefined;\n }\n}\n\n/** Records one build input, or undefined when it is not a readable regular file. */\nexport function fingerprintInput(target: string): InputFingerprint | undefined {\n let stat: fs.Stats;\n try {\n stat = fs.statSync(target);\n } catch {\n // A path that vanished between discovery and recording is simply not an\n // input; the compiler records the set it could read.\n return undefined;\n }\n if (!stat.isFile()) return undefined;\n const sha256 = digestOf(target);\n if (sha256 === undefined) return undefined;\n return { path: target, size: stat.size, mtimeMs: stat.mtimeMs, sha256 };\n}\n\n/** Parses one recorded input, rejecting a record written before digests existed. */\nexport function parseInputFingerprint(value: unknown): InputFingerprint | undefined {\n if (!isRecord(value)) return undefined;\n if (\n typeof value.path !== 'string' ||\n typeof value.size !== 'number' ||\n typeof value.mtimeMs !== 'number' ||\n typeof value.sha256 !== 'string'\n ) {\n return undefined;\n }\n return { path: value.path, size: value.size, mtimeMs: value.mtimeMs, sha256: value.sha256 };\n}\n\n/** Whether every recorded input still holds the bytes it was recorded with. */\nexport function inputsAreFresh(inputs: readonly InputFingerprint[]): boolean {\n const moved: InputFingerprint[] = [];\n const key = crypto.createHash('sha256');\n for (const input of inputs) {\n let stat: fs.Stats;\n try {\n stat = fs.statSync(input.path);\n } catch {\n // A recorded input that is gone cannot be confirmed, and no digest will\n // bring it back; the build it describes is stale.\n return false;\n }\n if (!stat.isFile()) return false;\n key.update(JSON.stringify([input.path, input.size, input.mtimeMs, input.sha256, stat.size, stat.mtimeMs]));\n if (stat.size !== input.size || stat.mtimeMs !== input.mtimeMs) moved.push(input);\n }\n if (moved.length === 0) return true;\n\n const cacheKey = key.digest('hex');\n const remembered = freshnessVerdicts.get(cacheKey);\n if (remembered !== undefined) return remembered;\n const fresh = moved.every((input) => digestOf(input.path) === input.sha256);\n freshnessVerdicts.set(cacheKey, fresh);\n return fresh;\n}\n\n/** Test seam: the memo is process-local and must not leak between cases. */\nexport function resetInputFreshnessCache(): void {\n freshnessVerdicts.clear();\n}\n"],"mappings":";;;;;;AAEA,SAAS,SAAS,OAAkD;CAClE,OAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;;;;;AAwBA,MAAM,oCAAoB,IAAI,IAAqB;AAEnD,SAAS,SAAS,UAAsC;CACtD,IAAI;EACF,OAAOA,YAAAA,QAAO,WAAW,QAAQ,CAAC,CAAC,OAAOC,QAAAA,QAAG,aAAa,QAAQ,CAAC,CAAC,CAAC,OAAO,KAAK;CACnF,QAAQ;EAGN;CACF;AACF;;AAGA,SAAgB,iBAAiB,QAA8C;CAC7E,IAAI;CACJ,IAAI;EACF,OAAOA,QAAAA,QAAG,SAAS,MAAM;CAC3B,QAAQ;EAGN;CACF;CACA,IAAI,CAAC,KAAK,OAAO,GAAG,OAAO,KAAA;CAC3B,MAAM,SAAS,SAAS,MAAM;CAC9B,IAAI,WAAW,KAAA,GAAW,OAAO,KAAA;CACjC,OAAO;EAAE,MAAM;EAAQ,MAAM,KAAK;EAAM,SAAS,KAAK;EAAS;CAAO;AACxE;;AAGA,SAAgB,sBAAsB,OAA8C;CAClF,IAAI,CAAC,SAAS,KAAK,GAAG,OAAO,KAAA;CAC7B,IACE,OAAO,MAAM,SAAS,YACtB,OAAO,MAAM,SAAS,YACtB,OAAO,MAAM,YAAY,YACzB,OAAO,MAAM,WAAW,UAExB;CAEF,OAAO;EAAE,MAAM,MAAM;EAAM,MAAM,MAAM;EAAM,SAAS,MAAM;EAAS,QAAQ,MAAM;CAAO;AAC5F;;AAGA,SAAgB,eAAe,QAA8C;CAC3E,MAAM,QAA4B,CAAC;CACnC,MAAM,MAAMD,YAAAA,QAAO,WAAW,QAAQ;CACtC,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI;EACJ,IAAI;GACF,OAAOC,QAAAA,QAAG,SAAS,MAAM,IAAI;EAC/B,QAAQ;GAGN,OAAO;EACT;EACA,IAAI,CAAC,KAAK,OAAO,GAAG,OAAO;EAC3B,IAAI,OAAO,KAAK,UAAU;GAAC,MAAM;GAAM,MAAM;GAAM,MAAM;GAAS,MAAM;GAAQ,KAAK;GAAM,KAAK;EAAO,CAAC,CAAC;EACzG,IAAI,KAAK,SAAS,MAAM,QAAQ,KAAK,YAAY,MAAM,SAAS,MAAM,KAAK,KAAK;CAClF;CACA,IAAI,MAAM,WAAW,GAAG,OAAO;CAE/B,MAAM,WAAW,IAAI,OAAO,KAAK;CACjC,MAAM,aAAa,kBAAkB,IAAI,QAAQ;CACjD,IAAI,eAAe,KAAA,GAAW,OAAO;CACrC,MAAM,QAAQ,MAAM,OAAO,UAAU,SAAS,MAAM,IAAI,MAAM,MAAM,MAAM;CAC1E,kBAAkB,IAAI,UAAU,KAAK;CACrC,OAAO;AACT"}
|
package/dist/compiler/inputs.mjs
CHANGED
|
@@ -5,12 +5,8 @@ function isRecord(value) {
|
|
|
5
5
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
|
-
* Verdicts already reached,
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* Keyed by the stats actually found on disk, so it can never answer for a set
|
|
12
|
-
* it did not see. Bounded by the compiler manifests in one composition, which
|
|
13
|
-
* is single digits.
|
|
8
|
+
* Verdicts already reached, keyed by both the receipt and the stats now on disk,
|
|
9
|
+
* so one receipt cannot answer the freshness question for another.
|
|
14
10
|
*/
|
|
15
11
|
const freshnessVerdicts = /* @__PURE__ */ new Map();
|
|
16
12
|
function digestOf(filePath) {
|
|
@@ -61,7 +57,14 @@ function inputsAreFresh(inputs) {
|
|
|
61
57
|
return false;
|
|
62
58
|
}
|
|
63
59
|
if (!stat.isFile()) return false;
|
|
64
|
-
key.update(
|
|
60
|
+
key.update(JSON.stringify([
|
|
61
|
+
input.path,
|
|
62
|
+
input.size,
|
|
63
|
+
input.mtimeMs,
|
|
64
|
+
input.sha256,
|
|
65
|
+
stat.size,
|
|
66
|
+
stat.mtimeMs
|
|
67
|
+
]));
|
|
65
68
|
if (stat.size !== input.size || stat.mtimeMs !== input.mtimeMs) moved.push(input);
|
|
66
69
|
}
|
|
67
70
|
if (moved.length === 0) return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inputs.mjs","names":[],"sources":["../../src/compiler/inputs.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * One recorded build input: the stat pair for speed, the digest for truth.\n *\n * Comparing only `size` and `mtimeMs` makes every rebuild look like a change,\n * including one that restored byte-identical output from a build cache, and a\n * sync triggered that way republishes a generation nobody asked for. Comparing\n * content alone is correct but reads every input on every check, and the\n * cockpit polls this. Recording both lets the stat pair answer the common\n * \"nothing was touched\" case without opening a file, while the digest stays the\n * authority for anything whose stat moved.\n */\nexport interface InputFingerprint {\n path: string;\n size: number;\n mtimeMs: number;\n sha256: string;\n}\n\n/**\n * Verdicts already reached,
|
|
1
|
+
{"version":3,"file":"inputs.mjs","names":[],"sources":["../../src/compiler/inputs.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * One recorded build input: the stat pair for speed, the digest for truth.\n *\n * Comparing only `size` and `mtimeMs` makes every rebuild look like a change,\n * including one that restored byte-identical output from a build cache, and a\n * sync triggered that way republishes a generation nobody asked for. Comparing\n * content alone is correct but reads every input on every check, and the\n * cockpit polls this. Recording both lets the stat pair answer the common\n * \"nothing was touched\" case without opening a file, while the digest stays the\n * authority for anything whose stat moved.\n */\nexport interface InputFingerprint {\n path: string;\n size: number;\n mtimeMs: number;\n sha256: string;\n}\n\n/**\n * Verdicts already reached, keyed by both the receipt and the stats now on disk,\n * so one receipt cannot answer the freshness question for another.\n */\nconst freshnessVerdicts = new Map<string, boolean>();\n\nfunction digestOf(filePath: string): string | undefined {\n try {\n return crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');\n } catch {\n // Unreadable is indistinguishable from absent here: either way the recorded\n // digest cannot be confirmed, so the caller must treat the build as stale.\n return undefined;\n }\n}\n\n/** Records one build input, or undefined when it is not a readable regular file. */\nexport function fingerprintInput(target: string): InputFingerprint | undefined {\n let stat: fs.Stats;\n try {\n stat = fs.statSync(target);\n } catch {\n // A path that vanished between discovery and recording is simply not an\n // input; the compiler records the set it could read.\n return undefined;\n }\n if (!stat.isFile()) return undefined;\n const sha256 = digestOf(target);\n if (sha256 === undefined) return undefined;\n return { path: target, size: stat.size, mtimeMs: stat.mtimeMs, sha256 };\n}\n\n/** Parses one recorded input, rejecting a record written before digests existed. */\nexport function parseInputFingerprint(value: unknown): InputFingerprint | undefined {\n if (!isRecord(value)) return undefined;\n if (\n typeof value.path !== 'string' ||\n typeof value.size !== 'number' ||\n typeof value.mtimeMs !== 'number' ||\n typeof value.sha256 !== 'string'\n ) {\n return undefined;\n }\n return { path: value.path, size: value.size, mtimeMs: value.mtimeMs, sha256: value.sha256 };\n}\n\n/** Whether every recorded input still holds the bytes it was recorded with. */\nexport function inputsAreFresh(inputs: readonly InputFingerprint[]): boolean {\n const moved: InputFingerprint[] = [];\n const key = crypto.createHash('sha256');\n for (const input of inputs) {\n let stat: fs.Stats;\n try {\n stat = fs.statSync(input.path);\n } catch {\n // A recorded input that is gone cannot be confirmed, and no digest will\n // bring it back; the build it describes is stale.\n return false;\n }\n if (!stat.isFile()) return false;\n key.update(JSON.stringify([input.path, input.size, input.mtimeMs, input.sha256, stat.size, stat.mtimeMs]));\n if (stat.size !== input.size || stat.mtimeMs !== input.mtimeMs) moved.push(input);\n }\n if (moved.length === 0) return true;\n\n const cacheKey = key.digest('hex');\n const remembered = freshnessVerdicts.get(cacheKey);\n if (remembered !== undefined) return remembered;\n const fresh = moved.every((input) => digestOf(input.path) === input.sha256);\n freshnessVerdicts.set(cacheKey, fresh);\n return fresh;\n}\n\n/** Test seam: the memo is process-local and must not leak between cases. */\nexport function resetInputFreshnessCache(): void {\n freshnessVerdicts.clear();\n}\n"],"mappings":";;;AAEA,SAAS,SAAS,OAAkD;CAClE,OAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;;;;;AAwBA,MAAM,oCAAoB,IAAI,IAAqB;AAEnD,SAAS,SAAS,UAAsC;CACtD,IAAI;EACF,OAAO,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,GAAG,aAAa,QAAQ,CAAC,CAAC,CAAC,OAAO,KAAK;CACnF,QAAQ;EAGN;CACF;AACF;;AAGA,SAAgB,iBAAiB,QAA8C;CAC7E,IAAI;CACJ,IAAI;EACF,OAAO,GAAG,SAAS,MAAM;CAC3B,QAAQ;EAGN;CACF;CACA,IAAI,CAAC,KAAK,OAAO,GAAG,OAAO,KAAA;CAC3B,MAAM,SAAS,SAAS,MAAM;CAC9B,IAAI,WAAW,KAAA,GAAW,OAAO,KAAA;CACjC,OAAO;EAAE,MAAM;EAAQ,MAAM,KAAK;EAAM,SAAS,KAAK;EAAS;CAAO;AACxE;;AAGA,SAAgB,sBAAsB,OAA8C;CAClF,IAAI,CAAC,SAAS,KAAK,GAAG,OAAO,KAAA;CAC7B,IACE,OAAO,MAAM,SAAS,YACtB,OAAO,MAAM,SAAS,YACtB,OAAO,MAAM,YAAY,YACzB,OAAO,MAAM,WAAW,UAExB;CAEF,OAAO;EAAE,MAAM,MAAM;EAAM,MAAM,MAAM;EAAM,SAAS,MAAM;EAAS,QAAQ,MAAM;CAAO;AAC5F;;AAGA,SAAgB,eAAe,QAA8C;CAC3E,MAAM,QAA4B,CAAC;CACnC,MAAM,MAAM,OAAO,WAAW,QAAQ;CACtC,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI;EACJ,IAAI;GACF,OAAO,GAAG,SAAS,MAAM,IAAI;EAC/B,QAAQ;GAGN,OAAO;EACT;EACA,IAAI,CAAC,KAAK,OAAO,GAAG,OAAO;EAC3B,IAAI,OAAO,KAAK,UAAU;GAAC,MAAM;GAAM,MAAM;GAAM,MAAM;GAAS,MAAM;GAAQ,KAAK;GAAM,KAAK;EAAO,CAAC,CAAC;EACzG,IAAI,KAAK,SAAS,MAAM,QAAQ,KAAK,YAAY,MAAM,SAAS,MAAM,KAAK,KAAK;CAClF;CACA,IAAI,MAAM,WAAW,GAAG,OAAO;CAE/B,MAAM,WAAW,IAAI,OAAO,KAAK;CACjC,MAAM,aAAa,kBAAkB,IAAI,QAAQ;CACjD,IAAI,eAAe,KAAA,GAAW,OAAO;CACrC,MAAM,QAAQ,MAAM,OAAO,UAAU,SAAS,MAAM,IAAI,MAAM,MAAM,MAAM;CAC1E,kBAAkB,IAAI,UAAU,KAAK;CACrC,OAAO;AACT"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/doompi",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.81",
|
|
4
4
|
"description": "Opinionated, composable Pi distribution for scoped agent tools, skills, MCP servers, and developer workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -277,27 +277,27 @@
|
|
|
277
277
|
"registry": "https://registry.npmjs.org/"
|
|
278
278
|
},
|
|
279
279
|
"dependencies": {
|
|
280
|
-
"@agimon-ai/doompi-autostop": "0.0.1-alpha.
|
|
281
|
-
"@agimon-ai/doompi-cache": "0.0.1-alpha.
|
|
282
|
-
"@agimon-ai/doompi-config": "0.0.1-alpha.
|
|
283
|
-
"@agimon-ai/doompi-core": "0.0.1-alpha.
|
|
284
|
-
"@agimon-ai/doompi-domain": "0.0.1-alpha.
|
|
285
|
-
"@agimon-ai/doompi-major-mode": "0.0.1-alpha.
|
|
286
|
-
"@agimon-ai/doompi-minor-mode": "0.0.1-alpha.
|
|
287
|
-
"@agimon-ai/doompi-notification": "0.0.1-alpha.
|
|
288
|
-
"@agimon-ai/doompi-profile": "0.0.1-alpha.
|
|
289
|
-
"@agimon-ai/doompi-session": "0.0.1-alpha.
|
|
290
|
-
"@agimon-ai/doompi-skill": "0.0.1-alpha.
|
|
291
|
-
"@agimon-ai/doompi-telemetry": "0.0.1-alpha.
|
|
292
|
-
"@agimon-ai/doompi-ui": "0.0.1-alpha.
|
|
293
|
-
"@agimon-ai/doompi-web-security": "0.0.1-alpha.
|
|
280
|
+
"@agimon-ai/doompi-autostop": "0.0.1-alpha.55",
|
|
281
|
+
"@agimon-ai/doompi-cache": "0.0.1-alpha.44",
|
|
282
|
+
"@agimon-ai/doompi-config": "0.0.1-alpha.77",
|
|
283
|
+
"@agimon-ai/doompi-core": "0.0.1-alpha.78",
|
|
284
|
+
"@agimon-ai/doompi-domain": "0.0.1-alpha.56",
|
|
285
|
+
"@agimon-ai/doompi-major-mode": "0.0.1-alpha.56",
|
|
286
|
+
"@agimon-ai/doompi-minor-mode": "0.0.1-alpha.78",
|
|
287
|
+
"@agimon-ai/doompi-notification": "0.0.1-alpha.55",
|
|
288
|
+
"@agimon-ai/doompi-profile": "0.0.1-alpha.56",
|
|
289
|
+
"@agimon-ai/doompi-session": "0.0.1-alpha.2",
|
|
290
|
+
"@agimon-ai/doompi-skill": "0.0.1-alpha.56",
|
|
291
|
+
"@agimon-ai/doompi-telemetry": "0.0.1-alpha.75",
|
|
292
|
+
"@agimon-ai/doompi-ui": "0.0.1-alpha.78",
|
|
293
|
+
"@agimon-ai/doompi-web-security": "0.0.1-alpha.38",
|
|
294
294
|
"@deepseek-ai/cordis": "4.0.2",
|
|
295
|
-
"@earendil-works/chord": "0.
|
|
296
|
-
"@earendil-works/pi-agent-core": "0.
|
|
297
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
298
|
-
"@earendil-works/pi-protocol": "0.
|
|
299
|
-
"@earendil-works/pi-server": "0.
|
|
300
|
-
"@earendil-works/pi-session-backend-sqlite-node": "0.
|
|
295
|
+
"@earendil-works/chord": "0.86.0",
|
|
296
|
+
"@earendil-works/pi-agent-core": "0.86.0",
|
|
297
|
+
"@earendil-works/pi-coding-agent": "0.86.0",
|
|
298
|
+
"@earendil-works/pi-protocol": "0.86.0",
|
|
299
|
+
"@earendil-works/pi-server": "0.86.0",
|
|
300
|
+
"@earendil-works/pi-session-backend-sqlite-node": "0.86.0",
|
|
301
301
|
"@hono/node-server": "2.1.1",
|
|
302
302
|
"@simplewebauthn/server": "^14.0.1",
|
|
303
303
|
"@tailwindcss/vite": "4.3.3",
|
|
@@ -315,11 +315,11 @@
|
|
|
315
315
|
"yaml": "2.9.0"
|
|
316
316
|
},
|
|
317
317
|
"devDependencies": {
|
|
318
|
-
"@agimon-ai/doompi-runner": "0.0.1-alpha.
|
|
319
|
-
"@agimon-ai/vibe-lint-plugin-doom-cli": "0.0.1-alpha.
|
|
320
|
-
"@earendil-works/pi-ai": "0.
|
|
321
|
-
"@earendil-works/pi-client": "0.
|
|
322
|
-
"@earendil-works/pi-tui": "0.
|
|
318
|
+
"@agimon-ai/doompi-runner": "0.0.1-alpha.78",
|
|
319
|
+
"@agimon-ai/vibe-lint-plugin-doom-cli": "0.0.1-alpha.10",
|
|
320
|
+
"@earendil-works/pi-ai": "0.86.0",
|
|
321
|
+
"@earendil-works/pi-client": "0.86.0",
|
|
322
|
+
"@earendil-works/pi-tui": "0.86.0",
|
|
323
323
|
"@types/cross-spawn": "6.0.6",
|
|
324
324
|
"@types/node": "26.5.1",
|
|
325
325
|
"@types/ws": "8.18.1",
|
|
@@ -329,7 +329,7 @@
|
|
|
329
329
|
"vitest": "5.0.0"
|
|
330
330
|
},
|
|
331
331
|
"peerDependencies": {
|
|
332
|
-
"@earendil-works/pi-tui": "0.
|
|
332
|
+
"@earendil-works/pi-tui": "0.86.0"
|
|
333
333
|
},
|
|
334
334
|
"peerDependenciesMeta": {
|
|
335
335
|
"@earendil-works/pi-tui": {
|