From 415b1a403bc0a23c46475721e9a2cc9fb8778a53 Mon Sep 17 00:00:00 2001 From: Devansh Jain <31609257+devanshrj@users.noreply.github.com> Date: Thu, 12 Feb 2026 18:26:46 -0800 Subject: [PATCH] feat: add --no-skills flag to disable bundled skills (#948) Co-authored-by: Letta --- src/agent/context.ts | 12 ++++++++++++ src/agent/skills.ts | 9 ++++++--- src/cli/App.tsx | 8 ++++++-- src/headless.ts | 8 ++++++-- src/index.ts | 11 +++++++++-- 5 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/agent/context.ts b/src/agent/context.ts index 2644b43..8de4b18 100644 --- a/src/agent/context.ts +++ b/src/agent/context.ts @@ -6,6 +6,7 @@ interface AgentContext { agentId: string | null; skillsDirectory: string | null; + noSkills: boolean; conversationId: string | null; } @@ -23,6 +24,7 @@ function getContext(): AgentContext { global[CONTEXT_KEY] = { agentId: null, skillsDirectory: null, + noSkills: false, conversationId: null, }; } @@ -35,13 +37,16 @@ const context = getContext(); * Set the current agent context * @param agentId - The agent ID * @param skillsDirectory - Optional skills directory path + * @param noSkills - Whether to skip bundled skills */ export function setAgentContext( agentId: string, skillsDirectory?: string, + noSkills?: boolean, ): void { context.agentId = agentId; context.skillsDirectory = skillsDirectory || null; + context.noSkills = noSkills ?? false; } /** @@ -70,6 +75,13 @@ export function getSkillsDirectory(): string | null { return context.skillsDirectory; } +/** + * Get whether bundled skills should be skipped + */ +export function getNoSkills(): boolean { + return context.noSkills; +} + /** * Set the current conversation ID * @param conversationId - The conversation ID, or null to clear diff --git a/src/agent/skills.ts b/src/agent/skills.ts index 42c3d6d..29766fc 100644 --- a/src/agent/skills.ts +++ b/src/agent/skills.ts @@ -167,14 +167,17 @@ async function discoverSkillsFromDir( export async function discoverSkills( projectSkillsPath: string = join(process.cwd(), SKILLS_DIR), agentId?: string, + options?: { skipBundled?: boolean }, ): Promise { const allErrors: SkillDiscoveryError[] = []; const skillsById = new Map(); // 1. Start with bundled skills (lowest priority) - const bundledSkills = await getBundledSkills(); - for (const skill of bundledSkills) { - skillsById.set(skill.id, skill); + if (!options?.skipBundled) { + const bundledSkills = await getBundledSkills(); + for (const skill of bundledSkills) { + skillsById.set(skill.id, skill); + } } // 2. Add global skills (override bundled) diff --git a/src/cli/App.tsx b/src/cli/App.tsx index 85747b9..c7e2c41 100644 --- a/src/cli/App.tsx +++ b/src/cli/App.tsx @@ -8074,10 +8074,14 @@ ${SYSTEM_REMINDER_CLOSE} try { const { discoverSkills: discover, SKILLS_DIR: defaultDir } = await import("../agent/skills"); - const { getSkillsDirectory } = await import("../agent/context"); + const { getSkillsDirectory, getNoSkills } = await import( + "../agent/context" + ); const skillsDir = getSkillsDirectory() || join(process.cwd(), defaultDir); - const { skills } = await discover(skillsDir, agentId); + const { skills } = await discover(skillsDir, agentId, { + skipBundled: getNoSkills(), + }); discoveredSkillsRef.current = skills; } catch { discoveredSkillsRef.current = []; diff --git a/src/headless.ts b/src/headless.ts index 77074ed..1d979b1 100644 --- a/src/headless.ts +++ b/src/headless.ts @@ -79,6 +79,7 @@ export async function handleHeadlessCommand( argv: string[], model?: string, skillsDirectory?: string, + noSkills?: boolean, ) { const settings = settingsManager.getSettings(); @@ -125,6 +126,7 @@ export async function handleHeadlessCommand( memfs: { type: "boolean" }, "no-memfs": { type: "boolean" }, + "no-skills": { type: "boolean" }, "max-turns": { type: "string" }, // Maximum number of agentic turns }, strict: false, @@ -807,7 +809,7 @@ export async function handleHeadlessCommand( } // Set agent context for tools that need it (e.g., Skill tool, Task tool) - setAgentContext(agent.id, skillsDirectory); + setAgentContext(agent.id, skillsDirectory, noSkills); // Validate output format const outputFormat = @@ -1071,7 +1073,9 @@ ${SYSTEM_REMINDER_CLOSE} const { join } = await import("node:path"); try { const skillsDir = getSkillsDirectory() || join(process.cwd(), defaultDir); - const { skills } = await discoverSkills(skillsDir, agent.id); + const { skills } = await discoverSkills(skillsDir, agent.id, { + skipBundled: noSkills, + }); const skillsReminder = formatSkillsAsSystemReminder(skills); if (skillsReminder) { pushPart(skillsReminder); diff --git a/src/index.ts b/src/index.ts index 167a3a4..5eb75a5 100755 --- a/src/index.ts +++ b/src/index.ts @@ -442,6 +442,7 @@ async function main(): Promise { memfs: { type: "boolean" }, "no-memfs": { type: "boolean" }, + "no-skills": { type: "boolean" }, "max-turns": { type: "string" }, }, strict: true, @@ -553,6 +554,7 @@ async function main(): Promise { const skillsDirectory = (values.skills as string | undefined) ?? undefined; const memfsFlag = values.memfs as boolean | undefined; const noMemfsFlag = values["no-memfs"] as boolean | undefined; + const noSkillsFlag = values["no-skills"] as boolean | undefined; const fromAfFile = (values.import as string | undefined) ?? (values["from-af"] as string | undefined); @@ -946,7 +948,12 @@ async function main(): Promise { markMilestone("TOOLS_LOADED"); const { handleHeadlessCommand } = await import("./headless"); - await handleHeadlessCommand(process.argv, specifiedModel, skillsDirectory); + await handleHeadlessCommand( + process.argv, + specifiedModel, + skillsDirectory, + noSkillsFlag, + ); return; } @@ -1679,7 +1686,7 @@ async function main(): Promise { } // Set agent context for tools that need it (e.g., Skill tool) - setAgentContext(agent.id, skillsDirectory); + setAgentContext(agent.id, skillsDirectory, noSkillsFlag); // Apply memfs flag if explicitly specified (memfs is opt-in via /memfs enable or --memfs) const isSubagent = process.env.LETTA_CODE_AGENT_ROLE === "subagent";