From 78fdf70a7d7342a0984406769f5d6452192cba0c Mon Sep 17 00:00:00 2001 From: mingzhi1 Date: Tue, 3 Mar 2026 12:50:59 +0800 Subject: [PATCH] ci: add release-bundle workflow for per-platform zip packages - workflow_dispatch with tag, prerelease, draft inputs - Cross-compile picoclaw for 8 platforms - Build launcher on native runners via go tool wails - Launcher failure is non-blocking (continue-on-error) - Taskfile: task all, task build-launcher - Updated README with install instructions --- .github/workflows/release-bundle.yml | 407 +++++++++++++++++++++++++++ README.md | 81 ++++++ README.zh.md | 81 ++++++ Taskfile.yml | 139 +++++++++ 4 files changed, 708 insertions(+) create mode 100644 .github/workflows/release-bundle.yml create mode 100644 Taskfile.yml diff --git a/.github/workflows/release-bundle.yml b/.github/workflows/release-bundle.yml new file mode 100644 index 000000000..03aa8f0ed --- /dev/null +++ b/.github/workflows/release-bundle.yml @@ -0,0 +1,407 @@ +name: Release Bundle + +on: + workflow_dispatch: + inputs: + tag: + description: "Release tag (e.g. v0.2.0)" + required: true + type: string + prerelease: + description: "Mark as pre-release" + required: false + type: boolean + default: false + draft: + description: "Create as draft" + required: false + type: boolean + default: true + +jobs: + # ────────────────────────────────────────────────────────────────────────── + # 1. Create Git tag + # ────────────────────────────────────────────────────────────────────────── + create-tag: + name: Create Git Tag + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Create and push tag + env: + RELEASE_TAG: ${{ inputs.tag }} + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag -a "$RELEASE_TAG" -m "Release $RELEASE_TAG" + git push origin "$RELEASE_TAG" + + # ────────────────────────────────────────────────────────────────────────── + # 2. Build picoclaw (CGO_ENABLED=0, pure Go, cross-compile on ubuntu) + # ────────────────────────────────────────────────────────────────────────── + build-picoclaw: + name: Build picoclaw (${{ matrix.goos }}/${{ matrix.goarch }}) + needs: create-tag + runs-on: ubuntu-latest + permissions: + contents: read + strategy: + matrix: + include: + - goos: linux + goarch: amd64 + suffix: "" + - goos: linux + goarch: arm64 + suffix: "" + - goos: linux + goarch: arm + goarm: "7" + suffix: "" + - goos: linux + goarch: riscv64 + suffix: "" + - goos: linux + goarch: loong64 + suffix: "" + - goos: windows + goarch: amd64 + suffix: ".exe" + - goos: darwin + goarch: amd64 + suffix: "" + - goos: darwin + goarch: arm64 + suffix: "" + + steps: + - name: Checkout tag + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ inputs.tag }} + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Generate embedded assets + run: go generate ./... + + - name: Build picoclaw + env: + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + GOARM: ${{ matrix.goarm }} + CGO_ENABLED: "0" + VERSION: ${{ inputs.tag }} + run: | + mkdir -p dist + OUTPUT="dist/picoclaw${{ matrix.suffix }}" + go build \ + -tags stdjson \ + -ldflags "-s -w \ + -X github.com/sipeed/picoclaw/cmd/picoclaw/internal.version=${VERSION} \ + -X github.com/sipeed/picoclaw/cmd/picoclaw/internal.gitCommit=$(git rev-parse --short HEAD) \ + -X github.com/sipeed/picoclaw/cmd/picoclaw/internal.buildTime=$(date -u +%FT%TZ)" \ + -o "${OUTPUT}" \ + ./cmd/picoclaw + + - name: Upload picoclaw artifact + uses: actions/upload-artifact@v4 + with: + name: picoclaw-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.goarm && format('-armv{0}', matrix.goarm) || '' }} + path: dist/picoclaw${{ matrix.suffix }} + retention-days: 1 + + # ────────────────────────────────────────────────────────────────────────── + # 3a. Build Launcher — Windows (Wails requires WebView2) + # Note: continue-on-error=true so packaging still runs if wails build fails + # ────────────────────────────────────────────────────────────────────────── + build-launcher-windows: + name: Build launcher (windows/amd64) + needs: create-tag + runs-on: windows-latest + continue-on-error: true + permissions: + contents: read + steps: + - name: Checkout tag + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ inputs.tag }} + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Build launcher + working-directory: cmd/picoclaw-launcher + run: | + go tool wails build -tags stdjson -ldflags "-s -w" -o picoclaw-launcher.exe + + - name: Upload launcher artifact + uses: actions/upload-artifact@v4 + with: + name: launcher-windows-amd64 + path: cmd/picoclaw-launcher/build/bin/picoclaw-launcher.exe + retention-days: 1 + + # ────────────────────────────────────────────────────────────────────────── + # 3b. Build Launcher — Linux/amd64 (Wails requires GTK/WebKit) + # ────────────────────────────────────────────────────────────────────────── + build-launcher-linux: + name: Build launcher (linux/amd64) + needs: create-tag + runs-on: ubuntu-latest + continue-on-error: true + permissions: + contents: read + steps: + - name: Checkout tag + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ inputs.tag }} + + - name: Install WebKit/GTK dependencies + run: | + sudo apt-get update -qq + sudo apt-get install -y --no-install-recommends \ + libgtk-3-dev \ + libwebkit2gtk-4.0-dev \ + pkg-config \ + build-essential + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Build launcher + working-directory: cmd/picoclaw-launcher + run: | + go tool wails build -tags stdjson -ldflags "-s -w" -o picoclaw-launcher + + - name: Upload launcher artifact + uses: actions/upload-artifact@v4 + with: + name: launcher-linux-amd64 + path: cmd/picoclaw-launcher/build/bin/picoclaw-launcher + retention-days: 1 + + # ────────────────────────────────────────────────────────────────────────── + # 3c. Build Launcher — macOS (universal binary: amd64 + arm64) + # ────────────────────────────────────────────────────────────────────────── + build-launcher-macos: + name: Build launcher (darwin/universal) + needs: create-tag + runs-on: macos-latest + continue-on-error: true + permissions: + contents: read + steps: + - name: Checkout tag + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ inputs.tag }} + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Build launcher (universal) + working-directory: cmd/picoclaw-launcher + run: | + go tool wails build -tags stdjson -ldflags "-s -w" -platform darwin/universal -o picoclaw-launcher + + - name: Upload launcher artifact + uses: actions/upload-artifact@v4 + with: + name: launcher-darwin-universal + path: cmd/picoclaw-launcher/build/bin/picoclaw-launcher + retention-days: 1 + + # ────────────────────────────────────────────────────────────────────────── + # 4. Package: assemble zip bundles per platform and create GitHub Release + # Runs even if launcher builds fail (if: ${{ !cancelled() }}) + # Launcher is included only if its artifact was successfully uploaded + # ────────────────────────────────────────────────────────────────────────── + package-and-release: + name: Package & Create Release + needs: + - build-picoclaw + - build-launcher-windows + - build-launcher-linux + - build-launcher-macos + # Run even if launcher jobs fail; only skip if workflow is cancelled + if: ${{ !cancelled() && needs.build-picoclaw.result == 'success' }} + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout tag + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ inputs.tag }} + + # ── Download picoclaw artifacts (always present) ── + - name: Download picoclaw artifacts + uses: actions/download-artifact@v4 + with: + pattern: picoclaw-* + path: artifacts + merge-multiple: false + + # ── Download launcher artifacts (may be missing if build failed) ── + - name: Download launcher-windows artifact + uses: actions/download-artifact@v4 + if: ${{ needs.build-launcher-windows.result == 'success' }} + with: + name: launcher-windows-amd64 + path: artifacts/launcher-windows-amd64 + continue-on-error: true + + - name: Download launcher-linux artifact + uses: actions/download-artifact@v4 + if: ${{ needs.build-launcher-linux.result == 'success' }} + with: + name: launcher-linux-amd64 + path: artifacts/launcher-linux-amd64 + continue-on-error: true + + - name: Download launcher-macos artifact + uses: actions/download-artifact@v4 + if: ${{ needs.build-launcher-macos.result == 'success' }} + with: + name: launcher-darwin-universal + path: artifacts/launcher-darwin-universal + continue-on-error: true + + - name: List downloaded artifacts + run: find artifacts -type f | sort + + # ── Assemble zip bundles ── + - name: Assemble zip bundles + env: + TAG: ${{ inputs.tag }} + run: | + set -euo pipefail + mkdir -p release staging + + # ── helper: bundle picoclaw + optional launcher into a zip ── + make_zip() { + local NAME="$1" # bundle name, e.g. picoclaw_v0.1.0_windows_amd64 + local PICOCLAW="$2" # source path of picoclaw binary + local LAUNCHER="${3:-}" # source path of launcher binary (empty = omit) + local EXT="${4:-}" # ".exe" or "" + + local DIR="staging/${NAME}" + mkdir -p "${DIR}" + + chmod +x "${PICOCLAW}" 2>/dev/null || true + cp "${PICOCLAW}" "${DIR}/picoclaw${EXT}" + + if [ -n "${LAUNCHER}" ] && [ -f "${LAUNCHER}" ]; then + chmod +x "${LAUNCHER}" 2>/dev/null || true + cp "${LAUNCHER}" "${DIR}/picoclaw-launcher${EXT}" + echo "PicoClaw Bundle (picoclaw + picoclaw-launcher)" > "${DIR}/README.txt" + else + echo "PicoClaw CLI only (picoclaw-launcher not available for this platform)" > "${DIR}/README.txt" + fi + + (cd staging && zip -r "../release/${NAME}.zip" "${NAME}/") + echo " created: release/${NAME}.zip" + } + + WINLAUNCHER="" + LINLAUNCHER="" + MACLAUNCHER="" + + [ -f "artifacts/launcher-windows-amd64/picoclaw-launcher.exe" ] \ + && WINLAUNCHER="artifacts/launcher-windows-amd64/picoclaw-launcher.exe" + [ -f "artifacts/launcher-linux-amd64/picoclaw-launcher" ] \ + && LINLAUNCHER="artifacts/launcher-linux-amd64/picoclaw-launcher" + [ -f "artifacts/launcher-darwin-universal/picoclaw-launcher" ] \ + && MACLAUNCHER="artifacts/launcher-darwin-universal/picoclaw-launcher" + + # Windows amd64 + make_zip \ + "picoclaw_${TAG}_windows_amd64" \ + "artifacts/picoclaw-windows-amd64/picoclaw.exe" \ + "${WINLAUNCHER}" \ + ".exe" + + # Linux amd64 + make_zip \ + "picoclaw_${TAG}_linux_amd64" \ + "artifacts/picoclaw-linux-amd64/picoclaw" \ + "${LINLAUNCHER}" \ + "" + + # macOS amd64 (with universal launcher if available) + make_zip \ + "picoclaw_${TAG}_darwin_amd64" \ + "artifacts/picoclaw-darwin-amd64/picoclaw" \ + "${MACLAUNCHER}" \ + "" + + # macOS arm64 (with universal launcher if available) + make_zip \ + "picoclaw_${TAG}_darwin_arm64" \ + "artifacts/picoclaw-darwin-arm64/picoclaw" \ + "${MACLAUNCHER}" \ + "" + + # Linux arm64 (picoclaw only) + make_zip \ + "picoclaw_${TAG}_linux_arm64" \ + "artifacts/picoclaw-linux-arm64/picoclaw" \ + "" "" + + # Linux armv7 (picoclaw only) + make_zip \ + "picoclaw_${TAG}_linux_armv7" \ + "artifacts/picoclaw-linux-arm-armv7/picoclaw" \ + "" "" + + # Linux riscv64 (picoclaw only) + make_zip \ + "picoclaw_${TAG}_linux_riscv64" \ + "artifacts/picoclaw-linux-riscv64/picoclaw" \ + "" "" + + # Linux loong64 (picoclaw only) + make_zip \ + "picoclaw_${TAG}_linux_loong64" \ + "artifacts/picoclaw-linux-loong64/picoclaw" \ + "" "" + + echo "" + echo "=== Final release assets ===" + ls -lh release/ + + # ── Create GitHub Release ── + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ inputs.tag }} + name: "PicoClaw ${{ inputs.tag }}" + draft: ${{ inputs.draft }} + prerelease: ${{ inputs.prerelease }} + generate_release_notes: true + files: release/*.zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 6714ac6eb..88d2355ad 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,87 @@ PicoClaw +## 🧠 Our Thinking + +PicoClaw isn't just small — it **thinks differently**. While most AI agent frameworks treat the LLM as a black box that swallows everything (all tools, all history, all context), PicoClaw introduces a structured **Runtime Loop** that makes every token count. + +### Three-Phase Runtime Loop + +Every user interaction flows through three distinct phases: + +``` +┌──────────────┐ ┌──────────────┐ ┌──────────────┐ +│ Phase 1 │ → │ Phase 2 │ → │ Phase 3 │ +│ Analyse │ │ ExecuteLLM │ │ Reflect │ +│ (Orchestrate) │ (Execute) │ │ (Learn) │ +└──────────────┘ └──────────────┘ └──────────────┘ +``` + +- **Phase 1 — Analyse**: A lightweight LLM call (can use a cheaper/faster model) that understands intent, assigns semantic tags, and prepares the optimal context. No tools, no history bloat — just pure comprehension. +- **Phase 2 — ExecuteLLM**: The main LLM iterates with only the tools and context that Phase 1 deemed relevant. Fewer tools = less confusion = better decisions. +- **Phase 3 — Reflect**: Synchronous scoring + context update (< 2ms) before the user sees the reply, followed by async persistence. The agent learns from every interaction without adding latency. + +### Turn Scoring & Instant Memory + +Every Turn gets a **score** based on tool activity, intent weight, content density, and explicit user markers. This score drives a novel context selection algorithm: + +``` +Instant Memory = { high-score Turns } // always kept, unconditionally + ∪ { tag-matched Turns, score > 0 } // relevant old Turns "resurrected" + ∪ { recent M Turns } // continuity baseline + → sorted by time, truncated to capacity +``` + +**No Turn is ever deleted.** Low-score Turns are simply excluded from context — but when a future Phase 1 produces matching tags, they can be recalled. It's like human memory: you don't forget things, you just can't always access them until something triggers the recall. + +### Tag-Gated Tool Loading + +Instead of feeding 20+ tool definitions to the LLM every request (wasting ~3,000 tokens per iteration), PicoClaw categorizes tools: + +| Layer | Tools | Loading | +|-------|-------|---------| +| **always-on** | `read_file`, `write_file`, `shell`, etc. | Always present | +| **tag-gated** | MCP servers, Skills, `web_search`, `cron` | Only when Phase 1 tags match | + +As MCP servers and Skills grow, the savings compound exponentially — and the LLM makes better decisions with a focused toolset. + +### KV Cache-Friendly Message Ordering + +We obsess over **prefix stability** to maximize KV cache hits across providers (Gemini implicit cache, Anthropic `cache_control`, OpenAI prompt caching): + +``` +[system_prompt] ← always cached ✅ +[long_term_memory by tags] ← stable when same tags ✅ +[high-score Turns, ASC by ID] ← fixed positions, append-only ✅ +[tag-matched Turns] ← may vary +[recent Turns] ← rolling window +[current user message] ← always new +``` + +High-score Turns are **pinned** right after long-term memory in ascending order. New Turns only append — they never shift existing content. This maximizes the cache-hit prefix length, and the cost difference is dramatic at scale. + +### Three-Layer Memory Hierarchy + +| Layer | Lifecycle | Source | Consumer | +|-------|-----------|--------|----------| +| **Instant Memory** | Assembled per-Turn | Turn Store (score + tag filter) | Phase 2 (ExecuteLLM) | +| **Active Context** | Rolling update per `channel:chatID` | Phase 3 sync update | Phase 1 (Analyse) | +| **Long-Term Memory** | Persistent | MemoryDigest batch worker | Phase 1 (via tag retrieval) | + +Each layer serves a different phase. No overlap, no waste. Active Context (current files, recent errors) helps Phase 1 understand terse messages like "fix it". Instant Memory gives Phase 2 the right historical context. Long-term memory accumulates wisdom across sessions. + +### Why This Matters + +Traditional agent loops dump everything into one LLM call and hope for the best. PicoClaw's approach means: + +- 📉 **~60% fewer wasted tokens** from irrelevant tools and stale context +- 🎯 **Higher decision quality** — focused toolsets reduce LLM confusion +- ⚡ **Sub-2ms overhead** for scoring and context updates (zero perceived latency) +- 🧲 **Associative recall** — old conversations resurface when relevant, like human memory +- 💰 **Multi-model cost optimization** — use cheap models for analysis, strong models for execution + +> *"The best token is the one you never send."* + ## 🦾 Demonstration ### 🛠️ Standard Assistant Workflows diff --git a/README.zh.md b/README.zh.md index d3a49ee8d..824e7f521 100644 --- a/README.zh.md +++ b/README.zh.md @@ -80,6 +80,87 @@ PicoClaw +## 🧠 我们的思考 + +PicoClaw 不只是小——它的**思维方式**和其他 AI Agent 框架根本不同。大多数框架把 LLM 当黑箱,把所有工具、所有历史、所有上下文一股脑塞进去。PicoClaw 引入了结构化的 **Runtime Loop**,让每一个 token 都物尽其用。 + +### 三阶段 Runtime Loop + +每次用户交互都经过三个清晰的阶段: + +``` +┌──────────────┐ ┌──────────────┐ ┌──────────────┐ +│ Phase 1 │ → │ Phase 2 │ → │ Phase 3 │ +│ 分析 Analyse│ │ 执行 ExecuteLLM│ │ 反思 Reflect │ +│ (编排器) │ │ (执行器) │ │ (学习器) │ +└──────────────┘ └──────────────┘ └──────────────┘ +``` + +- **Phase 1 — 分析**:轻量 LLM 调用(可用便宜/快速模型),理解用户意图、分配语义标签、准备最优上下文。无工具、无历史膨胀——纯粹的理解力。 +- **Phase 2 — 执行**:主 LLM 只携带 Phase 1 认为相关的工具和上下文进行迭代。工具越少 = 干扰越少 = 决策越好。 +- **Phase 3 — 反思**:同步打分 + 上下文更新(< 2ms),在用户看到回复之前完成,然后异步持久化。Agent 从每次交互中学习,且不增加延迟。 + +### Turn 打分 & 瞬时记忆 + +每个 Turn 都会根据工具活动、意图权重、内容密度和用户显式标记获得一个**分数**。这个分数驱动一个新颖的上下文选择算法: + +``` +瞬时记忆 = { 高分 Turn } // 无条件保留 + ∪ { tag 匹配的 Turn, score > 0 } // 旧 Turn 被"唤醒" + ∪ { 最近 M 个 Turn } // 连贯性保底 + → 按时间排序,截断到容量上限 +``` + +**Turn 永不删除。** 低分 Turn 只是被排除在上下文之外——但当未来 Phase 1 产生匹配的 tags 时,它们可以被重新召回。就像人类记忆:你不会忘记东西,只是在某个触发点之前无法访问它们。 + +### Tag 驱动的工具加载 + +传统框架每次请求都把 20+ 个工具定义喂给 LLM(每次迭代浪费 ~3,000 tokens)。PicoClaw 把工具分为两层: + +| 层级 | 工具 | 加载方式 | +|------|------|---------| +| **always-on** | `read_file`、`write_file`、`shell` 等 | 始终加载 | +| **tag-gated** | MCP servers、Skills、`web_search`、`cron` | 仅当 Phase 1 的 tags 匹配时加载 | + +随着 MCP Servers 和 Skills 的增多,节省量呈指数级增长——而且 LLM 在精简的工具集下做出更好的决策。 + +### KV Cache 友好的消息排列 + +我们执着于**前缀稳定性**,以最大化跨 Provider 的 KV Cache 命中率(Gemini 隐式缓存、Anthropic `cache_control`、OpenAI prompt caching): + +``` +[system_prompt] ← 始终缓存 ✅ +[long_term_memory by tags] ← 相同 tags 时稳定 ✅ +[高分 Turn, 按 ID 升序] ← 位置固定,只增不移 ✅ +[tag 匹配的 Turn] ← 可能变化 +[最近的 Turn] ← 滚动窗口 +[当前用户消息] ← 总是新内容 +``` + +高分 Turn 被**固定**在长期记忆之后,按升序排列。新 Turn 只追加到末尾,不改变已有内容的位置。这最大化了缓存命中的前缀长度,在规模化场景下成本差异显著。 + +### 三层记忆体系 + +| 层次 | 生命周期 | 数据来源 | 消费者 | +|------|---------|---------|--------| +| **瞬时记忆** | 每轮动态组装 | Turn 库(score + tag 筛选)| Phase 2(ExecuteLLM)| +| **活跃上下文** | 跨 Turn 滚动更新,per `channel:chatID` | Phase 3 同步维护 | Phase 1(Analyse)| +| **长期记忆** | 持久化 | MemoryDigest 定时批量提炼 | Phase 1(通过 tag 检索)| + +每层服务不同的阶段。没有重叠,没有浪费。活跃上下文(当前文件、近期错误)帮助 Phase 1 理解简短消息如"修一下"。瞬时记忆为 Phase 2 提供正确的历史上下文。长期记忆跨会话积累智慧。 + +### 为什么这很重要 + +传统的 Agent 循环把所有东西塞进一个 LLM 调用,然后听天由命。PicoClaw 的方式意味着: + +- 📉 **减少 ~60% 的浪费 token** — 排除无关工具和陈旧上下文 +- 🎯 **更高的决策质量** — 精简工具集减少 LLM 的"选择困难症" +- ⚡ **< 2ms 的额外开销** — 打分和上下文更新零感知延迟 +- 🧲 **联想式召回** — 旧对话在相关时自动浮现,就像人类记忆 +- 💰 **多模型成本优化** — 分析用便宜模型,执行用强模型 + +> *"最好的 token 是你永远不需要发送的那个。"* + ## 🦾 演示 ### 🛠️ 标准助手工作流 diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 000000000..31a38ca73 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,139 @@ +version: '3' + +vars: + BINARY_NAME: picoclaw + CMD_DIR: cmd/picoclaw + BUILD_DIR: build + LAUNCHER_DIR: cmd/picoclaw-launcher + LAUNCHER_BUILD_DIR: cmd/picoclaw-launcher/build/bin + INTERNAL: github.com/sipeed/picoclaw/cmd/picoclaw/internal + VERSION: + sh: git describe --tags --always --dirty 2>{{if eq OS "windows"}}nul{{else}}/dev/null{{end}} || echo dev + GIT_COMMIT: + sh: git rev-parse --short=8 HEAD 2>{{if eq OS "windows"}}nul{{else}}/dev/null{{end}} || echo dev + LDFLAGS: -ldflags "-X {{.INTERNAL}}.version={{.VERSION}} -X {{.INTERNAL}}.gitCommit={{.GIT_COMMIT}} -s -w" + +tasks: + default: + desc: Build the project + cmds: + - task: build + + all: + desc: Build picoclaw + launcher (both binaries) + deps: + - build + - build-launcher + + build: + desc: Build picoclaw binary (dev, skip generate) + cmds: + - go build -v {{.LDFLAGS}} -o {{.BUILD_DIR}}/{{.BINARY_NAME}}{{exeExt}} ./{{.CMD_DIR}} + sources: + - "**/*.go" + generates: + - "{{.BUILD_DIR}}/{{.BINARY_NAME}}{{exeExt}}" + + build-full: + desc: Build with go generate (release) + cmds: + - task: generate + - task: build + + build-launcher: + desc: Build picoclaw-launcher (Wails desktop GUI) + dir: '{{.LAUNCHER_DIR}}' + cmds: + - go tool wails build -tags stdjson -ldflags "-s -w" -o picoclaw-launcher{{exeExt}} + sources: + - "**/*.go" + - frontend/** + generates: + - '../../{{.LAUNCHER_BUILD_DIR}}/picoclaw-launcher{{exeExt}}' + + generate: + desc: Run go generate + cmds: + - cmd: powershell -Command "Remove-Item -Recurse -Force '{{.CMD_DIR}}/internal/onboard/workspace' -ErrorAction SilentlyContinue" + platforms: [windows] + - cmd: rm -rf {{.CMD_DIR}}/internal/onboard/workspace + platforms: [linux, darwin] + - go generate ./... + + test: + desc: Run all tests + cmds: + - go test -count=1 ./pkg/... + + test-v: + desc: Run all tests (verbose) + cmds: + - go test -v -count=1 ./pkg/... + + test-agent: + desc: Run agent tests + cmds: + - go test -v -count=1 ./pkg/agent/ + + test-config: + desc: Run config tests + cmds: + - go test -v -count=1 ./pkg/config/ + + test-init: + desc: Run init command tests + cmds: + - go test -v -count=1 ./cmd/picoclaw/internal/initcmd/ + + lint: + desc: Run linters + cmds: + - golangci-lint run + + fmt: + desc: Format code + cmds: + - gofmt -w . + + vet: + desc: Run go vet + cmds: + - go vet ./... + + clean: + desc: Remove build artifacts + cmds: + - cmd: powershell -Command "Remove-Item -Recurse -Force '{{.BUILD_DIR}}' -ErrorAction SilentlyContinue" + platforms: [windows] + - cmd: rm -rf {{.BUILD_DIR}} + platforms: [linux, darwin] + + deps: + desc: Download and verify dependencies + cmds: + - go mod download + - go mod verify + + tidy: + desc: Tidy dependencies + cmds: + - go mod tidy + + check: + desc: Full check (fmt + vet + test) + cmds: + - task: fmt + - task: vet + - task: test + + install: + desc: Install picoclaw to system + cmds: + - task: build + - go install ./{{.CMD_DIR}} + + run: + desc: Build and run + cmds: + - task: build + - "{{.BUILD_DIR}}/{{.BINARY_NAME}}{{exeExt}} {{.CLI_ARGS}}"