diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7279f0ff..49e1656d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -192,6 +192,24 @@ jobs: --output-path ".pkl-out/%{name}@%{version}/" \ Sources/ExFigCLI/Resources/Schemas + - name: Generate shell completions + run: | + set -euo pipefail + USAGE_VERSION="2.18.2" + curl --fail -sL "https://github.com/jdx/usage/releases/download/v${USAGE_VERSION}/usage-x86_64-unknown-linux-gnu.tar.gz" -o usage.tar.gz + tar xzf usage.tar.gz + chmod +x usage/bin/usage + mkdir -p completions + usage/bin/usage generate completion bash exfig -f exfig.usage.kdl > completions/exfig.bash + usage/bin/usage generate completion zsh exfig -f exfig.usage.kdl > completions/_exfig + usage/bin/usage generate completion fish exfig -f exfig.usage.kdl > completions/exfig.fish + for f in completions/exfig.bash completions/_exfig completions/exfig.fish; do + if [[ ! -s "$f" ]]; then + echo "::error::Generated completion file is empty: $f" + exit 1 + fi + done + - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: @@ -201,6 +219,9 @@ jobs: artifacts/exfig-macos/exfig-macos.zip artifacts/exfig-linux-x64/exfig-linux-x64.tar.gz .pkl-out/exfig@*/* + completions/exfig.bash + completions/_exfig + completions/exfig.fish update-homebrew: name: Update Homebrew Tap diff --git a/CLAUDE.md b/CLAUDE.md index 842fd39d..34063d8b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -87,6 +87,12 @@ and Flutter projects. ./bin/mise run clean # Clean build artifacts ./bin/mise run clean:all # Clean build + derived data +# Shell Completions & CLI Docs (via Usage spec) +./bin/mise run completions:bash # Generate bash completions +./bin/mise run completions:zsh # Generate zsh completions +./bin/mise run completions:fish # Generate fish completions +./bin/mise run docs:cli-reference # Generate CLI reference docs + # Run CLI .build/debug/exfig --help .build/debug/exfig colors -i exfig.pkl @@ -279,6 +285,8 @@ Changing `load()` return type affects: See `ExFigCLI/CLAUDE.md` (Adding a New Subcommand). +**Important:** When adding/changing CLI flags or subcommands, update `exfig.usage.kdl` (Usage spec) to keep shell completions and docs in sync. When bumping the app version in `ExFigCommand.swift`, also update the `version` field in `exfig.usage.kdl`. + ### Adding a Figma API Endpoint See `FigmaAPI/CLAUDE.md`. diff --git a/exfig.usage.kdl b/exfig.usage.kdl new file mode 100644 index 00000000..99fe6dc1 --- /dev/null +++ b/exfig.usage.kdl @@ -0,0 +1,334 @@ +// Usage specification for ExFig CLI +// https://usage.jdx.dev/spec/ +// +// Generate completions: usage generate completion bash exfig -f exfig.usage.kdl +// Generate docs: usage generate markdown -f exfig.usage.kdl + +name "ExFig" +bin "exfig" +version "v2.8.0" +about "Exports resources from Figma to iOS, Android, Flutter, and Web projects" + +// ============================================================================= +// Global flags +// ============================================================================= + +flag "-v --verbose" help="Show detailed output including debug information" global=#true +flag "-q --quiet" help="Show only errors, suppress progress output" global=#true + +// ============================================================================= +// colors (default subcommand) +// ============================================================================= + +cmd "colors" help="Exports colors from Figma" { + long_help "Exports light and dark color palette from Figma to Xcode / Android Studio project. Requires FIGMA_PERSONAL_TOKEN environment variable." + + flag "-i --input " help="Path to PKL config file. Auto-detects exfig.pkl if not specified." + flag "--cache" help="Enable version tracking cache (skip export if unchanged)" negate="--no-cache" + flag "--force" help="Force export and update cache (ignore cached version)" + flag "--cache-path " help="Custom path to cache file (default: .exfig-cache.json)" + flag "--experimental-granular-cache" help="[EXPERIMENTAL] Enable per-node hash tracking for granular cache invalidation" hide=#true + flag "--max-retries " help="Maximum retry attempts for failed API requests" default="4" + flag "--rate-limit " help="Maximum API requests per minute" default="10" + flag "--timeout " help="Figma API request timeout in seconds (overrides config)" + flag "--report " help="Path to write JSON report" + + arg "[filter]" help="Name of colors to export (e.g., 'background/default' for one, 'background/default, background/secondary' for several, 'background/*' for all from folder)" +} + +// ============================================================================= +// icons +// ============================================================================= + +cmd "icons" help="Exports icons from Figma" { + long_help "Exports icons from Figma to Xcode / Android Studio project. Requires FIGMA_PERSONAL_TOKEN environment variable." + + flag "-i --input " help="Path to PKL config file. Auto-detects exfig.pkl if not specified." + flag "--cache" help="Enable version tracking cache (skip export if unchanged)" negate="--no-cache" + flag "--force" help="Force export and update cache (ignore cached version)" + flag "--cache-path " help="Custom path to cache file (default: .exfig-cache.json)" + flag "--experimental-granular-cache" help="[EXPERIMENTAL] Enable per-node hash tracking for granular cache invalidation" hide=#true + flag "--max-retries " help="Maximum retry attempts for failed API requests" default="4" + flag "--rate-limit " help="Maximum API requests per minute" default="10" + flag "--timeout " help="Figma API request timeout in seconds (overrides config)" + flag "--fail-fast" help="Stop on first error without retrying" + flag "--resume" help="Continue from checkpoint after interruption" + flag "--concurrent-downloads " help="Maximum concurrent CDN downloads" default="20" + flag "--strict-path-validation" help="Exit with error if any Android icon pathData exceeds 32,767 bytes (AAPT limit)" + flag "--report " help="Path to write JSON report" + + arg "[filter]" help="Name of icons to export (e.g., 'ic/24/edit' for one, 'ic/24/edit, ic/16/notification' for several, 'ic/16/*' for all of size 16 pt)" +} + +// ============================================================================= +// images +// ============================================================================= + +cmd "images" help="Exports images from Figma" { + long_help "Exports images from Figma to Xcode / Android Studio project. Requires FIGMA_PERSONAL_TOKEN environment variable." + + flag "-i --input " help="Path to PKL config file. Auto-detects exfig.pkl if not specified." + flag "--cache" help="Enable version tracking cache (skip export if unchanged)" negate="--no-cache" + flag "--force" help="Force export and update cache (ignore cached version)" + flag "--cache-path " help="Custom path to cache file (default: .exfig-cache.json)" + flag "--experimental-granular-cache" help="[EXPERIMENTAL] Enable per-node hash tracking for granular cache invalidation" hide=#true + flag "--max-retries " help="Maximum retry attempts for failed API requests" default="4" + flag "--rate-limit " help="Maximum API requests per minute" default="10" + flag "--timeout " help="Figma API request timeout in seconds (overrides config)" + flag "--fail-fast" help="Stop on first error without retrying" + flag "--resume" help="Continue from checkpoint after interruption" + flag "--concurrent-downloads " help="Maximum concurrent CDN downloads" default="20" + flag "--report " help="Path to write JSON report" + + arg "[filter]" help="Name of images to export (e.g., 'img/login' for one, 'img/onboarding/1, img/onboarding/2' for several, 'img/onboarding/*' for all from group)" +} + +// ============================================================================= +// typography +// ============================================================================= + +cmd "typography" help="Exports typography from Figma" { + alias "text-styles" + long_help "Exports font styles from Figma to Xcode. Requires FIGMA_PERSONAL_TOKEN environment variable." + + flag "-i --input " help="Path to PKL config file. Auto-detects exfig.pkl if not specified." + flag "--cache" help="Enable version tracking cache (skip export if unchanged)" negate="--no-cache" + flag "--force" help="Force export and update cache (ignore cached version)" + flag "--cache-path " help="Custom path to cache file (default: .exfig-cache.json)" + flag "--experimental-granular-cache" help="[EXPERIMENTAL] Enable per-node hash tracking for granular cache invalidation" hide=#true + flag "--max-retries " help="Maximum retry attempts for failed API requests" default="4" + flag "--rate-limit " help="Maximum API requests per minute" default="10" + flag "--timeout " help="Figma API request timeout in seconds (overrides config)" + flag "--report " help="Path to write JSON report" +} + +// ============================================================================= +// init +// ============================================================================= + +cmd "init" help="Generates config file" { + long_help "Generates exfig.pkl config file in the current directory." + + flag "-p --platform " help="Platform: ios, android, flutter, or web" required=#true { + choices "ios" "android" "flutter" "web" + } +} + +// ============================================================================= +// schemas +// ============================================================================= + +cmd "schemas" help="Extracts PKL schemas to local directory" { + long_help "Extracts PKL schema files used for config validation and IDE support. Schemas are extracted to .exfig/schemas/ by default." + + flag "-o --output " help="Output directory for schemas." default=".exfig/schemas" + flag "-f --force" help="Overwrite existing schema files." +} + +// ============================================================================= +// fetch +// ============================================================================= + +cmd "fetch" help="Downloads images from Figma without config file" { + long_help "Downloads images from a specific Figma frame to a local directory. All parameters are passed via command-line arguments. Requires FIGMA_PERSONAL_TOKEN environment variable." + + flag "-f --file-id " help="Figma file ID (from the URL: figma.com/file//...)" required=#true + flag "-r --frame " help="Name of the Figma frame containing images" required=#true + flag "-p --page " help="Filter by Figma page name." + flag "-o --output " help="Output directory for downloaded images" required=#true + flag "--format " help="Image format" default="png" { + choices "png" "svg" "jpg" "pdf" "webp" + } + flag "--scale " help="Scale factor (0.01-4.0). Default: 3 for PNG, ignored for vector formats" + flag "--filter " help="Filter pattern (e.g., 'icon/*' or 'logo, banner')" + flag "--name-style