Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
01c2f47
feat: generate SDK for TypeScript, Python and Rust
gaojunran May 3, 2026
57596ce
fix: misc
gaojunran May 3, 2026
de299aa
fix: misc
gaojunran May 3, 2026
9f90083
fix: misc
gaojunran May 3, 2026
218b88d
Merge branch 'jdx:main' into main
gaojunran May 3, 2026
65bde86
Merge branch 'jdx:main' into main
gaojunran May 4, 2026
669ef4b
feat(sdk): add Python and TypeScript SDK with bug fixes and comprehen…
gaojunran May 4, 2026
d6bb256
test(sdk): add missing Python and TypeScript test coverage
gaojunran May 4, 2026
44a8480
refactor(sdk): remove Rust SDK generation code and snapshots
gaojunran May 4, 2026
0b0243a
fix(sdk): address P1/P2 issues across Python and TypeScript SDKs
gaojunran May 4, 2026
5b958fc
feat(sdk): make TypeScript SDK async with Promise-based API
gaojunran May 4, 2026
f389d6a
fix(sdk): escape string defaults, export CliError, remove dead flags …
gaojunran May 4, 2026
1687acf
fix(sdk): escape all spec-derived strings in generated code
gaojunran May 4, 2026
4c3f3cc
fix(sdk): Python exec docstring multiline style and docs sentence fra…
gaojunran May 4, 2026
dd222a2
fix(sdk): remove redundant Flags dataclass for global-only subcommands
gaojunran May 4, 2026
9747ab7
refactor(lib): remove sdk feature and enable by default
May 17, 2026
79a9828
style(python): remove unused Union import and clarify var-flag defaul…
May 17, 2026
f88897a
test(python): update snapshots after Union import removal
May 17, 2026
37b2a51
fix(sdk): guard internal member names in TS sanitize_ident and saniti…
May 17, 2026
48bf65c
style: apply formatting after render
May 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 49 additions & 0 deletions cli/assets/fig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,55 @@ const completionSpec: Fig.Spec = {
},
],
},
{
name: "sdk",
description: "Generate a type-safe SDK from a usage spec",
options: [
{
name: ["-f", "--file"],
description: "A usage spec taken in as a file",
isRepeatable: false,
args: {
name: "file",
template: "filepaths",
},
},
{
name: ["-l", "--language"],
description: "Target language for the SDK",
isRepeatable: false,
args: {
name: "language",
suggestions: ["typescript", "python"],
},
},
{
name: ["-o", "--output"],
description: "Output directory for generated SDK files",
isRepeatable: false,
args: {
name: "output",
},
},
{
name: ["-p", "--package-name"],
description:
"Override the package/module name (defaults to spec bin name)",
isRepeatable: false,
args: {
name: "package_name",
},
},
{
name: "--spec",
description: "Raw string spec input",
isRepeatable: false,
args: {
name: "spec",
},
},
],
},
],
},
{
Expand Down
25 changes: 25 additions & 0 deletions cli/assets/usage.1
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ Generate markdown documentation from usage specs
\fIAliases: \fRmd
.RE
.TP
\fBgenerate sdk\fR
Generate a type\-safe SDK from a usage spec
.TP
\fBlint\fR
Lint a usage spec file for common issues
.TP
Expand Down Expand Up @@ -310,6 +313,28 @@ Replace `<pre>` tags with markdown code fences
.TP
\fB\-\-url\-prefix\fR \fI<URL_PREFIX>\fR
Prefix to add to all URLs
.SH "USAGE GENERATE SDK"
Generate a type\-safe SDK from a usage spec
.PP
\fBUsage:\fR usage generate sdk [OPTIONS]
.PP
\fBOptions:\fR
.PP
.TP
\fB\-f, \-\-file\fR \fI<FILE>\fR
A usage spec taken in as a file
.TP
\fB\-l, \-\-language\fR \fI<LANGUAGE>\fR
Target language for the SDK
.TP
\fB\-o, \-\-output\fR \fI<OUTPUT>\fR
Output directory for generated SDK files
.TP
\fB\-p, \-\-package\-name\fR \fI<PACKAGE_NAME>\fR
Override the package/module name (defaults to spec bin name)
.TP
\fB\-\-spec\fR \fI<SPEC>\fR
Raw string spec input
.SH "USAGE LINT"
Lint a usage spec file for common issues
.PP
Expand Down
3 changes: 3 additions & 0 deletions cli/src/cli/generate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod fig;
mod json;
mod manpage;
mod markdown;
mod sdk;

/// Generate completions, documentation, and other artifacts from usage specs
#[derive(clap::Args)]
Expand All @@ -27,6 +28,7 @@ pub enum Command {
Json(json::Json),
Manpage(manpage::Manpage),
Markdown(markdown::Markdown),
Sdk(sdk::Sdk),
}

impl Generate {
Expand All @@ -38,6 +40,7 @@ impl Generate {
Command::Json(cmd) => cmd.run(),
Command::Manpage(cmd) => cmd.run(),
Command::Markdown(cmd) => cmd.run(),
Command::Sdk(cmd) => cmd.run(),
}
}
}
Expand Down
66 changes: 66 additions & 0 deletions cli/src/cli/generate/sdk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use std::path::PathBuf;

