Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: CI

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

jobs:
test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
- run: go test -count=1 ./...
- run: go vet ./...

quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Race detector
run: go test -race -count=1 ./...
- name: Coverage report
run: |
go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.out
go tool cover -func=coverage.out | awk '/^total:/ { gsub("%", "", $3); if ($3 + 0 < 80) { print "coverage below 80%: " $3 "%"; exit 1 } }'
- name: Installer syntax
run: sh -n install.sh
- name: Cross-compile supported targets
shell: bash
run: |
for target in linux/amd64 linux/arm64 darwin/amd64 darwin/arm64 windows/amd64 windows/arm64; do
os=${target%/*}
arch=${target#*/}
extension=
if [ "$os" = windows ]; then extension=.exe; fi
CGO_ENABLED=0 GOOS=$os GOARCH=$arch go build -trimpath -o "/tmp/remote-${os}-${arch}${extension}" ./cmd/remote
done

windows-installer:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Parse PowerShell installer
shell: pwsh
run: |
$tokens = $null
$errors = $null
[System.Management.Automation.Language.Parser]::ParseFile(
(Resolve-Path "./install.ps1"), [ref]$tokens, [ref]$errors
) > $null
if ($errors.Count) {
$errors | ForEach-Object { Write-Error $_ }
exit 1
}
64 changes: 64 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Release

on:
push:
tags:
- "v*"

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: false
- name: Test
run: |
go test ./...
go vet ./...
sh -n install.sh
- name: Validate Windows installer
shell: pwsh
run: |
$tokens = $null
$errors = $null
[System.Management.Automation.Language.Parser]::ParseFile(
(Resolve-Path "./install.ps1"),
[ref]$tokens,
[ref]$errors
) > $null
if ($errors.Count) {
$errors | ForEach-Object { Write-Error $_ }
exit 1
}
- name: Build release binaries
run: |
mkdir -p dist
for target in linux/amd64 linux/arm64 darwin/amd64 darwin/arm64 windows/amd64 windows/arm64; do
os=${target%/*}
arch=${target#*/}
extension=
if [ "$os" = windows ]; then extension=.exe; fi
CGO_ENABLED=0 GOOS=$os GOARCH=$arch go build \
-trimpath \
-ldflags "-s -w -X main.version=${GITHUB_REF_NAME}" \
-o "dist/remote-${os}-${arch}${extension}" \
./cmd/remote
done
cd dist
sha256sum remote-* > checksums.txt
- name: Publish GitHub release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release create "$GITHUB_REF_NAME" \
dist/remote-* \
dist/checksums.txt \
--verify-tag \
--generate-notes \
--title "Remote CLI $GITHUB_REF_NAME"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/bin/
/dist/
/coverage.out
27 changes: 27 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Architecture

Dependencies point inward through these layers:

```text
cmd/remote -> internal/cli -> internal/application
| |
v v
internal/domain
internal/project
internal/bundle
```

- `cmd/remote` is the composition root and process exit boundary.
- `internal/cli` parses commands and renders user-facing output.
- `internal/application` coordinates scaffold, validate, and build use cases.
- `internal/domain` owns manifest types and pure validation policy.
- `internal/project` adapts application projects on the filesystem.
- `internal/bundle` owns source collection, infrastructure payloads,
reproducible archives, and atomic artifact writes.

Domain code must not import CLI, filesystem, or archive packages. The CLI must
not implement domain or packaging policy. Infrastructure packages expose small
operations used by application workflows; they do not format CLI output.

Put a new manifest rule in `domain`, an archive exclusion in `bundle`, command
syntax in `cli`, and the order of a multi-step use case in `application`.
29 changes: 29 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Contributing

## Before opening a pull request

Run the same core checks as CI:

```sh
make test
make test-race
make coverage
go vet ./...
```

CI runs tests and vet on Linux, macOS, and Windows, validates both installers,
runs the race detector, cross-compiles every supported OS/architecture pair,
and rejects total statement coverage below 80%.

