cognova 0.1.10 → 0.2.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 (287) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_nuxt/{BLIw7q7e.js → 4MiwzAAt.js} +2 -2
  3. package/.output/public/_nuxt/{B8gRcrNP.js → 5ZXA0Ckq.js} +1 -1
  4. package/.output/public/_nuxt/9IQmsEjr.js +1 -0
  5. package/.output/public/_nuxt/{DlmPGBGX.js → 9QnH0xQM.js} +1 -1
  6. package/.output/public/_nuxt/{Qi4Arc25.js → B1X4Bzcy.js} +1 -1
  7. package/.output/public/_nuxt/{kuWCWJ92.js → B3y_Qqox.js} +1 -1
  8. package/.output/public/_nuxt/{CJVO4EWp.js → B6S_ob86.js} +1 -1
  9. package/.output/public/_nuxt/{xdp-pGVh.js → BAIz-dEB.js} +1 -1
  10. package/.output/public/_nuxt/{DIF41aNz.js → BCtfQCzC.js} +1 -1
  11. package/.output/public/_nuxt/BIIJhjQO.js +1 -0
  12. package/.output/public/_nuxt/{QOhiCNXN.js → BIckl6wA.js} +1 -1
  13. package/.output/public/_nuxt/{CqsT2LL7.js → BJ3o57WW.js} +1 -1
  14. package/.output/public/_nuxt/BNetzZzF.js +1 -0
  15. package/.output/public/_nuxt/{C5jkgqcy.js → BOj6t-oo.js} +1 -1
  16. package/.output/public/_nuxt/BS0ofHJK.js +1 -0
  17. package/.output/public/_nuxt/{CdKHIoKZ.js → BWhMnjID.js} +1 -1
  18. package/.output/public/_nuxt/{BUjpjVNI.js → BYjadNrw.js} +1 -1
  19. package/.output/public/_nuxt/{DLkyutNs.js → BZXMQuYP.js} +1 -1
  20. package/.output/public/_nuxt/B_3_hrpn.js +1 -0
  21. package/.output/public/_nuxt/BaBZjmMC.js +1 -0
  22. package/.output/public/_nuxt/BaMqDm5u.js +1 -0
  23. package/.output/public/_nuxt/{DwSbu8wg.js → BasgsT_S.js} +1 -1
  24. package/.output/public/_nuxt/{B4nb4eka.js → BffWCM73.js} +1 -1
  25. package/.output/public/_nuxt/{BxaDmjPM.js → BhzMoffi.js} +1 -1
  26. package/.output/public/_nuxt/{DjzaeRs8.js → BjjCvHLT.js} +1 -1
  27. package/.output/public/_nuxt/{CtJTw_81.js → BlAZO7nq.js} +1 -1
  28. package/.output/public/_nuxt/{C6nLQFo5.js → BlhFigLL.js} +1 -1
  29. package/.output/public/_nuxt/{CiJs9fhO.js → Bm9LyG4F.js} +1 -1
  30. package/.output/public/_nuxt/BoIxv-gM.js +1 -0
  31. package/.output/public/_nuxt/{C8xYfk_L.js → BqKFIuRj.js} +1 -1
  32. package/.output/public/_nuxt/Br19oYkq.js +1 -0
  33. package/.output/public/_nuxt/{htEHZODo.js → BrNqhp1a.js} +3 -3
  34. package/.output/public/_nuxt/{DFKQfGt8.js → BsEZoHd1.js} +1 -1
  35. package/.output/public/_nuxt/{CmF_IsFj.js → BxXOsXrM.js} +3 -3
  36. package/.output/public/_nuxt/{q7M6chAB.js → BzOqrmGa.js} +3 -3
  37. package/.output/public/_nuxt/{DT_NtxcV.js → C0JKNMDO.js} +1 -1
  38. package/.output/public/_nuxt/{BOb3QiPL.js → C0kh_F7v.js} +1 -1
  39. package/.output/public/_nuxt/{D472oOJg.js → C3FxIITy.js} +1 -1
  40. package/.output/public/_nuxt/{Bh7WRSzZ.js → C4pxqdgg.js} +1 -1
  41. package/.output/public/_nuxt/{ByWk4RVe.js → C61KgSco.js} +1 -1
  42. package/.output/public/_nuxt/{TjI_tk0y.js → C69W7k2j.js} +1 -1
  43. package/.output/public/_nuxt/{uqOLgWHD.js → C6RC3lA1.js} +1 -1
  44. package/.output/public/_nuxt/{DvA5meSv.js → C6aqGHu1.js} +1 -1
  45. package/.output/public/_nuxt/{CQM_5Ht0.js → CJUdYEdO.js} +1 -1
  46. package/.output/public/_nuxt/{BzWvPP8W.js → CKkC3Ptm.js} +1 -1
  47. package/.output/public/_nuxt/CNnJrDvu.js +1 -0
  48. package/.output/public/_nuxt/{Bf2SSE6Q.js → CVJQGP1Q.js} +1 -1
  49. package/.output/public/_nuxt/{0p0wrrYN.js → CVgTJeSq.js} +1 -1
  50. package/.output/public/_nuxt/CWMUi89H.js +1 -0
  51. package/.output/public/_nuxt/{DUYrX_jl.js → CYP_MLH8.js} +1 -1
  52. package/.output/public/_nuxt/{DEDt4pMc.js → C_BdYLzz.js} +1 -1
  53. package/.output/public/_nuxt/{CU3_Ewk2.js → Cdt3I3Go.js} +1 -1
  54. package/.output/public/_nuxt/{DX5y-Dpt.js → Cdu2qGgq.js} +1 -1
  55. package/.output/public/_nuxt/{DQunOghN.js → CeIVm4A3.js} +1 -1
  56. package/.output/public/_nuxt/CeIu7z4p.js +1 -0
  57. package/.output/public/_nuxt/{B73gh0JS.js → Ci7UEZQh.js} +1 -1
  58. package/.output/public/_nuxt/{C7NKjT6R.js → CihWZmJe.js} +2 -2
  59. package/.output/public/_nuxt/{902fefRh.js → CitkKxhw.js} +1 -1
  60. package/.output/public/_nuxt/{DH_IrAcJ.js → CmzH6R-N.js} +2 -2
  61. package/.output/public/_nuxt/{CgPM54k_.js → Cp2MA0cm.js} +1 -1
  62. package/.output/public/_nuxt/{hfaTbWV_.js → CqU2XbzO.js} +1 -1
  63. package/.output/public/_nuxt/{CuxiR1jN.js → Cqy_L_ip.js} +1 -1
  64. package/.output/public/_nuxt/{BFmbqknL.js → Cr8ixbr1.js} +1 -1
  65. package/.output/public/_nuxt/{BkiRMhMm.js → CsJ9KhQ4.js} +1 -1
  66. package/.output/public/_nuxt/{Dhvv7eaa.js → CtYFj7k1.js} +1 -1
  67. package/.output/public/_nuxt/{DG0VCYsy.js → CtchsY6e.js} +1 -1
  68. package/.output/public/_nuxt/{pqmDf_cQ.js → Cvp7FI3T.js} +1 -1
  69. package/.output/public/_nuxt/{38G-HXi8.js → CxuZBC8n.js} +5 -5
  70. package/.output/public/_nuxt/D2689qk4.js +1 -0
  71. package/.output/public/_nuxt/{Dw5YM8ji.js → D31L7Ks6.js} +1 -1
  72. package/.output/public/_nuxt/{DYTkcgRd.js → D3RiUGdz.js} +1 -1
  73. package/.output/public/_nuxt/{Csa19Lyu.js → D3e44mCL.js} +1 -1
  74. package/.output/public/_nuxt/{DmXY_FiQ.js → D6t3dcTl.js} +1 -1
  75. package/.output/public/_nuxt/{ChNxcBlH.js → DAQhmSv4.js} +1 -1
  76. package/.output/public/_nuxt/{BFtDSgo7.js → DB359q8R.js} +1 -1
  77. package/.output/public/_nuxt/{CrUNulhU.js → DCzfkCGa.js} +1 -1
  78. package/.output/public/_nuxt/{Cn1H4f01.js → DG-T44jj.js} +1 -1
  79. package/.output/public/_nuxt/{CAGIVdiL.js → DGX0tzL8.js} +1 -1
  80. package/.output/public/_nuxt/DHG66LPS.js +1 -0
  81. package/.output/public/_nuxt/{BV0QyLX9.js → DHKLCQRG.js} +1 -1
  82. package/.output/public/_nuxt/{DFs-kxzi.js → DIoI0uJm.js} +1 -1
  83. package/.output/public/_nuxt/{koWn-xAZ.js → DJ5V-y_x.js} +1 -1
  84. package/.output/public/_nuxt/{HUnwr3r4.js → DJMS2og1.js} +1 -1
  85. package/.output/public/_nuxt/{CjfarCg3.js → DJjDvbZE.js} +1 -1
  86. package/.output/public/_nuxt/DK9jxJ0g.js +1 -0
  87. package/.output/public/_nuxt/{BjN5vE4c.js → DKZxeXDQ.js} +1 -1
  88. package/.output/public/_nuxt/{CcWl1F-m.js → DLETdGFL.js} +1 -1
  89. package/.output/public/_nuxt/DOICd-Ld.js +1 -0
  90. package/.output/public/_nuxt/{bX88p2Mb.js → DPEcH-gi.js} +3 -3
  91. package/.output/public/_nuxt/{DDqY0xzR.js → DSRrg8JT.js} +1 -1
  92. package/.output/public/_nuxt/{B67bSHkr.js → DTDgHTuh.js} +2 -2
  93. package/.output/public/_nuxt/{BYzrgyQu.js → D_3Rq1BS.js} +1 -1
  94. package/.output/public/_nuxt/{TQO0uTDM.js → Daz4MeL6.js} +1 -1
  95. package/.output/public/_nuxt/{DxkqpACx.js → Db2v8O7O.js} +1 -1
  96. package/.output/public/_nuxt/{CHa8PuBL.js → Db8-_gO7.js} +1 -1
  97. package/.output/public/_nuxt/{BUEnkhcM.js → DfQu3kEw.js} +1 -1
  98. package/.output/public/_nuxt/{hDpBJf9-.js → DgV-EDJ9.js} +1 -1
  99. package/.output/public/_nuxt/{D1CNUSvv.js → DnjGH3SQ.js} +1 -1
  100. package/.output/public/_nuxt/Dp2X5R2m.js +1 -0
  101. package/.output/public/_nuxt/DqB723Z0.js +1 -0
  102. package/.output/public/_nuxt/{DcIiuvW-.js → DyRelyfs.js} +1 -1
  103. package/.output/public/_nuxt/{sgWRawF5.js → Dy_Cq5LQ.js} +1 -1
  104. package/.output/public/_nuxt/{B4T7pJz5.js → DznawRFW.js} +1 -1
  105. package/.output/public/_nuxt/EuOqK1A6.js +1 -0
  106. package/.output/public/_nuxt/{Dn4PGlE2.js → FNC8XZTk.js} +1 -1
  107. package/.output/public/_nuxt/{ZToTf_46.js → Fukkqjkf.js} +1 -1
  108. package/.output/public/_nuxt/{D5aulRKh.js → FvlxxmNk.js} +2 -2
  109. package/.output/public/_nuxt/{C2IkB9Vg.js → IRSbVPIu.js} +1 -1
  110. package/.output/public/_nuxt/{QESaAMMF.js → JJ3634gV.js} +1 -1
  111. package/.output/public/_nuxt/{DGUr7xQ7.js → Jez9DHn7.js} +1 -1
  112. package/.output/public/_nuxt/{DPdR3tu5.js → KKK6HVeG.js} +1 -1
  113. package/.output/public/_nuxt/{D0bh5qKf.js → Lwdv_RKd.js} +1 -1
  114. package/.output/public/_nuxt/{BkRZ4-cg.js → Nb2jBtYT.js} +1 -1
  115. package/.output/public/_nuxt/Q8Ps7oN5.js +1 -0
  116. package/.output/public/_nuxt/SXTDhzp6.js +1 -0
  117. package/.output/public/_nuxt/{GC9cY_vb.js → Sg2Lwc46.js} +1 -1
  118. package/.output/public/_nuxt/{GI6TTFJt.js → YuTZB7sD.js} +1 -1
  119. package/.output/public/_nuxt/{D2_L0Ze1.js → _CYZi8HN.js} +1 -1
  120. package/.output/public/_nuxt/_J_7XIn-.js +1 -0
  121. package/.output/public/_nuxt/builds/latest.json +1 -1
  122. package/.output/public/_nuxt/builds/meta/a1e9100c-1a4f-4f7e-bb53-9dbe0d07effb.json +1 -0
  123. package/.output/public/_nuxt/{DZNG1AbU.js → cABRLVee.js} +1 -1
  124. package/.output/public/_nuxt/eko-0FUm.js +1 -0
  125. package/.output/public/_nuxt/entry.CGxIBGAf.css +1 -0
  126. package/.output/public/_nuxt/{BIjt_cIY.js → g20UHUCv.js} +1 -1
  127. package/.output/public/_nuxt/{HIlTvc-2.js → gBC9k4Qj.js} +1 -1
  128. package/.output/public/_nuxt/{IQoLFBX8.js → ghuJ76mD.js} +1 -1
  129. package/.output/public/_nuxt/{BGYWH_3A.js → iBGCpHdw.js} +2 -2
  130. package/.output/public/_nuxt/{uUGV1V-6.js → inmzPrjz.js} +1 -1
  131. package/.output/public/_nuxt/{CgHipF8Q.js → m5kGCDpI.js} +1 -1
  132. package/.output/public/_nuxt/{Bj0OFhNx.js → nIU2F7ia.js} +3 -3
  133. package/.output/public/_nuxt/{Dcv_tyzb.js → oIX-ZDN6.js} +1 -1
  134. package/.output/public/_nuxt/{B7NSgclV.js → pcUI-zuY.js} +1 -1
  135. package/.output/public/_nuxt/{DpF6VoC4.js → sf57orEk.js} +1 -1
  136. package/.output/public/_nuxt/{DS_mF4Ol.js → wCGVE8_e.js} +1 -1
  137. package/.output/public/_nuxt/{KAzeBbaL.js → xuzLdW-o.js} +1 -1
  138. package/.output/public/_nuxt/{ytKsJZlj.js → yNrp2XvX.js} +1 -1
  139. package/.output/public/_nuxt/{B3mdl0Ae.js → yuf23kh9.js} +1 -1
  140. package/.output/server/chunks/build/{_uuid_-DAZ5CM0C.mjs → _uuid_-0UgdUhfY.mjs} +6 -5
  141. package/.output/server/chunks/build/{_uuid_-DAZ5CM0C.mjs.map → _uuid_-0UgdUhfY.mjs.map} +1 -1
  142. package/.output/server/chunks/build/chat-CZMiB68R.mjs.map +1 -1
  143. package/.output/server/chunks/build/client.precomputed.mjs +1 -1
  144. package/.output/server/chunks/build/dashboard-YEscLBQN.mjs +1109 -0
  145. package/.output/server/chunks/build/dashboard-YEscLBQN.mjs.map +1 -0
  146. package/.output/server/chunks/build/{docs-CFAI1clt.mjs → docs-Dk2JnYq3.mjs} +6 -5
  147. package/.output/server/chunks/build/{docs-CFAI1clt.mjs.map → docs-Dk2JnYq3.mjs.map} +1 -1
  148. package/.output/server/chunks/build/server.mjs +3 -3
  149. package/.output/server/chunks/build/settings-DdkKCJ00.mjs +1 -1
  150. package/.output/server/chunks/build/styles.mjs +4 -4
  151. package/.output/server/chunks/build/{CodeEditorFallback-DfYly7f5.mjs → useCopyToClipboard-vi6FYyyZ.mjs} +29 -2
  152. package/.output/server/chunks/build/useCopyToClipboard-vi6FYyyZ.mjs.map +1 -0
  153. package/.output/server/chunks/build/useNotificationBus-BG5JNQf1.mjs +1 -1
  154. package/.output/server/chunks/nitro/nitro.mjs +819 -824
  155. package/.output/server/chunks/routes/api/dashboard/overview.get.mjs +149 -0
  156. package/.output/server/chunks/routes/api/dashboard/overview.get.mjs.map +1 -0
  157. package/.output/server/chunks/routes/api/documents/_id/public.get.mjs +1 -1
  158. package/.output/server/chunks/routes/api/documents/_id/restore.post.mjs +1 -1
  159. package/.output/server/chunks/routes/api/documents/by-path.post.mjs +1 -1
  160. package/.output/server/chunks/routes/api/documents/index.delete.mjs +1 -1
  161. package/.output/server/chunks/routes/api/documents/index.put.mjs +1 -1
  162. package/.output/server/chunks/routes/api/fs/delete.post.mjs +1 -1
  163. package/.output/server/chunks/routes/api/fs/list.get.mjs +1 -1
  164. package/.output/server/chunks/routes/api/fs/mkdir.post.mjs +1 -1
  165. package/.output/server/chunks/routes/api/fs/move.post.mjs +1 -1
  166. package/.output/server/chunks/routes/api/fs/read.post.mjs +1 -1
  167. package/.output/server/chunks/routes/api/fs/rename.post.mjs +1 -1
  168. package/.output/server/chunks/routes/api/fs/write.post.mjs +1 -1
  169. package/.output/server/chunks/routes/api/health.get.mjs +1 -1
  170. package/.output/server/chunks/routes/api/home.get.mjs +1 -1
  171. package/.output/server/chunks/routes/api/hooks/index.get.mjs +1 -1
  172. package/.output/server/chunks/routes/api/hooks/index.post.mjs +1 -1
  173. package/.output/server/chunks/routes/api/hooks/stats.get.mjs +1 -1
  174. package/.output/server/chunks/routes/api/index.get3.mjs +1 -1
  175. package/.output/server/chunks/routes/api/index.get4.mjs +1 -1
  176. package/.output/server/chunks/routes/api/index.get5.mjs +1 -1
  177. package/.output/server/chunks/routes/api/index.get6.mjs +1 -1
  178. package/.output/server/chunks/routes/api/index.get7.mjs +1 -1
  179. package/.output/server/chunks/routes/api/index.get8.mjs +1 -1
  180. package/.output/server/chunks/routes/api/index.post2.mjs +1 -1
  181. package/.output/server/chunks/routes/api/index.post3.mjs +1 -1
  182. package/.output/server/chunks/routes/api/index.post4.mjs +1 -1
  183. package/.output/server/chunks/routes/api/index.put.mjs +1 -1
  184. package/.output/server/chunks/routes/api/memory/_id_.delete.mjs +1 -1
  185. package/.output/server/chunks/routes/api/memory/context.get.mjs +1 -1
  186. package/.output/server/chunks/routes/api/memory/extract.post.mjs +1 -1
  187. package/.output/server/chunks/routes/api/memory/search.get.mjs +1 -1
  188. package/.output/server/chunks/routes/api/memory/store.post.mjs +1 -1
  189. package/.output/server/chunks/routes/api/projects/index.delete.mjs +1 -1
  190. package/.output/server/chunks/routes/api/projects/index.get.mjs +1 -1
  191. package/.output/server/chunks/routes/api/projects/index.put.mjs +1 -1
  192. package/.output/server/chunks/routes/api/secrets/_key_.delete.mjs +1 -1
  193. package/.output/server/chunks/routes/api/secrets/_key_.get.mjs +1 -1
  194. package/.output/server/chunks/routes/api/secrets/_key_.put.mjs +1 -1
  195. package/.output/server/chunks/routes/api/tasks/_id/restore.post.mjs +1 -1
  196. package/.output/server/chunks/routes/api/tasks/index.delete.mjs +1 -1
  197. package/.output/server/chunks/routes/api/tasks/index.put.mjs +1 -1
  198. package/.output/server/chunks/routes/api/tasks/tags.get.mjs +1 -1
  199. package/.output/server/chunks/routes/api/usage/stats.get.mjs +1 -1
  200. package/.output/server/chunks/routes/renderer.mjs +1 -1
  201. package/.output/server/package.json +1 -1
  202. package/Claude/CLAUDE.md +9 -4
  203. package/Claude/skills/environment/SKILL.md +6 -0
  204. package/Claude/skills/memory/SKILL.md +6 -0
  205. package/Claude/skills/project/SKILL.md +6 -0
  206. package/Claude/skills/secret/SKILL.md +85 -0
  207. package/Claude/skills/secret/secret.py +146 -0
  208. package/Claude/skills/skill-creator/SKILL.md +30 -0
  209. package/Claude/skills/task/SKILL.md +6 -0
  210. package/app/components/dashboard/RecentChats.vue +93 -0
  211. package/app/components/dashboard/RecentDocs.vue +108 -0
  212. package/app/components/dashboard/StatCards.vue +82 -0
  213. package/app/components/dashboard/UpcomingTasks.vue +119 -0
  214. package/app/components/dashboard/UsageSummary.vue +94 -0
  215. package/app/components/editor/DocumentEditor.vue +5 -3
  216. package/app/components/skills/Card.vue +82 -0
  217. package/app/components/skills/CreateModal.vue +156 -0
  218. package/app/components/skills/Editor.vue +135 -0
  219. package/app/components/skills/FileTree.vue +336 -0
  220. package/app/components/skills/LibraryCard.vue +122 -0
  221. package/app/components/skills/RenameModal.vue +84 -0
  222. package/app/composables/useCopyToClipboard.ts +37 -0
  223. package/app/layouts/dashboard.vue +7 -0
  224. package/app/pages/chat.vue +11 -1
  225. package/app/pages/dashboard.vue +65 -64
  226. package/app/pages/skills/[name].vue +198 -0
  227. package/app/pages/skills/index.vue +157 -0
  228. package/app/pages/skills/library.vue +209 -0
  229. package/app/pages/view/[uuid].vue +4 -3
  230. package/dist/cli/index.js +23 -23
  231. package/nuxt.config.ts +9 -0
  232. package/package.json +1 -1
  233. package/server/api/dashboard/overview.get.ts +133 -0
  234. package/server/api/skills/[name]/files/create.post.ts +45 -0
  235. package/server/api/skills/[name]/files/delete.post.ts +45 -0
  236. package/server/api/skills/[name]/files/index.get.ts +28 -0
  237. package/server/api/skills/[name]/files/read.post.ts +41 -0
  238. package/server/api/skills/[name]/files/write.post.ts +42 -0
  239. package/server/api/skills/[name]/index.get.ts +54 -0
  240. package/server/api/skills/[name]/rename.post.ts +64 -0
  241. package/server/api/skills/[name]/toggle.post.ts +32 -0
  242. package/server/api/skills/create.post.ts +51 -0
  243. package/server/api/skills/generate.post.ts +126 -0
  244. package/server/api/skills/index.get.ts +57 -0
  245. package/server/api/skills/library/check-updates.get.ts +46 -0
  246. package/server/api/skills/library/index.get.ts +56 -0
  247. package/server/api/skills/library/install.post.ts +73 -0
  248. package/server/db/schema.ts +17 -0
  249. package/server/drizzle/migrations/0012_good_deadpool.sql +12 -0
  250. package/server/drizzle/migrations/0013_swift_snowbird.sql +1 -0
  251. package/server/drizzle/migrations/meta/0012_snapshot.json +1713 -0
  252. package/server/drizzle/migrations/meta/0013_snapshot.json +1720 -0
  253. package/server/drizzle/migrations/meta/_journal.json +14 -0
  254. package/server/middleware/auth.ts +0 -1
  255. package/server/plugins/05.skills-catalog.ts +105 -0
  256. package/server/utils/skills-path.ts +197 -0
  257. package/shared/types/index.ts +102 -0
  258. package/.output/public/_nuxt/BAWOIJ__.js +0 -1
  259. package/.output/public/_nuxt/B_paJhyq.js +0 -1
  260. package/.output/public/_nuxt/BdYiqlvd.js +0 -1
  261. package/.output/public/_nuxt/BpiqBbAF.js +0 -1
  262. package/.output/public/_nuxt/BqDfJ2hd.js +0 -1
  263. package/.output/public/_nuxt/BxgB0-mq.js +0 -1
  264. package/.output/public/_nuxt/C1hs5K_e.js +0 -1
  265. package/.output/public/_nuxt/C4Ncdd9K.js +0 -1
  266. package/.output/public/_nuxt/CAQ2bGHm.js +0 -1
  267. package/.output/public/_nuxt/CCpQAJpz.js +0 -1
  268. package/.output/public/_nuxt/CXXD-owe.js +0 -1
  269. package/.output/public/_nuxt/CcRijyCZ.js +0 -1
  270. package/.output/public/_nuxt/CoEfPCKj.js +0 -1
  271. package/.output/public/_nuxt/D08h2e32.js +0 -1
  272. package/.output/public/_nuxt/D_t_j6EB.js +0 -1
  273. package/.output/public/_nuxt/DaZy6Ki2.js +0 -1
  274. package/.output/public/_nuxt/DdrudNqf.js +0 -1
  275. package/.output/public/_nuxt/DdtmJxeb.js +0 -1
  276. package/.output/public/_nuxt/Dgv5fJ6Q.js +0 -1
  277. package/.output/public/_nuxt/DhIluGHO.js +0 -1
  278. package/.output/public/_nuxt/DuRk5Dc_.js +0 -1
  279. package/.output/public/_nuxt/VFwvl1mU.js +0 -1
  280. package/.output/public/_nuxt/builds/meta/555978fa-172f-4d0d-8238-88f8b610f712.json +0 -1
  281. package/.output/public/_nuxt/entry.DPbNaeQT.css +0 -1
  282. package/.output/public/_nuxt/hZV7Z8Ig.js +0 -1
  283. package/.output/public/_nuxt/lK0inMQ6.js +0 -1
  284. package/.output/server/chunks/build/CodeEditorFallback-DfYly7f5.mjs.map +0 -1
  285. package/.output/server/chunks/build/dashboard-DtbGchTa.mjs +0 -277
  286. package/.output/server/chunks/build/dashboard-DtbGchTa.mjs.map +0 -1
  287. /package/.output/public/_nuxt/{language-detection.Be_IvFWy.css → useCopyToClipboard.Be_IvFWy.css} +0 -0
