forgemap 0.1.0-dev.22-d31fc67 β†’ 0.1.0-dev.26-b896caf

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <div align="center">
2
2
 
3
- # 🧰 forgemap
3
+ # πŸ—ΊοΈ forgemap
4
4
 
5
5
  **One consistent local layout for every repo you clone β€” across every forge**
6
6
 
@@ -20,16 +20,20 @@ $ forgemap clone kirchDev/laravel-pbac
20
20
  βœ” Cloned kirchDev/laravel-pbac β†’ ~/projects/comGithub/kirchDev/laravel-pbac
21
21
  ```
22
22
 
23
- That's it. Every repo lands at a predictable `<root>/<forge.dir>/<owner>/<repo>` path, and `forgemap path <slug>` gives you that path back for `cd "$(forgemap path …)"` from anywhere.
23
+ That's it. Every repo lands at a predictable `<root>/<forge.dir>/<owner>/<repo>` path, and `forgemap cd <slug>` jumps into any of them from anywhere β€” exact slug, fuzzy match, or interactive picker.
24
24
 
25
25
  ## ✨ Features
26
26
 
27
27
  - **πŸ—‚οΈ Predictable layout** β€” every clone goes to `<root>/<forge.dir>/<owner>/<repo>`, configured once.
28
28
  - **πŸšͺ Flexible slug syntax** β€” `owner/repo`, `forge:owner/repo`, full HTTPS URLs, or SSH (`git@…:…`).
29
29
  - **πŸ” Fuzzy search** β€” `forgemap search <term>` finds local repos by owner or repo name (powered by [Fuse.js](https://www.fusejs.io/)).
30
- - **πŸ€– Forge-aware** β€” uses `gh` for GitHub today; GitLab / Gitea / Codeberg adapters planned.
31
- - **🧰 Typed config** β€” `forgemap.config.ts` with `defineForgeMapConfig()` and walk-up discovery.
32
- - **πŸš€ Shell-friendly** β€” `forgemap path <slug>` is a pure resolver, perfect for `cd "$(…)"` aliases.
30
+ - **πŸ€– Forge-aware** β€” `type: 'github'` shells out to `gh`; `type: 'git'` uses plain `git clone` with no extra dependencies.
31
+ - **πŸ” Mass sync + status** β€” `forgemap sync` fetches every clone in parallel, `forgemap status` shows branch / dirty / ahead / behind per repo.
32
+ - **πŸ“₯ Import existing trees** β€” `forgemap import <path>` adopts a folder already laid out as `<server>/<owner>/<repo>`, reconciles each repo against its git remote (spotting moved or deleted remotes), and derives a config.
33
+ - **🧹 Safe cleanup** β€” `forgemap cleanup` deletes long-idle local clones, but only the ones that are clean, fully pushed, and still exist on their remote β€” so nothing unbacked-up is ever lost.
34
+ - **πŸ›‘οΈ Preflight validate** β€” `forgemap validate` checks the config schema and required CLIs before you discover a problem mid-clone.
35
+ - **🧰 Typed config** β€” `forgemap.config.ts` with `defineForgeMapConfig()`, parent walk-up discovery, and a global fallback.
36
+ - **πŸš€ Shell-friendly** β€” `forgemap shell-init --install` wires up real `forgemap cd <slug>` **and** tab-completion in one step.
33
37
 
34
38
  ## πŸ“¦ Installation
35
39
 
@@ -39,11 +43,15 @@ npm install -g forgemap
39
43
  pnpm add -g forgemap
40
44
  ```
41
45
 
