{"id":35419641,"url":"https://github.com/dapi/port-selector","last_synced_at":"2026-02-02T18:17:02.881Z","repository":{"id":331452176,"uuid":"1126684653","full_name":"dapi/port-selector","owner":"dapi","description":"CLI utility for automatic free port selection from a configured range.","archived":false,"fork":false,"pushed_at":"2026-01-26T20:14:22.000Z","size":299,"stargazers_count":2,"open_issues_count":11,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-01-27T07:47:52.706Z","etag":null,"topics":["vibe-coding"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dapi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"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-01-02T11:47:24.000Z","updated_at":"2026-01-26T19:46:22.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/dapi/port-selector","commit_stats":null,"previous_names":["dapi/port-selector"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/dapi/port-selector","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dapi%2Fport-selector","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dapi%2Fport-selector/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dapi%2Fport-selector/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dapi%2Fport-selector/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dapi","download_url":"https://codeload.github.com/dapi/port-selector/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dapi%2Fport-selector/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29016826,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-02T16:17:30.374Z","status":"ssl_error","status_checked_at":"2026-02-02T15:58:50.469Z","response_time":58,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["vibe-coding"],"created_at":"2026-01-02T16:14:55.345Z","updated_at":"2026-02-02T18:17:02.862Z","avatar_url":"https://github.com/dapi.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# port-selector\n\n[![CI](https://github.com/dapi/port-selector/actions/workflows/ci.yml/badge.svg)](https://github.com/dapi/port-selector/actions/workflows/ci.yml)\n[![Release](https://github.com/dapi/port-selector/actions/workflows/release.yml/badge.svg)](https://github.com/dapi/port-selector/actions/workflows/release.yml)\n[![Go Report Card](https://goreportcard.com/badge/github.com/dapi/port-selector)](https://goreportcard.com/report/github.com/dapi/port-selector)\n[![Parallel AI Agents](https://img.shields.io/badge/Parallel_AI-Agents_Ready-00d4aa)](https://github.com/dapi/port-selector)\n\n[🇷🇺 Русская версия](README.ru.md)\n\nCLI utility for automatic free port selection from a configured range.\n\n## Motivation\n\nWhen developing with AI agents (Claude Code, Cursor, Copilot Workspace, etc.), you often have multiple parallel agents working on tasks in separate git worktrees. Each agent may need to start web servers for e2e testing, and they all need free ports.\n\n**Problem:** When 5-10 agents simultaneously try to start dev servers on port 3000, conflicts occur.\n\n**Solution:** `port-selector` automatically finds and returns the first free port from a configured range.\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│  Agent 1 (worktree: feature-auth)                           │\n│  $ PORT=$(port-selector) \u0026\u0026 npm run dev -- --port $PORT     │\n│  → Server running on http://localhost:3000                  │\n├─────────────────────────────────────────────────────────────┤\n│  Agent 2 (worktree: feature-dashboard)                      │\n│  $ PORT=$(port-selector) \u0026\u0026 npm run dev -- --port $PORT     │\n│  → Server running on http://localhost:3001                  │\n├─────────────────────────────────────────────────────────────┤\n│  Agent 3 (worktree: bugfix-login)                           │\n│  $ PORT=$(port-selector) \u0026\u0026 npm run dev -- --port $PORT     │\n│  → Server running on http://localhost:3002                  │\n└─────────────────────────────────────────────────────────────┘\n```\n\n## Further Reading\n\nThe practice of running multiple AI agents in parallel using git worktrees is becoming increasingly popular. Each worktree provides complete file isolation, but all agents still share network resources — including ports. When agents run dev servers, e2e tests, or preview deployments, port conflicts become inevitable.\n\n`port-selector` solves this by providing automatic port allocation with a freeze period, ensuring each agent gets a unique port even when multiple agents start simultaneously.\n\n**Articles about parallel AI agent development:**\n\n- [How we're shipping faster with Claude Code and Git Worktrees](https://incident.io/blog/shipping-faster-with-claude-code-and-git-worktrees) — incident.io's experience running multiple Claude Code sessions with custom worktree manager\n- [Parallel AI Development with Git Worktrees](https://sgryt.com/posts/git-worktree-parallel-ai-development/) — the \"three pillars\": state isolation, parallel execution, asynchronous integration\n- [How Git Worktrees Changed My AI Agent Workflow](https://nx.dev/blog/git-worktrees-ai-agents) — practical scenarios where agents work in background while you continue coding\n- [Git Worktrees: The Secret Weapon for Running Multiple AI Agents](https://medium.com/@mabd.dev/git-worktrees-the-secret-weapon-for-running-multiple-ai-coding-agents-in-parallel-e9046451eb96) — why worktrees became essential in the AI-assisted development era\n- [Parallel Coding Agents with Container Use and Git Worktree](https://www.youtube.com/watch?v=z1osqcNQRvw) — video walkthrough of three parallel agent workflows\n\n## Installation\n\n### Homebrew\n\n```bash\nbrew tap dapi/tap\nbrew install port-selector\n```\n\nUpdate:\n\n```bash\nbrew upgrade port-selector\n```\n\n### One-liner (for your main branch **master**)\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/dapi/port-selector/master/install.sh | sh\n```\n\n#### Common variants\n\nTo /usr/local/bin:\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/dapi/port-selector/master/install.sh | INSTALL_DIR=/usr/local/bin sh\n```\n\nPin version:\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/dapi/port-selector/master/install.sh | VERSION=v0.8.0 sh\n```\n\n### From GitHub Releases\n\n```bash\n# Linux (amd64)\ncurl -L https://github.com/dapi/port-selector/releases/latest/download/port-selector-linux-amd64 -o port-selector\nchmod +x port-selector\nsudo mv port-selector /usr/local/bin/\n\n# macOS (arm64 - Apple Silicon)\ncurl -L https://github.com/dapi/port-selector/releases/latest/download/port-selector-darwin-arm64 -o port-selector\nchmod +x port-selector\nsudo mv port-selector /usr/local/bin/\n\n# macOS (amd64 - Intel)\ncurl -L https://github.com/dapi/port-selector/releases/latest/download/port-selector-darwin-amd64 -o port-selector\nchmod +x port-selector\nsudo mv port-selector /usr/local/bin/\n```\n\n### Build from Source\n\n```bash\ngit clone https://github.com/dapi/port-selector.git\ncd port-selector\nmake install\n```\n\nThis will build the binary and install it to `/usr/local/bin/`.\n\n## Usage\n\n### Basic Usage\n\n```bash\n# Get a free port\nport-selector\n# Output: 3000\n\n# Use in a script\nPORT=$(port-selector)\nnpm run dev -- --port $PORT\n\n# Or in one line\nnpm run dev -- --port $(port-selector)\n```\n\n### Integration Examples\n\n#### Next.js / Vite / any dev server\n\n```bash\n# package.json scripts\n{\n  \"scripts\": {\n    \"dev\": \"PORT=$(port-selector) next dev -p $PORT\",\n    \"dev:vite\": \"vite --port $(port-selector)\"\n  }\n}\n```\n\n#### Docker Compose\n\n```bash\n# In .env or at startup\nexport APP_PORT=$(port-selector)\ndocker-compose up\n```\n\n#### Playwright / e2e tests\n\n```bash\n# In playwright config\nexport BASE_URL=\"http://localhost:$(port-selector)\"\nnpx playwright test\n```\n\n#### direnv (.envrc)\n\nPerfect for git worktree projects — port is automatically assigned when entering the directory:\n\n```bash\n# .envrc\nexport PORT=$(port-selector)\n\n# Now use $PORT in any project script\n# npm run dev will automatically get its unique port\n```\n\n```bash\n# Example workflow with git worktree\n$ cd ~/projects/myapp-feature-auth\ndirenv: loading .envrc\ndirenv: export +PORT\n\n$ echo $PORT\n3000\n\n$ cd ~/projects/myapp-feature-dashboard\ndirenv: loading .envrc\ndirenv: export +PORT\n\n$ echo $PORT\n3001\n```\n\n#### Claude Code / AI Agents\n\nAdd to your project's CLAUDE.md:\n\n```markdown\n## Running dev server\n\nAlways use port-selector before starting dev server:\n\\`\\`\\`bash\nPORT=$(port-selector) npm run dev -- --port $PORT\n\\`\\`\\`\n```\n\n### Directory-based Port Persistence\n\nEach directory automatically gets its own dedicated port. Running `port-selector` from the same directory always returns the same port:\n\n```bash\n$ cd ~/projects/project-a\n$ port-selector\n3000\n\n$ cd ~/projects/project-b\n$ port-selector\n3001\n\n$ cd ~/projects/project-a\n$ port-selector\n3000  # Same port as before!\n```\n\nThis is especially useful with git worktrees — each worktree gets a stable port.\n\n### Named Allocations\n\nA single directory can have multiple named allocations for different services (web, api, database, etc.):\n\n```bash\n# Allocate ports for different services in the same directory\n$ port-selector --name web\n3010\n\n$ port-selector --name api  \n3011\n\n$ port-selector --name db\n3012\n\n# List shows NAME column\n$ port-selector --list\nPORT  DIRECTORY         NAME   STATUS  LOCKED  USER  PID  PROCESS  ASSIGNED\n3010  ~/myproject       web    free    -       -     -    -        2026-01-06 20:00\n3011  ~/myproject       api    free    -       -     -    -        2026-01-06 20:01\n3012  ~/myproject       db     free    -       -     -    -        2026-01-06 20:02\n```\n\nThe default name is `main`, which is used when `--name` is not specified:\n\n```bash\n$ port-selector                    # Uses name \"main\"\n$ port-selector --name main        # Same as above\n```\n\nNamed allocations are useful for:\n- Microservices in monorepo that need different ports\n- Running multiple services from the same directory\n- Separating web, API, and database ports for the same project\n\n### Managing Allocations\n\n```bash\n# List all port allocations\nport-selector --list\n\n# Output:\nPORT  DIRECTORY                 NAME  STATUS  LOCKED  USER  PID  PROCESS  ASSIGNED\n3000  ~/code/merchantly/main    main  free    yes     -     -    -        2026-01-03 20:53\n3001  ~/code/valera             main  free    yes     -     -    -        2026-01-03 21:08\n3010  ~/myproject               web   free    -       -     -    -        2026-01-06 20:00\n3011  ~/myproject               api   free    -       -     -    -        2026-01-06 20:01\n#\n# Tip: Run with sudo for full process info: sudo port-selector --list\n\n# Clear all allocations for current directory\ncd ~/projects/old-project\nport-selector --forget\n# Cleared 2 allocation(s) for /home/user/projects/old-project (most recent was port 3005)\n\n# Clear specific named allocation\nport-selector --forget --name web\n# Cleared allocation 'web' for /home/user/projects/old-project (was port 3010)\n\n# Clear all allocations\nport-selector --forget-all\n# Cleared 5 allocation(s)\n```\n\n### Port Locking\n\nLock a port to prevent it from being allocated to other directories. Useful for long-running services that should keep their port even when restarted:\n\n```bash\n# Lock port for current directory (uses \"main\" name)\ncd ~/projects/my-service\nport-selector --lock\n# Locked port 3000 for 'main'\n\n# Lock named allocation\nport-selector --lock --name web\n# Locked port 3010 for 'web'\n\n# Lock a specific port (allocates AND locks in one step)\ncd ~/projects/new-service\nport-selector --lock 3005\n# Locked port 3005 for 'main'\n\n# Unlock port for current directory\nport-selector --unlock\n# Unlocked port 3000 for 'main'\n\n# Unlock named allocation\nport-selector --unlock --name web\n# Unlocked port 3010 for 'web'\n\n# Unlock a specific port\nport-selector --unlock 3005\n# Unlocked port 3005\n```\n\nWhen using `--lock \u003cPORT\u003e` with a specific port number:\n- If the port is not allocated, it will be allocated to the current directory AND locked\n- This is useful when you want a specific port for a new project\n- The port must be free and within the configured range\n\nWhen a port is locked:\n- It remains allocated to its directory\n- Other directories cannot get this port during allocation\n- The owning directory can still use the port normally\n\n### Discovering Existing Ports\n\nWhen first adopting `port-selector` in an environment where some ports are already in use, you can scan the range to discover and record them:\n\n```bash\nport-selector --scan\n# Scanning ports 3000-3200...\n# Port 3005: already allocated to ~/code/worktrees/feature/103-manager-reply\n# Port 3014: already allocated to ~/code/valera\n#\n# No new ports to record.\n\n# When discovering new ports:\n# Scanning ports 3000-3200...\n# Port 3000: used by node (pid=12345, cwd=~/projects/app-a)\n# Port 3007: used by docker-proxy (pid=585980, cwd=~/projects/my-compose-app)\n#\n# Recorded 2 port(s) to allocations.\n#\n# Tip: Run with sudo for full process info: sudo port-selector --scan\n```\n\nThis creates allocations for busy ports, so `port-selector` will skip them when allocating new ports.\n\n**Note:** Ports owned by root processes (like `docker-proxy`) may not have accessible process info. These ports are still recorded with `(unknown:PORT)` directory marker to prevent allocation conflicts.\n\n#### Running with sudo\n\nTo see full process information (PID, process name) for ports owned by other users, run with sudo. **Important:** use `-E` flag to preserve your environment, otherwise config will be created in `/root/.config/`:\n\n```bash\n# Wrong: creates separate config in /root/.config/port-selector/\nsudo port-selector --scan\n\n# Correct: uses your user's config\nsudo -E port-selector --scan\n\n# Alternative: explicitly pass HOME\nsudo HOME=$HOME port-selector --scan\n```\n\n### Docker Container Detection\n\nWhen a port is published by Docker, the host process is `docker-proxy` with a useless `cwd=/`. `port-selector` automatically resolves the actual project directory:\n\n```bash\nport-selector --scan\n# Port 3007: used by docker-proxy (pid=585980, cwd=/home/user/my-project)\n#                                                  ↑ resolved from container\n```\n\nThe resolution uses:\n1. `com.docker.compose.project.working_dir` label (docker-compose projects)\n2. Bind mount source directory (fallback for plain `docker run`)\n\n**Note:** Requires `docker` CLI to be available.\n\n### Command Line Arguments\n\n```\nport-selector [options]\n\nOptions:\n  -h, --help           Show help message\n  -v, --version        Show version\n  -l, --list           List all port allocations\n  -c, --lock [PORT]    Lock port for current directory and name (or specified port)\n  -u, --unlock [PORT]  Unlock port for current directory and name (or specified port)\n  --forget             Clear all port allocations for current directory\n  --forget --name NAME Clear port allocation for current directory with specific name\n  --forget-all         Clear all port allocations\n  --scan               Scan port range and record busy ports with their directories\n  --name NAME          Use named allocation (default: \"main\")\n  --verbose            Enable debug output (can be combined with other flags)\n```\n\n### Debug Output\n\nUse `--verbose` to see detailed debug information about the port selection process:\n\n```bash\nport-selector --verbose\n# [DEBUG] main: starting port selection\n# [DEBUG] config: loading config from /home/user/.config/port-selector/config.yaml\n# [DEBUG] config: loaded: portStart=3000, portEnd=4000, freezePeriod=1440, allocationTTL=30d\n# [DEBUG] main: config loaded: portStart=3000, portEnd=4000, freezePeriod=1440 min\n# [DEBUG] allocations: loading from /home/user/.config/port-selector/allocations.yaml\n# [DEBUG] allocations: loaded 5 allocations\n# [DEBUG] main: current directory: /home/user/projects/my-app\n# [DEBUG] main: found existing allocation: port 3001\n# [DEBUG] main: existing port 3001 is free, reusing\n# 3001\n```\n\nThe `--verbose` flag can be combined with other flags:\n\n```bash\nport-selector --scan --verbose\nport-selector --list --verbose\n```\n\n## Configuration\n\nOn first run, a configuration file is created:\n\n**~/.config/port-selector/config.yaml**\n\n```yaml\n# Start port of range\nportStart: 3000\n\n# End port of range\nportEnd: 4000\n\n# Freeze period after port issuance\n# Port won't be reused within this time\n# Supports: 24h (hours), 30m (minutes), 1d (days)\n# \"0\" = disabled, default: 24h\nfreezePeriod: 24h\n\n# Auto-expire allocations after this period\n# Supports: 30d (days), 720h (hours), 24h30m (combined)\n# \"0\" = disabled (default)\nallocationTTL: 30d\n\n# Log file path for operation logging (optional)\n# Uncomment to enable logging of all allocation changes\n# log: ~/.config/port-selector/port-selector.log\n```\n\n### Logging\n\nWhen `log` is set, all allocation changes are written to the specified file:\n\n```yaml\nlog: ~/.config/port-selector/port-selector.log\n```\n\nLog format:\n```\n2026-01-03T15:04:05Z ALLOC_ADD port=3001 dir=/home/user/project1 process=node\n2026-01-03T15:04:10Z ALLOC_LOCK port=3001 locked=true\n2026-01-03T15:05:00Z ALLOC_DELETE port=3002 dir=/home/user/forgotten\n```\n\nLogged events:\n- `ALLOC_ADD` — new port allocated\n- `ALLOC_UPDATE` — allocation timestamp updated (reuse)\n- `ALLOC_LOCK` — port locked/unlocked\n- `ALLOC_DELETE` — allocation removed (--forget)\n- `ALLOC_DELETE_ALL` — all allocations removed (--forget-all)\n- `ALLOC_EXPIRE` — allocation expired by TTL\n\n### Allocation TTL\n\nWhen `allocationTTL` is set, allocations older than the specified period are automatically removed during each run. This prevents accumulation of stale allocations from deleted projects:\n\n```yaml\nallocationTTL: 30d  # Allocations expire after 30 days of inactivity\n```\n\nThe timestamp is updated each time a port is returned for an existing allocation, so actively used allocations never expire.\n\n### Freeze Period\n\nAfter a port is issued, it becomes \"frozen\" for the specified time and won't be issued again. This solves the problem when an application starts slowly and the port appears free, even though another server is about to start on it.\n\n```\nTime 10:00 - Agent 1 requests port → gets 3000\nTime 10:01 - Agent 2 requests port → gets 3001 (3000 is frozen)\nTime 10:02 - Agent 1 stops, port 3000 is released\nTime 10:03 - Agent 3 requests port → gets 3002 (3000 is still frozen)\n...\nTime 34:01 - 24 hours passed, port 3000 is unfrozen\n```\n\nPort freeze information is stored in `~/.config/port-selector/allocations.yaml` as part of the allocation timestamps.\n\n### Caching\n\nFor optimization, the utility remembers the last issued port in `~/.config/port-selector/allocations.yaml` (field `last_issued_port`). On the next call, checking starts from this port, not from the beginning of the range.\n\n```\nFirst call:   checks 3000 → free → returns 3000, saves 3000\nSecond call:  checks 3001 → free → returns 3001, saves 3001\nThird call:   checks 3002 → busy → checks 3003 → free → returns 3003\n...\nAfter 4000:   checks 3000 (wrap-around)\n```\n\n## Algorithm\n\n```\n┌────────────────────────────────────────┐\n│          port-selector                 │\n└──────────────────┬─────────────────────┘\n                   │\n                   ▼\n┌────────────────────────────────────────┐\n│  1. Read config                        │\n│     ~/.config/port-selector/config.yaml │\n│     (create if missing)                │\n└──────────────────┬─────────────────────┘\n                   │\n                   ▼\n┌────────────────────────────────────────┐\n│  2. Read last-used and history         │\n│     last-used → starting point         │\n│     issued-ports.yaml → frozen ports   │\n└──────────────────┬─────────────────────┘\n                   │\n                   ▼\n┌────────────────────────────────────────┐\n│  3. Check port:                        │\n│     - Not frozen?                      │\n│     - Not locked by another dir?       │\n│     - Free? (net.Listen)               │\n└──────────────────┬─────────────────────┘\n                   │\n           ┌───────┴───────┐\n           │               │\n       suitable      frozen/busy\n           │               │\n           ▼               ▼\n┌──────────────────┐ ┌──────────────────┐\n│ 4a. Save:        │ │ 4b. Next port    │\n│  - last-used     │ │     (wrap-around │\n│  - to history    │ │     after end)   │\n│  Output STDOUT   │ │                  │\n└──────────────────┘ └────────┬─────────┘\n                              │\n                    ┌─────────┴─────────┐\n                    │                   │\n              more ports        all checked\n                    │                   │\n                    ▼                   ▼\n              → step 3         ┌────────────────┐\n                               │ ERROR to STDERR│\n                               │ exit code 1    │\n                               └────────────────┘\n```\n\n## Development\n\n### Requirements\n\n- Go 1.21+\n- mise (for version management)\n\n### Local Build\n\n```bash\n# Install dependencies via mise\nmise install\n\n# Run tests\nmake test\n\n# Build\nmake build\n\n# Build and install to /usr/local/bin\nmake install\n\n# Uninstall\nmake uninstall\n```\n\n### Project Structure\n\n### Allocations File Format\n\nPort allocations are stored in `~/.config/port-selector/allocations.yaml`:\n\n```yaml\nlast_issued_port: 3012\nallocations:\n  3000:\n    directory: /home/user/code/project-a\n    name: main\n    assigned_at: 2026-01-06T20:00:00Z\n    last_used_at: 2026-01-06T20:00:00Z\n    locked: true\n  3010:\n    directory: /home/user/myproject\n    name: web\n    assigned_at: 2026-01-06T20:00:00Z\n    last_used_at: 2026-01-06T20:30:00Z\n  3011:\n    directory: /home/user/myproject\n    name: api\n    assigned_at: 2026-01-06T20:01:00Z\n    last_used_at: 2026-01-06T20:35:00Z\n  3012:\n    directory: /home/user/myproject\n    name: db\n    assigned_at: 2026-01-06T20:02:00Z\n    last_used_at: 2026-01-06T21:15:00Z\n```\n\nThe `name` field is optional. Missing or empty names are treated as `\"main\"` for backward compatibility.\n\n## Project Structure\n\n```\nport-selector/\n├── cmd/\n│   └── port-selector/\n│       └── main.go          # Entry point\n├── internal/\n│   ├── allocations/\n│   │   └── allocations.go   # Port allocation persistence\n│   ├── config/\n│   │   └── config.go        # Configuration handling\n│   ├── docker/\n│   │   └── docker.go        # Docker container detection\n│   ├── logger/\n│   │   └── logger.go        # Logging\n│   ├── pathutil/\n│   │   └── pathutil.go      # Path utilities\n│   └── port/\n│       ├── checker.go       # Port checking\n│       └── procinfo.go      # Process info\n├── .github/\n│   └── workflows/\n│       └── release.yml      # GitHub Actions for releases\n├── .mise.toml               # mise configuration\n├── go.mod\n├── go.sum\n├── CLAUDE.md                # Instructions for AI agents\n└── README.md\n```\n\n## License\n\nMIT\n\n## Author\n\n[Danil Pismenny](https://pismenny.ru) ([@dapi](https://github.com/dapi))\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdapi%2Fport-selector","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdapi%2Fport-selector","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdapi%2Fport-selector/lists"}