diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..db1caa2
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,96 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+indent_style = space
+indent_size = 4
+
+[*.{md,markdown}]
+trim_trailing_whitespace = false
+
+[*.{yml,yaml,json}]
+indent_size = 2
+
+[*.{csproj,props,targets,slnx,xml}]
+indent_size = 2
+
+[*.cs]
+csharp_indent_case_contents = true
+csharp_indent_labels = flush_left
+csharp_indent_switch_labels = true
+csharp_preserve_single_line_blocks = true
+csharp_new_line_before_open_brace = all
+csharp_new_line_before_catch = true
+csharp_new_line_before_else = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_object_initializers = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_between_method_call_name_and_opening_parenthesis = true
+csharp_space_between_method_declaration_name_and_open_parenthesis = true
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+csharp_style_var_for_built_in_types = true:suggestion
+csharp_style_var_when_type_is_apparent = false:suggestion
+csharp_style_var_elsewhere = false:suggestion
+csharp_style_expression_bodied_methods = false:suggestion
+csharp_style_expression_bodied_constructors = false:suggestion
+csharp_style_expression_bodied_operators = false:suggestion
+csharp_style_expression_bodied_local_functions = false:suggestion
+csharp_style_expression_bodied_properties = when_on_single_line:suggestion
+csharp_style_expression_bodied_indexers = when_on_single_line:suggestion
+csharp_style_expression_bodied_accessors = when_on_single_line:suggestion
+csharp_style_expression_bodied_lambdas = true:suggestion
+dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion
+csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
+csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+csharp_style_prefer_pattern_matching = true:suggestion
+csharp_style_prefer_not_pattern = true:suggestion
+csharp_style_prefer_switch_expression = true:suggestion
+csharp_style_throw_expression = true:suggestion
+csharp_style_conditional_delegate_call = true:suggestion
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
+dotnet_style_prefer_conditional_expression_over_return = true:suggestion
+csharp_style_prefer_range_operator = true:suggestion
+csharp_style_prefer_index_operator = true:suggestion
+csharp_prefer_simple_using_statement = true:suggestion
+csharp_using_directive_placement = outside_namespace:suggestion
+dotnet_style_prefer_auto_properties = false:none
+csharp_prefer_braces = true:suggestion
+csharp_prefer_static_local_function = true:suggestion
+dotnet_style_readonly_field = true:suggestion
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
+dotnet_style_qualification_for_field = false:suggestion
+dotnet_style_qualification_for_property = false:suggestion
+dotnet_style_qualification_for_method = false:suggestion
+dotnet_style_qualification_for_event = false:suggestion
+csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion
+resharper_space_before_new_parentheses = true
+resharper_space_before_method_call_parentheses = true
+resharper_space_before_method_parentheses = true
+resharper_space_before_empty_method_call_parentheses = true
+resharper_space_before_empty_method_parentheses = true
+resharper_space_before_typeof_parentheses = true
+resharper_space_before_default_parentheses = true
+resharper_space_before_checked_parentheses = true
+resharper_space_before_sizeof_parentheses = true
+resharper_space_before_nameof_parentheses = true
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..cd0aac0
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,46 @@
+name: CI
+
+on:
+ push:
+ branches: ['**']
+ pull_request:
+ branches: [main, develop]
+
+permissions:
+ contents: read
+
+jobs:
+ build-and-test:
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ - uses: actions/checkout@v5
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: '10.0.x'
+ dotnet-quality: 'preview'
+
+ - name: Restore
+ run: dotnet restore Terminal.Gui.Cli.slnx
+
+ - name: Build
+ run: dotnet build Terminal.Gui.Cli.slnx --no-restore -c Debug
+
+ - name: Verify code style
+ if: matrix.os == 'ubuntu-latest'
+ run: dotnet format Terminal.Gui.Cli.slnx --no-restore --verify-no-changes
+
+ - name: Terminal.Gui.Cli.Tests
+ run: dotnet run --project tests/Terminal.Gui.Cli.Tests --no-build -c Debug
+
+ - name: Terminal.Gui.Cli.IntegrationTests
+ run: dotnet run --project tests/Terminal.Gui.Cli.IntegrationTests --no-build -c Debug
+
+ - name: Terminal.Gui.Cli.SmokeTests
+ run: dotnet run --project tests/Terminal.Gui.Cli.SmokeTests --no-build -c Debug
diff --git a/.github/workflows/finalize-release.yml b/.github/workflows/finalize-release.yml
new file mode 100644
index 0000000..0f49ca9
--- /dev/null
+++ b/.github/workflows/finalize-release.yml
@@ -0,0 +1,104 @@
+name: Finalize Release
+
+on:
+ pull_request:
+ types: [closed]
+ branches: [main]
+
+jobs:
+ finalize-release:
+ name: Tag, Release, and Back-merge
+ if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/')
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ pull-requests: write
+ steps:
+ - name: Checkout release merge commit
+ uses: actions/checkout@v5
+ with:
+ ref: ${{ github.event.pull_request.merge_commit_sha }}
+ fetch-depth: 0
+ token: ${{ secrets.RELEASE_PAT }}
+
+ - name: Configure Git
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+
+ - name: Extract release info from branch name
+ id: release
+ env:
+ RELEASE_BRANCH: ${{ github.event.pull_request.head.ref }}
+ shell: bash
+ run: |
+ BRANCH="$RELEASE_BRANCH"
+ TAG="${BRANCH#release/}"
+ SEMVER="${TAG#v}"
+
+ if echo "$SEMVER" | grep -q '-'; then
+ PRERELEASE="true"
+ else
+ PRERELEASE="false"
+ fi
+
+ echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
+ echo "semver=${SEMVER}" >> "$GITHUB_OUTPUT"
+ echo "prerelease=${PRERELEASE}" >> "$GITHUB_OUTPUT"
+
+ - name: Check if tag already exists
+ shell: bash
+ run: |
+ if git rev-parse "${{ steps.release.outputs.tag }}" >/dev/null 2>&1; then
+ echo "::error::Tag ${{ steps.release.outputs.tag }} already exists."
+ exit 1
+ fi
+
+ - name: Create annotated tag
+ shell: bash
+ run: |
+ TAG="${{ steps.release.outputs.tag }}"
+ git tag -a "$TAG" -m "Release $TAG"
+ git push origin "$TAG"
+
+ - name: Create GitHub Release
+ env:
+ GH_TOKEN: ${{ secrets.RELEASE_PAT }}
+ TAG: ${{ steps.release.outputs.tag }}
+ PRERELEASE: ${{ steps.release.outputs.prerelease }}
+ shell: bash
+ run: |
+ PRERELEASE_FLAG=""
+ if [ "$PRERELEASE" = "true" ]; then
+ PRERELEASE_FLAG="--prerelease"
+ fi
+
+ gh release create "$TAG" \
+ --title "$TAG" \
+ --generate-notes \
+ $PRERELEASE_FLAG
+
+ - name: Delete release branch
+ env:
+ RELEASE_BRANCH: ${{ github.event.pull_request.head.ref }}
+ shell: bash
+ run: |
+ BRANCH="$RELEASE_BRANCH"
+ git push origin --delete "$BRANCH" || true
+
+ - name: Create back-merge PR
+ env:
+ GH_TOKEN: ${{ secrets.RELEASE_PAT }}
+ TAG: ${{ steps.release.outputs.tag }}
+ shell: bash
+ run: |
+ BACKMERGE_BRANCH="backmerge/${TAG}"
+
+ git checkout -b "$BACKMERGE_BRANCH" "${{ github.event.pull_request.merge_commit_sha }}"
+ git push origin "$BACKMERGE_BRANCH"
+
+ gh pr create \
+ --base develop \
+ --head "$BACKMERGE_BRANCH" \
+ --title "Back-merge ${TAG} from main into develop" \
+ --body "Automatic back-merge after release ${TAG}. Merge this to keep \`develop\` in sync with \`main\`."
diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml
new file mode 100644
index 0000000..4f3a5bc
--- /dev/null
+++ b/.github/workflows/prepare-release.yml
@@ -0,0 +1,167 @@
+name: Prepare Release
+
+on:
+ workflow_dispatch:
+ inputs:
+ release_type:
+ description: 'Release type'
+ required: true
+ type: choice
+ options:
+ - beta
+ - rc
+ - stable
+ default: stable
+ version_override:
+ description: 'Version override (optional, e.g., 0.1.0). Defaults to Directory.Build.props without -develop.'
+ required: false
+ type: string
+
+concurrency:
+ group: prepare-release
+ cancel-in-progress: false
+
+jobs:
+ prepare-release:
+ name: Create Release PR
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ pull-requests: write
+ steps:
+ - name: Checkout develop
+ uses: actions/checkout@v5
+ with:
+ ref: develop
+ fetch-depth: 0
+ token: ${{ secrets.RELEASE_PAT }}
+
+ - name: Configure Git
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+
+ - name: Compute release version
+ id: version
+ shell: bash
+ run: |
+ if [ -n "${{ github.event.inputs.version_override }}" ]; then
+ VERSION="${{ github.event.inputs.version_override }}"
+ else
+ VERSION=$(sed -n 's|.*\(.*\).*|\1|p' Directory.Build.props | head -1)
+ VERSION="${VERSION%%-*}"
+ fi
+
+ RELEASE_TYPE="${{ github.event.inputs.release_type }}"
+
+ if [ -z "$VERSION" ]; then
+ echo "::error::Could not resolve release version."
+ exit 1
+ fi
+
+ if ! echo "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then
+ echo "::error::Release version must be a stable SemVer core like 0.1.0; got '$VERSION'."
+ exit 1
+ fi
+
+ if [ "$RELEASE_TYPE" = "stable" ]; then
+ SEMVER="$VERSION"
+ TAG="v${VERSION}"
+ else
+ LATEST_TAG=$(git tag -l "v${VERSION}-${RELEASE_TYPE}.*" --sort=-v:refname | head -1)
+ if [ -z "$LATEST_TAG" ]; then
+ NEXT_NUM=1
+ else
+ CURRENT_NUM="${LATEST_TAG##*.}"
+ NEXT_NUM=$((CURRENT_NUM + 1))
+ fi
+
+ SEMVER="${VERSION}-${RELEASE_TYPE}.${NEXT_NUM}"
+ TAG="v${SEMVER}"
+ fi
+
+ echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
+ echo "semver=${SEMVER}" >> "$GITHUB_OUTPUT"
+ echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
+ echo "release_type=${RELEASE_TYPE}" >> "$GITHUB_OUTPUT"
+
+ - name: Check for conflicts
+ shell: bash
+ run: |
+ TAG="${{ steps.version.outputs.tag }}"
+ BRANCH="release/${TAG}"
+
+ if git rev-parse "${TAG}" >/dev/null 2>&1; then
+ echo "::error::Tag ${TAG} already exists."
+ exit 1
+ fi
+
+ if git ls-remote --heads origin "${BRANCH}" | grep -q .; then
+ echo "::error::Branch ${BRANCH} already exists on the remote."
+ exit 1
+ fi
+
+ if curl -fsS https://api.nuget.org/v3-flatcontainer/terminal.gui.cli/index.json \
+ | jq -r '.versions[]' | grep -Fxq "${{ steps.version.outputs.semver }}"; then
+ echo "::error::Terminal.Gui.Cli ${{ steps.version.outputs.semver }} already exists on NuGet.org."
+ echo "::error::Choose a new version_override."
+ exit 1
+ fi
+
+ if gh pr list --state open --base main --head "${BRANCH}" --json number --jq 'length' | grep -vq '^0$'; then
+ echo "::error::An open release PR already exists for ${BRANCH}."
+ exit 1
+ fi
+ env:
+ GH_TOKEN: ${{ secrets.RELEASE_PAT }}
+
+ - name: Create release branch
+ shell: bash
+ run: |
+ BRANCH="release/${{ steps.version.outputs.tag }}"
+ git checkout -b "$BRANCH"
+ git push origin "$BRANCH"
+
+ - name: Create Pull Request
+ env:
+ GH_TOKEN: ${{ secrets.RELEASE_PAT }}
+ SEMVER: ${{ steps.version.outputs.semver }}
+ TAG: ${{ steps.version.outputs.tag }}
+ RELEASE_TYPE: ${{ steps.version.outputs.release_type }}
+ shell: bash
+ run: |
+ BRANCH="release/${TAG}"
+
+ if [ "$RELEASE_TYPE" = "stable" ]; then
+ PRERELEASE_NOTE=""
+ else
+ PRERELEASE_NOTE="This is a **${RELEASE_TYPE}** pre-release."
+ fi
+
+ cat > /tmp/pr_body.md << EOF2
+ ## Release ${TAG}
+
+ ${PRERELEASE_NOTE}
+
+ **Version:** \`${SEMVER}\`
+ **NuGet Package:** \`Terminal.Gui.Cli ${SEMVER}\`
+
+ ### What happens when this PR is merged
+
+ 1. The **Finalize Release** workflow creates tag \`${TAG}\`
+ 2. The **Release** workflow builds and pushes \`Terminal.Gui.Cli ${SEMVER}\` to NuGet.org
+ 3. A **GitHub Release** is created with auto-generated notes
+ 4. A **back-merge PR** from \`main\` -> \`develop\` is opened
+
+ ### Checklist
+
+ - [ ] CI passes on this PR
+ - [ ] Version looks correct: \`${SEMVER}\`
+ - [ ] Release notes reviewed
+ EOF2
+
+ gh pr create \
+ --base main \
+ --head "$BRANCH" \
+ --title "Release ${TAG}" \
+ --body-file /tmp/pr_body.md
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..4b78ecc
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,110 @@
+name: Release
+
+on:
+ push:
+ tags: ['v*']
+ branches: [develop]
+
+permissions:
+ contents: write
+
+concurrency:
+ group: release
+ cancel-in-progress: false
+
+jobs:
+ resolve-version:
+ runs-on: ubuntu-latest
+ outputs:
+ version: ${{ steps.v.outputs.version }}
+ steps:
+ - uses: actions/checkout@v5
+
+ - name: Compute version
+ id: v
+ shell: bash
+ run: |
+ if [ "${{ github.ref_type }}" = "tag" ]; then
+ VERSION="${GITHUB_REF_NAME#v}"
+ elif [ "${{ github.ref }}" = "refs/heads/develop" ]; then
+ BASE=$(sed -n 's|.*\(.*\).*|\1|p' Directory.Build.props | head -1)
+ if [ -z "$BASE" ]; then
+ echo "::error::Could not read from Directory.Build.props."
+ exit 1
+ fi
+ VERSION="${BASE}.${GITHUB_RUN_NUMBER}"
+ else
+ echo "::error::Unsupported trigger: event=${{ github.event_name }} ref=${{ github.ref }}"
+ exit 1
+ fi
+
+ if [ -z "$VERSION" ]; then
+ echo "::error::Could not resolve version."
+ exit 1
+ fi
+
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
+
+ build-and-test:
+ needs: resolve-version
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ runs-on: ${{ matrix.os }}
+ env:
+ VERSION: ${{ needs.resolve-version.outputs.version }}
+ steps:
+ - uses: actions/checkout@v5
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: '10.0.x'
+ dotnet-quality: 'preview'
+
+ - name: Restore
+ run: dotnet restore Terminal.Gui.Cli.slnx
+
+ - name: Build
+ run: dotnet build Terminal.Gui.Cli.slnx --no-restore -c Release -p:Version=${{ env.VERSION }}
+
+ - name: Terminal.Gui.Cli.Tests
+ run: dotnet run --project tests/Terminal.Gui.Cli.Tests --no-build -c Release
+
+ - name: Terminal.Gui.Cli.IntegrationTests
+ run: dotnet run --project tests/Terminal.Gui.Cli.IntegrationTests --no-build -c Release
+
+ - name: Terminal.Gui.Cli.SmokeTests
+ run: dotnet run --project tests/Terminal.Gui.Cli.SmokeTests --no-build -c Release
+
+ pack-and-publish:
+ needs: [resolve-version, build-and-test]
+ runs-on: ubuntu-latest
+ env:
+ VERSION: ${{ needs.resolve-version.outputs.version }}
+ steps:
+ - uses: actions/checkout@v5
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: '10.0.x'
+ dotnet-quality: 'preview'
+
+ - name: Pack Terminal.Gui.Cli
+ run: dotnet pack src/Terminal.Gui.Cli -c Release -p:Version=${{ env.VERSION }} -o packages/
+
+ - name: Upload packages
+ uses: actions/upload-artifact@v4
+ with:
+ name: nuget-packages
+ path: packages/*.nupkg
+ retention-days: 30
+
+ - name: Push to NuGet
+ run: >
+ dotnet nuget push packages/*.nupkg
+ --api-key ${{ secrets.NUGET_API_KEY }}
+ --source https://api.nuget.org/v3/index.json
+ --skip-duplicate
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..be5256d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+**/bin/
+**/obj/
+.vs/
+.idea/
+*.user
+*.suo
+*.DotSettings.user
+TestResults/
+packages/
+publish/
+*.nupkg
+launchSettings.json
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..ade21b0
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,3 @@
+# AGENTS.md
+
+All AI coding agents (GitHub Copilot, Codex, etc.) must read and follow the rules in [CLAUDE.md](CLAUDE.md).
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..5126311
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,42 @@
+# CLAUDE.md
+
+This file provides guidance to AI coding agents working in this repository.
+
+## Project status
+
+This repository currently contains scaffolding only for `Terminal.Gui.Cli`.
+No library implementation is present yet.
+
+## Source of truth
+
+- Engineering authority: `specs/constitution.md`
+- Build/style defaults: `.editorconfig`, `Directory.Build.props`
+
+If guidance conflicts, follow `specs/constitution.md`.
+
+## Project structure
+
+- `src/Terminal.Gui.Cli` — class library package (`Terminal.Gui.Cli`)
+- `tests/Terminal.Gui.Cli.Tests` — unit tests
+- `tests/Terminal.Gui.Cli.IntegrationTests` — integration tests
+- `tests/Terminal.Gui.Cli.SmokeTests` — smoke tests
+- `examples/Terminal.Gui.Cli.ExampleApp` — sample console app
+
+## Build and test
+
+- `dotnet restore Terminal.Gui.Cli.slnx`
+- `dotnet build Terminal.Gui.Cli.slnx --no-restore -c Debug`
+- `dotnet format Terminal.Gui.Cli.slnx --no-restore --verify-no-changes`
+- `dotnet run --project tests/Terminal.Gui.Cli.Tests --no-build -c Debug`
+- `dotnet run --project tests/Terminal.Gui.Cli.IntegrationTests --no-build -c Debug`
+- `dotnet run --project tests/Terminal.Gui.Cli.SmokeTests --no-build -c Debug`
+
+## Coding standards
+
+- Target framework is `net10.0`.
+- Keep warnings at zero (`TreatWarningsAsErrors=true`).
+- Use file-scoped namespaces for new C# files.
+- Use Allman braces and always include braces for conditionals/loops.
+- Prefer guard clauses / early returns to deep nesting.
+- One type per file for non-trivial public/internal types.
+- Do not add unrelated implementation while scaffolding.
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..f0681d4
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,29 @@
+
+
+
+ net10.0
+ latest
+ enable
+ enable
+ true
+ true
+
+ gui-cs
+ gui-cs
+ Copyright (c) gui-cs and contributors
+
+ 0.1.0-develop
+ https://github.com/gui-cs/cli
+ https://github.com/gui-cs/cli
+ git
+ LICENSE
+
+ 2.4.1-develop.11
+
+
+
+
+
+
+
+
diff --git a/Directory.Build.targets b/Directory.Build.targets
new file mode 100644
index 0000000..8c119d5
--- /dev/null
+++ b/Directory.Build.targets
@@ -0,0 +1,2 @@
+
+
diff --git a/Terminal.Gui.Cli.slnx b/Terminal.Gui.Cli.slnx
new file mode 100644
index 0000000..2a828b3
--- /dev/null
+++ b/Terminal.Gui.Cli.slnx
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/Terminal.Gui.Cli.ExampleApp/Program.cs b/examples/Terminal.Gui.Cli.ExampleApp/Program.cs
new file mode 100644
index 0000000..1c99635
--- /dev/null
+++ b/examples/Terminal.Gui.Cli.ExampleApp/Program.cs
@@ -0,0 +1 @@
+Console.WriteLine ("Terminal.Gui.Cli scaffold example app.");
diff --git a/examples/Terminal.Gui.Cli.ExampleApp/Terminal.Gui.Cli.ExampleApp.csproj b/examples/Terminal.Gui.Cli.ExampleApp/Terminal.Gui.Cli.ExampleApp.csproj
new file mode 100644
index 0000000..ba00716
--- /dev/null
+++ b/examples/Terminal.Gui.Cli.ExampleApp/Terminal.Gui.Cli.ExampleApp.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ Terminal.Gui.Cli.ExampleApp
+ Terminal.Gui.Cli.ExampleApp
+ false
+
+
+
+
+
+
+
diff --git a/specs/constitution.md b/specs/constitution.md
new file mode 100644
index 0000000..0dc4dd2
--- /dev/null
+++ b/specs/constitution.md
@@ -0,0 +1,63 @@
+# gui-cs/cli Constitution
+
+**Version**: 1.1 | **Ratified**: 2026-05-23 | **Last Amended**: 2026-05-23
+
+This constitution governs all contributions to `gui-cs/cli`. It is the highest-authority engineering document in this repository.
+
+## I. Purpose & Scope
+
+`Terminal.Gui.Cli` is a `.NET` class library for exposing `Terminal.Gui` capabilities through scriptable CLI surfaces.
+
+- Package ID: `Terminal.Gui.Cli`
+- Namespace: `Terminal.Gui.Cli`
+- TFM: `net10.0`
+
+## II. Non-Goals
+
+Until implementation begins, this repository remains scaffold-first and should not accrue speculative production features.
+
+## III. Architectural and Engineering Rules (C1-C8)
+
+Every PR must comply with all rules below.
+
+### C1 — Only CliHost calls Terminal.Gui lifecycle APIs
+
+Initialization and shutdown of Terminal.Gui runtime lifecycle APIs must be centralized in `CliHost`. No command, helper, or utility type may call lifecycle entrypoints directly.
+
+### C2 — Public API changes require spec updates
+
+Any change to public API surface must include corresponding updates in `specs/` in the same PR.
+
+### C3 — No reflection-based command discovery
+
+Command discovery must be explicit and deterministic. Reflection scanning for command registration or dispatch is prohibited.
+
+### C4 — Source-generated JSON only
+
+Runtime reflection-based JSON serialization is disallowed. JSON paths must use source-generated `System.Text.Json` contexts.
+
+### C5 — Tests run in parallel and must avoid process-global mutation
+
+Test projects run in parallel by default. Tests must not mutate process-global state unless explicitly isolated with collection-level opt-outs.
+
+### C6 — Commands must never call Environment.Exit
+
+Command implementations return exit codes/results through framework abstractions and must not terminate the process directly.
+
+### C7 — Schema v1 is append-only
+
+For versioned machine-readable contracts, `v1` schemas are append-only. Existing fields/semantics are not removed or redefined.
+
+### C8 — Zero warnings
+
+Warnings are treated as errors. Builds and CI must remain warning-free.
+
+## IV. Testing Tiers
+
+- `Terminal.Gui.Cli.Tests` — unit tests
+- `Terminal.Gui.Cli.IntegrationTests` — integration tests
+- `Terminal.Gui.Cli.SmokeTests` — smoke-level validation
+
+## V. Governance
+
+Constitution changes require a pull request that updates this file and explains the rationale and migration impact.
diff --git a/src/Terminal.Gui.Cli/Properties/AssemblyInfo.cs b/src/Terminal.Gui.Cli/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..8f9e0d5
--- /dev/null
+++ b/src/Terminal.Gui.Cli/Properties/AssemblyInfo.cs
@@ -0,0 +1,5 @@
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo ("Terminal.Gui.Cli.Tests")]
+[assembly: InternalsVisibleTo ("Terminal.Gui.Cli.IntegrationTests")]
+[assembly: InternalsVisibleTo ("Terminal.Gui.Cli.SmokeTests")]
diff --git a/src/Terminal.Gui.Cli/Terminal.Gui.Cli.csproj b/src/Terminal.Gui.Cli/Terminal.Gui.Cli.csproj
new file mode 100644
index 0000000..62b85da
--- /dev/null
+++ b/src/Terminal.Gui.Cli/Terminal.Gui.Cli.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Terminal.Gui.Cli
+ Terminal.Gui.Cli
+ true
+
+ Terminal.Gui.Cli
+ CLI helpers and abstractions for Terminal.Gui applications.
+ terminal-gui;tui;cli
+ README.md
+
+
+
+
+
+
+
diff --git a/tests/Terminal.Gui.Cli.IntegrationTests/Terminal.Gui.Cli.IntegrationTests.csproj b/tests/Terminal.Gui.Cli.IntegrationTests/Terminal.Gui.Cli.IntegrationTests.csproj
new file mode 100644
index 0000000..7dd9131
--- /dev/null
+++ b/tests/Terminal.Gui.Cli.IntegrationTests/Terminal.Gui.Cli.IntegrationTests.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ Terminal.Gui.Cli.IntegrationTests
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/Terminal.Gui.Cli.SmokeTests/Terminal.Gui.Cli.SmokeTests.csproj b/tests/Terminal.Gui.Cli.SmokeTests/Terminal.Gui.Cli.SmokeTests.csproj
new file mode 100644
index 0000000..d8b6008
--- /dev/null
+++ b/tests/Terminal.Gui.Cli.SmokeTests/Terminal.Gui.Cli.SmokeTests.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ Terminal.Gui.Cli.SmokeTests
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/Terminal.Gui.Cli.Tests/Terminal.Gui.Cli.Tests.csproj b/tests/Terminal.Gui.Cli.Tests/Terminal.Gui.Cli.Tests.csproj
new file mode 100644
index 0000000..6fd2fea
--- /dev/null
+++ b/tests/Terminal.Gui.Cli.Tests/Terminal.Gui.Cli.Tests.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ Terminal.Gui.Cli.Tests
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+