@agimon-ai/doompi 0.0.1-alpha.34 → 0.0.1-alpha.35
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 +7 -5
- package/dist/src/adapters/layerPackageInstaller.cjs +2 -1
- package/dist/src/adapters/layerPackageInstaller.cjs.map +1 -1
- package/dist/src/adapters/layerPackageInstaller.mjs +2 -1
- package/dist/src/adapters/layerPackageInstaller.mjs.map +1 -1
- package/dist/src/commands/cli/help.cjs +2 -1
- package/dist/src/commands/cli/help.cjs.map +1 -1
- package/dist/src/commands/cli/help.mjs +2 -1
- package/dist/src/commands/cli/help.mjs.map +1 -1
- package/dist/src/commands/syncCommand.cjs +5 -5
- package/dist/src/commands/syncCommand.cjs.map +1 -1
- package/dist/src/commands/syncCommand.d.cts +2 -1
- package/dist/src/commands/syncCommand.d.cts.map +1 -1
- package/dist/src/commands/syncCommand.d.mts +2 -1
- package/dist/src/commands/syncCommand.d.mts.map +1 -1
- package/dist/src/commands/syncCommand.mjs +4 -4
- package/dist/src/commands/syncCommand.mjs.map +1 -1
- package/dist/src/commands/syncPipeline.cjs +1 -1
- package/dist/src/commands/syncPipeline.cjs.map +1 -1
- package/dist/src/commands/syncPipeline.mjs +1 -1
- package/dist/src/commands/syncPipeline.mjs.map +1 -1
- package/dist/src/commands/syncPresenter.cjs +2 -0
- package/dist/src/commands/syncPresenter.cjs.map +1 -0
- package/dist/src/commands/syncPresenter.d.cts +17 -0
- package/dist/src/commands/syncPresenter.d.cts.map +1 -0
- package/dist/src/commands/syncPresenter.d.mts +17 -0
- package/dist/src/commands/syncPresenter.d.mts.map +1 -0
- package/dist/src/commands/syncPresenter.mjs +2 -0
- package/dist/src/commands/syncPresenter.mjs.map +1 -0
- package/package.json +13 -13
package/README.md
CHANGED
|
@@ -452,8 +452,8 @@ you want to restart one.
|
|
|
452
452
|
#### Workflow mode
|
|
453
453
|
|
|
454
454
|
[`@agimon-ai/doompi-workflow`][pkg-doompi-workflow] runs GitHub Actions-style job graphs with
|
|
455
|
-
dependencies, timeouts, artifacts, and a separate DoomPi session for each step. Use `SPC w
|
|
456
|
-
|
|
455
|
+
dependencies, timeouts, artifacts, and a separate DoomPi session for each step. Use `SPC w l` to
|
|
456
|
+
browse the repository's workflows and launch one with `r`, `SPC w r` to inspect this session's runs,
|
|
457
457
|
`SPC w c` to recover a failed run, and `SPC w e` to give the agent workflow tools or take them back. It is for work that needs hard job boundaries and explicit
|
|
458
458
|
handoffs rather than one long conversation.
|
|
459
459
|
|
|
@@ -506,9 +506,11 @@ baseline. The fixed host core remains active independently of this setting, and
|
|
|
506
506
|
keep their existing behavior.
|
|
507
507
|
|
|
508
508
|
Launch installs missing required packages from the defaults and selected layers. `doompi sync`
|
|
509
|
-
installs missing required packages from the defaults and every declared layer,
|
|
510
|
-
|
|
511
|
-
|
|
509
|
+
installs missing required packages from the defaults and every declared layer, moves every
|
|
510
|
+
package in Pi's managed `.pi/npm` store to its newest published version, then builds the
|
|
511
|
+
synchronized matrix. A package whose published version cannot be read, because the machine is
|
|
512
|
+
offline or the registry refuses the request, keeps its installed version and is named in the
|
|
513
|
+
sync output. `doompi sync --check` reports missing packages without modifying the repository. Optional packages and local paths are not installed automatically. Removing an
|
|
512
514
|
entry stops activating it but leaves Pi's package cache available for later reuse.
|
|
513
515
|
|
|
514
516
|
Packages in `default` and named layers may be bare strings, or mappings when the package accepts
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("./modules/moduleResolution.cjs"),n=require("../services/extensionAssembler.cjs"),r=require("./piSettings.cjs");let i=require("node:path");i=e.__toESM(i,1);let a=require("node:fs");a=e.__toESM(a,1);let o=require("@agimon-ai/doompi-config/majorModes"),s=require("@earendil-works/pi-coding-agent");const
|
|
1
|
+
const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("./modules/moduleResolution.cjs"),n=require("../services/extensionAssembler.cjs"),r=require("./piSettings.cjs");let i=require("node:path");i=e.__toESM(i,1);let a=require("node:fs");a=e.__toESM(a,1);let o=require("@agimon-ai/doompi-config/majorModes"),s=require("node:child_process"),c=require("node:util"),l=require("@earendil-works/pi-coding-agent");const u=`npm:`,d=i.default.join(`.pi`,`npm`),f=i.default.join(d,`node_modules`),p=`package.json`,m=(0,c.promisify)(s.execFile);function h(e){let t=e.indexOf(`@`,+!!e.startsWith(`@`));return t===-1?e:e.slice(0,t)}const g={"@hono/node-server@2.0.0 - 2.0.9":`^2.0.10`,"@opentelemetry/core@2.0.0 - 2.7.999":`2.8.0`,"brace-expansion@5.0.0 - 5.0.8":`^5.0.9`,"hono@4.0.0 - 4.12.33":`^4.12.34`,"js-yaml@4.0.0 - 4.3.0":`^4.3.1`,"js-yaml@5.0.0 - 5.2.1":`^5.2.2`,"liquidjs@10.0.0 - 10.27.0":`^10.27.1`,"protobufjs@8.0.0 - 8.7.1":`^8.7.2`},_=new Set(Object.keys(g).map(h));function v(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function y(e){return v(e)?{...e}:{}}function b(e){let t=y(e);for(let[e,n]of Object.entries(t))_.has(h(e))?delete t[e]:v(n)&&(t[e]=b(n));return t}function x(e){return i.default.join(e,d,p)}function S(e,t){let n=new Set,r=[...e.default?[e.default]:[],...t.map(t=>{let n=e.layers[t];if(!n)throw Error(`Unknown layer: ${t}`);return n})];for(let e of r)for(let t of e.packages??[]){let e=typeof t==`string`?t:t.name;(typeof t==`string`||t.optional!==!0)&&!(0,o.isLocalPackageSpecifier)(e)&&n.add(e)}return[...n]}function C(e,t){let n=e.optionalPackageEntries?.(t);if(n!==void 0)return n;let r=e.optionalPackageEntry(t);return r?[r]:void 0}function w(e,t,n=new Map){let r=x(e),o=a.default.existsSync(r)?a.default.readFileSync(r,`utf8`):void 0,s;if(o===void 0)s={name:`pi-extensions`,private:!0};else{let e=JSON.parse(o);if(!v(e))throw Error(`Pi managed package manifest at ${r} must be a JSON object.`);s=e}let c=y(s.dependencies);for(let e of t){let t=E(e),r=n.get(t);r===void 0?Object.hasOwn(c,t)||(c[t]=`*`):c[t]=r}s.dependencies=c,s.overrides={...b(s.overrides),...g};let l=y(s.pnpm);l.overrides={...b(l.overrides),...g},s.pnpm=l;let u=`${JSON.stringify(s,null,2)}\n`;return u!==o&&(a.default.mkdirSync(i.default.dirname(r),{recursive:!0}),a.default.writeFileSync(r,u,`utf8`),!0)}function T(e,t,n){return S(e,t).filter(e=>C(n,e)===void 0)}function E(e){return e.startsWith(u)?e.slice(4):e}function D(e){return[...new Set(e.map(e=>`${u}${t.splitPackageSpecifier(e).name}`))]}function O(e,t){return`${e} ${t}${e===1?``:`s`}`}function k(e,t){let n=i.default.join(e,f,t,p);if(a.default.existsSync(n))try{let e=JSON.parse(a.default.readFileSync(n,`utf8`));return v(e)&&typeof e.version==`string`?e.version:void 0}catch{return}}function A(e,t){let n=r.piAgentDirectory(t),i=l.SettingsManager.create(e,n,{projectTrusted:!0});return new l.DefaultPackageManager({cwd:e,agentDir:n,settingsManager:i})}function j(e,t){let[n,...i]=l.SettingsManager.create(e,r.piAgentDirectory(t),{projectTrusted:!0}).getNpmCommand()??[],a=n??`npm`,o=n?i:[];return async t=>{let{stdout:n}=await m(a,[...o,`view`,t,`version`,`--json`],{cwd:e,timeout:2e4,encoding:`utf8`}),r=JSON.parse(n.trim()||`null`);if(typeof r!=`string`)throw Error(`${a} view reported no published version`);return r}}async function M(e,t,n,r){let i=t.map(t=>({name:E(t),from:k(e,E(t))})).filter(e=>e.from!==void 0),a=await Promise.all(i.map(async e=>{try{return{...e,to:await n(e.name)}}catch(t){return{...e,failure:(t instanceof Error?t.message:String(t)).split(`
|
|
2
|
+
`)[0]}}})),o=[],s=[];for(let e of a){if(e.to===void 0){s.push(e.name),r?.(`kept ${e.name} ${e.from}: ${e.failure??`no published version`}`);continue}e.to!==e.from&&(o.push({name:e.name,from:e.from,to:e.to}),r?.(`${e.name} ${e.from} -> ${e.to}`))}return{updated:o,unchecked:s}}async function N(e,t={}){let r=e.environment??process.env,o=t.resolvers??n.createLayerResolvers(e.repoRoot),s=S(e.config,e.layers),c=s.filter(e=>C(o,e)===void 0),l=D(c),u=D(s);if(u.length===0)return{installed:[],updated:[],unchecked:[]};let d=i.default.dirname(x(e.repoRoot));if(c.length===0&&!a.default.existsSync(d))return{installed:[],updated:[],unchecked:[]};let{updated:f,unchecked:p}=e.refresh?await M(e.repoRoot,u,t.publishedVersion??j(e.repoRoot,r),e.onProgress):{updated:[],unchecked:[]},m=w(e.repoRoot,u,new Map(f.map(e=>[e.name,e.to])));if(c.length===0&&!m)return{installed:[],updated:[],unchecked:p};let h=t.packageManager??A(e.repoRoot,r);e.onProgress?.(`installing ${f.length>0?O(f.length,`update`):c.length>0?O(c.length,`missing package`):`the package manifest`}`);try{m?await h.install(u[0],{local:!0}):await h.resolveExtensionSources(l,{local:!0})}catch(e){throw Error(`Failed to reconcile DoomPi layer package(s): ${(m?u:l).join(`, `)}`,{cause:e})}let g=c.filter(e=>!C(o,e)?.length);if(g.length>0)throw Error(`Installed DoomPi layer package(s) did not expose the configured Pi extension: ${g.join(`, `)}`);return{installed:l,updated:f,unchecked:p}}exports.SAFE_TRANSITIVE_OVERRIDES=g,exports.ensureLayerPackages=N,exports.ensureManagedPackageManifest=w,exports.missingLayerPackageSpecifiers=T;
|
|
2
3
|
//# sourceMappingURL=layerPackageInstaller.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layerPackageInstaller.cjs","names":["path","isLocalPackageSpecifier","fs","splitPackageSpecifier","piAgentDirectory","SettingsManager","DefaultPackageManager","createLayerResolvers"],"sources":["../../../src/adapters/layerPackageInstaller.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { isLocalPackageSpecifier, type MajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { DefaultPackageManager, type PackageManager, SettingsManager } from '@earendil-works/pi-coding-agent';\nimport { createLayerResolvers, type ExtensionLayerResolvers } from '../services/extensionAssembler.ts';\nimport { splitPackageSpecifier } from './modules/moduleResolution.ts';\nimport { piAgentDirectory } from './piSettings.ts';\n\nconst NPM_SOURCE_PREFIX = 'npm:';\nconst PI_MANAGED_NPM_DIRECTORY = path.join('.pi', 'npm');\nconst PI_MANAGED_PACKAGE_NAME = 'pi-extensions';\nconst UNPINNED_PACKAGE_RANGE = '*';\n\nfunction overridePackageName(selector: string): string {\n const separatorIndex = selector.indexOf('@', selector.startsWith('@') ? 1 : 0);\n return separatorIndex === -1 ? selector : selector.slice(0, separatorIndex);\n}\n\n/**\n * Same-major patches for transitive versions pinned by the current foundation packages.\n * npm only honors overrides from the install root, so published extensions cannot carry\n * these fixes in their own manifests.\n */\nexport const SAFE_TRANSITIVE_OVERRIDES = {\n '@hono/node-server@2.0.0 - 2.0.9': '^2.0.10',\n '@opentelemetry/core@2.0.0 - 2.7.999': '2.8.0',\n 'brace-expansion@5.0.0 - 5.0.8': '^5.0.9',\n 'hono@4.0.0 - 4.12.33': '^4.12.34',\n 'js-yaml@4.0.0 - 4.3.0': '^4.3.1',\n 'js-yaml@5.0.0 - 5.2.1': '^5.2.2',\n 'liquidjs@10.0.0 - 10.27.0': '^10.27.1',\n 'protobufjs@8.0.0 - 8.7.1': '^8.7.2',\n} as const satisfies Readonly<Record<string, string>>;\n\nconst SAFE_TRANSITIVE_PACKAGE_NAMES = new Set(Object.keys(SAFE_TRANSITIVE_OVERRIDES).map(overridePackageName));\n\ntype LayerPackageManager = Pick<PackageManager, 'install' | 'resolveExtensionSources'>;\n\nexport interface EnsureLayerPackagesOptions {\n repoRoot: string;\n config: MajorModesConfig;\n layers: readonly string[];\n environment?: NodeJS.ProcessEnv;\n}\n\nexport interface LayerPackageInstallerDependencies {\n packageManager?: LayerPackageManager;\n resolvers?: ExtensionLayerResolvers;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction recordValue(value: unknown): Record<string, unknown> {\n return isRecord(value) ? { ...value } : {};\n}\n\nfunction withoutConflictingTransitiveOverrides(value: unknown): Record<string, unknown> {\n const overrides = recordValue(value);\n for (const [selector, replacement] of Object.entries(overrides)) {\n if (SAFE_TRANSITIVE_PACKAGE_NAMES.has(overridePackageName(selector))) {\n delete overrides[selector];\n } else if (isRecord(replacement)) {\n overrides[selector] = withoutConflictingTransitiveOverrides(replacement);\n }\n }\n return overrides;\n}\n\nfunction managedPackageManifestPath(repoRoot: string): string {\n return path.join(repoRoot, PI_MANAGED_NPM_DIRECTORY, 'package.json');\n}\n\nfunction configuredPackageSpecifiers(config: MajorModesConfig, layers: readonly string[]): string[] {\n const specifiers = new Set<string>();\n const selectedDefinitions = [\n ...(config.default ? [config.default] : []),\n ...layers.map((layerName) => {\n const layer = config.layers[layerName];\n if (!layer) throw new Error(`Unknown layer: ${layerName}`);\n return layer;\n }),\n ];\n for (const layer of selectedDefinitions) {\n for (const configured of layer.packages ?? []) {\n const specifier = typeof configured === 'string' ? configured : configured.name;\n const optional = typeof configured !== 'string' && configured.optional === true;\n if (!optional && !isLocalPackageSpecifier(specifier)) specifiers.add(specifier);\n }\n }\n return [...specifiers];\n}\n\nfunction resolvePackageEntries(resolvers: ExtensionLayerResolvers, specifier: string): string[] | undefined {\n const entries = resolvers.optionalPackageEntries?.(specifier);\n if (entries !== undefined) return entries;\n const entry = resolvers.optionalPackageEntry(specifier);\n return entry ? [entry] : undefined;\n}\n\n/** Adds the complete package set and patched transitive policy to Pi's managed install root. */\nexport function ensureManagedPackageManifest(repoRoot: string, sources: readonly string[]): boolean {\n const manifestPath = managedPackageManifestPath(repoRoot);\n const current = fs.existsSync(manifestPath) ? fs.readFileSync(manifestPath, 'utf8') : undefined;\n let manifest: Record<string, unknown>;\n if (current === undefined) {\n manifest = { name: PI_MANAGED_PACKAGE_NAME, private: true };\n } else {\n const parsed: unknown = JSON.parse(current);\n if (!isRecord(parsed)) throw new Error(`Pi managed package manifest at ${manifestPath} must be a JSON object.`);\n manifest = parsed;\n }\n\n const dependencies = recordValue(manifest.dependencies);\n for (const source of sources) {\n const name = source.slice(NPM_SOURCE_PREFIX.length);\n if (!Object.hasOwn(dependencies, name)) dependencies[name] = UNPINNED_PACKAGE_RANGE;\n }\n manifest.dependencies = dependencies;\n // npm gives nested and overlapping consumer rules precedence in cases that can retain\n // vulnerable versions, so DoomPi owns these package-specific selectors recursively.\n manifest.overrides = {\n ...withoutConflictingTransitiveOverrides(manifest.overrides),\n ...SAFE_TRANSITIVE_OVERRIDES,\n };\n const pnpm = recordValue(manifest.pnpm);\n pnpm.overrides = {\n ...withoutConflictingTransitiveOverrides(pnpm.overrides),\n ...SAFE_TRANSITIVE_OVERRIDES,\n };\n manifest.pnpm = pnpm;\n\n const serialized = `${JSON.stringify(manifest, null, 2)}\\n`;\n if (serialized === current) return false;\n fs.mkdirSync(path.dirname(manifestPath), { recursive: true });\n fs.writeFileSync(manifestPath, serialized, 'utf8');\n return true;\n}\n\n/** Finds required npm packages selected by defaults and active layers that DoomPi cannot resolve. */\nexport function missingLayerPackageSpecifiers(\n config: MajorModesConfig,\n layers: readonly string[],\n resolvers: ExtensionLayerResolvers,\n): string[] {\n return configuredPackageSpecifiers(config, layers).filter(\n (specifier) => resolvePackageEntries(resolvers, specifier) === undefined,\n );\n}\n\nfunction npmSources(specifiers: readonly string[]): string[] {\n return [...new Set(specifiers.map((specifier) => `${NPM_SOURCE_PREFIX}${splitPackageSpecifier(specifier).name}`))];\n}\n\nfunction createPackageManager(repoRoot: string, environment: NodeJS.ProcessEnv): LayerPackageManager {\n const agentDirectory = piAgentDirectory(environment);\n const settingsManager = SettingsManager.create(repoRoot, agentDirectory, { projectTrusted: true });\n return new DefaultPackageManager({ cwd: repoRoot, agentDir: agentDirectory, settingsManager });\n}\n\n/** Installs missing selected layer packages into Pi's project-local `.pi/npm` store and verifies the retry. */\nexport async function ensureLayerPackages(\n options: EnsureLayerPackagesOptions,\n dependencies: LayerPackageInstallerDependencies = {},\n): Promise<string[]> {\n const environment = options.environment ?? process.env;\n const resolvers = dependencies.resolvers ?? createLayerResolvers(options.repoRoot);\n const configured = configuredPackageSpecifiers(options.config, options.layers);\n const missing = configured.filter((specifier) => resolvePackageEntries(resolvers, specifier) === undefined);\n const missingSources = npmSources(missing);\n const sources = npmSources(configured);\n if (sources.length === 0) return [];\n\n const managedDirectory = path.dirname(managedPackageManifestPath(options.repoRoot));\n if (missing.length === 0 && !fs.existsSync(managedDirectory)) return [];\n const manifestChanged = ensureManagedPackageManifest(options.repoRoot, sources);\n if (missing.length === 0 && !manifestChanged) return [];\n\n const packageManager = dependencies.packageManager ?? createPackageManager(options.repoRoot, environment);\n try {\n if (manifestChanged) {\n // Installing one source reconciles the complete dependency set already written\n // to package.json, avoiding one npm install and audit report per DoomPi package.\n await packageManager.install(sources[0]!, { local: true });\n } else {\n await packageManager.resolveExtensionSources(missingSources, { local: true });\n }\n } catch (error) {\n const attempted = manifestChanged ? sources : missingSources;\n throw new Error(`Failed to reconcile DoomPi layer package(s): ${attempted.join(', ')}`, { cause: error });\n }\n\n const unresolved = missing.filter((specifier) => !resolvePackageEntries(resolvers, specifier)?.length);\n if (unresolved.length > 0) {\n throw new Error(\n `Installed DoomPi layer package(s) did not expose the configured Pi extension: ${unresolved.join(', ')}`,\n );\n }\n return missingSources;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"layerPackageInstaller.cjs","names":["path","promisify","execFile","isLocalPackageSpecifier","fs","splitPackageSpecifier","piAgentDirectory","SettingsManager","DefaultPackageManager","createLayerResolvers"],"sources":["../../../src/adapters/layerPackageInstaller.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { promisify } from 'node:util';\nimport { isLocalPackageSpecifier, type MajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { DefaultPackageManager, type PackageManager, SettingsManager } from '@earendil-works/pi-coding-agent';\nimport { createLayerResolvers, type ExtensionLayerResolvers } from '../services/extensionAssembler.ts';\nimport { splitPackageSpecifier } from './modules/moduleResolution.ts';\nimport { piAgentDirectory } from './piSettings.ts';\n\nconst NPM_SOURCE_PREFIX = 'npm:';\nconst PI_MANAGED_NPM_DIRECTORY = path.join('.pi', 'npm');\nconst PI_MANAGED_MODULES_DIRECTORY = path.join(PI_MANAGED_NPM_DIRECTORY, 'node_modules');\nconst PI_MANAGED_PACKAGE_NAME = 'pi-extensions';\nconst UNPINNED_PACKAGE_RANGE = '*';\nconst PACKAGE_MANIFEST = 'package.json';\nconst DEFAULT_NPM_COMMAND = 'npm';\nconst REGISTRY_TIMEOUT_MS = 20_000;\n\nconst execFileAsync = promisify(execFile);\n\nfunction overridePackageName(selector: string): string {\n const separatorIndex = selector.indexOf('@', selector.startsWith('@') ? 1 : 0);\n return separatorIndex === -1 ? selector : selector.slice(0, separatorIndex);\n}\n\n/**\n * Same-major patches for transitive versions pinned by the current foundation packages.\n * npm only honors overrides from the install root, so published extensions cannot carry\n * these fixes in their own manifests.\n */\nexport const SAFE_TRANSITIVE_OVERRIDES = {\n '@hono/node-server@2.0.0 - 2.0.9': '^2.0.10',\n '@opentelemetry/core@2.0.0 - 2.7.999': '2.8.0',\n 'brace-expansion@5.0.0 - 5.0.8': '^5.0.9',\n 'hono@4.0.0 - 4.12.33': '^4.12.34',\n 'js-yaml@4.0.0 - 4.3.0': '^4.3.1',\n 'js-yaml@5.0.0 - 5.2.1': '^5.2.2',\n 'liquidjs@10.0.0 - 10.27.0': '^10.27.1',\n 'protobufjs@8.0.0 - 8.7.1': '^8.7.2',\n} as const satisfies Readonly<Record<string, string>>;\n\nconst SAFE_TRANSITIVE_PACKAGE_NAMES = new Set(Object.keys(SAFE_TRANSITIVE_OVERRIDES).map(overridePackageName));\n\ntype LayerPackageManager = Pick<PackageManager, 'install' | 'resolveExtensionSources'>;\n\nexport interface EnsureLayerPackagesOptions {\n repoRoot: string;\n config: MajorModesConfig;\n layers: readonly string[];\n environment?: NodeJS.ProcessEnv;\n /**\n * Move every managed package to its newest published version.\n *\n * Launch installs what a selection needs and nothing more, so a session never\n * pays for a registry round trip. Sync is the explicit maintenance command and\n * is the only caller that asks for this.\n */\n refresh?: boolean;\n /** Receives one human-readable line per package decision. */\n onProgress?: (message: string) => void;\n}\n\nexport interface LayerPackageInstallerDependencies {\n packageManager?: LayerPackageManager;\n resolvers?: ExtensionLayerResolvers;\n /** Registry lookup seam; the default reads the newest version with Pi's npm client. */\n publishedVersion?: (name: string) => Promise<string>;\n}\n\n/** One managed package moving from the installed version to the newest published one. */\nexport interface LayerPackageUpdate {\n name: string;\n from: string;\n to: string;\n}\n\nexport interface LayerPackageResult {\n /** Sources installed because a configured extension could not be resolved. */\n installed: string[];\n /** Packages this run moved to a newer published version. */\n updated: LayerPackageUpdate[];\n /** Packages left at the installed version because the registry could not be read. */\n unchecked: string[];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction recordValue(value: unknown): Record<string, unknown> {\n return isRecord(value) ? { ...value } : {};\n}\n\nfunction withoutConflictingTransitiveOverrides(value: unknown): Record<string, unknown> {\n const overrides = recordValue(value);\n for (const [selector, replacement] of Object.entries(overrides)) {\n if (SAFE_TRANSITIVE_PACKAGE_NAMES.has(overridePackageName(selector))) {\n delete overrides[selector];\n } else if (isRecord(replacement)) {\n overrides[selector] = withoutConflictingTransitiveOverrides(replacement);\n }\n }\n return overrides;\n}\n\nfunction managedPackageManifestPath(repoRoot: string): string {\n return path.join(repoRoot, PI_MANAGED_NPM_DIRECTORY, PACKAGE_MANIFEST);\n}\n\nfunction configuredPackageSpecifiers(config: MajorModesConfig, layers: readonly string[]): string[] {\n const specifiers = new Set<string>();\n const selectedDefinitions = [\n ...(config.default ? [config.default] : []),\n ...layers.map((layerName) => {\n const layer = config.layers[layerName];\n if (!layer) throw new Error(`Unknown layer: ${layerName}`);\n return layer;\n }),\n ];\n for (const layer of selectedDefinitions) {\n for (const configured of layer.packages ?? []) {\n const specifier = typeof configured === 'string' ? configured : configured.name;\n const optional = typeof configured !== 'string' && configured.optional === true;\n if (!optional && !isLocalPackageSpecifier(specifier)) specifiers.add(specifier);\n }\n }\n return [...specifiers];\n}\n\nfunction resolvePackageEntries(resolvers: ExtensionLayerResolvers, specifier: string): string[] | undefined {\n const entries = resolvers.optionalPackageEntries?.(specifier);\n if (entries !== undefined) return entries;\n const entry = resolvers.optionalPackageEntry(specifier);\n return entry ? [entry] : undefined;\n}\n\n/** Adds the complete package set and patched transitive policy to Pi's managed install root. */\nexport function ensureManagedPackageManifest(\n repoRoot: string,\n sources: readonly string[],\n targets: ReadonlyMap<string, string> = new Map(),\n): boolean {\n const manifestPath = managedPackageManifestPath(repoRoot);\n const current = fs.existsSync(manifestPath) ? fs.readFileSync(manifestPath, 'utf8') : undefined;\n let manifest: Record<string, unknown>;\n if (current === undefined) {\n manifest = { name: PI_MANAGED_PACKAGE_NAME, private: true };\n } else {\n const parsed: unknown = JSON.parse(current);\n if (!isRecord(parsed)) throw new Error(`Pi managed package manifest at ${manifestPath} must be a JSON object.`);\n manifest = parsed;\n }\n\n const dependencies = recordValue(manifest.dependencies);\n for (const source of sources) {\n const name = packageName(source);\n const target = targets.get(name);\n // An exact target is what makes an update reach the store: npm keeps a\n // locked version whenever the recorded range still admits it, and the range\n // it writes back after an install always admits the version it just wrote.\n if (target !== undefined) dependencies[name] = target;\n else if (!Object.hasOwn(dependencies, name)) dependencies[name] = UNPINNED_PACKAGE_RANGE;\n }\n manifest.dependencies = dependencies;\n // npm gives nested and overlapping consumer rules precedence in cases that can retain\n // vulnerable versions, so DoomPi owns these package-specific selectors recursively.\n manifest.overrides = {\n ...withoutConflictingTransitiveOverrides(manifest.overrides),\n ...SAFE_TRANSITIVE_OVERRIDES,\n };\n const pnpm = recordValue(manifest.pnpm);\n pnpm.overrides = {\n ...withoutConflictingTransitiveOverrides(pnpm.overrides),\n ...SAFE_TRANSITIVE_OVERRIDES,\n };\n manifest.pnpm = pnpm;\n\n const serialized = `${JSON.stringify(manifest, null, 2)}\\n`;\n if (serialized === current) return false;\n fs.mkdirSync(path.dirname(manifestPath), { recursive: true });\n fs.writeFileSync(manifestPath, serialized, 'utf8');\n return true;\n}\n\n/** Finds required npm packages selected by defaults and active layers that DoomPi cannot resolve. */\nexport function missingLayerPackageSpecifiers(\n config: MajorModesConfig,\n layers: readonly string[],\n resolvers: ExtensionLayerResolvers,\n): string[] {\n return configuredPackageSpecifiers(config, layers).filter(\n (specifier) => resolvePackageEntries(resolvers, specifier) === undefined,\n );\n}\n\nfunction packageName(source: string): string {\n return source.startsWith(NPM_SOURCE_PREFIX) ? source.slice(NPM_SOURCE_PREFIX.length) : source;\n}\n\nfunction npmSources(specifiers: readonly string[]): string[] {\n return [...new Set(specifiers.map((specifier) => `${NPM_SOURCE_PREFIX}${splitPackageSpecifier(specifier).name}`))];\n}\n\nfunction pluralize(count: number, noun: string): string {\n return `${count} ${noun}${count === 1 ? '' : 's'}`;\n}\n\n/** Version of a package inside Pi's managed store, absent when sync does not own it. */\nfunction managedPackageVersion(repoRoot: string, name: string): string | undefined {\n const manifestPath = path.join(repoRoot, PI_MANAGED_MODULES_DIRECTORY, name, PACKAGE_MANIFEST);\n if (!fs.existsSync(manifestPath)) return undefined;\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));\n return isRecord(parsed) && typeof parsed.version === 'string' ? parsed.version : undefined;\n } catch {\n // An unreadable manifest carries the same meaning as an absent one: nothing\n // usable is installed, so the caller treats the package as uninstalled.\n return undefined;\n }\n}\n\nfunction createPackageManager(repoRoot: string, environment: NodeJS.ProcessEnv): LayerPackageManager {\n const agentDirectory = piAgentDirectory(environment);\n const settingsManager = SettingsManager.create(repoRoot, agentDirectory, { projectTrusted: true });\n return new DefaultPackageManager({ cwd: repoRoot, agentDir: agentDirectory, settingsManager });\n}\n\n/** Reads newest published versions with the same client Pi installs them with. */\nfunction createVersionReader(repoRoot: string, environment: NodeJS.ProcessEnv): (name: string) => Promise<string> {\n const configured = SettingsManager.create(repoRoot, piAgentDirectory(environment), {\n projectTrusted: true,\n }).getNpmCommand();\n const [command, ...args] = configured ?? [];\n const client = command ?? DEFAULT_NPM_COMMAND;\n const clientArguments = command ? args : [];\n return async (name) => {\n const { stdout } = await execFileAsync(client, [...clientArguments, 'view', name, 'version', '--json'], {\n cwd: repoRoot,\n timeout: REGISTRY_TIMEOUT_MS,\n encoding: 'utf8',\n });\n const parsed: unknown = JSON.parse(stdout.trim() || 'null');\n if (typeof parsed !== 'string') throw new Error(`${client} view reported no published version`);\n return parsed;\n };\n}\n\ninterface CheckedPackage {\n name: string;\n from: string;\n to?: string;\n failure?: string;\n}\n\n/**\n * Compares every package sync installed against the registry.\n *\n * Only the managed store is consulted: a package that resolves from the\n * repository's own modules is not sync's to move, and a package that is not\n * installed yet is already covered by the missing-package path.\n */\nasync function resolveUpdates(\n repoRoot: string,\n sources: readonly string[],\n publishedVersion: (name: string) => Promise<string>,\n onProgress: ((message: string) => void) | undefined,\n): Promise<Pick<LayerPackageResult, 'updated' | 'unchecked'>> {\n const managed = sources\n .map((source) => ({ name: packageName(source), from: managedPackageVersion(repoRoot, packageName(source)) }))\n .filter((entry): entry is { name: string; from: string } => entry.from !== undefined);\n const checked: CheckedPackage[] = await Promise.all(\n managed.map(async (entry) => {\n try {\n return { ...entry, to: await publishedVersion(entry.name) };\n } catch (error) {\n return { ...entry, failure: (error instanceof Error ? error.message : String(error)).split('\\n')[0] };\n }\n }),\n );\n\n const updated: LayerPackageUpdate[] = [];\n const unchecked: string[] = [];\n for (const entry of checked) {\n if (entry.to === undefined) {\n unchecked.push(entry.name);\n onProgress?.(`kept ${entry.name} ${entry.from}: ${entry.failure ?? 'no published version'}`);\n continue;\n }\n if (entry.to === entry.from) continue;\n updated.push({ name: entry.name, from: entry.from, to: entry.to });\n onProgress?.(`${entry.name} ${entry.from} -> ${entry.to}`);\n }\n return { updated, unchecked };\n}\n\n/** Installs selected layer packages into Pi's project-local `.pi/npm` store and verifies the retry. */\nexport async function ensureLayerPackages(\n options: EnsureLayerPackagesOptions,\n dependencies: LayerPackageInstallerDependencies = {},\n): Promise<LayerPackageResult> {\n const environment = options.environment ?? process.env;\n const resolvers = dependencies.resolvers ?? createLayerResolvers(options.repoRoot);\n const configured = configuredPackageSpecifiers(options.config, options.layers);\n const missing = configured.filter((specifier) => resolvePackageEntries(resolvers, specifier) === undefined);\n const missingSources = npmSources(missing);\n const sources = npmSources(configured);\n if (sources.length === 0) return { installed: [], updated: [], unchecked: [] };\n\n const managedDirectory = path.dirname(managedPackageManifestPath(options.repoRoot));\n if (missing.length === 0 && !fs.existsSync(managedDirectory)) return { installed: [], updated: [], unchecked: [] };\n\n const { updated, unchecked } = options.refresh\n ? await resolveUpdates(\n options.repoRoot,\n sources,\n dependencies.publishedVersion ?? createVersionReader(options.repoRoot, environment),\n options.onProgress,\n )\n : { updated: [], unchecked: [] };\n const manifestChanged = ensureManagedPackageManifest(\n options.repoRoot,\n sources,\n new Map(updated.map((update) => [update.name, update.to])),\n );\n if (missing.length === 0 && !manifestChanged) return { installed: [], updated: [], unchecked };\n\n const packageManager = dependencies.packageManager ?? createPackageManager(options.repoRoot, environment);\n options.onProgress?.(\n `installing ${\n updated.length > 0\n ? pluralize(updated.length, 'update')\n : missing.length > 0\n ? pluralize(missing.length, 'missing package')\n : 'the package manifest'\n }`,\n );\n try {\n if (manifestChanged) {\n // Installing one source reconciles the complete dependency set already written\n // to package.json, avoiding one npm install and audit report per DoomPi package.\n await packageManager.install(sources[0]!, { local: true });\n } else {\n await packageManager.resolveExtensionSources(missingSources, { local: true });\n }\n } catch (error) {\n const attempted = manifestChanged ? sources : missingSources;\n throw new Error(`Failed to reconcile DoomPi layer package(s): ${attempted.join(', ')}`, { cause: error });\n }\n\n const unresolved = missing.filter((specifier) => !resolvePackageEntries(resolvers, specifier)?.length);\n if (unresolved.length > 0) {\n throw new Error(\n `Installed DoomPi layer package(s) did not expose the configured Pi extension: ${unresolved.join(', ')}`,\n );\n }\n return { installed: missingSources, updated, unchecked };\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import{splitPackageSpecifier as e}from"./modules/moduleResolution.mjs";import{createLayerResolvers as t}from"../services/extensionAssembler.mjs";import{piAgentDirectory as n}from"./piSettings.mjs";import r from"node:path";import i from"node:fs";import{isLocalPackageSpecifier as a}from"@agimon-ai/doompi-config/majorModes";import{DefaultPackageManager as
|
|
1
|
+
import{splitPackageSpecifier as e}from"./modules/moduleResolution.mjs";import{createLayerResolvers as t}from"../services/extensionAssembler.mjs";import{piAgentDirectory as n}from"./piSettings.mjs";import r from"node:path";import i from"node:fs";import{isLocalPackageSpecifier as a}from"@agimon-ai/doompi-config/majorModes";import{execFile as o}from"node:child_process";import{promisify as s}from"node:util";import{DefaultPackageManager as c,SettingsManager as l}from"@earendil-works/pi-coding-agent";const u=`npm:`,d=r.join(`.pi`,`npm`),f=r.join(d,`node_modules`),p=`package.json`,m=s(o);function h(e){let t=e.indexOf(`@`,+!!e.startsWith(`@`));return t===-1?e:e.slice(0,t)}const g={"@hono/node-server@2.0.0 - 2.0.9":`^2.0.10`,"@opentelemetry/core@2.0.0 - 2.7.999":`2.8.0`,"brace-expansion@5.0.0 - 5.0.8":`^5.0.9`,"hono@4.0.0 - 4.12.33":`^4.12.34`,"js-yaml@4.0.0 - 4.3.0":`^4.3.1`,"js-yaml@5.0.0 - 5.2.1":`^5.2.2`,"liquidjs@10.0.0 - 10.27.0":`^10.27.1`,"protobufjs@8.0.0 - 8.7.1":`^8.7.2`},_=new Set(Object.keys(g).map(h));function v(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function y(e){return v(e)?{...e}:{}}function b(e){let t=y(e);for(let[e,n]of Object.entries(t))_.has(h(e))?delete t[e]:v(n)&&(t[e]=b(n));return t}function x(e){return r.join(e,d,p)}function S(e,t){let n=new Set,r=[...e.default?[e.default]:[],...t.map(t=>{let n=e.layers[t];if(!n)throw Error(`Unknown layer: ${t}`);return n})];for(let e of r)for(let t of e.packages??[]){let e=typeof t==`string`?t:t.name;(typeof t==`string`||t.optional!==!0)&&!a(e)&&n.add(e)}return[...n]}function C(e,t){let n=e.optionalPackageEntries?.(t);if(n!==void 0)return n;let r=e.optionalPackageEntry(t);return r?[r]:void 0}function w(e,t,n=new Map){let a=x(e),o=i.existsSync(a)?i.readFileSync(a,`utf8`):void 0,s;if(o===void 0)s={name:`pi-extensions`,private:!0};else{let e=JSON.parse(o);if(!v(e))throw Error(`Pi managed package manifest at ${a} must be a JSON object.`);s=e}let c=y(s.dependencies);for(let e of t){let t=E(e),r=n.get(t);r===void 0?Object.hasOwn(c,t)||(c[t]=`*`):c[t]=r}s.dependencies=c,s.overrides={...b(s.overrides),...g};let l=y(s.pnpm);l.overrides={...b(l.overrides),...g},s.pnpm=l;let u=`${JSON.stringify(s,null,2)}\n`;return u!==o&&(i.mkdirSync(r.dirname(a),{recursive:!0}),i.writeFileSync(a,u,`utf8`),!0)}function T(e,t,n){return S(e,t).filter(e=>C(n,e)===void 0)}function E(e){return e.startsWith(u)?e.slice(4):e}function D(t){return[...new Set(t.map(t=>`${u}${e(t).name}`))]}function O(e,t){return`${e} ${t}${e===1?``:`s`}`}function k(e,t){let n=r.join(e,f,t,p);if(i.existsSync(n))try{let e=JSON.parse(i.readFileSync(n,`utf8`));return v(e)&&typeof e.version==`string`?e.version:void 0}catch{return}}function A(e,t){let r=n(t),i=l.create(e,r,{projectTrusted:!0});return new c({cwd:e,agentDir:r,settingsManager:i})}function j(e,t){let[r,...i]=l.create(e,n(t),{projectTrusted:!0}).getNpmCommand()??[],a=r??`npm`,o=r?i:[];return async t=>{let{stdout:n}=await m(a,[...o,`view`,t,`version`,`--json`],{cwd:e,timeout:2e4,encoding:`utf8`}),r=JSON.parse(n.trim()||`null`);if(typeof r!=`string`)throw Error(`${a} view reported no published version`);return r}}async function M(e,t,n,r){let i=t.map(t=>({name:E(t),from:k(e,E(t))})).filter(e=>e.from!==void 0),a=await Promise.all(i.map(async e=>{try{return{...e,to:await n(e.name)}}catch(t){return{...e,failure:(t instanceof Error?t.message:String(t)).split(`
|
|
2
|
+
`)[0]}}})),o=[],s=[];for(let e of a){if(e.to===void 0){s.push(e.name),r?.(`kept ${e.name} ${e.from}: ${e.failure??`no published version`}`);continue}e.to!==e.from&&(o.push({name:e.name,from:e.from,to:e.to}),r?.(`${e.name} ${e.from} -> ${e.to}`))}return{updated:o,unchecked:s}}async function N(e,n={}){let a=e.environment??process.env,o=n.resolvers??t(e.repoRoot),s=S(e.config,e.layers),c=s.filter(e=>C(o,e)===void 0),l=D(c),u=D(s);if(u.length===0)return{installed:[],updated:[],unchecked:[]};let d=r.dirname(x(e.repoRoot));if(c.length===0&&!i.existsSync(d))return{installed:[],updated:[],unchecked:[]};let{updated:f,unchecked:p}=e.refresh?await M(e.repoRoot,u,n.publishedVersion??j(e.repoRoot,a),e.onProgress):{updated:[],unchecked:[]},m=w(e.repoRoot,u,new Map(f.map(e=>[e.name,e.to])));if(c.length===0&&!m)return{installed:[],updated:[],unchecked:p};let h=n.packageManager??A(e.repoRoot,a);e.onProgress?.(`installing ${f.length>0?O(f.length,`update`):c.length>0?O(c.length,`missing package`):`the package manifest`}`);try{m?await h.install(u[0],{local:!0}):await h.resolveExtensionSources(l,{local:!0})}catch(e){throw Error(`Failed to reconcile DoomPi layer package(s): ${(m?u:l).join(`, `)}`,{cause:e})}let g=c.filter(e=>!C(o,e)?.length);if(g.length>0)throw Error(`Installed DoomPi layer package(s) did not expose the configured Pi extension: ${g.join(`, `)}`);return{installed:l,updated:f,unchecked:p}}export{g as SAFE_TRANSITIVE_OVERRIDES,N as ensureLayerPackages,w as ensureManagedPackageManifest,T as missingLayerPackageSpecifiers};
|
|
2
3
|
//# sourceMappingURL=layerPackageInstaller.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layerPackageInstaller.mjs","names":[],"sources":["../../../src/adapters/layerPackageInstaller.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { isLocalPackageSpecifier, type MajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { DefaultPackageManager, type PackageManager, SettingsManager } from '@earendil-works/pi-coding-agent';\nimport { createLayerResolvers, type ExtensionLayerResolvers } from '../services/extensionAssembler.ts';\nimport { splitPackageSpecifier } from './modules/moduleResolution.ts';\nimport { piAgentDirectory } from './piSettings.ts';\n\nconst NPM_SOURCE_PREFIX = 'npm:';\nconst PI_MANAGED_NPM_DIRECTORY = path.join('.pi', 'npm');\nconst PI_MANAGED_PACKAGE_NAME = 'pi-extensions';\nconst UNPINNED_PACKAGE_RANGE = '*';\n\nfunction overridePackageName(selector: string): string {\n const separatorIndex = selector.indexOf('@', selector.startsWith('@') ? 1 : 0);\n return separatorIndex === -1 ? selector : selector.slice(0, separatorIndex);\n}\n\n/**\n * Same-major patches for transitive versions pinned by the current foundation packages.\n * npm only honors overrides from the install root, so published extensions cannot carry\n * these fixes in their own manifests.\n */\nexport const SAFE_TRANSITIVE_OVERRIDES = {\n '@hono/node-server@2.0.0 - 2.0.9': '^2.0.10',\n '@opentelemetry/core@2.0.0 - 2.7.999': '2.8.0',\n 'brace-expansion@5.0.0 - 5.0.8': '^5.0.9',\n 'hono@4.0.0 - 4.12.33': '^4.12.34',\n 'js-yaml@4.0.0 - 4.3.0': '^4.3.1',\n 'js-yaml@5.0.0 - 5.2.1': '^5.2.2',\n 'liquidjs@10.0.0 - 10.27.0': '^10.27.1',\n 'protobufjs@8.0.0 - 8.7.1': '^8.7.2',\n} as const satisfies Readonly<Record<string, string>>;\n\nconst SAFE_TRANSITIVE_PACKAGE_NAMES = new Set(Object.keys(SAFE_TRANSITIVE_OVERRIDES).map(overridePackageName));\n\ntype LayerPackageManager = Pick<PackageManager, 'install' | 'resolveExtensionSources'>;\n\nexport interface EnsureLayerPackagesOptions {\n repoRoot: string;\n config: MajorModesConfig;\n layers: readonly string[];\n environment?: NodeJS.ProcessEnv;\n}\n\nexport interface LayerPackageInstallerDependencies {\n packageManager?: LayerPackageManager;\n resolvers?: ExtensionLayerResolvers;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction recordValue(value: unknown): Record<string, unknown> {\n return isRecord(value) ? { ...value } : {};\n}\n\nfunction withoutConflictingTransitiveOverrides(value: unknown): Record<string, unknown> {\n const overrides = recordValue(value);\n for (const [selector, replacement] of Object.entries(overrides)) {\n if (SAFE_TRANSITIVE_PACKAGE_NAMES.has(overridePackageName(selector))) {\n delete overrides[selector];\n } else if (isRecord(replacement)) {\n overrides[selector] = withoutConflictingTransitiveOverrides(replacement);\n }\n }\n return overrides;\n}\n\nfunction managedPackageManifestPath(repoRoot: string): string {\n return path.join(repoRoot, PI_MANAGED_NPM_DIRECTORY, 'package.json');\n}\n\nfunction configuredPackageSpecifiers(config: MajorModesConfig, layers: readonly string[]): string[] {\n const specifiers = new Set<string>();\n const selectedDefinitions = [\n ...(config.default ? [config.default] : []),\n ...layers.map((layerName) => {\n const layer = config.layers[layerName];\n if (!layer) throw new Error(`Unknown layer: ${layerName}`);\n return layer;\n }),\n ];\n for (const layer of selectedDefinitions) {\n for (const configured of layer.packages ?? []) {\n const specifier = typeof configured === 'string' ? configured : configured.name;\n const optional = typeof configured !== 'string' && configured.optional === true;\n if (!optional && !isLocalPackageSpecifier(specifier)) specifiers.add(specifier);\n }\n }\n return [...specifiers];\n}\n\nfunction resolvePackageEntries(resolvers: ExtensionLayerResolvers, specifier: string): string[] | undefined {\n const entries = resolvers.optionalPackageEntries?.(specifier);\n if (entries !== undefined) return entries;\n const entry = resolvers.optionalPackageEntry(specifier);\n return entry ? [entry] : undefined;\n}\n\n/** Adds the complete package set and patched transitive policy to Pi's managed install root. */\nexport function ensureManagedPackageManifest(repoRoot: string, sources: readonly string[]): boolean {\n const manifestPath = managedPackageManifestPath(repoRoot);\n const current = fs.existsSync(manifestPath) ? fs.readFileSync(manifestPath, 'utf8') : undefined;\n let manifest: Record<string, unknown>;\n if (current === undefined) {\n manifest = { name: PI_MANAGED_PACKAGE_NAME, private: true };\n } else {\n const parsed: unknown = JSON.parse(current);\n if (!isRecord(parsed)) throw new Error(`Pi managed package manifest at ${manifestPath} must be a JSON object.`);\n manifest = parsed;\n }\n\n const dependencies = recordValue(manifest.dependencies);\n for (const source of sources) {\n const name = source.slice(NPM_SOURCE_PREFIX.length);\n if (!Object.hasOwn(dependencies, name)) dependencies[name] = UNPINNED_PACKAGE_RANGE;\n }\n manifest.dependencies = dependencies;\n // npm gives nested and overlapping consumer rules precedence in cases that can retain\n // vulnerable versions, so DoomPi owns these package-specific selectors recursively.\n manifest.overrides = {\n ...withoutConflictingTransitiveOverrides(manifest.overrides),\n ...SAFE_TRANSITIVE_OVERRIDES,\n };\n const pnpm = recordValue(manifest.pnpm);\n pnpm.overrides = {\n ...withoutConflictingTransitiveOverrides(pnpm.overrides),\n ...SAFE_TRANSITIVE_OVERRIDES,\n };\n manifest.pnpm = pnpm;\n\n const serialized = `${JSON.stringify(manifest, null, 2)}\\n`;\n if (serialized === current) return false;\n fs.mkdirSync(path.dirname(manifestPath), { recursive: true });\n fs.writeFileSync(manifestPath, serialized, 'utf8');\n return true;\n}\n\n/** Finds required npm packages selected by defaults and active layers that DoomPi cannot resolve. */\nexport function missingLayerPackageSpecifiers(\n config: MajorModesConfig,\n layers: readonly string[],\n resolvers: ExtensionLayerResolvers,\n): string[] {\n return configuredPackageSpecifiers(config, layers).filter(\n (specifier) => resolvePackageEntries(resolvers, specifier) === undefined,\n );\n}\n\nfunction npmSources(specifiers: readonly string[]): string[] {\n return [...new Set(specifiers.map((specifier) => `${NPM_SOURCE_PREFIX}${splitPackageSpecifier(specifier).name}`))];\n}\n\nfunction createPackageManager(repoRoot: string, environment: NodeJS.ProcessEnv): LayerPackageManager {\n const agentDirectory = piAgentDirectory(environment);\n const settingsManager = SettingsManager.create(repoRoot, agentDirectory, { projectTrusted: true });\n return new DefaultPackageManager({ cwd: repoRoot, agentDir: agentDirectory, settingsManager });\n}\n\n/** Installs missing selected layer packages into Pi's project-local `.pi/npm` store and verifies the retry. */\nexport async function ensureLayerPackages(\n options: EnsureLayerPackagesOptions,\n dependencies: LayerPackageInstallerDependencies = {},\n): Promise<string[]> {\n const environment = options.environment ?? process.env;\n const resolvers = dependencies.resolvers ?? createLayerResolvers(options.repoRoot);\n const configured = configuredPackageSpecifiers(options.config, options.layers);\n const missing = configured.filter((specifier) => resolvePackageEntries(resolvers, specifier) === undefined);\n const missingSources = npmSources(missing);\n const sources = npmSources(configured);\n if (sources.length === 0) return [];\n\n const managedDirectory = path.dirname(managedPackageManifestPath(options.repoRoot));\n if (missing.length === 0 && !fs.existsSync(managedDirectory)) return [];\n const manifestChanged = ensureManagedPackageManifest(options.repoRoot, sources);\n if (missing.length === 0 && !manifestChanged) return [];\n\n const packageManager = dependencies.packageManager ?? createPackageManager(options.repoRoot, environment);\n try {\n if (manifestChanged) {\n // Installing one source reconciles the complete dependency set already written\n // to package.json, avoiding one npm install and audit report per DoomPi package.\n await packageManager.install(sources[0]!, { local: true });\n } else {\n await packageManager.resolveExtensionSources(missingSources, { local: true });\n }\n } catch (error) {\n const attempted = manifestChanged ? sources : missingSources;\n throw new Error(`Failed to reconcile DoomPi layer package(s): ${attempted.join(', ')}`, { cause: error });\n }\n\n const unresolved = missing.filter((specifier) => !resolvePackageEntries(resolvers, specifier)?.length);\n if (unresolved.length > 0) {\n throw new Error(\n `Installed DoomPi layer package(s) did not expose the configured Pi extension: ${unresolved.join(', ')}`,\n );\n }\n return missingSources;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"layerPackageInstaller.mjs","names":[],"sources":["../../../src/adapters/layerPackageInstaller.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { promisify } from 'node:util';\nimport { isLocalPackageSpecifier, type MajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { DefaultPackageManager, type PackageManager, SettingsManager } from '@earendil-works/pi-coding-agent';\nimport { createLayerResolvers, type ExtensionLayerResolvers } from '../services/extensionAssembler.ts';\nimport { splitPackageSpecifier } from './modules/moduleResolution.ts';\nimport { piAgentDirectory } from './piSettings.ts';\n\nconst NPM_SOURCE_PREFIX = 'npm:';\nconst PI_MANAGED_NPM_DIRECTORY = path.join('.pi', 'npm');\nconst PI_MANAGED_MODULES_DIRECTORY = path.join(PI_MANAGED_NPM_DIRECTORY, 'node_modules');\nconst PI_MANAGED_PACKAGE_NAME = 'pi-extensions';\nconst UNPINNED_PACKAGE_RANGE = '*';\nconst PACKAGE_MANIFEST = 'package.json';\nconst DEFAULT_NPM_COMMAND = 'npm';\nconst REGISTRY_TIMEOUT_MS = 20_000;\n\nconst execFileAsync = promisify(execFile);\n\nfunction overridePackageName(selector: string): string {\n const separatorIndex = selector.indexOf('@', selector.startsWith('@') ? 1 : 0);\n return separatorIndex === -1 ? selector : selector.slice(0, separatorIndex);\n}\n\n/**\n * Same-major patches for transitive versions pinned by the current foundation packages.\n * npm only honors overrides from the install root, so published extensions cannot carry\n * these fixes in their own manifests.\n */\nexport const SAFE_TRANSITIVE_OVERRIDES = {\n '@hono/node-server@2.0.0 - 2.0.9': '^2.0.10',\n '@opentelemetry/core@2.0.0 - 2.7.999': '2.8.0',\n 'brace-expansion@5.0.0 - 5.0.8': '^5.0.9',\n 'hono@4.0.0 - 4.12.33': '^4.12.34',\n 'js-yaml@4.0.0 - 4.3.0': '^4.3.1',\n 'js-yaml@5.0.0 - 5.2.1': '^5.2.2',\n 'liquidjs@10.0.0 - 10.27.0': '^10.27.1',\n 'protobufjs@8.0.0 - 8.7.1': '^8.7.2',\n} as const satisfies Readonly<Record<string, string>>;\n\nconst SAFE_TRANSITIVE_PACKAGE_NAMES = new Set(Object.keys(SAFE_TRANSITIVE_OVERRIDES).map(overridePackageName));\n\ntype LayerPackageManager = Pick<PackageManager, 'install' | 'resolveExtensionSources'>;\n\nexport interface EnsureLayerPackagesOptions {\n repoRoot: string;\n config: MajorModesConfig;\n layers: readonly string[];\n environment?: NodeJS.ProcessEnv;\n /**\n * Move every managed package to its newest published version.\n *\n * Launch installs what a selection needs and nothing more, so a session never\n * pays for a registry round trip. Sync is the explicit maintenance command and\n * is the only caller that asks for this.\n */\n refresh?: boolean;\n /** Receives one human-readable line per package decision. */\n onProgress?: (message: string) => void;\n}\n\nexport interface LayerPackageInstallerDependencies {\n packageManager?: LayerPackageManager;\n resolvers?: ExtensionLayerResolvers;\n /** Registry lookup seam; the default reads the newest version with Pi's npm client. */\n publishedVersion?: (name: string) => Promise<string>;\n}\n\n/** One managed package moving from the installed version to the newest published one. */\nexport interface LayerPackageUpdate {\n name: string;\n from: string;\n to: string;\n}\n\nexport interface LayerPackageResult {\n /** Sources installed because a configured extension could not be resolved. */\n installed: string[];\n /** Packages this run moved to a newer published version. */\n updated: LayerPackageUpdate[];\n /** Packages left at the installed version because the registry could not be read. */\n unchecked: string[];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction recordValue(value: unknown): Record<string, unknown> {\n return isRecord(value) ? { ...value } : {};\n}\n\nfunction withoutConflictingTransitiveOverrides(value: unknown): Record<string, unknown> {\n const overrides = recordValue(value);\n for (const [selector, replacement] of Object.entries(overrides)) {\n if (SAFE_TRANSITIVE_PACKAGE_NAMES.has(overridePackageName(selector))) {\n delete overrides[selector];\n } else if (isRecord(replacement)) {\n overrides[selector] = withoutConflictingTransitiveOverrides(replacement);\n }\n }\n return overrides;\n}\n\nfunction managedPackageManifestPath(repoRoot: string): string {\n return path.join(repoRoot, PI_MANAGED_NPM_DIRECTORY, PACKAGE_MANIFEST);\n}\n\nfunction configuredPackageSpecifiers(config: MajorModesConfig, layers: readonly string[]): string[] {\n const specifiers = new Set<string>();\n const selectedDefinitions = [\n ...(config.default ? [config.default] : []),\n ...layers.map((layerName) => {\n const layer = config.layers[layerName];\n if (!layer) throw new Error(`Unknown layer: ${layerName}`);\n return layer;\n }),\n ];\n for (const layer of selectedDefinitions) {\n for (const configured of layer.packages ?? []) {\n const specifier = typeof configured === 'string' ? configured : configured.name;\n const optional = typeof configured !== 'string' && configured.optional === true;\n if (!optional && !isLocalPackageSpecifier(specifier)) specifiers.add(specifier);\n }\n }\n return [...specifiers];\n}\n\nfunction resolvePackageEntries(resolvers: ExtensionLayerResolvers, specifier: string): string[] | undefined {\n const entries = resolvers.optionalPackageEntries?.(specifier);\n if (entries !== undefined) return entries;\n const entry = resolvers.optionalPackageEntry(specifier);\n return entry ? [entry] : undefined;\n}\n\n/** Adds the complete package set and patched transitive policy to Pi's managed install root. */\nexport function ensureManagedPackageManifest(\n repoRoot: string,\n sources: readonly string[],\n targets: ReadonlyMap<string, string> = new Map(),\n): boolean {\n const manifestPath = managedPackageManifestPath(repoRoot);\n const current = fs.existsSync(manifestPath) ? fs.readFileSync(manifestPath, 'utf8') : undefined;\n let manifest: Record<string, unknown>;\n if (current === undefined) {\n manifest = { name: PI_MANAGED_PACKAGE_NAME, private: true };\n } else {\n const parsed: unknown = JSON.parse(current);\n if (!isRecord(parsed)) throw new Error(`Pi managed package manifest at ${manifestPath} must be a JSON object.`);\n manifest = parsed;\n }\n\n const dependencies = recordValue(manifest.dependencies);\n for (const source of sources) {\n const name = packageName(source);\n const target = targets.get(name);\n // An exact target is what makes an update reach the store: npm keeps a\n // locked version whenever the recorded range still admits it, and the range\n // it writes back after an install always admits the version it just wrote.\n if (target !== undefined) dependencies[name] = target;\n else if (!Object.hasOwn(dependencies, name)) dependencies[name] = UNPINNED_PACKAGE_RANGE;\n }\n manifest.dependencies = dependencies;\n // npm gives nested and overlapping consumer rules precedence in cases that can retain\n // vulnerable versions, so DoomPi owns these package-specific selectors recursively.\n manifest.overrides = {\n ...withoutConflictingTransitiveOverrides(manifest.overrides),\n ...SAFE_TRANSITIVE_OVERRIDES,\n };\n const pnpm = recordValue(manifest.pnpm);\n pnpm.overrides = {\n ...withoutConflictingTransitiveOverrides(pnpm.overrides),\n ...SAFE_TRANSITIVE_OVERRIDES,\n };\n manifest.pnpm = pnpm;\n\n const serialized = `${JSON.stringify(manifest, null, 2)}\\n`;\n if (serialized === current) return false;\n fs.mkdirSync(path.dirname(manifestPath), { recursive: true });\n fs.writeFileSync(manifestPath, serialized, 'utf8');\n return true;\n}\n\n/** Finds required npm packages selected by defaults and active layers that DoomPi cannot resolve. */\nexport function missingLayerPackageSpecifiers(\n config: MajorModesConfig,\n layers: readonly string[],\n resolvers: ExtensionLayerResolvers,\n): string[] {\n return configuredPackageSpecifiers(config, layers).filter(\n (specifier) => resolvePackageEntries(resolvers, specifier) === undefined,\n );\n}\n\nfunction packageName(source: string): string {\n return source.startsWith(NPM_SOURCE_PREFIX) ? source.slice(NPM_SOURCE_PREFIX.length) : source;\n}\n\nfunction npmSources(specifiers: readonly string[]): string[] {\n return [...new Set(specifiers.map((specifier) => `${NPM_SOURCE_PREFIX}${splitPackageSpecifier(specifier).name}`))];\n}\n\nfunction pluralize(count: number, noun: string): string {\n return `${count} ${noun}${count === 1 ? '' : 's'}`;\n}\n\n/** Version of a package inside Pi's managed store, absent when sync does not own it. */\nfunction managedPackageVersion(repoRoot: string, name: string): string | undefined {\n const manifestPath = path.join(repoRoot, PI_MANAGED_MODULES_DIRECTORY, name, PACKAGE_MANIFEST);\n if (!fs.existsSync(manifestPath)) return undefined;\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));\n return isRecord(parsed) && typeof parsed.version === 'string' ? parsed.version : undefined;\n } catch {\n // An unreadable manifest carries the same meaning as an absent one: nothing\n // usable is installed, so the caller treats the package as uninstalled.\n return undefined;\n }\n}\n\nfunction createPackageManager(repoRoot: string, environment: NodeJS.ProcessEnv): LayerPackageManager {\n const agentDirectory = piAgentDirectory(environment);\n const settingsManager = SettingsManager.create(repoRoot, agentDirectory, { projectTrusted: true });\n return new DefaultPackageManager({ cwd: repoRoot, agentDir: agentDirectory, settingsManager });\n}\n\n/** Reads newest published versions with the same client Pi installs them with. */\nfunction createVersionReader(repoRoot: string, environment: NodeJS.ProcessEnv): (name: string) => Promise<string> {\n const configured = SettingsManager.create(repoRoot, piAgentDirectory(environment), {\n projectTrusted: true,\n }).getNpmCommand();\n const [command, ...args] = configured ?? [];\n const client = command ?? DEFAULT_NPM_COMMAND;\n const clientArguments = command ? args : [];\n return async (name) => {\n const { stdout } = await execFileAsync(client, [...clientArguments, 'view', name, 'version', '--json'], {\n cwd: repoRoot,\n timeout: REGISTRY_TIMEOUT_MS,\n encoding: 'utf8',\n });\n const parsed: unknown = JSON.parse(stdout.trim() || 'null');\n if (typeof parsed !== 'string') throw new Error(`${client} view reported no published version`);\n return parsed;\n };\n}\n\ninterface CheckedPackage {\n name: string;\n from: string;\n to?: string;\n failure?: string;\n}\n\n/**\n * Compares every package sync installed against the registry.\n *\n * Only the managed store is consulted: a package that resolves from the\n * repository's own modules is not sync's to move, and a package that is not\n * installed yet is already covered by the missing-package path.\n */\nasync function resolveUpdates(\n repoRoot: string,\n sources: readonly string[],\n publishedVersion: (name: string) => Promise<string>,\n onProgress: ((message: string) => void) | undefined,\n): Promise<Pick<LayerPackageResult, 'updated' | 'unchecked'>> {\n const managed = sources\n .map((source) => ({ name: packageName(source), from: managedPackageVersion(repoRoot, packageName(source)) }))\n .filter((entry): entry is { name: string; from: string } => entry.from !== undefined);\n const checked: CheckedPackage[] = await Promise.all(\n managed.map(async (entry) => {\n try {\n return { ...entry, to: await publishedVersion(entry.name) };\n } catch (error) {\n return { ...entry, failure: (error instanceof Error ? error.message : String(error)).split('\\n')[0] };\n }\n }),\n );\n\n const updated: LayerPackageUpdate[] = [];\n const unchecked: string[] = [];\n for (const entry of checked) {\n if (entry.to === undefined) {\n unchecked.push(entry.name);\n onProgress?.(`kept ${entry.name} ${entry.from}: ${entry.failure ?? 'no published version'}`);\n continue;\n }\n if (entry.to === entry.from) continue;\n updated.push({ name: entry.name, from: entry.from, to: entry.to });\n onProgress?.(`${entry.name} ${entry.from} -> ${entry.to}`);\n }\n return { updated, unchecked };\n}\n\n/** Installs selected layer packages into Pi's project-local `.pi/npm` store and verifies the retry. */\nexport async function ensureLayerPackages(\n options: EnsureLayerPackagesOptions,\n dependencies: LayerPackageInstallerDependencies = {},\n): Promise<LayerPackageResult> {\n const environment = options.environment ?? process.env;\n const resolvers = dependencies.resolvers ?? createLayerResolvers(options.repoRoot);\n const configured = configuredPackageSpecifiers(options.config, options.layers);\n const missing = configured.filter((specifier) => resolvePackageEntries(resolvers, specifier) === undefined);\n const missingSources = npmSources(missing);\n const sources = npmSources(configured);\n if (sources.length === 0) return { installed: [], updated: [], unchecked: [] };\n\n const managedDirectory = path.dirname(managedPackageManifestPath(options.repoRoot));\n if (missing.length === 0 && !fs.existsSync(managedDirectory)) return { installed: [], updated: [], unchecked: [] };\n\n const { updated, unchecked } = options.refresh\n ? await resolveUpdates(\n options.repoRoot,\n sources,\n dependencies.publishedVersion ?? createVersionReader(options.repoRoot, environment),\n options.onProgress,\n )\n : { updated: [], unchecked: [] };\n const manifestChanged = ensureManagedPackageManifest(\n options.repoRoot,\n sources,\n new Map(updated.map((update) => [update.name, update.to])),\n );\n if (missing.length === 0 && !manifestChanged) return { installed: [], updated: [], unchecked };\n\n const packageManager = dependencies.packageManager ?? createPackageManager(options.repoRoot, environment);\n options.onProgress?.(\n `installing ${\n updated.length > 0\n ? pluralize(updated.length, 'update')\n : missing.length > 0\n ? pluralize(missing.length, 'missing package')\n : 'the package manifest'\n }`,\n );\n try {\n if (manifestChanged) {\n // Installing one source reconciles the complete dependency set already written\n // to package.json, avoiding one npm install and audit report per DoomPi package.\n await packageManager.install(sources[0]!, { local: true });\n } else {\n await packageManager.resolveExtensionSources(missingSources, { local: true });\n }\n } catch (error) {\n const attempted = manifestChanged ? sources : missingSources;\n throw new Error(`Failed to reconcile DoomPi layer package(s): ${attempted.join(', ')}`, { cause: error });\n }\n\n const unresolved = missing.filter((specifier) => !resolvePackageEntries(resolvers, specifier)?.length);\n if (unresolved.length > 0) {\n throw new Error(\n `Installed DoomPi layer package(s) did not expose the configured Pi extension: ${unresolved.join(', ')}`,\n );\n }\n return { installed: missingSources, updated, unchecked };\n}\n"],"mappings":"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"}
|
|
@@ -7,7 +7,8 @@ Usage: doompi [harness options] [Pi options] [prompt]
|
|
|
7
7
|
|
|
8
8
|
Initialization:
|
|
9
9
|
doompi init Fill missing files in ~/.pi/.doom
|
|
10
|
-
doompi sync
|
|
10
|
+
doompi sync Update packages, build, resolve, and register DoomPi
|
|
11
|
+
in Pi user settings
|
|
11
12
|
doompi sync --check Exit non-zero when the synced config is out of date
|
|
12
13
|
or report legacy repository-local state to migrate
|
|
13
14
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.cjs","names":["require","createRequire"],"sources":["../../../../src/commands/cli/help.ts"],"sourcesContent":["import { createRequire } from 'node:module';\n\n// createRequire rather than a JSON import: this file runs both from src under\n// Node's strip-only TypeScript mode (how pi.sh starts it) and from dist. The\n// package self-reference resolves the same published manifest from either tree.\nconst require = createRequire(import.meta.url);\nconst PACKAGE_MANIFEST = '@agimon-ai/doompi/package.json';\n\nexport const HARNESS_VERSION = (require(PACKAGE_MANIFEST) as { version: string }).version;\n\nexport function printHelp(): void {\n process.stdout.write(`doompi\n\nUsage: doompi [harness options] [Pi options] [prompt]\n doompi init\n doompi sync [matrix options] [--check]\n doompi compat <codex|claude|antigravity> [matrix options] [provider arguments]\n\nInitialization:\n doompi init Fill missing files in ~/.pi/.doom\n doompi sync
|
|
1
|
+
{"version":3,"file":"help.cjs","names":["require","createRequire"],"sources":["../../../../src/commands/cli/help.ts"],"sourcesContent":["import { createRequire } from 'node:module';\n\n// createRequire rather than a JSON import: this file runs both from src under\n// Node's strip-only TypeScript mode (how pi.sh starts it) and from dist. The\n// package self-reference resolves the same published manifest from either tree.\nconst require = createRequire(import.meta.url);\nconst PACKAGE_MANIFEST = '@agimon-ai/doompi/package.json';\n\nexport const HARNESS_VERSION = (require(PACKAGE_MANIFEST) as { version: string }).version;\n\nexport function printHelp(): void {\n process.stdout.write(`doompi\n\nUsage: doompi [harness options] [Pi options] [prompt]\n doompi init\n doompi sync [matrix options] [--check]\n doompi compat <codex|claude|antigravity> [matrix options] [provider arguments]\n\nInitialization:\n doompi init Fill missing files in ~/.pi/.doom\n doompi sync Update packages, build, resolve, and register DoomPi\n in Pi user settings\n doompi sync --check Exit non-zero when the synced config is out of date\n or report legacy repository-local state to migrate\n\nSync artifacts live under ~/.pi/.doom/sync in a repository- and worktree-specific\nnamespace. Linked worktrees isolate state, dist, manifests, resources, and run\nfiles while sharing immutable repository build objects. A legacy <repo>/.pi/doom\nstate is read only when no global state exists; run doompi sync to migrate it.\nDoomPi never removes old or orphaned generated directories automatically.\n\nAfter a sync, run pi directly. The synced session accepts --major-mode,\n--domains, --profile, and --mute, and /mode, /domains, and /profile switch\nwithout a restart. Selection defaults come from the selection block in\n.doom/config.yaml.\n\nCompatibility matrix options:\n --profile <name> Persona and env from .doom/profiles.yaml\n --domains <names> Comma-separated content domains\n --major-mode <name> Named major mode from .doom/modes.yaml\n -- Pass following matrix-named flags to the provider\n\nAll other compatibility arguments pass to the provider unchanged.\n\nHarness options:\n --major-mode <name> Named major mode from .doom/modes.yaml\n (default: copilot). Not --mode: Pi owns that one.\n --profile <name> Persona and env from .doom/profiles.yaml\n --domains <names> Comma-separated domains (content: plugins, skills)\n --no-domains Load no content domains\n --explain Print the resolved matrix and exit\n --emit-mcp <dir> Write the resolved MCP config to a directory and exit\n --plugin-dir <path> Load an explicit plugin directory, repeatable\n --add-dir <path> Add an accessible directory, repeatable\n --preset <name> default, kimi, or ollama\n --cwd <path> Run Pi in this directory\n --automation Use print mode, JSON output, and project approval\n --output-format vibe-lint Read a vibe-lint request from stdin and emit one JSON response\n --mute Disable the notification extension for this run\n --auto-stop Exit interactive Pi when the agent settles\n --allow-protected-writes Allow writes to protected repository paths\n --hooks, --no-hooks Enable or disable repository and plugin hooks\n --mcp, --no-mcp Enable or disable MCP configuration\n --agents, --no-agents Enable or disable spawnable agents\n -h, --help Show harness help\n -v, --version Show harness version\n\nInteractive commands:\n /loop Open the loop launcher picker\n /loops List active loops and stop future scheduling\n /workflow [on|off] Toggle workflow tools for the agent\n /effort <level> Set and persist the thinking effort\n /plan [on|off|status] Toggle repository read-only plan mode\n /mode Pick a named major mode\n /domains [names] Show the selected domains or pick a different set\n /profile List or switch persona and environment profiles\n\nAll remaining options are passed to Pi.\n`);\n}\n"],"mappings":"AAKA,MAGa,GAHPA,uBAAUC,CAAAA,CAAAA,cAAAA,CAAAA,QAAAA,KAAAA,CAAAA,CAAAA,cAAAA,UAAAA,CAAAA,CAAAA,IAGgBD,CAAAA,CAAQ,gCAAgB,CAAC,CAAyB,QAElF,SAAgB,GAAkB,CAChC,QAAQ,OAAO,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmEtB,CACD"}
|
|
@@ -7,7 +7,8 @@ Usage: doompi [harness options] [Pi options] [prompt]
|
|
|
7
7
|
|
|
8
8
|
Initialization:
|
|
9
9
|
doompi init Fill missing files in ~/.pi/.doom
|
|
10
|
-
doompi sync
|
|
10
|
+
doompi sync Update packages, build, resolve, and register DoomPi
|
|
11
|
+
in Pi user settings
|
|
11
12
|
doompi sync --check Exit non-zero when the synced config is out of date
|
|
12
13
|
or report legacy repository-local state to migrate
|
|
13
14
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.mjs","names":[],"sources":["../../../../src/commands/cli/help.ts"],"sourcesContent":["import { createRequire } from 'node:module';\n\n// createRequire rather than a JSON import: this file runs both from src under\n// Node's strip-only TypeScript mode (how pi.sh starts it) and from dist. The\n// package self-reference resolves the same published manifest from either tree.\nconst require = createRequire(import.meta.url);\nconst PACKAGE_MANIFEST = '@agimon-ai/doompi/package.json';\n\nexport const HARNESS_VERSION = (require(PACKAGE_MANIFEST) as { version: string }).version;\n\nexport function printHelp(): void {\n process.stdout.write(`doompi\n\nUsage: doompi [harness options] [Pi options] [prompt]\n doompi init\n doompi sync [matrix options] [--check]\n doompi compat <codex|claude|antigravity> [matrix options] [provider arguments]\n\nInitialization:\n doompi init Fill missing files in ~/.pi/.doom\n doompi sync
|
|
1
|
+
{"version":3,"file":"help.mjs","names":[],"sources":["../../../../src/commands/cli/help.ts"],"sourcesContent":["import { createRequire } from 'node:module';\n\n// createRequire rather than a JSON import: this file runs both from src under\n// Node's strip-only TypeScript mode (how pi.sh starts it) and from dist. The\n// package self-reference resolves the same published manifest from either tree.\nconst require = createRequire(import.meta.url);\nconst PACKAGE_MANIFEST = '@agimon-ai/doompi/package.json';\n\nexport const HARNESS_VERSION = (require(PACKAGE_MANIFEST) as { version: string }).version;\n\nexport function printHelp(): void {\n process.stdout.write(`doompi\n\nUsage: doompi [harness options] [Pi options] [prompt]\n doompi init\n doompi sync [matrix options] [--check]\n doompi compat <codex|claude|antigravity> [matrix options] [provider arguments]\n\nInitialization:\n doompi init Fill missing files in ~/.pi/.doom\n doompi sync Update packages, build, resolve, and register DoomPi\n in Pi user settings\n doompi sync --check Exit non-zero when the synced config is out of date\n or report legacy repository-local state to migrate\n\nSync artifacts live under ~/.pi/.doom/sync in a repository- and worktree-specific\nnamespace. Linked worktrees isolate state, dist, manifests, resources, and run\nfiles while sharing immutable repository build objects. A legacy <repo>/.pi/doom\nstate is read only when no global state exists; run doompi sync to migrate it.\nDoomPi never removes old or orphaned generated directories automatically.\n\nAfter a sync, run pi directly. The synced session accepts --major-mode,\n--domains, --profile, and --mute, and /mode, /domains, and /profile switch\nwithout a restart. Selection defaults come from the selection block in\n.doom/config.yaml.\n\nCompatibility matrix options:\n --profile <name> Persona and env from .doom/profiles.yaml\n --domains <names> Comma-separated content domains\n --major-mode <name> Named major mode from .doom/modes.yaml\n -- Pass following matrix-named flags to the provider\n\nAll other compatibility arguments pass to the provider unchanged.\n\nHarness options:\n --major-mode <name> Named major mode from .doom/modes.yaml\n (default: copilot). Not --mode: Pi owns that one.\n --profile <name> Persona and env from .doom/profiles.yaml\n --domains <names> Comma-separated domains (content: plugins, skills)\n --no-domains Load no content domains\n --explain Print the resolved matrix and exit\n --emit-mcp <dir> Write the resolved MCP config to a directory and exit\n --plugin-dir <path> Load an explicit plugin directory, repeatable\n --add-dir <path> Add an accessible directory, repeatable\n --preset <name> default, kimi, or ollama\n --cwd <path> Run Pi in this directory\n --automation Use print mode, JSON output, and project approval\n --output-format vibe-lint Read a vibe-lint request from stdin and emit one JSON response\n --mute Disable the notification extension for this run\n --auto-stop Exit interactive Pi when the agent settles\n --allow-protected-writes Allow writes to protected repository paths\n --hooks, --no-hooks Enable or disable repository and plugin hooks\n --mcp, --no-mcp Enable or disable MCP configuration\n --agents, --no-agents Enable or disable spawnable agents\n -h, --help Show harness help\n -v, --version Show harness version\n\nInteractive commands:\n /loop Open the loop launcher picker\n /loops List active loops and stop future scheduling\n /workflow [on|off] Toggle workflow tools for the agent\n /effort <level> Set and persist the thinking effort\n /plan [on|off|status] Toggle repository read-only plan mode\n /mode Pick a named major mode\n /domains [names] Show the selected domains or pick a different set\n /profile List or switch persona and environment profiles\n\nAll remaining options are passed to Pi.\n`);\n}\n"],"mappings":"4CAKA,MAGa,EAHG,EAAc,YAAY,GAGV,CAAA,CAAQ,gCAAgB,CAAC,CAAyB,QAElF,SAAgB,GAAkB,CAChC,QAAQ,OAAO,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmEtB,CACD"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../adapters/repository/repository.cjs"),n=require("../adapters/syncLocation.cjs"),r=require("../services/extensionAssembler.cjs");require("../adapters/syncStateContract.cjs");const i=require("../adapters/syncState.cjs"),a=require("../adapters/bootstrapLocator.cjs"),o=require("./cli/matrixOptions.cjs"),s=require("./cli/options.cjs");require("../adapters/config/harnessState.cjs");const c=require("../adapters/piExtensionAlias.cjs"),l=require("../adapters/piSettings.cjs"),u=require("../services/config/projectTrust.cjs"),d=require("../adapters/harnessContext.cjs"),f=require("../adapters/layerPackageInstaller.cjs"),p=require("../adapters/projectPiSettings.cjs"),m=require("../adapters/syncedRuntimeBuilder.cjs");let
|
|
2
|
-
`);return}t.write(`hooks: emit-hooks failed (${r.stderr?.trim()||`exit ${String(r.status)}`})\n`)}function
|
|
3
|
-
`)}var
|
|
4
|
-
`)}\n`),1;let n;try{n=i.readLocatedSyncState(p,
|
|
1
|
+
const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../adapters/repository/repository.cjs"),n=require("../adapters/syncLocation.cjs"),r=require("../services/extensionAssembler.cjs");require("../adapters/syncStateContract.cjs");const i=require("../adapters/syncState.cjs"),a=require("../adapters/bootstrapLocator.cjs"),o=require("./cli/matrixOptions.cjs"),s=require("./cli/options.cjs");require("../adapters/config/harnessState.cjs");const c=require("../adapters/piExtensionAlias.cjs"),l=require("../adapters/piSettings.cjs"),u=require("../services/config/projectTrust.cjs"),d=require("../adapters/harnessContext.cjs"),f=require("../adapters/layerPackageInstaller.cjs"),p=require("../adapters/projectPiSettings.cjs"),m=require("../adapters/syncedRuntimeBuilder.cjs"),h=require("./syncPresenter.cjs");let g=require("node:path");g=e.__toESM(g,1);let _=require("node:fs");_=e.__toESM(_,1);let v=require("node:os");v=e.__toESM(v,1);let y=require("@agimon-ai/doompi-config/domains"),b=require("@agimon-ai/doompi-config/majorModes"),x=require("node:child_process"),S=require("@agimon-ai/doompi-ui/theme"),C=require("@agimon-ai/doompi-config/harnessStore");const w=`--check`,T=g.default.join(`tools`,`harness`,`emit-hooks.mjs`),E=`(none)`,D=[`DOOMPI_`],O=[`CLAUDE_PROJECT_DIR`,`CODEX_REPO_ROOT`,`ORIGINAL_REPO_PATH`,`MCP_UI_VIEWER`],k=new Set([`DOOMPI_CHILD_EXTENSIONS`,`DOOMPI_COMPOSED`,`DOOMPI_MUTE`,`DOOMPI_TEMP_DIR`,C.HARNESS_STATE_POINTER,`PI_SUBAGENT_PI_BINARY`]);function A(e){let t={};for(let[n,r]of Object.entries(e))r===void 0||k.has(n)||(O.includes(n)||D.some(e=>n.startsWith(e)))&&(t[n]=r);return t}function j(e,t){let{selection:n}=u.loadDoomConfig(e);return n?{...t,...n.majorMode&&!t.DOOMPI_MAJOR_MODE?{[o.DOOMPI_MAJOR_MODE_ENV]:n.majorMode}:{},...n.profile&&!t.DOOMPI_PROFILE?{[o.DOOMPI_PROFILE_ENV]:n.profile}:{},...n.domains&&t.DOOMPI_DOMAINS===void 0?{[o.DOOMPI_DOMAINS_ENV]:n.domains.join(`,`)}:{}}:t}function M(e){return{majorMode:e.majorMode,domains:e.domains,profile:e.profile,preset:e.preset}}function N(e,t){let n=(0,b.loadMajorModesConfig)(e),i=r.createLayerResolvers(e);return r.resolveExtensionComposition({agents:t.agents,autoStop:!1,mute:!1,preset:t.preset,personaEntry:i.packageEntry(r.PERSONA_ENTRY),majorMode:t.majorMode,layers:(0,b.filterHookDisabledLayers)(n,(0,b.resolveLayers)(n,t.majorMode),t.hooks),majorModesConfig:n,resolvers:i}).fingerprint}function P(e,t,n,o=process.env,s=`persisted`,u){if(!n)return[`no sync state: run doompi sync`];let d=[];i.syncStateRootMatches(e,n.root)||d.push(`sync state belongs to a different repository`);let f=n.selection;(f.majorMode!==t.majorMode||f.profile!==t.profile||f.preset!==t.preset||f.domains.join(`,`)!==t.domains.join(`,`))&&d.push(`selection changed since the last sync`),i.computeInputsHash(e,f,o.HOME??v.default.homedir())!==n.inputsHash&&d.push(`.doom configuration changed`),JSON.stringify(i.recordResolvedEntries((0,b.loadMajorModesConfig)(e),r.createLayerResolvers(e)))!==JSON.stringify(n.resolved)&&d.push(`resolved extension paths changed`),u&&n.compositionFingerprint!==u&&d.push(`extension composition changed`);try{a.readBootstrapStatus(e,void 0,o.HOME??v.default.homedir()).fresh||d.push(`precompiled runtime is missing or stale`)}catch{d.push(`precompiled runtime is missing or stale`)}let m=l.piAgentDirectory(o),h=g.default.join(l.piThemeDirectory(m),`${S.DEFAULT_THEME_NAME}.json`);if(s===`persisted`){let t=l.readPiSettings(m),n=l.mergePiSettings(t,m,{themePath:h,themeName:S.DEFAULT_THEME_NAME});l.serializePiSettings(n)!==l.serializePiSettings(t)&&d.push(`Pi user settings are out of date`),p.projectRegistersDoom(e)&&d.push(p.DUPLICATE_REGISTRATION_DRIFT)}c.piExtensionAliasIsCurrent(m)||d.push(`Pi user extension alias is out of date`);let y=`${JSON.stringify(S.DEFAULT_THEME,null,2)}\n`;return(!_.default.existsSync(h)||_.default.readFileSync(h,`utf8`)!==y)&&d.push(`Pi user theme is out of date`),(n.baseline.themePath!==h||n.baseline.themeName!==S.DEFAULT_THEME_NAME)&&d.push(`synced theme location is out of date`),d}function F(e,t){let n=g.default.join(e,T);if(!_.default.existsSync(n))return;let r=(0,x.spawnSync)(process.execPath,[n,`--write`],{cwd:e,encoding:`utf8`});if(r.status===0){t.write(`hooks: regenerated for Claude Code and Codex
|
|
2
|
+
`);return}t.write(`hooks: emit-hooks failed (${r.stderr?.trim()||`exit ${String(r.status)}`})\n`)}function I(e,t=`pi`){let{selection:n}=e;return[`mode: ${n.majorMode}`,`domains: ${n.domains.join(`, `)||E}`,`profile: ${n.profile??E}`,`skills: ${e.skillCount}`,`agents: ${e.agentCount}`,`mcp: ${e.mcpServers.join(`, `)||E}`,`state: ${e.statePath}`,...e.settingsPath?[`settings: ${e.settingsPath}`]:[],...e.projectSettingsPath?[`project: removed duplicate registration from ${e.projectSettingsPath}`]:[],``,`Run ${t} from the repository root to use it.`,``].join(`
|
|
3
|
+
`)}var L=class{name=`sync`;settingsMode;homeDirectory;constructor(e={}){this.settingsMode=e.settingsMode??`persisted`,this.homeDirectory=e.homeDirectory}matches(e){return e[0]===this.name}async execute(e,a=process.env,o=process.cwd(),c=process.stdout){let l=e.includes(w),u=e.slice(1).filter(e=>e!==w),d=a.DOOMPI_ROOT,p=d?g.default.resolve(d):t.findRepositoryRoot(o),m=(0,b.loadMajorModesConfig)(p).defaultMajorMode,_=(0,y.loadDomains)(p).defaultDomains,x=s.parseHarnessArgs(u,j(p,a),o,m,_),S=M(x.options),C=this.homeDirectory??a.HOME??v.default.homedir();if(l){let e=(0,b.loadMajorModesConfig)(p),t=f.missingLayerPackageSpecifiers(e,Object.keys(e.layers),r.createLayerResolvers(p));if(t.length>0)return c.write(`doompi sync is out of date:\n${t.map(e=>` configured package is not installed: ${e}`).join(`
|
|
4
|
+
`)}\n`),1;let n;try{n=i.readLocatedSyncState(p,C)}catch(e){let t=e instanceof Error?e.message:String(e);return c.write(`doompi sync is out of date:\n ${t}\n`),1}let o=N(p,x.options),s=P(p,S,n?.state,a,this.settingsMode,o);return n?.layout===`legacy`&&s.unshift(`legacy repository-local sync state requires migration`),s.length===0?(c.write(`doompi sync is up to date
|
|
5
5
|
`),0):(c.write(`doompi sync is out of date:\n${s.map(e=>` ${e}`).join(`
|
|
6
|
-
`)}\n`),1)}let
|
|
6
|
+
`)}\n`),1)}let T=new h.SyncProgress(c),E=await n.acquireSyncLocationLock(n.resolveSyncLocation(p,C)),D;try{D=await this.stage(p,x.options,a,C,T)}finally{await E()}return F(p,c),c.write(I(D,this.settingsMode===`embedded`?`dpi`:`pi`)),0}async stage(e,t,a,o,s){let u=n.resolveSyncLocation(e,o),h=u.directory,v=new Set([`run`,`cache`,`dist`,`.sync.lock`]);for(let e of _.default.existsSync(h)?_.default.readdirSync(h):[])v.has(e)||_.default.rmSync(g.default.join(h,e),{recursive:!0,force:!0});let y=s.start(`sync`,`resolving the matrix and staging resources`),b=await d.buildHarnessContext({...t,repoRoot:e,homeDirectory:o,cwd:e,resourceDirectory:h});await f.ensureLayerPackages({repoRoot:e,config:b.majorModesConfig,layers:Object.keys(b.majorModesConfig.layers),environment:a}),y(`${String(b.resources.skillCount)} skills, ${String(b.resources.agentCount)} agents`);let x=M(t),w=r.createLayerResolvers(e),T=i.recordResolvedEntries(b.majorModesConfig,w),E=N(e,t),D=l.piAgentDirectory(a),O=l.piThemeDirectory(D);await _.default.promises.mkdir(O,{recursive:!0});let k=await(0,S.writeDefaultTheme)(O);g.default.resolve(b.defaultThemePath)!==g.default.resolve(k)&&await _.default.promises.rm(b.defaultThemePath,{force:!0});let j={version:13,root:e,identity:u.identity,inputsHash:i.computeInputsHash(e,x,o),compositionFingerprint:E,selection:x,env:A(b.environment),fileState:{profileEnvironment:(0,C.loadHarnessState)(b.environment).state.profileEnvironment,pluginHooks:b.resources.pluginHooks,mcpProjection:b.resources.mcpProjection},resolved:T,baseline:{mcpConfigPath:b.resources.mcpConfigPath,personaFile:b.environment.DOOMPI_PERSONA_FILE,themePath:k,themeName:S.DEFAULT_THEME_NAME}},P=await i.writeSyncState(e,j,o),F=s.start(`runtime`,`precompiling the mode bundles`),I=await m.buildSyncedRuntime(e,a,o);F(`${String(Object.keys(I.bundles).length)} mode bundles`),c.writePiExtensionAlias(D);let L=this.settingsMode===`persisted`?l.writePiSettings(D,{themePath:j.baseline.themePath,themeName:j.baseline.themeName}):void 0,R=this.settingsMode===`persisted`?p.writeProjectPiSettings(e):void 0;return{statePath:P,...L?{settingsPath:L}:{},...R?{projectSettingsPath:R}:{},selection:x,mcpServers:j.baseline.mcpConfigPath?i.readMcpServerNames(j.baseline.mcpConfigPath):[],skillCount:b.resources.skillCount,agentCount:b.resources.agentCount}}};exports.SyncCommand=L,exports.collectDrift=P,exports.formatSyncResult=I,exports.recordedEnvironment=A,exports.selectionCompositionFingerprint=N,exports.selectionEnvironment=j,exports.toSelection=M;
|
|
7
7
|
//# sourceMappingURL=syncCommand.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncCommand.cjs","names":["path","HARNESS_STATE_POINTER","loadDoomConfig","DOOMPI_MAJOR_MODE_ENV","DOOMPI_PROFILE_ENV","DOOMPI_DOMAINS_ENV","loadMajorModesConfig","createLayerResolvers","resolveExtensionComposition","PERSONA_ENTRY","filterHookDisabledLayers","resolveLayers","syncStateRootMatches","computeInputsHash","os","recordResolvedEntries","readBootstrapStatus","piAgentDirectory","piThemeDirectory","DEFAULT_THEME_NAME","readPiSettings","mergePiSettings","serializePiSettings","projectRegistersDoom","DUPLICATE_REGISTRATION_DRIFT","piExtensionAliasIsCurrent","DEFAULT_THEME","fs","spawnSync","findRepositoryRoot","loadDomains","parseHarnessArgs","missingLayerPackageSpecifiers","readLocatedSyncState","acquireSyncLocationLock","resolveSyncLocation","buildHarnessContext","ensureLayerPackages","writeDefaultTheme","loadHarnessState","writeSyncState","buildSyncedRuntime","writePiSettings","writeProjectPiSettings","readMcpServerNames"],"sources":["../../../src/commands/syncCommand.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { filterHookDisabledLayers, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport { buildHarnessContext } from '../adapters/harnessContext.ts';\nimport { ensureLayerPackages, missingLayerPackageSpecifiers } from '../adapters/layerPackageInstaller.ts';\nimport { readBootstrapStatus } from '../adapters/bootstrapLocator.ts';\nimport { piExtensionAliasIsCurrent, writePiExtensionAlias } from '../adapters/piExtensionAlias.ts';\nimport {\n mergePiSettings,\n piAgentDirectory,\n piThemeDirectory,\n readPiSettings,\n serializePiSettings,\n writePiSettings,\n} from '../adapters/piSettings.ts';\nimport {\n DUPLICATE_REGISTRATION_DRIFT,\n projectRegistersDoom,\n writeProjectPiSettings,\n} from '../adapters/projectPiSettings.ts';\nimport { buildSyncedRuntime } from '../adapters/syncedRuntimeBuilder.ts';\nimport { acquireSyncLocationLock, resolveSyncLocation } from '../adapters/syncLocation.ts';\nimport {\n computeInputsHash,\n readLocatedSyncState,\n readMcpServerNames,\n recordResolvedEntries,\n SYNC_STATE_VERSION,\n type SyncSelection,\n type SyncState,\n syncStateRootMatches,\n writeSyncState,\n} from '../adapters/syncState.ts';\nimport { HARNESS_STATE_POINTER, loadHarnessState } from '../adapters/config/harnessState';\nimport { loadDomains } from '@agimon-ai/doompi-config/domains';\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { DEFAULT_THEME, DEFAULT_THEME_NAME, writeDefaultTheme } from '@agimon-ai/doompi-ui/theme';\nimport { loadDoomConfig } from '../services/config/projectTrust';\nimport { createLayerResolvers, PERSONA_ENTRY, resolveExtensionComposition } from '../services/extensionAssembler.ts';\nimport type { HarnessOptions } from '../types/interfaces/harness';\nimport { findRepositoryRoot } from '../adapters/repository/repository';\nimport { DOOMPI_DOMAINS_ENV, DOOMPI_MAJOR_MODE_ENV, DOOMPI_PROFILE_ENV } from './cli/matrixOptions.ts';\nimport { parseHarnessArgs } from './cli/options.ts';\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';\nconst HARNESS_ROOT_ENV = 'DOOMPI_ROOT';\nconst PERSONA_FILE_ENV = 'DOOMPI_PERSONA_FILE';\nconst HOOK_EMITTER = path.join('tools', 'harness', 'emit-hooks.mjs');\nconst RUN_DIRECTORY = 'run';\nconst EXTENSION_CACHE_DIRECTORY = 'cache';\nconst MODE_DIST_DIRECTORY = 'dist';\nconst SYNC_LOCK_FILE = '.sync.lock';\nconst NONE = '(none)';\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 = Pick<NodeJS.WritableStream, 'write'>;\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}\n\nexport interface SyncResult {\n statePath: string;\n /** Omitted when DPI supplies the integration as a process-local overlay. */\n settingsPath?: string;\n /** Set only when the repository still carried its own DoomPi registration. */\n projectSettingsPath?: string;\n selection: SyncSelection;\n mcpServers: string[];\n skillCount: number;\n agentCount: number;\n}\n\nexport function recordedEnvironment(environment: NodeJS.ProcessEnv): Record<string, string> {\n const recorded: Record<string, string> = {};\n for (const [key, value] of Object.entries(environment)) {\n if (value === undefined || EXCLUDED_KEYS.has(key)) continue;\n if (RECORDED_KEYS.includes(key) || RECORDED_PREFIXES.some((prefix) => key.startsWith(prefix))) {\n recorded[key] = value;\n }\n }\n return recorded;\n}\n\n/**\n * Layers the repository's declared selection under the usual resolution.\n *\n * `.doom/config.yaml` holds what the repository selects by default, the way\n * init.el does for doom-emacs. Seeding the environment the parser reads keeps\n * the precedence the launcher already documents: an explicit flag wins, then an\n * exported variable, then the declared default.\n */\nexport function selectionEnvironment(repoRoot: string, environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const { selection } = loadDoomConfig(repoRoot);\n if (!selection) return environment;\n return {\n ...environment,\n ...(selection.majorMode && !environment[DOOMPI_MAJOR_MODE_ENV]\n ? { [DOOMPI_MAJOR_MODE_ENV]: selection.majorMode }\n : {}),\n ...(selection.profile && !environment[DOOMPI_PROFILE_ENV] ? { [DOOMPI_PROFILE_ENV]: selection.profile } : {}),\n ...(selection.domains && environment[DOOMPI_DOMAINS_ENV] === undefined\n ? { [DOOMPI_DOMAINS_ENV]: selection.domains.join(',') }\n : {}),\n };\n}\n\nexport function toSelection(\n options: Pick<HarnessOptions, 'majorMode' | 'domains' | 'profile' | 'preset'>,\n): SyncSelection {\n return {\n majorMode: options.majorMode,\n domains: options.domains,\n profile: options.profile,\n preset: options.preset,\n };\n}\n\nexport function selectionCompositionFingerprint(\n repoRoot: string,\n options: Pick<HarnessOptions, 'agents' | 'hooks' | 'majorMode' | 'mcp' | 'preset'>,\n): string {\n const majorModesConfig = loadMajorModesConfig(repoRoot);\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/** Differences between what a sync would produce and what is on disk. */\nexport function collectDrift(\n repoRoot: string,\n selection: SyncSelection,\n state: SyncState | undefined,\n environment: NodeJS.ProcessEnv = process.env,\n settingsMode: SyncSettingsMode = 'persisted',\n expectedCompositionFingerprint?: string,\n): string[] {\n if (!state) return ['no sync state: run doompi sync'];\n const drift: string[] = [];\n if (!syncStateRootMatches(repoRoot, state.root)) drift.push('sync state belongs to a different repository');\n const recorded = state.selection;\n if (\n recorded.majorMode !== selection.majorMode ||\n recorded.profile !== selection.profile ||\n recorded.preset !== selection.preset ||\n recorded.domains.join(',') !== selection.domains.join(',')\n ) {\n drift.push('selection changed since the last sync');\n }\n // Hashed against the recorded selection, not the requested one, so a\n // selection change is reported once rather than as two findings.\n if (computeInputsHash(repoRoot, recorded, environment.HOME ?? os.homedir()) !== state.inputsHash) {\n drift.push('.doom configuration changed');\n }\n // Re-resolving is what catches a dependency upgrade moving a package, which\n // the inputs hash deliberately does not read.\n if (\n JSON.stringify(recordResolvedEntries(loadMajorModesConfig(repoRoot), createLayerResolvers(repoRoot))) !==\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 const agentDirectory = piAgentDirectory(environment);\n const themePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n if (settingsMode === 'persisted') {\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');\n }\n if (projectRegistersDoom(repoRoot)) drift.push(DUPLICATE_REGISTRATION_DRIFT);\n }\n if (!piExtensionAliasIsCurrent(agentDirectory)) drift.push('Pi user extension alias is out of date');\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');\n }\n if (state.baseline.themePath !== themePath || state.baseline.themeName !== DEFAULT_THEME_NAME) {\n drift.push('synced theme location is out of date');\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/** Resolves the matrix, stages it into home-scoped worktree storage, and points Pi at the result. */\nexport class SyncCommand {\n readonly name = SYNC_COMMAND;\n private readonly settingsMode: SyncSettingsMode;\n private readonly homeDirectory: string | undefined;\n\n constructor(options: SyncCommandOptions = {}) {\n this.settingsMode = options.settingsMode ?? 'persisted';\n this.homeDirectory = options.homeDirectory;\n }\n\n matches(args: string[]): boolean {\n return args[0] === this.name;\n }\n\n async execute(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n ): Promise<number> {\n const check = args.includes(CHECK_OPTION);\n const rest = args.slice(1).filter((argument) => argument !== CHECK_OPTION);\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot ? path.resolve(inheritedRoot) : findRepositoryRoot(currentDirectory);\n const defaultMajorMode = loadMajorModesConfig(repoRoot).defaultMajorMode;\n const defaultDomains = loadDomains(repoRoot).defaultDomains;\n const parsed = parseHarnessArgs(\n rest,\n selectionEnvironment(repoRoot, environment),\n currentDirectory,\n defaultMajorMode,\n defaultDomains,\n );\n const selection = toSelection(parsed.options);\n const homeDirectory = this.homeDirectory ?? environment.HOME ?? os.homedir();\n\n if (check) {\n const majorModesConfig = loadMajorModesConfig(repoRoot);\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);\n const drift = collectDrift(\n repoRoot,\n selection,\n located?.state,\n environment,\n this.settingsMode,\n expectedCompositionFingerprint,\n );\n if (located?.layout === 'legacy') drift.unshift('legacy repository-local sync state requires migration');\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 const releaseLock = await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n let result: SyncResult;\n try {\n result = await this.stage(repoRoot, parsed.options, environment, homeDirectory);\n } finally {\n await releaseLock();\n }\n emitFrontendHooks(repoRoot, output);\n output.write(formatSyncResult(result, this.settingsMode === 'embedded' ? 'dpi' : 'pi'));\n return 0;\n }\n\n private async stage(\n repoRoot: string,\n options: Omit<HarnessOptions, 'repoRoot'>,\n environment: NodeJS.ProcessEnv,\n homeDirectory: string,\n ): Promise<SyncResult> {\n const location = resolveSyncLocation(repoRoot, homeDirectory);\n const directory = location.directory;\n // A full regeneration, so nothing a previous selection staged survives. Keep\n // per-session run directories because another Pi session may be using one,\n // and retain content-addressed cache and dist artifacts across selections.\n const preserved = new Set([RUN_DIRECTORY, EXTENSION_CACHE_DIRECTORY, MODE_DIST_DIRECTORY, SYNC_LOCK_FILE]);\n for (const entry of fs.existsSync(directory) ? fs.readdirSync(directory) : []) {\n if (!preserved.has(entry)) fs.rmSync(path.join(directory, entry), { recursive: true, force: true });\n }\n\n const context = await buildHarnessContext({\n ...options,\n repoRoot,\n homeDirectory,\n // Sync describes the repository, not the directory it was invoked from.\n cwd: repoRoot,\n resourceDirectory: directory,\n });\n await ensureLayerPackages({\n repoRoot,\n config: context.majorModesConfig,\n layers: Object.keys(context.majorModesConfig.layers),\n environment,\n });\n const selection = toSelection(options);\n const resolvers = createLayerResolvers(repoRoot);\n const resolved = recordResolvedEntries(context.majorModesConfig, resolvers);\n const compositionFingerprint = selectionCompositionFingerprint(repoRoot, options);\n const agentDirectory = piAgentDirectory(environment);\n const themeDirectory = piThemeDirectory(agentDirectory);\n await fs.promises.mkdir(themeDirectory, { recursive: true });\n const themePath = await writeDefaultTheme(themeDirectory);\n if (path.resolve(context.defaultThemePath) !== path.resolve(themePath)) {\n await fs.promises.rm(context.defaultThemePath, { force: true });\n }\n const state: SyncState = {\n version: SYNC_STATE_VERSION,\n root: repoRoot,\n identity: location.identity,\n inputsHash: computeInputsHash(repoRoot, selection, homeDirectory),\n compositionFingerprint,\n selection,\n env: recordedEnvironment(context.environment),\n // Read back from the session file the context just wrote, which is the\n // authority for fields deliberately omitted from the child environment.\n fileState: {\n profileEnvironment: loadHarnessState(context.environment).state.profileEnvironment,\n pluginHooks: context.resources.pluginHooks,\n mcpProjection: context.resources.mcpProjection,\n },\n resolved,\n baseline: {\n mcpConfigPath: context.resources.mcpConfigPath,\n personaFile: context.environment[PERSONA_FILE_ENV],\n themePath,\n themeName: DEFAULT_THEME_NAME,\n },\n };\n\n const statePath = await writeSyncState(repoRoot, state, homeDirectory);\n await buildSyncedRuntime(repoRoot, environment, homeDirectory);\n writePiExtensionAlias(agentDirectory);\n const settingsPath =\n this.settingsMode === 'persisted'\n ? writePiSettings(agentDirectory, {\n themePath: state.baseline.themePath,\n themeName: state.baseline.themeName,\n })\n : undefined;\n // After the user scope is current, so the repository is only ever stripped\n // of a registration the user scope already provides.\n const projectSettingsPath = this.settingsMode === 'persisted' ? writeProjectPiSettings(repoRoot) : undefined;\n\n return {\n statePath,\n ...(settingsPath ? { settingsPath } : {}),\n ...(projectSettingsPath ? { projectSettingsPath } : {}),\n selection,\n mcpServers: state.baseline.mcpConfigPath ? readMcpServerNames(state.baseline.mcpConfigPath) : [],\n skillCount: context.resources.skillCount,\n agentCount: context.resources.agentCount,\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"syncCommand.cjs","names":["path","HARNESS_STATE_POINTER","loadDoomConfig","DOOMPI_MAJOR_MODE_ENV","DOOMPI_PROFILE_ENV","DOOMPI_DOMAINS_ENV","loadMajorModesConfig","createLayerResolvers","resolveExtensionComposition","PERSONA_ENTRY","filterHookDisabledLayers","resolveLayers","syncStateRootMatches","computeInputsHash","os","recordResolvedEntries","readBootstrapStatus","piAgentDirectory","piThemeDirectory","DEFAULT_THEME_NAME","readPiSettings","mergePiSettings","serializePiSettings","projectRegistersDoom","DUPLICATE_REGISTRATION_DRIFT","piExtensionAliasIsCurrent","DEFAULT_THEME","fs","spawnSync","findRepositoryRoot","loadDomains","parseHarnessArgs","missingLayerPackageSpecifiers","readLocatedSyncState","SyncProgress","acquireSyncLocationLock","resolveSyncLocation","buildHarnessContext","ensureLayerPackages","writeDefaultTheme","loadHarnessState","writeSyncState","buildSyncedRuntime","writePiSettings","writeProjectPiSettings","readMcpServerNames"],"sources":["../../../src/commands/syncCommand.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { filterHookDisabledLayers, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport { buildHarnessContext } from '../adapters/harnessContext.ts';\nimport { ensureLayerPackages, missingLayerPackageSpecifiers } from '../adapters/layerPackageInstaller.ts';\nimport { readBootstrapStatus } from '../adapters/bootstrapLocator.ts';\nimport { piExtensionAliasIsCurrent, writePiExtensionAlias } from '../adapters/piExtensionAlias.ts';\nimport {\n mergePiSettings,\n piAgentDirectory,\n piThemeDirectory,\n readPiSettings,\n serializePiSettings,\n writePiSettings,\n} from '../adapters/piSettings.ts';\nimport {\n DUPLICATE_REGISTRATION_DRIFT,\n projectRegistersDoom,\n writeProjectPiSettings,\n} from '../adapters/projectPiSettings.ts';\nimport { buildSyncedRuntime } from '../adapters/syncedRuntimeBuilder.ts';\nimport { acquireSyncLocationLock, resolveSyncLocation } from '../adapters/syncLocation.ts';\nimport {\n computeInputsHash,\n readLocatedSyncState,\n readMcpServerNames,\n recordResolvedEntries,\n SYNC_STATE_VERSION,\n type SyncSelection,\n type SyncState,\n syncStateRootMatches,\n writeSyncState,\n} from '../adapters/syncState.ts';\nimport { HARNESS_STATE_POINTER, loadHarnessState } from '../adapters/config/harnessState';\nimport { loadDomains } from '@agimon-ai/doompi-config/domains';\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { DEFAULT_THEME, DEFAULT_THEME_NAME, writeDefaultTheme } from '@agimon-ai/doompi-ui/theme';\nimport { loadDoomConfig } from '../services/config/projectTrust';\nimport { createLayerResolvers, PERSONA_ENTRY, resolveExtensionComposition } from '../services/extensionAssembler.ts';\nimport type { HarnessOptions } from '../types/interfaces/harness';\nimport { findRepositoryRoot } from '../adapters/repository/repository';\nimport { DOOMPI_DOMAINS_ENV, DOOMPI_MAJOR_MODE_ENV, DOOMPI_PROFILE_ENV } from './cli/matrixOptions.ts';\nimport { parseHarnessArgs } from './cli/options.ts';\nimport { SyncProgress, type SyncProgressOutput } from './syncPresenter.ts';\n\n/**\n * `doom-pi sync`: resolve the matrix once and write it where plain Pi finds it.\n *\n * The doom-emacs split. Everything that needs a real Node process (module\n * resolution, staging skills and agents, generating the MCP config) happens\n * here, and the doom-pi extension then only reads what this produced. The\n * launcher is untouched and keeps resolving the same matrix per run.\n */\n\nconst SYNC_COMMAND = 'sync';\nconst CHECK_OPTION = '--check';\nconst HARNESS_ROOT_ENV = 'DOOMPI_ROOT';\nconst PERSONA_FILE_ENV = 'DOOMPI_PERSONA_FILE';\nconst HOOK_EMITTER = path.join('tools', 'harness', 'emit-hooks.mjs');\nconst RUN_DIRECTORY = 'run';\nconst EXTENSION_CACHE_DIRECTORY = 'cache';\nconst MODE_DIST_DIRECTORY = 'dist';\nconst SYNC_LOCK_FILE = '.sync.lock';\nconst NONE = '(none)';\nconst SYNC_LABEL = 'sync';\nconst RUNTIME_LABEL = 'runtime';\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}\n\nexport interface SyncResult {\n statePath: string;\n /** Omitted when DPI supplies the integration as a process-local overlay. */\n settingsPath?: string;\n /** Set only when the repository still carried its own DoomPi registration. */\n projectSettingsPath?: string;\n selection: SyncSelection;\n mcpServers: string[];\n skillCount: number;\n agentCount: number;\n}\n\nexport function recordedEnvironment(environment: NodeJS.ProcessEnv): Record<string, string> {\n const recorded: Record<string, string> = {};\n for (const [key, value] of Object.entries(environment)) {\n if (value === undefined || EXCLUDED_KEYS.has(key)) continue;\n if (RECORDED_KEYS.includes(key) || RECORDED_PREFIXES.some((prefix) => key.startsWith(prefix))) {\n recorded[key] = value;\n }\n }\n return recorded;\n}\n\n/**\n * Layers the repository's declared selection under the usual resolution.\n *\n * `.doom/config.yaml` holds what the repository selects by default, the way\n * init.el does for doom-emacs. Seeding the environment the parser reads keeps\n * the precedence the launcher already documents: an explicit flag wins, then an\n * exported variable, then the declared default.\n */\nexport function selectionEnvironment(repoRoot: string, environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const { selection } = loadDoomConfig(repoRoot);\n if (!selection) return environment;\n return {\n ...environment,\n ...(selection.majorMode && !environment[DOOMPI_MAJOR_MODE_ENV]\n ? { [DOOMPI_MAJOR_MODE_ENV]: selection.majorMode }\n : {}),\n ...(selection.profile && !environment[DOOMPI_PROFILE_ENV] ? { [DOOMPI_PROFILE_ENV]: selection.profile } : {}),\n ...(selection.domains && environment[DOOMPI_DOMAINS_ENV] === undefined\n ? { [DOOMPI_DOMAINS_ENV]: selection.domains.join(',') }\n : {}),\n };\n}\n\nexport function toSelection(\n options: Pick<HarnessOptions, 'majorMode' | 'domains' | 'profile' | 'preset'>,\n): SyncSelection {\n return {\n majorMode: options.majorMode,\n domains: options.domains,\n profile: options.profile,\n preset: options.preset,\n };\n}\n\nexport function selectionCompositionFingerprint(\n repoRoot: string,\n options: Pick<HarnessOptions, 'agents' | 'hooks' | 'majorMode' | 'mcp' | 'preset'>,\n): string {\n const majorModesConfig = loadMajorModesConfig(repoRoot);\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/** Differences between what a sync would produce and what is on disk. */\nexport function collectDrift(\n repoRoot: string,\n selection: SyncSelection,\n state: SyncState | undefined,\n environment: NodeJS.ProcessEnv = process.env,\n settingsMode: SyncSettingsMode = 'persisted',\n expectedCompositionFingerprint?: string,\n): string[] {\n if (!state) return ['no sync state: run doompi sync'];\n const drift: string[] = [];\n if (!syncStateRootMatches(repoRoot, state.root)) drift.push('sync state belongs to a different repository');\n const recorded = state.selection;\n if (\n recorded.majorMode !== selection.majorMode ||\n recorded.profile !== selection.profile ||\n recorded.preset !== selection.preset ||\n recorded.domains.join(',') !== selection.domains.join(',')\n ) {\n drift.push('selection changed since the last sync');\n }\n // Hashed against the recorded selection, not the requested one, so a\n // selection change is reported once rather than as two findings.\n if (computeInputsHash(repoRoot, recorded, environment.HOME ?? os.homedir()) !== state.inputsHash) {\n drift.push('.doom configuration changed');\n }\n // Re-resolving is what catches a dependency upgrade moving a package, which\n // the inputs hash deliberately does not read.\n if (\n JSON.stringify(recordResolvedEntries(loadMajorModesConfig(repoRoot), createLayerResolvers(repoRoot))) !==\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 const agentDirectory = piAgentDirectory(environment);\n const themePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n if (settingsMode === 'persisted') {\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');\n }\n if (projectRegistersDoom(repoRoot)) drift.push(DUPLICATE_REGISTRATION_DRIFT);\n }\n if (!piExtensionAliasIsCurrent(agentDirectory)) drift.push('Pi user extension alias is out of date');\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');\n }\n if (state.baseline.themePath !== themePath || state.baseline.themeName !== DEFAULT_THEME_NAME) {\n drift.push('synced theme location is out of date');\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/** Resolves the matrix, stages it into home-scoped worktree storage, and points Pi at the result. */\nexport class SyncCommand {\n readonly name = SYNC_COMMAND;\n private readonly settingsMode: SyncSettingsMode;\n private readonly homeDirectory: string | undefined;\n\n constructor(options: SyncCommandOptions = {}) {\n this.settingsMode = options.settingsMode ?? 'persisted';\n this.homeDirectory = options.homeDirectory;\n }\n\n matches(args: string[]): boolean {\n return args[0] === this.name;\n }\n\n async execute(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n ): Promise<number> {\n const check = args.includes(CHECK_OPTION);\n const rest = args.slice(1).filter((argument) => argument !== CHECK_OPTION);\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot ? path.resolve(inheritedRoot) : findRepositoryRoot(currentDirectory);\n const defaultMajorMode = loadMajorModesConfig(repoRoot).defaultMajorMode;\n const defaultDomains = loadDomains(repoRoot).defaultDomains;\n const parsed = parseHarnessArgs(\n rest,\n selectionEnvironment(repoRoot, environment),\n currentDirectory,\n defaultMajorMode,\n defaultDomains,\n );\n const selection = toSelection(parsed.options);\n const homeDirectory = this.homeDirectory ?? environment.HOME ?? os.homedir();\n\n if (check) {\n const majorModesConfig = loadMajorModesConfig(repoRoot);\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);\n const drift = collectDrift(\n repoRoot,\n selection,\n located?.state,\n environment,\n this.settingsMode,\n expectedCompositionFingerprint,\n );\n if (located?.layout === 'legacy') drift.unshift('legacy repository-local sync state requires migration');\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 const progress = new SyncProgress(output);\n const releaseLock = await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n let result: SyncResult;\n try {\n result = await this.stage(repoRoot, parsed.options, environment, homeDirectory, progress);\n } finally {\n await releaseLock();\n }\n emitFrontendHooks(repoRoot, output);\n output.write(formatSyncResult(result, this.settingsMode === 'embedded' ? 'dpi' : 'pi'));\n return 0;\n }\n\n private async stage(\n repoRoot: string,\n options: Omit<HarnessOptions, 'repoRoot'>,\n environment: NodeJS.ProcessEnv,\n homeDirectory: string,\n progress: SyncProgress,\n ): Promise<SyncResult> {\n const location = resolveSyncLocation(repoRoot, homeDirectory);\n const directory = location.directory;\n // A full regeneration, so nothing a previous selection staged survives. Keep\n // per-session run directories because another Pi session may be using one,\n // and retain content-addressed cache and dist artifacts across selections.\n const preserved = new Set([RUN_DIRECTORY, EXTENSION_CACHE_DIRECTORY, MODE_DIST_DIRECTORY, SYNC_LOCK_FILE]);\n for (const entry of fs.existsSync(directory) ? fs.readdirSync(directory) : []) {\n if (!preserved.has(entry)) fs.rmSync(path.join(directory, entry), { recursive: true, force: true });\n }\n\n const staged = progress.start(SYNC_LABEL, 'resolving the matrix and staging resources');\n const context = await buildHarnessContext({\n ...options,\n repoRoot,\n homeDirectory,\n // Sync describes the repository, not the directory it was invoked from.\n cwd: repoRoot,\n resourceDirectory: directory,\n });\n await ensureLayerPackages({\n repoRoot,\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(repoRoot);\n const resolved = recordResolvedEntries(context.majorModesConfig, resolvers);\n const compositionFingerprint = selectionCompositionFingerprint(repoRoot, options);\n const agentDirectory = piAgentDirectory(environment);\n const themeDirectory = piThemeDirectory(agentDirectory);\n await fs.promises.mkdir(themeDirectory, { recursive: true });\n const themePath = await writeDefaultTheme(themeDirectory);\n if (path.resolve(context.defaultThemePath) !== path.resolve(themePath)) {\n await fs.promises.rm(context.defaultThemePath, { force: true });\n }\n const state: SyncState = {\n version: SYNC_STATE_VERSION,\n root: repoRoot,\n identity: location.identity,\n inputsHash: computeInputsHash(repoRoot, selection, homeDirectory),\n compositionFingerprint,\n selection,\n env: recordedEnvironment(context.environment),\n // Read back from the session file the context just wrote, which is the\n // authority for fields deliberately omitted from the child environment.\n fileState: {\n profileEnvironment: loadHarnessState(context.environment).state.profileEnvironment,\n pluginHooks: context.resources.pluginHooks,\n mcpProjection: context.resources.mcpProjection,\n },\n resolved,\n baseline: {\n mcpConfigPath: context.resources.mcpConfigPath,\n personaFile: context.environment[PERSONA_FILE_ENV],\n themePath,\n themeName: DEFAULT_THEME_NAME,\n },\n };\n\n const statePath = await writeSyncState(repoRoot, state, homeDirectory);\n const precompiled = progress.start(RUNTIME_LABEL, 'precompiling the mode bundles');\n const synced = await buildSyncedRuntime(repoRoot, environment, homeDirectory);\n precompiled(`${String(Object.keys(synced.bundles).length)} mode bundles`);\n writePiExtensionAlias(agentDirectory);\n const settingsPath =\n this.settingsMode === 'persisted'\n ? writePiSettings(agentDirectory, {\n themePath: state.baseline.themePath,\n themeName: state.baseline.themeName,\n })\n : undefined;\n // After the user scope is current, so the repository is only ever stripped\n // of a registration the user scope already provides.\n const projectSettingsPath = this.settingsMode === 'persisted' ? writeProjectPiSettings(repoRoot) : undefined;\n\n return {\n statePath,\n ...(settingsPath ? { settingsPath } : {}),\n ...(projectSettingsPath ? { projectSettingsPath } : {}),\n selection,\n mcpServers: state.baseline.mcpConfigPath ? readMcpServerNames(state.baseline.mcpConfigPath) : [],\n skillCount: context.resources.skillCount,\n agentCount: context.resources.agentCount,\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { HarnessOptions } from "../types/interfaces/harness.cjs";
|
|
2
2
|
import { SyncSelection, SyncState } from "../adapters/syncState.cjs";
|
|
3
|
+
import { SyncProgressOutput } from "./syncPresenter.cjs";
|
|
3
4
|
//#region src/commands/syncCommand.d.ts
|
|
4
|
-
type SyncOutput =
|
|
5
|
+
type SyncOutput = SyncProgressOutput;
|
|
5
6
|
type SyncSettingsMode = 'persisted' | 'embedded';
|
|
6
7
|
interface SyncCommandOptions {
|
|
7
8
|
settingsMode?: SyncSettingsMode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncCommand.d.cts","names":[],"sources":["../../../src/commands/syncCommand.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"syncCommand.d.cts","names":[],"sources":["../../../src/commands/syncCommand.ts"],"mappings":";;;;KAgGK,aAAa;KAEN;UAEK;EACf,eAAe;;EAEf;;UAGe;EACf;;EAEA;;EAEA;EACA,WAAW;EACX;EACA;EACA;;iBAGc,oBAAoB,aAAa,OAAO,aAAa;;;;;;;;;iBAmBrD,qBAAqB,kBAAkB,aAAa,OAAO,aAAa,OAAO;iBAe/E,YACd,SAAS,KAAK,kEACb;iBASa,gCACd,kBACA,SAAS,KAAK;;iBAsBA,aACd,kBACA,WAAW,eACX,OAAO,uBACP,cAAa,OAAO,YACpB,eAAc,kBACd;iBAuEc,iBAAiB,QAAQ,YAAY;;cAqBxC;WACF;mBACQ;mBACA;EAEjB,YAAY,UAAS;EAKrB,QAAQ;EAIF,QACJ,gBACA,cAAa,OAAO,YACpB,2BACA,SAAQ,aACP;UAuEW"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { HarnessOptions } from "../types/interfaces/harness.mjs";
|
|
2
2
|
import { SyncSelection, SyncState } from "../adapters/syncState.mjs";
|
|
3
|
+
import { SyncProgressOutput } from "./syncPresenter.mjs";
|
|
3
4
|
//#region src/commands/syncCommand.d.ts
|
|
4
|
-
type SyncOutput =
|
|
5
|
+
type SyncOutput = SyncProgressOutput;
|
|
5
6
|
type SyncSettingsMode = 'persisted' | 'embedded';
|
|
6
7
|
interface SyncCommandOptions {
|
|
7
8
|
settingsMode?: SyncSettingsMode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncCommand.d.mts","names":[],"sources":["../../../src/commands/syncCommand.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"syncCommand.d.mts","names":[],"sources":["../../../src/commands/syncCommand.ts"],"mappings":";;;;KAgGK,aAAa;KAEN;UAEK;EACf,eAAe;;EAEf;;UAGe;EACf;;EAEA;;EAEA;EACA,WAAW;EACX;EACA;EACA;;iBAGc,oBAAoB,aAAa,OAAO,aAAa;;;;;;;;;iBAmBrD,qBAAqB,kBAAkB,aAAa,OAAO,aAAa,OAAO;iBAe/E,YACd,SAAS,KAAK,kEACb;iBASa,gCACd,kBACA,SAAS,KAAK;;iBAsBA,aACd,kBACA,WAAW,eACX,OAAO,uBACP,cAAa,OAAO,YACpB,eAAc,kBACd;iBAuEc,iBAAiB,QAAQ,YAAY;;cAqBxC;WACF;mBACQ;mBACA;EAEjB,YAAY,UAAS;EAKrB,QAAQ;EAIF,QACJ,gBACA,cAAa,OAAO,YACpB,2BACA,SAAQ,aACP;UAuEW"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{findRepositoryRoot as e}from"../adapters/repository/repository.mjs";import{acquireSyncLocationLock as t,resolveSyncLocation as n}from"../adapters/syncLocation.mjs";import{PERSONA_ENTRY as r,createLayerResolvers as i,resolveExtensionComposition as a}from"../services/extensionAssembler.mjs";import"../adapters/syncStateContract.mjs";import{computeInputsHash as o,readLocatedSyncState as s,readMcpServerNames as c,recordResolvedEntries as l,syncStateRootMatches as u,writeSyncState as d}from"../adapters/syncState.mjs";import{readBootstrapStatus as f}from"../adapters/bootstrapLocator.mjs";import{DOOMPI_DOMAINS_ENV as p,DOOMPI_MAJOR_MODE_ENV as m,DOOMPI_PROFILE_ENV as h}from"./cli/matrixOptions.mjs";import{parseHarnessArgs as g}from"./cli/options.mjs";import{HARNESS_STATE_POINTER as _,loadHarnessState as v}from"../adapters/config/harnessState.mjs";import{piExtensionAliasIsCurrent as y,writePiExtensionAlias as b}from"../adapters/piExtensionAlias.mjs";import{mergePiSettings as x,piAgentDirectory as S,piThemeDirectory as C,readPiSettings as w,serializePiSettings as T,writePiSettings as E}from"../adapters/piSettings.mjs";import{loadDoomConfig as D}from"../services/config/projectTrust.mjs";import{buildHarnessContext as O}from"../adapters/harnessContext.mjs";import{ensureLayerPackages as k,missingLayerPackageSpecifiers as A}from"../adapters/layerPackageInstaller.mjs";import{DUPLICATE_REGISTRATION_DRIFT as j,projectRegistersDoom as
|
|
2
|
-
`);return}t.write(`hooks: emit-hooks failed (${r.stderr?.trim()||`exit ${String(r.status)}`})\n`)}function $(e,t=`pi`){let{selection:n}=e;return[`mode: ${n.majorMode}`,`domains: ${n.domains.join(`, `)||
|
|
3
|
-
`)}var
|
|
1
|
+
import{findRepositoryRoot as e}from"../adapters/repository/repository.mjs";import{acquireSyncLocationLock as t,resolveSyncLocation as n}from"../adapters/syncLocation.mjs";import{PERSONA_ENTRY as r,createLayerResolvers as i,resolveExtensionComposition as a}from"../services/extensionAssembler.mjs";import"../adapters/syncStateContract.mjs";import{computeInputsHash as o,readLocatedSyncState as s,readMcpServerNames as c,recordResolvedEntries as l,syncStateRootMatches as u,writeSyncState as d}from"../adapters/syncState.mjs";import{readBootstrapStatus as f}from"../adapters/bootstrapLocator.mjs";import{DOOMPI_DOMAINS_ENV as p,DOOMPI_MAJOR_MODE_ENV as m,DOOMPI_PROFILE_ENV as h}from"./cli/matrixOptions.mjs";import{parseHarnessArgs as g}from"./cli/options.mjs";import{HARNESS_STATE_POINTER as _,loadHarnessState as v}from"../adapters/config/harnessState.mjs";import{piExtensionAliasIsCurrent as y,writePiExtensionAlias as b}from"../adapters/piExtensionAlias.mjs";import{mergePiSettings as x,piAgentDirectory as S,piThemeDirectory as C,readPiSettings as w,serializePiSettings as T,writePiSettings as E}from"../adapters/piSettings.mjs";import{loadDoomConfig as D}from"../services/config/projectTrust.mjs";import{buildHarnessContext as O}from"../adapters/harnessContext.mjs";import{ensureLayerPackages as k,missingLayerPackageSpecifiers as A}from"../adapters/layerPackageInstaller.mjs";import{DUPLICATE_REGISTRATION_DRIFT as j,projectRegistersDoom as M,writeProjectPiSettings as ee}from"../adapters/projectPiSettings.mjs";import{buildSyncedRuntime as te}from"../adapters/syncedRuntimeBuilder.mjs";import{SyncProgress as N}from"./syncPresenter.mjs";import P from"node:path";import F from"node:fs";import I from"node:os";import{loadDomains as L}from"@agimon-ai/doompi-config/domains";import{filterHookDisabledLayers as R,loadMajorModesConfig as z,resolveLayers as B}from"@agimon-ai/doompi-config/majorModes";import{spawnSync as V}from"node:child_process";import{DEFAULT_THEME as H,DEFAULT_THEME_NAME as U,writeDefaultTheme as W}from"@agimon-ai/doompi-ui/theme";const G=`--check`,K=P.join(`tools`,`harness`,`emit-hooks.mjs`),q=`(none)`,ne=[`DOOMPI_`],re=[`CLAUDE_PROJECT_DIR`,`CODEX_REPO_ROOT`,`ORIGINAL_REPO_PATH`,`MCP_UI_VIEWER`],ie=new Set([`DOOMPI_CHILD_EXTENSIONS`,`DOOMPI_COMPOSED`,`DOOMPI_MUTE`,`DOOMPI_TEMP_DIR`,_,`PI_SUBAGENT_PI_BINARY`]);function J(e){let t={};for(let[n,r]of Object.entries(e))r===void 0||ie.has(n)||(re.includes(n)||ne.some(e=>n.startsWith(e)))&&(t[n]=r);return t}function Y(e,t){let{selection:n}=D(e);return n?{...t,...n.majorMode&&!t.DOOMPI_MAJOR_MODE?{[m]:n.majorMode}:{},...n.profile&&!t.DOOMPI_PROFILE?{[h]:n.profile}:{},...n.domains&&t.DOOMPI_DOMAINS===void 0?{[p]:n.domains.join(`,`)}:{}}:t}function X(e){return{majorMode:e.majorMode,domains:e.domains,profile:e.profile,preset:e.preset}}function Z(e,t){let n=z(e),o=i(e);return a({agents:t.agents,autoStop:!1,mute:!1,preset:t.preset,personaEntry:o.packageEntry(r),majorMode:t.majorMode,layers:R(n,B(n,t.majorMode),t.hooks),majorModesConfig:n,resolvers:o}).fingerprint}function Q(e,t,n,r=process.env,a=`persisted`,s){if(!n)return[`no sync state: run doompi sync`];let c=[];u(e,n.root)||c.push(`sync state belongs to a different repository`);let d=n.selection;(d.majorMode!==t.majorMode||d.profile!==t.profile||d.preset!==t.preset||d.domains.join(`,`)!==t.domains.join(`,`))&&c.push(`selection changed since the last sync`),o(e,d,r.HOME??I.homedir())!==n.inputsHash&&c.push(`.doom configuration changed`),JSON.stringify(l(z(e),i(e)))!==JSON.stringify(n.resolved)&&c.push(`resolved extension paths changed`),s&&n.compositionFingerprint!==s&&c.push(`extension composition changed`);try{f(e,void 0,r.HOME??I.homedir()).fresh||c.push(`precompiled runtime is missing or stale`)}catch{c.push(`precompiled runtime is missing or stale`)}let p=S(r),m=P.join(C(p),`${U}.json`);if(a===`persisted`){let t=w(p),n=x(t,p,{themePath:m,themeName:U});T(n)!==T(t)&&c.push(`Pi user settings are out of date`),M(e)&&c.push(j)}y(p)||c.push(`Pi user extension alias is out of date`);let h=`${JSON.stringify(H,null,2)}\n`;return(!F.existsSync(m)||F.readFileSync(m,`utf8`)!==h)&&c.push(`Pi user theme is out of date`),(n.baseline.themePath!==m||n.baseline.themeName!==U)&&c.push(`synced theme location is out of date`),c}function ae(e,t){let n=P.join(e,K);if(!F.existsSync(n))return;let r=V(process.execPath,[n,`--write`],{cwd:e,encoding:`utf8`});if(r.status===0){t.write(`hooks: regenerated for Claude Code and Codex
|
|
2
|
+
`);return}t.write(`hooks: emit-hooks failed (${r.stderr?.trim()||`exit ${String(r.status)}`})\n`)}function $(e,t=`pi`){let{selection:n}=e;return[`mode: ${n.majorMode}`,`domains: ${n.domains.join(`, `)||q}`,`profile: ${n.profile??q}`,`skills: ${e.skillCount}`,`agents: ${e.agentCount}`,`mcp: ${e.mcpServers.join(`, `)||q}`,`state: ${e.statePath}`,...e.settingsPath?[`settings: ${e.settingsPath}`]:[],...e.projectSettingsPath?[`project: removed duplicate registration from ${e.projectSettingsPath}`]:[],``,`Run ${t} from the repository root to use it.`,``].join(`
|
|
3
|
+
`)}var oe=class{name=`sync`;settingsMode;homeDirectory;constructor(e={}){this.settingsMode=e.settingsMode??`persisted`,this.homeDirectory=e.homeDirectory}matches(e){return e[0]===this.name}async execute(r,a=process.env,o=process.cwd(),c=process.stdout){let l=r.includes(G),u=r.slice(1).filter(e=>e!==G),d=a.DOOMPI_ROOT,f=d?P.resolve(d):e(o),p=z(f).defaultMajorMode,m=L(f).defaultDomains,h=g(u,Y(f,a),o,p,m),_=X(h.options),v=this.homeDirectory??a.HOME??I.homedir();if(l){let e=z(f),t=A(e,Object.keys(e.layers),i(f));if(t.length>0)return c.write(`doompi sync is out of date:\n${t.map(e=>` configured package is not installed: ${e}`).join(`
|
|
4
4
|
`)}\n`),1;let n;try{n=s(f,v)}catch(e){let t=e instanceof Error?e.message:String(e);return c.write(`doompi sync is out of date:\n ${t}\n`),1}let r=Z(f,h.options),o=Q(f,_,n?.state,a,this.settingsMode,r);return n?.layout===`legacy`&&o.unshift(`legacy repository-local sync state requires migration`),o.length===0?(c.write(`doompi sync is up to date
|
|
5
5
|
`),0):(c.write(`doompi sync is out of date:\n${o.map(e=>` ${e}`).join(`
|
|
6
|
-
`)}\n`),1)}let y=await t(n(f,v)),
|
|
6
|
+
`)}\n`),1)}let y=new N(c),b=await t(n(f,v)),x;try{x=await this.stage(f,h.options,a,v,y)}finally{await b()}return ae(f,c),c.write($(x,this.settingsMode===`embedded`?`dpi`:`pi`)),0}async stage(e,t,r,a,s){let u=n(e,a),f=u.directory,p=new Set([`run`,`cache`,`dist`,`.sync.lock`]);for(let e of F.existsSync(f)?F.readdirSync(f):[])p.has(e)||F.rmSync(P.join(f,e),{recursive:!0,force:!0});let m=s.start(`sync`,`resolving the matrix and staging resources`),h=await O({...t,repoRoot:e,homeDirectory:a,cwd:e,resourceDirectory:f});await k({repoRoot:e,config:h.majorModesConfig,layers:Object.keys(h.majorModesConfig.layers),environment:r}),m(`${String(h.resources.skillCount)} skills, ${String(h.resources.agentCount)} agents`);let g=X(t),_=i(e),y=l(h.majorModesConfig,_),x=Z(e,t),w=S(r),T=C(w);await F.promises.mkdir(T,{recursive:!0});let D=await W(T);P.resolve(h.defaultThemePath)!==P.resolve(D)&&await F.promises.rm(h.defaultThemePath,{force:!0});let A={version:13,root:e,identity:u.identity,inputsHash:o(e,g,a),compositionFingerprint:x,selection:g,env:J(h.environment),fileState:{profileEnvironment:v(h.environment).state.profileEnvironment,pluginHooks:h.resources.pluginHooks,mcpProjection:h.resources.mcpProjection},resolved:y,baseline:{mcpConfigPath:h.resources.mcpConfigPath,personaFile:h.environment.DOOMPI_PERSONA_FILE,themePath:D,themeName:U}},j=await d(e,A,a),M=s.start(`runtime`,`precompiling the mode bundles`),N=await te(e,r,a);M(`${String(Object.keys(N.bundles).length)} mode bundles`),b(w);let I=this.settingsMode===`persisted`?E(w,{themePath:A.baseline.themePath,themeName:A.baseline.themeName}):void 0,L=this.settingsMode===`persisted`?ee(e):void 0;return{statePath:j,...I?{settingsPath:I}:{},...L?{projectSettingsPath:L}:{},selection:g,mcpServers:A.baseline.mcpConfigPath?c(A.baseline.mcpConfigPath):[],skillCount:h.resources.skillCount,agentCount:h.resources.agentCount}}};export{oe as SyncCommand,Q as collectDrift,$ as formatSyncResult,J as recordedEnvironment,Z as selectionCompositionFingerprint,Y as selectionEnvironment,X as toSelection};
|
|
7
7
|
//# sourceMappingURL=syncCommand.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncCommand.mjs","names":[],"sources":["../../../src/commands/syncCommand.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { filterHookDisabledLayers, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport { buildHarnessContext } from '../adapters/harnessContext.ts';\nimport { ensureLayerPackages, missingLayerPackageSpecifiers } from '../adapters/layerPackageInstaller.ts';\nimport { readBootstrapStatus } from '../adapters/bootstrapLocator.ts';\nimport { piExtensionAliasIsCurrent, writePiExtensionAlias } from '../adapters/piExtensionAlias.ts';\nimport {\n mergePiSettings,\n piAgentDirectory,\n piThemeDirectory,\n readPiSettings,\n serializePiSettings,\n writePiSettings,\n} from '../adapters/piSettings.ts';\nimport {\n DUPLICATE_REGISTRATION_DRIFT,\n projectRegistersDoom,\n writeProjectPiSettings,\n} from '../adapters/projectPiSettings.ts';\nimport { buildSyncedRuntime } from '../adapters/syncedRuntimeBuilder.ts';\nimport { acquireSyncLocationLock, resolveSyncLocation } from '../adapters/syncLocation.ts';\nimport {\n computeInputsHash,\n readLocatedSyncState,\n readMcpServerNames,\n recordResolvedEntries,\n SYNC_STATE_VERSION,\n type SyncSelection,\n type SyncState,\n syncStateRootMatches,\n writeSyncState,\n} from '../adapters/syncState.ts';\nimport { HARNESS_STATE_POINTER, loadHarnessState } from '../adapters/config/harnessState';\nimport { loadDomains } from '@agimon-ai/doompi-config/domains';\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { DEFAULT_THEME, DEFAULT_THEME_NAME, writeDefaultTheme } from '@agimon-ai/doompi-ui/theme';\nimport { loadDoomConfig } from '../services/config/projectTrust';\nimport { createLayerResolvers, PERSONA_ENTRY, resolveExtensionComposition } from '../services/extensionAssembler.ts';\nimport type { HarnessOptions } from '../types/interfaces/harness';\nimport { findRepositoryRoot } from '../adapters/repository/repository';\nimport { DOOMPI_DOMAINS_ENV, DOOMPI_MAJOR_MODE_ENV, DOOMPI_PROFILE_ENV } from './cli/matrixOptions.ts';\nimport { parseHarnessArgs } from './cli/options.ts';\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';\nconst HARNESS_ROOT_ENV = 'DOOMPI_ROOT';\nconst PERSONA_FILE_ENV = 'DOOMPI_PERSONA_FILE';\nconst HOOK_EMITTER = path.join('tools', 'harness', 'emit-hooks.mjs');\nconst RUN_DIRECTORY = 'run';\nconst EXTENSION_CACHE_DIRECTORY = 'cache';\nconst MODE_DIST_DIRECTORY = 'dist';\nconst SYNC_LOCK_FILE = '.sync.lock';\nconst NONE = '(none)';\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 = Pick<NodeJS.WritableStream, 'write'>;\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}\n\nexport interface SyncResult {\n statePath: string;\n /** Omitted when DPI supplies the integration as a process-local overlay. */\n settingsPath?: string;\n /** Set only when the repository still carried its own DoomPi registration. */\n projectSettingsPath?: string;\n selection: SyncSelection;\n mcpServers: string[];\n skillCount: number;\n agentCount: number;\n}\n\nexport function recordedEnvironment(environment: NodeJS.ProcessEnv): Record<string, string> {\n const recorded: Record<string, string> = {};\n for (const [key, value] of Object.entries(environment)) {\n if (value === undefined || EXCLUDED_KEYS.has(key)) continue;\n if (RECORDED_KEYS.includes(key) || RECORDED_PREFIXES.some((prefix) => key.startsWith(prefix))) {\n recorded[key] = value;\n }\n }\n return recorded;\n}\n\n/**\n * Layers the repository's declared selection under the usual resolution.\n *\n * `.doom/config.yaml` holds what the repository selects by default, the way\n * init.el does for doom-emacs. Seeding the environment the parser reads keeps\n * the precedence the launcher already documents: an explicit flag wins, then an\n * exported variable, then the declared default.\n */\nexport function selectionEnvironment(repoRoot: string, environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const { selection } = loadDoomConfig(repoRoot);\n if (!selection) return environment;\n return {\n ...environment,\n ...(selection.majorMode && !environment[DOOMPI_MAJOR_MODE_ENV]\n ? { [DOOMPI_MAJOR_MODE_ENV]: selection.majorMode }\n : {}),\n ...(selection.profile && !environment[DOOMPI_PROFILE_ENV] ? { [DOOMPI_PROFILE_ENV]: selection.profile } : {}),\n ...(selection.domains && environment[DOOMPI_DOMAINS_ENV] === undefined\n ? { [DOOMPI_DOMAINS_ENV]: selection.domains.join(',') }\n : {}),\n };\n}\n\nexport function toSelection(\n options: Pick<HarnessOptions, 'majorMode' | 'domains' | 'profile' | 'preset'>,\n): SyncSelection {\n return {\n majorMode: options.majorMode,\n domains: options.domains,\n profile: options.profile,\n preset: options.preset,\n };\n}\n\nexport function selectionCompositionFingerprint(\n repoRoot: string,\n options: Pick<HarnessOptions, 'agents' | 'hooks' | 'majorMode' | 'mcp' | 'preset'>,\n): string {\n const majorModesConfig = loadMajorModesConfig(repoRoot);\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/** Differences between what a sync would produce and what is on disk. */\nexport function collectDrift(\n repoRoot: string,\n selection: SyncSelection,\n state: SyncState | undefined,\n environment: NodeJS.ProcessEnv = process.env,\n settingsMode: SyncSettingsMode = 'persisted',\n expectedCompositionFingerprint?: string,\n): string[] {\n if (!state) return ['no sync state: run doompi sync'];\n const drift: string[] = [];\n if (!syncStateRootMatches(repoRoot, state.root)) drift.push('sync state belongs to a different repository');\n const recorded = state.selection;\n if (\n recorded.majorMode !== selection.majorMode ||\n recorded.profile !== selection.profile ||\n recorded.preset !== selection.preset ||\n recorded.domains.join(',') !== selection.domains.join(',')\n ) {\n drift.push('selection changed since the last sync');\n }\n // Hashed against the recorded selection, not the requested one, so a\n // selection change is reported once rather than as two findings.\n if (computeInputsHash(repoRoot, recorded, environment.HOME ?? os.homedir()) !== state.inputsHash) {\n drift.push('.doom configuration changed');\n }\n // Re-resolving is what catches a dependency upgrade moving a package, which\n // the inputs hash deliberately does not read.\n if (\n JSON.stringify(recordResolvedEntries(loadMajorModesConfig(repoRoot), createLayerResolvers(repoRoot))) !==\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 const agentDirectory = piAgentDirectory(environment);\n const themePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n if (settingsMode === 'persisted') {\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');\n }\n if (projectRegistersDoom(repoRoot)) drift.push(DUPLICATE_REGISTRATION_DRIFT);\n }\n if (!piExtensionAliasIsCurrent(agentDirectory)) drift.push('Pi user extension alias is out of date');\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');\n }\n if (state.baseline.themePath !== themePath || state.baseline.themeName !== DEFAULT_THEME_NAME) {\n drift.push('synced theme location is out of date');\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/** Resolves the matrix, stages it into home-scoped worktree storage, and points Pi at the result. */\nexport class SyncCommand {\n readonly name = SYNC_COMMAND;\n private readonly settingsMode: SyncSettingsMode;\n private readonly homeDirectory: string | undefined;\n\n constructor(options: SyncCommandOptions = {}) {\n this.settingsMode = options.settingsMode ?? 'persisted';\n this.homeDirectory = options.homeDirectory;\n }\n\n matches(args: string[]): boolean {\n return args[0] === this.name;\n }\n\n async execute(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n ): Promise<number> {\n const check = args.includes(CHECK_OPTION);\n const rest = args.slice(1).filter((argument) => argument !== CHECK_OPTION);\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot ? path.resolve(inheritedRoot) : findRepositoryRoot(currentDirectory);\n const defaultMajorMode = loadMajorModesConfig(repoRoot).defaultMajorMode;\n const defaultDomains = loadDomains(repoRoot).defaultDomains;\n const parsed = parseHarnessArgs(\n rest,\n selectionEnvironment(repoRoot, environment),\n currentDirectory,\n defaultMajorMode,\n defaultDomains,\n );\n const selection = toSelection(parsed.options);\n const homeDirectory = this.homeDirectory ?? environment.HOME ?? os.homedir();\n\n if (check) {\n const majorModesConfig = loadMajorModesConfig(repoRoot);\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);\n const drift = collectDrift(\n repoRoot,\n selection,\n located?.state,\n environment,\n this.settingsMode,\n expectedCompositionFingerprint,\n );\n if (located?.layout === 'legacy') drift.unshift('legacy repository-local sync state requires migration');\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 const releaseLock = await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n let result: SyncResult;\n try {\n result = await this.stage(repoRoot, parsed.options, environment, homeDirectory);\n } finally {\n await releaseLock();\n }\n emitFrontendHooks(repoRoot, output);\n output.write(formatSyncResult(result, this.settingsMode === 'embedded' ? 'dpi' : 'pi'));\n return 0;\n }\n\n private async stage(\n repoRoot: string,\n options: Omit<HarnessOptions, 'repoRoot'>,\n environment: NodeJS.ProcessEnv,\n homeDirectory: string,\n ): Promise<SyncResult> {\n const location = resolveSyncLocation(repoRoot, homeDirectory);\n const directory = location.directory;\n // A full regeneration, so nothing a previous selection staged survives. Keep\n // per-session run directories because another Pi session may be using one,\n // and retain content-addressed cache and dist artifacts across selections.\n const preserved = new Set([RUN_DIRECTORY, EXTENSION_CACHE_DIRECTORY, MODE_DIST_DIRECTORY, SYNC_LOCK_FILE]);\n for (const entry of fs.existsSync(directory) ? fs.readdirSync(directory) : []) {\n if (!preserved.has(entry)) fs.rmSync(path.join(directory, entry), { recursive: true, force: true });\n }\n\n const context = await buildHarnessContext({\n ...options,\n repoRoot,\n homeDirectory,\n // Sync describes the repository, not the directory it was invoked from.\n cwd: repoRoot,\n resourceDirectory: directory,\n });\n await ensureLayerPackages({\n repoRoot,\n config: context.majorModesConfig,\n layers: Object.keys(context.majorModesConfig.layers),\n environment,\n });\n const selection = toSelection(options);\n const resolvers = createLayerResolvers(repoRoot);\n const resolved = recordResolvedEntries(context.majorModesConfig, resolvers);\n const compositionFingerprint = selectionCompositionFingerprint(repoRoot, options);\n const agentDirectory = piAgentDirectory(environment);\n const themeDirectory = piThemeDirectory(agentDirectory);\n await fs.promises.mkdir(themeDirectory, { recursive: true });\n const themePath = await writeDefaultTheme(themeDirectory);\n if (path.resolve(context.defaultThemePath) !== path.resolve(themePath)) {\n await fs.promises.rm(context.defaultThemePath, { force: true });\n }\n const state: SyncState = {\n version: SYNC_STATE_VERSION,\n root: repoRoot,\n identity: location.identity,\n inputsHash: computeInputsHash(repoRoot, selection, homeDirectory),\n compositionFingerprint,\n selection,\n env: recordedEnvironment(context.environment),\n // Read back from the session file the context just wrote, which is the\n // authority for fields deliberately omitted from the child environment.\n fileState: {\n profileEnvironment: loadHarnessState(context.environment).state.profileEnvironment,\n pluginHooks: context.resources.pluginHooks,\n mcpProjection: context.resources.mcpProjection,\n },\n resolved,\n baseline: {\n mcpConfigPath: context.resources.mcpConfigPath,\n personaFile: context.environment[PERSONA_FILE_ENV],\n themePath,\n themeName: DEFAULT_THEME_NAME,\n },\n };\n\n const statePath = await writeSyncState(repoRoot, state, homeDirectory);\n await buildSyncedRuntime(repoRoot, environment, homeDirectory);\n writePiExtensionAlias(agentDirectory);\n const settingsPath =\n this.settingsMode === 'persisted'\n ? writePiSettings(agentDirectory, {\n themePath: state.baseline.themePath,\n themeName: state.baseline.themeName,\n })\n : undefined;\n // After the user scope is current, so the repository is only ever stripped\n // of a registration the user scope already provides.\n const projectSettingsPath = this.settingsMode === 'persisted' ? writeProjectPiSettings(repoRoot) : undefined;\n\n return {\n statePath,\n ...(settingsPath ? { settingsPath } : {}),\n ...(projectSettingsPath ? { projectSettingsPath } : {}),\n selection,\n mcpServers: state.baseline.mcpConfigPath ? readMcpServerNames(state.baseline.mcpConfigPath) : [],\n skillCount: context.resources.skillCount,\n agentCount: context.resources.agentCount,\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"syncCommand.mjs","names":[],"sources":["../../../src/commands/syncCommand.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { filterHookDisabledLayers, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport { buildHarnessContext } from '../adapters/harnessContext.ts';\nimport { ensureLayerPackages, missingLayerPackageSpecifiers } from '../adapters/layerPackageInstaller.ts';\nimport { readBootstrapStatus } from '../adapters/bootstrapLocator.ts';\nimport { piExtensionAliasIsCurrent, writePiExtensionAlias } from '../adapters/piExtensionAlias.ts';\nimport {\n mergePiSettings,\n piAgentDirectory,\n piThemeDirectory,\n readPiSettings,\n serializePiSettings,\n writePiSettings,\n} from '../adapters/piSettings.ts';\nimport {\n DUPLICATE_REGISTRATION_DRIFT,\n projectRegistersDoom,\n writeProjectPiSettings,\n} from '../adapters/projectPiSettings.ts';\nimport { buildSyncedRuntime } from '../adapters/syncedRuntimeBuilder.ts';\nimport { acquireSyncLocationLock, resolveSyncLocation } from '../adapters/syncLocation.ts';\nimport {\n computeInputsHash,\n readLocatedSyncState,\n readMcpServerNames,\n recordResolvedEntries,\n SYNC_STATE_VERSION,\n type SyncSelection,\n type SyncState,\n syncStateRootMatches,\n writeSyncState,\n} from '../adapters/syncState.ts';\nimport { HARNESS_STATE_POINTER, loadHarnessState } from '../adapters/config/harnessState';\nimport { loadDomains } from '@agimon-ai/doompi-config/domains';\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { DEFAULT_THEME, DEFAULT_THEME_NAME, writeDefaultTheme } from '@agimon-ai/doompi-ui/theme';\nimport { loadDoomConfig } from '../services/config/projectTrust';\nimport { createLayerResolvers, PERSONA_ENTRY, resolveExtensionComposition } from '../services/extensionAssembler.ts';\nimport type { HarnessOptions } from '../types/interfaces/harness';\nimport { findRepositoryRoot } from '../adapters/repository/repository';\nimport { DOOMPI_DOMAINS_ENV, DOOMPI_MAJOR_MODE_ENV, DOOMPI_PROFILE_ENV } from './cli/matrixOptions.ts';\nimport { parseHarnessArgs } from './cli/options.ts';\nimport { SyncProgress, type SyncProgressOutput } from './syncPresenter.ts';\n\n/**\n * `doom-pi sync`: resolve the matrix once and write it where plain Pi finds it.\n *\n * The doom-emacs split. Everything that needs a real Node process (module\n * resolution, staging skills and agents, generating the MCP config) happens\n * here, and the doom-pi extension then only reads what this produced. The\n * launcher is untouched and keeps resolving the same matrix per run.\n */\n\nconst SYNC_COMMAND = 'sync';\nconst CHECK_OPTION = '--check';\nconst HARNESS_ROOT_ENV = 'DOOMPI_ROOT';\nconst PERSONA_FILE_ENV = 'DOOMPI_PERSONA_FILE';\nconst HOOK_EMITTER = path.join('tools', 'harness', 'emit-hooks.mjs');\nconst RUN_DIRECTORY = 'run';\nconst EXTENSION_CACHE_DIRECTORY = 'cache';\nconst MODE_DIST_DIRECTORY = 'dist';\nconst SYNC_LOCK_FILE = '.sync.lock';\nconst NONE = '(none)';\nconst SYNC_LABEL = 'sync';\nconst RUNTIME_LABEL = 'runtime';\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}\n\nexport interface SyncResult {\n statePath: string;\n /** Omitted when DPI supplies the integration as a process-local overlay. */\n settingsPath?: string;\n /** Set only when the repository still carried its own DoomPi registration. */\n projectSettingsPath?: string;\n selection: SyncSelection;\n mcpServers: string[];\n skillCount: number;\n agentCount: number;\n}\n\nexport function recordedEnvironment(environment: NodeJS.ProcessEnv): Record<string, string> {\n const recorded: Record<string, string> = {};\n for (const [key, value] of Object.entries(environment)) {\n if (value === undefined || EXCLUDED_KEYS.has(key)) continue;\n if (RECORDED_KEYS.includes(key) || RECORDED_PREFIXES.some((prefix) => key.startsWith(prefix))) {\n recorded[key] = value;\n }\n }\n return recorded;\n}\n\n/**\n * Layers the repository's declared selection under the usual resolution.\n *\n * `.doom/config.yaml` holds what the repository selects by default, the way\n * init.el does for doom-emacs. Seeding the environment the parser reads keeps\n * the precedence the launcher already documents: an explicit flag wins, then an\n * exported variable, then the declared default.\n */\nexport function selectionEnvironment(repoRoot: string, environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const { selection } = loadDoomConfig(repoRoot);\n if (!selection) return environment;\n return {\n ...environment,\n ...(selection.majorMode && !environment[DOOMPI_MAJOR_MODE_ENV]\n ? { [DOOMPI_MAJOR_MODE_ENV]: selection.majorMode }\n : {}),\n ...(selection.profile && !environment[DOOMPI_PROFILE_ENV] ? { [DOOMPI_PROFILE_ENV]: selection.profile } : {}),\n ...(selection.domains && environment[DOOMPI_DOMAINS_ENV] === undefined\n ? { [DOOMPI_DOMAINS_ENV]: selection.domains.join(',') }\n : {}),\n };\n}\n\nexport function toSelection(\n options: Pick<HarnessOptions, 'majorMode' | 'domains' | 'profile' | 'preset'>,\n): SyncSelection {\n return {\n majorMode: options.majorMode,\n domains: options.domains,\n profile: options.profile,\n preset: options.preset,\n };\n}\n\nexport function selectionCompositionFingerprint(\n repoRoot: string,\n options: Pick<HarnessOptions, 'agents' | 'hooks' | 'majorMode' | 'mcp' | 'preset'>,\n): string {\n const majorModesConfig = loadMajorModesConfig(repoRoot);\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/** Differences between what a sync would produce and what is on disk. */\nexport function collectDrift(\n repoRoot: string,\n selection: SyncSelection,\n state: SyncState | undefined,\n environment: NodeJS.ProcessEnv = process.env,\n settingsMode: SyncSettingsMode = 'persisted',\n expectedCompositionFingerprint?: string,\n): string[] {\n if (!state) return ['no sync state: run doompi sync'];\n const drift: string[] = [];\n if (!syncStateRootMatches(repoRoot, state.root)) drift.push('sync state belongs to a different repository');\n const recorded = state.selection;\n if (\n recorded.majorMode !== selection.majorMode ||\n recorded.profile !== selection.profile ||\n recorded.preset !== selection.preset ||\n recorded.domains.join(',') !== selection.domains.join(',')\n ) {\n drift.push('selection changed since the last sync');\n }\n // Hashed against the recorded selection, not the requested one, so a\n // selection change is reported once rather than as two findings.\n if (computeInputsHash(repoRoot, recorded, environment.HOME ?? os.homedir()) !== state.inputsHash) {\n drift.push('.doom configuration changed');\n }\n // Re-resolving is what catches a dependency upgrade moving a package, which\n // the inputs hash deliberately does not read.\n if (\n JSON.stringify(recordResolvedEntries(loadMajorModesConfig(repoRoot), createLayerResolvers(repoRoot))) !==\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 const agentDirectory = piAgentDirectory(environment);\n const themePath = path.join(piThemeDirectory(agentDirectory), `${DEFAULT_THEME_NAME}.json`);\n if (settingsMode === 'persisted') {\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');\n }\n if (projectRegistersDoom(repoRoot)) drift.push(DUPLICATE_REGISTRATION_DRIFT);\n }\n if (!piExtensionAliasIsCurrent(agentDirectory)) drift.push('Pi user extension alias is out of date');\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');\n }\n if (state.baseline.themePath !== themePath || state.baseline.themeName !== DEFAULT_THEME_NAME) {\n drift.push('synced theme location is out of date');\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/** Resolves the matrix, stages it into home-scoped worktree storage, and points Pi at the result. */\nexport class SyncCommand {\n readonly name = SYNC_COMMAND;\n private readonly settingsMode: SyncSettingsMode;\n private readonly homeDirectory: string | undefined;\n\n constructor(options: SyncCommandOptions = {}) {\n this.settingsMode = options.settingsMode ?? 'persisted';\n this.homeDirectory = options.homeDirectory;\n }\n\n matches(args: string[]): boolean {\n return args[0] === this.name;\n }\n\n async execute(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncOutput = process.stdout,\n ): Promise<number> {\n const check = args.includes(CHECK_OPTION);\n const rest = args.slice(1).filter((argument) => argument !== CHECK_OPTION);\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot ? path.resolve(inheritedRoot) : findRepositoryRoot(currentDirectory);\n const defaultMajorMode = loadMajorModesConfig(repoRoot).defaultMajorMode;\n const defaultDomains = loadDomains(repoRoot).defaultDomains;\n const parsed = parseHarnessArgs(\n rest,\n selectionEnvironment(repoRoot, environment),\n currentDirectory,\n defaultMajorMode,\n defaultDomains,\n );\n const selection = toSelection(parsed.options);\n const homeDirectory = this.homeDirectory ?? environment.HOME ?? os.homedir();\n\n if (check) {\n const majorModesConfig = loadMajorModesConfig(repoRoot);\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);\n const drift = collectDrift(\n repoRoot,\n selection,\n located?.state,\n environment,\n this.settingsMode,\n expectedCompositionFingerprint,\n );\n if (located?.layout === 'legacy') drift.unshift('legacy repository-local sync state requires migration');\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 const progress = new SyncProgress(output);\n const releaseLock = await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n let result: SyncResult;\n try {\n result = await this.stage(repoRoot, parsed.options, environment, homeDirectory, progress);\n } finally {\n await releaseLock();\n }\n emitFrontendHooks(repoRoot, output);\n output.write(formatSyncResult(result, this.settingsMode === 'embedded' ? 'dpi' : 'pi'));\n return 0;\n }\n\n private async stage(\n repoRoot: string,\n options: Omit<HarnessOptions, 'repoRoot'>,\n environment: NodeJS.ProcessEnv,\n homeDirectory: string,\n progress: SyncProgress,\n ): Promise<SyncResult> {\n const location = resolveSyncLocation(repoRoot, homeDirectory);\n const directory = location.directory;\n // A full regeneration, so nothing a previous selection staged survives. Keep\n // per-session run directories because another Pi session may be using one,\n // and retain content-addressed cache and dist artifacts across selections.\n const preserved = new Set([RUN_DIRECTORY, EXTENSION_CACHE_DIRECTORY, MODE_DIST_DIRECTORY, SYNC_LOCK_FILE]);\n for (const entry of fs.existsSync(directory) ? fs.readdirSync(directory) : []) {\n if (!preserved.has(entry)) fs.rmSync(path.join(directory, entry), { recursive: true, force: true });\n }\n\n const staged = progress.start(SYNC_LABEL, 'resolving the matrix and staging resources');\n const context = await buildHarnessContext({\n ...options,\n repoRoot,\n homeDirectory,\n // Sync describes the repository, not the directory it was invoked from.\n cwd: repoRoot,\n resourceDirectory: directory,\n });\n await ensureLayerPackages({\n repoRoot,\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(repoRoot);\n const resolved = recordResolvedEntries(context.majorModesConfig, resolvers);\n const compositionFingerprint = selectionCompositionFingerprint(repoRoot, options);\n const agentDirectory = piAgentDirectory(environment);\n const themeDirectory = piThemeDirectory(agentDirectory);\n await fs.promises.mkdir(themeDirectory, { recursive: true });\n const themePath = await writeDefaultTheme(themeDirectory);\n if (path.resolve(context.defaultThemePath) !== path.resolve(themePath)) {\n await fs.promises.rm(context.defaultThemePath, { force: true });\n }\n const state: SyncState = {\n version: SYNC_STATE_VERSION,\n root: repoRoot,\n identity: location.identity,\n inputsHash: computeInputsHash(repoRoot, selection, homeDirectory),\n compositionFingerprint,\n selection,\n env: recordedEnvironment(context.environment),\n // Read back from the session file the context just wrote, which is the\n // authority for fields deliberately omitted from the child environment.\n fileState: {\n profileEnvironment: loadHarnessState(context.environment).state.profileEnvironment,\n pluginHooks: context.resources.pluginHooks,\n mcpProjection: context.resources.mcpProjection,\n },\n resolved,\n baseline: {\n mcpConfigPath: context.resources.mcpConfigPath,\n personaFile: context.environment[PERSONA_FILE_ENV],\n themePath,\n themeName: DEFAULT_THEME_NAME,\n },\n };\n\n const statePath = await writeSyncState(repoRoot, state, homeDirectory);\n const precompiled = progress.start(RUNTIME_LABEL, 'precompiling the mode bundles');\n const synced = await buildSyncedRuntime(repoRoot, environment, homeDirectory);\n precompiled(`${String(Object.keys(synced.bundles).length)} mode bundles`);\n writePiExtensionAlias(agentDirectory);\n const settingsPath =\n this.settingsMode === 'persisted'\n ? writePiSettings(agentDirectory, {\n themePath: state.baseline.themePath,\n themeName: state.baseline.themeName,\n })\n : undefined;\n // After the user scope is current, so the repository is only ever stripped\n // of a registration the user scope already provides.\n const projectSettingsPath = this.settingsMode === 'persisted' ? writeProjectPiSettings(repoRoot) : undefined;\n\n return {\n statePath,\n ...(settingsPath ? { settingsPath } : {}),\n ...(projectSettingsPath ? { projectSettingsPath } : {}),\n selection,\n mcpServers: state.baseline.mcpConfigPath ? readMcpServerNames(state.baseline.mcpConfigPath) : [],\n skillCount: context.resources.skillCount,\n agentCount: context.resources.agentCount,\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./syncCommand.cjs"),
|
|
1
|
+
const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../adapters/repository/repository.cjs"),n=require("../adapters/layerPackageInstaller.cjs"),r=require("./syncPresenter.cjs"),i=require("./syncCommand.cjs"),a=require("./buildCommand.cjs");let o=require("node:path");o=e.__toESM(o,1);let s=require("@agimon-ai/doompi-config/majorModes");const c=`--check`,l=`packages`;function u(e,t){return`${String(e)} ${t}${e===1?``:`s`}`}function d(e){let t=[];return e.updated.length>0&&t.push(`updated ${u(e.updated.length,`package`)}`),e.installed.length>0&&t.push(`installed ${u(e.installed.length,`missing package`)}`),t.length===0&&t.push(`already up to date`),e.unchecked.length>0&&t.push(`${u(e.unchecked.length,`package`)} left unchecked`),t.join(`, `)}var f=class{settingsMode;telemetry;constructor(e={}){this.settingsMode=e.settingsMode??`persisted`,this.telemetry=e.telemetry}async execute(e,t=process.env,n=process.cwd(),o=process.stdout){if(e.includes(c))return new i.SyncCommand({settingsMode:this.settingsMode}).execute(e,t,n,o);let s=new r.SyncProgress(o);await this.refreshPackages(t,n,s);let l=[],u=s.start(`build`,`compiling the mode extension`),d=await new a.BuildCommand(this.telemetry).execute([`build`,...e.slice(1).filter(e=>e!==c)],t,n,{write:e=>(l.push(String(e)),!0)});return d===0?(u(`mode extension compiled`),new i.SyncCommand({settingsMode:this.settingsMode}).execute(e,t,n,o)):(u(`failed`),o.write(l.join(``)),d)}async refreshPackages(e,r,i){let a=e.DOOMPI_ROOT,c=a?o.default.resolve(a):t.findRepositoryRoot(r),u=(0,s.loadMajorModesConfig)(c);i.start(l,`checking configured packages for updates`)(d(await n.ensureLayerPackages({repoRoot:c,config:u,layers:Object.keys(u.layers),environment:e,refresh:!0,onProgress:e=>i.line(l,e)})))}};exports.SyncPipeline=f;
|
|
2
2
|
//# sourceMappingURL=syncPipeline.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncPipeline.cjs","names":["BuildCommand","
|
|
1
|
+
{"version":3,"file":"syncPipeline.cjs","names":["SyncCommand","SyncProgress","BuildCommand","path","findRepositoryRoot","loadMajorModesConfig","ensureLayerPackages"],"sources":["../../../src/commands/syncPipeline.ts"],"sourcesContent":["import path from 'node:path';\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { ensureLayerPackages, type LayerPackageResult } from '../adapters/layerPackageInstaller.ts';\nimport { findRepositoryRoot } from '../adapters/repository/repository';\nimport type { HarnessTelemetry } from '../adapters/telemetry/logSinkTelemetry.ts';\nimport { BuildCommand } from './buildCommand.ts';\nimport { SyncCommand, type SyncSettingsMode } from './syncCommand.ts';\nimport { SyncProgress, type SyncProgressOutput } from './syncPresenter.ts';\n\nconst CHECK_OPTION = '--check';\nconst HARNESS_ROOT_ENV = 'DOOMPI_ROOT';\nconst BUILD_COMMAND = 'build';\nconst PACKAGES_LABEL = 'packages';\nconst BUILD_LABEL = 'build';\n\nexport interface SyncPipelineOptions {\n settingsMode?: SyncSettingsMode;\n telemetry?: HarnessTelemetry;\n}\n\nfunction pluralize(count: number, noun: string): string {\n return `${String(count)} ${noun}${count === 1 ? '' : 's'}`;\n}\n\nfunction packageSummary(result: LayerPackageResult): string {\n const parts: string[] = [];\n if (result.updated.length > 0) parts.push(`updated ${pluralize(result.updated.length, 'package')}`);\n if (result.installed.length > 0) parts.push(`installed ${pluralize(result.installed.length, 'missing package')}`);\n if (parts.length === 0) parts.push('already up to date');\n if (result.unchecked.length > 0) parts.push(`${pluralize(result.unchecked.length, 'package')} left unchecked`);\n return parts.join(', ');\n}\n\n/** Refreshes packages, runs the private cache build, then commits the synchronized state. */\nexport class SyncPipeline {\n private readonly settingsMode: SyncSettingsMode;\n private readonly telemetry: HarnessTelemetry | undefined;\n\n constructor(options: SyncPipelineOptions = {}) {\n this.settingsMode = options.settingsMode ?? 'persisted';\n this.telemetry = options.telemetry;\n }\n\n async execute(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncProgressOutput = process.stdout,\n ): Promise<number> {\n if (args.includes(CHECK_OPTION)) {\n return new SyncCommand({ settingsMode: this.settingsMode }).execute(args, environment, currentDirectory, output);\n }\n\n const progress = new SyncProgress(output);\n // Packages move before anything reads them: the build compiles the resolved\n // extension files and the sync stages their skills, agents, and MCP servers,\n // so an update landing after either phase would only take effect one sync later.\n await this.refreshPackages(environment, currentDirectory, progress);\n\n const captured: string[] = [];\n const done = progress.start(BUILD_LABEL, 'compiling the mode extension');\n const buildCode = await new BuildCommand(this.telemetry).execute(\n [BUILD_COMMAND, ...args.slice(1).filter((argument) => argument !== CHECK_OPTION)],\n environment,\n currentDirectory,\n {\n write: (chunk: unknown) => {\n captured.push(String(chunk));\n return true;\n },\n },\n );\n if (buildCode !== 0) {\n done('failed');\n // The build phase stays quiet while it succeeds. A failure is the one case\n // where its own report is the only explanation the user has.\n output.write(captured.join(''));\n return buildCode;\n }\n done('mode extension compiled');\n\n return new SyncCommand({ settingsMode: this.settingsMode }).execute(args, environment, currentDirectory, output);\n }\n\n /** Moves every package sync owns to its newest published version. */\n private async refreshPackages(\n environment: NodeJS.ProcessEnv,\n currentDirectory: string,\n progress: SyncProgress,\n ): Promise<void> {\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot ? path.resolve(inheritedRoot) : findRepositoryRoot(currentDirectory);\n const config = loadMajorModesConfig(repoRoot);\n const done = progress.start(PACKAGES_LABEL, 'checking configured packages for updates');\n const result = await ensureLayerPackages({\n repoRoot,\n config,\n // Every declared layer, not only the selected one: sync writes the state a\n // later /mode switch reads without reinstalling.\n layers: Object.keys(config.layers),\n environment,\n refresh: true,\n onProgress: (message) => progress.line(PACKAGES_LABEL, message),\n });\n done(packageSummary(result));\n }\n}\n"],"mappings":"+VASA,MAAM,EAAe,UAGf,EAAiB,WAQvB,SAAS,EAAU,EAAe,EAAsB,CACtD,MAAO,GAAG,OAAO,CAAK,EAAE,GAAG,IAAO,IAAU,EAAI,GAAK,KACvD,CAEA,SAAS,EAAe,EAAoC,CAC1D,IAAM,EAAkB,CAAC,EAKzB,OAJI,EAAO,QAAQ,OAAS,GAAG,EAAM,KAAK,WAAW,EAAU,EAAO,QAAQ,OAAQ,SAAS,GAAG,EAC9F,EAAO,UAAU,OAAS,GAAG,EAAM,KAAK,aAAa,EAAU,EAAO,UAAU,OAAQ,iBAAiB,GAAG,EAC5G,EAAM,SAAW,GAAG,EAAM,KAAK,oBAAoB,EACnD,EAAO,UAAU,OAAS,GAAG,EAAM,KAAK,GAAG,EAAU,EAAO,UAAU,OAAQ,SAAS,EAAE,gBAAgB,EACtG,EAAM,KAAK,IAAI,CACxB,CAGA,IAAa,EAAb,KAA0B,CACxB,aACA,UAEA,YAAY,EAA+B,CAAC,EAAG,CAC7C,KAAK,aAAe,EAAQ,cAAgB,YAC5C,KAAK,UAAY,EAAQ,SAC3B,CAEA,MAAM,QACJ,EACA,EAAiC,QAAQ,IACzC,EAAmB,QAAQ,IAAI,EAC/B,EAA6B,QAAQ,OACpB,CACjB,GAAI,EAAK,SAAS,CAAY,EAC5B,OAAO,IAAIA,EAAAA,YAAY,CAAE,aAAc,KAAK,YAAa,CAAC,CAAC,CAAC,QAAQ,EAAM,EAAa,EAAkB,CAAM,EAGjH,IAAM,EAAW,IAAIC,EAAAA,aAAa,CAAM,EAIxC,MAAM,KAAK,gBAAgB,EAAa,EAAkB,CAAQ,EAElE,IAAM,EAAqB,CAAC,EACtB,EAAO,EAAS,MAAM,QAAa,8BAA8B,EACjE,EAAY,MAAM,IAAIC,EAAAA,aAAa,KAAK,SAAS,CAAC,CAAC,QACvD,CAAC,QAAe,GAAG,EAAK,MAAM,CAAC,CAAC,CAAC,OAAQ,GAAa,IAAa,CAAY,CAAC,EAChF,EACA,EACA,CACE,MAAQ,IACN,EAAS,KAAK,OAAO,CAAK,CAAC,EACpB,GAEX,CACF,EAUA,OATI,IAAc,GAOlB,EAAK,yBAAyB,EAEvB,IAAIF,EAAAA,YAAY,CAAE,aAAc,KAAK,YAAa,CAAC,CAAC,CAAC,QAAQ,EAAM,EAAa,EAAkB,CAAM,IAR7G,EAAK,QAAQ,EAGb,EAAO,MAAM,EAAS,KAAK,EAAE,CAAC,EACvB,EAKX,CAGA,MAAc,gBACZ,EACA,EACA,EACe,CACf,IAAM,EAAgB,EAAY,YAC5B,EAAW,EAAgBG,EAAAA,QAAK,QAAQ,CAAa,EAAIC,EAAAA,mBAAmB,CAAgB,EAC5F,GAAA,EAASC,EAAAA,qBAAAA,CAAqB,CAAQ,EAY5C,EAXsB,MAAM,EAAgB,0CAWzC,CAAC,CAAC,EAAe,MAVCC,EAAAA,oBAAoB,CACvC,WACA,SAGA,OAAQ,OAAO,KAAK,EAAO,MAAM,EACjC,cACA,QAAS,GACT,WAAa,GAAY,EAAS,KAAK,EAAgB,CAAO,CAChE,CAAC,CACyB,CAAC,CAC7B,CACF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{findRepositoryRoot as e}from"../adapters/repository/repository.mjs";import{ensureLayerPackages as t}from"../adapters/layerPackageInstaller.mjs";import{SyncProgress as n}from"./syncPresenter.mjs";import{SyncCommand as r}from"./syncCommand.mjs";import{BuildCommand as i}from"./buildCommand.mjs";import a from"node:path";import{loadMajorModesConfig as o}from"@agimon-ai/doompi-config/majorModes";const s=`--check`,c=`packages`;function l(e,t){return`${String(e)} ${t}${e===1?``:`s`}`}function u(e){let t=[];return e.updated.length>0&&t.push(`updated ${l(e.updated.length,`package`)}`),e.installed.length>0&&t.push(`installed ${l(e.installed.length,`missing package`)}`),t.length===0&&t.push(`already up to date`),e.unchecked.length>0&&t.push(`${l(e.unchecked.length,`package`)} left unchecked`),t.join(`, `)}var d=class{settingsMode;telemetry;constructor(e={}){this.settingsMode=e.settingsMode??`persisted`,this.telemetry=e.telemetry}async execute(e,t=process.env,a=process.cwd(),o=process.stdout){if(e.includes(s))return new r({settingsMode:this.settingsMode}).execute(e,t,a,o);let c=new n(o);await this.refreshPackages(t,a,c);let l=[],u=c.start(`build`,`compiling the mode extension`),d=await new i(this.telemetry).execute([`build`,...e.slice(1).filter(e=>e!==s)],t,a,{write:e=>(l.push(String(e)),!0)});return d===0?(u(`mode extension compiled`),new r({settingsMode:this.settingsMode}).execute(e,t,a,o)):(u(`failed`),o.write(l.join(``)),d)}async refreshPackages(n,r,i){let s=n.DOOMPI_ROOT,l=s?a.resolve(s):e(r),d=o(l);i.start(c,`checking configured packages for updates`)(u(await t({repoRoot:l,config:d,layers:Object.keys(d.layers),environment:n,refresh:!0,onProgress:e=>i.line(c,e)})))}};export{d as SyncPipeline};
|
|
2
2
|
//# sourceMappingURL=syncPipeline.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncPipeline.mjs","names":[],"sources":["../../../src/commands/syncPipeline.ts"],"sourcesContent":["import type { HarnessTelemetry } from '../adapters/telemetry/logSinkTelemetry.ts';\nimport { BuildCommand } from './buildCommand.ts';\nimport { SyncCommand, type SyncSettingsMode } from './syncCommand.ts';\n\nconst CHECK_OPTION = '--check';\
|
|
1
|
+
{"version":3,"file":"syncPipeline.mjs","names":[],"sources":["../../../src/commands/syncPipeline.ts"],"sourcesContent":["import path from 'node:path';\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { ensureLayerPackages, type LayerPackageResult } from '../adapters/layerPackageInstaller.ts';\nimport { findRepositoryRoot } from '../adapters/repository/repository';\nimport type { HarnessTelemetry } from '../adapters/telemetry/logSinkTelemetry.ts';\nimport { BuildCommand } from './buildCommand.ts';\nimport { SyncCommand, type SyncSettingsMode } from './syncCommand.ts';\nimport { SyncProgress, type SyncProgressOutput } from './syncPresenter.ts';\n\nconst CHECK_OPTION = '--check';\nconst HARNESS_ROOT_ENV = 'DOOMPI_ROOT';\nconst BUILD_COMMAND = 'build';\nconst PACKAGES_LABEL = 'packages';\nconst BUILD_LABEL = 'build';\n\nexport interface SyncPipelineOptions {\n settingsMode?: SyncSettingsMode;\n telemetry?: HarnessTelemetry;\n}\n\nfunction pluralize(count: number, noun: string): string {\n return `${String(count)} ${noun}${count === 1 ? '' : 's'}`;\n}\n\nfunction packageSummary(result: LayerPackageResult): string {\n const parts: string[] = [];\n if (result.updated.length > 0) parts.push(`updated ${pluralize(result.updated.length, 'package')}`);\n if (result.installed.length > 0) parts.push(`installed ${pluralize(result.installed.length, 'missing package')}`);\n if (parts.length === 0) parts.push('already up to date');\n if (result.unchecked.length > 0) parts.push(`${pluralize(result.unchecked.length, 'package')} left unchecked`);\n return parts.join(', ');\n}\n\n/** Refreshes packages, runs the private cache build, then commits the synchronized state. */\nexport class SyncPipeline {\n private readonly settingsMode: SyncSettingsMode;\n private readonly telemetry: HarnessTelemetry | undefined;\n\n constructor(options: SyncPipelineOptions = {}) {\n this.settingsMode = options.settingsMode ?? 'persisted';\n this.telemetry = options.telemetry;\n }\n\n async execute(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncProgressOutput = process.stdout,\n ): Promise<number> {\n if (args.includes(CHECK_OPTION)) {\n return new SyncCommand({ settingsMode: this.settingsMode }).execute(args, environment, currentDirectory, output);\n }\n\n const progress = new SyncProgress(output);\n // Packages move before anything reads them: the build compiles the resolved\n // extension files and the sync stages their skills, agents, and MCP servers,\n // so an update landing after either phase would only take effect one sync later.\n await this.refreshPackages(environment, currentDirectory, progress);\n\n const captured: string[] = [];\n const done = progress.start(BUILD_LABEL, 'compiling the mode extension');\n const buildCode = await new BuildCommand(this.telemetry).execute(\n [BUILD_COMMAND, ...args.slice(1).filter((argument) => argument !== CHECK_OPTION)],\n environment,\n currentDirectory,\n {\n write: (chunk: unknown) => {\n captured.push(String(chunk));\n return true;\n },\n },\n );\n if (buildCode !== 0) {\n done('failed');\n // The build phase stays quiet while it succeeds. A failure is the one case\n // where its own report is the only explanation the user has.\n output.write(captured.join(''));\n return buildCode;\n }\n done('mode extension compiled');\n\n return new SyncCommand({ settingsMode: this.settingsMode }).execute(args, environment, currentDirectory, output);\n }\n\n /** Moves every package sync owns to its newest published version. */\n private async refreshPackages(\n environment: NodeJS.ProcessEnv,\n currentDirectory: string,\n progress: SyncProgress,\n ): Promise<void> {\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot ? path.resolve(inheritedRoot) : findRepositoryRoot(currentDirectory);\n const config = loadMajorModesConfig(repoRoot);\n const done = progress.start(PACKAGES_LABEL, 'checking configured packages for updates');\n const result = await ensureLayerPackages({\n repoRoot,\n config,\n // Every declared layer, not only the selected one: sync writes the state a\n // later /mode switch reads without reinstalling.\n layers: Object.keys(config.layers),\n environment,\n refresh: true,\n onProgress: (message) => progress.line(PACKAGES_LABEL, message),\n });\n done(packageSummary(result));\n }\n}\n"],"mappings":"gZASA,MAAM,EAAe,UAGf,EAAiB,WAQvB,SAAS,EAAU,EAAe,EAAsB,CACtD,MAAO,GAAG,OAAO,CAAK,EAAE,GAAG,IAAO,IAAU,EAAI,GAAK,KACvD,CAEA,SAAS,EAAe,EAAoC,CAC1D,IAAM,EAAkB,CAAC,EAKzB,OAJI,EAAO,QAAQ,OAAS,GAAG,EAAM,KAAK,WAAW,EAAU,EAAO,QAAQ,OAAQ,SAAS,GAAG,EAC9F,EAAO,UAAU,OAAS,GAAG,EAAM,KAAK,aAAa,EAAU,EAAO,UAAU,OAAQ,iBAAiB,GAAG,EAC5G,EAAM,SAAW,GAAG,EAAM,KAAK,oBAAoB,EACnD,EAAO,UAAU,OAAS,GAAG,EAAM,KAAK,GAAG,EAAU,EAAO,UAAU,OAAQ,SAAS,EAAE,gBAAgB,EACtG,EAAM,KAAK,IAAI,CACxB,CAGA,IAAa,EAAb,KAA0B,CACxB,aACA,UAEA,YAAY,EAA+B,CAAC,EAAG,CAC7C,KAAK,aAAe,EAAQ,cAAgB,YAC5C,KAAK,UAAY,EAAQ,SAC3B,CAEA,MAAM,QACJ,EACA,EAAiC,QAAQ,IACzC,EAAmB,QAAQ,IAAI,EAC/B,EAA6B,QAAQ,OACpB,CACjB,GAAI,EAAK,SAAS,CAAY,EAC5B,OAAO,IAAI,EAAY,CAAE,aAAc,KAAK,YAAa,CAAC,CAAC,CAAC,QAAQ,EAAM,EAAa,EAAkB,CAAM,EAGjH,IAAM,EAAW,IAAI,EAAa,CAAM,EAIxC,MAAM,KAAK,gBAAgB,EAAa,EAAkB,CAAQ,EAElE,IAAM,EAAqB,CAAC,EACtB,EAAO,EAAS,MAAM,QAAa,8BAA8B,EACjE,EAAY,MAAM,IAAI,EAAa,KAAK,SAAS,CAAC,CAAC,QACvD,CAAC,QAAe,GAAG,EAAK,MAAM,CAAC,CAAC,CAAC,OAAQ,GAAa,IAAa,CAAY,CAAC,EAChF,EACA,EACA,CACE,MAAQ,IACN,EAAS,KAAK,OAAO,CAAK,CAAC,EACpB,GAEX,CACF,EAUA,OATI,IAAc,GAOlB,EAAK,yBAAyB,EAEvB,IAAI,EAAY,CAAE,aAAc,KAAK,YAAa,CAAC,CAAC,CAAC,QAAQ,EAAM,EAAa,EAAkB,CAAM,IAR7G,EAAK,QAAQ,EAGb,EAAO,MAAM,EAAS,KAAK,EAAE,CAAC,EACvB,EAKX,CAGA,MAAc,gBACZ,EACA,EACA,EACe,CACf,IAAM,EAAgB,EAAY,YAC5B,EAAW,EAAgB,EAAK,QAAQ,CAAa,EAAI,EAAmB,CAAgB,EAC5F,EAAS,EAAqB,CAAQ,EAY5C,EAXsB,MAAM,EAAgB,0CAWzC,CAAC,CAAC,EAAe,MAVC,EAAoB,CACvC,WACA,SAGA,OAAQ,OAAO,KAAK,EAAO,MAAM,EACjC,cACA,QAAS,GACT,WAAa,GAAY,EAAS,KAAK,EAAgB,CAAO,CAChE,CAAC,CACyB,CAAC,CAC7B,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=`\x1B[2K\r`;function t(e,t){return`${`${e}:`.padEnd(10)}${t}`}function n(e){return`${(e/1e3).toFixed(1)}s`}var r=class{output;now;live=!1;constructor(e,t=Date.now){this.output=e,this.now=t}start(e,r){let i=this.now();return this.drawLive(t(e,`${r}...`)),r=>{this.clearLive(),this.output.write(`${t(e,`${r} (${n(this.now()-i)})`)}\n`)}}line(e,n){this.clearLive(),this.output.write(`${t(e,n)}\n`)}drawLive(t){this.output.isTTY&&(this.output.write(`${e}${t}`),this.live=!0)}clearLive(){this.live&&=(this.output.write(e),!1)}};exports.SyncProgress=r,exports.formatProgressLine=t;
|
|
2
|
+
//# sourceMappingURL=syncPresenter.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syncPresenter.cjs","names":[],"sources":["../../../src/commands/syncPresenter.ts"],"sourcesContent":["/**\n * Step-by-step progress for `doompi sync`.\n *\n * Sync spends almost all of its wall clock in three places a user cannot see:\n * the registry check, the mode build, and the runtime precompile. A terminal\n * gets a live line that the finished step overwrites, so the transcript stays\n * as short as the existing summary. Anything else (a pipe, CI, the embedded\n * dpi runner) gets one line per completed step and no escape sequences.\n */\n\nconst LABEL_WIDTH = 10;\nconst ANSI_ESCAPE = String.fromCharCode(0x1b);\nconst CLEAR_LINE = `${ANSI_ESCAPE}[2K\\r`;\nconst MILLISECONDS_PER_SECOND = 1000;\n\nexport interface SyncProgressOutput {\n write: NodeJS.WritableStream['write'];\n isTTY?: boolean;\n}\n\n/** Closes the step a `start` call opened, recording how long it took. */\nexport type SyncStepDone = (summary: string) => void;\n\nexport function formatProgressLine(label: string, message: string): string {\n return `${`${label}:`.padEnd(LABEL_WIDTH)}${message}`;\n}\n\nfunction formatElapsed(milliseconds: number): string {\n return `${(milliseconds / MILLISECONDS_PER_SECOND).toFixed(1)}s`;\n}\n\n/** Reports sync phases as they run rather than only after everything is done. */\nexport class SyncProgress {\n private readonly output: SyncProgressOutput;\n private readonly now: () => number;\n private live = false;\n\n constructor(output: SyncProgressOutput, now: () => number = Date.now) {\n this.output = output;\n this.now = now;\n }\n\n /** Opens a timed step and returns the call that closes it. */\n start(label: string, message: string): SyncStepDone {\n const startedAt = this.now();\n this.drawLive(formatProgressLine(label, `${message}...`));\n return (summary) => {\n this.clearLive();\n this.output.write(`${formatProgressLine(label, `${summary} (${formatElapsed(this.now() - startedAt)})`)}\\n`);\n };\n }\n\n /** Writes a standalone line, such as one package moving to a new version. */\n line(label: string, message: string): void {\n this.clearLive();\n this.output.write(`${formatProgressLine(label, message)}\\n`);\n }\n\n private drawLive(text: string): void {\n if (!this.output.isTTY) return;\n this.output.write(`${CLEAR_LINE}${text}`);\n this.live = true;\n }\n\n private clearLive(): void {\n if (!this.live) return;\n this.output.write(CLEAR_LINE);\n this.live = false;\n }\n}\n"],"mappings":"AAUA,MAEM,EAAa,YAWnB,SAAgB,EAAmB,EAAe,EAAyB,CACzE,MAAO,GAAG,GAAG,EAAM,GAAG,OAAO,EAAW,IAAI,GAC9C,CAEA,SAAS,EAAc,EAA8B,CACnD,MAAO,IAAI,EAAe,IAAA,CAAyB,QAAQ,CAAC,EAAE,EAChE,CAGA,IAAa,EAAb,KAA0B,CACxB,OACA,IACA,KAAe,GAEf,YAAY,EAA4B,EAAoB,KAAK,IAAK,CACpE,KAAK,OAAS,EACd,KAAK,IAAM,CACb,CAGA,MAAM,EAAe,EAA+B,CAClD,IAAM,EAAY,KAAK,IAAI,EAE3B,OADA,KAAK,SAAS,EAAmB,EAAO,GAAG,EAAQ,IAAI,CAAC,EAChD,GAAY,CAClB,KAAK,UAAU,EACf,KAAK,OAAO,MAAM,GAAG,EAAmB,EAAO,GAAG,EAAQ,IAAI,EAAc,KAAK,IAAI,EAAI,CAAS,EAAE,EAAE,EAAE,GAAG,CAC7G,CACF,CAGA,KAAK,EAAe,EAAuB,CACzC,KAAK,UAAU,EACf,KAAK,OAAO,MAAM,GAAG,EAAmB,EAAO,CAAO,EAAE,GAAG,CAC7D,CAEA,SAAiB,EAAoB,CAC9B,KAAK,OAAO,QACjB,KAAK,OAAO,MAAM,GAAG,IAAa,GAAM,EACxC,KAAK,KAAO,GACd,CAEA,WAA0B,CACnB,AAEL,KAAK,QADL,KAAK,OAAO,MAAM,CAAU,EAChB,GACd,CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/commands/syncPresenter.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Step-by-step progress for `doompi sync`.
|
|
4
|
+
*
|
|
5
|
+
* Sync spends almost all of its wall clock in three places a user cannot see:
|
|
6
|
+
* the registry check, the mode build, and the runtime precompile. A terminal
|
|
7
|
+
* gets a live line that the finished step overwrites, so the transcript stays
|
|
8
|
+
* as short as the existing summary. Anything else (a pipe, CI, the embedded
|
|
9
|
+
* dpi runner) gets one line per completed step and no escape sequences.
|
|
10
|
+
*/
|
|
11
|
+
interface SyncProgressOutput {
|
|
12
|
+
write: NodeJS.WritableStream['write'];
|
|
13
|
+
isTTY?: boolean;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { SyncProgressOutput };
|
|
17
|
+
//# sourceMappingURL=syncPresenter.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syncPresenter.d.cts","names":[],"sources":["../../../src/commands/syncPresenter.ts"],"mappings":";;;;;;;;;;UAeiB;EACf,OAAO,OAAO;EACd"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/commands/syncPresenter.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Step-by-step progress for `doompi sync`.
|
|
4
|
+
*
|
|
5
|
+
* Sync spends almost all of its wall clock in three places a user cannot see:
|
|
6
|
+
* the registry check, the mode build, and the runtime precompile. A terminal
|
|
7
|
+
* gets a live line that the finished step overwrites, so the transcript stays
|
|
8
|
+
* as short as the existing summary. Anything else (a pipe, CI, the embedded
|
|
9
|
+
* dpi runner) gets one line per completed step and no escape sequences.
|
|
10
|
+
*/
|
|
11
|
+
interface SyncProgressOutput {
|
|
12
|
+
write: NodeJS.WritableStream['write'];
|
|
13
|
+
isTTY?: boolean;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { SyncProgressOutput };
|
|
17
|
+
//# sourceMappingURL=syncPresenter.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syncPresenter.d.mts","names":[],"sources":["../../../src/commands/syncPresenter.ts"],"mappings":";;;;;;;;;;UAeiB;EACf,OAAO,OAAO;EACd"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=`\x1B[2K\r`;function t(e,t){return`${`${e}:`.padEnd(10)}${t}`}function n(e){return`${(e/1e3).toFixed(1)}s`}var r=class{output;now;live=!1;constructor(e,t=Date.now){this.output=e,this.now=t}start(e,r){let i=this.now();return this.drawLive(t(e,`${r}...`)),r=>{this.clearLive(),this.output.write(`${t(e,`${r} (${n(this.now()-i)})`)}\n`)}}line(e,n){this.clearLive(),this.output.write(`${t(e,n)}\n`)}drawLive(t){this.output.isTTY&&(this.output.write(`${e}${t}`),this.live=!0)}clearLive(){this.live&&=(this.output.write(e),!1)}};export{r as SyncProgress,t as formatProgressLine};
|
|
2
|
+
//# sourceMappingURL=syncPresenter.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syncPresenter.mjs","names":[],"sources":["../../../src/commands/syncPresenter.ts"],"sourcesContent":["/**\n * Step-by-step progress for `doompi sync`.\n *\n * Sync spends almost all of its wall clock in three places a user cannot see:\n * the registry check, the mode build, and the runtime precompile. A terminal\n * gets a live line that the finished step overwrites, so the transcript stays\n * as short as the existing summary. Anything else (a pipe, CI, the embedded\n * dpi runner) gets one line per completed step and no escape sequences.\n */\n\nconst LABEL_WIDTH = 10;\nconst ANSI_ESCAPE = String.fromCharCode(0x1b);\nconst CLEAR_LINE = `${ANSI_ESCAPE}[2K\\r`;\nconst MILLISECONDS_PER_SECOND = 1000;\n\nexport interface SyncProgressOutput {\n write: NodeJS.WritableStream['write'];\n isTTY?: boolean;\n}\n\n/** Closes the step a `start` call opened, recording how long it took. */\nexport type SyncStepDone = (summary: string) => void;\n\nexport function formatProgressLine(label: string, message: string): string {\n return `${`${label}:`.padEnd(LABEL_WIDTH)}${message}`;\n}\n\nfunction formatElapsed(milliseconds: number): string {\n return `${(milliseconds / MILLISECONDS_PER_SECOND).toFixed(1)}s`;\n}\n\n/** Reports sync phases as they run rather than only after everything is done. */\nexport class SyncProgress {\n private readonly output: SyncProgressOutput;\n private readonly now: () => number;\n private live = false;\n\n constructor(output: SyncProgressOutput, now: () => number = Date.now) {\n this.output = output;\n this.now = now;\n }\n\n /** Opens a timed step and returns the call that closes it. */\n start(label: string, message: string): SyncStepDone {\n const startedAt = this.now();\n this.drawLive(formatProgressLine(label, `${message}...`));\n return (summary) => {\n this.clearLive();\n this.output.write(`${formatProgressLine(label, `${summary} (${formatElapsed(this.now() - startedAt)})`)}\\n`);\n };\n }\n\n /** Writes a standalone line, such as one package moving to a new version. */\n line(label: string, message: string): void {\n this.clearLive();\n this.output.write(`${formatProgressLine(label, message)}\\n`);\n }\n\n private drawLive(text: string): void {\n if (!this.output.isTTY) return;\n this.output.write(`${CLEAR_LINE}${text}`);\n this.live = true;\n }\n\n private clearLive(): void {\n if (!this.live) return;\n this.output.write(CLEAR_LINE);\n this.live = false;\n }\n}\n"],"mappings":"AAUA,MAEM,EAAa,YAWnB,SAAgB,EAAmB,EAAe,EAAyB,CACzE,MAAO,GAAG,GAAG,EAAM,GAAG,OAAO,EAAW,IAAI,GAC9C,CAEA,SAAS,EAAc,EAA8B,CACnD,MAAO,IAAI,EAAe,IAAA,CAAyB,QAAQ,CAAC,EAAE,EAChE,CAGA,IAAa,EAAb,KAA0B,CACxB,OACA,IACA,KAAe,GAEf,YAAY,EAA4B,EAAoB,KAAK,IAAK,CACpE,KAAK,OAAS,EACd,KAAK,IAAM,CACb,CAGA,MAAM,EAAe,EAA+B,CAClD,IAAM,EAAY,KAAK,IAAI,EAE3B,OADA,KAAK,SAAS,EAAmB,EAAO,GAAG,EAAQ,IAAI,CAAC,EAChD,GAAY,CAClB,KAAK,UAAU,EACf,KAAK,OAAO,MAAM,GAAG,EAAmB,EAAO,GAAG,EAAQ,IAAI,EAAc,KAAK,IAAI,EAAI,CAAS,EAAE,EAAE,EAAE,GAAG,CAC7G,CACF,CAGA,KAAK,EAAe,EAAuB,CACzC,KAAK,UAAU,EACf,KAAK,OAAO,MAAM,GAAG,EAAmB,EAAO,CAAO,EAAE,GAAG,CAC7D,CAEA,SAAiB,EAAoB,CAC9B,KAAK,OAAO,QACjB,KAAK,OAAO,MAAM,GAAG,IAAa,GAAM,EACxC,KAAK,KAAO,GACd,CAEA,WAA0B,CACnB,AAEL,KAAK,QADL,KAAK,OAAO,MAAM,CAAU,EAChB,GACd,CACF"}
|
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.35",
|
|
4
4
|
"description": "Opinionated, composable Pi distribution for scoped agent tools, skills, MCP servers, and developer workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -301,17 +301,17 @@
|
|
|
301
301
|
"rolldown": "1.2.5",
|
|
302
302
|
"typebox": "1.3.16",
|
|
303
303
|
"yaml": "2.9.0",
|
|
304
|
-
"@agimon-ai/doompi-autostop": "0.0.1-alpha.
|
|
305
|
-
"@agimon-ai/doompi-cache": "0.0.1-alpha.
|
|
306
|
-
"@agimon-ai/doompi-config": "0.0.1-alpha.
|
|
307
|
-
"@agimon-ai/doompi-domain": "0.0.1-alpha.
|
|
308
|
-
"@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.
|
|
309
|
-
"@agimon-ai/doompi-
|
|
310
|
-
"@agimon-ai/doompi-
|
|
311
|
-
"@agimon-ai/doompi-
|
|
312
|
-
"@agimon-ai/doompi-
|
|
313
|
-
"@agimon-ai/doompi-
|
|
314
|
-
"@agimon-ai/doompi-
|
|
304
|
+
"@agimon-ai/doompi-autostop": "0.0.1-alpha.13",
|
|
305
|
+
"@agimon-ai/doompi-cache": "0.0.1-alpha.2",
|
|
306
|
+
"@agimon-ai/doompi-config": "0.0.1-alpha.35",
|
|
307
|
+
"@agimon-ai/doompi-domain": "0.0.1-alpha.13",
|
|
308
|
+
"@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.35",
|
|
309
|
+
"@agimon-ai/doompi-major-mode": "0.0.1-alpha.13",
|
|
310
|
+
"@agimon-ai/doompi-profile": "0.0.1-alpha.13",
|
|
311
|
+
"@agimon-ai/doompi-notification": "0.0.1-alpha.13",
|
|
312
|
+
"@agimon-ai/doompi-telemetry": "0.0.1-alpha.35",
|
|
313
|
+
"@agimon-ai/doompi-ui": "0.0.1-alpha.35",
|
|
314
|
+
"@agimon-ai/doompi-skill": "0.0.1-alpha.13"
|
|
315
315
|
},
|
|
316
316
|
"devDependencies": {
|
|
317
317
|
"@earendil-works/pi-ai": "0.84.2",
|
|
@@ -321,7 +321,7 @@
|
|
|
321
321
|
"@vitest/coverage-v8": "4.1.11",
|
|
322
322
|
"typescript": "7.0.2",
|
|
323
323
|
"vitest": "4.1.11",
|
|
324
|
-
"@agimon-ai/doompi-runner": "0.0.1-alpha.
|
|
324
|
+
"@agimon-ai/doompi-runner": "0.0.1-alpha.35"
|
|
325
325
|
},
|
|
326
326
|
"peerDependencies": {
|
|
327
327
|
"@earendil-works/pi-tui": "0.84.2"
|