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.
- package/.output/nitro.json +1 -1
- package/.output/public/_nuxt/{BLIw7q7e.js → 4MiwzAAt.js} +2 -2
- package/.output/public/_nuxt/{B8gRcrNP.js → 5ZXA0Ckq.js} +1 -1
- package/.output/public/_nuxt/9IQmsEjr.js +1 -0
- package/.output/public/_nuxt/{DlmPGBGX.js → 9QnH0xQM.js} +1 -1
- package/.output/public/_nuxt/{Qi4Arc25.js → B1X4Bzcy.js} +1 -1
- package/.output/public/_nuxt/{kuWCWJ92.js → B3y_Qqox.js} +1 -1
- package/.output/public/_nuxt/{CJVO4EWp.js → B6S_ob86.js} +1 -1
- package/.output/public/_nuxt/{xdp-pGVh.js → BAIz-dEB.js} +1 -1
- package/.output/public/_nuxt/{DIF41aNz.js → BCtfQCzC.js} +1 -1
- package/.output/public/_nuxt/BIIJhjQO.js +1 -0
- package/.output/public/_nuxt/{QOhiCNXN.js → BIckl6wA.js} +1 -1
- package/.output/public/_nuxt/{CqsT2LL7.js → BJ3o57WW.js} +1 -1
- package/.output/public/_nuxt/BNetzZzF.js +1 -0
- package/.output/public/_nuxt/{C5jkgqcy.js → BOj6t-oo.js} +1 -1
- package/.output/public/_nuxt/BS0ofHJK.js +1 -0
- package/.output/public/_nuxt/{CdKHIoKZ.js → BWhMnjID.js} +1 -1
- package/.output/public/_nuxt/{BUjpjVNI.js → BYjadNrw.js} +1 -1
- package/.output/public/_nuxt/{DLkyutNs.js → BZXMQuYP.js} +1 -1
- package/.output/public/_nuxt/B_3_hrpn.js +1 -0
- package/.output/public/_nuxt/BaBZjmMC.js +1 -0
- package/.output/public/_nuxt/BaMqDm5u.js +1 -0
- package/.output/public/_nuxt/{DwSbu8wg.js → BasgsT_S.js} +1 -1
- package/.output/public/_nuxt/{B4nb4eka.js → BffWCM73.js} +1 -1
- package/.output/public/_nuxt/{BxaDmjPM.js → BhzMoffi.js} +1 -1
- package/.output/public/_nuxt/{DjzaeRs8.js → BjjCvHLT.js} +1 -1
- package/.output/public/_nuxt/{CtJTw_81.js → BlAZO7nq.js} +1 -1
- package/.output/public/_nuxt/{C6nLQFo5.js → BlhFigLL.js} +1 -1
- package/.output/public/_nuxt/{CiJs9fhO.js → Bm9LyG4F.js} +1 -1
- package/.output/public/_nuxt/BoIxv-gM.js +1 -0
- package/.output/public/_nuxt/{C8xYfk_L.js → BqKFIuRj.js} +1 -1
- package/.output/public/_nuxt/Br19oYkq.js +1 -0
- package/.output/public/_nuxt/{htEHZODo.js → BrNqhp1a.js} +3 -3
- package/.output/public/_nuxt/{DFKQfGt8.js → BsEZoHd1.js} +1 -1
- package/.output/public/_nuxt/{CmF_IsFj.js → BxXOsXrM.js} +3 -3
- package/.output/public/_nuxt/{q7M6chAB.js → BzOqrmGa.js} +3 -3
- package/.output/public/_nuxt/{DT_NtxcV.js → C0JKNMDO.js} +1 -1
- package/.output/public/_nuxt/{BOb3QiPL.js → C0kh_F7v.js} +1 -1
- package/.output/public/_nuxt/{D472oOJg.js → C3FxIITy.js} +1 -1
- package/.output/public/_nuxt/{Bh7WRSzZ.js → C4pxqdgg.js} +1 -1
- package/.output/public/_nuxt/{ByWk4RVe.js → C61KgSco.js} +1 -1
- package/.output/public/_nuxt/{TjI_tk0y.js → C69W7k2j.js} +1 -1
- package/.output/public/_nuxt/{uqOLgWHD.js → C6RC3lA1.js} +1 -1
- package/.output/public/_nuxt/{DvA5meSv.js → C6aqGHu1.js} +1 -1
- package/.output/public/_nuxt/{CQM_5Ht0.js → CJUdYEdO.js} +1 -1
- package/.output/public/_nuxt/{BzWvPP8W.js → CKkC3Ptm.js} +1 -1
- package/.output/public/_nuxt/CNnJrDvu.js +1 -0
- package/.output/public/_nuxt/{Bf2SSE6Q.js → CVJQGP1Q.js} +1 -1
- package/.output/public/_nuxt/{0p0wrrYN.js → CVgTJeSq.js} +1 -1
- package/.output/public/_nuxt/CWMUi89H.js +1 -0
- package/.output/public/_nuxt/{DUYrX_jl.js → CYP_MLH8.js} +1 -1
- package/.output/public/_nuxt/{DEDt4pMc.js → C_BdYLzz.js} +1 -1
- package/.output/public/_nuxt/{CU3_Ewk2.js → Cdt3I3Go.js} +1 -1
- package/.output/public/_nuxt/{DX5y-Dpt.js → Cdu2qGgq.js} +1 -1
- package/.output/public/_nuxt/{DQunOghN.js → CeIVm4A3.js} +1 -1
- package/.output/public/_nuxt/CeIu7z4p.js +1 -0
- package/.output/public/_nuxt/{B73gh0JS.js → Ci7UEZQh.js} +1 -1
- package/.output/public/_nuxt/{C7NKjT6R.js → CihWZmJe.js} +2 -2
- package/.output/public/_nuxt/{902fefRh.js → CitkKxhw.js} +1 -1
- package/.output/public/_nuxt/{DH_IrAcJ.js → CmzH6R-N.js} +2 -2
- package/.output/public/_nuxt/{CgPM54k_.js → Cp2MA0cm.js} +1 -1
- package/.output/public/_nuxt/{hfaTbWV_.js → CqU2XbzO.js} +1 -1
- package/.output/public/_nuxt/{CuxiR1jN.js → Cqy_L_ip.js} +1 -1
- package/.output/public/_nuxt/{BFmbqknL.js → Cr8ixbr1.js} +1 -1
- package/.output/public/_nuxt/{BkiRMhMm.js → CsJ9KhQ4.js} +1 -1
- package/.output/public/_nuxt/{Dhvv7eaa.js → CtYFj7k1.js} +1 -1
- package/.output/public/_nuxt/{DG0VCYsy.js → CtchsY6e.js} +1 -1
- package/.output/public/_nuxt/{pqmDf_cQ.js → Cvp7FI3T.js} +1 -1
- package/.output/public/_nuxt/{38G-HXi8.js → CxuZBC8n.js} +5 -5
- package/.output/public/_nuxt/D2689qk4.js +1 -0
- package/.output/public/_nuxt/{Dw5YM8ji.js → D31L7Ks6.js} +1 -1
- package/.output/public/_nuxt/{DYTkcgRd.js → D3RiUGdz.js} +1 -1
- package/.output/public/_nuxt/{Csa19Lyu.js → D3e44mCL.js} +1 -1
- package/.output/public/_nuxt/{DmXY_FiQ.js → D6t3dcTl.js} +1 -1
- package/.output/public/_nuxt/{ChNxcBlH.js → DAQhmSv4.js} +1 -1
- package/.output/public/_nuxt/{BFtDSgo7.js → DB359q8R.js} +1 -1
- package/.output/public/_nuxt/{CrUNulhU.js → DCzfkCGa.js} +1 -1
- package/.output/public/_nuxt/{Cn1H4f01.js → DG-T44jj.js} +1 -1
- package/.output/public/_nuxt/{CAGIVdiL.js → DGX0tzL8.js} +1 -1
- package/.output/public/_nuxt/DHG66LPS.js +1 -0
- package/.output/public/_nuxt/{BV0QyLX9.js → DHKLCQRG.js} +1 -1
- package/.output/public/_nuxt/{DFs-kxzi.js → DIoI0uJm.js} +1 -1
- package/.output/public/_nuxt/{koWn-xAZ.js → DJ5V-y_x.js} +1 -1
- package/.output/public/_nuxt/{HUnwr3r4.js → DJMS2og1.js} +1 -1
- package/.output/public/_nuxt/{CjfarCg3.js → DJjDvbZE.js} +1 -1
- package/.output/public/_nuxt/DK9jxJ0g.js +1 -0
- package/.output/public/_nuxt/{BjN5vE4c.js → DKZxeXDQ.js} +1 -1
- package/.output/public/_nuxt/{CcWl1F-m.js → DLETdGFL.js} +1 -1
- package/.output/public/_nuxt/DOICd-Ld.js +1 -0
- package/.output/public/_nuxt/{bX88p2Mb.js → DPEcH-gi.js} +3 -3
- package/.output/public/_nuxt/{DDqY0xzR.js → DSRrg8JT.js} +1 -1
- package/.output/public/_nuxt/{B67bSHkr.js → DTDgHTuh.js} +2 -2
- package/.output/public/_nuxt/{BYzrgyQu.js → D_3Rq1BS.js} +1 -1
- package/.output/public/_nuxt/{TQO0uTDM.js → Daz4MeL6.js} +1 -1
- package/.output/public/_nuxt/{DxkqpACx.js → Db2v8O7O.js} +1 -1
- package/.output/public/_nuxt/{CHa8PuBL.js → Db8-_gO7.js} +1 -1
- package/.output/public/_nuxt/{BUEnkhcM.js → DfQu3kEw.js} +1 -1
- package/.output/public/_nuxt/{hDpBJf9-.js → DgV-EDJ9.js} +1 -1
- package/.output/public/_nuxt/{D1CNUSvv.js → DnjGH3SQ.js} +1 -1
- package/.output/public/_nuxt/Dp2X5R2m.js +1 -0
- package/.output/public/_nuxt/DqB723Z0.js +1 -0
- package/.output/public/_nuxt/{DcIiuvW-.js → DyRelyfs.js} +1 -1
- package/.output/public/_nuxt/{sgWRawF5.js → Dy_Cq5LQ.js} +1 -1
- package/.output/public/_nuxt/{B4T7pJz5.js → DznawRFW.js} +1 -1
- package/.output/public/_nuxt/EuOqK1A6.js +1 -0
- package/.output/public/_nuxt/{Dn4PGlE2.js → FNC8XZTk.js} +1 -1
- package/.output/public/_nuxt/{ZToTf_46.js → Fukkqjkf.js} +1 -1
- package/.output/public/_nuxt/{D5aulRKh.js → FvlxxmNk.js} +2 -2
- package/.output/public/_nuxt/{C2IkB9Vg.js → IRSbVPIu.js} +1 -1
- package/.output/public/_nuxt/{QESaAMMF.js → JJ3634gV.js} +1 -1
- package/.output/public/_nuxt/{DGUr7xQ7.js → Jez9DHn7.js} +1 -1
- package/.output/public/_nuxt/{DPdR3tu5.js → KKK6HVeG.js} +1 -1
- package/.output/public/_nuxt/{D0bh5qKf.js → Lwdv_RKd.js} +1 -1
- package/.output/public/_nuxt/{BkRZ4-cg.js → Nb2jBtYT.js} +1 -1
- package/.output/public/_nuxt/Q8Ps7oN5.js +1 -0
- package/.output/public/_nuxt/SXTDhzp6.js +1 -0
- package/.output/public/_nuxt/{GC9cY_vb.js → Sg2Lwc46.js} +1 -1
- package/.output/public/_nuxt/{GI6TTFJt.js → YuTZB7sD.js} +1 -1
- package/.output/public/_nuxt/{D2_L0Ze1.js → _CYZi8HN.js} +1 -1
- package/.output/public/_nuxt/_J_7XIn-.js +1 -0
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/a1e9100c-1a4f-4f7e-bb53-9dbe0d07effb.json +1 -0
- package/.output/public/_nuxt/{DZNG1AbU.js → cABRLVee.js} +1 -1
- package/.output/public/_nuxt/eko-0FUm.js +1 -0
- package/.output/public/_nuxt/entry.CGxIBGAf.css +1 -0
- package/.output/public/_nuxt/{BIjt_cIY.js → g20UHUCv.js} +1 -1
- package/.output/public/_nuxt/{HIlTvc-2.js → gBC9k4Qj.js} +1 -1
- package/.output/public/_nuxt/{IQoLFBX8.js → ghuJ76mD.js} +1 -1
- package/.output/public/_nuxt/{BGYWH_3A.js → iBGCpHdw.js} +2 -2
- package/.output/public/_nuxt/{uUGV1V-6.js → inmzPrjz.js} +1 -1
- package/.output/public/_nuxt/{CgHipF8Q.js → m5kGCDpI.js} +1 -1
- package/.output/public/_nuxt/{Bj0OFhNx.js → nIU2F7ia.js} +3 -3
- package/.output/public/_nuxt/{Dcv_tyzb.js → oIX-ZDN6.js} +1 -1
- package/.output/public/_nuxt/{B7NSgclV.js → pcUI-zuY.js} +1 -1
- package/.output/public/_nuxt/{DpF6VoC4.js → sf57orEk.js} +1 -1
- package/.output/public/_nuxt/{DS_mF4Ol.js → wCGVE8_e.js} +1 -1
- package/.output/public/_nuxt/{KAzeBbaL.js → xuzLdW-o.js} +1 -1
- package/.output/public/_nuxt/{ytKsJZlj.js → yNrp2XvX.js} +1 -1
- package/.output/public/_nuxt/{B3mdl0Ae.js → yuf23kh9.js} +1 -1
- package/.output/server/chunks/build/{_uuid_-DAZ5CM0C.mjs → _uuid_-0UgdUhfY.mjs} +6 -5
- package/.output/server/chunks/build/{_uuid_-DAZ5CM0C.mjs.map → _uuid_-0UgdUhfY.mjs.map} +1 -1
- package/.output/server/chunks/build/chat-CZMiB68R.mjs.map +1 -1
- package/.output/server/chunks/build/client.precomputed.mjs +1 -1
- package/.output/server/chunks/build/dashboard-YEscLBQN.mjs +1109 -0
- package/.output/server/chunks/build/dashboard-YEscLBQN.mjs.map +1 -0
- package/.output/server/chunks/build/{docs-CFAI1clt.mjs → docs-Dk2JnYq3.mjs} +6 -5
- package/.output/server/chunks/build/{docs-CFAI1clt.mjs.map → docs-Dk2JnYq3.mjs.map} +1 -1
- package/.output/server/chunks/build/server.mjs +3 -3
- package/.output/server/chunks/build/settings-DdkKCJ00.mjs +1 -1
- package/.output/server/chunks/build/styles.mjs +4 -4
- package/.output/server/chunks/build/{CodeEditorFallback-DfYly7f5.mjs → useCopyToClipboard-vi6FYyyZ.mjs} +29 -2
- package/.output/server/chunks/build/useCopyToClipboard-vi6FYyyZ.mjs.map +1 -0
- package/.output/server/chunks/build/useNotificationBus-BG5JNQf1.mjs +1 -1
- package/.output/server/chunks/nitro/nitro.mjs +819 -824
- package/.output/server/chunks/routes/api/dashboard/overview.get.mjs +149 -0
- package/.output/server/chunks/routes/api/dashboard/overview.get.mjs.map +1 -0
- package/.output/server/chunks/routes/api/documents/_id/public.get.mjs +1 -1
- package/.output/server/chunks/routes/api/documents/_id/restore.post.mjs +1 -1
- package/.output/server/chunks/routes/api/documents/by-path.post.mjs +1 -1
- package/.output/server/chunks/routes/api/documents/index.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/documents/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/fs/delete.post.mjs +1 -1
- package/.output/server/chunks/routes/api/fs/list.get.mjs +1 -1
- package/.output/server/chunks/routes/api/fs/mkdir.post.mjs +1 -1
- package/.output/server/chunks/routes/api/fs/move.post.mjs +1 -1
- package/.output/server/chunks/routes/api/fs/read.post.mjs +1 -1
- package/.output/server/chunks/routes/api/fs/rename.post.mjs +1 -1
- package/.output/server/chunks/routes/api/fs/write.post.mjs +1 -1
- package/.output/server/chunks/routes/api/health.get.mjs +1 -1
- package/.output/server/chunks/routes/api/home.get.mjs +1 -1
- package/.output/server/chunks/routes/api/hooks/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/hooks/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/hooks/stats.get.mjs +1 -1
- package/.output/server/chunks/routes/api/index.get3.mjs +1 -1
- package/.output/server/chunks/routes/api/index.get4.mjs +1 -1
- package/.output/server/chunks/routes/api/index.get5.mjs +1 -1
- package/.output/server/chunks/routes/api/index.get6.mjs +1 -1
- package/.output/server/chunks/routes/api/index.get7.mjs +1 -1
- package/.output/server/chunks/routes/api/index.get8.mjs +1 -1
- package/.output/server/chunks/routes/api/index.post2.mjs +1 -1
- package/.output/server/chunks/routes/api/index.post3.mjs +1 -1
- package/.output/server/chunks/routes/api/index.post4.mjs +1 -1
- package/.output/server/chunks/routes/api/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/memory/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/memory/context.get.mjs +1 -1
- package/.output/server/chunks/routes/api/memory/extract.post.mjs +1 -1
- package/.output/server/chunks/routes/api/memory/search.get.mjs +1 -1
- package/.output/server/chunks/routes/api/memory/store.post.mjs +1 -1
- package/.output/server/chunks/routes/api/projects/index.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/projects/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/projects/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/secrets/_key_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/secrets/_key_.get.mjs +1 -1
- package/.output/server/chunks/routes/api/secrets/_key_.put.mjs +1 -1
- package/.output/server/chunks/routes/api/tasks/_id/restore.post.mjs +1 -1
- package/.output/server/chunks/routes/api/tasks/index.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/tasks/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/tasks/tags.get.mjs +1 -1
- package/.output/server/chunks/routes/api/usage/stats.get.mjs +1 -1
- package/.output/server/chunks/routes/renderer.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/Claude/CLAUDE.md +9 -4
- package/Claude/skills/environment/SKILL.md +6 -0
- package/Claude/skills/memory/SKILL.md +6 -0
- package/Claude/skills/project/SKILL.md +6 -0
- package/Claude/skills/secret/SKILL.md +85 -0
- package/Claude/skills/secret/secret.py +146 -0
- package/Claude/skills/skill-creator/SKILL.md +30 -0
- package/Claude/skills/task/SKILL.md +6 -0
- package/app/components/dashboard/RecentChats.vue +93 -0
- package/app/components/dashboard/RecentDocs.vue +108 -0
- package/app/components/dashboard/StatCards.vue +82 -0
- package/app/components/dashboard/UpcomingTasks.vue +119 -0
- package/app/components/dashboard/UsageSummary.vue +94 -0
- package/app/components/editor/DocumentEditor.vue +5 -3
- package/app/components/skills/Card.vue +82 -0
- package/app/components/skills/CreateModal.vue +156 -0
- package/app/components/skills/Editor.vue +135 -0
- package/app/components/skills/FileTree.vue +336 -0
- package/app/components/skills/LibraryCard.vue +122 -0
- package/app/components/skills/RenameModal.vue +84 -0
- package/app/composables/useCopyToClipboard.ts +37 -0
- package/app/layouts/dashboard.vue +7 -0
- package/app/pages/chat.vue +11 -1
- package/app/pages/dashboard.vue +65 -64
- package/app/pages/skills/[name].vue +198 -0
- package/app/pages/skills/index.vue +157 -0
- package/app/pages/skills/library.vue +209 -0
- package/app/pages/view/[uuid].vue +4 -3
- package/dist/cli/index.js +23 -23
- package/nuxt.config.ts +9 -0
- package/package.json +1 -1
- package/server/api/dashboard/overview.get.ts +133 -0
- package/server/api/skills/[name]/files/create.post.ts +45 -0
- package/server/api/skills/[name]/files/delete.post.ts +45 -0
- package/server/api/skills/[name]/files/index.get.ts +28 -0
- package/server/api/skills/[name]/files/read.post.ts +41 -0
- package/server/api/skills/[name]/files/write.post.ts +42 -0
- package/server/api/skills/[name]/index.get.ts +54 -0
- package/server/api/skills/[name]/rename.post.ts +64 -0
- package/server/api/skills/[name]/toggle.post.ts +32 -0
- package/server/api/skills/create.post.ts +51 -0
- package/server/api/skills/generate.post.ts +126 -0
- package/server/api/skills/index.get.ts +57 -0
- package/server/api/skills/library/check-updates.get.ts +46 -0
- package/server/api/skills/library/index.get.ts +56 -0
- package/server/api/skills/library/install.post.ts +73 -0
- package/server/db/schema.ts +17 -0
- package/server/drizzle/migrations/0012_good_deadpool.sql +12 -0
- package/server/drizzle/migrations/0013_swift_snowbird.sql +1 -0
- package/server/drizzle/migrations/meta/0012_snapshot.json +1713 -0
- package/server/drizzle/migrations/meta/0013_snapshot.json +1720 -0
- package/server/drizzle/migrations/meta/_journal.json +14 -0
- package/server/middleware/auth.ts +0 -1
- package/server/plugins/05.skills-catalog.ts +105 -0
- package/server/utils/skills-path.ts +197 -0
- package/shared/types/index.ts +102 -0
- package/.output/public/_nuxt/BAWOIJ__.js +0 -1
- package/.output/public/_nuxt/B_paJhyq.js +0 -1
- package/.output/public/_nuxt/BdYiqlvd.js +0 -1
- package/.output/public/_nuxt/BpiqBbAF.js +0 -1
- package/.output/public/_nuxt/BqDfJ2hd.js +0 -1
- package/.output/public/_nuxt/BxgB0-mq.js +0 -1
- package/.output/public/_nuxt/C1hs5K_e.js +0 -1
- package/.output/public/_nuxt/C4Ncdd9K.js +0 -1
- package/.output/public/_nuxt/CAQ2bGHm.js +0 -1
- package/.output/public/_nuxt/CCpQAJpz.js +0 -1
- package/.output/public/_nuxt/CXXD-owe.js +0 -1
- package/.output/public/_nuxt/CcRijyCZ.js +0 -1
- package/.output/public/_nuxt/CoEfPCKj.js +0 -1
- package/.output/public/_nuxt/D08h2e32.js +0 -1
- package/.output/public/_nuxt/D_t_j6EB.js +0 -1
- package/.output/public/_nuxt/DaZy6Ki2.js +0 -1
- package/.output/public/_nuxt/DdrudNqf.js +0 -1
- package/.output/public/_nuxt/DdtmJxeb.js +0 -1
- package/.output/public/_nuxt/Dgv5fJ6Q.js +0 -1
- package/.output/public/_nuxt/DhIluGHO.js +0 -1
- package/.output/public/_nuxt/DuRk5Dc_.js +0 -1
- package/.output/public/_nuxt/VFwvl1mU.js +0 -1
- package/.output/public/_nuxt/builds/meta/555978fa-172f-4d0d-8238-88f8b610f712.json +0 -1
- package/.output/public/_nuxt/entry.DPbNaeQT.css +0 -1
- package/.output/public/_nuxt/hZV7Z8Ig.js +0 -1
- package/.output/public/_nuxt/lK0inMQ6.js +0 -1
- package/.output/server/chunks/build/CodeEditorFallback-DfYly7f5.mjs.map +0 -1
- package/.output/server/chunks/build/dashboard-DtbGchTa.mjs +0 -277
- package/.output/server/chunks/build/dashboard-DtbGchTa.mjs.map +0 -1
- /package/.output/public/_nuxt/{language-detection.Be_IvFWy.css → useCopyToClipboard.Be_IvFWy.css} +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: secret
|
|
3
|
+
description: Manage encrypted secrets for API keys, tokens, and credentials. Use when the user needs to store, retrieve, list, or delete sensitive values.
|
|
4
|
+
allowed-tools: Bash, Read
|
|
5
|
+
metadata:
|
|
6
|
+
version: "1.0.0"
|
|
7
|
+
requires-secrets: []
|
|
8
|
+
author: Cognova
|
|
9
|
+
repository: ""
|
|
10
|
+
installed-from: ""
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Secrets Management Skill
|
|
14
|
+
|
|
15
|
+
Manage encrypted secrets stored in Cognova. Use this for API keys, tokens, webhook URLs, and any sensitive credentials that skills or integrations need.
|
|
16
|
+
|
|
17
|
+
## Commands
|
|
18
|
+
|
|
19
|
+
### List all secrets
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
python3 ~/.claude/skills/secret/secret.py list
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Shows all stored secret keys with descriptions and last updated timestamps. Values are NOT shown.
|
|
26
|
+
|
|
27
|
+
### Get a secret value
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
python3 ~/.claude/skills/secret/secret.py get <KEY>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Retrieves the decrypted value of a secret. **Do NOT echo the value back to the user** — confirm it was retrieved successfully without displaying it.
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
```bash
|
|
37
|
+
python3 ~/.claude/skills/secret/secret.py get OPENAI_API_KEY
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Store a secret
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
python3 ~/.claude/skills/secret/secret.py set <KEY> --value <VALUE> [--description <DESC>]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Creates or updates an encrypted secret. Key names should use `SCREAMING_SNAKE_CASE`.
|
|
47
|
+
|
|
48
|
+
Examples:
|
|
49
|
+
```bash
|
|
50
|
+
python3 ~/.claude/skills/secret/secret.py set DISCORD_WEBHOOK_URL --value "https://discord.com/api/webhooks/..." --description "Discord notifications webhook"
|
|
51
|
+
python3 ~/.claude/skills/secret/secret.py set OPENAI_API_KEY --value "sk-..." --description "OpenAI API key for GPT integration"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Delete a secret
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
python3 ~/.claude/skills/secret/secret.py delete <KEY>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Permanently removes an encrypted secret.
|
|
61
|
+
|
|
62
|
+
## Natural Language Patterns
|
|
63
|
+
|
|
64
|
+
When users say things like:
|
|
65
|
+
- "Store this API key..." -> Use `set`
|
|
66
|
+
- "Save my token for..." -> Use `set`
|
|
67
|
+
- "What secrets do I have?" -> Use `list`
|
|
68
|
+
- "Get my Discord webhook" -> Use `get`
|
|
69
|
+
- "Remove the old API key" -> Use `delete`
|
|
70
|
+
- "I need to add a key for..." -> Use `set`
|
|
71
|
+
|
|
72
|
+
## Key Naming Convention
|
|
73
|
+
|
|
74
|
+
Use `SCREAMING_SNAKE_CASE` for all secret keys:
|
|
75
|
+
- `GOOGLE_API_KEY`
|
|
76
|
+
- `DISCORD_WEBHOOK_URL`
|
|
77
|
+
- `OPENAI_API_KEY`
|
|
78
|
+
- `GITHUB_TOKEN`
|
|
79
|
+
|
|
80
|
+
## Security Rules
|
|
81
|
+
|
|
82
|
+
1. **Never echo secret values** — After `get`, confirm success without displaying the value
|
|
83
|
+
2. **Never store secrets elsewhere** — No memory, no notes, no files, no conversation logs
|
|
84
|
+
3. **Always use this skill** — When a user provides a credential, store it here immediately
|
|
85
|
+
4. **Warn on exposure** — If a user pastes a key/token in chat, warn them and offer to store it
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Manage encrypted secrets in Cognova."""
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
# Add parent directory for _lib imports
|
|
9
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
10
|
+
from _lib.api import get, post, put, delete, get_secret
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def cmd_list(args):
|
|
14
|
+
"""List all stored secrets."""
|
|
15
|
+
success, data = get("/secrets")
|
|
16
|
+
if not success:
|
|
17
|
+
print(f"Error: {data}")
|
|
18
|
+
sys.exit(1)
|
|
19
|
+
|
|
20
|
+
secrets = data if isinstance(data, list) else []
|
|
21
|
+
if not secrets:
|
|
22
|
+
print("No secrets stored.")
|
|
23
|
+
print("\nUse: python3 secret.py set KEY --value VALUE")
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
# Calculate column widths
|
|
27
|
+
key_width = max(len(s.get("key", "")) for s in secrets)
|
|
28
|
+
key_width = max(key_width, 3)
|
|
29
|
+
|
|
30
|
+
print(f"{'Key':<{key_width}} {'Description':<40} {'Updated'}")
|
|
31
|
+
print(f"{'-' * key_width} {'-' * 40} {'-' * 19}")
|
|
32
|
+
|
|
33
|
+
for s in secrets:
|
|
34
|
+
key = s.get("key", "")
|
|
35
|
+
desc = s.get("description", "") or ""
|
|
36
|
+
updated = s.get("updatedAt", s.get("createdAt", ""))[:19] if s.get("updatedAt") or s.get("createdAt") else ""
|
|
37
|
+
# Truncate description if too long
|
|
38
|
+
if len(desc) > 40:
|
|
39
|
+
desc = desc[:37] + "..."
|
|
40
|
+
print(f"{key:<{key_width}} {desc:<40} {updated}")
|
|
41
|
+
|
|
42
|
+
print(f"\n{len(secrets)} secret(s) stored.")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def cmd_get(args):
|
|
46
|
+
"""Get a secret value."""
|
|
47
|
+
success, value = get_secret(args.key)
|
|
48
|
+
if not success:
|
|
49
|
+
print(f"Error: {value}")
|
|
50
|
+
print(f"\nSecret '{args.key}' not found. Use 'list' to see available secrets.")
|
|
51
|
+
sys.exit(1)
|
|
52
|
+
|
|
53
|
+
if args.raw:
|
|
54
|
+
# Raw output for piping to other commands
|
|
55
|
+
print(value, end="")
|
|
56
|
+
else:
|
|
57
|
+
print(f"Secret '{args.key}' retrieved successfully.")
|
|
58
|
+
print(f"Value: {value}")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def cmd_set(args):
|
|
62
|
+
"""Create or update a secret."""
|
|
63
|
+
if not args.value:
|
|
64
|
+
print("Error: --value is required")
|
|
65
|
+
sys.exit(1)
|
|
66
|
+
|
|
67
|
+
data = {
|
|
68
|
+
"key": args.key,
|
|
69
|
+
"value": args.value,
|
|
70
|
+
}
|
|
71
|
+
if args.description:
|
|
72
|
+
data["description"] = args.description
|
|
73
|
+
|
|
74
|
+
# Try to check if it exists first
|
|
75
|
+
check_success, _ = get_secret(args.key)
|
|
76
|
+
|
|
77
|
+
if check_success:
|
|
78
|
+
# Update existing
|
|
79
|
+
success, result = put(f"/secrets/{args.key}", data)
|
|
80
|
+
action = "updated"
|
|
81
|
+
else:
|
|
82
|
+
# Create new
|
|
83
|
+
success, result = post("/secrets", data)
|
|
84
|
+
action = "created"
|
|
85
|
+
|
|
86
|
+
if not success:
|
|
87
|
+
print(f"Error: {result}")
|
|
88
|
+
sys.exit(1)
|
|
89
|
+
|
|
90
|
+
print(f"Secret '{args.key}' {action} successfully.")
|
|
91
|
+
if args.description:
|
|
92
|
+
print(f"Description: {args.description}")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def cmd_delete(args):
|
|
96
|
+
"""Delete a secret."""
|
|
97
|
+
success, result = delete(f"/secrets/{args.key}")
|
|
98
|
+
if not success:
|
|
99
|
+
print(f"Error: {result}")
|
|
100
|
+
sys.exit(1)
|
|
101
|
+
|
|
102
|
+
print(f"Secret '{args.key}' deleted successfully.")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def main():
|
|
106
|
+
parser = argparse.ArgumentParser(
|
|
107
|
+
description="Manage encrypted secrets in Cognova"
|
|
108
|
+
)
|
|
109
|
+
subparsers = parser.add_subparsers(dest="command", help="Command to run")
|
|
110
|
+
|
|
111
|
+
# list
|
|
112
|
+
subparsers.add_parser("list", help="List all secrets")
|
|
113
|
+
|
|
114
|
+
# get
|
|
115
|
+
get_parser = subparsers.add_parser("get", help="Get a secret value")
|
|
116
|
+
get_parser.add_argument("key", help="Secret key name")
|
|
117
|
+
get_parser.add_argument("--raw", action="store_true", help="Output raw value only")
|
|
118
|
+
|
|
119
|
+
# set
|
|
120
|
+
set_parser = subparsers.add_parser("set", help="Create or update a secret")
|
|
121
|
+
set_parser.add_argument("key", help="Secret key name (SCREAMING_SNAKE_CASE)")
|
|
122
|
+
set_parser.add_argument("--value", required=True, help="Secret value")
|
|
123
|
+
set_parser.add_argument("--description", help="Description of what this secret is for")
|
|
124
|
+
|
|
125
|
+
# delete
|
|
126
|
+
del_parser = subparsers.add_parser("delete", help="Delete a secret")
|
|
127
|
+
del_parser.add_argument("key", help="Secret key name")
|
|
128
|
+
|
|
129
|
+
args = parser.parse_args()
|
|
130
|
+
|
|
131
|
+
if not args.command:
|
|
132
|
+
parser.print_help()
|
|
133
|
+
sys.exit(1)
|
|
134
|
+
|
|
135
|
+
commands = {
|
|
136
|
+
"list": cmd_list,
|
|
137
|
+
"get": cmd_get,
|
|
138
|
+
"set": cmd_set,
|
|
139
|
+
"delete": cmd_delete,
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
commands[args.command](args)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
if __name__ == "__main__":
|
|
146
|
+
main()
|
|
@@ -3,6 +3,12 @@ name: skill-creator
|
|
|
3
3
|
description: Assists users in creating new Claude Code skills. Uses web search to find latest conventions and suggests alternative approaches (existing MCPs, plugins) when appropriate.
|
|
4
4
|
allowed-tools: Bash, Read, Write, WebSearch, WebFetch
|
|
5
5
|
disable-model-invocation: true
|
|
6
|
+
metadata:
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
requires-secrets: []
|
|
9
|
+
author: Cognova
|
|
10
|
+
repository: ""
|
|
11
|
+
installed-from: ""
|
|
6
12
|
---
|
|
7
13
|
|
|
8
14
|
# Skill Creator
|
|
@@ -11,6 +17,8 @@ Assists in creating new Claude Code skills for Cognova or personal use.
|
|
|
11
17
|
|
|
12
18
|
## Process
|
|
13
19
|
|
|
20
|
+
> **CRITICAL: Skills must NEVER contain hardcoded secrets.** Always use `get_secret()` from `_lib/api.py` for API keys, tokens, and credentials. If the user provides a key/token during skill creation, store it via `/secret set KEY` first, then reference it by key name in the script. Declare required secrets in `metadata.requires-secrets` frontmatter.
|
|
21
|
+
|
|
14
22
|
When a user asks to create a new skill:
|
|
15
23
|
|
|
16
24
|
### 1. Understand the Requirement
|
|
@@ -193,6 +201,28 @@ my_var = os.environ.get('MY_CUSTOM_VAR', 'default')
|
|
|
193
201
|
| `user-invocable` | false | Claude-only, hidden from user menu |
|
|
194
202
|
| `context` | fork | Run in subagent |
|
|
195
203
|
| `agent` | Explore, Plan, general-purpose | Agent type for subagent |
|
|
204
|
+
| `metadata` | object | Custom metadata (version, author, etc.) |
|
|
205
|
+
|
|
206
|
+
#### Metadata Fields
|
|
207
|
+
|
|
208
|
+
Nest under `metadata:` in frontmatter:
|
|
209
|
+
|
|
210
|
+
```yaml
|
|
211
|
+
metadata:
|
|
212
|
+
version: "1.0.0"
|
|
213
|
+
requires-secrets: ["API_KEY_NAME"]
|
|
214
|
+
author: Your Name
|
|
215
|
+
repository: ""
|
|
216
|
+
installed-from: ""
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
| Field | Type | Description |
|
|
220
|
+
|-------|------|-------------|
|
|
221
|
+
| `version` | string | Semantic version |
|
|
222
|
+
| `requires-secrets` | string[] | Secret keys the skill needs (fetched via `get_secret()`) |
|
|
223
|
+
| `author` | string | Skill author name |
|
|
224
|
+
| `repository` | string | Source repository URL |
|
|
225
|
+
| `installed-from` | string | Library name if installed from community |
|
|
196
226
|
|
|
197
227
|
### 7. Skill Locations
|
|
198
228
|
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
name: task
|
|
3
3
|
description: Manage Cognova tasks - create, list, update, complete tasks. Use when the user wants to track work items, todos, or action items. Can search for and associate tasks with projects.
|
|
4
4
|
allowed-tools: Bash, Read
|
|
5
|
+
metadata:
|
|
6
|
+
version: "1.0.0"
|
|
7
|
+
requires-secrets: []
|
|
8
|
+
author: Cognova
|
|
9
|
+
repository: ""
|
|
10
|
+
installed-from: ""
|
|
5
11
|
---
|
|
6
12
|
|
|
7
13
|
# Task Management Skill
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { formatDistanceToNow } from 'date-fns'
|
|
3
|
+
import type { DashboardOverview } from '~~/shared/types'
|
|
4
|
+
|
|
5
|
+
defineProps<{
|
|
6
|
+
conversations: DashboardOverview['conversations']
|
|
7
|
+
loading?: boolean
|
|
8
|
+
}>()
|
|
9
|
+
|
|
10
|
+
function relativeTime(iso: string): string {
|
|
11
|
+
return formatDistanceToNow(new Date(iso), { addSuffix: true })
|
|
12
|
+
}
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<UCard>
|
|
17
|
+
<template #header>
|
|
18
|
+
<div class="flex items-center justify-between">
|
|
19
|
+
<div class="flex items-center gap-2">
|
|
20
|
+
<UIcon
|
|
21
|
+
name="i-lucide-message-square"
|
|
22
|
+
class="size-4 text-primary"
|
|
23
|
+
/>
|
|
24
|
+
<span class="text-sm font-medium">Recent Chats</span>
|
|
25
|
+
</div>
|
|
26
|
+
<UButton
|
|
27
|
+
to="/chat"
|
|
28
|
+
variant="ghost"
|
|
29
|
+
size="xs"
|
|
30
|
+
trailing-icon="i-lucide-arrow-right"
|
|
31
|
+
>
|
|
32
|
+
View all
|
|
33
|
+
</UButton>
|
|
34
|
+
</div>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<div
|
|
38
|
+
v-if="loading"
|
|
39
|
+
class="space-y-3"
|
|
40
|
+
>
|
|
41
|
+
<div
|
|
42
|
+
v-for="i in 3"
|
|
43
|
+
:key="i"
|
|
44
|
+
class="flex items-center gap-3"
|
|
45
|
+
>
|
|
46
|
+
<USkeleton class="size-8 rounded-full" />
|
|
47
|
+
<div class="flex-1">
|
|
48
|
+
<USkeleton class="h-4 w-32 mb-1" />
|
|
49
|
+
<USkeleton class="h-3 w-20" />
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<div
|
|
55
|
+
v-else-if="conversations.length === 0"
|
|
56
|
+
class="text-center py-4 text-muted text-sm"
|
|
57
|
+
>
|
|
58
|
+
No conversations yet
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<div
|
|
62
|
+
v-else
|
|
63
|
+
class="space-y-2"
|
|
64
|
+
>
|
|
65
|
+
<NuxtLink
|
|
66
|
+
v-for="chat in conversations"
|
|
67
|
+
:key="chat.id"
|
|
68
|
+
:to="{ path: '/chat', query: { conversation: chat.id } }"
|
|
69
|
+
class="flex items-center gap-3 p-2 -mx-2 rounded-md hover:bg-elevated transition-colors"
|
|
70
|
+
>
|
|
71
|
+
<div class="flex items-center justify-center size-8 rounded-full bg-primary/10">
|
|
72
|
+
<UIcon
|
|
73
|
+
name="i-lucide-message-square"
|
|
74
|
+
class="size-4 text-primary"
|
|
75
|
+
/>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div class="flex-1 min-w-0">
|
|
79
|
+
<p class="text-sm font-medium truncate">
|
|
80
|
+
{{ chat.title || 'Untitled Chat' }}
|
|
81
|
+
</p>
|
|
82
|
+
<p class="text-xs text-muted">
|
|
83
|
+
{{ chat.messageCount }} messages
|
|
84
|
+
</p>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<span class="text-xs text-muted whitespace-nowrap">
|
|
88
|
+
{{ relativeTime(chat.startedAt) }}
|
|
89
|
+
</span>
|
|
90
|
+
</NuxtLink>
|
|
91
|
+
</div>
|
|
92
|
+
</UCard>
|
|
93
|
+
</template>
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { formatDistanceToNow } from 'date-fns'
|
|
3
|
+
import type { DashboardOverview } from '~~/shared/types'
|
|
4
|
+
|
|
5
|
+
defineProps<{
|
|
6
|
+
documents: DashboardOverview['documents']
|
|
7
|
+
loading?: boolean
|
|
8
|
+
}>()
|
|
9
|
+
|
|
10
|
+
function relativeTime(iso: string | null): string {
|
|
11
|
+
if (!iso) return ''
|
|
12
|
+
return formatDistanceToNow(new Date(iso), { addSuffix: true })
|
|
13
|
+
}
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<UCard>
|
|
18
|
+
<template #header>
|
|
19
|
+
<div class="flex items-center justify-between">
|
|
20
|
+
<div class="flex items-center gap-2">
|
|
21
|
+
<UIcon
|
|
22
|
+
name="i-lucide-file-text"
|
|
23
|
+
class="size-4 text-primary"
|
|
24
|
+
/>
|
|
25
|
+
<span class="text-sm font-medium">Recent Docs</span>
|
|
26
|
+
</div>
|
|
27
|
+
<UButton
|
|
28
|
+
to="/docs"
|
|
29
|
+
variant="ghost"
|
|
30
|
+
size="xs"
|
|
31
|
+
trailing-icon="i-lucide-arrow-right"
|
|
32
|
+
>
|
|
33
|
+
View all
|
|
34
|
+
</UButton>
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<div
|
|
39
|
+
v-if="loading"
|
|
40
|
+
class="space-y-3"
|
|
41
|
+
>
|
|
42
|
+
<div
|
|
43
|
+
v-for="i in 3"
|
|
44
|
+
:key="i"
|
|
45
|
+
class="flex items-center gap-3"
|
|
46
|
+
>
|
|
47
|
+
<USkeleton class="size-8 rounded" />
|
|
48
|
+
<div class="flex-1">
|
|
49
|
+
<USkeleton class="h-4 w-32 mb-1" />
|
|
50
|
+
<USkeleton class="h-3 w-20" />
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div
|
|
56
|
+
v-else-if="documents.length === 0"
|
|
57
|
+
class="text-center py-4 text-muted text-sm"
|
|
58
|
+
>
|
|
59
|
+
No documents yet
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div
|
|
63
|
+
v-else
|
|
64
|
+
class="space-y-2"
|
|
65
|
+
>
|
|
66
|
+
<NuxtLink
|
|
67
|
+
v-for="doc in documents"
|
|
68
|
+
:key="doc.id"
|
|
69
|
+
:to="{ path: '/docs', query: { path: doc.path } }"
|
|
70
|
+
class="flex items-center gap-3 p-2 -mx-2 rounded-md hover:bg-elevated transition-colors"
|
|
71
|
+
>
|
|
72
|
+
<div class="flex items-center justify-center size-8 rounded bg-primary/10">
|
|
73
|
+
<UIcon
|
|
74
|
+
name="i-lucide-file-text"
|
|
75
|
+
class="size-4 text-primary"
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<div class="flex-1 min-w-0">
|
|
80
|
+
<p class="text-sm font-medium truncate">
|
|
81
|
+
{{ doc.title }}
|
|
82
|
+
</p>
|
|
83
|
+
<div class="flex items-center gap-2">
|
|
84
|
+
<UBadge
|
|
85
|
+
v-if="doc.projectName"
|
|
86
|
+
variant="subtle"
|
|
87
|
+
color="neutral"
|
|
88
|
+
size="xs"
|
|
89
|
+
>
|
|
90
|
+
<span
|
|
91
|
+
class="inline-block size-2 rounded-full mr-1"
|
|
92
|
+
:style="{ background: doc.projectColor || 'var(--ui-primary)' }"
|
|
93
|
+
/>
|
|
94
|
+
{{ doc.projectName }}
|
|
95
|
+
</UBadge>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<span
|
|
100
|
+
v-if="doc.modifiedAt"
|
|
101
|
+
class="text-xs text-muted whitespace-nowrap"
|
|
102
|
+
>
|
|
103
|
+
{{ relativeTime(doc.modifiedAt) }}
|
|
104
|
+
</span>
|
|
105
|
+
</NuxtLink>
|
|
106
|
+
</div>
|
|
107
|
+
</UCard>
|
|
108
|
+
</template>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { DashboardOverview } from '~~/shared/types'
|
|
3
|
+
|
|
4
|
+
defineProps<{
|
|
5
|
+
overview: DashboardOverview | null
|
|
6
|
+
loading?: boolean
|
|
7
|
+
}>()
|
|
8
|
+
|
|
9
|
+
function formatCurrency(value: number): string {
|
|
10
|
+
if (value < 0.01 && value > 0) return '<$0.01'
|
|
11
|
+
return `$${value.toFixed(2)}`
|
|
12
|
+
}
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<div class="grid gap-4 grid-cols-2 lg:grid-cols-4">
|
|
17
|
+
<template v-if="loading">
|
|
18
|
+
<div
|
|
19
|
+
v-for="i in 4"
|
|
20
|
+
:key="i"
|
|
21
|
+
class="p-4 rounded-lg bg-elevated border border-default"
|
|
22
|
+
>
|
|
23
|
+
<USkeleton class="h-4 w-16 mb-2" />
|
|
24
|
+
<USkeleton class="h-8 w-12" />
|
|
25
|
+
</div>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<template v-else-if="overview">
|
|
29
|
+
<div class="p-4 rounded-lg bg-elevated border border-default">
|
|
30
|
+
<div class="flex items-center gap-2 text-muted text-sm mb-1">
|
|
31
|
+
<UIcon
|
|
32
|
+
name="i-lucide-circle-dot"
|
|
33
|
+
class="size-4"
|
|
34
|
+
/>
|
|
35
|
+
<span>Todo</span>
|
|
36
|
+
</div>
|
|
37
|
+
<p class="text-2xl font-semibold">
|
|
38
|
+
{{ overview.tasks.todoCount }}
|
|
39
|
+
</p>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div class="p-4 rounded-lg bg-elevated border border-default">
|
|
43
|
+
<div class="flex items-center gap-2 text-muted text-sm mb-1">
|
|
44
|
+
<UIcon
|
|
45
|
+
name="i-lucide-loader"
|
|
46
|
+
class="size-4"
|
|
47
|
+
/>
|
|
48
|
+
<span>In Progress</span>
|
|
49
|
+
</div>
|
|
50
|
+
<p class="text-2xl font-semibold">
|
|
51
|
+
{{ overview.tasks.inProgressCount }}
|
|
52
|
+
</p>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div class="p-4 rounded-lg bg-elevated border border-default">
|
|
56
|
+
<div class="flex items-center gap-2 text-muted text-sm mb-1">
|
|
57
|
+
<UIcon
|
|
58
|
+
name="i-lucide-dollar-sign"
|
|
59
|
+
class="size-4"
|
|
60
|
+
/>
|
|
61
|
+
<span>7d Cost</span>
|
|
62
|
+
</div>
|
|
63
|
+
<p class="text-2xl font-semibold">
|
|
64
|
+
{{ formatCurrency(overview.usage.totalCost7d) }}
|
|
65
|
+
</p>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<div class="p-4 rounded-lg bg-elevated border border-default">
|
|
69
|
+
<div class="flex items-center gap-2 text-muted text-sm mb-1">
|
|
70
|
+
<UIcon
|
|
71
|
+
name="i-lucide-zap"
|
|
72
|
+
class="size-4"
|
|
73
|
+
/>
|
|
74
|
+
<span>7d API Calls</span>
|
|
75
|
+
</div>
|
|
76
|
+
<p class="text-2xl font-semibold">
|
|
77
|
+
{{ overview.usage.totalCalls7d }}
|
|
78
|
+
</p>
|
|
79
|
+
</div>
|
|
80
|
+
</template>
|
|
81
|
+
</div>
|
|
82
|
+
</template>
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { DashboardOverview } from '~~/shared/types'
|
|
3
|
+
|
|
4
|
+
defineProps<{
|
|
5
|
+
tasks: DashboardOverview['tasks']['upcoming']
|
|
6
|
+
loading?: boolean
|
|
7
|
+
}>()
|
|
8
|
+
|
|
9
|
+
const priorityLabels = { 1: 'Low', 2: 'Med', 3: 'High' } as const
|
|
10
|
+
const priorityColors = { 1: 'neutral', 2: 'warning', 3: 'error' } as const
|
|
11
|
+
const statusColors = { todo: 'neutral', in_progress: 'primary', blocked: 'error' } as const
|
|
12
|
+
|
|
13
|
+
function formatDueDate(iso: string | null): string {
|
|
14
|
+
if (!iso) return ''
|
|
15
|
+
const date = new Date(iso)
|
|
16
|
+
const now = new Date()
|
|
17
|
+
const diff = date.getTime() - now.getTime()
|
|
18
|
+
const days = Math.ceil(diff / (1000 * 60 * 60 * 24))
|
|
19
|
+
if (days < 0) return `${Math.abs(days)}d overdue`
|
|
20
|
+
if (days === 0) return 'Today'
|
|
21
|
+
if (days === 1) return 'Tomorrow'
|
|
22
|
+
return `${days}d`
|
|
23
|
+
}
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<template>
|
|
27
|
+
<UCard>
|
|
28
|
+
<template #header>
|
|
29
|
+
<div class="flex items-center justify-between">
|
|
30
|
+
<div class="flex items-center gap-2">
|
|
31
|
+
<UIcon
|
|
32
|
+
name="i-lucide-check-square"
|
|
33
|
+
class="size-4 text-primary"
|
|
34
|
+
/>
|
|
35
|
+
<span class="text-sm font-medium">Upcoming Tasks</span>
|
|
36
|
+
</div>
|
|
37
|
+
<UButton
|
|
38
|
+
to="/tasks"
|
|
39
|
+
variant="ghost"
|
|
40
|
+
size="xs"
|
|
41
|
+
trailing-icon="i-lucide-arrow-right"
|
|
42
|
+
>
|
|
43
|
+
View all
|
|
44
|
+
</UButton>
|
|
45
|
+
</div>
|
|
46
|
+
</template>
|
|
47
|
+
|
|
48
|
+
<div
|
|
49
|
+
v-if="loading"
|
|
50
|
+
class="space-y-3"
|
|
51
|
+
>
|
|
52
|
+
<div
|
|
53
|
+
v-for="i in 3"
|
|
54
|
+
:key="i"
|
|
55
|
+
class="flex items-center gap-3"
|
|
56
|
+
>
|
|
57
|
+
<USkeleton class="h-5 w-5 rounded" />
|
|
58
|
+
<USkeleton class="h-4 flex-1" />
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div
|
|
63
|
+
v-else-if="tasks.length === 0"
|
|
64
|
+
class="text-center py-4 text-muted text-sm"
|
|
65
|
+
>
|
|
66
|
+
No upcoming tasks
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div
|
|
70
|
+
v-else
|
|
71
|
+
class="space-y-2"
|
|
72
|
+
>
|
|
73
|
+
<NuxtLink
|
|
74
|
+
v-for="task in tasks"
|
|
75
|
+
:key="task.id"
|
|
76
|
+
:to="{ path: '/tasks', query: { selected: task.id } }"
|
|
77
|
+
class="flex items-center gap-3 p-2 -mx-2 rounded-md hover:bg-elevated transition-colors"
|
|
78
|
+
>
|
|
79
|
+
<UBadge
|
|
80
|
+
:color="statusColors[task.status as keyof typeof statusColors] || 'neutral'"
|
|
81
|
+
variant="subtle"
|
|
82
|
+
size="xs"
|
|
83
|
+
>
|
|
84
|
+
{{ task.status === 'in_progress' ? 'WIP' : task.status }}
|
|
85
|
+
</UBadge>
|
|
86
|
+
|
|
87
|
+
<span class="text-sm truncate flex-1">{{ task.title }}</span>
|
|
88
|
+
|
|
89
|
+
<UBadge
|
|
90
|
+
v-if="task.projectName"
|
|
91
|
+
variant="subtle"
|
|
92
|
+
color="neutral"
|
|
93
|
+
size="xs"
|
|
94
|
+
>
|
|
95
|
+
<span
|
|
96
|
+
class="inline-block size-2 rounded-full mr-1"
|
|
97
|
+
:style="{ background: task.projectColor || 'var(--ui-primary)' }"
|
|
98
|
+
/>
|
|
99
|
+
{{ task.projectName }}
|
|
100
|
+
</UBadge>
|
|
101
|
+
|
|
102
|
+
<UBadge
|
|
103
|
+
:color="priorityColors[task.priority as keyof typeof priorityColors] || 'neutral'"
|
|
104
|
+
variant="soft"
|
|
105
|
+
size="xs"
|
|
106
|
+
>
|
|
107
|
+
{{ priorityLabels[task.priority as keyof typeof priorityLabels] || 'Med' }}
|
|
108
|
+
</UBadge>
|
|
109
|
+
|
|
110
|
+
<span
|
|
111
|
+
v-if="task.dueDate"
|
|
112
|
+
class="text-xs text-muted whitespace-nowrap"
|
|
113
|
+
>
|
|
114
|
+
{{ formatDueDate(task.dueDate) }}
|
|
115
|
+
</span>
|
|
116
|
+
</NuxtLink>
|
|
117
|
+
</div>
|
|
118
|
+
</UCard>
|
|
119
|
+
</template>
|