bridgellm 0.1.7 → 0.2.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/README.md +113 -63
- package/dist/commands/connect.d.ts +1 -1
- package/dist/commands/connect.js +28 -96
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/login.js +8 -58
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/team.js +7 -10
- package/dist/commands/team.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.js +25 -28
- package/dist/config.js.map +1 -1
- package/dist/index.js +169 -116
- package/dist/index.js.map +1 -1
- package/dist/ui.d.ts +5 -4
- package/dist/ui.js +88 -44
- package/dist/ui.js.map +1 -1
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -1,117 +1,167 @@
|
|
|
1
1
|
# BridgeLLM
|
|
2
2
|
|
|
3
|
-
**Let your coding agents talk to each other across services.**
|
|
4
|
-
|
|
5
3
|
[](https://www.npmjs.com/package/bridgellm)
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
Your AI coding agents can't talk to each other. Backend Claude doesn't know what Frontend Claude is building. Someone ends up on Slack copy-pasting API contracts.
|
|
6
|
+
|
|
7
|
+
BridgeLLM is an MCP server that lets agents share context, query each other, and stay in sync — without you being the middleman.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
npm install -g bridgellm
|
|
13
|
+
|
|
14
|
+
# or via Homebrew
|
|
15
|
+
brew install starvader13/bridgellm/bridgellm
|
|
13
16
|
```
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
Requires Node.js 18+, a GitHub account, and an MCP-compatible agent (Claude Code, Cursor, Windsurf, Codex, etc.).
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Get Started
|
|
16
23
|
|
|
17
24
|
```bash
|
|
18
|
-
|
|
25
|
+
bridgellm
|
|
19
26
|
```
|
|
20
27
|
|
|
21
|
-
|
|
28
|
+
The CLI walks you through setup interactively:
|
|
29
|
+
|
|
30
|
+
1. **Login** — opens GitHub OAuth in your browser
|
|
31
|
+
2. **Team** — create a new team or join with an invite code
|
|
32
|
+
3. **Role** — pick yours (backend, frontend, mobile, infra, etc.)
|
|
33
|
+
4. **Feature** — select the feature you're working on
|
|
34
|
+
|
|
35
|
+
Once done, it writes a `.mcp.json` in your project. Restart your IDE and your agent is connected.
|
|
36
|
+
|
|
37
|
+
### Second project, same team
|
|
22
38
|
|
|
23
39
|
```bash
|
|
24
|
-
|
|
40
|
+
cd another-project/
|
|
41
|
+
bridgellm
|
|
42
|
+
# skips login/team/role — just picks feature
|
|
25
43
|
```
|
|
26
44
|
|
|
27
|
-
|
|
45
|
+
### Already set up?
|
|
28
46
|
|
|
29
47
|
```bash
|
|
30
|
-
|
|
31
|
-
|
|
48
|
+
bridgellm
|
|
49
|
+
# shows current config
|
|
50
|
+
```
|
|
32
51
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
bridgellm connect
|
|
36
|
-
# → picks your feature
|
|
37
|
-
# → writes .mcp.json + CLAUDE.md
|
|
52
|
+
```
|
|
53
|
+
✓ Connected
|
|
38
54
|
|
|
39
|
-
|
|
55
|
+
┌─────────────────────────────────┐
|
|
56
|
+
│ Team: payments │
|
|
57
|
+
│ Feature: gift-cards │
|
|
58
|
+
│ Role: backend │
|
|
59
|
+
└─────────────────────────────────┘
|
|
40
60
|
```
|
|
41
61
|
|
|
42
|
-
|
|
62
|
+
---
|
|
43
63
|
|
|
44
|
-
|
|
45
|
-
|------|-------------|
|
|
46
|
-
| `bridge_read` | Search for contracts, decisions, notes |
|
|
47
|
-
| `bridge_write` | Publish a contract, decision, or note |
|
|
48
|
-
| `bridge_ask` | Post an async question (saved for later) |
|
|
49
|
-
| `bridge_query_agent` | Ask a live agent in real-time |
|
|
50
|
-
| `bridge_respond` | Answer or decline a pending query |
|
|
51
|
-
| `bridge_features` | List features and who's online |
|
|
64
|
+
## Change Settings
|
|
52
65
|
|
|
53
|
-
|
|
66
|
+
```bash
|
|
67
|
+
bridgellm --set role frontend # switch role
|
|
68
|
+
bridgellm --set feature checkout # switch feature
|
|
69
|
+
bridgellm --set team platform # switch team
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Updates config and rewrites `.mcp.json` automatically.
|
|
73
|
+
|
|
74
|
+
To re-pick everything interactively:
|
|
54
75
|
|
|
55
76
|
```bash
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
77
|
+
bridgellm --reconfigure
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
59
81
|
|
|
60
|
-
|
|
61
|
-
bridgellm connect # Connect to a feature
|
|
82
|
+
## Cleanup
|
|
62
83
|
|
|
63
|
-
|
|
64
|
-
bridgellm team create <name> # Create team, get invite code
|
|
65
|
-
bridgellm team join <invite-code> # Join with invite code
|
|
84
|
+
Remove project config (`.mcp.json`, `.bridgellm.yml`) from the current directory:
|
|
66
85
|
|
|
67
|
-
|
|
68
|
-
bridgellm
|
|
69
|
-
|
|
70
|
-
|
|
86
|
+
```bash
|
|
87
|
+
bridgellm --disconnect
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Wipe all local config (`~/.bridgellm/` and project files):
|
|
71
91
|
|
|
72
|
-
|
|
73
|
-
bridgellm
|
|
74
|
-
bridgellm reset # Remove everything, start fresh
|
|
92
|
+
```bash
|
|
93
|
+
bridgellm --reset
|
|
75
94
|
```
|
|
76
95
|
|
|
77
|
-
|
|
96
|
+
Offline-safe. Server-side tokens expire automatically (90-day TTL).
|
|
97
|
+
|
|
98
|
+
---
|
|
78
99
|
|
|
79
|
-
|
|
100
|
+
## What Your Agent Gets
|
|
101
|
+
|
|
102
|
+
Once connected, your agent has 6 MCP tools:
|
|
103
|
+
|
|
104
|
+
| Tool | Use it to |
|
|
105
|
+
|------|-----------|
|
|
106
|
+
| `bridge_read` | Search for contracts, decisions, notes published by other agents |
|
|
107
|
+
| `bridge_write` | Publish a contract, decision, note, or assumption |
|
|
108
|
+
| `bridge_ask` | Post a question for another agent (async — they'll see it later) |
|
|
109
|
+
| `bridge_query_agent` | Ask a live agent a question in real-time |
|
|
110
|
+
| `bridge_respond` | Answer or decline a pending query from another agent |
|
|
111
|
+
| `bridge_features` | List features and see who's online |
|
|
112
|
+
|
|
113
|
+
---
|
|
80
114
|
|
|
81
115
|
## How It Works
|
|
82
116
|
|
|
83
117
|
```
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
118
|
+
Backend Agent ── bridge_write ──▶ BridgeLLM ◀── bridge_read ── Frontend Agent
|
|
119
|
+
│
|
|
120
|
+
PostgreSQL
|
|
121
|
+
(shared context)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
No LLM inference on the server. No compute costs. BridgeLLM is a database and message router — your agents do the thinking.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Reference
|
|
129
|
+
|
|
130
|
+
### CLI
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
bridgellm setup / status
|
|
134
|
+
bridgellm --set <key> <value> change a setting (team, role, feature)
|
|
135
|
+
bridgellm --reconfigure re-run full setup
|
|
136
|
+
bridgellm --disconnect remove project config
|
|
137
|
+
bridgellm --reset wipe all local config
|
|
88
138
|
```
|
|
89
139
|
|
|
90
|
-
|
|
140
|
+
### Files
|
|
141
|
+
|
|
142
|
+
| File | Scope | Purpose |
|
|
143
|
+
|------|-------|---------|
|
|
144
|
+
| `~/.bridgellm/token` | Global | Auth token |
|
|
145
|
+
| `~/.bridgellm/server` | Global | Server URL |
|
|
146
|
+
| `~/.bridgellm/config.yml` | Global | Team, role |
|
|
147
|
+
| `.bridgellm.yml` | Project | Feature name |
|
|
148
|
+
| `.mcp.json` | Project | MCP server config (contains token) |
|
|
91
149
|
|
|
92
|
-
|
|
150
|
+
Add `.bridgellm.yml` and `.mcp.json` to your `.gitignore`.
|
|
93
151
|
|
|
94
|
-
|
|
95
|
-
|------|----------|---------------|
|
|
96
|
-
| `~/.bridgellm/config.yml` | Home dir | Team, role |
|
|
97
|
-
| `~/.bridgellm/token` | Home dir | Auth token |
|
|
98
|
-
| `~/.bridgellm/server` | Home dir | Server URL |
|
|
99
|
-
| `.bridgellm.yml` | Project root | Feature name (gitignored) |
|
|
100
|
-
| `.mcp.json` | Project root | MCP server config (gitignored) |
|
|
101
|
-
| `CLAUDE.md` | Project root | Agent instructions |
|
|
152
|
+
### Roles
|
|
102
153
|
|
|
103
|
-
|
|
154
|
+
`backend` `frontend` `web` `mobile` `ios` `android` `infra` `data` `qa` `design`
|
|
104
155
|
|
|
105
|
-
|
|
106
|
-
- An MCP-compatible coding agent (Claude Code, Cursor, Windsurf, Codex, etc.)
|
|
107
|
-
- A GitHub account
|
|
156
|
+
---
|
|
108
157
|
|
|
109
158
|
## Links
|
|
110
159
|
|
|
111
|
-
- [
|
|
160
|
+
- [Website](https://www.bridgellm.tech/)
|
|
112
161
|
- [npm](https://www.npmjs.com/package/bridgellm)
|
|
162
|
+
- [GitHub](https://github.com/starvader13/bridgellm)
|
|
113
163
|
- [Homebrew](https://github.com/starvader13/homebrew-bridgellm)
|
|
114
|
-
- [
|
|
164
|
+
- [Issues](https://github.com/starvader13/bridgellm/issues)
|
|
115
165
|
|
|
116
166
|
## License
|
|
117
167
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function
|
|
1
|
+
export declare function selectFeature(serverUrl: string, token: string, defaultFeature?: string): Promise<string>;
|
package/dist/commands/connect.js
CHANGED
|
@@ -1,106 +1,38 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (!config.role) {
|
|
23
|
-
throw new Error("No role configured. Run `bridgellm login` first.");
|
|
24
|
-
}
|
|
25
|
-
heading("BridgeLLM Connect");
|
|
26
|
-
info(`Team: ${config.team} • Role: ${config.role}`);
|
|
27
|
-
// Determine feature
|
|
28
|
-
let feature = config.feature;
|
|
29
|
-
if (!feature) {
|
|
30
|
-
try {
|
|
31
|
-
const { features } = (await apiFetch(config.server, config.token, "/api/features"));
|
|
32
|
-
if (features.length > 0) {
|
|
33
|
-
const labels = features.map((f) => `${f.name} (${f.context_count} contexts, ${f.active_sessions} online)`);
|
|
34
|
-
const result = await select("Select feature", labels, {
|
|
35
|
-
newLabel: "Create new feature",
|
|
36
|
-
});
|
|
37
|
-
feature = result.value;
|
|
1
|
+
import { ask, select } from '../ui.js';
|
|
2
|
+
export async function selectFeature(serverUrl, token, defaultFeature) {
|
|
3
|
+
let feature;
|
|
4
|
+
try {
|
|
5
|
+
const res = await fetch(`${serverUrl}/api/features`, {
|
|
6
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
7
|
+
});
|
|
8
|
+
if (!res.ok)
|
|
9
|
+
throw new Error('Failed to fetch features');
|
|
10
|
+
const { features } = (await res.json());
|
|
11
|
+
if (features.length > 0) {
|
|
12
|
+
const labels = features.map((f) => `${f.name} (${f.context_count} contexts, ${f.active_sessions} online)`);
|
|
13
|
+
const defaultIdx = defaultFeature
|
|
14
|
+
? Math.max(0, features.findIndex((f) => f.name === defaultFeature))
|
|
15
|
+
: 0;
|
|
16
|
+
const result = await select('Select feature', labels, {
|
|
17
|
+
newLabel: 'Create new feature',
|
|
18
|
+
defaultIndex: defaultIdx,
|
|
19
|
+
});
|
|
20
|
+
if (result.isNew) {
|
|
21
|
+
feature = await ask('Feature name: ');
|
|
38
22
|
}
|
|
39
23
|
else {
|
|
40
|
-
feature =
|
|
24
|
+
feature = result.value;
|
|
41
25
|
}
|
|
42
26
|
}
|
|
43
|
-
|
|
44
|
-
feature = await ask(
|
|
27
|
+
else {
|
|
28
|
+
feature = await ask('No features yet. Feature name: ');
|
|
45
29
|
}
|
|
46
30
|
}
|
|
47
|
-
if (!feature)
|
|
48
|
-
throw new Error("Feature name is required.");
|
|
49
|
-
// Save local config
|
|
50
|
-
const local = await getLocalConfig(cwd);
|
|
51
|
-
await saveLocalConfig(cwd, { ...local, feature });
|
|
52
|
-
// Write .mcp.json
|
|
53
|
-
const mcpConfig = {
|
|
54
|
-
mcpServers: {
|
|
55
|
-
bridgellm: {
|
|
56
|
-
type: "http",
|
|
57
|
-
url: `${config.server}/mcp`,
|
|
58
|
-
headers: {
|
|
59
|
-
Authorization: `Bearer ${config.token}`,
|
|
60
|
-
"X-BridgeLLM-Feature": feature,
|
|
61
|
-
"X-BridgeLLM-Role": config.role,
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
const mcpPath = join(cwd, ".mcp.json");
|
|
67
|
-
await writeFile(mcpPath, JSON.stringify(mcpConfig, null, 2) + "\n");
|
|
68
|
-
// Write CLAUDE.md
|
|
69
|
-
const claudeBlock = `${CLAUDE_MD_START}
|
|
70
|
-
## BridgeLLM
|
|
71
|
-
|
|
72
|
-
Connected to **${config.team}** | Feature: **${feature}** | Role: **${config.role}**
|
|
73
|
-
|
|
74
|
-
- BEFORE writing cross-service code, call \`bridge_read\` first
|
|
75
|
-
- After creating/modifying interfaces, use \`bridge_write\` to publish
|
|
76
|
-
- Use \`bridge_query_agent\` for live queries, \`bridge_ask\` for async
|
|
77
|
-
- Your feature is "${feature}" and role is "${config.role}"
|
|
78
|
-
${CLAUDE_MD_END}`;
|
|
79
|
-
const claudePath = join(cwd, "CLAUDE.md");
|
|
80
|
-
let claudeContent = "";
|
|
81
|
-
try {
|
|
82
|
-
claudeContent = await readFile(claudePath, "utf-8");
|
|
83
|
-
}
|
|
84
31
|
catch {
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
if (claudeContent.includes(CLAUDE_MD_START)) {
|
|
88
|
-
const regex = new RegExp(`${escapeRegex(CLAUDE_MD_START)}[\\s\\S]*?${escapeRegex(CLAUDE_MD_END)}`);
|
|
89
|
-
claudeContent = claudeContent.replace(regex, claudeBlock);
|
|
32
|
+
feature = await ask('Feature name: ');
|
|
90
33
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
(claudeContent.endsWith("\n") ? "\n" : "\n\n") +
|
|
95
|
-
claudeBlock +
|
|
96
|
-
"\n";
|
|
97
|
-
}
|
|
98
|
-
await writeFile(claudePath, claudeContent);
|
|
99
|
-
summary({ Team: config.team, Feature: feature, Role: config.role });
|
|
100
|
-
success("Wrote .bridgellm.yml, .mcp.json, CLAUDE.md");
|
|
101
|
-
info("Restart Claude Code to connect.\n");
|
|
102
|
-
}
|
|
103
|
-
function escapeRegex(s) {
|
|
104
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
34
|
+
if (!feature)
|
|
35
|
+
throw new Error('Feature name is required.');
|
|
36
|
+
return feature;
|
|
105
37
|
}
|
|
106
38
|
//# sourceMappingURL=connect.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/commands/connect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/commands/connect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,SAAiB,EACjB,KAAa,EACb,cAAuB;IAEvB,IAAI,OAAe,CAAC;IAEpB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,SAAS,eAAe,EAAE;YACnD,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;SAC9C,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAEzD,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAMrC,CAAC;QAEF,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CACzB,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,aAAa,cAAc,CAAC,CAAC,eAAe,UAAU,CAC9E,CAAC;YACF,MAAM,UAAU,GAAG,cAAc;gBAC/B,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC;gBACnE,CAAC,CAAC,CAAC,CAAC;YAEN,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,gBAAgB,EAAE,MAAM,EAAE;gBACpD,QAAQ,EAAE,oBAAoB;gBAC9B,YAAY,EAAE,UAAU;aACzB,CAAC,CAAC;YAEH,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;gBACjB,OAAO,GAAG,MAAM,GAAG,CAAC,gBAAgB,CAAC,CAAC;YACxC,CAAC;iBAAM,CAAC;gBACN,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC;YACzB,CAAC;QACH,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,MAAM,GAAG,CAAC,iCAAiC,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,MAAM,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACxC,CAAC;IAED,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAE3D,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/commands/login.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import http from 'node:http';
|
|
2
|
-
import { saveToken, saveServerUrl
|
|
3
|
-
import { heading, info, success
|
|
2
|
+
import { saveToken, saveServerUrl } from '../config.js';
|
|
3
|
+
import { heading, info, success } from '../ui.js';
|
|
4
|
+
function escapeHtml(s) {
|
|
5
|
+
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
6
|
+
}
|
|
4
7
|
export async function login(serverUrl) {
|
|
5
8
|
const server = http.createServer();
|
|
6
9
|
const port = await new Promise((resolve, reject) => {
|
|
@@ -21,8 +24,7 @@ export async function login(serverUrl) {
|
|
|
21
24
|
});
|
|
22
25
|
const authUrl = `${serverUrl}/auth/github?cli_port=${port}`;
|
|
23
26
|
heading('GitHub Authentication');
|
|
24
|
-
info(
|
|
25
|
-
info(`If it doesn't open, visit:\n ${authUrl}\n`);
|
|
27
|
+
info(`If the browser doesn't open, visit:\n ${authUrl}\n`);
|
|
26
28
|
const open = (await import('open')).default;
|
|
27
29
|
await open(authUrl);
|
|
28
30
|
const result = await new Promise((resolve, reject) => {
|
|
@@ -39,7 +41,7 @@ export async function login(serverUrl) {
|
|
|
39
41
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
40
42
|
res.end(`
|
|
41
43
|
<html><body style="font-family: system-ui; padding: 2em; text-align: center;">
|
|
42
|
-
<h2>Logged in as ${name ?? 'unknown'}</h2>
|
|
44
|
+
<h2>Logged in as ${escapeHtml(name ?? 'unknown')}</h2>
|
|
43
45
|
<p>You can close this tab and return to the terminal.</p>
|
|
44
46
|
</body></html>
|
|
45
47
|
`);
|
|
@@ -62,58 +64,6 @@ export async function login(serverUrl) {
|
|
|
62
64
|
server.close();
|
|
63
65
|
await saveToken(result.token);
|
|
64
66
|
await saveServerUrl(serverUrl);
|
|
65
|
-
success(`
|
|
66
|
-
// Check if global config already has team + role
|
|
67
|
-
const existing = await getGlobalConfig();
|
|
68
|
-
if (existing.team && existing.role) {
|
|
69
|
-
summary({ Team: existing.team, Role: existing.role });
|
|
70
|
-
info('Run `bridgellm connect` in your project directory.\n');
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
// First-time setup
|
|
74
|
-
heading('First-time setup');
|
|
75
|
-
console.log('');
|
|
76
|
-
// Team
|
|
77
|
-
let teamName;
|
|
78
|
-
const meRes = await fetch(`${serverUrl}/api/me`, {
|
|
79
|
-
headers: { Authorization: `Bearer ${result.token}` },
|
|
80
|
-
});
|
|
81
|
-
const me = await meRes.json();
|
|
82
|
-
if (me.team) {
|
|
83
|
-
teamName = me.team.name;
|
|
84
|
-
success(`Team: ${teamName}`);
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
const { value, isNew } = await select('No team yet', ['Create a new team', 'Join with invite code']);
|
|
88
|
-
if (value === 'Create a new team') {
|
|
89
|
-
const name = await ask('Team name: ');
|
|
90
|
-
const res = await fetch(`${serverUrl}/api/teams`, {
|
|
91
|
-
method: 'POST',
|
|
92
|
-
headers: { Authorization: `Bearer ${result.token}`, 'Content-Type': 'application/json' },
|
|
93
|
-
body: JSON.stringify({ name }),
|
|
94
|
-
});
|
|
95
|
-
const data = await res.json();
|
|
96
|
-
teamName = data.team.name;
|
|
97
|
-
success(`Created "${teamName}"`);
|
|
98
|
-
info(`Invite code: ${data.invite_code}`);
|
|
99
|
-
info('Share this code so teammates can join.\n');
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
const code = await ask('Invite code: ');
|
|
103
|
-
const res = await fetch(`${serverUrl}/api/teams/join`, {
|
|
104
|
-
method: 'POST',
|
|
105
|
-
headers: { Authorization: `Bearer ${result.token}`, 'Content-Type': 'application/json' },
|
|
106
|
-
body: JSON.stringify({ invite_code: code }),
|
|
107
|
-
});
|
|
108
|
-
const data = await res.json();
|
|
109
|
-
teamName = data.team.name;
|
|
110
|
-
success(`Joined "${teamName}"`);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
// Role
|
|
114
|
-
const role = await selectRole();
|
|
115
|
-
await saveGlobalConfig({ team: teamName, role });
|
|
116
|
-
summary({ Team: teamName, Role: role });
|
|
117
|
-
info('Run `bridgellm connect` in your project directory.\n');
|
|
67
|
+
success(`Authenticated as ${result.name}`);
|
|
118
68
|
}
|
|
119
69
|
//# sourceMappingURL=login.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/team.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getToken, getServerUrl, getGlobalConfig, saveGlobalConfig } from '../config.js';
|
|
2
|
+
import { success, info } from '../ui.js';
|
|
2
3
|
export async function createTeam(name) {
|
|
3
4
|
const token = await getToken();
|
|
4
5
|
const serverUrl = await getServerUrl();
|
|
@@ -12,16 +13,14 @@ export async function createTeam(name) {
|
|
|
12
13
|
});
|
|
13
14
|
const data = await res.json();
|
|
14
15
|
if (!res.ok) {
|
|
15
|
-
|
|
16
|
-
process.exit(1);
|
|
16
|
+
throw new Error(data.error);
|
|
17
17
|
}
|
|
18
18
|
const result = data;
|
|
19
|
-
// Update global config
|
|
20
19
|
const global = await getGlobalConfig();
|
|
21
20
|
await saveGlobalConfig({ ...global, team: result.team.name });
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
success(`Team "${result.team.name}" created`);
|
|
22
|
+
info(`Invite code: ${result.invite_code}`);
|
|
23
|
+
info('Share this code so teammates can join.');
|
|
25
24
|
}
|
|
26
25
|
export async function joinTeam(inviteCode) {
|
|
27
26
|
const token = await getToken();
|
|
@@ -36,13 +35,11 @@ export async function joinTeam(inviteCode) {
|
|
|
36
35
|
});
|
|
37
36
|
const data = await res.json();
|
|
38
37
|
if (!res.ok) {
|
|
39
|
-
|
|
40
|
-
process.exit(1);
|
|
38
|
+
throw new Error(data.error);
|
|
41
39
|
}
|
|
42
40
|
const result = data;
|
|
43
|
-
// Update global config
|
|
44
41
|
const global = await getGlobalConfig();
|
|
45
42
|
await saveGlobalConfig({ ...global, team: result.team.name });
|
|
46
|
-
|
|
43
|
+
success(`Joined "${result.team.name}"`);
|
|
47
44
|
}
|
|
48
45
|
//# sourceMappingURL=team.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"team.js","sourceRoot":"","sources":["../../src/commands/team.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"team.js","sourceRoot":"","sources":["../../src/commands/team.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACzF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY;IAC3C,MAAM,KAAK,GAAG,MAAM,QAAQ,EAAE,CAAC;IAC/B,MAAM,SAAS,GAAG,MAAM,YAAY,EAAE,CAAC;IAEvC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,SAAS,YAAY,EAAE;QAChD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,KAAK,EAAE;SACjC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC;KAC/B,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAE9B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAE,IAA0B,CAAC,KAAK,CAAC,CAAC;IACrD,CAAC;IAED,MAAM,MAAM,GAAG,IAAuD,CAAC;IAEvE,MAAM,MAAM,GAAG,MAAM,eAAe,EAAE,CAAC;IACvC,MAAM,gBAAgB,CAAC,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAE9D,OAAO,CAAC,SAAS,MAAM,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,CAAC;IAC9C,IAAI,CAAC,gBAAgB,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IAC3C,IAAI,CAAC,wCAAwC,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,UAAkB;IAC/C,MAAM,KAAK,GAAG,MAAM,QAAQ,EAAE,CAAC;IAC/B,MAAM,SAAS,GAAG,MAAM,YAAY,EAAE,CAAC;IAEvC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,SAAS,iBAAiB,EAAE;QACrD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,KAAK,EAAE;SACjC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;KAClD,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAE9B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAE,IAA0B,CAAC,KAAK,CAAC,CAAC;IACrD,CAAC;IAED,MAAM,MAAM,GAAG,IAAmD,CAAC;IAEnE,MAAM,MAAM,GAAG,MAAM,eAAe,EAAE,CAAC;IACvC,MAAM,gBAAgB,CAAC,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAE9D,OAAO,CAAC,WAAW,MAAM,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;AAC1C,CAAC"}
|
package/dist/config.d.ts
CHANGED
|
@@ -23,5 +23,6 @@ export declare function saveGlobalConfig(config: GlobalConfig): Promise<void>;
|
|
|
23
23
|
export declare function getLocalConfig(cwd: string): Promise<LocalConfig>;
|
|
24
24
|
export declare function saveLocalConfig(cwd: string, config: LocalConfig): Promise<void>;
|
|
25
25
|
export declare function getMergedConfig(cwd: string): Promise<MergedConfig>;
|
|
26
|
+
export declare function writeMcpJson(cwd: string): Promise<void>;
|
|
26
27
|
export declare function clean(cwd: string): Promise<void>;
|
|
27
28
|
export declare function reset(cwd: string): Promise<void>;
|
package/dist/config.js
CHANGED
|
@@ -16,7 +16,7 @@ export async function getToken() {
|
|
|
16
16
|
return (await readFile(join(BRIDGELLM_DIR, 'token'), 'utf-8')).trim();
|
|
17
17
|
}
|
|
18
18
|
catch {
|
|
19
|
-
throw new Error('Not logged in.
|
|
19
|
+
throw new Error('Not logged in.');
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
// ── Server ──
|
|
@@ -83,7 +83,28 @@ export async function getMergedConfig(cwd) {
|
|
|
83
83
|
feature: local.feature,
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
|
-
// ──
|
|
86
|
+
// ── Write .mcp.json ──
|
|
87
|
+
export async function writeMcpJson(cwd) {
|
|
88
|
+
const config = await getMergedConfig(cwd);
|
|
89
|
+
if (!config.feature || !config.role) {
|
|
90
|
+
throw new Error('Feature and role are required to write .mcp.json');
|
|
91
|
+
}
|
|
92
|
+
const mcpConfig = {
|
|
93
|
+
mcpServers: {
|
|
94
|
+
bridgellm: {
|
|
95
|
+
type: 'http',
|
|
96
|
+
url: `${config.server}/mcp`,
|
|
97
|
+
headers: {
|
|
98
|
+
Authorization: `Bearer ${config.token}`,
|
|
99
|
+
'X-BridgeLLM-Feature': config.feature,
|
|
100
|
+
'X-BridgeLLM-Role': config.role,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
await writeFile(join(cwd, '.mcp.json'), JSON.stringify(mcpConfig, null, 2) + '\n');
|
|
106
|
+
}
|
|
107
|
+
// ── Clean (project config only) ──
|
|
87
108
|
async function tryUnlink(path) {
|
|
88
109
|
try {
|
|
89
110
|
await unlink(path);
|
|
@@ -99,28 +120,6 @@ export async function clean(cwd) {
|
|
|
99
120
|
removed.push('.mcp.json');
|
|
100
121
|
if (await tryUnlink(join(cwd, '.bridgellm.yml')))
|
|
101
122
|
removed.push('.bridgellm.yml');
|
|
102
|
-
// Remove bridgellm block from CLAUDE.md
|
|
103
|
-
const claudePath = join(cwd, 'CLAUDE.md');
|
|
104
|
-
try {
|
|
105
|
-
const content = await readFile(claudePath, 'utf-8');
|
|
106
|
-
const START = '<!-- bridgellm:start -->';
|
|
107
|
-
const END = '<!-- bridgellm:end -->';
|
|
108
|
-
if (content.includes(START)) {
|
|
109
|
-
const regex = new RegExp(`\\n?${START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?${END.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\n?`);
|
|
110
|
-
const cleaned = content.replace(regex, '\n').trim();
|
|
111
|
-
if (cleaned) {
|
|
112
|
-
await writeFile(claudePath, cleaned + '\n');
|
|
113
|
-
removed.push('CLAUDE.md (bridgellm block)');
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
await unlink(claudePath);
|
|
117
|
-
removed.push('CLAUDE.md');
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
catch {
|
|
122
|
-
// no CLAUDE.md
|
|
123
|
-
}
|
|
124
123
|
if (removed.length > 0) {
|
|
125
124
|
console.log(` Removed: ${removed.join(', ')}`);
|
|
126
125
|
}
|
|
@@ -128,11 +127,9 @@ export async function clean(cwd) {
|
|
|
128
127
|
console.log(' Nothing to clean.');
|
|
129
128
|
}
|
|
130
129
|
}
|
|
131
|
-
// ── Reset (local + global) ──
|
|
130
|
+
// ── Reset (local + global, offline-safe) ──
|
|
132
131
|
export async function reset(cwd) {
|
|
133
|
-
// Clean local first
|
|
134
132
|
await clean(cwd);
|
|
135
|
-
// Remove global config
|
|
136
133
|
try {
|
|
137
134
|
await rm(BRIDGELLM_DIR, { recursive: true, force: true });
|
|
138
135
|
console.log(' Removed ~/.bridgellm/');
|
|
@@ -140,7 +137,7 @@ export async function reset(cwd) {
|
|
|
140
137
|
catch {
|
|
141
138
|
// already gone
|
|
142
139
|
}
|
|
143
|
-
console.log(' Reset complete.
|
|
140
|
+
console.log(' Reset complete.');
|
|
144
141
|
}
|
|
145
142
|
function parseSimpleYaml(raw) {
|
|
146
143
|
const result = {};
|