Raw JSON report machine-readable
{
"data": {
"repo": {
"topics": [],
"is_fork": false,
"size_kb": 7453,
"has_wiki": true,
"homepage": "https://getsshift.dev",
"languages": {
"CSS": 115037,
"HTML": 614396,
"Shell": 33274,
"Dockerfile": 3633,
"JavaScript": 1011287,
"PowerShell": 39771
},
"pushed_at": "2026-07-22T16:15:20Z",
"created_at": "2026-04-02T02:10:23Z",
"owner_type": "User",
"updated_at": "2026-07-30T02:19:45Z",
"description": "A modern, responsive web-based SSH and SFTP terminal client for the AI stack",
"is_archived": false,
"is_disabled": false,
"license_spdx": "MIT",
"default_branch": "main",
"license_spdx_raw": "MIT",
"primary_language": "JavaScript",
"significant_languages": [
"JavaScript",
"HTML"
]
},
"owner": {
"blog": "https://vimlet.com",
"name": "Lethe",
"type": "User",
"login": "lethevimlet",
"company": "@vimlet",
"location": "Madrid, Spain",
"followers": 10,
"avatar_url": "https://avatars.githubusercontent.com/u/2963923?v=4",
"created_at": "2012-12-04T17:42:19Z",
"is_verified": null,
"public_repos": 12,
"account_age_days": 4987
},
"license": {
"state": "standard",
"spdx_id": "MIT",
"raw_spdx": "MIT",
"file_present": true,
"scorecard_found": true,
"profile_has_license": true
},
"activity": {
"releases": [],
"recent_commits": [
{
"oid": "d551d43a298c091d10e0e373670271f3ba075fa8",
"body": "forceResizeLikeRefit replicates a real window resize for one session\n(synchronous reflow -> measure -> fit -> clearTextureAtlas -> refresh ->\nssh-resize) and is wired into switchTab's safety net, ssh-control-acquired,\nssh-control-taken, and the force-resize button. _syncCharSizeThenClearAtlas\ncommit\n[…]\n in switchTab/setSessionFontSize/\nsetTerminalFontSize + SFTP setters. Adds deterministic regression tests\n(interlace-regression, force-resize-fix) and a manual repro harness.\n\nCo-authored-by: opencode",
"is_bot": false,
"headline": "v1.6.1: fix interlace black-band artefact on take-control/tab-grab",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-07-22T16:15:15Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "177b7c78a466cf53d7e80055fcc6ddc48583ebae",
"body": "…settings\n\n- Add mic button next to keyboard button in tab actions (SSH tabs only;\n disabled on SFTP). Merged Record/Pause toggle cycles idle -> recording ->\n stop-and-transcribe, appending each segment to the transcript.\n- Speech-to-Text modal: editable transcript, Record/Pause toggle, Wand butto\n[…]\npe) so they behave identically on mobile/desktop.\n- _wireActionBtn fires touchend on all touch devices (not just isMobile)\n fixing the 'mic dialog not working on mobile' bug.\n- Bump version to 1.6.0.",
"is_bot": false,
"headline": "v1.6.0: speech-to-text with mic button, AI wand cleanup, Speech & AI …",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-07-21T10:19:48Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "43a85892acc72e8e99bebe50ad59f4511452a794",
"body": "…ew ruler white line\n\n- Fix interlaced black bands when dragging tabs between layout panels\n (split, 2-columns, 2x2). After moveTabToPanel moves the terminal\n wrapper DOM to a new container, the WebGL glyph atlas still holds\n glyphs rasterised at the old panel's cell size. Fitting immediately\n m\n[…]\nScrollbar: 4px on mobile, 8px on desktop. Slider bg overridden to\n #3d444d via setProperty('background-color', ..., 'important').\n MutationObserver enforces dimensions + bg on every xterm re-render.",
"is_bot": false,
"headline": "v1.5.2: fix interlace on tab drag between layout panels + hide overvi…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-07-20T21:59:43Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "b5d2277eeffd6e22b74ec84795c806b18774104d",
"body": "…ew ruler white line\n\n- Mobile TUI scroll: touch scroll in mouse-tracking mode now sends SGR\n mouse wheel escape sequences directly via ssh-data instead of\n dispatching synthetic WheelEvents (which xterm's document-level touch\n handler interfered with). Checks coreMouseService.activeProtocol\n in\n[…]\nrent canvas created a visible white strip\n when terminal had no scrollback or on blur.\n- MutationObserver runs on all platforms to enforce scrollbar\n dimensions + slider bg on every xterm re-render.",
"is_bot": false,
"headline": "fix: mobile TUI scroll, rate-limit toast, thin scrollbar, hide overvi…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-07-20T21:55:19Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "22c84465965332accf238e50eb035ba20982013a",
"body": "…use/scroll inactivity lockup\n\nBug 1 — Interlaced lines after Take Control on a refreshed browser tab\n- Root cause: the ssh-screen-sync completion callback wrote the\n serialized terminal state + called terminal.resize(cols, rows) but\n did NOT clear the WebGL glyph atlas. The atlas still held glyph\n[…]\n available, and that the\n sftp-download code shape does not throw.\n\nTest totals: 94 unit tests (was) -> 101 unit tests (+7 bugfix\nregressions). 0 failing. All touched source files pass node --check.",
"is_bot": false,
"headline": "v1.5.1: fix interlace-after-take-control, SFTP download crash, TUI mo…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-07-20T20:05:37Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "5dba19252ec4f055a0434557b55927bd0d97e742",
"body": "…down fixes\n\nMarks the consolidated release of v1.4.9 + v1.4.10 changes:\n- Mobile tab-count duplicate-block bug fix + 8-phase hardening\n- xterm fidelity (surrogate split, OSC 52 cross-chunk, sync-window recovery, WebGL re-sync, payload validation, rate limits)\n- Sticky teardown respects remaining vi\n[…]\n tests + integration/browser suites (was 21 baseline)\n- Bug A: ssh-request-sync rate-limit no longer closes tabs (advisory flag)\n- Bug B: server restart = fresh slate, no localStorage auto-reconstruct",
"is_bot": false,
"headline": "v1.5.0: bump minor version — robustness pass + mobile tab/sticky tear…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-07-19T23:32:56Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "900ef22acb3c67d5d3302d997dd04a089c3db355",
"body": "…art stale-tab leak\n\nBug A — ssh-request-sync rate-limit cascaded into closeTab\n- Root cause: the Phase-3 ssh-request-sync rate limit (added in v1.4.9)\n emitted an 'ssh-error' with a sessionId. The client's ssh-error\n handler at app.js:4521 had a catchall closeTab(sessionId) for ANY\n error messag\n[…]\ns normally (server-restart case isolated)\n\nTest totals: 87 unit tests (was) → 94 unit tests (+7 bugfix regressions) + 11 skipped (SSH-credential-gated). Syntax checked: app.js, ssh.js, ssh-manager.js.",
"is_bot": false,
"headline": "v1.4.10: fix mobile dropdown switchTab closing all tabs + server-rest…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-07-19T23:31:33Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "196bbc20f06448c82b2c09665cf51f0fdc2d2c53",
"body": "…lity + sticky teardown, add 145 tests + Docker SSH fixture\n\nPhase 1 — Mobile tab-count bug\n- handleTabOpened: remove duplicate createSSHTab/createSFTPTab block\n- server: socket.broadcast.emit('tab-opened') so originator doesn't echo to itself\n- createSSHTab/createSFTPTab: idempotency guards by sess\n[…]\nructure, env vars, fixture docs, troubleshooting\n\nTest totals: 21 passing / 5 skipped (baseline) → 146 passing / 18 skipped / 0 failing in Docker-less env; with Docker fixture: 164 passing / 0 skipped",
"is_bot": false,
"headline": "v1.4.9: robustness pass — fix mobile tab-count bug, harden xterm fide…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-07-19T23:07:00Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "ca041efeca981340b3ce74d377ae3ff2fad411d2",
"body": "…ce re-measure after font swap\n\nThe v1.4.7 attempt (removing the redundant lineHeight/letterSpacing resets)\ndid not actually fix the interlace bug — those setters were no-ops since\nthe options were already at their base values, so removing them changed\nnothing.\n\nRoot cause: xterm.js v6's CharSizeSer\n[…]\nreshCharAtlas, so by the time fit() runs it sees the real\nfont's cell height and dims cols/rows correctly. Uses the same private\n_api pattern already in use elsewhere (terminal._core._renderService).",
"is_bot": false,
"headline": "v1.4.8: fix interlace (alternating black bands) — force CharSizeServi…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-07-19T20:31:44Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "e2e330917d99ebb4313f588fb4b841444abbbe21",
"body": "…nt bugs\n\nRoot cause for both symptoms was the sizing/fit path in _fitTerminal\nand initTerminal.\n\nBug 1 (alternating black horizontal bands between text rows):\n _fitTerminal reset term.options.lineHeight and letterSpacing to base\n values immediately before fitAddon.fit(). Those options are alread\n[…]\nbove transient collapsed\n states), bumped retries to 5, and added two delayed safety-net refits\n (250ms / 600ms) after the post-fonts-ready fit mirroring the existing\n retry pattern in switchTab().",
"is_bot": false,
"headline": "v1.4.7: fix terminal interlace (black bands) and tiny-terminal-on-mou…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-07-15T11:26:41Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "2c40067b7c7b3bcda192836b857caa6a41d6bdb0",
"body": "…tomGlyphs+WebGL as the correct approach",
"is_bot": false,
"headline": "v1.4.6: revert broken letterSpacing/lineHeight seam fix; document cus…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-06-11T15:34:47Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "fc8cd5b64e19abab901802686f60222b68912a5e",
"body": "Only applies letterSpacing padding when it's imperceptible (≤0.15 CSS px);\nlarger padding that would create visible gaps between characters is skipped\nand we accept the occasional 1px seam instead. This fixes seams on most\nDPRs (e.g. 2x displays typically need ~0.1px padding) while avoiding the\nvisible spacing regression seen in v1.4.2-v1.4.3.",
"is_bot": false,
"headline": "v1.4.5: re-enable letterSpacing snapping with tight 0.15px threshold",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-06-11T13:40:08Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "40c353c7dbd0652a4f0f5fbb1bc0f8c7c640e619",
"body": "The letterSpacing-based subpixel snapping (v1.4.2-v1.4.3) made seams\nmore visible rather than less — it added gaps between all characters\nbecause xterm.js customGlyphs does not fill the letterSpacing portion\nof block characters. Disabled _snapCellWidth and set letterSpacing: 0\nexplicitly. The primary seam mitigation is now customGlyphs: true,\nwhich draws block/box-drawing characters as vector fills that tile the\nfull cell width.",
"is_bot": false,
"headline": "v1.4.4: revert letterSpacing snapping, keep customGlyphs for seams",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-06-11T13:22:27Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "616efb13a77838555bca3cf9fa43396b27420391",
"body": "…ment\n\nThe previous v1.4.2 _snapCellWidth() set letterSpacing AFTER fit(), which\nchanged cell width without recalculating column count — widening every\ncell while xterm still laid out the old narrower columns, making seams\nworse. Now the sequence is: fit → measure → snap letterSpacing →\nrefit if spacing changed, so columns are recalculated with the snapped\n(integer device-pixel) cell width. Two fits is intentional: first\nmeasures, second uses the corrected spacing.",
"is_bot": false,
"headline": "v1.4.3: fix subpixel seam snapping — refit after letterSpacing adjust…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-06-11T13:08:57Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "32cac3eb31af0e1e71d468848c80165e6a480bed",
"body": "- Add 150ms delayed refit after every tab switch as safety net for\n layout transitions that haven't settled by the 2nd animation frame\n- Lower _fitTerminal dimension guard from 10px to 2px threshold\n- Set customGlyphs: true and letterSpacing: 0 in terminal options\n- Add _snapCellWidth() to nudge le\n[…]\nional-DPR displays\n- Expand DPR change listener to also refit, re-snap, and rebuild\n atlas (not just clear it) on zoom/monitor move\n- Call _snapCellWidth after every _fitTerminal and after font-ready",
"is_bot": false,
"headline": "v1.4.2: fix small terminal on tab switch, eliminate subpixel seams",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-06-11T12:55:47Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "e1ea1b6adb9124a167bf130be8bcdd5706f6e071",
"body": "- Lower _fitTerminal dimension guard threshold from 10px to 2px to\n avoid rejecting containers that are mid-layout-transition\n- Add retry loop in switchTab when _fitTerminal fails (up to 4\n retries with 50-300ms delays) so tab switching always converges\n- Only emit ssh-resize after successful fit in take-control and\n become-controller handlers; skip send when fit returns false to\n avoid sending stale dimensions to the remote PTY\n- Same fix in ssh-connected initial resize",
"is_bot": false,
"headline": "v1.4.1: fix terminal small square on tab switch, fix take-control resize",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-06-11T12:38:29Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f65d9083075a806615e507de054c4dd9215ddbf4",
"body": "- Resolve user's home directory on SFTP connect via realpath('.')\n- Auto-fallback from '/' to home dir when listing root fails with\n permission denied (common with chrooted SFTP servers)\n- Send homeDir in sftp-error responses so the client can navigate home\n- Client auto-redirects to home directory\n[…]\n(Bug 3: dim/black panes)\n- Clear WebGL atlas on tab switch, visibility change, and font swap\n- Use requestAnimationFrame for post-layout refits\n- Font-aware initial PTY resize in ssh-connected handler",
"is_bot": false,
"headline": "v1.4.0: fix SFTP permission denied on root directories",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-06-11T11:59:37Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "540856225e6a3d3949dcb1bd2dbb40aa13c99be2",
"body": "- Bug 1 (narrow column wrapping): await font loading before initial\n fit() and PTY resize so glyph width is measured from the actual\n monospace font, not the fallback; guard _fitTerminal() against\n zero/near-zero container dimensions\n- Bug 2 (bottom-row garbage): call terminal.refresh() after eve\n[…]\nbGL on\n tab activation; always clear atlas when a tab becomes visible\n- Also fix interspersed black lines in TUI apps (same root cause as\n Bug 1: cell height computed from fallback font before swap)",
"is_bot": false,
"headline": "v1.3.9: fix terminal rendering glitches that heal on resize",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-06-11T11:42:27Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "e038e74b23c07bca7b965a251def6a4846ade45c",
"body": "The mobile terminal's hidden textarea was cleared after every input event,\npreventing Gboard autocorrect from operating on the existing text. When\nGboard replaced a misspelled word, the deletion was silently dropped and\nthe corrected text was appended on top, causing duplication.\n\nReplace the e.data\n[…]\nearing textarea after every character to let Gboard operate on it\n- Clear textarea on Enter/Tab (line submission) and refocus instead\n- Let browser handle Backspace natively; diff detects the deletion",
"is_bot": false,
"headline": "v1.3.8: fix Gboard autocorrect text duplication in mobile terminal",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-06-08T23:39:59Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "6bb0d00507d304490de68ed867a339879429c839",
"body": null,
"is_bot": false,
"headline": "v1.3.7: fix tab flash animation showing underline on inactive tabs",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-06-04T11:52:12Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "86acb49cc4ec4f6c2885021fdd0bd99624fcbb20",
"body": null,
"is_bot": false,
"headline": "v1.3.6: fix WebGL context loss recovery on tab backgrounding",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-06-02T14:06:40Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9934b2b3c4aa88477a799044974082dc9545b15c",
"body": "- Await document.fonts.ready before first write, then clear atlas + refresh\n so glyphs are rasterised with the correct font instead of fallback\n- Add _resetWebGLAtlas() helper; clear texture atlas on resize, font-size\n change, theme change, tab-switch font restore, and DPR change\n- Add _setupDPRLi\n[…]\nars highlighting\n- Refresh after Ctrl+C / Ctrl+Shift+C copy operations\n- Improve onContextLoss: null out webglAddon ref and refresh terminal to\n repaint with fallback renderer\n- Bump version to 1.3.5",
"is_bot": false,
"headline": "v1.3.5: fix WebGL renderer blank cell artefacts",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-06-01T10:39:36Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "00828ba32bda56809ba7c787b07376aa683e3bdb",
"body": null,
"is_bot": false,
"headline": "bump version to 1.3.4",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-28T14:59:29Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "8d2f15cf81ce3a5c4e6655856072dce3147a9798",
"body": null,
"is_bot": false,
"headline": "bump version to 1.3.3",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-28T02:04:47Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "114988f8943c20eefa84b53481b19db33c9ded6e",
"body": "…r tab distribution\n\n- Add getClockwisePanels() for correct tab distribution across panels\n (top-left → top-right → bottom-right → bottom-left for 2x2)\n- Fix open-tabs handler: set layout BEFORE syncing tabs so panels\n exist before distributeTabsToPanels runs\n- Fix saveTabs emitting to server duri\n[…]\nropdown to show ALL tabs with panel badge\n indicators for tabs in other panels\n- Add .tab-panel-badge CSS for panel indicators in dropdown\n- Server: don't broadcast tabs-sync when tabs array is empty",
"is_bot": false,
"headline": "Fix multi-panel tab sync, mobile panel preservation, and cross-browse…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-28T02:01:18Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "1e2530fe296672f50539c86f42f9f449ae0f0969",
"body": "…terminal init on failure\n\n- During syncTabsFromServer, switchTab is suppressed to prevent the\n last-created tab becoming active. Previously it returned immediately,\n which meant terminal wrappers never got the 'active' CSS class,\n causing initTerminal to fail with 'container not visible'. Now\n \n[…]\ninstead of showing a toast.\n The container may not be visible during sync because the correct\n tab hasn't been activated yet — it becomes visible after the\n final switchTab call at the end of sync.",
"is_bot": false,
"headline": "fix: suppress switchTab during sync but keep wrappers visible, retry …",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-28T01:06:50Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "b38072f1a4a344d341fec8e3adf83c455a17fa9d",
"body": "…cked active state\n\nWhen the server restarts, activeTabsByPanel is empty (in-memory). The first\nclient connecting would get empty active tab info, causing the last-created\ntab to be selected instead of the first tab. Now if no active tab is tracked\nfor a panel, the first tab in that panel defaults to active.",
"is_bot": false,
"headline": "fix: default active tab to first tab per panel when server has no tra…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-28T00:28:51Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b7b602076717bdde2f1ce155b66b030004d4b5eb",
"body": "… truth\n\n1. Server now tracks active tab per panel (activeTabsByPanel) and\n includes it in open-tabs event. New clients see which tab was last\n active instead of defaulting to the last-created tab.\n\n2. syncTabsFromServer suppresses switchTab during tab creation, then\n activates the correct tab\n[…]\nr the correct tab is activated.\n\n5. tab-manager: removeTab() cleans up activeTabsByPanel references.\n setActiveTab/getActiveTab/getActiveTabsByPanel added to track\n and query active tab per panel.",
"is_bot": false,
"headline": "fix: active tab sync, mobile dropdown, and server as single source of…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-28T00:21:28Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "12a5f4fa403ee0b273de75e260261268d58ec8d4",
"body": "3 fixes for tabs disappearing or duplicating:\n\n1. When server has 0 active sessions (restart), restoreTabs() now always\n creates NEW connections instead of trying to join dead sessions. This\n eliminates the 'Session expired' message — credentials from localStorage\n are used to reconnect automa\n[…]\nprocessed after init.\n\n3. Prevent duplicate tabs by fixing the timing — deferred open-tabs\n ensures syncTabsFromServer runs only after the layout system and\n event listeners are fully initialized.",
"is_bot": false,
"headline": "fix: session restoration after server restart and prevent duplicate tabs",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-27T23:55:47Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "d621a9f594b6443ac134950bbee933803b63a529",
"body": "1. Server: SSH/SFTP session death no longer leaves zombie tab entries.\n Previously, when an SSH connection died on the server (remote shell\n exited, network drop, etc.) while no browser clients were connected,\n the openTabs entry was never cleaned up. On reconnect, the client\n would try ssh-\n[…]\nmissed.\n\n3. (Previous commit) Credentials from localStorage are merged into\n server-synced tab data so auto-reconnection works even when the\n server stripped passwords/private keys from open-tabs.",
"is_bot": false,
"headline": "fix: sessions disappearing on page refresh — 3 root causes",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-27T23:18:27Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "9185718f9a5a2bbc5713bca281fe2d6e19716e33",
"body": "…sion recovery\n\nWhen the server sends open-tabs on reconnect/refresh, it strips\npassword/privateKey from connectionData for security. This meant that\nif ssh-join failed (e.g. server restarted), the auto-reconnect path\ncouldn't re-establish the SSH connection because it had no credentials.\n\nNow syncT\n[…]\nalStorage (which\nsaves them locally with the full connectionData) into the server's\nstripped version, so auto-reconnection works correctly after a page\nrefresh even if the server has lost the session.",
"is_bot": false,
"headline": "fix: merge credentials from localStorage into server tab sync for ses…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-27T17:51:08Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "c15543664c29dd2dafb6773d69a74d9d8790a1b1",
"body": "…twork changes\n\n- Changed reconnectionAttempts from 5 to Infinity so the client never\n stops trying to reconnect (backoff caps at 30s)\n- Added visibilitychange handler to trigger immediate reconnect when\n the user returns to the tab\n- Improved disconnect/reconnect toast messages\n- Suppressed noisy\n[…]\ning the\nclient. With sticky sessions the SSH connection stays alive on the\nserver, but the client had no way back. Now it keeps trying and\nautomatically restores the session when connectivity returns.",
"is_bot": false,
"headline": "fix: make Socket.IO reconnect indefinitely so SSH sessions survive ne…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-27T17:24:33Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "7e91810c8a1fdac9c1b5da2495ded01896388fee",
"body": "…ter bug",
"is_bot": false,
"headline": "fix: remove selectionForeground to fix WebGL glyph cache block charac…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-27T17:14:32Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b3151909e2be3736b15dd9d407bbc1c1a19c608e",
"body": "Remove redundant localhost-only check on /api/update endpoint.\nThe global auth middleware already protects all endpoints when a\npassword is set. Without a password, the admin has chosen open access,\nso blocking only updates was inconsistent with other endpoints.",
"is_bot": false,
"headline": "v1.3.2: fix remote update blocked without password",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-27T14:14:33Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "670855733e62a01c57cb4c09a843324ef05f9b97",
"body": "- Bump default scrollback from 2000 to 10000 lines\n- Add scrollback as configurable setting in config.json (0-100000)\n- Add scrollback input to settings dialog (Terminal category)\n- Wire scrollback through server config, WS handler, and client terminal\n- Increase screen sync size limit from 256KB to\n[…]\nB for larger buffers\n- Redesign settings modal: desktop sidebar nav + 7 categories\n- Mobile: dropdown category selector, responsive layout\n- Fixed-height desktop settings modal with scrollable content",
"is_bot": false,
"headline": "v1.3.1 - configurable scrollback, categorized settings",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-27T11:33:51Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "f51851720e65396c8a5bc9e11f2d9042174fbfc6",
"body": null,
"is_bot": false,
"headline": "v1.3.0 - fix tab duplication, spurious auth errors, update layout",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-27T10:53:25Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "0a17f473eea6c851bc30fc49b79def46b3ab584b",
"body": "- Add sftp-join handler so SFTP sessions use join instead of new connection\n- createSFTPTab uses sftp-join when restoreSessionId is provided\n- Add sftp-joined client event handler\n- Suppress 'All configured authentication methods failed' toast when\n reconnecting with stripped credentials (server omits password/privateKey)\n- Show user-friendly message when session expires without stored credentials\n- SSH error handler: only reconnect if credentials are available",
"is_bot": false,
"headline": "fix: prevent spurious auth error toast on second browser tab (v1.2.2)",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-27T10:43:32Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "432673bf90544645fdc6946e3814c82816c1e8b4",
"body": "- Server is now the single source of truth for tabs on initial sync\n- Remove stale localStorage tabs not present on server\n- Skip localStorage restore when server tabs arrive first\n- Add 3s fallback timeout for unreachable servers\n- Break restore loop if server sync arrives mid-restore\n- Add removeTabLocally() for clean local-only tab cleanup",
"is_bot": false,
"headline": "fix: prevent cross-device tab duplication in sticky mode (v1.2.1)",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-27T10:32:48Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2b15eb391ec30c8f026ba57ef0f4b02fa152a230",
"body": "Root cause: multi-byte UTF-8 characters and ANSI escape sequences were\nbeing corrupted at chunk boundaries throughout the data pipeline.\n\nChanges:\n- Add @xterm/addon-unicode11 to client and server headless terminal.\n Without Unicode 11 width rules, xterm.js used Unicode 6 widths,\n causing cursor/e\n[…]\nunks to split at escape-safe\n boundaries (before ESC bytes, not mid-sequence) instead of\n arbitrary 32KB offsets.\n- Add Unicode11 addon script and loading in initTerminal().\n- Bump version to 1.2.0.",
"is_bot": false,
"headline": "fix: prevent xterm.js character dropping in TUI apps (OpenCode, etc.)",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-25T15:02:36Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "1481ef5acc0839537eb032331de65cc144d37941",
"body": "- Always pass passphrase to puttygen via temp file (--old-passphrase <file>)\n instead of inline argument, fixes 'cannot open mopycsa1988' error on all\n puttygen versions that only accept file arguments\n- Prevent interactive puttygen prompts (which hang Node) by always providing\n a passphrase file\n[…]\ntygen dependency for v2 encrypted keys\n- Fix double passphrase prompt - only re-prompt if user hasn't entered one\n- Better error messages: wrong passphrase, v3 format unsupported, decryption\n failure",
"is_bot": false,
"headline": "v1.1.9: Fix PPK key conversion - passphrase file and v3 detection",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-25T10:50:08Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "af71930543a3ea7712704d2518e4e7a7489fdf95",
"body": "…llback\n\n- Remove --old-passphrase-file fallback (only in puttygen 0.81+,\n causes confusing errors on older versions that hide the real error)\n- Use --old-passphrase=VALUE as primary format (standard GNU long opt)\n- Fallback to space-separated --old-passphrase VALUE only if equals\n format returns \n[…]\npt for passphrase on any passphrase-related error, not just\n when passphrase field is empty\n- Propagate key conversion errors to client instead of silently\n continuing with unconvertible key content",
"is_bot": false,
"headline": "v1.1.8: Fix PPK passphrase handling - remove --old-passphrase-file fa…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-25T10:06:23Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "969b944082402a72b7d7d158796e8c7251c836e6",
"body": "- Fix puttygen --old-passphrase argument parsing (use equals sign format\n to prevent passphrase being misinterpreted as filename)\n- Add --old-passphrase-file fallback for older puttygen versions\n- Prompt for passphrase when uploading encrypted key without one\n- Propagate key conversion errors to client instead of silently failing\n- Add onCancel callback to password modal for cancel handling",
"is_bot": false,
"headline": "v1.1.7: Fix encrypted PPK key passphrase handling",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-25T09:54:58Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "41884f36885d344fcf029c78dfbcbcd85a2f60d2",
"body": "- Auto-convert PPK keys to OpenSSH at connection time (ssh-manager, sftp-manager)\n- Support encrypted PPK files with passphrase via puttygen --old-passphrase\n- Add convertKeyIfNeeded() utility for server-side PPK conversion\n- Pass passphrase through frontend key upload and REST convert-key endpoint\n- Auto-convert PPK keys when connecting from bookmarks (connectFromBookmark, openSFTPFromBookmark)\n- Fix case-insensitive PPK detection in offline fallback (PuTTY-User-Key-File regex)",
"is_bot": false,
"headline": "feat: add PPK key support for SSH connections, bump to 1.1.6",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-25T09:28:41Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b23e1655a4e2783b6110c47d34000bd3fb52bb81",
"body": "…obile input composition handling\n\n- Remove terminal.onBinary handler that was causing double-sends of\n input on desktop (phantom characters in opencode input)\n- Add max toast limit (5) and deduplication to showToast()\n- Suppress socket connect/disconnect toasts during app update\n- Add max-height/overflow to toast container CSS\n- Fix mobile composition handling: check e.isComposing, e.inputType,\n and deduplicate input vs compositionend (Gboard fix)\n- Bump version to 1.1.5",
"is_bot": false,
"headline": "fix: remove duplicate onBinary handler, fix toast stacking, improve m…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-25T08:22:30Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "76065aae2144688f7375517298006bb859c3344d",
"body": null,
"is_bot": false,
"headline": "bump version to 1.1.4",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-24T11:23:53Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ff4cafeed3b4dfeb53351433f6cbf05bf3b79dcf",
"body": "…, use readAsDataURL for fast base64 encoding",
"is_bot": false,
"headline": "fix: multi-file upload - append input to DOM for reliable file picker…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-24T10:59:52Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d44432ba9cafb8f85119139377809a97b7e4aad8",
"body": "…ploads, and cancel button\n\n- Fix large file SFTP crash by switching from single-message base64 to\n chunked transfer protocol (1MB chunks with backpressure)\n- Fix Node.js 24+ crash (util.isDate removed) with polyfill in server entry\n- Add upload/download progress bar with percentage, size, and file\n[…]\nloop, destroys\n server-side stream, clears download state)\n- Increase Socket.IO maxHttpBufferSize to 5MB for chunk payloads\n- Stream downloads in 1MB chunks instead of buffering entire file in memory",
"is_bot": false,
"headline": "feat: chunked SFTP transfers, upload progress, multi-file/drag-drop u…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-24T10:59:07Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "e4a1c8c5ace2ce5b9c67d0d7d33852fd19723379",
"body": "… PowerShell (Admin)",
"is_bot": false,
"headline": "docs: add getsshift.dev badge, simplify Windows one-liner, relabel as…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-24T10:01:41Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9c302b9cafdcf76a5bc35fa51d57b4cd39f1eb3f",
"body": null,
"is_bot": false,
"headline": "bump version to 1.1.3",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-21T14:10:35Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c7a302e67c089c5d978f3750068880701bc19a82",
"body": null,
"is_bot": false,
"headline": "bump version to 1.1.2",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-21T11:36:51Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "6625fbe5d9dc2ee9ec305cb8bf4c791629d615ed",
"body": "…t (fixes arm64 crash)",
"is_bot": false,
"headline": "dockerfile: use npm install --package-lock-only instead of pnpm impor…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-21T11:31:45Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d8f018e65f9b7cf52c5708217a04f6be65720db6",
"body": "…pm ci",
"is_bot": false,
"headline": "dockerfile: regenerate package-lock.json from pnpm-lock.yaml before n…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-21T11:24:48Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5957563b1514346618fb3ecac2fcf22ba35c84ca",
"body": null,
"is_bot": false,
"headline": "regenerate package-lock.json to sync with package.json dependencies",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-21T11:19:43Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "027e2034b622c8ff6f132c9c19e264c6a2d17e8e",
"body": "…n, add pnpm-lock.yaml to builder stage, bump label version",
"is_bot": false,
"headline": "fix dockerfile: use --omit=dev instead of deprecated --only=productio…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-21T11:14:21Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9c19a1414daeefffa8a97b3a03e9478e2a46bb08",
"body": null,
"is_bot": false,
"headline": "v1.1.1: fix version and npm package name in publish workflow",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-21T11:08:33Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "bd0806252bf3eabdd240cb55feb027953a294d2d",
"body": null,
"is_bot": false,
"headline": "add pnpm-lock.yaml and trust policy exclusion for semver@6.3.1",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-21T11:01:10Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "cc2c1ff1c25972d78358a95da8ea7b3a2016f326",
"body": null,
"is_bot": false,
"headline": "ci: remove test step from CI workflow",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-21T10:53:10Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "dd7943c8895acb3e292f04637f71e9de7aba47ac",
"body": "- Remove pnpm version override in GHA (use packageManager from package.json)\n- Fix CI job name capitalization\n- Fix Socket.dev badge URL to use badge.socket.dev",
"is_bot": false,
"headline": "v1.1.2: fix CI workflow and Socket badge",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-21T10:51:09Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "8eb4b42f5b6610c880c2adab9d7cb212410b2bbc",
"body": "…ate endpoint\n\n- Add pnpm as primary package manager with 7-day minimum release age\n gate and no-downgrade trust policy (pnpm-workspace.yaml, .npmrc)\n- Add Socket Firewall (sfw) integration for install-time malware scanning\n (ci.yml, npm-publish.yml, package.json install:safe script)\n- Add gitleak\n[…]\ncy Bypass,\n add network auth gate for non-localhost requests (system.js)\n- Exclude @lethevimlet/sshift from release age and trust policy gates\n- Add Socket.dev badge to README\n- Bump version to 1.1.1",
"is_bot": false,
"headline": "v1.1.1: add supply chain security (pnpm + gitleaks + sfw), harden upd…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-21T10:43:54Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "0f460dda7bef05e7853a1c02a6df6266214c8e8a",
"body": null,
"is_bot": false,
"headline": "Add CLI reference docs with --dev flag documentation",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T21:46:16Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5ba3f585ea9da0cf8d6be6bda115d8a818b25cd4",
"body": "…ity config",
"is_bot": false,
"headline": "Harden npm supply chain: pin dependency versions and add .npmrc secur…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T21:39:50Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "35a6a67d1d8932dfcfc27ced52adb5d0fcced88b",
"body": null,
"is_bot": false,
"headline": "Bump version to 1.1.0; separate dev PID file for side-by-side instances",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T20:56:39Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5b01f2439922bd0adcd9750080eac1d07775f74f",
"body": "…heduler)\n\n- Rename isSystemdManaged/systemdUnit to isServiceManaged/serviceManager/serviceUnit\n- Add launchd detection (macOS) and Task Scheduler detection (Windows)\n- Update shell script to use SERVICE_MANAGED/SERVICE_MANAGER/SERVICE_UNIT vars\n- Add launchctl stop/start restart flow for macOS in shell script\n- Fix orphaned catch block and backtick/quote syntax errors",
"is_bot": false,
"headline": "Add cross-platform service manager detection (systemd/launchd/Task Sc…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T20:40:24Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "96ef9b4503d44b9f45baca061344e403e64b116d",
"body": null,
"is_bot": false,
"headline": "Bumped version",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T20:17:07Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "098a0d08da7593f1535c0723e2e3202c10b0e238",
"body": null,
"is_bot": false,
"headline": "Bump version to 1.0.20",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T20:02:35Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a32ca79f12ccdb2647e3c95ff462283c266b963e",
"body": null,
"is_bot": false,
"headline": "Fix ReferenceError: escape ${NPM_DELAY} in shell template literal",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T19:59:34Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9e49b894900d7672158dec374b560f2227500ea2",
"body": null,
"is_bot": false,
"headline": "Bumped version",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T19:40:00Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ff363cd8ed6af54259d1a6421429584f6e0ecdea",
"body": null,
"is_bot": false,
"headline": "Bump version to 1.0.17",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T19:34:41Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c0153317a04a2c359832c46138162594f86814a0",
"body": "…ttempts with 30s delay)\n\n- Use systemctl is-active as fallback when cgroup detection fails\n- Retry npm install up to 3 times with 30s delay for registry propagation\n- Same retry logic for Windows PowerShell script",
"is_bot": false,
"headline": "Fix systemd detection (systemctl is-active fallback) + npm retry (3 a…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T19:18:48Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f49e6fef80e6c6915ef1ddb069b16685937723f0",
"body": null,
"is_bot": false,
"headline": "Testing GUI update",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T19:07:37Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "755bf8eb26e1796f0bcdc8e84b3df2aa8c5b9537",
"body": null,
"is_bot": false,
"headline": "Bump version to 1.0.15",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T18:27:35Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "853e7ede1724ce4fa2dcd9d258a52bedcc2c8a76",
"body": "…erpolation",
"is_bot": false,
"headline": "Fix ReferenceError: move systemd detection before script template int…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T18:21:06Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "78b110273b6aa0b6922bb1d37cad26afee83c98f",
"body": "Under systemd, the update script was being killed when systemctl\nstop/restart was called because it was in the same cgroup as the\nsshift service. Now uses systemd-run --scope to move the update\nscript to its own transient scope so it survives service restarts.\nAlso uses systemctl restart (not stop+start) for systemd path.",
"is_bot": false,
"headline": "Fix systemd update: spawn script in separate scope via systemd-run",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T18:13:56Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "60272cadeeb368e8c5e2d0503cbe803bd330fd07",
"body": null,
"is_bot": false,
"headline": "Bump version to 1.0.12",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T18:07:59Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "59dd6711b7e055c2d3f1a506051fc5e7833cb5ee",
"body": "…d of re-detecting\n\nThe shell script was trying to detect systemd independently but\nrunning in a different cgroup/namespace than the sshift process.\nNow passes isSystemdManaged and systemdUnit directly from the\nNode.js server (which correctly detects it via /proc/PID/cgroup)\nto the shell script as environment variables.",
"is_bot": false,
"headline": "Fix systemd detection in shell script - pass flag from Node.js instea…",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T18:05:18Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "654462b5754f1b3be4325d84d68cb647d8b4959e",
"body": null,
"is_bot": false,
"headline": "Bump version to 1.0.10",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T17:59:36Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ca6cf4e481c9e249010281e1ecdedd6b0715173c",
"body": "- Detect systemd service and use systemctl stop/start instead of\n process.exit(0) + exec, which caused a race condition where systemd\n would restart the old version before npm install completed\n- When systemd-managed, keep the server running until the update\n script stops it via systemctl, preventing dual-instances\n- Add Task Scheduler detection for Windows (PowerShell script)\n- Delay process exit to 500ms under systemd (script handles stop)",
"is_bot": false,
"headline": "Fix GUI update for systemd-managed installations",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T17:52:11Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2e18e0a70d215a9ddd37c12075ee973b22179ba7",
"body": null,
"is_bot": false,
"headline": "Bumped version to 1.0.8",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T15:42:57Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ac315afaae00c902a83ccf86e641bf1091f8e2fb",
"body": null,
"is_bot": false,
"headline": "Bumped version",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T15:23:55Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "be1546b34aa871ab491da2b052f59be57fb421c5",
"body": null,
"is_bot": false,
"headline": "Bumped version",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T15:16:49Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "bed0a61508d40d8b8b193908484555aec49c740e",
"body": null,
"is_bot": false,
"headline": "Bumped version",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T13:52:04Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "efce7f345656db8a6e31deec48b5ce48a2ed966b",
"body": null,
"is_bot": false,
"headline": "Updated sshift bin pid logic",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T13:48:11Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c70b6179b168dceda093eef8e13f6a29793aa505",
"body": null,
"is_bot": false,
"headline": "Fixed manage sessions",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T12:06:50Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "4f718818e67c170625c88787f0c23da1806aec3c",
"body": null,
"is_bot": false,
"headline": "Added a better cache busting strategy",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-20T11:55:28Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "fe8a0679b24b4683d2202885d5360dcc4fd4ab3d",
"body": null,
"is_bot": false,
"headline": "Bumped version",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-19T14:56:48Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e563c5a2a473ff678e59554318defac087c09ff9",
"body": null,
"is_bot": false,
"headline": "Fixed ctrl+v double text issue",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-19T14:38:51Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "10afea8e2bbae39081e6dbafbb58eeb31e07f6b9",
"body": null,
"is_bot": false,
"headline": "Added debug button in settings",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-19T14:22:37Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ab31cd190d8733fb74b408974272fd915ddcb5c4",
"body": null,
"is_bot": false,
"headline": "Made mobile term text size smaller and fixed copy context menu position",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-19T12:36:37Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "32c98f0528bb2b7f36f913e2ab2616e998b0839c",
"body": null,
"is_bot": false,
"headline": "Added support for sc52 clipboard",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-19T12:24:58Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "79e0ce7ef838043defb4ac0cd89839d84aa8c2a9",
"body": null,
"is_bot": false,
"headline": "Updated security dialogue and docs",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-19T11:37:53Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e8276157205142ae38b09892fbc3ebe06d9d712f",
"body": null,
"is_bot": false,
"headline": "Fixed an issue with mobile input",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-18T21:02:05Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "3e3dd8c09fd919aea3415a968eee22666cf25d36",
"body": null,
"is_bot": false,
"headline": "Fixed mobile select scroll issue",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-18T16:51:15Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c353f598ddfb2a29f3d3f51ff7f111ce9d452821",
"body": null,
"is_bot": false,
"headline": "Bumped version",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-18T11:45:21Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "0475b7fb26b6017df79c30fc595edc492d55f962",
"body": null,
"is_bot": false,
"headline": "Made mobile clipboard menu bigger",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-18T11:41:54Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f1809f10e1217d0aacaac3c37e93ee0cb44d4672",
"body": null,
"is_bot": false,
"headline": "Fixed clipboard refactor issues",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-18T11:36:11Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "361027745c085c1f27fa4e83de7850a3e7a7260a",
"body": null,
"is_bot": false,
"headline": "Fixed paste issues",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-18T11:21:58Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c87f692ccd2906dd8ff376db80bf6a8cdc7973e9",
"body": null,
"is_bot": false,
"headline": "Fixed issue with update button",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-14T15:18:41Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "18afe14a9de98d6647bffa895be77049a10bf06c",
"body": null,
"is_bot": false,
"headline": "Bumped version",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-13T15:29:13Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "85356bae33aaa3d5c6727340afbc1c4d3b8b3f9f",
"body": null,
"is_bot": false,
"headline": "Fixed gitaction cleanup",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-13T15:24:13Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c13140a9543956f02c5c1e852f8fd02c3d5addcd",
"body": null,
"is_bot": false,
"headline": "Bumped version",
"author_name": "lethevimlet",
"author_login": "lethevimlet",
"committed_at": "2026-05-13T15:03:51Z",
"body_truncated": false,
"is_coding_agent": false
}
],
"releases_count": 0,
"commits_last_year": 244,
"latest_release_at": null,
"latest_release_tag": null,
"releases_from_tags": false,
"days_since_last_push": 9,
"active_weeks_last_year": 12,
"days_since_latest_release": null,
"mean_days_between_releases": null
},
"community": {
"has_readme": true,
"has_license": true,
"has_description": true,
"has_contributing": true,
"health_percentage": 57,
"has_issue_template": false,
"has_code_of_conduct": false,
"has_pull_request_template": false
},
"ecosystem": {
"packages": [
{
"name": "@lethevimlet/sshift",
"exists": true,
"license": "MIT",
"keywords": [
"ssh",
"sftp",
"terminal",
"web",
"xterm",
"web-terminal",
"ssh-client",
"sftp-client",
"browser-terminal"
],
"ecosystem": "npm",
"matches_repo": true,
"registry_url": "https://www.npmjs.com/package/@lethevimlet/sshift",
"is_deprecated": false,
"latest_version": "1.6.1",
"repository_url": "https://github.com/lethevimlet/sshift",
"versions_count": 77,
"total_downloads": null,
"dependents_count": null,
"deprecation_note": null,
"maintainers_count": 1,
"monthly_downloads": 2001,
"first_published_at": "2026-04-08T21:01:07.835000Z",
"latest_published_at": "2026-07-22T16:15:55.578000Z",
"latest_version_yanked": null,
"days_since_latest_publish": 9
}
]
},
"popularity": {
"forks": 3,
"stars": 35,
"watchers": 0,
"fork_history": {
"days": [
{
"date": "2026-04-05",
"count": 1
},
{
"date": "2026-04-27",
"count": 1
},
{
"date": "2026-05-05",
"count": 1
}
],
"complete": true,
"collected": 3,
"total_forks": 3
},
"star_history": null,
"open_issues_and_prs": 0
},
"ai_readiness": {
"has_nix": false,
"example_dirs": [],
"has_llms_txt": false,
"has_dockerfile": true,
"has_mcp_signal": false,
"bootstrap_files": [],
"api_schema_files": [],
"has_devcontainer": false,
"typecheck_configs": [],
"toolchain_manifests": [],
"largest_source_bytes": 497398,
"source_files_sampled": 77,
"oversized_source_files": 7,
"agent_instruction_files": [
"AGENTS.md"
],
"agent_instruction_max_bytes": 9064
},
"dependencies": {
"manifests": [
"package.json"
],
"advisories": {
"error": null,
"scope": "published_package",
"source": "osv",
"findings": [
{
"name": "uuid",
"direct": true,
"version": "9.0.0",
"severity": "high",
"ecosystem": "npm",
"cvss_score": 7.5,
"advisory_ids": [
"GHSA-w5hq-g745-h8pq"
],
"fixed_version": "13.0.1",
"advisory_count": 1,
"oldest_advisory_days": 100
},
{
"name": "body-parser",
"direct": false,
"version": "1.20.1",
"severity": "high",
"ecosystem": "npm",
"cvss_score": 7.5,
"advisory_ids": [
"GHSA-qwcr-r2fm-qrc7",
"GHSA-v422-hmwv-36x6"
],
"fixed_version": "2.3.0",
"advisory_count": 2,
"oldest_advisory_days": 689
},
{
"name": "engine.io",
"direct": false,
"version": "6.5.5",
"severity": "high",
"ecosystem": "npm",
"cvss_score": 7.5,
"advisory_ids": [
"GHSA-r635-g3xr-vw7x"
],
"fixed_version": "6.6.7",
"advisory_count": 1,
"oldest_advisory_days": 11
},
{
"name": "path-to-regexp",
"direct": false,
"version": "0.1.7",
"severity": "high",
"ecosystem": "npm",
"cvss_score": 7.5,
"advisory_ids": [
"GHSA-37ch-88jc-xwx2",
"GHSA-9wv6-86v2-598j",
"GHSA-rhx6-c78j-4q9w"
],
"fixed_version": "8.0.0",
"advisory_count": 3,
"oldest_advisory_days": 690
},
{
"name": "ws",
"direct": false,
"version": "8.17.1",
"severity": "high",
"ecosystem": "npm",
"cvss_score": 7.5,
"advisory_ids": [
"GHSA-58qx-3vcg-4xpx",
"GHSA-96hv-2xvq-fx4p"
],
"fixed_version": "8.21.0",
"advisory_count": 2,
"oldest_advisory_days": 74
},
{
"name": "express",
"direct": true,
"version": "4.18.2",
"severity": "moderate",
"ecosystem": "npm",
"cvss_score": 6.1,
"advisory_ids": [
"GHSA-qw6h-vgh9-j6wx",
"GHSA-rv95-896h-c2vc"
],
"fixed_version": "5.0.0-beta.3",
"advisory_count": 2,
"oldest_advisory_days": 858
},
{
"name": "qs",
"direct": false,
"version": "6.11.0",
"severity": "moderate",
"ecosystem": "npm",
"cvss_score": 5.3,
"advisory_ids": [
"GHSA-6rw7-vpxm-498p",
"GHSA-w7fw-mjwx-w883"
],
"fixed_version": "6.14.2",
"advisory_count": 2,
"oldest_advisory_days": 213
},
{
"name": "send",
"direct": false,
"version": "0.18.0",
"severity": "moderate",
"ecosystem": "npm",
"cvss_score": 5,
"advisory_ids": [
"GHSA-m6fv-jmcg-4jfg"
],
"fixed_version": "0.19.0",
"advisory_count": 1,
"oldest_advisory_days": 689
},
{
"name": "serve-static",
"direct": false,
"version": "1.15.0",
"severity": "moderate",
"ecosystem": "npm",
"cvss_score": 5,
"advisory_ids": [
"GHSA-cm22-4g7w-348p"
],
"fixed_version": "2.1.0",
"advisory_count": 1,
"oldest_advisory_days": 689
},
{
"name": "cookie",
"direct": false,
"version": "0.4.2",
"severity": "low",
"ecosystem": "npm",
"cvss_score": null,
"advisory_ids": [
"GHSA-pxg6-pf52-xh8x"
],
"fixed_version": "0.7.0",
"advisory_count": 1,
"oldest_advisory_days": 665
},
{
"name": "cookie",
"direct": false,
"version": "0.5.0",
"severity": "low",
"ecosystem": "npm",
"cvss_score": null,
"advisory_ids": [
"GHSA-pxg6-pf52-xh8x"
],
"fixed_version": "0.7.0",
"advisory_count": 1,
"oldest_advisory_days": 665
}
],
"collected": true,
"malicious": [],
"truncated": false,
"by_severity": {
"low": 2,
"high": 5,
"moderate": 4
},
"advisory_count": 17,
"affected_count": 11,
"assessed_count": 132,
"malicious_count": 0,
"assessed_package": "npm:@lethevimlet/sshift@1.6.1",
"unassessed_count": 0,
"direct_affected_count": 2
},
"ecosystems": [
"npm"
],
"dependencies": [
{
"name": "@xterm/addon-fit",
"manifest": "package.json",
"ecosystem": "npm",
"version_constraint": "0.11.0"
},
{
"name": "@xterm/addon-image",
"manifest": "package.json",
"ecosystem": "npm",
"version_constraint": "0.9.0"
},
{
"name": "@xterm/addon-search",
"manifest": "package.json",
"ecosystem": "npm",
"version_constraint": "0.16.0"
},
{
"name": "@xterm/addon-serialize",
"manifest": "package.json",
"ecosystem": "npm",
"version_constraint": "0.14.0"
},
{
"name": "@xterm/addon-unicode11",
"manifest": "package.json",
"ecosystem": "npm",
"version_constraint": "0.9.0"
},
{
"name": "@xterm/addon-web-links",
"manifest": "package.json",
"ecosystem": "npm",
"version_constraint": "0.12.0"
},
{
"name": "@xterm/addon-webgl",
"manifest": "package.json",
"ecosystem": "npm",
"version_constraint": "0.19.0"
},
{
"name": "@xterm/headless",
"manifest": "package.json",
"ecosystem": "npm",
"version_constraint": "6.0.0"
},
{
"name": "@xterm/xterm",
"manifest": "package.json",
"ecosystem": "npm",
"version_constraint": "6.0.0"
},
{
"name": "dotenv",
"manifest": "package.json",
"ecosystem": "npm",
"version_constraint": "17.4.0"
},
{
"name": "express",
"manifest": "package.json",
"ecosystem": "npm",
"version_constraint": "4.18.2"
},
{
"name": "httpolyglot",
"manifest": "package.json",
"ecosystem": "npm",
"version_constraint": "0.1.2"
},
{
"name": "lucide",
"manifest": "package.json",
"ecosystem": "npm",
"version_constraint": "1.7.0"
},
{
"name": "selfsigned",
"manifest": "package.json",
"ecosystem": "npm",
"version_constraint": "5.5.0"
},
{
"name": "socket.io",
"manifest": "package.json",
"ecosystem": "npm",
"version_constraint": "4.7.2"
},
{
"name": "ssh2",
"manifest": "package.json",
"ecosystem": "npm",
"version_constraint": "1.15.0"
},
{
"name": "uuid",
"manifest": "package.json",
"ecosystem": "npm",
"version_constraint": "9.0.0"
}
],
"all_dependencies": {
"error": "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository",
"source": null,
"packages": [],
"collected": false,
"truncated": false,
"total_count": null,
"direct_count": null,
"indirect_count": null
}
},
"maintainership": {
"issues": {
"open_prs": 0,
"merged_prs": 1,
"open_issues": 0,
"closed_ratio": null,
"closed_issues": 0,
"closed_unmerged_prs": 0
},
"bus_factor": 1,
"bot_contributors": 0,
"top_contributors": [
{
"type": "User",
"login": "lethevimlet",
"commits": 244,
"avatar_url": "https://avatars.githubusercontent.com/u/2963923?v=4"
}
],
"contributors_sampled": 1,
"top_contributor_share": 1
},
"quality_signals": {
"has_ci": true,
"has_tests": true,
"ci_workflows": [
"ci.yml",
"docker-publish.yml",
"npm-publish.yml"
],
"has_docs_dir": true,
"linter_configs": [],
"has_editorconfig": false,
"has_linter_config": true,
"has_precommit_config": true
},
"security_signals": {
"lockfiles": [
"package-lock.json",
"pnpm-lock.yaml"
],
"scorecard": {
"checks": [
{
"name": "Binary-Artifacts",
"score": 10,
"reason": "no binaries found in the repo",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
},
{
"name": "Branch-Protection",
"score": 0,
"reason": "branch protection not enabled on development/release branches",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
},
{
"name": "CI-Tests",
"score": null,
"reason": "no pull request found",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
},
{
"name": "CII-Best-Practices",
"score": 0,
"reason": "no effort to earn an OpenSSF best practices badge detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
},
{
"name": "Code-Review",
"score": 0,
"reason": "Found 0/30 approved changesets -- score normalized to 0",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
},
{
"name": "Contributors",
"score": 3,
"reason": "project has 1 contributing companies or organizations -- score normalized to 3",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
},
{
"name": "Dangerous-Workflow",
"score": 10,
"reason": "no dangerous workflow patterns detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
},
{
"name": "Dependency-Update-Tool",
"score": 0,
"reason": "no update tool detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
},
{
"name": "Fuzzing",
"score": 0,
"reason": "project is not fuzzed",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
},
{
"name": "License",
"score": 10,
"reason": "license file detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
},
{
"name": "Maintained",
"score": 10,
"reason": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
},
{
"name": "Packaging",
"score": 10,
"reason": "packaging workflow detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
},
{
"name": "Pinned-Dependencies",
"score": 0,
"reason": "dependency not pinned by hash detected -- score normalized to 0",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
},
{
"name": "SAST",
"score": 0,
"reason": "no SAST tool detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
},
{
"name": "Security-Policy",
"score": 0,
"reason": "security policy file not detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
},
{
"name": "Signed-Releases",
"score": null,
"reason": "no releases found",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
},
{
"name": "Token-Permissions",
"score": 0,
"reason": "detected GitHub workflow tokens with excessive permissions",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
},
{
"name": "Vulnerabilities",
"score": 0,
"reason": "25 existing vulnerabilities detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
}
],
"commit": "d551d43a298c091d10e0e373670271f3ba075fa8",
"ran_at": "2026-08-01T00:33:56Z",
"aggregate_score": 3.5,
"scorecard_version": "v5.5.0"
},
"has_codeql_workflow": false,
"has_security_policy": false,
"has_dependabot_config": false
},
"contribution_flow": {
"collected": true,
"ci_last_run_at": "2026-07-22T16:17:16Z",
"oldest_open_prs": [],
"last_merged_pr_at": "2026-04-07T12:53:21Z",
"ci_last_conclusion": "FAILURE",
"oldest_open_issues": []
}
},
"config": {
"disabled_metrics": [],
"disabled_categories": [],
"disabled_components": {}
},
"source": {
"url": "https://github.com/lethevimlet/sshift",
"host": "github.com",
"name": "sshift",
"owner": "lethevimlet"
},
"metrics": {
"overall": {
"key": "overall",
"band": "moderate",
"name": "Overall health",
"note": "The weighted overall 54 is calibrated to 56 on the published index scale (record calibration 2026-08-02).",
"notes": [
{
"code": "overall_calibration",
"params": {
"raw": 54,
"calibrated": 56,
"calibration": "2026-08-02"
}
}
],
"value": 56,
"inputs": {
"security": 35,
"vitality": 39,
"community": 49,
"governance": 52,
"calibration": "2026-08-02",
"engineering": 91,
"ai_readiness": 54,
"weighted_overall_raw": 54
},
"components": []
},
"categories": [
{
"key": "vitality",
"band": "weak",
"name": "Vitality",
"value": 39,
"weight": 0.21,
"metrics": [
{
"key": "development_activity",
"band": "good",
"name": "Development activity",
"note": null,
"notes": [],
"value": 65,
"inputs": {
"commits_last_year": 244,
"human_commit_share": 1,
"days_since_last_push": 9,
"active_weeks_last_year": 12
},
"components": [
{
"key": "push_recency",
"name": "Push recency",
"detail": "last push 9 days ago",
"points": 28.8,
"status": "partial",
"details": [
{
"code": "push_recency",
"params": {
"days": 9
}
}
],
"max_points": 36
},
{
"key": "commit_cadence",
"name": "Commit cadence",
"detail": "12/52 weeks with commits",
"points": 8.3,
"status": "partial",
"details": [
{
"code": "commit_cadence_weeks",
"params": {
"weeks": 12
}
}
],
"max_points": 36
},
{
"key": "commit_volume",
"name": "Commit volume",
"detail": "244 commits in the last year",
"points": 18,
"status": "met",
"details": [
{
"code": "commits_last_year",
"params": {
"count": 244
}
}
],
"max_points": 18
},
{
"key": "openssf_scorecard_maintained",
"name": "OpenSSF Scorecard: Maintained",
"detail": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
"points": 10,
"status": "met",
"details": [],
"max_points": 10
}
]
},
{
"key": "release_discipline",
"band": "critical",
"name": "Release discipline",
"note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"openssf_scorecard_signed_releases"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 1,
"inputs": {
"releases_count": 0
},
"components": [
{
"key": "ships_releases",
"name": "Ships releases",
"detail": "no releases published",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_releases_published",
"params": {}
}
],
"max_points": 27
},
{
"key": "release_recency",
"name": "Release recency",
"detail": "no releases",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_releases",
"params": {}
}
],
"max_points": 36
},
{
"key": "release_cadence",
"name": "Release cadence",
"detail": "no releases",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_releases",
"params": {}
}
],
"max_points": 27
},
{
"key": "openssf_scorecard_signed_releases",
"name": "OpenSSF Scorecard: Signed-Releases",
"detail": "no releases found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 10
}
]
},
{
"key": "abandonment",
"band": "exceptional",
"name": "Abandonment",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"cap": null,
"state": "unverified",
"guards": [],
"signals": [],
"red_flag": false,
"multiplier_pct": 100,
"declared_reason": null,
"unverified_reason": "repository_too_young",
"unanswered_open_prs": null,
"unanswered_open_issues": null,
"days_since_last_merged_pr": null,
"days_since_last_human_commit": null,
"days_since_last_human_commit_is_floor": false
},
"components": [
{
"key": "project_is_still_maintained",
"name": "Project is still maintained",
"detail": "maintenance record not established from the collected data",
"points": 100,
"status": "met",
"details": [
{
"code": "abandonment_unverified",
"params": {}
}
],
"max_points": 100
}
]
}
],
"description": "Is the project alive — is code being written and are releases shipping?"
},
{
"key": "community",
"band": "weak",
"name": "Community & Adoption",
"value": 49,
"weight": 0.17,
"metrics": [
{
"key": "popularity",
"band": "at_risk",
"name": "Popularity & adoption",
"note": null,
"notes": [],
"value": 27,
"inputs": {
"forks": 3,
"stars": 35,
"watchers": 0,
"growth_state": "unverified",
"growth_factor_pct": 100,
"growth_unverified_reason": "no_history"
},
"components": [
{
"key": "stars",
"name": "Stars",
"detail": "35 stars",
"points": 24.8,
"status": "partial",
"details": [
{
"code": "stars",
"params": {
"count": 35
}
}
],
"max_points": 60
},
{
"key": "forks",
"name": "Forks",
"detail": "3 forks",
"points": 2.5,
"status": "partial",
"details": [
{
"code": "forks",
"params": {
"count": 3
}
}
],
"max_points": 25
},
{
"key": "watchers",
"name": "Watchers",
"detail": "0 watchers",
"points": 0,
"status": "missed",
"details": [
{
"code": "watchers",
"params": {
"count": 0
}
}
],
"max_points": 15
}
]
},
{
"key": "community_health",
"band": "good",
"name": "Community health",
"note": null,
"notes": [],
"value": 70,
"inputs": {
"has_readme": true,
"has_license": true,
"readme_badges": null,
"has_contributing": true,
"has_issue_template": false,
"has_code_of_conduct": false,
"readme_badge_services": [],
"has_pull_request_template": false
},
"components": [
{
"key": "readme",
"name": "README",
"detail": null,
"points": 22.5,
"status": "met",
"details": [],
"max_points": 22.5
},
{
"key": "license",
"name": "License",
"detail": "recognized license (MIT)",
"points": 22.5,
"status": "met",
"details": [
{
"code": "license_standard",
"params": {}
},
{
"code": "license_spdx",
"params": {
"spdx": "MIT"
}
}
],
"max_points": 22.5
},
{
"key": "contributing_guide",
"name": "CONTRIBUTING guide",
"detail": null,
"points": 18,
"status": "met",
"details": [],
"max_points": 18
},
{
"key": "code_of_conduct",
"name": "Code of conduct",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 13.5
},
{
"key": "issue_template",
"name": "Issue template",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.2
},
{
"key": "pr_template",
"name": "PR template",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 6.3
}
]
},
{
"key": "ecosystem_adoption",
"band": "moderate",
"name": "Ecosystem adoption (downloads)",
"note": "Excluded from scoring (no data or not applicable): Registry dependents. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"registry_dependents"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 55,
"inputs": {
"packages": [
"@lethevimlet/sshift"
],
"dependents": null,
"ecosystems": "npm",
"total_downloads": null,
"monthly_downloads": 2001
},
"components": [
{
"key": "monthly_downloads",
"name": "Monthly downloads",
"detail": "2,001 downloads/month across npm",
"points": 44,
"status": "partial",
"details": [
{
"code": "downloads_monthly",
"params": {
"count": 2001,
"ecosystems": "npm"
}
}
],
"max_points": 80
},
{
"key": "registry_dependents",
"name": "Registry dependents",
"detail": "not reported by this ecosystem",
"points": 0,
"status": "excluded",
"details": [
{
"code": "not_reported_by_this_ecosystem",
"params": {}
}
],
"max_points": 20
}
]
}
],
"description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
},
{
"key": "governance",
"band": "moderate",
"name": "Sustainability & Governance",
"value": 52,
"weight": 0.23,
"metrics": [
{
"key": "maintainer_resilience",
"band": "critical",
"name": "Maintainer resilience (bus factor)",
"note": null,
"notes": [],
"value": 13,
"inputs": {
"bus_factor": 1,
"contributors_sampled": 1,
"top_contributor_share": 1
},
"components": [
{
"key": "bus_factor",
"name": "Bus factor",
"detail": "1 contributor(s) cover half of all commits",
"points": 9,
"status": "partial",
"details": [
{
"code": "bus_factor",
"params": {
"count": 1
}
}
],
"max_points": 54
},
{
"key": "commit_distribution",
"name": "Commit distribution",
"detail": "top contributor authored 100% of commits",
"points": 0,
"status": "missed",
"details": [
{
"code": "top_contributor_share",
"params": {
"share": 100
}
}
],
"max_points": 22.5
},
{
"key": "contributor_breadth",
"name": "Contributor breadth",
"detail": "1 contributors",
"points": 1.4,
"status": "partial",
"details": [
{
"code": "contributors_sampled",
"params": {
"count": 1
}
}
],
"max_points": 13.5
},
{
"key": "openssf_scorecard_contributors",
"name": "OpenSSF Scorecard: Contributors",
"detail": "project has 1 contributing companies or organizations -- score normalized to 3",
"points": 3,
"status": "partial",
"details": [],
"max_points": 10
}
]
},
{
"key": "responsiveness",
"band": "good",
"name": "Issue & PR responsiveness",
"note": "Excluded from scoring (no data or not applicable): Issue resolution, Newcomer PR acceptance. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"issue_resolution",
"newcomer_pr_acceptance"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 67,
"inputs": {
"merged_prs": 1,
"open_issues": 0,
"closed_issues": 0,
"prs_merged_7d": null,
"prs_decided_7d": null,
"prs_merged_30d": null,
"prs_decided_30d": null,
"issue_closed_ratio": null,
"closed_unmerged_prs": 0,
"first_time_authors_30d": null,
"first_time_prs_merged_30d": null,
"first_time_prs_decided_30d": null
},
"components": [
{
"key": "issue_resolution",
"name": "Issue resolution",
"detail": "no issues or no data",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_issues_or_data",
"params": {}
}
],
"max_points": 42
},
{
"key": "pr_acceptance",
"name": "PR acceptance",
"detail": "1/1 decided PRs merged",
"points": 30,
"status": "met",
"details": [
{
"code": "decided_prs_merged",
"params": {
"merged": 1,
"decided": 1
}
}
],
"max_points": 30
},
{
"key": "newcomer_pr_acceptance",
"name": "Newcomer PR acceptance",
"detail": "no first-time contributor's PR decided in 30d",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_newcomer_prs",
"params": {
"days": 30
}
}
],
"max_points": 13
},
{
"key": "openssf_scorecard_code_review",
"name": "OpenSSF Scorecard: Code-Review",
"detail": "Found 0/30 approved changesets -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 15
}
]
},
{
"key": "stewardship",
"band": "weak",
"name": "Ownership & stewardship",
"note": "Excluded from scoring (no data or not applicable): Verified domain. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"verified_domain"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 47,
"inputs": {
"followers": 10,
"owner_type": "User",
"is_verified": null,
"owner_login": "lethevimlet",
"public_repos": 12,
"account_age_days": 4987
},
"components": [
{
"key": "ownership_backing",
"name": "Ownership backing",
"detail": "personal (user) account",
"points": 10,
"status": "partial",
"details": [
{
"code": "owner_personal",
"params": {}
}
],
"max_points": 30
},
{
"key": "verified_domain",
"name": "Verified domain",
"detail": "not applicable to user accounts",
"points": 0,
"status": "excluded",
"details": [
{
"code": "not_applicable_to_user_accounts",
"params": {}
}
],
"max_points": 20
},
{
"key": "owner_reach",
"name": "Owner reach",
"detail": "10 followers of lethevimlet",
"points": 7.5,
"status": "partial",
"details": [
{
"code": "owner_followers",
"params": {
"count": 10,
"login": "lethevimlet"
}
}
],
"max_points": 25
},
{
"key": "track_record",
"name": "Track record",
"detail": "12 public repos, account ~13 yr old",
"points": 20.1,
"status": "partial",
"details": [
{
"code": "public_repos",
"params": {
"count": 12
}
},
{
"code": "account_age_years",
"params": {
"years": 13
}
}
],
"max_points": 25
}
]
},
{
"key": "package_maintenance",
"band": "exceptional",
"name": "Package maintenance",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"packages": [
"@lethevimlet/sshift"
],
"ecosystems": "npm",
"any_deprecated": false,
"min_days_since_publish": 9
},
"components": [
{
"key": "published_resolvable",
"name": "Published & resolvable",
"detail": "1 package(s) on npm",
"points": 25,
"status": "met",
"details": [
{
"code": "packages_published",
"params": {
"count": 1,
"ecosystems": "npm"
}
}
],
"max_points": 25
},
{
"key": "publish_recency",
"name": "Publish recency",
"detail": "latest publish 9 days ago",
"points": 35,
"status": "met",
"details": [
{
"code": "publish_recency",
"params": {
"days": 9
}
}
],
"max_points": 35
},
{
"key": "version_history",
"name": "Version history",
"detail": "77 published versions",
"points": 20,
"status": "met",
"details": [
{
"code": "published_versions",
"params": {
"count": 77
}
}
],
"max_points": 20
},
{
"key": "not_deprecated",
"name": "Not deprecated",
"detail": "active, not deprecated or yanked",
"points": 20,
"status": "met",
"details": [
{
"code": "package_not_deprecated",
"params": {}
}
],
"max_points": 20
}
]
}
],
"description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
},
{
"key": "engineering",
"band": "excellent",
"name": "Engineering Quality",
"value": 91,
"weight": 0.19,
"metrics": [
{
"key": "engineering_practices",
"band": "excellent",
"name": "Engineering practices",
"note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: CI-Tests. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"openssf_scorecard_ci_tests"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 92,
"inputs": {
"has_ci": true,
"has_tests": true,
"has_editorconfig": false,
"has_linter_config": true,
"has_precommit_config": true
},
"components": [
{
"key": "ci_workflows",
"name": "CI workflows",
"detail": "3 workflow(s)",
"points": 24,
"status": "met",
"details": [
{
"code": "ci_workflows",
"params": {
"count": 3
}
}
],
"max_points": 24
},
{
"key": "tests_present",
"name": "Tests present",
"detail": null,
"points": 24,
"status": "met",
"details": [],
"max_points": 24
},
{
"key": "linter_config",
"name": "Linter config",
"detail": null,
"points": 16,
"status": "met",
"details": [],
"max_points": 16
},
{
"key": "pre_commit_hooks",
"name": "Pre-commit hooks",
"detail": null,
"points": 9.6,
"status": "met",
"details": [],
"max_points": 9.6
},
{
"key": "editorconfig",
"name": ".editorconfig",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 6.4
},
{
"key": "openssf_scorecard_ci_tests",
"name": "OpenSSF Scorecard: CI-Tests",
"detail": "no pull request found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 20
}
]
},
{
"key": "documentation",
"band": "excellent",
"name": "Documentation",
"note": null,
"notes": [],
"value": 90,
"inputs": {
"topics": [],
"has_wiki": true,
"homepage": "https://getsshift.dev",
"has_readme": true,
"has_docs_dir": true,
"has_description": true
},
"components": [
{
"key": "readme",
"name": "README",
"detail": null,
"points": 30,
"status": "met",
"details": [],
"max_points": 30
},
{
"key": "documentation_directory",
"name": "Documentation directory",
"detail": null,
"points": 25,
"status": "met",
"details": [],
"max_points": 25
},
{
"key": "documentation_homepage_site",
"name": "Documentation / homepage site",
"detail": "https://getsshift.dev",
"points": 15,
"status": "met",
"details": [],
"max_points": 15
},
{
"key": "repository_description",
"name": "Repository description",
"detail": null,
"points": 10,
"status": "met",
"details": [],
"max_points": 10
},
{
"key": "topics",
"name": "Topics",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
},
{
"key": "wiki",
"name": "Wiki",
"detail": null,
"points": 10,
"status": "met",
"details": [],
"max_points": 10
}
]
}
],
"description": "Are baseline engineering and documentation practices in place?"
},
{
"key": "security",
"band": "weak",
"name": "Security",
"value": 35,
"weight": 0.16,
"metrics": [
{
"key": "security_posture",
"band": "weak",
"name": "Security posture",
"note": "Excluded from scoring (no data or not applicable): CI-Tests, Signed-Releases. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"ci_tests",
"signed_releases"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 35,
"inputs": {
"source": "openssf_scorecard",
"checks_evaluated": 16,
"scorecard_version": "v5.5.0",
"checks_inconclusive": 2,
"scorecard_aggregate": 3.5
},
"components": [
{
"key": "binary_artifacts",
"name": "Binary-Artifacts",
"detail": "no binaries found in the repo",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
},
{
"key": "branch_protection",
"name": "Branch-Protection",
"detail": "branch protection not enabled on development/release branches",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
},
{
"key": "ci_tests",
"name": "CI-Tests",
"detail": "no pull request found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 2.5
},
{
"key": "cii_best_practices",
"name": "CII-Best-Practices",
"detail": "no effort to earn an OpenSSF best practices badge detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 2.5
},
{
"key": "code_review",
"name": "Code-Review",
"detail": "Found 0/30 approved changesets -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
},
{
"key": "contributors",
"name": "Contributors",
"detail": "project has 1 contributing companies or organizations -- score normalized to 3",
"points": 0.8,
"status": "partial",
"details": [],
"max_points": 2.5
},
{
"key": "dangerous_workflow",
"name": "Dangerous-Workflow",
"detail": "no dangerous workflow patterns detected",
"points": 10,
"status": "met",
"details": [],
"max_points": 10
},
{
"key": "dependency_update_tool",
"name": "Dependency-Update-Tool",
"detail": "no update tool detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
},
{
"key": "fuzzing",
"name": "Fuzzing",
"detail": "project is not fuzzed",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "license",
"name": "License",
"detail": "license file detected",
"points": 2.5,
"status": "met",
"details": [],
"max_points": 2.5
},
{
"key": "maintained",
"name": "Maintained",
"detail": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
},
{
"key": "packaging",
"name": "Packaging",
"detail": "packaging workflow detected",
"points": 5,
"status": "met",
"details": [],
"max_points": 5
},
{
"key": "pinned_dependencies",
"name": "Pinned-Dependencies",
"detail": "dependency not pinned by hash detected -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "sast",
"name": "SAST",
"detail": "no SAST tool detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "security_policy",
"name": "Security-Policy",
"detail": "security policy file not detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "signed_releases",
"name": "Signed-Releases",
"detail": "no releases found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 7.5
},
{
"key": "token_permissions",
"name": "Token-Permissions",
"detail": "detected GitHub workflow tokens with excessive permissions",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
},
{
"key": "vulnerabilities",
"name": "Vulnerabilities",
"detail": "25 existing vulnerabilities detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
}
]
},
{
"key": "dependency_advisories",
"band": "weak",
"name": "Dependency advisories",
"note": "Matched the npm:@lethevimlet/sshift@1.6.1 runtime dependency closure — what installing the published package pulls in — 132 packages. Reachability is not analyzed.",
"notes": [
{
"code": "advisories_scope_published",
"params": {
"package": "npm:@lethevimlet/sshift@1.6.1",
"assessed": 132
}
},
{
"code": "advisories_reachability",
"params": {}
}
],
"value": 36,
"inputs": {
"source": "osv",
"advisories": 17,
"affected_packages": 11,
"assessed_packages": 132,
"unassessed_packages": 0,
"affected_by_severity": "high 5, moderate 4, low 2",
"direct_affected_packages": 2
},
"components": [
{
"key": "direct_dependencies_free_of_known_advisories",
"name": "Direct dependencies free of known advisories",
"detail": "2 affected: uuid 9.0.0 (high 7.5), express 4.18.2 (moderate 6.1)",
"points": 12.1,
"status": "partial",
"details": [
{
"code": "advisories_affected",
"params": {
"count": 2,
"packages": "uuid 9.0.0 (high 7.5), express 4.18.2 (moderate 6.1)"
}
}
],
"max_points": 35
},
{
"key": "indirect_dependencies_free_of_known_advisories",
"name": "Indirect dependencies free of known advisories",
"detail": "9 affected: body-parser 1.20.1 (high 7.5), engine.io 6.5.5 (high 7.5), path-to-regexp 0.1.7 (high 7.5), +6 more",
"points": 5,
"status": "partial",
"details": [
{
"code": "advisories_affected",
"params": {
"count": 9,
"packages": "body-parser 1.20.1 (high 7.5), engine.io 6.5.5 (high 7.5), path-to-regexp 0.1.7 (high 7.5)"
}
},
{
"code": "advisories_affected_more",
"params": {
"count": 6
}
}
],
"max_points": 25
},
{
"key": "no_advisories_left_outstanding",
"name": "No advisories left outstanding",
"detail": "9 advisory-carrying package(s) unaddressed past 90 days; oldest published 858 days ago",
"points": 18.6,
"status": "partial",
"details": [
{
"code": "advisories_stale",
"params": {
"days": 90,
"count": 9,
"oldest": 858
}
}
],
"max_points": 40
}
]
},
{
"key": "malicious_dependencies",
"band": "exceptional",
"name": "Malicious dependencies",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"source": "osv",
"meaning": "reported as a malicious package by the OpenSSF corpus; the remedy is removal or moving off the compromised name, never an upgrade of the same artifact. Versions the registry has since pulled are listed but not scored",
"packages": [],
"red_flag": false,
"assessed_packages": 132,
"malicious_packages": 0,
"direct_malicious_packages": 0,
"withdrawn_malicious_packages": 0,
"installable_malicious_packages": 0
},
"components": [
{
"key": "no_dependency_reported_as_a_malicious_package",
"name": "No dependency reported as a malicious package",
"detail": "no dependency is reported as a malicious package",
"points": 100,
"status": "met",
"details": [
{
"code": "no_malicious_dependencies",
"params": {}
}
],
"max_points": 100
}
]
},
{
"key": "high_risk_jurisdiction_exposure",
"band": "exceptional",
"name": "High-Risk Jurisdiction Exposure",
"note": "Only high-confidence self-published location evidence affects this multiplier. Ambiguous matches are review-only; country evidence is not proof of nationality, citizenship, legal registration, malicious intent, or sanctions status.",
"notes": [
{
"code": "jurisdiction_evidence_limits",
"params": {}
}
],
"value": 100,
"inputs": {
"meaning": "self-published location evidence; not nationality or citizenship",
"red_flag": false,
"exposures": [],
"policy_countries": [
"Russia",
"Iran",
"North Korea"
],
"commit_weight_rule": {
"min_commits": 50,
"min_commit_share": 0.1
},
"review_only_matches": 0,
"below_threshold_exposures": [],
"assessed_self_published_locations": 2
},
"components": [
{
"key": "policy_exposure_multiplier",
"name": "Policy exposure multiplier",
"detail": "no confirmed policy-scope location match",
"points": 100,
"status": "met",
"details": [
{
"code": "jurisdiction_no_match",
"params": {}
}
],
"max_points": 100
}
]
}
],
"description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
},
{
"key": "ai_readiness",
"band": "moderate",
"name": "AI Readiness",
"value": 54,
"weight": 0.04,
"metrics": [
{
"key": "ai_agent_context",
"band": "good",
"name": "Agent context & guidance",
"note": null,
"notes": [],
"value": 72,
"inputs": {
"has_llms_txt": false,
"legible_history_share": 0.51,
"agent_instruction_files": [
"AGENTS.md"
],
"agent_instruction_max_bytes": 9064
},
"components": [
{
"key": "agent_instructions",
"name": "Agent instructions",
"detail": "AGENTS.md",
"points": 45,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": "AGENTS.md"
}
}
],
"max_points": 45
},
{
"key": "machine_readable_docs_llms_txt",
"name": "Machine-readable docs (llms.txt)",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 15
},
{
"key": "legible_commit_history",
"name": "Legible commit history",
"detail": "51 of 100 human commits state their intent (structured subject or explanatory body)",
"points": 27.2,
"status": "partial",
"details": [
{
"code": "legible_history",
"params": {
"legible": 51,
"sampled": 100
}
}
],
"max_points": 40
}
]
},
{
"key": "ai_verify_loop",
"band": "weak",
"name": "Verify loop (build / test / typecheck)",
"note": null,
"notes": [],
"value": 43,
"inputs": {
"has_nix": false,
"has_tests": true,
"lockfiles": [
"package-lock.json",
"pnpm-lock.yaml"
],
"has_dockerfile": true,
"typed_language": false,
"bootstrap_files": [],
"has_devcontainer": false,
"has_linter_config": true,
"typecheck_configs": [],
"agent_commit_share": 0,
"toolchain_manifests": [],
"dependency_bot_commit_share": 0
},
"components": [
{
"key": "one_command_bootstrap",
"name": "One-command bootstrap",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 18
},
{
"key": "automated_tests",
"name": "Automated tests",
"detail": null,
"points": 22,
"status": "met",
"details": [],
"max_points": 22
},
{
"key": "lint_format_config",
"name": "Lint / format config",
"detail": null,
"points": 11,
"status": "met",
"details": [],
"max_points": 11
},
{
"key": "static_type_checking",
"name": "Static type checking",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 11
},
{
"key": "reproducible_environment",
"name": "Reproducible environment",
"detail": "Dockerfile, lockfile",
"points": 10,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": "Dockerfile, lockfile"
}
}
],
"max_points": 10
},
{
"key": "demonstrated_agent_practice",
"name": "Demonstrated agent practice",
"detail": "no agent-authored commits among the last 100",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_agent_authored_commits",
"params": {
"sampled": 100
}
}
],
"max_points": 10
},
{
"key": "automated_maintenance",
"name": "Automated maintenance",
"detail": "no automated dependency updates observed",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_dependency_automation",
"params": {}
}
],
"max_points": 8
},
{
"key": "openssf_scorecard_pinned_dependencies",
"name": "OpenSSF Scorecard: Pinned-Dependencies",
"detail": "dependency not pinned by hash detected -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
}
]
},
{
"key": "ai_code_legibility",
"band": "moderate",
"name": "Code legibility for models",
"note": null,
"notes": [],
"value": 50,
"inputs": {
"primary_language": "JavaScript",
"largest_source_bytes": 497398,
"source_files_sampled": 77,
"oversized_source_files": 7
},
"components": [
{
"key": "type_checkable_code",
"name": "Type-checkable code",
"detail": "JavaScript without a type-check config",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_typecheck_config_language",
"params": {
"language": "JavaScript"
}
}
],
"max_points": 45
},
{
"key": "manageable_file_sizes",
"name": "Manageable file sizes",
"detail": "7/77 source files over 60KB",
"points": 50,
"status": "partial",
"details": [
{
"code": "oversized_source_files",
"params": {
"kb": 60,
"sampled": 77,
"oversized": 7
}
}
],
"max_points": 55
}
]
}
],
"description": "How well is the repo equipped to be developed and maintained with AI coding agents? Carries a deliberately small weight: agent tooling is a real maintenance signal, but its absence must never gate the top of the scale (calibration saturates at raw 91, so 100/100 remains reachable with AI Readiness at zero)."
}
],
"classification": {
"top": [
"library",
"application"
],
"labels": [
"library",
"network-service"
],
"scores": {
"library": 6,
"network-service": 4
},
"primary": "library",
"evidence": [
{
"tier": "distribution",
"label": "library",
"source": "registry:npm",
"weight": 6
},
{
"tier": "dependencies",
"label": "network-service",
"source": "dep:express",
"weight": 4
}
],
"artifacts": [],
"confidence": "medium",
"host_extension": false,
"runs_as_process": true,
"consumed_by_code": true
},
"metrics_version": "2.5.0"
},
"warnings": [
"Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token",
"GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository"
],
"report_type": "repository",
"generated_at": "2026-08-01T00:34:02.224350Z",
"schema_version": "0.27.0",
"badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/l/lethevimlet/sshift.svg",
"full_name": "lethevimlet/sshift",
"license_state": "standard",
"license_spdx": "MIT"
}