Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
113 changes: 107 additions & 6 deletions .claude/skills/swiftyshell.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@ Before writing any code, follow this decision tree:
→ Use `Swift`
8. Is this a GitHub CLI operation (`gh pr`, `gh repo`, `gh workflow`, `gh api`, `gh copilot`, `gh skill`, ...)?
→ Use `Gh`
9. Does the operation need typed output, structured results, or conditional follow-up?
9. Is this a Docker CLI operation (`docker buildx`, `docker compose`, `docker debug`, `docker mcp`, `docker scout`, ...)?
→ Use `Docker`
10. Does the operation need typed output, structured results, or conditional follow-up?
→ Use the appropriate typed client
10. Are two or more commands chained by pipe?
11. Are two or more commands chained by pipe?
→ Use `.pipe(to:)` to build a `Pipeline`
11. Does the command write output to a file?
12. Does the command write output to a file?
→ Use `.stdout(.file(path:append:))` on the command
12. Is this any other command?
13. Is this any other command?
→ Use `Command`

### API Reference
Expand Down Expand Up @@ -851,6 +853,105 @@ public struct Gh: RunnableCommandFamily {
}
```

#### Docker CLI

```swift
public enum DockerSubcommand: String, Sendable, Equatable, Hashable {
case builder
case buildx
case checkpoint
case compose
case config
case container
case context
case debug
case desktop
case dhi
case image
case initialize
case inspect
case login
case logout
case manifest
case mcp
case model
case network
case node
case offload
case pass
case plugin
case sandbox
case scout
case search
case secret
case service
case stack
case swarm
case system
case trust
case version
case volume
}

public enum DockerBuildProgress: String, Sendable, Equatable, Hashable {
case auto
case plain
case tty
case rawjson
}

