Skip to content

feat: Add installable skills marketplace with remote registry#129

Merged
Akash-nath29 merged 3 commits intodevfrom
feature
Jan 31, 2026
Merged

feat: Add installable skills marketplace with remote registry#129
Akash-nath29 merged 3 commits intodevfrom
feature

Conversation

@Akash-nath29
Copy link
Owner

This pull request introduces a comprehensive "Skills Marketplace" system to Coderrr, allowing users to browse, install, and invoke third-party skills (plugins) that extend the agent's capabilities. The update includes both backend and frontend changes to support skill discovery, installation, management, and execution, as well as documentation and prompt updates to guide users and the AI model in using these new features.

Major new features and changes:

Skills Marketplace and Registry

  • Marketplace Client Implementation:
    Added src/skillMarketplace.js, which connects to a remote GitHub-based skill registry, supports searching, listing, downloading, and installing skills, and manages a local cache for efficiency. This module also handles skill installation, including dependency management and error recovery.

  • Skill Registry Management:
    Introduced src/skillRegistry.js to discover, validate, and load installed skills from the user's ~/.coderrr/skills/ directory. It parses skill metadata, tool docstrings, and generates a manifest of available tools for use by the agent and LLM context.

Agent and Execution Integration

  • Agent Support for Skills and Tools:
    Updated src/agent.js to load installed skills and generate a tool manifest on initialization. The agent now injects this manifest into the LLM prompt, enabling the AI to suggest or select skill tools as part of its plan. It also supports executing steps with the new invoke_skill action by calling the appropriate tool via skillRunner. [1] [2] [3] [4] [5]

  • Executor Integration:
    Updated src/executor.js to import skillRunner, preparing for skill tool execution from various contexts.

Backend and API

  • PlanStep Model Update:
    Extended the backend PlanStep model in backend/main.py to support the new invoke_skill action, including fields for skill name, tool name, and arguments, allowing the agent to plan and execute skill-based steps. [1] [2]

Documentation

  • README Marketplace Section:
    Added a new "Skills Marketplace" section to README.md, explaining how to browse, install, and use skills, and listing popular skills with descriptions. The table of contents was updated accordingly.

Most important changes:

Skills Marketplace & Registry

  • Added src/skillMarketplace.js to enable searching, listing, downloading, and installing skills from a remote registry, with cache management and dependency installation.
  • Added src/skillRegistry.js to discover, validate, and load installed skills, parse their metadata, and generate a manifest of available tools.

Agent & Execution Integration

  • Updated src/agent.js to load installed skills, inject a tool manifest into the LLM prompt, and handle the new invoke_skill action for executing skill tools. [1] [2] [3] [4] [5]
  • Updated src/executor.js to import skillRunner for executing skill tools.

Backend & API

  • Extended the PlanStep model in backend/main.py with support for the invoke_skill action and related fields, enabling the backend to plan steps that use installed skills. [1] [2]

Documentation

  • Added a "Skills Marketplace" section to README.md, guiding users on discovering and installing skills and updating the table of contents.

Copilot AI review requested due to automatic review settings January 31, 2026 23:16
@vercel
Copy link

vercel bot commented Jan 31, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
coderrr-backend Ready Ready Preview, Comment Jan 31, 2026 11:16pm

@github-actions
Copy link

🚀 Thanks for opening a Pull Request!

A maintainer will review this soon.

Meanwhile:
✔ Ensure tests are passing
✔ Link related issues
✔ Code follows the project guidelines

Your contribution helps make this project better!

@Akash-nath29 Akash-nath29 merged commit 6d33f90 into dev Jan 31, 2026
35 checks passed
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a comprehensive Skills Marketplace system that enables Coderrr to discover, install, and execute third-party Python-based skills from a remote GitHub registry. The implementation adds plugin architecture with local skill management, remote marketplace integration, and agent-level execution support.

Changes:

  • Added complete skill marketplace infrastructure with four new modules for registry management, remote downloading, Python tool execution, and CLI commands
  • Integrated skill tool execution into the agent's plan execution flow with a new invoke_skill action type
  • Extended backend API model and updated documentation to support the new marketplace feature

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 16 comments.

