@oomerevren/tryforge 0.1.2

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.
Files changed (60) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/LICENSE +21 -0
  3. package/README.md +361 -0
  4. package/dist/cli/src/adapters/claude.js +31 -0
  5. package/dist/cli/src/adapters/codex.js +30 -0
  6. package/dist/cli/src/adapters/cursor.js +42 -0
  7. package/dist/cli/src/adapters/dsh.js +46 -0
  8. package/dist/cli/src/adapters/generic.js +23 -0
  9. package/dist/cli/src/adapters/index.js +30 -0
  10. package/dist/cli/src/adapters/opencode.js +41 -0
  11. package/dist/cli/src/adapters/types.js +140 -0
  12. package/dist/cli/src/adapters/windsurf.js +43 -0
  13. package/dist/cli/src/commands/audit.js +62 -0
  14. package/dist/cli/src/commands/init.js +104 -0
  15. package/dist/cli/src/commands/install.js +212 -0
  16. package/dist/cli/src/commands/update.js +119 -0
  17. package/dist/cli/src/core/config.js +47 -0
  18. package/dist/cli/src/core/installer.js +279 -0
  19. package/dist/cli/src/core/lock.js +41 -0
  20. package/dist/cli/src/core/permissions.js +29 -0
  21. package/dist/cli/src/core/plugin.js +34 -0
  22. package/dist/cli/src/core/project.js +60 -0
  23. package/dist/cli/src/core/registry.js +137 -0
  24. package/dist/cli/src/core/semver.js +162 -0
  25. package/dist/cli/src/core/sign.js +92 -0
  26. package/dist/cli/src/core/store.js +71 -0
  27. package/dist/cli/src/index.js +484 -0
  28. package/dist/scripts/build-registry.js +145 -0
  29. package/dist/scripts/publish-verified.js +82 -0
  30. package/dist/scripts/seed-registry-13lite.js +208 -0
  31. package/dist/scripts/seed-registry.js +144 -0
  32. package/dist/scripts/verify-npm-mcps.js +60 -0
  33. package/forge.toml +18 -0
  34. package/install.ps1 +59 -0
  35. package/install.sh +81 -0
  36. package/package.json +64 -0
  37. package/registry/index.json +336 -0
  38. package/registry/packages/agent-changelog-writer.json +27 -0
  39. package/registry/packages/agent-debugger.json +27 -0
  40. package/registry/packages/agent-pr-reviewer.json +27 -0
  41. package/registry/packages/agent-researcher.json +27 -0
  42. package/registry/packages/agent-security-auditor.json +27 -0
  43. package/registry/packages/cmd-plan.json +26 -0
  44. package/registry/packages/cmd-review.json +26 -0
  45. package/registry/packages/mcp-filesystem.json +34 -0
  46. package/registry/packages/mcp-github.json +33 -0
  47. package/registry/packages/mcp-memory.json +33 -0
  48. package/registry/packages/mcp-postgres.json +34 -0
  49. package/registry/packages/mcp-sequential-thinking.json +33 -0
  50. package/registry/packages/obra-superpowers.json +29 -0
  51. package/registry/packages/pdf-compress.json +27 -0
  52. package/registry/packages/pdf-convert.json +27 -0
  53. package/registry/packages/pdf-extract.json +27 -0
  54. package/registry/packages/pdf-forms.json +27 -0
  55. package/registry/packages/pdf-merge.json +27 -0
  56. package/registry/packages/pdf-ocr.json +27 -0
  57. package/registry/packages/pdf-split.json +27 -0
  58. package/registry/packages/pdf-tables.json +27 -0
  59. package/registry/search.json +248 -0
  60. package/registry/stats.json +13 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,25 @@
