mirror of
https://github.com/penpot/penpot.git
synced 2026-08-01 10:56:20 +00:00
✨ Polish the devenv CLI and rewrite the agentic-devenv guide
Support --ws in all relevant commands, fix some bugs in the CLI and add more precondition validation. The guide was adjusted to give a better overview of the multi-workspace setup Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ea5a2f6b4c
commit
ed679c15bb
@ -5,18 +5,74 @@ desc: Dive into agentic Penpot development.
|
|||||||
|
|
||||||
# Agentic Development Environment
|
# Agentic Development Environment
|
||||||
|
|
||||||
The agentic DevEnv is an extension of the standard DevEnv
|
The agentic DevEnv is an extension of the standard DevEnv (the
|
||||||
(the [general DevEnv instructions](/technical-guide/developer/devenv/) apply),
|
[general DevEnv instructions](/technical-guide/developer/devenv/) apply),
|
||||||
which is optimised for AI agent-based development,
|
optimised for AI agent-based development. It adds MCP servers (Penpot,
|
||||||
adding additional tools and processes that support agentic automation.
|
Serena, Playwright) and supports a launcher that wires them into your AI client.
|
||||||
|
|
||||||
The general workflow is as follows:
|
Two things to know up front:
|
||||||
|
|
||||||
1. Start the agentic DevEnv.
|
- **Parallel workspaces are first-class.** Run several devenv instances side
|
||||||
2. Start a debugging-enabled browser and open Penpot, using a Penpot user with
|
by side - one per AI agent if you like - each with its own source-tree
|
||||||
the remote MCP integration enabled.
|
clone, ports, and tmux session. Pass `--ws N` to target one.
|
||||||
3. Use an AI client (MCP client) which is connected to a suite of MCP servers
|
- **Your existing AI-client config is preserved.** The launcher loads a
|
||||||
to solve development tasks.
|
per-workspace MCP config on top of your global one.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
1. **Bring up one or more workspaces**[^cfg]:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./manage.sh run-devenv-agentic # ws0 (the live repo)
|
||||||
|
./manage.sh run-devenv-agentic --ws 1 # ws1 (sibling clone)
|
||||||
|
```
|
||||||
|
|
||||||
|
Add `--ws 2`, `--ws 3`, … for more parallel workspaces.
|
||||||
|
|
||||||
|
2. **Launch a browser with remote debugging enabled:**
|
||||||
|
|
||||||
|
For example, with Chrome:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
google-chrome --remote-debugging-port=9222 --user-data-dir="$HOME/.chrome-debug-profile"
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Open Penpot in that browser:**
|
||||||
|
|
||||||
|
- ws0: <https://localhost:3449>
|
||||||
|
- ws1: <https://localhost:13449>
|
||||||
|
- etc. (ports are offset by `10000 × N` for `wsN`)
|
||||||
|
|
||||||
|
On first login per account, open settings → Integrations and toggle
|
||||||
|
"MCP Server" on. The agentic DevEnv runs the MCP server in single-user
|
||||||
|
mode - the key and proxied URL shown in the UI are not needed.
|
||||||
|
|
||||||
|
4. **Launch your AI client** against the workspace you want it to drive:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./manage.sh start-coding-agent claude # ws0
|
||||||
|
./manage.sh start-coding-agent claude --ws 1 # ws1
|
||||||
|
```
|
||||||
|
|
||||||
|
Supported clients: `claude` | `opencode` | `vscode` | `codex`. The
|
||||||
|
launcher loads a per-workspace MCP config *on top of* your global config.
|
||||||
|
5. **Attach to the tmux session** for the workspace (optional):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./manage.sh attach-devenv # ws0
|
||||||
|
./manage.sh attach-devenv --ws 1 # ws1
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Shut down workspaces** with `./manage.sh stop-devenv`, either one by one or all at once.
|
||||||
|
You cannot shut down `ws0` if any other workspace is still running, since it's the worker-bearer.
|
||||||
|
Shared infrastructure will be cleaned up when the last workspace is stopped.
|
||||||
|
|
||||||
|
Optional: watch Serena's activity in its dashboard
|
||||||
|
(<http://localhost:14182> for ws0, <http://localhost:24182> for ws1, etc.).
|
||||||
|
|
||||||
|
[^cfg]: One-time, if you don't already have it: set
|
||||||
|
`penpotFlags = "enable-mcp"` in `frontend/resources/public/js/config.js`
|
||||||
|
(gitignored; create if missing).
|
||||||
|
|
||||||
## Capabilities
|
## Capabilities
|
||||||
|
|
||||||
@ -46,116 +102,101 @@ i.e. it can ...
|
|||||||
* test the changes in the live Penpot instance, and
|
* test the changes in the live Penpot instance, and
|
||||||
* create commits and PRs resolving the issue.
|
* create commits and PRs resolving the issue.
|
||||||
|
|
||||||
## Configuring and Starting the Agentic DevEnv
|
## The flow in detail
|
||||||
|
|
||||||
**First-Time Setup: Building the Image.** If you are starting the agentic DevEnv for the first time, you need to build
|
### First-time setup
|
||||||
the updated docker image, adding support for agentic tools:
|
|
||||||
|
|
||||||
```bash
|
**The MCP frontend flag.** Edit `frontend/resources/public/js/config.js`
|
||||||
./manage.sh build-devenv --local
|
(create it if missing) and ensure `penpotFlags` contains `enable-mcp`:
|
||||||
```
|
|
||||||
|
|
||||||
**Enable the Penpot MCP Connection in the Frontend.**
|
|
||||||
The agentic DevEnv relies on a connection between the Penpot frontend and the Penpot MCP server
|
|
||||||
being established automatically.
|
|
||||||
Edit the file `frontend/resources/public/js/config.js`,
|
|
||||||
creating it if it does not exist, and make sure the `penpotFlags` variable contains the
|
|
||||||
`enable-mcp` flag.
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var penpotFlags = "enable-mcp";
|
var penpotFlags = "enable-mcp";
|
||||||
```
|
```
|
||||||
|
|
||||||
**Running the DevEnv in Agentic Mode.** Each invocation starts one instance.
|
The file is gitignored and lives in the live repo only. On every
|
||||||
`--ws N` (N≥1) auto-starts ws0 first if it's not already up — ws0 is the
|
`run-devenv-agentic` call it is read directly for ws0; for wsN (N ≥ 1) it is
|
||||||
worker-bearer and must be running whenever any ws1+ is:
|
copied into the workspace clone on the **initial** sync only - subsequent
|
||||||
|
`--sync` passes leave the workspace's copy alone so per-workspace
|
||||||
|
customisations survive. `run-devenv-agentic` refuses to start if the file is
|
||||||
|
missing.
|
||||||
|
|
||||||
|
**Browser remote debugging.** The Playwright MCP server drives a real
|
||||||
|
browser instance over the Chrome DevTools protocol. To enable it, launch a
|
||||||
|
Chromium-based browser (Chrome, Vivaldi, Opera, …) with the
|
||||||
|
`--remote-debugging-port` flag and a separate user-data directory:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./manage.sh run-devenv-agentic # main (ws0)
|
google-chrome --remote-debugging-port=9222 --user-data-dir="$HOME/.chrome-debug-profile"
|
||||||
./manage.sh run-devenv-agentic --ws 1 # ws0 if needed, then ws1
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Starting an instance that is already running is an error. Per-instance ports
|
Verify it works by visiting `http://127.0.0.1:9222/json/version`. If you
|
||||||
|
change the port, update the Playwright MCP entry in `.devenv/shared/*.json`
|
||||||
|
accordingly. For security reasons, do not enable remote debugging on the
|
||||||
|
profile you use for regular browsing.
|
||||||
|
|
||||||
|
**Enable the MCP integration in Penpot.** The Penpot UI has a per-account
|
||||||
|
MCP toggle. After logging into your Penpot instance at
|
||||||
|
[https://localhost:3449](https://localhost:3449), open account settings,
|
||||||
|
click "Integrations" in the sidebar, and enable the "MCP Server" toggle.
|
||||||
|
The agentic DevEnv runs the MCP server in single-user mode, so the
|
||||||
|
generated key and proxied URL printed in the UI are *not* needed - only the
|
||||||
|
toggle itself matters.
|
||||||
|
|
||||||
|
**(Optional) custom devenv image.** Only needed if you want to modify the
|
||||||
|
devenv image itself (add a tool, change a base layer):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./manage.sh build-devenv --local
|
||||||
|
```
|
||||||
|
|
||||||
|
The default `run-devenv-agentic` flow pulls the published image
|
||||||
|
automatically, so regular users never run this.
|
||||||
|
|
||||||
|
### Bringing up workspaces
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./manage.sh run-devenv-agentic \
|
||||||
|
[--ws N] [--sync] [--serena-context CTX] \
|
||||||
|
[--git-user-name NAME] [--git-user-email EMAIL]
|
||||||
|
```
|
||||||
|
|
||||||
|
Brings one agentic instance up. Errors out if the target is already running.
|
||||||
|
|
||||||
|
`--ws N` (N ≥ 1) auto-starts ws0 first if it is not already up - ws0 is the
|
||||||
|
worker-bearer and must be running whenever any wsN is. Per-instance ports
|
||||||
are offset by `10000 × N` (ws1's MCP at `http://localhost:14401/mcp`, Serena
|
are offset by `10000 × N` (ws1's MCP at `http://localhost:14401/mcp`, Serena
|
||||||
MCP at `http://localhost:24181`, Serena dashboard at `http://localhost:24182`,
|
MCP at `http://localhost:24181`, Serena dashboard at
|
||||||
etc.). `manage.sh` prints the full per-instance URL set (Penpot UI, MCP
|
`http://localhost:24182`, etc.). `manage.sh` prints the full URL set on
|
||||||
stream, Serena MCP, Serena dashboard, attach command) every time it brings an
|
every bring-up so you don't compute offsets by hand. See the
|
||||||
instance up, so you don't need to compute the offsets by hand. See the
|
[Dev environment guide](./devenv.md) for the workspace lifecycle, `--sync`
|
||||||
*Parallel workspaces* section in the [Dev environment guide](./devenv.md) for
|
semantics, and stop ordering.
|
||||||
the workspace and lifecycle details (including the `--sync` flag and shutdown
|
|
||||||
shape).
|
|
||||||
|
|
||||||
**Git identity for agent commits.** Coding agents typically need to commit
|
**Git identity for agent commits.** Coding agents typically need to commit
|
||||||
inside the devenv, so `run-devenv-agentic` wires a Git identity into the
|
inside the devenv, so `run-devenv-agentic` wires a Git identity into the
|
||||||
container's global config on every bring-up. By default it propagates the
|
container's global config on every bring-up. By default it propagates the
|
||||||
host's effective `git config user.{name,email}` (local repo override wins
|
host's effective `git config user.{name,email}` (local repo override wins
|
||||||
over `~/.gitconfig`, matching what `git commit` on the host would record).
|
over `~/.gitconfig`, matching what `git commit` on the host would record).
|
||||||
Override either with `--git-user-name "Full Name"` /
|
Override with `--git-user-name "Full Name"` / `--git-user-email
|
||||||
`--git-user-email you@example.com` — useful when you want agent commits to
|
you@example.com` when you want agent commits to carry an identity different
|
||||||
carry an identity different from your normal one. Without either source the
|
from your normal one. Without either source the script warns and proceeds;
|
||||||
script warns and proceeds; commits made by the agent will fail until you fix
|
commits inside the devenv will fail until you fix it. See the
|
||||||
it. See the
|
|
||||||
[Dev environment guide](./devenv.md#git-identity-inside-the-container) for
|
[Dev environment guide](./devenv.md#git-identity-inside-the-container) for
|
||||||
the full mechanics.
|
the full mechanics.
|
||||||
|
|
||||||
> **Note:** the MCP and Serena tmux windows are only added when the session is
|
> **Note:** the MCP and Serena tmux windows are only added when the tmux
|
||||||
> first created. If you've already run `./manage.sh run-devenv` (non-agentic)
|
> session is first created. If a workspace was already brought up with
|
||||||
> in an instance, `run-devenv-agentic` errors out because the instance is
|
> `./manage.sh run-devenv` (non-agentic), stop it before re-running
|
||||||
> already running. Kill the session first to recreate with the agentic
|
> agentically:
|
||||||
> windows:
|
|
||||||
>
|
>
|
||||||
> ```bash
|
> ```bash
|
||||||
> docker exec penpot-devenv-ws0-main sudo -u penpot tmux kill-session -t penpot
|
|
||||||
> ./manage.sh stop-devenv
|
> ./manage.sh stop-devenv
|
||||||
> ./manage.sh run-devenv-agentic
|
> ./manage.sh run-devenv-agentic
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
## Opening Penpot with Remote Debugging & MCP Enabled
|
### Launching an AI client
|
||||||
|
|
||||||
**Enable Remote Debugging in Your Browser.**
|
Every `run-devenv-agentic` regenerates four MCP-client config files with
|
||||||
Penpot needs to be opened in a browser that has remote debugging enabled.
|
the workspace's ports baked in:
|
||||||
In Chromium-based browsers (such as Google Chrome, Opera, Vivaldi, etc.),
|
|
||||||
this can be achieved by launching the browser with the `--remote-debugging-port` argument.
|
|
||||||
For most newer browsers, you will also need to specify a user data directory,
|
|
||||||
as using debugging with your regular browser profile is disallowed for security reasons.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
google-chrome --remote-debugging-port=9222 --user-data-dir="$HOME/.chrome-debug-profile"
|
|
||||||
```
|
|
||||||
|
|
||||||
This enables the Playwright MCP server to connect to the browser and control it.
|
|
||||||
Verify that debugging was enabled correctly by navigating to `http://127.0.0.1:9222/json/version`.
|
|
||||||
If you change the port, adjust the MCP server configuration accordingly (see below).
|
|
||||||
Note: For security reasons, you should not enable remote debugging with a profile
|
|
||||||
that you use for regular browsing activities.
|
|
||||||
|
|
||||||
**Open Penpot with the MCP Integration Enabled.**
|
|
||||||
The Penpot instance in the DevEnv can be accessed at [https://localhost:3449](https://localhost:3449).
|
|
||||||
Once logged in, navigate to your account settings, click on "Integrations" in the sidebar, and enable the "MCP Server" toggle.
|
|
||||||
Note: You do not need to use the generated key (or the provided URL), as the MCP server in the agentic DevEnv is running in single-user mode and does not require authentication.
|
|
||||||
|
|
||||||
## Configuring Your AI Client
|
|
||||||
|
|
||||||
A given AI client session drives **exactly one workspace**. The Penpot and
|
|
||||||
Serena MCP servers it talks to live inside that workspace's `main` container
|
|
||||||
on workspace-specific ports — a client configured against ws0 cannot drive
|
|
||||||
ws1, and vice versa. Running N parallel workspaces therefore means
|
|
||||||
configuring (or launching) N AI clients, each pointed at a different
|
|
||||||
workspace's ports.
|
|
||||||
|
|
||||||
There are two ways to wire this up:
|
|
||||||
|
|
||||||
* **For Claude Code, opencode, VS Code Copilot, and the Codex CLI**, use
|
|
||||||
`./manage.sh start-coding-agent`, which loads a per-workspace MCP config
|
|
||||||
generated by the devenv tooling. See *Quick start* below.
|
|
||||||
* **For any other client** (JetBrains AI Assistant, Claude Desktop,
|
|
||||||
Antigravity, …) — or if you'd rather configure the supported clients
|
|
||||||
yourself — see *Manual configuration*.
|
|
||||||
|
|
||||||
### Quick start: `start-coding-agent`
|
|
||||||
|
|
||||||
Every time you bring a workspace up with `run-devenv-agentic`, `manage.sh`
|
|
||||||
regenerates four MCP config files with that workspace's ports baked in:
|
|
||||||
|
|
||||||
| Client | File | Loaded how |
|
| Client | File | Loaded how |
|
||||||
| ------ | ---- | ---------- |
|
| ------ | ---- | ---------- |
|
||||||
@ -164,39 +205,28 @@ regenerates four MCP config files with that workspace's ports baked in:
|
|||||||
| VS Code Copilot | `<workspace>/.vscode/mcp.json` | Auto-discovered when opening the workspace |
|
| VS Code Copilot | `<workspace>/.vscode/mcp.json` | Auto-discovered when opening the workspace |
|
||||||
| Codex CLI | `<workspace>/.codex/config.toml` | Auto-discovered from the project root (trusted projects only) |
|
| Codex CLI | `<workspace>/.codex/config.toml` | Auto-discovered from the project root (trusted projects only) |
|
||||||
|
|
||||||
The files are committed templates + an envsubst pass; see
|
The files are committed templates + an `envsubst` pass; see
|
||||||
[`.devenv/README.md`](../../../.devenv/README.md) for the full layout.
|
[`.devenv/README.md`](../../../.devenv/README.md) for the full layout.
|
||||||
|
|
||||||
To launch a coding agent:
|
`./manage.sh start-coding-agent <client> [--ws N] [...passthrough]`
|
||||||
|
launches the chosen client against one workspace, `cd`'ing into the right
|
||||||
|
directory and refusing to launch if the instance is not running:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Default target is ws0 (the live repo).
|
./manage.sh start-coding-agent claude # ws0
|
||||||
./manage.sh start-coding-agent claude [...args forwarded to claude]
|
./manage.sh start-coding-agent opencode --ws 1 # ws1
|
||||||
./manage.sh start-coding-agent opencode [...args forwarded to opencode]
|
./manage.sh start-coding-agent vscode # opens VS Code on ws0
|
||||||
./manage.sh start-coding-agent vscode [...args forwarded to 'code']
|
./manage.sh start-coding-agent codex --ws 2 # ws2
|
||||||
./manage.sh start-coding-agent codex [...args forwarded to codex]
|
|
||||||
|
|
||||||
# Target a specific parallel workspace with --ws N (integer only).
|
|
||||||
./manage.sh start-coding-agent claude --ws 1 # drives ws1
|
|
||||||
./manage.sh start-coding-agent opencode --ws 2 # drives ws2
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The launcher `cd`'s into the target workspace before exec'ing the client, so
|
A given AI client session drives **exactly one workspace**, so running N
|
||||||
config files are resolved from the right directory regardless of where you
|
parallel workspaces typically means running N AI client sessions, each
|
||||||
invoke `manage.sh` from. It refuses to launch if the target instance's
|
pointed at a different workspace's ports.
|
||||||
`main` container is not running — the Penpot and Serena MCP servers only
|
|
||||||
exist while the devenv is up. Bring the instance up first with
|
|
||||||
`./manage.sh run-devenv-agentic --ws N`, then retry.
|
|
||||||
|
|
||||||
`--ws` accepts a non-negative integer only (`--ws 0`, `--ws 1`, …). Spellings
|
|
||||||
like `--ws main` or `--ws ws1` are rejected so the flag shape stays uniform
|
|
||||||
across `attach-devenv`, `run-devenv-agentic`, `stop-devenv`, and
|
|
||||||
`start-coding-agent`.
|
|
||||||
|
|
||||||
What each launcher does:
|
What each launcher does:
|
||||||
|
|
||||||
* **Claude Code** is started with `--mcp-config .devenv/mcp/claude-code.json`,
|
* **Claude Code** is started with `--mcp-config .devenv/mcp/claude-code.json`,
|
||||||
which is **additive** — your existing global Claude Code MCP entries stay
|
which is **additive** - your existing global Claude Code MCP entries stay
|
||||||
available alongside the three entries we ship (Penpot MCP, Serena MCP,
|
available alongside the three entries we ship (Penpot MCP, Serena MCP,
|
||||||
Playwright). To shadow one of our entries with a private one, install it
|
Playwright). To shadow one of our entries with a private one, install it
|
||||||
under Claude Code's local scope (`claude mcp add --scope local …`); local
|
under Claude Code's local scope (`claude mcp add --scope local …`); local
|
||||||
@ -205,31 +235,33 @@ What each launcher does:
|
|||||||
opencode's precedence chain is *global → `OPENCODE_CONFIG` → project*, so
|
opencode's precedence chain is *global → `OPENCODE_CONFIG` → project*, so
|
||||||
the file we generate **overrides** entries with the same name in your
|
the file we generate **overrides** entries with the same name in your
|
||||||
global config. To override our entries in turn, drop a personal
|
global config. To override our entries in turn, drop a personal
|
||||||
`opencode.json` at the repo root — it's gitignored on purpose.
|
`opencode.json` at the repo root - it's gitignored on purpose.
|
||||||
* **VS Code Copilot** — `code "$PWD"` opens VS Code on the current workspace.
|
* **VS Code Copilot** - `code "$workspace"` opens VS Code on the workspace.
|
||||||
Copilot loads both your user-profile MCP config and the workspace's
|
Copilot loads both your user-profile MCP config and the workspace's
|
||||||
`.vscode/mcp.json`, so our entries land alongside whatever you have
|
`.vscode/mcp.json`, so our entries land alongside whatever you have
|
||||||
globally. Same-name entries can be shadowed via your user profile.
|
globally. Same-name entries can be shadowed via your user profile.
|
||||||
* **Codex CLI** — `codex` is exec'd from `$PWD`. Codex auto-discovers
|
* **Codex CLI** - `codex` is exec'd from the workspace dir. Codex
|
||||||
`.codex/config.toml` at the project root, but **only for "trusted"
|
auto-discovers `.codex/config.toml` at the project root, but **only for
|
||||||
projects**; the first launch in a workspace will prompt you to trust it.
|
"trusted" projects** - the first launch in a workspace will prompt you to
|
||||||
Entries in `~/.codex/config.toml` override the project file on name
|
trust it. Entries in `~/.codex/config.toml` override the project file on
|
||||||
conflict.
|
name conflict.
|
||||||
|
|
||||||
### Manual configuration
|
## Manual AI-client configuration
|
||||||
|
|
||||||
The `start-coding-agent` launcher covers Claude Code and opencode. For any
|
The `start-coding-agent` launcher covers Claude Code, opencode, VS Code
|
||||||
other client, or if you prefer to wire things up yourself, configure the
|
Copilot, and the Codex CLI. For any other client (JetBrains AI Assistant,
|
||||||
MCP servers in your client's native config format using the URLs below.
|
Claude Desktop, Antigravity, …), or if you prefer to wire things up
|
||||||
|
yourself, configure the MCP servers in your client's native format using
|
||||||
|
the URLs below.
|
||||||
|
|
||||||
The Penpot and Serena URLs for the workspace you want to target are printed
|
The Penpot and Serena URLs for the workspace you want to target are printed
|
||||||
by `manage.sh` every time it brings an instance up; copy them straight from
|
by `manage.sh` every time it brings an instance up; copy them straight from
|
||||||
that output. The mechanical rule is `port = base + 10000 × N` for `wsN`, with
|
that output. The mechanical rule is `port = base + 10000 × N` for `wsN`,
|
||||||
bases `4401` (Penpot MCP) and `14181` (Serena MCP). Playwright is not
|
with bases `4401` (Penpot MCP) and `14181` (Serena MCP). Playwright is not
|
||||||
workspace-scoped — it connects to your local browser, so the same entry works
|
workspace-scoped - it connects to your local browser, so the same entry
|
||||||
for every client.
|
works for every client.
|
||||||
|
|
||||||
#### Project-level MCP config files (Claude Code, opencode — manual path)
|
### Project-level MCP config files (Claude Code, opencode - manual path)
|
||||||
|
|
||||||
If you'd rather not go through the launcher, both Claude Code and opencode
|
If you'd rather not go through the launcher, both Claude Code and opencode
|
||||||
support project-level MCP config files that **merge with** the developer's
|
support project-level MCP config files that **merge with** the developer's
|
||||||
@ -245,7 +277,7 @@ The schemas differ between the two, so a workspace supporting both ships
|
|||||||
both files. For multi-workspace work, edit the port numbers in each
|
both files. For multi-workspace work, edit the port numbers in each
|
||||||
workspace's copy once to match its offset.
|
workspace's copy once to match its offset.
|
||||||
|
|
||||||
#### Example configuration
|
### Example configuration
|
||||||
|
|
||||||
Below is a JSON-based configuration snippet in Claude Code's `mcpServers`
|
Below is a JSON-based configuration snippet in Claude Code's `mcpServers`
|
||||||
schema, targeting `ws0` (main), using `mcp-remote` to wrap HTTP-based
|
schema, targeting `ws0` (main), using `mcp-remote` to wrap HTTP-based
|
||||||
@ -276,26 +308,29 @@ appropriately, referring to your client's documentation.
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Penpot MCP Server**
|
**Penpot MCP Server**
|
||||||
* The URL above connects directly to the server in the DevEnv, which runs in single-user mode.
|
* The URL above connects directly to the server in the DevEnv, which runs
|
||||||
You do not need to use the proxied URL or the user token that is provided by the Penpot UI.
|
in single-user mode. You do not need to use the proxied URL or the user
|
||||||
|
token that is provided by the Penpot UI.
|
||||||
|
|
||||||
**Serena MCP Server**
|
**Serena MCP Server**
|
||||||
* The matching Serena dashboard lives on the next port (`14182` for ws0, `24182` for ws1, …) and is also printed by `manage.sh` on startup.
|
* The matching Serena dashboard lives on the next port (`14182` for ws0,
|
||||||
|
`24182` for ws1, …) and is also printed by `manage.sh` on startup.
|
||||||
|
|
||||||
## Working on Development Tasks
|
## Working on Development Tasks
|
||||||
|
|
||||||
After having made the configuration changes, restart your AI client.
|
After having made the configuration changes, restart your AI client. The
|
||||||
The configured MCP servers should now be running and accessible to your client.
|
configured MCP servers should now be running and accessible to your client.
|
||||||
|
|
||||||
The agent's entrypoint for development is an activation of the `penpot` project with Serena.
|
The agent's entrypoint for development is an activation of the `penpot`
|
||||||
Start by instructing your agent as follows,
|
project with Serena. Start by instructing your agent as follows,
|
||||||
|
|
||||||
> Activate project penpot.
|
> Activate project penpot.
|
||||||
|
|
||||||
and it should retrieve fundamental project information,
|
and it should retrieve fundamental project information, expecting further
|
||||||
expecting further instructions on what to do.
|
instructions on what to do.
|
||||||
|
|
||||||
**Always start your first prompt with these activation instructions**, as this bootstraps the agent's context.
|
**Always start your first prompt with these activation instructions**, as
|
||||||
|
this bootstraps the agent's context.
|
||||||
|
|
||||||
### Checking MCP Server Operability
|
### Checking MCP Server Operability
|
||||||
|
|
||||||
|
|||||||
@ -102,12 +102,13 @@ Host ports are offset by `10000 × N`:
|
|||||||
| Serena MCP | `http://localhost:14281` | `http://localhost:24281` | `http://localhost:34281` |
|
| Serena MCP | `http://localhost:14281` | `http://localhost:24281` | `http://localhost:34281` |
|
||||||
|
|
||||||
Container-internal ports stay fixed. Target a specific instance with
|
Container-internal ports stay fixed. Target a specific instance with
|
||||||
`--ws 1` on `attach-devenv`, `run-devenv-agentic`, `stop-devenv`, and
|
`--ws N` on `attach-devenv`, `run-devenv-agentic`, `stop-devenv`,
|
||||||
`start-coding-agent` (`--instance ws1` on `run-devenv-shell` /
|
`start-coding-agent`, `run-devenv-shell`, and `isolated-shell`. `--ws`
|
||||||
`run-devenv`). The `--ws` flag accepts a **non-negative integer only** —
|
accepts a **non-negative integer only** — `--ws main` or `--ws ws1` is
|
||||||
`--ws main` or `--ws ws1` is rejected, keeping the flag shape uniform across
|
rejected, keeping the flag shape uniform across commands. `run-devenv` is
|
||||||
commands. `run-devenv-agentic` also accepts `--serena-context CTX` and
|
ws0-only and takes no workspace flag. `run-devenv-agentic` also accepts
|
||||||
`--git-user-name NAME` / `--git-user-email EMAIL` (see below).
|
`--serena-context CTX` and `--git-user-name NAME` / `--git-user-email
|
||||||
|
EMAIL` (see below).
|
||||||
|
|
||||||
### Git identity inside the container
|
### Git identity inside the container
|
||||||
|
|
||||||
|
|||||||
262
manage.sh
262
manage.sh
@ -549,31 +549,68 @@ function stop-devenv {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# drop-devenv shares stop-devenv's CLI and invariants exactly; the only
|
||||||
|
# difference is that on a full teardown it also removes the devenv image
|
||||||
|
# (forcing the next bring-up to re-pull/rebuild). Single-workspace drops
|
||||||
|
# keep the image because the rest of the workspaces still depend on it.
|
||||||
|
#
|
||||||
|
# --ws N (N >= 1) delegate to stop-devenv; image is kept.
|
||||||
|
# --ws 0 | (none) delegate to stop-devenv; image is removed.
|
||||||
|
# --all delegate to stop-devenv; image is removed.
|
||||||
function drop-devenv {
|
function drop-devenv {
|
||||||
local ws
|
# Parse args ourselves to decide whether the image gets removed.
|
||||||
for ws in $(list-running-instances); do
|
# stop-devenv then re-parses the same flags and runs the actual stop.
|
||||||
# Never -v: data preservation rule.
|
local target=""
|
||||||
instance-compose "$ws" down -t 2
|
local all=false
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--ws)
|
||||||
|
target="$(parse-ws-integer "$2")" || return 1; shift 2;;
|
||||||
|
--all)
|
||||||
|
all=true; shift;;
|
||||||
|
*)
|
||||||
|
echo "drop-devenv: unknown argument '$1'" >&2
|
||||||
|
return 1;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
infra-compose down -t 2
|
if [[ -n "$target" && "$all" == "true" ]]; then
|
||||||
|
echo "drop-devenv: --ws and --all are mutually exclusive." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Clean old development image $DEVENV_IMGNAME..."
|
local stop_args=()
|
||||||
docker images $DEVENV_IMGNAME -q | xargs --no-run-if-empty docker rmi
|
[[ -n "$target" ]] && stop_args+=(--ws "${target#ws}")
|
||||||
|
[[ "$all" == "true" ]] && stop_args+=(--all)
|
||||||
|
stop-devenv "${stop_args[@]}" || return $?
|
||||||
|
|
||||||
|
# Image removal happens for the full-teardown paths only. A single-wsN
|
||||||
|
# (N >= 1) drop must keep the image since ws0 and any other wsN still
|
||||||
|
# rely on it.
|
||||||
|
if [[ -z "$target" || "$target" == "ws0" ]] || [[ "$all" == "true" ]]; then
|
||||||
|
echo "Clean old development image $DEVENV_IMGNAME..."
|
||||||
|
docker images $DEVENV_IMGNAME -q | xargs --no-run-if-empty docker rmi
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function log-devenv {
|
function log-devenv {
|
||||||
# Tail ws0 by default; for multi-instance dev, attach explicitly per project.
|
local target="ws0"
|
||||||
instance-compose ws0 logs -f --tail=50
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--ws)
|
||||||
|
target="$(parse-ws-integer "$2")" || return 1; shift 2;;
|
||||||
|
*)
|
||||||
|
echo "log-devenv: unknown argument '$1'" >&2
|
||||||
|
return 1;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
instance-compose "$target" logs -f --tail=50
|
||||||
}
|
}
|
||||||
|
|
||||||
function run-devenv-tmux {
|
function run-devenv-tmux {
|
||||||
local extra_env_args=()
|
local extra_env_args=()
|
||||||
local instance="ws0"
|
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--instance)
|
|
||||||
instance="$(normalize-instance "$2")"; shift 2;;
|
|
||||||
-e)
|
-e)
|
||||||
extra_env_args+=(-e "$2"); shift 2;;
|
extra_env_args+=(-e "$2"); shift 2;;
|
||||||
-e*)
|
-e*)
|
||||||
@ -584,39 +621,19 @@ function run-devenv-tmux {
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
if ! devenv-main-running "$instance"; then
|
if ! devenv-main-running ws0; then
|
||||||
if [[ "$instance" == "ws0" ]]; then
|
start-devenv
|
||||||
start-devenv
|
echo "Waiting for containers fully start (5s)..."
|
||||||
echo "Waiting for containers fully start (5s)..."
|
sleep 5
|
||||||
sleep 5
|
|
||||||
else
|
|
||||||
echo "Instance '$instance' is not running; bring it up first with './manage.sh run-devenv-agentic --n-instances N'." >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local container
|
local container
|
||||||
container=$(devenv-main-container "$instance")
|
container=$(devenv-main-container ws0)
|
||||||
docker exec -ti \
|
docker exec -ti \
|
||||||
"${extra_env_args[@]}" \
|
"${extra_env_args[@]}" \
|
||||||
"$container" sudo -EH -u penpot PENPOT_PLUGIN_DEV=$PENPOT_PLUGIN_DEV /home/start-tmux.sh
|
"$container" sudo -EH -u penpot PENPOT_PLUGIN_DEV=$PENPOT_PLUGIN_DEV /home/start-tmux.sh
|
||||||
}
|
}
|
||||||
|
|
||||||
# Normalize an instance specifier ("main", "0", "ws0", "1", "ws3", ...) to "wsN".
|
|
||||||
function normalize-instance {
|
|
||||||
local raw="$1"
|
|
||||||
if [[ "$raw" == "main" ]]; then
|
|
||||||
echo "ws0"
|
|
||||||
elif [[ "$raw" =~ ^ws[0-9]+$ ]]; then
|
|
||||||
echo "$raw"
|
|
||||||
elif [[ "$raw" =~ ^[0-9]+$ ]]; then
|
|
||||||
echo "ws$raw"
|
|
||||||
else
|
|
||||||
echo "Invalid instance value: '$raw' (expected main|0|ws0|1|ws1|...)" >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Strict parser for --ws values. Accepts a bare integer in the supported
|
# Strict parser for --ws values. Accepts a bare integer in the supported
|
||||||
# range (0..PENPOT_MAX_WS_INDEX) and returns the canonical "wsN" form.
|
# range (0..PENPOT_MAX_WS_INDEX) and returns the canonical "wsN" form.
|
||||||
# Anything else fails fast. The upper bound exists because the host ports
|
# Anything else fails fast. The upper bound exists because the host ports
|
||||||
@ -789,6 +806,23 @@ function run-devenv-agentic {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Pre-flight: frontend/resources/public/js/config.js must exist in the
|
||||||
|
# live repo. The file is gitignored; ws0 reads it directly from $PWD and
|
||||||
|
# wsN gets a one-shot copy on its initial sync. Without it the frontend
|
||||||
|
# never sets the 'enable-mcp' flag, so the agent can't drive Penpot via
|
||||||
|
# MCP. Fail fast (before any side effects) so the developer can fix it
|
||||||
|
# without leaving infra / containers half-started behind.
|
||||||
|
local cfg="frontend/resources/public/js/config.js"
|
||||||
|
if [[ ! -f "$PWD/$cfg" ]]; then
|
||||||
|
echo "$cfg is missing in the live repo." >&2
|
||||||
|
echo "Create it before running run-devenv-agentic -- the file is gitignored," >&2
|
||||||
|
echo "read directly from \$PWD on ws0 and copied into wsN only on its initial" >&2
|
||||||
|
echo "sync. Without it the Penpot frontend will not establish the MCP" >&2
|
||||||
|
echo "connection, so the agent cannot drive it. Minimal content:" >&2
|
||||||
|
echo " var penpotFlags = \"enable-mcp\";" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
pull-devenv-if-not-exists
|
pull-devenv-if-not-exists
|
||||||
ensure-devenv-network
|
ensure-devenv-network
|
||||||
ensure-infra-up
|
ensure-infra-up
|
||||||
@ -827,8 +861,8 @@ function run-devenv-shell {
|
|||||||
local positional=()
|
local positional=()
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--instance)
|
--ws)
|
||||||
instance="$(normalize-instance "$2")"; shift 2;;
|
instance="$(parse-ws-integer "$2")" || return 1; shift 2;;
|
||||||
*)
|
*)
|
||||||
positional+=("$1"); shift;;
|
positional+=("$1"); shift;;
|
||||||
esac
|
esac
|
||||||
@ -839,12 +873,20 @@ function run-devenv-shell {
|
|||||||
start-devenv
|
start-devenv
|
||||||
else
|
else
|
||||||
echo "Instance '$instance' is not running." >&2
|
echo "Instance '$instance' is not running." >&2
|
||||||
|
echo "Bring it up first with './manage.sh run-devenv-agentic --ws ${instance#ws}'." >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
# No positional args -> drop the user into a bash shell. Without this,
|
||||||
|
# `sudo -EH -u penpot` would be called with no command and just print its
|
||||||
|
# own usage.
|
||||||
|
if [[ ${#positional[@]} -eq 0 ]]; then
|
||||||
|
positional=(bash)
|
||||||
|
fi
|
||||||
local container
|
local container
|
||||||
container=$(devenv-main-container "$instance")
|
container=$(devenv-main-container "$instance")
|
||||||
docker exec -ti \
|
docker exec -ti \
|
||||||
|
-w /home/penpot/penpot \
|
||||||
-e JAVA_OPTS="$JAVA_OPTS" \
|
-e JAVA_OPTS="$JAVA_OPTS" \
|
||||||
-e EXTERNAL_UID=$CURRENT_USER_ID \
|
-e EXTERNAL_UID=$CURRENT_USER_ID \
|
||||||
"$container" sudo -EH -u penpot "${positional[@]}"
|
"$container" sudo -EH -u penpot "${positional[@]}"
|
||||||
@ -874,7 +916,8 @@ function attach-devenv {
|
|||||||
|
|
||||||
if ! docker exec "$container" sudo -EH -u penpot tmux has-session -t "$session" 2>/dev/null; then
|
if ! docker exec "$container" sudo -EH -u penpot tmux has-session -t "$session" 2>/dev/null; then
|
||||||
echo "No tmux session '$session' inside instance '$instance'." >&2
|
echo "No tmux session '$session' inside instance '$instance'." >&2
|
||||||
echo "Start it with './manage.sh run-devenv-agentic [--ws N]'." >&2
|
echo "The session may still be starting (the workspace's startup script runs the" >&2
|
||||||
|
echo "project setup before creating it) or it may have been closed. Wait and retry." >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -995,17 +1038,52 @@ function start-coding-agent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function run-devenv-isolated-shell {
|
function run-devenv-isolated-shell {
|
||||||
docker volume create ${PENPOT_USER_DATA_VOLUME};
|
local instance="ws0"
|
||||||
|
local positional=()
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--ws)
|
||||||
|
instance="$(parse-ws-integer "$2")" || return 1; shift 2;;
|
||||||
|
*)
|
||||||
|
positional+=("$1"); shift;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Resolve the user_data volume and source tree for the target workspace.
|
||||||
|
# ws0 uses the baseline volume name from defaults.env; wsN follows the
|
||||||
|
# write-instance-env naming convention (penpotdev_<instance>_user_data)
|
||||||
|
# and bind-mounts the workspace clone instead of $PWD.
|
||||||
|
local user_data_volume source_path
|
||||||
|
if [[ "$instance" == "ws0" ]]; then
|
||||||
|
user_data_volume="$PENPOT_USER_DATA_VOLUME"
|
||||||
|
source_path="$PWD"
|
||||||
|
else
|
||||||
|
user_data_volume="penpotdev_${instance}_user_data"
|
||||||
|
source_path="$(workspace-path "$instance")"
|
||||||
|
if [[ ! -d "$source_path" ]]; then
|
||||||
|
echo "isolated-shell: workspace for $instance not found at $source_path." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# No command -> drop into bash. Always cwd to the source-tree root;
|
||||||
|
# callers that need a subdir can `cd` inside the shell or pass
|
||||||
|
# `bash -c 'cd subdir && cmd'`.
|
||||||
|
if [[ ${#positional[@]} -eq 0 ]]; then
|
||||||
|
positional=(bash)
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker volume create "$user_data_volume" >/dev/null
|
||||||
docker run -ti --rm \
|
docker run -ti --rm \
|
||||||
--mount source=${PENPOT_USER_DATA_VOLUME},type=volume,target=/home/penpot/ \
|
--mount source="$user_data_volume",type=volume,target=/home/penpot/ \
|
||||||
--mount source=`pwd`,type=bind,target=/home/penpot/penpot \
|
--mount source="$source_path",type=bind,target=/home/penpot/penpot \
|
||||||
-e EXTERNAL_UID=$CURRENT_USER_ID \
|
-e EXTERNAL_UID=$CURRENT_USER_ID \
|
||||||
-e BUILD_STORYBOOK=$BUILD_STORYBOOK \
|
-e BUILD_STORYBOOK=$BUILD_STORYBOOK \
|
||||||
-e BUILD_WASM=$BUILD_WASM \
|
-e BUILD_WASM=$BUILD_WASM \
|
||||||
-e SHADOWCLJS_EXTRA_PARAMS=$SHADOWCLJS_EXTRA_PARAMS \
|
-e SHADOWCLJS_EXTRA_PARAMS=$SHADOWCLJS_EXTRA_PARAMS \
|
||||||
-e JAVA_OPTS="$JAVA_OPTS" \
|
-e JAVA_OPTS="$JAVA_OPTS" \
|
||||||
-w /home/penpot/penpot/$1 \
|
-w /home/penpot/penpot \
|
||||||
$DEVENV_IMGNAME:latest sudo -EH -u penpot $@
|
"$DEVENV_IMGNAME:latest" sudo -EH -u penpot "${positional[@]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
function build-imagemagick-docker-image {
|
function build-imagemagick-docker-image {
|
||||||
@ -1216,40 +1294,68 @@ function build-storybook-docker-image {
|
|||||||
function usage {
|
function usage {
|
||||||
echo "PENPOT build & release manager"
|
echo "PENPOT build & release manager"
|
||||||
echo "USAGE: $0 OPTION"
|
echo "USAGE: $0 OPTION"
|
||||||
echo "Options:"
|
echo ""
|
||||||
echo "- pull-devenv Pulls docker development oriented image"
|
echo "Development environment (devenv)"
|
||||||
echo "- build-devenv Build docker development oriented image"
|
echo "--------------------------------"
|
||||||
echo "- build-devenv --local Build a local docker development oriented image"
|
echo "The devenv runs Penpot in a Docker container and supports parallel"
|
||||||
echo "- create-devenv Create the development oriented docker compose service."
|
echo "'workspaces': ws0 (the live repo at \$PWD) and optional wsN (N >= 1, sibling"
|
||||||
echo "- start-devenv Start the development oriented docker compose service."
|
echo "clones). Use --ws N to target a specific workspace; the default is 0."
|
||||||
echo "- stop-devenv Stops the development oriented docker compose service."
|
echo "Full guide: docs/technical-guide/developer/{devenv,agentic-devenv}.md."
|
||||||
echo "- drop-devenv Remove the development oriented docker compose containers, volumes and clean images."
|
echo ""
|
||||||
echo "- run-devenv Brings ws0 up and attaches to its tmux session (no MCP, no Serena)."
|
echo "Image lifecycle"
|
||||||
echo " Optional --instance <wsN> targets a different instance."
|
echo "- pull-devenv Pull the devenv docker image from the registry."
|
||||||
echo " Optional -e flags are forwarded to 'docker exec' (e.g. -e MY_VAR=value)."
|
echo "- build-devenv [--local] Build the devenv docker image (--local skips the registry push)."
|
||||||
echo "- run-devenv-agentic Brings one agentic instance (MCP + Serena) up. Errors out if it is already running."
|
echo ""
|
||||||
echo " Auto-starts ws0 first when --ws N (N>=1) is requested (workers run only on ws0)."
|
echo "Bring a devenv up / down"
|
||||||
echo " Options: --ws N (default: 0; non-negative integer only),"
|
echo "- run-devenv-agentic Bring one workspace up with AI-agent tooling enabled (MCP + Serena),"
|
||||||
echo " --sync (re-seed workspace from live repo; ws1+ only),"
|
echo " start its tmux session in the background, regenerate the per-workspace"
|
||||||
echo " --serena-context CONTEXT (default: desktop-app),"
|
echo " MCP configs, and print the workspace's URLs. Errors out if the target"
|
||||||
echo " --git-user-name NAME / --git-user-email EMAIL"
|
echo " is already running."
|
||||||
echo " (default: host's effective 'git config user.{name,email}',"
|
echo " Options:"
|
||||||
echo " honouring per-repo local overrides)"
|
echo " --ws N target workspace (default: 0). N >= 1 auto-starts"
|
||||||
echo "- attach-devenv Attaches to the tmux session inside a running instance."
|
echo " ws0 first if it is not already up."
|
||||||
echo " Options: --ws N (default: 0; non-negative integer only)"
|
echo " --sync re-seed the wsN clone from the live repo before"
|
||||||
echo "- start-coding-agent <client> Launches an AI coding agent against one workspace with the right MCP config wired in."
|
echo " starting (forbidden on ws0; implicit on first"
|
||||||
|
echo " start of a wsN with no on-disk workspace yet)."
|
||||||
|
echo " --serena-context CTX passed to Serena (default: desktop-app)."
|
||||||
|
echo " --git-user-name NAME / --git-user-email EMAIL"
|
||||||
|
echo " identity wired into the container's git config"
|
||||||
|
echo " (default: host's effective 'git config user.X',"
|
||||||
|
echo " honouring per-repo local overrides; see"
|
||||||
|
echo " devenv.md > 'Git identity inside the container')."
|
||||||
|
echo "- start-devenv Bring ws0 + shared infra up in the background (no tmux, no MCP/Serena)."
|
||||||
|
echo "- create-devenv Create ws0 + shared-infra compose services without starting them."
|
||||||
|
echo "- stop-devenv Stop one or more workspaces. Shared infra stops with the last one."
|
||||||
|
echo " Options: --ws N (stop wsN, N >= 1) | (no flag) (stop ws0 + infra;"
|
||||||
|
echo " refused if any wsN is still running) | --all (stop every wsN"
|
||||||
|
echo " highest first, then ws0 + infra)."
|
||||||
|
echo "- drop-devenv Same CLI and invariants as stop-devenv (see above), plus removal of"
|
||||||
|
echo " the shared devenv image on full teardowns (no flag, --ws 0, or --all)."
|
||||||
|
echo " Refused if any wsN (N >= 1) is still running. A single --ws N (N >= 1)"
|
||||||
|
echo " keeps the image since other workspaces still need it."
|
||||||
|
echo "- log-devenv Tail a workspace's compose logs."
|
||||||
|
echo " Options: --ws N (default: 0)."
|
||||||
|
echo ""
|
||||||
|
echo "Work inside a running devenv"
|
||||||
|
echo "- attach-devenv Attach to the tmux session inside a running workspace."
|
||||||
|
echo " Options: --ws N (default: 0)."
|
||||||
|
echo "- start-coding-agent <client> Launch an AI coding agent against one workspace with the right MCP"
|
||||||
|
echo " config wired in. cd's into the workspace, refuses to launch if the"
|
||||||
|
echo " instance is not running, and forwards extra args to the client."
|
||||||
echo " client: claude | opencode | vscode | codex"
|
echo " client: claude | opencode | vscode | codex"
|
||||||
echo " Options: --ws N (default: 0; non-negative integer only)."
|
echo " Options: --ws N (default: 0). See agentic-devenv.md and"
|
||||||
echo " cd's into the target workspace and refuses to launch if the instance is not running."
|
echo " .devenv/README.md for per-client setup and override paths."
|
||||||
echo " Extra args after --ws (or after the client name) are forwarded to the underlying client."
|
echo "- run-devenv-shell Open a bash shell inside the workspace's running devenv container"
|
||||||
echo " See docs/technical-guide/developer/agentic-devenv.md and .devenv/README.md"
|
echo " ('docker exec' into the live 'main' container alongside the tmux"
|
||||||
echo " for per-client setup, override paths, and the Codex 'trusted project' caveat."
|
echo " session). Requires the workspace to be up."
|
||||||
echo "- run-devenv-shell Opens a bash shell inside a running instance."
|
echo " Options: --ws N (default: 0)."
|
||||||
echo " Options: --instance 0|wsN|N (default: 0)"
|
echo "- run-devenv Start ws0 if needed and attach to its tmux session interactively."
|
||||||
echo "- stop-devenv Stops instances. ws0 must be the last to stop; shared infra stops with ws0."
|
echo " Optional -e flags are forwarded to 'docker exec' (e.g. -e MY_VAR=value)."
|
||||||
echo " Options: --ws N (stop one ws1+) | --ws 0 | (none) (stop ws0 + infra) | --all"
|
echo "- isolated-shell Spawn a fresh, ephemeral devenv container ('docker run', not 'docker exec')"
|
||||||
echo "- isolated-shell Starts a bash shell in a new devenv container."
|
echo " with the workspace's source tree and build-cache volume mounted. Use this"
|
||||||
echo "- log-devenv Show logs of the running devenv docker compose service."
|
echo " for ad-hoc work that should not touch the running devenv (e.g. manual"
|
||||||
|
echo " builds); the workspace does not need to be running."
|
||||||
|
echo " Options: --ws N (default: 0)."
|
||||||
echo ""
|
echo ""
|
||||||
echo "- build-bundle Build all bundles (frontend, backend, exporter, storybook and mcp)."
|
echo "- build-bundle Build all bundles (frontend, backend, exporter, storybook and mcp)."
|
||||||
echo "- build-frontend-bundle Build frontend bundle"
|
echo "- build-frontend-bundle Build frontend bundle"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user