Skip to content

Commit 172d8bb

Browse files
committed
feat(skills): gate local agent delegation experiment
1 parent 5fe6957 commit 172d8bb

11 files changed

Lines changed: 62 additions & 41 deletions

File tree

docs/agent-profile-schema.md

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,9 @@ DevSpace local agent profiles are user-owned markdown files with YAML front matt
44
They describe how a local coding-agent CLI can be used as a worker under ChatGPT
55
supervision.
66

7-
Profiles are intended to live in:
8-
9-
```text
10-
~/.devspace/agents/*.md
11-
```
12-
137
The packaged files in `examples/agents/` are starter templates only. DevSpace does
14-
not automatically activate them, copy them into `~/.devspace/agents`, or run their
15-
commands. Users should copy, review, and edit a template before treating it as an
16-
active local worker definition.
8+
not currently parse, load, activate, or run local agent profile definitions. A
9+
future profile loader can define the user-owned directory for active profiles.
1710

1811
## Minimal shape
1912

@@ -339,7 +332,7 @@ The body is model-facing guidance. Keep it practical and concise.
339332

340333
The current examples do not add:
341334

342-
- `.devspace/agents` parsing.
335+
- local agent profile parsing.
343336
- automatic activation of packaged examples.
344337
- `devspace agents init`.
345338
- generated available-agent catalogs.

docs/chatgpt-coding-workflow.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,13 @@ DevSpace discovers standard Agent Skills from:
8787

8888
It also keeps compatibility with:
8989

90-
- the bundled `local-agent-delegation` skill, unless `~/.devspace/skills/local-agent-delegation/SKILL.md` exists
90+
- the bundled `local-agent-delegation` skill when `DEVSPACE_LOCAL_AGENTS=1`, unless `~/.devspace/skills/local-agent-delegation/SKILL.md` exists
9191
- `DEVSPACE_AGENT_DIR/skills`, defaulting to `~/.codex/skills`
9292
- additional paths from `DEVSPACE_SKILL_PATHS`
9393

9494
Example local coding-agent profiles are packaged under `examples/agents/` for
95-
users who want starter templates for `~/.devspace/agents/*.md`. These examples
96-
are not activated automatically.
95+
users who want starter templates. These examples are inert: DevSpace does not
96+
currently parse, load, activate, or run local agent profile definitions.
9797

9898
Legacy project paths such as `.pi/skills` can be added through `DEVSPACE_SKILL_PATHS` when needed.
9999

@@ -105,7 +105,9 @@ Skill paths may be outside the workspace. DevSpace only permits reading:
105105
- advertised `SKILL.md` files
106106
- files under a skill directory after that skill's `SKILL.md` has been read
107107

108-
Set `DEVSPACE_SKILLS=0` to hide skills from workspace output.
108+
Set `DEVSPACE_SKILLS=0` to hide skills from workspace output. Set
109+
`DEVSPACE_LOCAL_AGENTS=1` to expose the experimental `local-agent-delegation`
110+
skill.
109111

110112
## Tool Names
111113

docs/configuration.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ sessions.
9999
| Variable | Purpose |
100100
| --- | --- |
101101
| `DEVSPACE_SKILLS` | Set to `0` to hide skills. Enabled by default. |
102+
| `DEVSPACE_LOCAL_AGENTS` | Set to `1` to expose the local-agent delegation skill. Experimental and disabled by default. |
102103
| `DEVSPACE_AGENT_DIR` | Defaults to `~/.codex`; its `skills` child is loaded for compatibility. |
103104
| `DEVSPACE_SKILL_PATHS` | Optional comma-separated additional skill directories. |
104105

@@ -110,13 +111,13 @@ DevSpace discovers standard Agent Skills from:
110111

111112
It also keeps compatibility with:
112113

113-
- the bundled `local-agent-delegation` skill, unless `~/.devspace/skills/local-agent-delegation/SKILL.md` exists
114+
- the bundled `local-agent-delegation` skill when `DEVSPACE_LOCAL_AGENTS=1`, unless `~/.devspace/skills/local-agent-delegation/SKILL.md` exists
114115
- `DEVSPACE_AGENT_DIR/skills`, defaulting to `~/.codex/skills`
115116
- additional paths from `DEVSPACE_SKILL_PATHS`
116117

117118
Starter local coding-agent profile templates are available under
118-
`examples/agents/`. Users can copy them into `~/.devspace/agents/` and edit them
119-
for their local CLIs. DevSpace does not activate packaged examples automatically.
119+
`examples/agents/`. These files are inert examples: DevSpace does not currently
120+
parse, load, activate, or run local agent profile definitions.
120121

121122
Legacy project paths such as `.pi/skills` can be added through `DEVSPACE_SKILL_PATHS` when needed.
122123

