ultra-builder-pro-cli 0.5.1 → 0.5.2

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/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.2] — 2026-07-16
11
+
12
+ ### Fixed
13
+
14
+ - Preserved Codex hook entry points for already-running tasks across plugin
15
+ refreshes, including cache versions whose adapter file or entire cache
16
+ directory had already disappeared. Historical cache versions are now kept in
17
+ the managed runtime manifest and restored as validated forwarders to the
18
+ current adapter.
19
+
10
20
  ## [0.5.1] — 2026-07-16
11
21
 
12
22
  ### Changed
@@ -211,7 +221,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
211
221
  skill manifest, CLI protocol + mapping table; 5 spec validators.
212
222
  - **Phase 0 — skeleton**: multi-runtime installer scaffolding.
213
223
 
214
- [Unreleased]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.5.1...HEAD
224
+ [Unreleased]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.5.2...HEAD
225
+ [0.5.2]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.5.1...v0.5.2
215
226
  [0.5.1]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.5.0...v0.5.1
216
227
  [0.5.0]: https://github.com/rocky2431/ultra-builder-pro-cli/releases/tag/v0.5.0
217
228
  [0.4.0]: https://github.com/rocky2431/ultra-builder-pro-cli/releases/tag/v0.4.0
package/README.md CHANGED
@@ -8,7 +8,7 @@ authoritative `.ultra/state.db`.
8
8
 
9
9
  <div align="center">
10
10
 