public struct Docker: RunnableCommandFamily {
public init(context: ShellContext = .init())
public func subcommand(_ value: DockerSubcommand) -> Self
public func subcommand(_ value: String) -> Self
public func subcommand(_ value: DockerSubcommand, _ nested: String) -> Self
public func subcommand(_ value: String, _ nested: String) -> Self
public func buildx(_ nested: String? = nil) -> Self
public func compose(_ nested: String? = nil) -> Self
public func container(_ nested: String? = nil) -> Self
public func image(_ nested: String? = nil) -> Self
public func initialize() -> Self
public func debug(_ target: String? = nil) -> Self
public func mcp(_ nested: String? = nil) -> Self
public func scout(_ nested: String? = nil) -> Self
public func system(_ nested: String? = nil) -> Self
public func version() -> Self
public func configPath(_ path: String) -> Self
public func context(_ name: String) -> Self
public func host(_ value: String) -> Self
public func logLevel(_ value: String) -> Self
public func debugMode(_ enabled: Bool = true) -> Self
public func tls(_ enabled: Bool = true) -> Self
public func tlsVerify(_ enabled: Bool = true) -> Self
public func platform(_ value: String) -> Self
public func file(_ path: String) -> Self
public func tag(_ name: String) -> Self
public func tags(_ names: [String]) -> Self
public func buildArg(_ value: String) -> Self
public func buildArgs(_ values: [String]) -> Self
public func progress(_ value: DockerBuildProgress) -> Self
public func push(_ enabled: Bool = true) -> Self
public func load(_ enabled: Bool = true) -> Self
public func pull(_ enabled: Bool = true) -> Self
public func name(_ value: String) -> Self
public func removeWhenDone(_ enabled: Bool = true) -> Self
public func detach(_ enabled: Bool = true) -> Self
public func interactive(_ enabled: Bool = true) -> Self
public func tty(_ enabled: Bool = true) -> Self
public func commandString(_ value: String) -> Self
public func shell(_ value: String) -> Self
public func format(_ value: String) -> Self
public func option(_ name: String) -> Self
public func option(_ name: String, _ value: String) -> Self
public func argument(_ value: String) -> Self
public func arguments(_ values: [String]) -> Self
public func positionalArgument(_ value: String) -> Self
public func positionalArguments(_ values: [String]) -> Self
public func command() -> Command
public func run() async throws -> ShellOutput
}
```

#### Archives (Tar / Zip / Unzip)

```swift
Expand Down Expand Up @@ -1520,7 +1621,7 @@ SwiftyShell uses [SwiftPM Package Traits](https://github.com/swiftlang/swift-evo

Declared in `Package.swift`:

- **Per-family** — `Git`, `Brew`, `Grep`, `Fzf`, `Rg`, `Swift`, `Gh`, `Ls`, `Cp`, `Mkdir`, `Chmod`, `Rm`, `Mv`, `Pwd`, `Jq`, `Rsync`, `Tar`, `Zip`, `Unzip`. One trait per family directory; for `Common/`, one trait per file.
- **Per-family** — `Git`, `Brew`, `Grep`, `Fzf`, `Rg`, `Swift`, `Gh`, `Docker`, `Ls`, `Cp`, `Mkdir`, `Chmod`, `Rm`, `Mv`, `Pwd`, `Jq`, `Rsync`, `Tar`, `Zip`, `Unzip`. One trait per family directory; for `Common/`, one trait per file.
- **Umbrellas** — `CommonUtilities` (every `Common/*` family), `All` (every command family).

Consumers select families with `traits:` on `.package(...)`:
Expand All @@ -1533,7 +1634,7 @@ Consumers select families with `traits:` on `.package(...)`:

The contract is enforced by `Scripts/validate-traits.swift` and CI:

1. Every `.swift` file under a gated source directory (`Git/`, `Brew/`, `Grep/`, `Fzf/`, `Rg/`, `Swift/`, `Gh/`, and each file in `Common/`) is wrapped top-to-bottom in `#if <Trait> ... #endif`.
1. Every `.swift` file under a gated source directory (`Git/`, `Brew/`, `Grep/`, `Fzf/`, `Rg/`, `Swift/`, `Gh/`, `Docker/`, and each file in `Common/`) is wrapped top-to-bottom in `#if <Trait> ... #endif`.
2. Every test file targeting a gated family is wrapped the same way. Cross-family tests use combined guards (`#if Git && Grep`).
3. Every family directory (or `Common/*.swift` file) has a matching `.trait(name:)` entry in `Package.swift`.
4. The `All` umbrella's `enabledTraits` transitively enables every per-family trait. The `CommonUtilities` umbrella enables every `Common/*` trait.
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Compute affected traits
id: compute
run: |
FULL='["", "Git", "Brew", "Grep", "Fzf", "Rg", "Swift", "Gh", "Rsync", "Tar", "Zip", "Unzip", "CommonUtilities", "All"]'
FULL='["", "Git", "Brew", "Grep", "Fzf", "Rg", "Swift", "Gh", "Docker", "Rsync", "Tar", "Zip", "Unzip", "CommonUtilities", "All"]'

CHANGED=$(git diff --name-only "origin/${{ github.base_ref }}...HEAD")
echo "Changed files:"
Expand Down Expand Up @@ -55,6 +55,7 @@ jobs:
echo "$CHANGED" | grep -qE '^(Sources/SwiftyShell/Rg/|Tests/SwiftyShellTests/Rg/)' && family_traits+=("Rg")
echo "$CHANGED" | grep -qE '^(Sources/SwiftyShell/Swift/|Tests/SwiftyShellTests/Swift/)' && family_traits+=("Swift")
echo "$CHANGED" | grep -qE '^(Sources/SwiftyShell/Gh/|Tests/SwiftyShellTests/Gh/)' && family_traits+=("Gh")
echo "$CHANGED" | grep -qE '^(Sources/SwiftyShell/Docker/|Tests/SwiftyShellTests/Docker/)' && family_traits+=("Docker")
echo "$CHANGED" | grep -qE '^(Sources/SwiftyShell/Common/|Tests/SwiftyShellTests/Common/)' && family_traits+=("CommonUtilities")
traits+=("${family_traits[@]}")

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/reusable-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ on:
Defaults to the full matrix when omitted.
required: false
type: string
default: '["", "Git", "Brew", "Grep", "Fzf", "Rg", "Swift", "Gh", "Rsync", "Tar", "Zip", "Unzip", "CommonUtilities", "All"]'
default: '["", "Git", "Brew", "Grep", "Fzf", "Rg", "Swift", "Gh", "Docker", "Rsync", "Tar", "Zip", "Unzip", "CommonUtilities", "All"]'

jobs:
validate-traits:
Expand Down
8 changes: 6 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ Typed wrapper for the Swift toolchain: `Swift`, `SwiftSubcommand`, and `SwiftBui

Typed wrapper for the GitHub CLI: `Gh` and `GhSubcommand`.

### `Sources/SwiftyShell/Docker/`

Typed wrapper for the Docker CLI: `Docker`, `DockerSubcommand`, and `DockerBuildProgress`.

### `Sources/SwiftyShell/Common/`

Typed wrappers for frequently used shell utilities: `Ls`, `Cp`, `Mkdir`, `Chmod`, `Rm`, `Mv`, `Pwd`, `Jq`, `JqArgument`, `Rsync`, `Tar`, `TarOperation`, `TarCompression`, `Zip`, `ZipCompressionLevel`, `Unzip`, and `UnzipEntry`. Each follows the same fluent builder conventions as all other command families.
Expand All @@ -48,7 +52,7 @@ DocC documentation catalog. `SwiftyShell.md` is the top-level landing page. Arti

### `Tests/SwiftyShellTests/`

Test suite. Sub-folders mirror the source layout: `Brew/`, `Common/`, `Core/`, `Fzf/`, `Gh/`, `Git/`, `Grep/`, `Pipelines/`, `Rg/`, and `Swift/`. Test files for gated families are wrapped in `#if <Trait>` so the test target compiles under any trait selection. `Common/` has one test file per family (`LsTests.swift`, `CpTests.swift`, …) plus `CommonTestSupport.swift` (shared helpers, ungated).
Test suite. Sub-folders mirror the source layout: `Brew/`, `Common/`, `Core/`, `Docker/`, `Fzf/`, `Gh/`, `Git/`, `Grep/`, `Pipelines/`, `Rg/`, and `Swift/`. Test files for gated families are wrapped in `#if <Trait>` so the test target compiles under any trait selection. `Common/` has one test file per family (`LsTests.swift`, `CpTests.swift`, …) plus `CommonTestSupport.swift` (shared helpers, ungated).

### `Scripts/`

Expand Down Expand Up @@ -169,7 +173,7 @@ SwiftyShell uses [SwiftPM Package Traits](https://github.com/swiftlang/swift-evo

**Trait inventory (declared in `Package.swift`):**

- Per-family: `Git`, `Brew`, `Grep`, `Fzf`, `Rg`, `Swift`, `Gh`, `Ls`, `Cp`, `Mkdir`, `Chmod`, `Rm`, `Mv`, `Pwd`, `Jq`, `Rsync`, `Tar`, `Zip`, `Unzip` (one trait per family directory; for `Common/`, one trait per file).
- Per-family: `Git`, `Brew`, `Grep`, `Fzf`, `Rg`, `Swift`, `Gh`, `Docker`, `Ls`, `Cp`, `Mkdir`, `Chmod`, `Rm`, `Mv`, `Pwd`, `Jq`, `Rsync`, `Tar`, `Zip`, `Unzip` (one trait per family directory; for `Common/`, one trait per file).
- Umbrellas: `CommonUtilities` (all `Common/*`), `All` (every family).

**The wiring contract** — enforced by `Scripts/validate-traits.swift` and CI:
Expand Down
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ let package = Package(
.trait(name: "Rg", description: "Typed wrapper for ripgrep (rg)."),
.trait(name: "Swift", description: "Typed wrapper for the Swift toolchain CLI."),
.trait(name: "Gh", description: "Typed wrapper for the GitHub CLI."),
.trait(name: "Docker", description: "Typed wrapper for the Docker CLI."),
.trait(name: "Ls", description: "Typed wrapper for ls."),
.trait(name: "Cp", description: "Typed wrapper for cp."),
.trait(name: "Mkdir", description: "Typed wrapper for mkdir."),
Expand All @@ -45,7 +46,7 @@ let package = Package(
.trait(
name: "All",
description: "Enables every command family shipped by SwiftyShell.",
enabledTraits: ["Git", "Brew", "Grep", "Fzf", "Rg", "Swift", "Gh", "CommonUtilities"]
enabledTraits: ["Git", "Brew", "Grep", "Fzf", "Rg", "Swift", "Gh", "Docker", "CommonUtilities"]
),
// Default is intentionally empty: consumers opt in to the families they want.
.default(enabledTraits: []),
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ SwiftyShell ships typed wrappers for common tools. Each family is gated behind a
| `Fzf` | `fzf` | `Fzf` | Fuzzy finder options for interactive and filter-mode pipelines |
| `Swift` | `swift` | `Swift` | SwiftPM build, test, run, package commands, traits, compiler flags |
| `Gh` | `gh` | `Gh` | GitHub CLI automation for PRs, repos, workflows, Copilot, skills, API calls |
| `Docker` | `docker` | `Docker` | Docker automation for Buildx, Compose, Debug, MCP, Scout, images, containers |
| `Ls` | `ls` | `Ls` | All flags, recursive, human-readable sizes |
| `Cp` | `cp` | `Cp` | Recursive, force |
| `Mkdir` | `mkdir` | `Mkdir` | Parent directories, permissions |
Expand Down
Loading