diff --git a/.gitignore b/.gitignore
index 397a63f..db0ed7d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,5 @@ dist/
releases/
.env
*.log
+WORKING/
+skills/installed/
diff --git a/README.md b/README.md
index 001b084..f145069 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,11 @@
+ English | 简体中文 +
+ +[](docs/assets/devspace-screenshot.png) **Give ChatGPT a secure connection to your own machine and Turn ChatGPT into Codex** @@ -117,6 +121,116 @@ Most users should connect through a public HTTPS tunnel: https://your-tunnel-host.example.com/mcp ``` +## Configuration Management + +Update the local server config with short commands: + +```bash +# Show the effective runtime configuration +devspace config show + +# Change the local listening port +devspace config port 7676 + +# Change the local bind host +devspace config host 127.0.0.1 + +# Set the public domain or URL +devspace config domain devspace.example.com + +# Rotate the Owner password +devspace config key +``` + +Configuration changes are saved immediately. If a managed DevSpace background +service is currently running, DevSpace automatically restarts it so the new +settings take effect right away. + +`devspace config show` displays the effective bind host, port, MCP path, public +URL, workspace list, service state, and a masked access key. If the current +Owner password comes from `DEVSPACE_OAUTH_OWNER_TOKEN`, DevSpace masks and shows +that effective value instead of reporting it as missing. + +`devspace config key` rotates the existing DevSpace Owner password, clears saved +OAuth approvals and tokens, and forces connected clients to reauthorize. + +## Workspace Management + +Persist the workspace roots DevSpace is allowed to open: + +```bash +# Add a workspace and mark it as the default one +devspace workspace add ~/workspace/project-a --default + +# Add another workspace without changing the default +devspace workspace add ~/workspace/project-b + +# Show configured workspaces +devspace workspace list + +# Switch the default workspace +devspace workspace default ~/workspace/project-b + +# Remove a workspace from the allowlist +devspace workspace remove ~/workspace/project-a +``` + +You can also allow extra paths for one run only: + +```bash +devspace serve --add-dir ~/scratch/project-c --workspace ~/workspace/project-b +``` + +Workspace paths are the authorization boundary for DevSpace and MCP file tools. +Adding a workspace authorizes only that path and its children. + +If you start DevSpace without any configured workspaces or `DEVSPACE_ALLOWED_ROOTS`, +DevSpace now fails closed: the server can start, but workspace access is denied +until you explicitly add an allowed path. + +## Service Management + +DevSpace service management only manages DevSpace itself. `devspace service start` +acts as the single entrypoint: if the background service is missing, DevSpace +creates it for the current platform and starts it; if it already exists, it +just starts it. It does not manage arbitrary system services. + +```bash +# Start the managed DevSpace background service +devspace service start + +# Show whether the service is installed and running +devspace service status + +# Read the service log output +devspace service logs + +# Restart the running service +devspace service restart + +# Stop the running service +devspace service stop + +# Disable automatic service startup +devspace service disable + +# Remove the installed DevSpace background service +devspace service remove + +# Check service-manager support and current health +devspace service doctor +``` + +Platform behavior: + +- macOS uses a per-user LaunchAgent. +- Linux and Ubuntu use a per-user systemd service when available. +- Windows uses Task Scheduler. +- WSL prefers user systemd and otherwise reports a Windows Task Scheduler fallback. + +DevSpace does not automatically configure DNS, reverse proxies, TLS +certificates, or firewall rules. + ## What ChatGPT Can Do Once connected, ChatGPT can open one of your approved project folders as a @@ -133,6 +247,113 @@ DevSpace gives ChatGPT tools to: - discover local agent skills from your skill folders - show tool cards and optional change summaries in ChatGPT Apps-compatible hosts +DevSpace bundles durable workflow Skills rather than short prompt examples. Core Skills cover project Plan recovery, Goal definition and status, workflow resumption, architecture review, and Skill authoring. + +Project Skill directories are split by purpose: + +- `skills/.system`: exactly five DevSpace-owned system Skills: `plan`, `goal`, `workflow`, `architecture-review`, and `skill-authoring` +- `skills/local`: project-defined Skills you want to keep in version control +- `skills/installed`: user-installed external Skills, ignored by git by default + +ChatGPT Plus on the web cannot natively install or register Codex Skills. DevSpace provides MCP-side discovery, resolution, and controlled `skill://` resource access instead. + +`@devspace /plan` and `@devspace /goal` are stable alias-style workflow conventions. `/plan` always resolves to system `plan`; `/goal` always resolves to system `goal`; local, installed, and global Skills cannot silently override them. `skills/.system/README.md` records the system Skill policy and change log. + +## Using `/plan` and `/goal` + +Use these aliases in a normal ChatGPT message after DevSpace is connected. They are not native ChatGPT slash commands. Open the workspace first, then state the requested outcome clearly. + +### `/plan`: inspect first, then save an implementation plan + +Use `/plan` when you want repository analysis and a durable implementation plan before any file changes. DevSpace loads the current Plan when one exists, enters Plan Mode, inspects the repository read-only, then persists a Plan with ordered steps, validation, risks, and a revision number. + +```text +@devspace Open /path/to/project. + +/plan Add a hello CLI command that prints "Hello DevSpace". +First inspect the project and create a persistent Plan. +Do not modify project files or run commands that write to the repository. +``` + +A good `/plan` request states the outcome, relevant constraints, and whether implementation should wait for approval. To review a saved Plan later, ask DevSpace to open the same workspace and read the current Plan before taking action. + +```text +@devspace Open /path/to/project. + +Read the current Plan and summarize its title, revision, pending steps, validation, and blockers. Do not modify files. +``` + +### `/goal`: keep a durable outcome across sessions + +Use `/goal` when an objective should remain available across future DevSpace sessions. A Goal records the objective, scope, success criteria, verification, stop conditions, current status, and exact metrics where evidence exists. DevSpace reads the active Goal first and will not silently replace it with a competing one. + +```text +@devspace Open /path/to/project. + +/goal Create a durable Goal to add a hello CLI command. +Success criteria: the command runs and prints "Hello DevSpace". +Verification: run the command and its automated test. +Stop condition: the project requirements change to a non-CLI interface. +Do not modify files yet. +``` + +You can explicitly start or pause the Goal work timer, or update its status when work is blocked, completed, or archived. + +```text +@devspace Start the current Goal work timer. + +@devspace Pause the current Goal work timer and show the measured work duration. +``` + +### Using them together + +Create a Goal for the long-lived outcome, then create a Plan that breaks the Goal into concrete steps and explicitly links the Plan to that Goal. Goal progress is calculated only from completed steps in that linked Plan; DevSpace does not guess a percentage. Provider token metrics are recorded only when an API/provider returns real token usage and a stable request ID, so ChatGPT web usage is not filled in automatically. + +```text +@devspace Create a Plan for the current Goal, link the Plan to the Goal, and save it without modifying files. +``` + +Manage installed skills with: + +```bash +# Install a skill for the current context +devspace skills install --repo openai/skills --path skills/.curated/research + +# Install a skill for one specific workspace +devspace skills install --workspace /path/to/project --repo openai/skills --path skills/.curated/research + +# List skills for the current context +devspace skills list + +# List skills for one specific workspace +devspace skills list --workspace /path/to/project + +# Remove a skill from the current context +devspace skills remove research + +# Remove a skill from one specific workspace +devspace skills remove --workspace /path/to/project research + +# Install a global skill +devspace skills install -g --repo openai/skills --path skills/.curated/research + +# List global skills +devspace skills list -g + +# Remove a global skill +devspace skills remove -g research +``` + +`--repo/--path` and `--local-path` must point directly at one standard skill directory that contains `SKILL.md`. Repository roots, plugin roots, command folders, and agent-rules directories are rejected. + +## Project Workflow Store + +DevSpace stores compact project-scoped workflow state: the current Plan, Goal, Plan Mode, structured step state, and at most 100 concise workflow events. It does not store chat transcripts, raw tool output, shell logs, or file snapshots. Goal metrics are limited to exact provider-reported token records, an explicit server work timer, and progress derived from a Plan explicitly linked to that Goal. + +The same canonical project directory shares Plan and Goal state across ChatGPT sessions and DevSpace restarts. Different projects and different Git worktree roots remain isolated. `open_workspace` returns a small `workflowDigest`; call `get_plan`, `get_goal`, and paginated `get_workflow_history` only when full state is needed. + +Plan and Goal writes use optimistic concurrency. Read the current state first, then send `expectedRevision`; stale sessions receive a revision conflict instead of silently overwriting newer work. + ## Mental Model DevSpace is remote access to selected local folders. @@ -152,7 +373,8 @@ For a normal ChatGPT coding session: ## Platform Support DevSpace supports Linux, macOS, and Windows environments with a Bash-compatible -shell. +shell for the main CLI, and supports native per-user service control on macOS, +Linux, Windows, and WSL. | Platform | Status | Notes | | ------------------------------------------------- | ----------------- | ---------------------------------------------- | diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 0000000..fd2a339 --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,363 @@ +
+
+
把类似 Codex 的编程工作流带到 ChatGPT。
+ + + ++ English | 简体中文 +
+ +[](docs/assets/devspace-screenshot.png) + +**为 ChatGPT 提供一条安全连接到你自己机器的通道,把 ChatGPT 变成 Codex** + +DevSpace 是一个自托管的 MCP 服务器,让 ChatGPT 可以直接在你真实的本地项目里读取、编辑、搜索和运行代码,也就是使用你的文件、你的工具、你的终端,而无需把内容上传到第三方。它运行在你的机器上,通过你自己控制的隧道暴露出去,并使用只有你知道的密码来批准连接。 + +## 安装 + +DevSpace 需要 Node `>=20.12 <27`,推荐使用 Node 22 LTS。 + +安装 DevSpace CLI: + +```bash +npm install -g @waishnav/devspace +``` + +然后初始化并启动服务: + +```bash +devspace init +devspace serve +``` + +如果你不想全局安装,也可以直接运行: + +```bash +npx @waishnav/devspace init +npx @waishnav/devspace serve +``` + +在安装过程中,DevSpace 会询问你: + +- ChatGPT 被允许通过 DevSpace 打开的本地项目目录 +- 本地端口,通常为 `7676` +- 你的公网 HTTPS 基础地址,可以来自 Cloudflare Tunnel、ngrok、Pinggy、Tailscale Funnel 或其他反向代理 + +在初始化时,请填写不带 `/mcp` 的公网基础地址: + +```text +https://your-tunnel-host.example.com +``` + +完成设置后,再把带 `/mcp` 的公网地址配置到你的 MCP 客户端中。 + +当客户端连接时,DevSpace 会打开一个 Owner 密码确认页面。输入 `devspace init` 打印出来的 Owner 密码即可。这个密码也会保存到: + +```text +~/.devspace/auth.json +``` + +请妥善保管,不要泄露。 + +## 连接你的 MCP 客户端 + +默认的本地端点是: + +```text +http://127.0.0.1:7676/mcp +``` + +大多数用户应该通过公网 HTTPS 隧道连接: + +```text +https://your-tunnel-host.example.com/mcp +``` + +## 配置管理 + +你可以用这些简短命令更新本地服务配置: + +```bash +# 查看当前生效配置 +devspace config show + +# 修改本地监听端口 +devspace config port 7676 + +# 修改本地绑定地址 +devspace config host 127.0.0.1 + +# 设置公网域名或 URL +devspace config domain devspace.example.com + +# 轮换 Owner 密码 +devspace config key +``` + +配置修改后会立即保存。如果当前有由 DevSpace 管理的后台服务正在运行,DevSpace 会自动重启它,让新设置立刻生效。 + +`devspace config show` 会显示生效中的绑定地址、端口、MCP 路径、公网 URL、工作区列表、服务状态,以及打码后的访问密钥。如果当前 Owner 密码来自 `DEVSPACE_OAUTH_OWNER_TOKEN`,DevSpace 会显示打码后的实际值,而不是把它报告为缺失。 + +`devspace config key` 会轮换现有的 DevSpace Owner 密码、清除已保存的 OAuth 批准和令牌,并强制已连接客户端重新授权。 + +## 工作区管理 + +持久化保存 DevSpace 被允许打开的工作区根目录: + +```bash +# 添加工作区,并设为默认工作区 +devspace workspace add ~/workspace/project-a --default + +# 再添加一个工作区,但不修改默认项 +devspace workspace add ~/workspace/project-b + +# 查看当前工作区列表 +devspace workspace list + +# 把默认工作区切换到 project-b +devspace workspace default ~/workspace/project-b + +# 删除一个工作区配置 +devspace workspace remove ~/workspace/project-a +``` + +你也可以只在当前这次运行中临时允许额外路径: + +```bash +devspace serve --add-dir ~/scratch/project-c --workspace ~/workspace/project-b +``` + +工作区路径就是 DevSpace 与 MCP 文件工具的授权边界。添加某个工作区,只会授权这个路径及其子路径。 + +如果你启动 DevSpace 时既没有已配置工作区,也没有设置 `DEVSPACE_ALLOWED_ROOTS`,DevSpace 现在会默认拒绝访问:服务可以启动,但在你显式添加允许路径之前,工作区访问会被拒绝。 + +## 服务管理 + +DevSpace 的服务管理只负责管理 DevSpace 本身。`devspace service start` 是统一入口:如果后台服务不存在,DevSpace 会按当前平台创建并启动;如果已经存在,则只执行启动。它不会管理任意系统服务。 + +```bash +# 启动 DevSpace 后台服务 +devspace service start + +# 查看服务当前状态 +devspace service status + +# 查看服务日志 +devspace service logs + +# 重启服务 +devspace service restart + +# 停止服务 +devspace service stop + +# 禁用服务自启动 +devspace service disable + +# 删除已安装的 DevSpace 后台服务 +devspace service remove + +# 检查服务管理环境和健康状态 +devspace service doctor +``` + +平台行为如下: + +- macOS 使用按用户安装的 LaunchAgent +- Linux 和 Ubuntu 在可用时使用按用户安装的 systemd 服务 +- Windows 使用任务计划程序 +- WSL 优先使用用户级 systemd,否则会提示回退到 Windows 任务计划程序 + +DevSpace 不会自动帮你配置 DNS、反向代理、TLS 证书或防火墙规则。 + +## ChatGPT 能做什么 + +连接建立后,ChatGPT 可以把你已批准的某个项目目录作为工作区打开。之后它就可以检查仓库、做有限范围的修改、运行命令,并向你展示变更内容。 + +DevSpace 为 ChatGPT 提供了这些能力: + +- 读取、写入和编辑已打开工作区内的文件 +- 搜索代码并查看目录结构 +- 运行测试、构建、Git 和包管理脚本相关命令 +- 使用隔离的 Git worktree 并行处理多个编码会话 +- 遵循项目里的 `AGENTS.md` 和 `CLAUDE.md` 指令 +- 从你的技能目录中发现本地 agent skills +- 在兼容 ChatGPT Apps 的宿主中显示工具卡片和可选的变更摘要 + +DevSpace 内置一组稳定的工作流与工程技能,用于 Plan、Goal、跨会话恢复、架构审查和 Skill 编写。 + +项目技能目录按用途拆分为: + +- `skills/.system`:DevSpace 自己维护的 5 个系统技能:`plan`、`goal`、`workflow`、`architecture-review`、`skill-authoring` +- `skills/local`:你希望随项目版本控制保存的项目自定义技能 +- `skills/installed`:按需安装的外部技能,默认被 git 忽略 + +网页版 ChatGPT Plus 不能原生安装或注册 Codex Skills。DevSpace 改为在 MCP 这一侧提供技能安装、发现和解析这一层能力。 + +`@devspace /plan` 和 `@devspace /goal` 只是别名风格的工作流约定,不是 ChatGPT 原生斜杠命令。`/plan` 固定对应系统 `plan`,`/goal` 固定对应系统 `goal`;本地、已安装和全局技能都不能覆盖它们。 + +## 如何使用 `/plan` 和 `/goal` + +DevSpace 已连接后,在普通 ChatGPT 消息中使用这两个别名即可。它们不是 ChatGPT 界面原生注册的斜杠命令。先打开工作区,再清晰说明目标。 + +### `/plan`:先分析,再保存实施计划 + +需要在改代码前先梳理现状、设计实施步骤时使用 `/plan`。DevSpace 会优先读取已有 Plan,进入 Plan Mode,以只读方式检查项目,然后保存包含步骤、验证方式、风险和 revision 的持久 Plan。 + +```text +@devspace 打开 /path/to/project。 + +/plan 为项目增加一个 hello CLI 命令,输出 "Hello DevSpace"。 +先检查项目,再创建并保存 Plan。 +不要修改项目文件,也不要执行会写入仓库的命令。 +``` + +一个好的 `/plan` 请求应说明目标、关键约束,以及是否需要等待你确认后再实施。之后想查看 Plan 时,重新打开同一个工作区,并要求 DevSpace 先读取当前 Plan。 + +```text +@devspace 打开 /path/to/project。 + +读取当前 Plan,告诉我 title、revision、未完成步骤、validation 和 blocker;不要修改文件。 +``` + +### `/goal`:跨会话保存长期目标 + +需要跨会话持续推进一个结果时使用 `/goal`。Goal 会保存目标、范围、成功标准、验证方式、停止条件、当前状态,以及有可靠依据时的精确指标。DevSpace 会先读取当前活跃 Goal,不会静默用新 Goal 覆盖旧 Goal。 + +```text +@devspace 打开 /path/to/project。 + +/goal 创建一个持久 Goal:为项目增加 hello CLI 命令。 +成功标准:命令可运行并输出 "Hello DevSpace"。 +验证方式:运行命令和对应自动化测试。 +停止条件:项目需求改为非 CLI 交互。 +暂时不要修改文件。 +``` + +你可以显式开始或暂停 Goal 工作计时,也可以在任务受阻、完成或不再继续时更新 Goal 状态。 + +```text +@devspace 开始当前 Goal 的工作计时。 + +@devspace 暂停当前 Goal 的工作计时,并返回已测量的工作时长。 +``` + +### 配合使用 + +先用 Goal 保存长期目标,再创建 Plan 把目标拆成可执行步骤,并明确把该 Plan 绑定到 Goal。Goal 进度只根据已绑定 Plan 中已完成的步骤计算,DevSpace 不会猜测百分比。Provider Token 指标也只在 API/Provider 返回真实 token usage 和稳定 request ID 时记录,ChatGPT 网页版用量不会自动填充。 + +```text +@devspace 为当前 Goal 创建 Plan,显式绑定到这个 Goal,并保存 Plan;不要修改文件。 +``` + +用这些命令管理已安装技能: + +```bash +# 为当前上下文安装 skill +devspace skills install --repo openai/skills --path skills/.curated/research + +# 只为某个工作区安装 skill +devspace skills install --workspace /path/to/project --repo openai/skills --path skills/.curated/research + +# 查看当前上下文可用的 skill +devspace skills list + +# 查看指定工作区的 skill +devspace skills list --workspace /path/to/project + +# 从当前上下文移除 skill +devspace skills remove research + +# 从指定工作区移除 skill +devspace skills remove --workspace /path/to/project research + +# 安装全局 skill +devspace skills install -g --repo openai/skills --path skills/.curated/research + +# 查看全局 skill 列表 +devspace skills list -g + +# 删除全局 skill +devspace skills remove -g research +``` + +`--repo/--path` 和 `--local-path` 必须直接指向一个标准技能目录,并且其中包含 `SKILL.md`。仓库根目录、插件根目录、命令目录和 agent-rules 目录都会被拒绝。 + +## 心智模型 + +DevSpace 本质上是对选定本地目录的远程访问。 + +你来决定哪些根目录被允许访问。MCP 客户端在已打开工作区内仍然具备很强的本地能力,包括执行 shell 命令。因此,你应该把一个已连接的客户端视为一位受信任的编程协作者,它能够访问你的机器。 + +对于一次普通的 ChatGPT 编程会话: + +1. 启动你的隧道。 +2. 运行 `devspace serve`。 +3. 把 MCP 客户端连接到你的公网 `/mcp` URL。 +4. 使用 Owner 密码批准连接。 +5. 让 ChatGPT 在你的某个允许根目录内打开项目。 + +## 平台支持 + +DevSpace 支持 Linux、macOS、Windows 环境下带 Bash 兼容 shell 的主 CLI,并支持在 macOS、Linux、Windows 和 WSL 上进行原生的按用户服务控制。 + +| 平台 | 状态 | 说明 | +| --- | --- | --- | +| Linux | 支持 | 需要 Node、npm、Git 和 Bash。 | +| macOS | 支持 | 需要 Node、npm、Git 和 Bash。 | +| Windows with Git Bash, WSL, MSYS2, or Cygwin Bash | 支持 | 原生 Windows 环境下最简单的是 Git Bash。 | +| Windows PowerShell or `cmd.exe` only | 暂不支持 | 请安装 Git Bash 或使用 WSL。 | + +你可以运行下面的命令检查本地环境: + +```bash +devspace doctor +``` + +## 文档 + +- [安装指南](docs/setup.md) +- [ChatGPT 编码工作流](docs/chatgpt-coding-workflow.md) +- [配置参考](docs/configuration.md) +- [安全模型](docs/security.md) +- [常见问题与排障](docs/gotchas.md) + +## 理念 + +每一类软件都正在变得可对话。自然语言正在重新定义我们与工具、工作流和系统交互的方式。 + +我的判断是,ChatGPT 会成为一切的操作系统。一旦抵达 AGI,我们大概只需要和 ChatGPT 对话,它就会替我们提示、协调、编排子代理,并搭建合适的执行闭环。 + +但现在还没到那一步。 + +DevSpace 是一次试图把那个未来往前拉近的尝试:让像 ChatGPT、Claude 这样支持 MCP 的宿主,可以通过显式、可检查的工具,直接操作本地项目文件。 + +## Built by Waishnav + +我是 Waishnav,[GitCMS](https://gitcms.dev/) 的创建者。GitCMS 是一个面向 Markdown 网站、基于 Git 的 CMS。 + +我喜欢做带有明确产品判断的工具,而 DevSpace 也是这样的产品之一。我正在尝试建立一家由单人运营、能做到数百万营收的公司。如果你想围观其中的失败、胜利、经验和过程,欢迎在 [X](https://x.com/wshxnv) 上关注我。 + +## 本地开发 + +如果你要开发 DevSpace 自身,可以使用: + +```bash +npm install --include=dev +npm run dev +npm run typecheck +npm test +npm run build +npm run start +``` diff --git a/docs/chatgpt-coding-workflow.md b/docs/chatgpt-coding-workflow.md index c5efc66..41caf4c 100644 --- a/docs/chatgpt-coding-workflow.md +++ b/docs/chatgpt-coding-workflow.md @@ -14,8 +14,7 @@ ChatGPT should call `open_workspace` once for a project folder: } ``` -The result includes a `workspaceId`. All later file, search, edit, show-changes, -and shell calls should reuse that same `workspaceId`. +The result includes a `workspaceId` and a compact `workflowDigest`. All later file, search, edit, show-changes, shell, Skill, Plan, and Goal calls should reuse that same `workspaceId`. Do not reopen the same folder unless: @@ -79,22 +78,69 @@ new context during later tool calls. Skills are enabled by default for coding-agent workflows. -DevSpace discovers skills from: +DevSpace discovers Skills from: +- five DevSpace system Skills in `skills/.system`: `plan`, `goal`, `workflow`, `architecture-review`, and `skill-authoring` +- workspace-local Skills in `skills/local` +- workspace-installed Skills in `skills/installed` - `DEVSPACE_AGENT_DIR`, which defaults to `~/.codex` -- project `.pi/skills` - optional paths from `DEVSPACE_SKILL_PATHS` -When `open_workspace` returns matching skills, the model should read the -advertised `SKILL.md` before following that skill. +ChatGPT Plus on the web cannot natively install or register Codex Skills. In this setup, DevSpace provides MCP-based skill installation, discovery, and resolution. -Skill paths may be outside the workspace. DevSpace only permits reading: +`@devspace /plan` and `@devspace /goal` are workflow aliases, not native ChatGPT slash commands. `/plan` always resolves to system `plan`; `/goal` always resolves to system `goal`. Project-local, installed, and global Skills cannot silently override either alias. -- advertised `SKILL.md` files -- files under a skill directory after that skill's `SKILL.md` has been read +User-installed project skills can be managed through DevSpace itself: + +```text +请使用 DevSpace 打开当前项目,然后调用 install_skill,把 GitHub 仓库 openai/skills 里的 skills/.curated/research 安装到当前 workspace。 +``` + +```text +请注意 install_skill 只接受标准 skill 包目录。像仓库根目录、plugin 目录、commands 目录或 agent rules 目录都不应该安装,只有直接包含 SKILL.md 的 skill 目录才可以。 +``` + +```text +请调用 list_installed_skills,列出当前 workspace 的 installed skills。 +``` + +```text +请调用 remove_skill,删除当前 workspace 里名为 research 的 installed skill。 +``` + +```text +@devspace /plan 为跨平台服务管理增加 restart、status 和 logs 支持 +``` + +```text +@devspace /goal 将 DevSpace 的第三方 Skill 安装流程收敛为可测试、可回滚、跨平台兼容的实现 +``` + +`open_workspace` returns system and project Skill metadata only, capped at 24 entries, plus a source-count summary. Use `resolve_skill` to load the full `SKILL.md` once a Skill is selected. Use `search_skills` to discover additional local, installed, or global Skills without loading every Skill instruction into context. + +Skill resources use `skill://` locators. DevSpace only permits reading: + +- a resolved `SKILL.md` +- files under an activated Skill directory Set `DEVSPACE_SKILLS=0` to hide skills from workspace output. +DevSpace system Skills define the stable `/plan`, `/goal`, workflow recovery, and MCP Tool contracts. External Skills are installed only when needed and never control the core aliases. + +## Project Workflow Store + +DevSpace keeps only a small project-scoped workflow state. It is shared by every DevSpace session opened on the same canonical directory, while different project roots and different Git worktree roots stay isolated. + +`open_workspace` returns only `workflowDigest`, not Plan history, Goal history, chat transcripts, tool output, or shell logs. Load full state on demand: + +- `get_plan`: current Plan, step states, validation, risks, and revision +- `get_goal`: current Goal, criteria, verification, stop conditions, summary, and revision +- `get_workflow_history`: concise paginated status events; default 20, maximum 50 + +Create a Plan with `update_plan(expectedRevision=0, ...)`. For an existing Plan or Goal, first call `get_plan` or `get_goal`, then pass the returned `expectedRevision`. A conflict means another session updated state first; reload and merge rather than overwriting it. + +`plan` mode is a planning preference, not a permission boundary. It permits `update_plan` but should not perform project file changes until the user approves execution. + ## Tool Names Short names are the default: diff --git a/docs/configuration.md b/docs/configuration.md index 7107338..341a560 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -22,10 +22,92 @@ DEVSPACE_CONFIG_DIR=/path/to/config npx @waishnav/devspace serve npx @waishnav/devspace init npx @waishnav/devspace serve npx @waishnav/devspace doctor +npx @waishnav/devspace config show +npx @waishnav/devspace config port 7676 +npx @waishnav/devspace config host 127.0.0.1 +npx @waishnav/devspace config domain devspace.example.com +npx @waishnav/devspace config key +npx @waishnav/devspace workspace add ~/workspace/project-a --default +npx @waishnav/devspace workspace list +npx @waishnav/devspace service start +npx @waishnav/devspace service status +npx @waishnav/devspace service logs npx @waishnav/devspace config get -npx @waishnav/devspace config set publicBaseUrl https://devspace.example.com ``` +## Configuration Management + +The primary config commands are: + +```bash +devspace config show +devspace config port 7676 +devspace config host 127.0.0.1 +devspace config domain devspace.example.com +devspace config key +``` + +`config port`, `config host`, `config domain`, and `config key` save the new +value immediately. If a managed DevSpace background service is currently +running, DevSpace automatically restarts it. + +`config key` rotates the existing Owner password stored in `auth.json`, +invalidates saved OAuth approvals and tokens, and requires clients to +reauthorize. + +`config show` reports the effective runtime values. Access keys are always +masked. If the active Owner password comes from `DEVSPACE_OAUTH_OWNER_TOKEN`, +DevSpace masks and shows that effective value. + +## Workspace Management + +Persisted workspace roots replace the old one-shot “roots only at init” flow: + +```bash +devspace workspace add ~/workspace/project-a --default +devspace workspace add ~/workspace/project-b +devspace workspace list +devspace workspace remove ~/workspace/project-a +devspace workspace clear-default +``` + +Use temporary workspace overrides for one run: + +```bash +devspace serve --add-dir ~/scratch/project-c --workspace ~/workspace/project-b +``` + +These workspace paths define the authorization boundary for DevSpace file tools. +If no workspace is configured and `DEVSPACE_ALLOWED_ROOTS` is unset, DevSpace +starts in a safe blocked state with no authorized workspace roots. + +## Service Management + +DevSpace only manages its own background service. `devspace service start` +installs the service on first use for the current platform, and starts it on +later runs: + +```bash +devspace service start +devspace service status +devspace service restart +devspace service stop +devspace service disable +devspace service remove +devspace service logs +devspace service doctor +``` + +Platform behavior: + +- Linux and Ubuntu use `systemctl --user` when user systemd is available. +- macOS uses a per-user LaunchAgent. +- Windows uses Task Scheduler. +- WSL uses user systemd when available and otherwise reports a Task Scheduler fallback. + +DevSpace never auto-configures DNS, reverse proxies, TLS certificates, or +firewall rules. + ## Core Environment Variables | Variable | Purpose | @@ -34,10 +116,17 @@ npx @waishnav/devspace config set publicBaseUrl https://devspace.example.com | `PORT` | Local port. Defaults to `7676`. | | `DEVSPACE_ALLOWED_ROOTS` | Comma-separated local roots that workspaces may open. | | `DEVSPACE_PUBLIC_BASE_URL` | Public origin for the server, without `/mcp`. | +| `DEVSPACE_MCP_PATH` | Optional MCP path override. Defaults to `/mcp`. | +| `DEVSPACE_TUNNEL` | Optional automatic tunnel mode. Currently supports `cloudflare` when explicitly enabled. | | `DEVSPACE_ALLOWED_HOSTS` | Optional Host header allowlist override. | | `DEVSPACE_OAUTH_OWNER_TOKEN` | Owner password for OAuth approval. Must be at least 16 characters. | | `DEVSPACE_WORKTREE_ROOT` | Directory for managed Git worktrees. Defaults to `~/.devspace/worktrees`. | | `DEVSPACE_STATE_DIR` | Directory for SQLite state. Defaults to `~/.local/share/devspace`. | +| `DEVSPACE_SESSION_WORKSPACE` | Temporary default workspace for the current `serve` run. | + +When `DEVSPACE_ALLOWED_ROOTS` is omitted, DevSpace does not fall back to the +current working directory anymore. You must explicitly configure allowed roots +through `devspace workspace add ...` or this environment variable. ## OAuth @@ -49,6 +138,13 @@ DevSpace uses a single-user OAuth approval flow. | `DEVSPACE_OAUTH_REFRESH_TOKEN_TTL_SECONDS` | `2592000` | | `DEVSPACE_OAUTH_SCOPES` | `devspace` | | `DEVSPACE_OAUTH_ALLOWED_REDIRECT_HOSTS` | `chatgpt.com,localhost,127.0.0.1` | +| `DEVSPACE_OAUTH_STATE_PATH` | `$DEVSPACE_STATE_DIR/oauth.json` | + +Registered OAuth clients, token hashes, authorization code hashes, and approved +consents are persisted in SQLite at `$DEVSPACE_STATE_DIR/devspace.sqlite` by +default. `DEVSPACE_OAUTH_STATE_PATH` is kept as the legacy JSON state import +path; when an existing JSON file is present, DevSpace imports compatible clients, +token hashes, and consents into SQLite without storing raw tokens. MCP clients discover metadata from: @@ -73,6 +169,30 @@ MCP clients discover metadata from: | `minimal` | Default. Disables dedicated search and list tools. Clients use the shell tool with `rg`, `grep`, `find`, `ls`, or `tree` for inspection. | | `full` | Enables dedicated `grep`, `glob`, and `ls` tools. | +`DEVSPACE_SHELL_MODE` controls shell execution policy. + +| Value | Behavior | +| --- | --- | +| `full` | Default. Preserves the current shell behavior. | +| `read-only` | Allows only single-command inspection workflows such as `rg`, `git status`, `find`, or `ls`. Blocks shell control operators and mutating commands. | +| `off` | Disables shell execution entirely. | + +## Tunnel Modes + +DevSpace keeps the existing manual `publicBaseUrl` flow by default. Automatic +Cloudflare quick tunnel mode is opt-in only. + +Enable it explicitly with one of: + +```bash +npx @waishnav/devspace serve --tunnel +DEVSPACE_TUNNEL=cloudflare npx @waishnav/devspace serve +``` + +Or set `"tunnel": "cloudflare"` in `~/.devspace/config.json`. + +Use `--no-tunnel` to override configured tunnel mode for one run. + ## Widgets `DEVSPACE_WIDGETS` controls ChatGPT Apps iframe usage. @@ -91,6 +211,31 @@ MCP clients discover metadata from: | `DEVSPACE_AGENT_DIR` | Defaults to `~/.codex`. | | `DEVSPACE_SKILL_PATHS` | Optional comma-separated skill directories. | +Project skill layout: + +- system built-in DevSpace skills +- `skills/local`: project skills meant to be committed +- `skills/installed`: user-installed project skills, typically git-ignored + +ChatGPT Plus on the web cannot natively install or register Codex Skills. DevSpace provides the MCP-side skill installation, discovery, and resolution layer instead. + +Manage installed skills with: + +```bash +devspace skills install --repo openai/skills --path skills/.curated/research +devspace skills install --workspace /path/to/project --repo openai/skills --path skills/.curated/research +devspace skills list +devspace skills list --workspace /path/to/project +devspace skills remove research +devspace skills remove --workspace /path/to/project research + +devspace skills install -g --repo openai/skills --path skills/.curated/research +devspace skills list -g +devspace skills remove -g research +``` + +Both `--repo/--path` and `--local-path` must point directly at one standard skill directory with `SKILL.md`. Plugin roots, command folders, and agent-rules directories are not valid install targets. + Example: ```bash diff --git a/docs/gotchas.md b/docs/gotchas.md index c5099dc..6b7fd91 100644 --- a/docs/gotchas.md +++ b/docs/gotchas.md @@ -196,9 +196,17 @@ DEVSPACE_SKILLS=1 npx @waishnav/devspace serve DevSpace looks in: - `DEVSPACE_AGENT_DIR`, defaulting to `~/.codex` -- project `.pi/skills` +- `skills/local` +- `skills/installed` +- system built-in skills - `DEVSPACE_SKILL_PATHS` +Recommended meaning: + +- system built-in DevSpace skills +- `skills/local`: project-defined skills you want to commit +- `skills/installed`: user-installed project skills that should stay git-ignored + If a skill appears in `open_workspace`, the model must read that skill's `SKILL.md` before reading other files inside the skill directory. diff --git a/docs/setup.md b/docs/setup.md index 8efbcdc..4ad2c3e 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -11,8 +11,8 @@ projects through DevSpace. - Bash, including Git Bash or WSL on Windows - a public HTTPS URL that forwards to the local DevSpace server -DevSpace does not create the public tunnel for you. Use Cloudflare Tunnel, -ngrok, Pinggy, Tailscale Funnel, or your own HTTPS reverse proxy. +DevSpace does not create the public tunnel for you by default. Use Cloudflare +Tunnel, ngrok, Pinggy, Tailscale Funnel, or your own HTTPS reverse proxy. ## Install And Configure @@ -95,6 +95,22 @@ npx @waishnav/devspace config set publicBaseUrl https://devspace.example.com npx @waishnav/devspace serve ``` +If you explicitly want DevSpace to open a Cloudflare quick tunnel for a single +run, opt in with: + +```bash +npx @waishnav/devspace serve --tunnel +``` + +Or: + +```bash +DEVSPACE_TUNNEL=cloudflare npx @waishnav/devspace serve +``` + +This mode requires an existing `cloudflared` binary and does not replace the +default manual public URL workflow. + ## Approve The Client When ChatGPT, Claude, or another MCP client connects, DevSpace shows an Owner diff --git a/package-lock.json b/package-lock.json index acb8f63..c026ae1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "@clack/prompts": "^1.5.1", - "@earendil-works/pi-coding-agent": "^0.79.4", + "@earendil-works/pi-coding-agent": "^0.79.8", "@modelcontextprotocol/ext-apps": "^1.7.2", "@modelcontextprotocol/sdk": "^1.29.0", "@pierre/diffs": "^1.2.5", @@ -70,15 +70,15 @@ } }, "node_modules/@earendil-works/pi-coding-agent": { - "version": "0.79.4", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-0.79.4.tgz", - "integrity": "sha512-PthzVzM5m4XH/hrU+2fVjuwuH5M4eMFWbd0NCRScH14XKpwlPc8/Fh6JDz0jQb5kTBT9oQT183YLTHVVulFL9A==", + "version": "0.79.8", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-0.79.8.tgz", + "integrity": "sha512-wr9oTS/yrwURDXnYrONQgFgV7QDlwslXL/rvKU5X7TRtrGxIhippsRApXqYlRwSeMjb2YzgHMfZ/kAhOqrzoFQ==", "hasShrinkwrap": true, "license": "MIT", "dependencies": { - "@earendil-works/pi-agent-core": "^0.79.4", - "@earendil-works/pi-ai": "^0.79.4", - "@earendil-works/pi-tui": "^0.79.4", + "@earendil-works/pi-agent-core": "^0.79.8", + "@earendil-works/pi-ai": "^0.79.8", + "@earendil-works/pi-tui": "^0.79.8", "@silvia-odwyer/photon-node": "0.3.4", "chalk": "5.6.2", "cross-spawn": "7.0.6", @@ -92,7 +92,7 @@ "proper-lockfile": "4.1.2", "semver": "7.8.0", "typebox": "1.1.38", - "undici": "8.3.0", + "undici": "8.5.0", "yaml": "2.9.0" }, "bin": { @@ -541,11 +541,11 @@ } }, "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-agent-core": { - "version": "0.79.4", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.79.4.tgz", + "version": "0.79.8", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.79.8.tgz", "license": "MIT", "dependencies": { - "@earendil-works/pi-ai": "^0.79.4", + "@earendil-works/pi-ai": "^0.79.8", "ignore": "7.0.5", "typebox": "1.1.38", "yaml": "2.9.0" @@ -555,14 +555,15 @@ } }, "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-ai": { - "version": "0.79.4", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.79.4.tgz", + "version": "0.79.8", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.79.8.tgz", "license": "MIT", "dependencies": { "@anthropic-ai/sdk": "0.91.1", "@aws-sdk/client-bedrock-runtime": "3.1048.0", "@google/genai": "1.52.0", - "@mistralai/mistralai": "2.2.1", + "@mistralai/mistralai": "2.2.6", + "@opentelemetry/api": "1.9.0", "@smithy/node-http-handler": "4.7.3", "http-proxy-agent": "7.0.2", "https-proxy-agent": "7.0.6", @@ -571,19 +572,19 @@ "typebox": "1.1.38" }, "bin": { - "pi-ai": "dist/cli.js" + "pi-ai": "./dist/cli.js" }, "engines": { "node": ">=22.19.0" } }, "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-tui": { - "version": "0.79.4", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.79.4.tgz", + "version": "0.79.8", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.79.8.tgz", "license": "MIT", "dependencies": { "get-east-asian-width": "1.6.0", - "marked": "15.0.12" + "marked": "18.0.5" }, "engines": { "node": ">=22.19.0" @@ -687,6 +688,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -703,6 +707,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -719,6 +726,9 @@ "cpu": [ "riscv64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -735,6 +745,9 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -751,6 +764,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -793,14 +809,23 @@ } }, "node_modules/@earendil-works/pi-coding-agent/node_modules/@mistralai/mistralai": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-2.2.1.tgz", - "integrity": "sha512-uKU8CZmL2RzYKmplsU01hii4p3pe4HqJefpWNRWXm1Tcm0Sm4xXfwSLIy4k7ZCPlbETCGcp69E7hZs+WOJ5itQ==", + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-2.2.6.tgz", + "integrity": "sha512-W8pX7zHxjJvMIpw8JMxeJEleapXX0Q9NPszdNzqkM3MIEoIGPObdodujj+WHteXEvGfaP/AMwlNyRfEzSY6dQQ==", "license": "Apache-2.0", "dependencies": { + "@opentelemetry/semantic-conventions": "^1.40.0", "ws": "^8.18.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.25.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + } } }, "node_modules/@earendil-works/pi-coding-agent/node_modules/@nodable/entities": { @@ -815,6 +840,24 @@ ], "license": "MIT" }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz", + "integrity": "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -834,9 +877,9 @@ "license": "BSD-3-Clause" }, "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", "license": "BSD-3-Clause" }, "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/fetch": { @@ -854,12 +897,6 @@ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", "license": "BSD-3-Clause" }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/inquire": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz", - "integrity": "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==", - "license": "BSD-3-Clause" - }, "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", @@ -1451,15 +1488,15 @@ } }, "node_modules/@earendil-works/pi-coding-agent/node_modules/marked": { - "version": "15.0.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", - "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "version": "18.0.5", + "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.5.tgz", + "integrity": "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==", "license": "MIT", "bin": { "marked": "bin/marked.js" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@earendil-works/pi-coding-agent/node_modules/minimatch": { @@ -1637,24 +1674,23 @@ } }, "node_modules/@earendil-works/pi-coding-agent/node_modules/protobufjs": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.9.tgz", - "integrity": "sha512-Od4muIm3HW1AouyHF5lONOf1FWo3hY1NbFDoy191X9GzhpgW1clCoaFjfVs2rKJNFYpTNJbje4cbAIDBZJ63ZA==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.4.tgz", + "integrity": "sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.5", - "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/eventemitter": "^1.1.1", "@protobufjs/fetch": "^1.1.1", "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.2", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", - "long": "^5.0.0" + "long": "^5.3.2" }, "engines": { "node": ">=12.0.0" @@ -1759,9 +1795,9 @@ "license": "MIT" }, "node_modules/@earendil-works/pi-coding-agent/node_modules/undici": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-8.3.0.tgz", - "integrity": "sha512-TkUDgb6tl7KOGZ+7e8E3d2FYgUQgF6z5YypqjWmixVQSQERFcVrVg0ySADm2LVLRh5ljAaHTCR5Fmz3Q34rB7Q==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.5.0.tgz", + "integrity": "sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==", "license": "MIT", "engines": { "node": ">=22.19.0" @@ -1798,9 +1834,9 @@ } }, "node_modules/@earendil-works/pi-coding-agent/node_modules/ws": { - "version": "8.20.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", - "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "license": "MIT", "engines": { "node": ">=10.0.0" diff --git a/package.json b/package.json index cdac9c1..c4e8adf 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "dist", "docs", "scripts", + "skills", "README.md" ], "publishConfig": { @@ -25,7 +26,7 @@ "build:app": "vite build", "dev": "node scripts/dev-server.mjs", "start": "node dist/cli.js serve", - "test": "tsx src/config.test.ts && tsx src/roots.test.ts && tsx src/skills.test.ts && tsx src/workspaces.test.ts && tsx src/review-checkpoints.test.ts && tsx src/oauth-store.test.ts && tsx src/cli.test.ts", + "test": "node scripts/dev-server.test.mjs && node --import tsx src/config.test.ts && node --import tsx src/config-operations.test.ts && node --import tsx src/oauth-provider.test.ts && node --import tsx src/oauth-store.test.ts && node --import tsx src/cloudflare-tunnel.test.ts && node --import tsx src/shell-policy.test.ts && node --import tsx src/tool-result.test.ts && node --import tsx src/roots.test.ts && node --import tsx src/skills.test.ts && node --import tsx src/skill-manager.test.ts && node --import tsx src/cli-skills.test.ts && node --import tsx src/cli.test.ts && node --import tsx src/goal-definition.test.ts && node --import tsx src/prompting.test.ts && node --import tsx src/workspace-commands.test.ts && node --import tsx src/workspace-operations.test.ts && node --import tsx src/workspaces.test.ts && node --import tsx src/workflow-store.test.ts && node --import tsx src/workflow-migration.test.ts && node --import tsx src/package-smoke.test.ts && node --import tsx src/review-checkpoints.test.ts && node --import tsx src/service.test.ts", "typecheck": "tsc -p tsconfig.json --noEmit" }, "keywords": [], @@ -33,7 +34,7 @@ "license": "MIT", "dependencies": { "@clack/prompts": "^1.5.1", - "@earendil-works/pi-coding-agent": "^0.79.4", + "@earendil-works/pi-coding-agent": "^0.79.8", "@modelcontextprotocol/ext-apps": "^1.7.2", "@modelcontextprotocol/sdk": "^1.29.0", "@pierre/diffs": "^1.2.5", @@ -59,6 +60,7 @@ }, "overrides": { "protobufjs": "7.6.4", - "ws": "8.21.0" + "ws": "8.21.0", + "undici": "8.5.0" } } diff --git a/scripts/dev-server.mjs b/scripts/dev-server.mjs index 5585bda..2575214 100644 --- a/scripts/dev-server.mjs +++ b/scripts/dev-server.mjs @@ -1,9 +1,13 @@ import { spawn } from "node:child_process"; +import { createRequire } from "node:module"; import { readdirSync, statSync, watch } from "node:fs"; import { join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; -const repoRoot = resolve(fileURLToPath(new URL("..", import.meta.url))); +const scriptPath = fileURLToPath(import.meta.url); +export const repoRoot = resolve(fileURLToPath(new URL("..", import.meta.url))); +const require = createRequire(import.meta.url); +const tsxCliPath = require.resolve("tsx/cli"); const watchRoots = ["src"].map((entry) => join(repoRoot, entry)); const restartDelayMs = 750; const crashDelayMs = 1500; @@ -17,9 +21,17 @@ function log(message) { console.error(`[devspace:dev] ${message}`); } +export function createServerCommand() { + return { + command: process.execPath, + args: [tsxCliPath, "src/cli.ts", "serve"], + }; +} + function start() { stoppingForRestart = false; - child = spawn("npx", ["tsx", "src/cli.ts", "serve"], { + const { command, args } = createServerCommand(); + child = spawn(command, args, { cwd: repoRoot, env: process.env, stdio: "inherit", @@ -108,13 +120,19 @@ function shutdown() { setTimeout(() => process.exit(1), 3000).unref(); } -for (const signal of ["SIGINT", "SIGTERM"]) { - process.on(signal, shutdown); -} +function main() { + for (const signal of ["SIGINT", "SIGTERM"]) { + process.on(signal, shutdown); + } -for (const root of watchRoots) { - watchDirectory(root); + for (const root of watchRoots) { + watchDirectory(root); + } + + log("watching src; server restarts on changes and after crashes"); + start(); } -log("watching src; server restarts on changes and after crashes"); -start(); +if (resolve(process.argv[1] ?? "") === scriptPath) { + main(); +} diff --git a/scripts/dev-server.test.mjs b/scripts/dev-server.test.mjs new file mode 100644 index 0000000..8e7ecf5 --- /dev/null +++ b/scripts/dev-server.test.mjs @@ -0,0 +1,10 @@ +import assert from "node:assert/strict"; +import { createRequire } from "node:module"; +import { createServerCommand } from "./dev-server.mjs"; + +const require = createRequire(import.meta.url); + +assert.deepEqual(createServerCommand(), { + command: process.execPath, + args: [require.resolve("tsx/cli"), "src/cli.ts", "serve"], +}); diff --git a/skills/.system/README.md b/skills/.system/README.md new file mode 100644 index 0000000..40c98f3 --- /dev/null +++ b/skills/.system/README.md @@ -0,0 +1,42 @@ +# DevSpace System Skills + +This directory contains DevSpace-owned system Skills only. + +## Entries + +| Directory | Skill | Purpose | +|---|---|---| +| `plan/` | `plan` | `/plan`, durable Plans, steps, validation, and conflicts | +| `goal/` | `goal` | `/goal`, Goal lifecycle, metrics, and conflicts | +| `workflow/` | `workflow` | recovery, modes, isolation, routing, and history | +| `architecture-review/` | `architecture-review` | evidence-based architecture review | +| `skill-authoring/` | `skill-authoring` | Skill structure and quality rules | + +## Policy + +- `/plan` always resolves to `plan`. +- `/goal` always resolves to `goal`. +- The five Skill names above are reserved system names. +- Project-local, installed, and global Skills cannot override reserved names or aliases. +- External Skills belong in `skills/installed/`, not in `.system`. +- Old system Skill identifiers are not supported; use the names listed in the table above. +- System Skill frontmatter and the change-log Version column track the root `package.json` version. Update them in the same release commit. + +## OpenAI Skills upstream record + +| Field | Value | +|---|---| +| Upstream repository | `https://github.com/openai/skills.git` | +| Upstream Git commit | `972cb867affac58fda9afa76bb1a19b399a278cf` | +| Last sync check (UTC) | `2026-06-21T23:57:02Z` | +| Sync policy | DevSpace does not mirror the full upstream repository into `.system`; external Skills are installed individually into `skills/installed/`. | + +## Change log + +| Date | Version | Change | +|---|---:|---| +| 2026-06-22 | 1.0.1 | `create-plan` and `devspace-plan` merged into `plan` | +| 2026-06-22 | 1.0.1 | `define-goal` and `devspace-goal` merged into `goal` | +| 2026-06-22 | 1.0.1 | `devspace-workflow` renamed to `workflow` | +| 2026-06-22 | 1.0.1 | architecture and authoring Skills consolidated; `-lite` copies removed | +| 2026-06-22 | 1.0.1 | full OpenAI Skill mirror removed from the package | diff --git a/skills/.system/architecture-review/SKILL.md b/skills/.system/architecture-review/SKILL.md new file mode 100644 index 0000000..74215f2 --- /dev/null +++ b/skills/.system/architecture-review/SKILL.md @@ -0,0 +1,34 @@ +--- +name: architecture-review +description: Perform evidence-driven architecture review for a DevSpace workspace without bypassing project instructions, tests, workflow state, or authorization boundaries. +license: MIT +metadata: + version: 1.0.2 + author: DevSpace + category: system-engineering + updated: 2026-06-22 +--- + +# Architecture Review + +Use this Skill for decisions spanning modules, persistent state, compatibility, security boundaries, migrations, rollout risk, or operational recovery. + +## Method + +1. Read `AGENTS.md`, relevant entry points, schema, public interfaces, tests, configuration, and deployment paths before making claims. +2. Separate observed facts from assumptions and unresolved questions. +3. Prefer the smallest compatible change that preserves migration safety and authorization boundaries. +4. Evaluate ownership, lifecycle, concurrency, failure recovery, backwards compatibility, observability, rollout, and rollback. +5. When a durable implementation plan is needed, resolve `/plan` and persist a verified Plan. + +## Output + +State: + +- constraints and evidence; +- recommended boundary and approach; +- rejected alternatives and why; +- migration, security, and rollback effects; +- tests that prove the decision. + +Do not produce generic architecture slogans or introduce a subsystem without identifying its code boundary and operational cost. diff --git a/skills/.system/architecture-review/references/decision-guide.md b/skills/.system/architecture-review/references/decision-guide.md new file mode 100644 index 0000000..5caf613 --- /dev/null +++ b/skills/.system/architecture-review/references/decision-guide.md @@ -0,0 +1,19 @@ +# Architecture Decision Guide + +Before recommending a change, establish the minimum evidence: + +- current public API, command, or tool contract; +- data schema, migration behavior, and persistence ownership; +- relevant tests and failure behavior; +- deployment, operator, and authorization boundaries; +- compatibility expectations for existing clients and stored data. + +Ask: + +1. What concrete user or operator failure does the change solve? +2. Which module owns the behavior and lifecycle? +3. What happens during partial failure, restart, retry, or concurrent access? +4. Which callers, stored records, or deployment paths can break? +5. How is the change verified and rolled back? + +Prefer a narrow adapter or migration over a new framework when an existing boundary already fits the requirement. \ No newline at end of file diff --git a/skills/.system/goal/SKILL.md b/skills/.system/goal/SKILL.md new file mode 100644 index 0000000..441705d --- /dev/null +++ b/skills/.system/goal/SKILL.md @@ -0,0 +1,36 @@ +--- +name: goal +description: Define and maintain a durable, verifiable project Goal in DevSpace. Use for /goal when the user explicitly wants an outcome to persist across sessions. +license: MIT +metadata: + version: 1.0.2 + author: DevSpace + category: system-workflow + updated: 2026-06-22 +--- + +# DevSpace Goal + +Use this Skill for `/goal` or when the user explicitly requests a durable cross-session objective. Do not create a Goal for every ordinary coding request. + +## Required lifecycle + +1. Call `get_goal` first. +2. With no active Goal, create one with a concrete objective, scope, success criteria, verification, stop conditions, and concise current summary. +3. With a matching active Goal, continue it and update only fields that changed. +4. With a conflicting active Goal, ask the user whether to archive it, complete it, block it, or keep it. Never silently replace an active Goal. +5. Use the revision returned by `get_goal` as `expectedRevision` on every update. Reload before merging a revision conflict. +6. Start and pause measured work with `start_goal_work` and `pause_goal_work`. +7. Record tokens only with `record_goal_token_usage` when an upstream API or provider returned exact usage plus a stable request ID. +8. Link the current Plan through `update_plan(goalId=...)` only when the Plan is the authoritative breakdown for Goal progress. + +Read [references/state.md](references/state.md), [references/metrics.md](references/metrics.md), and [references/conflicts.md](references/conflicts.md) before acting on Goal state. + +## Status + +- `active`: the outcome can proceed. +- `blocked`: a specific decision, dependency, or permission is missing. +- `completed`: success criteria have been verified. +- `archived`: no longer current; history remains available. + +`currentSummary` contains only completed work, current work, and real blockers. Never put chat transcripts, raw tool output, file snapshots, or secrets into Goal state. diff --git a/skills/.system/goal/references/conflicts.md b/skills/.system/goal/references/conflicts.md new file mode 100644 index 0000000..d7f593b --- /dev/null +++ b/skills/.system/goal/references/conflicts.md @@ -0,0 +1,18 @@ +# Goal Conflicts + +A Goal conflicts when the requested objective, scope, or acceptance criteria would direct the project toward a different outcome than the active Goal. + +## Required user choice + +Show the current Goal and requested Goal briefly, then ask whether to: + +1. archive the current Goal and create the requested Goal; +2. complete the current Goal after verification; +3. mark the current Goal blocked with a concrete reason; or +4. keep the current Goal and treat the request as ordinary work. + +Do not create competing active Goals and do not silently replace one. + +## Revision conflict + +A revision conflict means another session changed the Goal after it was read. Call `get_goal`, preserve valid changes, and update once with the refreshed revision. \ No newline at end of file diff --git a/skills/.system/goal/references/metrics.md b/skills/.system/goal/references/metrics.md new file mode 100644 index 0000000..378c989 --- /dev/null +++ b/skills/.system/goal/references/metrics.md @@ -0,0 +1,25 @@ +# Exact Goal Metrics + +Goal metrics are recorded only under explicit evidence rules. + +## Provider tokens + +Use `record_goal_token_usage` only with exact counts returned by a model provider or API and a stable provider request ID. Usage is append-only and deduplicated by `provider + providerRequestId`. + +Never estimate tokens from text length, message bytes, context limits, model names, elapsed time, or intuition. + +## Work duration + +Call `start_goal_work` when measured work begins. Call `pause_goal_work` before waiting for approval, changing tasks, or stopping. DevSpace persists exact server wall-clock milliseconds only while this timer is running. A Goal transition out of `active` pauses a running timer automatically. + +This is an explicit timer interval, not a claim about hidden model reasoning or user attention. + +## Percentage progress + +Set the current Plan `goalId` to this Goal ID only when that Plan is the authoritative work breakdown. Progress then uses completed Plan steps: + +- canonical fraction: `completedSteps/totalSteps`; +- exact rational percentage: `percentageNumerator/percentageDenominator`; +- `displayPercent`: rounded human display only. + +Without a linked current Plan, percentage progress is unavailable rather than guessed. \ No newline at end of file diff --git a/skills/.system/goal/references/state.md b/skills/.system/goal/references/state.md new file mode 100644 index 0000000..e0688f8 --- /dev/null +++ b/skills/.system/goal/references/state.md @@ -0,0 +1,17 @@ +# Goal State + +A Goal is durable project-scoped state. It is shared across sessions for the same canonical project root and isolated from other projects and Git worktrees. + +## Fields + +- `objective`: concrete user-visible outcome. +- `scope.in` / `scope.out`: boundaries. +- `successCriteria`: observable completion requirements. +- `verification`: tests, builds, review steps, or manual checks. +- `stopConditions`: conditions that justify pausing, escalating, or stopping. +- `currentSummary`: compact completed/current/blocked record. +- `status`: `active`, `blocked`, `completed`, `archived`. +- `revision`: optimistic-concurrency version. +- `metrics`: exact token, duration, and Plan-progress data only where evidence exists. + +`create_goal` refuses to create a competing active Goal. After a Goal becomes blocked, completed, or archived, a new active Goal can be created deliberately. \ No newline at end of file diff --git a/skills/.system/plan/SKILL.md b/skills/.system/plan/SKILL.md new file mode 100644 index 0000000..90006a5 --- /dev/null +++ b/skills/.system/plan/SKILL.md @@ -0,0 +1,55 @@ +--- +name: plan +description: Create, resume, and maintain a durable DevSpace implementation Plan for the current project. Use for /plan and for requests that require read-only analysis before code changes. +license: MIT +metadata: + version: 1.0.2 + author: DevSpace + category: system-workflow + updated: 2026-06-22 +--- + +# DevSpace Plan + +Use this Skill for `/plan`, explicit implementation planning, or a task that should be analyzed before files are modified. + +## Required lifecycle + +1. Call `get_plan` first. Reuse a matching current Plan instead of silently replacing it. +2. Set collaboration mode to `plan` for the planning pass. +3. Read project instructions, source, tests, configuration, public interfaces, and migration paths. Planning is read-only: do not edit project files or claim implementation is complete. +4. Ask with `request_user_input` only when an unresolved decision changes scope, compatibility, architecture, safety, or rollout. +5. Produce one finite Plan with scope, ordered actions, validation, and risks. +6. Persist it with `update_plan`: + - use `expectedRevision=0` only when no current Plan exists; + - otherwise use the revision returned by `get_plan`; + - on a revision conflict, reload and merge instead of retrying stale content. + +Read [references/state.md](references/state.md) for Plan state and [references/conflicts.md](references/conflicts.md) before resolving concurrent changes. + +## Output contract + +```markdown +# Plan + +## Goal +