@@ -0,0 +1,209 @@
1
+ <script setup lang="ts">
2
+ import type { SkillCatalogItem } from '~~/shared/types'
3
+
4
+ definePageMeta({
5
+ layout: 'dashboard',
6
+ middleware: 'auth'
7
+ })
8
+
9
+ const toast = useToast()
10
+ const skills = ref<SkillCatalogItem[]>([])
11
+ const loading = ref(true)
12
+ const installing = ref<string | null>(null)
13
+ const search = ref('')
14
+ const activeTag = ref<string | null>(null)
15
+
16
+ // Collect all unique tags across skills
17
+ const allTags = computed(() => {
18
+ const tagSet = new Set<string>()
19
+ for (const s of skills.value) {
20
+ for (const t of s.tags) tagSet.add(t)
21
+ }
22
+ return [...tagSet].sort()
23
+ })
24
+
25
+ const filteredSkills = computed(() => {
26
+ let result = skills.value
27
+
28
+ if (activeTag.value) {
29
+ result = result.filter(s => s.tags.includes(activeTag.value!))
30
+ }
31
+
32
+ if (search.value.trim()) {
33
+ const q = search.value.toLowerCase()
34
+ result = result.filter(s =>
35
+ s.name.toLowerCase().includes(q)
36
+ || s.description.toLowerCase().includes(q)
37
+ || s.author.toLowerCase().includes(q)
38
+ )
39
+ }
40
+
41
+ return result
42
+ })
43
+
44
+ function toggleTag(tag: string) {
45
+ activeTag.value = activeTag.value === tag ? null : tag
46
+ }
47
+
48
+ async function loadLibrary() {
49
+ loading.value = true
50
+ try {
51
+ const res = await $fetch<{ data: SkillCatalogItem[] }>('/api/skills/library')
52
+ skills.value = res.data
53
+ } catch {
54
+ toast.add({ title: 'Failed to load skills library', color: 'error' })
55
+ } finally {
56
+ loading.value = false
57
+ }
58
+ }
59
+
60
+ async function handleInstall(name: string) {
61
+ installing.value = name
62
+ try {
63
+ await $fetch('/api/skills/library/install', {
64
+ method: 'POST',
65
+ body: { name }
66
+ })
67
+ toast.add({ title: `Installed ${name}`, color: 'success' })
68
+ await loadLibrary()
69
+ } catch (e: unknown) {
70
+ const message = e instanceof Error ? e.message : 'Failed to install skill'
71
+ toast.add({ title: message, color: 'error' })
72
+ } finally {
73
+ installing.value = null
74
+ }
75
+ }
76
+
77
+ async function handleUpdate(name: string) {
78
+ installing.value = name
79
+ try {
80
+ // Remove existing first
81
+ await $fetch(`/api/skills/${name}/files/delete`, {
82
+ method: 'POST',
83
+ body: { path: '.' }
84
+ }).catch(() => {})
85
+
86
+ await $fetch('/api/skills/library/install', {
87
+ method: 'POST',
88
+ body: { name }
89
+ })
90
+ toast.add({ title: `Updated ${name}`, color: 'success' })
91
+ await loadLibrary()
92
+ } catch {
93
+ toast.add({ title: `Failed to update ${name}`, color: 'error' })
94
+ } finally {
95
+ installing.value = null
96
+ }
97
+ }
98
+
99
+ onMounted(() => loadLibrary())
100
+ </script>
101
+
102
+ <template>
103
+ <div class="flex flex-1 min-w-0">
104
+ <UDashboardPanel
105
+ id="skills-library"
106
+ grow
107
+ >
108
+ <template #header>
109
+ <UDashboardNavbar title="Skills Library">
110
+ <template #right>
111
+ <UInput
112
+ v-model="search"
113
+ icon="i-lucide-search"
114
+ placeholder="Search library..."
115
+ size="sm"
116
+ class="w-48"
117
+ />
118
+ <NuxtLink to="/skills">
119
+ <UButton
120
+ icon="i-lucide-arrow-left"
121
+ variant="ghost"
122
+ color="neutral"
123
+ size="sm"
124
+ >
125
+ My Skills
126
+ </UButton>
127
+ </NuxtLink>
128
+ </template>
129
+ </UDashboardNavbar>
130
+ </template>
131
+
132
+ <template #body>
133
+ <div class="p-4">
134
+ <!-- Tag filter bar -->
135
+ <div
136
+ v-if="allTags.length > 0 && !loading"
137
+ class="flex flex-wrap gap-1.5 mb-4"
138
+ >
139
+ <UButton
140
+ v-for="tag in allTags"
141
+ :key="tag"
142
+ size="xs"
143
+ :variant="activeTag === tag ? 'solid' : 'outline'"
144
+ :color="tag === 'official' ? 'primary' : 'neutral'"
145
+ @click="toggleTag(tag)"
146
+ >
147
+ {{ tag }}
148
+ </UButton>
149
+ </div>
150
+
151
+ <!-- Loading -->
152
+ <div
153
+ v-if="loading"
154
+ class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3"
155
+ >
156
+ <div
157
+ v-for="i in 6"
158
+ :key="i"
159
+ class="p-4 rounded-lg border border-default bg-elevated/50"
160
+ >
161
+ <USkeleton class="h-4 w-24 mb-2" />
162
+ <USkeleton class="h-3 w-full mb-1" />
163
+ <USkeleton class="h-3 w-2/3" />
164
+ </div>
165
+ </div>
166
+
167
+ <!-- Empty -->
168
+ <div
169
+ v-else-if="skills.length === 0"
170
+ class="flex flex-col items-center justify-center py-16 text-dimmed"
171
+ >
172
+ <UIcon
173
+ name="i-lucide-library"
174
+ class="size-12 mb-4"
175
+ />
176
+ <p class="text-lg font-medium">
177
+ No community skills available
178
+ </p>
179
+ <p class="text-sm mt-1">
180
+ The skills registry is empty or hasn't synced yet.
181
+ </p>
182
+ </div>
183
+
184
+ <!-- No results -->
185
+ <div
186
+ v-else-if="filteredSkills.length === 0"
187
+ class="text-center py-12 text-muted text-sm"
188
+ >
189
+ No skills matching {{ activeTag ? `"${activeTag}"` : '' }} {{ search ? `"${search}"` : '' }}
190
+ </div>
191
+
192
+ <!-- Grid -->
193
+ <div
194
+ v-else
195
+ class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3"
196
+ >
197
+ <SkillsLibraryCard
198
+ v-for="skill in filteredSkills"
199
+ :key="skill.name"
200
+ :skill="skill"
201
+ @install="handleInstall"
202
+ @update="handleUpdate"
203
+ />
204
+ </div>
205
+ </div>
206
+ </template>
207
+ </UDashboardPanel>
208
+ </div>
209
+ </template>
@@ -60,18 +60,19 @@ watch(viewSource, v => viewSourceMode.value = v)
60
60
 
