skill-tree 0.1.5 → 0.1.6
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/dist/cli/index.js +1827 -1410
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/index.mjs +9539 -1658
- package/dist/cli/index.mjs.map +1 -0
- package/dist/index.d.mts +316 -7
- package/dist/index.d.ts +316 -7
- package/dist/index.js +1539 -92
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +9614 -70
- package/dist/index.mjs.map +1 -0
- package/package.json +2 -1
- package/dist/chunk-3SRB47JW.mjs +0 -8344
- package/dist/chunk-43YOKLZP.mjs +0 -6081
- package/dist/chunk-4AGZU52D.mjs +0 -7918
- package/dist/chunk-4OC5QFIF.mjs +0 -11267
- package/dist/chunk-55SMGVTP.mjs +0 -7126
- package/dist/chunk-6FX4IK4Z.mjs +0 -5368
- package/dist/chunk-7EGDKOHV.mjs +0 -9439
- package/dist/chunk-7LMOQW5H.mjs +0 -4893
- package/dist/chunk-7QIQJVNP.mjs +0 -14206
- package/dist/chunk-7VB4ZRZO.mjs +0 -7127
- package/dist/chunk-BPVRW25O.mjs +0 -6089
- package/dist/chunk-CI4476KM.mjs +0 -6607
- package/dist/chunk-DDXYQ74I.mjs +0 -13969
- package/dist/chunk-DQOFJXBX.mjs +0 -6595
- package/dist/chunk-E2CVK23F.mjs +0 -8751
- package/dist/chunk-F3YEUQAP.mjs +0 -654
- package/dist/chunk-FKJJ4RJG.mjs +0 -13874
- package/dist/chunk-II7DECZQ.mjs +0 -9111
- package/dist/chunk-INKVOZXK.mjs +0 -15898
- package/dist/chunk-K6NRCSAZ.mjs +0 -4355
- package/dist/chunk-OYHYXKXO.mjs +0 -7297
- package/dist/chunk-PDPN7FW7.mjs +0 -1045
- package/dist/chunk-TEUB6DZR.mjs +0 -6453
- package/dist/chunk-TWPEHDW4.mjs +0 -1067
- package/dist/chunk-Y54UK2J3.mjs +0 -13071
- package/dist/chunk-ZQVS7MQK.mjs +0 -6081
- package/dist/sqlite-OLU72GHB.mjs +0 -6
- package/dist/sqlite-XJRPMNAJ.mjs +0 -6
- package/dist/sync-BSWMMDA6.mjs +0 -14
package/dist/cli/index.js
CHANGED
|
@@ -1777,7 +1777,7 @@ var init_sync = __esm({
|
|
|
1777
1777
|
});
|
|
1778
1778
|
|
|
1779
1779
|
// src/cli/index.ts
|
|
1780
|
-
var
|
|
1780
|
+
var import_commander39 = require("commander");
|
|
1781
1781
|
|
|
1782
1782
|
// src/types.ts
|
|
1783
1783
|
function hasTaxonomySupport(storage) {
|
|
@@ -2852,8 +2852,214 @@ var SyncManager = class {
|
|
|
2852
2852
|
}
|
|
2853
2853
|
};
|
|
2854
2854
|
|
|
2855
|
-
// src/serving/
|
|
2855
|
+
// src/serving/xml-utils.ts
|
|
2856
|
+
function escapeXml(text) {
|
|
2857
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2858
|
+
}
|
|
2859
|
+
function getSkillSummary(skill, maxLength = 150) {
|
|
2860
|
+
if (skill.serving?.summary) {
|
|
2861
|
+
return skill.serving.summary;
|
|
2862
|
+
}
|
|
2863
|
+
const firstSentence = skill.description.split(/[.!?]/)[0];
|
|
2864
|
+
if (firstSentence.length <= maxLength) {
|
|
2865
|
+
return firstSentence;
|
|
2866
|
+
}
|
|
2867
|
+
return skill.description.substring(0, maxLength - 3) + "...";
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2870
|
+
// src/serving/catalog-renderer.ts
|
|
2871
|
+
function hasCatalogSupport(storage) {
|
|
2872
|
+
const s = storage;
|
|
2873
|
+
return typeof s.getTaxonomyTree === "function" && typeof s.getSkillsInTaxonomyNode === "function";
|
|
2874
|
+
}
|
|
2875
|
+
var UNTAGGED_CATEGORY = "other";
|
|
2876
|
+
var CATALOG_USAGE_LINES = [
|
|
2877
|
+
" Use skill_browse to explore categories.",
|
|
2878
|
+
" Use skill_search to find by keyword.",
|
|
2879
|
+
" Use skill_expand to load a skill into your context."
|
|
2880
|
+
];
|
|
2856
2881
|
var DEFAULT_CONFIG = {
|
|
2882
|
+
maxCategoriesPerLevel: 12,
|
|
2883
|
+
maxSkillsAtLeaf: 8,
|
|
2884
|
+
maxSummaryLength: 80,
|
|
2885
|
+
format: "xml"
|
|
2886
|
+
};
|
|
2887
|
+
var CatalogRenderer = class _CatalogRenderer {
|
|
2888
|
+
constructor(storage, config2) {
|
|
2889
|
+
this.storage = storage;
|
|
2890
|
+
this.overviewCache = null;
|
|
2891
|
+
this.config = { ...DEFAULT_CONFIG, ...config2 };
|
|
2892
|
+
}
|
|
2893
|
+
static {
|
|
2894
|
+
this.CACHE_TTL_MS = 6e4;
|
|
2895
|
+
}
|
|
2896
|
+
/**
|
|
2897
|
+
* Render level-0 catalog overview for system prompt injection.
|
|
2898
|
+
* Shows top-level categories with counts. ~200 tokens.
|
|
2899
|
+
* Returns empty string if no skills exist.
|
|
2900
|
+
* Results are cached for 60 seconds to avoid repeated storage queries.
|
|
2901
|
+
*/
|
|
2902
|
+
async renderOverview() {
|
|
2903
|
+
if (this.overviewCache && Date.now() - this.overviewCache.timestamp < _CatalogRenderer.CACHE_TTL_MS) {
|
|
2904
|
+
return this.overviewCache.value;
|
|
2905
|
+
}
|
|
2906
|
+
let result;
|
|
2907
|
+
if (hasCatalogSupport(this.storage)) {
|
|
2908
|
+
const tree = await this.storage.getTaxonomyTree();
|
|
2909
|
+
if (tree.length > 0) {
|
|
2910
|
+
result = this.renderOverviewFromNodes(tree);
|
|
2911
|
+
this.overviewCache = { value: result, timestamp: Date.now() };
|
|
2912
|
+
return result;
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
result = await this.renderOverviewFromTags();
|
|
2916
|
+
this.overviewCache = { value: result, timestamp: Date.now() };
|
|
2917
|
+
return result;
|
|
2918
|
+
}
|
|
2919
|
+
/**
|
|
2920
|
+
* Render a specific category path for browse drill-down.
|
|
2921
|
+
* Shows subcategories at intermediate nodes, or skill summaries at leaf nodes.
|
|
2922
|
+
*/
|
|
2923
|
+
async renderCategory(path18) {
|
|
2924
|
+
if (hasCatalogSupport(this.storage)) {
|
|
2925
|
+
return this.renderCategoryFromTaxonomy(this.storage, path18);
|
|
2926
|
+
}
|
|
2927
|
+
return this.renderCategoryFromTags(path18);
|
|
2928
|
+
}
|
|
2929
|
+
/**
|
|
2930
|
+
* Invalidate the overview cache (e.g., after skill changes).
|
|
2931
|
+
*/
|
|
2932
|
+
invalidateCache() {
|
|
2933
|
+
this.overviewCache = null;
|
|
2934
|
+
}
|
|
2935
|
+
// ===========================================================================
|
|
2936
|
+
// OVERVIEW RENDERING (shared structure)
|
|
2937
|
+
// ===========================================================================
|
|
2938
|
+
/**
|
|
2939
|
+
* Render the overview XML shell with a list of categories.
|
|
2940
|
+
* Used by both taxonomy-based and tag-based paths.
|
|
2941
|
+
*/
|
|
2942
|
+
renderOverviewXml(totalSkills, categories) {
|
|
2943
|
+
const lines = [];
|
|
2944
|
+
lines.push(`<skill_catalog total="${totalSkills}">`);
|
|
2945
|
+
lines.push(" <usage>");
|
|
2946
|
+
lines.push(...CATALOG_USAGE_LINES);
|
|
2947
|
+
lines.push(" </usage>");
|
|
2948
|
+
lines.push(" <categories>");
|
|
2949
|
+
for (const cat of categories) {
|
|
2950
|
+
lines.push(` <category path="${escapeXml(cat.name)}" count="${cat.count}" />`);
|
|
2951
|
+
}
|
|
2952
|
+
lines.push(" </categories>");
|
|
2953
|
+
lines.push("</skill_catalog>");
|
|
2954
|
+
return lines.join("\n");
|
|
2955
|
+
}
|
|
2956
|
+
// ===========================================================================
|
|
2957
|
+
// TAXONOMY-BASED RENDERING
|
|
2958
|
+
// ===========================================================================
|
|
2959
|
+
renderOverviewFromNodes(roots) {
|
|
2960
|
+
const counted = this.withCounts(roots);
|
|
2961
|
+
const totalSkills = counted.reduce((sum, c) => sum + c.count, 0);
|
|
2962
|
+
const categories = counted.sort((a, b) => b.count - a.count).slice(0, this.config.maxCategoriesPerLevel).map((c) => ({ name: c.node.name, count: c.count }));
|
|
2963
|
+
return this.renderOverviewXml(totalSkills, categories);
|
|
2964
|
+
}
|
|
2965
|
+
async renderCategoryFromTaxonomy(storage, path18) {
|
|
2966
|
+
const tree = await storage.getTaxonomyTree(path18);
|
|
2967
|
+
const pathStr = path18.join("/");
|
|
2968
|
+
if (tree.length > 0 && tree.some((n) => n.children.length > 0)) {
|
|
2969
|
+
const root = tree[0];
|
|
2970
|
+
const rootCount = this.countNodeSkills(root);
|
|
2971
|
+
const counted = this.withCounts(root.children);
|
|
2972
|
+
const children = counted.sort((a, b) => b.count - a.count).slice(0, this.config.maxCategoriesPerLevel);
|
|
2973
|
+
const lines = [];
|
|
2974
|
+
lines.push(`<catalog_browse path="${escapeXml(pathStr)}" count="${rootCount}">`);
|
|
2975
|
+
lines.push(" <subcategories>");
|
|
2976
|
+
for (const { node: child, count } of children) {
|
|
2977
|
+
const childPath = [...path18, child.name].join("/");
|
|
2978
|
+
lines.push(` <category path="${escapeXml(childPath)}" count="${count}" />`);
|
|
2979
|
+
}
|
|
2980
|
+
lines.push(" </subcategories>");
|
|
2981
|
+
lines.push("</catalog_browse>");
|
|
2982
|
+
return lines.join("\n");
|
|
2983
|
+
}
|
|
2984
|
+
if (tree.length > 0) {
|
|
2985
|
+
const nodeId = tree[0].id;
|
|
2986
|
+
const skills = await storage.getSkillsInTaxonomyNode(nodeId);
|
|
2987
|
+
return this.renderLeafSkills(skills, pathStr, tree[0].skillCount);
|
|
2988
|
+
}
|
|
2989
|
+
return `<catalog_browse path="${escapeXml(pathStr)}" count="0" />`;
|
|
2990
|
+
}
|
|
2991
|
+
// ===========================================================================
|
|
2992
|
+
// TAG-BASED FALLBACK
|
|
2993
|
+
// ===========================================================================
|
|
2994
|
+
async renderOverviewFromTags() {
|
|
2995
|
+
const skills = await this.storage.listSkills({ status: ["active"] });
|
|
2996
|
+
if (skills.length === 0) return "";
|
|
2997
|
+
const tagCounts = this.groupByPrimaryTag(skills);
|
|
2998
|
+
const categories = Array.from(tagCounts.entries()).sort((a, b) => b[1] - a[1]).slice(0, this.config.maxCategoriesPerLevel).map(([name, count]) => ({ name, count }));
|
|
2999
|
+
return this.renderOverviewXml(skills.length, categories);
|
|
3000
|
+
}
|
|
3001
|
+
async renderCategoryFromTags(path18) {
|
|
3002
|
+
if (path18.length === 0) {
|
|
3003
|
+
return this.renderOverviewFromTags();
|
|
3004
|
+
}
|
|
3005
|
+
const tag = path18[0];
|
|
3006
|
+
const matching = await this.storage.listSkills({ status: ["active"], tags: [tag] });
|
|
3007
|
+
const pathStr = path18.join("/");
|
|
3008
|
+
return this.renderLeafSkills(matching, pathStr, matching.length);
|
|
3009
|
+
}
|
|
3010
|
+
// ===========================================================================
|
|
3011
|
+
// SHARED RENDERING HELPERS
|
|
3012
|
+
// ===========================================================================
|
|
3013
|
+
renderLeafSkills(skills, pathStr, totalCount) {
|
|
3014
|
+
const displayed = skills.slice(0, this.config.maxSkillsAtLeaf);
|
|
3015
|
+
const remaining = totalCount - displayed.length;
|
|
3016
|
+
const lines = [];
|
|
3017
|
+
lines.push(`<catalog_browse path="${escapeXml(pathStr)}" count="${totalCount}">`);
|
|
3018
|
+
lines.push(" <skills>");
|
|
3019
|
+
for (const skill of displayed) {
|
|
3020
|
+
const tags = skill.tags.length > 0 ? ` tags="${escapeXml(skill.tags.join(", "))}"` : "";
|
|
3021
|
+
const desc = getSkillSummary(skill, this.config.maxSummaryLength);
|
|
3022
|
+
lines.push(` <skill id="${escapeXml(skill.id)}"${tags}>`);
|
|
3023
|
+
lines.push(` ${escapeXml(desc)}`);
|
|
3024
|
+
lines.push(" </skill>");
|
|
3025
|
+
}
|
|
3026
|
+
lines.push(" </skills>");
|
|
3027
|
+
if (remaining > 0) {
|
|
3028
|
+
lines.push(` <more remaining="${remaining}">`);
|
|
3029
|
+
lines.push(" Use skill_search for more, or skill_expand {id} to load.");
|
|
3030
|
+
lines.push(" </more>");
|
|
3031
|
+
}
|
|
3032
|
+
lines.push("</catalog_browse>");
|
|
3033
|
+
return lines.join("\n");
|
|
3034
|
+
}
|
|
3035
|
+
// ===========================================================================
|
|
3036
|
+
// UTILITIES
|
|
3037
|
+
// ===========================================================================
|
|
3038
|
+
groupByPrimaryTag(skills) {
|
|
3039
|
+
const tagCounts = /* @__PURE__ */ new Map();
|
|
3040
|
+
for (const skill of skills) {
|
|
3041
|
+
const tag = skill.tags[0] || UNTAGGED_CATEGORY;
|
|
3042
|
+
tagCounts.set(tag, (tagCounts.get(tag) || 0) + 1);
|
|
3043
|
+
}
|
|
3044
|
+
return tagCounts;
|
|
3045
|
+
}
|
|
3046
|
+
/**
|
|
3047
|
+
* Pre-compute counts for a list of nodes, avoiding redundant recursive walks.
|
|
3048
|
+
*/
|
|
3049
|
+
withCounts(nodes) {
|
|
3050
|
+
return nodes.map((node) => ({ node, count: this.countNodeSkills(node) }));
|
|
3051
|
+
}
|
|
3052
|
+
countNodeSkills(node) {
|
|
3053
|
+
let count = node.skillCount;
|
|
3054
|
+
for (const child of node.children) {
|
|
3055
|
+
count += this.countNodeSkills(child);
|
|
3056
|
+
}
|
|
3057
|
+
return count;
|
|
3058
|
+
}
|
|
3059
|
+
};
|
|
3060
|
+
|
|
3061
|
+
// src/serving/loadout-compiler.ts
|
|
3062
|
+
var DEFAULT_CONFIG2 = {
|
|
2857
3063
|
defaultMaxSkills: 15,
|
|
2858
3064
|
defaultStatus: ["active"],
|
|
2859
3065
|
semanticThreshold: 0.6
|
|
@@ -2862,7 +3068,7 @@ var LoadoutCompiler = class {
|
|
|
2862
3068
|
constructor(storage, config2) {
|
|
2863
3069
|
this.storage = storage;
|
|
2864
3070
|
this.config = {
|
|
2865
|
-
...
|
|
3071
|
+
...DEFAULT_CONFIG2,
|
|
2866
3072
|
...config2
|
|
2867
3073
|
};
|
|
2868
3074
|
}
|
|
@@ -3083,11 +3289,76 @@ var LoadoutCompiler = class {
|
|
|
3083
3289
|
// src/serving/project-detector.ts
|
|
3084
3290
|
var import_fs = require("fs");
|
|
3085
3291
|
var import_path = require("path");
|
|
3086
|
-
var
|
|
3292
|
+
var ProjectDetector = class _ProjectDetector {
|
|
3087
3293
|
constructor() {
|
|
3088
3294
|
/** Cache for project context */
|
|
3089
3295
|
this.cache = /* @__PURE__ */ new Map();
|
|
3090
3296
|
}
|
|
3297
|
+
static {
|
|
3298
|
+
/** Project type patterns */
|
|
3299
|
+
this.PROJECT_TYPES = [
|
|
3300
|
+
{ manifestFile: "package.json", type: "nodejs", tags: ["nodejs", "javascript"], packageManager: "npm" },
|
|
3301
|
+
{ manifestFile: "pyproject.toml", type: "python", tags: ["python"], packageManager: "pip" },
|
|
3302
|
+
{ manifestFile: "requirements.txt", type: "python", tags: ["python"], packageManager: "pip" },
|
|
3303
|
+
{ manifestFile: "Cargo.toml", type: "rust", tags: ["rust"], packageManager: "cargo" },
|
|
3304
|
+
{ manifestFile: "go.mod", type: "go", tags: ["go", "golang"] },
|
|
3305
|
+
{ manifestFile: "pom.xml", type: "java", tags: ["java", "maven"], packageManager: "maven" },
|
|
3306
|
+
{ manifestFile: "build.gradle", type: "java", tags: ["java", "gradle"], packageManager: "gradle" },
|
|
3307
|
+
{ manifestFile: "build.gradle.kts", type: "kotlin", tags: ["kotlin", "gradle"], packageManager: "gradle" }
|
|
3308
|
+
];
|
|
3309
|
+
}
|
|
3310
|
+
static {
|
|
3311
|
+
/** TypeScript detection */
|
|
3312
|
+
this.TYPESCRIPT_FILES = ["tsconfig.json", "tsconfig.base.json"];
|
|
3313
|
+
}
|
|
3314
|
+
static {
|
|
3315
|
+
/** Node.js framework patterns */
|
|
3316
|
+
this.NODE_FRAMEWORKS = [
|
|
3317
|
+
{ name: "react", packageName: "react", tags: ["react", "frontend"] },
|
|
3318
|
+
{ name: "next", packageName: "next", tags: ["nextjs", "react", "fullstack"] },
|
|
3319
|
+
{ name: "vue", packageName: "vue", tags: ["vue", "frontend"] },
|
|
3320
|
+
{ name: "nuxt", packageName: "nuxt", tags: ["nuxt", "vue", "fullstack"] },
|
|
3321
|
+
{ name: "angular", packageName: "@angular/core", tags: ["angular", "frontend"] },
|
|
3322
|
+
{ name: "svelte", packageName: "svelte", tags: ["svelte", "frontend"] },
|
|
3323
|
+
{ name: "express", packageName: "express", tags: ["express", "backend", "api"] },
|
|
3324
|
+
{ name: "fastify", packageName: "fastify", tags: ["fastify", "backend", "api"] },
|
|
3325
|
+
{ name: "nestjs", packageName: "@nestjs/core", tags: ["nestjs", "backend", "api"] },
|
|
3326
|
+
{ name: "hono", packageName: "hono", tags: ["hono", "backend", "api"] },
|
|
3327
|
+
{ name: "prisma", packageName: "@prisma/client", tags: ["prisma", "database", "orm"] },
|
|
3328
|
+
{ name: "drizzle", packageName: "drizzle-orm", tags: ["drizzle", "database", "orm"] },
|
|
3329
|
+
{ name: "typeorm", packageName: "typeorm", tags: ["typeorm", "database", "orm"] },
|
|
3330
|
+
{ name: "jest", packageName: "jest", tags: ["testing", "jest"] },
|
|
3331
|
+
{ name: "vitest", packageName: "vitest", tags: ["testing", "vitest"] },
|
|
3332
|
+
{ name: "playwright", packageName: "@playwright/test", tags: ["testing", "e2e", "playwright"] },
|
|
3333
|
+
{ name: "cypress", packageName: "cypress", tags: ["testing", "e2e", "cypress"] }
|
|
3334
|
+
];
|
|
3335
|
+
}
|
|
3336
|
+
static {
|
|
3337
|
+
/** Python framework patterns (from pyproject.toml or requirements.txt) */
|
|
3338
|
+
this.PYTHON_FRAMEWORKS = [
|
|
3339
|
+
{ name: "fastapi", packageName: "fastapi", tags: ["fastapi", "backend", "api"] },
|
|
3340
|
+
{ name: "django", packageName: "django", tags: ["django", "backend", "fullstack"] },
|
|
3341
|
+
{ name: "flask", packageName: "flask", tags: ["flask", "backend", "api"] },
|
|
3342
|
+
{ name: "sqlalchemy", packageName: "sqlalchemy", tags: ["sqlalchemy", "database", "orm"] },
|
|
3343
|
+
{ name: "pytest", packageName: "pytest", tags: ["testing", "pytest"] },
|
|
3344
|
+
{ name: "pydantic", packageName: "pydantic", tags: ["pydantic", "validation"] }
|
|
3345
|
+
];
|
|
3346
|
+
}
|
|
3347
|
+
static {
|
|
3348
|
+
/** Directory patterns */
|
|
3349
|
+
this.DIRECTORY_PATTERNS = [
|
|
3350
|
+
{ pattern: ".github/workflows", feature: "github-actions", tags: ["ci", "github-actions"] },
|
|
3351
|
+
{ pattern: ".gitlab-ci.yml", feature: "gitlab-ci", tags: ["ci", "gitlab"] },
|
|
3352
|
+
{ pattern: "Dockerfile", feature: "docker", tags: ["docker", "containers"] },
|
|
3353
|
+
{ pattern: "docker-compose.yml", feature: "docker-compose", tags: ["docker", "containers"] },
|
|
3354
|
+
{ pattern: "docker-compose.yaml", feature: "docker-compose", tags: ["docker", "containers"] },
|
|
3355
|
+
{ pattern: "terraform", feature: "terraform", tags: ["terraform", "infrastructure"] },
|
|
3356
|
+
{ pattern: "kubernetes", feature: "kubernetes", tags: ["kubernetes", "infrastructure"] },
|
|
3357
|
+
{ pattern: "k8s", feature: "kubernetes", tags: ["kubernetes", "infrastructure"] },
|
|
3358
|
+
{ pattern: ".env.example", feature: "env-config", tags: ["configuration"] },
|
|
3359
|
+
{ pattern: "prisma/schema.prisma", feature: "prisma", tags: ["prisma", "database"] }
|
|
3360
|
+
];
|
|
3361
|
+
}
|
|
3091
3362
|
/**
|
|
3092
3363
|
* Detect project context from a directory
|
|
3093
3364
|
*/
|
|
@@ -3249,71 +3520,15 @@ var _ProjectDetector = class _ProjectDetector {
|
|
|
3249
3520
|
}
|
|
3250
3521
|
}
|
|
3251
3522
|
};
|
|
3252
|
-
/** Project type patterns */
|
|
3253
|
-
_ProjectDetector.PROJECT_TYPES = [
|
|
3254
|
-
{ manifestFile: "package.json", type: "nodejs", tags: ["nodejs", "javascript"], packageManager: "npm" },
|
|
3255
|
-
{ manifestFile: "pyproject.toml", type: "python", tags: ["python"], packageManager: "pip" },
|
|
3256
|
-
{ manifestFile: "requirements.txt", type: "python", tags: ["python"], packageManager: "pip" },
|
|
3257
|
-
{ manifestFile: "Cargo.toml", type: "rust", tags: ["rust"], packageManager: "cargo" },
|
|
3258
|
-
{ manifestFile: "go.mod", type: "go", tags: ["go", "golang"] },
|
|
3259
|
-
{ manifestFile: "pom.xml", type: "java", tags: ["java", "maven"], packageManager: "maven" },
|
|
3260
|
-
{ manifestFile: "build.gradle", type: "java", tags: ["java", "gradle"], packageManager: "gradle" },
|
|
3261
|
-
{ manifestFile: "build.gradle.kts", type: "kotlin", tags: ["kotlin", "gradle"], packageManager: "gradle" }
|
|
3262
|
-
];
|
|
3263
|
-
/** TypeScript detection */
|
|
3264
|
-
_ProjectDetector.TYPESCRIPT_FILES = ["tsconfig.json", "tsconfig.base.json"];
|
|
3265
|
-
/** Node.js framework patterns */
|
|
3266
|
-
_ProjectDetector.NODE_FRAMEWORKS = [
|
|
3267
|
-
{ name: "react", packageName: "react", tags: ["react", "frontend"] },
|
|
3268
|
-
{ name: "next", packageName: "next", tags: ["nextjs", "react", "fullstack"] },
|
|
3269
|
-
{ name: "vue", packageName: "vue", tags: ["vue", "frontend"] },
|
|
3270
|
-
{ name: "nuxt", packageName: "nuxt", tags: ["nuxt", "vue", "fullstack"] },
|
|
3271
|
-
{ name: "angular", packageName: "@angular/core", tags: ["angular", "frontend"] },
|
|
3272
|
-
{ name: "svelte", packageName: "svelte", tags: ["svelte", "frontend"] },
|
|
3273
|
-
{ name: "express", packageName: "express", tags: ["express", "backend", "api"] },
|
|
3274
|
-
{ name: "fastify", packageName: "fastify", tags: ["fastify", "backend", "api"] },
|
|
3275
|
-
{ name: "nestjs", packageName: "@nestjs/core", tags: ["nestjs", "backend", "api"] },
|
|
3276
|
-
{ name: "hono", packageName: "hono", tags: ["hono", "backend", "api"] },
|
|
3277
|
-
{ name: "prisma", packageName: "@prisma/client", tags: ["prisma", "database", "orm"] },
|
|
3278
|
-
{ name: "drizzle", packageName: "drizzle-orm", tags: ["drizzle", "database", "orm"] },
|
|
3279
|
-
{ name: "typeorm", packageName: "typeorm", tags: ["typeorm", "database", "orm"] },
|
|
3280
|
-
{ name: "jest", packageName: "jest", tags: ["testing", "jest"] },
|
|
3281
|
-
{ name: "vitest", packageName: "vitest", tags: ["testing", "vitest"] },
|
|
3282
|
-
{ name: "playwright", packageName: "@playwright/test", tags: ["testing", "e2e", "playwright"] },
|
|
3283
|
-
{ name: "cypress", packageName: "cypress", tags: ["testing", "e2e", "cypress"] }
|
|
3284
|
-
];
|
|
3285
|
-
/** Python framework patterns (from pyproject.toml or requirements.txt) */
|
|
3286
|
-
_ProjectDetector.PYTHON_FRAMEWORKS = [
|
|
3287
|
-
{ name: "fastapi", packageName: "fastapi", tags: ["fastapi", "backend", "api"] },
|
|
3288
|
-
{ name: "django", packageName: "django", tags: ["django", "backend", "fullstack"] },
|
|
3289
|
-
{ name: "flask", packageName: "flask", tags: ["flask", "backend", "api"] },
|
|
3290
|
-
{ name: "sqlalchemy", packageName: "sqlalchemy", tags: ["sqlalchemy", "database", "orm"] },
|
|
3291
|
-
{ name: "pytest", packageName: "pytest", tags: ["testing", "pytest"] },
|
|
3292
|
-
{ name: "pydantic", packageName: "pydantic", tags: ["pydantic", "validation"] }
|
|
3293
|
-
];
|
|
3294
|
-
/** Directory patterns */
|
|
3295
|
-
_ProjectDetector.DIRECTORY_PATTERNS = [
|
|
3296
|
-
{ pattern: ".github/workflows", feature: "github-actions", tags: ["ci", "github-actions"] },
|
|
3297
|
-
{ pattern: ".gitlab-ci.yml", feature: "gitlab-ci", tags: ["ci", "gitlab"] },
|
|
3298
|
-
{ pattern: "Dockerfile", feature: "docker", tags: ["docker", "containers"] },
|
|
3299
|
-
{ pattern: "docker-compose.yml", feature: "docker-compose", tags: ["docker", "containers"] },
|
|
3300
|
-
{ pattern: "docker-compose.yaml", feature: "docker-compose", tags: ["docker", "containers"] },
|
|
3301
|
-
{ pattern: "terraform", feature: "terraform", tags: ["terraform", "infrastructure"] },
|
|
3302
|
-
{ pattern: "kubernetes", feature: "kubernetes", tags: ["kubernetes", "infrastructure"] },
|
|
3303
|
-
{ pattern: "k8s", feature: "kubernetes", tags: ["kubernetes", "infrastructure"] },
|
|
3304
|
-
{ pattern: ".env.example", feature: "env-config", tags: ["configuration"] },
|
|
3305
|
-
{ pattern: "prisma/schema.prisma", feature: "prisma", tags: ["prisma", "database"] }
|
|
3306
|
-
];
|
|
3307
|
-
var ProjectDetector = _ProjectDetector;
|
|
3308
3523
|
|
|
3309
3524
|
// src/serving/view-renderer.ts
|
|
3310
|
-
var
|
|
3525
|
+
var DEFAULT_CONFIG3 = {
|
|
3311
3526
|
includeTokenEstimates: false,
|
|
3312
3527
|
maxSummaryLength: 150
|
|
3313
3528
|
};
|
|
3314
3529
|
var ViewRenderer = class {
|
|
3315
3530
|
constructor(config2) {
|
|
3316
|
-
this.config = { ...
|
|
3531
|
+
this.config = { ...DEFAULT_CONFIG3, ...config2 };
|
|
3317
3532
|
}
|
|
3318
3533
|
/**
|
|
3319
3534
|
* Render loadout state as OpenSkills-compatible XML
|
|
@@ -3340,6 +3555,18 @@ var ViewRenderer = class {
|
|
|
3340
3555
|
lines.push(" <content>");
|
|
3341
3556
|
lines.push(skill.instructions.split("\n").map((line) => " " + line).join("\n"));
|
|
3342
3557
|
lines.push(" </content>");
|
|
3558
|
+
if (skill.relationships && skill.relationships.length > 0) {
|
|
3559
|
+
const hints = skill.relationships.filter((r) => r.confidence >= 0.5).sort((a, b) => b.confidence - a.confidence).slice(0, 5);
|
|
3560
|
+
if (hints.length > 0) {
|
|
3561
|
+
lines.push(" <related>");
|
|
3562
|
+
for (const rel of hints) {
|
|
3563
|
+
const relatedSkill = state.available.get(rel.targetSkillId);
|
|
3564
|
+
const desc = relatedSkill ? this.getSummary(relatedSkill) : rel.targetSkillId;
|
|
3565
|
+
lines.push(` <see_also id="${this.escapeXml(rel.targetSkillId)}" type="${rel.type}">${this.escapeXml(desc)}</see_also>`);
|
|
3566
|
+
}
|
|
3567
|
+
lines.push(" </related>");
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3343
3570
|
lines.push("</skill>");
|
|
3344
3571
|
} else {
|
|
3345
3572
|
const summary = this.getSummary(skill);
|
|
@@ -3452,23 +3679,18 @@ var ViewRenderer = class {
|
|
|
3452
3679
|
// Private helpers
|
|
3453
3680
|
// ===========================================================================
|
|
3454
3681
|
/**
|
|
3455
|
-
* Get summary for a skill (short description)
|
|
3682
|
+
* Get summary for a skill (short description).
|
|
3683
|
+
* Delegates to shared getSkillSummary utility.
|
|
3456
3684
|
*/
|
|
3457
3685
|
getSummary(skill) {
|
|
3458
|
-
|
|
3459
|
-
return skill.serving.summary;
|
|
3460
|
-
}
|
|
3461
|
-
const firstSentence = skill.description.split(/[.!?]/)[0];
|
|
3462
|
-
if (firstSentence.length <= this.config.maxSummaryLength) {
|
|
3463
|
-
return firstSentence;
|
|
3464
|
-
}
|
|
3465
|
-
return skill.description.substring(0, this.config.maxSummaryLength - 3) + "...";
|
|
3686
|
+
return getSkillSummary(skill, this.config.maxSummaryLength);
|
|
3466
3687
|
}
|
|
3467
3688
|
/**
|
|
3468
|
-
* Escape special XML characters
|
|
3689
|
+
* Escape special XML characters.
|
|
3690
|
+
* Delegates to shared escapeXml utility.
|
|
3469
3691
|
*/
|
|
3470
3692
|
escapeXml(str) {
|
|
3471
|
-
return str
|
|
3693
|
+
return escapeXml(str);
|
|
3472
3694
|
}
|
|
3473
3695
|
/**
|
|
3474
3696
|
* Estimate tokens for a full skill
|
|
@@ -3558,7 +3780,7 @@ var builtInProfiles = {
|
|
|
3558
3780
|
};
|
|
3559
3781
|
|
|
3560
3782
|
// src/serving/graph-server.ts
|
|
3561
|
-
var
|
|
3783
|
+
var DEFAULT_CONFIG4 = {
|
|
3562
3784
|
agentCanModify: true,
|
|
3563
3785
|
agentCanSetLoadout: false,
|
|
3564
3786
|
agentCanSwitchProfile: true,
|
|
@@ -3570,24 +3792,29 @@ var DEFAULT_CONFIG3 = {
|
|
|
3570
3792
|
persistState: false,
|
|
3571
3793
|
outputFormat: "xml",
|
|
3572
3794
|
includeTokenEstimates: false,
|
|
3795
|
+
enableCatalog: true,
|
|
3573
3796
|
profiles: {}
|
|
3574
3797
|
};
|
|
3575
3798
|
var SkillGraphServer = class {
|
|
3576
3799
|
// Track LRU for eviction
|
|
3577
3800
|
constructor(storage, config2) {
|
|
3578
3801
|
this.storage = storage;
|
|
3802
|
+
this.catalogRenderer = null;
|
|
3579
3803
|
this.handlers = /* @__PURE__ */ new Set();
|
|
3580
3804
|
this.lruOrder = [];
|
|
3581
3805
|
this.config = {
|
|
3582
|
-
...
|
|
3806
|
+
...DEFAULT_CONFIG4,
|
|
3583
3807
|
...config2,
|
|
3584
|
-
profiles: { ...builtInProfiles, ...
|
|
3808
|
+
profiles: { ...builtInProfiles, ...DEFAULT_CONFIG4.profiles, ...config2?.profiles }
|
|
3585
3809
|
};
|
|
3586
3810
|
this.compiler = new LoadoutCompiler(storage);
|
|
3587
3811
|
this.projectDetector = new ProjectDetector();
|
|
3588
3812
|
this.viewRenderer = new ViewRenderer({
|
|
3589
3813
|
includeTokenEstimates: this.config.includeTokenEstimates
|
|
3590
3814
|
});
|
|
3815
|
+
if (this.config.enableCatalog !== false) {
|
|
3816
|
+
this.catalogRenderer = new CatalogRenderer(storage, config2?.catalogConfig);
|
|
3817
|
+
}
|
|
3591
3818
|
this.state = {
|
|
3592
3819
|
available: /* @__PURE__ */ new Map(),
|
|
3593
3820
|
expanded: /* @__PURE__ */ new Set(),
|
|
@@ -3876,17 +4103,70 @@ var SkillGraphServer = class {
|
|
|
3876
4103
|
profiles: this.getProfiles()
|
|
3877
4104
|
};
|
|
3878
4105
|
}
|
|
4106
|
+
/**
|
|
4107
|
+
* Agent browses the skill catalog at a given path.
|
|
4108
|
+
* Returns rendered category view (subcategories or skill summaries at leaf).
|
|
4109
|
+
* Pass no path for the top-level overview.
|
|
4110
|
+
*/
|
|
4111
|
+
async agentBrowseCatalog(path18) {
|
|
4112
|
+
if (!this.catalogRenderer) {
|
|
4113
|
+
return "<error>Catalog browsing is not enabled</error>";
|
|
4114
|
+
}
|
|
4115
|
+
if (!path18 || path18.length === 0) {
|
|
4116
|
+
const result2 = await this.catalogRenderer.renderOverview();
|
|
4117
|
+
if (result2) {
|
|
4118
|
+
this.emit({ type: "catalog:browsed", path: [] });
|
|
4119
|
+
}
|
|
4120
|
+
return result2;
|
|
4121
|
+
}
|
|
4122
|
+
const result = await this.catalogRenderer.renderCategory(path18);
|
|
4123
|
+
this.emit({ type: "catalog:browsed", path: path18 });
|
|
4124
|
+
return result;
|
|
4125
|
+
}
|
|
4126
|
+
/**
|
|
4127
|
+
* Agent adds a skill discovered via browsing directly to loadout and expands it.
|
|
4128
|
+
* Bridges browse → loadout: found it in catalog, now load it.
|
|
4129
|
+
*/
|
|
4130
|
+
async agentAddFromCatalog(skillId) {
|
|
4131
|
+
if (!this.config.agentCanModify) {
|
|
4132
|
+
return { added: false, pending: false, expanded: false };
|
|
4133
|
+
}
|
|
4134
|
+
if (this.state.available.has(skillId)) {
|
|
4135
|
+
const expanded = this.expandSkill(skillId);
|
|
4136
|
+
return { added: false, pending: false, expanded };
|
|
4137
|
+
}
|
|
4138
|
+
const result = await this.agentRequestSkills([skillId]);
|
|
4139
|
+
if (result.added.length > 0) {
|
|
4140
|
+
const expanded = this.expandSkill(skillId);
|
|
4141
|
+
this.emit({ type: "catalog:added", skillId });
|
|
4142
|
+
return { added: true, pending: false, expanded };
|
|
4143
|
+
}
|
|
4144
|
+
if (result.pending.length > 0) {
|
|
4145
|
+
return { added: false, pending: true, expanded: false };
|
|
4146
|
+
}
|
|
4147
|
+
return { added: false, pending: false, expanded: false };
|
|
4148
|
+
}
|
|
3879
4149
|
// ===========================================================================
|
|
3880
4150
|
// RENDERING
|
|
3881
4151
|
// ===========================================================================
|
|
3882
4152
|
/**
|
|
3883
|
-
* Render current state as system prompt content
|
|
4153
|
+
* Render current state as system prompt content.
|
|
4154
|
+
* Includes catalog overview when catalog is enabled.
|
|
3884
4155
|
*/
|
|
3885
|
-
renderSystemPrompt() {
|
|
4156
|
+
async renderSystemPrompt() {
|
|
4157
|
+
let prompt;
|
|
3886
4158
|
if (this.config.outputFormat === "markdown") {
|
|
3887
|
-
|
|
4159
|
+
prompt = this.viewRenderer.renderMarkdown(this.state);
|
|
4160
|
+
} else {
|
|
4161
|
+
prompt = this.viewRenderer.renderXml(this.state);
|
|
4162
|
+
}
|
|
4163
|
+
if (this.catalogRenderer) {
|
|
4164
|
+
const overview = await this.catalogRenderer.renderOverview();
|
|
4165
|
+
if (overview) {
|
|
4166
|
+
prompt += "\n" + overview;
|
|
4167
|
+
}
|
|
3888
4168
|
}
|
|
3889
|
-
return
|
|
4169
|
+
return prompt;
|
|
3890
4170
|
}
|
|
3891
4171
|
/**
|
|
3892
4172
|
* Estimate total tokens for current loadout
|
|
@@ -4257,7 +4537,7 @@ function resolveSkilltreeDir(repoRoot) {
|
|
|
4257
4537
|
if (fs4.existsSync(swarmDir)) return swarmDir;
|
|
4258
4538
|
return path4.join(repoRoot, ".skilltree");
|
|
4259
4539
|
}
|
|
4260
|
-
var
|
|
4540
|
+
var DEFAULT_CONFIG5 = {
|
|
4261
4541
|
version: 1,
|
|
4262
4542
|
discovery: "default",
|
|
4263
4543
|
paths: ["skills"],
|
|
@@ -4285,7 +4565,7 @@ function getSkilltreeDir(repoRoot) {
|
|
|
4285
4565
|
}
|
|
4286
4566
|
function getSkillsDir(repoRoot, config2) {
|
|
4287
4567
|
const skilltreeDir = getSkilltreeDir(repoRoot);
|
|
4288
|
-
const paths = config2?.paths ||
|
|
4568
|
+
const paths = config2?.paths || DEFAULT_CONFIG5.paths;
|
|
4289
4569
|
return path4.join(skilltreeDir, paths[0]);
|
|
4290
4570
|
}
|
|
4291
4571
|
function parseSkilltreeConfig(content) {
|
|
@@ -4298,10 +4578,10 @@ function validateConfig(config2) {
|
|
|
4298
4578
|
}
|
|
4299
4579
|
return {
|
|
4300
4580
|
version: 1,
|
|
4301
|
-
discovery: config2.discovery ||
|
|
4302
|
-
paths: config2.paths ||
|
|
4303
|
-
exclude: [...
|
|
4304
|
-
skillFilePatterns: config2.skillFilePatterns ||
|
|
4581
|
+
discovery: config2.discovery || DEFAULT_CONFIG5.discovery,
|
|
4582
|
+
paths: config2.paths || DEFAULT_CONFIG5.paths,
|
|
4583
|
+
exclude: [...DEFAULT_CONFIG5.exclude || [], ...config2.exclude || []],
|
|
4584
|
+
skillFilePatterns: config2.skillFilePatterns || DEFAULT_CONFIG5.skillFilePatterns,
|
|
4305
4585
|
namespace: config2.namespace
|
|
4306
4586
|
};
|
|
4307
4587
|
}
|
|
@@ -4312,7 +4592,7 @@ async function loadSkilltreeConfig(repoRoot) {
|
|
|
4312
4592
|
return parseSkilltreeConfig(content);
|
|
4313
4593
|
} catch (error) {
|
|
4314
4594
|
if (error.code === "ENOENT") {
|
|
4315
|
-
return { ...
|
|
4595
|
+
return { ...DEFAULT_CONFIG5 };
|
|
4316
4596
|
}
|
|
4317
4597
|
throw error;
|
|
4318
4598
|
}
|
|
@@ -4347,7 +4627,7 @@ async function initSkilltreeDir(repoRoot, config2) {
|
|
|
4347
4627
|
);
|
|
4348
4628
|
}
|
|
4349
4629
|
}
|
|
4350
|
-
function isSkillFile(filename, patterns =
|
|
4630
|
+
function isSkillFile(filename, patterns = DEFAULT_CONFIG5.skillFilePatterns) {
|
|
4351
4631
|
const lower = filename.toLowerCase();
|
|
4352
4632
|
for (const pattern of patterns) {
|
|
4353
4633
|
if (pattern.includes("*")) {
|
|
@@ -4380,7 +4660,7 @@ async function discoverSkills(repoRoot) {
|
|
|
4380
4660
|
if (config2.discovery === "scan") {
|
|
4381
4661
|
await scanForSkills(skilltreeDir, skilltreeDir, config2, discovered);
|
|
4382
4662
|
} else {
|
|
4383
|
-
const paths = config2.paths ||
|
|
4663
|
+
const paths = config2.paths || DEFAULT_CONFIG5.paths;
|
|
4384
4664
|
for (const searchPath of paths) {
|
|
4385
4665
|
const fullPath = path4.join(skilltreeDir, searchPath);
|
|
4386
4666
|
await scanForSkills(skilltreeDir, fullPath, config2, discovered);
|
|
@@ -7496,11 +7776,12 @@ function createDefaultSyncConfig(remoteUrl, agentId, options) {
|
|
|
7496
7776
|
};
|
|
7497
7777
|
}
|
|
7498
7778
|
|
|
7499
|
-
// src/
|
|
7500
|
-
var
|
|
7779
|
+
// src/services/indexer.ts
|
|
7780
|
+
var path12 = __toESM(require("path"));
|
|
7781
|
+
var fs12 = __toESM(require("fs"));
|
|
7501
7782
|
|
|
7502
7783
|
// src/config/types.ts
|
|
7503
|
-
var
|
|
7784
|
+
var DEFAULT_CONFIG6 = {
|
|
7504
7785
|
storage: {
|
|
7505
7786
|
path: "~/.skill-tree"
|
|
7506
7787
|
},
|
|
@@ -7701,7 +7982,7 @@ var ConfigLoader = class {
|
|
|
7701
7982
|
constructor(configPath) {
|
|
7702
7983
|
this.loaded = false;
|
|
7703
7984
|
this.configPath = configPath || getConfigPath();
|
|
7704
|
-
this.config = { ...
|
|
7985
|
+
this.config = { ...DEFAULT_CONFIG6 };
|
|
7705
7986
|
}
|
|
7706
7987
|
/**
|
|
7707
7988
|
* Load configuration from all sources
|
|
@@ -7714,7 +7995,7 @@ var ConfigLoader = class {
|
|
|
7714
7995
|
if (this.loaded) {
|
|
7715
7996
|
return this.config;
|
|
7716
7997
|
}
|
|
7717
|
-
let config2 = deepMerge({},
|
|
7998
|
+
let config2 = deepMerge({}, DEFAULT_CONFIG6);
|
|
7718
7999
|
const fileConfig = loadConfigFromFile(this.configPath);
|
|
7719
8000
|
if (fileConfig) {
|
|
7720
8001
|
config2 = deepMerge(config2, fileConfig);
|
|
@@ -7828,1426 +8109,1538 @@ function loadConfig(configPath) {
|
|
|
7828
8109
|
return getConfigLoader(configPath).load();
|
|
7829
8110
|
}
|
|
7830
8111
|
|
|
7831
|
-
// src/
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
var DEFAULT_PATHS = [
|
|
7839
|
-
".claude/skills",
|
|
7840
|
-
// Project local (Claude)
|
|
7841
|
-
".agent/skills",
|
|
7842
|
-
// Project local (universal)
|
|
7843
|
-
"~/.claude/skills",
|
|
7844
|
-
// User global (Claude)
|
|
7845
|
-
"~/.agent/skills"
|
|
7846
|
-
// User global (universal)
|
|
7847
|
-
];
|
|
7848
|
-
function expandHome(p) {
|
|
7849
|
-
if (p.startsWith("~/")) {
|
|
7850
|
-
return path12.join(os2.homedir(), p.slice(2));
|
|
8112
|
+
// src/import/converter.ts
|
|
8113
|
+
function convertIndexerSkill(indexerSkill) {
|
|
8114
|
+
const warnings = [];
|
|
8115
|
+
const instructions = indexerSkill.content || "";
|
|
8116
|
+
const hasStructuredContent = instructions.trim().length > 0;
|
|
8117
|
+
if (!hasStructuredContent) {
|
|
8118
|
+
warnings.push("No content found, instructions will be empty");
|
|
7851
8119
|
}
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
return false;
|
|
8120
|
+
const tags = [...indexerSkill.tags || []];
|
|
8121
|
+
if (indexerSkill.sourceRepo) {
|
|
8122
|
+
const repoName = indexerSkill.sourceRepo.split("/").pop();
|
|
8123
|
+
if (repoName && !tags.includes(repoName)) {
|
|
8124
|
+
tags.push(repoName);
|
|
8125
|
+
}
|
|
7859
8126
|
}
|
|
8127
|
+
const status = indexerSkill.status === "indexed" ? "active" : indexerSkill.status === "raw" ? "draft" : "draft";
|
|
8128
|
+
const skill = {
|
|
8129
|
+
id: indexerSkill.slug,
|
|
8130
|
+
name: indexerSkill.displayName || indexerSkill.name,
|
|
8131
|
+
version: indexerSkill.version,
|
|
8132
|
+
description: indexerSkill.description,
|
|
8133
|
+
instructions,
|
|
8134
|
+
author: indexerSkill.author,
|
|
8135
|
+
tags,
|
|
8136
|
+
createdAt: new Date(indexerSkill.scrapedAt),
|
|
8137
|
+
updatedAt: new Date(indexerSkill.updatedAt),
|
|
8138
|
+
status,
|
|
8139
|
+
metrics: {
|
|
8140
|
+
usageCount: 0,
|
|
8141
|
+
successRate: 0,
|
|
8142
|
+
feedbackScores: []
|
|
8143
|
+
},
|
|
8144
|
+
source: {
|
|
8145
|
+
type: "imported",
|
|
8146
|
+
location: indexerSkill.sourceUrl,
|
|
8147
|
+
importedAt: /* @__PURE__ */ new Date()
|
|
8148
|
+
},
|
|
8149
|
+
// Extended fields for indexed skills
|
|
8150
|
+
taxonomy: indexerSkill.primaryPath ? {
|
|
8151
|
+
primaryPath: indexerSkill.primaryPath,
|
|
8152
|
+
secondaryPaths: indexerSkill.secondaryPaths,
|
|
8153
|
+
confidence: indexerSkill.confidence
|
|
8154
|
+
} : void 0,
|
|
8155
|
+
externalSource: {
|
|
8156
|
+
url: indexerSkill.sourceUrl,
|
|
8157
|
+
repo: indexerSkill.sourceRepo,
|
|
8158
|
+
scrapedAt: new Date(indexerSkill.scrapedAt)
|
|
8159
|
+
}
|
|
8160
|
+
};
|
|
8161
|
+
return { skill, warnings, hasStructuredContent };
|
|
7860
8162
|
}
|
|
7861
|
-
function
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
|
|
7865
|
-
|
|
7866
|
-
|
|
7867
|
-
|
|
7868
|
-
return path12.resolve(expandHome(envPath));
|
|
8163
|
+
function convertIndexerSkills(skills) {
|
|
8164
|
+
const results = [];
|
|
8165
|
+
const converted = [];
|
|
8166
|
+
for (const skill of skills) {
|
|
8167
|
+
const result = convertIndexerSkill(skill);
|
|
8168
|
+
results.push(result);
|
|
8169
|
+
converted.push(result.skill);
|
|
7869
8170
|
}
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
|
|
7873
|
-
|
|
8171
|
+
return {
|
|
8172
|
+
skills: converted,
|
|
8173
|
+
results,
|
|
8174
|
+
stats: {
|
|
8175
|
+
total: results.length,
|
|
8176
|
+
withStructuredContent: results.filter((r) => r.hasStructuredContent).length,
|
|
8177
|
+
withWarnings: results.filter((r) => r.warnings.length > 0).length
|
|
7874
8178
|
}
|
|
7875
|
-
}
|
|
7876
|
-
return path12.resolve(".claude/skills");
|
|
8179
|
+
};
|
|
7877
8180
|
}
|
|
7878
|
-
function
|
|
7879
|
-
|
|
7880
|
-
|
|
8181
|
+
function parseIndexerExport(content) {
|
|
8182
|
+
const data = JSON.parse(content);
|
|
8183
|
+
let indexerSkills;
|
|
8184
|
+
let exportMetadata;
|
|
8185
|
+
if (Array.isArray(data)) {
|
|
8186
|
+
indexerSkills = data;
|
|
8187
|
+
} else if (data.skills && Array.isArray(data.skills)) {
|
|
8188
|
+
indexerSkills = data.skills;
|
|
8189
|
+
exportMetadata = {
|
|
8190
|
+
exportedAt: data.exportedAt,
|
|
8191
|
+
originalStats: data.stats
|
|
8192
|
+
};
|
|
8193
|
+
} else {
|
|
8194
|
+
throw new Error("Invalid indexer export format: expected array or object with skills array");
|
|
7881
8195
|
}
|
|
8196
|
+
const { skills, stats } = convertIndexerSkills(indexerSkills);
|
|
8197
|
+
return { skills, stats, exportMetadata };
|
|
7882
8198
|
}
|
|
7883
8199
|
|
|
7884
|
-
// src/
|
|
7885
|
-
function
|
|
7886
|
-
const
|
|
7887
|
-
|
|
7888
|
-
if (!mat?.enabled) return void 0;
|
|
7889
|
-
return {
|
|
7890
|
-
enabled: true,
|
|
7891
|
-
mode: mat.mode ?? "symlink",
|
|
7892
|
-
symlinkPaths: mat.symlink_paths?.length ? mat.symlink_paths : void 0,
|
|
7893
|
-
agentsMdPath: mat.agents_md_path || void 0,
|
|
7894
|
-
agentsMdFormat: mat.agents_md_format ?? "xml",
|
|
7895
|
-
debounceMs: mat.debounce_ms ?? 500
|
|
7896
|
-
};
|
|
8200
|
+
// src/services/indexer.ts
|
|
8201
|
+
function hasIndexerSupport(storage) {
|
|
8202
|
+
const s = storage;
|
|
8203
|
+
return typeof s.addRelationship === "function" && typeof s.getTaxonomyTree === "function";
|
|
7897
8204
|
}
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
7902
|
-
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
}
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
// src/cli/utils/output.ts
|
|
7917
|
-
var import_chalk = __toESM(require("chalk"));
|
|
7918
|
-
function formatSkillLine(skill) {
|
|
7919
|
-
const status = formatStatus(skill.status);
|
|
7920
|
-
const version = import_chalk.default.dim(`v${skill.version}`);
|
|
7921
|
-
const id = import_chalk.default.bold(skill.id);
|
|
7922
|
-
const name = skill.name !== skill.id ? import_chalk.default.dim(` (${skill.name})`) : "";
|
|
7923
|
-
const tags = skill.tags.length > 0 ? import_chalk.default.dim(` [${skill.tags.join(", ")}]`) : "";
|
|
7924
|
-
return `${status} ${id}${name} ${version}${tags}`;
|
|
7925
|
-
}
|
|
7926
|
-
function formatStatus(status) {
|
|
7927
|
-
switch (status) {
|
|
7928
|
-
case "active":
|
|
7929
|
-
return import_chalk.default.green("\u25CF");
|
|
7930
|
-
case "draft":
|
|
7931
|
-
return import_chalk.default.yellow("\u25CB");
|
|
7932
|
-
case "deprecated":
|
|
7933
|
-
return import_chalk.default.red("\u2717");
|
|
7934
|
-
case "experimental":
|
|
7935
|
-
return import_chalk.default.blue("\u25D0");
|
|
7936
|
-
default:
|
|
7937
|
-
return import_chalk.default.gray("?");
|
|
7938
|
-
}
|
|
7939
|
-
}
|
|
7940
|
-
function formatSkillDetail(skill) {
|
|
7941
|
-
const lines = [];
|
|
7942
|
-
lines.push(import_chalk.default.bold(`${skill.name} v${skill.version}`));
|
|
7943
|
-
lines.push(import_chalk.default.dim("\u2500".repeat(50)));
|
|
7944
|
-
lines.push(`${import_chalk.default.dim("Status:")} ${formatStatusLabel(skill.status)}`);
|
|
7945
|
-
lines.push(`${import_chalk.default.dim("Author:")} ${skill.author}`);
|
|
7946
|
-
if (skill.tags.length > 0) {
|
|
7947
|
-
lines.push(`${import_chalk.default.dim("Tags:")} ${skill.tags.join(", ")}`);
|
|
7948
|
-
}
|
|
7949
|
-
if (skill.parentVersion) {
|
|
7950
|
-
lines.push(`${import_chalk.default.dim("Parent:")} v${skill.parentVersion}`);
|
|
7951
|
-
}
|
|
7952
|
-
if (skill.derivedFrom && skill.derivedFrom.length > 0) {
|
|
7953
|
-
lines.push(`${import_chalk.default.dim("Derived:")} ${skill.derivedFrom.join(", ")}`);
|
|
7954
|
-
}
|
|
7955
|
-
const { usageCount, successRate } = skill.metrics;
|
|
7956
|
-
if (usageCount > 0) {
|
|
7957
|
-
lines.push(`${import_chalk.default.dim("Usage:")} ${usageCount} times, ${Math.round(successRate * 100)}% success`);
|
|
7958
|
-
}
|
|
7959
|
-
lines.push("");
|
|
7960
|
-
lines.push(import_chalk.default.dim("Description"));
|
|
7961
|
-
lines.push(import_chalk.default.dim("\u2500".repeat(50)));
|
|
7962
|
-
lines.push(skill.description);
|
|
7963
|
-
lines.push("");
|
|
7964
|
-
if (skill.instructions) {
|
|
7965
|
-
lines.push(import_chalk.default.dim("Instructions"));
|
|
7966
|
-
lines.push(import_chalk.default.dim("\u2500".repeat(50)));
|
|
7967
|
-
lines.push(skill.instructions);
|
|
7968
|
-
}
|
|
7969
|
-
return lines.join("\n");
|
|
7970
|
-
}
|
|
7971
|
-
function formatStatusLabel(status) {
|
|
7972
|
-
switch (status) {
|
|
7973
|
-
case "active":
|
|
7974
|
-
return import_chalk.default.green("active");
|
|
7975
|
-
case "draft":
|
|
7976
|
-
return import_chalk.default.yellow("draft");
|
|
7977
|
-
case "deprecated":
|
|
7978
|
-
return import_chalk.default.red("deprecated");
|
|
7979
|
-
case "experimental":
|
|
7980
|
-
return import_chalk.default.blue("experimental");
|
|
7981
|
-
default:
|
|
7982
|
-
return import_chalk.default.gray(status);
|
|
7983
|
-
}
|
|
7984
|
-
}
|
|
7985
|
-
function formatVersionHistory(versions) {
|
|
7986
|
-
const lines = [];
|
|
7987
|
-
lines.push(import_chalk.default.bold("Version History"));
|
|
7988
|
-
lines.push(import_chalk.default.dim("\u2500".repeat(50)));
|
|
7989
|
-
for (const v of versions.reverse()) {
|
|
7990
|
-
const date = v.createdAt.toISOString().split("T")[0];
|
|
7991
|
-
const hash = import_chalk.default.dim(`#${v.contentHash.slice(0, 7)}`);
|
|
7992
|
-
const changelog = v.changelog ? ` - ${v.changelog}` : "";
|
|
7993
|
-
lines.push(` ${import_chalk.default.cyan(`v${v.version}`)} ${import_chalk.default.dim(date)} ${hash}${changelog}`);
|
|
7994
|
-
}
|
|
7995
|
-
return lines.join("\n");
|
|
7996
|
-
}
|
|
7997
|
-
function formatDiff(versionA, versionB, changes) {
|
|
7998
|
-
const lines = [];
|
|
7999
|
-
lines.push(import_chalk.default.bold(`Diff: v${versionA} \u2192 v${versionB}`));
|
|
8000
|
-
lines.push(import_chalk.default.dim("\u2500".repeat(50)));
|
|
8001
|
-
if (changes.modified.length === 0 && changes.added.length === 0 && changes.removed.length === 0) {
|
|
8002
|
-
lines.push(import_chalk.default.dim("No changes"));
|
|
8003
|
-
return lines.join("\n");
|
|
8004
|
-
}
|
|
8005
|
-
for (const mod of changes.modified) {
|
|
8006
|
-
lines.push(import_chalk.default.yellow(`~ ${mod.field}`));
|
|
8007
|
-
lines.push(import_chalk.default.red(` - ${truncate(mod.oldValue, 60)}`));
|
|
8008
|
-
lines.push(import_chalk.default.green(` + ${truncate(mod.newValue, 60)}`));
|
|
8009
|
-
}
|
|
8010
|
-
for (const add of changes.added) {
|
|
8011
|
-
lines.push(import_chalk.default.green(`+ ${add.type}: ${add.value}`));
|
|
8012
|
-
}
|
|
8013
|
-
for (const rem of changes.removed) {
|
|
8014
|
-
lines.push(import_chalk.default.red(`- ${rem.type}: ${rem.value}`));
|
|
8015
|
-
}
|
|
8016
|
-
return lines.join("\n");
|
|
8017
|
-
}
|
|
8018
|
-
function formatStats(stats) {
|
|
8019
|
-
const lines = [];
|
|
8020
|
-
lines.push(import_chalk.default.bold("Skill Bank Statistics"));
|
|
8021
|
-
lines.push(import_chalk.default.dim("\u2500".repeat(50)));
|
|
8022
|
-
lines.push(`${import_chalk.default.dim("Total skills:")} ${stats.totalSkills}`);
|
|
8023
|
-
lines.push("");
|
|
8024
|
-
lines.push(import_chalk.default.dim("By Status:"));
|
|
8025
|
-
lines.push(` ${import_chalk.default.green("\u25CF")} Active: ${stats.byStatus.active}`);
|
|
8026
|
-
lines.push(` ${import_chalk.default.yellow("\u25CB")} Draft: ${stats.byStatus.draft}`);
|
|
8027
|
-
lines.push(` ${import_chalk.default.blue("\u25D0")} Experimental: ${stats.byStatus.experimental}`);
|
|
8028
|
-
lines.push(` ${import_chalk.default.red("\u2717")} Deprecated: ${stats.byStatus.deprecated}`);
|
|
8029
|
-
lines.push("");
|
|
8030
|
-
if (Object.keys(stats.byTag).length > 0) {
|
|
8031
|
-
lines.push(import_chalk.default.dim("By Tag:"));
|
|
8032
|
-
const sortedTags = Object.entries(stats.byTag).sort((a, b) => b[1] - a[1]);
|
|
8033
|
-
for (const [tag, count] of sortedTags.slice(0, 10)) {
|
|
8034
|
-
lines.push(` ${tag}: ${count}`);
|
|
8035
|
-
}
|
|
8036
|
-
lines.push("");
|
|
8037
|
-
}
|
|
8038
|
-
if (stats.totalUsage > 0) {
|
|
8039
|
-
lines.push(import_chalk.default.dim("Usage:"));
|
|
8040
|
-
lines.push(` Total uses: ${stats.totalUsage}`);
|
|
8041
|
-
lines.push(` Avg success: ${Math.round(stats.avgSuccessRate * 100)}%`);
|
|
8205
|
+
var IndexerService = class {
|
|
8206
|
+
constructor(config2 = {}, skillBank) {
|
|
8207
|
+
this.initialized = false;
|
|
8208
|
+
this.globalConfig = loadConfig();
|
|
8209
|
+
this.serviceConfig = {
|
|
8210
|
+
githubToken: config2.githubToken || this.globalConfig.indexer.github_token,
|
|
8211
|
+
anthropicApiKey: config2.anthropicApiKey || this.globalConfig.indexer.anthropic_key,
|
|
8212
|
+
batchSize: config2.batchSize || this.globalConfig.indexer.batch_size,
|
|
8213
|
+
minConfidence: config2.minConfidence || this.globalConfig.indexer.min_confidence,
|
|
8214
|
+
concurrency: config2.concurrency || 3,
|
|
8215
|
+
cacheTtlSeconds: config2.cacheTtlSeconds || 3600,
|
|
8216
|
+
databasePath: config2.databasePath,
|
|
8217
|
+
cacheDir: config2.cacheDir,
|
|
8218
|
+
claudeModel: config2.claudeModel,
|
|
8219
|
+
scraperModules: config2.scraperModules
|
|
8220
|
+
};
|
|
8221
|
+
this.skillBank = skillBank;
|
|
8042
8222
|
}
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
|
|
8046
|
-
|
|
8047
|
-
}
|
|
8048
|
-
function printSuccess(message) {
|
|
8049
|
-
console.log(import_chalk.default.green(`\u2713 ${message}`));
|
|
8050
|
-
}
|
|
8051
|
-
function printWarning(message) {
|
|
8052
|
-
console.log(import_chalk.default.yellow(`\u26A0 ${message}`));
|
|
8053
|
-
}
|
|
8054
|
-
function printInfo(message) {
|
|
8055
|
-
console.log(import_chalk.default.dim(message));
|
|
8056
|
-
}
|
|
8057
|
-
function truncate(text, maxLength) {
|
|
8058
|
-
const oneLine = text.replace(/\n/g, " ");
|
|
8059
|
-
if (oneLine.length <= maxLength) return oneLine;
|
|
8060
|
-
return oneLine.slice(0, maxLength - 3) + "...";
|
|
8061
|
-
}
|
|
8062
|
-
|
|
8063
|
-
// src/cli/commands/list.ts
|
|
8064
|
-
var listCommand = new import_commander.Command("list").description("List all skills").option("-s, --status <status>", "Filter by status (active, draft, deprecated, experimental)").option("-t, --tag <tag>", "Filter by tag").option("-a, --author <author>", "Filter by author").action(async (options, command) => {
|
|
8065
|
-
const globalOpts = command.optsWithGlobals();
|
|
8066
|
-
try {
|
|
8067
|
-
const skillBank = await createSkillBankFromOptions(globalOpts);
|
|
8068
|
-
const filter = {};
|
|
8069
|
-
if (options.status) {
|
|
8070
|
-
filter.status = [options.status];
|
|
8071
|
-
}
|
|
8072
|
-
if (options.tag) {
|
|
8073
|
-
filter.tags = [options.tag];
|
|
8074
|
-
}
|
|
8075
|
-
if (options.author) {
|
|
8076
|
-
filter.author = options.author;
|
|
8077
|
-
}
|
|
8078
|
-
const skills = await skillBank.listSkills(
|
|
8079
|
-
Object.keys(filter).length > 0 ? filter : void 0
|
|
8080
|
-
);
|
|
8081
|
-
if (globalOpts.json) {
|
|
8082
|
-
console.log(JSON.stringify(skills, null, 2));
|
|
8083
|
-
return;
|
|
8084
|
-
}
|
|
8085
|
-
if (skills.length === 0) {
|
|
8086
|
-
printInfo(`No skills found in ${getSkillPath(globalOpts)}`);
|
|
8087
|
-
return;
|
|
8088
|
-
}
|
|
8089
|
-
if (!globalOpts.quiet) {
|
|
8090
|
-
printInfo(`Skills in ${getSkillPath(globalOpts)}:
|
|
8091
|
-
`);
|
|
8092
|
-
}
|
|
8093
|
-
for (const skill of skills) {
|
|
8094
|
-
console.log(formatSkillLine(skill));
|
|
8095
|
-
}
|
|
8096
|
-
if (!globalOpts.quiet) {
|
|
8097
|
-
console.log();
|
|
8098
|
-
printInfo(`${skills.length} skill(s)`);
|
|
8099
|
-
}
|
|
8100
|
-
} catch (error) {
|
|
8101
|
-
printError(error.message);
|
|
8102
|
-
process.exit(1);
|
|
8223
|
+
/**
|
|
8224
|
+
* Get effective configuration
|
|
8225
|
+
*/
|
|
8226
|
+
getConfig() {
|
|
8227
|
+
return { ...this.serviceConfig };
|
|
8103
8228
|
}
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8110
|
-
|
|
8111
|
-
|
|
8112
|
-
|
|
8113
|
-
|
|
8114
|
-
|
|
8115
|
-
|
|
8116
|
-
|
|
8117
|
-
|
|
8118
|
-
|
|
8119
|
-
|
|
8229
|
+
/**
|
|
8230
|
+
* Initialize the service (lazy load scraper modules)
|
|
8231
|
+
*/
|
|
8232
|
+
async initialize() {
|
|
8233
|
+
if (this.initialized) return;
|
|
8234
|
+
try {
|
|
8235
|
+
if (this.serviceConfig.scraperModules) {
|
|
8236
|
+
const mod = this.serviceConfig.scraperModules;
|
|
8237
|
+
this.databaseModule = mod;
|
|
8238
|
+
if (mod.createDatabase) {
|
|
8239
|
+
const dbPath = this.serviceConfig.databasePath || path12.join(process.cwd(), "scraper/data/skills.db");
|
|
8240
|
+
const dbDir = path12.dirname(dbPath);
|
|
8241
|
+
if (!fs12.existsSync(dbDir)) fs12.mkdirSync(dbDir, { recursive: true });
|
|
8242
|
+
this.db = mod.createDatabase({ type: "sqlite", path: dbPath });
|
|
8243
|
+
if (this.db.connect) await this.db.connect();
|
|
8244
|
+
if (this.db.migrate) await this.db.migrate();
|
|
8245
|
+
}
|
|
8246
|
+
const localDb = this.db;
|
|
8247
|
+
if (mod.ScraperService) {
|
|
8248
|
+
this.scraperModule = {
|
|
8249
|
+
createScraper: (config2) => {
|
|
8250
|
+
const scraperConfig = {
|
|
8251
|
+
githubToken: config2.githubToken || "",
|
|
8252
|
+
cacheEnabled: true,
|
|
8253
|
+
cacheDir: config2.cacheDir || path12.join(process.cwd(), ".cache/scraper"),
|
|
8254
|
+
cacheTtlSeconds: config2.cacheTtlSeconds || 3600,
|
|
8255
|
+
requestDelayMs: 100,
|
|
8256
|
+
maxRetries: 3
|
|
8257
|
+
};
|
|
8258
|
+
const svc = new mod.ScraperService(localDb, scraperConfig);
|
|
8259
|
+
let initialized = false;
|
|
8260
|
+
const ensureInit = async () => {
|
|
8261
|
+
if (!initialized) {
|
|
8262
|
+
await svc.init();
|
|
8263
|
+
initialized = true;
|
|
8264
|
+
}
|
|
8265
|
+
};
|
|
8266
|
+
return {
|
|
8267
|
+
scrapeAwesomeList: async (url, opts) => {
|
|
8268
|
+
await ensureInit();
|
|
8269
|
+
await svc.scrape({ type: "awesome-list", url }, opts);
|
|
8270
|
+
return await localDb.listSkills() || [];
|
|
8271
|
+
},
|
|
8272
|
+
scrapeRepository: async (url, opts) => {
|
|
8273
|
+
await ensureInit();
|
|
8274
|
+
await svc.scrape({ type: "repository", url }, opts);
|
|
8275
|
+
return await localDb.listSkills() || [];
|
|
8276
|
+
}
|
|
8277
|
+
};
|
|
8278
|
+
}
|
|
8279
|
+
};
|
|
8280
|
+
}
|
|
8281
|
+
if (mod.SkillClassifier) {
|
|
8282
|
+
this.indexerModule = {
|
|
8283
|
+
createIndexer: (config2) => {
|
|
8284
|
+
const classifier = new mod.SkillClassifier(config2);
|
|
8285
|
+
return {
|
|
8286
|
+
classifySkill: async (skill) => {
|
|
8287
|
+
let tree = null;
|
|
8288
|
+
if (mod.TaxonomyManager && localDb) {
|
|
8289
|
+
try {
|
|
8290
|
+
const tm = new mod.TaxonomyManager(localDb);
|
|
8291
|
+
tree = await tm.getTree();
|
|
8292
|
+
} catch {
|
|
8293
|
+
}
|
|
8294
|
+
}
|
|
8295
|
+
return classifier.classify(skill, tree);
|
|
8296
|
+
}
|
|
8297
|
+
};
|
|
8298
|
+
}
|
|
8299
|
+
};
|
|
8300
|
+
}
|
|
8301
|
+
this.initialized = true;
|
|
8302
|
+
return;
|
|
8303
|
+
}
|
|
8304
|
+
const possiblePaths = [
|
|
8305
|
+
// Relative to this file in dist
|
|
8306
|
+
path12.resolve(__dirname, "../../scraper/dist"),
|
|
8307
|
+
// Relative to project root
|
|
8308
|
+
path12.resolve(process.cwd(), "scraper/dist"),
|
|
8309
|
+
// Absolute paths from config
|
|
8310
|
+
this.serviceConfig.cacheDir ? path12.resolve(this.serviceConfig.cacheDir, "../scraper/dist") : null
|
|
8311
|
+
].filter(Boolean);
|
|
8312
|
+
let scraperBasePath = null;
|
|
8313
|
+
for (const basePath of possiblePaths) {
|
|
8314
|
+
const scraperIndex = path12.join(basePath, "scraper/index.js");
|
|
8315
|
+
if (fs12.existsSync(scraperIndex)) {
|
|
8316
|
+
scraperBasePath = basePath;
|
|
8317
|
+
break;
|
|
8318
|
+
}
|
|
8319
|
+
}
|
|
8320
|
+
if (!scraperBasePath) {
|
|
8321
|
+
throw new Error(
|
|
8322
|
+
"Scraper modules not found. Run `cd scraper && npm run build` first."
|
|
8323
|
+
);
|
|
8324
|
+
}
|
|
8325
|
+
const scraperPath = path12.join(scraperBasePath, "scraper/index.js");
|
|
8326
|
+
const indexerPath = path12.join(scraperBasePath, "indexer/index.js");
|
|
8327
|
+
const databasePath = path12.join(scraperBasePath, "database/index.js");
|
|
8328
|
+
this.scraperModule = await import(
|
|
8329
|
+
/* webpackIgnore: true */
|
|
8330
|
+
scraperPath
|
|
8331
|
+
);
|
|
8332
|
+
this.indexerModule = await import(
|
|
8333
|
+
/* webpackIgnore: true */
|
|
8334
|
+
indexerPath
|
|
8335
|
+
);
|
|
8336
|
+
this.databaseModule = await import(
|
|
8337
|
+
/* webpackIgnore: true */
|
|
8338
|
+
databasePath
|
|
8339
|
+
);
|
|
8340
|
+
if (this.databaseModule.createDatabase) {
|
|
8341
|
+
const dbPath = this.serviceConfig.databasePath || path12.join(process.cwd(), "scraper/data/skills.db");
|
|
8342
|
+
this.db = this.databaseModule.createDatabase(dbPath);
|
|
8343
|
+
}
|
|
8344
|
+
this.initialized = true;
|
|
8345
|
+
} catch (err) {
|
|
8346
|
+
console.warn(`Scraper modules not available: ${err.message}`);
|
|
8347
|
+
console.warn("Some indexer features will be limited.");
|
|
8348
|
+
this.initialized = true;
|
|
8120
8349
|
}
|
|
8121
|
-
console.log(formatSkillDetail(skill));
|
|
8122
|
-
} catch (error) {
|
|
8123
|
-
printError(error.message);
|
|
8124
|
-
process.exit(1);
|
|
8125
8350
|
}
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
const globalOpts = command.optsWithGlobals();
|
|
8132
|
-
try {
|
|
8133
|
-
const skillBank = await createSkillBankFromOptions(globalOpts);
|
|
8134
|
-
const skills = await skillBank.searchSkills(query);
|
|
8135
|
-
if (globalOpts.json) {
|
|
8136
|
-
console.log(JSON.stringify(skills, null, 2));
|
|
8137
|
-
return;
|
|
8138
|
-
}
|
|
8139
|
-
if (skills.length === 0) {
|
|
8140
|
-
printInfo(`No skills found matching "${query}"`);
|
|
8141
|
-
return;
|
|
8142
|
-
}
|
|
8143
|
-
if (!globalOpts.quiet) {
|
|
8144
|
-
printInfo(`Results for "${query}":
|
|
8145
|
-
`);
|
|
8146
|
-
}
|
|
8147
|
-
for (const skill of skills) {
|
|
8148
|
-
console.log(formatSkillLine(skill));
|
|
8149
|
-
}
|
|
8150
|
-
if (!globalOpts.quiet) {
|
|
8151
|
-
console.log();
|
|
8152
|
-
printInfo(`${skills.length} result(s)`);
|
|
8153
|
-
}
|
|
8154
|
-
} catch (error) {
|
|
8155
|
-
printError(error.message);
|
|
8156
|
-
process.exit(1);
|
|
8351
|
+
/**
|
|
8352
|
+
* Check if the indexer is available
|
|
8353
|
+
*/
|
|
8354
|
+
isAvailable() {
|
|
8355
|
+
return this.initialized && !!this.scraperModule;
|
|
8157
8356
|
}
|
|
8158
|
-
|
|
8159
|
-
|
|
8160
|
-
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
const globalOpts = command.optsWithGlobals();
|
|
8164
|
-
try {
|
|
8165
|
-
const skillBank = await createSkillBankFromOptions(globalOpts);
|
|
8166
|
-
const stats = await skillBank.getStats();
|
|
8167
|
-
if (globalOpts.json) {
|
|
8168
|
-
console.log(JSON.stringify(stats, null, 2));
|
|
8169
|
-
return;
|
|
8170
|
-
}
|
|
8171
|
-
console.log(formatStats(stats));
|
|
8172
|
-
} catch (error) {
|
|
8173
|
-
printError(error.message);
|
|
8174
|
-
process.exit(1);
|
|
8357
|
+
/**
|
|
8358
|
+
* Check if running in degraded mode (no scraper modules)
|
|
8359
|
+
*/
|
|
8360
|
+
isDegradedMode() {
|
|
8361
|
+
return this.initialized && !this.scraperModule;
|
|
8175
8362
|
}
|
|
8176
|
-
|
|
8177
|
-
|
|
8178
|
-
|
|
8179
|
-
|
|
8180
|
-
|
|
8181
|
-
|
|
8182
|
-
|
|
8183
|
-
|
|
8184
|
-
|
|
8185
|
-
if (versions.length === 0) {
|
|
8186
|
-
printInfo(`No version history found for: ${id}`);
|
|
8187
|
-
return;
|
|
8363
|
+
/**
|
|
8364
|
+
* Scrape skills from GitHub sources
|
|
8365
|
+
*/
|
|
8366
|
+
async scrape(sources, options) {
|
|
8367
|
+
await this.initialize();
|
|
8368
|
+
if (!this.scraperModule || !this.db) {
|
|
8369
|
+
throw new Error(
|
|
8370
|
+
"Scraper not available. Build the scraper module first: cd scraper && npm run build"
|
|
8371
|
+
);
|
|
8188
8372
|
}
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8373
|
+
const result = {
|
|
8374
|
+
discovered: 0,
|
|
8375
|
+
scraped: 0,
|
|
8376
|
+
skipped: 0,
|
|
8377
|
+
failed: 0,
|
|
8378
|
+
unchanged: 0,
|
|
8379
|
+
errors: []
|
|
8380
|
+
};
|
|
8381
|
+
try {
|
|
8382
|
+
const scraper = this.scraperModule.createScraper({
|
|
8383
|
+
githubToken: this.serviceConfig.githubToken,
|
|
8384
|
+
cacheDir: this.serviceConfig.cacheDir,
|
|
8385
|
+
cacheTtlSeconds: this.serviceConfig.cacheTtlSeconds
|
|
8386
|
+
});
|
|
8387
|
+
for (const source of sources) {
|
|
8388
|
+
try {
|
|
8389
|
+
let skills;
|
|
8390
|
+
if (source.type === "awesome-list") {
|
|
8391
|
+
skills = await scraper.scrapeAwesomeList(source.url, {
|
|
8392
|
+
force: options?.force
|
|
8393
|
+
});
|
|
8394
|
+
} else {
|
|
8395
|
+
skills = await scraper.scrapeRepository(source.url, {
|
|
8396
|
+
force: options?.force
|
|
8397
|
+
});
|
|
8398
|
+
}
|
|
8399
|
+
result.discovered += skills.length;
|
|
8400
|
+
for (const skill of skills) {
|
|
8401
|
+
try {
|
|
8402
|
+
const existing = this.db.getSkillBySlug?.(skill.slug);
|
|
8403
|
+
if (existing && !options?.force) {
|
|
8404
|
+
result.unchanged++;
|
|
8405
|
+
} else {
|
|
8406
|
+
this.db.saveSkill?.(skill);
|
|
8407
|
+
result.scraped++;
|
|
8408
|
+
}
|
|
8409
|
+
} catch (err) {
|
|
8410
|
+
result.failed++;
|
|
8411
|
+
result.errors.push(
|
|
8412
|
+
`Failed to save skill ${skill.slug}: ${err.message}`
|
|
8413
|
+
);
|
|
8414
|
+
}
|
|
8415
|
+
}
|
|
8416
|
+
} catch (err) {
|
|
8417
|
+
result.failed++;
|
|
8418
|
+
result.errors.push(
|
|
8419
|
+
`Failed to scrape ${source.url}: ${err.message}`
|
|
8420
|
+
);
|
|
8421
|
+
}
|
|
8422
|
+
}
|
|
8423
|
+
} catch (err) {
|
|
8424
|
+
result.errors.push(`Scrape failed: ${err.message}`);
|
|
8192
8425
|
}
|
|
8193
|
-
|
|
8194
|
-
} catch (error) {
|
|
8195
|
-
printError(error.message);
|
|
8196
|
-
process.exit(1);
|
|
8426
|
+
return result;
|
|
8197
8427
|
}
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
if (globalOpts.json) {
|
|
8208
|
-
console.log(JSON.stringify(diff, null, 2));
|
|
8209
|
-
return;
|
|
8428
|
+
/**
|
|
8429
|
+
* Classify unindexed skills using AI
|
|
8430
|
+
*/
|
|
8431
|
+
async classify(options) {
|
|
8432
|
+
await this.initialize();
|
|
8433
|
+
if (!this.indexerModule || !this.db) {
|
|
8434
|
+
throw new Error(
|
|
8435
|
+
"Indexer not available. Build the scraper module first: cd scraper && npm run build"
|
|
8436
|
+
);
|
|
8210
8437
|
}
|
|
8211
|
-
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
}
|
|
8217
|
-
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8438
|
+
const result = {
|
|
8439
|
+
indexed: 0,
|
|
8440
|
+
skipped: 0,
|
|
8441
|
+
failed: 0,
|
|
8442
|
+
errors: []
|
|
8443
|
+
};
|
|
8444
|
+
try {
|
|
8445
|
+
const indexer = this.indexerModule.createIndexer({
|
|
8446
|
+
anthropicApiKey: this.serviceConfig.anthropicApiKey,
|
|
8447
|
+
model: this.serviceConfig.claudeModel || "claude-sonnet-4-20250514",
|
|
8448
|
+
minConfidence: this.serviceConfig.minConfidence
|
|
8449
|
+
});
|
|
8450
|
+
let skills;
|
|
8451
|
+
if (options?.skillId) {
|
|
8452
|
+
const skill = this.db.getSkillBySlug?.(options.skillId);
|
|
8453
|
+
skills = skill ? [skill] : [];
|
|
8454
|
+
} else if (options?.all) {
|
|
8455
|
+
skills = this.db.getAllSkills?.() || [];
|
|
8456
|
+
} else {
|
|
8457
|
+
skills = this.db.getSkillsByStatus?.("raw") || [];
|
|
8458
|
+
}
|
|
8459
|
+
const batchSize = this.serviceConfig.batchSize || 10;
|
|
8460
|
+
for (let i = 0; i < skills.length; i += batchSize) {
|
|
8461
|
+
const batch = skills.slice(i, i + batchSize);
|
|
8462
|
+
for (const skill of batch) {
|
|
8463
|
+
if (skill.status === "indexed" && !options?.all) {
|
|
8464
|
+
result.skipped++;
|
|
8465
|
+
continue;
|
|
8466
|
+
}
|
|
8467
|
+
try {
|
|
8468
|
+
const classification = await indexer.classifySkill(skill);
|
|
8469
|
+
skill.status = "indexed";
|
|
8470
|
+
skill.primaryPath = classification.primaryPath;
|
|
8471
|
+
skill.secondaryPaths = classification.secondaryPaths;
|
|
8472
|
+
skill.confidence = classification.confidence;
|
|
8473
|
+
skill.classificationReasoning = classification.reasoning;
|
|
8474
|
+
skill.indexedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8475
|
+
this.db.saveSkill?.(skill);
|
|
8476
|
+
result.indexed++;
|
|
8477
|
+
} catch (err) {
|
|
8478
|
+
result.failed++;
|
|
8479
|
+
result.errors.push(
|
|
8480
|
+
`Failed to classify ${skill.slug}: ${err.message}`
|
|
8481
|
+
);
|
|
8482
|
+
}
|
|
8483
|
+
}
|
|
8484
|
+
}
|
|
8485
|
+
} catch (err) {
|
|
8486
|
+
result.errors.push(`Classification failed: ${err.message}`);
|
|
8228
8487
|
}
|
|
8229
|
-
|
|
8230
|
-
} catch (error) {
|
|
8231
|
-
printError(error.message);
|
|
8232
|
-
process.exit(1);
|
|
8488
|
+
return result;
|
|
8233
8489
|
}
|
|
8234
|
-
|
|
8235
|
-
|
|
8236
|
-
|
|
8237
|
-
|
|
8238
|
-
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
|
|
8242
|
-
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8490
|
+
/**
|
|
8491
|
+
* Detect relationships between skills
|
|
8492
|
+
*/
|
|
8493
|
+
async detectRelationships(options) {
|
|
8494
|
+
await this.initialize();
|
|
8495
|
+
const result = {
|
|
8496
|
+
detected: 0,
|
|
8497
|
+
skipped: 0,
|
|
8498
|
+
errors: []
|
|
8499
|
+
};
|
|
8500
|
+
if (this.skillBank) {
|
|
8501
|
+
try {
|
|
8502
|
+
const storage = this.skillBank.getStorage();
|
|
8503
|
+
if (hasIndexerSupport(storage)) {
|
|
8504
|
+
const skills = await this.skillBank.listSkills();
|
|
8505
|
+
const targetSkills = options?.skillId ? skills.filter((s) => s.id === options.skillId) : skills;
|
|
8506
|
+
for (const skill of targetSkills) {
|
|
8507
|
+
const relationships = this.detectSkillRelationships(skill, skills);
|
|
8508
|
+
for (const rel of relationships) {
|
|
8509
|
+
try {
|
|
8510
|
+
await storage.addRelationship(
|
|
8511
|
+
skill.id,
|
|
8512
|
+
rel.targetSkillId,
|
|
8513
|
+
rel.type,
|
|
8514
|
+
rel.confidence,
|
|
8515
|
+
rel.reasoning
|
|
8516
|
+
);
|
|
8517
|
+
result.detected++;
|
|
8518
|
+
} catch (err) {
|
|
8519
|
+
result.skipped++;
|
|
8520
|
+
}
|
|
8521
|
+
}
|
|
8522
|
+
}
|
|
8523
|
+
}
|
|
8524
|
+
} catch (err) {
|
|
8525
|
+
result.errors.push(
|
|
8526
|
+
`Relationship detection failed: ${err.message}`
|
|
8527
|
+
);
|
|
8528
|
+
}
|
|
8529
|
+
return result;
|
|
8251
8530
|
}
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
|
|
8255
|
-
|
|
8531
|
+
if (!this.db) {
|
|
8532
|
+
throw new Error("Neither SkillBank nor scraper database available.");
|
|
8533
|
+
}
|
|
8534
|
+
try {
|
|
8535
|
+
const skills = this.db.getAllSkills?.() || [];
|
|
8536
|
+
const targetSkills = options?.skillId ? skills.filter((s) => s.slug === options.skillId) : skills.filter((s) => s.status === "indexed");
|
|
8537
|
+
for (const skill of targetSkills) {
|
|
8538
|
+
const relationships = this.detectSkillRelationships(skill, skills);
|
|
8539
|
+
for (const rel of relationships) {
|
|
8540
|
+
try {
|
|
8541
|
+
this.db.saveRelationship?.({
|
|
8542
|
+
sourceSkillId: skill.id,
|
|
8543
|
+
...rel
|
|
8544
|
+
});
|
|
8545
|
+
result.detected++;
|
|
8546
|
+
} catch (err) {
|
|
8547
|
+
result.skipped++;
|
|
8548
|
+
}
|
|
8549
|
+
}
|
|
8550
|
+
}
|
|
8551
|
+
} catch (err) {
|
|
8552
|
+
result.errors.push(
|
|
8553
|
+
`Relationship detection failed: ${err.message}`
|
|
8554
|
+
);
|
|
8555
|
+
}
|
|
8556
|
+
return result;
|
|
8256
8557
|
}
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
8263
|
-
|
|
8264
|
-
const
|
|
8265
|
-
|
|
8266
|
-
|
|
8267
|
-
|
|
8268
|
-
|
|
8558
|
+
/**
|
|
8559
|
+
* Detect relationships for a single skill
|
|
8560
|
+
*/
|
|
8561
|
+
detectSkillRelationships(skill, allSkills) {
|
|
8562
|
+
const relationships = [];
|
|
8563
|
+
const skillId = skill.id || skill.slug;
|
|
8564
|
+
const skillContent = `${skill.name} ${skill.description} ${skill.instructions || ""} ${skill.content || ""}`.toLowerCase();
|
|
8565
|
+
for (const other of allSkills) {
|
|
8566
|
+
const otherId = other.id || other.slug;
|
|
8567
|
+
if (otherId === skillId) continue;
|
|
8568
|
+
const otherName = (other.name || "").toLowerCase();
|
|
8569
|
+
const dependencyPatterns = [
|
|
8570
|
+
"requires",
|
|
8571
|
+
"depends on",
|
|
8572
|
+
"uses",
|
|
8573
|
+
"needs",
|
|
8574
|
+
"builds on"
|
|
8575
|
+
];
|
|
8576
|
+
for (const pattern of dependencyPatterns) {
|
|
8577
|
+
if (skillContent.includes(`${pattern} ${otherName}`)) {
|
|
8578
|
+
relationships.push({
|
|
8579
|
+
targetSkillId: otherId,
|
|
8580
|
+
type: "depends_on",
|
|
8581
|
+
confidence: 0.7,
|
|
8582
|
+
reasoning: `Content mentions "${pattern} ${other.name}"`
|
|
8583
|
+
});
|
|
8584
|
+
break;
|
|
8585
|
+
}
|
|
8586
|
+
}
|
|
8587
|
+
const extensionPatterns = [
|
|
8588
|
+
"extends",
|
|
8589
|
+
"improves",
|
|
8590
|
+
"enhances",
|
|
8591
|
+
"builds upon"
|
|
8592
|
+
];
|
|
8593
|
+
for (const pattern of extensionPatterns) {
|
|
8594
|
+
if (skillContent.includes(`${pattern} ${otherName}`)) {
|
|
8595
|
+
relationships.push({
|
|
8596
|
+
targetSkillId: otherId,
|
|
8597
|
+
type: "extends",
|
|
8598
|
+
confidence: 0.7,
|
|
8599
|
+
reasoning: `Content mentions "${pattern} ${other.name}"`
|
|
8600
|
+
});
|
|
8601
|
+
break;
|
|
8602
|
+
}
|
|
8603
|
+
}
|
|
8604
|
+
const alternativePatterns = [
|
|
8605
|
+
"alternative to",
|
|
8606
|
+
"instead of",
|
|
8607
|
+
"replacement for"
|
|
8608
|
+
];
|
|
8609
|
+
for (const pattern of alternativePatterns) {
|
|
8610
|
+
if (skillContent.includes(`${pattern} ${otherName}`)) {
|
|
8611
|
+
relationships.push({
|
|
8612
|
+
targetSkillId: otherId,
|
|
8613
|
+
type: "alternative",
|
|
8614
|
+
confidence: 0.6,
|
|
8615
|
+
reasoning: `Content mentions "${pattern} ${other.name}"`
|
|
8616
|
+
});
|
|
8617
|
+
break;
|
|
8618
|
+
}
|
|
8619
|
+
}
|
|
8620
|
+
const skillPath = skill.taxonomy?.primaryPath || skill.primaryPath || [];
|
|
8621
|
+
const otherPath = other.taxonomy?.primaryPath || other.primaryPath || [];
|
|
8622
|
+
if (skillPath.length >= 2 && otherPath.length >= 2) {
|
|
8623
|
+
if (skillPath[0] === otherPath[0] && skillPath[1] === otherPath[1]) {
|
|
8624
|
+
relationships.push({
|
|
8625
|
+
targetSkillId: otherId,
|
|
8626
|
+
type: "related",
|
|
8627
|
+
confidence: 0.5,
|
|
8628
|
+
reasoning: `Same taxonomy category: ${skillPath.slice(0, 2).join(" > ")}`
|
|
8629
|
+
});
|
|
8630
|
+
}
|
|
8631
|
+
}
|
|
8269
8632
|
}
|
|
8270
|
-
|
|
8271
|
-
} catch (error) {
|
|
8272
|
-
printError(error.message);
|
|
8273
|
-
process.exit(1);
|
|
8633
|
+
return relationships;
|
|
8274
8634
|
}
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
process.exit(1);
|
|
8287
|
-
}
|
|
8288
|
-
skill.status = "active";
|
|
8289
|
-
skill.updatedAt = /* @__PURE__ */ new Date();
|
|
8290
|
-
await skillBank.saveSkill(skill);
|
|
8291
|
-
if (globalOpts.json) {
|
|
8292
|
-
console.log(JSON.stringify(skill, null, 2));
|
|
8293
|
-
return;
|
|
8635
|
+
/**
|
|
8636
|
+
* Get taxonomy tree
|
|
8637
|
+
*/
|
|
8638
|
+
async getTaxonomyTree(rootPath) {
|
|
8639
|
+
await this.initialize();
|
|
8640
|
+
if (this.skillBank) {
|
|
8641
|
+
const storage = this.skillBank.getStorage();
|
|
8642
|
+
if (hasIndexerSupport(storage)) {
|
|
8643
|
+
const nodes = await storage.getTaxonomyTree(rootPath);
|
|
8644
|
+
return this.wrapTreeNodes(nodes, rootPath);
|
|
8645
|
+
}
|
|
8294
8646
|
}
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
printError(error.message);
|
|
8298
|
-
process.exit(1);
|
|
8647
|
+
const skills = this.skillBank ? await this.skillBank.listSkills() : this.db?.getAllSkills?.() || [];
|
|
8648
|
+
return this.buildTaxonomyTreeFromSkills(skills, rootPath);
|
|
8299
8649
|
}
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
printWarning(`This will permanently delete ${id}${options.version ? `@${options.version}` : " and all versions"}`);
|
|
8315
|
-
printWarning("Use --force to confirm");
|
|
8316
|
-
process.exit(1);
|
|
8317
|
-
}
|
|
8318
|
-
const deleted = await skillBank.deleteSkill(id, options.version);
|
|
8319
|
-
if (!deleted) {
|
|
8320
|
-
printError(`Failed to delete: ${id}`);
|
|
8321
|
-
process.exit(1);
|
|
8322
|
-
}
|
|
8323
|
-
if (globalOpts.json) {
|
|
8324
|
-
console.log(JSON.stringify({ deleted: true, id, version: options.version }));
|
|
8325
|
-
return;
|
|
8650
|
+
/**
|
|
8651
|
+
* Wrap tree nodes returned from SQLite storage into a root node
|
|
8652
|
+
*/
|
|
8653
|
+
wrapTreeNodes(nodes, rootPath) {
|
|
8654
|
+
const root = {
|
|
8655
|
+
id: "root",
|
|
8656
|
+
name: rootPath?.join(" > ") || "All Skills",
|
|
8657
|
+
path: rootPath || [],
|
|
8658
|
+
skillCount: 0,
|
|
8659
|
+
children: []
|
|
8660
|
+
};
|
|
8661
|
+
for (const node of nodes) {
|
|
8662
|
+
root.children.push(this.convertToTaxonomyNode(node));
|
|
8663
|
+
root.skillCount += this.countNodeSkills(node);
|
|
8326
8664
|
}
|
|
8327
|
-
|
|
8328
|
-
} catch (error) {
|
|
8329
|
-
printError(error.message);
|
|
8330
|
-
process.exit(1);
|
|
8665
|
+
return root;
|
|
8331
8666
|
}
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8345
|
-
if (!globalOpts.quiet) {
|
|
8346
|
-
printSuccess(`Exported ${skills.length} skill(s) to ${options.output}`);
|
|
8347
|
-
}
|
|
8348
|
-
} else {
|
|
8349
|
-
console.log(json);
|
|
8350
|
-
}
|
|
8351
|
-
} catch (error) {
|
|
8352
|
-
printError(error.message);
|
|
8353
|
-
process.exit(1);
|
|
8667
|
+
/**
|
|
8668
|
+
* Convert storage tree node to TaxonomyNode format
|
|
8669
|
+
*/
|
|
8670
|
+
convertToTaxonomyNode(node) {
|
|
8671
|
+
return {
|
|
8672
|
+
id: node.id,
|
|
8673
|
+
name: node.name,
|
|
8674
|
+
path: Array.isArray(node.path) ? node.path : (node.path || "").split("/"),
|
|
8675
|
+
skillCount: node.skillCount || 0,
|
|
8676
|
+
children: (node.children || []).map(
|
|
8677
|
+
(child) => this.convertToTaxonomyNode(child)
|
|
8678
|
+
)
|
|
8679
|
+
};
|
|
8354
8680
|
}
|
|
8355
|
-
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
|
|
8359
|
-
|
|
8360
|
-
|
|
8361
|
-
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
const instructions = indexerSkill.content || "";
|
|
8365
|
-
const hasStructuredContent = instructions.trim().length > 0;
|
|
8366
|
-
if (!hasStructuredContent) {
|
|
8367
|
-
warnings.push("No content found, instructions will be empty");
|
|
8681
|
+
/**
|
|
8682
|
+
* Count total skills in a node tree
|
|
8683
|
+
*/
|
|
8684
|
+
countNodeSkills(node) {
|
|
8685
|
+
let count = node.skillCount || 0;
|
|
8686
|
+
for (const child of node.children || []) {
|
|
8687
|
+
count += this.countNodeSkills(child);
|
|
8688
|
+
}
|
|
8689
|
+
return count;
|
|
8368
8690
|
}
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
|
|
8373
|
-
|
|
8691
|
+
/**
|
|
8692
|
+
* Build taxonomy tree from flat nodes
|
|
8693
|
+
*/
|
|
8694
|
+
buildTaxonomyTree(nodes, rootPath) {
|
|
8695
|
+
const root = {
|
|
8696
|
+
id: "root",
|
|
8697
|
+
name: rootPath?.join(" > ") || "All Skills",
|
|
8698
|
+
path: rootPath || [],
|
|
8699
|
+
skillCount: 0,
|
|
8700
|
+
children: []
|
|
8701
|
+
};
|
|
8702
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
8703
|
+
nodeMap.set("root", root);
|
|
8704
|
+
for (const node of nodes) {
|
|
8705
|
+
const taxNode = {
|
|
8706
|
+
id: node.id,
|
|
8707
|
+
name: node.name,
|
|
8708
|
+
path: node.path,
|
|
8709
|
+
skillCount: node.skillCount || 0,
|
|
8710
|
+
children: []
|
|
8711
|
+
};
|
|
8712
|
+
nodeMap.set(node.id, taxNode);
|
|
8713
|
+
}
|
|
8714
|
+
for (const node of nodes) {
|
|
8715
|
+
const taxNode = nodeMap.get(node.id);
|
|
8716
|
+
const parentId = node.parentId || "root";
|
|
8717
|
+
const parent = nodeMap.get(parentId);
|
|
8718
|
+
if (parent) {
|
|
8719
|
+
parent.children.push(taxNode);
|
|
8720
|
+
parent.skillCount += taxNode.skillCount;
|
|
8721
|
+
}
|
|
8374
8722
|
}
|
|
8723
|
+
return root;
|
|
8375
8724
|
}
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8407
|
-
|
|
8725
|
+
/**
|
|
8726
|
+
* Build taxonomy tree from skills
|
|
8727
|
+
*/
|
|
8728
|
+
buildTaxonomyTreeFromSkills(skills, rootPath) {
|
|
8729
|
+
const root = {
|
|
8730
|
+
id: "root",
|
|
8731
|
+
name: rootPath?.join(" > ") || "All Skills",
|
|
8732
|
+
path: rootPath || [],
|
|
8733
|
+
skillCount: 0,
|
|
8734
|
+
children: []
|
|
8735
|
+
};
|
|
8736
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
8737
|
+
for (const skill of skills) {
|
|
8738
|
+
const taxonomy = skill.taxonomy || (skill.primaryPath ? { primaryPath: skill.primaryPath } : null);
|
|
8739
|
+
if (!taxonomy?.primaryPath) continue;
|
|
8740
|
+
const skillPath = taxonomy.primaryPath;
|
|
8741
|
+
if (rootPath && rootPath.length > 0) {
|
|
8742
|
+
let matches = true;
|
|
8743
|
+
for (let i = 0; i < rootPath.length; i++) {
|
|
8744
|
+
if (skillPath[i] !== rootPath[i]) {
|
|
8745
|
+
matches = false;
|
|
8746
|
+
break;
|
|
8747
|
+
}
|
|
8748
|
+
}
|
|
8749
|
+
if (!matches) continue;
|
|
8750
|
+
}
|
|
8751
|
+
let currentPath = [];
|
|
8752
|
+
let parent = root;
|
|
8753
|
+
for (const segment of skillPath) {
|
|
8754
|
+
currentPath = [...currentPath, segment];
|
|
8755
|
+
const pathKey = currentPath.join("/");
|
|
8756
|
+
let node = nodeMap.get(pathKey);
|
|
8757
|
+
if (!node) {
|
|
8758
|
+
node = {
|
|
8759
|
+
id: pathKey,
|
|
8760
|
+
name: segment,
|
|
8761
|
+
path: [...currentPath],
|
|
8762
|
+
skillCount: 0,
|
|
8763
|
+
children: []
|
|
8764
|
+
};
|
|
8765
|
+
nodeMap.set(pathKey, node);
|
|
8766
|
+
parent.children.push(node);
|
|
8767
|
+
}
|
|
8768
|
+
parent = node;
|
|
8769
|
+
}
|
|
8770
|
+
parent.skillCount++;
|
|
8771
|
+
root.skillCount++;
|
|
8408
8772
|
}
|
|
8409
|
-
|
|
8410
|
-
return { skill, warnings, hasStructuredContent };
|
|
8411
|
-
}
|
|
8412
|
-
function convertIndexerSkills(skills) {
|
|
8413
|
-
const results = [];
|
|
8414
|
-
const converted = [];
|
|
8415
|
-
for (const skill of skills) {
|
|
8416
|
-
const result = convertIndexerSkill(skill);
|
|
8417
|
-
results.push(result);
|
|
8418
|
-
converted.push(result.skill);
|
|
8773
|
+
return root;
|
|
8419
8774
|
}
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
|
|
8423
|
-
|
|
8424
|
-
|
|
8425
|
-
|
|
8426
|
-
|
|
8775
|
+
/**
|
|
8776
|
+
* Get indexer statistics
|
|
8777
|
+
*/
|
|
8778
|
+
async getStats() {
|
|
8779
|
+
await this.initialize();
|
|
8780
|
+
if (this.skillBank) {
|
|
8781
|
+
const skills = await this.skillBank.listSkills();
|
|
8782
|
+
const storage = this.skillBank.getStorage();
|
|
8783
|
+
let taxonomyNodes = 0;
|
|
8784
|
+
let relationships = 0;
|
|
8785
|
+
let sources = 0;
|
|
8786
|
+
if (storage) {
|
|
8787
|
+
if (hasIndexerSupport(storage)) {
|
|
8788
|
+
const tree = await storage.getTaxonomyTree();
|
|
8789
|
+
taxonomyNodes = this.countTaxonomyNodes(tree);
|
|
8790
|
+
if (storage.getRelationships) {
|
|
8791
|
+
for (const skill of skills) {
|
|
8792
|
+
const rels = await storage.getRelationships(skill.id);
|
|
8793
|
+
relationships += rels.length;
|
|
8794
|
+
}
|
|
8795
|
+
}
|
|
8796
|
+
}
|
|
8797
|
+
const sourceSet = /* @__PURE__ */ new Set();
|
|
8798
|
+
for (const skill of skills) {
|
|
8799
|
+
if (skill.externalSource?.repo) {
|
|
8800
|
+
sourceSet.add(skill.externalSource.repo);
|
|
8801
|
+
}
|
|
8802
|
+
if (skill.source?.type === "imported" && skill.externalSource?.url) {
|
|
8803
|
+
sourceSet.add(skill.externalSource.url);
|
|
8804
|
+
}
|
|
8805
|
+
}
|
|
8806
|
+
sources = sourceSet.size;
|
|
8807
|
+
}
|
|
8808
|
+
const indexed = skills.filter(
|
|
8809
|
+
(s) => s.status === "active" && (s.taxonomy || s.source?.type === "imported")
|
|
8810
|
+
).length;
|
|
8811
|
+
return {
|
|
8812
|
+
totalSkills: skills.length,
|
|
8813
|
+
indexedSkills: indexed,
|
|
8814
|
+
rawSkills: skills.filter((s) => s.status === "draft").length,
|
|
8815
|
+
failedSkills: skills.filter((s) => s.status === "deprecated").length,
|
|
8816
|
+
taxonomyNodes,
|
|
8817
|
+
relationships,
|
|
8818
|
+
sources
|
|
8819
|
+
};
|
|
8427
8820
|
}
|
|
8428
|
-
|
|
8429
|
-
}
|
|
8430
|
-
|
|
8431
|
-
|
|
8432
|
-
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8437
|
-
|
|
8438
|
-
|
|
8439
|
-
|
|
8440
|
-
|
|
8821
|
+
if (this.db) {
|
|
8822
|
+
const stats = this.db.getStats?.() || {};
|
|
8823
|
+
return {
|
|
8824
|
+
totalSkills: stats.totalSkills || 0,
|
|
8825
|
+
indexedSkills: stats.indexedSkills || 0,
|
|
8826
|
+
rawSkills: stats.rawSkills || 0,
|
|
8827
|
+
failedSkills: stats.failedSkills || 0,
|
|
8828
|
+
taxonomyNodes: stats.taxonomyNodes || 0,
|
|
8829
|
+
relationships: stats.relationships || 0,
|
|
8830
|
+
sources: stats.sources || 0
|
|
8831
|
+
};
|
|
8832
|
+
}
|
|
8833
|
+
return {
|
|
8834
|
+
totalSkills: 0,
|
|
8835
|
+
indexedSkills: 0,
|
|
8836
|
+
rawSkills: 0,
|
|
8837
|
+
failedSkills: 0,
|
|
8838
|
+
taxonomyNodes: 0,
|
|
8839
|
+
relationships: 0,
|
|
8840
|
+
sources: 0
|
|
8441
8841
|
};
|
|
8442
|
-
} else {
|
|
8443
|
-
throw new Error("Invalid indexer export format: expected array or object with skills array");
|
|
8444
8842
|
}
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
8448
|
-
|
|
8449
|
-
|
|
8450
|
-
|
|
8451
|
-
|
|
8452
|
-
|
|
8453
|
-
|
|
8454
|
-
}
|
|
8455
|
-
function isIndexerSkill(obj) {
|
|
8456
|
-
if (typeof obj !== "object" || obj === null) return false;
|
|
8457
|
-
const skill = obj;
|
|
8458
|
-
return typeof skill.slug === "string" && typeof skill.sourceRepo === "string" && typeof skill.sourcePath === "string" && typeof skill.sourceUrl === "string" && typeof skill.content === "string" && typeof skill.scrapedAt === "string" && (skill.status === "raw" || skill.status === "indexed" || skill.status === "failed");
|
|
8459
|
-
}
|
|
8460
|
-
function isIndexerExport(obj) {
|
|
8461
|
-
if (typeof obj !== "object" || obj === null) return false;
|
|
8462
|
-
const exp = obj;
|
|
8463
|
-
return typeof exp.exportedAt === "string" && Array.isArray(exp.skills);
|
|
8464
|
-
}
|
|
8465
|
-
function detectFormat(data) {
|
|
8466
|
-
if (Array.isArray(data)) {
|
|
8467
|
-
if (data.length === 0) {
|
|
8468
|
-
return { format: "unknown", confidence: 0, details: "Empty array" };
|
|
8469
|
-
}
|
|
8470
|
-
const first = data[0];
|
|
8471
|
-
if (isIndexerSkill(first)) {
|
|
8472
|
-
return { format: "indexer", confidence: 0.95, details: "Array of indexer skills" };
|
|
8473
|
-
}
|
|
8474
|
-
if (isSkillTreeSkill(first)) {
|
|
8475
|
-
return { format: "skill-tree", confidence: 0.95, details: "Array of skill-tree skills" };
|
|
8843
|
+
/**
|
|
8844
|
+
* Count nodes in taxonomy tree
|
|
8845
|
+
*/
|
|
8846
|
+
countTaxonomyNodes(nodes) {
|
|
8847
|
+
let count = 0;
|
|
8848
|
+
for (const node of nodes) {
|
|
8849
|
+
count++;
|
|
8850
|
+
if (node.children) {
|
|
8851
|
+
count += this.countTaxonomyNodes(node.children);
|
|
8852
|
+
}
|
|
8476
8853
|
}
|
|
8477
|
-
return
|
|
8854
|
+
return count;
|
|
8478
8855
|
}
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
|
|
8856
|
+
/**
|
|
8857
|
+
* Scrape and index skills directly into SkillBank
|
|
8858
|
+
* This is the streamlined workflow for integrated mode
|
|
8859
|
+
*/
|
|
8860
|
+
async scrapeAndIndex(sources, options) {
|
|
8861
|
+
if (!this.skillBank) {
|
|
8862
|
+
throw new Error(
|
|
8863
|
+
"SkillBank required for scrapeAndIndex. Use integrated mode."
|
|
8864
|
+
);
|
|
8482
8865
|
}
|
|
8483
|
-
|
|
8484
|
-
|
|
8866
|
+
await this.initialize();
|
|
8867
|
+
const skillsAdded = [];
|
|
8868
|
+
const scraped = await this.scrape(sources, { force: options?.force });
|
|
8869
|
+
let indexed = {
|
|
8870
|
+
indexed: 0,
|
|
8871
|
+
skipped: 0,
|
|
8872
|
+
failed: 0,
|
|
8873
|
+
errors: []
|
|
8874
|
+
};
|
|
8875
|
+
if (options?.autoClassify !== false) {
|
|
8876
|
+
indexed = await this.classify({ all: options?.force });
|
|
8485
8877
|
}
|
|
8486
|
-
if (
|
|
8487
|
-
|
|
8878
|
+
if (this.db) {
|
|
8879
|
+
const skills = await (this.db.getAllSkills?.() || this.db.listSkills?.()) || [];
|
|
8880
|
+
for (const skill of skills) {
|
|
8881
|
+
if (skill.status !== "indexed" && !options?.force) continue;
|
|
8882
|
+
try {
|
|
8883
|
+
const converted = convertIndexerSkill(skill);
|
|
8884
|
+
await this.skillBank.saveSkill(converted.skill);
|
|
8885
|
+
skillsAdded.push(converted.skill.id);
|
|
8886
|
+
} catch (err) {
|
|
8887
|
+
indexed.errors.push(
|
|
8888
|
+
`Failed to import ${skill.slug}: ${err.message}`
|
|
8889
|
+
);
|
|
8890
|
+
}
|
|
8891
|
+
}
|
|
8488
8892
|
}
|
|
8893
|
+
let relationships = {
|
|
8894
|
+
detected: 0,
|
|
8895
|
+
skipped: 0,
|
|
8896
|
+
errors: []
|
|
8897
|
+
};
|
|
8898
|
+
if (options?.detectRelationships !== false) {
|
|
8899
|
+
relationships = await this.detectRelationships();
|
|
8900
|
+
}
|
|
8901
|
+
return {
|
|
8902
|
+
scraped,
|
|
8903
|
+
indexed,
|
|
8904
|
+
relationships,
|
|
8905
|
+
skillsAdded
|
|
8906
|
+
};
|
|
8489
8907
|
}
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
} catch {
|
|
8497
|
-
return { format: "unknown", confidence: 0, details: "Invalid JSON" };
|
|
8498
|
-
}
|
|
8499
|
-
}
|
|
8500
|
-
function isLikelyIndexerFormat(content) {
|
|
8501
|
-
const result = detectFormatFromString(content);
|
|
8502
|
-
return result.format === "indexer" || result.format === "indexer-export";
|
|
8503
|
-
}
|
|
8504
|
-
|
|
8505
|
-
// src/cli/commands/import.ts
|
|
8506
|
-
var importCommand = new import_commander13.Command("import").description("Import skills from JSON file").argument("<file>", "JSON file to import").option("--from-indexer", "Import from skill-indexer export format").option("--auto-detect", "Auto-detect format (default: true)", true).action(async (file, options, command) => {
|
|
8507
|
-
const globalOpts = command.optsWithGlobals();
|
|
8508
|
-
try {
|
|
8509
|
-
if (!fs14.existsSync(file)) {
|
|
8510
|
-
printError(`File not found: ${file}`);
|
|
8511
|
-
process.exit(1);
|
|
8908
|
+
/**
|
|
8909
|
+
* Import skills from indexer database into SkillBank
|
|
8910
|
+
*/
|
|
8911
|
+
async importFromIndexerDb(options) {
|
|
8912
|
+
if (!this.skillBank) {
|
|
8913
|
+
throw new Error("SkillBank not configured. Use integrated mode.");
|
|
8512
8914
|
}
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
printInfo(`Original export from: ${exportMetadata.exportedAt}`);
|
|
8527
|
-
}
|
|
8528
|
-
}
|
|
8529
|
-
} catch (err) {
|
|
8530
|
-
printError(`Failed to parse indexer export: ${err.message}`);
|
|
8531
|
-
process.exit(1);
|
|
8915
|
+
await this.initialize();
|
|
8916
|
+
if (!this.db) {
|
|
8917
|
+
throw new Error(
|
|
8918
|
+
"Scraper database not available. Build the scraper module first."
|
|
8919
|
+
);
|
|
8920
|
+
}
|
|
8921
|
+
const result = { imported: 0, failed: 0, skills: [] };
|
|
8922
|
+
try {
|
|
8923
|
+
let skills;
|
|
8924
|
+
if (options?.status) {
|
|
8925
|
+
skills = this.db.getSkillsByStatus?.(options.status) || [];
|
|
8926
|
+
} else {
|
|
8927
|
+
skills = this.db.getAllSkills?.() || [];
|
|
8532
8928
|
}
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
const parsed = JSON.parse(content);
|
|
8536
|
-
skills = Array.isArray(parsed) ? parsed : [parsed];
|
|
8537
|
-
} catch {
|
|
8538
|
-
printError("Invalid JSON file");
|
|
8539
|
-
process.exit(1);
|
|
8929
|
+
if (options?.limit) {
|
|
8930
|
+
skills = skills.slice(0, options.limit);
|
|
8540
8931
|
}
|
|
8541
8932
|
for (const skill of skills) {
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
}
|
|
8550
|
-
if (skill.externalSource?.scrapedAt) {
|
|
8551
|
-
skill.externalSource.scrapedAt = new Date(skill.externalSource.scrapedAt);
|
|
8933
|
+
try {
|
|
8934
|
+
const converted = convertIndexerSkill(skill);
|
|
8935
|
+
await this.skillBank.saveSkill(converted.skill);
|
|
8936
|
+
result.imported++;
|
|
8937
|
+
result.skills.push(converted.skill);
|
|
8938
|
+
} catch (err) {
|
|
8939
|
+
result.failed++;
|
|
8552
8940
|
}
|
|
8553
8941
|
}
|
|
8942
|
+
} catch (err) {
|
|
8943
|
+
throw new Error(`Import failed: ${err.message}`);
|
|
8554
8944
|
}
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
|
|
8562
|
-
|
|
8945
|
+
return result;
|
|
8946
|
+
}
|
|
8947
|
+
/**
|
|
8948
|
+
* Get default skill sources from config
|
|
8949
|
+
*/
|
|
8950
|
+
getDefaultSources() {
|
|
8951
|
+
const configSources = this.globalConfig.indexer.sources;
|
|
8952
|
+
if (configSources.length > 0) {
|
|
8953
|
+
return configSources.map((url) => ({
|
|
8954
|
+
type: "awesome-list",
|
|
8955
|
+
url
|
|
8956
|
+
}));
|
|
8563
8957
|
}
|
|
8564
|
-
|
|
8565
|
-
|
|
8566
|
-
|
|
8958
|
+
return [
|
|
8959
|
+
{
|
|
8960
|
+
type: "awesome-list",
|
|
8961
|
+
url: "https://github.com/VoltAgent/awesome-agent-skills"
|
|
8962
|
+
}
|
|
8963
|
+
];
|
|
8964
|
+
}
|
|
8965
|
+
/**
|
|
8966
|
+
* Close database connections
|
|
8967
|
+
*/
|
|
8968
|
+
async close() {
|
|
8969
|
+
if (this.db && typeof this.db.close === "function") {
|
|
8970
|
+
this.db.close();
|
|
8567
8971
|
}
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
process.exit(1);
|
|
8972
|
+
this.db = void 0;
|
|
8973
|
+
this.initialized = false;
|
|
8571
8974
|
}
|
|
8572
|
-
}
|
|
8975
|
+
};
|
|
8976
|
+
function createIntegratedIndexer(skillBank, config2 = {}) {
|
|
8977
|
+
return new IndexerService(config2, skillBank);
|
|
8978
|
+
}
|
|
8573
8979
|
|
|
8574
|
-
// src/
|
|
8575
|
-
var
|
|
8980
|
+
// src/index.ts
|
|
8981
|
+
var VERSION = "0.1.0";
|
|
8576
8982
|
|
|
8577
|
-
// src/cli/commands/
|
|
8578
|
-
var
|
|
8579
|
-
var import_child_process2 = require("child_process");
|
|
8983
|
+
// src/cli/commands/list.ts
|
|
8984
|
+
var import_commander = require("commander");
|
|
8580
8985
|
|
|
8581
|
-
// src/
|
|
8986
|
+
// src/cli/utils/paths.ts
|
|
8987
|
+
var fs13 = __toESM(require("fs"));
|
|
8582
8988
|
var path13 = __toESM(require("path"));
|
|
8583
|
-
var
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8989
|
+
var os2 = __toESM(require("os"));
|
|
8990
|
+
var DEFAULT_PATHS = [
|
|
8991
|
+
".claude/skills",
|
|
8992
|
+
// Project local (Claude)
|
|
8993
|
+
".agent/skills",
|
|
8994
|
+
// Project local (universal)
|
|
8995
|
+
"~/.claude/skills",
|
|
8996
|
+
// User global (Claude)
|
|
8997
|
+
"~/.agent/skills"
|
|
8998
|
+
// User global (universal)
|
|
8999
|
+
];
|
|
9000
|
+
function expandHome(p) {
|
|
9001
|
+
if (p.startsWith("~/")) {
|
|
9002
|
+
return path13.join(os2.homedir(), p.slice(2));
|
|
9003
|
+
}
|
|
9004
|
+
return p;
|
|
8587
9005
|
}
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8595
|
-
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
|
|
9006
|
+
function dirExists(p) {
|
|
9007
|
+
try {
|
|
9008
|
+
return fs13.statSync(p).isDirectory();
|
|
9009
|
+
} catch {
|
|
9010
|
+
return false;
|
|
9011
|
+
}
|
|
9012
|
+
}
|
|
9013
|
+
function resolveSkillPath(explicitPath) {
|
|
9014
|
+
if (explicitPath) {
|
|
9015
|
+
const resolved = expandHome(explicitPath);
|
|
9016
|
+
return path13.resolve(resolved);
|
|
9017
|
+
}
|
|
9018
|
+
const envPath = process.env.SKILL_TREE_PATH;
|
|
9019
|
+
if (envPath) {
|
|
9020
|
+
return path13.resolve(expandHome(envPath));
|
|
9021
|
+
}
|
|
9022
|
+
for (const defaultPath of DEFAULT_PATHS) {
|
|
9023
|
+
const resolved = path13.resolve(expandHome(defaultPath));
|
|
9024
|
+
if (dirExists(resolved)) {
|
|
9025
|
+
return resolved;
|
|
9026
|
+
}
|
|
9027
|
+
}
|
|
9028
|
+
return path13.resolve(".claude/skills");
|
|
9029
|
+
}
|
|
9030
|
+
function ensureDir(dir) {
|
|
9031
|
+
if (!dirExists(dir)) {
|
|
9032
|
+
fs13.mkdirSync(dir, { recursive: true });
|
|
9033
|
+
}
|
|
9034
|
+
}
|
|
9035
|
+
|
|
9036
|
+
// src/cli/utils/skillbank.ts
|
|
9037
|
+
function getMaterializationConfig() {
|
|
9038
|
+
const config2 = loadConfig();
|
|
9039
|
+
const mat = config2.materialization;
|
|
9040
|
+
if (!mat?.enabled) return void 0;
|
|
9041
|
+
return {
|
|
9042
|
+
enabled: true,
|
|
9043
|
+
mode: mat.mode ?? "symlink",
|
|
9044
|
+
symlinkPaths: mat.symlink_paths?.length ? mat.symlink_paths : void 0,
|
|
9045
|
+
agentsMdPath: mat.agents_md_path || void 0,
|
|
9046
|
+
agentsMdFormat: mat.agents_md_format ?? "xml",
|
|
9047
|
+
debounceMs: mat.debounce_ms ?? 500
|
|
9048
|
+
};
|
|
9049
|
+
}
|
|
9050
|
+
async function createSkillBankFromOptions(options) {
|
|
9051
|
+
const skillPath = resolveSkillPath(options.path);
|
|
9052
|
+
ensureDir(skillPath);
|
|
9053
|
+
const skillBank = new SkillBank({
|
|
9054
|
+
storage: {
|
|
9055
|
+
basePath: skillPath,
|
|
9056
|
+
openSkillsCompatible: true
|
|
9057
|
+
},
|
|
9058
|
+
materialization: getMaterializationConfig()
|
|
9059
|
+
});
|
|
9060
|
+
await skillBank.initialize();
|
|
9061
|
+
return skillBank;
|
|
9062
|
+
}
|
|
9063
|
+
function getSkillPath(options) {
|
|
9064
|
+
return resolveSkillPath(options.path);
|
|
9065
|
+
}
|
|
9066
|
+
var getSkillBank = createSkillBankFromOptions;
|
|
9067
|
+
|
|
9068
|
+
// src/cli/utils/output.ts
|
|
9069
|
+
var import_chalk = __toESM(require("chalk"));
|
|
9070
|
+
function formatSkillLine(skill) {
|
|
9071
|
+
const status = formatStatus(skill.status);
|
|
9072
|
+
const version = import_chalk.default.dim(`v${skill.version}`);
|
|
9073
|
+
const id = import_chalk.default.bold(skill.id);
|
|
9074
|
+
const name = skill.name !== skill.id ? import_chalk.default.dim(` (${skill.name})`) : "";
|
|
9075
|
+
const tags = skill.tags.length > 0 ? import_chalk.default.dim(` [${skill.tags.join(", ")}]`) : "";
|
|
9076
|
+
return `${status} ${id}${name} ${version}${tags}`;
|
|
9077
|
+
}
|
|
9078
|
+
function formatStatus(status) {
|
|
9079
|
+
switch (status) {
|
|
9080
|
+
case "active":
|
|
9081
|
+
return import_chalk.default.green("\u25CF");
|
|
9082
|
+
case "draft":
|
|
9083
|
+
return import_chalk.default.yellow("\u25CB");
|
|
9084
|
+
case "deprecated":
|
|
9085
|
+
return import_chalk.default.red("\u2717");
|
|
9086
|
+
case "experimental":
|
|
9087
|
+
return import_chalk.default.blue("\u25D0");
|
|
9088
|
+
default:
|
|
9089
|
+
return import_chalk.default.gray("?");
|
|
9090
|
+
}
|
|
9091
|
+
}
|
|
9092
|
+
function formatSkillDetail(skill) {
|
|
9093
|
+
const lines = [];
|
|
9094
|
+
lines.push(import_chalk.default.bold(`${skill.name} v${skill.version}`));
|
|
9095
|
+
lines.push(import_chalk.default.dim("\u2500".repeat(50)));
|
|
9096
|
+
lines.push(`${import_chalk.default.dim("Status:")} ${formatStatusLabel(skill.status)}`);
|
|
9097
|
+
lines.push(`${import_chalk.default.dim("Author:")} ${skill.author}`);
|
|
9098
|
+
if (skill.tags.length > 0) {
|
|
9099
|
+
lines.push(`${import_chalk.default.dim("Tags:")} ${skill.tags.join(", ")}`);
|
|
9100
|
+
}
|
|
9101
|
+
if (skill.parentVersion) {
|
|
9102
|
+
lines.push(`${import_chalk.default.dim("Parent:")} v${skill.parentVersion}`);
|
|
9103
|
+
}
|
|
9104
|
+
if (skill.derivedFrom && skill.derivedFrom.length > 0) {
|
|
9105
|
+
lines.push(`${import_chalk.default.dim("Derived:")} ${skill.derivedFrom.join(", ")}`);
|
|
9106
|
+
}
|
|
9107
|
+
const { usageCount, successRate } = skill.metrics;
|
|
9108
|
+
if (usageCount > 0) {
|
|
9109
|
+
lines.push(`${import_chalk.default.dim("Usage:")} ${usageCount} times, ${Math.round(successRate * 100)}% success`);
|
|
9110
|
+
}
|
|
9111
|
+
lines.push("");
|
|
9112
|
+
lines.push(import_chalk.default.dim("Description"));
|
|
9113
|
+
lines.push(import_chalk.default.dim("\u2500".repeat(50)));
|
|
9114
|
+
lines.push(skill.description);
|
|
9115
|
+
lines.push("");
|
|
9116
|
+
if (skill.instructions) {
|
|
9117
|
+
lines.push(import_chalk.default.dim("Instructions"));
|
|
9118
|
+
lines.push(import_chalk.default.dim("\u2500".repeat(50)));
|
|
9119
|
+
lines.push(skill.instructions);
|
|
9120
|
+
}
|
|
9121
|
+
return lines.join("\n");
|
|
9122
|
+
}
|
|
9123
|
+
function formatStatusLabel(status) {
|
|
9124
|
+
switch (status) {
|
|
9125
|
+
case "active":
|
|
9126
|
+
return import_chalk.default.green("active");
|
|
9127
|
+
case "draft":
|
|
9128
|
+
return import_chalk.default.yellow("draft");
|
|
9129
|
+
case "deprecated":
|
|
9130
|
+
return import_chalk.default.red("deprecated");
|
|
9131
|
+
case "experimental":
|
|
9132
|
+
return import_chalk.default.blue("experimental");
|
|
9133
|
+
default:
|
|
9134
|
+
return import_chalk.default.gray(status);
|
|
9135
|
+
}
|
|
9136
|
+
}
|
|
9137
|
+
function formatVersionHistory(versions) {
|
|
9138
|
+
const lines = [];
|
|
9139
|
+
lines.push(import_chalk.default.bold("Version History"));
|
|
9140
|
+
lines.push(import_chalk.default.dim("\u2500".repeat(50)));
|
|
9141
|
+
for (const v of versions.reverse()) {
|
|
9142
|
+
const date = v.createdAt.toISOString().split("T")[0];
|
|
9143
|
+
const hash = import_chalk.default.dim(`#${v.contentHash.slice(0, 7)}`);
|
|
9144
|
+
const changelog = v.changelog ? ` - ${v.changelog}` : "";
|
|
9145
|
+
lines.push(` ${import_chalk.default.cyan(`v${v.version}`)} ${import_chalk.default.dim(date)} ${hash}${changelog}`);
|
|
9146
|
+
}
|
|
9147
|
+
return lines.join("\n");
|
|
9148
|
+
}
|
|
9149
|
+
function formatDiff(versionA, versionB, changes) {
|
|
9150
|
+
const lines = [];
|
|
9151
|
+
lines.push(import_chalk.default.bold(`Diff: v${versionA} \u2192 v${versionB}`));
|
|
9152
|
+
lines.push(import_chalk.default.dim("\u2500".repeat(50)));
|
|
9153
|
+
if (changes.modified.length === 0 && changes.added.length === 0 && changes.removed.length === 0) {
|
|
9154
|
+
lines.push(import_chalk.default.dim("No changes"));
|
|
9155
|
+
return lines.join("\n");
|
|
9156
|
+
}
|
|
9157
|
+
for (const mod of changes.modified) {
|
|
9158
|
+
lines.push(import_chalk.default.yellow(`~ ${mod.field}`));
|
|
9159
|
+
lines.push(import_chalk.default.red(` - ${truncate(mod.oldValue, 60)}`));
|
|
9160
|
+
lines.push(import_chalk.default.green(` + ${truncate(mod.newValue, 60)}`));
|
|
8604
9161
|
}
|
|
8605
|
-
|
|
8606
|
-
|
|
8607
|
-
*/
|
|
8608
|
-
getConfig() {
|
|
8609
|
-
return { ...this.serviceConfig };
|
|
9162
|
+
for (const add of changes.added) {
|
|
9163
|
+
lines.push(import_chalk.default.green(`+ ${add.type}: ${add.value}`));
|
|
8610
9164
|
}
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
*/
|
|
8614
|
-
async initialize() {
|
|
8615
|
-
if (this.initialized) return;
|
|
8616
|
-
try {
|
|
8617
|
-
const possiblePaths = [
|
|
8618
|
-
// Relative to this file in dist
|
|
8619
|
-
path13.resolve(__dirname, "../../scraper/dist"),
|
|
8620
|
-
// Relative to project root
|
|
8621
|
-
path13.resolve(process.cwd(), "scraper/dist"),
|
|
8622
|
-
// Absolute paths from config
|
|
8623
|
-
this.serviceConfig.cacheDir ? path13.resolve(this.serviceConfig.cacheDir, "../scraper/dist") : null
|
|
8624
|
-
].filter(Boolean);
|
|
8625
|
-
let scraperBasePath = null;
|
|
8626
|
-
for (const basePath of possiblePaths) {
|
|
8627
|
-
const scraperIndex = path13.join(basePath, "scraper/index.js");
|
|
8628
|
-
if (fs15.existsSync(scraperIndex)) {
|
|
8629
|
-
scraperBasePath = basePath;
|
|
8630
|
-
break;
|
|
8631
|
-
}
|
|
8632
|
-
}
|
|
8633
|
-
if (!scraperBasePath) {
|
|
8634
|
-
throw new Error("Scraper modules not found. Run `cd scraper && npm run build` first.");
|
|
8635
|
-
}
|
|
8636
|
-
const scraperPath = path13.join(scraperBasePath, "scraper/index.js");
|
|
8637
|
-
const indexerPath = path13.join(scraperBasePath, "indexer/index.js");
|
|
8638
|
-
const databasePath = path13.join(scraperBasePath, "database/index.js");
|
|
8639
|
-
this.scraperModule = await import(
|
|
8640
|
-
/* webpackIgnore: true */
|
|
8641
|
-
scraperPath
|
|
8642
|
-
);
|
|
8643
|
-
this.indexerModule = await import(
|
|
8644
|
-
/* webpackIgnore: true */
|
|
8645
|
-
indexerPath
|
|
8646
|
-
);
|
|
8647
|
-
this.databaseModule = await import(
|
|
8648
|
-
/* webpackIgnore: true */
|
|
8649
|
-
databasePath
|
|
8650
|
-
);
|
|
8651
|
-
if (this.databaseModule.createDatabase) {
|
|
8652
|
-
const dbPath = this.serviceConfig.databasePath || path13.join(process.cwd(), "scraper/data/skills.db");
|
|
8653
|
-
this.db = this.databaseModule.createDatabase(dbPath);
|
|
8654
|
-
}
|
|
8655
|
-
this.initialized = true;
|
|
8656
|
-
} catch (err) {
|
|
8657
|
-
console.warn(`Scraper modules not available: ${err.message}`);
|
|
8658
|
-
console.warn("Some indexer features will be limited.");
|
|
8659
|
-
this.initialized = true;
|
|
8660
|
-
}
|
|
9165
|
+
for (const rem of changes.removed) {
|
|
9166
|
+
lines.push(import_chalk.default.red(`- ${rem.type}: ${rem.value}`));
|
|
8661
9167
|
}
|
|
8662
|
-
|
|
8663
|
-
|
|
8664
|
-
|
|
8665
|
-
|
|
8666
|
-
|
|
9168
|
+
return lines.join("\n");
|
|
9169
|
+
}
|
|
9170
|
+
function formatStats(stats) {
|
|
9171
|
+
const lines = [];
|
|
9172
|
+
lines.push(import_chalk.default.bold("Skill Bank Statistics"));
|
|
9173
|
+
lines.push(import_chalk.default.dim("\u2500".repeat(50)));
|
|
9174
|
+
lines.push(`${import_chalk.default.dim("Total skills:")} ${stats.totalSkills}`);
|
|
9175
|
+
lines.push("");
|
|
9176
|
+
lines.push(import_chalk.default.dim("By Status:"));
|
|
9177
|
+
lines.push(` ${import_chalk.default.green("\u25CF")} Active: ${stats.byStatus.active}`);
|
|
9178
|
+
lines.push(` ${import_chalk.default.yellow("\u25CB")} Draft: ${stats.byStatus.draft}`);
|
|
9179
|
+
lines.push(` ${import_chalk.default.blue("\u25D0")} Experimental: ${stats.byStatus.experimental}`);
|
|
9180
|
+
lines.push(` ${import_chalk.default.red("\u2717")} Deprecated: ${stats.byStatus.deprecated}`);
|
|
9181
|
+
lines.push("");
|
|
9182
|
+
if (Object.keys(stats.byTag).length > 0) {
|
|
9183
|
+
lines.push(import_chalk.default.dim("By Tag:"));
|
|
9184
|
+
const sortedTags = Object.entries(stats.byTag).sort((a, b) => b[1] - a[1]);
|
|
9185
|
+
for (const [tag, count] of sortedTags.slice(0, 10)) {
|
|
9186
|
+
lines.push(` ${tag}: ${count}`);
|
|
9187
|
+
}
|
|
9188
|
+
lines.push("");
|
|
8667
9189
|
}
|
|
8668
|
-
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
return this.initialized && !this.scraperModule;
|
|
9190
|
+
if (stats.totalUsage > 0) {
|
|
9191
|
+
lines.push(import_chalk.default.dim("Usage:"));
|
|
9192
|
+
lines.push(` Total uses: ${stats.totalUsage}`);
|
|
9193
|
+
lines.push(` Avg success: ${Math.round(stats.avgSuccessRate * 100)}%`);
|
|
8673
9194
|
}
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
|
|
8677
|
-
|
|
8678
|
-
|
|
8679
|
-
|
|
8680
|
-
|
|
8681
|
-
|
|
8682
|
-
|
|
9195
|
+
return lines.join("\n");
|
|
9196
|
+
}
|
|
9197
|
+
function printError(message) {
|
|
9198
|
+
console.error(import_chalk.default.red(`Error: ${message}`));
|
|
9199
|
+
}
|
|
9200
|
+
function printSuccess(message) {
|
|
9201
|
+
console.log(import_chalk.default.green(`\u2713 ${message}`));
|
|
9202
|
+
}
|
|
9203
|
+
function printWarning(message) {
|
|
9204
|
+
console.log(import_chalk.default.yellow(`\u26A0 ${message}`));
|
|
9205
|
+
}
|
|
9206
|
+
function printInfo(message) {
|
|
9207
|
+
console.log(import_chalk.default.dim(message));
|
|
9208
|
+
}
|
|
9209
|
+
function truncate(text, maxLength) {
|
|
9210
|
+
const oneLine = text.replace(/\n/g, " ");
|
|
9211
|
+
if (oneLine.length <= maxLength) return oneLine;
|
|
9212
|
+
return oneLine.slice(0, maxLength - 3) + "...";
|
|
9213
|
+
}
|
|
9214
|
+
|
|
9215
|
+
// src/cli/commands/list.ts
|
|
9216
|
+
var listCommand = new import_commander.Command("list").description("List all skills").option("-s, --status <status>", "Filter by status (active, draft, deprecated, experimental)").option("-t, --tag <tag>", "Filter by tag").option("-a, --author <author>", "Filter by author").action(async (options, command) => {
|
|
9217
|
+
const globalOpts = command.optsWithGlobals();
|
|
9218
|
+
try {
|
|
9219
|
+
const skillBank = await createSkillBankFromOptions(globalOpts);
|
|
9220
|
+
const filter = {};
|
|
9221
|
+
if (options.status) {
|
|
9222
|
+
filter.status = [options.status];
|
|
8683
9223
|
}
|
|
8684
|
-
|
|
8685
|
-
|
|
8686
|
-
scraped: 0,
|
|
8687
|
-
skipped: 0,
|
|
8688
|
-
failed: 0,
|
|
8689
|
-
unchanged: 0,
|
|
8690
|
-
errors: []
|
|
8691
|
-
};
|
|
8692
|
-
try {
|
|
8693
|
-
const scraper = this.scraperModule.createScraper({
|
|
8694
|
-
githubToken: this.serviceConfig.githubToken,
|
|
8695
|
-
cacheDir: this.serviceConfig.cacheDir,
|
|
8696
|
-
cacheTtlSeconds: this.serviceConfig.cacheTtlSeconds
|
|
8697
|
-
});
|
|
8698
|
-
for (const source of sources) {
|
|
8699
|
-
try {
|
|
8700
|
-
let skills;
|
|
8701
|
-
if (source.type === "awesome-list") {
|
|
8702
|
-
skills = await scraper.scrapeAwesomeList(source.url, {
|
|
8703
|
-
force: options?.force
|
|
8704
|
-
});
|
|
8705
|
-
} else {
|
|
8706
|
-
skills = await scraper.scrapeRepository(source.url, {
|
|
8707
|
-
force: options?.force
|
|
8708
|
-
});
|
|
8709
|
-
}
|
|
8710
|
-
result.discovered += skills.length;
|
|
8711
|
-
for (const skill of skills) {
|
|
8712
|
-
try {
|
|
8713
|
-
const existing = this.db.getSkillBySlug?.(skill.slug);
|
|
8714
|
-
if (existing && !options?.force) {
|
|
8715
|
-
result.unchanged++;
|
|
8716
|
-
} else {
|
|
8717
|
-
this.db.saveSkill?.(skill);
|
|
8718
|
-
result.scraped++;
|
|
8719
|
-
}
|
|
8720
|
-
} catch (err) {
|
|
8721
|
-
result.failed++;
|
|
8722
|
-
result.errors.push(`Failed to save skill ${skill.slug}: ${err.message}`);
|
|
8723
|
-
}
|
|
8724
|
-
}
|
|
8725
|
-
} catch (err) {
|
|
8726
|
-
result.failed++;
|
|
8727
|
-
result.errors.push(`Failed to scrape ${source.url}: ${err.message}`);
|
|
8728
|
-
}
|
|
8729
|
-
}
|
|
8730
|
-
} catch (err) {
|
|
8731
|
-
result.errors.push(`Scrape failed: ${err.message}`);
|
|
9224
|
+
if (options.tag) {
|
|
9225
|
+
filter.tags = [options.tag];
|
|
8732
9226
|
}
|
|
8733
|
-
|
|
8734
|
-
|
|
8735
|
-
/**
|
|
8736
|
-
* Classify unindexed skills using AI
|
|
8737
|
-
*/
|
|
8738
|
-
async classify(options) {
|
|
8739
|
-
await this.initialize();
|
|
8740
|
-
if (!this.indexerModule || !this.db) {
|
|
8741
|
-
throw new Error(
|
|
8742
|
-
"Indexer not available. Build the scraper module first: cd scraper && npm run build"
|
|
8743
|
-
);
|
|
9227
|
+
if (options.author) {
|
|
9228
|
+
filter.author = options.author;
|
|
8744
9229
|
}
|
|
8745
|
-
const
|
|
8746
|
-
|
|
8747
|
-
|
|
8748
|
-
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
|
|
8752
|
-
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
8758
|
-
|
|
8759
|
-
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
}
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
|
|
8770
|
-
|
|
8771
|
-
|
|
8772
|
-
|
|
8773
|
-
|
|
8774
|
-
|
|
8775
|
-
|
|
8776
|
-
|
|
8777
|
-
|
|
8778
|
-
|
|
8779
|
-
|
|
8780
|
-
|
|
8781
|
-
|
|
8782
|
-
|
|
8783
|
-
result.indexed++;
|
|
8784
|
-
} catch (err) {
|
|
8785
|
-
result.failed++;
|
|
8786
|
-
result.errors.push(`Failed to classify ${skill.slug}: ${err.message}`);
|
|
8787
|
-
}
|
|
8788
|
-
}
|
|
8789
|
-
}
|
|
8790
|
-
} catch (err) {
|
|
8791
|
-
result.errors.push(`Classification failed: ${err.message}`);
|
|
9230
|
+
const skills = await skillBank.listSkills(
|
|
9231
|
+
Object.keys(filter).length > 0 ? filter : void 0
|
|
9232
|
+
);
|
|
9233
|
+
if (globalOpts.json) {
|
|
9234
|
+
console.log(JSON.stringify(skills, null, 2));
|
|
9235
|
+
return;
|
|
9236
|
+
}
|
|
9237
|
+
if (skills.length === 0) {
|
|
9238
|
+
printInfo(`No skills found in ${getSkillPath(globalOpts)}`);
|
|
9239
|
+
return;
|
|
9240
|
+
}
|
|
9241
|
+
if (!globalOpts.quiet) {
|
|
9242
|
+
printInfo(`Skills in ${getSkillPath(globalOpts)}:
|
|
9243
|
+
`);
|
|
9244
|
+
}
|
|
9245
|
+
for (const skill of skills) {
|
|
9246
|
+
console.log(formatSkillLine(skill));
|
|
9247
|
+
}
|
|
9248
|
+
if (!globalOpts.quiet) {
|
|
9249
|
+
console.log();
|
|
9250
|
+
printInfo(`${skills.length} skill(s)`);
|
|
9251
|
+
}
|
|
9252
|
+
} catch (error) {
|
|
9253
|
+
printError(error.message);
|
|
9254
|
+
process.exit(1);
|
|
9255
|
+
}
|
|
9256
|
+
});
|
|
9257
|
+
|
|
9258
|
+
// src/cli/commands/show.ts
|
|
9259
|
+
var import_commander2 = require("commander");
|
|
9260
|
+
var showCommand = new import_commander2.Command("show").description("Show skill details").argument("<id>", "Skill ID").option("-v, --version <version>", "Show specific version").action(async (id, options, command) => {
|
|
9261
|
+
const globalOpts = command.optsWithGlobals();
|
|
9262
|
+
try {
|
|
9263
|
+
const skillBank = await createSkillBankFromOptions(globalOpts);
|
|
9264
|
+
const skill = await skillBank.getSkill(id, options.version);
|
|
9265
|
+
if (!skill) {
|
|
9266
|
+
printError(`Skill not found: ${id}${options.version ? `@${options.version}` : ""}`);
|
|
9267
|
+
process.exit(1);
|
|
8792
9268
|
}
|
|
8793
|
-
|
|
9269
|
+
if (globalOpts.json) {
|
|
9270
|
+
console.log(JSON.stringify(skill, null, 2));
|
|
9271
|
+
return;
|
|
9272
|
+
}
|
|
9273
|
+
console.log(formatSkillDetail(skill));
|
|
9274
|
+
} catch (error) {
|
|
9275
|
+
printError(error.message);
|
|
9276
|
+
process.exit(1);
|
|
8794
9277
|
}
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
8799
|
-
|
|
8800
|
-
|
|
8801
|
-
|
|
8802
|
-
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
const storage = this.skillBank.getStorage();
|
|
8808
|
-
if (hasIndexerSupport(storage)) {
|
|
8809
|
-
const skills = await this.skillBank.listSkills();
|
|
8810
|
-
const targetSkills = options?.skillId ? skills.filter((s) => s.id === options.skillId) : skills;
|
|
8811
|
-
for (const skill of targetSkills) {
|
|
8812
|
-
const relationships = this.detectSkillRelationships(skill, skills);
|
|
8813
|
-
for (const rel of relationships) {
|
|
8814
|
-
try {
|
|
8815
|
-
await storage.addRelationship(
|
|
8816
|
-
skill.id,
|
|
8817
|
-
rel.targetSkillId,
|
|
8818
|
-
rel.type,
|
|
8819
|
-
rel.confidence,
|
|
8820
|
-
rel.reasoning
|
|
8821
|
-
);
|
|
8822
|
-
result.detected++;
|
|
8823
|
-
} catch (err) {
|
|
8824
|
-
result.skipped++;
|
|
8825
|
-
}
|
|
8826
|
-
}
|
|
8827
|
-
}
|
|
8828
|
-
}
|
|
8829
|
-
} catch (err) {
|
|
8830
|
-
result.errors.push(`Relationship detection failed: ${err.message}`);
|
|
8831
|
-
}
|
|
8832
|
-
return result;
|
|
9278
|
+
});
|
|
9279
|
+
|
|
9280
|
+
// src/cli/commands/search.ts
|
|
9281
|
+
var import_commander3 = require("commander");
|
|
9282
|
+
var searchCommand = new import_commander3.Command("search").description("Search skills by text").argument("<query>", "Search query").action(async (query, options, command) => {
|
|
9283
|
+
const globalOpts = command.optsWithGlobals();
|
|
9284
|
+
try {
|
|
9285
|
+
const skillBank = await createSkillBankFromOptions(globalOpts);
|
|
9286
|
+
const skills = await skillBank.searchSkills(query);
|
|
9287
|
+
if (globalOpts.json) {
|
|
9288
|
+
console.log(JSON.stringify(skills, null, 2));
|
|
9289
|
+
return;
|
|
8833
9290
|
}
|
|
8834
|
-
if (
|
|
8835
|
-
|
|
8836
|
-
|
|
8837
|
-
);
|
|
9291
|
+
if (skills.length === 0) {
|
|
9292
|
+
printInfo(`No skills found matching "${query}"`);
|
|
9293
|
+
return;
|
|
8838
9294
|
}
|
|
8839
|
-
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
for (const skill of targetSkills) {
|
|
8843
|
-
const relationships = this.detectSkillRelationships(skill, skills);
|
|
8844
|
-
for (const rel of relationships) {
|
|
8845
|
-
try {
|
|
8846
|
-
this.db.saveRelationship?.({
|
|
8847
|
-
sourceSkillId: skill.id,
|
|
8848
|
-
...rel
|
|
8849
|
-
});
|
|
8850
|
-
result.detected++;
|
|
8851
|
-
} catch (err) {
|
|
8852
|
-
result.skipped++;
|
|
8853
|
-
}
|
|
8854
|
-
}
|
|
8855
|
-
}
|
|
8856
|
-
} catch (err) {
|
|
8857
|
-
result.errors.push(`Relationship detection failed: ${err.message}`);
|
|
9295
|
+
if (!globalOpts.quiet) {
|
|
9296
|
+
printInfo(`Results for "${query}":
|
|
9297
|
+
`);
|
|
8858
9298
|
}
|
|
8859
|
-
|
|
9299
|
+
for (const skill of skills) {
|
|
9300
|
+
console.log(formatSkillLine(skill));
|
|
9301
|
+
}
|
|
9302
|
+
if (!globalOpts.quiet) {
|
|
9303
|
+
console.log();
|
|
9304
|
+
printInfo(`${skills.length} result(s)`);
|
|
9305
|
+
}
|
|
9306
|
+
} catch (error) {
|
|
9307
|
+
printError(error.message);
|
|
9308
|
+
process.exit(1);
|
|
8860
9309
|
}
|
|
8861
|
-
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
|
|
8871
|
-
|
|
8872
|
-
|
|
8873
|
-
for (const pattern of dependencyPatterns) {
|
|
8874
|
-
if (skillContent.includes(`${pattern} ${otherName}`)) {
|
|
8875
|
-
relationships.push({
|
|
8876
|
-
targetSkillId: otherId,
|
|
8877
|
-
type: "depends_on",
|
|
8878
|
-
confidence: 0.7,
|
|
8879
|
-
reasoning: `Content mentions "${pattern} ${other.name}"`
|
|
8880
|
-
});
|
|
8881
|
-
break;
|
|
8882
|
-
}
|
|
8883
|
-
}
|
|
8884
|
-
const extensionPatterns = ["extends", "improves", "enhances", "builds upon"];
|
|
8885
|
-
for (const pattern of extensionPatterns) {
|
|
8886
|
-
if (skillContent.includes(`${pattern} ${otherName}`)) {
|
|
8887
|
-
relationships.push({
|
|
8888
|
-
targetSkillId: otherId,
|
|
8889
|
-
type: "extends",
|
|
8890
|
-
confidence: 0.7,
|
|
8891
|
-
reasoning: `Content mentions "${pattern} ${other.name}"`
|
|
8892
|
-
});
|
|
8893
|
-
break;
|
|
8894
|
-
}
|
|
8895
|
-
}
|
|
8896
|
-
const alternativePatterns = ["alternative to", "instead of", "replacement for"];
|
|
8897
|
-
for (const pattern of alternativePatterns) {
|
|
8898
|
-
if (skillContent.includes(`${pattern} ${otherName}`)) {
|
|
8899
|
-
relationships.push({
|
|
8900
|
-
targetSkillId: otherId,
|
|
8901
|
-
type: "alternative",
|
|
8902
|
-
confidence: 0.6,
|
|
8903
|
-
reasoning: `Content mentions "${pattern} ${other.name}"`
|
|
8904
|
-
});
|
|
8905
|
-
break;
|
|
8906
|
-
}
|
|
8907
|
-
}
|
|
8908
|
-
const skillPath = skill.taxonomy?.primaryPath || skill.primaryPath || [];
|
|
8909
|
-
const otherPath = other.taxonomy?.primaryPath || other.primaryPath || [];
|
|
8910
|
-
if (skillPath.length >= 2 && otherPath.length >= 2) {
|
|
8911
|
-
if (skillPath[0] === otherPath[0] && skillPath[1] === otherPath[1]) {
|
|
8912
|
-
relationships.push({
|
|
8913
|
-
targetSkillId: otherId,
|
|
8914
|
-
type: "related",
|
|
8915
|
-
confidence: 0.5,
|
|
8916
|
-
reasoning: `Same taxonomy category: ${skillPath.slice(0, 2).join(" > ")}`
|
|
8917
|
-
});
|
|
8918
|
-
}
|
|
8919
|
-
}
|
|
9310
|
+
});
|
|
9311
|
+
|
|
9312
|
+
// src/cli/commands/stats.ts
|
|
9313
|
+
var import_commander4 = require("commander");
|
|
9314
|
+
var statsCommand = new import_commander4.Command("stats").description("Show skill bank statistics").action(async (options, command) => {
|
|
9315
|
+
const globalOpts = command.optsWithGlobals();
|
|
9316
|
+
try {
|
|
9317
|
+
const skillBank = await createSkillBankFromOptions(globalOpts);
|
|
9318
|
+
const stats = await skillBank.getStats();
|
|
9319
|
+
if (globalOpts.json) {
|
|
9320
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
9321
|
+
return;
|
|
8920
9322
|
}
|
|
8921
|
-
|
|
9323
|
+
console.log(formatStats(stats));
|
|
9324
|
+
} catch (error) {
|
|
9325
|
+
printError(error.message);
|
|
9326
|
+
process.exit(1);
|
|
8922
9327
|
}
|
|
8923
|
-
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
|
|
8933
|
-
}
|
|
9328
|
+
});
|
|
9329
|
+
|
|
9330
|
+
// src/cli/commands/versions.ts
|
|
9331
|
+
var import_commander5 = require("commander");
|
|
9332
|
+
var versionsCommand = new import_commander5.Command("versions").description("Show version history for a skill").argument("<id>", "Skill ID").action(async (id, options, command) => {
|
|
9333
|
+
const globalOpts = command.optsWithGlobals();
|
|
9334
|
+
try {
|
|
9335
|
+
const skillBank = await createSkillBankFromOptions(globalOpts);
|
|
9336
|
+
const versions = await skillBank.getVersionHistory(id);
|
|
9337
|
+
if (versions.length === 0) {
|
|
9338
|
+
printInfo(`No version history found for: ${id}`);
|
|
9339
|
+
return;
|
|
8934
9340
|
}
|
|
8935
|
-
|
|
8936
|
-
|
|
9341
|
+
if (globalOpts.json) {
|
|
9342
|
+
console.log(JSON.stringify(versions, null, 2));
|
|
9343
|
+
return;
|
|
9344
|
+
}
|
|
9345
|
+
console.log(formatVersionHistory(versions));
|
|
9346
|
+
} catch (error) {
|
|
9347
|
+
printError(error.message);
|
|
9348
|
+
process.exit(1);
|
|
8937
9349
|
}
|
|
8938
|
-
|
|
8939
|
-
|
|
8940
|
-
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
|
|
9350
|
+
});
|
|
9351
|
+
|
|
9352
|
+
// src/cli/commands/diff.ts
|
|
9353
|
+
var import_commander6 = require("commander");
|
|
9354
|
+
var diffCommand = new import_commander6.Command("diff").description("Compare two versions of a skill").argument("<id>", "Skill ID").argument("<version-a>", "First version").argument("<version-b>", "Second version").action(async (id, versionA, versionB, options, command) => {
|
|
9355
|
+
const globalOpts = command.optsWithGlobals();
|
|
9356
|
+
try {
|
|
9357
|
+
const skillBank = await createSkillBankFromOptions(globalOpts);
|
|
9358
|
+
const diff = await skillBank.compareVersions(id, versionA, versionB);
|
|
9359
|
+
if (globalOpts.json) {
|
|
9360
|
+
console.log(JSON.stringify(diff, null, 2));
|
|
9361
|
+
return;
|
|
9362
|
+
}
|
|
9363
|
+
console.log(formatDiff(diff.versionA, diff.versionB, diff.changes));
|
|
9364
|
+
} catch (error) {
|
|
9365
|
+
printError(error.message);
|
|
9366
|
+
process.exit(1);
|
|
9367
|
+
}
|
|
9368
|
+
});
|
|
9369
|
+
|
|
9370
|
+
// src/cli/commands/rollback.ts
|
|
9371
|
+
var import_commander7 = require("commander");
|
|
9372
|
+
var rollbackCommand = new import_commander7.Command("rollback").description("Rollback a skill to a previous version").argument("<id>", "Skill ID").requiredOption("--to <version>", "Version to rollback to").action(async (id, options, command) => {
|
|
9373
|
+
const globalOpts = command.optsWithGlobals();
|
|
9374
|
+
try {
|
|
9375
|
+
const skillBank = await createSkillBankFromOptions(globalOpts);
|
|
9376
|
+
const skill = await skillBank.rollbackSkill(id, options.to);
|
|
9377
|
+
if (globalOpts.json) {
|
|
9378
|
+
console.log(JSON.stringify(skill, null, 2));
|
|
9379
|
+
return;
|
|
8952
9380
|
}
|
|
8953
|
-
|
|
9381
|
+
printSuccess(`Rolled back ${id} to v${options.to} (new version: v${skill.version})`);
|
|
9382
|
+
} catch (error) {
|
|
9383
|
+
printError(error.message);
|
|
9384
|
+
process.exit(1);
|
|
8954
9385
|
}
|
|
8955
|
-
|
|
8956
|
-
|
|
8957
|
-
|
|
8958
|
-
|
|
8959
|
-
|
|
8960
|
-
|
|
8961
|
-
|
|
8962
|
-
|
|
8963
|
-
|
|
8964
|
-
|
|
8965
|
-
|
|
9386
|
+
});
|
|
9387
|
+
|
|
9388
|
+
// src/cli/commands/fork.ts
|
|
9389
|
+
var import_commander8 = require("commander");
|
|
9390
|
+
var forkCommand = new import_commander8.Command("fork").description("Fork a skill to create a variant").argument("<id>", "Skill ID to fork from").requiredOption("--new-id <id>", "ID for the new forked skill").requiredOption("--reason <reason>", "Reason for forking").option("--name <name>", "Name for the forked skill").option("--from-version <version>", "Version to fork from (defaults to latest)").action(async (id, options, command) => {
|
|
9391
|
+
const globalOpts = command.optsWithGlobals();
|
|
9392
|
+
try {
|
|
9393
|
+
const skillBank = await createSkillBankFromOptions(globalOpts);
|
|
9394
|
+
const forked = await skillBank.forkSkill(id, {
|
|
9395
|
+
newId: options.newId,
|
|
9396
|
+
newName: options.name,
|
|
9397
|
+
reason: options.reason,
|
|
9398
|
+
fromVersion: options.fromVersion
|
|
9399
|
+
});
|
|
9400
|
+
if (globalOpts.json) {
|
|
9401
|
+
console.log(JSON.stringify(forked, null, 2));
|
|
9402
|
+
return;
|
|
9403
|
+
}
|
|
9404
|
+
printSuccess(`Forked ${id} \u2192 ${forked.id} (v${forked.version})`);
|
|
9405
|
+
} catch (error) {
|
|
9406
|
+
printError(error.message);
|
|
9407
|
+
process.exit(1);
|
|
8966
9408
|
}
|
|
8967
|
-
|
|
8968
|
-
|
|
8969
|
-
|
|
8970
|
-
|
|
8971
|
-
|
|
8972
|
-
|
|
8973
|
-
|
|
9409
|
+
});
|
|
9410
|
+
|
|
9411
|
+
// src/cli/commands/deprecate.ts
|
|
9412
|
+
var import_commander9 = require("commander");
|
|
9413
|
+
var deprecateCommand = new import_commander9.Command("deprecate").description("Mark a skill as deprecated").argument("<id>", "Skill ID").action(async (id, options, command) => {
|
|
9414
|
+
const globalOpts = command.optsWithGlobals();
|
|
9415
|
+
try {
|
|
9416
|
+
const skillBank = await createSkillBankFromOptions(globalOpts);
|
|
9417
|
+
const skill = await skillBank.deprecateSkill(id);
|
|
9418
|
+
if (globalOpts.json) {
|
|
9419
|
+
console.log(JSON.stringify(skill, null, 2));
|
|
9420
|
+
return;
|
|
8974
9421
|
}
|
|
8975
|
-
|
|
9422
|
+
printSuccess(`Deprecated: ${id} (v${skill.version})`);
|
|
9423
|
+
} catch (error) {
|
|
9424
|
+
printError(error.message);
|
|
9425
|
+
process.exit(1);
|
|
8976
9426
|
}
|
|
8977
|
-
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
|
|
8981
|
-
|
|
8982
|
-
|
|
8983
|
-
|
|
8984
|
-
|
|
8985
|
-
|
|
8986
|
-
|
|
8987
|
-
|
|
8988
|
-
|
|
8989
|
-
nodeMap.set("root", root);
|
|
8990
|
-
for (const node of nodes) {
|
|
8991
|
-
const taxNode = {
|
|
8992
|
-
id: node.id,
|
|
8993
|
-
name: node.name,
|
|
8994
|
-
path: node.path,
|
|
8995
|
-
skillCount: node.skillCount || 0,
|
|
8996
|
-
children: []
|
|
8997
|
-
};
|
|
8998
|
-
nodeMap.set(node.id, taxNode);
|
|
9427
|
+
});
|
|
9428
|
+
|
|
9429
|
+
// src/cli/commands/activate.ts
|
|
9430
|
+
var import_commander10 = require("commander");
|
|
9431
|
+
var activateCommand = new import_commander10.Command("activate").description("Mark a skill as active").argument("<id>", "Skill ID").action(async (id, options, command) => {
|
|
9432
|
+
const globalOpts = command.optsWithGlobals();
|
|
9433
|
+
try {
|
|
9434
|
+
const skillBank = await createSkillBankFromOptions(globalOpts);
|
|
9435
|
+
const skill = await skillBank.getSkill(id);
|
|
9436
|
+
if (!skill) {
|
|
9437
|
+
printError(`Skill not found: ${id}`);
|
|
9438
|
+
process.exit(1);
|
|
8999
9439
|
}
|
|
9000
|
-
|
|
9001
|
-
|
|
9002
|
-
|
|
9003
|
-
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
parent.skillCount += taxNode.skillCount;
|
|
9007
|
-
}
|
|
9440
|
+
skill.status = "active";
|
|
9441
|
+
skill.updatedAt = /* @__PURE__ */ new Date();
|
|
9442
|
+
await skillBank.saveSkill(skill);
|
|
9443
|
+
if (globalOpts.json) {
|
|
9444
|
+
console.log(JSON.stringify(skill, null, 2));
|
|
9445
|
+
return;
|
|
9008
9446
|
}
|
|
9009
|
-
|
|
9447
|
+
printSuccess(`Activated: ${id} (v${skill.version})`);
|
|
9448
|
+
} catch (error) {
|
|
9449
|
+
printError(error.message);
|
|
9450
|
+
process.exit(1);
|
|
9010
9451
|
}
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
|
|
9016
|
-
|
|
9017
|
-
|
|
9018
|
-
|
|
9019
|
-
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
for (const skill of skills) {
|
|
9024
|
-
const taxonomy = skill.taxonomy || (skill.primaryPath ? { primaryPath: skill.primaryPath } : null);
|
|
9025
|
-
if (!taxonomy?.primaryPath) continue;
|
|
9026
|
-
const skillPath = taxonomy.primaryPath;
|
|
9027
|
-
if (rootPath && rootPath.length > 0) {
|
|
9028
|
-
let matches = true;
|
|
9029
|
-
for (let i = 0; i < rootPath.length; i++) {
|
|
9030
|
-
if (skillPath[i] !== rootPath[i]) {
|
|
9031
|
-
matches = false;
|
|
9032
|
-
break;
|
|
9033
|
-
}
|
|
9034
|
-
}
|
|
9035
|
-
if (!matches) continue;
|
|
9036
|
-
}
|
|
9037
|
-
let currentPath = [];
|
|
9038
|
-
let parent = root;
|
|
9039
|
-
for (const segment of skillPath) {
|
|
9040
|
-
currentPath = [...currentPath, segment];
|
|
9041
|
-
const pathKey = currentPath.join("/");
|
|
9042
|
-
let node = nodeMap.get(pathKey);
|
|
9043
|
-
if (!node) {
|
|
9044
|
-
node = {
|
|
9045
|
-
id: pathKey,
|
|
9046
|
-
name: segment,
|
|
9047
|
-
path: [...currentPath],
|
|
9048
|
-
skillCount: 0,
|
|
9049
|
-
children: []
|
|
9050
|
-
};
|
|
9051
|
-
nodeMap.set(pathKey, node);
|
|
9052
|
-
parent.children.push(node);
|
|
9053
|
-
}
|
|
9054
|
-
parent = node;
|
|
9055
|
-
}
|
|
9056
|
-
parent.skillCount++;
|
|
9057
|
-
root.skillCount++;
|
|
9452
|
+
});
|
|
9453
|
+
|
|
9454
|
+
// src/cli/commands/delete.ts
|
|
9455
|
+
var import_commander11 = require("commander");
|
|
9456
|
+
var deleteCommand = new import_commander11.Command("delete").description("Delete a skill").argument("<id>", "Skill ID").option("-f, --force", "Skip confirmation").option("-v, --version <version>", "Delete specific version only").action(async (id, options, command) => {
|
|
9457
|
+
const globalOpts = command.optsWithGlobals();
|
|
9458
|
+
try {
|
|
9459
|
+
const skillBank = await createSkillBankFromOptions(globalOpts);
|
|
9460
|
+
const skill = await skillBank.getSkill(id, options.version);
|
|
9461
|
+
if (!skill) {
|
|
9462
|
+
printError(`Skill not found: ${id}${options.version ? `@${options.version}` : ""}`);
|
|
9463
|
+
process.exit(1);
|
|
9058
9464
|
}
|
|
9059
|
-
|
|
9060
|
-
|
|
9061
|
-
|
|
9062
|
-
|
|
9063
|
-
*/
|
|
9064
|
-
async getStats() {
|
|
9065
|
-
await this.initialize();
|
|
9066
|
-
if (this.skillBank) {
|
|
9067
|
-
const skills = await this.skillBank.listSkills();
|
|
9068
|
-
const storage = this.skillBank.getStorage();
|
|
9069
|
-
let taxonomyNodes = 0;
|
|
9070
|
-
let relationships = 0;
|
|
9071
|
-
let sources = 0;
|
|
9072
|
-
if (storage) {
|
|
9073
|
-
if (hasIndexerSupport(storage)) {
|
|
9074
|
-
const tree = await storage.getTaxonomyTree();
|
|
9075
|
-
taxonomyNodes = this.countTaxonomyNodes(tree);
|
|
9076
|
-
if (storage.getRelationships) {
|
|
9077
|
-
for (const skill of skills) {
|
|
9078
|
-
const rels = await storage.getRelationships(skill.id);
|
|
9079
|
-
relationships += rels.length;
|
|
9080
|
-
}
|
|
9081
|
-
}
|
|
9082
|
-
}
|
|
9083
|
-
const sourceSet = /* @__PURE__ */ new Set();
|
|
9084
|
-
for (const skill of skills) {
|
|
9085
|
-
if (skill.externalSource?.repo) {
|
|
9086
|
-
sourceSet.add(skill.externalSource.repo);
|
|
9087
|
-
}
|
|
9088
|
-
if (skill.source?.type === "imported" && skill.externalSource?.url) {
|
|
9089
|
-
sourceSet.add(skill.externalSource.url);
|
|
9090
|
-
}
|
|
9091
|
-
}
|
|
9092
|
-
sources = sourceSet.size;
|
|
9093
|
-
}
|
|
9094
|
-
const indexed = skills.filter(
|
|
9095
|
-
(s) => s.status === "active" && (s.taxonomy || s.source?.type === "imported")
|
|
9096
|
-
).length;
|
|
9097
|
-
return {
|
|
9098
|
-
totalSkills: skills.length,
|
|
9099
|
-
indexedSkills: indexed,
|
|
9100
|
-
rawSkills: skills.filter((s) => s.status === "draft").length,
|
|
9101
|
-
failedSkills: skills.filter((s) => s.status === "deprecated").length,
|
|
9102
|
-
taxonomyNodes,
|
|
9103
|
-
relationships,
|
|
9104
|
-
sources
|
|
9105
|
-
};
|
|
9465
|
+
if (!options.force) {
|
|
9466
|
+
printWarning(`This will permanently delete ${id}${options.version ? `@${options.version}` : " and all versions"}`);
|
|
9467
|
+
printWarning("Use --force to confirm");
|
|
9468
|
+
process.exit(1);
|
|
9106
9469
|
}
|
|
9107
|
-
|
|
9108
|
-
|
|
9109
|
-
|
|
9110
|
-
|
|
9111
|
-
indexedSkills: stats.indexedSkills || 0,
|
|
9112
|
-
rawSkills: stats.rawSkills || 0,
|
|
9113
|
-
failedSkills: stats.failedSkills || 0,
|
|
9114
|
-
taxonomyNodes: stats.taxonomyNodes || 0,
|
|
9115
|
-
relationships: stats.relationships || 0,
|
|
9116
|
-
sources: stats.sources || 0
|
|
9117
|
-
};
|
|
9470
|
+
const deleted = await skillBank.deleteSkill(id, options.version);
|
|
9471
|
+
if (!deleted) {
|
|
9472
|
+
printError(`Failed to delete: ${id}`);
|
|
9473
|
+
process.exit(1);
|
|
9118
9474
|
}
|
|
9119
|
-
|
|
9120
|
-
|
|
9121
|
-
|
|
9122
|
-
|
|
9123
|
-
|
|
9124
|
-
|
|
9125
|
-
|
|
9126
|
-
|
|
9127
|
-
};
|
|
9475
|
+
if (globalOpts.json) {
|
|
9476
|
+
console.log(JSON.stringify({ deleted: true, id, version: options.version }));
|
|
9477
|
+
return;
|
|
9478
|
+
}
|
|
9479
|
+
printSuccess(`Deleted: ${id}${options.version ? `@${options.version}` : ""}`);
|
|
9480
|
+
} catch (error) {
|
|
9481
|
+
printError(error.message);
|
|
9482
|
+
process.exit(1);
|
|
9128
9483
|
}
|
|
9129
|
-
|
|
9130
|
-
|
|
9131
|
-
|
|
9132
|
-
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
|
|
9484
|
+
});
|
|
9485
|
+
|
|
9486
|
+
// src/cli/commands/export.ts
|
|
9487
|
+
var import_commander12 = require("commander");
|
|
9488
|
+
var fs14 = __toESM(require("fs"));
|
|
9489
|
+
var exportCommand = new import_commander12.Command("export").description("Export all skills to JSON").option("-o, --output <file>", "Output file path (defaults to stdout)").action(async (options, command) => {
|
|
9490
|
+
const globalOpts = command.optsWithGlobals();
|
|
9491
|
+
try {
|
|
9492
|
+
const skillBank = await createSkillBankFromOptions(globalOpts);
|
|
9493
|
+
const skills = await skillBank.exportAll();
|
|
9494
|
+
const json = JSON.stringify(skills, null, 2);
|
|
9495
|
+
if (options.output) {
|
|
9496
|
+
fs14.writeFileSync(options.output, json, "utf-8");
|
|
9497
|
+
if (!globalOpts.quiet) {
|
|
9498
|
+
printSuccess(`Exported ${skills.length} skill(s) to ${options.output}`);
|
|
9138
9499
|
}
|
|
9500
|
+
} else {
|
|
9501
|
+
console.log(json);
|
|
9139
9502
|
}
|
|
9140
|
-
|
|
9503
|
+
} catch (error) {
|
|
9504
|
+
printError(error.message);
|
|
9505
|
+
process.exit(1);
|
|
9141
9506
|
}
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
-
|
|
9148
|
-
|
|
9149
|
-
|
|
9150
|
-
|
|
9151
|
-
|
|
9152
|
-
|
|
9153
|
-
|
|
9154
|
-
|
|
9155
|
-
|
|
9507
|
+
});
|
|
9508
|
+
|
|
9509
|
+
// src/cli/commands/import.ts
|
|
9510
|
+
var import_commander13 = require("commander");
|
|
9511
|
+
var fs15 = __toESM(require("fs"));
|
|
9512
|
+
|
|
9513
|
+
// src/import/detect.ts
|
|
9514
|
+
function isSkillTreeSkill(obj) {
|
|
9515
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
9516
|
+
const skill = obj;
|
|
9517
|
+
return typeof skill.id === "string" && typeof skill.name === "string" && typeof skill.version === "string" && typeof skill.instructions === "string" && typeof skill.metrics === "object";
|
|
9518
|
+
}
|
|
9519
|
+
function isIndexerSkill(obj) {
|
|
9520
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
9521
|
+
const skill = obj;
|
|
9522
|
+
return typeof skill.slug === "string" && typeof skill.sourceRepo === "string" && typeof skill.sourcePath === "string" && typeof skill.sourceUrl === "string" && typeof skill.content === "string" && typeof skill.scrapedAt === "string" && (skill.status === "raw" || skill.status === "indexed" || skill.status === "failed");
|
|
9523
|
+
}
|
|
9524
|
+
function isIndexerExport(obj) {
|
|
9525
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
9526
|
+
const exp = obj;
|
|
9527
|
+
return typeof exp.exportedAt === "string" && Array.isArray(exp.skills);
|
|
9528
|
+
}
|
|
9529
|
+
function detectFormat(data) {
|
|
9530
|
+
if (Array.isArray(data)) {
|
|
9531
|
+
if (data.length === 0) {
|
|
9532
|
+
return { format: "unknown", confidence: 0, details: "Empty array" };
|
|
9156
9533
|
}
|
|
9157
|
-
|
|
9158
|
-
|
|
9159
|
-
|
|
9160
|
-
if (skill.status !== "indexed" && !options?.force) continue;
|
|
9161
|
-
try {
|
|
9162
|
-
const converted = convertIndexerSkill(skill);
|
|
9163
|
-
await this.skillBank.saveSkill(converted.skill);
|
|
9164
|
-
skillsAdded.push(converted.skill.id);
|
|
9165
|
-
} catch (err) {
|
|
9166
|
-
indexed.errors.push(`Failed to import ${skill.slug}: ${err.message}`);
|
|
9167
|
-
}
|
|
9168
|
-
}
|
|
9534
|
+
const first = data[0];
|
|
9535
|
+
if (isIndexerSkill(first)) {
|
|
9536
|
+
return { format: "indexer", confidence: 0.95, details: "Array of indexer skills" };
|
|
9169
9537
|
}
|
|
9170
|
-
|
|
9171
|
-
|
|
9172
|
-
relationships = await this.detectRelationships();
|
|
9538
|
+
if (isSkillTreeSkill(first)) {
|
|
9539
|
+
return { format: "skill-tree", confidence: 0.95, details: "Array of skill-tree skills" };
|
|
9173
9540
|
}
|
|
9174
|
-
return {
|
|
9175
|
-
scraped,
|
|
9176
|
-
indexed,
|
|
9177
|
-
relationships,
|
|
9178
|
-
skillsAdded
|
|
9179
|
-
};
|
|
9541
|
+
return { format: "unknown", confidence: 0.3, details: "Array of unknown objects" };
|
|
9180
9542
|
}
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
|
|
9184
|
-
async importFromIndexerDb(options) {
|
|
9185
|
-
if (!this.skillBank) {
|
|
9186
|
-
throw new Error("SkillBank not configured. Use integrated mode.");
|
|
9543
|
+
if (typeof data === "object" && data !== null) {
|
|
9544
|
+
if (isIndexerExport(data)) {
|
|
9545
|
+
return { format: "indexer-export", confidence: 0.98, details: "Indexer export with metadata" };
|
|
9187
9546
|
}
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
throw new Error(
|
|
9191
|
-
"Scraper database not available. Build the scraper module first."
|
|
9192
|
-
);
|
|
9547
|
+
if (isIndexerSkill(data)) {
|
|
9548
|
+
return { format: "indexer", confidence: 0.9, details: "Single indexer skill" };
|
|
9193
9549
|
}
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9550
|
+
if (isSkillTreeSkill(data)) {
|
|
9551
|
+
return { format: "skill-tree", confidence: 0.9, details: "Single skill-tree skill" };
|
|
9552
|
+
}
|
|
9553
|
+
}
|
|
9554
|
+
return { format: "unknown", confidence: 0, details: "Unrecognized format" };
|
|
9555
|
+
}
|
|
9556
|
+
function detectFormatFromString(content) {
|
|
9557
|
+
try {
|
|
9558
|
+
const data = JSON.parse(content);
|
|
9559
|
+
return detectFormat(data);
|
|
9560
|
+
} catch {
|
|
9561
|
+
return { format: "unknown", confidence: 0, details: "Invalid JSON" };
|
|
9562
|
+
}
|
|
9563
|
+
}
|
|
9564
|
+
function isLikelyIndexerFormat(content) {
|
|
9565
|
+
const result = detectFormatFromString(content);
|
|
9566
|
+
return result.format === "indexer" || result.format === "indexer-export";
|
|
9567
|
+
}
|
|
9568
|
+
|
|
9569
|
+
// src/cli/commands/import.ts
|
|
9570
|
+
var importCommand = new import_commander13.Command("import").description("Import skills from JSON file").argument("<file>", "JSON file to import").option("--from-indexer", "Import from skill-indexer export format").option("--auto-detect", "Auto-detect format (default: true)", true).action(async (file, options, command) => {
|
|
9571
|
+
const globalOpts = command.optsWithGlobals();
|
|
9572
|
+
try {
|
|
9573
|
+
if (!fs15.existsSync(file)) {
|
|
9574
|
+
printError(`File not found: ${file}`);
|
|
9575
|
+
process.exit(1);
|
|
9576
|
+
}
|
|
9577
|
+
const content = fs15.readFileSync(file, "utf-8");
|
|
9578
|
+
let skills;
|
|
9579
|
+
const isIndexerFormat = options.fromIndexer || options.autoDetect !== false && isLikelyIndexerFormat(content);
|
|
9580
|
+
if (isIndexerFormat) {
|
|
9581
|
+
if (!globalOpts.quiet) {
|
|
9582
|
+
printInfo("Detected skill-indexer format, converting...");
|
|
9201
9583
|
}
|
|
9202
|
-
|
|
9203
|
-
skills =
|
|
9584
|
+
try {
|
|
9585
|
+
const { skills: converted, stats, exportMetadata } = parseIndexerExport(content);
|
|
9586
|
+
skills = converted;
|
|
9587
|
+
if (!globalOpts.quiet) {
|
|
9588
|
+
printInfo(`Converted ${stats.total} skills (${stats.withStructuredContent} with structured content)`);
|
|
9589
|
+
if (exportMetadata) {
|
|
9590
|
+
printInfo(`Original export from: ${exportMetadata.exportedAt}`);
|
|
9591
|
+
}
|
|
9592
|
+
}
|
|
9593
|
+
} catch (err) {
|
|
9594
|
+
printError(`Failed to parse indexer export: ${err.message}`);
|
|
9595
|
+
process.exit(1);
|
|
9596
|
+
}
|
|
9597
|
+
} else {
|
|
9598
|
+
try {
|
|
9599
|
+
const parsed = JSON.parse(content);
|
|
9600
|
+
skills = Array.isArray(parsed) ? parsed : [parsed];
|
|
9601
|
+
} catch {
|
|
9602
|
+
printError("Invalid JSON file");
|
|
9603
|
+
process.exit(1);
|
|
9204
9604
|
}
|
|
9205
9605
|
for (const skill of skills) {
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
9606
|
+
skill.createdAt = new Date(skill.createdAt);
|
|
9607
|
+
skill.updatedAt = new Date(skill.updatedAt);
|
|
9608
|
+
if (skill.metrics.lastUsed) {
|
|
9609
|
+
skill.metrics.lastUsed = new Date(skill.metrics.lastUsed);
|
|
9610
|
+
}
|
|
9611
|
+
if (skill.source?.importedAt) {
|
|
9612
|
+
skill.source.importedAt = new Date(skill.source.importedAt);
|
|
9613
|
+
}
|
|
9614
|
+
if (skill.externalSource?.scrapedAt) {
|
|
9615
|
+
skill.externalSource.scrapedAt = new Date(skill.externalSource.scrapedAt);
|
|
9213
9616
|
}
|
|
9214
9617
|
}
|
|
9215
|
-
} catch (err) {
|
|
9216
|
-
throw new Error(`Import failed: ${err.message}`);
|
|
9217
9618
|
}
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
return configSources.map((url) => ({
|
|
9227
|
-
type: "awesome-list",
|
|
9228
|
-
url
|
|
9229
|
-
}));
|
|
9619
|
+
const skillBank = await createSkillBankFromOptions(globalOpts);
|
|
9620
|
+
const result = await skillBank.importSkills(skills);
|
|
9621
|
+
if (globalOpts.json) {
|
|
9622
|
+
console.log(JSON.stringify({
|
|
9623
|
+
...result,
|
|
9624
|
+
format: isIndexerFormat ? "indexer" : "skill-tree"
|
|
9625
|
+
}, null, 2));
|
|
9626
|
+
return;
|
|
9230
9627
|
}
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
}
|
|
9235
|
-
/**
|
|
9236
|
-
* Close database connections
|
|
9237
|
-
*/
|
|
9238
|
-
async close() {
|
|
9239
|
-
if (this.db && typeof this.db.close === "function") {
|
|
9240
|
-
this.db.close();
|
|
9628
|
+
printSuccess(`Imported ${result.imported} skill(s)`);
|
|
9629
|
+
if (result.failed > 0) {
|
|
9630
|
+
printWarning(`Failed to import ${result.failed} skill(s)`);
|
|
9241
9631
|
}
|
|
9242
|
-
|
|
9243
|
-
|
|
9632
|
+
} catch (error) {
|
|
9633
|
+
printError(error.message);
|
|
9634
|
+
process.exit(1);
|
|
9244
9635
|
}
|
|
9245
|
-
};
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
9636
|
+
});
|
|
9637
|
+
|
|
9638
|
+
// src/cli/commands/indexer/index.ts
|
|
9639
|
+
var import_commander20 = require("commander");
|
|
9249
9640
|
|
|
9250
9641
|
// src/cli/commands/indexer/scrape.ts
|
|
9642
|
+
var import_commander14 = require("commander");
|
|
9643
|
+
var import_child_process2 = require("child_process");
|
|
9251
9644
|
var scrapeCommand = new import_commander14.Command("scrape").description("Scrape skills from GitHub sources").argument("[url]", "Repository or awesome-list URL").option("-d, --discover", "Discover from default sources").option("-f, --force", "Force scrape even if no changes detected").option("--standalone", "Use standalone skillindexer CLI (fallback)").option("--import", "Auto-import scraped skills into skill-tree", true).action(async (url, options, command) => {
|
|
9252
9645
|
const globalOpts = command.optsWithGlobals();
|
|
9253
9646
|
if (options.standalone) {
|
|
@@ -10314,11 +10707,11 @@ configCommand.command("edit").description("Open configuration file in editor").a
|
|
|
10314
10707
|
configCommand.command("defaults").description("Show default configuration values").action(() => {
|
|
10315
10708
|
const globalOpts = configCommand.parent?.opts() || {};
|
|
10316
10709
|
if (globalOpts.json) {
|
|
10317
|
-
console.log(JSON.stringify(
|
|
10710
|
+
console.log(JSON.stringify(DEFAULT_CONFIG6, null, 2));
|
|
10318
10711
|
} else {
|
|
10319
10712
|
console.log("Default Configuration:");
|
|
10320
10713
|
console.log("=".repeat(40));
|
|
10321
|
-
printConfig(
|
|
10714
|
+
printConfig(DEFAULT_CONFIG6, 0);
|
|
10322
10715
|
}
|
|
10323
10716
|
});
|
|
10324
10717
|
configCommand.command("validate").description("Validate configuration file").action(() => {
|
|
@@ -10838,7 +11231,7 @@ var materializeCommand = new import_commander24.Command("materialize").descripti
|
|
|
10838
11231
|
});
|
|
10839
11232
|
|
|
10840
11233
|
// src/cli/commands/loadout/index.ts
|
|
10841
|
-
var
|
|
11234
|
+
var import_commander38 = require("commander");
|
|
10842
11235
|
|
|
10843
11236
|
// src/cli/commands/loadout/list.ts
|
|
10844
11237
|
var import_commander25 = require("commander");
|
|
@@ -11191,7 +11584,7 @@ var renderSubcommand = new import_commander35.Command("render").description("Ren
|
|
|
11191
11584
|
printInfo("Loadout is empty. Nothing to render.");
|
|
11192
11585
|
return;
|
|
11193
11586
|
}
|
|
11194
|
-
const output = server.renderSystemPrompt();
|
|
11587
|
+
const output = await server.renderSystemPrompt();
|
|
11195
11588
|
console.log(output);
|
|
11196
11589
|
} catch (error) {
|
|
11197
11590
|
printError(error.message);
|
|
@@ -11221,11 +11614,34 @@ var clearSubcommand = new import_commander36.Command("clear").description("Clear
|
|
|
11221
11614
|
}
|
|
11222
11615
|
});
|
|
11223
11616
|
|
|
11617
|
+
// src/cli/commands/loadout/browse.ts
|
|
11618
|
+
var import_commander37 = require("commander");
|
|
11619
|
+
var browseSubcommand = new import_commander37.Command("browse").description("Browse the skill catalog by category").argument("[path]", "Category path to browse (e.g., Development/Python)").action(async (pathArg, _options, command) => {
|
|
11620
|
+
const globalOpts = command.optsWithGlobals();
|
|
11621
|
+
try {
|
|
11622
|
+
const { server } = await createLoadoutServer(globalOpts);
|
|
11623
|
+
const path18 = pathArg ? pathArg.split("/").filter(Boolean) : void 0;
|
|
11624
|
+
const output = await server.agentBrowseCatalog(path18);
|
|
11625
|
+
if (globalOpts.json) {
|
|
11626
|
+
console.log(JSON.stringify({ path: path18 ?? [], output }, null, 2));
|
|
11627
|
+
return;
|
|
11628
|
+
}
|
|
11629
|
+
if (!output) {
|
|
11630
|
+
printInfo("No catalog data available.");
|
|
11631
|
+
return;
|
|
11632
|
+
}
|
|
11633
|
+
console.log(output);
|
|
11634
|
+
} catch (error) {
|
|
11635
|
+
printError(error.message);
|
|
11636
|
+
process.exit(1);
|
|
11637
|
+
}
|
|
11638
|
+
});
|
|
11639
|
+
|
|
11224
11640
|
// src/cli/commands/loadout/index.ts
|
|
11225
|
-
var loadoutCommand = new
|
|
11641
|
+
var loadoutCommand = new import_commander38.Command("loadout").description("Manage skill loadouts for agent sessions").addCommand(listSubcommand).addCommand(searchSubcommand).addCommand(addSubcommand).addCommand(removeSubcommand).addCommand(profileSubcommand).addCommand(setSubcommand).addCommand(expandSubcommand).addCommand(collapseSubcommand).addCommand(useSubcommand).addCommand(getSubcommand).addCommand(renderSubcommand).addCommand(clearSubcommand).addCommand(browseSubcommand);
|
|
11226
11642
|
|
|
11227
11643
|
// src/cli/index.ts
|
|
11228
|
-
var program = new
|
|
11644
|
+
var program = new import_commander39.Command();
|
|
11229
11645
|
var config = loadConfig();
|
|
11230
11646
|
program.name("skill-tree").description("Management CLI for agent skills").version(VERSION).option("-p, --path <dir>", "Skills directory path", config.storage.path).option("-c, --config <file>", "Config file path", getConfigPath()).option("--json", "Output as JSON", config.cli.output_format === "json").option("-q, --quiet", "Suppress non-essential output", config.cli.quiet).option("--no-color", "Disable colored output", !config.cli.color);
|
|
11231
11647
|
program.addCommand(listCommand);
|
|
@@ -11248,3 +11664,4 @@ program.addCommand(readCommand);
|
|
|
11248
11664
|
program.addCommand(materializeCommand);
|
|
11249
11665
|
program.addCommand(loadoutCommand);
|
|
11250
11666
|
program.parse();
|
|
11667
|
+
//# sourceMappingURL=index.js.map
|