use clap::Args;

use crate::cli::generate;

use usage::sdk::{SdkLanguage, SdkOptions};

#[derive(Args)]
#[clap(about = "Generate a type-safe SDK from a usage spec")]
pub struct Sdk {
/// A usage spec taken in as a file
#[clap(short, long)]
file: Option<PathBuf>,

/// Target language for the SDK
#[clap(short, long, value_parser = ["typescript", "python"])]
language: String,

/// Output directory for generated SDK files
#[clap(short, long)]
output: PathBuf,

/// Override the package/module name (defaults to spec bin name)
#[clap(short, long)]
package_name: Option<String>,

/// Raw string spec input
#[clap(long, required_unless_present = "file", overrides_with = "file")]
spec: Option<String>,
}

impl Sdk {
pub fn run(&self) -> miette::Result<()> {
let spec = generate::file_or_spec(&self.file, &self.spec)?;

let language = match self.language.as_str() {
"typescript" => SdkLanguage::TypeScript,
"python" => SdkLanguage::Python,
other => {
return Err(miette::miette!("unsupported language: {other}"));
}
};

let source_file = self.file.as_ref().map(|p| p.display().to_string());

let opts = SdkOptions {
language,
package_name: self.package_name.clone(),
source_file,
};

let output = usage::sdk::generate(&spec, &opts);

std::fs::create_dir_all(&self.output)
.map_err(|e| miette::miette!("failed to create output directory: {e}"))?;

for file in &output.files {
let path = self.output.join(&file.path);
println!("writing to {}", path.display());
xx::file::write(&path, &file.content)?;
}

Ok(())
}
}
19 changes: 19 additions & 0 deletions cli/usage.usage.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,25 @@ cmd generate subcommand_required=#true help="Generate completions, documentation
arg <URL_PREFIX>
}
}
cmd sdk help="Generate a type-safe SDK from a usage spec" {
flag "-f --file" help="A usage spec taken in as a file" {
arg <FILE>
}
flag "-l --language" help="Target language for the SDK" required=#true {
arg <LANGUAGE> {
choices typescript python
}
}
flag "-o --output" help="Output directory for generated SDK files" required=#true {
arg <OUTPUT>
}
flag "-p --package-name" help="Override the package/module name (defaults to spec bin name)" {
arg <PACKAGE_NAME>
}
flag --spec help="Raw string spec input" {
arg <SPEC>
}
}
}
cmd lint help="Lint a usage spec file for common issues" {
flag "-f --format" help="Output format" default=text {
Expand Down
1 change: 1 addition & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export default defineConfig({
{ text: "Completions", link: "/cli/completions" },
{ text: "Manpages", link: "/cli/manpages" },
{ text: "Markdown", link: "/cli/markdown" },
{ text: "SDK Generation", link: "/cli/sdk" },
{ text: "Scripts", link: "/cli/scripts" },
{
text: "CLI Reference", link: "/cli/reference/", items:
Expand Down
105 changes: 105 additions & 0 deletions docs/cli/reference/commands.json
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,111 @@
"aliases": ["md"],
"hidden_aliases": [],
"examples": []
},
"sdk": {
"full_cmd": ["generate", "sdk"],
"usage": "generate sdk <FLAGS>",
"subcommands": {},
"args": [],
"flags": [
{
"name": "file",
"usage": "-f --file <FILE>",
"help": "A usage spec taken in as a file",
"help_first_line": "A usage spec taken in as a file",
"short": ["f"],
"long": ["file"],
"hide": false,
"global": false,
"arg": {
"name": "FILE",
"usage": "<FILE>",
"required": true,
"double_dash": "Optional",
"hide": false
}
},
{
"name": "language",
"usage": "-l --language <LANGUAGE>",
"help": "Target language for the SDK",
"help_first_line": "Target language for the SDK",
"short": ["l"],
"long": ["language"],
"required": true,
"hide": false,
"global": false,
"arg": {
"name": "LANGUAGE",
"usage": "<LANGUAGE>",
"required": true,
"double_dash": "Optional",
"hide": false,
"choices": {
"choices": ["typescript", "python"]
}
}
},
{
"name": "output",
"usage": "-o --output <OUTPUT>",
"help": "Output directory for generated SDK files",
"help_first_line": "Output directory for generated SDK files",
"short": ["o"],
"long": ["output"],
"required": true,
"hide": false,
"global": false,
"arg": {
"name": "OUTPUT",
"usage": "<OUTPUT>",
"required": true,
"double_dash": "Optional",
"hide": false
}
},
{
"name": "package-name",
"usage": "-p --package-name <PACKAGE_NAME>",
"help": "Override the package/module name (defaults to spec bin name)",
"help_first_line": "Override the package/module name (defaults to spec bin name)",
"short": ["p"],
"long": ["package-name"],
"hide": false,
"global": false,
"arg": {
"name": "PACKAGE_NAME",
"usage": "<PACKAGE_NAME>",
"required": true,
"double_dash": "Optional",
"hide": false
}
},
{
"name": "spec",
"usage": "--spec <SPEC>",
"help": "Raw string spec input",
"help_first_line": "Raw string spec input",
"short": [],
"long": ["spec"],
"hide": false,
"global": false,
"arg": {
"name": "SPEC",
"usage": "<SPEC>",
"required": true,
"double_dash": "Optional",
"hide": false
}
}
],
"mounts": [],
"hide": false,
"help": "Generate a type-safe SDK from a usage spec",
"name": "sdk",
"aliases": [],
"hidden_aliases": [],
"examples": []
}
},
"args": [],
Expand Down
1 change: 1 addition & 0 deletions docs/cli/reference/generate.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ Generate completions, documentation, and other artifacts from usage specs
- [`usage generate json [-f --file <FILE>] [--spec <SPEC>]`](/cli/reference/generate/json.md)
- [`usage generate manpage <FLAGS>`](/cli/reference/generate/manpage.md)
- [`usage generate markdown <FLAGS>`](/cli/reference/generate/markdown.md)
- [`usage generate sdk <FLAGS>`](/cli/reference/generate/sdk.md)
35 changes: 35 additions & 0 deletions docs/cli/reference/generate/sdk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!-- @generated by usage-cli from usage spec -->

# `usage generate sdk`

- **Usage**: `usage generate sdk <FLAGS>`
- **Source code**: [`cli/src/cli/generate/sdk.rs`](https://github.com/jdx/usage/blob/main/cli/src/cli/generate/sdk.rs)

Generate a type-safe SDK from a usage spec

## Flags

### `-f --file <FILE>`

A usage spec taken in as a file

### `-l --language <LANGUAGE>`

Target language for the SDK

**Choices:**

- `typescript`
- `python`

### `-o --output <OUTPUT>`

Output directory for generated SDK files

### `-p --package-name <PACKAGE_NAME>`

Override the package/module name (defaults to spec bin name)

### `--spec <SPEC>`

Raw string spec input
1 change: 1 addition & 0 deletions docs/cli/reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Outputs a `usage.kdl` spec for this CLI itself
- [`usage generate json [-f --file <FILE>] [--spec <SPEC>]`](/cli/reference/generate/json.md)
- [`usage generate manpage <FLAGS>`](/cli/reference/generate/manpage.md)
- [`usage generate markdown <FLAGS>`](/cli/reference/generate/markdown.md)
- [`usage generate sdk <FLAGS>`](/cli/reference/generate/sdk.md)
- [`usage lint [-f --format <FORMAT>] [-W --warnings-as-errors] <FILE>`](/cli/reference/lint.md)
- [`usage powershell [-h] [--help] <SCRIPT> [ARGS]…`](/cli/reference/powershell.md)
- [`usage zsh [-h] [--help] <SCRIPT> [ARGS]…`](/cli/reference/zsh.md)
Loading