@guiho/runx 0.4.0 → 0.5.1

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.
Files changed (63) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/DOCS.md +24 -4
  3. package/README.md +6 -1
  4. package/devops/build-binaries.ts +3 -3
  5. package/devops/devops.xdocs.md +9 -6
  6. package/devops/extract-release-notes.spec.ts +54 -0
  7. package/devops/extract-release-notes.ts +40 -0
  8. package/devops/install.ps1 +29 -6
  9. package/devops/install.sh +36 -11
  10. package/devops/installers.spec.ts +152 -5
  11. package/devops/verify-release-assets.ts +14 -0
  12. package/docs/architecture/architecture.xdocs.md +2 -1
  13. package/docs/architecture/cli-architecture.md +18 -9
  14. package/docs/decisions/decisions.xdocs.md +4 -0
  15. package/docs/decisions/markdown-release-assets-and-version-scoped-notes.md +84 -0
  16. package/docs/plans/automatic-agent-maintenance.md +120 -0
  17. package/docs/plans/plans.xdocs.md +2 -0
  18. package/docs/plans/rfc-0034-cli-compliance-migration.md +17 -8
  19. package/docs/plans/upgrade-reliability-implementation.md +1 -1
  20. package/docs/reviews/implementation/automatic-agent-maintenance-review.md +63 -0
  21. package/docs/reviews/implementation/bash-installer-review.md +42 -0
  22. package/docs/reviews/implementation/implementation.xdocs.md +12 -2
  23. package/docs/reviews/implementation/rfc-0034-cli-compliance-migration-review.md +18 -5
  24. package/docs/reviews/implementation/unicode-help-tree-review.md +41 -0
  25. package/docs/reviews/implementation/upgrade-reliability-issue-12-review.md +62 -0
  26. package/docs/reviews/implementation/upgrade-reliability-issue-13-review.md +63 -0
  27. package/docs/reviews/plans/automatic-agent-maintenance-review.md +56 -0
  28. package/docs/reviews/plans/plans.xdocs.md +2 -0
  29. package/docs/superpowers/specs/2026-07-15-upgrade-reliability-design.md +1 -1
  30. package/docs/todo/automatic-agent-maintenance-implementation.md +55 -0
  31. package/docs/todo/automatic-agent-maintenance.md +86 -0
  32. package/docs/todo/bash-installer.md +45 -0
  33. package/docs/todo/rfc-0034-cli-compliance-migration-implementation.md +27 -3
  34. package/docs/todo/rfc-0034-cli-compliance-migration.md +9 -4
  35. package/docs/todo/todo.xdocs.md +12 -2
  36. package/docs/todo/unicode-help-tree.md +44 -0
  37. package/docs/todo/upgrade-reliability.md +51 -0
  38. package/docs/validation/automatic-agent-maintenance.md +63 -0
  39. package/docs/validation/bash-installer.md +55 -0
  40. package/docs/validation/rfc-0034-cli-compliance-migration.md +29 -7
  41. package/docs/validation/unicode-help-tree.md +43 -0
  42. package/docs/validation/upgrade-reliability-issue-12.md +74 -0
  43. package/docs/validation/upgrade-reliability-issue-13.md +72 -0
  44. package/docs/validation/upgrade-reliability.md +38 -99
  45. package/docs/validation/validation.xdocs.md +14 -4
  46. package/library/agent-maintenance.d.ts +19 -0
  47. package/library/agent-maintenance.d.ts.map +1 -0
  48. package/library/agent-maintenance.js +49 -0
  49. package/library/agents.d.ts +8 -2
  50. package/library/agents.d.ts.map +1 -1
  51. package/library/agents.js +58 -11
  52. package/library/cli.d.ts.map +1 -1
  53. package/library/cli.js +50 -15
  54. package/library/recovery.js +1 -1
  55. package/library/release-catalog.d.ts +2 -1
  56. package/library/release-catalog.d.ts.map +1 -1
  57. package/library/release-catalog.js +9 -1
  58. package/library/storage.d.ts +2 -1
  59. package/library/storage.d.ts.map +1 -1
  60. package/library/storage.js +13 -1
  61. package/package.json +1 -1
  62. package/skills/guiho-s-runx/SKILL.md +11 -0
  63. package/skills/guiho-s-runx/guiho-s-runx.xdocs.md +4 -1
