From 226d2acdec9565792561258c77f1e3832b2c9f32 Mon Sep 17 00:00:00 2001 From: ozzafar <48795672+ozzafar@users.noreply.github.com> Date: Sun, 25 Oct 2026 12:27:06 +0200 Subject: [PATCH] Install debug-live on activation without agent configuration Refresh the bundled skill before inspecting desktop MCP configuration so fresh remote hosts and Dev Containers do not depend on onboarding or pre-existing configuration. Log skipped and failed installs through the extension logger. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a882e001-7e0e-4256-8a20-8eaec9c947f4 --- .../architecture/agentConfigurationManager.md | 12 +++++-- src/test/agentConfigurationManager.test.ts | 31 +++++++++++++++++++ src/test/debugSkillInstaller.test.ts | 14 +++++++++ src/utils/agentConfigurationManager.ts | 25 +++++---------- 4 files changed, 62 insertions(+), 20 deletions(-) diff --git a/docs/architecture/agentConfigurationManager.md b/docs/architecture/agentConfigurationManager.md index 2ab0109..6d8f6da 100644 --- a/docs/architecture/agentConfigurationManager.md +++ b/docs/architecture/agentConfigurationManager.md @@ -102,6 +102,12 @@ Code extension and standalone CLI use this installer. The npm package includes the complete skill tree, and `debugmcp configure` installs it while registering the selected agents. +The extension's activation migration refreshes the skill unconditionally, before +looking for agent configuration files. Fresh Dev Container/Remote extension hosts +therefore install into their own home even without desktop MCP configuration or +onboarding. Missing bundles and copy failures are reported in the DebugMCP log. +Skill delivery does not depend on resolving desktop Copilot configuration paths. + This fixes issue #105: earlier builds copied the skill next to each agent's config (e.g. `Code/User/skills/` for VS Code Copilot), a directory no harness scans, so the skill never loaded. Installing to `~/.agents/skills/` — which VS Code agent mode does scan — makes it discoverable. ## Key Code Locations @@ -118,14 +124,16 @@ This fixes issue #105: earlier builds copied the skill next to each agent's conf ## User Flow -1. Extension activates +1. Extension activates and refreshes the bundled skill in the extension host's home 2. Check if popup was previously shown 3. If not, display multi-select dialog with supported agents 4. For each selected agent, write/update config file 5. Show success message with option to open config file 6. Mark popup as shown after it is accepted or dismissed -The bundled `debug-live` skill is installed into the standard skills directories (`~/.agents/skills/`, plus `~/.copilot/skills/` when present) during step 4, so every skills-compatible harness discovers it from one shared location. +The bundled `debug-live` skill is refreshed in the standard skills directories +at step 1 and also during explicit agent setup. Installation does not depend on +accepting the popup or having an existing MCP registration. ## Commands diff --git a/src/test/agentConfigurationManager.test.ts b/src/test/agentConfigurationManager.test.ts index c51accf..c101d42 100644 --- a/src/test/agentConfigurationManager.test.ts +++ b/src/test/agentConfigurationManager.test.ts @@ -32,6 +32,37 @@ suite('AgentConfigurationManager JSON configuration', () => { await fs.promises.rm(tempDir, { recursive: true, force: true }); } }); + +}); + +suite('Activation skill installation without desktop configuration (#174)', () => { + for (const scenario of ['no agents', 'missing configuration', 'malformed configuration', 'no DebugMCP entry']) { + test(`refreshes the skill on every activation with ${scenario}`, async () => { + const directory = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'debugmcp-remote-skill-')); + try { + const manager = new AgentConfigurationManager({ extensionPath: directory } as vscode.ExtensionContext, 180, 3001); + const agents = await manager['getSupportedAgents'](); + const copilot = agents.find(agent => agent.id === 'copilot'); + assert.ok(copilot); + const configPath = path.join(directory, 'mcp.json'); + if (scenario === 'malformed configuration') { + await fs.promises.writeFile(configPath, '{invalid'); + } else if (scenario === 'no DebugMCP entry') { + await fs.promises.writeFile(configPath, '{"servers":{}}'); + } + manager['getSupportedAgents'] = async () => + scenario === 'no agents' ? [] : [{ ...copilot, configPath }]; + let installs = 0; + manager['installDebugMCPSkill'] = async () => { installs++; return directory; }; + await manager.migrateExistingConfigurations(); + await manager.migrateExistingConfigurations(); + assert.strictEqual(installs, 2); + assert.strictEqual(fs.existsSync(configPath), scenario === 'malformed configuration' || scenario === 'no DebugMCP entry'); + } finally { + await fs.promises.rm(directory, { recursive: true, force: true }); + } + }); + } }); suite('AgentConfigurationManager Claude Code configuration', () => { diff --git a/src/test/debugSkillInstaller.test.ts b/src/test/debugSkillInstaller.test.ts index fdd254a..abfb9c0 100644 --- a/src/test/debugSkillInstaller.test.ts +++ b/src/test/debugSkillInstaller.test.ts @@ -31,6 +31,13 @@ suite('debug-live skill installer', () => { ]); }); + test('fresh remote homes still get the cross-agent target without Copilot configuration (#174)', () => { + const home = path.join(directory, 'fresh-remote-home'); + assert.deepStrictEqual(getDebugSkillInstallTargets(home, path.join(home, '.copilot')), [ + path.join(home, '.agents', 'skills', 'debug-live') + ]); + }); + test('copies the complete skill and removes legacy names', async () => { const source = path.join(directory, 'bundled', 'debug-live'); const destination = path.join(directory, 'home', '.agents', 'skills', 'debug-live'); @@ -57,6 +64,13 @@ suite('debug-live skill installer', () => { ); assert.strictEqual(fs.existsSync(path.join(skillsDirectory, 'debug')), false); assert.strictEqual(fs.existsSync(path.join(skillsDirectory, 'really-debug')), false); + + await fs.promises.writeFile(path.join(source, 'SKILL.md'), '# Updated debug live\n', 'utf8'); + await installDebugSkill(source, destination); + assert.strictEqual( + await fs.promises.readFile(path.join(destination, 'SKILL.md'), 'utf8'), + '# Updated debug live\n' + ); }); test('rejects a package without the skill entry point', async () => { diff --git a/src/utils/agentConfigurationManager.ts b/src/utils/agentConfigurationManager.ts index 41ee052..7efed14 100644 --- a/src/utils/agentConfigurationManager.ts +++ b/src/utils/agentConfigurationManager.ts @@ -14,6 +14,7 @@ import { installDebugSkill } from './debugSkillInstaller'; import { selectCopilotDebugMcpHost } from '../cli/copilotMcpConfig'; +import { logger } from './logger'; export { AgentInfo, JsonAgentInfo, TomlAgentInfo } from './agentCatalog'; export interface MCPServerConfig { @@ -188,7 +189,7 @@ export class AgentConfigurationManager { const bundledSkillPath = this.getBundledSkillPath(); if (!fs.existsSync(bundledSkillPath)) { - console.warn(`Bundled debugmcp skill not found at ${bundledSkillPath}; skipping skill install`); + logger.warn(`Bundled debugmcp skill not found at ${bundledSkillPath}; skipping skill install`); return null; } @@ -196,12 +197,12 @@ export class AgentConfigurationManager { for (const destination of getDebugSkillInstallTargets()) { try { await installDebugSkill(bundledSkillPath, destination); - console.log(`Installed debugmcp skill at ${destination}`); + logger.info(`Installed debugmcp skill at ${destination}`); if (!primaryDestination) { primaryDestination = destination; } } catch (error) { - console.warn(`Failed to install debugmcp skill at ${destination}:`, error); + logger.warn(`Failed to install debugmcp skill at ${destination}:`, error); } } @@ -256,6 +257,8 @@ export class AgentConfigurationManager { * This should be called on extension activation to ensure backward compatibility */ public async migrateExistingConfigurations(): Promise { + // Remote hosts may have no desktop agent config, but still need the skill. + await this.ensureSkillRegistered(); const agents = await this.getSupportedAgents(); let migrationCount = 0; @@ -279,12 +282,6 @@ export class AgentConfigurationManager { console.log(`Successfully migrated ${agent.displayName} configuration`); } - // Back-compat: existing Codex users had only the MCP server registered. - // If DebugMCP is configured, also ensure the bundled skill is installed. - if (this.hasCodexDebugMCPSection(configContent)) { - await this.ensureSkillRegistered(); - } - continue; } @@ -332,9 +329,6 @@ export class AgentConfigurationManager { console.log(`Successfully migrated ${agent.displayName} configuration`); } - // Back-compat: existing users had only the MCP server registered. - // If DebugMCP is configured, also ensure the bundled skill is installed. - await this.ensureSkillRegistered(); } catch (error) { console.error(`Error migrating config for ${agent.name}:`, error); // Continue with other agents even if one fails @@ -358,15 +352,10 @@ export class AgentConfigurationManager { try { await this.installDebugMCPSkill(); } catch (error) { - console.warn('Failed to ensure skill installation:', error); + logger.warn('Failed to ensure skill installation:', error); } } - private hasCodexDebugMCPSection(configContent: string): boolean { - const lines = configContent.replace(/\r\n/g, '\n').split('\n'); - return lines.some(line => isCodexDebugMCPSectionHeader(line)); - } - private shouldMigrateCodexConfig(configContent: string): boolean { const normalizedConfigContent = configContent.replace(/\r\n/g, '\n'); const lines = normalizedConfigContent.split('\n');