contextos-agents 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/AGENTS.md +421 -0
- package/.agents/adapters/aider/export.js +137 -0
- package/.agents/adapters/claude/export.js +55 -0
- package/.agents/adapters/copilot/export.js +107 -0
- package/.agents/adapters/cursor/export.js +210 -0
- package/.agents/adapters/gemini/export.js +115 -0
- package/.agents/adapters/shared.js +119 -0
- package/.agents/adapters/zed/export.js +111 -0
- package/.agents/core/profiles/backend.yaml +47 -0
- package/.agents/core/profiles/enterprise.yaml +46 -0
- package/.agents/core/profiles/frontend.yaml +46 -0
- package/.agents/core/profiles/hackathon.yaml +45 -0
- package/.agents/core/profiles/mvp.yaml +43 -0
- package/.agents/core/profiles/startup.yaml +48 -0
- package/.agents/core/skills/adapters/EXAMPLES.md +19 -0
- package/.agents/core/skills/adapters/SKILL.md +105 -0
- package/.agents/core/skills/adapters/TROUBLESHOOTING.md +7 -0
- package/.agents/core/skills/adapters/VALIDATION.json +12 -0
- package/.agents/core/skills/adapters/skill.yaml +10 -0
- package/.agents/core/skills/architecture-diagrams/SKILL.md +108 -0
- package/.agents/core/skills/architecture-diagrams/VALIDATION.json +12 -0
- package/.agents/core/skills/architecture-diagrams/skill.yaml +8 -0
- package/.agents/core/skills/brutalist-design/SKILL.md +150 -0
- package/.agents/core/skills/brutalist-design/VALIDATION.json +12 -0
- package/.agents/core/skills/brutalist-design/skill.yaml +8 -0
- package/.agents/core/skills/context-manager/EXAMPLES.md +19 -0
- package/.agents/core/skills/context-manager/SKILL.md +126 -0
- package/.agents/core/skills/context-manager/TROUBLESHOOTING.md +7 -0
- package/.agents/core/skills/context-manager/VALIDATION.json +12 -0
- package/.agents/core/skills/context-manager/skill.yaml +10 -0
- package/.agents/core/skills/context-os/EXAMPLES.md +21 -0
- package/.agents/core/skills/context-os/SKILL.md +169 -0
- package/.agents/core/skills/context-os/TROUBLESHOOTING.md +7 -0
- package/.agents/core/skills/context-os/VALIDATION.json +12 -0
- package/.agents/core/skills/context-os/packs.yaml +59 -0
- package/.agents/core/skills/context-os/references/context-rules.md +68 -0
- package/.agents/core/skills/context-os/references/pipeline.md +119 -0
- package/.agents/core/skills/context-os/references/project-graph.md +103 -0
- package/.agents/core/skills/context-os/rules.yaml +135 -0
- package/.agents/core/skills/context-os/skill.yaml +10 -0
- package/.agents/core/skills/database/EXAMPLES.md +74 -0
- package/.agents/core/skills/database/SKILL.md +101 -0
- package/.agents/core/skills/database/TROUBLESHOOTING.md +18 -0
- package/.agents/core/skills/database/VALIDATION.json +11 -0
- package/.agents/core/skills/database/skill.yaml +25 -0
- package/.agents/core/skills/ddd/EXAMPLES.md +42 -0
- package/.agents/core/skills/ddd/SKILL.md +247 -0
- package/.agents/core/skills/ddd/TROUBLESHOOTING.md +19 -0
- package/.agents/core/skills/ddd/VALIDATION.json +12 -0
- package/.agents/core/skills/ddd/ddd.md +178 -0
- package/.agents/core/skills/ddd/skill.yaml +10 -0
- package/.agents/core/skills/decisions/EXAMPLES.md +35 -0
- package/.agents/core/skills/decisions/SKILL.md +90 -0
- package/.agents/core/skills/decisions/TROUBLESHOOTING.md +13 -0
- package/.agents/core/skills/decisions/VALIDATION.json +12 -0
- package/.agents/core/skills/decisions/skill.yaml +10 -0
- package/.agents/core/skills/docker/EXAMPLES.md +56 -0
- package/.agents/core/skills/docker/SKILL.md +63 -0
- package/.agents/core/skills/docker/TROUBLESHOOTING.md +18 -0
- package/.agents/core/skills/docker/VALIDATION.json +11 -0
- package/.agents/core/skills/docker/skill.yaml +23 -0
- package/.agents/core/skills/engineering-workflow/EXAMPLES.md +57 -0
- package/.agents/core/skills/engineering-workflow/SKILL.md +343 -0
- package/.agents/core/skills/engineering-workflow/TROUBLESHOOTING.md +19 -0
- package/.agents/core/skills/engineering-workflow/VALIDATION.json +12 -0
- package/.agents/core/skills/engineering-workflow/skill.yaml +13 -0
- package/.agents/core/skills/fastapi/EXAMPLES.md +36 -0
- package/.agents/core/skills/fastapi/SKILL.md +148 -0
- package/.agents/core/skills/fastapi/TROUBLESHOOTING.md +19 -0
- package/.agents/core/skills/fastapi/VALIDATION.json +12 -0
- package/.agents/core/skills/fastapi/fastapi.md +112 -0
- package/.agents/core/skills/fastapi/skill.yaml +10 -0
- package/.agents/core/skills/gemini-precision/SKILL.md +169 -0
- package/.agents/core/skills/gemini-precision/VALIDATION.json +12 -0
- package/.agents/core/skills/gemini-precision/skill.yaml +9 -0
- package/.agents/core/skills/generators/EXAMPLES.md +19 -0
- package/.agents/core/skills/generators/SKILL.md +112 -0
- package/.agents/core/skills/generators/TROUBLESHOOTING.md +7 -0
- package/.agents/core/skills/generators/VALIDATION.json +12 -0
- package/.agents/core/skills/generators/skill.yaml +10 -0
- package/.agents/core/skills/generators/templates/API.md +77 -0
- package/.agents/core/skills/generators/templates/ARCHITECTURE.md +70 -0
- package/.agents/core/skills/generators/templates/DATABASE.md +42 -0
- package/.agents/core/skills/generators/templates/DECISION.md +46 -0
- package/.agents/core/skills/generators/templates/PRD.md +67 -0
- package/.agents/core/skills/generators/templates/PROJECT_GRAPH.md +56 -0
- package/.agents/core/skills/generators/templates/ROADMAP.md +51 -0
- package/.agents/core/skills/generators/templates/TASKS.md +43 -0
- package/.agents/core/skills/generators/templates/UI.md +73 -0
- package/.agents/core/skills/graphify/EXAMPLES.md +73 -0
- package/.agents/core/skills/graphify/SKILL.md +130 -0
- package/.agents/core/skills/graphify/VALIDATION.json +12 -0
- package/.agents/core/skills/graphify/skill.yaml +13 -0
- package/.agents/core/skills/gstack-roles/EXAMPLES.md +23 -0
- package/.agents/core/skills/gstack-roles/SKILL.md +152 -0
- package/.agents/core/skills/gstack-roles/TROUBLESHOOTING.md +13 -0
- package/.agents/core/skills/gstack-roles/VALIDATION.json +12 -0
- package/.agents/core/skills/gstack-roles/skill.yaml +12 -0
- package/.agents/core/skills/impeccable-design/EXAMPLES.md +26 -0
- package/.agents/core/skills/impeccable-design/SKILL.md +201 -0
- package/.agents/core/skills/impeccable-design/TROUBLESHOOTING.md +19 -0
- package/.agents/core/skills/impeccable-design/VALIDATION.json +12 -0
- package/.agents/core/skills/impeccable-design/skill.yaml +14 -0
- package/.agents/core/skills/interview-me/SKILL.md +97 -0
- package/.agents/core/skills/interview-me/VALIDATION.json +12 -0
- package/.agents/core/skills/interview-me/skill.yaml +8 -0
- package/.agents/core/skills/microservices/EXAMPLES.md +38 -0
- package/.agents/core/skills/microservices/SKILL.md +164 -0
- package/.agents/core/skills/microservices/TROUBLESHOOTING.md +19 -0
- package/.agents/core/skills/microservices/VALIDATION.json +12 -0
- package/.agents/core/skills/microservices/microservices.md +119 -0
- package/.agents/core/skills/microservices/skill.yaml +10 -0
- package/.agents/core/skills/minimalist-design/SKILL.md +113 -0
- package/.agents/core/skills/minimalist-design/VALIDATION.json +12 -0
- package/.agents/core/skills/minimalist-design/skill.yaml +8 -0
- package/.agents/core/skills/nestjs/EXAMPLES.md +40 -0
- package/.agents/core/skills/nestjs/SKILL.md +139 -0
- package/.agents/core/skills/nestjs/TROUBLESHOOTING.md +19 -0
- package/.agents/core/skills/nestjs/VALIDATION.json +12 -0
- package/.agents/core/skills/nestjs/nestjs.md +103 -0
- package/.agents/core/skills/nestjs/skill.yaml +10 -0
- package/.agents/core/skills/nextjs/EXAMPLES.md +40 -0
- package/.agents/core/skills/nextjs/SKILL.md +163 -0
- package/.agents/core/skills/nextjs/TROUBLESHOOTING.md +19 -0
- package/.agents/core/skills/nextjs/VALIDATION.json +12 -0
- package/.agents/core/skills/nextjs/nextjs.md +67 -0
- package/.agents/core/skills/nextjs/skill.yaml +10 -0
- package/.agents/core/skills/node/EXAMPLES.md +80 -0
- package/.agents/core/skills/node/SKILL.md +128 -0
- package/.agents/core/skills/node/TROUBLESHOOTING.md +19 -0
- package/.agents/core/skills/node/VALIDATION.json +12 -0
- package/.agents/core/skills/node/node.md +87 -0
- package/.agents/core/skills/node/skill.yaml +10 -0
- package/.agents/core/skills/performance/EXAMPLES.md +30 -0
- package/.agents/core/skills/performance/SKILL.md +75 -0
- package/.agents/core/skills/performance/TROUBLESHOOTING.md +19 -0
- package/.agents/core/skills/performance/VALIDATION.json +12 -0
- package/.agents/core/skills/performance/performance.md +52 -0
- package/.agents/core/skills/performance/skill.yaml +10 -0
- package/.agents/core/skills/ponytail-mindset/EXAMPLES.md +45 -0
- package/.agents/core/skills/ponytail-mindset/SKILL.md +193 -0
- package/.agents/core/skills/ponytail-mindset/TROUBLESHOOTING.md +19 -0
- package/.agents/core/skills/ponytail-mindset/VALIDATION.json +12 -0
- package/.agents/core/skills/ponytail-mindset/skill.yaml +14 -0
- package/.agents/core/skills/react/EXAMPLES.md +79 -0
- package/.agents/core/skills/react/SKILL.md +132 -0
- package/.agents/core/skills/react/TROUBLESHOOTING.md +19 -0
- package/.agents/core/skills/react/VALIDATION.json +12 -0
- package/.agents/core/skills/react/react.md +93 -0
- package/.agents/core/skills/react/skill.yaml +10 -0
- package/.agents/core/skills/react-best-practices/SKILL.md +155 -0
- package/.agents/core/skills/react-best-practices/VALIDATION.json +12 -0
- package/.agents/core/skills/react-best-practices/skill.yaml +10 -0
- package/.agents/core/skills/redesign-audit/SKILL.md +117 -0
- package/.agents/core/skills/redesign-audit/VALIDATION.json +12 -0
- package/.agents/core/skills/redesign-audit/skill.yaml +8 -0
- package/.agents/core/skills/security/EXAMPLES.md +64 -0
- package/.agents/core/skills/security/SKILL.md +158 -0
- package/.agents/core/skills/security/TROUBLESHOOTING.md +19 -0
- package/.agents/core/skills/security/VALIDATION.json +12 -0
- package/.agents/core/skills/security/security.md +106 -0
- package/.agents/core/skills/security/skill.yaml +10 -0
- package/.agents/core/skills/soft-design/SKILL.md +108 -0
- package/.agents/core/skills/soft-design/VALIDATION.json +12 -0
- package/.agents/core/skills/soft-design/skill.yaml +8 -0
- package/.agents/core/skills/state-management/EXAMPLES.md +56 -0
- package/.agents/core/skills/state-management/SKILL.md +48 -0
- package/.agents/core/skills/state-management/TROUBLESHOOTING.md +18 -0
- package/.agents/core/skills/state-management/VALIDATION.json +11 -0
- package/.agents/core/skills/state-management/skill.yaml +22 -0
- package/.agents/core/skills/subagent-orchestrator/SKILL.md +100 -0
- package/.agents/core/skills/subagent-orchestrator/VALIDATION.json +12 -0
- package/.agents/core/skills/subagent-orchestrator/skill.yaml +8 -0
- package/.agents/core/skills/system-design/EXAMPLES.md +75 -0
- package/.agents/core/skills/system-design/SKILL.md +419 -0
- package/.agents/core/skills/system-design/TROUBLESHOOTING.md +19 -0
- package/.agents/core/skills/system-design/VALIDATION.json +12 -0
- package/.agents/core/skills/system-design/skill.yaml +13 -0
- package/.agents/core/skills/system-design/system-design.md +112 -0
- package/.agents/core/skills/testing/EXAMPLES.md +71 -0
- package/.agents/core/skills/testing/SKILL.md +70 -0
- package/.agents/core/skills/testing/TROUBLESHOOTING.md +18 -0
- package/.agents/core/skills/testing/VALIDATION.json +11 -0
- package/.agents/core/skills/testing/skill.yaml +26 -0
- package/.agents/core/skills/typescript/EXAMPLES.md +64 -0
- package/.agents/core/skills/typescript/SKILL.md +112 -0
- package/.agents/core/skills/typescript/TROUBLESHOOTING.md +19 -0
- package/.agents/core/skills/typescript/VALIDATION.json +12 -0
- package/.agents/core/skills/typescript/skill.yaml +10 -0
- package/.agents/core/skills/typescript/typescript.md +71 -0
- package/.agents/core/skills/ui-design/EXAMPLES.md +21 -0
- package/.agents/core/skills/ui-design/SKILL.md +124 -0
- package/.agents/core/skills/ui-design/TROUBLESHOOTING.md +19 -0
- package/.agents/core/skills/ui-design/VALIDATION.json +12 -0
- package/.agents/core/skills/ui-design/skill.yaml +10 -0
- package/.agents/core/skills/ui-design/ui.md +88 -0
- package/.agents/core/skills/ui-ux-pro/EXAMPLES.md +62 -0
- package/.agents/core/skills/ui-ux-pro/SKILL.md +375 -0
- package/.agents/core/skills/ui-ux-pro/TROUBLESHOOTING.md +19 -0
- package/.agents/core/skills/ui-ux-pro/VALIDATION.json +12 -0
- package/.agents/core/skills/ui-ux-pro/skill.yaml +13 -0
- package/.agents/core/skills/ux-design/EXAMPLES.md +36 -0
- package/.agents/core/skills/ux-design/SKILL.md +116 -0
- package/.agents/core/skills/ux-design/TROUBLESHOOTING.md +19 -0
- package/.agents/core/skills/ux-design/VALIDATION.json +12 -0
- package/.agents/core/skills/ux-design/skill.yaml +10 -0
- package/.agents/core/skills/ux-design/ux.md +80 -0
- package/.agents/core/skills/vercel-optimize/SKILL.md +83 -0
- package/.agents/core/skills/vercel-optimize/VALIDATION.json +12 -0
- package/.agents/core/skills/vercel-optimize/skill.yaml +10 -0
- package/.agents/core/skills/web-accessibility/EXAMPLES.md +39 -0
- package/.agents/core/skills/web-accessibility/SKILL.md +170 -0
- package/.agents/core/skills/web-accessibility/TROUBLESHOOTING.md +19 -0
- package/.agents/core/skills/web-accessibility/VALIDATION.json +12 -0
- package/.agents/core/skills/web-accessibility/accessibility.md +63 -0
- package/.agents/core/skills/web-accessibility/skill.yaml +10 -0
- package/.agents/ctx.js +370 -0
- package/.agents/generated/claude/skills/adapters/SKILL.md +126 -0
- package/.agents/generated/claude/skills/architecture-diagrams/SKILL.md +101 -0
- package/.agents/generated/claude/skills/brutalist-design/SKILL.md +145 -0
- package/.agents/generated/claude/skills/context-manager/SKILL.md +147 -0
- package/.agents/generated/claude/skills/context-os/SKILL.md +191 -0
- package/.agents/generated/claude/skills/database/SKILL.md +191 -0
- package/.agents/generated/claude/skills/ddd/SKILL.md +305 -0
- package/.agents/generated/claude/skills/decisions/SKILL.md +134 -0
- package/.agents/generated/claude/skills/docker/SKILL.md +135 -0
- package/.agents/generated/claude/skills/engineering-workflow/SKILL.md +414 -0
- package/.agents/generated/claude/skills/fastapi/SKILL.md +200 -0
- package/.agents/generated/claude/skills/gemini-precision/SKILL.md +161 -0
- package/.agents/generated/claude/skills/generators/SKILL.md +133 -0
- package/.agents/generated/claude/skills/graphify/SKILL.md +198 -0
- package/.agents/generated/claude/skills/gstack-roles/SKILL.md +184 -0
- package/.agents/generated/claude/skills/impeccable-design/SKILL.md +241 -0
- package/.agents/generated/claude/skills/interview-me/SKILL.md +90 -0
- package/.agents/generated/claude/skills/microservices/SKILL.md +218 -0
- package/.agents/generated/claude/skills/minimalist-design/SKILL.md +108 -0
- package/.agents/generated/claude/skills/nestjs/SKILL.md +195 -0
- package/.agents/generated/claude/skills/nextjs/SKILL.md +219 -0
- package/.agents/generated/claude/skills/node/SKILL.md +224 -0
- package/.agents/generated/claude/skills/performance/SKILL.md +121 -0
- package/.agents/generated/claude/skills/ponytail-mindset/SKILL.md +252 -0
- package/.agents/generated/claude/skills/react/SKILL.md +227 -0
- package/.agents/generated/claude/skills/react-best-practices/SKILL.md +146 -0
- package/.agents/generated/claude/skills/redesign-audit/SKILL.md +112 -0
- package/.agents/generated/claude/skills/security/SKILL.md +237 -0
- package/.agents/generated/claude/skills/soft-design/SKILL.md +103 -0
- package/.agents/generated/claude/skills/state-management/SKILL.md +120 -0
- package/.agents/generated/claude/skills/subagent-orchestrator/SKILL.md +93 -0
- package/.agents/generated/claude/skills/system-design/SKILL.md +507 -0
- package/.agents/generated/claude/skills/testing/SKILL.md +157 -0
- package/.agents/generated/claude/skills/typescript/SKILL.md +192 -0
- package/.agents/generated/claude/skills/ui-design/SKILL.md +161 -0
- package/.agents/generated/claude/skills/ui-ux-pro/SKILL.md +451 -0
- package/.agents/generated/claude/skills/ux-design/SKILL.md +168 -0
- package/.agents/generated/claude/skills/vercel-optimize/SKILL.md +76 -0
- package/.agents/generated/claude/skills/web-accessibility/SKILL.md +225 -0
- package/.agents/generated/gemini/skills/adapters/SKILL.md +135 -0
- package/.agents/generated/gemini/skills/architecture-diagrams/SKILL.md +107 -0
- package/.agents/generated/gemini/skills/brutalist-design/SKILL.md +151 -0
- package/.agents/generated/gemini/skills/context-manager/SKILL.md +156 -0
- package/.agents/generated/gemini/skills/context-os/SKILL.md +200 -0
- package/.agents/generated/gemini/skills/database/SKILL.md +200 -0
- package/.agents/generated/gemini/skills/ddd/SKILL.md +314 -0
- package/.agents/generated/gemini/skills/decisions/SKILL.md +143 -0
- package/.agents/generated/gemini/skills/docker/SKILL.md +144 -0
- package/.agents/generated/gemini/skills/engineering-workflow/SKILL.md +423 -0
- package/.agents/generated/gemini/skills/fastapi/SKILL.md +209 -0
- package/.agents/generated/gemini/skills/gemini-precision/SKILL.md +167 -0
- package/.agents/generated/gemini/skills/generators/SKILL.md +142 -0
- package/.agents/generated/gemini/skills/graphify/SKILL.md +205 -0
- package/.agents/generated/gemini/skills/gstack-roles/SKILL.md +193 -0
- package/.agents/generated/gemini/skills/impeccable-design/SKILL.md +250 -0
- package/.agents/generated/gemini/skills/interview-me/SKILL.md +96 -0
- package/.agents/generated/gemini/skills/microservices/SKILL.md +227 -0
- package/.agents/generated/gemini/skills/minimalist-design/SKILL.md +114 -0
- package/.agents/generated/gemini/skills/nestjs/SKILL.md +204 -0
- package/.agents/generated/gemini/skills/nextjs/SKILL.md +298 -0
- package/.agents/generated/gemini/skills/node/SKILL.md +323 -0
- package/.agents/generated/gemini/skills/performance/SKILL.md +185 -0
- package/.agents/generated/gemini/skills/ponytail-mindset/SKILL.md +261 -0
- package/.agents/generated/gemini/skills/react/SKILL.md +332 -0
- package/.agents/generated/gemini/skills/react-best-practices/SKILL.md +152 -0
- package/.agents/generated/gemini/skills/redesign-audit/SKILL.md +118 -0
- package/.agents/generated/gemini/skills/security/SKILL.md +355 -0
- package/.agents/generated/gemini/skills/soft-design/SKILL.md +109 -0
- package/.agents/generated/gemini/skills/state-management/SKILL.md +129 -0
- package/.agents/generated/gemini/skills/subagent-orchestrator/SKILL.md +99 -0
- package/.agents/generated/gemini/skills/system-design/SKILL.md +631 -0
- package/.agents/generated/gemini/skills/testing/SKILL.md +166 -0
- package/.agents/generated/gemini/skills/typescript/SKILL.md +275 -0
- package/.agents/generated/gemini/skills/ui-design/SKILL.md +170 -0
- package/.agents/generated/gemini/skills/ui-ux-pro/SKILL.md +460 -0
- package/.agents/generated/gemini/skills/ux-design/SKILL.md +177 -0
- package/.agents/generated/gemini/skills/vercel-optimize/SKILL.md +82 -0
- package/.agents/generated/gemini/skills/web-accessibility/SKILL.md +300 -0
- package/.agents/mcp/runtime.py +454 -0
- package/.agents/mcp/server.mjs +189373 -0
- package/.agents/plugins.js +574 -0
- package/.agents/profiles.js +267 -0
- package/.agents/resolver.js +336 -0
- package/.agents/skills-index.json +200 -0
- package/.agents/skills.json +6 -0
- package/.agents/validate.js +500 -0
- package/LICENSE +21 -0
- package/README.md +415 -0
- package/benchmarks/gemini-issues.js +533 -0
- package/bin/index.js +458 -0
- package/package.json +70 -0
- package/registry.json +53 -0
- package/registry.schema.json +32 -0
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* .agents/plugins.js
|
|
3
|
+
* ContextOS — Skill Plugin Manager
|
|
4
|
+
*
|
|
5
|
+
* Installs third-party skills from:
|
|
6
|
+
* • npm packages (contextos-skill-*)
|
|
7
|
+
* • GitHub repos (owner/repo or owner/repo/path/to/skill)
|
|
8
|
+
*
|
|
9
|
+
* Usage (via ctx.js):
|
|
10
|
+
* node .agents/ctx.js skill add username/my-skill
|
|
11
|
+
* node .agents/ctx.js skill add my-npm-package
|
|
12
|
+
* node .agents/ctx.js skill remove my-skill
|
|
13
|
+
* node .agents/ctx.js skill list
|
|
14
|
+
* node .agents/ctx.js skill search <query>
|
|
15
|
+
*
|
|
16
|
+
* Or via npx:
|
|
17
|
+
* npx contextos-agents --add-skill username/my-skill
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
'use strict';
|
|
21
|
+
|
|
22
|
+
const fs = require('fs');
|
|
23
|
+
const os = require('os');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
const https = require('https');
|
|
26
|
+
const crypto = require('crypto');
|
|
27
|
+
const { execFileSync } = require('child_process');
|
|
28
|
+
|
|
29
|
+
// ── Paths ─────────────────────────────────────────────────────────────────────
|
|
30
|
+
const ROOT = process.cwd();
|
|
31
|
+
const AGENTS_DIR = path.join(ROOT, '.agents');
|
|
32
|
+
const CORE_SKILLS = path.join(AGENTS_DIR, 'core', 'skills');
|
|
33
|
+
const PLUGINS_DIR = path.join(AGENTS_DIR, 'plugins'); // installed third-party skills
|
|
34
|
+
const PLUGINS_JSON = path.join(AGENTS_DIR, 'plugins.json'); // local lock file
|
|
35
|
+
const REGISTRY_URL = 'https://raw.githubusercontent.com/kok-o/contextos-agents/main/registry.json';
|
|
36
|
+
const MAX_DOWNLOAD_BYTES = 5 * 1024 * 1024;
|
|
37
|
+
const SAFE_SKILL_NAME = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
|
|
38
|
+
|
|
39
|
+
// ── Prompt Injection Scanner ──────────────────────────────────────────────────
|
|
40
|
+
const PROMPT_INJECTION_PATTERNS = [
|
|
41
|
+
/ignore\s+(all\s+)?previous\s+instructions/i,
|
|
42
|
+
/forget\s+(all\s+)?previous/i,
|
|
43
|
+
/you\s+are\s+now/i,
|
|
44
|
+
/system\s*:\s*/i,
|
|
45
|
+
/\[SYSTEM\]/i,
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
function scanForPromptInjection(content) {
|
|
49
|
+
if (typeof content !== 'string') return false;
|
|
50
|
+
const found = PROMPT_INJECTION_PATTERNS.filter(p => p.test(content));
|
|
51
|
+
if (found.length > 0) {
|
|
52
|
+
console.warn(c.yellow(' ⚠️ Potential prompt injection detected in downloaded skill'));
|
|
53
|
+
console.warn(c.yellow(` Patterns: ${found.map(p => p.source).join(', ')}`));
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ── ANSI helpers ──────────────────────────────────────────────────────────────
|
|
60
|
+
const NO_COLOR = process.env.NO_COLOR || !process.stdout.isTTY;
|
|
61
|
+
const c = {
|
|
62
|
+
green: (s) => NO_COLOR ? s : `\x1b[32m${s}\x1b[0m`,
|
|
63
|
+
red: (s) => NO_COLOR ? s : `\x1b[31m${s}\x1b[0m`,
|
|
64
|
+
yellow: (s) => NO_COLOR ? s : `\x1b[33m${s}\x1b[0m`,
|
|
65
|
+
cyan: (s) => NO_COLOR ? s : `\x1b[36m${s}\x1b[0m`,
|
|
66
|
+
bold: (s) => NO_COLOR ? s : `\x1b[1m${s}\x1b[0m`,
|
|
67
|
+
dim: (s) => NO_COLOR ? s : `\x1b[2m${s}\x1b[0m`,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// ── plugins.json lock file ─────────────────────────────────────────────────────
|
|
71
|
+
function readLock() {
|
|
72
|
+
if (!fs.existsSync(PLUGINS_JSON)) return { plugins: [] };
|
|
73
|
+
try {
|
|
74
|
+
return JSON.parse(fs.readFileSync(PLUGINS_JSON, 'utf8'));
|
|
75
|
+
} catch {
|
|
76
|
+
return { plugins: [] };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function writeLock(lock) {
|
|
81
|
+
fs.mkdirSync(AGENTS_DIR, { recursive: true });
|
|
82
|
+
fs.writeFileSync(PLUGINS_JSON, JSON.stringify(lock, null, 2) + '\n');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ── HTTP fetch helper (no deps) ───────────────────────────────────────────────
|
|
86
|
+
function fetchText(url) {
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
const request = https.get(url, { headers: { 'User-Agent': 'contextos-agents' } }, (res) => {
|
|
89
|
+
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
90
|
+
const redirect = new URL(res.headers.location, url);
|
|
91
|
+
if (redirect.protocol !== 'https:' || redirect.hostname !== 'raw.githubusercontent.com') {
|
|
92
|
+
return reject(new Error('Refusing redirect outside raw.githubusercontent.com'));
|
|
93
|
+
}
|
|
94
|
+
return fetchText(redirect.toString()).then(resolve).catch(reject);
|
|
95
|
+
}
|
|
96
|
+
if (res.statusCode !== 200) {
|
|
97
|
+
return reject(new Error(`HTTP ${res.statusCode} for ${url}`));
|
|
98
|
+
}
|
|
99
|
+
let data = '';
|
|
100
|
+
let bytes = 0;
|
|
101
|
+
res.on('data', chunk => {
|
|
102
|
+
bytes += chunk.length;
|
|
103
|
+
if (bytes > MAX_DOWNLOAD_BYTES) {
|
|
104
|
+
request.destroy(new Error(`Download exceeds ${MAX_DOWNLOAD_BYTES} bytes`));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
data += chunk;
|
|
108
|
+
});
|
|
109
|
+
res.on('end', () => resolve(data));
|
|
110
|
+
});
|
|
111
|
+
request.setTimeout(15_000, () => request.destroy(new Error('Download timed out')));
|
|
112
|
+
request.on('error', reject);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ── Source resolver ──────────────────────────────────────────────────────────
|
|
117
|
+
/**
|
|
118
|
+
* Parse a skill reference into a structured descriptor.
|
|
119
|
+
*
|
|
120
|
+
* Supported formats:
|
|
121
|
+
* username/repo → GitHub: fetch /SKILL.md from repo root
|
|
122
|
+
* username/repo/path/to/skill → GitHub: fetch /path/to/skill/SKILL.md
|
|
123
|
+
* my-npm-package → npm: install package, copy skill dir
|
|
124
|
+
*/
|
|
125
|
+
function isValidNpmPackage(name) {
|
|
126
|
+
if (typeof name !== 'string' || !name.trim()) return false;
|
|
127
|
+
if (name.startsWith('-') || name.includes('/-')) return false;
|
|
128
|
+
return /^(?:@[a-z0-9_.-]+\/)?[a-z0-9_.-]+$/.test(name);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function parseRef(ref) {
|
|
132
|
+
if (typeof ref !== 'string' || !ref.trim()) {
|
|
133
|
+
throw new Error('A plugin reference is required');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Scoped packages contain a slash but are npm packages, not GitHub refs.
|
|
137
|
+
if (ref.startsWith('@')) {
|
|
138
|
+
if (!isValidNpmPackage(ref)) {
|
|
139
|
+
throw new Error(`Invalid scoped npm package name: '${ref}'`);
|
|
140
|
+
}
|
|
141
|
+
return { type: 'npm', package: ref, raw: ref };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// GitHub: contains a slash
|
|
145
|
+
if (ref.includes('/')) {
|
|
146
|
+
const parts = ref.split('/');
|
|
147
|
+
const owner = parts[0];
|
|
148
|
+
const repoRef = parts[1];
|
|
149
|
+
const subPath = parts.slice(2).join('/'); // may be empty
|
|
150
|
+
const [repo, gitRef = 'main'] = repoRef.split('@');
|
|
151
|
+
const safeSegment = /^[A-Za-z0-9._-]+$/;
|
|
152
|
+
|
|
153
|
+
const isSafePathPart = part => part !== '.' && part !== '..' && safeSegment.test(part);
|
|
154
|
+
if (!owner || !repo || !isSafePathPart(owner) || !isSafePathPart(repo) ||
|
|
155
|
+
!isSafePathPart(gitRef) || parts.slice(2).some(part => !isSafePathPart(part))) {
|
|
156
|
+
throw new Error('Invalid GitHub plugin reference');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return { type: 'github', owner, repo, gitRef, isPinned: repoRef.includes('@'), subPath, raw: ref };
|
|
160
|
+
}
|
|
161
|
+
// npm package
|
|
162
|
+
if (!isValidNpmPackage(ref)) {
|
|
163
|
+
throw new Error(`Invalid npm package name: '${ref}'`);
|
|
164
|
+
}
|
|
165
|
+
return { type: 'npm', package: ref, raw: ref };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ── GitHub installer ──────────────────────────────────────────────────────────
|
|
169
|
+
async function installFromGitHub(descriptor, skillName, dryRun, checksum) {
|
|
170
|
+
const { owner, repo, gitRef, isPinned, subPath } = descriptor;
|
|
171
|
+
const skillPath = subPath || '';
|
|
172
|
+
const base = `https://raw.githubusercontent.com/${owner}/${repo}/${gitRef}`;
|
|
173
|
+
const skillMdUrl = skillPath
|
|
174
|
+
? `${base}/${skillPath}/SKILL.md`
|
|
175
|
+
: `${base}/SKILL.md`;
|
|
176
|
+
|
|
177
|
+
console.log(c.dim(` Fetching: ${skillMdUrl}`));
|
|
178
|
+
if (!isPinned) {
|
|
179
|
+
console.log(c.yellow(' [WARN] Unpinned GitHub ref: prefer owner/repo@<commit> for reproducible installs.'));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
let skillMdContent;
|
|
183
|
+
try {
|
|
184
|
+
skillMdContent = await fetchText(skillMdUrl);
|
|
185
|
+
} catch (err) {
|
|
186
|
+
if (gitRef !== 'main') throw err;
|
|
187
|
+
// Try /master branch fallback only for the legacy default branch behavior.
|
|
188
|
+
const masterUrl = skillMdUrl.replace('/main/', '/master/');
|
|
189
|
+
console.log(c.dim(` Retrying on master branch: ${masterUrl}`));
|
|
190
|
+
skillMdContent = await fetchText(masterUrl);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (!skillMdContent || skillMdContent.length < 50) {
|
|
194
|
+
throw new Error('Fetched SKILL.md appears empty or invalid');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const sha256 = crypto.createHash('sha256').update(skillMdContent).digest('hex');
|
|
198
|
+
if (checksum && sha256.toLowerCase() !== checksum.toLowerCase()) {
|
|
199
|
+
throw new Error(`Checksum mismatch for ${skillName}: expected ${checksum}, got ${sha256}`);
|
|
200
|
+
}
|
|
201
|
+
scanForPromptInjection(skillMdContent);
|
|
202
|
+
|
|
203
|
+
// Optionally fetch skill.yaml if it exists
|
|
204
|
+
const yamlUrl = skillPath
|
|
205
|
+
? `${base}/${skillPath}/skill.yaml`
|
|
206
|
+
: `${base}/skill.yaml`;
|
|
207
|
+
|
|
208
|
+
let yamlContent = null;
|
|
209
|
+
try {
|
|
210
|
+
yamlContent = await fetchText(yamlUrl);
|
|
211
|
+
} catch {
|
|
212
|
+
// Optional — not all skills have skill.yaml
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const targetDir = path.join(PLUGINS_DIR, skillName);
|
|
216
|
+
|
|
217
|
+
if (dryRun) {
|
|
218
|
+
console.log(c.yellow(` [DRY-RUN] Would install to: ${targetDir}`));
|
|
219
|
+
console.log(c.yellow(` [DRY-RUN] SKILL.md size: ${skillMdContent.length} bytes (sha256: ${sha256})`));
|
|
220
|
+
return sha256;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
224
|
+
fs.writeFileSync(path.join(targetDir, 'SKILL.md'), skillMdContent);
|
|
225
|
+
if (yamlContent) {
|
|
226
|
+
fs.writeFileSync(path.join(targetDir, 'skill.yaml'), yamlContent);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Write a source marker so we know where to update from
|
|
230
|
+
fs.writeFileSync(path.join(targetDir, '.source'), JSON.stringify({
|
|
231
|
+
type: 'github',
|
|
232
|
+
owner,
|
|
233
|
+
repo,
|
|
234
|
+
gitRef,
|
|
235
|
+
subPath: skillPath,
|
|
236
|
+
ref: descriptor.raw,
|
|
237
|
+
sha256,
|
|
238
|
+
installedAt: new Date().toISOString(),
|
|
239
|
+
}, null, 2));
|
|
240
|
+
|
|
241
|
+
console.log(c.green(` ✓ Installed SKILL.md (${skillMdContent.length} bytes, sha256: ${sha256.slice(0, 12)}...)`));
|
|
242
|
+
if (yamlContent) console.log(c.green(` ✓ Installed skill.yaml`));
|
|
243
|
+
return sha256;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ── npm installer ─────────────────────────────────────────────────────────────
|
|
247
|
+
function installFromNpm(descriptor, skillName, dryRun, checksum) {
|
|
248
|
+
const pkgName = descriptor.package;
|
|
249
|
+
|
|
250
|
+
if (dryRun) {
|
|
251
|
+
console.log(c.yellow(` [DRY-RUN] Would run: npm install ${pkgName}`));
|
|
252
|
+
console.log(c.yellow(` [DRY-RUN] Would copy skill to: ${path.join(PLUGINS_DIR, skillName)}`));
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
console.log(c.dim(` Installing npm package: ${pkgName}`));
|
|
257
|
+
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
258
|
+
const temporaryPrefix = fs.mkdtempSync(path.join(os.tmpdir(), 'contextos-plugin-'));
|
|
259
|
+
|
|
260
|
+
let sha256 = null;
|
|
261
|
+
try {
|
|
262
|
+
// `--prefix` keeps both node_modules and npm metadata outside the user's
|
|
263
|
+
// project. Lifecycle scripts remain disabled for untrusted packages.
|
|
264
|
+
execFileSync(npm, [
|
|
265
|
+
'install', '--prefix', temporaryPrefix, '--no-save', '--ignore-scripts',
|
|
266
|
+
'--package-lock=false', '--no-audit', '--no-fund', pkgName,
|
|
267
|
+
], { cwd: temporaryPrefix, stdio: 'pipe' });
|
|
268
|
+
|
|
269
|
+
const nmPath = path.join(temporaryPrefix, 'node_modules', pkgName);
|
|
270
|
+
const skillSrc = fs.existsSync(path.join(nmPath, 'SKILL.md'))
|
|
271
|
+
? nmPath
|
|
272
|
+
: path.join(nmPath, 'skill'); // try skill/ subfolder
|
|
273
|
+
|
|
274
|
+
if (!fs.existsSync(path.join(skillSrc, 'SKILL.md'))) {
|
|
275
|
+
throw new Error(`Package '${pkgName}' does not contain a SKILL.md at its root or skill/ subdirectory`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const skillMdContent = fs.readFileSync(path.join(skillSrc, 'SKILL.md'), 'utf8');
|
|
279
|
+
sha256 = crypto.createHash('sha256').update(skillMdContent).digest('hex');
|
|
280
|
+
if (checksum && sha256.toLowerCase() !== checksum.toLowerCase()) {
|
|
281
|
+
throw new Error(`Checksum mismatch for ${skillName}: expected ${checksum}, got ${sha256}`);
|
|
282
|
+
}
|
|
283
|
+
scanForPromptInjection(skillMdContent);
|
|
284
|
+
|
|
285
|
+
const targetDir = path.join(PLUGINS_DIR, skillName);
|
|
286
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
287
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
288
|
+
fs.cpSync(skillSrc, targetDir, { recursive: true, force: true });
|
|
289
|
+
|
|
290
|
+
// Write source marker
|
|
291
|
+
fs.writeFileSync(path.join(targetDir, '.source'), JSON.stringify({
|
|
292
|
+
type: 'npm',
|
|
293
|
+
package: pkgName,
|
|
294
|
+
ref: descriptor.raw,
|
|
295
|
+
sha256,
|
|
296
|
+
installedAt: new Date().toISOString(),
|
|
297
|
+
}, null, 2));
|
|
298
|
+
} finally {
|
|
299
|
+
fs.rmSync(temporaryPrefix, { recursive: true, force: true });
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
console.log(c.green(` ✓ Installed from npm: ${pkgName} (sha256: ${sha256 ? sha256.slice(0, 12) + '...' : 'unknown'})`));
|
|
303
|
+
return sha256;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ── Derive skill name from ref ────────────────────────────────────────────────
|
|
307
|
+
function deriveSkillName(ref) {
|
|
308
|
+
// username/repo/path/to/my-skill → my-skill
|
|
309
|
+
// username/my-skill → my-skill
|
|
310
|
+
// my-npm-package → my-npm-package
|
|
311
|
+
const parts = ref.replace(/\.git$/, '').split('/');
|
|
312
|
+
return parts[parts.length - 1].toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function isSafeSkillName(skillName) {
|
|
316
|
+
return typeof skillName === 'string' && SAFE_SKILL_NAME.test(skillName);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ═════════════════════════════════════════════════════════════════════════════
|
|
320
|
+
// COMMANDS
|
|
321
|
+
// ═════════════════════════════════════════════════════════════════════════════
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* skill add <ref> [--dry-run] [--checksum <sha256>]
|
|
325
|
+
*/
|
|
326
|
+
async function add(ref, { dryRun = false, checksum } = {}) {
|
|
327
|
+
if (!ref) {
|
|
328
|
+
console.error(c.red('Usage: ctx.js skill add <ref> [--checksum <sha256>]'));
|
|
329
|
+
console.error(' ref can be: username/repo, username/repo/path/to/skill, or npm-package-name');
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const descriptor = parseRef(ref);
|
|
334
|
+
const skillName = deriveSkillName(ref);
|
|
335
|
+
|
|
336
|
+
if (!isSafeSkillName(skillName)) {
|
|
337
|
+
throw new Error(`Invalid plugin name derived from reference: '${skillName}'`);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
console.log('');
|
|
341
|
+
console.log(c.bold(`Installing skill: ${c.cyan(skillName)}`));
|
|
342
|
+
console.log(c.dim(` Source: ${ref} (${descriptor.type})`));
|
|
343
|
+
|
|
344
|
+
// Check for conflict with builtin skills
|
|
345
|
+
const builtinPath = path.join(CORE_SKILLS, skillName);
|
|
346
|
+
if (fs.existsSync(builtinPath)) {
|
|
347
|
+
console.error(c.red(`\n[ERROR] '${skillName}' conflicts with a built-in skill.`));
|
|
348
|
+
console.error(c.red(' Built-in skills cannot be overridden via --add-skill.'));
|
|
349
|
+
console.error(c.dim(' To customize a built-in skill, fork the repo and submit a PR.'));
|
|
350
|
+
process.exit(1);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Check if already installed
|
|
354
|
+
const lock = readLock();
|
|
355
|
+
const existing = lock.plugins.find(p => p.name === skillName);
|
|
356
|
+
if (existing && !dryRun) {
|
|
357
|
+
console.log(c.yellow(`\n[WARN] '${skillName}' is already installed (from: ${existing.ref})`));
|
|
358
|
+
console.log(c.yellow(' Re-installing will overwrite it. Continuing...'));
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
let installedSha256 = null;
|
|
362
|
+
try {
|
|
363
|
+
if (descriptor.type === 'github') {
|
|
364
|
+
installedSha256 = await installFromGitHub(descriptor, skillName, dryRun, checksum);
|
|
365
|
+
} else {
|
|
366
|
+
installedSha256 = installFromNpm(descriptor, skillName, dryRun, checksum);
|
|
367
|
+
}
|
|
368
|
+
} catch (err) {
|
|
369
|
+
console.error(c.red(`\n[ERROR] Failed to install '${skillName}': ${err.message}`));
|
|
370
|
+
process.exit(1);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (!dryRun) {
|
|
374
|
+
// Update lock file
|
|
375
|
+
const idx = lock.plugins.findIndex(p => p.name === skillName);
|
|
376
|
+
const entry = {
|
|
377
|
+
name: skillName,
|
|
378
|
+
ref,
|
|
379
|
+
type: descriptor.type,
|
|
380
|
+
sha256: installedSha256,
|
|
381
|
+
installedAt: new Date().toISOString(),
|
|
382
|
+
};
|
|
383
|
+
if (idx >= 0) lock.plugins[idx] = entry;
|
|
384
|
+
else lock.plugins.push(entry);
|
|
385
|
+
writeLock(lock);
|
|
386
|
+
|
|
387
|
+
console.log('');
|
|
388
|
+
console.log(c.green(c.bold('✓ Skill installed successfully!')));
|
|
389
|
+
console.log(c.dim(` Location: .agents/plugins/${skillName}/`));
|
|
390
|
+
console.log('');
|
|
391
|
+
console.log(' Next steps:');
|
|
392
|
+
console.log(` node .agents/ctx.js export all # recompile with new skill`);
|
|
393
|
+
console.log(` node .agents/ctx.js validate # verify everything is consistent`);
|
|
394
|
+
console.log('');
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* skill remove <name>
|
|
400
|
+
*/
|
|
401
|
+
function remove(skillName) {
|
|
402
|
+
if (!skillName) {
|
|
403
|
+
console.error(c.red('Usage: ctx.js skill remove <name>'));
|
|
404
|
+
process.exit(1);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (!isSafeSkillName(skillName)) {
|
|
408
|
+
console.error(c.red(`[ERROR] Invalid plugin name: '${skillName}'`));
|
|
409
|
+
process.exit(1);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const lock = readLock();
|
|
413
|
+
const entry = lock.plugins.find(p => p.name === skillName);
|
|
414
|
+
const skillDir = path.join(PLUGINS_DIR, skillName);
|
|
415
|
+
|
|
416
|
+
if (!entry && !fs.existsSync(skillDir)) {
|
|
417
|
+
console.error(c.red(`[ERROR] Plugin '${skillName}' is not installed.`));
|
|
418
|
+
console.log(c.dim(' Run: node .agents/ctx.js skill list'));
|
|
419
|
+
process.exit(1);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Protect builtins
|
|
423
|
+
const builtinPath = path.join(CORE_SKILLS, skillName);
|
|
424
|
+
if (fs.existsSync(builtinPath)) {
|
|
425
|
+
console.error(c.red(`[ERROR] '${skillName}' is a built-in skill and cannot be removed.`));
|
|
426
|
+
process.exit(1);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (fs.existsSync(skillDir)) {
|
|
430
|
+
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
lock.plugins = lock.plugins.filter(p => p.name !== skillName);
|
|
434
|
+
writeLock(lock);
|
|
435
|
+
|
|
436
|
+
console.log(c.green(`✓ Removed plugin: ${skillName}`));
|
|
437
|
+
console.log(c.dim(' Run: node .agents/ctx.js export all (to recompile)'));
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* skill list
|
|
442
|
+
*/
|
|
443
|
+
function list() {
|
|
444
|
+
const lock = readLock();
|
|
445
|
+
|
|
446
|
+
// Builtin skills
|
|
447
|
+
const builtins = fs.existsSync(CORE_SKILLS)
|
|
448
|
+
? fs.readdirSync(CORE_SKILLS).filter(n => fs.statSync(path.join(CORE_SKILLS, n)).isDirectory())
|
|
449
|
+
: [];
|
|
450
|
+
|
|
451
|
+
// Plugin skills
|
|
452
|
+
const plugins = lock.plugins;
|
|
453
|
+
|
|
454
|
+
console.log('');
|
|
455
|
+
console.log(c.bold('ContextOS Skills'));
|
|
456
|
+
console.log('');
|
|
457
|
+
|
|
458
|
+
// Built-ins
|
|
459
|
+
console.log(c.bold(` Built-in (${builtins.length})`));
|
|
460
|
+
for (const name of builtins) {
|
|
461
|
+
console.log(` ${c.cyan('●')} ${name} ${c.dim('(core)')}`);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Plugins
|
|
465
|
+
console.log('');
|
|
466
|
+
if (plugins.length === 0) {
|
|
467
|
+
console.log(c.bold(' Plugins (0)'));
|
|
468
|
+
console.log(c.dim(' No plugins installed yet.'));
|
|
469
|
+
console.log(c.dim(' Install one: node .agents/ctx.js skill add username/my-skill'));
|
|
470
|
+
} else {
|
|
471
|
+
console.log(c.bold(` Plugins (${plugins.length})`));
|
|
472
|
+
for (const p of plugins) {
|
|
473
|
+
const when = p.installedAt ? new Date(p.installedAt).toLocaleDateString() : '?';
|
|
474
|
+
console.log(` ${c.green('●')} ${p.name} ${c.dim(`← ${p.ref} (installed ${when})`)}`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
console.log('');
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* skill search <query> — searches the hosted registry.json
|
|
483
|
+
*/
|
|
484
|
+
async function search(query) {
|
|
485
|
+
console.log(c.dim(`\nFetching registry from: ${REGISTRY_URL}\n`));
|
|
486
|
+
|
|
487
|
+
let registry;
|
|
488
|
+
try {
|
|
489
|
+
const raw = await fetchText(REGISTRY_URL);
|
|
490
|
+
registry = JSON.parse(raw);
|
|
491
|
+
} catch (err) {
|
|
492
|
+
console.error(c.red(`[ERROR] Could not fetch registry: ${err.message}`));
|
|
493
|
+
console.error(c.dim(' Check your internet connection or try again later.'));
|
|
494
|
+
process.exit(1);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const q = (query || '').toLowerCase();
|
|
498
|
+
const results = registry.skills.filter(s =>
|
|
499
|
+
!q ||
|
|
500
|
+
s.name.includes(q) ||
|
|
501
|
+
s.description.toLowerCase().includes(q) ||
|
|
502
|
+
(s.tags || []).some(t => t.includes(q)) ||
|
|
503
|
+
s.author.includes(q)
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
if (results.length === 0) {
|
|
507
|
+
console.log(c.yellow(`No skills found matching "${query}".`));
|
|
508
|
+
console.log(c.dim(` Browse all: ${registry.contributing}`));
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
console.log(c.bold(`Found ${results.length} skill(s)${q ? ` matching "${query}"` : ''}:\n`));
|
|
513
|
+
|
|
514
|
+
for (const s of results) {
|
|
515
|
+
const installRef = s.builtin
|
|
516
|
+
? c.dim('(built-in)')
|
|
517
|
+
: c.cyan(s.github ? `${s.github}${s.path ? '/' + s.path : ''}` : s.npm);
|
|
518
|
+
|
|
519
|
+
const builtinBadge = s.builtin ? c.dim(' [built-in]') : '';
|
|
520
|
+
console.log(` ${c.bold(s.name)}${builtinBadge}`);
|
|
521
|
+
console.log(` ${s.description}`);
|
|
522
|
+
console.log(` ${c.dim('author:')} ${s.author} ${c.dim('tags:')} ${(s.tags || []).join(', ')}`);
|
|
523
|
+
console.log(` ${c.dim('install:')} node .agents/ctx.js skill add ${installRef}`);
|
|
524
|
+
console.log('');
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
console.log(c.dim(`To publish your skill: ${registry.contributing}`));
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// ═════════════════════════════════════════════════════════════════════════════
|
|
531
|
+
// Plugin-aware skill collection (used by adapters)
|
|
532
|
+
// ═════════════════════════════════════════════════════════════════════════════
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Returns an array of all skill source directories:
|
|
536
|
+
* both core (builtin) and plugins.
|
|
537
|
+
* Adapters call this instead of reading CORE_SKILLS directly.
|
|
538
|
+
*/
|
|
539
|
+
function collectAllSkillDirs() {
|
|
540
|
+
const dirs = [];
|
|
541
|
+
|
|
542
|
+
// Core skills
|
|
543
|
+
if (fs.existsSync(CORE_SKILLS)) {
|
|
544
|
+
for (const name of fs.readdirSync(CORE_SKILLS)) {
|
|
545
|
+
const d = path.join(CORE_SKILLS, name);
|
|
546
|
+
if (fs.statSync(d).isDirectory()) dirs.push(d);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Plugin skills
|
|
551
|
+
if (fs.existsSync(PLUGINS_DIR)) {
|
|
552
|
+
for (const name of fs.readdirSync(PLUGINS_DIR)) {
|
|
553
|
+
const d = path.join(PLUGINS_DIR, name);
|
|
554
|
+
if (fs.statSync(d).isDirectory()) dirs.push(d);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
return dirs;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
module.exports = {
|
|
562
|
+
add,
|
|
563
|
+
remove,
|
|
564
|
+
list,
|
|
565
|
+
search,
|
|
566
|
+
collectAllSkillDirs,
|
|
567
|
+
readLock,
|
|
568
|
+
parseRef,
|
|
569
|
+
deriveSkillName,
|
|
570
|
+
isSafeSkillName,
|
|
571
|
+
scanForPromptInjection,
|
|
572
|
+
PROMPT_INJECTION_PATTERNS,
|
|
573
|
+
PLUGINS_DIR,
|
|
574
|
+
};
|