tokentracker-cli 0.30.0 → 0.31.1

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 (62) hide show
  1. package/README.ja.md +3 -1
  2. package/README.ko.md +3 -1
  3. package/README.md +3 -1
  4. package/README.zh-CN.md +3 -1
  5. package/dashboard/dist/assets/{ActivityHeatmap-BzW0rtoR.js → ActivityHeatmap-BjiEMIl2.js} +1 -1
  6. package/dashboard/dist/assets/{Card-wgEcKm5u.js → Card-Nlgzr57Q.js} +1 -1
  7. package/dashboard/dist/assets/DashboardPage-z7rLj1gX.js +19 -0
  8. package/dashboard/dist/assets/{DevicePage-D82cqG-v.js → DevicePage-B1NhBt9Q.js} +1 -1
  9. package/dashboard/dist/assets/DialogTitle-CEBn-oBo.js +12 -0
  10. package/dashboard/dist/assets/FadeIn-Dbk47nhk.js +1 -0
  11. package/dashboard/dist/assets/{HeaderGithubStar-CSBGOKw2.js → HeaderGithubStar-B0OAKXVw.js} +1 -1
  12. package/dashboard/dist/assets/{IpCheckPage-WXEmhuVW.js → IpCheckPage-C0wQLtdK.js} +1 -1
  13. package/dashboard/dist/assets/{LandingPage-ap4xIJva.js → LandingPage-DWSbmVip.js} +2 -2
  14. package/dashboard/dist/assets/{LeaderboardAvatar-CjaHOh8E.js → LeaderboardAvatar-CLMnffKE.js} +1 -1
  15. package/dashboard/dist/assets/LeaderboardPage-CZvefN8H.js +6 -0
  16. package/dashboard/dist/assets/{LeaderboardProfileModal-BX4b5Uqc.js → LeaderboardProfileModal-DasxA8Lo.js} +3 -3
  17. package/dashboard/dist/assets/LeaderboardProfilePage-DJJ7l3Ix.js +1 -0
  18. package/dashboard/dist/assets/LimitsPage-xUmhxOqT.js +2 -0
  19. package/dashboard/dist/assets/{LocalOnlyNotice-D43ZmQJl.js → LocalOnlyNotice-B3uM29rj.js} +1 -1
  20. package/dashboard/dist/assets/{LoginPage-gqSOnPUr.js → LoginPage-BejL6eIO.js} +1 -1
  21. package/dashboard/dist/assets/PopoverPopup-V6vxWbIV.js +1 -0
  22. package/dashboard/dist/assets/SettingsPage-DossFM4s.js +1 -0
  23. package/dashboard/dist/assets/SkillsPage-COwiRUo1.js +1 -0
  24. package/dashboard/dist/assets/WidgetsPage-CtkM_RQn.js +1 -0
  25. package/dashboard/dist/assets/WrappedPage-BpXrsSoG.js +1 -0
  26. package/dashboard/dist/assets/agent-logos-D6Jmq7E1.js +1 -0
  27. package/dashboard/dist/assets/{arrow-up-right-BXWeIpuU.js → arrow-up-right-BS0sCPTz.js} +1 -1
  28. package/dashboard/dist/assets/check-DKwh5hl-.js +1 -0
  29. package/dashboard/dist/assets/{chevron-down-u-JNpncG.js → chevron-down-DyiXbuAL.js} +1 -1
  30. package/dashboard/dist/assets/{download-DvfHniid.js → download-CqDWRO99.js} +1 -1
  31. package/dashboard/dist/assets/{info-BdrJIus6.js → info-DMW1GFmI.js} +1 -1
  32. package/dashboard/dist/assets/main-CjFrTufR.css +1 -0
  33. package/dashboard/dist/assets/main-uqC1VAKB.js +959 -0
  34. package/dashboard/dist/assets/{use-limits-display-prefs-DU4WDeXF.js → use-limits-display-prefs-C04zvO7N.js} +1 -1
  35. package/dashboard/dist/assets/{use-native-settings-CefuRE0t.js → use-native-settings-BvFr6zgZ.js} +1 -1
  36. package/dashboard/dist/assets/{use-usage-limits-BvZsTD3e.js → use-usage-limits-5fwy0xeB.js} +1 -1
  37. package/dashboard/dist/assets/useCurrency-vRzRfd7L.js +1 -0
  38. package/dashboard/dist/index.html +2 -2
  39. package/dashboard/dist/share.html +2 -2
  40. package/package.json +1 -1
  41. package/src/lib/local-api.js +67 -0
  42. package/src/lib/pricing/seed-snapshot.json +1 -1
  43. package/src/lib/skill-usage.js +267 -0
  44. package/src/lib/skills-manager.js +371 -13
  45. package/dashboard/dist/assets/DashboardPage-BH_InuuJ.js +0 -19
  46. package/dashboard/dist/assets/DialogTitle-QumMNQjm.js +0 -12
  47. package/dashboard/dist/assets/FadeIn-B2rj5E5l.js +0 -1
  48. package/dashboard/dist/assets/LeaderboardPage-DN4Sd56j.js +0 -6
  49. package/dashboard/dist/assets/LeaderboardProfilePage-hkIYzooc.js +0 -1
  50. package/dashboard/dist/assets/LimitsPage-BgoAXuwU.js +0 -2
  51. package/dashboard/dist/assets/PopoverPopup-BkMR2-Ij.js +0 -1
  52. package/dashboard/dist/assets/ProviderIcon-No-nxvR1.js +0 -1
  53. package/dashboard/dist/assets/SettingsPage-BVpnWPq9.js +0 -1
  54. package/dashboard/dist/assets/SkillsPage-DnIxIt1d.js +0 -1
  55. package/dashboard/dist/assets/WidgetsPage-DY7qT8jg.js +0 -1
  56. package/dashboard/dist/assets/WrappedPage-BTkoEQPM.js +0 -1
  57. package/dashboard/dist/assets/agent-logos-Dp5LiRgu.js +0 -1
  58. package/dashboard/dist/assets/check-B_JRaxbD.js +0 -1
  59. package/dashboard/dist/assets/main-BojOq9Ae.css +0 -1
  60. package/dashboard/dist/assets/main-DhcQGvkP.js +0 -917
  61. package/dashboard/dist/assets/use-reduced-motion-nzFM1j1r.js +0 -1
  62. package/dashboard/dist/assets/useCurrency-C62kk8HZ.js +0 -1
@@ -1 +1 @@
1
- import{r as a}from"./main-DhcQGvkP.js";const d=["claude","codex","cursor","gemini","kimi","kiro","copilot","antigravity"],S={claude:"Claude",codex:"Codex",cursor:"Cursor",gemini:"Gemini",kimi:"Kimi",kiro:"Kiro",copilot:"GitHub Copilot",antigravity:"Antigravity"},v={claude:"/brand-logos/claude-code.svg",codex:"/brand-logos/codex.svg",cursor:"/brand-logos/cursor.svg",gemini:"/brand-logos/gemini.svg",kimi:"/brand-logos/kimi.svg",kiro:"/brand-logos/kiro.svg",copilot:"/brand-logos/copilot.svg",antigravity:"/brand-logos/antigravity.svg"},l="tt.limits.providerOrder",g="tt.limits.providerVisibility";function w(){if(typeof window>"u")return[...d];try{const i=window.localStorage.getItem(l);if(!i)return[...d];const s=JSON.parse(i);if(!Array.isArray(s))return[...d];const r=s.filter(c=>d.includes(c));for(const c of d)r.includes(c)||r.push(c);return r}catch{return[...d]}}function y(){const i=Object.fromEntries(d.map(s=>[s,!0]));if(typeof window>"u")return i;try{const s=window.localStorage.getItem(g);if(!s)return i;const r=JSON.parse(s);if(!r||typeof r!="object")return i;const c={...i};for(const u of d)typeof r[u]=="boolean"&&(c[u]=r[u]);return c}catch{return i}}function C(){const[i,s]=a.useState(w),[r,c]=a.useState(y);a.useEffect(()=>{if(!(typeof window>"u"))try{window.localStorage.setItem(l,JSON.stringify(i))}catch{}},[i]),a.useEffect(()=>{if(!(typeof window>"u"))try{window.localStorage.setItem(g,JSON.stringify(r))}catch{}},[r]),a.useEffect(()=>{if(typeof window>"u")return;const o=t=>{t.key===l&&s(w()),t.key===g&&c(y())};return window.addEventListener("storage",o),()=>window.removeEventListener("storage",o)},[]);const u=a.useCallback(o=>{c(t=>({...t,[o]:!t[o]}))},[]),b=a.useCallback(o=>{s(t=>{const e=t.indexOf(o);if(e<=0)return t;const n=[...t];return[n[e-1],n[e]]=[n[e],n[e-1]],n})},[]),p=a.useCallback(o=>{s(t=>{const e=t.indexOf(o);if(e<0||e>=t.length-1)return t;const n=[...t];return[n[e],n[e+1]]=[n[e+1],n[e]],n})},[]),O=a.useCallback((o,t)=>{o!==t&&s(e=>{const n=e.indexOf(o),m=e.indexOf(t);if(n<0||m<0)return e;const f=[...e],[I]=f.splice(n,1);return f.splice(m,0,I),f})},[]),k=a.useCallback(()=>{s([...d]),c(Object.fromEntries(d.map(o=>[o,!0])))},[]),x=a.useMemo(()=>i.filter(o=>r[o]!==!1),[i,r]);return{order:i,visibility:r,visibleOrdered:x,toggle:u,moveUp:b,moveDown:p,moveToward:O,reset:k}}export{v as L,S as a,C as u};
1
+ import{r as a}from"./main-uqC1VAKB.js";const d=["claude","codex","cursor","gemini","kimi","kiro","copilot","antigravity"],S={claude:"Claude",codex:"Codex",cursor:"Cursor",gemini:"Gemini",kimi:"Kimi",kiro:"Kiro",copilot:"GitHub Copilot",antigravity:"Antigravity"},v={claude:"/brand-logos/claude-code.svg",codex:"/brand-logos/codex.svg",cursor:"/brand-logos/cursor.svg",gemini:"/brand-logos/gemini.svg",kimi:"/brand-logos/kimi.svg",kiro:"/brand-logos/kiro.svg",copilot:"/brand-logos/copilot.svg",antigravity:"/brand-logos/antigravity.svg"},l="tt.limits.providerOrder",g="tt.limits.providerVisibility";function w(){if(typeof window>"u")return[...d];try{const i=window.localStorage.getItem(l);if(!i)return[...d];const s=JSON.parse(i);if(!Array.isArray(s))return[...d];const r=s.filter(c=>d.includes(c));for(const c of d)r.includes(c)||r.push(c);return r}catch{return[...d]}}function y(){const i=Object.fromEntries(d.map(s=>[s,!0]));if(typeof window>"u")return i;try{const s=window.localStorage.getItem(g);if(!s)return i;const r=JSON.parse(s);if(!r||typeof r!="object")return i;const c={...i};for(const u of d)typeof r[u]=="boolean"&&(c[u]=r[u]);return c}catch{return i}}function C(){const[i,s]=a.useState(w),[r,c]=a.useState(y);a.useEffect(()=>{if(!(typeof window>"u"))try{window.localStorage.setItem(l,JSON.stringify(i))}catch{}},[i]),a.useEffect(()=>{if(!(typeof window>"u"))try{window.localStorage.setItem(g,JSON.stringify(r))}catch{}},[r]),a.useEffect(()=>{if(typeof window>"u")return;const o=t=>{t.key===l&&s(w()),t.key===g&&c(y())};return window.addEventListener("storage",o),()=>window.removeEventListener("storage",o)},[]);const u=a.useCallback(o=>{c(t=>({...t,[o]:!t[o]}))},[]),b=a.useCallback(o=>{s(t=>{const e=t.indexOf(o);if(e<=0)return t;const n=[...t];return[n[e-1],n[e]]=[n[e],n[e-1]],n})},[]),p=a.useCallback(o=>{s(t=>{const e=t.indexOf(o);if(e<0||e>=t.length-1)return t;const n=[...t];return[n[e],n[e+1]]=[n[e+1],n[e]],n})},[]),O=a.useCallback((o,t)=>{o!==t&&s(e=>{const n=e.indexOf(o),m=e.indexOf(t);if(n<0||m<0)return e;const f=[...e],[I]=f.splice(n,1);return f.splice(m,0,I),f})},[]),k=a.useCallback(()=>{s([...d]),c(Object.fromEntries(d.map(o=>[o,!0])))},[]),x=a.useMemo(()=>i.filter(o=>r[o]!==!1),[i,r]);return{order:i,visibility:r,visibleOrdered:x,toggle:u,moveUp:b,moveDown:p,moveToward:O,reset:k}}export{v as L,S as a,C as u};
@@ -1 +1 @@
1
- import{u as a,H as x,r as n,aK as g,aL as m,aM as b,aN as u,aO as f,aI as h}from"./main-DhcQGvkP.js";import{C as y}from"./Card-wgEcKm5u.js";function p({checked:s,onChange:t,disabled:e,ariaLabel:i}){return a.jsx("button",{type:"button",role:"switch","aria-checked":s,"aria-label":i,onClick:t,disabled:e,className:x("relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-oai-brand-500 disabled:opacity-50 disabled:cursor-not-allowed",s?"bg-oai-brand-500":"bg-oai-gray-300 dark:bg-oai-gray-700"),children:a.jsx("span",{className:x("inline-block h-3.5 w-3.5 rounded-full bg-white transition-transform",s?"translate-x-[18px]":"translate-x-[3px]")})})}function N({label:s,hint:t,control:e}){return a.jsxs("div",{className:"flex items-center justify-between gap-4 py-3",children:[a.jsxs("div",{className:"min-w-0 flex-1",children:[a.jsx("div",{className:"text-sm text-oai-gray-900 dark:text-oai-gray-200",children:s}),t?a.jsx("div",{className:"mt-0.5 text-xs text-oai-gray-500 dark:text-oai-gray-400",children:t}):null]}),a.jsx("div",{className:"shrink-0",children:e})]})}function j({title:s,subtitle:t,action:e,children:i}){return a.jsxs(y,{children:[a.jsxs("div",{className:"mb-3 flex items-start justify-between gap-4",children:[a.jsxs("div",{className:"min-w-0 flex-1",children:[a.jsx("h2",{className:"text-sm font-medium text-oai-gray-500 dark:text-oai-gray-300 uppercase tracking-wide",children:s}),t?a.jsx("p",{className:"mt-1 truncate text-xs text-oai-gray-500 dark:text-oai-gray-400",children:t}):null]}),e?a.jsx("div",{className:"shrink-0",children:e}):null]}),a.jsx("div",{className:"-mb-3 divide-y divide-oai-gray-200/60 dark:divide-oai-gray-800/60",children:i})]})}function w({options:s,value:t,onChange:e}){return a.jsx("div",{className:"inline-flex items-center rounded-lg border border-oai-gray-200 bg-oai-gray-50 p-0.5 dark:border-oai-gray-800 dark:bg-oai-gray-900",children:s.map(({value:i,label:d,Icon:l})=>{const r=t===i;return a.jsxs("button",{type:"button",onClick:()=>e(i),"aria-pressed":r,className:x("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors",r?"bg-white text-oai-black shadow-sm dark:bg-oai-gray-800 dark:text-white":"text-oai-gray-500 hover:text-oai-black dark:text-oai-gray-400 dark:hover:text-white"),children:[l?a.jsx(l,{className:"h-3.5 w-3.5","aria-hidden":!0}):null,a.jsx("span",{children:d})]},i)})})}function S(){const[s,t]=n.useState(null),e=g()&&m();n.useEffect(()=>{if(!e)return;const r=b(o=>t(o));return u(),r},[e]);const i=n.useCallback((r,o)=>{e&&(t(c=>c&&{...c,[r]:o}),f(r,o))},[e]),d=n.useCallback(r=>{e&&h(r)},[e]),l=n.useCallback(()=>{e&&u()},[e]);return{available:e,settings:s,setSetting:i,runAction:d,refresh:l}}export{j as S,p as T,N as a,w as b,S as u};
1
+ import{u as a,N as x,r as n,ce as g,cf as m,cg as b,ch as u,ci as f,cc as h}from"./main-uqC1VAKB.js";import{C as y}from"./Card-Nlgzr57Q.js";function p({checked:s,onChange:t,disabled:e,ariaLabel:i}){return a.jsx("button",{type:"button",role:"switch","aria-checked":s,"aria-label":i,onClick:t,disabled:e,className:x("relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-oai-brand-500 disabled:opacity-50 disabled:cursor-not-allowed",s?"bg-oai-brand-500":"bg-oai-gray-300 dark:bg-oai-gray-700"),children:a.jsx("span",{className:x("inline-block h-3.5 w-3.5 rounded-full bg-white transition-transform",s?"translate-x-[18px]":"translate-x-[3px]")})})}function N({label:s,hint:t,control:e}){return a.jsxs("div",{className:"flex items-center justify-between gap-4 py-3",children:[a.jsxs("div",{className:"min-w-0 flex-1",children:[a.jsx("div",{className:"text-sm text-oai-gray-900 dark:text-oai-gray-200",children:s}),t?a.jsx("div",{className:"mt-0.5 text-xs text-oai-gray-500 dark:text-oai-gray-400",children:t}):null]}),a.jsx("div",{className:"shrink-0",children:e})]})}function j({title:s,subtitle:t,action:e,children:i}){return a.jsxs(y,{children:[a.jsxs("div",{className:"mb-3 flex items-start justify-between gap-4",children:[a.jsxs("div",{className:"min-w-0 flex-1",children:[a.jsx("h2",{className:"text-sm font-medium text-oai-gray-500 dark:text-oai-gray-300 uppercase tracking-wide",children:s}),t?a.jsx("p",{className:"mt-1 truncate text-xs text-oai-gray-500 dark:text-oai-gray-400",children:t}):null]}),e?a.jsx("div",{className:"shrink-0",children:e}):null]}),a.jsx("div",{className:"-mb-3 divide-y divide-oai-gray-200/60 dark:divide-oai-gray-800/60",children:i})]})}function w({options:s,value:t,onChange:e}){return a.jsx("div",{className:"inline-flex items-center rounded-lg border border-oai-gray-200 bg-oai-gray-50 p-0.5 dark:border-oai-gray-800 dark:bg-oai-gray-900",children:s.map(({value:i,label:c,Icon:l})=>{const r=t===i;return a.jsxs("button",{type:"button",onClick:()=>e(i),"aria-pressed":r,className:x("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors",r?"bg-white text-oai-black shadow-sm dark:bg-oai-gray-800 dark:text-white":"text-oai-gray-500 hover:text-oai-black dark:text-oai-gray-400 dark:hover:text-white"),children:[l?a.jsx(l,{className:"h-3.5 w-3.5","aria-hidden":!0}):null,a.jsx("span",{children:c})]},i)})})}function S(){const[s,t]=n.useState(null),e=g()&&m();n.useEffect(()=>{if(!e)return;const r=b(o=>t(o));return u(),r},[e]);const i=n.useCallback((r,o)=>{e&&(t(d=>d&&{...d,[r]:o}),f(r,o))},[e]),c=n.useCallback(r=>{e&&h(r)},[e]),l=n.useCallback(()=>{e&&u()},[e]);return{available:e,settings:s,setSetting:i,runAction:c,refresh:l}}export{j as S,p as T,N as a,w as b,S as u};
@@ -1 +1 @@
1
- import{r,an as l}from"./main-DhcQGvkP.js";function y(i){const[o,a]=r.useState(null),[u,s]=r.useState(null),[c,f]=r.useState(!0),n=!!i?.initialRefresh,g=r.useCallback(async()=>{try{const e=await l({refresh:!0});a(e&&typeof e=="object"?e:null),s(null)}catch(e){s(e?.message||String(e))}},[]);return r.useEffect(()=>{let e=!1;return(async()=>{try{const t=await l(n?{refresh:!0}:{});if(e)return;a(t&&typeof t=="object"?t:null),s(null)}catch(t){if(e)return;s(t?.message||String(t))}finally{e||f(!1)}})(),()=>{e=!0}},[n]),{data:o,error:u,isLoading:c,refresh:g}}export{y as u};
1
+ import{r,at as l}from"./main-uqC1VAKB.js";function y(i){const[o,a]=r.useState(null),[u,s]=r.useState(null),[c,f]=r.useState(!0),n=!!i?.initialRefresh,g=r.useCallback(async()=>{try{const e=await l({refresh:!0});a(e&&typeof e=="object"?e:null),s(null)}catch(e){s(e?.message||String(e))}},[]);return r.useEffect(()=>{let e=!1;return(async()=>{try{const t=await l(n?{refresh:!0}:{});if(e)return;a(t&&typeof t=="object"?t:null),s(null)}catch(t){if(e)return;s(t?.message||String(t))}finally{e||f(!1)}})(),()=>{e=!0}},[n]),{data:o,error:u,isLoading:c,refresh:g}}export{y as u};
@@ -0,0 +1 @@
1
+ import{r as e,ca as t,cb as r,E as c}from"./main-uqC1VAKB.js";const s=Object.freeze({currency:c,rate:1,symbol:"$",rates:{...r},rateSource:"default",rateFetchedAt:null,setCurrency:()=>{}});function u(){return e.useContext(t)??s}export{u};
@@ -235,8 +235,8 @@
235
235
  ]
236
236
  }
237
237
  </script>
238
- <script type="module" crossorigin src="/assets/main-DhcQGvkP.js"></script>
239
- <link rel="stylesheet" crossorigin href="/assets/main-BojOq9Ae.css">
238
+ <script type="module" crossorigin src="/assets/main-uqC1VAKB.js"></script>
239
+ <link rel="stylesheet" crossorigin href="/assets/main-CjFrTufR.css">
240
240
  </head>
241
241
  <body>
242
242
  <main class="aeo-seed-content" aria-label="Token Tracker AI-readable summary">
@@ -75,8 +75,8 @@
75
75
  "description": "Shareable Token Tracker dashboard snapshot."
76
76
  }
77
77
  </script>
78
- <script type="module" crossorigin src="/assets/main-DhcQGvkP.js"></script>
79
- <link rel="stylesheet" crossorigin href="/assets/main-BojOq9Ae.css">
78
+ <script type="module" crossorigin src="/assets/main-uqC1VAKB.js"></script>
79
+ <link rel="stylesheet" crossorigin href="/assets/main-CjFrTufR.css">
80
80
  </head>
81
81
  <body>
82
82
  <main class="aeo-seed-content" aria-label="Token Tracker share page summary">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokentracker-cli",
3
- "version": "0.30.0",
3
+ "version": "0.31.1",
4
4
  "description": "Token usage tracker for AI agent CLIs (Claude Code, Codex, Cursor, Gemini, Kiro, OpenCode, OpenClaw, Every Code, Hermes, GitHub Copilot, Kimi Code, CodeBuddy, Grok Build, oh-my-pi, pi, Craft Agents, Kilo CLI, Kilo Code, Roo Code, Zed Agent, Goose)",
5
5
  "main": "src/cli.js",
6
6
  "bin": {
@@ -1704,6 +1704,73 @@ function createLocalApiHandler({ queuePath }) {
1704
1704
  json(res, data);
1705
1705
  return true;
1706
1706
  }
1707
+ if (mode === "popular") {
1708
+ const force = url.searchParams.get("force") === "1";
1709
+ json(res, await skills.fetchPopularSkillsSh({ force }));
1710
+ return true;
1711
+ }
1712
+ if (mode === "updates") {
1713
+ const force = url.searchParams.get("force") === "1";
1714
+ json(res, await skills.checkUpdates({ force }));
1715
+ return true;
1716
+ }
1717
+ if (mode === "activity") {
1718
+ const limit = Number(url.searchParams.get("limit") || 50);
1719
+ json(res, { activity: skills.readActivity(limit) });
1720
+ return true;
1721
+ }
1722
+ if (mode === "skill_usage") {
1723
+ const force = url.searchParams.get("force") === "1";
1724
+ const usage = await require("./skill-usage").scanSkillUsage({ force });
1725
+ await ensurePricingLoaded();
1726
+ // Join raw per-skill aggregates against installed skills so we can
1727
+ // separate user-installed skills (where "dead weight" = unused) from
1728
+ // Claude Code built-in tools (bash/agent/…), which dominate the logs
1729
+ // and are NOT uninstallable. Cost is priced per-model (source=claude).
1730
+ const installed = skills.listInstalledSkills();
1731
+ const installedByName = new Map();
1732
+ for (const s of installed) {
1733
+ for (const key of [s.directory, s.name]) {
1734
+ const norm = String(key || "").trim().toLowerCase();
1735
+ if (norm) installedByName.set(norm, s);
1736
+ }
1737
+ }
1738
+ const priced = usage.skills.map((entry) => {
1739
+ let cost = 0;
1740
+ for (const [model, tokens] of Object.entries(entry.models || {})) {
1741
+ cost += computeRowCost({ ...tokens, model, source: "claude" });
1742
+ }
1743
+ const match = installedByName.get(String(entry.skill || "").trim().toLowerCase());
1744
+ return {
1745
+ skill: entry.skill,
1746
+ invocations: entry.invocations,
1747
+ lastUsedAt: entry.lastUsedAt,
1748
+ tokens: entry.tokens,
1749
+ cost,
1750
+ installed: Boolean(match),
1751
+ skillId: match?.id || null,
1752
+ directory: match?.directory || null,
1753
+ };
1754
+ });
1755
+ // Installed skills with zero invocations = dead-weight candidates.
1756
+ const usedNames = new Set(usage.skills.map((s) => String(s.skill || "").trim().toLowerCase()));
1757
+ const unusedInstalled = installed
1758
+ .filter((s) => {
1759
+ const dir = String(s.directory || "").trim().toLowerCase();
1760
+ const name = String(s.name || "").trim().toLowerCase();
1761
+ return !usedNames.has(dir) && !usedNames.has(name);
1762
+ })
1763
+ .map((s) => ({ skillId: s.id, directory: s.directory, name: s.name }));
1764
+ json(res, {
1765
+ generatedAt: usage.generatedAt,
1766
+ scannedFiles: usage.scannedFiles,
1767
+ totalInvocations: usage.totalInvocations,
1768
+ cached: usage.cached,
1769
+ skills: priced,
1770
+ unusedInstalled,
1771
+ });
1772
+ return true;
1773
+ }
1707
1774
  json(res, { error: "Unknown skills mode" }, 400);
1708
1775
  return true;
1709
1776
  }