11
- [![Version](https://img.shields.io/badge/version-0.5.1-blue)](./CHANGELOG.md)
11
+ [![Version](https://img.shields.io/badge/version-0.5.2-blue)](./CHANGELOG.md)
12
12
  [![Tests](https://img.shields.io/badge/tests-passing-brightgreen)](#verification)
13
13
  [![License: MIT](https://img.shields.io/badge/license-MIT-green)](./LICENSE)
14
14
  [![Node](https://img.shields.io/badge/node-%E2%89%A522-informational)](./package.json)
package/adapters/codex.js CHANGED
@@ -191,14 +191,53 @@ function pluginCacheRoot(configDir, marketplaceName) {
191
191
  return path.join(configDir, 'plugins', 'cache', marketplaceName, PLUGIN_NAME);
192
192
  }
193
193
 
194
- function listCachedHookAdapters(configDir, marketplaceName) {
194
+ function normalizeHookCacheVersions(versions) {
195
+ const normalized = new Set();
196
+ for (const version of versions) {
197
+ if (typeof version !== 'string'
198
+ || !/^[A-Za-z0-9][A-Za-z0-9.+_-]*$/.test(version)) {
199
+ throw new Error(`invalid Codex plugin cache version: ${String(version)}`);
200
+ }
201
+ normalized.add(version);
202
+ }
203
+ return [...normalized].sort();
204
+ }
205
+
206
+ function listKnownHookCacheVersions(configDir, marketplaceName, manifest = null) {
207
+ const root = pluginCacheRoot(configDir, marketplaceName);
208
+ const versions = [];
209
+ if (fs.existsSync(root)) {
210
+ versions.push(...fs.readdirSync(root, { withFileTypes: true })
211
+ .filter((entry) => entry.isDirectory() || entry.isSymbolicLink())
212
+ .map((entry) => entry.name));
213
+ }
214
+
215
+ if (manifest && manifest.source === SOURCE_TAG && manifest.adapter === 'codex') {
216
+ if (manifest.plugin && manifest.plugin.version !== undefined) {
217
+ versions.push(manifest.plugin.version);
218
+ }
219
+ if (manifest.hook_cache_versions !== undefined) {
220
+ if (!Array.isArray(manifest.hook_cache_versions)) {
221
+ throw new Error('invalid Codex runtime manifest hook_cache_versions');
222
+ }
223
+ versions.push(...manifest.hook_cache_versions);
224
+ }
225
+ }
226
+ return normalizeHookCacheVersions(versions);
227
+ }
228
+
229
+ function hookAdaptersForVersions(configDir, marketplaceName, versions) {
195
230
  const root = pluginCacheRoot(configDir, marketplaceName);
196
- if (!fs.existsSync(root)) return [];
197
- return fs.readdirSync(root, { withFileTypes: true })
198
- .filter((entry) => entry.isDirectory())
199
- .map((entry) => path.join(root, entry.name, HOOK_ADAPTER_RELATIVE))
200
- .filter((file) => fs.existsSync(file))
201
- .sort();
231
+ return normalizeHookCacheVersions(versions)
232
+ .map((version) => path.join(root, version, HOOK_ADAPTER_RELATIVE));
233
+ }
234
+
235
+ function listCachedHookAdapters(configDir, marketplaceName, manifest = null) {
236
+ return hookAdaptersForVersions(
237
+ configDir,
238
+ marketplaceName,
239
+ listKnownHookCacheVersions(configDir, marketplaceName, manifest),
240
+ );
202
241
  }
203
242
 
204
243
  function hookForwarder(target) {
@@ -211,6 +250,20 @@ runpy.run_path(${JSON.stringify(path.resolve(target))}, run_name="__main__")
211
250
  `;
212
251
  }
213
252
 
253
+ function prepareHookAdapterPath(file) {
254
+ const versionRoot = path.dirname(path.dirname(path.dirname(file)));
255
+ try {
256
+ const stat = fs.lstatSync(versionRoot);
257
+ if (stat.isSymbolicLink()) {
258
+ fs.unlinkSync(versionRoot);
259
+ } else if (!stat.isDirectory()) {
260
+ throw new Error(`Codex plugin cache version is not a directory: ${versionRoot}`);
261
+ }
262
+ } catch (error) {
263
+ if (error.code !== 'ENOENT') throw error;
264
+ }
265
+ }
266
+
214
267
  function restoreCachedHookAdapters(previousAdapters, target) {
215
268
  const resolvedTarget = path.resolve(target);
216
269
  if (!fs.existsSync(resolvedTarget)) {
@@ -219,6 +272,7 @@ function restoreCachedHookAdapters(previousAdapters, target) {
219
272
  const restored = [];
220
273
  for (const previous of previousAdapters) {
221
274
  if (path.resolve(previous) === resolvedTarget || fs.existsSync(previous)) continue;
275
+ prepareHookAdapterPath(previous);
222
276
  writeAtomic(previous, hookForwarder(resolvedTarget));
223
277
  restored.push(previous);
224
278
  }
@@ -239,6 +293,7 @@ function writeRuntimeManifest(configDir, data) {
239
293
  plugin: data.plugin,
240
294
  marketplace: data.marketplace,
241
295
  agents: data.agents,
296
+ hook_cache_versions: data.hookCacheVersions,
242
297
  }, null, 2) + '\n');
243
298
  return file;
244
299
  }
@@ -250,20 +305,25 @@ function install(ctx = {}) {
250
305
  const marketplaceFile = resolveMarketplaceFile(ctx);
251
306
  ensureDir(configDir);
252
307
 
308
+ const previousManifest = readRuntimeManifest(configDir);
253
309
  const legacy = cleanupLegacyConfig(configDir);
254
310
  const plugin = buildPlugin({ repoRoot, pluginRoot });
255
311
  const agents = installAgents({ repoRoot, configDir });
256
312
  const marketplace = upsertMarketplace(marketplaceFile);
257
- const manifestFile = writeRuntimeManifest(configDir, {
258
- plugin: { root: plugin.root, version: plugin.version },
259
- marketplace: { file: marketplace.file, name: marketplace.name },
260
- agents: agents.installed,
261
- });
313
+ const previousVersions = listKnownHookCacheVersions(
314
+ configDir,
315
+ marketplace.name,
316
+ previousManifest,
317
+ );
262
318
 
263
319
  let registration = null;
264
320
  let hookCompatibility = null;
265
321
  if (shouldRunPluginCli(ctx)) {
266
- const previousAdapters = listCachedHookAdapters(configDir, marketplace.name);
322
+ const previousAdapters = hookAdaptersForVersions(
323
+ configDir,
324
+ marketplace.name,
325
+ previousVersions,
326
+ );
267
327
  try {
268
328
  registration = runPluginCli('add', marketplace.name, ctx);
269
329
  } catch (registrationError) {
@@ -285,6 +345,16 @@ function install(ctx = {}) {
285
345
  currentHookAdapter(configDir, marketplace.name, plugin),
286
346
  );
287
347
  }
348
+ const hookCacheVersions = normalizeHookCacheVersions([
349
+ ...previousVersions,
350
+ plugin.version,
351
+ ]);
352
+ const manifestFile = writeRuntimeManifest(configDir, {
353
+ plugin: { root: plugin.root, version: plugin.version },
354
+ marketplace: { file: marketplace.file, name: marketplace.name },
355
+ agents: agents.installed,
356
+ hookCacheVersions,
357
+ });
288
358
 
289
359
  return {
290
360
  target: configDir,
@@ -379,7 +449,9 @@ module.exports = {
379
449
  removeMarketplaceEntry,
380
450
  runPluginCli,
381
451
  pluginCacheRoot,
452
+ listKnownHookCacheVersions,
382
453
  listCachedHookAdapters,
454
+ prepareHookAdapterPath,
383
455
  restoreCachedHookAdapters,
384
456
  },
385
457
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultra-builder-pro-cli",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Multi-runtime installer for the Ultra Builder Pro agent engineering system — deploys commands, agents, skills, hooks, and MCP server to Claude Code, OpenCode, Codex CLI, and Gemini CLI.",
5
5
  "bin": {
6
6
  "ultra-builder-pro-cli": "bin/install.js",