{"id":29460570,"url":"https://github.com/3w36zj6/recette","last_synced_at":"2025-12-30T20:06:42.798Z","repository":{"id":304002501,"uuid":"995717488","full_name":"3w36zj6/recette","owner":"3w36zj6","description":"A type-safe, declarative CLI framework for TypeScript","archived":false,"fork":false,"pushed_at":"2025-07-10T17:21:50.000Z","size":162,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-10T21:53:20.531Z","etag":null,"topics":["bun","cli","cli-app","cli-tool","cli-tools","command","command-line","command-line-tool","commands","console","deno","javascript","node","node-js","nodejs","shell","terminal","typescript"],"latest_commit_sha":null,"homepage":"","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/3w36zj6.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yaml","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},"funding":{"github":["3w36zj6"]}},"created_at":"2025-06-03T23:00:22.000Z","updated_at":"2025-07-10T17:21:54.000Z","dependencies_parsed_at":"2025-07-10T21:53:26.524Z","dependency_job_id":"52b001a0-abc8-4041-9498-c57a7f01aedc","html_url":"https://github.com/3w36zj6/recette","commit_stats":null,"previous_names":["3w36zj6/recette"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/3w36zj6/recette","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3w36zj6%2Frecette","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3w36zj6%2Frecette/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3w36zj6%2Frecette/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3w36zj6%2Frecette/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/3w36zj6","download_url":"https://codeload.github.com/3w36zj6/recette/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3w36zj6%2Frecette/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265233573,"owners_count":23731821,"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":["bun","cli","cli-app","cli-tool","cli-tools","command","command-line","command-line-tool","commands","console","deno","javascript","node","node-js","nodejs","shell","terminal","typescript"],"created_at":"2025-07-14T02:08:04.119Z","updated_at":"2025-12-30T20:06:42.784Z","avatar_url":"https://github.com/3w36zj6.png","language":"TypeScript","funding_links":["https://github.com/sponsors/3w36zj6"],"categories":[],"sub_categories":[],"readme":"# recette\n\n[![npm version](https://img.shields.io/npm/v/recette)](https://www.npmjs.com/package/recette?activeTab=versions)\n[![npm downloads](https://img.shields.io/npm/d18m/recette)](https://www.npmjs.com/package/recette)\n[![npm license](https://img.shields.io/npm/l/recette)](https://github.com/3w36zj6/recette/blob/HEAD/LICENSE)\n[![CI](https://github.com/3w36zj6/recette/actions/workflows/ci.yaml/badge.svg?branch=main\u0026event=push)](https://github.com/3w36zj6/recette/actions/workflows/ci.yaml)\n\nA type-safe, declarative CLI framework that lets you define your command-line tools clearly and intuitively.\n\n## Features\n\n- **Declarative and Simple API**\n  - Define commands, arguments, flags, and options in a clear and concise way. The API is designed to be intuitive and easy to read, so you can focus on what your CLI should do, not how to parse arguments. The command \"signature\"—a string that describes the structure of your command (e.g., `copy [src] [dest?] --force|-f`)—makes the command structure explicit and easy to understand.\n- **Type Safety by Design**\n  - All command definitions are fully type-safe. Argument and option types are inferred directly from your command signature, catching mistakes at compile time and providing a smooth developer experience with IDE autocompletion and error checking.\n- **Strong Constraints for Reliability**\n  - The framework enforces strict rules on command definitions, argument names, and flag patterns. This prevents ambiguous or invalid CLI specifications, reducing runtime errors and making your CLI tools more robust and maintainable.\n\n## Installation\n\nTo get started, install recette using your preferred package manager:\n\n```sh\n# npm\nnpm install recette\n\n# Yarn\nyarn add recette\n\n# pnpm\npnpm add recette\n\n# Bun\nbun add recette\n```\n\n## Usage\n\n\u003e [!NOTE]\n\u003e This framework is experimental. The API is subject to frequent changes.\n\nHere is an example of how to develop a CLI tool using this framework:\n\n```ts\n// mycli.ts\n\nimport { Cli } from \"recette\"\n\n// Create a new CLI instance\ntype Variables = {\n  userName?: string\n}\n\nconst cli = new Cli\u003c{ Variables: Variables }\u003e({\n  name: \"mycli\",\n})\n\n// Simple positional argument\ncli.command(\"hello [name]\", (c) =\u003e {\n  const name = c.arg(\"name\") // (name: \"name\") =\u003e string\n  console.log(`Hello, ${name}!`)\n})\n\n// Required and optional positional arguments\ncli.command(\"copy [src] [dest?]\", (c) =\u003e {\n  const src = c.arg(\"src\") // (name: \"src\") =\u003e string\n  const dest = c.arg(\"dest\") // (name: \"dest\") =\u003e string | undefined\n  console.log(`Copy from ${src} to ${dest ?? \"(not specified)\"}`)\n})\n\n// Optional argument and flags\ncli.command(\"list [dir?] --long|-l --all|-a\", (c) =\u003e {\n  const dir = c.arg(\"dir\") // (name: \"dir\") =\u003e string | undefined\n  const isLong = c.flag(\"long\") // (name: \"long\" | \"all\"): boolean\n  const showAll = c.flag(\"all\") // (name: \"long\" | \"all\"): boolean\n  console.log(`List ${dir ?? \"current directory\"} (long=${isLong}, all=${showAll})`)\n})\n\n// Options with values\ncli.command(\"commit --message|-m=\u003cstring\u003e --author=\u003cstring\u003e\", (c) =\u003e {\n  const message = c.option(\"message\") // (name: \"message\" | \"author\"): string | undefined\n  const author = c.option(\"author\") // (name: \"message\" | \"author\"): string | undefined\n  console.log(`Commit: \"${message}\" by ${author}`)\n})\n\n// Variadic arguments\ncli.command(\"remove [...files]\", (c) =\u003e {\n  const files = c.args(\"files\") // (name: \"files\") =\u003e string[]\n  console.log(`Remove files: ${files.join(\", \")}`)\n})\n\n// Accessing stored variables\ncli.command(\"whoami\", (c) =\u003e {\n  const user = c.get(\"userName\") // (key: \"userName\") =\u003e string | undefined\n  if (user) {\n    console.log(`Current user: ${user}`)\n  } else {\n    console.log(\"No user specified.\")\n  }\n})\n\n// Subcommand group\nconst branchCli = new Cli({ name: \"branch\" })\n\nbranchCli.command(\"list --remote\", (c) =\u003e {\n  const remote = c.flag(\"remote\")\n  console.log(`Branch list (remote: ${remote})`)\n})\nbranchCli.command(\"delete [name]\", (c) =\u003e {\n  const name = c.arg(\"name\")\n  console.log(`Delete branch: ${name}`)\n})\n\n// Nested command group\nconst featureCli = new Cli({ name: \"feature\" })\n\nfeatureCli.command(\"start [name]\", (c) =\u003e {\n  const name = c.arg(\"name\")\n  console.log(`Start feature: ${name}`)\n})\n\n// Mounting subcommands\nbranchCli.mount(\"feature\", featureCli)\ncli.mount(\"branch\", branchCli)\n\n// Middleware\ncli.use(\"--verbose|-v\", async (c, next) =\u003e {\n  if (c.flag(\"verbose\")) {\n    console.log(\"[verbose] Command execution started\")\n  }\n  await next()\n  if (c.flag(\"verbose\")) {\n    console.log(\"[verbose] Command execution finished\")\n  }\n})\n\ncli.use(\"--user=\u003cstring\u003e\", async (c, next) =\u003e {\n  const user = c.option(\"user\")\n  if (user) {\n    c.set(\"userName\", user)\n  }\n  await next()\n})\n\n// Run CLI\ncli.run()\n```\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./docs/images/usage.png\" alt=\"Example of CLI usage output\"\u003e\n\u003c/p\u003e\n\nYou can distribute your CLI tool via npmjs, or create a single executable file using Bun or Deno.\nFor example, to create a standalone executable with Bun, run the following command:\n\n```sh\nbun build mycli.ts --compile --outfile mycli\n```\n\nFor more details, see [Single-file executable – Runtime | Bun Docs](https://bun.sh/docs/bundler/executables).\n\n## Contributing\n\n\u003e [!NOTE]\n\u003e This framework is a work in progress. The implementation is verbose, tests are not comprehensive, and many features are missing.\n\u003e\n\u003e If you have any useful feedback or suggestions, feel free to contribute!\n\nThe development toolchain is managed with [mise](https://mise.jdx.dev/). To install it, run:\n\n```sh\nmise install\n```\n\nDependencies are managed with [Bun](https://bun.sh). To install them, run:\n\n```sh\nbun install --frozen-lockfile\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F3w36zj6%2Frecette","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F3w36zj6%2Frecette","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F3w36zj6%2Frecette/lists"}