@ghl-ai/aw 0.1.63 → 0.1.65

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 CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  readdirSync,
11
11
  copyFileSync,
12
12
  rmSync,
13
+ renameSync,
13
14
  writeFileSync,
14
15
  } from 'node:fs';
15
16
  import { join, dirname, basename } from 'node:path';
@@ -307,16 +308,6 @@ function resolveAwDocsPublishConfig(projectRoot) {
307
308
  };
308
309
  }
309
310
 
310
- function isOnlyUntrackedAwSymlink(status, cloneDir) {
311
- const lines = status.trim().split('\n').filter(Boolean);
312
- if (lines.length !== 1 || !/^\?\?\s+\.aw\/?$/.test(lines[0])) return false;
313
- try {
314
- return lstatSync(join(cloneDir, '.aw')).isSymbolicLink();
315
- } catch {
316
- return false;
317
- }
318
- }
319
-
320
311
  async function removeTrackedAwSymlink(cloneDir) {
321
312
  try {
322
313
  if (!lstatSync(join(cloneDir, '.aw')).isSymbolicLink()) return false;
@@ -340,6 +331,93 @@ async function removeTrackedAwSymlink(cloneDir) {
340
331
  return true;
341
332
  }
342
333
 
334
+ function parseStatusPaths(status) {
335
+ return status
336
+ .split('\n')
337
+ .filter(line => line.trim())
338
+ .flatMap(line => line.slice(3).trim().split(' -> '));
339
+ }
340
+
341
+ function isManagedAwCachePath(path) {
342
+ return path === '.aw' || path.startsWith('.aw/');
343
+ }
344
+
345
+ function isOnlyManagedAwCacheDirtiness(status) {
346
+ const paths = parseStatusPaths(status);
347
+ return paths.length > 0 && paths.every(isManagedAwCachePath);
348
+ }
349
+
350
+ async function hasTrackedAwCachePaths(cloneDir) {
351
+ const { stdout } = await execFile('git', ['ls-files', '--', '.aw'], {
352
+ cwd: cloneDir,
353
+ encoding: 'utf8',
354
+ });
355
+ return stdout.trim().length > 0;
356
+ }
357
+
358
+ async function treeHasAwCachePaths(repoDir, treeish) {
359
+ const { stdout } = await execFile('git', ['ls-tree', '-r', '--name-only', treeish, '--', '.aw'], {
360
+ cwd: repoDir,
361
+ encoding: 'utf8',
362
+ });
363
+ return stdout.trim().length > 0;
364
+ }
365
+
366
+ async function selfHealManagedAwCacheDirtiness(cloneDir, status) {
367
+ if (!isOnlyManagedAwCacheDirtiness(status)) return false;
368
+
369
+ rmSync(join(cloneDir, '.aw'), { recursive: true, force: true });
370
+ await execFile('git', ['clean', '-fd', '--', '.aw'], {
371
+ cwd: cloneDir,
372
+ encoding: 'utf8',
373
+ });
374
+
375
+ if (await hasTrackedAwCachePaths(cloneDir)) {
376
+ await execFile('git', ['restore', '--staged', '--worktree', '--', '.aw'], {
377
+ cwd: cloneDir,
378
+ encoding: 'utf8',
379
+ });
380
+ }
381
+
382
+ return true;
383
+ }
384
+
385
+ function moveLocalAwCachePathAside(cloneDir) {
386
+ const awPath = join(cloneDir, '.aw');
387
+ if (!existsSync(awPath)) return null;
388
+
389
+ const parentDir = dirname(cloneDir);
390
+ const cloneName = basename(cloneDir);
391
+ const stamp = new Date().toISOString().replace(/[^0-9T]/g, '').slice(0, 15);
392
+ let backupPath = join(parentDir, `${cloneName}.untracked-aw-backup-${stamp}`);
393
+ let suffix = 1;
394
+ while (existsSync(backupPath)) {
395
+ backupPath = join(parentDir, `${cloneName}.untracked-aw-backup-${stamp}-${suffix}`);
396
+ suffix += 1;
397
+ }
398
+
399
+ renameSync(awPath, backupPath);
400
+ if (process.env.AW_DEBUG) {
401
+ fmt.logWarn(`Moved ignored AW docs cache .aw path aside: ${backupPath}`);
402
+ }
403
+ return backupPath;
404
+ }
405
+
406
+ async function selfHealIgnoredAwCachePath(cloneDir) {
407
+ const awPath = join(cloneDir, '.aw');
408
+ if (!existsSync(awPath)) return null;
409
+ if (await hasTrackedAwCachePaths(cloneDir)) return null;
410
+ return moveLocalAwCachePathAside(cloneDir);
411
+ }
412
+
413
+ function isCheckoutOverwrittenByAwPathError(error) {
414
+ const text = [error?.message, error?.stderr, error?.stdout]
415
+ .filter(Boolean)
416
+ .join('\n');
417
+ return /untracked working tree files would be overwritten/i.test(text)
418
+ && /(?:^|\s)\.aw(?:\/|$)/m.test(text);
419
+ }
420
+
343
421
  async function getGitStatus(repoDir) {
344
422
  const { stdout } = await execFile('git', ['status', '--porcelain'], {
345
423
  cwd: repoDir,
@@ -397,8 +475,7 @@ async function ensureAwDocsRepoClone(home, publishConfig) {
397
475
  }
398
476
 
399
477
  let status = await getGitStatus(cloneDir);
400
- if (status.trim() && isOnlyUntrackedAwSymlink(status, cloneDir)) {
401
- rmSync(join(cloneDir, '.aw'), { force: true });
478
+ if (status.trim() && await selfHealManagedAwCacheDirtiness(cloneDir, status)) {
402
479
  status = await getGitStatus(cloneDir);
403
480
  }
404
481
  if (status.trim()) {
@@ -419,10 +496,27 @@ async function ensureAwDocsRepoClone(home, publishConfig) {
419
496
  ? `origin/${publishConfig.branch}`
420
497
  : `origin/${publishConfig.seedBranch}`;
421
498
 
422
- await execFile('git', ['checkout', '-B', publishConfig.branch, checkoutStart], {
423
- cwd: cloneDir,
424
- encoding: 'utf8',
425
- });
499
+ if (await treeHasAwCachePaths(cloneDir, checkoutStart)) {
500
+ await selfHealIgnoredAwCachePath(cloneDir);
501
+ }
502
+
503
+ try {
504
+ await execFile('git', ['checkout', '-B', publishConfig.branch, checkoutStart], {
505
+ cwd: cloneDir,
506
+ encoding: 'utf8',
507
+ });
508
+ } catch (e) {
509
+ const healedPath = isCheckoutOverwrittenByAwPathError(e)
510
+ ? await selfHealIgnoredAwCachePath(cloneDir)
511
+ : null;
512
+ if (!healedPath) {
513
+ throw e;
514
+ }
515
+ await execFile('git', ['checkout', '-B', publishConfig.branch, checkoutStart], {
516
+ cwd: cloneDir,
517
+ encoding: 'utf8',
518
+ });
519
+ }
426
520
 
427
521
  if (hasPublishBranch) {
428
522
  await execFile('git', ['pull', '--ff-only', 'origin', publishConfig.branch], {
@@ -501,7 +595,7 @@ function printAwDocsLinks(links, limit = 10) {
501
595
  fmt.logInfo(chalk.bold('Remote Docs'));
502
596
  for (const link of links.slice(0, limit)) {
503
597
  fmt.logInfo(` ${chalk.dim(link.relPath)}`);
504
- fmt.logInfo(` TeamOfOne: ${chalk.cyan(link.remoteUrl)}`);
598
+ fmt.logInfo(` Devtools: ${chalk.cyan(link.remoteUrl)}`);
505
599
  if (link.repositoryUrl && link.repositoryUrl !== link.remoteUrl) {
506
600
  fmt.logInfo(` GitHub: ${chalk.cyan(link.repositoryUrl)}`);
507
601
  }
package/constants.mjs CHANGED
@@ -33,7 +33,7 @@ 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 AW_DOCS_TEAMOFONE_ORIGIN = process.env.AW_DOCS_TEAMOFONE_ORIGIN || 'https://teamofone.msgsndr.net';
36
+ export const AW_DOCS_TEAMOFONE_ORIGIN = process.env.AW_DOCS_TEAMOFONE_ORIGIN || 'https://devtools.servers.stg.msgsndr.net';
37
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`;
38
38
 
39
39
  export function defaultAwDocsGithubDocsConfig() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghl-ai/aw",
3
- "version": "0.1.63",
3
+ "version": "0.1.65",
4
4
  "description": "Agentic Workspace CLI — pull, push & manage agents, skills and commands from the registry",
5
5
  "type": "module",
6
6
  "bin": {