61
61
  // Copy content to clipboard
62
62
  const toast = useToast()
63
+ const { copy } = useCopyToClipboard()
63
64
  async function copyContent() {
64
65
  const content = data.value?.data?.content
65
66
  if (!content) return
66
67
 
67
- try {
68
- await navigator.clipboard.writeText(content)
68
+ const ok = await copy(content)
69
+ if (ok) {
69
70
  toast.add({
70
71
  title: 'Copied to clipboard',
71
72
  icon: 'i-lucide-check',
72
73
  color: 'success'
73
74
  })
74
- } catch {
75
+ } else {
75
76
  toast.add({
76
77
  title: 'Failed to copy',
77
78
  icon: 'i-lucide-x',
package/dist/cli/index.js CHANGED
@@ -154,11 +154,6 @@ var require_src = __commonJS({
154
154
  }
155
155
  });
156
156
 
157
- // src/index.ts
158
- import { readFileSync as readFileSync4 } from "fs";
159
- import { join as join11, dirname as dirname2 } from "path";
160
- import { fileURLToPath as fileURLToPath2 } from "url";
161
-
162
157
  // src/commands/init.ts
163
158
  import { execSync as execSync5 } from "child_process";
164
159
  import { join as join7 } from "path";
@@ -1357,6 +1352,14 @@ var __dirname = dirname(__filename);
1357
1352
  function getPackageDir() {
1358
1353
  return join(__dirname, "..", "..");
1359
1354
  }
1355
+ function getPackageVersion() {
1356
+ try {
1357
+ const pkg = JSON.parse(readFileSync(join(getPackageDir(), "package.json"), "utf-8"));
1358
+ return pkg.version || "0.0.0";
1359
+ } catch {
1360
+ return "0.0.0";
1361
+ }
1362
+ }
1360
1363
  function getClaudeDir() {
1361
1364
  return join(homedir(), ".claude");
1362
1365
  }
@@ -1694,6 +1697,7 @@ You run as a persistent service managed by PM2. Your conversations are streamed
1694
1697
  | Task Management | \`/task\` | Create, list, update, complete tasks |
1695
1698
  | Project Management | \`/project\` | Organize tasks into projects |
1696
1699
  | Memory | \`/memory\` | Search past decisions, store insights |
1700
+ | Secrets | \`/secret\` | Store, retrieve, list, delete encrypted API keys and credentials |
1697
1701
  | Environment | \`/environment\` | Check system status, troubleshoot issues |
1698
1702
  | Skill Creator | \`/skill-creator\` | Create new Claude Code skills |
1699
1703
 
@@ -1749,11 +1753,15 @@ Memory is your most important tool. You are stateless between sessions \u2014 wi
1749
1753
  **Rule: When in doubt, store it.** A redundant memory is harmless. A forgotten one wastes ${p.userName}'s time.
1750
1754
 
1751
1755
  ### Secrets & Sensitive Data
1752
- - NEVER store passwords, tokens, API keys, or credentials in memory, notes, or conversation
1753
- - NEVER write secrets to files \u2014 use the Cognova settings UI or secrets API instead
1754
- - If ${p.userName} shares a credential in chat, warn them it should be stored as a secret
1755
- - When you need a token for an integration, check the secrets API first before asking ${p.userName}
1756
+
1757
+ **CRITICAL \u2014 Zero tolerance for leaked secrets:**
1758
+ - NEVER store passwords, tokens, API keys, or credentials in memory, notes, conversation, or any file
1759
+ - NEVER write secrets to files \u2014 use \`/secret set KEY\` or the Cognova settings UI instead
1760
+ - NEVER embed API keys, tokens, or credentials in SKILL.md files or Python scripts when creating or modifying skills \u2014 always use \`get_secret()\` from \`_lib/api.py\`
1761
+ - If ${p.userName} shares a credential in chat, warn them and store it via \`/secret set KEY\` immediately
1762
+ - When you need a token for an integration, check with \`/secret list\` and \`/secret get KEY\` before asking ${p.userName}
1756
1763
  - Treat any string that looks like a key, token, or password as sensitive \u2014 do not echo it back
1764
+ - When creating skills that need external API keys, declare them in \`metadata.requires-secrets\` frontmatter and use \`get_secret()\` in the script
1757
1765
 
1758
1766
  ### Troubleshooting
1759
1767
  - Use \`/environment status\` or \`/environment health\` to diagnose issues
@@ -2205,7 +2213,7 @@ async function init() {
2205
2213
  s.start("Installing Claude Code configuration");
2206
2214
  await installClaudeConfig(config);
2207
2215
  s.stop("Claude config installed to ~/.claude/");
2208
- writeMetadata(resolvedInstallDir, vault.path, "0.1.0");
2216
+ writeMetadata(resolvedInstallDir, vault.path, getPackageVersion());
2209
2217
  R2.step(import_picocolors5.default.bold("Setup"));
2210
2218
  s.start("Installing dependencies");
2211
2219
  execSync5("pnpm install", { cwd: resolvedInstallDir, stdio: "pipe" });
@@ -2387,8 +2395,10 @@ async function update() {
2387
2395
  process.exit(1);
2388
2396
  }
2389
2397
  }
2390
- if (!updateFailed)
2398
+ if (!updateFailed) {
2399
+ writeMetadata(installDir, metadata.vaultPath, latestVersion);
2391
2400
  cleanupBackup(backupDir);
2401
+ }
2392
2402
  if (updateFailed) {
2393
2403
  Le("Update failed. Previous version has been restored. Try again later.");
2394
2404
  process.exit(1);
@@ -2612,18 +2622,8 @@ async function reset() {
2612
2622
  }
2613
2623
 
2614
2624
  // src/index.ts
2615
- var __filename2 = fileURLToPath2(import.meta.url);
2616
- var __dirname2 = dirname2(__filename2);
2617
- function getVersion() {
2618
- try {
2619
- const pkg = JSON.parse(readFileSync4(join11(__dirname2, "..", "..", "package.json"), "utf-8"));
2620
- return pkg.version || "0.0.0";
2621
- } catch {
2622
- return "0.0.0";
2623
- }
2624
- }
2625
2625
  var HELP_TEXT = `
2626
- cognova v${getVersion()} \u2014 Personal knowledge management with Claude Code
2626
+ cognova v${getPackageVersion()} \u2014 Personal knowledge management with Claude Code
2627
2627
 
2628
2628
  Usage: cognova <command> [options]
2629
2629
 
@@ -2720,7 +2720,7 @@ var args = process.argv.slice(2);
2720
2720
  var command = args.find((a) => !a.startsWith("-"));
2721
2721
  var flags = new Set(args.filter((a) => a.startsWith("-")));
2722
2722
  if (flags.has("--version") || flags.has("-v")) {
2723
- console.log(getVersion());
2723
+ console.log(getPackageVersion());
2724
2724
  process.exit(0);
2725
2725
  }
2726
2726
  if (flags.has("--help") || flags.has("-h")) {
package/nuxt.config.ts CHANGED
@@ -10,6 +10,15 @@ export default defineNuxtConfig({
10
10
  enabled: true
11
11
  },
12
12
 
13
+ app: {
14
+ head: {
15
+ link: [
16
+ { rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' },
17
+ { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
18
+ ]
19
+ }
20
+ },
21
+
13
22
  css: ['~/assets/css/main.css'],
14
23
 
15
24
  mdc: {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cognova",
3
3
  "type": "module",
4
- "version": "0.1.10",
4
+ "version": "0.2.2",
5
5
  "description": "Personal knowledge management system with Claude Code integration",
6
6
  "repository": {
7
7
  "type": "git",
@@ -0,0 +1,133 @@
1
+ import { eq, isNull, and, inArray, gte, desc, sql, ne } from 'drizzle-orm'
2
+ import { getDb, schema } from '~~/server/db'
3
+ import { requireDb } from '~~/server/utils/db-guard'
4
+ import type { DashboardOverview } from '~~/shared/types'
5
+
6
+ export default defineEventHandler(async (event) => {
7
+ requireDb(event)
8
+
9
+ const db = getDb()
10
+ const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)
11
+
12
+ const [taskCounts, upcomingTasks, recentConversations, recentDocuments, usageAgg] = await Promise.all([
13
+ // Task counts: todo + in_progress (not deleted)
14
+ db.select({
15
+ status: schema.tasks.status,
16
+ count: sql<number>`count(*)::int`
17
+ })
18
+ .from(schema.tasks)
19
+ .where(and(
20
+ isNull(schema.tasks.deletedAt),
21
+ inArray(schema.tasks.status, ['todo', 'in_progress'])
22
+ ))
23
+ .groupBy(schema.tasks.status),
24
+
25
+ // Top 5 upcoming tasks (not done, not deleted)
26
+ db.select({
27
+ id: schema.tasks.id,
28
+ title: schema.tasks.title,
29
+ status: schema.tasks.status,
30
+ priority: schema.tasks.priority,
31
+ dueDate: schema.tasks.dueDate,
32
+ projectName: schema.projects.name,
33
+ projectColor: schema.projects.color
34
+ })
35
+ .from(schema.tasks)
36
+ .leftJoin(schema.projects, eq(schema.tasks.projectId, schema.projects.id))
37
+ .where(and(
38
+ isNull(schema.tasks.deletedAt),
39
+ ne(schema.tasks.status, 'done')
40
+ ))
41
+ .orderBy(
42
+ sql`${schema.tasks.dueDate} ASC NULLS LAST`,
43
+ desc(schema.tasks.priority)
44
+ )
45
+ .limit(5),
46
+
47
+ // Last 3 conversations
48
+ db.select({
49
+ id: schema.conversations.id,
50
+ sessionId: schema.conversations.sessionId,
51
+ title: schema.conversations.title,
52
+ messageCount: schema.conversations.messageCount,
53
+ startedAt: schema.conversations.startedAt
54
+ })
55
+ .from(schema.conversations)
56
+ .orderBy(desc(schema.conversations.startedAt))
57
+ .limit(3),
58
+
59
+ // Last 3 documents (not deleted)
60
+ db.select({
61
+ id: schema.documents.id,
62
+ title: schema.documents.title,
63
+ path: schema.documents.path,
64
+ modifiedAt: schema.documents.modifiedAt,
65
+ projectName: schema.projects.name,
66
+ projectColor: schema.projects.color
67
+ })
68
+ .from(schema.documents)
69
+ .leftJoin(schema.projects, eq(schema.documents.projectId, schema.projects.id))
70
+ .where(isNull(schema.documents.deletedAt))
71
+ .orderBy(desc(schema.documents.modifiedAt))
72
+ .limit(3),
73
+
74
+ // 7-day usage summary
75
+ db.select({
76
+ totalCost: sql<number>`coalesce(sum(${schema.tokenUsage.costUsd}), 0)::real`,
77
+ totalCalls: sql<number>`count(*)::int`,
78
+ totalInputTokens: sql<number>`coalesce(sum(${schema.tokenUsage.inputTokens}), 0)::int`,
79
+ totalOutputTokens: sql<number>`coalesce(sum(${schema.tokenUsage.outputTokens}), 0)::int`
80
+ })
81
+ .from(schema.tokenUsage)
82
+ .where(gte(schema.tokenUsage.createdAt, sevenDaysAgo))
83
+ ])
84
+
85
+ // Build task counts
86
+ let todoCount = 0
87
+ let inProgressCount = 0
88
+ for (const row of taskCounts) {
89
+ if (row.status === 'todo') todoCount = row.count
90
+ else if (row.status === 'in_progress') inProgressCount = row.count
91
+ }
92
+
93
+ const usageRow = usageAgg[0]
94
+
95
+ const overview: DashboardOverview = {
96
+ tasks: {
97
+ todoCount,
98
+ inProgressCount,
99
+ upcoming: upcomingTasks.map(t => ({
100
+ id: t.id,
101
+ title: t.title,
102
+ status: t.status as DashboardOverview['tasks']['upcoming'][0]['status'],
103
+ priority: t.priority,
104
+ dueDate: t.dueDate?.toISOString() ?? null,
105
+ projectName: t.projectName ?? null,
106
+ projectColor: t.projectColor ?? null
107
+ }))
108
+ },
109
+ conversations: recentConversations.map(c => ({
110
+ id: c.id,
111
+ sessionId: c.sessionId,
112
+ title: c.title ?? null,
113
+ messageCount: c.messageCount,
114
+ startedAt: c.startedAt.toISOString()
115
+ })),
116
+ documents: recentDocuments.map(d => ({
117
+ id: d.id,
118
+ title: d.title,
119
+ path: d.path,
120
+ modifiedAt: d.modifiedAt?.toISOString() ?? null,
121
+ projectName: d.projectName ?? null,
122
+ projectColor: d.projectColor ?? null
123
+ })),
124
+ usage: {
125
+ totalCost7d: usageRow?.totalCost ?? 0,
126
+ totalCalls7d: usageRow?.totalCalls ?? 0,
127
+ totalInputTokens7d: usageRow?.totalInputTokens ?? 0,
128
+ totalOutputTokens7d: usageRow?.totalOutputTokens ?? 0
129
+ }
130
+ }
131
+
132
+ return { data: overview }
133
+ })
@@ -0,0 +1,45 @@
1
+ import { mkdir, writeFile, stat } from 'fs/promises'
2
+ import { join, normalize } from 'path'
3
+ import { getSkillsDir, getInactiveSkillsDir } from '~~/server/utils/skills-path'
4
+
5
+ export default defineEventHandler(async (event) => {
6
+ const name = getRouterParam(event, 'name')
7
+ if (!name)
8
+ throw createError({ statusCode: 400, message: 'Skill name required' })
9
+
10
+ const body = await readBody<{ path: string, type: 'file' | 'directory' }>(event)
11
+ if (!body?.path)
12
+ throw createError({ statusCode: 400, message: 'Path is required' })
13
+
14
+ const skillDir = await findSkillDir(name)
15
+ if (!skillDir)
16
+ throw createError({ statusCode: 404, message: `Skill '${name}' not found` })
17
+
18
+ const targetPath = normalize(join(skillDir, body.path))
19
+ if (!targetPath.startsWith(skillDir))
20
+ throw createError({ statusCode: 403, message: 'Path traversal not allowed' })
21
+
22
+ const existing = await stat(targetPath).catch(() => null)
23
+ if (existing)
24
+ throw createError({ statusCode: 409, message: 'Already exists' })
25
+
26
+ if (body.type === 'directory') {
27
+ await mkdir(targetPath, { recursive: true })
28
+ } else {
29
+ await writeFile(targetPath, '', 'utf-8')
30
+ }
31
+
32
+ return { data: { path: body.path, type: body.type || 'file' } }
33
+ })
34
+
35
+ async function findSkillDir(name: string): Promise<string | null> {
36
+ const activeDir = join(getSkillsDir(), name)
37
+ const activeStat = await stat(activeDir).catch(() => null)
38
+ if (activeStat?.isDirectory()) return activeDir
39
+
40
+ const inactiveDir = join(getInactiveSkillsDir(), name)
41
+ const inactiveStat = await stat(inactiveDir).catch(() => null)
42
+ if (inactiveStat?.isDirectory()) return inactiveDir
43
+
44
+ return null
45
+ }
@@ -0,0 +1,45 @@
1
+ import { rm, stat } from 'fs/promises'
2
+ import { join, normalize } from 'path'
3
+ import { getSkillsDir, getInactiveSkillsDir } from '~~/server/utils/skills-path'
4
+
5
+ export default defineEventHandler(async (event) => {
6
+ const name = getRouterParam(event, 'name')
7
+ if (!name)
8
+ throw createError({ statusCode: 400, message: 'Skill name required' })
9
+
10
+ const body = await readBody<{ path: string }>(event)
11
+ if (!body?.path)
12
+ throw createError({ statusCode: 400, message: 'Path is required' })
13
+
14
+ const skillDir = await findSkillDir(name)
15
+ if (!skillDir)
16
+ throw createError({ statusCode: 404, message: `Skill '${name}' not found` })
17
+
18
+ const targetPath = normalize(join(skillDir, body.path))
19
+ if (!targetPath.startsWith(skillDir))
20
+ throw createError({ statusCode: 403, message: 'Path traversal not allowed' })
21
+
22
+ // Prevent deleting SKILL.md
23
+ if (body.path === 'SKILL.md')
24
+ throw createError({ statusCode: 403, message: 'Cannot delete SKILL.md' })
25
+
26
+ const targetStat = await stat(targetPath).catch(() => null)
27
+ if (!targetStat)
28
+ throw createError({ statusCode: 404, message: 'File not found' })
29
+
30
+ await rm(targetPath, { recursive: true })
31
+
32
+ return { data: { path: body.path, deleted: true } }
33
+ })
34
+
35
+ async function findSkillDir(name: string): Promise<string | null> {
36
+ const activeDir = join(getSkillsDir(), name)
37
+ const activeStat = await stat(activeDir).catch(() => null)
38
+ if (activeStat?.isDirectory()) return activeDir
39
+
40
+ const inactiveDir = join(getInactiveSkillsDir(), name)
41
+ const inactiveStat = await stat(inactiveDir).catch(() => null)
42
+ if (inactiveStat?.isDirectory()) return inactiveDir
43
+
44
+ return null
45
+ }
@@ -0,0 +1,28 @@
1
+ import { stat } from 'fs/promises'
2
+ import { join } from 'path'
3
+ import { getSkillsDir, getInactiveSkillsDir, buildSkillFileTree } from '~~/server/utils/skills-path'
4
+
5
+ export default defineEventHandler(async (event) => {
6
+ const name = getRouterParam(event, 'name')
7
+ if (!name)
8
+ throw createError({ statusCode: 400, message: 'Skill name required' })
9
+
10
+ const skillDir = await findSkillDir(name)
11
+ if (!skillDir)
12
+ throw createError({ statusCode: 404, message: `Skill '${name}' not found` })
13
+
14
+ const files = await buildSkillFileTree(skillDir)
15
+ return { data: files }
16
+ })
17
+
18
+ async function findSkillDir(name: string): Promise<string | null> {
19
+ const activeDir = join(getSkillsDir(), name)
20
+ const activeStat = await stat(activeDir).catch(() => null)
21
+ if (activeStat?.isDirectory()) return activeDir
22
+
23
+ const inactiveDir = join(getInactiveSkillsDir(), name)
24
+ const inactiveStat = await stat(inactiveDir).catch(() => null)
25
+ if (inactiveStat?.isDirectory()) return inactiveDir
26
+
27
+ return null
28
+ }
@@ -0,0 +1,41 @@
1
+ import { readFile, stat } from 'fs/promises'
2
+ import { join, normalize } from 'path'
3
+ import { getSkillsDir, getInactiveSkillsDir } from '~~/server/utils/skills-path'
4
+
5
+ export default defineEventHandler(async (event) => {
6
+ const name = getRouterParam(event, 'name')
7
+ if (!name)
8
+ throw createError({ statusCode: 400, message: 'Skill name required' })
9
+
10
+ const body = await readBody<{ path: string }>(event)
11
+ if (!body?.path)
12
+ throw createError({ statusCode: 400, message: 'File path is required' })
13
+
14
+ const skillDir = await findSkillDir(name)
15
+ if (!skillDir)
16
+ throw createError({ statusCode: 404, message: `Skill '${name}' not found` })
17
+
18
+ // Prevent path traversal
19
+ const filePath = normalize(join(skillDir, body.path))
20
+ if (!filePath.startsWith(skillDir))
21
+ throw createError({ statusCode: 403, message: 'Path traversal not allowed' })
22
+
23
+ const fileStat = await stat(filePath).catch(() => null)
24
+ if (!fileStat?.isFile())
25
+ throw createError({ statusCode: 404, message: 'File not found' })
26
+
27
+ const content = await readFile(filePath, 'utf-8')
28
+ return { data: { path: body.path, content } }
29
+ })
30
+
31
+ async function findSkillDir(name: string): Promise<string | null> {
32
+ const activeDir = join(getSkillsDir(), name)
33
+ const activeStat = await stat(activeDir).catch(() => null)
34
+ if (activeStat?.isDirectory()) return activeDir
35
+
36
+ const inactiveDir = join(getInactiveSkillsDir(), name)
37
+ const inactiveStat = await stat(inactiveDir).catch(() => null)
38
+ if (inactiveStat?.isDirectory()) return inactiveDir
39
+
40
+ return null
41
+ }