docs/gotchas.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,13 +201,13 @@ DevSpace looks in standard Agent Skills locations:
201201

202202
It also checks compatibility and custom paths:
203203

204-
- the bundled `local-agent-delegation` skill, unless `~/.devspace/skills/local-agent-delegation/SKILL.md` exists
204+
- the bundled `local-agent-delegation` skill when `DEVSPACE_LOCAL_AGENTS=1`, unless `~/.devspace/skills/local-agent-delegation/SKILL.md` exists
205205
- `DEVSPACE_AGENT_DIR/skills`, defaulting to `~/.codex/skills`
206206
- additional paths from `DEVSPACE_SKILL_PATHS`
207207

208-
Packaged local-agent examples under `examples/agents/` are templates only. Copy
209-
and review a profile before using it from `~/.devspace/agents/`; repo-provided or
210-
packaged examples should not become runnable worker definitions silently.
208+
Packaged local-agent examples under `examples/agents/` are inert templates only.
209+
DevSpace does not currently parse, load, activate, or run local agent profile
210+
definitions.
211211

212212
Legacy project paths such as `.pi/skills` can be added through `DEVSPACE_SKILL_PATHS` when needed.
213213

skills/local-agent-delegation/SKILL.md

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,11 @@ pi -p --mode json "$PROMPT"
5050
copilot -p "$PROMPT" --output-format json
5151
```
5252

53-
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.
53+
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.
5454

55-
Configured local agent profiles may live in:
55+
Packaged files under `examples/agents/` are templates only. DevSpace does not currently parse, load, activate, or run local agent profile definitions.
5656

57-
```text
58-
~/.devspace/agents/*.md
59-
```
60-
61-
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.
57+
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.
6258

6359
## Background execution
6460

@@ -174,14 +170,6 @@ Do not use local agents for destructive actions unless the user explicitly asks.
174170

175171
Avoid commands that delete files, reset branches, rewrite history, expose secrets, or install global dependencies unless clearly necessary and approved.
176172

177-
Do not allow project-provided agent profiles to run automatically unless the user has trusted them.
178-
179-
Prefer user-global profiles from:
180-
181-
```text
182-
~/.devspace/agents
183-
```
184-
185-
over repo-provided profiles.
173+
Do not treat repo-provided profile examples as trusted executable definitions.
186174

187175
Never hide that a local agent was used.

src/cli.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,14 +134,15 @@ async function runInit({ force }: { force: boolean }): Promise<void> {
134134
port,
135135
allowedRoots,
136136
publicBaseUrl,
137+
localAgents: files.config.localAgents,
137138
};
138139
const auth = {
139140
ownerToken: files.auth.ownerToken ?? generateOwnerToken(),
140141
};
141142

142143
const configPath = writeDevspaceConfig(config);
143144
const authPath = writeDevspaceAuth(auth);
144-
const seededSkillPaths = ensureDevspaceDefaultSkills();
145+
const seededSkillPaths = config.localAgents ? ensureDevspaceDefaultSkills() : [];
145146

146147
const lines = [
147148
`Config: ${configPath}`,

src/config.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,13 @@ assert.equal(loadConfig({ ...baseEnv, DEVSPACE_MINIMAL_TOOLS: "0" }).toolMode, "
2727
assert.equal(loadConfig({ ...baseEnv, DEVSPACE_MINIMAL_TOOLS: "1" }).toolMode, "minimal");
2828
assert.equal(loadConfig(baseEnv).skillsEnabled, true);
2929
assert.equal(loadConfig(baseEnv).devspaceSkillsDir, join(emptyConfigDir, "skills"));
30+
assert.equal(loadConfig(baseEnv).localAgents, false);
3031
assert.equal(loadConfig({ ...baseEnv, DEVSPACE_SKILLS: "0" }).skillsEnabled, false);
3132
assert.equal(loadConfig({ ...baseEnv, DEVSPACE_SKILLS: "1" }).skillsEnabled, true);
33+
assert.equal(
34+
loadConfig({ ...baseEnv, DEVSPACE_LOCAL_AGENTS: "1" }).localAgents,
35+
true,
36+
);
3237

3338
const seededConfigDir = mkdtempSync(join(tmpdir(), "devspace-seeded-skills-test-"));
3439
const seededSkillPaths = ensureDevspaceDefaultSkills({ DEVSPACE_CONFIG_DIR: seededConfigDir });
@@ -159,6 +164,7 @@ writeFileSync(
159164
port: 8787,
160165
allowedRoots: [process.cwd()],
161166
publicBaseUrl: "https://devspace.example.com",
167+
localAgents: true,
162168
}),
163169
);
164170
writeFileSync(
@@ -172,6 +178,7 @@ const fileConfig = loadConfig({ DEVSPACE_CONFIG_DIR: configDir });
172178
assert.equal(fileConfig.port, 8787);
173179
assert.equal(fileConfig.oauth.ownerToken, "persisted-owner-token-long-enough");
174180
assert.equal(fileConfig.publicBaseUrl, "https://devspace.example.com");
181+
assert.equal(fileConfig.localAgents, true);
175182
assert.deepEqual(fileConfig.allowedHosts, [
176183
"localhost",
177184
"127.0.0.1",

src/config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface ServerConfig {
2626
skillsEnabled: boolean;
2727
skillPaths: string[];
2828
devspaceSkillsDir: string;
29+
localAgents: boolean;
2930
agentDir: string;
3031
logging: LoggingConfig;
3132
}
@@ -237,6 +238,10 @@ export function loadConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig {
237238
skillsEnabled: env.DEVSPACE_SKILLS === undefined ? true : parseBoolean(env.DEVSPACE_SKILLS),
238239
skillPaths: parsePathList(env.DEVSPACE_SKILL_PATHS),
239240
devspaceSkillsDir: devspaceSkillsDir(env),
241+
localAgents:
242+
env.DEVSPACE_LOCAL_AGENTS === undefined
243+
? files.config.localAgents === true
244+
: parseBoolean(env.DEVSPACE_LOCAL_AGENTS),
240245
agentDir: resolve(expandHomePath(env.DEVSPACE_AGENT_DIR ?? files.config.agentDir ?? defaultAgentDir())),
241246
logging: parseLoggingConfig(env),
242247
};

src/skills.test.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,25 @@ try {
160160
assert.equal(loaded.skills.some((skill) => skill.name === "claude-project-skill"), true);
161161
assert.equal(loaded.skills.some((skill) => skill.name === "project-skill"), false);
162162
assert.equal(loaded.skills.some((skill) => skill.name === "devspace-local-skill"), true);
163-
assert.equal(loaded.skills.some((skill) => skill.name === "local-agent-delegation"), true);
163+
assert.equal(loaded.skills.some((skill) => skill.name === "local-agent-delegation"), false);
164164
assert.equal(loaded.skills.filter((skill) => skill.name === "duplicate-skill").length, 1);
165165
assert.equal(loaded.skills.some((skill) => skill.name === "hidden-skill"), true);
166166
assert.equal(loaded.diagnostics.some((diagnostic) => diagnostic.type === "collision"), true);
167167

168+
const experimentalConfig = loadConfig({
169+
DEVSPACE_ALLOWED_ROOTS: projectRoot,
170+
DEVSPACE_AGENT_DIR: agentDir,
171+
DEVSPACE_LOCAL_AGENTS: "1",
172+
DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough",
173+
PORT: "1",
174+
});
175+
assert.equal(
176+
loadWorkspaceSkills(experimentalConfig, projectRoot).skills.some(
177+
(skill) => skill.name === "local-agent-delegation",
178+
),
179+
true,
180+
);
181+
168182
const duplicateConfig = loadConfig({
169183
DEVSPACE_ALLOWED_ROOTS: projectRoot,
170184
DEVSPACE_AGENT_DIR: agentDir,

src/skills.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ export function effectiveSkillPaths(config: ServerConfig, cwd: string): string[]
3838
resolve(cwd, ".agents", "skills"),
3939
config.devspaceSkillsDir,
4040
join(config.agentDir, "skills"),
41-
hasLocalAgentDelegationSkill(config.devspaceSkillsDir) ? undefined : bundledSkills,
41+
config.localAgents && !hasLocalAgentDelegationSkill(config.devspaceSkillsDir)
42+
? bundledSkills
43+
: undefined,
4244
];
4345
const defaultPaths = defaultPathCandidates.filter(
4446
(path): path is string => path !== undefined && existsSync(path),
@@ -61,12 +63,19 @@ function resolveSkillPath(path: string, cwd: string): string {
6163
export function loadWorkspaceSkills(config: ServerConfig, cwd: string): LoadedSkills {
6264
if (!config.skillsEnabled) return { skills: [], diagnostics: [] };
6365

64-
return loadSkills({
66+
const result = loadSkills({
6567
cwd,
6668
agentDir: config.agentDir,
6769
skillPaths: effectiveSkillPaths(config, cwd),
6870
includeDefaults: false,
6971
});
72+
73+
if (config.localAgents) return result;
74+
75+
return {
76+
skills: result.skills.filter((skill) => skill.name !== "local-agent-delegation"),
77+
diagnostics: result.diagnostics,
78+
};
7079
}
7180

7281
export function resolveSkillReadPath(

0 commit comments

Comments
 (0)