skill-creator 1.5.1 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +173 -103
  2. package/dist/buildIndex-B_9ABRtx.mjs +2 -0
  3. package/dist/buildIndex-B_9ABRtx.mjs.map +1 -0
  4. package/dist/buildIndex-Cea97e0l.mjs +1 -0
  5. package/dist/cli.d.mts +0 -0
  6. package/dist/cli.d.mts.map +1 -1
  7. package/dist/cli.mjs +17 -33
  8. package/dist/cli.mjs.map +1 -1
  9. package/dist/contentManager-CoVTiSDT.mjs +13 -0
  10. package/dist/contentManager-CoVTiSDT.mjs.map +1 -0
  11. package/dist/contentManager-CwquwC1s.mjs +1 -0
  12. package/dist/context7-BejCCqkm.mjs +1 -0
  13. package/dist/context7-BxMpyQiW.mjs +2 -0
  14. package/dist/context7-BxMpyQiW.mjs.map +1 -0
  15. package/dist/index.d.mts +317 -123
  16. package/dist/index.d.mts.map +1 -1
  17. package/dist/index.mjs +1 -20
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/{listContext7-DKXzgWdn.mjs → listContext7-DHj9AL-z.mjs} +2 -2
  20. package/dist/{listContext7-DKXzgWdn.mjs.map → listContext7-DHj9AL-z.mjs.map} +1 -1
  21. package/dist/package-CFJazwTO.mjs +2 -0
  22. package/dist/package-CFJazwTO.mjs.map +1 -0
  23. package/dist/referenceSearchUtils-DZgupMZp.mjs +2 -0
  24. package/dist/referenceSearchUtils-DZgupMZp.mjs.map +1 -0
  25. package/dist/removeContext7-BZtL4_CJ.mjs +3 -0
  26. package/dist/{removeContext7-DOXrTXeI.mjs.map → removeContext7-BZtL4_CJ.mjs.map} +1 -1
  27. package/dist/{removeSkill-C-ehFexk.mjs → removeSkill-Bat_plVG.mjs} +4 -4
  28. package/dist/removeSkill-Bat_plVG.mjs.map +1 -0
  29. package/dist/removeSkill-DjDZjhJU.mjs +1 -0
  30. package/dist/runScript-CSKqb831.mjs +13 -0
  31. package/dist/runScript-CSKqb831.mjs.map +1 -0
  32. package/dist/scopeSelection-BWteJQSv.mjs +2 -0
  33. package/dist/scopeSelection-BWteJQSv.mjs.map +1 -0
  34. package/dist/searchQuality-DVdHmamh.mjs +2 -0
  35. package/dist/searchQuality-DVdHmamh.mjs.map +1 -0
  36. package/dist/search_format-RyjXACJa.mjs +5 -0
  37. package/dist/search_format-RyjXACJa.mjs.map +1 -0
  38. package/dist/shared-DsVh4gOV.mjs +2 -0
  39. package/dist/shared-DsVh4gOV.mjs.map +1 -0
  40. package/dist/skillCreator-BomXoqdw.mjs +2 -0
  41. package/dist/skillCreator-BomXoqdw.mjs.map +1 -0
  42. package/dist/skillMdManager-DGBZ6M5J.mjs +7 -0
  43. package/dist/skillMdManager-DGBZ6M5J.mjs.map +1 -0
  44. package/dist/sqliteVectorSearchAdapter-56kZjbUT.mjs +28 -0
  45. package/dist/sqliteVectorSearchAdapter-56kZjbUT.mjs.map +1 -0
  46. package/dist/sqliteVectorSearchAdapter-DNkyKuZs.mjs +1 -0
  47. package/dist/unifiedSearch-CLdazMYU.mjs +4 -0
  48. package/dist/unifiedSearch-CLdazMYU.mjs.map +1 -0
  49. package/dist/unifiedSearch-DCFPrtzZ.mjs +1 -0
  50. package/package.json +32 -28
  51. package/templates/SKILL.md +19 -4
  52. package/templates/SKILL.zh-CN.md +54 -12
  53. package/templates/skill-creator.md +52 -19
  54. package/templates/skill-creator.zh-CN.md +55 -22
  55. package/dist/chunk-Da8dvM5T.mjs +0 -1
  56. package/dist/contentManager-8rmWJMb6.mjs +0 -22
  57. package/dist/contentManager-8rmWJMb6.mjs.map +0 -1
  58. package/dist/contentManager-Xt8N5psY.mjs +0 -1
  59. package/dist/dist-DZL34HNo.mjs +0 -8
  60. package/dist/dist-DZL34HNo.mjs.map +0 -1
  61. package/dist/dist-X_kVEzbK.mjs +0 -1
  62. package/dist/esm-8qfZdcpd.mjs +0 -33
  63. package/dist/esm-8qfZdcpd.mjs.map +0 -1
  64. package/dist/esm-BF5pvSbc.mjs +0 -1
  65. package/dist/esm-BbhxxeGF.mjs +0 -4
  66. package/dist/esm-BbhxxeGF.mjs.map +0 -1
  67. package/dist/package-Cxx_VSyD.mjs +0 -2
  68. package/dist/package-Cxx_VSyD.mjs.map +0 -1
  69. package/dist/path-CuoiIaIe.mjs +0 -2
  70. package/dist/path-CuoiIaIe.mjs.map +0 -1
  71. package/dist/removeContext7-DOXrTXeI.mjs +0 -3
  72. package/dist/removeSkill-C-ehFexk.mjs.map +0 -1
  73. package/dist/removeSkill-DWwhvzej.mjs +0 -1
  74. package/dist/runScript-Cr9RRN9y.mjs +0 -14
  75. package/dist/runScript-Cr9RRN9y.mjs.map +0 -1
  76. package/dist/search_format-DIs-1lKk.mjs +0 -5
  77. package/dist/search_format-DIs-1lKk.mjs.map +0 -1
  78. package/dist/shared-CFoY2Xhg.mjs +0 -2
  79. package/dist/shared-CFoY2Xhg.mjs.map +0 -1
  80. package/dist/skillMdManager-DP8fGZb7.mjs +0 -7
  81. package/dist/skillMdManager-DP8fGZb7.mjs.map +0 -1
  82. package/dist/unifiedSearch-CGZXw0NB.mjs +0 -1
  83. package/dist/unifiedSearch-l-lrSc9C.mjs +0 -4
  84. package/dist/unifiedSearch-l-lrSc9C.mjs.map +0 -1