package/library/agents.js CHANGED
@@ -2,13 +2,15 @@
2
2
  * @copyright Copyright © 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
3
3
  */
4
4
  import { RunXError } from './errors.js';
5
- import { homeDirectory, joinPath, resolvePath } from './path-utils.js';
6
- import { ensureDirectory, pathExists, readTextIfExists, removePath, writeTextFile } from './storage.js';
7
- export { applyAgentInstructions, installAgentSkill, listAgentPrompts, listAgentSkills, removeAgentInstructions, showAgentInstructions, showAgentPrompt, showAgentSkill, uninstallAgentSkill, updateAgentInstructions, updateAgentSkill, };
5
+ import { directoryName, homeDirectory, joinPath, resolvePath } from './path-utils.js';
6
+ import { ensureDirectory, pathExists, readTextIfExists, removePath, writeTextFile, writeTextFileAtomic } from './storage.js';
7
+ export { applyAgentInstructions, installAgentSkill, listAgentPrompts, listAgentSkills, maintainAgentIntegration, removeAgentInstructions, showAgentInstructions, showAgentPrompt, showAgentSkill, uninstallAgentSkill, updateAgentInstructions, updateAgentSkill, };
8
8
  const skillId = 'guiho-s-runx';
9
9
  const promptId = 'guiho-i-runx';
10
10
  const managedStart = '<!-- BEGIN RUNX — DO NOT EDIT THIS SECTION -->';
11
11
  const managedEnd = '<!-- END RUNX -->';
12
+ const legacyManagedStart = '<!-- BEGIN RUNX AGENT INSTRUCTIONS -->';
13
+ const legacyManagedEnd = '<!-- END RUNX AGENT INSTRUCTIONS -->';
12
14
  async function installAgentSkill(scope, cwd) {
13
15
  return writeSkillTargets(scope, cwd);
14
16
  }
@@ -69,6 +71,26 @@ async function showAgentPrompt(id) {
69
71
  throw new RunXError(`Unknown RunX prompt: ${id}`, 2);
70
72
  return readBundledPrompt();
71
73
  }
