@ghl-ai/aw 0.1.25 → 0.1.26-beta.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/commands/init.mjs +1 -1
- package/commands/pull.mjs +3 -2
- package/commands/push.mjs +6 -6
- package/commands/search.mjs +5 -5
- package/constants.mjs +5 -2
- package/git.mjs +4 -7
- package/integrate.mjs +1 -1
- package/mcp.mjs +40 -4
- package/package.json +1 -1
package/commands/init.mjs
CHANGED
|
@@ -218,7 +218,7 @@ export async function initCommand(args) {
|
|
|
218
218
|
|
|
219
219
|
// Pull latest (parallel)
|
|
220
220
|
// cfg.include has the renamed namespace (e.g. 'revex/courses'), but the repo
|
|
221
|
-
// only has '
|
|
221
|
+
// only has '.aw_registry/[template]/' — remap non-platform entries back.
|
|
222
222
|
const freshCfg = config.load(GLOBAL_AW_DIR);
|
|
223
223
|
if (freshCfg && freshCfg.include.length > 0) {
|
|
224
224
|
const pullJobs = freshCfg.include.map(p => {
|
package/commands/pull.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import * as config from '../config.mjs';
|
|
|
8
8
|
import * as fmt from '../fmt.mjs';
|
|
9
9
|
import { chalk } from '../fmt.mjs';
|
|
10
10
|
import { sparseCheckout, sparseCheckoutAsync, cleanup, includeToSparsePaths } from '../git.mjs';
|
|
11
|
+
import { REGISTRY_DIR } from '../constants.mjs';
|
|
11
12
|
import { walkRegistryTree } from '../registry.mjs';
|
|
12
13
|
import { matchesAny } from '../glob.mjs';
|
|
13
14
|
import { computePlan } from '../plan.mjs';
|
|
@@ -111,7 +112,7 @@ export async function pullCommand(args) {
|
|
|
111
112
|
|
|
112
113
|
try {
|
|
113
114
|
const registryDirs = [];
|
|
114
|
-
const regBase = join(tempDir,
|
|
115
|
+
const regBase = join(tempDir, REGISTRY_DIR);
|
|
115
116
|
|
|
116
117
|
if (existsSync(regBase)) {
|
|
117
118
|
for (const name of listDirs(regBase)) {
|
|
@@ -235,7 +236,7 @@ export async function pullAsync(args) {
|
|
|
235
236
|
|
|
236
237
|
try {
|
|
237
238
|
const registryDirs = [];
|
|
238
|
-
const regBase = join(tempDir,
|
|
239
|
+
const regBase = join(tempDir, REGISTRY_DIR);
|
|
239
240
|
|
|
240
241
|
if (existsSync(regBase)) {
|
|
241
242
|
for (const name of listDirs(regBase)) {
|
package/commands/push.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { execSync, execFileSync } from 'node:child_process';
|
|
|
6
6
|
import { tmpdir } from 'node:os';
|
|
7
7
|
import * as fmt from '../fmt.mjs';
|
|
8
8
|
import { chalk } from '../fmt.mjs';
|
|
9
|
-
import { REGISTRY_REPO, REGISTRY_BASE_BRANCH } from '../constants.mjs';
|
|
9
|
+
import { REGISTRY_REPO, REGISTRY_BASE_BRANCH, REGISTRY_DIR } from '../constants.mjs';
|
|
10
10
|
import { resolveInput } from '../paths.mjs';
|
|
11
11
|
import { load as loadManifest } from '../manifest.mjs';
|
|
12
12
|
import { hashFile } from '../registry.mjs';
|
|
@@ -98,8 +98,8 @@ export function pushCommand(args) {
|
|
|
98
98
|
|
|
99
99
|
const isDir = statSync(absPath).isDirectory();
|
|
100
100
|
const registryTarget = isDir
|
|
101
|
-
?
|
|
102
|
-
:
|
|
101
|
+
? `${REGISTRY_DIR}/${namespacePath}/${parentDir}/${slug}`
|
|
102
|
+
: `${REGISTRY_DIR}/${namespacePath}/${parentDir}/${slug}.md`;
|
|
103
103
|
|
|
104
104
|
fmt.note(
|
|
105
105
|
[
|
|
@@ -148,13 +148,13 @@ export function pushCommand(args) {
|
|
|
148
148
|
|
|
149
149
|
// Check if this is a new namespace — auto-add CODEOWNERS entry
|
|
150
150
|
let newNamespace = false;
|
|
151
|
-
const nsDir = join(tempDir,
|
|
151
|
+
const nsDir = join(tempDir, REGISTRY_DIR, topNamespace);
|
|
152
152
|
const codeownersPath = join(tempDir, 'CODEOWNERS');
|
|
153
153
|
if (!existsSync(nsDir) || isNewNamespaceInCodeowners(codeownersPath, topNamespace)) {
|
|
154
154
|
newNamespace = true;
|
|
155
155
|
const ghUser = getGitHubUser();
|
|
156
156
|
if (ghUser && existsSync(codeownersPath)) {
|
|
157
|
-
const line =
|
|
157
|
+
const line = `/${REGISTRY_DIR}/${topNamespace}/ @${ghUser}\n`;
|
|
158
158
|
appendFileSync(codeownersPath, line);
|
|
159
159
|
execSync('git add CODEOWNERS', { cwd: tempDir, stdio: 'pipe' });
|
|
160
160
|
}
|
|
@@ -236,5 +236,5 @@ function getGitHubUser() {
|
|
|
236
236
|
function isNewNamespaceInCodeowners(codeownersPath, namespace) {
|
|
237
237
|
if (!existsSync(codeownersPath)) return true;
|
|
238
238
|
const content = readFileSync(codeownersPath, 'utf8');
|
|
239
|
-
return !content.includes(
|
|
239
|
+
return !content.includes(`/${REGISTRY_DIR}/${namespace}/`);
|
|
240
240
|
}
|
package/commands/search.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { execSync } from 'node:child_process';
|
|
|
7
7
|
import * as config from '../config.mjs';
|
|
8
8
|
import * as fmt from '../fmt.mjs';
|
|
9
9
|
import { chalk } from '../fmt.mjs';
|
|
10
|
-
import { REGISTRY_BASE_BRANCH, REGISTRY_REPO } from '../constants.mjs';
|
|
10
|
+
import { REGISTRY_BASE_BRANCH, REGISTRY_REPO, REGISTRY_DIR } from '../constants.mjs';
|
|
11
11
|
|
|
12
12
|
export function searchCommand(args) {
|
|
13
13
|
const query = (args._positional || []).join(' ').toLowerCase();
|
|
@@ -150,14 +150,14 @@ function searchRemote(repo, query) {
|
|
|
150
150
|
try {
|
|
151
151
|
// git archive fetches a single file from remote — no clone needed
|
|
152
152
|
content = execSync(
|
|
153
|
-
`git archive --remote="${repoUrl}" ${REGISTRY_BASE_BRANCH}
|
|
153
|
+
`git archive --remote="${repoUrl}" ${REGISTRY_BASE_BRANCH} ${REGISTRY_DIR}/registry.json | tar -xO ${REGISTRY_DIR}/registry.json`,
|
|
154
154
|
{ encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }
|
|
155
155
|
);
|
|
156
156
|
} catch {
|
|
157
157
|
// GitHub doesn't support git archive — fallback to gh API
|
|
158
158
|
try {
|
|
159
159
|
const raw = execSync(
|
|
160
|
-
`gh api "repos/${repo}/contents/registry
|
|
160
|
+
`gh api "repos/${repo}/contents/${REGISTRY_DIR}/registry.json?ref=${REGISTRY_BASE_BRANCH}" --jq .content -H "Accept: application/vnd.github.v3+json"`,
|
|
161
161
|
{ encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }
|
|
162
162
|
);
|
|
163
163
|
content = Buffer.from(raw.trim(), 'base64').toString('utf8');
|
|
@@ -167,9 +167,9 @@ function searchRemote(repo, query) {
|
|
|
167
167
|
try {
|
|
168
168
|
execSync(`git clone --filter=blob:none --no-checkout "${repoUrl}" "${tempDir}"`, { stdio: 'pipe' });
|
|
169
169
|
execSync('git sparse-checkout init --cone', { cwd: tempDir, stdio: 'pipe' });
|
|
170
|
-
execSync(
|
|
170
|
+
execSync(`git sparse-checkout set --skip-checks "${REGISTRY_DIR}/registry.json"`, { cwd: tempDir, stdio: 'pipe' });
|
|
171
171
|
execSync(`git checkout ${REGISTRY_BASE_BRANCH}`, { cwd: tempDir, stdio: 'pipe' });
|
|
172
|
-
content = readFileSync(join(tempDir,
|
|
172
|
+
content = readFileSync(join(tempDir, REGISTRY_DIR, 'registry.json'), 'utf8');
|
|
173
173
|
} finally {
|
|
174
174
|
try { execSync(`rm -rf "${tempDir}"`, { stdio: 'pipe' }); } catch {}
|
|
175
175
|
}
|
package/constants.mjs
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
// constants.mjs — Single source of truth for registry settings.
|
|
2
2
|
|
|
3
3
|
/** Base branch for PRs and sync checkout */
|
|
4
|
-
export const REGISTRY_BASE_BRANCH = '
|
|
4
|
+
export const REGISTRY_BASE_BRANCH = 'feat/aw-platform-docs';
|
|
5
5
|
|
|
6
6
|
/** Default registry repository */
|
|
7
|
-
export const REGISTRY_REPO = 'GoHighLevel/
|
|
7
|
+
export const REGISTRY_REPO = 'GoHighLevel/platform-docs';
|
|
8
|
+
|
|
9
|
+
/** Directory inside the registry repo that holds platform/ and [template]/ */
|
|
10
|
+
export const REGISTRY_DIR = '.aw_registry';
|
package/git.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { mkdtempSync, existsSync } from 'node:fs';
|
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import { tmpdir } from 'node:os';
|
|
7
7
|
import { promisify } from 'node:util';
|
|
8
|
-
import { REGISTRY_BASE_BRANCH } from './constants.mjs';
|
|
8
|
+
import { REGISTRY_BASE_BRANCH, REGISTRY_DIR } from './constants.mjs';
|
|
9
9
|
|
|
10
10
|
const exec = promisify(execCb);
|
|
11
11
|
|
|
@@ -76,16 +76,13 @@ export function cleanup(tempDir) {
|
|
|
76
76
|
|
|
77
77
|
/**
|
|
78
78
|
* Compute sparse checkout paths from include paths.
|
|
79
|
-
* e.g., ["platform", "dev/agents/debugger"] -> ["
|
|
79
|
+
* e.g., ["platform", "dev/agents/debugger"] -> [".aw_registry/platform", ".aw_registry/dev/agents/debugger"]
|
|
80
80
|
*/
|
|
81
81
|
export function includeToSparsePaths(paths) {
|
|
82
82
|
const result = new Set();
|
|
83
83
|
for (const p of paths) {
|
|
84
|
-
result.add(
|
|
84
|
+
result.add(`${REGISTRY_DIR}/${p}`);
|
|
85
85
|
}
|
|
86
|
-
|
|
87
|
-
result.add('registry/CLAUDE.md');
|
|
88
|
-
result.add('registry/AGENTS.md');
|
|
89
|
-
result.add('registry/AW-PROTOCOL.md');
|
|
86
|
+
result.add(`${REGISTRY_DIR}/AW-PROTOCOL.md`);
|
|
90
87
|
return [...result];
|
|
91
88
|
}
|
package/integrate.mjs
CHANGED
|
@@ -65,7 +65,7 @@ export function copyInstructions(cwd, tempDir, namespace) {
|
|
|
65
65
|
if (existsSync(dest)) continue;
|
|
66
66
|
|
|
67
67
|
if (tempDir) {
|
|
68
|
-
const src = join(tempDir, '
|
|
68
|
+
const src = join(tempDir, '.aw_registry', file);
|
|
69
69
|
if (existsSync(src)) {
|
|
70
70
|
let content = readFileSync(src, 'utf8');
|
|
71
71
|
if (namespace) {
|
package/mcp.mjs
CHANGED
|
@@ -35,12 +35,12 @@ export function setupMcp(cwd, namespace) {
|
|
|
35
35
|
|
|
36
36
|
const mcpUrl = paths.ghlMcpUrl;
|
|
37
37
|
|
|
38
|
-
const ghlAiServer = { type: '
|
|
38
|
+
const ghlAiServer = { type: 'http', url: mcpUrl };
|
|
39
39
|
const gitJenkinsServer = paths.gitJenkinsPath
|
|
40
40
|
? { command: 'node', args: [paths.gitJenkinsPath] }
|
|
41
41
|
: null;
|
|
42
42
|
|
|
43
|
-
// ── Claude Code: ~/.claude/settings.json ──
|
|
43
|
+
// ── Claude Code: ~/.claude/settings.json (global) ──
|
|
44
44
|
const claudeSettingsPath = join(HOME, '.claude', 'settings.json');
|
|
45
45
|
if (mergeJsonMcpServer(claudeSettingsPath, 'ghl-ai', ghlAiServer)) {
|
|
46
46
|
updatedFiles.push(claudeSettingsPath);
|
|
@@ -49,7 +49,37 @@ export function setupMcp(cwd, namespace) {
|
|
|
49
49
|
updatedFiles.push(claudeSettingsPath);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
// ──
|
|
52
|
+
// ── Claude Code: .mcp.json (project root — highest priority) ──
|
|
53
|
+
// Claude Code resolves project .mcp.json before global settings.json,
|
|
54
|
+
// so we must write here to ensure the HTTP URL takes precedence over
|
|
55
|
+
// any stale command-based configs in parent directories.
|
|
56
|
+
const projectMcpPath = join(cwd, '.mcp.json');
|
|
57
|
+
if (mergeJsonMcpServer(projectMcpPath, 'ghl-ai', ghlAiServer)) {
|
|
58
|
+
updatedFiles.push(projectMcpPath);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ── Claude Code: .claude/mcp.json (workspace-level) ──
|
|
62
|
+
// Claude Code also reads .claude/mcp.json for workspace-scoped MCP config.
|
|
63
|
+
// Write here so the MCP server is available regardless of which resolution
|
|
64
|
+
// path Claude Code uses (root .mcp.json or .claude/mcp.json).
|
|
65
|
+
const claudeWorkspaceMcpPath = join(cwd, '.claude', 'mcp.json');
|
|
66
|
+
if (mergeJsonMcpServer(claudeWorkspaceMcpPath, 'ghl-ai', ghlAiServer)) {
|
|
67
|
+
updatedFiles.push(claudeWorkspaceMcpPath);
|
|
68
|
+
}
|
|
69
|
+
if (gitJenkinsServer && mergeJsonMcpServer(claudeWorkspaceMcpPath, 'git-jenkins', gitJenkinsServer)) {
|
|
70
|
+
updatedFiles.push(claudeWorkspaceMcpPath);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ── Cursor: project .cursor/mcp.json (workspace-level) ──
|
|
74
|
+
const cursorProjectMcpPath = join(cwd, '.cursor', 'mcp.json');
|
|
75
|
+
if (mergeJsonMcpServer(cursorProjectMcpPath, 'ghl-ai', ghlAiServer)) {
|
|
76
|
+
updatedFiles.push(cursorProjectMcpPath);
|
|
77
|
+
}
|
|
78
|
+
if (gitJenkinsServer && mergeJsonMcpServer(cursorProjectMcpPath, 'git-jenkins', gitJenkinsServer)) {
|
|
79
|
+
updatedFiles.push(cursorProjectMcpPath);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ── Cursor: ~/.cursor/mcp.json (global) ──
|
|
53
83
|
const cursorMcpPath = join(HOME, '.cursor', 'mcp.json');
|
|
54
84
|
if (mergeJsonMcpServer(cursorMcpPath, 'ghl-ai', ghlAiServer)) {
|
|
55
85
|
updatedFiles.push(cursorMcpPath);
|
|
@@ -94,12 +124,18 @@ function mergeJsonMcpServer(filePath, serverName, serverConfig) {
|
|
|
94
124
|
config.mcpServers = {};
|
|
95
125
|
}
|
|
96
126
|
|
|
97
|
-
// Check if already configured with same
|
|
127
|
+
// Check if already configured with same config
|
|
98
128
|
const existing = config.mcpServers[serverName];
|
|
99
129
|
if (existing && JSON.stringify(existing) === JSON.stringify(serverConfig)) {
|
|
100
130
|
return false;
|
|
101
131
|
}
|
|
102
132
|
|
|
133
|
+
// If we're writing an HTTP URL server, remove any stale command-based config
|
|
134
|
+
// to prevent old local MCP servers from shadowing the remote one.
|
|
135
|
+
if (serverConfig.type === 'http' && existing && existing.command) {
|
|
136
|
+
fmt.logStep(`Replacing stale command-based '${serverName}' with HTTP URL`);
|
|
137
|
+
}
|
|
138
|
+
|
|
103
139
|
config.mcpServers[serverName] = serverConfig;
|
|
104
140
|
|
|
105
141
|
mkdirSync(join(filePath, '..'), { recursive: true });
|