@guiho/runx 0.2.0

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 (72) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/DOCS.md +118 -0
  3. package/LICENSE.md +36 -0
  4. package/README.md +79 -0
  5. package/SECURITY.md +28 -0
  6. package/devops/build-binaries.ts +99 -0
  7. package/devops/devops.xdocs.md +25 -0
  8. package/devops/install.ps1 +216 -0
  9. package/devops/install.sh +324 -0
  10. package/docs/architecture/architecture.xdocs.md +20 -0
  11. package/docs/architecture/cli-architecture.md +39 -0
  12. package/docs/decisions/alpha-boundaries.md +29 -0
  13. package/docs/decisions/decisions.xdocs.md +19 -0
  14. package/docs/docs.xdocs.md +24 -0
  15. package/docs/plans/alpha-implementation.md +27 -0
  16. package/docs/plans/plans.xdocs.md +19 -0
  17. package/docs/requirements/alpha-command-catalog.md +41 -0
  18. package/docs/requirements/requirements.xdocs.md +19 -0
  19. package/docs/todo/implement-runx-alpha.md +36 -0
  20. package/docs/todo/protect-branches-and-tag-creation.md +83 -0
  21. package/docs/todo/todo.xdocs.md +21 -0
  22. package/docs/validation/alpha-implementation-summary.md +52 -0
  23. package/docs/validation/validation.xdocs.md +21 -0
  24. package/library/agents.d.ts +11 -0
  25. package/library/agents.d.ts.map +1 -0
  26. package/library/agents.js +51 -0
  27. package/library/cli.d.ts +3 -0
  28. package/library/cli.d.ts.map +1 -0
  29. package/library/cli.js +146 -0
  30. package/library/embedded-resources.d.ts +7 -0
  31. package/library/embedded-resources.d.ts.map +1 -0
  32. package/library/embedded-resources.js +5 -0
  33. package/library/errors.d.ts +6 -0
  34. package/library/errors.d.ts.map +1 -0
  35. package/library/errors.js +12 -0
  36. package/library/executor.d.ts +3 -0
  37. package/library/executor.d.ts.map +1 -0
  38. package/library/executor.js +24 -0
  39. package/library/flags.d.ts +5 -0
  40. package/library/flags.d.ts.map +1 -0
  41. package/library/flags.js +32 -0
  42. package/library/guiho-runx-bin.d.ts +3 -0
  43. package/library/guiho-runx-bin.d.ts.map +1 -0
  44. package/library/guiho-runx-bin.js +3 -0
  45. package/library/guiho-runx-native-bin.d.ts +3 -0
  46. package/library/guiho-runx-native-bin.d.ts.map +1 -0
  47. package/library/guiho-runx-native-bin.js +5 -0
  48. package/library/guiho-runx.d.ts +4 -0
  49. package/library/guiho-runx.d.ts.map +1 -0
  50. package/library/guiho-runx.js +2 -0
  51. package/library/help.d.ts +6 -0
  52. package/library/help.d.ts.map +1 -0
  53. package/library/help.js +47 -0
  54. package/library/manifest.d.ts +41 -0
  55. package/library/manifest.d.ts.map +1 -0
  56. package/library/manifest.js +112 -0
  57. package/library/render.d.ts +6 -0
  58. package/library/render.d.ts.map +1 -0
  59. package/library/render.js +40 -0
  60. package/library/self-management.d.ts +13 -0
  61. package/library/self-management.d.ts.map +1 -0
  62. package/library/self-management.js +82 -0
  63. package/library/types.d.ts +31 -0
  64. package/library/types.d.ts.map +1 -0
  65. package/library/types.js +0 -0
  66. package/package.json +77 -0
  67. package/scripts/runx-bin.ts +18 -0
  68. package/scripts/scripts.xdocs.md +20 -0
  69. package/skills/guiho-s-runx/SKILL.md +57 -0
  70. package/skills/guiho-s-runx/agents/openai.yaml +4 -0
  71. package/skills/guiho-s-runx/guiho-s-runx.xdocs.md +22 -0
  72. package/skills/skills.xdocs.md +19 -0
