@guiho/runx 0.2.2 → 0.2.4

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/library/cli.js CHANGED
@@ -1,60 +1,243 @@
1
+ /**
2
+ * @copyright Copyright © 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
3
+ */
1
4
  import { resolve } from 'node:path';
5
+ import { defineCommand, renderUsage, runCommand as runCittyCommand } from 'citty';
2
6
  import { installAgentInstructions, installAgentSkill } from './agents.js';
3
7
  import { RunXError } from './errors.js';
4
8
  import { runCommand } from './executor.js';
5
- import { booleanFlag, parseArgs, stringFlag } from './flags.js';
6
- import { readVersion, showCommandHelp, showHelpDocs, showHelpTree, showHome } from './help.js';
9
+ import { readVersion, showHelpDocs, showHelpTree, showHome } from './help.js';
7
10
  import { readManifest, resolveCommand } from './manifest.js';
8
11
  import { renderDescription, renderExecutionPlan, renderJson, renderList } from './render.js';
9
12
  import { checkForLatestVersion, listAvailableVersions, uninstallSelf, upgradeSelf } from './self-management.js';
10
- const builtins = new Set(['list', 'describe', 'run', 'r', 'check', 'agents', 'upgrade', 'uninstall']);
11
- export const runCli = async (rawArgs = process.argv.slice(2)) => {
12
- const parsed = parseArgs(rawArgs);
13
- const options = resolveOptions(parsed.flags);
14
- if (booleanFlag(parsed.flags, 'version'))
15
- return write(`${readVersion()}\n`);
16
- if (booleanFlag(parsed.flags, 'helpTree'))
17
- return write(`${showHelpTree()}\n`);
18
- if (booleanFlag(parsed.flags, 'helpDocs'))
19
- return write(showHelpDocs());
20
- if (booleanFlag(parsed.flags, 'help'))
21
- return write(parsed.command && builtins.has(parsed.command) ? showCommandHelp(parsed.command) : showHome());
22
- if (!parsed.command)
23
- return write(showHome());
24
- if (!builtins.has(parsed.command)) {
25
- await runSelectedCommand(parsed.command, parsed.positionals, options, parsed.flags);
26
- return;
13
+ export { runCli, runCliWithErrorHandling, runxCommand, };
14
+ class CliHandled extends Error {
15
+ }
16
+ class CliUsageError extends Error {
17
+ usage;
18
+ constructor(message, usage) {
19
+ super(message);
20
+ this.name = 'CliUsageError';
21
+ this.usage = usage;
27
22
  }
28
- switch (parsed.command) {
29
- case 'list':
30
- await listCommands(options);
31
- return;
32
- case 'describe':
33
- await describeCommand(parsed.positionals[0], options);
34
- return;
35
- case 'run':
36
- case 'r':
37
- await runSelectedCommand(parsed.positionals[0], parsed.positionals.slice(1), options, parsed.flags);
38
- return;
39
- case 'check':
40
- await checkManifest(options);
41
- return;
42
- case 'agents':
43
- await runAgents(parsed.positionals, options, parsed.flags);
44
- return;
45
- case 'upgrade':
46
- await runUpgrade(parsed.positionals, options, parsed.flags);
47
- return;
48
- case 'uninstall':
49
- await runUninstall(options, parsed.flags);
23
+ }
24
+ const commonArgs = {
25
+ cwd: { type: 'string', description: 'Resolve the manifest from this directory.', valueHint: 'path' },
26
+ file: { type: 'string', description: 'Use this runx.yaml file.', valueHint: 'path' },
27
+ format: { type: 'string', description: 'Select text or JSON output.', valueHint: 'text|json', default: 'text' },
28
+ verbose: { type: 'boolean', description: 'Enable additional diagnostics.' },
29
+ 'help-tree': { type: 'boolean', description: 'Show the complete command hierarchy.' },
30
+ 'help-docs': { type: 'boolean', description: 'Show manifest and agent guidance.' },
31
+ help: { type: 'boolean', alias: 'h', description: 'Show command help.' },
32
+ version: { type: 'boolean', alias: 'v', description: 'Show the RunX version.' },
33
+ };
34
+ const runArgs = {
35
+ ...commonArgs,
36
+ 'dry-run': { type: 'boolean', description: 'Show the action without executing it.' },
37
+ yes: { type: 'boolean', description: 'Approve a confirmation-gated command.' },
38
+ };
39
+ const agentInstallArgs = {
40
+ ...commonArgs,
41
+ tool: { type: 'string', description: 'Select agents, claude, or all.', valueHint: 'agents|claude|all' },
42
+ };
43
+ const selfManagementArgs = {
44
+ ...commonArgs,
45
+ 'dry-run': { type: 'boolean', description: 'Show the action without changing the executable.' },
46
+ };
47
+ const rootArgs = {
48
+ ...runArgs,
49
+ tool: agentInstallArgs.tool,
50
+ };
51
+ const knownArgumentKeys = new Set([
52
+ '_',
53
+ 'cwd',
54
+ 'file',
55
+ 'format',
56
+ 'verbose',
57
+ 'dryRun',
58
+ 'dry-run',
59
+ 'yes',
60
+ 'tool',
61
+ 'helpTree',
62
+ 'help-tree',
63
+ 'helpDocs',
64
+ 'help-docs',
65
+ 'help',
66
+ 'h',
67
+ 'version',
68
+ 'v',
69
+ ]);
70
+ const createCommandTree = () => {
71
+ const state = { globalArgs: { _: [] }, usageCommand: {} };
72
+ const listCommand = defineCommand({
73
+ meta: { name: 'runx list', description: 'List commands in a RunX manifest.' },
74
+ args: commonArgs,
75
+ setup: withCommandHelp(state),
76
+ run: async () => listCommands(resolveOptions(state.globalArgs)),
77
+ });
78
+ const describeCommandDefinition = defineCommand({
79
+ meta: { name: 'runx describe', description: 'Describe one manifest command.' },
80
+ args: {
81
+ selector: { type: 'positional', description: 'Command UID, group/id, index, or unambiguous ID.' },
82
+ ...commonArgs,
83
+ },
84
+ setup: withCommandHelp(state),
85
+ run: async ({ args }) => {
86
+ if (!args.selector)
87
+ throw await missingSelector(state);
88
+ await describeCommand(args.selector, resolveOptions(state.globalArgs));
89
+ },
90
+ });
91
+ const runCommandDefinition = createRunCommand(state);
92
+ const checkCommand = defineCommand({
93
+ meta: { name: 'runx check', description: 'Validate a RunX manifest without executing it.' },
94
+ args: commonArgs,
95
+ setup: withCommandHelp(state),
96
+ run: async () => checkManifest(resolveOptions(state.globalArgs)),
97
+ });
98
+ const agentsInstallCommand = defineCommand({
99
+ meta: { name: 'runx agents install', description: 'Install the bundled RunX skill.' },
100
+ args: {
101
+ scope: { type: 'positional', description: 'Install locally or globally.', default: 'local', valueHint: 'local|global' },
102
+ ...agentInstallArgs,
103
+ },
104
+ setup: withCommandHelp(state),
105
+ run: async ({ args }) => runAgentsInstall(args.scope, resolveOptions(state.globalArgs), state.globalArgs),
106
+ });
107
+ const agentsInstructionsCommand = defineCommand({
108
+ meta: { name: 'runx agents instructions', description: 'Install or refresh managed RunX agent instructions.' },
109
+ args: commonArgs,
110
+ setup: withCommandHelp(state),
111
+ run: async () => runAgentsInstructions(resolveOptions(state.globalArgs)),
112
+ });
113
+ const agentsCommand = defineCommand({
114
+ meta: { name: 'runx agents', description: 'Manage the bundled RunX agent integration.' },
115
+ args: agentInstallArgs,
116
+ subCommands: {
117
+ install: agentsInstallCommand,
118
+ instructions: agentsInstructionsCommand,
119
+ },
120
+ setup: withCommandHelp(state),
121
+ });
122
+ const upgradeApplyCommand = defineCommand({
123
+ meta: { name: 'runx upgrade', description: 'Upgrade a native RunX executable.', hidden: true },
124
+ args: selfManagementArgs,
125
+ setup: withCommandHelp(state),
126
+ run: async () => runUpgrade(resolveOptions(state.globalArgs), state.globalArgs),
127
+ });
128
+ const upgradeCheckCommand = defineCommand({
129
+ meta: { name: 'runx upgrade check', description: 'Check whether a newer RunX release is available.' },
130
+ args: commonArgs,
131
+ setup: withCommandHelp(state),
132
+ run: async () => runUpgradeCheck(resolveOptions(state.globalArgs)),
133
+ });
134
+ const upgradeListCommand = defineCommand({
135
+ meta: { name: 'runx upgrade list', description: 'List recent RunX release versions.' },
136
+ args: commonArgs,
137
+ setup: withCommandHelp(state),
138
+ run: async () => runUpgradeList(resolveOptions(state.globalArgs)),
139
+ });
140
+ const upgradeCommand = defineCommand({
141
+ meta: { name: 'runx upgrade', description: 'Inspect or upgrade a native RunX executable.' },
142
+ args: selfManagementArgs,
143
+ default: '_apply',
144
+ subCommands: {
145
+ _apply: upgradeApplyCommand,
146
+ check: upgradeCheckCommand,
147
+ list: upgradeListCommand,
148
+ },
149
+ setup: withCommandHelp(state),
150
+ });
151
+ const uninstallCommand = defineCommand({
152
+ meta: { name: 'runx uninstall', description: 'Uninstall a native RunX executable.' },
153
+ args: selfManagementArgs,
154
+ setup: withCommandHelp(state),
155
+ run: async () => runUninstall(resolveOptions(state.globalArgs), state.globalArgs),
156
+ });
157
+ const homeCommand = defineCommand({
158
+ meta: { name: 'runx', description: 'Show the RunX home page.', hidden: true },
159
+ args: rootArgs,
160
+ setup: withCommandHelp(state),
161
+ run: () => write(showHome()),
162
+ });
163
+ const staticSubCommands = Object.assign(Object.create(null), {
164
+ _home: homeCommand,
165
+ list: listCommand,
166
+ describe: describeCommandDefinition,
167
+ run: runCommandDefinition,
168
+ check: checkCommand,
169
+ agents: agentsCommand,
170
+ upgrade: upgradeCommand,
171
+ uninstall: uninstallCommand,
172
+ });
173
+ const usageCommands = new Map([
174
+ ['list', listCommand],
175
+ ['describe', describeCommandDefinition],
176
+ ['run', runCommandDefinition],
177
+ ['r', runCommandDefinition],
178
+ ['check', checkCommand],
179
+ ['agents', agentsCommand],
180
+ ['agents install', agentsInstallCommand],
181
+ ['agents instructions', agentsInstructionsCommand],
182
+ ['upgrade', upgradeCommand],
183
+ ['upgrade check', upgradeCheckCommand],
184
+ ['upgrade list', upgradeListCommand],
185
+ ['uninstall', uninstallCommand],
186
+ ]);
187
+ const subCommands = createSelectorCompatibleCommands(staticSubCommands, state);
188
+ const command = defineCommand({
189
+ meta: {
190
+ name: 'runx',
191
+ version: readVersion(),
192
+ description: 'A documented, local command catalog for runx.yaml manifests.',
193
+ },
194
+ args: rootArgs,
195
+ default: '_home',
196
+ subCommands,
197
+ setup: async (context) => {
198
+ state.globalArgs = context.args;
199
+ state.usageCommand = resolveUsageCommand(context.args._, usageCommands, state);
200
+ await validateArguments(context, state);
201
+ if (state.globalArgs.version || state.globalArgs.v)
202
+ return handleOutput(`${readVersion()}\n`);
203
+ if (state.globalArgs.helpTree)
204
+ return handleOutput(`${showHelpTree()}\n`);
205
+ if (state.globalArgs.helpDocs)
206
+ return handleOutput(showHelpDocs());
207
+ if (state.globalArgs.help || state.globalArgs.h)
208
+ return handleUsage(state.usageCommand);
209
+ },
210
+ });
211
+ state.usageCommand = command;
212
+ return { command, state };
213
+ };
214
+ const { command: runxCommand } = createCommandTree();
215
+ async function runCli(rawArgs = process.argv.slice(2)) {
216
+ const { command, state } = createCommandTree();
217
+ try {
218
+ await runCittyCommand(command, { rawArgs });
219
+ }
220
+ catch (error) {
221
+ if (error instanceof CliHandled)
50
222
  return;
223
+ if (error instanceof CliUsageError)
224
+ throw error;
225
+ if (isCittyUsageError(error)) {
226
+ throw new CliUsageError(error.message, await renderUsage(state.usageCommand));
227
+ }
228
+ throw error;
51
229
  }
52
- };
53
- export const runCliWithErrorHandling = async (rawArgs) => {
230
+ }
231
+ async function runCliWithErrorHandling(rawArgs) {
54
232
  try {
55
233
  await runCli(rawArgs);
56
234
  }
57
235
  catch (error) {
236
+ if (error instanceof CliUsageError) {
237
+ process.stderr.write(`${error.usage}\n\nerror: ${error.message}\n`);
238
+ process.exitCode = 1;
239
+ return;
240
+ }
58
241
  if (error instanceof RunXError) {
59
242
  process.stderr.write(`error: ${error.message}\n`);
60
243
  process.exitCode = error.exitCode;
@@ -63,84 +246,155 @@ export const runCliWithErrorHandling = async (rawArgs) => {
63
246
  process.stderr.write(`error: ${error instanceof Error ? error.message : 'Unexpected failure.'}\n`);
64
247
  process.exitCode = 1;
65
248
  }
66
- };
67
- const listCommands = async (options) => {
249
+ }
250
+ function createRunCommand(state, selectorDefault) {
251
+ return defineCommand({
252
+ meta: {
253
+ name: selectorDefault ? 'runx <selector>' : 'runx run',
254
+ alias: selectorDefault ? undefined : 'r',
255
+ description: selectorDefault ? 'Run a selector using the root shorthand.' : 'Run one manifest command.',
256
+ },
257
+ args: selectorDefault
258
+ ? runArgs
259
+ : {
260
+ selector: { type: 'positional', description: 'Command UID, group/id, index, or unambiguous ID.' },
261
+ ...runArgs,
262
+ },
263
+ setup: withCommandHelp(state),
264
+ run: async ({ args }) => {
265
+ const selector = selectorDefault ?? (typeof args.selector === 'string' ? args.selector : undefined);
266
+ if (!selector)
267
+ throw await missingSelector(state);
268
+ await runSelectedCommand(selector, resolveOptions(state.globalArgs), state.globalArgs);
269
+ },
270
+ });
271
+ }
272
+ function createSelectorCompatibleCommands(commands, state) {
273
+ const aliases = new Set(['r']);
274
+ return new Proxy(commands, {
275
+ has: (target, property) => {
276
+ if (typeof property !== 'string')
277
+ return Reflect.has(target, property);
278
+ if (aliases.has(property))
279
+ return false;
280
+ return true;
281
+ },
282
+ get: (target, property, receiver) => {
283
+ if (typeof property !== 'string' || Reflect.has(target, property))
284
+ return Reflect.get(target, property, receiver);
285
+ return createRunCommand(state, property);
286
+ },
287
+ });
288
+ }
289
+ function resolveUsageCommand(positionals, commands, state) {
290
+ const name = positionals[0];
291
+ if (!name)
292
+ return state.usageCommand;
293
+ if (!commands.has(name))
294
+ return createRunCommand(state, name);
295
+ for (let length = positionals.length; length > 0; length -= 1) {
296
+ const command = commands.get(positionals.slice(0, length).join(' '));
297
+ if (command)
298
+ return command;
299
+ }
300
+ return commands.get(name);
301
+ }
302
+ function withCommandHelp(state) {
303
+ return (context) => {
304
+ state.usageCommand = context.cmd;
305
+ };
306
+ }
307
+ async function missingSelector(state) {
308
+ return new CliUsageError('Missing required positional argument: SELECTOR', await renderUsage(state.usageCommand));
309
+ }
310
+ async function validateArguments(context, state) {
311
+ const unknown = Object.keys(context.args).find((key) => !knownArgumentKeys.has(key));
312
+ if (!unknown)
313
+ return;
314
+ throw new CliUsageError(`Unknown option --${unknown}`, await renderUsage(state.usageCommand));
315
+ }
316
+ async function handleUsage(command) {
317
+ return handleOutput(`${await renderUsage(command)}\n`);
318
+ }
319
+ function handleOutput(value) {
320
+ write(value);
321
+ throw new CliHandled();
322
+ }
323
+ async function listCommands(options) {
68
324
  const { manifest, path } = await readManifest(options.cwd, options.file);
69
325
  write(options.format === 'json' ? renderJson({ manifestPath: path, manifest }) : renderList(manifest, path));
70
- };
71
- const describeCommand = async (selector, options) => {
72
- if (!selector)
73
- throw new RunXError('Missing selector. Usage: runx describe <selector>');
326
+ }
327
+ async function describeCommand(selector, options) {
74
328
  const { manifest, path } = await readManifest(options.cwd, options.file);
75
329
  const command = resolveCommand(manifest, path, selector);
76
330
  write(options.format === 'json' ? renderJson(command) : renderDescription(command));
77
- };
78
- const checkManifest = async (options) => {
331
+ }
332
+ async function checkManifest(options) {
79
333
  const { manifest, path } = await readManifest(options.cwd, options.file);
80
334
  const result = { valid: true, manifestPath: path, commandCount: manifest.commands.length, groups: Object.keys(manifest.groups) };
81
335
  write(options.format === 'json' ? renderJson(result) : `valid: true\nmanifest: ${path}\ncommands: ${result.commandCount}\n`);
82
- };
83
- const runSelectedCommand = async (selector, _, options, flags) => {
84
- if (!selector)
85
- throw new RunXError('Missing selector. Usage: runx run <selector>');
336
+ }
337
+ async function runSelectedCommand(selector, options, args) {
86
338
  const { manifest, path } = await readManifest(options.cwd, options.file);
87
339
  const command = resolveCommand(manifest, path, selector);
88
- if (booleanFlag(flags, 'dryRun')) {
340
+ if (args.dryRun) {
89
341
  write(options.format === 'json' ? renderJson({ dryRun: true, command }) : renderExecutionPlan(command));
90
342
  return;
91
343
  }
92
- if (command.confirm === 'always' && !booleanFlag(flags, 'yes')) {
344
+ if (command.confirm === 'always' && !args.yes) {
93
345
  throw new RunXError(`Command ${command.uid} requires confirmation. Review it with \`runx describe ${command.uid}\` and rerun with --yes when authorized.`);
94
346
  }
95
347
  if (options.format === 'text')
96
348
  write(`Running ${command.uid} (${command.selector})\n`);
97
- const exitCode = await runCommand(command);
98
- process.exitCode = exitCode;
99
- };
100
- const runAgents = async (positionals, options, flags) => {
101
- const action = positionals[0];
102
- if (action === 'install') {
103
- const scope = (positionals[1] ?? 'local');
104
- if (scope !== 'local' && scope !== 'global')
105
- throw new RunXError('Agent scope must be local or global.');
106
- const tool = (stringFlag(flags, 'tool') ?? 'agents');
107
- if (!['agents', 'claude', 'all'].includes(tool))
108
- throw new RunXError('Agent tool must be agents, claude, or all.');
109
- const installed = await installAgentSkill(scope, tool, options.cwd);
110
- write(options.format === 'json' ? renderJson({ installed }) : `${installed.map((path) => `installed: ${path}`).join('\n')}\n`);
111
- return;
112
- }
113
- if (action === 'instructions') {
114
- const path = await installAgentInstructions(options.cwd);
115
- write(options.format === 'json' ? renderJson({ instructions: path }) : `updated: ${path}\n`);
116
- return;
117
- }
118
- throw new RunXError('Usage: runx agents install <local|global> [--tool agents|claude|all] | runx agents instructions');
119
- };
120
- const runUpgrade = async (positionals, options, flags) => {
121
- if (positionals[0] === 'check') {
122
- const result = await checkForLatestVersion();
123
- write(options.format === 'json' ? renderJson(result) : `current: ${result.currentVersion}\nlatest: ${result.latestVersion}\nupdate_available: ${result.updateAvailable}\n`);
124
- return;
125
- }
126
- if (positionals[0] === 'list') {
127
- const versions = await listAvailableVersions();
128
- write(options.format === 'json' ? renderJson({ versions }) : `${versions.join('\n')}\n`);
129
- return;
130
- }
131
- const result = await upgradeSelf(booleanFlag(flags, 'dryRun'));
132
- write(options.format === 'json' ? renderJson(result) : `current: ${result.currentVersion}\ntarget: ${result.latestVersion}\npath: ${result.executablePath}\n${result.scheduled ? 'scheduled: true\n' : ''}`);
133
- };
134
- const runUninstall = async (options, flags) => {
135
- const result = await uninstallSelf(booleanFlag(flags, 'dryRun'));
349
+ process.exitCode = await runCommand(command);
350
+ }
351
+ async function runAgentsInstall(scopeValue, options, args) {
352
+ const scope = scopeValue;
353
+ if (scope !== 'local' && scope !== 'global')
354
+ throw new RunXError('Agent scope must be local or global.');
355
+ const tool = (args.tool ?? 'agents');
356
+ if (!['agents', 'claude', 'all'].includes(tool))
357
+ throw new RunXError('Agent tool must be agents, claude, or all.');
358
+ const installed = await installAgentSkill(scope, tool, options.cwd);
359
+ write(options.format === 'json' ? renderJson({ installed }) : `${installed.map((path) => `installed: ${path}`).join('\n')}\n`);
360
+ }
361
+ async function runAgentsInstructions(options) {
362
+ const path = await installAgentInstructions(options.cwd);
363
+ write(options.format === 'json' ? renderJson({ instructions: path }) : `updated: ${path}\n`);
364
+ }
365
+ async function runUpgradeCheck(options) {
366
+ const result = await checkForLatestVersion();
367
+ write(options.format === 'json' ? renderJson(result) : `current: ${result.currentVersion}\nlatest: ${result.latestVersion}\nupdate_available: ${result.updateAvailable}\n`);
368
+ }
369
+ async function runUpgradeList(options) {
370
+ const versions = await listAvailableVersions();
371
+ write(options.format === 'json' ? renderJson({ versions }) : `${versions.join('\n')}\n`);
372
+ }
373
+ async function runUpgrade(options, args) {
374
+ const result = await upgradeSelf(Boolean(args.dryRun));
375
+ if (options.format === 'json')
376
+ return write(renderJson(result));
377
+ const summary = `current: ${result.currentVersion}\ntarget: ${result.latestVersion}\n`;
378
+ write(result.upToDate ? `${summary}Already up to date.\n` : `${summary}path: ${result.executablePath}\n${result.scheduled ? 'scheduled: true\n' : ''}`);
379
+ }
380
+ async function runUninstall(options, args) {
381
+ const result = await uninstallSelf(Boolean(args.dryRun));
136
382
  write(options.format === 'json' ? renderJson(result) : `path: ${result.executablePath}\n${result.dryRun ? 'dry_run: true\n' : result.scheduled ? 'scheduled: true\n' : 'uninstalled: true\n'}`);
137
- };
138
- const resolveOptions = (flags) => {
139
- const format = stringFlag(flags, 'format') ?? 'text';
383
+ }
384
+ function resolveOptions(args) {
385
+ const format = args.format ?? 'text';
140
386
  if (format !== 'text' && format !== 'json')
141
387
  throw new RunXError('Invalid --format value. Expected text or json.');
142
- return { cwd: resolve(stringFlag(flags, 'cwd') ?? process.cwd()), file: stringFlag(flags, 'file'), format: format, verbose: booleanFlag(flags, 'verbose') };
143
- };
144
- const write = (value) => {
388
+ return {
389
+ cwd: resolve(args.cwd ?? process.cwd()),
390
+ file: args.file,
391
+ format: format,
392
+ verbose: Boolean(args.verbose),
393
+ };
394
+ }
395
+ function isCittyUsageError(error) {
396
+ return error instanceof Error && error.name === 'CLIError';
397
+ }
398
+ function write(value) {
145
399
  process.stdout.write(value);
146
- };
400
+ }
package/library/help.d.ts CHANGED
@@ -2,5 +2,4 @@ export declare const readVersion: () => string;
2
2
  export declare const showHome: () => string;
3
3
  export declare const showHelpTree: () => string;
4
4
  export declare const showHelpDocs: () => string;
5
- export declare const showCommandHelp: (command: string) => string;
6
5
  //# sourceMappingURL=help.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../source/help.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW,QAAO,MAAiF,CAAA;AAEhH,eAAO,MAAM,QAAQ,QAAO,MAe3B,CAAA;AAED,eAAO,MAAM,YAAY,QAAO,MAapB,CAAA;AAEZ,eAAO,MAAM,YAAY,QAAO,MAU/B,CAAA;AAED,eAAO,MAAM,eAAe,YAAa,MAAM,KAAG,MAGjD,CAAA"}
1
+ {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../source/help.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW,QAAO,MAAiF,CAAA;AAEhH,eAAO,MAAM,QAAQ,QAAO,MAe3B,CAAA;AAED,eAAO,MAAM,YAAY,QAAO,MAapB,CAAA;AAEZ,eAAO,MAAM,YAAY,QAAO,MAU/B,CAAA"}
package/library/help.js CHANGED
@@ -41,7 +41,3 @@ Use UID values for automation. Use runx describe <selector> and runx run <select
41
41
 
42
42
  Agent skill: runx agents install local installs guiho-s-runx under .agents/skills.
43
43
  `;
44
- export const showCommandHelp = (command) => `RunX ${command}
45
-
46
- Run \`runx --help-tree\` for command structure and \`runx --help-docs\` for manifest guidance.
47
- `;
@@ -1,10 +1,7 @@
1
- import type { UpdateResult } from './types.js';
1
+ import type { RunXUpgradeResult, UpdateResult } from './types.js';
2
2
  export declare const checkForLatestVersion: () => Promise<UpdateResult>;
3
3
  export declare const listAvailableVersions: () => Promise<string[]>;
4
- export declare const upgradeSelf: (dryRun: boolean) => Promise<UpdateResult & {
5
- executablePath: string;
6
- scheduled: boolean;
7
- }>;
4
+ export declare const upgradeSelf: (dryRun: boolean) => Promise<RunXUpgradeResult>;
8
5
  export declare const uninstallSelf: (dryRun: boolean) => Promise<{
9
6
  executablePath: string;
10
7
  scheduled: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"self-management.d.ts","sourceRoot":"","sources":["../source/self-management.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAM9C,eAAO,MAAM,qBAAqB,QAAa,OAAO,CAAC,YAAY,CAYlE,CAAA;AAED,eAAO,MAAM,qBAAqB,QAAa,OAAO,CAAC,MAAM,EAAE,CAK9D,CAAA;AAED,eAAO,MAAM,WAAW,WAAkB,OAAO,KAAG,OAAO,CAAC,YAAY,GAAG;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAkBxH,CAAA;AAED,eAAO,MAAM,aAAa,WAAkB,OAAO,KAAG,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAW5H,CAAA"}
1
+ {"version":3,"file":"self-management.d.ts","sourceRoot":"","sources":["../source/self-management.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAMjE,eAAO,MAAM,qBAAqB,QAAa,OAAO,CAAC,YAAY,CAYlE,CAAA;AAED,eAAO,MAAM,qBAAqB,QAAa,OAAO,CAAC,MAAM,EAAE,CAK9D,CAAA;AAED,eAAO,MAAM,WAAW,WAAkB,OAAO,KAAG,OAAO,CAAC,iBAAiB,CAmB5E,CAAA;AAED,eAAO,MAAM,aAAa,WAAkB,OAAO,KAAG,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAW5H,CAAA"}
@@ -28,10 +28,11 @@ export const listAvailableVersions = async () => {
28
28
  export const upgradeSelf = async (dryRun) => {
29
29
  const executablePath = requireNativeExecutable();
30
30
  const update = await checkForLatestVersion();
31
+ const upToDate = !update.updateAvailable && update.latestVersion === update.currentVersion && Boolean(update.url);
31
32
  if (!update.updateAvailable || !update.url)
32
- return { ...update, executablePath, scheduled: false };
33
+ return { ...update, executablePath, scheduled: false, upToDate };
33
34
  if (dryRun)
34
- return { ...update, executablePath, scheduled: false };
35
+ return { ...update, executablePath, scheduled: false, upToDate: false };
35
36
  const response = await fetch(update.url);
36
37
  if (!response.ok)
37
38
  throw new RunXError(`Could not download RunX update: HTTP ${response.status}`);
@@ -40,10 +41,10 @@ export const upgradeSelf = async (dryRun) => {
40
41
  if (process.platform !== 'win32') {
41
42
  await chmod(temporaryPath, 0o755);
42
43
  await rename(temporaryPath, executablePath);
43
- return { ...update, executablePath, scheduled: false };
44
+ return { ...update, executablePath, scheduled: false, upToDate: false };
44
45
  }
45
46
  scheduleWindowsReplacement(temporaryPath, executablePath);
46
- return { ...update, executablePath, scheduled: true };
47
+ return { ...update, executablePath, scheduled: true, upToDate: false };
47
48
  };
48
49
  export const uninstallSelf = async (dryRun) => {
49
50
  const executablePath = requireNativeExecutable();
@@ -60,7 +61,7 @@ export const uninstallSelf = async (dryRun) => {
60
61
  const findAsset = (release) => release.assets.find((asset) => asset.name === assetName());
61
62
  const assetName = () => `runx-${process.platform === 'win32' ? 'windows' : process.platform === 'darwin' ? 'darwin' : 'linux'}-${process.arch}${process.platform === 'win32' ? '.exe' : ''}`;
62
63
  const requireNativeExecutable = () => {
63
- const executablePath = process.execPath;
64
+ const executablePath = process.env['RUNX_SELF_PATH'] ?? process.execPath;
64
65
  if (basename(executablePath).toLowerCase().startsWith('bun'))
65
66
  throw new RunXError('Self-management requires a native RunX executable installed from a GitHub release.');
66
67
  return executablePath;
@@ -9,11 +9,6 @@ export type ResolvedCommand = RunXCommand & {
9
9
  cwd: string;
10
10
  };
11
11
  export type OutputFormat = 'text' | 'json';
12
- export type ParsedArgs = {
13
- command?: string;
14
- positionals: string[];
15
- flags: Record<string, boolean | string | string[]>;
16
- };
17
12
  export type CliOptions = {
18
13
  cwd: string;
19
14
  file?: string;
@@ -28,4 +23,9 @@ export type UpdateResult = {
28
23
  updateAvailable: boolean;
29
24
  url?: string;
30
25
  };
26
+ export type RunXUpgradeResult = UpdateResult & {
27
+ executablePath: string;
28
+ scheduled: boolean;
29
+ upToDate: boolean;
30
+ };
31
31
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../source/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAElE,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,cAAc,CAAC,CAAA;AACxD,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,aAAa,CAAC,CAAA;AAEtD,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG;IAC1C,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,CAAA;AAE1C,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC,CAAA;CACnD,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,YAAY,CAAA;IACpB,OAAO,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAA;AAEnD,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAA;AAE3C,MAAM,MAAM,YAAY,GAAG;IACzB,cAAc,EAAE,MAAM,CAAA;IACtB,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,OAAO,CAAA;IACxB,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../source/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAElE,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,cAAc,CAAC,CAAA;AACxD,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,aAAa,CAAC,CAAA;AAEtD,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG;IAC1C,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,CAAA;AAE1C,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,YAAY,CAAA;IACpB,OAAO,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAA;AAEnD,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAA;AAE3C,MAAM,MAAM,YAAY,GAAG;IACzB,cAAc,EAAE,MAAM,CAAA;IACtB,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,OAAO,CAAA;IACxB,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG;IAC7C,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,OAAO,CAAA;CAClB,CAAA"}
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.2.2",
4
+ "version": "0.2.4",
5
5
  "type": "module",
6
6
  "main": "./library/guiho-runx.js",
7
7
  "types": "./library/guiho-runx.d.ts",
@@ -68,7 +68,8 @@
68
68
  "_uid": "bun nanoid --alphabet abcdefghijklmnopqrstuvwxyz0123456789"
69
69
  },
70
70
  "dependencies": {
71
- "@sinclair/typebox": "^0.34.52"
71
+ "@sinclair/typebox": "^0.34.52",
72
+ "citty": "^0.2.2"
72
73
  },
73
74
  "devDependencies": {
74
75
  "@types/bun": "^1.3.14",