{"id":51720394,"url":"https://github.com/lgersman/mdjam","last_synced_at":"2026-07-17T10:04:39.098Z","repository":{"id":366404295,"uuid":"1276124749","full_name":"lgersman/mdjam","owner":"lgersman","description":"mdjam is a TUI markdown viewer where bash code blocks become interactive. Execute fences with a keypress, chain values between blocks via a shared state store, and pipe AI-generated markdown straight in via --stdin. Built on Bun + a Zig-native renderer.","archived":false,"fork":false,"pushed_at":"2026-07-07T10:11:17.000Z","size":2152,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-07T11:18:59.098Z","etag":null,"topics":["bash","jupyter","markdown","shell","terminal","zig"],"latest_commit_sha":null,"homepage":"","language":"Zig","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/lgersman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-06-21T15:12:40.000Z","updated_at":"2026-07-07T10:10:50.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/lgersman/mdjam","commit_stats":null,"previous_names":["lgersman/mdjam"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/lgersman/mdjam","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lgersman%2Fmdjam","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lgersman%2Fmdjam/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lgersman%2Fmdjam/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lgersman%2Fmdjam/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lgersman","download_url":"https://codeload.github.com/lgersman/mdjam/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lgersman%2Fmdjam/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35576837,"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-07-17T02:00:06.162Z","response_time":116,"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":["bash","jupyter","markdown","shell","terminal","zig"],"created_at":"2026-07-17T10:04:37.966Z","updated_at":"2026-07-17T10:04:39.086Z","avatar_url":"https://github.com/lgersman.png","language":"Zig","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mdjam\n\nA terminal markdown viewer where bash code blocks can be executed inline. Scripts run, their output appears directly below the fence, values flow between blocks, and the whole thing stays in your terminal.\n\n![screencast](screencast.gif)\n\nImplemented in [Zig](https://ziglang.org) — ships as a single statically-linked binary with no runtime dependencies.\n\n## Prerequisites\n\nThis project uses [mise](https://mise.jdx.dev) to manage the Zig version and [direnv](https://direnv.net) to activate it automatically when you enter the directory.\n\n1. **Install mise:**\n   ```bash\n   curl https://mise.run | sh\n   ```\n   Then add the activation hook to your shell (`~/.bashrc`, `~/.zshrc`, etc.):\n   ```bash\n   eval \"$(mise activate bash)\"   # replace bash with zsh / fish as needed\n   ```\n\n2. **Install direnv:**\n   ```bash\n   # macOS\n   brew install direnv\n   # Ubuntu / Debian\n   sudo apt install direnv\n   ```\n   Then hook it into your shell:\n   ```bash\n   echo 'eval \"$(direnv hook bash)\"' \u003e\u003e ~/.bashrc   # replace bash with zsh if needed\n   ```\n\n3. **Allow direnv** in the project directory:\n   ```bash\n   direnv allow\n   ```\n   mise will install Zig and ZLS at the versions declared in `.mise.toml` and activate them automatically on every subsequent `cd`.\n\n## Installation\n\n**Linux and macOS** — download and install the prebuilt binary:\n\n```bash\ncurl -sSL https://raw.githubusercontent.com/lgersman/mdjam/main/install.sh | sh\n```\n\nInstalls to `~/.local/bin` by default. Override with `INSTALL_DIR`:\n\n```bash\nINSTALL_DIR=/usr/local/bin curl -sSL https://raw.githubusercontent.com/lgersman/mdjam/main/install.sh | sh\n```\n\nPin a specific version with `VERSION`:\n\n```bash\nVERSION=0.2.0 curl -sSL https://raw.githubusercontent.com/lgersman/mdjam/main/install.sh | sh\n```\n\n**Windows** — use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) and run the Linux install command above.\n\n**Build from source** (requires [Zig](https://ziglang.org) ≥ 0.16.0):\n\n```bash\nzig build -Doptimize=ReleaseSafe\n# binary is at zig-out/bin/mdjam\n```\n\n## Usage\n\n```\nmdjam [options] \u003cfile.md\u003e\nmdjam --stdin [options]\n\nOptions:\n  --stdin            Read markdown from stdin instead of a file\n  --no-auto          Suppress auto-execution of auto:true blocks\n  --no-watch         Disable file watch / reload on change\n  --theme \u003cname\u003e     dark | light | dracula | tokyo-night  (default: dark)\n  --verbose          Show document frontmatter as a header\n  --delegate         On exit, write the focused block's stdout/stderr and use its exit code\n  --version          Print version\n  --help             Show help\n```\n\n`--stdin` is useful for piping dynamically generated markdown, e.g. from an AI agent:\n\n```bash\nai-agent \"explain this error\" | mdjam --stdin\necho \"# Hello\\n\\`\\`\\`bash\\necho hi\\n\\`\\`\\`\" | mdjam --stdin\n```\n\nWatch mode is automatically disabled when reading from stdin.\n\n## Keyboard map\n\n| Key | Action |\n|---|---|\n| `j` / `↓` | Scroll down |\n| `k` / `↑` | Scroll up |\n| `Space` / `PgDn` | Page down |\n| `b` / `PgUp` | Page up |\n| `g` | Jump to top |\n| `G` | Jump to bottom |\n| `Tab` | Focus next executable block |\n| `Enter` | Execute focused block |\n| `Esc` | Cancel running block |\n| `r` | Reload document |\n| `?` | Show / hide keyboard help |\n| `Ctrl+C` | Quit (runs teardown script if declared) |\n\n## Document format\n\n### Frontmatter\n\nOptional YAML frontmatter at the top of the file controls document-level behaviour.\n\n```markdown\n---\ntitle: Deploy Staging\nprerequisites:\n  tools: [kubectl, helm, jq]\n  env: [AWS_PROFILE, KUBECONFIG]\nsetup: |\n  export BASE_URL=https://staging.example.com\nteardown: |\n  echo \"Session ended\"\nvariables:\n  environment: staging\n  namespace:\n    description: Kubernetes namespace to deploy into\n    default: staging\n---\n```\n\n| Field | Description |\n|---|---|\n| `title` | Displayed in the viewer header |\n| `prerequisites.tools` | CLI tools that must be on `$PATH` before the viewer starts |\n| `prerequisites.env` | Environment variables that must be set before the viewer starts |\n| `setup` | Bash script that runs once after prerequisites pass, before rendering |\n| `teardown` | Bash script that runs on quit (`Ctrl+C`) |\n| `variables` | Named values pre-populated into the state store before any block runs |\n| `variables.\u003cname\u003e` | Plain scalar (`name: value`) for a value with no description |\n| `variables.\u003cname\u003e.description` | Documents what the value is for |\n| `variables.\u003cname\u003e.default` | The value itself, when using the nested form |\n| `variables.\u003cname\u003e.readonly` | Parsed for parity with code-block variables; not currently enforced in the frontmatter editor |\n| `variables.\u003cname\u003e.output` | When `true`, this variable's current value is included in the JSON object mdjam prints to stdout on exit |\n\nIf any prerequisite is unmet, mdjam exits immediately and prints why to stderr — the viewer never opens. If `setup` or `teardown` exits non-zero, mdjam's own exit code reflects that (execution isn't blocked, and an error banner is shown at the top of the document for a failed `setup`). Setup/teardown stdout is only shown with `--verbose`; stderr is always shown, printed to the real terminal once mdjam exits.\n\n#### Output variables\n\nMark a frontmatter variable with `output: true` to have its final value printed as JSON on stdout when mdjam exits normally (`Ctrl+C`, not a prerequisite/read failure):\n\n```markdown\n---\nvariables:\n  build_tag:\n    readonly: true\n    output: true\n  work_dir:\n    output: true\n---\n```\n\n```json\n{\"build_tag\":\"v20260706-abc123\",\"work_dir\":\"/tmp/tmp.XXXXXX\"}\n```\n\nValues reflect whatever is in the state store at exit time — the frontmatter default, or whatever a block wrote via `::set-output`/`export` since. This pairs well with `--delegate` and `--stdin` for scripting mdjam as a step in another program.\n\n### Executable code blocks\n\nBash fences become interactive. A plain fence with no metadata is manually executable with no variables or outputs:\n\n````markdown\n```bash\necho \"hello\"\n```\n````\n\nAdd a YAML metadata comment block at the top of the fence body to declare variables, outputs, dependencies, and auto-execution:\n\n````markdown\n```bash\n# ---\n# id: fetch-token\n# description: Retrieve auth token\n# auto: false\n# variables:\n#   API_HOST:\n#     description: Base URL\n#     default: https://api.example.com\n#     readonly: false\n# outputs: [TOKEN]\n# depends: []\n# ---\nTOKEN=$(curl -sf \"$API_HOST/auth/token\")\necho \"::set-output name=TOKEN::$TOKEN\"\n```\n````\n\n#### Metadata fields\n\n| Field | Type | Description |\n|---|---|---|\n| `id` | string | Unique name for this block, used by `depends` in other blocks |\n| `description` | string | Label shown in the block header |\n| `auto` | boolean | Execute automatically on document load (default: `false`) |\n| `variables` | map | Named values the block reads from the state store — same shape as frontmatter's `variables` |\n| `variables.\u003cname\u003e.description` | string | Shown above the field |\n| `variables.\u003cname\u003e.default` | string | Value used when no upstream block has set the key |\n| `variables.\u003cname\u003e.readonly` | boolean | Display only — cannot be edited, must come from upstream (default: `false`) |\n| `outputs` | string[] | Keys this block will export via `::set-output` |\n| `depends` | string[] | IDs of blocks that must succeed before this one runs |\n\n### Exporting values between blocks\n\nTwo mechanisms write values into the shared state store:\n\n**`::set-output` syntax** — intercepts the line without showing it in the output panel:\n\n```bash\necho \"::set-output name=MY_KEY::my_value\"\n```\n\n**Plain `export`** — any variable exported by the script that was not already in the environment is captured automatically:\n\n```bash\nexport MY_KEY=my_value\necho \"MY_KEY is: $MY_KEY\"\n```\n\nDownstream blocks receive every state store value as `MDJAM_\u003cKEY\u003e` environment variables:\n\n```bash\necho \"MY_KEY is: $MDJAM_MY_KEY\"\n```\n\nValues written by a block with `id: my-block` are stored under both the bare key (`TOKEN`) and the namespaced key (`my-block.TOKEN`). Values written by `setup` use bare keys only.\n\n### Block status indicators\n\nThe status bar shows a `[indicator]` badge for the focused block, except for\n`idle` (nothing has happened yet) and `done` results from `auto: true`\nexecution (the user never asked to see them) — both are shown as no badge at all.\n\n| Indicator | Meaning |\n|---|---|\n| spinner (`⠋⠙⠹⠸...`) | Script is running |\n| `done` | Succeeded |\n| `failed` | Exited non-zero |\n| `cancelled` | Cancelled with `Esc` |\n| `blocked` | Prerequisites failed |\n\n## Using mdjam as an agent tool\n\nThe non-interactive pattern: pipe markdown in via `--stdin`, mark blocks `auto: true` so they run without keypresses, and use `--delegate` to forward the focused block's stdout/stderr and exit code back to the caller.\n\n### Example — Claude Code tool configuration\n\nAdd this to your project's `CLAUDE.md` or `~/.claude/CLAUDE.md`:\n\n```markdown\n## Available tools\n\n**mdjam** — run a markdown runbook non-interactively and capture output.\n\nUsage:\n  printf '%s' \"$MARKDOWN\" | mdjam --stdin --delegate --no-watch\n\n- Mark bash fences with `auto: true` to execute them on load.\n- `--delegate` forwards the focused block's stdout/stderr and mirrors its exit code.\n- `echo \"::set-output name=KEY::value\"` inside a block captures KEY into the state store.\n```\n\n## Development\n\n```bash\n# Debug build and run\nzig build run -- examples/01-hello.md\n\n# Run tests\nzig build test\n\n# Format source\nzig fmt src/\n\n# Release build (statically linked)\nzig build -Doptimize=ReleaseSafe\n\n# Cross-compile\nzig build -Dtarget=aarch64-linux-musl -Doptimize=ReleaseSafe\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flgersman%2Fmdjam","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flgersman%2Fmdjam","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flgersman%2Fmdjam/lists"}