Fix --no-skills flag not preventing skills from loading

The --no-skills flag set options.skills = [] in main.ts, but the
interactive mode UI would rediscover skills anyway because it called
loadSkills() directly instead of using the already-loaded skills.

Changes:
- Add AgentSession.skills and AgentSession.skillWarnings properties
- discoverSkills() now returns { skills, warnings } instead of Skill[]
- Interactive mode uses session.skills instead of calling loadSkills()
- Update SDK docs and examples for new return type

Fixes #577
This commit is contained in:
Mario Zechner 2026-01-08 23:32:57 +01:00
parent c865ec1d19
commit af2d8509e6
7 changed files with 88 additions and 54 deletions

View file

@ -50,6 +50,7 @@ import type { ModelRegistry } from "./model-registry.js";
import { expandPromptTemplate, type PromptTemplate } from "./prompt-templates.js";
import type { BranchSummaryEntry, CompactionEntry, NewSessionOptions, SessionManager } from "./session-manager.js";
import type { SettingsManager, SkillsSettings } from "./settings-manager.js";
import type { Skill, SkillWarning } from "./skills.js";
import type { BashOperations } from "./tools/bash.js";
/** Session-specific events that extend the core AgentEvent */
@ -77,6 +78,10 @@ export interface AgentSessionConfig {
promptTemplates?: PromptTemplate[];
/** Extension runner (created in sdk.ts with wrapped tools) */
extensionRunner?: ExtensionRunner;
/** Loaded skills (already discovered by SDK) */
skills?: Skill[];
/** Skill loading warnings (already captured by SDK) */
skillWarnings?: SkillWarning[];
skillsSettings?: Required<SkillsSettings>;
/** Model registry for API key resolution and model discovery */
modelRegistry: ModelRegistry;
@ -177,6 +182,8 @@ export class AgentSession {
private _extensionRunner: ExtensionRunner | undefined = undefined;
private _turnIndex = 0;
private _skills: Skill[];
private _skillWarnings: SkillWarning[];
private _skillsSettings: Required<SkillsSettings> | undefined;
// Model registry for API key resolution
@ -198,6 +205,8 @@ export class AgentSession {
this._scopedModels = config.scopedModels ?? [];
this._promptTemplates = config.promptTemplates ?? [];
this._extensionRunner = config.extensionRunner;
this._skills = config.skills ?? [];
this._skillWarnings = config.skillWarnings ?? [];
this._skillsSettings = config.skillsSettings;
this._modelRegistry = config.modelRegistry;
this._toolRegistry = config.toolRegistry ?? new Map();
@ -870,6 +879,16 @@ export class AgentSession {
return this._skillsSettings;
}
/** Skills loaded by SDK (empty if --no-skills or skills: [] was passed) */
get skills(): readonly Skill[] {
return this._skills;
}
/** Skill loading warnings captured by SDK */
get skillWarnings(): readonly SkillWarning[] {
return this._skillWarnings;
}
/**
* Abort current operation and wait for agent to become idle.
*/

View file

@ -43,7 +43,7 @@ import { ModelRegistry } from "./model-registry.js";
import { loadPromptTemplates as loadPromptTemplatesInternal, type PromptTemplate } from "./prompt-templates.js";
import { SessionManager } from "./session-manager.js";
import { type Settings, SettingsManager, type SkillsSettings } from "./settings-manager.js";
import { loadSkills as loadSkillsInternal, type Skill } from "./skills.js";
import { loadSkills as loadSkillsInternal, type Skill, type SkillWarning } from "./skills.js";
import {
buildSystemPrompt as buildSystemPromptInternal,
loadProjectContextFiles as loadContextFilesInternal,
@ -225,13 +225,16 @@ export async function discoverExtensions(
/**
* Discover skills from cwd and agentDir.
*/
export function discoverSkills(cwd?: string, agentDir?: string, settings?: SkillsSettings): Skill[] {
const { skills } = loadSkillsInternal({
export function discoverSkills(
cwd?: string,
agentDir?: string,
settings?: SkillsSettings,
): { skills: Skill[]; warnings: SkillWarning[] } {
return loadSkillsInternal({
...settings,
cwd: cwd ?? process.cwd(),
agentDir: agentDir ?? getDefaultAgentDir(),
});
return skills;
}
/**
@ -419,7 +422,16 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
thinkingLevel = "off";
}
const skills = options.skills ?? discoverSkills(cwd, agentDir, settingsManager.getSkillsSettings());
let skills: Skill[];
let skillWarnings: SkillWarning[];
if (options.skills !== undefined) {
skills = options.skills;
skillWarnings = [];
} else {
const discovered = discoverSkills(cwd, agentDir, settingsManager.getSkillsSettings());
skills = discovered.skills;
skillWarnings = discovered.warnings;
}
time("discoverSkills");
const contextFiles = options.contextFiles ?? discoverContextFiles(cwd, agentDir);
@ -641,13 +653,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
sessionManager.appendThinkingLevelChange(thinkingLevel);
}
// Determine skillsSettings: if options.skills was explicitly provided (even []),
// mark skills as disabled so UI doesn't re-discover them
const skillsSettings =
options.skills !== undefined
? { ...settingsManager.getSkillsSettings(), enabled: false }
: settingsManager.getSkillsSettings();
const session = new AgentSession({
agent,
sessionManager,
@ -655,7 +660,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
scopedModels: options.scopedModels,
promptTemplates: promptTemplates,
extensionRunner,
skillsSettings,
skills,
skillWarnings,
skillsSettings: settingsManager.getSkillsSettings(),
modelRegistry,
toolRegistry: wrappedToolRegistry ?? toolRegistry,
rebuildSystemPrompt,

View file

@ -43,7 +43,6 @@ import type {
import { KeybindingsManager } from "../../core/keybindings.js";
import { createCompactionSummaryMessage } from "../../core/messages.js";
import { type SessionContext, SessionManager } from "../../core/session-manager.js";
import { loadSkills } from "../../core/skills.js";
import { loadProjectContextFiles } from "../../core/system-prompt.js";
import { allTools } from "../../core/tools/index.js";
import type { TruncationResult } from "../../core/tools/truncate.js";
@ -564,24 +563,22 @@ export class InteractiveMode {
this.chatContainer.addChild(new Spacer(1));
}
// Show loaded skills
const skillsSettings = this.session.skillsSettings;
if (skillsSettings?.enabled !== false) {
const { skills, warnings: skillWarnings } = loadSkills(skillsSettings ?? {});
if (skills.length > 0) {
const skillList = skills.map((s) => theme.fg("dim", ` ${s.filePath}`)).join("\n");
this.chatContainer.addChild(new Text(theme.fg("muted", "Loaded skills:\n") + skillList, 0, 0));
this.chatContainer.addChild(new Spacer(1));
}
// Show loaded skills (already discovered by SDK)
const skills = this.session.skills;
if (skills.length > 0) {
const skillList = skills.map((s) => theme.fg("dim", ` ${s.filePath}`)).join("\n");
this.chatContainer.addChild(new Text(theme.fg("muted", "Loaded skills:\n") + skillList, 0, 0));
this.chatContainer.addChild(new Spacer(1));
}
// Show skill warnings if any
if (skillWarnings.length > 0) {
const warningList = skillWarnings
.map((w) => theme.fg("warning", ` ${w.skillPath}: ${w.message}`))
.join("\n");
this.chatContainer.addChild(new Text(theme.fg("warning", "Skill warnings:\n") + warningList, 0, 0));
this.chatContainer.addChild(new Spacer(1));
}
// Show skill warnings if any
const skillWarnings = this.session.skillWarnings;
if (skillWarnings.length > 0) {
const warningList = skillWarnings
.map((w) => theme.fg("warning", ` ${w.skillPath}: ${w.message}`))
.join("\n");
this.chatContainer.addChild(new Text(theme.fg("warning", "Skill warnings:\n") + warningList, 0, 0));
this.chatContainer.addChild(new Spacer(1));
}
const extensionRunner = this.session.extensionRunner;