@@ -0,0 +1,112 @@
1
+ import { dirname, isAbsolute, relative, resolve } from 'node:path';
2
+ import { Type } from '@sinclair/typebox';
3
+ import { TypeCompiler } from '@sinclair/typebox/compiler';
4
+ import { RunXError, invariant } from './errors.js';
5
+ const identifier = '^[a-z][a-z0-9-]*$';
6
+ export const CommandSchema = Type.Object({
7
+ uid: Type.String({ pattern: identifier }),
8
+ id: Type.String({ pattern: identifier }),
9
+ group: Type.String({ pattern: identifier }),
10
+ summary: Type.String({ minLength: 1 }),
11
+ description: Type.String({ minLength: 1 }),
12
+ command: Type.String({ minLength: 1 }),
13
+ cwd: Type.Optional(Type.String({ minLength: 1 })),
14
+ shell: Type.Optional(Type.Union([
15
+ Type.Literal('auto'),
16
+ Type.Literal('bash'),
17
+ Type.Literal('sh'),
18
+ Type.Literal('powershell'),
19
+ Type.Literal('cmd'),
20
+ ])),
21
+ tags: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
22
+ confirm: Type.Optional(Type.Union([Type.Literal('never'), Type.Literal('always')])),
23
+ }, { additionalProperties: false });
24
+ export const ManifestSchema = Type.Object({
25
+ version: Type.Literal(1),
26
+ project: Type.Optional(Type.Object({ name: Type.String({ minLength: 1 }) }, { additionalProperties: false })),
27
+ groups: Type.Record(Type.String({ pattern: identifier }), Type.Object({ summary: Type.String({ minLength: 1 }) }, { additionalProperties: false })),
28
+ commands: Type.Array(CommandSchema, { minItems: 1 }),
29
+ }, { additionalProperties: false });
30
+ const manifestCheck = TypeCompiler.Compile(ManifestSchema);
31
+ export const findManifest = async (cwd, explicitFile) => {
32
+ if (explicitFile) {
33
+ const candidate = resolve(cwd, explicitFile);
34
+ if (await Bun.file(candidate).exists())
35
+ return candidate;
36
+ throw new RunXError(`Manifest not found: ${candidate}`);
37
+ }
38
+ let current = resolve(cwd);
39
+ while (true) {
40
+ const candidate = resolve(current, 'runx.yaml');
41
+ if (await Bun.file(candidate).exists())
42
+ return candidate;
43
+ const parent = dirname(current);
44
+ if (parent === current)
45
+ break;
46
+ current = parent;
47
+ }
48
+ throw new RunXError('No runx.yaml found. Run this command inside a configured project or pass --file <path>.');
49
+ };
50
+ export const readManifest = async (cwd, explicitFile) => {
51
+ const path = await findManifest(cwd, explicitFile);
52
+ let parsed;
53
+ try {
54
+ parsed = Bun.YAML.parse(await Bun.file(path).text());
55
+ }
56
+ catch (error) {
57
+ const message = error instanceof Error ? error.message : 'Unknown YAML parsing error.';
58
+ throw new RunXError(`Invalid YAML in ${path}: ${message}`);
59
+ }
60
+ if (!manifestCheck.Check(parsed)) {
61
+ const errors = [...manifestCheck.Errors(parsed)].map((error) => `${error.path || '/'}: ${error.message}`);
62
+ throw new RunXError(`Invalid RunX manifest ${path}:\n${errors.join('\n')}`);
63
+ }
64
+ const manifest = parsed;
65
+ validateManifestSemantics(manifest, path);
66
+ return { manifest, path };
67
+ };
68
+ export const resolveCommand = (manifest, manifestPath, selector) => {
69
+ const commands = manifest.commands;
70
+ let command = commands.find((entry) => entry.uid === selector);
71
+ if (!command && selector.includes('/')) {
72
+ command = commands.find((entry) => `${entry.group}/${entry.id}` === selector);
73
+ }
74
+ if (!command && /^\d+$/.test(selector)) {
75
+ command = commands[Number.parseInt(selector, 10) - 1];
76
+ }
77
+ if (!command) {
78
+ const matches = commands.filter((entry) => entry.id === selector);
79
+ if (matches.length > 1) {
80
+ throw new RunXError(`Ambiguous command ID "${selector}". Use one of: ${matches.map((entry) => `${entry.group}/${entry.id} (${entry.uid})`).join(', ')}`);
81
+ }
82
+ command = matches[0];
83
+ }
84
+ if (!command)
85
+ throw new RunXError(`Unknown command selector: ${selector}`);
86
+ const manifestDirectory = dirname(manifestPath);
87
+ const commandCwd = resolve(manifestDirectory, command.cwd ?? '.');
88
+ const relativeCwd = relative(manifestDirectory, commandCwd);
89
+ invariant(!isAbsolute(relativeCwd) && !relativeCwd.startsWith('..'), `Command ${command.uid} has a cwd outside the manifest directory.`);
90
+ return {
91
+ ...command,
92
+ index: commands.indexOf(command) + 1,
93
+ selector: `${command.group}/${command.id}`,
94
+ manifestPath,
95
+ cwd: commandCwd,
96
+ };
97
+ };
98
+ const validateManifestSemantics = (manifest, path) => {
99
+ const uids = new Set();
100
+ const selectors = new Set();
101
+ for (const command of manifest.commands) {
102
+ if (!manifest.groups[command.group])
103
+ throw new RunXError(`Command ${command.uid} references unknown group "${command.group}" in ${path}.`);
104
+ if (uids.has(command.uid))
105
+ throw new RunXError(`Duplicate command UID "${command.uid}" in ${path}.`);
106
+ const selector = `${command.group}/${command.id}`;
107
+ if (selectors.has(selector))
108
+ throw new RunXError(`Duplicate command selector "${selector}" in ${path}.`);
109
+ uids.add(command.uid);
110
+ selectors.add(selector);
111
+ }
112
+ };
@@ -0,0 +1,6 @@
1
+ import type { ResolvedCommand, RunXManifest } from './types.js';
2
+ export declare const renderJson: (value: unknown) => string;
3
+ export declare const renderList: (manifest: RunXManifest, manifestPath: string) => string;
4
+ export declare const renderDescription: (command: ResolvedCommand) => string;
5
+ export declare const renderExecutionPlan: (command: ResolvedCommand) => string;
6
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../source/render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE/D,eAAO,MAAM,UAAU,UAAW,OAAO,KAAG,MAA+C,CAAA;AAE3F,eAAO,MAAM,UAAU,aAAc,YAAY,gBAAgB,MAAM,KAAG,MAoBzE,CAAA;AAED,eAAO,MAAM,iBAAiB,YAAa,eAAe,KAAG,MAW1C,CAAA;AAEnB,eAAO,MAAM,mBAAmB,YAAa,eAAe,KAAG,MAO5C,CAAA"}
@@ -0,0 +1,40 @@
1
+ export const renderJson = (value) => JSON.stringify(value, null, 2) + '\n';
2
+ export const renderList = (manifest, manifestPath) => {
3
+ const rows = manifest.commands.map((command, index) => ({
4
+ index: index + 1,
5
+ uid: command.uid,
6
+ selector: `${command.group}/${command.id}`,
7
+ summary: command.summary,
8
+ confirm: command.confirm ?? 'never',
9
+ }));
10
+ const width = (key, label) => Math.max(label.length, ...rows.map((row) => String(row[key]).length));
11
+ const indexWidth = width('index', 'IDX');
12
+ const uidWidth = width('uid', 'UID');
13
+ const selectorWidth = width('selector', 'SELECTOR');
14
+ const lines = [`RunX commands from ${manifestPath}`, '', `${'IDX'.padEnd(indexWidth)} ${'UID'.padEnd(uidWidth)} ${'SELECTOR'.padEnd(selectorWidth)} SUMMARY`];
15
+ for (const row of rows) {
16
+ const confirmation = row.confirm === 'always' ? ' [confirm]' : '';
17
+ lines.push(`${String(row.index).padEnd(indexWidth)} ${row.uid.padEnd(uidWidth)} ${row.selector.padEnd(selectorWidth)} ${row.summary}${confirmation}`);
18
+ }
19
+ return lines.join('\n') + '\n';
20
+ };
21
+ export const renderDescription = (command) => [
22
+ `${command.uid} (${command.selector})`,
23
+ '',
24
+ command.description,
25
+ '',
26
+ `index: ${command.index}`,
27
+ `cwd: ${command.cwd}`,
28
+ `shell: ${command.shell ?? 'auto'}`,
29
+ `confirmation: ${command.confirm ?? 'never'}`,
30
+ `tags: ${(command.tags ?? []).join(', ') || 'none'}`,
31
+ `command: ${command.command}`,
32
+ ].join('\n') + '\n';
33
+ export const renderExecutionPlan = (command) => [
34
+ `uid: ${command.uid}`,
35
+ `selector: ${command.selector}`,
36
+ `manifest: ${command.manifestPath}`,
37
+ `cwd: ${command.cwd}`,
38
+ `shell: ${command.shell ?? 'auto'}`,
39
+ `command: ${command.command}`,
40
+ ].join('\n') + '\n';
@@ -0,0 +1,13 @@
1
+ import type { UpdateResult } from './types.js';
2
+ export declare const checkForLatestVersion: () => Promise<UpdateResult>;
3
+ export declare const listAvailableVersions: () => Promise<string[]>;
4
+ export declare const upgradeSelf: (dryRun: boolean) => Promise<UpdateResult & {
5
+ executablePath: string;
6
+ scheduled: boolean;
7
+ }>;
8
+ export declare const uninstallSelf: (dryRun: boolean) => Promise<{
9
+ executablePath: string;
10
+ scheduled: boolean;
11
+ dryRun: boolean;
12
+ }>;
13
+ //# sourceMappingURL=self-management.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,82 @@
1
+ import { chmod, rename, rm } from 'node:fs/promises';
2
+ import { basename } from 'node:path';
3
+ import { RunXError } from './errors.js';
4
+ import { readVersion } from './help.js';
5
+ const repositoryApi = 'https://api.github.com/repos/CGuiho/runx/releases';
6
+ export const checkForLatestVersion = async () => {
7
+ const currentVersion = readVersion();
8
+ try {
9
+ const response = await fetch(`${repositoryApi}/latest`, { headers: { accept: 'application/vnd.github+json' } });
10
+ if (!response.ok)
11
+ return { currentVersion, latestVersion: currentVersion, updateAvailable: false };
12
+ const release = await response.json();
13
+ const latestVersion = release.tag_name.replace(/^@guiho\/runx@/, '').replace(/^v/, '');
14
+ const asset = findAsset(release);
15
+ return { currentVersion, latestVersion, updateAvailable: compareVersions(latestVersion, currentVersion) > 0, url: asset?.browser_download_url };
16
+ }
17
+ catch {
18
+ return { currentVersion, latestVersion: currentVersion, updateAvailable: false };
19
+ }
20
+ };
21
+ export const listAvailableVersions = async () => {
22
+ const response = await fetch(`${repositoryApi}?per_page=20`, { headers: { accept: 'application/vnd.github+json' } });
23
+ if (!response.ok)
24
+ throw new RunXError(`Could not retrieve RunX releases: HTTP ${response.status}`);
25
+ const releases = await response.json();
26
+ return releases.map((release) => release.tag_name.replace(/^@guiho\/runx@/, '').replace(/^v/, ''));
27
+ };
28
+ export const upgradeSelf = async (dryRun) => {
29
+ const executablePath = requireNativeExecutable();
30
+ const update = await checkForLatestVersion();
31
+ if (!update.updateAvailable || !update.url)
32
+ return { ...update, executablePath, scheduled: false };
33
+ if (dryRun)
34
+ return { ...update, executablePath, scheduled: false };
35
+ const response = await fetch(update.url);
36
+ if (!response.ok)
37
+ throw new RunXError(`Could not download RunX update: HTTP ${response.status}`);
38
+ const temporaryPath = `${executablePath}.new`;
39
+ await Bun.write(temporaryPath, await response.arrayBuffer());
40
+ if (process.platform !== 'win32') {
41
+ await chmod(temporaryPath, 0o755);
42
+ await rename(temporaryPath, executablePath);
43
+ return { ...update, executablePath, scheduled: false };
44
+ }
45
+ scheduleWindowsReplacement(temporaryPath, executablePath);
46
+ return { ...update, executablePath, scheduled: true };
47
+ };
48
+ export const uninstallSelf = async (dryRun) => {
49
+ const executablePath = requireNativeExecutable();
50
+ if (dryRun)
51
+ return { executablePath, scheduled: false, dryRun: true };
52
+ if (process.platform === 'win32') {
53
+ const command = `ping 127.0.0.1 -n 2 > nul & del /f /q "${executablePath}"`;
54
+ Bun.spawn(['cmd.exe', '/d', '/s', '/c', command], { detached: true, stdout: 'ignore', stderr: 'ignore', stdin: 'ignore' });
55
+ return { executablePath, scheduled: true, dryRun: false };
56
+ }
57
+ await rm(executablePath);
58
+ return { executablePath, scheduled: false, dryRun: false };
59
+ };
60
+ const findAsset = (release) => release.assets.find((asset) => asset.name === assetName());
61
+ const assetName = () => `runx-${process.platform === 'win32' ? 'windows' : process.platform === 'darwin' ? 'darwin' : 'linux'}-${process.arch}${process.platform === 'win32' ? '.exe' : ''}`;
62
+ const requireNativeExecutable = () => {
63
+ const executablePath = process.execPath;
64
+ if (basename(executablePath).toLowerCase().startsWith('bun'))
65
+ throw new RunXError('Self-management requires a native RunX executable installed from a GitHub release.');
66
+ return executablePath;
67
+ };
68
+ const scheduleWindowsReplacement = (temporaryPath, executablePath) => {
69
+ const command = `ping 127.0.0.1 -n 2 > nul & move /y "${temporaryPath}" "${executablePath}" > nul`;
70
+ Bun.spawn(['cmd.exe', '/d', '/s', '/c', command], { detached: true, stdout: 'ignore', stderr: 'ignore', stdin: 'ignore' });
71
+ };
72
+ const compareVersions = (left, right) => {
73
+ const parse = (value) => value.split(/[.+-]/).map((part) => Number.parseInt(part, 10) || 0);
74
+ const a = parse(left);
75
+ const b = parse(right);
76
+ for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
77
+ const difference = (a[index] ?? 0) - (b[index] ?? 0);
78
+ if (difference !== 0)
79
+ return difference;
80
+ }
81
+ return 0;
82
+ };
@@ -0,0 +1,31 @@
1
+ import type { Static } from '@sinclair/typebox';
2
+ import type { CommandSchema, ManifestSchema } from './manifest.js';
3
+ export type RunXManifest = Static<typeof ManifestSchema>;
4
+ export type RunXCommand = Static<typeof CommandSchema>;
5
+ export type ResolvedCommand = RunXCommand & {
6
+ index: number;
7
+ selector: string;
8
+ manifestPath: string;
9
+ cwd: string;
10
+ };
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
+ export type CliOptions = {
18
+ cwd: string;
19
+ file?: string;
20
+ format: OutputFormat;
21
+ verbose: boolean;
22
+ };
23
+ export type AgentTool = 'agents' | 'claude' | 'all';
24
+ export type AgentScope = 'local' | 'global';
25
+ export type UpdateResult = {
26
+ currentVersion: string;
27
+ latestVersion: string;
28
+ updateAvailable: boolean;
29
+ url?: string;
30
+ };
31
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +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"}
File without changes
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@guiho/runx",
3
+ "description": "A language-agnostic, documented command catalog and local CLI executor.",
4
+ "version": "0.2.0",
5
+ "type": "module",
6
+ "main": "./library/guiho-runx.js",
7
+ "types": "./library/guiho-runx.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./library/guiho-runx.d.ts",
11
+ "import": "./library/guiho-runx.js"
12
+ }
13
+ },
14
+ "bin": {
15
+ "runx": "scripts/runx-bin.ts"
16
+ },
17
+ "files": [
18
+ "library/",
19
+ "scripts/",
20
+ "skills/",
21
+ "docs/",
22
+ "devops/",
23
+ "README.md",
24
+ "DOCS.md",
25
+ "CHANGELOG.md",
26
+ "LICENSE.md",
27
+ "SECURITY.md"
28
+ ],
29
+ "keywords": [
30
+ "automation",
31
+ "bun",
32
+ "cli",
33
+ "commands",
34
+ "runx",
35
+ "task-runner",
36
+ "yaml"
37
+ ],
38
+ "license": "MIT",
39
+ "author": {
40
+ "name": "Cristovao GUIHO",
41
+ "email": "cg@guiho.co",
42
+ "url": "https://guiho.co/cg"
43
+ },
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/CGuiho/runx.git"
47
+ },
48
+ "homepage": "https://github.com/CGuiho/runx#readme",
49
+ "bugs": {
50
+ "url": "https://github.com/CGuiho/runx/issues"
51
+ },
52
+ "publishConfig": {
53
+ "access": "public"
54
+ },
55
+ "scripts": {
56
+ "dev": "bun run --watch source/guiho-runx-bin.ts",
57
+ "build": "rm -rf library && tsc -p .",
58
+ "binary": "bun build source/guiho-runx-native-bin.ts --compile --outfile bin/runx",
59
+ "binaries": "bun run devops/build-binaries.ts",
60
+ "test": "bun test",
61
+ "typecheck": "tsc -p . --noEmit",
62
+ "check": "bun run typecheck && bun test",
63
+ "clean": "rm -rf .temp",
64
+ "clean-build": "rm -rf library bundle bin",
65
+ "clean-installation": "rm -rf node_modules bun.lock",
66
+ "_tc": "bun run typecheck",
67
+ "_ci": "bun run clean && bun run clean-build && bun run clean-installation",
68
+ "_uid": "bun nanoid --alphabet abcdefghijklmnopqrstuvwxyz0123456789"
69
+ },
70
+ "dependencies": {
71
+ "@sinclair/typebox": "^0.34.52"
72
+ },
73
+ "devDependencies": {
74
+ "@types/bun": "^1.3.14",
75
+ "typescript": "^7.0.2"
76
+ }
77
+ }
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env bun
2
+
3
+ const args = process.argv.slice(2)
4
+ const binaryPath = new URL(`../vendor/runx${process.platform === 'win32' ? '.exe' : ''}`, import.meta.url)
5
+ const sourcePath = new URL('../source/guiho-runx-bin.ts', import.meta.url)
6
+
7
+ if (await Bun.file(binaryPath).exists()) {
8
+ const child = Bun.spawn([Bun.fileURLToPath(binaryPath), ...args], { stdin: 'inherit', stdout: 'inherit', stderr: 'inherit' })
9
+ process.exit(await child.exited)
10
+ }
11
+
12
+ if (await Bun.file(sourcePath).exists()) {
13
+ const child = Bun.spawn([process.execPath, Bun.fileURLToPath(sourcePath), ...args], { stdin: 'inherit', stdout: 'inherit', stderr: 'inherit' })
14
+ process.exit(await child.exited)
15
+ }
16
+
17
+ console.error('error: RunX executable is unavailable. Install an official native release or reinstall @guiho/runx.')
18
+ process.exit(1)
@@ -0,0 +1,20 @@
1
+ ---
2
+ subject: runx-scripts
3
+ description: Package-manager launcher scripts for RunX.
4
+ parent: runx
5
+ children: []
6
+ files:
7
+ runx-bin.ts: Executes a packaged native binary when present or the source CLI in a checkout.
8
+ documents: {}
9
+ tags:
10
+ - scripts
11
+ - cli
12
+ keywords:
13
+ - runx
14
+ - launcher
15
+ flags: []
16
+ status: stable
17
+ ---
18
+
19
+ The package launcher preserves the `runx` bin command in development checkouts
20
+ and installed packages.
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: guiho-s-runx
3
+ description: Use when inspecting, creating, validating, organizing, documenting, or running a RunX `runx.yaml` command catalog. This includes listing available project commands, choosing a command by UID or selector, reviewing command descriptions, dry runs, confirmation-gated execution, and agent-safe project automation.
4
+ ---
5
+
6
+ # GUIHO RunX
7
+
8
+ Use RunX as the command catalog for a project. It documents executable local
9
+ commands but does not make them inherently safe: a manifest is trusted code.
10
+
11
+ ## Inspect Before Executing
12
+
13
+ 1. From the intended project directory, run `runx check --format json`.
14
+ 2. Run `runx list --format json` to inspect groups, UIDs, summaries, tags, and
15
+ confirmation requirements.
16
+ 3. Choose a stable `uid` for automation. An index is only for the current list
17
+ and an unqualified ID can be ambiguous.
18
+ 4. Run `runx describe <uid>` before an unfamiliar or high-impact command.
19
+ 5. Run `runx run <uid> --dry-run` before a real execution when the command has
20
+ filesystem, database, release, deployment, or production effects.
21
+
22
+ ## Execute Safely
23
+
24
+ - Use `runx run <uid>` or the short alias `runx r <uid>`.
25
+ - `runx <selector>` is allowed as a human shorthand only when it cannot be
26
+ confused with a built-in command; agents should prefer explicit `runx run`.
27
+ - If `confirm: always` is set, RunX requires `--yes`.
28
+ - Add `--yes` only after the user explicitly authorizes the specific command.
29
+ - Do not treat a group name such as `development` or `operations` as a safety
30
+ guarantee. Read the command description and command text.
31
+ - Do not add secrets to `runx.yaml`. Use the project's existing environment or
32
+ secret-management workflow.
33
+
34
+ ## Maintain Manifests
35
+
36
+ - Keep `uid`, `id`, `group`, `summary`, `description`, and `command` present.
37
+ - Keep UIDs stable; never reuse a UID for a materially different command.
38
+ - Use a group for organization, a concise summary for listings, and a complete
39
+ description for prerequisites, effects, and risk.
40
+ - Set `confirm: always` for destructive, release, deployment, migration, and
41
+ production-impacting commands when confirmation is useful.
42
+ - Run `runx check` after every manifest change. It rejects unknown fields,
43
+ duplicate UIDs/selectors, missing descriptions, invalid working directories,
44
+ and unsupported shell choices.
45
+
46
+ ## Useful Commands
47
+
48
+ ```text
49
+ runx Show the RunX home page and usage.
50
+ runx list List the nearest manifest.
51
+ runx describe <uid> Explain one command without execution.
52
+ runx run <uid> --dry-run Inspect the execution plan.
53
+ runx r <uid> Run through the short alias.
54
+ runx agents install local Install this skill into .agents/skills.
55
+ runx --help-tree Show the command hierarchy.
56
+ runx --help-docs Show manifest documentation guidance.
57
+ ```
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "RunX"
3
+ short_description: "Manage documented RunX command catalogs safely."
4
+ default_prompt: "Use the RunX CLI to inspect, validate, and run commands from runx.yaml safely."
@@ -0,0 +1,22 @@
1
+ ---
2
+ subject: guiho-s-runx
3
+ description: Agent workflow for inspecting, validating, and safely executing RunX manifest commands.
4
+ parent: runx-skills
5
+ children: []
6
+ files:
7
+ SKILL.md: RunX-specific agent instructions and safe execution workflow.
8
+ documents:
9
+ SKILL.md: RunX-specific agent instructions and safe execution workflow.
10
+ tags:
11
+ - agents
12
+ - runx
13
+ keywords:
14
+ - runx
15
+ - command catalog
16
+ - dry run
17
+ flags: []
18
+ status: stable
19
+ ---
20
+
21
+ The skill makes manifest inspection, UID selection, dry runs, and explicit
22
+ confirmation the default agent workflow for RunX commands.
@@ -0,0 +1,19 @@
1
+ ---
2
+ subject: runx-skills
3
+ description: Bundled agent skills that teach AI agents to operate RunX command catalogs safely.
4
+ parent: runx
5
+ children:
6
+ - guiho-s-runx
7
+ files: {}
8
+ documents: {}
9
+ tags:
10
+ - agents
11
+ - skills
12
+ keywords:
13
+ - runx
14
+ - agent skill
15
+ flags: []
16
+ status: stable
17
+ ---
18
+
19
+ Bundled skills ship with RunX and can be installed through `runx agents install`.