{"id":50324978,"url":"https://github.com/pathcosmos/cli-ascii-usage","last_synced_at":"2026-05-29T05:04:28.079Z","repository":{"id":354069642,"uuid":"1219857499","full_name":"pathcosmos/cli-ascii-usage","owner":"pathcosmos","description":"ASCII disk usage (physical disks, partitions, filesystems) for macOS and Linux","archived":false,"fork":false,"pushed_at":"2026-04-27T01:30:04.000Z","size":104,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-27T02:33:22.281Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pathcosmos.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-24T09:38:19.000Z","updated_at":"2026-04-27T01:30:08.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/pathcosmos/cli-ascii-usage","commit_stats":null,"previous_names":["pathcosmos/cli-ascii-usage"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/pathcosmos/cli-ascii-usage","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pathcosmos%2Fcli-ascii-usage","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pathcosmos%2Fcli-ascii-usage/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pathcosmos%2Fcli-ascii-usage/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pathcosmos%2Fcli-ascii-usage/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pathcosmos","download_url":"https://codeload.github.com/pathcosmos/cli-ascii-usage/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pathcosmos%2Fcli-ascii-usage/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33637486,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-29T02:00:06.066Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-29T05:04:14.141Z","updated_at":"2026-05-29T05:04:28.067Z","avatar_url":"https://github.com/pathcosmos.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dleft\n\n[![CI](https://github.com/pathcosmos/cli-ascii-usage/actions/workflows/ci.yml/badge.svg)](https://github.com/pathcosmos/cli-ascii-usage/actions/workflows/ci.yml)\n[![npm](https://img.shields.io/npm/v/@pathcosmos/dleft.svg)](https://www.npmjs.com/package/@pathcosmos/dleft)\n![Node](https://img.shields.io/badge/node-%E2%89%A522-brightgreen)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)\n![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Linux-lightgrey)\n\nASCII disk usage for macOS and Linux — physical disks, partitions, and mounted filesystems, rendered as dense tables with usage bars. **Read-only**: never mutates disk state.\n\n```\n$ dleft\nPHYSICAL DISKS\nID      MODEL              SIZE (TiB)  USED (TiB)  FREE (GiB)  USE%  BAR\ndisk3   APPLE SSD AP1024N         0.9         0.4       507.6   45%  █████▓▓▓▓▓▓\ndisk13  External 2TB              1.8         1.0       790.0   58%  ██████▓▓▓▓▓\n\nFILESYSTEMS\nMOUNT             DEVICE          FSTYPE  SIZE (GiB)  USED (GiB)  FREE (GiB)  USE%  BAR\n/mnt/data         /dev/sdb1       ext4        1863.0       931.3       931.3   50%  █████▓▓▓▓▓\n/home             /dev/mapper/…   ext4         367.8       191.9       175.9   52%  █████▓▓▓▓▓\n/                 /dev/mapper/…   ext4          98.0        33.0        67.0   33%  ███▓▓▓▓▓▓▓\n```\n\nCells turn yellow at ≥75 % use and red at ≥90 %.\n\n## Quick start\n\n### From source (current path until v0.1.0 hits npm)\n\n```sh\ngit clone https://github.com/pathcosmos/cli-ascii-usage\ncd cli-ascii-usage\nnpm install\nnpm run build\nnode dist/cli.js              # run once\nnpm link                      # optional: expose `dleft` globally\n```\n\n### From npm\n\n```sh\nnpm install -g @pathcosmos/dleft\n# or one-shot:\nnpx @pathcosmos/dleft\n```\n\nThe bin name is `dleft` (so the global install gives you `dleft`, not `pathcosmos-dleft`). Requires Node.js ≥ 22. Single-file ESM bundle, no postinstall hooks.\n\n## Usage\n\n```sh\ndleft [options]\n```\n\n| Flag | Description | Default |\n|---|---|---|\n| `-j`, `--json` | Emit JSON (`schemaVersion: 1`); suppresses color. | off |\n| `-a`, `--all` | Include pseudo-filesystems (tmpfs, overlay, etc.). | off |\n| `-s`, `--sort \u003cfield\u003e` | Sort by `size` / `used` / `free` / `use%` / `name`. | `size` |\n| `--si` | SI units (KB, GB, TB) instead of IEC (KiB, GiB, TiB). | IEC |\n| `--no-bars` | Hide the bar column. | bars on |\n| `--no-color` | Disable ANSI color. Respects `NO_COLOR` env. | auto |\n| `--only \u003csection\u003e` | Render only `disks` or `fs`. | both |\n| `-h`, `--help` | Show help. | — |\n| `-V`, `--version` | Print version. | — |\n\n### Recipes\n\n```sh\ndleft --sort use%             # which mounts are full?\ndleft -a                      # show tmpfs, overlay, etc.\ndleft --only disks            # just the physical-disk summary\ndleft --no-color \u003e usage.txt  # capture for diff later\ndleft --json | jq '.filesystems[] | select(.usedBytes / .sizeBytes \u003e 0.9) | .mountpoint'\n```\n\n### Environment\n\n- `NO_COLOR=1` — disables color (standard).\n- `DLEFT_ASCII=1` — forces ASCII bar chars (`#`, `-`) instead of unicode blocks.\n\n### Exit codes\n\n| Code | Meaning |\n|---|---|\n| `0` | Success (may still emit warnings to stderr). |\n| `1` | Fatal: unsupported platform, missing required command, parse failure. |\n| `2` | Invalid argument. |\n\n## How it works\n\n```\n                ┌──────────────────────┐\n                │   src/cli.ts         │  parseArgs → RenderOptions\n                └────────────┬─────────┘\n                             │\n        ┌────────────────────┴────────────────────┐\n        ▼                                         ▼\n┌──────────────────┐                    ┌──────────────────┐\n│ collectors/      │                    │ render/          │\n│  ├ darwin.ts     │  diskutil + df     │  ├ disks.ts      │\n│  ├ linux.ts      │  lsblk   + df      │  ├ filesystems.ts│\n│  └ index.ts      │                    │  ├ bar / unit /  │\n└────────┬─────────┘                    │  │  color / width│\n         │ DiskReport                   │  └ index.ts      │\n         └─────────────────►─────────────►  pure(): string │\n                                        └──────────────────┘\n```\n\n- **Collectors** spawn subprocesses through a `CommandRunner` interface (DI seam) and parse output into a `DiskReport`. The production runner uses Node's `execFile` with a fixed argv array — no shell, no injection surface — and a 5-second timeout. Tests use a `FakeRunner` seeded from committed fixtures.\n- **Render** is a pure function: `render(report, opts) → string`. No stdout, no env, no TTY checks. All environmental decisions (color, width, unicode) happen in `cli.ts` and are passed in.\n- A timeout on one data source emits a warning and the others still render (partial success → exit 0).\n\n## Platform notes\n\n| Platform | Required commands |\n|---|---|\n| macOS | `diskutil`, `df` |\n| Linux | `lsblk`, `df` |\n\n### macOS: APFS containers\n\nOne physical-disk row per APFS container. Used bytes come from container-level `CapacityCeiling - CapacityFree`, **not** Σ per-volume `CapacityInUse` — summing volume usage double-counts shared space across APFS volumes in the same container. If `diskutil apfs list` times out, the disk summary is omitted with a warning rather than inferred from `df` (silently wrong numbers in a disk-space tool are worse than fewer numbers).\n\n### Linux: LVM, LUKS, overlay\n\nFilesystems are joined to physical disks by walking the `lsblk` tree. LVM volumes and LUKS-backed partitions map to their parent block device. Bind mounts and overlay filesystems are flagged as pseudo and hidden by default (`--all` to show).\n\n## JSON output\n\n```sh\ndleft --json\n```\n\nEmits a `DiskReport` document. The shape is stable under `schemaVersion: 1`; gate downstream consumers on that field:\n\n```sh\ndleft --json | jq 'if .schemaVersion == 1 then .filesystems else error(\"unexpected dleft schema\") end'\n```\n\n`schemaVersion` will only bump on a breaking change to the report shape — non-breaking additions keep `1`.\n\n## Why not just `df`?\n\n- `df` doesn't show physical-disk totals or APFS container reconciliation.\n- `df` output alignment breaks on long device paths (`/dev/mapper/…`).\n- `df` doesn't know about bind mounts, pseudo-filesystems, or LVM parent disks.\n- `dleft` isn't a replacement — it's a denser skim for \"where is my space?\"\n\n## Development\n\n```sh\nnpm install\nnpm test           # Vitest: unit + collectors + CLI + smoke (~1.4 s)\nnpm run typecheck  # tsc --noEmit\nnpm run build      # tsup → dist/cli.js (12 KB ESM bundle, shebang)\nnode dist/cli.js   # run the built bundle\n```\n\nFixtures live under `tests/fixtures/{darwin,linux}/`. `FakeRunner` (`tests/helpers/fake-runner.ts`) injects fixture strings so collector tests run the real parser without spawning subprocesses. Refresh fixtures via the snippets in `tests/fixtures/README.md`.\n\nCI matrix: Node 22 / 24 × Ubuntu / macOS. Releases publish to npm via OIDC keyless provenance on `v*` tag push.\n\n## License\n\nMIT. See [LICENSE](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpathcosmos%2Fcli-ascii-usage","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpathcosmos%2Fcli-ascii-usage","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpathcosmos%2Fcli-ascii-usage/lists"}