@ghl-ai/aw 0.1.36-beta.25 → 0.1.36-beta.27

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 CHANGED
@@ -92,6 +92,7 @@ function printHelp() {
92
92
 
93
93
  sec('Manage'),
94
94
  cmd('aw status', 'Show synced paths, modified files & conflicts'),
95
+ cmd('aw link', 'Link current project as a git worktree (wires IDE symlinks)'),
95
96
  cmd('aw drop <path>', 'Stop syncing or delete local content'),
96
97
  cmd('aw nuke', 'Remove entire .aw_registry/ & start fresh'),
97
98
  cmd('aw daemon install', 'Auto-pull on a schedule (macOS launchd / Linux cron)'),
package/commands/init.mjs CHANGED
@@ -29,7 +29,7 @@ import {
29
29
  sparseCheckoutAsync,
30
30
  cleanup,
31
31
  } from '../git.mjs';
32
- import { REGISTRY_DIR, REGISTRY_REPO } from '../constants.mjs';
32
+ import { REGISTRY_DIR, REGISTRY_REPO, REGISTRY_URL } from '../constants.mjs';
33
33
 
34
34
  const __dirname = dirname(fileURLToPath(import.meta.url));
35
35
  const VERSION = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8')).version;
@@ -124,7 +124,7 @@ export async function initCommand(args) {
124
124
 
125
125
  // ── Detect installation state ─────────────────────────────────────────
126
126
 
127
- const repoUrl = `https://github.com/${REGISTRY_REPO}.git`;
127
+ const repoUrl = REGISTRY_URL;
128
128
  const isGitNative = isValidClone(AW_HOME, repoUrl);
129
129
  const isLegacy = !isGitNative && existsSync(GLOBAL_AW_DIR) && !lstatSync(GLOBAL_AW_DIR).isSymbolicLink();
130
130
 
@@ -309,8 +309,8 @@ export async function initCommand(args) {
309
309
  await installAwEcc(cwd, { silent });
310
310
  const instructionFiles = copyInstructions(HOME, null, team) || [];
311
311
  initAwDocs(HOME);
312
- const mcpFiles = await setupMcp(HOME, team) || [];
313
- if (cwd !== HOME) await setupMcp(cwd, team);
312
+ const mcpFiles = await setupMcp(HOME, team, { silent }) || [];
313
+ if (cwd !== HOME) await setupMcp(cwd, team, { silent });
314
314
  const hooksInstalled = installGlobalHooks();
315
315
  installIdeTasks();
316
316
 
@@ -6,7 +6,7 @@ import { homedir } from 'node:os';
6
6
  import * as fmt from '../fmt.mjs';
7
7
  import { chalk } from '../fmt.mjs';
8
8
  import { addProjectWorktree, isWorktree, isValidClone } from '../git.mjs';
9
- import { REGISTRY_DIR, REGISTRY_REPO } from '../constants.mjs';
9
+ import { REGISTRY_DIR, REGISTRY_URL } from '../constants.mjs';
10
10
  import { linkWorkspace } from '../link.mjs';
11
11
  import { generateCommands } from '../integrate.mjs';
12
12
 
@@ -18,8 +18,7 @@ export function linkProjectCommand(args) {
18
18
 
19
19
  fmt.intro('aw link');
20
20
 
21
- const repoUrl = `https://github.com/${REGISTRY_REPO}.git`;
22
- if (!isValidClone(AW_HOME, repoUrl)) {
21
+ if (!isValidClone(AW_HOME, REGISTRY_URL)) {
23
22
  fmt.cancel('Registry not initialized. Run: aw init');
24
23
  return;
25
24
  }
package/commands/pull.mjs CHANGED
@@ -8,7 +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 { fetchAndMerge, addToSparseCheckout, isValidClone, isWorktree, rebaseOntoOriginMain } from '../git.mjs';
11
- import { REGISTRY_DIR, REGISTRY_REPO, DOCS_SOURCE_DIR } from '../constants.mjs';
11
+ import { REGISTRY_DIR, REGISTRY_REPO, REGISTRY_URL, DOCS_SOURCE_DIR } from '../constants.mjs';
12
12
  import { linkWorkspace } from '../link.mjs';
13
13
  import { generateCommands, copyInstructions } from '../integrate.mjs';
14
14
 
@@ -33,7 +33,7 @@ export async function pullCommand(args) {
33
33
  spinner: silent ? () => ({ start: () => {}, stop: () => {} }) : fmt.spinner,
34
34
  };
35
35
 
36
- const repoUrl = `https://github.com/${REGISTRY_REPO}.git`;
36
+ const repoUrl = REGISTRY_URL;
37
37
  const hasClone = isValidClone(AW_HOME, repoUrl);
38
38
 
39
39
  if (!hasClone) {
package/commands/push.mjs CHANGED
@@ -6,7 +6,7 @@ import { execSync, execFileSync } from 'node:child_process';
6
6
  import { homedir } 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, REGISTRY_DIR } from '../constants.mjs';
9
+ import { REGISTRY_REPO, REGISTRY_URL, REGISTRY_BASE_BRANCH, REGISTRY_DIR } from '../constants.mjs';
10
10
  import { resolveInput } from '../paths.mjs';
11
11
  import { walkRegistryTree } from '../registry.mjs';
12
12
  import {
@@ -426,7 +426,7 @@ export function pushCommand(args) {
426
426
 
427
427
  fmt.intro('aw push');
428
428
 
429
- const repoUrl = `https://github.com/${REGISTRY_REPO}.git`;
429
+ const repoUrl = REGISTRY_URL;
430
430
  if (!hasWorktree && !isValidClone(awHome, repoUrl)) {
431
431
  fmt.cancel('Registry not initialized. Run: aw init');
432
432
  return;
@@ -475,7 +475,6 @@ export function pushCommand(args) {
475
475
  }
476
476
 
477
477
  const files = allEntries
478
- .filter(f => parseRegistryPath(f.registryPath) !== null)
479
478
  .map(f => {
480
479
  const meta = parseRegistryPath(f.registryPath);
481
480
  const parts = f.registryPath.split('/');
@@ -5,8 +5,8 @@ import { homedir } from 'node:os';
5
5
  import * as config from '../config.mjs';
6
6
  import * as fmt from '../fmt.mjs';
7
7
  import { chalk } from '../fmt.mjs';
8
- import { detectChanges, getCurrentBranch, isValidClone, isWorktree } from '../git.mjs';
9
- import { REGISTRY_DIR, REGISTRY_REPO } from '../constants.mjs';
8
+ import { detectChanges, getCurrentBranch, commitsAheadOfMain, isValidClone, isWorktree } from '../git.mjs';
9
+ import { REGISTRY_DIR, REGISTRY_REPO, REGISTRY_URL } from '../constants.mjs';
10
10
 
11
11
  export function statusCommand(args) {
12
12
  const HOME = homedir();
@@ -20,8 +20,7 @@ export function statusCommand(args) {
20
20
 
21
21
  fmt.intro('aw status');
22
22
 
23
- const repoUrl = `https://github.com/${REGISTRY_REPO}.git`;
24
- if (!isWorktree(localAw) && !isValidClone(AW_HOME, repoUrl)) {
23
+ if (!isWorktree(localAw) && !isValidClone(AW_HOME, REGISTRY_URL)) {
25
24
  fmt.cancel('Registry not initialized. Run: aw init');
26
25
  return;
27
26
  }
@@ -93,7 +92,9 @@ export function statusCommand(args) {
93
92
  }
94
93
 
95
94
  if (!isOnMain) {
96
- fmt.logWarn(`On branch ${chalk.yellow(branch)} — run ${chalk.dim('aw pull')} to sync or checkout main first`);
95
+ const ahead = commitsAheadOfMain(AW_HOME);
96
+ const aheadStr = ahead > 0 ? ` (${chalk.yellow(`${ahead} commit${ahead !== 1 ? 's' : ''} ahead`)})` : '';
97
+ fmt.logWarn(`On branch ${chalk.yellow(branch)}${aheadStr} — run ${chalk.dim('aw push')} to open a PR or ${chalk.dim('aw pull')} to sync`);
97
98
  }
98
99
 
99
100
  // Hints
package/constants.mjs CHANGED
@@ -9,6 +9,13 @@ export const REGISTRY_BASE_BRANCH = 'main';
9
9
  /** Default registry repository */
10
10
  export const REGISTRY_REPO = 'GoHighLevel/platform-docs';
11
11
 
12
+ /**
13
+ * Full registry clone URL.
14
+ * Tests override via AW_REGISTRY_URL env var (file:// bare repo).
15
+ */
16
+ export const REGISTRY_URL = process.env.AW_REGISTRY_URL
17
+ || `https://github.com/${REGISTRY_REPO}.git`;
18
+
12
19
  /** Directory inside the registry repo that holds platform/ and [template]/ */
13
20
  export const REGISTRY_DIR = '.aw_registry';
14
21
 
package/ecc.mjs CHANGED
@@ -61,6 +61,7 @@ export async function installAwEcc(
61
61
  cwd,
62
62
  { targets = ["cursor", "claude", "codex"], silent = false } = {},
63
63
  ) {
64
+ if (process.env.AW_NO_ECC === '1') return;
64
65
  if (!silent) fmt.logStep("Installing aw-ecc engine...");
65
66
 
66
67
  const repoDir = eccDir();
package/hooks.mjs CHANGED
@@ -111,6 +111,10 @@ exit 0
111
111
  * @returns {boolean} true if hooks were installed
112
112
  */
113
113
  export function installGlobalHooks() {
114
+ // AW_NO_HOOKS=1 lets test environments skip hook installation to prevent
115
+ // recursive aw init calls during git commits in tests.
116
+ if (process.env.AW_NO_HOOKS === '1') return false;
117
+
114
118
  try {
115
119
  mkdirSync(HOOKS_DIR, { recursive: true });
116
120
 
package/link.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  // link.mjs — Create symlinks from IDE dirs → .aw_registry/
2
2
 
3
- import { existsSync, lstatSync, mkdirSync, readdirSync, unlinkSync, symlinkSync, rmdirSync } from 'node:fs';
3
+ import { existsSync, lstatSync, mkdirSync, readdirSync, unlinkSync, symlinkSync, rmdirSync, realpathSync } from 'node:fs';
4
4
  import { join, relative } from 'node:path';
5
5
  import { homedir } from 'node:os';
6
6
  import * as fmt from './fmt.mjs';
@@ -130,8 +130,13 @@ function flatName(ns, name) {
130
130
  * @param {string|null} awDirOverride - Explicit registry dir; overrides auto-detection
131
131
  */
132
132
  export function linkWorkspace(cwd, awDirOverride = null) {
133
+ // Normalize cwd to real path so relative() produces valid symlink targets on macOS
134
+ // where $HOME may be /var/... but process.cwd() resolves to /private/var/...
135
+ try { cwd = realpathSync(cwd); } catch { /* use as-is */ }
136
+
133
137
  const GLOBAL_AW_DIR = join(homedir(), '.aw_registry');
134
- const awDir = awDirOverride || getLocalRegistryDir(cwd, GLOBAL_AW_DIR);
138
+ let awDir = awDirOverride || getLocalRegistryDir(cwd, GLOBAL_AW_DIR);
139
+ try { awDir = realpathSync(awDir); } catch { /* use as-is if it doesn't exist */ }
135
140
  if (!existsSync(awDir)) return 0;
136
141
 
137
142
  let created = 0;
package/package.json CHANGED
@@ -1,11 +1,9 @@
1
1
  {
2
2
  "name": "@ghl-ai/aw",
3
- "version": "0.1.36-beta.25",
3
+ "version": "0.1.36-beta.27",
4
4
  "description": "Agentic Workspace CLI — pull, push & manage agents, skills and commands from the registry",
5
5
  "type": "module",
6
- "bin": {
7
- "aw": "bin.js"
8
- },
6
+ "bin": "bin.js",
9
7
  "files": [
10
8
  "bin.js",
11
9
  "cli.mjs",
@@ -40,6 +38,8 @@
40
38
  "author": "GoHighLevel",
41
39
  "license": "MIT",
42
40
  "scripts": {
41
+ "test": "vitest run --reporter=verbose",
42
+ "test:watch": "vitest --reporter=verbose",
43
43
  "preuninstall": "node bin.js nuke 2>/dev/null || true"
44
44
  },
45
45
  "publishConfig": {
@@ -49,5 +49,8 @@
49
49
  "@clack/prompts": "0.8.2",
50
50
  "chalk": "^5.6.2",
51
51
  "figlet": "^1.11.0"
52
+ },
53
+ "devDependencies": {
54
+ "vitest": "^4.1.2"
52
55
  }
53
56
  }