@ghl-ai/aw 0.1.19 → 0.1.21
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/commands/push.mjs +25 -10
- package/mcp.mjs +70 -116
- package/package.json +1 -1
package/commands/push.mjs
CHANGED
|
@@ -113,7 +113,8 @@ export function pushCommand(args) {
|
|
|
113
113
|
const tempDir = mkdtempSync(join(tmpdir(), 'aw-upload-'));
|
|
114
114
|
|
|
115
115
|
try {
|
|
116
|
-
|
|
116
|
+
const repoUrl = repo.startsWith('http') ? repo : `https://github.com/${repo}.git`;
|
|
117
|
+
execSync(`git clone --filter=blob:none --no-checkout "${repoUrl}" "${tempDir}"`, { stdio: 'pipe' });
|
|
117
118
|
execSync(`git checkout ${REGISTRY_BASE_BRANCH}`, { cwd: tempDir, stdio: 'pipe' });
|
|
118
119
|
s.stop('Repository cloned');
|
|
119
120
|
|
|
@@ -178,20 +179,29 @@ export function pushCommand(args) {
|
|
|
178
179
|
|
|
179
180
|
const prTitle = `Add ${slug} (${parentDir}) to ${namespacePath}`;
|
|
180
181
|
const prBody = bodyParts.join('\n');
|
|
181
|
-
const prUrl = execFileSync('gh', [
|
|
182
|
-
'pr', 'create',
|
|
183
|
-
'--base', REGISTRY_BASE_BRANCH,
|
|
184
|
-
'--title', prTitle,
|
|
185
|
-
'--body', prBody,
|
|
186
|
-
], { cwd: tempDir, encoding: 'utf8' }).trim();
|
|
187
182
|
|
|
188
|
-
|
|
183
|
+
// Try gh for PR creation, fall back to manual URL
|
|
184
|
+
let prUrl;
|
|
185
|
+
try {
|
|
186
|
+
prUrl = execFileSync('gh', [
|
|
187
|
+
'pr', 'create',
|
|
188
|
+
'--base', REGISTRY_BASE_BRANCH,
|
|
189
|
+
'--title', prTitle,
|
|
190
|
+
'--body', prBody,
|
|
191
|
+
], { cwd: tempDir, encoding: 'utf8' }).trim();
|
|
192
|
+
} catch {
|
|
193
|
+
// gh not available — construct PR URL manually
|
|
194
|
+
const repoBase = repo.replace(/\.git$/, '');
|
|
195
|
+
prUrl = `https://github.com/${repoBase}/compare/${REGISTRY_BASE_BRANCH}...${branch}?expand=1`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
s3.stop('Branch pushed');
|
|
189
199
|
|
|
190
200
|
if (newNamespace) {
|
|
191
201
|
fmt.logInfo(`New namespace ${chalk.cyan(topNamespace)} — CODEOWNERS entry added`);
|
|
192
202
|
}
|
|
193
203
|
fmt.logSuccess(`PR: ${chalk.cyan(prUrl)}`);
|
|
194
|
-
fmt.outro('Upload complete');
|
|
204
|
+
fmt.outro('Upload complete — open the link above to create the PR');
|
|
195
205
|
} catch (e) {
|
|
196
206
|
fmt.cancel(`Upload failed: ${e.message}`);
|
|
197
207
|
} finally {
|
|
@@ -200,10 +210,15 @@ export function pushCommand(args) {
|
|
|
200
210
|
}
|
|
201
211
|
|
|
202
212
|
function getGitHubUser() {
|
|
213
|
+
// Try gh first, fall back to git config
|
|
203
214
|
try {
|
|
204
215
|
return execSync('gh api user --jq .login', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
205
216
|
} catch {
|
|
206
|
-
|
|
217
|
+
try {
|
|
218
|
+
return execSync('git config user.name', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
219
|
+
} catch {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
207
222
|
}
|
|
208
223
|
}
|
|
209
224
|
|
package/mcp.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
// mcp.mjs — MCP config generation for
|
|
1
|
+
// mcp.mjs — MCP config generation for Claude Code + Cursor (global ~/ configs)
|
|
2
|
+
// Uses native Streamable HTTP — no bridge process needed.
|
|
2
3
|
|
|
3
4
|
import { existsSync, writeFileSync, readFileSync, mkdirSync } from 'node:fs';
|
|
4
5
|
import { join, resolve } from 'node:path';
|
|
@@ -6,149 +7,102 @@ import { homedir } from 'node:os';
|
|
|
6
7
|
import * as fmt from './fmt.mjs';
|
|
7
8
|
|
|
8
9
|
const HOME = homedir();
|
|
10
|
+
const DEFAULT_MCP_URL = 'https://staging.services.leadconnectorhq.com/agentic-workspace/mcp';
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* Auto-detect MCP server paths.
|
|
12
14
|
*/
|
|
13
15
|
function detectPaths() {
|
|
14
|
-
const ghlAiBridge = join(HOME, '.claude', 'ghl-ai-bridge.mjs');
|
|
15
16
|
const gitJenkinsCandidates = [
|
|
16
17
|
join(HOME, 'Documents', 'GitHub', 'git-jenkins-mcp', 'dist', 'index.js'),
|
|
17
18
|
resolve('..', 'git-jenkins-mcp', 'dist', 'index.js'),
|
|
18
19
|
];
|
|
19
20
|
|
|
20
21
|
const gitJenkinsPath = gitJenkinsCandidates.find(p => existsSync(p)) || null;
|
|
21
|
-
const ghlMcpUrl = process.env.GHL_MCP_URL ||
|
|
22
|
+
const ghlMcpUrl = process.env.GHL_MCP_URL || DEFAULT_MCP_URL;
|
|
22
23
|
|
|
23
|
-
return {
|
|
24
|
-
ghlAiBridge: existsSync(ghlAiBridge) ? ghlAiBridge : null,
|
|
25
|
-
gitJenkinsPath,
|
|
26
|
-
ghlMcpUrl,
|
|
27
|
-
};
|
|
24
|
+
return { gitJenkinsPath, ghlMcpUrl };
|
|
28
25
|
}
|
|
29
26
|
|
|
30
27
|
/**
|
|
31
|
-
* Setup MCP configs for
|
|
32
|
-
*
|
|
28
|
+
* Setup MCP configs globally for Claude Code and Cursor.
|
|
29
|
+
* Merges ghl-ai server into existing configs without overwriting other servers.
|
|
30
|
+
* Returns list of file paths that were created or updated.
|
|
33
31
|
*/
|
|
34
32
|
export function setupMcp(cwd, namespace) {
|
|
35
33
|
const paths = detectPaths();
|
|
36
|
-
const
|
|
34
|
+
const updatedFiles = [];
|
|
37
35
|
|
|
38
|
-
const
|
|
39
|
-
if (writeIfMissing(filePath, contentFn, append)) {
|
|
40
|
-
createdFiles.push(filePath);
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// .claude/settings.local.json
|
|
45
|
-
attempt(join(cwd, '.claude', 'settings.local.json'), () => {
|
|
46
|
-
const servers = {};
|
|
47
|
-
if (paths.ghlAiBridge) {
|
|
48
|
-
servers['ghl-ai'] = {
|
|
49
|
-
command: 'node',
|
|
50
|
-
args: [paths.ghlAiBridge],
|
|
51
|
-
env: { GHL_MCP_URL: paths.ghlMcpUrl, TEAM_NAME: namespace || '' },
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
if (paths.gitJenkinsPath) {
|
|
55
|
-
servers['git-jenkins'] = { command: 'node', args: [paths.gitJenkinsPath] };
|
|
56
|
-
}
|
|
57
|
-
if (Object.keys(servers).length === 0) return null;
|
|
58
|
-
return JSON.stringify({ mcpServers: servers }, null, 2) + '\n';
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
// .cursor/mcp.json
|
|
62
|
-
attempt(join(cwd, '.cursor', 'mcp.json'), () => {
|
|
63
|
-
const servers = {};
|
|
64
|
-
if (paths.ghlAiBridge) {
|
|
65
|
-
servers['ghl-ai'] = {
|
|
66
|
-
command: 'node',
|
|
67
|
-
args: [paths.ghlAiBridge],
|
|
68
|
-
env: { GHL_MCP_URL: paths.ghlMcpUrl, TEAM_NAME: namespace || '' },
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
if (paths.gitJenkinsPath) {
|
|
72
|
-
servers['git-jenkins'] = { command: 'node', args: [paths.gitJenkinsPath] };
|
|
73
|
-
}
|
|
74
|
-
if (Object.keys(servers).length === 0) return null;
|
|
75
|
-
return JSON.stringify({ mcpServers: servers }, null, 2) + '\n';
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// .codex/config.toml — append MCP servers
|
|
79
|
-
const codexConfig = join(cwd, '.codex', 'config.toml');
|
|
80
|
-
if (!hasMcpSection(codexConfig)) {
|
|
81
|
-
attempt(codexConfig, () => {
|
|
82
|
-
const lines = [];
|
|
83
|
-
if (paths.ghlAiBridge) {
|
|
84
|
-
lines.push(
|
|
85
|
-
'[mcp_servers.ghl-ai]',
|
|
86
|
-
'command = "node"',
|
|
87
|
-
`args = ["${paths.ghlAiBridge}"]`,
|
|
88
|
-
'',
|
|
89
|
-
'[mcp_servers.ghl-ai.env]',
|
|
90
|
-
`GHL_MCP_URL = "${paths.ghlMcpUrl}"`,
|
|
91
|
-
`TEAM_NAME = "${namespace || ''}"`,
|
|
92
|
-
'',
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
if (paths.gitJenkinsPath) {
|
|
96
|
-
lines.push(
|
|
97
|
-
'[mcp_servers.git-jenkins]',
|
|
98
|
-
'command = "node"',
|
|
99
|
-
`args = ["${paths.gitJenkinsPath}"]`,
|
|
100
|
-
'',
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
if (lines.length === 0) return null;
|
|
104
|
-
return lines.join('\n');
|
|
105
|
-
}, true /* append mode */);
|
|
106
|
-
}
|
|
36
|
+
const mcpUrl = paths.ghlMcpUrl;
|
|
107
37
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
servers['ghl-ai'] = {
|
|
113
|
-
command: 'node',
|
|
114
|
-
args: [paths.ghlAiBridge],
|
|
115
|
-
env: { GHL_MCP_URL: paths.ghlMcpUrl, TEAM_NAME: namespace || '' },
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
if (Object.keys(servers).length === 0) return null;
|
|
119
|
-
return JSON.stringify({ mcpServers: servers }, null, 2) + '\n';
|
|
120
|
-
});
|
|
38
|
+
const ghlAiServer = { type: 'url', url: mcpUrl };
|
|
39
|
+
const gitJenkinsServer = paths.gitJenkinsPath
|
|
40
|
+
? { command: 'node', args: [paths.gitJenkinsPath] }
|
|
41
|
+
: null;
|
|
121
42
|
|
|
122
|
-
|
|
123
|
-
|
|
43
|
+
// ── Claude Code: ~/.claude/settings.json ──
|
|
44
|
+
const claudeSettingsPath = join(HOME, '.claude', 'settings.json');
|
|
45
|
+
if (mergeJsonMcpServer(claudeSettingsPath, 'ghl-ai', ghlAiServer)) {
|
|
46
|
+
updatedFiles.push(claudeSettingsPath);
|
|
47
|
+
}
|
|
48
|
+
if (gitJenkinsServer && mergeJsonMcpServer(claudeSettingsPath, 'git-jenkins', gitJenkinsServer)) {
|
|
49
|
+
updatedFiles.push(claudeSettingsPath);
|
|
124
50
|
}
|
|
125
51
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
|
|
52
|
+
// ── Cursor: ~/.cursor/mcp.json ──
|
|
53
|
+
const cursorMcpPath = join(HOME, '.cursor', 'mcp.json');
|
|
54
|
+
if (mergeJsonMcpServer(cursorMcpPath, 'ghl-ai', ghlAiServer)) {
|
|
55
|
+
updatedFiles.push(cursorMcpPath);
|
|
56
|
+
}
|
|
57
|
+
if (gitJenkinsServer && mergeJsonMcpServer(cursorMcpPath, 'git-jenkins', gitJenkinsServer)) {
|
|
58
|
+
updatedFiles.push(cursorMcpPath);
|
|
131
59
|
}
|
|
132
60
|
|
|
133
|
-
|
|
134
|
-
|
|
61
|
+
// Deduplicate (claude settings may be added twice if both servers written)
|
|
62
|
+
const unique = [...new Set(updatedFiles)];
|
|
135
63
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const content = contentFn();
|
|
139
|
-
if (!content) return false;
|
|
140
|
-
mkdirSync(join(filePath, '..'), { recursive: true });
|
|
141
|
-
if (append && existsSync(filePath)) {
|
|
142
|
-
const existing = readFileSync(filePath, 'utf8');
|
|
143
|
-
writeFileSync(filePath, existing + '\n' + content);
|
|
64
|
+
if (unique.length > 0) {
|
|
65
|
+
fmt.logSuccess(`MCP configured in ${unique.map(f => fmt.chalk.cyan(f)).join(', ')}`);
|
|
144
66
|
} else {
|
|
145
|
-
|
|
67
|
+
fmt.logInfo('MCP servers already configured — no changes needed');
|
|
146
68
|
}
|
|
147
|
-
|
|
69
|
+
|
|
70
|
+
if (!paths.gitJenkinsPath) {
|
|
71
|
+
fmt.logWarn('git-jenkins MCP not found — skipped');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return unique;
|
|
148
75
|
}
|
|
149
76
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Merge an MCP server entry into a JSON config file.
|
|
79
|
+
* Creates the file if it doesn't exist.
|
|
80
|
+
* Returns true if the file was modified.
|
|
81
|
+
*/
|
|
82
|
+
function mergeJsonMcpServer(filePath, serverName, serverConfig) {
|
|
83
|
+
let config = {};
|
|
84
|
+
|
|
85
|
+
if (existsSync(filePath)) {
|
|
86
|
+
try {
|
|
87
|
+
config = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
88
|
+
} catch {
|
|
89
|
+
config = {};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!config.mcpServers) {
|
|
94
|
+
config.mcpServers = {};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Check if already configured with same URL
|
|
98
|
+
const existing = config.mcpServers[serverName];
|
|
99
|
+
if (existing && JSON.stringify(existing) === JSON.stringify(serverConfig)) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
config.mcpServers[serverName] = serverConfig;
|
|
104
|
+
|
|
105
|
+
mkdirSync(join(filePath, '..'), { recursive: true });
|
|
106
|
+
writeFileSync(filePath, JSON.stringify(config, null, 2) + '\n');
|
|
107
|
+
return true;
|
|
154
108
|
}
|