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
12 changes: 12 additions & 0 deletions .changeset/deprecate-create-expert.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@perstack/perstack-toml": patch
"perstack": patch
---

feat: support running published experts without local perstack.toml

`perstack start <expertKey>` and `perstack run` no longer require a local `perstack.toml` when an expert key is provided. The CLI falls back to an empty default config, allowing the runtime to resolve the expert from the Perstack API.

Adds `getPerstackConfigOrDefault()` export to `@perstack/perstack-toml`.

Removes the `apps/create-expert` CLI package — the expert definition is now published via the Perstack API and run with `perstack start create-expert`.
81 changes: 81 additions & 0 deletions .github/workflows/expert-cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Expert CD

on:
push:
branches: [main]
paths:
- 'definitions/**/perstack.toml'

jobs:
publish:
name: Publish Expert Definitions
runs-on: ubuntu-24.04
strategy:
matrix:
definition:
- name: create-expert
path: definitions/create-expert
draft-id: ${{ vars.EXPERT_DRAFT_ID_CREATE_EXPERT }}
steps:
- uses: actions/checkout@v6

- uses: oven-sh/setup-bun@v2

- run: bun install --frozen-lockfile

- run: bun run build

- name: Extract version from perstack.toml
id: version
run: |
VERSION=$(bun -e "
const TOML = require('smol-toml');
const fs = require('fs');
const config = TOML.parse(fs.readFileSync('${{ matrix.definition.path }}/perstack.toml', 'utf-8'));
const experts = config.experts || {};
const coordinator = Object.entries(experts).find(([k]) => !k.startsWith('@'));
console.log(coordinator?.[1]?.version || '1.0.0');
")
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Extracted version: $VERSION"

- name: Check if version already exists
id: check
run: |
OUTPUT=$(npx perstack expert versions ${{ matrix.definition.name }} \
--api-key ${{ secrets.PERSTACK_API_KEY }} 2>&1) || true
echo "$OUTPUT"
if echo "$OUTPUT" | grep -q "^ ${{ steps.version.outputs.version }}"; then
echo "Version ${{ steps.version.outputs.version }} already exists — skipping"
echo "should-publish=false" >> "$GITHUB_OUTPUT"
else
echo "Version ${{ steps.version.outputs.version }} not found — will publish"
echo "should-publish=true" >> "$GITHUB_OUTPUT"
fi

- name: Publish scope if private
if: steps.check.outputs.should-publish == 'true'
run: |
npx perstack expert publish ${{ matrix.definition.name }} \
--api-key ${{ secrets.PERSTACK_API_KEY }} || true

- name: Push to draft
if: steps.check.outputs.should-publish == 'true'
id: push
run: |
OUTPUT=$(npx perstack expert push ${{ matrix.definition.draft-id }} \
--config ${{ matrix.definition.path }}/perstack.toml \
--api-key ${{ secrets.PERSTACK_API_KEY }})
echo "$OUTPUT"
REF_ID=$(echo "$OUTPUT" | grep "Ref ID:" | awk '{print $3}')
echo "ref-id=$REF_ID" >> "$GITHUB_OUTPUT"

- name: Assign version
if: steps.check.outputs.should-publish == 'true'
run: |
npx perstack expert version \
${{ matrix.definition.draft-id }} \
${{ steps.push.outputs.ref-id }} \
${{ steps.version.outputs.version }} \
--tag latest \
--api-key ${{ secrets.PERSTACK_API_KEY }}
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ perstack.lock
perstack.toml
!examples/**/perstack.toml
!benchmarks/**/perstack.toml
!apps/create-expert/perstack.toml
!definitions/**/perstack.toml
!docker/example/perstack.toml
perstack/
!apps/perstack
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ Perstack takes a third approach: purpose-specific micro-agents on value-tier mod

Every agent runs inside its own Docker container with an isolated filesystem and network. The host system is never exposed. This makes Perstack safe to run in production — agents can execute shell commands and write files without risk to the host.

`create-expert` generates agent definitions in a `perstack.toml` file. `perstack` executes them on an event-sourced runtime inside a sandboxed container.
Agent definitions live in a `perstack.toml` file. `perstack` executes them on an event-sourced runtime inside a sandboxed container.

```bash
npx create-expert "Form a team named ai-gaming to build a Bun-based CLI indie game playable on Bash for AI."
npx perstack start create-expert "Form a team named ai-gaming to build a Bun-based CLI indie game playable on Bash for AI."

docker run --rm -e ANTHROPIC_API_KEY \
-v ./perstack.toml:/workspace/perstack.toml:ro \
Expand All @@ -36,7 +36,7 @@ A game built with these commands: [demo-dungeon-crawler](https://github.com/pers

## How it works

`create-expert` generates a `perstack.toml` that defines a team of micro-agents. Each agent has a single responsibility and its own context window. Complex tasks are broken down and delegated to specialists.
`create-expert` generates a `perstack.toml` that defines a team of micro-agents. Run it with `npx perstack start create-expert`. Each agent has a single responsibility and its own context window. Complex tasks are broken down and delegated to specialists.

```toml
[experts."ai-gaming"]
Expand Down
240 changes: 0 additions & 240 deletions apps/create-expert/CHANGELOG.md

This file was deleted.

Loading