74
+ async function maintainAgentIntegration(cwd) {
75
+ const skill = await readBundledSkill();
76
+ const skills = [];
77
+ for (const directory of skillDirectories('global', cwd)) {
78
+ const path = joinPath(directory, 'SKILL.md');
79
+ if (await readTextIfExists(path) === skill)
80
+ continue;
81
+ await writeTextFileAtomic(path, skill);
82
+ skills.push(path);
83
+ }
84
+ const instructionPath = await nearestAgentsPath(cwd);
85
+ const existing = await readTextIfExists(instructionPath) ?? '';
86
+ const next = replaceManagedBlock(existing, instructionBlock());
87
+ const instructions = [];
88
+ if (next !== existing) {
89
+ await writeTextFileAtomic(instructionPath, next);
90
+ instructions.push(instructionPath);
91
+ }
92
+ return { skills, instructions };
93
+ }
72
94
  async function writeSkillTargets(scope, cwd) {
73
95
  const skill = await readBundledSkill();
74
96
  const installed = [];
@@ -115,23 +137,48 @@ function instructionBlock() {
115
137
  return `${managedStart}
116
138
  ## RunX Command Catalog
117
139
 
118
- Load the \`guiho-s-runx\` skill before working with \`runx.yaml\`. Inspect with
119
- \`runx check --format json\` and \`runx list --format json\`, select stable UIDs,
120
- use \`runx run <uid> --dry-run\` before unfamiliar work, and add \`--yes\` only
121
- after explicit approval for a confirmation-gated command.
140
+ Load the \`guiho-s-runx\` skill whenever discovering commands, creating or
141
+ updating catalog entries, validating \`runx.yaml\`, inspecting command details,
142
+ or executing RunX commands.
143
+ Start with \`runx check --format json\` and \`runx list --format json\`, select
144
+ stable UIDs, use \`runx describe <uid>\`, and run
145
+ \`runx run <uid> --dry-run\` before unfamiliar or side-effecting work.
122
146
  ${managedEnd}
123
147
  `;
124
148
  }
125
149
  function replaceManagedBlock(existing, block) {
126
- const pattern = new RegExp(`${escapeRegExp(managedStart)}[\\s\\S]*?${escapeRegExp(managedEnd)}\\s*`, 'g');
127
- const stripped = existing.replace(pattern, '').trimEnd();
150
+ const stripped = removeKnownManagedBlocks(existing).trimEnd();
128
151
  return `${stripped}${stripped ? '\n\n' : ''}${block}`;
129
152
  }
130
153
  function removeManagedBlock(existing) {
131
- const pattern = new RegExp(`\\s*${escapeRegExp(managedStart)}[\\s\\S]*?${escapeRegExp(managedEnd)}\\s*`, 'g');
132
- const next = existing.replace(pattern, '\n').trimEnd();
154
+ const next = removeKnownManagedBlocks(existing, true).trimEnd();
133
155
  return next ? `${next}\n` : '';
134
156
  }
157
+ function removeKnownManagedBlocks(existing, includeLeadingWhitespace = false) {
158
+ let output = existing;
159
+ for (const [start, end] of [
160
+ [managedStart, managedEnd],
161
+ [legacyManagedStart, legacyManagedEnd],
162
+ ]) {
163
+ const prefix = includeLeadingWhitespace ? '\\s*' : '';
164
+ const pattern = new RegExp(`${prefix}${escapeRegExp(start)}[\\s\\S]*?${escapeRegExp(end)}\\s*`, 'g');
165
+ output = output.replace(pattern, includeLeadingWhitespace ? '\n' : '');
166
+ }
167
+ return output;
168
+ }
169
+ async function nearestAgentsPath(cwd) {
170
+ const effectiveCwd = resolvePath(cwd);
171
+ let current = effectiveCwd;
172
+ while (true) {
173
+ const candidate = joinPath(current, 'AGENTS.md');
174
+ if (await pathExists(candidate))
175
+ return candidate;
176
+ const parent = directoryName(current);
177
+ if (parent === current)
178
+ return joinPath(effectiveCwd, 'AGENTS.md');
179
+ current = parent;
180
+ }
181
+ }
135
182
  async function readBundledSkill() {
136
183
  if (globalThis.__RUNX_EMBEDDED_RESOURCES__?.skill)
137
184
  return globalThis.__RUNX_EMBEDDED_RESOURCES__.skill;
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../source/cli.ts"],"names":[],"mappings":"AAAA;;GAEG;AA8BH,OAAO,KAAK,EAAkB,UAAU,EAAkB,MAAM,OAAO,CAAA;AAIvE,OAAO,EACL,MAAM,EACN,uBAAuB,EACvB,WAAW,GACZ,CAAA;AAuOD,QAAA,MAAiB,WAAW,iBAAwB,CAAA;AAEpD,iBAAe,MAAM,CAAC,OAAO,GAAE,MAAM,EAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB9E;AAED,iBAAe,uBAAuB,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAexE"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../source/cli.ts"],"names":[],"mappings":"AAAA;;GAEG;AA+BH,OAAO,KAAK,EAAkB,UAAU,EAAkB,MAAM,OAAO,CAAA;AAIvE,OAAO,EACL,MAAM,EACN,uBAAuB,EACvB,WAAW,GACZ,CAAA;AAyPD,QAAA,MAAiB,WAAW,iBAAwB,CAAA;AAEpD,iBAAe,MAAM,CAAC,OAAO,GAAE,MAAM,EAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAgC9E;AAMD,iBAAe,uBAAuB,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAexE"}
package/library/cli.js CHANGED
@@ -4,6 +4,7 @@
4
4
  import { Type } from '@sinclair/typebox';
5
5
  import { Value } from '@sinclair/typebox/value';
6
6
  import { defineCommand, renderUsage, runCommand as runCittyCommand } from 'citty';
7
+ import { agentMaintenanceWorkerCwd, runAgentMaintenanceWorker, spawnAgentMaintenanceWorker } from './agent-maintenance.js';
7
8
  import { applyAgentInstructions, installAgentSkill, listAgentPrompts, listAgentSkills, removeAgentInstructions, showAgentInstructions, showAgentPrompt, showAgentSkill, uninstallAgentSkill, updateAgentInstructions, updateAgentSkill, } from './agents.js';
8
9
  import { RunXError } from './errors.js';
9
10
  import { runCommand } from './executor.js';
