{"id":49498187,"url":"https://github.com/mihai-valentin/cdp","last_synced_at":"2026-07-02T14:00:48.382Z","repository":{"id":354989956,"uuid":"1225786829","full_name":"mihai-valentin/cdp","owner":"mihai-valentin","description":"Tiny bash CLI that replaces boring cd navigation","archived":false,"fork":false,"pushed_at":"2026-06-17T14:36:55.000Z","size":132,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-17T16:51:57.183Z","etag":null,"topics":["bash","cli","utility"],"latest_commit_sha":null,"homepage":"","language":"Shell","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/mihai-valentin.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","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-30T16:31:57.000Z","updated_at":"2026-06-17T14:40:31.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mihai-valentin/cdp","commit_stats":null,"previous_names":["mihai-valentin/cdp"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/mihai-valentin/cdp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mihai-valentin%2Fcdp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mihai-valentin%2Fcdp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mihai-valentin%2Fcdp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mihai-valentin%2Fcdp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mihai-valentin","download_url":"https://codeload.github.com/mihai-valentin/cdp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mihai-valentin%2Fcdp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35050017,"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-02T02:00:06.368Z","response_time":173,"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","cli","utility"],"created_at":"2026-05-01T11:01:25.607Z","updated_at":"2026-07-02T14:00:48.376Z","avatar_url":"https://github.com/mihai-valentin.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cdp — change-dir-project\n\n[![CI](https://github.com/mihai-valentin/cdp/actions/workflows/ci.yml/badge.svg)](https://github.com/mihai-valentin/cdp/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\nTiny bash CLI that replaces `cd /long/path/to/project` with `cdp \u003clabel\u003e`. Per-project **macros** (defined in an SSH-style config file) let you bundle a jump with the commands you usually type after it: `cdp myproject deploy` jumps and runs the deploy steps.\n\n```bash\n# Jump to a project\ncdp myproject\n\n# Jump and run a macro defined for that project\ncdp myproject deploy\n\n# Add / list / remove projects\ncdp add myproject /home/user/myproject\ncdp ls\ncdp rm myproject\n```\n\nNo npm, no composer, no Go binary, no Python. Bash 4+, coreutils, and an `flock` from util-linux are all you need at runtime. `tmux` is required only if you use [Tmux blocks](#tmux-integration); macros and bare jumps work without it.\n\n## Install\n\n### From source (recommended)\n\n```bash\ngit clone https://github.com/mihai-valentin/cdp.git\ncd cdp\nmake install                       # installs to $HOME/.local\n# Add this to your ~/.bashrc or ~/.zshrc:\neval \"$(~/.local/bin/cdp init bash)\"\n```\n\n`make install` puts `cdp` in `$PREFIX/bin`, scripts in `$PREFIX/libexec/cdp/`, and helper libs in `$PREFIX/lib/cdp/`. Override the prefix with `make install PREFIX=/usr/local`.\n\n### Without `make`\n\n```bash\n./install.sh\n```\n\n`install.sh` probes your `PATH` and picks `$HOME/.local` or `/usr/local` accordingly. Override with `./install.sh --prefix=\u003cpath\u003e`.\n\n### From a release tarball\n\n```bash\nVERSION=1.7.0\ncurl -sLO https://github.com/mihai-valentin/cdp/releases/download/v${VERSION}/cdp-${VERSION}.tar.gz\ncurl -sLO https://github.com/mihai-valentin/cdp/releases/download/v${VERSION}/cdp-${VERSION}.tar.gz.sha256\nsha256sum -c cdp-${VERSION}.tar.gz.sha256\ntar -xzf cdp-${VERSION}.tar.gz\ncd cdp-${VERSION} \u0026\u0026 ./install.sh\n```\n\n### Uninstall\n\n```bash\nmake uninstall\n```\n\n## Usage\n\n### Jump\n\n```bash\ncdp \u003clabel\u003e\n```\n\n`cdp myproject` resolves the `myproject` project's `Path` and `cd`s your shell into it. The shim function emitted by `cdp init` performs the actual `cd` — a regular binary cannot mutate its parent's cwd.\n\n### Jump + macro\n\n```bash\ncdp \u003clabel\u003e \u003cmacro\u003e\n```\n\n`cdp myproject deploy` jumps to `myproject` and then runs the `deploy` macro's `Run` lines, in order, **in your interactive shell**. Macros aren't sandboxed — `export FOO=bar` inside a macro affects your shell, by design.\n\n### Manage projects\n\n```bash\ncdp add \u003clabel\u003e \u003cpath\u003e      # add an entry\ncdp rm \u003clabel\u003e              # remove an entry\ncdp ls                      # list projects (readable on a terminal)\ncdp ls -l                   # force the readable layout when piping\ncdp edit                    # open the config in $VISUAL / $EDITOR\ncdp check                   # parse the config and report validity\n```\n\n`cdp add` derives the project label from the directory's basename when you don't pass one, so the common case is just `cdp add` from inside the project. The path argument accepts `.`, a relative path, `~`, or an absolute path and is canonicalized to an absolute path before it's written. If the basename isn't a valid label (e.g. it contains a `.`), pass one explicitly: `cdp add \u003clabel\u003e \u003cpath\u003e`. Forms: `cdp add` (current dir), `cdp add \u003cpath\u003e`, `cdp add \u003clabel\u003e \u003cpath\u003e`.\n\nOn a terminal, `cdp ls` prints a readable per-project block — the path on its own line, and actions grouped by kind (`macros`, `tmux`, `inherited`):\n\n```\ncdp\n  path       /home/user/xlnf/cdp\n  inherited  claude (from xlnf)\n\nxlnf\n  path       /home/user/xlnf\n  macros     deploy, logs\n  tmux       dev\n```\n\nWhen the output is **piped** (not a terminal), `cdp ls` switches to a stable, `awk`-friendly `LABEL\\tPATH\\tACTIONS` line per project — each entry in `ACTIONS` is `\u003cname\u003e:\u003ckind\u003e` (`\u003ckind\u003e` is `macro` or `tmux`) in source order, with macros inherited from a `Group` (see below) carrying a trailing `@\u003cgroup\u003e` suffix (e.g. `claude:macro@xlnf`). Pass `-l`/`--long` to force the readable layout even when piping (e.g. `cdp ls -l | less`). Macros and tmux blocks have no dedicated subcommand — use `cdp edit` (or any editor) to add or modify them.\n\n`cdp edit` resolves the editor in the standard `$VISUAL` → `$EDITOR` → `vi` chain. If the config file does not yet exist, the parent directory is created so the editor opens at the resolved path; the file is materialized on save.\n\n`cdp check` runs the parser over the resolved config and reports whether it is valid — exit `0` and a brief OK line on stderr on success, exit `2` if the file is missing or unreadable, exit `65` with the parser's `config:\u003cline\u003e: \u003cmessage\u003e` on a parse error. Pairs naturally with `cdp edit` for an edit-then-validate loop, and is safe to use as a precondition: `cdp check \u0026\u0026 cdp \u003clabel\u003e`.\n\n### Tmux integration\n\n`cdp \u003clabel\u003e \u003ctmux-name\u003e` materializes a per-project tmux pane layout (or attaches if the session already exists). Each `Tmux` block names its layout with a small DSL — `h:[…]` for side-by-side splits, `v:[…]` for stacked splits, with arbitrary nesting — and one `Pane \u003cname\u003e` block per pane carrying the commands to send.\n\n```text\nProject myapp\n    Path /home/user/myapp\n    Tmux dev\n        Layout h:[main | v:[test | logs]]\n        Pane main\n            Run pnpm dev\n        Pane test\n            Run pnpm test --watch\n        Pane logs\n            Run tail -f var/log/app.log\n```\n\nThen `cdp myapp dev` builds the layout (one `main` pane on the left; `test` over `logs` on the right) and attaches you. If the `myapp-dev` tmux session already exists, the user's running panes are kept as-is and `cdp` just attaches.\n\nDirection convention follows tmux: `h` = horizontal divider (panes side-by-side), `v` = vertical divider (panes stacked). The full grammar, walk semantics, and protocol live in [`docs/specs/tmux-layout.md`](docs/specs/tmux-layout.md).\n\n### Shell shim\n\n```bash\ncdp init bash      # prints shim source for bash\ncdp init zsh       # prints shim source for zsh (identical body in V1)\n```\n\nSource it once per interactive shell, typically by adding `eval \"$(cdp init bash)\"` to your `~/.bashrc` (or `~/.zshrc`).\n\n## Config file\n\nThe config file lives at `$CDP_CONFIG`, or `$XDG_CONFIG_HOME/cdp/config`, or `$HOME/.config/cdp/config` — searched in that order.\n\n```text\nProject myproject\n    Path /home/user/myproject\n    Macro deploy\n        Run cd ./static-pages\n        Run ./scripts/publish.sh\n    Macro logs\n        Run tail -f /var/log/myproject.log\n\nProject api\n    Path /home/user/projects/api\n    Macro dev\n        Run pnpm install\n        Run pnpm --filter web dev\n```\n\nThe formal grammar lives in [`docs/specs/config-format.md`](docs/specs/config-format.md). Highlights: indentation-based blocks, case-insensitive keywords, `#` line comments only (no trailing comments), tilde expansion at parse time.\n\n### Groups (shared macros)\n\nWrap a set of related projects under a `Group` block to share `Macro`s across them. A group's macros are inherited by every nested project; a project's own macro of the same name **shadows** the inherited one.\n\n```text\nGroup xlnf\n    Macro claude\n        Run ../xlnfclaude -c\n\n    Project xlnf\n        Path /home/user/xlnf\n        Macro claude              # shadows the group's claude\n            Run ./xlnfclaude -c\n\n    Project cdp\n        Path /home/user/xlnf/cdp\n                                  # inherits claude from the group\n\n    Project shawarma\n        Path /home/user/xlnf/shawarma\n                                  # inherits claude\n\nProject nexus                     # column 0 — outside any group\n    Path /home/user/nexus\n    Macro claude\n        Run claude -c\n```\n\nThen `cdp cdp claude` runs `../xlnfclaude -c` (inherited), `cdp xlnf claude` runs `./xlnfclaude -c` (override), and `cdp nexus claude` runs `claude -c` (no group affiliation). `cdp ls` shows the inheritance:\n\n```\ncdp     /home/user/xlnf/cdp     claude:macro@xlnf\nnexus   /home/user/nexus        claude:macro\nxlnf    /home/user/xlnf         claude:macro\n```\n\nGroup rules in brief: nesting determines membership (one group per project); a `Group` carries `Macro`s and, optionally, one `Path` root (see below) — `Tmux` and nested `Group` are parse errors; project-local always wins on name collision.\n\n#### Group workspace root\n\nAdd a single `Path` line directly under a `Group` (before its projects) to set a **workspace root**. Member projects are then resolved against it:\n\n```text\nGroup xlnf\n    Path /home/user/xlnf          # the group's workspace root\n    Project xlnf                  # no Path  → /home/user/xlnf  (the root itself)\n    Project cdp\n        Path cdp                  # relative → /home/user/xlnf/cdp\n    Project notes\n        Path /opt/notes           # absolute → /opt/notes  (wins, escapes the root)\n```\n\nA relative member `Path` is joined onto the root; an absolute member `Path` wins (escapes the root); a member with no `Path` resolves to the root itself. The group root is tilde-expanded and must be absolute. A `Group` without a `Path` root behaves as before — its members must each declare an absolute `Path`.\n\n## Shell support\n\n- **bash 4+** and **zsh** are tested daily.\n- macOS ships bash 3.2 by default. Install a modern bash via Homebrew (`brew install bash`) or use zsh (the system default on recent macOS).\n- fish and other shells are not supported in V1.\n\n## Troubleshooting\n\n**`cdp: command not found`** — the eval line isn't in your rc file, or your rc file isn't being read by your shell. Confirm with `type cdp`; if it returns \"not found\", add the eval line and start a fresh shell.\n\n**`cdp: requires bash 4 or newer`** — you're on macOS's default bash 3.2. Install bash 4+ (`brew install bash`) and exec it as your shell, or switch to zsh.\n\n**`cdp: config file not found at ...`** — the config file doesn't exist yet. Run `cdp add \u003clabel\u003e \u003cpath\u003e` to bootstrap it; the parent directory will be created automatically.\n\n**`cdp myproject` runs but my cwd doesn't change** — your shell's `cdp` is being shadowed by an alias or another function. Run `type cdp`; if it shows an alias, `unalias cdp` and re-source your rc file.\n\n## Status\n\nv1.7.0 — `cdp ls` now prints a readable per-project block on a terminal (path and actions grouped by kind), while keeping the stable TAB-separated form for pipes/scripts; `-l`/`--long` forces the readable layout. Built on the v1.6.x `Group` `Path` root / v1.5.x `cdp add` ergonomics / v1.4.x `Group` blocks / v1.3.x `cdp check` / v1.2.x `cdp edit` / v1.1.x tmux-integration line. The roadmap and open items are tracked as GitHub issues; the formal grammar and protocol live under [`docs/specs/`](docs/specs/).\n\n## Contributing\n\nSee [`CONTRIBUTING.md`](CONTRIBUTING.md).\n\n## License\n\nMIT — see [`LICENSE`](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmihai-valentin%2Fcdp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmihai-valentin%2Fcdp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmihai-valentin%2Fcdp/lists"}