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: 0 additions & 12 deletions .devcontainer/devcontainer.json

This file was deleted.

45 changes: 0 additions & 45 deletions .devcontainer/postCreateCommand.sh

This file was deleted.

42 changes: 0 additions & 42 deletions .devcontainer/postStartCommand.sh

This file was deleted.

1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
.github
.gitignore
.pre-commit-config.yaml
.devcontainer
.claude
.claude-plugin
.runtime
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
name: CodeQL

# No `push: branches: [main]` trigger: every change reaches main through a
# PR, so a push trigger would scan each change twice. The weekly cron covers
# newly-published CodeQL queries against an unchanged main.
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
Expand Down
23 changes: 20 additions & 3 deletions .github/workflows/publish-test.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Publish Dev to TestPyPI

# Every merge to main publishes a dev build to test.pypi.org.
# Version is auto-generated: {pyproject.toml version}.dev{run_number}
# Version is auto-generated: {src/synth_panel/__version__.py version}.dev{run_number}
#
# ONE-TIME MANUAL SETUP (required before the first successful publish):
#
Expand Down Expand Up @@ -47,9 +47,19 @@ jobs:
id: version
run: |
set -euo pipefail
BASE=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/')
# pyproject.toml declares `dynamic = ["version"]` and reads the
# version from src/synth_panel/__version__.py, so that file — not
# pyproject.toml — is what must be rewritten here. The previous
# pyproject-based sed silently no-oped after the dynamic-version
# migration and every push re-uploaded the same wheel, which
# TestPyPI rejected with "400 File already exists".
BASE=$(sed -n 's/^__version__ = "\(.*\)"$/\1/p' src/synth_panel/__version__.py)
if [ -z "$BASE" ]; then
echo "::error::Could not read base version from src/synth_panel/__version__.py"
exit 1
fi
DEV_VERSION="${BASE}.dev${GITHUB_RUN_NUMBER}"
sed -i "s/^version = \".*\"/version = \"${DEV_VERSION}\"/" pyproject.toml
printf '__version__ = "%s"\n' "$DEV_VERSION" > src/synth_panel/__version__.py
echo "version=$DEV_VERSION" >> "$GITHUB_OUTPUT"
echo "Dev version: $DEV_VERSION"

Expand All @@ -59,6 +69,13 @@ jobs:
- name: Build package
run: python -m build

