@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.
- package/CHANGELOG.md +25 -0
- package/LICENSE +21 -0
- package/README.md +361 -0
- package/dist/cli/src/adapters/claude.js +31 -0
- package/dist/cli/src/adapters/codex.js +30 -0
- package/dist/cli/src/adapters/cursor.js +42 -0
- package/dist/cli/src/adapters/dsh.js +46 -0
- package/dist/cli/src/adapters/generic.js +23 -0
- package/dist/cli/src/adapters/index.js +30 -0
- package/dist/cli/src/adapters/opencode.js +41 -0
- package/dist/cli/src/adapters/types.js +140 -0
- package/dist/cli/src/adapters/windsurf.js +43 -0
- package/dist/cli/src/commands/audit.js +62 -0
- package/dist/cli/src/commands/init.js +104 -0
- package/dist/cli/src/commands/install.js +212 -0
- package/dist/cli/src/commands/update.js +119 -0
- package/dist/cli/src/core/config.js +47 -0
- package/dist/cli/src/core/installer.js +279 -0
- package/dist/cli/src/core/lock.js +41 -0
- package/dist/cli/src/core/permissions.js +29 -0
- package/dist/cli/src/core/plugin.js +34 -0
- package/dist/cli/src/core/project.js +60 -0
- package/dist/cli/src/core/registry.js +137 -0
- package/dist/cli/src/core/semver.js +162 -0
- package/dist/cli/src/core/sign.js +92 -0
- package/dist/cli/src/core/store.js +71 -0
- package/dist/cli/src/index.js +484 -0
- package/dist/scripts/build-registry.js +145 -0
- package/dist/scripts/publish-verified.js +82 -0
- package/dist/scripts/seed-registry-13lite.js +208 -0
- package/dist/scripts/seed-registry.js +144 -0
- package/dist/scripts/verify-npm-mcps.js +60 -0
- package/forge.toml +18 -0
- package/install.ps1 +59 -0
- package/install.sh +81 -0
- package/package.json +64 -0
- package/registry/index.json +336 -0
- package/registry/packages/agent-changelog-writer.json +27 -0
- package/registry/packages/agent-debugger.json +27 -0
- package/registry/packages/agent-pr-reviewer.json +27 -0
- package/registry/packages/agent-researcher.json +27 -0
- package/registry/packages/agent-security-auditor.json +27 -0
- package/registry/packages/cmd-plan.json +26 -0
- package/registry/packages/cmd-review.json +26 -0
- package/registry/packages/mcp-filesystem.json +34 -0
- package/registry/packages/mcp-github.json +33 -0
- package/registry/packages/mcp-memory.json +33 -0
- package/registry/packages/mcp-postgres.json +34 -0
- package/registry/packages/mcp-sequential-thinking.json +33 -0
- package/registry/packages/obra-superpowers.json +29 -0
- package/registry/packages/pdf-compress.json +27 -0
- package/registry/packages/pdf-convert.json +27 -0
- package/registry/packages/pdf-extract.json +27 -0
- package/registry/packages/pdf-forms.json +27 -0
- package/registry/packages/pdf-merge.json +27 -0
- package/registry/packages/pdf-ocr.json +27 -0
- package/registry/packages/pdf-split.json +27 -0
- package/registry/packages/pdf-tables.json +27 -0
- package/registry/search.json +248 -0
- 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
|
+
[](https://github.com/oomerevren-beep/forge/actions)
|
|
4
|
+
[](https://www.npmjs.com/package/tryforge)
|
|
5
|
+
[](https://www.npmjs.com/package/tryforge)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](registry/index.json)
|
|
8
|
+
[](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
|
+
};
|