fix(coding-agent): --no-skills flag not preventing skills from loading (#577)

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

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()

Co-authored-by: Carlos Villela <cv@lixo.org>
This commit is contained in:
Carlos Villela 2026-01-08 14:40:05 -08:00 committed by GitHub
parent ec1ee7ddc6
commit c865ec1d19
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 88 additions and 1 deletions

View file

@ -641,6 +641,13 @@ 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,
@ -648,7 +655,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
scopedModels: options.scopedModels,
promptTemplates: promptTemplates,
extensionRunner,
skillsSettings: settingsManager.getSkillsSettings(),
skillsSettings,
modelRegistry,
toolRegistry: wrappedToolRegistry ?? toolRegistry,
rebuildSystemPrompt,

View file

@ -0,0 +1,80 @@
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { createAgentSession } from "../src/core/sdk.js";
import { SessionManager } from "../src/core/session-manager.js";
describe("createAgentSession skills option", () => {
let tempDir: string;
let skillsDir: string;
beforeEach(() => {
tempDir = join(tmpdir(), `pi-sdk-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
skillsDir = join(tempDir, "skills", "test-skill");
mkdirSync(skillsDir, { recursive: true });
// Create a test skill
writeFileSync(
join(skillsDir, "SKILL.md"),
`---
name: test-skill
description: A test skill for SDK tests.
---
# Test Skill
This is a test skill.
`,
);
});
afterEach(() => {
if (tempDir) {
rmSync(tempDir, { recursive: true, force: true });
}
});
it("should discover skills by default", async () => {
const { session } = await createAgentSession({
cwd: tempDir,
agentDir: tempDir,
sessionManager: SessionManager.inMemory(),
});
// skillsSettings.enabled should be true (from default settings)
expect(session.skillsSettings?.enabled).toBe(true);
});
it("should disable skills in skillsSettings when options.skills is empty array", async () => {
const { session } = await createAgentSession({
cwd: tempDir,
agentDir: tempDir,
sessionManager: SessionManager.inMemory(),
skills: [], // Explicitly empty - like --no-skills
});
// skillsSettings.enabled should be false so UI doesn't re-discover
expect(session.skillsSettings?.enabled).toBe(false);
});
it("should disable skills in skillsSettings when options.skills is provided with skills", async () => {
const { session } = await createAgentSession({
cwd: tempDir,
agentDir: tempDir,
sessionManager: SessionManager.inMemory(),
skills: [
{
name: "custom-skill",
description: "A custom skill",
filePath: "/fake/path/SKILL.md",
baseDir: "/fake/path",
source: "custom",
},
],
});
// skillsSettings.enabled should be false because skills were explicitly provided
expect(session.skillsSettings?.enabled).toBe(false);
});
});