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.
Files changed (38) hide show
  1. package/README.md +89 -41
  2. package/bundled-skills/.antigravity-install-manifest.json +1 -12
  3. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  4. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  5. package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
  6. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  7. package/bundled-skills/docs/users/bundles.md +1 -1
  8. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  9. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  10. package/bundled-skills/docs/users/getting-started.md +1 -1
  11. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  12. package/bundled-skills/docs/users/usage.md +4 -4
  13. package/bundled-skills/docs/users/visual-guide.md +4 -4
  14. package/dist/constants/constants.d.ts +14 -0
  15. package/dist/constants/constants.js +14 -0
  16. package/dist/index.d.ts +10 -0
  17. package/dist/index.js +24 -60
  18. package/dist/skill-pointer/index.d.ts +23 -0
  19. package/dist/skill-pointer/index.js +28 -0
  20. package/dist/skill-pointer/pointer-generator.d.ts +10 -0
  21. package/dist/skill-pointer/pointer-generator.js +69 -0
  22. package/dist/skill-pointer/vault-installer.d.ts +17 -0
  23. package/dist/skill-pointer/vault-installer.js +86 -0
  24. package/dist/utils/fs.utils.d.ts +8 -0
  25. package/dist/utils/fs.utils.js +21 -0
  26. package/package.json +9 -7
  27. package/skills_index.json +30359 -0
  28. package/bundled-skills/ui-a11y/SKILL.md +0 -77
  29. package/bundled-skills/ui-component/SKILL.md +0 -100
  30. package/bundled-skills/ui-page/SKILL.md +0 -96
  31. package/bundled-skills/ui-pattern/SKILL.md +0 -88
  32. package/bundled-skills/ui-review/SKILL.md +0 -86
  33. package/bundled-skills/ui-setup/SKILL.md +0 -100
  34. package/bundled-skills/ui-tokens/SKILL.md +0 -69
  35. package/bundled-skills/ux-audit/SKILL.md +0 -62
  36. package/bundled-skills/ux-copy/SKILL.md +0 -79
  37. package/bundled-skills/ux-feedback/SKILL.md +0 -64
  38. 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": "1.0.210",
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 <rawsar@gmail.com>",
33
+ "author": "Davide Ladisa <info@davideladisa.it>",
32
34
  "license": "MIT",
33
35
  "dependencies": {
34
- "@opencode-ai/plugin": "^1.0.0"
36
+ "@opencode-ai/plugin": "^1.4.0"
35
37
  },
36
38
  "devDependencies": {
37
- "@opencode-ai/sdk": "^1.0.0",
39
+ "@opencode-ai/sdk": "^1.4.0",
38
40
  "@types/bun": "latest",
39
- "@types/node": "^25.5.0",
41
+ "@types/node": "^25.5.2",
40
42
  "typescript": "^6.0.2"
41
43
  }
42
44
  }