@ghl-ai/aw 0.1.37-beta.53 → 0.1.37-beta.55

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
@@ -4,8 +4,9 @@ import { readFileSync } from 'node:fs';
4
4
  import { join, dirname } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import * as fmt from './fmt.mjs';
7
- import { chalk } from './fmt.mjs';
7
+ import { chalk, CancelError } from './fmt.mjs';
8
8
  import { checkForUpdate, notifyUpdate } from './update.mjs';
9
+ import { startSpan } from './telemetry.mjs';
9
10
 
10
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
11
12
  const VERSION = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf8')).version;
@@ -17,12 +18,12 @@ const COMMANDS = {
17
18
  'push-rules': () => import('./commands/push-rules.mjs').then(m => m.pushRulesCommand),
18
19
  drop: () => import('./commands/drop.mjs').then(m => m.dropCommand),
19
20
  status: () => import('./commands/status.mjs').then(m => m.statusCommand),
20
- routing: () => import('./commands/startup.mjs').then(m => m.routingCommand),
21
- startup: () => import('./commands/startup.mjs').then(m => m.startupCommand),
22
21
  search: () => import('./commands/search.mjs').then(m => m.searchCommand),
23
22
  link: () => import('./commands/link-project.mjs').then(m => m.linkProjectCommand),
24
23
  nuke: () => import('./commands/nuke.mjs').then(m => m.nukeCommand),
25
24
  daemon: () => import('./commands/daemon.mjs').then(m => m.daemonCommand),
25
+ telemetry: () => import('./commands/telemetry.mjs').then(m => m.telemetryCommand),
26
+ memory: () => import('./commands/memory.mjs').then(m => m.memoryCommand),
26
27
  };
27
28
 
