@ghl-ai/aw 0.1.51 → 0.1.52

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.
@@ -4,6 +4,8 @@ import { join } from 'node:path';
4
4
  import * as fmt from '../fmt.mjs';
5
5
  import { chalk } from '../fmt.mjs';
6
6
  import { removeWorkspaceHookDefaults } from '../codex.mjs';
7
+ import { syncHomeHarnessInstructions } from '../integrate.mjs';
8
+ import { renderRules } from '../render-rules.mjs';
7
9
  import {
8
10
  applyStoredStartupPreferences,
9
11
  getStartupStatus,
@@ -33,19 +35,27 @@ export function routingCommand(args) {
33
35
 
34
36
  saveStartupPreferences(mode, HOME);
35
37
  const updatedFiles = applyStoredStartupPreferences(HOME);
38
+ const updatedHomeInstructionFiles = syncHomeHarnessInstructions(HOME);
39
+ renderRules(cwd, { homeDir: HOME });
36
40
  const status = getStartupStatus(HOME);
41
+ const changedFiles = [...updatedFiles, ...updatedHomeInstructionFiles];
42
+ const effectiveModeLabel = status.effectiveMode === 'enabled' ? chalk.green('enabled') : chalk.yellow('disabled');
37
43
 
38
44
  fmt.outro([
39
45
  `⟁ Session routing ${action === 'disable' ? 'disabled' : 'enabled'}`,
40
46
  '',
41
47
  ` ${chalk.green('✓')} Preference saved: ${chalk.dim(status.preferencesPath.replace(`${HOME}/`, '~/'))}`,
48
+ ` ${chalk.green('✓')} Default AW router/rules: ${effectiveModeLabel}`,
42
49
  ` ${chalk.green('✓')} Claude session routing: ${status.claudeDisabled ? chalk.yellow('disabled') : chalk.green('enabled')}`,
43
50
  ` ${chalk.green('✓')} Codex session routing: ${status.codexHooksEnabled && status.codexSessionStartPresent ? chalk.green('enabled') : chalk.yellow('disabled')}`,
44
51
  ` ${chalk.green('✓')} Cursor session routing: ${status.cursorSessionStartPresent ? chalk.green('enabled') : chalk.yellow('disabled')}`,
52
+ updatedHomeInstructionFiles.length > 0
53
+ ? ` ${chalk.green('✓')} Updated ${updatedHomeInstructionFiles.length} home instruction file${updatedHomeInstructionFiles.length > 1 ? 's' : ''}`
54
+ : null,
45
55
  removedLegacyFiles.length > 0
46
56
  ? ` ${chalk.green('✓')} Removed ${removedLegacyFiles.length} legacy repo routing file${removedLegacyFiles.length > 1 ? 's' : ''} from the current project`
47
57
  : null,
48
- updatedFiles.length === 0
58
+ changedFiles.length === 0
49
59
  ? ` ${chalk.dim('Note:')} no global routing files needed changes`
50
60
  : null,
51
61
  ].filter(Boolean).join('\n'));
@@ -53,6 +63,9 @@ export function routingCommand(args) {
53
63
 
54
64
  function renderStatus() {
55
65
  const status = getStartupStatus(HOME);
66
+ const modeLabel = status.envDisableDefaultRouting
67
+ ? `${status.effectiveMode} (${status.envDisableDefaultRoutingName}=1 override; saved preference: ${status.mode})`
68
+ : status.mode;
56
69
  const codexStatus = status.codexHooksEnabled && status.codexSessionStartPresent
57
70
  ? 'enabled'
58
71
  : status.codexSessionStartPresent && !status.codexHooksEnabled
@@ -67,14 +80,20 @@ function renderStatus() {
67
80
  : status.claudePluginEnabled && !status.claudePluginInstalled
68
81
  ? 'misconfigured (plugin enabled in settings but not installed)'
69
82
  : 'disabled (plugin not installed)';
83
+ const cursorStatus = status.cursorSessionStartPresent
84
+ ? 'enabled'
85
+ : status.cursorAnySessionStartPresent
86
+ ? 'disabled (non-AW sessionStart hook present)'
87
+ : 'disabled';
70
88
 
71
89
  fmt.note([
72
- `${chalk.dim('mode:')} ${status.mode}`,
90
+ `${chalk.dim('mode:')} ${modeLabel}`,
73
91
  `${chalk.dim('prefs:')} ${status.preferencesPath.replace(`${HOME}/`, '~/')}`,
92
+ `${chalk.dim('default AW router/rules:')} ${status.effectiveMode}`,
74
93
  `${chalk.dim('claude session routing:')} ${claudeStatus}`,
75
94
  `${chalk.dim('codex session routing:')} ${codexStatus}`,
76
95
  `${chalk.dim('codex runtime:')} ${status.codexSessionStartScriptInstalled ? 'installed' : 'not installed'}`,
77
- `${chalk.dim('cursor session routing:')} ${status.cursorSessionStartPresent ? 'enabled' : 'disabled'}`,
96
+ `${chalk.dim('cursor session routing:')} ${cursorStatus}`,
78
97
  `${chalk.dim('cursor runtime:')} ${status.cursorSessionStartScriptInstalled ? 'installed' : 'not installed'}`,
79
98
  process.cwd() !== HOME
80
99
  ? `${chalk.dim('current repo legacy routing files:')} ${hasLegacyRepoStartupDefaults(process.cwd()) ? 'present' : 'not detected'}`
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.57";
15
+ export const AW_ECC_TAG = "v1.4.61";
16
16
 
17
17
  const MARKETPLACE_NAME = "aw-marketplace";
18
18
  const PLUGIN_KEY = `aw@${MARKETPLACE_NAME}`;
package/integrate.mjs CHANGED
@@ -12,6 +12,11 @@ import {
12
12
  renderRules,
13
13
  resolveRulesSourceDir,
14
14
  } from './render-rules.mjs';
15
+ import { isDefaultRoutingEnabled } from './startup.mjs';
16
+ import {
17
+ AW_DOCS_DIR,
18
+ defaultAwDocsGithubDocsConfig,
19
+ } from './constants.mjs';
15
20
 
16
21
  const AW_ROUTER_BRIDGE_HEADER = 'AW Router Bridge';
17
22
  const AW_ROUTER_BRIDGE_START_MARKER = '<!-- aw-managed:start router-bridge -->';
@@ -146,11 +151,13 @@ function applyManagedInstructionSections(content, file, rulesSections = {}, opti
146
151
  const rulesHeader = file === 'CLAUDE.md' ? CLAUDE_RULES_HEADER : AGENTS_RULES_HEADER;
147
152
  const rulesSection = file === 'CLAUDE.md' ? rulesSections.claudeSection : rulesSections.agentsSection;
148
153
  const includeBridge = options.includeBridge !== false;
154
+ const managedBoundary = '<!-- aw-managed: content below is regenerated by `aw init` — put your own content above this line -->';
149
155
 
150
156
  let next = stripLegacyRepoInstructionContent(content, file);
151
157
  next = stripManagedBlock(next, AW_ROUTER_BRIDGE_START_MARKER, AW_ROUTER_BRIDGE_END_MARKER);
152
158
  next = stripManagedSection(next, AW_ROUTER_BRIDGE_HEADER, [rulesHeader]);
153
159
  next = stripManagedSection(next, rulesHeader);
160
+ next = next.split('\n').filter(line => line.trim() !== managedBoundary).join('\n');
154
161
  next = next.trimEnd();
155
162
 
156
163
  const sections = [includeBridge ? generateAwRouterBridgeSection() : null, rulesSection].filter(Boolean);
@@ -161,12 +168,8 @@ function applyManagedInstructionSections(content, file, rulesSections = {}, opti
161
168
  // Marker tells users (and aw init) where the managed section starts.
162
169
  // Everything BEFORE this marker is repo-owned and never touched by aw.
163
170
  // Everything AFTER is managed by aw — re-rendered on every aw init.
164
- const managedBoundary = '<!-- aw-managed: content below is regenerated by `aw init` — put your own content above this line -->';
165
171
  const appended = [managedBoundary, ...sections].join('\n\n').trim();
166
- // Strip any prior managedBoundary line from `next` so we don't accumulate them
167
- // when re-running aw init.
168
- const cleaned = next.split('\n').filter(line => line.trim() !== managedBoundary).join('\n').trimEnd();
169
- return cleaned ? `${cleaned}\n\n${appended}\n` : `${appended}\n`;
172
+ return next ? `${next}\n\n${appended}\n` : `${appended}\n`;
170
173
  }
171
174
 
172
175
  /**
@@ -276,7 +279,33 @@ function syncHomeInstructionFile(destPath, file, rulesSection) {
276
279
  return destPath;
277
280
  }
278
281
 
282
+ function stripManagedHomeInstructionFile(destPath, file) {
283
+ if (!existsSync(destPath)) return null;
284
+
285
+ const existing = readFileSync(destPath, 'utf8');
286
+ const updated = applyManagedInstructionSections(existing, file, {}, { includeBridge: false });
287
+ const emptyDefault = defaultInstructionPreamble(file).trim();
288
+
289
+ if (updated.trim() === '' || updated.trim() === emptyDefault) {
290
+ rmSync(destPath, { force: true });
291
+ return destPath;
292
+ }
293
+
294
+ if (updated === existing) return null;
295
+
296
+ writeFileSync(destPath, updated);
297
+ return destPath;
298
+ }
299
+
279
300
  export function syncHomeHarnessInstructions(homeDir = homedir()) {
301
+ if (!isDefaultRoutingEnabled(homeDir)) {
302
+ return [
303
+ stripManagedHomeInstructionFile(join(homeDir, '.codex', 'AGENTS.md'), 'AGENTS.md'),
304
+ stripManagedHomeInstructionFile(join(homeDir, '.claude', 'CLAUDE.md'), 'CLAUDE.md'),
305
+ stripManagedHomeInstructionFile(join(homeDir, '.cursor', 'AGENTS.md'), 'AGENTS.md'),
306
+ ].filter(Boolean);
307
+ }
308
+
280
309
  const rulesDir = resolveRulesSourceDir(homeDir, { homeDir });
281
310
  const codexRulesSection = rulesDir
282
311
  ? generateAgentsMdRulesSection(rulesDir, {
@@ -585,14 +614,14 @@ Run with: \`/platform:eval agent:<slug>\` or \`/platform:eval skill:<slug>\`
585
614
  * Creates run tracking, learnings, task board, and cache directories.
586
615
  */
587
616
  export function initAwDocs(cwd) {
588
- const awDocsDir = join(cwd, '.aw_docs');
589
- if (existsSync(awDocsDir)) return; // Already initialized
617
+ const awDocsDir = join(cwd, AW_DOCS_DIR);
618
+ const alreadyInitialized = existsSync(awDocsDir);
590
619
 
591
620
  const dirs = [
592
- '.aw_docs/runs',
593
- '.aw_docs/learnings',
594
- '.aw_docs/tasks',
595
- '.aw_docs/cache',
621
+ `${AW_DOCS_DIR}/runs`,
622
+ `${AW_DOCS_DIR}/learnings`,
623
+ `${AW_DOCS_DIR}/tasks`,
624
+ `${AW_DOCS_DIR}/cache`,
596
625
  ];
597
626
 
598
627
  for (const dir of dirs) {
@@ -600,7 +629,9 @@ export function initAwDocs(cwd) {
600
629
  }
601
630
 
602
631
  // STATE.md — workspace state
603
- writeFileSync(join(awDocsDir, 'STATE.md'), `---
632
+ const statePath = join(awDocsDir, 'STATE.md');
633
+ if (!existsSync(statePath)) {
634
+ writeFileSync(statePath, `---
604
635
  active_run: null
605
636
  last_run: null
606
637
  last_workflow: null
@@ -613,24 +644,60 @@ pending_sync_count: 0
613
644
 
614
645
  No active runs. Use \`/aw:<team>-<command>\` to start a workflow.
615
646
  `);
647
+ }
616
648
 
617
649
  // config.json — registry paths, sync settings
618
- writeFileSync(join(awDocsDir, 'config.json'), JSON.stringify({
650
+ const configPath = join(awDocsDir, 'config.json');
651
+ const defaultConfig = {
619
652
  sync: {
620
653
  eager: true,
621
654
  batch_threshold: 10,
622
655
  mcp_memory_enabled: true,
656
+ github_docs: defaultAwDocsGithubDocsConfig(),
623
657
  },
624
658
  paths: {
625
- runs: '.aw_docs/runs',
626
- learnings: '.aw_docs/learnings',
627
- tasks: '.aw_docs/tasks',
628
- cache: '.aw_docs/cache',
659
+ runs: `${AW_DOCS_DIR}/runs`,
660
+ learnings: `${AW_DOCS_DIR}/learnings`,
661
+ tasks: `${AW_DOCS_DIR}/tasks`,
662
+ cache: `${AW_DOCS_DIR}/cache`,
629
663
  },
630
- }, null, 2) + '\n');
664
+ };
665
+ let nextConfig = defaultConfig;
666
+ if (existsSync(configPath)) {
667
+ try {
668
+ const existing = JSON.parse(readFileSync(configPath, 'utf8'));
669
+ nextConfig = {
670
+ ...existing,
671
+ sync: {
672
+ ...defaultConfig.sync,
673
+ ...(existing.sync || {}),
674
+ github_docs: {
675
+ ...defaultConfig.sync.github_docs,
676
+ ...(existing.sync?.github_docs || {}),
677
+ },
678
+ },
679
+ paths: {
680
+ ...defaultConfig.paths,
681
+ ...(existing.paths || {}),
682
+ },
683
+ };
684
+ } catch (e) {
685
+ fmt.logWarn(`Could not migrate ${AW_DOCS_DIR}/config.json: ${e.message}`);
686
+ nextConfig = null;
687
+ }
688
+ }
689
+ if (nextConfig) {
690
+ const nextConfigText = JSON.stringify(nextConfig, null, 2) + '\n';
691
+ const existingText = existsSync(configPath) ? readFileSync(configPath, 'utf8') : '';
692
+ if (existingText !== nextConfigText) {
693
+ writeFileSync(configPath, nextConfigText);
694
+ }
695
+ }
631
696
 
632
697
  // BOARD.md — task board
633
- writeFileSync(join(awDocsDir, 'tasks', 'BOARD.md'), `---
698
+ const boardPath = join(awDocsDir, 'tasks', 'BOARD.md');
699
+ if (!existsSync(boardPath)) {
700
+ writeFileSync(boardPath, `---
634
701
  tasks: []
635
702
  updated_at: null
636
703
  ---
@@ -639,11 +706,17 @@ updated_at: null
639
706
 
640
707
  No active tasks. Tasks are created during workflow execution.
641
708
  `);
709
+ }
642
710
 
643
711
  // _pending-sync.jsonl — sync queue (empty)
644
- writeFileSync(join(awDocsDir, 'learnings', '_pending-sync.jsonl'), '');
712
+ const pendingSyncPath = join(awDocsDir, 'learnings', '_pending-sync.jsonl');
713
+ if (!existsSync(pendingSyncPath)) {
714
+ writeFileSync(pendingSyncPath, '');
715
+ }
645
716
 
646
- fmt.logSuccess('Orchestration state ready');
717
+ if (!alreadyInitialized) {
718
+ fmt.logSuccess('Orchestration state ready');
719
+ }
647
720
  }
648
721
 
649
722
  /**