42
- > [!IMPORTANT]
43
- > `forgemap clone` shells out to the [GitHub CLI](https://cli.github.com/) (`gh`). Install it once and run `gh auth login` so cloning works against private repos.
46
+ **Requirements**
44
47
 
45
- > [!TIP]
46
- > Hacking on forgemap itself? See [CONTRIBUTING.md β†’ Trying the CLI locally](CONTRIBUTING.md#trying-the-cli-locally) β€” covers `pnpm setup`, `pnpm link --global .` and the shell-wrapper source.
48
+ - Node 24+
49
+ - `git` on `PATH`
50
+ - [`gh`](https://cli.github.com/) (GitHub CLI) β€” only when a `type: 'github'` forge is configured
51
+
52
+ Run `forgemap validate` after setup for an exact rundown of what's needed for your config.
53
+
54
+ Hacking on forgemap itself? See [CONTRIBUTING.md β†’ Trying the CLI locally](CONTRIBUTING.md#trying-the-cli-locally) β€” covers `pnpm setup`, `pnpm link --global .` and the shell-wrapper source.
47
55
 
48
56
  ## πŸš€ Quick start
49
57
 
@@ -52,62 +60,111 @@ pnpm add -g forgemap
52
60
  cd ~/projects
53
61
  forgemap config init
54
62
 
55
- # 2. Clone β€” any slug form works.
63
+ # 2. Wire up the shell integration once β€” real `forgemap cd` + tab-completion.
64
+ forgemap shell-init --install # appends loaders to ~/.zshrc (or bashrc/fish)
65
+ source ~/.zshrc # re-source once, then it's automatic
66
+ # Prefer manual? eval "$(forgemap shell-init)" Β· fish: forgemap shell-init fish | source
67
+
68
+ # 3. Clone β€” any slug form works.
56
69
  forgemap clone kirchDev/laravel-pbac
57
70
  forgemap clone github:TitusKirch/forgemap
58
71
  forgemap clone https://github.com/foo/bar
59
72
 
60
- # 3. Jump into a repo from anywhere.
61
- cd "$(forgemap path kirchDev/laravel-pbac)"
73
+ # 4. Jump into a repo from anywhere.
74
+ forgemap cd kirchDev/laravel-pbac # exact slug β†’ direct cd
75
+ forgemap cd laravel # fuzzy single match β†’ direct cd
76
+ forgemap cd kirch # multiple matches β†’ interactive picker
77
+ forgemap cd # no arg β†’ picker over every clone
78
+ ```
79
+
80
+ `forgemap cd` resolves the slug, walks/picks across your cloned repos,
81
+ and actually changes directory because the shell wrapper from
82
+ `shell-init` intercepts it before the binary runs. Every other
83
+ subcommand falls through to the real binary unchanged.
84
+
85
+ ### Search and pick on demand
86
+
87
+ ```bash
88
+ forgemap search forgemap # pretty tree (one line per match)
89
+ forgemap search forgemap | fzf # pipe-friendly path output
90
+ forgemap pick # interactive picker (consola prompt)
91
+ forgemap pick kirch # picker pre-filtered by fuzzy query
62
92
  ```
63
93
 
64
- Add a shell alias to make the jump even shorter:
94
+ ### Open the folder in the OS file manager
65
95
 
66
96
  ```bash
67
- fcd() { cd "$(forgemap path "$1")"; }
68
- fcd kirchDev/laravel-pbac
97
+ forgemap open kirchDev/laravel-pbac
69
98
  ```
70
99
 
71
- ### Real `cd` via shell integration (recommended)
100
+ - **WSL** β†’ launches `explorer.exe` against `\\wsl$\<distro>\…`, Explorer opens the folder
101
+ - **macOS** β†’ `open <path>` (Finder)
102
+ - **Linux** β†’ `xdg-open <path>`
72
103
 
73
- Source the shell wrapper once:
104
+ ### Mass operations across every clone
74
105
 
75
106
  ```bash
76
- # zsh/bash β€” drop into ~/.zshrc or ~/.bashrc
77
- eval "$(forgemap shell-init)"
107
+ forgemap sync # git fetch --all --prune, 4 in parallel
108
+ forgemap sync --pull # git pull --ff-only (skips dirty trees)
109
+ forgemap sync --forge work --query api # restrict scope
78
110
 
79
- # fish
80
- forgemap shell-init fish | source
111
+ forgemap status # tree: branch / dirty / ahead↑ / behind↓ / last commit
112
+ forgemap status --format json # structured for jq + scripts
81
113
  ```
82
114
 
83
- After that, `forgemap cd <slug>` actually changes directory:
115
+ All tree output (`status`, `search`, `import`) groups as `forge β†’ owner β†’ repo`. Network operations (`sync`, `import`, `cleanup`) run with a hard timeout and non-interactive SSH, so an unreachable host can never wedge a run.
116
+
117
+ ### Adopt an existing layout β€” `import`
118
+
119
+ Already have a folder full of repos laid out as `<server>/<owner>/<repo>`? Adopt it without re-cloning:
84
120
 
85
121
  ```bash
86
- forgemap cd laravel # cd straight into kirchDev/laravel-pbac (single match)
87
- forgemap cd kirch # multiple matches β†’ interactive picker
88
- forgemap cd # no arg β†’ picker over every cloned repo
122
+ forgemap import ~/projects # reconcile + derive/write forgemap.config.ts
123
+ forgemap import ~/projects --no-remote-check # offline: folder-vs-origin only (instant)
124
+ forgemap import ~/projects --fix # move folders / fix origin URLs to match the remote
125
+ forgemap import ~/projects --no-write-config # only report, don't touch the config
126
+ forgemap import ~/projects --format json
89
127
  ```
90
128
 
91
- All other `forgemap` subcommands pass through to the real binary unchanged.
129
+ For each repo `import` compares the folder's `<owner>/<repo>` against the git `origin`, checks whether the remote still exists or was moved/renamed (GitHub via a batched `gh` GraphQL query, other forges via `git ls-remote`), and derives `root` + one forge per server directory. Read-only by default β€” `--fix` is the only thing that touches the filesystem.
92
130
 
93
- ### Search and pick on demand
131
+ ### Reclaim disk β€” `cleanup`
94
132
 
95
133
  ```bash
96
- forgemap search forgemap # pretty tree (one line per match)
97
- forgemap search forgemap | fzf # pipe-friendly path output
98
- forgemap pick # interactive picker (consola prompt)
99
- forgemap pick kirch # picker pre-filtered by fuzzy query
134
+ forgemap cleanup # list deletable clones, then type "yes" to confirm
135
+ forgemap cleanup --dry-run # show candidates + why every other idle repo is kept
136
+ forgemap cleanup --days 540 # idle threshold in days (default 365)
137
+ forgemap cleanup --include-dirty --include-unpushed # also delete repos with local-only work (lost!)
100
138
  ```
101
139
 
102
- ### Open the folder in the OS file manager
140
+ A repo is only deleted when it is idle for `--days`+ days (by last **local** commit), has a clean working tree, has nothing unpushed, **and** its remote still exists β€” so everything removed is provably backed up. Repos without a remote (or with a gone/unreachable one) are never touched; empty owner directories left behind are pruned automatically. Deletion needs an explicit typed `yes` (or `--yes`).
141
+
142
+ ### Preflight your config
103
143
 
104
144
  ```bash
105
- forgemap open kirchDev/laravel-pbac
145
+ forgemap validate # pretty checklist with βœ“ / ! / βœ— per check
146
+ forgemap validate --json | jq # machine-readable for pre-commit hooks
106
147
  ```
107
148
 
108
- - **WSL** β†’ launches `explorer.exe` against `\\wsl$\<distro>\…`, Explorer opens the folder
109
- - **macOS** β†’ `open <path>` (Finder)
110
- - **Linux** β†’ `xdg-open <path>`
149
+ Validates the schema, required CLI tools (`git` always, `gh` when a `type: 'github'` forge is configured), `gh auth status`, and that the configured root exists.
150
+
151
+ ### Shell integration & completion
152
+
153
+ ```bash
154
+ forgemap shell-init --install # cd wrapper + completion β†’ your rc file (idempotent)
155
+ forgemap completion --install # completion only, if you don't want the cd wrapper
156
+ forgemap shell-init # print the wrapper (manual: eval "$(…)")
157
+ forgemap completion bash # print the completion script for bash/zsh/fish
158
+ ```
159
+
160
+ `--install` appends a marker-guarded block to the right rc file (`~/.zshrc`, `~/.bashrc`, or `~/.config/fish/config.fish`) β€” re-source it once and you're done. Tab-completion suggests every subcommand, and slugs for the commands that take one (`cd`, `clone`, `open`, …).
161
+
162
+ ### Inspect the config
163
+
164
+ ```bash
165
+ forgemap config init # write a starter forgemap.config.ts
166
+ forgemap config show # print the resolved config + which file it came from
167
+ ```
111
168
 
112
169
  ## βš™οΈ Configuration
113
170
 
@@ -121,26 +178,33 @@ export default defineForgeMapConfig({
121
178
  defaultForge: 'github',
122
179
  forges: {
123
180
  github: {
124
- type: 'github',
181
+ type: 'github', // uses `gh repo clone`
125
182
  host: 'github.com',
126
183
  dir: 'comGithub'
127
184
  },
128
185
  work: {
129
- type: 'gitlab',
186
+ type: 'git', // plain `git clone` β€” no gh needed
130
187
  host: 'gitlab.acme.com',
131
- dir: 'comGitlabAcme'
188
+ dir: 'comGitlabAcme',
189
+ protocol: 'ssh' // optional, ssh is the default
132
190
  }
133
191
  }
134
192
  });
135
193
  ```
136
194
 
137
- | Key | What it controls |
138
- | :------------- | :---------------------------------------------------------------------------------------------- |
139
- | `root` | Base directory for all clones. Relative paths resolve against the config file's directory. |
140
- | `defaultForge` | Forge alias used when a slug is just `owner/repo` (no host or forge prefix). |
141
- | `forges.<name>` | Map of forge aliases. Each entry has `type`, `host`, and `dir` (subdirectory under `root`). |
195
+ | Key | What it controls |
196
+ | :------------------- | :-------------------------------------------------------------------------------------------------------- |
197
+ | `root` | Base directory for all clones. Relative paths resolve against the config file's directory. |
198
+ | `defaultForge` | Forge alias used when a slug is just `owner/repo` (no host or forge prefix). |
199
+ | `forges.<name>.type` | `'github'` (shells out to `gh`) or `'git'` (plain `git clone`). `gitlab` / `gitea` / `codeberg` reserved. |
200
+ | `forges.<name>.host` | Hostname used to map full URLs and (for `git`) build the clone URL. |
201
+ | `forges.<name>.dir` | Subdirectory under `root` where this forge's clones live. |
202
+ | `forges.<name>.protocol` | `git`-type only. `'ssh'` (default) or `'https'`. Override per call with `--ssh` / `--https`. |
142
203
 
143
- The config file is discovered by walking up from your current directory. Override with `--config <path>` or the `FORGEMAP_CONFIG` env var.
204
+ The config file is discovered by walking **up** from your current directory (so `forgemap cd` works from inside any clone, not just the root), then falling back to a global `$XDG_CONFIG_HOME/forgemap/forgemap.config.*` (i.e. `~/.config/forgemap/`) so commands work from anywhere. Override with `--config <path>` or the `FORGEMAP_CONFIG` env var.
205
+
206
+ > [!TIP]
207
+ > Already have a directory full of repos? Skip writing this by hand β€” `forgemap import <path>` derives `root` + `forges` from the existing layout.
144
208
 
145
209
  ## πŸ—‚οΈ Layout
146
210
 
@@ -180,8 +244,11 @@ pnpm install
180
244
  pnpm test # vitest
181
245
  pnpm typecheck # tsc --noEmit
182
246
  pnpm check # lint + format
247
+ pnpm bench # microbench scanRepos / cache hit / cache rebuild
183
248
  ```
184
249
 
250
+ Tune the bench layout via env vars (`FORGEMAP_BENCH_FORGES`, `FORGEMAP_BENCH_OWNERS`, `FORGEMAP_BENCH_REPOS`, `FORGEMAP_BENCH_RUNS`).
251
+
185
252
  ## 🀝 Contributing
186
253
 
187
254
  PRs welcome. Conventional Commits required (enforced via commitlint). Husky runs lint-staged on every commit.