@ghl-ai/aw 0.1.38-beta.12 → 0.1.38-beta.14

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
@@ -18,9 +18,6 @@ const COMMANDS = {
18
18
  'push-rules': () => import('./commands/push-rules.mjs').then(m => m.pushRulesCommand),
19
19
  drop: () => import('./commands/drop.mjs').then(m => m.dropCommand),
20
20
  status: () => import('./commands/status.mjs').then(m => m.statusCommand),
21
- doctor: () => import('./commands/doctor.mjs').then(m => m.doctorCommand),
22
- routing: () => import('./commands/startup.mjs').then(m => m.routingCommand),
23
- startup: () => import('./commands/startup.mjs').then(m => m.startupCommand),
24
21
  search: () => import('./commands/search.mjs').then(m => m.searchCommand),
25
22
  link: () => import('./commands/link-project.mjs').then(m => m.linkProjectCommand),
26
23
  nuke: () => import('./commands/nuke.mjs').then(m => m.nukeCommand),
@@ -100,11 +97,7 @@ function printHelp() {
100
97
 
101
98
  sec('Manage'),
102
99
  cmd('aw status', 'Show synced paths, modified files & conflicts'),
103
- cmd('aw doctor', 'Run a health check for routing, MCP, plugin, and AW ECC surfaces'),
104
100
  cmd('aw link', 'Link current project as a git worktree (wires IDE symlinks)'),
105
- cmd('aw routing status', 'Show global AW session-routing mode for Claude/Cursor/Codex'),
106
- cmd('aw routing disable', 'Disable automatic AW session routing globally'),
107
- cmd('aw routing enable', 'Re-enable automatic AW session routing globally'),
108
101
  cmd('aw drop <path>', 'Stop syncing or delete local content'),
109
102
  cmd('aw nuke', 'Remove entire .aw_registry/ & start fresh'),
110
103
  cmd('aw daemon install', 'Auto-pull on a schedule (macOS launchd / Linux cron)'),
@@ -134,7 +127,7 @@ function printHelp() {
134
127
  cmd('aw push .aw_registry/agents/<name>.md', 'Push a single agent'),
135
128
  cmd('aw push .aw_registry/skills/<name>/', 'Push a single skill folder'),
136
129
  cmd('aw push .aw_rules', 'Auto-redirects to aw push-rules'),
137
- cmd('aw push-rules', 'Pushes .aw_rules'),
130
+ cmd('aw push-rules', 'Pushes .aw_rules or .aw_registry/.aw_rules'),
138
131
  '',
139
132
  ` ${chalk.dim('# Remove content from workspace')}`,
140
133
  cmd('aw drop <team>', 'Stop syncing a namespace (removes all files)'),
package/commands/init.mjs CHANGED
@@ -23,14 +23,12 @@ import * as config from '../config.mjs';
23
23
  import * as fmt from '../fmt.mjs';
24
24
  import { chalk } from '../fmt.mjs';
25
25
  import { linkWorkspace } from '../link.mjs';
26
- import { generateCommands, copyInstructions, initAwDocs, syncHomeHarnessInstructions } from '../integrate.mjs';
26
+ import { generateCommands, copyInstructions, initAwDocs } from '../integrate.mjs';
27
27
  import { setupMcp } from '../mcp.mjs';
28
- import { applyStoredStartupPreferences, ensureAwRuntimeHook } from '../startup.mjs';
29
28
  import { installLocalCommitHook } from '../hooks.mjs';
30
29
  import { autoUpdate, promptUpdate } from '../update.mjs';
31
30
  import { installGlobalHooks } from '../hooks.mjs';
32
31
  import { installAwEcc } from '../ecc.mjs';
33
- import { removeWorkspaceHookDefaults } from '../codex.mjs';
34
32
  import {
35
33
  initPersistentClone,
36
34
  isValidClone,
@@ -59,34 +57,6 @@ const HOME = (() => { try { return realpathSync(_rawHome); } catch { return _raw
59
57
  const GLOBAL_AW_DIR = join(HOME, '.aw_registry');
60
58
  const AW_HOME = join(HOME, '.aw');
61
59
 
62
- function syncRulesTargets(targetDir) {
63
- const rulesSrc = join(AW_HOME, RULES_SOURCE_DIR);
64
- if (!existsSync(rulesSrc)) return false;
65
- syncFileTree(rulesSrc, join(targetDir, RULES_SOURCE_DIR));
66
- return true;
67
- }
68
-
69
- function removeLegacyRegistryRules() {
70
- try {
71
- rmSync(join(GLOBAL_AW_DIR, RULES_SOURCE_DIR), { recursive: true, force: true });
72
- } catch {
73
- // best effort cleanup
74
- }
75
- }
76
-
77
- function syncInstructionsAndAwDocs(targetDir, namespace) {
78
- copyInstructions(targetDir, null, namespace);
79
- initAwDocs(targetDir);
80
- }
81
-
82
- function syncHomeAndProjectInstructions(cwd, namespace) {
83
- syncHomeHarnessInstructions(HOME);
84
- initAwDocs(HOME);
85
- if (cwd !== HOME) {
86
- syncInstructionsAndAwDocs(cwd, namespace);
87
- }
88
- }
89
-
90
60
  // ── Ensure ~/.aw/.gitignore has personal/local entries ───────────────────
91
61
 
92
62
  const AW_GITIGNORE_ENTRIES = [
@@ -277,11 +247,9 @@ export async function initCommand(args) {
277
247
 
278
248
  ensureAwGitignore(AW_HOME);
279
249
  const freshCfg = config.load(GLOBAL_AW_DIR);
280
- syncRulesTargets(HOME);
281
- if (cwd !== HOME) {
282
- syncRulesTargets(cwd);
250
+ if (existsSync(GLOBAL_AW_DIR)) {
251
+ syncFileTree(join(AW_HOME, RULES_SOURCE_DIR), join(GLOBAL_AW_DIR, RULES_SOURCE_DIR));
283
252
  }
284
- removeLegacyRegistryRules();
285
253
 
286
254
  // Ensure project worktree sparse checkout matches the global clone.
287
255
  // Covers the case where a namespace was added from HOME (or another project)
@@ -292,11 +260,9 @@ export async function initCommand(args) {
292
260
  }
293
261
 
294
262
  await installAwEcc(cwd, { silent });
295
- ensureAwRuntimeHook(HOME);
296
- syncHomeAndProjectInstructions(cwd, freshCfg?.namespace || team);
263
+ copyInstructions(HOME, null, freshCfg?.namespace || team) || [];
264
+ initAwDocs(HOME);
297
265
  await setupMcp(HOME, freshCfg?.namespace || team, { silent });
298
- applyStoredStartupPreferences(HOME);
299
- const removedLegacyStartupFiles = cwd !== HOME ? removeWorkspaceHookDefaults(cwd) : [];
300
266
  installGlobalHooks();
301
267
 
302
268
  // Remove old local .git/hooks/post-checkout that pre-dates core.hooksPath (creates stale .aw_registry symlink)
@@ -338,9 +304,6 @@ export async function initCommand(args) {
338
304
  '',
339
305
  ` ${chalk.green('✓')} Registry synced`,
340
306
  ` ${chalk.green('✓')} IDE refreshed — ${chalk.bold(symlinks)} symlinks · ${chalk.bold(commands)} commands`,
341
- removedLegacyStartupFiles.length > 0
342
- ? ` ${chalk.green('✓')} Removed ${removedLegacyStartupFiles.length} legacy repo startup file${removedLegacyStartupFiles.length > 1 ? 's' : ''}`
343
- : null,
344
307
  cwd !== HOME && isWorktree(join(cwd, '.aw')) ? ` ${chalk.green('✓')} Project linked` : null,
345
308
  ].filter(Boolean).join('\n'));
346
309
  }
@@ -421,19 +384,15 @@ export async function initCommand(args) {
421
384
  if (folderName) {
422
385
  config.addPattern(GLOBAL_AW_DIR, folderName);
423
386
  }
424
- syncRulesTargets(HOME);
425
- if (cwd !== HOME) {
426
- syncRulesTargets(cwd);
387
+ if (existsSync(GLOBAL_AW_DIR)) {
388
+ syncFileTree(join(AW_HOME, RULES_SOURCE_DIR), join(GLOBAL_AW_DIR, RULES_SOURCE_DIR));
427
389
  }
428
- removeLegacyRegistryRules();
429
390
 
430
391
  // Step 3: Setup tasks, MCP, hooks
431
392
  await installAwEcc(cwd, { silent });
432
- ensureAwRuntimeHook(HOME);
433
- syncHomeAndProjectInstructions(cwd, team);
393
+ const instructionFiles = copyInstructions(HOME, null, team) || [];
394
+ initAwDocs(HOME);
434
395
  const mcpFiles = await setupMcp(HOME, team, { silent }) || [];
435
- applyStoredStartupPreferences(HOME);
436
- const removedLegacyStartupFiles = cwd !== HOME ? removeWorkspaceHookDefaults(cwd) : [];
437
396
  const hooksInstalled = installGlobalHooks();
438
397
  installIdeTasks();
439
398
 
@@ -480,10 +439,6 @@ export async function initCommand(args) {
480
439
  ` ${chalk.green('✓')} Source of truth: ~/.aw/ (git clone)`,
481
440
  ` ${chalk.green('✓')} Symlink: ~/.aw_registry/ → ~/.aw/.aw_registry/`,
482
441
  ` ${chalk.green('✓')} IDE integration: ~/.claude/, ~/.cursor/, ~/.codex/`,
483
- cwd !== HOME ? ` ${chalk.green('✓')} Global startup managed from ~/.claude/, ~/.cursor/, ~/.codex/` : null,
484
- removedLegacyStartupFiles.length > 0
485
- ? ` ${chalk.green('✓')} Removed ${removedLegacyStartupFiles.length} legacy repo startup file${removedLegacyStartupFiles.length > 1 ? 's' : ''}`
486
- : null,
487
442
  hooksInstalled ? ` ${chalk.green('✓')} Git hooks: auto-sync on pull/clone (core.hooksPath)` : null,
488
443
  ` ${chalk.green('✓')} IDE task: auto-sync on workspace open`,
489
444
  cwd !== HOME && isWorktree(join(cwd, '.aw')) ? ` ${chalk.green('✓')} Linked in current project` : null,
@@ -9,8 +9,6 @@ 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';
14
12
  import { installLocalCommitHook } from '../hooks.mjs';
15
13
 
16
14
  const HOME = homedir();
@@ -43,11 +41,8 @@ export function linkProjectCommand(args) {
43
41
  const awDirForLinks = existsSync(projectRegistryDir) ? projectRegistryDir : null;
44
42
  const symlinks = linkWorkspace(HOME, awDirForLinks, { silent: true });
45
43
  const commands = generateCommands(HOME, { silent: true });
46
- applyStoredStartupPreferences(HOME);
47
44
  installLocalCommitHook(cwd);
48
- const removedLegacyStartupFiles = removeWorkspaceHookDefaults(cwd);
49
- 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' : ''}` : ''}`);
50
-
45
+ fmt.logSuccess(`Already linked refreshed ${chalk.bold(symlinks)} IDE symlinks · ${chalk.bold(commands)} commands`);
51
46
  return;
52
47
  }
53
48
 
@@ -57,19 +52,13 @@ export function linkProjectCommand(args) {
57
52
  const awDirForLinks = existsSync(projectRegistryDir) ? projectRegistryDir : null;
58
53
  const symlinks = linkWorkspace(HOME, awDirForLinks, { silent: true });
59
54
  const commands = generateCommands(HOME, { silent: true });
60
- applyStoredStartupPreferences(HOME);
61
55
  installLocalCommitHook(cwd);
62
- const removedLegacyStartupFiles = removeWorkspaceHookDefaults(cwd);
63
56
  fmt.logSuccess([
64
57
  `Project linked — ${chalk.bold(symlinks)} IDE symlinks · ${chalk.bold(commands)} commands`,
65
58
  '',
66
59
  ` ${chalk.green('✓')} ${chalk.dim('.aw/')} git worktree (IDE git panel enabled)`,
67
60
  ` ${chalk.green('✓')} ${chalk.dim(`.aw/${REGISTRY_DIR}/`)} registry content`,
68
61
  ` ${chalk.green('✓')} ${chalk.dim('.claude/.cursor/.codex/')} IDE symlinks wired`,
69
- ` ${chalk.green('✓')} ${chalk.dim('startup mode')} global-first via ~/.claude/, ~/.cursor/, ~/.codex/`,
70
- removedLegacyStartupFiles.length > 0
71
- ? ` ${chalk.green('✓')} ${chalk.dim('legacy repo hooks')} removed ${removedLegacyStartupFiles.length} AW-managed file${removedLegacyStartupFiles.length > 1 ? 's' : ''}`
72
- : null,
73
62
  ].join('\n'));
74
63
  } catch (e) {
75
64
  fmt.cancel(`Failed to link project: ${e.message}`);
package/commands/nuke.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  //
3
3
  // Safety guarantee: NEVER deletes files that AW didn't create.
4
4
 
5
- import { dirname, join } from 'node:path';
5
+ import { join } from 'node:path';
6
6
  import { existsSync, rmSync, lstatSync, unlinkSync, readdirSync, readFileSync, readlinkSync, writeFileSync } from 'node:fs';
7
7
  import { homedir } from 'node:os';
8
8
  import { execSync, exec as execCb } from 'node:child_process';
@@ -15,14 +15,13 @@ import { removeGlobalHooks } from '../hooks.mjs';
15
15
  import { uninstallAwEcc } from '../ecc.mjs';
16
16
  import { removeMcpConfig } from '../mcp.mjs';
17
17
  import { listProjectWorktrees } from '../git.mjs';
18
- import { removeWorkspaceHookDefaults } from '../codex.mjs';
19
18
 
20
19
  const HOME = homedir();
21
20
  const GLOBAL_AW_DIR = join(HOME, '.aw_registry');
22
21
  const MANIFEST_PATH = join(GLOBAL_AW_DIR, '.aw-manifest.json');
23
22
 
24
23
  const IDE_DIRS = ['.claude', '.cursor', '.codex', '.agents'];
25
- const CONTENT_TYPES = ['agents', 'skills', 'commands', 'evals', 'references'];
24
+ const CONTENT_TYPES = ['agents', 'skills', 'commands', 'evals'];
26
25
 
27
26
  function loadManifest() {
28
27
  if (!existsSync(MANIFEST_PATH)) return null;
@@ -140,11 +139,7 @@ async function removeProjectSymlinks() {
140
139
  { encoding: 'utf8', timeout: 30000 }
141
140
  );
142
141
  for (const linkPath of registryLinks.trim().split('\n').filter(Boolean)) {
143
- try {
144
- removeWorkspaceHookDefaults(dirname(linkPath));
145
- unlinkSync(linkPath);
146
- removed++;
147
- } catch { /* best effort */ }
142
+ try { unlinkSync(linkPath); removed++; } catch { /* best effort */ }
148
143
  }
149
144
 
150
145
  // Also remove legacy local .git/hooks/post-checkout installed by old aw versions
@@ -222,12 +217,7 @@ export async function nukeCommand(args) {
222
217
  // Remove stale local .aw_registry symlink if present (skip if cwd IS home — that's the global one)
223
218
  if (process.cwd() !== HOME) {
224
219
  const local = join(process.cwd(), '.aw_registry');
225
- try {
226
- if (lstatSync(local).isSymbolicLink()) {
227
- removeWorkspaceHookDefaults(process.cwd());
228
- unlinkSync(local);
229
- }
230
- } catch { /* fine */ }
220
+ try { if (lstatSync(local).isSymbolicLink()) unlinkSync(local); } catch { /* fine */ }
231
221
  }
232
222
 
233
223
  const manifest = loadManifest();
package/commands/pull.mjs CHANGED
@@ -3,9 +3,8 @@
3
3
  import {
4
4
  existsSync,
5
5
  lstatSync,
6
- rmSync,
7
6
  } from 'node:fs';
8
- import { dirname, join, extname } from 'node:path';
7
+ import { join, extname } from 'node:path';
9
8
  import { homedir } from 'node:os';
10
9
  import { exec as execCb, execSync } from 'node:child_process';
11
10
  import { promisify } from 'node:util';
@@ -31,29 +30,12 @@ import {
31
30
  } from '../constants.mjs';
32
31
  import { collectAllPaths, syncFileTree } from '../file-tree.mjs';
33
32
  import { linkWorkspace } from '../link.mjs';
34
- import { generateCommands, copyInstructions, syncHomeHarnessInstructions } from '../integrate.mjs';
35
- import { removeWorkspaceHookDefaults } from '../codex.mjs';
36
- import { applyStoredStartupPreferences, ensureAwRuntimeHook } from '../startup.mjs';
33
+ import { generateCommands, copyInstructions } from '../integrate.mjs';
37
34
 
38
35
  const HOME = homedir();
39
36
  const AW_HOME = join(HOME, '.aw');
40
37
  const GLOBAL_AW_DIR = join(HOME, '.aw_registry');
41
38
 
42
- function syncRulesTargets(targetDir) {
43
- const rulesSrc = join(AW_HOME, RULES_SOURCE_DIR);
44
- if (!existsSync(rulesSrc)) return false;
45
- syncFileTree(rulesSrc, join(targetDir, RULES_SOURCE_DIR));
46
- return true;
47
- }
48
-
49
- function removeLegacyRegistryRules() {
50
- try {
51
- rmSync(join(GLOBAL_AW_DIR, RULES_SOURCE_DIR), { recursive: true, force: true });
52
- } catch {
53
- // best effort cleanup
54
- }
55
- }
56
-
57
39
  export async function pullCommand(args) {
58
40
  const input = args._positional?.[0] || '';
59
41
  const cwd = process.cwd();
@@ -195,6 +177,13 @@ export async function pullCommand(args) {
195
177
  log.logWarn(`Conflicts in: ${fetchResult.conflicts.join(', ')}`);
196
178
  }
197
179
 
180
+ const rulesSrc = join(AW_HOME, RULES_SOURCE_DIR);
181
+ if (existsSync(rulesSrc)) {
182
+ const rulesDest = join(GLOBAL_AW_DIR, RULES_SOURCE_DIR);
183
+ syncFileTree(rulesSrc, rulesDest);
184
+ if (!silent) log.logSuccess('Synced .aw_rules');
185
+ }
186
+
198
187
  // Rebase project worktree branch onto origin/main — only for legacy git worktrees.
199
188
  // In the symlink model, <project>/.aw IS ~/.aw (same repo), so fetchAndMerge already
200
189
  // brought it up to date. Nothing to rebase.
@@ -253,20 +242,9 @@ export async function pullCommand(args) {
253
242
  }
254
243
  }
255
244
 
256
- let rulesSynced = syncRulesTargets(HOME);
257
- const workspaceRoot = localAw ? dirname(localAw) : (cwd !== HOME ? cwd : null);
258
- if (workspaceRoot && workspaceRoot !== HOME) {
259
- rulesSynced = syncRulesTargets(workspaceRoot) || rulesSynced;
260
- }
261
- removeLegacyRegistryRules();
262
- if (rulesSynced && !silent) {
263
- log.logSuccess('Synced .aw_rules');
264
- }
265
-
266
245
  if (!args._skipIntegrate) {
267
246
  const projectRegistryDir = cwd !== HOME ? join(cwd, '.aw', REGISTRY_DIR) : null;
268
247
  const awDirForLinks = (projectRegistryDir && existsSync(projectRegistryDir)) ? projectRegistryDir : null;
269
- const startupCleanupDir = localAw ? dirname(localAw) : (cwd !== HOME ? cwd : null);
270
248
 
271
249
  if (!silent) {
272
250
  const ideSpinner = log.spinner();
@@ -274,18 +252,12 @@ export async function pullCommand(args) {
274
252
  const symlinks = linkWorkspace(HOME, awDirForLinks, { silent: true });
275
253
  ideSpinner.message('Generating commands...');
276
254
  const commands = generateCommands(HOME, { silent: true });
277
- syncHomeHarnessInstructions(HOME);
278
- ensureAwRuntimeHook(HOME);
279
- applyStoredStartupPreferences(HOME);
280
- if (startupCleanupDir) removeWorkspaceHookDefaults(startupCleanupDir);
255
+ copyInstructions(HOME, null, cfg.namespace);
281
256
  ideSpinner.stop(`IDE wired — ${chalk.bold(symlinks)} symlink${symlinks !== 1 ? 's' : ''}, ${chalk.bold(commands)} command${commands !== 1 ? 's' : ''}`);
282
257
  } else {
283
258
  linkWorkspace(HOME, awDirForLinks, { silent: true });
284
259
  generateCommands(HOME, { silent: true });
285
- syncHomeHarnessInstructions(HOME);
286
- ensureAwRuntimeHook(HOME);
287
- applyStoredStartupPreferences(HOME);
288
- if (startupCleanupDir) removeWorkspaceHookDefaults(startupCleanupDir);
260
+ copyInstructions(HOME, null, cfg.namespace);
289
261
  }
290
262
  }
291
263
 
@@ -318,73 +290,3 @@ function registerMcp(namespace) {
318
290
  fmt.logWarn('MCP registration failed (pull still successful)');
319
291
  }
320
292
  }
321
- function printDryRun(actions, verbose) {
322
- const counts = { ADD: 0, UPDATE: 0, CONFLICT: 0, ORPHAN: 0, UNCHANGED: 0 };
323
- const lines = [];
324
-
325
- for (const type of ['agents', 'skills', 'commands', 'evals', 'references']) {
326
- const items = actions.filter(a => a.type === type);
327
- if (items.length === 0) continue;
328
-
329
- lines.push(chalk.bold(`${type}/`));
330
- for (const act of items.sort((a, b) => a.targetFilename.localeCompare(b.targetFilename))) {
331
- counts[act.action] = (counts[act.action] || 0) + 1;
332
- if (!verbose && act.action === 'UNCHANGED') continue;
333
- const ns = act.namespacePath ? chalk.dim(` [${act.namespacePath}]`) : '';
334
- lines.push(` ${fmt.actionLabel(act.action)} ${act.targetFilename}${ns}`);
335
- }
336
- }
337
-
338
- if (lines.length > 0) {
339
- fmt.note(lines.join('\n'), 'Dry Run');
340
- }
341
-
342
- fmt.logInfo(`Summary: ${fmt.countSummary(counts)}`);
343
- fmt.logWarn('No files modified (--dry-run)');
344
- }
345
-
346
- function printSummary(actions, verbose, conflictCount) {
347
- const conflicts = actions.filter(a => a.action === 'CONFLICT');
348
-
349
- for (const type of ['agents', 'skills', 'commands', 'evals', 'references']) {
350
- const typeActions = actions.filter(a => a.type === type);
351
- if (typeActions.length === 0) continue;
352
-
353
- const counts = { ADD: 0, UPDATE: 0, CONFLICT: 0, ORPHAN: 0, UNCHANGED: 0 };
354
- for (const a of typeActions) counts[a.action]++;
355
-
356
- const parts = [];
357
- if (counts.ADD > 0) parts.push(chalk.green(`${counts.ADD} new`));
358
- if (counts.UPDATE > 0) parts.push(chalk.cyan(`${counts.UPDATE} updated`));
359
- if (counts.CONFLICT > 0) parts.push(chalk.red(`${counts.CONFLICT} conflict`));
360
- if (counts.ORPHAN > 0) parts.push(chalk.yellow(`${counts.ORPHAN} removed`));
361
- const detail = parts.length > 0 ? ` (${parts.join(', ')})` : '';
362
-
363
- fmt.logSuccess(`${typeActions.length} ${type} pulled${detail}`);
364
-
365
- if (verbose) {
366
- for (const a of typeActions.filter(a => a.action !== 'UNCHANGED')) {
367
- const ns = a.namespacePath ? chalk.dim(` [${a.namespacePath}]`) : '';
368
- fmt.logMessage(` ${fmt.actionLabel(a.action)} ${a.targetFilename}${ns}`);
369
- }
370
- }
371
- }
372
-
373
- if (conflicts.length > 0) {
374
- const conflictLines = conflicts.map(c => {
375
- return `${chalk.red('both modified:')} ${c.type}/${c.targetFilename}`;
376
- }).join('\n');
377
-
378
- fmt.note(
379
- conflictLines + '\n\n' +
380
- chalk.dim('Fix conflicts, then re-run pull to verify.\n') +
381
- chalk.dim('grep -r "<<<<<<< " .aw_registry/'),
382
- chalk.red('Merge Conflicts')
383
- );
384
-
385
- fmt.outro(chalk.red('Pull completed with conflicts — resolve and re-run'));
386
- process.exit(1);
387
- }
388
-
389
- fmt.outro('Pull complete');
390
- }
@@ -23,9 +23,11 @@ export function isRulesPushInput(input) {
23
23
 
24
24
  export function resolveRulesPushSource(input, cwd = process.cwd()) {
25
25
  const localRulesRoot = join(cwd, RULES_SOURCE_DIR);
26
+ const syncedRulesRoot = join(cwd, '.aw_registry', RULES_SOURCE_DIR);
26
27
 
27
28
  if (!input) {
28
29
  if (existsSync(localRulesRoot)) return { sourceRoot: localRulesRoot, sourceType: 'local' };
30
+ if (existsSync(syncedRulesRoot)) return { sourceRoot: syncedRulesRoot, sourceType: 'synced' };
29
31
  return null;
30
32
  }
31
33
 
@@ -37,19 +39,22 @@ export function resolveRulesPushSource(input, cwd = process.cwd()) {
37
39
  }
38
40
 
39
41
  if (normalizedInput === `.aw_registry/${RULES_SOURCE_DIR}` || normalizedInput.startsWith(`.aw_registry/${RULES_SOURCE_DIR}/`)) {
42
+ if (!existsSync(syncedRulesRoot)) return null;
43
+
40
44
  const relativeRulesPath = normalizedInput.slice(`.aw_registry/${RULES_SOURCE_DIR}`.length).replace(/^\/+/, '');
41
45
  const localOverridePath = relativeRulesPath ? join(localRulesRoot, relativeRulesPath) : localRulesRoot;
42
46
  if (existsSync(localOverridePath)) {
43
47
  return { sourceRoot: localRulesRoot, sourceType: 'local' };
44
48
  }
45
- return null;
49
+
50
+ return { sourceRoot: syncedRulesRoot, sourceType: 'synced' };
46
51
  }
47
52
 
48
53
  return null;
49
54
  }
50
55
 
51
56
  export function hasRulesChanges(cwd = process.cwd()) {
52
- const candidateDirs = [RULES_SOURCE_DIR]
57
+ const candidateDirs = [RULES_SOURCE_DIR, `.aw_registry/${RULES_SOURCE_DIR}`]
53
58
  .filter(rel => existsSync(join(cwd, rel)));
54
59
 
55
60
  if (candidateDirs.length === 0) return false;
@@ -136,7 +141,9 @@ function pushRulesTree(sourceRoot, { repo, dryRun, cwd }) {
136
141
  fmt.cancel('Nothing to push — remote rules already match local content.');
137
142
  }
138
143
 
139
- const sourceType = 'local';
144
+ const sourceType = normalize(sourceRoot).includes(normalize(join('.aw_registry', RULES_SOURCE_DIR)))
145
+ ? 'synced'
146
+ : 'local';
140
147
  const prTitle = buildRulesPrTitle(sourceRoot, cwd);
141
148
  const prBody = buildRulesPrBody(sourceRoot, sourceType, cwd);
142
149
 
@@ -184,12 +191,16 @@ export function pushRulesCommand(args) {
184
191
  fmt.cancel([
185
192
  'Could not find a rules source to push.',
186
193
  '',
187
- ` Checked ${chalk.cyan('.aw_rules/')}.`,
194
+ ` Checked ${chalk.cyan('.aw_rules/')} and ${chalk.cyan('.aw_registry/.aw_rules/')}.`,
188
195
  '',
189
196
  ' Use `aw pull platform` first or create a local `.aw_rules/` authoring tree.',
190
197
  ].join('\n'));
191
198
  }
192
199
 
200
+ if (resolved.sourceType === 'synced') {
201
+ fmt.logWarn('Pushing from synced `.aw_registry/.aw_rules/`. Local `.aw_rules/` is safer for authoring.');
202
+ }
203
+
193
204
  pushRulesTree(resolved.sourceRoot, { repo, dryRun, cwd });
194
205
  }
195
206
 
package/commands/push.mjs CHANGED
@@ -29,7 +29,7 @@ import { hasRulesChanges, isRulesPushInput, pushRulesCommand } from './push-rule
29
29
  const __dirname = dirname(fileURLToPath(import.meta.url));
30
30
  const VERSION = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8')).version;
31
31
 
32
- const PUSHABLE_TYPES = ['agents', 'skills', 'commands', 'evals', 'references'];
32
+ const PUSHABLE_TYPES = ['agents', 'skills', 'commands', 'evals'];
33
33
 
34
34
  // ── PR content generation ────────────────────────────────────────────
35
35
 
@@ -635,8 +635,8 @@ export async function pushCommand(args) {
635
635
  const registryAbsPath = join(registrySubDir, relFromRegistry);
636
636
  const allFiles = collectBatchFiles(registryAbsPath, registrySubDir);
637
637
  if (allFiles.length === 0) {
638
- // Folder/namespace input batch push
639
- fmt.cancel(`Nothing to push in ${chalk.cyan(input)} — no agents, skills, commands, evals, or references found.`);
638
+ fmt.cancel(`Nothing to push in ${chalk.cyan(input)} no agents, skills, commands, or evals found.`);
639
+ return;
640
640
  }
641
641
  // Only include files that actually have changes (new or modified)
642
642
  const folderChanges = detectChanges(awHome, REGISTRY_DIR);
@@ -688,7 +688,7 @@ export async function pushCommand(args) {
688
688
  fmt.cancel([
689
689
  `Invalid push path: ${chalk.red(resolved.registryPath)}`,
690
690
  '',
691
- ` Content must live under an ${chalk.bold('agents/')}, ${chalk.bold('skills/')}, ${chalk.bold('commands/')}, ${chalk.bold('evals/')}, or ${chalk.bold('references/')} directory.`,
691
+ ` Content must live under an ${chalk.bold('agents/')}, ${chalk.bold('skills/')}, ${chalk.bold('commands/')}, or ${chalk.bold('evals/')} directory.`,
692
692
  '',
693
693
  ` ${chalk.dim('Valid examples:')}`,
694
694
  ` aw push .aw_registry/commerce/quality/agents/unit-tester.md`,
@@ -98,7 +98,7 @@ function searchLocal(workspaceDir, query) {
98
98
  if (!nsEntry.isDirectory() || nsEntry.name.startsWith('.')) continue;
99
99
  const ns = nsEntry.name;
100
100
 
101
- for (const type of ['agents', 'skills', 'commands', 'evals', 'references']) {
101
+ for (const type of ['agents', 'skills', 'commands', 'evals']) {
102
102
  const typeDir = join(workspaceDir, ns, type);
103
103
  if (!existsSync(typeDir)) continue;
104
104
 
package/constants.mjs CHANGED
@@ -25,7 +25,7 @@ export const DOCS_SOURCE_DIR = 'content';
25
25
  /** Persistent git clone root — ~/.aw/ */
26
26
  export const AW_HOME = join(homedir(), '.aw');
27
27
 
28
- /** Directory in platform-docs repo containing platform rules (synced into root-level .aw_rules/) */
28
+ /** Directory in platform-docs repo containing platform rules (pulled into .aw_registry/.aw_rules/) */
29
29
  export const RULES_SOURCE_DIR = '.aw_rules';
30
30
  /** Telemetry endpoint — override with AW_TELEMETRY_URL env var */
31
31
  export const TELEMETRY_URL = process.env.AW_TELEMETRY_URL || 'https://services.leadconnectorhq.com/agentic-workspace/api/telemetry/events';