@@ -0,0 +1,13 @@
1
+ import{a as e}from"./referenceSearchUtils-DZgupMZp.mjs";import{t}from"./searchQuality-DVdHmamh.mjs";import{isAbsolute as n,join as r}from"node:path";import{existsSync as i,mkdirSync as a,readFileSync as o,readdirSync as s,rmSync as c,statSync as l,writeFileSync as u}from"node:fs";import{createHash as d}from"node:crypto";var f=class{options;userDir;context7BaseDir;constructor(e){this.options=e,this.userDir=r(e.referencesDir,`user`),this.context7BaseDir=r(e.referencesDir,`context7`)}getContext7ProjectDir(e){return r(this.context7BaseDir,this.encodeContext7ProjectId(e))}encodeContext7ProjectId(e){return encodeURIComponent(e)}decodeContext7ProjectId(e){return decodeURIComponent(e)}async updateFromContext7(e,t=!1){let n={updated:!1,skipped:!1,filesCreated:0,message:``};try{let o=await this.downloadContext7Doc(e),l=this.getContext7ProjectDir(e);if(!t&&!this.needsUpdate(o,e))return n.skipped=!0,n.message=`Context7 documentation is up to date`,n;if(i(l)){let e=s(l);for(let t of e)t.endsWith(`.md`)&&c(r(l,t))}a(l,{recursive:!0});let u=await this.sliceDocument(o,l);n.filesCreated=u.length,this.saveContentHash(o,e),await this.invalidateSearchIndex(),n.updated=!0,n.message=`Updated ${u.length} documentation slices for project ${e}`}catch(e){n.message=`Failed to update Context7 docs: ${e}`}return n}async addUserContent(e){let t={added:!1,updated:!1,skipped:!1,message:``,similarFound:0};try{await this.prepareSearchEngine();let n=await this.findSimilarContent(e.content),r=this.mergeSimilarResults(n,this.findDirectUserMatches(e.content));if(e.force||e.forceAppend){let n=r.find(e=>e.source===`user`)??this.findBestUserMergeTarget(e.title,e.content)??this.findUserFileByTitle(e.title);if(n){let i=this.resolveSearchResultFilePath(n.file_path),a=o(i,`utf-8`);return e.forceAppend?(u(i,this.appendKnowledgeUpdate(a,e.title,e.content)),t.added=!0,t.filePath=i,t.message=`Appended content to existing knowledge note: ${i.split(`/`).pop()}`):(u(i,this.renderKnowledgeNote(e.title,e.content)),t.added=!0,t.filePath=i,t.message=`Replaced existing knowledge note: ${i.split(`/`).pop()}`),t.similarContent=this.summarizeSimilarContent([n,...r.filter(e=>e.id!==n.id)],e.content),t.similarFound=t.similarContent.length,await this.invalidateSearchIndex(),t}}if(r.length>0&&!e.force){let n=r[0];if(n.source===`user`){let r=this.resolveSearchResultFilePath(n.file_path);this.isContentDuplicate(n,e.content)?(t.skipped=!0,t.message=`Existing content is comprehensive enough`):e.autoUpdate&&this.isContentEnhanced(n,e.content)?(u(r,this.renderKnowledgeNote(e.title,e.content)),t.updated=!0,t.filePath=r,t.message=`Updated existing content: ${n.file_path}`,await this.invalidateSearchIndex()):(t.skipped=!0,t.message=`Existing content is comprehensive enough`)}else if(this.isContentDuplicate(n,e.content))t.skipped=!0,t.message=`Existing context7 content is comprehensive enough`;else{let n=this.writeUserContentFile(e);t.added=!0,t.filePath=n.filePath,t.message=n.message,await this.invalidateSearchIndex()}t.similarContent=this.summarizeSimilarContent(r,e.content),t.similarFound=r.length}else if(!t.added&&!t.updated&&!t.skipped){let n=this.getExpectedUserFilePath(e.title);if(i(n)&&!e.force&&!e.forceAppend){let{readFileSync:r}=await import(`node:fs`),i=r(n,`utf-8`),a={content:i,file_path:n};this.isContentDuplicate(a,e.content)?(t.skipped=!0,t.message=`Existing content is comprehensive enough`,t.similarContent=this.summarizeSimilarContent([{id:`user/${n.split(`/`).pop()??`existing.md`}`,title:e.title,content:i,source:`user`,file_path:n,score:1,metadata:{backendId:`direct-user-match`}}],e.content)):e.autoUpdate&&this.isContentEnhanced(a,e.content)?(u(n,this.renderKnowledgeNote(e.title,e.content)),t.updated=!0,t.filePath=n,t.message=`Updated existing content: ${n.split(`/`).pop()}`,await this.invalidateSearchIndex()):(t.message=`File already exists: ${n.split(`/`).pop()}. Use --force to overwrite or --force-append to append.`,t.existingFile={path:n,content:i})}else{let r=this.writeUserContentFile(e,{overwriteExisting:i(n)&&!!e.force});t.message=r.message,t.filePath=r.filePath,t.added=!0,await this.invalidateSearchIndex()}}}catch(e){t.message=`Failed to add content: ${e}`}return t.similarFound=t.similarContent?.length||0,t}getContentStats(){let e={userFiles:0,context7Files:0,totalFiles:0,userDirExists:i(this.userDir),context7DirExists:i(this.context7BaseDir)};if(e.userDirExists&&(e.userFiles=s(this.userDir).filter(e=>e.endsWith(`.md`)).length),e.context7DirExists){let t=s(this.context7BaseDir,{withFileTypes:!0}).filter(e=>e.isDirectory()).map(e=>e.name);for(let n of t){let t=r(this.context7BaseDir,n);i(t)&&(e.context7Files+=s(t).filter(e=>e.endsWith(`.md`)).length)}}return e.totalFiles=e.userFiles+e.context7Files,e}listContent(e){let t=[];if((e===`user`||!e)&&i(this.userDir)){let e=s(this.userDir);for(let n of e){if(!n.endsWith(`.md`))continue;let e=r(this.userDir,n),i=l(e),a=n.replace(`.md`,``);try{let t=o(e,`utf-8`).split(`
2
+ `)[0];t?.startsWith(`# `)&&(a=t.slice(2))}catch{}t.push({title:a,filename:n,source:`user`,path:e,size:i.size,modified:i.mtime||new Date})}}if((e===`context7`||!e)&&i(this.context7BaseDir)){let e=s(this.context7BaseDir,{withFileTypes:!0}).filter(e=>e.isDirectory()).map(e=>e.name);for(let n of e){this.decodeContext7ProjectId(n);let e=r(this.context7BaseDir,n);if(i(e)){let n=s(e);for(let i of n){if(!i.endsWith(`.md`))continue;let n=r(e,i),a=l(n),s=i.replace(`.md`,``);try{let e=o(n,`utf-8`).split(`
3
+ `)[0];e?.startsWith(`# `)&&(s=e.slice(2))}catch{}t.push({title:s,filename:i,source:`context7`,path:n,size:a.size,modified:a.mtime||new Date})}}}}return t.sort((e,t)=>t.modified.getTime()-e.modified.getTime())}async downloadContext7Doc(e){let t=`${(process.env.SKILL_CREATOR_CONTEXT7_BASE_URL??`https://context7.com`).replace(/\/$/,``)}${e}/llms.txt?tokens=100000000&topic=*`,n=await(this.options.fetchImpl??fetch)(t);if(!n.ok)throw Error(`Failed to download: ${n.statusText}`);return await n.text()}async sliceDocument(e,t){let n=this.extractSections(e),i=[];for(let e=0;e<n.length;e++){let{title:a,content:o}=n[e];if(!o.trim())continue;let s=r(t,this.createUniqueFileName(a,e,t));u(s,`# ${a}\n\n${o}`),i.push(s)}return i}extractSections(e){let t=[],n=e.split(`
4
+ `),r={title:`Introduction`,content:``},i=[],a=/^(#{1,6})\s+(.+)$/;for(let e=0;e<n.length;e++){let o=n[e],s=a.exec(o);s?(i.length>0&&(r.content=i.join(`
5
+ `).trim(),r.content.length>50&&t.push({...r}),i=[]),r={title:s[2].trim(),content:``}):(o.trim()||i.length>0)&&i.push(o)}return i.length>0&&(r.content=i.join(`
6
+ `).trim(),r.content.length>50&&t.push(r)),this.mergeShortSections(t)}mergeShortSections(e){let t=[];for(let n=0;n<e.length;n++){let r=e[n];if(r.content.length<200&&n<e.length-1){let i=e[n+1];this.isMajorSection(i.title)?t.push(r):(t.push({title:r.title,content:r.content+`
7
+
8
+ `+i.content}),n++)}else t.push(r)}return t}isMajorSection(e){let t=[`introduction`,`getting started`,`installation`,`setup`,`api`,`reference`,`examples`,`tutorial`,`guide`,`overview`,`quick start`,`basics`,`advanced`],n=e.toLowerCase();return t.some(e=>n.includes(e))}createUniqueFileName(e,t,n){let a=e.replace(/[^\w\s-]/g,``).replace(/[-\s]+/g,`_`).replace(/^_+|_+$/g,``).slice(0,50)||`section`,o=this.extractHeadingLevel(e),s=`${(o>1?`${`0`.repeat(Math.min(o-1,3))}${t}`:t.toString()).padStart(4,`0`)}_${a}`,c=`${s}.md`,l=1;for(;i(r(n,c));)c=`${s}_${l.toString().padStart(2,`0`)}.md`,l++;return c}extractHeadingLevel(e){let t=e.match(/^(#{1,6})\s*(.+)$/);return t?t[1].length:/^(Chapter|Section|Part)\s+\d+/i.test(e)?1:/^\d+\.\d+/i.test(e)?2:/^\d+\.\d+\.\d+/i.test(e)?3:1}createUniqueFilePath(e,t){let n=e.toLowerCase().replace(/[^\w\s-]/g,``).replace(/[-\s]+/g,`_`).replace(/^_+|_+$/g,``).slice(0,50)||`content`,a=`${n}.md`,o=1;for(;i(r(t,a));)a=`${n}_${o.toString().padStart(2,`0`)}.md`,o++;return r(t,a)}async findSimilarContent(e,t=.7,n=10){return(await this.options.searchEngine.search(e,n)).map(e=>({...e,similarity:this.getSimilarityScore(e)})).filter(e=>e.similarity>=t).sort((e,t)=>this.getSourcePriority(t.source)-this.getSourcePriority(e.source)||t.similarity-e.similarity||t.score-e.score)}isContentEnhanced(e,t){let n=e.content.split(/\s+/).length;return t.split(/\s+/).length>n*1.3}isContentDuplicate(e,t){let n=this.normalizeContentForComparison(e.content),r=this.normalizeContentForComparison(t);if(n===r)return!0;let i=Math.min(n.length,r.length);return i===0?!1:(n.includes(r)||r.includes(n))&&i/Math.max(n.length,r.length)>=.85}normalizeContentForComparison(t){return e(t).toLowerCase().replace(/\s+/g,` `).trim()}getSimilarityScore(e){let t=e.metadata?.rerankScore;return typeof t==`number`&&Number.isFinite(t)?this.clamp01(t):e.metadata?.backendId===`minisearch`||e.score>1?this.clamp01(e.score/(e.score+3)):this.clamp01(e.score)}mergeSimilarResults(e,t){let n=new Map;for(let r of[...e,...t]){let e=n.get(r.id);if(!e){n.set(r.id,r);continue}let t=this.getSimilarityScore(e);this.getSimilarityScore(r)>t&&n.set(r.id,r)}return Array.from(n.values()).sort((e,t)=>this.getSourcePriority(t.source)-this.getSourcePriority(e.source)||this.getSimilarityScore(t)-this.getSimilarityScore(e)||t.score-e.score)}findDirectUserMatches(e){if(!i(this.userDir))return[];let t=this.normalizeContentForComparison(e),n=new Set(t.split(/\s+/).filter(Boolean));return s(this.userDir).filter(e=>e.endsWith(`.md`)).map(i=>{let a=r(this.userDir,i),s=o(a,`utf-8`),c=s.split(`
9
+ `)[0]?.replace(/^#+\s*/,``).trim()||i.replace(/\.md$/,``),l=this.normalizeContentForComparison(s),u=new Set(l.split(/\s+/).filter(Boolean)),d=this.calculateTokenOverlap(n,u),f=this.isContentDuplicate({content:s},e)?1:l.includes(t)||t.includes(l)?.9:d;return{id:`user/${i}`,title:c,content:s,source:`user`,file_path:a,score:f,metadata:{backendId:`direct-user-match`}}}).filter(e=>e.score>=.7)}findUserFileByTitle(e){let t=this.getExpectedUserFilePath(e);if(!i(t))return;let n=o(t,`utf-8`);return{id:`user/${t.split(`/`).pop()??`existing.md`}`,title:e,content:n,source:`user`,file_path:t,score:1,metadata:{backendId:`direct-user-match`}}}findBestUserMergeTarget(e,t){if(!i(this.userDir))return;let n=this.normalizeContentForComparison(e),a=new Set(n.split(/\s+/).filter(Boolean)),c=this.normalizeContentForComparison(t),l=new Set(c.split(/\s+/).filter(Boolean)),u=s(this.userDir).filter(e=>e.endsWith(`.md`)).map(e=>{let i=r(this.userDir,e),s=o(i,`utf-8`),u=s.split(`
10
+ `)[0]?.replace(/^#+\s*/,``).trim()||e.replace(/\.md$/,``),d=this.normalizeContentForComparison(u),f=this.normalizeContentForComparison(s),p=this.calculateTokenOverlap(a,new Set(d.split(/\s+/).filter(Boolean))),m=this.calculateTokenOverlap(l,new Set(f.split(/\s+/).filter(Boolean))),h=this.isContentDuplicate({content:s},t)?1:f.includes(c)||c.includes(f)?.9:0,g=d===n?1:0,_=Math.max(h,g,p*.45+m*.55);return{result:{id:`user/${e}`,title:u,content:s,source:`user`,file_path:i,score:_,metadata:{backendId:`direct-user-match`}},mergeScore:_}}).sort((e,t)=>t.mergeScore-e.mergeScore);return u[0]!=null&&u[0].mergeScore>=.2?u[0].result:void 0}calculateTokenOverlap(e,t){if(e.size===0||t.size===0)return 0;let n=0;for(let r of e)t.has(r)&&(n+=1);return n/Math.max(e.size,t.size)}getSourcePriority(e){return e===`user`?1:0}clamp01(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}summarizeSimilarContent(e,n){return t(e.slice(0,3),n).results.map(e=>({title:e.result.title,score:Number((e.displayScore*100).toFixed(2)),source:e.result.source,sourceRank:e.index===0?`primary`:`secondary`,preview:e.result.content.slice(0,200)+`...`}))}resolveSearchResultFilePath(e){return n(e)?e:r(this.options.referencesDir,e)}getExpectedUserFilePath(e){a(this.userDir,{recursive:!0});let t=e.toLowerCase().replace(/[^\w\s-]/g,``).replace(/[-\s]+/g,`_`).replace(/^_+|_+$/g,``).slice(0,50)||`content`;return r(this.userDir,`${t}.md`)}renderKnowledgeNote(e,t){return`# ${e}\n\n${this.stripMatchingLeadingHeading(t,e)}`}appendKnowledgeUpdate(e,t,n){let r=e.trimEnd(),i=`## Knowledge updates`,a=`### Update ${this.getNextKnowledgeUpdateIndex(r)}: ${t}\n\n${this.stripMatchingLeadingHeading(n,t)}`;return r.includes(i)?`${r}\n\n${a}\n`:`${r}\n\n${i}\n\n${a}\n`}getNextKnowledgeUpdateIndex(e){return[...e.matchAll(/^### Update (\d+): /gm)].reduce((e,t)=>{let n=Number(t[1]);return Number.isFinite(n)?Math.max(e,n):e},0)+1}stripMatchingLeadingHeading(e,t){let n=e.trim();if(!n)return``;let r=n.split(`
11
+ `),i=r.findIndex(e=>e.trim().length>0);if(i<0)return n;let a=r[i]?.trim()??``,o=/^(#{1,6})\s+(.+)$/.exec(a);if(!o||this.normalizeContentForComparison(o[2]??``)!==this.normalizeContentForComparison(t))return n;let s=r.slice(i+1);for(;s[0]?.trim()===``;)s.shift();return s.join(`
12
+ `).trim()||n}writeUserContentFile(e,t={}){a(this.userDir,{recursive:!0});let n=this.getExpectedUserFilePath(e.title);if(t.overwriteExisting&&i(n))return u(n,this.renderKnowledgeNote(e.title,e.content)),{filePath:n,message:`Overwrote existing content: ${n.split(`/`).pop()}`};let r=this.createUniqueFilePath(e.title,this.userDir);return u(r,this.renderKnowledgeNote(e.title,e.content)),{filePath:r,message:`Created new content: ${r.split(`/`).pop()}`}}getHashFilePath(e){let t=this.encodeContext7ProjectId(e);return r(this.options.referencesDir,`..`,`.context7_hash_${t}`)}saveContentHash(e,t){let n=d(`md5`).update(e).digest(`hex`);u(this.getHashFilePath(t),n)}needsUpdate(e,t){let n=this.getHashFilePath(t);return i(n)?d(`md5`).update(e).digest(`hex`)!==o(n,`utf-8`):!0}async prepareSearchEngine(){typeof this.options.searchEngine.initialize==`function`&&await this.options.searchEngine.initialize(),typeof this.options.searchEngine.buildIndex==`function`&&await this.options.searchEngine.buildIndex(this.options.referencesDir)}async invalidateSearchIndex(){typeof this.options.searchEngine.clearIndex==`function`&&await this.options.searchEngine.clearIndex();let e=r(this.options.referencesDir,`..`,`search`);for(let t of[`index-state.json`,`minisearch-index.json`,`vector-index-state.json`,`vector-index.db`])c(r(e,t),{force:!0})}listContext7Projects(){let e=[];if(!i(this.context7BaseDir))return e;let t=s(this.context7BaseDir,{withFileTypes:!0}).filter(e=>e.isDirectory()).map(e=>e.name);for(let n of t){let t=this.decodeContext7ProjectId(n),a=r(this.context7BaseDir,n);if(i(a)){let n=s(a).filter(e=>e.endsWith(`.md`));e.push({projectId:t,filesCount:n.length,directory:a})}}return e}getContext7ProjectFiles(e){let t=this.getContext7ProjectDir(e);return i(t)?s(t).filter(e=>e.endsWith(`.md`)):[]}removeContext7Project(e){let t=this.getContext7ProjectDir(e);if(!i(t))return{success:!1,message:`Context7 project ${e} does not exist`};try{c(t,{recursive:!0,force:!0});let n=this.getHashFilePath(e);return i(n)&&c(n),this.invalidateSearchIndex(),{success:!0,message:`Context7 project ${e} removed successfully`}}catch(t){return{success:!1,message:`Failed to remove Context7 project ${e}: ${t}`}}}};export{f as t};
13
+ //# sourceMappingURL=contentManager-CoVTiSDT.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentManager-CoVTiSDT.mjs","names":["result: UpdateContext7Result","result: AddContentResult","result","readFileSync","stats: ContentStats","contentList: ContentItem[]","savedFiles: string[]","sections: Array<{ title: string; content: string }>","sectionContent: string[]","merged: Array<{ title: string; content: string }>","projects: Array<{ projectId: string; filesCount: number; directory: string }>"],"sources":["../src/core/contentManager.ts"],"sourcesContent":["/**\n * Content management with Context7 integration\n */\n\nimport { isAbsolute, join } from 'node:path'\nimport {\n existsSync,\n mkdirSync,\n readdirSync,\n writeFileSync,\n rmSync,\n statSync,\n readFileSync,\n} from 'node:fs'\nimport { createHash } from 'node:crypto'\nimport type {\n SearchResult,\n ContentStats,\n ContentItem,\n UpdateContext7Result,\n AddContentResult,\n} from '../types/index.js'\nimport { normalizeSearchableText } from './referenceSearchUtils.js'\nimport { analyzeSearchQuality } from './searchQuality.js'\n\nexport interface ContentManagerOptions {\n searchEngine: {\n initialize?(): Promise<void>\n buildIndex?(referencesDir: string): Promise<void>\n clearIndex?(): Promise<void> | void\n search(query: string, topK?: number, where?: any): Promise<any[]>\n }\n referencesDir: string\n fetchImpl?: typeof fetch\n}\n\nexport class ContentManager {\n private options: ContentManagerOptions\n private userDir: string\n private context7BaseDir: string\n\n constructor(options: ContentManagerOptions) {\n this.options = options\n this.userDir = join(options.referencesDir, 'user')\n this.context7BaseDir = join(options.referencesDir, 'context7')\n }\n\n /**\n * Get the directory path for a specific context7 project\n */\n private getContext7ProjectDir(projectId: string): string {\n return join(this.context7BaseDir, this.encodeContext7ProjectId(projectId))\n }\n\n private encodeContext7ProjectId(projectId: string): string {\n return encodeURIComponent(projectId)\n }\n\n private decodeContext7ProjectId(encodedProjectId: string): string {\n return decodeURIComponent(encodedProjectId)\n }\n\n async updateFromContext7(\n libraryId: string,\n force: boolean = false\n ): Promise<UpdateContext7Result> {\n const result: UpdateContext7Result = {\n updated: false,\n skipped: false,\n filesCreated: 0,\n message: '',\n }\n\n try {\n // Download content\n const content = await this.downloadContext7Doc(libraryId)\n\n // Get project-specific directory\n const projectDir = this.getContext7ProjectDir(libraryId)\n\n // Check if update needed\n if (!force && !this.needsUpdate(content, libraryId)) {\n result.skipped = true\n result.message = 'Context7 documentation is up to date'\n return result\n }\n\n // Clear existing context7 docs for this project\n if (existsSync(projectDir)) {\n const files = readdirSync(projectDir)\n for (const file of files) {\n if (file.endsWith('.md')) {\n rmSync(join(projectDir, file))\n }\n }\n }\n\n // Ensure directory exists\n mkdirSync(projectDir, { recursive: true })\n\n // Slice and save\n const savedFiles = await this.sliceDocument(content, projectDir)\n result.filesCreated = savedFiles.length\n\n // Save hash for this project\n this.saveContentHash(content, libraryId)\n\n // Trigger reindex\n await this.invalidateSearchIndex()\n\n result.updated = true\n result.message = `Updated ${savedFiles.length} documentation slices for project ${libraryId}`\n } catch (error) {\n result.message = `Failed to update Context7 docs: ${error}`\n }\n\n return result\n }\n\n async addUserContent(options: {\n title: string\n content: string\n force?: boolean\n forceAppend?: boolean\n autoUpdate?: boolean\n }): Promise<AddContentResult> {\n const result: AddContentResult = {\n added: false,\n updated: false,\n skipped: false,\n message: '',\n similarFound: 0,\n }\n\n try {\n // Search for similar content\n await this.prepareSearchEngine()\n const similar = await this.findSimilarContent(options.content)\n const mergedSimilar = this.mergeSimilarResults(\n similar,\n this.findDirectUserMatches(options.content)\n )\n\n if (options.force || options.forceAppend) {\n const mergeTarget =\n mergedSimilar.find((result) => result.source === 'user') ??\n this.findBestUserMergeTarget(options.title, options.content) ??\n this.findUserFileByTitle(options.title)\n\n if (mergeTarget) {\n const filePath = this.resolveSearchResultFilePath(mergeTarget.file_path)\n const existingContent = readFileSync(filePath, 'utf-8')\n\n if (options.forceAppend) {\n const newContent = this.appendKnowledgeUpdate(existingContent, options.title, options.content)\n writeFileSync(filePath, newContent)\n result.added = true\n result.filePath = filePath\n result.message = `Appended content to existing knowledge note: ${filePath.split('/').pop()}`\n } else {\n writeFileSync(filePath, this.renderKnowledgeNote(options.title, options.content))\n result.added = true\n result.filePath = filePath\n result.message = `Replaced existing knowledge note: ${filePath.split('/').pop()}`\n }\n\n result.similarContent = this.summarizeSimilarContent(\n [mergeTarget, ...mergedSimilar.filter((candidate) => candidate.id !== mergeTarget.id)],\n options.content\n )\n result.similarFound = result.similarContent.length\n\n await this.invalidateSearchIndex()\n return result\n }\n }\n\n if (mergedSimilar.length > 0 && !options.force) {\n const bestMatch = mergedSimilar[0]\n\n if (bestMatch.source === 'user') {\n // Update user file\n const filePath = this.resolveSearchResultFilePath(bestMatch.file_path)\n\n if (this.isContentDuplicate(bestMatch, options.content)) {\n result.skipped = true\n result.message = 'Existing content is comprehensive enough'\n } else if (options.autoUpdate && this.isContentEnhanced(bestMatch, options.content)) {\n writeFileSync(filePath, this.renderKnowledgeNote(options.title, options.content))\n\n result.updated = true\n result.filePath = filePath\n result.message = `Updated existing content: ${bestMatch.file_path}`\n\n // Trigger index update\n await this.invalidateSearchIndex()\n } else {\n result.skipped = true\n result.message = 'Existing content is comprehensive enough'\n }\n } else {\n if (this.isContentDuplicate(bestMatch, options.content)) {\n result.skipped = true\n result.message = 'Existing context7 content is comprehensive enough'\n } else {\n const created = this.writeUserContentFile(options)\n result.added = true\n result.filePath = created.filePath\n result.message = created.message\n\n // Trigger index update\n await this.invalidateSearchIndex()\n }\n }\n result.similarContent = this.summarizeSimilarContent(mergedSimilar, options.content)\n result.similarFound = mergedSimilar.length\n } else if (!result.added && !result.updated && !result.skipped) {\n const existingUserFile = this.getExpectedUserFilePath(options.title)\n\n if (existsSync(existingUserFile) && !options.force && !options.forceAppend) {\n const { readFileSync } = await import('node:fs')\n const existingContent = readFileSync(existingUserFile, 'utf-8')\n const existingEntry = {\n content: existingContent,\n file_path: existingUserFile,\n }\n\n if (this.isContentDuplicate(existingEntry, options.content)) {\n result.skipped = true\n result.message = 'Existing content is comprehensive enough'\n result.similarContent = this.summarizeSimilarContent(\n [\n {\n id: `user/${existingUserFile.split('/').pop() ?? 'existing.md'}`,\n title: options.title,\n content: existingContent,\n source: 'user',\n file_path: existingUserFile,\n score: 1,\n metadata: {\n backendId: 'direct-user-match',\n },\n },\n ],\n options.content\n )\n } else if (options.autoUpdate && this.isContentEnhanced(existingEntry, options.content)) {\n writeFileSync(existingUserFile, this.renderKnowledgeNote(options.title, options.content))\n result.updated = true\n result.filePath = existingUserFile\n result.message = `Updated existing content: ${existingUserFile.split('/').pop()}`\n\n await this.invalidateSearchIndex()\n } else {\n // File exists and no force flag - show error and file content\n result.message = `File already exists: ${existingUserFile.split('/').pop()}. Use --force to overwrite or --force-append to append.`\n result.existingFile = {\n path: existingUserFile,\n content: existingContent,\n }\n }\n } else {\n const created = this.writeUserContentFile(options, {\n overwriteExisting: existsSync(existingUserFile) && Boolean(options.force),\n })\n result.message = created.message\n result.filePath = created.filePath\n\n result.added = true\n\n // Trigger index update\n await this.invalidateSearchIndex()\n }\n }\n } catch (error) {\n result.message = `Failed to add content: ${error}`\n }\n\n result.similarFound = result.similarContent?.length || 0\n return result\n }\n\n getContentStats(): ContentStats {\n const stats: ContentStats = {\n userFiles: 0,\n context7Files: 0,\n totalFiles: 0,\n userDirExists: existsSync(this.userDir),\n context7DirExists: existsSync(this.context7BaseDir),\n }\n\n if (stats.userDirExists) {\n stats.userFiles = readdirSync(this.userDir).filter((f) => f.endsWith('.md')).length\n }\n\n if (stats.context7DirExists) {\n // Count files in all context7 project subdirectories\n const projects = readdirSync(this.context7BaseDir, { withFileTypes: true })\n .filter((dirent) => dirent.isDirectory())\n .map((dirent) => dirent.name)\n\n for (const encodedProjectId of projects) {\n const projectDir = join(this.context7BaseDir, encodedProjectId)\n if (existsSync(projectDir)) {\n stats.context7Files += readdirSync(projectDir).filter((f) => f.endsWith('.md')).length\n }\n }\n }\n\n stats.totalFiles = stats.userFiles + stats.context7Files\n return stats\n }\n\n listContent(source?: 'user' | 'context7'): ContentItem[] {\n const contentList: ContentItem[] = []\n\n // List user content\n if (source === 'user' || !source) {\n if (existsSync(this.userDir)) {\n const files = readdirSync(this.userDir)\n for (const file of files) {\n if (!file.endsWith('.md')) continue\n\n const filePath = join(this.userDir, file)\n const stat = statSync(filePath)\n\n let title = file.replace('.md', '')\n try {\n const content = readFileSync(filePath, 'utf-8')\n const firstLine = content.split('\\n')[0]\n if (firstLine?.startsWith('# ')) {\n title = firstLine.slice(2)\n }\n } catch {\n // Use filename if can't read\n }\n\n contentList.push({\n title,\n filename: file,\n source: 'user',\n path: filePath,\n size: stat.size,\n modified: stat.mtime || new Date(),\n })\n }\n }\n }\n\n // List context7 content from all projects\n if (source === 'context7' || !source) {\n if (existsSync(this.context7BaseDir)) {\n const projects = readdirSync(this.context7BaseDir, { withFileTypes: true })\n .filter((dirent) => dirent.isDirectory())\n .map((dirent) => dirent.name)\n\n for (const encodedProjectId of projects) {\n const projectId = this.decodeContext7ProjectId(encodedProjectId)\n const projectDir = join(this.context7BaseDir, encodedProjectId)\n if (existsSync(projectDir)) {\n const files = readdirSync(projectDir)\n for (const file of files) {\n if (!file.endsWith('.md')) continue\n\n const filePath = join(projectDir, file)\n const stat = statSync(filePath)\n\n let title = file.replace('.md', '')\n try {\n const content = readFileSync(filePath, 'utf-8')\n const firstLine = content.split('\\n')[0]\n if (firstLine?.startsWith('# ')) {\n title = firstLine.slice(2)\n }\n } catch {\n // Use filename if can't read\n }\n\n contentList.push({\n title,\n filename: file,\n source: 'context7',\n path: filePath,\n size: stat.size,\n modified: stat.mtime || new Date(),\n })\n }\n }\n }\n }\n }\n\n return contentList.sort((a, b) => b.modified.getTime() - a.modified.getTime())\n }\n\n private async downloadContext7Doc(libraryId: string): Promise<string> {\n const baseUrl = (process.env.SKILL_CREATOR_CONTEXT7_BASE_URL ?? 'https://context7.com').replace(\n /\\/$/,\n ''\n )\n const url = `${baseUrl}${libraryId}/llms.txt?tokens=100000000&topic=*`\n\n const fetchImpl = this.options.fetchImpl ?? fetch\n const response = await fetchImpl(url)\n if (!response.ok) {\n throw new Error(`Failed to download: ${response.statusText}`)\n }\n\n return await response.text()\n }\n\n private async sliceDocument(content: string, outputDir: string): Promise<string[]> {\n const sections = this.extractSections(content)\n const savedFiles: string[] = []\n\n for (let i = 0; i < sections.length; i++) {\n const { title, content: sectionContent } = sections[i]\n if (!sectionContent.trim()) continue\n\n const filename = this.createUniqueFileName(title, i, outputDir)\n const filePath = join(outputDir, filename)\n\n writeFileSync(filePath, `# ${title}\\n\\n${sectionContent}`)\n savedFiles.push(filePath)\n }\n\n return savedFiles\n }\n\n private extractSections(content: string): Array<{ title: string; content: string }> {\n const sections: Array<{ title: string; content: string }> = []\n const lines = content.split('\\n')\n let currentSection = { title: 'Introduction', content: '' }\n let sectionContent: string[] = []\n\n const headingPattern = /^(#{1,6})\\s+(.+)$/\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n const match = headingPattern.exec(line)\n\n if (match) {\n // Save previous section if it has content\n if (sectionContent.length > 0) {\n currentSection.content = sectionContent.join('\\n').trim()\n\n // Only add section if it meets minimum content threshold\n if (currentSection.content.length > 50) {\n sections.push({ ...currentSection })\n }\n\n sectionContent = []\n }\n\n // Start new section\n currentSection = {\n title: match[2].trim(),\n content: '',\n }\n } else {\n // Add non-heading lines to current section\n if (line.trim() || sectionContent.length > 0) {\n sectionContent.push(line)\n }\n }\n }\n\n // Add the last section\n if (sectionContent.length > 0) {\n currentSection.content = sectionContent.join('\\n').trim()\n if (currentSection.content.length > 50) {\n sections.push(currentSection)\n }\n }\n\n // Post-process sections to merge very short ones\n return this.mergeShortSections(sections)\n }\n\n private mergeShortSections(\n sections: Array<{ title: string; content: string }>\n ): Array<{ title: string; content: string }> {\n const merged: Array<{ title: string; content: string }> = []\n\n for (let i = 0; i < sections.length; i++) {\n const section = sections[i]\n\n // If section is very short and not the last, consider merging with next\n if (section.content.length < 200 && i < sections.length - 1) {\n const nextSection = sections[i + 1]\n\n // Merge if next section is not a major section\n if (!this.isMajorSection(nextSection.title)) {\n merged.push({\n title: section.title,\n content: section.content + '\\n\\n' + nextSection.content,\n })\n i++ // Skip next section\n } else {\n merged.push(section)\n }\n } else {\n merged.push(section)\n }\n }\n\n return merged\n }\n\n private isMajorSection(title: string): boolean {\n const majorKeywords = [\n 'introduction',\n 'getting started',\n 'installation',\n 'setup',\n 'api',\n 'reference',\n 'examples',\n 'tutorial',\n 'guide',\n 'overview',\n 'quick start',\n 'basics',\n 'advanced',\n ]\n\n const titleLower = title.toLowerCase()\n return majorKeywords.some((keyword) => titleLower.includes(keyword))\n }\n\n private createUniqueFileName(title: string, index: number, dir: string): string {\n // Clean up the title for use in filename\n const safeTitle =\n title\n .replace(/[^\\w\\s-]/g, '')\n .replace(/[-\\s]+/g, '_')\n .replace(/^_+|_+$/g, '') // Remove leading/trailing underscores\n .slice(0, 50) || 'section'\n\n // Create hierarchical naming based on heading level if possible\n const headingLevel = this.extractHeadingLevel(title)\n const prefix =\n headingLevel > 1 ? `${'0'.repeat(Math.min(headingLevel - 1, 3))}${index}` : index.toString()\n\n // Use both index and title for better organization\n const baseName = `${prefix.padStart(4, '0')}_${safeTitle}`\n let filename = `${baseName}.md`\n let counter = 1\n\n while (existsSync(join(dir, filename))) {\n filename = `${baseName}_${counter.toString().padStart(2, '0')}.md`\n counter++\n }\n\n return filename\n }\n\n private extractHeadingLevel(title: string): number {\n // Try to extract heading level from title if it contains markdown-like indicators\n const match = title.match(/^(#{1,6})\\s*(.+)$/)\n if (match) {\n return match[1].length\n }\n\n // Look for other indicators in the title\n if (/^(Chapter|Section|Part)\\s+\\d+/i.test(title)) return 1\n if (/^\\d+\\.\\d+/i.test(title)) return 2\n if (/^\\d+\\.\\d+\\.\\d+/i.test(title)) return 3\n\n return 1 // Default to level 1\n }\n\n private createUniqueFilePath(title: string, dir: string): string {\n // Convert to lowercase and clean up for filename\n const safeTitle =\n title\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '')\n .replace(/[-\\s]+/g, '_')\n .replace(/^_+|_+$/g, '') // Remove leading/trailing underscores\n .slice(0, 50) || 'content'\n\n let filename = `${safeTitle}.md`\n let counter = 1\n while (existsSync(join(dir, filename))) {\n filename = `${safeTitle}_${counter.toString().padStart(2, '0')}.md`\n counter++\n }\n return join(dir, filename)\n }\n\n private async findSimilarContent(\n content: string,\n threshold: number = 0.7,\n maxResults: number = 10\n ): Promise<any[]> {\n const results = await this.options.searchEngine.search(content, maxResults)\n return results\n .map((result: any) => ({\n ...result,\n similarity: this.getSimilarityScore(result),\n }))\n .filter((result: any) => result.similarity >= threshold)\n .sort(\n (left: any, right: any) =>\n this.getSourcePriority(right.source) - this.getSourcePriority(left.source) ||\n right.similarity - left.similarity ||\n right.score - left.score\n )\n }\n\n private isContentEnhanced(existing: any, newContent: string): boolean {\n const existingWords = existing.content.split(/\\s+/).length\n const newWords = newContent.split(/\\s+/).length\n return newWords > existingWords * 1.3\n }\n\n private isContentDuplicate(existing: any, newContent: string): boolean {\n const normalizedExisting = this.normalizeContentForComparison(existing.content)\n const normalizedNew = this.normalizeContentForComparison(newContent)\n\n if (normalizedExisting === normalizedNew) {\n return true\n }\n\n const shorterLength = Math.min(normalizedExisting.length, normalizedNew.length)\n if (shorterLength === 0) {\n return false\n }\n\n return (\n (normalizedExisting.includes(normalizedNew) || normalizedNew.includes(normalizedExisting)) &&\n shorterLength / Math.max(normalizedExisting.length, normalizedNew.length) >= 0.85\n )\n }\n\n private normalizeContentForComparison(content: string): string {\n return normalizeSearchableText(content)\n .toLowerCase()\n .replace(/\\s+/g, ' ')\n .trim()\n }\n\n private getSimilarityScore(result: { score: number; metadata?: Record<string, unknown> }): number {\n const rerankScore = result.metadata?.['rerankScore']\n if (typeof rerankScore === 'number' && Number.isFinite(rerankScore)) {\n return this.clamp01(rerankScore)\n }\n\n const backendId = result.metadata?.['backendId']\n if (backendId === 'minisearch' || result.score > 1) {\n return this.clamp01(result.score / (result.score + 3))\n }\n\n return this.clamp01(result.score)\n }\n\n private mergeSimilarResults(primary: SearchResult[], secondary: SearchResult[]): SearchResult[] {\n const merged = new Map<string, SearchResult>()\n\n for (const result of [...primary, ...secondary]) {\n const existing = merged.get(result.id)\n if (!existing) {\n merged.set(result.id, result)\n continue\n }\n\n const existingScore = this.getSimilarityScore(existing)\n const nextScore = this.getSimilarityScore(result)\n if (nextScore > existingScore) {\n merged.set(result.id, result)\n }\n }\n\n return Array.from(merged.values()).sort(\n (left, right) =>\n this.getSourcePriority(right.source) - this.getSourcePriority(left.source) ||\n this.getSimilarityScore(right) - this.getSimilarityScore(left) ||\n right.score - left.score\n )\n }\n\n private findDirectUserMatches(content: string): SearchResult[] {\n if (!existsSync(this.userDir)) {\n return []\n }\n\n const normalizedQuery = this.normalizeContentForComparison(content)\n const queryTokens = new Set(normalizedQuery.split(/\\s+/).filter(Boolean))\n\n return readdirSync(this.userDir)\n .filter((file) => file.endsWith('.md'))\n .map((file) => {\n const filePath = join(this.userDir, file)\n const rawContent = readFileSync(filePath, 'utf-8')\n const title = rawContent.split('\\n')[0]?.replace(/^#+\\s*/, '').trim() || file.replace(/\\.md$/, '')\n const normalizedExisting = this.normalizeContentForComparison(rawContent)\n const existingTokens = new Set(normalizedExisting.split(/\\s+/).filter(Boolean))\n const overlap = this.calculateTokenOverlap(queryTokens, existingTokens)\n const duplicateScore =\n this.isContentDuplicate({ content: rawContent }, content) ? 1 :\n normalizedExisting.includes(normalizedQuery) || normalizedQuery.includes(normalizedExisting) ? 0.9\n : overlap\n\n return {\n id: `user/${file}`,\n title,\n content: rawContent,\n source: 'user' as const,\n file_path: filePath,\n score: duplicateScore,\n metadata: {\n backendId: 'direct-user-match',\n },\n }\n })\n .filter((result) => result.score >= 0.7)\n }\n\n private findUserFileByTitle(title: string): SearchResult | undefined {\n const expectedPath = this.getExpectedUserFilePath(title)\n if (!existsSync(expectedPath)) {\n return undefined\n }\n\n const content = readFileSync(expectedPath, 'utf-8')\n return {\n id: `user/${expectedPath.split('/').pop() ?? 'existing.md'}`,\n title,\n content,\n source: 'user',\n file_path: expectedPath,\n score: 1,\n metadata: {\n backendId: 'direct-user-match',\n },\n }\n }\n\n private findBestUserMergeTarget(title: string, content: string): SearchResult | undefined {\n if (!existsSync(this.userDir)) {\n return undefined\n }\n\n const normalizedTitle = this.normalizeContentForComparison(title)\n const titleTokens = new Set(normalizedTitle.split(/\\s+/).filter(Boolean))\n const normalizedContent = this.normalizeContentForComparison(content)\n const contentTokens = new Set(normalizedContent.split(/\\s+/).filter(Boolean))\n\n const candidates = readdirSync(this.userDir)\n .filter((file) => file.endsWith('.md'))\n .map((file) => {\n const filePath = join(this.userDir, file)\n const rawContent = readFileSync(filePath, 'utf-8')\n const existingTitle =\n rawContent.split('\\n')[0]?.replace(/^#+\\s*/, '').trim() || file.replace(/\\.md$/, '')\n const normalizedExistingTitle = this.normalizeContentForComparison(existingTitle)\n const normalizedExistingContent = this.normalizeContentForComparison(rawContent)\n\n const titleOverlap = this.calculateTokenOverlap(\n titleTokens,\n new Set(normalizedExistingTitle.split(/\\s+/).filter(Boolean))\n )\n const contentOverlap = this.calculateTokenOverlap(\n contentTokens,\n new Set(normalizedExistingContent.split(/\\s+/).filter(Boolean))\n )\n const duplicateScore =\n this.isContentDuplicate({ content: rawContent }, content) ? 1 :\n normalizedExistingContent.includes(normalizedContent) ||\n normalizedContent.includes(normalizedExistingContent) ?\n 0.9\n : 0\n const exactTitleScore = normalizedExistingTitle === normalizedTitle ? 1 : 0\n const mergeScore = Math.max(\n duplicateScore,\n exactTitleScore,\n titleOverlap * 0.45 + contentOverlap * 0.55\n )\n\n return {\n result: {\n id: `user/${file}`,\n title: existingTitle,\n content: rawContent,\n source: 'user' as const,\n file_path: filePath,\n score: mergeScore,\n metadata: {\n backendId: 'direct-user-match',\n },\n } satisfies SearchResult,\n mergeScore,\n }\n })\n .sort((left, right) => right.mergeScore - left.mergeScore)\n\n return candidates[0] != null && candidates[0].mergeScore >= 0.2 ? candidates[0].result : undefined\n }\n\n private calculateTokenOverlap(left: Set<string>, right: Set<string>): number {\n if (left.size === 0 || right.size === 0) {\n return 0\n }\n\n let intersection = 0\n for (const token of left) {\n if (right.has(token)) {\n intersection += 1\n }\n }\n\n return intersection / Math.max(left.size, right.size)\n }\n\n private getSourcePriority(source: string): number {\n return source === 'user' ? 1 : 0\n }\n\n private clamp01(value: number): number {\n if (!Number.isFinite(value)) return 0\n if (value < 0) return 0\n if (value > 1) return 1\n return value\n }\n\n private summarizeSimilarContent(\n similar: SearchResult[],\n query: string\n ): NonNullable<AddContentResult['similarContent']> {\n const topResults = similar.slice(0, 3)\n const qualitySummary = analyzeSearchQuality(topResults, query)\n\n return qualitySummary.results.map((qualityResult) => ({\n title: qualityResult.result.title,\n score: Number((qualityResult.displayScore * 100).toFixed(2)),\n source: qualityResult.result.source,\n sourceRank: qualityResult.index === 0 ? 'primary' : 'secondary',\n preview: qualityResult.result.content.slice(0, 200) + '...',\n }))\n }\n\n private resolveSearchResultFilePath(filePath: string): string {\n return isAbsolute(filePath) ? filePath : join(this.options.referencesDir, filePath)\n }\n\n private getExpectedUserFilePath(title: string): string {\n mkdirSync(this.userDir, { recursive: true })\n const safeTitle =\n title\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '')\n .replace(/[-\\s]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .slice(0, 50) || 'content'\n return join(this.userDir, `${safeTitle}.md`)\n }\n\n private renderKnowledgeNote(title: string, content: string): string {\n const body = this.stripMatchingLeadingHeading(content, title)\n return `# ${title}\\n\\n${body}`\n }\n\n private appendKnowledgeUpdate(existingContent: string, title: string, content: string): string {\n const trimmedExisting = existingContent.trimEnd()\n const updateSectionHeading = '## Knowledge updates'\n const nextUpdateIndex = this.getNextKnowledgeUpdateIndex(trimmedExisting)\n const updateBody = this.stripMatchingLeadingHeading(content, title)\n const updateEntry = `### Update ${nextUpdateIndex}: ${title}\\n\\n${updateBody}`\n\n if (trimmedExisting.includes(updateSectionHeading)) {\n return `${trimmedExisting}\\n\\n${updateEntry}\\n`\n }\n\n return `${trimmedExisting}\\n\\n${updateSectionHeading}\\n\\n${updateEntry}\\n`\n }\n\n private getNextKnowledgeUpdateIndex(content: string): number {\n const matches = [...content.matchAll(/^### Update (\\d+): /gm)]\n const maxIndex = matches.reduce((currentMax, match) => {\n const nextValue = Number(match[1])\n return Number.isFinite(nextValue) ? Math.max(currentMax, nextValue) : currentMax\n }, 0)\n\n return maxIndex + 1\n }\n\n private stripMatchingLeadingHeading(content: string, expectedTitle: string): string {\n const trimmed = content.trim()\n if (!trimmed) {\n return ''\n }\n\n const lines = trimmed.split('\\n')\n const firstContentIndex = lines.findIndex((line) => line.trim().length > 0)\n if (firstContentIndex < 0) {\n return trimmed\n }\n\n const firstLine = lines[firstContentIndex]?.trim() ?? ''\n const headingMatch = /^(#{1,6})\\s+(.+)$/.exec(firstLine)\n if (!headingMatch) {\n return trimmed\n }\n\n const headingTitle = this.normalizeContentForComparison(headingMatch[2] ?? '')\n const normalizedExpectedTitle = this.normalizeContentForComparison(expectedTitle)\n if (headingTitle !== normalizedExpectedTitle) {\n return trimmed\n }\n\n const remainingLines = lines.slice(firstContentIndex + 1)\n while (remainingLines[0]?.trim() === '') {\n remainingLines.shift()\n }\n\n return remainingLines.join('\\n').trim() || trimmed\n }\n\n private writeUserContentFile(\n options: {\n title: string\n content: string\n },\n behavior: {\n overwriteExisting?: boolean\n } = {}\n ): { filePath: string; message: string } {\n mkdirSync(this.userDir, { recursive: true })\n const expectedFilePath = this.getExpectedUserFilePath(options.title)\n\n if (behavior.overwriteExisting && existsSync(expectedFilePath)) {\n writeFileSync(expectedFilePath, this.renderKnowledgeNote(options.title, options.content))\n return {\n filePath: expectedFilePath,\n message: `Overwrote existing content: ${expectedFilePath.split('/').pop()}`,\n }\n }\n\n const filePath = this.createUniqueFilePath(options.title, this.userDir)\n writeFileSync(filePath, this.renderKnowledgeNote(options.title, options.content))\n return {\n filePath,\n message: `Created new content: ${filePath.split('/').pop()}`,\n }\n }\n\n private getHashFilePath(projectId: string): string {\n // Sanitize project ID for use in filename (replace slashes with underscores)\n const safeProjectId = this.encodeContext7ProjectId(projectId)\n return join(this.options.referencesDir, '..', `.context7_hash_${safeProjectId}`)\n }\n\n private saveContentHash(content: string, projectId: string): void {\n const hash = createHash('md5').update(content).digest('hex')\n const hashFile = this.getHashFilePath(projectId)\n writeFileSync(hashFile, hash)\n }\n\n private needsUpdate(content: string, projectId: string): boolean {\n const hashFile = this.getHashFilePath(projectId)\n\n if (!existsSync(hashFile)) {\n return true\n }\n\n const currentHash = createHash('md5').update(content).digest('hex')\n const storedHash = readFileSync(hashFile, 'utf-8')\n\n return currentHash !== storedHash\n }\n\n private async prepareSearchEngine(): Promise<void> {\n if (typeof this.options.searchEngine.initialize === 'function') {\n await this.options.searchEngine.initialize()\n }\n if (typeof this.options.searchEngine.buildIndex === 'function') {\n await this.options.searchEngine.buildIndex(this.options.referencesDir)\n }\n }\n\n private async invalidateSearchIndex(): Promise<void> {\n if (typeof this.options.searchEngine.clearIndex === 'function') {\n await this.options.searchEngine.clearIndex()\n }\n\n const searchDir = join(this.options.referencesDir, '..', 'search')\n const searchArtifacts = [\n 'index-state.json',\n 'minisearch-index.json',\n 'vector-index-state.json',\n 'vector-index.db',\n ]\n\n for (const artifact of searchArtifacts) {\n rmSync(join(searchDir, artifact), { force: true })\n }\n }\n\n /**\n * List all context7 projects\n */\n listContext7Projects(): Array<{ projectId: string; filesCount: number; directory: string }> {\n const projects: Array<{ projectId: string; filesCount: number; directory: string }> = []\n\n if (!existsSync(this.context7BaseDir)) {\n return projects\n }\n\n const projectDirs = readdirSync(this.context7BaseDir, { withFileTypes: true })\n .filter((dirent) => dirent.isDirectory())\n .map((dirent) => dirent.name)\n\n for (const encodedProjectId of projectDirs) {\n const projectId = this.decodeContext7ProjectId(encodedProjectId)\n const projectDir = join(this.context7BaseDir, encodedProjectId)\n if (existsSync(projectDir)) {\n const files = readdirSync(projectDir).filter((f) => f.endsWith('.md'))\n projects.push({\n projectId,\n filesCount: files.length,\n directory: projectDir,\n })\n }\n }\n\n return projects\n }\n\n /**\n * Get files for a specific context7 project\n */\n getContext7ProjectFiles(projectId: string): string[] {\n const projectDir = this.getContext7ProjectDir(projectId)\n\n if (!existsSync(projectDir)) {\n return []\n }\n\n return readdirSync(projectDir).filter((f) => f.endsWith('.md'))\n }\n\n /**\n * Remove a context7 project\n */\n removeContext7Project(projectId: string): { success: boolean; message: string } {\n const projectDir = this.getContext7ProjectDir(projectId)\n\n if (!existsSync(projectDir)) {\n return {\n success: false,\n message: `Context7 project ${projectId} does not exist`,\n }\n }\n\n try {\n // Remove project directory\n rmSync(projectDir, { recursive: true, force: true })\n\n // Remove project hash file\n const hashFile = this.getHashFilePath(projectId)\n if (existsSync(hashFile)) {\n rmSync(hashFile)\n }\n\n // Trigger reindex\n void this.invalidateSearchIndex()\n\n return {\n success: true,\n message: `Context7 project ${projectId} removed successfully`,\n }\n } catch (error) {\n return {\n success: false,\n message: `Failed to remove Context7 project ${projectId}: ${error}`,\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ import"./referenceSearchUtils-DZgupMZp.mjs";import"./searchQuality-DVdHmamh.mjs";import{t as e}from"./contentManager-CoVTiSDT.mjs";export{e as ContentManager};
@@ -0,0 +1 @@
1
+ import"./package-CFJazwTO.mjs";import{t as e}from"./context7-BxMpyQiW.mjs";export{e as Context7Utils};
@@ -0,0 +1,2 @@
1
+ import{t as e}from"./package-CFJazwTO.mjs";import{z as t}from"zod";const n=t.object({id:t.string(),title:t.string(),description:t.string().optional().default(``),branch:t.string().optional(),lastUpdateDate:t.string().optional(),state:t.string().optional(),totalTokens:t.number().optional().default(0),totalSnippets:t.number().optional().default(0),stars:t.number().optional().default(0),trustScore:t.number().optional().default(0),benchmarkScore:t.number().optional().default(0),versions:t.array(t.string()).optional().default([])}),r=t.object({results:t.array(n),searchFilterApplied:t.boolean().optional().default(!1)});var i=class{static async resolveLibrary(t,n={}){let r=n.version??await e.getPackageVersion(t)??void 0,i=this.createSearchQuery(t,r),a=(await this.searchLibraries(t,i,n)).results.map(e=>this.annotateCandidate(e,t,r)).sort((e,t)=>t.selectionScore-e.selectionScore).slice(0,n.limit??10),o=a[0];return!o||!this.acceptBestMatch(o).accepted?null:{packageName:t,version:r,query:i,bestMatch:o,candidates:a}}static createSearchQuery(t,n){let r=this.getPackageBaseName(t),i=n?e.formatVersion(n):``;return i?`${r} v${i}`:r}static async searchLibraries(e,t,n){let i=n.fetchImpl??fetch,a=this.getLibrarySearchNames(e),o=new Map;for(let e of a){let n=new URL(process.env.SKILL_CREATOR_CONTEXT7_SEARCH_BASE_URL??`https://context7.com/api/v2/libs/search`);n.searchParams.set(`libraryName`,e),n.searchParams.set(`query`,t);let a=await i(n.toString());if(!a.ok)throw Error(`Failed to search Context7 libraries: ${a.status} ${a.statusText}`);let s=r.parse(await a.json());for(let e of s.results){let t=o.get(e.id);(!t||this.getCandidateMergeScore(e)>this.getCandidateMergeScore(t))&&o.set(e.id,e)}}return{results:Array.from(o.values()),searchFilterApplied:!1}}static getLibrarySearchNames(e){let t=e,n=this.getPackageBaseName(e);return Array.from(new Set([t,n]))}static getPackageBaseName(e){return e.replace(/^@/,``).split(`/`).pop()??e}static tokenizeIdentifier(e){return e.toLowerCase().replace(/^@/,``).split(/[^a-z0-9]+/g).filter(Boolean)}static acceptBestMatch(e){return e?e.matchKind===`package-path`||e.matchKind===`package-slug`?{accepted:!0}:e.matchKind===`website`?{accepted:!1,reason:`website-without-package-match`}:{accepted:!1,reason:`unrelated-match`}:{accepted:!1,reason:`empty`}}static annotateCandidate(e,t,n){let r=this.classifyCandidate(e.id,t),i=this.matchesVersion(e.versions,n),a=(r===`package-path`?4:r===`package-slug`?3:r===`website`?2:1)*1e9+(i?1e8:0)+e.totalSnippets*1e3+Math.round(e.trustScore*100)*10+Math.round(e.benchmarkScore);return{...e,matchKind:r,versionMatched:i,selectionScore:a}}static classifyCandidate(e,t){let n=e.replace(/^\/+/,``).toLowerCase().split(`/`).filter(Boolean),r=t.replace(/^@/,``).toLowerCase().split(`/`).filter(Boolean);if(n[0]===`websites`)return`website`;let i=r.flatMap(e=>this.tokenizeIdentifier(e)),a=n.flatMap(e=>this.tokenizeIdentifier(e));if(n.slice(-r.length).join(`/`)===r.join(`/`)||a.slice(-i.length).join(`/`)===i.join(`/`))return`package-path`;let o=r[r.length-1],s=n[n.length-1]??``;if(s===o)return`package-slug`;let c=this.tokenizeIdentifier(o);return this.tokenizeIdentifier(s).join(`/`)===c.join(`/`)?`package-slug`:`other`}static matchesVersion(t,n){if(!n)return!1;let r=e.formatVersion(n);return t.some(e=>{let t=e.toLowerCase().replace(/^v/,``).replace(/_/g,`.`);return t===r||t.startsWith(`${r}.`)})}static getCandidateMergeScore(e){return e.totalSnippets*1e3+Math.round(e.trustScore*100)*10+Math.round(e.benchmarkScore)}};export{i as t};
2
+ //# sourceMappingURL=context7-BxMpyQiW.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context7-BxMpyQiW.mjs","names":[],"sources":["../src/utils/context7.ts"],"sourcesContent":["import { z } from 'zod'\nimport { PackageUtils } from './package.js'\n\nconst Context7SearchCandidateSchema = z.object({\n id: z.string(),\n title: z.string(),\n description: z.string().optional().default(''),\n branch: z.string().optional(),\n lastUpdateDate: z.string().optional(),\n state: z.string().optional(),\n totalTokens: z.number().optional().default(0),\n totalSnippets: z.number().optional().default(0),\n stars: z.number().optional().default(0),\n trustScore: z.number().optional().default(0),\n benchmarkScore: z.number().optional().default(0),\n versions: z.array(z.string()).optional().default([]),\n})\n\nconst Context7SearchResponseSchema = z.object({\n results: z.array(Context7SearchCandidateSchema),\n searchFilterApplied: z.boolean().optional().default(false),\n})\n\nexport type Context7SearchCandidate = z.infer<typeof Context7SearchCandidateSchema>\n\nexport interface ResolvedContext7Candidate extends Context7SearchCandidate {\n matchKind: 'package-path' | 'package-slug' | 'website' | 'other'\n versionMatched: boolean\n selectionScore: number\n}\n\nexport interface ResolveContext7Result {\n packageName: string\n version?: string\n query: string\n bestMatch: ResolvedContext7Candidate\n candidates: ResolvedContext7Candidate[]\n}\n\nexport interface ResolveContext7Options {\n version?: string\n limit?: number\n fetchImpl?: typeof fetch\n}\n\ninterface Context7CandidateAcceptance {\n accepted: boolean\n reason?:\n | 'empty'\n | 'unrelated-match'\n | 'website-without-package-match'\n}\n\nexport class Context7Utils {\n static async resolveLibrary(\n packageName: string,\n options: ResolveContext7Options = {}\n ): Promise<ResolveContext7Result | null> {\n const version = options.version ?? (await PackageUtils.getPackageVersion(packageName)) ?? undefined\n const query = this.createSearchQuery(packageName, version)\n const response = await this.searchLibraries(packageName, query, options)\n const candidates = response.results\n .map((candidate) => this.annotateCandidate(candidate, packageName, version))\n .sort((left, right) => right.selectionScore - left.selectionScore)\n .slice(0, options.limit ?? 10)\n\n const bestMatch = candidates[0]\n if (!bestMatch) return null\n const acceptance = this.acceptBestMatch(bestMatch)\n if (!acceptance.accepted) {\n return null\n }\n\n return {\n packageName,\n version,\n query,\n bestMatch,\n candidates,\n }\n }\n\n static createSearchQuery(packageName: string, version?: string): string {\n const baseName = this.getPackageBaseName(packageName)\n const formattedVersion = version ? PackageUtils.formatVersion(version) : ''\n return formattedVersion ? `${baseName} v${formattedVersion}` : baseName\n }\n\n private static async searchLibraries(\n packageName: string,\n query: string,\n options: ResolveContext7Options\n ): Promise<z.infer<typeof Context7SearchResponseSchema>> {\n const fetchImpl = options.fetchImpl ?? fetch\n const attempts = this.getLibrarySearchNames(packageName)\n const mergedResults = new Map<string, Context7SearchCandidate>()\n\n for (const libraryName of attempts) {\n const url = new URL(\n process.env.SKILL_CREATOR_CONTEXT7_SEARCH_BASE_URL ?? 'https://context7.com/api/v2/libs/search'\n )\n url.searchParams.set('libraryName', libraryName)\n url.searchParams.set('query', query)\n\n const response = await fetchImpl(url.toString())\n if (!response.ok) {\n throw new Error(`Failed to search Context7 libraries: ${response.status} ${response.statusText}`)\n }\n\n const payload = Context7SearchResponseSchema.parse(await response.json())\n for (const candidate of payload.results) {\n const existing = mergedResults.get(candidate.id)\n if (!existing || this.getCandidateMergeScore(candidate) > this.getCandidateMergeScore(existing)) {\n mergedResults.set(candidate.id, candidate)\n }\n }\n }\n\n return {\n results: Array.from(mergedResults.values()),\n searchFilterApplied: false,\n }\n }\n\n private static getLibrarySearchNames(packageName: string): string[] {\n const exact = packageName\n const baseName = this.getPackageBaseName(packageName)\n return Array.from(new Set([exact, baseName]))\n }\n\n private static getPackageBaseName(packageName: string): string {\n return packageName.replace(/^@/, '').split('/').pop() ?? packageName\n }\n\n private static tokenizeIdentifier(value: string): string[] {\n return value\n .toLowerCase()\n .replace(/^@/, '')\n .split(/[^a-z0-9]+/g)\n .filter(Boolean)\n }\n\n private static acceptBestMatch(\n candidate: ResolvedContext7Candidate\n ): Context7CandidateAcceptance {\n if (!candidate) {\n return { accepted: false, reason: 'empty' }\n }\n\n if (candidate.matchKind === 'package-path' || candidate.matchKind === 'package-slug') {\n return { accepted: true }\n }\n\n if (candidate.matchKind === 'website') {\n return {\n accepted: false,\n reason: 'website-without-package-match',\n }\n }\n\n return {\n accepted: false,\n reason: 'unrelated-match',\n }\n }\n\n private static annotateCandidate(\n candidate: Context7SearchCandidate,\n packageName: string,\n version?: string\n ): ResolvedContext7Candidate {\n const matchKind = this.classifyCandidate(candidate.id, packageName)\n const versionMatched = this.matchesVersion(candidate.versions, version)\n const matchWeight =\n matchKind === 'package-path' ? 4 :\n matchKind === 'package-slug' ? 3 :\n matchKind === 'website' ? 2 :\n 1\n const selectionScore =\n matchWeight * 1_000_000_000 +\n (versionMatched ? 100_000_000 : 0) +\n candidate.totalSnippets * 1_000 +\n Math.round(candidate.trustScore * 100) * 10 +\n Math.round(candidate.benchmarkScore)\n\n return {\n ...candidate,\n matchKind,\n versionMatched,\n selectionScore,\n }\n }\n\n private static classifyCandidate(\n candidateId: string,\n packageName: string\n ): ResolvedContext7Candidate['matchKind'] {\n const candidateSegments = candidateId\n .replace(/^\\/+/, '')\n .toLowerCase()\n .split('/')\n .filter(Boolean)\n const packageSegments = packageName\n .replace(/^@/, '')\n .toLowerCase()\n .split('/')\n .filter(Boolean)\n\n if (candidateSegments[0] === 'websites') {\n return 'website'\n }\n\n const packageTokens = packageSegments.flatMap((segment) => this.tokenizeIdentifier(segment))\n const candidateTokens = candidateSegments.flatMap((segment) => this.tokenizeIdentifier(segment))\n const candidateTail = candidateSegments.slice(-packageSegments.length)\n if (candidateTail.join('/') === packageSegments.join('/')) {\n return 'package-path'\n }\n\n const candidateTokenTail = candidateTokens.slice(-packageTokens.length)\n if (candidateTokenTail.join('/') === packageTokens.join('/')) {\n return 'package-path'\n }\n\n const packageBaseName = packageSegments[packageSegments.length - 1]\n const candidateLastSegment = candidateSegments[candidateSegments.length - 1] ?? ''\n if (candidateLastSegment === packageBaseName) {\n return 'package-slug'\n }\n\n const packageBaseTokens = this.tokenizeIdentifier(packageBaseName)\n const candidateLastTokens = this.tokenizeIdentifier(candidateLastSegment)\n if (candidateLastTokens.join('/') === packageBaseTokens.join('/')) {\n return 'package-slug'\n }\n\n return 'other'\n }\n\n private static matchesVersion(versions: string[], version?: string): boolean {\n if (!version) return false\n const formattedVersion = PackageUtils.formatVersion(version)\n return versions.some((candidateVersion) => {\n const normalizedVersion = candidateVersion.toLowerCase().replace(/^v/, '').replace(/_/g, '.')\n return (\n normalizedVersion === formattedVersion ||\n normalizedVersion.startsWith(`${formattedVersion}.`)\n )\n })\n }\n\n private static getCandidateMergeScore(candidate: Context7SearchCandidate): number {\n return (\n candidate.totalSnippets * 1_000 +\n Math.round(candidate.trustScore * 100) * 10 +\n Math.round(candidate.benchmarkScore)\n )\n }\n}\n"],"mappings":"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"}