@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/CHANGELOG.md +23 -0
- package/DOCS.md +16 -2
- package/README.md +6 -0
- package/docs/architecture/architecture.xdocs.md +3 -2
- package/docs/architecture/cli-architecture.md +15 -7
- package/docs/decisions/citty-cli-migration.md +139 -0
- package/docs/decisions/decisions.xdocs.md +2 -0
- package/docs/plans/citty-cli-migration.md +169 -0
- package/docs/plans/plans.xdocs.md +3 -0
- package/docs/reviews/implementation/citty-cli-migration-review.md +85 -0
- package/docs/reviews/implementation/implementation.xdocs.md +22 -0
- package/docs/reviews/plans/citty-cli-migration-review.md +80 -0
- package/docs/reviews/plans/plans.xdocs.md +3 -0
- package/docs/reviews/reviews.xdocs.md +1 -0
- package/docs/validation/citty-cli-migration.md +95 -0
- package/docs/validation/npm-trusted-publishing-0.2.2.md +54 -0
- package/docs/validation/validation.xdocs.md +4 -0
- package/library/cli.d.ts +8 -2
- package/library/cli.d.ts.map +1 -1
- package/library/cli.js +357 -103
- package/library/help.d.ts +0 -1
- package/library/help.d.ts.map +1 -1
- package/library/help.js +0 -4
- package/library/self-management.d.ts +2 -5
- package/library/self-management.d.ts.map +1 -1
- package/library/self-management.js +6 -5
- package/library/types.d.ts +5 -5
- package/library/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/skills/guiho-s-runx/SKILL.md +16 -0
- package/skills/guiho-s-runx/guiho-s-runx.xdocs.md +4 -3
- package/library/flags.d.ts +0 -5
- package/library/flags.d.ts.map +0 -1
- package/library/flags.js +0 -32
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 {
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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' && !
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
139
|
-
const format =
|
|
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 {
|
|
143
|
-
|
|
144
|
-
|
|
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
|
package/library/help.d.ts.map
CHANGED
|
@@ -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
|
|
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<
|
|
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;
|
|
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;
|
package/library/types.d.ts
CHANGED
|
@@ -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
|
package/library/types.d.ts.map
CHANGED
|
@@ -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,
|
|
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.
|
|
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",
|