## Testing changes

- Put pure domain-rule tests in `internal/domain`.
- Put filesystem adapter tests in `internal/project`.
- Put archive and artifact tests in `internal/bundle`.
- Put use-case and end-to-end tests in `internal/application`.
- Put command parsing, output, aliases, and error mapping tests in `internal/cli`.
- Test success, invalid input, boundary failures, and exact observable output.
- Reproduce every bug with a failing regression test before fixing it.

Keep commits focused. Structural refactors must preserve observable behavior and
should be separate from feature or bug-fix commits.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Futrx

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.PHONY: build test test-race coverage install

VERSION ?= dev

build:
go build -trimpath -ldflags "-s -w -X main.version=$(VERSION)" -o bin/remote ./cmd/remote

test:
go test ./...

test-race:
go test -race ./...

coverage:
go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.out

install:
go install -trimpath -ldflags "-s -w -X main.version=$(VERSION)" ./cmd/remote
80 changes: 80 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Remote CLI

The developer CLI for creating and packaging Remote applications.

See [ARCHITECTURE.md](ARCHITECTURE.md) for package boundaries and
[CONTRIBUTING.md](CONTRIBUTING.md) for local checks and testing expectations.

## Install

Install the latest release on Linux or macOS:

```sh
curl -fsSL https://raw.githubusercontent.com/futrx-com/remote.futrx-cli/main/install.sh | sh
```

The installer supports AMD64 and ARM64, verifies the release checksum, and
places `remote` in `~/.local/bin` by default. Install a specific release or
choose another directory with environment variables on the `sh` command:

```sh
curl -fsSL https://raw.githubusercontent.com/futrx-com/remote.futrx-cli/main/install.sh \
| REMOTE_VERSION=v0.1.0 REMOTE_INSTALL_DIR="$HOME/bin" sh
```

On Windows, run this in PowerShell:

```powershell
irm https://raw.githubusercontent.com/futrx-com/remote.futrx-cli/main/install.ps1 | iex
```

The Windows installer supports AMD64 and ARM64, verifies the release checksum,
installs `remote.exe` under `%LOCALAPPDATA%\Programs\Remote` by default, and
adds that directory to the user `PATH`. `REMOTE_VERSION` and
`REMOTE_INSTALL_DIR` provide the same overrides as on Linux and macOS.

Platform detection, download, and checksum verification happen once when the
installer runs. The installed CLI does not contact GitHub or repeat installation
checks when you run a command.

## Install from source

```sh
go install github.com/futrx-com/remote.futrx-cli/cmd/remote@latest
```

During local development:

```sh
go build -o ./bin/remote ./cmd/remote
```

## Create an application

```sh
remote create app my-app
cd my-app
remote validate
remote build
```

`create` produces a complete composable application with `ui/`, `backend/`,
`infra/`, `skills/`, `application.json`, documentation, and an MIT license.
Delete capabilities the application does not need and update the manifest.

`build` validates the package and writes `<id>-<version>.zip` beside the app.
The ZIP is reproducible. Infrastructure support files are turned into
`infra/payload.tar.gz` in memory, so an app does not need to ship or run a
`package.sh` script. Legacy `infra/package.sh` and generated payloads are
excluded from the package automatically.

## Commands

```text
remote create app <name> [--dir PATH]
remote app create <name> [--dir PATH]
remote validate [PATH]
remote build [PATH] [-o FILE]
remote package [PATH] [-o FILE] # alias for build
remote version
```
17 changes: 17 additions & 0 deletions cmd/remote/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

import (
"fmt"
"os"

"github.com/futrx-com/remote.futrx-cli/internal/cli"
)

var version = "dev"

func main() {
if err := cli.Run(os.Args[1:], os.Stdout, os.Stderr, version); err != nil {
fmt.Fprintln(os.Stderr, "remote:", err)
os.Exit(1)
}
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/futrx-com/remote.futrx-cli

go 1.22
Loading
Loading