opencode-skills-collection 1.0.210 → 2.0.0-beta.2
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/README.md +89 -41
- package/bundled-skills/.antigravity-install-manifest.json +1 -12
- package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
- package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
- package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
- package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
- package/bundled-skills/docs/users/bundles.md +1 -1
- package/bundled-skills/docs/users/claude-code-skills.md +1 -1
- package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
- package/bundled-skills/docs/users/getting-started.md +1 -1
- package/bundled-skills/docs/users/kiro-integration.md +1 -1
- package/bundled-skills/docs/users/usage.md +4 -4
- package/bundled-skills/docs/users/visual-guide.md +4 -4
- package/dist/constants/constants.d.ts +14 -0
- package/dist/constants/constants.js +14 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +24 -60
- package/dist/skill-pointer/index.d.ts +23 -0
- package/dist/skill-pointer/index.js +28 -0
- package/dist/skill-pointer/pointer-generator.d.ts +10 -0
- package/dist/skill-pointer/pointer-generator.js +69 -0
- package/dist/skill-pointer/vault-installer.d.ts +17 -0
- package/dist/skill-pointer/vault-installer.js +86 -0
- package/dist/utils/fs.utils.d.ts +8 -0
- package/dist/utils/fs.utils.js +21 -0
- package/package.json +9 -7
- package/skills_index.json +30359 -0
- package/bundled-skills/ui-a11y/SKILL.md +0 -77
- package/bundled-skills/ui-component/SKILL.md +0 -100
- package/bundled-skills/ui-page/SKILL.md +0 -96
- package/bundled-skills/ui-pattern/SKILL.md +0 -88
- package/bundled-skills/ui-review/SKILL.md +0 -86
- package/bundled-skills/ui-setup/SKILL.md +0 -100
- package/bundled-skills/ui-tokens/SKILL.md +0 -69
- package/bundled-skills/ux-audit/SKILL.md +0 -62
- package/bundled-skills/ux-copy/SKILL.md +0 -79
- package/bundled-skills/ux-feedback/SKILL.md +0 -64
- package/bundled-skills/ux-flow/SKILL.md +0 -68
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { POINTER_SUFFIX, SKILL_FILENAME, UNCATEGORIZED_CATEGORY } from "../constants/constants.js";
|
|
4
|
+
import { ensureDir, listSubdirectories } from "../utils/fs.utils.js";
|
|
5
|
+
function buildPointerContent(category, skills, libraryPath) {
|
|
6
|
+
const title = category
|
|
7
|
+
.replace(/-/g, " ")
|
|
8
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
9
|
+
const normalizedPath = libraryPath.replace(/\\/g, "/");
|
|
10
|
+
const skillCount = skills.length;
|
|
11
|
+
const skillList = skills
|
|
12
|
+
.map((s) => `- **${s.id}** — ${s.description || s.name}`)
|
|
13
|
+
.join("\n");
|
|
14
|
+
return `---
|
|
15
|
+
name: ${category}${POINTER_SUFFIX}
|
|
16
|
+
description: "Pointer to a library of ${skillCount} specialized ${title} skills. Use when working on ${category}-related tasks."
|
|
17
|
+
risk: safe
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# ${title} Capability Library 🎯
|
|
21
|
+
|
|
22
|
+
This is a **pointer skill**. The ${skillCount} specialized ${title} skills are stored in a hidden vault to keep your startup context minimal.
|
|
23
|
+
|
|
24
|
+
## Available skills in this category
|
|
25
|
+
|
|
26
|
+
${skillList}
|
|
27
|
+
|
|
28
|
+
## How to load a skill
|
|
29
|
+
|
|
30
|
+
1. Identify the skill name above matching your task.
|
|
31
|
+
2. Use \`view_file\` to read its \`SKILL.md\` from the vault:
|
|
32
|
+
\`${normalizedPath}/<skill-name>/SKILL.md\`
|
|
33
|
+
3. Follow those instructions to complete the request.
|
|
34
|
+
|
|
35
|
+
**Vault path:** \`${normalizedPath}\`
|
|
36
|
+
|
|
37
|
+
> Do not guess best practices — always read from the vault first.
|
|
38
|
+
`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Scans every category directory in the vault and writes
|
|
42
|
+
* a lightweight pointer SKILL.md into the active skills directory.
|
|
43
|
+
*
|
|
44
|
+
* Each pointer includes the full list of skill names + descriptions
|
|
45
|
+
* so keyword searches (e.g. "laravel", "wordpress") resolve correctly
|
|
46
|
+
* via get_available_skills without loading every SKILL.md.
|
|
47
|
+
*/
|
|
48
|
+
export function generatePointers(activeSkillsDir, vaultDir, index = []) {
|
|
49
|
+
const categoryDirs = listSubdirectories(vaultDir);
|
|
50
|
+
const byCategory = new Map();
|
|
51
|
+
for (const entry of index) {
|
|
52
|
+
const cat = entry.category ?? UNCATEGORIZED_CATEGORY;
|
|
53
|
+
if (!byCategory.has(cat))
|
|
54
|
+
byCategory.set(cat, []);
|
|
55
|
+
byCategory.get(cat).push(entry);
|
|
56
|
+
}
|
|
57
|
+
for (const categoryName of categoryDirs) {
|
|
58
|
+
const categoryVaultPath = path.join(vaultDir, categoryName);
|
|
59
|
+
const skills = byCategory.get(categoryName) ?? [];
|
|
60
|
+
if (skills.length === 0) {
|
|
61
|
+
const subDirs = fs.readdirSync(categoryVaultPath).filter((e) => fs.statSync(path.join(categoryVaultPath, e)).isDirectory());
|
|
62
|
+
if (subDirs.length === 0)
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const pointerDir = path.join(activeSkillsDir, `${categoryName}${POINTER_SUFFIX}`);
|
|
66
|
+
ensureDir(pointerDir);
|
|
67
|
+
fs.writeFileSync(path.join(pointerDir, SKILL_FILENAME), buildPointerContent(categoryName, skills, categoryVaultPath), "utf-8");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface SkillIndexEntry {
|
|
2
|
+
id: string;
|
|
3
|
+
category: string;
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Loads the pre-built skills_index.json from the project root.
|
|
9
|
+
* Falls back to a dynamically generated index from SKILL.md frontmatter
|
|
10
|
+
* when the file is missing, so the plugin always works correctly.
|
|
11
|
+
*/
|
|
12
|
+
export declare function loadSkillsIndex(bundledSkillsPath: string): SkillIndexEntry[];
|
|
13
|
+
/**
|
|
14
|
+
* Copies every skill folder from bundledSkillsPath directly into
|
|
15
|
+
* the vault under the appropriate category sub-directory.
|
|
16
|
+
*/
|
|
17
|
+
export declare function installSkillsToVault(bundledSkillsPath: string, vaultDir: string, index: SkillIndexEntry[]): void;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { ensureDir } from "../utils/fs.utils.js";
|
|
4
|
+
import { UNCATEGORIZED_CATEGORY } from "../constants/constants.js";
|
|
5
|
+
/**
|
|
6
|
+
* Extracts a frontmatter field value from a SKILL.md string.
|
|
7
|
+
* Handles both quoted and unquoted values.
|
|
8
|
+
*/
|
|
9
|
+
function parseFrontmatterField(content, field) {
|
|
10
|
+
const match = content.match(new RegExp(`^${field}:\\s*["']?([^"'\\n]+)["']?`, "m"));
|
|
11
|
+
return match ? match[1].trim() : "";
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Derives a category slug from a skill folder name by taking the
|
|
15
|
+
* first hyphen-separated segment (e.g. "laravel-expert" → "laravel",
|
|
16
|
+
* "wordpress-core" → "wordpress", "php-pro" → "php").
|
|
17
|
+
* Falls back to UNCATEGORIZED_CATEGORY for single-word names.
|
|
18
|
+
*/
|
|
19
|
+
function categoryFromFolderName(folderName) {
|
|
20
|
+
const parts = folderName.split("-");
|
|
21
|
+
return parts.length > 1 ? parts[0] : UNCATEGORIZED_CATEGORY;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Builds a SkillIndexEntry[] by scanning every skill folder in
|
|
25
|
+
* bundledSkillsPath and reading its SKILL.md frontmatter.
|
|
26
|
+
* This is used as fallback when skills_index.json is absent.
|
|
27
|
+
*/
|
|
28
|
+
function buildIndexFromBundledSkills(bundledSkillsPath) {
|
|
29
|
+
const index = [];
|
|
30
|
+
for (const entry of fs.readdirSync(bundledSkillsPath)) {
|
|
31
|
+
if (entry.startsWith(".") || entry === "skills_index.json" || entry === "README.md")
|
|
32
|
+
continue;
|
|
33
|
+
const skillDir = path.join(bundledSkillsPath, entry);
|
|
34
|
+
if (!fs.statSync(skillDir).isDirectory())
|
|
35
|
+
continue;
|
|
36
|
+
const skillMdPath = path.join(skillDir, "SKILL.md");
|
|
37
|
+
if (!fs.existsSync(skillMdPath))
|
|
38
|
+
continue;
|
|
39
|
+
const content = fs.readFileSync(skillMdPath, "utf-8");
|
|
40
|
+
const name = parseFrontmatterField(content, "name") || entry;
|
|
41
|
+
const description = parseFrontmatterField(content, "description") || name;
|
|
42
|
+
const category = parseFrontmatterField(content, "category") || categoryFromFolderName(entry);
|
|
43
|
+
index.push({ id: entry, category, name, description });
|
|
44
|
+
}
|
|
45
|
+
return index;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Loads the pre-built skills_index.json from the project root.
|
|
49
|
+
* Falls back to a dynamically generated index from SKILL.md frontmatter
|
|
50
|
+
* when the file is missing, so the plugin always works correctly.
|
|
51
|
+
*/
|
|
52
|
+
export function loadSkillsIndex(bundledSkillsPath) {
|
|
53
|
+
const indexPath = path.join(bundledSkillsPath, "..", "skills_index.json");
|
|
54
|
+
if (fs.existsSync(indexPath)) {
|
|
55
|
+
try {
|
|
56
|
+
const raw = fs.readFileSync(indexPath, "utf-8");
|
|
57
|
+
return JSON.parse(raw);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// fall through to dynamic generation
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return buildIndexFromBundledSkills(bundledSkillsPath);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Copies every skill folder from bundledSkillsPath directly into
|
|
67
|
+
* the vault under the appropriate category sub-directory.
|
|
68
|
+
*/
|
|
69
|
+
export function installSkillsToVault(bundledSkillsPath, vaultDir, index) {
|
|
70
|
+
if (!fs.existsSync(bundledSkillsPath))
|
|
71
|
+
return;
|
|
72
|
+
const categoryMap = new Map(index.map((e) => [e.id, e.category ?? UNCATEGORIZED_CATEGORY]));
|
|
73
|
+
for (const entry of fs.readdirSync(bundledSkillsPath)) {
|
|
74
|
+
if (entry.startsWith(".") ||
|
|
75
|
+
entry === "skills_index.json" ||
|
|
76
|
+
entry === "README.md")
|
|
77
|
+
continue;
|
|
78
|
+
const srcPath = path.join(bundledSkillsPath, entry);
|
|
79
|
+
if (!fs.statSync(srcPath).isDirectory())
|
|
80
|
+
continue;
|
|
81
|
+
const category = categoryMap.get(entry) ?? UNCATEGORIZED_CATEGORY;
|
|
82
|
+
const destPath = path.join(vaultDir, category, entry);
|
|
83
|
+
ensureDir(path.join(vaultDir, category));
|
|
84
|
+
fs.cpSync(srcPath, destPath, { recursive: true, force: true });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensures a directory exists, creating it recursively if needed.
|
|
3
|
+
*/
|
|
4
|
+
export declare function ensureDir(dirPath: string): void;
|
|
5
|
+
/**
|
|
6
|
+
* Returns the names of all direct child directories inside a given path.
|
|
7
|
+
*/
|
|
8
|
+
export declare function listSubdirectories(dirPath: string): string[];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
/**
|
|
4
|
+
* Ensures a directory exists, creating it recursively if needed.
|
|
5
|
+
*/
|
|
6
|
+
export function ensureDir(dirPath) {
|
|
7
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Returns the names of all direct child directories inside a given path.
|
|
11
|
+
*/
|
|
12
|
+
export function listSubdirectories(dirPath) {
|
|
13
|
+
if (!fs.existsSync(dirPath))
|
|
14
|
+
return [];
|
|
15
|
+
return fs
|
|
16
|
+
.readdirSync(dirPath)
|
|
17
|
+
.filter((entry) => {
|
|
18
|
+
const fullPath = path.join(dirPath, entry);
|
|
19
|
+
return fs.statSync(fullPath).isDirectory();
|
|
20
|
+
});
|
|
21
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-skills-collection",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-beta.2",
|
|
4
4
|
"description": "OpenCode CLI plugin that automatically downloads and keeps skills up to date.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"files": [
|
|
8
8
|
"dist",
|
|
9
|
-
"bundled-skills"
|
|
9
|
+
"bundled-skills",
|
|
10
|
+
"skills_index.json"
|
|
10
11
|
],
|
|
11
12
|
"repository": {
|
|
12
13
|
"type": "git",
|
|
@@ -23,20 +24,21 @@
|
|
|
23
24
|
"keywords": [
|
|
24
25
|
"opencode",
|
|
25
26
|
"opencode-plugin",
|
|
26
|
-
"antigravity",
|
|
27
27
|
"skills",
|
|
28
|
+
"skills-collection",
|
|
29
|
+
"skills-pointer",
|
|
28
30
|
"ai",
|
|
29
31
|
"agent"
|
|
30
32
|
],
|
|
31
|
-
"author": "Davide Ladisa <
|
|
33
|
+
"author": "Davide Ladisa <info@davideladisa.it>",
|
|
32
34
|
"license": "MIT",
|
|
33
35
|
"dependencies": {
|
|
34
|
-
"@opencode-ai/plugin": "^1.
|
|
36
|
+
"@opencode-ai/plugin": "^1.4.0"
|
|
35
37
|
},
|
|
36
38
|
"devDependencies": {
|
|
37
|
-
"@opencode-ai/sdk": "^1.
|
|
39
|
+
"@opencode-ai/sdk": "^1.4.0",
|
|
38
40
|
"@types/bun": "latest",
|
|
39
|
-
"@types/node": "^25.5.
|
|
41
|
+
"@types/node": "^25.5.2",
|
|
40
42
|
"typescript": "^6.0.2"
|
|
41
43
|
}
|
|
42
44
|
}
|