diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..576152f40 --- /dev/null +++ b/Makefile @@ -0,0 +1,193 @@ +.PHONY: all build install uninstall clean help test + +# Build variables +BINARY_NAME=picoclaw +BUILD_DIR=build +CMD_DIR=cmd/$(BINARY_NAME) +MAIN_GO=$(CMD_DIR)/main.go + +# Version +VERSION?=$(shell git describe --tags --always --dirty 2>/dev/null || echo "dev") +GIT_COMMIT=$(shell git rev-parse --short=8 HEAD 2>/dev/null || echo "dev") +BUILD_TIME=$(shell date +%FT%T%z) +GO_VERSION=$(shell $(GO) version | awk '{print $$3}') +LDFLAGS=-ldflags "-X main.version=$(VERSION) -X main.gitCommit=$(GIT_COMMIT) -X main.buildTime=$(BUILD_TIME) -X main.goVersion=$(GO_VERSION) -s -w" + +# Go variables +GO?=CGO_ENABLED=0 go +GOFLAGS?=-v -tags stdjson + +# Golangci-lint +GOLANGCI_LINT?=golangci-lint + +# Installation +INSTALL_PREFIX?=$(HOME)/.local +INSTALL_BIN_DIR=$(INSTALL_PREFIX)/bin +INSTALL_MAN_DIR=$(INSTALL_PREFIX)/share/man/man1 +INSTALL_TMP_SUFFIX=.new + +# Workspace and Skills +PICOCLAW_HOME?=$(HOME)/.picoclaw +WORKSPACE_DIR?=$(PICOCLAW_HOME)/workspace +WORKSPACE_SKILLS_DIR=$(WORKSPACE_DIR)/skills +BUILTIN_SKILLS_DIR=$(CURDIR)/skills + +# OS detection +UNAME_S:=$(shell uname -s) +UNAME_M:=$(shell uname -m) + +# Platform-specific settings +ifeq ($(UNAME_S),Linux) + PLATFORM=linux + ifeq ($(UNAME_M),x86_64) + ARCH=amd64 + else ifeq ($(UNAME_M),aarch64) + ARCH=arm64 + else ifeq ($(UNAME_M),loongarch64) + ARCH=loong64 + else ifeq ($(UNAME_M),riscv64) + ARCH=riscv64 + else + ARCH=$(UNAME_M) + endif +else ifeq ($(UNAME_S),Darwin) + PLATFORM=darwin + ifeq ($(UNAME_M),x86_64) + ARCH=amd64 + else ifeq ($(UNAME_M),arm64) + ARCH=arm64 + else + ARCH=$(UNAME_M) + endif +else + PLATFORM=$(UNAME_S) + ARCH=$(UNAME_M) +endif + +BINARY_PATH=$(BUILD_DIR)/$(BINARY_NAME)-$(PLATFORM)-$(ARCH) + +# Default target +all: build + +## generate: Run generate +generate: + @echo "Run generate..." + @rm -r ./$(CMD_DIR)/workspace 2>/dev/null || true + @$(GO) generate ./... + @echo "Run generate complete" + +## build: Build the picoclaw binary for current platform +build: generate + @echo "Building $(BINARY_NAME) for $(PLATFORM)/$(ARCH)..." + @mkdir -p $(BUILD_DIR) + @$(GO) build $(GOFLAGS) $(LDFLAGS) -o $(BINARY_PATH) ./$(CMD_DIR) + @echo "Build complete: $(BINARY_PATH)" + @ln -sf $(BINARY_NAME)-$(PLATFORM)-$(ARCH) $(BUILD_DIR)/$(BINARY_NAME) + +## build-all: Build picoclaw for all platforms +build-all: generate + @echo "Building for multiple platforms..." + @mkdir -p $(BUILD_DIR) + GOOS=linux GOARCH=amd64 $(GO) build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-amd64 ./$(CMD_DIR) + GOOS=linux GOARCH=arm64 $(GO) build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-arm64 ./$(CMD_DIR) + GOOS=linux GOARCH=loong64 $(GO) build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-loong64 ./$(CMD_DIR) + GOOS=linux GOARCH=riscv64 $(GO) build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-riscv64 ./$(CMD_DIR) + GOOS=darwin GOARCH=arm64 $(GO) build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-darwin-arm64 ./$(CMD_DIR) + GOOS=windows GOARCH=amd64 $(GO) build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-windows-amd64.exe ./$(CMD_DIR) + @echo "All builds complete" + +## install: Install picoclaw to system and copy builtin skills +install: build + @echo "Installing $(BINARY_NAME)..." + @mkdir -p $(INSTALL_BIN_DIR) + # Copy binary with temporary suffix to ensure atomic update + @cp $(BUILD_DIR)/$(BINARY_NAME) $(INSTALL_BIN_DIR)/$(BINARY_NAME)$(INSTALL_TMP_SUFFIX) + @chmod +x $(INSTALL_BIN_DIR)/$(BINARY_NAME)$(INSTALL_TMP_SUFFIX) + @mv -f $(INSTALL_BIN_DIR)/$(BINARY_NAME)$(INSTALL_TMP_SUFFIX) $(INSTALL_BIN_DIR)/$(BINARY_NAME) + @echo "Installed binary to $(INSTALL_BIN_DIR)/$(BINARY_NAME)" + @echo "Installation complete!" + +## uninstall: Remove picoclaw from system +uninstall: + @echo "Uninstalling $(BINARY_NAME)..." + @rm -f $(INSTALL_BIN_DIR)/$(BINARY_NAME) + @echo "Removed binary from $(INSTALL_BIN_DIR)/$(BINARY_NAME)" + @echo "Note: Only the executable file has been deleted." + @echo "If you need to delete all configurations (config.json, workspace, etc.), run 'make uninstall-all'" + +## uninstall-all: Remove picoclaw and all data +uninstall-all: + @echo "Removing workspace and skills..." + @rm -rf $(PICOCLAW_HOME) + @echo "Removed workspace: $(PICOCLAW_HOME)" + @echo "Complete uninstallation done!" + +## clean: Remove build artifacts +clean: + @echo "Cleaning build artifacts..." + @rm -rf $(BUILD_DIR) + @echo "Clean complete" + +## vet: Run go vet for static analysis +vet: + @$(GO) vet ./... + +## test: Test Go code +test: + @$(GO) test ./... + +## fmt: Format Go code +fmt: + @$(GOLANGCI_LINT) fmt + +## lint: Run linters +lint: + @$(GOLANGCI_LINT) run + +## fix: Fix linting issues +fix: + @$(GOLANGCI_LINT) run --fix + +## deps: Download dependencies +deps: + @$(GO) mod download + @$(GO) mod verify + +## update-deps: Update dependencies +update-deps: + @$(GO) get -u ./... + @$(GO) mod tidy + +## check: Run vet, fmt, and verify dependencies +check: deps fmt vet test + +## run: Build and run picoclaw +run: build + @$(BUILD_DIR)/$(BINARY_NAME) $(ARGS) + +## help: Show this help message +help: + @echo "picoclaw Makefile" + @echo "" + @echo "Usage:" + @echo " make [target]" + @echo "" + @echo "Targets:" + @grep -E '^## ' $(MAKEFILE_LIST) | sort | awk -F': ' '{printf " %-16s %s\n", substr($$1, 4), $$2}' + @echo "" + @echo "Examples:" + @echo " make build # Build for current platform" + @echo " make install # Install to ~/.local/bin" + @echo " make uninstall # Remove from /usr/local/bin" + @echo " make install-skills # Install skills to workspace" + @echo "" + @echo "Environment Variables:" + @echo " INSTALL_PREFIX # Installation prefix (default: ~/.local)" + @echo " WORKSPACE_DIR # Workspace directory (default: ~/.picoclaw/workspace)" + @echo " VERSION # Version string (default: git describe)" + @echo "" + @echo "Current Configuration:" + @echo " Platform: $(PLATFORM)/$(ARCH)" + @echo " Binary: $(BINARY_PATH)" + @echo " Install Prefix: $(INSTALL_PREFIX)" + @echo " Workspace: $(WORKSPACE_DIR)" diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100755 index f894530c5..000000000 --- a/scripts/build.sh +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# ── project constants ──────────────────────────────────────── -BINARY_NAME="picoclaw" -CMD_DIR="cmd/${BINARY_NAME}" -BUILD_DIR="build" - -# ── Go flags ───────────────────────────────────────────────── -GO="${GO:-go}" -GOFLAGS="${GOFLAGS:--v -tags stdjson}" - -# ── version info (injected via ldflags) ────────────────────── -VERSION="${VERSION:-$(git describe --tags --always --dirty 2>/dev/null || echo "dev")}" -GIT_COMMIT="$(git rev-parse --short=8 HEAD 2>/dev/null || echo "dev")" -BUILD_TIME="$(date +%FT%T%z)" -GO_VERSION="$($GO version | awk '{print $3}')" -LDFLAGS="-X main.version=${VERSION} -X main.gitCommit=${GIT_COMMIT} -X main.buildTime=${BUILD_TIME} -X main.goVersion=${GO_VERSION} -s -w" - -# ── platform detection ─────────────────────────────────────── -detect_platform() { - local os arch - os="$(uname -s | tr '[:upper:]' '[:lower:]')" - arch="$(uname -m)" - - case "$arch" in - x86_64) arch="amd64" ;; - aarch64) arch="arm64" ;; - loongarch64) arch="loong64" ;; - esac - - echo "${os} ${arch}" -} - -# ── generate ───────────────────────────────────────────────── -generate() { - echo "Run generate..." - rm -rf "./${CMD_DIR}/workspace" 2>/dev/null || true - $GO generate ./... - echo "Run generate complete" -} - -# ── build single platform ──────────────────────────────────── -build_one() { - local goos="$1" goarch="$2" - local suffix="${BINARY_NAME}-${goos}-${goarch}" - [[ "$goos" == "windows" ]] && suffix+=".exe" - - echo "Building ${BINARY_NAME} for ${goos}/${goarch}..." - mkdir -p "$BUILD_DIR" - GOOS="$goos" GOARCH="$goarch" $GO build $GOFLAGS -ldflags "$LDFLAGS" -o "${BUILD_DIR}/${suffix}" "./${CMD_DIR}" - echo "Build complete: ${BUILD_DIR}/${suffix}" -} - -# ── build current platform ─────────────────────────────────── -build_current() { - generate - read -r os arch <<< "$(detect_platform)" - build_one "$os" "$arch" - ln -sf "${BINARY_NAME}-${os}-${arch}" "${BUILD_DIR}/${BINARY_NAME}" -} - -# ── build all platforms ────────────────────────────────────── -build_all() { - generate - echo "Building for multiple platforms..." - mkdir -p "$BUILD_DIR" - build_one linux amd64 - build_one linux arm64 - build_one linux loong64 - build_one linux riscv64 - build_one darwin arm64 - build_one windows amd64 - echo "All builds complete" -} - -# ── clean ──────────────────────────────────────────────────── -clean() { - echo "Cleaning build artifacts..." - rm -rf "$BUILD_DIR" - echo "Clean complete" -} - -# ── main ───────────────────────────────────────────────────── -case "${1:-}" in - --all) build_all ;; - --clean) clean ;; - *) build_current ;; -esac diff --git a/scripts/check.sh b/scripts/check.sh deleted file mode 100755 index af02c11e9..000000000 --- a/scripts/check.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -GO="${GO:-go}" -GOLANGCI_LINT="${GOLANGCI_LINT:-golangci-lint}" - -# ── individual checks ──────────────────────────────────────── -do_deps() { - echo "==> Downloading dependencies..." - $GO mod download - $GO mod verify -} - -do_fmt() { - echo "==> Formatting..." - $GOLANGCI_LINT fmt -} - -do_vet() { - echo "==> Running vet..." - $GO vet ./... -} - -do_test() { - echo "==> Running tests..." - $GO test ./... -} - -do_lint() { - echo "==> Running linter..." - $GOLANGCI_LINT run -} - -do_all() { - do_deps - do_fmt - do_vet - do_test - echo "" - echo "All checks passed." -} - -# ── main ───────────────────────────────────────────────────── -case "${1:-}" in - test) do_test ;; - lint) do_lint ;; - fmt) do_fmt ;; - vet) do_vet ;; - deps) do_deps ;; - "") do_all ;; - *) - echo "Usage: $(basename "$0") [test|lint|fmt|vet|deps]" - echo " (no argument runs all checks)" - exit 1 - ;; -esac diff --git a/scripts/deploy.sh b/scripts/deploy.sh deleted file mode 100755 index 2d01dfd63..000000000 --- a/scripts/deploy.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -BINARY_NAME="picoclaw" -BUILD_DIR="build" -INSTALL_BIN_DIR="${HOME}/.local/bin" -GATEWAY_LOG="/tmp/picoclaw-gateway.log" -HEALTH_URL="http://localhost:18790/health" - -# ── step 1: build ──────────────────────────────────────────── -echo "==> Building..." -"${SCRIPT_DIR}/build.sh" - -# ── step 2: stop old gateway ───────────────────────────────── -OLD_PID=$(pgrep -f "${BINARY_NAME} gateway" 2>/dev/null || true) - -if [[ -n "$OLD_PID" ]]; then - echo "==> Stopping gateway (PID ${OLD_PID})..." - kill "$OLD_PID" 2>/dev/null || true - - # Wait up to 5 seconds for graceful shutdown - for i in $(seq 1 10); do - if ! kill -0 "$OLD_PID" 2>/dev/null; then - break - fi - sleep 0.5 - done - - # Force kill if still running - if kill -0 "$OLD_PID" 2>/dev/null; then - echo " Force killing..." - kill -9 "$OLD_PID" 2>/dev/null || true - sleep 0.5 - fi - echo " Gateway stopped." -else - echo "==> No running gateway found." -fi - -# ── step 3: install new binary ─────────────────────────────── -echo "==> Installing..." -mkdir -p "$INSTALL_BIN_DIR" -cp "${BUILD_DIR}/${BINARY_NAME}" "${INSTALL_BIN_DIR}/${BINARY_NAME}.new" -chmod +x "${INSTALL_BIN_DIR}/${BINARY_NAME}.new" -mv -f "${INSTALL_BIN_DIR}/${BINARY_NAME}.new" "${INSTALL_BIN_DIR}/${BINARY_NAME}" -echo " Installed to ${INSTALL_BIN_DIR}/${BINARY_NAME}" - -# ── step 4: start new gateway ──────────────────────────────── -echo "==> Starting gateway..." -nohup "${INSTALL_BIN_DIR}/${BINARY_NAME}" gateway > "$GATEWAY_LOG" 2>&1 & -NEW_PID=$! -echo " PID: ${NEW_PID}" -echo " Log: ${GATEWAY_LOG}" - -# ── step 5: health check ───────────────────────────────────── -echo "==> Waiting for health check..." -sleep 2 - -if curl -sf "$HEALTH_URL" > /dev/null 2>&1; then - echo " Health check passed." - echo "" - echo "Deploy complete. Gateway running as PID ${NEW_PID}." -else - echo " Health check failed (gateway may still be starting)." - echo " Check logs: tail -f ${GATEWAY_LOG}" -fi diff --git a/scripts/docker.sh b/scripts/docker.sh deleted file mode 100755 index 8fe07e4b6..000000000 --- a/scripts/docker.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -IMAGE_NAME="picoclaw" -TAG="${1:-latest}" - -# ── version from git ───────────────────────────────────────── -if [[ "$TAG" == "latest" ]]; then - GIT_TAG="$(git describe --tags --always --dirty 2>/dev/null || echo "dev")" -else - GIT_TAG="$TAG" -fi - -echo "==> Building Docker image: ${IMAGE_NAME}:${TAG}" -echo " Version: ${GIT_TAG}" - -docker build \ - -t "${IMAGE_NAME}:${TAG}" \ - -t "${IMAGE_NAME}:${GIT_TAG}" \ - . - -echo "" -echo "Build complete:" -echo " ${IMAGE_NAME}:${TAG}" -echo " ${IMAGE_NAME}:${GIT_TAG}" -echo "" -echo "Run with:" -echo " docker run --rm ${IMAGE_NAME}:${TAG} version" -echo " docker run -v config.json:/home/picoclaw/.picoclaw/config.json:ro ${IMAGE_NAME}:${TAG} gateway" diff --git a/scripts/install.sh b/scripts/install.sh deleted file mode 100755 index 05b8df5f9..000000000 --- a/scripts/install.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -BINARY_NAME="picoclaw" -BUILD_DIR="build" -INSTALL_PREFIX="${HOME}/.local" -PICOCLAW_HOME="${HOME}/.picoclaw" - -# ── parse args ─────────────────────────────────────────────── -ACTION="install" -while [[ $# -gt 0 ]]; do - case "$1" in - --prefix) INSTALL_PREFIX="$2"; shift 2 ;; - --uninstall) ACTION="uninstall"; shift ;; - --uninstall-all) ACTION="uninstall-all"; shift ;; - *) echo "Unknown option: $1"; exit 1 ;; - esac -done - -INSTALL_BIN_DIR="${INSTALL_PREFIX}/bin" - -# ── install ────────────────────────────────────────────────── -do_install() { - "${SCRIPT_DIR}/build.sh" - - echo "Installing ${BINARY_NAME}..." - mkdir -p "$INSTALL_BIN_DIR" - - # Atomic install: copy to temp, then rename - cp "${BUILD_DIR}/${BINARY_NAME}" "${INSTALL_BIN_DIR}/${BINARY_NAME}.new" - chmod +x "${INSTALL_BIN_DIR}/${BINARY_NAME}.new" - mv -f "${INSTALL_BIN_DIR}/${BINARY_NAME}.new" "${INSTALL_BIN_DIR}/${BINARY_NAME}" - - echo "Installed to ${INSTALL_BIN_DIR}/${BINARY_NAME}" -} - -# ── uninstall ──────────────────────────────────────────────── -do_uninstall() { - echo "Uninstalling ${BINARY_NAME}..." - rm -f "${INSTALL_BIN_DIR}/${BINARY_NAME}" - echo "Removed binary from ${INSTALL_BIN_DIR}/${BINARY_NAME}" - echo "Note: config and workspace preserved. Use --uninstall-all to remove everything." -} - -# ── uninstall-all ──────────────────────────────────────────── -do_uninstall_all() { - do_uninstall - echo "Removing workspace and config..." - rm -rf "$PICOCLAW_HOME" - echo "Removed ${PICOCLAW_HOME}" - echo "Complete uninstallation done!" -} - -# ── main ───────────────────────────────────────────────────── -case "$ACTION" in - install) do_install ;; - uninstall) do_uninstall ;; - uninstall-all) do_uninstall_all ;; -esac diff --git a/scripts/setup.sh b/scripts/setup.sh deleted file mode 100755 index 1ccccc36b..000000000 --- a/scripts/setup.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -REQUIRED_GO_MAJOR=1 -REQUIRED_GO_MINOR=25 -PICOCLAW_HOME="${HOME}/.picoclaw" - -passed=0 -failed=0 - -check() { - local name="$1" ok="$2" msg="$3" - if [[ "$ok" == "true" ]]; then - echo " [ok] ${name}: ${msg}" - passed=$((passed + 1)) - else - echo " [!!] ${name}: ${msg}" - failed=$((failed + 1)) - fi -} - -echo "PicoClaw Environment Setup" -echo "==========================" -echo "" - -# ── Go ─────────────────────────────────────────────────────── -echo "Checking dependencies..." -if command -v go &>/dev/null; then - go_ver="$(go version | awk '{print $3}' | sed 's/go//')" - go_major="${go_ver%%.*}" - go_minor="${go_ver#*.}" - go_minor="${go_minor%%.*}" - - if [[ "$go_major" -gt "$REQUIRED_GO_MAJOR" ]] || \ - { [[ "$go_major" -eq "$REQUIRED_GO_MAJOR" ]] && [[ "$go_minor" -ge "$REQUIRED_GO_MINOR" ]]; }; then - check "Go" "true" "go${go_ver}" - else - check "Go" "false" "go${go_ver} (need >= ${REQUIRED_GO_MAJOR}.${REQUIRED_GO_MINOR})" - fi -else - check "Go" "false" "not installed (https://go.dev/dl/)" -fi - -# ── golangci-lint ──────────────────────────────────────────── -if command -v golangci-lint &>/dev/null; then - lint_ver="$(golangci-lint version --format short 2>/dev/null || echo "unknown")" - check "golangci-lint" "true" "v${lint_ver}" -else - check "golangci-lint" "false" "not installed (https://golangci-lint.run/welcome/install/)" -fi - -# ── git ────────────────────────────────────────────────────── -if command -v git &>/dev/null; then - git_ver="$(git --version | awk '{print $3}')" - check "git" "true" "${git_ver}" -else - check "git" "false" "not installed" -fi - -# ── curl (for health checks) ──────────────────────────────── -if command -v curl &>/dev/null; then - check "curl" "true" "available" -else - check "curl" "false" "not installed (needed for deploy health checks)" -fi - -echo "" - -# ── download dependencies ──────────────────────────────────── -if command -v go &>/dev/null; then - echo "Downloading Go dependencies..." - go mod download - go mod verify - echo " Dependencies ready." - echo "" -fi - -# ── picoclaw onboard ──────────────────────────────────────── -if [[ ! -d "$PICOCLAW_HOME" ]]; then - if command -v picoclaw &>/dev/null; then - echo "Running picoclaw onboard..." - picoclaw onboard - echo "" - else - echo "Note: Run 'picoclaw onboard' after first install to initialize workspace." - echo "" - fi -else - echo "Workspace: ${PICOCLAW_HOME} (exists)" - echo "" -fi - -# ── summary ────────────────────────────────────────────────── -echo "==========================" -if [[ "$failed" -eq 0 ]]; then - echo "All checks passed (${passed}/${passed})." - echo "Ready to build: scripts/build.sh" -else - echo "${passed} passed, ${failed} failed." - echo "Fix the issues above, then re-run this script." -fi