@ghl-ai/aw 0.1.0 → 0.1.1
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/cli.mjs +1 -1
- package/commands/init.mjs +106 -14
- package/commands/nuke.mjs +15 -19
- package/integrate.mjs +4 -0
- package/mcp.mjs +78 -90
- package/package.json +2 -2
package/cli.mjs
CHANGED
package/commands/init.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// commands/init.mjs — Initialize workspace config + pull ghl + template
|
|
2
2
|
|
|
3
|
-
import { mkdirSync, existsSync } from 'node:fs';
|
|
3
|
+
import { mkdirSync, existsSync, writeFileSync, readFileSync } from 'node:fs';
|
|
4
4
|
import { execSync } from 'node:child_process';
|
|
5
|
-
import { join } from 'node:path';
|
|
5
|
+
import { join, resolve } from 'node:path';
|
|
6
|
+
import { homedir } from 'node:os';
|
|
7
|
+
import { select, isCancel } from '@clack/prompts';
|
|
6
8
|
import * as config from '../config.mjs';
|
|
7
9
|
import * as fmt from '../fmt.mjs';
|
|
8
10
|
import { chalk } from '../fmt.mjs';
|
|
@@ -11,11 +13,72 @@ import { linkWorkspace } from '../link.mjs';
|
|
|
11
13
|
import { generateCommands, copyInstructions, initAwDocs } from '../integrate.mjs';
|
|
12
14
|
import { setupMcp } from '../mcp.mjs';
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
// Scope definitions
|
|
17
|
+
const SCOPES = {
|
|
18
|
+
user: {
|
|
19
|
+
label: 'Install for you (user scope)',
|
|
20
|
+
hint: 'Available in all your projects — stored in ~/.claude/',
|
|
21
|
+
rootDir: () => homedir(),
|
|
22
|
+
gitignore: false,
|
|
23
|
+
},
|
|
24
|
+
project: {
|
|
25
|
+
label: 'Install for all collaborators (project scope)',
|
|
26
|
+
hint: 'Checked into git — shared with your whole team via .claude/',
|
|
27
|
+
rootDir: () => process.cwd(),
|
|
28
|
+
gitignore: false,
|
|
29
|
+
},
|
|
30
|
+
local: {
|
|
31
|
+
label: 'Install for you, in this repo only (local scope)',
|
|
32
|
+
hint: 'Added to .gitignore — only you see it in this project',
|
|
33
|
+
rootDir: () => process.cwd(),
|
|
34
|
+
gitignore: true,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
async function pickScope(preselected) {
|
|
39
|
+
if (preselected && SCOPES[preselected]) return preselected;
|
|
40
|
+
|
|
41
|
+
const result = await select({
|
|
42
|
+
message: 'Where would you like to install?',
|
|
43
|
+
options: Object.entries(SCOPES).map(([value, s]) => ({
|
|
44
|
+
value,
|
|
45
|
+
label: s.label,
|
|
46
|
+
hint: s.hint,
|
|
47
|
+
})),
|
|
48
|
+
initialValue: 'project',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (isCancel(result)) {
|
|
52
|
+
fmt.cancel('Installation cancelled.');
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function addToGitignore(cwd, patterns) {
|
|
60
|
+
const gitignorePath = join(cwd, '.gitignore');
|
|
61
|
+
const toAdd = patterns.filter(p => {
|
|
62
|
+
if (!existsSync(gitignorePath)) return true;
|
|
63
|
+
const content = readFileSync(gitignorePath, 'utf8');
|
|
64
|
+
return !content.split('\n').some(line => line.trim() === p);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (toAdd.length === 0) return;
|
|
68
|
+
|
|
69
|
+
const block = `\n# Agentic Workspace (local scope)\n${toAdd.join('\n')}\n`;
|
|
70
|
+
if (existsSync(gitignorePath)) {
|
|
71
|
+
writeFileSync(gitignorePath, readFileSync(gitignorePath, 'utf8') + block);
|
|
72
|
+
} else {
|
|
73
|
+
writeFileSync(gitignorePath, block.trimStart());
|
|
74
|
+
}
|
|
75
|
+
fmt.logStep(`Added to .gitignore: ${toAdd.join(', ')}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function initCommand(args) {
|
|
15
79
|
const namespace = args['--namespace'] || null;
|
|
16
80
|
let user = args['--user'] || '';
|
|
17
81
|
const cwd = process.cwd();
|
|
18
|
-
const workspaceDir = join(cwd, '.aw_registry');
|
|
19
82
|
|
|
20
83
|
fmt.intro('aw init');
|
|
21
84
|
|
|
@@ -28,6 +91,14 @@ export function initCommand(args) {
|
|
|
28
91
|
fmt.cancel("'ghl' is a reserved namespace — it is the shared platform layer");
|
|
29
92
|
}
|
|
30
93
|
|
|
94
|
+
// Pick install scope
|
|
95
|
+
const scopeKey = await pickScope(args['--scope']);
|
|
96
|
+
const scope = SCOPES[scopeKey];
|
|
97
|
+
const rootDir = scope.rootDir();
|
|
98
|
+
const workspaceDir = join(rootDir, '.aw_registry');
|
|
99
|
+
|
|
100
|
+
fmt.logStep(`Scope: ${chalk.bold(scope.label)}`);
|
|
101
|
+
|
|
31
102
|
// Auto-detect user from GitHub if not provided
|
|
32
103
|
if (!user) {
|
|
33
104
|
try {
|
|
@@ -40,7 +111,7 @@ export function initCommand(args) {
|
|
|
40
111
|
// Create workspace dir
|
|
41
112
|
if (!existsSync(workspaceDir)) {
|
|
42
113
|
mkdirSync(workspaceDir, { recursive: true });
|
|
43
|
-
fmt.logStep(
|
|
114
|
+
fmt.logStep(`Created ${scopeKey === 'user' ? '~/' : ''}.aw_registry/`);
|
|
44
115
|
}
|
|
45
116
|
|
|
46
117
|
// Check existing config
|
|
@@ -48,28 +119,49 @@ export function initCommand(args) {
|
|
|
48
119
|
fmt.cancel('.sync-config.json already exists. Delete it to re-init.');
|
|
49
120
|
}
|
|
50
121
|
|
|
51
|
-
// Create config
|
|
52
|
-
const cfg = config.create(workspaceDir, { namespace, user });
|
|
122
|
+
// Create config (store scope so other commands know where to look)
|
|
123
|
+
const cfg = config.create(workspaceDir, { namespace, user, scope: scopeKey });
|
|
53
124
|
|
|
54
125
|
const infoLines = [
|
|
55
126
|
namespace ? `${chalk.dim('namespace:')} ${cfg.namespace}` : `${chalk.dim('namespace:')} ${chalk.dim('none')}`,
|
|
56
127
|
user ? `${chalk.dim('user:')} ${cfg.user}` : null,
|
|
128
|
+
`${chalk.dim('scope:')} ${scopeKey} → ${resolve(workspaceDir)}`,
|
|
57
129
|
].filter(Boolean).join('\n');
|
|
58
130
|
|
|
59
131
|
fmt.note(infoLines, 'Config created');
|
|
60
132
|
|
|
61
133
|
// Always pull ghl/ (platform layer)
|
|
62
|
-
pullCommand({ ...args, _positional: ['ghl'] });
|
|
134
|
+
pullCommand({ ...args, _positional: ['ghl'], _workspaceDir: workspaceDir });
|
|
63
135
|
|
|
64
136
|
// Pull [template]/ as the team namespace
|
|
65
137
|
if (namespace) {
|
|
66
|
-
pullCommand({ ...args, _positional: ['[template]'], _renameNamespace: namespace });
|
|
138
|
+
pullCommand({ ...args, _positional: ['[template]'], _renameNamespace: namespace, _workspaceDir: workspaceDir });
|
|
67
139
|
}
|
|
68
140
|
|
|
69
141
|
// Post-pull IDE integration
|
|
70
|
-
linkWorkspace(
|
|
71
|
-
generateCommands(
|
|
72
|
-
copyInstructions(
|
|
73
|
-
initAwDocs(
|
|
74
|
-
setupMcp(
|
|
142
|
+
linkWorkspace(rootDir);
|
|
143
|
+
generateCommands(rootDir);
|
|
144
|
+
const instructionFiles = copyInstructions(rootDir, null, namespace);
|
|
145
|
+
initAwDocs(rootDir);
|
|
146
|
+
const mcpFiles = setupMcp(rootDir, namespace);
|
|
147
|
+
|
|
148
|
+
// Write manifest of files created by aw init so nuke only deletes what we made
|
|
149
|
+
const manifestPath = join(workspaceDir, '.created-files.json');
|
|
150
|
+
const createdFiles = [...instructionFiles, ...mcpFiles].map(p =>
|
|
151
|
+
p.startsWith(rootDir) ? p.slice(rootDir.length + 1) : p
|
|
152
|
+
);
|
|
153
|
+
writeFileSync(manifestPath, JSON.stringify(createdFiles, null, 2) + '\n');
|
|
154
|
+
|
|
155
|
+
// For local scope: add to .gitignore
|
|
156
|
+
if (scope.gitignore) {
|
|
157
|
+
addToGitignore(cwd, ['.aw_registry/', '.aw_docs/', '.claude/', '.cursor/', '.codex/', '.agents/', 'mcp.json', 'CLAUDE.md', 'AGENTS.md']);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
fmt.outro(
|
|
161
|
+
scopeKey === 'user'
|
|
162
|
+
? `Installed to ~/ — available in all your projects`
|
|
163
|
+
: scopeKey === 'project'
|
|
164
|
+
? `Installed to .claude/ — commit this to share with your team`
|
|
165
|
+
: `Installed locally — added to .gitignore`
|
|
166
|
+
);
|
|
75
167
|
}
|
package/commands/nuke.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// commands/nuke.mjs — Remove entire .aw_registry/ and all generated IDE files
|
|
2
2
|
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import { existsSync, rmSync, lstatSync, unlinkSync, readdirSync } from 'node:fs';
|
|
4
|
+
import { existsSync, rmSync, lstatSync, unlinkSync, readdirSync, readFileSync } from 'node:fs';
|
|
5
5
|
import * as fmt from '../fmt.mjs';
|
|
6
6
|
import { chalk } from '../fmt.mjs';
|
|
7
7
|
|
|
@@ -9,22 +9,10 @@ import { chalk } from '../fmt.mjs';
|
|
|
9
9
|
const IDE_DIRS = ['.claude', '.cursor', '.codex'];
|
|
10
10
|
const CONTENT_TYPES = ['agents', 'skills', 'commands', 'blueprints', 'evals'];
|
|
11
11
|
|
|
12
|
-
//
|
|
13
|
-
const FILES_TO_CLEAN = [
|
|
14
|
-
'.claude/settings.local.json',
|
|
15
|
-
'.cursor/mcp.json',
|
|
16
|
-
'.codex/config.toml',
|
|
17
|
-
'mcp.json',
|
|
18
|
-
'CLAUDE.md',
|
|
19
|
-
'AGENTS.md',
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
// Dirs to remove if empty after cleanup (deepest first)
|
|
12
|
+
// Dirs to remove if empty after cleanup (deepest first — IDE roots intentionally excluded)
|
|
23
13
|
const DIRS_TO_PRUNE = [
|
|
24
14
|
// IDE content dirs
|
|
25
15
|
...IDE_DIRS.flatMap(ide => CONTENT_TYPES.map(t => `${ide}/${t}`)),
|
|
26
|
-
// IDE root dirs
|
|
27
|
-
...IDE_DIRS,
|
|
28
16
|
// Codex agents dir
|
|
29
17
|
'.agents/skills',
|
|
30
18
|
'.agents',
|
|
@@ -40,7 +28,7 @@ export function nukeCommand(args) {
|
|
|
40
28
|
fmt.cancel('No .aw_registry/ found — nothing to remove');
|
|
41
29
|
}
|
|
42
30
|
|
|
43
|
-
// 1. Remove symlinks
|
|
31
|
+
// 1. Remove symlinks from all IDE dirs
|
|
44
32
|
for (const ide of IDE_DIRS) {
|
|
45
33
|
for (const type of CONTENT_TYPES) {
|
|
46
34
|
const dir = join(cwd, ide, type);
|
|
@@ -61,14 +49,22 @@ export function nukeCommand(args) {
|
|
|
61
49
|
rmSync(awCmdDir, { recursive: true, force: true });
|
|
62
50
|
}
|
|
63
51
|
}
|
|
52
|
+
|
|
64
53
|
// 2. Clean .agents/skills/ (symlinks + generated Codex skill dirs)
|
|
65
54
|
const agentsSkillsDir = join(cwd, '.agents/skills');
|
|
66
55
|
if (existsSync(agentsSkillsDir)) {
|
|
67
56
|
rmSync(agentsSkillsDir, { recursive: true, force: true });
|
|
68
57
|
}
|
|
69
58
|
|
|
70
|
-
// 3. Remove
|
|
71
|
-
|
|
59
|
+
// 3. Remove only files that aw init actually created (from manifest)
|
|
60
|
+
const manifestPath = join(workspaceDir, '.created-files.json');
|
|
61
|
+
let filesToClean = [];
|
|
62
|
+
if (existsSync(manifestPath)) {
|
|
63
|
+
try {
|
|
64
|
+
filesToClean = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
65
|
+
} catch { /* ignore parse errors */ }
|
|
66
|
+
}
|
|
67
|
+
for (const rel of filesToClean) {
|
|
72
68
|
const p = join(cwd, rel);
|
|
73
69
|
try { if (existsSync(p)) rmSync(p); } catch { /* best effort */ }
|
|
74
70
|
}
|
|
@@ -80,7 +76,7 @@ export function nukeCommand(args) {
|
|
|
80
76
|
rmSync(awDocsDir, { recursive: true, force: true });
|
|
81
77
|
}
|
|
82
78
|
|
|
83
|
-
// 5. Prune empty dirs
|
|
79
|
+
// 5. Prune empty dirs (IDE roots are excluded — never fully delete .claude/.cursor/.codex)
|
|
84
80
|
for (const rel of DIRS_TO_PRUNE) {
|
|
85
81
|
const p = join(cwd, rel);
|
|
86
82
|
try {
|
|
@@ -90,6 +86,6 @@ export function nukeCommand(args) {
|
|
|
90
86
|
} catch { /* best effort */ }
|
|
91
87
|
}
|
|
92
88
|
|
|
93
|
-
fmt.logSuccess('Removed .aw_registry
|
|
89
|
+
fmt.logSuccess('Removed .aw_registry/, .aw_docs/, and all generated IDE files');
|
|
94
90
|
fmt.outro(`Run ${chalk.dim('aw init')} to start fresh`);
|
|
95
91
|
}
|
package/integrate.mjs
CHANGED
|
@@ -92,6 +92,7 @@ export function generateCommands(cwd) {
|
|
|
92
92
|
* Copy CLAUDE.md and AGENTS.md to project root.
|
|
93
93
|
*/
|
|
94
94
|
export function copyInstructions(cwd, tempDir, namespace) {
|
|
95
|
+
const createdFiles = [];
|
|
95
96
|
for (const file of ['CLAUDE.md', 'AGENTS.md']) {
|
|
96
97
|
const dest = join(cwd, file);
|
|
97
98
|
if (existsSync(dest)) continue;
|
|
@@ -105,6 +106,7 @@ export function copyInstructions(cwd, tempDir, namespace) {
|
|
|
105
106
|
}
|
|
106
107
|
writeFileSync(dest, content);
|
|
107
108
|
fmt.logSuccess(`Created ${file}`);
|
|
109
|
+
createdFiles.push(dest);
|
|
108
110
|
continue;
|
|
109
111
|
}
|
|
110
112
|
}
|
|
@@ -115,8 +117,10 @@ export function copyInstructions(cwd, tempDir, namespace) {
|
|
|
115
117
|
if (content) {
|
|
116
118
|
writeFileSync(dest, content);
|
|
117
119
|
fmt.logSuccess(`Created ${file}`);
|
|
120
|
+
createdFiles.push(dest);
|
|
118
121
|
}
|
|
119
122
|
}
|
|
123
|
+
return createdFiles;
|
|
120
124
|
}
|
|
121
125
|
|
|
122
126
|
function generateClaudeMd(cwd, namespace) {
|
package/mcp.mjs
CHANGED
|
@@ -29,110 +29,98 @@ function detectPaths() {
|
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Setup MCP configs for all IDEs. Only writes if file doesn't exist.
|
|
32
|
+
* Returns list of file paths that were actually created.
|
|
32
33
|
*/
|
|
33
34
|
export function setupMcp(cwd, namespace) {
|
|
34
35
|
const paths = detectPaths();
|
|
35
|
-
|
|
36
|
+
const createdFiles = [];
|
|
37
|
+
|
|
38
|
+
const attempt = (filePath, contentFn, append = false) => {
|
|
39
|
+
if (writeIfMissing(filePath, contentFn, append)) {
|
|
40
|
+
createdFiles.push(filePath);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
36
43
|
|
|
37
44
|
// .claude/settings.local.json
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
()
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
if (paths.gitJenkinsPath) {
|
|
50
|
-
servers['git-jenkins'] = {
|
|
51
|
-
command: 'node',
|
|
52
|
-
args: [paths.gitJenkinsPath],
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
if (Object.keys(servers).length === 0) return null;
|
|
56
|
-
return JSON.stringify({ mcpServers: servers }, null, 2) + '\n';
|
|
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] };
|
|
57
56
|
}
|
|
58
|
-
|
|
57
|
+
if (Object.keys(servers).length === 0) return null;
|
|
58
|
+
return JSON.stringify({ mcpServers: servers }, null, 2) + '\n';
|
|
59
|
+
});
|
|
59
60
|
|
|
60
61
|
// .cursor/mcp.json
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
()
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
if (paths.gitJenkinsPath) {
|
|
73
|
-
servers['git-jenkins'] = {
|
|
74
|
-
command: 'node',
|
|
75
|
-
args: [paths.gitJenkinsPath],
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
if (Object.keys(servers).length === 0) return null;
|
|
79
|
-
return JSON.stringify({ mcpServers: servers }, null, 2) + '\n';
|
|
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] };
|
|
80
73
|
}
|
|
81
|
-
|
|
74
|
+
if (Object.keys(servers).length === 0) return null;
|
|
75
|
+
return JSON.stringify({ mcpServers: servers }, null, 2) + '\n';
|
|
76
|
+
});
|
|
82
77
|
|
|
83
78
|
// .codex/config.toml — append MCP servers
|
|
84
79
|
const codexConfig = join(cwd, '.codex', 'config.toml');
|
|
85
80
|
if (!hasMcpSection(codexConfig)) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
()
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return lines.join('\n');
|
|
112
|
-
},
|
|
113
|
-
true // append mode
|
|
114
|
-
);
|
|
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 */);
|
|
115
106
|
}
|
|
116
107
|
|
|
117
108
|
// mcp.json at project root (Claude Code auto-discovery)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
()
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
env: { GHL_MCP_URL: paths.ghlMcpUrl, TEAM_NAME: namespace || '' },
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
if (Object.keys(servers).length === 0) return null;
|
|
130
|
-
return JSON.stringify({ mcpServers: servers }, null, 2) + '\n';
|
|
109
|
+
attempt(join(cwd, 'mcp.json'), () => {
|
|
110
|
+
const servers = {};
|
|
111
|
+
if (paths.ghlAiBridge) {
|
|
112
|
+
servers['ghl-ai'] = {
|
|
113
|
+
command: 'node',
|
|
114
|
+
args: [paths.ghlAiBridge],
|
|
115
|
+
env: { GHL_MCP_URL: paths.ghlMcpUrl, TEAM_NAME: namespace || '' },
|
|
116
|
+
};
|
|
131
117
|
}
|
|
132
|
-
|
|
118
|
+
if (Object.keys(servers).length === 0) return null;
|
|
119
|
+
return JSON.stringify({ mcpServers: servers }, null, 2) + '\n';
|
|
120
|
+
});
|
|
133
121
|
|
|
134
|
-
if (
|
|
135
|
-
fmt.logSuccess(`Created ${
|
|
122
|
+
if (createdFiles.length > 0) {
|
|
123
|
+
fmt.logSuccess(`Created ${createdFiles.length} MCP config${createdFiles.length > 1 ? 's' : ''}`);
|
|
136
124
|
}
|
|
137
125
|
|
|
138
126
|
const warnings = [];
|
|
@@ -142,13 +130,13 @@ export function setupMcp(cwd, namespace) {
|
|
|
142
130
|
fmt.logWarn(w);
|
|
143
131
|
}
|
|
144
132
|
|
|
145
|
-
return
|
|
133
|
+
return createdFiles;
|
|
146
134
|
}
|
|
147
135
|
|
|
148
136
|
function writeIfMissing(filePath, contentFn, append = false) {
|
|
149
|
-
if (!append && existsSync(filePath)) return
|
|
137
|
+
if (!append && existsSync(filePath)) return false;
|
|
150
138
|
const content = contentFn();
|
|
151
|
-
if (!content) return
|
|
139
|
+
if (!content) return false;
|
|
152
140
|
mkdirSync(join(filePath, '..'), { recursive: true });
|
|
153
141
|
if (append && existsSync(filePath)) {
|
|
154
142
|
const existing = readFileSync(filePath, 'utf8');
|
|
@@ -156,7 +144,7 @@ function writeIfMissing(filePath, contentFn, append = false) {
|
|
|
156
144
|
} else {
|
|
157
145
|
writeFileSync(filePath, content);
|
|
158
146
|
}
|
|
159
|
-
return
|
|
147
|
+
return true;
|
|
160
148
|
}
|
|
161
149
|
|
|
162
150
|
function hasMcpSection(filePath) {
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghl-ai/aw",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Agentic Workspace CLI — pull, push & manage agents, skills and commands from the registry",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"aw": "
|
|
7
|
+
"aw": "bin.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"bin.js",
|