28
29
  function parseArgs(argv) {
@@ -98,9 +99,6 @@ function printHelp() {
98
99
  sec('Manage'),
99
100
  cmd('aw status', 'Show synced paths, modified files & conflicts'),
100
101
  cmd('aw link', 'Link current project as a git worktree (wires IDE symlinks)'),
101
- cmd('aw routing status', 'Show global AW session-routing mode for Claude/Cursor/Codex'),
102
- cmd('aw routing disable', 'Disable automatic AW session routing globally'),
103
- cmd('aw routing enable', 'Re-enable automatic AW session routing globally'),
104
102
  cmd('aw drop <path>', 'Stop syncing or delete local content'),
105
103
  cmd('aw nuke', 'Remove entire .aw_registry/ & start fresh'),
106
104
  cmd('aw daemon install', 'Auto-pull on a schedule (macOS launchd / Linux cron)'),
@@ -108,6 +106,17 @@ function printHelp() {
108
106
  cmd('aw daemon uninstall', 'Stop the background daemon'),
109
107
  cmd('aw daemon status', 'Check if daemon is running'),
110
108
 
109
+ sec('Memory'),
110
+ cmd('aw memory store <content>', 'Store a memory (curated)'),
111
+ cmd('aw memory search <query>', 'Search memories'),
112
+ cmd('aw memory pack <query>', 'Get a memory pack (token-budgeted)'),
113
+ cmd('aw memory stats', 'Show memory statistics'),
114
+
115
+ sec('Settings'),
116
+ cmd('aw telemetry status', 'Show telemetry status'),
117
+ cmd('aw telemetry disable', 'Opt out of anonymous analytics'),
118
+ cmd('aw telemetry enable', 'Re-enable analytics'),
119
+
111
120
  sec('Examples'),
112
121
  '',
113
122
  ` ${chalk.dim('# Pull content from registry using path')}`,
@@ -156,9 +165,21 @@ export async function run(argv) {
156
165
  }
157
166
 
158
167
  if (command && COMMANDS[command]) {
168
+ const span = await startSpan(command, args);
169
+ span.notice();
159
170
  args._updateCheck = updateCheck;
160
- const handler = await COMMANDS[command]();
161
- await handler(args);
171
+ try {
172
+ const handler = await COMMANDS[command]();
173
+ await handler(args);
174
+ await span.end({ status: 'completed' });
175
+ } catch (err) {
176
+ if (err instanceof CancelError) {
177
+ await span.end({ status: 'cancelled', error_type: 'CancelError' });
178
+ process.exit(err.exitCode ?? 1);
179
+ }
180
+ await span.end({ status: 'failed', error_type: err.constructor.name });
181
+ throw err;
182
+ }
162
183
  notifyUpdate(await updateCheck);
163
184
  return;
164
185
  }
@@ -168,5 +189,5 @@ export async function run(argv) {
168
189
  process.exit(0);
169
190
  }
170
191
 
171
- fmt.cancel(`Unknown command: ${command}`);
192
+ fmt.cancelAndExit(`Unknown command: ${command}`);
172
193
  }
package/commands/init.mjs CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  import {
8
8
  existsSync,
9
+ mkdirSync,
9
10
  writeFileSync,
10
11
  symlinkSync,
11
12
  lstatSync,
@@ -25,11 +26,10 @@ import { chalk } from '../fmt.mjs';
25
26
  import { linkWorkspace } from '../link.mjs';
26
27
  import { generateCommands, copyInstructions, initAwDocs } from '../integrate.mjs';
27
28
  import { setupMcp } from '../mcp.mjs';
28
- import { applyStoredStartupPreferences } from '../startup.mjs';
29
+ import { installLocalCommitHook } from '../hooks.mjs';
29
30
  import { autoUpdate, promptUpdate } from '../update.mjs';
30
31
  import { installGlobalHooks } from '../hooks.mjs';
31
32
  import { installAwEcc } from '../ecc.mjs';
32
- import { removeWorkspaceHookDefaults } from '../codex.mjs';
33
33
  import {
34
34
  initPersistentClone,
35
35
  isValidClone,
@@ -58,18 +58,6 @@ const HOME = (() => { try { return realpathSync(_rawHome); } catch { return _raw
58
58
  const GLOBAL_AW_DIR = join(HOME, '.aw_registry');
59
59
  const AW_HOME = join(HOME, '.aw');
60
60
 
61
- function syncInstructionsAndAwDocs(targetDir, namespace) {
62
- copyInstructions(targetDir, null, namespace);
63
- initAwDocs(targetDir);
64
- }
65
-
66
- function syncHomeAndProjectInstructions(cwd, namespace) {
67
- syncInstructionsAndAwDocs(HOME, namespace);
68
- if (cwd !== HOME) {
69
- syncInstructionsAndAwDocs(cwd, namespace);
70
- }
71
- }
72
-
73
61
  // ── Ensure ~/.aw/.gitignore has personal/local entries ───────────────────
74
62
 
75
63
  const AW_GITIGNORE_ENTRIES = [
@@ -99,7 +87,7 @@ function installIdeTasks() {
99
87
  {
100
88
  label: 'aw: sync registry',
101
89
  type: 'shell',
102
- command: 'aw init --silent',
90
+ command: 'AW_TRIGGER=ide:task aw init --silent',
103
91
  presentation: { reveal: 'silent', panel: 'shared', close: true },
104
92
  runOptions: { runOn: 'folderOpen' },
105
93
  problemMatcher: [],
@@ -220,7 +208,7 @@ export async function initCommand(args) {
220
208
  }
221
209
 
222
210
  if (choice === 'platform-only') {
223
- namespace = null; team = null; subTeam = null; folderName = null;
211
+ namespace = 'platform'; team = 'platform'; subTeam = null; folderName = null;
224
212
  }
225
213
  }
226
214
 
@@ -259,6 +247,7 @@ export async function initCommand(args) {
259
247
  }
260
248
 
261
249
  ensureAwGitignore(AW_HOME);
250
+ mkdirSync(join(GLOBAL_AW_DIR, 'memory'), { recursive: true });
262
251
  const freshCfg = config.load(GLOBAL_AW_DIR);
263
252
  if (existsSync(GLOBAL_AW_DIR)) {
264
253
  syncFileTree(join(AW_HOME, RULES_SOURCE_DIR), join(GLOBAL_AW_DIR, RULES_SOURCE_DIR));
@@ -273,9 +262,9 @@ export async function initCommand(args) {
273
262
  }
274
263
 
275
264
  await installAwEcc(cwd, { silent });
276
- syncHomeAndProjectInstructions(cwd, freshCfg?.namespace || team);
265
+ copyInstructions(HOME, null, freshCfg?.namespace || team) || [];
266
+ initAwDocs(HOME);
277
267
  await setupMcp(HOME, freshCfg?.namespace || team, { silent });
278
- const removedLegacyStartupFiles = cwd !== HOME ? removeWorkspaceHookDefaults(cwd) : [];
279
268
  installGlobalHooks();
280
269
 
281
270
  // Remove old local .git/hooks/post-checkout that pre-dates core.hooksPath (creates stale .aw_registry symlink)
@@ -307,6 +296,7 @@ export async function initCommand(args) {
307
296
  const awDirForLinks = (projectRegistryDir && existsSync(projectRegistryDir)) ? projectRegistryDir : null;
308
297
  const symlinks = linkWorkspace(HOME, awDirForLinks, { silent: true });
309
298
  const commands = generateCommands(HOME, { silent: true });
299
+ if (cwd !== HOME) installLocalCommitHook(cwd);
310
300
 
311
301
  if (silent) {
312
302
  autoUpdate(await args._updateCheck);
@@ -316,9 +306,6 @@ export async function initCommand(args) {
316
306
  '',
317
307
  ` ${chalk.green('✓')} Registry synced`,
318
308
  ` ${chalk.green('✓')} IDE refreshed — ${chalk.bold(symlinks)} symlinks · ${chalk.bold(commands)} commands`,
319
- removedLegacyStartupFiles.length > 0
320
- ? ` ${chalk.green('✓')} Removed ${removedLegacyStartupFiles.length} legacy repo startup file${removedLegacyStartupFiles.length > 1 ? 's' : ''}`
321
- : null,
322
309
  cwd !== HOME && isWorktree(join(cwd, '.aw')) ? ` ${chalk.green('✓')} Project linked` : null,
323
310
  ].filter(Boolean).join('\n'));
324
311
  }
@@ -394,8 +381,11 @@ export async function initCommand(args) {
394
381
  }
395
382
  }
396
383
 
397
- // Create sync config
398
- const cfg = config.create(GLOBAL_AW_DIR, { namespace: team, user });
384
+ // Create memory dir after symlink so GLOBAL_AW_DIR resolves correctly
385
+ mkdirSync(join(GLOBAL_AW_DIR, 'memory'), { recursive: true });
386
+
387
+ // Create sync config — default to 'platform' when no namespace specified
388
+ const cfg = config.create(GLOBAL_AW_DIR, { namespace: team || 'platform', user });
399
389
  if (folderName) {
400
390
  config.addPattern(GLOBAL_AW_DIR, folderName);
401
391
  }
@@ -405,10 +395,9 @@ export async function initCommand(args) {
405
395
 
406
396
  // Step 3: Setup tasks, MCP, hooks
407
397
  await installAwEcc(cwd, { silent });
408
- syncHomeAndProjectInstructions(cwd, team);
398
+ const instructionFiles = copyInstructions(HOME, null, team) || [];
399
+ initAwDocs(HOME);
409
400
  const mcpFiles = await setupMcp(HOME, team, { silent }) || [];
410
- applyStoredStartupPreferences(HOME);
411
- const removedLegacyStartupFiles = cwd !== HOME ? removeWorkspaceHookDefaults(cwd) : [];
412
401
  const hooksInstalled = installGlobalHooks();
413
402
  installIdeTasks();
414
403
 
@@ -441,6 +430,7 @@ export async function initCommand(args) {
441
430
  const projectRegistryDir = cwd !== HOME ? join(cwd, '.aw', REGISTRY_DIR) : null;
442
431
  const awDirForLinks = (projectRegistryDir && existsSync(projectRegistryDir)) ? projectRegistryDir : null;
443
432
  const symlinks = linkWorkspace(HOME, awDirForLinks, { silent: true });
433
+ if (cwd !== HOME) installLocalCommitHook(cwd);
444
434
  ideSpinner.message('Generating commands...');
445
435
  const commands = generateCommands(HOME, { silent: true });
446
436
  ideSpinner.stop(`IDE wired — ${chalk.bold(symlinks)} symlinks · ${chalk.bold(commands)} commands`);
@@ -454,10 +444,6 @@ export async function initCommand(args) {
454
444
  ` ${chalk.green('✓')} Source of truth: ~/.aw/ (git clone)`,
455
445
  ` ${chalk.green('✓')} Symlink: ~/.aw_registry/ → ~/.aw/.aw_registry/`,
456
446
  ` ${chalk.green('✓')} IDE integration: ~/.claude/, ~/.cursor/, ~/.codex/`,
457
- cwd !== HOME ? ` ${chalk.green('✓')} Global startup managed from ~/.claude/, ~/.cursor/, ~/.codex/` : null,
458
- removedLegacyStartupFiles.length > 0
459
- ? ` ${chalk.green('✓')} Removed ${removedLegacyStartupFiles.length} legacy repo startup file${removedLegacyStartupFiles.length > 1 ? 's' : ''}`
460
- : null,
461
447
  hooksInstalled ? ` ${chalk.green('✓')} Git hooks: auto-sync on pull/clone (core.hooksPath)` : null,
462
448
  ` ${chalk.green('✓')} IDE task: auto-sync on workspace open`,
463
449
  cwd !== HOME && isWorktree(join(cwd, '.aw')) ? ` ${chalk.green('✓')} Linked in current project` : null,
@@ -9,8 +9,7 @@ import { addProjectWorktree, isWorktree, isValidClone } from '../git.mjs';
9
9
  import { REGISTRY_DIR, REGISTRY_URL } from '../constants.mjs';
10
10
  import { linkWorkspace } from '../link.mjs';
11
11
  import { generateCommands } from '../integrate.mjs';
12
- import { removeWorkspaceHookDefaults } from '../codex.mjs';
13
- import { applyStoredStartupPreferences } from '../startup.mjs';
12
+ import { installLocalCommitHook } from '../hooks.mjs';
14
13
 
15
14
  const HOME = homedir();
16
15
  const AW_HOME = join(HOME, '.aw');
@@ -42,9 +41,8 @@ export function linkProjectCommand(args) {
42
41
  const awDirForLinks = existsSync(projectRegistryDir) ? projectRegistryDir : null;
43
42
  const symlinks = linkWorkspace(HOME, awDirForLinks, { silent: true });
44
43
  const commands = generateCommands(HOME, { silent: true });
45
- applyStoredStartupPreferences(HOME);
46
- const removedLegacyStartupFiles = removeWorkspaceHookDefaults(cwd);
47
- fmt.logSuccess(`Already linked — refreshed ${chalk.bold(symlinks)} IDE symlinks · ${chalk.bold(commands)} commands${removedLegacyStartupFiles.length > 0 ? ` · removed ${removedLegacyStartupFiles.length} legacy repo startup file${removedLegacyStartupFiles.length > 1 ? 's' : ''}` : ''}`);
44
+ installLocalCommitHook(cwd);
45
+ fmt.logSuccess(`Already linked refreshed ${chalk.bold(symlinks)} IDE symlinks · ${chalk.bold(commands)} commands`);
48
46
  return;
49
47
  }
50
48
 
@@ -54,18 +52,13 @@ export function linkProjectCommand(args) {
54
52
  const awDirForLinks = existsSync(projectRegistryDir) ? projectRegistryDir : null;
55
53
  const symlinks = linkWorkspace(HOME, awDirForLinks, { silent: true });
56
54
  const commands = generateCommands(HOME, { silent: true });
57
- applyStoredStartupPreferences(HOME);
58
- const removedLegacyStartupFiles = removeWorkspaceHookDefaults(cwd);
55
+ installLocalCommitHook(cwd);
59
56
  fmt.logSuccess([
60
57
  `Project linked — ${chalk.bold(symlinks)} IDE symlinks · ${chalk.bold(commands)} commands`,
61
58
  '',
62
59
  ` ${chalk.green('✓')} ${chalk.dim('.aw/')} git worktree (IDE git panel enabled)`,
63
60
  ` ${chalk.green('✓')} ${chalk.dim(`.aw/${REGISTRY_DIR}/`)} registry content`,
64
61
  ` ${chalk.green('✓')} ${chalk.dim('.claude/.cursor/.codex/')} IDE symlinks wired`,
65
- ` ${chalk.green('✓')} ${chalk.dim('startup mode')} global-first via ~/.claude/, ~/.cursor/, ~/.codex/`,
66
- removedLegacyStartupFiles.length > 0
67
- ? ` ${chalk.green('✓')} ${chalk.dim('legacy repo hooks')} removed ${removedLegacyStartupFiles.length} AW-managed file${removedLegacyStartupFiles.length > 1 ? 's' : ''}`
68
- : null,
69
62
  ].join('\n'));
70
63
  } catch (e) {
71
64
  fmt.cancel(`Failed to link project: ${e.message}`);