@aion0/forge 0.6.1 → 0.8.0
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/.forge/mcp.json +8 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-316c6574/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-316c6574/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-44a94121/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-44a94121/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/07-projects.md +1 -1
- package/CLAUDE.md +2 -2
- package/RELEASE_NOTES.md +101 -5
- package/app/api/auth/check/route.ts +18 -0
- package/app/api/browser-bridge/route.ts +70 -0
- package/app/api/chat/sessions/[id]/events/route.ts +17 -0
- package/app/api/chat/sessions/[id]/fork/route.ts +15 -0
- package/app/api/chat/sessions/[id]/messages/route.ts +21 -0
- package/app/api/chat/sessions/[id]/route.ts +23 -0
- package/app/api/chat/sessions/route.ts +12 -0
- package/app/api/chat/temper-ping/route.ts +18 -0
- package/app/api/chat-proxy/[...path]/route.ts +83 -0
- package/app/api/connector-tool/route.ts +38 -0
- package/app/api/connectors/[id]/settings/route.ts +112 -0
- package/app/api/connectors/route.ts +108 -0
- package/app/api/health/tools/route.ts +14 -0
- package/app/api/issue-scanner-gitlab/route.ts +95 -0
- package/app/api/jobs/[id]/reset_dedup/route.ts +15 -0
- package/app/api/jobs/[id]/route.ts +31 -0
- package/app/api/jobs/[id]/run/route.ts +44 -0
- package/app/api/jobs/[id]/runs/[runId]/route.ts +15 -0
- package/app/api/jobs/[id]/runs/route.ts +15 -0
- package/app/api/jobs/preview/route.ts +193 -0
- package/app/api/jobs/route.ts +36 -0
- package/app/api/notify/test/route.ts +39 -7
- package/app/api/pipelines/[id]/route.ts +10 -1
- package/app/api/pipelines/route.ts +16 -2
- package/app/api/plugins/route.ts +40 -8
- package/app/api/project-sessions/route.ts +50 -10
- package/app/api/settings/route.ts +13 -0
- package/app/chat/page.tsx +531 -0
- package/bin/forge-server.mjs +3 -1
- package/cli/chat.ts +283 -0
- package/cli/jobs.ts +176 -0
- package/cli/mw.ts +28 -1
- package/cli/worktree.ts +245 -0
- package/components/ConnectorsPanel.tsx +275 -0
- package/components/Dashboard.tsx +90 -37
- package/components/JobsView.tsx +361 -0
- package/components/LogViewer.tsx +12 -2
- package/components/PipelineView.tsx +275 -56
- package/components/PluginsPanel.tsx +3 -1
- package/components/SettingsModal.tsx +229 -40
- package/components/SkillsPanel.tsx +12 -4
- package/components/TerminalLauncher.tsx +3 -1
- package/components/WebTerminal.tsx +32 -9
- package/components/WorkspaceView.tsx +18 -10
- package/docs/Connector-DeclarativeExtract-Handoff.md +471 -0
- package/docs/Connector-DeclarativeExtract-Spec.md +364 -0
- package/docs/Implementation-Plan-Browser-Agent.md +487 -0
- package/docs/Jobs-Design.md +240 -0
- package/docs/LOCAL-DEPLOY.md +3 -3
- package/docs/RFC-Browser-Connectors.md +509 -0
- package/lib/agents/index.ts +44 -6
- package/lib/agents/types.ts +1 -1
- package/lib/browser-bridge-standalone.ts +317 -0
- package/lib/builtin-plugins/github-api.yaml +93 -0
- package/lib/builtin-plugins/gitlab.yaml +860 -0
- package/lib/builtin-plugins/mantis.probe.js +176 -0
- package/lib/builtin-plugins/mantis.yaml +964 -0
- package/lib/builtin-plugins/pmdb.yaml +178 -0
- package/lib/builtin-plugins/teams.yaml +913 -0
- package/lib/chat/__test__/smoke.ts +30 -0
- package/lib/chat/agent-loop.ts +523 -0
- package/lib/chat/bridge-client.ts +59 -0
- package/lib/chat/llm/anthropic.ts +99 -0
- package/lib/chat/llm/index.ts +20 -0
- package/lib/chat/llm/openai.ts +215 -0
- package/lib/chat/llm/types.ts +42 -0
- package/lib/chat/local-memory.ts +300 -0
- package/lib/chat/memory-store.ts +87 -0
- package/lib/chat/memory-tools.ts +157 -0
- package/lib/chat/protocols/http.ts +118 -0
- package/lib/chat/protocols/shell.ts +101 -0
- package/lib/chat/proxy.ts +51 -0
- package/lib/chat/session-store.ts +272 -0
- package/lib/chat/telegram-bridge.ts +276 -0
- package/lib/chat/temper.ts +281 -0
- package/lib/chat/tool-dispatcher.ts +190 -0
- package/lib/chat/types.ts +50 -0
- package/lib/chat-standalone.ts +286 -0
- package/lib/crypto.ts +1 -1
- package/lib/health.ts +131 -0
- package/lib/help-docs/00-overview.md +2 -1
- package/lib/help-docs/01-settings.md +46 -25
- package/lib/help-docs/07-projects.md +1 -1
- package/lib/help-docs/10-troubleshooting.md +10 -2
- package/lib/help-docs/16-gitlab-autofix.md +114 -0
- package/lib/help-docs/17-connectors.md +322 -0
- package/lib/help-docs/18-chrome-mcp.md +134 -0
- package/lib/help-docs/19-jobs.md +140 -0
- package/lib/help-docs/20-mantis-bug-fix.md +115 -0
- package/lib/help-docs/CLAUDE.md +10 -0
- package/lib/init.ts +137 -50
- package/lib/iso-time.ts +30 -0
- package/lib/issue-scanner-gitlab.ts +281 -0
- package/lib/jobs/dispatcher.ts +217 -0
- package/lib/jobs/scheduler.ts +334 -0
- package/lib/jobs/store.ts +319 -0
- package/lib/jobs/types.ts +117 -0
- package/lib/pipeline-scheduler.ts +1 -6
- package/lib/pipeline.ts +790 -10
- package/lib/plugins/registry.ts +133 -8
- package/lib/plugins/templates.ts +83 -0
- package/lib/plugins/types.ts +140 -1
- package/lib/session-watcher.ts +36 -10
- package/lib/settings.ts +65 -33
- package/lib/skills.ts +3 -1
- package/lib/task-manager.ts +50 -22
- package/lib/telegram-bot.ts +71 -0
- package/lib/terminal-standalone.ts +58 -36
- package/lib/workspace/orchestrator.ts +1 -0
- package/middleware.ts +10 -0
- package/package.json +3 -2
- package/scripts/bench/README.md +1 -1
- package/scripts/bench/tasks/01-text-utils/validator.sh +1 -1
- package/scripts/bench/tasks/02-pagination/setup.sh +1 -1
- package/scripts/bench/tasks/02-pagination/validator.sh +1 -1
- package/scripts/bench/tasks/03-bug-fix/setup.sh +1 -1
- package/scripts/bench/tasks/03-bug-fix/validator.sh +1 -1
- package/src/core/db/database.ts +21 -12
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# GitLab Issue Auto-fix
|
|
2
|
+
|
|
3
|
+
Scan **self-hosted GitLab issues**, resolve base branch from issue metadata,
|
|
4
|
+
create a per-issue **worktree**, run Claude on the issue (with Premium epic
|
|
5
|
+
context + downloaded image attachments), and open an MR — all automated.
|
|
6
|
+
|
|
7
|
+
Mirrors the existing GitHub flow (`issue-fix-and-review`) but built around
|
|
8
|
+
the `glab` CLI and adapted for Premium epic links + image uploads.
|
|
9
|
+
|
|
10
|
+
## Setup
|
|
11
|
+
|
|
12
|
+
### 1. Install + auth glab
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
brew install glab
|
|
16
|
+
glab auth login --hostname <your-gitlab.example.com>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
GitLab host is auto-detected per project from the git remote, so you only
|
|
20
|
+
need to authenticate once per host. Self-hosted is fully supported.
|
|
21
|
+
|
|
22
|
+
### 2. Bind the pipeline to your project
|
|
23
|
+
|
|
24
|
+
Project tab → **Pipelines** → `+ Add` → `gitlab-issue-fix-and-review`.
|
|
25
|
+
|
|
26
|
+
In the binding form set:
|
|
27
|
+
- **Labels** (optional) — only process issues matching these labels
|
|
28
|
+
- **Assignee filter** — `me` (default), specific username, or empty
|
|
29
|
+
- **Base branch rule** — how to derive target branch from issue:
|
|
30
|
+
- `milestone` (default): `release/<milestone-title-slug>`
|
|
31
|
+
- `label:<prefix>`: pick first label starting with `<prefix>`, strip it
|
|
32
|
+
(e.g. `label:branch:` → label `branch:hotfix/4.5` → base `hotfix/4.5`)
|
|
33
|
+
- `desc`: regex match `^Base: <branch>` in issue description
|
|
34
|
+
- `default`: just `main` (or repo default)
|
|
35
|
+
- **Base branch override** — string; bypasses the rule entirely
|
|
36
|
+
- **MR title template** — vars: `{issue_id}`, `{issue_title}`.
|
|
37
|
+
Default: `Fix #{issue_id}: {issue_title}`
|
|
38
|
+
- **MR body template** — vars: `{issue_id}`, `{issue_url}`, `{summary}`.
|
|
39
|
+
Default closes the issue and inlines Claude's summary.
|
|
40
|
+
- **Schedule** — interval in minutes (`0` = manual only)
|
|
41
|
+
|
|
42
|
+
### 3. Triggering
|
|
43
|
+
|
|
44
|
+
Three ways:
|
|
45
|
+
|
|
46
|
+
| Mode | How |
|
|
47
|
+
|---|---|
|
|
48
|
+
| Manual single-issue | `Run` button in Pipelines tab → enter `issue_id` |
|
|
49
|
+
| Manual scan-all | `Scan` button → fetches assigned issues, fans out, dedups |
|
|
50
|
+
| Scheduled | Set interval > 0 in config; runs on Forge boot + every N min |
|
|
51
|
+
|
|
52
|
+
## Pipeline nodes
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
resolve → parse host + project path from origin
|
|
56
|
+
fetch-issue → glab issue view → JSON (title, description, labels, milestone, epic.iid)
|
|
57
|
+
fetch-extras → glab api → comments JSON + epic JSON (Premium)
|
|
58
|
+
resolve-base-branch → apply rule from config
|
|
59
|
+
worktree-setup → git worktree add .forge/worktrees/issue-<id>
|
|
60
|
+
download-attachments → curl GitLab /uploads/.../filename into .attachments/
|
|
61
|
+
fix-code → claude in worktree, prompt = issue + comments + epic + images
|
|
62
|
+
push-and-mr → git push + glab mr create
|
|
63
|
+
notify-issue → glab issue note → "🤖 Forge opened MR: <url>"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Pipeline output: an MR URL on the issue + linked from comments.
|
|
67
|
+
|
|
68
|
+
## Storage
|
|
69
|
+
|
|
70
|
+
Per-project config + dedup tracking lives in `workflow.db`:
|
|
71
|
+
|
|
72
|
+
- `issue_autofix_gitlab_config` — projectPath → config
|
|
73
|
+
- `issue_autofix_gitlab_processed` — projectPath × issueIid → status + MR
|
|
74
|
+
|
|
75
|
+
Already-processed issues are skipped in scan mode. Use `reset` to re-trigger,
|
|
76
|
+
or `retry` with extra context to nudge Claude in a different direction.
|
|
77
|
+
|
|
78
|
+
## Image attachments
|
|
79
|
+
|
|
80
|
+
GitLab uploads (`/uploads/<32-hex>/<file>`) in issue descriptions are
|
|
81
|
+
auto-downloaded to `<worktree>/.attachments/`. Claude can `Read` those
|
|
82
|
+
files. PNG/JPG/PDF supported via Claude Code's image reader.
|
|
83
|
+
|
|
84
|
+
The download uses the same PAT that glab already has authenticated (so
|
|
85
|
+
no extra credential setup). Falls back to anonymous HTTPS if the upload
|
|
86
|
+
happens to be public.
|
|
87
|
+
|
|
88
|
+
## Premium: Epic context
|
|
89
|
+
|
|
90
|
+
If the issue links to an epic (`issue.epic.iid` present), the pipeline:
|
|
91
|
+
1. Fetches the epic via `glab api groups/<id>/epics/<iid>`
|
|
92
|
+
2. Includes epic description + acceptance criteria in Claude's prompt
|
|
93
|
+
3. Claude can read the full JSON dump file path from `fetch-extras`
|
|
94
|
+
|
|
95
|
+
Free-tier instances just skip this step — no error.
|
|
96
|
+
|
|
97
|
+
## Authentication notes
|
|
98
|
+
|
|
99
|
+
`glab auth login` stores a token at `~/.config/glab-cli/config.yml`. Forge
|
|
100
|
+
inherits the env; the spawned `glab` invocations from the pipeline use the
|
|
101
|
+
same credentials. **No env vars needed** unless you want to override per-
|
|
102
|
+
session.
|
|
103
|
+
|
|
104
|
+
For headless / CI mode use `GITLAB_TOKEN` env var — `glab` picks it up too.
|
|
105
|
+
|
|
106
|
+
## Troubleshooting
|
|
107
|
+
|
|
108
|
+
| Symptom | Cause + fix |
|
|
109
|
+
|---|---|
|
|
110
|
+
| `ERROR: glab CLI not installed` | `brew install glab` (or apt) and `glab auth login` |
|
|
111
|
+
| `glab CLI not authenticated` | Token expired or wrong host. `glab auth login --hostname <host>` |
|
|
112
|
+
| MR push fails with "main branch protected" | Configure GitLab to allow force-push to `fix/*` branches OR change pipeline to drop `--force-with-lease` |
|
|
113
|
+
| Base branch resolves wrong | Switch `baseBranchRule` to `desc` and add `Base: <branch>` to the issue description as a forcing override |
|
|
114
|
+
| `403 on attachment fetch` | Some self-hosted instances require token even for public-link uploads. Make sure glab is authenticated to the same host that issued the upload URL |
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# Connectors
|
|
2
|
+
|
|
3
|
+
**Connectors** are Forge plugins of `category: connector` that expose tool
|
|
4
|
+
schemas + **the extraction scripts that implement them** to the **Forge
|
|
5
|
+
browser extension**. The extension is a generic runner: it doesn't know
|
|
6
|
+
about Mantis or GitLab specifically — it loads the manifest, finds/opens
|
|
7
|
+
the right tab, and executes whatever script the manifest ships.
|
|
8
|
+
|
|
9
|
+
> **Architectural principle**: adding a new connector should be a
|
|
10
|
+
> Forge-only change. No browser extension release required.
|
|
11
|
+
>
|
|
12
|
+
> See `docs/Connector-DeclarativeExtract-Spec.md` for the full spec of
|
|
13
|
+
> the manifest schema and runtime contract.
|
|
14
|
+
|
|
15
|
+
## Execution model: DOM extraction, not REST
|
|
16
|
+
|
|
17
|
+
**Browser-side connectors do NOT call REST APIs.** They navigate the
|
|
18
|
+
user's tabs and parse rendered HTML via `chrome.scripting.executeScript`.
|
|
19
|
+
This is the whole point of routing through a browser extension — reuse
|
|
20
|
+
the user's already-authenticated UI session, with zero token management.
|
|
21
|
+
|
|
22
|
+
Why this matters in practice:
|
|
23
|
+
|
|
24
|
+
| System | REST API needs | Browser DOM needs |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| MantisBT | Per-user API token (`Authorization` header) | Already-logged-in PHPSESSID cookie ✅ |
|
|
27
|
+
| GitLab | Personal Access Token | Session cookie on `/-/issues/` etc. ✅ |
|
|
28
|
+
| JIRA Server | PAT or basic auth | Session cookie ✅ |
|
|
29
|
+
| Teams web | MSAL bearer token | Session cookie ✅ |
|
|
30
|
+
|
|
31
|
+
If a connector hit the REST API path, every user would need to mint and
|
|
32
|
+
manage a token per system. That defeats the extension's value
|
|
33
|
+
proposition. **Token-based fallback is opt-in per connector, not the
|
|
34
|
+
default code path.**
|
|
35
|
+
|
|
36
|
+
This means:
|
|
37
|
+
- **No PAT / API tokens to manage** — connector reuses the user's logged-in browser session.
|
|
38
|
+
- **Data never flows through Forge** — Forge only ships the manifest + extraction script. The LLM in the extension calls the tool, the extension scrapes in the user's tab, the LLM sees the result.
|
|
39
|
+
- Forge's role: **discovery + settings sync + script delivery**.
|
|
40
|
+
- Trade-off: site redesigns break scripts. Bump the manifest `version` and update the `script` block — users get the fix on next refresh, no extension release.
|
|
41
|
+
|
|
42
|
+
## Architecture
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
Forge Extension User's tabs
|
|
46
|
+
───── ───────── ───────────
|
|
47
|
+
mantis.yaml ┌──────────────┐
|
|
48
|
+
tools: Generic runner │ Mantis tab │
|
|
49
|
+
list_my_bugs: ─────────▶ ① fetch manifest │ (logged in) │
|
|
50
|
+
page: { url, ... } ② render templates │ │
|
|
51
|
+
script: | ③ acquire tab matching │ #bug_list │
|
|
52
|
+
const list = ... host_match, navigate │ ◀── runs │
|
|
53
|
+
return { bugs, ... } to page.url the script
|
|
54
|
+
④ executeScript(script) from the
|
|
55
|
+
in the tab manifest
|
|
56
|
+
⑤ return JSON to LLM │ │
|
|
57
|
+
└──────────────┘
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The extension has **no per-connector code**. Adding GitLab means dropping
|
|
61
|
+
a `gitlab.yaml` next to `mantis.yaml` — extension auto-discovers it.
|
|
62
|
+
|
|
63
|
+
## Plugin manifest format
|
|
64
|
+
|
|
65
|
+
A connector plugin is a YAML in `lib/builtin-plugins/<id>.yaml` (or
|
|
66
|
+
`~/.forge/plugins/<id>/plugin.yaml`):
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
id: mantis
|
|
70
|
+
name: MantisBT
|
|
71
|
+
icon: "🐞"
|
|
72
|
+
version: "0.2.0"
|
|
73
|
+
category: connector # ← marks this as a connector
|
|
74
|
+
mode: browser-side # server-side | browser-side | hybrid
|
|
75
|
+
|
|
76
|
+
# Per-user settings (rendered as a form in the extension)
|
|
77
|
+
settings:
|
|
78
|
+
base_url:
|
|
79
|
+
type: string
|
|
80
|
+
label: Mantis base URL
|
|
81
|
+
required: true
|
|
82
|
+
|
|
83
|
+
# Plugin-level: where the extension finds / opens an authenticated tab.
|
|
84
|
+
# Chrome match pattern; {settings.*} expanded at API response time.
|
|
85
|
+
host_match: "{base_url}/*"
|
|
86
|
+
|
|
87
|
+
# Substring detected after navigation → tells the runner "user not logged in".
|
|
88
|
+
login_redirect: "/login_page.php"
|
|
89
|
+
|
|
90
|
+
tools:
|
|
91
|
+
list_my_bugs:
|
|
92
|
+
description: "List bugs assigned to me."
|
|
93
|
+
parameters:
|
|
94
|
+
status: { type: select, options: ["open", "closed", "all"], default: "open" }
|
|
95
|
+
limit: { type: number, default: 50 }
|
|
96
|
+
|
|
97
|
+
# Page to navigate to (or stay on, if on_target matches current URL).
|
|
98
|
+
page:
|
|
99
|
+
url: "{base_url}/view_all_bug_page.php"
|
|
100
|
+
on_target: "/view_all_bug_page.php"
|
|
101
|
+
|
|
102
|
+
# Function BODY. Implicit `args` parameter. Returns JSON-serializable.
|
|
103
|
+
# Runs IN THE USER'S TAB (page context). No closures over Forge / extension.
|
|
104
|
+
script: |
|
|
105
|
+
const { status, limit } = args;
|
|
106
|
+
const list = document.querySelector('#bug_list');
|
|
107
|
+
if (!list) return { bugs: [], total: 0, _error: '#bug_list not found' };
|
|
108
|
+
// ... extract rows, filter, return
|
|
109
|
+
return { bugs, total };
|
|
110
|
+
|
|
111
|
+
add_comment:
|
|
112
|
+
destructive: true # ← extension prompts user before running
|
|
113
|
+
description: "Add a note to a bug."
|
|
114
|
+
parameters:
|
|
115
|
+
bug_id: { type: number, required: true }
|
|
116
|
+
text: { type: string, required: true }
|
|
117
|
+
page:
|
|
118
|
+
url: "{base_url}/bug_view_page.php?bug_id={args.bug_id}"
|
|
119
|
+
script: |
|
|
120
|
+
// Form-submit or fetch() with same-origin cookies
|
|
121
|
+
...
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Template variables**:
|
|
125
|
+
- `{base_url}` / `{settings.<name>}` → expanded server-side from saved settings
|
|
126
|
+
- `{args.<name>}` → expanded by the extension at run time from the LLM's tool input
|
|
127
|
+
|
|
128
|
+
**Script contract**:
|
|
129
|
+
- Receives `args` (the LLM's parameters)
|
|
130
|
+
- Returns a JSON-serializable value (no DOM nodes, no functions)
|
|
131
|
+
- Has access to `document`, `fetch`, `URL`, etc. (page context)
|
|
132
|
+
- Can call same-origin `fetch()` — cookies auto-attached
|
|
133
|
+
- Must be self-contained — no closures over the manifest's surroundings
|
|
134
|
+
- Errors thrown are caught by the runner and returned as tool errors
|
|
135
|
+
|
|
136
|
+
See `docs/Connector-DeclarativeExtract-Spec.md` for the complete contract.
|
|
137
|
+
|
|
138
|
+
### 1 plugin = 1 connector (default)
|
|
139
|
+
|
|
140
|
+
The above shape is the **default 1:1 case**. For same-vendor suites with
|
|
141
|
+
shared auth (Atlassian, Google Workspace, M365), use the `connectors[]`
|
|
142
|
+
escape hatch — one plugin declares multiple connector entries that share
|
|
143
|
+
the user's OAuth/SSO:
|
|
144
|
+
|
|
145
|
+
```yaml
|
|
146
|
+
id: atlassian-suite
|
|
147
|
+
category: connector
|
|
148
|
+
mode: hybrid
|
|
149
|
+
connectors:
|
|
150
|
+
- id: jira
|
|
151
|
+
host_match: "*://*.atlassian.net/*"
|
|
152
|
+
tools: { ... }
|
|
153
|
+
- id: confluence
|
|
154
|
+
host_match: "*://*.atlassian.net/wiki/*"
|
|
155
|
+
tools: { ... }
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Most plugins should stay 1:1. Use 1:N only for genuine shared-auth suites.
|
|
159
|
+
|
|
160
|
+
## HTTP API
|
|
161
|
+
|
|
162
|
+
All endpoints require `X-Forge-Token` header (obtain via `POST /api/auth/verify`).
|
|
163
|
+
|
|
164
|
+
### `GET /api/connectors`
|
|
165
|
+
List connector plugins (installed + available). For installed connectors,
|
|
166
|
+
`page.url` / `host_match` etc. have `{base_url}` / `{settings.*}` expanded
|
|
167
|
+
from the user's saved settings; `{args.*}` is left literal.
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"connectors": [
|
|
172
|
+
{
|
|
173
|
+
"plugin_id": "mantis",
|
|
174
|
+
"name": "MantisBT",
|
|
175
|
+
"icon": "🐞",
|
|
176
|
+
"version": "0.2.0",
|
|
177
|
+
"mode": "browser-side",
|
|
178
|
+
"installed": true,
|
|
179
|
+
"host_match": "https://mantis.acme.com/*",
|
|
180
|
+
"login_redirect": "/login_page.php",
|
|
181
|
+
"entries": [
|
|
182
|
+
{
|
|
183
|
+
"id": "mantis",
|
|
184
|
+
"tools": {
|
|
185
|
+
"list_my_bugs": {
|
|
186
|
+
"description": "...",
|
|
187
|
+
"parameters": {...},
|
|
188
|
+
"page": { "url": "https://mantis.acme.com/view_all_bug_page.php", "on_target": "/view_all_bug_page.php" },
|
|
189
|
+
"script": "const { status, limit } = args; ..."
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
"settings": { "base_url": {...} }
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Query params:
|
|
201
|
+
- `installed=true` → only plugins the user has configured
|
|
202
|
+
- `id=<plugin_id>` → single connector detail (alternative to a path param)
|
|
203
|
+
|
|
204
|
+
### `GET /api/connectors?id=<id>` / `GET /api/connectors/<id>`
|
|
205
|
+
Single connector detail. Same shape as a list entry, wrapped in `{ connector: ... }`.
|
|
206
|
+
|
|
207
|
+
### `GET /api/connectors/<id>/settings`
|
|
208
|
+
Read the user's saved settings for a connector.
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"settings": { "base_url": "https://mantis.acme.com", "default_project": "Web" },
|
|
213
|
+
"schema": { "base_url": {...}, "default_project": {...} },
|
|
214
|
+
"installed": true
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
`schema` is the merged settings schema across all entries (1:N). `settings`
|
|
219
|
+
includes any defaults declared in the schema, overlaid with the user's
|
|
220
|
+
saved values.
|
|
221
|
+
|
|
222
|
+
### `POST /api/connectors/<id>/settings`
|
|
223
|
+
Save settings. Body: `{ settings: { base_url: "..." } }` (or the object directly).
|
|
224
|
+
|
|
225
|
+
First save = creates the install record. Subsequent saves update it.
|
|
226
|
+
|
|
227
|
+
```json
|
|
228
|
+
{ "ok": true, "settings": { "base_url": "..." } }
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Built-in connectors
|
|
232
|
+
|
|
233
|
+
| ID | Description |
|
|
234
|
+
|---|---|
|
|
235
|
+
| `mantis` | MantisBT — list/get/search bugs, add comments. Self-hosted. |
|
|
236
|
+
| `gitlab-browser` | GitLab — issues, MRs, pipelines via browser session. gitlab.com + self-hosted. |
|
|
237
|
+
|
|
238
|
+
The CLI-backed `glab` GitLab integration in `gitlab-issue-fix-and-review`
|
|
239
|
+
is **separate** — that runs server-side as a pipeline. The browser
|
|
240
|
+
connector is for the extension's interactive use.
|
|
241
|
+
|
|
242
|
+
## Writing a new connector
|
|
243
|
+
|
|
244
|
+
1. Drop a YAML in `~/.forge/plugins/<id>/plugin.yaml` with `category: connector`.
|
|
245
|
+
2. Define `settings` (e.g. `base_url`) the user must provide.
|
|
246
|
+
3. Define `host_match` and `login_redirect` at the plugin level.
|
|
247
|
+
4. For each tool: schema (`parameters`), `page` block, `script` body.
|
|
248
|
+
5. **Use DOM extraction**, not REST API calls. `script` runs in the tab,
|
|
249
|
+
has same-origin cookies via `fetch()`, can read the DOM directly.
|
|
250
|
+
6. Use `mantis.probe.js`-style helper scripts at dev time to discover
|
|
251
|
+
stable selectors; paste the resulting selectors into your `script`.
|
|
252
|
+
7. Restart Forge — extension picks it up via `GET /api/connectors` on
|
|
253
|
+
next refresh. **No extension code changes needed.**
|
|
254
|
+
|
|
255
|
+
### When REST is unavoidable
|
|
256
|
+
|
|
257
|
+
Some sites are pure SPAs that virtualize lists, render via canvas, or
|
|
258
|
+
require many interactions to expose data. In those rare cases, add an
|
|
259
|
+
optional `api_token` setting (`type: secret`) and call `fetch()` with
|
|
260
|
+
the token from within `script`. Default code path stays DOM; token is
|
|
261
|
+
the fallback the user explicitly accepts.
|
|
262
|
+
|
|
263
|
+
## Server-side protocols (http, shell)
|
|
264
|
+
|
|
265
|
+
When a site has a clean REST API and you don't need a browser tab at
|
|
266
|
+
all, declare `protocol: http` on the tool. Forge issues the request
|
|
267
|
+
server-side and returns the body to the LLM. Same for `protocol:
|
|
268
|
+
shell` — Forge spawns a process with an explicit arg array (no
|
|
269
|
+
`shell:true`, so templated values cannot inject metacharacters).
|
|
270
|
+
|
|
271
|
+
```yaml
|
|
272
|
+
# lib/builtin-plugins/github-api.yaml — see file for full example
|
|
273
|
+
tools:
|
|
274
|
+
get_repo:
|
|
275
|
+
protocol: http
|
|
276
|
+
parameters:
|
|
277
|
+
repo: { type: string, required: true }
|
|
278
|
+
request:
|
|
279
|
+
method: GET
|
|
280
|
+
url: 'https://api.github.com/repos/{args.repo}'
|
|
281
|
+
headers:
|
|
282
|
+
Accept: 'application/vnd.github+json'
|
|
283
|
+
Authorization: 'Bearer {settings.token}'
|
|
284
|
+
|
|
285
|
+
git_log:
|
|
286
|
+
protocol: shell
|
|
287
|
+
parameters:
|
|
288
|
+
repo: { type: string, required: true }
|
|
289
|
+
n: { type: number }
|
|
290
|
+
command: ['git', '-C', '{args.repo}', 'log', '-n', '{args.n}', '--oneline']
|
|
291
|
+
timeout_ms: 5000
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Templates `{base_url}`, `{settings.*}`, `{args.*}` all expand at
|
|
295
|
+
dispatch time. For `http`, body can be a string or a JSON object; for
|
|
296
|
+
`shell`, every arg is templated independently so an arg with spaces or
|
|
297
|
+
quotes stays a single literal arg.
|
|
298
|
+
|
|
299
|
+
Response body / stdout is truncated to ~8 KB for the LLM context;
|
|
300
|
+
default timeout 30 s, max 5 min. `protocol: browser` (the default)
|
|
301
|
+
keeps using the extension runner; `http` and `shell` run entirely on
|
|
302
|
+
Forge (chat-standalone, port 8408).
|
|
303
|
+
|
|
304
|
+
**Safety:** `protocol: shell` lets a YAML pick any binary on PATH —
|
|
305
|
+
review at install time. There is no auto allow-list in v1.
|
|
306
|
+
|
|
307
|
+
### Iterating selectors
|
|
308
|
+
|
|
309
|
+
If a connector breaks after a site redesign, **only the YAML's `script`
|
|
310
|
+
body needs to change**. Bump `version`, edit, restart Forge — extension
|
|
311
|
+
users pick up the fix on their next refresh.
|
|
312
|
+
|
|
313
|
+
## Troubleshooting
|
|
314
|
+
|
|
315
|
+
| Symptom | Cause + fix |
|
|
316
|
+
|---|---|
|
|
317
|
+
| Extension shows "no connectors" | Token expired — call `POST /api/auth/verify` to refresh |
|
|
318
|
+
| Tool not appearing for LLM | Plugin loaded but `category` missing, OR `script` missing for the tool |
|
|
319
|
+
| Settings POST returns 500 | Plugin id not found (typo?) or `plugin-configs.json` not writable |
|
|
320
|
+
| Tool returns "login required" | `login_redirect` substring matched the tab URL after nav — user needs to log in to that site |
|
|
321
|
+
| Script throws but error opaque | Wrap script body in try/catch and `return { _error: e.message }`; the runner catches uncaught throws but caller-visible message is cleaner |
|
|
322
|
+
| Strict-CSP site (github.com etc.) refuses `new Function` | Page CSP blocks dynamic eval. For those sites, write a server-side connector (REST + token) instead of browser-side. Document the limitation per connector. |
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Chrome MCP Setup
|
|
2
|
+
|
|
3
|
+
Connect Forge's Claude Code sessions to a running Chrome instance via
|
|
4
|
+
**chrome-devtools-mcp**, so the agent can navigate pages, query the
|
|
5
|
+
DOM, and execute scripts against the user's real browser session.
|
|
6
|
+
|
|
7
|
+
This is the development-time path for authoring browser connectors —
|
|
8
|
+
the agent writes a connector handler, runs it against a real Mantis /
|
|
9
|
+
GitLab / etc. page, sees the actual DOM output, and iterates.
|
|
10
|
+
|
|
11
|
+
## How it fits
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
Forge Claude Code session
|
|
15
|
+
│
|
|
16
|
+
│ MCP (stdio)
|
|
17
|
+
▼
|
|
18
|
+
chrome-devtools-mcp ──CDP──▶ Chrome (user's real instance)
|
|
19
|
+
│
|
|
20
|
+
▼
|
|
21
|
+
Mantis / GitLab / any logged-in tab
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## One-time setup
|
|
25
|
+
|
|
26
|
+
### 1. Start Chrome with remote debugging enabled
|
|
27
|
+
|
|
28
|
+
Quit all Chrome windows first (so the new instance can take the debug
|
|
29
|
+
port).
|
|
30
|
+
|
|
31
|
+
**macOS:**
|
|
32
|
+
```bash
|
|
33
|
+
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
|
|
34
|
+
--remote-debugging-port=9222 \
|
|
35
|
+
--user-data-dir="$HOME/Library/Application Support/Google/Chrome"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The `--user-data-dir` argument keeps your normal profile (and all your
|
|
39
|
+
logins) — drop it only if you want a fresh profile.
|
|
40
|
+
|
|
41
|
+
**Linux:**
|
|
42
|
+
```bash
|
|
43
|
+
google-chrome --remote-debugging-port=9222 --user-data-dir="$HOME/.config/google-chrome"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Windows:**
|
|
47
|
+
```powershell
|
|
48
|
+
"C:\Program Files\Google\Chrome\Application\chrome.exe" `
|
|
49
|
+
--remote-debugging-port=9222 `
|
|
50
|
+
--user-data-dir="$env:LOCALAPPDATA\Google\Chrome\User Data"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Verify it worked: open `http://localhost:9222/json/version` in the new
|
|
54
|
+
Chrome — you should see a JSON page with `Browser`, `Protocol-Version`, etc.
|
|
55
|
+
|
|
56
|
+
### 2. Add the MCP server to Forge settings
|
|
57
|
+
|
|
58
|
+
Settings → MCP Servers → Add. Fill in:
|
|
59
|
+
|
|
60
|
+
| Field | Value |
|
|
61
|
+
|---|---|
|
|
62
|
+
| Name | `chrome` |
|
|
63
|
+
| Type | `stdio` |
|
|
64
|
+
| Command | `npx` |
|
|
65
|
+
| Args | `["-y", "chrome-devtools-mcp"]` |
|
|
66
|
+
| Env | (leave empty — defaults to localhost:9222) |
|
|
67
|
+
|
|
68
|
+
Save. Forge will now merge this into every project's `.mcp.json` next
|
|
69
|
+
time a session is created or refreshed.
|
|
70
|
+
|
|
71
|
+
Equivalent `settings.yaml` snippet:
|
|
72
|
+
|
|
73
|
+
```yaml
|
|
74
|
+
mcpServers:
|
|
75
|
+
chrome:
|
|
76
|
+
type: stdio
|
|
77
|
+
command: npx
|
|
78
|
+
args:
|
|
79
|
+
- "-y"
|
|
80
|
+
- "chrome-devtools-mcp"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 3. Open a Forge project / workspace
|
|
84
|
+
|
|
85
|
+
When Claude Code launches in the project, it'll auto-discover the new
|
|
86
|
+
MCP server (the `.mcp.json` Forge writes now has both `forge` and
|
|
87
|
+
`chrome` entries). The agent will see browser tools like
|
|
88
|
+
`chrome_navigate`, `chrome_query_selector`, `chrome_evaluate`, etc.
|
|
89
|
+
|
|
90
|
+
Verify in a session: ask the agent "what MCP tools do you have?" — it
|
|
91
|
+
should list chrome_* tools alongside Forge tools.
|
|
92
|
+
|
|
93
|
+
## Using it for connector development
|
|
94
|
+
|
|
95
|
+
Workflow inside a Forge workspace:
|
|
96
|
+
|
|
97
|
+
1. Ask the agent: *"Develop a Mantis list_my_bugs connector. Target
|
|
98
|
+
page is https://mantis.forge.com/view_all_bug_page.php."*
|
|
99
|
+
2. Agent navigates the Chrome tab there via `chrome_navigate`.
|
|
100
|
+
3. Agent queries the DOM via `chrome_query_selector` /
|
|
101
|
+
`chrome_evaluate` to understand the page structure.
|
|
102
|
+
4. Agent writes the handler code (`lib/builtin-plugins/mantis.handler.js`
|
|
103
|
+
or wherever your code-publishing scheme lands).
|
|
104
|
+
5. Agent runs the candidate code in the page via `chrome_evaluate`,
|
|
105
|
+
sees real output, iterates.
|
|
106
|
+
6. When done, agent commits the connector via normal git flow / publishes
|
|
107
|
+
per the connector marketplace flow.
|
|
108
|
+
|
|
109
|
+
No copy-paste loop. No probe scripts. Real DOM at every step.
|
|
110
|
+
|
|
111
|
+
## Cleanup
|
|
112
|
+
|
|
113
|
+
To stop using Chrome MCP:
|
|
114
|
+
|
|
115
|
+
1. Remove the `chrome` entry from Settings → MCP Servers.
|
|
116
|
+
2. Forge regenerates `.mcp.json` on the next session creation.
|
|
117
|
+
3. Optionally close the debug-enabled Chrome and restart normally.
|
|
118
|
+
|
|
119
|
+
## Troubleshooting
|
|
120
|
+
|
|
121
|
+
| Symptom | Cause + fix |
|
|
122
|
+
|---|---|
|
|
123
|
+
| Agent says it has no chrome_* tools | Forge hasn't written the new `.mcp.json` yet — open the project tab in Forge to trigger `ensureMcpConfig`. Or just delete the project's `.mcp.json` and reopen. |
|
|
124
|
+
| `chrome_navigate` errors with "no debug target" | Chrome isn't running with `--remote-debugging-port=9222`. Verify `curl localhost:9222/json/version`. |
|
|
125
|
+
| Multiple Chrome processes | Quit all Chrome windows first, then start with the debug flag. macOS app launches and bare-binary launches don't share the port. |
|
|
126
|
+
| Login redirect on every nav | You're using `--user-data-dir=/tmp` or a fresh profile — point it at your real one to reuse cookies. |
|
|
127
|
+
| Enterprise / SSO restrictions | Some corporate MDM disables CDP. There's no workaround in those environments — fall back to the in-extension probe loop. |
|
|
128
|
+
|
|
129
|
+
## Security note
|
|
130
|
+
|
|
131
|
+
Connecting to Chrome via CDP means anything able to reach
|
|
132
|
+
`localhost:9222` can drive your browser. Only enable on machines where
|
|
133
|
+
you trust everything local. Don't expose the debug port over the
|
|
134
|
+
network.
|