@@ -12,7 +13,7 @@ import { initializeRunXManifest } from './init.js';
12
13
  import { readManifest, resolveCommand } from './manifest.js';
13
14
  import { resolvePath } from './path-utils.js';
14
15
  import { renderDescription, renderExecutionPlan, renderJson, renderList } from './render.js';
15
- import { fetchReleaseCatalog, resolveUpgradePlatform } from './release-catalog.js';
16
+ import { fetchReleaseCatalog, paginateReleaseCatalog, resolveUpgradePlatform } from './release-catalog.js';
16
17
  import { checkForLatestVersion, uninstallSelf, upgradeSelf } from './self-management.js';
17
18
  import { readCachedUpdateNotice, runUpdateWorker, spawnUpdateWorker } from './update-cache.js';
18
19
  import { renderReleaseCatalog, renderUpgradeEvent, renderUpgradeHeading, renderUpgradePlan, renderUpgradeResult } from './upgrade-reporting.js';
@@ -102,7 +103,14 @@ function createCommandTree() {
102
103
  const instruction = group('runx agent instruction', 'Manage RunX instruction blocks.', { apply: instructionApply, remove: instructionRemove, update: instructionUpdate, show: instructionShow }, state);
103
104
  const promptList = leaf('runx agent prompt list', 'List bundled RunX prompts.', {
104
105
  names: { type: 'boolean', description: 'Print prompt names only.' }, format: catalogArgs.format,
105
- }, () => writeFormatted(listAgentPrompts(Boolean(state.args.names)), state.args));
106
+ }, () => {
107
+ if (state.args.names) {
108
+ const names = listAgentPrompts(true);
109
+ write(options(state.args).format === 'json' ? renderJson(names) : `${names.join('\n')}\n`);
110
+ return;
111
+ }
112
+ writeFormatted(listAgentPrompts(false), state.args);
113
+ });
106
114
  const promptShow = leaf('runx agent prompt show', 'Print one raw bundled prompt.', {
107
115
  id: { type: 'positional', description: 'Bundled prompt ID.' },
108
116
  }, async ({ args }) => {
@@ -112,14 +120,13 @@ function createCommandTree() {
112
120
  });
113
121
  const prompt = group('runx agent prompt', 'Inspect bundled agent prompts.', { list: promptList, show: promptShow }, state);
114
122
  const agent = group('runx agent', 'Manage RunX agent integration.', { skill, instruction, prompt }, state);
115
- const upgradeApply = leaf('runx upgrade', 'Upgrade the canonical native RunX executable.', {
123
+ const upgradeArgs = {
116
124
  version: { type: 'string', valueHint: 'version', description: 'Select an exact release version.' },
117
125
  arch: { type: 'string', valueHint: 'x64|arm64', description: 'Select target architecture.' },
118
126
  variant: { type: 'string', valueHint: 'baseline|default|modern', description: 'Select x64 binary variant.' },
119
127
  'dry-run': { type: 'boolean', description: 'Plan without mutation.' },
120
128
  format: catalogArgs.format,
121
- }, async () => runUpgrade(options(state.args), state.args));
122
- upgradeApply.meta = { ...upgradeApply.meta, hidden: true };
129
+ };
123
130
  const upgradeCheck = leaf('runx upgrade check', 'Check whether a newer stable release exists.', {
124
131
  format: catalogArgs.format,
125
132
  }, async () => {
@@ -129,12 +136,23 @@ function createCommandTree() {
129
136
  const upgradeList = leaf('runx upgrade list', 'List RunX releases newest first.', {
130
137
  page: { type: 'string', valueHint: 'positive-integer', description: 'Select result page.' },
131
138
  'per-page': { type: 'string', valueHint: 'positive-integer', description: 'Select page size.' },
132
- 'pre-releases': { type: 'boolean', description: 'Include prerelease versions.' },
139
+ 'pre-releases': { type: 'boolean', description: 'Accepted explicitly; prereleases are always included.' },
133
140
  arch: { type: 'string', valueHint: 'x64|arm64', description: 'Select target architecture.' },
134
141
  variant: { type: 'string', valueHint: 'baseline|default|modern', description: 'Select x64 variant.' },
135
142
  format: catalogArgs.format,
136
143
  }, async () => runUpgradeList(state.args));
137
- const upgrade = group('runx upgrade', 'Inspect or upgrade a native RunX executable.', { _apply: upgradeApply, check: upgradeCheck, list: upgradeList }, state, '_apply');
144
+ const upgrade = defineCommand({
145
+ meta: { name: 'runx upgrade', description: 'Inspect or upgrade a native RunX executable.' },
146
+ args: { ...upgradeArgs, ...helpArgs },
147
+ subCommands: { check: upgradeCheck, list: upgradeList },
148
+ setup: helpSetup(state),
149
+ run: async () => {
150
+ if (state.command === upgradeCheck || state.command === upgradeList)
151
+ return;
152
+ await runUpgrade(options(state.args), state.args);
153
+ },
154
+ });
155
+ state.commands.set('upgrade', upgrade);
138
156
  const uninstall = leaf('runx uninstall', 'Uninstall the native RunX executable.', {
139
157
  'dry-run': { type: 'boolean', description: 'Print the target without deleting it.' }, format: catalogArgs.format,
140
158
  }, async () => {
@@ -149,8 +167,9 @@ function createCommandTree() {
149
167
  state.args = context.args;
150
168
  state.command = resolveHelpCommand(state);
151
169
  await validateInvocation(state);
152
- if (state.args.v || state.args.version === true)
170
+ if (context.rawArgs.length === 1 && ['-v', '--version'].includes(context.rawArgs[0] ?? '')) {
153
171
  return handled(`${readVersion()}\n`);
172
+ }
154
173
  await handleHelp(state);
155
174
  },
156
175
  run: ({ args }) => {
@@ -215,12 +234,21 @@ async function runCli(rawArgs = process.argv.slice(2)) {
215
234
  await runUpdateWorker();
216
235
  return;
217
236
  }
237
+ const maintenanceWorkerCwd = agentMaintenanceWorkerCwd(rawArgs);
238
+ if (maintenanceWorkerCwd !== null) {
239
+ await runAgentMaintenanceWorker(maintenanceWorkerCwd);
240
+ return;
241
+ }
218
242
  const { command, state } = createCommandTree();
219
243
  const cleanOutput = rawArgs.some((arg) => ['-h', '--help', '-v', '--version', '--help-tree', '--help-docs'].includes(arg) || arg.startsWith('--help-tree-depth'));
220
- if (!cleanOutput && rawArgs.length > 0) {
244
+ if (!cleanOutput) {
221
245
  const notice = await readCachedUpdateNotice(rawArgs.includes('--verbose'));
222
- if (notice)
223
- process.stderr.write(`${notice}\n`);
246
+ if (notice) {
247
+ if (rawArgs.length === 0)
248
+ write(`${notice}\n`);
249
+ else
250
+ process.stderr.write(`${notice}\n`);
251
+ }
224
252
  }
225
253
  spawnUpdateWorker();
226
254
  try {
@@ -235,6 +263,14 @@ async function runCli(rawArgs = process.argv.slice(2)) {
235
263
  throw new CliUsageError(error.message, await renderUsage(state.command));
236
264
  throw error;
237
265
  }
266
+ finally {
267
+ if (shouldScheduleAgentMaintenance(rawArgs)) {
268
+ spawnAgentMaintenanceWorker(resolvePath(state.args.cwd ?? process.cwd()));
269
+ }
270
+ }
271
+ }
272
+ function shouldScheduleAgentMaintenance(rawArgs) {
273
+ return !['agent', 'uninstall'].includes(rawArgs[0] ?? '');
238
274
  }
239
275
  async function runCliWithErrorHandling(rawArgs) {
240
276
  try {
@@ -317,11 +353,10 @@ async function runUpgradeList(args) {
317
353
  const requestedArch = args.arch ? decode(archSchema, args.arch, '--arch') : process.arch;
318
354
  const platform = resolveUpgradePlatform(process.platform, requestedArch);
319
355
  const variant = decode(variantSchema, args.variant ?? 'baseline', '--variant');
320
- const page = positiveInteger(args.page ?? '1', '--page');
321
- const perPage = positiveInteger(args.perPage ?? '20', '--per-page');
356
+ const page = args.page ? positiveInteger(args.page, '--page') : undefined;
357
+ const perPage = args.perPage ? positiveInteger(args.perPage, '--per-page') : undefined;
322
358
  const catalog = await fetchReleaseCatalog({ ...platform, variant, currentVersion: readVersion() });
323
- const filtered = args.preReleases ? catalog.releases : catalog.releases.filter((release) => !release.prerelease);
324
- const paged = { ...catalog, releases: filtered.slice((page - 1) * perPage, page * perPage) };
359
+ const paged = paginateReleaseCatalog(catalog, page, perPage);
325
360
  write(options(args).format === 'json' ? renderJson(paged) : renderReleaseCatalog(paged));
326
361
  }
327
362
  async function runUpgrade(optionsValue, args) {
@@ -15,7 +15,7 @@ const createRecoveryInstructions = (targetVersion, os, targetSource = 'resolved'
15
15
  return {
16
16
  targetVersion,
17
17
  targetSource,
18
- installCommand: `curl -fsSL ${installerBaseUrl}/install.sh | sh -s -- --version '${escapeShell(targetVersion)}'`,
18
+ installCommand: `curl -fsSL ${installerBaseUrl}/install.sh | bash -s -- --version '${escapeShell(targetVersion)}'`,
19
19
  stopProcessCommand: 'pkill -x runx',
20
20
  };
21
21
  };
@@ -2,7 +2,7 @@
2
2
  * @copyright Copyright © 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
3
3
  */
4
4
  import type { ReleaseAsset, ReleaseCatalog, UpgradeArch, UpgradeOs, UpgradeVariant } from './upgrade-types.js';
5
- export { assetCandidates, fetchReleaseCatalog, findCompatibleAsset, normalizeReleaseVersion, resolveUpgradePlatform, };
5
+ export { assetCandidates, fetchReleaseCatalog, findCompatibleAsset, normalizeReleaseVersion, paginateReleaseCatalog, resolveUpgradePlatform, };
6
6
  declare const githubReleaseSchema: import("@sinclair/typebox").TObject<{
7
7
  tag_name: import("@sinclair/typebox").TString;
8
8
  draft: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
@@ -29,4 +29,5 @@ declare const findCompatibleAsset: (release: GitHubRelease, platform: ReleasePla
29
29
  declare const assetCandidates: ({ os, arch, variant }: ReleasePlatform) => string[];
30
30
  declare const resolveUpgradePlatform: (platform?: NodeJS.Platform, architecture?: NodeJS.Architecture) => ReleasePlatform;
31
31
  declare const normalizeReleaseVersion: (tag: string) => string;
32
+ declare const paginateReleaseCatalog: (catalog: ReleaseCatalog, page?: number, perPage?: number) => ReleaseCatalog;
32
33
  //# sourceMappingURL=release-catalog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"release-catalog.d.ts","sourceRoot":"","sources":["../source/release-catalog.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAuB,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEnI,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,GACvB,CAAA;AAED,QAAA,MAAM,mBAAmB;;;;;;;;;;EAUS,CAAA;AAIlC,MAAM,MAAM,aAAa,GAAG,OAAO,mBAAmB,EAAE,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAE1F,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,SAAS,CAAA;IACb,IAAI,EAAE,WAAW,CAAA;IACjB,OAAO,EAAE,cAAc,CAAA;CACxB,CAAA;AAED,KAAK,mBAAmB,GAAG,eAAe,GAAG;IAC3C,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CACzB,CAAA;AAID,QAAA,MAAM,mBAAmB,YAAmB,mBAAmB,KAAG,OAAO,CAAC,cAAc,CAoCvF,CAAA;AAkBD,QAAA,MAAM,mBAAmB,YAAa,aAAa,YAAY,eAAe,KAAG,YAAY,GAAG,IAM/F,CAAA;AAED,QAAA,MAAM,eAAe,0BAA2B,eAAe,KAAG,MAAM,EAUvE,CAAA;AAED,QAAA,MAAM,sBAAsB,sEAA+D,eAM1F,CAAA;AAED,QAAA,MAAM,uBAAuB,QAAS,MAAM,KAAG,MAA6D,CAAA"}
1
+ {"version":3,"file":"release-catalog.d.ts","sourceRoot":"","sources":["../source/release-catalog.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAuB,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEnI,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,EACtB,sBAAsB,GACvB,CAAA;AAED,QAAA,MAAM,mBAAmB;;;;;;;;;;EAUS,CAAA;AAIlC,MAAM,MAAM,aAAa,GAAG,OAAO,mBAAmB,EAAE,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAE1F,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,SAAS,CAAA;IACb,IAAI,EAAE,WAAW,CAAA;IACjB,OAAO,EAAE,cAAc,CAAA;CACxB,CAAA;AAED,KAAK,mBAAmB,GAAG,eAAe,GAAG;IAC3C,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CACzB,CAAA;AAID,QAAA,MAAM,mBAAmB,YAAmB,mBAAmB,KAAG,OAAO,CAAC,cAAc,CAoCvF,CAAA;AAkBD,QAAA,MAAM,mBAAmB,YAAa,aAAa,YAAY,eAAe,KAAG,YAAY,GAAG,IAM/F,CAAA;AAED,QAAA,MAAM,eAAe,0BAA2B,eAAe,KAAG,MAAM,EAUvE,CAAA;AAED,QAAA,MAAM,sBAAsB,sEAA+D,eAM1F,CAAA;AAED,QAAA,MAAM,uBAAuB,QAAS,MAAM,KAAG,MAA6D,CAAA;AAE5G,QAAA,MAAM,sBAAsB,YAAa,cAAc,SAAS,MAAM,YAAY,MAAM,KAAG,cAM1F,CAAA"}
@@ -5,7 +5,7 @@ import { Type } from '@sinclair/typebox';
5
5
  import { Value } from '@sinclair/typebox/value';
6
6
  import { compare, parse, valid } from 'semver';
7
7
  import { RunXError } from './errors.js';
8
- export { assetCandidates, fetchReleaseCatalog, findCompatibleAsset, normalizeReleaseVersion, resolveUpgradePlatform, };
8
+ export { assetCandidates, fetchReleaseCatalog, findCompatibleAsset, normalizeReleaseVersion, paginateReleaseCatalog, resolveUpgradePlatform, };
9
9
  const githubReleaseSchema = Type.Object({
10
10
  tag_name: Type.String(),
11
11
  draft: Type.Optional(Type.Boolean()),
@@ -100,6 +100,14 @@ const resolveUpgradePlatform = (platform = process.platform, architecture = proc
100
100
  return { os, arch, variant: 'baseline' };
101
101
  };
102
102
  const normalizeReleaseVersion = (tag) => tag.replace(/^@guiho\/runx@/, '').replace(/^v/, '');
103
+ const paginateReleaseCatalog = (catalog, page, perPage) => {
104
+ if (page === undefined && perPage === undefined)
105
+ return catalog;
106
+ const selectedPage = page ?? 1;
107
+ const selectedPageSize = perPage ?? 20;
108
+ const start = (selectedPage - 1) * selectedPageSize;
109
+ return { ...catalog, releases: catalog.releases.slice(start, start + selectedPageSize) };
110
+ };
103
111
  const releaseChannel = (version, prerelease) => {
104
112
  const parsed = parse(version);
105
113
  if (!parsed)
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @copyright Copyright © 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
3
3
  */
4
- export { copyTextFile, ensureDirectory, globalRunXDirectory, movePath, pathExists, readTextIfExists, removePath, writeTextFile, };
4
+ export { copyTextFile, ensureDirectory, globalRunXDirectory, movePath, pathExists, readTextIfExists, removePath, writeTextFile, writeTextFileAtomic, };
5
5
  declare function globalRunXDirectory(): string;
6
6
  declare function pathExists(path: string): Promise<boolean>;
7
7
  declare function ensureDirectory(path: string): Promise<void>;
@@ -9,5 +9,6 @@ declare function removePath(path: string): Promise<void>;
9
9
  declare function movePath(from: string, to: string): Promise<void>;
10
10
  declare function readTextIfExists(path: string): Promise<string | null>;
11
11
  declare function writeTextFile(path: string, value: string): Promise<void>;
12
+ declare function writeTextFileAtomic(path: string, value: string): Promise<void>;
12
13
  declare function copyTextFile(source: string, target: string): Promise<void>;
13
14
  //# sourceMappingURL=storage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../source/storage.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,EACL,YAAY,EACZ,eAAe,EACf,mBAAmB,EACnB,QAAQ,EACR,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,aAAa,GACd,CAAA;AAED,iBAAS,mBAAmB,IAAI,MAAM,CAErC;AAED,iBAAe,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAExD;AAED,iBAAe,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1D;AAED,iBAAe,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAErD;AAED,iBAAe,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ/D;AAED,iBAAe,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEpE;AAED,iBAAe,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGvE;AAED,iBAAe,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzE"}
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../source/storage.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,EACL,YAAY,EACZ,eAAe,EACf,mBAAmB,EACnB,QAAQ,EACR,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,mBAAmB,GACpB,CAAA;AAED,iBAAS,mBAAmB,IAAI,MAAM,CAErC;AAED,iBAAe,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAExD;AAED,iBAAe,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1D;AAED,iBAAe,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAErD;AAED,iBAAe,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ/D;AAED,iBAAe,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEpE;AAED,iBAAe,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGvE;AAED,iBAAe,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAU7E;AAED,iBAAe,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzE"}
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { $ } from 'bun';
5
5
  import { directoryName, homeDirectory, joinPath } from './path-utils.js';
6
- export { copyTextFile, ensureDirectory, globalRunXDirectory, movePath, pathExists, readTextIfExists, removePath, writeTextFile, };
6
+ export { copyTextFile, ensureDirectory, globalRunXDirectory, movePath, pathExists, readTextIfExists, removePath, writeTextFile, writeTextFileAtomic, };
7
7
  function globalRunXDirectory() {
8
8
  return joinPath(homeDirectory(), '.guiho', 'runx');
9
9
  }
@@ -33,6 +33,18 @@ async function writeTextFile(path, value) {
33
33
  await ensureDirectory(directoryName(path));
34
34
  await Bun.write(path, value);
35
35
  }
36
+ async function writeTextFileAtomic(path, value) {
37
+ await ensureDirectory(directoryName(path));
38
+ const temporary = `${path}.${process.pid}.${crypto.randomUUID()}.tmp`;
39
+ try {
40
+ await Bun.write(temporary, value);
41
+ await movePath(temporary, path);
42
+ }
43
+ catch (error) {
44
+ await removePath(temporary);
45
+ throw error;
46
+ }
47
+ }
36
48
  async function copyTextFile(source, target) {
37
49
  await writeTextFile(target, await Bun.file(source).text());
38
50
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@guiho/runx",
3
3
  "description": "A language-agnostic, documented command catalog and local CLI executor.",
4
- "version": "0.4.0",
4
+ "version": "0.5.1",
5
5
  "type": "module",
6
6
  "main": "./library/guiho-runx.js",
7
7
  "types": "./library/guiho-runx.d.ts",
@@ -76,3 +76,14 @@ runx upgrade --dry-run
76
76
 
77
77
  Use `--help`, `--help-tree`, or `--help-docs` at any command scope for the
78
78
  current executable contract.
79
+
80
+ ## Automatic Agent Maintenance
81
+
82
+ Ordinary RunX commands schedule a silent, non-blocking worker that keeps the
83
+ bundled skill current in both global agent-tool directories and reconciles one
84
+ compact managed block in the nearest `AGENTS.md`. A current installation is not
85
+ rewritten. Automatic failures never fail or pollute the foreground command.
86
+
87
+ Explicit `runx agent ...` commands remain the manual repair and local-scope
88
+ interface. Explicit agent-resource removal and `runx uninstall` do not schedule
89
+ automatic reinstallation.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  subject: guiho-s-runx
3
- description: Agent workflow for safe RunX catalog execution and verified native upgrade/list/recovery operations.
3
+ description: Agent workflow for safe RunX catalog work, automatic resource maintenance, and verified native upgrade/list/recovery operations.
4
4
  parent: runx-skills
5
5
  children: []
6
6
  files:
@@ -17,6 +17,7 @@ keywords:
17
17
  - dry run
18
18
  - runx upgrade
19
19
  - recovery install
20
+ - automatic maintenance
20
21
  flags: []
21
22
  status: stable
22
23
  ---
@@ -24,3 +25,5 @@ status: stable
24
25
  The skill makes manifest inspection, UID selection, dry runs, and explicit
25
26
  confirmation the default agent workflow for RunX commands and teaches agents
26
27
  how to interpret transactional native upgrades and their recovery contract.
28
+ Ordinary RunX invocations also reconcile the current global skill and nearest
29
+ managed project instruction block through a silent detached worker.