@ghl-ai/aw 0.1.48-beta.0 → 0.1.48-beta.2
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 +4 -0
- package/commands/push.mjs +109 -26
- package/constants.mjs +2 -1
- package/ecc.mjs +1 -1
- package/integrations.mjs +23 -6
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -50,6 +50,9 @@ function parseArgs(argv) {
|
|
|
50
50
|
if (arg === '--dry-run') {
|
|
51
51
|
args['--dry-run'] = true;
|
|
52
52
|
i++;
|
|
53
|
+
} else if (arg === '--aw-docs-only' || arg === '--docs-only') {
|
|
54
|
+
args[arg] = true;
|
|
55
|
+
i++;
|
|
53
56
|
} else if (arg === '-v' || arg === '--verbose') {
|
|
54
57
|
args['-v'] = true;
|
|
55
58
|
i++;
|
|
@@ -99,6 +102,7 @@ function printHelp() {
|
|
|
99
102
|
sec('Upload'),
|
|
100
103
|
cmd('aw push', 'Push all modified files (creates one PR)'),
|
|
101
104
|
cmd('aw push --aw-docs-only', 'Publish generated .aw_docs companions and print share links'),
|
|
105
|
+
cmd('aw push --aw-docs-only --feature <slug>', 'Publish one .aw_docs feature folder and print share links'),
|
|
102
106
|
cmd('aw push <path>', 'Push file, folder, or namespace to registry'),
|
|
103
107
|
cmd('aw push-rules [path]', 'Push platform rules to platform-docs'),
|
|
104
108
|
cmd('aw push --dry-run [path]', 'Preview what would be pushed'),
|
package/commands/push.mjs
CHANGED
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
AW_DOCS_SEED_BRANCH,
|
|
37
37
|
AW_DOCS_PUBLISH_DIR,
|
|
38
38
|
AW_DOCS_PUBLIC_BASE_URL,
|
|
39
|
+
AW_DOCS_TEAMOFONE_ORIGIN,
|
|
39
40
|
AW_DOCS_TEAMOFONE_BASE_URL,
|
|
40
41
|
AW_CO_AUTHOR,
|
|
41
42
|
defaultAwDocsGithubDocsConfig,
|
|
@@ -143,13 +144,71 @@ function collectFiles(root, base = root) {
|
|
|
143
144
|
return files;
|
|
144
145
|
}
|
|
145
146
|
|
|
146
|
-
function
|
|
147
|
+
function normalizeRelPath(value) {
|
|
148
|
+
return String(value || '')
|
|
149
|
+
.trim()
|
|
150
|
+
.replace(/\\/g, '/')
|
|
151
|
+
.replace(/^\.\//, '')
|
|
152
|
+
.replace(/\/+$/, '');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function featureScopeFromInput(input) {
|
|
156
|
+
const value = normalizeRelPath(input);
|
|
157
|
+
if (!value) return null;
|
|
158
|
+
|
|
159
|
+
const match = value.match(/^(?:\.aw_docs\/)?features\/([^/]+)$/);
|
|
160
|
+
if (!match) {
|
|
161
|
+
throw new Error('Docs-only publish path must be .aw_docs/features/<feature-slug> or use --feature <feature-slug>.');
|
|
162
|
+
}
|
|
163
|
+
return awDocsFeatureScope(match[1]);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function awDocsFeatureScope(featureSlug) {
|
|
167
|
+
const slug = String(featureSlug || '').trim();
|
|
168
|
+
if (!slug || slug === 'true') {
|
|
169
|
+
throw new Error('Missing feature slug. Use: aw push --aw-docs-only --feature <feature-slug>');
|
|
170
|
+
}
|
|
171
|
+
if (!/^[A-Za-z0-9._-]+$/.test(slug)) {
|
|
172
|
+
throw new Error(`Invalid feature slug "${slug}". Feature slugs may contain letters, numbers, dot, underscore, and dash only.`);
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
type: 'feature',
|
|
176
|
+
slug,
|
|
177
|
+
relPrefix: `features/${slug}`,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function resolveAwDocsScope(input, featureFlag) {
|
|
182
|
+
const inputScope = featureScopeFromInput(input);
|
|
183
|
+
const flagScope = featureFlag ? awDocsFeatureScope(featureFlag) : null;
|
|
184
|
+
if (inputScope && flagScope && inputScope.relPrefix !== flagScope.relPrefix) {
|
|
185
|
+
throw new Error(`Docs-only publish received conflicting scopes: ${inputScope.relPrefix} and ${flagScope.relPrefix}.`);
|
|
186
|
+
}
|
|
187
|
+
return flagScope || inputScope;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function collectProjectAwDocs(cwd, home, scope = null) {
|
|
147
191
|
const projectRoot = getProjectRoot(cwd, home);
|
|
148
192
|
const source = join(projectRoot, AW_DOCS_DIR);
|
|
149
193
|
|
|
150
194
|
if (!existsSync(source)) return { projectRoot, files: [] };
|
|
151
195
|
|
|
152
196
|
const files = [];
|
|
197
|
+
if (scope) {
|
|
198
|
+
const sourceRoot = join(source, scope.relPrefix);
|
|
199
|
+
if (!existsSync(sourceRoot) || !statSync(sourceRoot).isDirectory()) {
|
|
200
|
+
throw new Error(`No publishable AW docs found under ${AW_DOCS_DIR}/${scope.relPrefix}.`);
|
|
201
|
+
}
|
|
202
|
+
for (const relFromRoot of collectFiles(sourceRoot)) {
|
|
203
|
+
const relPath = `${scope.relPrefix}/${relFromRoot}`.replace(/\\/g, '/');
|
|
204
|
+
files.push({
|
|
205
|
+
relPath,
|
|
206
|
+
absPath: join(source, relPath),
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
return { projectRoot, files };
|
|
210
|
+
}
|
|
211
|
+
|
|
153
212
|
for (const root of PROJECT_AW_DOCS_AUTO_ROOTS) {
|
|
154
213
|
const sourceRoot = join(source, root);
|
|
155
214
|
if (!existsSync(sourceRoot)) continue;
|
|
@@ -220,11 +279,11 @@ function resolveAwDocsPublishConfig(projectRoot) {
|
|
|
220
279
|
const teamofoneBaseUrl = String(
|
|
221
280
|
process.env.AW_DOCS_TEAMOFONE_BASE_URL
|
|
222
281
|
|| githubDocs.teamofone_base_url
|
|
223
|
-
|| AW_DOCS_TEAMOFONE_BASE_URL
|
|
224
|
-
|| '',
|
|
282
|
+
|| AW_DOCS_TEAMOFONE_BASE_URL,
|
|
225
283
|
).trim();
|
|
226
284
|
const publicBaseUrl = process.env.AW_DOCS_PUBLIC_BASE_URL
|
|
227
285
|
|| githubDocs.public_base_url
|
|
286
|
+
|| AW_DOCS_PUBLIC_BASE_URL
|
|
228
287
|
|| `https://github.com/${String(repo).replace(/\.git$/, '')}/blob/${branch}`;
|
|
229
288
|
|
|
230
289
|
return {
|
|
@@ -234,7 +293,7 @@ function resolveAwDocsPublishConfig(projectRoot) {
|
|
|
234
293
|
branch,
|
|
235
294
|
seedBranch: process.env.AW_DOCS_SEED_BRANCH || githubDocs.seed_branch || AW_DOCS_SEED_BRANCH,
|
|
236
295
|
dest,
|
|
237
|
-
teamofoneBaseUrl,
|
|
296
|
+
teamofoneBaseUrl: normalizeTeamOfOneBaseUrl(teamofoneBaseUrl),
|
|
238
297
|
publicBaseUrl,
|
|
239
298
|
};
|
|
240
299
|
}
|
|
@@ -389,6 +448,14 @@ function appendPathToUrl(baseUrl, path) {
|
|
|
389
448
|
return `${basePath.replace(/\/$/, '')}/${encodedPath}${query}${hash}`;
|
|
390
449
|
}
|
|
391
450
|
|
|
451
|
+
function normalizeTeamOfOneBaseUrl(baseUrl) {
|
|
452
|
+
const value = String(baseUrl || '').trim();
|
|
453
|
+
if (!value) return '';
|
|
454
|
+
if (/^https?:\/\//i.test(value)) return value;
|
|
455
|
+
if (value.startsWith('/')) return `${AW_DOCS_TEAMOFONE_ORIGIN.replace(/\/$/, '')}${value}`;
|
|
456
|
+
return `https://${value.replace(/^\/+/, '')}`;
|
|
457
|
+
}
|
|
458
|
+
|
|
392
459
|
function appendQueryParam(url, key, value) {
|
|
393
460
|
const hashIndex = url.indexOf('#');
|
|
394
461
|
const withoutHash = hashIndex === -1 ? url : url.slice(0, hashIndex);
|
|
@@ -422,8 +489,13 @@ function awDocsRepositoryUrl(publishedPath, publishConfig) {
|
|
|
422
489
|
}
|
|
423
490
|
|
|
424
491
|
function printAwDocsLinks(links, limit = 10) {
|
|
492
|
+
fmt.logInfo(chalk.bold('Remote Docs'));
|
|
425
493
|
for (const link of links.slice(0, limit)) {
|
|
426
|
-
fmt.logInfo(chalk.
|
|
494
|
+
fmt.logInfo(` ${chalk.dim(link.relPath)}`);
|
|
495
|
+
fmt.logInfo(` TeamOfOne: ${chalk.cyan(link.remoteUrl)}`);
|
|
496
|
+
if (link.repositoryUrl && link.repositoryUrl !== link.remoteUrl) {
|
|
497
|
+
fmt.logInfo(` GitHub: ${chalk.cyan(link.repositoryUrl)}`);
|
|
498
|
+
}
|
|
427
499
|
}
|
|
428
500
|
if (links.length > limit) {
|
|
429
501
|
fmt.logInfo(chalk.dim(`...and ${links.length - limit} more`));
|
|
@@ -466,7 +538,7 @@ function titleForAwDoc(relPath) {
|
|
|
466
538
|
return `${label} ${ext}`;
|
|
467
539
|
}
|
|
468
540
|
|
|
469
|
-
function updateAwDocsManifest(docsRepoDir, { repoSlug, sourceRepo, githubUsername, docs, publishConfig }) {
|
|
541
|
+
function updateAwDocsManifest(docsRepoDir, { repoSlug, sourceRepo, githubUsername, docs, publishConfig, scope = null }) {
|
|
470
542
|
const manifestPath = join(docsRepoDir, 'manifest.json');
|
|
471
543
|
const manifest = existsSync(manifestPath)
|
|
472
544
|
? JSON.parse(readFileSync(manifestPath, 'utf8'))
|
|
@@ -480,25 +552,32 @@ function updateAwDocsManifest(docsRepoDir, { repoSlug, sourceRepo, githubUsernam
|
|
|
480
552
|
repoEntry.slug = repoSlug;
|
|
481
553
|
repoEntry.sourceRepo = sourceRepo;
|
|
482
554
|
repoEntry.users ||= {};
|
|
555
|
+
const existingUser = repoEntry.users[githubUsername] || {};
|
|
556
|
+
const nextDocs = docs.map(doc => ({
|
|
557
|
+
relPath: doc.relPath,
|
|
558
|
+
publishedPath: doc.publishedPath,
|
|
559
|
+
remoteUrl: awDocsRemoteUrl(doc.publishedPath, publishConfig),
|
|
560
|
+
repositoryUrl: awDocsRepositoryUrl(doc.publishedPath, publishConfig),
|
|
561
|
+
sha: createHash('sha256').update(readFileSync(join(docsRepoDir, doc.publishedPath))).digest('hex'),
|
|
562
|
+
syncedAt: now,
|
|
563
|
+
title: titleForAwDoc(doc.relPath),
|
|
564
|
+
}));
|
|
565
|
+
const preservedDocs = scope?.relPrefix
|
|
566
|
+
? (existingUser.docs || []).filter(doc => !String(doc.relPath || '').startsWith(`${scope.relPrefix}/`))
|
|
567
|
+
: [];
|
|
568
|
+
|
|
483
569
|
repoEntry.users[githubUsername] = {
|
|
570
|
+
...existingUser,
|
|
484
571
|
githubUsername,
|
|
485
|
-
docs:
|
|
486
|
-
relPath: doc.relPath,
|
|
487
|
-
publishedPath: doc.publishedPath,
|
|
488
|
-
remoteUrl: awDocsRemoteUrl(doc.publishedPath, publishConfig),
|
|
489
|
-
repositoryUrl: awDocsRepositoryUrl(doc.publishedPath, publishConfig),
|
|
490
|
-
sha: createHash('sha256').update(readFileSync(join(docsRepoDir, doc.publishedPath))).digest('hex'),
|
|
491
|
-
syncedAt: now,
|
|
492
|
-
title: titleForAwDoc(doc.relPath),
|
|
493
|
-
})),
|
|
572
|
+
docs: scope?.relPrefix ? [...preservedDocs, ...nextDocs] : nextDocs,
|
|
494
573
|
};
|
|
495
574
|
manifest.awDocs.repos[repoSlug] = repoEntry;
|
|
496
575
|
|
|
497
576
|
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
498
577
|
}
|
|
499
578
|
|
|
500
|
-
async function publishProjectAwDocs(cwd, home, dryRun) {
|
|
501
|
-
const { projectRoot, files } = collectProjectAwDocs(cwd, home);
|
|
579
|
+
async function publishProjectAwDocs(cwd, home, dryRun, scope = null) {
|
|
580
|
+
const { projectRoot, files } = collectProjectAwDocs(cwd, home, scope);
|
|
502
581
|
if (files.length === 0) return { hasDocs: false, publishedPaths: [] };
|
|
503
582
|
|
|
504
583
|
const publishConfig = resolveAwDocsPublishConfig(projectRoot);
|
|
@@ -537,7 +616,10 @@ async function publishProjectAwDocs(cwd, home, dryRun) {
|
|
|
537
616
|
s.start(`Publishing ${files.length} AW doc${files.length > 1 ? 's' : ''} to ${publishConfig.repo}...`);
|
|
538
617
|
try {
|
|
539
618
|
const docsRepoDir = await ensureAwDocsRepoClone(home, publishConfig);
|
|
540
|
-
|
|
619
|
+
const deleteTarget = scope?.relPrefix
|
|
620
|
+
? join(docsRepoDir, publishConfig.dest, repoSlug, githubUsername, scope.relPrefix)
|
|
621
|
+
: join(docsRepoDir, publishConfig.dest, repoSlug, githubUsername);
|
|
622
|
+
rmSync(deleteTarget, {
|
|
541
623
|
recursive: true,
|
|
542
624
|
force: true,
|
|
543
625
|
});
|
|
@@ -546,7 +628,7 @@ async function publishProjectAwDocs(cwd, home, dryRun) {
|
|
|
546
628
|
mkdirSync(dirname(dest), { recursive: true });
|
|
547
629
|
copyFileSync(doc.absPath, dest);
|
|
548
630
|
}
|
|
549
|
-
updateAwDocsManifest(docsRepoDir, { repoSlug, sourceRepo, githubUsername, docs, publishConfig });
|
|
631
|
+
updateAwDocsManifest(docsRepoDir, { repoSlug, sourceRepo, githubUsername, docs, publishConfig, scope });
|
|
550
632
|
|
|
551
633
|
const { stdout: status } = await execFile('git', ['status', '--porcelain'], {
|
|
552
634
|
cwd: docsRepoDir,
|
|
@@ -560,7 +642,9 @@ async function publishProjectAwDocs(cwd, home, dryRun) {
|
|
|
560
642
|
}
|
|
561
643
|
|
|
562
644
|
await commitAndPushAwDocsRepo(docsRepoDir, {
|
|
563
|
-
message:
|
|
645
|
+
message: scope?.relPrefix
|
|
646
|
+
? `docs(aw): sync ${repoSlug}/${githubUsername} ${scope.relPrefix}`
|
|
647
|
+
: `docs(aw): sync ${repoSlug}/${githubUsername} AW docs`,
|
|
564
648
|
branch: publishConfig.branch,
|
|
565
649
|
});
|
|
566
650
|
writeAwDocsLinkSummary(projectRoot, links, publishConfig);
|
|
@@ -1060,14 +1144,13 @@ export async function pushCommand(args) {
|
|
|
1060
1144
|
}
|
|
1061
1145
|
|
|
1062
1146
|
if (docsOnly) {
|
|
1063
|
-
if (input) {
|
|
1064
|
-
fmt.cancel('Docs-only publish does not accept a path. Run: aw push --aw-docs-only');
|
|
1065
|
-
return;
|
|
1066
|
-
}
|
|
1067
1147
|
try {
|
|
1068
|
-
const
|
|
1148
|
+
const scope = resolveAwDocsScope(input, args['--feature']);
|
|
1149
|
+
const result = await publishProjectAwDocs(cwd, HOME, dryRun, scope);
|
|
1069
1150
|
if (!result.hasDocs) {
|
|
1070
|
-
fmt.cancel(
|
|
1151
|
+
fmt.cancel(scope
|
|
1152
|
+
? `No publishable AW docs found under ${AW_DOCS_DIR}/${scope.relPrefix}.`
|
|
1153
|
+
: 'No publishable AW docs found under .aw_docs/features or .aw_docs/html.');
|
|
1071
1154
|
return;
|
|
1072
1155
|
}
|
|
1073
1156
|
if (dryRun) fmt.outro(chalk.dim('Remove --dry-run to publish AW docs'));
|
package/constants.mjs
CHANGED
|
@@ -33,7 +33,8 @@ export const AW_DOCS_BASE_BRANCH = 'master-sync';
|
|
|
33
33
|
export const AW_DOCS_SEED_BRANCH = process.env.AW_DOCS_SEED_BRANCH || 'scaffold';
|
|
34
34
|
export const AW_DOCS_PUBLISH_DIR = 'aw_docs';
|
|
35
35
|
export const AW_DOCS_PUBLIC_BASE_URL = process.env.AW_DOCS_PUBLIC_BASE_URL || `https://github.com/${AW_DOCS_REPO}/blob/${AW_DOCS_BASE_BRANCH}`;
|
|
36
|
-
export const
|
|
36
|
+
export const AW_DOCS_TEAMOFONE_ORIGIN = process.env.AW_DOCS_TEAMOFONE_ORIGIN || 'https://teamofone.msgsndr.net';
|
|
37
|
+
export const AW_DOCS_TEAMOFONE_BASE_URL = process.env.AW_DOCS_TEAMOFONE_BASE_URL || `${AW_DOCS_TEAMOFONE_ORIGIN}/too/docs/GoHighLevel/ghl-aw-docs`;
|
|
37
38
|
|
|
38
39
|
export function defaultAwDocsGithubDocsConfig() {
|
|
39
40
|
return {
|
package/ecc.mjs
CHANGED
|
@@ -12,7 +12,7 @@ import { applyStoredStartupPreferences } from "./startup.mjs";
|
|
|
12
12
|
|
|
13
13
|
const AW_ECC_REPO_SSH = "git@github.com:shreyansh-ghl/aw-ecc.git";
|
|
14
14
|
const AW_ECC_REPO_HTTPS = "https://github.com/shreyansh-ghl/aw-ecc.git";
|
|
15
|
-
export const AW_ECC_TAG = "v1.4.
|
|
15
|
+
export const AW_ECC_TAG = "v1.4.55";
|
|
16
16
|
|
|
17
17
|
const MARKETPLACE_NAME = "aw-marketplace";
|
|
18
18
|
const PLUGIN_KEY = `aw@${MARKETPLACE_NAME}`;
|
package/integrations.mjs
CHANGED
|
@@ -45,6 +45,8 @@ export const INTEGRATIONS = {
|
|
|
45
45
|
type: 'universal-installer',
|
|
46
46
|
label: 'LeanCTX',
|
|
47
47
|
description: 'Context OS for AI — compresses file reads + shell output + memory (60-99% fewer input tokens)',
|
|
48
|
+
autoInstall: false, // opt-in only — install via: aw integrations add lean-ctx
|
|
49
|
+
uninstallCmd: 'lean-ctx uninstall', // runs on: aw integrations remove lean-ctx
|
|
48
50
|
scripts: {
|
|
49
51
|
win32: null, // no PS1 — uses npm fallback (lean-ctx-bin)
|
|
50
52
|
posix: 'https://leanctx.com/install.sh',
|
|
@@ -773,12 +775,25 @@ export async function removeIntegration(key, { silent = false } = {}) {
|
|
|
773
775
|
);
|
|
774
776
|
}
|
|
775
777
|
} else if (integration.type === 'universal-installer') {
|
|
776
|
-
if (
|
|
777
|
-
fmt.
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
778
|
+
if (integration.uninstallCmd) {
|
|
779
|
+
if (!silent) fmt.logStep(`Running ${integration.uninstallCmd}...`);
|
|
780
|
+
try {
|
|
781
|
+
execSync(integration.uninstallCmd, { stdio: silent ? 'ignore' : 'inherit' });
|
|
782
|
+
if (!silent) fmt.logSuccess(`${integration.label} uninstalled`);
|
|
783
|
+
} catch {
|
|
784
|
+
if (!silent) fmt.logWarn(
|
|
785
|
+
`${integration.uninstallCmd} failed or not found. Manual cleanup may be needed.`,
|
|
786
|
+
'Uninstall Warning',
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
} else {
|
|
790
|
+
if (!silent) {
|
|
791
|
+
fmt.logWarn(
|
|
792
|
+
`Removed ${integration.label} from the aw manifest.\n` +
|
|
793
|
+
` To fully uninstall: follow the uninstall instructions for ${integration.label} in its documentation.`,
|
|
794
|
+
'Manual Cleanup',
|
|
795
|
+
);
|
|
796
|
+
}
|
|
782
797
|
}
|
|
783
798
|
}
|
|
784
799
|
|
|
@@ -816,6 +831,8 @@ export function suggestForTeam(namespace) {
|
|
|
816
831
|
|
|
817
832
|
return Object.entries(INTEGRATIONS)
|
|
818
833
|
.filter(([, integration]) => {
|
|
834
|
+
// Skip opt-in only integrations (autoInstall: false)
|
|
835
|
+
if (integration.autoInstall === false) return false;
|
|
819
836
|
// Show if: team is in the integration's teams list OR teams list is empty (universal)
|
|
820
837
|
return (
|
|
821
838
|
integration.teams.length === 0 || integration.teams.includes(team)
|