1
+ # Changelog
2
+
3
+ Tüm önemli değişiklikler bu dosyada tutulur. Format [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) esaslıdır, sürümler [SemVer](https://semver.org/) izler.
4
+
5
+ ## [0.1.1] — 2026-09-03
6
+
7
+ ### Added
8
+ - `tryforge` npm paketi yayınlandı (`npm i -g tryforge`).
9
+ - 7 harness adapter'ı: Claude Code, Codex, OpenCode, Cursor, DeepSeek (dsh), Windsurf, Generic.
10
+ - 21 verified paketlik registry (`registry/index.json` + `search.json` + `stats.json`).
11
+ - `forge.toml` + `forge.lock` ile takım senkronu (`forge install`, `--frozen`).
12
+ - `forge audit` (mock/unverified işaretleme), `forge doctor` sağlık kontrolü.
13
+ - Fail-closed installer: `sha256` doğrulanamayan içerik `--mock` olmadan kurulmaz.
14
+
15
+ ### Security
16
+ - `tar` extract `execFileSync` arg-array'e taşındı; symlink/escape taraması eklendi.
17
+ - `add`/`remove` bağımlılık adı validasyonu (`owner/name`).
18
+ - MCP config yazımı öncesi `.bak` yedeği.
19
+
20
+ ## [0.1.0] — 2026-09-01
21
+
22
+ ### Added
23
+ - İlk CLI iskeleti: `add/search/list/info/doctor/init/install/update/outdated/remove`.
24
+ - Semver çözümleme (`^`, `~`, pin) ve offline scored search (<200ms).
25
+ - `install.sh` / `install.ps1` yükleyiciler.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ömer Evren — Forge
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,361 @@
1
+ # Forge — The Homebrew for AI Agents
2
+
3
+ [![CI](https://github.com/oomerevren-beep/forge/actions/workflows/ci.yml/badge.svg)](https://github.com/oomerevren-beep/forge/actions)
4
+ [![npm version](https://img.shields.io/npm/v/tryforge?label=npm&color=CB3837)](https://www.npmjs.com/package/tryforge)
5
+ [![npm downloads](https://img.shields.io/npm/dw/tryforge?label=downloads)](https://www.npmjs.com/package/tryforge)
6
+ [![license MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
+ [![registry](https://img.shields.io/badge/registry-250%20packages-green.svg)](registry/index.json)
8
+ [![GitHub stars](https://img.shields.io/github/stars/oomerevren-beep/forge?style=social)](https://github.com/oomerevren-beep/forge)
9
+
10
+ > **One CLI to install skills, MCPs, plugins, agents on any harness.**
11
+
12
+ `brew` is for system packages. `forge` is for AI agent packages. One command, every harness — no more `git clone + cp SKILL.md` four times for four tools.
13
+
14
+ ```bash
15
+ # macOS / Linux
16
+ curl -fsSL https://raw.githubusercontent.com/oomerevren-beep/forge/main/install.sh | sh
17
+ # Windows PowerShell
18
+ irm https://raw.githubusercontent.com/oomerevren-beep/forge/main/install.ps1 | iex
19
+ # or, with Node 22+
20
+ npm i -g tryforge
21
+ ```
22
+
23
+ ```bash
24
+ forge doctor # detect your harnesses
25
+ forge add pdf/extract # skill → all harnesses at once
26
+ forge add mcp/filesystem # MCP server → auto mcp.json
27
+ ```
28
+
29
+ **Star to get notified at v1.0** — questions in [Discussions](https://github.com/oomerevren-beep/forge/discussions).
30
+
31
+ <p align="center">
32
+ <img src="docs/assets/og.png" alt="Forge — The Homebrew for AI Agents" width="640" />
33
+ </p>
34
+ <p align="center">
35
+ <img src="docs/assets/demo.gif" alt="forge demo — doctor, search, add, list in seconds" width="640" />
36
+ <br><em>7-second wow: <code>forge doctor → search → add → list</code></em>
37
+ </p>
38
+
39
+ ---
40
+
41
+ ## Table of Contents
42
+
43
+ - [Why Forge?](#why-forge)
44
+ - [Features](#features)
45
+ - [Install](#install)
46
+ - [Quick Start](#quick-start)
47
+ - [Team Sync — `forge install`](#team-sync--forge-install)
48
+ - [Create a Package](#create-a-package)
49
+ - [How it Works](#how-it-works)
50
+ - [Comparison](#comparison)
51
+ - [Registry — 250 Packages](#registry--250-packages)
52
+ - [Architecture](#architecture)
53
+ - [Docs](#docs)
54
+ - [Roadmap](#roadmap)
55
+ - [Contributing](#contributing)
56
+ - [FAQ](#faq)
57
+ - [License](#license)
58
+
59
+ ---
60
+
61
+ ## Why Forge?
62
+
63
+ 2026: everything is a skill, plugin, or MCP — but installing one is still stone age. Every harness has its own folder, no versioning, no update path.
64
+
65
+ - `obra/superpowers` — **~280k stars** — install is still manual `git clone + cp`
66
+ - `deepseek-harness` — **~208k stars, ~2 days to 100k** — proved "Everything is a Plugin", but DeepSeek-only
67
+ - `anthropics/skills` — **~173k stars** — Claude-only
68
+ - `mcp-registry` — MCP-only, no skills or agents
69
+
70
+ Result: a developer using Claude Code + Codex + OpenCode + Cursor installs the **same package 4 times by hand**. No `update`, no `forge.toml`, no team sync.
71
+
72
+ **Forge fixes it:** 6 package types × 7 harnesses × one CLI. Like `brew` unified system packages, `forge` unifies agent packages. Viral loop: creators promote `forge add me` to distribute their own work.
73
+
74
+ | Type | Example | What it does |
75
+ |------|---------|--------------|
76
+ | `skill` | `anthropics/plan` | `SKILL.md` capability (Anthropic standard) |
77
+ | `mcp` | `mcp/filesystem` | Model Context Protocol server |
78
+ | `plugin` | `dsh/desktop` | Harness extension |
79
+ | `agent` | `agency/frontend` | Subagent definition |
80
+ | `command` | `cmd/plan` | Slash command `/plan` |
81
+ | `hook` | `hook/pre-tool` | Lifecycle hook |
82
+
83
+ ---
84
+
85
+ ## Features
86
+
87
+ - **Universal — 6 types, one registry.** Skills + MCPs + plugins + agents + commands + hooks in one place.
88
+ - **Multi-harness — 7 adapters today.** One file per harness, so adding Antigravity, Droid, or Copilot is a single PR. Symlink (junction on Windows) preferred, copy fallback.
89
+ - **Team sync — `forge.toml` + `forge.lock`.** Like `package.json` for agents. `forge install` / `forge install --frozen` for CI.
90
+ - **Search in <200ms.** Offline scored `search.json` + typed index, no API key.
91
+ - **Git-native registry.** Every package is a GitHub repo. Forkable, private registry ready. No central DB.
92
+ - **Safe by default.** `sha256`-verified tarballs or hard fail (no silent fallback); mock content only with explicit `--mock`; `forge audit` flags unverified installs; `forge doctor` health check; Windows/Linux/macOS.
93
+
94
+ Supported harnesses (`forge doctor` detects all of them):
95
+
96
+ | Harness | Status |
97
+ |---------|:------:|
98
+ | Claude Code | ✅ |
99
+ | Codex | ✅ |
100
+ | OpenCode | ✅ |
101
+ | Cursor | ✅ |
102
+ | DeepSeek (dsh) | ⚠️ (community/untested) |
103
+ | Windsurf | ✅ |
104
+ | Generic (any other) | ✅ |
105
+
106
+ ---
107
+
108
+ ## Install
109
+
110
+ ```bash
111
+ # macOS / Linux
112
+ curl -fsSL https://raw.githubusercontent.com/oomerevren-beep/forge/main/install.sh | sh
113
+
114
+ # Windows PowerShell
115
+ irm https://raw.githubusercontent.com/oomerevren-beep/forge/main/install.ps1 | iex
116
+
117
+ # via npm (Node 22+)
118
+ npm i -g tryforge
119
+ ```
120
+
121
+ Requires Node 22+ (TypeScript). Details and troubleshooting: [docs/INSTALL.md](docs/INSTALL.md).
122
+
123
+ ---
124
+
125
+ ## Quick Start
126
+
127
+ 60 seconds, copy-paste:
128
+
129
+ ```bash
130
+ npm i -g tryforge # or: curl -fsSL https://raw.githubusercontent.com/oomerevren-beep/forge/main/install.sh | sh
131
+ forge doctor # detect your harnesses (7 checked)
132
+ forge add anthropics/plan # install latest → all harnesses at once
133
+ ```
134
+
135
+ More once you're in:
136
+
137
+ ```bash
138
+ forge search plan # find packages (try: pdf, mcp, agent)
139
+ forge list # installed, with versions
140
+ forge info mcp/github # detail + versions + engines
141
+ forge outdated # what can be updated
142
+ forge update # update all (or: forge update mcp/github)
143
+ forge remove obra/superpowers
144
+ ```
145
+
146
+ ```bash
147
+ # version pinning
148
+ forge add anthropics/plan@1.1.0
149
+ forge add anthropics/plan@^1.2.0
150
+
151
+ # dry run — see what would happen
152
+ forge add anthropics/plan --dry-run
153
+
154
+ # mock content for packages with no verified tarball yet (explicit opt-in)
155
+ forge add skill/pdf --mock
156
+ forge install --mock # same opt-in for project installs
157
+ forge update mcp/github --mock
158
+ ```
159
+
160
+ ---
161
+
162
+ ## Team Sync — `forge install`
163
+
164
+ Add `forge.toml` to your project, everyone gets the same set with one command.
165
+
166
+ ```toml
167
+ # my-project/forge.toml
168
+ [project]
169
+ name = "my-app"
170
+ version = "0.1.0"
171
+
172
+ [dependencies]
173
+ "anthropics/plan" = "^1.2.0"
174
+ "mcp/filesystem" = "^1.0.0"
175
+ "obra/superpowers" = "^3.0.0"
176
+ "skill/pdf" = "^1.0.0"
177
+ ```
178
+
179
+ ```bash
180
+ forge install # installs all, writes forge.lock
181
+ forge install --frozen # CI: exact versions from lock (like npm ci)
182
+ forge outdated # shows 1.1.0 → 1.2.0 etc.
183
+ forge update # bumps to latest within semver
184
+ ```
185
+
186
+ Lockfile `forge.lock` is `[[packages]]` TOML — commit it. `~/.forge/config.toml` holds user defaults (registry URL, default harnesses). Worked example: [examples/team-sync/](examples/team-sync/).
187
+
188
+ ---
189
+
190
+ ## Create a Package
191
+
192
+ ```bash
193
+ forge init my-skill --type skill --yes # → SKILL.md + forge.toml
194
+ forge init my-mcp --type mcp --yes # → MCP template + [mcp] config
195
+ forge init my-agent --type agent --yes # → agent.md template
196
+ # types: skill | mcp | plugin | agent | command | hook
197
+
198
+ cat forge.toml
199
+ # [package] name="my-skill" version="0.1.0" type="skill" ...
200
+
201
+ forge publish # (v0.2) validates → tarball → GitHub Release → registry PR
202
+ ```
203
+
204
+ Until `publish` lands, open a PR adding `registry/packages/<slug>.json` (see `schema/forge.schema.json`), then `npm run registry:build`. Minimal working example: [examples/minimal-skill/](examples/minimal-skill/).
205
+
206
+ ---
207
+
208
+ ## How it Works
209
+
210
+ ```
211
+ forge add anthropics/plan
212
+ → resolve ^1.2.0 → tarball URL + sha256 (from local/bundled registry/index.json; CDN in v0.3)
213
+ → download to ~/.forge/cache/tarballs/ → verify sha256 (hard fail on mismatch) → extract to ~/.forge/packages/anthropics-plan@1.2.0/
214
+ → adapter: symlink/copy to all 7 harnesses (Claude/Codex/OpenCode/Cursor/DSH/Windsurf/Generic)
215
+ → if MCP: backup mcp.json → inject server entry
216
+ ```
217
+
218
+ Add a harness = one file in `cli/src/adapters/`. See `docs/ADAPTERS.md`.
219
+
220
+ ```
221
+ ~/.forge/
222
+ config.toml # user config
223
+ packages/<slug>@<ver>/ # extracted content
224
+ cache/tarballs/ # downloaded .tar.gz
225
+ links.json # {pkg, version, adapters[]} source of truth
226
+ ```
227
+
228
+ ---
229
+
230
+ ## Rekabet
231
+
232
+ | | **Forge** | `npx skills` (Vercel) | `anthropics/skills` | `DSH` | `mcp-registry` |
233
+ |---|---|---|---|---|---|
234
+ | Türler | **6** (skill/mcp/plugin/agent/command/hook) | 1 (skill) | 1 (skill) | plugin | 1 (mcp) |
235
+ | Harness | **7** (Claude/Codex/OpenCode/Cursor/DSH/Windsurf/Generic) | 75+ ajan | 1 | 1 | ~1 |
236
+ | Versiyonlama | **semver + lock** | skills-lock.json | none | none | partial |
237
+ | Güncelleme | `forge update` | check/update | manual | manual | manual |
238
+ | Takım senkronu | **`forge.toml` + `forge.lock`** | none | none | none | none |
239
+ | Arama | `<200ms` offline | `skills find` | GitHub search | none | web only |
240
+ | Paket doğrulama | **sha256 + fail-closed** | none | none | none | none |
241
+ | Yerel CLI | **evet** (npm i -g) | npx (her seferinde indirir) | manual | manual | — |
242
+
243
+ Fark: Forge, her paketi sha256 ile doğrular (fail-closed), `forge.toml` ile takım senkronu yapar, offline arama yapar ve yerel CLI olarak çalışır. Vercel skills repo-agnostik çalışır ama doğrulama ve takım senkronu yok.
244
+
245
+ ---
246
+
247
+ ## Registry — 21 Verified Packages
248
+
249
+ Epoch 1d: Sadece sha256 doğrulanmış paketler listeleniyor (fail-closed).
250
+
251
+ ```bash
252
+ forge search mcp # 5 results
253
+ forge search pdf # 8 results
254
+ forge search --type skill plan # filtered
255
+ forge info pdf/compress # versions, engines, sha
256
+ ```
257
+
258
+ Stats: **9 skills, 5 MCPs, 5 agents, 2 commands.** Tümü verified (sha256 pinned).
259
+
260
+ ```bash
261
+ npm run registry:build # index + search + stats + --check in CI
262
+ ```
263
+
264
+ Düzenli güncelleme: Yeni paketler eklemek için `registry/packages/<slug>.json` oluşturun → `npm run registry:build -- --check`.
265
+
266
+ ---
267
+
268
+ ## Architecture
269
+
270
+ ```
271
+ CLI (TypeScript v0.1 → Rust v0.2)
272
+ ├─ registry client (index.json + semver)
273
+ ├─ store (~/.forge/packages/ + links.json + cache)
274
+ ├─ adapters (detect/install/list — 1 file per harness)
275
+ └─ commands (add/remove/list/search/info/doctor/init/install/update/outdated/audit)
276
+ ```
277
+
278
+ Full diagram and crate layout in `docs/ARCHITECTURE.md`. Tech choices: `commander` + `smol-toml` + `tsx` (no `bun` on Windows), `tsc` emit to `dist/` for `npm pack`.
279
+
280
+ ---
281
+
282
+ ## Docs
283
+
284
+ - [Install](docs/INSTALL.md) — npm / curl / source, verification
285
+ - [Examples](examples/) — minimal skill + team sync, copy-paste
286
+ - [Vision](docs/VISION.md) — why now, why 100k
287
+ - [PRD](docs/PRD.md) — users, stories, commands, harness matrix
288
+ - [Architecture](docs/ARCHITECTURE.md) — components, security, perf
289
+ - [Package Spec](docs/SPEC.md) — `forge.toml` fields per type
290
+ - [Registry](docs/REGISTRY.md) — index schema, publish, fork
291
+ - [Adapters](docs/ADAPTERS.md) — add a harness in one file
292
+ - [Roadmap](docs/ROADMAP.md) — v0.1 → v1.0
293
+ - [Changelog](CHANGELOG.md) — release history
294
+
295
+ ---
296
+
297
+ ## Roadmap
298
+
299
+ **v0.1 — Homebrew moment** (now): 7 harnesses, 21 verified packages, `add/search/list/doctor/install/init`.
300
+ **v0.2 — NPM moment**: `forge publish`, deps, `cargo install`.
301
+ **v0.3 — Store moment**: `forge run`, Rust binary, GUI.
302
+ **v1.0 — Cloud**: team registry, `brew install forge`, `winget`.
303
+
304
+ See `docs/ROADMAP.md`.
305
+
306
+ ---
307
+
308
+ ## Paket Doğrulama
309
+
310
+ Forge, fail-closed paket doğrulama sağlar:
311
+
312
+ 1. **sha256 pin**: Her paket sürümü SHA-256 hash'i ile imzalanır
313
+ 2. **Verify on install**: İndirme sırasında hash kontrol edilir — uyarsa exit 1
314
+ 3. **Audit trail**: `forge audit` mock/tamamlanmamış kurulumları listeler
315
+
316
+ Gelecek (v0.2): Sigstore/cosign ile imzalama, verified publisher programı.
317
+
318
+ - Add a package: create `registry/packages/<slug>.json` → `npm run registry:build`
319
+ - Add a harness: `cli/src/adapters/<name>.ts` → register in `adapters/index.ts` → `forge doctor`
320
+ - PR checklist: `npm run build && npm test && npm run registry:build -- --check`
321
+ - New? Start with [`good first issue`](https://github.com/oomerevren-beep/forge/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) — questions in [Discussions](https://github.com/oomerevren-beep/forge/discussions)
322
+
323
+ See `CONTRIBUTING.md`, [Code of Conduct](CODE_OF_CONDUCT.md), [Security](SECURITY.md) and `AGENTS.md`.
324
+
325
+ ---
326
+
327
+ ## FAQ
328
+
329
+ **Is this just for Claude Code?**
330
+ No. 7 harnesses today, one file per new harness. The point is *universal* — same reason `brew` beat per-OS managers.
331
+
332
+ **How is this different from `mcp-registry` or `skills` repos?**
333
+ Those are single-type and single-harness. Forge is `brew` for *all* agent package types, on *every* harness, with versioning/lock/search.
334
+
335
+ **Does `install.sh` need `forge.sh` domain?**
336
+ No. `curl -fsSL https://raw.githubusercontent.com/oomerevren-beep/forge/main/install.sh | sh` works today. `forge.sh` is a future vanity alias.
337
+
338
+ **Are most packages mock content?**
339
+ Honest answer: 245 of 250 registry entries still carry placeholder SHAs (only 5 verified so far). Forge is fail-closed — installing one without `--mock` errors out instead of silently faking it; `forge audit` flags every unverified install. Real tarballs land as maintainers verify them (tracked for Faz 13-full).
340
+
341
+ **Windows symlinks?**
342
+ Junction (`symlinkSync(..., 'junction')`) on dirs, fallback to `cpSync` on `EPERM`. `doctor` checks `links.json` (source of truth), not just dir scan.
343
+
344
+ **Why TypeScript first, not Rust?**
345
+ Speed to market. v0.1 is TypeScript + `tsx`; v0.2 rewrites to Rust for single binary `cargo install`. Adapters stay cheap.
346
+
347
+ **Private registry?**
348
+ Yes. Fork this repo, keep `registry/` private, point `~/.forge/config.toml` `registry = "https://your-r2/index.json"`.
349
+
350
+ ---
351
+
352
+ ## License
353
+
354
+ MIT — see [LICENSE](LICENSE).
355
+
356
+ ---
357
+
358
+ <p align="center">
359
+ <strong>Star this repo to get notified at launch. 21 verified packages are live at <a href="https://github.com/oomerevren-beep/forge/releases/tag/v0.1.1">v0.1.1</a>.</strong><br>
360
+ <code>forge add</code> → 7 places at once. <code>forge install</code> → team sync. Homebrew simple.
361
+ </p>
@@ -0,0 +1,31 @@
1
+ // cli/src/adapters/claude.ts — Claude Code harness
2
+ import { existsSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ import { installSkillFiles, uninstallSkillFiles, removeLinkOrDir, listDirNames, targetDirFor } from "./types.js";
6
+ export const claudeAdapter = {
7
+ name: "claude-code",
8
+ displayName: "Claude Code",
9
+ version: "0.1.0",
10
+ detect: () => existsSync(join(homedir(), ".claude")),
11
+ skillDir: (slug) => join(homedir(), ".claude", "skills", slug),
12
+ // Epoch 1c: Claude user-scope MCP = ~/.claude.json (not settings.json which is behavior config)
13
+ mcpConfigPath: () => join(homedir(), ".claude.json"),
14
+ async install(pkgSlug, srcDir, type) {
15
+ // Epoch 1d: type-aware install — her tip kendi dizinine gider
16
+ const base = join(homedir(), ".claude");
17
+ installSkillFiles("claude-code", pkgSlug, srcDir, targetDirFor(base, type));
18
+ },
19
+ async uninstall(pkgSlug, type) {
20
+ void type;
21
+ uninstallSkillFiles(pkgSlug, join(homedir(), ".claude", "skills"));
22
+ const agentPath = join(homedir(), ".claude", "agents", pkgSlug);
23
+ removeLinkOrDir(agentPath);
24
+ },
25
+ async list() {
26
+ return listDirNames(join(homedir(), ".claude", "skills"));
27
+ },
28
+ async isInstalled(pkgSlug) {
29
+ return existsSync(join(homedir(), ".claude", "skills", pkgSlug));
30
+ },
31
+ };
@@ -0,0 +1,30 @@
1
+ // cli/src/adapters/codex.ts — Codex harness
2
+ import { existsSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ import { installSkillFiles, uninstallSkillFiles } from "./types.js";
6
+ export const codexAdapter = {
7
+ name: "codex",
8
+ displayName: "Codex",
9
+ version: "0.1.0",
10
+ detect: () => existsSync(join(homedir(), ".codex")),
11
+ skillDir: (slug) => join(homedir(), ".codex", "skills", slug),
12
+ mcpConfigPath: () => join(homedir(), ".codex", "mcp.json"),
13
+ async install(pkgSlug, srcDir, type) {
14
+ void type;
15
+ installSkillFiles("codex", pkgSlug, srcDir, join(homedir(), ".codex", "skills"));
16
+ },
17
+ async uninstall(pkgSlug, _type) {
18
+ uninstallSkillFiles(pkgSlug, join(homedir(), ".codex", "skills"));
19
+ },
20
+ async list() {
21
+ const dir = join(homedir(), ".codex", "skills");
22
+ if (!existsSync(dir))
23
+ return [];
24
+ const { readdirSync } = await import("fs");
25
+ return readdirSync(dir, { withFileTypes: true }).filter((d) => (d.isDirectory() || d.isSymbolicLink()) && !d.name.startsWith(".")).map((d) => d.name);
26
+ },
27
+ async isInstalled(pkgSlug) {
28
+ return existsSync(join(homedir(), ".codex", "skills", pkgSlug));
29
+ },
30
+ };
@@ -0,0 +1,42 @@
1
+ // cli/src/adapters/cursor.ts — Cursor harness
2
+ import { existsSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ import { installSkillFiles, uninstallSkillFiles, listDirNames } from "./types.js";
6
+ export const cursorAdapter = {
7
+ name: "cursor",
8
+ displayName: "Cursor",
9
+ version: "0.1.0",
10
+ detect: () => existsSync(join(process.cwd(), ".cursor")) || existsSync(join(homedir(), ".cursor")),
11
+ skillDir: (slug) => {
12
+ if (existsSync(join(process.cwd(), ".cursor")))
13
+ return join(process.cwd(), ".cursor", "skills", slug);
14
+ return join(homedir(), ".cursor", "skills", slug);
15
+ },
16
+ mcpConfigPath: () => {
17
+ if (existsSync(join(process.cwd(), ".cursor")))
18
+ return join(process.cwd(), ".cursor", "mcp.json");
19
+ return join(homedir(), ".cursor", "mcp.json");
20
+ },
21
+ async install(pkgSlug, srcDir, type) {
22
+ void type;
23
+ const base = existsSync(join(process.cwd(), ".cursor")) ? join(process.cwd(), ".cursor", "skills") : join(homedir(), ".cursor", "skills");
24
+ installSkillFiles("cursor", pkgSlug, srcDir, base);
25
+ },
26
+ async uninstall(pkgSlug, _type) {
27
+ const base = existsSync(join(process.cwd(), ".cursor")) ? join(process.cwd(), ".cursor", "skills") : join(homedir(), ".cursor", "skills");
28
+ uninstallSkillFiles(pkgSlug, base);
29
+ },
30
+ async list() {
31
+ const bases = [join(process.cwd(), ".cursor", "skills"), join(homedir(), ".cursor", "skills")];
32
+ for (const b of bases) {
33
+ if (existsSync(b))
34
+ return listDirNames(b);
35
+ }
36
+ return [];
37
+ },
38
+ async isInstalled(pkgSlug) {
39
+ const bases = [join(process.cwd(), ".cursor", "skills"), join(homedir(), ".cursor", "skills")];
40
+ return bases.some((b) => existsSync(join(b, pkgSlug)));
41
+ },
42
+ };
@@ -0,0 +1,46 @@
1
+ // cli/src/adapters/dsh.ts — DeepSeek harness
2
+ import { existsSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ import { installSkillFiles, uninstallSkillFiles, listDirNames } from "./types.js";
6
+ // Epoch 1c: DSH adapter artık deprecated/legacy olarak işaretlenmiş.
7
+ // DeepSeek Harness'ında "skills" kavramı yok; cordis.yml + TS plugin modeli kullanılır.
8
+ // Adapter'ı tutuyoruz (geriye uyumluluk) ama "community-maintained, untested" olarak gösteriyoruz.
9
+ export const dshAdapter = {
10
+ name: "dsh",
11
+ displayName: "DeepSeek (DSH, community/untested)",
12
+ version: "0.1.0",
13
+ detect: () => existsSync(join(homedir(), ".dsh")) || existsSync(join(process.cwd(), ".dsh")),
14
+ skillDir: (slug) => {
15
+ if (existsSync(join(process.cwd(), ".dsh")))
16
+ return join(process.cwd(), ".dsh", "skills", slug);
17
+ return join(homedir(), ".dsh", "skills", slug);
18
+ },
19
+ mcpConfigPath: () => {
20
+ if (existsSync(join(process.cwd(), ".dsh")))
21
+ return join(process.cwd(), ".dsh", "mcp.json");
22
+ return join(homedir(), ".dsh", "mcp.json");
23
+ },
24
+ async install(pkgSlug, srcDir, type) {
25
+ void type;
26
+ const base = existsSync(join(process.cwd(), ".dsh")) ? join(process.cwd(), ".dsh", "skills") : join(homedir(), ".dsh", "skills");
27
+ installSkillFiles("dsh", pkgSlug, srcDir, base);
28
+ },
29
+ async uninstall(pkgSlug, _type) {
30
+ const bases = [join(process.cwd(), ".dsh", "skills"), join(homedir(), ".dsh", "skills")];
31
+ for (const b of bases)
32
+ uninstallSkillFiles(pkgSlug, b);
33
+ },
34
+ async list() {
35
+ const bases = [join(process.cwd(), ".dsh", "skills"), join(homedir(), ".dsh", "skills")];
36
+ for (const b of bases) {
37
+ if (existsSync(b))
38
+ return listDirNames(b);
39
+ }
40
+ return [];
41
+ },
42
+ async isInstalled(pkgSlug) {
43
+ const bases = [join(process.cwd(), ".dsh", "skills"), join(homedir(), ".dsh", "skills")];
44
+ return bases.some((b) => existsSync(join(b, pkgSlug)));
45
+ },
46
+ };
@@ -0,0 +1,23 @@
1
+ // cli/src/adapters/generic.ts — Generic fallback (./.forge/packages)
2
+ import { join } from "path";
3
+ import { installSkillFiles, uninstallSkillFiles, listDirNames } from "./types.js";
4
+ import { existsSync } from "fs";
5
+ export const genericAdapter = {
6
+ name: "generic",
7
+ displayName: "Generic (.forge)",
8
+ detect: () => true, // always as fallback
9
+ skillDir: (slug) => join(process.cwd(), ".forge", "packages", slug),
10
+ mcpConfigPath: () => join(process.cwd(), ".forge", "mcp.json"),
11
+ async install(pkgSlug, srcDir, _type) {
12
+ installSkillFiles("generic", pkgSlug, srcDir, join(process.cwd(), ".forge", "packages"));
13
+ },
14
+ async uninstall(pkgSlug, _type) {
15
+ uninstallSkillFiles(pkgSlug, join(process.cwd(), ".forge", "packages"));
16
+ },
17
+ async list() {
18
+ return listDirNames(join(process.cwd(), ".forge", "packages"));
19
+ },
20
+ async isInstalled(pkgSlug) {
21
+ return existsSync(join(process.cwd(), ".forge", "packages", pkgSlug));
22
+ },
23
+ };
@@ -0,0 +1,30 @@
1
+ export { readMcpConfig, writeMcpConfig, addMcpServerToConfig, removeMcpServerFromConfig, forgeHome, packagesDir, packageDir, toSlug, targetDirFor, } from "./types.js";
2
+ export { claudeAdapter } from "./claude.js";
3
+ export { codexAdapter } from "./codex.js";
4
+ export { opencodeAdapter } from "./opencode.js";
5
+ export { cursorAdapter } from "./cursor.js";
6
+ export { dshAdapter } from "./dsh.js";
7
+ export { windsurfAdapter } from "./windsurf.js";
8
+ export { genericAdapter } from "./generic.js";
9
+ import { claudeAdapter } from "./claude.js";
10
+ import { codexAdapter } from "./codex.js";
11
+ import { opencodeAdapter } from "./opencode.js";
12
+ import { cursorAdapter } from "./cursor.js";
13
+ import { dshAdapter } from "./dsh.js";
14
+ import { windsurfAdapter } from "./windsurf.js";
15
+ import { genericAdapter } from "./generic.js";
16
+ export const allAdapters = [
17
+ claudeAdapter,
18
+ codexAdapter,
19
+ opencodeAdapter,
20
+ cursorAdapter,
21
+ dshAdapter,
22
+ windsurfAdapter,
23
+ genericAdapter,
24
+ ];
25
+ export function detectAdapters() {
26
+ const detected = allAdapters.filter((a) => (a.name === "generic" ? false : a.detect()));
27
+ if (detected.length === 0)
28
+ return [genericAdapter];
29
+ return [...detected, genericAdapter];
30
+ }
@@ -0,0 +1,41 @@
1
+ // cli/src/adapters/opencode.ts — OpenCode harness
2
+ import { existsSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ import { installSkillFiles, uninstallSkillFiles, listDirNames } from "./types.js";
6
+ export const opencodeAdapter = {
7
+ name: "opencode",
8
+ displayName: "OpenCode",
9
+ version: "0.1.0",
10
+ detect: () => existsSync(join(process.cwd(), "opencode.json")) || existsSync(join(homedir(), ".config", "opencode", "opencode.json")),
11
+ skillDir: (slug) => join(process.cwd(), ".opencode", "skills", slug),
12
+ mcpConfigPath: () => {
13
+ const proj = join(process.cwd(), "opencode.json");
14
+ if (existsSync(proj))
15
+ return proj;
16
+ return join(homedir(), ".config", "opencode", "opencode.json");
17
+ },
18
+ async install(pkgSlug, srcDir, type) {
19
+ void type;
20
+ const isProject = existsSync(join(process.cwd(), "opencode.json"));
21
+ const base = isProject ? join(process.cwd(), ".opencode", "skills") : join(homedir(), ".config", "opencode", "skills");
22
+ installSkillFiles("opencode", pkgSlug, srcDir, base);
23
+ },
24
+ async uninstall(pkgSlug, _type) {
25
+ const isProject = existsSync(join(process.cwd(), "opencode.json"));
26
+ const base = isProject ? join(process.cwd(), ".opencode", "skills") : join(homedir(), ".config", "opencode", "skills");
27
+ uninstallSkillFiles(pkgSlug, base);
28
+ },
29
+ async list() {
30
+ const bases = [join(process.cwd(), ".opencode", "skills"), join(homedir(), ".config", "opencode", "skills")];
31
+ for (const b of bases) {
32
+ if (existsSync(b))
33
+ return listDirNames(b);
34
+ }
35
+ return [];
36
+ },
37
+ async isInstalled(pkgSlug) {
38
+ const bases = [join(process.cwd(), ".opencode", "skills"), join(homedir(), ".config", "opencode", "skills")];
39
+ return bases.some((b) => existsSync(join(b, pkgSlug)));
40
+ },
41
+ };