@agimon-ai/doompi 0.0.1-alpha.58 → 0.0.1-alpha.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -115,8 +115,8 @@ matter, and profiles choose a point of view.
115
115
  ### Major and minor modes
116
116
 
117
117
  A major mode is the base config. It names the extension layers for development, marketing,
118
- or whatever else you do. Define as many as you like; only one is active at a time, and you
119
- can switch it without leaving the session.
118
+ or whatever else you do. Define as many as you like; only one is active at a time. Synchronized
119
+ sessions can reload compatible changes; launcher-owned composition changes require a relaunch.
120
120
 
121
121
  Minor modes are batteries-included switches inside that base. They start off, stack freely,
122
122
  and keep their model tools and skills out of context until turned on. DoomPi ships six:
@@ -371,11 +371,11 @@ generated mode gets it. Short commands still return inline; commands that pass t
371
371
  threshold of 60 seconds by default move into the background with durable logs. Use
372
372
  `background: true` for an immediate detached run or `interactive: true` when the command needs
373
373
  terminal input. Runner IDs and complete raw logs remain available to the current session through
374
- Runner Space at `SPC r l` or the `doom-runner` CLI. Platform-specific RMUX binaries are selected
375
- automatically; non-interactive commands use a supervised subprocess fallback when RMUX is absent,
376
- while interactive commands require RMUX. Conservatively recognized single commands use the matching
377
- RTK stdin filter after completion. Compound commands, pipelines, incompatible formats, and
378
- unsupported commands keep bounded raw output. Remove Runner from `default.packages` to keep Pi's
374
+ Runner Space at `SPC r l` or the `doom-runner` CLI. Runner tries bundled RMUX, then `rmux` on PATH,
375
+ then `tmux` on PATH. If no pane backend accepts the run, non-interactive commands use a supervised
376
+ subprocess; interactive commands require RMUX or tmux. Conservatively recognized single commands
377
+ use the matching RTK stdin filter after completion. Compound commands, pipelines, incompatible
378
+ formats, and unsupported commands keep bounded raw output. Remove Runner from `default.packages` to keep Pi's
379
379
  `bash`, or add it to a named layer when only selected modes should replace `bash`.
380
380
 
381
381
  #### Auto-compact
@@ -464,7 +464,7 @@ handoffs rather than one long conversation.
464
464
 
465
465
  [`@agimon-ai/doompi-voice`][pkg-doompi-voice] captures PCM audio through the local platform helper
466
466
  and can transcribe it locally. Transcript text, session state, and model requests may still cross
467
- process or provider boundaries according to the configured engines. `SPC v v` is one-shot manual
467
+ process or provider boundaries according to the configured engines. `SPC v m` is one-shot manual
468
468
  dictation and never enables Voice tools. `SPC v e` enters autonomous capture and exits it again; only its exact-active
469
469
  TUI session receives the two Voice façades plus the standalone `narrate` tool.
470
470
 
@@ -706,8 +706,8 @@ its owning package's controls; values supplied by callers are not automatically
706
706
 
707
707
  - Run `doompi sync --check` to detect drift or missing configured packages, and `doompi sync`
708
708
  to install them and rebuild synchronized state.
709
- - If RMUX is unavailable, non-interactive Runner calls report the supervised subprocess fallback.
710
- Interactive calls require RMUX. Linux native artifacts require a compatible loader and libc.
709
+ - If neither RMUX nor tmux is available, non-interactive Runner calls report the supervised subprocess
710
+ fallback. Interactive calls require RMUX or tmux. Linux native artifacts require a compatible loader and libc.
711
711
  - Leader menus and overlays require Pi's interactive TUI. Commands and tools remain the interface
712
712
  for JSON, RPC, and other headless sessions where the owning package supports them.
713
713
  - Install a directly loaded Pi subsystem with `pi install npm:@agimon-ai/<package>`. Library consumers