- name: Assert dev suffix applied
run: |
# Tripwire: if the version rewrite above ever silently no-ops again
# (e.g. the version source moves), fail loud here instead of letting
# TestPyPI reject a duplicate upload with an opaque 400.
ls dist/*.dev* >/dev/null || { echo "::error::dev suffix not applied"; exit 1; }

- name: Show built artifacts
run: ls -lh dist/

Expand Down
61 changes: 61 additions & 0 deletions .github/workflows/semver-label.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Semver Label

# Pre-merge guard: every PR must carry exactly one semver:* label
# (semver:patch, semver:minor, semver:major, or semver:skip) BEFORE merge.
#
# The post-merge Auto Semver Tag workflow (auto-tag.yml) fails loud when a
# merged PR lacks a label — but by then the omission has already shipped and
# needs a manual label + rerun to recover. This check surfaces the same
# requirement on the PR itself, while it can still be fixed with one click.
# It is wired as a required status check on main separately (branch ruleset,
# managed in dataviking-infra) — this file only defines the check.
#
# `labeled` / `unlabeled` are in the trigger list so the check re-evaluates
# immediately when labels change; the event payload's label set reflects the
# state after the change.

on:
pull_request:
types: [opened, reopened, synchronize, labeled, unlabeled]

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
semver-label:
name: Semver Label
runs-on: ubuntu-latest
steps:
- name: Require exactly one semver label
env:
LABELS_JSON: ${{ toJSON(github.event.pull_request.labels.*.name) }}
run: |
set -euo pipefail
echo "PR labels: ${LABELS_JSON}"

SEMVER_LABELS=$(echo "$LABELS_JSON" | jq -r '.[] | select(startswith("semver:"))')
COUNT=$(echo "$SEMVER_LABELS" | grep -c . || true)

if [ "$COUNT" -eq 0 ]; then
echo "::error::No semver label on this PR. Add exactly one of: semver:patch, semver:minor, semver:major, semver:skip."
exit 1
fi

if [ "$COUNT" -gt 1 ]; then
echo "::error::Multiple semver labels found: $(echo "$SEMVER_LABELS" | tr '\n' ' '). Use exactly one."
exit 1
fi

case "$SEMVER_LABELS" in
semver:patch|semver:minor|semver:major|semver:skip)
echo "Semver label OK: $SEMVER_LABELS"
;;
*)
echo "::error::Unrecognized semver label '$SEMVER_LABELS'. Valid labels: semver:patch, semver:minor, semver:major, semver:skip."
exit 1
;;
esac
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ state.json
gastown-*.json
CLAUDE.local.md

# DevContainer temp files
.devcontainer/.env
.devcontainer/tmp/

# synthpanel data
.synthpanel/
sessions/
Expand Down
4 changes: 1 addition & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
# Production runtime image for synthpanel.
#
# This image is sized for ephemeral / serverless invocation (Lambda, Cloud Run,
# GitHub Actions, n8n, etc.) — small footprint, no editor tooling. The
# devcontainer at .devcontainer/devcontainer.json remains the source of truth
# for *development*; this file is a slimmed-down production sibling.
# GitHub Actions, n8n, etc.) — small footprint, no editor tooling.
#
# Default CMD launches the MCP server on stdio. Override with any synthpanel
# subcommand:
Expand Down
13 changes: 4 additions & 9 deletions docs/agent-integration-landscape.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import.

## A. Current Integration Surfaces (Inventory)

SynthPanel ships six integration surfaces today:
SynthPanel ships five integration surfaces today:

| Surface | State | Agent-Reachable? |
|---|---|---|
Expand All @@ -54,7 +54,6 @@ SynthPanel ships six integration surfaces today:
| **MCP server** (12 tools, 4 resources, 3 prompts) | FastMCP, stdio transport, defaults to haiku | Yes — primary agent interface |
| **Claude Code plugin** (`.claude-plugin/plugin.json`) | Wraps MCP server + ships `/focus-group` skill | Yes — Claude Code only |
| **Claude Code skill** (`skills/focus-group/SKILL.md`) | Structured focus-group workflow | Yes — Claude Code only |
| **Devcontainer** (`.devcontainer/devcontainer.json`) | GitHub Codespaces / dev environments | Partial — dev-only, not agent-facing |

**Key observations:**
- The MCP server is the only general-purpose agent interface.
Expand Down Expand Up @@ -270,14 +269,11 @@ docker run -e ANTHROPIC_API_KEY=sk-... synthpanel/synthpanel mcp-serve
**Why it matters:**
- Agents in serverless environments (Lambda, Cloud Run, GitHub Actions)
can spin up a SynthPanel container as a tool-call target.
- Devcontainer already exists (`.devcontainer/devcontainer.json`) with a
base image (`ghcr.io/dataviking-tech/ai-dev-base:edge`). A production
image is a subset.
- n8n, Zapier, and custom orchestrators that support Docker-based tools
get a zero-install path.

**Effort:** 1-2 days. Dockerfile based on existing devcontainer. Publish to
GHCR and Docker Hub.
**Effort:** 1-2 days. Dockerfile based on an official `python:*-slim` image.
Publish to GHCR and Docker Hub.

**Impact:** MEDIUM. Enables specific use cases (serverless, ephemeral) but
most developers will pip-install. Worth doing after the top 3.
Expand Down Expand Up @@ -374,7 +370,7 @@ in examples that prove MCP works.
| REST API | No | Yes | Yes |
| Custom GPT | No | Unknown | Unknown |
| LangChain/CrewAI wrapper | No (MCP bridge works) | No | No |
| Docker image | No (devcontainer exists) | Unknown | Unknown |
| Docker image | No | Unknown | Unknown |
| Composio listing | No | No | No |
| Open source | Yes (MIT) | Yes (MIT) | No (SaaS) |
| Pricing | Free (BYOK) | SaaS | SaaS |
Expand Down Expand Up @@ -468,7 +464,6 @@ they form a complete strategy:
- `src/synth_panel/mcp/server.py`: 12 tool handlers wrapping orchestrator functions
- `.claude-plugin/plugin.json`: MCP server config + skill reference
- `skills/focus-group/SKILL.md`: 5-step workflow skill
- `.devcontainer/devcontainer.json`: `ghcr.io/dataviking-tech/ai-dev-base:edge`

---

Expand Down
Loading