Repository: smithy-lang/smithy
Pull request #2139 (2024-02-14 20:38:39)

Update quickstart example with new gradle plugin and cli
#### Background
Updates the Quickstart examples to: 
1. Use the updated Gradle plugin
2. Include examples of building with CLI as well as Gradle plugin
3. Add captions to files
4. Include instructions to clone quickstart with `init` command.

#### Testing
Built locally with `make html` 

[Download Zip archive of doc artifacts](https://github.com/smithy-lang/smithy/actions/runs/7921805367/artifacts/1249248736)

---
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 79eb4ba8e..59ab6f48c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,25 @@
 # Smithy Changelog
 
+## 1.45.0 (2024-02-14)
+
+### Features
+
+* Added new option to CLI to configure the format (`text` or `csv`) of validation output. ([#2133](https://github.com/smithy-lang/smithy/pull/2133))
+* Added options to CLI to hide or show validation events for specified validators. ([#2127](https://github.com/smithy-lang/smithy/pull/2127))
+* Added protocol tests for verifying serialization/deserialization behavior for maps with document values. ([#2125](https://github.com/smithy-lang/smithy/pull/2125))
+* Changed `UnreferencedShape` to be an opt-in linter instead of on-by-default to reduce friction when defining common shapes that are not connected to a service shape. Added a validator that allows you to configure what shape to check connectedness (defaults to service shape). ([#2119](https://github.com/smithy-lang/smithy/pull/2119))
+
+### Bug Fixes
+
+* Fixed headers when printing validation event output in csv format. ([#2136](https://github.com/smithy-lang/smithy/pull/2136))
+* Fixed `RestJsonZeroAndFalseQueryValues` protocol test to correctly include a params value for servers. ([#2132](https://github.com/smithy-lang/smithy/pull/2132))
+* Fixed regression in model validation that incorrectly allowed suppression of ERROR events. ([#2130](https://github.com/smithy-lang/smithy/pull/2130))
+
+### Documentation
+
+* Fixed typo in @pattern validation example. ([#2126](https://github.com/smithy-lang/smithy/pull/2126))
+
+
 ## 1.44.0 (2024-01-25)
 
 ### Features
diff --git a/VERSION b/VERSION
index 372cf402c..50aceaa7b 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-1.44.0
+1.45.0
diff --git a/docs/source-2.0/guides/gradle-plugin.rst b/docs/source-2.0/guides/gradle-plugin.rst
deleted file mode 100644
index 6a8fa7fa6..000000000
--- a/docs/source-2.0/guides/gradle-plugin.rst
+++ /dev/null
@@ -1,345 +0,0 @@
-.. _smithy-gradle-plugin:
-
-====================
-Smithy Gradle Plugin
-====================
-
-The `Smithy Gradle plugin`_ integrates Smithy with `Gradle`_. This plugin can
-build artifacts from Smithy models, generate JARs that contain Smithy models
-found in Java projects, and generate JARs that contain filtered :ref:`projections <projections>`
-of Smithy models.
-
-
-.. _plugin-install:
-
-Installation
-============
-
-The Smithy Gradle plugin is applied using the ``software.amazon.smithy`` plugin.
-The following example configures a project to use the Smithy Gradle plugin:
-
-.. code-block:: kotlin
-
-    plugins {
-        id("software.amazon.smithy").version("0.6.0")
-    }
-
-
-.. _smithy-model-sources:
-
-Smithy model sources
-====================
-
-When a JAR is generated for a project, any Smithy models found in the
-following directories will be added to the JAR:
-
-* ``model/``
-* ``src/main/smithy``
-* ``src/main/resources/META-INF/smithy``
-
-Models found in these directories are combined into a flattened directory
-structure and used to validate and build the Smithy model. A Smithy manifest
-file is automatically created for the detected models, and it along with the
-model files, are placed in the ``META-INF/smithy/`` resource of the created
-JAR. Any project that then depends on this created JAR will be able to find
-and use the Smithy models contained in the JAR when using *model discovery*.
-
-
-.. _building-smithy-models:
-
-Building Smithy models
-======================
-
-This plugin operates in two different modes:
-
-1. If no ``projection`` is specified for the ``SmithyExtension``, then the plugin
-   runs a "source" build using the "source" projection.
-2. If a ``projection`` is specified for the ``SmithyExtension``, then the plugin
-   runs in a "projection" mode.
-
-
-.. _smithy-extension-properties:
-
-Smithy extension properties
-===========================
-
-This plugin is configured using the ``software.amazon.smithy.gradle.SmithyExtension``
-extension:
-
-.. code-block:: kotlin
-
-    configure<software.amazon.smithy.gradle.SmithyExtension> {
-        projection = "foo"
-    }
-
-
-This extension supports the following properties:
-
-.. list-table::
-    :header-rows: 1
-    :widths: 10 20 70
-
-    * - Property
-      - Type
-      - Description
-    * - projection
-      - ``String``
-      - Sets the :ref:`projection <projections>` name to build. There must
-        be a corresponding projection definition in the :ref:`smithy-build.json <smithy-build-json>`
-        file in the project.
-    * - projectionSourceTags
-      - ``Set<String>``
-      - Get the tags that are searched for in classpaths when determining
-        which models are projected into the created JAR. This plugin will look
-        through the JARs in the buildscript classpath to see if they contain a
-        META-INF/MANIFEST.MF attribute named "Smithy-Tags" that matches any of
-        the given projection source tags. The Smithy models found in each
-        matching JAR are copied into the JAR being projected. This allows a
-        projection JAR to aggregate models into a single JAR.
-    * - tags
-      - ``Set<String>``
-      - Set the tags that are added to the JAR. These tags are placed in the
-        META-INF/MANIFEST.MF attribute named "Smithy-Tags" as a comma
-        separated list. JARs with Smithy-Tags can be queried when building
-        projections so that the Smithy models found in each matching JAR are
-        placed into the projection JAR.
-    * - smithyBuildConfigs
-      - ``FileCollection``
-      - Sets a custom collection of smithy-build.json files to use when
-        building the model.
-    * - allowUnknownTraits
-      - ``boolean``
-      - Sets whether or not unknown traits in the model should be ignored. By
-        default, the build will fail if unknown traits are encountered.
-    * - outputDirectory
-      - ``File``
-      - Defines where Smithy build artifacts are written.
-
-
-.. _building-source-model:
-
-Building a source model
------------------------
-
-A "source" build is run when no ``projection`` is configured in
-``SmithyExtension``. Because no projection was specified, **smithy-build** is
-executed using the ``compileClasspath`` plus the ``buildscript`` classpath. To
-prevent accidentally relying on Smithy models that are only available to
-build scripts, Smithy models are discovered using only the
-``compileClasspath`` and ``runtimeClasspath``.
-
-The following example ``build.gradle.kts`` will build a Smithy model using a
-"source" build:
-
-.. code-block:: kotlin
-
-    plugins {
-        id("software.amazon.smithy").version("0.6.0")
-    }
-
-    // The SmithyExtension is used to customize the build. This example
-    // doesn't set any values and can be completely omitted.
-    configure<software.amazon.smithy.gradle.SmithyExtension> {}
-
-    repositories {
-        mavenLocal()
-        mavenCentral()
-    }
-
-    dependencies {
-        implementation("software.amazon.smithy:smithy-model:__smithy_version__")
-
-        // These are just examples of dependencies. This model has a dependency on
-        // a "common" model package and uses the external AWS traits.
-        implementation("com.foo.baz:foo-model-internal-common:1.0.0")
-        implementation("software.amazon.smithy:smithy-aws-traits:__smithy_version__")
-    }
-
-
-.. _generating-projection:
-
-Generating a projection
------------------------
-
-A "projection" build is run when a :ref:`projection <projections>` is
-specified in the ``SmithyExtension``. You might want to create a projection of
-a model if you need to maintain an internal version of a model that contains
-more information and features than an external version of a model published to
-your customers.
-
-A "projection" build is executed using only the ``buildscript`` classpath, and
-Smithy models are discovered using only the ``buildscript`` classpath. This
-prevents models discovered in the original model from appearing in the
-projected version of the model.
-
-The following example ``build.gradle.kts`` file will run a "projection"
-build that uses the "external" projection.
-
-.. code-block:: kotlin
-
-    plugins {
-        id("software.amazon.smithy").version("0.6.0")
-    }
-
-    buildscript {
-        repositories {
-            mavenLocal()
-            mavenCentral()
-        }
-        dependencies {
-            classpath("software.amazon.smithy:smithy-aws-traits:__smithy_version__")
-
-            // Take a dependency on the internal model package. This
-            // dependency *must* be a buildscript only dependency to ensure
-            // that is does not appear in the generated JAR.
-            classpath("com.foo.baz:foo-internal-model:1.0.0")
-        }
-    }
-
-    // Use the "external" projection. This projection must be found in the
-    // smithy-build.json file. This also ensures that models found in the
-    // foo-internal-package that weren't filtered out are copied into the
-    // projection created by this package.
-    configure<software.amazon.smithy.gradle.SmithyExtension> {
-        projection = "external"
-        projectionSourceTags = setOf("com.foo.baz:foo-internal-model")
-    }
-
-    repositories {
-        mavenLocal()
-        mavenCentral()
-    }
-
-    dependencies {
-        implementation("software.amazon.smithy:smithy-model:__smithy_version__")
-
-        // Any dependencies that the projected model needs must be (re)declared
-        // here. For example, let's assume that the smithy-aws-traits package is
-        // needed in the projected model too.
-        implementation("software.amazon.smithy:smithy-aws-traits:__smithy_version__")
-    }
-
-
-Because the ``projection`` of the ``SmithyExtension`` was set to ``external``, a
-``smithy-build.json`` file **must** be found that defines the ``external``
-projection. For example:
-
-.. code-block:: json
-
-    {
-        "version": "1.0",
-        "projections": {
-            "external": {
-                "transforms": [
-                    {
-                        "name": "excludeShapesByTag",
-                        "args": {
-                            "tags": ["internal"]
-                        }
-                    },
-                    {
-                        "name": "excludeShapesByTrait",
-                        "args": {
-                            "traits": ["internal"]
-                        }
-                    },
-                    {
-                        "name": "excludeMetadata",
-                        "args": {
-                            "keys": ["suppressions", "validators"]
-                        }
-                    },
-                    {
-                        "name": "removeUnusedShapes"
-                    }
-                ]
-            }
-        }
-    }
-
-
-.. _projection-tags:
-
-Projection tags
----------------
-
-Projections are meant to *project* and filter other models into another
-model. You need to specify which dependencies are being projected into your
-JAR by setting the ``projectionSourceTags`` property.
-
-.. code-block:: kotlin
-
-    configure<software.amazon.smithy.gradle.SmithyExtension> {
-            projection = "external"
-            projectionSourceTags = setOf("Foo", "com.baz:bar")
-    }
-
-
-Tags are used to logically group packages to make it easier to build
-projections. The ``tags`` property is used to add ``Smithy-Tags`` to a JAR.
-
-.. code-block:: kotlin
-
-    configure<software.amazon.smithy.gradle.SmithyExtension> {
-        tags = setOf("X", "foobaz-model")
-    }
-
-
-For example, if your service is made up of 10 packages, you can add the
-"foobaz-model" Smithy tag to each package so that the only value that needs
-to be provided for ``tags`` to correctly project your model is "foobaz-model".
-
-When building a model package, this plugin will automatically add the group
-name of the package being built, the group name + ":" + name of the package,
-and group name + ":" + name + ":" version. This allows models to always
-be queried by group and artifact names in addition to custom tags.
-
-
-.. _artifacts-from-smithy-models:
-
-Building artifacts from Smithy models
--------------------------------------
-
-If a ``smithy-build.json`` file is found at the root of the project, then it
-will be used to generate :ref:`artifacts <projection-artifacts>` from the Smithy model.
-
-The following example generates an OpenAPI model from a Smithy model:
-
-.. code-block:: json
-
-    {
-        "version": "1.0",
-        "plugins": {
-            "openapi": {
-                "service": "foo.baz#MyService"
-            }
-        }
-    }
-
-
-The above Smithy plugin also requires a ``buildscript`` dependency in
-``build.gradle.kts``:
-
-.. code-block:: kotlin
-
-    buildscript {
-        // ...
-        dependencies {
-            // ...
-
-            // This dependency is required in order to apply the "openapi"
-            // plugin in smithy-build.json
-            classpath("software.amazon.smithy:smithy-openapi:__smithy_version__")
-        }
-    }
-
-Complete Examples
-=================
-
-For several complete examples, see the `examples directory`_ of the Smithy
-Gradle plugin repository, or check out the :doc:`Quick start guide </quickstart>` for a tutorial on
-creating a Smithy model and building it with the Smithy Gradle plugin.
-
-.. _examples directory: https://github.com/awslabs/smithy-gradle-plugin/tree/main/examples
-.. _Smithy Gradle plugin: https://github.com/awslabs/smithy-gradle-plugin/
-.. _Gradle: https://gradle.org/
diff --git a/docs/source-2.0/guides/gradle-plugin/gradle-migration-guide.rst b/docs/source-2.0/guides/gradle-plugin/gradle-migration-guide.rst
new file mode 100644
index 000000000..293d7c6f4
--- /dev/null
+++ b/docs/source-2.0/guides/gradle-plugin/gradle-migration-guide.rst
@@ -0,0 +1,186 @@
+.. _gradle_migration_guide:
+
+==========================================
+Migrating to Gradle plugin version 0.10.0+
+==========================================
+
+This guide describes how to migrate your Gradle build files
+to use the `Smithy Gradle plugins`_ v0.10.0+ without breaking
+your existing Gradle projects.
+
+.. warning::
+    Versions 0.10.0+ of the Smithy Gradle plugins will automatically apply
+    the Smithy formatter to your Smithy source files. This can result in a
+    large number of formatting changes the first time you apply these plugins
+    to your project. For instructions on how to disable automatic formatting
+    see :ref:`Disabling the Smithy formatter <disable-smithy-formatter>`.
+
+Update plugin artifact
+======================
+
+The Smithy Gradle plugin has been broken up into separate capability
+(``smithy-jar``) and convention (``smithy-base``) plugins. The
+``smithy-jar`` plugin operates similarly to the previous ``smithy`` plugin,
+but no longer applies the ``java`` plugin automatically. To migrate to the
+new plugin, change the artifact id in the ``plugins`` block and apply the
+``java`` (or ``java-library``) plugin if it is not already applied to
+your project.
+
+.. tab:: Kotlin
+
+    .. code-block:: diff
+        :caption: build.gradle.kts
+
+        plugins {
+        -     id("software.amazon.smithy").version("0.7.0")
+        +     `java`
+        +     id("software.amazon.smithy.gradle.smithy-jar").version("0.10.0")
+        }
+
+.. tab:: Groovy
+
+    .. code-block:: diff
+        :caption: build.gradle
+
+        plugins {
+        -      id 'software.amazon.smithy' version '0.7.0'
+        +      id 'java'
+        +      id 'software.amazon.smithy.gradle.smithy-jar' version '0.10.0'
+        }
+
+Remove Buildscript Dependencies
+===============================
+
+Pre-0.10.0 versions of the Smithy Gradle plugins use the buildscript block
+for build-only dependencies such as Smithy build plugins. The 0.10.0+
+Gradle plugins create a new Gradle `Configuration`_ ``smithyBuild``
+for these build-only dependencies.
+
+.. tab:: Kotlin
+
+    .. code-block:: diff
+        :caption: build.gradle.kts
+
+        -buildscript {
+        -       repositories {
+        -           mavenLocal()
+        -           mavenCentral()
+        -       }
+        -
+        -       dependencies {
+        -           // Dependency repeated here and below because model discovery previously
+        -           // only ran on buildscript classpath for projections
+        -           classpath("software.amazon.smithy:smithy-aws-traits:__smithy_version__")
+        -           // Take a dependency on the internal model package. This
+        -           // dependency *must* be a buildscript only dependency to ensure
+        -           // that is does not appear in the generated JAR.
+        -           classpath("com.foo.baz:foo-internal-model:1.0.0")
+        -       }
+        -}
+
+        dependencies {
+            implementation("software.amazon.smithy:smithy-model:__smithy_version__")
+
+            implementation("software.amazon.smithy:smithy-aws-traits:__smithy_version__")
+        +   smithyBuild("com.foo.baz:foo-internal-model:1.0.0")
+        }
+
+.. tab:: Groovy
+
+    .. code-block:: diff
+        :caption: build.gradle
+
+        -buildscript {
+        -       repositories {
+        -           mavenLocal()
+        -           mavenCentral()
+        -       }
+        -
+        -       dependencies {
+        -           // Dependency repeated here and below because model discovery previously
+        -           // only ran on buildscript classpath for projections
+        -           classpath 'software.amazon.smithy:smithy-aws-traits:__smithy_version__'
+        -           // Take a dependency on the internal model package. This
+        -           // dependency *must* be a buildscript only dependency to ensure
+        -           // that is does not appear in the generated JAR.
+        -           classpath 'com.foo.baz:foo-internal-model:1.0.0'
+        -       }
+        -}
+
+        dependencies {
+            implementation 'software.amazon.smithy:smithy-model:__smithy_version__'
+
+            implementation 'software.amazon.smithy:smithy-aws-traits:__smithy_version__'
+        +   smithyBuild 'com.foo.baz:foo-internal-model:1.0.0'
+        }
+
+Change ``projection`` property name
+===================================
+
+The property ``projection`` has also been updated to ``sourceProjection``.
+
+.. tab:: Kotlin
+
+        .. code-block:: diff
+            :caption: build.gradle.kts
+
+            -configure<software.amazon.smithy.gradle.SmithyExtension> {
+            +smithy {
+            -    projection = "foo"
+            +    sourceProjection.set("foo")
+            }
+
+.. tab:: Groovy
+
+        .. code-block:: diff
+            :caption: build.gradle
+
+            -configure<software.amazon.smithy.gradle.SmithyExtension> {
+            +smithy {
+            -    projection = "foo"
+            +    sourceProjection = "foo"
+            }
+
+Change ``smithyBuildJar`` task name
+===================================
+
+The ``smithyBuildJar`` task has been removed. Instead, the plugin now
+executes separate ``smithyBuild`` and ``smithyJarStaging`` tasks. If
+your project previously configured the ``smithyBuildJar``,
+configure the ``smithyBuild`` task instead. Tasks that depended on
+``smithyBuildJar`` should now depend on the ``jar`` task.
+
+.. tab:: Kotlin
+
+        .. code-block:: diff
+            :caption: build.gradle.kts
+
+            tasks {
+            -   smithyBuildJar {
+            +   smithyBuild {
+                    smithyBuildConfigs.set(files("smithy-build.json", other))
+                }
+                // ..
+            }
+
+            -tasks["smithyBuildJar"].dependsOn("otherTask")
+            +tasks["jar"].dependsOn("otherTask")
+
+.. tab:: Groovy
+
+        .. code-block:: diff
+            :caption: build.gradle
+
+            tasks {
+            -   smithyBuildJar {
+            +   smithyBuild {
+                    smithyBuildConfigs = files("smithy-build.json", other)
+                }
+                // ..
+            }
+
+            -tasks["smithyBuildJar"].dependsOn("otherTask")
+            +tasks["jar"].dependsOn("otherTask")
+
+.. _Smithy Gradle plugins: https://github.com/awslabs/smithy-gradle-plugin/
+.. _Configuration: https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.Configuration.html
diff --git a/docs/source-2.0/guides/gradle-plugin/index.rst b/docs/source-2.0/guides/gradle-plugin/index.rst
new file mode 100644
index 000000000..4139418b2
--- /dev/null
+++ b/docs/source-2.0/guides/gradle-plugin/index.rst
@@ -0,0 +1,748 @@
+.. _smithy-gradle-plugin:
+
+=====================
+Smithy Gradle Plugins
+=====================
+
+The `Smithy Gradle plugins`_ integrate Smithy with the `Gradle`_ build system. These plugin can
+build artifacts from Smithy models, and generate JARs for Smithy models and model
+:ref:`projections <projections>`.
+
+.. toctree::
+    :maxdepth: 1
+    :caption: Migrate to version 0.8.0+
+
+    gradle-migration-guide
+
+Plugins
+=======
+
+Two official `Gradle`_ plugins are available:
+
+* `smithy-base`_:
+    This plugin configures the `SourceSet`_'s and `Configuration`_'s
+    for a Smithy project. It also creates the base ``smithyBuild`` task for the project that
+    builds the Smithy models and build artifacts for the project.
+* `smithy-jar`_:
+    Adds Smithy files to an existing JAR created by a ``jar`` task (usually created
+    by the `Java plugin`_ or `Kotlin JVM plugin`_). The ``smithy-jar`` plugin also adds build metadata
+    and tags to the JAR's MANIFEST. The ``smithy-jar`` plugin applies the ``smithy-base`` plugin when
+    applied.
+
+.. admonition:: Which plugin should I use?
+    :class: note
+
+    Most users should use the ``smithy-jar`` plugin. If you are building shared model packages,
+    custom traits, custom linters, or codegen integrations you should use the ``smithy-jar`` plugin.
+    If you are writing a code generator you can use the ``smithy-base`` plugin to set up basic
+    `Configuration`_'s and provide access to Smithy gradle tasks.
+
+.. _plugin-apply:
+
+Applying Plugins
+----------------
+
+The Smithy Gradle plugins are applied using the ``plugins`` block of a gradle build script.
+The following example configures a project to use the ``smithy-base`` Gradle plugin:
+
+.. tab:: Kotlin
+
+    .. code-block:: kotlin
+        :caption: build.gradle.kts
+
+        plugins {
+            id("software.amazon.smithy.gradle.smithy-base").version("0.10.0")
+        }
+
+.. tab:: Groovy
+
+    .. code-block:: groovy
+        :caption: build.gradle
+
+        plugins {
+            id 'software.amazon.smithy.gradle.smithy-base' version '0.10.0'
+        }
+
+
+.. _smithy-model-sources:
+
+.. note::
+
+    The ``smithy-jar`` plugin requires that a `jar` task be registered
+    before it is applied. The `Java Plugin`_ and `Kotlin JVM plugin`_
+    are commonly used plugins that both register a `jar` task.
+
+Complete Examples
+=================
+
+For several complete examples, see the `examples directory`_ of the `Smithy
+Gradle plugins`_ repository, or check out the :doc:`Quick start guide </quickstart>` for a tutorial on
+creating a Smithy model and building it with the Smithy Gradle plugin.
+
+The examples in from the `examples directory`_  can be copied into your workspace using
+the :ref:`Smithy CLI <smithy-cli>` ``init`` command as follows:
+
+.. code-block:: text
+
+    smithy init -t <EXAMPLE_NAME> -o <OUTPUT_DIRECTORY> --url https://github.com/smithy-lang/smithy-gradle-plugin
+
+You can list all examples available in the `Smithy Gradle plugins`_ repository with the following command:
+
+.. code-block:: text
+
+    smithy init --list --url https://github.com/smithy-lang/smithy-gradle-plugin
+
+Configuration
+=============
+
+Smithy model sources
+--------------------
+
+Smithy gradle plugins assume Smithy model files (``*.smithy``) are organized in a similar way as Java source files,
+in sourceSets. The smithy-base plugin adds a new sources block named smithy to every sourceSet.
+By default, this source block will include Smithy models in:
+
+* ``model/``
+* ``src/$sourceSetName/smithy``
+* ``src/$sourceSetName/resources/META-INF/smithy``
+
+New source directories can be added to a smithy sources block as follows:
+
+.. tab:: Kotlin
+
+    .. code-block:: kotlin
+        :caption: build.gradle.kts
+
+        sourceSets {
+            main {
+                smithy {
+                    srcDir("includes")
+                }
+            }
+        }
+
+.. tab:: Groovy
+
+    .. code-block:: groovy
+        :caption: build.gradle
+
+        sourceSets {
+            main {
+                smithy {
+                    srcDir 'includes'
+                }
+            }
+        }
+
+Models found in these directories are combined into a flattened directory
+structure and used to validate and build the Smithy model. A Smithy manifest
+file is automatically created for the detected models, and it, along with the
+model files, are placed in the ``META-INF/smithy/`` resource of the created
+JAR. Any project that depends on this created JAR will be able to find
+and use the Smithy models contained in the JAR when using *model discovery*.
+
+Dependencies
+------------
+
+The Smithy build plugins use two different configurations to search for dependencies
+such as shared models or :ref:`smithy build plugins <plugins>`:
+
+* ``runtimeClasspath``:
+    Runtime dependencies that will be required by any output JARS or publications.
+    For example, a shared model package or Java trait definition.
+* ``smithyBuild``:
+    Build dependencies that are not required by output models.
+
+Runtime dependencies can be added directly to the ``runtimeClasspath`` configuration
+or to a configuration that extends ``runtimeClasspath``, such as the ``implementation``
+configuration added by the ``java-library`` plugin.
+
+.. _dependencies:
+
+Build Dependencies
+^^^^^^^^^^^^^^^^^^
+
+The ``smithy-base`` plugin adds a ``smithyBuild`` `Configuration`_ that can be used to specify
+dependencies used when calling ``smithy build``. These dependencies will not be included in any output JARs or publications. They are akin to `compileOnly`
+dependencies for Java projects. Smithy build plugins and projected dependencies should be
+included in the `smithyBuild` configuration. For example:
+
+.. tab:: Kotlin
+
+    .. code-block:: kotlin
+        :caption: build.gradle.kts
+
+        dependencies {
+            smithyBuild("com.example.software:build-only:1.0.0")
+        }
+
+.. tab:: Groovy
+
+    .. code-block:: groovy
+        :caption: build.gradle
+
+        dependencies {
+            smithyBuild 'com.example.software:build-only:1.0.0'
+        }
+
+.. _smithy-extension-properties:
+
+Smithy extension properties
+---------------------------
+
+The Smithy Gradle plugins are configured using the `SmithyExtension`_ extension:
+
+.. tab:: Kotlin
+
+    .. code-block:: kotlin
+        :caption: build.gradle.kts
+
+        smithy {
+            projection.set("foo")
+        }
+
+.. tab:: Groovy
+
+    .. code-block:: groovy
+        :caption: build.gradle
+
+        smithy {
+            projection = "foo"
+        }
+
+
+This extension supports the following properties:
+
+.. list-table::
+    :header-rows: 1
+    :widths: 10 20 70
+
+    * - Property
+      - Type
+      - Description
+    * - format
+      - ``boolean``
+      - Flag indicating whether to format Smithy source files. By default, the :ref:`Smithy CLI <smithy-cli>` ``format``
+        command is executed on all source directories. This opinionated formatter follows the best practices
+        recommended by the Smithy team. It is possible to disable the formatter by setting this flag to ``false``.
+    * - smithyBuildConfigs
+      - ``FileCollection``
+      - Sets a custom collection of smithy-build.json files to use when building the model.
+    * - sourceProjection
+      - ``String``
+      - Sets the :ref:`projection <projections>` name to use as the source (primary) projection.
+        The smithy sources for this projection will be packaged in output JARs by the ``smithy-jar``
+        plugin. There must be a corresponding projection definition in the :ref:`smithy-build.json <smithy-build-json>`
+        file in the project. Defaults to ``"source"``.
+    * - projectionSourceTags
+      - ``Set<String>``
+      - Get the tags that are searched for in classpaths when determining
+        which models are projected into the created JAR. This plugin will look
+        through the JARs in the buildscript classpath to see if they contain a
+        META-INF/MANIFEST.MF attribute named "Smithy-Tags" that matches any of
+        the given projection source tags. The Smithy models found in each
+        matching JAR are copied into the JAR being projected. This allows a
+        projection JAR to aggregate models into a single JAR.
+    * - tags
+      - ``Set<String>``
+      - Set the tags that are added to the JAR. These tags are placed in the
+        META-INF/MANIFEST.MF attribute named "Smithy-Tags" as a comma
+        separated list. JARs with Smithy-Tags can be queried when building
+        projections so that the Smithy models found in each matching JAR are
+        placed into the projection JAR.
+    * - allowUnknownTraits
+      - ``boolean``
+      - Sets whether or not unknown traits in the model should be ignored. By
+        default, the build will fail if unknown traits are encountered.
+    * - fork
+      - ``boolean``
+      - By default, the CLI is run in the same process as Gradle,
+        but inside a thread with a custom class loader. This should
+        work in most cases, but there is an option to run inside a
+        process if necessary.
+    * - outputDirectory
+      - ``Directory``
+      - Defines where Smithy build artifacts are written.
+
+
+Customize output directory
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+By default, Smithy build artifacts will be placed in the project build
+directory in a ``smithyprojections/`` directory. There are two ways to override
+the output directory. The first method is to set the ``outputDirectory`` property
+in the ``smithy-build.json`` config for your Smithy project. For example:
+
+.. code-block:: json
+    :caption: smithy-build.json
+
+    {
+        "outputDirectory": "build/output"
+    }
+
+The second method is to configure the plugin using the ``smithy`` extension:
+
+.. tab:: Kotlin
+
+    .. code-block:: kotlin
+        :caption: build.gradle.kts
+
+        smithy {
+            outputDirectory.set(file("path/to/output"))
+        }
+
+.. tab:: Groovy
+
+    .. code-block:: groovy
+        :caption: build.gradle
+
+        smithy {
+            outputDirectory = file("path/to/output")
+        }
+
+.. note::
+
+    Setting the output directory on the plugin extension will
+    override any ``outputDirectory`` property set in the
+    smithy-build config.
+
+Set ``smithy-build`` configs to use
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+By default, the plugin will look for a file called ``smithy-build.json``
+at the project's root and will use that as the :ref:`smithy-build config <smithy-build>`
+for your project. If no smithy-build.json file is found, then an empty build config
+is used to build the project.
+
+Alternatively, you can explicitly configure one or more smithy-build configs to use for
+your project as follows:
+
+.. tab:: Kotlin
+
+    .. code-block:: kotlin
+        :caption: build.gradle.kts
+
+        smithy {
+            smithyBuildConfigs.set(files("smithy-build-config.json"))
+        }
+
+.. tab:: Groovy
+
+    .. code-block:: groovy
+        :caption: build.gradle
+
+        smithy {
+            smithyBuildConfigs = files("smithy-build-config.json")
+        }
+
+Set Smithy Tags to add to a JAR
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When the smithy-jar plugin is applied to a project, it can add a number of Smithy
+tags to the MANIFEST of a generated JAR. These tags can be used by downstream
+consumers to filter which models to include in projections. Tags can be configured
+for the plugin as follows:
+
+.. tab:: Kotlin
+
+    .. code-block:: kotlin
+        :caption: build.gradle.kts
+
+        smithy {
+            tags.addAll("tag1", "anotherTag", "anotherTag2")
+        }
+
+.. tab:: Groovy
+
+    .. code-block:: groovy
+        :caption: build.gradle
+
+        smithy {
+            tags += ["tag1", "anotherTag", "anotherTag2"]
+        }
+
+Fork a new process when executing Smithy CLI commands
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+By default, :ref:`Smithy CLI <smithy-cli>` commands are run in the
+same process as Gradle, but inside a thread with a custom class loader.
+This should work in most cases, but there is an option to run inside a
+process if necessary. To run Smithy CLI commands in a process set the
+``fork`` configuration option to true:
+
+.. tab:: Kotlin
+
+    .. code-block:: kotlin
+        :caption: build.gradle.kts
+
+        smithy {
+            fork.set(true)
+        }
+
+.. tab:: Groovy
+
+    .. code-block:: groovy
+        :caption: build.gradle
+
+        smithy {
+            fork = true
+        }
+
+.. _disable-smithy-formatter:
+
+Disable Smithy Formatter
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+By default, the :ref:`Smithy CLI <smithy-cli>` ``format`` command is
+executed on all source directories. This opinionated formatter follows
+the best practices recommended by the Smithy team. It is possible to disable
+the formatter by setting the format setting on the plugin extension to false:
+
+.. tab:: Kotlin
+
+    .. code-block::
+        :caption: build.gradle.kts
+
+        smithy {
+            format.set(false)
+        }
+
+.. tab:: Groovy
+
+    .. code-block:: groovy
+        :caption: build.gradle
+
+        smithy {
+            format = false
+        }
+
+.. _building-smithy-models:
+
+Building Smithy models
+======================
+
+The ``smithyBuild`` task that builds smithy models operates in two different modes:
+
+1. If no ``projection`` is specified for the ``SmithyExtension``, then the task
+   runs a "source" build using the "source" projection.
+2. If a ``projection`` is specified for the ``SmithyExtension``, then the task
+   runs in "projection" mode.
+
+.. _building-source-model:
+
+Building a source model
+-----------------------
+
+A "source" build is run when no ``projection`` is configured in
+``SmithyExtension``. Because no projection was specified, **smithy-build** is
+executed using the ``compileClasspath`` plus the ``buildscript`` classpath. To
+prevent accidentally relying on Smithy models that are only available to
+build scripts, Smithy models are discovered using only the
+``compileClasspath`` and ``runtimeClasspath``.
+
+The following example ``build.gradle.kts`` will build a Smithy model using a
+"source" build:
+
+.. tab:: Kotlin
+
+        .. code-block:: kotlin
+            :caption: build.gradle.kts
+
+            plugins {
+                `java-library`
+                id("software.amazon.smithy.gradle.smithy-jar").version("0.10.0")
+            }
+
+            // The SmithyExtension is used to customize the build. This example
+            // doesn't set any values and can be completely omitted.
+            smithy {}
+
+            repositories {
+                mavenLocal()
+                mavenCentral()
+            }
+
+            dependencies {
+                implementation("software.amazon.smithy:smithy-model:__smithy_version__")
+
+                // These are just examples of dependencies. This model has a dependency on
+                // a "common" model package and uses the external AWS traits.
+                implementation("com.foo.baz:foo-model-internal-common:1.0.0")
+                implementation("software.amazon.smithy:smithy-aws-traits:__smithy_version__")
+            }
+
+.. tab:: Groovy
+
+        .. code-block:: groovy
+            :caption: build.gradle
+
+            plugins {
+                id 'java-library'
+                'software.amazon.smithy.gradle.smithy-jar' version '0.10.0'
+            }
+
+            // The SmithyExtension is used to customize the build. This example
+            // doesn't set any values and can be completely omitted.
+            smithy {}
+
+            repositories {
+                mavenLocal()
+                mavenCentral()
+            }
+
+            dependencies {
+                implementation 'software.amazon.smithy:smithy-model:__smithy_version__'
+
+                // These are just examples of dependencies. This model has a dependency on
+                // a "common" model package and uses the external AWS traits.
+                implementation 'com.foo.baz:foo-model-internal-common:1.0.0'
+                implementation 'software.amazon.smithy:smithy-aws-traits:__smithy_version__'
+            }
+
+.. _generating-projection:
+
+Generating a projection
+-----------------------
+
+A "projection" build is run when a :ref:`projection <projections>` is
+specified in the ``SmithyExtension``. You might create a projection of
+a model if you need to maintain an internal version of a model that contains
+more information and features than an external version of a model published to
+your customers.
+
+Any projected models should be added to the ``smithyBuild`` configuration. This
+prevents packages with projected models from appearing as dependencies of the
+projected version of the model.
+
+The following example gradle build script will run a "projection"
+build using the "external" projection.
+
+.. tab:: Kotlin
+
+        .. code-block:: kotlin
+            :caption: build.gradle.kts
+
+            plugins {
+                `java-library`
+                id("software.amazon.smithy.gradle.smithy-jar").version("0.10.0")
+            }
+
+            repositories {
+                mavenLocal()
+                mavenCentral()
+            }
+
+            dependencies {
+                implementation("software.amazon.smithy:smithy-aws-traits:__smithy_version__")
+
+                // Take a dependency on the internal model package. This
+                // dependency *must* be a smithyBuild dependency to ensure
+                // that is does not appear in the generated JAR.
+                smithyBuild("com.foo.baz:foo-internal-model:1.0.0")
+            }
+
+            smithy {
+                // Use the "external" projection. This projection must be found in the
+                // smithy-build.json file. This also ensures that models found in the
+                // foo-internal-package that weren't filtered out are copied into the
+                // projection created by this package.
+                sourceProjection.set("external")
+                projectionSourceTags.addAll("com.foo.baz:foo-internal-model")
+            }
+
+.. tab:: Groovy
+
+        .. code-block:: groovy
+            :caption: build.gradle
+
+            plugins {
+                id 'java-library'
+                id 'software.amazon.smithy.gradle.smithy-jar' version '0.10.0'
+            }
+
+            repositories {
+                mavenLocal()
+                mavenCentral()
+            }
+
+            dependencies {
+                implementation 'software.amazon.smithy:smithy-aws-traits:__smithy_version__'
+
+                // Take a dependency on the internal model package. This
+                // dependency *must* be a smithyBuild dependency to ensure
+                // that is does not appear in the generated JAR.
+                smithyBuild 'com.foo.baz:foo-internal-model:1.0.0'
+            }
+
+            smithy {
+                // Use the "external" projection. This projection must be found in the
+                // smithy-build.json file. This also ensures that models found in the
+                // foo-internal-package that weren't filtered out are copied into the
+                // projection created by this package.
+                sourceProjection = "external"
+                projectionSourceTags += ["com.foo.baz:foo-internal-model"]
+            }
+
+Because the ``sourceProjection`` of the ``SmithyExtension`` was set to ``external``, a
+``smithy-build.json`` file **must** be found that defines the ``external``
+projection. For example:
+
+.. code-block:: json
+    :caption: smithy-build.json
+
+    {
+        "version": "1.0",
+        "projections": {
+            "external": {
+                "transforms": [
+                    {
+                        "name": "excludeShapesByTag",
+                        "args": {
+                            "tags": ["internal"]
+                        }
+                    },
+                    {
+                        "name": "excludeShapesByTrait",
+                        "args": {
+                            "traits": ["internal"]
+                        }
+                    },
+                    {
+                        "name": "excludeMetadata",
+                        "args": {
+                            "keys": ["suppressions", "validators"]
+                        }
+                    },
+                    {
+                        "name": "removeUnusedShapes"
+                    }
+                ]
+            }
+        }
+    }
+
+
+.. _projection-tags:
+
+Projection tags
+---------------
+
+Projections are meant to *project* and filter other models into another
+model. You need to specify which dependencies are being projected into your
+JAR by setting the ``projectionSourceTags`` property.
+
+.. tab:: Kotlin
+
+        .. code-block:: kotlin
+            :caption: build.gradle.kts
+
+            smithy {
+                sourceProjection.set("external")
+                projectionSourceTags.addAll("Foo", "com.baz:bar")
+            }
+
+.. tab:: Groovy
+
+        .. code-block:: groovy
+            :caption: build.gradle
+
+            smithy {
+                projection = "external"
+                projectionSourceTags += setOf("Foo", "com.baz:bar")
+            }
+
+
+Tags are used to logically group packages to make it easier to build
+projections. The ``tags`` property is used to add ``Smithy-Tags`` to a JAR.
+
+.. tab:: Kotlin
+
+        .. code-block:: kotlin
+            :caption: build.gradle.kts
+
+            smithy {
+                tags.addAll("X", "foobaz-model")
+            }
+
+.. tab:: Groovy
+
+        .. code-block:: groovy
+            :caption: build.gradle
+
+            smithy {
+                tags += ["X", "foobaz-model"]
+            }
+
+
+For example, if your service is made up of 10 packages, you can add the
+"foobaz-model" Smithy tag to each package so that the only value that needs
+to be provided for ``tags`` to correctly project your model is "foobaz-model".
+
+When building a model package, this plugin will automatically add the group
+name of the package being built, the group name + ":" + name of the package,
+and group name + ":" + name + ":" version. This allows models to always
+be queried by group and artifact names in addition to custom tags.
+
+
+.. _artifacts-from-smithy-models:
+
+Building artifacts from Smithy models
+-------------------------------------
+
+If a ``smithy-build.json`` file is found at the root of the project, then it
+will be used to generate :ref:`artifacts <projection-artifacts>` from the Smithy model.
+
+The following example generates an OpenAPI model from a Smithy model:
+
+.. code-block:: json
+    :caption: smithy-build.json
+
+    {
+        "version": "1.0",
+        "plugins": {
+            "openapi": {
+                "service": "foo.baz#MyService"
+            }
+        }
+    }
+
+
+The above Smithy plugin also requires a ``smithyBuild`` dependency in
+the gradle build script:
+
+.. tab:: Kotlin
+
+        .. code-block:: kotlin
+            :caption: build.gradle.kts
+
+            dependencies {
+                // ...
+
+                // This dependency is required in order to apply the "openapi"
+                // plugin in smithy-build.json
+                smithyBuild("software.amazon.smithy:smithy-openapi:__smithy_version__")
+            }
+
+.. tab:: Groovy
+
+        .. code-block:: groovy
+            :caption: build.gradle
+
+            dependencies {
+                // ...
+
+                // This dependency is required in order to apply the "openapi"
+                // plugin in smithy-build.json
+                smithyBuild 'software.amazon.smithy:smithy-openapi:__smithy_version__'
+            }
+
+.. _examples directory: https://github.com/awslabs/smithy-gradle-plugin/tree/main/examples
+.. _Smithy Gradle plugins: https://github.com/awslabs/smithy-gradle-plugin/
+.. _Gradle: https://gradle.org/
+.. _smithy-base: https://github.com/smithy-lang/smithy-gradle-plugin#smithy-base-plugin
+.. _smithy-jar: https://github.com/smithy-lang/smithy-gradle-plugin#smithy-jar-plugin
+.. _Configuration: https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.Configuration.html
+.. _SourceSet: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceSet.html
+.. _Java plugin: https://docs.gradle.org/current/userguide/java_plugin.html
+.. _Kotlin JVM plugin: https://plugins.gradle.org/plugin/org.jetbrains.kotlin.jvm
+.. _SmithyExtension: https://github.com/smithy-lang/smithy-gradle-plugin/blob/main/smithy-base/src/main/java/software/amazon/smithy/gradle/SmithyExtension.java
diff --git a/docs/source-2.0/guides/index.rst b/docs/source-2.0/guides/index.rst
index dee6887df..a45214311 100644
--- a/docs/source-2.0/guides/index.rst
+++ b/docs/source-2.0/guides/index.rst
@@ -7,7 +7,7 @@ Guides
 
     smithy-cli/index
     smithy-build-json
-    gradle-plugin
+    gradle-plugin/index
     using-code-generation/index
     model-linters
     model-validation-examples
diff --git a/docs/source-2.0/quickstart.rst b/docs/source-2.0/quickstart.rst
index c99297472..4f4643147 100644
--- a/docs/source-2.0/quickstart.rst
+++ b/docs/source-2.0/quickstart.rst
@@ -93,10 +93,31 @@ weather service.
 4. This service closure contains the following operations:
    ``ListCities``, ``GetCity``, ``GetForecast``, ``GetCurrentTime``.
 
-``Weather`` is a :ref:`service` shape that is defined inside of a
-:ref:`namespace <namespaces>`.
+First, create a directory called `smithy-quickstart` with a `model` directory
+and a weather model file such that your `smithy-quickstart` directory has the
+following file structure:
+
+.. code-block:: text
+
+    smithy-quickstart/
+    └── model/
+        └── weather.smithy
+
+.. tip::
+
+    Run the following command to create the quickstart directory
+
+    .. code-block:: text
+
+        mkdir -p smithy-quickstart/model \
+        && touch smithy-quickstart/model/weather.smithy \
+        && cd smithy-quickstart
+
+Next, we will start to model a ``Weather`` service in the `weather.smithy` file.
+``Weather`` is a :ref:`service` shape that is defined inside of a :ref:`namespace <namespaces>`.
 
 .. code-block:: smithy
+    :caption: model/weather.smithy
 
     $version: "2"
     namespace example.weather
@@ -129,6 +150,7 @@ A resource is contained within a service or another resource. Resources have
 identifiers, operations, and any number of child resources.
 
 .. code-block:: smithy
+    :caption: model/weather.smithy
 
     $version: "2"
     namespace example.weather
@@ -163,6 +185,7 @@ Each ``City`` has a single ``Forecast``. This can be defined by adding the
 ``Forecast`` to the ``resources`` property of the ``City``.
 
 .. code-block:: smithy
+    :caption: model/weather.smithy
 
     resource City {
         identifiers: { cityId: CityId }
@@ -189,6 +212,7 @@ members of resource operations map to resource properties or identifiers to
 perform updates on or examine the state of a resource.
 
 .. code-block:: smithy
+    :caption: model/weather.smithy
 
     resource City {
         identifiers: { cityId: CityId }
@@ -244,6 +268,7 @@ your API.
 Let's define the operation used to "read" a ``City``.
 
 .. code-block:: smithy
+    :caption: model/weather.smithy
 
     @readonly
     operation GetCity {
@@ -291,6 +316,7 @@ Let's define the operation used to "read" a ``City``.
 And define the operation used to "read" a ``Forecast``.
 
 .. code-block:: smithy
+    :caption: model/weather.smithy
 
     @readonly
     operation GetForecast {
@@ -332,6 +358,7 @@ bind the identifier of a ``City`` to its input structure; we are listing
 cities, so there's no way we could provide a ``City`` identifier.
 
 .. code-block:: smithy
+    :caption: model/weather.smithy
 
     /// Provides weather forecasts.
     @paginated(inputToken: "nextToken", outputToken: "nextToken",
@@ -413,6 +440,7 @@ service.
 
 
 .. code-block:: smithy
+    :caption: model/weather.smithy
 
     /// Provides weather forecasts.
     @paginated(inputToken: "nextToken", outputToken: "nextToken",
@@ -442,51 +470,80 @@ service.
 Building the Model
 ==================
 
-Now that you have a model, you'll want to build it and generate things from it.
-Building the model creates projections of the model, applies plugins to
-generate artifacts, runs validation, and creates a JAR that contains the
-filtered projection. The `Smithy Gradle Plugin`_ is the best way to get started
-building a Smithy model. First, create a ``smithy-build.json`` file:
+Now that you have a model, you'll want to build it and generate additional
+artifacts from it. Building the model creates projections of the model, applies plugins to
+generate artifacts, and runs validation.
 
-.. code-block:: json
+.. tab:: Smithy CLI
 
-    {
-        "version": "1.0"
-    }
+    .. admonition:: Install required tools
+        :class: tip
 
-Then create a new ``build.gradle.kts`` file:
+        Before you proceed, make sure you have the :ref:`Smithy CLI installed <cli_installation>`.
 
-.. code-block:: kotlin
+    To build a Smithy model using the :ref:`the Smithy CLI <smithy-cli>`,
+    create a :ref:`smithy-build.json <smithy-build-json>` file in the ``smithy-quickstart`` directory:
 
-    plugins {
-        id("software.amazon.smithy").version("0.6.0")
-    }
+    .. code-block:: json
+        :caption: smithy-build.json
 
-    repositories {
-        mavenLocal()
-        mavenCentral()
-    }
+        {
+            // Version of the smithy-build.json file specification
+            "version": "1.0",
+            // Location to search for Smithy model source files
+            "sources": ["model"]
+        }
 
-    dependencies {
-        implementation("software.amazon.smithy:smithy-model:__smithy_version__")
-    }
+    Next, run ``smithy build``. That's it! We just created a simple, read-only, ``Weather`` service.
 
-    configure<software.amazon.smithy.gradle.SmithyExtension> {
-        // Uncomment this to use a custom projection when building the JAR.
-        // projection = "foo"
-    }
+.. tab:: Gradle
+
+    .. admonition:: Install required tools
+        :class: tip
+
+        Before you proceed, make sure you have `gradle installed`_.
+
+
+    To build a Smithy model using the :ref:`Smithy Gradle Plugin <smithy-gradle-plugin>`,
+    first, create a gradle build script file in the ``smithy-quickstart`` directory:
+
+    .. tab:: Kotlin
+
+        .. code-block:: kotlin
+            :caption: build.gradle.kts
 
-    // Uncomment to disable creating a JAR.
-    //tasks["jar"].enabled = false
+            plugins {
+                `java-library`
+                id("software.amazon.smithy.gradle.smithy-jar").version("0.10.0")
+            }
+
+            repositories {
+                mavenLocal()
+                mavenCentral()
+            }
+
+    .. tab:: Groovy
+
+        .. code-block:: groovy
+            :caption: build.gradle
+
+            plugins {
+                id 'java-library'
+                id 'software.amazon.smithy.gradle.smithy-jar' version '0.10.0'
+            }
+
+            repositories {
+                mavenLocal()
+                mavenCentral()
+            }
+
+
+    Next, run ``gradle build``. That's it! We just created a simple, read-only, ``Weather`` service.
 
-Finally, copy the weather model to ``model/weather.smithy`` and
-then run ``gradle build`` (make sure you have `gradle installed`_).
 
 Next steps
 ==========
 
-That's it! We just created a simple, read-only, ``Weather`` service.
-
 1. Try adding a "create" lifecycle operation to ``City``.
 2. Try adding a "delete" lifecycle operation to ``City``.
 3. Try adding :ref:`HTTP binding traits <http-traits>` to the API.
@@ -511,49 +568,46 @@ There's plenty more to explore in Smithy.
 Complete example
 ================
 
-If you followed all the steps in this guide, you should have three files, laid
-out like so::
 
-    .
-    ├── build.gradle.kts
-    ├── model
-    │   └── weather.smithy
-    └── smithy-build.json
 
-The ``smithy-build.json`` should have the following contents:
+.. note::
 
-.. code-block:: json
+    You can clone a working version of this quickstart example using the
+    :ref:`Smithy CLI <smithy-cli>` ``init`` command.
 
-    {
-        "version": "1.0"
-    }
+    .. tab:: Quickstart with Smithy CLI
 
-The ``build.gradle.kts`` should have the following contents:
+        .. code-block::
 
-.. code-block:: kotlin
+            smithy init -o <output_directory>
 
-    plugins {
-        id("software.amazon.smithy").version("0.6.0")
-    }
+    .. tab:: Quickstart with Gradle
 
-    repositories {
-        mavenLocal()
-        mavenCentral()
-    }
+        .. code-block::
 
-    dependencies {
-        implementation("software.amazon.smithy:smithy-model:__smithy_version__")
-    }
+            smithy init -t quickstart-gradle -o <output_directory>
 
-    configure<software.amazon.smithy.gradle.SmithyExtension> {
-        // Uncomment this to use a custom projection when building the JAR.
-        // projection = "foo"
-    }
+If you followed all the steps in this guide, your working directory should be laid out like so:
+
+.. tab:: Smithy CLI
+
+    .. code-block:: text
+
+        .
+        ├── smithy-build.json
+        └── model
+            └── weather.smithy
+
+.. tab:: Gradle
+
+    .. code-block:: text
 
-    // Uncomment to disable creating a JAR.
-    //tasks["jar"].enabled = false
+        .
+        ├── build.gradle.kts
+        └── model
+            └── weather.smithy
 
-Finally, the complete ``weather.smithy`` model should look like:
+The complete ``weather.smithy`` model should look like:
 
 .. code-block:: smithy
 
diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlStringLexer.java b/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlStringLexer.java
index d6128d045..bcd30b909 100644
--- a/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlStringLexer.java
+++ b/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlStringLexer.java
@@ -237,22 +237,16 @@ final class IdlStringLexer {
     /**
      * Computes the number of leading whitespace characters in a string.
      *
-     * <p>This method returns -1 if the string is empty or if the string
-     * contains only whitespace. When determining the whitespace of the
-     * last line, the length of the line is returned if the entire
-     * line consists of only spaces. This ensures that the whitespace
-     * of the last line can be used to influence the indentation of the
-     * content as a whole (a significant trailing line policy).
+     * <p>This method returns -1 if the string contains only whitespace. When determining the whitespace of the
+     * last line, the length of the line is returned if the entire line consists of only spaces. This ensures that
+     * the whitespace of the last line can be used to influence the indentation of the content as a whole
+     * (a significant trailing line policy).
      *
      * @param line Line to search for whitespace.
      * @param isLastLine Whether or not this is the last line.
      * @return Returns the last whitespace index starting at 0 or -1.
      */
     private static int computeLeadingWhitespace(CharSequence line, boolean isLastLine) {
-        if (line.length() == 0) {
-            return -1;
-        }
-
         for (int offset = 0; offset < line.length(); offset++) {
             if (line.charAt(offset) != ' ') {
                 return offset;
diff --git a/smithy-syntax/src/main/java/software/amazon/smithy/syntax/FormatVisitor.java b/smithy-syntax/src/main/java/software/amazon/smithy/syntax/FormatVisitor.java
index 2df51058e..a9b1a47f6 100644
--- a/smithy-syntax/src/main/java/software/amazon/smithy/syntax/FormatVisitor.java
+++ b/smithy-syntax/src/main/java/software/amazon/smithy/syntax/FormatVisitor.java
@@ -17,14 +17,13 @@ package software.amazon.smithy.syntax;
 
 import com.opencastsoftware.prettier4j.Doc;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Optional;
 import java.util.function.Function;
-import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import software.amazon.smithy.model.shapes.ShapeId;
+import software.amazon.smithy.utils.StringUtils;
 
 final class FormatVisitor {
 
@@ -447,11 +446,49 @@ final class FormatVisitor {
             case TEXT_BLOCK: {
                 // Dispersing the lines of the text block preserves any indentation applied from formatting parent
                 // nodes.
-                List<Doc> lines = Arrays.stream(tree.concatTokens().split(System.lineSeparator()))
-                        .map(String::trim)
-                        .map(Doc::text)
-                        .collect(Collectors.toList());
-                return Doc.intersperse(Doc.line(), lines);
+
+                // We need to rebuild the text block to remove any incidental leading whitespace. The easiest way to
+                // do that is to use the already parsed and resolved value from the lexer.
+                String stringValue = cursor.getTree()
+                        .tokens()
+                        .findFirst()
+                        .orElseThrow(() -> new RuntimeException("TEXT_BLOCK cursor does not have an IDL token"))
+                        .getStringContents();
+
+                // If the last character is a newline, then the closing triple quote must be on the next line.
+                boolean endQuoteOnNextLine = stringValue.endsWith("\n") || stringValue.endsWith("\r");
+
+                List<Doc> resultLines = new ArrayList<>();
+                resultLines.add(Doc.text("\"\"\""));
+
+                String[] inputLines = stringValue.split("\\r?\\n", -1);
+                for (int i = 0; i < inputLines.length; i++) {
+                    boolean lastLine = i == inputLines.length - 1;
+
+                    // If this is the last line and the ending quote is on the next line, then skip the extra line.
+                    if (endQuoteOnNextLine && lastLine) {
+                        break;
+                    }
+
+                    String lineValue = inputLines[i];
+
+                    // Trim trailing whitespace.
+                    // TODO: This may need to be configurable.
+                    lineValue = StringUtils.stripEnd(lineValue, null);
+
+                    // Add the closing quote to this line if it needs to be on the last line.
+                    if (lastLine) {
+                        lineValue += "\"\"\"";
+                    }
+
+                    resultLines.add(Doc.text(lineValue));
+                }
+
+                if (endQuoteOnNextLine) {
+                    resultLines.add(Doc.text("\"\"\""));
+                }
+
+                return Doc.intersperse(Doc.line(), resultLines);
             }
 
             case TOKEN:


Test patch:
diff --git a/smithy-model/src/test/java/software/amazon/smithy/model/loader/IdlInternalTokenizerTest.java b/smithy-model/src/test/java/software/amazon/smithy/model/loader/IdlInternalTokenizerTest.java
index dfce68688..a0ae485b7 100644
--- a/smithy-model/src/test/java/software/amazon/smithy/model/loader/IdlInternalTokenizerTest.java
+++ b/smithy-model/src/test/java/software/amazon/smithy/model/loader/IdlInternalTokenizerTest.java
@@ -21,9 +21,13 @@ import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.nullValue;
 import static org.hamcrest.Matchers.startsWith;
 
+import java.util.stream.Stream;
 import org.hamcrest.MatcherAssert;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
 
 public class IdlInternalTokenizerTest {
 
@@ -181,4 +185,81 @@ public class IdlInternalTokenizerTest {
         assertThat(lines, equalTo("Hi\nThere\n123\n456"));
         MatcherAssert.assertThat(tokenizer.removePendingDocCommentLines(), nullValue());
     }
+
+    public static Stream<Arguments> textBlockTests() {
+        return Stream.of(
+                Arguments.of(
+                        "\"\"\"\n"
+                        + "    Hello\n"
+                        + "        - Indented\"\"\"\n",
+                        "Hello\n    - Indented"
+                ),
+                Arguments.of(
+                        "\"\"\"\n"
+                        + "    Hello\n"
+                        + "        - Indented\n"
+                        + "    \"\"\"\n",
+                        "Hello\n    - Indented\n"
+                ),
+                Arguments.of(
+                    "\"\"\"\n"
+                    + "    Hello\n"
+                    + "        - Indented\n"
+                    + "\"\"\"\n",
+                    "    Hello\n        - Indented\n"
+                ),
+                Arguments.of(
+                    "\"\"\"\n"
+                    + "    Hello\"\"\"\n",
+                    "Hello"
+                ),
+                Arguments.of(
+                        "\"\"\"\n"
+                        + "    Hello\n"
+                        + "\n"
+                        + "        - Indented\n"
+                        + "\"\"\"\n",
+                        "    Hello\n\n        - Indented\n"
+                ),
+                Arguments.of(
+                        "\"\"\"\n"
+                        + "                        \n" // only WS doesn't influence line length calculations.
+                        + "          Hello\n"
+                        + "                        \n" // only WS doesn't influence line length calculations.
+                        + "          \"\"\"",
+                        "\nHello\n\n"
+                ),
+                Arguments.of(
+                        "\"\"\"\n"
+                        + "\n" // empty lines are incidental whitespace.
+                        + "          Hello\n"
+                        + "                        \n" // only WS doesn't influence line length calculations.
+                        + "          \"\"\"",
+                        "\nHello\n\n"
+                ),
+                Arguments.of(
+                        "\"\"\"\n"
+                        + "\n" // empty lines are incidental whitespace.
+                        + "Hello\n"
+                        + "\n"
+                        + "\n"
+                        + "\"\"\"",
+                        "\nHello\n\n\n"
+                ),
+                Arguments.of(
+                        "\"\"\"\n"
+                        + "\"\"\"",
+                        ""
+                )
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("textBlockTests")
+    public void parsesTextBlocks(String model, String stringValue) {
+        IdlInternalTokenizer tokenizer = new IdlInternalTokenizer("a.smithy", model);
+        tokenizer.expect(IdlToken.TEXT_BLOCK);
+
+        assertThat(tokenizer.getCurrentTokenStringSlice().toString(), equalTo(stringValue));
+    }
 }
diff --git a/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/node-text-blocks.formatted.smithy b/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/node-text-blocks.formatted.smithy
index 9a39712d8..0e4d4b241 100644
--- a/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/node-text-blocks.formatted.smithy
+++ b/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/node-text-blocks.formatted.smithy
@@ -9,7 +9,7 @@ metadata validators = [
         configuration: {
             selector: """
                 operation -[input]-> structure > member
-                :test(member > string:not([trait|enum]))
+                    :test(member > string:not([trait|enum]))
                 :test(member > string:not([trait|length]))
                 :test(member > string:not([trait|aws.api#arnReference]))
                 :test(member > string:not([trait|aws.api#providesPassRole]))
diff --git a/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/trait-textblock.formatted.smithy b/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/trait-textblock.formatted.smithy
index 899acc3ee..242e7b96f 100644
--- a/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/trait-textblock.formatted.smithy
+++ b/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/trait-textblock.formatted.smithy
@@ -2,6 +2,7 @@ $version: "2.0"
 
 namespace smithy.example
 
+// This will look exactly the same when formatted.
 @documentation(
     """
     This is the documentation for Foo.
@@ -9,6 +10,7 @@ namespace smithy.example
 )
 string Foo
 
+// This will look exactly the same when formatted.
 @documentation(
     """
     This is the documentation for Bar.
@@ -17,6 +19,7 @@ string Foo
 )
 string Bar
 
+// The quotes and text will be indented inside the parens when formatted.
 @documentation(
     """
     This is the documentation for Baz.
@@ -25,10 +28,61 @@ string Bar
 )
 string Baz
 
+// The opening quotes will be indented and move to the next line, the contents of the block will remain indented
+// because indentation is based on the closing quote, the closing quote will be indented, and the closing paren on
+// the next line.
 @documentation(
     """
-    This is the documentation for Bux.
-    Lorem ipsum dolor.
+        This is the documentation for Bux.
+        Lorem ipsum dolor.
     """
 )
 string Bux
+
+// The opening quote will move to the next line, indentend, the content will remain the same, the closing quote
+// will stay on last line, and the closing paren will move to the next line.
+@documentation(
+    """
+    This doc string must not have the leading whitespace altered.
+
+    {
+        "foo": true,
+        bar: [
+            false
+        ]
+    }"""
+)
+string JsonDocsNoTrailingNewLine
+
+// The opening quote will move to the next line, indentend, the content will remain the same, the closing quote
+// will stay on its own line, and the closing paren will move to the next line.
+@documentation(
+    """
+    This doc string must not have the leading whitespace altered.
+
+    {
+        "foo": true,
+        bar: [
+            false
+        ]
+    }
+    """
+)
+string JsonDocsTrailingNewLine
+
+// Ensure extra newlines at the end are included.
+@documentation(
+    """
+    Hello with extra newlines.
+
+
+    """
+)
+string ExtraTrailingNewlines
+
+// Moves the quotes to the same indentation level and the closing parens to the next line.
+@documentation(
+    """
+    """
+)
+string EmptyTextBlock
diff --git a/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/trait-textblock.smithy b/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/trait-textblock.smithy
index 834b3579b..2527d7609 100644
--- a/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/trait-textblock.smithy
+++ b/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/trait-textblock.smithy
@@ -2,6 +2,7 @@ $version: "2.0"
 
 namespace smithy.example
 
+// This will look exactly the same when formatted.
 @documentation(
     """
     This is the documentation for Foo.
@@ -9,6 +10,7 @@ namespace smithy.example
 )
 string Foo
 
+// This will look exactly the same when formatted.
 @documentation(
     """
     This is the documentation for Bar.
@@ -17,6 +19,7 @@ string Foo
 )
 string Bar
 
+// The quotes and text will be indented inside the parens when formatted.
 @documentation(
 """
 This is the documentation for Baz.
@@ -25,8 +28,51 @@ Lorem ipsum dolor.
 )
 string Baz
 
+// The opening quotes will be indented and move to the next line, the contents of the block will remain indented
+// because indentation is based on the closing quote, the closing quote will be indented, and the closing paren on
+// the next line.
 @documentation("""
     This is the documentation for Bux.
     Lorem ipsum dolor.
 """)
 string Bux
+
+// The opening quote will move to the next line, indentend, the content will remain the same, the closing quote
+// will stay on last line, and the closing paren will move to the next line.
+@documentation("""
+    This doc string must not have the leading whitespace altered.
+
+    {
+        "foo": true,
+        bar: [
+            false
+        ]
+    }""")
+string JsonDocsNoTrailingNewLine
+
+// The opening quote will move to the next line, indentend, the content will remain the same, the closing quote
+// will stay on its own line, and the closing paren will move to the next line.
+@documentation("""
+    This doc string must not have the leading whitespace altered.
+
+    {
+        "foo": true,
+        bar: [
+            false
+        ]
+    }
+    """)
+string JsonDocsTrailingNewLine
+
+// Ensure extra newlines at the end are included.
+@documentation("""
+    Hello with extra newlines.
+
+
+    """)
+string ExtraTrailingNewlines
+
+// Moves the quotes to the same indentation level and the closing parens to the next line.
+@documentation("""
+    """)
+string EmptyTextBlock