{"id":15630633,"url":"https://github.com/schniz/cmd-ts","last_synced_at":"2025-05-16T14:05:19.868Z","repository":{"id":37826477,"uuid":"237742072","full_name":"Schniz/cmd-ts","owner":"Schniz","description":"💻 A type-driven command line argument parser","archived":false,"fork":false,"pushed_at":"2025-05-13T11:12:13.000Z","size":989,"stargazers_count":260,"open_issues_count":50,"forks_count":27,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-05-13T12:25:06.772Z","etag":null,"topics":["argument-parser","hacktoberfest","nodejs","parse","typescript"],"latest_commit_sha":null,"homepage":"https://cmd-ts.now.sh","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Schniz.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-02-02T08:39:32.000Z","updated_at":"2025-05-13T00:02:34.000Z","dependencies_parsed_at":"2023-10-14T22:36:56.133Z","dependency_job_id":"c2d79075-dc15-44ec-aa96-df2d794f8ad9","html_url":"https://github.com/Schniz/cmd-ts","commit_stats":{"total_commits":198,"total_committers":17,"mean_commits":"11.647058823529411","dds":0.3787878787878788,"last_synced_commit":"e6759073041f19e07906087ddcc15a416361ec7c"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Schniz%2Fcmd-ts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Schniz%2Fcmd-ts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Schniz%2Fcmd-ts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Schniz%2Fcmd-ts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Schniz","download_url":"https://codeload.github.com/Schniz/cmd-ts/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254544146,"owners_count":22088807,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["argument-parser","hacktoberfest","nodejs","parse","typescript"],"created_at":"2024-10-03T10:34:09.491Z","updated_at":"2025-05-16T14:05:19.847Z","avatar_url":"https://github.com/Schniz.png","language":"TypeScript","readme":"# `cmd-ts`\n\n\u003e 💻 A type-driven command line argument parser, with awesome error reporting 🤤\n\nNot all command line arguments are strings, but for some reason, our CLI parsers force us to use strings everywhere. 🤔 `cmd-ts` is a fully-fledged command line argument parser, influenced by Rust's [`clap`](https://github.com/clap-rs/clap) and [`structopt`](https://github.com/TeXitoi/structopt):\n\n🤩 Awesome autocomplete, awesome safeness\n\n🎭 Decode your own custom types from strings with logic and context-aware error handling\n\n🌲 Nested subcommands, composable API\n\n### Basic usage\n\n```ts\nimport { command, run, string, number, positional, option } from 'cmd-ts';\n\nconst cmd = command({\n  name: 'my-command',\n  description: 'print something to the screen',\n  version: '1.0.0',\n  args: {\n    number: positional({ type: number, displayName: 'num' }),\n    message: option({\n      long: 'greeting',\n      type: string,\n    }),\n  },\n  handler: (args) =\u003e {\n    args.message; // string\n    args.number; // number\n    console.log(args);\n  },\n});\n\nrun(cmd, process.argv.slice(2));\n```\n\n#### `command(arguments)`\n\nCreates a CLI command.\n\n### Decoding custom types from strings\n\nNot all command line arguments are strings. You sometimes want integers, UUIDs, file paths, directories, globs...\n\n\u003e **Note:** this section describes the `ReadStream` type, implemented in `./src/example/test-types.ts`\n\nLet's say we're about to write a `cat` clone. We want to accept a file to read into stdout. A simple example would be something like:\n\n```ts\n// my-app.ts\n\nimport { command, run, positional, string } from 'cmd-ts';\n\nconst app = command({\n  /// name: ...,\n  args: {\n    file: positional({ type: string, displayName: 'file' }),\n  },\n  handler: ({ file }) =\u003e {\n    // read the file to the screen\n    fs.createReadStream(file).pipe(stdout);\n  },\n});\n\n// parse arguments\nrun(app, process.argv.slice(2));\n```\n\nThat works okay. But we can do better. In which ways?\n\n- Error handling is out of the command line argument parser context, and in userland, making things less consistent and pretty.\n- It shows we lack composability and encapsulation — and we miss a way to distribute shared \"command line\" behavior.\n\nWhat if we had a way to get a `Stream` out of the parser, instead of a plain string? This is where `cmd-ts` gets its power from, custom type decoding:\n\n```ts\n// ReadStream.ts\n\nimport { Type } from 'cmd-ts';\nimport fs from 'fs';\n\n// Type\u003cstring, Stream\u003e reads as \"A type from `string` to `Stream`\"\nconst ReadStream: Type\u003cstring, Stream\u003e = {\n  async from(str) {\n    if (!fs.existsSync(str)) {\n      // Here is our error handling!\n      throw new Error('File not found');\n    }\n\n    return fs.createReadStream(str);\n  },\n};\n```\n\nNow we can use (and share) this type and always get a `Stream`, instead of carrying the implementation detail around:\n\n```ts\n// my-app.ts\n\nimport { command, run, positional } from 'cmd-ts';\n\nconst app = command({\n  // name: ...,\n  args: {\n    stream: positional({ type: ReadStream, displayName: 'file' }),\n  },\n  handler: ({ stream }) =\u003e stream.pipe(process.stdout),\n});\n\n// parse arguments\nrun(app, process.argv.slice(2));\n```\n\nEncapsulating runtime behaviour and safe type conversions can help us with awesome user experience:\n\n- We can throw an error when the file is not found\n- We can try to parse the string as a URI and check if the protocol is HTTP, if so - make an HTTP request and return the body stream\n- We can see if the string is `-`, and when it happens, return `process.stdin` like many Unix applications\n\nAnd the best thing about it — everything is encapsulated to an easily tested type definition, which can be easily shared and reused. Take a look at [io-ts-types](https://github.com/gcanti/io-ts-types), for instance, which has types like DateFromISOString, NumberFromString and more, which is something we can totally do.\n\n## Inspiration\n\nThis project was previously called `clio-ts`, because it was based on `io-ts`. This is no longer the case, because I want to reduce the dependency count and mental overhead. I might have a function to migrate types between the two.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fschniz%2Fcmd-ts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fschniz%2Fcmd-ts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fschniz%2Fcmd-ts/lists"}