Skip to content

Commit 509d4ac

Browse files
committed
fix(skills): keep bundled agent skills package-managed
1 parent 9a3f5d8 commit 509d4ac

5 files changed

Lines changed: 27 additions & 59 deletions

File tree

src/cli.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import {
2929
import { resolveLocalAgentExecution } from "./local-agent-resolution.js";
3030
import { createLocalAgentStore, type LocalAgentRecord } from "./local-agent-store.js";
3131
import {
32-
ensureDevspaceDefaultSkills,
3332
generateOwnerToken,
3433
loadDevspaceFiles,
3534
resolveSubagentsFlag,
@@ -186,12 +185,9 @@ async function runInit({ force }: { force: boolean }): Promise<void> {
186185

187186
const configPath = writeDevspaceConfig(config);
188187
const authPath = writeDevspaceAuth(auth);
189-
const seededSkillPaths = config.subagents ? ensureDevspaceDefaultSkills() : [];
190-
191188
const lines = [
192189
`Config: ${configPath}`,
193190
`Auth: ${authPath}`,
194-
...seededSkillPaths.map((path) => `Default skill: ${path}`),
195191
`Local MCP URL: http://${config.host}:${config.port}/mcp`,
196192
...(publicBaseUrl ? [`Public MCP URL: ${publicBaseUrl}/mcp`] : []),
197193
];

src/config.test.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import assert from "node:assert/strict";
2-
import { existsSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
2+
import { mkdtempSync, writeFileSync } from "node:fs";
33
import { tmpdir } from "node:os";
44
import { join } from "node:path";
55
import { loadConfig } from "./config.js";
6-
import { ensureDevspaceDefaultSkills, resolveSubagentsFlag } from "./user-config.js";
6+
import { resolveSubagentsFlag } from "./user-config.js";
77

88
const emptyConfigDir = mkdtempSync(join(tmpdir(), "devspace-empty-config-test-"));
99
const baseEnv = {
@@ -50,18 +50,6 @@ assert.equal(resolveSubagentsFlag({ subagents: true }, {}), true);
5050
assert.equal(resolveSubagentsFlag({ subagents: true }, { DEVSPACE_SUBAGENTS: "0" }), false);
5151
assert.equal(resolveSubagentsFlag({}, { DEVSPACE_SUBAGENTS: "1" }), true);
5252

53-
const seededConfigDir = mkdtempSync(join(tmpdir(), "devspace-seeded-skills-test-"));
54-
const seededSkillPaths = ensureDevspaceDefaultSkills({ DEVSPACE_CONFIG_DIR: seededConfigDir });
55-
assert.deepEqual(seededSkillPaths, [
56-
join(seededConfigDir, "skills", "subagents", "SKILL.md"),
57-
join(seededConfigDir, "skills", "dynamic-workflows", "SKILL.md"),
58-
]);
59-
assert.equal(existsSync(seededSkillPaths[0]), true);
60-
assert.equal(existsSync(seededSkillPaths[1]), true);
61-
assert.match(readFileSync(seededSkillPaths[0], "utf8"), /name: subagents/);
62-
assert.match(readFileSync(seededSkillPaths[1], "utf8"), /name: dynamic-workflows/);
63-
assert.deepEqual(ensureDevspaceDefaultSkills({ DEVSPACE_CONFIG_DIR: seededConfigDir }), []);
64-
6553
assert.throws(
6654
() => loadConfig({ ...baseEnv, DEVSPACE_WIDGETS: "invalid" }),
6755
/Invalid DEVSPACE_WIDGETS: invalid/,

src/skills.test.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -199,16 +199,15 @@ try {
199199
DEVSPACE_ALLOWED_ROOTS: projectRoot,
200200
DEVSPACE_AGENT_DIR: agentDir,
201201
DEVSPACE_SUBAGENTS: "1",
202+
DEVSPACE_WORKFLOWS: "0",
202203
DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough",
203204
PORT: "1",
204205
});
205-
assert.equal(
206-
loadWorkspaceSkills(experimentalConfig, projectRoot).skills.some(
207-
(skill) => skill.name === "subagents",
208-
),
209-
true,
210-
);
211-
const subagentsSkill = loadWorkspaceSkills(experimentalConfig, projectRoot).skills.find(
206+
const subagentSkills = loadWorkspaceSkills(experimentalConfig, projectRoot).skills;
207+
assert.equal(subagentSkills.some((skill) => skill.name === "subagents"), true);
208+
assert.equal(subagentSkills.some((skill) => skill.name === "dynamic-workflows"), false);
209+
assert.equal(subagentSkills.some((skill) => skill.name === "subagent-delegation"), false);
210+
const subagentsSkill = subagentSkills.find(
212211
(skill) => skill.name === "subagents",
213212
);
214213
assert.ok(subagentsSkill);
@@ -220,6 +219,19 @@ try {
220219
codexReference,
221220
);
222221

222+
const workflowsOnlyConfig = loadConfig({
223+
DEVSPACE_ALLOWED_ROOTS: projectRoot,
224+
DEVSPACE_AGENT_DIR: agentDir,
225+
DEVSPACE_SUBAGENTS: "0",
226+
DEVSPACE_WORKFLOWS: "1",
227+
DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough",
228+
PORT: "1",
229+
});
230+
const workflowSkills = loadWorkspaceSkills(workflowsOnlyConfig, projectRoot).skills;
231+
assert.equal(workflowSkills.some((skill) => skill.name === "subagents"), false);
232+
assert.equal(workflowSkills.some((skill) => skill.name === "dynamic-workflows"), true);
233+
assert.equal(workflowSkills.some((skill) => skill.name === "subagent-delegation"), false);
234+
223235
const duplicateConfig = loadConfig({
224236
DEVSPACE_ALLOWED_ROOTS: projectRoot,
225237
DEVSPACE_AGENT_DIR: agentDir,

src/skills.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,14 @@ function bundledSkillsDir(): string {
2929
return fileURLToPath(new URL("../skills", import.meta.url));
3030
}
3131

32-
/**
33-
* Always include the bundled skills root when subagents are enabled.
34-
* Previously the whole dir was dropped if the user had seeded
35-
* subagent-delegation — that hid later skills (e.g. dynamic-workflows).
36-
* User/devspace copies still win via earlier path order + name collisions.
37-
*/
32+
/** Package skills are defaults; user/project copies win by path order. */
3833
export function effectiveSkillPaths(config: ServerConfig, cwd: string): string[] {
3934
const defaultPathCandidates = [
4035
join(homedir(), ".agents", "skills"),
4136
resolve(cwd, ".agents", "skills"),
4237
config.devspaceSkillsDir,
4338
join(config.agentDir, "skills"),
44-
config.subagents ? bundledSkillsDir() : undefined,
39+
config.subagents || config.workflows ? bundledSkillsDir() : undefined,
4540
];
4641
const defaultPaths = defaultPathCandidates.filter(
4742
(path): path is string => path !== undefined && existsSync(path),
@@ -71,13 +66,9 @@ export function loadWorkspaceSkills(config: ServerConfig, cwd: string): LoadedSk
7166
includeDefaults: false,
7267
});
7368

74-
if (config.subagents) return result;
75-
76-
const gated = new Set<string>([
77-
SUBAGENTS_NAME,
78-
LEGACY_SUBAGENT_DELEGATION_NAME,
79-
DYNAMIC_WORKFLOWS_NAME,
80-
]);
69+
const gated = new Set<string>([LEGACY_SUBAGENT_DELEGATION_NAME]);
70+
if (!config.subagents) gated.add(SUBAGENTS_NAME);
71+
if (!config.workflows) gated.add(DYNAMIC_WORKFLOWS_NAME);
8172
return {
8273
skills: result.skills.filter((skill) => !gated.has(skill.name)),
8374
diagnostics: result.diagnostics.filter((diagnostic) => {

src/user-config.ts

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
writeFileSync,
77
} from "node:fs";
88
import { homedir } from "node:os";
9-
import { dirname, join, resolve } from "node:path";
9+
import { join, resolve } from "node:path";
1010
import { expandHomePath } from "./roots.js";
1111

1212
export interface DevspaceUserConfig {
@@ -97,25 +97,6 @@ export function generateOwnerToken(): string {
9797
return randomBytes(32).toString("base64url");
9898
}
9999

100-
const DEFAULT_SKILLS = ["subagents", "dynamic-workflows"] as const;
101-
102-
export function ensureDevspaceDefaultSkills(env: NodeJS.ProcessEnv = process.env): string[] {
103-
const seeded: string[] = [];
104-
for (const name of DEFAULT_SKILLS) {
105-
const targetPath = join(devspaceSkillsDir(env), name, "SKILL.md");
106-
if (existsSync(targetPath)) continue;
107-
const sourcePath = new URL(`../skills/${name}/SKILL.md`, import.meta.url);
108-
try {
109-
mkdirSync(dirname(targetPath), { recursive: true });
110-
writeFileSync(targetPath, readFileSync(sourcePath, "utf8"), { mode: 0o644 });
111-
seeded.push(targetPath);
112-
} catch {
113-
// skill may not exist in package yet; skip
114-
}
115-
}
116-
return seeded;
117-
}
118-
119100
export function resolveSubagentsFlag(
120101
config: Pick<DevspaceUserConfig, "subagents">,
121102
env: NodeJS.ProcessEnv = process.env,

0 commit comments

Comments
 (0)