Show a summary per file
File Description
src/skillMarketplace.js Implements remote registry client for fetching, searching, caching, and downloading skills from GitHub
src/skillRegistry.js Manages local skill discovery, validation, metadata parsing, and tool manifest generation
src/skillRunner.js Executes Python tools in isolated subprocesses with argument conversion and timeout handling
src/skillsUI.js Provides CLI commands for skill management (install, uninstall, list, search, info)
src/agent.js Loads skills on initialization, injects tool manifest into prompts, and executes invoke_skill actions
src/executor.js Imports skillRunner module to support skill execution infrastructure
bin/coderrr.js Registers skill management commands with the CLI program
backend/main.py Extends PlanStep model to support invoke_skill action with skill, tool, and args fields
README.md Documents the Skills Marketplace feature with usage examples and popular skills table

Comment on lines +510 to +527
} else if (step.action === 'invoke_skill') {
// Execute a skill tool
ui.info(`Invoking skill tool: ${step.skill}/${step.tool}`);

const result = await skillRunner.executeTool(
step.skill,
step.tool,
step.args || {},
{ cwd: this.workingDir }
);

if (result.success) {
stepResult = `Skill ${step.skill}/${step.tool} executed successfully`;
stepSuccess = true;
ui.success(`Tool output:\n${result.output}`);
} else {
throw new Error(result.error || 'Skill tool execution failed');
}
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skill tool execution lacks user permission prompts. According to the project's architecture guidelines (CodingGuidelineID: 1000000), "ALL commands require user permission" like GitHub Copilot's model. The run_command action uses requirePermission: true (line 454), but the new invoke_skill action executes Python tools directly without asking for user confirmation. This violates the established safety pattern where users must approve potentially dangerous operations.

Consider adding a permission prompt before executing skill tools, similar to how command execution is handled. This is especially important since skills are third-party code that could perform arbitrary operations.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +179 to +183
for (const tool of skillInfo.tools) {
console.log(` Downloading ${tool}.py...`);
try {
const toolContent = await downloadFile(`${baseUrl}/tools/${tool}.py`);
fs.writeFileSync(path.join(toolsDir, `${tool}.py`), toolContent, 'utf8');
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Path traversal vulnerability in tool names during download. Tool names from skillInfo.tools are used directly in file paths without validation. A malicious registry entry with tool names like ../../../malicious could write Python files outside the tools directory.

Add validation to ensure tool names contain only safe characters (alphanumeric, hyphens, underscores) and use path.basename() to prevent path traversal. Reject tool names containing path separators or traversal sequences.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +315
/**
* Skills UI for Coderrr CLI
*
* Provides CLI commands for managing agent skills.
* Supports both local and remote (marketplace) installation.
*/

const path = require('path');
const fs = require('fs');
const chalk = require('chalk');
const inquirer = require('inquirer');
const skillRegistry = require('./skillRegistry');
const marketplace = require('./skillMarketplace');
const { checkPythonAvailable, installSkillDependencies } = require('./skillRunner');

/**
* Display list of installed skills
*/
function displaySkillsList() {
const skills = skillRegistry.loadAllSkills();

if (skills.length === 0) {
console.log(chalk.yellow('\n▲ No skills installed.'));
console.log(chalk.gray(' Install skills with: coderrr install <skill-name>'));
console.log(chalk.gray(' Browse marketplace with: coderrr market\n'));
return;
}

console.log(chalk.cyan.bold('\n├─ Installed Skills\n'));

for (const skill of skills) {
console.log(` ${chalk.white.bold(skill.name)} - ${chalk.gray(skill.description)}`);
for (const tool of skill.tools) {
const params = tool.parameters.length > 0 ? `(${tool.parameters.join(', ')})` : '()';
console.log(` ${chalk.green('•')} ${tool.name}${chalk.gray(params)}`);
}
console.log();
}
}

/**
* Install a skill from local path
* @param {string} sourcePath - Resolved path to skill folder
*/
async function installLocalSkill(sourcePath) {
if (!fs.existsSync(sourcePath)) {
console.log(chalk.red(`\n✗ Source not found: ${sourcePath}\n`));
return false;
}

const validation = skillRegistry.validateSkillStructure(sourcePath);
if (!validation.valid) {
console.log(chalk.red(`\n✗ Invalid skill: ${validation.error}`));
console.log(chalk.gray('\n Required structure:'));
console.log(chalk.gray(' <skill>/'));
console.log(chalk.gray(' ├── Skills.md'));
console.log(chalk.gray(' └── tools/'));
console.log(chalk.gray(' └── *.py\n'));
return false;
}

const skillName = path.basename(sourcePath);
const targetPath = path.join(skillRegistry.SKILLS_DIR, skillName);

if (fs.existsSync(targetPath)) {
console.log(chalk.yellow(`\n▲ Skill "${skillName}" already installed.`));
console.log(chalk.gray(` Use: coderrr uninstall ${skillName}\n`));
return false;
}

skillRegistry.ensureSkillsDir();
fs.cpSync(sourcePath, targetPath, { recursive: true });

const depsResult = await installSkillDependencies(skillName);
if (!depsResult.success && depsResult.error) {
console.log(chalk.yellow(`\n▲ Warning: ${depsResult.error}`));
}

const skill = skillRegistry.loadSkill(skillName);
console.log(chalk.green(`\n■ Skill "${skillName}" installed!`));
console.log(` Tools: ${skill.tools.map(t => t.name).join(', ')}\n`);
return true;
}

/**
* Install a skill from marketplace
* @param {string} skillName - Name of skill in registry
*/
async function installRemoteSkill(skillName) {
console.log(` Fetching "${skillName}" from marketplace...`);

const result = await marketplace.downloadSkill(skillName);

if (!result.success) {
console.log(chalk.red(`\n✗ ${result.error}\n`));
return false;
}

console.log(chalk.green(`\n■ Skill "${skillName}" installed!`));
console.log(` Tools: ${result.skill.tools.join(', ')}\n`);
return true;
}

/**
* Install a skill (auto-detect local vs remote)
* @param {string} source - Local path or skill name
*/
async function installSkill(source) {
console.log(chalk.cyan.bold('\n├─ Installing Skill\n'));

const python = await checkPythonAvailable();
if (!python.available) {
console.log(chalk.red('✗ Python not available. Skills require Python 3.8+.\n'));
return false;
}
console.log(chalk.green(` ■ Python ${python.version} found`));

if (marketplace.isLocalPath(source)) {
return await installLocalSkill(path.resolve(source));
} else {
return await installRemoteSkill(source);
}
}

/**
* Uninstall a skill
* @param {string} skillName - Name of the skill
*/
async function uninstallSkill(skillName) {
if (!skillRegistry.isSkillInstalled(skillName)) {
console.log(chalk.yellow(`\n▲ Skill "${skillName}" not installed.\n`));
return false;
}

const { confirm } = await inquirer.prompt([{
type: 'confirm',
name: 'confirm',
message: `Remove skill "${skillName}"?`,
default: false
}]);

if (!confirm) {
console.log(chalk.yellow('\n▲ Cancelled.\n'));
return false;
}

if (skillRegistry.removeSkill(skillName)) {
console.log(chalk.green(`\n■ Skill "${skillName}" uninstalled.\n`));
return true;
} else {
console.log(chalk.red(`\n✗ Failed to uninstall.\n`));
return false;
}
}

/**
* Search marketplace for skills
* @param {string} query - Search query
*/
async function searchMarketplace(query) {
console.log(chalk.cyan.bold('\n├─ Searching Marketplace\n'));

try {
const results = await marketplace.searchSkills(query);

if (results.length === 0) {
console.log(chalk.yellow(` No skills found for: "${query}"\n`));
return;
}

console.log(` Found ${results.length} skill(s):\n`);

for (const skill of results) {
const installed = skillRegistry.isSkillInstalled(skill.name);
const status = installed ? chalk.green(' [installed]') : '';

console.log(` ${chalk.cyan.bold(skill.name)}${status}`);
console.log(` ${skill.description}`);
if (skill.tags && skill.tags.length > 0) {
console.log(` ${chalk.gray('Tags: ' + skill.tags.join(', '))}`);
}
console.log();
}
} catch (error) {
console.log(chalk.red(` ✗ ${error.message}\n`));
}
}

/**
* List all available skills in marketplace
*/
async function listMarketplace() {
console.log(chalk.cyan.bold('\n├─ Available Skills (Marketplace)\n'));

try {
const skills = await marketplace.listAvailableSkills();

if (skills.length === 0) {
console.log(chalk.yellow(' No skills available in marketplace.\n'));
return;
}

for (const skill of skills) {
const installed = skillRegistry.isSkillInstalled(skill.name);
const status = installed ? chalk.green(' ✓') : '';

console.log(` ${chalk.cyan(skill.name)}${status} - ${skill.description}`);
}
console.log();
console.log(chalk.gray(` Install with: coderrr install <skill-name>\n`));
} catch (error) {
console.log(chalk.red(` ✗ ${error.message}\n`));
}
}

/**
* Show detailed info about a skill
* @param {string} skillName - Name of skill
*/
async function showSkillInfo(skillName) {
console.log(chalk.cyan.bold('\n├─ Skill Info\n'));

try {
const skill = await marketplace.getSkillInfo(skillName);

if (!skill) {
console.log(chalk.red(` Skill not found: ${skillName}\n`));
return;
}

const installed = skillRegistry.isSkillInstalled(skillName);

console.log(` ${chalk.white.bold(skill.displayName || skill.name)}\n`);
console.log(` Name: ${skill.name}`);
console.log(` Status: ${installed ? chalk.green('Installed') : chalk.yellow('Not installed')}`);
console.log(` Version: ${skill.version}`);
console.log(` Author: ${skill.author}`);
console.log(` Description: ${skill.description}`);
console.log(` Tools: ${skill.tools.join(', ')}`);
if (skill.tags && skill.tags.length > 0) {
console.log(` Tags: ${skill.tags.join(', ')}`);
}
console.log();

if (!installed) {
console.log(chalk.gray(` Install with: coderrr install ${skillName}\n`));
}
} catch (error) {
console.log(chalk.red(` ✗ ${error.message}\n`));
}
}

/**
* Register skill commands with commander program
* @param {Command} program - Commander program instance
*/
function registerSkillCommands(program) {
// List installed skills
program
.command('skills')
.description('List all installed agent skills')
.action(() => {
displaySkillsList();
});

// Install a skill (local or from marketplace)
program
.command('install <source>')
.description('Install a skill (name from marketplace or local path)')
.action(async (source) => {
await installSkill(source);
});

// Uninstall a skill
program
.command('uninstall <skill-name>')
.description('Uninstall an installed skill')
.action(async (skillName) => {
await uninstallSkill(skillName);
});

// Search marketplace
program
.command('search <query>')
.description('Search for skills in the marketplace')
.action(async (query) => {
await searchMarketplace(query);
});

// List all available skills in marketplace
program
.command('market')
.description('Browse all available skills in the marketplace')
.action(async () => {
await listMarketplace();
});

// Show skill info
program
.command('info <skill-name>')
.description('Show detailed information about a skill')
.action(async (skillName) => {
await showSkillInfo(skillName);
});
}

module.exports = {
displaySkillsList,
installSkill,
uninstallSkill,
searchMarketplace,
listMarketplace,
showSkillInfo,
registerSkillCommands
};
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test coverage gap for new skill marketplace functionality. The repository has comprehensive automated testing using Jest (as evidenced by existing test files like test/doctor.test.js, test/insights.test.js, and test/recipes.test.js), but the newly added skill marketplace modules lack any test coverage.

Consider adding tests for:

  • skillMarketplace.js: Registry fetching, caching, skill download/installation, error handling
  • skillRegistry.js: Skill discovery, validation, metadata parsing, manifest generation
  • skillRunner.js: Tool execution, argument building, Python availability checking, timeout handling
  • skillsUI.js: Command registration and execution flows

This is especially important given the security-sensitive nature of downloading and executing third-party code.

Copilot uses AI. Check for mistakes.
Comment on lines +178 to +188
// Download each tool
for (const tool of skillInfo.tools) {
console.log(` Downloading ${tool}.py...`);
try {
const toolContent = await downloadFile(`${baseUrl}/tools/${tool}.py`);
fs.writeFileSync(path.join(toolsDir, `${tool}.py`), toolContent, 'utf8');
} catch (e) {
console.warn(` Warning: Could not download ${tool}.py`);
}
}

Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent error handling in downloadSkill function. When individual tool downloads fail (line 184-186), the function logs a warning but continues. However, if all tools fail to download, the skill would be installed with no functional tools, but the function would still return success. This could lead to a confusing user experience where a skill appears installed but doesn't work.

Consider tracking how many tools were successfully downloaded and returning an error or warning if no tools were successfully downloaded. Alternatively, document this behavior clearly so users understand partial installations are possible.

Suggested change
// Download each tool
for (const tool of skillInfo.tools) {
console.log(` Downloading ${tool}.py...`);
try {
const toolContent = await downloadFile(`${baseUrl}/tools/${tool}.py`);
fs.writeFileSync(path.join(toolsDir, `${tool}.py`), toolContent, 'utf8');
} catch (e) {
console.warn(` Warning: Could not download ${tool}.py`);
}
}
// Download each tool
const totalTools = Array.isArray(skillInfo.tools) ? skillInfo.tools.length : 0;
let downloadedTools = 0;
for (const tool of skillInfo.tools) {
console.log(` Downloading ${tool}.py...`);
try {
const toolContent = await downloadFile(`${baseUrl}/tools/${tool}.py`);
fs.writeFileSync(path.join(toolsDir, `${tool}.py`), toolContent, 'utf8');
downloadedTools += 1;
} catch (e) {
console.warn(` Warning: Could not download ${tool}.py`);
}
}
// If no tools were successfully downloaded, treat as a failed installation
if (totalTools > 0 && downloadedTools === 0) {
throw new Error(`No tools for skill "${skillName}" could be downloaded`);
}
// If some, but not all, tools were downloaded, warn about partial install
if (totalTools > 0 && downloadedTools > 0 && downloadedTools < totalTools) {
console.warn(
` Warning: Only ${downloadedTools}/${totalTools} tools were downloaded for skill "${skillName}".`
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +470 to +473

// Load installed agent skills for tool invocation
this.installedSkills = skillRegistry.loadAllSkills();
this.toolManifest = skillRegistry.generateToolManifest();
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant skill loading during execution. Lines 471-473 reload installed skills and regenerate the tool manifest inside the run_command execution block, but this is already done during agent initialization (lines 64-66). This redundant loading serves no purpose as it's only executed when this.runningProcesses is undefined, which is unlikely after proper initialization.

The comment says "Load installed agent skills for tool invocation" but this is not related to command execution. This appears to be leftover code or a copy-paste error. Consider removing this redundant code block as skills are already loaded at initialization.

Suggested change
// Load installed agent skills for tool invocation
this.installedSkills = skillRegistry.loadAllSkills();
this.toolManifest = skillRegistry.generateToolManifest();

Copilot uses AI. Check for mistakes.
Comment on lines +48 to +58
const proc = spawn(pythonCommand, [toolPath, ...argsList], {
cwd,
env: {
...process.env,
CODERRR_SKILL: skillName,
CODERRR_TOOL: toolName,
CODERRR_CWD: cwd
},
shell: true,
timeout
});
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent use of shell option with spawn. The code passes shell: true to spawn (line 56) when executing Python scripts, which is unnecessary and introduces security risks. When spawn is given an array of arguments (as done here with [toolPath, ...argsList]), the shell option is not needed for basic execution and opens up potential command injection vulnerabilities if any arguments are not properly sanitized.

Python script execution doesn't require a shell. Consider removing shell: true unless there's a specific reason it's needed (which should be documented). This would improve security and performance. If shell features are required, document why and ensure all inputs are thoroughly validated.

Copilot uses AI. Check for mistakes.
Comment on lines +155 to +183
const skillDir = path.join(skillRegistry.SKILLS_DIR, skillName);
const toolsDir = path.join(skillDir, 'tools');
const baseUrl = skillInfo.download_url;

try {
// Create directories
skillRegistry.ensureSkillsDir();
fs.mkdirSync(toolsDir, { recursive: true });

// Download Skills.md
console.log(` Downloading Skills.md...`);
const skillsMd = await downloadFile(`${baseUrl}/Skills.md`);
fs.writeFileSync(path.join(skillDir, 'Skills.md'), skillsMd, 'utf8');

// Try to download requirements.txt (optional)
try {
const requirements = await downloadFile(`${baseUrl}/requirements.txt`);
fs.writeFileSync(path.join(skillDir, 'requirements.txt'), requirements, 'utf8');
console.log(` Found requirements.txt`);
} catch (e) {
// requirements.txt is optional, ignore
}

// Download each tool
for (const tool of skillInfo.tools) {
console.log(` Downloading ${tool}.py...`);
try {
const toolContent = await downloadFile(`${baseUrl}/tools/${tool}.py`);
fs.writeFileSync(path.join(toolsDir, `${tool}.py`), toolContent, 'utf8');
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing URL validation allows arbitrary file downloads. The downloadSkill function constructs download URLs from skillInfo.download_url (from the remote registry) without validating that the URL is from a trusted domain. While the default REGISTRY_URL is GitHub, a compromised or malicious registry could return arbitrary URLs, potentially leading to SSRF attacks or downloading malicious content.

Add validation to ensure download_url matches expected patterns (e.g., starts with the expected GitHub raw content URL pattern). Consider implementing a whitelist of allowed domains or URL patterns for security.

Copilot uses AI. Check for mistakes.
Comment on lines +92 to +104
// Timeout handler
setTimeout(() => {
if (resolved) return;
resolved = true;

proc.kill('SIGTERM');
resolve({
success: false,
output: stdout.trim(),
error: `Tool execution timed out after ${timeout}ms`,
exitCode: 124
});
}, timeout);
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Race condition in timeout handling. The timeout handler at line 93-104 uses a separate setTimeout that runs concurrently with the process's 'close' and 'error' event handlers. If the process completes naturally just as the timeout fires, there's a race condition where both handlers could attempt to resolve the promise. While the resolved flag prevents double resolution, the proc.kill('SIGTERM') on line 97 could still be called on an already-completed process, potentially causing an uncaught error.

The spawn timeout option at line 57 should already handle timeout automatically, making the manual setTimeout redundant. Consider removing the manual timeout handler (lines 93-104) and relying solely on the spawn timeout option, which handles this more cleanly. If manual handling is needed, ensure proc.kill() is wrapped in a try-catch.

Suggested change
// Timeout handler
setTimeout(() => {
if (resolved) return;
resolved = true;
proc.kill('SIGTERM');
resolve({
success: false,
output: stdout.trim(),
error: `Tool execution timed out after ${timeout}ms`,
exitCode: 124
});
}, timeout);

Copilot uses AI. Check for mistakes.
Comment on lines +210 to +213
const proc = spawn(pythonCommand, ['-m', 'pip', 'install', '-r', requirementsPath], {
shell: true,
timeout: 120000 // 2 minute timeout for pip install
});
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Insufficient timeout for pip install operations. The timeout for pip install -r requirements.txt is set to 120000ms (2 minutes) at line 212. For skills with many or large dependencies, this timeout may be insufficient, especially on slower internet connections or when packages need to be compiled. This could lead to partial installations or false failures.

Consider either increasing the timeout (e.g., to 5-10 minutes) or making it configurable. Also consider informing the user when dependency installation is taking a long time, rather than silently timing out.

Copilot uses AI. Check for mistakes.
const path = require('path');
const os = require('os');
const ui = require('./ui');
const skillRunner = require('./skillRunner');
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable skillRunner.

Suggested change
const skillRunner = require('./skillRunner');

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant