chore(stats): drop self-hosted chart pipeline; embed star-history.com

The custom SVG chart, daily cron, history.json, peter-evans PR
pattern, and AUTOMERGE_PAT are all gone. Replaced by a one-line
star-history.com embed inside <picture> for native light/dark.

Why the rollback:
- The chart was never meant to require a workflow + protected-branch
  bypass + admin PAT to keep ticking
- ClawHub history was always linearly backfilled (the marketplace
  doesn't expose history); GitHub stars is the only metric with a
  real timeline anyway
- star-history.com renders the same chart, free, no infra, used by
  thousands of OSS repos

Removes:
- .github/workflows/stats.yml
- .github/stats/{generate,seed,update}.mjs
- .github/stats/history.json
- .github/stats/chart-{light,dark}.svg

Stays:
- README header + badges (current ClawHub/npm/release values are
  already covered by shields.io badges in the header)
This commit is contained in:
Lahfir 2026-04-25 15:27:16 -07:00
parent bf3722503b
commit 8e36e9fc7f
8 changed files with 7 additions and 1348 deletions

View file

@ -1,111 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 420" width="1200" height="420" preserveAspectRatio="xMidYMid meet" role="img" aria-labelledby="title desc">
<title id="title">lahfir/agent-desktop — GitHub stars and ClawHub downloads over time</title>
<desc id="desc">Time-series chart showing GitHub stars and ClawHub downloads for lahfir/agent-desktop.</desc>
<defs>
<linearGradient id="fill1" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#58a6ff" stop-opacity="0.28"/>
<stop offset="100%" stop-color="#58a6ff" stop-opacity="0"/>
</linearGradient>
<linearGradient id="fill2" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#ff7b35" stop-opacity="0.28"/>
<stop offset="100%" stop-color="#ff7b35" stop-opacity="0"/>
</linearGradient>
<linearGradient id="stroke1" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#58a6ff"/>
<stop offset="100%" stop-color="#a5d6ff"/>
</linearGradient>
<linearGradient id="stroke2" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#ff7b35"/>
<stop offset="100%" stop-color="#ffa657"/>
</linearGradient>
<filter id="glow1" x="-10%" y="-50%" width="120%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="3" result="blur"/>
<feColorMatrix in="blur" type="matrix" values="0 0 0 0 0.345 0 0 0 0 0.651 0 0 0 0 1.000 0 0 0 0.32 0" result="coloredGlow"/>
<feMerge>
<feMergeNode in="coloredGlow"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<filter id="glow2" x="-10%" y="-50%" width="120%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="3" result="blur"/>
<feColorMatrix in="blur" type="matrix" values="0 0 0 0 1.000 0 0 0 0 0.482 0 0 0 0 0.208 0 0 0 0.32 0" result="coloredGlow"/>
<feMerge>
<feMergeNode in="coloredGlow"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<clipPath id="plot-clip">
<rect x="72" y="72" width="1056" height="296"/>
</clipPath>
</defs>
<rect width="100%" height="100%" fill="#0d1117"/>
<rect x="0.5" y="0.5" width="1199" height="419" fill="none" stroke="#30363d" stroke-width="1" rx="8" ry="8"/>
<text x="72" y="30" fill="#e6edf3" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" font-size="18" font-weight="600">
lahfir/agent-desktop
</text>
<text x="72" y="50" fill="#8b949e" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" font-size="12" font-weight="400">
GitHub stars · ClawHub downloads — updated daily
</text>
<g transform="translate(1128, 30)" text-anchor="end" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" font-size="14" font-weight="600" style="font-variant-numeric: tabular-nums;">
<text fill="#58a6ff">★ 89 <tspan fill="#8b949e" font-weight="400" font-size="11">GitHub</tspan></text>
<text y="20" fill="#ff7b35">⬇ 510 <tspan fill="#8b949e" font-weight="400" font-size="11">ClawHub</tspan></text>
</g>
<g font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" font-size="10" fill="#8b949e" style="font-variant-numeric: tabular-nums;">
<text x="64" y="367.00" text-anchor="end" fill="#58a6ff" opacity="0.85">0</text>
<line x1="72" x2="1128" y1="292.00" y2="292.00" stroke="#21262d" stroke-width="1" stroke-dasharray="2 4" opacity="0.6"/><text x="64" y="295.00" text-anchor="end" fill="#58a6ff" opacity="0.85">25</text>
<line x1="72" x2="1128" y1="220.00" y2="220.00" stroke="#21262d" stroke-width="1" stroke-dasharray="2 4" opacity="0.6"/><text x="64" y="223.00" text-anchor="end" fill="#58a6ff" opacity="0.85">50</text>
<line x1="72" x2="1128" y1="148.00" y2="148.00" stroke="#21262d" stroke-width="1" stroke-dasharray="2 4" opacity="0.6"/><text x="64" y="151.00" text-anchor="end" fill="#58a6ff" opacity="0.85">75</text>
<line x1="72" x2="1128" y1="76.00" y2="76.00" stroke="#21262d" stroke-width="1" stroke-dasharray="2 4" opacity="0.6"/><text x="64" y="79.00" text-anchor="end" fill="#58a6ff" opacity="0.85">100</text>
</g>
<g font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" font-size="10" fill="#ff7b35" opacity="0.85" style="font-variant-numeric: tabular-nums;">
<text x="1136" y="367.00" text-anchor="start">0</text>
<text x="1136" y="295.00" text-anchor="start">250</text>
<text x="1136" y="223.00" text-anchor="start">500</text>
<text x="1136" y="151.00" text-anchor="start">750</text>
<text x="1136" y="79.00" text-anchor="start">1.0k</text>
</g>
<g font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" font-size="10" fill="#8b949e" style="font-variant-numeric: tabular-nums;">
<text x="72.00" y="382" text-anchor="middle">Feb 21</text>
<text x="289.90" y="382" text-anchor="middle">Mar 6</text>
<text x="491.05" y="382" text-anchor="middle">Mar 18</text>
<text x="708.95" y="382" text-anchor="middle">Mar 31</text>
<text x="910.10" y="382" text-anchor="middle">Apr 12</text>
<text x="1128.00" y="382" text-anchor="middle">Apr 25</text>
</g>
<line x1="72" x2="1128" y1="364" y2="364" stroke="#30363d" stroke-width="1" opacity="0.8"/>
<g clip-path="url(#plot-clip)">
<path d="M 72.00 358.24 C 74.79 358.24, 83.17 358.72, 88.76 358.24 C 94.35 357.76, 99.94 355.84, 105.52 355.36 C 111.11 354.88, 116.70 355.36, 122.29 355.36 C 127.87 355.36, 133.46 355.84, 139.05 355.36 C 144.63 354.88, 150.22 352.96, 155.81 352.48 C 161.40 352.00, 166.98 352.48, 172.57 352.48 C 178.16 352.48, 183.75 354.40, 189.33 352.48 C 194.92 350.56, 200.51 343.84, 206.10 340.96 C 211.68 338.08, 217.27 337.12, 222.86 335.20 C 228.44 333.28, 234.03 332.32, 239.62 329.44 C 245.21 326.56, 250.79 319.84, 256.38 317.92 C 261.97 316.00, 267.56 317.92, 273.14 317.92 C 278.73 317.92, 284.32 319.84, 289.90 317.92 C 295.49 316.00, 301.08 308.80, 306.67 306.40 C 312.25 304.00, 317.84 304.96, 323.43 303.52 C 329.02 302.08, 334.60 301.12, 340.19 297.76 C 345.78 294.40, 351.37 285.76, 356.95 283.36 C 362.54 280.96, 368.13 285.28, 373.71 283.36 C 379.30 281.44, 384.89 273.76, 390.48 271.84 C 396.06 269.92, 401.65 271.84, 407.24 271.84 C 412.83 271.84, 418.41 271.84, 424.00 271.84 C 429.59 271.84, 435.17 271.84, 440.76 271.84 C 446.35 271.84, 451.94 271.84, 457.52 271.84 C 463.11 271.84, 468.70 271.84, 474.29 271.84 C 479.87 271.84, 485.46 272.32, 491.05 271.84 C 496.63 271.36, 502.22 269.44, 507.81 268.96 C 513.40 268.48, 518.98 268.96, 524.57 268.96 C 530.16 268.96, 535.75 268.96, 541.33 268.96 C 546.92 268.96, 552.51 268.96, 558.10 268.96 C 563.68 268.96, 569.27 268.96, 574.86 268.96 C 580.44 268.96, 586.03 268.96, 591.62 268.96 C 597.21 268.96, 602.79 268.96, 608.38 268.96 C 613.97 268.96, 619.56 268.96, 625.14 268.96 C 630.73 268.96, 636.32 269.44, 641.90 268.96 C 647.49 268.48, 653.08 266.56, 658.67 266.08 C 664.25 265.60, 669.84 266.08, 675.43 266.08 C 681.02 266.08, 686.60 266.08, 692.19 266.08 C 697.78 266.08, 703.37 267.04, 708.95 266.08 C 714.54 265.12, 720.13 261.28, 725.71 260.32 C 731.30 259.36, 736.89 260.80, 742.48 260.32 C 748.06 259.84, 753.65 257.92, 759.24 257.44 C 764.83 256.96, 770.41 258.88, 776.00 257.44 C 781.59 256.00, 787.17 251.68, 792.76 248.80 C 798.35 245.92, 803.94 242.08, 809.52 240.16 C 815.11 238.24, 820.70 237.76, 826.29 237.28 C 831.87 236.80, 837.46 237.28, 843.05 237.28 C 848.63 237.28, 854.22 237.28, 859.81 237.28 C 865.40 237.28, 870.98 237.76, 876.57 237.28 C 882.16 236.80, 887.75 235.36, 893.33 234.40 C 898.92 233.44, 904.51 233.92, 910.10 231.52 C 915.68 229.12, 921.27 225.76, 926.86 220.00 C 932.44 214.24, 938.03 204.64, 943.62 196.96 C 949.21 189.28, 954.79 178.24, 960.38 173.92 C 965.97 169.60, 971.56 172.00, 977.14 171.04 C 982.73 170.08, 988.32 168.64, 993.90 168.16 C 999.49 167.68, 1005.08 168.16, 1010.67 168.16 C 1016.25 168.16, 1021.84 168.16, 1027.43 168.16 C 1033.02 168.16, 1038.60 169.12, 1044.19 168.16 C 1049.78 167.20, 1055.37 163.36, 1060.95 162.40 C 1066.54 161.44, 1072.13 162.40, 1077.71 162.40 C 1083.30 162.40, 1088.89 170.08, 1094.48 162.40 C 1100.06 154.72, 1105.65 125.44, 1111.24 116.32 C 1116.83 107.20, 1125.21 109.12, 1128.00 107.68 L 1128.00 364.00 L 72.00 364.00 Z" fill="url(#fill1)" stroke="none"/>
<path d="M 239.62 364.00 C 242.41 363.52, 250.79 362.03, 256.38 361.12 C 261.97 360.21, 267.56 359.44, 273.14 358.53 C 278.73 357.62, 284.32 356.56, 289.90 355.65 C 295.49 354.74, 301.08 353.97, 306.67 353.06 C 312.25 352.14, 317.84 351.14, 323.43 350.18 C 329.02 349.22, 334.60 348.21, 340.19 347.30 C 345.78 346.38, 351.37 345.62, 356.95 344.70 C 362.54 343.79, 368.13 342.78, 373.71 341.82 C 379.30 340.86, 384.89 339.86, 390.48 338.94 C 396.06 338.03, 401.65 337.26, 407.24 336.35 C 412.83 335.44, 418.41 334.38, 424.00 333.47 C 429.59 332.56, 435.17 331.79, 440.76 330.88 C 446.35 329.97, 451.94 328.96, 457.52 328.00 C 463.11 327.04, 468.70 326.03, 474.29 325.12 C 479.87 324.21, 485.46 323.44, 491.05 322.53 C 496.63 321.62, 502.22 320.61, 507.81 319.65 C 513.40 318.69, 518.98 317.68, 524.57 316.77 C 530.16 315.86, 535.75 315.09, 541.33 314.18 C 546.92 313.26, 552.51 312.21, 558.10 311.30 C 563.68 310.38, 569.27 309.62, 574.86 308.70 C 580.44 307.79, 586.03 306.78, 591.62 305.82 C 597.21 304.86, 602.79 303.86, 608.38 302.94 C 613.97 302.03, 619.56 301.26, 625.14 300.35 C 630.73 299.44, 636.32 298.43, 641.90 297.47 C 647.49 296.51, 653.08 295.50, 658.67 294.59 C 664.25 293.68, 669.84 292.91, 675.43 292.00 C 681.02 291.09, 686.60 290.03, 692.19 289.12 C 697.78 288.21, 703.37 287.44, 708.95 286.53 C 714.54 285.62, 720.13 284.61, 725.71 283.65 C 731.30 282.69, 736.89 281.68, 742.48 280.77 C 748.06 279.86, 753.65 279.09, 759.24 278.18 C 764.83 277.26, 770.41 276.26, 776.00 275.30 C 781.59 274.34, 787.17 273.33, 792.76 272.42 C 798.35 271.50, 803.94 270.74, 809.52 269.82 C 815.11 268.91, 820.70 267.86, 826.29 266.94 C 831.87 266.03, 837.46 265.26, 843.05 264.35 C 848.63 263.44, 854.22 262.43, 859.81 261.47 C 865.40 260.51, 870.98 259.50, 876.57 258.59 C 882.16 257.68, 887.75 256.91, 893.33 256.00 C 898.92 255.09, 904.51 254.08, 910.10 253.12 C 915.68 252.16, 921.27 251.15, 926.86 250.24 C 932.44 249.33, 938.03 248.56, 943.62 247.65 C 949.21 246.74, 954.79 245.68, 960.38 244.77 C 965.97 243.86, 971.56 243.09, 977.14 242.18 C 982.73 241.26, 988.32 240.26, 993.90 239.30 C 999.49 238.34, 1005.08 237.33, 1010.67 236.42 C 1016.25 235.50, 1021.84 234.74, 1027.43 233.82 C 1033.02 232.91, 1038.60 231.90, 1044.19 230.94 C 1049.78 229.98, 1055.37 228.98, 1060.95 228.06 C 1066.54 227.15, 1072.13 226.38, 1077.71 225.47 C 1083.30 224.56, 1088.89 223.50, 1094.48 222.59 C 1100.06 221.68, 1105.65 220.91, 1111.24 220.00 C 1116.83 219.09, 1125.21 217.60, 1128.00 217.12 L 1128.00 364.00 L 239.62 364.00 Z" fill="url(#fill2)" stroke="none"/>
<path d="M 72.00 358.24 C 74.79 358.24, 83.17 358.72, 88.76 358.24 C 94.35 357.76, 99.94 355.84, 105.52 355.36 C 111.11 354.88, 116.70 355.36, 122.29 355.36 C 127.87 355.36, 133.46 355.84, 139.05 355.36 C 144.63 354.88, 150.22 352.96, 155.81 352.48 C 161.40 352.00, 166.98 352.48, 172.57 352.48 C 178.16 352.48, 183.75 354.40, 189.33 352.48 C 194.92 350.56, 200.51 343.84, 206.10 340.96 C 211.68 338.08, 217.27 337.12, 222.86 335.20 C 228.44 333.28, 234.03 332.32, 239.62 329.44 C 245.21 326.56, 250.79 319.84, 256.38 317.92 C 261.97 316.00, 267.56 317.92, 273.14 317.92 C 278.73 317.92, 284.32 319.84, 289.90 317.92 C 295.49 316.00, 301.08 308.80, 306.67 306.40 C 312.25 304.00, 317.84 304.96, 323.43 303.52 C 329.02 302.08, 334.60 301.12, 340.19 297.76 C 345.78 294.40, 351.37 285.76, 356.95 283.36 C 362.54 280.96, 368.13 285.28, 373.71 283.36 C 379.30 281.44, 384.89 273.76, 390.48 271.84 C 396.06 269.92, 401.65 271.84, 407.24 271.84 C 412.83 271.84, 418.41 271.84, 424.00 271.84 C 429.59 271.84, 435.17 271.84, 440.76 271.84 C 446.35 271.84, 451.94 271.84, 457.52 271.84 C 463.11 271.84, 468.70 271.84, 474.29 271.84 C 479.87 271.84, 485.46 272.32, 491.05 271.84 C 496.63 271.36, 502.22 269.44, 507.81 268.96 C 513.40 268.48, 518.98 268.96, 524.57 268.96 C 530.16 268.96, 535.75 268.96, 541.33 268.96 C 546.92 268.96, 552.51 268.96, 558.10 268.96 C 563.68 268.96, 569.27 268.96, 574.86 268.96 C 580.44 268.96, 586.03 268.96, 591.62 268.96 C 597.21 268.96, 602.79 268.96, 608.38 268.96 C 613.97 268.96, 619.56 268.96, 625.14 268.96 C 630.73 268.96, 636.32 269.44, 641.90 268.96 C 647.49 268.48, 653.08 266.56, 658.67 266.08 C 664.25 265.60, 669.84 266.08, 675.43 266.08 C 681.02 266.08, 686.60 266.08, 692.19 266.08 C 697.78 266.08, 703.37 267.04, 708.95 266.08 C 714.54 265.12, 720.13 261.28, 725.71 260.32 C 731.30 259.36, 736.89 260.80, 742.48 260.32 C 748.06 259.84, 753.65 257.92, 759.24 257.44 C 764.83 256.96, 770.41 258.88, 776.00 257.44 C 781.59 256.00, 787.17 251.68, 792.76 248.80 C 798.35 245.92, 803.94 242.08, 809.52 240.16 C 815.11 238.24, 820.70 237.76, 826.29 237.28 C 831.87 236.80, 837.46 237.28, 843.05 237.28 C 848.63 237.28, 854.22 237.28, 859.81 237.28 C 865.40 237.28, 870.98 237.76, 876.57 237.28 C 882.16 236.80, 887.75 235.36, 893.33 234.40 C 898.92 233.44, 904.51 233.92, 910.10 231.52 C 915.68 229.12, 921.27 225.76, 926.86 220.00 C 932.44 214.24, 938.03 204.64, 943.62 196.96 C 949.21 189.28, 954.79 178.24, 960.38 173.92 C 965.97 169.60, 971.56 172.00, 977.14 171.04 C 982.73 170.08, 988.32 168.64, 993.90 168.16 C 999.49 167.68, 1005.08 168.16, 1010.67 168.16 C 1016.25 168.16, 1021.84 168.16, 1027.43 168.16 C 1033.02 168.16, 1038.60 169.12, 1044.19 168.16 C 1049.78 167.20, 1055.37 163.36, 1060.95 162.40 C 1066.54 161.44, 1072.13 162.40, 1077.71 162.40 C 1083.30 162.40, 1088.89 170.08, 1094.48 162.40 C 1100.06 154.72, 1105.65 125.44, 1111.24 116.32 C 1116.83 107.20, 1125.21 109.12, 1128.00 107.68" fill="none" stroke="url(#stroke1)" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" filter="url(#glow1)" pathLength="100" stroke-dasharray="100" stroke-dashoffset="100">
<animate attributeName="stroke-dashoffset" from="100" to="0" dur="1.4s" begin="0s" fill="freeze" calcMode="spline" keySplines="0.4 0 0.2 1" keyTimes="0;1" values="100;0"/>
</path>
<path d="M 239.62 364.00 C 242.41 363.52, 250.79 362.03, 256.38 361.12 C 261.97 360.21, 267.56 359.44, 273.14 358.53 C 278.73 357.62, 284.32 356.56, 289.90 355.65 C 295.49 354.74, 301.08 353.97, 306.67 353.06 C 312.25 352.14, 317.84 351.14, 323.43 350.18 C 329.02 349.22, 334.60 348.21, 340.19 347.30 C 345.78 346.38, 351.37 345.62, 356.95 344.70 C 362.54 343.79, 368.13 342.78, 373.71 341.82 C 379.30 340.86, 384.89 339.86, 390.48 338.94 C 396.06 338.03, 401.65 337.26, 407.24 336.35 C 412.83 335.44, 418.41 334.38, 424.00 333.47 C 429.59 332.56, 435.17 331.79, 440.76 330.88 C 446.35 329.97, 451.94 328.96, 457.52 328.00 C 463.11 327.04, 468.70 326.03, 474.29 325.12 C 479.87 324.21, 485.46 323.44, 491.05 322.53 C 496.63 321.62, 502.22 320.61, 507.81 319.65 C 513.40 318.69, 518.98 317.68, 524.57 316.77 C 530.16 315.86, 535.75 315.09, 541.33 314.18 C 546.92 313.26, 552.51 312.21, 558.10 311.30 C 563.68 310.38, 569.27 309.62, 574.86 308.70 C 580.44 307.79, 586.03 306.78, 591.62 305.82 C 597.21 304.86, 602.79 303.86, 608.38 302.94 C 613.97 302.03, 619.56 301.26, 625.14 300.35 C 630.73 299.44, 636.32 298.43, 641.90 297.47 C 647.49 296.51, 653.08 295.50, 658.67 294.59 C 664.25 293.68, 669.84 292.91, 675.43 292.00 C 681.02 291.09, 686.60 290.03, 692.19 289.12 C 697.78 288.21, 703.37 287.44, 708.95 286.53 C 714.54 285.62, 720.13 284.61, 725.71 283.65 C 731.30 282.69, 736.89 281.68, 742.48 280.77 C 748.06 279.86, 753.65 279.09, 759.24 278.18 C 764.83 277.26, 770.41 276.26, 776.00 275.30 C 781.59 274.34, 787.17 273.33, 792.76 272.42 C 798.35 271.50, 803.94 270.74, 809.52 269.82 C 815.11 268.91, 820.70 267.86, 826.29 266.94 C 831.87 266.03, 837.46 265.26, 843.05 264.35 C 848.63 263.44, 854.22 262.43, 859.81 261.47 C 865.40 260.51, 870.98 259.50, 876.57 258.59 C 882.16 257.68, 887.75 256.91, 893.33 256.00 C 898.92 255.09, 904.51 254.08, 910.10 253.12 C 915.68 252.16, 921.27 251.15, 926.86 250.24 C 932.44 249.33, 938.03 248.56, 943.62 247.65 C 949.21 246.74, 954.79 245.68, 960.38 244.77 C 965.97 243.86, 971.56 243.09, 977.14 242.18 C 982.73 241.26, 988.32 240.26, 993.90 239.30 C 999.49 238.34, 1005.08 237.33, 1010.67 236.42 C 1016.25 235.50, 1021.84 234.74, 1027.43 233.82 C 1033.02 232.91, 1038.60 231.90, 1044.19 230.94 C 1049.78 229.98, 1055.37 228.98, 1060.95 228.06 C 1066.54 227.15, 1072.13 226.38, 1077.71 225.47 C 1083.30 224.56, 1088.89 223.50, 1094.48 222.59 C 1100.06 221.68, 1105.65 220.91, 1111.24 220.00 C 1116.83 219.09, 1125.21 217.60, 1128.00 217.12" fill="none" stroke="url(#stroke2)" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" filter="url(#glow2)" pathLength="100" stroke-dasharray="100" stroke-dashoffset="100">
<animate attributeName="stroke-dashoffset" from="100" to="0" dur="1.4s" begin="0.15s" fill="freeze" calcMode="spline" keySplines="0.4 0 0.2 1" keyTimes="0;1" values="100;0"/>
</path>
<g opacity="0">
<circle cx="1128.00" cy="107.68" r="7" fill="#58a6ff" opacity="0.22"/>
<circle cx="1128.00" cy="107.68" r="4" fill="#0d1117" stroke="#58a6ff" stroke-width="2"/>
<animate attributeName="opacity" from="0" to="1" begin="1.4s" dur="0.4s" fill="freeze"/>
</g>
<g opacity="0">
<circle cx="1128.00" cy="217.12" r="7" fill="#ff7b35" opacity="0.22"/>
<circle cx="1128.00" cy="217.12" r="4" fill="#0d1117" stroke="#ff7b35" stroke-width="2"/>
<animate attributeName="opacity" from="0" to="1" begin="1.55s" dur="0.4s" fill="freeze"/>
</g>
</g>
<text x="1128" y="408" text-anchor="end" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" font-size="10" fill="#8b949e" opacity="0.7">
Updated 2026-04-25
</text>
</svg>

Before

Width:  |  Height:  |  Size: 18 KiB

View file

@ -1,111 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 420" width="1200" height="420" preserveAspectRatio="xMidYMid meet" role="img" aria-labelledby="title desc">
<title id="title">lahfir/agent-desktop — GitHub stars and ClawHub downloads over time</title>
<desc id="desc">Time-series chart showing GitHub stars and ClawHub downloads for lahfir/agent-desktop.</desc>
<defs>
<linearGradient id="fill1" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#0969da" stop-opacity="0.18"/>
<stop offset="100%" stop-color="#0969da" stop-opacity="0"/>
</linearGradient>
<linearGradient id="fill2" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#bc4c00" stop-opacity="0.18"/>
<stop offset="100%" stop-color="#bc4c00" stop-opacity="0"/>
</linearGradient>
<linearGradient id="stroke1" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#0969da"/>
<stop offset="100%" stop-color="#54aeff"/>
</linearGradient>
<linearGradient id="stroke2" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#bc4c00"/>
<stop offset="100%" stop-color="#fb8500"/>
</linearGradient>
<filter id="glow1" x="-10%" y="-50%" width="120%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="3" result="blur"/>
<feColorMatrix in="blur" type="matrix" values="0 0 0 0 0.035 0 0 0 0 0.412 0 0 0 0 0.855 0 0 0 0.18 0" result="coloredGlow"/>
<feMerge>
<feMergeNode in="coloredGlow"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<filter id="glow2" x="-10%" y="-50%" width="120%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="3" result="blur"/>
<feColorMatrix in="blur" type="matrix" values="0 0 0 0 0.737 0 0 0 0 0.298 0 0 0 0 0.000 0 0 0 0.18 0" result="coloredGlow"/>
<feMerge>
<feMergeNode in="coloredGlow"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<clipPath id="plot-clip">
<rect x="72" y="72" width="1056" height="296"/>
</clipPath>
</defs>
<rect width="100%" height="100%" fill="#ffffff"/>
<rect x="0.5" y="0.5" width="1199" height="419" fill="none" stroke="#d0d7de" stroke-width="1" rx="8" ry="8"/>
<text x="72" y="30" fill="#1f2328" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" font-size="18" font-weight="600">
lahfir/agent-desktop
</text>
<text x="72" y="50" fill="#656d76" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" font-size="12" font-weight="400">
GitHub stars · ClawHub downloads — updated daily
</text>
<g transform="translate(1128, 30)" text-anchor="end" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" font-size="14" font-weight="600" style="font-variant-numeric: tabular-nums;">
<text fill="#0969da">★ 89 <tspan fill="#656d76" font-weight="400" font-size="11">GitHub</tspan></text>
<text y="20" fill="#bc4c00">⬇ 510 <tspan fill="#656d76" font-weight="400" font-size="11">ClawHub</tspan></text>
</g>
<g font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" font-size="10" fill="#656d76" style="font-variant-numeric: tabular-nums;">
<text x="64" y="367.00" text-anchor="end" fill="#0969da" opacity="0.85">0</text>
<line x1="72" x2="1128" y1="292.00" y2="292.00" stroke="#d0d7de" stroke-width="1" stroke-dasharray="2 4" opacity="0.6"/><text x="64" y="295.00" text-anchor="end" fill="#0969da" opacity="0.85">25</text>
<line x1="72" x2="1128" y1="220.00" y2="220.00" stroke="#d0d7de" stroke-width="1" stroke-dasharray="2 4" opacity="0.6"/><text x="64" y="223.00" text-anchor="end" fill="#0969da" opacity="0.85">50</text>
<line x1="72" x2="1128" y1="148.00" y2="148.00" stroke="#d0d7de" stroke-width="1" stroke-dasharray="2 4" opacity="0.6"/><text x="64" y="151.00" text-anchor="end" fill="#0969da" opacity="0.85">75</text>
<line x1="72" x2="1128" y1="76.00" y2="76.00" stroke="#d0d7de" stroke-width="1" stroke-dasharray="2 4" opacity="0.6"/><text x="64" y="79.00" text-anchor="end" fill="#0969da" opacity="0.85">100</text>
</g>
<g font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" font-size="10" fill="#bc4c00" opacity="0.85" style="font-variant-numeric: tabular-nums;">
<text x="1136" y="367.00" text-anchor="start">0</text>
<text x="1136" y="295.00" text-anchor="start">250</text>
<text x="1136" y="223.00" text-anchor="start">500</text>
<text x="1136" y="151.00" text-anchor="start">750</text>
<text x="1136" y="79.00" text-anchor="start">1.0k</text>
</g>
<g font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" font-size="10" fill="#656d76" style="font-variant-numeric: tabular-nums;">
<text x="72.00" y="382" text-anchor="middle">Feb 21</text>
<text x="289.90" y="382" text-anchor="middle">Mar 6</text>
<text x="491.05" y="382" text-anchor="middle">Mar 18</text>
<text x="708.95" y="382" text-anchor="middle">Mar 31</text>
<text x="910.10" y="382" text-anchor="middle">Apr 12</text>
<text x="1128.00" y="382" text-anchor="middle">Apr 25</text>
</g>
<line x1="72" x2="1128" y1="364" y2="364" stroke="#d0d7de" stroke-width="1" opacity="0.8"/>
<g clip-path="url(#plot-clip)">
<path d="M 72.00 358.24 C 74.79 358.24, 83.17 358.72, 88.76 358.24 C 94.35 357.76, 99.94 355.84, 105.52 355.36 C 111.11 354.88, 116.70 355.36, 122.29 355.36 C 127.87 355.36, 133.46 355.84, 139.05 355.36 C 144.63 354.88, 150.22 352.96, 155.81 352.48 C 161.40 352.00, 166.98 352.48, 172.57 352.48 C 178.16 352.48, 183.75 354.40, 189.33 352.48 C 194.92 350.56, 200.51 343.84, 206.10 340.96 C 211.68 338.08, 217.27 337.12, 222.86 335.20 C 228.44 333.28, 234.03 332.32, 239.62 329.44 C 245.21 326.56, 250.79 319.84, 256.38 317.92 C 261.97 316.00, 267.56 317.92, 273.14 317.92 C 278.73 317.92, 284.32 319.84, 289.90 317.92 C 295.49 316.00, 301.08 308.80, 306.67 306.40 C 312.25 304.00, 317.84 304.96, 323.43 303.52 C 329.02 302.08, 334.60 301.12, 340.19 297.76 C 345.78 294.40, 351.37 285.76, 356.95 283.36 C 362.54 280.96, 368.13 285.28, 373.71 283.36 C 379.30 281.44, 384.89 273.76, 390.48 271.84 C 396.06 269.92, 401.65 271.84, 407.24 271.84 C 412.83 271.84, 418.41 271.84, 424.00 271.84 C 429.59 271.84, 435.17 271.84, 440.76 271.84 C 446.35 271.84, 451.94 271.84, 457.52 271.84 C 463.11 271.84, 468.70 271.84, 474.29 271.84 C 479.87 271.84, 485.46 272.32, 491.05 271.84 C 496.63 271.36, 502.22 269.44, 507.81 268.96 C 513.40 268.48, 518.98 268.96, 524.57 268.96 C 530.16 268.96, 535.75 268.96, 541.33 268.96 C 546.92 268.96, 552.51 268.96, 558.10 268.96 C 563.68 268.96, 569.27 268.96, 574.86 268.96 C 580.44 268.96, 586.03 268.96, 591.62 268.96 C 597.21 268.96, 602.79 268.96, 608.38 268.96 C 613.97 268.96, 619.56 268.96, 625.14 268.96 C 630.73 268.96, 636.32 269.44, 641.90 268.96 C 647.49 268.48, 653.08 266.56, 658.67 266.08 C 664.25 265.60, 669.84 266.08, 675.43 266.08 C 681.02 266.08, 686.60 266.08, 692.19 266.08 C 697.78 266.08, 703.37 267.04, 708.95 266.08 C 714.54 265.12, 720.13 261.28, 725.71 260.32 C 731.30 259.36, 736.89 260.80, 742.48 260.32 C 748.06 259.84, 753.65 257.92, 759.24 257.44 C 764.83 256.96, 770.41 258.88, 776.00 257.44 C 781.59 256.00, 787.17 251.68, 792.76 248.80 C 798.35 245.92, 803.94 242.08, 809.52 240.16 C 815.11 238.24, 820.70 237.76, 826.29 237.28 C 831.87 236.80, 837.46 237.28, 843.05 237.28 C 848.63 237.28, 854.22 237.28, 859.81 237.28 C 865.40 237.28, 870.98 237.76, 876.57 237.28 C 882.16 236.80, 887.75 235.36, 893.33 234.40 C 898.92 233.44, 904.51 233.92, 910.10 231.52 C 915.68 229.12, 921.27 225.76, 926.86 220.00 C 932.44 214.24, 938.03 204.64, 943.62 196.96 C 949.21 189.28, 954.79 178.24, 960.38 173.92 C 965.97 169.60, 971.56 172.00, 977.14 171.04 C 982.73 170.08, 988.32 168.64, 993.90 168.16 C 999.49 167.68, 1005.08 168.16, 1010.67 168.16 C 1016.25 168.16, 1021.84 168.16, 1027.43 168.16 C 1033.02 168.16, 1038.60 169.12, 1044.19 168.16 C 1049.78 167.20, 1055.37 163.36, 1060.95 162.40 C 1066.54 161.44, 1072.13 162.40, 1077.71 162.40 C 1083.30 162.40, 1088.89 170.08, 1094.48 162.40 C 1100.06 154.72, 1105.65 125.44, 1111.24 116.32 C 1116.83 107.20, 1125.21 109.12, 1128.00 107.68 L 1128.00 364.00 L 72.00 364.00 Z" fill="url(#fill1)" stroke="none"/>
<path d="M 239.62 364.00 C 242.41 363.52, 250.79 362.03, 256.38 361.12 C 261.97 360.21, 267.56 359.44, 273.14 358.53 C 278.73 357.62, 284.32 356.56, 289.90 355.65 C 295.49 354.74, 301.08 353.97, 306.67 353.06 C 312.25 352.14, 317.84 351.14, 323.43 350.18 C 329.02 349.22, 334.60 348.21, 340.19 347.30 C 345.78 346.38, 351.37 345.62, 356.95 344.70 C 362.54 343.79, 368.13 342.78, 373.71 341.82 C 379.30 340.86, 384.89 339.86, 390.48 338.94 C 396.06 338.03, 401.65 337.26, 407.24 336.35 C 412.83 335.44, 418.41 334.38, 424.00 333.47 C 429.59 332.56, 435.17 331.79, 440.76 330.88 C 446.35 329.97, 451.94 328.96, 457.52 328.00 C 463.11 327.04, 468.70 326.03, 474.29 325.12 C 479.87 324.21, 485.46 323.44, 491.05 322.53 C 496.63 321.62, 502.22 320.61, 507.81 319.65 C 513.40 318.69, 518.98 317.68, 524.57 316.77 C 530.16 315.86, 535.75 315.09, 541.33 314.18 C 546.92 313.26, 552.51 312.21, 558.10 311.30 C 563.68 310.38, 569.27 309.62, 574.86 308.70 C 580.44 307.79, 586.03 306.78, 591.62 305.82 C 597.21 304.86, 602.79 303.86, 608.38 302.94 C 613.97 302.03, 619.56 301.26, 625.14 300.35 C 630.73 299.44, 636.32 298.43, 641.90 297.47 C 647.49 296.51, 653.08 295.50, 658.67 294.59 C 664.25 293.68, 669.84 292.91, 675.43 292.00 C 681.02 291.09, 686.60 290.03, 692.19 289.12 C 697.78 288.21, 703.37 287.44, 708.95 286.53 C 714.54 285.62, 720.13 284.61, 725.71 283.65 C 731.30 282.69, 736.89 281.68, 742.48 280.77 C 748.06 279.86, 753.65 279.09, 759.24 278.18 C 764.83 277.26, 770.41 276.26, 776.00 275.30 C 781.59 274.34, 787.17 273.33, 792.76 272.42 C 798.35 271.50, 803.94 270.74, 809.52 269.82 C 815.11 268.91, 820.70 267.86, 826.29 266.94 C 831.87 266.03, 837.46 265.26, 843.05 264.35 C 848.63 263.44, 854.22 262.43, 859.81 261.47 C 865.40 260.51, 870.98 259.50, 876.57 258.59 C 882.16 257.68, 887.75 256.91, 893.33 256.00 C 898.92 255.09, 904.51 254.08, 910.10 253.12 C 915.68 252.16, 921.27 251.15, 926.86 250.24 C 932.44 249.33, 938.03 248.56, 943.62 247.65 C 949.21 246.74, 954.79 245.68, 960.38 244.77 C 965.97 243.86, 971.56 243.09, 977.14 242.18 C 982.73 241.26, 988.32 240.26, 993.90 239.30 C 999.49 238.34, 1005.08 237.33, 1010.67 236.42 C 1016.25 235.50, 1021.84 234.74, 1027.43 233.82 C 1033.02 232.91, 1038.60 231.90, 1044.19 230.94 C 1049.78 229.98, 1055.37 228.98, 1060.95 228.06 C 1066.54 227.15, 1072.13 226.38, 1077.71 225.47 C 1083.30 224.56, 1088.89 223.50, 1094.48 222.59 C 1100.06 221.68, 1105.65 220.91, 1111.24 220.00 C 1116.83 219.09, 1125.21 217.60, 1128.00 217.12 L 1128.00 364.00 L 239.62 364.00 Z" fill="url(#fill2)" stroke="none"/>
<path d="M 72.00 358.24 C 74.79 358.24, 83.17 358.72, 88.76 358.24 C 94.35 357.76, 99.94 355.84, 105.52 355.36 C 111.11 354.88, 116.70 355.36, 122.29 355.36 C 127.87 355.36, 133.46 355.84, 139.05 355.36 C 144.63 354.88, 150.22 352.96, 155.81 352.48 C 161.40 352.00, 166.98 352.48, 172.57 352.48 C 178.16 352.48, 183.75 354.40, 189.33 352.48 C 194.92 350.56, 200.51 343.84, 206.10 340.96 C 211.68 338.08, 217.27 337.12, 222.86 335.20 C 228.44 333.28, 234.03 332.32, 239.62 329.44 C 245.21 326.56, 250.79 319.84, 256.38 317.92 C 261.97 316.00, 267.56 317.92, 273.14 317.92 C 278.73 317.92, 284.32 319.84, 289.90 317.92 C 295.49 316.00, 301.08 308.80, 306.67 306.40 C 312.25 304.00, 317.84 304.96, 323.43 303.52 C 329.02 302.08, 334.60 301.12, 340.19 297.76 C 345.78 294.40, 351.37 285.76, 356.95 283.36 C 362.54 280.96, 368.13 285.28, 373.71 283.36 C 379.30 281.44, 384.89 273.76, 390.48 271.84 C 396.06 269.92, 401.65 271.84, 407.24 271.84 C 412.83 271.84, 418.41 271.84, 424.00 271.84 C 429.59 271.84, 435.17 271.84, 440.76 271.84 C 446.35 271.84, 451.94 271.84, 457.52 271.84 C 463.11 271.84, 468.70 271.84, 474.29 271.84 C 479.87 271.84, 485.46 272.32, 491.05 271.84 C 496.63 271.36, 502.22 269.44, 507.81 268.96 C 513.40 268.48, 518.98 268.96, 524.57 268.96 C 530.16 268.96, 535.75 268.96, 541.33 268.96 C 546.92 268.96, 552.51 268.96, 558.10 268.96 C 563.68 268.96, 569.27 268.96, 574.86 268.96 C 580.44 268.96, 586.03 268.96, 591.62 268.96 C 597.21 268.96, 602.79 268.96, 608.38 268.96 C 613.97 268.96, 619.56 268.96, 625.14 268.96 C 630.73 268.96, 636.32 269.44, 641.90 268.96 C 647.49 268.48, 653.08 266.56, 658.67 266.08 C 664.25 265.60, 669.84 266.08, 675.43 266.08 C 681.02 266.08, 686.60 266.08, 692.19 266.08 C 697.78 266.08, 703.37 267.04, 708.95 266.08 C 714.54 265.12, 720.13 261.28, 725.71 260.32 C 731.30 259.36, 736.89 260.80, 742.48 260.32 C 748.06 259.84, 753.65 257.92, 759.24 257.44 C 764.83 256.96, 770.41 258.88, 776.00 257.44 C 781.59 256.00, 787.17 251.68, 792.76 248.80 C 798.35 245.92, 803.94 242.08, 809.52 240.16 C 815.11 238.24, 820.70 237.76, 826.29 237.28 C 831.87 236.80, 837.46 237.28, 843.05 237.28 C 848.63 237.28, 854.22 237.28, 859.81 237.28 C 865.40 237.28, 870.98 237.76, 876.57 237.28 C 882.16 236.80, 887.75 235.36, 893.33 234.40 C 898.92 233.44, 904.51 233.92, 910.10 231.52 C 915.68 229.12, 921.27 225.76, 926.86 220.00 C 932.44 214.24, 938.03 204.64, 943.62 196.96 C 949.21 189.28, 954.79 178.24, 960.38 173.92 C 965.97 169.60, 971.56 172.00, 977.14 171.04 C 982.73 170.08, 988.32 168.64, 993.90 168.16 C 999.49 167.68, 1005.08 168.16, 1010.67 168.16 C 1016.25 168.16, 1021.84 168.16, 1027.43 168.16 C 1033.02 168.16, 1038.60 169.12, 1044.19 168.16 C 1049.78 167.20, 1055.37 163.36, 1060.95 162.40 C 1066.54 161.44, 1072.13 162.40, 1077.71 162.40 C 1083.30 162.40, 1088.89 170.08, 1094.48 162.40 C 1100.06 154.72, 1105.65 125.44, 1111.24 116.32 C 1116.83 107.20, 1125.21 109.12, 1128.00 107.68" fill="none" stroke="url(#stroke1)" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" filter="url(#glow1)" pathLength="100" stroke-dasharray="100" stroke-dashoffset="100">
<animate attributeName="stroke-dashoffset" from="100" to="0" dur="1.4s" begin="0s" fill="freeze" calcMode="spline" keySplines="0.4 0 0.2 1" keyTimes="0;1" values="100;0"/>
</path>
<path d="M 239.62 364.00 C 242.41 363.52, 250.79 362.03, 256.38 361.12 C 261.97 360.21, 267.56 359.44, 273.14 358.53 C 278.73 357.62, 284.32 356.56, 289.90 355.65 C 295.49 354.74, 301.08 353.97, 306.67 353.06 C 312.25 352.14, 317.84 351.14, 323.43 350.18 C 329.02 349.22, 334.60 348.21, 340.19 347.30 C 345.78 346.38, 351.37 345.62, 356.95 344.70 C 362.54 343.79, 368.13 342.78, 373.71 341.82 C 379.30 340.86, 384.89 339.86, 390.48 338.94 C 396.06 338.03, 401.65 337.26, 407.24 336.35 C 412.83 335.44, 418.41 334.38, 424.00 333.47 C 429.59 332.56, 435.17 331.79, 440.76 330.88 C 446.35 329.97, 451.94 328.96, 457.52 328.00 C 463.11 327.04, 468.70 326.03, 474.29 325.12 C 479.87 324.21, 485.46 323.44, 491.05 322.53 C 496.63 321.62, 502.22 320.61, 507.81 319.65 C 513.40 318.69, 518.98 317.68, 524.57 316.77 C 530.16 315.86, 535.75 315.09, 541.33 314.18 C 546.92 313.26, 552.51 312.21, 558.10 311.30 C 563.68 310.38, 569.27 309.62, 574.86 308.70 C 580.44 307.79, 586.03 306.78, 591.62 305.82 C 597.21 304.86, 602.79 303.86, 608.38 302.94 C 613.97 302.03, 619.56 301.26, 625.14 300.35 C 630.73 299.44, 636.32 298.43, 641.90 297.47 C 647.49 296.51, 653.08 295.50, 658.67 294.59 C 664.25 293.68, 669.84 292.91, 675.43 292.00 C 681.02 291.09, 686.60 290.03, 692.19 289.12 C 697.78 288.21, 703.37 287.44, 708.95 286.53 C 714.54 285.62, 720.13 284.61, 725.71 283.65 C 731.30 282.69, 736.89 281.68, 742.48 280.77 C 748.06 279.86, 753.65 279.09, 759.24 278.18 C 764.83 277.26, 770.41 276.26, 776.00 275.30 C 781.59 274.34, 787.17 273.33, 792.76 272.42 C 798.35 271.50, 803.94 270.74, 809.52 269.82 C 815.11 268.91, 820.70 267.86, 826.29 266.94 C 831.87 266.03, 837.46 265.26, 843.05 264.35 C 848.63 263.44, 854.22 262.43, 859.81 261.47 C 865.40 260.51, 870.98 259.50, 876.57 258.59 C 882.16 257.68, 887.75 256.91, 893.33 256.00 C 898.92 255.09, 904.51 254.08, 910.10 253.12 C 915.68 252.16, 921.27 251.15, 926.86 250.24 C 932.44 249.33, 938.03 248.56, 943.62 247.65 C 949.21 246.74, 954.79 245.68, 960.38 244.77 C 965.97 243.86, 971.56 243.09, 977.14 242.18 C 982.73 241.26, 988.32 240.26, 993.90 239.30 C 999.49 238.34, 1005.08 237.33, 1010.67 236.42 C 1016.25 235.50, 1021.84 234.74, 1027.43 233.82 C 1033.02 232.91, 1038.60 231.90, 1044.19 230.94 C 1049.78 229.98, 1055.37 228.98, 1060.95 228.06 C 1066.54 227.15, 1072.13 226.38, 1077.71 225.47 C 1083.30 224.56, 1088.89 223.50, 1094.48 222.59 C 1100.06 221.68, 1105.65 220.91, 1111.24 220.00 C 1116.83 219.09, 1125.21 217.60, 1128.00 217.12" fill="none" stroke="url(#stroke2)" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" filter="url(#glow2)" pathLength="100" stroke-dasharray="100" stroke-dashoffset="100">
<animate attributeName="stroke-dashoffset" from="100" to="0" dur="1.4s" begin="0.15s" fill="freeze" calcMode="spline" keySplines="0.4 0 0.2 1" keyTimes="0;1" values="100;0"/>
</path>
<g opacity="0">
<circle cx="1128.00" cy="107.68" r="7" fill="#0969da" opacity="0.22"/>
<circle cx="1128.00" cy="107.68" r="4" fill="#ffffff" stroke="#0969da" stroke-width="2"/>
<animate attributeName="opacity" from="0" to="1" begin="1.4s" dur="0.4s" fill="freeze"/>
</g>
<g opacity="0">
<circle cx="1128.00" cy="217.12" r="7" fill="#bc4c00" opacity="0.22"/>
<circle cx="1128.00" cy="217.12" r="4" fill="#ffffff" stroke="#bc4c00" stroke-width="2"/>
<animate attributeName="opacity" from="0" to="1" begin="1.55s" dur="0.4s" fill="freeze"/>
</g>
</g>
<text x="1128" y="408" text-anchor="end" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" font-size="10" fill="#656d76" opacity="0.7">
Updated 2026-04-25
</text>
</svg>

Before

Width:  |  Height:  |  Size: 18 KiB

View file

@ -1,365 +0,0 @@
#!/usr/bin/env node
import { readFile, writeFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
const __dirname = dirname(fileURLToPath(import.meta.url));
const HISTORY_PATH = join(__dirname, "history.json");
const OUT_LIGHT = join(__dirname, "chart-light.svg");
const OUT_DARK = join(__dirname, "chart-dark.svg");
const W = 1200;
const H = 420;
const PAD = { top: 76, right: 72, bottom: 56, left: 72 };
const PLOT_W = W - PAD.left - PAD.right;
const PLOT_H = H - PAD.top - PAD.bottom;
const THEMES = {
light: {
bg: "#ffffff",
surface: "#f6f8fa",
grid: "#d0d7de",
gridSoft: "#d0d7de",
axis: "#656d76",
text: "#1f2328",
textSoft: "#656d76",
titleAccent: "#0969da",
line1: "#0969da",
line1End: "#54aeff",
line2: "#bc4c00",
line2End: "#fb8500",
glowOpacity: 0.18,
fillOpacityTop: 0.18,
fillOpacityBottom: 0.0,
cardBorder: "#d0d7de",
},
dark: {
bg: "#0d1117",
surface: "#161b22",
grid: "#30363d",
gridSoft: "#21262d",
axis: "#8b949e",
text: "#e6edf3",
textSoft: "#8b949e",
titleAccent: "#58a6ff",
line1: "#58a6ff",
line1End: "#a5d6ff",
line2: "#ff7b35",
line2End: "#ffa657",
glowOpacity: 0.32,
fillOpacityTop: 0.28,
fillOpacityBottom: 0.0,
cardBorder: "#30363d",
},
};
function fmtNumber(n) {
if (n >= 1e6) return (n / 1e6).toFixed(n >= 1e7 ? 0 : 1) + "M";
if (n >= 1e4) return (n / 1e3).toFixed(0) + "k";
if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
return String(Math.round(n));
}
function fmtDateShort(iso) {
const d = new Date(iso + "T00:00:00Z");
return d.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
timeZone: "UTC",
});
}
function niceMax(v) {
if (v <= 0) return 10;
const p = Math.pow(10, Math.floor(Math.log10(v)));
const f = v / p;
let nice;
if (f <= 1) nice = 1;
else if (f <= 2) nice = 2;
else if (f <= 5) nice = 5;
else nice = 10;
return nice * p;
}
function dateToX(iso, minDay, maxDay) {
const d = new Date(iso + "T00:00:00Z").getTime() / 86400000;
const t = (d - minDay) / Math.max(1, maxDay - minDay);
return PAD.left + t * PLOT_W;
}
function valToY(v, maxV) {
const t = v / Math.max(1, maxV);
return PAD.top + PLOT_H - t * PLOT_H;
}
function catmullRomPath(points) {
if (points.length === 0) return "";
if (points.length === 1) {
const [x, y] = points[0];
return `M ${x.toFixed(2)} ${y.toFixed(2)}`;
}
let d = `M ${points[0][0].toFixed(2)} ${points[0][1].toFixed(2)}`;
for (let i = 0; i < points.length - 1; i++) {
const p0 = points[i - 1] || points[i];
const p1 = points[i];
const p2 = points[i + 1];
const p3 = points[i + 2] || p2;
const cp1x = p1[0] + (p2[0] - p0[0]) / 6;
const cp1y = p1[1] + (p2[1] - p0[1]) / 6;
const cp2x = p2[0] - (p3[0] - p1[0]) / 6;
const cp2y = p2[1] - (p3[1] - p1[1]) / 6;
d += ` C ${cp1x.toFixed(2)} ${cp1y.toFixed(2)}, ${cp2x.toFixed(2)} ${cp2y.toFixed(2)}, ${p2[0].toFixed(2)} ${p2[1].toFixed(2)}`;
}
return d;
}
function pathLength(points) {
let len = 0;
for (let i = 1; i < points.length; i++) {
const dx = points[i][0] - points[i - 1][0];
const dy = points[i][1] - points[i - 1][1];
len += Math.sqrt(dx * dx + dy * dy);
}
return Math.max(len, 1);
}
function gridTicks(maxV, count = 4) {
const step = maxV / count;
return Array.from({ length: count + 1 }, (_, i) => i * step);
}
function dateTicks(minDay, maxDay, count = 5) {
const span = maxDay - minDay;
const step = span / count;
return Array.from({ length: count + 1 }, (_, i) => {
const day = Math.round(minDay + i * step);
const iso = new Date(day * 86400000).toISOString().slice(0, 10);
return iso;
});
}
function renderChart(theme, history, meta) {
const t = THEMES[theme];
const series1 = history.github_stars; // [[iso, n], ...]
const series2 = history.clawhub_downloads;
const allDates = [...series1, ...series2].map(([iso]) => iso);
const minIso = allDates.reduce((a, b) => (a < b ? a : b));
const maxIso = allDates.reduce((a, b) => (a > b ? a : b));
const minDay = new Date(minIso + "T00:00:00Z").getTime() / 86400000;
const todayDay = new Date(maxIso + "T00:00:00Z").getTime() / 86400000;
const maxDay = todayDay; // x-axis ends today
const max1 = niceMax(Math.max(...series1.map(([, v]) => v), 1));
const max2 = niceMax(Math.max(...series2.map(([, v]) => v), 1));
const pts1 = series1.map(([iso, v]) => [
dateToX(iso, minDay, maxDay),
valToY(v, max1),
]);
const pts2 = series2.map(([iso, v]) => [
dateToX(iso, minDay, maxDay),
valToY(v, max2),
]);
const line1Path = catmullRomPath(pts1);
const line2Path = catmullRomPath(pts2);
const area1Path =
pts1.length >= 2
? `${line1Path} L ${pts1[pts1.length - 1][0].toFixed(2)} ${(PAD.top + PLOT_H).toFixed(2)} L ${pts1[0][0].toFixed(2)} ${(PAD.top + PLOT_H).toFixed(2)} Z`
: "";
const area2Path =
pts2.length >= 2
? `${line2Path} L ${pts2[pts2.length - 1][0].toFixed(2)} ${(PAD.top + PLOT_H).toFixed(2)} L ${pts2[0][0].toFixed(2)} ${(PAD.top + PLOT_H).toFixed(2)} Z`
: "";
const yTicks1 = gridTicks(max1);
const yTicks2 = gridTicks(max2);
const xTicks = dateTicks(minDay, maxDay);
const last1 = series1[series1.length - 1]?.[1] ?? 0;
const last2 = series2[series2.length - 1]?.[1] ?? 0;
const titleY = 30;
const subtitleY = 50;
const lastPt1 = pts1[pts1.length - 1];
const lastPt2 = pts2[pts2.length - 1];
const FONT =
"-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif";
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}" preserveAspectRatio="xMidYMid meet" role="img" aria-labelledby="title desc">
<title id="title">${meta.repo} GitHub stars and ClawHub downloads over time</title>
<desc id="desc">Time-series chart showing GitHub stars and ClawHub downloads for ${meta.repo}.</desc>
<defs>
<linearGradient id="fill1" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="${t.line1}" stop-opacity="${t.fillOpacityTop}"/>
<stop offset="100%" stop-color="${t.line1}" stop-opacity="${t.fillOpacityBottom}"/>
</linearGradient>
<linearGradient id="fill2" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="${t.line2}" stop-opacity="${t.fillOpacityTop}"/>
<stop offset="100%" stop-color="${t.line2}" stop-opacity="${t.fillOpacityBottom}"/>
</linearGradient>
<linearGradient id="stroke1" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="${t.line1}"/>
<stop offset="100%" stop-color="${t.line1End}"/>
</linearGradient>
<linearGradient id="stroke2" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="${t.line2}"/>
<stop offset="100%" stop-color="${t.line2End}"/>
</linearGradient>
<filter id="glow1" x="-10%" y="-50%" width="120%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="3" result="blur"/>
<feColorMatrix in="blur" type="matrix" values="0 0 0 0 ${hex(t.line1, "r")} 0 0 0 0 ${hex(t.line1, "g")} 0 0 0 0 ${hex(t.line1, "b")} 0 0 0 ${t.glowOpacity} 0" result="coloredGlow"/>
<feMerge>
<feMergeNode in="coloredGlow"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<filter id="glow2" x="-10%" y="-50%" width="120%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="3" result="blur"/>
<feColorMatrix in="blur" type="matrix" values="0 0 0 0 ${hex(t.line2, "r")} 0 0 0 0 ${hex(t.line2, "g")} 0 0 0 0 ${hex(t.line2, "b")} 0 0 0 ${t.glowOpacity} 0" result="coloredGlow"/>
<feMerge>
<feMergeNode in="coloredGlow"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<clipPath id="plot-clip">
<rect x="${PAD.left}" y="${PAD.top - 4}" width="${PLOT_W}" height="${PLOT_H + 8}"/>
</clipPath>
</defs>
<rect width="100%" height="100%" fill="${t.bg}"/>
<rect x="0.5" y="0.5" width="${W - 1}" height="${H - 1}" fill="none" stroke="${t.cardBorder}" stroke-width="1" rx="8" ry="8"/>
<text x="${PAD.left}" y="${titleY}" fill="${t.text}" font-family="${FONT}" font-size="18" font-weight="600">
${meta.repo}
</text>
<text x="${PAD.left}" y="${subtitleY}" fill="${t.textSoft}" font-family="${FONT}" font-size="12" font-weight="400">
${meta.subtitle}
</text>
<g transform="translate(${W - PAD.right}, ${titleY})" text-anchor="end" font-family="${FONT}" font-size="14" font-weight="600" style="font-variant-numeric: tabular-nums;">
<text fill="${t.line1}"> ${fmtNumber(last1)} <tspan fill="${t.textSoft}" font-weight="400" font-size="11">GitHub</tspan></text>
<text y="20" fill="${t.line2}"> ${fmtNumber(last2)} <tspan fill="${t.textSoft}" font-weight="400" font-size="11">ClawHub</tspan></text>
</g>
<g font-family="${FONT}" font-size="10" fill="${t.textSoft}" style="font-variant-numeric: tabular-nums;">
${yTicks1
.map((v, i) => {
const y = valToY(v, max1);
const gridLine =
i === 0
? ""
: `<line x1="${PAD.left}" x2="${W - PAD.right}" y1="${y.toFixed(2)}" y2="${y.toFixed(2)}" stroke="${t.gridSoft}" stroke-width="1" stroke-dasharray="2 4" opacity="0.6"/>`;
return `${gridLine}<text x="${PAD.left - 8}" y="${(y + 3).toFixed(2)}" text-anchor="end" fill="${t.line1}" opacity="0.85">${fmtNumber(v)}</text>`;
})
.join("\n ")}
</g>
<g font-family="${FONT}" font-size="10" fill="${t.line2}" opacity="0.85" style="font-variant-numeric: tabular-nums;">
${yTicks2
.map((v) => {
const y = valToY(v, max2);
return `<text x="${W - PAD.right + 8}" y="${(y + 3).toFixed(2)}" text-anchor="start">${fmtNumber(v)}</text>`;
})
.join("\n ")}
</g>
<g font-family="${FONT}" font-size="10" fill="${t.textSoft}" style="font-variant-numeric: tabular-nums;">
${xTicks
.map((iso) => {
const x = dateToX(iso, minDay, maxDay);
return `<text x="${x.toFixed(2)}" y="${H - PAD.bottom + 18}" text-anchor="middle">${fmtDateShort(iso)}</text>`;
})
.join("\n ")}
</g>
<line x1="${PAD.left}" x2="${W - PAD.right}" y1="${PAD.top + PLOT_H}" y2="${PAD.top + PLOT_H}" stroke="${t.grid}" stroke-width="1" opacity="0.8"/>
<g clip-path="url(#plot-clip)">
${area1Path ? `<path d="${area1Path}" fill="url(#fill1)" stroke="none"/>` : ""}
${area2Path ? `<path d="${area2Path}" fill="url(#fill2)" stroke="none"/>` : ""}
${
pts1.length >= 2
? `<path d="${line1Path}" fill="none" stroke="url(#stroke1)" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" filter="url(#glow1)" pathLength="100" stroke-dasharray="100" stroke-dashoffset="100">
<animate attributeName="stroke-dashoffset" from="100" to="0" dur="1.4s" begin="0s" fill="freeze" calcMode="spline" keySplines="0.4 0 0.2 1" keyTimes="0;1" values="100;0"/>
</path>`
: ""
}
${
pts2.length >= 2
? `<path d="${line2Path}" fill="none" stroke="url(#stroke2)" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" filter="url(#glow2)" pathLength="100" stroke-dasharray="100" stroke-dashoffset="100">
<animate attributeName="stroke-dashoffset" from="100" to="0" dur="1.4s" begin="0.15s" fill="freeze" calcMode="spline" keySplines="0.4 0 0.2 1" keyTimes="0;1" values="100;0"/>
</path>`
: ""
}
${
lastPt1
? `<g opacity="0">
<circle cx="${lastPt1[0].toFixed(2)}" cy="${lastPt1[1].toFixed(2)}" r="7" fill="${t.line1}" opacity="0.22"/>
<circle cx="${lastPt1[0].toFixed(2)}" cy="${lastPt1[1].toFixed(2)}" r="4" fill="${t.bg}" stroke="${t.line1}" stroke-width="2"/>
<animate attributeName="opacity" from="0" to="1" begin="1.4s" dur="0.4s" fill="freeze"/>
</g>`
: ""
}
${
lastPt2
? `<g opacity="0">
<circle cx="${lastPt2[0].toFixed(2)}" cy="${lastPt2[1].toFixed(2)}" r="7" fill="${t.line2}" opacity="0.22"/>
<circle cx="${lastPt2[0].toFixed(2)}" cy="${lastPt2[1].toFixed(2)}" r="4" fill="${t.bg}" stroke="${t.line2}" stroke-width="2"/>
<animate attributeName="opacity" from="0" to="1" begin="1.55s" dur="0.4s" fill="freeze"/>
</g>`
: ""
}
</g>
<text x="${W - PAD.right}" y="${H - 12}" text-anchor="end" font-family="${FONT}" font-size="10" fill="${t.textSoft}" opacity="0.7">
Updated ${meta.updatedAt}
</text>
</svg>`;
}
function hex(color, channel) {
const m = color.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i);
if (!m) return "0";
const r = parseInt(m[1], 16) / 255;
const g = parseInt(m[2], 16) / 255;
const b = parseInt(m[3], 16) / 255;
return ({ r, g, b })[channel].toFixed(3);
}
async function main() {
const raw = await readFile(HISTORY_PATH, "utf8");
const history = JSON.parse(raw);
const meta = {
repo: history.repo,
subtitle: history.subtitle ?? "GitHub stars · ClawHub downloads",
updatedAt: new Date().toISOString().slice(0, 10),
};
const lightSvg = renderChart("light", history, meta);
const darkSvg = renderChart("dark", history, meta);
await writeFile(OUT_LIGHT, lightSvg);
await writeFile(OUT_DARK, darkSvg);
console.log(`Wrote ${OUT_LIGHT}`);
console.log(`Wrote ${OUT_DARK}`);
console.log(
`GitHub stars: ${history.github_stars[history.github_stars.length - 1]?.[1] ?? 0}`,
);
console.log(
`ClawHub downloads: ${history.clawhub_downloads[history.clawhub_downloads.length - 1]?.[1] ?? 0}`,
);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});

View file

@ -1,488 +0,0 @@
{
"repo": "lahfir/agent-desktop",
"slug": "agent-desktop",
"clawhub_created_at": "2026-03-03",
"subtitle": "GitHub stars · ClawHub downloads — updated daily",
"github_stars": [
[
"2026-02-21",
2
],
[
"2026-02-22",
2
],
[
"2026-02-23",
3
],
[
"2026-02-24",
3
],
[
"2026-02-25",
3
],
[
"2026-02-26",
4
],
[
"2026-02-27",
4
],
[
"2026-02-28",
4
],
[
"2026-03-01",
8
],
[
"2026-03-02",
10
],
[
"2026-03-03",
12
],
[
"2026-03-04",
16
],
[
"2026-03-05",
16
],
[
"2026-03-06",
16
],
[
"2026-03-07",
20
],
[
"2026-03-08",
21
],
[
"2026-03-09",
23
],
[
"2026-03-10",
28
],
[
"2026-03-11",
28
],
[
"2026-03-12",
32
],
[
"2026-03-13",
32
],
[
"2026-03-14",
32
],
[
"2026-03-15",
32
],
[
"2026-03-16",
32
],
[
"2026-03-17",
32
],
[
"2026-03-18",
32
],
[
"2026-03-19",
33
],
[
"2026-03-20",
33
],
[
"2026-03-21",
33
],
[
"2026-03-22",
33
],
[
"2026-03-23",
33
],
[
"2026-03-24",
33
],
[
"2026-03-25",
33
],
[
"2026-03-26",
33
],
[
"2026-03-27",
33
],
[
"2026-03-28",
34
],
[
"2026-03-29",
34
],
[
"2026-03-30",
34
],
[
"2026-03-31",
34
],
[
"2026-04-01",
36
],
[
"2026-04-02",
36
],
[
"2026-04-03",
37
],
[
"2026-04-04",
37
],
[
"2026-04-05",
40
],
[
"2026-04-06",
43
],
[
"2026-04-07",
44
],
[
"2026-04-08",
44
],
[
"2026-04-09",
44
],
[
"2026-04-10",
44
],
[
"2026-04-11",
45
],
[
"2026-04-12",
46
],
[
"2026-04-13",
50
],
[
"2026-04-14",
58
],
[
"2026-04-15",
66
],
[
"2026-04-16",
67
],
[
"2026-04-17",
68
],
[
"2026-04-18",
68
],
[
"2026-04-19",
68
],
[
"2026-04-20",
68
],
[
"2026-04-21",
70
],
[
"2026-04-22",
70
],
[
"2026-04-23",
70
],
[
"2026-04-24",
86
],
[
"2026-04-25",
89
]
],
"clawhub_downloads": [
[
"2026-03-03",
0
],
[
"2026-03-04",
10
],
[
"2026-03-05",
19
],
[
"2026-03-06",
29
],
[
"2026-03-07",
38
],
[
"2026-03-08",
48
],
[
"2026-03-09",
58
],
[
"2026-03-10",
67
],
[
"2026-03-11",
77
],
[
"2026-03-12",
87
],
[
"2026-03-13",
96
],
[
"2026-03-14",
106
],
[
"2026-03-15",
115
],
[
"2026-03-16",
125
],
[
"2026-03-17",
135
],
[
"2026-03-18",
144
],
[
"2026-03-19",
154
],
[
"2026-03-20",
164
],
[
"2026-03-21",
173
],
[
"2026-03-22",
183
],
[
"2026-03-23",
192
],
[
"2026-03-24",
202
],
[
"2026-03-25",
212
],
[
"2026-03-26",
221
],
[
"2026-03-27",
231
],
[
"2026-03-28",
241
],
[
"2026-03-29",
250
],
[
"2026-03-30",
260
],
[
"2026-03-31",
269
],
[
"2026-04-01",
279
],
[
"2026-04-02",
289
],
[
"2026-04-03",
298
],
[
"2026-04-04",
308
],
[
"2026-04-05",
318
],
[
"2026-04-06",
327
],
[
"2026-04-07",
337
],
[
"2026-04-08",
346
],
[
"2026-04-09",
356
],
[
"2026-04-10",
366
],
[
"2026-04-11",
375
],
[
"2026-04-12",
385
],
[
"2026-04-13",
395
],
[
"2026-04-14",
404
],
[
"2026-04-15",
414
],
[
"2026-04-16",
423
],
[
"2026-04-17",
433
],
[
"2026-04-18",
443
],
[
"2026-04-19",
452
],
[
"2026-04-20",
462
],
[
"2026-04-21",
472
],
[
"2026-04-22",
481
],
[
"2026-04-23",
491
],
[
"2026-04-24",
500
],
[
"2026-04-25",
510
]
],
"sources": {
"github": "https://github.com/lahfir/agent-desktop",
"clawhub": "https://clawhub.ai/lahfir/agent-desktop",
"convex": "https://wry-manatee-359.convex.cloud"
},
"schema_version": 1
}

147
.github/stats/seed.mjs vendored
View file

@ -1,147 +0,0 @@
#!/usr/bin/env node
import { writeFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
import { execFileSync } from "node:child_process";
const __dirname = dirname(fileURLToPath(import.meta.url));
const HISTORY_PATH = join(__dirname, "history.json");
const REPO = process.env.STATS_REPO || "lahfir/agent-desktop";
const SLUG = process.env.CLAWHUB_SLUG || "agent-desktop";
const CONVEX_URL = "https://wry-manatee-359.convex.cloud";
function gh(args) {
return execFileSync("gh", args, { encoding: "utf8", maxBuffer: 50 * 1024 * 1024 });
}
async function fetchClawHubSkill(slug) {
const res = await fetch(`${CONVEX_URL}/api/query`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Convex-Client": "npm-1.20.0",
},
body: JSON.stringify({
path: "skills:getBySlug",
format: "convex_encoded_json",
args: [{ slug }],
}),
});
if (!res.ok) throw new Error(`ClawHub HTTP ${res.status}: ${await res.text()}`);
const body = await res.json();
if (body.status !== "success") {
throw new Error(`ClawHub error: ${body.errorMessage ?? "unknown"}`);
}
return body.value.skill;
}
function backfillClawHubDaily(createdAtMs, currentDownloads, todayIso) {
const startMs = new Date(
new Date(createdAtMs).toISOString().slice(0, 10) + "T00:00:00Z",
).getTime();
const endMs = new Date(todayIso + "T00:00:00Z").getTime();
const days = Math.max(1, Math.round((endMs - startMs) / 86400000));
const out = [];
for (let i = 0; i <= days; i++) {
const dayMs = startMs + i * 86400000;
const iso = new Date(dayMs).toISOString().slice(0, 10);
const value = Math.round((currentDownloads * i) / days);
out.push([iso, value]);
}
return out;
}
function fetchStarHistory(repo) {
const out = gh([
"api",
"-H",
"Accept: application/vnd.github.star+json",
"--paginate",
`repos/${repo}/stargazers`,
"--jq",
"[.[] | .starred_at]",
]);
const events = out
.split("\n")
.filter(Boolean)
.flatMap((chunk) => JSON.parse(chunk));
return events.sort();
}
function bucketCumulative(starredAt) {
const counts = new Map();
let cum = 0;
for (const iso of starredAt) {
cum += 1;
counts.set(iso.slice(0, 10), cum);
}
return [...counts.entries()].sort();
}
function fillDailyForward(buckets, untilIso) {
if (buckets.length === 0) return [];
const out = [];
const startMs = new Date(buckets[0][0] + "T00:00:00Z").getTime();
const endMs = new Date(untilIso + "T00:00:00Z").getTime();
const days = Math.round((endMs - startMs) / 86400000);
let bi = 0;
let cur = buckets[0][1];
for (let i = 0; i <= days; i++) {
const dayMs = startMs + i * 86400000;
const iso = new Date(dayMs).toISOString().slice(0, 10);
while (bi < buckets.length && buckets[bi][0] <= iso) {
cur = buckets[bi][1];
bi++;
}
out.push([iso, cur]);
}
return out;
}
async function main() {
const today = new Date().toISOString().slice(0, 10);
console.log(`[seed] repo=${REPO} slug=${SLUG} today=${today}`);
console.log("[seed] fetching GitHub star history...");
const starredAt = fetchStarHistory(REPO);
console.log(`[seed] got ${starredAt.length} star events`);
const buckets = bucketCumulative(starredAt);
const ghDaily = fillDailyForward(buckets, today);
console.log(`[seed] github series: ${ghDaily.length} daily points`);
console.log("[seed] fetching ClawHub stats...");
const skill = await fetchClawHubSkill(SLUG);
const downloads = Math.round(skill.stats?.downloads ?? 0);
const createdAtMs = skill.createdAt ?? skill._creationTime ?? Date.now();
console.log(
`[seed] clawhub downloads=${downloads} createdAt=${new Date(createdAtMs).toISOString().slice(0, 10)}`,
);
const chDaily = backfillClawHubDaily(createdAtMs, downloads, today);
console.log(`[seed] clawhub series: ${chDaily.length} daily points (linear backfill)`);
const history = {
repo: REPO,
slug: SLUG,
clawhub_created_at: new Date(createdAtMs).toISOString().slice(0, 10),
subtitle: "GitHub stars · ClawHub downloads — updated daily",
github_stars: ghDaily,
clawhub_downloads: chDaily,
sources: {
github: `https://github.com/${REPO}`,
clawhub: `https://clawhub.ai/${REPO.split("/")[0]}/${SLUG}`,
convex: CONVEX_URL,
},
schema_version: 1,
};
await writeFile(HISTORY_PATH, JSON.stringify(history, null, 2) + "\n");
console.log(`[seed] wrote ${HISTORY_PATH}`);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});

View file

@ -1,75 +0,0 @@
#!/usr/bin/env node
import { readFile, writeFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
import { execFileSync } from "node:child_process";
const __dirname = dirname(fileURLToPath(import.meta.url));
const HISTORY_PATH = join(__dirname, "history.json");
const CONVEX_URL = "https://wry-manatee-359.convex.cloud";
function gh(args) {
return execFileSync("gh", args, { encoding: "utf8" }).trim();
}
async function fetchClawHubDownloads(slug) {
const res = await fetch(`${CONVEX_URL}/api/query`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Convex-Client": "npm-1.20.0",
},
body: JSON.stringify({
path: "skills:getBySlug",
format: "convex_encoded_json",
args: [{ slug }],
}),
});
if (!res.ok) throw new Error(`ClawHub HTTP ${res.status}: ${await res.text()}`);
const body = await res.json();
if (body.status !== "success") {
throw new Error(`ClawHub error: ${body.errorMessage ?? "unknown"}`);
}
return Math.round(body.value.skill.stats.downloads ?? 0);
}
function upsertPoint(series, iso, value) {
const last = series[series.length - 1];
if (last && last[0] === iso) {
last[1] = value;
return series;
}
return [...series, [iso, value]];
}
async function main() {
const today = new Date().toISOString().slice(0, 10);
const raw = await readFile(HISTORY_PATH, "utf8");
const history = JSON.parse(raw);
console.log(`[update] repo=${history.repo} slug=${history.slug} today=${today}`);
const ghStars = parseInt(
gh(["api", `repos/${history.repo}`, "--jq", ".stargazers_count"]),
10,
);
console.log(`[update] github stars=${ghStars}`);
const chDownloads = await fetchClawHubDownloads(history.slug);
console.log(`[update] clawhub downloads=${chDownloads}`);
history.github_stars = upsertPoint(history.github_stars, today, ghStars);
history.clawhub_downloads = upsertPoint(
history.clawhub_downloads,
today,
chDownloads,
);
await writeFile(HISTORY_PATH, JSON.stringify(history, null, 2) + "\n");
console.log(`[update] wrote ${HISTORY_PATH}`);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});

View file

@ -1,46 +0,0 @@
name: stats
on:
schedule:
- cron: "17 6 * * *"
workflow_dispatch: {}
permissions:
contents: write
concurrency:
group: stats
cancel-in-progress: false
jobs:
update:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.ref_name }}
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: "20"
- name: Fetch latest stats and append history
env:
GH_TOKEN: ${{ github.token }}
run: node .github/stats/update.mjs
- name: Regenerate chart SVGs
run: node .github/stats/generate.mjs
- name: Commit and push if changed
run: |
if git diff --quiet -- .github/stats/; then
echo "No stats change."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add .github/stats/history.json .github/stats/chart-light.svg .github/stats/chart-dark.svg
git commit -m "chore(stats): refresh history and chart"
git push

View file

@ -21,11 +21,13 @@
<img src="docs/architecture.png" alt="agent-desktop architecture diagram" width="800" />
</p>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./.github/stats/chart-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="./.github/stats/chart-light.svg">
<img alt="GitHub stars and ClawHub downloads for lahfir/agent-desktop" src="./.github/stats/chart-light.svg" width="100%">
</picture>
<a href="https://star-history.com/#lahfir/agent-desktop&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=lahfir/agent-desktop&type=Date&theme=dark">
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=lahfir/agent-desktop&type=Date">
<img alt="Star history for lahfir/agent-desktop" src="https://api.star-history.com/svg?repos=lahfir/agent-desktop&type=Date">
</picture>
</a>
## Key Features