@@ -1,4 +1,4 @@
1
- const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../adapters/repository/repository.cjs"),n=require("../adapters/syncLocation.cjs"),r=require("../adapters/layerPackageInstaller.cjs"),i=require("../adapters/syncDrift.cjs"),a=require("./syncPresenter.cjs"),o=require("./syncCommand.cjs"),s=require("./buildCommand.cjs");let c=require("node:path");c=e.__toESM(c,1);let l=require("node:os");l=e.__toESM(l,1);let u=require("@agimon-ai/doompi-config/majorModes");const d=`--check`,f=`--force`,p=`DOOMPI_ROOT`,m=`packages`;function h(e,t){return`${String(e)} ${t}${e===1?``:`s`}`}function g(e){let t=[];return e.updated.length>0&&t.push(`updated ${h(e.updated.length,`package`)}`),e.installed.length>0&&t.push(`installed ${h(e.installed.length,`missing package`)}`),t.length===0&&t.push(`already up to date`),e.unchecked.length>0&&t.push(`${h(e.unchecked.length,`package`)} left unchecked`),t.join(`, `)}var _=class{settingsMode;telemetry;constructor(e={}){this.settingsMode=e.settingsMode??`persisted`,this.telemetry=e.telemetry}async execute(e,r=process.env,u=process.cwd(),m=process.stdout){if(e.includes(d))return new o.SyncCommand({settingsMode:this.settingsMode}).execute(e,r,u,m);let h=r.HOME??l.default.homedir(),g=r[p],_=g?c.default.resolve(g):t.resolveDoomConfigurationRoot(u,h);if(!e.includes(f)&&i.readSyncDrift({repoRoot:_,homeDirectory:h}).fresh)return m.write(`doompi sync is already up to date
2
- `),0;let v=await n.acquireSyncLocationLock(n.resolveSyncLocation(_,h));try{if(!e.includes(f)&&i.readSyncDrift({repoRoot:_,homeDirectory:h}).fresh)return m.write(`doompi sync is already up to date
3
- `),0;let t=new a.SyncProgress(m);await this.refreshPackages(r,u,t);let n=[],c=t.start(`build`,`compiling the mode extension`),l=await new s.BuildCommand(this.telemetry).execute([`build`,...e.slice(1).filter(e=>e!==d)],r,u,{write:e=>(n.push(String(e)),!0)});return l===0?(c(`mode extension compiled`),await new o.SyncCommand({settingsMode:this.settingsMode,homeDirectory:h,lockHeld:!0}).execute(e,r,u,m)):(c(`failed`),m.write(n.join(``)),l)}finally{await v()}}async refreshPackages(e,n,i){let a=e.HOME??l.default.homedir(),o=e[p],s=o?c.default.resolve(o):t.resolveDoomConfigurationRoot(n,a),d=(0,u.loadMajorModesConfig)(s,a);i.start(m,`checking configured packages for updates`)(g(await r.ensureLayerPackages({repoRoot:s,config:d,layers:Object.keys(d.layers),environment:e,refresh:!0,onProgress:e=>i.line(m,e)})))}};exports.SyncPipeline=_;
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../adapters/repository/repository.cjs"),n=require("../adapters/syncLocation.cjs"),r=require("../adapters/layerPackageInstaller.cjs"),i=require("../adapters/syncDrift.cjs"),a=require("./syncPresenter.cjs"),o=require("./syncCommand.cjs"),s=require("./buildCommand.cjs");let c=require("node:path");c=e.__toESM(c,1);let l=require("node:os");l=e.__toESM(l,1);let u=require("@agimon-ai/doompi-config/majorModes");const d=`--check`,f=`--force`,p=`DOOMPI_ROOT`,m=`packages`;function h(e,t){return`${String(e)} ${t}${e===1?``:`s`}`}function g(e){let t=[];return e.updated.length>0&&t.push(`updated ${h(e.updated.length,`package`)}`),e.installed.length>0&&t.push(`installed ${h(e.installed.length,`missing package`)}`),t.length===0&&t.push(`already up to date`),e.unchecked.length>0&&t.push(`${h(e.unchecked.length,`package`)} left unchecked`),t.join(`, `)}var _=class{settingsMode;telemetry;constructor(e={}){this.settingsMode=e.settingsMode??`persisted`,this.telemetry=e.telemetry}async execute(e,r=process.env,u=process.cwd(),m=process.stdout){if(e.includes(d))return new o.SyncCommand({settingsMode:this.settingsMode}).execute(e,r,u,m);let h=r.HOME??l.default.homedir(),g=r[p],_=g?c.default.resolve(g):t.resolveDoomConfigurationRoot(u,h),v={repoRoot:_,homeDirectory:h,requireWebBundle:!!r.DOOMPI_WEB_PACKAGE_ROOT};if(!e.includes(f)&&i.readSyncDrift(v).fresh)return m.write(`doompi sync is already up to date
2
+ `),0;let y=await n.acquireSyncLocationLock(n.resolveSyncLocation(_,h));try{if(!e.includes(f)&&i.readSyncDrift(v).fresh)return m.write(`doompi sync is already up to date
3
+ `),0;let t=new a.SyncProgress(m);await this.refreshPackages(r,u,t);let n=[],c=t.start(`build`,`compiling the mode extension`),l=await new s.BuildCommand(this.telemetry).execute([`build`,...e.slice(1).filter(e=>e!==d)],r,u,{write:e=>(n.push(String(e)),!0)});return l===0?(c(`mode extension compiled`),await new o.SyncCommand({settingsMode:this.settingsMode,homeDirectory:h,lockHeld:!0}).execute(e,r,u,m)):(c(`failed`),m.write(n.join(``)),l)}finally{await y()}}async refreshPackages(e,n,i){let a=e.HOME??l.default.homedir(),o=e[p],s=o?c.default.resolve(o):t.resolveDoomConfigurationRoot(n,a),d=(0,u.loadMajorModesConfig)(s,a);i.start(m,`checking configured packages for updates`)(g(await r.ensureLayerPackages({repoRoot:s,config:d,layers:Object.keys(d.layers),environment:e,refresh:!0,onProgress:e=>i.line(m,e)})))}};exports.SyncPipeline=_;
4
4
  //# sourceMappingURL=syncPipeline.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"syncPipeline.cjs","names":["SyncCommand","os","path","resolveDoomConfigurationRoot","readSyncDrift","acquireSyncLocationLock","resolveSyncLocation","SyncProgress","BuildCommand","loadMajorModesConfig","ensureLayerPackages"],"sources":["../../../src/commands/syncPipeline.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { ensureLayerPackages, type LayerPackageResult } from '../adapters/layerPackageInstaller.ts';\nimport { resolveDoomConfigurationRoot } from '../adapters/repository/repository';\nimport type { HarnessTelemetry } from '../adapters/telemetry/logSinkTelemetry.ts';\nimport { acquireSyncLocationLock, resolveSyncLocation } from '../adapters/syncLocation.ts';\nimport { readSyncDrift } from '../adapters/syncDrift.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';\n/** Rebuilds and republishes even when nothing drifted. */\nconst FORCE_OPTION = '--force';\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 homeDirectory = environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n\n // Nothing drifted means the packages are current, the mode extension is\n // compiled from these exact bytes, and the published generation already\n // describes them. Refreshing and rebuilding anyway costs seconds per call\n // and, worse, ends in a republished generation that reloads every attached\n // cockpit for no change at all. The cockpit calls this before every session\n // launch, so the cheap answer has to be the common one.\n if (!args.includes(FORCE_OPTION) && readSyncDrift({ repoRoot, homeDirectory }).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n\n const releaseLock = await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n try {\n // A concurrent publisher may have resolved the drift while this command\n // waited for the lock. Do not rebuild and republish the same generation.\n if (!args.includes(FORCE_OPTION) && readSyncDrift({ repoRoot, homeDirectory }).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n const progress = new SyncProgress(output);\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 output.write(captured.join(''));\n return buildCode;\n }\n done('mode extension compiled');\n\n return await new SyncCommand({\n settingsMode: this.settingsMode,\n homeDirectory,\n lockHeld: true,\n }).execute(args, environment, currentDirectory, output);\n } finally {\n await releaseLock();\n }\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 homeDirectory = environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n const config = loadMajorModesConfig(repoRoot, homeDirectory);\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":"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"}
1
+ {"version":3,"file":"syncPipeline.cjs","names":["SyncCommand","os","path","resolveDoomConfigurationRoot","readSyncDrift","acquireSyncLocationLock","resolveSyncLocation","SyncProgress","BuildCommand","loadMajorModesConfig","ensureLayerPackages"],"sources":["../../../src/commands/syncPipeline.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { ensureLayerPackages, type LayerPackageResult } from '../adapters/layerPackageInstaller.ts';\nimport { resolveDoomConfigurationRoot } from '../adapters/repository/repository';\nimport type { HarnessTelemetry } from '../adapters/telemetry/logSinkTelemetry.ts';\nimport { acquireSyncLocationLock, resolveSyncLocation } from '../adapters/syncLocation.ts';\nimport { readSyncDrift } from '../adapters/syncDrift.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';\n/** Rebuilds and republishes even when nothing drifted. */\nconst FORCE_OPTION = '--force';\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 homeDirectory = environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n\n // Nothing drifted means the packages are current, the mode extension is\n // compiled from these exact bytes, and the published generation already\n // describes them. Refreshing and rebuilding anyway costs seconds per call\n // and, worse, ends in a republished generation that reloads every attached\n // cockpit for no change at all. The cockpit calls this before every session\n // launch, so the cheap answer has to be the common one.\n const driftOptions = {\n repoRoot,\n homeDirectory,\n requireWebBundle: Boolean(environment.DOOMPI_WEB_PACKAGE_ROOT),\n };\n if (!args.includes(FORCE_OPTION) && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n\n const releaseLock = await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n try {\n // A concurrent publisher may have resolved the drift while this command\n // waited for the lock. Do not rebuild and republish the same generation.\n if (!args.includes(FORCE_OPTION) && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n const progress = new SyncProgress(output);\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 output.write(captured.join(''));\n return buildCode;\n }\n done('mode extension compiled');\n\n return await new SyncCommand({\n settingsMode: this.settingsMode,\n homeDirectory,\n lockHeld: true,\n }).execute(args, environment, currentDirectory, output);\n } finally {\n await releaseLock();\n }\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 homeDirectory = environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n const config = loadMajorModesConfig(repoRoot, homeDirectory);\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":"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"}
@@ -1,4 +1,4 @@
1
- import{resolveDoomConfigurationRoot as e}from"../adapters/repository/repository.mjs";import{acquireSyncLocationLock as t,resolveSyncLocation as n}from"../adapters/syncLocation.mjs";import{ensureLayerPackages as r}from"../adapters/layerPackageInstaller.mjs";import{readSyncDrift as i}from"../adapters/syncDrift.mjs";import{SyncProgress as a}from"./syncPresenter.mjs";import{SyncCommand as o}from"./syncCommand.mjs";import{BuildCommand as s}from"./buildCommand.mjs";import c from"node:path";import l from"node:os";import{loadMajorModesConfig as u}from"@agimon-ai/doompi-config/majorModes";const d=`--check`,f=`--force`,p=`DOOMPI_ROOT`,m=`packages`;function h(e,t){return`${String(e)} ${t}${e===1?``:`s`}`}function g(e){let t=[];return e.updated.length>0&&t.push(`updated ${h(e.updated.length,`package`)}`),e.installed.length>0&&t.push(`installed ${h(e.installed.length,`missing package`)}`),t.length===0&&t.push(`already up to date`),e.unchecked.length>0&&t.push(`${h(e.unchecked.length,`package`)} left unchecked`),t.join(`, `)}var _=class{settingsMode;telemetry;constructor(e={}){this.settingsMode=e.settingsMode??`persisted`,this.telemetry=e.telemetry}async execute(r,u=process.env,m=process.cwd(),h=process.stdout){if(r.includes(d))return new o({settingsMode:this.settingsMode}).execute(r,u,m,h);let g=u.HOME??l.homedir(),_=u[p],v=_?c.resolve(_):e(m,g);if(!r.includes(f)&&i({repoRoot:v,homeDirectory:g}).fresh)return h.write(`doompi sync is already up to date
2
- `),0;let y=await t(n(v,g));try{if(!r.includes(f)&&i({repoRoot:v,homeDirectory:g}).fresh)return h.write(`doompi sync is already up to date
3
- `),0;let e=new a(h);await this.refreshPackages(u,m,e);let t=[],n=e.start(`build`,`compiling the mode extension`),c=await new s(this.telemetry).execute([`build`,...r.slice(1).filter(e=>e!==d)],u,m,{write:e=>(t.push(String(e)),!0)});return c===0?(n(`mode extension compiled`),await new o({settingsMode:this.settingsMode,homeDirectory:g,lockHeld:!0}).execute(r,u,m,h)):(n(`failed`),h.write(t.join(``)),c)}finally{await y()}}async refreshPackages(t,n,i){let a=t.HOME??l.homedir(),o=t[p],s=o?c.resolve(o):e(n,a),d=u(s,a);i.start(m,`checking configured packages for updates`)(g(await r({repoRoot:s,config:d,layers:Object.keys(d.layers),environment:t,refresh:!0,onProgress:e=>i.line(m,e)})))}};export{_ as SyncPipeline};
1
+ import{resolveDoomConfigurationRoot as e}from"../adapters/repository/repository.mjs";import{acquireSyncLocationLock as t,resolveSyncLocation as n}from"../adapters/syncLocation.mjs";import{ensureLayerPackages as r}from"../adapters/layerPackageInstaller.mjs";import{readSyncDrift as i}from"../adapters/syncDrift.mjs";import{SyncProgress as a}from"./syncPresenter.mjs";import{SyncCommand as o}from"./syncCommand.mjs";import{BuildCommand as s}from"./buildCommand.mjs";import c from"node:path";import l from"node:os";import{loadMajorModesConfig as u}from"@agimon-ai/doompi-config/majorModes";const d=`--check`,f=`--force`,p=`DOOMPI_ROOT`,m=`packages`;function h(e,t){return`${String(e)} ${t}${e===1?``:`s`}`}function g(e){let t=[];return e.updated.length>0&&t.push(`updated ${h(e.updated.length,`package`)}`),e.installed.length>0&&t.push(`installed ${h(e.installed.length,`missing package`)}`),t.length===0&&t.push(`already up to date`),e.unchecked.length>0&&t.push(`${h(e.unchecked.length,`package`)} left unchecked`),t.join(`, `)}var _=class{settingsMode;telemetry;constructor(e={}){this.settingsMode=e.settingsMode??`persisted`,this.telemetry=e.telemetry}async execute(r,u=process.env,m=process.cwd(),h=process.stdout){if(r.includes(d))return new o({settingsMode:this.settingsMode}).execute(r,u,m,h);let g=u.HOME??l.homedir(),_=u[p],v=_?c.resolve(_):e(m,g),y={repoRoot:v,homeDirectory:g,requireWebBundle:!!u.DOOMPI_WEB_PACKAGE_ROOT};if(!r.includes(f)&&i(y).fresh)return h.write(`doompi sync is already up to date
2
+ `),0;let b=await t(n(v,g));try{if(!r.includes(f)&&i(y).fresh)return h.write(`doompi sync is already up to date
3
+ `),0;let e=new a(h);await this.refreshPackages(u,m,e);let t=[],n=e.start(`build`,`compiling the mode extension`),c=await new s(this.telemetry).execute([`build`,...r.slice(1).filter(e=>e!==d)],u,m,{write:e=>(t.push(String(e)),!0)});return c===0?(n(`mode extension compiled`),await new o({settingsMode:this.settingsMode,homeDirectory:g,lockHeld:!0}).execute(r,u,m,h)):(n(`failed`),h.write(t.join(``)),c)}finally{await b()}}async refreshPackages(t,n,i){let a=t.HOME??l.homedir(),o=t[p],s=o?c.resolve(o):e(n,a),d=u(s,a);i.start(m,`checking configured packages for updates`)(g(await r({repoRoot:s,config:d,layers:Object.keys(d.layers),environment:t,refresh:!0,onProgress:e=>i.line(m,e)})))}};export{_ as SyncPipeline};
4
4
  //# sourceMappingURL=syncPipeline.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"syncPipeline.mjs","names":[],"sources":["../../../src/commands/syncPipeline.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { ensureLayerPackages, type LayerPackageResult } from '../adapters/layerPackageInstaller.ts';\nimport { resolveDoomConfigurationRoot } from '../adapters/repository/repository';\nimport type { HarnessTelemetry } from '../adapters/telemetry/logSinkTelemetry.ts';\nimport { acquireSyncLocationLock, resolveSyncLocation } from '../adapters/syncLocation.ts';\nimport { readSyncDrift } from '../adapters/syncDrift.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';\n/** Rebuilds and republishes even when nothing drifted. */\nconst FORCE_OPTION = '--force';\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 homeDirectory = environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n\n // Nothing drifted means the packages are current, the mode extension is\n // compiled from these exact bytes, and the published generation already\n // describes them. Refreshing and rebuilding anyway costs seconds per call\n // and, worse, ends in a republished generation that reloads every attached\n // cockpit for no change at all. The cockpit calls this before every session\n // launch, so the cheap answer has to be the common one.\n if (!args.includes(FORCE_OPTION) && readSyncDrift({ repoRoot, homeDirectory }).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n\n const releaseLock = await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n try {\n // A concurrent publisher may have resolved the drift while this command\n // waited for the lock. Do not rebuild and republish the same generation.\n if (!args.includes(FORCE_OPTION) && readSyncDrift({ repoRoot, homeDirectory }).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n const progress = new SyncProgress(output);\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 output.write(captured.join(''));\n return buildCode;\n }\n done('mode extension compiled');\n\n return await new SyncCommand({\n settingsMode: this.settingsMode,\n homeDirectory,\n lockHeld: true,\n }).execute(args, environment, currentDirectory, output);\n } finally {\n await releaseLock();\n }\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 homeDirectory = environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n const config = loadMajorModesConfig(repoRoot, homeDirectory);\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":"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"}
1
+ {"version":3,"file":"syncPipeline.mjs","names":[],"sources":["../../../src/commands/syncPipeline.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { ensureLayerPackages, type LayerPackageResult } from '../adapters/layerPackageInstaller.ts';\nimport { resolveDoomConfigurationRoot } from '../adapters/repository/repository';\nimport type { HarnessTelemetry } from '../adapters/telemetry/logSinkTelemetry.ts';\nimport { acquireSyncLocationLock, resolveSyncLocation } from '../adapters/syncLocation.ts';\nimport { readSyncDrift } from '../adapters/syncDrift.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';\n/** Rebuilds and republishes even when nothing drifted. */\nconst FORCE_OPTION = '--force';\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 homeDirectory = environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n\n // Nothing drifted means the packages are current, the mode extension is\n // compiled from these exact bytes, and the published generation already\n // describes them. Refreshing and rebuilding anyway costs seconds per call\n // and, worse, ends in a republished generation that reloads every attached\n // cockpit for no change at all. The cockpit calls this before every session\n // launch, so the cheap answer has to be the common one.\n const driftOptions = {\n repoRoot,\n homeDirectory,\n requireWebBundle: Boolean(environment.DOOMPI_WEB_PACKAGE_ROOT),\n };\n if (!args.includes(FORCE_OPTION) && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n\n const releaseLock = await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n try {\n // A concurrent publisher may have resolved the drift while this command\n // waited for the lock. Do not rebuild and republish the same generation.\n if (!args.includes(FORCE_OPTION) && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n const progress = new SyncProgress(output);\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 output.write(captured.join(''));\n return buildCode;\n }\n done('mode extension compiled');\n\n return await new SyncCommand({\n settingsMode: this.settingsMode,\n homeDirectory,\n lockHeld: true,\n }).execute(args, environment, currentDirectory, output);\n } finally {\n await releaseLock();\n }\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 homeDirectory = environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n const config = loadMajorModesConfig(repoRoot, homeDirectory);\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":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi",
3
- "version": "0.0.1-alpha.58",
3
+ "version": "0.0.1-alpha.59",
4
4
  "description": "Opinionated, composable Pi distribution for scoped agent tools, skills, MCP servers, and developer workflows.",
5
5
  "keywords": [
6
6
  "ai",
@@ -320,17 +320,17 @@
320
320
  "rolldown": "1.2.6",
321
321
  "typebox": "1.3.19",
322
322
  "yaml": "2.9.0",
323
- "@agimon-ai/doompi-autostop": "0.0.1-alpha.34",
324
- "@agimon-ai/doompi-cache": "0.0.1-alpha.23",
325
- "@agimon-ai/doompi-config": "0.0.1-alpha.56",
326
- "@agimon-ai/doompi-domain": "0.0.1-alpha.35",
327
- "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.56",
328
- "@agimon-ai/doompi-major-mode": "0.0.1-alpha.35",
329
- "@agimon-ai/doompi-notification": "0.0.1-alpha.34",
330
- "@agimon-ai/doompi-profile": "0.0.1-alpha.35",
331
- "@agimon-ai/doompi-skill": "0.0.1-alpha.35",
323
+ "@agimon-ai/doompi-autostop": "0.0.1-alpha.35",
324
+ "@agimon-ai/doompi-config": "0.0.1-alpha.57",
325
+ "@agimon-ai/doompi-domain": "0.0.1-alpha.36",
326
+ "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.57",
327
+ "@agimon-ai/doompi-cache": "0.0.1-alpha.24",
328
+ "@agimon-ai/doompi-major-mode": "0.0.1-alpha.36",
329
+ "@agimon-ai/doompi-notification": "0.0.1-alpha.35",
330
+ "@agimon-ai/doompi-skill": "0.0.1-alpha.36",
332
331
  "@agimon-ai/doompi-telemetry": "0.0.1-alpha.56",
333
- "@agimon-ai/doompi-ui": "0.0.1-alpha.57"
332
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.58",
333
+ "@agimon-ai/doompi-profile": "0.0.1-alpha.36"
334
334
  },
335
335
  "devDependencies": {
336
336
  "@earendil-works/pi-ai": "0.85.0",
@@ -340,7 +340,7 @@
340
340
  "@vitest/coverage-v8": "4.1.11",
341
341
  "typescript": "7.0.2",
342
342
  "vitest": "4.1.11",
343
- "@agimon-ai/doompi-runner": "0.0.1-alpha.57"
343
+ "@agimon-ai/doompi-runner": "0.0.1-alpha.58"
344
344
  },
345
345
  "peerDependencies": {
346
346
  "@earendil-works/pi-tui": "0.85.0"