contextos-agents 2.1.0 → 2.2.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/adapters/aider/export.js +2 -2
- package/.agents/adapters/claude/export.js +53 -2
- package/.agents/adapters/drift-detector.js +6 -3
- package/.agents/adapters/pure-compiler.js +18 -6
- package/.agents/ctx.js +13 -8
- package/.agents/plugins.js +347 -26
- package/.agents/profiles.js +32 -11
- package/README.md +38 -3
- package/bin/commands/hook.js +50 -12
- package/bin/commands/scan.js +10 -3
- package/bin/index.js +165 -53
- package/bin/lib/git-snapshot.js +70 -43
- package/bin/lib/scan.js +108 -27
- package/bin/lib/ui.js +140 -0
- package/catalog/skills/adapters/EXAMPLES.md +19 -0
- package/catalog/skills/adapters/SKILL.md +101 -0
- package/catalog/skills/adapters/TROUBLESHOOTING.md +7 -0
- package/catalog/skills/adapters/VALIDATION.json +12 -0
- package/catalog/skills/adapters/skill.yaml +13 -0
- package/catalog/skills/api-design/EXAMPLES.md +91 -0
- package/catalog/skills/api-design/SKILL.md +63 -0
- package/catalog/skills/api-design/TROUBLESHOOTING.md +54 -0
- package/catalog/skills/api-design/VALIDATION.json +11 -0
- package/catalog/skills/api-design/skill.yaml +14 -0
- package/catalog/skills/architecture-diagrams/SKILL.md +108 -0
- package/catalog/skills/architecture-diagrams/VALIDATION.json +12 -0
- package/catalog/skills/architecture-diagrams/skill.yaml +9 -0
- package/catalog/skills/brutalist-design/EXAMPLES.md +59 -0
- package/catalog/skills/brutalist-design/SKILL.md +150 -0
- package/catalog/skills/brutalist-design/VALIDATION.json +12 -0
- package/catalog/skills/brutalist-design/skill.yaml +10 -0
- package/catalog/skills/ci-cd/EXAMPLES.md +79 -0
- package/catalog/skills/ci-cd/SKILL.md +69 -0
- package/catalog/skills/ci-cd/TROUBLESHOOTING.md +52 -0
- package/catalog/skills/ci-cd/VALIDATION.json +11 -0
- package/catalog/skills/ci-cd/skill.yaml +13 -0
- package/catalog/skills/database/EXAMPLES.md +74 -0
- package/catalog/skills/database/SKILL.md +101 -0
- package/catalog/skills/database/TROUBLESHOOTING.md +18 -0
- package/catalog/skills/database/VALIDATION.json +11 -0
- package/catalog/skills/database/skill.yaml +14 -0
- package/catalog/skills/ddd/EXAMPLES.md +42 -0
- package/catalog/skills/ddd/SKILL.md +247 -0
- package/catalog/skills/ddd/TROUBLESHOOTING.md +19 -0
- package/catalog/skills/ddd/VALIDATION.json +12 -0
- package/catalog/skills/ddd/skill.yaml +14 -0
- package/catalog/skills/decisions/EXAMPLES.md +35 -0
- package/catalog/skills/decisions/SKILL.md +90 -0
- package/catalog/skills/decisions/TROUBLESHOOTING.md +13 -0
- package/catalog/skills/decisions/VALIDATION.json +12 -0
- package/catalog/skills/decisions/skill.yaml +13 -0
- package/catalog/skills/docker/EXAMPLES.md +56 -0
- package/catalog/skills/docker/SKILL.md +169 -0
- package/catalog/skills/docker/TROUBLESHOOTING.md +18 -0
- package/catalog/skills/docker/VALIDATION.json +11 -0
- package/catalog/skills/docker/skill.yaml +13 -0
- package/catalog/skills/fastapi/EXAMPLES.md +36 -0
- package/catalog/skills/fastapi/SKILL.md +171 -0
- package/catalog/skills/fastapi/TROUBLESHOOTING.md +19 -0
- package/catalog/skills/fastapi/VALIDATION.json +12 -0
- package/catalog/skills/fastapi/skill.yaml +14 -0
- package/catalog/skills/generators/EXAMPLES.md +19 -0
- package/catalog/skills/generators/SKILL.md +110 -0
- package/catalog/skills/generators/TROUBLESHOOTING.md +7 -0
- package/catalog/skills/generators/VALIDATION.json +12 -0
- package/catalog/skills/generators/skill.yaml +22 -0
- package/catalog/skills/generators/templates/API.md +77 -0
- package/catalog/skills/generators/templates/ARCHITECTURE.md +70 -0
- package/catalog/skills/generators/templates/DATABASE.md +42 -0
- package/catalog/skills/generators/templates/DECISION.md +46 -0
- package/catalog/skills/generators/templates/PRD.md +67 -0
- package/catalog/skills/generators/templates/PROJECT_GRAPH.md +56 -0
- package/catalog/skills/generators/templates/ROADMAP.md +51 -0
- package/catalog/skills/generators/templates/TASKS.md +43 -0
- package/catalog/skills/generators/templates/UI.md +73 -0
- package/catalog/skills/graphify/EXAMPLES.md +73 -0
- package/catalog/skills/graphify/SKILL.md +130 -0
- package/catalog/skills/graphify/VALIDATION.json +12 -0
- package/catalog/skills/graphify/skill.yaml +13 -0
- package/catalog/skills/impeccable-design/EXAMPLES.md +26 -0
- package/catalog/skills/impeccable-design/SKILL.md +201 -0
- package/catalog/skills/impeccable-design/TROUBLESHOOTING.md +19 -0
- package/catalog/skills/impeccable-design/VALIDATION.json +12 -0
- package/catalog/skills/impeccable-design/skill.yaml +15 -0
- package/catalog/skills/interview-me/SKILL.md +97 -0
- package/catalog/skills/interview-me/VALIDATION.json +12 -0
- package/catalog/skills/interview-me/skill.yaml +9 -0
- package/catalog/skills/microservices/EXAMPLES.md +38 -0
- package/catalog/skills/microservices/SKILL.md +164 -0
- package/catalog/skills/microservices/TROUBLESHOOTING.md +19 -0
- package/catalog/skills/microservices/VALIDATION.json +12 -0
- package/catalog/skills/microservices/skill.yaml +14 -0
- package/catalog/skills/minimalist-design/EXAMPLES.md +58 -0
- package/catalog/skills/minimalist-design/SKILL.md +113 -0
- package/catalog/skills/minimalist-design/VALIDATION.json +12 -0
- package/catalog/skills/minimalist-design/skill.yaml +10 -0
- package/catalog/skills/nestjs/EXAMPLES.md +40 -0
- package/catalog/skills/nestjs/SKILL.md +139 -0
- package/catalog/skills/nestjs/TROUBLESHOOTING.md +19 -0
- package/catalog/skills/nestjs/VALIDATION.json +12 -0
- package/catalog/skills/nestjs/skill.yaml +14 -0
- package/catalog/skills/nextjs/EXAMPLES.md +40 -0
- package/catalog/skills/nextjs/SKILL.md +163 -0
- package/catalog/skills/nextjs/TROUBLESHOOTING.md +19 -0
- package/catalog/skills/nextjs/VALIDATION.json +12 -0
- package/catalog/skills/nextjs/skill.yaml +14 -0
- package/catalog/skills/node/EXAMPLES.md +80 -0
- package/catalog/skills/node/SKILL.md +128 -0
- package/catalog/skills/node/TROUBLESHOOTING.md +19 -0
- package/catalog/skills/node/VALIDATION.json +12 -0
- package/catalog/skills/node/skill.yaml +14 -0
- package/catalog/skills/performance/EXAMPLES.md +30 -0
- package/catalog/skills/performance/SKILL.md +75 -0
- package/catalog/skills/performance/TROUBLESHOOTING.md +19 -0
- package/catalog/skills/performance/VALIDATION.json +12 -0
- package/catalog/skills/performance/skill.yaml +14 -0
- package/catalog/skills/react/EXAMPLES.md +79 -0
- package/catalog/skills/react/SKILL.md +132 -0
- package/catalog/skills/react/TROUBLESHOOTING.md +19 -0
- package/catalog/skills/react/VALIDATION.json +12 -0
- package/catalog/skills/react/skill.yaml +14 -0
- package/catalog/skills/react-best-practices/SKILL.md +158 -0
- package/catalog/skills/react-best-practices/VALIDATION.json +12 -0
- package/catalog/skills/react-best-practices/skill.yaml +13 -0
- package/catalog/skills/redesign-audit/SKILL.md +117 -0
- package/catalog/skills/redesign-audit/VALIDATION.json +12 -0
- package/catalog/skills/redesign-audit/skill.yaml +9 -0
- package/catalog/skills/security-audit/EXAMPLES.md +79 -0
- package/catalog/skills/security-audit/SKILL.md +91 -0
- package/catalog/skills/security-audit/TROUBLESHOOTING.md +46 -0
- package/catalog/skills/security-audit/VALIDATION.json +11 -0
- package/catalog/skills/security-audit/skill.yaml +14 -0
- package/catalog/skills/soft-design/EXAMPLES.md +51 -0
- package/catalog/skills/soft-design/SKILL.md +108 -0
- package/catalog/skills/soft-design/VALIDATION.json +12 -0
- package/catalog/skills/soft-design/skill.yaml +10 -0
- package/catalog/skills/state-management/EXAMPLES.md +56 -0
- package/catalog/skills/state-management/SKILL.md +168 -0
- package/catalog/skills/state-management/TROUBLESHOOTING.md +18 -0
- package/catalog/skills/state-management/VALIDATION.json +11 -0
- package/catalog/skills/state-management/skill.yaml +14 -0
- package/catalog/skills/subagent-orchestrator/SKILL.md +117 -0
- package/catalog/skills/subagent-orchestrator/VALIDATION.json +12 -0
- package/catalog/skills/subagent-orchestrator/skill.yaml +9 -0
- package/catalog/skills/system-design/EXAMPLES.md +75 -0
- package/catalog/skills/system-design/SKILL.md +419 -0
- package/catalog/skills/system-design/TROUBLESHOOTING.md +19 -0
- package/catalog/skills/system-design/VALIDATION.json +12 -0
- package/catalog/skills/system-design/skill.yaml +14 -0
- package/catalog/skills/terraform/EXAMPLES.md +74 -0
- package/catalog/skills/terraform/SKILL.md +55 -0
- package/catalog/skills/terraform/TROUBLESHOOTING.md +53 -0
- package/catalog/skills/terraform/VALIDATION.json +11 -0
- package/catalog/skills/terraform/skill.yaml +14 -0
- package/catalog/skills/testing/EXAMPLES.md +122 -0
- package/catalog/skills/testing/SKILL.md +70 -0
- package/catalog/skills/testing/TROUBLESHOOTING.md +18 -0
- package/catalog/skills/testing/VALIDATION.json +11 -0
- package/catalog/skills/testing/skill.yaml +14 -0
- package/catalog/skills/typescript/EXAMPLES.md +64 -0
- package/catalog/skills/typescript/SKILL.md +112 -0
- package/catalog/skills/typescript/TROUBLESHOOTING.md +19 -0
- package/catalog/skills/typescript/VALIDATION.json +12 -0
- package/catalog/skills/typescript/skill.yaml +14 -0
- package/catalog/skills/ui-design/EXAMPLES.md +21 -0
- package/catalog/skills/ui-design/SKILL.md +124 -0
- package/catalog/skills/ui-design/TROUBLESHOOTING.md +19 -0
- package/catalog/skills/ui-design/VALIDATION.json +12 -0
- package/catalog/skills/ui-design/skill.yaml +16 -0
- package/catalog/skills/ui-ux-pro/EXAMPLES.md +62 -0
- package/catalog/skills/ui-ux-pro/SKILL.md +418 -0
- package/catalog/skills/ui-ux-pro/TROUBLESHOOTING.md +19 -0
- package/catalog/skills/ui-ux-pro/VALIDATION.json +12 -0
- package/catalog/skills/ui-ux-pro/skill.yaml +14 -0
- package/catalog/skills/ux-design/EXAMPLES.md +36 -0
- package/catalog/skills/ux-design/SKILL.md +116 -0
- package/catalog/skills/ux-design/TROUBLESHOOTING.md +19 -0
- package/catalog/skills/ux-design/VALIDATION.json +12 -0
- package/catalog/skills/ux-design/skill.yaml +16 -0
- package/catalog/skills/vercel-optimize/SKILL.md +83 -0
- package/catalog/skills/vercel-optimize/VALIDATION.json +12 -0
- package/catalog/skills/vercel-optimize/scripts/collect-signals.mjs +131 -0
- package/catalog/skills/vercel-optimize/scripts/gate-investigations.mjs +142 -0
- package/catalog/skills/vercel-optimize/scripts/merge-signals.mjs +143 -0
- package/catalog/skills/vercel-optimize/scripts/scan-codebase.mjs +174 -0
- package/catalog/skills/vercel-optimize/skill.yaml +15 -0
- package/catalog/skills/web-accessibility/EXAMPLES.md +39 -0
- package/catalog/skills/web-accessibility/SKILL.md +151 -0
- package/catalog/skills/web-accessibility/TROUBLESHOOTING.md +19 -0
- package/catalog/skills/web-accessibility/VALIDATION.json +12 -0
- package/catalog/skills/web-accessibility/skill.yaml +14 -0
- package/package.json +3 -2
package/.agents/plugins.js
CHANGED
|
@@ -26,7 +26,7 @@ const https = require('https');
|
|
|
26
26
|
const crypto = require('crypto');
|
|
27
27
|
const { execFileSync } = require('child_process');
|
|
28
28
|
const { ProjectMutationLock } = require('./filesystem/project-lock.js');
|
|
29
|
-
const JournaledTransaction = require('./filesystem/journaled-transaction.js');
|
|
29
|
+
const { JournaledTransaction } = require('./filesystem/journaled-transaction.js');
|
|
30
30
|
const {
|
|
31
31
|
calculateTreeDigest,
|
|
32
32
|
validateArchiveEntry,
|
|
@@ -47,6 +47,25 @@ const REGISTRY_URL = 'https://raw.githubusercontent.com/kok-o/contextos-agents
|
|
|
47
47
|
const MAX_DOWNLOAD_BYTES = 5 * 1024 * 1024;
|
|
48
48
|
const SAFE_SKILL_NAME = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
|
|
49
49
|
|
|
50
|
+
function getCatalogDir() {
|
|
51
|
+
if (process.env.CONTEXTOS_CATALOG_DIR && fs.existsSync(process.env.CONTEXTOS_CATALOG_DIR)) {
|
|
52
|
+
return process.env.CONTEXTOS_CATALOG_DIR;
|
|
53
|
+
}
|
|
54
|
+
const localAgentsCatalog = path.join(__dirname, 'catalog', 'skills');
|
|
55
|
+
if (fs.existsSync(localAgentsCatalog)) return localAgentsCatalog;
|
|
56
|
+
|
|
57
|
+
const relativeCatalog = path.join(path.resolve(__dirname, '..'), 'catalog', 'skills');
|
|
58
|
+
if (fs.existsSync(relativeCatalog)) return relativeCatalog;
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const pkgPath = require.resolve('contextos-agents/package.json', { paths: [__dirname, process.cwd()] });
|
|
62
|
+
const pkgCatalog = path.join(path.dirname(pkgPath), 'catalog', 'skills');
|
|
63
|
+
if (fs.existsSync(pkgCatalog)) return pkgCatalog;
|
|
64
|
+
} catch {}
|
|
65
|
+
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
50
69
|
// ── ANSI helpers ──────────────────────────────────────────────────────────────
|
|
51
70
|
const NO_COLOR = process.env.NO_COLOR || !process.stdout.isTTY;
|
|
52
71
|
const c = {
|
|
@@ -284,11 +303,49 @@ function isValidNpmPackage(name) {
|
|
|
284
303
|
return /^(?:@[a-z0-9_.-]+\/)?[a-z0-9_.-]+$/.test(name);
|
|
285
304
|
}
|
|
286
305
|
|
|
287
|
-
function parseRef(ref) {
|
|
306
|
+
function parseRef(ref, projectRoot = null) {
|
|
288
307
|
if (typeof ref !== 'string' || !ref.trim()) {
|
|
289
308
|
throw new Error('A plugin reference is required');
|
|
290
309
|
}
|
|
291
310
|
|
|
311
|
+
// 1. Built-in package catalog skills take strict precedence for catalog names.
|
|
312
|
+
// Consumer project local files can never spoof or hijack package catalog skills.
|
|
313
|
+
const pkgCatalogPath = path.join(getCatalogDir(), ref);
|
|
314
|
+
if (fs.existsSync(pkgCatalogPath) && fs.existsSync(path.join(pkgCatalogPath, 'SKILL.md'))) {
|
|
315
|
+
return {
|
|
316
|
+
type: 'catalog',
|
|
317
|
+
name: ref,
|
|
318
|
+
path: pkgCatalogPath,
|
|
319
|
+
isPinned: true,
|
|
320
|
+
raw: ref,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// 2. Explicit local references (must start with 'local:', './', '../', or 'file:')
|
|
325
|
+
if (ref.startsWith('local:') || ref.startsWith('./') || ref.startsWith('../') || ref.startsWith('file:')) {
|
|
326
|
+
const rawTarget = ref.startsWith('local:')
|
|
327
|
+
? ref.slice(6)
|
|
328
|
+
: (ref.startsWith('file:') ? ref.slice(5) : ref);
|
|
329
|
+
|
|
330
|
+
const baseRoot = projectRoot || process.cwd();
|
|
331
|
+
let localDir = path.resolve(baseRoot, rawTarget);
|
|
332
|
+
if (!fs.existsSync(localDir)) {
|
|
333
|
+
localDir = path.join(baseRoot, 'catalog', 'skills', rawTarget);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (fs.existsSync(localDir) && fs.existsSync(path.join(localDir, 'SKILL.md'))) {
|
|
337
|
+
const skillName = path.basename(localDir);
|
|
338
|
+
return {
|
|
339
|
+
type: 'catalog',
|
|
340
|
+
name: skillName,
|
|
341
|
+
path: localDir,
|
|
342
|
+
isPinned: true,
|
|
343
|
+
raw: ref,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
throw new Error(`Local skill not found at: '${ref}'`);
|
|
347
|
+
}
|
|
348
|
+
|
|
292
349
|
// Scoped packages contain a slash but are npm packages, not GitHub refs.
|
|
293
350
|
if (ref.startsWith('@')) {
|
|
294
351
|
if (!isValidNpmPackage(ref)) {
|
|
@@ -529,6 +586,62 @@ function installFromNpm(descriptor, skillName, dryRun, checksum, forceUnsafe = f
|
|
|
529
586
|
return sha256;
|
|
530
587
|
}
|
|
531
588
|
|
|
589
|
+
// ── Catalog installer ─────────────────────────────────────────────────────────
|
|
590
|
+
function installFromCatalog(descriptor, skillName, dryRun, checksum, forceUnsafe = false, tx = null) {
|
|
591
|
+
const sourceDir = descriptor.path;
|
|
592
|
+
const targetDir = path.join(PLUGINS_DIR, skillName);
|
|
593
|
+
const relTargetDir = toPosix(path.relative(ROOT, targetDir));
|
|
594
|
+
|
|
595
|
+
if (dryRun) {
|
|
596
|
+
console.log(c.dim(` [DRY-RUN] Would copy catalog skill from: ${sourceDir}`));
|
|
597
|
+
return 'sha256:0000000000000000000000000000000000000000000000000000000000000000';
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function copyRecursive(src, dest, relDest) {
|
|
601
|
+
if (!fs.existsSync(dest)) {
|
|
602
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
603
|
+
}
|
|
604
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
605
|
+
for (const entry of entries) {
|
|
606
|
+
const srcPath = path.join(src, entry.name);
|
|
607
|
+
const destPath = path.join(dest, entry.name);
|
|
608
|
+
const relPath = toPosix(path.join(relDest, entry.name));
|
|
609
|
+
|
|
610
|
+
if (entry.isDirectory()) {
|
|
611
|
+
copyRecursive(srcPath, destPath, relPath);
|
|
612
|
+
} else {
|
|
613
|
+
const content = fs.readFileSync(srcPath, 'utf8');
|
|
614
|
+
if (tx) {
|
|
615
|
+
tx.stageWrite(relPath, content);
|
|
616
|
+
} else {
|
|
617
|
+
fs.writeFileSync(destPath, content, 'utf8');
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
copyRecursive(sourceDir, targetDir, relTargetDir);
|
|
624
|
+
|
|
625
|
+
const primarySkillMd = path.join(sourceDir, 'SKILL.md');
|
|
626
|
+
const sha256 = fs.existsSync(primarySkillMd) ? crypto.createHash('sha256').update(fs.readFileSync(primarySkillMd)).digest('hex') : null;
|
|
627
|
+
|
|
628
|
+
const sourceMeta = {
|
|
629
|
+
source: 'catalog',
|
|
630
|
+
name: skillName,
|
|
631
|
+
path: toPosix(path.relative(ROOT, sourceDir)),
|
|
632
|
+
sha256,
|
|
633
|
+
installedAt: new Date().toISOString(),
|
|
634
|
+
};
|
|
635
|
+
if (tx) {
|
|
636
|
+
tx.stageWrite(`${relTargetDir}/.source`, JSON.stringify(sourceMeta, null, 2) + '\n');
|
|
637
|
+
} else {
|
|
638
|
+
fs.writeFileSync(path.join(targetDir, '.source'), JSON.stringify(sourceMeta, null, 2) + '\n');
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
console.log(c.green(` ✓ Installed from built-in catalog: ${skillName}`));
|
|
642
|
+
return sha256;
|
|
643
|
+
}
|
|
644
|
+
|
|
532
645
|
// ── Derive skill name from ref ────────────────────────────────────────────────
|
|
533
646
|
function deriveSkillName(ref) {
|
|
534
647
|
// username/repo/path/to/my-skill → my-skill
|
|
@@ -542,9 +655,137 @@ function isSafeSkillName(skillName) {
|
|
|
542
655
|
return typeof skillName === 'string' && SAFE_SKILL_NAME.test(skillName);
|
|
543
656
|
}
|
|
544
657
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
658
|
+
function findClosestSkill(target, candidates) {
|
|
659
|
+
let closest = null;
|
|
660
|
+
let minDistance = Infinity;
|
|
661
|
+
for (const item of candidates) {
|
|
662
|
+
if (item === target) return item;
|
|
663
|
+
if (item.includes(target) || target.includes(item)) return item;
|
|
664
|
+
const dist = levenshtein(target, item);
|
|
665
|
+
if (dist < minDistance && dist <= 3) {
|
|
666
|
+
minDistance = dist;
|
|
667
|
+
closest = item;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
return closest;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
function levenshtein(a, b) {
|
|
674
|
+
const m = a.length, n = b.length;
|
|
675
|
+
const d = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
|
|
676
|
+
for (let i = 0; i <= m; i++) d[i][0] = i;
|
|
677
|
+
for (let j = 0; j <= n; j++) d[0][j] = j;
|
|
678
|
+
for (let i = 1; i <= m; i++) {
|
|
679
|
+
for (let j = 1; j <= n; j++) {
|
|
680
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
681
|
+
d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return d[m][n];
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Batch install all available catalog skills.
|
|
689
|
+
*/
|
|
690
|
+
async function addAllFromCatalog(options = {}) {
|
|
691
|
+
const dryRun = Boolean(options.dryRun || options['dry-run']);
|
|
692
|
+
const force = Boolean(options.force);
|
|
693
|
+
const catalogDir = getCatalogDir();
|
|
694
|
+
if (!fs.existsSync(catalogDir)) {
|
|
695
|
+
throw new Error('Catalog directory not found in package');
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const catalogSkills = fs.readdirSync(catalogDir).filter(n => {
|
|
699
|
+
const full = path.join(catalogDir, n);
|
|
700
|
+
return fs.statSync(full).isDirectory() && fs.existsSync(path.join(full, 'SKILL.md'));
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
console.log('');
|
|
704
|
+
console.log(c.bold(`Batch installing ${catalogSkills.length} catalog skills...`));
|
|
705
|
+
|
|
706
|
+
const lock = readLock();
|
|
707
|
+
const existingNames = new Set(lock.plugins.map(p => p.name));
|
|
708
|
+
|
|
709
|
+
const projectLock = new ProjectMutationLock(ROOT);
|
|
710
|
+
const tx = new JournaledTransaction(ROOT);
|
|
711
|
+
let lockToken = null;
|
|
712
|
+
|
|
713
|
+
let installedCount = 0;
|
|
714
|
+
let skippedCount = 0;
|
|
715
|
+
const newlyInstalled = [];
|
|
716
|
+
|
|
717
|
+
try {
|
|
718
|
+
lockToken = projectLock.acquire({ command: 'skill:add-all' });
|
|
719
|
+
|
|
720
|
+
for (const skillName of catalogSkills) {
|
|
721
|
+
if (existingNames.has(skillName) && !force) {
|
|
722
|
+
skippedCount++;
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
const descriptor = {
|
|
727
|
+
type: 'catalog',
|
|
728
|
+
name: skillName,
|
|
729
|
+
path: path.join(catalogDir, skillName),
|
|
730
|
+
isPinned: true,
|
|
731
|
+
raw: skillName,
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
const installedSha256 = installFromCatalog(descriptor, skillName, dryRun, null, false, tx);
|
|
735
|
+
|
|
736
|
+
if (!dryRun) {
|
|
737
|
+
const idx = lock.plugins.findIndex(p => p.name === skillName);
|
|
738
|
+
const entry = {
|
|
739
|
+
name: skillName,
|
|
740
|
+
ref: skillName,
|
|
741
|
+
type: 'catalog',
|
|
742
|
+
sha256: installedSha256,
|
|
743
|
+
installedAt: new Date().toISOString(),
|
|
744
|
+
};
|
|
745
|
+
if (idx >= 0) lock.plugins[idx] = entry;
|
|
746
|
+
else lock.plugins.push(entry);
|
|
747
|
+
newlyInstalled.push(skillName);
|
|
748
|
+
}
|
|
749
|
+
installedCount++;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
if (!dryRun) {
|
|
753
|
+
writeLock(lock, tx);
|
|
754
|
+
tx.commit();
|
|
755
|
+
|
|
756
|
+
for (const skillName of newlyInstalled) {
|
|
757
|
+
const targetDir = path.join(PLUGINS_DIR, skillName);
|
|
758
|
+
if (fs.existsSync(targetDir)) {
|
|
759
|
+
const digestResult = calculateTreeDigest(targetDir);
|
|
760
|
+
const entry = lock.plugins.find(p => p.name === skillName);
|
|
761
|
+
if (entry) entry.treeDigest = digestResult.treeDigest;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
writeLock(lock);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
console.log('');
|
|
768
|
+
if (dryRun) {
|
|
769
|
+
console.log(c.dim(`[DRY-RUN] Would install ${installedCount} skills (${skippedCount} already installed - skipped).`));
|
|
770
|
+
} else {
|
|
771
|
+
console.log(c.green(c.bold(`✓ Batch install complete: ${installedCount} catalog skills installed.`)));
|
|
772
|
+
if (skippedCount > 0) {
|
|
773
|
+
console.log(c.dim(` (${skippedCount} skills already installed - skipped)`));
|
|
774
|
+
}
|
|
775
|
+
console.log('');
|
|
776
|
+
console.log(' Next steps:');
|
|
777
|
+
console.log(' node .agents/ctx.js export all # recompile with new skills');
|
|
778
|
+
console.log(' node .agents/ctx.js validate # verify consistency');
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
return { installedCount, skippedCount };
|
|
782
|
+
} catch (err) {
|
|
783
|
+
console.error(c.red(`\n[ERROR] Batch installation failed: ${err.message}`));
|
|
784
|
+
process.exit(1);
|
|
785
|
+
} finally {
|
|
786
|
+
if (lockToken) projectLock.release(lockToken);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
548
789
|
|
|
549
790
|
/**
|
|
550
791
|
* skill add <ref> [--dry-run] [--checksum <sha256>] [--force-unsafe-prompts]
|
|
@@ -556,6 +797,10 @@ async function add(ref, options = {}) {
|
|
|
556
797
|
const allowFloating = Boolean(options.allowFloating || options['allow-floating']);
|
|
557
798
|
const force = Boolean(options.force);
|
|
558
799
|
|
|
800
|
+
if (ref === '--all' || ref === 'all' || options.all) {
|
|
801
|
+
return await addAllFromCatalog(options);
|
|
802
|
+
}
|
|
803
|
+
|
|
559
804
|
if (!ref) {
|
|
560
805
|
console.error(c.red('Usage: ctx.js skill add <ref> [--checksum <sha256>]'));
|
|
561
806
|
console.error(' ref can be: username/repo, username/repo/path/to/skill, or npm-package-name');
|
|
@@ -569,8 +814,38 @@ async function add(ref, options = {}) {
|
|
|
569
814
|
throw new Error(`Invalid plugin name derived from reference: '${skillName}'`);
|
|
570
815
|
}
|
|
571
816
|
|
|
572
|
-
//
|
|
573
|
-
|
|
817
|
+
// 1. Check for conflict with builtin skills first
|
|
818
|
+
const builtinPath = path.join(CORE_SKILLS, skillName);
|
|
819
|
+
if (fs.existsSync(builtinPath)) {
|
|
820
|
+
console.error(c.red(`\n[ERROR] '${skillName}' conflicts with a built-in skill.`));
|
|
821
|
+
console.error(c.red(' Built-in skills cannot be overridden via --add-skill.'));
|
|
822
|
+
console.error(c.dim(` To customize a built-in skill for your project, run: contextos skill override ${skillName}`));
|
|
823
|
+
process.exit(1);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// 2. If single word not in catalog, check for typo suggestion
|
|
827
|
+
if (descriptor.type !== 'catalog' && !ref.includes('/') && !ref.includes('@')) {
|
|
828
|
+
const catalogDir = getCatalogDir();
|
|
829
|
+
if (fs.existsSync(catalogDir)) {
|
|
830
|
+
const catalogSkills = fs.readdirSync(catalogDir).filter(n => {
|
|
831
|
+
try {
|
|
832
|
+
return fs.statSync(path.join(catalogDir, n)).isDirectory();
|
|
833
|
+
} catch {
|
|
834
|
+
return false;
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
const suggestion = findClosestSkill(skillName, catalogSkills);
|
|
838
|
+
if (suggestion) {
|
|
839
|
+
console.error(c.red(`\n[ERROR] Skill '${skillName}' not found in catalog.`));
|
|
840
|
+
console.log(c.yellow(` Did you mean: "${suggestion}"?`));
|
|
841
|
+
console.log(c.dim(' Run "contextos skill list --available" to see all catalog skills.'));
|
|
842
|
+
process.exit(1);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// 3. Supply-chain source pinning check (Wave 6)
|
|
848
|
+
if (!dryRun && descriptor.type !== 'catalog') {
|
|
574
849
|
const pinning = validatePluginPinning(
|
|
575
850
|
{
|
|
576
851
|
type: descriptor.type,
|
|
@@ -594,15 +869,6 @@ async function add(ref, options = {}) {
|
|
|
594
869
|
console.log(c.bold(`Installing skill: ${c.cyan(skillName)}`));
|
|
595
870
|
console.log(c.dim(` Source: ${ref} (${descriptor.type})`));
|
|
596
871
|
|
|
597
|
-
// Check for conflict with builtin skills
|
|
598
|
-
const builtinPath = path.join(CORE_SKILLS, skillName);
|
|
599
|
-
if (fs.existsSync(builtinPath)) {
|
|
600
|
-
console.error(c.red(`\n[ERROR] '${skillName}' conflicts with a built-in skill.`));
|
|
601
|
-
console.error(c.red(' Built-in skills cannot be overridden via --add-skill.'));
|
|
602
|
-
console.error(c.dim(' To customize a built-in skill, fork the repo and submit a PR.'));
|
|
603
|
-
process.exit(1);
|
|
604
|
-
}
|
|
605
|
-
|
|
606
872
|
// Check if already installed
|
|
607
873
|
const lock = readLock();
|
|
608
874
|
const existing = lock.plugins.find(p => p.name === skillName);
|
|
@@ -637,7 +903,9 @@ async function add(ref, options = {}) {
|
|
|
637
903
|
lockToken = projectLock.acquire({ command: 'skill:add' });
|
|
638
904
|
|
|
639
905
|
let installedSha256 = null;
|
|
640
|
-
if (descriptor.type === '
|
|
906
|
+
if (descriptor.type === 'catalog') {
|
|
907
|
+
installedSha256 = installFromCatalog(descriptor, skillName, dryRun, checksum, forceUnsafe, tx);
|
|
908
|
+
} else if (descriptor.type === 'github') {
|
|
641
909
|
installedSha256 = await installFromGitHub(descriptor, skillName, dryRun, checksum, forceUnsafe, tx);
|
|
642
910
|
} else {
|
|
643
911
|
installedSha256 = installFromNpm(descriptor, skillName, dryRun, checksum, forceUnsafe, tx);
|
|
@@ -683,6 +951,21 @@ async function add(ref, options = {}) {
|
|
|
683
951
|
// We don't need to do anything since it wasn't committed
|
|
684
952
|
}
|
|
685
953
|
console.error(c.red(`\n[ERROR] Failed to install '${skillName}': ${err.message}`));
|
|
954
|
+
const catalogDir = getCatalogDir();
|
|
955
|
+
if (fs.existsSync(catalogDir)) {
|
|
956
|
+
const catalogSkills = fs.readdirSync(catalogDir).filter(n => {
|
|
957
|
+
try {
|
|
958
|
+
return fs.statSync(path.join(catalogDir, n)).isDirectory();
|
|
959
|
+
} catch {
|
|
960
|
+
return false;
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
const suggestion = findClosestSkill(skillName, catalogSkills);
|
|
964
|
+
if (suggestion) {
|
|
965
|
+
console.log(c.yellow(` Did you mean: "${suggestion}"?`));
|
|
966
|
+
console.log(c.dim(' Run "contextos skill list --available" to see all catalog skills.'));
|
|
967
|
+
}
|
|
968
|
+
}
|
|
686
969
|
process.exit(1);
|
|
687
970
|
} finally {
|
|
688
971
|
if (lockToken) projectLock.release(lockToken);
|
|
@@ -802,25 +1085,46 @@ function remove(skillName) {
|
|
|
802
1085
|
}
|
|
803
1086
|
|
|
804
1087
|
/**
|
|
805
|
-
* skill list
|
|
1088
|
+
* skill list [options]
|
|
806
1089
|
*/
|
|
807
|
-
function list() {
|
|
1090
|
+
function list(options = {}) {
|
|
1091
|
+
const showAvailable = Boolean(options.available || options['--available'] || options.all);
|
|
808
1092
|
const lock = readLock();
|
|
809
1093
|
|
|
810
1094
|
// Builtin skills
|
|
811
1095
|
const builtins = fs.existsSync(CORE_SKILLS)
|
|
812
|
-
? fs.readdirSync(CORE_SKILLS).filter(n =>
|
|
1096
|
+
? fs.readdirSync(CORE_SKILLS).filter(n => {
|
|
1097
|
+
try {
|
|
1098
|
+
return fs.statSync(path.join(CORE_SKILLS, n)).isDirectory();
|
|
1099
|
+
} catch {
|
|
1100
|
+
return false;
|
|
1101
|
+
}
|
|
1102
|
+
})
|
|
813
1103
|
: [];
|
|
814
1104
|
|
|
815
1105
|
// Plugin skills
|
|
816
1106
|
const plugins = lock.plugins;
|
|
817
1107
|
|
|
1108
|
+
// Catalog skills
|
|
1109
|
+
const catalogDir = getCatalogDir();
|
|
1110
|
+
const catalogSkills = fs.existsSync(catalogDir)
|
|
1111
|
+
? fs.readdirSync(catalogDir).filter(n => {
|
|
1112
|
+
try {
|
|
1113
|
+
return fs.statSync(path.join(catalogDir, n)).isDirectory();
|
|
1114
|
+
} catch {
|
|
1115
|
+
return false;
|
|
1116
|
+
}
|
|
1117
|
+
})
|
|
1118
|
+
: [];
|
|
1119
|
+
const installedNames = new Set(plugins.map(p => p.name));
|
|
1120
|
+
const availableCatalog = catalogSkills.filter(n => !installedNames.has(n));
|
|
1121
|
+
|
|
818
1122
|
console.log('');
|
|
819
1123
|
console.log(c.bold('ContextOS Skills'));
|
|
820
1124
|
console.log('');
|
|
821
1125
|
|
|
822
1126
|
// Built-ins
|
|
823
|
-
console.log(c.bold(` Built-in (${builtins.length})`));
|
|
1127
|
+
console.log(c.bold(` Built-in Core (${builtins.length})`));
|
|
824
1128
|
for (const name of builtins) {
|
|
825
1129
|
console.log(` ${c.cyan('●')} ${name} ${c.dim('(core)')}`);
|
|
826
1130
|
}
|
|
@@ -828,22 +1132,39 @@ function list() {
|
|
|
828
1132
|
// Plugins
|
|
829
1133
|
console.log('');
|
|
830
1134
|
if (plugins.length === 0) {
|
|
831
|
-
console.log(c.bold(' Plugins (0)'));
|
|
832
|
-
console.log(c.dim(' No plugins installed yet.'));
|
|
833
|
-
console.log(c.dim(' Install one:
|
|
1135
|
+
console.log(c.bold(' Installed Plugins & Domain Skills (0)'));
|
|
1136
|
+
console.log(c.dim(' No domain skills or plugins installed yet.'));
|
|
1137
|
+
console.log(c.dim(' Install one: contextos skill add <name> (or --all)'));
|
|
834
1138
|
} else {
|
|
835
|
-
console.log(c.bold(` Plugins (${plugins.length})`));
|
|
1139
|
+
console.log(c.bold(` Installed Plugins & Domain Skills (${plugins.length})`));
|
|
836
1140
|
for (const p of plugins) {
|
|
837
1141
|
const when = p.installedAt ? new Date(p.installedAt).toLocaleDateString() : '?';
|
|
838
1142
|
console.log(` ${c.green('●')} ${p.name} ${c.dim(`← ${p.ref} (installed ${when})`)}`);
|
|
839
1143
|
}
|
|
840
1144
|
}
|
|
841
1145
|
|
|
1146
|
+
// Available in Catalog
|
|
1147
|
+
console.log('');
|
|
1148
|
+
if (showAvailable || availableCatalog.length > 0) {
|
|
1149
|
+
console.log(c.bold(` Available in Catalog (${availableCatalog.length})`));
|
|
1150
|
+
if (showAvailable) {
|
|
1151
|
+
for (const name of availableCatalog) {
|
|
1152
|
+
console.log(` ${c.dim('○')} ${name}`);
|
|
1153
|
+
}
|
|
1154
|
+
console.log('');
|
|
1155
|
+
console.log(c.dim(' Install with: contextos skill add <name> (or contextos skill add --all)'));
|
|
1156
|
+
} else {
|
|
1157
|
+
console.log(c.dim(` ${availableCatalog.length} domain skills available in local catalog.`));
|
|
1158
|
+
console.log(c.dim(' Run "contextos skill list --available" to see all, or install with:'));
|
|
1159
|
+
console.log(c.dim(' contextos skill add <name> | contextos skill add --all'));
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
|
|
842
1163
|
console.log('');
|
|
843
1164
|
}
|
|
844
1165
|
|
|
845
1166
|
/**
|
|
846
|
-
* skill search <query>
|
|
1167
|
+
* skill search <query> - searches the hosted registry.json
|
|
847
1168
|
*/
|
|
848
1169
|
async function search(query) {
|
|
849
1170
|
console.log(c.dim(`\nFetching registry from: ${REGISTRY_URL}\n`));
|
package/.agents/profiles.js
CHANGED
|
@@ -298,14 +298,26 @@ function validateProfile(profile, registry = null) {
|
|
|
298
298
|
}
|
|
299
299
|
|
|
300
300
|
/**
|
|
301
|
-
* Lists all available project profiles defined in core/profiles.
|
|
301
|
+
* Lists all available project profiles defined in core/profiles and optionally consumer project.
|
|
302
|
+
* Searches built-in profiles in packageRoot, and if projectRoot is supplied, searches
|
|
303
|
+
* consumer project .agents/profiles, .agents/core/profiles, and profiles directories.
|
|
302
304
|
*
|
|
305
|
+
* @param {string|null} [projectRoot=null] - Optional consumer project root
|
|
303
306
|
* @returns {ProfileConfig[]} Array of profile configurations
|
|
304
307
|
*/
|
|
305
|
-
function listProfiles() {
|
|
308
|
+
function listProfiles(projectRoot = null) {
|
|
306
309
|
const profiles = [];
|
|
307
310
|
const searchDirs = [PROFILES_DIR, path.join(AGENTS_DIR, 'catalog', 'presets')];
|
|
308
311
|
|
|
312
|
+
if (projectRoot) {
|
|
313
|
+
const absConsumer = path.resolve(projectRoot);
|
|
314
|
+
searchDirs.push(
|
|
315
|
+
path.join(absConsumer, '.agents', 'profiles'),
|
|
316
|
+
path.join(absConsumer, '.agents', 'core', 'profiles'),
|
|
317
|
+
path.join(absConsumer, 'profiles')
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
|
|
309
321
|
for (const dir of searchDirs) {
|
|
310
322
|
if (!fs.existsSync(dir)) continue;
|
|
311
323
|
const files = fs.readdirSync(dir).filter(f => f.endsWith('.yaml') || f.endsWith('.yml'));
|
|
@@ -314,22 +326,31 @@ function listProfiles() {
|
|
|
314
326
|
const parsed = parseYamlProfile(content);
|
|
315
327
|
if (!parsed.id) parsed.id = path.basename(file, path.extname(file));
|
|
316
328
|
if (!parsed.name) parsed.name = parsed.id;
|
|
317
|
-
|
|
329
|
+
|
|
330
|
+
// Consumer profiles can override or extend built-in profiles; deduplicate by id
|
|
331
|
+
const normId = parsed.id.toLowerCase();
|
|
332
|
+
const existingIdx = profiles.findIndex(p => p.id.toLowerCase() === normId);
|
|
333
|
+
if (existingIdx !== -1) {
|
|
334
|
+
profiles[existingIdx] = parsed;
|
|
335
|
+
} else {
|
|
336
|
+
profiles.push(parsed);
|
|
337
|
+
}
|
|
318
338
|
}
|
|
319
339
|
}
|
|
320
340
|
return profiles;
|
|
321
341
|
}
|
|
322
342
|
|
|
323
343
|
/**
|
|
324
|
-
* Finds a profile by its slug ID or title.
|
|
344
|
+
* Finds a profile by its slug ID or title across package and consumer roots.
|
|
325
345
|
*
|
|
326
346
|
* @param {string} name - Profile identifier or name
|
|
347
|
+
* @param {string|null} [projectRoot=null] - Optional consumer project root
|
|
327
348
|
* @returns {ProfileConfig|null} Found profile or null
|
|
328
349
|
*/
|
|
329
|
-
function getProfile(name) {
|
|
350
|
+
function getProfile(name, projectRoot = null) {
|
|
330
351
|
if (!name) return null;
|
|
331
352
|
const clean = name.toLowerCase().trim();
|
|
332
|
-
const all = listProfiles();
|
|
353
|
+
const all = listProfiles(projectRoot);
|
|
333
354
|
return all.find(p => p.id.toLowerCase() === clean || p.name.toLowerCase() === clean) || null;
|
|
334
355
|
}
|
|
335
356
|
|
|
@@ -398,7 +419,7 @@ function getActiveProfile(projectDir = process.cwd(), targetFileOrScope = null)
|
|
|
398
419
|
}
|
|
399
420
|
|
|
400
421
|
if (matchedProfileName) {
|
|
401
|
-
const overrideProf = getProfile(matchedProfileName);
|
|
422
|
+
const overrideProf = getProfile(matchedProfileName, projectDir);
|
|
402
423
|
if (overrideProf) {
|
|
403
424
|
return {
|
|
404
425
|
...overrideProf,
|
|
@@ -424,9 +445,9 @@ function getActiveProfile(projectDir = process.cwd(), targetFileOrScope = null)
|
|
|
424
445
|
* @returns {Object} Updated profile lock data
|
|
425
446
|
*/
|
|
426
447
|
function applyProfile(profileName, projectDir = process.cwd(), options = {}) {
|
|
427
|
-
const profile = getProfile(profileName);
|
|
448
|
+
const profile = getProfile(profileName, projectDir);
|
|
428
449
|
if (!profile) {
|
|
429
|
-
throw new Error(`Profile '${profileName}' not found. Available profiles: ${listProfiles().map(p => p.id).join(', ')}`);
|
|
450
|
+
throw new Error(`Profile '${profileName}' not found. Available profiles: ${listProfiles(projectDir).map(p => p.id).join(', ')}`);
|
|
430
451
|
}
|
|
431
452
|
|
|
432
453
|
validateProfile(profile);
|
|
@@ -563,9 +584,9 @@ function removeActiveProfile(projectDir = process.cwd(), options = {}) {
|
|
|
563
584
|
* @returns {Object} Explanation structure
|
|
564
585
|
*/
|
|
565
586
|
function explainProfile(profileName, projectDir = process.cwd()) {
|
|
566
|
-
const profile = getProfile(profileName);
|
|
587
|
+
const profile = getProfile(profileName, projectDir);
|
|
567
588
|
if (!profile) {
|
|
568
|
-
throw new Error(`Profile '${profileName}' not found. Available profiles: ${listProfiles().map(p => p.id).join(', ')}`);
|
|
589
|
+
throw new Error(`Profile '${profileName}' not found. Available profiles: ${listProfiles(projectDir).map(p => p.id).join(', ')}`);
|
|
569
590
|
}
|
|
570
591
|
|
|
571
592
|
return {
|
package/README.md
CHANGED
|
@@ -40,7 +40,16 @@ You do not need to clone anything manually. Just open your terminal in the root
|
|
|
40
40
|
npx contextos-agents init
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
By default, ContextOS sets up a focused **Lean Core** (7 essential skills: `engineering-workflow`, `ponytail-mindset`, `gemini-precision`, `security`, `context-os`, `context-manager`, and `gstack-roles`). This ensures your AI agent context remains lightweight and focused, preventing context degradation.
|
|
44
|
+
|
|
45
|
+
Want more skills right away? Install pre-packaged presets or the entire catalog:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npx contextos-agents init --preset frontend # React, Next.js, TypeScript, UI/UX, a11y
|
|
49
|
+
npx contextos-agents init --preset backend # System design, API design, Node.js, databases
|
|
50
|
+
npx contextos-agents init --preset devops # Docker, CI/CD, Terraform
|
|
51
|
+
npx contextos-agents init --all # Install all 36 catalog skills at once
|
|
52
|
+
```
|
|
44
53
|
|
|
45
54
|
### Options
|
|
46
55
|
|
|
@@ -48,6 +57,8 @@ The script will automatically detect your project tech stack, create the `.agent
|
|
|
48
57
|
npx contextos-agents --help # Show all options
|
|
49
58
|
npx contextos-agents --version # Show version
|
|
50
59
|
npx contextos-agents --minimal # Install only the core bootstrap skills
|
|
60
|
+
npx contextos-agents --all # Install all 36 catalog domain skills during init
|
|
61
|
+
npx contextos-agents --preset <name> # Install stack preset: frontend, backend, devops, full
|
|
51
62
|
npx contextos-agents --profile init # Install with specific profile
|
|
52
63
|
npx contextos-agents --auto # Auto-detect tech stack and apply recommended profile
|
|
53
64
|
npx contextos-agents --dry-run # Preview what will be installed
|
|
@@ -110,6 +121,30 @@ contextos resolve "Build an accessible modal component with React and Tailwind"
|
|
|
110
121
|
contextos resolve "security review" --files apps/web/app/login/page.tsx --explain
|
|
111
122
|
```
|
|
112
123
|
|
|
124
|
+
### Skill & Catalog Management (`contextos skill`)
|
|
125
|
+
|
|
126
|
+
Discover, install, and customize skills:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Explore all available catalog skills (36 domain skills):
|
|
130
|
+
contextos skill list --available
|
|
131
|
+
|
|
132
|
+
# Install a specific skill from the catalog (with typo suggestions):
|
|
133
|
+
contextos skill add fastapi
|
|
134
|
+
|
|
135
|
+
# Install all 36 catalog skills at once:
|
|
136
|
+
contextos skill add --all
|
|
137
|
+
|
|
138
|
+
# Fork a built-in skill into your project for team customizations:
|
|
139
|
+
contextos skill override gemini-precision
|
|
140
|
+
|
|
141
|
+
# Diff your local customizations against upstream updates:
|
|
142
|
+
contextos skill diff gemini-precision
|
|
143
|
+
|
|
144
|
+
# Eject a skill to decouple it from upstream updates:
|
|
145
|
+
contextos skill eject gemini-precision
|
|
146
|
+
```
|
|
147
|
+
|
|
113
148
|
### Diagnostic Health Check (`contextos doctor`)
|
|
114
149
|
|
|
115
150
|
Run a comprehensive pre-flight verification across your repository to ensure valid skills, profile alignment, and compiler synchronization:
|
|
@@ -172,9 +207,9 @@ jobs:
|
|
|
172
207
|
runs-on: ubuntu-latest
|
|
173
208
|
steps:
|
|
174
209
|
- uses: actions/checkout@v4
|
|
175
|
-
- uses: kok-o/contextos-agents/.github/actions/contextos-gate@v2.
|
|
210
|
+
- uses: kok-o/contextos-agents/.github/actions/contextos-gate@v2.2.0
|
|
176
211
|
with:
|
|
177
|
-
version: '2.
|
|
212
|
+
version: '2.2.0' # Pinned version of contextos-agents runner
|
|
178
213
|
adapters: 'all' # Adapters to verify (or specific: 'cursor', 'claude')
|
|
179
214
|
working-directory: '.' # Project root directory
|
|
180
215
|
```
|