From 62aa43db8b2bdf0008fea24dfaa00fb5d123371c Mon Sep 17 00:00:00 2001 From: Waishnav Date: Wed, 1 Jul 2026 03:12:32 +0530 Subject: [PATCH 1/8] feat(skills): bundle local agent delegation skill --- skills/local-agent-delegation/SKILL.md | 187 +++++++++++++++++++++++++ src/cli.ts | 3 + src/config.test.ts | 11 +- src/config.ts | 4 +- src/skills.test.ts | 15 ++ src/skills.ts | 21 ++- src/user-config.ts | 16 ++- 7 files changed, 252 insertions(+), 5 deletions(-) create mode 100644 skills/local-agent-delegation/SKILL.md diff --git a/skills/local-agent-delegation/SKILL.md b/skills/local-agent-delegation/SKILL.md new file mode 100644 index 00000000..c50173c7 --- /dev/null +++ b/skills/local-agent-delegation/SKILL.md @@ -0,0 +1,187 @@ +--- +name: local-agent-delegation +description: Delegate coding tasks to user-configured local coding agents such as Codex, Claude Code, OpenCode, Cursor Agent, Pi, or Copilot CLI. +--- + +# Local Agent Delegation + +Use this skill when the user explicitly asks to delegate work to another coding agent, use a named local agent, get a second opinion, compare implementations, run agents in parallel, or create a subagent-like workflow. + +Do not use local agents silently. Tell the user when another local agent is being used. + +## Core idea + +You are the supervisor. A local coding agent is the worker. + +Your responsibilities are: + +1. Understand the user's goal. +2. Decide whether delegation is useful. +3. Choose the right local agent or CLI. +4. Give the worker a focused prompt. +5. Inspect the result yourself. +6. Review diffs, tests, and risks before telling the user the work is done. + +## When to delegate + +Good delegation requests include: + +- "Ask another agent to look at this." +- "Have Claude/Codex/OpenCode/Pi implement this." +- "Run this in the background." +- "Compare two approaches." +- "Use a local subagent for this." +- "Get a second opinion on the architecture/test gaps/security risk." + +Do not delegate just because the task is coding-related. Use the normal DevSpace tools directly unless the user asks for delegation, another agent's opinion, parallel work, or a named local coding agent. + +## CLI execution guidance + +Prefer structured non-interactive CLI modes when available. + +Examples of useful patterns: + +```bash +codex exec --json -C "$WORKSPACE" "$PROMPT" +claude -p --output-format stream-json "$PROMPT" +opencode run --format json --dir "$WORKSPACE" "$PROMPT" +cursor-agent -p --output-format stream-json "$PROMPT" +pi -p --mode json "$PROMPT" +copilot -p "$PROMPT" --output-format json +``` + +Use exact command templates from user-configured DevSpace agent profiles when they are available. Do not invent provider-specific flags when a profile already defines them. + +Configured local agent profiles may live in: + +```text +~/.devspace/agents/*.md +``` + +Treat those profiles as user-approved local worker definitions. If no profile exists for a requested agent, use the installed CLI's help output only when needed, then summarize what you found before running it. + +## Background execution + +When DevSpace exposes long-running process tools, prefer background execution for long tasks. + +Start the local agent process, keep the returned process/session id, and poll output later. + +Use this pattern for long implementations, test repair loops, large reviews, multi-step investigations, and agents that stream JSON or progress logs. + +When polling output, summarize useful progress instead of forwarding noisy terminal logs. + +If the worker is clearly stuck, running the wrong task, or burning resources, interrupt the current process or turn. Do not delete provider session history unless the user explicitly asks. + +## Follow-up prompts + +When sending a follow-up to the same local agent, include: + +```text +previous_task: +current_status: +review_findings: +requested_changes: +success_criteria: +``` + +Prefer the agent profile's resume/session mechanism when available. + +If resume is not available, include the previous worker summary and relevant diff context in a fresh prompt. + +## Worker prompt templates + +Use this structure when delegating implementation: + +```text +You are acting as a local coding worker under ChatGPT supervision. + +Goal: + + +Context: + + +Plan to execute: + + +Rules: +- Follow the existing project style. +- Keep changes focused. +- Do not perform unrelated refactors. +- Do not hide failures. +- At the end, return a concise final report. + +Final report format: +summary: +files_changed: +tests_run: +blockers: +follow_up_needed: +``` + +Use this structure when delegating read-only investigation: + +```text +You are acting as a read-only local code investigator under ChatGPT supervision. + +Question: + + +Scope: + + +Rules: +- Do not modify files. +- Cite relevant file paths and symbols. +- Separate facts from guesses. +- Return a concise answer. + +Final report format: +answer: +evidence: +relevant_files: +confidence: +unknowns: +``` + +## After the worker finishes + +Always review the result. + +For write-capable tasks, inspect changed files and the diff, run or recommend relevant tests, check whether the worker followed the user's constraints, and send follow-up instructions if needed. + +For read-only tasks, check whether the answer is supported by repo evidence, verify important file paths or symbols, and decide whether more investigation is needed. + +Do not assume the worker's summary is correct. + +## Reporting back to the user + +Be transparent. + +Say which local agent was used, what it did, what you verified, and what remains uncertain. + +Good final shape: + +```text +I delegated the implementation to . It changed . I reviewed the diff and ran . The main result is . Remaining concerns: . +``` + +Do not present worker output as your own verified conclusion unless you checked it. + +## Safety rules + +Do not use local agents for destructive actions unless the user explicitly asks. + +Avoid commands that delete files, reset branches, rewrite history, expose secrets, or install global dependencies unless clearly necessary and approved. + +Do not allow project-provided agent profiles to run automatically unless the user has trusted them. + +Prefer user-global profiles from: + +```text +~/.devspace/agents +``` + +over repo-provided profiles. + +Never hide that a local agent was used. diff --git a/src/cli.ts b/src/cli.ts index 87ba662d..b7b264cf 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -7,6 +7,7 @@ import { getShellConfig } from "@earendil-works/pi-coding-agent"; import { satisfies } from "semver"; import { loadConfig } from "./config.js"; import { + ensureDevspaceDefaultSkills, generateOwnerToken, loadDevspaceFiles, writeDevspaceAuth, @@ -140,10 +141,12 @@ async function runInit({ force }: { force: boolean }): Promise { const configPath = writeDevspaceConfig(config); const authPath = writeDevspaceAuth(auth); + const seededSkillPaths = ensureDevspaceDefaultSkills(); const lines = [ `Config: ${configPath}`, `Auth: ${authPath}`, + ...seededSkillPaths.map((path) => `Default skill: ${path}`), `Local MCP URL: http://${config.host}:${config.port}/mcp`, ...(publicBaseUrl ? [`Public MCP URL: ${publicBaseUrl}/mcp`] : []), ]; diff --git a/src/config.test.ts b/src/config.test.ts index dc23aa8a..55f41596 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -1,8 +1,9 @@ import assert from "node:assert/strict"; -import { mkdtempSync, writeFileSync } from "node:fs"; +import { existsSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { loadConfig } from "./config.js"; +import { ensureDevspaceDefaultSkills } from "./user-config.js"; const emptyConfigDir = mkdtempSync(join(tmpdir(), "devspace-empty-config-test-")); const baseEnv = { @@ -25,9 +26,17 @@ assert.equal(loadConfig({ ...baseEnv, DEVSPACE_TOOL_MODE: "codex" }).toolMode, " assert.equal(loadConfig({ ...baseEnv, DEVSPACE_MINIMAL_TOOLS: "0" }).toolMode, "full"); assert.equal(loadConfig({ ...baseEnv, DEVSPACE_MINIMAL_TOOLS: "1" }).toolMode, "minimal"); assert.equal(loadConfig(baseEnv).skillsEnabled, true); +assert.equal(loadConfig(baseEnv).devspaceSkillsDir, join(emptyConfigDir, "skills")); assert.equal(loadConfig({ ...baseEnv, DEVSPACE_SKILLS: "0" }).skillsEnabled, false); assert.equal(loadConfig({ ...baseEnv, DEVSPACE_SKILLS: "1" }).skillsEnabled, true); +const seededConfigDir = mkdtempSync(join(tmpdir(), "devspace-seeded-skills-test-")); +const seededSkillPaths = ensureDevspaceDefaultSkills({ DEVSPACE_CONFIG_DIR: seededConfigDir }); +assert.deepEqual(seededSkillPaths, [join(seededConfigDir, "skills", "local-agent-delegation", "SKILL.md")]); +assert.equal(existsSync(seededSkillPaths[0]), true); +assert.match(readFileSync(seededSkillPaths[0], "utf8"), /name: local-agent-delegation/); +assert.deepEqual(ensureDevspaceDefaultSkills({ DEVSPACE_CONFIG_DIR: seededConfigDir }), []); + assert.throws( () => loadConfig({ ...baseEnv, DEVSPACE_WIDGETS: "invalid" }), /Invalid DEVSPACE_WIDGETS: invalid/, diff --git a/src/config.ts b/src/config.ts index d065b17c..4b970123 100644 --- a/src/config.ts +++ b/src/config.ts @@ -3,7 +3,7 @@ import { join, resolve } from "node:path"; import { expandHomePath } from "./roots.js"; import type { LoggingConfig, LogFormat, LogLevel } from "./logger.js"; import type { OAuthConfig } from "./oauth-provider.js"; -import { loadDevspaceFiles } from "./user-config.js"; +import { devspaceSkillsDir, loadDevspaceFiles } from "./user-config.js"; export type ToolNamingMode = "legacy" | "short"; export type ToolMode = "minimal" | "full" | "codex"; @@ -25,6 +25,7 @@ export interface ServerConfig { worktreeRoot: string; skillsEnabled: boolean; skillPaths: string[]; + devspaceSkillsDir: string; agentDir: string; logging: LoggingConfig; } @@ -235,6 +236,7 @@ export function loadConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig { worktreeRoot: resolve(expandHomePath(env.DEVSPACE_WORKTREE_ROOT ?? files.config.worktreeRoot ?? defaultWorktreeRoot())), skillsEnabled: env.DEVSPACE_SKILLS === undefined ? true : parseBoolean(env.DEVSPACE_SKILLS), skillPaths: parsePathList(env.DEVSPACE_SKILL_PATHS), + devspaceSkillsDir: devspaceSkillsDir(env), agentDir: resolve(expandHomePath(env.DEVSPACE_AGENT_DIR ?? files.config.agentDir ?? defaultAgentDir())), logging: parseLoggingConfig(env), }; diff --git a/src/skills.test.ts b/src/skills.test.ts index 16a49c8a..c4bfe231 100644 --- a/src/skills.test.ts +++ b/src/skills.test.ts @@ -20,6 +20,7 @@ try { const projectRoot = join(root, "project"); const agentDir = join(root, "agent"); const explicitSkills = join(root, "explicit-skills"); + const devspaceSkills = join(root, ".devspace", "skills"); const globalAgentsSkills = join(root, ".agents", "skills"); const projectAgentsSkills = join(projectRoot, ".agents", "skills"); const globalClaudeSkills = join(root, ".claude", "skills"); @@ -32,6 +33,7 @@ try { await mkdir(join(agentDir, "skills", "global-skill"), { recursive: true }); await mkdir(join(explicitSkills, "duplicate"), { recursive: true }); await mkdir(join(explicitSkills, "disabled"), { recursive: true }); + await mkdir(join(devspaceSkills, "devspace-local-skill"), { recursive: true }); await writeFile( join(globalAgentsSkills, "agent-global-skill", "SKILL.md"), @@ -88,6 +90,17 @@ try { "# Project Skill", ].join("\n"), ); + await writeFile( + join(devspaceSkills, "devspace-local-skill", "SKILL.md"), + [ + "---", + "name: devspace-local-skill", + "description: DevSpace local skill description.", + "---", + "", + "# DevSpace Local Skill", + ].join("\n"), + ); await writeFile( join(agentDir, "skills", "global-skill", "SKILL.md"), [ @@ -146,6 +159,8 @@ try { assert.equal(loaded.skills.some((skill) => skill.name === "claude-global-skill"), true); assert.equal(loaded.skills.some((skill) => skill.name === "claude-project-skill"), true); assert.equal(loaded.skills.some((skill) => skill.name === "project-skill"), false); + assert.equal(loaded.skills.some((skill) => skill.name === "devspace-local-skill"), true); + assert.equal(loaded.skills.some((skill) => skill.name === "local-agent-delegation"), true); assert.equal(loaded.skills.filter((skill) => skill.name === "duplicate-skill").length, 1); assert.equal(loaded.skills.some((skill) => skill.name === "hidden-skill"), true); assert.equal(loaded.diagnostics.some((diagnostic) => diagnostic.type === "collision"), true); diff --git a/src/skills.ts b/src/skills.ts index e3da62ff..9ad49e5e 100644 --- a/src/skills.ts +++ b/src/skills.ts @@ -1,6 +1,7 @@ import { existsSync } from "node:fs"; import { homedir } from "node:os"; import { join, resolve, sep } from "node:path"; +import { fileURLToPath } from "node:url"; import { loadSkills, type Skill, @@ -20,12 +21,28 @@ export interface SkillReadResolution { isSkillFile: boolean; } +const LOCAL_AGENT_DELEGATION_SKILL = join("local-agent-delegation", "SKILL.md"); + +function bundledSkillsDir(): string { + return fileURLToPath(new URL("../skills", import.meta.url)); +} + +function hasLocalAgentDelegationSkill(skillDir: string): boolean { + return existsSync(join(skillDir, LOCAL_AGENT_DELEGATION_SKILL)); +} + export function effectiveSkillPaths(config: ServerConfig, cwd: string): string[] { - const defaultPaths = [ + const bundledSkills = bundledSkillsDir(); + const defaultPathCandidates = [ join(homedir(), ".agents", "skills"), resolve(cwd, ".agents", "skills"), + config.devspaceSkillsDir, join(config.agentDir, "skills"), - ].filter((path) => existsSync(path)); + hasLocalAgentDelegationSkill(config.devspaceSkillsDir) ? undefined : bundledSkills, + ]; + const defaultPaths = defaultPathCandidates.filter( + (path): path is string => path !== undefined && existsSync(path), + ); const seen = new Set(); return [...defaultPaths, ...config.skillPaths] diff --git a/src/user-config.ts b/src/user-config.ts index 0b79c519..dcf718c2 100644 --- a/src/user-config.ts +++ b/src/user-config.ts @@ -6,7 +6,7 @@ import { writeFileSync, } from "node:fs"; import { homedir } from "node:os"; -import { join, resolve } from "node:path"; +import { dirname, join, resolve } from "node:path"; import { expandHomePath } from "./roots.js"; export interface DevspaceUserConfig { @@ -46,6 +46,10 @@ export function devspaceAuthPath(env: NodeJS.ProcessEnv = process.env): string { return join(devspaceConfigDir(env), "auth.json"); } +export function devspaceSkillsDir(env: NodeJS.ProcessEnv = process.env): string { + return join(devspaceConfigDir(env), "skills"); +} + export function loadDevspaceFiles(env: NodeJS.ProcessEnv = process.env): DevspaceFiles { const dir = devspaceConfigDir(env); const configPath = join(dir, "config.json"); @@ -88,6 +92,16 @@ export function generateOwnerToken(): string { return randomBytes(32).toString("base64url"); } +export function ensureDevspaceDefaultSkills(env: NodeJS.ProcessEnv = process.env): string[] { + const targetPath = join(devspaceSkillsDir(env), "local-agent-delegation", "SKILL.md"); + if (existsSync(targetPath)) return []; + + const sourcePath = new URL("../skills/local-agent-delegation/SKILL.md", import.meta.url); + mkdirSync(dirname(targetPath), { recursive: true }); + writeFileSync(targetPath, readFileSync(sourcePath, "utf8"), { mode: 0o644 }); + return [targetPath]; +} + function readJsonFile(filePath: string): T { try { return JSON.parse(readFileSync(filePath, "utf8")) as T; From 67fa1127fcd53f93d1d5b9f74b8424899f35419d Mon Sep 17 00:00:00 2001 From: Waishnav Date: Wed, 1 Jul 2026 03:12:36 +0530 Subject: [PATCH 2/8] docs(agents): add local agent profile examples --- docs/agent-profile-schema.md | 349 +++++++++++++++++++++++++ examples/agents/claude-implementer.md | 106 ++++++++ examples/agents/codex-explorer.md | 91 +++++++ examples/agents/codex-worker.md | 102 ++++++++ examples/agents/copilot-reviewer.md | 92 +++++++ examples/agents/cursor-agent-worker.md | 90 +++++++ examples/agents/opencode-explorer.md | 94 +++++++ examples/agents/pi-reviewer.md | 90 +++++++ 8 files changed, 1014 insertions(+) create mode 100644 docs/agent-profile-schema.md create mode 100644 examples/agents/claude-implementer.md create mode 100644 examples/agents/codex-explorer.md create mode 100644 examples/agents/codex-worker.md create mode 100644 examples/agents/copilot-reviewer.md create mode 100644 examples/agents/cursor-agent-worker.md create mode 100644 examples/agents/opencode-explorer.md create mode 100644 examples/agents/pi-reviewer.md diff --git a/docs/agent-profile-schema.md b/docs/agent-profile-schema.md new file mode 100644 index 00000000..dcaa3c75 --- /dev/null +++ b/docs/agent-profile-schema.md @@ -0,0 +1,349 @@ +# Local agent profile schema + +DevSpace local agent profiles are user-owned markdown files with YAML front matter. +They describe how a local coding-agent CLI can be used as a worker under ChatGPT +supervision. + +Profiles are intended to live in: + +```text +~/.devspace/agents/*.md +``` + +The packaged files in `examples/agents/` are starter templates only. DevSpace does +not automatically activate them, copy them into `~/.devspace/agents`, or run their +commands. Users should copy, review, and edit a template before treating it as an +active local worker definition. + +## Minimal shape + +```md +--- +schema: devspace-agent/v1 +name: codex-explorer +description: Read-only Codex agent for bounded codebase questions. +provider: codex +backend: cli + +capabilities: + read: true + write: false + shell: false + background: true + resume: true + +workspace: + default: current + isolation: none + writeMode: read_only + +actions: + start: + command: codex + args: + - exec + - --json + - -C + - "{workspace}" + - "{prompt}" + background: true + output: jsonl + + followup: + strategy: resume_command + command: codex + args: + - exec + - resume + - "{externalSessionId}" + - --json + - "{prompt}" + + read: + strategy: devspace_process_poll + + cancel: + strategy: devspace_process_signal + signal: SIGINT + + diff: + strategy: none + +safety: + requireExplicitUserIntent: true + allowWrites: false + requireReviewBeforeFinal: true +--- + +Use this agent for bounded read-only codebase investigation. +``` + +## Front matter fields + +### `schema` + +Required schema identifier. + +Current value: + +```yaml +schema: devspace-agent/v1 +``` + +### `name` + +Stable profile identifier shown to the model and user. + +Use lowercase kebab-case names, for example: + +```yaml +name: codex-explorer +``` + +### `description` + +Short human-readable purpose. This should help the supervising model decide when +the agent is appropriate. + +### `provider` + +The local agent family or CLI provider. + +Examples: + +```yaml +provider: codex +provider: claude +provider: opencode +provider: cursor +provider: pi +provider: copilot +``` + +### `backend` + +Execution backend. The near-term templates use CLI-backed agents: + +```yaml +backend: cli +``` + +Future profiles may support protocol-backed backends such as ACP without changing +the high-level profile name. + +## Capabilities + +Capabilities describe what the worker is allowed or expected to do. + +```yaml +capabilities: + read: true + write: false + shell: false + background: true + resume: true +``` + +- `read`: the agent can inspect project files. +- `write`: the agent may modify files. +- `shell`: the agent may run shell commands. +- `background`: the agent can be started as a long-running process. +- `resume`: the agent supports follow-up prompts against an existing session. + +These fields are descriptive in the current template-only stage. A future parser +should validate them before rendering an available-agent catalog or exposing +runtime tools. + +## Workspace policy + +```yaml +workspace: + default: current + isolation: user_decides + writeMode: allowed +``` + +- `default`: default workspace source. Current templates use `current`. +- `isolation`: whether to use the same checkout, a branch, a worktree, or let the + user decide. +- `writeMode`: whether writes are allowed. + +Recommended values: + +```yaml +isolation: none +isolation: user_decides + +writeMode: read_only +writeMode: allowed +``` + +Use read-only profiles for review, lookup, and second opinions. Use write-capable +profiles only when the user explicitly asks a local agent to implement or edit. + +## Actions + +Actions define lifecycle commands and strategies. + +### `actions.start` + +Starts the local agent. + +```yaml +actions: + start: + command: codex + args: + - exec + - --json + - -C + - "{workspace}" + - "{prompt}" + background: true + output: jsonl +``` + +Use `command` plus `args` arrays. Do not use free-form shell strings. + +Good: + +```yaml +command: codex +args: + - exec + - --json + - -C + - "{workspace}" + - "{prompt}" +``` + +Avoid: + +```yaml +run: "codex exec --json -C {workspace} {prompt}" +``` + +Argv arrays are easier to validate, escape, log, review, and migrate to future +backends. + +### `actions.followup` + +Defines how to continue a previous worker session. + +Supported strategy names used by the starter templates: + +```yaml +strategy: resume_command +strategy: fresh_prompt_with_context +``` + +Use `resume_command` when the provider has an explicit resume/session flag. Use +`fresh_prompt_with_context` when follow-up work must include previous summaries, +review findings, and diff context in a new prompt. + +### `actions.read` + +Defines how DevSpace should read worker output. + +The starter templates use: + +```yaml +read: + strategy: devspace_process_poll +``` + +### `actions.cancel` + +Defines how DevSpace should interrupt a running worker. + +The starter templates use: + +```yaml +cancel: + strategy: devspace_process_signal + signal: SIGINT +``` + +Prefer interruption over deleting provider session history. + +### `actions.diff` + +Defines how DevSpace can inspect worker file changes. + +Read-only profiles should use: + +```yaml +diff: + strategy: none +``` + +Write-capable profiles should use: + +```yaml +diff: + strategy: git_diff +``` + +## Placeholders + +The examples use placeholders that a future runtime can substitute safely: + +```text +{workspace} +{prompt} +{externalSessionId} +``` + +- `{workspace}`: absolute workspace path selected by DevSpace or the user. +- `{prompt}`: focused worker prompt created by the supervising model. +- `{externalSessionId}`: provider session id returned by a previous agent run. + +## Safety policy + +```yaml +safety: + requireExplicitUserIntent: true + allowWrites: false + requireReviewBeforeFinal: true +``` + +Recommended write-capable profile safety: + +```yaml +safety: + requireExplicitUserIntent: true + allowWrites: true + requireDiffReview: true + requireTestsOrExplanation: true +``` + +Profiles should make user intent and review requirements explicit. DevSpace should +not silently delegate work to local agents, and the supervising model should not +present worker output as verified until it has reviewed the result. + +## Markdown body + +The markdown body should explain when to use the agent and provide a worker prompt +template. + +Recommended sections: + +- `Use this agent when ...` +- `Good tasks:` +- `Worker prompt style:` +- A final report format that the supervising model can review. + +The body is model-facing guidance. Keep it practical and concise. + +## Current non-goals + +The current examples do not add: + +- `.devspace/agents` parsing. +- automatic activation of packaged examples. +- `devspace agents init`. +- generated available-agent catalogs. +- first-class agent runtime tools. +- ACP-backed execution. + +Those can be added in later PRs without changing the template intent. diff --git a/examples/agents/claude-implementer.md b/examples/agents/claude-implementer.md new file mode 100644 index 00000000..58f91ed0 --- /dev/null +++ b/examples/agents/claude-implementer.md @@ -0,0 +1,106 @@ +--- +schema: devspace-agent/v1 +name: claude-implementer +description: Claude Code implementation worker for larger edits, refactors, and follow-up loops. +provider: claude +backend: cli + +capabilities: + read: true + write: true + shell: true + background: true + resume: true + +workspace: + default: current + isolation: user_decides + writeMode: allowed + +actions: + start: + command: claude + args: + - -p + - --output-format + - stream-json + - "{prompt}" + background: true + output: stream-json + + followup: + strategy: resume_command + command: claude + args: + - --resume + - "{externalSessionId}" + - -p + - --output-format + - stream-json + - "{prompt}" + + list: + command: claude + args: + - agents + - --json + + read: + strategy: devspace_process_poll + + cancel: + strategy: devspace_process_signal + signal: SIGINT + + diff: + strategy: git_diff + +safety: + requireExplicitUserIntent: true + allowWrites: true + requireDiffReview: true + requireTestsOrExplanation: true +--- + +Use this agent when the task benefits from a stronger implementation worker. + +Good tasks: + +- Multi-file implementation. +- Refactor with clear boundaries. +- Test repair loop. +- Apply detailed review comments. +- Continue an already-started implementation. + +Worker prompt style: + +```text +You are a local Claude Code implementation worker under ChatGPT supervision. + +Goal: + + +Context: + + +Plan: + + +Constraints: + + +Rules: +- Keep changes focused. +- Do not rewrite unrelated code. +- Preserve public behavior unless asked. +- Run or explain relevant tests. +- Return a concise final report. + +Final report format: +summary: +files_changed: +tests_run: +risks: +blockers: +follow_up_needed: +``` diff --git a/examples/agents/codex-explorer.md b/examples/agents/codex-explorer.md new file mode 100644 index 00000000..5ca22120 --- /dev/null +++ b/examples/agents/codex-explorer.md @@ -0,0 +1,91 @@ +--- +schema: devspace-agent/v1 +name: codex-explorer +description: Read-only Codex agent for bounded codebase questions and architecture exploration. +provider: codex +backend: cli + +capabilities: + read: true + write: false + shell: false + background: true + resume: true + +workspace: + default: current + isolation: none + writeMode: read_only + +actions: + start: + command: codex + args: + - exec + - --json + - -C + - "{workspace}" + - "{prompt}" + background: true + output: jsonl + + followup: + strategy: resume_command + command: codex + args: + - exec + - resume + - "{externalSessionId}" + - --json + - "{prompt}" + + read: + strategy: devspace_process_poll + + cancel: + strategy: devspace_process_signal + signal: SIGINT + + diff: + strategy: none + +safety: + requireExplicitUserIntent: true + allowWrites: false + requireReviewBeforeFinal: true +--- + +Use this agent when the user wants a bounded read-only investigation, second opinion, +or explanation of a code path. + +Good tasks: + +- Find where a feature is implemented. +- Explain an architecture boundary. +- Review a module without changing files. +- Identify likely files for a future change. + +Worker prompt style: + +```text +You are a read-only codebase explorer. + +Question: + + +Scope: + + +Rules: +- Do not modify files. +- Cite file paths and symbols. +- Separate facts from guesses. +- Keep the answer concise. + +Final report format: +answer: +evidence: +relevant_files: +confidence: +unknowns: +``` diff --git a/examples/agents/codex-worker.md b/examples/agents/codex-worker.md new file mode 100644 index 00000000..00d89b02 --- /dev/null +++ b/examples/agents/codex-worker.md @@ -0,0 +1,102 @@ +--- +schema: devspace-agent/v1 +name: codex-worker +description: Codex implementation worker for focused, user-approved coding tasks. +provider: codex +backend: cli + +capabilities: + read: true + write: true + shell: true + background: true + resume: true + +workspace: + default: current + isolation: user_decides + writeMode: allowed + +actions: + start: + command: codex + args: + - exec + - --json + - -C + - "{workspace}" + - "{prompt}" + background: true + output: jsonl + + followup: + strategy: resume_command + command: codex + args: + - exec + - resume + - "{externalSessionId}" + - --json + - "{prompt}" + + read: + strategy: devspace_process_poll + + cancel: + strategy: devspace_process_signal + signal: SIGINT + + diff: + strategy: git_diff + +safety: + requireExplicitUserIntent: true + allowWrites: true + requireDiffReview: true + requireTestsOrExplanation: true +--- + +Use this agent when ChatGPT has already planned a focused implementation and the +user wants Codex to execute it locally. + +Good tasks: + +- Implement a small feature from a clear plan. +- Fix a bug with known reproduction steps. +- Add tests for an existing code path. +- Apply review comments. + +Worker prompt style: + +```text +You are a local implementation worker under ChatGPT supervision. + +Goal: + + +Plan: + + +Constraints: + + +Files to focus: + + +Tests to run: + + +Rules: +- Keep changes focused. +- Follow existing project style. +- Do not perform unrelated refactors. +- Do not hide failures. +- Return a final report. + +Final report format: +summary: +files_changed: +tests_run: +blockers: +follow_up_needed: +``` diff --git a/examples/agents/copilot-reviewer.md b/examples/agents/copilot-reviewer.md new file mode 100644 index 00000000..f3b66074 --- /dev/null +++ b/examples/agents/copilot-reviewer.md @@ -0,0 +1,92 @@ +--- +schema: devspace-agent/v1 +name: copilot-reviewer +description: GitHub Copilot CLI reviewer for read-only code questions and review passes. +provider: copilot +backend: cli + +capabilities: + read: true + write: false + shell: false + background: true + resume: true + +workspace: + default: current + isolation: none + writeMode: read_only + +actions: + start: + command: copilot + args: + - -p + - "{prompt}" + - --output-format + - json + background: true + output: json + + followup: + strategy: resume_command + command: copilot + args: + - -p + - "{prompt}" + - --resume + - "{externalSessionId}" + - --output-format + - json + + read: + strategy: devspace_process_poll + + cancel: + strategy: devspace_process_signal + signal: SIGINT + + diff: + strategy: none + +safety: + requireExplicitUserIntent: true + allowWrites: false + requireReviewBeforeFinal: true +--- + +Use this agent when the user wants a GitHub Copilot-powered second opinion, +review, or codebase answer. + +Good tasks: + +- Review changed files. +- Find likely bug sources. +- Explain repository structure. +- Suggest tests. + +Worker prompt style: + +```text +You are a read-only Copilot reviewer under ChatGPT supervision. + +Question: + + +Scope: + + +Rules: +- Do not modify files. +- Cite exact files and symbols. +- Return concise findings. +- Separate facts from guesses. + +Final report format: +answer: +findings: +evidence: +relevant_files: +confidence: +unknowns: +``` diff --git a/examples/agents/cursor-agent-worker.md b/examples/agents/cursor-agent-worker.md new file mode 100644 index 00000000..4dffef2a --- /dev/null +++ b/examples/agents/cursor-agent-worker.md @@ -0,0 +1,90 @@ +--- +schema: devspace-agent/v1 +name: cursor-agent-worker +description: Cursor Agent worker for fast implementation or review using local Cursor CLI. +provider: cursor +backend: cli + +capabilities: + read: true + write: true + shell: true + background: true + resume: false + +workspace: + default: current + isolation: user_decides + writeMode: allowed + +actions: + start: + command: cursor-agent + args: + - -p + - --output-format + - stream-json + - "{prompt}" + background: true + output: stream-json + + followup: + strategy: fresh_prompt_with_context + + read: + strategy: devspace_process_poll + + cancel: + strategy: devspace_process_signal + signal: SIGINT + + diff: + strategy: git_diff + +safety: + requireExplicitUserIntent: true + allowWrites: true + requireDiffReview: true + requireTestsOrExplanation: true +--- + +Use this agent when the user wants Cursor's local agent/model to quickly execute +or inspect a task. + +Good tasks: + +- Fast implementation pass. +- UI/UX-oriented code review. +- Alternative implementation idea. +- Lightweight refactor. + +Worker prompt style: + +```text +You are a local Cursor Agent worker under ChatGPT supervision. + +Goal: + + +Context: + + +Plan: + + +Rules: +- Keep changes focused. +- Do not make unrelated edits. +- Preserve existing style. +- Report tests and blockers. + +Final report format: +summary: +files_changed: +tests_run: +blockers: +follow_up_needed: +``` + +Because this profile uses `fresh_prompt_with_context`, include the previous worker +summary and review findings when sending follow-up work. diff --git a/examples/agents/opencode-explorer.md b/examples/agents/opencode-explorer.md new file mode 100644 index 00000000..93c7a846 --- /dev/null +++ b/examples/agents/opencode-explorer.md @@ -0,0 +1,94 @@ +--- +schema: devspace-agent/v1 +name: opencode-explorer +description: OpenCode read-only explorer for fast codebase lookup and bounded questions. +provider: opencode +backend: cli + +capabilities: + read: true + write: false + shell: false + background: true + resume: true + +workspace: + default: current + isolation: none + writeMode: read_only + +actions: + start: + command: opencode + args: + - run + - --format + - json + - --dir + - "{workspace}" + - "{prompt}" + background: true + output: json + + followup: + strategy: resume_command + command: opencode + args: + - run + - --format + - json + - --session + - "{externalSessionId}" + - --dir + - "{workspace}" + - "{prompt}" + + read: + strategy: devspace_process_poll + + cancel: + strategy: devspace_process_signal + signal: SIGINT + + diff: + strategy: none + +safety: + requireExplicitUserIntent: true + allowWrites: false + requireReviewBeforeFinal: true +--- + +Use this agent for fast, read-only codebase exploration. + +Good tasks: + +- Find relevant files. +- Explain a subsystem. +- Identify test coverage gaps. +- Compare possible implementation locations. + +Worker prompt style: + +```text +You are a read-only OpenCode explorer. + +Question: + + +Scope: + + +Rules: +- Do not modify files. +- Cite exact file paths. +- Prefer concise findings. +- State uncertainty. + +Final report format: +answer: +evidence: +relevant_files: +confidence: +unknowns: +``` diff --git a/examples/agents/pi-reviewer.md b/examples/agents/pi-reviewer.md new file mode 100644 index 00000000..27510c27 --- /dev/null +++ b/examples/agents/pi-reviewer.md @@ -0,0 +1,90 @@ +--- +schema: devspace-agent/v1 +name: pi-reviewer +description: Pi read-only reviewer for quick code review and targeted questions. +provider: pi +backend: cli + +capabilities: + read: true + write: false + shell: false + background: true + resume: true + +workspace: + default: current + isolation: none + writeMode: read_only + +actions: + start: + command: pi + args: + - -p + - --mode + - json + - "{prompt}" + background: true + output: json + + followup: + strategy: resume_command + command: pi + args: + - -p + - --mode + - json + - --session-id + - "{externalSessionId}" + - "{prompt}" + + read: + strategy: devspace_process_poll + + cancel: + strategy: devspace_process_signal + signal: SIGINT + + diff: + strategy: none + +safety: + requireExplicitUserIntent: true + allowWrites: false + requireReviewBeforeFinal: true +--- + +Use this agent for lightweight review and targeted read-only investigation. + +Good tasks: + +- Review a diff. +- Find possible bugs. +- Explain a small subsystem. +- Check whether tests cover an edge case. + +Worker prompt style: + +```text +You are a read-only local code reviewer. + +Question: + + +Scope: + + +Rules: +- Do not modify files. +- Cite evidence. +- Focus on actionable findings. +- Avoid broad rewrites. + +Final report format: +findings: +evidence: +risk_level: +recommended_next_steps: +unknowns: +``` From 5fe69575df60a7099604c4226057c1384d3fe869 Mon Sep 17 00:00:00 2001 From: Waishnav Date: Wed, 1 Jul 2026 03:12:42 +0530 Subject: [PATCH 3/8] docs(agents): surface packaged agent templates --- docs/chatgpt-coding-workflow.md | 6 ++++++ docs/configuration.md | 6 ++++++ docs/gotchas.md | 6 ++++++ package.json | 2 ++ 4 files changed, 20 insertions(+) diff --git a/docs/chatgpt-coding-workflow.md b/docs/chatgpt-coding-workflow.md index 13269f52..593987a8 100644 --- a/docs/chatgpt-coding-workflow.md +++ b/docs/chatgpt-coding-workflow.md @@ -83,12 +83,18 @@ DevSpace discovers standard Agent Skills from: - `~/.agents/skills` - project `.agents/skills` +- `~/.devspace/skills` It also keeps compatibility with: +- the bundled `local-agent-delegation` skill, unless `~/.devspace/skills/local-agent-delegation/SKILL.md` exists - `DEVSPACE_AGENT_DIR/skills`, defaulting to `~/.codex/skills` - additional paths from `DEVSPACE_SKILL_PATHS` +Example local coding-agent profiles are packaged under `examples/agents/` for +users who want starter templates for `~/.devspace/agents/*.md`. These examples +are not activated automatically. + Legacy project paths such as `.pi/skills` can be added through `DEVSPACE_SKILL_PATHS` when needed. When `open_workspace` returns matching skills, the model should read the diff --git a/docs/configuration.md b/docs/configuration.md index 32293631..2a97376e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -106,12 +106,18 @@ DevSpace discovers standard Agent Skills from: - `~/.agents/skills` - project `.agents/skills` +- `~/.devspace/skills` It also keeps compatibility with: +- the bundled `local-agent-delegation` skill, unless `~/.devspace/skills/local-agent-delegation/SKILL.md` exists - `DEVSPACE_AGENT_DIR/skills`, defaulting to `~/.codex/skills` - additional paths from `DEVSPACE_SKILL_PATHS` +Starter local coding-agent profile templates are available under +`examples/agents/`. Users can copy them into `~/.devspace/agents/` and edit them +for their local CLIs. DevSpace does not activate packaged examples automatically. + Legacy project paths such as `.pi/skills` can be added through `DEVSPACE_SKILL_PATHS` when needed. Example: diff --git a/docs/gotchas.md b/docs/gotchas.md index c6ef1d2e..bb584b28 100644 --- a/docs/gotchas.md +++ b/docs/gotchas.md @@ -197,12 +197,18 @@ DevSpace looks in standard Agent Skills locations: - `~/.agents/skills` - project `.agents/skills` +- `~/.devspace/skills` It also checks compatibility and custom paths: +- the bundled `local-agent-delegation` skill, unless `~/.devspace/skills/local-agent-delegation/SKILL.md` exists - `DEVSPACE_AGENT_DIR/skills`, defaulting to `~/.codex/skills` - additional paths from `DEVSPACE_SKILL_PATHS` +Packaged local-agent examples under `examples/agents/` are templates only. Copy +and review a profile before using it from `~/.devspace/agents/`; repo-provided or +packaged examples should not become runnable worker definitions silently. + Legacy project paths such as `.pi/skills` can be added through `DEVSPACE_SKILL_PATHS` when needed. If a skill appears in `open_workspace`, the model must read that skill's diff --git a/package.json b/package.json index aabd4926..4a8d6057 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,9 @@ "files": [ "dist", "docs", + "examples", "scripts", + "skills", "README.md" ], "publishConfig": { From 172d8bb6594deefac8f65df917fd7161e094644f Mon Sep 17 00:00:00 2001 From: Waishnav Date: Wed, 1 Jul 2026 12:36:03 +0530 Subject: [PATCH 4/8] feat(skills): gate local agent delegation experiment --- docs/agent-profile-schema.md | 13 +++---------- docs/chatgpt-coding-workflow.md | 10 ++++++---- docs/configuration.md | 7 ++++--- docs/gotchas.md | 8 ++++---- skills/local-agent-delegation/SKILL.md | 20 ++++---------------- src/cli.ts | 3 ++- src/config.test.ts | 7 +++++++ src/config.ts | 5 +++++ src/skills.test.ts | 16 +++++++++++++++- src/skills.ts | 13 +++++++++++-- src/user-config.ts | 1 + 11 files changed, 62 insertions(+), 41 deletions(-) diff --git a/docs/agent-profile-schema.md b/docs/agent-profile-schema.md index dcaa3c75..20709160 100644 --- a/docs/agent-profile-schema.md +++ b/docs/agent-profile-schema.md @@ -4,16 +4,9 @@ DevSpace local agent profiles are user-owned markdown files with YAML front matt They describe how a local coding-agent CLI can be used as a worker under ChatGPT supervision. -Profiles are intended to live in: - -```text -~/.devspace/agents/*.md -``` - The packaged files in `examples/agents/` are starter templates only. DevSpace does -not automatically activate them, copy them into `~/.devspace/agents`, or run their -commands. Users should copy, review, and edit a template before treating it as an -active local worker definition. +not currently parse, load, activate, or run local agent profile definitions. A +future profile loader can define the user-owned directory for active profiles. ## Minimal shape @@ -339,7 +332,7 @@ The body is model-facing guidance. Keep it practical and concise. The current examples do not add: -- `.devspace/agents` parsing. +- local agent profile parsing. - automatic activation of packaged examples. - `devspace agents init`. - generated available-agent catalogs. diff --git a/docs/chatgpt-coding-workflow.md b/docs/chatgpt-coding-workflow.md index 593987a8..2a734337 100644 --- a/docs/chatgpt-coding-workflow.md +++ b/docs/chatgpt-coding-workflow.md @@ -87,13 +87,13 @@ DevSpace discovers standard Agent Skills from: It also keeps compatibility with: -- the bundled `local-agent-delegation` skill, unless `~/.devspace/skills/local-agent-delegation/SKILL.md` exists +- the bundled `local-agent-delegation` skill when `DEVSPACE_LOCAL_AGENTS=1`, unless `~/.devspace/skills/local-agent-delegation/SKILL.md` exists - `DEVSPACE_AGENT_DIR/skills`, defaulting to `~/.codex/skills` - additional paths from `DEVSPACE_SKILL_PATHS` Example local coding-agent profiles are packaged under `examples/agents/` for -users who want starter templates for `~/.devspace/agents/*.md`. These examples -are not activated automatically. +users who want starter templates. These examples are inert: DevSpace does not +currently parse, load, activate, or run local agent profile definitions. Legacy project paths such as `.pi/skills` can be added through `DEVSPACE_SKILL_PATHS` when needed. @@ -105,7 +105,9 @@ Skill paths may be outside the workspace. DevSpace only permits reading: - advertised `SKILL.md` files - files under a skill directory after that skill's `SKILL.md` has been read -Set `DEVSPACE_SKILLS=0` to hide skills from workspace output. +Set `DEVSPACE_SKILLS=0` to hide skills from workspace output. Set +`DEVSPACE_LOCAL_AGENTS=1` to expose the experimental `local-agent-delegation` +skill. ## Tool Names diff --git a/docs/configuration.md b/docs/configuration.md index 2a97376e..29cf1169 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -99,6 +99,7 @@ sessions. | Variable | Purpose | | --- | --- | | `DEVSPACE_SKILLS` | Set to `0` to hide skills. Enabled by default. | +| `DEVSPACE_LOCAL_AGENTS` | Set to `1` to expose the local-agent delegation skill. Experimental and disabled by default. | | `DEVSPACE_AGENT_DIR` | Defaults to `~/.codex`; its `skills` child is loaded for compatibility. | | `DEVSPACE_SKILL_PATHS` | Optional comma-separated additional skill directories. | @@ -110,13 +111,13 @@ DevSpace discovers standard Agent Skills from: It also keeps compatibility with: -- the bundled `local-agent-delegation` skill, unless `~/.devspace/skills/local-agent-delegation/SKILL.md` exists +- the bundled `local-agent-delegation` skill when `DEVSPACE_LOCAL_AGENTS=1`, unless `~/.devspace/skills/local-agent-delegation/SKILL.md` exists - `DEVSPACE_AGENT_DIR/skills`, defaulting to `~/.codex/skills` - additional paths from `DEVSPACE_SKILL_PATHS` Starter local coding-agent profile templates are available under -`examples/agents/`. Users can copy them into `~/.devspace/agents/` and edit them -for their local CLIs. DevSpace does not activate packaged examples automatically. +`examples/agents/`. These files are inert examples: DevSpace does not currently +parse, load, activate, or run local agent profile definitions. Legacy project paths such as `.pi/skills` can be added through `DEVSPACE_SKILL_PATHS` when needed. diff --git a/docs/gotchas.md b/docs/gotchas.md index bb584b28..e6874d1b 100644 --- a/docs/gotchas.md +++ b/docs/gotchas.md @@ -201,13 +201,13 @@ DevSpace looks in standard Agent Skills locations: It also checks compatibility and custom paths: -- the bundled `local-agent-delegation` skill, unless `~/.devspace/skills/local-agent-delegation/SKILL.md` exists +- the bundled `local-agent-delegation` skill when `DEVSPACE_LOCAL_AGENTS=1`, unless `~/.devspace/skills/local-agent-delegation/SKILL.md` exists - `DEVSPACE_AGENT_DIR/skills`, defaulting to `~/.codex/skills` - additional paths from `DEVSPACE_SKILL_PATHS` -Packaged local-agent examples under `examples/agents/` are templates only. Copy -and review a profile before using it from `~/.devspace/agents/`; repo-provided or -packaged examples should not become runnable worker definitions silently. +Packaged local-agent examples under `examples/agents/` are inert templates only. +DevSpace does not currently parse, load, activate, or run local agent profile +definitions. Legacy project paths such as `.pi/skills` can be added through `DEVSPACE_SKILL_PATHS` when needed. diff --git a/skills/local-agent-delegation/SKILL.md b/skills/local-agent-delegation/SKILL.md index c50173c7..4324bcbf 100644 --- a/skills/local-agent-delegation/SKILL.md +++ b/skills/local-agent-delegation/SKILL.md @@ -50,15 +50,11 @@ pi -p --mode json "$PROMPT" copilot -p "$PROMPT" --output-format json ``` -Use exact command templates from user-configured DevSpace agent profiles when they are available. Do not invent provider-specific flags when a profile already defines them. +Use exact command templates from user-provided instructions when they are available. Do not invent provider-specific flags when the user has already supplied a command shape. -Configured local agent profiles may live in: +Packaged files under `examples/agents/` are templates only. DevSpace does not currently parse, load, activate, or run local agent profile definitions. -```text -~/.devspace/agents/*.md -``` - -Treat those profiles as user-approved local worker definitions. If no profile exists for a requested agent, use the installed CLI's help output only when needed, then summarize what you found before running it. +If no command shape exists for a requested agent, use the installed CLI's help output only when needed, then summarize what you found before running it. ## Background execution @@ -174,14 +170,6 @@ Do not use local agents for destructive actions unless the user explicitly asks. Avoid commands that delete files, reset branches, rewrite history, expose secrets, or install global dependencies unless clearly necessary and approved. -Do not allow project-provided agent profiles to run automatically unless the user has trusted them. - -Prefer user-global profiles from: - -```text -~/.devspace/agents -``` - -over repo-provided profiles. +Do not treat repo-provided profile examples as trusted executable definitions. Never hide that a local agent was used. diff --git a/src/cli.ts b/src/cli.ts index b7b264cf..c1081e81 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -134,6 +134,7 @@ async function runInit({ force }: { force: boolean }): Promise { port, allowedRoots, publicBaseUrl, + localAgents: files.config.localAgents, }; const auth = { ownerToken: files.auth.ownerToken ?? generateOwnerToken(), @@ -141,7 +142,7 @@ async function runInit({ force }: { force: boolean }): Promise { const configPath = writeDevspaceConfig(config); const authPath = writeDevspaceAuth(auth); - const seededSkillPaths = ensureDevspaceDefaultSkills(); + const seededSkillPaths = config.localAgents ? ensureDevspaceDefaultSkills() : []; const lines = [ `Config: ${configPath}`, diff --git a/src/config.test.ts b/src/config.test.ts index 55f41596..09d82444 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -27,8 +27,13 @@ assert.equal(loadConfig({ ...baseEnv, DEVSPACE_MINIMAL_TOOLS: "0" }).toolMode, " assert.equal(loadConfig({ ...baseEnv, DEVSPACE_MINIMAL_TOOLS: "1" }).toolMode, "minimal"); assert.equal(loadConfig(baseEnv).skillsEnabled, true); assert.equal(loadConfig(baseEnv).devspaceSkillsDir, join(emptyConfigDir, "skills")); +assert.equal(loadConfig(baseEnv).localAgents, false); assert.equal(loadConfig({ ...baseEnv, DEVSPACE_SKILLS: "0" }).skillsEnabled, false); assert.equal(loadConfig({ ...baseEnv, DEVSPACE_SKILLS: "1" }).skillsEnabled, true); +assert.equal( + loadConfig({ ...baseEnv, DEVSPACE_LOCAL_AGENTS: "1" }).localAgents, + true, +); const seededConfigDir = mkdtempSync(join(tmpdir(), "devspace-seeded-skills-test-")); const seededSkillPaths = ensureDevspaceDefaultSkills({ DEVSPACE_CONFIG_DIR: seededConfigDir }); @@ -159,6 +164,7 @@ writeFileSync( port: 8787, allowedRoots: [process.cwd()], publicBaseUrl: "https://devspace.example.com", + localAgents: true, }), ); writeFileSync( @@ -172,6 +178,7 @@ const fileConfig = loadConfig({ DEVSPACE_CONFIG_DIR: configDir }); assert.equal(fileConfig.port, 8787); assert.equal(fileConfig.oauth.ownerToken, "persisted-owner-token-long-enough"); assert.equal(fileConfig.publicBaseUrl, "https://devspace.example.com"); +assert.equal(fileConfig.localAgents, true); assert.deepEqual(fileConfig.allowedHosts, [ "localhost", "127.0.0.1", diff --git a/src/config.ts b/src/config.ts index 4b970123..2fb7ec9a 100644 --- a/src/config.ts +++ b/src/config.ts @@ -26,6 +26,7 @@ export interface ServerConfig { skillsEnabled: boolean; skillPaths: string[]; devspaceSkillsDir: string; + localAgents: boolean; agentDir: string; logging: LoggingConfig; } @@ -237,6 +238,10 @@ export function loadConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig { skillsEnabled: env.DEVSPACE_SKILLS === undefined ? true : parseBoolean(env.DEVSPACE_SKILLS), skillPaths: parsePathList(env.DEVSPACE_SKILL_PATHS), devspaceSkillsDir: devspaceSkillsDir(env), + localAgents: + env.DEVSPACE_LOCAL_AGENTS === undefined + ? files.config.localAgents === true + : parseBoolean(env.DEVSPACE_LOCAL_AGENTS), agentDir: resolve(expandHomePath(env.DEVSPACE_AGENT_DIR ?? files.config.agentDir ?? defaultAgentDir())), logging: parseLoggingConfig(env), }; diff --git a/src/skills.test.ts b/src/skills.test.ts index c4bfe231..86b7c402 100644 --- a/src/skills.test.ts +++ b/src/skills.test.ts @@ -160,11 +160,25 @@ try { assert.equal(loaded.skills.some((skill) => skill.name === "claude-project-skill"), true); assert.equal(loaded.skills.some((skill) => skill.name === "project-skill"), false); assert.equal(loaded.skills.some((skill) => skill.name === "devspace-local-skill"), true); - assert.equal(loaded.skills.some((skill) => skill.name === "local-agent-delegation"), true); + assert.equal(loaded.skills.some((skill) => skill.name === "local-agent-delegation"), false); assert.equal(loaded.skills.filter((skill) => skill.name === "duplicate-skill").length, 1); assert.equal(loaded.skills.some((skill) => skill.name === "hidden-skill"), true); assert.equal(loaded.diagnostics.some((diagnostic) => diagnostic.type === "collision"), true); + const experimentalConfig = loadConfig({ + DEVSPACE_ALLOWED_ROOTS: projectRoot, + DEVSPACE_AGENT_DIR: agentDir, + DEVSPACE_LOCAL_AGENTS: "1", + DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", + PORT: "1", + }); + assert.equal( + loadWorkspaceSkills(experimentalConfig, projectRoot).skills.some( + (skill) => skill.name === "local-agent-delegation", + ), + true, + ); + const duplicateConfig = loadConfig({ DEVSPACE_ALLOWED_ROOTS: projectRoot, DEVSPACE_AGENT_DIR: agentDir, diff --git a/src/skills.ts b/src/skills.ts index 9ad49e5e..61678e6c 100644 --- a/src/skills.ts +++ b/src/skills.ts @@ -38,7 +38,9 @@ export function effectiveSkillPaths(config: ServerConfig, cwd: string): string[] resolve(cwd, ".agents", "skills"), config.devspaceSkillsDir, join(config.agentDir, "skills"), - hasLocalAgentDelegationSkill(config.devspaceSkillsDir) ? undefined : bundledSkills, + config.localAgents && !hasLocalAgentDelegationSkill(config.devspaceSkillsDir) + ? bundledSkills + : undefined, ]; const defaultPaths = defaultPathCandidates.filter( (path): path is string => path !== undefined && existsSync(path), @@ -61,12 +63,19 @@ function resolveSkillPath(path: string, cwd: string): string { export function loadWorkspaceSkills(config: ServerConfig, cwd: string): LoadedSkills { if (!config.skillsEnabled) return { skills: [], diagnostics: [] }; - return loadSkills({ + const result = loadSkills({ cwd, agentDir: config.agentDir, skillPaths: effectiveSkillPaths(config, cwd), includeDefaults: false, }); + + if (config.localAgents) return result; + + return { + skills: result.skills.filter((skill) => skill.name !== "local-agent-delegation"), + diagnostics: result.diagnostics, + }; } export function resolveSkillReadPath( diff --git a/src/user-config.ts b/src/user-config.ts index dcf718c2..96ca116a 100644 --- a/src/user-config.ts +++ b/src/user-config.ts @@ -18,6 +18,7 @@ export interface DevspaceUserConfig { stateDir?: string; worktreeRoot?: string; agentDir?: string; + localAgents?: boolean; } export interface DevspaceAuthConfig { From 5c36d0fd7fb7df8e8b5f6bbbb6e779de74e56caa Mon Sep 17 00:00:00 2001 From: Waishnav Date: Wed, 1 Jul 2026 17:01:50 +0530 Subject: [PATCH 5/8] fix(config): honor local agents env during init --- src/cli.ts | 3 ++- src/config.test.ts | 6 +++++- src/user-config.ts | 8 ++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index c1081e81..8010120d 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -10,6 +10,7 @@ import { ensureDevspaceDefaultSkills, generateOwnerToken, loadDevspaceFiles, + resolveLocalAgentsFlag, writeDevspaceAuth, writeDevspaceConfig, type DevspaceUserConfig, @@ -134,7 +135,7 @@ async function runInit({ force }: { force: boolean }): Promise { port, allowedRoots, publicBaseUrl, - localAgents: files.config.localAgents, + localAgents: resolveLocalAgentsFlag(files.config), }; const auth = { ownerToken: files.auth.ownerToken ?? generateOwnerToken(), diff --git a/src/config.test.ts b/src/config.test.ts index 09d82444..e6054f00 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -3,7 +3,7 @@ import { existsSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { loadConfig } from "./config.js"; -import { ensureDevspaceDefaultSkills } from "./user-config.js"; +import { ensureDevspaceDefaultSkills, resolveLocalAgentsFlag } from "./user-config.js"; const emptyConfigDir = mkdtempSync(join(tmpdir(), "devspace-empty-config-test-")); const baseEnv = { @@ -34,6 +34,10 @@ assert.equal( loadConfig({ ...baseEnv, DEVSPACE_LOCAL_AGENTS: "1" }).localAgents, true, ); +assert.equal(resolveLocalAgentsFlag({}, {}), undefined); +assert.equal(resolveLocalAgentsFlag({ localAgents: true }, {}), true); +assert.equal(resolveLocalAgentsFlag({ localAgents: true }, { DEVSPACE_LOCAL_AGENTS: "0" }), false); +assert.equal(resolveLocalAgentsFlag({}, { DEVSPACE_LOCAL_AGENTS: "1" }), true); const seededConfigDir = mkdtempSync(join(tmpdir(), "devspace-seeded-skills-test-")); const seededSkillPaths = ensureDevspaceDefaultSkills({ DEVSPACE_CONFIG_DIR: seededConfigDir }); diff --git a/src/user-config.ts b/src/user-config.ts index 96ca116a..b8c9a0ba 100644 --- a/src/user-config.ts +++ b/src/user-config.ts @@ -103,6 +103,14 @@ export function ensureDevspaceDefaultSkills(env: NodeJS.ProcessEnv = process.env return [targetPath]; } +export function resolveLocalAgentsFlag( + config: Pick, + env: NodeJS.ProcessEnv = process.env, +): boolean | undefined { + if (env.DEVSPACE_LOCAL_AGENTS === undefined) return config.localAgents; + return ["1", "true", "yes", "on"].includes(env.DEVSPACE_LOCAL_AGENTS.toLowerCase()); +} + function readJsonFile(filePath: string): T { try { return JSON.parse(readFileSync(filePath, "utf8")) as T; From afd669498d56ea320070a4b6bdd630ea4cadadf3 Mon Sep 17 00:00:00 2001 From: Waishnav Date: Wed, 1 Jul 2026 17:02:53 +0530 Subject: [PATCH 6/8] fix(skills): hide local agent diagnostics when disabled --- src/skills.test.ts | 30 ++++++++++++++++++++++++++++++ src/skills.ts | 10 +++++++--- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/skills.test.ts b/src/skills.test.ts index 86b7c402..af396bbc 100644 --- a/src/skills.test.ts +++ b/src/skills.test.ts @@ -31,8 +31,10 @@ try { await mkdir(join(projectClaudeSkills, "claude-project-skill"), { recursive: true }); await mkdir(join(projectRoot, ".pi", "skills", "project-skill"), { recursive: true }); await mkdir(join(agentDir, "skills", "global-skill"), { recursive: true }); + await mkdir(join(agentDir, "skills", "local-agent-delegation"), { recursive: true }); await mkdir(join(explicitSkills, "duplicate"), { recursive: true }); await mkdir(join(explicitSkills, "disabled"), { recursive: true }); + await mkdir(join(explicitSkills, "local-agent-delegation"), { recursive: true }); await mkdir(join(devspaceSkills, "devspace-local-skill"), { recursive: true }); await writeFile( @@ -123,6 +125,28 @@ try { "# Duplicate Skill", ].join("\n"), ); + await writeFile( + join(agentDir, "skills", "local-agent-delegation", "SKILL.md"), + [ + "---", + "name: local-agent-delegation", + "description: Hidden local agent skill winner.", + "---", + "", + "# Local Agent Delegation", + ].join("\n"), + ); + await writeFile( + join(explicitSkills, "local-agent-delegation", "SKILL.md"), + [ + "---", + "name: local-agent-delegation", + "description: Hidden local agent skill loser.", + "---", + "", + "# Local Agent Delegation Duplicate", + ].join("\n"), + ); await writeFile( join(explicitSkills, "disabled", "SKILL.md"), [ @@ -164,6 +188,12 @@ try { assert.equal(loaded.skills.filter((skill) => skill.name === "duplicate-skill").length, 1); assert.equal(loaded.skills.some((skill) => skill.name === "hidden-skill"), true); assert.equal(loaded.diagnostics.some((diagnostic) => diagnostic.type === "collision"), true); + assert.equal( + loaded.diagnostics.some( + (diagnostic) => diagnostic.collision?.name === "local-agent-delegation", + ), + false, + ); const experimentalConfig = loadConfig({ DEVSPACE_ALLOWED_ROOTS: projectRoot, diff --git a/src/skills.ts b/src/skills.ts index 61678e6c..c71b7ef7 100644 --- a/src/skills.ts +++ b/src/skills.ts @@ -21,7 +21,8 @@ export interface SkillReadResolution { isSkillFile: boolean; } -const LOCAL_AGENT_DELEGATION_SKILL = join("local-agent-delegation", "SKILL.md"); +const LOCAL_AGENT_DELEGATION_NAME = "local-agent-delegation"; +const LOCAL_AGENT_DELEGATION_SKILL = join(LOCAL_AGENT_DELEGATION_NAME, "SKILL.md"); function bundledSkillsDir(): string { return fileURLToPath(new URL("../skills", import.meta.url)); @@ -73,8 +74,11 @@ export function loadWorkspaceSkills(config: ServerConfig, cwd: string): LoadedSk if (config.localAgents) return result; return { - skills: result.skills.filter((skill) => skill.name !== "local-agent-delegation"), - diagnostics: result.diagnostics, + skills: result.skills.filter((skill) => skill.name !== LOCAL_AGENT_DELEGATION_NAME), + diagnostics: result.diagnostics.filter((diagnostic) => { + const collision = diagnostic.collision; + return !(collision?.resourceType === "skill" && collision.name === LOCAL_AGENT_DELEGATION_NAME); + }), }; } From 89beea99fa10665b1943302f7518baac7496edf1 Mon Sep 17 00:00:00 2001 From: Waishnav Date: Wed, 1 Jul 2026 17:03:07 +0530 Subject: [PATCH 7/8] docs(skills): note safe local agent argv handling --- skills/local-agent-delegation/SKILL.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/skills/local-agent-delegation/SKILL.md b/skills/local-agent-delegation/SKILL.md index 4324bcbf..5af2f9a4 100644 --- a/skills/local-agent-delegation/SKILL.md +++ b/skills/local-agent-delegation/SKILL.md @@ -50,6 +50,8 @@ pi -p --mode json "$PROMPT" copilot -p "$PROMPT" --output-format json ``` +These examples are illustrative. Real implementations should pass workspace and prompt values as separate argv entries without a shell, not concatenate untrusted prompt text into a shell command string. + Use exact command templates from user-provided instructions when they are available. Do not invent provider-specific flags when the user has already supplied a command shape. Packaged files under `examples/agents/` are templates only. DevSpace does not currently parse, load, activate, or run local agent profile definitions. From 4c436061a37369d499f6b31ad15dd0515641604a Mon Sep 17 00:00:00 2001 From: Waishnav Date: Wed, 1 Jul 2026 18:05:59 +0530 Subject: [PATCH 8/8] fix(agents): correct local agent example commands --- examples/agents/claude-implementer.md | 2 ++ examples/agents/copilot-reviewer.md | 6 +++++- examples/agents/cursor-agent-worker.md | 1 + examples/agents/opencode-explorer.md | 2 +- examples/agents/pi-reviewer.md | 2 +- 5 files changed, 10 insertions(+), 3 deletions(-) diff --git a/examples/agents/claude-implementer.md b/examples/agents/claude-implementer.md index 58f91ed0..235d7e5f 100644 --- a/examples/agents/claude-implementer.md +++ b/examples/agents/claude-implementer.md @@ -24,6 +24,7 @@ actions: - -p - --output-format - stream-json + - --verbose - "{prompt}" background: true output: stream-json @@ -37,6 +38,7 @@ actions: - -p - --output-format - stream-json + - --verbose - "{prompt}" list: diff --git a/examples/agents/copilot-reviewer.md b/examples/agents/copilot-reviewer.md index f3b66074..640b405a 100644 --- a/examples/agents/copilot-reviewer.md +++ b/examples/agents/copilot-reviewer.md @@ -21,17 +21,21 @@ actions: start: command: copilot args: + - --model + - auto - -p - "{prompt}" - --output-format - json background: true - output: json + output: jsonl followup: strategy: resume_command command: copilot args: + - --model + - auto - -p - "{prompt}" - --resume diff --git a/examples/agents/cursor-agent-worker.md b/examples/agents/cursor-agent-worker.md index 4dffef2a..89be2b17 100644 --- a/examples/agents/cursor-agent-worker.md +++ b/examples/agents/cursor-agent-worker.md @@ -24,6 +24,7 @@ actions: - -p - --output-format - stream-json + - --trust - "{prompt}" background: true output: stream-json diff --git a/examples/agents/opencode-explorer.md b/examples/agents/opencode-explorer.md index 93c7a846..9a86ce6a 100644 --- a/examples/agents/opencode-explorer.md +++ b/examples/agents/opencode-explorer.md @@ -28,7 +28,7 @@ actions: - "{workspace}" - "{prompt}" background: true - output: json + output: jsonl followup: strategy: resume_command diff --git a/examples/agents/pi-reviewer.md b/examples/agents/pi-reviewer.md index 27510c27..21e842e9 100644 --- a/examples/agents/pi-reviewer.md +++ b/examples/agents/pi-reviewer.md @@ -26,7 +26,7 @@ actions: - json - "{prompt}" background: true - output: json + output: jsonl followup: strategy: resume_command