@runa-cmd/create 0.0.0-stage → 0.2.0-beta.6

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 ADDED
@@ -0,0 +1,9 @@
1
+ # @runa-cmd/create
2
+
3
+ ## 0.2.0-beta.6
4
+
5
+ ## 0.2.0-beta.5
6
+
7
+ ### Minor Changes
8
+
9
+ - ba3be24: New package: `npm create @runa-cmd@beta my-tool` creates a CLI ready to run, test and build. It makes the project of the getting started guide, named after yours, and installs nothing on its own.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Runa CLI Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,28 @@
1
- # Temporary Holding Version
1
+ # @runa-cmd/create
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Create a CLI made with [Runa](https://github.com/jrodrigopuca/runa-cmd), ready to run, test and publish.
4
+
5
+ ```bash
6
+ npm create @runa-cmd@beta my-tool
7
+ cd my-tool
8
+ npm install
9
+ node src/main.ts hello Ana
10
+ ```
11
+
12
+ Until Runa reaches 1.0, add `@beta`, as above.
13
+
14
+ It works with pnpm, yarn and bun too, as `pnpm create @runa-cmd@beta my-tool`, and the next steps it prints use the one you ran it with.
15
+
16
+ ## What you get
17
+
18
+ | File | What it is |
19
+ |---|---|
20
+ | `src/cli.ts` | Your commands. It exports the CLI without starting it, so tests can import it |
21
+ | `src/main.ts` | Starts the CLI. It's the program that gets installed |
22
+ | `src/cli.test.ts` | Tests with `testCLI()`, run by `node --test` |
23
+ | `tsconfig.json` | Runs your TypeScript as it is while you develop, and builds `dist/` to ship |
24
+ | `package.json` | `bin` named after your project, and `build` to compile |
25
+
26
+ The name is used for the folder, the package and the command people type, so it takes lowercase letters, numbers and dashes. It won't write into a folder that already has files, and it installs nothing: you choose the package manager and when.
27
+
28
+ These are the files of the [getting started guide](https://github.com/jrodrigopuca/runa-cmd/blob/main/guides/getting-started.md), which explains each of them. Its code runs in CI, so what this creates works.
@@ -0,0 +1,21 @@
1
+ /** Something the person can fix: a name, or a folder that's taken. */
2
+ export declare class CreateError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ export interface CreateOptions {
6
+ /** The project, its folder, its package and the program it installs */
7
+ name: string;
8
+ /** Where the folder goes */
9
+ cwd: string;
10
+ /** A copy of examples/getting-started */
11
+ templateDir: string;
12
+ /** The @runa-cmd/core the project depends on */
13
+ runaVersion: string;
14
+ }
15
+ /** Create the project, or explain why not before writing anything. */
16
+ export declare function createProject(options: CreateOptions): {
17
+ dir: string;
18
+ };
19
+ /** What to run next, with the package manager that ran the generator. */
20
+ export declare function nextSteps(name: string, userAgent: string | undefined): string;
21
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"AAUA,sEAAsE;AACtE,qBAAa,WAAY,SAAQ,KAAK;IACrC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACD;AAED,MAAM,WAAW,aAAa;IAC7B,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;CACpB;AAkDD,sEAAsE;AACtE,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CA0BrE;AAED,yEAAyE;AACzE,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAiB7E"}
package/dist/create.js ADDED
@@ -0,0 +1,97 @@
1
+ /**
2
+ * @runa-cmd/create — Make a new CLI project (T-605)
3
+ *
4
+ * The template is the project of the getting started guide, so what this
5
+ * creates is what the guide teaches, and CI runs both. It installs nothing:
6
+ * which package manager to use, and when, is the person's call.
7
+ */
8
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
9
+ import { join, resolve } from 'node:path';
10
+ /** Something the person can fix: a name, or a folder that's taken. */
11
+ export class CreateError extends Error {
12
+ constructor(message) {
13
+ super(message);
14
+ this.name = 'CreateError';
15
+ }
16
+ }
17
+ /** A name that works as a folder, an npm package and a command a person types. */
18
+ const PROJECT_NAME = /^[a-z0-9]+(-[a-z0-9]+)*$/;
19
+ /** The files copied as they are: they're the code the guide shows. */
20
+ const COPIED = ['tsconfig.json', 'src/cli.ts', 'src/main.ts', 'src/cli.test.ts'];
21
+ const GITIGNORE = 'node_modules/\ndist/\n';
22
+ function readme(name) {
23
+ return `# ${name}
24
+
25
+ A CLI made with [Runa](https://github.com/jrodrigopuca/runa-cmd).
26
+
27
+ \`\`\`bash
28
+ node src/main.ts hello Ana # Run it
29
+ node --test src/cli.test.ts # Test it
30
+ npm run build # Build dist/, which is what gets installed
31
+ npm install -g . # Install it as ${name}
32
+ \`\`\`
33
+
34
+ The commands are in \`src/cli.ts\`. The [getting started guide](https://github.com/jrodrigopuca/runa-cmd/blob/main/guides/getting-started.md) explains every file.
35
+ `;
36
+ }
37
+ function packageJson(templateDir, name, runaVersion) {
38
+ const template = JSON.parse(readFileSync(join(templateDir, 'package.json'), 'utf8'));
39
+ const pkg = {
40
+ name,
41
+ // The one src/cli.ts prints with --version, so they start equal
42
+ version: template.version,
43
+ description: '',
44
+ type: template.type,
45
+ bin: { [name]: 'dist/main.js' },
46
+ files: template.files,
47
+ scripts: template.scripts,
48
+ dependencies: { '@runa-cmd/core': `^${runaVersion}`, zod: template.dependencies.zod },
49
+ devDependencies: template.devDependencies,
50
+ engines: template.engines,
51
+ };
52
+ return `${JSON.stringify(pkg, null, '\t')}\n`;
53
+ }
54
+ function named(cli, name) {
55
+ const marker = "name: 'greet'";
56
+ if (!cli.includes(marker))
57
+ throw new Error(`The template lost ${marker}: update the generator.`);
58
+ return cli.replace(marker, `name: '${name}'`);
59
+ }
60
+ /** Create the project, or explain why not before writing anything. */
61
+ export function createProject(options) {
62
+ const { name, cwd, templateDir, runaVersion } = options;
63
+ if (!PROJECT_NAME.test(name)) {
64
+ throw new CreateError(`'${name}' can't be the name: use lowercase letters, numbers and dashes, as in 'my-tool'. It's also what people type to run it.`);
65
+ }
66
+ const dir = resolve(cwd, name);
67
+ if (existsSync(dir) && readdirSync(dir).length > 0) {
68
+ throw new CreateError(`The folder '${name}' already has files. Pick another name, or empty it.`);
69
+ }
70
+ mkdirSync(join(dir, 'src'), { recursive: true });
71
+ for (const file of COPIED) {
72
+ const content = readFileSync(join(templateDir, file), 'utf8');
73
+ writeFileSync(join(dir, file), file === 'src/cli.ts' ? named(content, name) : content);
74
+ }
75
+ writeFileSync(join(dir, 'package.json'), packageJson(templateDir, name, runaVersion));
76
+ writeFileSync(join(dir, '.gitignore'), GITIGNORE);
77
+ writeFileSync(join(dir, 'README.md'), readme(name));
78
+ return { dir };
79
+ }
80
+ /** What to run next, with the package manager that ran the generator. */
81
+ export function nextSteps(name, userAgent) {
82
+ const manager = ['pnpm', 'yarn', 'bun'].find((tool) => userAgent?.startsWith(`${tool}/`)) ?? 'npm';
83
+ const build = `${manager === 'npm' ? 'npm run' : manager} build`;
84
+ const test = 'node --test src/cli.test.ts';
85
+ const width = Math.max(build.length, test.length) + 2;
86
+ return `
87
+ Created ${name}. Next:
88
+
89
+ cd ${name}
90
+ ${manager} install
91
+ node src/main.ts hello Ana
92
+
93
+ ${test.padEnd(width)}# Test it
94
+ ${build.padEnd(width)}# Build it before installing or publishing
95
+ `;
96
+ }
97
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @runa-cmd/create — `npm create @runa-cmd my-tool`
4
+ *
5
+ * No dependencies, Runa included: `npm create` downloads this every time, and
6
+ * it should take a moment, not Zod.
7
+ */
8
+ import { readFileSync } from 'node:fs';
9
+ import { dirname, join } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+ import { parseArgs } from 'node:util';
12
+ import { CreateError, createProject, nextSteps } from './create.js';
13
+ const root = join(dirname(fileURLToPath(import.meta.url)), '..');
14
+ const templateDir = join(root, 'template');
15
+ const readJson = (path) => JSON.parse(readFileSync(path, 'utf8'));
16
+ const { version } = readJson(join(root, 'package.json'));
17
+ // The core this was built with, which isn't always this package's version
18
+ const runaVersion = readJson(join(templateDir, 'runa.json')).core;
19
+ const USAGE = `Create a CLI made with Runa.
20
+
21
+ Usage: npm create @runa-cmd <name>
22
+
23
+ <name> The folder, the package and the command, as in my-tool
24
+ -h, --help Show this
25
+ -v, --version Show the version`;
26
+ function main() {
27
+ let parsed;
28
+ try {
29
+ parsed = parseArgs({
30
+ allowPositionals: true,
31
+ options: {
32
+ help: { type: 'boolean', short: 'h' },
33
+ version: { type: 'boolean', short: 'v' },
34
+ },
35
+ });
36
+ }
37
+ catch (error) {
38
+ console.error(`${error.message}\n\n${USAGE}`);
39
+ return 2;
40
+ }
41
+ const { values, positionals } = parsed;
42
+ if (values.help) {
43
+ console.log(USAGE);
44
+ return 0;
45
+ }
46
+ if (values.version) {
47
+ console.log(version);
48
+ return 0;
49
+ }
50
+ const [name, ...extra] = positionals;
51
+ if (!name || extra.length > 0) {
52
+ console.error(USAGE);
53
+ return 2;
54
+ }
55
+ try {
56
+ createProject({ name, cwd: process.cwd(), templateDir, runaVersion });
57
+ }
58
+ catch (error) {
59
+ if (!(error instanceof CreateError))
60
+ throw error;
61
+ console.error(error.message);
62
+ return 2;
63
+ }
64
+ console.log(nextSteps(name, process.env.npm_config_user_agent));
65
+ return 0;
66
+ }
67
+ process.exitCode = main();
68
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEpE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AACjE,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;AAC3C,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;AAC1E,MAAM,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;AACzD,0EAA0E;AAC1E,MAAM,WAAW,GAAW,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;AAE1E,MAAM,KAAK,GAAG;;;;;;mCAMqB,CAAC;AAEpC,SAAS,IAAI;IACZ,IAAI,MAAoC,CAAC;IACzC,IAAI,CAAC;QACJ,MAAM,GAAG,SAAS,CAAC;YAClB,gBAAgB,EAAE,IAAI;YACtB,OAAO,EAAE;gBACR,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;gBACrC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;aACxC;SACD,CAAC,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,GAAI,KAAe,CAAC,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC;QACzD,OAAO,CAAC,CAAC;IACV,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IACvC,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACnB,OAAO,CAAC,CAAC;IACV,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrB,OAAO,CAAC,CAAC;IACV,CAAC;IAED,MAAM,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,GAAG,WAAW,CAAC;IACrC,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACrB,OAAO,CAAC,CAAC;IACV,CAAC;IAED,IAAI,CAAC;QACJ,aAAa,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAAC;IACvE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,CAAC,CAAC,KAAK,YAAY,WAAW,CAAC;YAAE,MAAM,KAAK,CAAC;QACjD,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7B,OAAO,CAAC,CAAC;IACV,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC,CAAC;IAChE,OAAO,CAAC,CAAC;AACV,CAAC;AAED,OAAO,CAAC,QAAQ,GAAG,IAAI,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,51 @@
1
1
  {
2
2
  "name": "@runa-cmd/create",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.2.0-beta.6",
4
+ "description": "Create a CLI made with Runa: npm create @runa-cmd my-tool",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "keywords": [
8
+ "cli",
9
+ "create",
10
+ "scaffold",
11
+ "template",
12
+ "runa"
13
+ ],
14
+ "bin": {
15
+ "create": "./dist/index.js"
16
+ },
17
+ "engines": {
18
+ "node": ">=24.0.0"
19
+ },
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/jrodrigopuca/runa-cmd.git",
26
+ "directory": "packages/create"
27
+ },
28
+ "homepage": "https://github.com/jrodrigopuca/runa-cmd/tree/main/packages/create#readme",
29
+ "bugs": {
30
+ "url": "https://github.com/jrodrigopuca/runa-cmd/issues"
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "template",
35
+ "README.md",
36
+ "CHANGELOG.md"
37
+ ],
38
+ "devDependencies": {
39
+ "@types/node": "^24.19.2",
40
+ "typescript": "^7.0.2",
41
+ "zod": "^4.6.7",
42
+ "@runa-cmd/core": "0.2.0-beta.6"
43
+ },
44
+ "scripts": {
45
+ "build": "tsc && node scripts/copy-template.mjs",
46
+ "typecheck": "tsc --noEmit",
47
+ "test": "vitest run --dir src",
48
+ "lint": "biome check .",
49
+ "clean": "rm -rf dist template"
50
+ }
6
51
  }
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "greet-cli",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "description": "The project of guides/getting-started.md",
6
+ "type": "module",
7
+ "bin": {
8
+ "greet": "dist/main.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "start": "node src/main.ts",
15
+ "test": "node --test src/cli.test.ts",
16
+ "build": "tsc",
17
+ "typecheck": "tsc --noEmit"
18
+ },
19
+ "dependencies": {
20
+ "@runa-cmd/core": "workspace:*",
21
+ "zod": "^4.6.7"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^24.19.2",
25
+ "typescript": "^7.0.2"
26
+ },
27
+ "engines": {
28
+ "node": ">=24.0.0"
29
+ }
30
+ }
@@ -0,0 +1 @@
1
+ {"core":"0.2.0-beta.6"}
@@ -0,0 +1,21 @@
1
+ import assert from 'node:assert/strict';
2
+ import { test } from 'node:test';
3
+ import { testCLI } from '@runa-cmd/core/testing';
4
+ import { cli } from './cli.ts';
5
+
6
+ test('greets someone', async () => {
7
+ const result = await testCLI(cli, ['hello', 'Ana']);
8
+ assert.equal(result.code, 0);
9
+ assert.equal(result.stdout, 'Hello, Ana!\n');
10
+ });
11
+
12
+ test('shouts with --shout', async () => {
13
+ const result = await testCLI(cli, ['hello', 'Ana', '--shout']);
14
+ assert.equal(result.stdout, 'HELLO, ANA!\n');
15
+ });
16
+
17
+ test('needs a name', async () => {
18
+ const result = await testCLI(cli, ['hello']);
19
+ assert.equal(result.code, 2);
20
+ assert.match(result.stderr, /<name>/);
21
+ });
@@ -0,0 +1,17 @@
1
+ import { defineCLI, defineCommand } from '@runa-cmd/core';
2
+ import { z } from '@runa-cmd/core/zod';
3
+
4
+ const hello = defineCommand({
5
+ meta: { description: 'Say hello to someone' },
6
+ args: { name: z.string().describe('Who to greet') },
7
+ options: { shout: z.boolean().default(false).describe('Use capital letters') },
8
+ run: ({ args, options }) => {
9
+ const text = `Hello, ${args.name}!`;
10
+ return options.shout ? text.toUpperCase() : text;
11
+ },
12
+ });
13
+
14
+ export const cli = defineCLI({
15
+ meta: { name: 'greet', version: '1.0.0', description: 'Greet people' },
16
+ commands: { hello },
17
+ });
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { cli } from './cli.ts';
3
+
4
+ await cli.run();
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2024",
4
+ "module": "nodenext",
5
+ "strict": true,
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "rewriteRelativeImportExtensions": true,
9
+ "erasableSyntaxOnly": true,
10
+ "verbatimModuleSyntax": true,
11
+ "skipLibCheck": true,
12
+ "types": ["node"]
13
+ },
14
+ "include": ["src"],
15
+ "exclude": ["src/**/*.test.ts"]
16
+ }